Files
zt-closet-backend/internal/service/adreward.go
T
2026-09-14 02:57:13 +08:00

134 lines
3.9 KiB
Go

package service
import (
"context"
"time"
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/global"
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/dto"
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqldao"
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqlmodel"
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/pkg/errcode"
)
// AdRewardService 广告激励额度服务。
type AdRewardService struct{}
// NewAdRewardService 创建广告激励额度服务。
func NewAdRewardService() *AdRewardService {
return &AdRewardService{}
}
// Grant 发放广告激励额度:校验开关后按服务端配置的固定金额创建当天有效凭证。
// ref_id+quota_type 唯一索引兜底幂等,重复领取返回 AdRewardClaimed。
func (s *AdRewardService) Grant(ctx context.Context, userID int64, request *dto.AdRewardGrantRequest) (*dto.AdRewardGrantResponse, *errcode.Error) {
ctx, span := global.Tracer.Start(ctx, "svc.AdRewardGrant")
defer span.End()
quotaType, ok := parseAdRewardType(request.RewardType)
if !ok {
return nil, errcode.AdRewardInvalid
}
// 开关校验:广告激励未开启时拒绝发放。
if global.AdRewardSetting == nil || !global.AdRewardSetting.Enable {
return nil, errcode.AdRewardDisabled
}
// 服务端定价:发放金额由配置固定,前端仅传类型与广告记录 ID,杜绝刷额度。
amount := adRewardAmount(quotaType)
if amount <= 0 {
return nil, errcode.AdRewardInvalid.WithDetails("该激励类型未配置发放数量")
}
// 变更前后取「有效剩余」,仅用于流水展示;权威数值以额度凭证表为准。
before := int64(0)
if eff, qerr := getEffectiveQuota(ctx, userID, quotaType); qerr == nil {
before = eff.Remain
}
grant := &mysqlmodel.QuotaGrant{
UserID: userID,
QuotaType: quotaType,
Amount: amount,
Used: 0,
Source: QuotaGrantSourceAdReward,
ExpireTime: todayEndUnix(), // 当天 23:59:59 到期
RefID: request.RefID,
}
// 流水 order_no 复用广告记录ID(与凭证 ref_id 一致,便于回溯;非空故不影响唯一索引幂等)。
flow := &mysqlmodel.QuotaFlowLog{
QuotaFlowUUID: genUUID(),
UserID: userID,
ChangeTarget: QuotaChangeTargetAdjust,
ChangeType: QuotaChangeTypeActivity,
ChangeNum: amount,
OldValue: before,
NewValue: before + amount,
OrderNo: request.RefID,
QuotaType: quotaType,
Reason: "广告激励赠送",
}
created, err := mysqldao.NewQuotaGrantDao().QuotaGrantCreateWithFlow(ctx, grant, flow)
if err != nil {
return nil, errcode.DatabaseError
}
if !created {
return nil, errcode.AdRewardClaimed // 凭证已存在:重复领取。
}
return &dto.AdRewardGrantResponse{
RefID: request.RefID,
QuotaType: quotaType,
Amount: amount,
}, nil
}
// adRewardAmount 返回指定额度类型的广告激励固定金额(服务端配置)。
// 未配置(指针为 nil)或配置为非正数时返回 0,表示不发放该类型激励。
func adRewardAmount(quotaType int8) int64 {
cfg := global.AdRewardSetting
if cfg == nil {
return 0
}
var p *int64
switch quotaType {
case QuotaTypeCos:
p = cfg.Cos
case QuotaTypeAI:
p = cfg.AI
case QuotaTypeOutfitShare:
p = cfg.OutfitShare
case QuotaTypeClosetShare:
p = cfg.ClosetShare
default:
return 0
}
if p == nil || *p <= 0 {
return 0
}
return *p
}
// parseAdRewardType 将 reward_type 字符串映射到额度类型。
func parseAdRewardType(rewardType string) (int8, bool) {
switch rewardType {
case "cos":
return QuotaTypeCos, true
case "ai":
return QuotaTypeAI, true
case "outfit_share":
return QuotaTypeOutfitShare, true
case "closet_share":
return QuotaTypeClosetShare, true
default:
return 0, false
}
}
// todayEndUnix 返回当天 23:59:59 的秒级时间戳(广告激励当天有效)。
func todayEndUnix() int64 {
now := time.Now()
return time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()).Unix()
}