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.

648 lines
15 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 campaignuser
import (
"joybuywork/internal/app"
"joybuywork/internal/consts"
"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/logic"
"joybuywork/internal/utils/verify"
pb "joybuywork/proto/go_proto/w_campaign_user"
"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 (
campaignUserDiceFailTypeNone = iota // 没有错误
campaignUserDiceFailTypeLack // 缺骰子数
campaignUserDiceFailTypeProps // 道具不足
)
type Dice struct {
logic.Logic
Req *pb.DiceReq
Res *pb.DiceRes
isView bool //是否预览用户
prop map[string]*model.FissionDiceProp
user *model.FissionUser
}
func (s *Dice) Run() error {
return logic.Run(s)
}
func (s *Dice) Exec() (err error) {
player, run, err := s.player()
if err != nil {
return
}
if s._isFixedRoll() {
return s.useFixedRoll(player, run)
}
return s.normal(player, run)
}
func (s *Dice) player() (player *model.FissionDice, run *model.FissionDiceRun, err error) {
cpn, err := finder.Campaign(s.Ctx, s.Req.GetCampaignId())
if err != nil {
err = s.BE().CacheError(err)
return
}
if cpn == nil || cpn.IsDel == mtype.Yes {
err = workerr.NotFound("not found cpn")
return
}
user, err := finder.FissionUser(s.Ctx, cpn.ID, s.Req.GetUserId())
if err != nil {
err = s.BE().CacheError(err)
return
}
if user == nil {
err = workerr.UnauthorizedError
return
}
m := s.GetDao().FissionDice
player, err = m.WithContext(s.Ctx).Where(m.UserID.Eq(user.ID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if player == nil {
err = workerr.NotFound("not found player")
return
}
run, err = cmm.NewCMM(s.Ctx, s.GetDao()).GetUserMap(user)
if err != nil {
return
}
s.isView = user.IsView == mtype.Yes
s.user = user
return
}
func (s *Dice) useFixedRoll(player *model.FissionDice, run *model.FissionDiceRun) (err error) {
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 {
s.Res.FailType = campaignUserDiceFailTypeProps
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(s.Req.GetVal()) - 1
res, err := s._rollMap(run, step)
if err != nil || res == nil {
return
}
// 记录日志
err = s._diceRecord(player, res, true)
if err != nil {
return
}
// 使用道具埋点
fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).UseProp(time.Now().Unix(), consts.PropNameFixedDice)
return s._resp(run, res)
}
func (s *Dice) normal(player *model.FissionDice, run *model.FissionDiceRun) (err error) {
if player.Chance <= 0 {
s.Res.FailType = campaignUserDiceFailTypeLack
return
}
step := utils.RandNumber(6)
res, err := s._rollMap(run, int(step))
if err != nil || res == nil {
return
}
// 不是预览用户要减次数,并且不是再来一次就减一次
if !s.isView && res.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._diceRecord(player, res, false)
if err != nil {
return
}
return s._resp(run, res)
}
// 结果处理
func (s *Dice) _resp(run *model.FissionDiceRun, res *rollData) (err error) {
// 更新地图信息
runM := s.GetDao().FissionDiceRun
_, err = runM.WithContext(s.Ctx).Where(runM.ID.Eq(run.ID)).UpdateSimple(
runM.MapID.Value(res.MapId),
runM.MapHashID.Value(res.MapHashId),
runM.GoStep.Add(int64(res.Step)+1),
)
if err != nil {
err = s.BE().MysqlError(err)
return
}
s.Res.MarkType = int32(res.MarkType)
s.Res.MapHashId = res.MapHashId
s.Res.RewardTitle = res.RewardTitle
s.Res.ImgUrl = res.ImgUrl
s.Res.IsIntact = res.Intact
s.Res.RollNum = res.Step + 1
s.Res.Lack = res.Lack
s.Res.RewardId = res.RewardId
return
}
func (s *Dice) _rollMap(run *model.FissionDiceRun, idx int) (res *rollData, err error) {
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(run.CampaignID, win)
}
// 不是固定骰子,要看看走不走概率
if !s._isFixedRoll() {
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(run, list, win, lose)
}
}
// 如果都没中奖品,那就不用做奖品处理
if !verify.InArray(win.MarkType, mtype.FissionDiceMapMarkTypeNormal, mtype.FissionDiceMapMarkTypeSuper) {
return win, nil
}
res, err = s._reward(run, win, lose)
if err != nil {
return
}
return
}
func (s *Dice) _reward(run *model.FissionDiceRun, win, lose *rollData) (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._getCollect(run, reward)
if err != nil {
return
}
return s._rewardInDb(reward, collect, win, lose)
}
// 完整的奖品处理
func (s *Dice) _intactReward(cpn *model.FissionCampaign, 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(), cpn.ProjectID, s.user).One(reward, false, opts...)
if lack {
return
}
s.AddAfterRun(func() (err error) {
for _, do := range async {
do()
}
return
})
return
}
// 概率获取奖品
func (s *Dice) _odds(run *model.FissionDiceRun, diceMaps []*model.FissionDiceMap, win *rollData, lose *rollData) (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 allOdds int
var prize []lucky.Reward
for _, v := range list {
odds := int(v.Odds)
prize = append(prize, lucky.Reward{
Share: odds,
Mark: v.ID,
})
allOdds = allOdds + odds
rewardMap[v.ID] = v
}
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._getCollect(run, rewardMap[id])
if err != nil {
return
}
return s._rewardInDb(rewardMap[id], collect, win, lose)
}
// 获取收集碎片的记录
func (s *Dice) _getCollect(run *model.FissionDiceRun, reward *model.FissionCampaignReward) (collect *model.FissionDiceCollect, err error) {
collectM := s.GetDao().FissionDiceCollect
collect, err = collectM.WithContext(s.Ctx).Where(collectM.UserID.Eq(run.UserID), collectM.RewardID.Eq(reward.ID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
if collect == nil {
collect = withdefault.FissionDiceCollect(&model.FissionDiceCollect{
UserID: run.UserID,
CampaignID: run.CampaignID,
RewardID: reward.ID,
Num: 0,
IntactNum: 0,
})
if err = collectM.WithContext(s.Ctx).Create(collect); err != nil {
err = s.BE().MysqlError(err)
return
}
}
return
}
// 奖品入库
func (s *Dice) _rewardInDb(reward *model.FissionCampaignReward, collect *model.FissionDiceCollect, win, lose *rollData) (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 && s.Req.GetMark() != mtype.FissionDicePropMarkFixedRoll {
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(cpn, reward, lack)
if err != nil {
return
}
if lack { // 没有库存,那就不让客户看动画了
up = append(up, collectM.Num.Value(0), collectM.IntactNum.Add(1))
s.AddAfterRun(func() (err error) {
// 骰子记录
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,
})
// 这里事务已经提交了,不能用有事务的dao
err = s.Dao().FissionDiceRecord.WithContext(s.Ctx).Create(diceRecord)
if err != nil {
err = s.BE().MysqlError(err)
return
}
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) _viewWin(cpnId int64, rd *rollData) (win *rollData, err error) {
win = rd
if win.RewardId == 0 {
return
}
cpn, err := finder.Campaign(s.Ctx, cpnId)
if err != nil {
err = s.BE().CacheError(err)
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, 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) _diceRecord(player *model.FissionDice, win *rollData, luckyProp bool) (err error) {
if s.Res.FailType != campaignUserDiceFailTypeNone {
return
}
record := withdefault.FissionDiceRecord(&model.FissionDiceRecord{
CampaignID: player.CampaignID,
UserID: player.UserID,
})
data := &mtype.DiceRecord{
Point: win.Step + 1,
RewardId: win.RewardId,
}
defer func() {
err = s.GetDao().FissionDiceRecord.WithContext(s.Ctx).Create(record)
if err != nil {
err = s.BE().MysqlError(err)
}
}()
// 使用幸运骰子道具
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
}
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
}
// 是否固定骰子
func (s *Dice) _isFixedRoll() bool {
return s.Req.GetMark() == mtype.FissionDicePropMarkFixedRoll
}