签入版本

This commit is contained in:
2026-09-12 23:12:15 +08:00
commit 91998056ca
1841 changed files with 341325 additions and 0 deletions
@@ -0,0 +1,504 @@
# 第四章 架构设计 - 核心基类体系
## 4.1 基类总览
核心基类体系由四大基类组成,覆盖从数据模型到请求校验再到响应输出的完整链路。子类只需声明差异点,即可获得标准化的增删改查能力。
```
BaseModel (application/models.py)
|
+-- 通用字段: id / create_user / create_time / update_user / update_time / is_delete
+-- 字段顺序重排: class_prepared 信号
|
v
Example(BaseModel) <-- 业务模型继承
PermissionRequired (middleware/permission_middleware.py)
|
+-- RBAC权限验证: has_permission()
+-- 超级管理员放行: user_id == 1
+-- 无权限处理: handle_no_permission()
|
v
ExamplePageView(PermissionRequired, View) <-- 视图类混入
check_login (middleware/login_middleware.py)
|
+-- JWT认证: parse_payload()
+-- 忽略URL: /login, /captcha
+-- 认证失败: R.failed(code=401)
|
v
@method_decorator(check_login, name="get") <-- 视图方法装饰器
R (utils/R.py)
|
+-- ok(): 成功响应
+-- failed(): 失败响应
+-- response(): 通用响应
|
v
JsonResponse <-- Django HTTP响应
```
## 4.2 BaseModel 模型基类
**文件位置**:`application/models.py`
### 设计目标
为所有业务模型提供统一的通用字段和字段顺序管理,消除重复定义。
### 通用字段
```python
class BaseModel(models.Model):
"""基础模型抽象类"""
# 主键ID字段
id = models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='主键ID',
db_comment='主键ID'
)
# 创建人字段
create_user = models.CharField(
null=True,
max_length=50,
default=None,
db_default=None,
verbose_name='创建人',
db_comment='创建人'
)
# 创建时间字段
create_time = models.DateTimeField(
null=True,
auto_now_add=True,
verbose_name="创建时间",
max_length=11,
db_comment='创建时间'
)
# 更新人字段
update_user = models.CharField(
null=True,
max_length=50,
default=None,
db_default=None,
verbose_name='更新人',
db_comment='更新人'
)
# 更新时间字段
update_time = models.DateTimeField(
null=True,
auto_now=True,
verbose_name="更新时间",
max_length=11,
db_comment='更新时间'
)
# 逻辑删除标识
is_delete = models.BooleanField(
default=0,
db_default=0,
verbose_name="逻辑删除",
db_comment='逻辑删除:0-正常 1-已删除'
)
class Meta:
abstract = True # 抽象模型,不创建数据库表
```
### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | AutoField, PK | 主键自增 |
| `create_user` | CharField(50) | 创建人(用户名) |
| `create_time` | DateTimeField | 创建时间(auto_now_add) |
| `update_user` | CharField(50) | 更新人(用户名) |
| `update_time` | DateTimeField | 更新时间(auto_now) |
| `is_delete` | BooleanField | 软删除标识:0=正常,1=已删除 |
### 字段顺序重排
通过 `class_prepared` 信号,自动将公共字段移到业务字段之后:
```python
BASE_FIELD_NAMES = ['id', 'create_user', 'create_time',
'update_user', 'update_time', 'is_delete']
def _reorder_fields(sender, **kwargs):
"""信号处理函数:重排模型字段顺序"""
if not hasattr(sender, '_meta') or sender._meta.abstract:
return
# 分离公共字段和业务字段
base_fields = []
business_fields = []
for field in sender._meta.local_fields:
if field.name in BASE_FIELD_NAMES:
base_fields.append(field)
else:
business_fields.append(field)
# 重新排列:业务字段在前,公共字段在后
sender._meta.local_fields = business_fields + base_fields
# 注册信号处理器
class_prepared.connect(_reorder_fields)
```
### 使用示例
```python
from application.models import BaseModel
from utils.common import get_table_name
class Example(BaseModel):
"""案例模型类"""
name = models.CharField(max_length=100, verbose_name="案例名称")
status = models.IntegerField(verbose_name="状态")
class Meta:
db_table = get_table_name('example')
ordering = ("sort",)
```
继承 BaseModel 后,Example 模型自动拥有 6 个公共字段,无需手动定义。
## 4.3 check_login 认证装饰器
**文件位置**:`middleware/login_middleware.py`
### 设计目标
提供基于 JWT 的用户登录状态验证装饰器,保护需要登录才能访问的视图函数。
### 核心实现
```python
from utils import R
from utils.jwt import parse_payload
def check_login(func):
"""JWT登录认证装饰器"""
def wrapper(request, *args, **kwargs):
# 定义不需要认证的URL列表
ignoreURL = ['/login', '/captcha']
# 判断当前请求是否需要验证登录
if request.path not in ignoreURL:
# 从请求头中获取token值
access_token = request.headers['Authorization']
# 移除"Bearer "前缀
access_token = access_token.replace('Bearer ', "")
# JWT解密验证
result = parse_payload(access_token)
# token验证失败处理
code = result['code']
if code != 0:
return R.failed(code=401, msg=result['msg'])
# 认证通过,继续执行原视图函数
return func(request, *args, **kwargs)
return wrapper
```
### 认证流程
```
1. 检查请求路径是否在忽略列表中
+-- 是 -> 直接执行视图函数
+-- 否 -> 继续验证
2. 从请求头获取 Authorization 值
+-- 移除 "Bearer " 前缀
+-- 获取纯净的 token 字符串
3. 调用 parse_payload() 验证 token
+-- 检查 Token 是否在黑名单中
+-- 解密验证签名和有效期
+-- 返回验证结果
4. 根据验证结果决定是否放行
+-- code == 0 -> 放行
+-- code != 0 -> 返回 401 错误
```
### 使用方式
```python
from django.utils.decorators import method_decorator
from middleware.login_middleware import check_login
# 在类视图中使用
@method_decorator(check_login, name="get")
class ExamplePageView(View):
def get(self, request):
pass
# 在函数视图中使用
@check_login
def user_info(request):
pass
```
## 4.4 PermissionRequired 权限混入类
**文件位置**:`middleware/permission_middleware.py`
### 设计目标
提供基于 RBAC 的细粒度权限控制,通过检查用户是否拥有指定权限节点来决定是否允许访问。
### 核心实现
```python
from django.contrib.auth.mixins import PermissionRequiredMixin
from utils import R
from utils.security import get_user_id
class PermissionRequired(PermissionRequiredMixin):
"""自定义权限控制混入类"""
def has_permission(self):
"""检查用户是否拥有所需权限"""
# 获取所需权限节点列表
permissions = self.get_permission_required()
# 获取当前登录用户ID
user_id = get_user_id(self.request)
# 超级管理员(ID=1)自动放行
if user_id and user_id != 1:
from application.menu import services
# 获取用户权限节点
permission_list = services.get_user_permissions(user_id)
# 遍历所需权限列表
for permission in permissions:
if permission not in permission_list:
return False
return True
def handle_no_permission(self):
"""无权限访问时的处理函数"""
return R.failed("暂无操作权限", 401)
```
### 权限验证流程
```
1. 获取视图所需的权限节点列表
permission_required = ("sys:example:page",)
2. 获取当前登录用户ID
user_id = get_user_id(request)
3. 判断是否为超级管理员
+-- user_id == 1 -> 直接放行
+-- user_id != 1 -> 继续验证
4. 获取用户的实际权限列表
permission_list = services.get_user_permissions(user_id)
5. 比对所需权限是否在用户权限列表中
+-- 全部存在 -> 放行
+-- 有缺失 -> 返回 401 错误
```
### 权限获取逻辑
```python
# application/menu/services.py
def get_user_permissions(user_id):
"""获取用户权限节点列表"""
if user_id == 1:
# 超级管理员拥有所有权限
menu_list = Menu.objects.filter(
is_delete=False, type=1, status=0
).values()
return [item['permission'] for item in menu_list]
else:
# 普通用户从角色关联中获取权限
sql = '''
SELECT m.* FROM django_menu AS m
INNER JOIN django_role_menu AS rm ON m.id=rm.menu_id
INNER JOIN django_user_role AS ur ON ur.role_id=rm.role_id
WHERE ur.user_id=%s
AND (m.type=1 OR (m.type=0 AND m.permission!=''))
AND m.status=0 AND m.is_delete=0
'''
menu_list = Menu.objects.raw(sql, [user_id])
return [item.permission for item in menu_list]
```
### 使用方式
```python
from django.views import View
from middleware.permission_middleware import PermissionRequired
class ExamplePageView(PermissionRequired, View):
"""案例分页数据查询视图"""
# 声明所需权限节点
permission_required = ("sys:example:page",)
def get(self, request):
pass
```
## 4.5 R 统一响应类
**文件位置**:`utils/R.py`
### 设计目标
提供系统统一的 JSON 响应格式,确保所有 API 接口返回格式一致。
### 响应格式规范
```json
{
"code": 0, // 状态码:0-成功,1-失败,401-未授权
"data": {}, // 响应数据
"msg": "操作成功", // 提示信息
"ok": true // 操作是否成功
}
```
### 核心实现
```python
from django.http import JsonResponse
def ok(data=None, msg="操作成功", code=0, **kwargs):
"""生成成功响应"""
response_body = {
"code": code,
"data": data,
"msg": msg,
"ok": True
}
if kwargs:
response_body.update(kwargs)
return JsonResponse(data=response_body)
def failed(msg="操作失败", code=1, data=None, **kwargs):
"""生成失败响应"""
response_body = {
"code": code,
"data": data,
"msg": msg,
"ok": False
}
if kwargs:
response_body.update(kwargs)
return JsonResponse(data=response_body)
def response(data=None, msg="操作成功", code=0, success=True, **kwargs):
"""通用响应生成器"""
response_body = {
"code": code,
"data": data,
"msg": msg,
"ok": success
}
if kwargs:
response_body.update(kwargs)
return JsonResponse(data=response_body)
```
### 使用示例
```python
from utils import R
# 成功响应
return R.ok(data={"id": 1, "name": "张三"})
# {"code": 0, "data": {"id": 1, "name": "张三"}, "msg": "操作成功", "ok": true}
# 分页响应
return R.ok(data=page_data, total=100, pages=10)
# {"code": 0, "data": {...}, "msg": "操作成功", "ok": true, "total": 100, "pages": 10}
# 失败响应
return R.failed(msg="用户名或密码错误")
# {"code": 1, "data": null, "msg": "用户名或密码错误", "ok": false}
# 认证失败响应
return R.failed(code=401, msg="token已失效")
# {"code": 401, "data": null, "msg": "token已失效", "ok": false}
```
### 状态码常量
```python
class Codes:
"""常用状态码定义"""
SUCCESS = 0
FAILED = 1
UNAUTHORIZED = 401 # 未认证
FORBIDDEN = 403 # 无权限
NOT_FOUND = 404 # 资源不存在
VALIDATE_ERROR = 422 # 验证错误
SERVER_ERROR = 500 # 服务器错误
```
## 4.6 基类协作关系
### 请求处理中的基类协作
```
HTTP Request
|
v
@check_login (认证装饰器)
|
+-- 验证JWT Token
+-- 失败 -> R.failed(code=401)
|
v
PermissionRequired (权限混入类)
|
+-- 验证权限节点
+-- 失败 -> R.failed("暂无操作权限", 401)
|
v
View.get/post (视图方法)
|
+-- 调用 Service 函数
|
v
Service函数
|
+-- 使用 BaseModel 子类进行数据库操作
+-- 成功 -> R.ok(data=...)
+-- 失败 -> R.failed(msg=...)
|
v
JsonResponse (HTTP响应)
```
### 各基类职责总结
| 基类 | 文件位置 | 核心职责 |
|------|----------|----------|
| `BaseModel` | `application/models.py` | 提供6个通用字段,字段顺序重排 |
| `check_login` | `middleware/login_middleware.py` | JWT认证,验证登录状态 |
| `PermissionRequired` | `middleware/permission_middleware.py` | RBAC权限控制,细粒度权限验证 |
| `R` | `utils/R.py` | 统一响应格式,JSON响应生成 |
@@ -0,0 +1,238 @@
# 第四章 架构设计 - 缓存架构
## 4.1 缓存架构概述
项目使用 Redis 作为缓存后端,通过 Django 的缓存框架实现统一的缓存管理。缓存主要用于以下场景:
```
+------------------+
| Django App |
+------------------+
|
v
+------------------+
| Django Cache | <-- Django缓存框架
| Framework |
+------------------+
|
v
+------------------+
| django-redis | <-- Redis缓存后端
+------------------+
|
v
+------------------+
| Redis | <-- Redis服务器
+------------------+
```
## 4.2 Redis 配置
### 缓存后端配置
在 `application/settings.py` 中配置 Redis 缓存:
```python
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{env.REDIS_HOST}:{env.REDIS_PORT}/{env.REDIS_DB}",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": env.REDIS_PASSWORD if env.REDIS_PASSWORD else None,
"SOCKET_CONNECT_TIMEOUT": 5, # 连接超时(秒)
"SOCKET_TIMEOUT": 5, # 读写超时(秒)
"CONNECTION_POOL_KWARGS": {"max_connections": 100}, # 连接池大小
"IGNORE_EXCEPTIONS": True, # Redis不可用时不抛异常,降级处理
},
"KEY_PREFIX": "djangoadmin", # Key前缀,避免多项目共用Redis时冲突
}
}
```
### Redis 连接参数
在 `config/env.py` 中定义 Redis 连接参数:
```python
REDIS_HOST = os.getenv('REDIS_HOST', '127.0.0.1')
REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))
REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', '123456')
REDIS_DB = int(os.getenv('REDIS_DB', 0))
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `REDIS_HOST` | `127.0.0.1` | Redis服务器地址 |
| `REDIS_PORT` | `6379` | Redis端口 |
| `REDIS_PASSWORD` | `123456` | Redis密码 |
| `REDIS_DB` | `0` | Redis数据库索引(0-15) |
## 4.3 缓存应用场景
### 4.3.1 Token 黑名单
退出登录时将 Token 加入黑名单,使 Token 立即失效。
**实现代码**(`utils/jwt.py`):
```python
_TOKEN_BLACKLIST_PREFIX = 'token:blacklist:'
def _token_fingerprint(token: str) -> str:
"""生成Token指纹(SHA256摘要)"""
return hashlib.sha256(token.encode('utf-8')).hexdigest()
def add_token_to_blacklist(token: str) -> bool:
"""将Token加入黑名单"""
from django_redis import get_redis_connection
redis = get_redis_connection("default")
# 获取Token剩余有效期
expiration = get_token_expiration(token)
if expiration is None:
ttl_seconds = DEFAULT_TIMEOUT_MINUTES * 60
else:
ttl_seconds = int(
(expiration - datetime.datetime.now(tz=datetime.timezone.utc)).total_seconds()
)
if ttl_seconds <= 0:
return True # Token已自然过期,无需加入黑名单
# 使用SHA256指纹作为Key,避免存储完整Token
fingerprint = _token_fingerprint(token)
key = f'{_TOKEN_BLACKLIST_PREFIX}{fingerprint}'
redis.set(key, '1', ex=ttl_seconds)
return True
def is_token_blacklisted(token: str) -> bool:
"""检查Token是否在黑名单中"""
from django_redis import get_redis_connection
redis = get_redis_connection("default")
fingerprint = _token_fingerprint(token)
key = f'{_TOKEN_BLACKLIST_PREFIX}{fingerprint}'
return redis.exists(key) > 0
```
**Key格式**:`djangoadmin:token:blacklist:<sha256_fingerprint>`
**TTL策略**:与 Token 剩余有效期一致,Token 自然过期后黑名单条目自动清除。
**降级策略**:Redis 不可用时采用 fail-open 策略放行请求,避免全部请求被拒。
### 4.3.2 IP 地理位置缓存
缓存 IP 地理位置查询结果,避免重复查询外部 API 或本地数据库。
**配置项**(`config/env.py`):
```python
# IP地理位置信息来源:'local' - 本地数据库, 'api' - 在线API
IP_LOCATION_SOURCE = os.environ.get('IP_LOCATION_SOURCE', 'api')
# 是否启用IP地理位置缓存
IP_CACHE_ENABLED = os.environ.get('IP_CACHE_ENABLED', 'True') == 'True'
# IP缓存时间(秒),默认1天
IP_CACHE_TIMEOUT = int(os.environ.get('IP_CACHE_TIMEOUT', 86400))
```
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `IP_LOCATION_SOURCE` | `api` | IP查询来源:本地数据库或在线API |
| `IP_CACHE_ENABLED` | `True` | 是否启用缓存 |
| `IP_CACHE_TIMEOUT` | `86400` | 缓存过期时间(秒),默认1天 |
### 4.3.3 验证码存储
图形验证码通过 Redis 存储,支持自动过期。
**配置项**(`application/settings.py`):
```python
CAPTCHA_IMAGE_SIZE = (200, 60) # 验证码图片尺寸
CAPTCHA_LENGTH = 6 # 验证码字符个数
CAPTCHA_TIMEOUT = 1 # 验证码超时时间(分钟)
CAPTCHA_FONT_SIZE = 40 # 验证码字体大小
CAPTCHA_FOREGROUND_COLOR = "#1d953f" # 验证码前景色
CAPTCHA_BACKGROUND_COLOR = "#ffffff" # 验证码背景色
```
## 4.4 缓存 Key 命名规范
所有缓存 Key 统一使用 `djangoadmin:` 前缀(通过 `KEY_PREFIX` 配置),避免多项目共用 Redis 时冲突。
| 场景 | Key格式 | TTL | 说明 |
|------|---------|-----|------|
| Token黑名单 | `djangoadmin:token:blacklist:<fingerprint>` | Token剩余有效期 | SHA256指纹 |
| IP位置缓存 | `djangoadmin:ip:location:<ip>` | 86400秒 | IP查询结果 |
| 验证码 | `djangoadmin:captcha:<key>` | 60秒 | 图形验证码 |
## 4.5 缓存降级策略
### Redis 不可用时的处理
系统配置了 `IGNORE_EXCEPTIONS: True`,当 Redis 不可用时:
1. **Token黑名单检查**:采用 fail-open 策略,放行请求(已注销的Token可能被误放行)
2. **IP位置缓存**:跳过缓存,直接查询数据源
3. **验证码**:降级为内存存储或其他方案
```python
# Token黑名单检查的降级处理
def is_token_blacklisted(token: str) -> bool:
try:
from django_redis import get_redis_connection
redis = get_redis_connection("default")
fingerprint = _token_fingerprint(token)
key = f'{_TOKEN_BLACKLIST_PREFIX}{fingerprint}'
return redis.exists(key) > 0
except Exception as e:
# fail-open: Redis不可用时放行
_log_blacklist_check_alert(e)
return False
```
### 告警节流
Redis 故障期间,采用节流告警避免日志刷屏:
```python
_BLACKLIST_ALERT_INTERVAL = 60.0 # 告警间隔(秒)
_blacklist_check_last_alert = 0.0
def _log_blacklist_check_alert(error: Exception) -> None:
"""节流记录黑名单检查失败告警"""
global _blacklist_check_last_alert
now = time.monotonic()
if now - _blacklist_check_last_alert >= _BLACKLIST_ALERT_INTERVAL:
_blacklist_check_last_alert = now
logger.error("Token黑名单检查异常,已按 fail-open 放行: %s", error)
else:
logger.debug("Token黑名单检查异常(节流中,跳过 ERROR): %s", error)
```
## 4.6 缓存最佳实践
### Key 设计原则
1. **使用前缀**:所有 Key 使用统一前缀,便于管理和清理
2. **避免冲突**:不同业务使用不同的 Key 格式
3. **合理TTL**:根据业务场景设置合适的过期时间
4. **使用指纹**:敏感数据(如Token)使用摘要值作为Key
### 性能优化
1. **连接池**:配置 `max_connections: 100`,复用连接
2. **超时控制**:连接和读写超时各5秒,避免长时间阻塞
3. **批量操作**:尽量使用 `pipeline` 减少网络往返
4. **降级处理**:Redis 不可用时不影响核心业务
### 安全考虑
1. **密码保护**:Redis 连接使用密码认证
2. **网络隔离**:生产环境 Redis 应限制内网访问
3. **Key加密**:敏感数据的 Key 使用 SHA256 摘要
4. **定期清理**:依赖 TTL 自动过期,避免内存溢出
@@ -0,0 +1,228 @@
# 数据库设计指南
:::tip 说明
本文介绍本项目的数据库设计规范,包括表命名、字段类型选择、索引策略、常见表模式等。
:::
## 表命名规范
| 规则 | 说明 | 示例 |
|------|------|------|
| 前缀 | 统一使用 `django_` 前缀 | `django_user` |
| 命名 | 小写字母 + 下划线 | `django_role_menu` |
| 关联表 | 两张表名拼接 | `django_user_role` |
| 日志表 | 模块名 + `_log` | `django_login_log` |
### Django 模型配置
```python
class Example(BaseModel):
class Meta:
db_table = 'django_example' # 表名
db_comment = '案例表' # 表注释
```
## 字段类型选择
### 常用字段类型
| Django 类型 | MySQL 类型 | 适用场景 | 示例 |
|------------|-----------|---------|------|
| `AutoField` | INT AUTO_INCREMENT | 主键 | `id` |
| `CharField` | VARCHAR | 短文本 | 用户名、标题 |
| `TextField` | TEXT | 长文本 | 文章内容、备注 |
| `IntegerField` | INT | 整数 | 状态、排序、数量 |
| `BigIntegerField` | BIGINT | 大整数 | 时间戳、耗时 |
| `DecimalField` | DECIMAL | 精确小数 | 金额、价格 |
| `DateTimeField` | DATETIME | 日期时间 | 创建时间、更新时间 |
| `BooleanField` | TINYINT | 布尔值 | 是否启用 |
### 字段长度建议
| 字段类型 | 建议长度 | 说明 |
|---------|---------|------|
| 用户名 | 50 | `CharField(max_length=50)` |
| 标题 | 150-255 | `CharField(max_length=150)` |
| URL/路径 | 255 | `CharField(max_length=255)` |
| 编码/标识 | 100 | `CharField(max_length=100)` |
| 手机号 | 20 | `CharField(max_length=20)` |
| 邮箱 | 100 | `CharField(max_length=100)` |
| 备注 | 255 | `CharField(max_length=255)` |
| 内容 | 无限制 | `TextField()` |
## BaseModel 公共字段
所有业务模型继承 `BaseModel`,自动包含 6 个公共字段:
```python
# application/models.py
class BaseModel(models.Model):
"""基础模型类"""
class Meta:
abstract = True
id = models.AutoField(primary_key=True, verbose_name='主键ID')
create_user = models.CharField(max_length=50, null=True, blank=True, verbose_name='创建人')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_user = models.CharField(max_length=50, null=True, blank=True, verbose_name='更新人')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
is_delete = models.IntegerField(default=0, verbose_name='是否删除:0-正常 1-已删除')
```
:::tip 字段排序
通过 `class_prepared` 信号,BaseModel 的公共字段会自动排在业务字段之后,保证数据库表结构的可读性。
:::
## 索引设计原则
### 索引类型
| 类型 | Django 配置 | 说明 |
|------|------------|------|
| 单列索引 | `db_index=True` | 单字段查询 |
| 唯一索引 | `unique=True` | 唯一性约束 |
| 复合索引 | `index_together` | 多字段联合查询 |
### 索引设计建议
```python
class Example(BaseModel):
name = models.CharField(max_length=100, db_index=True) # 常用查询字段
code = models.CharField(max_length=100, unique=True) # 唯一编码
status = models.IntegerField(db_index=True) # 状态字段
category_id = models.IntegerField(db_index=True) # 外键字段
class Meta:
db_table = 'django_example'
index_together = [
['status', 'is_delete'], # 复合索引:状态 + 软删除
['category_id', 'sort'], # 复合索引:分类 + 排序
]
```
### 索引注意事项
```
1. 不要过度索引:每个索引都会降低写入性能
2. 选择性高的字段优先:username 比 status 选择性更高
3. 复合索引顺序:选择性高的字段放前面
4. 覆盖索引:查询字段都在索引中时,无需回表
5. 定期分析:使用 EXPLAIN 分析慢查询
```
## 常见表模式
### 树形结构表
```python
class Category(BaseModel):
"""树形结构表(parent_id)"""
name = models.CharField(max_length=100)
parent_id = models.IntegerField(default=0) # 0 表示顶级
sort = models.IntegerField(default=0)
class Meta:
db_table = 'django_category'
```
### 关联表(多对多)
```python
class RoleMenu(BaseModel):
"""角色菜单关联表"""
role_id = models.IntegerField(db_index=True)
menu_id = models.IntegerField(db_index=True)
class Meta:
db_table = 'django_role_menu'
```
### 配置表(KV 结构)
```python
class ConfigItem(BaseModel):
"""配置项表(键值对)"""
code = models.CharField(max_length=100, unique=True) # 配置编码
value = models.CharField(max_length=1000) # 配置值
type = models.CharField(max_length=50) # 值类型
class Meta:
db_table = 'django_config_item'
```
### 日志表
```python
class LoginLog(BaseModel):
"""登录日志表"""
username = models.CharField(max_length=50, db_index=True)
ip = models.CharField(max_length=50)
status = models.IntegerField(default=0)
log_type = models.IntegerField(default=1)
class Meta:
db_table = 'django_login_log'
```
## 数据库迁移最佳实践
### 创建迁移
```bash
# 为指定应用创建迁移
python manage.py makemigrations example
# 为所有应用创建迁移
python manage.py makemigrations
```
### 执行迁移
```bash
# 执行所有未执行的迁移
python manage.py migrate
# 执行指定应用的迁移
python manage.py migrate example
# 回滚到指定迁移
python manage.py migrate example 0001
```
### 迁移注意事项
```
1. 先备份:生产环境执行迁移前先备份数据库
2. 测试环境验证:先在测试环境验证迁移脚本
3. 分批执行:大数据表的迁移分批执行,避免锁表
4. 不要修改已提交的迁移:已提交到 Git 的迁移文件不要修改
5. 生成迁移后检查:检查生成的迁移文件是否符合预期
```
## 多数据库支持
项目通过 `DATABASE_ENGINE` 环境变量支持多种数据库:
```bash
# .env
# 可选值:mysql / postgresql / sqlite / oracle / sqlserver
DATABASE_ENGINE=mysql
DATABASE_NAME=djangoadmin
DATABASE_HOST=127.0.0.1
DATABASE_PORT=3306
DATABASE_USER=root
DATABASE_PASSWORD=root
```
### 数据库方言差异
| 特性 | MySQL | PostgreSQL | SQLite |
|------|-------|-----------|--------|
| 自增主键 | AUTO_INCREMENT | SERIAL | AUTOINCREMENT |
| 布尔类型 | TINYINT | BOOLEAN | INTEGER |
| JSON 字段 | JSON | JSONB | TEXT |
| 全文索引 | FULLTEXT | GIN/GiST | FTS5 |
## 总结
数据库设计指南涵盖表命名规范、字段类型选择、索引设计、常见表模式、迁移最佳实践、多数据库支持等方面。核心原则:统一命名、合理索引、软删除、公共字段复用。
@@ -0,0 +1,308 @@
# 第四章 架构设计 - 数据库设计
## 4.1 数据库设计概述
项目使用 MySQL 8.0 作为主数据库,通过 Django ORM 进行数据访问。所有业务表继承 BaseModel 抽象基类,自动获得 6 个通用字段。
## 4.2 通用字段规范
### BaseModel 字段定义
所有业务表继承 `BaseModel`(`application/models.py`),自动包含以下字段:
```python
class BaseModel(models.Model):
"""基础模型抽象类"""
id = models.AutoField(
auto_created=True, primary_key=True, serialize=False,
verbose_name='主键ID', db_comment='主键ID'
)
create_user = models.CharField(
null=True, max_length=50, default=None, db_default=None,
verbose_name='创建人', db_comment='创建人'
)
create_time = models.DateTimeField(
null=True, auto_now_add=True,
verbose_name="创建时间", db_comment='创建时间'
)
update_user = models.CharField(
null=True, max_length=50, default=None, db_default=None,
verbose_name='更新人', db_comment='更新人'
)
update_time = models.DateTimeField(
null=True, auto_now=True,
verbose_name="更新时间", db_comment='更新时间'
)
is_delete = models.BooleanField(
default=0, db_default=0,
verbose_name="逻辑删除",
db_comment='逻辑删除:0-正常 1-已删除'
)
class Meta:
abstract = True
```
### 字段说明
| 字段 | 类型 | 约束 | 默认值 | 说明 |
|------|------|------|--------|------|
| `id` | AutoField | PK, 自增 | - | 主键ID |
| `create_user` | CharField(50) | NULL | None | 创建人用户名 |
| `create_time` | DateTimeField | NULL | auto_now_add | 创建时间,自动填充 |
| `update_user` | CharField(50) | NULL | None | 更新人用户名 |
| `update_time` | DateTimeField | NULL | auto_now | 更新时间,自动刷新 |
| `is_delete` | BooleanField | NOT NULL | 0 | 逻辑删除标识 |
### 字段自动管理
- `create_time`:使用 `auto_now_add=True`,仅在创建时自动设置当前时间
- `update_time`:使用 `auto_now=True`,每次保存时自动更新为当前时间
- `create_user` 和 `update_user`:需要业务代码手动赋值
## 4.3 表名命名规范
### 表前缀配置
所有业务表使用统一的前缀,通过 `config/env.py` 配置:
```python
DATABASE_PREFIX = os.getenv('DATABASE_PREFIX', "django_")
```
### 表名生成函数
使用 `get_table_name()` 函数自动生成带前缀的表名:
```python
# utils/common.py
def get_table_name(name):
"""获取带前缀的表名"""
from config.env import DATABASE_PREFIX
return f"{DATABASE_PREFIX}{name}"
```
### 表名示例
| 模块 | 配置的表名 | 实际表名 |
|------|-----------|----------|
| 案例 | `get_table_name('example')` | `django_example` |
| 用户 | `get_table_name('user')` | `django_user` |
| 角色 | `get_table_name('role')` | `django_role` |
| 菜单 | `get_table_name('menu')` | `django_menu` |
| 部门 | `get_table_name('dept')` | `django_dept` |
| 字典 | `get_table_name('dict')` | `django_dict` |
## 4.4 软删除设计
### 设计原理
系统采用软删除策略,所有业务表包含 `is_delete` 字段:
- `is_delete = 0`:正常记录(默认值)
- `is_delete = 1`:已删除记录
### 软删除实现
```python
# Service层删除操作
def delete_examples(instance_ids):
"""删除案例(批量删除)"""
id_list = [int(id_str.strip())
for id_str in instance_ids.split(',') if id_str.strip()]
# 批量更新删除标识(逻辑删除)
updated_count = Example.objects.filter(
id__in=id_list, is_delete=False
).update(is_delete=True)
return R.ok(msg=f"本次共删除{updated_count}条数据")
```
### 查询过滤
所有查询都需要过滤已删除的记录:
```python
# 分页查询
query = Example.objects.filter(is_delete=False)
# 详情查询
instance = Example.objects.filter(id=id, is_delete=False).first()
# 列表查询
queryset = Example.objects.filter(is_delete=False, status=1)
```
### 软删除优势
| 优势 | 说明 |
|------|------|
| 数据可恢复 | 误删除的数据可以恢复 |
| 审计追踪 | 保留删除记录的历史 |
| 关联完整 | 不破坏外键关联关系 |
| 操作安全 | 避免物理删除造成的数据丢失 |
## 4.5 字段顺序重排
### 设计目标
通过 Django 的 `class_prepared` 信号,自动将 BaseModel 的公共字段移到业务字段之后,使数据库表结构更清晰。
### 实现代码
```python
# application/models.py
BASE_FIELD_NAMES = ['id', 'create_user', 'create_time',
'update_user', 'update_time', 'is_delete']
def _reorder_fields(sender, **kwargs):
"""信号处理函数:重排模型字段顺序"""
if not hasattr(sender, '_meta') or sender._meta.abstract:
return
# 检查是否有公共字段
has_base_fields = any(
f.name in BASE_FIELD_NAMES for f in sender._meta.local_fields
)
if not has_base_fields:
return
# 分离公共字段和业务字段
base_fields = []
business_fields = []
for field in sender._meta.local_fields:
if field.name in BASE_FIELD_NAMES:
base_fields.append(field)
else:
business_fields.append(field)
# 重新排列:业务字段在前,公共字段在后
sender._meta.local_fields = business_fields + base_fields
# 注册信号处理器
class_prepared.connect(_reorder_fields)
```
### 重排效果
```
重排前:id, create_user, create_time, name, status, sort, update_user, update_time, is_delete
重排后:name, status, sort, id, create_user, create_time, update_user, update_time, is_delete
```
## 4.6 数据库表结构示例
### 案例表 (django_example)
```sql
CREATE TABLE `django_example` (
`name` varchar(100) NOT NULL COMMENT '案例名称',
`avatar` varchar(255) DEFAULT NULL COMMENT '案例图片',
`type` int NOT NULL COMMENT '案例类型:1-类型1 2-类型2 3-类型3 4-类型4',
`status` int NOT NULL COMMENT '案例状态:1-正常 2-禁用',
`sort` int NOT NULL COMMENT '排序',
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`create_user` varchar(50) DEFAULT NULL COMMENT '创建人',
`create_time` datetime(6) DEFAULT NULL COMMENT '创建时间',
`update_user` varchar(50) DEFAULT NULL COMMENT '更新人',
`update_time` datetime(6) DEFAULT NULL COMMENT '更新时间',
`is_delete` tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-正常 1-已删除',
PRIMARY KEY (`id`),
KEY `idx_example_name` (`name`)
) ENGINE=InnoDB COMMENT='案例表';
```
### 用户表 (django_user)
```sql
CREATE TABLE `django_user` (
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(128) NOT NULL COMMENT '密码',
`realname` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`status` int NOT NULL DEFAULT 1 COMMENT '状态:1-正常 2-禁用',
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`create_user` varchar(50) DEFAULT NULL COMMENT '创建人',
`create_time` datetime(6) DEFAULT NULL COMMENT '创建时间',
`update_user` varchar(50) DEFAULT NULL COMMENT '更新人',
`update_time` datetime(6) DEFAULT NULL COMMENT '更新时间',
`is_delete` tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-正常 1-已删除',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_username` (`username`)
) ENGINE=InnoDB COMMENT='用户表';
```
## 4.7 字段类型映射
| Django 字段 | MySQL 类型 | 说明 |
|-------------|-----------|------|
| `AutoField` | `INT AUTO_INCREMENT` | 自增主键 |
| `CharField` | `VARCHAR(n)` | 变长字符串 |
| `TextField` | `LONGTEXT` | 长文本 |
| `IntegerField` | `INT` | 整数 |
| `BooleanField` | `TINYINT(1)` | 布尔值 |
| `DateTimeField` | `DATETIME(6)` | 日期时间 |
| `DecimalField` | `DECIMAL(p,s)` | 精确小数 |
| `ImageField` | `VARCHAR(255)` | 图片路径 |
| `ForeignKey` | `INT` + 外键约束 | 外键关联 |
## 4.8 索引设计规范
### 自动索引
- 主键 `id` 自动创建主键索引
- 唯一约束字段自动创建唯一索引
### 手动索引
在模型定义中使用 `db_index=True` 为常用查询字段创建索引:
```python
name = models.CharField(
max_length=100,
db_index=True, # 创建索引
verbose_name="案例名称"
)
```
### 索引命名规范
```
idx_<表名>_<字段名>
```
示例:`idx_example_name`、`idx_user_username`
## 4.9 数据库迁移
### 生成迁移文件
```bash
python manage.py makemigrations <app_name>
```
### 执行迁移
```bash
python manage.py migrate
```
### 代码生成器自动迁移
代码生成器在生成新模块时会自动执行迁移:
```python
# generator_config.py
os.system('python manage.py makemigrations')
os.system('python manage.py migrate')
```
@@ -0,0 +1,235 @@
# 第四章 架构设计 - 目录结构设计
## 4.1 项目根目录
```
DjangoAdmin_Django_EleVue/
|
+-- application/ # Django后端应用主目录
+-- middleware/ # 自定义中间件
+-- utils/ # 工具函数库
+-- config/ # 配置文件目录
+-- constant/ # 常量定义
+-- extends/ # 扩展功能
+-- public/ # 公共资源
| +-- static/ # 静态文件
| +-- templates/ # 代码生成模板
| +-- uploads/ # 上传文件存储
+-- data/ # 数据文件
+-- ui/ # Vue前端项目
+-- manage.py # Django管理脚本
+-- generator.py # 代码生成器CLI
+-- config.json # 代码生成配置
+-- requirements.txt # Python依赖
+-- .env # 环境变量配置
+-- CLAUDE.md # Claude Code指导文件
```
## 4.2 后端应用目录 (application/)
```
application/
|
+-- __init__.py # 包初始化
+-- settings.py # Django项目配置
+-- urls.py # 主路由配置
+-- models.py # BaseModel抽象基类
+-- constants.py # 项目常量定义
+-- asgi.py # ASGI入口
+-- wsgi.py # WSGI入口
|
+-- login/ # 登录模块
+-- index/ # 首页模块
+-- user/ # 用户管理模块
+-- role/ # 角色管理模块
+-- menu/ # 菜单管理模块
+-- dept/ # 部门管理模块
+-- dict/ # 字典管理模块
+-- config/ # 配置管理模块
+-- job/ # 定时任务模块
+-- article/ # 文章管理模块
+-- example/ # 案例模块(参考示例)
+-- generator/ # 代码生成器模块
+-- operation_log/ # 操作日志模块
+-- login_log/ # 登录日志模块
+-- upload/ # 文件上传模块
+-- ... # 其他业务模块
```
## 4.3 标准模块目录结构
每个业务模块遵循统一的文件结构:
```
application/<module>/
|
+-- __init__.py # 包初始化文件
+-- apps.py # Django应用配置
+-- models.py # 数据模型定义
+-- forms.py # 表单验证类
+-- services.py # 业务逻辑函数
+-- views.py # 视图处理类
+-- urls.py # URL路由配置
+-- admin.py # Admin后台注册
+-- constants.py # 模块常量(可选)
+-- migrations/ # 数据库迁移文件
+-- __init__.py
+-- 0001_initial.py
```
### 文件职责说明
| 文件 | 职责 | 典型内容 |
|------|------|----------|
| `models.py` | 数据模型定义 | 继承 BaseModel,定义业务字段 |
| `forms.py` | 表单验证 | ModelForm 子类,字段验证规则 |
| `services.py` | 业务逻辑 | CRUD 操作,分页查询,数据处理 |
| `views.py` | 视图处理 | 类视图,装饰器,权限检查 |
| `urls.py` | 路由配置 | path() 映射,URL 到视图的绑定 |
| `apps.py` | 应用配置 | AppConfig 子类,应用名称 |
| `admin.py` | Admin注册 | 模型在 Django Admin 的注册 |
## 4.4 中间件目录 (middleware/)
```
middleware/
|
+-- __init__.py # 包初始化
+-- login_middleware.py # JWT登录认证装饰器
+-- permission_middleware.py # 权限控制混入类
```
### 中间件文件说明
| 文件 | 功能 | 核心类/函数 |
|------|------|-------------|
| `login_middleware.py` | JWT认证 | `check_login` 装饰器 |
| `permission_middleware.py` | RBAC权限 | `PermissionRequired` 混入类 |
## 4.5 工具函数目录 (utils/)
```
utils/
|
+-- __init__.py # 包初始化
+-- R.py # 统一响应格式
+-- jwt.py # JWT令牌处理
+-- security.py # 用户安全工具
+-- common.py # 通用工具函数
+-- regular.py # 正则验证工具
+-- password.py # 密码处理工具
+-- file.py # 文件处理工具
+-- ip_location.py # IP地理位置查询
+-- ip2region.py # IP数据库查询
+-- ip2region.xdb # IP数据库文件
```
### 工具文件说明
| 文件 | 功能 | 核心函数 |
|------|------|----------|
| `R.py` | 统一响应 | `R.ok()`, `R.failed()`, `R.response()` |
| `jwt.py` | JWT处理 | `create_token()`, `parse_payload()`, `refresh_token()` |
| `security.py` | 用户信息 | `get_user_id()`, `get_username()` |
| `common.py` | 通用工具 | `parse_request_body()`, `format_datetime()`, `get_file_url()` |
| `regular.py` | 表单验证 | `get_err()` |
| `password.py` | 密码处理 | `hash_password()`, `verify_password()` |
## 4.6 配置目录 (config/)
```
config/
|
+-- __init__.py # 包初始化
+-- env.py # 环境变量配置
```
### 配置项说明
`config/env.py` 定义了所有可配置的环境变量:
```python
# 应用配置
DJANGO_NAME = os.getenv('DJANGO_NAME', 'Django+EleVue旗舰版')
DJANGO_VERSION = os.getenv('DJANGO_VERSION', 'v3.0.0')
DJANGO_DEBUG = (os.getenv('DJANGO_DEBUG', 'True') == 'True')
DJANGO_DEMO = (os.getenv('DJANGO_DEMO', 'True') == 'True')
# 数据库配置
DATABASE_ENGINE = os.getenv('DATABASE_ENGINE', "django.db.backends.mysql")
DATABASE_NAME = os.getenv('DATABASE_NAME', 'djangoadmin.django.elevue')
DATABASE_HOST = os.getenv('DATABASE_HOST', "127.0.0.1")
DATABASE_PORT = os.getenv('DATABASE_PORT', 3306)
DATABASE_PREFIX = os.getenv('DATABASE_PREFIX', "django_")
# Redis配置
REDIS_HOST = os.getenv('REDIS_HOST', '127.0.0.1')
REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))
REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', '123456')
REDIS_DB = int(os.getenv('REDIS_DB', 0))
# IP地理位置配置
IP_LOCATION_SOURCE = os.environ.get('IP_LOCATION_SOURCE', 'api')
IP_CACHE_ENABLED = os.environ.get('IP_CACHE_ENABLED', 'True') == 'True'
IP_CACHE_TIMEOUT = int(os.environ.get('IP_CACHE_TIMEOUT', 86400))
```
## 4.7 前端目录 (ui/)
```
ui/
|
+-- public/ # 静态资源
+-- src/
| +-- api/ # API接口定义
| | +-- tool/ # 业务API模块
| | +-- example.ts # 案例API
| | +-- user.ts # 用户API
| |
| +-- views/ # 页面组件
| | +-- tool/ # 业务页面
| | +-- example/ # 案例页面
| | | +-- index.vue # 列表页
| | | +-- edit.vue # 编辑页
| | | +-- detail.vue # 详情页
| |
| +-- components/ # 公共组件
| | +-- BasicTable/ # 表格组件
| | +-- BasicForm/ # 表单组件
| | +-- PageWrapper/ # 页面容器
| |
| +-- router/ # 路由配置
| +-- store/ # Pinia状态管理
| +-- utils/ # 工具函数
| +-- layouts/ # 布局组件
|
+-- .env.development # 开发环境配置
+-- .env.production # 生产环境配置
+-- package.json # 依赖配置
+-- vite.config.ts # Vite配置
+-- tsconfig.json # TypeScript配置
```
## 4.8 公共资源目录 (public/)
```
public/
|
+-- static/ # 静态文件
+-- templates/ # 代码生成模板
| +-- models.py.tpl # 模型模板
| +-- forms.py.tpl # 表单模板
| +-- services.py.tpl # 服务模板
| +-- views.py.tpl # 视图模板
| +-- urls.py.tpl # 路由模板
| +-- ui/
| +-- index.vue.tpl # 列表页模板
| +-- edit.vue.tpl # 编辑页模板
| +-- detail.vue.tpl # 详情页模板
| +-- api.ts.tpl # API模块模板
|
+-- uploads/ # 上传文件存储
+-- example/ # 按模块分类存储
+-- avatar/ # 头像文件
+-- temp/ # 临时文件
```
@@ -0,0 +1,352 @@
# 第四章 架构设计 - 扩展性设计
## 4.1 扩展性设计原则
项目遵循开闭原则(OCP):对扩展开放,对修改关闭。新功能通过添加新模块实现,而非修改现有代码。系统通过 Django App 机制、代码生成器、模板定制等方式提供扩展能力。
## 4.2 Django App 机制
### 4.2.1 模块化设计
每个业务模块独立为一个 Django App,可独立开发、测试和部署:
```
application/
+-- example/ # 案例模块
| +-- __init__.py
| +-- apps.py # 应用配置
| +-- models.py # 数据模型
| +-- forms.py # 表单验证
| +-- services.py # 业务逻辑
| +-- views.py # 视图处理
| +-- urls.py # 路由配置
|
+-- user/ # 用户模块
+-- role/ # 角色模块
+-- ... # 其他模块
```
### 4.2.2 应用注册
新模块需要在 `application/settings.py` 中注册:
```python
INSTALLED_APPS = [
# ... 其他应用
'application.example', # 注册案例模块
]
```
### 4.2.3 路由注册
新模块需要在 `application/urls.py` 中注册路由:
```python
urlpatterns = [
# ... 其他路由
path('example/', include('application.example.urls')),
]
```
## 4.3 代码生成器
### 4.3.1 生成器概述
代码生成器是 核心功能,通过数据库表定义自动生成完整的 CRUD 模块,包括:
- 后端:Model、Form、Service、View、URL、Admin
- 前端:Vue 页面、API 模块、表格列配置、查询表单配置
- 数据库:迁移文件、菜单记录、权限节点
### 4.3.2 使用方式
```bash
# 从数据库表生成配置
python generator.py django_<table>
# 从配置文件生成代码
python generator.py --config config.json
# 仅预览配置,不生成代码
python generator.py django_<table> --dry-run
```
### 4.3.3 生成器工作流程
```
1. 解析数据库表结构
+-- 获取字段名、类型、注释、长度、索引
+-- 启发式分类字段(图片、富文本、状态、排序等)
|
v
2. 生成配置文件 (config.json)
+-- 模块名称、表名、字段定义
+-- 前端组件类型、验证规则
|
v
3. 渲染 Jinja2 模板
+-- 后端模板:models.py.tpl, forms.py.tpl, services.py.tpl 等
+-- 前端模板:index.vue.tpl, edit.vue.tpl, api.ts.tpl 等
|
v
4. 写入文件
+-- application/<module>/ # 后端模块
+-- ui/src/views/... # 前端页面
+-- ui/src/api/... # API模块
|
v
5. 自动注册
+-- 执行 makemigrations/migrate
+-- 插入菜单和权限节点
+-- 追加路由到 application/urls.py
+-- 追加应用到 INSTALLED_APPS
```
### 4.3.4 配置文件格式
```json
{
"appName": "example",
"tableName": "django_example",
"tableComment": "案例表",
"className": "Example",
" moduleName": "案例",
"fields": [
{
"fieldName": "name",
"fieldType": "CharField",
"fieldComment": "案例名称",
"maxLength": 100,
"isRequired": true,
"isSearch": true,
"componentType": "input"
},
{
"fieldName": "status",
"fieldType": "IntegerField",
"fieldComment": "案例状态",
"choices": [
{"value": 1, "label": "正常"},
{"value": 2, "label": "禁用"}
],
"componentType": "select"
}
]
}
```
## 4.4 模板定制
### 4.4.1 模板目录
代码生成器使用 Jinja2 模板,模板文件位于 `public/templates/` 目录:
```
public/templates/
+-- models.py.tpl # 模型模板
+-- forms.py.tpl # 表单模板
+-- services.py.tpl # 服务模板
+-- views.py.tpl # 视图模板
+-- urls.py.tpl # 路由模板
+-- apps.py.tpl # 应用配置模板
+-- admin.py.tpl # Admin注册模板
+-- migration.py.tpl # 迁移文件模板
+-- ui/
+-- index.vue.tpl # 列表页模板
+-- edit.vue.tpl # 编辑页模板
+-- detail.vue.tpl # 详情页模板
+-- api.ts.tpl # API模块模板
+-- columns.ts.tpl # 表格列配置模板
+-- querySchemas.ts.tpl # 查询表单配置模板
```
### 4.4.2 模板语法示例
```jinja2
{# models.py.tpl #}
from django.db import models
from application.models import BaseModel
from utils.common import get_table_name
class {{ className }}(BaseModel):
"""{{ tableComment }}"""
{% for field in fields %}
{{ field.fieldName }} = models.{{ field.fieldType }}(
{% if field.maxLength %} max_length={{ field.maxLength }},{% endif %}
{% if not field.isRequired %} null=True, blank=True,{% endif %}
verbose_name="{{ field.fieldComment }}",
db_comment='{{ field.fieldComment }}'
)
{% endfor %}
class Meta:
db_table = get_table_name('{{ appName }}')
db_table_comment = "{{ tableComment }}"
```
### 4.4.3 自定义模板
开发者可以根据项目需求定制模板:
1. 修改 `public/templates/` 目录下的模板文件
2. 使用 Jinja2 语法添加自定义逻辑
3. 重新运行代码生成器应用新的模板
## 4.5 中间件扩展
### 4.5.1 自定义中间件
通过 Django 中间件机制可灵活添加请求/响应处理逻辑:
```python
# middleware/custom_middleware.py
class CustomMiddleware:
"""自定义中间件"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 请求处理前的逻辑
# ...
response = self.get_response(request)
# 响应处理后的逻辑
# ...
return response
```
### 4.5.2 注册中间件
在 `application/settings.py` 中注册自定义中间件:
```python
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'middleware.custom_middleware.CustomMiddleware', # 注册自定义中间件
# ... 其他中间件
]
```
## 4.6 信号机制扩展
### 4.6.1 Django 信号
Django 提供信号机制,可以在特定事件发生时执行自定义逻辑:
```python
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Example)
def example_post_save(sender, instance, created, **kwargs):
"""案例保存后的处理"""
if created:
# 新增记录后的逻辑
pass
else:
# 更新记录后的逻辑
pass
```
### 4.6.2 内置信号
| 信号 | 触发时机 | 说明 |
|------|----------|------|
| `pre_save` | 保存前 | 数据验证前 |
| `post_save` | 保存后 | 数据保存后 |
| `pre_delete` | 删除前 | 删除前检查 |
| `post_delete` | 删除后 | 清理关联数据 |
| `class_prepared` | 模型类准备就绪 | 字段顺序重排 |
## 4.7 自定义管理命令
### 4.7.1 创建管理命令
```python
# application/management/commands/my_command.py
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = '自定义管理命令说明'
def add_arguments(self, parser):
parser.add_argument('arg1', type=str, help='参数说明')
def handle(self, *args, **options):
arg1 = options['arg1']
# 命令逻辑
self.stdout.write(self.style.SUCCESS(f'执行成功: {arg1}'))
```
### 4.7.2 使用管理命令
```bash
python manage.py my_command value1
```
### 4.7.3 内置管理命令
```bash
python manage.py runserver # 启动开发服务器
python manage.py makemigrations <app> # 生成迁移文件
python manage.py migrate # 执行迁移
python manage.py createsuperuser # 创建超级用户
python manage.py add_full_comments # 回填数据库字段注释
```
## 4.8 前端扩展
### 4.8.1 动态路由
前端路由由后端菜单数据动态生成,新页面通过插入菜单记录自动注册:
```sql
INSERT INTO django_menu (name, path, component, permission, type, status)
VALUES ('案例管理', '/example', 'tool/example/index', 'sys:example:page', 0, 0);
```
### 4.8.2 组件扩展
前端使用 Vue3 + ElementPlus,可通过以下方式扩展:
1. **自定义组件**:在 `ui/src/components/` 目录下创建新组件
2. **组合式函数**:在 `ui/src/hooks/` 目录下创建自定义 hooks
3. **全局指令**:在 `ui/src/directives/` 目录下创建自定义指令
### 4.8.3 API 扩展
新增 API 接口需要在 `ui/src/api/` 目录下创建对应的 TypeScript 模块:
```typescript
// ui/src/api/tool/example.ts
import { http } from '@/utils/http/axios'
export function getExamplePage(params) {
return http.get('/example/page', params)
}
export function addExample(data) {
return http.post('/example/add', data)
}
```
## 4.9 扩展性最佳实践
### 新增模块流程
1. **设计数据库表**:确定字段和关联关系
2. **使用代码生成器**:`python generator.py django_<table>`
3. **调整生成代码**:根据业务需求修改 Service 层逻辑
4. **注册菜单权限**:通过管理后台或直接操作数据库
5. **前端页面定制**:调整 Vue 页面的表格列和表单字段
### 扩展注意事项
1. **遵循规范**:保持与现有模块一致的代码风格
2. **软删除**:所有查询都要过滤 `is_delete=False`
3. **权限节点**:为每个操作定义 `sys:<module>:<action>` 权限
4. **响应格式**:统一使用 `R.ok()` 和 `R.failed()` 返回响应
5. **操作日志**:使用 `@operation_log` 装饰器记录操作日志
@@ -0,0 +1,451 @@
# 第四章 架构设计 - 分层架构详解
## 4.1 分层架构概述
项目采用经典的五层架构设计,各层职责明确,依赖关系清晰:
```
+------------------+
| View Layer | <-- 视图层:接收请求,返回响应
+------------------+
|
v
+------------------+
| Service Layer | <-- 服务层:业务逻辑处理
+------------------+
|
v
+------------------+
| Form Layer | <-- 表单层:数据验证
+------------------+
|
v
+------------------+
| Model Layer | <-- 模型层:数据库操作
+------------------+
|
v
+------------------+
| URL Layer | <-- 路由层:URL映射
+------------------+
```
## 4.2 模型层 (Model Layer)
### 4.2.1 BaseModel 抽象基类
所有业务模型继承自 `BaseModel`(`application/models.py`),自动获得 6 个公共字段:
```python
class BaseModel(models.Model):
"""基础模型抽象类"""
# 主键ID字段
id = models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='主键ID',
db_comment='主键ID'
)
# 创建人字段
create_user = models.CharField(
null=True,
max_length=50,
default=None,
verbose_name='创建人',
db_comment='创建人'
)
# 创建时间字段
create_time = models.DateTimeField(
null=True,
auto_now_add=True,
verbose_name="创建时间",
db_comment='创建时间'
)
# 更新人字段
update_user = models.CharField(
null=True,
max_length=50,
default=None,
verbose_name='更新人',
db_comment='更新人'
)
# 更新时间字段
update_time = models.DateTimeField(
null=True,
auto_now=True,
verbose_name="更新时间",
db_comment='更新时间'
)
# 逻辑删除标识
is_delete = models.BooleanField(
default=0,
db_default=0,
verbose_name="逻辑删除",
db_comment='逻辑删除:0-正常 1-已删除'
)
class Meta:
abstract = True # 抽象模型,不创建数据库表
```
### 4.2.2 业务模型示例
以案例模型为例(`application/example/models.py`):
```python
from application.models import BaseModel
from utils.common import get_table_name
class Example(BaseModel):
"""案例模型类"""
name = models.CharField(
null=False, max_length=100, db_index=True,
verbose_name="案例名称", db_comment='案例名称'
)
avatar = models.CharField(
null=True, blank=True, max_length=255,
verbose_name="案例图片", db_comment='案例图片'
)
TYPE_CHOICES = ((1, "类型1"), (2, "类型2"), (3, "类型3"), (4, "类型4"))
type = models.IntegerField(
null=False, choices=TYPE_CHOICES,
verbose_name="案例类型", db_comment='案例类型'
)
STATUS_CHOICES = ((1, "正常"), (2, "禁用"))
status = models.IntegerField(
null=False, choices=STATUS_CHOICES,
verbose_name="案例状态", db_comment='案例状态:1-正常 2-禁用'
)
sort = models.IntegerField(
null=False, verbose_name="排序", db_comment='排序'
)
class Meta:
db_table = get_table_name('example') # 自动生成表名
db_table_comment = "案例表"
ordering = ("sort",)
```
### 4.2.3 字段顺序重排
通过 `class_prepared` 信号,自动将公共字段移到业务字段之后:
```python
BASE_FIELD_NAMES = ['id', 'create_user', 'create_time',
'update_user', 'update_time', 'is_delete']
def _reorder_fields(sender, **kwargs):
"""信号处理函数:重排模型字段顺序"""
if not hasattr(sender, '_meta') or sender._meta.abstract:
return
base_fields = []
business_fields = []
for field in sender._meta.local_fields:
if field.name in BASE_FIELD_NAMES:
base_fields.append(field)
else:
business_fields.append(field)
# 重新排列:业务字段在前,公共字段在后
sender._meta.local_fields = business_fields + base_fields
class_prepared.connect(_reorder_fields)
```
## 4.3 表单层 (Form Layer)
### 4.3.1 ModelForm 验证
表单层使用 Django 的 `ModelForm` 进行数据验证(`application/example/forms.py`):
```python
from django import forms
from application.example import models
class ExampleForm(forms.ModelForm):
"""案例表单验证类"""
name = forms.CharField(
required=True, max_length=100,
error_messages={
'required': '案例名称不能为空',
'max_length': '案例名称长度不得超过100个字符',
}
)
type = forms.IntegerField(
required=True, min_value=1, max_value=4,
error_messages={
'required': '案例类型不能为空',
'min_value': '案例类型值不能小于1',
'max_value': '案例类型值不能大于4',
}
)
status = forms.IntegerField(
required=True, min_value=1, max_value=2,
error_messages={
'required': '案例状态不能为空',
}
)
sort = forms.IntegerField(
required=True,
error_messages={'required': '排序不能为空'}
)
class Meta:
model = models.Example
fields = ['name', 'avatar', 'type', 'status', 'sort']
```
### 4.3.2 验证错误处理
```python
# 在Service层使用表单验证
form = ExampleForm(data)
if not form.is_valid():
# 获取第一个错误信息
return R.failed(msg=regular.get_err(form))
```
## 4.4 服务层 (Service Layer)
### 4.4.1 服务函数命名规范
服务层函数遵循统一的命名规范:
| 函数类型 | 命名格式 | 示例 |
|----------|----------|------|
| 分页查询 | `get_<module>_page` | `get_example_page` |
| 列表查询 | `get_<module>_list` | `get_example_list` |
| 详情查询 | `get_<module>_detail` | `get_example_detail` |
| 添加 | `add_<module>` | `add_example` |
| 更新 | `update_<module>` | `update_example` |
| 删除 | `delete_<module>s` | `delete_examples` |
| 状态更新 | `update_<module>_status` | `update_example_status` |
### 4.4.2 分页查询实现
```python
def get_example_page(request):
"""查询案例分页数据"""
try:
page_no = int(request.GET.get('pageNo', 1))
page_size = int(request.GET.get('pageSize', PAGE_SIZE))
# 构建基础查询(只查询未删除的记录)
query = Example.objects.filter(is_delete=False)
query = _apply_filters(query, request)
query = query.order_by("sort")
# 执行分页查询
paginator = Paginator(query, page_size)
page_list = paginator.page(page_no)
# 构建并返回分页数据
page_data = _build_page_data(page_list, paginator, page_no, page_size)
return R.ok(data=page_data)
except Exception as e:
logging.error(f"查询案例分页数据异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
```
### 4.4.3 添加操作实现
```python
def add_example(request):
"""添加案例"""
data, error = parse_request_body(request)
if error:
return R.failed(msg=error)
form = ExampleForm(data)
if not form.is_valid():
return R.failed(msg=regular.get_err(form))
try:
cleaned_data = form.cleaned_data
avatar = cleaned_data.get('avatar')
if avatar:
avatar = save_file(avatar, "example")
Example.objects.create(
name=cleaned_data.get('name'),
avatar=avatar,
type=cleaned_data.get('type'),
status=cleaned_data.get('status'),
sort=cleaned_data.get('sort'),
create_user=get_username(request),
update_user=get_username(request)
)
return R.ok(msg="创建成功")
except Exception as e:
logging.error(f"添加案例异常: {str(e)}")
return R.failed(msg="添加失败,请稍后重试")
```
### 4.4.4 删除操作实现(软删除)
```python
def delete_examples(instance_ids):
"""删除案例(批量删除)"""
if not instance_ids:
return R.failed("记录ID不存在")
try:
id_list = [int(id_str.strip())
for id_str in instance_ids.split(',') if id_str.strip()]
# 批量更新删除标识(逻辑删除)
updated_count = Example.objects.filter(
id__in=id_list, is_delete=False
).update(is_delete=True)
return R.ok(msg=f"本次共删除{updated_count}条数据")
except ValueError:
return R.failed("记录ID格式错误")
```
## 4.5 视图层 (View Layer)
### 4.5.1 视图类结构
视图层使用 Django 的类视图,结合装饰器和混入类实现认证和权限控制:
```python
from django.utils.decorators import method_decorator
from django.views import View
from middleware.login_middleware import check_login
from middleware.permission_middleware import PermissionRequired
@method_decorator(check_login, name="get")
class ExamplePageView(PermissionRequired, View):
"""案例分页数据查询视图"""
permission_required = ("sys:example:page",)
@operation_log(title="案例管理-查询分页列表", log_type=LogType.QUERY)
def get(self, request):
result = services.get_example_page(request)
return result
```
### 4.5.2 视图类职责
| 职责 | 说明 |
|------|------|
| 请求接收 | 接收 HTTP 请求,提取参数 |
| 认证检查 | 通过 `@check_login` 装饰器验证登录状态 |
| 权限检查 | 通过 `PermissionRequired` 混入类验证操作权限 |
| 参数校验 | 简单的参数格式校验(如 ID 有效性) |
| 服务调用 | 调用 Service 层函数处理业务逻辑 |
| 响应返回 | 返回 Service 层返回的响应结果 |
## 4.6 路由层 (URL Layer)
### 4.6.1 模块路由定义
每个模块在 `urls.py` 中定义自己的路由:
```python
# application/example/urls.py
from django.urls import path
from application.example import views
urlpatterns = [
path('page', views.ExamplePageView.as_view()),
path('list', views.ExampleListView.as_view()),
path('detail/<int:id>', views.ExampleDetailView.as_view()),
path('add', views.ExampleAddView.as_view()),
path('update', views.ExampleUpdateView.as_view()),
path('delete/<str:id>', views.ExampleDeleteView.as_view()),
path('status', views.ExampleStatusView.as_view()),
]
```
### 4.6.2 主路由注册
所有模块路由在主路由中注册(`application/urls.py`):
```python
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('application.login.urls')),
path('index/', include('application.index.urls')),
path('example/', include('application.example.urls')),
# ... 其他模块
]
```
## 4.7 数据流向
### 4.7.1 请求数据流
```
HTTP Request
|
v
View.get/post/put/delete
|
v
Service函数
|
v
parse_request_body(request) --> 解析JSON请求体
|
v
Form验证 --> 数据校验
|
v
Model操作 --> 数据库读写
|
v
构建返回数据字典
|
v
R.ok()/R.failed() --> 构造响应
|
v
JsonResponse --> HTTP Response
```
### 4.7.2 响应数据格式转换
```python
# Model对象 -> 字典
record = {
'id': item.id,
'name': item.name,
'avatar': get_file_url(item.avatar),
'createUser': item.create_user, # snake_case -> camelCase
'createTime': format_datetime(item.create_time),
}
# 字典 -> JSON
return R.ok(data=record)
# {
# "code": 0,
# "data": {"id": 1, "name": "...", "createUser": "admin"},
# "msg": "操作成功",
# "ok": true
# }
```
@@ -0,0 +1,157 @@
# 第四章 架构设计 - 系统架构总览
## 4.1 整体架构
项目采用前后端分离的 B/S 架构,后端基于 Django 6.0.3 提供 RESTful API,前端基于 Vue3 + ElementPlus 构建单页应用。系统通过 Nginx 反向代理统一入口,实现静态资源托管与 API 转发。
### 架构图
```
浏览器 (Vue3 + ElementPlus)
|
v
Nginx (反向代理)
|
v
Django Application
|
+-- Middleware Chain
| +-- CORS (django-cors-headers)
| +-- check_login (JWT认证)
| +-- PermissionRequired (RBAC鉴权)
|
+-- URL Router (application/urls.py)
| +-- include() -> 各模块urls.py
|
+-- View Layer (views.py)
| +-- Class-based Views + decorators
|
+-- Service Layer (services.py)
| +-- 业务逻辑 + Django ORM
|
+-- Model Layer (models.py)
+-- BaseModel -> 业务模型
|
v
MySQL + Redis
```
## 4.2 核心分层
系统采用经典的分层架构设计,各层职责明确:
| 层级 | 职责 | 典型文件 |
|------|------|----------|
| **视图层 (View)** | 接收HTTP请求、参数校验、调用Service、返回响应 | `views.py` |
| **服务层 (Service)** | 业务逻辑处理、数据组装、事务控制 | `services.py` |
| **表单层 (Form)** | 数据验证、类型转换、错误信息生成 | `forms.py` |
| **模型层 (Model)** | 数据库表定义、字段约束、关联关系 | `models.py` |
| **路由层 (URL)** | URL到视图的映射注册 | `urls.py` |
## 4.3 请求处理流程
一个典型的 API 请求经过以下处理链路:
```
1. 浏览器发起请求 (携带 Authorization: Bearer <token>)
2. CORS中间件 - 处理跨域预检请求
3. check_login - 验证JWT token有效性
4. URL路由匹配 - 分发到对应视图类
5. PermissionRequired - 验证用户权限节点
6. View.get/post/put/delete - 处理请求
7. Service函数 - 执行业务逻辑
8. R.ok()/R.failed() - 构造统一响应
9. JsonResponse - 返回JSON数据
```
## 4.4 模块化设计
每个业务模块(如 example、user、role 等)遵循统一的文件结构:
```
application/<module>/
__init__.py # 包初始化
apps.py # Django应用配置
models.py # 数据模型
forms.py # 表单验证
services.py # 业务逻辑
views.py # 视图处理
urls.py # 路由配置
admin.py # Admin后台注册
migrations/ # 数据库迁移文件
```
## 4.5 关键设计决策
### 前后端分离
- 后端仅提供 JSON API,不渲染模板
- 前端通过 Vite 开发服务器代理请求到 Django
- 生产环境由 Nginx 统一处理静态资源和 API 转发
### JWT 认证替代 Session
- 无状态认证,适合分布式部署
- Token 通过 Authorization 请求头传递
- 支持 Token 黑名单机制(Redis 存储)
### 软删除策略
- 所有业务表包含 `is_delete` 字段
- 删除操作仅标记 `is_delete=True`,不物理删除
- 所有查询自动过滤 `is_delete=False`
### 代码生成器
- 通过数据库表定义自动生成完整模块
- 包含后端 Model/Form/Service/View/URL
- 包含前端 Vue 页面和 API 模块
- 自动注册菜单和权限节点
## 4.6 技术栈概览
| 类别 | 技术选型 | 版本 |
|------|----------|------|
| 后端框架 | Django | 6.0.3 |
| 数据库 | MySQL | 8.0+ |
| 缓存 | Redis | 6.0+ |
| 认证 | JWT (PyJWT) | - |
| 前端框架 | Vue3 | 3.x |
| UI组件库 | ElementPlus | - |
| 构建工具 | Vite | - |
| 状态管理 | Pinia | - |
## 4.7 目录结构
```
DjangoAdmin_Django_EleVue/
+-- application/ # Django后端应用
| +-- settings.py # 项目配置
| +-- urls.py # 主路由
| +-- models.py # BaseModel定义
| +-- <module>/ # 业务模块
+-- middleware/ # 中间件
| +-- login_middleware.py # JWT认证
| +-- permission_middleware.py # 权限控制
+-- utils/ # 工具函数
| +-- R.py # 统一响应
| +-- jwt.py # JWT工具
| +-- security.py # 安全工具
+-- config/ # 配置文件
| +-- env.py # 环境变量
+-- ui/ # Vue前端
+-- src/
+-- views/ # 页面组件
+-- api/ # API接口
+-- components/ # 公共组件
```
## 4.8 扩展性设计
系统通过以下机制保证良好的扩展性:
1. **Django App 机制**:每个模块独立为一个 Django App,可独立开发和部署
2. **代码生成器**:新模块通过数据库表定义自动生成,减少重复开发
3. **模板定制**:代码生成器使用 Jinja2 模板,可根据项目需求定制生成规则
4. **中间件扩展**:通过 Django 中间件机制可灵活添加请求/响应处理逻辑
5. **权限节点**:基于 `sys:<module>:<action>` 的权限标识,支持细粒度权限控制
@@ -0,0 +1,273 @@
# 性能优化指南
:::tip 说明
本文介绍本项目中常见的性能优化策略,涵盖后端 ORM 查询、缓存、分页、前端加载等方面。
:::
## ORM 查询优化
### N+1 查询问题
N+1 查询是最常见的性能问题:查询 N 条记录时,每条记录又触发一次关联查询。
```python
# ❌ 错误:N+1 查询
articles = Article.objects.filter(is_delete=False) # 1 次查询
for article in articles:
category = Category.objects.get(id=article.category_id) # N 次查询
# ✅ 正确:使用 select_related(外键关联)
articles = Article.objects.filter(is_delete=False).select_related('category')
# ✅ 正确:使用 prefetch_related(多对多/反向关联)
roles = Role.objects.filter(is_delete=False).prefetch_related('menus')
```
### select_related vs prefetch_related
| 方法 | 适用场景 | 实现方式 |
|------|---------|---------|
| `select_related` | 外键关联(ForeignKey) | JOIN 查询,一次 SQL |
| `prefetch_related` | 多对多/反向关联 | 两次查询,Python 层合并 |
### 只查询需要的字段
```python
# ❌ 错误:SELECT * 查询所有字段
users = User.objects.filter(is_delete=False)
# ✅ 正确:只查询需要的字段
users = User.objects.filter(is_delete=False).values('id', 'username', 'realname')
# ✅ 正确:使用 only() 只加载指定字段
users = User.objects.filter(is_delete=False).only('id', 'username', 'realname')
```
### 避免循环查询
```python
# ❌ 错误:循环中查询数据库
for role_id in role_ids:
role = Role.objects.get(id=role_id)
# ✅ 正确:批量查询
roles = Role.objects.filter(id__in=role_ids)
```
## 数据库索引策略
### 常用索引类型
| 类型 | 说明 | 适用场景 |
|------|------|---------|
| 单列索引 | `db_index=True` | WHERE 条件字段 |
| 唯一索引 | `unique=True` | 唯一性约束字段 |
| 复合索引 | `index_together` | 多字段联合查询 |
| 覆盖索引 | `include` | 查询字段都在索引中 |
### 索引设计原则
```python
class Example(BaseModel):
name = models.CharField(max_length=100, db_index=True) # 常用查询字段加索引
status = models.IntegerField(db_index=True) # 状态字段加索引
category_id = models.IntegerField(db_index=True) # 外键字段加索引
class Meta:
db_table = 'django_example'
index_together = [
['status', 'is_delete'], # 复合索引:状态 + 软删除
]
```
### 索引注意事项
```
1. 不要过度索引:每个索引都会降低写入性能
2. 选择性高的字段优先:如 username 比 status 选择性更高
3. 复合索引顺序:选择性高的字段放前面
4. 定期分析:使用 EXPLAIN 分析慢查询
```
## 分页优化
### 传统分页 vs 游标分页
```python
# 传统分页(OFFSET):大偏移量时性能差
paginator = Paginator(queryset, page_size)
page = paginator.page(page_no) # OFFSET 100000 时很慢
# 游标分页(推荐大数据量):性能稳定
last_id = request.GET.get('lastId', 0)
queryset = Example.objects.filter(
is_delete=False, id__gt=last_id
).order_by('id')[:page_size]
```
### 分页优化建议
```
1. 小数据量(< 10万):传统分页即可
2. 大数据量(> 10万):使用游标分页
3. 避免 SELECT *:只查需要的字段
4. 避免 COUNT(*):大表 COUNT 很慢,可用缓存
5. 使用索引:ORDER BY 字段必须有索引
```
## Redis 缓存策略
### 缓存穿透
查询不存在的数据,每次都打到数据库:
```python
# 解决方案:缓存空值
def get_user(user_id):
cache_key = f'user:{user_id}'
cached = cache.get(cache_key)
if cached is not None:
return None if cached == 'NULL' else cached
user = User.objects.filter(id=user_id, is_delete=False).first()
if user:
cache.set(cache_key, user, timeout=3600)
else:
cache.set(cache_key, 'NULL', timeout=300) # 缓存空值,短 TTL
return user
```
### 缓存击穿
热点数据过期时,大量请求打到数据库:
```python
# 解决方案:互斥锁
def get_hot_data(key):
cached = cache.get(key)
if cached:
return cached
# 加锁,只允许一个请求查询数据库
lock_key = f'lock:{key}'
if cache.add(lock_key, '1', timeout=10):
try:
data = query_database(key)
cache.set(key, data, timeout=3600)
return data
finally:
cache.delete(lock_key)
else:
# 等待其他请求查询完成
time.sleep(0.1)
return get_hot_data(key)
```
### 缓存雪崩
大量缓存同时过期,请求全部打到数据库:
```python
# 解决方案:随机 TTL
import random
base_timeout = 3600
random_offset = random.randint(0, 600)
cache.set(key, data, timeout=base_timeout + random_offset)
```
## 前端性能优化
### 路由懒加载
```typescript
// 路由懒加载:按需加载页面组件
const routes = [
{
path: '/system/user',
component: () => import('@/views/system/user/index.vue'),
},
];
```
### 组件按需导入
```typescript
// ElementPlus 按需导入
import { ElButton, ElTable, ElForm } from 'element-plus';
```
### 图片懒加载
```vue
<img v-lazy="imageUrl" alt="图片" />
```
### 代码分割
```typescript
// Vite 自动代码分割
// 大型依赖单独打包
import(/* webpackChunkName: "editor" */ '@wangeditor/editor');
```
## Gunicorn Worker 配置
```ini
# gunicorn.conf.py
import multiprocessing
# Worker 数量:CPU 核心数 * 2 + 1
workers = multiprocessing.cpu_count() * 2 + 1
# Worker 类型:sync(默认)/ gevent(异步)
worker_class = 'gevent'
# 超时时间
timeout = 120
# 最大请求数(防止内存泄漏)
max_requests = 1000
max_requests_jitter = 50
```
## 数据库连接池
```python
# application/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 600, # 连接存活时间(秒)
'CONN_HEALTH_CHECKS': True, # 连接健康检查
}
}
```
## 慢查询排查
```bash
# 开启 MySQL 慢查询日志
mysql -u root -p -e "
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
"
# 查看慢查询
tail -f /var/log/mysql/slow.log
# 分析查询计划
mysql -u root -p -e "EXPLAIN SELECT * FROM django_example WHERE status = 1;"
```
## 总结
性能优化的核心原则:
```
1. ORM 优化:避免 N+1 查询,使用 select_related/prefetch_related
2. 索引策略:WHERE/ORDER BY 字段加索引,避免过度索引
3. 分页优化:大数据量使用游标分页
4. 缓存策略:穿透/击穿/雪崩三种场景的解决方案
5. 前端优化:路由懒加载、组件按需导入、图片懒加载
6. 配置调优:Gunicorn Worker、数据库连接池
```
@@ -0,0 +1,336 @@
# 第四章 架构设计 - 请求处理流程
## 4.1 请求处理全链路
一个完整的 HTTP 请求在 系统中的处理流程如下:
```
浏览器
|
| (1) 发起HTTP请求 (携带 Authorization: Bearer <token>)
v
Nginx
|
| (2) 反向代理,静态资源由Nginx处理,API请求转发到Django
v
Django WSGI
|
| (3) 进入中间件链
v
CORS中间件 (corsheaders.middleware.CorsMiddleware)
|
| (4) 处理跨域请求,添加CORS响应头
v
check_login装饰器 (middleware/login_middleware.py)
|
| (5) 验证JWT token有效性
v
URL路由匹配 (application/urls.py)
|
| (6) 根据URL分发到对应视图类
v
PermissionRequired混入类 (middleware/permission_middleware.py)
|
| (7) 验证用户权限节点
v
View视图类 (views.py)
|
| (8) 解析请求参数,调用Service
v
Service服务函数 (services.py)
|
| (9) 执行业务逻辑,操作数据库
v
R.ok()/R.failed() (utils/R.py)
|
| (10) 构造统一JSON响应
v
JsonResponse
|
| (11) 返回响应数据
v
浏览器
```
## 4.2 中间件处理顺序
根据 `application/settings.py` 的配置,中间件按以下顺序执行:
```python
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 1. CORS跨域处理
'django.middleware.security.SecurityMiddleware', # 2. 安全处理
'django.contrib.sessions.middleware.SessionMiddleware', # 3. Session处理
'django.middleware.common.CommonMiddleware', # 4. 通用处理
# 'django.middleware.csrf.CsrfViewMiddleware', # CSRF已禁用(使用JWT)
'django.contrib.auth.middleware.AuthenticationMiddleware', # 5. 认证处理
'django.contrib.messages.middleware.MessageMiddleware', # 6. 消息处理
'django.middleware.clickjacking.XFrameOptionsMiddleware', # 7. 点击劫持防护
]
```
**注意**:`check_login` 和 `PermissionRequired` 不是 Django 中间件,而是通过装饰器和混入类实现的,作用于视图层面。
## 4.3 JWT 认证流程
### 4.3.1 登录认证
```python
# 1. 用户提交用户名密码
POST /login
{
"username": "admin",
"password": "123456",
"captcha": "abc123"
}
# 2. 验证通过后生成JWT Token
from utils.jwt import create_token
payload = {
'userId': user.id,
'username': user.username,
'realname': user.realname
}
token = create_token(payload, timeout=20) # 20分钟过期
# 3. 返回Token给前端
{
"code": 0,
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
},
"msg": "登录成功"
}
```
### 4.3.2 请求认证
```python
# 1. 前端在请求头中携带Token
headers = {
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
# 2. check_login装饰器验证Token
def check_login(func):
def wrapper(request, *args, **kwargs):
# 忽略不需要认证的URL
ignoreURL = ['/login', '/captcha']
if request.path not in ignoreURL:
# 从请求头获取Token
access_token = request.headers['Authorization']
access_token = access_token.replace('Bearer ', "")
# 验证Token
result = parse_payload(access_token)
if result['code'] != 0:
return R.failed(code=401, msg=result['msg'])
return func(request, *args, **kwargs)
return wrapper
```
### 4.3.3 Token 黑名单检查
```python
# 1. 验证Token时先检查黑名单
def parse_payload(token):
# 检查Token是否在黑名单中
if is_token_blacklisted(token):
return {'code': -1, 'msg': 'token已失效,请重新登录'}
# 解密验证JWT令牌
verified_payload = jwt.decode(token, JWT_SALT, algorithms=[JWT_ALGORITHM])
return {'code': 0, 'data': verified_payload}
# 2. 黑名单检查使用Redis
def is_token_blacklisted(token):
redis = get_redis_connection("default")
fingerprint = hashlib.sha256(token.encode('utf-8')).hexdigest()
key = f'token:blacklist:{fingerprint}'
return redis.exists(key) > 0
```
## 4.4 权限验证流程
### 4.4.1 权限节点定义
每个操作对应一个权限节点,格式为 `sys:<module>:<action>`:
```python
# 示例:案例模块的权限节点
permission_required = ("sys:example:page",) # 分页查询
permission_required = ("sys:example:detail",) # 详情查询
permission_required = ("sys:example:add",) # 添加
permission_required = ("sys:example:update",) # 更新
permission_required = ("sys:example:delete",) # 删除
permission_required = ("sys:example:status",) # 状态更新
```
### 4.4.2 权限验证逻辑
```python
class PermissionRequired(PermissionRequiredMixin):
def has_permission(self):
# 获取所需权限节点
permissions = self.get_permission_required()
# 获取当前用户ID
user_id = get_user_id(self.request)
# 超级管理员(ID=1)自动放行
if user_id and user_id != 1:
# 获取用户权限列表
permission_list = services.get_user_permissions(user_id)
# 检查是否拥有所需权限
for permission in permissions:
if permission not in permission_list:
return False
return True
```
### 4.4.3 权限获取逻辑
```python
# application/menu/services.py
def get_user_permissions(user_id):
if user_id == 1:
# 超级管理员拥有所有权限
menu_list = Menu.objects.filter(is_delete=False, type=1, status=0).values()
return [item['permission'] for item in menu_list]
else:
# 普通用户从角色关联中获取权限
sql = '''
SELECT m.* FROM django_menu AS m
INNER JOIN django_role_menu AS rm ON m.id=rm.menu_id
INNER JOIN django_user_role AS ur ON ur.role_id=rm.role_id
WHERE ur.user_id=%s AND (m.type=1 OR (m.type=0 AND m.permission!=''))
AND m.status=0 AND m.is_delete=0
'''
menu_list = Menu.objects.raw(sql, [user_id])
return [item.permission for item in menu_list]
```
## 4.5 业务处理流程
以"添加案例"为例,完整的业务处理流程:
```
1. View层 (ExampleAddView.post)
+-- 检查演示模式 (DJANGO_DEMO)
+-- 调用 Service: services.add_example(request)
2. Service层 (add_example)
+-- 解析请求体: parse_request_body(request)
+-- 表单验证: forms.ExampleForm(data)
+-- 处理文件: save_file(avatar, "example")
+-- 创建记录: Example.objects.create(...)
+-- 返回结果: R.ok(msg="创建成功")
3. Model层 (Example)
+-- Django ORM 生成 SQL
+-- MySQL 执行 INSERT
+-- 自动生成 create_time、update_time
```
## 4.6 响应格式规范
所有 API 接口返回统一的 JSON 格式:
### 成功响应
```json
{
"code": 0,
"data": {
"id": 1,
"name": "案例名称",
"status": 1
},
"msg": "操作成功",
"ok": true
}
```
### 分页响应
```json
{
"code": 0,
"data": {
"records": [
{"id": 1, "name": "案例1"},
{"id": 2, "name": "案例2"}
],
"total": 100,
"size": 10,
"current": 1,
"pages": 10
},
"msg": "操作成功",
"ok": true
}
```
### 失败响应
```json
{
"code": 1,
"data": null,
"msg": "操作失败",
"ok": false
}
```
### 认证失败响应
```json
{
"code": 401,
"data": null,
"msg": "token已失效,请重新登录",
"ok": false
}
```
## 4.7 异常处理机制
### 4.7.1 全局异常处理
```python
# Service层统一使用 try-except 捕获异常
def add_example(request):
try:
# 业务逻辑
Example.objects.create(...)
return R.ok(msg="创建成功")
except Exception as e:
logging.error(f"添加案例异常: {str(e)}")
return R.failed(msg="添加失败,请稍后重试")
```
### 4.7.2 表单验证异常
```python
# 表单验证失败时返回具体错误信息
form = ExampleForm(data)
if not form.is_valid():
return R.failed(msg=regular.get_err(form))
```
### 4.7.3 数据库异常
```python
# 数据库操作异常被捕获并返回友好提示
try:
instance.save()
except IntegrityError as e:
return R.failed(msg="数据完整性错误")
except Exception as e:
logging.error(f"数据库异常: {str(e)}")
return R.failed(msg="操作失败,请稍后重试")
```
@@ -0,0 +1,416 @@
# 第四章 架构设计 - 安全架构
## 4.1 安全架构概述
系统从认证、授权、防护三个维度构建安全体系:
```
+------------------+
| 安全架构 |
+------------------+
|
+-- 认证层 (Authentication)
| +-- JWT Token 认证
| +-- Token 黑名单机制
| +-- 密码哈希存储
|
+-- 授权层 (Authorization)
| +-- RBAC 权限模型
| +-- 权限节点控制
| +-- 超级管理员机制
|
+-- 防护层 (Protection)
+-- CORS 跨域配置
+-- 演示模式保护
+-- SQL注入防护
+-- XSS防护
```
## 4.2 JWT 认证机制
### 4.2.1 Token 生成
用户登录成功后,系统生成 JWT Token 返回给前端:
```python
# utils/jwt.py
import jwt
import datetime
JWT_SALT = os.environ.get('JWT_SALT', None)
JWT_ALGORITHM = 'HS256'
DEFAULT_TIMEOUT_MINUTES = 20
def create_token(payload, timeout=None):
"""生成JWT令牌"""
if timeout is None:
timeout = DEFAULT_TIMEOUT_MINUTES
payload_copy = payload.copy()
current_time = datetime.datetime.now(tz=datetime.timezone.utc)
payload_copy['exp'] = current_time + datetime.timedelta(minutes=timeout)
payload_copy['iat'] = current_time
headers = {
"typ": "JWT",
"alg": JWT_ALGORITHM
}
token = jwt.encode(
payload=payload_copy,
key=JWT_SALT,
algorithm=JWT_ALGORITHM,
headers=headers
)
return token
```
### 4.2.2 Token 验证
每次请求都需要验证 Token 的有效性:
```python
def parse_payload(token):
"""解析验证JWT令牌"""
result = {"code": 0, "data": None, "msg": "操作成功"}
if not token:
result['code'] = -1
result['msg'] = "token不能为空"
return result
# 检查Token是否在黑名单中
if is_token_blacklisted(token):
result['code'] = -1
result['msg'] = "token已失效,请重新登录"
return result
try:
# 解密验证JWT令牌
verified_payload = jwt.decode(
token, JWT_SALT, algorithms=[JWT_ALGORITHM]
)
result['data'] = verified_payload
except exceptions.ExpiredSignatureError:
result['code'] = -1
result['msg'] = "token已失效,请重新登录"
except exceptions.DecodeError:
result['code'] = -1
result['msg'] = "token认证失败,无效的令牌格式"
except exceptions.InvalidTokenError:
result['code'] = -1
result['msg'] = "非法的token,请检查令牌有效性"
return result
```
### 4.2.3 Token 黑名单
退出登录时将 Token 加入 Redis 黑名单,使 Token 立即失效:
```python
_TOKEN_BLACKLIST_PREFIX = 'token:blacklist:'
def add_token_to_blacklist(token: str) -> bool:
"""将Token加入黑名单"""
from django_redis import get_redis_connection
redis = get_redis_connection("default")
# 获取Token剩余有效期作为TTL
expiration = get_token_expiration(token)
if expiration is None:
ttl_seconds = DEFAULT_TIMEOUT_MINUTES * 60
else:
ttl_seconds = int(
(expiration - datetime.datetime.now(tz=datetime.timezone.utc)).total_seconds()
)
# 使用SHA256指纹作为Key
fingerprint = hashlib.sha256(token.encode('utf-8')).hexdigest()
key = f'{_TOKEN_BLACKLIST_PREFIX}{fingerprint}'
redis.set(key, '1', ex=ttl_seconds)
return True
def is_token_blacklisted(token: str) -> bool:
"""检查Token是否在黑名单中"""
from django_redis import get_redis_connection
redis = get_redis_connection("default")
fingerprint = hashlib.sha256(token.encode('utf-8')).hexdigest()
key = f'{_TOKEN_BLACKLIST_PREFIX}{fingerprint}'
return redis.exists(key) > 0
```
### 4.2.4 JWT 密钥安全
```python
def _validate_key_strength(key: str) -> None:
"""启动时校验JWT密钥强度"""
if not key:
raise RuntimeError(
"JWT_SALT 未配置!请在 .env 中设置至少32字节的强随机密钥。"
)
key_bytes = len(key.encode('utf-8'))
if key_bytes < 32:
logger.warning(f"JWT密钥长度不足:{key_bytes} 字节,建议至少 32 字节。")
# 启动时校验密钥强度
_validate_key_strength(JWT_SALT)
```
## 4.3 RBAC 权限模型
### 4.3.1 权限模型设计
```
用户 (User)
|
+-- 用户角色关联 (UserRole)
|
+-- 角色 (Role)
|
+-- 角色菜单关联 (RoleMenu)
|
+-- 菜单/权限 (Menu)
|
+-- 权限标识: sys:<module>:<action>
```
### 4.3.2 权限节点格式
每个操作对应一个权限节点,格式为 `sys:<module>:<action>`:
```python
# 示例权限节点
"sys:example:page" # 案例分页查询
"sys:example:detail" # 案例详情查询
"sys:example:add" # 案例添加
"sys:example:update" # 案例更新
"sys:example:delete" # 案例删除
"sys:example:status" # 案例状态更新
```
### 4.3.3 权限验证实现
```python
# middleware/permission_middleware.py
from django.contrib.auth.mixins import PermissionRequiredMixin
from utils import R
from utils.security import get_user_id
class PermissionRequired(PermissionRequiredMixin):
"""自定义权限控制混入类"""
def has_permission(self):
"""检查用户是否拥有所需权限"""
permissions = self.get_permission_required()
user_id = get_user_id(self.request)
# 超级管理员(ID=1)自动放行
if user_id and user_id != 1:
from application.menu import services
permission_list = services.get_user_permissions(user_id)
for permission in permissions:
if permission not in permission_list:
return False
return True
def handle_no_permission(self):
"""无权限访问时的处理"""
return R.failed("暂无操作权限", 401)
```
### 4.3.4 超级管理员机制
用户 ID 为 1 的用户被视为超级管理员,自动跳过所有权限检查:
```python
# 超级管理员自动放行
if user_id and user_id != 1:
# 普通用户需要验证权限
permission_list = services.get_user_permissions(user_id)
# ...
else:
# 超级管理员直接放行
return True
```
## 4.4 CORS 跨域配置
### 4.4.1 配置说明
```python
# application/settings.py
# 允许跨域请求携带凭证
CORS_ALLOW_CREDENTIALS = True
# 允许所有域名跨域访问(开发环境)
CORS_ORIGIN_ALLOW_ALL = True
# 允许跨域的正则表达式匹配规则
CORS_ALLOWED_ORIGINS_REGEXES = [
r'^http://.*?$',
]
# 允许跨域的HTTP请求方法
CORS_ALLOW_METHODS = (
'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW',
)
# 允许跨域的HTTP请求头
CORS_ALLOW_HEADERS = (
'XMLHttpRequest', 'X_FILENAME', 'accept-encoding',
'authorization', # JWT认证头
'content-type', 'dnt', 'origin', 'user-agent',
'x-csrftoken', 'x-requested-with', 'Pragma',
)
```
### 4.4.2 中间件顺序
CORS 中间件必须放在最前面:
```python
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 必须放在最前面
# ... 其他中间件
]
```
## 4.5 演示模式保护
### 4.5.1 配置说明
通过环境变量 `DJANGO_DEMO` 控制演示模式:
```python
# config/env.py
DJANGO_DEMO = (os.getenv('DJANGO_DEMO', 'True') == 'True')
```
### 4.5.2 演示模式限制
在演示模式下,所有写操作(添加、更新、删除)都会被拦截:
```python
# application/example/views.py
from config.env import DJANGO_DEMO
class ExampleAddView(PermissionRequired, View):
def post(self, request):
# 演示环境禁止操作
if DJANGO_DEMO:
return R.failed("演示环境,暂无操作权限")
result = services.add_example(request)
return result
```
### 4.5.3 受保护的操作
| 操作 | HTTP方法 | 演示模式行为 |
|------|----------|--------------|
| 添加 | POST | 拦截 |
| 更新 | PUT | 拦截 |
| 删除 | DELETE | 拦截 |
| 状态更新 | PUT | 拦截 |
| 查询 | GET | 放行 |
## 4.6 密码安全
### 4.6.1 密码哈希存储
密码使用 Django 内置的密码哈希机制,支持多种哈希算法:
```python
# utils/password.py
from django.contrib.auth.hashers import make_password, check_password
def hash_password(password):
"""哈希密码"""
return make_password(password)
def verify_password(password, hashed_password):
"""验证密码"""
return check_password(password, hashed_password)
```
### 4.6.2 密码验证器
```python
# application/settings.py
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
```
## 4.7 SQL 注入防护
### 4.7.1 Django ORM 防护
Django ORM 自动使用参数化查询,防止 SQL 注入:
```python
# ORM查询 - 自动参数化,安全
Example.objects.filter(name__contains=name)
Example.objects.filter(id=instance_id, is_delete=False)
```
### 4.7.2 原始 SQL 注意事项
在使用原始 SQL 时,必须使用参数化查询:
```python
# 正确 - 参数化查询
sql = 'SELECT m.* FROM django_menu AS m WHERE ur.user_id=%s'
menu_list = Menu.objects.raw(sql, [user_id])
# 错误 - 字符串拼接(存在SQL注入风险)
# sql = f'SELECT m.* FROM django_menu AS m WHERE ur.user_id={user_id}'
```
## 4.8 CSRF 防护
### 4.8.1 CSRF 禁用说明
由于系统使用 JWT 进行认证,CSRF 中间件已禁用:
```python
MIDDLEWARE = [
# CSRF中间件已注释,使用JWT进行认证
# 'django.middleware.csrf.CsrfViewMiddleware',
]
```
### 4.8.2 安全替代方案
JWT Token 通过 `Authorization` 请求头传递,天然免疫 CSRF 攻击:
```python
headers = {
"Authorization": "Bearer <token>"
}
```
## 4.9 安全配置清单
### 生产环境检查项
| 配置项 | 开发环境 | 生产环境 | 说明 |
|--------|----------|----------|------|
| `DEBUG` | `True` | `False` | 必须关闭调试模式 |
| `SECRET_KEY` | 默认值 | 随机强密钥 | 必须修改 |
| `ALLOWED_HOSTS` | `['*']` | 具体域名 | 限制访问来源 |
| `CORS_ORIGIN_ALLOW_ALL` | `True` | `False` | 限制跨域来源 |
| `DJANGO_DEMO` | `True` | `False` | 关闭演示模式 |
| `JWT_SALT` | 默认值 | 随机强密钥 | 至少32字节 |
| `REDIS_PASSWORD` | `123456` | 强密码 | Redis访问密码 |
| `DATABASE_PASSWORD` | `root` | 强密码 | 数据库访问密码 |
@@ -0,0 +1,221 @@
# 第四章 架构设计 - 技术栈选型
## 4.1 后端技术栈
### Django 6.0.3
**选型理由**:
- 成熟稳定的 Python Web 框架,社区活跃,文档完善
- 内置 ORM、Admin 后台、表单验证等开箱即用的功能
- 完善的安全机制(CSRF、XSS、SQL注入防护)
- 灵活的中间件和信号机制,便于扩展
**关键配置**(`application/settings.py`):
```python
# 数据库配置
DATABASES = {
"default": {
"ENGINE": env.DATABASE_ENGINE, # django.db.backends.mysql
"NAME": env.DATABASE_NAME,
"USER": env.DATABASE_USER,
"PASSWORD": env.DATABASE_PASSWORD,
"HOST": env.DATABASE_HOST,
"PORT": env.DATABASE_PORT,
}
}
# 缓存配置(Redis)
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{env.REDIS_HOST}:{env.REDIS_PORT}/{env.REDIS_DB}",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100},
},
"KEY_PREFIX": "djangoadmin",
}
}
```
### MySQL 8.0
**选型理由**:
- 生产级关系型数据库,性能稳定可靠
- 支持事务、外键、索引等企业级特性
- 与 Django ORM 深度集成,驱动成熟
- 社区版免费,降低项目成本
**配置说明**(`config/env.py`):
```python
DATABASE_ENGINE = os.getenv('DATABASE_ENGINE', "django.db.backends.mysql")
DATABASE_NAME = os.getenv('DATABASE_NAME', 'djangoadmin.django.elevue')
DATABASE_HOST = os.getenv('DATABASE_HOST', "127.0.0.1")
DATABASE_PORT = os.getenv('DATABASE_PORT', 3306)
DATABASE_USER = os.getenv('DATABASE_USER', 'root')
DATABASE_PASSWORD = os.getenv('DATABASE_PASSWORD', 'root')
DATABASE_PREFIX = os.getenv('DATABASE_PREFIX', "django_")
```
### Redis
**选型理由**:
- 高性能内存数据库,读写延迟极低
- 支持多种数据结构(String、Hash、List、Set)
- 用于缓存、会话存储、Token 黑名单等场景
- 支持过期时间设置,适合临时数据存储
**应用场景**:
| 场景 | 数据结构 | Key格式 | 说明 |
|------|----------|---------|------|
| Token黑名单 | String | `token:blacklist:<fingerprint>` | 存储已注销的JWT Token |
| IP地理位置缓存 | String | `ip:location:<ip>` | 缓存IP查询结果 |
| 验证码存储 | String | `captcha:<key>` | 存储图形验证码 |
## 4.2 认证技术
### JWT (JSON Web Token)
**选型理由**:
- 无状态认证,服务端无需存储会话信息
- 支持分布式部署,无 Session 共享问题
- Token 自包含用户信息,减少数据库查询
- 标准化协议,前后端通用
**实现细节**(`utils/jwt.py`):
```python
# JWT配置
JWT_SALT = os.environ.get('JWT_SALT', None) # 密钥,优先从环境变量读取
JWT_ALGORITHM = 'HS256' # 签名算法
DEFAULT_TIMEOUT_MINUTES = 20 # 默认过期时间(分钟)
def create_token(payload, timeout=None):
"""生成JWT令牌"""
payload_copy = payload.copy()
payload_copy['exp'] = current_time + datetime.timedelta(minutes=timeout)
payload_copy['iat'] = current_time
token = jwt.encode(payload=payload_copy, key=JWT_SALT, algorithm=JWT_ALGORITHM)
return token
def parse_payload(token):
"""解析验证JWT令牌"""
verified_payload = jwt.decode(token, JWT_SALT, algorithms=[JWT_ALGORITHM])
return verified_payload
```
**Token 黑名单机制**:
- 退出登录时将 Token 加入 Redis 黑名单
- 黑名单条目的 TTL 与 Token 剩余有效期一致
- Token 自然过期后黑名单条目自动清除
- 使用 SHA256 指纹避免存储完整 Token
## 4.3 前端技术栈
### Vue3 + TypeScript
**选型理由**:
- 响应式框架,开发体验优秀
- Composition API 提供更好的代码组织方式
- TypeScript 提供类型安全,减少运行时错误
- 生态成熟,社区活跃
### ElementPlus
**选型理由**:
- 基于 Vue3 的企业级 UI 组件库
- 组件丰富,覆盖表单、表格、弹窗等常见场景
- 文档完善,中文社区支持好
- 与 Vue3 深度集成
### Vite
**选型理由**:
- 下一代前端构建工具,启动速度快
- 原生 ESM 支持,开发体验好
- 热更新速度快,提升开发效率
- 配置简洁,易于上手
**开发代理配置**(`ui/.env.development`):
```
VITE_PROXY = [["/api", "http://127.0.0.1:8000"]]
```
### Pinia
**选型理由**:
- Vue3 官方推荐的状态管理方案
- 相比 Vuex 更简洁,支持 TypeScript
- 模块化设计,便于代码组织
- 支持 SSR,扩展性强
## 4.4 代码生成技术
### Jinja2
**选型理由**:
- Python 生态成熟的模板引擎
- 语法简洁,学习成本低
- 支持模板继承和宏定义
- 与 Django 模板语法不冲突
**模板目录**(`public/templates/`):
```
public/templates/
+-- models.py.tpl # 模型模板
+-- forms.py.tpl # 表单模板
+-- services.py.tpl # 服务模板
+-- views.py.tpl # 视图模板
+-- urls.py.tpl # 路由模板
+-- apps.py.tpl # 应用配置模板
+-- admin.py.tpl # Admin注册模板
+-- ui/
+-- index.vue.tpl # 列表页模板
+-- edit.vue.tpl # 编辑页模板
+-- detail.vue.tpl # 详情页模板
+-- api.ts.tpl # API模块模板
```
## 4.5 依赖版本
### 后端依赖(requirements.txt)
```
Django==6.0.3
djangorestframework
django-redis
django-cors-headers
django-simple-captcha
PyJWT
mysqlclient
redis
jinja2
```
### 前端依赖(package.json)
```json
{
"dependencies": {
"vue": "^3.x",
"element-plus": "^2.x",
"pinia": "^2.x",
"vue-router": "^4.x",
"axios": "^1.x"
},
"devDependencies": {
"vite": "^5.x",
"typescript": "^5.x",
"vue-tsc": "^2.x"
}
}
```
## 4.6 版本兼容性
| 组件 | 最低版本 | 推荐版本 | 说明 |
|------|----------|----------|------|
| Python | 3.12 | 3.12+ | 推荐使用 Python 3.12 |
| MySQL | 8.0 | 8.0+ | 需要支持 JSON 字段 |
| Redis | 6.0 | 7.0+ | 需要支持 Stream 等特性 |
| Node.js | 18.0 | 20+ | Vite 5 要求 Node 18+ |
| pnpm | 12.0 | 12+ | 包管理器 |