package sociaty import ( "context" "errors" "fmt" "go_dreamfactory/comm" "go_dreamfactory/lego/core" "go_dreamfactory/lego/sys/log" rn "go_dreamfactory/lego/sys/redis" "go_dreamfactory/lego/sys/redis/pipe" "go_dreamfactory/modules" "go_dreamfactory/pb" "go_dreamfactory/sys/configure" "go_dreamfactory/utils" "sort" "time" "github.com/go-redis/redis/v8" "go.mongodb.org/mongo-driver/mongo" ) var ( BOSS_SPORTS = "sports" BOSS_PERSONAL_RANK = "personalrank" //个人排行key BOSS_SOCIATY_RANK = "sociatyrank" //公会排名 SPORTS_HOUR = 3 * 24 //赛季周期 ) type ModelSociatyBoss struct { modules.MCompModel moduleSociaty *Sociaty service core.IService } func (this *ModelSociatyBoss) Init(service core.IService, module core.IModule, comp core.IModuleComp, options core.IModuleOptions) (err error) { this.TableName = comm.TableSociatyBoss err = this.MCompModel.Init(service, module, comp, options) this.moduleSociaty = module.(*Sociaty) this.service = service return } // 初始化赛季 func (s *ModelSociatyBoss) initSports() error { if !s.moduleSociaty.IsCross() { return nil } ticker := time.NewTicker(time.Second) sports := &pb.DBSociatyBossSports{} if err := s.Get(BOSS_SPORTS, sports); err != nil { if err == rn.RedisNil || err == mongo.ErrNoDocuments { sports.EndTime, sports.SettlementTime = s.sportsTime() if err2 := s.Add(BOSS_SPORTS, sports); err2 != nil { return err2 } } else { s.moduleSociaty.Errorln(err) return err } } go func() { for { select { case <-ticker.C: now := configure.Now() //触发结算 if now.Unix() >= sports.SettlementTime { _, sports.SettlementTime = s.sportsTime() s.settlement() s.moduleSociaty.Debugln("更新赛季结算时间:" + utils.FormatStr(sports.SettlementTime, "")) } if now.Unix() >= sports.EndTime { sports.EndTime, sports.SettlementTime = s.sportsTime() //归档前赛季数据 s.resetSportsData() // 更新下一赛季周期结束时间 update := map[string]interface{}{ "endTime": sports.EndTime, "settlementTime": sports.SettlementTime, "uids": []string{}, } if err := s.Change(BOSS_SPORTS, update); err != nil { s.moduleSociaty.Errorln("Failed to change") return } s.moduleSociaty.Debugln("更新赛季周期时间:" + utils.FormatStr(sports.EndTime, "")) } } } }() return nil } // 获取赛季数据 func (s *ModelSociatyBoss) getSociatyBossSports() *pb.DBSociatyBossSports { sports := &pb.DBSociatyBossSports{} if err := s.Get(BOSS_SPORTS, sports); err != nil { s.moduleSociaty.Errorln(err) return nil } return sports } // 更新赛季数据 func (s *ModelSociatyBoss) updateSociatyBossSports(data map[string]interface{}) { if err := s.Change(BOSS_SPORTS, data); err != nil { s.moduleSociaty.Error("更新赛季数据", log.Field{Key: "err", Value: err.Error()}) } } // 赛季结算和结束时间 func (s *ModelSociatyBoss) sportsTime() (t1, t2 int64) { now := configure.Now() end1 := now.Add(time.Duration(SPORTS_HOUR) * time.Hour) //测试时单位分钟 else Hour h, _ := time.ParseDuration("-1h") //测试时-2m else -1h end2 := end1.Add(h) return end1.Unix(), end2.Unix() } // 公会BOSS赛季是否开始 func (s *ModelSociatyBoss) sportsIsStarted() bool { sports := s.getSociatyBossSports() if sports == nil { return false } if sports.EndTime == 0 { return false } else { now := configure.Now().Unix() end := sports.SettlementTime if now < end { return true } } return false } // 公会BOSS赛季是否结束 func (s *ModelSociatyBoss) sportsIsFinished() bool { sports := s.getSociatyBossSports() if sports == nil { return false } if sports.EndTime == 0 { return true } else { now := configure.Now().Unix() end := sports.SettlementTime if now > end && now < sports.EndTime { return true } } return false } // 设置阵容 func (s *ModelSociatyBoss) setFormation(sociaty *pb.DBSociaty, uid string, teams map[int32]*pb.ChallengeTeam) error { if !s.moduleSociaty.modelSociaty.isMember(uid, sociaty) { return comm.NewCustomError(pb.ErrorCode_SociatyBelongTo) } for _, m := range sociaty.Members { if m.Uid == uid { m.Teams = teams } } update := map[string]interface{}{ "members": sociaty.Members, } return s.moduleSociaty.modelSociaty.updateSociaty(sociaty.Id, update) } // 挑战开始 func (s *ModelSociatyBoss) challengestart(session comm.IUserSession, sociaty *pb.DBSociaty) error { //组装阵容数据 var formations []*pb.BattleFormation m := s.moduleSociaty.modelSociaty.getMemberInfo(sociaty, session.GetUserId()) for _, v := range m.Teams { formations = append(formations, v.Formation) } if len(formations) == 0 { return comm.NewCustomError(pb.ErrorCode_SociatyNoFormation) } iBattle, err := s.service.GetModule(comm.ModuleBattle) if err != nil { return err } if b, y := iBattle.(comm.IBattle); y { if code, _ := b.CreatePvbBattle(session, &pb.BattlePVBReq{ Ptype: pb.PlayType_sociaty, Title: "公会BOSS", Format: formations, }); code != nil { return comm.NewCustomError(pb.ErrorCode_ExternalModule) } } return nil } // 挑战结束 func (s *ModelSociatyBoss) challengefinish(sociaty *pb.DBSociaty, uid string, report *pb.BattleReport) error { // BOSS无伤害 if report.Harm == 0 { return nil } sm := s.moduleSociaty.modelSociaty.getMemberInfo(sociaty, uid) if sm == nil { s.moduleSociaty.Error("没有该成员", log.Field{Key: "uid", Value: uid}, log.Field{Key: "sociatyId", Value: sociaty.Id}) return comm.NewCustomError(pb.ErrorCode_SociatyNoMember) } //保存挑战记录 record := &pb.ChallengeRecord{ Teams: sm.Teams, Integral: s.transIntegral(report.Harm), Duration: report.Costtime, Rtime: configure.Now().Unix(), } if err := s.updateChallengeRecord(uid, sociaty.Id, record); err != nil { return err } s.addSociatyBossUser(uid) return nil } // 添加参赛玩家 func (s *ModelSociatyBoss) addSociatyBossUser(uid string) { sports := s.getSociatyBossSports() if sports == nil { return } if len(sports.Uids) == 0 { sports.Uids = append(sports.Uids, uid) } else { if _, ok := utils.Findx(sports.Uids, uid); !ok { sports.Uids = append(sports.Uids, uid) } } update := map[string]interface{}{ "uids": sports.Uids, } if err := s.Change(BOSS_SPORTS, update); err != nil { s.moduleSociaty.Error("添加参赛玩家", log.Field{Key: "err", Value: err.Error()}) } } // 获取玩家赛季挑战记录 func (s *ModelSociatyBoss) getChallengeRecord(uid string) *pb.DBSociatyBossRecord { his := &pb.DBSociatyBossRecord{} if err := s.Get(uid, his); err != nil { return nil } if his.Uid == "" { return nil } return his } // 挑战记录 作为阵容推荐的依据; func (s *ModelSociatyBoss) updateChallengeRecord(uid, sociatyId string, record *pb.ChallengeRecord) error { his := &pb.DBSociatyBossRecord{} if err := s.Get(uid, his); err != nil { if err == rn.RedisNil || err == mongo.ErrNoDocuments { if err := s.Add(uid, &pb.DBSociatyBossRecord{ Uid: uid, SociatyId: sociatyId, Record: []*pb.ChallengeRecord{record}, }); err != nil { return comm.NewCustomError(pb.ErrorCode_DBError) } } else { return comm.NewCustomError(pb.ErrorCode_DBError) } } //更新挑战记录 his.Record = append(his.Record, record) update := map[string]interface{}{ "record": his.Record, } if err := s.Change(uid, update); err != nil { return comm.NewCustomError(pb.ErrorCode_DBError) } return nil } // 记入个人排行 func (s *ModelSociatyBoss) addPersonalRank(uid string, integral int64) error { var ( pipe *pipe.RedisPipe = s.Redis.RedisPipe(context.TODO()) m *redis.Z ) m = &redis.Z{Score: float64(integral), Member: uid} if cmd := pipe.ZAdd(fmt.Sprintf("%s:%s", s.TableName, BOSS_PERSONAL_RANK), m); cmd != nil { _, err := cmd.Result() if err != nil { return err } } if _, err := pipe.Exec(); err != nil { return err } return nil } // 记入公会排行 func (s *ModelSociatyBoss) addSociatyRank(uid string, integral int64) error { dbr := s.getChallengeRecord(uid) if dbr == nil { return errors.New("没有挑战记录") } if dbr.SociatyId != "" { var pipe *pipe.RedisPipe = s.Redis.RedisPipe(context.TODO()) if cmd := pipe.ZIncrBy(fmt.Sprintf("%s:%s", s.TableName, BOSS_SOCIATY_RANK), float64(integral), dbr.SociatyId); cmd != nil { _, err := cmd.Result() if err != nil { return err } } if _, err := pipe.Exec(); err != nil { return err } } return nil } // 伤害转换积分 func (s *ModelSociatyBoss) transIntegral(harm int32) int64 { return int64(harm) } // 查询排行榜 func (s *ModelSociatyBoss) queryRankUid(count int64) (ranks []string, err error) { var ( result []string ) if result, err = s.DBModel.Redis.ZRevRange(s.TableName, 0, count).Result(); err != nil { return } ranks = make([]string, 0) for i := 0; i < len(result); i += 1 { ranks = append(ranks, result[i]) } return } // 取积分 func (s *ModelSociatyBoss) getScoreByUid(key, member string) int64 { result, err := s.DBModel.Redis.ZScore(key, member) if err != nil { return 0 } return int64(result) } // 取排名 func (s *ModelSociatyBoss) getRankingByMember(key, member string) int64 { result, err := s.DBModel.Redis.ZRevRank(key, member) if err != nil { return 0 } return (result + 1) } // 排行榜 func (s *ModelSociatyBoss) bossRank(sociaty *pb.DBSociaty, rankType int32) (res []*pb.SociatyRankInfo) { var ( rankCount int64 key string ) // 所有排行记录(个人、公会) rank := func() []*pb.SociatyRankInfo { rankUids, err := s.queryRankUid(rankCount) if err != nil { return nil } for _, uid := range rankUids { imodule, err := s.service.GetModule(comm.ModuleUser) if err != nil { return nil } if iuser, ok := imodule.(comm.IUser); ok { if user := iuser.GetUser(uid); user != nil && user.Uid != "" { res = append(res, &pb.SociatyRankInfo{ SociatyName: sociaty.Name, Name: user.Name, Head: user.Avatar, Lv: user.Lv, Ranking: s.getRankingByMember(key, user.Uid), Integral: s.getScoreByUid(key, user.Uid), }) } } } return res } switch rankType { case 1: //个人 rankCount = 1000 //数据数量 key = fmt.Sprintf("%s:%s", s.TableName, BOSS_PERSONAL_RANK) rank() case 2: //公会 rankCount = 50 //数据数量 key = fmt.Sprintf("%s:%s", s.TableName, BOSS_SOCIATY_RANK) rank() case 3: //公会成员排行 for _, m := range sociaty.Members { imodule, err := s.service.GetModule(comm.ModuleUser) if err != nil { return nil } if iuser, ok := imodule.(comm.IUser); ok { if user := iuser.GetUser(m.Uid); user != nil && user.Uid != "" { res = append(res, &pb.SociatyRankInfo{ SociatyName: sociaty.Name, Name: user.Name, Head: user.Avatar, Lv: user.Lv, Ranking: s.getRankingByMember(key, user.Uid), Integral: s.getScoreByUid(key, user.Uid), }) } } } sort.SliceStable(res, func(i, j int) bool { return res[i].Integral > res[j].Integral }) } return nil } // 公会BOSS重置数据(每个周期) func (s *ModelSociatyBoss) resetSportsData() error { sports := s.getSociatyBossSports() if sports == nil { return errors.New("sociatyboss sports is nil") } // 归档前赛季数据 for _, uid := range sports.Uids { var record []*pb.DBSociatyBossRecord _ = s.GetList(uid, &record) for _, v := range record { if err := s.DelByUId(uid); err != nil { s.moduleSociaty.Error("清理玩家赛季记录", log.Field{Key: "uid", Value: v.Uid}, log.Field{Key: "err", Value: err.Error()}) } } } // 清理排行榜 // if err := s.Del(BOSS_PERSONAL_RANK); err != nil { // s.moduleSociaty.Error("清理排行榜", log.Field{Key: "err", Value: err.Error()}) // } return nil } // 是否参赛 func (s *ModelSociatyBoss) IsInSports(uid string) bool { sports := s.getSociatyBossSports() if sports == nil { return false } for _, v := range sports.Uids { if v == uid { return true } } return false } // 赛季结算 func (s *ModelSociatyBoss) settlement() error { sports := s.getSociatyBossSports() if sports == nil { return errors.New("sociatyboss sports is nil") } // 计算玩家的赛季积分 for _, uid := range sports.Uids { cr := s.getChallengeRecord(uid) //排序-倒叙 sort.SliceStable(cr.Record, func(i, j int) bool { return cr.Record[i].Integral > cr.Record[j].Integral }) var total int64 //当前赛季总积分 var highScore []int64 //暂存玩家积分 for i, v := range cr.Record { highScore = append(highScore, v.Integral) total += v.Integral if i > 2 { break } } // 更新玩家赛季历史记录 isExist := func(tasks []*pb.ChallengeTask, taskId int32) bool { for _, task := range tasks { if task.TaskId == taskId { return true } } return false } // 判断积分任务是否达标 var tasks []*pb.ChallengeTask var taskFlag bool //任务更新状态 taskList := s.moduleSociaty.configure.getBossTaskList() for _, task := range taskList { if total >= int64(task.Score) && !isExist(cr.Tasks, task.Id) { tasks = append(tasks, &pb.ChallengeTask{ TaskId: task.Id, Status: 1, //可领取 }) taskFlag = true } } cr.Total = total cr.Integrals = highScore update := map[string]interface{}{ "total": total, "integrals": highScore, } // 任务状态有变化时更新 if taskFlag { update["tasks"] = tasks cr.Tasks = tasks } if err := s.Change(uid, update); err != nil { s.moduleSociaty.Error("更新玩家赛事信息", log.Field{Key: "uid", Value: uid}, log.Field{Key: "err", Value: err.Error()}) continue } // 将玩家积分加入排行榜 s.addPersonalRank(uid, total) // 将玩家积分加入公会排行榜 s.addSociatyRank(uid, total) s.moduleSociaty.Debug("结算:", log.Field{Key: "uid", Value: uid}) } //归档前10的玩家记录 return nil }