Files
whale-town-end/src/gateway/zulip/zulip_accounts.controller.ts
moyin ed04b8c92d docs(zulip): 完善Zulip业务模块功能文档
范围: src/business/zulip/README.md
- 补充对外提供的接口章节(14个公共方法)
- 添加使用的项目内部依赖说明(7个依赖)
- 完善核心特性描述(5个特性)
- 添加潜在风险评估(4个风险及缓解措施)
- 优化文档结构和内容完整性
2026-01-15 10:53:04 +08:00

683 lines
18 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账号关联管理的REST API接口
* - 支持CRUD操作和批量管理
* - 提供账号验证和统计功能
* - 集成性能监控和结构化日志记录
* - 实现统一的错误处理和响应格式
*
* 架构定位:
* - 层级Gateway层网关层
* - 职责HTTP协议处理、API接口暴露
* - 依赖调用Business层的ZulipAccountsBusinessService
*
* 职责分离:
* - API接口提供RESTful风格的HTTP接口
* - 参数验证使用DTO进行请求参数验证
* - 业务调用调用Service层处理业务逻辑
* - 响应格式统一API响应格式和错误处理
* - 性能监控:记录接口调用耗时和性能指标
* - 日志记录使用AppLoggerService记录结构化日志
*
* 最近修改:
* - 2026-01-14: 架构优化 - 从Business层迁移到Gateway层符合四层架构规范 (修改者: moyin)
* - 2026-01-14: 代码质量优化 - 移除未使用的requestLogger属性 (修改者: moyin)
* - 2026-01-14: 代码质量优化 - 移除未使用的导入 (修改者: moyin)
* - 2026-01-12: 性能优化 - 集成AppLoggerService和性能监控优化错误处理
* - 2025-01-07: 初始创建 - 实现基础的CRUD和管理接口
*
* @author angjustinl
* @version 2.0.0
* @since 2025-01-07
* @lastModified 2026-01-14
*/
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 { 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 {
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<string, any>) {
const startTime = Date.now();
const requestLogger = this.logger.bindRequest(req, 'ZulipAccountsController');
requestLogger.info(`开始${operation}`, context);
return {
success: (additionalContext?: Record<string, any>) => {
const duration = Date.now() - startTime;
requestLogger.info(`${operation}成功`, {
...context,
...additionalContext,
duration
});
},
error: (error: unknown, additionalContext?: Record<string, any>) => {
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<ZulipAccountResponseDto> {
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<ZulipAccountListResponseDto> {
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<ZulipAccountResponseDto> {
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<ZulipAccountResponseDto | null> {
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<ZulipAccountResponseDto | null> {
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<ZulipAccountResponseDto | null> {
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<ZulipAccountResponseDto> {
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<ZulipAccountResponseDto> {
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<ZulipAccountListResponseDto> {
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<ZulipAccountListResponseDto> {
return this.zulipAccountsService.findErrorAccounts(maxRetryCount);
}
/**
* 批量更新账号状态
*/
@Put('management/batch-status')
@ApiOperation({
summary: '批量更新账号状态',
description: '批量更新多个账号的状态'
})
@ApiResponse({
status: 200,
description: '更新成功',
type: BatchUpdateResponseDto,
})
async batchUpdateStatus(@Body() batchDto: BatchUpdateStatusDto): Promise<BatchUpdateResponseDto> {
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<ZulipAccountStatsResponseDto> {
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<VerifyAccountResponseDto> {
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 };
}
}