forked from datawhale/whale-town-end
feat:实现管理员系统核心功能
- 添加管理员数据库管理控制器和服务 - 实现管理员操作日志记录系统 - 添加数据库异常处理过滤器 - 完善管理员权限验证和响应格式 - 添加全面的属性测试覆盖
This commit is contained in:
400
src/business/admin/admin_database.controller.ts
Normal file
400
src/business/admin/admin_database.controller.ts
Normal file
@@ -0,0 +1,400 @@
|
||||
/**
|
||||
* 管理员数据库管理控制器
|
||||
*
|
||||
* 功能描述:
|
||||
* - 提供管理员专用的数据库管理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
|
||||
} 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<AdminListResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminListResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminListResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminListResponse> {
|
||||
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: any): Promise<AdminApiResponse> {
|
||||
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: any
|
||||
): Promise<AdminApiResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminListResponse> {
|
||||
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<AdminApiResponse> {
|
||||
return await this.databaseManagementService.getZulipAccountById(id);
|
||||
}
|
||||
|
||||
@ApiOperation({
|
||||
summary: '获取Zulip账号关联统计',
|
||||
description: '获取各种状态的Zulip账号关联数量统计信息'
|
||||
})
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
@Get('zulip-accounts/statistics')
|
||||
async getZulipAccountStatistics(): Promise<AdminApiResponse> {
|
||||
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: any): Promise<AdminApiResponse> {
|
||||
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: any
|
||||
): Promise<AdminApiResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminApiResponse> {
|
||||
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<AdminApiResponse> {
|
||||
return createSuccessResponse({
|
||||
status: 'healthy',
|
||||
timestamp: getCurrentTimestamp(),
|
||||
services: {
|
||||
users: 'connected',
|
||||
user_profiles: 'connected',
|
||||
zulip_accounts: 'connected'
|
||||
}
|
||||
}, '数据库管理系统运行正常', REQUEST_ID_PREFIXES.HEALTH_CHECK);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user