/** * 管理员数据库管理控制器 * * 功能描述: * - 提供管理员专用的数据库管理HTTP接口 * - 集成用户、用户档案、Zulip账号关联的CRUD操作 * - 实现统一的权限控制和参数验证 * - 支持分页查询和搜索功能 * * 职责分离: * - HTTP请求处理:接收和验证HTTP请求参数 * - 权限控制:通过AdminGuard确保只有管理员可以访问 * - 业务委托:将业务逻辑委托给DatabaseManagementService处理 * - 响应格式化:返回统一格式的HTTP响应 * * API端点分组: * - /admin/database/users/* 用户管理相关接口 * - /admin/database/user-profiles/* 用户档案管理相关接口 * - /admin/database/zulip-accounts/* Zulip账号关联管理相关接口 * * 最近修改: * - 2026-01-08: 注释规范优化 - 修正@author字段,更新版本号和修改记录 (修改者: moyin) * - 2026-01-08: 代码质量优化 - 清理未使用的导入 (修改者: moyin) * - 2026-01-08: 文件夹扁平化 - 从controllers/子文件夹移动到上级目录 (修改者: moyin) * - 2026-01-08: 功能新增 - 创建管理员数据库管理控制器 (修改者: assistant) * * @author moyin * @version 1.1.0 * @since 2026-01-08 * @lastModified 2026-01-08 */ import { Controller, Get, Post, Put, Delete, Param, Query, Body, UseGuards, UseFilters, UseInterceptors, ParseIntPipe, DefaultValuePipe } from '@nestjs/common'; import { ApiTags, ApiBearerAuth, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiBody } from '@nestjs/swagger'; import { AdminGuard } from './admin.guard'; import { AdminDatabaseExceptionFilter } from './admin_database_exception.filter'; import { AdminOperationLogInterceptor } from './admin_operation_log.interceptor'; import { LogAdminOperation } from './log_admin_operation.decorator'; import { DatabaseManagementService, AdminApiResponse, AdminListResponse } from './database_management.service'; import { AdminCreateUserDto, AdminUpdateUserDto, AdminBatchUpdateStatusDto, AdminDatabaseResponseDto, AdminHealthCheckResponseDto, AdminCreateUserProfileDto, AdminUpdateUserProfileDto, AdminCreateZulipAccountDto, AdminUpdateZulipAccountDto } from './admin_database.dto'; import { PAGINATION_LIMITS, REQUEST_ID_PREFIXES } from './admin_constants'; import { safeLimitValue, createSuccessResponse, getCurrentTimestamp } from './admin_utils'; @ApiTags('admin-database') @Controller('admin/database') @UseGuards(AdminGuard) @UseFilters(AdminDatabaseExceptionFilter) @UseInterceptors(AdminOperationLogInterceptor) @ApiBearerAuth('JWT-auth') export class AdminDatabaseController { constructor( private readonly databaseManagementService: DatabaseManagementService ) {} // ==================== 用户管理接口 ==================== @ApiOperation({ summary: '获取用户列表', description: '分页获取用户列表,支持管理员查看所有用户信息' }) @ApiQuery({ name: 'limit', required: false, description: '返回数量(默认20,最大100)', example: 20 }) @ApiQuery({ name: 'offset', required: false, description: '偏移量(默认0)', example: 0 }) @ApiResponse({ status: 200, description: '获取成功' }) @ApiResponse({ status: 401, description: '未授权访问' }) @ApiResponse({ status: 403, description: '权限不足' }) @LogAdminOperation({ operationType: 'QUERY', targetType: 'users', description: '获取用户列表', isSensitive: false }) @Get('users') async getUserList( @Query('limit', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_LIMIT), ParseIntPipe) limit: number, @Query('offset', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_OFFSET), ParseIntPipe) offset: number ): Promise { const safeLimit = safeLimitValue(limit, PAGINATION_LIMITS.USER_LIST_MAX_LIMIT); return await this.databaseManagementService.getUserList(safeLimit, offset); } @ApiOperation({ summary: '获取用户详情', description: '根据用户ID获取详细的用户信息' }) @ApiParam({ name: 'id', description: '用户ID', example: '1' }) @ApiResponse({ status: 200, description: '获取成功' }) @ApiResponse({ status: 404, description: '用户不存在' }) @Get('users/:id') async getUserById(@Param('id') id: string): Promise { return await this.databaseManagementService.getUserById(BigInt(id)); } @ApiOperation({ summary: '搜索用户', description: '根据关键词搜索用户,支持用户名、邮箱、昵称模糊匹配' }) @ApiQuery({ name: 'keyword', description: '搜索关键词', example: 'admin' }) @ApiQuery({ name: 'limit', required: false, description: '返回数量(默认20,最大50)', example: 20 }) @ApiResponse({ status: 200, description: '搜索成功' }) @Get('users/search') async searchUsers( @Query('keyword') keyword: string, @Query('limit', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_LIMIT), ParseIntPipe) limit: number ): Promise { const safeLimit = safeLimitValue(limit, PAGINATION_LIMITS.SEARCH_MAX_LIMIT); return await this.databaseManagementService.searchUsers(keyword, safeLimit); } @ApiOperation({ summary: '创建用户', description: '创建新用户,需要提供用户名和昵称等基本信息' }) @ApiBody({ type: AdminCreateUserDto, description: '用户创建数据' }) @ApiResponse({ status: 201, description: '创建成功', type: AdminDatabaseResponseDto }) @ApiResponse({ status: 400, description: '请求参数错误' }) @ApiResponse({ status: 409, description: '用户名或邮箱已存在' }) @LogAdminOperation({ operationType: 'CREATE', targetType: 'users', description: '创建用户', isSensitive: true }) @Post('users') async createUser(@Body() createUserDto: AdminCreateUserDto): Promise { return await this.databaseManagementService.createUser(createUserDto); } @ApiOperation({ summary: '更新用户', description: '根据用户ID更新用户信息' }) @ApiParam({ name: 'id', description: '用户ID', example: '1' }) @ApiBody({ type: AdminUpdateUserDto, description: '用户更新数据' }) @ApiResponse({ status: 200, description: '更新成功', type: AdminDatabaseResponseDto }) @ApiResponse({ status: 404, description: '用户不存在' }) @Put('users/:id') async updateUser( @Param('id') id: string, @Body() updateUserDto: AdminUpdateUserDto ): Promise { return await this.databaseManagementService.updateUser(BigInt(id), updateUserDto); } @ApiOperation({ summary: '删除用户', description: '根据用户ID删除用户(软删除)' }) @ApiParam({ name: 'id', description: '用户ID', example: '1' }) @ApiResponse({ status: 200, description: '删除成功' }) @ApiResponse({ status: 404, description: '用户不存在' }) @LogAdminOperation({ operationType: 'DELETE', targetType: 'users', description: '删除用户', isSensitive: true }) @Delete('users/:id') async deleteUser(@Param('id') id: string): Promise { return await this.databaseManagementService.deleteUser(BigInt(id)); } // ==================== 用户档案管理接口 ==================== @ApiOperation({ summary: '获取用户档案列表', description: '分页获取用户档案列表,包含位置信息和档案数据' }) @ApiQuery({ name: 'limit', required: false, description: '返回数量(默认20,最大100)', example: 20 }) @ApiQuery({ name: 'offset', required: false, description: '偏移量(默认0)', example: 0 }) @ApiResponse({ status: 200, description: '获取成功' }) @Get('user-profiles') async getUserProfileList( @Query('limit', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_LIMIT), ParseIntPipe) limit: number, @Query('offset', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_OFFSET), ParseIntPipe) offset: number ): Promise { const safeLimit = safeLimitValue(limit, PAGINATION_LIMITS.USER_LIST_MAX_LIMIT); return await this.databaseManagementService.getUserProfileList(safeLimit, offset); } @ApiOperation({ summary: '获取用户档案详情', description: '根据档案ID获取详细的用户档案信息' }) @ApiParam({ name: 'id', description: '档案ID', example: '1' }) @ApiResponse({ status: 200, description: '获取成功' }) @ApiResponse({ status: 404, description: '档案不存在' }) @Get('user-profiles/:id') async getUserProfileById(@Param('id') id: string): Promise { return await this.databaseManagementService.getUserProfileById(BigInt(id)); } @ApiOperation({ summary: '根据地图获取用户档案', description: '获取指定地图中的所有用户档案信息' }) @ApiParam({ name: 'mapId', description: '地图ID', example: 'plaza' }) @ApiQuery({ name: 'limit', required: false, description: '返回数量(默认20,最大100)', example: 20 }) @ApiQuery({ name: 'offset', required: false, description: '偏移量(默认0)', example: 0 }) @ApiResponse({ status: 200, description: '获取成功' }) @Get('user-profiles/by-map/:mapId') async getUserProfilesByMap( @Param('mapId') mapId: string, @Query('limit', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_LIMIT), ParseIntPipe) limit: number, @Query('offset', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_OFFSET), ParseIntPipe) offset: number ): Promise { const safeLimit = safeLimitValue(limit, PAGINATION_LIMITS.USER_LIST_MAX_LIMIT); return await this.databaseManagementService.getUserProfilesByMap(mapId, safeLimit, offset); } @ApiOperation({ summary: '创建用户档案', description: '为指定用户创建档案信息' }) @ApiBody({ type: AdminCreateUserProfileDto, description: '用户档案创建数据' }) @ApiResponse({ status: 201, description: '创建成功' }) @ApiResponse({ status: 400, description: '请求参数错误' }) @ApiResponse({ status: 409, description: '用户档案已存在' }) @Post('user-profiles') async createUserProfile(@Body() createProfileDto: AdminCreateUserProfileDto): Promise { return await this.databaseManagementService.createUserProfile(createProfileDto); } @ApiOperation({ summary: '更新用户档案', description: '根据档案ID更新用户档案信息' }) @ApiParam({ name: 'id', description: '档案ID', example: '1' }) @ApiBody({ type: AdminUpdateUserProfileDto, description: '用户档案更新数据' }) @ApiResponse({ status: 200, description: '更新成功' }) @ApiResponse({ status: 404, description: '档案不存在' }) @Put('user-profiles/:id') async updateUserProfile( @Param('id') id: string, @Body() updateProfileDto: AdminUpdateUserProfileDto ): Promise { return await this.databaseManagementService.updateUserProfile(BigInt(id), updateProfileDto); } @ApiOperation({ summary: '删除用户档案', description: '根据档案ID删除用户档案' }) @ApiParam({ name: 'id', description: '档案ID', example: '1' }) @ApiResponse({ status: 200, description: '删除成功' }) @ApiResponse({ status: 404, description: '档案不存在' }) @Delete('user-profiles/:id') async deleteUserProfile(@Param('id') id: string): Promise { return await this.databaseManagementService.deleteUserProfile(BigInt(id)); } // ==================== Zulip账号关联管理接口 ==================== @ApiOperation({ summary: '获取Zulip账号关联列表', description: '分页获取Zulip账号关联列表,包含关联状态和错误信息' }) @ApiQuery({ name: 'limit', required: false, description: '返回数量(默认20,最大100)', example: 20 }) @ApiQuery({ name: 'offset', required: false, description: '偏移量(默认0)', example: 0 }) @ApiResponse({ status: 200, description: '获取成功' }) @Get('zulip-accounts') async getZulipAccountList( @Query('limit', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_LIMIT), ParseIntPipe) limit: number, @Query('offset', new DefaultValuePipe(PAGINATION_LIMITS.DEFAULT_OFFSET), ParseIntPipe) offset: number ): Promise { const safeLimit = safeLimitValue(limit, PAGINATION_LIMITS.USER_LIST_MAX_LIMIT); return await this.databaseManagementService.getZulipAccountList(safeLimit, offset); } @ApiOperation({ summary: '获取Zulip账号关联详情', description: '根据关联ID获取详细的Zulip账号关联信息' }) @ApiParam({ name: 'id', description: '关联ID', example: '1' }) @ApiResponse({ status: 200, description: '获取成功' }) @ApiResponse({ status: 404, description: '关联不存在' }) @Get('zulip-accounts/:id') async getZulipAccountById(@Param('id') id: string): Promise { return await this.databaseManagementService.getZulipAccountById(id); } @ApiOperation({ summary: '获取Zulip账号关联统计', description: '获取各种状态的Zulip账号关联数量统计信息' }) @ApiResponse({ status: 200, description: '获取成功' }) @Get('zulip-accounts/statistics') async getZulipAccountStatistics(): Promise { return await this.databaseManagementService.getZulipAccountStatistics(); } @ApiOperation({ summary: '创建Zulip账号关联', description: '创建游戏用户与Zulip账号的关联' }) @ApiBody({ type: AdminCreateZulipAccountDto, description: 'Zulip账号关联创建数据' }) @ApiResponse({ status: 201, description: '创建成功' }) @ApiResponse({ status: 400, description: '请求参数错误' }) @ApiResponse({ status: 409, description: '关联已存在' }) @Post('zulip-accounts') async createZulipAccount(@Body() createAccountDto: AdminCreateZulipAccountDto): Promise { return await this.databaseManagementService.createZulipAccount(createAccountDto); } @ApiOperation({ summary: '更新Zulip账号关联', description: '根据关联ID更新Zulip账号关联信息' }) @ApiParam({ name: 'id', description: '关联ID', example: '1' }) @ApiBody({ type: AdminUpdateZulipAccountDto, description: 'Zulip账号关联更新数据' }) @ApiResponse({ status: 200, description: '更新成功' }) @ApiResponse({ status: 404, description: '关联不存在' }) @Put('zulip-accounts/:id') async updateZulipAccount( @Param('id') id: string, @Body() updateAccountDto: AdminUpdateZulipAccountDto ): Promise { return await this.databaseManagementService.updateZulipAccount(id, updateAccountDto); } @ApiOperation({ summary: '删除Zulip账号关联', description: '根据关联ID删除Zulip账号关联' }) @ApiParam({ name: 'id', description: '关联ID', example: '1' }) @ApiResponse({ status: 200, description: '删除成功' }) @ApiResponse({ status: 404, description: '关联不存在' }) @Delete('zulip-accounts/:id') async deleteZulipAccount(@Param('id') id: string): Promise { return await this.databaseManagementService.deleteZulipAccount(id); } @ApiOperation({ summary: '批量更新Zulip账号状态', description: '批量更新多个Zulip账号关联的状态' }) @ApiBody({ type: AdminBatchUpdateStatusDto, description: '批量更新数据' }) @ApiResponse({ status: 200, description: '批量更新完成', type: AdminDatabaseResponseDto }) @LogAdminOperation({ operationType: 'BATCH', targetType: 'zulip_accounts', description: '批量更新Zulip账号状态', isSensitive: true }) @Post('zulip-accounts/batch-update-status') async batchUpdateZulipAccountStatus(@Body() batchUpdateDto: AdminBatchUpdateStatusDto): Promise { return await this.databaseManagementService.batchUpdateZulipAccountStatus( batchUpdateDto.ids, batchUpdateDto.status, batchUpdateDto.reason ); } // ==================== 系统健康检查接口 ==================== @ApiOperation({ summary: '数据库管理系统健康检查', description: '检查数据库管理系统的运行状态和连接情况' }) @ApiResponse({ status: 200, description: '系统正常', type: AdminHealthCheckResponseDto }) @Get('health') async healthCheck(): Promise { return createSuccessResponse({ status: 'healthy', timestamp: getCurrentTimestamp(), services: { users: 'connected', user_profiles: 'connected', zulip_accounts: 'connected' } }, '数据库管理系统运行正常', REQUEST_ID_PREFIXES.HEALTH_CHECK); } }