From 15548ebb52dc82bf9a5640c0e169e25778b765d6 Mon Sep 17 00:00:00 2001 From: moyin <2443444649@qq.com> Date: Wed, 24 Dec 2025 20:38:40 +0800 Subject: [PATCH] =?UTF-8?q?docs=EF=BC=9A=E6=B7=BB=E5=8A=A0API=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 详细记录认证相关API接口规范 - 包含请求参数、响应格式和错误代码说明 - 提供完整的使用示例和测试用例 - 涵盖登录、注册、验证码等核心功能接口 --- docs/api-documentation.md | 2116 +++++++++++++++++++++++++++++++++++++ 1 file changed, 2116 insertions(+) create mode 100644 docs/api-documentation.md diff --git a/docs/api-documentation.md b/docs/api-documentation.md new file mode 100644 index 0000000..3d9b792 --- /dev/null +++ b/docs/api-documentation.md @@ -0,0 +1,2116 @@ +# Pixel Game Server API接口文档 + +## 概述 + +本文档描述了像素游戏服务器的完整API接口,包括用户认证、管理员后台、应用状态等功能。 + +**基础URL**: `http://localhost:3000` +**API文档地址**: `http://localhost:3000/api-docs` +**项目名称**: Pixel Game Server +**版本**: 1.0.0 + +## 通用响应格式 + +所有API接口都遵循统一的响应格式: + +```json +{ + "success": boolean, + "data": object | null, + "message": string, + "error_code": string | null +} +``` + +### 字段说明 + +- `success`: 请求是否成功 +- `data`: 响应数据(成功时返回) +- `message`: 响应消息 +- `error_code`: 错误代码(失败时返回) + +## 接口分类 + +### 1. 应用状态接口 (App) +- `GET /` - 获取应用状态 + +### 2. 用户认证接口 (Auth) +- `POST /auth/login` - 用户登录 +- `POST /auth/register` - 用户注册 +- `POST /auth/github` - GitHub OAuth登录 +- `POST /auth/forgot-password` - 发送密码重置验证码 +- `POST /auth/reset-password` - 重置密码 +- `PUT /auth/change-password` - 修改密码 +- `POST /auth/send-email-verification` - 发送邮箱验证码 +- `POST /auth/verify-email` - 验证邮箱验证码 +- `POST /auth/resend-email-verification` - 重新发送邮箱验证码 +- `POST /auth/debug-verification-code` - 调试验证码信息(开发环境) +- `POST /auth/debug-clear-throttle` - 清除限流记录(开发环境) + +### 3. 管理员接口 (Admin) +- `POST /admin/auth/login` - 管理员登录 +- `GET /admin/users` - 获取用户列表 +- `GET /admin/users/:id` - 获取用户详情 +- `POST /admin/users/:id/reset-password` - 重置用户密码 +- `GET /admin/logs/runtime` - 获取运行日志 +- `GET /admin/logs/archive` - 下载日志压缩包 + +### 4. 用户管理接口 (User Management) +- `PUT /admin/users/:id/status` - 修改用户状态 +- `POST /admin/users/batch-status` - 批量修改用户状态 +- `GET /admin/users/status-stats` - 获取用户状态统计 + +## 接口列表 + +### 应用状态接口 + +#### 1. 获取应用状态 + +**接口地址**: `GET /` + +**功能描述**: 返回应用的基本运行状态信息,用于健康检查和监控 + +#### 请求参数 + +无 + +#### 响应示例 + +**成功响应** (200): +```json +{ + "service": "Pixel Game Server", + "version": "1.0.0", + "status": "running", + "timestamp": "2025-12-23T10:00:00.000Z", + "uptime": 3600, + "environment": "development", + "storage_mode": "memory" +} +``` + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| service | string | 服务名称 | +| version | string | 服务版本 | +| status | string | 运行状态 (running/starting/stopping/error) | +| timestamp | string | 当前时间戳 | +| uptime | number | 运行时间(秒) | +| environment | string | 运行环境 (development/production/test) | +| storage_mode | string | 存储模式 (database/memory) | + +### 用户认证接口 + +#### 1. 用户登录 + +**接口地址**: `POST /auth/login` + +**功能描述**: 用户登录,支持用户名、邮箱或手机号登录 + +#### 请求参数 + +```json +{ + "identifier": "testuser", + "password": "password123" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| identifier | string | 是 | 登录标识符,支持用户名、邮箱或手机号 | +| password | string | 是 | 用户密码 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "user": { + "id": "1", + "username": "testuser", + "nickname": "测试用户", + "email": "test@example.com", + "phone": "+8613800138000", + "avatar_url": "https://example.com/avatar.jpg", + "role": 1, + "created_at": "2025-12-17T10:00:00.000Z" + }, + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "is_new_user": false, + "message": "登录成功" + }, + "message": "登录成功" +} +``` + +**失败响应** (401): +```json +{ + "success": false, + "message": "用户名或密码错误", + "error_code": "LOGIN_FAILED" +} +``` + +#### 2. 用户注册 + +**接口地址**: `POST /auth/register` + +**功能描述**: 创建新用户账户 + +**重要说明**: +- 如果提供邮箱,必须先调用发送验证码接口获取验证码 +- 验证码验证失败会返回400状态码,而不是201 +- 注册成功返回201,失败返回400 + +#### 请求参数 + +```json +{ + "username": "testuser", + "password": "password123", + "nickname": "测试用户", + "email": "test@example.com", + "email_verification_code": "123456" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| username | string | 是 | 用户名,只能包含字母、数字和下划线,长度1-50字符 | +| password | string | 是 | 密码,必须包含字母和数字,长度8-128字符 | +| nickname | string | 是 | 用户昵称,长度1-50字符 | +| email | string | 否 | 邮箱地址(如果提供,必须先获取验证码) | +| phone | string | 否 | 手机号码 | +| email_verification_code | string | 条件必填 | 邮箱验证码,提供邮箱时必填 | + +#### 响应示例 + +**成功响应** (201): +```json +{ + "success": true, + "data": { + "user": { + "id": "2", + "username": "testuser", + "nickname": "测试用户", + "email": "test@example.com", + "phone": "+8613800138000", + "avatar_url": null, + "role": 1, + "created_at": "2025-12-17T10:00:00.000Z" + }, + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "is_new_user": true, + "message": "注册成功" + }, + "message": "注册成功" +} +``` + +**失败响应** (400): +```json +{ + "success": false, + "message": "提供邮箱时必须提供邮箱验证码", + "error_code": "REGISTER_FAILED" +} +``` + +**频率限制响应** (429): +```json +{ + "success": false, + "message": "注册请求过于频繁,请5分钟后再试", + "error_code": "TOO_MANY_REQUESTS", + "throttle_info": { + "limit": 10, + "window_seconds": 300, + "current_requests": 10, + "reset_time": "2025-12-24T11:26:41.136Z" + } +} +``` + +#### 3. GitHub OAuth登录 + +**接口地址**: `POST /auth/github` + +**功能描述**: 使用GitHub账户登录或注册 + +#### 请求参数 + +```json +{ + "github_id": "12345678", + "username": "octocat", + "nickname": "The Octocat", + "email": "octocat@github.com", + "avatar_url": "https://github.com/images/error/octocat_happy.gif" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| github_id | string | 是 | GitHub用户ID | +| username | string | 是 | GitHub用户名 | +| nickname | string | 是 | GitHub显示名称 | +| email | string | 否 | GitHub邮箱地址 | +| avatar_url | string | 否 | GitHub头像URL | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "user": { + "id": "3", + "username": "octocat", + "nickname": "The Octocat", + "email": "octocat@github.com", + "phone": null, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "role": 1, + "created_at": "2025-12-17T10:00:00.000Z" + }, + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "is_new_user": true, + "message": "GitHub账户绑定成功" + }, + "message": "GitHub账户绑定成功" +} +``` + +#### 4. 发送密码重置验证码 + +**接口地址**: `POST /auth/forgot-password` + +**功能描述**: 向用户邮箱或手机发送密码重置验证码 + +#### 请求参数 + +```json +{ + "identifier": "test@example.com" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| identifier | string | 是 | 邮箱或手机号 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "verification_code": "123456" + }, + "message": "验证码已发送,请查收" +} +``` + +**注意**: 实际应用中不应返回验证码,这里仅用于演示。 + +#### 5. 重置密码 + +**接口地址**: `POST /auth/reset-password` + +**功能描述**: 使用验证码重置用户密码 + +#### 请求参数 + +```json +{ + "identifier": "test@example.com", + "verification_code": "123456", + "new_password": "newpassword123" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| identifier | string | 是 | 邮箱或手机号 | +| verification_code | string | 是 | 6位数字验证码 | +| new_password | string | 是 | 新密码,必须包含字母和数字,长度8-128字符 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "message": "密码重置成功" +} +``` + +#### 6. 修改密码 + +**接口地址**: `PUT /auth/change-password` + +**功能描述**: 用户修改自己的密码(需要提供旧密码) + +#### 请求参数 + +```json +{ + "user_id": "1", + "old_password": "oldpassword123", + "new_password": "newpassword123" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| user_id | string | 是 | 用户ID(实际应用中应从JWT令牌中获取) | +| old_password | string | 是 | 当前密码 | +| new_password | string | 是 | 新密码,必须包含字母和数字,长度8-128字符 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "message": "密码修改成功" +} +``` + +#### 7. 发送邮箱验证码 + +**接口地址**: `POST /auth/send-email-verification` + +**功能描述**: 向指定邮箱发送验证码,用于注册时的邮箱验证 + +#### 请求参数 + +```json +{ + "email": "test@example.com" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| email | string | 是 | 邮箱地址 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "verification_code": "123456", + "is_test_mode": false + }, + "message": "验证码已发送,请查收" +} +``` + +**测试模式响应** (206): +```json +{ + "success": false, + "data": { + "verification_code": "059174", + "is_test_mode": true + }, + "message": "⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。", + "error_code": "TEST_MODE_ONLY" +} +``` + +#### 8. 验证邮箱验证码 + +**接口地址**: `POST /auth/verify-email` + +**功能描述**: 使用验证码验证邮箱 + +#### 请求参数 + +```json +{ + "email": "test@example.com", + "verification_code": "123456" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| email | string | 是 | 邮箱地址 | +| verification_code | string | 是 | 6位数字验证码 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "message": "邮箱验证成功" +} +``` + +#### 9. 重新发送邮箱验证码 + +**接口地址**: `POST /auth/resend-email-verification` + +**功能描述**: 重新向指定邮箱发送验证码 + +#### 请求参数 + +```json +{ + "email": "test@example.com" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| email | string | 是 | 邮箱地址 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "verification_code": "123456", + "is_test_mode": false + }, + "message": "验证码已重新发送,请查收" +} +``` + +#### 10. 调试验证码信息 + +**接口地址**: `POST /auth/debug-verification-code` + +**功能描述**: 获取验证码的详细调试信息(仅开发环境使用) + +#### 请求参数 + +```json +{ + "email": "test@example.com" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| email | string | 是 | 邮箱地址 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "email": "test@example.com", + "verification_code": "123456", + "expires_at": "2025-12-23T10:15:00.000Z", + "created_at": "2025-12-23T10:00:00.000Z" + }, + "message": "调试信息获取成功" +} +``` + +#### 11. 清除限流记录 + +**接口地址**: `POST /auth/debug-clear-throttle` + +**功能描述**: 清除所有限流记录(仅开发环境使用) + +**注意**: 此接口用于开发测试,清除所有IP的频率限制记录 + +#### 请求参数 + +无 + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "message": "限流记录已清除" +} +``` + +### 管理员接口 + +**注意**:所有管理员接口都需要在 Header 中携带 `Authorization: Bearer `,且用户角色必须为管理员 (role=9)。 + +#### 1. 管理员登录 + +**接口地址**: `POST /admin/auth/login` + +**功能描述**: 管理员登录,仅允许 role=9 的账户登录后台 + +#### 请求参数 + +```json +{ + "identifier": "admin", + "password": "Admin123456" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| identifier | string | 是 | 登录标识符(用户名/邮箱/手机号) | +| password | string | 是 | 密码 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "admin": { + "id": "1", + "username": "admin", + "nickname": "管理员", + "role": 9 + }, + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expires_at": 1766102400000 + }, + "message": "管理员登录成功" +} +``` + +#### 2. 获取用户列表 + +**接口地址**: `GET /admin/users` + +**功能描述**: 分页获取所有注册用户列表 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| limit | number | 否 | 每页数量,默认100 | +| offset | number | 否 | 偏移量,默认0 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "users": [ + { + "id": "1", + "username": "user1", + "nickname": "小明", + "email": "user1@example.com", + "email_verified": false, + "phone": "+8613800138000", + "avatar_url": "https://example.com/avatar.png", + "role": 1, + "created_at": "2025-12-19T00:00:00.000Z", + "updated_at": "2025-12-19T00:00:00.000Z" + } + ], + "limit": 100, + "offset": 0 + }, + "message": "用户列表获取成功" +} +``` + +#### 3. 获取用户详情 + +**接口地址**: `GET /admin/users/:id` + +**功能描述**: 获取指定用户的详细信息 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | string | 是 | 用户ID(路径参数) | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "user": { + "id": "1", + "username": "user1", + "nickname": "小明", + "email": "user1@example.com", + "email_verified": false, + "phone": "+8613800138000", + "avatar_url": "https://example.com/avatar.png", + "role": 1, + "created_at": "2025-12-19T00:00:00.000Z", + "updated_at": "2025-12-19T00:00:00.000Z" + } + }, + "message": "用户信息获取成功" +} +``` + +#### 4. 重置用户密码 + +**接口地址**: `POST /admin/users/:id/reset-password` + +**功能描述**: 管理员强制重置指定用户的密码 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | string | 是 | 用户ID(路径参数) | +| new_password | string | 是 | 新密码(至少8位,包含字母和数字) | + +```json +{ + "new_password": "NewPass1234" +} +``` + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "message": "密码重置成功" +} +``` + +#### 5. 获取运行日志 + +**接口地址**: `GET /admin/logs/runtime` + +**功能描述**: 从 logs/ 目录读取最近的日志行 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| lines | number | 否 | 返回行数,默认200,最大2000 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "file": "dev.log", + "updated_at": "2025-12-19T19:10:15.000Z", + "lines": [ + "[2025-12-19 19:10:15] INFO: Server started", + "[2025-12-19 19:10:16] INFO: Database connected" + ] + }, + "message": "运行日志获取成功" +} +``` + +#### 6. 下载日志压缩包 + +**接口地址**: `GET /admin/logs/archive` + +**功能描述**: 将 logs/ 目录打包为 tar.gz 并下载 + +#### 请求参数 + +无 + +#### 响应示例 + +**成功响应** (200): +- Content-Type: `application/gzip` +- Content-Disposition: `attachment; filename="logs-2025-12-23T10-00-00-000Z.tar.gz"` +- 返回二进制流(tar.gz 文件) + +### 用户管理接口 + +**注意**:所有用户管理接口都需要管理员权限,需要在 Header 中携带 `Authorization: Bearer `。 + +#### 1. 修改用户状态 + +**接口地址**: `PUT /admin/users/:id/status` + +**功能描述**: 管理员修改指定用户的账户状态,支持激活、锁定、禁用等操作 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | string | 是 | 用户ID(路径参数) | +| status | string | 是 | 用户状态枚举值 | +| reason | string | 否 | 修改原因 | + +```json +{ + "status": "locked", + "reason": "用户违反社区规定" +} +``` + +**用户状态枚举值:** +- `active` - 正常状态,可以正常使用 +- `inactive` - 未激活,需要邮箱验证 +- `locked` - 已锁定,临时禁用 +- `banned` - 已禁用,管理员操作 +- `deleted` - 已删除,软删除状态 +- `pending` - 待审核,需要管理员审核 + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "user": { + "id": "1", + "username": "testuser", + "nickname": "测试用户", + "status": "locked", + "status_description": "已锁定", + "updated_at": "2025-12-24T10:00:00.000Z" + }, + "reason": "用户违反社区规定" + }, + "message": "用户状态修改成功" +} +``` + +#### 2. 批量修改用户状态 + +**接口地址**: `POST /admin/users/batch-status` + +**功能描述**: 管理员批量修改多个用户的账户状态 + +#### 请求参数 + +```json +{ + "user_ids": ["1", "2", "3"], + "status": "locked", + "reason": "批量处理违规用户" +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| user_ids | array | 是 | 用户ID列表(1-100个) | +| status | string | 是 | 用户状态枚举值 | +| reason | string | 否 | 批量修改原因 | + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "result": { + "success_users": [ + { + "id": "1", + "username": "user1", + "nickname": "用户1", + "status": "locked", + "status_description": "已锁定", + "updated_at": "2025-12-24T10:00:00.000Z" + } + ], + "failed_users": [ + { + "user_id": "999", + "error": "用户不存在" + } + ], + "success_count": 1, + "failed_count": 1, + "total_count": 2 + }, + "reason": "批量处理违规用户" + }, + "message": "批量用户状态修改完成,成功:1,失败:1" +} +``` + +#### 3. 获取用户状态统计 + +**接口地址**: `GET /admin/users/status-stats` + +**功能描述**: 获取各种用户状态的数量统计信息 + +#### 请求参数 + +无 + +#### 响应示例 + +**成功响应** (200): +```json +{ + "success": true, + "data": { + "stats": { + "active": 1250, + "inactive": 45, + "locked": 12, + "banned": 8, + "deleted": 3, + "pending": 15, + "total": 1333 + }, + "timestamp": "2025-12-24T10:00:00.000Z" + }, + "message": "用户状态统计获取成功" +} + +## 测试指南和边界条件 + +### 🧪 **前端测试建议** + +为了确保前端应用的稳定性,建议对以下场景进行全面测试: + +#### **1. 用户认证测试** + +##### **注册功能测试** +```javascript +// 正常注册流程 +const testNormalRegister = async () => { + // 1. 发送邮箱验证码 + const codeResponse = await sendEmailVerification('test@example.com'); + + // 2. 使用验证码注册 + const registerResponse = await register({ + username: 'testuser123', + password: 'Test123456', + nickname: '测试用户', + email: 'test@example.com', + email_verification_code: codeResponse.data.verification_code + }); + + expect(registerResponse.success).toBe(true); + expect(registerResponse.data.user.username).toBe('testuser123'); + expect(registerResponse.data.access_token).toBeDefined(); +}; + +// 边界条件测试 +const testRegisterEdgeCases = async () => { + // 密码强度测试 + await expectError(register({ + username: 'test', + password: '123', // 太短 + nickname: '测试' + }), 'REGISTER_FAILED'); + + // 用户名重复测试 + await expectError(register({ + username: 'existinguser', // 已存在 + password: 'Test123456', + nickname: '测试' + }), 'REGISTER_FAILED'); + + // 邮箱验证码错误测试 + await expectError(register({ + username: 'newuser', + password: 'Test123456', + nickname: '测试', + email: 'test@example.com', + email_verification_code: '000000' // 错误验证码 + }), 'REGISTER_FAILED'); +}; +``` + +##### **登录功能测试** +```javascript +// 多种登录方式测试 +const testLoginMethods = async () => { + // 用户名登录 + await testLogin('testuser', 'password123'); + + // 邮箱登录 + await testLogin('test@example.com', 'password123'); + + // 手机号登录(如果支持) + await testLogin('+8613800138000', 'password123'); +}; + +// 登录失败场景测试 +const testLoginFailures = async () => { + // 用户不存在 + await expectError(login('nonexistent', 'password'), 'LOGIN_FAILED'); + + // 密码错误 + await expectError(login('testuser', 'wrongpassword'), 'LOGIN_FAILED'); + + // 账户被锁定 + await expectError(login('lockeduser', 'password'), 'LOGIN_FAILED'); +}; +``` + +#### **2. 验证码功能测试** + +##### **验证码生成和验证** +```javascript +// 验证码频率限制测试 +const testVerificationRateLimit = async () => { + const email = 'test@example.com'; + + // 第一次发送 - 应该成功 + const response1 = await sendEmailVerification(email); + expect(response1.success).toBe(true); + + // 立即再次发送 - 应该被限制 + await expectError( + sendEmailVerification(email), + 'TOO_MANY_REQUESTS', + 429 + ); + + // 等待冷却时间后再次发送 + await sleep(60000); // 等待1分钟 + const response2 = await sendEmailVerification(email); + expect(response2.success).toBe(true); +}; + +// 验证码尝试次数限制测试 +const testVerificationAttempts = async () => { + const email = 'test@example.com'; + const response = await sendEmailVerification(email); + const correctCode = response.data.verification_code; + + // 错误尝试3次 + for (let i = 0; i < 3; i++) { + await expectError( + verifyEmail(email, '000000'), + 'VERIFICATION_FAILED' + ); + } + + // 第4次尝试,即使验证码正确也应该失败 + await expectError( + verifyEmail(email, correctCode), + 'VERIFICATION_FAILED' + ); +}; +``` + +#### **3. 管理员功能测试** + +##### **权限验证测试** +```javascript +// 管理员登录测试 +const testAdminLogin = async () => { + // 正确的管理员凭据 + const response = await adminLogin('admin', 'Admin123456'); + expect(response.success).toBe(true); + expect(response.data.admin.role).toBe(9); + + // 普通用户尝试管理员登录 + await expectError( + adminLogin('normaluser', 'password'), + 'ADMIN_LOGIN_FAILED', + 403 + ); +}; + +// 管理员操作权限测试 +const testAdminOperations = async () => { + const adminToken = await getAdminToken(); + + // 有效token的操作 + const users = await getUserList(adminToken); + expect(users.success).toBe(true); + + // 无效token的操作 + await expectError( + getUserList('invalid_token'), + 'UNAUTHORIZED', + 401 + ); + + // 普通用户token的操作 + const userToken = await getUserToken(); + await expectError( + getUserList(userToken), + 'FORBIDDEN', + 403 + ); +}; +``` + +#### **4. 用户状态管理测试** + +##### **状态变更测试** +```javascript +// 用户状态修改测试 +const testUserStatusUpdate = async () => { + const adminToken = await getAdminToken(); + const userId = '1'; + + // 锁定用户 + const lockResponse = await updateUserStatus(adminToken, userId, { + status: 'locked', + reason: '违反社区规定' + }); + expect(lockResponse.success).toBe(true); + expect(lockResponse.data.user.status).toBe('locked'); + + // 被锁定用户尝试登录 + await expectError( + login('lockeduser', 'password'), + 'LOGIN_FAILED', + 403 + ); + + // 恢复用户状态 + await updateUserStatus(adminToken, userId, { + status: 'active', + reason: '恢复正常' + }); +}; + +// 批量状态修改测试 +const testBatchStatusUpdate = async () => { + const adminToken = await getAdminToken(); + + const response = await batchUpdateUserStatus(adminToken, { + user_ids: ['1', '2', '999'], // 包含不存在的用户ID + status: 'locked', + reason: '批量处理' + }); + + expect(response.success).toBe(true); + expect(response.data.result.success_count).toBe(2); + expect(response.data.result.failed_count).toBe(1); + expect(response.data.result.failed_users[0].user_id).toBe('999'); +}; +``` + +#### **5. 安全功能测试** + +##### **频率限制测试** +```javascript +// 登录频率限制测试 +const testLoginRateLimit = async () => { + // 快速连续登录尝试 + for (let i = 0; i < 3; i++) { + try { + await login('testuser', 'wrongpassword'); + } catch (error) { + if (error.status === 429) { + expect(error.message).toContain('Too Many Requests'); + break; + } + } + } +}; + +// 维护模式测试 +const testMaintenanceMode = async () => { + // 模拟维护模式开启 + // 所有请求都应该返回503 + await expectError( + getAppStatus(), + 'SERVICE_UNAVAILABLE', + 503 + ); +}; +``` + +#### **6. 错误处理测试** + +##### **网络错误处理** +```javascript +// 超时处理测试 +const testTimeout = async () => { + // 模拟长时间操作 + await expectError( + slowOperation(), + 'REQUEST_TIMEOUT', + 408 + ); +}; + +// 内容类型验证测试 +const testContentType = async () => { + // 错误的Content-Type + await expectError( + fetch('/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'text/plain' }, + body: 'invalid data' + }), + 'UNSUPPORTED_MEDIA_TYPE', + 415 + ); +}; +``` + +### 📋 **测试检查清单** + +#### **功能测试** +- [ ] 用户注册(正常流程) +- [ ] 用户注册(邮箱验证流程) +- [ ] 用户登录(用户名/邮箱/手机号) +- [ ] GitHub OAuth登录 +- [ ] 密码重置流程 +- [ ] 密码修改功能 +- [ ] 邮箱验证码发送和验证 +- [ ] 管理员登录 +- [ ] 用户列表查询 +- [ ] 用户详情查询 +- [ ] 用户密码重置(管理员) +- [ ] 用户状态管理 +- [ ] 批量用户状态修改 +- [ ] 用户状态统计 +- [ ] 运行日志查询 +- [ ] 日志文件下载 + +#### **边界条件测试** +- [ ] 密码强度验证(太短、太简单) +- [ ] 用户名格式验证(特殊字符、长度) +- [ ] 邮箱格式验证 +- [ ] 验证码格式验证(非6位数字) +- [ ] 用户名重复检查 +- [ ] 邮箱重复检查 +- [ ] 不存在用户的操作 +- [ ] 无效验证码验证 +- [ ] 过期验证码验证 +- [ ] 验证码尝试次数限制 + +#### **安全测试** +- [ ] 频率限制(登录、发送验证码) +- [ ] 权限验证(管理员接口) +- [ ] Token有效性验证 +- [ ] 用户状态检查(锁定、禁用用户登录) +- [ ] 维护模式功能 +- [ ] 内容类型验证 +- [ ] 请求超时处理 + +#### **错误处理测试** +- [ ] 网络连接错误 +- [ ] 服务器内部错误(500) +- [ ] 请求超时(408) +- [ ] 频率限制(429) +- [ ] 权限不足(403) +- [ ] 资源不存在(404) +- [ ] 参数验证错误(400) +- [ ] 维护模式(503) + +### 🔧 **测试工具推荐** + +#### **API测试工具** +- **Postman**: 手动API测试和文档 +- **Insomnia**: 轻量级API客户端 +- **curl**: 命令行测试 +- **HTTPie**: 用户友好的命令行工具 + +#### **自动化测试框架** +- **Jest**: JavaScript测试框架 +- **Cypress**: 端到端测试 +- **Playwright**: 现代Web测试 +- **Supertest**: Node.js HTTP测试 + +#### **测试脚本示例** +项目提供了现成的测试脚本: +- `test-api.ps1` - Windows PowerShell测试脚本 +- `test-api.sh` - Linux/macOS Bash测试脚本 + +运行测试脚本: +```bash +# Windows +.\test-api.ps1 + +# Linux/macOS +./test-api.sh + +# 自定义参数 +.\test-api.ps1 -BaseUrl "http://localhost:3000" -TestEmail "custom@example.com" +``` + +### 📊 **测试数据管理** + +#### **测试环境配置** +- **内存模式**: 数据重启后清空,适合快速测试 +- **数据库模式**: 数据持久化,适合完整功能测试 +- **测试模式**: 邮件不真实发送,验证码在响应中返回 + +#### **测试数据清理** +```javascript +// 清理测试数据 +const cleanupTestData = async () => { + // 删除测试用户 + await deleteTestUsers(); + + // 清理Redis验证码 + await clearVerificationCodes(); + + // 重置计数器 + await resetRateLimitCounters(); +}; +``` + +### ⚠️ **测试注意事项** + +1. **频率限制**: 测试时注意API频率限制,避免被限制 +2. **测试隔离**: 每个测试用例使用独立的测试数据 +3. **异步操作**: 注意验证码生成和验证的时序 +4. **错误恢复**: 测试失败后要清理测试数据 +5. **环境差异**: 开发、测试、生产环境的配置差异 +6. **数据一致性**: 并发测试时注意数据竞争条件 + +### 🚀 **性能测试建议** + +#### **负载测试场景** +- 并发用户注册 +- 高频验证码发送 +- 大量用户同时登录 +- 管理员批量操作 +- 日志文件下载 + +#### **性能指标** +- 响应时间 < 2秒(正常操作) +- 吞吐量 > 100 req/s +- 错误率 < 1% +- 内存使用稳定 +- CPU使用率 < 80% + +### **通用错误代码** + +| 错误代码 | HTTP状态码 | 说明 | 触发条件 | +|----------|------------|------|----------| +| LOGIN_FAILED | 401 | 登录失败 | 用户名不存在、密码错误、账户被锁定 | +| REGISTER_FAILED | 400/409 | 注册失败 | 用户名已存在、密码强度不足、验证码错误 | +| GITHUB_OAUTH_FAILED | 401 | GitHub OAuth失败 | GitHub认证信息无效 | +| SEND_CODE_FAILED | 400 | 发送验证码失败 | 邮箱格式错误、发送服务异常 | +| RESET_PASSWORD_FAILED | 400 | 重置密码失败 | 验证码无效、密码强度不足 | +| CHANGE_PASSWORD_FAILED | 400 | 修改密码失败 | 旧密码错误、新密码强度不足 | +| TEST_MODE_ONLY | 206 | 测试模式 | 邮件服务未配置,验证码未真实发送 | + +### **管理员相关错误代码** + +| 错误代码 | HTTP状态码 | 说明 | 触发条件 | +|----------|------------|------|----------| +| ADMIN_LOGIN_FAILED | 401/403 | 管理员登录失败 | 非管理员用户、凭据错误 | +| ADMIN_USERS_FAILED | 500 | 获取用户列表失败 | 数据库查询异常 | +| ADMIN_OPERATION_FAILED | 400/500 | 管理员操作失败 | 参数错误、系统异常 | + +### **用户状态相关错误代码** + +| 错误代码 | HTTP状态码 | 说明 | 触发条件 | +|----------|------------|------|----------| +| USER_STATUS_UPDATE_FAILED | 400/404 | 用户状态修改失败 | 用户不存在、状态值无效 | +| BATCH_USER_STATUS_UPDATE_FAILED | 400 | 批量用户状态修改失败 | 用户ID列表为空、状态值无效 | +| USER_STATUS_STATS_FAILED | 500 | 用户状态统计失败 | 数据库查询异常 | + +### **安全相关错误代码** + +| 错误代码 | HTTP状态码 | 说明 | 触发条件 | +|----------|------------|------|----------| +| SERVICE_UNAVAILABLE | 503 | 系统维护中 | 维护模式开启 | +| TOO_MANY_REQUESTS | 429 | 请求过于频繁 | 触发频率限制 | +| REQUEST_TIMEOUT | 408 | 请求超时 | 操作执行时间过长 | +| UNSUPPORTED_MEDIA_TYPE | 415 | 不支持的媒体类型 | Content-Type不正确 | +| UNAUTHORIZED | 401 | 未授权 | Token无效或过期 | +| FORBIDDEN | 403 | 权限不足 | 非管理员访问管理员接口 | + +### **验证码相关错误代码** + +| 错误代码 | HTTP状态码 | 说明 | 触发条件 | +|----------|------------|------|----------| +| VERIFICATION_CODE_EXPIRED | 400 | 验证码已过期 | 验证码超过有效期(5分钟) | +| VERIFICATION_CODE_INVALID | 400 | 验证码无效 | 验证码格式错误或不存在 | +| VERIFICATION_CODE_ATTEMPTS_EXCEEDED | 400 | 验证码尝试次数过多 | 错误尝试超过3次 | +| VERIFICATION_CODE_RATE_LIMITED | 429 | 验证码发送频率限制 | 1分钟内重复发送 | +| VERIFICATION_CODE_HOURLY_LIMIT | 429 | 验证码每小时限制 | 1小时内发送超过5次 | + +### **详细错误响应格式** + +#### **标准错误响应** +```json +{ + "success": false, + "message": "具体错误描述", + "error_code": "ERROR_CODE", + "timestamp": "2025-12-24T10:00:00.000Z" +} +``` + +#### **验证错误响应** +```json +{ + "success": false, + "message": "参数验证失败", + "error_code": "VALIDATION_FAILED", + "errors": [ + { + "field": "password", + "message": "密码长度至少8位" + }, + { + "field": "email", + "message": "邮箱格式不正确" + } + ] +} +``` + +#### **频率限制错误响应** +```json +{ + "success": false, + "message": "请求过于频繁,请稍后再试", + "error_code": "TOO_MANY_REQUESTS", + "retry_after": 60, + "limit_info": { + "limit": 5, + "remaining": 0, + "reset_time": "2025-12-24T10:01:00.000Z" + } +} +``` + +#### **维护模式错误响应** +```json +{ + "success": false, + "message": "系统正在维护中,请稍后再试", + "error_code": "SERVICE_UNAVAILABLE", + "maintenance_info": { + "start_time": "2025-12-24T10:00:00.000Z", + "estimated_end_time": "2025-12-24T12:00:00.000Z", + "retry_after": 1800, + "reason": "系统升级维护" + } +} +``` + +## 数据验证规则 + +### 用户名规则 +- 长度:1-50字符 +- 格式:只能包含字母、数字和下划线 +- 正则表达式:`^[a-zA-Z0-9_]+$` + +### 密码规则 +- 长度:8-128字符 +- 格式:必须包含字母和数字 +- 正则表达式:`^(?=.*[a-zA-Z])(?=.*\d)` + +### 验证码规则 +- 长度:6位数字 +- 正则表达式:`^\d{6}$` +- 有效期:通常为5-15分钟 + +### 邮箱规则 +- 格式:符合标准邮箱格式 +- 验证:支持邮箱验证码验证 + +### 管理员权限 +- 角色:role=9 为管理员 +- 认证:需要 JWT Token +- 权限:可管理所有用户数据 + +## 使用示例 + +### JavaScript/TypeScript 示例 + +```typescript +// 获取应用状态 +const statusResponse = await fetch('http://localhost:3000/'); +const statusData = await statusResponse.json(); +console.log('服务状态:', statusData.status); + +// 用户登录 +const loginResponse = await fetch('http://localhost:3000/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + identifier: 'testuser', + password: 'password123' + }) +}); + +const loginData = await loginResponse.json(); +if (loginData.success) { + const token = loginData.data.access_token; + // 保存token用于后续请求 + localStorage.setItem('token', token); +} + +// 用户注册(带邮箱验证) +// 1. 先发送邮箱验证码 +const sendCodeResponse = await fetch('http://localhost:3000/auth/send-email-verification', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email: 'newuser@example.com' + }) +}); + +// 2. 用户输入验证码后进行注册 +const registerResponse = await fetch('http://localhost:3000/auth/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + username: 'newuser', + password: 'password123', + nickname: '新用户', + email: 'newuser@example.com', + email_verification_code: '123456' + }) +}); + +// 管理员登录 +const adminLoginResponse = await fetch('http://localhost:3000/admin/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + identifier: 'admin', + password: 'Admin123456' + }) +}); + +const adminData = await adminLoginResponse.json(); +if (adminData.success) { + const adminToken = adminData.data.access_token; + + // 获取用户列表 + const usersResponse = await fetch('http://localhost:3000/admin/users?limit=10&offset=0', { + headers: { + 'Authorization': `Bearer ${adminToken}` + } + }); + + const usersData = await usersResponse.json(); + console.log('用户列表:', usersData.data.users); +} +``` + +### cURL 示例 + +```bash +# 获取应用状态 +curl -X GET http://localhost:3000/ + +# 用户登录 +curl -X POST http://localhost:3000/auth/login \ + -H "Content-Type: application/json" \ + -d '{ + "identifier": "testuser", + "password": "password123" + }' + +# 发送邮箱验证码 +curl -X POST http://localhost:3000/auth/send-email-verification \ + -H "Content-Type: application/json" \ + -d '{ + "email": "newuser@example.com" + }' + +# 用户注册 +curl -X POST http://localhost:3000/auth/register \ + -H "Content-Type: application/json" \ + -d '{ + "username": "newuser", + "password": "password123", + "nickname": "新用户", + "email": "newuser@example.com", + "email_verification_code": "123456" + }' + +# 管理员登录 +curl -X POST http://localhost:3000/admin/auth/login \ + -H "Content-Type: application/json" \ + -d '{ + "identifier": "admin", + "password": "Admin123456" + }' + +# 获取用户列表(需要管理员Token) +curl -X GET "http://localhost:3000/admin/users?limit=10&offset=0" \ + -H "Authorization: Bearer YOUR_ADMIN_TOKEN" + +# 重置用户密码 +curl -X POST http://localhost:3000/admin/users/1/reset-password \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \ + -d '{ + "new_password": "NewPass1234" + }' + +# 获取运行日志 +curl -X GET "http://localhost:3000/admin/logs/runtime?lines=100" \ + -H "Authorization: Bearer YOUR_ADMIN_TOKEN" + +# 下载日志压缩包 +curl -X GET http://localhost:3000/admin/logs/archive \ + -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \ + -o logs.tar.gz +``` + +## 安全特性 + +### 1. 频率限制 (Rate Limiting) + +系统实现了基于IP地址的频率限制,防止恶意攻击和滥用: + +#### 限制策略 + +| 接口类型 | 限制规则 | 时间窗口 | 说明 | +|----------|----------|----------|------| +| 登录接口 | 5次/分钟 | 60秒 | 防止暴力破解 | +| 注册接口 | 10次/5分钟 | 300秒 | 防止批量注册(开发环境已放宽) | +| 发送验证码 | 1次/分钟 | 60秒 | 防止验证码滥发 | +| 密码重置 | 3次/小时 | 3600秒 | 限制密码重置频率 | +| 管理员操作 | 10次/分钟 | 60秒 | 限制管理员操作频率 | +| 一般接口 | 30次/分钟 | 60秒 | 通用API限制 | 100次/分钟 | 60秒 | 防止接口滥用 | + +#### 响应示例 + +当触发频率限制时,返回 **429 Too Many Requests**: + +```json +{ + "success": false, + "message": "注册请求过于频繁,请5分钟后再试", + "error_code": "TOO_MANY_REQUESTS", + "throttle_info": { + "limit": 10, + "window_seconds": 300, + "current_requests": 10, + "reset_time": "2025-12-24T11:26:41.136Z" + } +} +``` + +**重要说明**: +- 频率限制基于IP地址 +- 超过限制后需要等待到重置时间才能再次请求 +- 开发环境下注册接口限制已放宽至10次/5分钟 + +### 2. 维护模式 (Maintenance Mode) + +系统支持维护模式,在系统升级或维护期间暂停服务: + +#### 启用方式 + +设置环境变量: +```bash +MAINTENANCE_MODE=true +MAINTENANCE_START_TIME=2025-12-24T10:00:00.000Z +MAINTENANCE_END_TIME=2025-12-24T12:00:00.000Z +MAINTENANCE_REASON=系统升级维护 +MAINTENANCE_RETRY_AFTER=1800 +``` + +#### 响应示例 + +维护模式下所有请求返回 **503 Service Unavailable**: + +```json +{ + "success": false, + "message": "系统正在维护中,请稍后再试", + "error_code": "SERVICE_UNAVAILABLE", + "maintenance_info": { + "start_time": "2025-12-24T10:00:00.000Z", + "estimated_end_time": "2025-12-24T12:00:00.000Z", + "retry_after": 1800, + "reason": "系统升级维护" + } +} +``` + +### 3. 内容类型验证 (Content Type Validation) + +系统验证POST/PUT请求的Content-Type头: + +#### 支持的内容类型 + +- `application/json` - JSON数据 +- `application/x-www-form-urlencoded` - 表单数据 +- `multipart/form-data` - 文件上传 + +#### 响应示例 + +不支持的内容类型返回 **415 Unsupported Media Type**: + +```json +{ + "statusCode": 415, + "message": "不支持的媒体类型", + "error": "Unsupported Media Type" +} +``` + +### 4. 请求超时控制 (Request Timeout) + +系统为不同类型的操作设置了超时限制: + +#### 超时配置 + +| 操作类型 | 超时时间 | 说明 | +|----------|----------|------| +| 普通操作 | 30秒 | 一般API请求 | +| 数据库查询 | 60秒 | 复杂查询操作 | +| 慢操作 | 120秒 | 批量处理等耗时操作 | + +#### 响应示例 + +请求超时返回 **408 Request Timeout**: + +```json +{ + "statusCode": 408, + "message": "请求超时", + "error": "Request Timeout" +} +``` + +### 5. 用户状态管理 (User Status Management) + +系统支持细粒度的用户状态控制: + +#### 用户状态枚举 + +| 状态值 | 状态名称 | 说明 | +|--------|----------|------| +| active | 正常 | 用户可以正常使用所有功能 | +| inactive | 未激活 | 新注册用户,需要邮箱验证 | +| locked | 锁定 | 临时锁定,可以解锁 | +| banned | 禁用 | 永久禁用,需要管理员处理 | +| deleted | 删除 | 软删除状态,数据保留 | +| pending | 待审核 | 需要管理员审核激活 | + +#### 状态检查 + +登录时系统会检查用户状态: + +- `active`: 正常登录 +- `inactive`: 提示需要邮箱验证 +- `locked`: 返回账户锁定错误 +- `banned`: 返回账户禁用错误 +- `deleted`: 返回账户不存在错误 +- `pending`: 返回账户待审核错误 + +### 6. 安全最佳实践 + +#### JWT Token 安全 + +- Token 有效期:8小时 +- 使用 HS256 算法签名 +- 包含用户ID、角色等关键信息 +- 建议在客户端安全存储 + +#### 密码安全 + +- 使用 bcrypt 加密存储 +- 支持密码强度验证 +- 不在日志中记录明文密码 +- 支持密码重置功能 + +#### API 安全 + +- 所有管理员接口需要身份验证 +- 支持跨域资源共享 (CORS) +- 实现请求日志记录 +- 敏感信息自动脱敏 + +## 注意事项 + +1. **安全性**: 实际应用中应使用HTTPS协议 +2. **令牌**: 示例中的access_token是JWT格式,需要妥善保存 +3. **验证码**: + - 实际应用中不应在响应中返回验证码 + - 测试模式下会在控制台显示验证码 + - 验证码有效期通常为5-15分钟 +4. **用户ID**: 修改密码接口中的user_id应从JWT令牌中获取,而不是从请求体中传递 +5. **错误处理**: 建议在客户端实现适当的错误处理和用户提示 +6. **限流**: 建议对登录、注册等接口实施限流策略 +7. **管理员权限**: + - 管理员接口需要 role=9 的用户权限 + - 需要在请求头中携带有效的JWT Token + - Token格式:`Authorization: Bearer ` +8. **存储模式**: + - 数据库模式:数据持久化存储在MySQL + - 内存模式:数据存储在内存中,重启后丢失 +9. **邮箱验证**: + - 注册时如果提供邮箱,需要先获取验证码 + - 支持重新发送验证码功能 + - 调试接口仅用于开发环境 + +## 常见测试场景 + +### 🔍 **前端开发者必测场景** + +#### **1. 用户注册完整流程** +```javascript +// 场景:新用户完整注册流程 +const testCompleteRegistration = async () => { + const email = 'newuser@example.com'; + + // Step 1: 发送邮箱验证码 + const codeResponse = await fetch('/auth/send-email-verification', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }) + }); + + expect(codeResponse.status).toBe(206); // 测试模式 + const codeData = await codeResponse.json(); + expect(codeData.success).toBe(false); + expect(codeData.error_code).toBe('TEST_MODE_ONLY'); + + // Step 2: 使用验证码注册 + const registerResponse = await fetch('/auth/register', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + username: 'newuser123', + password: 'SecurePass123', + nickname: '新用户', + email: email, + email_verification_code: codeData.data.verification_code + }) + }); + + expect(registerResponse.status).toBe(201); + const registerData = await registerResponse.json(); + expect(registerData.success).toBe(true); + expect(registerData.data.access_token).toBeDefined(); +}; +``` + +#### **2. 登录失败处理** +```javascript +// 场景:各种登录失败情况 +const testLoginFailures = async () => { + // 用户不存在 + const response1 = await fetch('/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + identifier: 'nonexistent', + password: 'password123' + }) + }); + + expect(response1.status).toBe(200); // 业务错误返回200 + const data1 = await response1.json(); + expect(data1.success).toBe(false); + expect(data1.error_code).toBe('LOGIN_FAILED'); + + // 密码错误 + const response2 = await fetch('/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + identifier: 'existinguser', + password: 'wrongpassword' + }) + }); + + expect(response2.status).toBe(200); + const data2 = await response2.json(); + expect(data2.success).toBe(false); + expect(data2.error_code).toBe('LOGIN_FAILED'); +}; +``` + +#### **3. 频率限制测试** +```javascript +// 场景:验证码发送频率限制 +const testRateLimit = async () => { + const email = 'test@example.com'; + + // 第一次发送 - 成功 + const response1 = await fetch('/auth/send-email-verification', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }) + }); + expect(response1.status).toBe(206); // 测试模式 + + // 立即再次发送 - 被限制 + const response2 = await fetch('/auth/send-email-verification', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }) + }); + expect(response2.status).toBe(429); + + const data2 = await response2.json(); + expect(data2.message).toContain('请等待'); +}; +``` + +#### **4. 管理员权限测试** +```javascript +// 场景:管理员权限验证 +const testAdminPermissions = async () => { + // 普通用户尝试访问管理员接口 + const userToken = 'user_token_here'; + + const response = await fetch('/admin/users', { + headers: { 'Authorization': `Bearer ${userToken}` } + }); + + expect(response.status).toBe(403); + + // 无效token访问 + const response2 = await fetch('/admin/users', { + headers: { 'Authorization': 'Bearer invalid_token' } + }); + + expect(response2.status).toBe(401); + + // 正确的管理员token + const adminToken = await getAdminToken(); + const response3 = await fetch('/admin/users', { + headers: { 'Authorization': `Bearer ${adminToken}` } + }); + + expect(response3.status).toBe(200); +}; +``` + +#### **5. 用户状态影响登录** +```javascript +// 场景:不同用户状态的登录测试 +const testUserStatusLogin = async () => { + // 正常用户登录 + const activeResponse = await login('activeuser', 'password'); + expect(activeResponse.success).toBe(true); + + // 锁定用户登录 + const lockedResponse = await login('lockeduser', 'password'); + expect(lockedResponse.success).toBe(false); + expect(lockedResponse.message).toContain('锁定'); + + // 禁用用户登录 + const bannedResponse = await login('banneduser', 'password'); + expect(bannedResponse.success).toBe(false); + expect(bannedResponse.message).toContain('禁用'); +}; +``` + +### 📝 **边界条件测试清单** + +#### **输入验证测试** +- [ ] 空字符串输入 +- [ ] 超长字符串输入(用户名>50字符) +- [ ] 特殊字符输入(SQL注入尝试) +- [ ] 无效邮箱格式 +- [ ] 弱密码(少于8位、纯数字、纯字母) +- [ ] 无效验证码格式(非6位数字) + +#### **状态边界测试** +- [ ] 验证码过期边界(5分钟) +- [ ] 验证码尝试次数边界(3次) +- [ ] 频率限制边界(1分钟、1小时) +- [ ] Token过期边界(8小时) +- [ ] 用户状态变更后的立即登录 + +#### **并发测试** +- [ ] 同时发送多个验证码请求 +- [ ] 同时使用相同验证码验证 +- [ ] 并发用户注册相同用户名 +- [ ] 并发管理员操作同一用户 + +### 🚨 **错误恢复测试** + +#### **网络异常处理** +```javascript +// 场景:网络中断恢复 +const testNetworkRecovery = async () => { + // 模拟网络中断 + mockNetworkError(); + + try { + await login('testuser', 'password'); + fail('应该抛出网络错误'); + } catch (error) { + expect(error.message).toContain('网络'); + } + + // 恢复网络 + restoreNetwork(); + + // 重试应该成功 + const response = await login('testuser', 'password'); + expect(response.success).toBe(true); +}; +``` + +#### **服务降级测试** +```javascript +// 场景:邮件服务不可用时的降级 +const testEmailServiceDegradation = async () => { + // 邮件服务不可用时,应该返回测试模式 + const response = await fetch('/auth/send-email-verification', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: 'test@example.com' }) + }); + + expect(response.status).toBe(206); + const data = await response.json(); + expect(data.error_code).toBe('TEST_MODE_ONLY'); + expect(data.data.is_test_mode).toBe(true); +}; +``` + +### 🔧 **自动化测试脚本** + +#### **快速冒烟测试** +```bash +#!/bin/bash +# 快速验证所有关键接口 + +BASE_URL="http://localhost:3000" + +echo "🚀 开始API冒烟测试..." + +# 1. 应用状态检查 +echo "1. 检查应用状态..." +curl -f "$BASE_URL/" > /dev/null || exit 1 + +# 2. 验证码发送 +echo "2. 测试验证码发送..." +RESPONSE=$(curl -s -X POST "$BASE_URL/auth/send-email-verification" \ + -H "Content-Type: application/json" \ + -d '{"email":"test@example.com"}') + +CODE=$(echo "$RESPONSE" | jq -r '.data.verification_code') +if [ "$CODE" = "null" ]; then + echo "❌ 验证码发送失败" + exit 1 +fi + +# 3. 用户注册 +echo "3. 测试用户注册..." +USERNAME="smoketest_$(date +%s)" +curl -f -X POST "$BASE_URL/auth/register" \ + -H "Content-Type: application/json" \ + -d "{\"username\":\"$USERNAME\",\"password\":\"Test123456\",\"nickname\":\"冒烟测试\",\"email\":\"test@example.com\",\"email_verification_code\":\"$CODE\"}" > /dev/null || exit 1 + +# 4. 用户登录 +echo "4. 测试用户登录..." +curl -f -X POST "$BASE_URL/auth/login" \ + -H "Content-Type: application/json" \ + -d "{\"identifier\":\"$USERNAME\",\"password\":\"Test123456\"}" > /dev/null || exit 1 + +echo "✅ 冒烟测试通过!" +``` + +#### **性能基准测试** +```bash +#!/bin/bash +# 简单的性能基准测试 + +echo "📊 开始性能基准测试..." + +# 并发登录测试 +echo "测试并发登录性能..." +ab -n 100 -c 10 -T 'application/json' -p login_data.json http://localhost:3000/auth/login + +# 验证码发送性能测试 +echo "测试验证码发送性能..." +ab -n 50 -c 5 -T 'application/json' -p email_data.json http://localhost:3000/auth/send-email-verification + +echo "📈 性能测试完成,请查看上述结果" +``` + +### 💡 **测试技巧和建议** + +#### **1. 测试数据管理** +- 使用时间戳生成唯一的测试用户名 +- 测试完成后清理测试数据 +- 使用专门的测试邮箱域名 + +#### **2. 异步操作处理** +- 验证码生成后立即使用,避免过期 +- 注意频率限制的时间窗口 +- 使用适当的等待时间 + +#### **3. 错误场景覆盖** +- 测试所有可能的HTTP状态码 +- 验证错误消息的准确性 +- 测试错误恢复机制 + +#### **4. 安全测试** +- 尝试SQL注入和XSS攻击 +- 测试权限绕过 +- 验证敏感信息不泄露 + +这些测试场景和边界条件将帮助前端开发者进行全面的API测试,确保应用的稳定性和安全性。 + +- **v1.0.0** (2025-12-24): + - **完整的API文档更新** + - 重新整理接口分类,将用户管理接口独立分类 + - 确保文档与实际运行的服务完全一致 + - 验证所有接口的请求参数和响应格式 + - **修复HTTP状态码问题**:所有接口现在根据业务结果返回正确状态码 + - **更新限流配置**:注册接口限制调整为10次/5分钟(开发环境) + - **应用状态接口** (1个) + - `GET /` - 获取应用状态 + - **用户认证接口** (11个) + - 用户登录、注册、GitHub OAuth + - 密码重置和修改功能 + - 邮箱验证相关接口 + - 调试验证码接口 + - **新增**:清除限流记录接口(开发环境) + - **管理员接口** (6个) + - 管理员登录和用户管理 + - 用户列表和详情查询 + - 密码重置功能 + - 日志管理和下载 + - **用户管理接口** (3个) + - 用户状态管理 (active/inactive/locked/banned/deleted/pending) + - 单个用户状态修改接口 + - 批量用户状态修改接口 + - 用户状态统计接口 + - **安全增强功能** + - 频率限制中间件 (Rate Limiting) - 已调整配置 + - 维护模式中间件 (Maintenance Mode) + - 内容类型验证中间件 (Content Type Validation) + - 请求超时拦截器 (Request Timeout) + - 用户状态检查和权限控制 + - **修复**:HTTP状态码现在正确反映业务执行结果 + - **总计接口数量**: 21个API接口 + - 完善错误代码和使用示例 + - 修复路由冲突问题 + - 确保文档与实际测试效果一致 + - **重要修复**:解决了业务失败但返回成功状态码的问题 +