forked from datawhale/whale-town-front
docs: 重新组织文档结构,按开发阶段分类
新的目录结构: 01-项目入门/ # 新人必读,项目基础 02-开发规范/ # 编码标准和规范 03-技术实现/ # 具体开发指导 04-高级开发/ # 进阶开发技巧 05-部署运维/ # 发布和部署 06-功能模块/ # 特定功能文档 新增导航文档: - docs/README.md - 完整的文档导航和使用指南 - 各目录下的README.md - 分类说明和使用指导 优化效果: - 开发者可以按阶段快速定位需要的文档 - 新人有清晰的学习路径 - 不同角色有针对性的文档推荐 - 提供了问题导向的快速查找功能
This commit is contained in:
963
docs/03-技术实现/API接口文档.md
Normal file
963
docs/03-技术实现/API接口文档.md
Normal file
@@ -0,0 +1,963 @@
|
||||
# Pixel Game Server API 文档
|
||||
|
||||
**版本**: 1.1.1
|
||||
**更新时间**: 2025-12-25
|
||||
|
||||
## 🚨 后端对前端的提示与注意点
|
||||
|
||||
### 重要提醒
|
||||
1. **邮箱冲突检测**: 发送邮箱验证码前会检查邮箱是否已被注册,已注册邮箱返回409状态码
|
||||
2. **HTTP状态码**: 所有接口根据业务结果返回正确状态码(409冲突、400参数错误、401认证失败等)
|
||||
3. **验证码有效期**: 所有验证码有效期为5分钟
|
||||
4. **频率限制**: 验证码发送限制1次/分钟,注册限制10次/5分钟
|
||||
5. **测试模式**: 开发环境下邮件服务返回206状态码,验证码在响应中返回
|
||||
6. **冷却时间自动清除**: 注册、密码重置、验证码登录成功后会自动清除验证码冷却时间,方便后续操作
|
||||
|
||||
### 错误处理规范
|
||||
- **409 Conflict**: 资源冲突(用户名、邮箱已存在)
|
||||
- **400 Bad Request**: 参数错误、验证码错误
|
||||
- **401 Unauthorized**: 认证失败、密码错误
|
||||
- **429 Too Many Requests**: 频率限制
|
||||
- **206 Partial Content**: 测试模式(验证码未真实发送)
|
||||
|
||||
### 前端开发建议
|
||||
1. 根据HTTP状态码进行错误处理,不要只依赖success字段
|
||||
2. 邮箱注册流程:先发送验证码 → 检查409冲突 → 使用验证码注册
|
||||
3. 测试模式下验证码在响应中返回,生产环境需用户查收邮件
|
||||
4. 实现重试机制处理429频率限制错误
|
||||
5. 注册/重置密码成功后,验证码冷却时间会自动清除,可立即发送新验证码
|
||||
|
||||
---
|
||||
|
||||
## 📋 API接口列表
|
||||
|
||||
### 应用状态接口
|
||||
- `GET /` - 获取应用状态
|
||||
|
||||
### 用户认证接口
|
||||
- `POST /auth/login` - 用户登录
|
||||
- `POST /auth/register` - 用户注册
|
||||
- `POST /auth/github` - GitHub OAuth登录
|
||||
- `POST /auth/verification-code-login` - 验证码登录
|
||||
- `POST /auth/send-login-verification-code` - 发送登录验证码
|
||||
- `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 /admin/auth/login` - 管理员登录
|
||||
- `GET /admin/users` - 获取用户列表
|
||||
- `GET /admin/users/:id` - 获取用户详情
|
||||
- `POST /admin/users/:id/reset-password` - 管理员重置用户密码
|
||||
- `GET /admin/logs/runtime` - 获取运行时日志
|
||||
- `GET /admin/logs/archive` - 获取归档日志
|
||||
|
||||
### 用户管理接口
|
||||
- `PUT /admin/users/:id/status` - 修改用户状态
|
||||
- `POST /admin/users/batch-status` - 批量修改用户状态
|
||||
- `GET /admin/users/status-stats` - 获取用户状态统计
|
||||
|
||||
---
|
||||
|
||||
## 🧪 API接口详细说明与测试用例
|
||||
### 1. 获取应用状态
|
||||
|
||||
**接口**: `GET /`
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"service": "Pixel Game Server",
|
||||
"version": "1.1.1",
|
||||
"status": "running",
|
||||
"timestamp": "2025-12-25T10:27:44.352Z",
|
||||
"uptime": 8,
|
||||
"environment": "development",
|
||||
"storage_mode": "database"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 用户登录
|
||||
|
||||
**接口**: `POST /auth/login`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"identifier": "testuser",
|
||||
"password": "password123"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "1",
|
||||
"username": "testuser",
|
||||
"nickname": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": null,
|
||||
"avatar_url": null,
|
||||
"role": 1,
|
||||
"created_at": "2025-12-17T10:00:00.000Z"
|
||||
},
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"is_new_user": false,
|
||||
"message": "登录成功"
|
||||
},
|
||||
"message": "登录成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 认证失败响应 (401)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户名、邮箱或手机号不存在",
|
||||
"error_code": "LOGIN_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
#### 密码错误响应 (401)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "密码错误",
|
||||
"error_code": "LOGIN_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 用户注册
|
||||
|
||||
**接口**: `POST /auth/register`
|
||||
|
||||
#### 请求体(无邮箱)
|
||||
```json
|
||||
{
|
||||
"username": "newuser",
|
||||
"password": "password123",
|
||||
"nickname": "新用户"
|
||||
}
|
||||
```
|
||||
|
||||
#### 请求体(带邮箱验证)
|
||||
```json
|
||||
{
|
||||
"username": "newuser",
|
||||
"password": "password123",
|
||||
"nickname": "新用户",
|
||||
"email": "newuser@example.com",
|
||||
"email_verification_code": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (201)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "2",
|
||||
"username": "newuser",
|
||||
"nickname": "新用户",
|
||||
"email": "newuser@example.com",
|
||||
"phone": null,
|
||||
"avatar_url": null,
|
||||
"role": 1,
|
||||
"created_at": "2025-12-17T10:00:00.000Z"
|
||||
},
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"is_new_user": true,
|
||||
"message": "注册成功"
|
||||
},
|
||||
"message": "注册成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户名冲突响应 (409)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户名已存在",
|
||||
"error_code": "REGISTER_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
#### 邮箱冲突响应 (409)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "邮箱已存在",
|
||||
"error_code": "REGISTER_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
#### 验证码错误响应 (400)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "验证码不存在或已过期",
|
||||
"error_code": "REGISTER_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 发送邮箱验证码
|
||||
|
||||
**接口**: `POST /auth/send-email-verification`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"email": "test@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200) - 生产环境
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"is_test_mode": false
|
||||
},
|
||||
"message": "验证码已发送,请查收邮件"
|
||||
}
|
||||
```
|
||||
|
||||
#### 测试模式响应 (206) - 开发环境
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"data": {
|
||||
"verification_code": "123456",
|
||||
"is_test_mode": true
|
||||
},
|
||||
"message": "⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。",
|
||||
"error_code": "TEST_MODE_ONLY"
|
||||
}
|
||||
```
|
||||
|
||||
#### 邮箱冲突响应 (409)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "邮箱已被注册,请使用其他邮箱或直接登录",
|
||||
"error_code": "SEND_EMAIL_VERIFICATION_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
#### 频率限制响应 (429)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "验证码发送过于频繁,请1分钟后再试",
|
||||
"error_code": "TOO_MANY_REQUESTS",
|
||||
"throttle_info": {
|
||||
"limit": 1,
|
||||
"window_seconds": 60,
|
||||
"current_requests": 1,
|
||||
"reset_time": "2025-12-25T10:07:37.056Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### 5. 验证码登录
|
||||
|
||||
**接口**: `POST /auth/verification-code-login`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"identifier": "test@example.com",
|
||||
"verification_code": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "1",
|
||||
"username": "testuser",
|
||||
"nickname": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": null,
|
||||
"avatar_url": null,
|
||||
"role": 1,
|
||||
"created_at": "2025-12-17T10:00:00.000Z"
|
||||
},
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"is_new_user": false,
|
||||
"message": "验证码登录成功"
|
||||
},
|
||||
"message": "验证码登录成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 验证码错误响应 (401)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "验证码验证失败",
|
||||
"error_code": "VERIFICATION_CODE_LOGIN_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户不存在响应 (404)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户不存在,请先注册账户",
|
||||
"error_code": "VERIFICATION_CODE_LOGIN_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 发送登录验证码
|
||||
|
||||
**接口**: `POST /auth/send-login-verification-code`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"identifier": "test@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200) - 生产环境
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"is_test_mode": false
|
||||
},
|
||||
"message": "验证码已发送,请查收"
|
||||
}
|
||||
```
|
||||
|
||||
#### 测试模式响应 (206) - 开发环境
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"data": {
|
||||
"verification_code": "654321",
|
||||
"is_test_mode": true
|
||||
},
|
||||
"message": "⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。",
|
||||
"error_code": "TEST_MODE_ONLY"
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户不存在响应 (404)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户不存在",
|
||||
"error_code": "SEND_LOGIN_CODE_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 发送密码重置验证码
|
||||
|
||||
**接口**: `POST /auth/forgot-password`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"identifier": "test@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200) - 生产环境
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"is_test_mode": false
|
||||
},
|
||||
"message": "验证码已发送,请查收"
|
||||
}
|
||||
```
|
||||
|
||||
#### 测试模式响应 (206) - 开发环境
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"data": {
|
||||
"verification_code": "789012",
|
||||
"is_test_mode": true
|
||||
},
|
||||
"message": "⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。",
|
||||
"error_code": "TEST_MODE_ONLY"
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户不存在响应 (404)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户不存在",
|
||||
"error_code": "SEND_CODE_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 重置密码
|
||||
|
||||
**接口**: `POST /auth/reset-password`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"identifier": "test@example.com",
|
||||
"verification_code": "789012",
|
||||
"new_password": "newpassword123"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "密码重置成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 验证码错误响应 (400)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "验证码验证失败",
|
||||
"error_code": "RESET_PASSWORD_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. 修改密码
|
||||
|
||||
**接口**: `PUT /auth/change-password`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"user_id": "1",
|
||||
"old_password": "oldpassword123",
|
||||
"new_password": "newpassword123"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "密码修改成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 旧密码错误响应 (401)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "旧密码错误",
|
||||
"error_code": "CHANGE_PASSWORD_FAILED"
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### 10. GitHub OAuth登录
|
||||
|
||||
**接口**: `POST /auth/github`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"github_id": "12345678",
|
||||
"username": "octocat",
|
||||
"nickname": "The Octocat",
|
||||
"email": "octocat@github.com",
|
||||
"avatar_url": "https://github.com/images/error/octocat_happy.gif"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (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": false,
|
||||
"message": "GitHub登录成功"
|
||||
},
|
||||
"message": "GitHub登录成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200) - 新用户注册
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "4",
|
||||
"username": "octocat_1",
|
||||
"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账户绑定成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 11. 验证邮箱验证码
|
||||
|
||||
**接口**: `POST /auth/verify-email`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"email": "test@example.com",
|
||||
"verification_code": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "邮箱验证成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 验证码错误响应 (400)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "验证码错误",
|
||||
"error_code": "EMAIL_VERIFICATION_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 12. 重新发送邮箱验证码
|
||||
|
||||
**接口**: `POST /auth/resend-email-verification`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"email": "test@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200) - 生产环境
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"is_test_mode": false
|
||||
},
|
||||
"message": "验证码已重新发送,请查收邮件"
|
||||
}
|
||||
```
|
||||
|
||||
#### 测试模式响应 (206) - 开发环境
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"data": {
|
||||
"verification_code": "456789",
|
||||
"is_test_mode": true
|
||||
},
|
||||
"message": "⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。",
|
||||
"error_code": "TEST_MODE_ONLY"
|
||||
}
|
||||
```
|
||||
|
||||
#### 邮箱已验证响应 (400)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "邮箱已验证,无需重复验证",
|
||||
"error_code": "RESEND_EMAIL_VERIFICATION_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 13. 管理员登录
|
||||
|
||||
**接口**: `POST /admin/auth/login`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "Admin123456"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"admin": {
|
||||
"id": "1",
|
||||
"username": "admin",
|
||||
"nickname": "管理员",
|
||||
"role": 0
|
||||
},
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"expires_in": 28800,
|
||||
"message": "管理员登录成功"
|
||||
},
|
||||
"message": "管理员登录成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 认证失败响应 (401)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户名或密码错误",
|
||||
"error_code": "ADMIN_LOGIN_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
#### 权限不足响应 (403)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "权限不足,需要管理员权限",
|
||||
"error_code": "ADMIN_LOGIN_FAILED"
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### 14. 获取用户列表
|
||||
|
||||
**接口**: `GET /admin/users`
|
||||
|
||||
#### 查询参数
|
||||
- `page`: 页码(可选,默认1)
|
||||
- `limit`: 每页数量(可选,默认10)
|
||||
- `status`: 用户状态筛选(可选)
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"users": [
|
||||
{
|
||||
"id": "1",
|
||||
"username": "testuser",
|
||||
"nickname": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": null,
|
||||
"role": 1,
|
||||
"status": "active",
|
||||
"email_verified": true,
|
||||
"created_at": "2025-12-17T10:00:00.000Z",
|
||||
"updated_at": "2025-12-17T10:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 10,
|
||||
"total": 1,
|
||||
"pages": 1
|
||||
}
|
||||
},
|
||||
"message": "用户列表获取成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 15. 获取用户详情
|
||||
|
||||
**接口**: `GET /admin/users/:id`
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "1",
|
||||
"username": "testuser",
|
||||
"nickname": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": null,
|
||||
"role": 1,
|
||||
"status": "active",
|
||||
"email_verified": true,
|
||||
"github_id": null,
|
||||
"avatar_url": null,
|
||||
"created_at": "2025-12-17T10:00:00.000Z",
|
||||
"updated_at": "2025-12-17T10:00:00.000Z"
|
||||
}
|
||||
},
|
||||
"message": "用户详情获取成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户不存在响应 (404)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户不存在",
|
||||
"error_code": "USER_NOT_FOUND"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 16. 管理员重置用户密码
|
||||
|
||||
**接口**: `POST /admin/users/:id/reset-password`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"new_password": "newpassword123"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "用户密码重置成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户不存在响应 (404)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户不存在",
|
||||
"error_code": "USER_NOT_FOUND"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 17. 修改用户状态
|
||||
|
||||
**接口**: `PUT /admin/users/:id/status`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"status": "locked",
|
||||
"reason": "违规操作"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "1",
|
||||
"username": "testuser",
|
||||
"status": "locked",
|
||||
"updated_at": "2025-12-17T10:00:00.000Z"
|
||||
}
|
||||
},
|
||||
"message": "用户状态修改成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 状态值无效响应 (400)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "无效的用户状态值",
|
||||
"error_code": "USER_STATUS_UPDATE_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 18. 批量修改用户状态
|
||||
|
||||
**接口**: `POST /admin/users/batch-status`
|
||||
|
||||
#### 请求体
|
||||
```json
|
||||
{
|
||||
"user_ids": ["1", "2", "3"],
|
||||
"status": "active",
|
||||
"reason": "批量激活"
|
||||
}
|
||||
```
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"updated_count": 3,
|
||||
"failed_count": 0,
|
||||
"results": [
|
||||
{
|
||||
"user_id": "1",
|
||||
"success": true,
|
||||
"new_status": "active"
|
||||
},
|
||||
{
|
||||
"user_id": "2",
|
||||
"success": true,
|
||||
"new_status": "active"
|
||||
},
|
||||
{
|
||||
"user_id": "3",
|
||||
"success": true,
|
||||
"new_status": "active"
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": "批量状态修改完成"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 19. 获取用户状态统计
|
||||
|
||||
**接口**: `GET /admin/users/status-stats`
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"stats": {
|
||||
"active": 15,
|
||||
"inactive": 3,
|
||||
"locked": 2,
|
||||
"banned": 1,
|
||||
"deleted": 0,
|
||||
"pending": 5
|
||||
},
|
||||
"total": 26
|
||||
},
|
||||
"message": "用户状态统计获取成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 20. 获取运行时日志
|
||||
|
||||
**接口**: `GET /admin/logs/runtime`
|
||||
|
||||
#### 查询参数
|
||||
- `lines`: 日志行数(可选,默认100)
|
||||
- `level`: 日志级别(可选)
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"logs": [
|
||||
"[2025-12-25 18:27:35] LOG [NestApplication] Nest application successfully started",
|
||||
"[2025-12-25 18:27:35] LOG [RouterExplorer] Mapped {/, GET} route"
|
||||
],
|
||||
"total_lines": 2,
|
||||
"timestamp": "2025-12-25T10:27:44.352Z"
|
||||
},
|
||||
"message": "运行时日志获取成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 21. 获取归档日志
|
||||
|
||||
**接口**: `GET /admin/logs/archive`
|
||||
|
||||
#### 查询参数
|
||||
- `date`: 日期(YYYY-MM-DD格式,可选)
|
||||
- `download`: 是否下载(可选)
|
||||
|
||||
#### 成功响应 (200)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"files": [
|
||||
{
|
||||
"filename": "app-2025-12-24.log",
|
||||
"size": 1024,
|
||||
"created_at": "2025-12-24T00:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"total_files": 1
|
||||
},
|
||||
"message": "归档日志列表获取成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 版本更新记录
|
||||
|
||||
### v1.1.2 (2025-12-25)
|
||||
- **验证码冷却优化**: 注册、密码重置、验证码登录成功后自动清除验证码冷却时间
|
||||
- **用户体验提升**: 成功操作后可立即发送新的验证码,无需等待冷却时间
|
||||
- **代码健壮性**: 冷却时间清除失败不影响主要业务流程
|
||||
|
||||
### v1.1.1 (2025-12-25)
|
||||
- **邮箱冲突检测优化**: 发送邮箱验证码前检查邮箱是否已被注册
|
||||
- **用户体验提升**: 避免向已注册邮箱发送无用验证码
|
||||
- **错误处理改进**: 返回409 Conflict状态码和明确错误信息
|
||||
|
||||
### v1.1.0 (2025-12-25)
|
||||
- **新增验证码登录功能**: 支持邮箱验证码登录
|
||||
- **HTTP状态码修复**: 所有接口返回正确的业务状态码
|
||||
- **完善错误处理**: 统一错误响应格式和错误代码
|
||||
120
docs/03-技术实现/README.md
Normal file
120
docs/03-技术实现/README.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 🔧 技术实现
|
||||
|
||||
> **适用人群**: 正在开发功能的程序员
|
||||
> **使用时机**: 具体功能开发时
|
||||
|
||||
这个目录包含了具体的技术实现指导,帮助开发者快速实现各种游戏功能和系统集成。
|
||||
|
||||
## 📋 实现指南
|
||||
|
||||
### 核心实现 🎮
|
||||
**[实现细节规范.md](实现细节规范.md)**
|
||||
- 玩家角色实现标准
|
||||
- NPC交互系统实现
|
||||
- TileMap图层配置规范
|
||||
- 交互物品实现模板
|
||||
- 性能优化要求
|
||||
|
||||
### 网络集成 🌐
|
||||
**[API接口文档.md](API接口文档.md)**
|
||||
- 完整的后端API接口说明
|
||||
- 请求格式和响应格式
|
||||
- 错误码和处理方式
|
||||
- 认证和权限管理
|
||||
|
||||
**[网络管理器设置.md](网络管理器设置.md)**
|
||||
- NetworkManager配置方法
|
||||
- 网络请求封装使用
|
||||
- 错误处理机制
|
||||
- 响应数据处理
|
||||
|
||||
### 质量保证 🧪
|
||||
**[测试指南.md](测试指南.md)**
|
||||
- API接口测试方法
|
||||
- Python测试脚本使用
|
||||
- Godot内置测试
|
||||
- 测试用例编写
|
||||
|
||||
## 🎯 开发流程
|
||||
|
||||
### 功能开发标准流程
|
||||
1. **需求分析** - 明确功能需求和技术方案
|
||||
2. **架构设计** - 参考实现细节规范设计架构
|
||||
3. **编码实现** - 按照规范编写代码
|
||||
4. **接口集成** - 使用API文档进行后端集成
|
||||
5. **功能测试** - 使用测试指南验证功能
|
||||
6. **代码审查** - 检查规范遵循情况
|
||||
|
||||
### 常见开发场景
|
||||
|
||||
#### 🎮 实现新的游戏角色
|
||||
1. 阅读 [实现细节规范.md](实现细节规范.md) 中的角色实现部分
|
||||
2. 使用提供的代码模板创建角色
|
||||
3. 配置相机和交互系统
|
||||
4. 测试移动和交互功能
|
||||
|
||||
#### 🌐 集成新的API接口
|
||||
1. 查看 [API接口文档.md](API接口文档.md) 了解接口规范
|
||||
2. 使用 [网络管理器设置.md](网络管理器设置.md) 配置网络请求
|
||||
3. 实现数据处理和错误处理
|
||||
4. 使用 [测试指南.md](测试指南.md) 验证接口功能
|
||||
|
||||
#### 🗺️ 创建新的游戏场景
|
||||
1. 参考 [实现细节规范.md](实现细节规范.md) 中的TileMap配置
|
||||
2. 设置正确的图层结构和碰撞检测
|
||||
3. 配置相机边界和Y排序
|
||||
4. 添加交互物品和NPC
|
||||
|
||||
## 🔍 问题排查
|
||||
|
||||
### 常见问题类型
|
||||
|
||||
#### 网络相关问题
|
||||
- **API调用失败** → 检查 [API接口文档.md](API接口文档.md) 中的接口格式
|
||||
- **网络超时** → 参考 [网络管理器设置.md](网络管理器设置.md) 的超时配置
|
||||
- **数据解析错误** → 查看响应格式和错误处理
|
||||
|
||||
#### 游戏对象问题
|
||||
- **角色移动异常** → 检查 [实现细节规范.md](实现细节规范.md) 中的移动实现
|
||||
- **交互不响应** → 验证InteractionArea配置和事件系统
|
||||
- **相机边界错误** → 检查TileMap边界计算
|
||||
|
||||
#### 性能问题
|
||||
- **帧率下降** → 参考性能优化要求
|
||||
- **内存泄漏** → 检查资源管理和对象生命周期
|
||||
|
||||
### 调试技巧
|
||||
1. **使用Godot调试器** - 设置断点调试代码逻辑
|
||||
2. **查看控制台输出** - 关注错误和警告信息
|
||||
3. **使用测试脚本** - 编写简单测试验证功能
|
||||
4. **性能分析** - 使用Godot的性能分析工具
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
### 内部资源
|
||||
- [02-开发规范](../02-开发规范/) - 编码规范和架构设计
|
||||
- [04-高级开发](../04-高级开发/) - 进阶开发技巧
|
||||
- [06-功能模块](../06-功能模块/) - 特定功能实现
|
||||
|
||||
### 外部资源
|
||||
- [Godot官方文档](https://docs.godotengine.org/)
|
||||
- [GDScript语言参考](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/)
|
||||
- [Godot最佳实践](https://docs.godotengine.org/en/stable/tutorials/best_practices/)
|
||||
|
||||
## 💡 开发建议
|
||||
|
||||
### 效率提升
|
||||
- 使用代码模板快速创建标准结构
|
||||
- 善用Godot的场景实例化功能
|
||||
- 合理使用@export变量进行配置
|
||||
- 充分利用事件系统解耦组件
|
||||
|
||||
### 质量保证
|
||||
- 每个功能都要编写对应测试
|
||||
- 定期进行代码审查
|
||||
- 关注性能指标和内存使用
|
||||
- 保持代码简洁和可读性
|
||||
|
||||
---
|
||||
|
||||
**记住:好的实现不仅要功能正确,更要易于维护和扩展!**
|
||||
388
docs/03-技术实现/实现细节规范.md
Normal file
388
docs/03-技术实现/实现细节规范.md
Normal file
@@ -0,0 +1,388 @@
|
||||
# 实现细节规范
|
||||
|
||||
本文档详细说明了WhaleTown项目中各种游戏对象的具体实现要求和技术细节。
|
||||
|
||||
## 🎮 玩家实现规范
|
||||
|
||||
### 基础要求
|
||||
- **节点类型**: 必须使用 `CharacterBody2D`
|
||||
- **移动系统**: 使用 `move_and_slide()` 方法
|
||||
- **相机集成**: 必须包含 `Camera2D` 子节点
|
||||
|
||||
### 玩家节点结构
|
||||
```
|
||||
Player (CharacterBody2D)
|
||||
├── Sprite2D # 玩家精灵
|
||||
├── CollisionShape2D # 碰撞形状
|
||||
├── Camera2D # 玩家相机
|
||||
│ └── [相机配置]
|
||||
├── InteractionArea (Area2D) # 交互检测区域
|
||||
│ └── CollisionShape2D
|
||||
└── AnimationPlayer # 动画播放器
|
||||
```
|
||||
|
||||
### 相机配置要求
|
||||
```gdscript
|
||||
# Player.gd 中的相机设置
|
||||
@onready var camera: Camera2D = $Camera2D
|
||||
|
||||
func _ready() -> void:
|
||||
# 必须启用位置平滑
|
||||
camera.position_smoothing_enabled = true
|
||||
camera.position_smoothing_speed = 5.0
|
||||
|
||||
# 设置相机边界(基于TileMap)
|
||||
_setup_camera_limits()
|
||||
|
||||
func _setup_camera_limits() -> void:
|
||||
# 获取当前场景的TileMap
|
||||
var tilemap = get_tree().get_first_node_in_group("tilemap")
|
||||
if tilemap and tilemap is TileMap:
|
||||
var used_rect = tilemap.get_used_rect()
|
||||
var tile_size = tilemap.tile_set.tile_size
|
||||
|
||||
# 计算世界坐标边界
|
||||
camera.limit_left = used_rect.position.x * tile_size.x
|
||||
camera.limit_top = used_rect.position.y * tile_size.y
|
||||
camera.limit_right = used_rect.end.x * tile_size.x
|
||||
camera.limit_bottom = used_rect.end.y * tile_size.y
|
||||
```
|
||||
|
||||
### 移动实现模板
|
||||
```gdscript
|
||||
extends CharacterBody2D
|
||||
class_name Player
|
||||
|
||||
@export var move_speed: float = 200.0
|
||||
@export var acceleration: float = 1000.0
|
||||
@export var friction: float = 1000.0
|
||||
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
@onready var camera: Camera2D = $Camera2D
|
||||
|
||||
func _ready() -> void:
|
||||
# 设置相机
|
||||
camera.position_smoothing_enabled = true
|
||||
_setup_camera_limits()
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_handle_movement(delta)
|
||||
|
||||
func _handle_movement(delta: float) -> void:
|
||||
# 获取输入方向
|
||||
var input_direction := Input.get_vector(
|
||||
"move_left", "move_right",
|
||||
"move_up", "move_down"
|
||||
)
|
||||
|
||||
# 应用移动
|
||||
if input_direction != Vector2.ZERO:
|
||||
velocity = velocity.move_toward(input_direction * move_speed, acceleration * delta)
|
||||
else:
|
||||
velocity = velocity.move_toward(Vector2.ZERO, friction * delta)
|
||||
|
||||
move_and_slide()
|
||||
|
||||
# 发送移动事件
|
||||
if velocity.length() > 0:
|
||||
EventSystem.emit_event(EventNames.PLAYER_MOVED, {
|
||||
"position": global_position,
|
||||
"velocity": velocity
|
||||
})
|
||||
```
|
||||
|
||||
## 🤖 NPC实现规范
|
||||
|
||||
### 基础要求
|
||||
- **节点类型**: 使用 `CharacterBody2D` 或 `StaticBody2D`
|
||||
- **交互区域**: 必须包含名为 `InteractionArea` 的 `Area2D`
|
||||
- **事件通信**: 通过 `EventSystem` 触发交互
|
||||
|
||||
### NPC节点结构
|
||||
```
|
||||
NPC (CharacterBody2D)
|
||||
├── Sprite2D # NPC精灵
|
||||
├── CollisionShape2D # 物理碰撞
|
||||
├── InteractionArea (Area2D) # 交互检测区域
|
||||
│ └── CollisionShape2D # 交互碰撞形状
|
||||
├── DialogueComponent # 对话组件
|
||||
└── AnimationPlayer # 动画播放器
|
||||
```
|
||||
|
||||
### NPC实现模板
|
||||
```gdscript
|
||||
extends CharacterBody2D
|
||||
class_name NPC
|
||||
|
||||
@export var npc_name: String = "NPC"
|
||||
@export var dialogue_resource: DialogueResource
|
||||
|
||||
@onready var interaction_area: Area2D = $InteractionArea
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
|
||||
signal interaction_available(npc: NPC)
|
||||
signal interaction_unavailable(npc: NPC)
|
||||
|
||||
func _ready() -> void:
|
||||
# 连接交互区域信号
|
||||
interaction_area.body_entered.connect(_on_interaction_area_entered)
|
||||
interaction_area.body_exited.connect(_on_interaction_area_exited)
|
||||
|
||||
# 监听交互事件
|
||||
EventSystem.connect_event(EventNames.INTERACT_PRESSED, _on_interact_pressed)
|
||||
|
||||
func _on_interaction_area_entered(body: Node2D) -> void:
|
||||
if body.is_in_group("player"):
|
||||
interaction_available.emit(self)
|
||||
# 显示交互提示
|
||||
_show_interaction_hint()
|
||||
|
||||
func _on_interaction_area_exited(body: Node2D) -> void:
|
||||
if body.is_in_group("player"):
|
||||
interaction_unavailable.emit(self)
|
||||
# 隐藏交互提示
|
||||
_hide_interaction_hint()
|
||||
|
||||
func _on_interact_pressed(data: Dictionary = {}) -> void:
|
||||
# 检查玩家是否在交互范围内
|
||||
if _is_player_in_range():
|
||||
start_dialogue()
|
||||
|
||||
func start_dialogue() -> void:
|
||||
EventSystem.emit_event(EventNames.NPC_TALKED, {
|
||||
"npc": self,
|
||||
"npc_name": npc_name,
|
||||
"dialogue": dialogue_resource
|
||||
})
|
||||
|
||||
func _is_player_in_range() -> bool:
|
||||
var bodies = interaction_area.get_overlapping_bodies()
|
||||
for body in bodies:
|
||||
if body.is_in_group("player"):
|
||||
return true
|
||||
return false
|
||||
```
|
||||
|
||||
## 🗺️ TileMap图层规范
|
||||
|
||||
### 图层配置要求
|
||||
TileMap必须按以下标准配置图层:
|
||||
|
||||
#### 图层0:地面层 (Ground)
|
||||
- **用途**: 地面纹理、道路、草地等
|
||||
- **碰撞**: 禁用物理层
|
||||
- **渲染**: 最底层渲染
|
||||
- **Y排序**: 禁用
|
||||
|
||||
```gdscript
|
||||
# 设置地面层
|
||||
var ground_layer = tilemap.get_layer(0)
|
||||
tilemap.set_layer_name(0, "Ground")
|
||||
tilemap.set_layer_enabled(0, true)
|
||||
tilemap.set_layer_y_sort_enabled(0, false)
|
||||
# 不设置物理层
|
||||
```
|
||||
|
||||
#### 图层1:障碍层 (Obstacles)
|
||||
- **用途**: 墙壁、树木、建筑等不可通过的障碍
|
||||
- **碰撞**: 启用物理层
|
||||
- **渲染**: 中间层
|
||||
- **Y排序**: 禁用
|
||||
|
||||
```gdscript
|
||||
# 设置障碍层
|
||||
tilemap.set_layer_name(1, "Obstacles")
|
||||
tilemap.set_layer_enabled(1, true)
|
||||
tilemap.set_layer_y_sort_enabled(1, false)
|
||||
# 设置物理层用于碰撞检测
|
||||
tilemap.set_layer_physics_enabled(1, true)
|
||||
```
|
||||
|
||||
#### 图层2:装饰层 (Decoration)
|
||||
- **用途**: 装饰物、前景元素
|
||||
- **碰撞**: 根据需要设置
|
||||
- **渲染**: 最上层
|
||||
- **Y排序**: 启用(重要!)
|
||||
|
||||
```gdscript
|
||||
# 设置装饰层
|
||||
tilemap.set_layer_name(2, "Decoration")
|
||||
tilemap.set_layer_enabled(2, true)
|
||||
tilemap.set_layer_y_sort_enabled(2, true) # 启用Y排序
|
||||
tilemap.set_layer_y_sort_origin(2, 16) # 设置排序原点
|
||||
```
|
||||
|
||||
### TileMap设置模板
|
||||
```gdscript
|
||||
extends TileMap
|
||||
class_name GameTileMap
|
||||
|
||||
func _ready() -> void:
|
||||
# 设置TileMap为tilemap组
|
||||
add_to_group("tilemap")
|
||||
|
||||
# 配置图层
|
||||
_setup_layers()
|
||||
|
||||
# 通知相机系统更新边界
|
||||
EventSystem.emit_event(EventNames.TILEMAP_READY, {
|
||||
"tilemap": self,
|
||||
"used_rect": get_used_rect()
|
||||
})
|
||||
|
||||
func _setup_layers() -> void:
|
||||
# 确保有足够的图层
|
||||
while get_layers_count() < 3:
|
||||
add_layer(-1)
|
||||
|
||||
# 配置地面层 (0)
|
||||
set_layer_name(0, "Ground")
|
||||
set_layer_y_sort_enabled(0, false)
|
||||
|
||||
# 配置障碍层 (1)
|
||||
set_layer_name(1, "Obstacles")
|
||||
set_layer_y_sort_enabled(1, false)
|
||||
set_layer_physics_enabled(1, true)
|
||||
|
||||
# 配置装饰层 (2)
|
||||
set_layer_name(2, "Decoration")
|
||||
set_layer_y_sort_enabled(2, true)
|
||||
set_layer_y_sort_origin(2, tile_set.tile_size.y / 2)
|
||||
```
|
||||
|
||||
## 🎯 交互物实现规范
|
||||
|
||||
### 可收集物品
|
||||
```gdscript
|
||||
extends Area2D
|
||||
class_name CollectibleItem
|
||||
|
||||
@export var item_name: String = "Item"
|
||||
@export var item_value: int = 1
|
||||
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
@onready var collision: CollisionShape2D = $CollisionShape2D
|
||||
|
||||
func _ready() -> void:
|
||||
body_entered.connect(_on_body_entered)
|
||||
|
||||
func _on_body_entered(body: Node2D) -> void:
|
||||
if body.is_in_group("player"):
|
||||
collect_item(body)
|
||||
|
||||
func collect_item(collector: Node2D) -> void:
|
||||
# 发送收集事件
|
||||
EventSystem.emit_event(EventNames.ITEM_COLLECTED, {
|
||||
"item_name": item_name,
|
||||
"item_value": item_value,
|
||||
"collector": collector,
|
||||
"position": global_position
|
||||
})
|
||||
|
||||
# 播放收集动画
|
||||
_play_collect_animation()
|
||||
|
||||
func _play_collect_animation() -> void:
|
||||
var tween = create_tween()
|
||||
tween.parallel().tween_property(self, "scale", Vector2.ZERO, 0.3)
|
||||
tween.parallel().tween_property(self, "modulate:a", 0.0, 0.3)
|
||||
await tween.finished
|
||||
queue_free()
|
||||
```
|
||||
|
||||
### 可交互对象
|
||||
```gdscript
|
||||
extends StaticBody2D
|
||||
class_name InteractableObject
|
||||
|
||||
@export var interaction_text: String = "按E交互"
|
||||
@export var can_interact: bool = true
|
||||
|
||||
@onready var interaction_area: Area2D = $InteractionArea
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
|
||||
var player_in_range: bool = false
|
||||
|
||||
func _ready() -> void:
|
||||
interaction_area.body_entered.connect(_on_interaction_area_entered)
|
||||
interaction_area.body_exited.connect(_on_interaction_area_exited)
|
||||
EventSystem.connect_event(EventNames.INTERACT_PRESSED, _on_interact_pressed)
|
||||
|
||||
func _on_interaction_area_entered(body: Node2D) -> void:
|
||||
if body.is_in_group("player") and can_interact:
|
||||
player_in_range = true
|
||||
_show_interaction_prompt()
|
||||
|
||||
func _on_interaction_area_exited(body: Node2D) -> void:
|
||||
if body.is_in_group("player"):
|
||||
player_in_range = false
|
||||
_hide_interaction_prompt()
|
||||
|
||||
func _on_interact_pressed(data: Dictionary = {}) -> void:
|
||||
if player_in_range and can_interact:
|
||||
interact()
|
||||
|
||||
func interact() -> void:
|
||||
# 子类重写此方法实现具体交互逻辑
|
||||
print("与 ", name, " 交互")
|
||||
|
||||
EventSystem.emit_event(EventNames.OBJECT_INTERACTED, {
|
||||
"object": self,
|
||||
"interaction_type": "default"
|
||||
})
|
||||
```
|
||||
|
||||
## 🎨 资源过滤设置
|
||||
|
||||
### 纹理过滤规范
|
||||
所有像素艺术资源必须使用最近邻过滤:
|
||||
|
||||
```gdscript
|
||||
# 在导入设置中或代码中设置
|
||||
func _setup_pixel_perfect_texture(texture: Texture2D) -> void:
|
||||
if texture is ImageTexture:
|
||||
var image = texture.get_image()
|
||||
image.generate_mipmaps(false)
|
||||
# 在导入设置中设置Filter为Off
|
||||
```
|
||||
|
||||
### 导入设置模板
|
||||
对于所有精灵资源,在导入设置中:
|
||||
- **Filter**: Off (关闭)
|
||||
- **Mipmaps**: Off (关闭)
|
||||
- **Fix Alpha Border**: On (开启)
|
||||
|
||||
## 🔧 性能优化要求
|
||||
|
||||
### 节点缓存
|
||||
```gdscript
|
||||
# ✅ 正确:使用@onready缓存节点引用
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
@onready var collision: CollisionShape2D = $CollisionShape2D
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
sprite.modulate.a = 0.5 # 使用缓存的引用
|
||||
|
||||
# ❌ 错误:在_process中重复获取节点
|
||||
func _process(delta: float) -> void:
|
||||
$Sprite2D.modulate.a = 0.5 # 每帧都要查找节点
|
||||
```
|
||||
|
||||
### 事件频率控制
|
||||
```gdscript
|
||||
# 控制事件发送频率
|
||||
var last_event_time: float = 0.0
|
||||
const EVENT_INTERVAL: float = 0.1 # 100ms间隔
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var current_time = Time.get_time_dict_from_system()
|
||||
if current_time - last_event_time >= EVENT_INTERVAL:
|
||||
EventSystem.emit_event(EventNames.POSITION_UPDATE, {
|
||||
"position": global_position
|
||||
})
|
||||
last_event_time = current_time
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**记住:遵循这些实现细节规范可以确保游戏对象的一致性和性能!**
|
||||
312
docs/03-技术实现/测试指南.md
Normal file
312
docs/03-技术实现/测试指南.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# API接口测试指南
|
||||
|
||||
**更新日期**: 2025-12-25
|
||||
**适用版本**: API v1.1.1
|
||||
|
||||
---
|
||||
|
||||
## 🎯 测试概述
|
||||
|
||||
本指南提供了完整的API接口测试方案,包括Godot内置测试和独立的Python测试脚本,确保在不同环境下都能有效验证API功能。
|
||||
|
||||
---
|
||||
|
||||
## 📋 测试工具对比
|
||||
|
||||
| 测试工具 | 适用场景 | 优势 | 使用难度 |
|
||||
|----------|----------|------|----------|
|
||||
| **Python快速测试** | 日常检查 | 快速、简单 | ⭐ |
|
||||
| **Python完整测试** | 全面验证 | 覆盖全面、详细 | ⭐⭐ |
|
||||
| **Godot内置测试** | 引擎环境 | 真实环境、UI测试 | ⭐⭐⭐ |
|
||||
| **简单连接测试** | 基础检查 | 最小依赖 | ⭐ |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. Python测试(推荐)
|
||||
|
||||
#### 安装依赖
|
||||
```bash
|
||||
cd tests/api
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
#### 运行快速测试
|
||||
```bash
|
||||
# Windows
|
||||
run_tests.bat
|
||||
|
||||
# Linux/Mac
|
||||
./run_tests.sh
|
||||
|
||||
# 或直接运行
|
||||
python quick_test.py
|
||||
```
|
||||
|
||||
### 2. Godot测试
|
||||
|
||||
#### 运行API测试脚本
|
||||
```gdscript
|
||||
# 在Godot中运行
|
||||
var api_test = preload("res://scripts/network/ApiTestScript.gd").new()
|
||||
add_child(api_test)
|
||||
```
|
||||
|
||||
#### 运行UI测试
|
||||
```bash
|
||||
# 打开Godot项目
|
||||
# 运行 tests/auth/auth_ui_test.tscn 场景
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 测试类型详解
|
||||
|
||||
### 1. 快速测试 (`quick_test.py`)
|
||||
|
||||
**用途**: 日常开发中的快速验证
|
||||
**时间**: 约30秒
|
||||
**覆盖**: 基础API端点
|
||||
|
||||
```bash
|
||||
python tests/api/quick_test.py
|
||||
```
|
||||
|
||||
**测试内容**:
|
||||
- ✅ 服务器状态检查
|
||||
- ✅ 邮箱验证码发送
|
||||
- ✅ 用户登录/注册
|
||||
- ✅ 基础错误处理
|
||||
|
||||
### 2. 完整测试 (`api_client_test.py`)
|
||||
|
||||
**用途**: 发布前的全面验证
|
||||
**时间**: 约2-3分钟
|
||||
**覆盖**: 所有业务流程
|
||||
|
||||
```bash
|
||||
python tests/api/api_client_test.py
|
||||
```
|
||||
|
||||
**测试内容**:
|
||||
- 🔄 完整的用户注册流程
|
||||
- 🔄 邮箱验证流程
|
||||
- 🔄 登录流程(密码+验证码)
|
||||
- 🔄 密码重置流程
|
||||
- 🔄 错误场景测试
|
||||
- 🔄 频率限制测试
|
||||
|
||||
### 3. Godot内置测试
|
||||
|
||||
**用途**: 引擎环境下的真实测试
|
||||
**时间**: 根据测试场景而定
|
||||
**覆盖**: UI交互和网络请求
|
||||
|
||||
#### API测试脚本
|
||||
```gdscript
|
||||
# 文件: scripts/network/ApiTestScript.gd
|
||||
# 功能: 验证NetworkManager和ResponseHandler
|
||||
```
|
||||
|
||||
#### UI测试场景
|
||||
```gdscript
|
||||
# 文件: tests/auth/auth_ui_test.tscn
|
||||
# 功能: 测试认证界面的各种响应情况
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 测试配置
|
||||
|
||||
### API服务器配置
|
||||
```python
|
||||
# 默认配置
|
||||
API_BASE_URL = "https://whaletownend.xinghangee.icu"
|
||||
|
||||
# 本地开发配置
|
||||
API_BASE_URL = "http://localhost:3000"
|
||||
```
|
||||
|
||||
### 测试数据配置
|
||||
```python
|
||||
# 测试用户信息
|
||||
TEST_EMAIL = "test@example.com"
|
||||
TEST_USERNAME = "testuser"
|
||||
TEST_PASSWORD = "password123"
|
||||
```
|
||||
|
||||
### 超时配置
|
||||
```python
|
||||
DEFAULT_TIMEOUT = 30 # 秒
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 测试结果解读
|
||||
|
||||
### 成功标志
|
||||
- ✅ **测试通过** - 功能正常
|
||||
- 🧪 **测试模式** - 开发环境,验证码在响应中返回
|
||||
- 🔑 **获取验证码** - 成功获取到测试验证码
|
||||
|
||||
### 警告标志
|
||||
- ⚠️ **资源冲突** - 409状态码,用户名/邮箱已存在
|
||||
- ⏰ **频率限制** - 429状态码,请求过于频繁
|
||||
|
||||
### 错误标志
|
||||
- ❌ **测试失败** - 功能异常,需要检查
|
||||
- 🔌 **连接失败** - 网络问题或服务器不可用
|
||||
- 📊 **状态码异常** - HTTP状态码不符合预期
|
||||
|
||||
### 示例输出解读
|
||||
```
|
||||
🧪 测试: 发送邮箱验证码
|
||||
📡 POST https://whaletownend.xinghangee.icu/auth/send-email-verification
|
||||
📦 数据: {"email": "test@example.com"}
|
||||
📊 状态码: 206
|
||||
🧪 测试模式响应
|
||||
🔑 验证码: 123456
|
||||
✅ 测试通过
|
||||
```
|
||||
|
||||
**解读**:
|
||||
- 请求成功发送到正确的端点
|
||||
- 返回206状态码表示测试模式
|
||||
- 成功获取验证码123456
|
||||
- 整体测试通过
|
||||
|
||||
---
|
||||
|
||||
## 🐛 故障排除
|
||||
|
||||
### 常见问题及解决方案
|
||||
|
||||
#### 1. 连接失败
|
||||
**症状**: `❌ 连接失败` 或 `网络连接异常`
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查网络连接
|
||||
ping whaletownend.xinghangee.icu
|
||||
|
||||
# 检查服务器状态
|
||||
curl https://whaletownend.xinghangee.icu
|
||||
|
||||
# 检查防火墙设置
|
||||
```
|
||||
|
||||
#### 2. 频率限制
|
||||
**症状**: `⏰ 请求过于频繁` 或 `429状态码`
|
||||
|
||||
**解决方案**:
|
||||
- 等待冷却时间(通常1分钟)
|
||||
- 使用不同的测试邮箱
|
||||
- 检查API频率限制配置
|
||||
|
||||
#### 3. 验证码错误
|
||||
**症状**: `验证码错误或已过期`
|
||||
|
||||
**解决方案**:
|
||||
- 确保使用最新获取的验证码
|
||||
- 检查验证码是否在5分钟有效期内
|
||||
- 在测试模式下使用响应中返回的验证码
|
||||
|
||||
#### 4. 参数验证失败
|
||||
**症状**: `400状态码` 或 `参数验证失败`
|
||||
|
||||
**解决方案**:
|
||||
- 检查请求参数格式
|
||||
- 确认必填字段都已提供
|
||||
- 验证邮箱、用户名格式是否正确
|
||||
|
||||
#### 5. Python依赖问题
|
||||
**症状**: `ModuleNotFoundError: No module named 'requests'`
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 安装依赖
|
||||
pip install requests
|
||||
|
||||
# 或使用requirements.txt
|
||||
pip install -r tests/api/requirements.txt
|
||||
|
||||
# 检查Python版本
|
||||
python --version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 测试最佳实践
|
||||
|
||||
### 1. 测试前准备
|
||||
- 确认API服务器正常运行
|
||||
- 检查网络连接稳定
|
||||
- 准备测试数据(邮箱、用户名等)
|
||||
|
||||
### 2. 测试执行
|
||||
- 按照从简单到复杂的顺序执行测试
|
||||
- 记录测试结果和异常情况
|
||||
- 对失败的测试进行重试验证
|
||||
|
||||
### 3. 测试后分析
|
||||
- 分析测试结果,识别问题模式
|
||||
- 更新测试用例覆盖新发现的场景
|
||||
- 文档化测试发现的问题和解决方案
|
||||
|
||||
### 4. 持续集成
|
||||
- 将测试脚本集成到CI/CD流程
|
||||
- 设置自动化测试触发条件
|
||||
- 建立测试结果通知机制
|
||||
|
||||
---
|
||||
|
||||
## 🔄 测试流程建议
|
||||
|
||||
### 开发阶段
|
||||
1. **快速测试** - 每次代码修改后运行
|
||||
2. **功能测试** - 新功能开发完成后运行
|
||||
3. **回归测试** - 修复bug后运行
|
||||
|
||||
### 测试阶段
|
||||
1. **完整测试** - 每日构建后运行
|
||||
2. **压力测试** - 定期运行频率限制测试
|
||||
3. **兼容性测试** - 不同环境下运行测试
|
||||
|
||||
### 发布阶段
|
||||
1. **预发布测试** - 生产环境部署前运行
|
||||
2. **冒烟测试** - 生产环境部署后运行
|
||||
3. **监控测试** - 生产环境持续监控
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [API文档](api-documentation.md) - 完整的API接口说明
|
||||
- [API更新日志](api_update_log.md) - 最新的API变更记录
|
||||
- [项目结构说明](project_structure.md) - 项目整体架构
|
||||
- [网络管理器设置](network_manager_setup.md) - Godot网络配置
|
||||
|
||||
---
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
### 添加新测试
|
||||
1. 在对应的测试文件中添加新的测试方法
|
||||
2. 遵循现有的测试模式和命名规范
|
||||
3. 添加适当的错误处理和结果验证
|
||||
4. 更新相关文档
|
||||
|
||||
### 报告问题
|
||||
1. 提供详细的错误信息和复现步骤
|
||||
2. 包含测试环境信息(Python版本、操作系统等)
|
||||
3. 附上相关的日志和截图
|
||||
|
||||
### 改进建议
|
||||
1. 提出测试覆盖的改进建议
|
||||
2. 优化测试执行效率的方案
|
||||
3. 增强测试结果可读性的想法
|
||||
|
||||
---
|
||||
|
||||
**测试愉快!🎉**
|
||||
362
docs/03-技术实现/网络管理器设置.md
Normal file
362
docs/03-技术实现/网络管理器设置.md
Normal file
@@ -0,0 +1,362 @@
|
||||
# NetworkManager 设置指南
|
||||
|
||||
## 概述
|
||||
|
||||
NetworkManager 是一个统一的网络请求管理器,提供了简洁的API接口和统一的错误处理机制。配合 ResponseHandler,可以大大简化网络请求的处理逻辑。
|
||||
|
||||
## 🚀 快速设置
|
||||
|
||||
### 1. 设置AutoLoad
|
||||
|
||||
在Godot编辑器中:
|
||||
|
||||
1. 打开 `Project` → `Project Settings`
|
||||
2. 切换到 `AutoLoad` 标签
|
||||
3. 添加新的AutoLoad:
|
||||
- **Path**: `res://core/managers/NetworkManager.gd`
|
||||
- **Name**: `NetworkManager`
|
||||
- **Singleton**: ✅ 勾选
|
||||
|
||||
### 2. 项目设置文件配置
|
||||
|
||||
在 `project.godot` 文件中会自动添加:
|
||||
|
||||
```ini
|
||||
[autoload]
|
||||
|
||||
NetworkManager="*res://core/managers/NetworkManager.gd"
|
||||
```
|
||||
|
||||
### 3. 验证设置
|
||||
|
||||
在任何脚本中可以直接使用:
|
||||
|
||||
```gdscript
|
||||
func _ready():
|
||||
# 直接访问全局单例
|
||||
var request_id = NetworkManager.login("username", "password", callback)
|
||||
print("请求ID: ", request_id)
|
||||
```
|
||||
|
||||
## 📚 使用方法
|
||||
|
||||
### 基本用法
|
||||
|
||||
```gdscript
|
||||
# 1. 简单的GET请求
|
||||
var request_id = NetworkManager.get_app_status(func(success, data, error_info):
|
||||
if success:
|
||||
print("应用状态: ", data)
|
||||
else:
|
||||
print("获取状态失败: ", error_info)
|
||||
)
|
||||
|
||||
# 2. 用户登录
|
||||
NetworkManager.login("username", "password", func(success, data, error_info):
|
||||
if success:
|
||||
print("登录成功: ", data.data.user.username)
|
||||
else:
|
||||
print("登录失败: ", error_info.message)
|
||||
)
|
||||
|
||||
# 3. 发送验证码
|
||||
NetworkManager.send_email_verification("test@example.com", func(success, data, error_info):
|
||||
if success:
|
||||
print("验证码已发送")
|
||||
else:
|
||||
print("发送失败: ", error_info.message)
|
||||
)
|
||||
```
|
||||
|
||||
### 配合ResponseHandler使用
|
||||
|
||||
```gdscript
|
||||
# 在回调函数中使用ResponseHandler处理响应
|
||||
func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||||
# 使用ResponseHandler统一处理
|
||||
var result = ResponseHandler.handle_login_response(success, data, error_info)
|
||||
|
||||
# 显示Toast消息
|
||||
show_toast(result.message, result.success)
|
||||
|
||||
# 执行自定义动作
|
||||
if result.custom_action.is_valid():
|
||||
result.custom_action.call()
|
||||
|
||||
# 处理成功情况
|
||||
if result.success:
|
||||
# 登录成功的处理逻辑
|
||||
login_success.emit(username)
|
||||
```
|
||||
|
||||
### 全局事件监听
|
||||
|
||||
```gdscript
|
||||
func _ready():
|
||||
# 监听全局网络事件
|
||||
NetworkManager.request_completed.connect(_on_global_request_completed)
|
||||
NetworkManager.request_failed.connect(_on_global_request_failed)
|
||||
|
||||
func _on_global_request_completed(request_id: String, success: bool, data: Dictionary):
|
||||
print("全局请求完成: ", request_id)
|
||||
# 可以在这里隐藏全局加载指示器
|
||||
|
||||
func _on_global_request_failed(request_id: String, error_type: String, message: String):
|
||||
print("全局请求失败: ", request_id, " - ", message)
|
||||
# 可以在这里显示全局错误提示
|
||||
```
|
||||
|
||||
## 🔧 高级功能
|
||||
|
||||
### 请求管理
|
||||
|
||||
```gdscript
|
||||
# 取消特定请求
|
||||
var request_id = NetworkManager.login("user", "pass", callback)
|
||||
NetworkManager.cancel_request(request_id)
|
||||
|
||||
# 取消所有请求
|
||||
NetworkManager.cancel_all_requests()
|
||||
|
||||
# 检查请求状态
|
||||
if NetworkManager.is_request_active(request_id):
|
||||
print("请求仍在进行中")
|
||||
|
||||
# 获取活动请求数量
|
||||
print("当前活动请求: ", NetworkManager.get_active_request_count())
|
||||
```
|
||||
|
||||
### 自定义请求
|
||||
|
||||
```gdscript
|
||||
# 发送自定义POST请求
|
||||
var custom_data = {
|
||||
"custom_field": "custom_value"
|
||||
}
|
||||
var request_id = NetworkManager.post_request("/custom/endpoint", custom_data, func(success, data, error_info):
|
||||
print("自定义请求完成")
|
||||
)
|
||||
|
||||
# 发送自定义GET请求
|
||||
NetworkManager.get_request("/custom/data", func(success, data, error_info):
|
||||
print("获取自定义数据")
|
||||
)
|
||||
```
|
||||
|
||||
## 📋 API接口列表
|
||||
|
||||
### 认证相关
|
||||
|
||||
| 方法 | 参数 | 说明 |
|
||||
|------|------|------|
|
||||
| `login(identifier, password, callback)` | 用户标识符、密码、回调函数 | 用户登录 |
|
||||
| `verification_code_login(identifier, code, callback)` | 用户标识符、验证码、回调函数 | 验证码登录 |
|
||||
| `send_login_verification_code(identifier, callback)` | 用户标识符、回调函数 | 发送登录验证码 |
|
||||
| `register(username, password, nickname, email, code, callback)` | 注册信息、回调函数 | 用户注册 |
|
||||
| `send_email_verification(email, callback)` | 邮箱、回调函数 | 发送邮箱验证码 |
|
||||
| `verify_email(email, code, callback)` | 邮箱、验证码、回调函数 | 验证邮箱 |
|
||||
|
||||
### 通用请求
|
||||
|
||||
| 方法 | 参数 | 说明 |
|
||||
|------|------|------|
|
||||
| `get_request(endpoint, callback, timeout)` | 端点、回调函数、超时时间 | GET请求 |
|
||||
| `post_request(endpoint, data, callback, timeout)` | 端点、数据、回调函数、超时时间 | POST请求 |
|
||||
| `put_request(endpoint, data, callback, timeout)` | 端点、数据、回调函数、超时时间 | PUT请求 |
|
||||
| `delete_request(endpoint, callback, timeout)` | 端点、回调函数、超时时间 | DELETE请求 |
|
||||
|
||||
### 请求管理
|
||||
|
||||
| 方法 | 参数 | 说明 |
|
||||
|------|------|------|
|
||||
| `cancel_request(request_id)` | 请求ID | 取消特定请求 |
|
||||
| `cancel_all_requests()` | 无 | 取消所有请求 |
|
||||
| `is_request_active(request_id)` | 请求ID | 检查请求是否活动 |
|
||||
| `get_active_request_count()` | 无 | 获取活动请求数量 |
|
||||
| `get_request_info(request_id)` | 请求ID | 获取请求详细信息 |
|
||||
|
||||
## 🎯 ResponseHandler 使用
|
||||
|
||||
### 支持的响应类型
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `handle_login_response()` | 处理登录响应 |
|
||||
| `handle_verification_code_login_response()` | 处理验证码登录响应 |
|
||||
| `handle_send_verification_code_response()` | 处理发送验证码响应 |
|
||||
| `handle_send_login_code_response()` | 处理发送登录验证码响应 |
|
||||
| `handle_register_response()` | 处理注册响应 |
|
||||
| `handle_verify_email_response()` | 处理邮箱验证响应 |
|
||||
| `handle_network_test_response()` | 处理网络测试响应 |
|
||||
| `handle_response(operation_type, ...)` | 通用响应处理 |
|
||||
|
||||
### ResponseResult 结构
|
||||
|
||||
```gdscript
|
||||
class ResponseResult:
|
||||
var success: bool # 是否成功
|
||||
var message: String # 显示消息
|
||||
var toast_type: String # Toast类型 ("success" 或 "error")
|
||||
var data: Dictionary # 响应数据
|
||||
var should_show_toast: bool # 是否显示Toast
|
||||
var custom_action: Callable # 自定义动作
|
||||
```
|
||||
|
||||
## 🔄 迁移指南
|
||||
|
||||
### 从旧版AuthScene迁移
|
||||
|
||||
#### 旧代码:
|
||||
```gdscript
|
||||
func send_login_request(username: String, password: String):
|
||||
var url = API_BASE_URL + "/auth/login"
|
||||
var headers = ["Content-Type: application/json"]
|
||||
var body = JSON.stringify({
|
||||
"username": username,
|
||||
"password": password
|
||||
})
|
||||
|
||||
current_request_type = "login"
|
||||
|
||||
var error = http_request.request(url, headers, HTTPClient.METHOD_POST, body)
|
||||
if error != OK:
|
||||
show_toast('网络请求失败', false)
|
||||
restore_button(login_btn, "密码登录")
|
||||
current_request_type = ""
|
||||
```
|
||||
|
||||
#### 新代码:
|
||||
```gdscript
|
||||
func send_login_request(username: String, password: String):
|
||||
NetworkManager.login(username, password, _on_login_response)
|
||||
|
||||
func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||||
var result = ResponseHandler.handle_login_response(success, data, error_info)
|
||||
show_toast(result.message, result.success)
|
||||
|
||||
if result.success:
|
||||
login_success.emit(username)
|
||||
```
|
||||
|
||||
### 迁移步骤
|
||||
|
||||
1. **设置AutoLoad**:按照上述步骤设置NetworkManager为AutoLoad
|
||||
2. **替换请求方法**:将直接的HTTP请求替换为NetworkManager调用
|
||||
3. **统一响应处理**:使用ResponseHandler处理所有响应
|
||||
4. **移除重复代码**:删除重复的错误处理和请求构建代码
|
||||
5. **测试功能**:确保所有功能正常工作
|
||||
|
||||
## 🧪 测试
|
||||
|
||||
### 单元测试示例
|
||||
|
||||
```gdscript
|
||||
extends GutTest
|
||||
|
||||
func test_network_manager_login():
|
||||
var network_manager = NetworkManager.new()
|
||||
var callback_called = false
|
||||
var callback_success = false
|
||||
|
||||
var callback = func(success, data, error_info):
|
||||
callback_called = true
|
||||
callback_success = success
|
||||
|
||||
var request_id = network_manager.login("testuser", "password", callback)
|
||||
|
||||
assert_ne(request_id, "", "应该返回有效的请求ID")
|
||||
|
||||
# 等待请求完成
|
||||
await get_tree().create_timer(2.0).timeout
|
||||
|
||||
assert_true(callback_called, "回调函数应该被调用")
|
||||
```
|
||||
|
||||
### 集成测试
|
||||
|
||||
```gdscript
|
||||
func test_complete_login_flow():
|
||||
# 1. 发送登录验证码
|
||||
var code_sent = false
|
||||
NetworkManager.send_login_verification_code("test@example.com", func(success, data, error_info):
|
||||
code_sent = success
|
||||
)
|
||||
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
assert_true(code_sent, "验证码应该发送成功")
|
||||
|
||||
# 2. 使用验证码登录
|
||||
var login_success = false
|
||||
NetworkManager.verification_code_login("test@example.com", "123456", func(success, data, error_info):
|
||||
login_success = success
|
||||
)
|
||||
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
assert_true(login_success, "验证码登录应该成功")
|
||||
```
|
||||
|
||||
## 🔍 调试
|
||||
|
||||
### 启用详细日志
|
||||
|
||||
在NetworkManager中,所有请求都会输出详细的调试信息:
|
||||
|
||||
```
|
||||
=== 发送网络请求 ===
|
||||
请求ID: req_1
|
||||
URL: https://whaletownend.xinghangee.icu/auth/login
|
||||
方法: POST
|
||||
Headers: ["Content-Type: application/json"]
|
||||
Body: {"username":"testuser","password":"password123"}
|
||||
发送结果: 0
|
||||
|
||||
=== 网络请求完成 ===
|
||||
请求ID: req_1
|
||||
结果: 0
|
||||
状态码: 200
|
||||
响应头: ["content-type: application/json"]
|
||||
响应体长度: 156 字节
|
||||
响应内容: {"success":true,"data":{"user":{"username":"testuser"}}}
|
||||
```
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **请求ID为空**:检查NetworkManager是否正确设置为AutoLoad
|
||||
2. **回调未调用**:检查网络连接和API地址是否正确
|
||||
3. **解析错误**:检查服务器返回的JSON格式是否正确
|
||||
|
||||
## 📈 性能优化
|
||||
|
||||
### 请求池管理
|
||||
|
||||
NetworkManager自动管理请求资源:
|
||||
- 自动清理完成的请求
|
||||
- 防止内存泄漏
|
||||
- 支持请求取消
|
||||
|
||||
### 最佳实践
|
||||
|
||||
1. **及时取消不需要的请求**
|
||||
2. **使用合适的超时时间**
|
||||
3. **避免同时发送大量请求**
|
||||
4. **在场景切换时取消活动请求**
|
||||
|
||||
```gdscript
|
||||
func _exit_tree():
|
||||
# 场景退出时取消所有请求
|
||||
NetworkManager.cancel_all_requests()
|
||||
```
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
使用NetworkManager和ResponseHandler的优势:
|
||||
|
||||
- ✅ **代码简洁**:一行代码发送请求
|
||||
- ✅ **统一处理**:所有错误情况统一处理
|
||||
- ✅ **易于维护**:网络逻辑与UI逻辑分离
|
||||
- ✅ **功能强大**:支持请求管理、超时、取消等
|
||||
- ✅ **调试友好**:详细的日志和错误信息
|
||||
- ✅ **类型安全**:明确的回调参数类型
|
||||
- ✅ **可扩展**:易于添加新的API接口
|
||||
|
||||
通过这套统一的网络管理系统,你的项目将拥有更好的代码结构和更强的可维护性!
|
||||
Reference in New Issue
Block a user