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() }