149 lines
9.3 KiB
Markdown
149 lines
9.3 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。
|
||
- **`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.**
|
||
|
||
- `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/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/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
|
||
- 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)
|
||
3. Create repository in `src/modules/<domain>/<name>/repository.py` inheriting `BaseRepository`
|
||
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
|
||
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_*` — MySQL connection (database: `djangoadmin.flask.elevue`, prefix: `flask_`)
|
||
- `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`
|