From 17c9bd024a1d89fc1d4010855e3b1f5f7bcb1615 Mon Sep 17 00:00:00 2001 From: xiaodongzhu825 <97694732@qq.com> Date: Thu, 18 Jun 2026 11:42:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86CreateSalesOrderWith?= =?UTF-8?q?Detail=E5=92=8CGetProductInventory=E4=B8=A4=E4=B8=AA=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=9A=84=E5=BA=93=E5=AD=98=E6=9F=A5=E8=AF=A2=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E4=B8=8D=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/process.go | 79 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/service/process.go b/service/process.go index 6a95e36..d060a76 100644 --- a/service/process.go +++ b/service/process.go @@ -1476,9 +1476,15 @@ func (s *ProcessService) CreateSalesOrderWithDetail(req systemReq.SalesOrderCrea return fmt.Errorf("批量创建销售订单明细失败: %v", err) } + // 锁定库存 + //for _, itemReq := range req.Items { + // if err := s.lockInventory(tx, invWarehouseID, itemReq.ProductID, itemReq.Quantity, now); err != nil { + // return fmt.Errorf("锁定库存失败[商品ID=%d]: %v", itemReq.ProductID, err) + // } + //} // 锁定库存 for _, itemReq := range req.Items { - if err := s.lockInventory(tx, invWarehouseID, itemReq.ProductID, itemReq.Quantity, now); err != nil { + if err := s.lockInventoryByAppearance(tx, invWarehouseID, itemReq.ProductID, itemReq.Quantity, now); err != nil { return fmt.Errorf("锁定库存失败[商品ID=%d]: %v", itemReq.ProductID, err) } } @@ -3652,6 +3658,77 @@ func (s *ProcessService) processInventoryDetailOperationForAdjustment(tx *gorm.D return nil } +// lockInventoryByAppearance 按品相+ISBN匹配所有商品的库存进行锁定 +func (s *ProcessService) lockInventoryByAppearance(tx *gorm.DB, warehouseID, productID, quantity int64, now int64) error { + var product models.Product + if err := tx.Where("id = ? AND is_del = 0", productID).First(&product).Error; err != nil { + return fmt.Errorf("查询商品信息失败: %v", err) + } + + var matchingProductIDs []int64 + if err := tx.Model(&models.Product{}). + Where("barcode = ? AND appearance = ? AND is_del = 0", product.Barcode, product.Appearance). + Pluck("id", &matchingProductIDs).Error; err != nil { + return fmt.Errorf("查询同品相商品失败: %v", err) + } + + if len(matchingProductIDs) == 0 { + return fmt.Errorf("无匹配的商品记录") + } + + var inventories []models.Inventory + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("warehouse_id = ? AND product_id IN ? AND is_del = 0 AND quantity > locked_quantity", + warehouseID, matchingProductIDs). + Order("product_id ASC, expiry_date ASC, created_at ASC"). + Find(&inventories).Error; err != nil { + return fmt.Errorf("查询可用库存失败: %v", err) + } + + if len(inventories) == 0 { + return fmt.Errorf("商品(barcode=%s,appearance=%d)在仓库ID=%d中无可用库存", product.Barcode, product.Appearance, warehouseID) + } + + remainingLock := quantity + for i := range inventories { + if remainingLock <= 0 { + break + } + + availableQty := inventories[i].Quantity - inventories[i].LockedQuantity + if availableQty <= 0 { + continue + } + + lockQty := availableQty + if lockQty > remainingLock { + lockQty = remainingLock + } + + result := tx.Model(&inventories[i]). + Where("quantity - locked_quantity >= ?", lockQty). + UpdateColumns(map[string]interface{}{ + "locked_quantity": gorm.Expr("locked_quantity + ?", lockQty), + "updated_at": now, + }) + + if result.Error != nil { + return fmt.Errorf("锁定库存失败: %v", result.Error) + } + if result.RowsAffected == 0 { + return fmt.Errorf("库存已被其他事务修改,请重试") + } + + remainingLock -= lockQty + } + + if remainingLock > 0 { + return fmt.Errorf("可用库存不足,还需锁定:%d", remainingLock) + } + + return nil +} + // 执行事务(使用指定数据库) func executeInTransactionWithDB(db *gorm.DB, txFunc func(*gorm.DB) error) error { tx := db.Begin()