package match import ( "encoding/json" "os" "sort" "sync" "time" "bet24.com/log" "bet24.com/servers/common" "bet24.com/servers/micros/guess/handler/team" pb "bet24.com/servers/micros/guess/proto" platformconfig "bet24.com/servers/micros/platformconfig/proto" ) const config_key = "guess_config" var mgr *matchmgr // 赛事 type matchmgr struct { matchList map[string]*match lock *sync.RWMutex configString string pb.GuessCfg // 竞猜配置 } func getMatchManager() *matchmgr { if mgr == nil { mgr = new(matchmgr) mgr.ctor() } return mgr } func (this *matchmgr) ctor() { mgr.matchList = make(map[string]*match) mgr.lock = &sync.RWMutex{} this.readConf() this.loadMatchList() mgr.doCheck() log.Debug("matchmgr.ctor starting ...") return } func (this *matchmgr) readConf() { time.AfterFunc(time.Second*600, this.readConf) configString := platformconfig.GetConfig(config_key) if configString == "" { data, err := os.ReadFile("serviceconf/guess_config.json") if err != nil { log.Release("matchmgr.readConf read config failed") return } configString = string(data) if configString != "" { platformconfig.SetConfig(config_key, configString) } } if configString == this.configString { return } this.configString = configString // 读取配置 err := json.Unmarshal([]byte(configString), &this.GuessCfg) if err != nil { log.Release("matchmgr.readConf Unmarshal failed %s", configString) } } // 获取赛事配置 func (this *matchmgr) getConfig() pb.GuessCfg { return this.GuessCfg } // 加载赛事列表 func (this *matchmgr) loadMatchList() { list := trans_GetMatchList() for _, sn := range list { this.getMatch(sn) } return } func (this *matchmgr) doCheck() { ticker := time.NewTicker(1 * time.Second) go func(t *time.Ticker) { for { select { case <-t.C: go this.checkMatchExpire() } } }(ticker) } // 获取赛事 func (this *matchmgr) getMatch(serialNumber string) *match { if serialNumber == "" { log.Release("matchmgr.getMatch serialNumber is invalid") return nil } this.lock.RLock() m, ok := this.matchList[serialNumber] this.lock.RUnlock() if ok { return m } m = newMatch(serialNumber) // 无效数据 if m.isInvalid() { return m } this.lock.Lock() this.matchList[serialNumber] = m this.lock.Unlock() return m } // 是否允许修改 func (this *matchmgr) isMatchUpdate(serialNumber string) bool { this.lock.RLock() m, ok := this.matchList[serialNumber] this.lock.RUnlock() if !ok { return true } // 赛事开始了,不允许修改 if m.StartAt <= common.GetTimeStamp() { return false } return true } // 刷新赛事 func (this *matchmgr) refreshMatch(serialNumber string) { this.lock.RLock() _, ok := this.matchList[serialNumber] this.lock.RUnlock() if !ok { return } // 删除 this.lock.Lock() delete(this.matchList, serialNumber) this.lock.Unlock() // 重新获取 this.getMatch(serialNumber) return } // 检查赛事 func (this *matchmgr) checkMatchExpire() { this.lock.Lock() defer this.lock.Unlock() for _, v := range this.matchList { if !v.isExpire() { continue } delete(this.matchList, v.SerialNumber) log.Debug("matchmgr.checkMatchExpire serialNumber=%s 已完成清理", v.SerialNumber) } return } // 获取赛事列表(0=所有赛事 1=展示的赛事 2=正在进行的赛事 3=结束的赛事 4=预热的赛事 func (this *matchmgr) getMatchList(userId, matchType int) []pb.Response_Match { var list []pb.Response_Match for _, v := range this.matchList { // 赛事类型 if !v.getMatchType(matchType) { continue } list = append(list, this.getMatchInfo(userId, v.SerialNumber)) } sort.SliceStable(list, func(i, j int) bool { return list[i].StartAt > list[j].StartAt }) return list } // 获取赛事信息 func (this *matchmgr) getMatchInfo(userId int, serialNumber string) pb.Response_Match { //log.Debug("getMatchInfo userId=%d serialNumber=%s", userId, serialNumber) m := this.getMatch(serialNumber) if m == nil { log.Debug("matchmgr.getMatchInfo serialNumber=%s is not exist", serialNumber) return pb.Response_Match{} } var userBets []pb.UserBet if userId > 0 { userBets = this.getUserBet(userId, m.SerialNumber) } return pb.Response_Match{ Match: m.getInfo(), UserBets: userBets, } } // 获取用户投注 func (this *matchmgr) getUserBet(userId int, serialNumber string) []pb.UserBet { m := this.getMatch(serialNumber) if m == nil { log.Debug("matchmgr.getUserBet serialNumber=%s is not exist", serialNumber) return nil } return m.getUserBet(userId) } // 投注 func (this *matchmgr) bet(serialNumber string, userId, betId, amount int, ipAddress string) (ret pb.RetMsg) { m := this.getMatch(serialNumber) if m == nil { log.Debug("matchmgr.bet serialNumber=%s is not exist", serialNumber) return } return m.bet(userId, betId, amount, ipAddress) } // 设置结果 func (this *matchmgr) setResult(serialNumber string, betId int, result string, op pb.OpUser) (ret pb.RetMsg) { m := this.getMatch(serialNumber) if m == nil { log.Debug("matchmgr.setResult serialNumber=%s is not exist", serialNumber) return } return m.setResult(betId, result, op) } // 派奖 func (this *matchmgr) award(serialNumber string, op pb.OpUser) (ret pb.RetMsg) { m := this.getMatch(serialNumber) if m == nil { log.Debug("matchmgr.award serialNumber=%s is not exist", serialNumber) return } return m.award(op, this.GuessCfg) } // 添加赛事 func (this *matchmgr) addMatch(title, startAt, endAt, showStartAt, showEndAt string, op pb.OpUser) pb.RetMsg { return trans_AddMatch(title, startAt, endAt, showStartAt, showEndAt, op) } // 修改赛事 func (this *matchmgr) updateMatch(serialNumber, title, startAt, endAt, showStartAt, showEndAt string, op pb.OpUser) pb.RetMsg { // 判断是否允许修改 if !this.isMatchUpdate(serialNumber) { return pb.RetMsg{ RetCode: 500, Message: "The event has started and cannot be modified", } } // 修改完后 defer this.refreshMatch(serialNumber) return trans_UpdateMatch(serialNumber, title, startAt, endAt, showStartAt, showEndAt, op) } // 添加赛事球队 func (this *matchmgr) addMatchTeam(serialNumber string, teamId int, op pb.OpUser) pb.RetMsg { if info := team.GetTeam(teamId); info.Id <= 0 { return pb.RetMsg{ RetCode: 501, Message: "", } } return trans_AddMatchTeam(serialNumber, teamId, op) } // 修改赛事球队 func (this *matchmgr) updateMatchTeam(rId int, serialNumber string, teamId int, op pb.OpUser) pb.RetMsg { // 判断是否允许修改 if !this.isMatchUpdate(serialNumber) { return pb.RetMsg{ RetCode: 500, Message: "The event has started and cannot be modified", } } if info := team.GetTeam(teamId); info.Id <= 0 { return pb.RetMsg{ RetCode: 501, Message: "", } } // 修改完后 defer this.refreshMatch(serialNumber) return trans_UpdateMatchTeam(rId, serialNumber, teamId, op) } // 刷新赛事球队 func (this *matchmgr) refreshMatchTeam(teamId int) { for _, m := range this.matchList { m.refreshTeam(teamId) } return } // 添加赛事投注选项 func (this *matchmgr) addMatchBet(serialNumber, betName string, betOdds float64, op pb.OpUser) pb.RetMsg { return trans_AddMatchBet(serialNumber, betName, betOdds, op) } // 修改赛事投注选项 func (this *matchmgr) updateMatchBet(betId int, serialNumber, betName string, betOdds float64, op pb.OpUser) pb.RetMsg { // 判断是否允许修改 if !this.isMatchUpdate(serialNumber) { return pb.RetMsg{ RetCode: 500, Message: "The event has started and cannot be modified", } } // 修改完后 defer this.refreshMatch(serialNumber) return trans_UpdateMatchBet(betId, serialNumber, betName, betOdds, op) } // 设置赛事开启 func (this *matchmgr) setMatchOpen(serialNumber string, status int) (ret pb.RetMsg) { // TODO:数据库处理 if retCode := trans_UpdateMatchStatus(serialNumber, status); retCode != 1 { ret.RetCode = 501 ret.Message = "Setting open failed" return } // 设置完后 this.refreshMatch(serialNumber) ret.RetCode = 1 ret.Message = "success" return } // 获取用户投注记录 func (this *matchmgr) getUserBetRecordList(userId, pageIndex, pageSize int) (int, []pb.BetRecord) { // TODO:数据库处理 recordCount, list := trans_GetUserBetRecordList(userId, "", pageIndex, pageSize) for k, v := range list { m := this.getMatch(v.SerialNumber) if m == nil { continue } list[k].Title = m.Title list[k].StartTime = m.StartAt list[k].Status = m.Status list[k].Teams = m.Teams list[k].Result = m.Result info := m.getBetInfo(v.BetId) if info.Id <= 0 { continue } list[k].BetName = info.Name } return recordCount, list } // 打印赛事 func (this *matchmgr) dumpMatch(param1 string) { var sn string if param1 != "" { sn = param1 } log.Debug(" ^_^ 开始打印赛事数据,房间(%d)个", len(this.matchList)) this.lock.RLock() for k, v := range this.matchList { if sn != "" && k != sn { continue } v.dump() } this.lock.RUnlock() log.Debug("完成赛事数据打印 ^_^") }