| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- /*
- 1.玩家的投注总额都记录起来
- 2.每天结算开奖,开奖后把当前数据到为一个历史奖池的数据,历史奖池只保存一份,然后发奖,清空投注总额,奖池清空,并且重新开始.
- 3.每小局游戏的投注总额的百分之2作为奖池,不断累加
- 4.第一名得到百分之25的奖池
- 5.第二名得到百分之18的奖池
- 6.第三名得到百分之13的奖池
- 7.第四名得到百分之9的奖池
- 8.第五名得到百分之5的奖池
- 9.第六名到第十得到百分之3的奖池
- 10.第十一名到第十五得到百分之2的奖池
- 11.第十六名到第二十得到百分之1的奖池
- */
- package gamelogic
- import (
- "encoding/json"
- "fmt"
- "sort"
- "sync"
- "time"
- "bet24.com/log"
- "bet24.com/redis"
- timetool "bet24.com/servers/common"
- "bet24.com/servers/games/masharie_table/common"
- "bet24.com/servers/games/masharie_table/config"
- item "bet24.com/servers/micros/item_inventory/proto"
- userservices "bet24.com/servers/micros/userservices/proto"
- "bet24.com/servers/transaction"
- )
- const BetPoolKey = "betPool"
- const RankCount = 20
- // 奖池排名奖励
- type Prize struct {
- Day string // 奖池日期
- Rank int // 名次 (1-15)
- UserId int // 用户ID
- TotalBet int // 总投注额
- Award []item.ItemPack // 奖励
- }
- type Rank struct {
- UserId int
- BetAmount int
- }
- // 奖池
- type betPool struct {
- CycleTime int // 奖池周期
- Total int // 总投注额
- Jackpot int // 奖池 (总下注额的2%) 从config.Room.JackpotRate中取
- BetList map[int]int // 所有玩家的下注总额
- cycle string `json:"-"` // 奖池周期
- lock *sync.RWMutex
- roomInfo *config.RoomInfo
- }
- // 创建一个周的奖池
- func newWeekBetPool(roomInfo *config.RoomInfo) *betPool {
- ret := new(betPool)
- ret.lock = &sync.RWMutex{}
- ret.BetList = make(map[int]int)
- ret.roomInfo = roomInfo
- var cycle = "Week"
- ret.cycle = cycle
- // 从redis中获取奖池数据
- key := fmt.Sprintf("%v:%v", BetPoolKey, cycle)
- data, _ := redis.String_Get(common.GetRedisKey(key))
- ts := timetool.GetTimeStamp()
- weekIndex := timetool.GetWeekIndex(ts)
- if data == "" {
- // redis中没有奖池数据
- ret.CycleTime = weekIndex
- ret.betPoolToRedis()
- } else {
- // redis中有奖池数据
- bp := &betPool{
- CycleTime: 0,
- BetList: make(map[int]int),
- Total: 0,
- Jackpot: 0,
- }
- err := json.Unmarshal([]byte(data), bp)
- if err != nil {
- log.Release("newWeekBetPool json.Unmarshal err:%v", err)
- }
- if bp.CycleTime != weekIndex {
- // 奖池数据不是今天的,重新初始化
- ret.CycleTime = weekIndex
- ret.Total = 0
- ret.Jackpot = 0
- log.Debug("====newWeekBetPool.Refresh CycleTime[%d] Total[%d] Jackpot[%d]====", bp.CycleTime, bp.Total, bp.Jackpot)
- ret.betPoolToRedis()
- } else {
- // 奖池数据是今天的,直接赋值
- ret.CycleTime = bp.CycleTime
- ret.BetList = bp.BetList
- ret.Total = bp.Total
- ret.Jackpot = bp.Jackpot
- }
- }
- //起一个定时器,每周发奖并且清空奖池
- ret.startTimer()
- return ret
- }
- // 创建一个天的奖池
- func newDayBetPool(roomInfo *config.RoomInfo) *betPool {
- ret := new(betPool)
- ret.lock = &sync.RWMutex{}
- ret.BetList = make(map[int]int)
- ret.roomInfo = roomInfo
- var cycle = "Day"
- ret.cycle = cycle
- // 从redis中获取奖池数据
- key := fmt.Sprintf("%v:%v", BetPoolKey, cycle)
- data, _ := redis.String_Get(common.GetRedisKey(key))
- if data == "" {
- // redis中没有奖池数据
- ret.CycleTime = time.Now().Day()
- ret.betPoolToRedis()
- } else {
- // redis中有奖池数据
- bp := &betPool{
- CycleTime: 0,
- BetList: make(map[int]int),
- Total: 0,
- Jackpot: 0,
- }
- err := json.Unmarshal([]byte(data), bp)
- if err != nil {
- log.Release("newDayBetPool json.Unmarshal err:%v", err)
- }
- if bp.CycleTime != time.Now().Day() {
- // 奖池数据不是今天的,重新初始化
- ret.CycleTime = time.Now().Day()
- ret.Total = 0
- ret.Jackpot = 0
- log.Debug("====newDayBetPool.Refresh CycleTime[%d] Total[%d] Jackpot[%d]====", bp.CycleTime, bp.Total, bp.Jackpot)
- ret.betPoolToRedis()
- } else {
- // 奖池数据是今天的,直接赋值
- ret.CycleTime = bp.CycleTime
- ret.BetList = bp.BetList
- ret.Total = bp.Total
- ret.Jackpot = bp.Jackpot
- }
- }
- //起一个定时器,每天0点发奖并且清空奖池
- ret.startTimer()
- return ret
- }
- func (bp *betPool) startTimer() {
- bp.sendPrize()
- go func() {
- time.AfterFunc(1*time.Minute, func() {
- bp.startTimer()
- })
- }()
- }
- // 添加用户
- func (bp *betPool) addUser(userId int) {
- bp.lock.RLock()
- _, ok := bp.BetList[userId]
- bp.lock.RUnlock()
- if !ok {
- bp.lock.Lock()
- bp.BetList[userId] = 0
- bp.lock.Unlock()
- }
- }
- // 移除用户
- func (bp *betPool) removeUser(userId int) {
- bp.lock.Lock()
- delete(bp.BetList, userId)
- bp.lock.Unlock()
- // log.Release("betPool.removeUser %d not found", userId)
- }
- // 是否上榜
- func (bp *betPool) isRank(userId int) bool {
- _, _, _, myRank := bp.getRankList(userId)
- return myRank <= RankCount
- }
- // 每次结算时 将投注数据保存到历史奖池中
- func (bp *betPool) addBetPool(userId int, betAmount int) {
- bp.lock.RLock()
- data, ok := bp.BetList[userId]
- bp.lock.RUnlock()
- // 检查用户是否已经投注过
- if ok {
- bp.lock.Lock()
- data += betAmount
- bp.BetList[userId] = data
- bp.lock.Unlock()
- } else {
- bp.lock.Lock()
- bp.BetList[userId] = betAmount
- // log.Release("addBetPool failed bp.BetList is nil userId:%v,betAmount:%d", userId, betAmount)
- bp.lock.Unlock()
- }
- // 更新奖池总金额和奖池金额
- bp.lock.Lock()
- bp.Total += betAmount
- bp.Jackpot += betAmount * bp.roomInfo.JackpotRate / 100
- bp.lock.Unlock()
- }
- func (bp *betPool) betPoolToRedis() {
- bp.lock.RLock()
- var cycle = bp.cycle
- data, err := json.Marshal(bp)
- bp.lock.RUnlock()
- key := fmt.Sprintf("%v:%v", BetPoolKey, cycle)
- if err != nil {
- log.Debug("%vbetPool json.Marshal err:%v", cycle, err)
- return
- }
- redis.String_Set(common.GetRedisKey(key), string(data))
- }
- // 获取排名列表
- func (bp *betPool) getRankList(userId int) ([]Rank, int, int, int) {
- // 统计每个用户的投注金额
- var myBetAmount = 0
- var myRank = RankCount
- var rankList []Rank
- bp.lock.RLock()
- for k, v := range bp.BetList {
- rankList = append(rankList, Rank{k, v})
- }
- bp.lock.RUnlock()
- if len(rankList) == 0 {
- return rankList, bp.Jackpot, myBetAmount, myRank
- }
- // 将投注金额从大到小排序
- sort.Slice(rankList, func(i, j int) bool {
- return rankList[i].BetAmount > rankList[j].BetAmount
- })
- // 查询自己的下注额度和排名
- if userId > 0 {
- for i, bet := range rankList {
- if bet.UserId == userId {
- myBetAmount += bet.BetAmount
- myRank = i + 1
- break
- }
- }
- }
- // 截取排名在前20名的数据
- if len(rankList) > RankCount {
- rankList = rankList[:RankCount]
- }
- return rankList, bp.Jackpot, myBetAmount, myRank
- }
- // 发奖
- func (bp *betPool) sendPrize() {
- ts := timetool.GetTimeStamp()
- weekIndex := timetool.GetWeekIndex(ts)
- bp.lock.RLock()
- var cycle = bp.cycle
- var cycleTime = bp.CycleTime
- var total = bp.Total
- var jackpot = bp.Jackpot
- bp.lock.RUnlock()
- if cycle == "Week" {
- if cycleTime == 0 || cycleTime == weekIndex {
- return
- }
- } else if cycle == "Day" {
- if cycleTime == 0 || cycleTime == time.Now().Day() {
- return
- }
- }
- // 从当前奖池中抽取20名获奖者
- winners := bp.drawWinners()
- if len(winners) == 0 {
- return
- }
- log.Debug("====%vBetPool.sendPrize CycleTime[%d] Total[%d] Jackpot[%d]====", cycle, cycleTime, total, jackpot)
- // 发奖
- for _, winner := range winners {
- log.Debug("userId=%d, rank=%d, award=%v", winner.UserId, winner.Rank, winner.Award)
- //金额为0跳过
- if len(winner.Award) == 0 {
- //打印错误日志
- log.Release("sendPrize failed Award %v", winner.Award)
- continue
- }
- var gameName = "تحدي المشاريع"
- var rankName = fmt.Sprintf("%v %v", gameName, "يوميًا")
- if cycle == "Week" {
- rankName = fmt.Sprintf("%v %v", gameName, "أسبوعي")
- }
- var mailTitle = `ممتازة في قائمة ترتيب تحدي المشاريع`
- var mailContent = fmt.Sprintf("عزيزي المستخدم،لقد حصلت على المرتبة %v في تصنيف %d، وقد حصلت على الجوائز التالية، يرجى التحقق من المرفقات.",
- rankName, winner.Rank)
- // 发送中奖通知
- go userservices.SendSysMail(winner.UserId, &userservices.SysMail{
- Id: 0,
- Title: mailTitle,
- Content: mailContent,
- Status: 0,
- SourceName: gameName,
- Crdate: timetool.GetTimeStamp(),
- Tools: winner.Award,
- })
- // 写用户纪录
- d, _ := json.Marshal(winner)
- go transaction.WriteGameRecord(winner.UserId, common.GAMEID, bp.roomInfo.RoomName, string(d))
- }
- log.Debug("--------------------------------------")
- // 清空奖池
- bp.lock.Lock()
- if cycle == "Week" {
- bp.CycleTime = weekIndex
- } else if cycle == "Day" {
- bp.CycleTime = time.Now().Day()
- }
- bp.BetList = make(map[int]int)
- bp.Total = 0
- bp.Jackpot = 0
- bp.lock.Unlock()
- //清空奖池打印日志
- log.Debug("====Refresh BetPool cycle[%v] Total[%d] Jackpot[%d]====", cycle, 0, 0)
- bp.betPoolToRedis()
- }
- // 从当前池中抽取 20 名获奖者并计算他们的奖金
- func (bp *betPool) drawWinners() []Prize {
- bp.lock.RLock()
- var cycle = bp.cycle
- var jackpot = bp.Jackpot
- if jackpot == 0 {
- log.Error("drawWinners failed jackpot is 0")
- return nil
- }
- betList, _, _, _ := bp.getRankList(0)
- bp.lock.RUnlock()
- if len(betList) == 0 {
- return nil
- }
- weekRankAward := bp.roomInfo.WeekRankAward
- winners := make([]Prize, 0, RankCount)
- // 循环投注并分配排名和奖金
- for i, bet := range betList {
- // 只取前20名
- if i >= RankCount {
- break
- }
- prize := Prize{
- Day: time.Now().Format("2006-01-02"),
- Rank: i + 1,
- UserId: bet.UserId,
- Award: nil,
- }
- // 根据排名和累积奖金计算奖金金额
- var award []item.ItemPack
- items := make(map[int]int)
- switch cycle {
- case "Week":
- //周榜从配置中读取 weekRankAward
- award = weekRankAward[i]
- case "Day":
- switch prize.Rank {
- case 1:
- items[item.Item_Gold] = jackpot * 25 / 100 // 25% of jackpot
- case 2:
- items[item.Item_Gold] = jackpot * 18 / 100 // 18% of jackpot
- case 3:
- items[item.Item_Gold] = jackpot * 13 / 100 // 13% of jackpot
- case 4:
- items[item.Item_Gold] = jackpot * 9 / 100 // 9% of jackpot
- case 5:
- items[item.Item_Gold] = jackpot * 5 / 100 // 5% of jackpot
- case 6, 7, 8, 9, 10:
- items[item.Item_Gold] = jackpot * 3 / 100 // 3% of jackpot
- case 11, 12, 13, 14, 15:
- items[item.Item_Gold] = jackpot * 2 / 100 // 2% of jackpot
- case 16, 17, 18, 19, 20:
- items[item.Item_Gold] = jackpot * 1 / 100 // 1% of jackpot
- default:
- // 如果排名不在前 20 名内,则不发放奖励
- continue
- }
- // 将 items 转换为奖励道具列表
- for itemId, count := range items {
- award = append(award, item.ItemPack{
- ItemId: itemId,
- Count: count,
- })
- }
- }
- prize.Award = award
- winners = append(winners, prize)
- }
- return winners
- }
|