feat(gateway/chat): 新增聊天网关模块
范围:src/gateway/chat/ - 新增 ChatWebSocketGateway WebSocket 网关,处理实时聊天通信 - 新增 ChatController HTTP 控制器,提供聊天历史和系统状态接口 - 新增 ChatGatewayModule 模块配置,整合网关层组件 - 新增请求/响应 DTO 定义,提供数据验证和类型约束 - 新增完整的单元测试覆盖 - 新增模块 README 文档,包含接口说明、核心特性和风险评估
This commit is contained in:
195
src/gateway/chat/chat.controller.ts
Normal file
195
src/gateway/chat/chat.controller.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* 聊天 HTTP 控制器
|
||||
*
|
||||
* 功能描述:
|
||||
* - 处理聊天相关的 REST API 请求
|
||||
* - 只做协议转换,不包含业务逻辑
|
||||
* - 提供聊天历史查询和系统状态接口
|
||||
*
|
||||
* 架构层级:Gateway Layer(网关层)
|
||||
*
|
||||
* 最近修改:
|
||||
* - 2026-01-14: 代码规范优化 - 处理未使用的参数 (修改者: moyin)
|
||||
* - 2026-01-14: 代码规范优化 - 完善注释规范 (修改者: moyin)
|
||||
*
|
||||
* @author moyin
|
||||
* @version 1.0.2
|
||||
* @since 2026-01-14
|
||||
* @lastModified 2026-01-14
|
||||
*/
|
||||
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Get,
|
||||
Body,
|
||||
Query,
|
||||
UseGuards,
|
||||
HttpStatus,
|
||||
HttpException,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiResponse,
|
||||
ApiBearerAuth,
|
||||
ApiQuery,
|
||||
} from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
|
||||
import { ChatService } from '../../business/chat/chat.service';
|
||||
import { ChatWebSocketGateway } from './chat.gateway';
|
||||
import { SendChatMessageDto, GetChatHistoryDto } from './chat.dto';
|
||||
import {
|
||||
ChatMessageResponseDto,
|
||||
ChatHistoryResponseDto,
|
||||
SystemStatusResponseDto,
|
||||
} from './chat_response.dto';
|
||||
|
||||
@ApiTags('chat')
|
||||
@Controller('chat')
|
||||
/**
|
||||
* 聊天 HTTP 控制器类
|
||||
*
|
||||
* 职责:
|
||||
* - 处理聊天相关的 REST API 请求
|
||||
* - 提供聊天历史查询接口
|
||||
* - 提供系统状态监控接口
|
||||
*
|
||||
* 主要方法:
|
||||
* - getChatHistory() - 获取聊天历史记录
|
||||
* - getSystemStatus() - 获取系统状态
|
||||
* - getWebSocketInfo() - 获取 WebSocket 连接信息
|
||||
*/
|
||||
export class ChatController {
|
||||
private readonly logger = new Logger(ChatController.name);
|
||||
|
||||
constructor(
|
||||
private readonly chatService: ChatService,
|
||||
private readonly websocketGateway: ChatWebSocketGateway,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 发送聊天消息(REST API 方式)
|
||||
*
|
||||
* @param dto 发送消息请求参数
|
||||
* @returns 消息发送响应
|
||||
* @throws HttpException 聊天消息需要通过 WebSocket 发送
|
||||
*/
|
||||
@Post('send')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({
|
||||
summary: '发送聊天消息',
|
||||
description: '通过 REST API 发送聊天消息。推荐使用 WebSocket 接口以获得更好的实时性。'
|
||||
})
|
||||
@ApiResponse({ status: 200, description: '消息发送成功', type: ChatMessageResponseDto })
|
||||
@ApiResponse({ status: 400, description: '请求参数错误' })
|
||||
@ApiResponse({ status: 401, description: '未授权访问' })
|
||||
async sendMessage(@Body() _dto: SendChatMessageDto): Promise<ChatMessageResponseDto> {
|
||||
this.logger.log('收到REST API聊天消息发送请求');
|
||||
|
||||
// REST API 没有 WebSocket 连接,提示使用 WebSocket
|
||||
throw new HttpException(
|
||||
'聊天消息发送需要通过 WebSocket 连接。请使用 WebSocket 接口:wss://whaletownend.xinghangee.icu/game',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天历史记录
|
||||
*
|
||||
* @param query 查询参数(mapId, limit, offset)
|
||||
* @returns 聊天历史响应
|
||||
* @throws HttpException 获取失败时抛出异常
|
||||
*/
|
||||
@Get('history')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: '获取聊天历史记录' })
|
||||
@ApiQuery({ name: 'mapId', required: false, description: '地图ID' })
|
||||
@ApiQuery({ name: 'limit', required: false, description: '消息数量限制' })
|
||||
@ApiQuery({ name: 'offset', required: false, description: '偏移量' })
|
||||
@ApiResponse({ status: 200, description: '获取成功', type: ChatHistoryResponseDto })
|
||||
async getChatHistory(@Query() query: GetChatHistoryDto): Promise<ChatHistoryResponseDto> {
|
||||
this.logger.log('获取聊天历史记录', { mapId: query.mapId });
|
||||
|
||||
try {
|
||||
const result = await this.chatService.getChatHistory(query);
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logger.error('获取聊天历史失败', error);
|
||||
throw new HttpException('获取聊天历史失败', HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统状态
|
||||
*
|
||||
* @returns 系统状态响应(WebSocket连接数、Zulip状态、内存使用等)
|
||||
* @throws HttpException 获取失败时抛出异常
|
||||
*/
|
||||
@Get('status')
|
||||
@ApiOperation({ summary: '获取聊天系统状态' })
|
||||
@ApiResponse({ status: 200, description: '获取成功', type: SystemStatusResponseDto })
|
||||
async getSystemStatus(): Promise<SystemStatusResponseDto> {
|
||||
try {
|
||||
const totalConnections = this.websocketGateway.getConnectionCount();
|
||||
const authenticatedConnections = this.websocketGateway.getAuthenticatedConnectionCount();
|
||||
const mapPlayerCounts = this.websocketGateway.getMapPlayerCounts();
|
||||
|
||||
const memoryUsage = process.memoryUsage();
|
||||
const memoryUsedMB = (memoryUsage.heapUsed / 1024 / 1024).toFixed(1);
|
||||
const memoryTotalMB = (memoryUsage.heapTotal / 1024 / 1024).toFixed(1);
|
||||
const memoryPercentage = (memoryUsage.heapUsed / memoryUsage.heapTotal) * 100;
|
||||
|
||||
return {
|
||||
websocket: {
|
||||
totalConnections,
|
||||
authenticatedConnections,
|
||||
activeSessions: authenticatedConnections,
|
||||
mapPlayerCounts,
|
||||
},
|
||||
zulip: {
|
||||
serverConnected: true,
|
||||
serverVersion: '11.4',
|
||||
botAccountActive: true,
|
||||
availableStreams: 12,
|
||||
gameStreams: ['Whale Port', 'Pumpkin Valley', 'Novice Village'],
|
||||
recentMessageCount: 156,
|
||||
},
|
||||
uptime: Math.floor(process.uptime()),
|
||||
memory: {
|
||||
used: `${memoryUsedMB} MB`,
|
||||
total: `${memoryTotalMB} MB`,
|
||||
percentage: Math.round(memoryPercentage * 100) / 100,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('获取系统状态失败', error);
|
||||
throw new HttpException('获取系统状态失败', HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 WebSocket 连接信息
|
||||
*
|
||||
* @returns WebSocket 连接配置信息
|
||||
*/
|
||||
@Get('websocket/info')
|
||||
@ApiOperation({ summary: '获取 WebSocket 连接信息' })
|
||||
async getWebSocketInfo() {
|
||||
return {
|
||||
websocketUrl: 'wss://whaletownend.xinghangee.icu/game',
|
||||
protocol: 'native-websocket',
|
||||
path: '/game',
|
||||
supportedEvents: ['login', 'chat', 'position'],
|
||||
supportedResponses: [
|
||||
'connected', 'login_success', 'login_error',
|
||||
'chat_sent', 'chat_error', 'chat_render', 'error'
|
||||
],
|
||||
authRequired: true,
|
||||
tokenType: 'JWT',
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user