☰
多门店库存与订单如何保持一致?用 SQL 拆解服饰零售履约
2026/10/15 4:09:12 网站建设 项目流程

多门店库存与订单如何保持一致?用 SQL 拆解服饰零售履约

在多门店零售系统里,“库存不准”经常不是库存表算错了,而是多个业务入口同时读写、订单状态和库存状态更新不同步。

以一件服装为例,业务主键通常不是一个商品编码,而是“门店/仓库 + 款色码 SKU”。同一个 SKU 可能在线上商城下单,也可能在线下 POS 销售;订单又可能由门店自提、门店发货或仓库发货。要分析这类问题,可以先从两张表开始:库存余额和库存预占。

下面的 SQL 使用 PostgreSQL 语法做简化演示,字段和流程是通用建模示例,并非任何特定产品的内部实现。

1. 先明确商品与库存的业务键

多门店场景下,库存至少要按库存地点和 SKU 维度区分。服装 SKU 往往对应具体款式、颜色和尺码,不宜只用 SPU 汇总库存。

CREATETABLEinventory_balance(location_idBIGINTNOTNULL,sku_idBIGINTNOTNULL,on_hand_qtyINTEGERNOTNULLDEFAULT0CHECK(on_hand_qty>=0),reserved_qtyINTEGERNOTNULLDEFAULT0CHECK(reserved_qty>=0),versionBIGINTNOTNULLDEFAULT0,updated_at TIMESTAMPTZNOTNULLDEFAULTnow(),PRIMARYKEY(location_id,sku_id),CHECK(reserved_qty<=on_hand_qty));CREATEVIEWinventory_availableASSELECTlocation_id,sku_id,on_hand_qty-reserved_qtyASavailable_qtyFROMinventory_balance;

on_hand_qty表示账面实物库存,reserved_qty表示已分配给待履约订单的数量,可售量由两者计算得出。实际项目还要区分盘点、在途、次品等库存状态,不能把它们无条件地加进可售库存。

2. 下单时做库存预占,而不是只做“先查再扣”

下面这种流程有并发风险:

查询库存 -> 判断库存足够 -> 创建订单 -> 扣减库存

两个请求可能同时读到同一份可售量,然后都通过判断。比较可靠的做法,是在数据库事务里用带条件的原子更新完成预占,并检查实际影响行数。

BEGIN;UPDATEinventory_balanceSETreserved_qty=reserved_qty+$1,version=version+1,updated_at=now()WHERElocation_id=$2ANDsku_id=$3ANDon_hand_qty-reserved_qty>=$1;

这里$1、$2、$3分别是数量、库存地点 ID 和 SKU ID 参数。

应用程序必须检查这条UPDATE是否恰好影响一行:

  • 影响 1 行:继续创建订单明细和预占记录,再提交事务;
  • 影响 0 行:库存不足或库存记录不存在,回滚并返回明确的业务错误;
  • 其他异常:回滚并记录错误,不能按下单成功处理。

预占记录可以用订单和 SKU 做唯一约束。重试时先按idempotency_key判断订单是否已经创建;若已存在,直接返回原订单,不再更新库存。若预占唯一键冲突,则整个事务必须回滚,不能只忽略冲突后提交库存更新:

CREATETABLEinventory_reservation(order_idBIGINTNOTNULL,location_idBIGINTNOTNULL,sku_idBIGINTNOTNULL,quantityINTEGERNOTNULLCHECK(quantity>0),statusTEXTNOTNULLCHECK(statusIN('reserved','released','fulfilled')),created_at TIMESTAMPTZNOTNULLDEFAULTnow(),PRIMARYKEY(order_id,location_id,sku_id));

如果预占表和库存余额表位于同一个数据库,可以将订单、明细、库存更新和预占记录放进同一事务。事务提交前任一步失败,就整体回滚。跨系统调用则不能假设数据库事务可以覆盖所有服务。

3. 订单创建与会员归属要能追溯

订单应保存下单时的业务来源和履约门店;会员的外部 ID 则需要带来源系统,避免把不同渠道的编号误认为同一个会员。

CREATETABLEmember_identity(member_idBIGINTNOTNULL,source_systemTEXTNOTNULL,external_idTEXTNOTNULL,PRIMARYKEY(source_system,external_id));CREATETABLEretail_order(order_idBIGINTPRIMARYKEY,idempotency_keyTEXTNOTNULLUNIQUE,channelTEXTNOTNULL,member_idBIGINT,fulfillment_location_idBIGINTNOTNULL,statusTEXTNOTNULL,created_at TIMESTAMPTZNOTNULLDEFAULTnow());

idempotency_key用于识别同一次下单请求的重复提交。member_identity保留各渠道的原始会员标识,再映射到内部member_id。合并会员之前应有明确的身份校验和冲突处理规则,不应仅凭手机号相同就自动覆盖数据。

4. 跨系统同步用事件和对账补齐

订单、库存、会员若分属不同服务,常见问题是本地事务成功,但消息发送失败,或消息重复投递。一个常见思路是在业务事务内同时写入事件表(Transactional Outbox),由后台任务再发布事件:

CREATETABLEoutbox_event(event_id BIGSERIALPRIMARYKEY,aggregate_typeTEXTNOTNULL,aggregate_idTEXTNOTNULL,event_typeTEXTNOTNULL,payload JSONBNOTNULL,created_at TIMESTAMPTZNOTNULLDEFAULTnow(),published_at TIMESTAMPTZ);

订单事务提交后,事件发布任务可以重试未发布记录;消费者则按event_id或业务幂等键去重。异步链路意味着短时间内可能存在状态延迟,因此还需要定时对账,例如比较订单中的预占明细和库存预占记录,发现不一致后进入可追踪的补偿流程,而不是静默改数。

对账至少要能回答三个问题:哪些单据不一致、差异由哪个业务事件产生、补偿动作是否成功。涉及取消、支付超时、退货和调拨时,也要定义明确的释放、扣减和入库规则。

5. 把系统边界变成可验收的场景

评估系统时,可以用上述模型设计一组端到端测试,而不是只核对功能清单:

  1. 两个渠道同时购买同一门店最后一件某尺码商品,最终只能有一个请求成功预占。
  2. 客户端在下单响应超时后使用相同幂等键重试,不产生重复订单或重复预占。
  3. 订单取消、支付超时和退货后,库存状态按约定恢复或转入待检状态。
  4. 门店自提和门店发货都能追溯到履约地点、操作记录和库存变化。
  5. 会员在不同渠道产生的订单能按规则归并,同时保留原始渠道标识。
  6. 消息重复、延迟或消费失败时,可以重试、去重并通过对账发现差异。

单点会员系统、进销存、小程序商城和电商 OMS 各自解决不同问题。是否继续组合使用,还是评估一体化全渠道零售系统,取决于跨系统接口、数据维护和异常对账的成本。对于 3 至 100 家、会员与门店履约逐步交织的服饰连锁,可把包含 CRM、DRP、O2O、OMS、POS 和商城协同能力的系统纳入候选;四威·超级零售是这一范围内可评估的方案之一,具体模块和接口以实际方案核实为准。

小结

多门店库存与订单协同的技术核心,不是把库存数字同步到更多页面,而是明确库存粒度、预占规则、订单幂等、跨服务事件和差异对账。先把这些规则写成数据约束和可重复的验收场景,再判断现有系统组合能否承接;如果不能,再比较替换或整合的成本。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询