Files
nexus/Project/fonrey/TECH_STACK/API_CONTRACT.md
2026-04-28 07:38:30 +08:00

288 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> **For AI assistants**: Read this entire file before designing or implementing any API. Contract rules here are mandatory. Do not invent per-module variants unless explicitly allowed.
# Fonrey API 契约规范API_CONTRACT
**版本**: 1.0
**适用范围**: 全模块account / permission / property / client / complex / org / setting
**关联总纲**: `TECH_STACK/TECH_STACK.md`
**最后更新**: 2026-04-27
---
## 1. 文档定位与原则
本文件定义 Fonrey 全局 API 契约标准,解决跨模块接口风格漂移问题。模块技术方案中的 API 章节必须遵循本文件,不得各自定义冲突规则。
### 1.1 强制级别
- **MUST**:必须遵守,违反视为缺陷
- **SHOULD**:建议遵守,若不遵守需在模块文档注明原因
- **MAY**:可选能力
### 1.2 适用接口类型
- JSON API`/api/**`
- HTMX 片段端点HTML response
- 文件上传/下载端点
---
## 2. 请求 / 响应格式规范
## 2.1 请求体规范JSON API
- `Content-Type` MUST 为 `application/json`
- `charset=utf-8` SHOULD 显式声明
- 写操作POST/PUT/PATCHMUST 传业务 payload禁止空对象写入
推荐请求结构:
```json
{
"data": {
"...": "业务字段"
},
"meta": {
"request_id": "可选,客户端透传"
}
}
```
兼容说明:历史端点已存在 `filters/sort/pagination` 平铺结构时可继续使用,但新接口 SHOULD 迁移到 `data` 容器。
## 2.2 成功响应规范JSON API
成功响应 MUST 使用统一 envelope
```json
{
"ok": true,
"data": {},
"meta": {
"request_id": "uuid",
"timestamp": "2026-04-27T16:30:00+08:00"
}
}
```
说明:
- `ok` MUST 为 `true`
- `data` MUST 存在(可为空对象 `{}` 或空数组 `[]`
- `meta` SHOULD 包含 `request_id` 与服务端时间
## 2.3 失败响应规范JSON API
失败响应 MUST 使用统一 envelope
```json
{
"ok": false,
"error": "权限不足",
"code": "PROPERTY_PERMISSION_DENIED",
"details": {},
"meta": {
"request_id": "uuid",
"timestamp": "2026-04-27T16:30:00+08:00"
}
}
```
说明:
- `error` MUST 为面向用户/调用方可读消息
- `code` MUST 为稳定机器可读码(大写下划线)
- `details` MAY 提供字段级错误(如校验失败)
## 2.4 HTMX 响应规范
- 成功:返回 HTML 片段;必要时通过 `HX-Trigger` 触发前端事件
- 失败:
- 状态码 MUST 正确4xx/5xx
- SHOULD 在响应头返回 `HX-Trigger`,例如:
- `{"toast:error":"权限不足"}`
- `{"toast:error":"请求失败,请重试"}`
---
## 3. 错误码规范
## 3.1 命名规则
- 错误码 MUST 为 `UPPER_SNAKE_CASE`
- 推荐前缀:`<MODULE>_`(如 `PROPERTY_` / `CLIENT_` / `ORG_`
## 3.2 HTTP 状态码基线
| HTTP | 使用场景 | 示例 code |
|---|---|---|
| 400 | 参数错误、业务前置条件不满足 | `PROPERTY_VALIDATION_ERROR` |
| 401 | 仅用于纯 API Token 鉴权失败(当前 Web 会话模式一般不用) | `AUTH_UNAUTHORIZED` |
| 403 | 已登录但无权限 | `*_PERMISSION_DENIED` |
| 404 | 资源不存在或不可见 | `*_NOT_FOUND` |
| 409 | 状态冲突、任务未就绪 | `*_STATE_CONFLICT` / `*_JOB_NOT_READY` |
| 422 | 字段级校验错误(可选) | `*_VALIDATION_FAILED` |
| 429 | 频控触发 | `RATE_LIMITED` |
| 500 | 未预期异常 | `INTERNAL_ERROR` |
## 3.3 稳定性要求
- `code` MUST 可稳定依赖,不得频繁改名
- 错误文案 `error` 可优化,但不应影响调用方流程判断
---
## 4. 分页规范
Fonrey 列表查询 MUST 使用 Keyset 分页;禁止 OFFSET 深分页。
## 4.1 请求格式
```json
{
"filters": {},
"sort": {"field": "updated_at", "order": "desc"},
"pagination": {"mode": "keyset", "cursor": null, "limit": 20}
}
```
## 4.2 响应格式
```json
{
"ok": true,
"data": {
"items": [],
"next_cursor": "opaque_cursor_2"
},
"meta": {
"pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20}
}
}
```
## 4.3 约束
- `limit` MUST 有上限(建议 ≤ 100
- `cursor` MUST 为不透明字符串,禁止暴露内部排序字段组合
- 排序字段 MUST 来自白名单,防止 SQL 注入与慢查询
---
## 5. 搜索 / 筛选规范
## 5.1 推荐请求结构
```json
{
"filters": {
"keyword": "保利",
"status": ["active", "pending"],
"district_id": "uuid"
},
"sort": {"field": "updated_at", "order": "desc"},
"pagination": {"mode": "keyset", "cursor": null, "limit": 20}
}
```
## 5.2 语义规范
- `keyword`:模糊检索词(服务端统一做 trim
- 多选条件 MUST 使用数组(如 `status: []`
- 空数组 `[]` 语义:不限制该条件
- `null` 语义:由模块文档明确(默认建议等同“不传”)
## 5.3 安全与性能
- 仅允许白名单字段参与筛选和排序
- LIKE/全文检索字段 SHOULD 建立索引或搜索策略
- 查询快照哈希(用于缓存/导出SHOULD 对 filters+sort+scope 进行规范化后计算
---
## 6. 上传规范
Fonrey 优先采用“预签名上传 + 回执提交commit”两段式。
## 6.1 标准流程
1. 客户端请求 upload-token业务 API
2. 客户端直传对象存储R2
3. 客户端调用 commit API 回写元数据
## 6.2 合约要求
- upload-token MUST 短时有效(建议 5~15 分钟)
- commit MUST 幂等(建议支持 `idempotency_key`
- 上传白名单与大小限制 MUST 在模块文档声明并在服务端校验
- SHOULD 校验 `content_type``size`
- MAY 增加 `sha256` 校验确保完整性
## 6.3 错误码建议
- `*_UPLOAD_TOKEN_EXPIRED` (409/400)
- `*_UPLOAD_FILE_TOO_LARGE` (400)
- `*_UPLOAD_FILE_TYPE_NOT_ALLOWED` (400)
- `*_UPLOAD_COMMIT_CONFLICT` (409)
---
## 7. 文件下载规范
下载统一采用“导出任务 + 状态查询 + download endpoint”。
## 7.1 标准流程
1. 创建导出任务 `POST /api/**/export/jobs/`
2. 轮询任务状态 `GET /api/**/export/jobs/{job_id}/`
3. 下载结果 `GET /api/**/export/jobs/{job_id}/download/`
## 7.2 合约要求
- 任务未完成下载 MUST 返回 `409` + `*_EXPORT_JOB_NOT_READY`
- 下载链接 SHOULD 为一次性或短时有效 URL
- 响应 SHOULD 设置 `Content-Disposition`(附件下载)
- 文件名 SHOULD 带模块与日期,便于审计
---
## 8. 权限拒绝返回规范
## 8.1 JSON API
- 未登录MUST 返回 `302`Web Session 场景)或 `401`(纯 API 场景)
- 已登录无权限MUST 返回 `403`
- 失败体 MUST 使用统一错误 envelope`code``*_PERMISSION_DENIED`
## 8.2 页面路由SSR/HTMX
- 未登录302 跳转登录页
- 已登录无权限403 页面(或 HTMX 403 片段)
- HTMX 拒绝 SHOULD 触发 `HX-Trigger` toast 事件
## 8.3 测试强约束
每个受保护端点 MUST 覆盖三态:
- 200有权限
- 403已登录无权限
- 302/401未登录视端点类型
---
## 9. 与模块文档的衔接规则
- 各模块技术方案中的“四、API 设计原则”“六、关键 API 规范”“十二、错误码建议”必须引用本文件
- 模块文档可补充模块特有 code 与字段,但不得与本规范冲突
- 冲突时以本文件为准;若需例外,必须在模块文档显式记录 ADR 链接
---
## 10. 落地检查清单Review Checklist
- [ ] 是否使用统一成功/失败 envelope
- [ ] 错误码是否为稳定 `UPPER_SNAKE_CASE`
- [ ] 列表接口是否全部 Keyset 分页
- [ ] filters/sort 字段是否白名单化
- [ ] 上传是否采用 token+commit 且具备幂等保障
- [ ] 下载是否采用 job 流程并处理未就绪 409
- [ ] 权限拒绝是否遵循 200/403/302(401) 三态
- [ ] 测试是否覆盖契约关键路径