feat: 开发采集、采集箱和商品编辑功能

This commit is contained in:
Joey
2026-08-15 22:17:26 +08:00
parent c61d1a3154
commit 36357843d0
130 changed files with 18005 additions and 12 deletions
+219
View File
@@ -0,0 +1,219 @@
# 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/*` | 无 | 新建 |