---
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`。
