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.
625 lines
14 KiB
Go
625 lines
14 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/provider/cachecurd"
|
|
"joybuywork/internal/service/provider/fissionaction"
|
|
stos "joybuywork/internal/service/provider/s2s"
|
|
"time"
|
|
|
|
"gorm.io/gen/field"
|
|
)
|
|
|
|
const (
|
|
CoinDeductStatusLack = iota + 1 // 缺乏金币
|
|
CoinDeductStatusMax // 达到投放上限
|
|
CoinDeductStatusEnd // 当前场次已结束
|
|
)
|
|
|
|
const (
|
|
CoinGainStatusNotOpen = iota + 1 // 未开奖
|
|
CoinGainStatusLackJoin // 参与次数不足
|
|
CoinGainStatusExpired // 领奖超时
|
|
CoinGainStatusGot // 已领取
|
|
CoinGainStatusFail // 领取失败
|
|
)
|
|
|
|
type CoinStatus struct {
|
|
GetCoin int64 // 预计获得金币数
|
|
UseNum int32 // 投放次数
|
|
AllCoin int64 // 总奖池金额
|
|
OpenSec int64 // 剩余开奖时间(秒)
|
|
CanGet int32 // 是否可以领取瓜分了
|
|
End int32 // 是否没有下一场了
|
|
}
|
|
|
|
// Coin 金币池交互逻辑
|
|
type Coin struct {
|
|
Player
|
|
player *model.FissionCoin
|
|
conf *model.FissionCampaignCoin
|
|
|
|
nowScene *model.FissionCoinScene
|
|
|
|
end int32
|
|
|
|
preSceneOk int8
|
|
|
|
lackKey string
|
|
}
|
|
|
|
func NewCoin(ctx context.Context, user *model.FissionUser, opts ...Option) (player *Coin, err error) {
|
|
player = &Coin{}
|
|
|
|
err = player.Init(ctx, user, consts.IdCoin, opts...)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = player.initNowScene()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = player.initPlayer()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Coin) FinishTask(tasks ...*model.FissionCampaignTask) (err error) {
|
|
player := s.GetPlayer()
|
|
|
|
var logs []*model.FissionCoinLog
|
|
for _, task := range tasks {
|
|
if task.AwardMode == mtype.FissionCampaignTaskAwardModeManual || task.PropMark == mtype.FissionCampaignTaskPropMarkChance {
|
|
continue
|
|
}
|
|
|
|
var source = mtype.FissionCoinLogSourceFinishTask
|
|
if task.TaskType == mtype.FissionCampaignTaskTypeCondAll {
|
|
source = mtype.FissionCoinLogSourceFinishAllTask
|
|
}
|
|
|
|
log := withdefault.FissionCoinLog(&model.FissionCoinLog{
|
|
UserID: player.UserID,
|
|
CampaignID: player.CampaignID,
|
|
CoinID: player.ID,
|
|
SceneID: player.SceneID,
|
|
Type: mtype.FissionCoinLogTypeReward,
|
|
Source: source,
|
|
})
|
|
|
|
log.SetVal(mtype.CoinLogVal{
|
|
TaskId: task.ID,
|
|
RewardId: task.RewardID,
|
|
})
|
|
|
|
logs = append(logs, log)
|
|
}
|
|
|
|
if len(logs) == 0 {
|
|
return
|
|
}
|
|
|
|
return s._addLog(logs...)
|
|
}
|
|
|
|
func (s *Coin) Deduct() (useNum, status int32, err error) {
|
|
player := s.GetPlayer()
|
|
|
|
if s.IsView() {
|
|
useNum = 2
|
|
return
|
|
}
|
|
|
|
nowScene := s.GetNowScene()
|
|
if nowScene.OpenTime <= time.Now().Unix() {
|
|
status = CoinDeductStatusEnd
|
|
return
|
|
}
|
|
|
|
conf, lackKey := s.GetConf(), s.lackKey
|
|
|
|
if player.UseNum >= conf.UseLimit {
|
|
status = CoinDeductStatusMax
|
|
return
|
|
}
|
|
|
|
// 看看是不是有
|
|
if app.Redis.Cache.Exists(s.Ctx, lackKey).Val() != 0 {
|
|
status = CoinDeductStatusLack
|
|
return
|
|
}
|
|
|
|
deductM := s.GetDao().FissionPointDeduct
|
|
deduct, err := deductM.WithContext(s.Ctx).Where(deductM.UserID.Eq(player.UserID)).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
if deduct == nil {
|
|
deduct = withdefault.FissionPointsDeduct(&model.FissionPointDeduct{
|
|
UserID: player.UserID,
|
|
})
|
|
|
|
if err = s.GetDao().FissionPointDeduct.WithContext(s.Ctx).Create(deduct); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
s2s, err := stos.NewPointsChange(s.Ctx, s.cpn, s.user, conf.URLID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
res, err := s2s.CoinDeduct(player, conf.UseCoin, deduct.ID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if res.State == stos.PointsChangeLack {
|
|
app.Redis.Cache.Set(s.Ctx, lackKey, 1, 10*time.Second)
|
|
status = CoinDeductStatusLack
|
|
return
|
|
}
|
|
|
|
m := s.GetDao().FissionCoin
|
|
var up = []field.AssignExpr{
|
|
m.UseCoin.Add(conf.UseCoin),
|
|
m.UseNum.Add(1),
|
|
}
|
|
|
|
if player.Status == mtype.FissionCoinStatusWait {
|
|
up = append(up, m.Status.Value(mtype.FissionCoinStatusJoin))
|
|
}
|
|
|
|
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(up...)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
_, err = s._sceneCoin(player.SceneID, conf.UseCoin, player.UseNum == 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
val, _ := jsonm.MarshalToString(&mtype.CoinLogVal{
|
|
CoinNum: int64(conf.UseCoin),
|
|
RelateId: res.RelateId,
|
|
})
|
|
|
|
err = s._addLog(withdefault.FissionCoinLog(&model.FissionCoinLog{
|
|
CampaignID: player.CampaignID,
|
|
UserID: player.UserID,
|
|
CoinID: player.ID,
|
|
SceneID: player.SceneID,
|
|
Type: mtype.FissionCoinLogTypeUse,
|
|
Source: mtype.FissionCoinLogSourcePutIn,
|
|
Val: val,
|
|
}))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
useNum = player.UseNum + 1
|
|
s.player.UseNum += 1
|
|
|
|
fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).UseCoin(time.Now().Unix(), player.SceneID, int64(useNum))
|
|
|
|
_, err = deductM.WithContext(s.Ctx).Delete(deduct)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Coin) Gain() (getCoin int64, status int32, err error) {
|
|
|
|
if s.IsView() {
|
|
getCoin = 99999
|
|
return
|
|
}
|
|
|
|
nowScene := s.GetNowScene()
|
|
if nowScene.OpenTime > time.Now().Unix() {
|
|
status = CoinGainStatusNotOpen
|
|
return
|
|
}
|
|
|
|
player, conf := s.GetPlayer(), s.GetConf()
|
|
if player.Status == mtype.FissionCoinStatusWait {
|
|
status = CoinGainStatusLackJoin
|
|
return
|
|
}
|
|
|
|
if player.Status == mtype.FissionCoinStatusOk {
|
|
status = CoinGainStatusGot
|
|
return
|
|
}
|
|
|
|
if player.Status == mtype.FissionCoinStatusExpired {
|
|
status = CoinGainStatusExpired
|
|
return
|
|
}
|
|
|
|
s2s, err := stos.NewPointsChange(s.Ctx, s.cpn, s.user, conf.URLID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, getCoin, err = s._getCoin(player.SceneID, player.UseNum)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
res, err := s2s.CoinGain(player, int32(getCoin))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if res.State != stos.PointsChangeSuccess {
|
|
status = CoinGainStatusFail
|
|
return
|
|
}
|
|
|
|
m := s.GetDao().FissionCoin
|
|
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(
|
|
m.GetCoin.Add(int32(getCoin)),
|
|
m.Status.Value(mtype.FissionCoinStatusOk),
|
|
)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
sceneData, err := s._sceneData(player.SceneID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
val, _ := jsonm.MarshalToString(&mtype.CoinLogVal{
|
|
CoinNum: getCoin,
|
|
RelateId: res.RelateId,
|
|
})
|
|
|
|
err = s._addLog(withdefault.FissionCoinLog(&model.FissionCoinLog{
|
|
CampaignID: player.CampaignID,
|
|
UserID: player.UserID,
|
|
CoinID: player.ID,
|
|
SceneID: player.SceneID,
|
|
Type: mtype.FissionCoinLogTypeAdd,
|
|
Source: mtype.FissionCoinLogSourceGetOk,
|
|
Val: val,
|
|
}))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = sceneData.Incr(cachecurd.CoinSceneGetNum, 1)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
_, err = sceneData.Incr(cachecurd.CoinSceneGetTotal, getCoin)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).GetCoin(time.Now().Unix(), player.SceneID)
|
|
|
|
return
|
|
}
|
|
|
|
// GetStatus 获取用户状态
|
|
func (s *Coin) GetStatus() (status CoinStatus, err error) {
|
|
nowScene, player := s.GetNowScene(), s.GetPlayer()
|
|
|
|
userCoin, getCoin, err := s._getCoin(nowScene.ID, player.UseNum)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
status = CoinStatus{
|
|
GetCoin: getCoin,
|
|
UseNum: player.UseNum,
|
|
AllCoin: int64(nowScene.InitCoin) + userCoin,
|
|
OpenSec: nowScene.OpenTime - time.Now().Unix(),
|
|
CanGet: 0,
|
|
End: s.end,
|
|
}
|
|
|
|
if status.OpenSec < 0 {
|
|
status.OpenSec = 0
|
|
}
|
|
|
|
// 开奖了
|
|
if nowScene.OpenTime <= time.Now().Unix() && nowScene.RewardTime > time.Now().Unix() && player.Status == mtype.FissionCoinStatusJoin {
|
|
status.CanGet = 1
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// GetConf 获取玩法配置
|
|
func (s *Coin) GetConf() *model.FissionCampaignCoin {
|
|
if s.conf == nil {
|
|
s.conf, _ = cache.NewCampaignCoin(s.Ctx).FindByCampaignId(s.user.CampaignID)
|
|
}
|
|
|
|
return s.conf
|
|
}
|
|
|
|
// GetNowScene 当前场次数据
|
|
func (s *Coin) GetNowScene() *model.FissionCoinScene {
|
|
return s.nowScene
|
|
}
|
|
|
|
// GetNowScene 当前场次数据
|
|
func (s *Coin) initNowScene() (err error) {
|
|
|
|
m := s.GetDao().FissionCoinScene
|
|
nowScene, err := m.WithContext(s.Ctx).Where(m.CampaignID.Eq(s.cpn.ID), m.OpenTime.Gte(time.Now().Unix())).Order(m.OpenTime).First()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
if nowScene == nil {
|
|
nowScene, _ = m.WithContext(s.Ctx).Where(m.CampaignID.Eq(s.cpn.ID)).Last()
|
|
s.end = 1
|
|
}
|
|
|
|
s.nowScene = nowScene
|
|
|
|
return
|
|
}
|
|
|
|
// GetPlayer 获取玩家基础数据
|
|
func (s *Coin) GetPlayer() *model.FissionCoin {
|
|
return s.player
|
|
}
|
|
|
|
// 玩家
|
|
func (s *Coin) initPlayer() (err error) {
|
|
player, err := s._dbFirst()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
s.player = player
|
|
s.lackKey = ckey.FissionCoinLack(player.UserID)
|
|
}()
|
|
|
|
if player != nil {
|
|
return
|
|
}
|
|
|
|
player, err = s.firstOrCreate()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// 创建玩家基础数据
|
|
func (s *Coin) firstOrCreate() (player *model.FissionCoin, 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 *Coin) createPlayer() (player *model.FissionCoin, err error) {
|
|
nowScene := s.GetNowScene()
|
|
playerM := dao.FissionCoin
|
|
player = withdefault.FissionCoin(&model.FissionCoin{
|
|
CampaignID: s.user.CampaignID,
|
|
UserID: s.user.ID,
|
|
SceneID: nowScene.ID,
|
|
PreSceneOk: s.preSceneOk,
|
|
})
|
|
|
|
err = playerM.WithContext(s.Ctx).Create(player)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Coin) _dbFirst() (player *model.FissionCoin, err error) {
|
|
nowScene := s.GetNowScene()
|
|
|
|
playerM := dao.FissionCoin
|
|
player, err = playerM.WithContext(s.Ctx).Where(playerM.UserID.Eq(s.user.ID), playerM.SceneID.Eq(nowScene.ID)).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
return nil, s.BE().MysqlError(err)
|
|
}
|
|
|
|
if nowScene.PreScene == 0 || (player != nil && player.PreSceneOk == mtype.Yes) || s.end == 1 {
|
|
return
|
|
}
|
|
|
|
lastPlayer, err := playerM.WithContext(s.Ctx).Where(playerM.UserID.Eq(s.user.ID)).Last()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
return nil, s.BE().MysqlError(err)
|
|
}
|
|
|
|
if lastPlayer == nil {
|
|
s.preSceneOk = mtype.Yes
|
|
return
|
|
}
|
|
|
|
// 不是上一场
|
|
if lastPlayer.SceneID != nowScene.PreScene {
|
|
|
|
if lastPlayer.Status == mtype.FissionCoinStatusJoin {
|
|
var scene *model.FissionCoinScene
|
|
m := s.GetDao().FissionCoinScene
|
|
scene, err = m.WithContext(s.Ctx).Where(m.ID.Eq(lastPlayer.SceneID)).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
_, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(lastPlayer.ID)).UpdateSimple(playerM.Status.Value(mtype.FissionCoinStatusExpired))
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
val, _ := jsonm.MarshalToString(&mtype.CoinLogVal{
|
|
CoinNum: s._count(scene.UserCoin, scene.JoinNum, lastPlayer.UseNum),
|
|
})
|
|
|
|
err = s._addLog(withdefault.FissionCoinLog(&model.FissionCoinLog{
|
|
CreatedAt: scene.RewardTime + consts.DaySec,
|
|
CampaignID: lastPlayer.CampaignID,
|
|
UserID: lastPlayer.UserID,
|
|
CoinID: lastPlayer.ID,
|
|
SceneID: lastPlayer.SceneID,
|
|
Type: mtype.FissionCoinLogTypeAdd,
|
|
Source: mtype.FissionCoinLogSourceGetExpired,
|
|
Val: val,
|
|
}))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
}
|
|
|
|
s.preSceneOk = mtype.Yes
|
|
|
|
return
|
|
}
|
|
|
|
// 如果这个用户上一场没结算,要拿来结算一下
|
|
if lastPlayer.Status == mtype.FissionCoinStatusJoin {
|
|
m := s.GetDao().FissionCoinScene
|
|
var preScene *model.FissionCoinScene
|
|
preScene, err = m.WithContext(s.Ctx).Where(m.ID.Eq(lastPlayer.SceneID)).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
return nil, s.BE().MysqlError(err)
|
|
}
|
|
|
|
if preScene.RewardTime > time.Now().Unix() {
|
|
player = lastPlayer
|
|
s.nowScene = preScene
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
s.preSceneOk = mtype.Yes
|
|
|
|
return player, nil
|
|
}
|
|
|
|
func (s *Coin) _sceneCoin(sceneId int64, add int32, first bool) (num int64, err error) {
|
|
sceneData, err := s._sceneData(sceneId)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if add > 0 {
|
|
_, _ = sceneData.Incr(cachecurd.CoinSceneJoinNum, 1)
|
|
if first {
|
|
_, _ = sceneData.Incr(cachecurd.CoinSceneJoinUserNum, 1)
|
|
}
|
|
|
|
return sceneData.Incr(cachecurd.CoinSceneUserCoin, int64(add))
|
|
}
|
|
|
|
return sceneData.GetInt64(cachecurd.CoinSceneUserCoin)
|
|
}
|
|
|
|
// 奖池金币总数,用户获得的金币数
|
|
func (s *Coin) _getCoin(sceneId int64, useNum int32) (userCoin, getCoin int64, err error) {
|
|
sceneData, err := s._sceneData(sceneId)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
userCoin, _ = sceneData.GetInt64(cachecurd.CoinSceneUserCoin)
|
|
joinNum, _ := sceneData.GetInt64(cachecurd.CoinSceneJoinNum)
|
|
|
|
getCoin = s._count(userCoin, joinNum, useNum)
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Coin) _sceneData(sceneId int64) (sceneData *cachecurd.HMap, err error) {
|
|
conf, key := s.GetConf(), ckey.FissionCoinSceneData(sceneId)
|
|
return cachecurd.NewHMap(s.Ctx, key,
|
|
cachecurd.SetCoinSceneBuild(s.cpn.ID, sceneId),
|
|
cachecurd.SetHMapExp(time.Duration(conf.OpenDays+1)*consts.TimeDay),
|
|
)
|
|
}
|
|
|
|
func (s *Coin) _addLog(logs ...*model.FissionCoinLog) (err error) {
|
|
if len(logs) == 0 {
|
|
return
|
|
}
|
|
|
|
err = s.GetDao().FissionCoinLog.WithContext(s.Ctx).Create(logs...)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Coin) _count(userCoin, joinNum int64, useNum int32) (getCoin int64) {
|
|
scene := s.GetNowScene()
|
|
|
|
var oneCoin int64
|
|
if joinNum > 0 || scene.InitUse > 0 {
|
|
oneCoin = (userCoin + int64(scene.InitCoin)) / (joinNum + int64(scene.InitUse))
|
|
if oneCoin == 0 {
|
|
oneCoin = 1
|
|
}
|
|
}
|
|
|
|
return int64(useNum) * oneCoin
|
|
}
|