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.

1470 lines
33 KiB
Go

This file contains ambiguous Unicode characters!

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

package 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/finder"
"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/fissionaction"
"joybuywork/internal/utils"
"joybuywork/internal/utils/lockdo"
"joybuywork/internal/utils/verify"
"joybuywork/proto/go_proto/workerr"
"fmt"
"time"
"gits.branchcn.com/backend/microcode/plugins/lucky"
"gorm.io/gen/field"
)
type rollData struct {
MarkType int8 // 格子类型
Step int32 // 下标数据
RewardId int64 // 活动奖品id
MapId int64 // 地图int类型id
MapHashId string // 地图hash_id
RewardTitle string // 奖品名称
ImgUrl string // 奖品地址
Intact bool // 合成了奖品
Lack bool // 缺库存
}
const (
diceFailTypeNone = iota // 没有错误
diceFailTypeLack // 缺骰子数
diceFailTypeProps // 道具不足
)
// Dice 金币池交互逻辑
type Dice struct {
Player
player *model.FissionDice
conf *model.FissionCampaignDice
run *model.FissionDiceRun
fissionPlayerSetInitOnceKey string
diceOnceKey string
}
func NewDice(ctx context.Context, user *model.FissionUser, opts ...Option) (player *Dice, err error) {
player = &Dice{}
err = player.Init(ctx, user, consts.IdDice, opts...)
if err != nil {
return
}
err = player.initPlayer()
if err != nil {
return
}
return
}
func (s *Dice) PointsUse() (status int32, err error) {
player := s.GetPlayer()
logM := s.GetDao().FissionDiceRecord
status, deduct, err := s.pointsUse(mtype.FissionPointsLogSourceDice, logM.TableName(), mtype.PointsLogOther{
PlayerId: player.ID,
}, []*pointsUseCond{
{
Field: logM.Mark.ColumnName().String(),
Value: mtype.FissionDiceRecordMarkPointsUse,
},
})
defer s.pointsUseOk(deduct)
if status != pointsUseOk || err != nil {
return
}
m := s.GetDao().FissionDice
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Chance.Add(1))
if err != nil {
return
}
log := withdefault.FissionDiceRecord(&model.FissionDiceRecord{
CampaignID: player.CampaignID,
UserID: player.UserID,
Mark: mtype.FissionDiceRecordMarkPointsUse,
Data: "",
})
log.SetData(&mtype.DiceRecord{
Chance: 1,
PointsNum: deduct.Num,
})
err = s._addLog(log)
if err != nil {
return
}
return
}
// ReceiveReward 手动领取奖励
func (s *Dice) ReceiveReward(taskRewards ...*model.FissionUserTaskReward) (chance, fixChance int32, reward *award.RewardRes, err error) {
err = s._receiveReward(taskRewards, func(tasks []*model.FissionCampaignTask, records []*award.RewardRes) error {
chance, fixChance, 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 *Dice) FinishTask(tasks ...*model.FissionCampaignTask) (err error) {
_, _, err = s.taskFinish(tasks, true)
if err != nil {
return
}
return
}
// 完成任务
func (s *Dice) taskFinish(tasks []*model.FissionCampaignTask, checkManual bool) (chance, fixChance int32, err error) {
player := s.GetPlayer()
var logs []*model.FissionDiceRecord
for _, task := range tasks {
if checkManual && task.AwardMode == mtype.FissionCampaignTaskAwardModeManual {
continue
}
log := withdefault.FissionDiceRecord(&model.FissionDiceRecord{
CampaignID: player.CampaignID,
UserID: player.UserID,
})
val := &mtype.DiceRecord{
TaskId: task.ID,
Chance: task.Chance,
}
switch task.TaskType {
case mtype.FissionCampaignTaskTypeRegister:
log.Mark = mtype.FissionDiceRecordMarkGetRollRegister
case mtype.FissionCampaignTaskTypeInstall:
log.Mark = mtype.FissionDiceRecordMarkGetRollInstall
case mtype.FissionCampaignTaskTypeCondAll:
log.Mark = utils.Ternary[string](task.AwardMode == mtype.FissionCampaignTaskAwardModeManual, mtype.FissionDiceRecordMarkGetManualRollAll, mtype.FissionDiceRecordMarkGetRollAll)
case mtype.FissionCampaignTaskTypeCondOne:
log.Mark = utils.Ternary[string](task.AwardMode == mtype.FissionCampaignTaskAwardModeManual, mtype.FissionDiceRecordMarkGetManualRollTask, mtype.FissionDiceRecordMarkGetRollTask)
val.CondId = utils.FissionCampaignTaskMarkCondOneCondId(task.TaskMark)
}
log.SetData(val)
switch task.PropMark {
case mtype.FissionCampaignTaskPropMarkChance:
chance += task.Chance
case mtype.FissionCampaignTaskPropMarkFixedRoll:
log.Mark = mtype.FissionDiceRecordMarkGetLucky
fixChance += task.Chance
}
logs = append(logs, log)
}
if chance > 0 {
m := s.GetDao().FissionDice
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.TaskChance.Add(chance), m.Chance.Add(chance))
if err != nil {
err = s.BE().MysqlError(err)
return
}
}
if fixChance > 0 {
var prop *model.FissionDiceProp
prop, err = s._propOrCreate(mtype.FissionDicePropMarkFixedRoll)
if err != nil {
return
}
m := s.GetDao().FissionDiceProp
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(prop.ID)).UpdateSimple(m.Num.Add(fixChance), m.TotalNum.Add(fixChance))
if err != nil {
err = s.BE().MysqlError(err)
return
}
s.appTrack().GetProp(time.Now().Unix(), consts.PropNameFixedDice, fixChance)
}
err = s._addLog(logs...)
if err != nil {
return
}
return
}
type RollRes struct {
FailType int32
MarkType int32
MapHashId string
RewardTitle string
ImgUrl string
IsIntact bool
RollNum int32
Lack bool
RewardId int64
}
// Roll 普通掷骰子
func (s *Dice) Roll() (res RollRes, err error) {
s.Tran()
defer func() {
s.TranCommit(err)
s.AfterDo(err)
}()
player := s.GetPlayer()
if player.Chance <= 0 {
res.FailType = diceFailTypeLack
return
}
step := int(utils.RandNumber(6))
win, err := s._rollMap(step, false)
if err != nil || win == nil {
return
}
// 不是预览用户要减次数,并且不是再来一次就减一次
if !s.IsView() && win.MarkType != mtype.FissionDiceMapMarkTypeChance {
m := s.GetDao().FissionDice
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.Chance.Sub(1))
if err != nil {
err = s.BE().MysqlError(err)
return
}
}
// 记录日志
err = s._rollDiceRecord(win, false)
if err != nil {
return
}
return s._rollRes(win)
}
// FixRoll 使用幸运骰子
func (s *Dice) FixRoll(rollVal string) (res RollRes, err error) {
s.Tran()
defer func() {
s.TranCommit(err)
s.AfterDo(err)
}()
player := s.GetPlayer()
m := s.GetDao().FissionDiceProp
prop, err := m.WithContext(s.Ctx).Where(m.UserID.Eq(player.UserID), m.Mark.Eq(mtype.FissionDicePropMarkFixedRoll)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if prop == nil || prop.Num <= 0 {
res.FailType = diceFailTypeProps
return
}
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(prop.ID)).UpdateSimple(m.Num.Sub(1))
if err != nil {
err = s.BE().MysqlError(err)
return
}
step := utils.StrToInt(rollVal) - 1
win, err := s._rollMap(step, true)
if err != nil || win == nil {
return
}
// 记录日志
err = s._rollDiceRecord(win, true)
if err != nil {
return
}
// 使用道具埋点
fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).UseProp(time.Now().Unix(), consts.PropNameFixedDice)
return s._rollRes(win)
}
type DiceMapOne struct {
MapHashId string `json:"map_hash_id"`
MarkType int32 `json:"mark_type"`
RewardTitle string `json:"reward_title"`
ImgUrl string `json:"img_url"`
RewardId int64 `json:"reward_id"`
}
func (s *Dice) DiceMap(mapHashId string, showStep int64, prefixStep int32) (maps []*DiceMapOne, err error) {
player := s.GetPlayer()
run, err := s._runMap()
if err != nil {
return
}
m := s.GetDao().FissionDiceMap
list, err := m.WithContext(s.Ctx).Where(m.CampaignID.Eq(player.CampaignID), m.DmgID.Eq(run.DmgID)).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
if len(list) == 0 {
return
}
maps = make([]*DiceMapOne, 0, 54)
var point = run.MapID
if mapHashId != "" {
var dm *model.FissionDiceMap
dm, err = m.WithContext(s.Ctx).Where(m.HashID.Eq(mapHashId)).Take()
if err != nil {
err = s.BE().MysqlError(err)
return
}
if dm != nil {
point = dm.ID
}
}
rewardMap, err := cmm.NewCMM(s.Ctx, s.GetDao()).UserAppRewardInfo(s.cpn, s.user)
if err != nil {
return
}
var add = func(i int) {
v := list[i]
one := &DiceMapOne{
MapHashId: v.HashID,
MarkType: int32(v.MarkType),
RewardTitle: "",
ImgUrl: "",
}
if reward, ok := rewardMap[v.RewardID]; ok {
one.RewardTitle = reward.Title
one.ImgUrl = reward.ImgUrl
one.RewardId = reward.RewardId
}
maps = append(maps, one)
}
if point == 0 || (showStep > 0 && run.GoStep <= showStep) {
maps = append(maps, &DiceMapOne{
MapHashId: "",
MarkType: -1,
RewardTitle: "GO",
ImgUrl: "",
})
for i := 0; i < 53; i++ {
add(i)
}
return
}
// 在第几格
var idx int
for i, v := range list {
if v.ID == point {
idx = i
break
}
}
// 走了超过12步
var mapL = len(list)
var start = idx - utils.Ternary(mapHashId == "", int(prefixStep), -1)
start = utils.Ternary(start < 0, 0, start)
var end = 54 + start
if end > mapL {
end = mapL
}
for i := start; i < end; i++ {
add(i)
}
// 如果超过了地图最大长度,从头再来
var resLen = len(maps)
for i := 0; i < 54-resLen; i++ {
add(i)
}
return
}
// SetInit 获得初始次数
func (s *Dice) SetInit() (err error) {
player, conf := s.GetPlayer(), s.GetConf()
if player.SetInit == mtype.Yes || conf.InitChance == 0 {
return
}
key := s.fissionPlayerSetInitOnceKey
if !app.NewRedisLock(s.Ctx, key).SoftLock() {
return
}
m := s.GetDao().FissionDice
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.SetInit.Value(mtype.Yes), m.Chance.Add(conf.InitChance))
if err != nil {
err = s.BE().MysqlError(err)
return
}
val, _ := jsonm.MarshalToString(mtype.DiceRecord{
Chance: conf.InitChance,
})
log := withdefault.FissionDiceRecord(&model.FissionDiceRecord{
UserID: player.UserID,
CampaignID: player.CampaignID,
Mark: mtype.FissionDiceRecordMarkGetRollSys,
Data: val,
})
err = s._addLog(log)
if err != nil {
return
}
s.player.Chance += conf.InitChance
return
}
type MixRewardRes struct {
Lack bool //奖品无库存
RewardId int64 //奖品ID
RewardTitle string //奖品名称
ImgUrl string //奖品图片
ReceiveType int32 //发放类型(0-无操作,1-系统发放,2-联系客服,3-客户端推送)
KvData []*mtype.RewardKV //键值数据
Contact string //联系方式
RewardHashId string //领取的用户奖品hash_id
OpenToUse int32 //开启去使用(0-否,1-是)
WebUrl string //Web端的跳转地址
AppParam string //App端的跳转参数
PcParam string //pc端的跳转参数
}
func (s *Dice) MixReward(rewardId int64) (res MixRewardRes, err error) {
s.Tran()
defer func() {
s.TranCommit(err)
}()
user := s.user
m := s.GetDao().FissionDiceCollect
collect, err := m.WithContext(s.Ctx).Where(m.UserID.Eq(user.ID), m.RewardID.Eq(rewardId)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if collect == nil || collect.RecordID == 0 {
return
}
rewardM := s.GetDao().FissionCampaignReward
reward, err := rewardM.WithContext(s.Ctx).Where(rewardM.ID.Eq(collect.RewardID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
var upData = []field.AssignExpr{
m.IntactNum.Add(1),
m.Num.Sub(reward.CollectNum),
m.RecordID.Value(0),
}
cr := reward.GetCollectRule()
if cr.Open == mtype.Yes && cr.ResetType == mtype.FissionCampaignRewardCollectRuleResetGet {
upData = append(upData, m.SignNum.Value(0))
}
// 完整度+1
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(collect.ID)).UpdateSimple(upData...)
if err != nil {
err = s.BE().MysqlError(err)
return
}
recordM := s.GetDao().FissionRewardRecord.SubTable(user.CampaignID)
record, err := recordM.WithContext(s.Ctx).Where(recordM.ID.Eq(collect.RecordID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
var lack bool
if record.FailType == mtype.FissionRewardRecordFailTypeCli && record.FailMsg == mtype.FissionRewardRecordFailTypeCliLackMsg {
lack = true
}
// 骰子记录
var diceRecord = withdefault.FissionDiceRecord(&model.FissionDiceRecord{
CreatedAt: record.CreatedAt,
CampaignID: user.CampaignID,
UserID: user.ID,
Mark: mtype.FissionDiceRecordMarkReward,
Data: "",
})
diceRecord.SetData(&mtype.DiceRecord{
RewardId: record.RewardID,
RecordId: record.ID,
})
err = s._addLog(diceRecord)
if err != nil {
return
}
preward, err := provider.NewRewards(s.Ctx, s.cpn.ProjectID, record.BaseRewardID)
if err != nil {
return
}
info, _ := preward.InfoAutoLang(record.BaseRewardID, user.LangMark)
res = MixRewardRes{
Lack: lack,
RewardId: record.RewardID,
RewardTitle: info.Title,
ImgUrl: info.ImgUrl,
ReceiveType: info.ReceiveType,
KvData: info.KvData,
Contact: info.Contact,
RewardHashId: record.HashID,
OpenToUse: info.OpenToUse,
WebUrl: info.WebUrl,
AppParam: info.AppParam,
PcParam: info.PcParam,
}
return
}
type DiceStatus struct {
Stat *model.FissionUserStat
Run *model.FissionDiceRun
CollectMap map[int64]int32
PropMap map[string]int32
FixedRollTaskFinish int64
}
func (s *Dice) Status() (status DiceStatus, err error) {
player, conf := s.GetPlayer(), s.GetConf()
// 统计数据
status.Stat, err = cmm.NewFirstCreate(s.Ctx, s.GetDao()).FissionUserStat(s.user)
if err != nil {
return
}
// 用户当前所在地图的信息
status.Run, err = s._runMap()
if err != nil {
return
}
collectM := s.GetDao().FissionDiceCollect
collects, err := collectM.WithContext(s.Ctx).Where(collectM.UserID.Eq(player.UserID)).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
status.CollectMap = make(map[int64]int32)
for _, v := range collects {
status.CollectMap[v.RewardID] = v.Num
}
propM := s.GetDao().FissionDiceProp
props, err := propM.WithContext(s.Ctx).Where(propM.UserID.Eq(player.UserID)).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
status.PropMap = make(map[string]int32)
for _, prop := range props {
status.PropMap[prop.Mark] = prop.Num
}
if conf.FixDice == mtype.No {
return
}
taskM := s.GetDao().FissionCampaignTask
task, err := taskM.WithContext(s.Ctx).Where(
taskM.CampaignID.Eq(player.CampaignID),
taskM.PropMark.Eq(mtype.FissionCampaignTaskPropMarkFixedRoll),
taskM.IsDel.Eq(mtype.No),
).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if task == nil {
return
}
taskRecordM := s.GetDao().FissionUserTaskRecord
status.FixedRollTaskFinish, err = taskRecordM.WithContext(s.Ctx).Where(taskRecordM.UserID.Eq(player.UserID), taskRecordM.TaskID.Eq(task.ID)).Count()
if err != nil {
err = s.BE().MysqlError(err)
return
}
if task.ChanceLimit > 0 && int64(task.ChanceLimit) < status.FixedRollTaskFinish {
status.FixedRollTaskFinish = int64(task.ChanceLimit)
}
return
}
// Sign 签到
func (s *Dice) Sign() (err error) {
player, conf := s.GetPlayer(), s.GetConf()
signOk, lastSign := s._lastSign(player.LastSign)
if !signOk {
return
}
playerM := s.GetDao().FissionDice
var upData []field.AssignExpr
upData = append(upData, playerM.LastSign.Value(lastSign))
if conf.DayChance > 0 {
upData = append(upData,
playerM.Chance.Add(conf.DayChance),
playerM.DayChance.Add(conf.DayChance),
)
val, _ := jsonm.MarshalToString(mtype.DiceRecord{
Chance: conf.DayChance,
})
log := withdefault.FissionDiceRecord(&model.FissionDiceRecord{
UserID: player.UserID,
CampaignID: player.CampaignID,
Mark: mtype.FissionDiceRecordMarkSign,
Data: val,
})
err = s._addLog(log)
if err != nil {
return
}
s.player.Chance = player.Chance + conf.DayChance
}
scrIds, err := s._signCollectReward()
if err != nil {
return
}
if len(scrIds) > 0 {
var addSignIds []int64
for _, id := range scrIds {
var collect *model.FissionDiceCollect
collect, err = s._rewardCollect(id)
if err != nil {
return
}
addSignIds = append(addSignIds, collect.ID)
}
m := s.GetDao().FissionDiceCollect
_, err = m.WithContext(s.Ctx).Where(m.ID.In(addSignIds...)).UpdateSimple(m.SignNum.Add(1))
if err != nil {
err = s.BE().MysqlError(err)
return
}
}
_, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).UpdateSimple(upData...)
if err != nil {
return s.BE().MysqlError(err)
}
return
}
// GetConf 获取玩法配置
func (s *Dice) GetConf() *model.FissionCampaignDice {
if s.conf == nil {
s.conf, _ = cache.NewCampaignDice(s.Ctx).FindByCampaignId(s.user.CampaignID)
}
return s.conf
}
// GetPlayer 获取玩家基础数据
func (s *Dice) GetPlayer() *model.FissionDice {
return s.player
}
// 玩家
func (s *Dice) initPlayer() (err error) {
player, err := s._dbFirst()
if err != nil {
return
}
defer func() {
if err != nil {
return
}
s.player = player
s.fissionPlayerSetInitOnceKey = ckey.FissionPlayerSetInitOnce(player.UserID)
s.diceOnceKey = ckey.FissionDiceOnce(player.UserID)
}()
if player != nil {
return
}
player, err = s.firstOrCreate()
if err != nil {
return
}
return
}
// 创建玩家基础数据
func (s *Dice) firstOrCreate() (player *model.FissionDice, 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()
if err != nil {
return
}
if player != nil {
return
}
return s.createPlayer()
}
// 数据库查询
func (s *Dice) _dbFirst() (*model.FissionDice, error) {
playerM := dao.FissionDice
player, err := playerM.WithContext(s.Ctx).Where(playerM.UserID.Eq(s.user.ID)).Take()
if err = dbcheck.DbError(err); err != nil {
return nil, s.BE().MysqlError(err)
}
return player, nil
}
// 创建一个玩家
func (s *Dice) createPlayer() (player *model.FissionDice, err error) {
playerM := dao.FissionDice
player = withdefault.FissionDice(&model.FissionDice{
CampaignID: s.user.CampaignID,
UserID: s.user.ID,
})
err = playerM.WithContext(s.Ctx).Create(player)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
func (s *Dice) _addLog(logs ...*model.FissionDiceRecord) (err error) {
err = s.GetDao().FissionDiceRecord.WithContext(s.Ctx).Create(logs...)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
}
func (s *Dice) _signCollectReward() (ids []int64, err error) {
rewards, err := cmm.NewUserAppReward(s.Ctx).Get(s.GetDao(), s.user)
if err != nil {
return
}
for _, reward := range rewards {
cr := reward.GetCollectRule()
if cr.Open == mtype.Yes {
ids = append(ids, reward.ID)
}
}
return
}
func (s *Dice) _rewardCollect(rewardId int64) (collect *model.FissionDiceCollect, err error) {
player := s.GetPlayer()
err = lockdo.FindNilDo(
s.Ctx,
ckey.FissionUserCreateRewardCollect(player.UserID, rewardId),
func() (none bool, err error) {
m := s.GetDao().FissionDiceCollect
collect, err = m.WithContext(s.Ctx).Where(m.UserID.Eq(player.UserID), m.RewardID.Eq(rewardId)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
return collect == nil, nil
},
func() (err error) {
collect = withdefault.FissionDiceCollect(&model.FissionDiceCollect{
UserID: player.UserID,
CampaignID: player.CampaignID,
RewardID: rewardId,
})
err = s.GetDao().FissionDiceCollect.WithContext(s.Ctx).Create(collect)
if err != nil {
err = s.BE().MysqlError(err)
return
}
return
})
if err != nil {
return
}
return
}
func (s *Dice) _runMap() (run *model.FissionDiceRun, err error) {
if s.run != nil {
return s.run, nil
}
user := s.user
m := s.GetDao().FissionDiceMapGroup
dmg, err := m.WithContext(s.Ctx).Where(
m.CampaignID.Eq(user.CampaignID),
m.LangMark.Eq(user.UserLang),
m.LevelID.Eq(user.UserLevel),
m.CountryMark.Eq(user.UserCountry),
).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if dmg == nil {
err = workerr.NotFound("not found dice map")
return
}
lc := app.NewFollower(s.Ctx, ckey.FissionUserRunCreateOnce(user.ID)).Lock()
defer lc.Release()
if lc.Error() != nil {
err = workerr.BusyError
return
}
runM := s.GetDao().FissionDiceRun
run, err = runM.WithContext(s.Ctx).Where(runM.UserID.Eq(user.ID), runM.DmgID.Eq(dmg.ID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if run == nil {
run = withdefault.FissionDiceRun(&model.FissionDiceRun{
CampaignID: user.CampaignID,
UserID: user.ID,
DmgID: dmg.ID,
})
if err = runM.WithContext(s.Ctx).Create(run); err != nil {
err = s.BE().MysqlError(err)
return
}
}
s.run = run
return
}
func (s *Dice) _rollMap(idx int, isFixRoll bool) (res *rollData, err error) {
run, err := s._runMap()
if err != nil {
return
}
m := s.GetDao().FissionDiceMap
list, err := m.WithContext(s.Ctx).Where(m.CampaignID.Eq(run.CampaignID), m.DmgID.Eq(run.DmgID), m.ID.Gt(run.MapID)).Limit(6).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
if len(list) < 6 {
back := 6 - len(list)
var backList []*model.FissionDiceMap
backList, err = m.WithContext(s.Ctx).Where(m.CampaignID.Eq(run.CampaignID), m.DmgID.Eq(run.DmgID)).Limit(back).Order(m.ID).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
for _, v := range backList {
list = append(list, v)
}
}
if len(list) == 0 {
app.Logger.Error(s.Ctx, fmt.Sprintf("活动[%d]地图生成异常, 用户id[%d]", run.CampaignID, run.UserID))
err = workerr.ServerError
return
}
var loseMap = make(map[int]*model.FissionDiceMap)
for i, v := range list {
if verify.InArray(v.MarkType, mtype.FissionDiceMapMarkTypeNone, mtype.FissionDiceMapMarkTypeChance, mtype.FissionDiceMapMarkTypeFixedRoll) {
loseMap[i] = v
}
}
var win, lose *rollData
for i, v := range loseMap {
switch v.MarkType {
case mtype.FissionDiceMapMarkTypeNone, mtype.FissionDiceMapMarkTypeChance:
lose = &rollData{
MarkType: v.MarkType,
Step: int32(i),
RewardId: v.RewardID,
MapId: v.ID,
MapHashId: v.HashID,
}
break
}
}
win = &rollData{
MarkType: list[idx].MarkType,
Step: int32(idx),
RewardId: list[idx].RewardID,
MapId: list[idx].ID,
MapHashId: list[idx].HashID,
}
// 预览用户直接返回结果
if s.IsView() {
return s._viewWin(win)
}
// 不是固定骰子,要看看走不走概率
if !isFixRoll {
confM := s.GetDao().FissionCampaignDice
var conf *model.FissionCampaignDice
conf, err = confM.WithContext(s.Ctx).Where(confM.CampaignID.Eq(run.CampaignID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
// 走概率发奖
if conf.AwardType == mtype.FissionCampaignDiceAwardTypeOdds {
winner, _ := lucky.NewDiscrete(s.Ctx, []lucky.Reward{
{Share: int(conf.Odds), Mark: true},
{Share: 1000 - int(conf.Odds), Mark: false},
}).Lottery()
// 赢不了
if !winner.Bool() {
return lose, nil
}
return s._odds(list, win, lose, isFixRoll)
}
}
// 如果都没中奖品,那就不用做奖品处理
if !verify.InArray(win.MarkType, mtype.FissionDiceMapMarkTypeNormal, mtype.FissionDiceMapMarkTypeSuper) {
return win, nil
}
res, err = s._reward(win, lose, isFixRoll)
if err != nil {
return
}
return
}
// 预览用户返回的数据
func (s *Dice) _viewWin(rd *rollData) (win *rollData, err error) {
win = rd
if win.RewardId == 0 {
return
}
m := s.GetDao().FissionCampaignReward
reward, err := m.WithContext(s.Ctx).Where(m.ID.Eq(win.RewardId)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
preward, err := provider.NewRewards(s.Ctx, s.cpn.ProjectID, reward.RewardID)
if err != nil {
return
}
info := preward.AutoLang(reward.RewardID, s.user.LangMark)
win.RewardTitle = info.Title
win.ImgUrl = info.ImgUrl
return
}
// 概率获取奖品
func (s *Dice) _odds(diceMaps []*model.FissionDiceMap, win, lose *rollData, isFixRoll bool) (res *rollData, err error) {
var rewardMap = make(map[int64]*model.FissionCampaignReward)
var ids []int64
var mapIdx = make(map[int]*model.FissionDiceMap)
for idx, v := range diceMaps {
if _, ok := rewardMap[v.RewardID]; !ok {
ids = append(ids, v.RewardID)
rewardMap[v.RewardID] = nil
}
mapIdx[idx] = v
}
if len(ids) == 0 {
return win, nil
}
m := s.GetDao().FissionCampaignReward
list, err := m.WithContext(s.Ctx).Where(m.ID.In(ids...)).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
var signIds []int64
var signMap = make(map[int64]mtype.CollectRule)
for _, v := range list {
cr := v.GetCollectRule()
if cr.Open == mtype.Yes && cr.SignDay > 0 {
signIds = append(signIds, v.ID)
signMap[v.ID] = cr
}
}
filterMap, err := s._filterCollect(signIds, signMap)
if err != nil {
return
}
var allOdds int
var prize []lucky.Reward
for _, v := range list {
if _, ok := filterMap[v.ID]; ok {
continue
}
odds := int(v.Odds)
prize = append(prize, lucky.Reward{
Share: odds,
Mark: v.ID,
})
allOdds = allOdds + odds
rewardMap[v.ID] = v
}
if len(prize) == 0 {
return lose, nil
}
reward, err := lucky.NewDiscrete(s.Ctx, prize).Lottery()
if err != nil {
return
}
id := reward.Int64()
// 概率中奖了,就走到那个中奖的格子
for idx, diceMap := range mapIdx {
if diceMap.RewardID == id {
win = &rollData{
MarkType: diceMap.MarkType,
Step: int32(idx),
RewardId: diceMap.RewardID,
MapId: diceMap.ID,
MapHashId: diceMap.HashID,
}
break
}
}
collect, err := s._rewardCollect(id)
if err != nil {
return
}
return s._rewardInDb(rewardMap[id], collect, win, lose, isFixRoll)
}
// 奖品入库
func (s *Dice) _rewardInDb(reward *model.FissionCampaignReward, collect *model.FissionDiceCollect, win, lose *rollData, isFixRoll bool) (res *rollData, err error) {
var intact bool
var lack bool
if reward.CollectNum == collect.Num+1 {
//中奖了
var check bool
check, err = award.NewAwardIssVerify(s.Ctx, s.user, reward).DeductStock().RuleFilter().IsOK()
if err != nil {
return
}
if !check && !isFixRoll {
return lose, nil
}
intact = true
lack = !check
}
cpn, err := finder.Campaign(s.Ctx, reward.CampaignID)
if err != nil {
err = s.BE().CacheError(err)
return
}
preward, err := provider.NewRewards(s.Ctx, cpn.ProjectID, reward.RewardID)
if err != nil {
return
}
info := preward.AutoLang(reward.RewardID, s.user.LangMark)
collectM := s.GetDao().FissionDiceCollect
var up []field.AssignExpr
var record *model.FissionRewardRecord
if intact {
record, err = s._intactReward(reward, lack)
if err != nil {
return
}
if lack { // 没有库存,那就不让客户看动画了
up = append(up, collectM.Num.Value(0), collectM.IntactNum.Add(1))
// 骰子记录
var diceRecord = withdefault.FissionDiceRecord(&model.FissionDiceRecord{
CreatedAt: record.CreatedAt,
CampaignID: record.CampaignID,
UserID: record.UserID,
Mark: mtype.FissionDiceRecordMarkReward,
Data: "",
})
diceRecord.SetData(&mtype.DiceRecord{
RewardId: record.RewardID,
RecordId: record.ID,
})
err = s._addLog(diceRecord)
if err != nil {
return
}
} else { // 有库存,让用户看到合成动画
up = append(up, collectM.Num.Add(1), collectM.RecordID.Value(record.ID))
}
} else { // 没有收集完碎片,碎片数 +1
up = append(up, collectM.Num.Add(1))
}
_, err = collectM.WithContext(s.Ctx).Where(collectM.ID.Eq(collect.ID)).UpdateSimple(up...)
if err != nil {
err = s.BE().MysqlError(err)
return
}
win.RewardTitle = info.Title
win.ImgUrl = info.ImgUrl
win.Intact = intact
win.Lack = lack
return win, nil
}
// 完整的奖品处理
func (s *Dice) _intactReward(reward *model.FissionCampaignReward, lack bool) (record *model.FissionRewardRecord, err error) {
var opts []award.RecordOption
if lack {
opts = append(opts, award.RewardRecordStatus(
mtype.FissionRewardRecordStatusFail,
mtype.FissionRewardRecordFailTypeCli,
mtype.FissionRewardRecordFailTypeCliLackMsg,
))
}
record, _, async, err := award.NewAwardReward(s.Ctx, s.GetDao(), s.cpn.ProjectID, s.user).One(reward, false, opts...)
if lack {
return
}
s.AddOkDone(func() {
for _, fn := range async {
fn()
}
})
return
}
// 加骰子记录
func (s *Dice) _rollDiceRecord(win *rollData, luckyProp bool) (err error) {
player := s.GetPlayer()
record := withdefault.FissionDiceRecord(&model.FissionDiceRecord{
CampaignID: player.CampaignID,
UserID: player.UserID,
})
data := &mtype.DiceRecord{
Point: win.Step + 1,
RewardId: win.RewardId,
}
// 使用幸运骰子道具
if luckyProp {
switch win.MarkType {
case mtype.FissionDiceMapMarkTypeNone:
record.Mark = mtype.FissionDiceRecordMarkLucky
case mtype.FissionDiceMapMarkTypeChance:
record.Mark = mtype.FissionDiceRecordMarkLuckyChance
data.Chance = 1
default:
record.Mark = mtype.FissionDiceRecordMarkLuckyCollect
}
record.SetData(data)
return s._addLog(record)
}
switch win.MarkType {
case mtype.FissionDiceMapMarkTypeNone:
record.Mark = mtype.FissionDiceRecordMarkRoll
case mtype.FissionDiceMapMarkTypeChance:
record.Mark = mtype.FissionDiceRecordMarkRollChance
data.Chance = 1
default:
record.Mark = mtype.FissionDiceRecordMarkRollCollect
}
record.SetData(data)
return s._addLog(record)
}
// 结果处理
func (s *Dice) _rollRes(win *rollData) (res RollRes, err error) {
run, err := s._runMap()
if err != nil {
return
}
// 更新地图信息
runM := s.GetDao().FissionDiceRun
_, err = runM.WithContext(s.Ctx).Where(runM.ID.Eq(run.ID)).UpdateSimple(
runM.MapID.Value(win.MapId),
runM.MapHashID.Value(win.MapHashId),
runM.GoStep.Add(int64(win.Step)+1),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
res.MarkType = int32(win.MarkType)
res.MapHashId = win.MapHashId
res.RewardTitle = win.RewardTitle
res.ImgUrl = win.ImgUrl
res.IsIntact = win.Intact
res.RollNum = win.Step + 1
res.Lack = win.Lack
res.RewardId = win.RewardId
return
}
func (s *Dice) _reward(win, lose *rollData, isFixRoll bool) (res *rollData, err error) {
m := s.GetDao().FissionCampaignReward
reward, err := m.WithContext(s.Ctx).Where(m.ID.Eq(win.RewardId)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if reward == nil || reward.IsDel == mtype.Yes {
return lose, nil
}
collect, err := s._rewardCollect(reward.ID)
if err != nil {
return
}
cr := reward.GetCollectRule()
if cr.Open == mtype.Yes {
collectNum := cr.InitCollect + ((collect.SignNum / cr.SignDay) * cr.AddCollect)
if collect.Num >= collectNum {
return lose, nil
}
}
return s._rewardInDb(reward, collect, win, lose, isFixRoll)
}
// 用户道具
func (s *Dice) _propOrCreate(mark string) (prop *model.FissionDiceProp, err error) {
user := s.user
m := s.GetDao().FissionDiceProp
err = lockdo.FindNilDo(
s.Ctx,
ckey.FissionUserCreateDicePropOnce(user.ID, mark),
func() (none bool, err error) {
prop, err = m.WithContext(s.Ctx).Where(m.UserID.Eq(user.ID), m.Mark.Eq(mark)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
return prop == nil, nil
},
func() (err error) {
prop = withdefault.FissionDiceProp(&model.FissionDiceProp{
UserID: user.ID,
CampaignID: user.CampaignID,
Mark: mark,
Num: 0,
TotalNum: 0,
})
if err = m.WithContext(s.Ctx).Create(prop); err != nil {
err = s.BE().MysqlError(err)
return
}
return
})
if err != nil {
err = s.BE().CacheError(err)
return
}
return
}
func (s *Dice) _filterCollect(ids []int64, signMap map[int64]mtype.CollectRule) (filter map[int64]struct{}, err error) {
filter = make(map[int64]struct{})
if len(ids) == 0 {
return
}
player := s.GetPlayer()
m := s.GetDao().FissionDiceCollect
list, err := m.WithContext(s.Ctx).Where(m.UserID.Eq(player.UserID), m.RewardID.In(ids...)).Find()
if err != nil {
err = s.BE().MysqlError(err)
return
}
for _, v := range list {
cr := signMap[v.RewardID]
collectNum := cr.InitCollect + ((v.SignNum / cr.SignDay) * cr.AddCollect)
if v.Num >= collectNum {
filter[v.RewardID] = struct{}{}
}
}
return
}