Compare commits
8 Commits
54402e68e1
...
a1669626fd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1669626fd | ||
|
|
08bf2bbaf3 | ||
|
|
1e47c4db60 | ||
|
|
d28100e103 | ||
|
|
0692aaadcc | ||
|
|
76f5fa99a6 | ||
|
|
ac92dcc67b | ||
|
|
fb8d166f00 |
41
README.md
41
README.md
@@ -173,10 +173,18 @@ export class PlayerService {
|
||||
- [命名规范](./docs/naming_convention.md) - 项目命名规范和最佳实践
|
||||
- [NestJS 使用指南](./docs/nestjs_guide.md) - 详细的 NestJS 开发指南,包含实战案例
|
||||
|
||||
### 📖 API 文档
|
||||
- **[API 文档总览](./docs/api/README.md)** - API 文档使用指南和快速开始
|
||||
- **[Swagger UI](http://localhost:3000/api-docs)** - 交互式 API 文档(需启动服务器)
|
||||
- [详细接口文档](./docs/api/api-documentation.md) - 完整的 API 接口说明
|
||||
- [OpenAPI 规范](./docs/api/openapi.yaml) - 标准化的 API 描述文件
|
||||
- [Postman 集合](./docs/api/postman-collection.json) - 可导入的 API 测试集合
|
||||
|
||||
### 💡 使用建议
|
||||
1. **开发前**:先读 AI 辅助指南,了解如何用 AI 帮助遵循规范
|
||||
2. **开发中**:参考具体规范文档,使用 AI 实时检查代码质量
|
||||
3. **提交前**:用 AI 检查代码和提交信息是否符合规范
|
||||
3. **API 开发**:使用 Swagger UI 进行接口测试,参考 API 文档进行开发
|
||||
4. **提交前**:用 AI 检查代码和提交信息是否符合规范
|
||||
|
||||
## 前置要求
|
||||
|
||||
@@ -262,10 +270,19 @@ test/
|
||||
├── api/ # API 测试
|
||||
└── service/ # 服务测试
|
||||
docs/ # 项目文档
|
||||
├── api/ # API 接口文档
|
||||
│ ├── README.md # API 文档使用指南
|
||||
│ ├── api-documentation.md # 详细接口文档
|
||||
│ ├── openapi.yaml # OpenAPI 规范文件
|
||||
│ └── postman-collection.json # Postman 测试集合
|
||||
├── systems/ # 系统设计文档
|
||||
│ ├── logger/ # 日志系统文档
|
||||
│ └── user-auth/ # 用户认证系统文档
|
||||
├── backend_development_guide.md # 后端开发规范
|
||||
├── git_commit_guide.md # Git 提交规范
|
||||
├── naming_convention.md # 命名规范
|
||||
└── nestjs_guide.md # NestJS 使用指南
|
||||
├── nestjs_guide.md # NestJS 使用指南
|
||||
└── AI辅助开发规范指南.md # AI 辅助开发指南
|
||||
```
|
||||
|
||||
## 核心功能
|
||||
@@ -282,6 +299,26 @@ docs/ # 项目文档
|
||||
|
||||
**详细文档**: [用户认证系统文档](./docs/systems/user-auth/README.md)
|
||||
|
||||
### 📖 API 文档系统
|
||||
|
||||
集成了完整的 API 文档解决方案,提供多种格式的接口文档:
|
||||
|
||||
- **Swagger UI** - 交互式 API 文档界面
|
||||
- **OpenAPI 规范** - 标准化的 API 描述文件
|
||||
- **Postman 集合** - 可导入的 API 测试集合
|
||||
- **详细文档** - 包含示例和最佳实践的完整说明
|
||||
|
||||
**快速访问**:
|
||||
```bash
|
||||
# 启动服务器
|
||||
pnpm run dev
|
||||
|
||||
# 访问 Swagger UI 文档
|
||||
# 浏览器打开: http://localhost:3000/api-docs
|
||||
```
|
||||
|
||||
**详细文档**: [API 文档说明](./docs/api/README.md)
|
||||
|
||||
### 📊 日志系统
|
||||
|
||||
基于 Pino 的高性能日志系统,提供结构化日志记录:
|
||||
|
||||
139
docs/README.md
Normal file
139
docs/README.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# 项目文档
|
||||
|
||||
本目录包含了像素游戏服务器的完整文档。
|
||||
|
||||
## 文档结构
|
||||
|
||||
### 📁 api/
|
||||
API接口相关文档,包含:
|
||||
- **api-documentation.md** - 详细的API接口文档
|
||||
- **openapi.yaml** - OpenAPI 3.0规范文件
|
||||
- **postman-collection.json** - Postman测试集合
|
||||
- **README.md** - API文档使用说明
|
||||
|
||||
### 📁 systems/
|
||||
系统设计文档,包含:
|
||||
- **logger/** - 日志系统文档
|
||||
- **user-auth/** - 用户认证系统文档
|
||||
|
||||
### 📄 其他文档
|
||||
- **AI辅助开发规范指南.md** - AI开发规范
|
||||
- **backend_development_guide.md** - 后端开发指南
|
||||
- **git_commit_guide.md** - Git提交规范
|
||||
- **naming_convention.md** - 命名规范
|
||||
- **nestjs_guide.md** - NestJS开发指南
|
||||
- **日志系统详细说明.md** - 日志系统说明
|
||||
|
||||
## 如何使用
|
||||
|
||||
### 1. 启动服务器并查看Swagger文档
|
||||
|
||||
```bash
|
||||
# 启动开发服务器
|
||||
pnpm run dev
|
||||
|
||||
# 访问Swagger UI
|
||||
# 浏览器打开: http://localhost:3000/api-docs
|
||||
```
|
||||
|
||||
### 2. 使用Postman测试API
|
||||
|
||||
1. 打开Postman
|
||||
2. 点击 Import 按钮
|
||||
3. 选择 `docs/postman-collection.json` 文件
|
||||
4. 导入后即可看到所有API接口
|
||||
5. 修改环境变量 `baseUrl` 为你的服务器地址(默认:http://localhost:3000)
|
||||
|
||||
### 3. 使用OpenAPI规范
|
||||
|
||||
#### 在Swagger Editor中查看
|
||||
1. 访问 [Swagger Editor](https://editor.swagger.io/)
|
||||
2. 将 `docs/openapi.yaml` 的内容复制粘贴到编辑器中
|
||||
3. 即可查看可视化的API文档
|
||||
|
||||
#### 生成客户端SDK
|
||||
```bash
|
||||
# 使用swagger-codegen生成JavaScript客户端
|
||||
swagger-codegen generate -i docs/openapi.yaml -l javascript -o ./client-sdk
|
||||
|
||||
# 使用openapi-generator生成TypeScript客户端
|
||||
openapi-generator generate -i docs/openapi.yaml -g typescript-axios -o ./client-sdk
|
||||
```
|
||||
|
||||
## API接口概览
|
||||
|
||||
| 接口 | 方法 | 路径 | 描述 |
|
||||
|------|------|------|------|
|
||||
| 用户登录 | POST | /auth/login | 支持用户名、邮箱或手机号登录 |
|
||||
| 用户注册 | POST | /auth/register | 创建新用户账户 |
|
||||
| GitHub OAuth | POST | /auth/github | 使用GitHub账户登录或注册 |
|
||||
| 发送验证码 | POST | /auth/forgot-password | 发送密码重置验证码 |
|
||||
| 重置密码 | POST | /auth/reset-password | 使用验证码重置密码 |
|
||||
| 修改密码 | PUT | /auth/change-password | 修改用户密码 |
|
||||
|
||||
## 快速测试
|
||||
|
||||
### 使用cURL测试登录接口
|
||||
|
||||
```bash
|
||||
# 测试用户登录
|
||||
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/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "newuser",
|
||||
"password": "password123",
|
||||
"nickname": "新用户",
|
||||
"email": "newuser@example.com"
|
||||
}'
|
||||
```
|
||||
|
||||
### 使用JavaScript测试
|
||||
|
||||
```javascript
|
||||
// 用户登录
|
||||
const response = await fetch('http://localhost:3000/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
identifier: 'testuser',
|
||||
password: 'password123'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **开发环境**: 当前配置适用于开发环境,生产环境需要使用HTTPS
|
||||
2. **认证**: 实际应用中应实现JWT认证机制
|
||||
3. **限流**: 建议对认证接口实施限流策略
|
||||
4. **验证码**: 示例中返回验证码仅用于演示,生产环境不应返回
|
||||
5. **错误处理**: 建议实现统一的错误处理机制
|
||||
|
||||
## 更新文档
|
||||
|
||||
当API接口发生变化时,请同步更新以下文件:
|
||||
1. 更新DTO类的Swagger装饰器
|
||||
2. 更新 `api-documentation.md`
|
||||
3. 更新 `openapi.yaml`
|
||||
4. 更新 `postman-collection.json`
|
||||
5. 重新生成Swagger文档
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [NestJS Swagger文档](https://docs.nestjs.com/openapi/introduction)
|
||||
- [OpenAPI规范](https://swagger.io/specification/)
|
||||
- [Postman文档](https://learning.postman.com/docs/getting-started/introduction/)
|
||||
- [Swagger Editor](https://editor.swagger.io/)
|
||||
141
docs/api/README.md
Normal file
141
docs/api/README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# API接口文档
|
||||
|
||||
本目录包含了像素游戏服务器用户认证API的完整文档。
|
||||
|
||||
## 📋 文档文件说明
|
||||
|
||||
### 1. api-documentation.md
|
||||
详细的API接口文档,包含:
|
||||
- 接口概述和通用响应格式
|
||||
- 每个接口的详细说明、参数、响应示例
|
||||
- 错误代码说明
|
||||
- 数据验证规则
|
||||
- 使用示例(JavaScript/TypeScript 和 cURL)
|
||||
|
||||
### 2. openapi.yaml
|
||||
OpenAPI 3.0规范文件,可以用于:
|
||||
- 导入到Swagger Editor查看和编辑
|
||||
- 生成客户端SDK
|
||||
- 集成到API网关
|
||||
- 自动化测试
|
||||
|
||||
### 3. postman-collection.json
|
||||
Postman集合文件,包含:
|
||||
- 所有API接口的请求示例
|
||||
- 预设的请求参数
|
||||
- 响应示例
|
||||
- 可直接导入Postman进行测试
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 启动服务器并查看Swagger文档
|
||||
|
||||
```bash
|
||||
# 启动开发服务器
|
||||
pnpm run dev
|
||||
|
||||
# 访问Swagger UI
|
||||
# 浏览器打开: http://localhost:3000/api-docs
|
||||
```
|
||||
|
||||
### 2. 使用Postman测试API
|
||||
|
||||
1. 打开Postman
|
||||
2. 点击 Import 按钮
|
||||
3. 选择 `docs/api/postman-collection.json` 文件
|
||||
4. 导入后即可看到所有API接口
|
||||
5. 修改环境变量 `baseUrl` 为你的服务器地址(默认:http://localhost:3000)
|
||||
|
||||
### 3. 使用OpenAPI规范
|
||||
|
||||
#### 在Swagger Editor中查看
|
||||
1. 访问 [Swagger Editor](https://editor.swagger.io/)
|
||||
2. 将 `docs/api/openapi.yaml` 的内容复制粘贴到编辑器中
|
||||
3. 即可查看可视化的API文档
|
||||
|
||||
#### 生成客户端SDK
|
||||
```bash
|
||||
# 使用swagger-codegen生成JavaScript客户端
|
||||
swagger-codegen generate -i docs/api/openapi.yaml -l javascript -o ./client-sdk
|
||||
|
||||
# 使用openapi-generator生成TypeScript客户端
|
||||
openapi-generator generate -i docs/api/openapi.yaml -g typescript-axios -o ./client-sdk
|
||||
```
|
||||
|
||||
## 📊 API接口概览
|
||||
|
||||
| 接口 | 方法 | 路径 | 描述 |
|
||||
|------|------|------|------|
|
||||
| 用户登录 | POST | /auth/login | 支持用户名、邮箱或手机号登录 |
|
||||
| 用户注册 | POST | /auth/register | 创建新用户账户 |
|
||||
| GitHub OAuth | POST | /auth/github | 使用GitHub账户登录或注册 |
|
||||
| 发送验证码 | POST | /auth/forgot-password | 发送密码重置验证码 |
|
||||
| 重置密码 | POST | /auth/reset-password | 使用验证码重置密码 |
|
||||
| 修改密码 | PUT | /auth/change-password | 修改用户密码 |
|
||||
|
||||
## 🧪 快速测试
|
||||
|
||||
### 使用cURL测试登录接口
|
||||
|
||||
```bash
|
||||
# 测试用户登录
|
||||
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/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "newuser",
|
||||
"password": "password123",
|
||||
"nickname": "新用户",
|
||||
"email": "newuser@example.com"
|
||||
}'
|
||||
```
|
||||
|
||||
### 使用JavaScript测试
|
||||
|
||||
```javascript
|
||||
// 用户登录
|
||||
const response = await fetch('http://localhost:3000/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
identifier: 'testuser',
|
||||
password: 'password123'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **开发环境**: 当前配置适用于开发环境,生产环境需要使用HTTPS
|
||||
2. **认证**: 实际应用中应实现JWT认证机制
|
||||
3. **限流**: 建议对认证接口实施限流策略
|
||||
4. **验证码**: 示例中返回验证码仅用于演示,生产环境不应返回
|
||||
5. **错误处理**: 建议实现统一的错误处理机制
|
||||
|
||||
## 🔄 更新文档
|
||||
|
||||
当API接口发生变化时,请同步更新以下文件:
|
||||
1. 更新DTO类的Swagger装饰器
|
||||
2. 更新 `api-documentation.md`
|
||||
3. 更新 `openapi.yaml`
|
||||
4. 更新 `postman-collection.json`
|
||||
5. 重新生成Swagger文档
|
||||
|
||||
## 🔗 相关链接
|
||||
|
||||
- [NestJS Swagger文档](https://docs.nestjs.com/openapi/introduction)
|
||||
- [OpenAPI规范](https://swagger.io/specification/)
|
||||
- [Postman文档](https://learning.postman.com/docs/getting-started/introduction/)
|
||||
- [Swagger Editor](https://editor.swagger.io/)
|
||||
412
docs/api/api-documentation.md
Normal file
412
docs/api/api-documentation.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# 用户认证API接口文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述了像素游戏服务器的用户认证相关API接口,包括登录、注册、密码找回等功能。
|
||||
|
||||
**基础URL**: `http://localhost:3000`
|
||||
**API文档地址**: `http://localhost:3000/api-docs`
|
||||
|
||||
## 通用响应格式
|
||||
|
||||
所有API接口都遵循统一的响应格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": boolean,
|
||||
"data": object | null,
|
||||
"message": string,
|
||||
"error_code": string | null
|
||||
}
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
|
||||
- `success`: 请求是否成功
|
||||
- `data`: 响应数据(成功时返回)
|
||||
- `message`: 响应消息
|
||||
- `error_code`: 错误代码(失败时返回)
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 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`
|
||||
|
||||
**功能描述**: 创建新用户账户
|
||||
|
||||
#### 请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "testuser",
|
||||
"password": "password123",
|
||||
"nickname": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": "+8613800138000"
|
||||
}
|
||||
```
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| username | string | 是 | 用户名,只能包含字母、数字和下划线,长度1-50字符 |
|
||||
| password | string | 是 | 密码,必须包含字母和数字,长度8-128字符 |
|
||||
| nickname | string | 是 | 用户昵称,长度1-50字符 |
|
||||
| email | string | 否 | 邮箱地址 |
|
||||
| phone | 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": "注册成功"
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应** (409):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "用户名已存在",
|
||||
"error_code": "REGISTER_FAILED"
|
||||
}
|
||||
```
|
||||
|
||||
### 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": "密码修改成功"
|
||||
}
|
||||
```
|
||||
|
||||
## 错误代码说明
|
||||
|
||||
| 错误代码 | 说明 |
|
||||
|----------|------|
|
||||
| LOGIN_FAILED | 登录失败 |
|
||||
| REGISTER_FAILED | 注册失败 |
|
||||
| GITHUB_OAUTH_FAILED | GitHub OAuth失败 |
|
||||
| SEND_CODE_FAILED | 发送验证码失败 |
|
||||
| RESET_PASSWORD_FAILED | 重置密码失败 |
|
||||
| CHANGE_PASSWORD_FAILED | 修改密码失败 |
|
||||
|
||||
## 数据验证规则
|
||||
|
||||
### 用户名规则
|
||||
- 长度:1-50字符
|
||||
- 格式:只能包含字母、数字和下划线
|
||||
- 正则表达式:`^[a-zA-Z0-9_]+$`
|
||||
|
||||
### 密码规则
|
||||
- 长度:8-128字符
|
||||
- 格式:必须包含字母和数字
|
||||
- 正则表达式:`^(?=.*[a-zA-Z])(?=.*\d)`
|
||||
|
||||
### 验证码规则
|
||||
- 长度:6位数字
|
||||
- 正则表达式:`^\d{6}$`
|
||||
|
||||
## 使用示例
|
||||
|
||||
### JavaScript/TypeScript 示例
|
||||
|
||||
```typescript
|
||||
// 用户登录
|
||||
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);
|
||||
}
|
||||
|
||||
// 用户注册
|
||||
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'
|
||||
})
|
||||
});
|
||||
|
||||
const registerData = await registerResponse.json();
|
||||
```
|
||||
|
||||
### cURL 示例
|
||||
|
||||
```bash
|
||||
# 用户登录
|
||||
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/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "newuser",
|
||||
"password": "password123",
|
||||
"nickname": "新用户",
|
||||
"email": "newuser@example.com"
|
||||
}'
|
||||
|
||||
# 发送密码重置验证码
|
||||
curl -X POST http://localhost:3000/auth/forgot-password \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"identifier": "test@example.com"
|
||||
}'
|
||||
|
||||
# 重置密码
|
||||
curl -X POST http://localhost:3000/auth/reset-password \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"identifier": "test@example.com",
|
||||
"verification_code": "123456",
|
||||
"new_password": "newpassword123"
|
||||
}'
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **安全性**: 实际应用中应使用HTTPS协议
|
||||
2. **令牌**: 示例中的access_token是简单的Base64编码,实际应用中应使用JWT
|
||||
3. **验证码**: 实际应用中不应在响应中返回验证码
|
||||
4. **用户ID**: 修改密码接口中的user_id应从JWT令牌中获取,而不是从请求体中传递
|
||||
5. **错误处理**: 建议在客户端实现适当的错误处理和用户提示
|
||||
6. **限流**: 建议对登录、注册等接口实施限流策略
|
||||
|
||||
## 更新日志
|
||||
|
||||
- **v1.0.0** (2025-12-17): 初始版本,包含基础的用户认证功能
|
||||
568
docs/api/openapi.yaml
Normal file
568
docs/api/openapi.yaml
Normal file
@@ -0,0 +1,568 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Pixel Game Server - Auth API
|
||||
description: 像素游戏服务器用户认证API接口文档
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: API Support
|
||||
email: support@example.com
|
||||
license:
|
||||
name: MIT
|
||||
url: https://opensource.org/licenses/MIT
|
||||
|
||||
servers:
|
||||
- url: http://localhost:3000
|
||||
description: 开发环境
|
||||
|
||||
tags:
|
||||
- name: auth
|
||||
description: 用户认证相关接口
|
||||
|
||||
paths:
|
||||
/auth/login:
|
||||
post:
|
||||
tags:
|
||||
- auth
|
||||
summary: 用户登录
|
||||
description: 支持用户名、邮箱或手机号登录
|
||||
operationId: login
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LoginDto'
|
||||
example:
|
||||
identifier: testuser
|
||||
password: password123
|
||||
responses:
|
||||
'200':
|
||||
description: 登录成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LoginResponse'
|
||||
example:
|
||||
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: 登录成功
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'401':
|
||||
description: 用户名或密码错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/auth/register:
|
||||
post:
|
||||
tags:
|
||||
- auth
|
||||
summary: 用户注册
|
||||
description: 创建新用户账户
|
||||
operationId: register
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterDto'
|
||||
example:
|
||||
username: newuser
|
||||
password: password123
|
||||
nickname: 新用户
|
||||
email: newuser@example.com
|
||||
phone: "+8613800138001"
|
||||
responses:
|
||||
'201':
|
||||
description: 注册成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'409':
|
||||
description: 用户名或邮箱已存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/auth/github:
|
||||
post:
|
||||
tags:
|
||||
- auth
|
||||
summary: GitHub OAuth登录
|
||||
description: 使用GitHub账户登录或注册
|
||||
operationId: githubOAuth
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GitHubOAuthDto'
|
||||
example:
|
||||
github_id: "12345678"
|
||||
username: octocat
|
||||
nickname: The Octocat
|
||||
email: octocat@github.com
|
||||
avatar_url: https://github.com/images/error/octocat_happy.gif
|
||||
responses:
|
||||
'200':
|
||||
description: GitHub登录成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GitHubOAuthResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'401':
|
||||
description: GitHub认证失败
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/auth/forgot-password:
|
||||
post:
|
||||
tags:
|
||||
- auth
|
||||
summary: 发送密码重置验证码
|
||||
description: 向用户邮箱或手机发送密码重置验证码
|
||||
operationId: forgotPassword
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ForgotPasswordDto'
|
||||
example:
|
||||
identifier: test@example.com
|
||||
responses:
|
||||
'200':
|
||||
description: 验证码发送成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ForgotPasswordResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 用户不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/auth/reset-password:
|
||||
post:
|
||||
tags:
|
||||
- auth
|
||||
summary: 重置密码
|
||||
description: 使用验证码重置用户密码
|
||||
operationId: resetPassword
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ResetPasswordDto'
|
||||
example:
|
||||
identifier: test@example.com
|
||||
verification_code: "123456"
|
||||
new_password: newpassword123
|
||||
responses:
|
||||
'200':
|
||||
description: 密码重置成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CommonResponse'
|
||||
'400':
|
||||
description: 请求参数错误或验证码无效
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 用户不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/auth/change-password:
|
||||
put:
|
||||
tags:
|
||||
- auth
|
||||
summary: 修改密码
|
||||
description: 用户修改自己的密码(需要提供旧密码)
|
||||
operationId: changePassword
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChangePasswordDto'
|
||||
example:
|
||||
user_id: "1"
|
||||
old_password: oldpassword123
|
||||
new_password: newpassword123
|
||||
responses:
|
||||
'200':
|
||||
description: 密码修改成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CommonResponse'
|
||||
'400':
|
||||
description: 请求参数错误或旧密码不正确
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 用户不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
components:
|
||||
schemas:
|
||||
LoginDto:
|
||||
type: object
|
||||
required:
|
||||
- identifier
|
||||
- password
|
||||
properties:
|
||||
identifier:
|
||||
type: string
|
||||
description: 登录标识符,支持用户名、邮箱或手机号
|
||||
minLength: 1
|
||||
maxLength: 100
|
||||
example: testuser
|
||||
password:
|
||||
type: string
|
||||
description: 用户密码
|
||||
minLength: 1
|
||||
maxLength: 128
|
||||
example: password123
|
||||
|
||||
RegisterDto:
|
||||
type: object
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
- nickname
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: 用户名,只能包含字母、数字和下划线
|
||||
minLength: 1
|
||||
maxLength: 50
|
||||
pattern: '^[a-zA-Z0-9_]+$'
|
||||
example: testuser
|
||||
password:
|
||||
type: string
|
||||
description: 密码,必须包含字母和数字,长度8-128字符
|
||||
minLength: 8
|
||||
maxLength: 128
|
||||
pattern: '^(?=.*[a-zA-Z])(?=.*\d)'
|
||||
example: password123
|
||||
nickname:
|
||||
type: string
|
||||
description: 用户昵称
|
||||
minLength: 1
|
||||
maxLength: 50
|
||||
example: 测试用户
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
description: 邮箱地址(可选)
|
||||
example: test@example.com
|
||||
phone:
|
||||
type: string
|
||||
description: 手机号码(可选)
|
||||
example: "+8613800138000"
|
||||
|
||||
GitHubOAuthDto:
|
||||
type: object
|
||||
required:
|
||||
- github_id
|
||||
- username
|
||||
- nickname
|
||||
properties:
|
||||
github_id:
|
||||
type: string
|
||||
description: GitHub用户ID
|
||||
minLength: 1
|
||||
maxLength: 100
|
||||
example: "12345678"
|
||||
username:
|
||||
type: string
|
||||
description: GitHub用户名
|
||||
minLength: 1
|
||||
maxLength: 50
|
||||
example: octocat
|
||||
nickname:
|
||||
type: string
|
||||
description: GitHub显示名称
|
||||
minLength: 1
|
||||
maxLength: 50
|
||||
example: The Octocat
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
description: GitHub邮箱地址(可选)
|
||||
example: octocat@github.com
|
||||
avatar_url:
|
||||
type: string
|
||||
description: GitHub头像URL(可选)
|
||||
example: https://github.com/images/error/octocat_happy.gif
|
||||
|
||||
ForgotPasswordDto:
|
||||
type: object
|
||||
required:
|
||||
- identifier
|
||||
properties:
|
||||
identifier:
|
||||
type: string
|
||||
description: 邮箱或手机号
|
||||
minLength: 1
|
||||
maxLength: 100
|
||||
example: test@example.com
|
||||
|
||||
ResetPasswordDto:
|
||||
type: object
|
||||
required:
|
||||
- identifier
|
||||
- verification_code
|
||||
- new_password
|
||||
properties:
|
||||
identifier:
|
||||
type: string
|
||||
description: 邮箱或手机号
|
||||
minLength: 1
|
||||
maxLength: 100
|
||||
example: test@example.com
|
||||
verification_code:
|
||||
type: string
|
||||
description: 6位数字验证码
|
||||
pattern: '^\d{6}$'
|
||||
example: "123456"
|
||||
new_password:
|
||||
type: string
|
||||
description: 新密码,必须包含字母和数字,长度8-128字符
|
||||
minLength: 8
|
||||
maxLength: 128
|
||||
pattern: '^(?=.*[a-zA-Z])(?=.*\d)'
|
||||
example: newpassword123
|
||||
|
||||
ChangePasswordDto:
|
||||
type: object
|
||||
required:
|
||||
- user_id
|
||||
- old_password
|
||||
- new_password
|
||||
properties:
|
||||
user_id:
|
||||
type: string
|
||||
description: 用户ID(实际应用中应从JWT令牌中获取)
|
||||
example: "1"
|
||||
old_password:
|
||||
type: string
|
||||
description: 当前密码
|
||||
minLength: 1
|
||||
maxLength: 128
|
||||
example: oldpassword123
|
||||
new_password:
|
||||
type: string
|
||||
description: 新密码,必须包含字母和数字,长度8-128字符
|
||||
minLength: 8
|
||||
maxLength: 128
|
||||
pattern: '^(?=.*[a-zA-Z])(?=.*\d)'
|
||||
example: newpassword123
|
||||
|
||||
UserInfo:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: 用户ID
|
||||
example: "1"
|
||||
username:
|
||||
type: string
|
||||
description: 用户名
|
||||
example: testuser
|
||||
nickname:
|
||||
type: string
|
||||
description: 用户昵称
|
||||
example: 测试用户
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
description: 邮箱地址
|
||||
example: test@example.com
|
||||
phone:
|
||||
type: string
|
||||
description: 手机号码
|
||||
example: "+8613800138000"
|
||||
avatar_url:
|
||||
type: string
|
||||
description: 头像URL
|
||||
example: https://example.com/avatar.jpg
|
||||
role:
|
||||
type: integer
|
||||
description: 用户角色
|
||||
example: 1
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
example: "2025-12-17T10:00:00.000Z"
|
||||
|
||||
LoginResponseData:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
$ref: '#/components/schemas/UserInfo'
|
||||
access_token:
|
||||
type: string
|
||||
description: 访问令牌
|
||||
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
refresh_token:
|
||||
type: string
|
||||
description: 刷新令牌
|
||||
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
is_new_user:
|
||||
type: boolean
|
||||
description: 是否为新用户
|
||||
example: false
|
||||
message:
|
||||
type: string
|
||||
description: 响应消息
|
||||
example: 登录成功
|
||||
|
||||
LoginResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: 请求是否成功
|
||||
example: true
|
||||
data:
|
||||
$ref: '#/components/schemas/LoginResponseData'
|
||||
message:
|
||||
type: string
|
||||
description: 响应消息
|
||||
example: 登录成功
|
||||
|
||||
RegisterResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: 请求是否成功
|
||||
example: true
|
||||
data:
|
||||
$ref: '#/components/schemas/LoginResponseData'
|
||||
message:
|
||||
type: string
|
||||
description: 响应消息
|
||||
example: 注册成功
|
||||
|
||||
GitHubOAuthResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: 请求是否成功
|
||||
example: true
|
||||
data:
|
||||
$ref: '#/components/schemas/LoginResponseData'
|
||||
message:
|
||||
type: string
|
||||
description: 响应消息
|
||||
example: GitHub登录成功
|
||||
|
||||
ForgotPasswordResponseData:
|
||||
type: object
|
||||
properties:
|
||||
verification_code:
|
||||
type: string
|
||||
description: 验证码(仅用于演示,实际应用中不应返回)
|
||||
example: "123456"
|
||||
|
||||
ForgotPasswordResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: 请求是否成功
|
||||
example: true
|
||||
data:
|
||||
$ref: '#/components/schemas/ForgotPasswordResponseData'
|
||||
message:
|
||||
type: string
|
||||
description: 响应消息
|
||||
example: 验证码已发送,请查收
|
||||
|
||||
CommonResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: 请求是否成功
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
description: 响应消息
|
||||
example: 操作成功
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: 请求是否成功
|
||||
example: false
|
||||
message:
|
||||
type: string
|
||||
description: 错误消息
|
||||
example: 操作失败
|
||||
error_code:
|
||||
type: string
|
||||
description: 错误代码
|
||||
example: OPERATION_FAILED
|
||||
347
docs/api/postman-collection.json
Normal file
347
docs/api/postman-collection.json
Normal file
@@ -0,0 +1,347 @@
|
||||
{
|
||||
"info": {
|
||||
"name": "Pixel Game Server - Auth API",
|
||||
"description": "像素游戏服务器用户认证API接口集合",
|
||||
"version": "1.0.0",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"variable": [
|
||||
{
|
||||
"key": "baseUrl",
|
||||
"value": "http://localhost:3000",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"item": [
|
||||
{
|
||||
"name": "用户登录",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"identifier\": \"testuser\",\n \"password\": \"password123\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/login",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "login"]
|
||||
},
|
||||
"description": "用户登录接口,支持用户名、邮箱或手机号登录"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "登录成功",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"identifier\": \"testuser\",\n \"password\": \"password123\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/login",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "login"]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": "{\n \"success\": true,\n \"data\": {\n \"user\": {\n \"id\": \"1\",\n \"username\": \"testuser\",\n \"nickname\": \"测试用户\",\n \"email\": \"test@example.com\",\n \"phone\": \"+8613800138000\",\n \"avatar_url\": \"https://example.com/avatar.jpg\",\n \"role\": 1,\n \"created_at\": \"2025-12-17T10:00:00.000Z\"\n },\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"refresh_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"is_new_user\": false,\n \"message\": \"登录成功\"\n },\n \"message\": \"登录成功\"\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "用户注册",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"username\": \"newuser\",\n \"password\": \"password123\",\n \"nickname\": \"新用户\",\n \"email\": \"newuser@example.com\",\n \"phone\": \"+8613800138001\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/register",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "register"]
|
||||
},
|
||||
"description": "用户注册接口,创建新用户账户"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "注册成功",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"username\": \"newuser\",\n \"password\": \"password123\",\n \"nickname\": \"新用户\",\n \"email\": \"newuser@example.com\",\n \"phone\": \"+8613800138001\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/register",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "register"]
|
||||
}
|
||||
},
|
||||
"status": "Created",
|
||||
"code": 201,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": "{\n \"success\": true,\n \"data\": {\n \"user\": {\n \"id\": \"2\",\n \"username\": \"newuser\",\n \"nickname\": \"新用户\",\n \"email\": \"newuser@example.com\",\n \"phone\": \"+8613800138001\",\n \"avatar_url\": null,\n \"role\": 1,\n \"created_at\": \"2025-12-17T10:00:00.000Z\"\n },\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"is_new_user\": true,\n \"message\": \"注册成功\"\n },\n \"message\": \"注册成功\"\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GitHub OAuth登录",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"github_id\": \"12345678\",\n \"username\": \"octocat\",\n \"nickname\": \"The Octocat\",\n \"email\": \"octocat@github.com\",\n \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/github",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "github"]
|
||||
},
|
||||
"description": "GitHub OAuth登录接口,使用GitHub账户登录或注册"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "GitHub登录成功",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"github_id\": \"12345678\",\n \"username\": \"octocat\",\n \"nickname\": \"The Octocat\",\n \"email\": \"octocat@github.com\",\n \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/github",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "github"]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": "{\n \"success\": true,\n \"data\": {\n \"user\": {\n \"id\": \"3\",\n \"username\": \"octocat\",\n \"nickname\": \"The Octocat\",\n \"email\": \"octocat@github.com\",\n \"phone\": null,\n \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n \"role\": 1,\n \"created_at\": \"2025-12-17T10:00:00.000Z\"\n },\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"is_new_user\": true,\n \"message\": \"GitHub账户绑定成功\"\n },\n \"message\": \"GitHub账户绑定成功\"\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "发送密码重置验证码",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"identifier\": \"test@example.com\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/forgot-password",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "forgot-password"]
|
||||
},
|
||||
"description": "发送密码重置验证码到用户邮箱或手机"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "验证码发送成功",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"identifier\": \"test@example.com\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/forgot-password",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "forgot-password"]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": "{\n \"success\": true,\n \"data\": {\n \"verification_code\": \"123456\"\n },\n \"message\": \"验证码已发送,请查收\"\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "重置密码",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"identifier\": \"test@example.com\",\n \"verification_code\": \"123456\",\n \"new_password\": \"newpassword123\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/reset-password",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "reset-password"]
|
||||
},
|
||||
"description": "使用验证码重置用户密码"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "密码重置成功",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"identifier\": \"test@example.com\",\n \"verification_code\": \"123456\",\n \"new_password\": \"newpassword123\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/reset-password",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "reset-password"]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": "{\n \"success\": true,\n \"message\": \"密码重置成功\"\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "修改密码",
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"user_id\": \"1\",\n \"old_password\": \"oldpassword123\",\n \"new_password\": \"newpassword123\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/change-password",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "change-password"]
|
||||
},
|
||||
"description": "用户修改自己的密码(需要提供旧密码)"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "密码修改成功",
|
||||
"originalRequest": {
|
||||
"method": "PUT",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"user_id\": \"1\",\n \"old_password\": \"oldpassword123\",\n \"new_password\": \"newpassword123\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/auth/change-password",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["auth", "change-password"]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": "{\n \"success\": true,\n \"message\": \"密码修改成功\"\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
"@nestjs/platform-express": "^10.4.20",
|
||||
"@nestjs/platform-socket.io": "^10.4.20",
|
||||
"@nestjs/schedule": "^4.1.2",
|
||||
"@nestjs/swagger": "^11.2.3",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"@nestjs/websockets": "^10.4.20",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
@@ -39,6 +40,7 @@
|
||||
"pino": "^10.1.0",
|
||||
"reflect-metadata": "^0.1.14",
|
||||
"rxjs": "^7.8.2",
|
||||
"swagger-ui-express": "^5.0.1",
|
||||
"typeorm": "^0.3.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
267
src/business/login/login-response.dto.ts
Normal file
267
src/business/login/login-response.dto.ts
Normal file
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* 登录业务响应数据传输对象
|
||||
*
|
||||
* 功能描述:
|
||||
* - 定义登录相关API的响应数据结构
|
||||
* - 提供Swagger文档生成支持
|
||||
* - 确保API响应的数据格式一致性
|
||||
*
|
||||
* @author moyin
|
||||
* @version 1.0.0
|
||||
* @since 2025-12-17
|
||||
*/
|
||||
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
/**
|
||||
* 用户信息响应DTO
|
||||
*/
|
||||
export class UserInfoDto {
|
||||
@ApiProperty({
|
||||
description: '用户ID',
|
||||
example: '1'
|
||||
})
|
||||
id: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '用户名',
|
||||
example: 'testuser'
|
||||
})
|
||||
username: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '用户昵称',
|
||||
example: '测试用户'
|
||||
})
|
||||
nickname: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '邮箱地址',
|
||||
example: 'test@example.com',
|
||||
required: false
|
||||
})
|
||||
email?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '手机号码',
|
||||
example: '+8613800138000',
|
||||
required: false
|
||||
})
|
||||
phone?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '头像URL',
|
||||
example: 'https://example.com/avatar.jpg',
|
||||
required: false
|
||||
})
|
||||
avatar_url?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '用户角色',
|
||||
example: 1
|
||||
})
|
||||
role: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: '创建时间',
|
||||
example: '2025-12-17T10:00:00.000Z'
|
||||
})
|
||||
created_at: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录响应数据DTO
|
||||
*/
|
||||
export class LoginResponseDataDto {
|
||||
@ApiProperty({
|
||||
description: '用户信息',
|
||||
type: UserInfoDto
|
||||
})
|
||||
user: UserInfoDto;
|
||||
|
||||
@ApiProperty({
|
||||
description: '访问令牌',
|
||||
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
||||
})
|
||||
access_token: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '刷新令牌',
|
||||
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
||||
required: false
|
||||
})
|
||||
refresh_token?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '是否为新用户',
|
||||
example: false,
|
||||
required: false
|
||||
})
|
||||
is_new_user?: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应消息',
|
||||
example: '登录成功'
|
||||
})
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录响应DTO
|
||||
*/
|
||||
export class LoginResponseDto {
|
||||
@ApiProperty({
|
||||
description: '请求是否成功',
|
||||
example: true
|
||||
})
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应数据',
|
||||
type: LoginResponseDataDto,
|
||||
required: false
|
||||
})
|
||||
data?: LoginResponseDataDto;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应消息',
|
||||
example: '登录成功'
|
||||
})
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '错误代码',
|
||||
example: 'LOGIN_FAILED',
|
||||
required: false
|
||||
})
|
||||
error_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册响应DTO
|
||||
*/
|
||||
export class RegisterResponseDto {
|
||||
@ApiProperty({
|
||||
description: '请求是否成功',
|
||||
example: true
|
||||
})
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应数据',
|
||||
type: LoginResponseDataDto,
|
||||
required: false
|
||||
})
|
||||
data?: LoginResponseDataDto;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应消息',
|
||||
example: '注册成功'
|
||||
})
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '错误代码',
|
||||
example: 'REGISTER_FAILED',
|
||||
required: false
|
||||
})
|
||||
error_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* GitHub OAuth响应DTO
|
||||
*/
|
||||
export class GitHubOAuthResponseDto {
|
||||
@ApiProperty({
|
||||
description: '请求是否成功',
|
||||
example: true
|
||||
})
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应数据',
|
||||
type: LoginResponseDataDto,
|
||||
required: false
|
||||
})
|
||||
data?: LoginResponseDataDto;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应消息',
|
||||
example: 'GitHub登录成功'
|
||||
})
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '错误代码',
|
||||
example: 'GITHUB_OAUTH_FAILED',
|
||||
required: false
|
||||
})
|
||||
error_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码响应数据DTO
|
||||
*/
|
||||
export class ForgotPasswordResponseDataDto {
|
||||
@ApiProperty({
|
||||
description: '验证码(仅用于演示,实际应用中不应返回)',
|
||||
example: '123456',
|
||||
required: false
|
||||
})
|
||||
verification_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码响应DTO
|
||||
*/
|
||||
export class ForgotPasswordResponseDto {
|
||||
@ApiProperty({
|
||||
description: '请求是否成功',
|
||||
example: true
|
||||
})
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应数据',
|
||||
type: ForgotPasswordResponseDataDto,
|
||||
required: false
|
||||
})
|
||||
data?: ForgotPasswordResponseDataDto;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应消息',
|
||||
example: '验证码已发送,请查收'
|
||||
})
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '错误代码',
|
||||
example: 'SEND_CODE_FAILED',
|
||||
required: false
|
||||
})
|
||||
error_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用响应DTO(用于重置密码、修改密码等)
|
||||
*/
|
||||
export class CommonResponseDto {
|
||||
@ApiProperty({
|
||||
description: '请求是否成功',
|
||||
example: true
|
||||
})
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: '响应消息',
|
||||
example: '操作成功'
|
||||
})
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '错误代码',
|
||||
example: 'OPERATION_FAILED',
|
||||
required: false
|
||||
})
|
||||
error_code?: string;
|
||||
}
|
||||
@@ -20,9 +20,18 @@
|
||||
*/
|
||||
|
||||
import { Controller, Post, Put, Body, HttpCode, HttpStatus, ValidationPipe, UsePipes, Logger } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse as SwaggerApiResponse, ApiBody } from '@nestjs/swagger';
|
||||
import { LoginService, ApiResponse, LoginResponse } from './login.service';
|
||||
import { LoginDto, RegisterDto, GitHubOAuthDto, ForgotPasswordDto, ResetPasswordDto, ChangePasswordDto } from './login.dto';
|
||||
import {
|
||||
LoginResponseDto,
|
||||
RegisterResponseDto,
|
||||
GitHubOAuthResponseDto,
|
||||
ForgotPasswordResponseDto,
|
||||
CommonResponseDto
|
||||
} from './login-response.dto';
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('auth')
|
||||
export class LoginController {
|
||||
private readonly logger = new Logger(LoginController.name);
|
||||
@@ -35,6 +44,24 @@ export class LoginController {
|
||||
* @param loginDto 登录数据
|
||||
* @returns 登录结果
|
||||
*/
|
||||
@ApiOperation({
|
||||
summary: '用户登录',
|
||||
description: '支持用户名、邮箱或手机号登录'
|
||||
})
|
||||
@ApiBody({ type: LoginDto })
|
||||
@SwaggerApiResponse({
|
||||
status: 200,
|
||||
description: '登录成功',
|
||||
type: LoginResponseDto
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 400,
|
||||
description: '请求参数错误'
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 401,
|
||||
description: '用户名或密码错误'
|
||||
})
|
||||
@Post('login')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
@@ -51,6 +78,24 @@ export class LoginController {
|
||||
* @param registerDto 注册数据
|
||||
* @returns 注册结果
|
||||
*/
|
||||
@ApiOperation({
|
||||
summary: '用户注册',
|
||||
description: '创建新用户账户'
|
||||
})
|
||||
@ApiBody({ type: RegisterDto })
|
||||
@SwaggerApiResponse({
|
||||
status: 201,
|
||||
description: '注册成功',
|
||||
type: RegisterResponseDto
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 400,
|
||||
description: '请求参数错误'
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 409,
|
||||
description: '用户名或邮箱已存在'
|
||||
})
|
||||
@Post('register')
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
@@ -70,6 +115,24 @@ export class LoginController {
|
||||
* @param githubDto GitHub OAuth数据
|
||||
* @returns 登录结果
|
||||
*/
|
||||
@ApiOperation({
|
||||
summary: 'GitHub OAuth登录',
|
||||
description: '使用GitHub账户登录或注册'
|
||||
})
|
||||
@ApiBody({ type: GitHubOAuthDto })
|
||||
@SwaggerApiResponse({
|
||||
status: 200,
|
||||
description: 'GitHub登录成功',
|
||||
type: GitHubOAuthResponseDto
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 400,
|
||||
description: '请求参数错误'
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 401,
|
||||
description: 'GitHub认证失败'
|
||||
})
|
||||
@Post('github')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
@@ -89,6 +152,24 @@ export class LoginController {
|
||||
* @param forgotPasswordDto 忘记密码数据
|
||||
* @returns 发送结果
|
||||
*/
|
||||
@ApiOperation({
|
||||
summary: '发送密码重置验证码',
|
||||
description: '向用户邮箱或手机发送密码重置验证码'
|
||||
})
|
||||
@ApiBody({ type: ForgotPasswordDto })
|
||||
@SwaggerApiResponse({
|
||||
status: 200,
|
||||
description: '验证码发送成功',
|
||||
type: ForgotPasswordResponseDto
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 400,
|
||||
description: '请求参数错误'
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 404,
|
||||
description: '用户不存在'
|
||||
})
|
||||
@Post('forgot-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
@@ -102,6 +183,24 @@ export class LoginController {
|
||||
* @param resetPasswordDto 重置密码数据
|
||||
* @returns 重置结果
|
||||
*/
|
||||
@ApiOperation({
|
||||
summary: '重置密码',
|
||||
description: '使用验证码重置用户密码'
|
||||
})
|
||||
@ApiBody({ type: ResetPasswordDto })
|
||||
@SwaggerApiResponse({
|
||||
status: 200,
|
||||
description: '密码重置成功',
|
||||
type: CommonResponseDto
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 400,
|
||||
description: '请求参数错误或验证码无效'
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 404,
|
||||
description: '用户不存在'
|
||||
})
|
||||
@Post('reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
@@ -119,6 +218,24 @@ export class LoginController {
|
||||
* @param changePasswordDto 修改密码数据
|
||||
* @returns 修改结果
|
||||
*/
|
||||
@ApiOperation({
|
||||
summary: '修改密码',
|
||||
description: '用户修改自己的密码(需要提供旧密码)'
|
||||
})
|
||||
@ApiBody({ type: ChangePasswordDto })
|
||||
@SwaggerApiResponse({
|
||||
status: 200,
|
||||
description: '密码修改成功',
|
||||
type: CommonResponseDto
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 400,
|
||||
description: '请求参数错误或旧密码不正确'
|
||||
})
|
||||
@SwaggerApiResponse({
|
||||
status: 404,
|
||||
description: '用户不存在'
|
||||
})
|
||||
@Put('change-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
Matches,
|
||||
IsNumberString
|
||||
} from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
/**
|
||||
* 登录请求DTO
|
||||
@@ -30,6 +31,12 @@ export class LoginDto {
|
||||
* 登录标识符
|
||||
* 支持用户名、邮箱或手机号登录
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '登录标识符,支持用户名、邮箱或手机号',
|
||||
example: 'testuser',
|
||||
minLength: 1,
|
||||
maxLength: 100
|
||||
})
|
||||
@IsString({ message: '登录标识符必须是字符串' })
|
||||
@IsNotEmpty({ message: '登录标识符不能为空' })
|
||||
@Length(1, 100, { message: '登录标识符长度需在1-100字符之间' })
|
||||
@@ -38,6 +45,12 @@ export class LoginDto {
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '用户密码',
|
||||
example: 'password123',
|
||||
minLength: 1,
|
||||
maxLength: 128
|
||||
})
|
||||
@IsString({ message: '密码必须是字符串' })
|
||||
@IsNotEmpty({ message: '密码不能为空' })
|
||||
@Length(1, 128, { message: '密码长度需在1-128字符之间' })
|
||||
@@ -51,6 +64,13 @@ export class RegisterDto {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '用户名,只能包含字母、数字和下划线',
|
||||
example: 'testuser',
|
||||
minLength: 1,
|
||||
maxLength: 50,
|
||||
pattern: '^[a-zA-Z0-9_]+$'
|
||||
})
|
||||
@IsString({ message: '用户名必须是字符串' })
|
||||
@IsNotEmpty({ message: '用户名不能为空' })
|
||||
@Length(1, 50, { message: '用户名长度需在1-50字符之间' })
|
||||
@@ -60,6 +80,12 @@ export class RegisterDto {
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '密码,必须包含字母和数字,长度8-128字符',
|
||||
example: 'password123',
|
||||
minLength: 8,
|
||||
maxLength: 128
|
||||
})
|
||||
@IsString({ message: '密码必须是字符串' })
|
||||
@IsNotEmpty({ message: '密码不能为空' })
|
||||
@Length(8, 128, { message: '密码长度需在8-128字符之间' })
|
||||
@@ -69,6 +95,12 @@ export class RegisterDto {
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '用户昵称',
|
||||
example: '测试用户',
|
||||
minLength: 1,
|
||||
maxLength: 50
|
||||
})
|
||||
@IsString({ message: '昵称必须是字符串' })
|
||||
@IsNotEmpty({ message: '昵称不能为空' })
|
||||
@Length(1, 50, { message: '昵称长度需在1-50字符之间' })
|
||||
@@ -77,6 +109,11 @@ export class RegisterDto {
|
||||
/**
|
||||
* 邮箱(可选)
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '邮箱地址(可选)',
|
||||
example: 'test@example.com',
|
||||
required: false
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEmail({}, { message: '邮箱格式不正确' })
|
||||
email?: string;
|
||||
@@ -84,6 +121,11 @@ export class RegisterDto {
|
||||
/**
|
||||
* 手机号(可选)
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '手机号码(可选)',
|
||||
example: '+8613800138000',
|
||||
required: false
|
||||
})
|
||||
@IsOptional()
|
||||
@IsPhoneNumber(null, { message: '手机号格式不正确' })
|
||||
phone?: string;
|
||||
@@ -96,6 +138,12 @@ export class GitHubOAuthDto {
|
||||
/**
|
||||
* GitHub用户ID
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: 'GitHub用户ID',
|
||||
example: '12345678',
|
||||
minLength: 1,
|
||||
maxLength: 100
|
||||
})
|
||||
@IsString({ message: 'GitHub ID必须是字符串' })
|
||||
@IsNotEmpty({ message: 'GitHub ID不能为空' })
|
||||
@Length(1, 100, { message: 'GitHub ID长度需在1-100字符之间' })
|
||||
@@ -104,6 +152,12 @@ export class GitHubOAuthDto {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: 'GitHub用户名',
|
||||
example: 'octocat',
|
||||
minLength: 1,
|
||||
maxLength: 50
|
||||
})
|
||||
@IsString({ message: '用户名必须是字符串' })
|
||||
@IsNotEmpty({ message: '用户名不能为空' })
|
||||
@Length(1, 50, { message: '用户名长度需在1-50字符之间' })
|
||||
@@ -112,6 +166,12 @@ export class GitHubOAuthDto {
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: 'GitHub显示名称',
|
||||
example: 'The Octocat',
|
||||
minLength: 1,
|
||||
maxLength: 50
|
||||
})
|
||||
@IsString({ message: '昵称必须是字符串' })
|
||||
@IsNotEmpty({ message: '昵称不能为空' })
|
||||
@Length(1, 50, { message: '昵称长度需在1-50字符之间' })
|
||||
@@ -120,6 +180,11 @@ export class GitHubOAuthDto {
|
||||
/**
|
||||
* 邮箱(可选)
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: 'GitHub邮箱地址(可选)',
|
||||
example: 'octocat@github.com',
|
||||
required: false
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEmail({}, { message: '邮箱格式不正确' })
|
||||
email?: string;
|
||||
@@ -127,6 +192,11 @@ export class GitHubOAuthDto {
|
||||
/**
|
||||
* 头像URL(可选)
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: 'GitHub头像URL(可选)',
|
||||
example: 'https://github.com/images/error/octocat_happy.gif',
|
||||
required: false
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString({ message: '头像URL必须是字符串' })
|
||||
avatar_url?: string;
|
||||
@@ -139,6 +209,12 @@ export class ForgotPasswordDto {
|
||||
/**
|
||||
* 邮箱或手机号
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '邮箱或手机号',
|
||||
example: 'test@example.com',
|
||||
minLength: 1,
|
||||
maxLength: 100
|
||||
})
|
||||
@IsString({ message: '标识符必须是字符串' })
|
||||
@IsNotEmpty({ message: '邮箱或手机号不能为空' })
|
||||
@Length(1, 100, { message: '标识符长度需在1-100字符之间' })
|
||||
@@ -152,6 +228,12 @@ export class ResetPasswordDto {
|
||||
/**
|
||||
* 邮箱或手机号
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '邮箱或手机号',
|
||||
example: 'test@example.com',
|
||||
minLength: 1,
|
||||
maxLength: 100
|
||||
})
|
||||
@IsString({ message: '标识符必须是字符串' })
|
||||
@IsNotEmpty({ message: '邮箱或手机号不能为空' })
|
||||
@Length(1, 100, { message: '标识符长度需在1-100字符之间' })
|
||||
@@ -160,6 +242,11 @@ export class ResetPasswordDto {
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '6位数字验证码',
|
||||
example: '123456',
|
||||
pattern: '^\\d{6}$'
|
||||
})
|
||||
@IsString({ message: '验证码必须是字符串' })
|
||||
@IsNotEmpty({ message: '验证码不能为空' })
|
||||
@Matches(/^\d{6}$/, { message: '验证码必须是6位数字' })
|
||||
@@ -168,6 +255,12 @@ export class ResetPasswordDto {
|
||||
/**
|
||||
* 新密码
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '新密码,必须包含字母和数字,长度8-128字符',
|
||||
example: 'newpassword123',
|
||||
minLength: 8,
|
||||
maxLength: 128
|
||||
})
|
||||
@IsString({ message: '新密码必须是字符串' })
|
||||
@IsNotEmpty({ message: '新密码不能为空' })
|
||||
@Length(8, 128, { message: '新密码长度需在8-128字符之间' })
|
||||
@@ -183,6 +276,10 @@ export class ChangePasswordDto {
|
||||
* 用户ID
|
||||
* 实际应用中应从JWT令牌中获取,这里为了演示放在请求体中
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '用户ID(实际应用中应从JWT令牌中获取)',
|
||||
example: '1'
|
||||
})
|
||||
@IsNumberString({}, { message: '用户ID必须是数字字符串' })
|
||||
@IsNotEmpty({ message: '用户ID不能为空' })
|
||||
user_id: string;
|
||||
@@ -190,6 +287,12 @@ export class ChangePasswordDto {
|
||||
/**
|
||||
* 旧密码
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '当前密码',
|
||||
example: 'oldpassword123',
|
||||
minLength: 1,
|
||||
maxLength: 128
|
||||
})
|
||||
@IsString({ message: '旧密码必须是字符串' })
|
||||
@IsNotEmpty({ message: '旧密码不能为空' })
|
||||
@Length(1, 128, { message: '旧密码长度需在1-128字符之间' })
|
||||
@@ -198,6 +301,12 @@ export class ChangePasswordDto {
|
||||
/**
|
||||
* 新密码
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: '新密码,必须包含字母和数字,长度8-128字符',
|
||||
example: 'newpassword123',
|
||||
minLength: 8,
|
||||
maxLength: 128
|
||||
})
|
||||
@IsString({ message: '新密码必须是字符串' })
|
||||
@IsNotEmpty({ message: '新密码不能为空' })
|
||||
@Length(8, 128, { message: '新密码长度需在8-128字符之间' })
|
||||
|
||||
@@ -200,8 +200,8 @@ export class LoggerConfigFactory {
|
||||
statusCode: res.statusCode,
|
||||
statusMessage: res.statusMessage,
|
||||
headers: {
|
||||
'content-type': res.getHeader('content-type'),
|
||||
'content-length': res.getHeader('content-length'),
|
||||
'content-type': res.getHeader ? res.getHeader('content-type') : res.headers?.['content-type'],
|
||||
'content-length': res.getHeader ? res.getHeader('content-length') : res.headers?.['content-length'],
|
||||
},
|
||||
responseTime: res.responseTime,
|
||||
}),
|
||||
|
||||
30
src/main.ts
30
src/main.ts
@@ -1,9 +1,11 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
// 全局启用校验管道(核心配置)
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
@@ -12,8 +14,36 @@ async function bootstrap() {
|
||||
transform: true, // 自动把入参转为 DTO 对应的类型(比如前端传的字符串数字 `'1'` 转为数字 `1`)
|
||||
}),
|
||||
);
|
||||
|
||||
// 配置Swagger文档
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Pixel Game Server API')
|
||||
.setDescription('像素游戏服务器API文档 - 包含用户认证、登录注册等功能')
|
||||
.setVersion('1.0.0')
|
||||
.addTag('auth', '用户认证相关接口')
|
||||
.addBearerAuth(
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
name: 'JWT',
|
||||
description: '请输入JWT token',
|
||||
in: 'header',
|
||||
},
|
||||
'JWT-auth',
|
||||
)
|
||||
.build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api-docs', app, document, {
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
},
|
||||
});
|
||||
|
||||
await app.listen(3000);
|
||||
console.log('Pixel Game Server is running on http://localhost:3000');
|
||||
console.log('API Documentation is available at http://localhost:3000/api-docs');
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
||||
Reference in New Issue
Block a user