数据库实体设计商品(1.1)之商品信息
艿艿目前正在做一个开源的电商项目,胖友可以 star 下。https://gitee.com/zhijiantianya/onemall
1. 概述
本文主要分享商品模块的商品信息的数据库实体设计。
基于如下信息,逆向猜测数据库实体:
【护脸旁白】笔者非电商行业出身 && 非有赞工程师,所以有错误或不合理的地方,烦请斧正和探讨。有赞是个各方面都很 NICE 的公司,推荐 。
2. 背景了解
在看具体的数据库实体设计之前,我们先一起了解下电商的名词定义和有赞微商城界面。
2.1 名词定义
参考 《产品 SKU 是什么意思?与之相关的还有哪些?》 整理。
SKU:Stock Keeping Unit
中文翻译为库存单位。SKU 从库存视角,以库存进出为单位,可以是件、瓶、箱等等。
例如,iPhone 手机,按照规格( 颜色 + 内存 )可以组合出如下多个 SKU :
| SKU | 颜色 | 内存 |
|---|---|---|
| A | 白色 | 16G |
| B | 白色 | 64G |
| C | 黑色 | 16G |
| D | 黑色 | 64G |
可以看出,颜色(白色、黑色)与内存(16G、64G)组合排列出四种 iPhone SKU。
SPU:Standard Product Unit
中文翻译为标准产品单位。SPU 从产品视角,是产品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以被称为一个 SPU 。例如 iPhone 8 就是一个 SPU ,iPhone 8 Plus 也是一个 SPU ,这个与商家无关,与颜色、款式、套餐等规格无关。
商品:
商家出售某个 SPU ,那么这就是一个商品。商品在 SPU 之上,增加了销售价格、促销活动、运费等等信息。另外,一个商品可以包含多个 SKU。
总结
现实的场景往往比定义复杂的多,在本文中,SKU 代表销售的单元。主要考虑如下两方面:
- 实际我们看到的商品详情页,购买的是一个销售组合单元 。例如,很多商家会打包 【iPhone X :银色-64G-套餐三】,其中套餐三为赠送贴膜 + 保护壳等等,当然价格上会更贵。这明显就违背了我们上述提到 SKU 库存的概念,已经变成了多个 SKU 的销售组合单元。

- 一个商家会在不同平台销售商品,例如三只松鼠,其在天猫、京东等等平台都有官方旗舰店,同时也供货给其他渠道商,那么实际关系会变成如下图所示:
通过这样的方式,三只松鼠在不同的平台,定义不同的价格,设置不同的促销信息等等个性化的运营。
那么注意了!!!下文开始,SKU 代表销售的单元下文开始,SKU 代表销售的单元下文开始,SKU 代表销售的单元
2.2 界面
3. 数据库实体
整体实体类关系如下图:
全部实体在 Github 商品实体目录 下可见。
3.1 Item
Item 字段较多,我们进行简单的切块。
3.1.1 基础字段
```plain text plain /** * 编号 */ private Integer id; /** * 别名 * * 系统生成,作为唯一标识。例如,2fpa62tbmsl9h */ private String alias; /** * 店铺编号 */ private Integer shopId; /** * 创建时间 */ private Date createTime; /** * 更新时间 */ private Date updateTime; /** * 状态 * * 1-正常 * 2-删除 */ private Integer status;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
- id
,Item 编号,自增。数据类型是使用 Integer 还是 Long ,胖友可以根据自己的系统需要做调整。
- alias[https://h5.youzan.com/v2/goods/3f1o7jxm0gshh](https://h5.youzan.com/v2/goods/3f1o7jxm0gshh)
,别名,系统自动生成,作为唯一标识。例如,
“2fpa62tbmsl9h”
。目前有赞商城商品详情地址使用别名而不使用编号,防止无脑抓取。例如,
。
- shopId
,店铺编号。有赞是基于 Sass 模式,支持多商户( 店铺 )。
### 3.1.2 基本信息
```plain text
plain /** * 商品标题 * * 不能超过100字,受违禁词控制 */ private String title; /** * 副标题,分享链接时显示 */ private String summary; /** * 商品描述。 * * 字数要大于5个字符,小于25000个字符 ,受违禁词控制 */ @Deprecated private String desc; /** * 商品分类的叶子类目编号 * * 有赞——店铺主营类目和商品类目对应表:https://bbs.youzan.com/forum.php?mod=viewthread&tid=25252 */ private Integer cid; /** * 商品主图地址 * * 数组,以逗号分隔 * * 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张 */ private String picURLs; /** * 商品类型 * * 0:普通商品(物流发货) * 3:UMP降价拍 * 5:外卖商品 * 10:分销商品 * 20:会员卡商品 * 21:礼品卡商品 * 22:团购券 * 25:批发商品 * 30:收银台商品 * 31:知识付费商品 * 35:酒店商品(无需物流) * 40:美业商品 * 60:虚拟商品(无需物流) * 61:电子卡券(无需物流) */ private Integer itemType; /** * 商品类型 * * 0:自营商品 * 10:分销商品 */ private Integer goodsType;
- desc考虑到数据库性能,单独成 ItemContent 表《数据库实体设计 —— 商品(1.2)之商品详情》 ,商品描述,HTML 富文本。 ,在 有详细分享。
- cidhttps://bbs.youzan.com/forum.php?mod=viewthread&tid=25252 ,商品分类的叶子类目编号。有赞——店铺主营类目和商品类目对应表: 。
- itemType ,商品类型。不同的商品类型,会有相关的自定义逻辑。
- goodsType分销市场 ,商品类型(已经不知道咋翻译好,和 itemType 无关系)。店铺可以从 引入其他店铺的商品进行分销。TODO 6002:商品分销文章解析。

3.1.3 价格库存
```plain text plain /** * 价格,单位分 */ private Integer price; /** * 商品重量,没有SKU时用 */ private Double itemWeight; /** * 商品货号(商家为商品设置的外部编号) */ private String itemNo; /** * 总库存 * * 基于 sku 的库存数量累加 */ private Integer quantity; /** * 总销量 */ private Integer soldNum; /** * 是否隐藏商品库存。在商品展示时不显示商品的库存。 * * 0 - 显示库存(默认) * 1 - 不显示库存 */ private Integer hideStock; /** * 商品划线价格,可以自定义。例如 促销价:888 * * 商品没有优惠的情况下,划线价在商品详情会以划线形式显示。 */ private Double originPrice; /** * 是否参加会员折扣。 * * 1 - 参加会员折扣(默认) * 0 - 不参加会员折扣 */ private Integer joinLevelDiscount;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
---
- Item SKU 相关,有赞分成两种情况:
- 第一种,无 SKU 的情况,
price
、
itemWeight
、
quantity
、
soldNum
、
itemNo
对应这个商品的价格库存等信息。
- 第二种,有 SKU 的情况,
price
、
itemWeight
、
quantity
、
soldNum
、
itemNo
对应这个商品 SKU 的整体情况。其中,
price
对应 SKU 最低价格。
- ps:关于第一种的情况,也可以通过**虚拟没有规格的 SKU**
,这样和第二种的情况就可以等价了。
- price**分注意元**
,价格,单位为
,避免 Double 在根据营销优惠信息计算价格时,精度丢失。
,如果胖友一定要使用单位为
,在 Java 里请使用 BigDecimal 。
- joinLevelDiscount
,是否参加会员折扣。有赞支持会员卡功能,可以对商品进行优惠打折。

- itemNo**通过该字段,打通不同系统的信息**
,商品货号。商家为商品设置的外部编号,例如,ERP 系统。
。
### 3.1.4 运费信息
```plain text
plain /** * 运费类型 * * 1-统一运费 * 2-运费模板 */ private Integer postType; /** * 运费,单位分 */ private Integer postFee; /** * 运费模版id */ private Integer deliveryTemplateId;
- 根据不同的 《如何设置运费模版(按件按重量)?》 postType ,使用 postFee 或 deliveryTemplateId 。
运费模板的操作见 : 文章。
3.1.5 其他信息
```plain text plain /** * 是否上架商品。 * * true 为已上架 * false 为已下架 */ private Boolean isListing; /** * 排序字段 */ private Integer order; /** * 开始出售时间。 * * 没设置则为空 */ private Date autoListingTime; /** * 商品是否锁定。 * * true 为已锁定 * false 为未锁定 */ private Boolean isLock; /** * 留言表单数组配置 * * JSON 字符串 [{ * name: // 表单名,String * required: // 是否必填,Integer,1-必填;0-选填 * type: // 表单类型,String,枚举:文本格式/数字格式/邮件/日期/时间/身份证号/图片 * multiple: // 是否多行,Integer,1-多行,0-单行 * datetime:// 是否包含日期,用于 type=时间 * }] */ private String messages;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---
- isListing
,是否上架商品。
- true
,已上架,用户可见,直到售罄(
quantity = 0
)。售罄时,该状态不变,通过库存判断。
- false
,已下架,在仓库,用户不可见。
-

- order
,排序字段。手动填写数字设置,序号越大越靠前。

- messages
,留言表单数组配置。


### 3.1.6 ItemEtd
[ItemEtd](https://github.com/YunaiV/doraemon-entity/blob/8e7e7463ea72c2b3e952252b0c5caa13166f9638/src/main/java/cn/iocoder/doraemon/itemgroup/item/entity/ItemEtd.java),预售扩展信息
```plain text
plain /** * Item 编号 * * {@link Item#id} */ private Integer id; /** * 发货类型 * * 0 - xxx 时间开始发货 * 1 - 付款 n 天后发货。 */ private Integer etdType; /** * 预计发货开始时间, 字符串格式的时间,格式如:2018-01-01 */ private Date etdStartDate; /** * 付款成功 后发货天数, 默认0 */ private Integer etdDays;
- 《预售界面优化及新增N天后发货,适合更多预售场景》
- 通过 Item 的 etdStatus 字段,开启和关闭预售功能。
- id ,Item 编号,1:1 指向对应的 Item 。
- 根据不同的 etdType ,使用 etdStartDate 或 etdDays 。

来自老徐的提示:
- 字段以 Date 结尾,用于仅有日期格式的时间。
- 字段以 Time 结尾,用于带有时间格式的时间。
3.1.7 ItemFenxiao
ItemFenxiao,分销扩展信息
```plain text plain /** * Item 编号 * * {@link Item#id} */ private Integer id; /** * 供货店铺Id */ private Integer supplierShopId; /** * 供货商品Id */ private Integer supplierItemId;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
- 通过 Item 的
goodsType = 10
判断为分销商品。
- id
,Item 编号,1:1 指向对应的 Item 。
### 3.1.8 ItemPurchaseRight
[ItemPurchaseRight](https://github.com/YunaiV/doraemon-entity/blob/5d6cf95894ed556d307c2188dd5fba89a4c97159/src/main/java/cn/iocoder/doraemon/itemgroup/item/entity/ItemPurchaseRight.java),购买权限拓展信息
```plain text
plain /** * Item 编号 * * {@link Item#id} */ private Integer id; /** * 允许购买的粉丝标签用,号分隔 * * 数组,以逗号分隔 */ private String umpTags; /** * 允许购买的粉丝等级,用逗号分隔 */ private String umpLevels; /** * 每人限购多少件。0代表无限购,默认为0 */ private Integer buyQuota;
- 通过 Item 的 purchaseRightStatus 字段,开启和关闭商品购买权限。
- id ,Item 编号,1:1 指向对应的 Item 。

3.2 ItemSku
ItemSku,商品 SKU 。
```plain text plain /** * 唯一编码,店铺Id 和 商品skuId 组合 * * 分销场景下,skuId 多个店铺相同,uniqueCode 不同 */ private String uniqueCode; /** * sku 编号 * * 非唯一 */ private Integer skuId; /** * 商品编号 */ private Integer itemId; /** * 店铺编号 */ private Integer shopId; /** * 状态 * * 1-正常 * 2-删除 */ private Integer status; /** * 图片地址 */ private String imageURL; /** * 商品规格 * * 格式:kid[0]-vid[0],kid[1]-vid[1]…kid[n]-vid[n] * 例如:20000-3275069,1753146-3485013 */ private String properties; /** * 商品货号(商家为商品设置的外部编号) */ private String itemNo; /** * 价格,单位分 */ private Integer price; /** * 库存数量 */ private Integer quantity; /** * 商品在付款减库存的状态下,该Sku上未付款的订单数量 */ private Integer withHoldQuantity; /** * 销量 */ private Integer soldNum; /** * 创建时间 */ private Date createTime; /** * 更新时间 */ private Date updateTime;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
---
- uniqueCode**唯一注意相同唯一引入**
,SKU
编码,格式为
*shopId*{skuId}
。
,在分销场景下,引入商品的
skuId
,而
shopId
不同。通过该字段,保证
。
- skuId
,SKU 编号,自增,非唯一,参见分销场景。
- itemId
,Item 编号,N:1 指向对应的 Item 。
- status**正常**
,SKU 状态。编辑商品时,当规格属性发生变化时,优先重用( 保留 )
状态的 SKU ,标记删除移除的 SKU ,例如:
- 颜色:红;尺寸:X、L;
- SKU 如下:
- 【1】红-X
- 【2】红-L
- ————— 分隔 —————
- 新增颜色,蓝;
- SKU 如下:
- 【1】红-X
- 【2】红-L
- 【3】蓝-X
- 【4】蓝-L
- ————— 分隔 ——————
- 删除颜色,红;
- SKU 如下:
- 【3】蓝-X
- 【4】蓝-L
- properties
,商品规格,字符串拼接格式。
- 绝大多数情况下,数据库里的该字段,不存在检索的需求,更多的时候,是查询整体记录,在内存中解析使用。
- 少部分情况,灵活的检索,使用 Elasticsearch 进行解决。
- quantity
,库存数量。在分销场景下,因为
skuId
相同,所以根据
skuId
可以批量修改,解决分销库存的同步问题。
- withHoldQuantity
,商品在付款减库存的状态下( 例如秒杀场景 ),该 SKU 上未付款的订单数量。
- itemNo**通过该字段,打通不同系统的信息**
,商品货号。商家为商品设置的外部编号,例如,ERP 系统。
。
## 3.3 ItemSkuProperty
[ItemSkuProperty](https://github.com/YunaiV/doraemon-entity/blob/5d6cf95894ed556d307c2188dd5fba89a4c97159/src/main/java/cn/iocoder/doraemon/itemgroup/item/entity/ItemSkuProperty.java),Item SKU 规格属性。
```plain text
plain public class ItemSkuProperty { /** * 属性编号 */ private Integer id; /** * 属性文本 */ private String name; /** * 添加时间 */ private Date addTime; }
- id ,属性编号。
- name注意 ,属性文本。 ,当规格名( PropertyKey )和规格值( PropertyValue )使用相同文本,对应的编号相同。例如,

- PropertyKey 与 PropertyValue 通过 「3.3.2 ItemSkuPropertyValueReference」 关联。
有赞的规格属性,这样的设定感觉有丢丢怪怪的,笔者后面调研了淘宝和微店的 Item SKU 规格属性的设计。
微店
分成 ItemSkuPropertyKey 和 ItemSkuPropertyValue 两个表,并且 ItemSkuPropertyValue 相同字符串时,不同编号。
淘宝
不同类目固定可选的商品规格名,并且部分商品规格不能自定义规格值。
不同于有赞、微店,淘宝、京东是支持全平台进行检索,而有赞、微店是没有这方面的需求。
那么规格和类目做关联一定合适么?笔者觉得不一定。类目和规格的关联整理是非常大的工作量,一旦整理缺失,反倒给上架带来不好的体验。而且,有赞、微店存在较多微商,非标的商品较多,需要更多灵活的空间。
另外,淘宝商品的属性拆分的很细,猜测分成如下:
- 是否是规格属性
- 是否是搜索属性
- 是否是基本属性
- … TODO 6003https://open.taobao.com/docs/api.htm?spm=a219a.7629065.0.0.6yhiZN&apiId=4 淘宝属性研究
同时,属性的类型除了包括选项完,也可以是文本、数值、百分比等等。
推荐阅读 《B2C电子商务系统研发——商品SKU分析和设计(一)》 ,里面对SKU属性的管理做了很有借鉴性的思考。
3.3.1 ItemSkuPropertyKeyReference
ItemSkuPropertyKeyReference ,Item SKU 规格名引用。用于和店铺关联,不同店铺有不同的规格名引用数据。
```plain text plain /** * 编号 */ private Integer id; /** * 店铺编号 */ private Integer shopId; /** * 属性键编号 * * {@link ItemSkuProperty#id} */ private Integer keyId; /** * 添加时间 */ private Date addTime;
1
2
3
4
5
6
7
8
9
---
### 3.3.2 ItemSkuPropertyValueReference
[ItemSkuPropertyValueReference](https://github.com/YunaiV/doraemon-entity/blob/5d6cf95894ed556d307c2188dd5fba89a4c97159/src/main/java/cn/iocoder/doraemon/itemgroup/item/entity/ItemSkuPropertyValueReference.java) ,Item SKU 规格值引用。用于和店铺关联,不同店铺有不同的规格值引用数据。
```plain text
plain /** * 编号 */ private Integer id; /** * 店铺编号 */ private Integer shopId; /** * 关联编号 * * {@link ItemSkuPropertyKeyReference#id} */ private Integer referenceId; /** * 属性键编号 * * {@link ItemSkuProperty#id} */ private Integer keyId; /** * 属性值编号 * * {@link ItemSkuProperty#id} */ private Integer valueId; /** * 添加时间 */ private Date addTime;
4. API
基于 有赞云提供的商品API ,整理如下 API 类。
4.1 ItemAPI
ItemAPI ,商品 API 。
4.2 ItemSkuAPI
ItemSkuAPI ,商品 SKU API 。
666. 彩蛋
第一次尝试写这种类型的文章,如果内容有错误的地方,烦请斧正。如果有写的不详细或者不明白的地方,欢迎一起探讨。
推荐与参考文章:











