简介
本文件面向电商平台开发者,系统化梳理 DouPHP 电商系统的商品管理数据模型与相关实现,重点覆盖以下主题:
- 商品主表、商品分类表、商品属性表、商品属性值表的结构设计与字段含义
- SKU 体系与多规格支持的数据建模方式
- 库存管理与价格管理的表结构与业务联动
- 搜索优化、SEO 字段与商品状态流转的数据支撑
- 商品数据导入导出与批量操作的性能优化策略
项目结构
围绕商品管理的数据模型主要分布在以下位置:
- 数据库建表脚本:模块备份 SQL
- 后台模型:定义表名、主键、可写字段、关联与查询范围
- 后台服务:封装商品与属性的增删改查、批量操作、图片处理、价格计算等流程
- 升级脚本:历史字段增量变更(如促销时间、导航同步、slug 索引等)
- 前台 SEO:页面标题、关键词、描述的统一拼装逻辑
graph TB
subgraph "数据层"
A["dou_product<br/>商品主表"]
B["dou_product_category<br/>商品分类表"]
C["dou_attribute<br/>商品属性表"]
D["dou_attribute_value<br/>商品属性值表"]
end
subgraph "模型层"
M1["Product 模型"]
M2["ProductCategory 模型"]
M3["Attribute 模型"]
end
subgraph "服务层"
S1["ProductService"]
S2["AttributeService"]
end
A --> M1
B --> M2
C --> M3
D --> M3
M1 --> S1
M2 --> S1
M3 --> S2
D --> S2
核心组件
- 商品主表(dou_product):承载商品基础信息、价格、库存、销量、SEO、状态、创建者等。
- 商品分类表(dou_product_category):树形分类结构,支持图标、SEO、排序、是否同步至导航。
- 商品属性表(dou_attribute):定义属性维度(如颜色、尺寸),支持按模块与分类过滤。
- 商品属性值表(dou_attribute_value):记录具体属性值、类型、图片、备注、价格变动,用于构建 SKU。
架构总览
商品管理在“数据层—模型层—服务层”的三层结构中协作:
- 数据层:以 dou_product、dou_product_category、dou_attribute、dou_attribute_value 为核心
- 模型层:通过 Model 声明表名、主键、白名单字段,提供关联与查询范围
- 服务层:封装列表构建、新增/更新/删除、批量操作、图片处理、价格计算、属性值管理等业务流程
sequenceDiagram
participant U as "管理员"
participant C as "控制器"
participant S as "ProductService"
participant M as "Product 模型"
participant DB as "数据库"
U->>C : 提交商品表单
C->>S : insert/update(...)
S->>M : create/fill/save(...)
M->>DB : 写入 product 表
DB-->>M : 返回主键/影响行数
M-->>S : 模型实例
S-->>C : 成功/错误
C-->>U : 跳转或提示
详细组件分析
商品主表(dou_product)设计
- 关键字段
- id:自增主键
- category_id:所属分类
- operator_type/operator_id:创建者类型与ID(区分 admin/work)
- brand_id:品牌
- title/slug:标题与URL标识(slug 有索引)
- price/promote_price/promote_start_at/promote_end_at:基础价、促销价及促销时间
- level_price:会员等级价(序列化数组)
- stock:库存
- defined:自定义字段(序列化数组)
- content:商品详情(长文本)
- image:缩略图
- model:型号(用于同型号归组)
- point:赠送积分
- sales:销量
- keywords/description:SEO 关键词与描述
- sort/status:排序与状态(启用/禁用)
- created_at:创建时间
- 索引与约束
- 主键:id
- 索引:idx_slug(slug)、idx_operator(operator_type, operator_id)
- 业务要点
- 状态字段 status 控制上架/下架
- slug 用于友好URL并建立索引提升检索效率
- 促销价带起止时间,便于活动期展示与校验
- level_price 为序列化结构,配合定价服务进行等级价计算
- model 字段支持将多个商品归组(例如同一SKU的不同变体)
商品分类表(dou_product_category)设计
- 关键字段
- id:主键
- name/slug:名称与URL标识
- icon:分类图标
- keywords/description:SEO 关键词与描述
- parent_id:父分类ID(树形结构)
- sync_to_nav:是否同步至导航
- sort:排序
- 业务要点
- 树形分类通过 parent_id 组织层级
- 支持 SEO 元信息,便于分类页优化
- 可配置是否参与导航生成
商品属性表(dou_attribute)与属性值表(dou_attribute_value)设计
- 属性表(dou_attribute)
- id:主键
- module:所属模块(默认 product)
- category_id:关联分类ID(支持全局属性 category_id=0)
- name/type/sort:属性名、类型、排序
- 属性值表(dou_attribute_value)
- id:主键
- module/item_id/att_id:归属模块、商品ID、属性ID
- value/type:属性值与值类型
- image/remark:值图片与备注
- price_change:价格变动(相对基础价的浮动)
- 业务要点
- 属性按模块与分类管理,支持全局属性与分类专属属性
- 属性值绑定到具体商品,形成 SKU 组合
- 第一个属性值作为默认规格时不允许设置价格浮动(由服务层校验)
- 支持图片型属性值(如颜色色板)
SKU 体系与多规格支持
- 数据模型
- 商品基础价与库存位于 dou_product
- 多规格通过 dou_attribute + dou_attribute_value 组合表达
- 每个属性值可携带 price_change,表示该规格对基础价的价格调整
- 规则与约束
- 首个属性值作为默认规格时,price_change 必须为零(服务层校验)
- 属性值在同一商品+属性维度下唯一(重复添加会提示已存在)
- 典型流程
- 选择属性维度与值 → 生成规格组合 → 计算最终售价(基础价 + 各规格价格变动)→ 扣减对应库存(若需要)
flowchart TD
Start(["开始"]) --> PickAttr["选择属性维度与值"]
PickAttr --> CheckFirst{"是否为默认规格?"}
CheckFirst --> |是| ZeroPrice["强制 price_change=0"]
CheckFirst --> |否| CalcPrice["计算价格变动累加"]
ZeroPrice --> CalcPrice
CalcPrice --> GenSpec["生成规格组合"]
GenSpec --> SaveValue["保存属性值(含price_change)"]
SaveValue --> End(["结束"])
库存管理与价格管理
- 库存
- dou_product.stock 表示商品库存
- 订单侧涉及库存锁定与释放(order_item.stock_lock),下单时检查可售库存,取消/完成后释放
- 价格
- dou_product.price 为基础价;promote_price 为促销价(带起止时间)
- level_price 为会员等级价(序列化),在服务层结合用户等级计算
- attribute_value.price_change 为规格价格浮动,叠加后得到最终售价
sequenceDiagram
participant O as "订单服务"
participant G as "库存守卫"
participant P as "商品服务"
participant DB as "数据库"
O->>G : 检查库存(module='product', item_id, number)
G->>DB : 读取stock与锁定数量
DB-->>G : 返回实时可售库存
alt 库存充足
G-->>O : 允许下单
else 库存不足
G-->>O : 拒绝下单
end
搜索优化与 SEO 字段
- 搜索优化
- 商品 slug 字段建立索引 idx_slug,利于基于 URL 标识的快速定位
- 列表查询使用分页与必要字段投影,减少冗余数据传输
- SEO 字段
- dou_product.keywords/description 与 dou_product_category.keywords/description 分别用于商品与分类页的 SEO 元信息
- 前台通过 SeoResolver 统一拼装 title/keywords/description,支持回退到站点级配置
graph LR
A["dou_product<br/>keywords/description"] --> R["SeoResolver"]
B["dou_product_category<br/>keywords/description"] --> R
R --> T["页面<title>/<meta>"]
商品状态流转
- 状态字段
- dou_product.status:1 启用(上架),0 禁用(下架)
- 查询过滤
- 模型提供 Published 范围仅返回启用商品
- 业务场景
- 前台展示通常仅显示启用商品
- 后台列表可按状态筛选
stateDiagram-v2
[*] --> 禁用
禁用 --> 启用 : "status=1"
启用 --> 禁用 : "status=0"
商品数据导入导出与批量操作
- 导入
- AI 模块包含商品导入器,支持常见字段映射(title/content/keywords/description/price/stock/defined)
- 导出
- 可通过列表接口或后台工具导出当前筛选结果(建议结合分页与字段投影)
- 批量操作
- 批量删除、批量移动分类等操作在服务层集中处理,减少多次往返
- 批量操作需先校验参数与权限,再执行事务性更新
flowchart TD
I["准备导入数据"] --> V["校验字段与格式"]
V --> |通过| M["映射到产品字段"]
M --> W["批量写入/更新"]
W --> L["记录日志/审计"]
V --> |失败| E["返回错误与行级反馈"]
依赖关系分析
- 模型与表
- Product 模型映射 product 表,并提供 with('category') 关联分类
- ProductCategory 模型映射 product_category 表,支持树形遍历
- Attribute 模型映射 attribute 表,支持按分类与全局属性过滤
- AttributeValue 模型映射 attribute_value 表,绑定到具体商品与属性
- 服务与模型
- ProductService 依赖 Product/ProductCategory 模型完成商品CRUD与列表构建
- AttributeService 依赖 Attribute/AttributeValue 模型完成属性与属性值管理
- 外部依赖
- 订单模块通过库存守卫访问商品库存,确保下单一致性
- 前台 SEO 解析依赖商品与分类的 SEO 字段
classDiagram
class Product {
+table="product"
+fillable[...]
+category()
}
class ProductCategory {
+table="product_category"
+fillable[...]
}
class Attribute {
+table="attribute"
+fillable[...]
}
class AttributeValue {
+table="attribute_value"
}
class ProductService {
+insert(...)
+update(...)
+action(...)
}
class AttributeService {
+processValueAjax(...)
}
Product --> ProductCategory : "belongsTo"
ProductService --> Product
ProductService --> ProductCategory
AttributeService --> Attribute
AttributeService --> AttributeValue
性能考虑
- 查询优化
- 列表查询使用 field 投影仅取必要字段,减少网络传输
- 使用 paginate 分页避免一次性加载大量数据
- 利用 slug 索引与 operator 复合索引加速检索
- 批量操作
- 批量删除/移动分类采用批量更新,降低数据库交互次数
- 删除前二次确认,避免误删
- 图片与缩略图
- 上传时自动生成缩略图,提供批量重生成能力,保证前端展示一致性
- 库存与并发
- 下单前检查实时可售库存,避免超卖
- 订单取消/完成后释放锁定库存,保持库存准确性
故障排查指南
- 属性值价格校验失败
- 现象:首次添加属性值时报错“第一个值必须匹配默认价格”
- 原因:首个规格值不允许设置价格浮动
- 处理:将 price_change 清零后再保存
- 属性删除失败
- 现象:删除属性时报错“存在子属性值”
- 原因:该属性仍有属性值记录
- 处理:先删除或解绑属性值,再删除属性
- 商品不存在
- 现象:编辑/删除时报错“记录不存在”
- 原因:目标商品已被删除或ID无效
- 处理:检查ID与权限边界
- 库存不足
- 现象:下单被拒
- 原因:实时可售库存不足
- 处理:补货或调整订单数量
结论
DouPHP 的商品管理数据模型以 dou_product 为核心,结合 dou_product_category、dou_attribute、dou_attribute_value 构建了灵活的多规格 SKU 体系。通过状态字段、SEO 字段、促销时间与会员等级价等设计,满足电商常见的运营需求。服务层提供了完善的CRUD、批量操作与图片处理能力,并与订单模块协同保障库存一致性。建议在大规模数据场景下充分利用索引、分页与批量操作,以提升系统性能与稳定性。
附录
- 字段命名约定
- 主键统一为 id
- 外键遵循语义化命名(如 category_id、brand_id、att_id、item_id)
- 扩展建议
- 如需更细粒度的库存管理,可在属性值维度增加独立库存字段
- 如需更复杂的促销策略,可扩展促销表与规则引擎
- 参考路径
- 建表脚本:product.sql、attribute.sql
- 模型与服务:见“本文引用的文件”所列路径