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
+44 -10
View File
@@ -69,7 +69,7 @@ Headers:`Idempotency-Key` 必填。
`object_key` 是服务端内部字段,不放在普通客户端响应。上传意图和完成状态均持久化在数据库,不能只存在于 Redis 或预签名 token 中;Worker 每 5 分钟将逾期未完成意图标记为 `expired`,并删除 `failed` / `expired` 意图的临时对象。删除成功才记录清理时间;失败会在下一轮重试。
### 1.3 临时预览 URL
### 1.3 临时预览与下载 URL
`POST /api/media-assets/{asset_id}/access-urls`
@@ -77,7 +77,7 @@ Headers:`Idempotency-Key` 必填。
{ "purpose": "preview" }
```
返回 10 分钟有效的短时 `url` 与 `expires_at`。仅当前工作空间的素材可签发;前端在 URL 过期后重新请求,不能把 URL 写回业务表或长期缓存。
`purpose` 可为 `preview` 或 `download`。下载时传 `{ "purpose": "download" }`,服务端在签名 GET 请求中指定 `Content-Disposition: attachment`,文件名为 `atlas-image-{asset_id}` 加素材类型对应扩展名,浏览器打开该短时地址时保存文件。两种用途都返回 10 分钟有效的 `url` 与 `expires_at`。仅当前工作空间的素材可签发;前端在 URL 过期后重新请求,不能把 URL 写回业务表或长期缓存。
`GET /api/media-assets/product-uploads` 返回当前工作空间最近 100 个已经完成校验的 `product_image` 素材。每项包含 `MediaAsset` 字段及 10 分钟有效的 `preview_url`、`preview_url_expires_at`,供编辑页“上传图片”候选区展示;该地址不得写入草稿。
@@ -87,6 +87,18 @@ Headers:`Idempotency-Key` 必填。
`GET /api/media-assets/product-imports` 返回当前工作空间最近 100 条已导入的网络图片,响应项同上。图片已生成正式素材 ID 后才可进入 SKU 草稿;网络来源地址和预览地址均不得写入 `media_asset_ids`。
### 1.5 商品图片一键 3:4
`POST /api/media-assets/{asset_id}/aspect-ratio-derivatives`,需 `Idempotency-Key`,请求 `{ "preset": "3:4", "fit": "contain", "product_id": "uuid" }`。`product_id` 在商品编辑器中传入,以便将结果归档到该商品;旧调用可省略。`asset_id` 必须是当前工作空间已校验的 JPG/PNG/WebP 正式素材。采集候选图先按需转存,再以正式素材 ID 请求。服务端在同一事务创建工作空间隔离的派生记录、用户可见 `operation` 和 Outbox 事件,返回 HTTP `202`:
```json
{ "data": { "id": "uuid", "status": "queued", "source_asset_id": "uuid", "output_asset": null, "error_code": null } }
```
`GET /api/media-derivatives/{derivative_id}` 返回相同结构,`status` 为 `queued/running/succeeded/failed`。成功时 `output_asset` 为独立正式素材,包含 `id`、宽高、类型、大小以及 10 分钟有效的 `preview_url` 和 `preview_url_expires_at`;失败时只返回脱敏 `error_code`。任务等比缩放原图;留白贴近图片的部分采用对应边缘像素,向外平滑过渡到该边缘窄带的平均色,避免细纹理被拉成贯穿留白的条纹。透明边缘先与白色合成。输出为 1200×1600 JPG,不裁切商品,原图不修改。相同幂等键重放同一任务,Worker 重试必须复用同一派生记录。
单张或批量操作都逐张创建任务。弹窗用成功的 output asset 替换当前已选位置;未选的来源图处理成功后加入已选区。失败的图保持原样。弹窗「确认」只把新素材 ID 写入当前编辑表单,页面「保存」才持久化 SKU 选图。短时预览 URL、存储路径和远程来源 URL 不写入草稿或 Outbox。
## 2. 可用字体
`GET /api/watermark-fonts`
@@ -108,11 +120,16 @@ Headers:`Idempotency-Key` 必填。
"id": "uuid",
"name": "右下角品牌",
"kind": "image",
"is_default": false,
"current_version": 3,
"config": {
"image_asset": { "id": "uuid", "preview_url": "https://short-lived", "preview_url_expires_at": "2026-09-02T08:15:00Z" },
"opacity": "0.8000",
"scale_ratio": "0.2500",
"scale_ratio": "0.5556",
"image_size_px": 80,
"border_radius_px": 0,
"position_x": 1,
"position_y": 1,
"position": "bottom_right"
},
"created_at": "2026-09-02T08:00:00Z",
@@ -129,11 +146,13 @@ Headers:`Idempotency-Key` 必填。
"font_code": "NotoSans",
"font_size_px": 24,
"opacity": "0.8000",
"position_x": 1,
"position_y": 1,
"position": "bottom_right"
}
```
`position` 只能是 `top_left`、`top_center`、`top_right`、`middle_left`、`center`、`middle_right`、`bottom_left`、`bottom_center`、`bottom_right`。`opacity` 范围 `"0"`–`"1"`;图片 `scale_ratio` 范围 `"0.1"`–`"2"`;文字 `font_size_px` 范围 12–60。
`position_x`、`position_y` 是 0–1 的数字,分别表示水印在水平、垂直**可移动范围**内的相对位置:0 贴左/上边缘,1 贴右/下边缘,中间值不吸附九宫格;坐标保存到小数点后四位。`position` 是兼容旧版客户端的九宫格近似值,不得用于精确渲染;其枚举值仍为 `top_left`、`top_center`、`top_right`、`middle_left`、`center`、`middle_right`、`bottom_left`、`bottom_center`、`bottom_right`。`opacity` 范围 `"0"`–`"1"`;图片 `image_size_px` 是在 240×320 基准预览框中等比缩放后的最长边像素数,范围 10–200,默认 80;`border_radius_px` 是该预览框中的圆角半径,范围 0–100,默认 0。`scale_ratio` 仅用于旧客户端兼容,范围 `"0.1"`–`"2"`,新客户端以 `image_size_px` 为准;10–14px 时兼容比例固定为最小值 `"0.1"`,实际渲染仍以像素尺寸为准。文字 `font_size_px` 范围 12–60,也以基准预览框为单位。实际渲染时,Worker 用 `max(源图显示宽度/240, 源图显示高度/320)` 同比例放大图片大小、文字大小、圆角和 5px 边距;水印超出源图时再等比缩小到可容纳范围。这样商品图缩小到预览尺度后,水印大小与底边距接近模板预览。
### 3.1 查询
@@ -145,6 +164,10 @@ Headers:`Idempotency-Key` 必填。
返回模板当前版本,供编辑弹窗和“查看效果”使用。固定演示底图由前端内置,前端按 `config` 实时渲染预览,不在每次滑动 Slider 时创建服务端图片。
`GET /api/watermark-templates/default`
返回 `{ "data": 模板资源 }`;当前工作空间未设置默认模板时返回 `{ "data": null }`。列表和详情中的 `is_default` 表示当前工作空间的唯一默认水印。打开「SKU 图片选择」弹窗时客户端静默读取一次默认模板;点击单张或批量「添加水印」时,有默认模板便直接创建派生任务,没有默认模板才打开三列模板选择弹窗。查询尚未完成时等待结果再决定,查询失败时允许用户从选择弹窗手动选择。设置默认不会自动修改已有图片、店铺推荐水印或已生成的派生图。
### 3.2 创建
`POST /api/watermark-templates`
@@ -162,7 +185,8 @@ Headers:`Idempotency-Key` 必填。
"font_code": "NotoSans",
"font_size_px": 24,
"opacity": "0.8",
"position": "bottom_right"
"position_x": 0.73,
"position_y": 0.82
}
}
```
@@ -175,20 +199,22 @@ Headers:`Idempotency-Key` 必填。
"kind": "image",
"config": {
"image_asset_id": "uuid",
"scale_ratio": "0.25",
"image_size_px": 80,
"border_radius_px": 12,
"opacity": "0.8",
"position": "bottom_right"
"position_x": 0.73,
"position_y": 0.82
}
}
```
模板名称在当前工作空间内唯一;图片资产必须属于当前工作空间且已完成校验。成功返回 `201 Created` 与模板资源。
创建时须同时提交 `position_x` 和 `position_y`;旧版客户端可继续只提交 `position`,服务端会换算成 0、0.5 或 1 的坐标。若两种格式都提交,以精确坐标为准。图片水印须提交 `image_size_px`;旧版客户端可继续提交 `scale_ratio`,服务端会换算为像素尺寸。若两者都提交,以像素尺寸为准;未提交 `border_radius_px` 时取 0。模板名称在当前工作空间内唯一;图片资产必须属于当前工作空间且已完成校验。成功返回 `201 Created` 与模板资源。
### 3.3 编辑与删除
`PATCH /api/watermark-templates/{template_id}`
Headers:`If-Match` 与 `Idempotency-Key` 必填。请求体与创建相同,但可只提交待改字段;若切换 `kind`,须提交新类型完整 `config`,服务端不保留旧类型专属字段。
Headers:`If-Match` 与 `Idempotency-Key` 必填。请求体与创建相同,但可只提交待改字段;坐标可单独修改一个轴,另一轴沿用当前值。旧版客户端只提交 `position` 时,两个坐标按九宫格同步换算。图片尺寸和圆角也可独立修改;仅提交旧字段 `scale_ratio` 时重新换算像素尺寸。若切换 `kind`,须提交新类型完整 `config`,服务端不保留旧类型专属字段。
每次影响渲染的更新都会创建新的 `watermark_template_version`,并增加模板 `current_version`。旧版本不会被覆盖,供已生成派生图追溯;模板改名只更新模板名称,不需要创建渲染版本。
@@ -196,6 +222,14 @@ Headers:`If-Match` 与 `Idempotency-Key` 必填。请求体与创建相同,
Headers:`Idempotency-Key` 必填。服务端在同一事务软删除模板并解除所有当前店铺绑定,返回 `204 No Content`。不删除原始图片资产,也不影响已生成水印派生图和历史发布快照。
`PUT /api/watermark-templates/{template_id}/default`
Headers:`Idempotency-Key` 必填;请求体为 `{}`。将当前工作空间的活动模板设为唯一默认模板,原默认模板同时取消默认状态,返回 `200 OK` 与更新后的模板资源。重复设置同一模板成功且不会创建新的渲染版本。模板不存在或不属于当前工作空间时返回 `404 RESOURCE_NOT_FOUND`。删除默认模板后,工作空间不再有默认模板。
## 4. 与商品编辑的边界
本文件只定义水印模板和通用素材。商品编辑的“图片悬浮 → 编辑 → 添加水印”将在商品接口中定义:它选择这里的活动模板、创建持久化派生任务、固化模板版本,并将生成的独立素材替换当前 SKU 已选图片位置;不会在上架时临时加水印。
商品编辑的“图片悬浮 → 编辑 → 添加水印”使用 `POST /api/media-assets/{asset_id}/watermark-derivatives` 创建异步派生任务,需 `Idempotency-Key`,请求体为 `{ "watermark_template_id": "uuid", "product_id": "uuid" }`。`product_id` 在商品编辑器中传入,旧调用可省略。仅接受当前工作空间内已校验、最大 20 MiB、最大边长 10,000px 的 JPG/PNG/WebP 正式素材和活动模板。创建时固定模板当前版本,在同一事务写入派生任务、operation 和 Outbox,返回 HTTP `202`;`GET /api/media-watermark-derivatives/{derivative_id}` 查询进度。响应包含 `id`、`status`、`source_asset_id`、`watermark_template_id`、`watermark_template_version`、`output_asset` 和 `error_code`。成功时结果为保留源图显示方向及尺寸的独立 JPG 素材,透明源图用白底合成,返回短时预览地址;失败只返回稳定错误码。
单张和批量处理逐张创建任务。成功结果在图片弹窗中替换对应位置;来源候选图成功后加入已选区,失败图保持原样。弹窗可撤销本次水印替换;确认后才写入当前编辑表单,页面保存后持久化 SKU 选图。上架任务不会临时加水印。
`GET /api/products/{product_id}/image-derivatives` 返回当前工作空间内该商品最近 100 条成功的 3:4 与水印处理记录,按处理时间倒序;商品不存在返回 `404`。每条记录包含 `id`、`kind`(`aspect_ratio_3_4` / `watermark`)、`source_asset_id`、`created_at` 和带短时预览地址的 `output_asset`。编辑页选图弹窗的「图片处理记录」Tab 用它找回结果,选用后仍需确认选图并保存页面。未绑定商品的历史任务无法确定商品归属,不进入该列表;来源图与原素材不被覆盖。