153 lines
12 KiB
Markdown
153 lines
12 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
|
||
> **重构状态**:项目已按《重构方案-v2.md》完成 P1-P7 全部阶段。
|
||
> 后端源码位于 `src/`(core/common/modules/routes),API 统一 `/api/v1` 前缀,
|
||
> 服务层为「入参数据/出参数据 + BizError」范式,全项目 flake8 零告警。
|
||
|
||
## Project Overview
|
||
|
||
DjangoAdmin Flask+EleVue is a separated frontend-backend admin framework. Backend: Flask + SQLAlchemy + MySQL + Redis. Frontend: Vue 3 + Element Plus + TypeScript + Vite + Pinia. The project is primarily documented and commented in Chinese.
|
||
|
||
## Development Commands
|
||
|
||
### Backend (Python/Flask)
|
||
|
||
```bash
|
||
# Install dependencies
|
||
pip install -r requirements.txt
|
||
|
||
# Run dev server (default: 127.0.0.1:8021)
|
||
python app.py
|
||
|
||
# Database migrations (Flask-Migrate/Alembic)
|
||
flask db init # Initialize migration repository (already done)
|
||
flask db migrate # Generate migration from model changes
|
||
flask db upgrade # Apply migrations
|
||
flask db downgrade # Rollback migration
|
||
```
|
||
|
||
### Frontend (Vue 3 / ui/)
|
||
|
||
```bash
|
||
cd ui
|
||
|
||
# Install dependencies (uses pnpm)
|
||
pnpm install
|
||
|
||
# Run dev server (default: localhost:8001, proxies /api to backend :8021)
|
||
pnpm run dev
|
||
|
||
# Build for production
|
||
pnpm run build:prod
|
||
|
||
# Lint
|
||
pnpm run lint:eslint
|
||
pnpm run lint:prettier
|
||
pnpm run lint:stylelint
|
||
|
||
# Type check
|
||
pnpm run type:check
|
||
```
|
||
|
||
### Docker
|
||
|
||
```bash
|
||
cd docker
|
||
docker-compose up -d
|
||
```
|
||
|
||
## Architecture
|
||
|
||
### Backend Structure (Three-Layer Pattern)
|
||
|
||
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。标准 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/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`)
|
||
- `teardown_request`: rollback on unhandled exceptions
|
||
|
||
`BaseDB.save()` and `BaseDB.save_all()` call `db.session.flush()` only — the AOP interceptor handles commit. For special cases, use `@transactional` decorator, `@transactional_response` decorator, or `TransactionContext` context manager.
|
||
|
||
### Key Backend Modules
|
||
|
||
- **`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/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 序列化)。
|
||
- **`src/common/constants/`** — 共享常量:`PAGE_SIZE=10`, `DEFAULT_PASSWORD="123456"`, `MESSAGE_OK/FAIL`, `SERIALIZE_EXCLUDE_FIELDS`.
|
||
- **`config/`** — Split into `base.py` (Flask host/port/debug/paths), `database.py` (SQLAlchemy URI, SSL options), `redis.py` (Redis URL), `auth.py` (JWT salt, permission toggle, login whitelist, internal API key). All read from `.env` via python-dotenv. `config/__init__.py` loads `.env` before any sub-module imports.
|
||
- **`src/extensions.py`** — Flask extension registration orchestration (`register_extends(app)`). 实例定义在 `src/core/`(database/redis_client/mail/scheduler)。
|
||
- **`src/core/`** — `response`(R 统一响应)、`jwt`、`password`(bcrypt)、`security`(get_user_id/get_username)、`redis_client`。
|
||
- **`src/common/utils/`** — `model_utils`(models_to_list/model_to_dict)、`request`(parse_request_body/parse_id_list)、`case_converter`(camelCase <-> snake_case)、`regular`(WTForms 错误提取)、`captcha`、`file`、`rich_text`、`ip`、`user_agent`、`formatter`、`json`、`ip2region`.
|
||
|
||
### Frontend Structure (ui/src/)
|
||
|
||
- **`api/`** — Axios API calls organized by module. Each function uses `http.request()` from `utils/http/axios/`. The HTTP client auto-prepends `VITE_GLOB_API_URL_PREFIX` (`/api`) and sends JWT as `Authorization: Bearer <token>`.
|
||
- **`views/`** — Page components matching backend modules (system, content, monitor, etc.).
|
||
- **`router/`** — Vue Router with dynamic route generation from backend menu data. `generator-routers.ts` transforms backend menu tree into Vue Router routes; `asyncImportRoute` uses `import.meta.glob('../views/**/*.{vue,tsx}')` for lazy loading.
|
||
- **`store/`** — Pinia stores: `user` (auth state), `asyncRoute` (dynamic routes), `projectSetting`, `tabsView`, `lockscreen`, `ossConfig`.
|
||
- **`components/`** — Reusable UI components.
|
||
- **`layout/`** — Main layout shell (sidebar, header, content area).
|
||
- **`hooks/`** — Composable functions.
|
||
- **`utils/`** — Frontend utilities (HTTP client with interceptors, date formatting, etc.).
|
||
|
||
### API Conventions
|
||
|
||
- 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/"]]`)
|
||
|
||
### Naming Conventions
|
||
|
||
- Backend service functions: snake_case (e.g., `get_user_page`, `add_user`)
|
||
- Backend model tables: `flask_` prefix + snake_case (e.g., `flask_user`, `flask_role`)
|
||
- Service layer uses camelCase for parameters/return keys (matching frontend), snake_case for DB fields
|
||
- Private service functions prefixed with `_` (e.g., `_apply_filters`, `_build_page_data`)
|
||
- `BaseModel.__init__` auto-converts camelCase kwargs to snake_case and filters to valid model columns only
|
||
- `to_dict()` returns snake_case keys; services use `convert_snake_to_camel` when frontend needs camelCase
|
||
|
||
### 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):
|
||
- 标准 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(唯一性校验用 `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)
|
||
|
||
## Environment Configuration
|
||
|
||
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_*` — 数据库连接,**多库支持**(`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
|
||
- `PERMISSION_ENABLED` — Toggle auth middleware
|
||
- `INTERNAL_API_KEY` — Key for scheduler/internal calls (sent as `X-Internal-API-Key` or `X-Scheduler-Key` header)
|
||
- `LOGIN_WHITELIST_*` — Paths exempt from login check (exact, prefix, wildcard patterns)
|
||
- `PROXY_NUM` — Trusted proxy count for ProxyFix (0=none, 1=Nginx)
|
||
|
||
Frontend env in `ui/.env.development`: `VITE_PORT=8001`, `VITE_PROXY`, `VITE_GLOB_API_URL_PREFIX=/api`
|