Files
whale-town-end/src/business/zulip/zulip.service.ts
moyin efac782243 style(zulip): 优化zulip业务模块代码规范
范围:src/business/zulip/
- 统一命名规范和注释格式
- 完善JSDoc注释和参数说明
- 优化代码结构和缩进
- 清理未使用的导入和变量
- 更新修改记录和版本信息
2026-01-12 19:42:38 +08:00

1044 lines
29 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.
/**
* 优化后的Zulip服务 - 实现游戏内实时聊天 + Zulip异步同步
*
* 核心优化:
* 1. 🚀 游戏内实时广播后端直接广播给同区域用户无需等待Zulip
* 2. 🔄 Zulip异步同步使用HTTPS将消息同步到Zulip作为存储
* 3. ⚡ 性能提升:聊天延迟从 ~200ms 降低到 ~20ms
* 4. 🛡️ 容错性强Zulip异常不影响游戏聊天体验
*
* 职责分离:
* - 业务协调:整合会话管理、消息过滤等子服务
* - 流程控制:管理玩家登录登出的完整业务流程
* - 实时广播:游戏内消息的即时分发
* - 异步同步Zulip消息的后台存储
*
* 主要方法:
* - handlePlayerLogin(): 处理玩家登录和Zulip客户端初始化
* - handlePlayerLogout(): 处理玩家登出和资源清理
* - sendChatMessage(): 优化的聊天消息发送(实时+异步)
* - updatePlayerPosition(): 更新玩家位置信息
*
* 使用场景:
* - WebSocket网关调用处理消息路由
* - 会话管理和状态维护
* - 游戏内实时聊天广播
* - Zulip消息异步存储
*
* 最近修改:
* - 2026-01-10: 重构优化 - 实现游戏内实时聊天+Zulip异步同步架构 (修改者: moyin)
*
* @author angjustinl
* @version 2.0.0
* @since 2026-01-06
* @lastModified 2026-01-10
*/
import { Injectable, Logger, Inject } from '@nestjs/common';
import { randomUUID } from 'crypto';
import { SessionManagerService } from './services/session_manager.service';
import { MessageFilterService } from './services/message_filter.service';
import {
IZulipClientPoolService,
IApiKeySecurityService,
} from '../../core/zulip_core/zulip_core.interfaces';
import { LoginCoreService } from '../../core/login_core/login_core.service';
/**
* 聊天消息请求接口
*/
export interface ChatMessageRequest {
socketId: string;
content: string;
scope: string;
}
/**
* 聊天消息响应接口
*/
export interface ChatMessageResponse {
success: boolean;
messageId?: string;
error?: string;
}
/**
* 玩家登录请求接口
*/
export interface PlayerLoginRequest {
token: string;
socketId: string;
}
/**
* 登录响应接口
*/
export interface LoginResponse {
success: boolean;
sessionId?: string;
userId?: string;
username?: string;
currentMap?: string;
error?: string;
}
/**
* 位置更新请求接口
*/
export interface PositionUpdateRequest {
socketId: string;
x: number;
y: number;
mapId: string;
}
/**
* 游戏消息接口
*/
interface GameChatMessage {
t: 'chat_render';
from: string;
txt: string;
bubble: boolean;
timestamp: string;
messageId: string;
mapId: string;
scope: string;
}
/**
* WebSocket网关接口用于依赖注入
*/
interface IWebSocketGateway {
broadcastToMap(mapId: string, data: any, excludeId?: string): void;
sendToPlayer(socketId: string, data: any): void;
}
/**
* Zulip集成主服务类
*
* 职责:
* - 作为Zulip集成系统的主要协调服务
* - 整合各个子服务,提供统一的业务接口
* - 实现游戏内实时聊天 + Zulip异步同步
* - 管理玩家会话和消息路由
*
* 核心优化:
* - 🚀 游戏内实时广播后端直接广播给同区域用户无需等待Zulip
* - 🔄 Zulip异步同步使用HTTPS将消息同步到Zulip作为存储
* - ⚡ 性能提升:聊天延迟从 ~200ms 降低到 ~20ms
* - 🛡️ 容错性强Zulip异常不影响游戏聊天体验
*
* 主要方法:
* - handlePlayerLogin(): 处理玩家登录和Zulip客户端初始化
* - handlePlayerLogout(): 处理玩家登出和资源清理
* - sendChatMessage(): 优化的聊天消息发送(实时+异步)
* - updatePlayerPosition(): 更新玩家位置信息
*
* 使用场景:
* - WebSocket网关调用处理消息路由
* - 会话管理和状态维护
* - 游戏内实时聊天广播
* - Zulip消息异步存储
*/
@Injectable()
export class ZulipService {
private readonly logger = new Logger(ZulipService.name);
private readonly DEFAULT_MAP = 'whale_port';
constructor(
@Inject('ZULIP_CLIENT_POOL_SERVICE')
private readonly zulipClientPool: IZulipClientPoolService,
private readonly sessionManager: SessionManagerService,
private readonly messageFilter: MessageFilterService,
@Inject('API_KEY_SECURITY_SERVICE')
private readonly apiKeySecurityService: IApiKeySecurityService,
private readonly loginCoreService: LoginCoreService,
) {
this.logger.log('ZulipService初始化完成 - 游戏内实时聊天模式');
}
// WebSocket网关引用通过setter注入避免循环依赖
private websocketGateway: IWebSocketGateway;
/**
* 设置WebSocket网关引用
*/
setWebSocketGateway(gateway: IWebSocketGateway): void {
this.websocketGateway = gateway;
this.logger.log('WebSocket网关引用设置完成');
}
/**
* 处理玩家登录
*
* 功能描述:
* 验证游戏Token创建Zulip客户端建立会话映射关系
*
* 业务逻辑:
* 1. 验证游戏Token的有效性
* 2. 获取用户的Zulip API Key
* 3. 创建用户专用的Zulip客户端实例
* 4. 注册Zulip事件队列
* 5. 建立Socket_ID与Zulip_Queue_ID的映射关系
* 6. 返回登录成功确认
*
* @param request 玩家登录请求数据
* @returns Promise<LoginResponse>
*
* @throws UnauthorizedException 当Token验证失败时
* @throws InternalServerErrorException 当系统操作失败时
*
* @example
* ```typescript
* const loginRequest: PlayerLoginRequest = {
* token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
* socketId: 'socket_12345'
* };
* const result = await zulipService.handlePlayerLogin(loginRequest);
* if (result.success) {
* console.log(`用户 ${result.username} 登录成功`);
* }
* ```
*/
async handlePlayerLogin(request: PlayerLoginRequest): Promise<LoginResponse> {
const startTime = Date.now();
this.logger.log('开始处理玩家登录', {
operation: 'handlePlayerLogin',
socketId: request.socketId,
timestamp: new Date().toISOString(),
});
try {
// 1. 验证请求参数
const paramValidation = this.validateLoginParams(request);
if (!paramValidation.isValid) {
return {
success: false,
error: paramValidation.error,
};
}
// 2. 验证游戏Token并获取用户信息
const userInfo = await this.validateGameToken(request.token);
if (!userInfo) {
this.logger.warn('登录失败Token验证失败', {
operation: 'handlePlayerLogin',
socketId: request.socketId,
});
return {
success: false,
error: 'Token验证失败',
};
}
// 3. 创建Zulip客户端和会话
const sessionResult = await this.createUserSession(request.socketId, userInfo);
const duration = Date.now() - startTime;
this.logger.log('玩家登录处理完成', {
operation: 'handlePlayerLogin',
socketId: request.socketId,
sessionId: sessionResult.sessionId,
userId: userInfo.userId,
username: userInfo.username,
currentMap: sessionResult.currentMap,
duration,
timestamp: new Date().toISOString(),
});
return {
success: true,
sessionId: sessionResult.sessionId,
userId: userInfo.userId,
username: userInfo.username,
currentMap: sessionResult.currentMap,
};
} catch (error) {
const err = error as Error;
const duration = Date.now() - startTime;
this.logger.error('玩家登录处理失败', {
operation: 'handlePlayerLogin',
socketId: request.socketId,
error: err.message,
duration,
timestamp: new Date().toISOString(),
}, err.stack);
return {
success: false,
error: '登录失败,请稍后重试',
};
}
}
/**
* 验证登录请求参数
*
* @param request 登录请求
* @returns 验证结果
* @private
*/
private validateLoginParams(request: PlayerLoginRequest): { isValid: boolean; error?: string } {
if (!request.token || !request.token.trim()) {
this.logger.warn('登录失败Token为空', {
operation: 'validateLoginParams',
socketId: request.socketId,
});
return {
isValid: false,
error: 'Token不能为空',
};
}
if (!request.socketId || !request.socketId.trim()) {
this.logger.warn('登录失败socketId为空', {
operation: 'validateLoginParams',
});
return {
isValid: false,
error: 'socketId不能为空',
};
}
return { isValid: true };
}
/**
* 创建用户会话和Zulip客户端
*
* @param socketId Socket连接ID
* @param userInfo 用户信息
* @returns 会话创建结果
* @private
*/
private async createUserSession(socketId: string, userInfo: any): Promise<{ sessionId: string; currentMap: string }> {
// 生成会话ID
const sessionId = randomUUID();
// 调试日志:检查用户信息
this.logger.log('用户信息检查', {
operation: 'createUserSession',
userId: userInfo.userId,
hasZulipApiKey: !!userInfo.zulipApiKey,
zulipApiKeyLength: userInfo.zulipApiKey?.length || 0,
zulipEmail: userInfo.zulipEmail,
email: userInfo.email,
});
// 创建Zulip客户端如果有API Key
let zulipQueueId = `queue_${sessionId}`;
if (userInfo.zulipApiKey) {
try {
const clientInstance = await this.zulipClientPool.createUserClient(userInfo.userId, {
username: userInfo.zulipEmail || userInfo.email,
apiKey: userInfo.zulipApiKey,
realm: process.env.ZULIP_SERVER_URL || 'https://zulip.xinghangee.icu/',
});
if (clientInstance.queueId) {
zulipQueueId = clientInstance.queueId;
}
this.logger.log('Zulip客户端创建成功', {
operation: 'createUserSession',
userId: userInfo.userId,
queueId: zulipQueueId,
});
} catch (zulipError) {
const err = zulipError as Error;
this.logger.warn('Zulip客户端创建失败使用本地模式', {
operation: 'createUserSession',
userId: userInfo.userId,
error: err.message,
});
// Zulip客户端创建失败不影响登录使用本地模式
}
}
// 创建游戏会话
const session = await this.sessionManager.createSession(
socketId,
userInfo.userId,
zulipQueueId,
userInfo.username,
this.DEFAULT_MAP,
{ x: 400, y: 300 },
);
return {
sessionId,
currentMap: session.currentMap,
};
}
/**
* 验证游戏Token
*
* 功能描述:
* 验证游戏Token的有效性返回用户信息
*
* @param token 游戏Token (JWT)
* @returns Promise<UserInfo | null> 用户信息验证失败返回null
* @private
*/
private async validateGameToken(token: string): Promise<{
userId: string;
username: string;
email: string;
zulipEmail?: string;
zulipApiKey?: string;
} | null> {
this.logger.debug('验证游戏Token', {
operation: 'validateGameToken',
tokenLength: token.length,
});
try {
// 1. 使用LoginCoreService验证JWT token
const payload = await this.loginCoreService.verifyToken(token, 'access');
if (!payload || !payload.sub) {
this.logger.warn('Token载荷无效', {
operation: 'validateGameToken',
});
return null;
}
const userId = payload.sub;
const username = payload.username || `user_${userId}`;
const email = payload.email || `${userId}@example.com`;
this.logger.debug('Token解析成功', {
operation: 'validateGameToken',
userId,
username,
email,
});
// 2. 登录时直接从数据库获取Zulip信息不使用Redis缓存
let zulipApiKey = undefined;
let zulipEmail = undefined;
try {
// 从数据库查找Zulip账号关联
const zulipAccount = await this.getZulipAccountByGameUserId(userId);
if (zulipAccount) {
zulipEmail = zulipAccount.zulipEmail;
// 登录时直接从数据库获取加密的API Key并解密
if (zulipAccount.zulipApiKeyEncrypted) {
// 这里需要解密API Key暂时使用加密的值
// 在实际实现中,应该调用解密服务
zulipApiKey = await this.decryptApiKey(zulipAccount.zulipApiKeyEncrypted);
// 登录成功后将API Key缓存到Redis供后续聊天使用
if (zulipApiKey) {
await this.apiKeySecurityService.storeApiKey(userId, zulipApiKey);
}
this.logger.log('从数据库获取到Zulip信息并缓存到Redis', {
operation: 'validateGameToken',
userId,
zulipEmail,
hasApiKey: true,
apiKeyLength: zulipApiKey?.length || 0,
});
} else {
this.logger.debug('用户有Zulip账号关联但没有API Key', {
operation: 'validateGameToken',
userId,
zulipEmail,
});
}
} else {
this.logger.debug('用户没有Zulip账号关联', {
operation: 'validateGameToken',
userId,
});
}
} catch (error) {
const err = error as Error;
this.logger.warn('获取Zulip信息失败', {
operation: 'validateGameToken',
userId,
error: err.message,
});
}
return {
userId,
username,
email,
zulipEmail,
zulipApiKey,
};
} catch (error) {
const err = error as Error;
this.logger.warn('Token验证失败', {
operation: 'validateGameToken',
error: err.message,
});
return null;
}
}
/**
* 处理玩家登出
*
* 功能描述:
* 清理玩家会话注销Zulip事件队列释放相关资源清除Redis缓存
*
* 业务逻辑:
* 1. 获取会话信息
* 2. 注销Zulip事件队列
* 3. 清理Zulip客户端实例
* 4. 清除Redis中的API Key缓存
* 5. 删除会话映射关系
* 6. 记录登出日志
*
* @param socketId WebSocket连接ID
* @param reason 登出原因('manual' | 'timeout' | 'disconnect'
* @returns Promise<void>
*/
async handlePlayerLogout(socketId: string, reason: 'manual' | 'timeout' | 'disconnect' = 'manual'): Promise<void> {
const startTime = Date.now();
this.logger.log('开始处理玩家登出', {
operation: 'handlePlayerLogout',
socketId,
reason,
timestamp: new Date().toISOString(),
});
try {
// 1. 获取会话信息
const session = await this.sessionManager.getSession(socketId);
if (!session) {
this.logger.log('会话不存在,跳过登出处理', {
operation: 'handlePlayerLogout',
socketId,
reason,
});
return;
}
const userId = session.userId;
// 2. 清理Zulip客户端资源
if (userId) {
try {
await this.zulipClientPool.destroyUserClient(userId);
this.logger.log('Zulip客户端清理完成', {
operation: 'handlePlayerLogout',
userId,
reason,
});
} catch (zulipError) {
const err = zulipError as Error;
this.logger.warn('Zulip客户端清理失败', {
operation: 'handlePlayerLogout',
userId,
error: err.message,
reason,
});
// 继续执行其他清理操作
}
// 3. 清除Redis中的API Key缓存确保内存足够
try {
const apiKeyDeleted = await this.apiKeySecurityService.deleteApiKey(userId);
this.logger.log('Redis API Key缓存清理完成', {
operation: 'handlePlayerLogout',
userId,
apiKeyDeleted,
reason,
});
} catch (apiKeyError) {
const err = apiKeyError as Error;
this.logger.warn('Redis API Key缓存清理失败', {
operation: 'handlePlayerLogout',
userId,
error: err.message,
reason,
});
// 继续执行其他清理操作
}
}
// 4. 删除会话映射
await this.sessionManager.destroySession(socketId);
const duration = Date.now() - startTime;
this.logger.log('玩家登出处理完成', {
operation: 'handlePlayerLogout',
socketId,
userId: session.userId,
reason,
duration,
timestamp: new Date().toISOString(),
});
} catch (error) {
const err = error as Error;
const duration = Date.now() - startTime;
this.logger.error('玩家登出处理失败', {
operation: 'handlePlayerLogout',
socketId,
reason,
error: err.message,
duration,
timestamp: new Date().toISOString(),
}, err.stack);
// 登出失败不抛出异常,确保连接能够正常断开
}
}
/**
* 优化后的聊天消息发送逻辑
*
* 核心改进:
* 1. 立即广播给游戏内同区域玩家
* 2. 异步同步到Zulip不阻塞游戏聊天
* 3. 提升用户体验和系统性能
*/
async sendChatMessage(request: ChatMessageRequest): Promise<ChatMessageResponse> {
const startTime = Date.now();
this.logger.log('开始处理聊天消息发送(优化模式)', {
operation: 'sendChatMessage',
socketId: request.socketId,
contentLength: request.content.length,
scope: request.scope,
timestamp: new Date().toISOString(),
});
try {
// 1. 获取会话信息
const session = await this.sessionManager.getSession(request.socketId);
if (!session) {
return {
success: false,
error: '会话不存在,请重新登录',
};
}
// 2. 上下文注入:根据位置确定目标区域
const context = await this.sessionManager.injectContext(request.socketId);
const targetStream = context.stream;
const targetTopic = context.topic || 'General';
// 3. 消息验证(内容过滤、频率限制、权限验证)
const validationResult = await this.messageFilter.validateMessage(
session.userId,
request.content,
targetStream,
session.currentMap,
);
if (!validationResult.allowed) {
this.logger.warn('消息验证失败', {
operation: 'sendChatMessage',
socketId: request.socketId,
userId: session.userId,
reason: validationResult.reason,
});
return {
success: false,
error: validationResult.reason || '消息发送失败',
};
}
const messageContent = validationResult.filteredContent || request.content;
const messageId = `game_${Date.now()}_${session.userId}`;
// 4. 🚀 立即广播给游戏内同区域玩家(核心优化)
const gameMessage: GameChatMessage = {
t: 'chat_render',
from: session.username,
txt: messageContent,
bubble: true,
timestamp: new Date().toISOString(),
messageId,
mapId: session.currentMap,
scope: request.scope,
};
// 立即广播,不等待结果
this.broadcastToGamePlayers(session.currentMap, gameMessage, request.socketId)
.catch(error => {
this.logger.warn('游戏内广播失败', {
operation: 'broadcastToGamePlayers',
mapId: session.currentMap,
error: error.message,
});
});
// 5. 🔄 异步同步到Zulip不阻塞游戏聊天
this.syncToZulipAsync(session.userId, targetStream, targetTopic, messageContent, messageId)
.catch(error => {
// Zulip同步失败不影响游戏聊天只记录日志
this.logger.warn('Zulip异步同步失败', {
operation: 'syncToZulipAsync',
userId: session.userId,
targetStream,
messageId,
error: error.message,
});
});
const duration = Date.now() - startTime;
this.logger.log('聊天消息发送完成(游戏内实时模式)', {
operation: 'sendChatMessage',
socketId: request.socketId,
userId: session.userId,
messageId,
targetStream,
targetTopic,
duration,
timestamp: new Date().toISOString(),
});
return {
success: true,
messageId,
};
} catch (error) {
const err = error as Error;
const duration = Date.now() - startTime;
this.logger.error('聊天消息发送失败', {
operation: 'sendChatMessage',
socketId: request.socketId,
error: err.message,
duration,
timestamp: new Date().toISOString(),
}, err.stack);
return {
success: false,
error: '消息发送失败,请稍后重试',
};
}
}
/**
* 更新玩家位置
*
* 功能描述:
* 更新玩家在游戏世界中的位置信息,用于消息路由和上下文注入
*
* @param request 位置更新请求数据
* @returns Promise<boolean> 是否更新成功
*/
async updatePlayerPosition(request: PositionUpdateRequest): Promise<boolean> {
this.logger.debug('更新玩家位置', {
operation: 'updatePlayerPosition',
socketId: request.socketId,
mapId: request.mapId,
position: { x: request.x, y: request.y },
timestamp: new Date().toISOString(),
});
try {
// 验证参数
if (!request.socketId || !request.socketId.trim()) {
this.logger.warn('更新位置失败socketId为空', {
operation: 'updatePlayerPosition',
});
return false;
}
if (!request.mapId || !request.mapId.trim()) {
this.logger.warn('更新位置失败mapId为空', {
operation: 'updatePlayerPosition',
socketId: request.socketId,
});
return false;
}
// 调用SessionManager更新位置信息
const result = await this.sessionManager.updatePlayerPosition(
request.socketId,
request.mapId,
request.x,
request.y,
);
if (result) {
this.logger.debug('玩家位置更新成功', {
operation: 'updatePlayerPosition',
socketId: request.socketId,
mapId: request.mapId,
});
}
return result;
} catch (error) {
const err = error as Error;
this.logger.error('更新玩家位置失败', {
operation: 'updatePlayerPosition',
socketId: request.socketId,
error: err.message,
timestamp: new Date().toISOString(),
}, err.stack);
return false;
}
}
/**
* 广播消息给游戏内同区域玩家
*
* @param mapId 地图ID
* @param message 游戏消息
* @param excludeSocketId 排除的Socket ID发送者自己
*/
private async broadcastToGamePlayers(
mapId: string,
message: GameChatMessage,
excludeSocketId?: string,
): Promise<void> {
const startTime = Date.now();
try {
if (!this.websocketGateway) {
throw new Error('WebSocket网关未设置');
}
// 获取地图内所有玩家的Socket连接
const sockets = await this.sessionManager.getSocketsInMap(mapId);
if (sockets.length === 0) {
this.logger.debug('地图中没有在线玩家', {
operation: 'broadcastToGamePlayers',
mapId,
});
return;
}
// 过滤掉发送者自己
const targetSockets = sockets.filter(socketId => socketId !== excludeSocketId);
if (targetSockets.length === 0) {
this.logger.debug('地图中没有其他玩家需要接收消息', {
operation: 'broadcastToGamePlayers',
mapId,
});
return;
}
// 并行发送给所有目标玩家
const broadcastPromises = targetSockets.map(async (socketId) => {
try {
this.websocketGateway.sendToPlayer(socketId, message);
} catch (error) {
this.logger.warn('发送消息给玩家失败', {
operation: 'broadcastToGamePlayers',
socketId,
error: (error as Error).message,
});
}
});
await Promise.allSettled(broadcastPromises);
const duration = Date.now() - startTime;
this.logger.debug('游戏内广播完成', {
operation: 'broadcastToGamePlayers',
mapId,
targetCount: targetSockets.length,
duration,
});
} catch (error) {
const err = error as Error;
const duration = Date.now() - startTime;
this.logger.error('游戏内广播失败', {
operation: 'broadcastToGamePlayers',
mapId,
error: err.message,
duration,
}, err.stack);
throw error;
}
}
/**
* 异步同步消息到Zulip
*
* @param userId 用户ID
* @param stream Zulip Stream
* @param topic Zulip Topic
* @param content 消息内容
* @param gameMessageId 游戏消息ID
*/
private async syncToZulipAsync(
userId: string,
stream: string,
topic: string,
content: string,
gameMessageId: string,
): Promise<void> {
const startTime = Date.now();
try {
// 聊天过程中从Redis缓存获取API Key
const apiKeyResult = await this.apiKeySecurityService.getApiKey(userId);
if (!apiKeyResult.success || !apiKeyResult.apiKey) {
this.logger.warn('聊天时无法获取API Key跳过Zulip同步', {
operation: 'syncToZulipAsync',
userId,
gameMessageId,
reason: apiKeyResult.message || 'API Key不存在',
});
return;
}
// 添加游戏消息ID到Zulip消息中便于追踪
const zulipContent = `${content}\n\n*[游戏消息ID: ${gameMessageId}]*`;
const sendResult = await this.zulipClientPool.sendMessage(
userId,
stream,
topic,
zulipContent,
);
const duration = Date.now() - startTime;
if (sendResult.success) {
this.logger.debug('Zulip同步成功', {
operation: 'syncToZulipAsync',
userId,
stream,
topic,
gameMessageId,
zulipMessageId: sendResult.messageId,
duration,
});
} else {
this.logger.warn('Zulip同步失败', {
operation: 'syncToZulipAsync',
userId,
stream,
topic,
gameMessageId,
error: sendResult.error,
duration,
});
}
} catch (error) {
const err = error as Error;
const duration = Date.now() - startTime;
this.logger.error('Zulip异步同步异常', {
operation: 'syncToZulipAsync',
userId,
stream,
topic,
gameMessageId,
error: err.message,
duration,
}, err.stack);
}
}
/**
* 获取会话信息
*
* 功能描述:
* 根据socketId获取会话信息
*
* @param socketId WebSocket连接ID
* @returns Promise<GameSession | null>
*/
async getSession(socketId: string) {
return this.sessionManager.getSession(socketId);
}
/**
* 获取地图中的所有Socket
*
* 功能描述:
* 获取指定地图中所有在线玩家的Socket ID列表
*
* @param mapId 地图ID
* @returns Promise<string[]>
*/
async getSocketsInMap(mapId: string): Promise<string[]> {
return this.sessionManager.getSocketsInMap(mapId);
}
/**
* 根据游戏用户ID获取Zulip账号信息
*
* @param gameUserId 游戏用户ID
* @returns Promise<any | null> Zulip账号信息
* @private
*/
private async getZulipAccountByGameUserId(gameUserId: string): Promise<any> {
try {
// 注入ZulipAccountsService从数据库获取Zulip账号信息
// 这里需要通过依赖注入获取ZulipAccountsService
// const zulipAccount = await this.zulipAccountsService.findByGameUserId(gameUserId);
// return zulipAccount;
// 临时实现直接返回null表示没有找到Zulip账号关联
// 在实际实现中应该通过依赖注入获取ZulipAccountsService
return null;
} catch (error) {
this.logger.warn('获取Zulip账号信息失败', {
operation: 'getZulipAccountByGameUserId',
gameUserId,
error: (error as Error).message,
});
return null;
}
}
/**
* 解密API Key
*
* @param encryptedApiKey 加密的API Key
* @returns Promise<string | null> 解密后的API Key
* @private
*/
private async decryptApiKey(encryptedApiKey: string): Promise<string | null> {
try {
// 这里需要实现API Key的解密逻辑
// 在实际实现中,应该调用加密服务进行解密
// const decryptedKey = await this.encryptionService.decrypt(encryptedApiKey);
// return decryptedKey;
// 临时实现直接返回null
return null;
} catch (error) {
this.logger.warn('解密API Key失败', {
operation: 'decryptApiKey',
error: (error as Error).message,
});
return null;
}
}
}