150 lines
4.1 KiB
Go
150 lines
4.1 KiB
Go
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
|
||
}
|