fix(backend): 修复库存 TOCTOU 竞态 + handler 白名单更新 + 对账接口
#29 ApproveStockOut:FOR UPDATE 锁定批次后再内存汇总总量, 消除预检 SUM 与加锁之间的竞态窗口,减少一次 DB 往返 #30 partner/warehouse/user/product_attr/product_option Update 方法: - 绑定到独立 req struct,防止请求体覆盖记录 ID - 改用 db.Model.Where("shop_id=?").Updates(map) 白名单更新, 数据库层强制 shop_id 隔离约束 #31 新增 GET /api/v1/admin/reconcile:对比 inventories 当前库存 与 inventory_logs 流水净量,返回差异行,用于发现不平账异常 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -163,12 +163,17 @@ func (s *StockService) ApproveStockOut(shopID, orderID, reviewerID uint64) error
|
||||
productID := itemCopy.ProductID
|
||||
needed := itemCopy.Quantity
|
||||
|
||||
// 1. 预检:SUM 是否充足
|
||||
var totalQty float64
|
||||
tx.Model(&model.Inventory{}).
|
||||
Where("shop_id = ? AND warehouse_id = ? AND product_id = ? AND deleted_at IS NULL",
|
||||
// 1. FOR UPDATE 锁定批次后再汇总,消除 TOCTOU 窗口
|
||||
var batches []model.Inventory
|
||||
tx.Set("gorm:query_option", "FOR UPDATE").
|
||||
Where("shop_id = ? AND warehouse_id = ? AND product_id = ? AND quantity > 0 AND deleted_at IS NULL",
|
||||
shopID, warehouseID, productID).
|
||||
Select("COALESCE(SUM(quantity), 0)").Scan(&totalQty)
|
||||
Order("created_at ASC").Find(&batches)
|
||||
|
||||
var totalQty float64
|
||||
for _, b := range batches {
|
||||
totalQty += b.Quantity
|
||||
}
|
||||
if totalQty < needed {
|
||||
return fmt.Errorf("%w: product_id=%d, available=%.3f, required=%.3f",
|
||||
ErrInsufficientStock, productID, totalQty, needed)
|
||||
@@ -176,13 +181,7 @@ func (s *StockService) ApproveStockOut(shopID, orderID, reviewerID uint64) error
|
||||
|
||||
qtyBefore := totalQty
|
||||
|
||||
// 2. FIFO 扣减批次
|
||||
var batches []model.Inventory
|
||||
tx.Set("gorm:query_option", "FOR UPDATE").
|
||||
Where("shop_id = ? AND warehouse_id = ? AND product_id = ? AND quantity > 0 AND deleted_at IS NULL",
|
||||
shopID, warehouseID, productID).
|
||||
Order("created_at ASC").Find(&batches)
|
||||
|
||||
// 2. FIFO 扣减
|
||||
remaining := needed
|
||||
for i := range batches {
|
||||
if remaining <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user