fix(refund): 修复退款后翻牌游戏资格未回收的问题
问题描述: 用户退单后,翻牌游戏资格会重新出现(被重置),但用户已经抽过奖了。 这导致用户可以通过退款获得额外的翻牌机会。 根本原因: 退款处理逻辑 reclaimLivestreamAssets 只回收了 user_inventory 中的实物奖品, 但没有回收 user_game_tickets 中的翻牌游戏资格。 解决方案: 在 reclaimLivestreamAssets 函数后添加 reclaimFlipCardTicket 函数, 用于检测并回收翻牌游戏资格: 1. 通过 shop_order_id 查询抖店订单获取商品ID 2. 查询 douyin_product_rewards 表检查商品是否配置了翻牌游戏奖励 - 检查 reward_type = 'game_ticket' - 检查 payload.game_code = 'flip_card' 3. 如果配置了翻牌奖励,回收用户的翻牌次数 - 扣减 user_game_tickets.available - 扣减 user_game_tickets.total_earned 4. 在 game_ticket_logs 表中记录回收日志 影响范围: - 仅影响配置了翻牌游戏奖励的商品订单退款 - 退款时会同步回收翻牌游戏资格 - 已使用过的翻牌次数不会被回收(只回收 available > 0 的记录) 测试建议: 1. 购买配置了翻牌奖励的商品 2. 进行翻牌游戏 3. 申请退款 4. 验证翻牌资格是否被正确回收
This commit is contained in:
parent
2aa7cdbd61
commit
d45096d13f
@ -1,6 +1,7 @@
|
|||||||
package douyin
|
package douyin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"bindbox-game/internal/pkg/logger"
|
"bindbox-game/internal/pkg/logger"
|
||||||
@ -419,6 +420,113 @@ func (s *service) reclaimLivestreamAssets(ctx context.Context, log *model.Livest
|
|||||||
// 3. 恢复奖品库存
|
// 3. 恢复奖品库存
|
||||||
db.Exec("UPDATE livestream_prizes SET remaining = remaining + 1 WHERE id = ? AND remaining >= 0", log.PrizeID)
|
db.Exec("UPDATE livestream_prizes SET remaining = remaining + 1 WHERE id = ? AND remaining >= 0", log.PrizeID)
|
||||||
s.logger.Info("[资产回收] 恢复奖品库存", zap.Int64("prize_id", log.PrizeID))
|
s.logger.Info("[资产回收] 恢复奖品库存", zap.Int64("prize_id", log.PrizeID))
|
||||||
|
|
||||||
|
// 4. 回收翻牌游戏资格(如果奖品配置了翻牌游戏)
|
||||||
|
s.reclaimFlipCardTicket(ctx, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reclaimFlipCardTicket 回收翻牌游戏资格
|
||||||
|
// 逻辑:如果订单对应的商品配置了翻牌游戏奖励,需要回收用户的翻牌次数
|
||||||
|
func (s *service) reclaimFlipCardTicket(ctx context.Context, log *model.LivestreamDrawLogs) {
|
||||||
|
db := s.repo.GetDbW().WithContext(ctx)
|
||||||
|
|
||||||
|
// 1. 查找对应的抖店订单,获取商品ID
|
||||||
|
var order model.DouyinOrders
|
||||||
|
if err := s.repo.GetDbR().Where("shop_order_id = ?", log.ShopOrderID).First(&order).Error; err != nil {
|
||||||
|
s.logger.Warn("[翻牌回收] 查询订单失败", zap.String("shop_order_id", log.ShopOrderID), zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if order.DouyinProductID == "" {
|
||||||
|
return // 订单没有商品ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查询该商品是否配置了翻牌游戏奖励
|
||||||
|
var rewards []model.DouyinProductRewards
|
||||||
|
if err := s.repo.GetDbR().Where("product_id = ? AND status = 1", order.DouyinProductID).Find(&rewards).Error; err != nil {
|
||||||
|
s.logger.Warn("[翻牌回收] 查询奖励配置失败", zap.String("product_id", order.DouyinProductID), zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查是否有翻牌游戏奖励
|
||||||
|
hasFlipCard := false
|
||||||
|
for _, reward := range rewards {
|
||||||
|
if reward.RewardType == "game_ticket" && reward.RewardPayload != "" {
|
||||||
|
var payload struct {
|
||||||
|
GameCode string `json:"game_code"`
|
||||||
|
}
|
||||||
|
_ = json.Unmarshal([]byte(reward.RewardPayload), &payload)
|
||||||
|
if payload.GameCode == "flip_card" {
|
||||||
|
hasFlipCard = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasFlipCard {
|
||||||
|
return // 没有配置翻牌游戏奖励
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 回收翻牌游戏资格
|
||||||
|
// 查找用户的翻牌游戏资格记录
|
||||||
|
var ticket model.UserGameTickets
|
||||||
|
err := s.repo.GetDbR().Where("user_id = ? AND game_code = ?", log.LocalUserID, "flip_card").First(&ticket).Error
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Warn("[翻牌回收] 用户没有翻牌资格记录",
|
||||||
|
zap.Int64("user_id", log.LocalUserID),
|
||||||
|
zap.String("shop_order_id", log.ShopOrderID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有可用次数
|
||||||
|
if ticket.Available <= 0 {
|
||||||
|
s.logger.Info("[翻牌回收] 用户翻牌次数已为0,无需回收",
|
||||||
|
zap.Int64("user_id", log.LocalUserID),
|
||||||
|
zap.Int32("available", ticket.Available))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扣减翻牌次数
|
||||||
|
result := db.Model(&model.UserGameTickets{}).
|
||||||
|
Where("user_id = ? AND game_code = ? AND available > 0", log.LocalUserID, "flip_card").
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"available": gorm.Expr("available - 1"),
|
||||||
|
"total_earned": gorm.Expr("total_earned - 1"),
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if result.Error != nil {
|
||||||
|
s.logger.Error("[翻牌回收] 扣减翻牌次数失败",
|
||||||
|
zap.Error(result.Error),
|
||||||
|
zap.Int64("user_id", log.LocalUserID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
s.logger.Warn("[翻牌回收] 扣减翻牌次数失败,可能次数不足",
|
||||||
|
zap.Int64("user_id", log.LocalUserID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
logEntry := &model.GameTicketLogs{
|
||||||
|
UserID: log.LocalUserID,
|
||||||
|
GameCode: "flip_card",
|
||||||
|
ChangeType: 3, // 3=扣除/回收
|
||||||
|
Amount: -1,
|
||||||
|
Balance: ticket.Available - 1,
|
||||||
|
Source: "refund_reclaim",
|
||||||
|
SourceID: log.ID,
|
||||||
|
Remark: fmt.Sprintf("订单退款回收翻牌资格 (订单: %s)", log.ShopOrderID),
|
||||||
|
}
|
||||||
|
if err := db.Create(logEntry).Error; err != nil {
|
||||||
|
s.logger.Error("[翻牌回收] 记录日志失败", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("[翻牌回收] 成功回收翻牌资格",
|
||||||
|
zap.Int64("user_id", log.LocalUserID),
|
||||||
|
zap.String("shop_order_id", log.ShopOrderID),
|
||||||
|
zap.Int32("remaining", ticket.Available-1))
|
||||||
}
|
}
|
||||||
|
|
||||||
type activityAttribution struct {
|
type activityAttribution struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user