/* 1.所有人的投注总额都记录起来 2.每天结算开奖,开奖后把当前数据到为一个历史奖池的数据,历史奖池只保存一份,然后发奖,清空投注总额,奖池清空,并且重新开始. 3.每小局游戏的投注总额的百分之2作为奖池,不断累加 4.第一名得到百分之50的奖池 5.第二名得到百分之22的奖池 6.第三名得到百分之10的奖池 7.第四名到第九得到百分之2的奖池 8.第十名到第十五得到百分之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/luckyfruit_table/common" "bet24.com/servers/games/luckyfruit_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 = 15 // 奖池排名奖励 type Prize struct { Day string // 奖池日期 Rank int // 名次 (1-15) UserId int // 用户ID TotalBet int // 总投注额 Amount int // 奖励金额 } type Rank struct { UserId int BetAmount int } // 奖池 每天刷新一次 type betPool struct { Day int // 奖池日期 Total int // 总投注额 Jackpot int // 奖池 (总下注额的2%) 从ts.roomInfo.JackpotRate中取 BetList map[int]int // 所有玩家的下注总额 lock *sync.RWMutex roomInfo *config.RoomInfo } func newBetPool(roomInfo *config.RoomInfo) *betPool { ret := new(betPool) ret.lock = &sync.RWMutex{} ret.BetList = make(map[int]int) ret.roomInfo = roomInfo // 从redis中获取奖池数据 key := BetPoolKey data, _ := redis.String_Get(common.GetRedisKey(key)) if data == "" { // redis中没有奖池数据 ret.Day = time.Now().Day() ret.betPoolToRedis() } else { // redis中有奖池数据 bp := &betPool{ Day: 0, BetList: make(map[int]int), Total: 0, Jackpot: 0, } err := json.Unmarshal([]byte(data), bp) if err != nil { log.Release("betPool json.Unmarshal err:%v", err) } if bp.Day != time.Now().Day() { // 奖池数据不是今天的,重新初始化 ret.Day = time.Now().Day() ret.Total = 0 ret.Jackpot = 0 log.Debug("====newBetPool.Refresh Day[%d] Total[%d] Jackpot[%d]====", bp.Day, bp.Total, bp.Jackpot) ret.betPoolToRedis() } else { // 奖池数据是今天的,直接赋值 ret.Day = bp.Day ret.BetList = bp.BetList ret.Total = bp.Total ret.Jackpot = bp.Jackpot } } //起一个定时器,每天0点发奖并且清空奖池 ret.startTimer() return ret } func (bp *betPool) startTimer() { bp.sendPrize() go time.AfterFunc(1*time.Minute, 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() { key := BetPoolKey bp.lock.RLock() data, err := json.Marshal(bp) bp.lock.RUnlock() if err != nil { log.Debug("betPool json.Marshal err:%v", 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 } } } // 截取排名在前15名的数据 if len(rankList) > RankCount { rankList = rankList[:RankCount] } return rankList, bp.Jackpot, myBetAmount, myRank } // 发奖 func (bp *betPool) sendPrize() { if bp.Day == 0 || bp.Day == time.Now().Day() { return } // 从当前奖池中抽取15名获奖者 winners := bp.drawWinners() if len(winners) == 0 { return } log.Debug("====BetPool.sendPrize Day[%d] Total[%d] Jackpot[%d] RoomName[%v]====", bp.Day, bp.Total, bp.Jackpot, bp.roomInfo.RoomName) // 发奖 for _, winner := range winners { log.Debug("userId=%d, rank=%d, amount=%d", winner.UserId, winner.Rank, winner.Amount) //金额为0跳过 if winner.Amount <= 0 { //打印错误日志 log.Release("sendPrize failed Amount<=0 %v", winner.Amount) continue } var items []item.ItemPack items = append(items, item.ItemPack{ ItemId: item.Item_Gold, Count: winner.Amount, }) var mailTitle = `ممتازة في قائمة ترتيب عجلة الحظ` var mailContent = fmt.Sprintf("تهانينا على حصولك على المركز رقم %d في قائمة ترتيب مبلغ التداول في عجلة الحظ ، والجائزة %d عملة ذهبية", winner.Rank, winner.Amount) // 发送中奖通知 go userservices.SendSysMail(winner.UserId, &userservices.SysMail{ Id: 0, Title: mailTitle, Content: mailContent, Status: 0, SourceName: "عجلة الحظ", Crdate: timetool.GetTimeStamp(), Tools: items, }) // 写用户纪录 d, _ := json.Marshal(winner) go transaction.WriteGameRecord(winner.UserId, common.GAMEID, bp.roomInfo.RoomName, string(d)) } log.Debug("--------------------------------------") // 清空奖池 bp.lock.Lock() bp.Day = time.Now().Day() bp.BetList = make(map[int]int) bp.Total = 0 bp.Jackpot = 0 bp.lock.Unlock() //清空奖池打印日志 log.Debug("====Refresh BetPool Day[%d] Total[%d] Jackpot[%d]====", bp.Day, bp.Total, bp.Jackpot) bp.betPoolToRedis() } // 从当前池中抽取 15 名获奖者并计算他们的奖金 func (bp *betPool) drawWinners() []Prize { bp.lock.RLock() betList, _, _, _ := bp.getRankList(0) var jackpot = bp.Jackpot bp.lock.RUnlock() if len(betList) == 0 { return nil } winners := make([]Prize, 0, RankCount) // 循环投注并分配排名和奖金 for i, bet := range betList { // 只取前15名 if i >= RankCount { break } prize := Prize{ Day: time.Now().Format("2006-01-02"), Rank: i + 1, UserId: bet.UserId, Amount: 0, // no prize } // 根据排名和累积奖金计算奖金金额 switch prize.Rank { case 1: prize.Amount = jackpot * 50 / 100 // 50% of jackpot case 2: prize.Amount = jackpot * 22 / 100 // 22% of jackpot case 3: prize.Amount = jackpot * 10 / 100 // 10% of jackpot case 4, 5, 6, 7, 8, 9: prize.Amount = jackpot * 2 / 100 // 2% of jackpot case 10, 11, 12, 13, 14, 15: prize.Amount = jackpot * 1 / 100 // 1% of jackpot default: prize.Amount = 0 // no prize } winners = append(winners, prize) } return winners }