# 开发者代码检查规范 - Whale Town 游戏服务器 ## 📖 概述 本文档为Whale Town游戏服务器开发者提供全面的代码检查规范,确保代码质量、可维护性和团队协作效率。规范针对NestJS游戏服务器的双模式架构、实时通信、属性测试等特点进行了专门优化。 ## 🎯 检查流程 代码检查分为6个步骤,建议按顺序执行: 1. **命名规范检查** - 文件、变量、函数、类的命名规范 2. **注释规范检查** - 文件头、类、方法注释的完整性 3. **代码质量检查** - 代码清洁度、性能优化 4. **架构分层检查** - 分层架构的合规性 5. **测试覆盖检查** - 测试文件的完整性和覆盖率 6. **功能文档生成** - README文档的生成和维护 --- ## 1️⃣ 命名规范检查 ### 📁 文件和文件夹命名 **核心规则:使用下划线分隔(snake_case),保持项目一致性** ```typescript ✅ 正确示例: - user_controller.ts - admin_operation_log_service.ts - location_broadcast_gateway.ts - websocket_auth_guard.ts - src/business/user_mgmt/ - src/core/location_broadcast_core/ ❌ 错误示例: - UserController.ts # 大驼峰命名 - user-service.ts # 短横线分隔 - adminOperationLog.service.ts # 小驼峰命名 - src/Business/Auth/ # 大驼峰命名 ``` **⚠️ 特别注意:保持项目现有的下划线命名风格,确保代码库一致性!** **游戏服务器特殊文件类型:** ```typescript ✅ 游戏服务器专用文件类型: - location_broadcast.gateway.ts # WebSocket网关 - users_memory.service.ts # 内存模式服务 - file_redis.service.ts # 文件模式Redis - admin.property.spec.ts # 属性测试 - zulip_integration.e2e.spec.ts # E2E测试 - performance_monitor.middleware.ts # 性能监控中间件 - websocket_docs.controller.ts # WebSocket文档控制器 ``` ### 🏗️ 文件夹结构优化 **避免过度嵌套,减少单文件文件夹** ```typescript ❌ 错误:过度嵌套 src/ guards/ auth.guard.ts # 只有一个文件,不需要单独文件夹 interceptors/ logging.interceptor.ts # 只有一个文件,不需要单独文件夹 ✅ 正确:扁平化结构 src/ auth.guard.ts logging.interceptor.ts ``` **文件夹创建判断标准:** - 不超过3个文件:移到上级目录(扁平化) - 4个以上文件:可以保持独立文件夹 - 完整功能模块:即使文件较少也可以保持独立(需特殊说明) - **游戏服务器特殊考虑**: - WebSocket相关文件可以独立成文件夹(实时通信复杂性) - 双模式服务文件建议放在同一文件夹(便于对比) - 属性测试文件较多的模块可以保持独立结构 **检查方法(重要):** 1. **必须使用工具详细检查**:不能凭印象判断文件夹内容 2. **逐个统计文件数量**:使用`listDirectory(path, depth=2)`获取准确数据 3. **识别单文件文件夹**:只有1个文件的文件夹必须扁平化 4. **更新引用路径**:移动文件后必须更新所有import语句 5. **考虑游戏服务器特殊性**:实时通信、双模式、测试复杂度 **常见检查错误:** - ❌ 只看到文件夹存在就认为结构合理 - ❌ 没有统计每个文件夹的文件数量 - ❌ 凭印象判断而不使用工具验证 - ❌ 遗漏单文件文件夹的识别 **正确检查流程:** 1. 使用listDirectory工具查看详细结构 2. 逐个文件夹统计文件数量 3. 识别需要扁平化的文件夹(≤3个文件) 4. 考虑游戏服务器特殊性(WebSocket、双模式、测试复杂度) 5. 执行文件移动和路径更新操作 ### 🔤 变量和函数命名 **规则:小驼峰命名(camelCase)** ```typescript ✅ 正确示例: const userName = 'Alice'; function getUserInfo() { } async function validateUser() { } const isGameStarted = false; ❌ 错误示例: const UserName = 'Alice'; function GetUserInfo() { } const is_game_started = false; ``` ### 🏷️ 类和接口命名 **规则:大驼峰命名(PascalCase)** ```typescript ✅ 正确示例: class UserService { } interface GameConfig { } class CreateUserDto { } enum UserStatus { } ❌ 错误示例: class userService { } interface gameConfig { } class createUserDto { } ``` ### 📊 常量命名 **规则:全大写 + 下划线分隔(SCREAMING_SNAKE_CASE)** ```typescript ✅ 正确示例: const PORT = 3000; const MAX_PLAYERS = 10; const SALT_ROUNDS = 10; const DEFAULT_TIMEOUT = 5000; ❌ 错误示例: const port = 3000; const maxPlayers = 10; const saltRounds = 10; ``` ### 🛣️ 路由命名 **规则:全小写 + 短横线分隔(kebab-case)** ```typescript ✅ 正确示例: @Get('user/get-info') @Post('room/join-room') @Put('player/update-position') @WebSocketGateway({ path: '/location-broadcast' }) # WebSocket路径 @MessagePattern('user-position-update') # 消息模式 ❌ 错误示例: @Get('user/getInfo') @Post('room/joinRoom') @Put('player/update_position') ``` --- ## 2️⃣ 注释规范检查 ### 📄 文件头注释 **必须包含的信息:** ```typescript /** * 文件功能描述 * * 功能描述: * - 主要功能点1 * - 主要功能点2 * - 主要功能点3 * * 职责分离: * - 职责描述1 * - 职责描述2 * * 最近修改: * - 2024-01-07: 代码规范优化 - 修复命名规范问题 (修改者: 张三) * - 2024-01-06: 功能新增 - 添加用户验证功能 (修改者: 李四) * * @author 原始作者名称 * @version 1.0.1 * @since 2024-01-01 * @lastModified 2024-01-07 */ ``` ### 🏛️ 类注释 **必须包含的信息:** ```typescript /** * 类功能描述 * * 职责: * - 主要职责1 * - 主要职责2 * * 主要方法: * - method1() - 方法1功能 * - method2() - 方法2功能 * * 使用场景: * - 场景描述 */ @Injectable() export class ExampleService { // 类实现 } ``` ### 🔧 方法注释(三级标准) **必须包含的信息:** ```typescript /** * 用户登录验证 * * 业务逻辑: * 1. 验证用户名或邮箱格式 * 2. 查找用户记录 * 3. 验证密码哈希值 * 4. 检查用户状态是否允许登录 * 5. 记录登录日志 * 6. 返回认证结果 * * @param loginRequest 登录请求数据 * @returns 认证结果,包含用户信息和认证状态 * @throws UnauthorizedException 用户名或密码错误时 * @throws ForbiddenException 用户状态不允许登录时 * * @example * ```typescript * const result = await loginService.validateUser({ * identifier: 'user@example.com', * password: 'password123' * }); * ``` */ async validateUser(loginRequest: LoginRequest): Promise { // 实现代码 } ``` ### 📝 修改记录规范 **修改类型定义:** - `代码规范优化` - 命名规范、注释规范、代码清理等 - `功能新增` - 添加新的功能或方法 - `功能修改` - 修改现有功能的实现 - `Bug修复` - 修复代码缺陷 - `性能优化` - 提升代码性能 - `重构` - 代码结构调整但功能不变 **格式要求:** ```typescript /** * 最近修改: * - 2024-01-07: 代码规范优化 - 清理未使用的导入 (修改者: 张三) * - 2024-01-06: Bug修复 - 修复邮箱验证逻辑错误 (修改者: 李四) * - 2024-01-05: 功能新增 - 添加用户验证码登录功能 (修改者: 王五) * * @version 1.0.1 * @lastModified 2024-01-07 */ ``` **作者字段处理规范:** - **保留原则**:@author字段中的人名必须保留,不得随意修改 - **AI标识替换**:只有当@author字段包含AI标识(如kiro、ChatGPT、Claude、AI等)时,才可以替换为实际的修改者名称 - **判断标准**: - ✅ 可以替换:`@author kiro` → `@author 张三` - ✅ 可以替换:`@author ChatGPT` → `@author 李四` - ❌ 不可替换:`@author 王五` → 必须保留为 `@author 王五` - ❌ 不可替换:`@author John Smith` → 必须保留为 `@author John Smith` **修改记录更新要求:** - **必须添加**:每次修改文件后,必须在"最近修改"部分添加新的修改记录 - **信息完整**:包含修改日期、修改类型、修改内容、修改者姓名 - **时间更新**:只有真正修改了文件内容时才更新@lastModified字段,仅检查不修改内容时不更新日期 - **版本递增**:根据修改类型适当递增版本号 **版本号递增规则:** - 代码规范优化、Bug修复 → 修订版本 +1 (1.0.0 → 1.0.1) - 功能新增、功能修改 → 次版本 +1 (1.0.1 → 1.1.0) - 重构、架构变更 → 主版本 +1 (1.1.0 → 2.0.0) --- ## 3️⃣ 代码质量检查 ### 🧹 导入清理 **清理未使用的导入:** ```typescript // ✅ 正确:只导入使用的模块 import { Injectable, NotFoundException } from '@nestjs/common'; import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; import { Server } from 'socket.io'; // ❌ 错误:导入未使用的模块 import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; import { User, Admin } from './user.entity'; import * as crypto from 'crypto'; // 未使用 import { RedisService } from '../redis/redis.service'; // 未使用 ``` **游戏服务器特殊导入检查:** ```typescript // 检查双模式服务导入 import { UsersService } from './users.service'; import { UsersMemoryService } from './users-memory.service'; // 确保两个都被使用 // 检查WebSocket相关导入 import { Server, Socket } from 'socket.io'; // 确保Socket类型被使用 import { WsException } from '@nestjs/websockets'; // 确保异常处理被使用 ``` ### 📊 常量定义检查 ```typescript // ✅ 正确:使用全大写+下划线 const SALT_ROUNDS = 10; const MAX_LOGIN_ATTEMPTS = 5; const DEFAULT_PAGE_SIZE = 20; // ❌ 错误:使用小驼峰 const saltRounds = 10; const maxLoginAttempts = 5; ``` ### 🗑️ 未使用代码清理 ```typescript // ❌ 需要删除:未使用的私有方法 private generateVerificationCode(): string { // 如果这个方法没有被调用,应该删除 } // ❌ 需要删除:未使用的变量 const unusedVariable = 'test'; ``` ### 🚫 TODO项处理 **强制要求:最终文件不能包含TODO项** ```typescript // ❌ 错误:包含TODO项的代码 async getUserProfile(id: string): Promise { // TODO: 实现用户档案查询 throw new Error('Not implemented'); } // ❌ 游戏服务器常见TODO(需要处理) async sendSmsVerification(phone: string): Promise { // TODO: 集成短信服务提供商 throw new Error('SMS service not implemented'); } async cleanupOldPositions(): Promise { // TODO: 实现位置历史数据清理 console.log('Position cleanup not implemented'); } // ✅ 正确:真正实现功能 async getUserProfile(id: string): Promise { const profile = await this.userProfileRepository.findOne({ where: { userId: id } }); if (!profile) { throw new NotFoundException('用户档案不存在'); } return profile; } // ✅ 正确:游戏服务器实现示例 async broadcastPositionUpdate(userId: string, position: Position): Promise { const room = await this.getRoomByUserId(userId); this.server.to(room.id).emit('position-update', { userId, position, timestamp: Date.now() }); // 记录位置历史(如果需要) await this.savePositionHistory(userId, position); } ``` **游戏服务器TODO处理优先级:** - **高优先级**:实时通信功能、用户认证、数据持久化 - **中优先级**:性能优化、监控告警、数据清理 - **低优先级**:辅助功能、统计分析、第三方集成 **TODO处理原则:** - **真正实现**:如果功能需要,必须提供完整的实现 - **删除代码**:如果功能不需要,删除相关方法和接口 - **分阶段实现**:如果功能复杂,可以分多个版本实现,但每个版本都不能有TODO - **文档说明**:如果某些功能暂不实现,在README中说明原因和计划 ### 📏 方法长度检查 ```typescript // ✅ 正确:方法长度合理(建议不超过50行) async createUser(userData: CreateUserDto): Promise { // 简洁的实现 } // ❌ 错误:方法过长,需要拆分 async complexMethod() { // 超过50行的复杂逻辑,应该拆分成多个小方法 } ``` --- ## 4️⃣ 架构分层检查 ### 🏗️ 架构层级识别 **项目采用分层架构:** ``` src/ ├── core/ # Core层:技术实现层 │ ├── db/ # 数据访问 │ ├── redis/ # 缓存服务 │ └── utils/ # 工具服务 ├── business/ # Business层:业务逻辑层 │ ├── auth/ # 认证业务 │ ├── users/ # 用户业务 │ └── admin/ # 管理业务 └── common/ # 公共层:通用组件 ``` ### 🔧 Core层规范 **职责:专注技术实现,不包含业务逻辑** #### 命名规范 - **检查范围**:仅检查当前执行检查的文件夹,不考虑其他同层功能模块 - **业务支撑模块**:专门为特定业务功能提供技术支撑,使用`_core`后缀(如`location_broadcast_core`、`admin_core`) - **通用工具模块**:提供可复用的数据访问或基础技术服务,不使用`_core`后缀(如`user_profiles`、`redis`、`logger`) **游戏服务器Core层特殊模块:** ```typescript ✅ 正确示例: src/core/location_broadcast_core/ # 专门为位置广播业务提供技术支撑 src/core/admin_core/ # 专门为管理员业务提供技术支撑 src/core/zulip_core/ # 专门为Zulip集成提供技术支撑 src/core/login_core/ # 专门为登录认证提供技术支撑 src/core/security_core/ # 专门为安全功能提供技术支撑 src/core/db/user_profiles/ # 通用的用户档案数据访问服务 src/core/redis/ # 通用的Redis技术封装 src/core/utils/logger/ # 通用的日志工具服务 ❌ 错误示例: src/core/location_broadcast/ # 应该是location_broadcast_core src/core/db/user_profiles_core/ # 应该是user_profiles(通用工具) src/core/redis_core/ # 应该是redis(通用工具) ``` **判断流程:** ``` 1. 模块是否专门为某个特定业务功能服务? ├─ 是 → 检查模块名称是否体现业务领域 │ ├─ 是 → 使用 _core 后缀 (如: location_broadcast_core) │ └─ 否 → 重新设计模块职责 └─ 否 → 模块是否提供通用的技术服务? ├─ 是 → 不使用 _core 后缀 (如: user_profiles, redis) └─ 否 → 重新评估模块定位 2. 实际案例判断: - user_profiles: 通用的用户档案数据访问 → 不使用后缀 ✓ - location_broadcast_core: 专门为位置广播业务服务 → 使用_core后缀 ✓ - redis: 通用的缓存技术服务 → 不使用后缀 ✓ - user_auth_core: 专门为用户认证业务服务 → 使用_core后缀 ✓ ``` ```typescript ✅ 正确示例: 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/location_broadcast/ # 应该是location_broadcast_core src/core/db/user_profiles_core/ # 应该是user_profiles(通用工具) src/core/redis_core/ # 应该是redis(通用工具) ``` #### 技术实现示例 ```typescript // ✅ 正确:Core层专注技术实现 @Injectable() export class LocationBroadcastCoreService { /** * 广播位置更新到指定房间 * * 技术实现: * 1. 验证WebSocket连接状态 * 2. 序列化位置数据 * 3. 通过Socket.IO广播消息 * 4. 记录广播性能指标 * 5. 处理广播异常和重试 */ async broadcastToRoom(roomId: string, data: PositionData): Promise { // 专注WebSocket技术实现细节 const room = this.server.sockets.adapter.rooms.get(roomId); if (!room) { throw new NotFoundException(`Room ${roomId} not found`); } this.server.to(roomId).emit('position-update', data); this.metricsService.recordBroadcast(roomId, data.userId); } } // ❌ 错误:Core层包含业务逻辑 @Injectable() export class LocationBroadcastCoreService { async broadcastUserPosition(userId: string, position: Position): Promise { // 错误:包含了用户权限检查的业务概念 const user = await this.userService.findById(userId); if (user.status !== UserStatus.ACTIVE) { throw new ForbiddenException('用户状态不允许位置广播'); } } } ``` #### 依赖关系 - ✅ 允许:导入其他Core层模块 - ✅ 允许:导入第三方技术库 - ✅ 允许:导入Node.js内置模块 - ❌ 禁止:导入Business层模块 - ❌ 禁止:包含具体业务概念的命名 ### 💼 Business层规范 **职责:专注业务逻辑实现,不关心底层技术细节** #### 业务逻辑完备性 ```typescript // ✅ 正确:完整的业务逻辑 @Injectable() export class UserBusinessService { /** * 用户注册业务流程 * * 业务逻辑: * 1. 验证用户信息完整性 * 2. 检查用户名/邮箱是否已存在 * 3. 验证邮箱格式和域名白名单 * 4. 生成用户唯一标识 * 5. 设置默认用户权限 * 6. 发送欢迎邮件 * 7. 记录注册日志 * 8. 返回注册结果 */ async registerUser(registerData: RegisterUserDto): Promise { // 完整的业务逻辑实现 } } // ❌ 错误:业务逻辑不完整 @Injectable() export class UserBusinessService { async registerUser(registerData: RegisterUserDto): Promise { // 只是简单调用数据库保存,缺少业务验证和流程 return this.userRepository.save(registerData); } } ``` #### 依赖关系 - ✅ 允许:导入对应的Core层业务支撑模块 - ✅ 允许:导入Core层通用工具模块 - ✅ 允许:导入其他Business层模块(谨慎使用) - ✅ 允许:导入第三方业务库 - ❌ 禁止:直接导入底层技术实现(如数据库连接、Redis客户端等) - ❌ 禁止:包含技术实现细节 #### 正确的分层实现 ```typescript // ✅ 正确:Business层调用Core层服务 @Injectable() export class UserBusinessService { constructor( private readonly userCoreService: UserCoreService, private readonly cacheService: CacheService, private readonly emailService: EmailService, ) {} async createUser(userData: CreateUserDto): Promise { // 业务验证 await this.validateUserBusinessRules(userData); // 调用Core层服务 const user = await this.userCoreService.create(userData); await this.cacheService.set(`user:${user.id}`, user); await this.emailService.sendWelcomeEmail(user.email); return user; } } ``` ### 🔍 常见架构违规 #### Business层违规示例 ```typescript // ❌ 错误:Business层包含技术实现细节 @Injectable() export class UserBusinessService { async createUser(userData: CreateUserDto): Promise { // 违规:直接操作Redis连接 const redis = new Redis({ host: 'localhost', port: 6379 }); await redis.set(`user:${userData.id}`, JSON.stringify(userData)); // 违规:直接写SQL语句 const sql = 'INSERT INTO users (name, email) VALUES (?, ?)'; await this.database.query(sql, [userData.name, userData.email]); } } ``` #### Core层违规示例 ```typescript // ❌ 错误:Core层包含业务逻辑 @Injectable() export class DatabaseService { async saveUser(userData: CreateUserDto): Promise { // 违规:包含用户注册的业务验证 if (userData.age < 18) { throw new BadRequestException('用户年龄必须大于18岁'); } // 违规:包含业务规则 if (userData.email.endsWith('@competitor.com')) { throw new ForbiddenException('不允许竞争对手注册'); } } } ``` --- ## 5️⃣ 测试覆盖检查 ### 📋 测试文件存在性 **规则:每个Service、Controller、Gateway都必须有对应的测试文件** **⚠️ 游戏服务器测试要求(重要):** 以下类型需要测试文件: - ✅ **Service类**:文件名包含`.service.ts`的业务逻辑类 - ✅ **Controller类**:文件名包含`.controller.ts`的控制器类 - ✅ **Gateway类**:文件名包含`.gateway.ts`的WebSocket网关类 - ✅ **Guard类**:文件名包含`.guard.ts`的守卫类(游戏服务器安全重要) - ✅ **Interceptor类**:文件名包含`.interceptor.ts`的拦截器类(日志监控重要) - ✅ **Middleware类**:文件名包含`.middleware.ts`的中间件类(性能监控重要) **❌ 以下类型不需要测试文件:** - ❌ **DTO类**:数据传输对象(`.dto.ts`)不需要测试文件 - ❌ **Interface文件**:接口定义(`.interface.ts`)不需要测试文件 - ❌ **简单Utils工具类**:简单工具函数(`.utils.ts`)不需要测试文件 - ❌ **Config文件**:配置文件(`.config.ts`)不需要测试文件 - ❌ **Constants文件**:常量定义(`.constants.ts`)不需要测试文件 **游戏服务器特殊测试要求:** ```typescript // ✅ 必须有测试的文件类型 src/business/location-broadcast/location-broadcast.gateway.ts src/business/location-broadcast/location-broadcast.gateway.spec.ts src/core/security-core/websocket-auth.guard.ts src/core/security-core/websocket-auth.guard.spec.ts src/business/admin/performance-monitor.middleware.ts src/business/admin/performance-monitor.middleware.spec.ts // ❌ 不需要测试的文件类型 src/business/location-broadcast/dto/position-update.dto.ts # DTO不需要测试 src/core/location-broadcast-core/position.interface.ts # 接口不需要测试 src/business/admin/admin.constants.ts # 常量不需要测试 ``` **测试文件位置规范(重要):** - ✅ **正确位置**:测试文件必须与对应源文件放在同一目录 - ❌ **错误位置**:测试文件放在单独的tests/、test/、spec/、__tests__/等文件夹中 ```typescript // ✅ 正确:测试文件与源文件同目录 src/core/db/users/users.service.ts src/core/db/users/users.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中的所有公共方法** ```typescript // 示例Service @Injectable() export class UserService { async createUser(userData: CreateUserDto): Promise { } async findUserById(id: string): Promise { } async updateUser(id: string, updateData: UpdateUserDto): Promise { } async deleteUser(id: string): Promise { } async findUsersByStatus(status: UserStatus): Promise { } } // ✅ 正确:完整的测试覆盖 describe('UserService', () => { // 每个公共方法都有对应的测试 describe('createUser', () => { it('should create user successfully', () => { }); it('should throw error when email already exists', () => { }); it('should throw error when required fields missing', () => { }); }); describe('findUserById', () => { it('should return user when found', () => { }); it('should throw NotFoundException when user not found', () => { }); it('should throw error when id is invalid', () => { }); }); // ... 其他方法的测试 }); ``` ### 🧪 测试场景真实性 **要求:每个方法必须测试正常情况、异常情况和边界情况** ```typescript // ✅ 正确:游戏服务器完整测试场景 describe('LocationBroadcastGateway', () => { describe('handleConnection', () => { // 正常情况 it('should accept valid WebSocket connection with JWT token', async () => { const mockSocket = createMockSocket({ token: validJwtToken }); const result = await gateway.handleConnection(mockSocket); expect(result).toBeTruthy(); expect(mockSocket.join).toHaveBeenCalledWith(expectedRoomId); }); // 异常情况 it('should reject connection with invalid JWT token', async () => { const mockSocket = createMockSocket({ token: 'invalid-token' }); expect(() => gateway.handleConnection(mockSocket)).toThrow(WsException); }); // 边界情况 it('should handle connection when room is at capacity limit', async () => { const mockSocket = createMockSocket({ token: validJwtToken }); jest.spyOn(gateway, 'getRoomMemberCount').mockResolvedValue(MAX_ROOM_CAPACITY); expect(() => gateway.handleConnection(mockSocket)) .toThrow(new WsException('房间已满')); }); }); describe('handlePositionUpdate', () => { // 实时通信测试 it('should broadcast position to all room members', async () => { const positionData = { x: 100, y: 200, timestamp: Date.now() }; await gateway.handlePositionUpdate(mockSocket, positionData); expect(mockServer.to).toHaveBeenCalledWith(roomId); expect(mockServer.emit).toHaveBeenCalledWith('position-update', { userId: mockSocket.userId, position: positionData }); }); // 数据验证测试 it('should validate position data format', async () => { const invalidPosition = { x: 'invalid', y: 200 }; expect(() => gateway.handlePositionUpdate(mockSocket, invalidPosition)) .toThrow(WsException); }); }); }); // ✅ 双模式服务测试 describe('UsersService vs UsersMemoryService', () => { it('should have identical behavior for user creation', async () => { const userData = { name: 'Test User', email: 'test@example.com' }; const dbResult = await usersService.create(userData); const memoryResult = await usersMemoryService.create(userData); expect(dbResult).toMatchObject(memoryResult); }); }); ``` ### 🏗️ 测试代码质量 **要求:测试代码必须清晰、可维护、真实有效** ```typescript // ✅ 正确:游戏服务器高质量测试代码 describe('LocationBroadcastGateway', () => { let gateway: LocationBroadcastGateway; let mockServer: jest.Mocked; let mockLocationService: jest.Mocked; beforeEach(async () => { const mockServer = { to: jest.fn().mockReturnThis(), emit: jest.fn(), sockets: { adapter: { rooms: new Map() } } }; const mockLocationService = { broadcastToRoom: jest.fn(), validatePosition: jest.fn(), getRoomMembers: jest.fn() }; const module: TestingModule = await Test.createTestingModule({ providers: [ LocationBroadcastGateway, { provide: 'SERVER', useValue: mockServer }, { provide: LocationBroadcastCoreService, useValue: mockLocationService }, ], }).compile(); gateway = module.get(LocationBroadcastGateway); mockServer = module.get('SERVER'); mockLocationService = module.get(LocationBroadcastCoreService); }); afterEach(() => { jest.clearAllMocks(); }); describe('handlePositionUpdate', () => { it('should broadcast valid position update to room members', async () => { // Arrange const mockSocket = createMockSocket({ userId: 'user123', roomId: 'room456' }); const positionData = { x: 100, y: 200, timestamp: Date.now() }; mockLocationService.validatePosition.mockResolvedValue(true); mockLocationService.getRoomMembers.mockResolvedValue(['user123', 'user456']); // Act await gateway.handlePositionUpdate(mockSocket, positionData); // Assert expect(mockLocationService.validatePosition).toHaveBeenCalledWith(positionData); expect(mockServer.to).toHaveBeenCalledWith('room456'); expect(mockServer.emit).toHaveBeenCalledWith('position-update', { userId: 'user123', position: positionData, timestamp: expect.any(Number) }); }); }); }); // ✅ 属性测试示例(管理员模块) describe('AdminService Properties', () => { it('should handle any valid user status update', () => { fc.assert(fc.property( fc.integer({ min: 1, max: 1000000 }), // userId fc.constantFrom(...Object.values(UserStatus)), // status async (userId, status) => { // 属性:任何有效的用户状态更新都应该成功或抛出明确的异常 try { const result = await adminService.updateUserStatus(userId, status); expect(result).toBeDefined(); expect(result.status).toBe(status); } catch (error) { // 如果抛出异常,应该是已知的业务异常 expect(error).toBeInstanceOf(NotFoundException || BadRequestException); } } )); }); }); ``` ### 🔗 集成测试 **要求:复杂Service需要集成测试文件(.integration.spec.ts)** ```typescript // ✅ 正确:游戏服务器集成测试 src/core/location_broadcast_core/location_broadcast_core.service.ts src/core/location_broadcast_core/location_broadcast_core.service.spec.ts # 单元测试 src/core/location_broadcast_core/location_broadcast_core.integration.spec.ts # 集成测试 src/business/zulip/zulip.service.ts src/business/zulip/zulip.service.spec.ts # 单元测试 src/business/zulip/zulip_integration.e2e.spec.ts # E2E测试 ``` ### ⚡ 测试执行 **游戏服务器推荐的测试命令:** ```bash # 单元测试(排除集成测试和E2E测试) npm run test:unit # 等价于: jest --testPathPattern=spec.ts --testPathIgnorePatterns="integration.spec.ts|e2e.spec.ts" # 集成测试 jest --testPathPattern=integration.spec.ts # E2E测试(需要设置环境变量) npm run test:e2e # 等价于: cross-env RUN_E2E_TESTS=true jest --testPathPattern=e2e.spec.ts # 属性测试(管理员模块) jest --testPathPattern=property.spec.ts # 性能测试(WebSocket相关) jest --testPathPattern=perf.spec.ts # 全部测试 npm run test:all # 带覆盖率的测试执行 npm run test:cov ``` --- ## 6️⃣ 功能文档生成 ### 📚 README文档结构 **要求:每个功能模块文件夹都必须有README.md文档** #### 1. 模块概述 ```markdown # [模块名称] [中文描述] [模块名称] 是 [一段话总结文件夹的整体功能和作用,说明其在项目中的定位和价值]。 ``` #### 2. 对外提供的接口 ```markdown ## 用户数据操作 ### create() 创建新用户记录,支持数据验证和唯一性检查。 ### findByEmail() 根据邮箱地址查询用户,用于登录验证和账户找回。 ### updateUserStatus() 更新用户状态,支持激活、禁用、待验证等状态切换。 ``` #### 2.1 API接口列表(如适用) **如果business模块开放了可访问的API,必须在此处列出:** ```markdown ## 对外API接口 ### POST /api/auth/login 用户登录接口,支持用户名/邮箱/手机号多种方式登录。 ### GET /api/users/:id 根据用户ID获取用户详细信息。 ### PUT /api/users/:id/status 更新指定用户的状态(激活/禁用/待验证)。 ### DELETE /api/users/:id 删除指定用户账户及相关数据。 ### GET /api/users/search 根据条件搜索用户,支持邮箱、用户名、状态等筛选。 ## WebSocket事件接口 ### 'connection' 客户端建立WebSocket连接,需要提供JWT认证token。 ### 'position_update' 接收客户端位置更新,广播给房间内其他用户。 - 输入: `{ x: number, y: number, timestamp: number }` - 输出: 广播给房间成员 ### 'join_room' 用户加入游戏房间,建立实时通信连接。 - 输入: `{ roomId: string }` - 输出: `{ success: boolean, members: string[] }` ### 'chat_message' 处理聊天消息,支持Zulip集成和消息过滤。 - 输入: `{ message: string, roomId: string }` - 输出: 广播给房间成员或转发到Zulip ### 'disconnect' 客户端断开连接,清理相关资源和通知其他用户。 ``` #### 3. 使用的项目内部依赖 ```markdown ## 使用的项目内部依赖 ### UserStatus (来自 business/user-mgmt/enums/user-status.enum) 用户状态枚举,定义用户的激活、禁用、待验证等状态值。 ### CreateUserDto (本模块) 用户创建数据传输对象,提供完整的数据验证规则和类型定义。 ### LoggerService (来自 core/utils/logger) 日志服务,用于记录用户操作和系统事件。 ``` #### 4. 核心特性 ```markdown ## 核心特性 ### 双存储模式支持 - 数据库模式:使用TypeORM连接MySQL,适用于生产环境 - 内存模式:使用Map存储,适用于开发测试和故障降级 - 动态模块配置:通过UsersModule.forDatabase()和forMemory()灵活切换 - 自动检测:根据环境变量自动选择存储模式 ### 实时通信能力 - WebSocket支持:基于Socket.IO的实时双向通信 - 房间管理:支持用户加入/离开游戏房间 - 位置广播:实时广播用户位置更新给房间成员 - 连接管理:自动处理连接断开和重连机制 ### 数据完整性保障 - 唯一性约束检查:用户名、邮箱、手机号、GitHub ID - 数据验证:使用class-validator进行输入验证 - 事务支持:批量操作支持回滚机制 - 双模式一致性:确保内存模式和数据库模式行为一致 ### 性能优化与监控 - 查询优化:使用索引和查询缓存 - 批量操作:支持批量创建和更新 - 内存缓存:热点数据缓存机制 - 性能监控:WebSocket连接数、消息处理延迟等指标 - 属性测试:使用fast-check进行随机化测试 ### 第三方集成 - Zulip集成:支持与Zulip聊天系统的消息同步 - 邮件服务:用户注册验证和通知 - Redis缓存:支持Redis和文件存储双模式 - JWT认证:完整的用户认证和授权体系 ``` #### 5. 潜在风险 ```markdown ## 潜在风险 ### 内存模式数据丢失风险 - 内存存储在应用重启后数据会丢失 - 不适用于生产环境的持久化需求 - 建议仅在开发测试环境使用 - 缓解措施:提供数据导出/导入功能 ### WebSocket连接管理风险 - 大量并发连接可能导致内存泄漏 - 网络不稳定时连接频繁断开重连 - 房间成员过多时广播性能下降 - 缓解措施:连接数限制、心跳检测、分片广播 ### 实时通信性能风险 - 高频位置更新可能导致服务器压力 - 消息广播延迟影响游戏体验 - WebSocket消息丢失或重复 - 缓解措施:消息限流、优先级队列、消息确认机制 ### 双模式一致性风险 - 内存模式和数据库模式行为可能不一致 - 模式切换时数据同步问题 - 测试覆盖不完整导致隐藏差异 - 缓解措施:统一接口抽象、完整的对比测试 ### 第三方集成风险 - Zulip服务不可用时影响聊天功能 - 邮件服务故障影响用户注册 - Redis连接失败时缓存降级 - 缓解措施:服务降级、重试机制、监控告警 ### 并发操作风险 - 内存模式的ID生成锁机制相对简单 - 高并发场景可能存在性能瓶颈 - 位置更新冲突和数据竞争 - 建议在生产环境使用数据库模式和分布式锁 ### 数据一致性风险 - 跨模块操作时可能存在数据不一致 - WebSocket连接状态与用户状态不同步 - 需要注意事务边界的设计 - 建议使用分布式事务或补偿机制 ### 安全风险 - WebSocket连接缺少足够的认证验证 - 用户位置信息泄露风险 - 管理员权限过度集中 - 缓解措施:JWT认证、数据脱敏、权限细分 ``` ### 📝 文档质量要求 #### 内容质量标准 - **准确性**:所有信息必须与代码实现一致 - **完整性**:覆盖所有公共接口和重要功能 - **简洁性**:每个说明控制在一句话内,突出核心要点 - **实用性**:提供对开发者有价值的信息和建议 #### 语言表达规范 - 使用中文进行描述,专业术语可保留英文 - 语言简洁明了,避免冗长的句子 - 统一术语使用,保持前后一致 - 避免主观评价,客观描述功能和特性 --- ## 🛠️ 实用工具和技巧 ### 📋 检查清单 #### 命名规范检查清单 - [ ] 文件名使用snake_case(下划线分隔) - [ ] 变量和函数使用camelCase(小驼峰) - [ ] 类和接口使用PascalCase(大驼峰) - [ ] 常量使用SCREAMING_SNAKE_CASE(全大写+下划线) - [ ] 路由使用kebab-case(短横线分隔) - [ ] 避免过度嵌套的文件夹结构 - [ ] Core层业务支撑模块使用_core后缀,通用工具模块不使用后缀 #### 注释规范检查清单 - [ ] 文件头注释包含功能描述、职责分离、修改记录 - [ ] 类注释包含职责、主要方法、使用场景 - [ ] 方法注释包含业务逻辑、参数说明、返回值、异常、示例 - [ ] 修改记录使用正确的日期和修改者信息 - [ ] 版本号按规则递增 - [ ] @author字段正确处理(AI标识替换为实际作者) #### 代码质量检查清单 - [ ] 清理所有未使用的导入 - [ ] 清理所有未使用的变量和方法 - [ ] 常量使用正确的命名规范 - [ ] 方法长度控制在合理范围内(建议不超过50行) - [ ] 避免代码重复 - [ ] 处理所有TODO项(实现功能或删除代码) #### 架构分层检查清单 - [ ] Core层专注技术实现,不包含业务逻辑 - [ ] Business层专注业务逻辑,不包含技术实现细节 - [ ] 依赖关系符合分层架构要求 - [ ] 模块职责清晰,边界明确 #### 测试覆盖检查清单 - [ ] 每个Service都有对应的.spec.ts测试文件 - [ ] 所有公共方法都有测试覆盖 - [ ] 测试覆盖正常情况、异常情况、边界情况 - [ ] 测试代码质量高,真实有效 - [ ] 复杂Service提供集成测试 - [ ] 测试能够成功执行 #### 功能文档检查清单 - [ ] 每个功能模块都有README.md文档 - [ ] 文档包含模块概述、对外接口、内部依赖、核心特性、潜在风险 - [ ] 所有公共接口都有准确的功能描述 - [ ] 如果是business模块且开放了API,必须列出所有API接口及功能说明 - [ ] 文档内容与代码实现一致 - [ ] 语言表达简洁明了 ### 🔧 常用命令 #### 测试相关命令 ```bash # 游戏服务器测试命令 npm run test:unit # 单元测试 npm run test:cov # 测试覆盖率 npm run test:e2e # E2E测试 npm run test:all # 全部测试 # Jest特定测试类型 jest --testPathPattern=property.spec.ts # 属性测试 jest --testPathPattern=integration.spec.ts # 集成测试 jest --testPathPattern=perf.spec.ts # 性能测试 # WebSocket测试(需要启动服务) npm run dev & # 后台启动开发服务器 npm run test:e2e # 运行E2E测试 ``` #### 代码检查命令 ```bash # TypeScript类型检查 npx tsc --noEmit # ESLint代码检查 npx eslint src/**/*.ts # Prettier代码格式化 npx prettier --write src/**/*.ts ``` ### 🚨 常见错误和解决方案 #### 命名规范常见错误 1. **短横线命名错误(不符合项目规范)** - 错误:`admin-operation-log.service.ts` - 正确:`admin_operation_log.service.ts` - 解决:统一使用下划线分隔,保持项目一致性 2. **游戏服务器特殊文件命名错误** - 错误:`locationBroadcast.gateway.ts` - 正确:`location_broadcast.gateway.ts` - 错误:`websocketAuth.guard.ts` - 正确:`websocket_auth.guard.ts` 3. **常量命名错误** - 错误:`const saltRounds = 10;` - 正确:`const SALT_ROUNDS = 10;` - 解决:常量使用全大写+下划线 #### 架构分层常见错误 1. **Business层包含技术实现** - 错误:直接操作数据库连接 - 正确:调用Core层服务 - 解决:通过依赖注入使用Core层服务 2. **Core层包含业务逻辑** - 错误:在数据层进行业务验证 - 正确:只处理技术实现 - 解决:将业务逻辑移到Business层 #### 测试覆盖常见错误 1. **WebSocket测试文件缺失** - 错误:Gateway没有对应的.spec.ts文件 - 解决:为每个Gateway创建完整的连接、消息处理测试 2. **双模式测试不完整** - 错误:只测试数据库模式,忽略内存模式 - 正确:确保两种模式行为一致性测试 - 解决:创建对比测试用例 3. **属性测试缺失** - 错误:管理员模块缺少随机化测试 - 正确:使用fast-check进行属性测试 - 解决:补充基于属性的测试用例 4. **实时通信测试场景不完整** - 错误:只测试正常连接,忽略异常断开 - 正确:测试连接、断开、重连、消息处理全流程 - 解决:补充WebSocket生命周期测试 --- ## 📈 最佳实践建议 ### 🎯 开发流程建议 1. **编码前**:明确模块职责和架构定位 2. **编码中**:遵循命名规范和注释规范 3. **编码后**:进行代码质量检查和测试覆盖 4. **提交前**:生成或更新功能文档 ### 🔄 持续改进 1. **定期检查**:建议每周进行一次全面的代码规范检查 2. **团队协作**:通过Code Review确保规范执行 3. **工具辅助**:使用ESLint、Prettier等工具自动化检查 4. **文档维护**:及时更新文档,保持与代码同步 ### 📊 质量指标 1. **命名规范达标率**:目标100% 2. **注释覆盖率**:文件头、类、公共方法100%覆盖 3. **测试覆盖率**:单元测试覆盖率>90% 4. **文档完整性**:每个功能模块都有README文档 --- ## 🤝 团队协作 ### 👥 角色职责 - **开发者**:遵循规范进行开发,自检代码质量 - **Code Reviewer**:检查代码是否符合规范要求 - **架构师**:制定和维护架构分层规范 - **测试工程师**:确保测试覆盖率和测试质量 ### 📋 Review检查点 1. **命名规范**:文件、变量、函数、类的命名是否符合规范 2. **注释完整性**:文件头、类、方法注释是否完整准确 3. **代码质量**:是否有未使用的代码,常量定义是否规范 4. **架构合规性**:是否符合分层架构要求 5. **测试覆盖**:是否有对应的测试文件和完整的测试用例 6. **文档同步**:README文档是否与代码实现一致 ### 🛡️ 质量保障 1. **自动化检查**:集成ESLint、Prettier、Jest等工具 2. **CI/CD集成**:在构建流程中加入代码规范检查 3. **定期审计**:定期进行代码规范审计和改进 4. **培训推广**:定期组织团队培训,提高规范意识 --- ## 📞 支持和反馈 如果在使用过程中遇到问题或有改进建议,请: 1. 查阅本文档的相关章节 2. 参考常见错误和解决方案 3. 向团队架构师或技术负责人咨询 4. 提交改进建议,持续优化规范 **记住:代码规范不是束缚,而是提高代码质量和团队协作效率的有力工具!** 🚀 --- ## 🎮 游戏服务器特殊优化建议 ### 🚀 实时通信优化 1. **WebSocket连接管理** - 实现连接池和心跳检测 - 设置合理的连接超时和重连机制 - 监控连接数量和消息处理延迟 2. **消息广播优化** - 使用房间分片减少广播范围 - 实现消息优先级队列 - 添加消息确认和重试机制 3. **位置更新优化** - 实现位置更新频率限制 - 使用差分更新减少数据传输 - 添加位置验证防止作弊 ### 🔄 双模式架构优化 1. **模式切换优化** - 提供平滑的模式切换机制 - 实现数据迁移和同步工具 - 添加模式状态监控 2. **一致性保障** - 统一接口抽象层 - 完整的行为对比测试 - 自动化一致性检查 3. **性能对比** - 定期进行性能基准测试 - 监控两种模式的资源使用 - 优化内存模式的并发处理 ### 🧪 测试策略优化 1. **属性测试应用** - 管理员模块使用fast-check - 随机化用户状态变更测试 - 边界条件自动发现 2. **集成测试重点** - WebSocket连接生命周期 - 双模式服务一致性 - 第三方服务集成 3. **E2E测试场景** - 完整的用户游戏流程 - 多用户实时交互 - 异常恢复和降级 ### 📊 监控和告警 1. **关键指标监控** - WebSocket连接数和延迟 - 位置更新频率和处理时间 - 内存使用和GC频率 - 第三方服务可用性 2. **告警策略** - 连接数超过阈值 - 消息处理延迟过高 - 服务降级和故障转移 - 数据一致性检查失败