--- description: pythonbackend 项目总览、分层架构与运行方式 alwaysApply: true --- # pythonbackend 项目约定 aiclient 的 Python 后端,为桌面/前端提供 HTTP API。默认监听 `http://127.0.0.1:8001`(见根目录 `main.py`)。 ## 技术栈 | 组件 | 库 | 用途 | |------|-----|------| | Web | FastAPI + Uvicorn | 异步 HTTP、OpenAPI(`/docs`) | | 校验/配置 | Pydantic v2、`pydantic-settings` | 请求/响应模型、`.env` 配置 | | 数据库 | SQLAlchemy 2(async)+ **aiomysql** | MySQL 异步访问 | | 迁移 | Alembic | 表结构版本管理(异步 `alembic/env.py`) | | 缓存/会话 | **redis**(`redis.asyncio`) | JWT 会话存证、登出吊销 | | 安全 | passlib(bcrypt)、python-jose | 密码哈希、JWT 签发/校验 | ## 目录结构 ``` pythonbackend/ ├── main.py # 开发入口:uvicorn 启动 app.main:app ├── requirements.txt ├── .env / .env.example # 环境变量(不入库 .env) ├── alembic.ini ├── alembic/ │ ├── env.py # 从 app.config 读 database_url,异步迁移 │ └── versions/ # 迁移脚本 └── app/ ├── main.py # FastAPI 工厂、CORS、lifespan、路由挂载 ├── config.py # Settings(get_settings 单例) ├── database.py # AsyncEngine、Session、Base、get_db ├── redis_client.py # init/close/get_redis、redis_dependency ├── dependencies.py # FastAPI Depends(DbSession、CurrentUser 等) ├── core/ # 与框架无关的工具(security) ├── models/ # SQLAlchemy ORM 模型 ├── schemas/ # Pydantic 入参/出参、ApiResponse ├── services/ # 业务逻辑(不依赖 Request/Response) └── api/v1/ # 路由层:薄控制器,组装 ApiResponse ``` ## 分层架构(自上而下) ``` HTTP 请求 → api/v1/*.py 路由:解析 body、注入 Depends、捕获业务异常 → ApiResponse → services/*.py 业务:查询/写入 DB、抛 AuthError 等,返回 schema 或模型 → models/*.py ORM:表定义,仅在 services / dependencies 使用 → database.py 连接池与 get_db(请求结束 commit,异常 rollback) 横切: → schemas/ 请求体验证、响应类型;对外 JSON 字段 snake_case(注册 confirmPassword 例外见 auth schema) → core/security.py 密码哈希、JWT、Redis session key 前缀 → dependencies.py 鉴权:Redis session 优先,否则回退 JWT sub → redis_client.py lifespan 内 init_redis,路由经 redis_dependency 注入 ``` **原则** - `api/` 不写 SQL、不直接操作 Redis 细节;调用 `services/`。 - `services/` 不 import FastAPI 的 `Request`/`HTTPException`(业务错误用自定义异常,如 `AuthError`)。 - 新增表:先 `models/` → Alembic 迁移 → 再 `schemas` + `services` + `api`。 ## 配置(app/config.py) - 从项目根 `.env` 加载,字段见 `.env.example`。 - `database_url`:`mysql+aiomysql://...` - `redis_url`、`secret_key`、`access_token_expire_minutes`、`cors_origins`(逗号分隔或 `*`)。 - 通过 `get_settings()` 获取,勿在模块顶层反复实例化 Settings。 ## 数据库与会话 - `get_db()`:每个请求一个 `AsyncSession`,正常结束 `commit`,异常 `rollback`。 - 路由使用类型别名 `DbSession = Annotated[AsyncSession, Depends(get_db)]`。 - ORM 基类:`database.Base`;新模型须在 `alembic/env.py` 中 import 以纳入 `target_metadata`。 ## Redis 与会话 - 应用 `lifespan`:`init_redis()` → 运行 → `close_redis()`。 - 登录/注册成功:`services.auth.store_session(redis, token, user_id)`,key `session:{token}`,TTL 与 JWT 过期一致。 - 登出:`revoke_session` 删除 key;鉴权时 Redis 无记录则回退解析 JWT(已登出会话失效)。 ## 统一响应 ApiResponse 定义于 `app/schemas/common.py`: - `ok: bool` — 业务是否成功 - `message: str` — 用户可见提示(中文) - `data: T | None` — 成功载荷 业务失败(如用户名已存在)返回 **HTTP 200** + `ok: false`,勿抛未捕获异常。未认证访问受保护路由由 `dependencies.get_current_user` 抛 **401**(非 ApiResponse 信封)。 前端约定详见 aiclient 仓库 `.cursor/rules/aiclient-overview.mdc` 中「后端 API 约定」一节。 ## 认证模块(当前实现) | 层 | 文件 | |----|------| | 路由 | `app/api/v1/auth.py` | | 业务 | `app/services/auth.py` | | 模型 | `app/models/user.py` | | Schema | `app/schemas/auth.py` | | 挂载 | `app/api/v1/router.py` → `app/main.py` 前缀 `/api/v1` | 端点:`POST /auth/register|login|logout`,`GET /auth/me`;根路径 `GET /health`。 ## Alembic ```bash # 项目根目录,激活 venv 后 alembic upgrade head # 应用迁移 alembic revision -m "描述" # 新建迁移(需人工编写 upgrade/downgrade) ``` - `alembic/env.py` 使用异步引擎,URL 来自 `Settings.database_url`。 - 改表后必须提交 `alembic/versions/` 下的迁移文件。 ## 常用命令 ```bash alembic upgrade head python main.py # 或: uvicorn app.main:app --reload --port 8001 ``` ## 新增功能 checklist 1. `app/models/` 定义 ORM(如需持久化) 2. `alembic revision` + `upgrade head` 3. `app/schemas/` 请求/响应模型 4. `app/services/` 业务函数 5. `app/api/v1/` 路由,挂到 `router.py` 6. 需登录:`dependencies.CurrentUser`;需 Redis:参数 `RedisClient` 7. 对外 JSON 保持 `ApiResponse` 信封;错误文案简体中文 ## 通用原则 - Python 3.11+ 类型注解;异步路由与 async service 一致。 - 密码仅存 `password_hash`,禁止日志或响应中输出明文密码。 - 生产环境必须修改 `SECRET_KEY`,勿提交 `.env`。