通用干货类商城与库存通用干货类

库存与名额:两种可售数量在系统里不是一套模型

维双云2026-11-056 分钟阅读

商城的商品有库存,预约的服务有名额。两个概念在后台上都表现为"还剩几个",很容易被当成同一件事,用同一个字段、同一套扣减逻辑去处理。真这么做,两类问题会同时出现:把名额当库存算,热点时段被约爆;把库存当名额算,未付款的订单长期锁货。

这两者不是同一个数字的两种叫法,它们的宿主对象、失效方式、扣减时机、并发粒度、释放条件都不一样。以维双云商城和维双云门店通为例,这两套数量分别由商城模块和预约模块各自管理,中间不做换算——这不是产品偷懒,而是模型本身不该合并。

下面按工程实现拆开讲。

一、宿主不同:库存挂 SKU,名额挂时段

库存的宿主是 SKU,也就是"商品 + 规格"。一件衬衫有三个颜色、四个尺码,就是十二个 SKU,每个 SKU 一个库存数。库存描述的是实物件数,跟时间无关:今天没卖掉的,明天还在。

名额的宿主是"可预约项 × 时段"。同一个剪发项目,10:00–11:00 能接两位、11:00–12:00 能接两位,这是两个独立的名额池。名额描述的是时间容量,跟时间强相关:这一格过了,剩余名额自动归零,不会累积到下一格。

这个差别决定了两个对象在数据表里的形状。库存表的主键通常落在 sku_id 上,一家店一个 SKU 一条记录就够;名额表的主键必须带时间维度,item_id + slot_start 才定位得到一条记录,同一条服务项会生成一串按天滚动的记录。

库存与名额:两种可售数量在系统里不是一套模型 配图 1

二、可售量的算法不同:一个是存量,一个是窗口

库存的可售量是存量运算:

```

可售 = 总库存 − 已锁定 − 已售出

```

三个数都是累计值,跨天延续,需要定期盘点校正,因为实物会损耗、会错发、会退货入库。

名额的可售量是窗口运算:

```

可售 = 时段容量 − 该时段已占名额

```

只有两个数,且只在时段有效期内成立。时段结束,两个数一起作废。它没有"盘点"这个概念,因为不存在实物,只存在"这段时间还能不能接人"。

不少系统把名额也建成"总量 + 已用"的两列,看起来和库存一样,但一旦要给某个时段单独关停(员工请假、设备检修),就需要在这张表上做时段级的开关,而库存表没有对应语义。硬塞在一起,字段会开始互相污染:一个 quantity 字段,有时表示件数,有时表示人数,导出到报表里连列名都说不清。

三、扣减时机:库存可预占,名额只能即时占

库存有三种常见策略:

下单即扣。 提交订单就减库存,未支付超时后释放。优点是绝不超卖,缺点是恶意占货的成本很低。

支付后扣。 支付成功才减库存,下单阶段只做前端提示。并发高时容易超卖,需要在下单时做一次预校验。

预占 + 超时释放。 下单锁一部分库存并记录锁定时间,支付成功后转为已售,超时未付自动解锁。这是多数商城的折中做法,也是维双云商城这类标准化商城模块的常规路径:下单占、支付确认、超时回滚。

名额没有"预占"这一层。顾客提交预约的那一刻就占住了那个时段,因为名额不存在"先留着、回头再确认"的业务含义——时段是独占资源,留着不给别人用,对顾客和商家都没有意义。所以名额的扣减是即时且单一的:提交成功即占用,取消即释放。

这个差别在接口设计上很直接:库存接口需要 `lock / confirm / release` 三个动作,名额接口只需要 `book / cancel` 两个。

四、并发粒度不同:行锁对计数器

库存并发的热点在爆款 SKU 上,同一个 sku_id 可能在一秒内被几十个请求同时扣减。常规做法是条件更新:

```sql

UPDATE stock SET locked = locked + 1

WHERE sku_id = ? AND total - locked - sold >= 1;

```

影响行数为 0 就代表扣减失败,直接返回"库存不足",不需要额外加锁。也可以用版本号做乐观锁,冲突时重试。两种方式都围绕同一行记录竞争。

名额并发的热点更分散,因为冲突只发生在"同一个时段"内部。10:00 的时段和 11:00 的时段互不影响,所以按 slot 做计数器就够,冲突面比库存小得多。真正的风险不是并发,而是重复提交:顾客在弱网下连点两次,可能生成两条占用记录。解决办法是以预约号作去重键,或用请求幂等键挡住重复提交。

一句话概括:库存怕超卖,名额怕重复。防线不一样。

五、释放条件不同:一个要看实物状态,一个只看时间

库存的释放不是简单加回去。顾客退货,商品可能完好、可能破损、可能拆封,退回的件数能不能重新计入可售,取决于商品状态和商家政策。系统层面通常要区分"退货入库"和"退货报损"两条路径,前者加回可售,后者只改状态不加回。

名额的释放没有状态判断,只有时间判断。取消预约就把名额放回去,前提是这个时段还没过。时段已过再取消,释放没有意义,只留一条爽约记录。

所以名额表的"释放"往往不是一次数据更新,而是两个动作:把占用数减一,同时写一条状态记录。后者用于统计爽约率,前者用于让位给下一个人。库存表则相反,它更需要一张流水表来追溯每一次增减的原因,因为库存数的正确性依赖账实相符。

六、统计口径:两套指标不能互相顶替

库存关心的指标是动销率、周转天数、缺货率,核心问题有两个:卖不掉的压了多少钱,卖断的丢了多少单。

名额关心的指标是到店率、空置率、爽约率,核心问题是:约了不来浪费了多少工位,没约满又空了多少时间。

这两组指标经常被混着看。典型错误是拿"剩余名额"当"可售库存"导出给财务,数字看起来都是"还有多少",实际一个是随时间失效的容量,一个是可跨期结转的实物。财务按库存口径核账,结果和实际收款永远对不上。

七、同一家店两类并存时,边界要划清

餐饮店是典型的两类并存:菜品有份数(库存),桌位有时段(名额)。套餐核销时,两个数量同时变化——扣一份菜品、占一个桌位时段。系统里这是两条链路各自记账,不是一次乘法运算。

美业更清楚:服务项目本身不设库存(剪发没有"剩几次"),技师工时是名额;店里顺带卖的洗护产品才是库存。

落到产品线上,维双云商城管的是 SKU 库存,维双云门店通与维双云轻站管的是时段名额,两条链在各自主场里闭环。同一笔订单里既有实物又有预约时,按两条链路分别扣减、分别留痕,不做数量换算——把"一份套餐"折算成"几个名额",只会让两边的账都算不清。

库存与名额:两种可售数量在系统里不是一套模型 配图 2

八、几个高频坑

把时段剩余当库存导出。 报表里出现"剩余 120",其实是一周内的时段容量总和,跨天不可累加。

名额不设释放策略。 顾客取消后名额没放回去,时段就一直锁着,后台看着"已满",实际一个人没来。

库存预占没有超时。 下单锁货不设时限,一次促销就能把库存锁空。

双链路下单不校验一致性。 商品扣减成功、名额占用失败,订单变成半成品,只能人工补。

幂等键缺失。 库存侧表现为重复扣减,名额侧表现为重复占位,两类问题排查路径完全不同,但根因都是同一个。

退款不区分商品状态。 一律回库,账实很快对不上。

小结

判断一个数量该按库存建模还是按名额建模,问三个问题就够:

这个数量会不会随时间自然失效?会,是名额。

这个数量会不会因为一个人没来而空着?会,是名额。

这个数量是否需要实物清点、是否可能破损?需要,是库存。

三条都指向同一个方向的,就别硬套另一种模型。系统结构上的省事,最后都会以对账和超卖的形式还回来。

返回博客列表

准备好开始了吗?

免费试用维双云,专属顾问一对一指导您快速上线。

免费试用