package campaignnotice import ( "joybuywork/internal/app" "joybuywork/internal/consts" "joybuywork/internal/consts/ckey" "joybuywork/internal/repository/cache" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/service/cmm" "joybuywork/internal/service/provider/award" "joybuywork/internal/service/provider/stocks" "joybuywork/internal/service/queue/producer" "joybuywork/internal/service/queue/types" "joybuywork/internal/utils/lockdo" "joybuywork/internal/utils/logic" "time" ) type RewardLimitUp struct { logic.Logic Req *types.RewardLimitUp } func (s *RewardLimitUp) Exec() (err error) { cpn, err := cache.NewCampaign(s.Ctx).FindById(s.Req.CampaignId) if err != nil { err = s.BE().CacheError(err) return } switch cpn.FissionID { case consts.IdReferral, consts.IdWinTogether: app.Redis.Cache.Del(s.Ctx, ckey.FissionCampaignRewardLimitPool(cpn.ID)) stocks.NewCleaner(s.Ctx, cpn.ID).ClearCampaign() default: } switch cpn.FissionID { case consts.IdReferral: return s._referral(cpn) case consts.IdWinTogether: return s._win(cpn) case consts.IdGrow: return s._grow(cpn) default: } return } // 版本控制执行 func (s *RewardLimitUp) _verDone(cpnId int64, fn func() (bool, error)) (err error) { key := ckey.FissionRewardLimitUpVer(cpnId) now := time.Now() for { if app.NewVerControl(s.Ctx, key).HasNewVersion(now) { return } end, err1 := fn() if err1 != nil || end { err = err1 return } } } func (s *RewardLimitUp) _grow(cpn *model.FissionCampaign) (err error) { var id int64 limit := 50 rewardM := s.GetDao().FissionCampaignReward rewardList, err := rewardM.WithContext(s.Ctx).Where(rewardM.ID.In(s.Req.RewardIds...)).Find() if err != nil { err = s.BE().MysqlError(err) return } var ids []int64 var rewardMap = make(map[int64]*model.FissionCampaignReward) for _, reward := range rewardList { rewardMap[reward.ID] = reward ids = append(ids, reward.ID) } m := s.GetDao().FissionRewardRecord ms := s.GetDao().FissionRewardRecord.SubTable(cpn.ID) return s._verDone(cpn.ID, func() (bool, error) { var list []*model.FissionRewardRecord list, err = ms.WithContext(s.Ctx). Where( ms.CampaignID.Eq(cpn.ID), ms.RewardID.In(ids...), ms.Status.Eq(mtype.FissionRewardRecordStatusFail), ms.FailMsg.Eq(mtype.FissionRewardRecordFailTypeCliLackMsg), ms.ID.Gt(id), ).Limit(limit).Order(ms.ID).Find() if err != nil { return false, s.BE().MysqlError(err) } if len(list) == 0 { return true, nil } var recordIds []int64 var pushRecords []*model.FissionRewardRecord for _, record := range list { ok, _ := award.NewAwardIssVerify(s.Ctx, nil, rewardMap[record.RewardID]).DeductStock().IsOK() if !ok { continue } recordIds = append(recordIds, record.ID) pushRecords = append(pushRecords, record) } if len(recordIds) == 0 { return true, nil } _, _ = m.WithContext(s.Ctx).Where(m.ID.In(recordIds...)).UpdateSimple( m.Status.Value(mtype.FissionRewardRecordStatusWait), m.FailType.Value(""), m.FailMsg.Value(""), ) _, _ = ms.WithContext(s.Ctx).Where(ms.ID.In(recordIds...)).UpdateSimple( ms.Status.Value(mtype.FissionRewardRecordStatusWait), ms.FailType.Value(""), ms.FailMsg.Value(""), ) for _, record := range pushRecords { producer.NewCampaignNotice(s.Ctx).CampaignRewardNotify(types.CampaignRewardNotify{ CampaignId: record.CampaignID, UserId: record.UserID, UserRewardId: record.ID, }) } l := len(list) id = list[l-1].ID return l < limit, nil }) } func (s *RewardLimitUp) _referral(cpn *model.FissionCampaign) (err error) { conf, err := cache.NewCampaignReferral(s.Ctx).FindByCampaignId(cpn.ID) if err != nil { err = s.BE().CacheError(err) return } var id int64 limit := 20 m := s.GetDao().FissionReferral return s._verDone(cpn.FissionID, func() (bool, error) { var list []*model.FissionReferral list, err = m.WithContext(s.Ctx). Where( m.CampaignID.Eq(cpn.ID), m.LimitStatus.Eq(mtype.FissionReferralLimitStatusBudget), m.ID.Gt(id), ).Limit(limit).Order(m.ID).Find() if err != nil { return false, s.BE().MysqlError(err) } if len(list) == 0 { return true, nil } var userId []int64 for _, v := range list { userId = append(userId, v.UserID) } userM := s.GetDao().FissionUser.SubTable(cpn.ID) var users []*model.FissionUser users, err = userM.WithContext(s.Ctx).Where(userM.ID.In(userId...)).Find() if err != nil { return false, s.BE().MysqlError(err) } var userMap = make(map[int64]*model.FissionUser, len(users)) for _, v := range users { userMap[v.ID] = v } for _, v := range list { err = s._compensate(cpn, userMap[v.UserID], conf.RewardRule, conf.StepLoop, conf.RewardLimit, v.NextStep, v.LoopInvite, func(limitStatus int8, nextStep, loopInvite int32) (err error) { playerM := s.GetDao().FissionReferral _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(v.ID)).UpdateSimple(playerM.LimitStatus.Value(limitStatus)) if err != nil { err = s.BE().MysqlError(err) return } return }) } l := len(list) id = list[l-1].ID return l < limit, nil }) } func (s *RewardLimitUp) _win(cpn *model.FissionCampaign) (err error) { conf, err := cache.NewCampaignWin(s.Ctx).FindByCampaignId(cpn.ID) if err != nil { err = s.BE().CacheError(err) return } var id int64 limit := 20 m := s.GetDao().FissionWin return s._verDone(cpn.FissionID, func() (bool, error) { var list []*model.FissionWin list, err = m.WithContext(s.Ctx). Where( m.CampaignID.Eq(cpn.ID), m.LimitStatus.Eq(mtype.FissionReferralLimitStatusBudget), m.ID.Gt(id), ).Limit(limit).Order(m.ID).Find() if err != nil { return false, s.BE().MysqlError(err) } if len(list) == 0 { return true, nil } var userId []int64 for _, v := range list { userId = append(userId, v.UserID) } userM := s.GetDao().FissionUser.SubTable(cpn.ID) var users []*model.FissionUser users, err = userM.WithContext(s.Ctx).Where(userM.ID.In(userId...)).Find() if err != nil { return false, s.BE().MysqlError(err) } var userMap = make(map[int64]*model.FissionUser, len(users)) for _, v := range users { userMap[v.ID] = v } for _, v := range list { err = s._compensate(cpn, userMap[v.UserID], conf.RewardRule, conf.StepLoop, conf.RewardLimit, v.NextStep, v.LoopInvite, func(limitStatus int8, nextStep, loopInvite int32) (err error) { playerM := s.GetDao().FissionWin _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(v.ID)).UpdateSimple(playerM.LimitStatus.Value(limitStatus)) if err != nil { err = s.BE().MysqlError(err) return } return }) } l := len(list) id = list[l-1].ID return l < limit, nil }) } func (s *RewardLimitUp) _compensate(cpn *model.FissionCampaign, user *model.FissionUser, rewardRule, stepLoop int8, rewardLimit, nextStep, loopInvite int32, limitDo limitDo) (err error) { rewards, err := cmm.NewUserAppReward(s.Ctx).Get(s.GetDao(), user, s.GetDao().FissionCampaignReward.Step) if err != nil { err = s.BE().MysqlError(err) return } m := s.GetDao().FissionUser.SubTable(user.CampaignID) statKey := ckey.FissionUserInviteStat(user.ID) num, err := lockdo.Int64(s.Ctx, statKey, func() (num int64, err error) { return m.WithContext(s.Ctx).Where( m.CampaignID.Eq(user.CampaignID), m.ParentID.Eq(user.ID), m.IsHelp.Eq(mtype.Yes), ).Count() }) if err != nil { err = s.BE().CacheError(err) return } switch rewardRule { case mtype.FissionReferralRewardRuleStep: return s._step(cpn, user, num, rewards, stepLoop, nextStep, loopInvite, rewardLimit, limitDo) case mtype.FissionReferralRewardRuleCycle: return s._cycle(cpn, user, num, rewards, rewardLimit, limitDo) } return } type limitDo func(limitStatus int8, nextStep, loopInvite int32) (err error) func (s *RewardLimitUp) _step(cpn *model.FissionCampaign, user *model.FissionUser, effectNum int64, rewards []*model.FissionCampaignReward, stepLoop int8, nextStep, loopInvite, rewardLimit int32, limitDo limitDo) (err error) { var stepMap = make(map[int32]*model.FissionCampaignReward, len(rewards)) var inviteAll int32 var maxStep int32 for _, reward := range rewards { inviteAll += reward.InviteNum stepMap[reward.Step] = reward if reward.Step > maxStep { maxStep = reward.Step } } for { if stepLoop == mtype.Yes && (loopInvite*inviteAll) >= rewardLimit { return limitDo(mtype.FissionReferralLimitStatusInvite, nextStep, loopInvite) } cn := int32(effectNum) - (loopInvite * inviteAll) reward, ok := stepMap[nextStep] if !ok { return limitDo(mtype.FissionReferralLimitStatusFinish, 0, loopInvite) } if reward.InviteNum > cn { return limitDo(mtype.FissionReferralLimitStatusNone, reward.Step, loopInvite) } var decr = int64(reward.RewardNum) //奖品数据 //扣除总预算是否成功 var stock *stocks.Campaign stock, err = stocks.NewCampaign(s.Ctx, cpn.ID) if err != nil { return } decrOk, _ := stock.DecrPoolAndAdd(cpn.ID, decr) if !decrOk { return limitDo(mtype.FissionReferralLimitStatusBudget, reward.Step, loopInvite) } _, _, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), cpn.ProjectID, user).One(reward, true) if err != nil { return } nextStep++ if nextStep > maxStep && stepLoop == mtype.Yes { nextStep = 1 loopInvite++ } } } func (s *RewardLimitUp) _cycle(cpn *model.FissionCampaign, user *model.FissionUser, effectNum int64, rewards []*model.FissionCampaignReward, rewardLimit int32, limitDo limitDo) (err error) { key := ckey.FissionUserGetRewardNum(user.ID) num, err := lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionRewardRecord.SubTable(cpn.ID) return m.WithContext(s.Ctx).Where( m.UserID.Eq(user.ID), m.RewardType.Eq(mtype.FissionCampaignRewardTypeApp), ).Count() }) if err != nil { err = s.BE().CacheError(err) return } reward := rewards[0] var needInvite = int64(reward.InviteNum) //完成任务需要邀请的人数 var decr = int64(reward.RewardNum) //奖品数据 for { cn := effectNum - (num * needInvite) //总邀请人数 -(完成任务数 * 完成任务需要邀请的人数)= 计算完成任务的邀请人数 //邀请的人不足以获取到奖品 if cn < needInvite { return limitDo(mtype.FissionReferralLimitStatusNone, -1, 0) } if (num * needInvite) >= int64(rewardLimit) { return limitDo(mtype.FissionReferralLimitStatusInvite, -1, 0) } //扣除总预算是否成功 var stock *stocks.Campaign stock, err = stocks.NewCampaign(s.Ctx, cpn.ID) if err != nil { return } decrOk, _ := stock.DecrPoolAndAdd(cpn.ID, decr) if !decrOk { return limitDo(mtype.FissionReferralLimitStatusBudget, -1, 0) } _, _, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), cpn.ProjectID, user).One(reward, true) if err != nil { return } // 领奖数+1 app.Redis.Cache.Incr(s.Ctx, key) num++ } }