chore: 基线提交(重构前代码快照)

- git 初始化 + .gitignore 补全(根 .env / logs / instance / __pycache__ 等)
- 修复 utils/.gitignore 误将整个 utils/ 源码层忽略的问题,补入全部工具源码(含 ip2region.xdb)
- 含后端全部代码:application / extensions / utils / config / migrations
- 含前端 ui/(后续整体迁走)
- 不含根 .env(含密钥,本地保留)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DjangoAdmin Dev
2026-08-07 12:12:06 +08:00
co-authored by Claude
commit 15f2a5e097
591 changed files with 95894 additions and 0 deletions
+383
View File
@@ -0,0 +1,383 @@
# Flask+EleVue 使用文档
## 目录
- [环境配置](#环境配置)
- [基础配置](#基础配置)
- [数据库配置](#数据库配置)
- [Redis缓存配置](#redis缓存配置)
- [邮件配置](#邮件配置)
- [短信配置(阿里云)](#短信配置阿里云)
- [JWT令牌配置](#jwt令牌配置)
- [登录白名单配置](#登录白名单配置)
- [权限验证配置](#权限验证配置)
- [启动方式](#启动方式)
- [开发环境](#开发环境)
- [生产环境](#生产环境)
- [Docker部署](#docker部署)
- [Gunicorn参数调优](#gunicorn参数调优)
- [应用流程](#应用流程)
- [启动流程](#启动流程)
- [请求处理流程](#请求处理流程)
- [AOP事务管理](#aop事务管理)
- [邮件服务](#邮件服务)
- [短信服务](#短信服务)
---
## 环境配置
所有配置项在项目根目录 `.env` 文件中管理,应用启动时由 `python-dotenv` 自动加载。
### 基础配置
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `FLASK_NAME` | Flask+EleVue旗舰版 | 应用名称 |
| `FLASK_VERSION` | v2.8.0 | 应用版本 |
| `FLASK_SECRET_KEY` | - | 应用秘钥,用于Session加密和CSRF保护,必须≥16字符 |
| `FLASK_HOST` | 127.0.0.1 | 运行地址。`0.0.0.0` 允许外部访问 |
| `FLASK_PORT` | 8021 | 运行端口 |
| `FLASK_ENV` | development | 环境标识:`development` / `production` |
| `FLASK_DEBUG` | True | 调试模式。**生产环境必须设为 False** |
| `FLASK_USE_RELOADER` | False | 自动重启(仅 DEBUG=True 时生效) |
| `PROXY_NUM` | 0 | 可信代理层数。Nginx后设为1 |
| `CORS_ALLOWED_ORIGINS` | - | CORS允许的源,逗号分隔。配置后支持携带凭证 |
| `FLASK_DEMO` | False | 演示模式,True时限制写操作 |
| `CAPTCHA_BYPASS_CODE` | 618618 | 万能验证码(仅DEMO模式生效,生产环境必须为空) |
| `FLASK_UPLOAD_DIR` | - | 文件上传存储目录 |
| `FLASK_FILE_URL` | - | 图片域名,用于生成完整URL |
| `UPLOAD_ALLOWED_EXTS` | .jpg,.jpeg,.png,... | 允许上传的文件扩展名 |
| `UPLOAD_MAX_SIZE_MB` | 10 | 上传文件大小上限(MB) |
### 数据库配置
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `DB_DRIVER` | mysql | 数据库驱动:`mysql` / `pgsql` / `sqlite` |
| `DB_HOST` | 127.0.0.1 | 数据库地址 |
| `DB_PORT` | 3306 | 数据库端口 |
| `DB_DATABASE` | - | 数据库名称 |
| `DB_USERNAME` | root | 数据库账号 |
| `DB_PASSWORD` | - | 数据库密码 |
| `DB_PREFIX` | flask_ | 表前缀 |
| `DB_DEBUG` | True | 是否打印SQL日志 |
| `DB_SSL` | False | 是否启用SSL加密连接 |
连接池高级参数(取消注释即可生效):
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `DB_POOL_SIZE` | 10 | 常驻连接数 |
| `DB_MAX_OVERFLOW` | 10 | 最大额外连接数 |
| `DB_POOL_RECYCLE` | 1800 | 连接回收时间(秒),应小于MySQL的wait_timeout |
| `DB_POOL_TIMEOUT` | 30 | 获取连接超时(秒) |
| `DB_POOL_PRE_PING` | True | 使用前检测连接存活 |
### Redis缓存配置
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `REDIS_HOST` | 127.0.0.1 | Redis地址 |
| `REDIS_PORT` | 6379 | Redis端口 |
| `REDIS_PASSWORD` | - | Redis密码 |
| `REDIS_INDEX` | 0 | 库索引(0-15) |
| `REDIS_AUTH` | True | 是否需要认证 |
| `REDIS_DECODE_RESPONSES` | True | 是否自动解码 |
### 邮件配置
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `MAIL_MAILER` | smtp | 邮寄方式:`smtp` / `sendmail` |
| `MAIL_SERVER` | smtp.163.com | SMTP服务器地址 |
| `MAIL_PORT` | 465 | SMTP端口(465=SSL,587=TLS) |
| `MAIL_USE_SSL` | True | 是否启用SSL |
| `MAIL_USERNAME` | - | 授权邮箱用户名 |
| `MAIL_PASSWORD` | - | 授权码(非登录密码) |
| `MAIL_ENCRYPTION` | - | 加密方式:`ssl` / `tls` |
| `MAIL_FROM_NAME` | - | 发件人名称 |
| `MAIL_FROM_ADDRESS` | - | 发件人地址 |
### 短信配置(阿里云)
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `SMS_PROVIDER` | aliyun | 短信服务商(当前仅支持aliyun) |
| `SMS_ALIYUN_ACCESS_KEY_ID` | - | 阿里云 AccessKey ID |
| `SMS_ALIYUN_ACCESS_KEY_SECRET` | - | 阿里云 AccessKey Secret |
| `SMS_ALIYUN_SIGN_NAME` | - | 短信签名(需在阿里云审核通过) |
| `SMS_ALIYUN_ENDPOINT` | dysmsapi.aliyuncs.com | 接入点 |
| `SMS_ALIYUN_CONNECT_TIMEOUT` | 10000 | 连接超时(毫秒) |
| `SMS_ALIYUN_READ_TIMEOUT` | 10000 | 读取超时(毫秒) |
### JWT令牌配置
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `JWT_SALT` | - | JWT签名密钥,至少32字节,务必使用强随机密钥 |
| `JWT_EXPIRE_MINUTES` | 20 | 令牌过期时间(分钟) |
生成密钥:
```bash
python -c "import secrets; print(secrets.token_urlsafe(48))"
```
### 登录白名单配置
白名单路径免登录验证,支持三种匹配模式:
| 配置项 | 说明 |
|--------|------|
| `LOGIN_WHITELIST_EXACT` | 精确匹配,逗号分隔。如 `/login,/captcha` |
| `LOGIN_WHITELIST_PREFIX` | 前缀匹配,逗号分隔。如 `/public,/static` |
| `LOGIN_WHITELIST_WILDCARD` | 通配符匹配,`*` 匹配任意字符,`?` 匹配单字符 |
| `LOGIN_WHITELIST_DEBUG` | 是否打印匹配调试日志 |
| `LOGIN_WHITELIST_LOG_ACCESS` | 白名单请求是否记录访问日志 |
### 权限验证配置
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `PERMISSION_ENABLED` | True | 是否启用权限验证。False时所有请求放行 |
| `INTERNAL_API_KEY` | - | 内部调用API密钥,请求头携带 `X-Internal-API-Key` |
| `INTERNAL_ALLOWED_IPS` | 127.0.0.1 | 内部调用白名单IP,逗号分隔 |
---
## 启动方式
### 开发环境
```bash
# 安装依赖
pip install -r requirements.txt
# 启动后端(默认 127.0.0.1:8021)
python app.py
```
`python app.py` 执行 `__main__` 块,使用 `app.run()` 启动 Werkzeug 开发服务器。
开发环境支持 debug 模式和自动重载,由 `.env` 中 `FLASK_DEBUG` 和 `FLASK_USE_RELOADER` 控制。
前端开发服务器:
```bash
cd ui
pnpm install
pnpm run dev # 默认 localhost:8001,自动代理 /api 到后端 :8021
```
### 生产环境
```bash
gunicorn -c gunicorn.conf.py app:app
```
Gunicorn 通过 `app:app` 导入 Flask 应用对象,**不执行 `__main__` 块**。`gunicorn.conf.py` 中配置了 gevent worker,支持高并发。
关键区别:
| | 开发环境 | 生产环境 |
|---|---------|---------|
| 启动命令 | `python app.py` | `gunicorn -c gunicorn.conf.py app:app` |
| WSGI服务器 | Werkzeug | Gunicorn + gevent |
| Debug | 可开启 | 必须关闭 |
| 自动重载 | 可开启 | 不支持 |
| 并发模型 | 单进程 | 多worker + 协程 |
### Docker部署
```bash
cd docker
# 构建并启动
docker-compose up -d --build
# 查看日志
docker-compose logs -f flask_elevue
# 重新构建(代码更新后)
docker-compose up -d --build
# 停止
docker-compose down
```
Docker 容器内使用 Gunicorn 启动,Dockerfile 和 docker-compose.yml 已配置好 gevent worker。
### Gunicorn参数调优
所有参数通过环境变量覆盖,无需修改配置文件:
| 环境变量 | 默认值 | 说明 |
|----------|--------|------|
| `GUNICORN_BIND` | 0.0.0.0:8021 | 监听地址 |
| `GUNICORN_WORKERS` | 2*CPU+1 | worker数量 |
| `GUNICORN_WORKER_CONNECTIONS` | 1000 | 每worker协程数 |
| `GUNICORN_MAX_REQUESTS` | 1000 | 处理请求数后重启(防内存泄漏) |
| `GUNICORN_MAX_REQUESTS_JITTER` | 50 | 重启抖动,避免同时重启 |
| `GUNICORN_TIMEOUT` | 120 | worker超时(秒) |
| `GUNICORN_GRACEFUL_TIMEOUT` | 30 | 优雅关闭超时(秒) |
| `GUNICORN_LOG_LEVEL` | info | 日志级别 |
| `GUNICORN_ACCESS_LOG` | - | 访问日志路径(- 为stdout) |
| `GUNICORN_ERROR_LOG` | - | 错误日志路径(- 为stderr) |
Docker 中通过 docker-compose.yml 的 `environment` 注入:
```yaml
environment:
GUNICORN_WORKERS: 4
GUNICORN_WORKER_CONNECTIONS: 2000
```
---
## 应用流程
### 启动流程
```
app.py
└─ create_app() # 应用工厂
├─ Flask 核心对象创建
├─ Redis 配置
├─ 数据库配置加载
├─ JSON 序列化器设置
├─ CORS 跨域配置
├─ ProxyFix 可信代理配置
├─ register_operation_log() # 操作日志中间件(先于事务注册)
├─ register_transaction() # AOP 事务拦截器
├─ register_blueprint() # 蓝图自动发现与注册
├─ register_extends() # 扩展初始化(DB/Redis/Mail/...)
├─ register_middleware() # 登录验证中间件
└─ register_error_handlers() # 全局异常处理
```
### 请求处理流程
```
HTTP 请求
└─ before_request
├─ 操作日志记录(记录请求信息)
├─ AOP事务标记(标记事务开始)
├─ 登录验证(check_login,白名单路径跳过)
└─ 权限验证(check_permission 装饰器,超管/内部调用跳过)
└─ View 路由处理
└─ Service 业务逻辑
└─ Model 数据操作(flush,不commit)
└─ after_request
├─ AOP事务提交/回滚(POST/PUT/DELETE/PATCH,2xx且code=0时提交)
└─ 操作日志写入
└─ 响应返回
```
### AOP事务管理
框架通过 `application/middleware/transaction.py` 实现声明式事务管理,**业务代码无需手动 commit/rollback**。
- `before_request`:标记事务活跃
- `after_request`:
- 写操作(POST/PUT/DELETE/PATCH)且 HTTP 2xx 且响应 `code==0` → 自动提交
- 业务失败(`R.failed` 返回 HTTP 200 但 `code=1`)→ 自动回滚
- `teardown_request`:未处理异常时回滚
`BaseDB.save()` 和 `BaseDB.save_all()` 仅调用 `db.session.flush()`,由 AOP 拦截器统一提交。
特殊场景可使用:
- `@transactional` 装饰器
- `@transactional_response` 装饰器
- `TransactionContext` 上下文管理器
---
## 邮件服务
邮件服务封装在 `application/services/mail_svc.py`,基于 Flask-Mail,供业务模块和定时任务直接调用。
**调用方式:**
```python
from application.services import mail_svc
# 简单文本邮件
mail_svc.send_mail(to='user@example.com', subject='标题', body='内容')
# HTML 邮件
mail_svc.send_mail(to='user@example.com', subject='标题', html='<h1>Hello</h1>')
# 多收件人 + 抄送
mail_svc.send_mail(
to=['a@example.com', 'b@example.com'],
subject='标题',
body='内容',
cc=['cc@example.com'],
)
```
**参数说明:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `to` | str / List[str] | 是 | 收件人 |
| `subject` | str | 是 | 邮件主题 |
| `body` | str | 否* | 纯文本正文 |
| `html` | str | 否* | HTML正文(与body至少提供一个) |
| `cc` | str / List[str] | 否 | 抄送 |
| `bcc` | str / List[str] | 否 | 密送 |
| `reply_to` | str | 否 | 回复地址 |
返回 `True` 表示发送成功,`False` 表示失败(异常已捕获并记录日志)。
**前置条件:** `.env` 中配置 `MAIL_SERVER`、`MAIL_USERNAME`、`MAIL_PASSWORD`。
---
## 短信服务
短信服务封装在 `application/services/sms_svc.py`,基于阿里云短信 SDK,供业务模块和定时任务直接调用。
**调用方式:**
```python
from application.services import sms_svc
# 模板短信
sms_svc.send_sms(
phone='13800138000',
template_code='SMS_123456',
template_params={'code': '123456'}
)
# 指定签名(覆盖默认签名)
sms_svc.send_sms(
phone='13800138000',
template_code='SMS_123456',
template_params={'code': '123456'},
sign_name='我的应用'
)
# 群发(多号码)
sms_svc.send_sms(
phone=['13800138000', '13900139000'],
template_code='SMS_123456',
template_params={'name': '张三'}
)
```
**参数说明:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `phone` | str / List[str] | 是 | 手机号,支持单个或多个 |
| `template_code` | str | 是 | 短信模板CODE(阿里云控制台获取) |
| `template_params` | dict | 否 | 模板变量,如 `{'code': '123456'}` 对应 `${code}` |
| `sign_name` | str | 否 | 短信签名,默认使用配置中的 `SMS_ALIYUN_SIGN_NAME` |
返回 `True` 表示全部发送成功,`False` 表示至少一条失败。
**前置条件:**
1. `.env` 中配置 `SMS_ALIYUN_ACCESS_KEY_ID`、`SMS_ALIYUN_ACCESS_KEY_SECRET`、`SMS_ALIYUN_SIGN_NAME`
2. 安装依赖:`pip install alibabacloud-dysmsapi20170525 alibabacloud-tea-openapi`(已包含在 requirements.txt)