签入版本

This commit is contained in:
2026-09-12 18:57:19 +08:00
parent d45b61d808
commit 2857e0f2da
295 changed files with 20713 additions and 13125 deletions
+11 -7
View File
@@ -65,13 +65,13 @@ docker-compose up -d
Each business module follows a strict **View -> Service -> Model** separation:
- **`src/modules/<domain>/<name>/router.py`** — Route controllers (Flask Blueprint). Registered under `/api/v1` prefix (see `src/routes/v1.py`). Each file defines routes decorated with `@check_permission("sys:module:action")`. Routers parse request params, delegate to service functions, and wrap with `R.ok()`/`R.failed()`. Write operations check `FLASK_DEMO` first (via `@check_demo`).
- **`src/modules/<domain>/<name>/service.py`** — Business logic layer. **服务函数「入参数据/出参数据」**(不直接读 `request`,不返回 R);业务失败 `raise BizError`;私有辅助函数以 `_` 开头。数据访问委托 repository。
- **`src/modules/<domain>/<name>/service.py`** — Business logic layer. **服务函数「入参数据/出参数据」**(不直接读 `request`,不返回 R);业务失败 `raise BizError`;私有辅助函数以 `_` 开头。数据访问委托 repository。标准 CRUD 模块继承 `BaseService`(`src/common/base_service.py`,含 page/detail/add/update/remove/update_status/list 模板方法 + before_*/after_*/_detail_dict/_enrich_page_records 覆写钩子),模块级代理函数暴露给 router 调用(router 零改动)。
- **`src/modules/<domain>/<name>/models.py`** — SQLAlchemy models. All models inherit from `BaseModel` + `BaseDB` (in `src/common/`). Table names use `DB_PREFIX` (default `flask_`) + snake_case class name.
- **`src/modules/<domain>/<name>/repository.py`** — 数据访问层,继承 `BaseRepository`(`src/common/base_repository.py`)。
### AOP Transaction Management
The framework uses an AOP-style transaction interceptor (`src/core/middleware.py`) registered via `register_transaction(app)`. **Business code never calls commit/rollback directly.**
The framework uses an AOP-style transaction interceptor (`src/middleware/transaction.py`) registered via `register_transaction(app)`. **Business code never calls commit/rollback directly.**
- `before_request`: marks transaction active
- `after_request`: auto-commits on POST/PUT/DELETE/PATCH only if HTTP 2xx AND `code==0` in response JSON; auto-rollbacks on business failures (`R.failed` returns HTTP 200 but `code=1`)
@@ -83,7 +83,7 @@ The framework uses an AOP-style transaction interceptor (`src/core/middleware.py
- **`src/app.py`** — App factory (`create_app()`). Initialization order: Flask core -> Redis config -> DB config -> JSON encoder -> CORS -> ProxyFix -> op_log/transaction -> register `/api/v1` routes -> init extensions -> middleware -> error handlers -> DI injection.
- **`src/routes/v1.py`** — API v1 路由注册中心。所有业务蓝图挂载到 `/api/v1` 前缀。
- **`src/core/middleware.py`** — `check_login` (before_request hook, JWT validation with token blacklist, whitelist matching), `check_demo`, `register_transaction` (AOP 事务), `register_operation_log` (操作日志,写入器 DI 注入), `register_error_handlers`.
- **`src/middleware/`** — 中间件包(按职责拆分:`auth.py` 登录校验 + 白名单、`demo.py` 演示模式、`transaction.py` AOP 事务、`operation_log.py` 操作日志 + `@operation_log` 标题装饰器、`errors.py` 异常处理)。对外统一 `from src.middleware import ...`。
- **`src/core/permission.py`** — `check_permission` (RBAC decorator; superadmin user_id=1 bypasses; 权限数据通过 `current_app.extensions['permission_provider']` 注入,见 `src/app.py` `_inject_dependencies`).
- **`src/core/exceptions.py`** — `BizError` 业务异常(业务层 `raise BizError` 统一转 `R.failed`)。
- **`src/common/forms/`** — WTForms 验证表单。继承 `BaseForm`(含 `ArrayField` JSON 序列化)。
@@ -109,6 +109,7 @@ The framework uses an AOP-style transaction interceptor (`src/core/middleware.py
- Backend routes follow RESTful patterns: `GET /{module}/page`, `GET /{module}/detail/<id>`, `POST /{module}/add`, `PUT /{module}/update`, `DELETE /{module}/delete/<id>`, `PUT /{module}/status`, `GET /{module}/list`
- All responses use `R.ok(data=...)` / `R.failed(msg=...)` format with `{code, data, msg, ok}` structure
- Frontend response handler checks `code === 0` for success; `code=401` triggers re-login
- **统一约定:所有响应(含认证/权限失败 401/403)返回 HTTP 200 + 业务 code**,不返回对应 HTTP 状态码——前端按 `code` 判断(401 重登、403 无权限)。若需按 HTTP 状态码防护(WAF/代理),应解析业务 code 而非 HTTP status
- Permission strings follow pattern: `sys:module:action` (e.g., `sys:user:page`, `sys:role:add`)
- Frontend proxies `/api` prefix to backend via Vite dev server proxy (configured in `ui/.env.development` as `VITE_PROXY=[["/api","http://127.0.0.1:8021/"]]`)
@@ -124,10 +125,12 @@ The framework uses an AOP-style transaction interceptor (`src/core/middleware.py
### Adding a New Module
1. Create model in `src/modules/<domain>/<name>/models.py` inheriting `BaseModel` + `BaseDB`
2. Create service in `src/modules/<domain>/<name>/service.py`(入参数据/出参数据 + raise BizError)
3. Create repository in `src/modules/<domain>/<name>/repository.py` inheriting `BaseRepository`
2. Create service in `src/modules/<domain>/<name>/service.py`(入参数据/出参数据 + raise BizError):
- 标准 CRUD 模块:`class XxxService(BaseService)` 指定 `repository/form/status_form/model_name`,差异逻辑覆写钩子,末尾暴露模块级代理函数(如 `get_xxx_page = XxxService.get_page`)供 router 调用
- 深度定制模块(如 user/auth):保持模块级函数 + raise BizError
3. Create repository in `src/modules/<domain>/<name>/repository.py` inheriting `BaseRepository`(标准模块需实现 `page(params)` 与 `list_enabled()`)
4. Create router in `src/modules/<domain>/<name>/router.py` with routes and permission decorators
5. Create schema in `src/modules/<domain>/<name>/schema.py` inheriting `BaseForm` if input validation needed
5. Create schema in `src/modules/<domain>/<name>/schema.py` inheriting `BaseForm` if input validation needed(唯一性校验用 `XxxRepository.exists_by(filters, exclude_id)`,禁止直接 `Model.query`——查询只允许在 Repository 层)
6. Add the blueprint to `BLUEPRINTS` list in `src/routes/v1.py`
7. Add frontend API in `ui/src/api/{module}/`, views in `ui/src/views/{module}/`(API 前缀 /api/v1)
@@ -136,7 +139,8 @@ The framework uses an AOP-style transaction interceptor (`src/core/middleware.py
All config is in `.env` at project root. Key settings:
- `FLASK_HOST` / `FLASK_PORT` (default 127.0.0.1:8021)
- `FLASK_SECRET_KEY` — must be >= 16 chars or app refuses to start
- `DB_*` — MySQL connection (database: `djangoadmin.flask.elevue`, prefix: `flask_`)
- `DB_*` — 数据库连接,**多库支持**(`config/database.py` 的 `_build_database_url()` 按 `DB_DRIVER` 构建 URL):`mysql`(默认)/`postgresql`/`mssql`/`sqlite`/`oracle`(oracledb 驱动,thin 模式无需 Oracle Client;`DB_DATABASE` 填 SERVICE_NAME)。切换只需改 `DB_DRIVER` + 账号,然后运行 `python scripts/setup_db.py`(`create_all` 建表 + 种子数据,幂等)。注意:`flask db migrate/upgrade`(方案 B)仍针对 MySQL,新建其它库请用 `scripts/setup_db.py`
- **跨库数据迁移**:`python scripts/migrate_db.py --src-driver=mysql --src-host=127.0.0.1 --src-port=3306 --src-db=<源库名> --src-user=root --src-pass=<密码> --dst-driver=postgresql --dst-host=127.0.0.1 --dst-port=5432 --dst-db=<目标库名> --dst-user=postgres --dst-pass=<密码> --drop-target-first`。CLI 显式参数、不依赖 `.env`;源/目标均支持 `mysql/postgresql/mssql/sqlite/oracle`(别名 `postgres=postgresql`、`sqlserver=mssql`、`sqlite3=sqlite`),自动建目标库 + `create_all` 建表 + 逐表泵数据 + 按方言重置自增(PG setval / MSSQL CHECKIDENT / Oracle 补 sequence+触发器)+ 逐表行数校验;先加 `--dry-run` 干跑核对计划
- `REDIS_*` — Redis connection
- `JWT_SALT` — JWT signing key; `JWT_EXPIRE_MINUTES` (default 20)
- `FLASK_DEMO` — Demo mode restricts write operations