import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; import { LADDER_STATUS } from '../consts'; import { LadderDefense, LadderOppPlayerInfo, LadderOppPlayerHeroInfo } from '../domain/battleField/ladder'; import { genCode } from '../pubUtils/util'; @index({ battleCode: 1 }) @index({ roleId1: 1 }) @index({ roleId2: 1 }) export default class LadderMatchRec extends BaseModel { @prop({ required: true }) battleCode: string; // 唯一code @prop({ required: true }) roleId1: string; // 攻方玩家id @prop({ required: true }) roleId2: string; // 守方玩家id @prop({ required: true }) serverId: number; // 区 id @prop({ required: true }) status: LADDER_STATUS; // 1-出兵中 2-挑战中 3-挑战完毕 @prop({ required: true }) timeout: boolean; // 是否超时 @prop({ required: true }) giveup: boolean; // 是否取消 @prop({ required: true }) checkTime: number; // 开始出兵的时间,13位时间戳 @prop({ required: true }) battleTime: number; // 开始挑战的时间,13位时间 @prop({ required: true }) endTime: number; // 结束时间,13位时间戳 @prop({ required: true, type: () => LadderOppPlayerInfo, default: {}, _id: false }) attackInfo: LadderOppPlayerInfo; // 攻方信息 @prop({ required: true, type: () => LadderOppPlayerInfo, default: {}, _id: false }) defenseInfo: LadderOppPlayerInfo; // 守方信息 @prop({ required: true, type: () => LadderDefense, default: {}, _id: false }) defense: LadderDefense; // 守方信息 @prop({ required: true, default: false }) hasRpl: boolean; // 是否存在对应录像 @prop({ required: true, default: false }) remoteUrl: string; // 录像地址 public static async findByRoleId(roleId: string, getters = false) { const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ roleId1: roleId }).lean({ getters }); return result; } public static async findNotCompleteRecByRoleId(roleId: string, getters = false) { const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ roleId1: roleId, status: { $in: [LADDER_STATUS.BATTLE, LADDER_STATUS.CHECK] } }).lean({ getters }); return result; } public static async createRec(serverId: number, roleId1: string, roleId2: string, defense: LadderDefense, attackInfo: LadderOppPlayerInfo, defenseInfo: LadderOppPlayerInfo) { const battleCode = genCode(10); const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: { roleId1, roleId2, serverId, defense, status: LADDER_STATUS.CHECK, checkTime: Date.now(), attackInfo, defenseInfo } }, { new: true, upsert: true }).lean(); return result; } public static async timeout(battleCode: string) { const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: { status: LADDER_STATUS.COMPLETE, timeout: true, endTime: Date.now() } }, { new: true }).lean(); return result; } public static async giveup(roleId: string, battleCode: string) { const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ roleId1: roleId, battleCode }, { $set: { status: LADDER_STATUS.COMPLETE, timeout: false, giveup: true, endTime: Date.now() } }, { new: true }).lean(); return result; } public static async findByBattleCode(battleCode: string, select: string = '') { const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ battleCode }).select(select).lean(); return result; } public static async startBattle(battleCode: string, attackHeroes: LadderOppPlayerHeroInfo[]) { const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: { status: LADDER_STATUS.BATTLE, battleTime: Date.now(), 'attackInfo.heroes': attackHeroes } }, { new: true }).lean(); return result; } public static async battleEnd(battleCode: string, isSuccess: boolean, endTime: number, atkNewRank?: number, defNewRank?: number) { let update: LadderMatchRecUpdate = { status: LADDER_STATUS.COMPLETE, endTime, timeout: false }; if (isSuccess) { update['attackInfo.isSuccess'] = true; update['defenseInfo.isSuccess'] = false; } if (atkNewRank != undefined && defNewRank != undefined) { update['attackInfo.newRank'] = atkNewRank; update['attackInfo.oldRank'] = defNewRank; update['defenseInfo.newRank'] = defNewRank; update['defenseInfo.oldRank'] = atkNewRank; } const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: update }, { new: true }).lean(); return result; } public static async findRec(roleId: string) { let recs: LadderMatchRecType[] = await LadderMatchRecModel.find({ $or: [{ roleId1: roleId }, { roleId2: roleId }], status: LADDER_STATUS.COMPLETE }).select({ battleCode: 1, roleId1: 1, roleId2: 1, _id: -1, endTime: 1, attackInfo: 1, defenseInfo: 1, hasRpl: 1 }).limit(1000).lean(); return recs; } /** * 按时间区间查询名将擂台战报(用战斗结束时间(endTime)做比较) * @static * @param {string} roleId * @param {number} startMs 开始时间(13位时间戳) * @return {*} * @memberof LadderMatchRec */ public static async findRecentRec(roleId: string, startMs: number) { let recs: LadderMatchRecType[] = await LadderMatchRecModel.find({ endTime: { $gt: startMs }, $or: [{ roleId1: roleId }, { roleId2: roleId }], status: LADDER_STATUS.COMPLETE }).select({ battleCode: 1, roleId1: 1, roleId2: 1, _id: -1, endTime: 1, attackInfo: 1, defenseInfo: 1, hasRpl: 1, remoteUrl: 1 }).limit(1000).lean(); return recs; } /** * 查找包含roleId最新一条记录 * @param roleId * @returns true--表示查到最新一条roleId作为守方失败 */ // public static async findOneRecByRoleId(roleId: string) { // let recs: LadderMatchRecType = await LadderMatchRecModel.findOne({ $or: [{ roleId1: roleId }, { roleId2: roleId }], status: LADDER_STATUS.COMPLETE }).sort({ endTime: -1 }).limit(1).lean(); // if (recs && recs.roleId2 == roleId && (!recs.defenseInfo.isSuccess)) return true; // return false; // } public static async findRecByRoleId(roleId: string, quitTime: number) { const pipeline = [ { $match: { endTime: { $gt: quitTime }, $or: [{ roleId1: roleId }, { roleId2: roleId }], status: LADDER_STATUS.COMPLETE } }, { $sort: { endTime: -1 } }, // { $limit: 1 }, { $project: { _id: 0, roleId2: 1, "defenseInfo.isSuccess": 1, "defenseInfo.oldRank": 1, "defenseInfo.newRank": 1} } ]; const recs: LadderMatchRecType[] = await LadderMatchRecModel.aggregate(pipeline); for (let obj of recs) { if (obj.roleId2 === roleId && (!obj.defenseInfo.isSuccess) && obj.defenseInfo.newRank > obj.defenseInfo.oldRank) return true; } return false; } public static async checkByRank(serverId: number, roleId2: string) { const result = await LadderMatchRecModel.exists({ serverId, roleId2, status: { $in: [LADDER_STATUS.BATTLE, LADDER_STATUS.CHECK] } }); return result; } public static async getUncompleteData() { let recs = await LadderMatchRecModel.find({ $or: [ { status: LADDER_STATUS.CHECK }, { status: LADDER_STATUS.BATTLE } ] }); return recs; } public static async timeoutMany(battleCode: string[]) { await LadderMatchRecModel.updateMany({ battleCode: { $in: battleCode } }, { $set: { status: LADDER_STATUS.COMPLETE, timeout: true, endTime: Date.now() } }); } public static async updateRplStatus(battleCode: string, hasRpl: boolean, remoteUrl: string) { let result = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { hasRpl, remoteUrl }, { new: true }).lean(); return result; } } export const LadderMatchRecModel = getModelForClass(LadderMatchRec); export interface LadderMatchRecType extends Pick, keyof LadderMatchRec> { }; export type LadderMatchRecUpdate = Partial;