diff --git a/src/public/rank/rank.ts b/src/public/rank/rank.ts index c367d09..72e28e2 100644 --- a/src/public/rank/rank.ts +++ b/src/public/rank/rank.ts @@ -1,6 +1,5 @@ import { rankType, ResOpen } from '../../shared/protocols/rank/PtlOpen'; import { rankInfo } from '../../shared/protocols/type'; -import { player } from '../../shared/protocols/user/type'; import { Queue } from '../../shared/public/queue'; import { RankClslCross } from './rank_clsl'; //import { RankHbzbJfsCross, RankHbzbJfsLocal, RankHbzbZbsCross } from './rank_hbzb_jfs'; @@ -12,21 +11,17 @@ import { RankTujian } from './rank_tujian'; import { RankWzryCross } from './rank_wzry'; import { RankXszm } from './rank_xszm'; import { RankZccg } from './rank_zccg'; -import { sortEd } from '../../module/redis' import * as util from 'util' -import {RankKfjs} from "./rank_kfjs"; - +import { RankKfjs } from "./rank_kfjs"; import { clusterRunOnce } from '../../clusterUtils'; + export abstract class Rank { static list: Partial<{ qjzzd: RankQjzzd; zhanli: RankPower; tanxian: RankTanXian; - //hbzbLocal: RankHbzbJfsLocal; - // hbzbCross: RankHbzbJfsCross; - // hbzbZbsCross: RankHbzbZbsCross; slzd1: RankSlzd1; slzd2: RankSlzd2; slzd3: RankSlzd3; @@ -41,17 +36,12 @@ export abstract class Rank { kfjs: RankKfjs }> = {}; - // list: rankInfo[]; queue = new Queue(); findKey = 'uid'; countMaxNum = 50; utimeTTL = 60; abstract getType(): rankType; - // abstract compare(other: rankInfo, cur: rankInfo): boolean; - // abstract compareSort(a: rankInfo, b: rankInfo): number; - // abstract getRankList(uid: string, obj?: {gud?:player, min?: number, max?:number}): Promise; - // abstract getValArr(info: rankInfo): number|string; // 运算后的积分值,排名依据 // 获取rank数据, 注:可能重写,具体根据type类型判断 async getRankList(uid: string, { min, max }): Promise<{ rankList: rankInfo[]; myRank: rankInfo; }> { @@ -65,13 +55,18 @@ export abstract class Rank { myRank: { rank, player: data?.player || conn?.gud || {}, - valArr: [ score ] + valArr: [score] } }; } - // 积分, 排名依据, ,注:可能重写,具体根据type类型判断 - getValArr(info: rankInfo):number|string { + /** + * 根据RankInfo数据,获取用于排名的积分值得 + * 注:可能重写,具体根据type类型判断 + * @param info + * @returns 积分值 + */ + getValArr(info: rankInfo): number | string { return info?.valArr[0] || 0 } @@ -79,15 +74,19 @@ export abstract class Rank { 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]; } - - // 页码转换为min,max - static pageToMin (page, offset) { + /** + * 页面转换,根据page和offset转换出min和max + * @param page 页面 + * @param offset 每页数量 + * @returns + */ + static pageToMin(page: number, offset: number) { let res = { min: page * offset, max: page * offset + offset @@ -95,9 +94,16 @@ export abstract class Rank { return res } + /** + * 返回排行榜DB连接对象 + */ get db() { return G.mongodb.collection('rankList'); } + + /** + * 返回排行榜类型 + */ get type() { return this.getType(); } @@ -107,10 +113,16 @@ export abstract class Rank { 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()) } @@ -119,152 +131,275 @@ export abstract class Rank { async cotr() { clusterRunOnce(async () => { // redis已存在则不初始化 - if(await this.getRankLen() > 0) return - this.db.find({ type: this.type }).toArray().then(listArr => { + //if(await this.getRankLen() > 0) return + + //将db里的数据,写入到 rank:xxx:sort里 + //写入的单条数据为: {uid:score} + 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) + if (item.idKey && item.data) this.setRankData(item.idKey, item.data) }) }); }) + + this.getRankListRange(); } - // 更新数据与排名 - async setRankData(idKey: string, data: rankInfo) { - let key = this.getRedisKey + /** + * 更新玩家的积分 + * @param uid uid,在rank里通常保存于idKey这个字段 + * @param data 积分数据,主要是需要里面的data.valArr字段 + * @returns + */ + async setRankData(uid: string, data: rankInfo) { let keySort = this.getRedisKeySort - data.utime = G.time - // data - G.redis.hSet(key, idKey, data) - // sort - let valArr = await this.getValArr(data) - G.redis.zAdd(keySort, {value: idKey, score: valArr}) - return + + // 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 } - // 获取单个用户的数据 - async getRankData(idKey: string) { - 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}) - if(res) { - data = G.mongodb.conversionIdObj(res) - this.setRankData(idKey, data.data) - } + /** + * 获取单个用户的数据 + * @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 } }); } - if(data) return data - return undefined || {} + 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 || {} } - // 获取单个用户的分数 - async getRankScore(idKey:string): Promise { - let score = await G.redis.zScore(this.getRedisKeySort, idKey) + /** + * 获取单个用户的排序分数 + * @param uid + * @returns + */ + async getRankScore(uid: string): Promise { + let score = await G.redis.zScore(this.getRedisKeySort, uid) return score || 0 - } - - // 获取单个用户的排名 *降序 - async getRankSortByOne(idKey: string):Promise { - let rank = await G.redis.zRevRank(this.getRedisKeySort, idKey) - return rank === 0 || rank > 0 ? rank: -1; } - // 获取单个指定排名的用户数据 *降序 + /** + * 从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 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) - } + // async getRankLen() { + // return await G.redis.hLen(this.getRedisKey) + // } - // 获取指定排名范围的数据 *降序 - async getRankListRange(min:number = 0, max:number = 50): Promise { - let idKeys = await this.getRankListIdKeyRange(min, max) - if(idKeys && idKeys.length > 0) { - let res = await G.redis.hmGet(this.getRedisKey, idKeys) - res = await this.checkData(res) - return res.sort(this.compareSort) - } - return [] - } - - // 获取指定排名范围的idKey *降序 - async getRankListIdKeyRange(min:number = 0, max: number = 50): Promise { - let idKeys = await G.redis.zRevRange(this.getRedisKeySort, min, max - 1) - return idKeys || [] - } + /** + * 获取指定排名范围的数据 *降序 + * @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() - // 获取指定类型的全部rank列表,返回为积分排序后的数组 - async getRankListAll(): Promise { - let res = await G.redis.hGetAll(this.getRedisKey) - if(res) { - // 如果是用户数据,则比对utime,更新数据 - // res = await this.checkData(res) - return Object.values(res).sort(this.compareSort) + 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){ + (await G.mongodb.collection("gonghui").find( + { _id: { $in: ghid } }, { projection: { name: 1 } } + )).forEach(item => { + let index = res.findIndex(x => x.idKey == item._id.toHexString()); + res[index].data.player.ghName = item.name; + this.db.updateOne({ idKey: item._id.toHexString(), type: this.getType() }, { $set: { "data.player.ghName": item.name } }); + }) + } + 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); + }) + } + + return res.map(ele => ele.data).sort(this.compareSort) as any; } return [] } - // 按排名获取全部的idKey + /** + * 从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 + // 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}` } + }); } - async delRankData(idKey:string) { - G.redis.hDel(this.getRedisKey, idKey) - G.redis.zRem(this.getRedisKeySort, idKey) - } - - // 原逻辑前50(countMaxNum)名才更新数据(上榜),多余的数据会删除。 + /** + * 原逻辑前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) - // }) + 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 }); - G.redis.rawDel(this.getRedisKey) - G.redis.rawDel(this.getRedisKeySort) }); } } \ No newline at end of file