商品是电商系统的核心,这里我们一起来设计「商品」的数据库结构。
在这之前,我们需要先了解以下商品 SKU 的概念。
SKU = Stock Keeping Unit(库存量单位),也可以称之为「单品」。对一种商品而言,当其品牌、型号、配置、等级、花色、包装容量、单位、生产日期、保质期、用途、价格、产地等属性中任一属性与其他商品存在不同时,可称为一个单品。
为了更好地理解商品与商品 SKU 的关系,我们拿天猫上的商品作为例子:
在这个例子中,商品就是 iPhone,不同的版本、不同的存储容量所对应的具体型号就是这个商品的 SKU,比如 iPhone 7 - 无需合约版 - 玫瑰金色 - 32GB
就是一个 SKU,iPhone 7 - 无需合约版 - 红色 - 128GB
是另外一个 SKU,不同的 SKU 价格可能不一样,库存也不一样。
商家在管理库存的时候,就是以 SKU 为单位来操作的,比如入库时会写清楚「购入 iPhone 7 - 无需合约版 - 玫瑰金色 - 32GB 10 台,购入 iPhone 7 - 无需合约版 - 红色 - 128GB 10 台」,而不是「购入 iPhone 7 20 台」。
我们先实现一个维度的 SKU。多维度的 SKU 只不过运营层面工作流会大一些,简而言之就是编辑需要录入更多的商品类型而已,这里的数据结构和技术方案,也适用于多维度 SUK 的。
我们会有两个数据表:
products - 产品信息表,对应数据模型 Product
products_skus - 产品的 SKU 表,对应数据模型 ProductSku
products 表:
字段名称 | 描述 | 类型 | 加索引缘由 |
---|---|---|---|
id | 自增长 | unsigned int | 主键 |
title | 商品名称 | varchar | - |
description | 商品详情 | text | - |
image | 商品封面图片文件路径 | varchar | - |
on_sale | 商品是否正在售卖 | tiny int, default 1 | - |
rating | 商品平均评分 | float, default 5 | - |
sold_count | 销量 | unsigned int, default 0 | - |
review_count | 评价数量 | unsigned int, default 0 | - |
price | SKU 最低价格 | decimal | - |
商品本身没有固定的价格,我们在商品表放置 price 字段的目的是方便用户搜索、排序。
product_skus 表:
字段名称 | 描述 | 类型 | 加索引缘由 |
---|---|---|---|
id | 自增长 | unsigned int | 主键 |
title | SKU 名称 | varchar | - |
description | SKU 描述 | varchar | - |
price | SKU 价格 | decimal | - |
stock | 库存 | unsigned int | - |
product_id | 所属商品 id | unsigned int | 外键 |
电商项目中与钱相关的小数点的字段一律使用 decimal 类型,而不是 float 和 double,后面两种类型在做小数运算时有可能出现精度丢失的问题,这在电商系统里是绝对不允许出现的。
多维度其实也并不复杂,再添加一个 product_sku_attributes 表,字段如下:
id
product_id
name
然后给 SKU 加一个 attributes 字段。
假如有一个 id = 3 的商品 iPhone X,有 3 个 product_sku_attributes:
[
['id' => 1, 'product_id' => 1, 'name' => '套餐类型'],
['id' => 2, 'product_id' => 1, 'name' => '颜色'],
['id' => 3, 'product_id' => 1, 'name' => '内存']
]
新建 SKU 的时候,attributes 值为:
[
['id' => 1, 'value' => '套餐一'],
['id' => 2, 'value' => '土豪金'],
['id' => 3, 'value' => '256G'],
]
在逻辑上并不复杂。
有时候不需要一味地最求数据库设计范式,特别是 MySQL 加入 JSON 支持之后,只要不涉及到搜索查询的都可以往 JSON 字段里丢。