Files

220 lines
8.7 KiB
Markdown
Raw Permalink 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.
# V2 后端 API 设计
> 状态:方案设计(待确认)
> 上游:[V2 总览](./README.md) · [V2 架构](./architecture.md) · [数据库](./database.md)
统一约定:
- 前缀 `/api`;鉴权 `Authorization: Bearer <JWT>`(除 `/auth/login``/health` 外)。
- 错误:FastAPI 语义状态码;`detail` 为可读中文;校验错误 `422`
- 列表分页:`?page=&page_size=``?limit=&last_id=`(Ozon 风格,仅对代理 Ozon 的接口)。
- 时间一律 ISO 8601 UTC。
---
## 1. 鉴权 `/api/auth`
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/auth/login` | `{token}``{access_token, expires_at}`。MVPtoken 等于 `.env``APP_TOKEN`;升级后:用户名密码 |
---
## 2. 采集入库 `/api/materials`(插件 → 服务端)
沿用 `docs/extension/plan.md` §14 的契约,**把「文件夹」换成「商品」**。
### `POST /api/materials` ★ 主接口
```jsonc
{
"product": { // 对应「文件夹」;首次上传可空=新建
"id": "uuid-or-null" // 传了=追加到已有商品(跨平台补素材)
},
"source": {
"platform": "ozon", // ozon | 1688 | taobao
"itemId": "123456789",
"url": "https://www.ozon.ru/product/…",
"collectedAt": 1710000000000
},
"texts": [
{ "kind": "title", "content": "…" },
{ "kind": "params", "content": "…", "pairs": [{ "key": "…", "value": "…" }] }
],
"images": [
{ "groupKey": "main", "groupName": "主图", "variantName": null,
"url": "https://…原图…", "index": 0, "type": "img", "dedupeKey": "…" }
],
"refererOrigin": "https://www.ozon.ru" // 该站图片下载需带的 Referer
}
```
响应:
```jsonc
{ "product": { "id": "…", "stage": "collected" }, "assetsQueued": 12 }
```
**语义**:服务端立即落库(product + texts 进 raw + assets 置 `pending`),**响应不等图片下载完成**;后台协程按 `refererOrigin` 下载源图 → 转存七牛 → 更新 asset 状态。插件无需等待。
### 其余
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/materials/bytes` | 字节兜底:`multipart/form-data``product_id` + `meta`(JSON) + `file`),供源图带登录态、插件在页面上下文抓字节上传 |
| GET | `/products/:id/fingerprints` | 跨页去重:返回已采集 `dedupeKey` 列表 |
| GET | `/collected?platform=&itemId=` | 状态回显:`{collected, count}`(页面徽标用) |
---
## 3. 商品 `/api/products`
### 列表(采集箱 / 发布列表)
`GET /api/products?stage=&q=&page=&page_size=`
```jsonc
{
"total": 128,
"items": [
{
"id": "…", "stage": "collected",
"name": "…", "offer_id": "", "price": null,
"source_platform": "ozon", "source_url": "…",
"asset_count": { "main": 6, "sku": 4, "detail": 9 },
"ozon_product_id": null,
"created_at": "…", "updated_at": "…"
}
]
}
```
### 详情 / 编辑 / 删除
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/products/:id` | 完整商品(含 raw / pricing / copy / attributes / assets |
| PATCH | `/products/:id` | 部分更新(编辑页 autosave)。可改字段见 database.md §2.3 |
| POST | `/products` | 手动新建商品(不经过插件) |
| DELETE | `/products/:id` | 软删(stage→archived)或硬删(采集箱未发布项) |
| POST | `/products/:id/stage` | `{stage}` 流转(editing→ready 前校验必填项) |
> 编辑页的计价、文案、图片、类目/属性都是「编辑 `products` 的某几列」,统一走 `PATCH /products/:id` 或细分子资源(见 §6–§8),不新增独立存储。
---
## 4. 汇率 `/api/fx`
`GET /api/fx``{ "rate": 11.84, "source": "cbr", "updated_at": "…" }`
服务端抓取(FloatRates → 俄央行 → er-api 三级降级,沿用 v1 数据源但**移到服务端**),缓存 + 脏数据过滤(5~25 区间)。前端计价用它,也可在计价时快照进 `products.fx_rate`
---
## 5. AI 文案 `/api/ai`(沿用现有,零改动)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/ai/models` | 模型列表(models.yaml |
| POST | `/ai/copy` | 中文采买信息 → 俄文标题/描述/标签 + 中文对照 |
V2 编辑页的「文案」面板直接消费这两个接口,生成结果写入 `products.copy` + `products.name/description`(用户确认后)。
---
## 6. 图片 `/api/image`
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/image/edit` | **智能修图**wanx2.1-imageedit,改名,沿用现有)——单图换背景/去水印/局部重绘/加文字 |
| POST | `/image/suite` | **电商套图**(集成 ecommerce-image-suite,新增)——原图 + 卖点 → 套图 |
| POST | `/image/upload-token` | 获取七牛直传 token(若走前端直传;一期可省,走服务端中转) |
`/image/edit``/image/suite` 的结果图由服务端**下载 → 转存七牛 → 返回七牛 URL**(改造现状 image_edit.py 已预留的扩展点)。
详见 [`image-strategy.md`](./image-strategy.md)。
---
## 7. 店铺 `/api/shops`
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/shops` | 列表(脱敏:不返回 key) |
| POST | `/shops` | `{name, client_id, api_key, currency_code}` 绑定(加密落库) |
| PATCH | `/shops/:id` | 更新(可只更新 name/currency,或换 key |
| DELETE | `/shops/:id` | 删除(级联校验是否有进行中发布) |
| POST | `/shops/:id/test` | **连通性校验**:用该店铺凭证调 Ozon `/v1/roles`,成功→`{ok:true, roles:[…]}`,失败→`status=invalid` 并返回原因 |
> `test` 用 `/v1/roles`(返回该 key 的角色与方法权限),既验凭证又验权限范围,成本为零。
---
## 8. 类目与属性 `/api/categories`(服务端代理 Ozon
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/categories/tree?lang=RU` | 类目树(代理 `/v1/description-category/tree`,服务端缓存) |
| GET | `/categories/:category_id/attributes?type_id=` | 属性列表(代理 `/v1/description-category/attribute` |
| GET | `/categories/attribute/:attribute_id/values?category_id=&type_id=&q=` | 属性值字典(代理 `/values``/values/search` 按需) |
> 这些接口需要店铺凭证(Client-Id/Api-Key)。请求体里带 `shop_id`,服务端用对应店铺凭证调 Ozon。类目树可全局缓存(与店铺无关);属性/值按类目缓存。详见 `ozon-publish.md` §3。
---
## 9. 发布 `/api/publish`
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/products/:id/publish` | `{shop_id}` 发布:组装 items[0] → `/v3/product/import` → 建 `publish_tasks` → 返回 `{task_id}` |
| GET | `/publish/:taskId` | 查询发布任务状态(服务端已轮询回写,直接读库) |
| GET | `/products/:id/publish-history` | 该商品历史发布记录 |
**发布请求体组装**(服务端职责,见 `ozon-publish.md` §5):
1.`products` 平铺字段 + `attributes`/`complex_attributes`
2. 剥离 `_` 前缀扩展字段(DB 里已天然分层,无需剥离);
3. 校验必填(name/description/category/尺寸重量/offer_id/images);
4. `POST /v3/product/import`(头 `Client-Id`/`Api-Key`)→ 得 `task_id`
5. 后台轮询 `POST /v1/product/import/info` → 回写 `products.stage` + `ozon_product_id`
---
## 10. CSV 导出 `/api/export`
`GET /api/export/products.csv?stage=&ids=` → 流式返回带 BOM 的 UTF-8 CSV。
字段(默认全量,`fields=` 可指定子集):
```
offer_id, product_id, name, description_category_id, price, old_price,
currency_code, vat, weight, weight_unit, depth, width, height, dimension_unit,
barcode, primary_image, images(join "|"), source_platform, source_item_id, source_url,
stage, published_at, created_at
```
已发布商品含 `product_id`;未发布留空。支持按 `stage`collected/published/全部)与 `ids`(勾选导出)筛选。CSV 字段明细与公式见 [`ozon-publish.md`](./ozon-publish.md) §6。
---
## 11. 插件运维接口(沿用 plan.md §14)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/ext/profiles` | 远程采集配置下发(选择器热更) |
| POST | `/ext/logs` | 埋点批量上报(抗改版看板) |
---
## 12. 接口 → 现有代码映射(改造量)
| V2 接口 | 现状 | 改造 |
|---|---|---|
| `/auth/*` | 无 | 新建 |
| `/materials` 系列 | 无(plan 里有设计) | 新建 |
| `/products` 系列 | 无 | 新建 |
| `/fx` | 无(v1 前端直连第三方) | 新建(搬 v1 数据源到服务端) |
| `/ai/*` | ✅ 有 | 复用 |
| `/image/edit` | ✅ 有 | 复用 + 加七牛转存 |
| `/image/suite` | 无 | 新建(集成 ecommerce-image-suite |
| `/shops``/categories``/publish``/export` | 无(`api/ozon.py` 占位) | 新建 |
| `/ext/*` | 无 | 新建 |