From dd5cc48b4982d4ed1ae184d2da9a6c23d81825cc Mon Sep 17 00:00:00 2001 From: moyin <244344649@qq.com> Date: Thu, 8 Jan 2026 23:03:40 +0800 Subject: [PATCH] =?UTF-8?q?docs=EF=BC=9A=E6=9B=B4=E6=96=B0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A3=80=E6=9F=A5=E8=A7=84=E8=8C=83=E5=92=8CAPI?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新AI代码检查规范简洁版 - 完善开发者代码检查规范 - 扩展OpenAPI文档,添加新的接口定义 --- AI代码检查规范_简洁版.md | 59 +- docs/api/openapi.yaml | 1690 ++++++++++++++++++++++++++++- 开发者代码检查规范.md | 79 +- 3 files changed, 1797 insertions(+), 31 deletions(-) diff --git a/AI代码检查规范_简洁版.md b/AI代码检查规范_简洁版.md index 9110344..4b69772 100644 --- a/AI代码检查规范_简洁版.md +++ b/AI代码检查规范_简洁版.md @@ -14,7 +14,7 @@ - **常量**:SCREAMING_SNAKE_CASE - **路由**:kebab-case - **文件夹优化**:删除单文件文件夹,扁平化结构 -- **Core层命名**:业务支撑模块用_core后缀,工具模块不用 +- **Core层命名**:业务支撑模块用_core后缀,通用工具模块不用 #### 文件夹结构检查要求 **必须使用listDirectory工具详细检查每个文件夹的内容:** @@ -28,11 +28,19 @@ - 不超过3个文件的文件夹:必须扁平化处理 - 4个以上文件:通常保持独立文件夹 - 完整功能模块:即使文件较少也可以保持独立(需特殊说明) +- **测试文件位置**:测试文件必须与对应源文件放在同一目录,不允许单独的tests文件夹 + +**测试文件位置规范(重要):** +- ✅ 正确:`src/business/admin/admin.service.ts` 和 `src/business/admin/admin.service.spec.ts` 同目录 +- ❌ 错误:`src/business/admin/tests/admin.service.spec.ts` 单独tests文件夹 +- **强制要求**:所有tests/、test/等测试专用文件夹必须扁平化,测试文件移动到源文件同目录 +- **扁平化处理**:包括tests/、test/、spec/、__tests__/等所有测试文件夹都必须扁平化 **常见错误:** - 只看文件夹名称,不检查内容 - 凭印象判断,不使用工具获取准确数据 - 遗漏3个文件以下文件夹的识别 +- **忽略测试文件夹**:认为tests文件夹是"标准结构"而不进行扁平化检查 ### 步骤2:注释规范检查 - **文件头注释**:功能描述、职责分离、修改记录、@author、@version、@since、@lastModified @@ -44,22 +52,38 @@ - **AI标识替换**:只有AI标识(kiro、ChatGPT、Claude、AI等)才可替换为用户名称 - **判断示例**:`@author kiro` → 可替换,`@author 张三` → 必须保留 - **版本号递增**:规范优化/Bug修复→修订版本+1,功能变更→次版本+1,重构→主版本+1 -- **时间更新**:每次修改必须更新@lastModified字段 +- **时间更新**:只有真正修改了文件内容时才更新@lastModified字段,仅检查不修改内容时不更新日期 ### 步骤3:代码质量检查 - **清理未使用**:导入、变量、方法 - **常量定义**:使用SCREAMING_SNAKE_CASE - **方法长度**:建议不超过50行 - **代码重复**:识别并消除重复代码 +- **魔法数字**:提取为常量定义 +- **工具函数**:抽象重复逻辑为可复用函数 ### 步骤4:架构分层检查 -- **Core层**:专注技术实现,不含业务逻辑,业务支撑模块用_core后缀 +- **检查范围**:仅检查当前执行检查的文件夹,不考虑其他同层功能模块 +- **Core层**:专注技术实现,不含业务逻辑 +- **Core层命名规则**: + - **业务支撑模块**:为特定业务功能提供技术支撑,使用`_core`后缀(如:`location_broadcast_core`) + - **通用工具模块**:提供可复用的数据访问或技术服务,不使用后缀(如:`user_profiles`、`redis_cache`) + - **判断方法**:检查模块是否专门为某个业务服务,如果是则使用`_core`后缀,如果是通用服务则不使用 - **Business层**:专注业务逻辑,不含技术实现细节 - **依赖关系**:Core层不能导入Business层,Business层通过依赖注入使用Core层 - **职责分离**:确保各层职责清晰,边界明确 ### 步骤5:测试覆盖检查 - **测试文件存在性**:每个Service必须有.spec.ts文件 +- **Service定义**:只有以下类型需要测试文件 + - ✅ **Service类**:文件名包含`.service.ts`的业务逻辑类 + - ✅ **Controller类**:文件名包含`.controller.ts`的控制器类 + - ✅ **Gateway类**:文件名包含`.gateway.ts`的WebSocket网关类 + - ❌ **Middleware类**:中间件不需要测试文件 + - ❌ **Guard类**:守卫不需要测试文件 + - ❌ **DTO类**:数据传输对象不需要测试文件 + - ❌ **Interface文件**:接口定义不需要测试文件 + - ❌ **Utils工具类**:工具函数不需要测试文件 - **方法覆盖**:所有公共方法必须有测试 - **场景覆盖**:正常、异常、边界情况 - **测试质量**:真实有效的测试用例,不是空壳 @@ -118,25 +142,40 @@ ### 架构分层 ```typescript -// Core层 - 技术实现 +// Core层 - 业务支撑模块(使用_core后缀) @Injectable() -export class RedisService { - async set(key: string, value: any): Promise { - // 专注技术实现 +export class LocationBroadcastCoreService { + async broadcastPosition(data: PositionData): Promise { + // 为位置广播业务提供技术支撑 + } +} + +// Core层 - 通用工具模块(不使用后缀) +@Injectable() +export class UserProfilesService { + async findByUserId(userId: bigint): Promise { + // 通用的用户档案数据访问服务 } } // Business层 - 业务逻辑 @Injectable() -export class UserBusinessService { - constructor(private readonly userCoreService: UserCoreService) {} +export class LocationBroadcastService { + constructor( + private readonly locationBroadcastCore: LocationBroadcastCoreService, + private readonly userProfiles: UserProfilesService + ) {} - async registerUser(data: RegisterDto): Promise { + async updateUserLocation(userId: string, position: Position): Promise { // 业务逻辑:验证、调用Core层、返回结果 } } ``` +**Core层命名判断标准:** +- **业务支撑模块**:专门为某个业务功能提供技术支撑 → 使用`_core`后缀 +- **通用工具模块**:提供可复用的数据访问或基础服务 → 不使用后缀 + ### 测试覆盖 ```typescript describe('UserService', () => { diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 7a4b7fd..5b39ac3 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,8 +1,8 @@ openapi: 3.0.3 info: - title: Pixel Game Server - Auth API - description: 像素游戏服务器用户认证API接口文档 - 包含验证码登录功能、邮箱冲突检测、验证码冷却优化和邮件模板修复 - version: 1.1.3 + title: Pixel Game Server API + description: 像素游戏服务器完整API接口文档 - 包含用户认证、位置广播、聊天系统、管理员后台等功能模块 + version: 1.2.0 contact: name: API Support email: support@example.com @@ -19,10 +19,18 @@ tags: description: 应用状态相关接口 - name: auth description: 用户认证相关接口 - - name: admin - description: 管理员后台相关接口 - - name: user-management - description: 用户管理相关接口 + - name: location-broadcast + description: 位置广播系统相关接口 + - name: health + description: 健康检查相关接口 + - name: chat + description: 聊天系统相关接口 + - name: zulip-accounts + description: Zulip账号关联管理相关接口 + - name: admin-database + description: 管理员数据库管理相关接口 + - name: admin-operation-logs + description: 管理员操作日志相关接口 paths: /: @@ -569,7 +577,1237 @@ paths: schema: $ref: '#/components/schemas/CommonResponse' + /auth/refresh-token: + post: + tags: + - auth + summary: 刷新访问令牌 + description: 使用有效的刷新令牌生成新的访问令牌,实现无感知的令牌续期 + operationId: refreshToken + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RefreshTokenDto' + example: + refresh_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + responses: + '200': + description: 令牌刷新成功 + content: + application/json: + schema: + $ref: '#/components/schemas/RefreshTokenResponse' + '400': + description: 请求参数错误 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: 刷新令牌无效或已过期 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: 用户不存在或已被禁用 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '429': + description: 刷新请求过于频繁 + content: + application/json: + schema: + $ref: '#/components/schemas/ThrottleErrorResponse' + + # ==================== 位置广播系统接口 ==================== + + /location-broadcast/sessions: + post: + tags: + - location-broadcast + summary: 创建新游戏会话 + description: 创建一个新的位置广播会话,支持自定义配置 + operationId: createSession + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSessionDto' + example: + sessionId: session_12345 + name: 测试会话 + description: 这是一个测试会话 + maxUsers: 100 + isPublic: true + responses: + '201': + description: 会话创建成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + session: + type: object + message: + type: string + example: 会话创建成功 + '400': + description: 请求参数错误 + '409': + description: 会话ID已存在 + get: + tags: + - location-broadcast + summary: 查询会话列表 + description: 根据条件查询游戏会话列表,支持分页和过滤 + operationId: querySessions + security: + - bearerAuth: [] + parameters: + - name: status + in: query + required: false + description: 会话状态 + schema: + type: string + - name: limit + in: query + required: false + description: 分页大小 + schema: + type: integer + default: 20 + - name: offset + in: query + required: false + description: 分页偏移 + schema: + type: integer + default: 0 + responses: + '200': + description: 查询成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + sessions: + type: array + items: + type: object + total: + type: number + example: 10 + message: + type: string + example: 查询成功 + + /location-broadcast/sessions/{sessionId}: + get: + tags: + - location-broadcast + summary: 获取会话详情 + description: 获取指定会话的详细信息,包括用户列表和位置信息 + operationId: getSessionDetail + security: + - bearerAuth: [] + parameters: + - name: sessionId + in: path + required: true + description: 会话ID + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + session: + type: object + users: + type: array + items: + type: object + message: + type: string + example: 获取成功 + '404': + description: 会话不存在 + + /location-broadcast/positions: + get: + tags: + - location-broadcast + summary: 查询位置信息 + description: 根据条件查询用户位置信息,支持范围查询和地图过滤 + operationId: queryPositions + security: + - bearerAuth: [] + parameters: + - name: mapId + in: query + required: false + description: 地图ID + schema: + type: string + - name: sessionId + in: query + required: false + description: 会话ID + schema: + type: string + - name: limit + in: query + required: false + description: 分页大小 + schema: + type: integer + default: 20 + responses: + '200': + description: 查询成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + positions: + type: array + items: + type: object + total: + type: number + example: 5 + message: + type: string + example: 查询成功 + + /location-broadcast/positions/stats: + get: + tags: + - location-broadcast + summary: 获取位置统计信息 + description: 获取系统位置数据的统计信息,包括用户分布和活跃度 + operationId: getPositionStats + security: + - bearerAuth: [] + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + stats: + type: object + message: + type: string + example: 获取成功 + + /location-broadcast/users/{userId}/data: + delete: + tags: + - location-broadcast + summary: 清理用户数据 + description: 清理指定用户的位置数据和会话信息 + operationId: cleanupUserData + security: + - bearerAuth: [] + parameters: + - name: userId + in: path + required: true + description: 用户ID + schema: + type: string + responses: + '200': + description: 清理成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + message: + type: string + example: 清理成功 + + # ==================== 健康检查接口 ==================== + + /health: + get: + tags: + - health + summary: 基础健康检查 + description: 检查位置广播服务的基本可用性 + operationId: healthCheck + responses: + '200': + description: 服务正常 + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: ok + timestamp: + type: number + example: 1641234567890 + service: + type: string + example: location-broadcast + version: + type: string + example: 1.0.0 + '503': + description: 服务不可用 + + /health/detailed: + get: + tags: + - health + summary: 详细健康报告 + description: 获取位置广播系统各组件的详细健康状态 + operationId: detailedHealth + responses: + '200': + description: 健康报告获取成功 + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: ok + timestamp: + type: number + example: 1641234567890 + service: + type: string + example: location-broadcast + components: + type: object + properties: + redis: + type: object + database: + type: object + core_services: + type: object + metrics: + type: object + + /health/ready: + get: + tags: + - health + summary: 就绪检查 + description: 检查位置广播服务是否准备好接收请求 + operationId: readinessCheck + responses: + '200': + description: 服务已就绪 + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: ready + timestamp: + type: number + example: 1641234567890 + checks: + type: object + + /health/live: + get: + tags: + - health + summary: 存活检查 + description: 检查位置广播服务是否仍在运行 + operationId: livenessCheck + responses: + '200': + description: 服务存活 + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: alive + timestamp: + type: number + example: 1641234567890 + uptime: + type: number + example: 3600000 + + /health/metrics: + get: + tags: + - health + summary: 性能指标 + description: 获取位置广播系统的性能指标和资源使用情况 + operationId: getMetrics + responses: + '200': + description: 指标获取成功 + content: + application/json: + schema: + type: object + properties: + timestamp: + type: number + example: 1641234567890 + system: + type: object + application: + type: object + performance: + type: object + + # ==================== 聊天系统接口 ==================== + + /chat/send: + post: + tags: + - chat + summary: 发送聊天消息 + description: 通过 REST API 发送聊天消息到 Zulip。注意:推荐使用 WebSocket 接口以获得更好的实时性 + operationId: sendMessage + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SendChatMessageDto' + responses: + '200': + description: 消息发送成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ChatMessageResponseDto' + '400': + description: 请求参数错误 + '401': + description: 未授权访问 + '500': + description: 服务器内部错误 + + /chat/history: + get: + tags: + - chat + summary: 获取聊天历史记录 + description: 获取指定地图或全局的聊天历史记录 + operationId: getChatHistory + security: + - bearerAuth: [] + parameters: + - name: mapId + in: query + required: false + description: 地图ID,不指定则获取全局消息 + schema: + type: string + example: whale_port + - name: limit + in: query + required: false + description: 消息数量限制 + schema: + type: integer + example: 50 + - name: offset + in: query + required: false + description: 偏移量(分页用) + schema: + type: integer + example: 0 + responses: + '200': + description: 获取聊天历史成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ChatHistoryResponseDto' + '401': + description: 未授权访问 + '500': + description: 服务器内部错误 + + /chat/status: + get: + tags: + - chat + summary: 获取聊天系统状态 + description: 获取 WebSocket 连接状态、Zulip 集成状态等系统信息 + operationId: getSystemStatus + responses: + '200': + description: 获取系统状态成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SystemStatusResponseDto' + '500': + description: 服务器内部错误 + + /chat/websocket/info: + get: + tags: + - chat + summary: 获取 WebSocket 连接信息 + description: 获取 WebSocket 连接的详细信息,包括连接地址、协议等 + operationId: getWebSocketInfo + responses: + '200': + description: 获取连接信息成功 + content: + application/json: + schema: + type: object + properties: + websocketUrl: + type: string + example: ws://localhost:3000/game + description: WebSocket 连接地址 + namespace: + type: string + example: /game + description: WebSocket 命名空间 + supportedEvents: + type: array + items: + type: string + example: ['login', 'chat', 'position_update'] + description: 支持的事件类型 + authRequired: + type: boolean + example: true + description: 是否需要认证 + documentation: + type: string + example: https://docs.example.com/websocket + description: 文档链接 + + # ==================== Zulip账号关联管理接口 ==================== + + /zulip-accounts: + post: + tags: + - zulip-accounts + summary: 创建Zulip账号关联 + description: 为游戏用户创建与Zulip账号的关联关系 + operationId: createZulipAccount + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateZulipAccountDto' + responses: + '201': + description: 创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ZulipAccountResponseDto' + '400': + description: 请求参数错误 + '409': + description: 关联已存在 + get: + tags: + - zulip-accounts + summary: 查询Zulip账号关联列表 + description: 根据条件查询Zulip账号关联列表 + operationId: findManyZulipAccounts + security: + - bearerAuth: [] + parameters: + - name: gameUserId + in: query + required: false + description: 游戏用户ID + schema: + type: string + example: '12345' + - name: zulipUserId + in: query + required: false + description: Zulip用户ID + schema: + type: integer + example: 67890 + - name: zulipEmail + in: query + required: false + description: Zulip邮箱地址 + schema: + type: string + example: user@example.com + - name: status + in: query + required: false + description: 账号状态 + schema: + type: string + enum: [active, inactive, suspended, error] + - name: includeGameUser + in: query + required: false + description: 是否包含游戏用户信息 + schema: + type: boolean + example: false + responses: + '200': + description: 查询成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ZulipAccountListResponseDto' + + /zulip-accounts/{id}: + get: + tags: + - zulip-accounts + summary: 根据ID获取Zulip账号关联 + description: 根据关联记录ID获取详细信息 + operationId: findZulipAccountById + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: 关联记录ID + schema: + type: string + example: '1' + - name: includeGameUser + in: query + required: false + description: 是否包含游戏用户信息 + schema: + type: boolean + example: false + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ZulipAccountResponseDto' + '404': + description: 记录不存在 + put: + tags: + - zulip-accounts + summary: 更新Zulip账号关联 + description: 根据ID更新Zulip账号关联信息 + operationId: updateZulipAccount + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: 关联记录ID + schema: + type: string + example: '1' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateZulipAccountDto' + responses: + '200': + description: 更新成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ZulipAccountResponseDto' + '404': + description: 记录不存在 + delete: + tags: + - zulip-accounts + summary: 删除Zulip账号关联 + description: 根据ID删除Zulip账号关联记录 + operationId: deleteZulipAccount + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: 关联记录ID + schema: + type: string + example: '1' + responses: + '200': + description: 删除成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + message: + type: string + example: 删除成功 + '404': + description: 记录不存在 + + /zulip-accounts/game-user/{gameUserId}: + get: + tags: + - zulip-accounts + summary: 根据游戏用户ID获取Zulip账号关联 + description: 根据游戏用户ID获取关联的Zulip账号信息 + operationId: findZulipAccountByGameUserId + security: + - bearerAuth: [] + parameters: + - name: gameUserId + in: path + required: true + description: 游戏用户ID + schema: + type: string + example: '12345' + - name: includeGameUser + in: query + required: false + description: 是否包含游戏用户信息 + schema: + type: boolean + example: false + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ZulipAccountResponseDto' + '404': + description: 关联不存在 + put: + tags: + - zulip-accounts + summary: 根据游戏用户ID更新关联 + description: 根据游戏用户ID更新Zulip账号关联信息 + operationId: updateZulipAccountByGameUserId + security: + - bearerAuth: [] + parameters: + - name: gameUserId + in: path + required: true + description: 游戏用户ID + schema: + type: string + example: '12345' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateZulipAccountDto' + responses: + '200': + description: 更新成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ZulipAccountResponseDto' + '404': + description: 关联不存在 + delete: + tags: + - zulip-accounts + summary: 根据游戏用户ID删除关联 + description: 根据游戏用户ID删除Zulip账号关联记录 + operationId: deleteZulipAccountByGameUserId + security: + - bearerAuth: [] + parameters: + - name: gameUserId + in: path + required: true + description: 游戏用户ID + schema: + type: string + example: '12345' + responses: + '200': + description: 删除成功 + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + message: + type: string + example: 删除成功 + '404': + description: 关联不存在 + + /zulip-accounts/management/statistics: + get: + tags: + - zulip-accounts + summary: 获取账号状态统计 + description: 获取各种状态的账号数量统计 + operationId: getZulipAccountStatistics + security: + - bearerAuth: [] + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ZulipAccountStatsResponseDto' + + /zulip-accounts/management/batch-status: + put: + tags: + - zulip-accounts + summary: 批量更新账号状态 + description: 批量更新多个账号的状态 + operationId: batchUpdateZulipAccountStatus + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BatchUpdateStatusDto' + responses: + '200': + description: 更新成功 + content: + application/json: + schema: + $ref: '#/components/schemas/BatchUpdateResponseDto' + + # ==================== 管理员数据库管理接口 ==================== + + /admin/database/users: + get: + tags: + - admin-database + summary: 获取用户列表 + description: 分页获取用户列表,支持管理员查看所有用户信息 + operationId: getUserList + security: + - bearerAuth: [] + parameters: + - name: limit + in: query + required: false + description: 返回数量(默认20,最大100) + schema: + type: integer + example: 20 + - name: offset + in: query + required: false + description: 偏移量(默认0) + schema: + type: integer + example: 0 + responses: + '200': + description: 获取成功 + '401': + description: 未授权访问 + '403': + description: 权限不足 + post: + tags: + - admin-database + summary: 创建用户 + description: 创建新用户,需要提供用户名和昵称等基本信息 + operationId: createUser + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AdminCreateUserDto' + responses: + '201': + description: 创建成功 + '400': + description: 请求参数错误 + '409': + description: 用户名或邮箱已存在 + + /admin/database/users/{id}: + get: + tags: + - admin-database + summary: 获取用户详情 + description: 根据用户ID获取详细的用户信息 + operationId: getUserById + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: 用户ID + schema: + type: string + example: '1' + responses: + '200': + description: 获取成功 + '404': + description: 用户不存在 + put: + tags: + - admin-database + summary: 更新用户 + description: 根据用户ID更新用户信息 + operationId: updateUser + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: 用户ID + schema: + type: string + example: '1' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AdminUpdateUserDto' + responses: + '200': + description: 更新成功 + '404': + description: 用户不存在 + delete: + tags: + - admin-database + summary: 删除用户 + description: 根据用户ID删除用户(软删除) + operationId: deleteUser + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: 用户ID + schema: + type: string + example: '1' + responses: + '200': + description: 删除成功 + '404': + description: 用户不存在 + + /admin/database/users/search: + get: + tags: + - admin-database + summary: 搜索用户 + description: 根据关键词搜索用户,支持用户名、邮箱、昵称模糊匹配 + operationId: searchUsers + security: + - bearerAuth: [] + parameters: + - name: keyword + in: query + required: true + description: 搜索关键词 + schema: + type: string + example: admin + - name: limit + in: query + required: false + description: 返回数量(默认20,最大50) + schema: + type: integer + example: 20 + responses: + '200': + description: 搜索成功 + + /admin/database/health: + get: + tags: + - admin-database + summary: 数据库管理系统健康检查 + description: 检查数据库管理系统的运行状态和连接情况 + operationId: adminDatabaseHealthCheck + security: + - bearerAuth: [] + responses: + '200': + description: 系统正常 + + # ==================== 管理员操作日志接口 ==================== + + /admin/operation-logs: + get: + tags: + - admin-operation-logs + summary: 获取操作日志列表 + description: 分页获取管理员操作日志,支持多种过滤条件 + operationId: getOperationLogs + security: + - bearerAuth: [] + parameters: + - name: limit + in: query + required: false + description: 返回数量(默认50,最大200) + schema: + type: integer + example: 50 + - name: offset + in: query + required: false + description: 偏移量(默认0) + schema: + type: integer + example: 0 + - name: adminUserId + in: query + required: false + description: 管理员用户ID过滤 + schema: + type: string + example: '123' + - name: operationType + in: query + required: false + description: 操作类型过滤 + schema: + type: string + example: CREATE + - name: targetType + in: query + required: false + description: 目标类型过滤 + schema: + type: string + example: users + - name: operationResult + in: query + required: false + description: 操作结果过滤 + schema: + type: string + example: SUCCESS + - name: startDate + in: query + required: false + description: 开始日期(ISO格式) + schema: + type: string + example: '2026-01-01T00:00:00.000Z' + - name: endDate + in: query + required: false + description: 结束日期(ISO格式) + schema: + type: string + example: '2026-01-08T23:59:59.999Z' + - name: isSensitive + in: query + required: false + description: 是否敏感操作 + schema: + type: boolean + example: true + responses: + '200': + description: 获取成功 + '401': + description: 未授权访问 + '403': + description: 权限不足 + + /admin/operation-logs/{id}: + get: + tags: + - admin-operation-logs + summary: 获取操作日志详情 + description: 根据日志ID获取操作日志的详细信息 + operationId: getOperationLogById + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: 日志ID + schema: + type: string + example: uuid-123 + responses: + '200': + description: 获取成功 + '404': + description: 日志不存在 + + /admin/operation-logs/statistics: + get: + tags: + - admin-operation-logs + summary: 获取操作统计信息 + description: 获取管理员操作的统计信息,包括操作数量、类型分布等 + operationId: getOperationStatistics + security: + - bearerAuth: [] + parameters: + - name: startDate + in: query + required: false + description: 开始日期(ISO格式) + schema: + type: string + example: '2026-01-01T00:00:00.000Z' + - name: endDate + in: query + required: false + description: 结束日期(ISO格式) + schema: + type: string + example: '2026-01-08T23:59:59.999Z' + responses: + '200': + description: 获取成功 + + /admin/operation-logs/sensitive: + get: + tags: + - admin-operation-logs + summary: 获取敏感操作日志 + description: 获取标记为敏感的操作日志,用于安全审计 + operationId: getSensitiveOperations + security: + - bearerAuth: [] + parameters: + - name: limit + in: query + required: false + description: 返回数量(默认50,最大200) + schema: + type: integer + example: 50 + - name: offset + in: query + required: false + description: 偏移量(默认0) + schema: + type: integer + example: 0 + responses: + '200': + description: 获取成功 + + /admin/operation-logs/cleanup: + delete: + tags: + - admin-operation-logs + summary: 清理过期日志 + description: 清理超过指定天数的操作日志,释放存储空间 + operationId: cleanupExpiredLogs + security: + - bearerAuth: [] + parameters: + - name: daysToKeep + in: query + required: false + description: 保留天数(默认90,最少7,最多365) + schema: + type: integer + example: 90 + responses: + '200': + description: 清理成功 + '400': + description: 参数错误 + components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT schemas: AppStatusResponse: type: object @@ -1137,4 +2375,440 @@ components: currentTime: type: integer description: 当前时间戳 - example: 1766649341250 \ No newline at end of file + example: 1766649341250 + + # ==================== 新增的DTO定义 ==================== + + RefreshTokenDto: + type: object + required: + - refresh_token + properties: + refresh_token: + type: string + description: 刷新令牌 + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + + RefreshTokenResponse: + type: object + properties: + success: + type: boolean + description: 请求是否成功 + example: true + data: + type: object + properties: + access_token: + type: string + description: 新的访问令牌 + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + refresh_token: + type: string + description: 新的刷新令牌 + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + expires_in: + type: integer + description: 访问令牌过期时间(秒) + example: 3600 + message: + type: string + description: 响应消息 + example: 令牌刷新成功 + + CreateSessionDto: + type: object + required: + - sessionId + - name + properties: + sessionId: + type: string + description: 会话ID + example: session_12345 + name: + type: string + description: 会话名称 + example: 测试会话 + description: + type: string + description: 会话描述 + example: 这是一个测试会话 + maxUsers: + type: integer + description: 最大用户数 + example: 100 + isPublic: + type: boolean + description: 是否公开 + example: true + + SendChatMessageDto: + type: object + required: + - content + - scope + properties: + content: + type: string + description: 消息内容 + example: 大家好! + scope: + type: string + description: 消息范围 + enum: [local, global] + example: local + mapId: + type: string + description: 地图ID(local范围时需要) + example: whale_port + + ChatMessageResponseDto: + type: object + properties: + success: + type: boolean + example: true + messageId: + type: string + example: msg_12345 + timestamp: + type: string + format: date-time + example: "2026-01-08T10:00:00.000Z" + + ChatHistoryResponseDto: + type: object + properties: + success: + type: boolean + example: true + messages: + type: array + items: + type: object + properties: + id: + type: integer + example: 1 + sender: + type: string + example: Player_123 + content: + type: string + example: 大家好!我刚进入游戏 + scope: + type: string + example: local + mapId: + type: string + example: whale_port + timestamp: + type: string + format: date-time + example: "2026-01-08T10:00:00.000Z" + streamName: + type: string + example: Whale Port + topicName: + type: string + example: Game Chat + total: + type: integer + example: 2 + count: + type: integer + example: 2 + + SystemStatusResponseDto: + type: object + properties: + websocket: + type: object + properties: + totalConnections: + type: integer + example: 25 + authenticatedConnections: + type: integer + example: 20 + activeSessions: + type: integer + example: 20 + mapPlayerCounts: + type: object + properties: + whale_port: + type: integer + example: 8 + pumpkin_valley: + type: integer + example: 6 + novice_village: + type: integer + example: 6 + zulip: + type: object + properties: + serverConnected: + type: boolean + example: true + serverVersion: + type: string + example: "11.4" + botAccountActive: + type: boolean + example: true + availableStreams: + type: integer + example: 12 + gameStreams: + type: array + items: + type: string + example: ["Whale Port", "Pumpkin Valley", "Novice Village"] + recentMessageCount: + type: integer + example: 156 + uptime: + type: integer + example: 3600 + memory: + type: object + properties: + used: + type: string + example: "45.2 MB" + total: + type: string + example: "128.0 MB" + percentage: + type: number + example: 35.31 + + CreateZulipAccountDto: + type: object + required: + - gameUserId + - zulipUserId + - zulipEmail + properties: + gameUserId: + type: string + description: 游戏用户ID + example: "12345" + zulipUserId: + type: integer + description: Zulip用户ID + example: 67890 + zulipEmail: + type: string + format: email + description: Zulip邮箱地址 + example: user@example.com + zulipFullName: + type: string + description: Zulip全名 + example: John Doe + status: + type: string + description: 账号状态 + enum: [active, inactive, suspended, error] + default: active + + UpdateZulipAccountDto: + type: object + properties: + zulipUserId: + type: integer + description: Zulip用户ID + example: 67890 + zulipEmail: + type: string + format: email + description: Zulip邮箱地址 + example: user@example.com + zulipFullName: + type: string + description: Zulip全名 + example: John Doe + status: + type: string + description: 账号状态 + enum: [active, inactive, suspended, error] + + ZulipAccountResponseDto: + type: object + properties: + success: + type: boolean + example: true + data: + type: object + properties: + id: + type: string + example: "1" + gameUserId: + type: string + example: "12345" + zulipUserId: + type: integer + example: 67890 + zulipEmail: + type: string + example: user@example.com + zulipFullName: + type: string + example: John Doe + status: + type: string + example: active + createdAt: + type: string + format: date-time + example: "2026-01-08T10:00:00.000Z" + updatedAt: + type: string + format: date-time + example: "2026-01-08T10:00:00.000Z" + + ZulipAccountListResponseDto: + type: object + properties: + success: + type: boolean + example: true + data: + type: array + items: + $ref: '#/components/schemas/ZulipAccountResponseDto' + total: + type: integer + example: 10 + count: + type: integer + example: 10 + + ZulipAccountStatsResponseDto: + type: object + properties: + success: + type: boolean + example: true + data: + type: object + properties: + total: + type: integer + example: 100 + active: + type: integer + example: 85 + inactive: + type: integer + example: 10 + suspended: + type: integer + example: 3 + error: + type: integer + example: 2 + + BatchUpdateStatusDto: + type: object + required: + - ids + - status + properties: + ids: + type: array + items: + type: string + description: 要更新的记录ID列表 + example: ["1", "2", "3"] + status: + type: string + description: 新状态 + enum: [active, inactive, suspended, error] + example: active + + BatchUpdateResponseDto: + type: object + properties: + success: + type: boolean + example: true + data: + type: object + properties: + updated: + type: integer + example: 3 + failed: + type: integer + example: 0 + message: + type: string + example: 批量更新完成 + + AdminCreateUserDto: + type: object + required: + - username + - nickname + properties: + username: + type: string + description: 用户名 + example: newuser + nickname: + type: string + description: 用户昵称 + example: 新用户 + email: + type: string + format: email + description: 邮箱地址 + example: newuser@example.com + phone: + type: string + description: 手机号码 + example: "+8613800138000" + password: + type: string + description: 密码 + example: password123 + role: + type: integer + description: 用户角色 + example: 1 + + AdminUpdateUserDto: + type: object + properties: + username: + type: string + description: 用户名 + example: updateduser + nickname: + type: string + description: 用户昵称 + example: 更新用户 + email: + type: string + format: email + description: 邮箱地址 + example: updated@example.com + phone: + type: string + description: 手机号码 + example: "+8613800138001" + role: + type: integer + description: 用户角色 + example: 1 + status: + type: string + description: 用户状态 + example: active \ No newline at end of file diff --git a/开发者代码检查规范.md b/开发者代码检查规范.md index bb98df9..8d49a6c 100644 --- a/开发者代码检查规范.md +++ b/开发者代码检查规范.md @@ -273,7 +273,7 @@ async validateUser(loginRequest: LoginRequest): Promise { **修改记录更新要求:** - **必须添加**:每次修改文件后,必须在"最近修改"部分添加新的修改记录 - **信息完整**:包含修改日期、修改类型、修改内容、修改者姓名 -- **时间更新**:同时更新@lastModified字段为当前修改时间 +- **时间更新**:只有真正修改了文件内容时才更新@lastModified字段,仅检查不修改内容时不更新日期 - **版本递增**:根据修改类型适当递增版本号 **版本号递增规则:** @@ -364,19 +364,43 @@ src/ **职责:专注技术实现,不包含业务逻辑** #### 命名规范 -- **业务支撑模块**:使用`_core`后缀(如`users_core`、`login_core`) -- **底层工具模块**:不使用`_core`后缀(如`redis`、`logger`) +- **检查范围**:仅检查当前执行检查的文件夹,不考虑其他同层功能模块 +- **业务支撑模块**:专门为特定业务功能提供技术支撑,使用`_core`后缀(如`location_broadcast_core`、`user_auth_core`) +- **通用工具模块**:提供可复用的数据访问或基础技术服务,不使用`_core`后缀(如`user_profiles`、`redis_cache`、`logger`) + +**判断标准:** +- **业务支撑模块**:模块名称体现特定业务领域,为该业务提供技术实现 → 使用`_core`后缀 +- **通用工具模块**:模块提供通用的数据访问或技术服务,可被多个业务复用 → 不使用后缀 + +**判断流程:** +``` +1. 模块是否专门为某个特定业务功能服务? + ├─ 是 → 检查模块名称是否体现业务领域 + │ ├─ 是 → 使用 _core 后缀 (如: location_broadcast_core) + │ └─ 否 → 重新设计模块职责 + └─ 否 → 模块是否提供通用的技术服务? + ├─ 是 → 不使用 _core 后缀 (如: user_profiles, redis) + └─ 否 → 重新评估模块定位 + +2. 实际案例判断: + - user_profiles: 通用的用户档案数据访问 → 不使用后缀 ✓ + - location_broadcast_core: 专门为位置广播业务服务 → 使用_core后缀 ✓ + - redis: 通用的缓存技术服务 → 不使用后缀 ✓ + - user_auth_core: 专门为用户认证业务服务 → 使用_core后缀 ✓ +``` ```typescript ✅ 正确示例: -src/core/db/users_core/ # 为business/users提供数据层支撑 -src/core/login_core/ # 为business/auth提供登录技术实现 -src/core/redis/ # 纯Redis技术封装 -src/core/utils/logger/ # 纯日志工具 +src/core/location_broadcast_core/ # 专门为位置广播业务提供技术支撑 +src/core/user_auth_core/ # 专门为用户认证业务提供技术支撑 +src/core/db/user_profiles/ # 通用的用户档案数据访问服务 +src/core/redis/ # 通用的Redis技术封装 +src/core/utils/logger/ # 通用的日志工具服务 ❌ 错误示例: -src/core/db/users/ # 应该是users_core -src/core/redis_core/ # 应该是redis +src/core/location_broadcast/ # 应该是location_broadcast_core +src/core/db/user_profiles_core/ # 应该是user_profiles(通用工具) +src/core/redis_core/ # 应该是redis(通用工具) ``` #### 技术实现示例 @@ -530,19 +554,48 @@ export class DatabaseService { **规则:每个Service都必须有对应的.spec.ts测试文件** +**⚠️ Service定义(重要):** +只有以下类型需要测试文件: +- ✅ **Service类**:文件名包含`.service.ts`的业务逻辑类 +- ✅ **Controller类**:文件名包含`.controller.ts`的控制器类 +- ✅ **Gateway类**:文件名包含`.gateway.ts`的WebSocket网关类 + +**❌ 以下类型不需要测试文件:** +- ❌ **Middleware类**:中间件(`.middleware.ts`)不需要测试文件 +- ❌ **Guard类**:守卫(`.guard.ts`)不需要测试文件 +- ❌ **DTO类**:数据传输对象(`.dto.ts`)不需要测试文件 +- ❌ **Interface文件**:接口定义(`.interface.ts`)不需要测试文件 +- ❌ **Utils工具类**:工具函数(`.utils.ts`)不需要测试文件 +- ❌ **Config文件**:配置文件(`.config.ts`)不需要测试文件 + +**测试文件位置规范(重要):** +- ✅ **正确位置**:测试文件必须与对应源文件放在同一目录 +- ❌ **错误位置**:测试文件放在单独的tests/、test/、spec/、__tests__/等文件夹中 + ```typescript -// ✅ 正确:Service与测试文件一一对应 +// ✅ 正确:测试文件与源文件同目录 src/core/db/users/users.service.ts src/core/db/users/users.service.spec.ts -src/core/db/users/users_memory.service.ts -src/core/db/users/users_memory.service.spec.ts +src/business/admin/admin.service.ts +src/business/admin/admin.service.spec.ts + +// ❌ 错误:测试文件在单独文件夹 +src/business/admin/admin.service.ts +src/business/admin/tests/admin.service.spec.ts # 错误位置 +src/business/admin/__tests__/admin.service.spec.ts # 错误位置 // ❌ 错误:缺少测试文件 src/core/login_core/login_core.service.ts # 缺少:src/core/login_core/login_core.service.spec.ts ``` +**扁平化要求:** +- **强制扁平化**:所有tests/、test/、spec/、__tests__/等测试专用文件夹必须扁平化 +- **移动规则**:将测试文件移动到对应源文件的同一目录 +- **更新引用**:移动后必须更新所有import路径引用 +- **删除空文件夹**:移动完成后删除空的测试文件夹 + ### 🎯 测试用例覆盖完整性 **要求:测试文件必须覆盖Service中的所有公共方法** @@ -792,7 +845,7 @@ npx jest src/core/db/users --coverage --testPathIgnorePatterns="integration.spec - [ ] 常量使用SCREAMING_SNAKE_CASE(全大写+下划线) - [ ] 路由使用kebab-case(短横线分隔) - [ ] 避免过度嵌套的文件夹结构 -- [ ] Core层业务支撑模块使用_core后缀 +- [ ] Core层业务支撑模块使用_core后缀,通用工具模块不使用后缀 #### 注释规范检查清单 - [ ] 文件头注释包含功能描述、职责分离、修改记录