CRMEB Pro 商品字段二开:为什么加一个字段会牵动 SKU、缓存和前端展示?

摘要

很多 CRMEB Pro 二开需求看起来很小:给商品加一个业务字段,比如商品卖点、渠道标识、适用场景、内部备注、采购属性、展示标签。真正动手时才会发现,商品字段不是只改一张表那么简单。

商品保存会经过后台表单、Controller 参数收集、Services 数据整理、Dao/Model 入库、规格库存同步、列表搜索、移动端展示和缓存清理。字段如果只加在前端,保存时会丢;只加在后端,列表不显示;只改主表,不考虑 SKU 和缓存,移动端可能还是旧数据。

这篇结合 CRMEB Pro 当前商品模块实现,整理一套商品字段二开的检查链路。

一、先看商品保存的真实入口

后台商品模块的核心路由在 crmeb_pro/route/admin.php 的 product 分组中,和商品新增、编辑、列表、规格、库存有关的入口包括:

text 复制代码
GET  product/product                 商品列表
POST product/product/:id             新建或修改商品
GET  product/product/:id             商品编辑详情
GET  product/product/type_header     商品列表头部数据
GET  product/product/attrs/:id       获取商品规格
PUT  product/product/saveStocks/:id  快速批量修改库存
POST generate_attr/:id/:type         生成商品规格列表

后台前端对应接口封装在 crmeb_pro_admin/src/api/product.js:

text 复制代码
getGoods             商品列表
productInfoApi       商品编辑详情
productAddApi        商品提交
getGoodHeade         商品头部统计
productAttrsApi      商品规格
productSaveStocksApi 提交规格库存

所以商品字段二开不要只盯一个页面,要先确认这个字段属于:

text 复制代码
商品主表字段
商品 SKU 字段
商品分类/品牌/标签关系
商品详情 description
列表筛选字段
移动端展示字段

字段归属不同,改动位置完全不同。

二、Controller 里要先接住字段

后台商品保存入口是 StoreProduct::save()。它并不直接读取所有请求参数,而是通过 getProductSaveFields() 定义允许接收的字段。

当前商品保存字段里已经包含很多业务项,例如:

text 复制代码
product_type      商品类型
cate_id           商品分类
store_name        商品名称
store_info        商品简介
keyword           关键字
slider_image      商品轮播图
spec_type         单规格/多规格
items             SKU 明细
attrs             多规格规则
label_id          用户标签
store_label_id    商品标签
brand_id          品牌
delivery_type     配送方式
temp_id           运费模板
is_vip_product    付费会员商品
product_clear     商品适用群体
custom_form       自定义表单
system_form_id    系统表单

如果新增字段没有放进这个参数白名单,前端传了也会被过滤,后续 Services 根本拿不到。

二开建议:

text 复制代码
1. 新增商品主字段:先补 getProductSaveFields()
2. 新增列表筛选字段:再补 index() 和 type_header() 的 getMore()
3. 新增快速编辑字段:检查 set_product 或批量操作入口
4. 新增移动端字段:再检查 API 输出字段

Controller 只做参数接收和调用,不要在这里写查询或业务判断。

三、Services 才是商品字段落库前的核心整理层

商品保存真正的业务编排在 StoreProductServices::saveData(),它会先调用 prepareProductSaveData() 整理数据,然后进入事务保存。

这个链路里会做很多事情:

text 复制代码
1. 校验商品基础数据
2. 处理运费和配送方式
3. 区分普通商品、供应商商品、门店商品
4. 整理单规格/多规格数据
5. 处理分类、标签、轮播图、主图
6. 保存商品详情 description
7. 校验并保存 SKU
8. 汇总商品库存、会员价、售罄状态
9. 触发商品创建事件和库存流水事件
10. 清理商品和规格缓存

这也是为什么"加一个字段"会牵动这么多地方。比如你加的是主表字段,至少要确认:

text 复制代码
prepareProductSaveData() 中不会被 unset 掉
normalizeProductBaseData() 或相关整理方法是否需要处理格式
fillProductPriceData() 是否会影响价格字段
saveData() 事务里是否需要和 SKU 一起更新
event('product.create') 后续监听是否需要这个字段

如果字段和 SKU 没关系,不要放到 items 或 attrs 里;如果字段是 SKU 维度,比如规格独立编码、规格成本、规格外部库存,就应该放到 SKU 保存链路,而不是商品主表。

四、SKU 和库存不能顺手乱改

商品保存时,StoreProductServices::saveData() 会调用:

text 复制代码
StoreProductAttrServices::validateProductAttr()
StoreProductAttrServices::saveProductAttr()

规格保存完成后,还会根据所有 SKU 的库存汇总更新商品主表:

text 复制代码
商品 stock = 所有 SKU stock 求和
商品 is_sold = SKU 最小库存判断
商品 vip_price = SKU 会员价最小值

这意味着,如果你的字段和库存、价格、会员价、佣金、成本价有关,就不能只改商品主表。

例如新增"规格外部编码"时,应该考虑:

text 复制代码
store_product_attr_value 是否新增字段
StoreProductAttrServices::validateProductAttr() 是否允许该字段
StoreProductAttrServices::saveProductAttr() 是否保存该字段
StoreProductAttrValueServices::updateAttrs() 是否快速编辑同步
前端规格表格是否显示和提交该字段
导入/导出是否需要支持该字段

否则后台新增商品时字段能保存,列表快速改库存时又可能把它漏掉。

五、列表展示和搜索也要一起补

后台商品列表在 StoreProduct::index() 接收筛选条件,再调用 StoreProductServices::getList(),最后走 StoreProductDao::getList() 和 Model 搜索器。

当前列表筛选已经支持很多条件:

text 复制代码
商品名称/ID/关键字/条码
商品类型
分类、品牌、配送方式
单规格/多规格
商品标签
供应商
销量区间、价格区间、库存区间
创建时间区间
付费会员价
等级会员价格
是否参与返佣
活动类型
适用群体

如果新增字段需要搜索,不要在 Services 拼 SQL。更稳的方式是:

text 复制代码
Controller 增加筛选参数
Dao search() 传入 where
Model 增加搜索器
前端 tableForm/artFrom 增加字段
列表导出字段同步更新

例如适用群体 product_clear 最终对应 Model 搜索器,会转换成 is_general_product、is_channel_product、is_vip_product 等字段判断。类似字段最好也按搜索器封装,避免列表页和导出页各写一份查询逻辑。

六、缓存和草稿也容易被忽略

商品新增编辑页有临时保存能力:

text 复制代码
GET    product/cache     获取退出未保存的数据
POST   product/cache     保存还未提交数据
DELETE product/cache     删除退出未保存数据

Controller 中 saveCacheData() 会把商品编辑中的大量字段写到缓存里。如果新增字段只加正式保存,不加草稿保存,运营编辑一半离开页面后再回来,这个字段可能丢失。

商品保存成功后,系统会清理:

text 复制代码
商品缓存 tag
规格缓存 tag

涉及移动端商品详情、商品列表、SKU 展示的字段,发布后还要确认缓存是否清掉,否则会出现后台已保存、前端仍展示旧值。

七、前端需要同步三类位置

后台前端至少要看这些位置:

text 复制代码
crmeb_pro_admin/src/api/product.js
crmeb_pro_admin/src/pages/product/productAdd/index.vue
crmeb_pro_admin/src/pages/product/productAdd/components/productBaseSet.vue
crmeb_pro_admin/src/pages/product/productAdd/components/vipPriceSet.vue
crmeb_pro_admin/src/pages/product/productList/index.vue

一般字段改造分三类:

text 复制代码
编辑页字段:表单展示、校验、提交
列表字段:表格展示、搜索、导出
SKU 字段:规格表格列、单规格默认值、多规格批量填充

移动端如果要展示,还要看:

text 复制代码
crmeb_pro_uniapp/api/store.js
product/detail/:id
products
product/hot
v2/get_attr/:id/:type

不要只改后台保存,忘了移动端读取。

八、推荐二开步骤

text 复制代码
1. 先判断字段归属:主表、SKU、详情、关系表还是筛选字段
2. 涉及数据库时,同步维护完整安装 SQL 和升级 SQL
3. Controller 参数白名单接住字段
4. Services 中整理格式、校验边界、避免被 unset
5. Dao/Model 搜索器补查询,不在业务层写 SQL
6. 前端编辑页、列表页、导出和移动端展示一起同步
7. 保存后确认缓存 tag 清理
8. 测试新增、编辑、复制商品、批量操作、导入导出、移动端详情

如果字段影响价格、库存、佣金、会员价,一定要额外测试下单、退款、库存扣减和分销结算,不要只看商品页保存成功。

九、关键目录说明

text 复制代码
crmeb_pro/route/admin.php
后台商品路由入口,能看到商品列表、保存、规格、库存等接口。

crmeb_pro/app/controller/admin/v1/product/StoreProduct.php
商品后台控制器,负责接收参数、调用 Services、返回统一响应。

crmeb_pro/app/services/product/product/StoreProductServices.php
商品主业务服务,负责保存编排、字段整理、库存汇总、缓存清理和事件触发。

crmeb_pro/app/services/product/sku/StoreProductAttrServices.php
商品规格服务,负责规格校验、SKU 生成和 SKU 保存。

crmeb_pro/app/services/product/sku/StoreProductAttrValueServices.php
规格值服务,负责 SKU 库存、价格、库存流水和快速库存修改。

crmeb_pro/app/dao/product/product/StoreProductDao.php
商品查询 Dao,列表、搜索、分页、统计都应优先从这里扩展。

crmeb_pro/app/model/product/product/StoreProduct.php
商品模型,包含关联和搜索器。

crmeb_pro_admin/src/api/product.js
后台前端商品接口封装。

crmeb_pro_admin/src/pages/product/productAdd
商品新增编辑页面和组件。

crmeb_pro_admin/src/pages/product/productList/index.vue
商品列表、筛选、批量操作和库存入口。

十、注意事项

  • 不要在 Controller 或 Services 中直接写临时 SQL。
  • 新增查询条件优先放到 Dao 和 Model 搜索器。
  • 新增字段涉及数据库时,安装 SQL 和升级 SQL 都要维护。
  • SKU 字段不要混到商品主表字段里。
  • 价格、库存、会员价、佣金字段必须额外测试下单和退款链路。
  • 商品编辑草稿、复制商品、导入商品、导出商品都要检查字段是否丢失。
  • 发布后要确认商品缓存和规格缓存被清理。

十一、标签建议

text 复制代码
CRMEB
CRMEB Pro
二次开发
商品模块
SKU
ThinkPHP
商城系统
源码解析
相关推荐
努力变的更好10 分钟前
H5与小程序之间的回调
前端·小程序·apache
汤米粥17 分钟前
目前流行的前端 CSS 框架
前端·css·bootstrap·ant design·‌tailwind css·bulma
砚凝霜17 分钟前
CTF Misc 签到 GXUST·CTF:查看源码 + HTML 注释 + Base64 解码(附 Base64 原理)
前端·html
徐小夕34 分钟前
扒了JitWord的源码,我终于搞懂了企业级在线Word到底有多难写
前端·vue.js·github
lily52278335 分钟前
工具池与分发
前端·chrome
徐小夕@趣谈前端1 小时前
老旧Word格式全支持!JitWord协同AI文档4.1,让Word文档无缝迁移到Web端
前端·人工智能·word
螺丝钉tina1 小时前
[Playwright][01] 从 Puppeteer 到 Playwright:为什么选择 Playwright + TypeScript?
前端·javascript·typescript
Sailing1 小时前
🚀🚀【封闭结束6】Agent 会话服务与鉴权上下文
前端·人工智能·agent
hongyangcao1 小时前
Cloudflare GLM-5.3 串号事故复盘:KV 缓存页记账错了一个字段,155 条回复答进了别人的会话
人工智能·缓存·ai
IT_陈寒1 小时前
Java项目实战经验分享:一次由JPA懒加载引发的生产事故
前端·人工智能·后端