Files
2026-08-31 14:37:28 +08:00

133 lines
9.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.1 API 契约(新增 + 复用)
> 状态:前端服务层已按本文开发(Phase A);服务端 Phase B 按 本文实现。
> 通用:鉴权 `Authorization: Bearer <JWT>`(studio 走 axios 拦截器自动附带);错误统一 FastAPI `detail`。
---
## 1. 复用的现有接口(Phase A 即可用)
| 接口 | 用途(试算页) |
|---|---|
| `GET /api/products/{id}`、`PATCH /api/products/{id}` | 商品详情 / 试算页自动保存(raw/pricing/copy/price/weight/... 全字段) |
| `GET /api/products/{id}/assets` | 素材列表(分组/规格/状态/current `stored_url`、`original_url`、水印状态) |
| `GET /api/products?stage&q&page&page_size` | 批量 CSV / 组合码导出的数据源 |
| `POST /api/materials/bytes` | 试算页「上传图片」(FormData:`product_id, group_key=upload, file`)→ `{asset_id, status}` |
| `GET /api/fx` | 汇率 `{rate, source, ts}` |
| `GET /api/ai/models`、`POST /api/ai/copy` | 俄文文案(模型白名单 / 生成) |
| `POST /api/image/edit` | 智能修图页保留,不在试算页主链路 |
## 2. 套图规划 `POST /api/suite/plan`(Phase B)
**Req**
```jsonc
{
"product_id": "uuid", // 可选:便于日志与上下文缓存
"texts": [ { "kind": "title|price|params|selling_point|desc|brand|sales|shop",
"content": "…", "pairs": [ { "key": "…", "value": "…" } ] } ],
"sku_variants": ["粉色", "蓝色"],
"image_stats": { "main": 8, "sku": 6, "detail": 12 },
"platform": "ozon",
"requirements": "…|null" // 用户强制要求,最高优先级
}
```
**Res** `{ "summary": "一句话方案说明", "items": [ { "kind": "white_bg", "title": "白底主图", "detail": "…", "prompt_hint": "…", "count": 2, "variant_name": "粉色|null" } ] }`
实现要点:DeepSeek 单行紧凑 JSON;kind 白名单清洗;count 钳 0-3;幻觉 variant 丢弃绑定;总张数 AI 自主(建议 8 张左右;安全上限:默认 15,生图要求指定「N 张」时按 N、最大 24)。同步接口(数秒级)。
## 3. 一键生成 `POST /api/suite/generate`(Phase B)
**Req**
```jsonc
{
"product_id": "uuid",
"texts": [ …同上… ],
"images": [ { "url": "…", "group_key": "main|sku|detail|upload", "variant_name": "粉色|null" } ], // 勾选参考底图
"style_set": 1,
"style_prompt": "…|null", // 用户改写的风格提示词,覆盖后端模板
"requirements": "…|null",
"plan": [ …PlanItem(count>0)… ],
"platform": "ozon",
"model": "gpt-image-2-vip",
"watermark": { "enabled": true, "type": "image|text", "text": "xiongmaoyx", "opacity": 30 } // 关闭时缺省
}
```
**Res** `{ "suite_id": "…" }`
实现要点:方案按 count 展开为持久化逐图子任务;独立 Worker 领取子项并在完成后回写进度。Pcore、RightAPI 与通义万相的第三方任务 ID 会先写入数据库,重启后继续轮询同一个模型任务;完成后再水印、落 storage、追加 `product_assets(group_key='generated')`。
## 4. 任务查询 `GET /api/suites/{id}`(Phase B)
**Res**
```jsonc
{
"id": "…", "status": "queued|running|done|partial|failed|cancelled",
"style_set": 1, "platform": "ozon", "lang": "俄文", "ratio": "3:4", "provider": "rightapi",
"total": 8, "completed": 3, "failed": 1,
"images": [ { "type_id": "white_bg", "name": "白底主图-01.png", "url": "/media/suites/…png",
"status": "queued|waiting_provider|running|done|failed|cancelled", "error": null } ],
"error": null
}
```
前端采用“本次请求结束后再延迟下一次”的递归轮询(正常 3s、请求失败指数退避),并在切换任务、组件卸载时取消在途请求;`done/partial/failed/cancelled` 为终态。每条 `images` 项另含 `item_id`,用于按图重试。浏览器会暂存当前任务 ID,并从服务端查询商品的活动任务;Web 服务或 Worker 重启后仍可恢复任务状态。
辅助接口:`GET /api/products/{product_id}/suites?active_only=true` 恢复活动任务;`POST /api/suites/{id}/cancel` 取消任务;`POST /api/suites/{id}/retry` 重试全部失败子项;`POST /api/suites/{id}/items/{item_id}/retry` 只重试指定失败项。
## 5. 结果导出 `GET /api/suites/{id}/zip`(Phase B)
→ `application/zip` blob(按方案标题命名内部文件)。404/失败返回 `detail` JSON。
## 6. 单张 AI 生图 `POST /api/suite/image-edit`(Phase B)
**Req**
```jsonc
{
"product_id": "uuid",
"image_url": "…", // stored_url 或 source_url(服务端代下绕防盗链)
"prompt": "把背景换成纯白色摄影棚", // 必填
"model": "nano-banana-2",
"append": true // true:结果追加 product_assets(generated)
}
```
**Res** `{ "suite_id": "…" }`
接口仅创建并提交统一图片任务,立即返回;前端通过 `GET /api/suites/{suite_id}` 查询状态和 `images[0].url`。完成后若 `append=true`,结果会回写为 `product_assets(group_key='generated')`。单张编辑优先级高于套图,提交模型后同样持久化第三方任务 ID;参考图 = 传入单图;Ozon 规格(俄文 3:4)。
## 7. 素材水印 `POST /api/assets/watermark` / `POST /api/assets/watermark/remove`
`POST /api/assets/watermark` 的请求体为 `{ "asset_ids": ["uuid"], "watermark": { "enabled": true, "type": "text|image", "text": "…", "opacity": 30 } }`,立即返回 `{ "job_id": "uuid", "total": n }`。Worker 对选中的采集图或生成图逐张应用水印,前端经 `GET /api/suites/{job_id}` 查看逐图进度和错误;`GET /api/products/{product_id}/watermark-jobs?active_only=true` 用于页面刷新后恢复。
`POST /api/assets/watermark/remove` 的请求体为 `{ "asset_ids": ["uuid"] }`,将选中图片恢复为无水印原图。
`POST /api/assets/watermark/remove` 当前仍为短时同步操作,返回 `{ "updated_ids": [], "failed": [] }`。两接口均只保留原图和当前展示版本:重新加水印会在数据库提交后删除旧水印文件;前端展示、单张下载与导出均使用 `stored_url`。
## 8. 商品轮播视频
- `POST /api/products/{product_id}/video-jobs`:请求 `{ "asset_ids": ["uuid", "uuid"] }`,按传入顺序选择当前商品的 2–5 张已转存图片,立即返回 `{ "job_id": "uuid" }`。服务端只接受素材 ID,不接受任意图片 URL。
- `GET /api/products/{product_id}/video-jobs`、`GET /api/video-jobs/{job_id}`:读取任务和成片状态;`POST /api/video-jobs/{job_id}/cancel` 会取消并立即清理任务记录,`POST /api/video-jobs/{job_id}/retry` 重试失败任务,`DELETE /api/video-jobs/{job_id}` 删除成片及记录,`GET /api/video-jobs/{job_id}/download` 下载 MP4。
- Worker 使用 FFmpeg 合成无音轨轮播视频:AI 生成图在前端选择器中优先展示;支持 2–5 张,按 4.5 / 3.5 / 3.0 / 2.8 秒单图停留并使用 0.35 秒淡入淡出。默认输出为 `810×1080`、`30 fps`、`H.264`、`yuv420p`、最大 45 MB,结果存入 `products/{product_id}/videos/{job_id}/result.mp4`。
- 需要系统安装 `ffmpeg` 与 `ffprobe`(Ubuntu:`apt install -y ffmpeg`);路径和大小/超时限制可通过 `VIDEO_*` 环境变量覆盖。任务复用 `background_jobs`,无需新增视频业务表;进行中、失败和已完成成片可在刷新后继续查看,取消会立即移除任务及临时文件,不保留记录。
## 9. 导出与代理(Phase B / D)
| 接口 | 方法 | Req | Res |
|---|---|---|---|
| `/api/export/images` | POST | `{ "title": "…", "images": [{ "url", "groupName", "variantName", "key" }] }` | zip blob(分组建文件夹) |
| `/api/proxy-image?url=` | GET | 源站图片 URL | 图片字节(带站点 Referer 代下,绕防盗链) |
| `/api/export/trial-csv` | GET | query:`stage?` | csv blob(16 列,见 trial-page.md §7;Phase D 服务端化,替代前端拼装) |
图片资源上限:默认单张上传/远程下载 20 MB;一次导出最多 50 张,ZIP 内源图片总量与成品 ZIP 均最多 200 MB。超过限制返回 HTTP `413` 及可读错误提示;ZIP 超过 2 MB 自动写入临时磁盘,并以分块响应下载。
图片校验与元数据:图片上传、采集转存和 AI 生成图写入素材库时,服务端以实际字节识别 JPEG、PNG、WebP、GIF 或 BMP,不信任 URL 后缀和上传声明的 MIME。解码失败或不支持的格式返回/记录为失败;默认限制为 40,000,000 像素(`IMAGE_MAX_PIXELS`),并记录 `product_assets.width`、`height`。静态图片若带 EXIF 方向,会在落库前纠正并移除该方向标记;动画图不重编码,以避免丢帧。`POST /api/materials/bytes` 成功响应会额外返回 `width`、`height`,不合规图片返回 `422`。
远程图片安全规则:`/api/proxy-image`、采集图转存、AI 参考图及远程导出统一只请求公网 `http(s)` 地址;`localhost`、回环、内网、链路本地、多播和其他保留 IP 会返回 `400`。重定向不会自动跟随:服务端最多跟随 3 跳,并在每一跳重新解析和校验目标地址。已转存的本项目 `/media/...` 素材直接读取本地磁盘,不经过 HTTP 回环请求。下载仍受 `IMAGE_DOWNLOAD_MAX_BYTES` 限制,连接/读写/总超时与重定向次数可通过 `IMAGE_DOWNLOAD_CONNECT_TIMEOUT`、`IMAGE_DOWNLOAD_READ_TIMEOUT`、`IMAGE_DOWNLOAD_TOTAL_TIMEOUT`、`IMAGE_DOWNLOAD_MAX_REDIRECTS` 配置;下载超时返回 `504`。
## 10. 采集相关(Phase C 使用,均已实现)
- `POST /api/materials`:插件上传采集结果;**响应含 `product_id`**(插件用于打开 `/trial/{id}`)。
- `GET /api/collected?platform=&itemId=`:采集前查重。
## 11. 冻结接口(不投入,保留代码)
`/api/publish/*`、`/api/shops/*`、`/api/categories/*`——Ozon API 直传链路(D1)。