feat(ai-image): AI生图开发

This commit is contained in:
2026-09-27 22:40:52 +08:00
parent e670d06ca6
commit a29dd92676
105 changed files with 9943 additions and 361 deletions
+18 -5
View File
@@ -86,6 +86,18 @@ Headers:`Idempotency-Key` 必填,页头平台由 header 指定,body 为空
## 2. 原子保存草稿与 SKU 规则
### 单张 AI 图片编辑(SKU 选图弹窗)
商品编辑页固定任务入口使用 `GET /api/products/{product_id}/image-tasks?limit=20&cursor=...`。`limit` 默认 20、最大 100,按进行中优先、创建时间倒序、ID 倒序稳定分页,返回 `{ data: ProductImageTask[], meta: { next_cursor, has_more } }`。接口只返回当前工作空间、当前商品的图片 AI 任务;首期包含单张编辑,后续套图任务按同一结构加入。每项包含 `kind: single_ai_edit | ai_suite`、`status`、`phase`、已完成数/总数、创建/完成时间、稳定错误码、`failure_source: pcore | atlas | null`、`failure_stage: task_setup | input_preparation | provider_submission | provider_polling | result_download | result_processing | result_storage | null`、`failure_detail: string | null`、`provider_error_raw: string | null` 与结果素材 ID 数组。失败来源表示 Pcore 接口返回/调用失败或 Atlas 自身处理失败;步骤指出实际出错的位置。`failure_detail` 是最长 500 字符的脱敏诊断摘要,不包含密钥、对象键或预签名 URL。Pcore 失败时 `provider_error_raw` 保留供应商原始错误响应文本(JSON 响应保存错误对象,最多 32,768 字符),便于在当前工作空间的任务列表中查看;没有供应商响应时为 `null`。历史任务未记录的字段返回 `null`,不得按通用错误码猜测失败来源。`GET /api/media-ai-edits/{task_id}` 的单张编辑响应也返回三个诊断字段,供未关闭的编辑弹窗显示。不返回供应商任务 ID、对象键或预览 URL。任务入口每 3 秒刷新运行中的任务,其余时间低频刷新;任务提交后即时刷新。查看结果时再用素材访问接口签发短时预览 URL。单张编辑任务取消沿用 `POST /api/media-ai-edits/{task_id}/cancel`。
`POST /api/media-assets/{asset_id}/ai-edits`:必须带 `Idempotency-Key`,请求体带当前工作空间 `product_id`,图片字段只使用正式素材 ID,不接受图片 URL。`asset_id` 为固定主图;`reference_asset_ids` 最多两项,仅作辅助参考。请求支持 `prompt`、`mode: edit | outpaint`、`ratio: 3:4 | 1:1 | 4:5`、图中文字翻译与目标语言、背景模式与描述、五种可改写的视觉风格、商品长宽高及尺寸标注图。`outpaint` 只允许 3:4、单主图,不生成尺寸标注。成功返回 HTTP 202、任务 ID 和 `queued` 状态。
`GET /api/media-ai-edits/{task_id}` 查询 `queued | running | succeeded | failed | cancelled`,成功时才返回独立 `output_asset` 的正式素材 ID、尺寸与短时预览地址;失败仅返回稳定错误码。`GET /api/media-ai-edits?product_id={product_id}` 返回当前商品最近 100 条成功任务,供右侧「AI 生图记录」复用素材。`POST /api/media-ai-edits/{task_id}/cancel` 需带 `Idempotency-Key`,将未完成任务标记为取消;已完成任务保持原结果。任务、operation、Outbox 在同一工作空间事务提交;Worker 持久化供应商任务 ID 后通过延迟 Outbox 继续轮询。供应商密钥、对象存储键和供应商临时图片地址不进入 API 响应或 Outbox。
供应商异步任务返回终态失败时不再轮询或重试同一任务 ID。只有供应商明确返回内容政策或审核错误码时才使用稳定错误码 `AI_IMAGE_CONTENT_REJECTED`;笼统的 `failed`(即使消息称“可能”涉及安全政策)及其他供应商终态失败使用 `AI_IMAGE_PROVIDER_FAILED`;提交、网络、存储等非终态问题在重试耗尽后使用 `AI_IMAGE_GENERATION_FAILED`。Worker 在终态失败或重试耗尽时把失败来源、步骤和脱敏摘要写入同工作空间的 operation 结果;用户界面显示阶段和摘要,并在任务卡片展示已取得的 Pcore 原始错误内容。历史任务若原先未保存错误内容,则相关字段为 `null`。
生成结果先在单张 AI 编辑抽屉中预览。用户点击“添加到当前 SKU”后,新素材插入原图后方,原图保留;选图弹窗“确认”仅写入编辑页表单,页面“保存”后才持久化 SKU `media_asset_ids`。关闭或取消编辑不修改 SKU 草稿。图片源、参考图、结果均受工作空间隔离;任务输出与源图分别存储。
`PUT /api/products/{product_id}/editor-draft`
Headers:`If-Match: "{product_revision}:{profile_revision}"`、`Idempotency-Key` 必填。请求是整个编辑页的原子快照:
@@ -123,11 +135,12 @@ Headers:`If-Match: "{product_revision}:{profile_revision}"`、`Idempotency-Key
- `profile.platform_product_category_id` 必须是当前平台的活动类目;同平台门店共享类目、类型属性和字典缓存。服务端从平台类目记录取得 `platform_type_id`,客户端不能伪造类型 ID。过渡期仍接受 `store_product_category_id`,但只用于按 `description_category_id + type_id` 映射平台类目,不参与当前读取和校验。
- 类目切换或平台 schema 移除属性时,原有不再适用的属性不会进入新的发布草稿;服务端将它们写入 `unmapped_attributes` 并在编辑快照中返回。用户删除 SKU 时,其 SKU 属性随 SKU 一起移除,不生成无主未映射属性。
- `variants` 是当前完整未删除 SKU 集合。省略既有 SKU 表示软删除;无论当前 SPU 状态都允许新增、复制、删除。`copy_from_variant_id` 仅用于新行,复制通用 SKU 数据但不复制任何平台导入映射,生成的新 SKU 从 `not_listed` 开始。
- `variants[].purchase_cost_cny` 是人民币采购价,编辑快照以两位小数字符串返回;草稿输入最多两位小数。来源采集写入时按两位小数四舍五入。
- 删除已导入 SKU 只从 Atlas 草稿及当前平台可发布集合移除,**不删除、不下架、不修改** Ozon 商品或在线镜像;其他平台历史映射保留。删除后当前平台只剩已导入 SKU 时,SPU 汇总自然为“已上架”。
- `null` 的 SKU 规格字段表示继承父商品;包装物流规格的 `status` 为 `missing`/`ambiguous` 时,不允许定价确认或首次上架,但仍允许保存草稿。发布前服务端必须对每个待发布 SKU 解析 `effective_package_spec`,确认长、宽、高、重量均存在且大于 0 后才可生成 Ozon item。实物规格不用于物流;是否向 SPU 或 SKU 展示由其对应的类目 schema 属性决定,尺寸图仍可使用已确认的有效实物规格。
- `target_store_id` 只能是当前工作空间、当前平台的活动店铺。变更目标店铺只更新**尚未导入** listing 的将来发布目标;`imported` listing 永远保留其首次导入时的店铺快照。
- 类目变化后不匹配的旧属性作为未映射草稿返回 warnings,不能自动进入发布 payload。属性由平台 schema 校验 `type`、必填、字典、多值/复杂属性和 `is_aspect`;客户端不能用任意 JSON 绕过。
- `media_asset_ids` 是本 SKU 当前平台的最终选图顺序;最多 30 张。格式只允许 JPG/PNG/WebP,单文件最多 20 MB,边长超过 10,000 px 拒绝,短边小于 700 px 不得作为首图并给出警告。第一张是主图。已完成的 `product_image` 上传素材和网络导入素材可供同工作空间的 SKU 选择;采集图片仍须先转存,且来源变体须匹配。
- `media_asset_ids` 是本 SKU 当前平台的最终选图顺序;最多 30 张。格式只允许 JPG/PNG/WebP,单文件最多 20 MB,边长超过 10,000 px 拒绝,短边小于 700 px 不得作为首图并给出警告。第一张是主图。已完成的 `product_image` 上传素材和网络导入素材可供同工作空间的 SKU 选择;当前商品成功的水印与 3:4 处理结果也可选择。采集图片仍须先转存,且来源变体须匹配。
- `common.model` 是用户手填或主动 AI 填报的商品型号;采集时允许为空,但每次首次上架前必须非空。`offer_id` 是用户手填或主动 AI 填报的目标平台卖家货号;采集入箱时不填值,生成时不直接复用 `source_sku`。草稿可为空,但每个待首次上架 SKU 的 `offer_id` 必须非空且在同一店铺/平台唯一。AI 填报接口和回填规则见[方案](../product/ai-autofill-plan.md)。
- `common.source_title` 在编辑页显示为“中文商品名”;`profile.title` 显示为“俄文发布标题”,页面保存时必填。为允许先保存采集草稿再使用 AI 填报,API 草稿契约仍接受空标题;发布就绪校验在 `profile.title` 为空时返回 `PUBLISH_TITLE_REQUIRED`。SKU 发布标题 `variants[].title` 为 `null` 时继承 `profile.title`,不回退到 `display_name`;现有 SKU `tags=[]`、`description=null` 保持继承语义。标题、标签、描述分别独立覆盖。中俄对照按 SPU/SKU 与字段随 `editor-draft` 原子保存;SKU 继承项直接引用 SPU 对照,手工俄文覆盖但无匹配中文时标为对照待更新。发布适配器使用 SKU 有效文案仍是后续工作。
@@ -251,17 +264,17 @@ Headers:`Idempotency-Key` 必填。Body:
媒体选择通常随第 2 节草稿一起保存。为支持图片弹窗的异步“添加水印”,提供:
`POST /api/product-variant-platform-listings/{variant_listing_id}/watermark-derivatives`
`POST /api/media-assets/{asset_id}/watermark-derivatives`
Headers:`Idempotency-Key` 必填。Body:
```json
{ "source_asset_id": "uuid", "watermark_template_id": "uuid" }
{ "watermark_template_id": "uuid" }
```
服务端验证 source asset 是该 SKU 当前已选图片、模板属于当前工作空间且活动;固化模板**当前版本**后创建 `media_derivatives` 和 Worker 任务,返回 `202` 及 operation。任务成功后返回独立 output asset;原图和其他 SKU 的选择均不改变。用户在图片弹窗确认后,将 output asset 写入下一次 `editor-draft.media_asset_ids` 来替换原图位置;可再次选择原图恢复。首期不在发布时自动添加水印。
服务端验证源图片是当前工作空间已校验的 JPG/PNG/WebP 正式素材,模板属于同一工作空间且活动;固化模板**当前版本**后,在同一事务创建水印派生记录、operation 和 Outbox 事件,返回 `202` 及任务摘要。采集候选图先按需转存为正式素材。任务成功后返回独立 output asset;原图和其他 SKU 的选择均不改变。用户在图片弹窗确认后,将 output asset 写入下一次 `editor-draft.media_asset_ids` 来替换原图位置;同一弹窗内可撤销为原图。首期不在发布时自动添加水印。
`GET /api/media-derivatives/{derivative_id}` 返回任务状态、脱敏错误及成功时的输出素材短时预览;不能泄露 `object_key`。
`GET /api/media-watermark-derivatives/{derivative_id}` 返回 `id`、`status`、`source_asset_id`、`watermark_template_id`、`watermark_template_version`、`output_asset` 和脱敏 `error_code`。成功时 `output_asset` 包含正式素材 ID、宽高、类型、大小及短时预览 URL;不能泄露 `object_key`。输出保留原图显示方向与宽高,合成水印后编码为 JPG;透明原图以白色合成。模板的文字大小、图片像素尺寸、不透明度、圆角和相对位置以固化版本为准。失败后原图留在弹窗中。
## 6. AI 填报任务与页面回填