refactor(api): TOML 配置 SSOT、统一错误契约、Auth/事务加固与可观测性 (#33)
配置 SSOT(TOML + .env) 统一错误契约 Auth 与事务边界 Redis / Celery 可靠性:业务 Redis(DB/0)与 Celery broker/backend(DB/1)显式拆分;连接池、sync client 可观测性(OpenTelemetry + LGTM)
This commit is contained in:
@@ -11,11 +11,39 @@ alwaysApply: true
|
||||
4. **feature 间禁止 import router**:跨 feature 调用通过 service 注入
|
||||
5. **所有 schema 变更走 Alembic**:禁止直接 DDL
|
||||
6. **新增 provider 必须实现 port protocol**:不得在 feature 中直接调用 SDK
|
||||
7. **事务边界:repo 不提交,service 管事务**:`repo.py` 只做 `add/delete/query`,`commit/rollback` 由 service 或 UoW 统一执行。`get_async_db()` 不自动 commit
|
||||
7. **事务边界:repo 不提交,service 管事务**:`repo.py` 只做 `add/delete/query`,`commit/rollback` 由 service 或 Celery task 统一执行。`get_async_db()` 不自动 commit;禁止在 router/repo 中 `await db.commit()` / `session.commit()`
|
||||
8. **port 边界不可打穿**:service 需要厂商增强能力时,必须扩充 port 或定义第二个窄 port,禁止直接引用 adapter 扩展方法
|
||||
9. **quota 是独立 feature**:conversation、memoir、payment 如需配额检查,通过注入 `QuotaService`,不得直接 import quota 内部函数
|
||||
10. **成功响应不强制包装**:`core/errors.py` 只统一错误响应格式 `{error_code, message, request_id}`,成功响应直接返回 Pydantic model / FileResponse / 原始结构
|
||||
|
||||
## 配置 SSOT(TOML + .env)
|
||||
- **Secrets / bootstrap 仅进 `.env`**:`DATABASE_URL`、`SECRET_KEY`、各厂商 API key、支付私钥等 → `app/core/config.py` 的 `Settings`
|
||||
- **产品 / 部署调参仅进 TOML**:`api/config/default.toml` + `api/config/{APP_ENV}.toml`(`development` / `staging` / `production`)
|
||||
- **禁止**在 `Settings` 新增 `chat_*` / `memoir_*` / `memory_*` 等产品字段;`tests/test_settings_allowlist.py` 会拦截 env 反弹
|
||||
- **禁止**业务代码散落 `os.getenv()`;读配置走既有入口:
|
||||
- feature 常量:`from app.features.<feature>.constants import chat`(等)
|
||||
- 运行时默认:`from app.core.runtime_constants import llm_defaults, tts_defaults, ...`
|
||||
- deploy 开关:`settings.enable_tts` 等(`SettingsFacade` 代理到 TOML `[deploy]`)
|
||||
- 改默认值时 **`default.toml` 与 `app_config_models.py` 必须同步**;`tests/test_default_toml_legacy_parity.py` 锁定关键默认行为
|
||||
- 字段对照与运维说明见 `api/docs/configuration.md`
|
||||
|
||||
## 错误处理
|
||||
- router / service **抛 `AppError` 子类**(`BadRequestError`、`AuthenticationError`、`NotFoundError`、feature 内 `AuthError` 等),**禁止** `HTTPException`
|
||||
- 业务专用 `error_code` 登记在 `app/core/error_codes.py`,OpenAPI 通过 `ErrorResponse` 组件文档化
|
||||
- 429 语义:`QuotaExceededError` vs `RateLimitedError`;勿用裸 `HTTPException(429)`(legacy handler 无法区分 quota)
|
||||
- 客户端(`app-expo` / `app-eval-web`)统一用 `parseApiError` 读 `message` / `error_code`(兼容旧 `detail`)
|
||||
|
||||
## 事务 helpers(补充规则 7)
|
||||
- 多步写、需原子提交 → `transactional()` / `transactional_sync()`(`app/core/db.py`)
|
||||
- 外层事务 active 时、局部失败可独立回滚 → `transactional_nested()` / `transactional_nested_sync()`(savepoint)
|
||||
- **同一 session 上连续两次 `transactional()` = 两次独立 commit**(WS 分段持久化等刻意为之);不要假设嵌套合并成一个事务
|
||||
- Celery sync 路径用 `transactional_sync()` + `get_sync_db()`;外部副作用(SMS、COS、LLM)放在 commit 成功之后
|
||||
|
||||
## Redis / Celery
|
||||
- 业务 key:`settings.redis_url_resolved`(通常 DB/0)
|
||||
- Celery broker/backend:`settings.celery_redis_url_resolved`(通常 DB/1;compose 显式 `CELERY_REDIS_URL`)
|
||||
- `REDIS_URL` 使用 DB/15 时必须显式设置 `CELERY_REDIS_URL`(无法 auto +1)
|
||||
|
||||
## 依赖管理(uv)
|
||||
11. **依赖统一用 uv 管理**:禁止直接 `pip install`、禁止手动编辑 `pyproject.toml` 的 `[project.dependencies]` 或 `[dependency-groups]`
|
||||
12. **新增依赖用 `uv add <pkg>`**,dev 依赖用 `uv add --dev <pkg>`,移除用 `uv remove <pkg>`
|
||||
@@ -23,4 +51,4 @@ alwaysApply: true
|
||||
14. **安装环境统一用 `uv sync`**:开发环境 `uv sync --dev`,生产环境 `uv sync --no-dev`
|
||||
15. **运行命令统一用 `uv run`**:如 `uv run pytest`、`uv run alembic upgrade head`、`uv run uvicorn ...`
|
||||
16. 每次添加新代码时,一定要阅读已有部分代码,确保符合项目架构,pattern,loguru模式
|
||||
17. **日志**:业务代码使用 `app.core.logging.get_logger(__name__)`(loguru `bind`);禁止在 `app` 包内用 `import logging` 取业务 logger;仅第三方 SDK / 适配器层可对标准库 `logging` 做桥接(如 `InterceptHandler`)
|
||||
17. **日志**:业务代码使用 `app.core.logging.get_logger(__name__)`(loguru `bind`);禁止在 `app` 包内用 `import logging` 取业务 logger;仅第三方 SDK / 适配器层可对标准库 `logging` 做桥接(如 `InterceptHandler`)
|
||||
|
||||
21
.cursor/rules/app-api-client.mdc
Normal file
21
.cursor/rules/app-api-client.mdc
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
description: Mobile and eval-web API client error handling and auth refresh
|
||||
globs: app-expo/src/**/*.{ts,tsx},app-eval-web/src/**/*.{ts,tsx}
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# API Client(app-expo / app-eval-web)
|
||||
|
||||
后端错误体为 `{ error_code, message, request_id }`(旧版可能仍有 `{ detail }`)。
|
||||
|
||||
## 错误解析
|
||||
- **统一使用 `parseApiError`**(`app-expo/src/core/api/parseApiError.ts`、`app-eval-web/src/parseApiError.ts`)
|
||||
- 展示给用户 / toast 用返回的 `message`;分支逻辑用 `errorCode`(对应后端 `error_code`)
|
||||
- **禁止**在新代码里直接读 `body.detail` 或假设 FastAPI 默认错误形状
|
||||
|
||||
## Token 刷新
|
||||
- `app-expo` 并发 401 refresh 必须走 `refresh-lock.ts`,避免双端同时 refresh 触发 `REFRESH_TOKEN_REUSE`
|
||||
- refresh 失败应清 session 并引导重新登录,不要无限重试同一 refresh token
|
||||
|
||||
## 类型
|
||||
- 共享错误 body 形状见各 app 的 `ApiErrorBody` / `types.ts`;与 OpenAPI `ErrorResponse` 对齐
|
||||
@@ -19,10 +19,23 @@ alwaysApply: false
|
||||
- 用 `httpx.AsyncClient` + `ASGITransport` 进行异步 HTTP 测试。
|
||||
- `api/tests/conftest.py` 和 `api/tests/factories.py` 只提供通用基础设施,不要把具体业务测试硬编码进去。
|
||||
|
||||
## HTTP 错误契约
|
||||
|
||||
- 失败响应断言 **`error_code` + `message`**(及可选 `request_id`),不要断言 FastAPI 旧格式 `{ detail }`。
|
||||
- OpenAPI / router 级 smoke:参考 `tests/test_http_router_error_contract.py`、`tests/test_openapi_error_response.py`。
|
||||
- Auth / payment / SMS 等 feature 码:断言具体 `error_code`(如 `INVALID_SMS_CODE`、`REFRESH_TOKEN_REUSE`),不只测 status code。
|
||||
|
||||
## 配置 / TOML 测试
|
||||
|
||||
- TOML 加载测试用 `CONFIG_DIR` 指向 fixture 目录,或 `reload_app_config()`;见 `tests/test_app_config_loader.py`。
|
||||
- **不要**用 `os.environ["OTEL_ENABLED"]` 等已迁出 Settings 的 env 键驱动应用行为(运行时已读 TOML)。
|
||||
- 新增 TOML 默认值时,若影响产品行为,更新 `tests/test_default_toml_legacy_parity.py` 或明确 overlay 意图。
|
||||
|
||||
## Test Layering
|
||||
|
||||
- HTTP 场景测试:注册、登录、刷新、登出、受保护资源访问、关键资源 CRUD、重要失败分支。
|
||||
- 纯单元测试:纯函数、规则计算、序列化、适配器错误分支与格式转换。
|
||||
- Service / 事务边界测试:仅当保护明确业务承诺(如 refresh rotation、SMS 回滚、幂等 ingest)且 HTTP 层难以稳定复现时允许;参考 `tests/test_auth_refresh_rotation.py`、`tests/test_db_transactional.py`。
|
||||
- 手工 / E2E:WebSocket 多轮对话、真实短信、Celery + Redis + LLM 编排、支付/对象存储/ASR/图像生成联调。
|
||||
|
||||
## Decision Filter
|
||||
|
||||
Reference in New Issue
Block a user