电商小程序SKU数据模型设计:SPU、规格组合、库存与价格的工程实践

做电商小程序,商品模型是整个系统的地基。规格(颜色/尺码/套餐)、库存、价格、图片这几样东西一旦建模出错,后面的购物车、下单、营销活动全会跟着返工。本文从最基础的SPU/SKU概念讲起,给出一套可直接落地的表结构与核心代码,并复盘我们在真实项目里踩过的四个坑。

一、为什么不能把"商品"做成一张表

新手最常见的错误:商品表直接存 price、stock,规格用一个 JSON 字段塞进去。商品只有单一规格时没问题,一旦出现"红色M码""红色L码"这种组合,库存和价格必须按组合独立管理------红色M码卖完不代表红色L码也卖完,套餐版和单机版价格完全不同。

行业通用的抽象是两层:

  • SPU(Standard Product Unit):标准化产品单元,即"一款商品",如"某品牌纯棉T恤",承载商品名称、分类、详情、主图等公共属性。
  • SKU(Stock Keeping Unit):库存最小单元,即"一款商品的一个具体规格组合",如"红色/M码",承载该组合独立的价格、库存、编码。

规格维度(规格名+规格值)与SKU是多对多关系:一个SKU对应多个规格值(红色、M码),一个规格值也被多个SKU共享。

二、表结构设计

sql 复制代码
-- SPU:商品主表
CREATE TABLE product_spu (
  id           BIGINT PRIMARY KEY AUTO_INCREMENT,
  title        VARCHAR(200) NOT NULL COMMENT '商品标题',
  category_id  BIGINT NOT NULL,
  main_image   VARCHAR(500),
  detail_html  MEDIUMTEXT COMMENT '商品详情',
  status       TINYINT DEFAULT 1 COMMENT '1上架 0下架',
  created_at   DATETIME DEFAULT CURRENT_TIMESTAMP
) COMMENT='商品SPU表';

-- 规格名:颜色、尺码、套餐
CREATE TABLE product_spec (
  id     BIGINT PRIMARY KEY AUTO_INCREMENT,
  spu_id BIGINT NOT NULL,
  name   VARCHAR(50) NOT NULL COMMENT '规格名,如:颜色'
) COMMENT='规格名表';

-- 规格值:红色、蓝色;M、L
CREATE TABLE product_spec_value (
  id      BIGINT PRIMARY KEY AUTO_INCREMENT,
  spec_id BIGINT NOT NULL,
  value   VARCHAR(50) NOT NULL COMMENT '规格值,如:红色'
) COMMENT='规格值表';

-- SKU:具体规格组合
CREATE TABLE product_sku (
  id          BIGINT PRIMARY KEY AUTO_INCREMENT,
  spu_id      BIGINT NOT NULL,
  sku_code    VARCHAR(64) NOT NULL COMMENT '商家编码/条码',
  price       DECIMAL(10,2) NOT NULL COMMENT '销售价',
  origin_price DECIMAL(10,2) COMMENT '划线价',
  stock       INT NOT NULL DEFAULT 0,
  image       VARCHAR(500) COMMENT '规格图,如颜色图',
  INDEX idx_spu (spu_id)
) COMMENT='商品SKU表';

-- SKU与规格值关联(核心:多对多)
CREATE TABLE product_sku_spec_value (
  id             BIGINT PRIMARY KEY AUTO_INCREMENT,
  sku_id         BIGINT NOT NULL,
  spec_value_id  BIGINT NOT NULL,
  UNIQUE KEY uk_sku_value (sku_id, spec_value_id),
  INDEX idx_value (spec_value_id)
) COMMENT='SKU规格值关联表';

下单时库存扣减必须在SKU维度进行,并配合乐观锁防超卖:

sql 复制代码
UPDATE product_sku
SET stock = stock - #{num}
WHERE id = #{skuId} AND stock >= #{num};

影响行数为0即库存不足,直接回滚订单。

三、规格选择交互:前端怎么知道哪个组合有货

用户选规格时的经典交互:先选"红色",再选"M码",前端要能立刻定位到对应SKU并展示价格/库存。后端给前端的数据结构建议如下:

json 复制代码
{
  "spuId": 1001,
  "specs": [
    { "specId": 1, "name": "颜色", "values": [
      { "valueId": 11, "value": "红色" },
      { "valueId": 12, "value": "蓝色" }
    ]},
    { "specId": 2, "name": "尺码", "values": [
      { "valueId": 21, "value": "M" },
      { "valueId": 22, "value": "L" }
    ]}
  ],
  "skus": [
    { "skuId": 9001, "valueIds": [11, 21], "price": "99.00", "stock": 32, "image": "..." },
    { "skuId": 9002, "valueIds": [11, 22], "price": "99.00", "stock": 0 },
    { "skuId": 9003, "valueIds": [12, 21], "price": "109.00", "stock": 15 }
  ]
}

前端把 valueIds 排序后拼成 key(如 "11,21")建 Map,用户每选一项就查一次 Map,O(n) 规格维度内即可定位SKU,无库存组合置灰。

四、踩坑复盘

坑1:规格值用字符串匹配而不是ID关联

早期图省事,SKU表直接存 spec_desc="红色,M"。结果商家把"红色"改名成"酒红色"后,历史订单、购物车里的规格全部对不上;字符串匹配也无法支撑规格图联动。规格必须用ID关联,展示名只存规格值表一处,改名全局生效。

坑2:笛卡尔积生成无效SKU组合

两个规格各5个值,理论组合25个,但不是每个组合都真实存在(某颜色可能不产XXL码)。如果后端按笛卡尔积自动生成SKU,会出现大量库存为0的"幽灵SKU",前端置灰逻辑和库存报表全乱。SKU必须由商家在后台显式创建,系统只校验"同一SPU下规格值组合唯一"。

坑3:库存扣减和订单创建不在同一事务

先 update stock 再插订单,看似没问题;但订单插入失败时如果事务没包住,库存就丢了。更隐蔽的是营销活动叠加(拼团+优惠券)导致的并发扣减:必须把"锁库存→算价→建订单明细"放在同一个数据库事务里,超卖回滚由 stock >= num 的乐观锁兜底,不要用 SELECT ... FOR UPDATE 长事务扛高并发,行锁持有时间一长连接池就打满。

坑4:价格快照不入库

下单时只存了 sku_id,没存成交单价。半年后商家调价,历史订单详情里的价格全变成新价,退款金额也算错。订单明细表必须冗余 sku_title、spec_desc、deal_price、sku_image 快照字段------商品会改名、调价、删除,订单是法律凭证,数据必须定格在成交那一刻。

五、小结

商品SKU模型的核心就三句话:SPU管公共信息,SKU管价格库存,规格用ID多对多关联;库存扣减用乐观锁并与订单同事务;订单数据永远存快照。这套结构能覆盖服装、生鲜、套餐组合等绝大多数零售场景,后续做多规格营销、库存预警、销量统计也都有干净的数据基础。

相关推荐
Huangjin007_4 小时前
【Linux 系统篇(二十五)】文件(二) 系统文件I/O详解
linux·运维·服务器
云表无代码开发4 小时前
日本开发者彻底破防:中文太强了!换成英文直接裂开
大数据·服务器·人工智能·microsoft·信息可视化
m0_587383005 小时前
西安同城拼车软件开发实战指南:从零搭建高效系统
人工智能·小程序·数据挖掘·系统架构·需求分析
LBL12205 小时前
内网容器部署FTP日志监控服务
运维·服务器·容器
weixin_lynhgworld8 小时前
抽取类盲盒小程序的五条合规红线意味着什么?
小程序
筝筝ba8 小时前
openBMC自动化测试说明
服务器
进击的荆棘8 小时前
Linux系统——文件(上)
linux·运维·服务器·文件
现任明教教主~9 小时前
运营级多门店洗车贴膜汽车在线核销保养服务系统洗车预约洗车系统消费分佣扫码核销系统
微信小程序·小程序·汽车
檀越剑指大厂9 小时前
抽屉里的手机还能开博客?用 Typecho 和 cpolar 搭一台可远程访问的小服务器
运维·服务器·智能手机
牢姐与蒯9 小时前
Linux信号(一).信号产生
linux·运维·服务器·ubuntu