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.

424 lines
11 KiB
Go

package campaignnotice
import (
"joybuywork/internal/app"
"joybuywork/internal/consts"
"joybuywork/internal/consts/ckey"
"joybuywork/internal/repository/cache"
"joybuywork/internal/repository/model"
"joybuywork/internal/repository/mtype"
"joybuywork/internal/service/cmm"
"joybuywork/internal/service/provider/award"
"joybuywork/internal/service/provider/stocks"
"joybuywork/internal/service/queue/producer"
"joybuywork/internal/service/queue/types"
"joybuywork/internal/utils/lockdo"
"joybuywork/internal/utils/logic"
"time"
)
type RewardLimitUp struct {
logic.Logic
Req *types.RewardLimitUp
}
func (s *RewardLimitUp) Exec() (err error) {
cpn, err := cache.NewCampaign(s.Ctx).FindById(s.Req.CampaignId)
if err != nil {
err = s.BE().CacheError(err)
return
}
switch cpn.FissionID {
case consts.IdReferral, consts.IdWinTogether:
app.Redis.Cache.Del(s.Ctx, ckey.FissionCampaignRewardLimitPool(cpn.ID))
stocks.NewCleaner(s.Ctx, cpn.ID).ClearCampaign()
default:
}
switch cpn.FissionID {
case consts.IdReferral:
return s._referral(cpn)
case consts.IdWinTogether:
return s._win(cpn)
case consts.IdGrow:
return s._grow(cpn)
default:
}
return
}
// 版本控制执行
func (s *RewardLimitUp) _verDone(cpnId int64, fn func() (bool, error)) (err error) {
key := ckey.FissionRewardLimitUpVer(cpnId)
now := time.Now()
for {
if app.NewVerControl(s.Ctx, key).HasNewVersion(now) {
return
}
end, err1 := fn()
if err1 != nil || end {
err = err1
return
}
}
}
func (s *RewardLimitUp) _grow(cpn *model.FissionCampaign) (err error) {
var id int64
limit := 50
rewardM := s.GetDao().FissionCampaignReward
rewardList, err := rewardM.WithContext(s.Ctx).Where(rewardM.ID.In(s.Req.RewardIds...)).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
var ids []int64
var rewardMap = make(map[int64]*model.FissionCampaignReward)
for _, reward := range rewardList {
rewardMap[reward.ID] = reward
ids = append(ids, reward.ID)
}
m := s.GetDao().FissionRewardRecord
ms := s.GetDao().FissionRewardRecord.SubTable(cpn.ID)
return s._verDone(cpn.ID, func() (bool, error) {
var list []*model.FissionRewardRecord
list, err = ms.WithContext(s.Ctx).
Where(
ms.CampaignID.Eq(cpn.ID),
ms.RewardID.In(ids...),
ms.Status.Eq(mtype.FissionRewardRecordStatusFail),
ms.FailMsg.Eq(mtype.FissionRewardRecordFailTypeCliLackMsg),
ms.ID.Gt(id),
).Limit(limit).Order(ms.ID).Find()
if err != nil {
return false, s.BE().MysqlError(err)
}
if len(list) == 0 {
return true, nil
}
var recordIds []int64
var pushRecords []*model.FissionRewardRecord
for _, record := range list {
ok, _ := award.NewAwardIssVerify(s.Ctx, nil, rewardMap[record.RewardID]).DeductStock().IsOK()
if !ok {
continue
}
recordIds = append(recordIds, record.ID)
pushRecords = append(pushRecords, record)
}
if len(recordIds) == 0 {
return true, nil
}
_, _ = m.WithContext(s.Ctx).Where(m.ID.In(recordIds...)).UpdateSimple(
m.Status.Value(mtype.FissionRewardRecordStatusWait),
m.FailType.Value(""),
m.FailMsg.Value(""),
)
_, _ = ms.WithContext(s.Ctx).Where(ms.ID.In(recordIds...)).UpdateSimple(
ms.Status.Value(mtype.FissionRewardRecordStatusWait),
ms.FailType.Value(""),
ms.FailMsg.Value(""),
)
for _, record := range pushRecords {
producer.NewCampaignNotice(s.Ctx).CampaignRewardNotify(types.CampaignRewardNotify{
CampaignId: record.CampaignID,
UserId: record.UserID,
UserRewardId: record.ID,
})
}
l := len(list)
id = list[l-1].ID
return l < limit, nil
})
}
func (s *RewardLimitUp) _referral(cpn *model.FissionCampaign) (err error) {
conf, err := cache.NewCampaignReferral(s.Ctx).FindByCampaignId(cpn.ID)
if err != nil {
err = s.BE().CacheError(err)
return
}
var id int64
limit := 20
m := s.GetDao().FissionReferral
return s._verDone(cpn.FissionID, func() (bool, error) {
var list []*model.FissionReferral
list, err = m.WithContext(s.Ctx).
Where(
m.CampaignID.Eq(cpn.ID),
m.LimitStatus.Eq(mtype.FissionReferralLimitStatusBudget),
m.ID.Gt(id),
).Limit(limit).Order(m.ID).Find()
if err != nil {
return false, s.BE().MysqlError(err)
}
if len(list) == 0 {
return true, nil
}
var userId []int64
for _, v := range list {
userId = append(userId, v.UserID)
}
userM := s.GetDao().FissionUser.SubTable(cpn.ID)
var users []*model.FissionUser
users, err = userM.WithContext(s.Ctx).Where(userM.ID.In(userId...)).Find()
if err != nil {
return false, s.BE().MysqlError(err)
}
var userMap = make(map[int64]*model.FissionUser, len(users))
for _, v := range users {
userMap[v.ID] = v
}
for _, v := range list {
err = s._compensate(cpn, userMap[v.UserID], conf.RewardRule, conf.StepLoop, conf.RewardLimit, v.NextStep, v.LoopInvite, func(limitStatus int8, nextStep, loopInvite int32) (err error) {
playerM := s.GetDao().FissionReferral
_, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(v.ID)).UpdateSimple(playerM.LimitStatus.Value(limitStatus))
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
})
}
l := len(list)
id = list[l-1].ID
return l < limit, nil
})
}
func (s *RewardLimitUp) _win(cpn *model.FissionCampaign) (err error) {
conf, err := cache.NewCampaignWin(s.Ctx).FindByCampaignId(cpn.ID)
if err != nil {
err = s.BE().CacheError(err)
return
}
var id int64
limit := 20
m := s.GetDao().FissionWin
return s._verDone(cpn.FissionID, func() (bool, error) {
var list []*model.FissionWin
list, err = m.WithContext(s.Ctx).
Where(
m.CampaignID.Eq(cpn.ID),
m.LimitStatus.Eq(mtype.FissionReferralLimitStatusBudget),
m.ID.Gt(id),
).Limit(limit).Order(m.ID).Find()
if err != nil {
return false, s.BE().MysqlError(err)
}
if len(list) == 0 {
return true, nil
}
var userId []int64
for _, v := range list {
userId = append(userId, v.UserID)
}
userM := s.GetDao().FissionUser.SubTable(cpn.ID)
var users []*model.FissionUser
users, err = userM.WithContext(s.Ctx).Where(userM.ID.In(userId...)).Find()
if err != nil {
return false, s.BE().MysqlError(err)
}
var userMap = make(map[int64]*model.FissionUser, len(users))
for _, v := range users {
userMap[v.ID] = v
}
for _, v := range list {
err = s._compensate(cpn, userMap[v.UserID], conf.RewardRule, conf.StepLoop, conf.RewardLimit, v.NextStep, v.LoopInvite, func(limitStatus int8, nextStep, loopInvite int32) (err error) {
playerM := s.GetDao().FissionWin
_, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(v.ID)).UpdateSimple(playerM.LimitStatus.Value(limitStatus))
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
})
}
l := len(list)
id = list[l-1].ID
return l < limit, nil
})
}
func (s *RewardLimitUp) _compensate(cpn *model.FissionCampaign, user *model.FissionUser, rewardRule, stepLoop int8, rewardLimit, nextStep, loopInvite int32, limitDo limitDo) (err error) {
rewards, err := cmm.NewUserAppReward(s.Ctx).Get(s.GetDao(), user, s.GetDao().FissionCampaignReward.Step)
if err != nil {
err = s.BE().MysqlError(err)
return
}
m := s.GetDao().FissionUser.SubTable(user.CampaignID)
statKey := ckey.FissionUserInviteStat(user.ID)
num, err := lockdo.Int64(s.Ctx, statKey, func() (num int64, err error) {
return m.WithContext(s.Ctx).Where(
m.CampaignID.Eq(user.CampaignID),
m.ParentID.Eq(user.ID),
m.IsHelp.Eq(mtype.Yes),
).Count()
})
if err != nil {
err = s.BE().CacheError(err)
return
}
switch rewardRule {
case mtype.FissionReferralRewardRuleStep:
return s._step(cpn, user, num, rewards, stepLoop, nextStep, loopInvite, rewardLimit, limitDo)
case mtype.FissionReferralRewardRuleCycle:
return s._cycle(cpn, user, num, rewards, rewardLimit, limitDo)
}
return
}
type limitDo func(limitStatus int8, nextStep, loopInvite int32) (err error)
func (s *RewardLimitUp) _step(cpn *model.FissionCampaign, user *model.FissionUser, effectNum int64, rewards []*model.FissionCampaignReward, stepLoop int8, nextStep, loopInvite, rewardLimit int32, limitDo limitDo) (err error) {
var stepMap = make(map[int32]*model.FissionCampaignReward, len(rewards))
var inviteAll int32
var maxStep int32
for _, reward := range rewards {
inviteAll += reward.InviteNum
stepMap[reward.Step] = reward
if reward.Step > maxStep {
maxStep = reward.Step
}
}
for {
if stepLoop == mtype.Yes && (loopInvite*inviteAll) >= rewardLimit {
return limitDo(mtype.FissionReferralLimitStatusInvite, nextStep, loopInvite)
}
cn := int32(effectNum) - (loopInvite * inviteAll)
reward, ok := stepMap[nextStep]
if !ok {
return limitDo(mtype.FissionReferralLimitStatusFinish, 0, loopInvite)
}
if reward.InviteNum > cn {
return limitDo(mtype.FissionReferralLimitStatusNone, reward.Step, loopInvite)
}
var decr = int64(reward.RewardNum) //奖品数据
//扣除总预算是否成功
var stock *stocks.Campaign
stock, err = stocks.NewCampaign(s.Ctx, cpn.ID)
if err != nil {
return
}
decrOk, _ := stock.DecrPoolAndAdd(cpn.ID, decr)
if !decrOk {
return limitDo(mtype.FissionReferralLimitStatusBudget, reward.Step, loopInvite)
}
_, _, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), cpn.ProjectID, user).One(reward, true)
if err != nil {
return
}
nextStep++
if nextStep > maxStep && stepLoop == mtype.Yes {
nextStep = 1
loopInvite++
}
}
}
func (s *RewardLimitUp) _cycle(cpn *model.FissionCampaign, user *model.FissionUser, effectNum int64, rewards []*model.FissionCampaignReward, rewardLimit int32, limitDo limitDo) (err error) {
key := ckey.FissionUserGetRewardNum(user.ID)
num, err := lockdo.Int64(s.Ctx, key, func() (num int64, err error) {
m := s.GetDao().FissionRewardRecord.SubTable(cpn.ID)
return m.WithContext(s.Ctx).Where(
m.UserID.Eq(user.ID),
m.RewardType.Eq(mtype.FissionCampaignRewardTypeApp),
).Count()
})
if err != nil {
err = s.BE().CacheError(err)
return
}
reward := rewards[0]
var needInvite = int64(reward.InviteNum) //完成任务需要邀请的人数
var decr = int64(reward.RewardNum) //奖品数据
for {
cn := effectNum - (num * needInvite) //总邀请人数 -(完成任务数 * 完成任务需要邀请的人数)= 计算完成任务的邀请人数
//邀请的人不足以获取到奖品
if cn < needInvite {
return limitDo(mtype.FissionReferralLimitStatusNone, -1, 0)
}
if (num * needInvite) >= int64(rewardLimit) {
return limitDo(mtype.FissionReferralLimitStatusInvite, -1, 0)
}
//扣除总预算是否成功
var stock *stocks.Campaign
stock, err = stocks.NewCampaign(s.Ctx, cpn.ID)
if err != nil {
return
}
decrOk, _ := stock.DecrPoolAndAdd(cpn.ID, decr)
if !decrOk {
return limitDo(mtype.FissionReferralLimitStatusBudget, -1, 0)
}
_, _, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), cpn.ProjectID, user).One(reward, true)
if err != nil {
return
}
// 领奖数+1
app.Redis.Cache.Incr(s.Ctx, key)
num++
}
}