package match import ( "sync" "time" "bet24.com/log" "bet24.com/servers/common" "bet24.com/servers/micros/guess/handler/team" pb "bet24.com/servers/micros/guess/proto" item "bet24.com/servers/micros/item_inventory/proto" money "bet24.com/servers/micros/money/proto" mail "bet24.com/servers/micros/userservices/proto" ) const idle_seconds = 24 * 60 * 60 * 30 // 过期时长30天(秒) // 赛事 type match struct { pb.Match lock *sync.RWMutex user_list map[int][]pb.UserBet endChan chan int } func newMatch(serialNumber string) *match { ret := new(match) ret.endChan = make(chan int) ret.lock = &sync.RWMutex{} ret.user_list = make(map[int][]pb.UserBet) ret.SerialNumber = serialNumber ret.loadData() ret.doCheck() return ret } func (this *match) loadData() { // 赛事信息 m := trans_GetMatchInfo(this.SerialNumber) if m.SerialNumber == "" { log.Release("match.loadData GetMatchInfo serialNumber=%s is not exist", this.SerialNumber) return } // 球队 teams := trans_GetMatchTeamList(m.SerialNumber) if teams == nil { log.Release("match.loadData GetMatchTeamList serialNumber=%s is empty", m.SerialNumber) return } // 球队信息 for _, t := range teams { teamInfo := team.GetTeam(t.Id) teamInfo.Rid = t.Rid m.Teams = append(m.Teams, teamInfo) } // 投注信息 m.Bets = trans_GetMatchBetList(m.SerialNumber) if m.Bets == nil { log.Release("match.loadData GetMatchBetList serialNumber=%s is empty", m.SerialNumber) return } this.Match = m // 获取用户投注 user_list := trans_GetUserBetAmount(this.SerialNumber) this.lock.Lock() this.user_list = user_list this.lock.Unlock() } // 轮询检查 func (this *match) doCheck() { ticker := time.NewTicker(1 * time.Second) go func(t *time.Ticker) { for { select { case <-this.endChan: t.Stop() return case <-t.C: this.checkEnd() } } }(ticker) } // 检查是否结束状态 func (this *match) checkEnd() { //defer func() { // log.Debug("match.checkStatus %+v", this.Match) //}() // 非开启状态 if this.Status != pb.MatchStatus_Open { close(this.endChan) return } // 还没结束 if this.EndAt > common.GetTimeStamp() { return } // 修改结束状态 this.Status = pb.MatchStatus_End // TODO:写入数据库 go trans_UpdateMatchStatus(this.SerialNumber, this.Status) return } // 是否过期 func (this *match) isExpire() bool { return this.EndAt+idle_seconds < common.GetTimeStamp() } // 是否游戏无效 func (this *match) isInvalid() bool { return this.Status == pb.MatchStatus_Invalid } // 获取赛事 func (this *match) getInfo() pb.Match { return this.Match } // 赛事类型(0=所有赛事 1=展示的赛事 2=正在进行的赛事 3=结束的赛事 4=预热的赛事 func (this *match) getMatchType(matchType int) bool { // 无效状态 if this.Status <= pb.MatchStatus_Invalid || this.Status >= pb.MatchStatus_Max { return false } ts := common.GetTimeStamp() switch matchType { case pb.MatchType_All: // 0=所有赛事 return true case pb.MatchType_Show: // 1=展示的赛事 if this.ShowStartAt <= ts && this.ShowEndAt >= ts { return true } case pb.MatchType_Bet: // 2=正在进行的赛事 if this.ShowStartAt <= ts && this.ShowEndAt >= ts && this.StartAt < ts && this.EndAt >= ts { return true } case pb.MatchType_End: // 3=结束的赛事 if this.Status == pb.MatchStatus_End || this.Status == pb.MatchStatus_Award { return true } case pb.MatchType_Preheat: // 4=预热的赛事 if this.Status == pb.MatchStatus_Open && this.ShowStartAt > ts && this.StartAt > ts { return true } } return false } // 投注 func (this *match) bet(userId, betId, amount int, ipAddress string) (ret pb.RetMsg) { // 先扣款 if ok := money.ReduceMoney(userId, amount, common.LOGTYPE_GUESS_BET, "竞猜", "投注", ipAddress); !ok { ret.RetCode = 501 ret.Message = "Insufficient gold coins, deduction failed" return } // 投注总额 for i := 0; i < len(this.Match.Bets); i++ { if this.Match.Bets[i].Id != betId { continue } this.Match.Bets[i].Amount += amount // TODO: 投注记录 go trans_AddBetRecord(userId, this.SerialNumber, betId, amount) // TODO:更新投注金额 go trans_UpdateBetAmount(this.SerialNumber, betId, this.Match.Bets[i].Amount) break } // 添加用户投注 this.addUserBet(userId, betId, amount) ret.RetCode = 1 ret.Message = "success" return } // 添加用户投注 func (this *match) addUserBet(userId, betId, amount int) { this.lock.RLock() bets, ok := this.user_list[userId] this.lock.RUnlock() if ok { var isExist bool for k, v := range bets { if v.BetId != betId { continue } isExist = true this.user_list[userId][k].BetAmount += amount break } if !isExist { this.user_list[userId] = append(this.user_list[userId], pb.UserBet{ BetId: betId, BetAmount: amount, }) } return } var list []pb.UserBet list = append(list, pb.UserBet{ BetId: betId, BetAmount: amount, }) this.lock.Lock() this.user_list[userId] = list this.lock.Unlock() return } // 获取用户投注 func (this *match) getUserBet(userId int) []pb.UserBet { this.lock.RLock() bets, ok := this.user_list[userId] this.lock.RUnlock() if ok { return bets } return nil } // 设置结果 func (this *match) setResult(betId int, result string, op pb.OpUser) (ret pb.RetMsg) { // 非结束状态 if this.Status != pb.MatchStatus_End { ret.RetCode = 501 ret.Message = "Unable to set result for non ending state" return } this.Result = result for i := 0; i < len(this.Bets); i++ { if this.Bets[i].Id != betId { continue } this.Bets[i].IsWin = true break } // TODO:存入数据库 go trans_SetMatchResult(this.SerialNumber, betId, result, op) ret.RetCode = 1 ret.Message = "success" return } // 派奖 func (this *match) award(op pb.OpUser, cfg pb.GuessCfg) (ret pb.RetMsg) { // 非结束状态 if this.Status != pb.MatchStatus_End { ret.RetCode = 501 ret.Message = "Unable to distribute prizes until the end state" return } var bet pb.Bet for _, v := range this.Bets { if !v.IsWin { continue } bet = v break } // 投注项无效 if bet.Id <= 0 { log.Release("match.award %+v is invalid", this.Match) ret.RetCode = 502 ret.Message = "Invalid bonus betting item" return } // 派奖状态 this.Status = pb.MatchStatus_Award // 设置为派奖状态 if retCode := trans_UpdateMatchStatus(this.SerialNumber, this.Status); retCode != 1 { log.Release("match.award %+v is award", this.Match) ret.RetCode = 503 ret.Message = "Rewards have been issued and cannot be issued repeatedly" return } // 获取当前赛事相应的投注id list := trans_GetBetSettleList(this.SerialNumber, bet.Id) users := make(map[int]struct{}, 0) for i := 0; i < len(list); i++ { // 已派过奖 if list[i].ResultAmount > 0 { continue } // 计算派奖金额 amount := int(float64(list[i].BetAmount) * bet.Odds) if amount <= 0 { continue } // 派奖金额 list[i].ResultAmount = amount // TODO:存入数据库 go trans_BetRecordAward(list[i].Rid, amount, op) var items []item.ItemPack items = append(items, item.ItemPack{ ItemId: item.Item_Gold, Count: amount, }) // 给道具 item.AddItems(list[i].UserId, items, "竞猜派奖", common.LOGTYPE_GUESS_AWARD) users[list[i].UserId] = struct{}{} } for k := range users { // TODO:发送邮件通知 go mail.SendSysMail(k, &mail.SysMail{ Id: 0, Title: cfg.MailTitle, Content: cfg.MailContent, Status: 0, SourceName: cfg.MailSource, Crdate: common.GetTimeStamp(), Tools: nil, }) } ret.RetCode = 1 ret.Message = "success" return } // 获取投注选项 func (this *match) getBetInfo(betId int) pb.Bet { for _, v := range this.Bets { if v.Id == betId { return v } } return pb.Bet{} } // 刷新球队信息 func (this *match) refreshTeam(teamId int) { for i := 0; i < len(this.Teams); i++ { if this.Teams[i].Id != teamId { continue } teamInfo := team.GetTeam(teamId) this.Teams[i].Name = teamInfo.Name this.Teams[i].ShortName = teamInfo.ShortName this.Teams[i].Icon = teamInfo.Icon } } func (this *match) dump() { log.Debug("%+v", this.Match) log.Debug(" 投注信息:") for k, v := range this.user_list { log.Debug(" userId=%d ==> %+v", k, v) } }