From 5ba7a993dee07407924647f0367fc7575e76ea81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 24 Sep 2026 17:30:53 +0800 Subject: [PATCH] =?UTF-8?q?3D=20=E6=8F=90=E4=BA=A4=E7=BC=96=E6=8E=92?= =?UTF-8?q?=E6=95=B4=E6=AE=B5=E8=90=BD=E5=9C=A8=E9=94=99=E8=AF=AF=E8=BE=B9?= =?UTF-8?q?=E7=95=8C=E5=86=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/components/image-editor/model3d-generation/useModel3dGenerationTask.ts:把取占位框、算结果标题与构造提交计划一并移进 try,注入的回调或老快照缺字段导致的抛错不再漏成未捕获 rejection,而是照常写回失败态与提示 - src/components/image-editor/model3d-generation/useModel3dGenerationTask.test.tsx:补「计划构造阶段抛错」的用例(缺 prompt 的老快照) --- .../useModel3dGenerationTask.test.tsx | 17 +++++ .../useModel3dGenerationTask.ts | 66 ++++++++++--------- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/components/image-editor/model3d-generation/useModel3dGenerationTask.test.tsx b/src/components/image-editor/model3d-generation/useModel3dGenerationTask.test.tsx index b2398bb0e..ddd1b3065 100644 --- a/src/components/image-editor/model3d-generation/useModel3dGenerationTask.test.tsx +++ b/src/components/image-editor/model3d-generation/useModel3dGenerationTask.test.tsx @@ -276,6 +276,23 @@ describe('useModel3dGenerationTask', () => { expect(result.current.dialog.composerOpen).toBe(true); }); + it('构造提交计划时抛的错也落到失败态,而不是漏成未捕获的 rejection', async () => { + // 老快照缺字段:resolveModel3dResultTitle 会读 dialog.prompt.trim(),在计划构造前就抛。 + const { result } = renderTask({ + dialog: createDialog({ prompt: undefined }), + }); + + await act(async () => { + await result.current.submitModel3dGeneration( + createDialog({ prompt: undefined }), + ); + }); + + expect(result.current.dialog.status).toBe('failed'); + expect(result.current.dialog.composerOpen).toBe(true); + expect(submitModel3dTextToModelRequestMock).not.toHaveBeenCalled(); + }); + it('定价段缺失时不发请求,只把不可提交的原因写回面板', async () => { applyEditorGenerationPricingConfig({ models: EDITOR_MODEL_MUD_POINT_CONFIG, diff --git a/src/components/image-editor/model3d-generation/useModel3dGenerationTask.ts b/src/components/image-editor/model3d-generation/useModel3dGenerationTask.ts index 506214383..1a8c19925 100644 --- a/src/components/image-editor/model3d-generation/useModel3dGenerationTask.ts +++ b/src/components/image-editor/model3d-generation/useModel3dGenerationTask.ts @@ -74,41 +74,43 @@ export function useModel3dGenerationTask({ if (!dialogId || !isModel3dGenerationMode(dialog.mode)) { return; } - const placeholder = getGeneratingDialogPlaceholder(dialog); - const canvasCompletion = - projectId && placeholder - ? { - dialogId, - title: resolveModel3dResultTitle(dialog), - placeholder, - } - : null; - const planResult = buildModel3dSubmissionPlan({ - dialog, - projectId, - canvasCompletion, - assetFolderId, - assetLabel: resolveModel3dResultTitle(dialog), - pricing: readEditorModel3dPricingConfig(), - }); - if (!planResult.ok) { + // 整个提交编排都落在 try 里:取占位框、算标题、构造提交计划同样可能抛 + // (注入的回调、老快照缺字段),漏出去就成了一次没有用户提示的未捕获 rejection。 + try { + const placeholder = getGeneratingDialogPlaceholder(dialog); + const canvasCompletion = + projectId && placeholder + ? { + dialogId, + title: resolveModel3dResultTitle(dialog), + placeholder, + } + : null; + const planResult = buildModel3dSubmissionPlan({ + dialog, + projectId, + canvasCompletion, + assetFolderId, + assetLabel: resolveModel3dResultTitle(dialog), + pricing: readEditorModel3dPricingConfig(), + }); + if (!planResult.ok) { + updateCanvasGenerationDialogById(dialogId, (currentDialog) => ({ + ...currentDialog, + status: 'failed', + composerOpen: true, + errorMessage: planResult.message, + })); + return; + } updateCanvasGenerationDialogById(dialogId, (currentDialog) => ({ ...currentDialog, - status: 'failed', - composerOpen: true, - errorMessage: planResult.message, + // 兜底铸的代次要写回状态:否则老快照每次提交都换幂等键,去重保护失效。 + model3dAttemptNonce: planResult.plan.attemptNonce, + status: 'generating', + composerOpen: false, + errorMessage: undefined, })); - return; - } - updateCanvasGenerationDialogById(dialogId, (currentDialog) => ({ - ...currentDialog, - // 兜底铸的代次要写回状态:否则老快照每次提交都换幂等键,去重保护失效。 - model3dAttemptNonce: planResult.plan.attemptNonce, - status: 'generating', - composerOpen: false, - errorMessage: undefined, - })); - try { const plan = planResult.plan; // 判别联合在这里收窄:文生与图生各走自己的客户端函数,端点与提交体的配对由类型保证。 const response =