package validevents import ( "joybuywork/internal/app" "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/utils/lockdo" "time" "gits.branchcn.com/backend/microcode/plugins/derivative/lock" "gorm.io/gen/field" ) type referral struct { base } func (s *referral) Event(user *model.FissionUser) (targetNum int32, setHelp bool, err error) { fc := cmm.NewFirstCreate(s.Ctx, s.GetDao()) player, err := fc.FissionReferral(user) if err != nil { return } data, err := s._event(user) if err != nil { return } targetNum = data.targetNum if data.targetNum == 0 || user.IsHelp == mtype.Yes { return } ticket, err := cache.NewCampaignTicket(s.Ctx).FindByMark(user.CampaignID, mtype.FissionCampaignTicketCustom) if err != nil { err = s.BE().CacheError(err) return } var preOk = true switch ticket.PreEvent { case mtype.FissionCampaignTicketPreEventInstall: preOk = player.BindInstall == mtype.Yes case mtype.FissionCampaignTicketPreEventRegister: preOk = player.BindRegister == mtype.Yes } if !preOk { return } setHelp = true err = s._eventHelp(user) if err != nil { return } return } func (s *referral) Register(helper, beHelper *model.FissionUser) (setHelp bool, err error) { player, err := cmm.NewFirstCreate(s.Ctx, s.GetDao()).FissionReferral(helper) if err != nil { return } if player.BindRegister == mtype.Yes { return } m := s.GetDao().FissionReferral _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.BindRegister.Value(mtype.Yes)) if err != nil { err = s.BE().MysqlError(err) return } tasks, setHelp, err := s._getRegisterTasks(helper.CampaignID) if err != nil || len(tasks) == 0 { return } err = s._fixTaskDo(beHelper, tasks) if err != nil { return } return } func (s *referral) Install(helper, beHelper *model.FissionUser) (setHelp bool, err error) { player, err := cmm.NewFirstCreate(s.Ctx, s.GetDao()).FissionReferral(helper) if err != nil { return } if player.BindInstall == mtype.Yes { return } m := s.GetDao().FissionReferral _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.BindInstall.Value(mtype.Yes)) if err != nil { err = s.BE().MysqlError(err) return } tasks, setHelp, err := s._getInstallTasks(helper.CampaignID) if err != nil || len(tasks) == 0 { return } err = s._fixTaskDo(beHelper, tasks) if err != nil { return } return } type referralStepDo func(player *model.FissionReferral, conf *model.FissionCampaignReferral, reward, nextReward *model.FissionCampaignReward, allInviteNum int32) (err error) type referralCycleDo func(player *model.FissionReferral, conf *model.FissionCampaignReferral, reward *model.FissionCampaignReward) (err error) type referralDo struct { step referralStepDo cycle referralCycleDo } func (s *referral) _do(user *model.FissionUser, do referralDo) (err error) { //防止同一个人并发发礼物超发 lc := app.NewRedisLock(s.Ctx, ckey.FissionParentInviteOnce(user.ID), lock.SetExpire(10*time.Second)).Lock() defer lc.Release() if lc.Error() != nil { return } fc := cmm.NewFirstCreate(s.Ctx, s.GetDao()) player, err := fc.FissionReferral(user) if err != nil { return } // 被限制了 if player.LimitStatus != mtype.FissionReferralLimitStatusNone { return } conf, err := cache.NewCampaignReferral(s.Ctx).FindByCampaignId(player.CampaignID) if err != nil { err = s.BE().CacheError(err) return } m := s.GetDao().FissionCampaignReward rewards, err := cmm.NewCMM(s.Ctx, s.GetDao()).UserAppReward(user, m.Step) if err != nil { err = s.BE().MysqlError(err) return } if conf.RewardRule == mtype.FissionReferralRewardRuleStep { var allInviteNum int32 for _, campaignReward := range rewards { if campaignReward.InviteNum > allInviteNum { allInviteNum = campaignReward.InviteNum } } rewardL := len(rewards) var reward, nextReward *model.FissionCampaignReward for i, campaignReward := range rewards { if campaignReward.Step == player.NextStep { reward = campaignReward if i < rewardL-1 { nextReward = rewards[i+1] } break } } if nextReward == nil && conf.StepLoop == mtype.Yes { nextReward = rewards[0] } return do.step(player, conf, reward, nextReward, allInviteNum) } else if conf.RewardRule == mtype.FissionReferralRewardRuleCycle { return do.cycle(player, conf, rewards[0]) } return } func (s *referral) _eventHelp(helper *model.FissionUser) (err error) { // 找被助力的人 user, err := cache.NewFissionUser(s.Ctx, helper.CampaignID).FindById(helper.ParentID) if err != nil { err = s.BE().CacheError(err) return } if user == nil { return } effectNum, err := s._statDirectNum(user.CampaignID, user.ID) if err != nil { return } do := referralDo{ step: func(player *model.FissionReferral, conf *model.FissionCampaignReferral, reward, nextReward *model.FissionCampaignReward, allInviteNum int32) (err error) { return s._step(user, effectNum, player, conf, reward, nextReward, allInviteNum) }, cycle: func(player *model.FissionReferral, conf *model.FissionCampaignReferral, reward *model.FissionCampaignReward) (err error) { err = s._cycle(user, effectNum, player, conf, reward) if err != nil { return } return s._extra(user, player, effectNum) }, } return s._do(user, do) } // 用户固定事件任务 func (s *referral) _fixTaskDo(user *model.FissionUser, tasks []*model.FissionCampaignTask) (err error) { do := referralDo{ step: func(player *model.FissionReferral, conf *model.FissionCampaignReferral, reward, nextReward *model.FissionCampaignReward, allInviteNum int32) (err error) { for _, task := range tasks { switch task.PropMark { case mtype.FissionCampaignTaskPropMarkChance: err = s._taskEffectDo(user, task, func() (err error) { return s._step(user, s.effectNum, player, conf, reward, nextReward, allInviteNum) }) if err != nil { return } } } return }, cycle: func(player *model.FissionReferral, conf *model.FissionCampaignReferral, reward *model.FissionCampaignReward) (err error) { for _, task := range tasks { switch task.PropMark { case mtype.FissionCampaignTaskPropMarkChance: err = s._taskEffectDo(user, task, func() (err error) { err = s._cycle(user, s.effectNum, player, conf, reward) if err != nil { return } return s._extra(user, player, s.effectNum) }) if err != nil { return } } } return }, } return s._do(user, do) } // 增加抽奖次数 func (s *referral) _step(user *model.FissionUser, effectNum int64, player *model.FissionReferral, conf *model.FissionCampaignReferral, reward, nextReward *model.FissionCampaignReward, allInviteNum int32) (err error) { var limitStatus int8 defer func() { if err != nil { return } err = s._limitStatus(player.ID, limitStatus) }() if conf.StepLoop == mtype.Yes && effectNum > int64(conf.InviteLimit) { limitStatus = mtype.FissionReferralLimitStatusInvite return } //没有下一级任务 if reward == nil { limitStatus = mtype.FissionReferralLimitStatusFinish return } var inviteNum = effectNum - int64(player.LoopInvite*allInviteNum) //没有完成 if int64(reward.InviteNum) > inviteNum { return } var decr = int64(reward.RewardNum) //扣除总预算是否成功 var stock *stocks.Campaign stock, err = stocks.NewCampaign(s.Ctx, player.CampaignID) if err != nil { return } ok, _ := stock.DecrPoolAndAdd(player.CampaignID, decr) if !ok { limitStatus = mtype.FissionReferralLimitStatusBudget return } _, _, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), s.cpn.ProjectID, user).One(reward, true) if err != nil { return } if nextReward == nil { limitStatus = mtype.FissionReferralLimitStatusFinish return } // 初始化奖品的东西 playerM := s.GetDao().FissionReferral var up []field.AssignExpr up = append(up, playerM.NextStep.Value(nextReward.Step)) if nextReward.Step <= reward.Step { up = append(up, playerM.LoopInvite.Add(1)) } _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).UpdateSimple(up...) if err != nil { err = s.BE().MysqlError(err) return } return } // 增加抽奖次数 func (s *referral) _cycle(user *model.FissionUser, effectNum int64, player *model.FissionReferral, conf *model.FissionCampaignReferral, reward *model.FissionCampaignReward) (err error) { var limitStatus int8 defer func() { if err != nil { return } err = s._limitStatus(player.ID, limitStatus) }() if effectNum > int64(conf.InviteLimit) { limitStatus = mtype.FissionReferralLimitStatusInvite return } key := ckey.FissionUserGetRewardNum(user.ID) num, err := lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionRewardRecord.SubTable(user.CampaignID) 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 } var needInvite = int64(reward.InviteNum) //完成任务需要邀请的人数 var decr = int64(reward.RewardNum) //奖品数据 cn := effectNum - (num * needInvite) //总邀请人数 -(完成任务数 * 完成任务需要邀请的人数)= 计算完成任务的邀请人数 //邀请的人不足以获取到奖品 if cn < needInvite { return } //扣除总预算是否成功 var stock *stocks.Campaign stock, err = stocks.NewCampaign(s.Ctx, player.CampaignID) if err != nil { return } ok, _ := stock.DecrPoolAndAdd(player.CampaignID, decr) if !ok { limitStatus = mtype.FissionReferralLimitStatusBudget return } _, _, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), s.cpn.ProjectID, user).One(reward, true) if err != nil { return } // 领奖数+1 app.Redis.Cache.Incr(s.Ctx, key) return } func (s *referral) _limitStatus(playerId int64, ls int8) (err error) { if ls == mtype.FissionReferralLimitStatusNone { return } m := s.GetDao().FissionReferral var up = []field.AssignExpr{ m.LimitStatus.Value(ls), } if ls == mtype.FissionReferralLimitStatusFinish { up = append(up, m.NextStep.Value(0)) } _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(playerId)).UpdateSimple(up...) if err != nil { err = s.BE().MysqlError(err) return } return } // 额外 func (s *referral) _extra(user *model.FissionUser, player *model.FissionReferral, effectNum int64) (err error) { m := s.GetDao().FissionCampaignReward var extras []*model.FissionCampaignReward var extraReward, nextExtraReward *model.FissionCampaignReward extras, err = m.WithContext(s.Ctx).Where( m.CampaignID.Eq(user.CampaignID), m.RewardType.Eq(mtype.FissionCampaignRewardTypeExtra), m.IsDel.Eq(mtype.No), ).Find() if err != nil { err = s.BE().MysqlError(err) return } rewardL := len(extras) for i, extra := range extras { if extra.Step == player.NextStep { extraReward = extra if i < rewardL-1 { nextExtraReward = extras[i+1] } break } } if extraReward == nil { return } //没有达标任务 if int64(extraReward.InviteNum) > effectNum { return } // 额外奖品 _, _, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), s.cpn.ProjectID, user).One(extraReward, true) if err != nil { return } var nextStep int32 if nextExtraReward != nil { nextStep = nextExtraReward.Step } // 下次的额外奖 playerM := s.GetDao().FissionReferral _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).UpdateSimple(playerM.NextStep.Value(nextStep)) if err != nil { err = s.BE().MysqlError(err) return } return }