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

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
}