# AI 重新修图接口优化需求 ## 1. 背景 图片预览页面支持用户在“原图”Tab 上重新选择修图模板。 用户可能只生成过一种关联图片,例如之前仅生成了“原图精修”,尚未生成“氛围感”图片。此时用户重新修图时,可以同时选择: - 原图精修模板 - 氛围感修图模板 客户端会调用现有 `ai-reretouch` 接口,并通过 `type = 3` 提交两个模板,希望一次创建两个修图输出任务。 ## 2. 问题复现 ### 前置状态 - 原图素材 ID:`702` - 原 AI 修图批次 ID:`61` - 已存在并完成:原图精修 - 从未生成:氛围感图片 ### 请求 ```http POST /api/yf-handset-app/photog/travel-album/ai-reretouch ``` ```json { "ai_retouch_batch_id": 61, "id": 702, "type": 3, "refined_template_id": 2, "atmosphere_template_id": 14 } ``` 字段含义: | 字段 | 值 | 说明 | | --- | ---: | --- | | `ai_retouch_batch_id` | `61` | 原 AI 修图批次 ID | | `id` | `702` | 原图素材 ID | | `type` | `3` | 同时处理精修和氛围感 | | `refined_template_id` | `2` | 本次选择的精修模板 | | `atmosphere_template_id` | `14` | 本次选择的氛围感模板 | ### 当前响应 ```json { "code": 100099, "data": [], "msg": "仅已完成修图的照片可重新修图", "time": "2026-08-14 17:41:17" } ``` ## 3. 当前问题 从响应判断,后端可能在 `type = 3` 时要求精修图和氛围感图都已经存在且完成,才允许重新修图。 但本场景中: - 精修图已经存在,本次需要重新生成,并在成功后覆盖旧精修图。 - 氛围感图从未生成,本次需要创建新的关联图片。 由于氛围感图历史上不存在,接口拒绝了整个请求,导致客户端无法通过一次请求同时完成“覆盖已有精修图”和“新增氛围感图”。 客户端当前请求已经正确传递 `type = 3` 和两个模板 ID,无需修改请求结构。 ## 4. 期望接口行为 建议将 `ai-reretouch` 调整为按输出类型分别执行的 **upsert(存在则覆盖,不存在则新增)** 语义。 对于本次请求: ### 原图精修 - 请求传入了 `refined_template_id`。 - 旧精修图已经存在。 - 后端创建新的精修任务。 - 新任务成功后覆盖旧精修图。 - 新任务处理中或失败时保留旧精修图。 ### 氛围感修图 - 请求传入了 `atmosphere_template_id`。 - 旧氛围感图不存在。 - 后端创建新的氛围感任务。 - 任务成功后,将结果保存为原图的新关联图片。 ### 整体任务 - 一次请求创建两个输出任务:`refined` 和 `atmosphere`。 - 按两个实际创建的输出预占 2 张修图额度。 - 两个输出分别执行、分别记录状态、分别结算额度。 - 单个输出失败不应影响另一个已经成功的输出。 ## 5. 建议的后端处理规则 ### 5.1 基础校验 建议校验: - 原图素材存在。 - 原图素材属于指定相册或批次。 - 原图当前状态允许提交 AI 修图任务。 - 请求中至少传入一个与 `type` 匹配的有效模板 ID。 - 用户剩余额度满足本次实际创建的输出数量。 不建议校验: - `type = 3` 时要求精修和氛围感历史结果都必须存在。 - 某一种关联图从未生成时直接拒绝整个请求。 ### 5.2 按模板字段创建任务 后端根据本次实际传入的模板字段创建对应任务: | 请求情况 | 期望处理 | | --- | --- | | 只传 `refined_template_id` | 只创建精修任务 | | 只传 `atmosphere_template_id` | 只创建氛围感任务 | | 两个模板 ID 都传 | 同时创建精修和氛围感任务 | | 某个模板 ID 未传 | 不处理、不覆盖该类型的已有结果 | ### 5.3 按输出类型处理已有结果 对于每个实际提交的输出类型,分别判断: | 历史状态 | 建议处理 | | --- | --- | | 已存在成功结果 | 创建重修任务,新结果成功后替换旧结果 | | 从未生成 | 创建新任务,成功后新增关联图片 | | 历史任务失败或已取消 | 允许重新创建任务 | | 当前已有处理中或排队任务 | 拒绝该类型重复提交,或通过幂等机制复用已有任务 | ### 5.4 替换时机 继续采用现有 `replace_on_success` 语义: - 新结果成功后,才替换同类型旧结果。 - 新任务排队、处理中或失败时,保留旧结果。 - 精修和氛围感分别替换,互不影响。 ## 6. `type` 参数建议语义 保持现有请求类型和接口路径不变: | `type` | 语义 | 模板规则 | | ---: | --- | --- | | `1` | 只处理精修 | 必须传 `refined_template_id`,忽略氛围感模板 | | `2` | 只处理氛围感 | 必须传 `atmosphere_template_id`,忽略精修模板 | | `3` | 同时处理精修和氛围感 | 根据实际传入的两个模板分别创建任务 | 对于 `type = 3`: - 必须传 `refined_template_id`。 - `atmosphere_template_id` 可以选传。 - 传入氛围感模板时创建氛围感任务。 - 未传氛围感模板时,只重修精修图,并保留已有氛围感图。 ## 7. 建议处理伪代码 ```text validateOriginalMaterial(request.id) validateBatch(request.ai_retouch_batch_id) outputs = [] if request.refined_template_id is not null: outputs.add( createTask( outputType = refined, templateId = request.refined_template_id, mode = existingRefinedResult ? replace_on_success : create ) ) if request.atmosphere_template_id is not null: outputs.add( createTask( outputType = atmosphere, templateId = request.atmosphere_template_id, mode = existingAtmosphereResult ? replace_on_success : create ) ) if outputs is empty: return invalid_template_error reserveQuota(outputs.count) submitTasks(outputs) ``` 注意:实际是否存在旧结果,应按照各自的 `output_type` 独立查询,不能用“两个类型都已完成”作为 `type = 3` 的整体前置条件。 ## 8. 期望响应 接口成功响应建议继续返回任务批次信息,并明确本次创建的输出类型。例如: ```json { "code": 100000, "data": { "ai_retouch_batch_id": 61, "source_material_id": 702, "outputs": [ { "type": "refined", "mode": "replace_on_success", "status": "queued" }, { "type": "atmosphere", "mode": "create", "status": "queued" } ], "reserved_units": 2 }, "msg": "AI修图任务已提交" } ``` 响应结构可沿用现有实现,不强制增加上述字段;关键要求是一次请求能够成功创建两个对应的修图任务。 ## 9. 验收用例 ### 用例一:只有旧精修图,同时提交两个模板 - 历史状态:精修已完成,氛围感不存在。 - 请求:`type = 3`,同时传精修和氛围感模板。 - 期望:创建两个任务;精修成功后覆盖,氛围感成功后新增。 ### 用例二:只有旧氛围感图,同时提交两个模板 - 历史状态:精修不存在,氛围感已完成。 - 请求:`type = 3`,同时传精修和氛围感模板。 - 期望:创建两个任务;精修成功后新增,氛围感成功后覆盖。 ### 用例三:两种旧结果都存在 - 历史状态:精修、氛围感都已完成。 - 请求:`type = 3`,同时传两个模板。 - 期望:创建两个任务,分别在成功后覆盖各自旧结果。 ### 用例四:原图 Tab 只选择精修模板 - 历史状态:精修、氛围感均可能存在。 - 请求:`type = 3`,只传 `refined_template_id`。 - 期望:只创建精修任务;不处理、不删除、不覆盖旧氛围感图;预占 1 张额度。 ### 用例五:精修后 Tab 重新修图 - 请求:`type = 1`,只传 `refined_template_id`。 - 期望:只创建精修任务,新精修成功后覆盖旧精修图。 ### 用例六:氛围感 Tab 重新修图 - 请求:`type = 2`,只传 `atmosphere_template_id`。 - 期望:只创建氛围感任务,新氛围感成功后覆盖旧氛围感图。 ### 用例七:其中一个输出失败 - 请求创建精修和氛围感两个任务。 - 精修成功,氛围感失败。 - 期望:保留成功的新精修结果;氛围感旧图存在时继续保留旧图,不存在时不生成关联图;额度分别结算。 ### 用例八:防止重复提交 - 同一原图、同一输出类型已经存在排队中或处理中的任务。 - 用户再次提交相同任务。 - 期望:通过幂等机制返回原任务,或仅拒绝重复的输出类型,避免重复扣减额度和重复生成结果。 ## 10. 总结 希望 `ai-reretouch` 支持以下统一语义: > 对本次传入模板对应的每种输出独立处理:已有结果则在新任务成功后覆盖,没有结果则新增;未传模板的类型不处理。不要因为某一种关联图历史上从未生成,就拒绝整个重新修图请求。