Files
DjangoAdmin_Flask_EleVue/重构方案-v2.md
T

24 KiB
Raw Blame History

DjangoAdmin Flask+EleVue 重构方案 v2(补齐版)

本文档为《重构设计.txt》的企业级补齐版,是后续执行的唯一依据。 适用范围:后端全部代码,不含 ui/(前端后续整体迁走)。 依赖分析基于 2026-08-07 代码库快照:152 个 Python 文件、约 33K 行、528 处内部 import。


0. 决策记录

# 决策点 结论 影响
D1 core→modules 依赖倒置 依赖注入:core/permission.py 定义权限校验接口,应用工厂启动时注入 menu 服务 见 §2.1
D2 验证层 仅用 WTForms,schema.py 只是表单文件改名,不引入 Marshmallow 24 个表单文件零重写
D3 执行策略 增量迁移·绞杀者模式:application/ 留薄转发层,模块逐一搬迁 见 §2.5,全程可运行可回滚
D4 API 版本化 引入 /api/v1 前缀,所有蓝图挂在 v1 下 见 §2.3,需联动前端与白名单
D5 服务层改造 做:服务函数改为「入参数据、出参数据」,router 负责取 request/包 R 见 §2.4,本次最大工作量
D6 tenant 模块 本轮不做,从方案删除,后续作为新功能单独立项 见 §3 映射表

前置硬约束(P0):动手前必须先 git init + 基线提交。当前不是 git 仓库,无任何回滚兜底。


1. 目标架构总览

1.1 目录树(更新版)

DjangoAdmin_Flask_EleVue/
├── .env / .env.example / .gitignore / .editorconfig
├── .flake8 / .pre-commit-config.yaml
├── pyproject.toml            # 项目管理 + tool 配置(pytest/black/isort/mypy 并入,不另设 pytest.ini)
├── requirements.txt          # 生产依赖(保持)
├── requirements-dev.txt      # 开发依赖
├── Makefile
├── README.md / README.en.md / CHANGELOG.md / CONTRIBUTING.md
├── CLAUDE.md / USAGE.md / LICENSE
├── gunicorn.conf.py / gunicorn_dev.conf.py
├── Dockerfile                # ★ 从 docker/ 提到根目录(修复 compose build 上下文)
├── docker-compose.yml        # ★ 从 docker/ 提到根目录
├── .dockerignore
│
├── src/                      # ★ 核心源码(原 application/)
│   ├── __init__.py
│   ├── app.py                # 应用工厂(原 application/__init__.py)
│   ├── wsgi.py               # 生产 WSGI 入口
│   ├── extensions.py         # register_extends:扩展编排(原 extensions/__init__.py)
│   ├── models_registry.py    # ★ 模型聚合器(Flask-Migrate 依赖,见 §2.2)
│   │
│   ├── core/                 # 基础设施层(不依赖 modules)
│   │   ├── __init__.py
│   │   ├── database.py       # db 实例(原 extensions/sqlalchemy.py)
│   │   ├── redis_client.py   # redis 实例(原 extensions/redis.py)
│   │   ├── scheduler.py      # APScheduler 扩展实例(原 extensions/scheduler.py)
│   │   ├── mail.py           # Flask-Mail 实例(原 extensions/mail.py)
│   │   ├── jwt.py            # 原 utils/jwt.py
│   │   ├── password.py       # 原 utils/password.py
│   │   ├── security.py       # get_user_id / get_username(原 utils/security.py)
│   │   ├── response.py       # R(原 utils/response.py)
│   │   ├── exceptions.py     # BizError / AuthError 等 + 全局异常处理器
│   │   ├── middleware.py     # check_login / check_demo / transaction / op_log(op_log 接口化)
│   │   ├── permission.py     # ★ PermissionProvider 协议 + check_permission 装饰器(见 §2.1)
│   │   ├── logger.py         # 结构化日志初始化(配置在 config/logging.py)
│   │   └── context.py        # 请求上下文管理
│   │
│   ├── common/               # 共享层(依赖 core)
│   │   ├── __init__.py
│   │   ├── constants/
│   │   │   ├── __init__.py
│   │   │   ├── common.py
│   │   │   ├── dictionary_constants.py   # ★ 原 constants/dict.py(改名避内建遮蔽)
│   │   │   └── error_code.py             # 新增:错误码定义
│   │   ├── enums.py
│   │   ├── pagination.py                 # 分页统一封装
│   │   ├── base_model.py                 # 原 models/base_model.py
│   │   ├── base_db.py                    # ★ 原 models/base_db.py(原方案漏列)
│   │   ├── base_repository.py            # 新增
│   │   ├── base_service.py               # 新增
│   │   ├── forms/                        # ★ 原 application/forms/ 拆出
│   │   │   ├── __init__.py
│   │   │   ├── base_form.py
│   │   │   └── fields.py                 # ★ 原 forms/fields.py(ArrayField,原方案漏列)
│   │   ├── decorators.py
│   │   ├── validators.py
│   │   └── utils/
│   │       ├── __init__.py               # ★ 保留 `from common.utils import R, regular, password` 兼容
│   │       ├── request.py / model_utils.py / case_converter.py / regular.py
│   │       ├── captcha.py / file.py / rich_text.py / ip.py / user_agent.py
│   │       ├── formatter.py / json.py
│   │       ├── dict_utils.py             # ★ 原 utils/dict.py(改名)
│   │       └── ip2region/                # 原 utils/ip2region.py + .xdb(10MB 二进制)
│   │           ├── __init__.py
│   │           └── ip2region.xdb
│   │
│   ├── modules/             # 业务层(依赖 core/common;允许跨模块调用业务服务)
│   │   ├── __init__.py      # ★ 导入全部模块(含各模块 models),供模型聚合
│   │   ├── auth/            # router(login/captcha/logout + index 自服务) / schema / service / repository
│   │   ├── system/          # user / role / menu / dept / position / level
│   │   ├── association/     # user_role / role_menu
│   │   ├── dictionary/      # dict + dict_item 合并(router 保留 /dict 与 /dict/item 两个前缀)
│   │   ├── configuration/   # config + config_item 合并(/config 与 /config/item)
│   │   ├── cms/             # article / category / link
│   │   ├── notice/  param/  city/  file_template/  upload/
│   │   ├── job/             # router / schema / service / models / executor
│   │   │                    #   + scheduler_service.py(★ 原 services/scheduler.py)
│   │   │                    #   + mail_service.py(★ 原 services/mail_svc.py)
│   │   │                    #   + sms_service.py(★ 原 services/sms_svc.py)
│   │   ├── log/             # login_log + operation_log 合并
│   │   └── monitor/         # ★ 新增:/health 健康检查
│   │
│   ├── routes/
│   │   ├── __init__.py
│   │   └── v1.py            # v1 = Blueprint('/api/v1'),注册全部业务蓝图
│   │
│   └── templates/           # ★ 从方案移除(当前异常全走 JSON,无模板渲染需求,见 §3 说明)
│
├── config/                  # 保持并新增 logging.py
├── migrations/              # 保持(env.py 不变,模型聚合依赖 src/models_registry.py)
├── tests/
│   ├── conftest.py          # 测试 App 构建 + 测试库/Redis fixture
│   ├── unit/                # core / common / modules 服务单测
│   └── integration/         # API 集成测试 + test_database.py
├── scripts/                 # init_db / seed_data / migrate_structure / backup_db / health_check
├── static/  uploads/  logs/  instance/
├── docker/                  # ★ 保留目录,仅放 nginx/ + entrypoint.sh(Dockerfile/compose 已上提根目录)
└── document/

1.2 分层依赖规则(硬性约束)

        ┌─────────────────────────────┐
        │  modules(业务层,可互调)     │
        └──────────────┬──────────────┘
                       │
        ┌──────────────▼──────────────┐
        │  common(共享层)             │
        └──────────────┬──────────────┘
                       │
        ┌──────────────▼──────────────┐
        │  core(基础设施层)           │
        └─────────────────────────────┘

约束:
1. core 不得 import modules(依赖倒置由 DI 解决,见 §2.1)
2. modules 之间允许跨模块 import 业务服务(如 auth→log、user→association)
3. 全局异常由 core/exceptions.py 统一处理
4. 所有模型必须经 src/models_registry.py 在启动时导入一次

2. 关键设计

2.1 权限校验依赖注入(D1,P0-1)

问题:现 application/middleware/check_permission.py:49 直接 from application.services import menu。迁入 core/ 后违反依赖规则。

设计:

# src/core/permission.py
from typing import Protocol, List

class PermissionProvider(Protocol):
    """权限数据提供方接口:core 只认接口,不认实现。"""
    def get_user_permissions(self, user_id: int) -> List[str]: ...

# check_permission 装饰器内部:
provider = current_app.extensions.get('permission_provider')
if provider is None:
    # 未注入(如 PERMISSION_ENABLED=False)→ 放行或报错,由配置决定
    ...
permissions = provider.get_user_permissions(user_id)
# src/app.py 应用工厂,注册完蓝图后注入:
from modules.system.menu.service import get_user_permissions
from modules.system.menu.repository import MenuRepository
app.extensions['permission_provider'] = MenuPermissionProvider(get_user_permissions)

同一模式覆盖 op_log:op_log.py 直接引用 OperationLog 模型(application/middleware/op_log.py:21)。同样在 core/middleware.py 定义 OperationLogWriter 回调协议,由 modules/log/ 在启动时注册写入实现。

校验项:check_permission 与 op_log 迁移到 core 后,grep -r "from application" src/core/ 结果必须为空。

2.2 模型聚合与 Flask-Migrate(P1-3)

migrations/env.py:26 依赖 current_app.extensions['migrate'].db.metadata 全量收集表。模型打散到各模块后,必须保证每个模型模块在启动时被 import 一次。

# src/models_registry.py —— 唯一权威清单(等价原 application/models/__init__.py)
from modules.auth import models as _auth_models          # auth 无独立表则不导入
from modules.system.user import models as _user_models
from modules.system.role import models as _role_models
# ... 全量列出
  • src/app.py 在 register_extends 之前 import src.models_registry。
  • 绞杀者过渡期:已迁移模型进 registry,未迁移的仍由 application/models/__init__.py 导入,二者共同填满 db.metadata;每次迁移一批后跑一次 flask db migrate 验证 autogenerate 仍全量识别。
  • 校验项:flask db migrate 生成的空迁移中 upgrade_ops.is_empty() 必须为 False(即所有表仍被识别)。

2.3 API v1 版本化(D4)

# src/routes/v1.py
v1 = Blueprint('v1', __name__, url_prefix='/api/v1')
v1.register_blueprint(user_blue)       # 模块内蓝图保持 url_prefix='/user'
v1.register_blueprint(menu_blue)
# ...
app.register_blueprint(v1)

联动清单(缺一不可):

  1. config/auth.py 的 LOGIN_WHITELIST_* 增加 /api/v1/login、/api/v1/captcha 白名单。
  2. 前端 ui/ 所有 API 调用前缀改为 /api/v1/...(VITE_PROXY 仍为 /api → 8021,无需改 proxy)。
  3. 后端内部 HTTP 调用(modules/job/scheduler_service.py 用 requests 调内部接口,携带 X-Scheduler-Key)URL 追加 /api/v1。
  4. 验收点:当前 ui/ 与后端将短暂不同步,需排一个前端 API 前缀同步改动(可在 ui/ 迁走前一次性改)。

2.4 服务层测试化改造(D5)

现状:user.get_user_page() 内部直接读 request、返回 R 对象 → 无法单测。

目标形态:

# router.py
@user_blue.route('/page', methods=['GET'])
@check_permission('sys:user:page')
def page():
    params, err = parse_query_params()          # router 层取参
    if err: return err
    return R.ok(data=user_service.get_user_page(params))   # router 层包 R

# service.py(纯函数化,可单测)
def get_user_page(params: dict) -> dict:
    query = repository.build_query(params)
    items, total = repository.paginate(query, params.get('page', 1), PAGE_SIZE)
    return {'records': convert(items), 'total': total}

# 业务错误
def reset_user_password(params: dict) -> None:
    user = repository.get_by_id(params['id'])
    if not user:
        raise BizError('用户不存在', code=1)     # 由 core/exceptions.py 统一转 R.failed
  • 业务错误一律 raise BizError,不再 return R.failed(core/exceptions.py 提供 register_error_handlers 捕获转换)。
  • base_service.py / base_repository.py 提供泛型 CRUD 骨架,各模块 repository.py 承载查询。
  • 工作量提示:这是本次对业务代码最深的一刀,26 个服务文件全部涉及,独立成 Phase 4(见 §4)。

2.5 绞杀者迁移模式(D3)

目标:全程 app.py(app:app)可启动、接口可访问,每步可回滚。

原则:自底向上、按依赖序迁移;迁完一个旧文件立即换成薄转发层。

旧文件 shim 示例:
# application/services/user.py  →  shim
from src.modules.system.user.service import *    # 保持 `from application.services import user` 可用

# application/models/user.py    →  shim
from src.modules.system.user.models import *
# (注意 models shim 必须同时导出模型类 User 与 BaseDB/BaseModel 组合,保证 application/models/__init__.py 汇总正常)

# extensions/sqlalchemy.py      →  shim
from src.core.database import db                # 统一 db 单例

迁移顺序(严格依赖序):

  1. src/core/(db/redis/R/exceptions/logger)→ 打 shim 于 extensions/、utils/
  2. src/common/(base_model/base_db/base_form/utils)→ 打 shim 于 application/models/、application/forms/、utils/
  3. 各模块 models → forms → services → views,逐组迁移、逐组验证
  4. core/permission.py + 应用工厂注入
  5. src/routes/v1.py + /api/v1 切换
  6. src/app.py 全量接管,删除 application/ extensions/ utils/,WSGI 切 src.wsgi:app

回滚:任一阶段失败,回滚到上一阶段 shim(git commit 粒度)。

2.6 验证层(D2)

  • 24 个表单文件零逻辑重写,仅随模块搬移并改名 schema.py(文件名 forms/login.py → modules/auth/schema.py)。
  • BaseForm + ArrayField 迁至 common/forms/。
  • 不引入 Marshmallow,requirements.txt 不加新依赖。

2.7 健康检查与 Docker 修复

  • src/modules/monitor/router.py:GET /api/v1/health(返回 {code:0, data:{status:'ok', version:...}}),并加入登录白名单。
  • Docker 修复清单:
    1. Dockerfile/docker-compose.yml 上提根目录,build: context: ./、dockerfile: Dockerfile(修复现有 build 上下文错误);
    2. 去掉 privileged: true、user: root、network_mode: host → cap_add + 非特权用户 + bridge 网络;
    3. HEALTHCHECK 改为探测 /api/v1/health;
    4. CMD 改为 gunicorn -c gunicorn.conf.py src.wsgi:app。

3. 完整文件映射清单(含原方案遗漏项修正)

3.1 原方案遗漏文件(★ = 补齐项)

原文件 去向 说明
services/scheduler.py(19KB) modules/job/scheduler_service.py 业务调度器,被 extensions/scheduler.py 6 处引用
services/mail_svc.py / sms_svc.py modules/job/mail_service.py / sms_service.py 被 job_executor / job 引用
forms/fields.py common/forms/fields.py ArrayField,被 base_form 与 user 表单引用
views/index.py modules/auth/router.py 自服务接口(getMenu/getUser/userInfo/updatePwd),依赖 menu/user 服务
models/base_db.py common/base_db.py 原方案只列了 base_model.py
models/login_log.py modules/log/models.py ★ 原方案 auth/models.py 与 log/models.py 冲突,定案入 log
models/operation_log.py modules/log/models.py 随 log 合并
constant/(空目录) 删除 仅 pycache
src/templates/ 从方案移除 根目录无 templates,异常全走 JSON;如确需 HTML 错误页再单独立项
system/tenant/ 从方案删除 当前代码不存在(D6)
Dockerfile / docker-compose.yml 上提根目录 见 §2.7
src/extensions.py 保留 与 core/*.py 分工:core 持有实例,extensions.py 只做 init_app 编排

3.2 模块映射汇总

新模块 蓝图中路由前缀(保持不变) 来源
auth /login /captcha /logout /index/* views/login.py + index.py
system/user /user views/user.py
system/role /role views/role.py
system/menu /menu views/menu.py
system/dept /dept views/dept.py
system/position /position views/position.py
system/level /level views/level.py
association/user_role /user/role views/user_role.py
association/role_menu /role/menu views/role_menu.py
dictionary /dict + /dict/item views/dict.py + dict_item.py
configuration /config + /config/item views/config.py + config_item.py
cms/article /article views/article.py
cms/category /category views/category.py
cms/link /link views/link.py
notice /notice views/notice.py
param /param views/param.py
city /city views/city.py
job /job views/job.py + services/scheduler.py + job_executor.py + mail_svc.py + sms_svc.py
log /login/log + /oper/log views/login_log.py + operation_log.py
file_template /file/template views/file_template.py
upload /upload views/upload.py
monitor /health 新增

Blueprint 名称(config_item、login_log 等)在 op_log 记录 request.endpoint 时使用。合并后 endpoint 名变化只影响日志展示字段,不破坏功能;如需稳定可保留原 blueprint name。


4. 分阶段执行计划(每阶段含验收标准)

阶段 内容 验收标准 风险
P0 基线 git init;.gitignore 补 logs/ instance/ *.pyc;基线提交 git 仓库可用,可 git reset --hard 回滚 无
P1 工具链 pyproject.toml / requirements-dev.txt / .flake8 / .pre-commit / Makefile / pytest 骨架 现有代码 flake8 通过(或登记存量告警);pytest 空跑通过。已登记存量告警:154 条(F401×111 / E402×36 / 其余 7);pytest 5 passed 低
P2 core+common 建 src/core/ src/common/;extensions/utils 打 shim;模型聚合器建 src/models_registry.py 已验收:22 模型表全量识别且 DB 全部存在;src/ flake8 零告警;pytest 5 passed;应用启动;/login 表单校验与 /user/page 鉴权拦截冒烟正常。基线修复:application/models + middleware 原被嵌套 .gitignore(*)误排除,已补入。登记既有缺陷:① flask db CLI 因 .env 的 FLASK_VERSION 与 --version 冲突报错(Makefile 已加 FLASK_SKIP_DOTENV 规避);② 本机 Redis 未设密码但 REDIS_AUTH=True,/captcha 报 AUTH 错误(环境问题,非重构) 高(依赖面最大,已完成)
P3 模块搬迁 按 §2.5 顺序逐组迁移 23 个模块(models→forms→services→views),每批打 shim + 验证 已验收:22 模型→19 模块 models.py;23 表单→schema.py;29 服务→service.py(含 executor/scheduler_service/mail_service/sms_service);26 视图→router.py(login+index 合并入 auth)。应用启动、25 蓝图无重复注册(views/__init__.py 已加去重)、pytest 5 passed、接口冒烟(登录校验/鉴权 401)正常 中(已完成)
P4 服务层改造 26 个服务文件改「入参数据/出参数据」;router 取参包 R;引入 base_service/base_repository + BizError ✅ 全部完成(21 模块):P4a 地基、P4b position 标杆(含单测)、P4c 六个批次(notice/param/link/level、article/category/file_template、dept/city、role/menu/user_role/role_menu、log/dictionary/configuration、job/auth/user)。每模块 flake8 零告警 + 应用启动 + 运行时验证。合并模块已修复重名函数覆盖/双 import 块/路由导入 bug。注:level 的 Excel import/export 仍返回 R(暂保留,后续统一改造) 高(全部完成)
P5 DI 注入 core/permission.py + op_log 接口化;app 工厂注入 menu 服务 ✅ 已完成:PermissionProvider 协议 + check_permission 去 menu 依赖;core/middleware.py 整合全部中间件;factory 注入 MenuPermissionProvider + record_operation_log;src/core 零 application 引用;权限/日志行为一致;flake8 零告警 中(已完成)
P6 API v1 routes/v1.py + /api/v1;白名单/内部调用/前端前缀同步 全部接口路径 curl /api/v1/... 通过;/login 白名单放行 中
P7 切尾 删除 application/extensions/utils;src/wsgi.py;gunicorn.conf 指向 src.wsgi;更新 CLAUDE.md gunicorn -c gunicorn.conf.py src.wsgi:app 启动;全量回归 高
P8 测试+文档 tests/ 全量落地;CHANGELOG / CONTRIBUTING / API 文档 pytest 通过;覆盖率报告 低
P9 运维 Docker 修复(§2.7)+ CI/CD(lint→type-check→pytest→build) docker-compose up -d 健康检查通过 低

执行纪律:

  • 每阶段独立 commit,提交信息带阶段前缀(P2: ...)。
  • 每个 P3 迁移批次后跑一遍全接口冒烟(可先用 Postman collection)。
  • P4 与 P5 可并行准备,但P3 未全部收口前不得开始 P4(服务层改造需在稳定结构上进行)。

5. 风险登记

风险 等级 缓解
无版本控制 高 P0 先行 git init
core→modules 依赖(permission/op_log) 高 DI 协议 + 启动注入(§2.1)
Flask-Migrate 表丢失 高 models_registry + 每批迁移后 flask db migrate 空迁移校验(§2.2)
/api/v1 切换导致前端/内部调用不同步 中 P6 联动清单一次性核对(§2.3)
服务层改造回归 高 P4 单独阶段 + 接口回归为硬门槛
utils R 的 import utils.response as R 别名模式 中 common/utils/__init__.py 保留同形兼容导出
10MB ip2region.xdb 路径解析 低 迁移时同步改 ip2region/__init__.py 内路径定位逻辑

6. 遗留命名权衡(已知,接受)

项 说明
根 config/ 与 modules/configuration/ 两个「config」含义不同(应用配置 vs 业务配置),import 路径不冲突,予以保留
modules/dictionary/ 与 common/ 内字典文件 已通过 dictionary_constants.py / dict_utils.py 改名消解,不再遮蔽内建 dict
repository 层为新增代码 23 个模块各加 repository.py,属结构性增量,工作量已计入 P3/P4

附:与《重构设计.txt》的主要差异

  1. 补全 9 处遗漏文件映射、修正 2 处矛盾(auth 与 log 争 login_log、templates 不存在)
  2. 新增 5 项关键设计(DI、模型聚合、/api/v1、服务层改造、绞杀者)
  3. 新增分层依赖规则与分层校验项
  4. 新增分阶段验收标准、风险登记、执行纪律