348 lines
8.1 KiB
Markdown
348 lines
8.1 KiB
Markdown
# 用户视图文档
|
|
|
|
## 📄 **文件位置**
|
|
`user/views/user.py`
|
|
|
|
### **概述**
|
|
处理用户认证和邮件验证。所有视图都已转换为异步操作并使用标准化响应代码以提高性能和可维护性。
|
|
|
|
### **类结构**
|
|
|
|
```python
|
|
@permission_classes([AllowAny])
|
|
class SendUserEmailAPIView(APIView):
|
|
async def post(self, request): # 发送邮件
|
|
|
|
@permission_classes([AllowAny])
|
|
class UserLoginOrRegisterAPIView(APIView):
|
|
async def post(self, request): # 登录/注册
|
|
```
|
|
|
|
#### **权限**
|
|
- **访问级别**: 公共(无需认证)
|
|
- **认证**: 无 (`@permission_classes([AllowAny])`)
|
|
|
|
### **方法详情**
|
|
|
|
---
|
|
|
|
### **1. SendUserEmailAPIView (邮件验证)**
|
|
|
|
#### **端点**: `POST /api/send-email/`
|
|
|
|
##### **描述**
|
|
为用户注册或登录发送验证码邮件。生成并存储 8 位验证码到缓存中进行验证。
|
|
|
|
##### **参数**
|
|
```json
|
|
{
|
|
"to_email": "user@example.com" // 必需:收件人邮箱地址
|
|
}
|
|
```
|
|
|
|
##### **响应格式**
|
|
|
|
**成功 - 注册邮件:**
|
|
```json
|
|
{
|
|
"message": "账号未注册,已发送注册邮件",
|
|
"code": "10001",
|
|
"data": {
|
|
"email_sent": true,
|
|
"type": "register"
|
|
}
|
|
}
|
|
```
|
|
|
|
**成功 - 登录邮件:**
|
|
```json
|
|
{
|
|
"message": "邮件发送成功",
|
|
"code": "10002",
|
|
"data": {
|
|
"email_sent": true,
|
|
"type": "login"
|
|
}
|
|
}
|
|
```
|
|
|
|
##### **逻辑流程**
|
|
1. 验证邮箱参数
|
|
2. 检查数据库中用户是否存在
|
|
3. 如果用户不存在: 发送注册邮件
|
|
4. 如果用户存在: 发送登录邮件
|
|
5. 将验证码存储到 Redis 缓存,超时时间 10 分钟
|
|
6. 返回适当的成功响应
|
|
|
|
##### **错误响应**
|
|
|
|
| 代码 | 状态 | 描述 |
|
|
|------|------|------|
|
|
| 20002 | Bad Request | 邮箱参数缺失或为空 |
|
|
| 20003 | Internal Server Error | 邮件发送失败 |
|
|
|
|
---
|
|
|
|
### **2. UserLoginOrRegisterAPIView (认证)**
|
|
|
|
#### **端点**: `POST /api/login-register/`
|
|
|
|
##### **描述**
|
|
使用邮箱验证码处理用户注册和登录流程。支持 JWT token 生成用于认证会话。
|
|
|
|
##### **参数**
|
|
```json
|
|
{
|
|
"email": "user@example.com", // 必需:用户邮箱地址
|
|
"code": "12345678" // 必需:验证码
|
|
}
|
|
```
|
|
|
|
##### **响应格式**
|
|
|
|
**注册成功:**
|
|
```json
|
|
{
|
|
"message": "注册成功",
|
|
"code": "10003",
|
|
"data": {
|
|
"user": {
|
|
"id": 1,
|
|
"email": "user@example.com",
|
|
"username": "example_user"
|
|
},
|
|
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"token_type": "bearer",
|
|
"expires_in": 604800 // 7 天秒数
|
|
}
|
|
}
|
|
```
|
|
|
|
**登录成功:**
|
|
```json
|
|
{
|
|
"message": "登录成功",
|
|
"code": "10004",
|
|
"data": {
|
|
"user": {
|
|
"id": 1,
|
|
"email": "user@example.com",
|
|
"username": "example_user"
|
|
},
|
|
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"token_type": "bearer",
|
|
"expires_in": 604800 // 7 天秒数
|
|
}
|
|
}
|
|
```
|
|
|
|
##### **逻辑流程**
|
|
|
|
**注册流程:**
|
|
1. 验证邮箱和验证码参数
|
|
2. 从数据库获取用户 (期望为 None 表示注册)
|
|
3. 从缓存检索验证码 (`register_{email}`)
|
|
4. 验证验证码匹配
|
|
5. 通过序列化器验证用户数据
|
|
6. 创建用户账户
|
|
7. 生成 JWT tokens
|
|
8. 返回成功响应
|
|
|
|
**登录流程:**
|
|
1. 验证邮箱和验证码参数
|
|
2. 从数据库获取现有用户
|
|
3. 从缓存检索验证码 (`login_{email}`)
|
|
4. 验证验证码匹配
|
|
5. 为现有用户生成 JWT tokens
|
|
6. 返回成功响应
|
|
|
|
##### **错误响应**
|
|
|
|
| 代码 | 状态 | 描述 |
|
|
|------|------|------|
|
|
| 20001 | Bad Request | 参数验证错误 |
|
|
| 20004 | Bad Request | 注册验证码过期 |
|
|
| 20005 | Bad Request | 用户数据验证失败 |
|
|
| 20006 | Bad Request | 注册验证码不正确 |
|
|
| 20007 | Bad Request | 登录验证码过期 |
|
|
| 20008 | Bad Request | 登录验证码不正确 |
|
|
| 20009 | Internal Server Error | 服务器内部错误 |
|
|
|
|
---
|
|
|
|
### **实现详情**
|
|
|
|
#### **异步操作**
|
|
所有数据库操作都使用 `sync_to_async` 进行非阻塞执行:
|
|
|
|
```python
|
|
# 数据库查询变为异步
|
|
user = await sync_to_async(FUser.objects.filter)(email=to_email).first()
|
|
```
|
|
|
|
#### **邮件系统**
|
|
- 使用 Django 的 `EmailMessage` 类
|
|
- 从 `cs10086086@qq.com` 发送
|
|
- 验证码存储在 Redis 中,超时时间 10 分钟
|
|
- 单独的缓存键: `register_{email}` 和 `login_{email}`
|
|
|
|
#### **Token 系统**
|
|
- 使用 Django REST Framework Simple JWT
|
|
- Access token 7 天后过期
|
|
- 实现了 refresh token 系统
|
|
- Token payload 包含过期时间计算
|
|
|
|
#### **数据验证**
|
|
- UserSerializer 处理数据验证
|
|
- create_by_email 方法处理用户创建
|
|
- 对无效数据的综合错误处理
|
|
|
|
---
|
|
|
|
### **安全与性能**
|
|
|
|
#### **验证码安全**
|
|
- 8 位数字码随机生成
|
|
- 10 分钟过期窗口
|
|
- 一次性使用 (验证后消耗)
|
|
- 存储在安全的 Redis 缓存中
|
|
|
|
#### **速率限制考虑**
|
|
- 无内置速率限制
|
|
- 考虑实现客户端侧限制
|
|
- Redis 可用于分布式速率限制
|
|
|
|
#### **输入验证**
|
|
- 邮箱格式验证
|
|
- 验证码长度和格式验证
|
|
- 参数存在性验证
|
|
- 数据库约束强制执行
|
|
|
|
#### **错误处理**
|
|
- 综合 try-catch 块
|
|
- 有意义的错误消息
|
|
- 适当的 HTTP 状态码
|
|
- 不泄露敏感信息
|
|
|
|
---
|
|
|
|
### **使用示例**
|
|
|
|
#### **Python 客户端**
|
|
```python
|
|
import requests
|
|
|
|
# 发送注册邮件
|
|
response = requests.post('/api/send-email/', json={'to_email': 'test@example.com'})
|
|
if response.status_code == 201:
|
|
print("注册邮件已发送")
|
|
|
|
# 注册用户
|
|
response = requests.post('/api/login-register/', json={
|
|
'email': 'test@example.com',
|
|
'code': '12345678'
|
|
})
|
|
if response.status_code == 200:
|
|
tokens = response.json()['data']
|
|
print(f"Access token: {tokens['access']}")
|
|
```
|
|
|
|
#### **JavaScript 前端**
|
|
```javascript
|
|
// 发送验证邮件
|
|
const sendVerificationEmail = async (email) => {
|
|
const response = await fetch('/api/send-email/', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({to_email: email})
|
|
});
|
|
return response.json();
|
|
};
|
|
|
|
// 注册用户
|
|
const registerUser = async (email, code, userData) => {
|
|
const response = await fetch('/api/login-register/', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({email, code, ...userData})
|
|
});
|
|
return response.json();
|
|
};
|
|
```
|
|
|
|
#### **测试端点**
|
|
```bash
|
|
# 发送邮件验证
|
|
curl -X POST http://localhost:8000/api/send-email/ \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"to_email":"test@example.com"}'
|
|
|
|
# 注册用户
|
|
curl -X POST http://localhost:8000/api/login-register/ \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"email":"test@example.com","code":"12345678","username":"testuser"}'
|
|
|
|
# 登录用户
|
|
curl -X POST http://localhost:8000/api/login-register/ \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"email":"test@example.com","code":"87654321"}'
|
|
```
|
|
|
|
---
|
|
|
|
### **集成说明**
|
|
|
|
#### **Django URL 配置**
|
|
```python
|
|
# urls.py
|
|
from django.urls import path
|
|
from user.views.user import SendUserEmailAPIView, UserLoginOrRegisterAPIView
|
|
|
|
urlpatterns = [
|
|
path('send-email/', SendUserEmailAPIView.as_view(), name='send-email'),
|
|
path('login-register/', UserLoginOrRegisterAPIView.as_view(), name='login-register'),
|
|
]
|
|
```
|
|
|
|
#### **前端集成**
|
|
- 使用响应 `data.access` 进行 API 认证
|
|
- 存储 `data.refresh` 用于 token 刷新
|
|
- 处理不同的消息代码以提供用户反馈
|
|
- 实现倒计时器用于验证码过期
|
|
|
|
#### **移动应用集成**
|
|
- 相同的 API 端点适用于移动应用
|
|
- 为网络问题添加适当的错误处理
|
|
- 实现离线缓存以获得更好的用户体验
|
|
|
|
#### **第三方服务**
|
|
- 邮件服务: QQ Mail SMTP
|
|
- 认证: JWT-based
|
|
- 存储: MySQL + Redis 缓存
|
|
|
|
---
|
|
|
|
### **监控与调试**
|
|
|
|
#### **日志记录**
|
|
- 邮件发送操作已记录
|
|
- 参数验证错误已记录
|
|
- 考虑为生产环境添加结构化日志记录
|
|
|
|
#### **指标**
|
|
- 邮件送达成功率
|
|
- 注册完成率
|
|
- 登录成功率
|
|
- Token 刷新模式
|
|
|
|
#### **健康检查**
|
|
- Redis 连接性
|
|
- SMTP 服务器可用性
|
|
- 数据库连接池
|
|
|
|
---
|
|
|
|
**最后更新**: 当前会话
|
|
**版本**: 1.0 |