diff --git a/src/components/image-editor/useImageCanvasGenerationWorkflow.ts b/src/components/image-editor/useImageCanvasGenerationWorkflow.ts index 08b539202..bd81193d1 100644 --- a/src/components/image-editor/useImageCanvasGenerationWorkflow.ts +++ b/src/components/image-editor/useImageCanvasGenerationWorkflow.ts @@ -121,6 +121,7 @@ import { readPerfectPixelOperations, savePerfectPixelOperation, } from './perfectPixelOperationStore'; +import type { CanvasGenerationDialogDraft } from './useCanvasGenerationDialogs'; import { applyQueuedEditorGenerationProject, createEditorGenerationMediaUploadId, @@ -913,9 +914,7 @@ type GenerationWorkflowOptions = { layerCounterRef: MutableRefObject; generateDialog: GenerateDialogState | null; setGenerateDialog: Dispatch>; - openCanvasGenerationDialog: ( - dialog: Omit & { id?: string }, - ) => string; + openCanvasGenerationDialog: (dialog: CanvasGenerationDialogDraft) => string; activateCanvasGenerationDialog: ( targetDialog: CanvasGenerationDialogState, ) => void; @@ -2534,11 +2533,14 @@ export function useImageCanvasGenerationWorkflow({ // 中文注释:保留服务端原文(例如 assetKind 校验失败)便于定位,同时附上对账结论。 // 结果未知时必须保留 operation 快照并停在 pending-confirmation;这既不会把在途结果 // 谎报成失败,也不会开放一条会创建新 identity 的普通重提路径。 + // + // 这里曾有第三条分支「operation 已形成但 POST 未发出」。严格布局保存通道删除后它已 + // 不可达:从 operation 赋值到 `perfectPixelPostAttempted = true` 之间只剩 Set.add、 + // 纯函数、状态更新器,以及 `await flush(...).catch(() => undefined)` 与内部吞异常的 + // `savePerfectPixelOperation`,没有任何语句能抛。留着它只会让人以为该失败态另有提示。 const errorMessage = reconciledMessage ? `${serverMessage ? `${serverMessage} ` : ''}${reconciledMessage}请稍后继续核对,或显式重试同一操作。` - : perfectPixelOperation && !perfectPixelPostAttempted - ? `完美像素请求尚未发出:${serverMessage ?? '提交前准备失败'} 请在原占位重试,或删除占位后重来。` - : (serverMessage ?? '完美像素处理失败'); + : (serverMessage ?? '完美像素处理失败'); if ( perfectPixelDialogId && hasCanvasGenerationDialogById(perfectPixelDialogId) @@ -2763,12 +2765,14 @@ export function useImageCanvasGenerationWorkflow({ error instanceof Error && error.message.trim() ? error.message : '完美像素处理失败'; + // 中文注释:文案不再按 `postAttempted` 分档——严格布局保存通道删除后,从进入 try 到 + // `postAttempted = true` 之间已经没有任何能抛异常的语句(flush 带 `.catch`、 + // `savePerfectPixelOperation` 内部吞异常,其余是纯函数与状态更新器),所以走到这里 + // 时 POST 必然已经发出过。 updateCanvasGenerationDialogById(normalizedDialogId, (current) => ({ ...current, status: 'failed', - errorMessage: postAttempted - ? errorMessage - : `完美像素请求尚未发出:${errorMessage} 请在原占位重试同一操作。`, + errorMessage, })); } finally { releaseActiveInlineGenerationDialog(normalizedDialogId); diff --git a/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx b/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx index 72efe2db1..51b80e849 100644 --- a/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx +++ b/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx @@ -163,30 +163,6 @@ function createCompletedEditorProjectSnapshot( }; } -function createEmptyEditorProjectSnapshot( - revision = 0, - title = '空画布项目', -): EditorProjectSnapshot { - return { - projectId: 'editor-project-default', - title, - canvas: { - canvasId: 'editor-project-default:canvas:default', - projectId: 'editor-project-default', - title: '默认画布', - viewport: { x: 0, y: 0, scale: 1 }, - layers: [], - revision, - layoutStorageVersion: 0, - updatedAt: `2026-06-12T00:00:0${revision}.000Z`, - }, - viewport: { x: 0, y: 0, scale: 1 }, - layers: [], - resources: [], - updatedAt: `2026-06-12T00:00:0${revision}.000Z`, - }; -} - it('merges pending geometry and dialog edits while retaining backend additions and deletions', () => { const authoritativeItems: EditorProjectLayerSnapshot[] = [ { diff --git a/src/services/image-editor/editorProjectClient.ts b/src/services/image-editor/editorProjectClient.ts index c01531888..3135301fb 100644 --- a/src/services/image-editor/editorProjectClient.ts +++ b/src/services/image-editor/editorProjectClient.ts @@ -581,11 +581,6 @@ export type EditorProjectLayoutSaveInput = { expectedRevision: number; }; -export type EditorProjectLayoutSaveOptions = { - signal?: AbortSignal; - deadlineAt?: number; -}; - export type EditorProjectLayoutSaveResult = { projectId: string; canvasId: string; @@ -820,29 +815,23 @@ export async function deleteEditorProject(projectId: string) { return response.deletedProjectId; } +// 中文注释:这里曾接受第三参数(调用方注入的 `signal` / `deadlineAt`),只服务于已删除的 +// 严格布局保存通道;该通道删除后全部调用点都只传两个参数,注入分支恒不生效。一并摘掉, +// 免得后来者以为调用方还能控制这次保存的取消与截止。 export async function saveEditorProjectLayout( projectId: string, input: EditorProjectLayoutSaveInput, - options: EditorProjectLayoutSaveOptions = {}, ) { - const deadlineAt = - typeof options.deadlineAt === 'number' && - Number.isFinite(options.deadlineAt) - ? options.deadlineAt - : Date.now() + 60_000; return requestJson( `${EDITOR_PROJECT_API_BASE}/${encodeURIComponent(projectId)}`, - { - ...jsonRequest('PATCH', { - viewport: input.viewport, - layers: input.layers, - expectedRevision: input.expectedRevision, - }), - ...(options.signal ? { signal: options.signal } : {}), - }, + jsonRequest('PATCH', { + viewport: input.viewport, + layers: input.layers, + expectedRevision: input.expectedRevision, + }), '保存图片画布工程失败', // 布局保存会被生成提交链同步等待,必须连鉴权恢复和响应体读取一起有界。 - { deadlineAt }, + { deadlineAt: Date.now() + 60_000 }, ); }