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.
1981 lines
46 KiB
Go
1981 lines
46 KiB
Go
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
|
|
}
|