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 }