# 2026-08-11 M09-D3 商品寄存与安全取出 ## 工程结果 - 工程状态:当前工作树已完成,待以 `feat(M09-D3): 完成商品寄存与安全取出闭环` 中文标题提交并取得 SHA。 - 寄存来源限定为本人已完成商品订单或具备 `goods.storage.manage` 且拥有门店范围的员工手工创建;订单来源最多创建一张寄存单,手工来源必须绑定同租户有效顾客和当前门店有效上架 SKU。 - 状态机覆盖 `STORED`、`PARTIALLY_RETRIEVED`、`RETRIEVED`、`EXPIRED`、`CANCELLED`。每次取出锁定寄存单与明细、校验剩余数量和版本,逐项扣减后写不可变事件项流水。 - 领取凭证原文只在创建、主动轮换或部分取出后的响应中返回;数据库仅保存 SHA-256。部分取出自动产生下一版凭证,旧凭证立即失效;全量取出、取消和过期会消费凭证。 - 顾客只能查询和操作本人的寄存;管理端同时校验租户、`goods.storage.read/manage` 和授权门店范围。创建重放不会再次返回原始凭证,动作重放不会泄露轮换后的凭证。 - 寄存、取出、凭证轮换、取消和过期均在同一事务写审计与 `PRODUCT_STORAGE` outbox;死锁和锁等待超时进行最多 3 次完整事务重试。 ## 数据库结果 `2026081109_m09d3_product_storage` 新增 4 张表: 1. `qipai_product_storage_records` 2. `qipai_product_storage_items` 3. `qipai_product_storage_events` 4. `qipai_product_storage_event_items` 迁移同时为商品订单项补充租户/门店/订单/主键复合唯一键,以支持寄存明细来源复合外键。寄存来源订单、客户端请求、事件请求、事件版本和订单项来源均有唯一约束;4 个触发器拒绝修改或删除寄存事件和逐项数量流水。 ## 验证证据 - `node scripts/check-m09-d3-product-storage.mjs`:PASS。 - 后端 `npm run build` 与全量 `npm test`:PASS;新增服务测试覆盖凭证摘要、取出状态转换和输入边界,路由测试覆盖顾客归属、管理权限与门店范围。 - WSL 原生临时副本 + MySQL 8.4.10:完成 `up → verify → down → up → verify`,217 条 up、159 条 verify、199 条 down;M09-D1 interrupted up/down 重试会按依赖先撤销 D3/D2并完整恢复。 - 真实 MySQL 业务用例覆盖订单寄存、手工寄存、创建幂等与指纹冲突、同源重复拒绝、跨租户/跨寄存人拒绝、超量拒绝、部分取出与旧凭证失效、并发全量取出单赢家、员工领取、主动轮换、取消、过期、outbox、审计和不可变触发器。 ## 阶段结论 `M09-D3=DONE`。唯一执行游标进入 `M09-D4`,下一阶段在顾客小程序和运营后台接入商品目录、订单、库存与寄存/取出工作台。