/** * Zulip账号关联管理控制器 * * 功能描述: * - 提供Zulip账号关联管理的REST API接口 * - 支持CRUD操作和批量管理 * - 提供账号验证和统计功能 * - 集成性能监控和结构化日志记录 * - 实现统一的错误处理和响应格式 * * 职责分离: * - API接口:提供RESTful风格的HTTP接口 * - 参数验证:使用DTO进行请求参数验证 * - 业务调用:调用Service层处理业务逻辑 * - 响应格式:统一API响应格式和错误处理 * - 性能监控:记录接口调用耗时和性能指标 * - 日志记录:使用AppLoggerService记录结构化日志 * * 最近修改: * - 2026-01-12: 性能优化 - 集成AppLoggerService和性能监控,优化错误处理 * - 2025-01-07: 初始创建 - 实现基础的CRUD和管理接口 * * @author angjustinl * @version 1.1.0 * @since 2025-01-07 * @lastModified 2026-01-12 */ import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards, HttpStatus, HttpCode, Inject, Req, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiParam, ApiQuery, } from '@nestjs/swagger'; import { Request } from 'express'; import { JwtAuthGuard } from '../auth/jwt_auth.guard'; import { ZulipAccountsService } from '../../core/db/zulip_accounts/zulip_accounts.service'; import { ZulipAccountsMemoryService } from '../../core/db/zulip_accounts/zulip_accounts_memory.service'; import { AppLoggerService } from '../../core/utils/logger/logger.service'; import { CreateZulipAccountDto, UpdateZulipAccountDto, QueryZulipAccountDto, ZulipAccountResponseDto, ZulipAccountListResponseDto, ZulipAccountStatsResponseDto, BatchUpdateStatusDto, BatchUpdateResponseDto, VerifyAccountDto, VerifyAccountResponseDto, } from '../../core/db/zulip_accounts/zulip_accounts.dto'; @ApiTags('zulip-accounts') @Controller('zulip-accounts') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') export class ZulipAccountsController { private readonly requestLogger: any; constructor( @Inject('ZulipAccountsService') private readonly zulipAccountsService: any, @Inject(AppLoggerService) private readonly logger: AppLoggerService, ) { this.logger.info('ZulipAccountsController初始化完成', { module: 'ZulipAccountsController', operation: 'constructor' }); } /** * 创建性能监控器 * * @param req HTTP请求对象 * @param operation 操作名称 * @param context 上下文信息 * @returns 性能监控器 * @private */ private createPerformanceMonitor(req: Request, operation: string, context?: Record) { const startTime = Date.now(); const requestLogger = this.logger.bindRequest(req, 'ZulipAccountsController'); requestLogger.info(`开始${operation}`, context); return { success: (additionalContext?: Record) => { const duration = Date.now() - startTime; requestLogger.info(`${operation}成功`, { ...context, ...additionalContext, duration }); }, error: (error: unknown, additionalContext?: Record) => { const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); requestLogger.error( `${operation}失败`, error instanceof Error ? error.stack : undefined, { ...context, ...additionalContext, error: errorMessage, duration } ); } }; } /** * 创建Zulip账号关联 */ @Post() @ApiOperation({ summary: '创建Zulip账号关联', description: '为游戏用户创建与Zulip账号的关联关系' }) @ApiResponse({ status: 201, description: '创建成功', type: ZulipAccountResponseDto, }) @ApiResponse({ status: 400, description: '请求参数错误', }) @ApiResponse({ status: 409, description: '关联已存在', }) @HttpCode(HttpStatus.CREATED) async create( @Req() req: Request, @Body() createDto: CreateZulipAccountDto ): Promise { const monitor = this.createPerformanceMonitor(req, '创建Zulip账号关联', { gameUserId: createDto.gameUserId, zulipUserId: createDto.zulipUserId, zulipEmail: createDto.zulipEmail }); try { const result = await this.zulipAccountsService.create(createDto); monitor.success({ accountId: result.id, status: result.status }); return result; } catch (error) { monitor.error(error); throw error; } } /** * 获取所有Zulip账号关联 */ @Get() @ApiOperation({ summary: '查询Zulip账号关联列表', description: '根据条件查询Zulip账号关联列表' }) @ApiQuery({ name: 'gameUserId', required: false, description: '游戏用户ID', example: '12345' }) @ApiQuery({ name: 'zulipUserId', required: false, description: 'Zulip用户ID', example: 67890 }) @ApiQuery({ name: 'zulipEmail', required: false, description: 'Zulip邮箱地址', example: 'user@example.com' }) @ApiQuery({ name: 'status', required: false, description: '账号状态', enum: ['active', 'inactive', 'suspended', 'error'] }) @ApiQuery({ name: 'includeGameUser', required: false, description: '是否包含游戏用户信息', type: Boolean, example: false }) @ApiResponse({ status: 200, description: '查询成功', type: ZulipAccountListResponseDto, }) async findMany(@Query() queryDto: QueryZulipAccountDto): Promise { return this.zulipAccountsService.findMany(queryDto); } /** * 根据ID获取Zulip账号关联 */ @Get(':id') @ApiOperation({ summary: '根据ID获取Zulip账号关联', description: '根据关联记录ID获取详细信息' }) @ApiParam({ name: 'id', description: '关联记录ID', example: '1' }) @ApiQuery({ name: 'includeGameUser', required: false, description: '是否包含游戏用户信息', type: Boolean, example: false }) @ApiResponse({ status: 200, description: '获取成功', type: ZulipAccountResponseDto, }) @ApiResponse({ status: 404, description: '记录不存在', }) async findById( @Param('id') id: string, @Query('includeGameUser') includeGameUser?: boolean, ): Promise { return this.zulipAccountsService.findById(id, includeGameUser); } /** * 根据游戏用户ID获取Zulip账号关联 */ @Get('game-user/:gameUserId') @ApiOperation({ summary: '根据游戏用户ID获取Zulip账号关联', description: '根据游戏用户ID获取关联的Zulip账号信息' }) @ApiParam({ name: 'gameUserId', description: '游戏用户ID', example: '12345' }) @ApiQuery({ name: 'includeGameUser', required: false, description: '是否包含游戏用户信息', type: Boolean, example: false }) @ApiResponse({ status: 200, description: '获取成功', type: ZulipAccountResponseDto, }) @ApiResponse({ status: 404, description: '关联不存在', }) async findByGameUserId( @Param('gameUserId') gameUserId: string, @Query('includeGameUser') includeGameUser?: boolean, ): Promise { return this.zulipAccountsService.findByGameUserId(gameUserId, includeGameUser); } /** * 根据Zulip用户ID获取账号关联 */ @Get('zulip-user/:zulipUserId') @ApiOperation({ summary: '根据Zulip用户ID获取账号关联', description: '根据Zulip用户ID获取关联的游戏账号信息' }) @ApiParam({ name: 'zulipUserId', description: 'Zulip用户ID', example: '67890' }) @ApiQuery({ name: 'includeGameUser', required: false, description: '是否包含游戏用户信息', type: Boolean, example: false }) @ApiResponse({ status: 200, description: '获取成功', type: ZulipAccountResponseDto, }) @ApiResponse({ status: 404, description: '关联不存在', }) async findByZulipUserId( @Param('zulipUserId') zulipUserId: string, @Query('includeGameUser') includeGameUser?: boolean, ): Promise { return this.zulipAccountsService.findByZulipUserId(parseInt(zulipUserId), includeGameUser); } /** * 根据Zulip邮箱获取账号关联 */ @Get('zulip-email/:zulipEmail') @ApiOperation({ summary: '根据Zulip邮箱获取账号关联', description: '根据Zulip邮箱地址获取关联的游戏账号信息' }) @ApiParam({ name: 'zulipEmail', description: 'Zulip邮箱地址', example: 'user@example.com' }) @ApiQuery({ name: 'includeGameUser', required: false, description: '是否包含游戏用户信息', type: Boolean, example: false }) @ApiResponse({ status: 200, description: '获取成功', type: ZulipAccountResponseDto, }) @ApiResponse({ status: 404, description: '关联不存在', }) async findByZulipEmail( @Param('zulipEmail') zulipEmail: string, @Query('includeGameUser') includeGameUser?: boolean, ): Promise { return this.zulipAccountsService.findByZulipEmail(zulipEmail, includeGameUser); } /** * 更新Zulip账号关联 */ @Put(':id') @ApiOperation({ summary: '更新Zulip账号关联', description: '根据ID更新Zulip账号关联信息' }) @ApiParam({ name: 'id', description: '关联记录ID', example: '1' }) @ApiResponse({ status: 200, description: '更新成功', type: ZulipAccountResponseDto, }) @ApiResponse({ status: 404, description: '记录不存在', }) async update( @Param('id') id: string, @Body() updateDto: UpdateZulipAccountDto, ): Promise { return this.zulipAccountsService.update(id, updateDto); } /** * 根据游戏用户ID更新关联 */ @Put('game-user/:gameUserId') @ApiOperation({ summary: '根据游戏用户ID更新关联', description: '根据游戏用户ID更新Zulip账号关联信息' }) @ApiParam({ name: 'gameUserId', description: '游戏用户ID', example: '12345' }) @ApiResponse({ status: 200, description: '更新成功', type: ZulipAccountResponseDto, }) @ApiResponse({ status: 404, description: '关联不存在', }) async updateByGameUserId( @Param('gameUserId') gameUserId: string, @Body() updateDto: UpdateZulipAccountDto, ): Promise { return this.zulipAccountsService.updateByGameUserId(gameUserId, updateDto); } /** * 删除Zulip账号关联 */ @Delete(':id') @ApiOperation({ summary: '删除Zulip账号关联', description: '根据ID删除Zulip账号关联记录' }) @ApiParam({ name: 'id', description: '关联记录ID', example: '1' }) @ApiResponse({ status: 200, description: '删除成功', schema: { type: 'object', properties: { success: { type: 'boolean', example: true }, message: { type: 'string', example: '删除成功' } } } }) @ApiResponse({ status: 404, description: '记录不存在', }) async delete(@Param('id') id: string): Promise<{ success: boolean; message: string }> { await this.zulipAccountsService.delete(id); return { success: true, message: '删除成功' }; } /** * 根据游戏用户ID删除关联 */ @Delete('game-user/:gameUserId') @ApiOperation({ summary: '根据游戏用户ID删除关联', description: '根据游戏用户ID删除Zulip账号关联记录' }) @ApiParam({ name: 'gameUserId', description: '游戏用户ID', example: '12345' }) @ApiResponse({ status: 200, description: '删除成功', schema: { type: 'object', properties: { success: { type: 'boolean', example: true }, message: { type: 'string', example: '删除成功' } } } }) @ApiResponse({ status: 404, description: '关联不存在', }) async deleteByGameUserId(@Param('gameUserId') gameUserId: string): Promise<{ success: boolean; message: string }> { await this.zulipAccountsService.deleteByGameUserId(gameUserId); return { success: true, message: '删除成功' }; } /** * 获取需要验证的账号列表 */ @Get('management/verification-needed') @ApiOperation({ summary: '获取需要验证的账号列表', description: '获取超过指定时间未验证的账号列表' }) @ApiQuery({ name: 'maxAge', required: false, description: '最大验证间隔(毫秒),默认24小时', example: 86400000 }) @ApiResponse({ status: 200, description: '获取成功', type: ZulipAccountListResponseDto, }) async findAccountsNeedingVerification( @Query('maxAge') maxAge?: number, ): Promise { return this.zulipAccountsService.findAccountsNeedingVerification(maxAge); } /** * 获取错误状态的账号列表 */ @Get('management/error-accounts') @ApiOperation({ summary: '获取错误状态的账号列表', description: '获取处于错误状态的账号列表' }) @ApiQuery({ name: 'maxRetryCount', required: false, description: '最大重试次数,默认3次', example: 3 }) @ApiResponse({ status: 200, description: '获取成功', type: ZulipAccountListResponseDto, }) async findErrorAccounts( @Query('maxRetryCount') maxRetryCount?: number, ): Promise { return this.zulipAccountsService.findErrorAccounts(maxRetryCount); } /** * 批量更新账号状态 */ @Put('management/batch-status') @ApiOperation({ summary: '批量更新账号状态', description: '批量更新多个账号的状态' }) @ApiResponse({ status: 200, description: '更新成功', type: BatchUpdateResponseDto, }) async batchUpdateStatus(@Body() batchDto: BatchUpdateStatusDto): Promise { return this.zulipAccountsService.batchUpdateStatus(batchDto.ids, batchDto.status); } /** * 获取账号状态统计 */ @Get('management/statistics') @ApiOperation({ summary: '获取账号状态统计', description: '获取各种状态的账号数量统计' }) @ApiResponse({ status: 200, description: '获取成功', type: ZulipAccountStatsResponseDto, }) async getStatusStatistics(@Req() req: Request): Promise { const monitor = this.createPerformanceMonitor(req, '获取账号状态统计'); try { const result = await this.zulipAccountsService.getStatusStatistics(); monitor.success({ total: result.total, active: result.active, error: result.error }); return result; } catch (error) { monitor.error(error); throw error; } } /** * 验证账号有效性 */ @Post('management/verify') @ApiOperation({ summary: '验证账号有效性', description: '验证指定游戏用户的Zulip账号关联是否有效' }) @ApiResponse({ status: 200, description: '验证完成', type: VerifyAccountResponseDto, }) async verifyAccount(@Body() verifyDto: VerifyAccountDto): Promise { return this.zulipAccountsService.verifyAccount(verifyDto.gameUserId); } /** * 检查邮箱是否已存在 */ @Get('validation/email-exists/:email') @ApiOperation({ summary: '检查邮箱是否已存在', description: '检查指定的Zulip邮箱是否已被其他账号使用' }) @ApiParam({ name: 'email', description: 'Zulip邮箱地址', example: 'user@example.com' }) @ApiQuery({ name: 'excludeId', required: false, description: '排除的记录ID(用于更新时检查)', example: '1' }) @ApiResponse({ status: 200, description: '检查完成', schema: { type: 'object', properties: { exists: { type: 'boolean', example: false }, email: { type: 'string', example: 'user@example.com' } } } }) async checkEmailExists( @Param('email') email: string, @Query('excludeId') excludeId?: string, ): Promise<{ exists: boolean; email: string }> { const exists = await this.zulipAccountsService.existsByEmail(email, excludeId); return { exists, email }; } /** * 检查Zulip用户ID是否已存在 */ @Get('validation/zulip-user-exists/:zulipUserId') @ApiOperation({ summary: '检查Zulip用户ID是否已存在', description: '检查指定的Zulip用户ID是否已被其他账号使用' }) @ApiParam({ name: 'zulipUserId', description: 'Zulip用户ID', example: '67890' }) @ApiQuery({ name: 'excludeId', required: false, description: '排除的记录ID(用于更新时检查)', example: '1' }) @ApiResponse({ status: 200, description: '检查完成', schema: { type: 'object', properties: { exists: { type: 'boolean', example: false }, zulipUserId: { type: 'number', example: 67890 } } } }) async checkZulipUserIdExists( @Param('zulipUserId') zulipUserId: string, @Query('excludeId') excludeId?: string, ): Promise<{ exists: boolean; zulipUserId: number }> { const zulipUserIdNum = parseInt(zulipUserId); const exists = await this.zulipAccountsService.existsByZulipUserId(zulipUserIdNum, excludeId); return { exists, zulipUserId: zulipUserIdNum }; } }