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" "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 win struct { base } func (s *win) Event(user *model.FissionUser) (targetNum int32, setHelp bool, err error) { fc := cmm.NewFirstCreate(s.Ctx, s.GetDao()) player, err := fc.FissionWin(user) if err != nil { return } defer func() { if err != nil { return } err = s._newcomerReward(user, player.ID, setHelp) }() 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 *win) Register(helper, beHelper *model.FissionUser) (setHelp bool, err error) { player, err := cmm.NewFirstCreate(s.Ctx, s.GetDao()).FissionWin(helper) if err != nil { return } defer func() { if err != nil { return } err = s._newcomerReward(helper, player.ID, setHelp) }() if player.BindRegister == mtype.Yes { return } m := s.GetDao().FissionWin _, 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 *win) Install(helper, beHelper *model.FissionUser) (setHelp bool, err error) { player, err := cmm.NewFirstCreate(s.Ctx, s.GetDao()).FissionWin(helper) if err != nil { return } defer func() { if err != nil { return } err = s._newcomerReward(helper, player.ID, setHelp) }() if player.BindInstall == mtype.Yes { return } m := s.GetDao().FissionWin _, 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 } // 一起赢发放新人奖 func (s *win) _newcomerReward(user *model.FissionUser, playerId int64, setHelp bool) (err error) { conf, err := cache.NewCampaignWin(s.Ctx).FindByCampaignId(user.CampaignID) if err != nil { err = s.BE().CacheError(err) return } switch conf.NewcomerCond { case mtype.FissionNewcomerCondInstall: if user.Source != mtype.FissionUserSourceInstall || !s.Req.NewUser { return } case mtype.FissionNewcomerCondHelp: if user.IsHelp == mtype.Yes || !setHelp { return } case mtype.FissionNewcomerCondInstallAndHelp: if user.Source != mtype.FissionUserSourceInstall || user.IsHelp == mtype.Yes || !setHelp { return } } if !app.NewRedisLock(s.Ctx, ckey.FissionWinNewcomerOnce(user.ID)).SoftLock() { return } rewardM := s.GetDao().FissionCampaignReward rl, err := rewardM.WithContext(s.Ctx).Where( rewardM.CampaignID.Eq(user.CampaignID), rewardM.RewardType.Eq(mtype.FissionCampaignRewardTypeNewcomer), rewardM.IsDel.Eq(mtype.No), ).Find() if err != nil { return s.BE().MysqlError(err) } var randMap = make(map[int64]*model.FissionCampaignReward) for _, v := range rl { randMap[v.ID] = v } var reward *model.FissionCampaignReward for _, v := range randMap { reward = v break } record, _, async, err := award.NewAwardReward(s.Ctx, s.GetDao(), s.cpn.ProjectID, user).One(reward, false) if err != nil || record == nil { return } provider.NewFissionClue(s.Ctx, user.ID).AddNewcomerReward(record.ID) s.MP().InApp(user).Newcomer(record.CreatedAt, record.RewardID, record.ID) playerM := s.GetDao().FissionWin _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(playerId)).UpdateSimple(playerM.IsNewcomer.Value(mtype.Yes)) if err != nil { err = s.BE().MysqlError(err) return } // 如果为没有用户id,就不用通知客户 if user.SubUserID == "" { return } s.AddAfterRun(func() (err error) { // 执行异步任务 for _, do := range async { do() } return }) return } type winStepDo func(player *model.FissionWin, conf *model.FissionCampaignWin, reward, nextReward *model.FissionCampaignReward, allInviteNum int32) (err error) type winCycleDo func(player *model.FissionWin, conf *model.FissionCampaignWin, reward *model.FissionCampaignReward) (err error) type winDo struct { step winStepDo cycle winCycleDo } func (s *win) _do(user *model.FissionUser, do winDo) (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.FissionWin(user) if err != nil { return } // 被限制了 if player.LimitStatus != mtype.FissionReferralLimitStatusNone { return } conf, err := cache.NewCampaignWin(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 *win) _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 := winDo{ step: func(player *model.FissionWin, conf *model.FissionCampaignWin, reward, nextReward *model.FissionCampaignReward, allInviteNum int32) (err error) { return s._step(user, effectNum, player, conf, reward, nextReward, allInviteNum) }, cycle: func(player *model.FissionWin, conf *model.FissionCampaignWin, 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 *win) _fixTaskDo(user *model.FissionUser, tasks []*model.FissionCampaignTask) (err error) { do := winDo{ step: func(player *model.FissionWin, conf *model.FissionCampaignWin, 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.FissionWin, conf *model.FissionCampaignWin, 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 *win) _step(user *model.FissionUser, effectNum int64, player *model.FissionWin, conf *model.FissionCampaignWin, 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().FissionWin 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 *win) _cycle(user *model.FissionUser, effectNum int64, player *model.FissionWin, conf *model.FissionCampaignWin, 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 *win) _limitStatus(playerId int64, ls int8) (err error) { if ls == mtype.FissionReferralLimitStatusNone { return } m := s.GetDao().FissionWin 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 *win) _extra(user *model.FissionUser, player *model.FissionWin, 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().FissionWin _, 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 }