package players import ( "context" "joybuywork/internal/app" "joybuywork/internal/consts" "joybuywork/internal/consts/ckey" "joybuywork/internal/plugins/hashid" "joybuywork/internal/plugins/jsonm" "joybuywork/internal/repository/cache" "joybuywork/internal/repository/dao" "joybuywork/internal/repository/dbcheck" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/repository/withdefault" "joybuywork/internal/service/provider" "joybuywork/internal/service/provider/award" "joybuywork/internal/service/provider/cachecurd" "joybuywork/internal/service/provider/fissionaction" "joybuywork/internal/utils" "joybuywork/internal/utils/lockdo" "joybuywork/internal/utils/timez" "fmt" "math" "time" "gorm.io/gen/field" ) const ( keepMachineLimitNone = iota keepMachineLimitDone // 被限制不能领取了 keepMachineLimitWait // 还没有到时间 ) // KeepDog 激励养成交互逻辑 type KeepDog struct { Player player *model.FissionKeep conf *model.FissionCampaignKeep propFeedNumKey string // 道具喂养次数key dayFirstInviteKey string // 当天首次喂养状态key getMachineLastKey string machineSaveKey string machineNotSaveKey string dogFoodLimit string // 狗粮限制缓存状态 } func NewKeepDog(ctx context.Context, user *model.FissionUser, opts ...Option) (player *KeepDog, err error) { player = &KeepDog{} err = player.Init(ctx, user, consts.IdKeep, opts...) if err != nil { return } err = player.initPlayer() if err != nil { return nil, err } return } // TaskLimit 任务是否能完成 func (s *KeepDog) TaskLimit(task *model.FissionCampaignTask) (limit bool, err error) { player := s.GetPlayer() if player.RewardID == 0 || player.ExpiredAt <= time.Now().Unix() { return true, nil } return } // ReceiveReward 手动领取奖励 func (s *KeepDog) ReceiveReward(taskRewards ...*model.FissionUserTaskReward) (chance int32, reward *award.RewardRes, err error) { err = s._receiveReward(taskRewards, func(tasks []*model.FissionCampaignTask, records []*award.RewardRes) error { chance, err = s.taskFinish(tasks, false) if err != nil { return err } if len(records) == 0 { return nil } reward = records[0] return nil }) if err != nil { return } return } // FinishTask 完成任务 func (s *KeepDog) FinishTask(tasks ...*model.FissionCampaignTask) (err error) { _, err = s.taskFinish(tasks, true) if err != nil { return } return } // 完成任务 func (s *KeepDog) taskFinish(tasks []*model.FissionCampaignTask, checkManual bool) (chance int32, err error) { player := s.GetPlayer() var logs []*model.FissionKeepLog for _, task := range tasks { if checkManual && task.AwardMode == mtype.FissionCampaignTaskAwardModeManual { continue } var source = mtype.FissionKeepLogSourceFinishTask if task.TaskType == mtype.FissionCampaignTaskTypeCondAll { source = mtype.FissionKeepLogSourceFinishAllTask } log := &model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Source: source, } if task.PropMark == mtype.FissionCampaignTaskPropMarkChance { log.Type = mtype.FissionKeepLogTypeProp log.Val = fmt.Sprint(task.Chance) log.ValSource, _ = jsonm.MarshalToString(mtype.KeepLogValSource{ TaskId: task.ID, Chance: task.Chance, }) chance += task.Chance } else { log.Type = mtype.FissionKeepLogTypeReward log.Val = "1" log.ValSource, _ = jsonm.MarshalToString(mtype.KeepLogValSource{ TaskId: task.ID, RewardId: task.RewardID, }) } logs = append(logs, log) } if chance > 0 { playerM := s.GetDao().FissionKeep _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).UpdateSimple(playerM.Num.Add(chance)) if err != nil { err = s.BE().MysqlError(err) return } } if len(logs) > 0 { err = s.addKeepLog(logs...) if err != nil { return } } return } const ( KeepDogRewardSelectStatusOk = iota // 成功 KeepDogRewardSelectStatusRemove // 奖品下架 KeepDogRewardSelectStatusLack // 奖品不足 KeepDogRewardSelectStatusLimit // 奖品已经是获得的最大限制 ) // ChildActive 完成子活动 func (s *KeepDog) ChildActive(num int32, childType int8) (err error) { player := s.GetPlayer() m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Num.Add(num)) if err != nil { err = s.BE().MysqlError(err) return } var source string switch childType { case mtype.FissionCampaignChildTypeInvite: source = mtype.FissionKeepLogSourceChildInvite case mtype.FissionCampaignChildTypeSign: source = mtype.FissionKeepLogSourceChildSign case mtype.FissionCampaignChildTypeLucky: source = mtype.FissionKeepLogSourceChildLucky case mtype.FissionCampaignChildTypeBlind: source = mtype.FissionKeepLogSourceChildBlind case mtype.FissionCampaignChildTypeTask: source = mtype.FissionKeepLogSourceChildTask } return s.addKeepLog(&model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: source, Val: fmt.Sprint(num), }) } // RewardSelect 选择奖品 func (s *KeepDog) RewardSelect(rewardId int64) (status int32, err error) { player, conf := s.GetPlayer(), s.GetConf() if player.RewardID != 0 { return } rewardM := dao.FissionCampaignReward reward, err := rewardM.WithContext(s.Ctx).Where(rewardM.ID.Eq(rewardId), rewardM.IsDel.Eq(mtype.No)).Take() if err = dbcheck.DbError(err); err != nil { err = s.BE().MysqlError(err) return } if reward == nil { return KeepDogRewardSelectStatusRemove, nil } // 非预览用户占用库存 if !s.IsView() { var point string var ok bool point, ok, err = award.NewAwardIssVerify(s.Ctx, s.user, reward).DeductSelectStock().RuleFilter().PointOK() if err != nil { return } if !ok { switch point { case award.IssVerifyPointDeductPreStock: return KeepDogRewardSelectStatusLack, nil case award.IssVerifyPointRuleFilter: return KeepDogRewardSelectStatusLimit, nil } return } } m := dao.FissionKeep var up = []field.AssignExpr{ m.RewardID.Value(rewardId), m.Step.Value(1), m.Num.Value(conf.InitGet), m.ExpiredAt.Value(time.Now().AddDate(0, 0, int(conf.FeedCycle)).Unix()), } if conf.MachineType == mtype.FissionCampaignKeepMachineTypeOffline { up = append(up, m.ReceiveOffline.Value(time.Now().Unix())) } _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(up...) if err != nil { err = s.BE().MysqlError(err) return } if conf.InitGet > 0 { err = s.addKeepLog(&model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceInitGet, Val: fmt.Sprint(conf.InitGet), }) if err != nil { return } } // 预览用户不用处理下面的邀请、埋点动作、奖品被选的动作 if s.IsView() { return } fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).Grow(time.Now().Unix(), rewardId) statM := dao.FissionUserStat _, err = statM.WithContext(s.Ctx).Where(statM.UserID.Eq(player.UserID)).UpdateSimple(statM.GrowNum.Add(1)) if err != nil { err = s.BE().MysqlError(err) return } hmap, err := cachecurd.NewHMap(s.Ctx, ckey.FissionKeepRewardSelectedUser(player.CampaignID), cachecurd.SetKeepRewardSelectBuild(player.CampaignID)) if err != nil { return } _, err = hmap.Incr(fmt.Sprint(rewardId), 1) if err != nil { err = s.BE().CacheError(err) return } if s.user.ParentID == 0 || s.user.IsHelp == mtype.No { return } dayFirst, err := s.dayFirst() if err != nil { return } parent, err := cache.NewFissionUser(s.Ctx, player.CampaignID).FindById(s.user.ParentID) if err != nil { return } pkd, err := NewKeepDog(s.Ctx, parent) if err != nil { return } err = pkd.InviteSelect(dayFirst) if err != nil { return } return } type KeepDogCollectReward struct { RewardType int32 Chance int32 RewardId int64 RewardTitle string RewardImg string Lack bool RewardRecordId int64 LastReward bool ReceiveType int32 } // Collect 收集一级道具 func (s *KeepDog) Collect() (num int32, gets []*KeepDogCollectReward, err error) { player, conf := s.GetPlayer(), s.GetConf() if player.Collect == 0 || player.RecordID > 0 { return } collectNum := player.CollectNum + player.Collect var logs = []*model.FissionKeepLog{ { UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeLove, Source: mtype.FissionKeepLogSourceCollect, Val: fmt.Sprintf("%d", player.Collect), }, } m := dao.FissionKeep var keepUp = []field.AssignExpr{ m.CollectNum.Add(player.Collect), m.Collect.Value(0), } defer func() { if err != nil { return } _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(keepUp...) if err != nil { err = s.BE().MysqlError(err) return } err = s.addKeepLog(logs...) if err != nil { return } num = player.Collect }() if player.Step == 4 { if collectNum < conf.Collect { return } var get *KeepDogCollectReward get, err = s.finishKeep() if err != nil { return } gets = append(gets, get) keepUp = append(keepUp, m.Step.Value(0), m.RecordID.Value(get.RewardRecordId)) return } stepM := dao.FissionCampaignKeepStep steps, err := stepM.WithContext(s.Ctx).Where(stepM.CampaignID.Eq(player.CampaignID)).Order(stepM.Step).Find() if err != nil { err = s.BE().MysqlError(err) return } var stepMap = make(map[int32]*model.FissionCampaignKeepStep) for _, step := range steps { stepMap[step.Step] = step } var finishStep []int32 var chance int32 var rewardIds []int64 var nextStep = player.Step for i := player.Step; i <= 3; i++ { step := stepMap[i] needNum := s.stepPercent(conf.Collect, step.Percent) if needNum > float64(collectNum) { break } nextStep++ finishStep = append(finishStep, step.Step) switch step.RewardType { case mtype.FissionCampaignKeepStepRewardTypeProp: chance += step.RewardNum gets = append(gets, &KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeProp, Chance: step.RewardNum, }) val, _ := jsonm.MarshalToString(mtype.KeepLogValSource{ Step: step.Step, Chance: step.RewardNum, }) logs = append(logs, &model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceStep, Val: fmt.Sprintf("%d", step.RewardNum), ValSource: val, }) case mtype.FissionCampaignKeepStepRewardTypeReward: rewardIds = append(rewardIds, step.RewardID) val, _ := jsonm.MarshalToString(mtype.KeepLogValSource{ Step: step.Step, RewardId: step.RewardID, }) logs = append(logs, &model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeReward, Source: mtype.FissionKeepLogSourceStep, Val: fmt.Sprintf("%d", step.RewardNum), ValSource: val, }) } } if nextStep == 4 && collectNum >= conf.Collect { var get *KeepDogCollectReward get, err = s.finishKeep() if err != nil { return } // 最终奖品放最后 defer func() { gets = append(gets, get) }() keepUp = append(keepUp, m.RecordID.Value(get.RewardRecordId)) nextStep = 0 } keepUp = append(keepUp, m.Step.Value(nextStep), m.Num.Add(chance)) if len(rewardIds) == 0 { return } rewardM := dao.FissionCampaignReward rewards, err := rewardM.WithContext(s.Ctx).Where(rewardM.ID.In(rewardIds...)).Find() if err != nil { err = s.BE().MysqlError(err) return } cpn := s.cpn // 预览用户直接显示奖品 if s.IsView() { var bids []int64 var bMap = make(map[int64]struct{}) for _, v := range rewards { if _, ok := bMap[v.RewardID]; !ok { bids = append(bids, v.RewardID) bMap[v.RewardID] = struct{}{} } } var preward *provider.Rewards preward, err = provider.NewRewards(s.Ctx, cpn.ProjectID, bids...) if err != nil { return } for _, v := range rewards { info, _ := preward.InfoAutoLang(v.RewardID, s.user.LangMark) gets = append(gets, &KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeReward, RewardId: v.ID, RewardTitle: info.Title, RewardImg: info.ImgUrl, ReceiveType: info.ReceiveType, }) } return } var sendRewards = make([]*award.RewardOne, 0, len(rewards)) for _, v := range rewards { one := &award.RewardOne{ Reward: v, Opts: nil, } var ok bool ok, err = award.NewAwardIssVerify(s.Ctx, s.user, v).DeductStock().IsOK() if err != nil { err = s.BE().MysqlError(err) return } if !ok { one.Opts = append(one.Opts, award.RewardRecordStatus( mtype.FissionRewardRecordStatusFail, mtype.FissionRewardRecordFailTypeCli, mtype.FissionRewardRecordFailTypeCliLackMsg, )) } sendRewards = append(sendRewards, one) } records, _, err := award.NewAwardReward(s.Ctx, dao.Q, cpn.ProjectID, s.user).Batch(sendRewards, true) if err != nil { return } for _, v := range records { gets = append(gets, &KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeReward, RewardId: v.Record.RewardID, RewardTitle: v.Info.Title, RewardImg: v.Info.ImgUrl, Lack: v.Record.FailMsg == mtype.FissionRewardRecordFailTypeCliLackMsg, RewardRecordId: v.Record.ID, ReceiveType: v.Info.ReceiveType, }) } return } const ( KeepDogFeedStatusOk = iota // 喂养成功 KeepDogFeedStatusMaxCollect // 收集数达到最大 KeepDogFeedStatusLack // 狗粮不足 KeepDogFeedStatusGetFeedNum // 需要收喂养次数道具 ) // Feed 喂养 func (s *KeepDog) Feed() (status, addSecond int32, err error) { player, conf, key := s.GetPlayer(), s.GetConf(), s.propFeedNumKey num, propNum, err := s.feedNum() if err != nil { return } if propNum >= int64(conf.FeedGetNum) { return KeepDogFeedStatusGetFeedNum, 0, nil } if player.Num < conf.FeedNum { return KeepDogFeedStatusLack, 0, nil } if player.Collect >= 4 { return KeepDogFeedStatusMaxCollect, 0, nil } decr := conf.FeedNum collectNum := player.CollectNum second := player.SecondNum + decr m := dao.FissionKeep var up []field.AssignExpr if second < conf.CollectSecond { up = append(up, m.SecondNum.Value(second), m.Num.Sub(decr)) } else if collectNum == 3 { decr = conf.CollectSecond - player.SecondNum up = append(up, m.SecondNum.Value(0), m.Collect.Add(1), m.Num.Sub(decr)) } else { var incr int32 for { second -= conf.CollectSecond incr++ if incr+collectNum == 4 { decr = (conf.CollectSecond - player.SecondNum) + incr*conf.CollectSecond second = 0 break } if second < conf.CollectSecond { break } } up = append(up, m.SecondNum.Value(second), m.Collect.Add(incr), m.Num.Sub(decr)) } _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(up...) if err != nil { err = s.BE().MysqlError(err) return } defer func() { if err != nil { return } err = s.addKeepLog(&model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeFeed, Source: mtype.FissionKeepLogSourceFeed, Val: fmt.Sprintf("%d", decr), }) if err != nil { return } app.Redis.Cache.HIncrBy(s.Ctx, key, keepDogFeedNumMapAllNumKey, 1) app.Redis.Cache.HIncrBy(s.Ctx, key, keepDogFeedNumMapPropNumKey, 1) addSecond = (decr * 100) / conf.CollectSecond }() if num+1 == 3 && s.HelpOk() { var dayFirst bool dayFirst, err = s.dayFirst() if err != nil { return } var parent *model.FissionUser parent, err = cache.NewFissionUser(s.Ctx, player.CampaignID).FindById(s.user.ParentID) if err != nil { return } var pkd *KeepDog pkd, err = NewKeepDog(s.Ctx, parent) if err != nil { return } err = pkd.InviteThirdFeed(dayFirst) if err != nil { return } } return } // MachineRecycle 回收机器产生的道具 func (s *KeepDog) MachineRecycle(num float64) (limit, numVal int32, err error) { tz := provider.NewCampaignTZ(s.Ctx, s.cpn, s.user.LangMark) day, start, end := timez.GMTStartAndEndTs(tz.GetTz()) conf := s.GetConf() if conf.MachineType == mtype.FissionCampaignKeepMachineTypeOffline { return s._offlineRecycle(day, start, end) } return s._onlineRecycle(day, start, end, num) } // 在线收益回收 func (s *KeepDog) _onlineRecycle(day string, start, end int64, num float64) (limit, numVal int32, err error) { player, key := s.GetPlayer(), s.getMachineLastKey dayLimit, err := s.machineDayLimit(day, start, end, 0) if err != nil { return } if dayLimit { limit = keepMachineLimitDone return } if num < 10 || s.secGet() < num { return } if num > 99 { num = 99 } lc := app.NewRedisLock(s.Ctx, s.machineSaveKey).Lock() defer lc.Release() if lc.Error() != nil { err = lc.Error() return } addNum := math.Floor(num) machineNum := num - addNum //收了之后,剩余多少 numVal = int32(addNum) m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Num.Add(numVal), m.MachineNum.Value(machineNum)) if err != nil { err = s.BE().MysqlError(err) return } t := time.Now().Unix() err = s.addKeepLog(&model.FissionKeepLog{ CreatedAt: t, UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceMachine, Val: fmt.Sprintf("%d", numVal), }) if err != nil { return } app.Redis.Cache.Set(s.Ctx, key, t, lockdo.Rand2HourExp()) app.Redis.Cache.Set(s.Ctx, s.machineNotSaveKey, 1, 2*time.Second) dayLimit, err = s.machineDayLimit(day, start, end, num) if err != nil { return } if dayLimit { limit = keepMachineLimitDone } return } func (s *KeepDog) _offlineRecycle(day string, start, end int64) (limit, numVal int32, err error) { player, conf := s.GetPlayer(), s.GetConf() offlineLimit, err := s.offlineNumLimit(day, start, end, 0) if err != nil { return } if offlineLimit { limit = keepMachineLimitDone return } nextReceive := player.ReceiveOffline + int64(conf.OfflineMin)*60 if time.Now().Unix() < nextReceive { limit = keepMachineLimitWait return } m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Num.Add(conf.OfflineGet), m.ReceiveOffline.Value(time.Now().Unix())) if err != nil { err = s.BE().MysqlError(err) return } numVal = conf.OfflineGet err = s.addKeepLog(&model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceMachineOffline, Val: fmt.Sprintf("%d", conf.OfflineGet), }) if err != nil { return } if conf.OfflineLimit == 0 { return } offlineLimit, err = s.offlineNumLimit(day, start, end, 1) if err != nil { return } if offlineLimit { limit = keepMachineLimitDone } return } func (s *KeepDog) OfflineLimit() (ok bool, err error) { tz := provider.NewCampaignTZ(s.Ctx, s.cpn, s.user.LangMark) day, start, end := timez.GMTStartAndEndTs(tz.GetTz()) conf := s.GetConf() if conf.MachineType != mtype.FissionCampaignKeepMachineTypeOffline { return false, nil } return s.offlineNumLimit(day, start, end, 0) } // MachineSave 保存机器产生的道具 func (s *KeepDog) MachineSave(num float64) (err error) { player := s.GetPlayer() if num > 99 { num = 99 } if player.MachineNum == num { return } lc := app.NewRedisLock(s.Ctx, s.machineSaveKey).Lock() defer lc.Release() if lc.Error() != nil { err = lc.Error() return } // 不能保存的时间 exist, err := app.Redis.Exists(s.Ctx, s.machineNotSaveKey) if err != nil { err = s.BE().CacheError(err) return } if exist { return } if s.secGet() < num { return s.BE().VerifyError("can not do it") } m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.MachineNum.Value(num)) if err != nil { err = s.BE().MysqlError(err) return } return } // GetPropFeed 收喂养次数的道具 func (s *KeepDog) GetPropFeed() (num int32, err error) { player, conf, key := s.GetPlayer(), s.GetConf(), s.propFeedNumKey m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Num.Add(conf.FeedGet)) if err != nil { err = s.BE().MysqlError(err) return } num = conf.FeedGet err = s.addKeepLog(&model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceFeedProp, Val: fmt.Sprintf("%d", conf.FeedGet), }) if err != nil { return } _, propNum, err := s.feedNum() if err != nil { return } if int32(propNum) < conf.FeedGetNum { err = s.BE().VerifyError("can not do it") return } app.Redis.Cache.HIncrBy(s.Ctx, key, keepDogFeedNumMapPropNumKey, -propNum) return } // GetPropNeedFeedNum 获得喂养的道具还需要几次 func (s *KeepDog) GetPropNeedFeedNum() (need int32, err error) { _, propNum, err := s.feedNum() if err != nil { return } return s.GetConf().FeedGetNum - int32(propNum), nil } // InviteThirdFeed 邀请的下级喂养了3次 func (s *KeepDog) InviteThirdFeed(dayFirst bool) (err error) { player, conf := s.GetPlayer(), s.GetConf() invite := conf.GetInvite() if conf.OpenInvite == mtype.No || invite == nil || !s.canGetInviteFood() { return } var logs = []*model.FissionKeepLog{ { UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceThird, Val: fmt.Sprintf("%d", invite.FeedThird), }, } num := invite.FeedThird if dayFirst { logs = append(logs, &model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceDayFirstThird, Val: fmt.Sprintf("%d", invite.DayFirstThird), }) num += invite.DayFirstThird } m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Num.Add(num)) if err != nil { err = s.BE().MysqlError(err) return } err = s.addKeepLog(logs...) if err != nil { return } return } // InviteSelect 邀请的下级选择了奖品 func (s *KeepDog) InviteSelect(dayFirst bool) (err error) { player, conf := s.GetPlayer(), s.GetConf() invite := conf.GetInvite() if conf.OpenInvite == mtype.No || invite == nil || !s.canGetInviteFood() { return } var logs = []*model.FissionKeepLog{ { UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceSelect, Val: fmt.Sprintf("%d", invite.RewardSelected), }, } num := invite.RewardSelected if dayFirst { logs = append(logs, &model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceDayFirstSelect, Val: fmt.Sprintf("%d", invite.DayFirstSelected), }) num += invite.DayFirstSelected } m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Num.Add(num)) if err != nil { err = s.BE().MysqlError(err) return } err = s.addKeepLog(logs...) if err != nil { return } return s.addFoodInviteLimit(int64(num)) } // EffectInvite 用户有效邀请 func (s *KeepDog) EffectInvite(childUserId int64) (err error) { player, conf := s.GetPlayer(), s.GetConf() if conf.OpenInvite == mtype.No || !s.canGetInviteFood() { return } invite := conf.GetInvite() var num = invite.InviteGet var logs = []*model.FissionKeepLog{ { UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceEffectInvite, Val: fmt.Sprintf("%d", invite.InviteGet), }, } defer func() { if err != nil { return } m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Num.Add(num)) if err != nil { err = s.BE().MysqlError(err) return } err = s.addKeepLog(logs...) if err != nil { return } err = s.addFoodInviteLimit(int64(num)) }() if invite.DayFirstGet == mtype.No { return } cpn := s.cpn tz := provider.NewCampaignTZ(s.Ctx, cpn, s.user.LangMark) start, _ := timez.GMTTimeStart(tz.GetTz()) day := start.Format(timez.ShortDateOnly) key := ckey.FissionUserDayFirstInviteUser(day, player.UserID) var first bool err = lockdo.ExistDo(s.Ctx, key, func(exist bool) (err error) { if exist { return } m := dao.FissionUser.SubTable(cpn.ID) info, err1 := m.WithContext(s.Ctx).Where( m.CampaignID.Eq(cpn.ID), m.ParentID.Eq(player.UserID), m.IsHelp.Eq(mtype.Yes), ).First() if err1 = dbcheck.DbError(err1); err1 != nil { return err1 } if info != nil { app.Redis.Cache.Set(s.Ctx, key, info.ID, lockdo.Rand2HourExp()) return } first = true app.Redis.Cache.Set(s.Ctx, key, childUserId, lockdo.Rand2HourExp()) err = dao.FissionUserAttr.WithContext(s.Ctx).Create(withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: cpn.ID, UserID: childUserId, Attr: mtype.FissionUserAttrMarkDayFirst, Value: fmt.Sprintf("%d:%s", tz.GetTz(), day), })) if err != nil { err = s.BE().MysqlError(err) return } return }) if err != nil { err = s.BE().CacheError(err) return } if !first { return } num += invite.DayFirstGet logs = append(logs, &model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceDayFirstEffectInvite, Val: fmt.Sprintf("%d", invite.DayFirstGet), }) return } // GetPlayer 获取玩家基础数据 func (s *KeepDog) GetPlayer() *model.FissionKeep { return s.player } // GetConf 获取活动配置 func (s *KeepDog) GetConf() *model.FissionCampaignKeep { if s.conf == nil { s.conf, _ = cache.NewCampaignKeep(s.Ctx).FindByCampaignId(s.user.CampaignID) } return s.conf } type KeepDogStatus struct { PropNeedFeed int32 // 获得喂养道具还需要几次 DayFirstInvite int32 // 当天是否有进行首次邀请 DayFirstFeed int32 // 当天是否进行了第一次喂养 SecondPercent int32 // 爱心进度 Percent string // 奖品进度 Left int32 // 还差的爱心树 MachineLimit int32 // 狗粮机限制 OfflineFull int64 // 离线狗粮机填满剩余秒数 } // GetStatus 获取一些需要计算的状态 func (s *KeepDog) GetStatus() (status KeepDogStatus, err error) { status.PropNeedFeed, err = s.GetPropNeedFeedNum() if err != nil { return } player, conf := s.GetPlayer(), s.GetConf() stepM := dao.FissionCampaignKeepStep steps, err := stepM.WithContext(s.Ctx).Where(stepM.CampaignID.Eq(player.CampaignID)).Order(stepM.Step).Find() if err != nil { err = s.BE().MysqlError(err) return } var stepMap = make(map[int32]int32) for _, step := range steps { stepMap[step.Step] = step.Percent } status.Left = conf.Collect - player.CollectNum if status.Left < 0 { status.Left = 0 } var cent float64 status.SecondPercent = (player.SecondNum * 100) / conf.CollectSecond cent = float64(player.CollectNum) * 60 / float64(conf.Collect) if player.Guided == mtype.Yes { cent += 40 } if percent, ok := stepMap[player.Step]; ok && cent >= float64(percent) { cent = float64(percent) - 1 } else if player.Step == 4 && cent >= 100 { cent = 99 } if player.RecordID > 0 { cent = 100 } status.Percent = utils.Float64PointCutZero(cent, 1) cpn := s.cpn tz := provider.NewCampaignTZ(s.Ctx, cpn, s.user.LangMark) day, start, end := timez.GMTStartAndEndTs(tz.GetTz()) dayFirstInviteUserKey := ckey.FissionUserDayFirstInviteUser(day, player.UserID) err = lockdo.ExistDo(s.Ctx, dayFirstInviteUserKey, func(exist bool) (err error) { if exist { return } m := dao.FissionUser.SubTable(cpn.ID) info, err1 := m.WithContext(s.Ctx).Where( m.CampaignID.Eq(cpn.ID), m.ParentID.Eq(player.UserID), m.IsHelp.Eq(mtype.Yes), m.CreatedAt.Gte(start), ).First() if err1 = dbcheck.DbError(err1); err1 != nil { return err1 } if info != nil { app.Redis.Cache.Set(s.Ctx, dayFirstInviteUserKey, info.ID, lockdo.Rand2HourExp()) return } status.DayFirstInvite = 1 return }) if err != nil { err = s.BE().CacheError(err) return } dayFirstFeedKey := ckey.FissionUserDayFirstFeed(day, player.UserID) err = lockdo.ExistDo(s.Ctx, dayFirstFeedKey, func(exist bool) (err error) { if exist { return } m := dao.FissionKeepLog info, err1 := m.WithContext(s.Ctx).Where( m.UserID.Eq(player.UserID), m.Type.Eq(mtype.FissionKeepLogTypeFeed), m.CreatedAt.Gte(start), ).First() if err1 = dbcheck.DbError(err1); err1 != nil { return err1 } if info != nil { app.Redis.Cache.Set(s.Ctx, dayFirstFeedKey, info.ID, lockdo.Rand2HourExp()) return } status.DayFirstFeed = 1 return }) if err != nil { err = s.BE().CacheError(err) return } switch conf.MachineType { case mtype.FissionCampaignKeepMachineTypeOnline: var dayLimit bool dayLimit, err = s.machineDayLimit(day, start, end, 0) if err != nil { return } if dayLimit { status.MachineLimit = keepMachineLimitDone } case mtype.FissionCampaignKeepMachineTypeOffline: status.OfflineFull = player.ReceiveOffline + int64(conf.OfflineMin)*60 - time.Now().Unix() var offlineLimit bool offlineLimit, err = s.offlineNumLimit(day, start, end, 0) if err != nil { return } if offlineLimit { status.MachineLimit = keepMachineLimitDone } } return } const ( KeepDogFeedReselectOk = iota // 喂养成功 KeepDogFeedReselectLimit // 超过最大养成次数 ) // Reselect 重新选择奖品 func (s *KeepDog) Reselect() (status int32, err error) { player, conf := s.GetPlayer(), s.GetConf() if player.RecordID == 0 && player.ExpiredAt > time.Now().Unix() { return } m := dao.FissionKeep count, err := m.WithContext(s.Ctx).Where(m.UserID.Eq(s.user.ID), m.RecordID.Gt(0)).Count() if err != nil { err = s.BE().MysqlError(err) return } if conf.MaxFeed > 0 && count >= int64(conf.MaxFeed) { return KeepDogFeedReselectLimit, nil } _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.ReselectAt.Value(time.Now().Unix())) if err != nil { err = s.BE().MysqlError(err) return } _, err = s.createPlayer() if err != nil { return } return } // Guided 引导 func (s *KeepDog) Guided() (get KeepDogCollectReward, err error) { player := s.GetPlayer() if player.Guided == mtype.Yes { return } var logs = []*model.FissionKeepLog{ { UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeGuided, Source: mtype.FissionKeepLogSourceFeed, Val: "20", }, } var num int32 = -20 defer func() { if err != nil { return } m := dao.FissionKeep _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Guided.Value(mtype.Yes), m.Step.Value(2), m.Num.Add(num)) if err != nil { err = s.BE().MysqlError(err) return } err = s.addKeepLog(logs...) if err != nil { return } }() stepM := dao.FissionCampaignKeepStep step, err := stepM.WithContext(s.Ctx).Where(stepM.CampaignID.Eq(player.CampaignID), stepM.Step.Eq(1), stepM.IsDel.Eq(mtype.No)).Take() if err = dbcheck.DbError(err); err != nil { err = s.BE().MysqlError(err) return } if step == nil { return } if step.RewardType == mtype.FissionCampaignKeepStepRewardTypeProp { logs = append(logs, &model.FissionKeepLog{ UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeProp, Source: mtype.FissionKeepLogSourceStep, Val: fmt.Sprint(step.RewardNum), }) num += step.RewardNum return KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeProp, Chance: step.RewardNum, }, nil } rewardM := dao.FissionCampaignReward reward, err := rewardM.WithContext(s.Ctx).Where(rewardM.ID.Eq(step.RewardID)).Take() if err = dbcheck.DbError(err); err != nil { err = s.BE().MysqlError(err) return } if reward == nil { return } cpn := s.cpn // 预览用户直接显示奖品 if s.IsView() { var preward *provider.Rewards preward, err = provider.NewRewards(s.Ctx, cpn.ProjectID, reward.RewardID) if err != nil { return } info, _ := preward.InfoAutoLang(reward.RewardID, s.user.LangMark) return KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeReward, Chance: reward.RewardNum, RewardId: reward.ID, RewardTitle: info.Title, RewardImg: info.ImgUrl, Lack: false, RewardRecordId: 0, LastReward: false, ReceiveType: info.ReceiveType, }, nil } ok, err := award.NewAwardIssVerify(s.Ctx, s.user, reward).DeductStock().IsOK() if err != nil { err = s.BE().CacheError(err) return } var opts []award.RecordOption if !ok { opts = append(opts, award.RewardRecordStatus( mtype.FissionRewardRecordStatusFail, mtype.FissionRewardRecordFailTypeCli, mtype.FissionRewardRecordFailTypeCliLackMsg, )) } record, info, _, err := award.NewAwardReward(s.Ctx, dao.Q, cpn.ProjectID, s.user).One(reward, true, opts...) if err != nil { return } return KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeReward, Chance: reward.RewardNum, RewardId: record.RewardID, RewardTitle: info.Title, RewardImg: info.ImgUrl, Lack: !ok, RewardRecordId: record.ID, ReceiveType: info.ReceiveType, }, nil } // RefreshView 刷新预览用户 func (s *KeepDog) RefreshView() (err error) { if !s.IsView() { err = s.BE().VerifyError("非预览用户无法重新体验") return } player := s.GetPlayer() m := s.GetDao().FissionKeepLog _, err = m.WithContext(s.Ctx).Where(m.KeepID.Eq(player.ID)).Delete() if err != nil { err = s.BE().MysqlError(err) return } playerM := s.GetDao().FissionKeep _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).Delete() if err != nil { err = s.BE().MysqlError(err) return } app.Redis.Cache.Del(s.Ctx, s.propFeedNumKey, s.dayFirstInviteKey, s.getMachineLastKey, s.machineSaveKey, s.machineNotSaveKey, s.dogFoodLimit, ) return } const ( keepDogFeedNumMapPropNumKey = "prop_num" // 获得道具的喂养数 keepDogFeedNumMapAllNumKey = "all_num" // 喂养总数 ) // 喂养数据 func (s *KeepDog) feedNum() (num, propNum int64, err error) { player, key := s.GetPlayer(), s.propFeedNumKey err = lockdo.ExistDo(s.Ctx, key, func(exist bool) (err error) { if exist { data := app.Redis.Cache.HGetAll(s.Ctx, key).Val() for k, v := range data { switch k { case keepDogFeedNumMapPropNumKey: propNum = utils.StrToInt64(v) case keepDogFeedNumMapAllNumKey: num = utils.StrToInt64(v) } } return } m := dao.FissionKeepLog propNum, err = m.WithContext(s.Ctx).Where(m.KeepID.Eq(player.ID), m.Type.Eq(mtype.FissionKeepLogTypeFeed), m.CreatedAt.Gt(player.FeedGetAt)).Count() if err != nil { return } allNum, err := m.WithContext(s.Ctx).Where(m.KeepID.Eq(player.ID), m.Type.Eq(mtype.FissionKeepLogTypeFeed)).Count() if err != nil { return } app.Redis.Cache.HSet(s.Ctx, key, allNum, keepDogFeedNumMapPropNumKey, propNum, keepDogFeedNumMapAllNumKey, allNum) app.Redis.Cache.Expire(s.Ctx, key, lockdo.Rand2HourExp()) return }) if err != nil { err = s.BE().CacheError(err) return } return } // 玩家 func (s *KeepDog) initPlayer() (err error) { playerM := dao.FissionKeep player, err := playerM.WithContext(s.Ctx).Where(playerM.UserID.Eq(s.user.ID)).Last() if err = dbcheck.DbError(err); err != nil { err = s.BE().MysqlError(err) return } defer func() { s.player = player s.propFeedNumKey = ckey.FissionUserPropFeedNum(player.ID) s.dayFirstInviteKey = ckey.FissionUserDayFirstInvite(player.UserID) s.getMachineLastKey = ckey.FissionUserKeepGetMachineLast(player.ID) s.machineSaveKey = ckey.FissionUserKeepMachineSave(player.ID) s.machineNotSaveKey = ckey.FissionUserKeepMachineNotSave(player.ID) s.dogFoodLimit = ckey.FissionUserKeepInviteDogFoodLimit(player.UserID) }() if player != nil { return } player, err = s.firstOrCreate() if err != nil { return } return } // 用户最后一次获得机器狗粮的时间 func (s *KeepDog) getMachineLast() (t int64, err error) { player, key := s.GetPlayer(), s.getMachineLastKey t, err = lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := dao.FissionKeepLog info, err1 := m.WithContext(s.Ctx).Where( m.KeepID.Eq(player.ID), m.Type.Eq(mtype.FissionKeepLogTypeProp), m.Source.Eq(mtype.FissionKeepLogSourceMachine), ).Last() if err1 = dbcheck.DbError(err1); err1 != nil { return 0, err1 } if info != nil { return info.CreatedAt, nil } return 0, nil }) if err != nil { err = s.BE().CacheError(err) return } return } // 用户是否是当天被上级首次邀请的用户 func (s *KeepDog) dayFirst() (dayFirst bool, err error) { err = lockdo.ExistDo(s.Ctx, s.dayFirstInviteKey, func(exist bool) (err error) { if exist { dayFirst, err = app.Redis.Cache.Get(s.Ctx, s.dayFirstInviteKey).Bool() return } dayFirstAttr, err1 := cache.NewFissionUserAttr(s.Ctx).FindByUniqIdx(s.user.ID, mtype.FissionUserAttrMarkDayFirst) if err1 != nil { return err1 } dayFirst = dayFirstAttr != nil app.Redis.Cache.Set(s.Ctx, s.dayFirstInviteKey, dayFirst, lockdo.Rand2HourExp()) return }) if err != nil { err = s.BE().CacheError(err) return } return } // 创建玩家基础数据 func (s *KeepDog) firstOrCreate() (player *model.FissionKeep, err error) { playerM := dao.FissionKeep lock := app.NewFollower(s.Ctx, ckey.FissionUserCreatePlayerOnce(s.user.ID)).Lock() defer lock.Release() if lock.Error() != nil { err = lock.Error() return } player, err = playerM.WithContext(s.Ctx).Where(playerM.UserID.Eq(s.user.ID)).Last() if err = dbcheck.DbError(err); err != nil { err = s.BE().MysqlError(err) return } if player != nil { return } return s.createPlayer() } func (s *KeepDog) createPlayer() (player *model.FissionKeep, err error) { playerM := dao.FissionKeep player = withdefault.FissionKeep(&model.FissionKeep{ UserID: s.user.ID, CampaignID: s.user.CampaignID, ExpiredAt: time.Now().AddDate(0, 0, int(s.GetConf().FeedCycle)).Unix(), }) err = playerM.WithContext(s.Ctx).Create(player) if err != nil { err = s.BE().MysqlError(err) return } return } // 完成最终奖品养成 func (s *KeepDog) finishKeep() (get *KeepDogCollectReward, err error) { player := s.GetPlayer() rewardM := dao.FissionCampaignReward reward, err := rewardM.WithContext(s.Ctx).Where(rewardM.ID.In(player.RewardID)).Take() if err = dbcheck.DbError(err); err != nil { err = s.BE().MysqlError(err) return } if reward == nil { err = s.BE().VerifyError("not found reward") return } val, _ := jsonm.MarshalToString(mtype.KeepLogValSource{ Step: 4, RewardId: reward.ID, }) err = s.addKeepLog(&model.FissionKeepLog{ ID: hashid.IdBigint(), UserID: player.UserID, CampaignID: player.CampaignID, KeepID: player.ID, Type: mtype.FissionKeepLogTypeReward, Source: mtype.FissionKeepLogSourceStep, Val: "1", ValSource: val, }) if err != nil { return } cpn := s.cpn // 预览用户直接显示奖品 if s.IsView() { var preward *provider.Rewards preward, err = provider.NewRewards(s.Ctx, cpn.ProjectID, reward.RewardID) if err != nil { return } info, _ := preward.InfoAutoLang(reward.RewardID, s.user.LangMark) return &KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeReward, Chance: reward.RewardNum, RewardId: reward.ID, RewardTitle: info.Title, RewardImg: info.ImgUrl, ReceiveType: info.ReceiveType, }, nil } ok, err := award.NewAwardIssVerify(s.Ctx, s.user, reward).DeductStock().IsOK() if err != nil { return } var opts []award.RecordOption if !ok { opts = append(opts, award.RewardRecordStatus( mtype.FissionRewardRecordStatusFail, mtype.FissionRewardRecordFailTypeCli, mtype.FissionRewardRecordFailTypeCliLackMsg, )) } record, info, _, err := award.NewAwardReward(s.Ctx, dao.Q, cpn.ProjectID, s.user).One(reward, true, opts...) if err != nil { return } fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).GrowOk(time.Now().Unix(), record.RewardID, record.ID) statM := dao.FissionUserStat _, err = statM.WithContext(s.Ctx).Where(statM.UserID.Eq(player.UserID)).UpdateSimple(statM.GrowRewardNum.Add(1)) if err != nil { err = s.BE().MysqlError(err) return } return &KeepDogCollectReward{ RewardType: mtype.FissionCampaignKeepStepRewardTypeReward, RewardId: record.RewardID, RewardTitle: info.Title, RewardImg: info.ImgUrl, RewardRecordId: record.ID, LastReward: true, ReceiveType: info.ReceiveType, }, nil } // 真实的阶段百分比 func (s *KeepDog) stepPercent(collect, percent int32) float64 { return (float64(collect) / 60) * float64(percent-40) } // 是否可以领取邀请的狗粮 func (s *KeepDog) canGetInviteFood() (ok bool) { player, conf := s.GetPlayer(), s.GetConf() if conf.OpenInvite == mtype.No || player.InviteLimit == mtype.Yes { return } invite := conf.GetInvite() if invite.InviteMaxGet == 0 { return true } num, _ := s.getFoodInviteLimit() if int64(invite.InviteMaxGet) > num { return true } m := dao.FissionKeep _, _ = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.InviteLimit.Value(mtype.Yes)) s.GetPlayer().InviteLimit = mtype.Yes return } // 追加邀请获得的狗粮数,用来防治最大获得数量 func (s *KeepDog) addFoodInviteLimit(num int64) (err error) { player, conf := s.GetPlayer(), s.GetConf() if conf.OpenInvite == mtype.No || player.InviteLimit == mtype.Yes { return } invite := conf.GetInvite() if invite.InviteMaxGet == 0 { return } _, err = s.getFoodInviteLimit() if err != nil { return } app.Redis.Cache.IncrBy(s.Ctx, s.dogFoodLimit, num) app.Redis.Cache.Expire(s.Ctx, s.dogFoodLimit, consts.TimeDay) return } // 获取当前通过邀请获得的狗粮数 func (s *KeepDog) getFoodInviteLimit() (num int64, err error) { player, key := s.GetPlayer(), s.dogFoodLimit return lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := dao.FissionKeepLog return m.WithContext(s.Ctx).Where( m.UserID.Eq(player.UserID), m.Source.In( mtype.FissionKeepLogSourceThird, mtype.FissionKeepLogSourceDayFirstThird, mtype.FissionKeepLogSourceSelect, mtype.FissionKeepLogSourceDayFirstSelect, mtype.FissionKeepLogSourceEffectInvite, mtype.FissionKeepLogSourceDayFirstEffectInvite, ), ).Count() }) } func (s *KeepDog) secGet() float64 { lastGet, _ := s.getMachineLast() bt := time.Now().Unix() - lastGet return float64(bt) * s.GetConf().SecGet } func (s *KeepDog) addKeepLog(logs ...*model.FissionKeepLog) (err error) { var addLogs = make([]*model.FissionKeepLog, 0, len(logs)) for _, log := range logs { addLogs = append(addLogs, withdefault.FissionKeepLog(log)) } err = dao.FissionKeepLog.WithContext(s.Ctx).Create(addLogs...) if err != nil { err = s.BE().MysqlError(err) } return } func (s *KeepDog) machineDayLimit(day string, start, end int64, add float64) (ok bool, err error) { player, conf := s.GetPlayer(), s.GetConf() if conf.SecGetDayLimit == 0 { return } key := ckey.FissionKeepMachineDayLimit(day, s.user.ID) err = lockdo.ExistDo(s.Ctx, key, func(exist bool) (err error) { if exist { return } m := s.GetDao().FissionKeepLog type num struct { Num float64 `json:"num"` } sumField := field.NewFloat64(m.TableName(), m.Val.ColumnName().String()) var sum num err = m.WithContext(s.Ctx). Select(sumField.Sum().As("num")). Where( m.UserID.Eq(player.UserID), m.Type.Eq(mtype.FissionKeepLogTypeProp), m.Source.Eq(mtype.FissionKeepLogSourceMachine), m.CreatedAt.Gte(start), m.CreatedAt.Lte(end), ). Scan(&sum) if err != nil { return } app.Redis.Cache.IncrByFloat(s.Ctx, key, sum.Num) app.Redis.Cache.Expire(s.Ctx, key, consts.TimeDay) return }) if err != nil { return } var count float64 if add > 0 { count = app.Redis.Cache.IncrByFloat(s.Ctx, key, add).Val() } else { count, _ = app.Redis.Cache.Get(s.Ctx, key).Float64() } ok = count >= conf.SecGetDayLimit return } func (s *KeepDog) offlineNumLimit(day string, start, end, add int64) (ok bool, err error) { player, conf, key := s.GetPlayer(), s.GetConf(), ckey.FissionUserKeepOfflineGetNum(day, s.user.ID) if conf.OfflineLimit == 0 { return } num, err := lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionKeepLog return m.WithContext(s.Ctx).Where(m.KeepID.Eq(player.ID), m.Type.Eq(mtype.FissionKeepLogTypeProp), m.Source.Eq(mtype.FissionKeepLogSourceMachineOffline), m.CreatedAt.Between(start, end), ).Count() }) if err != nil { err = s.BE().CacheError(err) return } if add > 0 { app.Redis.Cache.IncrBy(s.Ctx, key, add) num += add } return int64(conf.OfflineLimit) <= num, nil }