bindbox-game/internal/service/douyin/reward_dispatcher.go

148 lines
4.1 KiB
Go
Executable File
Raw 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 douyin
import (
"context"
"encoding/json"
"fmt"
"time"
"bindbox-game/internal/repository/mysql/model"
"bindbox-game/internal/service/game"
"bindbox-game/internal/service/user"
)
// TitleAssigner 称号发放接口(只需要 AssignUserTitle 方法)
type TitleAssigner interface {
AssignUserTitle(ctx context.Context, userID int64, titleID int64, expiresAt *time.Time, remark string) error
}
// RewardPayload 奖励参数结构
type RewardPayload struct {
// game_ticket
GameCode string `json:"game_code,omitempty"`
// points
Points int64 `json:"points,omitempty"`
// coupon
CouponID int64 `json:"coupon_id,omitempty"`
// product
ProductID int64 `json:"product_id,omitempty"`
// item_card
CardID int64 `json:"card_id,omitempty"`
// title
TitleID int64 `json:"title_id,omitempty"`
}
// RewardDispatcher 奖励发放器
type RewardDispatcher struct {
ticketSvc game.TicketService
userSvc user.Service
titleSvc TitleAssigner
}
// NewRewardDispatcher 创建奖励发放器
func NewRewardDispatcher(ticketSvc game.TicketService, userSvc user.Service, titleSvc TitleAssigner) *RewardDispatcher {
return &RewardDispatcher{
ticketSvc: ticketSvc,
userSvc: userSvc,
titleSvc: titleSvc,
}
}
// GrantReward 根据奖励配置发放奖励
// userID: 用户ID
// reward: 奖励配置
// productCount: 商品数量(会乘以 reward.Quantity
// source: 来源标识
// sourceID: 来源ID如订单ID
// extOrderID: 外部订单号(如抖店单号)
func (d *RewardDispatcher) GrantReward(ctx context.Context, userID int64, reward model.DouyinProductRewards, productCount int, source string, sourceID int64, extOrderID string) error {
if reward.Status != 1 {
return nil // 规则未启用,跳过
}
// 解析 payload
var payload RewardPayload
if reward.RewardPayload != "" {
_ = json.Unmarshal([]byte(reward.RewardPayload), &payload)
}
totalQuantity := int(reward.Quantity) * productCount
if totalQuantity <= 0 {
totalQuantity = productCount
}
remark := fmt.Sprintf("购买商品奖励 (规则ID: %d)", reward.ID)
switch reward.RewardType {
case "game_ticket":
// 发放游戏资格
gameCode := payload.GameCode
if gameCode == "" {
gameCode = "minesweeper" // 默认扫雷
}
return d.ticketSvc.GrantTicket(ctx, userID, gameCode, totalQuantity, source, sourceID, remark)
case "points":
// 发放积分
points := payload.Points
if points <= 0 {
points = 1
}
totalPoints := points * int64(totalQuantity)
return d.userSvc.AddPointsWithAction(ctx, userID, totalPoints, "order_reward", remark, "douyin_product_reward", nil, nil)
case "coupon":
// 发放优惠券
if payload.CouponID <= 0 {
return fmt.Errorf("coupon_id not configured")
}
for i := 0; i < totalQuantity; i++ {
if err := d.userSvc.AddCoupon(ctx, userID, payload.CouponID); err != nil {
return err
}
}
return nil
case "product":
// 发放商品到用户库存
if payload.ProductID <= 0 {
return fmt.Errorf("product_id not configured")
}
_, err := d.userSvc.GrantReward(ctx, userID, user.GrantRewardRequest{
ProductID: payload.ProductID,
Quantity: totalQuantity,
Remark: remark,
ExtOrderID: extOrderID,
})
return err
case "item_card":
// 发放道具卡
if payload.CardID <= 0 {
return fmt.Errorf("card_id not configured")
}
return d.userSvc.AddItemCard(ctx, userID, payload.CardID, totalQuantity)
case "title":
// 发放称号(称号不支持数量叠加,只发一次)
if payload.TitleID <= 0 {
return fmt.Errorf("title_id not configured")
}
return d.titleSvc.AssignUserTitle(ctx, userID, payload.TitleID, nil, remark)
default:
return fmt.Errorf("unknown reward type: %s", reward.RewardType)
}
}
// IsFlipCardReward 判断是否为翻牌游戏奖励(需要特殊处理,不自动发放)
func (d *RewardDispatcher) IsFlipCardReward(reward model.DouyinProductRewards) bool {
if reward.RewardType != "game_ticket" {
return false
}
var payload RewardPayload
if reward.RewardPayload != "" {
_ = json.Unmarshal([]byte(reward.RewardPayload), &payload)
}
return payload.GameCode == "flip_card"
}