- core/redis_client.py 移除 scheduler 耦合,lifespan 由 core/app.py 组合(Redis→调度器→Redis) - main.py 改从 core.app 导入 create_app - 删除 apps/、extensions/ 目录;utils 删除已迁移垫片与死代码 formatter.py - 新增 deploy/(nginx、supervisor)、scripts/(init_db、seed)、logs/(结构化日志落盘) - core/app.py 接入 core.logger setup_logging - 更新 CLAUDE.md 反映 core/shared/modules 新结构 Co-Authored-By: Claude <noreply@anthropic.com>
9.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
DjangoAdmin FastAPI+EleVue is a backend admin framework built with FastAPI (Python), Vue2.x + ElementUI (frontend, separate repo), and MySQL. It provides RBAC-based user/role/menu management, dictionary/config management, and CMS-like modules (notices, ads, members, etc.). The UI language and code comments are in Chinese.
Running the Application
# Install dependencies
pip install -r requirements.txt
# Run dev server (default: 127.0.0.1:8031)
python main.py
# Run via uvicorn directly
uvicorn main:app --host 127.0.0.1 --port 8031
# Docker
docker-compose up --build
Requires MySQL and Redis running. Configure connection details in .env (see .env for all variables). The database table prefix is fastapi_ (set via DB_PREFIX).
No test suite, linter, or formatter configuration exists in this project.
Architecture
Request Flow
main.py -> apps.create_app() -> registers CORS, Redis, routers, middleware, exception handlers -> all routes go through login_required dependency (except /login and /captcha) -> permission_required decorator checks node-level RBAC per endpoint -> check_demo decorator blocks write operations in demo mode.
Layered Module Pattern
Each business module (e.g., level, position, dept, role, user, menu, notice, dicts, config, etc.) follows the same 4-file pattern under apps/:
views/{module}.py- FastAPI router with endpoint definitions. Uses@permission_required("sys:{module}:{action}")and@check_demodecorators. ReturnsR.ok()/R.failed(). Endpoints that accept a body declare a Pydantic form parameter (e.g.,data: PositionForm) — FastAPI parses and validates the JSON automatically before the handler runs.services/{module}.py- Business logic: queries, CRUD operations, Excel import/export. Usesdb.query(Model)directly with SQLAlchemy. Reads query params viarequest.query_params; body params arrive as already-validated Pydantic models, so services access fields viadata.fieldordata.model_dump()(noawait request.json()in service bodies).forms/{module}.py- PydanticBaseModelvalidation classes. Fields useField(...)for constraints (min_length,max_length,ge,le,gt). Custom validators use@field_validator('field')raisingValueError(msg). Uniqueness checks querydb.query(Model).filter(...).first()inside validators.models/{module}.py- SQLAlchemy ORM model inheritingbase_model(provides id, create_user, create_time, update_user, update_time, is_delete) andbase_db(provides save(), save_all(), delete()). Table name usesDB_PREFIXprefix.
Request Validation
Pydantic v2 is used for all request body validation. The pattern for an endpoint:
@router.post('/add')
@permission_required("sys:level:add")
@check_demo
async def add(request: Request, data: LevelForm):
return await level.LevelAdd(request, data)
Validation errors are caught by a global RequestValidationError handler registered in core/app.py and converted to the standard response shape {"code": -1, "data": null, "msg": "field: message/..."} with HTTP 200. Services receive the validated Pydantic instance and use data.model_dump() (full dict), data.model_dump(exclude={'id'}) (for updates), or direct attribute access (data.id, data.status).
Key Directories
config/- Configuration split into sub-modules:app.py(app settings, CORS, upload limits),database.py(DB connection, pool config,SQLALCHEMY_MYSQL_URL),redis.py,auth.py(JWT),captcha.py,email.py,sms.py. All loaded from.envviapython-dotenv.config/__init__.pyloads.envfirst, then re-exports everything.core/- Core infrastructure:database.py(engine/SessionLocal/_db_ctx/get_db_session()/commit_independently/Base),redis_client.py(get_redis()/redis_lifespan/redis_middleware),jwt.py(JWT create/parse/refresh/blacklist with HS256),password.py(bcrypt hashing with salt),response.py(R.ok()/R.failed()),exceptions.py(AuthorizationException/BusinessException),security.py(get_user_id/get_username),middleware.py(login_required/permission_required/check_demo/db_session_middleware/operation_log_middleware/register_cros),deps.py(DI),logger.py(structured logging),app.py(create_app factory + lifespan).shared/- Cross-module shared layer:enums.py(enum dicts),constants.py(PAGE_SIZE/DEFAULT_PASSWORD/SERIALIZE_EXCLUDE_FIELDS),pagination.py,base_model.py,base_db.py,base_repository.py(Generic repository base),base_service.py.modules/- Business modules, each a directory withrouter.py(may export multiple routers, e.g.dict_router+dict_item_router),schemas.py,service.py,repository.py,models.py. Module groups:auth/,system/(user, role, menu, dept, position, level),dictionary/,configuration/,cms/(item, item_cate, ad, ad_sort),member/,region/,link/,notice/,job/(job + job_log + scheduler + job_executor),log/,upload/,index/.routers/-v1.pycentral route registration (imports routers frommodules/),__init__.pymounts login (免认证) on app and v1 behindlogin_required.utils/- Remaining utilities that stay here:request(parse_batch_ids,like_escape,like_contains),captcha,file,ip/ip2region,ua,rich_text,perm_cache.R/passwordare re-exported fromcoreviautils/__init__.pyfor back-compat.
Database Access
Database sessions are request-scoped via contextvars + Starlette middleware (core/middleware.py: db_session_middleware). Services call db = get_db_session() (from core.database) to get the current request's session. Models use self.save() / self.delete() from shared/base_db.py. The middleware auto-closes the session after each request (with rollback). Soft deletes via is_delete flag (0=active, 1=deleted).
Redis Access
Redis uses async client via core/redis_client.py. Connection pool created in redis_lifespan (composed with scheduler startup in core/app.py lifespan), injected per-request via redis_middleware into contextvars. Business code calls get_redis() to get the current request's async Redis client.
Authentication & Authorization
- JWT tokens via
core/jwt.py(HS256, configurable expiry via JWT_EXPIRE_MINUTES, secret via JWT_SALT env var). Token sent asAuthorization: Bearer <token>header. Supports token refresh and blacklist (logout) via Redis. login_requiredis a FastAPIDependsapplied globally to the v1 router. On success, it setsrequest.state.user_id,request.state.username,request.state.realname.permission_requiredis a per-endpoint decorator checking permission strings like"sys:level:list". User ID 1 (admin) bypasses all permission checks. It lazily queriesmodules.system.menu.service.get_permissions_list_sync.check_demois a per-endpoint decorator that returnsR.failed("演示环境,暂无操作权限")whenFASTAPI_DEMO=True. Apply it after@permission_requiredon all write endpoints (add, update, delete, status, import, export).
API Response Format
All endpoints return JSONResponse via R.ok() or R.failed():
{"code": 0, "data": ..., "msg": "操作成功", "ok": true} // success
{"code": -1, "data": null, "msg": "错误信息", "ok": false} // failure
R.ok() supports **kwargs for extra fields (e.g., R.ok(data=result, count=total) adds a count field).
Pagination Pattern
List endpoints read pageNo and pageSize from request.query_params, defaulting to page 1 and PAGE_SIZE (10). Services use SQLAlchemy .limit(limit).offset((page - 1) * limit).all() and return R.ok(data=result, count=count).
Delete Pattern
Single delete: DELETE /delete/{id} where id can be comma-separated (e.g., "1,2,3"). Batch delete: DELETE /batchDelete with JSON body [1, 2, 3], parsed via parse_batch_ids(request). Both use soft delete (set is_delete = 1).
Adding a New Module
- Create
modules/{group}/{name}/models.py- model class inheritingbase_modelandbase_db(fromshared/), set__tablename__ = DB_PREFIX + "{name}" - Create
modules/{group}/{name}/schemas.py- PydanticBaseModelvalidation classes withField(...)constraints and@field_validatorfor custom checks - Create
modules/{group}/{name}/repository.py- subclassshared.base_repository.BaseRepository+ module-level instance - Create
modules/{group}/{name}/service.py- CRUD service functions following theget_level_page/get_level_detail/add_level/update_level/delete_level/update_level_statuspattern - Create
modules/{group}/{name}/router.py- APIRouter with endpoints, apply@permission_requiredand@check_demoon write endpoints - Register the router in
routers/v1.py-v1.include_router({name}_router, prefix="/{prefix}", tags=["标签"])
Key Dependencies
- FastAPI 0.140.0, Uvicorn 0.51.0
- SQLAlchemy 2.0.51 (declarative style with
declarative_base(), not 2.0mapped_columnstyle) - PyMySQL 1.2.0, Redis 8.0.1 (async), PyJWT 2.13.0
- Pydantic 2.13.4 (for request validation via BaseModel + Field + field_validator), openpyxl (for Excel import/export)
- python-dotenv for
.envloading, bcrypt for password hashing, Pillow for captcha generation