import { rankType, ResOpen } from '../../shared/protocols/rank/PtlOpen'; import { rankInfo } from '../../shared/protocols/type'; import { Queue } from '../../shared/public/queue'; import { RankClslCross } from './rank_clsl'; //import { RankHbzbJfsCross, RankHbzbJfsLocal, RankHbzbZbsCross } from './rank_hbzb_jfs'; import { RankPower } from './rank_power'; import { RankQjzzd } from './rank_qjzzd'; import { RankSlzd1, RankSlzd2, RankSlzd3, RankSlzd4, RankSlzd5, RankSlzd6 } from './rank_slzd'; import { RankTanXian } from './rank_tanxian'; import { RankTujian } from './rank_tujian'; import { RankWzryCross } from './rank_wzry'; import { RankXszm } from './rank_xszm'; import { RankZccg } from './rank_zccg'; import * as util from 'util' import { RankKfjs_1 } from "./rank_kfjs"; import { clusterRunOnce } from '../../clusterUtils'; import { number } from 'mathjs'; export abstract class Rank { static list: Partial<{ qjzzd: RankQjzzd; zhanli: RankPower; tanxian: RankTanXian; slzd1: RankSlzd1; slzd2: RankSlzd2; slzd3: RankSlzd3; slzd4: RankSlzd4; slzd5: RankSlzd5; slzd6: RankSlzd6; xszm: RankXszm; clslCross: RankClslCross; zccg: RankZccg; tujian: RankTujian; wzryCross: RankWzryCross; }> & k_v = {}; queue = new Queue(); findKey = 'uid'; countMaxNum = 50; utimeTTL = 60; abstract getType(): rankType; // 获取rank数据, 注:可能重写,具体根据type类型判断 async getRankList(uid: string, { min, max }): Promise<{ rankList: rankInfo[]; myRank: rankInfo; }> { let conn = G?.server?.uid_connections[uid]; let data = await this.getRankData(uid); let rankList = await this.getRankListRange(min, max); let rank = await this.getRankSortByOne(uid); let score = await this.getRankScore(uid) return { rankList: rankList, myRank: { rank, player: data?.player || conn?.gud || {}, valArr: [score] } }; } /** * 根据RankInfo数据,获取用于排名的积分值得 * 注:可能重写,具体根据type类型判断 * @param info * @returns 积分值 */ getValArr(info: rankInfo): number | string { return info?.valArr[0] || 0 } // 注:可能重写,具体根据type类型判断 compare(other: rankInfo, cur: rankInfo): boolean { return cur.valArr[0] > other.valArr[0]; } // rankList排序,注:可能重写,具体根据type类型判断 compareSort(a: rankInfo, b: rankInfo): number { return b.valArr[0] - a.valArr[0]; } /** * 页面转换,根据page和offset转换出min和max * @param page 页面 * @param offset 每页数量 * @returns */ static pageToMin(page: number, offset: number) { let res = { min: page * offset, max: page * offset + offset } return res } /** * 返回排行榜DB连接对象 */ get db() { return G.mongodb.collection('rankList'); } /** * 返回排行榜类型 */ get type() { return this.getType(); } constructor() { Rank.list[this.getType() as string] = this; this.cotr(); } /** * 根据type,获取redis Data的key */ get getRedisKey() { return util.format('rank:%s:data', this.getType()) } /** * 根据type,获取redis Sort的key */ get getRedisKeySort() { return util.format('rank:%s:sort', this.getType()) } // 初始化 async cotr() { clusterRunOnce(async () => { // redis已存在则不初始化 //if(await this.getRankLen() > 0) return //将db里的数据,写入到 rank:xxx:sort里 //写入的单条数据为: {uid:score} // 首先清理redis中sort数据 在从数据库中初始化 await G.ioredis.del(this.getRedisKeySort); this.db.find({ type: this.type }, { projection: { "idKey": 1, "type": 1, "data.valArr": 1, "utime": 1 } }).toArray().then(listArr => { // 写入初始化数据 listArr = listArr || []; listArr.forEach(item => { if (item.idKey && item.data) this.setRankData(item.idKey, item.data) }) }); }) this.getRankListRange(); } /** * 更新玩家的积分 * @param uid uid,在rank里通常保存于idKey这个字段 * @param data 积分数据,主要是需要里面的data.valArr字段 * @returns */ async setRankData(uid: string, data: rankInfo) { let keySort = this.getRedisKeySort // let key = this.getRedisKey // G.redis.hSet(key, idKey, data) /**积分 */ let score = await this.getValArr(data) //G.redis.zAdd(keySort, {value: idKey, score: valArr}) await G.ioredis.zadd(keySort, score, uid) return } /** * 获取单个用户的数据 * @param uid uid * @returns */ async getRankData(uid: string) { let data: rankInfo; let res = await this.db.findOne({ "idKey": uid, "type": this.getType() }) if (res?.data && res.data.utime + 60 > G.time) { data = (G.mongodb.conversionIdObj(res)).data; } else if (res?.data) { let player = await G.mongodb.collection("user").findOne({ uid: uid }, { projection: { _id: 0 } }); data = Object.assign(res.data, { player: player }); this.db.updateOne({ idKey: uid, type: this.getType() }, { $set: { "data.player": data.player, "data.utime": G.time } }); } return data; // let key = this.getRedisKey // let data = await G.redis.hGet(key, idKey) // if(!data || data.utime < (G.time - this.utimeTTL)) { // let type = this.getType() // let res = await this.db.findOne({isKey: idKey, type}) //<--isKey??意味着整个rank从来不会更新,res永远是空 // if(res) { // data = G.mongodb.conversionIdObj(res) // this.setRankData(idKey, data.data) // } // } // if(data) return data // return undefined || {} } /** * 获取单个用户的排序分数 * @param uid * @returns */ async getRankScore(uid: string): Promise { let score = await G.redis.zScore(this.getRedisKeySort, uid) return score || 0 } /** * 从redis中获取单个用户的排名 *降序 * @param uid * @returns */ async getRankSortByOne(uid: string): Promise { let rank = await G.redis.zRevRank(this.getRedisKeySort, uid) return rank === 0 || rank > 0 ? rank : -1; } // 从redis中获取单个指定排名的用户数据 *降序 async getRankSortDataByOne(rank: number): Promise { let rankList: rankInfo[] = await this.getRankListRange(rank, rank + 1) let rankInfo = rankList && rankList.length > 0 ? rankList[0] : {} as rankInfo return rankInfo } // 获取排名长度 // async getRankLen() { // return await G.redis.hLen(this.getRedisKey) // } /** * 获取指定排名范围的数据 *降序 * @param min * @param max * @returns */ async getRankListRange(min: number = 0, max: number = 50): Promise { let uids = await this.getRankListIdKeyRange(min, max) if (uids && uids.length > 0) { let res = await this.db.find({ idKey: { $in: uids }, type: this.getType() }).toArray() switch (this.getType()) { case "slzd1": case "slzd2": case "slzd3": case "slzd4": case "slzd5": case "slzd6": let ghid = []; res = res.map(item => { if (!item.data?.player?.ghid || item.data.utime + 60 < G.time) { ghid.push(G.mongodb.conversionId(item.idKey)); } return item; }) if (ghid.length > 0) { let ghinfo = await G.mongodb.collection("gonghui").find( { _id: { $in: ghid } }, { projection: { name: 1 } } ).toArray(); ghinfo.forEach(item => { let index = res.findIndex(x => x.idKey == item._id.toHexString()); res[index].data.player = { ghName: item.name, ghId: item._id.toHexString(), }; this.db.updateOne({ idKey: item._id.toHexString(), type: this.getType() }, { $set: { "data.player": res[index].data.player } }); }) } break; default: // 排行数据更新逻辑 默认更新playerInfo let updateUids = []; res = res.map(item => { // 没有player 或者 player 过期 if (!item.data?.player || item.data.utime + 60 < G.time) { updateUids.push(item.idKey); } return item; }); let newUserArr = await G.mongodb.collection('user').find( { uid: { $in: updateUids } }, { projection: { _id: 0 } } ).toArray(); newUserArr.forEach(item => { // 每次遍历查找? let index = res.findIndex(x => x.idKey == item.uid); res[index].data.player = item; this.db.updateOne({ idKey: item.uid, type: this.getType() }, { $set: { "data.player": item } }); // 跟新redis score // this.setRankData(item.uid, res[index].data as any); }) } // 按照redis uids 排序顺序排序 return res.sort((a,b)=>uids.indexOf(a.idKey)-uids.indexOf(b.idKey)).map(ele => ele.data); } return [] } /** * 从redis中获取指定排名范围的uid集合 *降序 * @param min * @param max * @returns uid集合数组 */ async getRankListIdKeyRange(min: number = 0, max: number = 50): Promise { let uids = await G.redis.zRevRange(this.getRedisKeySort, min, max - 1) return uids || [] } /** * 获取指定类型的全部rank列表,返回为积分排序后的数组 * @returns */ async getRankListAll(): Promise { // let res = await G.redis.hGetAll(this.getRedisKey) let res = (await this.db.find({ type: this.getType() }).toArray()).map(ele => ele.data); if (res) { // 如果是用户数据,则比对utime,更新数据 // res = await this.checkData(res) return res.sort(this.compareSort) } return [] } /** * 按排名获取全部的idKey * @returns */ async getRankListIdKeyAll(): Promise { let res = this.getRankListIdKeyRange(0, -1) return res } // 验证数据的过期时间,更新数据 // async checkData(rankList: rankInfo[]): Promise { // let updateUid = [] // rankList.forEach((value,key) => { // // 仅针对用户表,公会表暂不考虑 // if(rankList[key].player?.uid && (rankList[key].utime || 0) < (G.time - this.utimeTTL)) { // // 更新数据 // updateUid.push(rankList[key].player.uid) // } // }) // if(updateUid.length > 0) { // let newUserArr = await G.mongodb.collection('user').find({uid:{$in: updateUid}}).toArray() // // let newUserArr = await G.redis.gets('user', ...updateUid.map(uid => [uid] as [string])) // // let newUserArr = await G.mongodb.collection('user').find({uid:{$in: updateUid}}).toArray() // newUserArr.forEach(item => { // let index = rankList.findIndex( x => x.player.uid == item.uid); // rankList[index].player = item; // this.setRankData(item.uid, rankList[index]); // }) // } // return rankList // } /** * 删除指定idk的排名数据 * @param idKey * @returns */ async delRankData(idKey: string) { // G.redis.hDel(this.getRedisKey, idKey) G.redis.zRem(this.getRedisKeySort, idKey); this.db.updateOne({ idKey: idKey, type: this.getType() }, { $set: { idKey: `del_${idKey}` } }); } /** * 原逻辑前50(countMaxNum)名才更新数据(上榜),多余的数据会删除。 * @param info * @returns */ async addNew(info: rankInfo) { //this.queue.enqueue(async () => { await this.setRankData(info.player[this.findKey], info) await this.db.updateOne({ type: this.type, idKey: info.player[this.findKey] }, { $set: { data: info } }, { upsert: true }); // 删除第50名以后的数据,(排名从0开始计算) // let idKeys:string[] = await this.getRankListIdKeyRange(50, -1) // idKeys.forEach(idKey => { // this.db.deleteOne({ type: this.type, idKey: idKey }); // this.delRankData(idKey) // }) //}); } // 清空相关rank数据 async clear() { this.queue.enqueue(async () => { // G.redis.rawDel(this.getRedisKey) G.redis.rawDel(this.getRedisKeySort); await this.db.deleteMany({ type: this.type }); }); } }