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.

580 lines
13 KiB
Go

package players
import (
"context"
"joybuywork/internal/app"
"joybuywork/internal/consts"
"joybuywork/internal/consts/ckey"
"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/cmm"
"joybuywork/internal/service/provider"
"joybuywork/internal/service/provider/award"
"joybuywork/internal/service/provider/stocks"
"joybuywork/internal/utils"
"math/rand"
"strconv"
"strings"
"time"
"joybuywork/proto/go_proto/workerr"
"github.com/golang-module/carbon/v2"
)
const (
goldSpinEnergy = 100 // 转盘消耗能量
)
const (
goldFailTypeEnergyLack = iota + 1 // 能量不足
goldFailTypeRewardLack // 奖品库存不足
)
// Gold 金币任务交互逻辑
type Gold struct {
Player
player *model.FissionGold
conf *model.FissionCampaignGold
blindLang string
}
func NewGold(ctx context.Context, user *model.FissionUser, opts ...Option) (player *Gold, err error) {
player = &Gold{}
err = player.Init(ctx, user, consts.IdGold, opts...)
if err != nil {
return
}
err = player.initPlayer()
if err != nil {
return
}
return
}
// FinishTask 完成任务
func (s *Gold) FinishTask(tasks ...*model.FissionCampaignTask) (err error) {
player, conf := s.GetPlayer(), s.GetConf()
var chance int32
var logs = make([]*model.FissionGoldLog, 0, len(tasks))
var goldNum = player.GoldNum
for _, task := range tasks {
switch task.PropMark {
case mtype.FissionCampaignTaskPropMarkChance:
chance += task.Chance
}
val := mtype.GoldLogVal{
TaskId: task.ID,
Chance: task.Chance,
}
log := withdefault.FissionGoldLog(&model.FissionGoldLog{
UserID: player.UserID,
CampaignID: player.CampaignID,
GoldID: player.ID,
Type: mtype.FissionGoldLogTypeDoTask,
})
switch task.TaskType {
case mtype.FissionCampaignTaskTypeRegister:
log.Source = mtype.FissionLogSourceRegister
case mtype.FissionCampaignTaskTypeInstall:
log.Source = mtype.FissionLogSourceInstall
case mtype.FissionCampaignTaskTypeCondAll:
log.Source = mtype.FissionLogSourceCondAll
case mtype.FissionCampaignTaskTypeCondOne:
log.Source = mtype.FissionLogSourceCondOne
val.CondId = utils.FissionCampaignTaskMarkCondOneCondId(task.TaskMark)
}
goldNum += task.Chance
log.Val, _ = jsonm.MarshalToString(val)
logs = append(logs, log)
}
provider.NewFissionClue(s.Ctx, player.UserID).AddGold(player.ID, int64(player.GoldNum), int64(chance))
m := s.GetDao().FissionGold
if conf.GoldNum > goldNum {
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.GoldNum.Add(chance))
if err != nil {
return
}
return s._addLog(logs...)
}
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.GoldNum.Add(chance), m.NextStep.Value(mtype.FissionGoldNextStepGoldRain))
if err != nil {
return
}
return s._addLog(logs...)
}
// Clue 金币获得提醒
func (s *Gold) Clue() (old, add int32) {
player := s.GetPlayer()
clue := provider.NewFissionClue(s.Ctx, player.UserID).GetGold()
if clue.GoldId != player.ID {
return
}
old = int32(clue.OldGold)
add = int32(clue.Add)
return
}
// Spin 转转盘
func (s *Gold) Spin() (id int64, failType int32, err error) {
cpn, user := s.cpn, s.user
player, conf := s.GetPlayer(), s.GetConf()
if player.Energy < goldSpinEnergy {
failType = goldFailTypeEnergyLack
return
}
var logs []*model.FissionGoldLog
defer func() {
if err != nil || len(logs) == 0 {
return
}
err = s._addLog(logs...)
}()
if player.LotteryNum == 0 {
id = 4
m := s.GetDao().FissionGold
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
m.Energy.Sub(goldSpinEnergy),
m.LotteryNum.Add(1),
m.ShowPrice.Add(0.99),
m.NextStep.Value(mtype.FissionGoldNextStepLastSpin),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
val, _ := jsonm.MarshalToString(&mtype.GoldLogVal{
Cash: 0.99,
})
logs = append(logs, withdefault.FissionGoldLog(&model.FissionGoldLog{
CampaignID: cpn.ID,
UserID: user.ID,
GoldID: player.ID,
Type: mtype.FissionGoldLogTypeLottery,
Source: mtype.FissionGoldLogSourceLottery,
Val: val,
}))
return
}
// 有库存限制
if conf.PriceNum > 0 {
if conf.Lack == mtype.Yes {
failType = goldFailTypeRewardLack
return
}
var stock *stocks.Campaign
stock, err = stocks.NewCampaign(s.Ctx, cpn.ID)
if err != nil {
return
}
var ok bool
ok, err = stock.DecrPoolAndAdd(cpn.ID, 1)
if err != nil {
return
}
if !ok {
if conf.Lack == mtype.No {
confM := s.GetDao().FissionCampaignGold
_, err = confM.WithContext(s.Ctx).Where(confM.ID.Eq(conf.ID)).UpdateSimple(confM.Lack.Value(mtype.Yes))
if err != nil {
err = s.BE().MysqlError(err)
return
}
_ = cache.NewCampaignGold(s.Ctx).Clear(conf)
}
failType = goldFailTypeRewardLack
return
}
}
cm := cmm.NewCMM(s.Ctx, s.GetDao())
var rewards []*model.FissionCampaignReward
rewards, err = cm.UserAppReward(s.user)
if err != nil {
return
}
var addRewards = make([]*award.RewardOne, 0, len(rewards))
var ids []int64
for _, reward := range rewards {
addRewards = append(addRewards, &award.RewardOne{Reward: reward})
}
rewardsRes, _, err := award.NewAwardReward(s.Ctx, s.GetDao(), cpn.ProjectID, user).Batch(addRewards, true)
if err != nil {
return
}
for _, reward := range rewardsRes {
ids = append(ids, reward.Record.ID)
}
m := s.GetDao().FissionGold
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
m.Energy.Sub(goldSpinEnergy),
m.LotteryNum.Add(1),
m.ShowPrice.Add(0.01),
m.NextStep.Value(mtype.FissionGoldNextStepShowReward),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
val, _ := jsonm.MarshalToString(&mtype.GoldLogVal{
Cash: 0.01,
})
rewardVal, _ := jsonm.MarshalToString(&mtype.GoldLogVal{
RecordIds: ids,
})
logs = append(logs, withdefault.FissionGoldLog(&model.FissionGoldLog{
CampaignID: cpn.ID,
UserID: user.ID,
GoldID: player.ID,
Type: mtype.FissionGoldLogTypeLottery,
Source: mtype.FissionGoldLogSourceLottery,
Val: val,
}), withdefault.FissionGoldLog(&model.FissionGoldLog{
CampaignID: cpn.ID,
UserID: user.ID,
GoldID: player.ID,
Type: mtype.FissionGoldLogTypeReward,
Source: mtype.FissionGoldLogSourceReward,
Val: rewardVal,
}))
id = 1
return
}
// GoldRain 弹窗还差最后一次抽奖获得奖品动画
func (s *Gold) GoldRain() (err error) {
player := s.player
if player.NextStep != mtype.FissionGoldNextStepGoldRain {
return
}
m := s.GetDao().FissionGold
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
m.NextStep.Value(mtype.FissionGoldNextStepNone),
m.Energy.Add(1),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
// LastSpin 弹窗还差最后一次抽奖获得奖品动画
func (s *Gold) LastSpin() (err error) {
player := s.player
if player.NextStep != mtype.FissionGoldNextStepLastSpin {
return
}
m := s.GetDao().FissionGold
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
m.NextStep.Value(mtype.FissionGoldNextStepNone),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
// ShowReward 播放展示奖品动画
func (s *Gold) ShowReward() (err error) {
player := s.player
if player.NextStep != mtype.FissionGoldNextStepShowReward {
return
}
m := s.GetDao().FissionGold
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
m.NextStep.Value(mtype.FissionGoldNextStepNone),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
// TriggerShare 赠送动画
func (s *Gold) TriggerShare() (err error) {
player := s.GetPlayer()
if player.NextStep != mtype.FissionGoldNextStepTriggerShare {
return
}
m := s.GetDao().FissionGold
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
m.ShowPrice.Add(1),
m.Energy.Add(100),
m.NextStep.Value(mtype.FissionGoldNextStepNone),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
// OpenGift 播放开盒子动画
func (s *Gold) OpenGift() (openBox, leftBox, rightBox float64, err error) {
player, cpn, conf := s.GetPlayer(), s.cpn, s.GetConf()
if player.NextStep != mtype.FissionGoldNextStepOpenGift {
return
}
spl := cpn.GetLangMarks()
var price = conf.Price
if player.Lang != spl[0] {
var lang *model.FissionCampaignLang
lang, err = cache.NewCampaignLang(s.Ctx).FindByCampaignId(cpn.ID, player.Lang)
if err != nil {
err = s.BE().CacheError(err)
return
}
if lang == nil {
err = workerr.NotFound("not found cash conf")
return
}
if c := lang.GetGold(); c != nil {
price = c.Price
}
}
openBox = price - 2
if strings.Contains(strconv.FormatFloat(price, 'f', -1, 64), ".") {
leftBox = openBox - float64(rand.Int31n(5)+15) - float64(rand.Int31n(50)+15)/100
rightBox = openBox - float64(rand.Int31n(5)+10) - float64(rand.Int31n(30)+10)/100
} else {
leftBox = openBox - float64(rand.Int31n(5)+15)
rightBox = openBox - float64(rand.Int31n(5)+10)
}
m := s.GetDao().FissionGold
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
m.ShowPrice.Value(openBox),
m.NextStep.Value(mtype.FissionGoldNextStepTriggerShare),
m.Energy.Value(99),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
val, _ := jsonm.MarshalToString(&mtype.GoldLogVal{
Cash: openBox,
})
err = s._addLog(withdefault.FissionGoldLog(&model.FissionGoldLog{
CampaignID: player.CampaignID,
UserID: player.UserID,
GoldID: player.ID,
Type: mtype.FissionGoldLogTypeOpenGift,
Source: mtype.FissionGoldLogSourceOpenGift,
Val: val,
}))
return
}
// RefreshView 刷新预览用户
func (s *Gold) RefreshView() (err error) {
if !s.IsView() {
err = s.BE().VerifyError("非预览用户无法重新体验")
return
}
player := s.GetPlayer()
m := s.GetDao().FissionGoldLog
_, err = m.WithContext(s.Ctx).Where(m.GoldID.Eq(player.ID)).Delete()
if err != nil {
err = s.BE().MysqlError(err)
return
}
playerM := s.GetDao().FissionGold
_, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).Delete()
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
// GetConf 获取玩法配置
func (s *Gold) GetConf() *model.FissionCampaignGold {
if s.conf == nil {
s.conf, _ = cache.NewCampaignGold(s.Ctx).FindByCampaignId(s.user.CampaignID)
}
return s.conf
}
// GetPlayer 获取玩家基础数据
func (s *Gold) GetPlayer() *model.FissionGold {
return s.player
}
// 玩家
func (s *Gold) initPlayer() (err error) {
user, sp := s.user, s.cpn.GetLangMarks()
lang := utils.NotExistGetFirst(user.LangMark, sp)
s.blindLang = lang
player, err := s._dbFirst(lang)
if err != nil {
return
}
defer func() {
if err != nil {
return
}
s.player = player
}()
if player != nil {
// 没有过期,或者已经完成了任务
if player.ExpiredIn > time.Now().Unix() || player.LotteryNum >= int8(len(consts.SpinGetId)) {
return
}
// 过期了
playerM := s.GetDao().FissionGold
_, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).UpdateSimple(playerM.IsExpired.Value(mtype.Yes))
if err != nil {
err = s.BE().MysqlError(err)
return
}
}
player, err = s.firstOrCreate(lang)
if err != nil {
return
}
return
}
// 创建玩家基础数据
func (s *Gold) firstOrCreate(lang string) (player *model.FissionGold, err error) {
lc := app.NewFollower(s.Ctx, ckey.FissionUserCreatePlayerOnce(s.user.ID)).Lock()
defer lc.Release()
if lc.Error() != nil {
err = lc.Error()
return
}
player, err = s._dbFirst(lang)
if err != nil {
return
}
if player != nil {
return
}
return s.createPlayer(lang)
}
func (s *Gold) createPlayer(lang string) (player *model.FissionGold, err error) {
conf := s.GetConf()
playerM := dao.FissionGold
player = withdefault.FissionGold(&model.FissionGold{
CampaignID: s.user.CampaignID,
UserID: s.user.ID,
LotteryNum: 0,
NextStep: mtype.FissionGoldNextStepOpenGift,
Energy: 0,
ShowPrice: 0,
ExpiredIn: carbon.Now().AddDays(int(conf.ExpiredDay)).Timestamp(),
IsExpired: 0,
Lang: lang,
})
err = playerM.WithContext(s.Ctx).Create(player)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
func (s *Gold) _dbFirst(lang string) (*model.FissionGold, error) {
playerM := dao.FissionGold
player, err := playerM.WithContext(s.Ctx).Where(playerM.UserID.Eq(s.user.ID), playerM.Lang.Value(lang), playerM.IsExpired.Eq(mtype.No)).Last()
if err = dbcheck.DbError(err); err != nil {
return nil, s.BE().MysqlError(err)
}
return player, nil
}
func (s *Gold) _addLog(logs ...*model.FissionGoldLog) (err error) {
err = s.GetDao().FissionGoldLog.WithContext(s.Ctx).Create(logs...)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}