match.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. package match
  2. import (
  3. "sync"
  4. "time"
  5. "bet24.com/log"
  6. "bet24.com/servers/common"
  7. "bet24.com/servers/micros/guess/handler/team"
  8. pb "bet24.com/servers/micros/guess/proto"
  9. item "bet24.com/servers/micros/item_inventory/proto"
  10. money "bet24.com/servers/micros/money/proto"
  11. mail "bet24.com/servers/micros/userservices/proto"
  12. )
  13. const idle_seconds = 24 * 60 * 60 * 30 // 过期时长30天(秒)
  14. // 赛事
  15. type match struct {
  16. pb.Match
  17. lock *sync.RWMutex
  18. user_list map[int][]pb.UserBet
  19. endChan chan int
  20. }
  21. func newMatch(serialNumber string) *match {
  22. ret := new(match)
  23. ret.endChan = make(chan int)
  24. ret.lock = &sync.RWMutex{}
  25. ret.user_list = make(map[int][]pb.UserBet)
  26. ret.SerialNumber = serialNumber
  27. ret.loadData()
  28. ret.doCheck()
  29. return ret
  30. }
  31. func (this *match) loadData() {
  32. // 赛事信息
  33. m := trans_GetMatchInfo(this.SerialNumber)
  34. if m.SerialNumber == "" {
  35. log.Release("match.loadData GetMatchInfo serialNumber=%s is not exist", this.SerialNumber)
  36. return
  37. }
  38. // 球队
  39. teams := trans_GetMatchTeamList(m.SerialNumber)
  40. if teams == nil {
  41. log.Release("match.loadData GetMatchTeamList serialNumber=%s is empty", m.SerialNumber)
  42. return
  43. }
  44. // 球队信息
  45. for _, t := range teams {
  46. teamInfo := team.GetTeam(t.Id)
  47. teamInfo.Rid = t.Rid
  48. m.Teams = append(m.Teams, teamInfo)
  49. }
  50. // 投注信息
  51. m.Bets = trans_GetMatchBetList(m.SerialNumber)
  52. if m.Bets == nil {
  53. log.Release("match.loadData GetMatchBetList serialNumber=%s is empty", m.SerialNumber)
  54. return
  55. }
  56. this.Match = m
  57. // 获取用户投注
  58. user_list := trans_GetUserBetAmount(this.SerialNumber)
  59. this.lock.Lock()
  60. this.user_list = user_list
  61. this.lock.Unlock()
  62. }
  63. // 轮询检查
  64. func (this *match) doCheck() {
  65. ticker := time.NewTicker(1 * time.Second)
  66. go func(t *time.Ticker) {
  67. for {
  68. select {
  69. case <-this.endChan:
  70. t.Stop()
  71. return
  72. case <-t.C:
  73. this.checkEnd()
  74. }
  75. }
  76. }(ticker)
  77. }
  78. // 检查是否结束状态
  79. func (this *match) checkEnd() {
  80. //defer func() {
  81. // log.Debug("match.checkStatus %+v", this.Match)
  82. //}()
  83. // 非开启状态
  84. if this.Status != pb.MatchStatus_Open {
  85. close(this.endChan)
  86. return
  87. }
  88. // 还没结束
  89. if this.EndAt > common.GetTimeStamp() {
  90. return
  91. }
  92. // 修改结束状态
  93. this.Status = pb.MatchStatus_End
  94. // TODO:写入数据库
  95. go trans_UpdateMatchStatus(this.SerialNumber, this.Status)
  96. return
  97. }
  98. // 是否过期
  99. func (this *match) isExpire() bool {
  100. return this.EndAt+idle_seconds < common.GetTimeStamp()
  101. }
  102. // 是否游戏无效
  103. func (this *match) isInvalid() bool {
  104. return this.Status == pb.MatchStatus_Invalid
  105. }
  106. // 获取赛事
  107. func (this *match) getInfo() pb.Match {
  108. return this.Match
  109. }
  110. // 赛事类型(0=所有赛事 1=展示的赛事 2=正在进行的赛事 3=结束的赛事 4=预热的赛事
  111. func (this *match) getMatchType(matchType int) bool {
  112. // 无效状态
  113. if this.Status <= pb.MatchStatus_Invalid || this.Status >= pb.MatchStatus_Max {
  114. return false
  115. }
  116. ts := common.GetTimeStamp()
  117. switch matchType {
  118. case pb.MatchType_All: // 0=所有赛事
  119. return true
  120. case pb.MatchType_Show: // 1=展示的赛事
  121. if this.ShowStartAt <= ts && this.ShowEndAt >= ts {
  122. return true
  123. }
  124. case pb.MatchType_Bet: // 2=正在进行的赛事
  125. if this.ShowStartAt <= ts && this.ShowEndAt >= ts && this.StartAt < ts && this.EndAt >= ts {
  126. return true
  127. }
  128. case pb.MatchType_End: // 3=结束的赛事
  129. if this.Status == pb.MatchStatus_End || this.Status == pb.MatchStatus_Award {
  130. return true
  131. }
  132. case pb.MatchType_Preheat: // 4=预热的赛事
  133. if this.Status == pb.MatchStatus_Open && this.ShowStartAt > ts && this.StartAt > ts {
  134. return true
  135. }
  136. }
  137. return false
  138. }
  139. // 投注
  140. func (this *match) bet(userId, betId, amount int, ipAddress string) (ret pb.RetMsg) {
  141. // 先扣款
  142. if ok := money.ReduceMoney(userId, amount, common.LOGTYPE_GUESS_BET, "竞猜", "投注", ipAddress); !ok {
  143. ret.RetCode = 501
  144. ret.Message = "Insufficient gold coins, deduction failed"
  145. return
  146. }
  147. // 投注总额
  148. for i := 0; i < len(this.Match.Bets); i++ {
  149. if this.Match.Bets[i].Id != betId {
  150. continue
  151. }
  152. this.Match.Bets[i].Amount += amount
  153. // TODO: 投注记录
  154. go trans_AddBetRecord(userId, this.SerialNumber, betId, amount)
  155. // TODO:更新投注金额
  156. go trans_UpdateBetAmount(this.SerialNumber, betId, this.Match.Bets[i].Amount)
  157. break
  158. }
  159. // 添加用户投注
  160. this.addUserBet(userId, betId, amount)
  161. ret.RetCode = 1
  162. ret.Message = "success"
  163. return
  164. }
  165. // 添加用户投注
  166. func (this *match) addUserBet(userId, betId, amount int) {
  167. this.lock.RLock()
  168. bets, ok := this.user_list[userId]
  169. this.lock.RUnlock()
  170. if ok {
  171. var isExist bool
  172. for k, v := range bets {
  173. if v.BetId != betId {
  174. continue
  175. }
  176. isExist = true
  177. this.user_list[userId][k].BetAmount += amount
  178. break
  179. }
  180. if !isExist {
  181. this.user_list[userId] = append(this.user_list[userId], pb.UserBet{
  182. BetId: betId,
  183. BetAmount: amount,
  184. })
  185. }
  186. return
  187. }
  188. var list []pb.UserBet
  189. list = append(list, pb.UserBet{
  190. BetId: betId,
  191. BetAmount: amount,
  192. })
  193. this.lock.Lock()
  194. this.user_list[userId] = list
  195. this.lock.Unlock()
  196. return
  197. }
  198. // 获取用户投注
  199. func (this *match) getUserBet(userId int) []pb.UserBet {
  200. this.lock.RLock()
  201. bets, ok := this.user_list[userId]
  202. this.lock.RUnlock()
  203. if ok {
  204. return bets
  205. }
  206. return nil
  207. }
  208. // 设置结果
  209. func (this *match) setResult(betId int, result string, op pb.OpUser) (ret pb.RetMsg) {
  210. // 非结束状态
  211. if this.Status != pb.MatchStatus_End {
  212. ret.RetCode = 501
  213. ret.Message = "Unable to set result for non ending state"
  214. return
  215. }
  216. this.Result = result
  217. for i := 0; i < len(this.Bets); i++ {
  218. if this.Bets[i].Id != betId {
  219. continue
  220. }
  221. this.Bets[i].IsWin = true
  222. break
  223. }
  224. // TODO:存入数据库
  225. go trans_SetMatchResult(this.SerialNumber, betId, result, op)
  226. ret.RetCode = 1
  227. ret.Message = "success"
  228. return
  229. }
  230. // 派奖
  231. func (this *match) award(op pb.OpUser, cfg pb.GuessCfg) (ret pb.RetMsg) {
  232. // 非结束状态
  233. if this.Status != pb.MatchStatus_End {
  234. ret.RetCode = 501
  235. ret.Message = "Unable to distribute prizes until the end state"
  236. return
  237. }
  238. var bet pb.Bet
  239. for _, v := range this.Bets {
  240. if !v.IsWin {
  241. continue
  242. }
  243. bet = v
  244. break
  245. }
  246. // 投注项无效
  247. if bet.Id <= 0 {
  248. log.Release("match.award %+v is invalid", this.Match)
  249. ret.RetCode = 502
  250. ret.Message = "Invalid bonus betting item"
  251. return
  252. }
  253. // 派奖状态
  254. this.Status = pb.MatchStatus_Award
  255. // 设置为派奖状态
  256. if retCode := trans_UpdateMatchStatus(this.SerialNumber, this.Status); retCode != 1 {
  257. log.Release("match.award %+v is award", this.Match)
  258. ret.RetCode = 503
  259. ret.Message = "Rewards have been issued and cannot be issued repeatedly"
  260. return
  261. }
  262. // 获取当前赛事相应的投注id
  263. list := trans_GetBetSettleList(this.SerialNumber, bet.Id)
  264. users := make(map[int]struct{}, 0)
  265. for i := 0; i < len(list); i++ {
  266. // 已派过奖
  267. if list[i].ResultAmount > 0 {
  268. continue
  269. }
  270. // 计算派奖金额
  271. amount := int(float64(list[i].BetAmount) * bet.Odds)
  272. if amount <= 0 {
  273. continue
  274. }
  275. // 派奖金额
  276. list[i].ResultAmount = amount
  277. // TODO:存入数据库
  278. go trans_BetRecordAward(list[i].Rid, amount, op)
  279. var items []item.ItemPack
  280. items = append(items, item.ItemPack{
  281. ItemId: item.Item_Gold,
  282. Count: amount,
  283. })
  284. // 给道具
  285. item.AddItems(list[i].UserId, items, "竞猜派奖", common.LOGTYPE_GUESS_AWARD)
  286. users[list[i].UserId] = struct{}{}
  287. }
  288. for k := range users {
  289. // TODO:发送邮件通知
  290. go mail.SendSysMail(k, &mail.SysMail{
  291. Id: 0,
  292. Title: cfg.MailTitle,
  293. Content: cfg.MailContent,
  294. Status: 0,
  295. SourceName: cfg.MailSource,
  296. Crdate: common.GetTimeStamp(),
  297. Tools: nil,
  298. })
  299. }
  300. ret.RetCode = 1
  301. ret.Message = "success"
  302. return
  303. }
  304. // 获取投注选项
  305. func (this *match) getBetInfo(betId int) pb.Bet {
  306. for _, v := range this.Bets {
  307. if v.Id == betId {
  308. return v
  309. }
  310. }
  311. return pb.Bet{}
  312. }
  313. // 刷新球队信息
  314. func (this *match) refreshTeam(teamId int) {
  315. for i := 0; i < len(this.Teams); i++ {
  316. if this.Teams[i].Id != teamId {
  317. continue
  318. }
  319. teamInfo := team.GetTeam(teamId)
  320. this.Teams[i].Name = teamInfo.Name
  321. this.Teams[i].ShortName = teamInfo.ShortName
  322. this.Teams[i].Icon = teamInfo.Icon
  323. }
  324. }
  325. func (this *match) dump() {
  326. log.Debug("%+v", this.Match)
  327. log.Debug(" 投注信息:")
  328. for k, v := range this.user_list {
  329. log.Debug(" userId=%d ==> %+v", k, v)
  330. }
  331. }