148 lines
4.1 KiB
Go
Executable File
148 lines
4.1 KiB
Go
Executable File
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"
|
||
}
|