diff --git a/game-server/app.ts b/game-server/app.ts index aa7e8afb5..27abeb93d 100644 --- a/game-server/app.ts +++ b/game-server/app.ts @@ -22,7 +22,7 @@ import * as redLockService from './app/services/redLockService'; import _pinus = require('pinus'); import { updateTeamStatus } from './app/services/comBattleService'; import { init } from './app/pubUtils/gmData/gmDataUtil'; -import { addActive } from './app/services/guildService'; +import { gameData } from './app/pubUtils/data'; const filePath = (_pinus as any).FILEPATH; filePath.MASTER = '/config/master'; filePath.SERVER = '/config/servers'; diff --git a/game-server/app/servers/battle/handler/guildHandler.ts b/game-server/app/servers/battle/handler/guildHandler.ts index 046e4ae6b..6239bbc6e 100644 --- a/game-server/app/servers/battle/handler/guildHandler.ts +++ b/game-server/app/servers/battle/handler/guildHandler.ts @@ -74,7 +74,7 @@ export class GuildHandler { // 加入排行 let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, {serverId}); await r.setRankWithGuildInfo(guild.code, 0, Date.now(), guild); - let rank = await r.getMyRank(guild.code); + let rank = await r.getMyRank({ guildCode: guild.code }); // 加入channel session.set('guildCode', guild.code); @@ -280,7 +280,7 @@ export class GuildHandler { // 获取排行榜 let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); - const rank = await r.getMyRank(guild.code); + const rank = await r.getMyRank({ guildCode: guild.code }); // 返回 const result = { hasGuild: true, ...guild, leader: { ...leader, isOnline: leaderIsOnline }, rank, myInfo: {...userGuild }}; @@ -606,7 +606,7 @@ export class GuildHandler { this.app.rpc.chat.guildRemote.dismiss.toServer(chatSid,code); let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, {serverId}); - await r.removeFromRank(code); + await r.removeFromRank({ guildCode: code }); return resResult(STATUS.SUCCESS, { code, status: guild.status }); } @@ -790,6 +790,11 @@ export class GuildHandler { if(!result) { return resResult(STATUS.GUILD_FUND_NOT_ENOUGH); } + if(id == GUILD_STRUCTURE.ARMY_CENTER) { + let r = new Rank(REDIS_KEY.GUILD_LV_RANK, { serverId }); + await r.setRankWithGuildInfo(code, guild.lv, guild.lvUpdateTime, guild); + } + const resultStructure = result.structure.find(cur => cur.id == id); // 修改信息 @@ -919,7 +924,7 @@ export class GuildHandler { if(!checkResult) return resResult(STATUS.GUILD_AUTH_NOT_ENOUGH); let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); - let { ranks, myRank } = await r.getRankListWithMyRank(myUserGuild.guildCode); + let { ranks, myRank } = await r.getRankListWithMyRank({ guildCode: myUserGuild.guildCode }); if(!myRank) { let guild = await GuildModel.findByCode(myUserGuild.guildCode, serverId, 'code icon name lv leader activeWeekly'); myRank = await r.generMyRankWithGuild(myUserGuild.guildCode, guild.activeWeekly, guild.activeUpdateTime, guild); diff --git a/game-server/app/servers/battle/handler/normalBattleHandler.ts b/game-server/app/servers/battle/handler/normalBattleHandler.ts index 5bbc5aabc..2b05d371d 100644 --- a/game-server/app/servers/battle/handler/normalBattleHandler.ts +++ b/game-server/app/servers/battle/handler/normalBattleHandler.ts @@ -3,7 +3,7 @@ import { BattleRecordModel } from '../../../db/BattleRecord'; import { BattleSweepRecordModel } from '../../../db/BattleSweepRecord'; import { getWarById, } from '../../../pubUtils/gamedata'; import { genCode } from '../../../pubUtils/util'; -import { WAR_TYPE, EVENT_STATUS, FUNC_OPT_TYPE, MSG_SOURCE } from '../../../consts'; +import { WAR_TYPE, EVENT_STATUS, FUNC_OPT_TYPE, MSG_SOURCE, REDIS_KEY } from '../../../consts'; import { checkDaily, checkDailyAndIncrease } from '../../../services/dailyBattleService'; import { checkTowerWar, towerBattleEnd } from '../../../services/battleService'; import { WarReward } from '../../../services/warRewardService'; @@ -18,6 +18,8 @@ import { checkDungeonNum, checkDungeonAndIncrease } from '../../../services/dung import { switchOnFunc } from '../../../services/funcSwitchService'; import { gameData } from '../../../pubUtils/data'; import { pushMysteryFirstMsg, pushTowerMsg, pushVestigeFirstMsg } from '../../../services/chatService'; +import { nowSeconds } from '../../../pubUtils/timeUtil'; +import { Rank } from '../../../services/rankService'; export default function(app: Application) { return new NormalBattleHandler(app); @@ -215,11 +217,22 @@ export class NormalBattleHandler { } } else if (warInfo.warType == WAR_TYPE.MYSTERY || warInfo.warType == WAR_TYPE.MYSTERY_ELITE) { let checkResult = await checkDungeonAndIncrease(roleId, 1, false); - await RoleModel.saveDungeonHero(roleId, battleId, heroes, isSuccess); + let role = await RoleModel.saveDungeonHero(roleId, battleId, heroes, isSuccess); + let r = new Rank(REDIS_KEY.DUNGEON_RANK, { serverId }); + await r.setRankWithRoleInfo(roleId, role.dungeonWarId, role.dungeonUpdatedAt, role); + if(checkResult.status == -1) { return checkResult.resResult; } dungeonNum = Object.assign(dungeonNum, checkResult.data) + } else if (warInfo.warType == WAR_TYPE.NORMAL) { + let role = await RoleModel.updateMainWarId(roleId, battleId, nowSeconds()); + let r = new Rank(REDIS_KEY.MAIN_RANK, { serverId }); + await r.setRankWithRoleInfo(roleId, role.mainWarId, role.mainUpdatedAt, role); + } else if (warInfo.warType == WAR_TYPE.MAIN_ELITE) { + let role = await RoleModel.updateMainEliteWarId(roleId, battleId, nowSeconds()); + let r = new Rank(REDIS_KEY.MAIN_ELITE_RANK, { serverId }); + await r.setRankWithRoleInfo(roleId, role.mainEliteWarId, role.mainEliteUpdatedAt, role); } if(isSuccess) { // 挑战胜利 diff --git a/game-server/app/servers/battle/handler/pvpHandler.ts b/game-server/app/servers/battle/handler/pvpHandler.ts index 877a1ae6c..aa8545d4c 100644 --- a/game-server/app/servers/battle/handler/pvpHandler.ts +++ b/game-server/app/servers/battle/handler/pvpHandler.ts @@ -442,7 +442,7 @@ export class PvpHandler { } heroes.sort((a, b) => b.score - a.score); let r = new Rank(REDIS_KEY.PVP_RANK, {}); - let rank = await r.getMyRank(oppoRoleId);//去redis中获取排名 + let rank = await r.getMyRank({ roleId: oppoRoleId });//去redis中获取排名 result = new PlayerDetail({...role, ...pvpDefense, heroes, rank}); } diff --git a/game-server/app/servers/connector/handler/entryHandler.ts b/game-server/app/servers/connector/handler/entryHandler.ts index a1fa5292f..bd8f8688e 100644 --- a/game-server/app/servers/connector/handler/entryHandler.ts +++ b/game-server/app/servers/connector/handler/entryHandler.ts @@ -7,7 +7,7 @@ import { Application } from 'pinus'; import {FrontendSession} from 'pinus'; import { HeroModel } from './../../../db/Hero'; import { resResult } from '../../../pubUtils/util'; -import { COM_BTL_QUALITY, ROLE_SELECT, HERO_SELECT, GUILD_SELECT, USER_GUILD_SELECT, DEBUG_MAGIC_WORD } from '../../../consts'; +import { COM_BTL_QUALITY, ROLE_SELECT, HERO_SELECT, GUILD_SELECT, USER_GUILD_SELECT, DEBUG_MAGIC_WORD, REDIS_KEY } from '../../../consts'; import { getAp } from '../../../services/actionPointService'; import { ItemModel } from '../../../db/Item'; import { chackFunOpenWhenLogin } from '../../../services/funcSwitchService'; @@ -21,6 +21,7 @@ import { gameData } from '../../../pubUtils/data'; import { getMails } from '../../../services/mailService'; import { addRoleToGuildChannel, addRoleToSysChannel, addRoleToWorldChannel, leaveGuildChannel, leaveSysChannel, leaveWorldChannel, recentGuildMsgs, recentPrivateChatInfos, recentSysMsgs, recentWorldMsgs } from '../../../services/chatService'; import { reportOneOnline } from '../../../services/timeTaskService'; +import { Rank } from '../../../services/rankService'; export default function (app: Application) { return new EntryHandler(app); } @@ -104,6 +105,11 @@ export class EntryHandler { await loginRefresh(role.roleId); reportOneOnline(role.roleId, user.userCode, self.app.get('serverId'), user.pkgName); + let r = new Rank(REDIS_KEY.HERO_NUM_RANK, { serverId }); + r.setRankWithRoleInfo(role.roleId, role.heroNum, role.heroNumUpdatedAt, role); + let r2 = new Rank(REDIS_KEY.SUM_CE_RANK, { serverId }); + r2.setRankWithRoleInfo(role.roleId, role.lv, role.updatedAt.getTime(), role); + role['heros'] = heros; role['equips'] = equips; role['consumeGoods'] = items; diff --git a/game-server/app/servers/guild/handler/cityActivityHandler.ts b/game-server/app/servers/guild/handler/cityActivityHandler.ts index 1f0f31a7d..fb1fd8948 100644 --- a/game-server/app/servers/guild/handler/cityActivityHandler.ts +++ b/game-server/app/servers/guild/handler/cityActivityHandler.ts @@ -8,7 +8,7 @@ import { UserGuildActivityRecModel } from "../../../db/UserGuildActivityRec"; import { leaveCityChannel, addRoleToCityChannel, getCityChannelSid } from "../../../services/chatService"; import { UserGuildModel } from "../../../db/UserGuild"; import { GuildActivityRecordModel } from "../../../db/GuildActivityRec"; -import { nowSeconds, getBeforeSeconds } from "../../../pubUtils/timeUtil"; +import { nowSeconds, getBeforeSeconds, getNextHourPoint } from "../../../pubUtils/timeUtil"; import { getGoldObject } from "../../../pubUtils/itemUtils"; import { GUILDACTIVITY } from "../../../pubUtils/dicParam"; import { handleCost } from "../../../services/rewardService"; @@ -233,10 +233,12 @@ export class CityActivityHandler { // 更新redis数据 let myR = new Rank(REDIS_KEY.USER_CITY_ACTIVITY, { serverId, guildCode }); - let myScore = await myR.setRankWithRoleInfo(roleId, damage, Date.now(), null, false, true); + let myScore = await myR.setRankWithRoleInfo(roleId, damage, Date.now(), null, false); + await myR.setExpire(getNextHourPoint(5)); let r = new Rank(REDIS_KEY.CITY_ACTIVITY, { serverId, cityId }, true ); let guildScore = await r.setRankWithGuildInfo(guildCode, damage, Date.now(), null, true); + await r.setExpire(getNextHourPoint(5)); gateHp = obj.getGateHpAndInc(serverId, cityId, dicCity.hp, -1 * damage); if(gateHp <= 0) { diff --git a/game-server/app/servers/guild/handler/gateActivityHandler.ts b/game-server/app/servers/guild/handler/gateActivityHandler.ts index e672d6e90..a9021db59 100644 --- a/game-server/app/servers/guild/handler/gateActivityHandler.ts +++ b/game-server/app/servers/guild/handler/gateActivityHandler.ts @@ -13,6 +13,7 @@ import { getGuildChannelSid } from "../../../services/chatService"; import { UserGuildModel } from "../../../db/UserGuild"; import { GuildActivityCityModel } from "../../../db/GuildActivityCity"; import { Rank } from "../../../services/rankService"; +import { getNextHourPoint } from "../../../pubUtils/timeUtil"; export default function (app: Application) { return new GateActivityHandler(app); @@ -138,10 +139,12 @@ export class GateActivityHandler { // 更新redis数据 let myR = new Rank(REDIS_KEY.USER_GATE_ACTIVITY, { serverId, guildCode }, false); - let myScore = await myR.setRankWithRoleInfo(roleId, score, Date.now(), null, false, true); + let myScore = await myR.setRankWithRoleInfo(roleId, score, Date.now(), null, false); + await myR.setExpire(getNextHourPoint(5)); let r = new Rank(REDIS_KEY.GATE_ACTIVITY, { serverId }, true); let guildScore = await r.setRankWithGuildInfo(guildCode, score, Date.now(), null, true); + await r.setExpire(getNextHourPoint(5)); // 更新数据库 let rec = await UserGuildActivityRecModel.pushRecord(code, newRecords, round); diff --git a/game-server/app/servers/role/handler/friendHandler.ts b/game-server/app/servers/role/handler/friendHandler.ts index a2d9cbcee..4102b3d56 100644 --- a/game-server/app/servers/role/handler/friendHandler.ts +++ b/game-server/app/servers/role/handler/friendHandler.ts @@ -688,7 +688,7 @@ export class FriendHandler { } let r = new Rank(REDIS_KEY.PVP_RANK, {}); - let rank = await r.getMyRank(oppoRoleId);//去redis中获取排名 + let rank = await r.getMyRank({ roleId: oppoRoleId });//去redis中获取排名 result = new PlayerDetail({...role, defCe: topLineupCe, heroes}); result.setWarStar(role.warStar, rank, towerLv); diff --git a/game-server/app/servers/role/handler/heroHandler.ts b/game-server/app/servers/role/handler/heroHandler.ts index edd5f24fa..608477a26 100644 --- a/game-server/app/servers/role/handler/heroHandler.ts +++ b/game-server/app/servers/role/handler/heroHandler.ts @@ -4,7 +4,7 @@ import { calPlayerCeAndSave, calAllHeroCe } from '../../../services/playerCeServ import { resResult, returnHeroCeRatio, deepCopy } from '../../../pubUtils/util'; import { STATUS } from '../../../consts/statusCode'; import { HeroModel, Connect } from '../../../db/Hero'; -import {CURRENCY_BY_TYPE, CURRENCY_TYPE, CONSUME_TYPE, HERO_GROW_MAX, HERO_SYSTEM_TYPE, ABI_STAGE, DEBUG_MAGIC_WORD, HERO_INITIAL_QUALITY} from '../../../consts'; +import {CURRENCY_BY_TYPE, CURRENCY_TYPE, CONSUME_TYPE, HERO_GROW_MAX, HERO_SYSTEM_TYPE, ABI_STAGE, DEBUG_MAGIC_WORD, HERO_INITIAL_QUALITY, REDIS_KEY} from '../../../consts'; import { RoleModel } from '../../../db/Role'; import { ItemModel } from '../../../db/Item'; import { gameData, getHeroExpByLv, getHeroStarByQuality, getHeroWakeByQuality, getHeroLvByExp, getMaxGradeByjobClass, getJobByGradeAndClass, getFriendShipById, getFavourLvByExp } from '../../../pubUtils/data'; @@ -14,6 +14,8 @@ import { pushComposeOrangeHero, pushHeroQualityUpMsg, pushHeroStarMax, pushHeroW import { calculatetopLineup } from '../../../pubUtils/playerCe'; import { PvpDefenseModel } from '../../../db/PvpDefense'; import { Attribute } from '../../../domain/roleField/attribute'; +import { nowSeconds } from '../../../pubUtils/timeUtil'; +import { Rank } from '../../../services/rankService'; export default function(app: Application) { return new HeroHandler(app); @@ -139,6 +141,10 @@ export class HeroHandler { let curHero = await HeroModel.createHero({ roleId, serverId, roleName, hid, hName, star, quality, job, skins:[{id: initialSkin, enable: true}] }); + let role = await RoleModel.incRoleInfo(roleId, { heroNum: 1 }, { heroNumUpdatedAt: nowSeconds() }); + let r = new Rank(REDIS_KEY.HERO_NUM_RANK, { serverId }); + await r.setRankWithRoleInfo(roleId, role.heroNum, role.heroNumUpdatedAt, role); + await unlockFigure(sid, roleId, [{ type: FIGURE_UNLOCK_CONDITION.GET_HERO, paramHid: hid }]); // 解锁头像 let hero = await calPlayerCeAndSave(HERO_SYSTEM_TYPE.INIT, sid, roleId, curHero, {}); await calAllHeroCe(HERO_SYSTEM_TYPE.ADD_SKIN, sid, roleId, {}, [initialSkin]) diff --git a/game-server/app/servers/role/handler/rankHandler.ts b/game-server/app/servers/role/handler/rankHandler.ts index 812ea7d46..a6b4e205f 100644 --- a/game-server/app/servers/role/handler/rankHandler.ts +++ b/game-server/app/servers/role/handler/rankHandler.ts @@ -1,14 +1,13 @@ import { Application, BackendSession } from "pinus"; -import { resResult, aesEncryptcfb } from "../../../pubUtils/util"; -import { STATUS, REDIS_KEY, ENCRYPT_KEY, ENCRYPT_IV } from "../../../consts"; -import { RoleModel, RoleType } from "../../../db/Role"; +import { resResult } from "../../../pubUtils/util"; +import { STATUS, REDIS_KEY, RANK_TYPE_TO_KEY, ROLE_SELECT, RANK_TYPE, HERO_SELECT, GUILD_SELECT } from "../../../consts"; +import { RoleModel } from "../../../db/Role"; import { UserGuildModel } from "../../../db/UserGuild"; import { GuildModel } from "../../../db/Guild"; -import { GuildRankParam } from "../../../domain/rank"; import { HeroModel } from "../../../db/Hero"; import { UserModel } from "../../../db/User"; import { Rank } from "../../../services/rankService"; -import { redisClient } from "../../../services/redisService"; +import { nowSeconds } from "../../../pubUtils/timeUtil"; export default function (app: Application) { return new RoleHandler(app); @@ -23,7 +22,7 @@ export class RoleHandler { let roleName = session.get('roleName'); let serverId = session.get('serverId'); - + return resResult(STATUS.SUCCESS, { role: [ { type: 1, roleId: "8l6HPZAbdS", roleName: "东方月初", head: 11219, frame: 11301, spine: 11419, lv: 40, vLv: 0, guildName: "军团A", ce: 464262, num: 464262, received: [], }, @@ -43,40 +42,47 @@ export class RoleHandler { async getRank(msg: { type: number }, session: BackendSession) { let roleId = session.get('roleId'); - let roleName = session.get('roleName'); let serverId = session.get('serverId'); let { type } = msg; + let redisKey = RANK_TYPE_TO_KEY.get(type); + if(!redisKey) return resResult(STATUS.WRONG_PARMS); - let role = await RoleModel.findByRoleId(roleId, null, true); - let { lv, vLv, head, frame, spine, guildName, ce } = role; - - let lineup = [ - { hid: 27, star: 6, colorStar: 6, lv: 96, quality: 3, job: 11 }, - { hid: 32, star: 6, colorStar: 6, lv: 96, quality: 3, job: 1 }, - { hid: 19, star: 6, colorStar: 6, lv: 96, quality: 3, job: 11 }, - { hid: 11, star: 6, colorStar: 6, lv: 96, quality: 3, job: 11 }, - { hid: 12, star: 6, colorStar: 6, lv: 96, quality: 3, job: 16 }, - { hid: 8, star: 6, colorStar: 6, lv: 96, quality: 3, job: 1 } - ]; - if(type == 2) { - lineup = [{ hid: 27, star: 6, colorStar: 6, lv: 96, quality: 3, job: 11 }] + let r: Rank; + switch(type) { + case RANK_TYPE.TOP_LINTUP: + case RANK_TYPE.TOP_HERO: + case RANK_TYPE.SUM_CE: + r = new Rank(redisKey, { serverId }, false, 0); break; + default: + r = new Rank(redisKey, { serverId }); break; + } + let { ranks, myRank } = await r.getRankListWithMyRank(roleId); + if(!myRank) { + let role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); + if(type == RANK_TYPE.TOP_LINTUP) { + myRank = await r.generMyRankWithRole(roleId, role.topLineupCe, 0, role); + } else if (type == RANK_TYPE.TOP_HERO) { + let hero = await HeroModel.getMyTopHero(roleId, HERO_SELECT.RANK_LINEUP); + myRank = await r.generMyRankWithHero(roleId, hero.hid, hero.ce, 0, hero, role); + } else if (type == RANK_TYPE.HERO_NUM) { + myRank = await r.generMyRankWithRole(roleId, role.heroNum, role.heroNumUpdatedAt, role); + } else if (type == RANK_TYPE.USER_LV) { + myRank = await r.generMyRankWithRole(roleId, role.lv, role.updatedAt.getTime(), role); + } else if (type == RANK_TYPE.SUM_CE) { + myRank = await r.generMyRankWithRole(roleId, role.ce, role.updatedAt.getTime(), role); + } else if (type == RANK_TYPE.TOWER) { + myRank = await r.generMyRankWithRole(roleId, role.towerLv, role.towerUpTime?.getTime()||0, role); + } else if (type == RANK_TYPE.DUNGEON) { + myRank = await r.generMyRankWithRole(roleId, role.dungeonWarId, role.dungeonUpdatedAt, role); + } else if (type == RANK_TYPE.MAIN) { + myRank = await r.generMyRankWithRole(roleId, role.mainWarId, role.mainUpdatedAt, role); + } else if (type == RANK_TYPE.MAIN_ELITE) { + myRank = await r.generMyRankWithRole(roleId, role.mainEliteWarId, role.mainEliteUpdatedAt, role); + } } - let ranks = await RoleModel.getRank('tower', serverId, ["lv", "roleName", "vLv", "head", "frame", "spine", "guildName", "ce"], 1, 10); - - return resResult(STATUS.SUCCESS, { - type, - ranks: ranks.map((cur, i) => { - let { roleId, roleName, head, frame, spine, lv, vLv, ce } = cur; - return { - rank: i + 1, num: 323 - i, roleId, roleName, head, frame, spine, lv, vLv, guildName: `军团${i + 1}`, ce, lineup - } - }), - myRank: { - rank: 0, num: 104, roleId, roleName, head, frame, spine, lv, vLv, guildName, ce, lineup - } - }); + return resResult(STATUS.SUCCESS, { type, ranks, myRank }); } // 查看活跃排行榜 @@ -87,47 +93,38 @@ export class RoleHandler { let { type } = msg; - const myUserGuild = await UserGuildModel.getMyGuild(roleId, 'auth guildCode'); - if (!myUserGuild) return resResult(STATUS.GUILD_NOT_FOUND); + let redisKey = RANK_TYPE_TO_KEY.get(type); + if(!redisKey) return resResult(STATUS.WRONG_PARMS); - let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); - let { ranks, myRank } = await r.getRankListWithMyRank(myUserGuild.guildCode,); + let r = new Rank(redisKey, { serverId }); + const myUserGuild = await UserGuildModel.getMyGuild(roleId, 'auth guildCode'); + let { ranks, myRank } = await r.getRankListWithMyRank({guildCode: myUserGuild?.guildCode||''}); if (!myRank) { - let guild = await GuildModel.findByCode(myUserGuild.guildCode, serverId, 'code icon name lv leader activeWeekly memberCnt'); - myRank = await r.generMyRankWithGuild(guild.code, guild.activeWeekly, guild.activeUpdateTime, guild); + let guild = await GuildModel.findByCode(myUserGuild.guildCode, serverId, GUILD_SELECT.RANK); + if(type == RANK_TYPE.GUILD_LV) { + myRank = await r.generMyRankWithGuild(guild.code, guild.lv, guild.lvUpdateTime, guild); + } else if(type == RANK_TYPE.GUILD_ACTIVE) { + myRank = await r.generMyRankWithGuild(guild.code, guild.activeWeekly, guild.activeUpdateTime, guild); + } } return resResult(STATUS.SUCCESS, { type, ranks, myRank }); } - // 查看活跃排行榜 + // 查看单个武将排行榜 async getHeroRank(msg: { type: number, hid: number }, session: BackendSession) { - let roleId = session.get('roleId'); let serverId = session.get('serverId') let { type, hid } = msg; + let redisKey = RANK_TYPE_TO_KEY.get(type); + if(!redisKey) return resResult(STATUS.WRONG_PARMS); - let lineup = [ - { hid: 27, star: 6, colorStar: 6, lv: 96, quality: 3, job: 11 }, - { hid: 32, star: 6, colorStar: 6, lv: 96, quality: 3, job: 1 }, - { hid: 19, star: 6, colorStar: 6, lv: 96, quality: 3, job: 11 }, - { hid: 11, star: 6, colorStar: 6, lv: 96, quality: 3, job: 11 }, - { hid: 12, star: 6, colorStar: 6, lv: 96, quality: 3, job: 16 }, - { hid: 8, star: 6, colorStar: 6, lv: 96, quality: 3, job: 1 } - ] - let ranks = await HeroModel.getRank(hid, serverId, null, 10); + let r = new Rank(redisKey, { serverId, hid }, false, 0); + let ranks = await r.getRankByRange(); return resResult(STATUS.SUCCESS, { - type, hid, ranks: ranks.map((cur, i) => { - let { ce, roleId, roleName, hid, star, colorStar, lv, quality } = cur; - - return { - rank: i + 1, num: ce, roleId, roleName, head: 11219, frame: 11301, spine: 11419, lv: 40, vLv: 0, guildName: "军团A", ce: 464262, - hero: [{ hid, star, colorStar, lv, quality }], - lineup - } - }) + type, hid, ranks }); } @@ -141,4 +138,11 @@ export class RoleHandler { }) } + async test(msg: { }, session: BackendSession) { + let roles = await RoleModel.find({}).lean(); + for(let role of roles) { + let heroes = await HeroModel.find({ roleId: role.roleId }).lean(); + await RoleModel.updateRoleInfo(role.roleId, { heroNum: heroes.length, heroNumUpdatedAt: nowSeconds() }); + } + } } \ No newline at end of file diff --git a/game-server/app/services/guildActivityObjService.ts b/game-server/app/services/guildActivityObjService.ts index b34c24013..6bd8dd12b 100644 --- a/game-server/app/services/guildActivityObjService.ts +++ b/game-server/app/services/guildActivityObjService.ts @@ -12,6 +12,7 @@ import { getGuildChannelSid } from "./chatService"; import { pinus } from "pinus"; import { getRandEelm, sortArrRandom, resResult } from "../pubUtils/util"; import { Rank } from "./rankService"; +import { getNextHourPoint } from "../pubUtils/timeUtil"; export class GateActivityObject { private gateHp: Map = new Map(); // 城门血条,每个军团有一条血条 guildCode => hp @@ -266,7 +267,8 @@ export class RaceActivityObject { let needSendEnd = woodenHorse.calCurWoodenHorse(events); if(woodenHorse.status == 1 || woodenHorse.status == 2) { // 更新距离 // TODO 写进const表 let r = new Rank(REDIS_KEY.RACE_ACTIVITY, { serverId }, true, 6, 5); - await r.setRank(guildCode, Math.floor(woodenHorse.distance * 1000), woodenHorse.time - woodenHorse.startTime, false); + await r.setRank({guildCode}, Math.floor(woodenHorse.distance * 1000), woodenHorse.time - woodenHorse.startTime, false); + await r.setExpire(getNextHourPoint(5)); if (needSendEnd) { // 抵达后发送奖励,发送消息,结算 await sendSingleRaceActEndMsg(guildCode, woodenHorse); } diff --git a/game-server/app/services/guildActivityService.ts b/game-server/app/services/guildActivityService.ts index 06cae7b65..7f99f8b33 100644 --- a/game-server/app/services/guildActivityService.ts +++ b/game-server/app/services/guildActivityService.ts @@ -156,14 +156,14 @@ export async function getGateActivityRank(guildCode: string, serverId: number, r let r = new Rank(REDIS_KEY.GATE_ACTIVITY, { serverId }, true); r.setGenerFieldsFun(generGuildActivityRankField); - let { ranks: guildRank, myRank: myGuildRank } = await r.getRankListWithMyRank(guildCode); + let { ranks: guildRank, myRank: myGuildRank } = await r.getRankListWithMyRank({guildCode}); if(!myGuildRank) { myGuildRank = await r.generMyRankWithGuild(guildCode, 0, 0); } let myR = new Rank(REDIS_KEY.USER_GATE_ACTIVITY, { serverId, guildCode }); myR.setGenerFieldsFun(generGuildActivityMemberRankField); - let { ranks: memberRank, myRank: myMemberRank } = await r.getRankListWithMyRank(roleId); + let { ranks: memberRank, myRank: myMemberRank } = await r.getRankListWithMyRank({roleId}); if(roleName && !myMemberRank) { myMemberRank = await r.generMyRankWithRole(roleId, 0, 0); } @@ -175,14 +175,14 @@ export async function getCityActivityRank(guildCode: string, serverId: number, c let r = new Rank(REDIS_KEY.CITY_ACTIVITY, { serverId, cityId }, true); r.setGenerFieldsFun(generGuildActivityRankField); - let { ranks: guildRank, myRank: myGuildRank } = await r.getRankListWithMyRank(guildCode); + let { ranks: guildRank, myRank: myGuildRank } = await r.getRankListWithMyRank({guildCode}); if(!myGuildRank) { myGuildRank = await r.generMyRankWithGuild(guildCode, 0, 0); } let myR = new Rank(REDIS_KEY.USER_CITY_ACTIVITY, { serverId, guildCode }); myR.setGenerFieldsFun(generGuildActivityMemberRankField); - let { ranks: memberRank, myRank: myMemberRank } = await r.getRankListWithMyRank(roleId); + let { ranks: memberRank, myRank: myMemberRank } = await r.getRankListWithMyRank({roleId}); if(roleName && !myMemberRank) { myMemberRank = await r.generMyRankWithRole(roleId, 0, 0); } @@ -193,7 +193,7 @@ export async function getCityActivityRank(guildCode: string, serverId: number, c export async function getRaceActivityRank(guildCode: string, serverId: number) { let r = new Rank(REDIS_KEY.RACE_ACTIVITY, { serverId }, true, 6); r.setGenerFieldsFun(generField); - let { myRank, ranks } = await r.getRankListWithMyRank(guildCode); + let { myRank, ranks } = await r.getRankListWithMyRank({guildCode}); if(!myRank) { myRank = await r.generMyRankWithGuild(guildCode, 0, 0); } @@ -217,7 +217,7 @@ export async function getWoodenHorseList(guildCode: string, serverId: number) { let obj = getRaceActivityObj(); let r = new Rank(REDIS_KEY.RACE_ACTIVITY, {serverId}, true, 6, 5); - let myRank = await r.getMyRank(guildCode); + let myRank = await r.getMyRank({guildCode}); let startRank = myRank - 2 > 0? myRank - 2: 1; let endRank = startRank + 4; let range = await r.getRankByRange(startRank, endRank); @@ -362,8 +362,8 @@ export async function gateActivitySettleReward(guildCode: string, serverId: numb let { gateHp, members } = obj.getObj(guildCode, serverId); let r = new Rank(REDIS_KEY.GATE_ACTIVITY, { serverId }, true); - let rank = await r.getMyRank(guildCode); - let guildScore = await r.getMyScore(guildCode); + let rank = await r.getMyRank({guildCode}); + let guildScore = await r.getMyScore({guildCode}); let rewards = getGuildAuctionRewards(GUILD_ACTIVITY_TYPE.GATE_ACTIVITY, rank); @@ -654,7 +654,7 @@ export async function raceActivitySettleReward(guildCode: string, woodenHorse: W // 计算排名,计算耐久,发送奖励 let r = new Rank(REDIS_KEY.RACE_ACTIVITY, { serverId }, true); let isSuccess = distance >= GUILDACTIVITY.RACEACTIVITY_LENGTH; // 血条未击破则没有占领军团 - let myGuildRank = await r.getMyRank(guildCode); + let myGuildRank = await r.getMyRank({guildCode}); let members = obj.getMembersOfGuild(guildCode); let rewards = getGuildAuctionRewards(GUILD_ACTIVITY_TYPE.RACE_ACTIVITY, myGuildRank); diff --git a/game-server/app/services/guildService.ts b/game-server/app/services/guildService.ts index 2743bf219..c148d32b9 100644 --- a/game-server/app/services/guildService.ts +++ b/game-server/app/services/guildService.ts @@ -175,9 +175,8 @@ export async function addActive(roleId: string, serverId: number, id: number, ty guild = await GuildModel.updateInfo(guildCode, { activeUpdateTime: nowSeconds() }, { activeDaily: active, activeWeekly: active }); // 排行榜更新 - let leader = await RoleModel.findById(guild.leader); let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); - await r.setRankWithGuildInfo(guildCode, guild.activeWeekly, guild.activeUpdateTime * 1000, guild); + await r.setRankWithGuildInfo(guildCode, guild.activeWeekly, guild.activeUpdateTime, guild); pinus.app.rpc.chat.guildRemote.updateInfo.toServer(CHAT_SERVER, guildCode, { activeDaily: guild.activeDaily, activeWeekly: guild.activeWeekly }); return { status: 1, guild, userGuild }; } @@ -267,7 +266,7 @@ export async function settleGuildWeekly() { // 转换周活跃奖励 let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); - let rank = await r.getMyRank(code); + let rank = await r.getMyRank({guildCode: code}); let allWeeklyReward = getGuildActiveWeekReward(rank); for(let [roleId, job] of members) { let jobActiveRatio = gameData.guildPosition.get(job).activeRatio; diff --git a/game-server/app/services/normalBattleService.ts b/game-server/app/services/normalBattleService.ts index 51f5f693f..3197b3c94 100644 --- a/game-server/app/services/normalBattleService.ts +++ b/game-server/app/services/normalBattleService.ts @@ -6,7 +6,8 @@ import { updateUserInfo } from './redisService'; import { switchOnFunc } from './funcSwitchService'; import { FUNC_OPT_TYPE } from '../consts'; import { BackendSession } from 'pinus'; -import { REDIS_KEY } from '../consts/consts'; +import { REDIS_KEY } from '../consts'; +import { Rank } from './rankService'; export async function roleLevelup(roleId: string, kingExp: number, session: BackendSession) { let role = await RoleModel.findByRoleId(roleId); @@ -24,7 +25,10 @@ export async function roleLevelup(roleId: string, kingExp: number, session: Back role = await RoleModel.levelup(roleId, newLv, newExp); if(newLv > lv) { // 升级 await switchOnFunc(roleId, FUNC_OPT_TYPE.LEVEL_UP, newLv, session); - await updateUserInfo(REDIS_KEY.USER_INFO, roleId, [{field: 'lv', value: newLv}]) + await updateUserInfo(REDIS_KEY.USER_INFO, roleId, [{field: 'lv', value: newLv}]); + + let r = new Rank(REDIS_KEY.USER_LV, { serverId: role.serverId }); + await r.setRankWithRoleInfo(roleId, newLv, Date.now(), role); } let actordata = []; for(let i = lv; i <= newLv; i++) { diff --git a/game-server/app/services/playerCeService.ts b/game-server/app/services/playerCeService.ts index ccb5c0d7a..ffd9d06ce 100644 --- a/game-server/app/services/playerCeService.ts +++ b/game-server/app/services/playerCeService.ts @@ -10,12 +10,14 @@ import { calPlayerCeAndSave as pubCalPlayerCeAndSave, reCalAllHeroCe } from '.. import Hero, { HeroType, HeroUpdate } from '../db/Hero'; import { defaultHeroes } from './pvpService'; import { RoleUpdate } from '../db/Role'; -import { redisUserInfoUpdate } from './redisService'; +import { Rank, updateUserInfo } from './rankService'; + import { REDIS_KEY } from '../consts'; //修改并下发战力 export async function calPlayerCeAndSave(type: number, sid: string, roleId: string, originHero: HeroType, update: HeroUpdate, args?: Array) { - let {role, pushHeros, topLineupCe, hero, guild} = await pubCalPlayerCeAndSave(type, roleId, originHero, update, args); + let {role, pushHeros, topLineupCe, hero, guild, serverId} = await pubCalPlayerCeAndSave(type, roleId, originHero, update, args); + console.log(JSON.stringify(pushHeros)) //下发战力 let uids = [{ uid: roleId, sid }]; @@ -23,21 +25,57 @@ export async function calPlayerCeAndSave(type: number, sid: string, roleId: stri defaultHeroes(role); if(guild) { - await redisUserInfoUpdate(REDIS_KEY.GUILD_INFO, guild.code, [{ field: 'guildCe', value: guild.guildCe }]); + await updateUserInfo(REDIS_KEY.GUILD_INFO, guild.code, [{ field: 'guildCe', value: guild.guildCe }]); } + // 最强阵容 + let r = new Rank(REDIS_KEY.TOP_LINEUP_RANK, { serverId }, false, 0); + await r.setRankWithRoleInfo(roleId, reduceCe(topLineupCe), 0, role); + + // 最强武将 + for(let { hid, ce } of pushHeros) { + let r2 = new Rank(REDIS_KEY.TOP_HERO_RANK, { serverId }, false, 0); + await r2.setRankWithHeroInfo(roleId, hid, ce, 0, hero); + + let r4 = new Rank(REDIS_KEY.HERO_RANK, { serverId, hid }, false, 0); + await r4.setRankWithHeroInfo(roleId, hid, ce, 0, hero); + } + + // 总战力 + let r3 = new Rank(REDIS_KEY.SUM_CE_RANK, { serverId }, false, 0); + await r3.setRankWithRoleInfo(roleId, reduceCe(role.ce), 0, role); + return hero; } export async function calAllHeroCe(type:number, sid: string, roleId: string, update: RoleUpdate, args?:Array) { - let {role, ce, pushHeros, topLineupCe, guild } = await reCalAllHeroCe(type, roleId, update, args); + let {role, ce, pushHeros, topLineupCe, guild, serverId } = await reCalAllHeroCe(type, roleId, update, args); + if(pushHeros.length > 0) { let uids = [{ uid: roleId, sid }]; pinus.app.get('channelService').pushMessageByUids('onPlayerCeUpdate', resResult(STATUS.SUCCESS, { ce: reduceCe(ce), heros: pushHeros, topLineupCe: reduceCe(topLineupCe) }), uids); if(guild) { - await redisUserInfoUpdate(REDIS_KEY.GUILD_INFO, guild.code, [{ field: 'guildCe', value: guild.guildCe }]); + await updateUserInfo(REDIS_KEY.GUILD_INFO, guild.code, [{ field: 'guildCe', value: guild.guildCe }]); } + + // 最强阵容 + let r = new Rank(REDIS_KEY.TOP_LINEUP_RANK, { serverId }, false, 0); + await r.setRankWithRoleInfo(roleId, reduceCe(topLineupCe), 0, role); + + // 最强武将 + for(let { hid, ce } of pushHeros) { + let r2 = new Rank(REDIS_KEY.TOP_HERO_RANK, { serverId }, false, 0); + await r2.setRankWithHeroInfo(roleId, hid, ce, 0); + + let r4 = new Rank(REDIS_KEY.HERO_RANK, { serverId, hid }, false, 0); + await r4.setRankWithHeroInfo(roleId, hid, ce, 0); + } + + // 总战力 + let r3 = new Rank(REDIS_KEY.SUM_CE_RANK, { serverId }, false, 0); + await r3.setRankWithRoleInfo(roleId, reduceCe(role.ce), 0, role); + } return role; } \ No newline at end of file diff --git a/game-server/app/services/pvpService.ts b/game-server/app/services/pvpService.ts index d00c16934..9a7388106 100644 --- a/game-server/app/services/pvpService.ts +++ b/game-server/app/services/pvpService.ts @@ -115,11 +115,11 @@ export async function matchPlayerByRank(seasonNum: number, oppPlayers: OppPlayer let r = new Rank(REDIS_KEY.PVP_RANK, {}); let ridRanks = new Array(); // 已经被使用了的排名 for(let { roleId: curRoleId } of oppPlayers) { - let rankLv = await r.getMyRank(robotIdComBack(curRoleId)); + let rankLv = await r.getMyRank({ roleId: robotIdComBack(curRoleId)}); ridRanks.push(rankLv); } - let myRank = await r.getMyRank(roleId); + let myRank = await r.getMyRank({roleId}); ridRanks.push(myRank); let oppRoleId = ''; @@ -213,7 +213,7 @@ async function generPlayerOppHis(pvpdefense: PvpDefenseType, mapWarJson: DicWarJ let { heroScores, heroes: defenseHeroes } = pvpdefense; let role = pvpdefense.role; let r = new Rank(REDIS_KEY.PVP_RANK, {}); - let rankLv = await r.getMyRank(role.roleId); + let rankLv = await r.getMyRank({ roleId: role.roleId }); let dbHeroes = await HeroModel.findByRole(role.roleId, [{field: 'ce', sortBy: -1}]); let heroes = new Array(); let otherHeroes = new Array(); // 阵容外的所有武将信息 diff --git a/game-server/app/services/rankService.ts b/game-server/app/services/rankService.ts index e71adaaad..14021e0c7 100644 --- a/game-server/app/services/rankService.ts +++ b/game-server/app/services/rankService.ts @@ -1,8 +1,13 @@ -import { KeyName, KeyNameParam, RankParam, GuildRankParam, RoleRankInfo, GuildRankInfo, GuildLeader } from "../domain/rank"; -import { REDIS_RANK_TO_INFO, ROLE_SELECT, GUILD_SELECT, REDIS_KEY } from "../consts"; +import { KeyName, KeyNameParam, RankParam, GuildRankParam, RoleRankInfo, GuildRankInfo, GuildLeader, LineupParam, myIdInter } from "../domain/rank"; +import { REDIS_RANK_TO_INFO, ROLE_SELECT, GUILD_SELECT, REDIS_KEY, REDIS_RANK_TO_EXTRA, HERO_SELECT, COMPOSE_FIELD_TYPE, KEY_TO_COMPOSE_FIELD } from "../consts"; import { redisClient, setUserInfo } from "./redisService"; import { RoleType, RoleModel } from "../db/Role"; import { GuildType, GuildModel } from "../db/Guild"; +import Hero, { HeroModel, HeroType } from "../db/Hero"; +import { SystemConfigModel } from "../db/SystemConfig"; +import { PvpDefenseModel } from "../db/PvpDefense"; +import { gameData } from "../pubUtils/data"; + /** * @description 排行榜相关操作 @@ -13,25 +18,27 @@ export class Rank { key: string; // 排行榜原始key keyName: KeyName; // 拼接之后的key infoKey: string; // 玩家数据key + extraKeys: string[]; isUnion: boolean; // 是否使用多个zset联合计算 limit: number = 200; // 排行榜长度 timelen: number = 10; // 给时间位留的长度 unionRankLife: number = 10; - + constructor(key: string, keyParam: KeyNameParam, isUnion = false, timelen = 10, limit = 200) { this.key = key; this.keyName = new KeyName(key, keyParam); this.infoKey = REDIS_RANK_TO_INFO.get(key); + this.extraKeys = REDIS_RANK_TO_EXTRA.get(key) || []; this.isUnion = isUnion; this.timelen = timelen; this.limit = limit; } - private async generFields(obj: GuildRankInfo|RoleRankInfo) { + private async generFields(obj: GuildRankInfo | RoleRankInfo) { return obj; } - public setGenerFieldsFun(cb: (obj: GuildRankInfo|RoleRankInfo) => any) { + public setGenerFieldsFun(cb: (obj: GuildRankInfo | RoleRankInfo) => any) { this.generFields = cb; } @@ -45,41 +52,162 @@ export class Rank { return result; } - public async setRankWithRoleInfo(roleId: string, score: number, timestamp: number, role?: RoleType, needReduceCe = false, isInc = true ) { + public async setRankWithRoleInfo(roleId: string, score: number, timestamp: number, role?: RoleType, isInc = true) { // 如果没有信息,更新玩家信息 - const hasCurUser = await redisClient().hexistsAsync(this.infoKey, roleId); - if(!hasCurUser) { - if(!role) { - role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); - needReduceCe = true; + for (let infoKey of [this.infoKey, ...this.extraKeys]) { + const hasCurUser = await redisClient().hexistsAsync(infoKey, roleId); + if (!hasCurUser) { + await this.generParamAndSet(infoKey, { roleId }, { role }); } - let param = new RankParam(role, needReduceCe); - await setUserInfo(this.infoKey, roleId, param); } - let newScore = await this.setRank(roleId, score, timestamp, isInc); + let newScore = await this.setRank({ roleId }, score, timestamp, isInc); return newScore; } public async setRankWithGuildInfo(guildCode: string, score: number, timestamp: number, guild?: GuildType, isInc = true) { - const hasCurUser = await redisClient().hexistsAsync(this.infoKey, guildCode); - if(!hasCurUser) { - if(!guild) { - guild = await GuildModel.findByCode(guildCode, this.keyName.serverId, GUILD_SELECT.RANK) + for (let infoKey of [this.infoKey, ...this.extraKeys]) { + const hasCurUser = await redisClient().hexistsAsync(infoKey, guildCode); + if (!hasCurUser) { + await this.generParamAndSet(infoKey, { guildCode }, { guild }); } - let param = new GuildRankParam(guild); - await setUserInfo(this.infoKey, guildCode, param); } - let newScore = await this.setRank(guildCode, score, timestamp, isInc); + let newScore = await this.setRank({ guildCode }, score, timestamp, isInc); return newScore; } - public async setRank(myId: string, score: number, timestamp: number, isInc = false) { - + public async setRankWithHeroInfo(roleId: string, hid: number, score: number, timestamp: number, hero?: HeroType, isInc = true) { + // 如果没有信息,更新玩家信息 + for (let infoKey of [this.infoKey, ...this.extraKeys]) { + const hasCurUser = await redisClient().hexistsAsync(infoKey, roleId); + if (!hasCurUser) { + await this.generParamAndSet(infoKey, { roleId, hid }, { hero }); + } + } + let newScore = await this.setRank({ roleId, hid }, score, timestamp, isInc); + + return newScore; + } + + private composeFields(key: string, param: myIdInter) { + let type = KEY_TO_COMPOSE_FIELD.get(key); + + if (type == COMPOSE_FIELD_TYPE.ROLE) { + return param.roleId; + } else if (type == COMPOSE_FIELD_TYPE.GUILD) { + return param.guildCode; + } else if (type == COMPOSE_FIELD_TYPE.ROLE_HERO) { + return `${param.roleId}:${param.hid}`; + } else { + return ''; + } + } + + private decodeFields(key: string, field: string) { + let type = KEY_TO_COMPOSE_FIELD.get(key); + + let arr = field.split(':'); + if (type == COMPOSE_FIELD_TYPE.ROLE) { + return { roleId: arr[0] }; + } else if (type == COMPOSE_FIELD_TYPE.GUILD) { + return { guildCode: arr[0] }; + } else if (type == COMPOSE_FIELD_TYPE.ROLE_HERO) { + return { roleId: arr[0], hid: parseInt(arr[1]) }; + } else { + return {}; + } + } + + private async generParamAndSet(infoKey: string, fields: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroType }) { + let { roleId, guildCode, hid } = fields; + let { role, guild, hero } = db; + + if (infoKey == REDIS_KEY.USER_INFO) { + if (!role) { + role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); + } + let param = new RankParam(role); + await this.setUserInfo(infoKey, { roleId }, param); + } else if (infoKey == REDIS_KEY.GUILD_INFO) { + if (!guild) { + guild = await GuildModel.findByCode(guildCode, this.keyName.serverId, GUILD_SELECT.RANK) + } + let param = new GuildRankParam(guild); + await this.setUserInfo(infoKey, { guildCode }, param); + } else if (infoKey == REDIS_KEY.TOP_LINEUP_INFO) { + if (!role) { + role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); + } + let { topLineup = [] } = role; + let heroes = await HeroModel.findByRole(roleId, [], HERO_SELECT.RANK_LINEUP); + let arr = new Array(); + for (let { hid } of topLineup) { + let curHero = heroes.find(cur => cur.hid == hid); + if (curHero) { + let param = new LineupParam(curHero); + arr.push(param); + } + } + await this.setUserInfo(infoKey, { roleId }, arr); + } else if (infoKey == REDIS_KEY.HERO_INFO) { + if (!hero) { + hero = await HeroModel.findByHidAndRole(hid, roleId, HERO_SELECT.RANK_LINEUP, true); + } + let arr = new Array(); + if (hero) { + let param = new LineupParam(hero); + arr.push(param); + await this.setUserInfo(infoKey, { roleId, hid }, arr); + } + } else if (infoKey == REDIS_KEY.DUNGEON_LINEUP) { + if (!role) { + role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); + } + let { dungeonWarId, dungeonHeroes } = role; + if(dungeonWarId > 0) { + let cur = dungeonHeroes.find(cur => cur.battleId == dungeonWarId); + let lineup = cur?cur.heroes: []; + let heroes = await HeroModel.findByRole(roleId, [], HERO_SELECT.RANK_LINEUP); + let arr = new Array(); + for (let seqId of lineup) { + let curHero = heroes.find(cur => cur.seqId == seqId); + if (curHero) { + let param = new LineupParam(curHero); + arr.push(param); + } + } + await this.setUserInfo(infoKey, { roleId }, arr); + } + } else if (infoKey == REDIS_KEY.SHOW_LINEUP) { + if (!role) { + role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); + } + let { showLineup, topLineup = [] } = role; + let heroes = await HeroModel.findByRole(roleId, [], HERO_SELECT.RANK_LINEUP); + let arr = new Array(); + if(!showLineup) showLineup = topLineup.map(cur => cur.hid); + for (let hid of showLineup) { + let curHero = heroes.find(cur => cur.hid == hid); + if (curHero) { + let param = new LineupParam(curHero); + arr.push(param); + } + } + await this.setUserInfo(infoKey, { roleId }, arr); + } + } + + private async setUserInfo(infoKey: string, myId: myIdInter, param: RankParam | GuildRankParam | LineupParam[]) { + let field = this.composeFields(infoKey, myId); + await setUserInfo(infoKey, field, param); + } + + public async setRank(myId: myIdInter, score: number, timestamp: number, isInc = false) { + // 更新分数 let newScore = score; - if(this.isUnion) { + if (this.isUnion) { newScore = await this.updateRankScoreAtom(myId, score, timestamp, isInc); } else { newScore = await this.updateRankScoreEncode(myId, score, timestamp, isInc) @@ -93,16 +221,16 @@ export class Rank { * @param score * @param timestamp */ - private async updateRankScoreEncode(myId: string, score: number, timestamp: number, isInc = false) { + private async updateRankScoreEncode(myId: myIdInter, score: number = 0, timestamp: number = 0, isInc = false) { let newScore = score; let key = this.keyName.getName(); - if(isInc) { + if (isInc) { let oldScore = await this.getMyScore(myId); newScore = oldScore + score; } let scoreStr = this.encodeScore(newScore, timestamp); - await redisClient().zaddAsync(key, scoreStr, myId); + await redisClient().zaddAsync(key, scoreStr, this.composeFields(this.key, myId)); return score } @@ -114,24 +242,25 @@ export class Rank { * @param isInc * @class Rank */ - private async updateRankScoreAtom(myId: string, score: number, timestamp: number, isInc = false) { + private async updateRankScoreAtom(myId: myIdInter, score: number = 0, timestamp: number = 0, isInc = false) { let key = this.keyName.getName(); let timeKey = this.keyName.getTimeName(); - + let pow = Math.pow(10, this.timelen + 1); let newScore = 0; // 分数zset - if(isInc) { - newScore = await redisClient().zincrbyAsync(key, score, myId); + if (isInc) { + newScore = await redisClient().zincrbyAsync(key, score, this.composeFields(this.key, myId)); } else { - newScore = await redisClient().zaddAsync(key, score, myId); + newScore = await redisClient().zaddAsync(key, score, this.composeFields(this.key, myId)); } // 时间zset - await redisClient().zaddAsync(timeKey, pow - 1 - Math.floor(timestamp/1000), myId); - + await redisClient().zaddAsync(timeKey, pow - 1 - this.handleTimestamp(timestamp), this.composeFields(this.key, myId)); + return parseInt(newScore.toString()); } + /** * @description 设置到期时间 * @param time 到期时间,10位时间戳 @@ -145,58 +274,202 @@ export class Rank { await redisClient().expireatAsync(timeKey, time); } - public async generMyRankWithRole(roleId: string, score: number, time: number, role?: RoleType, needReduceCe = false) { + public async generMyRankWithRole(roleId: string, score: number, time: number, role?: RoleType) { // 如果没有信息,更新玩家信息 + let param: RoleRankInfo; + let hasCurUser = await redisClient().hexistsAsync(this.infoKey, roleId); - if(!hasCurUser) { - if(!role) { + if (!hasCurUser) { + if (!role) { role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); - needReduceCe = true; } - let param = new RoleRankInfo(role, needReduceCe); - param.setInfo(0, roleId, score, time); - return await this.generFields(param); + param = new RoleRankInfo(role); + param.setInfo(0, { roleId }, score, time); } else { const info = await redisClient().hgetAsync(this.infoKey, roleId); const userInfo = JSON.parse(info); - let param = new RoleRankInfo(userInfo); - param.setInfo(0, roleId, score, time); - return await this.generFields(param); + param = new RoleRankInfo(userInfo); + param.setInfo(0, { roleId }, score, time); } + + for (let extraKey of this.extraKeys) { + await this.setExInfoToParam(extraKey, param, { roleId }, {role}); + } + + return await this.generFields(param); } public async generMyRankWithGuild(guildCode: string, score: number, time: number, guild?: GuildType) { // 如果没有信息,更新玩家信息 + let param: GuildRankInfo; + let hasCurUser = await redisClient().hexistsAsync(this.infoKey, guildCode); - if(!hasCurUser) { - if(!guild) { + if (!hasCurUser) { + if (!guild) { guild = await GuildModel.findByCode(guildCode, this.keyName.serverId, GUILD_SELECT.RANK); } - let param = new GuildRankInfo(guild); - param.setInfo(0, guildCode, score, time); - return await this.generFields(param); + param = new GuildRankInfo(guild); + param.setInfo(0, { guildCode }, score, time); } else { const info = await redisClient().hgetAsync(this.infoKey, guildCode); const guildInfo = JSON.parse(info); - let param = new GuildRankInfo(guildInfo); - param.setInfo(0, guildCode, score, time); - return await this.generFields(param); + param = new GuildRankInfo(guildInfo); + param.setInfo(0, { guildCode }, score, time); + } + + for (let extraKey of this.extraKeys) { + await this.setExInfoToParam(extraKey, param, { guildCode }, { guild } ); + } + + return await this.generFields(param); + } + + public async generMyRankWithHero(roleId: string, hid: number, score: number, time: number, hero?: HeroType, role?: RoleType) { + // 如果没有信息,更新玩家信息 + let param: RoleRankInfo; + + let hasCurUser = await redisClient().hexistsAsync(this.infoKey, roleId); + if (!hasCurUser) { + if(!role) { + role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); + } + param = new RoleRankInfo(role); + param.setInfo(0, { roleId, hid }, score, time); + } else { + const info = await redisClient().hgetAsync(this.infoKey, roleId); + const userInfo = JSON.parse(info); + + param = new RoleRankInfo(userInfo); + param.setInfo(0, { roleId, hid }, score, time); + } + + for (let extraKey of this.extraKeys) { + await this.setExInfoToParam(extraKey, param, { roleId, hid }, { hero, role }); + } + + return await this.generFields(param); + } + + private async setExInfoToParam(extraKey: string, param: RoleRankInfo | GuildRankInfo, myId: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroType }) { + let { role, guild, hero } = db; + if (extraKey == REDIS_KEY.TOP_LINEUP_INFO) { + param = param; + + let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId)); + if (!hasCurUser) { + if (!role) { + role = await RoleModel.findByRoleId(myId.roleId, ROLE_SELECT.RANK, true); + } + + let { topLineup } = role; + let heroes = await HeroModel.findByRole(myId.roleId, [], HERO_SELECT.HERO_DETAIL); + let arr = new Array(); + for (let { hid } of topLineup) { + let curHero = heroes.find(cur => cur.hid == hid); + if (curHero) { + let param = new LineupParam(curHero); + arr.push(param); + } + } + param.setTopLine(arr); + } else { + const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId)); + const infoObj = JSON.parse(info); + param.setTopLine(infoObj); + } + } else if (extraKey == REDIS_KEY.HERO_INFO) { + param = param; + + let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId)); + if (!hasCurUser) { + if(!hero) { + hero = await HeroModel.findByHidAndRole(myId.hid, myId.roleId, HERO_SELECT.RANK_LINEUP); + } + let arr = new Array(); + let lineParam = new LineupParam(hero); + arr.push(lineParam); + if(this.key == REDIS_KEY.HERO_RANK) { + param.setSingleHero(arr); + } else { + param.setTopLine(arr); + } + } else { + const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId)); + const infoObj = JSON.parse(info); + if(this.key == REDIS_KEY.HERO_RANK) { + param.setSingleHero(infoObj); + } else { + param.setTopLine(infoObj); + } + } + } else if (extraKey == REDIS_KEY.DUNGEON_LINEUP) { + param = param; + + let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId)); + if (!hasCurUser) { + if (!role) { + role = await RoleModel.findByRoleId(myId.roleId, ROLE_SELECT.RANK, true); + } + let { dungeonWarId, dungeonHeroes } = role; + let cur = dungeonHeroes.find(cur => cur.battleId == dungeonWarId); + let lineup = cur?cur.heroes: []; + let heroes = await HeroModel.findByRole(myId.roleId, [], HERO_SELECT.RANK_LINEUP); + let arr = new Array(); + for (let seqId of lineup) { + let curHero = heroes.find(cur => cur.seqId == seqId); + if (curHero) { + let param = new LineupParam(curHero); + arr.push(param); + } + } + param.setTopLine(arr); + } else { + const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId)); + const infoObj = JSON.parse(info); + param.setTopLine(infoObj); + } + } else if (extraKey == REDIS_KEY.SHOW_LINEUP) { + param = param; + + let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId)); + if (!hasCurUser) { + if (!role) { + role = await RoleModel.findByRoleId(myId.roleId, ROLE_SELECT.RANK, true); + } + + let { showLineup, topLineup = [] } = role; + let heroes = await HeroModel.findByRole(myId.roleId, [], HERO_SELECT.HERO_DETAIL); + let arr = new Array(); + if(!showLineup) showLineup = topLineup.map(cur => cur.hid); + for (let hid of showLineup) { + let curHero = heroes.find(cur => cur.hid == hid); + if (curHero) { + let param = new LineupParam(curHero); + arr.push(param); + } + } + param.setTopLine(arr); + } else { + const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId)); + const infoObj = JSON.parse(info); + param.setTopLine(infoObj); + } } } - public async getRankListWithMyRank(myId: string) { + public async getRankListWithMyRank(myId: myIdInter) { let ranks = await this.getRankByRange(); - let newRanks = [], newMyRank; + let newRanks = [], newMyRank: RoleRankInfo | GuildRankInfo; let myRank = ranks.find(cur => { return cur.isMyInfo(myId); }); - if(this.generFields) { - for(let rank of ranks) { + if (this.generFields) { + for (let rank of ranks) { let n = await this.generFields(rank); newRanks.push(n); } - if(myRank) { + if (myRank) { newMyRank = await this.generFields(myRank); } } @@ -204,44 +477,53 @@ export class Rank { return { myRank: newMyRank, ranks: newRanks } } - public async getRankByRange(from: number|string = '+inf', to: number|string = '-inf') { + public async getRankByRange(from: number | string = '+inf', to: number | string = '-inf') { - let ranks = new Array(); + let ranks = new Array(); let key = this.keyName.getName(); - if(this.isUnion) { + if (this.isUnion) { key = await this.generateUnionRank(); } - + const rankFromDb = await redisClient().zrevrangebyscoreAsync(key, from, to, "WITHSCORES", "LIMIT", 0, this.limit); - for(let ii = 0; ii < rankFromDb.length; ii+=2) { - const myId = rankFromDb[ii]; + for (let ii = 0; ii < rankFromDb.length; ii += 2) { + const field = rankFromDb[ii]; + let myId = this.decodeFields(this.key, field); const { score, time } = this.decodeScore(rankFromDb[ii + 1]); - const info = await redisClient().hgetAsync(this.infoKey, myId); + const info = await redisClient().hgetAsync(this.infoKey, this.composeFields(this.infoKey, myId)); const userInfo = JSON.parse(info); - - if(this.infoKey == REDIS_KEY.USER_INFO) { - let param = new RoleRankInfo(userInfo); - param.setInfo(Math.floor(ii/2)+1, myId, score, time); - ranks.push(param); - } else if(this.infoKey == REDIS_KEY.GUILD_INFO) { - let param = new GuildRankInfo(userInfo); - param.setInfo(Math.floor(ii/2)+1, myId, score, time); - ranks.push(param); + + let param: RoleRankInfo | GuildRankInfo; + if (this.infoKey == REDIS_KEY.USER_INFO) { + param = new RoleRankInfo(userInfo); + param.setInfo(Math.floor(ii / 2) + 1, myId, score, time); + } else if (this.infoKey == REDIS_KEY.GUILD_INFO) { + param = new GuildRankInfo(userInfo); + param.setInfo(Math.floor(ii / 2) + 1, myId, score, time); } + + for (let extraKey of this.extraKeys) { + await this.setExInfoToParam(extraKey, param, myId, {}); + } + ranks.push(param); } return ranks } - // 获取我的排名 - public async getMyRank(myId: string) { + // 获取我的排名 + public async getMyRank(myId: myIdInter) { let key = this.keyName.getName(); - if(this.isUnion) { + if (this.isUnion) { key = await this.generateUnionRank(); } - let myRank = await redisClient().zrevrankAsync(key, myId); - return myRank + 1; + let myRank = await redisClient().zrevrankAsync(key, this.composeFields(this.key, myId)); + if(!myRank && myRank != 0) { + return 0; + } else { + return myRank + 1; + } } // 获取排名第几名的信息 @@ -253,11 +535,11 @@ export class Rank { // 获取排名第几名的信息 - public async getMyScore(myId: string) { + public async getMyScore(myId: myIdInter) { let key = this.keyName.getName(); - let score = await redisClient().zscoreAsync(key, myId); - if(!score) score = 0; - if(!this.isUnion) { + let score = await redisClient().zscoreAsync(key, this.composeFields(this.key, myId)); + if (!score) score = 0; + if (!this.isUnion) { let result = this.decodeScore(score.toString()); score = result.score; } @@ -265,9 +547,9 @@ export class Rank { } // 从排行榜中移除 - public async removeFromRank(myId: string) { + public async removeFromRank(myId: myIdInter) { let key = this.keyName.getName(); - await redisClient().zremAsync(key, myId); + await redisClient().zremAsync(key, this.composeFields(this.key, myId)); return true; } @@ -275,12 +557,12 @@ export class Rank { private async generateUnionRank() { let unionKey = this.keyName.getUnionName(); // 联合的key let existsKey = await redisClient().existsAsync(unionKey); - if(!existsKey) { + if (!existsKey) { let originKey = this.keyName.getName(); let timeKey = this.keyName.getTimeName(); - + let pow = Math.pow(10, this.timelen + 1); - await redisClient().zunionstoreAsync(unionKey, 2, originKey, timeKey, 'WEIGHTS', pow, 1); + await redisClient().zunionstoreAsync(unionKey, 2, originKey, timeKey, 'WEIGHTS', pow, 1); await redisClient().expireAsync(unionKey, this.unionRankLife); // 10秒更新一次 } return unionKey; @@ -290,7 +572,7 @@ export class Rank { private encodeScore(score: number, timestamp: number) { let timelen = this.timelen; let pow = Math.pow(10, timelen + 1); - return score * pow + pow - 1 - Math.floor(timestamp/1000) + return score * pow + pow - 1 - this.handleTimestamp(timestamp) } private decodeScore(num: string) { @@ -299,7 +581,127 @@ export class Rank { let _num = parseInt(num); return { time: pow - _num % pow, - score: Math.floor(_num/pow) + score: Math.floor(_num / pow) }; } + + private handleTimestamp(timestamp: number = 0) { + let l = timestamp.toString().length; + if(l > this.timelen) { + timestamp = Math.floor(timestamp / Math.pow(10, l - this.timelen)); + } + return timestamp + } +} + + +/** + * 从数据库内获取排行榜存入redis + * @param type 排行榜类型 + * @param serverId 分服 + */ +export async function setRankRedisFromDb(type: string, args?: {serverId?: number}) { + + if(type == REDIS_KEY.TOWER_RANK) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('tower', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }); + for(let role of ranks) { + // console.log(roleId); + await r.setRankWithRoleInfo(role.roleId, role.towerLv, role.towerUpTime?role.towerUpTime.getTime():0, role); + } + } else if (type == REDIS_KEY.GUILD_ACTIVE_RANK) { + let serverId = args.serverId; + let ranks = await GuildModel.getRank(type, serverId); + let r = new Rank(type, { serverId }); + for(let guild of ranks) { + await r.setRankWithGuildInfo(guild.code, guild.activeWeekly, guild.activeUpdateTime, guild); + } + } else if (type == REDIS_KEY.GUILD_LV_RANK) { + let serverId = args.serverId; + let ranks = await GuildModel.getRank(type, serverId); + let r = new Rank(type, { serverId }); + for(let guild of ranks) { + await r.setRankWithGuildInfo(guild.code, guild.lv, guild.lvUpdateTime, guild); + } + } else if ( type == REDIS_KEY.PVP_RANK) { + let { seasonNum } = await SystemConfigModel.findSystemConfig(); + console.log('execute season resetPvpRanks seasonNum = ' + seasonNum); + let ranks = await PvpDefenseModel.getRank(seasonNum);//获得全服前1000名的排名,加入到redis中 + let r = new Rank(type, {}); + for(let {roleId, role: _role, score, updatedAt } of ranks) { + let role = _role; + if (!role) { + continue; + } + await r.setRankWithRoleInfo(roleId, score, updatedAt.getTime(), role); + } + } else if (type == REDIS_KEY.TOP_LINEUP_RANK) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('topLineup', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }, false, 0); + for(let role of ranks) { + // console.log(roleId); + await r.setRankWithRoleInfo(role.roleId, role.topLineupCe, role.updatedAt.getTime(), role); + } + } else if (type == REDIS_KEY.TOP_HERO_RANK) { + let serverId = args.serverId; + let ranks = await HeroModel.getAllRank(serverId, HERO_SELECT.RANK_LINEUP); + let r = new Rank(type, { serverId }, false, 0); + for(let hero of ranks) { + await r.setRankWithHeroInfo(hero.roleId, hero.hid, hero.ce, hero.updatedAt.getTime(), hero); + } + } else if (type == REDIS_KEY.HERO_NUM_RANK) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('heroNum', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }); + for(let role of ranks) { + await r.setRankWithRoleInfo(role.roleId, role.heroNum, role.heroNumUpdatedAt, role ); + } + } else if (type == REDIS_KEY.USER_LV) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('lv', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }); + for(let role of ranks) { + await r.setRankWithRoleInfo(role.roleId, role.lv, role.updatedAt.getTime(), role); + } + } else if (type == REDIS_KEY.SUM_CE_RANK) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('ce', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }, false, 0); + for(let role of ranks) { + await r.setRankWithRoleInfo(role.roleId, role.ce, role.updatedAt.getTime(), role); + } + } else if (type == REDIS_KEY.DUNGEON_RANK) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('dungeon', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }); + for(let role of ranks) { + await r.setRankWithRoleInfo(role.roleId, role.dungeonWarId, role.dungeonUpdatedAt, role); + } + } else if (type == REDIS_KEY.MAIN_RANK) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('main', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }); + for(let role of ranks) { + await r.setRankWithRoleInfo(role.roleId, role.mainWarId, role.mainUpdatedAt, role); + } + } else if (type == REDIS_KEY.MAIN_ELITE_RANK) { + let serverId = args.serverId; + let ranks = await RoleModel.getRank('mainElite', serverId, ROLE_SELECT.RANK); + let r = new Rank(type, { serverId }); + for(let role of ranks) { + await r.setRankWithRoleInfo(role.roleId, role.mainEliteWarId, role.mainEliteUpdatedAt, role); + } + } else if (type == REDIS_KEY.HERO_RANK) { + let serverId = args.serverId; + + for(let hid of gameData.dicMyHeroes) { + let ranks = await HeroModel.getRank(hid, serverId, HERO_SELECT.RANK_LINEUP); + let r = new Rank(type, { serverId, hid }, false, 0); + for(let hero of ranks) { + await r.setRankWithHeroInfo(hero.roleId, hid, hero.ce, hero.updatedAt.getTime(), hero); + } + } + } } \ No newline at end of file diff --git a/game-server/app/services/redisService.ts b/game-server/app/services/redisService.ts index 19dea752d..c2ab21406 100644 --- a/game-server/app/services/redisService.ts +++ b/game-server/app/services/redisService.ts @@ -1,16 +1,12 @@ -import { GOOD_QUALITY, REDIS_RANK_TO_INFO, COM_BTL_QUALITY, GUILD_ACTIVITY_TYPE, GUILD_ACTIVITY_STATUS } from './../consts'; -import { RoleModel, RoleType } from "../db/Role"; +import { COM_BTL_QUALITY, GUILD_ACTIVITY_TYPE } from './../consts'; import * as Redis from 'redis'; import {REDIS_KEY} from '../consts' import { GameModel } from "../db/Game"; import { promisifyAll } from 'bluebird'; import { pinus } from 'pinus'; -import { PvpDefenseModel } from '../db/PvpDefense'; -import { SystemConfigModel } from '../db/SystemConfig'; -import { GuildRankParam, GuildLeader, RankParam } from '../domain/rank'; -import { GuildModel } from '../db/Guild'; +import { GuildRankParam, GuildLeader, RankParam, LineupParam } from '../domain/rank'; import { comBtlRanges } from '../pubUtils/gamedata'; -import { Rank } from './rankService'; +import { setRankRedisFromDb } from './rankService'; /** * 在服务重新启动时,将信息存入redis */ @@ -19,9 +15,22 @@ export async function initAllRank() { await delKeys(REDIS_KEY.ONLINE_USERS); await delKeys(REDIS_KEY.USER_INFO); await delKeys(REDIS_KEY.GUILD_INFO); + await delKeys(REDIS_KEY.TOP_LINEUP_INFO); + await delKeys(REDIS_KEY.HERO_INFO); await delKeys(REDIS_KEY.PVP_RANK); await delKeys(REDIS_KEY.TOWER_RANK); await delKeys(REDIS_KEY.GUILD_ACTIVE_RANK); + await delKeys(REDIS_KEY.TOP_LINEUP_RANK); + await delKeys(REDIS_KEY.TOP_HERO_RANK); + await delKeys(REDIS_KEY.HERO_NUM_RANK); + await delKeys(REDIS_KEY.USER_LV); + await delKeys(REDIS_KEY.SUM_CE_RANK); + await delKeys(REDIS_KEY.DUNGEON_RANK); + await delKeys(REDIS_KEY.DUNGEON_LINEUP); + await delKeys(REDIS_KEY.MAIN_RANK); + await delKeys(REDIS_KEY.MAIN_ELITE_RANK); + await delKeys(REDIS_KEY.HERO_RANK); + await delKeys(REDIS_KEY.SHOW_LINEUP); for(let {id} of serverList) { await initRank(id); } @@ -34,8 +43,18 @@ export async function initAllRank() { */ export async function initRank(serverId: number) { // console.log('*****', 'initRank') - await setRankRedisFromDb(REDIS_KEY.TOWER_RANK, {serverId}); - await setRankRedisFromDb(REDIS_KEY.GUILD_ACTIVE_RANK, {serverId}); + await setRankRedisFromDb(REDIS_KEY.TOWER_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.GUILD_LV_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.TOP_LINEUP_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.TOP_HERO_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.HERO_NUM_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.USER_LV, { serverId }); + await setRankRedisFromDb(REDIS_KEY.SUM_CE_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.DUNGEON_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.MAIN_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.MAIN_ELITE_RANK, { serverId }); + await setRankRedisFromDb(REDIS_KEY.HERO_RANK, { serverId }); } @@ -81,7 +100,7 @@ async function delKeys(key: string) { return keys } -export async function setUserInfo(key: string, roleId: string, params: RankParam|GuildRankParam) { +export async function setUserInfo(key: string, roleId: string, params: RankParam|GuildRankParam|LineupParam[]) { let value = JSON.stringify(params); return await redisClient().hsetAsync(key, roleId, value); } @@ -99,42 +118,6 @@ export async function updateUserInfo(key: string, roleId: string, arr: Array<{fi /********排行榜结束 */ -/** - * 从数据库内获取排行榜存入redis - * @param type 排行榜类型 - * @param serverId 分服 - */ -async function setRankRedisFromDb(type: string, args?: {serverId?: number}) { - - if(type == REDIS_KEY.TOWER_RANK) { - let serverId = args.serverId; - let ranks = await RoleModel.getRank('tower', serverId, ['roleId', 'roleName', 'towerLv', 'lv', 'vLv', 'head', 'frame', 'spine','title', 'updatedAt']); - let r = new Rank(REDIS_KEY.TOWER_RANK, { serverId }); - for(let role of ranks) { - // console.log(roleId); - await r.setRankWithRoleInfo(role.roleId, role.towerLv, role.towerUpTime?role.towerUpTime.getTime():0, role); - } - } else if (type == REDIS_KEY.GUILD_ACTIVE_RANK) { - let serverId = args.serverId; - let ranks = await GuildModel.getRank(serverId); - let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); - for(let guild of ranks) { - await r.setRankWithGuildInfo(guild.code, guild.activeWeekly, guild.activeUpdateTime * 1000, guild); - } - } else if ( type == REDIS_KEY.PVP_RANK) { - let { seasonNum } = await SystemConfigModel.findSystemConfig(); - console.log('execute season resetPvpRanks seasonNum = ' + seasonNum); - let ranks = await PvpDefenseModel.getRank(seasonNum);//获得全服前1000名的排名,加入到redis中 - let r = new Rank(REDIS_KEY.PVP_RANK, {}); - for(let {roleId, role: _role, score, updatedAt } of ranks) { - let role = _role; - if (!role) { - continue; - } - await r.setRankWithRoleInfo(roleId, score, updatedAt.getTime(), role); - } - } -} /**************** 寻宝相关 start */ diff --git a/game-server/app/services/timeTaskService.ts b/game-server/app/services/timeTaskService.ts index 83f3a1056..8090183ff 100644 --- a/game-server/app/services/timeTaskService.ts +++ b/game-server/app/services/timeTaskService.ts @@ -146,7 +146,7 @@ export async function checkResult(pvpDefense: PvpDefenseType, seasonNum: number, let pvpHeroRewards = getPvpHeroRewards(); if (!rankLv) { let r = new Rank(REDIS_KEY.PVP_RANK, {}); - rankLv = await r.getMyRank(pvpDefense.roleId);// 获得排行榜排名 + rankLv = await r.getMyRank({ roleId: pvpDefense.roleId });// 获得排行榜排名 } let oldPLv = getLvByScore(pvpDefense.heroScores);//结算前玩家的pvp等级 let { challengeCnt, challengeRefTime } = pvpDefense; diff --git a/game-server/test/rank.test.ts b/game-server/test/rank.test.ts new file mode 100644 index 000000000..849b47b94 --- /dev/null +++ b/game-server/test/rank.test.ts @@ -0,0 +1,87 @@ +import { Client } from './Client'; +import 'mocha'; +import { PinusWSClient } from 'pinus-robot-plugin'; +import { expect } from 'chai'; +import { checkSuccessResponse } from './CheckPatten'; +import { BLOCK_OPEATE, RANK_TYPE } from '../app/consts'; + +describe('排行榜测试', function() { + + let pinusClient: PinusWSClient; + let roleInfo; + + beforeEach(function(done) { + const c = new Client(); + const timer = setInterval(() => { + if (c.client) { + pinusClient = c.client; + roleInfo = c.roleInfo; + clearInterval(timer); + done(); + } + }, 500); + }); + + afterEach(function(done) { + pinusClient.disconnect(); + // disconnect 后等待 500ms,供服务器清理环境、退出频道等 + setTimeout(() => { + done(); + }, 500); + }); + + it('获取玩家最强阵容排行榜', function(done) { + pinusClient.request('role.rankHandler.getRank', { type: RANK_TYPE.TOP_LINTUP } , (res) => { + checkParamWithLineup(res); + done(); + }); + }); + + it('获取玩家最强武将排行榜', function(done) { + pinusClient.request('role.rankHandler.getRank', { type: RANK_TYPE.TOP_HERO } , (res) => { + checkParamWithLineup(res); + done(); + }); + }); +}); + +function checkParamWithLineup(res) { + checkSuccessResponse(res); + expect(res.data.type).to.be.a('number'); + expect(res.data.ranks).to.be.a('array'); + expect(res.data.myRank).to.be.an('object'); + checkRankParamWithLineup(res.data.myRank); + res.data.ranks.forEach(cur => { + checkRankParamWithLineup(cur); + }) +} + +function checkRankParamWithLineup(data) { + checkBasicParam(data); + expect(data.lineup).to.be.a('array'); + data.lineup.forEach(cur => { + checkLineup(cur); + }) +} + +function checkBasicParam(data) { + expect(data.rank).to.be.a('number'); + expect(data.num).to.be.a('number'); + expect(data.roleId).to.be.a('string'); + expect(data.roleName).to.be.a('string'); + expect(data.head).to.be.a('number'); + expect(data.frame).to.be.a('number'); + expect(data.spine).to.be.a('number'); + expect(data.lv).to.be.a('number'); + expect(data.guildName).to.be.a('string'); + expect(data.ce).to.be.a('number'); +} + +function checkLineup(hero) { + expect(hero.hid).to.be.a('number'); + expect(hero.star).to.be.a('number'); + expect(hero.colorStar).to.be.a('number'); + expect(hero.lv).to.be.a('number'); + expect(hero.quality).to.be.a('number'); + expect(hero.job).to.be.a('number'); +} \ No newline at end of file diff --git a/gm-server/app/service/users.ts b/gm-server/app/service/users.ts index 43cdf6aca..8cd83dffa 100644 --- a/gm-server/app/service/users.ts +++ b/gm-server/app/service/users.ts @@ -33,6 +33,7 @@ import { FriendShipModel } from '@db/FriendShip'; import { FriendApplyModel } from '@db/FriendApply'; import { FriendRelationModel } from '@db/FriendRelation'; import { unlockFigure } from '@pubUtils/itemUtils'; +import { nowSeconds } from '@pubUtils/timeUtil'; /** * Test Service @@ -268,6 +269,7 @@ export default class GMUsers extends Service { try { for(let heroInfo of heroInfos) { let hero = await HeroModel.createHero(heroInfo); + await RoleModel.incRoleInfo(heroInfo.roleId, { heroNum: 1 }, { heroNumUpdatedAt: nowSeconds() }); await unlockFigure(heroInfo.roleId, [{ type: FIGURE_UNLOCK_CONDITION.GET_HERO, paramHid: heroInfo.hid }]); // 解锁头像 await calPlayerCeAndSave(HERO_SYSTEM_TYPE.INIT, heroInfo.roleId, hero, {}); } diff --git a/shared/consts/constModules/selectConst.ts b/shared/consts/constModules/selectConst.ts index 0dbd7dc5d..49671ef82 100644 --- a/shared/consts/constModules/selectConst.ts +++ b/shared/consts/constModules/selectConst.ts @@ -1,7 +1,7 @@ export enum ROLE_SELECT { // 初始登录数据 - ENTRY = 'serverId userInfo.uid userInfo.tel userInfo.serverType ce topLineup topLineupCe teraphs roleId roleName tili lv exp gold coin vLv title hasGuild funcs eventStatus heads head frames frame spines spine guildCode frdCnt showLineup', + ENTRY = 'serverId userInfo.uid userInfo.tel userInfo.serverType ce topLineup topLineupCe teraphs roleId roleName tili lv exp gold coin vLv title hasGuild funcs eventStatus heads head frames frame spines spine guildCode frdCnt showLineup updatedAt', // 玩家列表显示基础数据 SHOW_SIMPLE = 'roleId roleName ce head frame spine lv title job quitTime loginTime vLv guildName serverId userInfo.serverType', // 显示申请需要的信息 @@ -14,12 +14,14 @@ export enum ROLE_SELECT { COM_BATTLE = 'lv head frame spine topLineupCe', GET_HEADS = 'heads head frames frame spines spine', // 排行榜基础数据 - RANK = 'roleId roleName lv vLv head frame spine title guildName ce' + RANK = 'roleId roleName lv vLv head frame spine title guildName ce isReducedCe topLineup towerLv towerUpTime topLineupCe heroNum updatedAt heroNumUpdatedAt dungeonWarId dungeonUpdatedAt dungeonHeroes mainWarId mainUpdatedAt mainEliteWarId mainEliteUpdatedAt showLineup', }; export enum HERO_SELECT { ENTRY = '-attr', - HERO_DETAIL = 'roleId roleName hid hName ce lv star colorStar quality job skins attr' + HERO_DETAIL = 'roleId roleName hid hName ce isReducedCe lv star colorStar quality job skins attr', + // 排行榜中lineup字段 + RANK_LINEUP = 'seqId roleId hid star colorStar lv quality job ce isReducedCe updatedAt' } export enum EQUIP_SELECT { @@ -40,7 +42,7 @@ export enum GUILD_SELECT { // 获得邀请列表 INVITED_MEMBER = '_id code isMemberMax +invitedMembers +inviteTime', // 排行榜字段 - RANK = 'code name icon lv memberCnt leader' + RANK = 'code name icon lv memberCnt leader leader activeWeekly activeUpdateTime' } export enum FRIEND_SHIP_SELECT { diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 931785781..52641f6fe 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -226,20 +226,34 @@ export const DEFAULT_HERO_LV = 1; export const FIX_SMS_CODE_TELS = ['18855953630', '13911134885', '15167549151', '15618654010', '15167549151', '18342915387', '15000250967']; export const REDIS_KEY = { - USER_INFO: "userInfo", // 玩家缓存信息 - TOWER_RANK: "towerRank", // 天梯排行榜 - COM_TEAM_SEARCH_PRE: 'comTeamSerQ', // 匹配中的玩家,按品质分 - PVP_RANK: "pvpRank", // pvp排行榜 - GUILD_INFO: "guildInfo", // 公会信息 - GUILD_ACTIVE_RANK: "guildActiveRank", // 公会周活跃排行榜 - DB_GAME: 'db_game', // 服务器列表 - ONLINE_USERS: 'onlineUsers', // 在线用户情况 - CHANNEL_SERVERS: 'chat:channelServers', // 渠道对应的 chat 服务器 Id, - USER_GATE_ACTIVITY: 'usrGateAct', // 蛮夷入侵玩家排行 - GATE_ACTIVITY: 'gateAct', // 蛮夷入侵军团排行 - USER_CITY_ACTIVITY: 'usrCityAct', // 诸侯混战玩家排行 - CITY_ACTIVITY: 'cityAct', // 诸侯混战军团排行 - RACE_ACTIVITY: 'raceAct', // 粮草先行军团排行 + USER_INFO: "userInfo", // 玩家缓存信息 + TOWER_RANK: "towerRank", // 天梯排行榜 + COM_TEAM_SEARCH_PRE: 'comTeamSerQ', // 匹配中的玩家,按品质分 + PVP_RANK: "pvpRank", // pvp排行榜 + GUILD_INFO: "guildInfo", // 公会信息 + GUILD_ACTIVE_RANK: "guildActiveRank", // 公会周活跃排行榜 + DB_GAME: 'dbGame', // 服务器列表 + ONLINE_USERS: 'onlineUsers', // 在线用户情况 + CHANNEL_SERVERS: 'chat:channelServers', // 渠道对应的 chat 服务器 Id, + USER_GATE_ACTIVITY: 'usrGateAct', // 蛮夷入侵玩家排行 + GATE_ACTIVITY: 'gateAct', // 蛮夷入侵军团排行 + USER_CITY_ACTIVITY: 'usrCityAct', // 诸侯混战玩家排行 + CITY_ACTIVITY: 'cityAct', // 诸侯混战军团排行 + RACE_ACTIVITY: 'raceAct', // 粮草先行军团排行 + TOP_LINEUP_INFO: 'topLineInfo', // 最强阵容数据 + TOP_LINEUP_RANK: 'topLineRank', // 最强阵容排行 + TOP_HERO_RANK: 'topHeroRank', // 最强武将排行 + HERO_INFO: 'heroInfo', // 最强武将信息 + HERO_NUM_RANK: 'heroNum', // 武将数量排行 + USER_LV: 'usrLv', // 主公等级排行榜 + SUM_CE_RANK: 'sumCeRank', // 总战力排名 + DUNGEON_RANK: 'dungeonRank', // 秘境排名 + DUNGEON_LINEUP: 'dungeonLineup', // 秘境通关阵容 + MAIN_RANK: 'mainRank', // 主线通关排名 + MAIN_ELITE_RANK: 'mainEliteRank', // 精英通关排名 + GUILD_LV_RANK: "guildLvRank", // 公会等级排行榜 + HERO_RANK: "heroRank", // 武将排行榜 + SHOW_LINEUP: "showLineup", // 展示阵容 } // 各排行榜对应hash的key @@ -247,13 +261,97 @@ export const REDIS_RANK_TO_INFO = new Map([ [REDIS_KEY.TOWER_RANK, REDIS_KEY.USER_INFO], [REDIS_KEY.PVP_RANK, REDIS_KEY.USER_INFO], [REDIS_KEY.GUILD_ACTIVE_RANK, REDIS_KEY.GUILD_INFO], + [REDIS_KEY.GUILD_LV_RANK, REDIS_KEY.GUILD_INFO], [REDIS_KEY.GATE_ACTIVITY, REDIS_KEY.GUILD_INFO], [REDIS_KEY.USER_GATE_ACTIVITY, REDIS_KEY.USER_INFO], [REDIS_KEY.CITY_ACTIVITY, REDIS_KEY.GUILD_INFO], [REDIS_KEY.USER_CITY_ACTIVITY, REDIS_KEY.USER_INFO], - [REDIS_KEY.RACE_ACTIVITY, REDIS_KEY.GUILD_INFO] + [REDIS_KEY.RACE_ACTIVITY, REDIS_KEY.GUILD_INFO], + [REDIS_KEY.TOP_LINEUP_RANK, REDIS_KEY.USER_INFO], + [REDIS_KEY.TOP_HERO_RANK, REDIS_KEY.USER_INFO], + [REDIS_KEY.HERO_NUM_RANK, REDIS_KEY.USER_INFO], + [REDIS_KEY.USER_LV, REDIS_KEY.USER_INFO], + [REDIS_KEY.SUM_CE_RANK, REDIS_KEY.USER_INFO], + [REDIS_KEY.DUNGEON_RANK, REDIS_KEY.USER_INFO], + [REDIS_KEY.MAIN_RANK, REDIS_KEY.USER_INFO], + [REDIS_KEY.MAIN_ELITE_RANK, REDIS_KEY.USER_INFO], + [REDIS_KEY.HERO_RANK, REDIS_KEY.USER_INFO] ]); +export const REDIS_RANK_TO_EXTRA = new Map([ + [REDIS_KEY.TOP_LINEUP_RANK, [REDIS_KEY.TOP_LINEUP_INFO]], + [REDIS_KEY.TOP_HERO_RANK, [REDIS_KEY.HERO_INFO]], + [REDIS_KEY.DUNGEON_RANK, [REDIS_KEY.DUNGEON_LINEUP]], + [REDIS_KEY.HERO_RANK, [REDIS_KEY.HERO_INFO, REDIS_KEY.SHOW_LINEUP]] +]); + +// 排行榜类型 +export enum RANK_TYPE { + TOP_LINTUP = 1, // 最强阵容战力 + TOP_HERO = 2, // 最强武将 + HERO_NUM = 3, // 武将数量 + USER_LV = 4, // 主公等级 + SUM_CE = 5, // 总战力 + TOWER = 6, // 镇念塔 + DUNGEON = 7, // 秘境 + MAIN = 8, // 主线 + MAIN_ELITE = 9, // 精英 + GUILD_LV = 10, // 军团按等级排序 + GUILD_ACTIVE = 11, // 军团活跃度排序 + HERO = 12, // 武将排行 +} + +// 接口中的排行榜类型对应的redis中的key +export const RANK_TYPE_TO_KEY = new Map([ + [RANK_TYPE.TOP_LINTUP, REDIS_KEY.TOP_LINEUP_RANK], + [RANK_TYPE.TOP_HERO, REDIS_KEY.TOP_HERO_RANK], + [RANK_TYPE.HERO_NUM, REDIS_KEY.HERO_NUM_RANK], + [RANK_TYPE.USER_LV, REDIS_KEY.USER_LV], + [RANK_TYPE.SUM_CE, REDIS_KEY.SUM_CE_RANK], + [RANK_TYPE.TOWER, REDIS_KEY.TOWER_RANK], + [RANK_TYPE.DUNGEON, REDIS_KEY.DUNGEON_RANK], + [RANK_TYPE.MAIN, REDIS_KEY.MAIN_RANK], + [RANK_TYPE.MAIN_ELITE, REDIS_KEY.MAIN_ELITE_RANK], + [RANK_TYPE.GUILD_LV, REDIS_KEY.GUILD_LV_RANK], + [RANK_TYPE.GUILD_ACTIVE, REDIS_KEY.GUILD_ACTIVE_RANK], + [RANK_TYPE.HERO, REDIS_KEY.HERO_RANK] +]); + +// field处理方法 +export enum COMPOSE_FIELD_TYPE { + ROLE = 1, + GUILD = 2, + ROLE_HERO = 3 +} + +// 各个key的field如何处理 +export const KEY_TO_COMPOSE_FIELD = new Map([ + [REDIS_KEY.USER_INFO, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.TOWER_RANK, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.PVP_RANK, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.GUILD_INFO, COMPOSE_FIELD_TYPE.GUILD], + [REDIS_KEY.GUILD_ACTIVE_RANK, COMPOSE_FIELD_TYPE.GUILD], + [REDIS_KEY.GUILD_LV_RANK, COMPOSE_FIELD_TYPE.GUILD], + [REDIS_KEY.USER_GATE_ACTIVITY, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.GATE_ACTIVITY, COMPOSE_FIELD_TYPE.GUILD], + [REDIS_KEY.USER_CITY_ACTIVITY, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.CITY_ACTIVITY, COMPOSE_FIELD_TYPE.GUILD], + [REDIS_KEY.RACE_ACTIVITY, COMPOSE_FIELD_TYPE.GUILD], + [REDIS_KEY.TOP_LINEUP_INFO, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.TOP_LINEUP_RANK, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.TOP_HERO_RANK, COMPOSE_FIELD_TYPE.ROLE_HERO], + [REDIS_KEY.HERO_INFO, COMPOSE_FIELD_TYPE.ROLE_HERO], + [REDIS_KEY.HERO_NUM_RANK, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.USER_LV, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.DUNGEON_RANK, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.DUNGEON_LINEUP, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.MAIN_RANK, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.MAIN_ELITE_RANK, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.HERO_RANK, COMPOSE_FIELD_TYPE.ROLE_HERO], + [REDIS_KEY.SHOW_LINEUP, COMPOSE_FIELD_TYPE.ROLE], +]); + + export const FUNC_OPT_TYPE = { LEVEL_UP: 1, BATTLE_END: 2 diff --git a/shared/db/Guild.ts b/shared/db/Guild.ts index fc5640e0b..bd2d54a94 100644 --- a/shared/db/Guild.ts +++ b/shared/db/Guild.ts @@ -2,7 +2,7 @@ import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType, Ref } from '@typegoose/typegoose'; import Role, { RoleType } from './Role'; import { genCode } from '../pubUtils/util'; -import { GUILD_STRUCTURE, GUILD_STATUS, GUILD_PER_PAGE, GUILD_SELECT } from '../consts'; +import { GUILD_STRUCTURE, GUILD_STATUS, GUILD_PER_PAGE, GUILD_SELECT, RANK_TYPE, REDIS_KEY } from '../consts'; import { getCurWeekTime, nowSeconds } from '../pubUtils/timeUtil'; import { reduceCe } from '../pubUtils/util'; @@ -28,6 +28,9 @@ export default class Guild extends BaseModel { @prop({ required: true, default: 1 }) lv: number; + @prop({ required: true, default: 1 }) + lvUpdateTime: number; + @prop({ required: true, default: 1 }) memberCnt: number; @@ -98,7 +101,7 @@ export default class Guild extends BaseModel { public static async createGuild(params: { name: string, icon: number, notice: string }, role: RoleType, serverId: number) { const doc = new GuildModel(); - const update = Object.assign(doc.toJSON(), params, { leader: role._id, members: [role.roleId], guildCe: role.ce, serverId }); + const update = Object.assign(doc.toJSON(), params, { leader: role._id, members: [role.roleId], guildCe: role.ce, serverId, lvUpdateTime: nowSeconds() }); delete update._id; const code = genCode(6); const result: GuildType = await GuildModel.findOneAndUpdate({ code }, update, { upsert: true, new: true }) @@ -132,12 +135,19 @@ export default class Guild extends BaseModel { } - public static async getRank(serverId: number, page: number = 1) { + public static async getRank(type: string, serverId: number, page: number = 1) { + let sort = {}, condition = {}; + switch(type) { + case REDIS_KEY.GUILD_LV_RANK: + sort = { lv: -1, lvUpdateTime: 1 }; break; + case REDIS_KEY.GUILD_ACTIVE_RANK: + sort = { activeWeekly: -1, activeUpdateTime: 1 }; condition = { activeWeekly: { $gt: 0 } }; break; + } - const guildList: GuildType[] = await GuildModel.find({ status: GUILD_STATUS.RUNNING, serverId }, {_id: 0}) - .sort({ activeWeekly: -1, activeUpdateTime: -1 }) + const guildList: GuildType[] = await GuildModel.find({ status: GUILD_STATUS.RUNNING, serverId, ...condition }, {_id: 0}) + .sort(sort) .limit(100).skip((page - 1) * 100) - .select('code icon name lv leader activeWeekly activeUpdateTime') + .select(GUILD_SELECT.RANK) .populate('leader', {roleName: 1, title: 1, frame: 1, head: 1, spine: 1, lv: 1, _id: 0}, 'Role') .lean({ virtuals: true }); return guildList; @@ -189,7 +199,7 @@ export default class Guild extends BaseModel { public static async upStructure(code: string, id: number, cost: number, select?: string) { if (id == GUILD_STRUCTURE.ARMY_CENTER) { - const result: GuildType = await GuildModel.findOneAndUpdate({ code, 'structure.id': id, fund: { $gte: cost } }, { $inc: { 'structure.$.lv': 1, lv: 1, fund: -1 * cost }, $set: { isMemberMax: false } }, { new: true }).select(select).lean(); + const result: GuildType = await GuildModel.findOneAndUpdate({ code, 'structure.id': id, fund: { $gte: cost } }, { $inc: { 'structure.$.lv': 1, lv: 1, fund: -1 * cost }, $set: { isMemberMax: false, lvUpdateTime: nowSeconds() } }, { new: true }).select(select).lean(); return result; } else { const result: GuildType = await GuildModel.findOneAndUpdate({ code, 'structure.id': id, fund: { $gte: cost } }, { $inc: { 'structure.$.lv': 1, fund: -1 * cost } }, { new: true }).select(select).lean(); diff --git a/shared/db/Hero.ts b/shared/db/Hero.ts index 0b4757b80..2a73c08cf 100644 --- a/shared/db/Hero.ts +++ b/shared/db/Hero.ts @@ -4,6 +4,7 @@ import Equip, { } from './Equip'; import { CounterModel } from './Counter'; import { COUNTER, EQUIP_TYPE } from '../consts'; import { reduceCe } from '../pubUtils/util'; +import { nowSeconds } from '../pubUtils/timeUtil'; class CeAttrData { @prop({ required: true }) @@ -90,6 +91,8 @@ export default class Hero extends BaseModel { lv: number; // 武将等级 @prop({ required: true, default: 0, set: (val: number) => val, get: (val: number) => reduceCe(val) }) ce: number; // 武将战力 + @prop({ required: false, set: (val: boolean) => val, get: () => true }) + isReducedCe: boolean; // 如果战力没有缩过就会返回false,缩过了就会返回true @prop({ required: true, default: 0 }) historyCe: number; // 武将历史最高战力 @prop({required: true, type: CeAttrData, default: [], _id: false }) @@ -190,7 +193,7 @@ export default class Hero extends BaseModel { return hero; } - public static async createHero(heroInfo: {roleId: string, serverId: number, roleName: string, hid: number, hName: string, star: number, quality: number, job: number, lv?: number, exp?: number, skins:Skin[]}, lean = true) { + public static async createHero(heroInfo: HeroUpdate, lean = true) { const doc = new HeroModel(); const seqId = await CounterModel.getNewCounter(COUNTER.HID)||-1; const update = Object.assign(doc.toJSON(), heroInfo, {seqId}); @@ -267,7 +270,17 @@ export default class Hero extends BaseModel { return user; } - public static async getRank(hid: number, serverId: number, select?: string, limit = 100) { + public static async getAllRank(serverId: number, select?: string, limit = 200) { + let result: HeroType[] = await HeroModel.find({serverId}, { _id: false} ).select(select).limit(limit).sort({ ce: -1, updatedAt: 1 }).lean({ getters: true }); + return result; + } + + public static async getMyTopHero(roleId: string, select?: string) { + let result: HeroType = await HeroModel.findOne({ roleId }, { _id: false} ).select(select).sort({ ce: -1, updatedAt: 1 }).lean({ getters: true }); + return result; + } + + public static async getRank(hid: number, serverId: number, select?: string, limit = 200) { let result: HeroType[] = await HeroModel.find({serverId, hid}, { _id: false} ).select(select).limit(limit).lean({ getters: true }); return result; } diff --git a/shared/db/Role.ts b/shared/db/Role.ts index ac084de54..a7df8f922 100644 --- a/shared/db/Role.ts +++ b/shared/db/Role.ts @@ -160,6 +160,8 @@ export default class Role extends BaseModel { topLineup: Array; // 总战力 @prop({ required: true, default: 100 }) tili: number; // 体力值 + @prop({ required: false, set: (val: boolean) => val, get: () => true }) + isReducedCe: boolean; // 如果战力没有缩过就会返回false,缩过了就会返回true @prop({ required: true, default: 0 }) vLv: number; // VIP 等级 @@ -264,6 +266,26 @@ export default class Role extends BaseModel { recFrdApplyCnt: number; // 玩家收取好友申请数量 @prop({ required: true, default: 0 }) updatedMailAt: number; + + // 排行相关 + @prop({ required: true, default: 0 }) + heroNum: number; // 武将数量 + @prop({ required: true, default: 0 }) + heroNumUpdatedAt: number; // 武将数量更新时间 + @prop({ required: true, default: 0 }) + dungeonWarId: number; // 秘境通关关卡 + @prop({ required: true, default: 0 }) + dungeonUpdatedAt: number; // 秘境通关时间 + @prop({ required: true, default: 0 }) + mainWarId: number; // 主线通关关卡 + @prop({ required: true, default: 0 }) + mainUpdatedAt: number; // 秘境通关时间 + @prop({ required: true, default: 0 }) + mainEliteWarId: number; // 精英通关关卡 + @prop({ required: true, default: 0 }) + mainEliteUpdatedAt: number; // 精英通关时间 + + public static async findAllByUid(uid: number, getters = false, virtuals = true) { const role: RoleType[] = await RoleModel.find({ 'userInfo.uid': uid }).select('roleId roleName serverId head frame spine lv updatedAt').lean({ getters, virtuals}); return role; @@ -436,13 +458,31 @@ export default class Role extends BaseModel { } // 获取排行榜 - public static async getRank(type: string, serverId: number, fields: string[], page = 1, limit = 100) { + public static async getRank(type: string, serverId: number, select?: string, page = 1, limit = 200) { let sortBy = {}; - if(type == 'tower') { - sortBy['towerLv'] = -1; - sortBy['towerUpTime'] = 1; + let condition = {}; + switch(type) { + case 'tower': + sortBy = { towerLv: -1, towerUpTime: 1 }; condition = { towerLv: { $gt: 0 } }; break; + case 'topLineup': + sortBy = { topLineupCe: -1, updatedAt: 1 }; condition = { topLineupCe: { $gt: 0 } }; break; + case 'heroNum': + sortBy = { heroNum: -1, heroNumUpdatedAt: 1 }; condition = { heroNum: { $gt: 0 } }; break; + case 'lv': + sortBy = { lv: -1, updatedAt: 1 }; condition = { lv: { $gt: 0 } }; break; + case 'ce': + sortBy = { ce: -1, updatedAt: 1 }; condition = { ce: { $gt: 0 } }; break; + case 'dungeon': + sortBy = { dungeonWarId: -1, dungeonUpdatedAt: 1 }; condition = { dungeonWarId: { $gt: 0 } }; break; + case 'main': + sortBy = { mainWarId: -1, mainUpdatedAt: 1 }; condition = { mainWarId: { $gt: 0 } }; break; + case 'mainElite': + sortBy = { mainEliteWarId: -1, mainEliteUpdatedAt: 1 }; condition = { mainEliteWarId: { $gt: 0 } }; break; + default: + sortBy = {}; break; } - const ranks: RoleType[] = await RoleModel.find({serverId}).select(fields.join(' ')).sort(sortBy).limit(limit).skip((page - 1) * limit).lean({ virtuals: true }); + + const ranks: RoleType[] = await RoleModel.find({serverId, ...condition}).select(select).sort(sortBy).limit(limit).skip((page - 1) * limit).lean({ virtuals: true, getters: true }); return ranks; } @@ -453,7 +493,7 @@ export default class Role extends BaseModel { let dungeonHeroes = role && role.dungeonHeroes; let hasHero = dungeonHeroes&&dungeonHeroes.find(cur => cur.battleId == battleId); if(!dungeonHeroes || !hasHero) { - role = await RoleModel.findOneAndUpdate({roleId}, {$push:{dungeonHeroes: {battleId, heroes}}}) + role = await RoleModel.findOneAndUpdate({roleId}, {$push:{dungeonHeroes: {battleId, heroes}}, $set: { dungeonWarId: battleId, dungeonUpdatedAt: nowSeconds() }}); } return role; } @@ -471,6 +511,22 @@ export default class Role extends BaseModel { return result; } + public static async incRoleInfo(roleId: string, inc: RoleInc, set: RoleUpdate, getters = false, virtuals = true) { + delete set._id; + let result: RoleType = await RoleModel.findOneAndUpdate({roleId}, {$inc: inc, $set: set}, {new: true}).lean({ getters, virtuals }); + return result; + } + + public static async updateMainWarId(roleId: string, mainWarId: number, mainUpdatedAt: number, getters = false, virtuals = true) { + let result: RoleType = await RoleModel.findOneAndUpdate({roleId, mainWarId: { $lt: mainWarId }}, {$set:{mainWarId, mainUpdatedAt}}, {new: true}).lean({ getters, virtuals }); + return result; + } + + public static async updateMainEliteWarId(roleId: string, mailEliteWarId: number, mainEliteUpdatedAt: number, getters = false, virtuals = true) { + let result: RoleType = await RoleModel.findOneAndUpdate({roleId, mailEliteWarId: { $lt: mailEliteWarId }}, {$set:{mailEliteWarId, mainEliteUpdatedAt}}, {new: true}).lean({ getters, virtuals }); + return result; + } + // 记录加入公会 public static async joinGuild(roleId: string, guildCode: string, guildName: string) { const result = await RoleModel.findOneAndUpdate({ roleId, hasGuild: false }, { hasGuild: true, guildCode, guildName }, { new: true }).lean(); @@ -576,6 +632,7 @@ export const RoleModel = getModelForClass(Role); export interface RoleType extends Pick, keyof Role>{}; export type RoleUpdate = Partial; // 将所有字段变成可选项 +export type RoleInc = Partial, 'heroNum'|'blockCnt'|'friendCnt'|'gold'|'coin'>>; // 初始化 function getInitialTeraph() { diff --git a/shared/domain/rank.ts b/shared/domain/rank.ts index bb191c803..946f385dc 100644 --- a/shared/domain/rank.ts +++ b/shared/domain/rank.ts @@ -4,6 +4,7 @@ import { RoleUpdate, RoleType } from "../db/Role"; import { reduceCe } from "../pubUtils/util"; import { GuildUpdateParam } from "../db/Guild"; import { currentId } from "async_hooks"; +import { HeroType } from "../db/Hero"; // 排行榜返回玩家值 export class RankParam { @@ -17,7 +18,7 @@ export class RankParam { title: number; ce: number; - constructor(role: RoleUpdate, needReduceCe = false) { + constructor(role: RoleUpdate) { this.roleName = role.roleName; this.lv = role.lv; this.vLv = role.vLv; @@ -25,11 +26,11 @@ export class RankParam { this.frame = role.frame; this.spine = role.spine; this.title = role.title; - this.guildName = role.guildName; - if(needReduceCe) { - this.ce = reduceCe(role.ce); - } else { + this.guildName = role.guildName||""; + if(role.isReducedCe) { this.ce = role.ce; + } else { + this.ce = reduceCe(role.ce); } } } @@ -39,16 +40,26 @@ export class RoleRankInfo extends RankParam { roleId: string; num: number; time: number; + hero: LineupParam[]; + lineup: LineupParam[]; - setInfo(rank: number, roleId: string, num: number, time: number) { + setInfo(rank: number, myId: myIdInter, num: number, time: number) { this.rank = rank; - this.roleId = roleId; + this.roleId = myId.roleId; this.num = num; this.time = time; } - isMyInfo(myId: string) { - return this.roleId == myId; + isMyInfo(myId: myIdInter) { + return this.roleId == myId.roleId; + } + + setTopLine(lineup: LineupParam[]){ + this.lineup = lineup; + } + + setSingleHero(hero: LineupParam[]){ + this.hero = hero; } } @@ -85,15 +96,15 @@ export class GuildRankInfo extends GuildRankParam { num: number; time: number; - setInfo(rank: number, code: string, num: number, time: number) { + setInfo(rank: number, myId: myIdInter, num: number, time: number) { this.rank = rank; - this.code = code; + this.code = myId.guildCode; this.num = num; this.time = time; } - isMyInfo(myId: string) { - return this.code == myId; + isMyInfo(myId: myIdInter) { + return this.code == myId.guildCode; } } @@ -115,6 +126,25 @@ export class GuildLeader { } } +// 排行榜返回玩家值 +export class LineupParam { + hid: number; + star: number; + colorStar: number; + lv: number; + quality: number; + job: number; + + constructor(hero: HeroType) { + this.hid = hero.hid; + this.star = hero.star; + this.colorStar = hero.colorStar; + this.lv = hero.lv; + this.quality = hero.quality; + this.job = hero.job; + } +} + export class SimpleGuildRankParam { rank: number = 0; code: string = ""; @@ -163,12 +193,14 @@ export class KeyName { serverId?: number; guildCode?: string; cityId?: number; + hid?: number; constructor(key: string, param: KeyNameParam) { this.key = key; if(param.serverId) this.serverId = param.serverId; if(param.guildCode) this.guildCode = param.guildCode; if(param.cityId) this.cityId = param.cityId; + if(param.hid) this.hid = param.hid; } public getName() { @@ -176,6 +208,7 @@ export class KeyName { if(this.serverId) res += `:${this.serverId}`; if(this.guildCode) res += `:${this.guildCode}`; if(this.cityId) res += `:${this.cityId}`; + if(this.hid) res += `:${this.hid}`; return res; } @@ -196,4 +229,11 @@ export class KeyName { } } -export type KeyNameParam = Partial; \ No newline at end of file +export type KeyNameParam = Partial; + + +export interface myIdInter { + roleId?: string; + guildCode?: string; + hid?: number; +} \ No newline at end of file diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index 6e94396f7..b1bf9723a 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -1,4 +1,4 @@ -import { dicHero } from "./dictionary/DicHero"; +import { dicHero, dicMyHeroes } from "./dictionary/DicHero"; import { dicGoods, blueprt, dicJewel, figureCondition } from "./dictionary/DicGoods"; import { dicBlueprtCompose } from "./dictionary/DicBlueprtCompose"; import { dicBlueprtPossibility } from "./dictionary/DicBlueprtPossibility"; @@ -166,7 +166,8 @@ export const gameData = { raceEventItems: decodeRaceEventItems(), shop: dicShop, shopItem: dicShopItem, - shopList: dicShopList + shopList: dicShopList, + dicMyHeroes: dicMyHeroes }; // 在此提供一些原先在gamedata中提供的方法,以便更方便获取gameData数据 diff --git a/shared/pubUtils/dictionary/DicHero.ts b/shared/pubUtils/dictionary/DicHero.ts index 100703aea..b3fc2980b 100644 --- a/shared/pubUtils/dictionary/DicHero.ts +++ b/shared/pubUtils/dictionary/DicHero.ts @@ -34,8 +34,12 @@ let arr = JSON.parse(str); type KeysEnum = { [P in keyof Required]: true }; const DicHeroKeys: KeysEnum = {heroId: true, name: true, quality: true, camp: true, jobid: true, skill: true, pieceId: true, initialStars: true, pieceCount: true, baseAbilityArr: true, baseAbilityUpArr: true, initialSkin: true}; +export const dicMyHeroes = new Array(); export const dicHero = new Map(); arr.forEach(o => { + if(o.heroId > 0 && o.heroId <= 300) { + dicMyHeroes.push(o.heroId); + } o.baseAbilityArr = parseBaseAbilityArr(o); o.baseAbilityUpArr = parseBaseAbilityUpArr(o); diff --git a/shared/pubUtils/itemUtils.ts b/shared/pubUtils/itemUtils.ts index 917246449..7bf2bcf75 100644 --- a/shared/pubUtils/itemUtils.ts +++ b/shared/pubUtils/itemUtils.ts @@ -1,17 +1,18 @@ -import { HeroModel } from '../db/Hero'; +import { HeroModel, HeroUpdate, HeroType } from '../db/Hero'; import { ItemModel } from '../db/Item'; import { EquipModel, RandSe, Holes } from './../db/Equip'; import { BagInter, EquipInter } from './interface'; import { gameData } from './data'; -import { RANDOM_SE_COUNT, FIX_ATTRIBUTES_RAN, ITID, CURRENCY_BY_TYPE, CURRENCY_TYPE, ROLE_SELECT, FIGURE_UNLOCK_CONDITION, CONSUME_TYPE } from '../consts'; +import { RANDOM_SE_COUNT, FIX_ATTRIBUTES_RAN, ITID, CURRENCY_BY_TYPE, CURRENCY_TYPE, ROLE_SELECT, FIGURE_UNLOCK_CONDITION, CONSUME_TYPE, HERO_SYSTEM_TYPE } from '../consts'; import { getRandValueByMinMax, getRandEelm } from './util'; import { findWhere } from 'underscore'; import { RoleModel, RoleType } from '../db/Role'; import { Figure } from '../domain/dbGeneral'; import { getBeforeDaySeconds } from './timeUtil'; +import { calPlayerCeAndSave } from './playerCe'; export async function addSkins(roleId: string, id: number) { let skinInfo = gameData.fashion.get(id); @@ -213,4 +214,4 @@ function unlockSingleFigure(dbFigures: Figure[], id: number, unlockDirect = fals } return figure -} +} \ No newline at end of file diff --git a/shared/pubUtils/playerCe.ts b/shared/pubUtils/playerCe.ts index 35d87890f..40ef63e49 100644 --- a/shared/pubUtils/playerCe.ts +++ b/shared/pubUtils/playerCe.ts @@ -24,7 +24,7 @@ import { DicJob } from './dictionary/DicJob'; export async function calPlayerCeAndSave(type: number, roleId: string, originHero: HeroType, update: HeroUpdate, args?: Array) { let role = await RoleModel.findByRoleId(roleId); - let { attr: roleAttrs = [] } = role; + let { attr: roleAttrs = [], serverId } = role; let heroAttrs = calPlayerCe(originHero, update, type, args); // 根据操作计算attr的增加 @@ -52,7 +52,7 @@ export async function calPlayerCeAndSave(type: number, roleId: string, originHer ce: reduceCe(heroCe), incHeroCe: reduceCe(incCe), }); - return { pushHeros, role, topLineupCe, hero, guild } + return { pushHeros, role, topLineupCe, hero, guild, serverId } } //全局属性加成 @@ -89,8 +89,10 @@ export async function reCalAllHeroCe(type: number, roleId: string, update: RoleU update.topLineupCe = topLineupCe; role = await RoleModel.updateRoleInfo(roleId, update); + let guild = await GuildModel.updateCe(roleId, allIncCe); // 公会更新战力 - return { role, pushHeros, ce: role.ce, topLineupCe: role.topLineupCe, guild } + return { role, pushHeros, ce: role.ce, topLineupCe: role.topLineupCe, guild, serverId: role.serverId } + } // 计算武将全局战力 diff --git a/web-server/app/service/Auth.ts b/web-server/app/service/Auth.ts index 7c1e7d428..8001af3c8 100644 --- a/web-server/app/service/Auth.ts +++ b/web-server/app/service/Auth.ts @@ -12,7 +12,7 @@ import { getHeroInfoById } from 'app/pubUtils/gamedata'; import { calPlayerCeAndSave, reCalAllHeroCe } from 'app/pubUtils/playerCe'; import { getExpByLv, getHeroExpByLv, gameData } from 'app/pubUtils/data'; import { isString } from 'underscore'; -import { getAge } from 'app/pubUtils/timeUtil'; +import { getAge, nowSeconds } from 'app/pubUtils/timeUtil'; import { shouldRefresh, resResult } from 'app/pubUtils/util'; import { authenticate } from 'app/pubUtils/httpUtil'; @@ -304,6 +304,7 @@ export default class Auth extends Service { let skinIds = new Array(); let conditions = new Array<{type: number, paramHid?: number, paramFavourLv?: number, paramSkinId?: number }>() + let heroNum = 0; for (let hid of DEFAULT_HEROES) { let hero = await HeroModel.findByHidAndRole(hid, roleId); if (hero) { @@ -325,8 +326,10 @@ export default class Auth extends Service { conditions.push({type: FIGURE_UNLOCK_CONDITION.GET_SKIN, paramSkinId: initialSkin}); await calPlayerCeAndSave(HERO_SYSTEM_TYPE.INIT, roleId, hero, {}); + heroNum ++; } + await RoleModel.incRoleInfo(role.roleId, { heroNum }, { heroNumUpdatedAt: nowSeconds() }); // 解锁形象 await ctx.service.utils.unlockFigure(roleId, conditions, role);