记录图片编辑业务ID约束

更新画布快速编辑与红框辅助引用的专题文档
统一 External v1 API 调用方和上传后登记口径
补充业务 ID 窄查询、队列快照复核的决策与踩坑记录
This commit is contained in:
2026-08-07 14:06:09 +08:00
parent cde1202f92
commit 6cee4b8c5c
5 changed files with 20 additions and 11 deletions
@@ -6557,6 +6557,7 @@
- 决策:快速编辑只支持普通静态图片、角色图、规范图、完整图标图集、UI 设计图、宣发图和视频。单个拆分图标、角色动作 / 序列帧、音效与背景音乐不支持;新增媒体或素材类型默认不开放。浮动工具栏、图层右键菜单、独立图片菜单、打开面板入口和提交门禁统一调用同一个正向白名单;后端图片编辑 BFF 基于目标图层的有效素材类型与媒体类型执行同一正向门禁,视频快速编辑只走视频生成接口。
- 边界:角色动作继续通过对应的动作生成链路处理,不再把当前帧当作可快速编辑图片。
- 2026-08-06 修订:画布 Agent 的 `edit_image` 只接受图片输入,新任务以 `assetKind=null` 表示普通静态图片,不再使用 synthetic `editor_agent_edit_image`。worker 仅按服务端生成的 `editor-agent:` dedupe namespace 识别并归一历史排队 payload;普通调用伪造旧值继续被拒绝。已持久化资源中的旧值只有在后端从真实目标图层 / 项目资源解析后才兼容为空类型,避免历史 Agent 结果失去快速编辑能力,同时不扩大请求白名单。
- 2026-08-07 修订:站内与 External v1 图片编辑请求统一只接受必填 `sourceReferenceId`,且该值必须是当前账号已登记的项目资源 ID 或素材 IDobjectKey、URL、Data URL、Blob URL 以及旧 `sourceImageSrc/sourceResourceId/assetKind` 字段全部返回 400,不提供兼容别名。后端用共享窄查询分别按两张表主键定点解析,双表同 ID、未命中、跨账号、对象缺失或越权均失败关闭;权威类型完全来自业务记录,只允许普通静态图片、规范图、角色图、完整图标图集、图标规范、宣发图和 UI 设计图。请求带 `targetLayerId` 时必须同时带 `projectId`,来源与目标优先比较 `assetObjectId`,任一方缺失才比较 canonical `(bucket, objectKey)`,且来源默认类型必须与目标资源默认类型一致;最终类型取目标覆盖值或目标资源类型。HTTP 入队写入版本化服务端解析快照,worker 执行前按同一业务 ID 再次定点解析,身份或类型漂移即失败关闭。旧任务只把已有资源 ID 或旧来源字符串本身当业务 ID 迁移,绝不按 objectKey 反查。Canvas Agent 必须从 `ImageMetadata.reference_id` 取主来源;红框标注上传图只作为辅助 `referenceImageSrcs`,不能冒充被编辑资源。仅以素材 ID 编辑时,队列审计与 `generationInputs.references` 保留素材 ID,不伪造项目资源关系。
- 验证:模型测试覆盖允许与拒绝类型,工具栏和两类右键菜单测试覆盖单个拆分图标、角色动作及音频不展示,提交工作流测试覆盖单个拆分图标和角色动作绕过入口时仍拒绝;后端表驱动测试覆盖全部现役素材 / 媒体类型与未知类型,锁定图片编辑端点失败关闭。
- 关联:`src/components/image-editor/ImageCanvasGenerationModel.ts``ImageCanvasSelectedLayerToolbarView.tsx``ImageCanvasContextMenusView.tsx``useImageCanvasGenerationWorkflow.ts``useImageCanvasGenerationSubmissionWorkflow.ts`
+10 -2
View File
@@ -670,7 +670,7 @@
- 现象:用户点击图片素材的“快速编辑”后,画布上额外出现 `Quick Edit Generator` 占位,像是新建了一个生成器;但用户预期是在原图下方框选区域、填写一个提示词和模型,然后直接修改当前图。
- 原因:快速编辑入口和提交链路误用了 `createQuickEditGenerationDialogDraft(...)` / `CanvasGenerationDialogState`,把“覆盖源图”的快速编辑伪装成会产出新图层的生成器占位。
- 处理:图片快速编辑必须走 `QuickEditPanelState`,打开时归档当前 active generation dialog 但不创建新的 `mode="quick-edit"` dialog;提交时调用 `/api/editor/images/edits`把当前图片或带编号标注的图片作为 `sourceImageSrc`,成功后覆盖源图,失败时保留快速编辑面板。快速编辑任务进入 `generating` 后必须移除框选工具和覆盖层,禁止继续新增框选;失败恢复面板后可继续调整框选再重试。图片重绘、去背景、视频快速编辑等会产出新图层或异步占位的入口仍可走 generation dialog / placement 链路。
- 处理:图片快速编辑必须走 `QuickEditPanelState`,打开时归档当前 active generation dialog 但不创建新的 `mode="quick-edit"` dialog;提交时调用 `/api/editor/images/edits`主来源始终使用当前图片已登记的 `resourceId``sourceAssetId`。带编号标注的图片上传后只作为辅助 `referenceImageSrcs`不能替换主来源身份;成功后覆盖源图,失败时保留快速编辑面板。快速编辑任务进入 `generating` 后必须移除框选工具和覆盖层,禁止继续新增框选;失败恢复面板后可继续调整框选再重试。图片重绘、去背景、视频快速编辑等会产出新图层或异步占位的入口仍可走 generation dialog / placement 链路。
- 验证:`npm run test -- src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx src/components/image-editor/ImageCanvasQuickEditPanelView.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx -- --runInBand`,以及按需运行 `npm run test -- src/components/image-editor/ImageCanvasEditorGenerationIntegration.test.tsx -t "快速编辑|quick edit" -- --runInBand`
- 关联:`src/components/image-editor/ImageCanvasEditorView.tsx``src/components/image-editor/useImageCanvasGenerationWorkflow.ts``src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts``src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts``src/services/image-editor/editorImageReference.ts`
@@ -717,11 +717,19 @@
## 图片画布快速编辑元数据必须记录原图引用
- 现象:快速编辑生成的新图可以替换画布,但打开图片信息时“生成输入”里看不到被修改的原图。
- 原因:信息面板直接渲染 `generationInputs.references`;快速编辑虽然把原图作为 `sourceImageSrc` 传给 provider,但如果 `buildQuickEditGenerationInputs(...)` 不把源图写成引用,后端资源和画布层都没有可展示的原图引用。
- 原因:信息面板直接渲染 `generationInputs.references`;快速编辑虽然 `sourceReferenceId` 指定原图,但如果 `buildQuickEditGenerationInputs(...)` 不把该业务 ID 写成引用,后端资源和画布层都没有可展示的原图引用。
- 处理:快速编辑的 `generationInputs.references` 必须始终包含 `原图`,再追加用户额外参考图;关闭额外参考图入口时也不能删除这条源图引用。
- 验证:`npm run test -- src/components/image-editor/ImageCanvasGenerationModel.test.ts src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx -- --runInBand`
- 关联:`src/components/image-editor/ImageCanvasGenerationModel.ts``src/components/image-editor/ImageCanvasMetadataModalView.tsx``src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts`
## 图片编辑主来源不能接受 objectKey 或请求类型
- 现象:调用方可把 objectKey、URL 或 Data URL 当作主来源,再用请求 `assetKind` 或另一个允许编辑的目标图层为禁止类型“借壳”;无目标图层时,后端还会扫描账号全部项目和素材库。
- 原因:HTTP DTO 同时承担外部请求与队列载荷,来源身份、存储定位和类型真相混在 `sourceImageSrc/sourceResourceId/assetKind` 中;worker 没有按业务 ID 复核入队后的身份漂移。
- 处理:站内与 External v1 API 调用方只提交必填 `sourceReferenceId`,且只接受当前账号项目资源 ID 或素材 ID;上传对象必须先登记。后端按两张表主键分别窄查,双表同 ID 时失败关闭,objectKey 仅作为服务端解析结果。目标绑定优先比较双方 `assetObjectId`,缺失才比较 canonical `(bucket, objectKey)`,并校验双方默认类型一致。队列保存版本化解析快照,worker 执行前再次定点解析;旧任务只把既有资源 ID 或旧来源字符串本身作为业务 ID 尝试迁移,禁止 objectKey 反查和旧 `assetKind` 真相回退。
- 验证:覆盖资源 ID、素材 ID、双表冲突、跨账号、raw objectKey/URL/Data URL/Blob URL、旧字段、禁止类型、目标对象与类型冲突、快照漂移、旧任务迁移、红框图辅助引用,以及 Canvas Agent 缺少 `reference_id`
- 关联:`server-rs/crates/spacetime-module/src/editor_project_storage.rs``server-rs/crates/api-server/src/editor_project.rs``server-rs/crates/api-server/src/external_generation_worker.rs``src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts``docs/openapi/genarrative-external-v1.openapi.json`
## 图片画布生成完成应用项目快照后也要刷新素材库
- 现象:部分素材生成成功后画布上已经出现结果,但左侧素材库没有立刻出现新素材,刷新页面后才显示。
File diff suppressed because one or more lines are too long
@@ -212,7 +212,7 @@ SpacetimeDB procedure
外部生成接口复用站内编辑器已有 DTO、入队器和 worker executor,不维护第二套生成语义:
- 图片生成 / 重绘 / 规范图 / 宣发图 / UI 设计图复用 `/api/editor/images/generations``/api/editor/images/edits` 的校验、模型归一、计费和持久化规则,但 External handler 固定只入队。
- 图片生成 / 重绘 / 规范图 / 宣发图 / UI 设计图复用 `/api/editor/images/generations``/api/editor/images/edits` 的校验、模型归一、计费和持久化规则,但 External handler 固定只入队。External v1 API 调用方编辑图片时必须提交当前账号已登记的项目资源 ID 或素材 ID 作为 `sourceReferenceId`;上传对象必须先登记为项目资源或素材。objectKey、URL、Data URL、Blob URL 以及旧 `sourceImageSrc/sourceResourceId/assetKind` 字段均返回 400。服务端分别按资源 ID 与素材 ID 主键窄查,双表冲突、未命中、越权或对象记录无效均失败关闭,类型只接受业务记录派生的 `null / spec / character / icon-spritesheet / icon-spec / publication-material / ui-design`。请求带 `targetLayerId` 时必须同时带 `projectId`;目标图层必须关联有效项目资源,来源与目标优先比较 `assetObjectId`,缺失时比较 canonical `(bucket, objectKey)`,来源默认类型还必须与目标资源默认类型一致。入队载荷保存版本化权威快照,worker 执行前再次定点解析并拒绝身份或类型漂移;仅以素材 ID 编辑时不伪造项目资源关系。
- 图标 spritesheet 和 UI 设计图素材提取复用站内拆分逻辑,生成图集后按连通域切片,并把图集与切片都按请求写入项目资源和素材库。
- 角色动画、视频、音效和背景音乐复用站内编辑器生成链路;请求携带 `assetFolderId` 时按站内规则写入素材库,音频类外部调用使用 API Key 所属账号作为 asset owner。
- API Key 管理接口仍只属于登录态个人中心,不进入外部 OpenAPI JSON。
@@ -40,7 +40,7 @@
- 生成视频:`你希望生成什么视频?`
8. 多输入框面板必须保留每个字段标题和输入框边界,例如生成规范。图标素材生成不再使用多描述列表,改为复用角色形象生成面板同款单文本输入框。
9. 生成规范下的角色规范、图标规范和自定义规范都使用同一生成类 shell:首行参考图区域、中央字段区、底部生成按钮区,不再出现缺首行参考区或单独 footer 样式。
10. 图片快速编辑不展示额外参考图入口;原图或绘制了红框和序号的标注图始终作为 `/api/editor/images/edits``sourceImageSrc` 直接提交,不作为 `referenceImageSrcs`
10. 图片快速编辑不展示用户可添加的额外参考图入口;原图已登记的 `resourceId``sourceAssetId` 始终作为 `/api/editor/images/edits``sourceReferenceId`,绘制了红框和序号的标注图上传后只作为辅助 `referenceImageSrcs`
11. 快速编辑打开后,画布视口应调整到原图完整展示,且面板位于原图下方并不遮挡原图;原图右侧显示竖向框选工具,支持矩形、椭圆和画笔自由框选。快速编辑进入时不默认启用框选工具,点击工具后出现选中态并保持高亮,再点同一工具取消启用;红色圈选框使用细描边。每完成一次框选,红色圈选框按完成顺序标注 `1 / 2 / 3...`,并在快速编辑提示词中追加一行 `对N号红色圈选框里的内容做以下修改:`
## 参数交互
@@ -187,10 +187,10 @@
- `生成音乐` 选项面板出现在音乐按钮上方,不再固定在底栏中间。
- 规范面板比图片生成面板更紧凑,字段间距和输入高度更小,但外层 shell、首行参考图和底部按钮区必须继续对齐生成图片 / 生成角色 / 生成视频。
- 生成规范类图片底部展示禁用态参数按钮 `16:9·2K``gpt-image-2`,视觉对齐可编辑面板的比例 / 尺寸 / 模型按钮;提交参数也固定为这三项,不出现可展开选项。
- 图片快速编辑底部左侧展示比例 / 尺寸组合选择,右侧展示模型选择和 `修改` 按钮;原图或红框序号标注图作为 `sourceImageSrc` 直接编辑,不展示额外参考图条。单个拆分图标不展示快速编辑入口,完整图标图集与图标规范仍可快速编辑。
- 图片快速编辑底部左侧展示比例 / 尺寸组合选择,右侧展示模型选择和 `修改` 按钮;原图已登记业务 ID 作为 `sourceReferenceId`,红框序号标注图作为内部辅助 `referenceImageSrcs`,不展示额外参考图条。单个拆分图标不展示快速编辑入口,完整图标图集与图标规范仍可快速编辑。
- 快速编辑打开后画布自动缩放平移到原图完整展示,并让面板位于原图下方且不遮挡原图;原图右侧出现竖向矩形 / 椭圆 / 画笔自由框选按钮。进入快速编辑不默认启用框选,点击工具启用并保持高亮,再点同一工具取消;完成框选后画布红色细框显示连续序号,输入框同步追加 `对N号红色圈选框里的内容做以下修改:`
- 快速编辑提交前保留提示词里对原图的 `原图``当前图片``当前图``图1` 引用,不再改写成 `图N`
- 快速编辑提交给后端时只把原图或已绘制红框和序号的标注图作为 `sourceImageSrc`,不提交隐藏的 `referenceImageSrcs`
- 快速编辑提交给后端时主来源只使用原图已登记的 `sourceReferenceId`;已绘制红框和序号的标注图上传后作为隐藏辅助 `referenceImageSrcs`,不得冒充目标图层主来源
- 生成中的占位图聚焦后可用 `Delete` / `Backspace` 删除;删除后异步结果不再落回画布,也不显示额外删除 UI。
- 快速编辑不创建生成中占位图;提交后当前面板显示修改中,异步结果只允许回填到源图。
- 生成视频 / 角色形象 / 角色动作 / 音效 / 背景音乐新建后,画布占位空白样式和右上角标签均与对应生成类型一致,不再统一使用图片占位 icon。