Files
atlas-seller-kit/docs/api/conventions.md
T

125 lines
6.2 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.
# API 统一约定
## 1. 鉴权与工作空间隔离
除 `POST /api/auth/register`、`POST /api/auth/login`、`POST /api/auth/refresh` 外,所有接口都要求:
```http
Authorization: Bearer <short-lived-access-token>
```
访问令牌只携带用户会话标识,不以客户端传入的 `workspace_id` 判定数据归属。服务端解析用户、选择首期唯一的有效工作空间、验证成员关系后,在每一个数据库事务中执行 `SET LOCAL app.workspace_id`。RLS 仅是第二道防线,业务层仍须验证资源归属。
Web 登录后的 refresh token 放在 `HttpOnly`、`Secure`(生产)、`SameSite=Lax` 的 cookie 中,永不出现在 JSON、日志或前端持久化存储。access token 由前端仅在内存中短暂持有;插件使用独立授权码流程,留待采集插件接口文档定义。
## 2. 页头平台与默认店铺
平台范围接口使用:
```http
X-Atlas-Platform-Id: 57ad…-uuid
```
- 无此 header 的接口不依赖平台,例如 `GET /api/auth/me`、水印和通用素材接口。
- 需要平台却没有 header 时返回 `400 PLATFORM_CONTEXT_REQUIRED`。
- header 指向停用或不存在的平台时返回 `422 PLATFORM_UNAVAILABLE`。
- 默认店铺是服务端按工作空间和该 header 查出的状态,响应中以 `default_store` 明确返回;客户端不应缓存为事实。
列表筛选中的 `platform_id` 只用于平台目录或跨平台管理页,不能绕开数据授权;同一请求不得同时带 `X-Atlas-Platform-Id` 和语义相同的 `platform_id`。
## 3. 响应、分页和字段格式
### 3.1 强制响应信封
本项目不采用完整 JSON:API 规范,但所有业务 HTTP 接口必须使用以下响应信封;这是
前端、服务端、Worker 和扩展共同遵守的强制契约:
| 场景 | HTTP 响应体 |
|---|---|
| 成功的单资源或同步动作 | `{ "data": T }` |
| 成功的列表 | `{ "data": T[], "meta": PaginationMeta }` |
| 失败 | `{ "error": ApiError }` |
| `204 No Content` | 无响应体 |
`data` 只能位于顶层且只能出现一次;不得返回 `{ "data": { "data": ... } }`,也不得以
`{ "data": null }` 表示失败。HTTP 状态码仍表达请求结果,错误必须使用本文件第 4 节的
`error` 信封,不能混入成功响应。`meta` 只承载分页、游标或与整个响应相关的元信息,不能
替代资源字段。
### 3.2 成功与列表响应
成功响应统一为:
```json
{ "data": { "id": "uuid" } }
```
列表响应:
```json
{
"data": [{ "id": "uuid" }],
"meta": { "next_cursor": "opaque-string-or-null", "has_more": false }
}
```
- 游标是服务端不透明值,客户端只能原样传回 `cursor`;默认按文档指定稳定排序。
- `limit` 默认 `20`,最大 `100`;超出时返回 `422 VALIDATION_FAILED`。
- 金额与费率以 JSON **字符串**返回和提交,例如 `"199.90"`、`"0.125"`,避免 JavaScript 浮点误差;币种用 ISO 4217 大写代码。
- 所有时间用 UTC,例如 `"2026-09-02T08:00:00Z"`;日期用 `YYYY-MM-DD`。
- 不返回 `password_hash`、API Key、Cookie、密文、对象存储永久密钥或长期访问 URL。
## 4. 错误格式和 HTTP 状态
```json
{
"error": {
"code": "VALIDATION_FAILED",
"message": "请求参数不符合要求",
"request_id": "req_…",
"details": [
{ "field": "email", "code": "EMAIL_ALREADY_REGISTERED", "message": "该邮箱已注册" }
]
}
}
```
| 状态 | 使用场景 |
|---:|---|
| `400` | 缺少平台上下文、请求语义矛盾、无法解析的 JSON。 |
| `401` | 缺少、失效或撤销的 access token。 |
| `403` | 已登录但无工作空间成员资格或无操作权限。 |
| `404` | 当前工作空间范围内不存在该资源;不暴露其他工作空间资源。 |
| `409` | 重复唯一资源、幂等键与既有请求不一致、乐观锁冲突。 |
| `422` | 字段格式、业务状态或平台能力不满足。 |
| `429` | 登录、刷新、上传签发或重试操作触发限流。 |
| `500` | 未预期服务错误;不返回秘密或平台原始敏感响应。 |
## 5. 写操作的幂等、并发和异步
- 创建店铺、上传意图、删除水印模板等可重试写操作应携带 `Idempotency-Key: <UUID>`。服务端按“当前用户 + 工作空间 + HTTP 方法 + 规范化实际路径 + key”保存至少 24 小时(首期默认 48 小时);请求摘要保存为服务端 HMAC,而非明文请求体。相同键、相同请求返回已保存响应;相同键、不同请求返回 `409 IDEMPOTENCY_KEY_REUSED`;原请求仍在执行返回 `409 IDEMPOTENCY_REQUEST_IN_PROGRESS`。
- 更新可编辑资源时,读取响应携带 `revision`;客户端在 `PATCH`/`PUT` 中提交 `If-Match: "<revision>"`。版本已变化返回 `409 VERSION_CONFLICT` 和最新 `current_revision` 摘要。`revision` 是并发控制版本;不要把水印的渲染 `current_version` 当作它。
- 耗时动作(店铺凭据验证、仓库同步、媒体处理)先返回 `202 Accepted`,并在资源中返回 `operation`。客户端轮询 `GET /api/operations/{operation_id}`;后续可追加 SSE,不改变资源接口。
- 所有启动异步工作的业务写入,必须在同一 PostgreSQL 事务中写入业务资源/`operation` 和 `outbox_events`;独立 Dispatcher 再投递 BullMQ。BullMQ 和 Redis 不得作为任务存在或最终业务结果的唯一事实。
`operation` 统一形状:
```json
{
"id": "uuid",
"kind": "store_credential_verification",
"status": "queued",
"created_at": "2026-09-02T08:00:00Z",
"completed_at": null,
"error": null
}
```
操作接口只返回当前工作空间可见的任务,且任务状态不能作为唯一事实;业务资源中的状态字段才是页面最终展示依据。
`GET /api/operations/{operation_id}` 返回上述 operation 形状。任务不属于当前工作空间时返回 `404`;`error` / `error_message` 只能是可展示的脱敏摘要,不能包含 API Key、Cookie、对象存储签名或平台原始敏感响应。
## 6. 删除与软删除
首期店铺和水印模板使用软删除。成功删除返回 `204 No Content`;之后以普通 `GET` 读取返回 `404`,列表默认不包含删除记录。历史发布、素材派生和审计记录保留其快照,不因软删除被改写。