Files
ZYZ/game-server/app/services/guildService.ts
2023-02-17 10:58:03 +08:00

462 lines
20 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { gameData, getGuildActiveWeekReward, getGuildActiveByIdAndType, getGoodById } from "../pubUtils/data";
import { GuildModel, GuildType, GuildUpdateParam } from "../db/Guild";
import { resResult, shouldRefresh } from "../pubUtils/util";
import { STATUS, MAIL_TYPE, GUILD_AUTH, GUILD_JOB, REDIS_KEY, CHAT_SERVER, TASK_TYPE, COUNTER, GUILD_REC_TYPE, PUSH_ROUTE, WAR_TYPE } from "../consts";
import { RoleModel, RoleType } from "../db/Role";
import { UserGuildModel, UserGuildType, WishGood } from "../db/UserGuild";
import { UserGuildApplyModel } from "../db/UserGuildApply";
import { getZeroPointD, getZeroPointOfTime, getZeroPointOfTimeD, nowSeconds } from "../pubUtils/timeUtil";
import { pinus, BackendSession, FrontendOrBackendSession } from "pinus";
import { ARMY } from "../pubUtils/dicParam";
import { sendMailByContent } from "./mailService";
import { initSingleRank, getRoleOnlineInfo, updateUserInfo, isRoleOnline } from "./redisService";
import { lockData, lockDataNoRetry } from '../services/redLockService';
import { ErrLogModel } from '../db/ErrLog';
import { DATA_NAME } from '../consts/dataName';
import { addRoleToGuildChannel } from "./chatService";
import { Rank } from "./rankService";
import { checkTask } from "./task/taskService";
import { CounterModel } from "../db/Counter";
import { getAuction } from "./auctionService";
import { changeGuildActivity } from "./activity/guildPayService";
import { GuildRecModel } from "../db/GuildRec";
import { sendMessageToGuildWithSuc, sendMessageToUserWithSuc } from "./pushService";
import { delGuildChannel, leaveGuildChannel } from "./chatChannelService";
import { GuildActiveModel, } from "../db/GuildActive";
import { RewardInter } from "../pubUtils/interface";
import { getGuildFundByRefTime } from "./donateService";
import { BattleRecordModel } from "../db/BattleRecord";
import { BossInstanceModel } from "../db/BossInstance";
import { pick } from "underscore";
import { memberJoinGuildToLeague } from "./gvg/gvgTeamService";
export async function getMyGuildInfo(roleId: string, sid: string, userGuild: UserGuildType, guild: GuildType, serverId: number, session: FrontendOrBackendSession) {
let leader = <RoleType>guild.leader;
let leaderIsOnline = await isRoleOnline(leader.roleId);
// 打开公会页面加入channel
if (userGuild.guildCode) {
addRoleToGuildChannel(roleId, sid, guild.code);
session.set('guildCode', guild.code);
session.push('guildCode', () => { });
}
// 获取排行榜
let r = new Rank(REDIS_KEY.GUILD_LV_RANK, { serverId });
const rank = await r.getMyRank({ guildCode: guild.code });
let { lv: guildLv, memberCnt } = guild;
let dicGuild = gameData.centerBase.get(guildLv);
let guildMemberMax = dicGuild && memberCnt >= dicGuild.peopleNum;
let guildActive = await getTodayGuildActive(guild.code);
// 返回
return { hasGuild: true, guildMemberMax, ...guild, activeDaily: guildActive, leader: { ...pick(leader, ['ce', 'lv', 'quitTime', 'roleId', 'roleName', 'head', 'frame', 'spine', 'title', 'isOnline']), isOnline: leaderIsOnline }, rank, myInfo: { ...userGuild } };
}
/**
* @description 检查该玩家是否有权限做操作
* @param func 操作id
* @param auth 权限
*/
export async function checkAuth(func: number, targetCode: string, guildAuth: number, myGuildCode = targetCode ) {
if(targetCode != myGuildCode || !guildAuth) {
guildAuth = GUILD_AUTH.OTHERS;
}
const dicGuildAuth = gameData.guildAuth.get(func);
// console.log('*checkAuth*', func, guildAuth, dicGuildAuth)
if (!dicGuildAuth) return false;
return dicGuildAuth.includes(guildAuth);
}
/**
* @description 加入公会
* @param code 公会code
* @param lv 公会当前等级,判断人数用
* @param roleId 加入的玩家
*/
export async function joinGuild(code: string, guildName: string, lv: number, roleId: string, serverId: number, session: BackendSession) {
// 周结算锁
let weeklySumLock = await lockDataNoRetry(serverId, DATA_NAME.WEEKLY_GUILD_SUM, code);
if (!!weeklySumLock.err) {
weeklySumLock.releaseCallback();
return { status: -1, resResult: resResult(STATUS.GUILD_WEEKLY_SUM) };
}
weeklySumLock.releaseCallback();
// 人数锁
let res: any = await lockData(serverId, DATA_NAME.JOIN_GUILD, code);// 加锁
if (!!res.err) return { status: -1, resResult: resResult(STATUS.REDLOCK_ERR) };
let role = await RoleModel.findByRoleId(roleId);
if (role.hasGuild) {
res.releaseCallback();//解锁
return { status: -1, resResult: resResult(STATUS.GUILD_HAS_JOIN) };
}
const dicCenterBase = gameData.centerBase.get(lv);
if (!dicCenterBase) {
res.releaseCallback();//解锁
return { status: -1, resResult: resResult(STATUS.DIC_DATA_NOT_FOUND) };
}
const maxMemberCnt = dicCenterBase.peopleNum;
const guild = await GuildModel.addMember(code, roleId, maxMemberCnt, serverId, role.ce);
if (!guild) {
res.releaseCallback();//解锁
return { status: -1, resResult: resResult(STATUS.GUILD_MEMBER_MAX) };
} else {
await updateUserInfo(REDIS_KEY.GUILD_INFO, guild.code, [{ field: 'guildCe', value: guild.guildCe }])
}
role = await RoleModel.joinGuild(roleId, code, guildName, false);
if (!role) {
await GuildModel.notAddMember(code, roleId, role.ce);
res.releaseCallback();//解锁
return { status: -1, resResult: resResult(STATUS.GUILD_HAS_JOIN) };
}
await updateUserInfo(REDIS_KEY.USER_INFO, roleId, [{ field: 'guildName', value: guildName }]);
await memberJoinGuildToLeague(guild, role);
const userGuild = await UserGuildModel.createUserGuild(guild.code, role, false);
if (!userGuild) {
res.releaseCallback();//解锁
return { status: -1, resResult: resResult(STATUS.GUILD_CREATE_ERROR) };
}
await UserGuildApplyModel.deleteApply(roleId); // 删除玩家所有对其他公会的申请
res.releaseCallback();//解锁
const { sid } = await getRoleOnlineInfo(roleId);
if (sid) {
await addRoleToGuildChannel(roleId, sid, code);
await pinus.app.rpc.connector.connectorRemote.setOtherUserGuildSession.toServer(sid,[{ roleId, userGuild }]);
sendMessageToUserWithSuc(roleId, PUSH_ROUTE.MEMBER_JOIN_GUILD, { hasGuild: true, code }, sid);
session.set('guildCode', code);
let auctionInfo = await getAuction(code, session);
sendMessageToUserWithSuc(roleId, PUSH_ROUTE.AUCTION_UPDATE, auctionInfo, sid);
changeGuildActivity(code, serverId, roleId, sid);
}
//成长任务-加入军团
await checkTask(serverId, roleId, sid, TASK_TYPE.GUILD_JOIN);
return { status: 0, guild, userGuild, roleName: role.roleName, memberCnt: guild.memberCnt, guildCe: guild.guildCe }
}
export async function getTodayGuildActive(guildCode: string) {
let today = getZeroPointD();
return await getGuildActiveByRefTime(guildCode, today);
}
export async function getGuildActiveByRefTime(guildCode: string, refTime: Date) {
let guildActive = await GuildActiveModel.getActive(guildCode, refTime);
return guildActive? guildActive.active: 0;
}
/**
* 增加活跃
* @param guildCode 军团唯一code
* @param serverId 区
* @param id 增加活跃的操作
* @param type 类型 activePoint内配的type
* @param active debug直接增加的活跃
*/
export async function addActive(roleId: string, serverId: number, id: number, type: number = 1, active?: number) {
let dicActiveWay = gameData.guildActiveWays.get(id);
if (id && !dicActiveWay) {
return { status: 0, resResult: resResult(STATUS.DIC_DATA_NOT_FOUND) };
}
if (!active) {
active = getGuildActiveByIdAndType(id, type);
}
let userGuild = await getUserGuildWithRefActive(roleId, 'activeRecord receivedActive activeDaily activeWeekly guildCode');
if (!userGuild) return { status: 0, resResult: resResult(STATUS.GUILD_NOT_FOUND) };
let guildCode = userGuild.guildCode;
// 周结算锁
let { activeRecord } = userGuild;
if (id != 0) { // 用于debug传0时直接增加活跃
let curActiveRecord = activeRecord.find(cur => cur.id == id);
if (curActiveRecord) {
curActiveRecord.count++;
} else {
curActiveRecord = { id, count: 1 };
activeRecord.push(curActiveRecord);
}
if (curActiveRecord.count > dicActiveWay.count) { // 次数超过时,不增加活跃
active = 0;
}
}
userGuild = await UserGuildModel.updateInfo(roleId, { activeRecord, activeUpdateTime: nowSeconds() }, { activeDaily: active, activeWeekly: active });
let guild = await GuildModel.updateInfo(guildCode, { activeUpdateTime: nowSeconds() }, { activeWeekly: active });
let guildActive = await GuildActiveModel.addActive(guildCode, getZeroPointD(), active);
// 排行榜更新
let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId });
await r.setRankWithGuildInfo(guildCode, guild.activeWeekly, guild.activeUpdateTime, guild);
let r2 = new Rank(REDIS_KEY.GUILD_LV_RANK, { serverId });
await r2.setRankWithGuildInfo2(guildCode, guild.lv, guild.activeWeekly, guild.lvUpdateTime, guild);
await pushGuildInfoUpdate(guildCode, { activeDaily: guildActive.active, activeWeekly: guild.activeWeekly });
return { status: 1, guild, userGuild };
}
/**
* 获取用户公会表并刷新活跃和每日捐赠以及每日许愿数据
* @param roleId 用户id
* @param select 筛选字段
*/
export async function getUserGuildWithRefActive(roleId: string, select?: string) {
let userGuild = await UserGuildModel.getMyGuild(roleId, select ? select + ' wishGoods +refTimeDaily' : '+refTimeDaily');
// console.log(JSON.stringify(userGuild))
if (!userGuild) return false;
let { receivedActive, refTimeDaily, guildCode, wishGoods, receiveBoxs } = userGuild;
const now = new Date();
let isRefDaily = shouldRefresh(refTimeDaily, now);
// console.log('####### isRefDaily', isRefDaily, refTimeDaily, now)
if (isRefDaily) {
userGuild = await UserGuildModel.resetDailyInfo(roleId);
if (!userGuild) return false;
if(refTimeDaily) {
await sendUnreceivedWishPool(wishGoods, roleId);
await sendUnreceivedActiveBox(roleId, guildCode, refTimeDaily, receivedActive);
await sendUnreceivedDonateBox(roleId, guildCode, refTimeDaily, receiveBoxs);
await sendUnreceivedBossWar(roleId, refTimeDaily);
}
}
return userGuild;
}
async function sendUnreceivedWishPool(wishGoods: WishGood[], roleId: string) {
wishGoods.map(async function ({ donateNames, goodId, drawCnt }) {
let goodInfo = getGoodById(goodId)
if (!goodInfo)
return;
for (let i = 0; i < drawCnt; i++) {
let donateName = donateNames[donateNames.length - i - 1];
await sendMailByContent(MAIL_TYPE.WISH_POOL_REWARD, roleId, {
params: [donateName, goodInfo.name],
goods: [{ id: goodId, count: 1 }]
});
}
});
}
async function sendUnreceivedActiveBox(roleId: string, guildCode: string, refTimeDaily: Date, receiveActiveBox: number[]) {
let refTime = getZeroPointOfTimeD(refTimeDaily);
let guildActive = await getGuildActiveByRefTime(guildCode, refTime);
let goods: RewardInter[] = [];
for(let [id, { activeDayPoint, reward }] of gameData.guildActiveDayReward) {
if(guildActive >= activeDayPoint && receiveActiveBox.indexOf(id) == -1) goods.push(...reward);
}
if(goods.length > 0) {
await sendMailByContent(MAIL_TYPE.GUILD_DAILY_BOX, roleId, { goods });
}
}
async function sendUnreceivedDonateBox(roleId: string, guildCode: string, refTimeDaily: Date, receiveBoxs: number[]) {
let refTime = getZeroPointOfTime(refTimeDaily);
let { fund: guildFund, donationLv } = await getGuildFundByRefTime(guildCode, refTime);
let goods: RewardInter[] = [];
for(let [id, { boxRewards, fund, level }] of gameData.armyDonateBox) {
if(level == donationLv && guildFund >= fund && receiveBoxs.indexOf(id) == -1) {
if(boxRewards) goods.push(...boxRewards);
}
}
if(goods.length > 0) {
await sendMailByContent(MAIL_TYPE.GUILD_FUND_BOX, roleId, { goods });
}
}
async function sendUnreceivedBossWar(roleId: string, refTimeDaily: Date) {
let beginTime = getZeroPointOfTime(refTimeDaily);
let endTime = beginTime + 24 * 60 * 60;
let battleRecords = await BattleRecordModel.findByWarTypeAndStatus(roleId, WAR_TYPE.BOSS, 0, new Date(beginTime * 1000), new Date(endTime * 1000));
for(let { battleCode, record = {} } of battleRecords) {
let { bossDamage = 0, bossInstanceCode } = record;
if(bossDamage > 0) {
let bossInstance = await BossInstanceModel.findByCode(bossInstanceCode);
if(!bossInstance) continue;
let dicBossBase = gameData.bossBaseByBossLv.get(bossInstance.bossLv);
let { basicReward, damageRewardTotal } = dicBossBase;
let damageReward = damageRewardTotal.map(cur => {
return { id: cur.id, count: Math.ceil(cur.count * bossDamage / bossInstance.bossTotalHp )}
})
let goods = [...basicReward, ...damageReward];
if(goods.length > 0) {
await sendMailByContent(MAIL_TYPE.GUILD_BOSS, roleId, { goods });
}
await BattleRecordModel.updateBattleRecordByCode(battleCode, { $set: { status: 1 } });
}
}
}
/**
* 每周结算上周公会周功勋和 活跃并发奖励(未完)
*
*/
export async function settleGuildWeekly() {
console.log('————— settleGuildWeekly —————');
const guildList = await GuildModel.findAllGuild('code activeWeekly memberCnt serverId');
// 周结算时1. 不能变动memberCnt 2.玩家activeWeekly不能变动 3.公会activeWeekly不能变动
for (let { code, memberCnt, serverId } of guildList) {
let res: any = await lockDataNoRetry(serverId, DATA_NAME.WEEKLY_GUILD_SUM, code);//加锁
if (!!res.err) {
await ErrLogModel.create(`settle guild lock data error: ${res.err}`)
}
const userGuildList = await UserGuildModel.getListByGuild(code, 'roleId auth activeWeekly', { activeWeekly: -1, activeUpdateTime: 1 });
let otherMemberCnt = 0; // 除了大将军以外从活跃高到底成员人数,用户计算活跃排名百分比
let members = new Map<string, number>();
for (let { roleId, auth, activeWeekly } of userGuildList) {
let job = 0;
if (auth == GUILD_AUTH.LEADER) {
job = GUILD_JOB.DAJIANGJUN;
} else if (activeWeekly <= ARMY.ARMY_WEEKHONOUR_LIMIT) {
job = GUILD_JOB.SHIBING;
} else {
otherMemberCnt++;
for (let [id, { rankProportion }] of gameData.guildPosition) {
let rankCnt = Math.ceil(memberCnt * rankProportion / 100);
job = id;
if (otherMemberCnt <= rankCnt) break;
}
}
await UserGuildModel.updateInfo(roleId, { job, activeWeekly: 0 }, {});
if (activeWeekly > ARMY.ARMY_WEEKHONOUR_LIMIT) { // 低于一定不发奖励
members.set(roleId, job);
}
}
// 转换周活跃奖励
let r = new Rank(REDIS_KEY.GUILD_LV_RANK, { serverId });
let rank = await r.getMyRank({ guildCode: code });
let allWeeklyReward = getGuildActiveWeekReward(rank);
for (let [roleId, job] of members) {
let jobActiveRatio = gameData.guildPosition.get(job).activeRatio;
let reward = allWeeklyReward.map(cur => {
return {
id: cur.id,
count: Math.ceil(cur.count * jobActiveRatio)
}
});
await sendMailByContent(MAIL_TYPE.GUILD_ACTIVE_REWARD, roleId, { goods: reward });
// 任务
await checkTask(serverId, roleId, null, TASK_TYPE.GUILD_JOB, { guildJob: job });
}
await GuildModel.updateInfo(code, { activeWeekly: 0 }, {});
res.releaseCallback();//解锁
}
await initSingleRank(REDIS_KEY.GUILD_ACTIVE_RANK);
await initSingleRank(REDIS_KEY.GUILD_LV_RANK);
let curSeasonNum = await CounterModel.getCounter(COUNTER.PVP_SEASON_NUM);
console.log('————— settleGuildWeekly结束 —————');
}
export async function getWishPool(userGuild: UserGuildType) {
let { guildCode: code, wishDntCnt, wishGoods, receivedWishPool, refTimeDaily } = userGuild;
const now = new Date();
let isRefDaily = shouldRefresh(refTimeDaily, now);
// console.log('####### isRefDaily', isRefDaily, refTimeDaily, now)
if (isRefDaily) {
wishGoods = []; receivedWishPool = [], wishDntCnt = 0;
}
let userGuilds = await UserGuildModel.getWishPoolGoods(code, ' wishDntCnt wishGoods roleId');
let list = [];
userGuilds.forEach(({ wishGoods, roleId }) => {
wishGoods.forEach(({ type, goodId, count, receiveCnt, drawCnt, id }) => {
list.push({ type, goodId, count, receiveCnt, drawCnt, id, roleId })
});
});
return { list, wishDntCnt: wishDntCnt || 0, wishGoods, receivedWishPool };
}
export function setUserGuildSession(session:FrontendOrBackendSession, myUserGuild: UserGuildType) {
if(myUserGuild) {
session.set('guildCode', myUserGuild.guildCode);
session.set('guildAuth', myUserGuild.auth);
session.push('guildCode', () => {});
session.push('guildAuth', () => {});
} else {
session.set('guildCode', null);
session.set('guildAuth', null);
session.push('guildCode', () => {});
session.push('guildAuth', () => {});
}
}
export async function getInvitationList(roleId: string, lastApplyCode = '') {
const result = await UserGuildApplyModel.findInviteByRole(roleId, lastApplyCode);
const list = result.map(cur => {
let guild = <GuildType>cur.guild;
let leader = <RoleType>guild.leader;
delete cur.guild;
return { applyCode: cur.applyCode, ...guild, leader: leader.roleName };
});
return list;
}
// 添加军团动态
export async function addGuildRecord(roleId: string, guildCode: string, type: GUILD_REC_TYPE, params: string[]) {
const rec = await GuildRecModel.createGuildRec(roleId, guildCode, type, params);
{
let { type, params, createTime } = rec;
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.GUILD_REC_ADD, { type, params, createTime });
}
}
export async function pushGuildInfoUpdate(guildCode: string, info: any) {
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.GUILD_INFO_UPDATE, info);
}
// 更换团长推送
export async function pushChangeGuildLeader(guildCode: string, managerCnt: number, newLeader: RoleType, oldLeaderId: string) {
let { roleId, roleName, frame, head, spine, lv, quitTime } = newLeader;
await pushGuildInfoUpdate(guildCode, { managerCnt, leader: { roleId, roleName, frame, head, spine, lv, quitTime } });
// 给旧团长推送
await sendMessageToUserWithSuc(oldLeaderId, PUSH_ROUTE.DEMOTION, { code: guildCode });
// 给新团长推送
await sendMessageToUserWithSuc(roleId, PUSH_ROUTE.PROMOTION, { code: guildCode });
}
/**
* 推送踢出某个成员
* @param guildCode 军团code
* @param roleId 踢出玩家roleId
* @param guild 军团信息
* @param sid 玩家sid
*/
export async function pushGuildMemberQuit(roleId: string, guildCode: string, guild: GuildType, sid: string) {
// 被踢出公会
await sendMessageToUserWithSuc(roleId, PUSH_ROUTE.MEMBER_QUIT, { code: guildCode, roleId }, sid);
// 更新人数减少
await pushGuildInfoUpdate(guildCode, { memberCnt: guild.memberCnt, guildCe: guild.guildCe });
leaveGuildChannel(roleId, sid, guildCode);
}
/**
* 推送解散军团
* @param guildCode 军团code
*/
export async function pushGuildDismiss(guildCode: string) {
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.DISMISS, { code: guildCode });
delGuildChannel(guildCode);
}