签入版本
This commit is contained in:
@@ -0,0 +1,419 @@
|
||||
# 系统架构说明
|
||||
|
||||
本文档详细说明 DjangoAdmin FastAPI + EleVue 版本的整体架构设计、分层模式、核心模块以及关键设计决策。
|
||||
|
||||
|
||||
## 📐 整体架构
|
||||
|
||||
### 前后端分离架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 前端 (Vue 3) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Vue 3 │ │ Element │ │ Pinia │ │ Vue │ │
|
||||
│ │ + TS │ │ Plus │ │ 状态管理 │ │ Router │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ ↕ Axios HTTP Client (Bearer Token) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↕ JSON / JWT
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 后端 (FastAPI) │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ 中间件链 │ │
|
||||
│ │ CORS → Redis注入 → 限流 → DB会话 → 操作日志 → 日志 │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ API 路由层 │ │
|
||||
│ │ /api/v1/auth/* (免认证) /api/v1/* (login_required) │ │
|
||||
│ │ @permission_required @check_demo │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ 业务模块层 │ │
|
||||
│ │ Service → Repository → Model │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
│ ↕ ↕ │
|
||||
│ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ MySQL / │ │ Redis │ │
|
||||
│ │ PG / │ │ 缓存 │ │
|
||||
│ │ MSSQL / │ │ 会话 │ │
|
||||
│ │ Oracle │ │ 黑名单 │ │
|
||||
│ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
## 🔄 请求处理流程
|
||||
|
||||
### 完整请求生命周期
|
||||
|
||||
```
|
||||
1. 客户端发起 HTTP 请求
|
||||
↓
|
||||
2. CORS 中间件(src/middleware/cors.py)
|
||||
- 处理跨域预检请求
|
||||
↓
|
||||
3. Redis 注入中间件(src/core/redis_client.py: redis_middleware)
|
||||
- 将异步 Redis 客户端注入 contextvars
|
||||
↓
|
||||
4. 限流中间件(src/middleware/rate_limit.py)
|
||||
- 基于 Redis 滑动窗口的 per-IP 限流
|
||||
- 超限返回 {code: 1} 标准响应
|
||||
↓
|
||||
5. DB 会话中间件(src/middleware/db_session.py)
|
||||
- 创建请求级数据库会话
|
||||
- 请求结束后自动 commit/rollback
|
||||
↓
|
||||
6. 操作日志中间件(src/middleware/operation_log.py)
|
||||
- 记录写操作(POST/PUT/DELETE)的行为日志
|
||||
↓
|
||||
7. 路由匹配
|
||||
- auth 路由(/api/v1/auth/*):免认证
|
||||
- v1 路由(/api/v1/*):login_required 依赖校验 JWT
|
||||
↓
|
||||
8. 权限装饰器 @permission_required
|
||||
- 校验当前用户是否有该接口的权限
|
||||
- 用户 ID 1(admin)自动跳过权限检查
|
||||
↓
|
||||
9. 演示模式装饰器 @check_demo
|
||||
- 演示环境下拦截写操作
|
||||
↓
|
||||
10. 端点处理函数
|
||||
- 参数由 Pydantic 自动校验
|
||||
- 调用 Service 层业务逻辑
|
||||
- 返回 R.ok() / R.failed() 统一响应
|
||||
↓
|
||||
11. 异常处理
|
||||
- RequestValidationError → {code: 1, msg: "field: message"}
|
||||
- BusinessException → {code: 1, msg: "业务错误"}
|
||||
- AuthorizationException → {code: 401, msg: "认证失败"}
|
||||
```
|
||||
|
||||
|
||||
## 📦 分层模块模式
|
||||
|
||||
### 四层架构
|
||||
|
||||
每个业务模块遵循统一的四层架构设计:
|
||||
|
||||
```
|
||||
src/modules/{group}/{name}/
|
||||
├── models.py # 数据模型层 — SQLAlchemy ORM 模型定义
|
||||
├── schemas.py # 数据校验层 — Pydantic 请求/响应模型
|
||||
├── repository.py # 数据访问层 — 通用 CRUD 与自定义查询
|
||||
└── service.py # 业务逻辑层 — 事务管理、缓存、业务规则
|
||||
```
|
||||
|
||||
#### Model 层(数据模型)
|
||||
|
||||
```python
|
||||
# src/modules/system/level/models.py
|
||||
from core.base_model import base_model
|
||||
from core.base_db import base_db
|
||||
from core.config.database import DB_PREFIX
|
||||
|
||||
class SysLevel(base_model, base_db):
|
||||
"""职级表"""
|
||||
__tablename__ = DB_PREFIX + "level"
|
||||
|
||||
name = Column(String(100), nullable=False, comment="职级名称")
|
||||
code = Column(String(50), nullable=False, comment="职级编码")
|
||||
sort = Column(Integer, default=0, comment="排序")
|
||||
status = Column(Integer, default=1, comment="状态 1=启用 0=禁用")
|
||||
remark = Column(String(500), comment="备注")
|
||||
```
|
||||
|
||||
- 继承 `base_model`:提供 `id`, `create_user`, `create_time`, `update_user`, `update_time`, `is_delete`
|
||||
- 继承 `base_db`:提供 `save()`, `delete()` 实例方法
|
||||
- 表名使用 `DB_PREFIX` 前缀,支持多项目共库
|
||||
|
||||
#### Schema 层(数据校验)
|
||||
|
||||
```python
|
||||
# src/modules/system/level/schemas.py
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
class LevelForm(BaseModel):
|
||||
"""职级表单"""
|
||||
id: Optional[int] = Field(None, description="ID")
|
||||
name: str = Field(..., min_length=1, max_length=100, description="职级名称")
|
||||
code: str = Field(..., min_length=1, max_length=50, description="职级编码")
|
||||
sort: int = Field(0, ge=0, description="排序")
|
||||
status: int = Field(1, description="状态")
|
||||
remark: Optional[str] = Field(None, max_length=500, description="备注")
|
||||
|
||||
@field_validator('name')
|
||||
@classmethod
|
||||
def validate_name(cls, v):
|
||||
if not v.strip():
|
||||
raise ValueError("职级名称不能为空")
|
||||
return v.strip()
|
||||
```
|
||||
|
||||
- 继承 Pydantic `BaseModel`
|
||||
- 使用 `Field(...)` 定义约束(`min_length`, `max_length`, `ge`, `le`)
|
||||
- 使用 `@field_validator` 自定义校验逻辑
|
||||
|
||||
#### Repository 层(数据访问)
|
||||
|
||||
```python
|
||||
# src/modules/system/level/repository.py
|
||||
from core.base_repository import BaseRepository
|
||||
from .models import SysLevel
|
||||
|
||||
class LevelRepository(BaseRepository[SysLevel]):
|
||||
model = SysLevel
|
||||
|
||||
level_repo = LevelRepository()
|
||||
```
|
||||
|
||||
- 继承 `BaseRepository[Model]`,自动获得通用 CRUD 方法
|
||||
- 模块级单例 `{name}_repo`,全局复用
|
||||
- 基类提供:`get_by_id`, `get_one`, `get_all`, `exists`, `exists_by_field`, `count`, `filter`, `filter_by`, `create`, `update`, `paginate`, `batch_delete`
|
||||
- 等值查询自动软删过滤且跳过 None 值
|
||||
|
||||
#### Service 层(业务逻辑)
|
||||
|
||||
```python
|
||||
# src/modules/system/level/service.py
|
||||
from core.base_service import BaseService
|
||||
from .models import SysLevel
|
||||
from .repository import level_repo
|
||||
|
||||
class LevelService(BaseService[SysLevel]):
|
||||
repo = level_repo
|
||||
model = SysLevel
|
||||
page_like_fields = ["name", "code"]
|
||||
page_eq_fields = ["status"]
|
||||
page_order_by = ["sort", "id"]
|
||||
unique_fields = [("name", "职级名称"), ("code", "职级编码")]
|
||||
|
||||
level_service = LevelService()
|
||||
```
|
||||
|
||||
- 继承 `BaseService[Model]`,声明差异即可获得完整 CRUD
|
||||
- 可覆盖钩子:`_apply_page_filters`, `_serialize`, `_build_create_fields`, `_build_update_fields`
|
||||
- 删除统一使用 `BaseService.batch_delete_with_r(repo, ids)` 静态方法
|
||||
|
||||
### HTTP 层(API 端点)
|
||||
|
||||
```python
|
||||
# src/api/v1/endpoints/level.py
|
||||
from fastapi import APIRouter, Request
|
||||
from core.access_decorators import permission_required, check_demo
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get('/list')
|
||||
@permission_required("sys:level:list")
|
||||
async def list(request: Request):
|
||||
"""职级列表"""
|
||||
return await level_service.page_list(request)
|
||||
|
||||
@router.post('/add')
|
||||
@permission_required("sys:level:add")
|
||||
@check_demo
|
||||
async def add(request: Request, data: LevelForm):
|
||||
"""新增职级"""
|
||||
return await level_service.add(request, data)
|
||||
```
|
||||
|
||||
- 路由定义、权限装饰器、请求/响应处理
|
||||
- 无业务逻辑,纯委托给 Service 层
|
||||
|
||||
|
||||
## 🔐 认证与授权
|
||||
|
||||
### JWT 认证流程
|
||||
|
||||
```
|
||||
1. 登录(/api/v1/auth/login)
|
||||
- 验证用户名 + 密码(bcrypt)
|
||||
- 验证图形验证码
|
||||
- 生成 JWT Token(HS256 签名)
|
||||
- 返回 {access_token, token_type}
|
||||
|
||||
2. 请求认证(login_required 依赖)
|
||||
- 从 Authorization: Bearer <token> 提取 Token
|
||||
- 解析 JWT,验证签名与过期时间
|
||||
- 检查 Token 是否在 Redis 黑名单中
|
||||
- 设置 request.state.user_id / username / realname
|
||||
|
||||
3. Token 刷新(/api/v1/auth/refresh)
|
||||
- 使用当前 Token 获取新 Token
|
||||
- 旧 Token 加入 Redis 黑名单
|
||||
|
||||
4. 登出(/api/v1/auth/logout)
|
||||
- 当前 Token 加入 Redis 黑名单
|
||||
```
|
||||
|
||||
### RBAC 权限模型
|
||||
|
||||
```
|
||||
用户(User) ←→ 角色(Role) ←→ 菜单(Menu)
|
||||
│ │ │
|
||||
└─ user_role └─ role_menu └─ 权限标识
|
||||
(sys:level:list)
|
||||
```
|
||||
|
||||
- **用户-角色**:多对多关系(`sys_user_role` 中间表)
|
||||
- **角色-菜单**:多对多关系(`sys_role_menu` 中间表)
|
||||
- **菜单**:存储权限标识(如 `sys:level:list`),支持目录/菜单/按钮三种类型
|
||||
- **权限检查**:`@permission_required("sys:level:list")` 装饰器在端点执行前校验
|
||||
- **管理员豁免**:用户 ID 1(admin)自动跳过所有权限检查
|
||||
|
||||
|
||||
## 🗄️ 数据库设计
|
||||
|
||||
### 多数据库支持
|
||||
|
||||
框架通过 `DB_DRIVER` 环境变量切换数据库方言:
|
||||
|
||||
```python
|
||||
# src/core/config/database.py
|
||||
DB_DRIVER = os.getenv("DB_DRIVER", "mysql")
|
||||
|
||||
# 驱动注册表
|
||||
DRIVERS = {
|
||||
"mysql": "pymysql",
|
||||
"postgresql": "psycopg",
|
||||
"mssql": "pymssql",
|
||||
"oracle": "oracledb",
|
||||
"sqlite": "sqlite3",
|
||||
}
|
||||
```
|
||||
|
||||
- SQLAlchemy 引擎根据驱动自动选择方言
|
||||
- Model 层使用 SQLAlchemy 声明式映射,与具体数据库解耦
|
||||
- Alembic 迁移脚本自动适配方言 DDL
|
||||
|
||||
### 软删除机制
|
||||
|
||||
所有业务表使用 `is_delete` 字段实现软删除:
|
||||
|
||||
- `is_delete = 0`:正常记录
|
||||
- `is_delete = 1`:已删除记录
|
||||
|
||||
Repository 基类的等值查询方法(`get_one`, `get_all`, `count`, `filter_by`, `exists_by_field`)自动过滤已删除记录。
|
||||
|
||||
### 跨库数据迁移
|
||||
|
||||
`scripts/migrate_db.py` 支持从 MySQL 迁移数据到任意目标数据库:
|
||||
|
||||
1. 拓扑排序确定表依赖关系
|
||||
2. 分块 `SELECT * ... ORDER BY id` + `executemany` 显式带 ID 插入
|
||||
3. 自增序列重置(PG `setval` / MSSQL `DBCC CHECKIDENT`)
|
||||
4. 逐表 `COUNT(*)` 校验数据一致性
|
||||
|
||||
|
||||
## ⚡ 同步/异步约定
|
||||
|
||||
| 场景 | 方式 | 说明 |
|
||||
|---|---|---|
|
||||
| 纯 DB CRUD | 同步 `def` | FastAPI 自动在线程池执行,不阻塞事件循环 |
|
||||
| 需要 `await`(Redis/异步IO) | `async def` + `asyncio.to_thread` | 同步操作用 `to_thread` 包裹 |
|
||||
| 批量删除端点 | `async def` | 需 `await parse_batch_ids(request)` 异步读请求体 |
|
||||
|
||||
**禁止**在 `async def` 端点或 Service 内直接调用同步 DB 查询(会阻塞事件循环)。
|
||||
|
||||
|
||||
## 📁 目录结构
|
||||
|
||||
```
|
||||
DjangoAdmin_FastAPI_EleVue/
|
||||
├── src/ # 后端源码
|
||||
│ ├── main.py # 应用入口
|
||||
│ ├── core/ # 核心基础设施
|
||||
│ │ ├── app.py # 应用工厂(create_app)
|
||||
│ │ ├── config/ # 配置模块(app/database/redis/auth/captcha/email/sms)
|
||||
│ │ ├── database.py # 引擎/会话/上下文
|
||||
│ │ ├── redis_client.py # Redis 异步客户端
|
||||
│ │ ├── jwt.py # JWT 认证
|
||||
│ │ ├── password.py # 密码加密
|
||||
│ │ ├── response.py # 统一响应封装
|
||||
│ │ ├── exceptions.py # 异常定义
|
||||
│ │ ├── security.py # 安全工具
|
||||
│ │ ├── access_decorators.py # 权限/演示模式装饰器
|
||||
│ │ ├── logger.py # 日志配置
|
||||
│ │ ├── base_model.py # ORM 基础模型
|
||||
│ │ ├── base_db.py # 数据库操作基类
|
||||
│ │ ├── base_repository.py # Repository 基类
|
||||
│ │ └── base_service.py # Service 基类
|
||||
│ ├── api/ # API 路由层
|
||||
│ │ ├── __init__.py # 路由注册(register_router)
|
||||
│ │ ├── deps.py # login_required 依赖
|
||||
│ │ └── v1/
|
||||
│ │ ├── router.py # 路由汇总
|
||||
│ │ └── endpoints/ # 各模块端点定义
|
||||
│ ├── modules/ # 业务模块层
|
||||
│ │ ├── auth/ # 认证模块
|
||||
│ │ ├── system/ # 系统管理(user/role/menu/dept/position/level)
|
||||
│ │ ├── dictionary/ # 字典管理
|
||||
│ │ ├── configuration/ # 配置管理
|
||||
│ │ ├── article/ # 文章管理
|
||||
│ │ ├── notice/ # 通知公告
|
||||
│ │ ├── job/ # 定时任务
|
||||
│ │ └── ... # 其他业务模块
|
||||
│ ├── middleware/ # HTTP 中间件
|
||||
│ │ ├── cors.py # 跨域处理
|
||||
│ │ ├── rate_limit.py # 限流中间件
|
||||
│ │ ├── db_session.py # DB 会话管理
|
||||
│ │ ├── operation_log.py # 操作日志
|
||||
│ │ └── logger.py # 请求日志
|
||||
│ ├── constants/ # 常量与枚举
|
||||
│ └── utils/ # 工具函数
|
||||
├── ui/ # 前端源码(Vue 3 + TypeScript)
|
||||
├── scripts/ # 运维脚本
|
||||
│ ├── init_db.py # 数据库初始化
|
||||
│ ├── seed_dict.py # 字典数据种子
|
||||
│ ├── migrate_db.py # 跨库数据迁移
|
||||
│ └── demo_job_loop.py # 定时任务演示
|
||||
├── tests/ # 测试代码
|
||||
│ ├── contract/ # 契约快照测试
|
||||
│ ├── unit/ # 单元测试
|
||||
│ └── integration/ # 集成测试
|
||||
├── migrations/ # Alembic 迁移脚本
|
||||
├── document/ # 项目文档
|
||||
├── .env # 环境变量配置
|
||||
├── docker-compose.yml # Docker 编排
|
||||
├── Dockerfile # Docker 镜像构建
|
||||
├── Makefile # 常用命令
|
||||
├── requirements.txt # Python 依赖
|
||||
├── alembic.ini # Alembic 配置
|
||||
└── pytest.ini # 测试配置
|
||||
```
|
||||
|
||||
|
||||
## 🔧 核心设计决策
|
||||
|
||||
### 1. 为什么选择 Repository + Service 双层?
|
||||
|
||||
- **Repository** 封装数据访问细节,提供通用 CRUD,隔离 ORM 实现
|
||||
- **Service** 封装业务逻辑,管理事务、缓存、权限等横切关注点
|
||||
- 两层分离使得业务逻辑可独立测试,数据访问可独立优化
|
||||
|
||||
### 2. 为什么使用 contextvars 管理 DB 会话?
|
||||
|
||||
- FastAPI 的依赖注入不适合跨层级传递数据库会话
|
||||
- `contextvars` 提供协程安全的请求级上下文
|
||||
- 中间件创建会话,`get_db_session()` 在任意层级获取当前会话
|
||||
- 请求结束自动 commit/rollback,无需手动管理
|
||||
|
||||
### 3. 为什么使用软删除?
|
||||
|
||||
- 保留历史数据,支持数据审计和恢复
|
||||
- 避免外键级联删除导致数据丢失
|
||||
- Repository 基类自动过滤,业务层无需关心
|
||||
|
||||
### 4. 为什么 BaseRepository 等值查询跳过 None?
|
||||
|
||||
- 避免 `filter_by(name=None)` 生成 `WHERE name = NULL`(SQL 中应为 `IS NULL`)
|
||||
- 简化 Service 层的条件构建,无需逐一判断 None
|
||||
- 提供 `filter()` 原始出口供复杂查询使用
|
||||
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [部署指南](DEPLOYMENT.md) — Docker / 手动 / 生产环境部署
|
||||
- [数据库迁移](../document/数据库迁移_runbook.md) — 跨库迁移操作手册
|
||||
- [开发文档](http://docs.fastapi.elevue.djangoadmin.cn) — 在线开发文档
|
||||
@@ -0,0 +1,476 @@
|
||||
# 部署指南
|
||||
|
||||
本文档介绍 DjangoAdmin FastAPI + EleVue 版本的多种部署方式,包括本地开发、Docker 容器化部署以及生产环境部署。
|
||||
|
||||
|
||||
## 📋 环境要求
|
||||
|
||||
### 后端
|
||||
|
||||
| 依赖 | 最低版本 | 说明 |
|
||||
|---|---|---|
|
||||
| Python | 3.10+ | 推荐 3.11 或 3.12 |
|
||||
| MySQL | 5.7+ | 默认数据库,推荐 8.0+ |
|
||||
| PostgreSQL | 12+ | 可选,设置 `DB_DRIVER=postgresql` |
|
||||
| SQL Server | 2017+ | 可选,设置 `DB_DRIVER=mssql` |
|
||||
| Oracle | 19c+ | 可选,设置 `DB_DRIVER=oracle` |
|
||||
| Redis | 6.0+ | 必需,用于缓存/会话/限流 |
|
||||
|
||||
### 前端
|
||||
|
||||
| 依赖 | 最低版本 | 说明 |
|
||||
|---|---|---|
|
||||
| Node.js | 18+ | 推荐 20 LTS |
|
||||
| npm | 8+ | 或使用 pnpm / yarn |
|
||||
|
||||
|
||||
## 🚀 方式一:本地开发环境
|
||||
|
||||
### 1. 克隆项目
|
||||
|
||||
```bash
|
||||
git clone https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_EleVue.git
|
||||
cd DjangoAdmin_Fastapi_EleVue
|
||||
```
|
||||
|
||||
### 2. 配置环境变量
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
编辑 `.env` 文件,配置以下关键变量:
|
||||
|
||||
```env
|
||||
# ============ 数据库配置 ============
|
||||
DB_DRIVER=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=your_password
|
||||
DB_NAME=djangoadmin
|
||||
DB_PREFIX=fastapi_
|
||||
|
||||
# ============ Redis 配置 ============
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
REDIS_DB=0
|
||||
|
||||
# ============ JWT 配置 ============
|
||||
JWT_SALT=your-secret-key-change-in-production
|
||||
JWT_EXPIRE_MINUTES=120
|
||||
|
||||
# ============ 应用配置 ============
|
||||
APP_HOST=127.0.0.1
|
||||
APP_PORT=8031
|
||||
FASTAPI_DEMO=False
|
||||
```
|
||||
|
||||
### 3. 安装依赖
|
||||
|
||||
```bash
|
||||
# 运行 + 开发依赖
|
||||
pip install -r requirements.txt -r requirements-dev.txt
|
||||
|
||||
# 或使用 Makefile
|
||||
make install
|
||||
```
|
||||
|
||||
### 4. 初始化数据库
|
||||
|
||||
```bash
|
||||
# 自动建库 + 导入 SQL + 创建表 + Alembic stamp
|
||||
python scripts/init_db.py
|
||||
|
||||
# 或使用 Makefile
|
||||
make db-init
|
||||
```
|
||||
|
||||
### 5. 初始化字典数据(可选)
|
||||
|
||||
```bash
|
||||
python scripts/seed_dict.py
|
||||
make seed-dict
|
||||
```
|
||||
|
||||
### 6. 启动后端服务
|
||||
|
||||
```bash
|
||||
python src/main.py
|
||||
# 或使用 Makefile
|
||||
make dev
|
||||
|
||||
# 默认监听 http://127.0.0.1:8031
|
||||
# API 文档:http://127.0.0.1:8031/docs
|
||||
```
|
||||
|
||||
### 7. 启动前端服务
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
npm install
|
||||
npm run dev
|
||||
|
||||
# 默认监听 http://localhost:3000
|
||||
```
|
||||
|
||||
|
||||
## 🐳 方式二:Docker Compose 部署
|
||||
|
||||
### 1. 配置环境变量
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# 编辑 .env,将 DB_HOST 和 REDIS_HOST 改为容器名或宿主机 IP
|
||||
```
|
||||
|
||||
> ⚠️ Docker `host` 网络模式下,`DB_HOST` 和 `REDIS_HOST` 可直接使用 `127.0.0.1`。
|
||||
> 若使用 `bridge` 网络模式,需改为 `host.docker.internal` 或宿主机实际 IP。
|
||||
|
||||
### 2. 构建并启动
|
||||
|
||||
```bash
|
||||
docker-compose up --build -d
|
||||
```
|
||||
|
||||
### 3. 查看日志
|
||||
|
||||
```bash
|
||||
docker-compose logs -f fastapi_elevue
|
||||
```
|
||||
|
||||
### 4. 停止服务
|
||||
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Docker Compose 配置说明
|
||||
|
||||
```yaml
|
||||
services:
|
||||
fastapi_elevue:
|
||||
image: fastapi_elevue
|
||||
container_name: fastapi_elevue
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
network_mode: host # host 模式,直接连通宿主机网络
|
||||
ports:
|
||||
- 8031:8031 # bridge 模式下的端口映射
|
||||
volumes:
|
||||
- ./uploads:/data/apps/uploads # 上传文件持久化
|
||||
env_file:
|
||||
- .env # 注入环境变量
|
||||
command: python src/main.py
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "500m" # 单个日志文件最大 500MB
|
||||
max-file: "10" # 最多保留 10 个日志文件
|
||||
```
|
||||
|
||||
|
||||
## 🏭 方式三:生产环境部署
|
||||
|
||||
### 使用 Gunicorn + Uvicorn Worker
|
||||
|
||||
生产环境推荐使用 Gunicorn 管理 Uvicorn Worker:
|
||||
|
||||
```bash
|
||||
pip install gunicorn
|
||||
|
||||
gunicorn main:app \
|
||||
--app-dir src \
|
||||
--workers 4 \
|
||||
--worker-class uvicorn.workers.UvicornWorker \
|
||||
--bind 0.0.0.0:8031 \
|
||||
--timeout 120 \
|
||||
--access-logfile - \
|
||||
--error-logfile -
|
||||
```
|
||||
|
||||
**Worker 数量建议**:
|
||||
- CPU 密集型:`workers = CPU 核心数 + 1`
|
||||
- IO 密集型:`workers = CPU 核心数 * 2 + 1`
|
||||
- 一般推荐:4 ~ 8 个 Worker
|
||||
|
||||
### 使用 Supervisor 进程管理
|
||||
|
||||
项目已提供 `deploy/supervisor.conf` 配置文件:
|
||||
|
||||
```ini
|
||||
[program:fastapi_elevue]
|
||||
command=/path/to/venv/bin/gunicorn main:app
|
||||
--app-dir /path/to/project/src
|
||||
--workers 4
|
||||
--worker-class uvicorn.workers.UvicornWorker
|
||||
--bind 0.0.0.0:8031
|
||||
directory=/path/to/project
|
||||
user=www
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/fastapi_elevue/stdout.log
|
||||
stderr_logfile=/var/log/fastapi_elevue/stderr.log
|
||||
```
|
||||
|
||||
```bash
|
||||
# 启动
|
||||
sudo supervisorctl start fastapi_elevue
|
||||
|
||||
# 查看状态
|
||||
sudo supervisorctl status fastapi_elevue
|
||||
|
||||
# 重启
|
||||
sudo supervisorctl restart fastapi_elevue
|
||||
```
|
||||
|
||||
### Nginx 反向代理
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
# 前端静态文件
|
||||
location / {
|
||||
root /path/to/ui/dist;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 后端 API 代理
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8031;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
# 上传文件访问
|
||||
location /uploads/ {
|
||||
alias /path/to/project/uploads/;
|
||||
expires 7d;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 前端构建
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 生产构建
|
||||
npm run build
|
||||
|
||||
# 构建产物在 ui/dist/ 目录
|
||||
```
|
||||
|
||||
|
||||
## 🗄️ 数据库配置
|
||||
|
||||
### MySQL(默认)
|
||||
|
||||
```env
|
||||
DB_DRIVER=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=your_password
|
||||
DB_NAME=djangoadmin
|
||||
```
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
```env
|
||||
DB_DRIVER=postgresql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=5432
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=your_password
|
||||
DB_NAME=djangoadmin
|
||||
```
|
||||
|
||||
安装驱动:
|
||||
```bash
|
||||
pip install "psycopg[binary]"
|
||||
```
|
||||
|
||||
### SQL Server
|
||||
|
||||
```env
|
||||
DB_DRIVER=mssql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=1433
|
||||
DB_USER=sa
|
||||
DB_PASSWORD=your_password
|
||||
DB_NAME=djangoadmin
|
||||
```
|
||||
|
||||
安装驱动:
|
||||
```bash
|
||||
pip install pymssql
|
||||
```
|
||||
|
||||
### Oracle
|
||||
|
||||
```env
|
||||
DB_DRIVER=oracle
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=1521
|
||||
DB_USER=system
|
||||
DB_PASSWORD=your_password
|
||||
DB_NAME=ORCL
|
||||
```
|
||||
|
||||
安装驱动:
|
||||
```bash
|
||||
pip install oracledb
|
||||
```
|
||||
|
||||
### SQLite
|
||||
|
||||
```env
|
||||
DB_DRIVER=sqlite
|
||||
DB_NAME=djangoadmin.db
|
||||
```
|
||||
|
||||
> 💡 SQLite 无需额外驱动,Python 内置支持。适合开发/测试环境。
|
||||
|
||||
|
||||
## 🔄 跨库数据迁移
|
||||
|
||||
将现有 MySQL 数据迁移到其他数据库:
|
||||
|
||||
```bash
|
||||
# 1. 干跑 — 只连接源库,逐表数行数,打印迁移计划
|
||||
make db-migrate-dry-run ARGS="--src-driver=mysql --src-host=127.0.0.1 --src-db=djangoadmin --src-user=root --src-password=xxx --dst-driver=postgresql --dst-host=127.0.0.1 --dst-db=djangoadmin --dst-user=postgres --dst-password=xxx"
|
||||
|
||||
# 2. 正式迁移
|
||||
make db-migrate ARGS="--src-driver=mysql --src-host=127.0.0.1 --src-db=djangoadmin --src-user=root --src-password=xxx --dst-driver=postgresql --dst-host=127.0.0.1 --dst-db=djangoadmin --dst-user=postgres --dst-password=xxx"
|
||||
|
||||
# 3. 幂等重跑(加 --drop-target-first)
|
||||
make db-migrate ARGS="... --drop-target-first"
|
||||
```
|
||||
|
||||
详细操作手册参阅 [数据库迁移 Runbook](../document/数据库迁移_runbook.md)。
|
||||
|
||||
|
||||
## 🧪 测试
|
||||
|
||||
```bash
|
||||
# 运行全部测试(默认跳过集成测试)
|
||||
pytest
|
||||
|
||||
# 单元测试
|
||||
pytest tests/unit
|
||||
|
||||
# 契约快照测试(守护路由/权限/表名)
|
||||
pytest tests/contract -m contract
|
||||
|
||||
# 集成测试(需真实 MySQL/Redis)
|
||||
pytest tests/integration -m integration
|
||||
|
||||
# 查看覆盖率
|
||||
pytest --cov=src --cov-report=html
|
||||
```
|
||||
|
||||
|
||||
## 🔍 健康检查
|
||||
|
||||
### API 健康检查端点
|
||||
|
||||
```bash
|
||||
# 检查后端服务是否正常
|
||||
curl http://127.0.0.1:8031/api/v1/auth/captcha
|
||||
|
||||
# 检查 Redis 连接
|
||||
redis-cli ping
|
||||
|
||||
# 检查 MySQL 连接
|
||||
mysql -u root -p -e "SELECT 1"
|
||||
```
|
||||
|
||||
### 日志查看
|
||||
|
||||
```bash
|
||||
# 应用日志
|
||||
tail -f logs/app.log
|
||||
|
||||
# 错误日志
|
||||
tail -f logs/error.log
|
||||
|
||||
# Docker 日志
|
||||
docker-compose logs -f --tail=100
|
||||
```
|
||||
|
||||
|
||||
## ⚙️ 性能调优
|
||||
|
||||
### 数据库连接池
|
||||
|
||||
在 `.env` 中配置:
|
||||
|
||||
```env
|
||||
# 连接池大小
|
||||
DB_POOL_SIZE=10
|
||||
# 最大溢出连接数
|
||||
DB_MAX_OVERFLOW=20
|
||||
# 连接超时(秒)
|
||||
DB_POOL_TIMEOUT=30
|
||||
# 连接回收时间(秒)
|
||||
DB_POOL_RECYCLE=1800
|
||||
```
|
||||
|
||||
### Redis 连接池
|
||||
|
||||
```env
|
||||
REDIS_MAX_CONNECTIONS=20
|
||||
```
|
||||
|
||||
### Uvicorn Worker
|
||||
|
||||
根据服务器配置调整 Worker 数量:
|
||||
|
||||
```bash
|
||||
# 4 核 8G 内存服务器
|
||||
gunicorn main:app --app-dir src --workers 4 --worker-class uvicorn.workers.UvicornWorker
|
||||
|
||||
# 8 核 16G 内存服务器
|
||||
gunicorn main:app --app-dir src --workers 8 --worker-class uvicorn.workers.UvicornWorker
|
||||
```
|
||||
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q: 数据库连接失败?
|
||||
|
||||
1. 检查数据库服务是否启动
|
||||
2. 检查 `.env` 中的连接参数是否正确
|
||||
3. 检查防火墙是否开放对应端口
|
||||
4. 检查数据库用户是否有远程访问权限
|
||||
|
||||
### Q: Redis 连接失败?
|
||||
|
||||
1. 检查 Redis 服务是否启动:`redis-cli ping`
|
||||
2. 检查 `.env` 中的 Redis 配置
|
||||
3. 如设置了密码,确认 `REDIS_PASSWORD` 正确
|
||||
|
||||
### Q: 前端请求 404?
|
||||
|
||||
1. 确认后端服务已启动
|
||||
2. 检查前端代理配置(`vite.config.ts` 中的 `proxy`)
|
||||
3. 确认 API 前缀为 `/api/v1`
|
||||
|
||||
### Q: Token 过期后无法刷新?
|
||||
|
||||
1. 检查 Redis 是否正常运行(Token 黑名单存储在 Redis 中)
|
||||
2. 确认 `JWT_EXPIRE_MINUTES` 配置合理
|
||||
3. 前端应在收到 401 时自动调用刷新接口
|
||||
Reference in New Issue
Block a user