diff --git a/game-server/app/servers/guild/handler/gvgFighterHandler.ts b/game-server/app/servers/guild/handler/gvgFighterHandler.ts index fc0a7d3b1..dcfd7c38b 100644 --- a/game-server/app/servers/guild/handler/gvgFighterHandler.ts +++ b/game-server/app/servers/guild/handler/gvgFighterHandler.ts @@ -3,6 +3,7 @@ import { GVG_ACTIVE_TYPE, GVG_ITEM, GVG_PERIOD, ITEM_CHANGE_REASON, REDIS_KEY, R import { BattleRecordModel } from "../../../db/BattleRecord"; import { GVGLeagueModel } from "../../../db/GVGLeague"; import { GVGUserItemModel } from "../../../db/GVGUserItem"; +import GVGVestigeLeagueRank, { GVGVestigeLeagueRankModel } from "../../../db/GVGVestigeLeagueRank"; import { GVGVestigeLockModel } from "../../../db/GVGVestigeLock"; import { GVGVestigeRankModel } from "../../../db/GVGVestigeRank"; import { GVGVestigeRecModel, GVGVestigeRecUpdate } from "../../../db/GVGVestigeRec"; @@ -11,12 +12,13 @@ import { RoleModel } from "../../../db/Role"; import { GVGVestigeOppLineup, VestigeRank } from "../../../domain/gvgField/returnData"; import { RoleRankInfo } from "../../../domain/rank"; import { getRemoteRplFilePath, getRemoteRplPrefix } from "../../../pubUtils/battleUtils"; -import { gameData } from "../../../pubUtils/data"; +import { gameData, getGVGVestigeLeagueRank } from "../../../pubUtils/data"; import { GVG } from "../../../pubUtils/dicParam"; +import { RewardInter } from "../../../pubUtils/interface"; import { resResult } from "../../../pubUtils/util"; import { isHeroHidden } from "../../../services/dataService"; -import { calBreakGoods, checkHeroIsUsedInOtherVestige, checkVestige, checkVestigeOppStatus, checkVestigeRank, generateAttackHeroInfo, generateAttackInfo, generateDefenseInfo, getOppDetailData, getOppPlayerByRanks, getVestigeRecStatus, getVestigeUsedHeroes, isRobot, refreshVestigeOppRanks, updateMyVestigeRank } from "../../../services/gvg/gvgFightService"; -import { addGVGReward, handleGVGCost } from "../../../services/gvg/gvgItemService"; +import { calBreakGoods, checkHeroIsUsedInOtherVestige, checkVestige, checkVestigeOppStatus, checkVestigeRank, generateAttackHeroInfo, generateAttackInfo, generateDefenseInfo, getDayKeyInfo, getMyVestigeRank, getOppDetailData, getOppPlayerByRanks, getVestigeRecStatus, getVestigeRank, getVestigeUsedHeroes, isRobot, refreshVestigeOppRanks, saveScoreToRank, updateMyVestigeRank, saveVestigeRankSchedule } from "../../../services/gvg/gvgFightService"; +import { addGVGReward, combinePushItem, handleGVGCost } from "../../../services/gvg/gvgItemService"; import { addGVGActive } from "../../../services/gvg/gvgPrepareService"; import { getGroupIdOfServer, getGVGPeriodData, getGVGServerType } from "../../../services/gvg/gvgService"; import { checkBattleHeroesByHid } from "../../../services/normalBattleService"; @@ -38,6 +40,7 @@ export class GVGProduceHandler { async getVestige(msg: { vestigeId: number }, session: BackendSession) { const roleId = session.get('roleId'); const serverId = session.get('serverId'); + const guildCode = session.get('guildCode'); const { vestigeId } = msg; let { configId, period } = getGVGPeriodData(); @@ -46,20 +49,20 @@ export class GVGProduceHandler { if(!await checkVestige(serverId, vestigeId)) { return resResult(STATUS.GVG_VESTIGE_ERR); } + + const myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + let groupId = await getGroupIdOfServer(serverId); let serverType = await getGVGServerType(serverId); let dicRankMap = gameData.gvgVestige.get(vestigeId); if(!dicRankMap) return resResult(STATUS.DIC_DATA_NOT_FOUND); - let myVestigeRank = await GVGVestigeRankModel.findByRole(vestigeId, roleId); // 我在这个遗迹的排名 - if(!myVestigeRank) { - let role = await RoleModel.findByRoleId(roleId, 'roleId'); - let oppRanks = refreshVestigeOppRanks(-1); - myVestigeRank = await GVGVestigeRankModel.initRank(configId, vestigeId, groupId, serverType, role, oppRanks ); - } + let myVestigeRank = await getMyVestigeRank(configId, groupId, serverType, vestigeId, roleId, myLeague); + if(!myVestigeRank) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + let { rank, lineup, oppRanks, refreshCnt } = myVestigeRank; - let score = dicRankMap?.get(rank)||0; let lineupCe = lineup.reduce((pre, cur) => pre + cur.ce, 0); let oppPlayers = await getOppPlayerByRanks(serverId, groupId, serverType, vestigeId, oppRanks); @@ -68,7 +71,7 @@ export class GVGProduceHandler { return resResult(STATUS.SUCCESS, { vestigeId, myRank: rank, - score, + score: dicRankMap?.get(rank)?.score||0, lineup, lineupCe, oppPlayers, refreshCnt, @@ -88,12 +91,8 @@ export class GVGProduceHandler { let groupId = await getGroupIdOfServer(serverId); let serverType = await getGVGServerType(serverId); - let myVestigeRank = await GVGVestigeRankModel.findByRole(vestigeId, roleId); // 我在这个遗迹的排名 - if(!myVestigeRank) { - let role = await RoleModel.findByRoleId(roleId, 'roleId'); - let oppRanks = refreshVestigeOppRanks(-1); - myVestigeRank = await GVGVestigeRankModel.initRank(configId, vestigeId, groupId, serverType, role, oppRanks ); - } + let myVestigeRank = await getMyVestigeRank(configId, groupId, serverType, vestigeId, roleId); + if(!myVestigeRank) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); for(let { actorId } of heroes) { if(isHeroHidden(actorId)) return resResult(STATUS.HERO_IS_HIDDEN); @@ -162,13 +161,7 @@ export class GVGProduceHandler { let groupId = await getGroupIdOfServer(serverId); let serverType = await getGVGServerType(serverId); - let myVestigeRank = await GVGVestigeRankModel.findByRole(vestigeId, roleId); // 我在这个遗迹的排名 - if(!myVestigeRank) { - let role = await RoleModel.findByRoleId(roleId, 'roleId'); - let oppRanks = refreshVestigeOppRanks(-1); - myVestigeRank = await GVGVestigeRankModel.initRank(configId, vestigeId, groupId, serverType, role, oppRanks ); - } - + let myVestigeRank = await getMyVestigeRank(configId, groupId, serverType, vestigeId, roleId); let refreshCnt = myVestigeRank.refreshCnt||0; if(refreshCnt >= GVG.GVG_REFRESH_TIME) return resResult(STATUS.GVG_VESTIGE_REFESH_CNT_MAX); @@ -226,7 +219,7 @@ export class GVGProduceHandler { // 消耗征战令的获得活跃 let active = await addGVGActive(myLeague.leagueCode, roleId, GVG_ACTIVE_TYPE.COST_FIGHT_COIN); // 创建rec - const attackInfo = await generateAttackInfo(roleId, myRank); + const attackInfo = await generateAttackInfo(roleId, myLeague.leagueCode, myRank); const defenseInfo = await generateDefenseInfo(targetRoleId, vestigeId, rank); const rec = await GVGVestigeRecModel.createRec(configId, vestigeId, groupId, serverType, dicRank.warId, attackInfo, defenseInfo, leagueGoods); const oppDetail = await getOppDetailData(rec); @@ -343,14 +336,14 @@ export class GVGProduceHandler { let { rewards, leagueRewards } = calBreakGoods(rec.vestigeId, historyRank, atkData.rank); let breakGoods = await addGVGReward(roleId, roleName, myLeague.leagueCode, sid, leagueRewards, rewards, ITEM_CHANGE_REASON.GVG_VESTIGE_END); - // TODO 更新redis排行榜 + await saveScoreToRank(rec); return resResult(STATUS.SUCCESS, { vestigeId: rec.vestigeId, battleCode: rec.battleCode, leagueGoods: rec.leagueGoods, rank: atkData.rank, - score: gameData.gvgVestige.get(rec.vestigeId)?.get(atkData.rank)?.score, + score: gameData.gvgVestige.get(rec.vestigeId)?.get(atkData.rank)?.score??0, historyRank: atkData.historyRank, ...getVestigeRecStatus(rec), oppPlayers: await getOppPlayerByRanks(serverId, rec.groupId, rec.serverType, rec.vestigeId, atkData.oppRanks), @@ -381,11 +374,48 @@ export class GVGProduceHandler { // 征战中原简单小排行榜 async getLeagueRank(msg: {}, session: BackendSession) { + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + const guildCode = session.get('guildCode'); + + const myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + let groupId = await getGroupIdOfServer(serverId); + let serverType = await getGVGServerType(serverId); + let serverNames = await getAllServerName(); + + const { ranks: leagueRank, myRank: myLeagueRank } = await getVestigeRank(REDIS_KEY.GVG_VESTIGE_LEAGUE, true, { groupId, serverType, day: getDayKeyInfo() }, { leagueCode: myLeague.leagueCode }); + const { ranks: memberRank, myRank: myMemberRank } = await getVestigeRank(REDIS_KEY.GVG_VESTIGE_MEMBER_ALL, true, { groupId, serverType, day: getDayKeyInfo() }, { roleId }, serverNames); + + return resResult(STATUS.SUCCESS, { + leagueRank, myLeagueRank, memberRank, myMemberRank + }) } // 联军详细排名 async getLeagueDetailRank(msg: {}, session: BackendSession) { + const serverId = session.get('serverId'); + const guildCode = session.get('guildCode'); + const myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + let groupId = await getGroupIdOfServer(serverId); + let serverType = await getGVGServerType(serverId); + let serverNames = await getAllServerName(); + let { configId } = getGVGPeriodData(); + + const { ranks, myRank } = await getVestigeRank(REDIS_KEY.GVG_VESTIGE_LEAGUE, false, { groupId, serverType, day: getDayKeyInfo() }, { leagueCode: myLeague.leagueCode }, serverNames); + + let yesterdayRank = await GVGVestigeLeagueRank.getYesterdayRank(myLeague.leagueCode); + let canReceiveRanks = await GVGVestigeLeagueRank.getCanReceiveRanks(configId, myLeague.leagueCode); + + return resResult(STATUS.SUCCESS, { + ranks, myRank, + latestRank: yesterdayRank?.rank||0, + canReceiveBox: canReceiveRanks.length > 0 + }) } // 玩家排行 @@ -432,7 +462,42 @@ export class GVGProduceHandler { } // 领取排行榜获得的每日奖励 - async receiveBox(msg: { vestigeId: number }, session: BackendSession) { + async receiveBox(msg: { }, session: BackendSession) { + const guildCode = session.get('guildCode'); + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const sid = session.get('sid'); + const myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + let { configId } = getGVGPeriodData(); + let canReceiveRanks = await GVGVestigeLeagueRankModel.getCanReceiveRanks(configId, myLeague.leagueCode); + if(canReceiveRanks.length <= 0) return resResult(STATUS.GVG_RECEIVE_NO_RANK_REWARD); + let rewards: RewardInter[] = [], leagueReward: RewardInter[] = []; + for(let { rank } of canReceiveRanks) { + let dicRank = getGVGVestigeLeagueRank(rank); + if(!dicRank) { console.error('dic_zyz_GVGVestigeLeagueRank error'); continue; } + combinePushItem(rewards, dicRank.rankReward); + combinePushItem(leagueReward, dicRank.rankLeagueReward); + } + let leagueGoods = await addGVGReward(roleId, roleName, myLeague.leagueCode, sid, leagueReward, rewards, ITEM_CHANGE_REASON.GVG_VESTIGE_RECEIVE_RANK) + await GVGVestigeLeagueRankModel.receiveRanks(canReceiveRanks.map(cur => cur._id)); + + return resResult(STATUS.SUCCESS, { + leagueGoods, + canReceiveRanks: false + }) + } + + async debugCalRec(msg: { battleCode: string }, session: BackendSession) { + let rec = await GVGVestigeRecModel.findByBattleCode(msg.battleCode); + await saveScoreToRank(rec); + return resResult(STATUS.SUCCESS); + } + + async debugSaveScore(msg: {}, session: BackendSession) { + await saveVestigeRankSchedule(); + return resResult(STATUS.SUCCESS); } } \ No newline at end of file diff --git a/game-server/app/servers/guild/handler/gvgProduceHandler.ts b/game-server/app/servers/guild/handler/gvgProduceHandler.ts index 7a7c00c94..6e0303536 100644 --- a/game-server/app/servers/guild/handler/gvgProduceHandler.ts +++ b/game-server/app/servers/guild/handler/gvgProduceHandler.ts @@ -146,8 +146,11 @@ export class GVGProduceHandler { let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); - - await lockField(myLeague.leagueCode, roleId, roleName, farmId); + + let dicResource = gameData.gvgResource.get(farmId); + if(dicResource.type == GVG_RESOURCE_TYPE.FOOD) { + await lockField(myLeague.leagueCode, roleId, roleName, farmId); + } const myRank = await getmyDistributeRank(myLeague.leagueCode, myLeague.members, roleId); return resResult(STATUS.SUCCESS, { diff --git a/game-server/app/services/gvg/gvgBattleService.ts b/game-server/app/services/gvg/gvgBattleService.ts index ca29b5f3d..bc15cdfdb 100644 --- a/game-server/app/services/gvg/gvgBattleService.ts +++ b/game-server/app/services/gvg/gvgBattleService.ts @@ -11,7 +11,7 @@ export async function getGVGCities(league: GVGLeagueType) { /** * TODO 获取当前城池状态 - * @returns [{cityId: number, guardLeagueName: string, teamCnt: number }] + * @returns [{cityId: number, guardLeagueCode: string, guardLeagueName: string, teamCnt: number }] */ export async function getGVGCitiesInfo(league: GVGLeagueType): Promise<{cityId: number, guardLeagueName: string, teamCnt: number }[]> { return []; diff --git a/game-server/app/services/gvg/gvgFightService.ts b/game-server/app/services/gvg/gvgFightService.ts index 5a741efcf..cc4f03e4b 100644 --- a/game-server/app/services/gvg/gvgFightService.ts +++ b/game-server/app/services/gvg/gvgFightService.ts @@ -1,24 +1,30 @@ // 征战中原相关 -import { GVG_PERIOD, STATUS, VESTIGE_OPP_STATUS, VESTIGE_STATUS } from "../../consts"; +import moment = require("moment"); +import { GVG_PERIOD, REDIS_KEY, VESTIGE_OPP_STATUS, VESTIGE_STATUS } from "../../consts"; import { ArtifactModel } from "../../db/Artifact"; +import { GVGLeagueModel, GVGLeagueType } from "../../db/GVGLeague"; import { GVGVestigeModel } from "../../db/GVGVestige"; +import { GVGVestigeLeagueRankModel } from "../../db/GVGVestigeLeagueRank"; import { GVGVestigeLockModel } from "../../db/GVGVestigeLock"; import { GVGVestigeRankModel, GVGVestigeRankType, GVGVestigeRankUpdate } from "../../db/GVGVestigeRank"; import { GVGVestigeRecType } from "../../db/GVGVestigeRec"; +import { GVGVestigeSumRankModel } from "../../db/GVGVestigeSumRank"; import { HeroModel, HeroType } from "../../db/Hero"; -import { RoleModel, RoleType } from "../../db/Role"; -import { OppDetailData, OppDetailHeroData, OppPlayerHeroInfo, OppPlayerInfo } from "../../domain/gvgField/gvgDb"; +import { RoleModel } from "../../db/Role"; +import { OppDetailData, OppPlayerHeroInfo, OppPlayerInfo } from "../../domain/gvgField/gvgDb"; import { GVGVestigeOppPlayer } from "../../domain/gvgField/returnData"; +import { KeyNameParam, LeagueRankInfo, myIdInter, RoleRankInfo } from "../../domain/rank"; import { gameData, getGVGVestigeRange } from "../../pubUtils/data"; import { GVG } from "../../pubUtils/dicParam"; import { RewardInter } from "../../pubUtils/interface"; import { getTimeFun } from "../../pubUtils/timeUtil"; -import { getRandEelm, getRandValueByMinMax, resResult } from "../../pubUtils/util"; +import { getRandEelm } from "../../pubUtils/util"; import { getNumberArr, uniqueArr } from "../ladderService"; import { getHeroesAttributes } from "../playerCeService"; -import { getAllServerName } from "../redisService"; -import { combineItems } from "../role/util"; +import { Rank } from "../rankService"; +import { findKeys, getAllServerName, redisClient } from "../redisService"; +import { combinePushItem } from "./gvgItemService"; import { getGroupIdOfServer, getGVGServerType } from "./gvgService"; // 备战期的遗迹和激战期的开始结束战斗时间 @@ -147,13 +153,8 @@ export function isRobot(roleId: string) { export async function checkVestigeOppStatus(configId: number, groupId: number, serverType: number, vestigeId: number, roleId: string, myRank: number, targetRoleId: string, rank: number) { - let myData = await GVGVestigeRankModel.findByRole(vestigeId, roleId); - if(!myData) { - let role = await RoleModel.findByRoleId(roleId, 'roleId'); - let oppRanks = refreshVestigeOppRanks(-1); - myData = await GVGVestigeRankModel.initRank(configId, vestigeId, groupId, serverType, role, oppRanks ); - } - if(myData?.rank != myRank) return VESTIGE_OPP_STATUS.MY_RANK_CHANGE; + let myData = await getMyVestigeRank(configId, groupId, serverType, vestigeId, roleId); + if(!myData || myData.rank != myRank) return VESTIGE_OPP_STATUS.MY_RANK_CHANGE; if(isRobot(targetRoleId)) { let targetData = await GVGVestigeRankModel.findByRank(vestigeId, rank); @@ -196,16 +197,16 @@ export async function generateDefenseInfo(roleId: string, vestigeId: number, ran let heroes = await HeroModel.findByHidRange(hids, playerVestigeData.roleId, 'hid skinId lv star colorStar quality ce'); let info = new OppPlayerInfo(); - info.initByPlayer(playerVestigeData.rank, role, true); + info.initByPlayer(playerVestigeData.rank, role, playerVestigeData.leagueCode, true); info.setPlayerHeroes(heroes, lineup); return info; } } -export async function generateAttackInfo(roleId: string, rank: number) { +export async function generateAttackInfo(roleId: string, leagueCode: string, rank: number) { let role = await RoleModel.findByRoleId(roleId, 'roleId roleName heads head frames frame spines spine title lv'); let info = new OppPlayerInfo(); - info.initByPlayer(rank, role, false); + info.initByPlayer(rank, role, leagueCode, false); return info; } @@ -262,7 +263,7 @@ export async function updateMyVestigeRank(isChange: boolean, atkData: GVGVestige update.hasDefense = true; needUpdate = true; } - if(atkData && atkData.rank > historyRank) { // 更新历史最高排名 + if(atkData && (historyRank == 0 || atkData.rank < historyRank)) { // 更新历史最高排名 update.historyRank = atkData.rank; needUpdate = true; } @@ -285,22 +286,87 @@ export function calBreakGoods(vestige: number, historyRank: number, rank: number for(let i = historyRank + 1; i <= rank; i++) { let dicRank = dicRankMap.get(i); if(!dicRank) continue; - for(let { id, count } of dicRank.onceReward) { - let obj = rewards.find(obj => obj.id == id); - if(!obj) { - rewards.push({ id, count }); - } else { - obj.count += count; - } - } - for(let { id, count } of dicRank.onceLeagueReward) { - let obj = leagueRewards.find(obj => obj.id == id); - if(!obj) { - leagueRewards.push({ id, count }); - } else { - obj.count += count; - } - } + combinePushItem(rewards, dicRank.onceReward); + combinePushItem(leagueRewards, dicRank.onceLeagueReward); } return { rewards, leagueRewards } +} + +// 玩家当天的驻扎记录,没有的话就创建一个 +export async function getMyVestigeRank(configId: number, groupId: number, serverType: number, vestigeId: number, roleId: string, myLeague?: GVGLeagueType) { + + let myVestigeRank = await GVGVestigeRankModel.findByRole(vestigeId, roleId); // 我在这个遗迹的排名 + if(!myVestigeRank) { + let role = await RoleModel.findByRoleId(roleId, 'roleId guildCode'); + let oppRanks = refreshVestigeOppRanks(-1); + if(!myLeague) { + myLeague = await GVGLeagueModel.findLeagueByGuild(role.guildCode); + if(!myLeague) return null + } + myVestigeRank = await GVGVestigeRankModel.initRank(configId, vestigeId, groupId, serverType, role, myLeague.leagueCode, oppRanks ); + } + return myVestigeRank; +} + +// 保存排行榜 +export async function saveScoreToRank(rec: GVGVestigeRecType) { + await savePlayerRank(rec.configId, rec.groupId, rec.serverType, rec.vestigeId, rec.attackInfo); + await savePlayerRank(rec.configId, rec.groupId, rec.serverType, rec.vestigeId, rec.defenseInfo); +} + +export function getDayKeyInfo() { + return moment().format('YYMMDD'); +} + +async function savePlayerRank(configId: number, groupId: number, serverType: number, vestigeId: number, playerInfo: OppPlayerInfo) { + if(playerInfo.isRobot) return + let dicRankMap = gameData.gvgVestige.get(vestigeId); + let newRank = dicRankMap?.get(playerInfo.newRank)?.score||0; + let oldRank = dicRankMap?.get(playerInfo.oldRank)?.score||0; + let myScore = await GVGVestigeSumRankModel.incScore(playerInfo.roleId, groupId, serverType, newRank - oldRank); + let r1 = new Rank(REDIS_KEY.GVG_VESTIGE_MEMBER_ALL, { serverType, groupId, day: getDayKeyInfo() }); + await r1.setRankWithRoleInfo(playerInfo.roleId, myScore.score, myScore.updatedAt.getTime()); + let leagueScore = await GVGVestigeLeagueRankModel.incScore(configId, playerInfo.leagueCode, groupId, serverType, newRank - oldRank); + let r2 = new Rank(REDIS_KEY.GVG_VESTIGE_LEAGUE, { serverType, groupId, day: getDayKeyInfo() }); + await r2.setRankWithLeagueInfo(playerInfo.leagueCode, leagueScore.score, leagueScore.updatedAt.getTime()); +} + +export async function getVestigeRank(redisKey: REDIS_KEY, isSimple: boolean, keyParam: KeyNameParam, myId: myIdInter, serverNames?: any) { + let r = new Rank(redisKey, keyParam); + r.setGenerFieldsFun((obj => { + if(redisKey == REDIS_KEY.GVG_VESTIGE_MEMBER_ALL && isSimple && obj instanceof RoleRankInfo) { + return { rank: obj.rank, name: obj.roleName, serverName: serverNames[obj.serverId], score: obj.num } + } + if(redisKey == REDIS_KEY.GVG_VESTIGE_LEAGUE && obj instanceof LeagueRankInfo) { + if(isSimple) { + return { rank: obj.rank, name: obj.name, score: obj.num } + } else { + return { ...obj, leader: {...obj.leader, serverName: serverNames[obj.leader.serverId]}}; + } + } + return obj + })); + + let { ranks, myRank } = await r.getRankListWithMyRank(myId); + if (!myRank) { + if(redisKey == REDIS_KEY.GVG_VESTIGE_MEMBER_ALL) { + myRank = await r.generMyRankWithRole(myId.roleId, 0, 0); + } else if (redisKey == REDIS_KEY.GVG_VESTIGE_LEAGUE) { + myRank = await r.generMyRankWithLeague(myId.leagueCode, 0, 0); + } + } + return { ranks, myRank } +} + +export async function saveVestigeRankSchedule() { + let day = getDayKeyInfo(); + let keys = await findKeys(`${REDIS_KEY.GVG_VESTIGE_LEAGUE}:${day}:`); + for(let key of keys) { + let [,, _groupId, _serverType] = key.split(':'); + let groupId = parseInt(_groupId); + let serverType = parseInt(_serverType); + let r = new Rank(REDIS_KEY.GVG_VESTIGE_LEAGUE, { groupId, serverType, day}); + let ranks = await r.getRankByRangeRaw(); + await GVGVestigeLeagueRankModel.saveRank(groupId, serverType, ranks); + } } \ No newline at end of file diff --git a/game-server/app/services/gvg/gvgItemService.ts b/game-server/app/services/gvg/gvgItemService.ts index d709138cf..8f779356a 100644 --- a/game-server/app/services/gvg/gvgItemService.ts +++ b/game-server/app/services/gvg/gvgItemService.ts @@ -82,4 +82,15 @@ export function getProduceCoinCnt(items: RewardInter[]) { } } return produceCoinCnt; +} + +export function combinePushItem(targetItems: RewardInter[], dicItems: RewardInter[]) { + for(let { id, count } of dicItems) { + let obj = targetItems.find(obj => obj.id == id); + if(!obj) { + targetItems.push({ id, count }); + } else { + obj.count += count; + } + } } \ No newline at end of file diff --git a/game-server/app/services/rankService.ts b/game-server/app/services/rankService.ts index 11648e461..88bc28ac1 100644 --- a/game-server/app/services/rankService.ts +++ b/game-server/app/services/rankService.ts @@ -1,4 +1,4 @@ -import { KeyName, KeyNameParam, RankParam, GuildRankParam, RoleRankInfo, LineupParam, myIdInter, GeneralRankParam, ValueConfig, GuildRankInfo, RoleAndGuildRankInfo } from "../domain/rank"; +import { KeyName, KeyNameParam, RankParam, GuildRankParam, RoleRankInfo, LineupParam, myIdInter, GeneralRankParam, ValueConfig, GuildRankInfo, RoleAndGuildRankInfo, LeagueRankParam, LeagueRankInfo } from "../domain/rank"; import { getInfoKeyByRedisKey, ROLE_SELECT, GUILD_SELECT, REDIS_KEY, HERO_SELECT, COMPOSE_FIELD_TYPE, KEY_TO_COMPOSE_FIELD, getRedisKeyByRankType, COUNTER, RANK_TYPE, PUSH_ROUTE } from "../consts"; import { redisClient, delKeys, saveLadderDefCeByData } from "./redisService"; import { RoleType, RoleModel } from "../db/Role"; @@ -16,7 +16,10 @@ import { ActivityTimeLimitRankModel } from "../db/ActivityTimeLimitRank"; import { sendMessageToServerWithSuc } from "./pushService"; import { LadderMatchModel } from "../db/LadderMatch"; import { completeRanks } from "./ladderService"; -import { GVGVestigeRankModel } from "../db/GVGVestigeRank"; +import { GVGVestigeSumRankModel } from "../db/GVGVestigeSumRank"; +import { GVGLeagueModel, GVGLeagueType } from "../db/GVGLeague"; +import { GVGVestigeLeagueRankModel } from "../db/GVGVestigeLeagueRank"; +import { getDayKeyInfo } from "./gvg/gvgFightService"; /** @@ -111,7 +114,7 @@ export class Rank { * 返回字段修改 * @param obj */ - private async generFields(obj: GuildRankInfo | RoleRankInfo) { + private async generFields(obj: GuildRankInfo | RoleRankInfo | LeagueRankInfo) { return obj; } @@ -119,7 +122,7 @@ export class Rank { * 从外部设置返回字段 * @param cb */ - public setGenerFieldsFun(cb: (obj: GuildRankInfo | RoleRankInfo) => any) { + public setGenerFieldsFun(cb: (obj: GuildRankInfo | RoleRankInfo | LeagueRankInfo) => any) { this.generFields = cb; } @@ -189,6 +192,22 @@ export class Rank { return newScore; } + public async setRankWithLeagueInfo(leagueCode: string, score: number, timestamp: number, league?: GVGLeagueType, isInc = false) { + return await this.setRankWithLeagueInfoArrParam(leagueCode, [score, timestamp], league, isInc); + } + + public async setRankWithLeagueInfoArrParam(leagueCode: string, scores: number[], league?: GVGLeagueType, isInc = false) { + for (let infoKey of [this.infoKey, ...this.extraKeys]) { + const hasCurUser = await redisClient().hexistsAsync(infoKey, leagueCode); + if (!hasCurUser) { + await this.generParamAndSet(infoKey, { leagueCode }, { league }); + } + } + let newScore = await this.setRank({ leagueCode }, scores, isInc); + + return newScore; + } + /** * 根据武将表设置更新排行榜&玩家信息 * @param roleId 玩家id @@ -211,7 +230,7 @@ export class Rank { /** * 将玩家信息按顺序组成 如:roleId:hid * @param {{ string }} key redis key - * @param {{ roleId?: string; guildCode?: string; hid?: number }} param 玩家信息 + * @param {{ roleId?: string; guildCode?: string; hid?: number; leagueCode?: string }} param 玩家信息 */ private composeFields(key: REDIS_KEY, param: myIdInter) { let type = KEY_TO_COMPOSE_FIELD.get(key); @@ -222,6 +241,8 @@ export class Rank { return param.guildCode; } else if (type == COMPOSE_FIELD_TYPE.ROLE_HERO) { return `${param.roleId}:${param.hid}`; + } else if (type == COMPOSE_FIELD_TYPE.LEAGUE) { + return `${param.leagueCode}`; } else { return ''; } @@ -243,6 +264,8 @@ export class Rank { return { guildCode: arr[0] }; } else if (type == COMPOSE_FIELD_TYPE.ROLE_HERO) { return { roleId: arr[0], hid: parseInt(arr[1]) }; + } else if (type == COMPOSE_FIELD_TYPE.LEAGUE) { + return { leagueCode: arr[0] }; } else { return {}; } @@ -254,9 +277,9 @@ export class Rank { * @param fields 玩家id * @param db 数据库内的数据 */ - public async generParamAndSet(infoKey: string, fields: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroUpdate }) { - let { roleId, guildCode, hid } = fields; - let { role, guild, hero } = db; + public async generParamAndSet(infoKey: string, fields: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroUpdate, league?: GVGLeagueType }) { + let { roleId, guildCode, hid, leagueCode } = fields; + let { role, guild, hero, league } = db; if (infoKey == REDIS_KEY.USER_INFO) { if (!role) { @@ -272,6 +295,13 @@ export class Rank { } let param = new GuildRankParam(guild); await this.setUserInfo(infoKey, { guildCode }, param); + } else if (infoKey == REDIS_KEY.LEAGUE_INFO) { + if (!league) { + league = await GVGLeagueModel.findByCode(leagueCode); + if(!league) return; + } + let param = new LeagueRankParam(league); + await this.setUserInfo(infoKey, { leagueCode }, param); } else if (infoKey == REDIS_KEY.TOP_LINEUP_INFO) { if (!role) { role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true); @@ -345,7 +375,7 @@ export class Rank { * @param myId * @param param */ - private async setUserInfo(infoKey: REDIS_KEY, myId: myIdInter, param: RankParam | GuildRankParam | LineupParam[]) { + private async setUserInfo(infoKey: REDIS_KEY, myId: myIdInter, param: RankParam | GuildRankParam | LineupParam[] | LeagueRankParam) { let field = this.composeFields(infoKey, myId); let value = JSON.stringify(param); return await redisClient().hsetAsync(infoKey, field, value); @@ -532,6 +562,29 @@ export class Rank { return await this.generFields(param); } + public async generMyRankWithLeague(leagueCode: string, score: number, time: number, league?: GVGLeagueType) { + // 如果没有信息,更新玩家信息 + let param: LeagueRankInfo; + + let hasCurUser = await redisClient().hexistsAsync(this.infoKey, leagueCode); + if (!hasCurUser) { + if (!league) { + league = await GVGLeagueModel.findByCode(leagueCode); + if(!league) return null + } + param = new LeagueRankInfo(league); + param.setInfo(0, { leagueCode }, score, time); + } else { + const info = await redisClient().hgetAsync(this.infoKey, leagueCode); + if(!info) return null + const leagueInfo = JSON.parse(info); + param = new LeagueRankInfo(leagueInfo); + param.setInfo(0, { leagueCode }, score, time); + } + + return await this.generFields(param); + } + public async generMyRankWithHero(roleId: string, hid: number, score: number, time: number, hero?: HeroType, role?: RoleType) { // 如果没有信息,更新玩家信息 let param: RoleRankInfo; @@ -558,7 +611,7 @@ export class Rank { return await this.generFields(param); } - private async setExInfoToParam(extraKey: string, param: RoleRankInfo | GuildRankInfo, myId: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroType }) { + private async setExInfoToParam(extraKey: string, param: RoleRankInfo | GuildRankInfo | LeagueRankInfo, myId: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroType }) { let { role, guild, hero } = db; if (extraKey == REDIS_KEY.TOP_LINEUP_INFO) { @@ -668,7 +721,6 @@ export class Rank { public async getRankListWithMyRank(myId: myIdInter, isReverse = true) { let ranks = await this.getRankByRange('+inf', '-inf', isReverse); - console.log('#### ranks', ranks); let newRanks = [], newMyRank: any; let myRank = ranks.find(cur => { return cur.isMyInfo(myId); @@ -738,7 +790,7 @@ export class Rank { public async getRankByRange(max: number | string = '+inf', min: number | string = '-inf', isReverse = true) { let raws = await this.getRankByRangeRaw(max, min, isReverse); - let ranks = new Array(); + let ranks = new Array(); let l = 1; for (let { field, scores } of raws) { let param = await this.getParam(l, field, scores); @@ -756,7 +808,7 @@ export class Rank { const userInfo = JSON.parse(info); if(!userInfo) return null; - let param: RoleRankInfo | GuildRankInfo; + let param: RoleRankInfo | GuildRankInfo | LeagueRankInfo; if (this.infoKey == REDIS_KEY.USER_INFO) { if (this.key != REDIS_KEY.LADDER && this.key != REDIS_KEY.GVG_VESTIGE_MEMBER && nowSeconds() - userInfo.updatedAt > 2 * 30 * 24 * 60 * 60) { return null; @@ -771,6 +823,10 @@ export class Rank { param = new GuildRankInfo(userInfo); param.setInfo(rank, myId, scores[0], scores[1]); } + } else if (this.infoKey == REDIS_KEY.LEAGUE_INFO) { + param = new LeagueRankInfo(userInfo); + param.setInfo(rank, myId, scores[0], scores[1]); + } for (let extraKey of this.extraKeys) { @@ -781,7 +837,7 @@ export class Rank { public async getRankByRank(start: number, end: number) { - let ranks = new Array(); + let ranks = new Array(); let key = this.keyName.getName(); if (this.isUnion) { key = await this.generateUnionRank(); @@ -1125,6 +1181,32 @@ export async function setRankRedisFromDb(type: string, args?: { serverId?: numbe await r.setRankWithRoleInfo(rank.roleId, rank.rank, 0, role); await saveLadderDefCeByData(rank.roleId, rank); } + } else if (type == REDIS_KEY.GVG_VESTIGE_MEMBER_ALL) { + let ranks = await GVGVestigeSumRankModel.findRank(); + for(let { _id: { groupId, serverType }, arr, roleIds } of ranks) { + let roles = await RoleModel.findByRoleIds(roleIds, ROLE_SELECT.RANK); + let r = new Rank(type, { groupId, serverType, day: getDayKeyInfo() }); + r.setIsInit(true); + for(let { roleId, score, updatedAt } of arr) { + let role = roles.find(cur => cur.roleId == roleId); + if(role) { + await r.setRankWithRoleInfo(roleId, score, updatedAt.getTime(), role); + } + } + } + } else if (type == REDIS_KEY.GVG_VESTIGE_LEAGUE) { + let ranks = await GVGVestigeLeagueRankModel.findRank(); + for(let { _id: { groupId, serverType }, arr, leagueCodes } of ranks) { + let leagues = await GVGLeagueModel.findByCodes(leagueCodes); + let r = new Rank(type, { groupId, serverType, day: getDayKeyInfo() }); + r.setIsInit(true); + for(let { leagueCode, score, updatedAt } of arr) { + let league = leagues.find(cur => cur.leagueCode == leagueCode); + if(league) { + await r.setRankWithLeagueInfo(leagueCode, score, updatedAt.getTime(), league); + } + } + } } } diff --git a/game-server/app/services/redisService.ts b/game-server/app/services/redisService.ts index e506b6e19..2cdcba02c 100644 --- a/game-server/app/services/redisService.ts +++ b/game-server/app/services/redisService.ts @@ -16,8 +16,6 @@ import { RoleModel, RoleType } from '../db/Role'; import { GuildType } from '../db/Guild'; import { ActivityGroupModel } from '../db/ActivityGroup'; import { LadderMatchModel, LadderMatchType } from '../db/LadderMatch'; -import { GVGServerGroupModel } from '../db/GVGServerGroup'; -import { nowSeconds } from '../pubUtils/timeUtil'; /** * 在服务重新启动时,将信息存入redis @@ -50,9 +48,14 @@ export async function initAllRank() { await delKeys(REDIS_KEY.SUM_CE_SNAPSHOT); await delKeys(REDIS_KEY.TOP_LINE_SNAPSHOT); await delKeys(REDIS_KEY.LADDER); + await delKeys(REDIS_KEY.GVG_VESTIGE_MEMBER_ALL); + await delKeys(REDIS_KEY.GVG_VESTIGE_LEAGUE); + await delKeys(REDIS_KEY.LEAGUE_INFO); await setRankRedisFromDb(REDIS_KEY.PVP_RANK, {}); - + await setRankRedisFromDb(REDIS_KEY.GVG_VESTIGE_MEMBER_ALL, {}); + await setRankRedisFromDb(REDIS_KEY.GVG_VESTIGE_LEAGUE, {}); + for(let {id} of serverList) { await initRankByServerId(id); await initActivitiesRank(id) @@ -163,6 +166,11 @@ export async function updateUserInfo(key: string, roleId: string, arr: Array<{fi } } +export async function findKeys(key: string) { + let keys = await redisClient().keysAsync(`${key}*`); + return keys +} + /********排行榜结束 */ diff --git a/game-server/app/services/timeTaskService.ts b/game-server/app/services/timeTaskService.ts index 5f0882e22..0b65aad20 100644 --- a/game-server/app/services/timeTaskService.ts +++ b/game-server/app/services/timeTaskService.ts @@ -44,6 +44,7 @@ import { HiddenDataModel, HiddenDataModelType } from '../db/HiddenData'; import { setHiddenData, setHiddenDataToMemory } from './dataService'; import { GVGConfigModel } from '../db/GVGConfig'; import { createNewGVGConfig, initLeaguePrepare } from './gvg/gvgService'; +import { saveVestigeRankSchedule } from './gvg/gvgFightService'; const PER_SECOND = 1 * 1000; const PER_DAY = 24 * 60 * 60; @@ -905,11 +906,12 @@ export async function initGVGConfigSchedule() { scheduleJob(`gvgConfig`, config.scheduleTime * 1000, createNewGVGConfig); - console.log('##### initGVGConfigSchedule', nowSeconds(), config.prepareTime) if(nowSeconds() > config.prepareTime) { initLeaguePrepare(); } else { scheduleJob(`gvgPrepare`, config.prepareTime * 1000, initLeaguePrepare); } + + scheduleJob('gvgFightSchedule', '0 0 22 * * ?', saveVestigeRankSchedule); } // —————————————— gvg end —————————————— // \ No newline at end of file diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index b754042a9..25a27cb20 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -257,6 +257,8 @@ export enum REDIS_KEY { GA_HISTORY_CITY = 'gaHisCity', // 诸侯混战打开过的城池页面 GVG_VESTIGE_MEMBER = 'vestigeUsr', // 玩家一个遗迹的积分 GVG_VESTIGE_MEMBER_ALL = 'vestigeUsrAll', // 玩家所有遗迹积分 + GVG_VESTIGE_LEAGUE = 'vestigeLeague', // 联军所有遗迹积分 + LEAGUE_INFO ="leagueInfo", // 联军信息 } // 各排行榜对应hash的key @@ -311,6 +313,8 @@ export function getInfoKeyByRedisKey(redisKey: REDIS_KEY) { return { infoKey: REDIS_KEY.USER_INFO, extraKey: [] }; case REDIS_KEY.GVG_VESTIGE_MEMBER_ALL: // 全遗迹玩家排行榜 return { infoKey: REDIS_KEY.USER_INFO, extraKey: [] }; + case REDIS_KEY.GVG_VESTIGE_LEAGUE: // 遗迹联军排行榜 + return { infoKey: REDIS_KEY.LEAGUE_INFO, extraKey: [] } default: return { infoKey: REDIS_KEY.USER_INFO, extraKey: [] }; } @@ -371,7 +375,8 @@ export const RANK_TYPE_TO_KEY = new Map(); export enum COMPOSE_FIELD_TYPE { ROLE = 1, GUILD = 2, - ROLE_HERO = 3 + ROLE_HERO = 3, + LEAGUE = 4 } // 各个key的field如何处理 @@ -406,6 +411,9 @@ export const KEY_TO_COMPOSE_FIELD = new Map([ [REDIS_KEY.TOP_LINE_SNAPSHOT, COMPOSE_FIELD_TYPE.ROLE], [REDIS_KEY.LADDER, COMPOSE_FIELD_TYPE.ROLE], [REDIS_KEY.GVG_VESTIGE_MEMBER, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.GVG_VESTIGE_MEMBER_ALL, COMPOSE_FIELD_TYPE.ROLE], + [REDIS_KEY.GVG_VESTIGE_LEAGUE, COMPOSE_FIELD_TYPE.LEAGUE], + [REDIS_KEY.LEAGUE_INFO, COMPOSE_FIELD_TYPE.LEAGUE], ]); @@ -1102,7 +1110,8 @@ export enum ITEM_CHANGE_REASON { GVG_RECEIVE_LV = 168, // 领取等级奖励 GVG_RECEIVE_TASK = 169, // 领取任务奖励 GVG_VESTIGE_START = 170, // gvg征战中原挑战 - GVG_VESTIGE_END = 171, // gvg征战中原挑战 + GVG_VESTIGE_END = 171, // gvg征战中原挑战 + GVG_VESTIGE_RECEIVE_RANK = 172, // gvg征战中原领取排行榜奖励 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index b3ce768ea..fabbc38a5 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -348,6 +348,7 @@ export const STATUS = { GVG_VESTIGE_BATTLE_END_STATUS_ERR: { code: 21365, simStr: '挑战时间超时' }, GVG_VESTIGE_RANK_NOT_INIT: { code: 21366, simStr: '玩家数据未初始' }, GVG_VESTIGE_REFESH_CNT_MAX: { code: 21367, simStr: '遗迹刷新次数不足' }, + GVG_RECEIVE_NO_RANK_REWARD: { code: 21368, simStr: '没有可领取的奖励' }, // 通用 30000 - 30099 DIC_DATA_NOT_FOUND: { code: 30000, simStr: '数据表未找到' }, diff --git a/shared/db/GVGLeague.ts b/shared/db/GVGLeague.ts index 3460204a2..ca89c951e 100644 --- a/shared/db/GVGLeague.ts +++ b/shared/db/GVGLeague.ts @@ -109,7 +109,7 @@ export default class GVGLeague extends BaseModel { public static async findByCodes(leagueCodes: string[]) { const leagues: GVGLeagueType[] = await GVGLeagueModel.find({ leagueCode: { $in: leagueCodes }, status: 1 }) - .populate('leader', { roleId: 1, roleName: 1, head: 1, frame: 1, spine: 1, lv: 1, quitTime: 1, ce: 1, title: 1, serverId: 1, _id: 0 }, 'Role') + .populate('leader', { roleId: 1, roleName: 1, head: 1, frame: 1, spine: 1, lv: 1, quitTime: 1, ce: 1, title: 1, serverId: 1, guildName: 1, _id: 0 }, 'Role') .lean(); return leagues } diff --git a/shared/db/GVGLeagueFarm.ts b/shared/db/GVGLeagueFarm.ts index 629b1f6d1..f8ebbe27d 100644 --- a/shared/db/GVGLeagueFarm.ts +++ b/shared/db/GVGLeagueFarm.ts @@ -139,7 +139,7 @@ export default class GVGLeagueFarm extends BaseModel { public static async lockMineOrForestry(configId: number, leagueCode: string, farmId: number, type: number, roleId: string, fieldId: number, itemId: number) { // 先创建 await GVGLeagueFarmModel.findOneAndUpdate({ configId, leagueCode, farmId, fieldId }, { $setOnInsert: { unlockTime: 0, itemId: 0, type } }, { upsert: true }); - let unlockTime = nowSeconds() + type == GVG_RESOURCE_TYPE.MINERAL? GVG.GVG_MINE_LOCK_TIME: GVG.GVG_FORESTRY_LOCK_TIME; + let unlockTime = nowSeconds() + (type == GVG_RESOURCE_TYPE.MINERAL? GVG.GVG_MINE_LOCK_TIME: GVG.GVG_FORESTRY_LOCK_TIME); const result: GVGLeagueFarmType = await GVGLeagueFarmModel.findOneAndUpdate( { configId, leagueCode, farmId, fieldId, $or:[{ unlockTime: { $lt: nowSeconds() } }, {lockRoleId: roleId}] }, { $set: { unlockTime, lockRoleId: roleId, itemId } }, diff --git a/shared/db/GVGVestigeLeagueRank.ts b/shared/db/GVGVestigeLeagueRank.ts new file mode 100644 index 000000000..ef3c73e4c --- /dev/null +++ b/shared/db/GVGVestigeLeagueRank.ts @@ -0,0 +1,80 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; +import { DAY_TO_SECOND, getZeroPoint } from '../pubUtils/timeUtil'; + +@index({ day: 1, groupId: 1, serverType: 1, score: 1 }) +@index({ leagueCode: 1 }) +export default class GVGVestigeLeagueRank extends BaseModel { + + @prop({ required: true }) + configId: number; // 赛期 + + @prop({ required: true }) + day: number; // 每天5点 + + @prop({ required: true }) + leagueCode: string; // 玩家id + + @prop({ required: true }) + groupId: number; // 战区id + + @prop({ required: true }) + serverType: number; // 1-单服 2-跨服 + + @prop({ required: true }) + score: number; // 得分 + + @prop({ required: true }) + rank: number; // 最后的排名,每天22点定格 + + @prop({ required: true }) + isReceived: boolean; // 是否领取过 + + public static async incScore(configId: number, leagueCode: string, groupId: number, serverType: number, inc: number) { + let today = getZeroPoint(); + let result: GVGVestigeLeagueRankType = await GVGVestigeLeagueRankModel.findOneAndUpdate({ + day: today, groupId, serverType, leagueCode + }, { $inc: { score: inc }, $set: { configId }, $setOnInsert: { rank: 0, isReceived: false } }, { new: true, upsert: true }).lean(); + return result; + } + + // 排行榜 + public static async findRank(): Promise<{ _id: { groupId: number, serverType: number }, arr: { leagueCode: string, score: number, updatedAt: Date }[], leagueCodes: string[] }[]> { + let today = getZeroPoint(); + let ranks = await GVGVestigeLeagueRankModel.aggregate([ + { $match: { day: today } }, + { $group: { + _id: { groupId: "$groupId", serverType: "$serverType" }, + arr: { $push: { leagueCode: "$leagueCode", score: "$score", updatedAt: "$updatedAt" } }, leagueCodes: { $addToSet: "$leagueCode" } + }}, + ]); + return ranks; + } + + public static async saveRank(groupId: number, serverType: number, ranks: { rank: number, field: string }[]) { + let today = getZeroPoint(); + await GVGVestigeLeagueRankModel.bulkWrite(ranks.map(({ field, rank }) => { + return { updateOne: { filter: { day: today, groupId, serverType, leagueCode: field }, update: { $set: { rank } }} } + })); + } + + public static async getYesterdayRank(leagueCode: string) { + let yesterday = getZeroPoint() - DAY_TO_SECOND; + let result: GVGVestigeLeagueRankType = await GVGVestigeLeagueRankModel.findOne({ day: yesterday, leagueCode }).lean(); + return result; + } + + public static async getCanReceiveRanks(configId: number, leagueCode: string) { + let result: GVGVestigeLeagueRankType[] = await GVGVestigeLeagueRankModel.find({ configId, leagueCode, rank: { $gt: 0 }, isReceived: false }).lean(); + return result; + } + + public static async receiveRanks(_ids: string[]) { + await GVGVestigeLeagueRankModel.updateMany({ _id: { $in: _ids } }, { $set: { isReceived: true } }); + } +} + +export const GVGVestigeLeagueRankModel = getModelForClass(GVGVestigeLeagueRank); + +export interface GVGVestigeLeagueRankType extends Pick, keyof GVGVestigeLeagueRank> {}; +export type GVGVestigeLeagueRankUpdate = Partial; // 将所有字段变成可选项 diff --git a/shared/db/GVGVestigeRank.ts b/shared/db/GVGVestigeRank.ts index 1b7d13d93..5a4a2d49e 100644 --- a/shared/db/GVGVestigeRank.ts +++ b/shared/db/GVGVestigeRank.ts @@ -1,6 +1,6 @@ import BaseModel from './BaseModel'; -import { index, getModelForClass, prop, DocumentType, mongoose, Ref } from '@typegoose/typegoose'; -import Role, { RoleType } from './Role'; +import { index, getModelForClass, prop, DocumentType, mongoose } from '@typegoose/typegoose'; +import { RoleType } from './Role'; import { getZeroPoint } from '../pubUtils/timeUtil'; export class Lineup { @@ -36,6 +36,9 @@ export default class GVGVestigeRank extends BaseModel { @prop({ required: true }) roleId: string; // 玩家 + @prop({ required: true }) + leagueCode: string; // 联军 + @prop({ required: true, default: 0 }) rank: number; // 排名 @@ -75,10 +78,10 @@ export default class GVGVestigeRank extends BaseModel { return result; } - public static async initRank(configId: number, vestigeId: number, groupId: number, serverType: number, role: RoleType, oppRanks: number[]) { + public static async initRank(configId: number, vestigeId: number, groupId: number, serverType: number, role: RoleType, leagueCode: string, oppRanks: number[]) { let today = getZeroPoint(); let doc = new GVGVestigeRankModel(); - let update = Object.assign(doc.toJSON(), { role: role._id, configId, groupId, serverType, oppRanks }); + let update = Object.assign(doc.toJSON(), { role: role._id, configId, groupId, serverType, oppRanks, leagueCode }); let result: GVGVestigeRankType = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId: role.roleId }, { $setOnInsert: update }, { new: true, upsert: true }).lean(); return result; } @@ -153,10 +156,9 @@ export default class GVGVestigeRank extends BaseModel { return ranks; } - public static async findRankByVestige(groupId: number, serverType: number, vestigeId: number) { let today = getZeroPoint(); - let result: GVGVestigeRankType[] = await GVGVestigeRankModel.find({ day: today, vestigeId, groupId, serverType }).sort({ rank: 1 }).select('roleId rank lineup').lean(); + let result: GVGVestigeRankType[] = await GVGVestigeRankModel.find({ day: today, vestigeId, groupId, serverType, rank: { $gt: 0 } }).sort({ rank: 1 }).select('roleId rank lineup').lean(); return result; } } diff --git a/shared/db/GVGVestigeRec.ts b/shared/db/GVGVestigeRec.ts index 0865b8073..9825694fa 100644 --- a/shared/db/GVGVestigeRec.ts +++ b/shared/db/GVGVestigeRec.ts @@ -32,6 +32,9 @@ export default class GVGVestigeRec extends BaseModel { @prop({ required: true }) defenseRoleId: string; // 守方玩家id + @prop({ required: true }) + configId: number; // 赛期 + @prop({ required: true }) groupId: number; // 战区id @@ -65,7 +68,7 @@ export default class GVGVestigeRec extends BaseModel { @prop({ required: true, default: false }) hasRpl: boolean; // 是否存在对应录像 - public static async createRec(configId: number, vestigeId: number, groupId: number, serverType: number, warId: number, attackInfo: OppPlayerInfo, defenseInfo: OppPlayerInfo, leagueGoods: LeagueGood[]) { + public static async createRec(configId: number, vestigeId: number, groupId: number, serverType: number, warId: number, attackInfo: OppPlayerInfo, defenseInfo: OppPlayerInfo, leagueGoods: LeagueGood[]) { const battleCode = genCode(10); const result: GVGVestigeRecType = await GVGVestigeRecModel.findOneAndUpdate({ battleCode }, { $set: { configId, vestigeId, groupId, serverType, attackRoleId: attackInfo.roleId, defenseRoleId: defenseInfo.roleId, warId, checkTime: Date.now(), attackInfo, defenseInfo, leagueGoods } diff --git a/shared/db/GVGVestigeSumRank.ts b/shared/db/GVGVestigeSumRank.ts new file mode 100644 index 000000000..187ecd443 --- /dev/null +++ b/shared/db/GVGVestigeSumRank.ts @@ -0,0 +1,49 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; +import { getZeroPoint } from '../pubUtils/timeUtil'; + +@index({ day: 1, groupId: 1, serverType: 1, score: 1 }) +@index({ roleId: 1 }) +export default class GVGVestigeSumRank extends BaseModel { + + @prop({ required: true }) + day: number; // 每天5点 + + @prop({ required: true }) + roleId: string; // 玩家id + + @prop({ required: true }) + groupId: number; // 战区id + + @prop({ required: true }) + serverType: number; // 1-单服 2-跨服 + + @prop({ required: true }) + score: number; // 得分 + + public static async incScore(roleId: string, groupId: number, serverType: number, inc: number) { + let today = getZeroPoint(); + let result: GVGVestigeSumRankType = await GVGVestigeSumRankModel.findOneAndUpdate({ + day: today, groupId, serverType, roleId + }, { $inc: { score: inc } }, { new: true, upsert: true }).lean(); + return result; + } + + // 排行榜 + public static async findRank(): Promise<{ _id: { groupId: number, serverType: number }, arr: { roleId: string, score: number, updatedAt: Date }[], roleIds: string[] }[]> { + let today = getZeroPoint(); + let ranks = await GVGVestigeSumRankModel.aggregate([ + { $match: { day: today } }, + { $group: { + _id: { groupId: "$groupId", serverType: "$serverType" }, + arr: { $push: { roleId: "$roleId", score: "$score", updatedAt: "$updatedAt" } }, roleIds: { $addToSet: "$roleId" } + }}, + ]); + return ranks; + } +} + +export const GVGVestigeSumRankModel = getModelForClass(GVGVestigeSumRank); + +export interface GVGVestigeSumRankType extends Pick, keyof GVGVestigeSumRank> {}; +export type GVGVestigeSumRankUpdate = Partial; // 将所有字段变成可选项 diff --git a/shared/domain/gvgField/gvgDb.ts b/shared/domain/gvgField/gvgDb.ts index f1654ed66..37ac798fc 100644 --- a/shared/domain/gvgField/gvgDb.ts +++ b/shared/domain/gvgField/gvgDb.ts @@ -73,7 +73,9 @@ export class OppPlayerInfo { @prop({ required: true }) roleId: string = ''; // 角色 id @prop({ required: true }) - roleName: string = GVG.GVG_ROBOT_NAME; // 角色 名 + roleName: string = GVG.GVG_ROBOT_NAME; // 角色 名 + @prop({ required: true }) + leagueCode: string = ''; // 联军id @prop({ required: true, default: 0 }) lv: number = 1; // 等级 @prop({ required: true, default: EXTERIOR.EXTERIOR_FACE }) @@ -93,7 +95,7 @@ export class OppPlayerInfo { @prop({ required: true }) isRobot: boolean = false; // 原排名 - initByPlayer(rank: number, role: RoleType, isSuccess: boolean ) { + initByPlayer(rank: number, role: RoleType, leagueCode: string, isSuccess: boolean ) { this.oldRank = rank; this.newRank = rank; this.roleId = role.roleId; @@ -105,6 +107,7 @@ export class OppPlayerInfo { this.lv = role.lv; this.isSuccess = isSuccess; this.isRobot = false; + this.leagueCode = leagueCode; } setPlayerHeroes(heroes: HeroType[], lineup: {actorId: number, dataId: number, order: number}[]) { diff --git a/shared/domain/rank.ts b/shared/domain/rank.ts index 325d0cd5b..a96e834c2 100644 --- a/shared/domain/rank.ts +++ b/shared/domain/rank.ts @@ -6,6 +6,7 @@ import { getSeconds } from "../pubUtils/timeUtil"; import { prop } from "@typegoose/typegoose"; import { pick } from "underscore"; import { RANK_TYPE, REDIS_KEY } from "../consts"; +import { GVGLeagueType } from "../db/GVGLeague"; // 排行榜返回玩家值 export class RankParam { @@ -194,6 +195,7 @@ export class GuildRankParam { } } + export class GuildRankInfo extends GuildRankParam { rank: number; code: string; @@ -223,6 +225,65 @@ export class GuildRankInfo extends GuildRankParam { } +export class LeagueLeader { + @prop({ required: true }) + roleId: string; + + @prop({ required: true }) + roleName: string; + + @prop({ required: true }) + guildName: string; + + @prop({ required: true }) + serverId: number; + + constructor(leader: { roleId: string, roleName: string, guildName: string, serverId: number}) { + this.roleId = leader.roleId; + this.roleName = leader.roleName; + this.guildName = leader.guildName; + this.serverId = leader.serverId; + } +} + +// 联军信息 +export class LeagueRankParam { + @prop({ required: true }) + icon: number; + + @prop({ required: true }) + name: string; + + @prop({ required: true, _id: false }) + leader: LeagueLeader; + + constructor(league: GVGLeagueType) { + this.icon = league.icon; + this.name = league.name; + let leader = league.leader; + let _leader = new LeagueLeader(leader); + this.leader = _leader; + } +} + +// LeagueRankParam显示到排行榜界面上的信息 +export class LeagueRankInfo extends LeagueRankParam { + rank: number; + code: string; + num: number; + time: number; + + setInfo(rank: number, myId: myIdInter, num: number, time: number) { + this.rank = rank; + this.code = myId.leagueCode; + this.num = num; + this.time = time; + } + + isMyInfo(myId: myIdInter) { + return this.code == myId.leagueCode; + } +} export class RoleAndGuildRankInfo { rank: number; @@ -304,6 +365,7 @@ export class KeyName { vestigeId?: number; groupId?: number; serverType?: number; + day?: string; constructor(key: string, param: KeyNameParam) { this.key = key; @@ -317,6 +379,7 @@ export class KeyName { if(param.vestigeId) this.vestigeId = param.vestigeId; if(param.groupId) this.groupId = param.groupId; if(param.serverType) this.serverType = param.serverType; + if(param.day) this.day = param.day; } public getName() { @@ -350,9 +413,11 @@ export class KeyName { case REDIS_KEY.TOP_LINE_SNAPSHOT: return `${this.key}:${this.serverId}:${this.activityId}`; case REDIS_KEY.GVG_VESTIGE_MEMBER: - return `${this.key}:${this.groupId}:${this.serverType}:${this.vestigeId}`; + return `${this.key}:${this.day}:${this.groupId}:${this.serverType}:${this.vestigeId}`; case REDIS_KEY.GVG_VESTIGE_MEMBER_ALL: - return `${this.key}:${this.groupId}`; + return `${this.key}:${this.day}:${this.groupId}:${this.serverType}`; + case REDIS_KEY.GVG_VESTIGE_LEAGUE: + return `${this.key}:${this.day}:${this.groupId}:${this.serverType}`; default: return this.key; } @@ -402,6 +467,7 @@ export interface myIdInter { roleId?: string; guildCode?: string; hid?: number; + leagueCode?: string; } // 排行榜总览左边的数据 @@ -412,7 +478,7 @@ export class GeneralRankParam { num: number = 0; received: number[]; - constructor(type: number, rankInfo: RoleRankInfo|GuildRankInfo, general: number, received?: number[]) { + constructor(type: number, rankInfo: RoleRankInfo|GuildRankInfo|LeagueRankInfo, general: number, received?: number[]) { this.type = type; if(general == 1) { let rank = rankInfo? (type == RANK_TYPE.LADDER? rankInfo.num: rankInfo.rank): 0; diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index 22d4969a2..13de4d9d9 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -130,6 +130,7 @@ import { dicGVGTask, dicGVGTaskByType, loadGVGTask } from './dictionary/DicGVGTa import { dicGVGVestigeByType, loadGVGVestigeType } from './dictionary/DicGVGVestigeType'; import { dicGVGVestige, loadGVGVestige } from './dictionary/DicGVGVestige'; import { DicGVGVestigeRange, dicGVGVestigeRange, loadGVGVestigeRange } from './dictionary/DicGVGVestigeRange'; +import { DicGVGVestigeLeagueRank, dicGVGVestigeLeagueRank, loadGVGVestigeLeagueRank } from "./dictionary/DicGVGVestigeLeagueRank"; export const gameData = { daily: dicDaily, @@ -327,6 +328,7 @@ export const gameData = { gvgVestigeCntByServerType: new Map(), // 单服or跨服 => 遗迹数量 gvgVestige: dicGVGVestige, gvgVestigeRange: dicGVGVestigeRange, + gvgVestigeLeagueRank: dicGVGVestigeLeagueRank, }; // 在此提供一些原先在gamedata中提供的方法,以便更方便获取gameData数据 @@ -1183,6 +1185,18 @@ export function getGVGVestigeRange(myRank: number) { return lastRank } +export function getGVGVestigeLeagueRank(myLeagueRank: number) { + let lastRank: DicGVGVestigeLeagueRank; + for(let dic of gameData.gvgVestigeLeagueRank) { + lastRank = dic; + let { rankMin, rankMax } = dic; + if(rankMin < myLeagueRank && (rankMax >= myLeagueRank || rankMax == 0)) { + return dic; + } + } + return lastRank +} + // 初始加载 function initDatas() { parseDicParam(); @@ -1394,6 +1408,7 @@ function loadDatas() { loadGVGVestigeType(); loadGVGVestige(); loadGVGVestigeRange(); + loadGVGVestigeLeagueRank(); } // 重载dicParam diff --git a/shared/resource/jsons/dic_zyz_GVGTask.json b/shared/resource/jsons/dic_zyz_GVGTask.json index b581bf393..3104931ce 100644 --- a/shared/resource/jsons/dic_zyz_GVGTask.json +++ b/shared/resource/jsons/dic_zyz_GVGTask.json @@ -6,6 +6,7 @@ "taskInfo": "农庄种植/矿山采集/木堆砍柴完成1次", "taskParam": "1&", "condition": 1, + "tips": "(3选1完成即可)", "reward": "31002&500", "leagueReward": "13&1" }, @@ -16,6 +17,7 @@ "taskInfo": "中原遗迹挑战1次", "taskParam": "1&", "condition": 1, + "tips": "&", "reward": "31002&500", "leagueReward": "13&1" }, @@ -26,6 +28,7 @@ "taskInfo": "千机阁捐献1次", "taskParam": "1&", "condition": 1, + "tips": "&", "reward": "31002&500", "leagueReward": "13&1" } diff --git a/shared/resource/jsons/dic_zyz_GVGVestigeType.json b/shared/resource/jsons/dic_zyz_GVGVestigeType.json index 8e36cd632..312e93259 100644 --- a/shared/resource/jsons/dic_zyz_GVGVestigeType.json +++ b/shared/resource/jsons/dic_zyz_GVGVestigeType.json @@ -6,8 +6,9 @@ "mapType": "1&2", "imageName": "&", "additionType": 1, - "additionValue": "2&1000", + "additionValue": "2&", "content": "枪兵", + "seid": 2401311, "position": "&" }, { @@ -17,8 +18,117 @@ "mapType": "1&2", "imageName": "&", "additionType": 1, - "additionValue": "6&1000", + "additionValue": "6&", "content": "策士", + "seid": 501411, + "position": "&" + }, + { + "id": 3, + "vestigeId": 3, + "name": "燕都遗址", + "mapType": "1&2", + "imageName": "&", + "additionType": 1, + "additionValue": "5&", + "content": "游侠", + "seid": 301411, + "position": "&" + }, + { + "id": 4, + "vestigeId": 4, + "name": "韩都遗址", + "mapType": "1&2", + "imageName": "&", + "additionType": 1, + "additionValue": "4&", + "content": "弓兵", + "seid": 3001411, + "position": "&" + }, + { + "id": 5, + "vestigeId": 5, + "name": "赵都遗址", + "mapType": "1&2", + "imageName": "&", + "additionType": 1, + "additionValue": "3&", + "content": "骑兵", + "seid": 301411, + "position": "&" + }, + { + "id": 6, + "vestigeId": 6, + "name": "魏都遗址", + "mapType": "1&2", + "imageName": "&", + "additionType": 1, + "additionValue": "1&", + "content": "步兵", + "seid": 301411, + "position": "&" + }, + { + "id": 7, + "vestigeId": 7, + "name": "山巅帝陵", + "mapType": "1&2", + "imageName": "&", + "additionType": 2, + "additionValue": "1&", + "content": "魏国", + "seid": 1201611, + "position": "&" + }, + { + "id": 8, + "vestigeId": 8, + "name": "崖底河泽", + "mapType": "1&2", + "imageName": "&", + "additionType": 2, + "additionValue": "2&", + "content": "吴国", + "seid": 5001311, + "position": "&" + }, + { + "id": 9, + "vestigeId": 9, + "name": "深山迷林", + "mapType": "1&2", + "imageName": "&", + "additionType": 2, + "additionValue": "3&", + "content": "蜀国", + "seid": 5001311, + "position": "&" + }, + { + "id": 10, + "vestigeId": 10, + "name": "桃源古村", + "mapType": "1&2", + "imageName": "&", + "additionType": 2, + "additionValue": "4&", + "content": "群雄", + "seid": 5001311, + "position": "&" + }, + { + "id": 11, + "vestigeId": 11, + "name": "鹿鸣幽谷", + "mapType": "1&2", + "imageName": "&", + "additionType": 3, + "additionValue": "2&", + "content": "女性", + "seid": 5001311, "position": "&" } ] \ No newline at end of file