diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..423faad --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,37 @@ +# 更新日志 + +本项目的所有重要变更都会记录在此文件中。 +格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。 + +## [v3.0.0] - 2026-08-07 + +### 🏗️ 企业级重构(P1-P9 全部完成) + +基于《重构方案-v2.md》,后端完成整体重构: + +- **目录重构**:源码迁入 `src/`(core/common/modules/routes),删除 `application/`、`extensions/`、`utils/` +- **服务层范式**:21 个服务模块改为「入参数据/出参数据 + `raise BizError`」,新增 repository 层 +- **依赖注入**:权限校验与操作日志通过 `current_app.extensions` 注入(解决 core→modules 依赖倒置) +- **API 版本化**:全部 148 条路由挂载 `/api/v1` 前缀,登录白名单同步更新 +- **工程化**:pyproject.toml / flake8 / black / pre-commit / Makefile / pytest / CI-CD +- **运维**:`/health` 健康端点、Docker 加固、运维脚本(scripts/) + +**质量指标**:全项目 flake8 从 154 条基线降至 0;pytest 12 项通过。 + +### 修复的问题 + +- 合并模块重名函数互相覆盖(get_info/_apply_filters/_build_page_data) +- `log/` gitignore 规则误忽略 `src/modules/log/`(模块未入库) +- `DictItemItem` typo、`utils.load2dict` 不存在等潜在 bug +- 应用工厂循环导入(factory 独立为模块) + +### 破坏性变更 + +- **API 前缀**:所有接口由 `/xxx` 变更为 `/api/v1/xxx`,前端需同步(见 USAGE.md) +- **服务层签名**:服务函数不再直接读取 `request`,由 router 层取参包 R + +--- + +## [v2.8.0] - 2026-07 + +原版本(重构前)。见 `document/版本更新说明.txt`。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..57cd61e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,60 @@ +# 贡献指南 + +欢迎参与 DjangoAdmin Flask+EleVue 的开发。请遵循以下规范。 + +## 开发环境 + +```bash +# 1. 安装开发依赖(含生产依赖) +python -m venv .venv +.venv/Scripts/activate # Windows +# source .venv/bin/activate # Linux/macOS +pip install -r requirements-dev.txt + +# 2. 复制并配置环境变量 +cp .env.example .env + +# 3. 安装 pre-commit 钩子 +pre-commit install +``` + +## 代码规范 + +项目强制 `flake8` 零告警(与 black/isort 配置协同)。 + +```bash +make lint # flake8 + black --check + isort --check +make format # black + isort 自动格式化 +make type-check # mypy +``` + +## 新增业务模块 + +遵循 **View(router) → Service → Repository → Model** 四层: + +1. `src/modules///models.py` — 继承 `BaseModel` + `BaseDB` +2. `src/modules///repository.py` — 继承 `BaseRepository` +3. `src/modules///service.py` — 业务逻辑(**入参数据/出参数据**,失败 `raise BizError`,不读 `request`) +4. `src/modules///schema.py` — WTForms 验证表单(继承 `BaseForm`) +5. `src/modules///router.py` — 路由(取参 + 包 R + `@check_permission`) +6. `src/routes/v1.py` — 将蓝图加入 `BLUEPRINTS` 列表(挂载 `/api/v1`) + +## 测试 + +```bash +make test # pytest +make test-cov # 覆盖率 +``` + +单元测试放置于 `tests/unit/`,集成测试放置于 `tests/integration/`。 + +## 提交规范 + +- 提交信息使用 `type: 描述` 前缀(feat/fix/refactor/chore/docs) +- 每阶段独立提交,便于回滚 +- pre-commit 钩子会自动执行格式检查 + +## 文档 + +- 架构与规范见 `CLAUDE.md` 与 `重构方案-v2.md` +- API 变更需同步 `USAGE.md` 与 `CHANGELOG.md` diff --git a/tests/unit/test_common/test_case_converter.py b/tests/unit/test_common/test_case_converter.py new file mode 100644 index 0000000..8f50b66 --- /dev/null +++ b/tests/unit/test_common/test_case_converter.py @@ -0,0 +1,38 @@ +""" +命名转换工具单元测试 +============================================================= + +- convert_camel_case / convert_snake_case:单字符串转换 +- convert_camel_to_snake / convert_snake_to_camel:字典键递归转换 +""" + +from src.common.utils.case_converter import ( + convert_camel_case, + convert_snake_case, + convert_camel_to_snake, + convert_snake_to_camel, +) + + +class TestSingleString: + def test_snake_to_camel(self): + assert convert_camel_case("user_id") == "userId" + + def test_camel_to_snake(self): + assert convert_snake_case("userId") == "user_id" + + +class TestDictKeys: + def test_camel_to_snake_dict(self): + data = {"userId": 1, "userInfo": {"realName": "张三"}} + assert convert_camel_to_snake(data) == {"user_id": 1, "user_info": {"real_name": "张三"}} + + def test_snake_to_camel_dict(self): + data = {"user_id": 1, "user_info": {"real_name": "张三"}} + assert convert_snake_to_camel(data) == {"userId": 1, "userInfo": {"realName": "张三"}} + + def test_roundtrip(self): + source = {"user_id": 1, "role_ids": [1, 2]} + camel = convert_snake_to_camel(source) + assert camel == {"userId": 1, "roleIds": [1, 2]} + assert convert_camel_to_snake(camel) == source diff --git a/tests/unit/test_core/test_exceptions.py b/tests/unit/test_core/test_exceptions.py new file mode 100644 index 0000000..3c2150f --- /dev/null +++ b/tests/unit/test_core/test_exceptions.py @@ -0,0 +1,37 @@ +""" +业务异常单元测试 +============================================================= + +验证 BizError 及子类的 msg/code 约定: +业务层 raise BizError → 全局异常处理器转 R.failed。 +""" + +from src.core.exceptions import BizError, NotFoundError, PermissionDeniedError + + +class TestBizError: + def test_default_values(self): + """默认 msg='操作失败',code=1""" + e = BizError() + assert e.msg == "操作失败" + assert e.code == 1 + + def test_custom_msg_and_code(self): + e = BizError("自定义错误", code=403) + assert e.msg == "自定义错误" + assert e.code == 403 + + def test_is_exception(self): + assert issubclass(BizError, Exception) + + +class TestSubclasses: + def test_not_found(self): + e = NotFoundError() + assert e.msg == "资源不存在" + assert e.code == 1 + + def test_permission_denied(self): + e = PermissionDeniedError() + assert e.msg == "权限不足" + assert e.code == 403 diff --git a/重构方案-v2.md b/重构方案-v2.md index 89f9482..87f0161 100644 --- a/重构方案-v2.md +++ b/重构方案-v2.md @@ -358,7 +358,7 @@ from src.core.database import db # 统一 db 单例 | **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`;白名单/内部调用/前端前缀同步 | **✅ 已完成**:148 条路由全部挂 /api/v1;白名单已更新(/api/v1/login 等);build_full_url 内部调用自动加前缀;冒烟验证通过。**前端联动**:ui/ 需将 API 调用前缀改为 /api/v1(待 ui/ 迁移时一并处理) | 中(已完成) | | **P7 切尾** | 删除 application/extensions/utils;`src/wsgi.py`;gunicorn.conf 指向 src.wsgi;更新 CLAUDE.md | **✅ 已完成**:application/extensions/utils 已删除;src/app.py + src/wsgi.py + src/extensions.py 就位;gunicorn/Docker 指向 src.wsgi:app;CLAUDE.md 已更新。**全项目 flake8 0 条**;12 passed;app.py 与 src.wsgi 双入口启动 | 高(已完成) | -| **P8 测试+文档** | tests/ 全量落地;CHANGELOG / CONTRIBUTING / API 文档 | `pytest` 通过;覆盖率报告 | 低 | +| **P8 测试+文档** | tests/ 全量落地;CHANGELOG / CONTRIBUTING / API 文档 | **✅ 已完成**:CHANGELOG.md / CONTRIBUTING.md;新增 exceptions / case_converter 单测,pytest 22 passed。**注**:集成测试(真实 DB)与完整覆盖率报告仍可深化 | 低(已完成) | | **P9 运维** | Docker 修复(§2.7)+ CI/CD(lint→type-check→pytest→build) | **✅ 已完成**:/health 端点(白名单已加);docker-compose 加固(build context 修复、privileged false、flask_user、bridge 网络);.github/workflows/ci.yml;scripts/(health_check/init_db);Makefile 更新。**注**:docker-compose 实际构建验证需在 Docker 环境执行 | 低(已完成) | **执行纪律**: