package validevents import ( "joybuywork/internal/app" "joybuywork/internal/config" "joybuywork/internal/consts" "joybuywork/internal/consts/ckey" "joybuywork/internal/repository/cache" "joybuywork/internal/repository/dbcheck" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/repository/withdefault" "joybuywork/internal/service/cmm" "joybuywork/internal/service/provider" "joybuywork/internal/service/provider/award" "joybuywork/internal/service/provider/players" "joybuywork/internal/service/provider/pusher" "joybuywork/internal/service/queue/producer" "joybuywork/internal/service/queue/types" "joybuywork/internal/utils" "joybuywork/internal/utils/lockdo" "joybuywork/internal/utils/timez" "joybuywork/internal/utils/verify" "fmt" "math/rand" "strings" "time" "gorm.io/gen" ) type condFinishOne struct { condId int64 // 条件id num int64 // 用户 userNum int64 // 完成人数 } type condTotalOne struct { condId int64 // 条件id total float64 // 累计值 } type event struct { *base tasks []*model.FissionCampaignTask // 校验完之后,剩下的用户可以完成的任务 taskDoneMap map[int64]int64 taskDayDoneMap map[int64]int64 taskWeekDoneMap map[int64]int64 condDoneMap map[int64]int64 condDayDoneMap map[int64]int64 condWeekDoneMap map[int64]int64 lvLimitMap map[int64][]string preCondMap map[int32]struct{} } func (s *event) event(user *model.FissionUser, callbacks ...taskDoneCallback) (data eventData, err error) { err = s.unlockReward(user, callbacks...) if err != nil { return } conf, err := cache.NewCampaignTicket(s.Ctx).FindByMark(user.CampaignID, mtype.FissionCampaignTicketCustom) if err != nil { err = s.BE().CacheError(err) return } if conf == nil || conf.Open == mtype.No { return } conds, userCondMap, err := s.getUserCondMap(user, false) if err != nil { return } if len(conds) == 0 { return } switch conf.BothType { case mtype.FissionCampaignTicketBothTypeOne: err = s.acceptCheckOne(user, conds) if err != nil { return } case mtype.FissionCampaignTicketBothTypeAll: err = s.acceptCheckAll(user) if err != nil { return } } // 如果接收事件验证校验过后没有任务了,那么就不用去计算了 if len(s.tasks) == 0 { return } // 过期数据处理 err = s.userCondExp(conds, userCondMap, conf.BothType) if err != nil { return } switch conf.BothType { case mtype.FissionCampaignTicketBothTypeOne: return s.one(user, conds, userCondMap, callbacks...) case mtype.FissionCampaignTicketBothTypeAll: return s.all(user, conds, userCondMap, callbacks...) } return } // 所有任务完成逻辑 func (s *event) all(user *model.FissionUser, allConds []*model.FissionCampaignCond, userCondMap map[int64]*model.FissionUserCond, callbacks ...taskDoneCallback) (data eventData, err error) { var task *model.FissionCampaignTask for _, v := range s.tasks { if v.TaskMark == mtype.FissionCampaignTaskMarkCondAll { task = v break } } if task == nil { return } // 过滤被被限制了的事件 conds, err := s.condLimit(user, allConds, task) if err != nil { return } // 过滤到没有到没有可以完成的事件 if len(conds) == 0 { return } // 接收到key对应的值 var valMap = make(map[string][]string) for _, v := range s.Req.EventData { valMap[v.EventKey] = append(valMap[v.EventKey], v.EventVal) } var condIdMap = make(map[int64]*model.FissionCampaignCond) for _, v := range allConds { condIdMap[v.ID] = v } var condTotal = make(map[int64]string) // 累计条件下的已累计的值 var condTotalFinishNumMap = make(map[int64]int64) // 累计条件下完成的次数 for _, v := range userCondMap { cond := condIdMap[v.CondID] if cond.IfCond == mtype.FissionCampaignCondIfCondTotal { condTotal[cond.ID] = v.Total condTotalFinishNumMap[cond.ID] = 0 } } var userCondRecord []*model.FissionUserCondRecord for _, cond := range conds { if cond.Event != s.Req.Event { continue } condCpnLimit, condDayLimit, condWeekLimit := s.condDoneMap[cond.ID], s.condDayDoneMap[cond.ID], s.condWeekDoneMap[cond.ID] userCond := userCondMap[cond.ID] total, num := userCond.Total, userCond.Num var doneNum int64 for _, val := range valMap[cond.EventKey] { // 任务被限制完成了 if s._beLimit(task, condCpnLimit, condDayLimit, condWeekLimit, user) { break } switch cond.IfCond { case mtype.FissionCampaignCondIfCondEq: if cond.EventVal != val { continue } condCpnLimit++ condDayLimit++ condWeekLimit++ doneNum = 1 case mtype.FissionCampaignCondIfCondGte: if !utils.NumberStrCompareGte(val, cond.EventVal) { continue } condCpnLimit++ condDayLimit++ condWeekLimit++ doneNum = 1 case mtype.FissionCampaignCondIfCondTotal: total = utils.StrAdd(total, val) // 计算一下完成的次数 doneRes := utils.CondTotalFinishNum(total, cond.EventVal, func(doneNum int32) bool { condCpnLimit++ condDayLimit++ condWeekLimit++ return s._beTaskNumLimit(task, condCpnLimit, condDayLimit, condWeekLimit) }) total = doneRes.Remain doneNum = int64(doneRes.Num) // 统计完成次数追加 condTotalFinishNumMap[cond.ID] += doneNum // 累计总值 s._condTotal(cond.ID, val) } // 如果事件被消费了 if doneNum > 0 { // 加一条记录 userCondRecord = append(userCondRecord, withdefault.FissionUserCondRecord(&model.FissionUserCondRecord{ CampaignID: user.CampaignID, UserID: user.ID, CondID: cond.ID, Val: val, IsUse: mtype.Yes, BehaviorTime: s.Req.BehaviorTime, })) // 完成次数条件 for i := 0; i < int(doneNum); i++ { s._condFinish(user, cond.ID, val, num == 0) num++ } } } userCondMap[cond.ID].Num += doneNum // 完成次数 userCondMap[cond.ID].CountNum += doneNum // 可以计算的完成次数 userCondMap[cond.ID].Total = total // 累计值 // 加限制值 err = s._addCondLimit(user, task, cond, doneNum) if err != nil { return } } // 把有效的记录记录一下. err = s._addUserCondRecord(user, userCondRecord, conds, task) if err != nil { return } var fl int // 完成的次数 // 更新一下条件数据 defer func() { m := s.GetDao().FissionUserCond for _, v := range userCondMap { _, _ = m.WithContext(s.Ctx).Where(m.ID.Eq(v.ID)).UpdateSimple( m.Num.Value(v.Num), m.CountNum.Value(v.CountNum-int64(fl)), m.Total.Value(v.Total), ) } }() // 看看最小可计算数是谁,就是达标次数 for _, userCond := range userCondMap { if userCond.CountNum == 0 { fl = 0 break } if fl == 0 || fl > int(userCond.CountNum) { fl = int(userCond.CountNum) } } if fl == 0 { return } data.props = make(map[string]int32) data.targetNum = int32(fl) // 达标数 var callbackTasks = make([]*model.FissionCampaignTask, 0, fl) // 要回调的任务 var addTaskRecord = make([]*model.FissionUserTaskRecord, 0, fl) // 任务记录 var rewardIds []int64 // 获得的奖品 var addTaskRewards []*model.FissionUserTaskReward // 待领取记录 for i := 0; i < fl; i++ { record := withdefault.FissionUserTaskRecord(&model.FissionUserTaskRecord{ CreatedAt: s.Req.BehaviorTime, CampaignID: user.CampaignID, UserID: user.ID, TaskID: task.ID, IndicatorData: "[]", Chance: task.Chance, LimitProp: 0, }) addTaskRecord = append(addTaskRecord, record) callbackTasks = append(callbackTasks, task) if task.AwardMode == mtype.FissionCampaignTaskAwardModeAuto { switch task.PropMark { case mtype.FissionCampaignRewardPropMarkReward: data.props[task.PropMark] += 1 rewardIds = append(rewardIds, task.RewardID) case mtype.FissionCampaignRewardPropMarkChance: data.props[task.PropMark] += task.Chance } } else { addTaskRewards = append(addTaskRewards, withdefault.FissionUserTaskReward(&model.FissionUserTaskReward{ UserID: user.ID, CampaignID: user.CampaignID, TaskID: task.ID, ReceiveTime: 0, TaskRecordID: record.ID, })) } } for _, callback := range callbacks { err = callback(user, callbackTasks) if err != nil { return } } if len(addTaskRecord) > 0 { err = s._addUserTaskRecord(addTaskRecord...) if err != nil { return } for _, v := range addTaskRecord { if s.reportType == mtype.FissionCampaignReportTypeNew { s.MP().InApp(user).FinishTask(v.CreatedAt, v.TaskID) } } err = s._addTaskFinish(user, task, int64(fl)) if err != nil { return } } if len(rewardIds) > 0 { _, err = s._taskReward(user, rewardIds) if err != nil { return } } if len(addTaskRewards) > 0 { err = s.GetDao().FissionUserTaskReward.WithContext(s.Ctx).Create(addTaskRewards...) if err != nil { err = s.BE().MysqlError(err) return } } return } // 任一任务完成 func (s *event) one(user *model.FissionUser, conds []*model.FissionCampaignCond, userCondMap map[int64]*model.FissionUserCond, callbacks ...taskDoneCallback) (data eventData, err error) { // 接收到key对应的值 var valMap = make(map[string][]string) for _, v := range s.Req.EventData { valMap[v.EventKey] = append(valMap[v.EventKey], v.EventVal) } // 活动的条件数据 var condIdMap = make(map[int64]*model.FissionCampaignCond) for _, v := range conds { condIdMap[v.ID] = v } var condTotal = make(map[int64]string) // 累计条件下的已累计的值 for _, v := range userCondMap { cond := condIdMap[v.CondID] if cond.IfCond == mtype.FissionCampaignCondIfCondTotal { condTotal[cond.ID] = v.Total } } m := s.GetDao().FissionUserCond cm := cmm.NewCMM(s.Ctx, s.GetDao()) var taskDoneMap = make(map[int64]int64) // 任务完成次数 var userCondRecord []*model.FissionUserCondRecord // 可以完成的任务 for _, v := range s.tasks { // 找到完成这个任务需要完成的条件 cond, ok := condIdMap[utils.FissionCampaignTaskMarkCondOneCondId(v.TaskMark)] if !ok || !cm.EventCanTotal(cond, user.SubUserID, s.Req.AdsCondId, user.JoinAt, s.Req.BehaviorTime) { // 不存在,或者事件是验证目标类型不通过都不处理 continue } cpnLimit, dayLimit, weekLimit := s.taskDoneMap[v.ID], s.taskDayDoneMap[v.ID], s.taskWeekDoneMap[v.ID] userCond := userCondMap[cond.ID] total, num := userCond.Total, userCond.Num // 遍历接收到的key,计算任务完成的次数 for _, val := range valMap[cond.EventKey] { // 任务被限制完成了 if s._beLimit(v, cpnLimit, dayLimit, weekLimit, user) { break } var doneNum int64 switch cond.IfCond { case mtype.FissionCampaignCondIfCondEq: if cond.EventVal != val { continue } cpnLimit++ dayLimit++ weekLimit++ doneNum = 1 case mtype.FissionCampaignCondIfCondGte: if !utils.NumberStrCompareGte(val, cond.EventVal) { continue } cpnLimit++ dayLimit++ weekLimit++ doneNum = 1 case mtype.FissionCampaignCondIfCondTotal: total = utils.StrAdd(total, val) // 计算一下完成的次数 doneRes := utils.CondTotalFinishNum(total, cond.EventVal, func(doneNum int32) bool { cpnLimit++ dayLimit++ weekLimit++ return s._beTaskNumLimit(v, cpnLimit, dayLimit, weekLimit) }) total = doneRes.Remain doneNum = int64(doneRes.Num) // 累计总值 s._condTotal(cond.ID, val) } // 如果事件被消费了 if doneNum > 0 { // 加任务完成次数 taskDoneMap[v.ID] += doneNum // 加一条记录 userCondRecord = append(userCondRecord, withdefault.FissionUserCondRecord(&model.FissionUserCondRecord{ CampaignID: user.CampaignID, UserID: user.ID, CondID: cond.ID, Val: val, IsUse: mtype.Yes, BehaviorTime: s.Req.BehaviorTime, })) // 完成次数条件 for i := 0; i < int(doneNum); i++ { s._condFinish(user, cond.ID, val, num == 0) num++ switch num { case 1: err = s._invokePreCond(user, mtype.FissionCampaignTaskPreCondTask1) if err != nil { return } case 2: err = s._invokePreCond(user, mtype.FissionCampaignTaskPreCondTask2) if err != nil { return } } } } } // 更新一下完成次数 switch cond.IfCond { case mtype.FissionCampaignCondIfCondTotal: // 更新一下累计值和完成次数 _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(userCond.ID)).UpdateSimple(m.Num.Add(taskDoneMap[v.ID]), m.Total.Value(total)) if err != nil { err = s.BE().MysqlError(err) return } default: // 更新一下用户条件的完成次数 if fNum := taskDoneMap[v.ID]; fNum > 0 { _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(userCond.ID)).UpdateSimple(m.Num.Add(fNum)) if err != nil { err = s.BE().MysqlError(err) return } } } } // 把有效的记录记录一下. err = s._addUserCondRecord(user, userCondRecord, conds, nil) if err != nil { return } data.props = make(map[string]int32) // 完成任务 var rewardIds []int64 var callbackTasks []*model.FissionCampaignTask var add []*model.FissionUserTaskRecord var addTaskRewards []*model.FissionUserTaskReward // 待领取记录 for _, task := range s.tasks { fNum := taskDoneMap[task.ID] if fNum == 0 { continue } err = s._addTaskFinish(user, task, fNum) if err != nil { return } // 达标数 data.targetNum += int32(fNum) // 完成一次回调一次 for i := 0; i < int(fNum); i++ { record := withdefault.FissionUserTaskRecord(&model.FissionUserTaskRecord{ CreatedAt: s.Req.BehaviorTime, CampaignID: user.CampaignID, UserID: user.ID, TaskID: task.ID, IndicatorData: "[]", Chance: task.Chance, LimitProp: 0, }) add = append(add, record) callbackTasks = append(callbackTasks, task) if task.AwardMode == mtype.FissionCampaignTaskAwardModeAuto { switch task.PropMark { case mtype.FissionCampaignRewardPropMarkReward: data.props[task.PropMark] += 1 rewardIds = append(rewardIds, task.RewardID) case mtype.FissionCampaignRewardPropMarkChance: data.props[task.PropMark] += task.Chance players.AddTaskTip(s.Ctx, user.ID, mtype.FissionCampaignRewardPropMarkChance, task.Chance, 0) case mtype.FissionCampaignRewardPropMarkPoint: data.props[task.PropMark] += task.Chance rewardIds = append(rewardIds, task.RewardID) } } else { addTaskRewards = append(addTaskRewards, withdefault.FissionUserTaskReward(&model.FissionUserTaskReward{ UserID: user.ID, CampaignID: user.CampaignID, TaskID: task.ID, ReceiveTime: 0, TaskRecordID: record.ID, })) } } } if len(callbackTasks) > 0 { for _, callback := range callbacks { err = callback(user, callbackTasks) if err != nil { return } } } if len(rewardIds) > 0 { _, err = s._taskReward(user, rewardIds) if err != nil { return } } if len(add) > 0 { err = s._addUserTaskRecord(add...) if err != nil { return } for _, v := range add { if s.reportType == mtype.FissionCampaignReportTypeNew { s.MP().InApp(user).FinishTask(v.CreatedAt, v.TaskID) } } } if len(addTaskRewards) > 0 { err = s.GetDao().FissionUserTaskReward.WithContext(s.Ctx).Create(addTaskRewards...) if err != nil { err = s.BE().MysqlError(err) return } } return } // 获取用户对应条件完成状态hash表数据 func (s *event) getUserCondMap(user *model.FissionUser, unlockReward bool) (conds []*model.FissionCampaignCond, data map[int64]*model.FissionUserCond, err error) { condM := s.GetDao().FissionCampaignCond var wheres = []gen.Condition{ condM.CampaignID.Eq(user.CampaignID), condM.IsDel.Eq(mtype.No), } if unlockReward { wheres = append(wheres, condM.RewardID.Gt(0)) } else { wheres = append(wheres, condM.RewardID.Eq(0)) } conds, err = condM.WithContext(s.Ctx).Where(wheres...).Find() if err != nil { err = s.BE().MysqlError(err) return } var condIds = make([]int64, 0, len(conds)) for _, cond := range conds { condIds = append(condIds, cond.ID) } m := s.GetDao().FissionUserCond list, err := m.WithContext(s.Ctx).Where(m.UserID.Eq(user.ID), m.CondID.In(condIds...)).Find() if err != nil { err = s.BE().MysqlError(err) return } data = make(map[int64]*model.FissionUserCond) for _, v := range list { data[v.CondID] = v } var add []*model.FissionUserCond for _, condId := range condIds { if _, ok := data[condId]; !ok { add = append(add, withdefault.FissionUserCond(&model.FissionUserCond{ CampaignID: user.CampaignID, UserID: user.ID, CondID: condId, Num: 0, Total: "", CountNum: 0, })) } } if len(add) == 0 { return } err = m.WithContext(s.Ctx).Create(add...) if err != nil { err = s.BE().MysqlError(err) return } for _, v := range add { data[v.CondID] = v } return } // 看看需不需要做一些过期处理 func (s *event) userCondExp(conds []*model.FissionCampaignCond, data map[int64]*model.FissionUserCond, bothType int8) (err error) { if bothType == mtype.FissionCampaignTicketBothTypeAll { task := s.tasks[0] var start, end int64 if task.DayLimit > 0 { _, start, end = timez.GMTStartAndEndTs(s.tOffset, s.Req.BehaviorTime) } else if task.WeekLimit > 0 { start, end = timez.GetWeekEnd(s.tOffset, int32(task.WeekNum)) } if start == 0 { return } var expId []int64 for _, cond := range conds { if data[cond.ID].TotalExp < start { data[cond.ID].TotalExp = end data[cond.ID].Total = "" data[cond.ID].CountNum = 0 expId = append(expId, data[cond.ID].ID) } } if len(expId) == 0 { return } m := s.GetDao().FissionUserCond _, err = m.WithContext(s.Ctx).Where(m.ID.In(expId...)).UpdateSimple( m.TotalExp.Value(end), m.Total.Value(""), m.CountNum.Value(0), ) if err != nil { err = s.BE().MysqlError(err) return } return } // 完成任一任务 var taskMap = make(map[string]int64) for _, cond := range conds { taskMap[utils.FissionCampaignTaskMarkCondOneVal(cond.ID)] = cond.ID } for _, task := range s.tasks { var start, end int64 if task.DayLimit > 0 { _, start, end = timez.GMTStartAndEndTs(s.tOffset, s.Req.BehaviorTime) } else if task.WeekLimit > 0 { start, end = timez.GetWeekEnd(s.tOffset, int32(task.WeekNum)) } if start == 0 { continue } condId, ok := taskMap[task.TaskMark] if ok && data[condId].TotalExp < start { data[condId].TotalExp = end data[condId].Total = "" m := s.GetDao().FissionUserCond _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(data[condId].ID)).UpdateSimple( m.TotalExp.Value(end), m.Total.Value(""), m.CountNum.Value(0), ) if err != nil { err = s.BE().MysqlError(err) return } } } return } // 接收校验任一条件完成的事件 func (s *event) acceptCheckAll(user *model.FissionUser) (err error) { m := s.GetDao().FissionCampaignTask tasks, err := m.WithContext(s.Ctx).Where(m.CampaignID.Eq(user.CampaignID), m.TaskMark.Eq(mtype.FissionCampaignTaskMarkCondAll), m.IsDel.Eq(mtype.No)).Find() if err != nil { err = s.BE().MysqlError(err) return } return s.taskLimit(user, tasks) } // 接收校验任一条件完成的事件 func (s *event) acceptCheckOne(user *model.FissionUser, list []*model.FissionCampaignCond) (err error) { var t = utils.Ternary[int32](s.Req.EventType == 0, mtype.FissionCampaignCondEventTypeCustom, s.Req.EventType) var keys = make(map[string]struct{}, len(s.Req.EventData)) for _, v := range s.Req.EventData { keys[fmt.Sprintf("%s_%s_%d", s.Req.Event, v.EventKey, t)] = struct{}{} } var conds []*model.FissionCampaignCond for _, v := range list { if _, ok := keys[fmt.Sprintf("%s_%s_%d", v.Event, v.EventKey, t)]; ok { conds = append(conds, v) } } var marks []string for _, v := range conds { marks = append(marks, utils.FissionCampaignTaskMarkCondOneVal(v.ID)) } if len(marks) == 0 { return } m := s.GetDao().FissionCampaignTask tasks, err := m.WithContext(s.Ctx).Where(m.CampaignID.Eq(user.CampaignID), m.TaskMark.In(marks...), m.IsDel.Eq(mtype.No)).Find() if err != nil { err = s.BE().MysqlError(err) return } return s.taskLimit(user, tasks) } // 任务已经被限制 func (s *event) taskLimit(user *model.FissionUser, tasks []*model.FissionCampaignTask) (err error) { var userId = user.ID var preCond []int32 var levelLimit = make(map[int64][]int64) for _, task := range tasks { var fLimit bool fLimit, err = s._fissionLimit(user, task) if err != nil { return } if fLimit { continue } if task.TaskLimit > 0 { var num int64 num, err = s._cpnNum(userId, task.ID) if err != nil { return } if num >= int64(task.TaskLimit) { continue } } if task.DayLimit > 0 { var num int64 num, err = s._dayNum(userId, task.ID) if err != nil { return } if num >= int64(task.DayLimit) { continue } } if task.WeekLimit > 0 { var num int64 num, err = s._weekNum(userId, task.ID, int32(task.WeekNum)) if err != nil { return } if num >= int64(task.WeekLimit) { continue } } if task.LevelLimit != "" { levelLimit[task.ID] = task.GetLevelLimit() } if task.HasPre == mtype.Yes { preCond = append(preCond, task.PreCond) } s.tasks = append(s.tasks, task) } err = s._levelLimit(levelLimit) if err != nil { return } return s._preCond(userId, preCond) } func (s *event) _fissionLimit(user *model.FissionUser, task *model.FissionCampaignTask) (ok bool, err error) { switch s.cpn.FissionID { case consts.IdKeep: var kg *players.KeepDog kg, err = players.NewKeepDog(s.Ctx, user) if err != nil { return } return kg.TaskLimit(task) } return } // 用户等级限制 func (s *event) _levelLimit(taskLvIdMap map[int64][]int64) (err error) { var titleMap = make(map[int64]string) var ids []int64 for _, lvIds := range taskLvIdMap { for _, lvId := range lvIds { if _, ok := titleMap[lvId]; !ok { titleMap[lvId] = "" ids = append(ids, lvId) } } } s.lvLimitMap = make(map[int64][]string) if len(ids) == 0 { return } m := s.GetDao().FissionUserLevel list, err := m.WithContext(s.Ctx).Where(m.ID.In(ids...), m.DeletedAt.Eq(0)).Find() if err != nil { err = s.BE().MysqlError(err) return } for _, v := range list { titleMap[v.ID] = v.Mark } for id, lvIds := range taskLvIdMap { for _, lvId := range lvIds { s.lvLimitMap[id] = append(s.lvLimitMap[id], titleMap[lvId]) } } return } // 前置任务 func (s *event) _preCond(userId int64, preCond []int32) (err error) { s.preCondMap = make(map[int32]struct{}, len(preCond)) if len(preCond) == 0 { return } m := s.GetDao().FissionUserPreCond done, err := m.WithContext(s.Ctx).Select(m.PreCond).Where(m.UserID.Eq(userId), m.PreCond.In(preCond...), m.ExpiredAt.Gt(time.Now().Unix())).Find() if err != nil { err = s.BE().MysqlError(err) return } for _, v := range done { s.preCondMap[v.PreCond] = struct{}{} } return } // 活动内限制 func (s *event) _cpnNum(userId, taskId int64) (num int64, err error) { num, err = s._taskFinishNum(userId, taskId) if err != nil { return } if s.taskDoneMap == nil { s.taskDoneMap = make(map[int64]int64) } s.taskDoneMap[taskId] = num return } // 当天任务完成数 func (s *event) _dayNum(userId, taskId int64) (num int64, err error) { day, start, end := timez.GMTStartAndEndTs(s.tOffset, s.Req.BehaviorTime) key := ckey.FissionUserTaskDayLimit(day, userId, taskId) num, err = lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionUserTaskRecord return m.WithContext(s.Ctx).Where( m.UserID.Eq(userId), m.TaskID.Eq(taskId), m.CreatedAt.Gte(start), m.CreatedAt.Lte(end), ).Count() }) if err != nil { err = s.BE().CacheError(err) return } if s.taskDayDoneMap == nil { s.taskDayDoneMap = make(map[int64]int64) } s.taskDayDoneMap[taskId] = num return } // 周任务完成数 func (s *event) _weekNum(userId, taskId int64, weekNum int32) (num int64, err error) { start, end := timez.GetWeekEnd(s.tOffset, weekNum) key := ckey.FissionUserWeekLimit(start, userId, taskId) num, err = lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionUserTaskRecord return m.WithContext(s.Ctx).Where( m.UserID.Eq(userId), m.TaskID.Eq(taskId), m.CreatedAt.Between(start, end), ).Count() }) if err != nil { err = s.BE().CacheError(err) return } if s.taskWeekDoneMap == nil { s.taskWeekDoneMap = make(map[int64]int64) } s.taskWeekDoneMap[taskId] = num return } // 判断是否被限制了 func (s *event) _beLimit(task *model.FissionCampaignTask, cpnLimit, dayLimit, weekLimit int64, user *model.FissionUser) bool { // 用户等级限制 if task.LevelLimit != "" && s._beLevelLimit(task.ID, user.LevelMark) { return true } // 语言限制 if lm := task.GetLangMarks(); len(lm) > 0 && !verify.InArray(user.LangMark, lm...) { return true } // 有前置任务 if task.HasPre == mtype.Yes { if _, ok := s.preCondMap[task.PreCond]; !ok { return true } } return s._beTaskNumLimit(task, cpnLimit, dayLimit, weekLimit) } // 任务可完成次数限制 func (s *event) _beTaskNumLimit(task *model.FissionCampaignTask, cpnLimit, dayLimit, weekLimit int64) bool { // 活动限制 if task.TaskLimit > 0 && task.TaskLimit <= int32(cpnLimit) { return true } // 每日限制 if task.DayLimit > 0 && task.DayLimit <= int32(dayLimit) { return true } // 周限制 if task.WeekLimit > 0 && task.WeekLimit <= int32(weekLimit) { return true } return false } // 事件已经被限制 func (s *event) condLimit(user *model.FissionUser, conds []*model.FissionCampaignCond, task *model.FissionCampaignTask) (canDo []*model.FissionCampaignCond, err error) { cm := cmm.NewCMM(s.Ctx, s.GetDao()) for _, cond := range conds { if !cm.EventCanTotal(cond, user.SubUserID, s.Req.AdsCondId, user.JoinAt, s.Req.BehaviorTime) { continue } if cond.IfCond == mtype.FissionCampaignCondIfCondTotal { canDo = append(canDo, cond) continue } if task.TaskLimit > 0 { var num int64 num, err = s._cpnCondDoneNum(user.ID, cond.ID) if err != nil { return } if num >= int64(task.TaskLimit) { continue } } if task.DayLimit > 0 { var num int64 num, err = s._dayCondDoneNum(user.ID, cond.ID) if err != nil { return } if num >= int64(task.DayLimit) { continue } } if task.WeekLimit > 0 { var num int64 num, err = s._weekCondDoneNum(user.ID, cond.ID, int32(task.WeekNum)) if err != nil { return } if num >= int64(task.WeekLimit) { continue } } canDo = append(canDo, cond) } return } // 活动内限制 func (s *event) _cpnCondDoneNum(userId, condId int64) (num int64, err error) { key := ckey.FissionUserCondCpnLimit(userId, condId) num, err = lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionUserCondRecord return m.WithContext(s.Ctx).Where(m.UserID.Eq(userId), m.CondID.Eq(condId)).Count() }) if err != nil { err = s.BE().CacheError(err) return } if s.condDoneMap == nil { s.condDoneMap = make(map[int64]int64) } s.condDoneMap[condId] = num return } // 当天任务完成数 func (s *event) _dayCondDoneNum(userId, condId int64) (num int64, err error) { day, start, end := timez.GMTStartAndEndTs(s.tOffset, s.Req.BehaviorTime) key := ckey.FissionUserCondDayLimit(day, userId, condId) num, err = lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionUserCondRecord return m.WithContext(s.Ctx).Where( m.UserID.Eq(userId), m.CondID.Eq(condId), m.BehaviorTime.Gte(start), m.BehaviorTime.Lte(end), ).Count() }) if err != nil { err = s.BE().CacheError(err) return } if s.condDayDoneMap == nil { s.condDayDoneMap = make(map[int64]int64) } s.condDayDoneMap[condId] = num return } // 周任务完成数 func (s *event) _weekCondDoneNum(userId, condId int64, weekNum int32) (num int64, err error) { start, end := timez.GetWeekEnd(s.tOffset, weekNum) key := ckey.FissionUserCondWeekLimit(start, userId, condId) num, err = lockdo.Int64(s.Ctx, key, func() (num int64, err error) { m := s.GetDao().FissionUserCondRecord return m.WithContext(s.Ctx).Where( m.UserID.Eq(userId), m.CondID.Eq(condId), m.BehaviorTime.Between(start, end), ).Count() }) if err != nil { err = s.BE().CacheError(err) return } if s.condWeekDoneMap == nil { s.condWeekDoneMap = make(map[int64]int64) } s.condWeekDoneMap[condId] = num return } // 同步限制数到缓存 func (s *event) _addCondLimit(user *model.FissionUser, task *model.FissionCampaignTask, cond *model.FissionCampaignCond, chance int64) (err error) { if task.TaskLimit > 0 { key := ckey.FissionUserCondCpnLimit(user.ID, cond.ID) err = lockdo.ExistDo(s.Ctx, key, func(exist bool) (err error) { if !exist { var num int64 m := s.GetDao().FissionUserCondRecord num, err = m.WithContext(s.Ctx).Where(m.UserID.Eq(user.ID), m.CondID.Eq(cond.ID)).Count() if err != nil { return } app.Redis.Cache.IncrBy(s.Ctx, key, num) } app.Redis.Cache.IncrBy(s.Ctx, key, chance) app.Redis.Cache.Expire(s.Ctx, key, time.Duration(rand.Intn(60)+60)*time.Minute) return }) if err != nil { err = s.BE().CacheError(err) return } } if task.DayLimit > 0 { day, start, end := timez.GMTStartAndEndTs(s.tOffset) key := ckey.FissionUserCondDayLimit(day, user.ID, cond.ID) err = lockdo.ExistDo(s.Ctx, key, func(exist bool) (err error) { if !exist { var num int64 m := s.GetDao().FissionUserCondRecord num, err = m.WithContext(s.Ctx).Where( m.UserID.Eq(user.ID), m.CondID.Eq(cond.ID), m.CreatedAt.Gte(start), m.CreatedAt.Lte(end), ).Count() if err != nil { return } app.Redis.Cache.IncrBy(s.Ctx, key, num) } app.Redis.Cache.IncrBy(s.Ctx, key, chance) app.Redis.Cache.Expire(s.Ctx, key, time.Duration(rand.Intn(60)+60)*time.Minute) return }) if err != nil { err = s.BE().CacheError(err) return } } if task.WeekLimit > 0 { start, end := timez.GetWeekEnd(s.tOffset, int32(task.WeekNum)) key := ckey.FissionUserCondWeekLimit(start, user.ID, cond.ID) err = lockdo.ExistDo(s.Ctx, key, func(exist bool) (err error) { if !exist { var num int64 m := s.GetDao().FissionUserCondRecord num, err = m.WithContext(s.Ctx).Where( m.UserID.Eq(user.ID), m.CondID.Eq(cond.ID), m.CreatedAt.Between(start, end), ).Count() if err != nil { return } app.Redis.Cache.IncrBy(s.Ctx, key, num) } app.Redis.Cache.IncrBy(s.Ctx, key, chance) app.Redis.Cache.Expire(s.Ctx, key, time.Duration(rand.Intn(60)+60)*time.Minute) return }) if err != nil { err = s.BE().CacheError(err) return } } return } // 是不是等级限制 func (s *event) _beLevelLimit(taskId int64, levelMark string) bool { marks, ok := s.lvLimitMap[taskId] if !ok { //没有证明没开限制 return false } return !verify.OneInArray(marks, strings.Split(levelMark, ",")) } // 用户事件的完成记录 func (s *event) _addUserCondRecord(user *model.FissionUser, userCondRecord []*model.FissionUserCondRecord, conds []*model.FissionCampaignCond, task *model.FissionCampaignTask) (err error) { if len(userCondRecord) == 0 { return } err = s.GetDao().FissionUserCondRecord.WithContext(s.Ctx).Create(userCondRecord...) if err != nil { err = s.BE().MysqlError(err) return } var condMap = make(map[int64]*model.FissionCampaignCond, len(conds)) var openPush = make(map[int64]struct{}) var ids []int64 for _, cond := range conds { ids = append(ids, cond.ID) condMap[cond.ID] = cond if cond.TriggetType == mtype.FissionCampaignCondTriggerTypeGlobal && cond.OpenPush == mtype.Yes { openPush[cond.ID] = struct{}{} } } var taskLotteryMap = make(map[int64]int32, len(s.tasks)) var taskDayLimitMap = make(map[int64]int32, len(s.tasks)) for _, v := range s.tasks { taskLotteryMap[utils.FissionCampaignTaskMarkCondOneCondId(v.TaskMark)] = v.Chance taskDayLimitMap[utils.FissionCampaignTaskMarkCondOneCondId(v.TaskMark)] = v.DayLimit } langTitle, err := provider.NewLangTitle(s.Ctx, s.cpn, user.LangMark) if err != nil { return } taskTitleMap, err := langTitle.GetCondTitle(ids...) if err != nil { return } pageTitle, err := langTitle.GetPageTitle() if err != nil { return } push, _ := pusher.NewPusher(s.Ctx, s.cpn) fc, _ := provider.NewFissionConfig(s.Ctx, push.GetProject()) for _, v := range userCondRecord { if fc.Config().PushCampaignTask == mtype.Yes { cond, ok := condMap[v.CondID] if !ok { continue } var lotteryNum int32 var dayLimit int32 if task != nil { lotteryNum = task.Chance dayLimit = task.DayLimit } else { lotteryNum = taskLotteryMap[v.CondID] dayLimit = taskDayLimitMap[v.CondID] } producer.NewOplatformOther(s.Ctx).OpoCampaignCondOk(types.OpoCampaignCondOk{ ProjectId: s.cpn.ProjectID, CampaignHashId: s.cpn.HashID, CampaignTitle: s.cpn.Title, UserId: user.SubUserID, DeviceId: user.DeviceID, TaskTitle: taskTitleMap[v.CondID], PageTitle: pageTitle, FissionIcon: fmt.Sprintf("%s/%s", config.Config.Upload.Host, consts.GetPushLogo(s.cpn.FissionID, int32(s.cpn.Target))), CampaignUrl: push.GetChannelUrl(user.ChannelID), MarkDesc: app.TalkMap.CompeteTask(utils.NotExistGetFirst[string](user.LangMark, s.cpn.GetLangMarks())), Event: cond.Event, EventKey: cond.EventKey, LotteryNum: lotteryNum, DayLimit: dayLimit, }, producer.SetHeaderLang(user.LangMark)) } if _, ok := openPush[v.CondID]; ok && push != nil { _ = push.CompleteTask(user) } } return } // 自动发放奖品 func (s *event) _taskReward(user *model.FissionUser, rewardIds []int64) (records []*award.RewardRes, err error) { records, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), s.cpn.ProjectID, user).RewardIdsBatch(rewardIds, true) if err != nil { return } for _, record := range records { r := record.Record players.AddTaskTip(s.Ctx, user.ID, r.PropMark, 0, r.ID) } return } // 触发前置任务 func (s *event) _invokePreCond(user *model.FissionUser, preCond int32) (err error) { taskM := s.GetDao().FissionCampaignTask task, err := taskM.WithContext(s.Ctx).Where( taskM.CampaignID.Eq(user.CampaignID), taskM.PreCond.Eq(preCond), taskM.IsDel.Eq(mtype.No), ).Take() if err = dbcheck.DbError(err); err != nil { err = s.BE().MysqlError(err) return } if task == nil { return } m := s.GetDao().FissionUserPreCond err = m.WithContext(s.Ctx).Create(withdefault.FissionUserPreCond(&model.FissionUserPreCond{ CampaignID: user.CampaignID, UserID: user.ID, PreCond: task.PreCond, ExpiredAt: time.Now().Add(time.Duration(task.ExpiredMin) * time.Minute).Unix(), })) if err != nil { err = s.BE().MysqlError(err) return } provider.NewFissionClue(s.Ctx, user.ID).SetPreCond(preCond, task.ExpiredMin) return } func (s *event) unlockReward(user *model.FissionUser, callbacks ...taskDoneCallback) (err error) { unlockConds, userCondMap, err := s.getUserCondMap(user, true) if err != nil { return } if len(unlockConds) == 0 { return } var unlockCond = make(map[int64]*model.FissionCampaignCond, len(unlockConds)) var rewardIds []int64 for _, cond := range unlockConds { rewardIds = append(rewardIds, cond.RewardID) unlockCond[cond.RewardID] = cond } m := s.GetDao().FissionUnlockReward list, err := m.WithContext(s.Ctx).Where( m.CampaignID.Eq(user.CampaignID), m.UserID.Eq(user.ID), m.RewardID.In(rewardIds...), ).Find() if err != nil { return } for _, v := range list { _, ok := unlockCond[v.RewardID] if ok { delete(unlockCond, v.RewardID) } } if len(unlockCond) == 0 { return } var conds []*model.FissionCampaignCond for _, cond := range unlockConds { conds = append(conds, cond) } err = s.acceptCheckOne(user, conds) if err != nil { return } // 如果接收事件验证校验过后没有任务了,那么就不用去计算了 if len(s.tasks) == 0 { return } _, err = s.one(user, conds, userCondMap, callbacks...) if err != nil { return } s.tasks = nil return }