betrank.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /*
  2. 1.所有人的投注总额都记录起来
  3. 2.每天结算开奖,开奖后把当前数据到为一个历史奖池的数据,历史奖池只保存一份,然后发奖,清空投注总额,奖池清空,并且重新开始.
  4. 3.每小局游戏的投注总额的百分之2作为奖池,不断累加
  5. 4.第一名得到百分之50的奖池
  6. 5.第二名得到百分之22的奖池
  7. 6.第三名得到百分之10的奖池
  8. 7.第四名到第九得到百分之2的奖池
  9. 8.第十名到第十五得到百分之1的奖池
  10. */
  11. package gamelogic
  12. import (
  13. "encoding/json"
  14. "fmt"
  15. "sort"
  16. "sync"
  17. "time"
  18. "bet24.com/log"
  19. "bet24.com/redis"
  20. timetool "bet24.com/servers/common"
  21. "bet24.com/servers/games/luckyfruit_table/common"
  22. "bet24.com/servers/games/luckyfruit_table/config"
  23. item "bet24.com/servers/micros/item_inventory/proto"
  24. userservices "bet24.com/servers/micros/userservices/proto"
  25. "bet24.com/servers/transaction"
  26. )
  27. const BetPoolKey = "betPool"
  28. const RankCount = 15
  29. // 奖池排名奖励
  30. type Prize struct {
  31. Day string // 奖池日期
  32. Rank int // 名次 (1-15)
  33. UserId int // 用户ID
  34. TotalBet int // 总投注额
  35. Amount int // 奖励金额
  36. }
  37. type Rank struct {
  38. UserId int
  39. BetAmount int
  40. }
  41. // 奖池 每天刷新一次
  42. type betPool struct {
  43. Day int // 奖池日期
  44. Total int // 总投注额
  45. Jackpot int // 奖池 (总下注额的2%) 从ts.roomInfo.JackpotRate中取
  46. BetList map[int]int // 所有玩家的下注总额
  47. lock *sync.RWMutex
  48. roomInfo *config.RoomInfo
  49. }
  50. func newBetPool(roomInfo *config.RoomInfo) *betPool {
  51. ret := new(betPool)
  52. ret.lock = &sync.RWMutex{}
  53. ret.BetList = make(map[int]int)
  54. ret.roomInfo = roomInfo
  55. // 从redis中获取奖池数据
  56. key := BetPoolKey
  57. data, _ := redis.String_Get(common.GetRedisKey(key))
  58. if data == "" {
  59. // redis中没有奖池数据
  60. ret.Day = time.Now().Day()
  61. ret.betPoolToRedis()
  62. } else {
  63. // redis中有奖池数据
  64. bp := &betPool{
  65. Day: 0,
  66. BetList: make(map[int]int),
  67. Total: 0,
  68. Jackpot: 0,
  69. }
  70. err := json.Unmarshal([]byte(data), bp)
  71. if err != nil {
  72. log.Release("betPool json.Unmarshal err:%v", err)
  73. }
  74. if bp.Day != time.Now().Day() {
  75. // 奖池数据不是今天的,重新初始化
  76. ret.Day = time.Now().Day()
  77. ret.Total = 0
  78. ret.Jackpot = 0
  79. log.Debug("====newBetPool.Refresh Day[%d] Total[%d] Jackpot[%d]====", bp.Day, bp.Total, bp.Jackpot)
  80. ret.betPoolToRedis()
  81. } else {
  82. // 奖池数据是今天的,直接赋值
  83. ret.Day = bp.Day
  84. ret.BetList = bp.BetList
  85. ret.Total = bp.Total
  86. ret.Jackpot = bp.Jackpot
  87. }
  88. }
  89. //起一个定时器,每天0点发奖并且清空奖池
  90. ret.startTimer()
  91. return ret
  92. }
  93. func (bp *betPool) startTimer() {
  94. bp.sendPrize()
  95. go time.AfterFunc(1*time.Minute, bp.startTimer)
  96. }
  97. // 添加用户
  98. func (bp *betPool) addUser(userId int) {
  99. bp.lock.RLock()
  100. _, ok := bp.BetList[userId]
  101. bp.lock.RUnlock()
  102. if !ok {
  103. bp.lock.Lock()
  104. bp.BetList[userId] = 0
  105. bp.lock.Unlock()
  106. }
  107. }
  108. // 移除用户
  109. func (bp *betPool) removeUser(userId int) {
  110. bp.lock.Lock()
  111. delete(bp.BetList, userId)
  112. bp.lock.Unlock()
  113. // log.Release("betPool.removeUser %d not found", userId)
  114. }
  115. // 是否上榜
  116. func (bp *betPool) isRank(userId int) bool {
  117. _, _, _, myRank := bp.getRankList(userId)
  118. return myRank <= RankCount
  119. }
  120. // 每次结算时 将投注数据保存到历史奖池中
  121. func (bp *betPool) addBetPool(userId int, betAmount int) {
  122. bp.lock.RLock()
  123. data, ok := bp.BetList[userId]
  124. bp.lock.RUnlock()
  125. // 检查用户是否已经投注过
  126. if ok {
  127. bp.lock.Lock()
  128. data += betAmount
  129. bp.BetList[userId] = data
  130. bp.lock.Unlock()
  131. } else {
  132. bp.lock.Lock()
  133. bp.BetList[userId] = betAmount
  134. // log.Release("addBetPool failed bp.BetList is nil userId:%v,betAmount:%d", userId, betAmount)
  135. bp.lock.Unlock()
  136. }
  137. // 更新奖池总金额和奖池金额
  138. bp.lock.Lock()
  139. bp.Total += betAmount
  140. bp.Jackpot += betAmount * bp.roomInfo.JackpotRate / 100
  141. bp.lock.Unlock()
  142. }
  143. func (bp *betPool) betPoolToRedis() {
  144. key := BetPoolKey
  145. bp.lock.RLock()
  146. data, err := json.Marshal(bp)
  147. bp.lock.RUnlock()
  148. if err != nil {
  149. log.Debug("betPool json.Marshal err:%v", err)
  150. return
  151. }
  152. redis.String_Set(common.GetRedisKey(key), string(data))
  153. }
  154. // 获取排名列表
  155. func (bp *betPool) getRankList(userId int) ([]Rank, int, int, int) {
  156. // 统计每个用户的投注金额
  157. var myBetAmount = 0
  158. var myRank = RankCount
  159. var rankList []Rank
  160. bp.lock.RLock()
  161. for k, v := range bp.BetList {
  162. rankList = append(rankList, Rank{k, v})
  163. }
  164. bp.lock.RUnlock()
  165. if len(rankList) == 0 {
  166. return rankList, bp.Jackpot, myBetAmount, myRank
  167. }
  168. // 将投注金额从大到小排序
  169. sort.Slice(rankList, func(i, j int) bool {
  170. return rankList[i].BetAmount > rankList[j].BetAmount
  171. })
  172. // 查询自己的下注额度和排名
  173. if userId > 0 {
  174. for i, bet := range rankList {
  175. if bet.UserId == userId {
  176. myBetAmount += bet.BetAmount
  177. myRank = i + 1
  178. break
  179. }
  180. }
  181. }
  182. // 截取排名在前15名的数据
  183. if len(rankList) > RankCount {
  184. rankList = rankList[:RankCount]
  185. }
  186. return rankList, bp.Jackpot, myBetAmount, myRank
  187. }
  188. // 发奖
  189. func (bp *betPool) sendPrize() {
  190. if bp.Day == 0 || bp.Day == time.Now().Day() {
  191. return
  192. }
  193. // 从当前奖池中抽取15名获奖者
  194. winners := bp.drawWinners()
  195. if len(winners) == 0 {
  196. return
  197. }
  198. log.Debug("====BetPool.sendPrize Day[%d] Total[%d] Jackpot[%d] RoomName[%v]====", bp.Day, bp.Total, bp.Jackpot, bp.roomInfo.RoomName)
  199. // 发奖
  200. for _, winner := range winners {
  201. log.Debug("userId=%d, rank=%d, amount=%d", winner.UserId, winner.Rank, winner.Amount)
  202. //金额为0跳过
  203. if winner.Amount <= 0 {
  204. //打印错误日志
  205. log.Release("sendPrize failed Amount<=0 %v", winner.Amount)
  206. continue
  207. }
  208. var items []item.ItemPack
  209. items = append(items, item.ItemPack{
  210. ItemId: item.Item_Gold,
  211. Count: winner.Amount,
  212. })
  213. var mailTitle = `ممتازة في قائمة ترتيب عجلة الحظ`
  214. var mailContent = fmt.Sprintf("تهانينا على حصولك على المركز رقم %d في قائمة ترتيب مبلغ التداول في عجلة الحظ ، والجائزة %d عملة ذهبية",
  215. winner.Rank, winner.Amount)
  216. // 发送中奖通知
  217. go userservices.SendSysMail(winner.UserId, &userservices.SysMail{
  218. Id: 0,
  219. Title: mailTitle,
  220. Content: mailContent,
  221. Status: 0,
  222. SourceName: "عجلة الحظ",
  223. Crdate: timetool.GetTimeStamp(),
  224. Tools: items,
  225. })
  226. // 写用户纪录
  227. d, _ := json.Marshal(winner)
  228. go transaction.WriteGameRecord(winner.UserId, common.GAMEID, bp.roomInfo.RoomName, string(d))
  229. }
  230. log.Debug("--------------------------------------")
  231. // 清空奖池
  232. bp.lock.Lock()
  233. bp.Day = time.Now().Day()
  234. bp.BetList = make(map[int]int)
  235. bp.Total = 0
  236. bp.Jackpot = 0
  237. bp.lock.Unlock()
  238. //清空奖池打印日志
  239. log.Debug("====Refresh BetPool Day[%d] Total[%d] Jackpot[%d]====", bp.Day, bp.Total, bp.Jackpot)
  240. bp.betPoolToRedis()
  241. }
  242. // 从当前池中抽取 15 名获奖者并计算他们的奖金
  243. func (bp *betPool) drawWinners() []Prize {
  244. bp.lock.RLock()
  245. betList, _, _, _ := bp.getRankList(0)
  246. var jackpot = bp.Jackpot
  247. bp.lock.RUnlock()
  248. if len(betList) == 0 {
  249. return nil
  250. }
  251. winners := make([]Prize, 0, RankCount)
  252. // 循环投注并分配排名和奖金
  253. for i, bet := range betList {
  254. // 只取前15名
  255. if i >= RankCount {
  256. break
  257. }
  258. prize := Prize{
  259. Day: time.Now().Format("2006-01-02"),
  260. Rank: i + 1,
  261. UserId: bet.UserId,
  262. Amount: 0, // no prize
  263. }
  264. // 根据排名和累积奖金计算奖金金额
  265. switch prize.Rank {
  266. case 1:
  267. prize.Amount = jackpot * 50 / 100 // 50% of jackpot
  268. case 2:
  269. prize.Amount = jackpot * 22 / 100 // 22% of jackpot
  270. case 3:
  271. prize.Amount = jackpot * 10 / 100 // 10% of jackpot
  272. case 4, 5, 6, 7, 8, 9:
  273. prize.Amount = jackpot * 2 / 100 // 2% of jackpot
  274. case 10, 11, 12, 13, 14, 15:
  275. prize.Amount = jackpot * 1 / 100 // 1% of jackpot
  276. default:
  277. prize.Amount = 0 // no prize
  278. }
  279. winners = append(winners, prize)
  280. }
  281. return winners
  282. }