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

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
}