diff --git a/game-server/app/servers/battle/handler/guildHandler.ts b/game-server/app/servers/battle/handler/guildHandler.ts index 83ed1e162..543b6468c 100644 --- a/game-server/app/servers/battle/handler/guildHandler.ts +++ b/game-server/app/servers/battle/handler/guildHandler.ts @@ -4,10 +4,12 @@ import { STATUS, GUILD_OPERATE, GUILD_AUTH, GUILD_JOB } from '../../../consts'; import { UserGuildModel } from '../../../db/UserGuild'; import { checkAuth } from '../../../services/guildService'; import { GuildModel } from '../../../db/Guild'; -import { RoleModel } from '../../../db/Role'; +import { RoleModel, RoleType } from '../../../db/Role'; import { GUILD } from '../../../pubUtils/dicParam'; import { handleCost } from '../../../services/rewardService'; import { getGoldObject } from '../../../pubUtils/itemUtils'; +import { getSeconds } from '../../../pubUtils/timeUtil'; +import { GuildListInfo } from '../../../pubUtils/interface'; export default function (app: Application) { return new GuildHandler(app); @@ -17,7 +19,7 @@ export class GuildHandler { constructor(private app: Application) { } - //TODO创建军团 + // 创建军团 async createGuild(msg: { name: string, icon: number, notice: string }, session: BackendSession) { const roleId = session.get('roleId'); @@ -25,7 +27,7 @@ export class GuildHandler { const { name, icon, notice } = msg; // 检查权限是否够,包括是否参过团 - const { auth } = await UserGuildModel.getMyGuild(roleId); + const auth = await UserGuildModel.getMyAuth(roleId); const checkResult = checkAuth(GUILD_OPERATE.CREATE_GUILD, auth); if(!checkResult) return resResult(STATUS.GUILD_AUTH_NOT_ENOUGH); @@ -42,33 +44,63 @@ export class GuildHandler { await handleCost(roleId, sid, [getGoldObject(GUILD.GUILD_CREATE_COST)]); // 创建公会 - const guild = await GuildModel.createGuild({ name, icon, notice, leader: role._id, members: [roleId], ce: role.ce }); + const guild = await GuildModel.createGuild({ name, icon, notice }, role); if(!guild) return resResult(STATUS.GUILD_CREATE_ERROR); - const userGuild = await UserGuildModel.createUserGuild({ guildCode: guild.code, roleId, role: role._id, guild: guild._id, auth: GUILD_AUTH.LEADER, job: GUILD_JOB.JIANGJUN }); + const userGuild = await UserGuildModel.createUserGuild(guild.code, role, true); if(!userGuild) return resResult(STATUS.GUILD_CREATE_ERROR); // TODO 加入排行 const rank = 0; + // TODO 加入channel // 返回 - const result = { ...guild, rank, myInfo: {...userGuild}}; + const result = { ...guild, rank, myInfo: userGuild}; return resResult(STATUS.SUCCESS, result); } - //TODO 获取军团列表 + // 获取军团列表 async getGuildList(msg: { page: number, showPeopleMax: boolean, name: string }, session: BackendSession) { + + const roleId = session.get('roleId'); + // const sid = session.get('sid'); + const { page = 1, showPeopleMax, name } = msg; - const list = [{ - code: "code", - name: "name", - icon: 1, - lv: 0, - memberCnt: 0, - leader: "leader", - ceLimit: 0, - quitTime: 0, - hasApply: true - }]; - return resResult(STATUS.SUCCESS, { list }); + const guildList = await GuildModel.findByCondition(page, showPeopleMax, name); + // TODO 加入申请列表 + + const { quitGuildTime: quitTime = 0 } = await RoleModel.findByRoleId(roleId); + + const list = new Array(); + for(let guild of guildList) { + let info = new GuildListInfo(guild); + + // TODO 查询申请 + info.setApply(false); + list.push(info); + } + return resResult(STATUS.SUCCESS, { quitTime, list }); } + + // 团员获取自己军团详细信息 + async getMyGuildInfo(msg: { }, session: BackendSession) { + + const roleId = session.get('roleId'); + + //TODO 检查权限是否够 等策划表 + + const userGuild = await UserGuildModel.getMyGuild(roleId, 'honour job auth guildCode'); + if(!userGuild) { + return resResult(STATUS.SUCCESS, { hasGuild: false }) + }; + const guild = await GuildModel.findByCode(userGuild.guildCode); + + // TODO 获取排行榜 + const rank = 0; + + // 返回 + const result = { hasGuild: true, ...guild, rank, myInfo: userGuild}; + return resResult(STATUS.SUCCESS, result); + } + + } \ No newline at end of file diff --git a/game-server/test/guild.test.ts b/game-server/test/guild.test.ts index 22ca85d59..60336f6c7 100644 --- a/game-server/test/guild.test.ts +++ b/game-server/test/guild.test.ts @@ -56,51 +56,6 @@ describe('军团测试', function() { done(); }); - it('创建军团', function (done) { - pinusClient.request('battle.guildHandler.createGuild', { name: "test", icon: 1, notice: "notice" }, (res) => { - console.log(JSON.stringify(res)) - // 消息回调 - expect(res).to.be.an('object'); - expect(res.code).equal(0); - expect(res.data).to.be.an('object'); - expect(res.data).to.have.deep.property('code').that.is.an('string'); - expect(res.data).to.have.deep.property('name').that.is.an('string'); - expect(res.data).to.have.deep.property('icon').that.is.an('number'); - expect(res.data).to.have.deep.property('notice').that.is.an('string'); - expect(res.data).to.have.deep.property('introduce').that.is.an('string'); - expect(res.data).to.have.deep.property('rank').that.is.an('number'); - expect(res.data).to.have.deep.property('lv').that.is.an('number'); - expect(res.data).to.have.deep.property('fund').that.is.an('number'); - expect(res.data).to.have.deep.property('memberCnt').that.is.an('number'); - expect(res.data).to.have.deep.property('managerCnt').that.is.an('number'); - expect(res.data).to.have.deep.property('activeDaily').that.is.an('number'); - expect(res.data).to.have.deep.property('activeWeekly').that.is.an('number'); - expect(res.data).to.have.deep.property('ceLimit').that.is.an('number'); - expect(res.data).to.have.deep.property('isAuto').that.is.an('boolean'); - - expect(res.data).to.have.deep.property('leader').that.is.an('object'); - expect(res.data.leader).to.have.deep.property('name').that.is.an('string'); - expect(res.data.leader).to.have.deep.property('sHid').that.is.an('number'); - expect(res.data.leader).to.have.deep.property('headHid').that.is.an('number'); - expect(res.data.leader).to.have.deep.property('lv').that.is.an('number'); - expect(res.data.leader).to.have.deep.property('loginTime').that.is.an('number'); - - expect(res.data).to.have.deep.property('myInfo').that.is.an('object'); - expect(res.data.myInfo).to.have.deep.property('honour').that.is.an('number'); - expect(res.data.myInfo).to.have.deep.property('active').that.is.an('number'); - expect(res.data.myInfo).to.have.deep.property('job').that.is.an('number'); - expect(res.data.myInfo).to.have.deep.property('auth').that.is.an('number'); - - expect(res.data).to.have.deep.property('structure').that.is.an('array'); - res.data.structure.forEach(list => { - expect(list).to.have.deep.property('id').that.is.a('number'); - expect(list).to.have.deep.property('lv').that.is.a('number'); - }); - done(); - }); - }); - - it('获取军团列表', function(done) { pinusClient.request('battle.guildHandler.getGuildList', {page:1, showPeopleMax: true, name: ""} , (res) => { console.log(JSON.stringify(res)) @@ -109,18 +64,66 @@ describe('军团测试', function() { expect(res.code).equal(0); expect(res.data).to.be.an('object'); expect(res.data).to.have.deep.property('list').that.is.an('array'); + expect(res.data).to.have.deep.property('quitTime').that.is.an('number'); res.data.list.forEach(list => { expect(list).to.have.deep.property('code').that.is.a('string'); expect(list).to.have.deep.property('name').that.is.a('string'); expect(list).to.have.deep.property('icon').that.is.a('number'); expect(list).to.have.deep.property('lv').that.is.a('number'); - expect(list).to.have.deep.property('peopleNum').that.is.a('number'); + expect(list).to.have.deep.property('memberCnt').that.is.a('number'); expect(list).to.have.deep.property('leader').that.is.a('string'); expect(list).to.have.deep.property('ceLimit').that.is.a('number'); - expect(list).to.have.deep.property('quitTime').that.is.a('number'); expect(list).to.have.deep.property('hasApply').that.is.a('boolean'); }); done(); }); }); + + + it('获取个人军团', function (done) { + pinusClient.request('battle.guildHandler.getMyGuildInfo', { }, (res) => { + // 消息回调 + expect(res).to.be.an('object'); + expect(res.code).equal(0); + expect(res.data).to.be.an('object'); + expect(res.data).to.have.deep.property('hasGuild').that.is.an('boolean'); + if(res.data.hasGuild) { + expect(res.data).to.have.deep.property('code').that.is.an('string'); + expect(res.data).to.have.deep.property('name').that.is.an('string'); + expect(res.data).to.have.deep.property('icon').that.is.an('number'); + expect(res.data).to.have.deep.property('notice').that.is.an('string'); + expect(res.data).to.have.deep.property('introduce').that.is.an('string'); + expect(res.data).to.have.deep.property('rank').that.is.an('number'); + expect(res.data).to.have.deep.property('lv').that.is.an('number'); + expect(res.data).to.have.deep.property('fund').that.is.an('number'); + expect(res.data).to.have.deep.property('memberCnt').that.is.an('number'); + expect(res.data).to.have.deep.property('managerCnt').that.is.an('number'); + expect(res.data).to.have.deep.property('activeDaily').that.is.an('number'); + expect(res.data).to.have.deep.property('activeWeekly').that.is.an('number'); + expect(res.data).to.have.deep.property('ceLimit').that.is.an('number'); + expect(res.data).to.have.deep.property('isAuto').that.is.an('boolean'); + + expect(res.data).to.have.deep.property('leader').that.is.an('object'); + expect(res.data.leader).to.have.deep.property('roleName').that.is.an('string'); + expect(res.data.leader).to.have.deep.property('sHid').that.is.an('number'); + expect(res.data.leader).to.have.deep.property('headHid').that.is.an('number'); + expect(res.data.leader).to.have.deep.property('lv').that.is.an('number'); + expect(res.data.leader).to.have.deep.property('ce').that.is.an('number'); + expect(res.data.leader).to.have.deep.property('loginTime').that.is.an('number'); + + expect(res.data).to.have.deep.property('myInfo').that.is.an('object'); + expect(res.data.myInfo).to.have.deep.property('honour').that.is.an('number'); + expect(res.data.myInfo).to.have.deep.property('job').that.is.an('number'); + expect(res.data.myInfo).to.have.deep.property('auth').that.is.an('number'); + + expect(res.data).to.have.deep.property('structure').that.is.an('array'); + res.data.structure.forEach(list => { + expect(list).to.have.deep.property('id').that.is.a('number'); + expect(list).to.have.deep.property('lv').that.is.a('number'); + }); + } + done(); + }); + }); + }); diff --git a/shared/consts/constModules/guildConst.ts b/shared/consts/constModules/guildConst.ts index 09a28ecfd..8b684cee8 100644 --- a/shared/consts/constModules/guildConst.ts +++ b/shared/consts/constModules/guildConst.ts @@ -45,4 +45,7 @@ export enum USER_GUILD_STATUS { ON = 1, // 成员 QUIT = 2, // 退出 DISMISSED = 3 // 解散 -} \ No newline at end of file +} + +// 查询每页显示 +export const GUILD_PER_PAGE = 30; \ No newline at end of file diff --git a/shared/db/Guild.ts b/shared/db/Guild.ts index 651d76bb2..703445abc 100644 --- a/shared/db/Guild.ts +++ b/shared/db/Guild.ts @@ -1,8 +1,8 @@ import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType, Ref } from '@typegoose/typegoose'; -import Role from './Role'; +import Role, { RoleType } from './Role'; import { genCode } from '../pubUtils/util'; -import { GUILD_STRUCTURE, GUILD_STATUS } from '../consts'; +import { GUILD_STRUCTURE, GUILD_STATUS, GUILD_PER_PAGE } from '../consts'; class Structure { @prop({ required: true }) @@ -29,6 +29,9 @@ export default class Guild extends BaseModel { @prop({ required: true, default: 1 }) memberCnt: number; + @prop({ required: true, default: false, select: false }) + isMemberMax: boolean; + @prop({ required: true, default: 1 }) managerCnt: number; @@ -53,16 +56,19 @@ export default class Guild extends BaseModel { @prop({ required: true, default: 0 }) activeDaily: number; - @prop({ required: true, default: new Date() }) - refTimeDaily: number; + @prop({ required: true, default: 0 }) + activeWeekly: number; - @prop({ required: true, default: new Date() }) - refTimeWeekly: number; + @prop({ required: true, default: new Date(), select: false }) + refTimeDaily: Date; + + @prop({ required: true, default: new Date(), select: false }) + refTimeWeekly: Date; @prop({ required: true, default: 0 }) ce: number; // 总战力 - @prop({ required: true, type: String, default: [] }) + @prop({ required: true, type: String, default: [], select: false }) members: string[]; // 军团成员的roleId,用于增加战力的时候加入总战力 @prop({ required: true, type: Structure, default: getInitStructure(), _id: false }) @@ -71,31 +77,51 @@ export default class Guild extends BaseModel { @prop({ required: true, default: GUILD_STATUS.RUNNING, enum: GUILD_STATUS }) status: number; - public static async createGuild(params: CreateParam) { + public static async createGuild(params: { name: string, icon: number, notice: string }, role: RoleType) { const doc = new GuildModel(); - const update = Object.assign(doc.toJSON(), params); + const update = Object.assign(doc.toJSON(), params, { leader: role._id, members: [role.roleId], ce: role.ce }); delete update._id; const code = genCode(6); const result: GuildType = await GuildModel.findOneAndUpdate({ code }, update, { upsert: true, new: true }) - .select('code name icon notice introduce lv fund memberCnt managerCnt activeDaily activeWeekly ceLimit isAuto leader structure') - .populate('leader', 'name sHid headHid lv updatedAt', 'Role') + .populate('leader', 'roleName sHid headHid lv loginTime ce', 'Role') .lean(); return result; } public static async checkName(name: string) { - const result = await GuildModel.findOne({ name, status: 1 }).lean(); + const result = await GuildModel.findOne({ name, status: GUILD_STATUS.RUNNING }).lean(); return !!result; } + public static async findByCondition(page: number, showPeopleMax: boolean, name: string) { + const condition = {}; + if(!showPeopleMax) { + condition['isMemberMax'] = false; + } + if(!!name) { + condition['name'] = { $regex: new RegExp(name, 'i') } + } + const guildList = await GuildModel.find(condition) + .limit(GUILD_PER_PAGE).skip((page - 1) * GUILD_PER_PAGE) + .select('code icon name lv memberCnt leader ceLimit isAuto') + .populate('leader', 'roleName', 'Role') + .lean(); + return guildList; + } + + public static async findByCode(code: string) { + const result = await GuildModel.findOne({ code, status: GUILD_STATUS.RUNNING }, { _id: 0 }) + .populate('leader', 'roleName sHid headHid lv loginTime ce', 'Role') + .lean(); + return result; + } } export const GuildModel = getModelForClass(Guild); export interface GuildType extends Pick, keyof Guild> { }; export type GuildUpdateParam = Partial; // 将所有字段变成可选项 -type CreateParam = Pick function getInitStructure() { let structure = new Array(); diff --git a/shared/db/Role.ts b/shared/db/Role.ts index 14e9e2150..ee73e4656 100644 --- a/shared/db/Role.ts +++ b/shared/db/Role.ts @@ -6,6 +6,7 @@ import User from './User'; import { shouldRefresh } from '../pubUtils/util'; import { initRoleAtrr } from '../pubUtils/playerCe'; import Hero,{} from '../db/Hero'; +import { nowSeconds } from '../pubUtils/timeUtil'; interface roleUpdate { ce?: number; _id?:number; @@ -162,10 +163,10 @@ export default class Role extends BaseModel { @prop({ required: true, default: 1 }) loginCnt: number; // 登录次数 - @prop({ required: true }) - createTime: Date; // 创建时间 - @prop({ required: true }) - loginTime: Date; // 更新 / 登录时间 + @prop({ required: true, default: nowSeconds() }) + createTime: number; // 创建时间 + @prop({ required: true, default: nowSeconds() }) + loginTime: number; // 更新 / 登录时间 @prop({ required: true, type: Number, default: [], _id: false }) funcs: Array; // 开启了的功能 @@ -217,8 +218,12 @@ export default class Role extends BaseModel { @prop({ required: true, type: Teraph, default: getInitialTeraph(), _id: false }) teraphs:Array; + // 公会 + @prop({ required: true, default: 0 }) + quitGuildTime: number; // 上次退出公会的时间 + public static async findByUid(uid: number, serverId: number, lean = true) { - const role: RoleType = await RoleModel.findOne({ 'userInfo.uid': uid, serverId }).lean(lean); + const role: RoleType = await RoleModel.findOneAndUpdate({ 'userInfo.uid': uid, serverId }, { loginTime: nowSeconds() }, { new: true }).lean(lean); return role; } diff --git a/shared/db/UserGuild.ts b/shared/db/UserGuild.ts index 6afbf97fe..940be8960 100644 --- a/shared/db/UserGuild.ts +++ b/shared/db/UserGuild.ts @@ -1,8 +1,9 @@ import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType, Ref } from '@typegoose/typegoose'; -import Role from './Role'; +import Role, { RoleType } from './Role'; import Guild from './Guild'; import { GUILD_AUTH, USER_GUILD_STATUS, GUILD_JOB } from '../consts'; +import { GUILD } from '../pubUtils/dicParam'; class ActiveRecord { @prop({ required: true }) @@ -22,9 +23,6 @@ export default class UserGuild extends BaseModel { @prop({ required: true }) role: Ref; - @prop({ required: true }) - guild: Ref; - @prop({ required: true, default: GUILD_AUTH.MEMBER, enum: GUILD_AUTH }) auth: number; @@ -45,18 +43,32 @@ export default class UserGuild extends BaseModel { @prop({ required: true, default: new Date() }) refTimeWeekly: number; - - public static async getMyGuild(roleId: string) { - const myGuild: UserGuildType = await UserGuildModel.findOne({ roleId, status: USER_GUILD_STATUS.ON }).populate('guild').lean(); - return myGuild||{ auth: GUILD_AUTH.OTHERS }; + + public static async getMyAuth(roleId: string, userGuild?: UserGuildType) { + let myGuild: UserGuildType; + if(!userGuild) { + myGuild = await this.getMyGuild(roleId, 'auth'); + } else { + myGuild = userGuild; + } + return myGuild?myGuild.auth: GUILD_AUTH.OTHERS; + } + + public static async getMyGuild(roleId: string, select?: string) { + const myGuild: UserGuildType = await UserGuildModel.findOne({ roleId, status: USER_GUILD_STATUS.ON }) + .select(select) + .lean(); + return myGuild; } - public static async createUserGuild(params: UserGuildCreateParam) { + public static async createUserGuild(guildCode: string, role: RoleType, isLeader: boolean) { const doc = new UserGuildModel(); - const update = Object.assign(doc.toJSON(), params); + let job = isLeader? GUILD_JOB.JIANGJUN: GUILD_JOB.SHIZHANG; + let auth = isLeader? GUILD_AUTH.LEADER: GUILD_AUTH.MEMBER; + const update = Object.assign(doc.toJSON(), { guildCode, roleId: role.roleId, job, auth }); delete update._id; - const result: UserGuildType = await UserGuildModel.findOneAndUpdate({ guildCode: params.guildCode, status: USER_GUILD_STATUS.ON }, update, { upsert: true, new: true }) + const result: UserGuildType = await UserGuildModel.findOneAndUpdate({ guildCode, status: USER_GUILD_STATUS.ON }, update, { upsert: true, new: true }) .select('honour job auth') .lean(); @@ -68,4 +80,3 @@ export const UserGuildModel = getModelForClass(UserGuild); export interface UserGuildType extends Pick, keyof UserGuild> { }; export type UserGuildUpdateParam = Partial; // 将所有字段变成可选项 -type UserGuildCreateParam = Pick; \ No newline at end of file diff --git a/shared/pubUtils/interface.ts b/shared/pubUtils/interface.ts index 3952042eb..63269861c 100644 --- a/shared/pubUtils/interface.ts +++ b/shared/pubUtils/interface.ts @@ -2,6 +2,8 @@ import { reduceCe } from "./util"; import { PvpEnemies, PvpHeroInfo, PvpOtherHeroes } from "../db/generalField"; +import { GuildType } from "../db/Guild"; +import { RoleType } from "../db/Role"; export interface RewardInter { id: number; @@ -165,4 +167,41 @@ export class PlayerDetail { if(detail.heroes) this.heroes = detail.heroes; if(detail.rank) this.rank = detail.rank; } +} + +// 军团返回 +export class GuildMainInfo { + code: string; // 军团唯一编号 + icon: number; // 头像 + name: string; // 军团名 + lv: number; // 等级 + memberCnt: number; // 人数 + isAuto: boolean; // 是否是自动加入 + ceLimit: number; // 战力条件 + + constructor(guild: GuildType) { + this.code = guild.code; + this.icon = guild.icon; + this.name = guild.name; + this.lv = guild.lv; + this.memberCnt = guild.memberCnt; + this.ceLimit = guild.ceLimit; + this.isAuto = guild.isAuto; + } +} + +// getGuildList里的返回 +export class GuildListInfo extends GuildMainInfo { + leader: string; // 团长名 + hasApply: boolean = false; // 是否有申请过 + + constructor(guild: GuildType) { + super(guild); + let leader = guild.leader; + this.leader = leader.roleName; + } + + setApply(hasApply: boolean) { + this.hasApply = hasApply; + } } \ No newline at end of file diff --git a/shared/pubUtils/timeUtil.ts b/shared/pubUtils/timeUtil.ts index ffaf2053a..27579b301 100644 --- a/shared/pubUtils/timeUtil.ts +++ b/shared/pubUtils/timeUtil.ts @@ -2,6 +2,10 @@ const PER_SECOND = 1 * 1000; const PER_DAY = 24 * 60 * 60; +export function getSeconds(time: Date) { + return Math.floor(time.getTime() / PER_SECOND); +} + export function nowSeconds() { return Math.floor(Date.now() / PER_SECOND ); }