This commit is contained in:
Yangxt
2026-09-30 11:11:54 +08:00
parent cb9b7a4bd4
commit 2fb57ec490
160 changed files with 14764 additions and 53 deletions
+38
View File
@@ -0,0 +1,38 @@
---
description: 库存模型、库存流水与并发安全规则(任何涉及库存数量变化的代码)
globs: backend/app/**/Inventory/**,backend/app/**/Inbound/**,backend/app/**/Outbound/**,backend/app/**/Picking/**,backend/app/**/Return/**
alwaysApply: false
---
# 库存规则
详见 `docs/05-inventory.md`。库存是整个 WMS 的核心。
## 模型
- 维度:仓库 → 库区 → 库位 → SKU → 批次 → 库存状态,不要简化为 `sku + warehouse + quantity`。
- 数量:`available_qty`、`reserved_qty`、`picked_qty`、`hold_qty`、`damaged_qty`、`in_transit_qty`。
## 流水
- 任何库存数量变化,必须在**同一事务**内写入 `wms_stock_log`(变化量、变化前后、业务单据类型与单号、操作人)。
- 库存变更只能通过库存领域服务 / Action 完成,业务模块不得直接 update 库存表。
- 流水只追加,不修改、不删除。
## 并发
禁止 "先 SELECT → PHP 计算 → UPDATE"。使用事务 + 原子条件更新 / 行锁 / 乐观锁,必要时 Redis 锁。
```php
// ❌ 并发下会超卖
$inv = Inventory::find($id);
if ($inv->available_qty >= $qty) { $inv->available_qty -= $qty; $inv->save(); }
// ✅ 原子条件更新,受影响行数为 0 即库存不足
$affected = Inventory::whereKey($id)
->where('available_qty', '>=', $qty)
->update([
'available_qty' => DB::raw('available_qty - '.(int) $qty),
'reserved_qty' => DB::raw('reserved_qty + '.(int) $qty),
]);
if ($affected === 0) { throw new InsufficientStockException(); }
```
- 多行扣减按固定顺序(如库存主键升序)加锁,避免死锁。
- 必须有并发测试:库存 10,两个请求同时扣 8,只能成功一个,且不会出现负库存。