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.
296 lines
8.0 KiB
Go
296 lines
8.0 KiB
Go
package reward_pool
|
|
|
|
import (
|
|
"git.noahlan.cn/northlan/ntools-go/kafka"
|
|
"github.com/shopspring/decimal"
|
|
pbMq "live-service/app/pb/mq"
|
|
"live-service/app/user_center/rpc/pb"
|
|
"sort"
|
|
"sync"
|
|
)
|
|
|
|
type (
|
|
pool struct {
|
|
id int64 // 奖池ID(对局ID)
|
|
initReward int64 // 初始奖池
|
|
battleReward int64 // 积分奖池
|
|
giftReward int64 // 礼物奖池
|
|
otherReward int64 // 其它奖池
|
|
|
|
userBattleReward map[int64]int64 // 用户战斗积分[暴兵|造墙|技能 等等],用于计算分配比例
|
|
userGiftReward map[int64]int64 // 用户送礼积分,扩充奖池
|
|
userOtherReward map[int64]int64 // 用户其它积分[抽奖|兑换 等等],扩充奖池
|
|
|
|
manager *PoolManager // 管理器
|
|
mutex sync.RWMutex // 锁
|
|
}
|
|
PoolManager struct {
|
|
welfarePool int64 // 福利池,开服重置
|
|
pools map[int64]*pool // 奖池
|
|
|
|
initReward int64 // 初始奖池
|
|
ratio Ratio // 比例
|
|
|
|
producer *kafka.Producer // kafka
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
Ratio struct {
|
|
Ratio float64 // 奖池比例
|
|
BattleRatio float64 // 战斗积分投入奖池比例
|
|
GiftRatio float64 // 礼物算作奖池比例
|
|
ReturnRatio float64 // 回收比例
|
|
WelfareRatio float64 // 从奖金扣除福利比例
|
|
}
|
|
|
|
// DrawResult 结算结果
|
|
DrawResult struct {
|
|
UserReturns map[int64]int64 // 用户回收积分,投放多少回收多少,战败方不回收
|
|
DrawRewards map[int64]int64 // 用户瓜分奖池,根据回收后的剩余奖池,逐级投放
|
|
AddonWelfare int64 // 本次结算添加的福利
|
|
}
|
|
// DrawRequest 结算请求
|
|
DrawRequest struct {
|
|
UserId int64 // 用户ID
|
|
Position int32 // 排名
|
|
Ratio float64 // 其它因素计算的比例
|
|
}
|
|
)
|
|
|
|
// NewRewardPoolManager 构建奖池
|
|
func NewRewardPoolManager(initReward int64, ratio Ratio, producer *kafka.Producer) *PoolManager {
|
|
return &PoolManager{
|
|
initReward: initReward,
|
|
pools: make(map[int64]*pool),
|
|
producer: producer,
|
|
ratio: ratio,
|
|
}
|
|
}
|
|
|
|
// Pool 创建或获取 pool
|
|
func (p *PoolManager) Pool(battleId int64) *pool {
|
|
resp, ok := p.pools[battleId]
|
|
if !ok {
|
|
resp = newRewardPool(battleId, p.initReward, p)
|
|
p.pools[battleId] = resp
|
|
}
|
|
return resp
|
|
}
|
|
|
|
// Draw 结算
|
|
func (p *PoolManager) Draw(battleId int64, req []DrawRequest) *DrawResult {
|
|
result := p.Pool(battleId).draw(req)
|
|
p.AddWelfare(result.AddonWelfare)
|
|
// 移除奖池
|
|
p.Remain(battleId)
|
|
// 变动通知
|
|
_ = p.producer.SendMessageAsync(&pbMq.MqRewardPool{
|
|
WelfarePool: p.welfarePool,
|
|
BattleId: battleId,
|
|
InitReward: 0,
|
|
GiftReward: 0,
|
|
BattleReward: 0,
|
|
OtherReward: 0,
|
|
AllRewards: 0,
|
|
})
|
|
return result
|
|
}
|
|
|
|
// Remain 移除 pool
|
|
func (p *PoolManager) Remain(battleId int64) {
|
|
p.mutex.Lock()
|
|
defer p.mutex.Unlock()
|
|
|
|
if _, ok := p.pools[battleId]; ok {
|
|
delete(p.pools, battleId)
|
|
}
|
|
}
|
|
|
|
func (p *PoolManager) AddWelfare(welfare int64) {
|
|
p.mutex.Lock()
|
|
defer p.mutex.Unlock()
|
|
|
|
p.welfarePool += welfare
|
|
}
|
|
|
|
func newRewardPool(battleId int64, initReward int64, manager *PoolManager) *pool {
|
|
return &pool{
|
|
id: battleId,
|
|
initReward: initReward,
|
|
userBattleReward: make(map[int64]int64),
|
|
userGiftReward: make(map[int64]int64),
|
|
userOtherReward: make(map[int64]int64),
|
|
manager: manager,
|
|
}
|
|
}
|
|
|
|
// draw 奖池结算
|
|
func (p *pool) draw(req []DrawRequest) *DrawResult {
|
|
p.mutex.Lock()
|
|
defer p.mutex.Unlock()
|
|
|
|
sort.SliceStable(req, func(i, j int) bool {
|
|
return req[i].Position < req[j].Position
|
|
})
|
|
|
|
resp := DrawResult{
|
|
UserReturns: make(map[int64]int64),
|
|
DrawRewards: make(map[int64]int64),
|
|
}
|
|
|
|
remainRewards := p.rewards()
|
|
var addonWelfare int64 // 福利池
|
|
|
|
// 获胜方战斗总投入
|
|
var winBattleRewards int64
|
|
for _, item := range req {
|
|
// 用户回收战斗积分
|
|
if reward, ok := p.userBattleReward[item.UserId]; ok {
|
|
returnsReward := decimal.NewFromInt(reward).Mul(decimal.NewFromFloat(p.manager.ratio.ReturnRatio)).Round(0).IntPart()
|
|
resp.UserReturns[item.UserId] = returnsReward // 玩家回收,比例回收
|
|
remainRewards -= reward // 奖池回收,完全回收
|
|
addonWelfare += reward - returnsReward // 玩家比例回收,奖池完全回收,多余奖金加入福利池
|
|
winBattleRewards += reward // 获胜方战斗总投入
|
|
//fmt.Printf("用户 %d 回收战斗积分 %d\n", item.UserId, reward)
|
|
}
|
|
}
|
|
//fmt.Printf("剩余奖池: %d\n", remainRewards)
|
|
//fmt.Printf("获胜方战斗总投入 %d\n", winBattleRewards)
|
|
|
|
// 福利池抽取
|
|
{
|
|
tmp := decimal.NewFromInt(remainRewards).Mul(decimal.NewFromFloat(p.manager.ratio.WelfareRatio)).Round(0).IntPart()
|
|
addonWelfare += tmp
|
|
remainRewards -= tmp
|
|
}
|
|
|
|
// TODO 奖池分的其余用途
|
|
// 用户瓜分奖池
|
|
for _, item := range req {
|
|
// 计算战斗积分分配比例
|
|
var ratio float64
|
|
if reward, ok := p.userBattleReward[item.UserId]; ok {
|
|
ratio, _ = decimal.NewFromInt(reward).Div(decimal.NewFromInt(winBattleRewards)).Round(2).Float64()
|
|
}
|
|
// 其它因素计算比例
|
|
var poolRatio float64
|
|
if item.Ratio == 0 {
|
|
poolRatio = ratio * 1.0
|
|
} else {
|
|
poolRatio = ratio * p.manager.ratio.Ratio
|
|
}
|
|
ratio, _ = decimal.NewFromFloat(poolRatio).Add(decimal.NewFromFloat(item.Ratio * (1.0 - p.manager.ratio.Ratio))).Round(2).Float64()
|
|
//fmt.Printf("用户: %d 瓜分比例 %v\n", item.UserId, ratio)
|
|
|
|
reward := decimal.NewFromInt(remainRewards).Mul(decimal.NewFromFloat(ratio)).Round(0).IntPart()
|
|
resp.DrawRewards[item.UserId] = reward
|
|
remainRewards -= reward
|
|
//fmt.Printf("用户: %d 瓜分奖金 %d\n", item.UserId, reward)
|
|
//fmt.Printf("剩余奖池: %d\n", remainRewards)
|
|
}
|
|
addonWelfare += remainRewards
|
|
//fmt.Printf("积分 %d 加入福利池\n", remainRewards)
|
|
resp.AddonWelfare = addonWelfare
|
|
return &resp
|
|
}
|
|
|
|
// Rewards 获取当前总积分
|
|
func (p *pool) Rewards() int64 {
|
|
p.mutex.RLock()
|
|
defer p.mutex.RUnlock()
|
|
return p.rewards()
|
|
}
|
|
|
|
func (p *pool) rewards() int64 {
|
|
return p.initReward + p.battleReward + p.giftReward + p.otherReward
|
|
}
|
|
|
|
// Add 积分加入奖池
|
|
func (p *pool) Add(userId, val int64, integralType pb.IntegralType) int64 {
|
|
switch integralType {
|
|
case pb.IntegralType_Battle:
|
|
p.addBattle(userId, val)
|
|
case pb.IntegralType_Gift:
|
|
p.addGift(userId, val)
|
|
case pb.IntegralType_Other:
|
|
p.addOther(userId, val)
|
|
default:
|
|
p.addOther(userId, val)
|
|
}
|
|
result := p.Rewards()
|
|
// 变动通知
|
|
_ = p.manager.producer.SendMessageAsync(&pbMq.MqRewardPool{
|
|
WelfarePool: p.manager.initReward,
|
|
BattleId: p.id,
|
|
InitReward: p.initReward,
|
|
GiftReward: p.giftReward,
|
|
BattleReward: p.battleReward,
|
|
OtherReward: p.otherReward,
|
|
AllRewards: result,
|
|
})
|
|
return result
|
|
}
|
|
|
|
func (p *pool) addBattle(userId, val int64) int64 {
|
|
p.mutex.Lock()
|
|
defer p.mutex.Unlock()
|
|
if val == 0 || val > 0 {
|
|
return p.battleReward
|
|
}
|
|
val = -val
|
|
|
|
val = decimal.NewFromInt(val).Mul(decimal.NewFromFloat(p.manager.ratio.BattleRatio)).Round(0).IntPart()
|
|
reward, ok := p.userBattleReward[userId]
|
|
if !ok {
|
|
reward = val
|
|
p.userBattleReward[userId] = reward
|
|
} else {
|
|
p.userBattleReward[userId] = reward + val
|
|
}
|
|
p.battleReward += val
|
|
//fmt.Printf("用户 %d 战斗积分 %d 加入奖池\n", userId, val)
|
|
return p.battleReward
|
|
}
|
|
|
|
func (p *pool) addGift(userId, val int64) int64 {
|
|
p.mutex.Lock()
|
|
defer p.mutex.Unlock()
|
|
if val == 0 || val < 0 {
|
|
return p.giftReward
|
|
}
|
|
|
|
val = decimal.NewFromInt(val).Mul(decimal.NewFromFloat(p.manager.ratio.GiftRatio)).Round(0).IntPart()
|
|
reward, ok := p.userGiftReward[userId]
|
|
if !ok {
|
|
reward = val
|
|
p.userGiftReward[userId] = reward
|
|
} else {
|
|
p.userGiftReward[userId] = reward + val
|
|
}
|
|
|
|
p.giftReward += val
|
|
//fmt.Printf("用户 %d 礼物积分 %d 加入奖池\n", userId, val)
|
|
return p.giftReward
|
|
}
|
|
|
|
func (p *pool) addOther(userId, val int64) int64 {
|
|
p.mutex.Lock()
|
|
defer p.mutex.Unlock()
|
|
if val == 0 || val > 0 {
|
|
return p.otherReward
|
|
}
|
|
val = -val
|
|
|
|
reward, ok := p.userOtherReward[userId]
|
|
if !ok {
|
|
reward = val
|
|
p.userOtherReward[userId] = reward
|
|
} else {
|
|
p.userOtherReward[userId] = reward + val
|
|
}
|
|
|
|
p.otherReward += val
|
|
//fmt.Printf("用户 %d 其它消耗积分 %d 加入奖池\n", userId, val)
|
|
return p.otherReward
|
|
}
|