# ADR 011: API 响应格式标准化 ## 状态 已接受 (Accepted) - 2026-02-05 ## 背景 在代码审查过程中,发现 API 响应格式存在不一致的问题: ### 发现的问题 1. **文档不一致** - `api-design.md` 第 90-96 行定义了完整的响应格式(5 个字段) - 但第 617-628 行及其他多处示例缺少 `success` 和 `timestamp` 字段 2. **Schema 定义不完整** - `schemas/common.py` 中的 `SuccessResponse` 只有 3 个字段(code, message, data) - 缺少 `success` 和 `timestamp` 字段 - 导致部分 API(如 `credits.py`)返回不完整的响应 3. **前端已兼容** - `client/src/services/api/client.ts` 已经支持完整格式 - 响应拦截器正确处理 5 字段格式(第 48 行) - 并兼容旧的 3 字段格式(第 66 行) ### 正确的响应格式 **成功响应**: ```json { "success": true, "code": 200, "message": "Success", "data": {...}, "timestamp": "2026-02-05T10:30:00+00:00" } ``` **错误响应**: ```json { "success": false, "code": 400, "message": "错误消息", "data": null, "timestamp": "2026-02-05T10:30:00+00:00" } ``` ## 决策 **统一所有 API 响应格式为 5 字段格式**: 1. `success` (boolean) - 请求是否成功 2. `code` (number) - 业务状态码 3. `message` (string) - 响应消息 4. `data` (T | null) - 业务数据 5. `timestamp` (string) - ISO 8601 格式的响应时间戳 ## 实施 ### 1. 修复文档 **已修复**:`.claude/skills/jointo-tech-stack/references/api-design.md` - ✅ 第 617-628 行:分页响应示例 - ✅ 第 352-366 行:邮箱登录响应 - ✅ 第 380-389 行:发送验证码响应 - ✅ 第 403-418 行:手机号登录响应 - ✅ 第 428-437 行:微信二维码响应 - ✅ 第 446-455 行:微信登录(等待扫码) - ✅ 第 458-472 行:微信登录(成功) - ✅ 第 490-497 行:未认证响应 - ✅ 第 499-506 行:权限不足响应 - ✅ 第 660-680 行:项目列表响应 - ✅ 第 700-710 行:创建项目响应 - ✅ 第 733-740 行:删除项目响应 - ✅ 第 788-799 行:积分余额响应 - ✅ 第 809-830 行:积分交易记录响应 - ✅ 第 849-861 行:上传附件响应 - ✅ 第 872-880 行:下载链接响应 - ✅ 第 906-917 行:创建充值订单响应 - ✅ 第 928-940 行:查询订单状态响应 ### 2. 修复 Schema 定义 **已修复**:`server/app/schemas/common.py` ```python class SuccessResponse(BaseModel, Generic[T]): """统一成功响应格式(完整版本)""" success: bool = Field(default=True, description="请求是否成功") code: int = Field(default=200, description="业务状态码") message: str = Field(default="Success", description="响应消息") data: Optional[T] = Field(default=None, description="响应数据") timestamp: datetime = Field( default_factory=lambda: datetime.now(timezone.utc), description="响应时间戳" ) @field_serializer('timestamp') def serialize_timestamp(self, value: datetime) -> str: return value.isoformat() ``` ### 3. API 路由现状 **已确认正确**: - ✅ `api/v1/projects.py` - 使用 `success_response()` ✅ - ✅ `api/v1/folders.py` - 使用 `success_response()` ✅ - ✅ `api/v1/auth.py` - 使用 `success_response()` ✅ - ✅ `api/v1/credits.py` - 使用 `SuccessResponse()` (已修复 Schema)✅ ### 4. 前端兼容性 **已确认**:`client/src/services/api/client.ts` 前端响应拦截器已正确处理: - ✅ 优先检查新格式(5 字段,第 48 行) - ✅ 兼容旧格式(3 字段,第 66 行) - ✅ 正确处理错误响应(第 96 行) ## 影响 ### 正面影响 1. **统一性**:所有 API 响应格式一致,前端处理更简单 2. **可追踪性**:`timestamp` 字段便于排查问题 3. **明确性**:`success` 字段明确表示请求成功/失败 4. **兼容性**:前端已兼容新旧格式,无破坏性变更 ### 需要注意 1. **新 API 开发**:必须使用完整的 5 字段格式 2. **旧 API 迁移**:逐步迁移使用不完整格式的旧接口 3. **测试用例**:更新测试用例验证响应格式 ## 实施指南 ### 后端开发 **✅ 推荐方式 1:使用便捷函数** ```python from app.schemas.response import success_response @router.get("/example") async def example(): return success_response(data={"key": "value"}) ``` **✅ 推荐方式 2:使用 Pydantic 模型(推荐用于有明确类型的场景)** ```python from app.schemas.common import SuccessResponse @router.get("/example", response_model=SuccessResponse[MyResponse]) async def example(): return SuccessResponse(data=my_data) ``` **❌ 禁止方式:手动构建不完整的字典** ```python # ❌ 错误:缺少 success 和 timestamp return {"code": 200, "message": "Success", "data": data} ``` ### 前端开发 前端无需修改,响应拦截器已正确处理: ```typescript // 自动提取 data 字段 const projects = await projectApi.getAll(); // 直接得到 data 内容 // 错误自动抛出异常 try { await api.call(); } catch (error) { console.error(error.message); // 来自 response.message } ``` ## 检查清单 创建新 API 时必须确认: - [ ] 使用 `success_response()` 或 `SuccessResponse` 返回 - [ ] `response_model` 使用 `ApiResponse[T]` 或 `SuccessResponse[T]` - [ ] 响应包含 5 个必需字段:success, code, message, data, timestamp - [ ] 错误响应也遵循相同格式 - [ ] 更新 API 文档示例 ## 相关文档 - `.claude/skills/jointo-tech-stack/references/api-design.md` - API 设计规范 - `server/app/schemas/response.py` - 响应格式定义 - `server/app/schemas/common.py` - 通用响应模型 - `client/src/services/api/client.ts` - 前端响应拦截器 ## 历史 - 2026-02-05: 创建 ADR,修复文档和 Schema 定义