532 lines
16 KiB
Markdown
532 lines
16 KiB
Markdown
# AI 修图接口文档
|
||
|
||
## 接口列表
|
||
|
||
| # | 方法 | 路径 | 用途 |
|
||
|---|---|---|---|
|
||
| 1 | `GET` | `/api/yf-handset-app/photog/travel-album/material-list` | 获取相册网格素材和预览初始数据 |
|
||
| 2 | `GET` | `/api/yf-handset-app/photog/travel-album/material-info` | 获取/刷新当前原图及关联图 |
|
||
| 3 | `GET` | `/api/yf-handset-app/photog/travel-album/ai-retouch-options` | 获取 AI 模板、选择规则和剩余额度 |
|
||
| 4 | `POST` | `/api/yf-handset-app/photog/travel-album/ai-retouch` | 提交首次修图或重新修图任务 |
|
||
| 5 | `GET` | `/api/yf-handset-app/photog/travel-album/ai-retouch-job-info` | 查询 AI 修图任务进度和结果 |
|
||
| 6 | `POST` | `/api/yf-handset-app/photog/travel-album/delete-material` | 单张/批量删除素材及关联图 |
|
||
|
||
## 接口详情
|
||
|
||
### 获取相册素材列表
|
||
|
||
```http
|
||
GET /api/yf-handset-app/photog/travel-album/material-list?user_equity_travel_id=88&limit=30&cursor=opaque_cursor&sort=created_at_desc
|
||
```
|
||
|
||
| 参数 | 必填 | 说明 |
|
||
|---|---|---|
|
||
| `user_equity_travel_id` | 是 | 相册 ID |
|
||
| `limit` | 否 | 默认 30,最大 100 |
|
||
| `cursor` | 否 | 服务端返回的不透明游标,首页不传 |
|
||
| `sort` | 否 | `created_at_desc` 或 `created_at_asc` |
|
||
| `material_type` | 否 | `original`、`cover`;不传表示全部 |
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 100000,
|
||
"msg": "success",
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": "2031",
|
||
"user_equity_travel_id": "88",
|
||
"material_type": "original",
|
||
"original_asset": {
|
||
"id": "asset_8001",
|
||
"url": "https://cdn.example.com/albums/88/IMG_1024.jpg",
|
||
"thumbnail_url": "https://cdn.example.com/albums/88/IMG_1024_thumb.jpg",
|
||
"file_name": "IMG_1024.jpg",
|
||
"mime_type": "image/jpeg",
|
||
"file_size": 4821931,
|
||
"width": 4032,
|
||
"height": 3024,
|
||
"version": 1,
|
||
"created_at": "2026-08-09T01:00:00.000Z"
|
||
},
|
||
"display_status": "uploaded",
|
||
"variant_slots": [],
|
||
"revision": 7,
|
||
"created_at": "2026-08-09T01:00:00.000Z",
|
||
"updated_at": "2026-08-11T07:30:18.000Z"
|
||
}
|
||
],
|
||
"next_cursor": "next_opaque_cursor",
|
||
"has_more": true
|
||
}
|
||
}
|
||
```
|
||
|
||
- 只返回网格顶层素材:原图和独立封面。
|
||
- 原图如存在关联图,`variant_slots` 包含预览页 Tab 初始数据。
|
||
- 建议使用游标分页,因为 AI 任务完成时可能实时插入封面素材。
|
||
|
||
### 获取/刷新单个原图项目
|
||
|
||
```http
|
||
GET /api/yf-handset-app/photog/travel-album/material-info?user_equity_travel_id=88&material_id=2031
|
||
```
|
||
|
||
查询参数 `user_equity_travel_id` 和 `material_id` 均必填。响应的 `data` 直接返完整 `material_project`,字段与列表 Item 完全一致。该接口供预览页刷新和提交后定向更新使用。
|
||
|
||
如 `material_id` 已删除,返回 `404 MATERIAL_NOT_FOUND`。
|
||
|
||
### 获取 AI 修图选项
|
||
|
||
```http
|
||
GET /api/yf-handset-app/photog/travel-album/ai-retouch-options?user_equity_travel_id=88&scope=batch&source_count=4
|
||
```
|
||
|
||
| `scope` | 使用场景 |
|
||
|---|---|
|
||
| `batch` | 网格多选 AI 修图 |
|
||
| `all_variants` | 预览页无 Tab,或当前是原图 Tab |
|
||
| `refined_only` | 当前是精修后 Tab |
|
||
| `atmosphere_only` | 当前是氛围感 Tab |
|
||
|
||
`user_equity_travel_id`、`scope` 和 `source_count` 均必填。`source_count` 用于计算封面模板是否显示和必选。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 100000,
|
||
"msg": "success",
|
||
"data": {
|
||
"scope": "batch",
|
||
"source_count": 4,
|
||
"groups": [
|
||
{
|
||
"type": "refined",
|
||
"title": "原图精修",
|
||
"required": true,
|
||
"selection_mode": "single",
|
||
"quota_cost": {
|
||
"mode": "per_source",
|
||
"units": 1
|
||
},
|
||
"templates": [
|
||
{
|
||
"id": "tpl_refined_12",
|
||
"name": "清透精修",
|
||
"preview_url": "https://cdn.example.com/templates/refined_12.jpg",
|
||
"before_url": "https://cdn.example.com/templates/refined_12_before.jpg",
|
||
"after_url": "https://cdn.example.com/templates/refined_12_after.jpg",
|
||
"enabled": true
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"type": "atmosphere",
|
||
"title": "氛围感修图",
|
||
"required": false,
|
||
"selection_mode": "single",
|
||
"quota_cost": {
|
||
"mode": "per_source",
|
||
"units": 1
|
||
},
|
||
"templates": [
|
||
{
|
||
"id": "tpl_atmosphere_06",
|
||
"name": "暖阳",
|
||
"preview_url": "https://cdn.example.com/templates/atmosphere_06.jpg",
|
||
"before_url": "https://cdn.example.com/templates/atmosphere_06_before.jpg",
|
||
"after_url": "https://cdn.example.com/templates/atmosphere_06_after.jpg",
|
||
"enabled": true
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"type": "cover",
|
||
"title": "封面风格模板",
|
||
"required": true,
|
||
"selection_mode": "single",
|
||
"quota_cost": {
|
||
"mode": "per_job",
|
||
"units": 0
|
||
},
|
||
"templates": [
|
||
{
|
||
"id": "tpl_cover_03",
|
||
"name": "旅行画册",
|
||
"preview_url": "https://cdn.example.com/templates/cover_03.jpg",
|
||
"before_url": "https://cdn.example.com/templates/cover_03_before.jpg",
|
||
"after_url": "https://cdn.example.com/templates/cover_03_after.jpg",
|
||
"enabled": true
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"quota": {
|
||
"unit": "retouch_time",
|
||
"remaining": 12,
|
||
"reserved": 3,
|
||
"display_text": "剩余 12 次",
|
||
"updated_at": "2026-08-11T07:29:58.000Z"
|
||
},
|
||
"limits": {
|
||
"minimum_source_count": 1,
|
||
"maximum_source_count": 50,
|
||
"cover_minimum_source_count": 4
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- 不再让 App 传 `scenic_id`,后端从相册归属景区获取可用模板。
|
||
- 由后端返回 `required` 和可见分组,避免多端各写一套“4 张显示封面”规则。
|
||
- `preview_url` 用于模板卡片缩略图;`before_url` 与 `after_url` 必须是同尺寸、同构图的配对图片,供客户端滑动对比。
|
||
- 客户端可默认选中必选分组的第一个可用模板,可选分组默认不选。
|
||
- 此接口用于 UI 配置;提交时后端仍必须根据真实素材 ID 重新校验。
|
||
- 同一响应返回当前用户的剩余可用额度和每类输出的额度单价,弹窗无需再发起第二个额度请求。
|
||
- `quota.remaining` 是扣除其他未完成任务已预占数量后、当前立即可用的额度;弹窗底部使用此值显示“剩余 N 次”。
|
||
- 额度文案位于模板选择弹窗的固定底部区域,不随中间模板列表滚动。
|
||
- `quota.reserved` 是当前用户其他未完成任务的预占额度,仅用于解释账户状态。
|
||
- 客户端预估本次消耗时,对已选输出求和:`per_source` 类型为 `source_count * units`,`per_job` 类型为 `units`。封面始终为 0。
|
||
- 本文档默认精修和氛围感每生成一张各消耗 1 次。如实际商业规则变更,后端只需调整 `quota_cost`,客户端不需改接口。
|
||
|
||
### 创建 AI 修图任务
|
||
|
||
```http
|
||
POST /api/yf-handset-app/photog/travel-album/ai-retouch
|
||
Idempotency-Key: 9A7820CE-78AB-45C6-9E0F-5DF268995379
|
||
Content-Type: application/json
|
||
```
|
||
|
||
#### 5.4.1 网格批量修图
|
||
|
||
```json
|
||
{
|
||
"user_equity_travel_id": "88",
|
||
"scope": "batch",
|
||
"source_material_ids": ["2031", "2032", "2033", "2034"],
|
||
"outputs": [
|
||
{
|
||
"type": "refined",
|
||
"template_id": "tpl_refined_12"
|
||
},
|
||
{
|
||
"type": "atmosphere",
|
||
"template_id": "tpl_atmosphere_06"
|
||
},
|
||
{
|
||
"type": "cover",
|
||
"template_id": "tpl_cover_03"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.4.2 预览页原图 Tab
|
||
|
||
```json
|
||
{
|
||
"user_equity_travel_id": "88",
|
||
"scope": "all_variants",
|
||
"source_material_ids": ["2031"],
|
||
"outputs": [
|
||
{
|
||
"type": "refined",
|
||
"template_id": "tpl_refined_15"
|
||
},
|
||
{
|
||
"type": "atmosphere",
|
||
"template_id": "tpl_atmosphere_09"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
如用户未选氛围感模板,`outputs` 中不传 `atmosphere`。缺失表示“本次不处理”,不表示删除现有氛围感图。
|
||
|
||
#### 5.4.3 只重新精修
|
||
|
||
```json
|
||
{
|
||
"user_equity_travel_id": "88",
|
||
"scope": "refined_only",
|
||
"source_material_ids": ["2031"],
|
||
"outputs": [
|
||
{
|
||
"type": "refined",
|
||
"template_id": "tpl_refined_18"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.4.4 只重新生成氛围感
|
||
|
||
```json
|
||
{
|
||
"user_equity_travel_id": "88",
|
||
"scope": "atmosphere_only",
|
||
"source_material_ids": ["2031"],
|
||
"outputs": [
|
||
{
|
||
"type": "atmosphere",
|
||
"template_id": "tpl_atmosphere_11"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.4.5 提交校验矩阵
|
||
|
||
| `scope` | 原图数 | 精修 | 氛围感 | 封面 |
|
||
|---|---:|---|---|---|
|
||
| `batch` | 1 至 50 | 必选 | 可选 | 少于 4 张禁止;4 张及以上必选 |
|
||
| `all_variants` | 必须为 1 | 必选 | 可选 | 禁止 |
|
||
| `refined_only` | 必须为 1 | 必选 | 禁止 | 禁止 |
|
||
| `atmosphere_only` | 必须为 1 | 禁止 | 必选 | 禁止 |
|
||
|
||
另外:
|
||
|
||
- `source_material_ids` 必须去重,且全部为 `original` 类型。
|
||
- 模板必须启用、类型匹配,且适用于相册所属景区。
|
||
- 后端必须使用提交时的最新额度计费规则重新计算消耗,不信任客户端的预估数值。
|
||
- 封面输出不论首次生成、重试或任务结果如何,额度消耗始终为 0。
|
||
- 每个输出槽位均采用 `replace_on_success`:成功后替换旧版本,失败保留旧版本。
|
||
- 批量选中已有 AI 结果的原图时,允许重新生成,只覆盖本次 `outputs` 列出的类型。
|
||
- 同一原图、同一槽位已有 `queued/processing` 任务时,整次请求返回 `409 RETOUCH_TARGET_BUSY`,并列出冲突项。
|
||
|
||
#### 5.4.6 提交响应
|
||
|
||
HTTP 状态码:`202 Accepted`
|
||
|
||
```json
|
||
{
|
||
"code": 100000,
|
||
"msg": "AI 修图任务已提交",
|
||
"data": {
|
||
"job": {
|
||
"id": "job_01K2E4",
|
||
"user_equity_travel_id": "88",
|
||
"scope": "all_variants",
|
||
"status": "queued",
|
||
"source_material_ids": ["2031"],
|
||
"progress": {
|
||
"total": 2,
|
||
"queued": 2,
|
||
"processing": 0,
|
||
"succeeded": 0,
|
||
"failed": 0
|
||
},
|
||
"created_at": "2026-08-11T07:30:15.123Z",
|
||
"started_at": null,
|
||
"finished_at": null
|
||
},
|
||
"quota_reservation": {
|
||
"before_remaining": 12,
|
||
"reserved_units": 2,
|
||
"remaining_after_reservation": 10,
|
||
"cover_units": 0
|
||
},
|
||
"affected_projects": [
|
||
{
|
||
"id": "2031",
|
||
"user_equity_travel_id": "88",
|
||
"material_type": "original",
|
||
"original_asset": {
|
||
"id": "asset_8001",
|
||
"url": "https://cdn.example.com/albums/88/IMG_1024.jpg",
|
||
"thumbnail_url": "https://cdn.example.com/albums/88/IMG_1024_thumb.jpg",
|
||
"file_name": "IMG_1024.jpg",
|
||
"mime_type": "image/jpeg",
|
||
"file_size": 4821931,
|
||
"width": 4032,
|
||
"height": 3024,
|
||
"version": 1,
|
||
"created_at": "2026-08-09T01:00:00.000Z"
|
||
},
|
||
"display_status": "pending",
|
||
"variant_slots": [
|
||
{
|
||
"type": "refined",
|
||
"asset": null,
|
||
"latest_generation": {
|
||
"job_id": "job_01K2E4",
|
||
"status": "queued",
|
||
"is_replacement": false,
|
||
"template": {
|
||
"id": "tpl_refined_15",
|
||
"name": "自然精修"
|
||
},
|
||
"requested_at": "2026-08-11T07:30:15.123Z",
|
||
"started_at": null,
|
||
"finished_at": null,
|
||
"error": null
|
||
}
|
||
},
|
||
{
|
||
"type": "atmosphere",
|
||
"asset": null,
|
||
"latest_generation": {
|
||
"job_id": "job_01K2E4",
|
||
"status": "queued",
|
||
"is_replacement": false,
|
||
"template": {
|
||
"id": "tpl_atmosphere_09",
|
||
"name": "暖阳"
|
||
},
|
||
"requested_at": "2026-08-11T07:30:15.123Z",
|
||
"started_at": null,
|
||
"finished_at": null,
|
||
"error": null
|
||
}
|
||
}
|
||
],
|
||
"revision": 8,
|
||
"created_at": "2026-08-09T01:00:00.000Z",
|
||
"updated_at": "2026-08-11T07:30:15.123Z"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
必须返回 `job.id`、`quota_reservation` 和更新后的 `affected_projects`。客户端用 `remaining_after_reservation` 立即更新剩余额度,并更新 Cell 状态;已有 `asset` 的重修 Tab 可同步显示处理态,首次生成且 `asset = null` 的槽位不显示 Tab。
|
||
|
||
### 查询 AI 修图任务
|
||
|
||
```http
|
||
GET /api/yf-handset-app/photog/travel-album/ai-retouch-job-info?user_equity_travel_id=88&job_id=job_01K2E4
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 100000,
|
||
"msg": "success",
|
||
"data": {
|
||
"id": "job_01K2E4",
|
||
"user_equity_travel_id": "88",
|
||
"scope": "batch",
|
||
"status": "partially_succeeded",
|
||
"source_material_ids": ["2031", "2032", "2033", "2034"],
|
||
"progress": {
|
||
"total": 9,
|
||
"queued": 0,
|
||
"processing": 0,
|
||
"succeeded": 8,
|
||
"failed": 1
|
||
},
|
||
"quota_settlement": {
|
||
"status": "settled",
|
||
"reserved_units": 8,
|
||
"consumed_units": 7,
|
||
"released_units": 1,
|
||
"cover_units": 0
|
||
},
|
||
"targets": [
|
||
{
|
||
"source_material_id": "2031",
|
||
"output_type": "refined",
|
||
"status": "succeeded",
|
||
"result_asset_id": "asset_9201",
|
||
"error": null
|
||
},
|
||
{
|
||
"source_material_id": "2032",
|
||
"output_type": "atmosphere",
|
||
"status": "failed",
|
||
"result_asset_id": null,
|
||
"error": {
|
||
"code": "AI_PROVIDER_TIMEOUT",
|
||
"message": "AI 服务处理超时,请重试",
|
||
"retryable": true
|
||
}
|
||
},
|
||
{
|
||
"source_material_id": null,
|
||
"output_type": "cover",
|
||
"status": "succeeded",
|
||
"result_material_id": "cover_301",
|
||
"error": null
|
||
}
|
||
],
|
||
"created_at": "2026-08-11T07:30:15.123Z",
|
||
"started_at": "2026-08-11T07:30:18.000Z",
|
||
"finished_at": "2026-08-11T07:33:00.000Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
| 整体状态 | 含义 |
|
||
|---|---|
|
||
| `queued` | 已受理,尚无子任务开始 |
|
||
| `processing` | 至少一个子任务正在执行,且尚未全部完成 |
|
||
| `succeeded` | 所有子任务成功 |
|
||
| `partially_succeeded` | 子任务全部结束,且既有成功也有失败 |
|
||
| `failed` | 子任务全部结束,没有任何成功结果 |
|
||
| `canceled` | 任务因素材删除或管理操作被取消 |
|
||
|
||
额度结算规则:
|
||
|
||
- 任务提交成功时先预占所有非封面子任务的额度,封面不进入预占。
|
||
- 每个精修/氛围感子任务成功后,将对应预占转为实际消耗。
|
||
- 子任务失败或取消时释放对应预占。因此部分成功示例中,预占 8 次、成功消耗 7 次、失败退回 1 次。
|
||
- `quota_settlement.status` 取值为 `reserved | partially_settled | settled`。
|
||
|
||
客户端如只关心当前预览图,优先调用单素材接口;本接口主要用于批量进度、问题排查和任务详情。
|
||
|
||
### 批量删除素材
|
||
|
||
```http
|
||
POST /api/yf-handset-app/photog/travel-album/delete-material
|
||
Content-Type: application/json
|
||
```
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"user_equity_travel_id": "88",
|
||
"material_ids": ["2031", "2032", "cover_301"]
|
||
}
|
||
```
|
||
|
||
为兼容旧客户端,原单张请求体继续有效:
|
||
|
||
```json
|
||
{
|
||
"id": "2031"
|
||
}
|
||
```
|
||
|
||
新请求使用 `user_equity_travel_id + material_ids`,旧请求使用 `id`,两组参数不同时传入。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 100000,
|
||
"msg": "删除成功",
|
||
"data": {
|
||
"deleted_material_ids": ["2031", "2032", "cover_301"],
|
||
"deleted_variant_asset_ids": ["asset_9101", "asset_9102"],
|
||
"canceled_job_ids": ["job_01K2E4"],
|
||
"oss_cleanup_status": "scheduled"
|
||
}
|
||
}
|
||
```
|
||
|
||
语义:
|
||
|
||
- 传入原图 ID:级联软删除精修图、氛围感图,并取消该原图的未完成子任务。
|
||
- 传入独立封面 ID:只删除该封面素材。
|
||
- 删除原图不级联删除以它为输入之一的已生成封面。
|
||
- 数据库业务删除采用一个事务,默认全部成功或全部失败。
|
||
- 任一 ID 不属于当前相册或无权删除时,整次请求失败。已删除的 ID 可视为幂等成功。
|
||
- OSS 文件清理异步执行,不影响业务删除成功。
|