You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1508 lines
36 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}