bindbox-game/internal/service/user/reward_grant_batch.go

150 lines
4.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}