Files
ZYZ/game-server/app/services/redisService.ts
T

581 lines
20 KiB
TypeScript

import { ACTIVITY_TYPE, getRedisKeyByRankType, GUILD_ACTIVITY_TYPE } from './../consts';
import * as Redis from 'redis';
import {REDIS_KEY} from '../consts'
import { pinus, ServerInfo } from 'pinus';
import { GuildRankParam, GuildLeader, RankParam, LineupParam } from '../domain/rank';
import { comBtlRanges } from '../pubUtils/data';
import { Rank, setRankRedisFromDb } from './rankService';
import { ServerlistModel } from '../db/Serverlist';
import moment = require('moment');
import { getRedisSubChannel } from '../pubUtils/sdkUtil';
import { getRandSingleEelm } from '../pubUtils/util';
import { ActivityModel } from '../db/Activity';
import { TimeLimitRankData } from '../domain/activityField/timeLimitRankField';
import { ActivityTimeLimitRankModel } from '../db/ActivityTimeLimitRank';
import { RoleModel, RoleType } from '../db/Role';
import { GuildType } from '../db/Guild';
import { ActivityGroupModel } from '../db/ActivityGroup';
/**
* 在服务重新启动时,将信息存入redis
*/
export async function initAllRank() {
console.log('******* initAllRank ******')
const serverList = await ServerlistModel.findByEnv(pinus.app.get('env'));
await delKeys(REDIS_KEY.ONLINE_USERS);
await delKeys(REDIS_KEY.USER_INFO);
await delKeys(REDIS_KEY.GUILD_INFO);
await delKeys(REDIS_KEY.TOP_LINEUP_INFO);
await delKeys(REDIS_KEY.HERO_INFO);
await delKeys(REDIS_KEY.TOWER_RANK);
await delKeys(REDIS_KEY.GUILD_LV_RANK);
await delKeys(REDIS_KEY.GUILD_ACTIVE_RANK);
await delKeys(REDIS_KEY.TOP_LINEUP_RANK);
await delKeys(REDIS_KEY.TOP_HERO_RANK);
await delKeys(REDIS_KEY.HERO_NUM_RANK);
await delKeys(REDIS_KEY.USER_LV);
await delKeys(REDIS_KEY.SUM_CE_RANK);
// await delKeys(REDIS_KEY.DUNGEON_RANK);
await delKeys(REDIS_KEY.DUNGEON_LINEUP);
await delKeys(REDIS_KEY.MAIN_RANK);
await delKeys(REDIS_KEY.MAIN_ELITE_RANK);
await delKeys(REDIS_KEY.HERO_RANK);
await delKeys(REDIS_KEY.SHOW_LINEUP);
// await delKeys(REDIS_KEY.PVP_RANK);
await delKeys(REDIS_KEY.GUILD_FUND);
await delKeys(REDIS_KEY.SUM_CE_SNAPSHOT);
await setRankRedisFromDb(REDIS_KEY.PVP_RANK, {});
for(let {id} of serverList) {
await initRank(id);
await initActivitiesRank(id)
}
}
/**
* 分服内初始redis排行榜
*
* @param serverId 服务器
*/
export async function initRank(serverId: number) {
// console.log('*****', 'initRank')
await setRankRedisFromDb(REDIS_KEY.TOWER_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.GUILD_LV_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.TOP_LINEUP_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.TOP_HERO_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.HERO_NUM_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.USER_LV, { serverId });
await setRankRedisFromDb(REDIS_KEY.SUM_CE_RANK, { serverId });
// await setRankRedisFromDb(REDIS_KEY.DUNGEON_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.MAIN_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.MAIN_ELITE_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.HERO_RANK, { serverId });
}
export async function initActivitiesRank(serverId: number) {
let serverTime = await getServerCreateTime(serverId);
let activityGroup = await ActivityGroupModel.findByServerId(serverId);
let activityGroupId = activityGroup.map(cur => cur.groupId);
let activities = await ActivityModel.findOpenActivityByType(activityGroupId, ACTIVITY_TYPE.TIME_LIMIT_RANK);
for(let activity of activities) {
let data = new TimeLimitRankData(activity, 0, serverTime);
if(data.beginTime <= Date.now() && data.endTime >= Date.now()) {
let redisKey = getRedisKeyByRankType(data.rankType, true);
let ranks = await ActivityTimeLimitRankModel.getRank(serverId, data.activityId);
let r = new Rank(redisKey, { serverId, activityId: data.activityId });
r.setIsInit(true);
for (let rank of ranks) {
if(r.infoKey == REDIS_KEY.USER_INFO) {
await r.setRankWithRoleInfo(rank.roleId, rank.score, rank.time||0, <RoleType>rank.role);
} else if(r.infoKey == REDIS_KEY.GUILD_INFO) {
await r.setRankWithGuildInfo(rank.guildCode, rank.score, rank.time||0, <GuildType>rank.guild);
}
}
}
}
}
/**
* 初始化某一个排行榜,如定时器内
* @param key redis内的key
*/
export async function initSingleRank(key: string) {
await delKeys(key);
if(key == REDIS_KEY.PVP_RANK) {
await setRankRedisFromDb(key, {});
} else {
const serverList = await ServerlistModel.findByEnv(pinus.app.get('env'));
for(let { id } of serverList) {
await setRankRedisFromDb(key, { serverId: id });
}
}
}
/**
* debug接口使用,直接删除排行榜数据
* @param params serverId => guildCodes
*/
export async function delGuildActivityRank(aid: number) {
if(aid == GUILD_ACTIVITY_TYPE.GATE_ACTIVITY) {
await delKeys(REDIS_KEY.USER_GATE_ACTIVITY);
await delKeys(REDIS_KEY.GATE_ACTIVITY);
} else if (aid == GUILD_ACTIVITY_TYPE.CITY_ACTIVITY) {
await delKeys(REDIS_KEY.USER_CITY_ACTIVITY);
await delKeys(REDIS_KEY.CITY_ACTIVITY);
} else if (aid == GUILD_ACTIVITY_TYPE.RACE_ACTIVITY) {
await delKeys(REDIS_KEY.RACE_ACTIVITY);
}
}
export async function delKeys(key: string) {
let keys = await redisClient().keysAsync(`${key}*`);
for(let key of keys) {
await redisClient().delAsync(key);
}
return keys
}
export async function setUserInfo(key: string, roleId: string, params: RankParam|GuildRankParam|LineupParam[]) {
let value = JSON.stringify(params);
return await redisClient().hsetAsync(key, roleId, value);
}
export async function updateUserInfo(key: string, roleId: string, arr: Array<{field: string, value:(string|number|GuildLeader)}>) {
let params = await redisClient().hgetAsync(key, roleId);
if(params) {
let obj = JSON.parse(params);
for(let {field, value} of arr) {
obj[field] = value;
}
return await redisClient().hsetAsync(key, roleId, JSON.stringify(obj));
}
}
/********排行榜结束 */
/**************** 寻宝相关 start */
/**
* @description 拼接匹配分组的 key
* @param {number} lv 藏宝图品阶
*/
function getComTeamKey(lv: number) {
return `${REDIS_KEY.COM_TEAM_SEARCH_PRE}:${lv}`;
}
/**
* @description 拼接匹配分组中 member 的值
* @param {string} roleId
* @param {string} sid connector serverId
* @returns
*/
function getComTeamValue(roleId: string, sid: string) {
return `${roleId}:${sid}`;
}
/**
* @description 把寻宝的玩家信息存入 redis
* @export
* @param {string} roleId
* @param {string} sid
* @param {Array<number>} qualityArr
* @param {number} lvRange
* @returns
*/
export async function setTeamSearchReq(roleId: string, sid: string, lvs: number[]) {
let cmds = [];
lvs.forEach(lv => {
if (lv) {
cmds.push(['sadd', getComTeamKey(lv), getComTeamValue(roleId, sid)]);
}
});
const res = await redisClient().multi(cmds).execAsync();
return res;
}
/**
* @description 从匹配队列中删除某个用户
* @export
* @param {string} roleId
* @param {string} sid
* @param {number} lv 如果不填表示所有品阶
*/
export async function rmRoleFromQueue(roleId: string, sid: string, lvs?: number[]) {
let cmds = [];
if (lvs) {
for(let lv of lvs) {
cmds.push(['srem', getComTeamKey(lv), getComTeamValue(roleId, sid)]);
}
} else {
for (let range of comBtlRanges()) {
cmds.push(['srem', getComTeamKey(range), getComTeamValue(roleId, sid)]);
}
}
await redisClient().multi(cmds).execAsync();
}
/**
* @description 在寻宝匹配队列中随机两个玩家
* @export
* @param {number} quality 队伍品质
* @param {number} lvRange 等级范围
* @returns
*/
export async function getTeamSearchByLv(lv: number) {
// TODO: 操作不具有原子性
const userInfos = await redisClient().srandmemberAsync(getComTeamKey(lv), 2);
console.log('getTeamSearchByLv: ' + userInfos);
if (!userInfos || !userInfos.length) return null;
let res = [];
for (let userInfo of userInfos) {
const decodeData = `${userInfo}`.split(':');
if (decodeData.length !== 2) return null;
res.push({roleId: decodeData[0], sid: decodeData[1]});
}
console.log('getTeamSearchByLv res: ', res);
return res;
}
/**
* @description 检查玩家是否在某个队列中等待寻宝匹配
* @export
* @param {string} roleId
* @param {string} sid
* @param {number} lv
* @returns
*/
export async function checkRoleInQueue(roleId: string, sid: string, lvs: number[]) {
for (let lv of lvs) {
let res = await redisClient().sismemberAsync(getComTeamKey(lv), `${roleId}:${sid}`);
if (res) {
return true;
}
};
return false;
}
/**
* @description 清除所有的寻宝匹配队列
* @export
*/
export async function clearComBtlQueue() {
await delKeys(REDIS_KEY.COM_TEAM_SEARCH_PRE);
}
export function setRedis(key: string, data: string) {
redisClient().setAsync(key, data);
}
export async function getRedis(key: string) {
const str = await redisClient().getAsync(key);
return str;
}
export async function delRedis(key: string) {
await redisClient().delAsync(key);
}
export function redisSidKey(roleId: string) {
return `login_roleId_${roleId}`;
}
export async function redisChannelServer(roomId: string) {
const sid = await redisClient().hgetAsync(REDIS_KEY.CHANNEL_SERVERS, roomId);
return sid;
}
export async function addRedisChannel(roomId: string, sid: string) {
const result = await redisClient().hsetAsync(REDIS_KEY.CHANNEL_SERVERS, roomId, sid);
return result;
}
export async function clearChannelServers() {
const result = await redisClient().delAsync(REDIS_KEY.CHANNEL_SERVERS);
return result;
}
/**
* 玩家上线
* @param roleId role表id
* @param userCode user表唯一字符串标识
* @param sid connector服的那个sid
*/
export async function roleLogin(roleId: string, userCode: string, sid: string, pkgName: string, createTime: number, serverId: number) {
await redisClient().hincrbyAsync(REDIS_KEY.ONLINE_CNT, roleId, 1);
return await redisClient().hsetAsync(REDIS_KEY.ONLINE_USERS, roleId, `${userCode}|${sid}|${pkgName}|${createTime}|${serverId}`);
}
/**
* 玩家下线
* @param roleId role表id
*/
export async function roleLeave(roleId: string) {
let role = await getRoleOnlineInfo(roleId);
let count = await redisClient().hincrbyAsync(REDIS_KEY.ONLINE_CNT, roleId, -1);
if(count <= 0) {
await redisClient().hdelAsync(REDIS_KEY.ONLINE_USERS, roleId);
}
return role;
}
/**
* 判断玩家是否在线
* @param roleId role表id
*/
export async function isRoleOnline(roleId: string) {
let result = await redisClient().hexistsAsync(REDIS_KEY.ONLINE_USERS, roleId);
return !!result;
}
/**
* 获得在线玩家userCode和sid
* @param roleId
*/
export async function getRoleOnlineInfo(roleId: string) {
let str = await redisClient().hgetAsync(REDIS_KEY.ONLINE_USERS, roleId);
if(str) {
try {
let result = str.split('|');
let [userCode, sid, pkgName, createTime, serverId] = result;
return {
isOnline: true, userCode, sid, pkgName,
createtime: parseInt(createTime),
serverId: parseInt(serverId)
}
} catch(e) {
return { isOnline: false }
}
} else {
return { isOnline: false }
}
}
export async function getRoleCreateTime(roleId: string, role?: RoleType) {
if(role && role.createTime) return role.createTime;
let onlineInfo = await getRoleOnlineInfo(roleId);
if(onlineInfo.isOnline) {
return onlineInfo.createtime;
} else {
let role = await RoleModel.findByRoleId(roleId);
return role?.createTime||0;
}
}
/**
* 获得所有在线的玩家
*/
export async function getAllOnlineRoles() {
let allRoles = await redisClient().hgetallAsync(REDIS_KEY.ONLINE_USERS);
let result = new Array<{roleId: string, userCode: string, sid: string, pkgName: string, serverId: number}>();
for(let roleId in allRoles) {
try{
let param = allRoles[roleId].split('|');
let [userCode, sid, pkgName, _createTime, serverId] = param;
result.push({ roleId, userCode, sid, pkgName, serverId: parseInt(serverId) });
} catch(e) {
continue;
}
}
return result;
}
/**
* 将玩家登录时间记录到
* @param userCode 玩家user表里的userCode
* @param isToday 累计记录还是当日记录
* @returns 分钟
*/
export async function incOnlineTime(userCode: string, isToday: boolean, incTime: number) {
moment.locale('zh-cn');
let today = moment().format('YYYYMMDD');
let key = isToday? `${REDIS_KEY.ONLINE_TIME}:${today}`: REDIS_KEY.ONLINE_TIME;
if(isToday) {
let tomorrow = moment().add(1, 'days').hours(0).minutes(0).seconds(0).unix();
let result = await redisClient().hincrbyAsync(key, userCode, incTime);
let result2 = await redisClient().expireatAsync(key, tomorrow);
// console.log(result2)
return result;
} else {
return await redisClient().hincrbyAsync(key, userCode, incTime);
}
}
export async function setOnlineTime(userCode: string, isToday: boolean, setTime: number) {
moment.locale('zh-cn');
let today = moment().format('YYYYMMDD');
let key = isToday? REDIS_KEY.ONLINE_TIME: `${REDIS_KEY.ONLINE_TIME}:${today}`;
if(isToday) {
let tomorrow = moment().add(1, 'days').hours(0).minutes(0).seconds(0).format('x');
await redisClient().expireatAsync(key, parseInt(tomorrow));
}
return await redisClient().hsetAsync(key, userCode, setTime);
}
// 排行榜是否存在
export async function smembersAsync(key: string) {
const result = await redisClient().smembersAsync(key);
return result;
}
export async function saddAsync(key: string, values: Array<string>) {
const result = await redisClient().saddAsync(key, values);
return result;
}
export async function sismemberAsync(key: string, value: string) {
const result = await redisClient().sismemberAsync(key, value);
return result;
}
export async function delAsync(key:string) {
await redisClient().delAsync(key);
}
export async function sremAsync(key:string, member:string) {
await redisClient().sremAsync(key, member);
}
/**************** 寻宝相关 end */
/**************** 数据库表存入缓存 */
export async function readDataBase() {
await setServerList();
}
async function setServerList() {
const serverList = await ServerlistModel.getAllServerList();
await redisClient().delAsync(REDIS_KEY.SERVER);
await redisClient().delAsync(REDIS_KEY.SERVER_OPEN_TIME);
for(let { id, name, openTime } of serverList) {
// console.log(roleId);
await redisClient().hsetAsync(REDIS_KEY.SERVER, `${id}`, `${name}`);
await redisClient().hsetAsync(REDIS_KEY.SERVER_OPEN_TIME, `${id}`, `${openTime}`);
}
}
export async function getAllServers() {
let servers = await redisClient().hgetallAsync(REDIS_KEY.SERVER);
let serverlist = new Array<number>();
for(let serverStr in servers) {
let serverId = parseInt(serverStr);
if(!isNaN(serverId) && !serverlist.includes(serverId)) {
serverlist.push(serverId);
}
}
return serverlist;
}
export async function getServerName(serverId: number) {
let name = await redisClient().hgetAsync(REDIS_KEY.SERVER, `${serverId}`);
return name||'常山少年'
}
export async function getServerCreateTime(serverId: number) {
let time = await redisClient().hgetAsync(REDIS_KEY.SERVER_OPEN_TIME, `${serverId}`);
return parseInt(time);
}
export function redisClient() {
const client: Redis.RedisClient = pinus.app.get('redis');
return client;
}
export async function hasKey(key: string) {
return await redisClient().existsAsync(key);
}
/**************** 数据库表end */
/*************** 将connector服插入redis *******/
export async function setConnectors(servers: ServerInfo[]) {
for(let server of servers) {
if(server.serverType == 'connector' && server.id != 'connector-server-gm') {
let { serverType, clientHost, clientPort, id } = server;
await redisClient().hsetAsync(REDIS_KEY.SYS_SERVER, server.id, JSON.stringify({ serverType, clientHost, clientPort, id }));
}
}
}
export async function removeConnectors(servers: string[]) {
for(let server of servers) {
let hasServer = await redisClient().hgetAsync(REDIS_KEY.SYS_SERVER, server);
if(!!hasServer) await redisClient().hdelAsync(REDIS_KEY.SYS_SERVER, server);
}
}
export async function checkConnectors() {
let servers = pinus.app.getServersByType('connector');
let redisServers = await redisClient().hgetallAsync(REDIS_KEY.SYS_SERVER);
for(let id in redisServers) {
let server = servers.find(cur => cur.id == id);
if(!server || server.id == 'connector-server-gm') {
await redisClient().hdelAsync(REDIS_KEY.SYS_SERVER, id);
}
}
}
/*************** 将connector服插入redis *******/
/*************** 订阅短链接redis *******/
export function redisClientPub() {
const client: Redis.RedisClient = pinus.app.get('redisPub');
return client;
}
export async function redisSubScribe() {
let env = pinus.app.get('env');
if(env == 'development') env = 'local'; // 本地处理
let payChannel = getRedisSubChannel(REDIS_KEY.PAY_CHANNEL, env);
let refundChannel = getRedisSubChannel(REDIS_KEY.REFUND_CHANNEL, env);
let treatRoleChannel = getRedisSubChannel(REDIS_KEY.TREAT_ROLE_CHANNEL, env);
let treatGuildChannel = getRedisSubChannel(REDIS_KEY.TREAT_GUILD_CHANNEL, env);
let surveyChannel = getRedisSubChannel(REDIS_KEY.SURVEY_CHANNEL, env);
await redisClientPub().subscribeAsync(payChannel, treatRoleChannel, treatGuildChannel, refundChannel, surveyChannel);
redisClientPub().on('message', (channel, message) => {
console.log('**********redisSubScribe*******')
console.log('channel: ', channel, payChannel);
if(channel == payChannel) {
let servers = pinus.app.getServersByType('order');
let server = getRandSingleEelm(servers);
pinus.app.rpc.order.orderRemote.settleOrderFromRedisPub.toServer(server.id, message);
} else if (channel == treatRoleChannel) {
let servers = pinus.app.getServersByType('role');
let server = getRandSingleEelm(servers);
pinus.app.rpc.role.roleRemote.treatRoleName.toServer(server.id, message);
} else if (channel == treatGuildChannel) {
let servers = pinus.app.getServersByType('guild');
let server = getRandSingleEelm(servers);
pinus.app.rpc.guild.guildRemote.treatGuildName.toServer(server.id, message);
} else if (channel == refundChannel) {
let servers = pinus.app.getServersByType('order');
let server = getRandSingleEelm(servers);
pinus.app.rpc.order.orderRemote.refundOrderFromRedisPub.toServer(server.id, message);
} else if (channel == surveyChannel) {
let servers = pinus.app.getServersByType('role');
let server = getRandSingleEelm(servers);
pinus.app.rpc.role.roleRemote.sendSurveyMail.toServer(server.id, message);
}
});
}
/*************** 订阅短链接redis *******/