package user import ( "context" "fmt" "bindbox-game/internal/repository/mysql/dao" "bindbox-game/internal/repository/mysql/model" ) // BatchRewardItem 批量发放的单个奖励项 type BatchRewardItem struct { ProductID int64 RewardID *int64 // 可选,一番赏模式需要传入以扣减库存 ActivityID int64 Remark string } // BatchGrantRewardsToOrder 批量发放奖励到订单(单事务处理) // 相比循环调用 GrantRewardToOrder,此方法在单个事务内批量插入所有记录,大幅提升性能 func (s *service) BatchGrantRewardsToOrder(ctx context.Context, userID int64, orderID int64, items []BatchRewardItem) ([]int64, error) { if len(items) == 0 { return nil, nil } var inventoryIDs []int64 err := s.writeDB.Transaction(func(tx *dao.Query) error { // 1. 验证订单 order, err := tx.Orders.WithContext(ctx).Where( tx.Orders.ID.Eq(orderID), tx.Orders.UserID.Eq(userID), ).First() if err != nil || order == nil { return fmt.Errorf("订单不存在或不属于该用户") } if order.Status != 2 { return fmt.Errorf("订单未支付或状态异常(status=%d)", order.Status) } // 2. 预加载所有需要的商品信息(避免循环内查询) productIDs := make([]int64, 0, len(items)) for _, item := range items { productIDs = append(productIDs, item.ProductID) } // 去重 productIDSet := make(map[int64]bool) uniqueProductIDs := make([]int64, 0) for _, pid := range productIDs { if !productIDSet[pid] { productIDSet[pid] = true uniqueProductIDs = append(uniqueProductIDs, pid) } } products, err := tx.Products.WithContext(ctx).Where(tx.Products.ID.In(uniqueProductIDs...)).Find() if err != nil { return fmt.Errorf("查询商品失败: %w", err) } productMap := make(map[int64]*model.Products) for _, p := range products { productMap[p.ID] = p } // 3. 批量创建订单项和库存记录 var orderItems []*model.OrderItems var inventories []*model.UserInventory for _, item := range items { product := productMap[item.ProductID] if product == nil { continue // 跳过无效商品 } orderItems = append(orderItems, &model.OrderItems{ OrderID: orderID, ProductID: item.ProductID, Title: product.Name, Quantity: 1, Price: 0, TotalAmount: 0, ProductImages: product.ImagesJSON, Status: 1, }) inventories = append(inventories, &model.UserInventory{ UserID: userID, ProductID: item.ProductID, OrderID: orderID, ActivityID: item.ActivityID, RewardID: func() int64 { if item.RewardID != nil { return *item.RewardID } return 0 }(), Status: 1, Remark: func() string { if item.Remark != "" { return item.Remark } return product.Name }(), }) } // 4. 批量插入(一次数据库操作) if len(orderItems) > 0 { if err := tx.OrderItems.WithContext(ctx).CreateInBatches(orderItems, 100); err != nil { return fmt.Errorf("批量创建订单项失败: %w", err) } } if len(inventories) > 0 { if err := tx.UserInventory.WithContext(ctx).CreateInBatches(inventories, 100); err != nil { return fmt.Errorf("批量创建库存记录失败: %w", err) } for _, inv := range inventories { inventoryIDs = append(inventoryIDs, inv.ID) } } // 5. 处理一番赏库存扣减(按RewardID聚合后批量更新) rewardDeductMap := make(map[int64]int64) for _, item := range items { if item.RewardID != nil { rewardDeductMap[*item.RewardID]++ } } for rewardID, count := range rewardDeductMap { result, err := tx.ActivityRewardSettings.WithContext(ctx).Where( tx.ActivityRewardSettings.ID.Eq(rewardID), tx.ActivityRewardSettings.Quantity.Gte(count), ).UpdateSimple(tx.ActivityRewardSettings.Quantity.Add(-count)) if err != nil { return fmt.Errorf("扣减奖品库存失败: %w", err) } if result.RowsAffected == 0 { return fmt.Errorf("奖品ID=%d库存不足", rewardID) } } return nil }) if err != nil { return nil, err } return inventoryIDs, nil }