fix(canvas): 画布资源恢复 - 生成结果下载或 OSS 落盘失败支持原任务重新加载
This commit is contained in:
1 parent
fc546c7989
commit
9d848b9daa
15 files changed
+116
-35
No files matched your search
@@ -97,6 +97,7 @@ export async function executeImageGeneration({
|
||||
...styleMetadata,
|
||||
imageBatchExpanded: count > 1 ? true : undefined,
|
||||
generationErrorCode: undefined,
|
||||
resourceReloadAvailable: undefined,
|
||||
failedPromptFingerprint: undefined,
|
||||
},
|
||||
};
|
||||
@@ -115,6 +116,7 @@ export async function executeImageGeneration({
|
||||
...generationMetadata,
|
||||
...styleMetadata,
|
||||
generationErrorCode: undefined,
|
||||
resourceReloadAvailable: undefined,
|
||||
failedPromptFingerprint: undefined,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -51,6 +51,7 @@ export async function executeVideoGeneration({
|
||||
status: NODE_STATUS_LOADING,
|
||||
errorDetails: undefined,
|
||||
generationErrorCode: undefined,
|
||||
resourceReloadAvailable: undefined,
|
||||
failedPromptFingerprint: undefined,
|
||||
model: generationConfig.model,
|
||||
size: generationConfig.size,
|
||||
|
||||
@@ -64,6 +64,7 @@ type CanvasProjectWorldLayersProps = {
|
||||
onToggleBatch: (nodeId: string) => void;
|
||||
onSetBatchPrimary: (node: CanvasNodeData) => void;
|
||||
onRetry: (node: CanvasNodeData) => void;
|
||||
onReloadResource: (node: CanvasNodeData) => void;
|
||||
onOpenTaskDetails: (node: CanvasNodeData) => void;
|
||||
onOpenVersions: (node: CanvasNodeData) => void;
|
||||
onViewImage: (node: CanvasNodeData) => void;
|
||||
@@ -161,6 +162,7 @@ export const CanvasProjectWorldLayers = memo(function CanvasProjectWorldLayers(p
|
||||
onToggleBatch={props.onToggleBatch}
|
||||
onSetBatchPrimary={props.onSetBatchPrimary}
|
||||
onRetry={props.onRetry}
|
||||
onReloadResource={props.onReloadResource}
|
||||
onOpenTaskDetails={props.onOpenTaskDetails}
|
||||
onOpenVersions={props.onOpenVersions}
|
||||
onViewImage={props.onViewImage}
|
||||
|
||||
@@ -85,6 +85,6 @@ export async function executeTextGeneration({
|
||||
if (controller.signal.aborted) return;
|
||||
void answers;
|
||||
if (isConfigNode) {
|
||||
setNodes((current) => current.map((node) => (node.id === nodeId ? { ...node, metadata: { ...node.metadata, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } } : node)));
|
||||
setNodes((current) => current.map((node) => (node.id === nodeId ? { ...node, metadata: { ...node.metadata, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, resourceReloadAvailable: undefined, failedPromptFingerprint: undefined } } : node)));
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ import { CanvasProjectWorldLayers } from "./canvas-project-world-layers";
|
||||
import { CanvasNodeActionContext } from "@/components/canvas/canvas-node-action-context";
|
||||
import { CanvasNodeGraphContext, type CanvasNodeGraphContextValue } from "@/components/canvas/canvas-node-graph-context";
|
||||
import { CanvasRefreshShell } from "./canvas-refresh-shell";
|
||||
import { queryGenerationTask } from "@/services/api/task-center";
|
||||
import type { CanvasImageEmotionPayload } from "@/components/canvas/canvas-node-emotion-panel";
|
||||
import { CanvasEmotionWorkspace } from "@/components/canvas/canvas-emotion-workspace";
|
||||
import { removeCanvasDrawing } from "@/lib/canvas/canvas-drawing-storage";
|
||||
@@ -1515,6 +1516,21 @@ function InfiniteCanvasPage() {
|
||||
bindGenerationTask,
|
||||
applyGenerationTaskResult,
|
||||
});
|
||||
const reloadCanvasNodeResource = useCallback(
|
||||
async (node: CanvasNodeData) => {
|
||||
const taskId = node.metadata?.taskId;
|
||||
if (!taskId || !node.metadata?.resourceReloadAvailable) return;
|
||||
setNodes((current) => current.map((item) => (item.id === node.id ? { ...item, metadata: { ...item.metadata, status: "loading", taskStage: "正在重新加载资源", errorDetails: undefined } } : item)));
|
||||
try {
|
||||
const task = await queryGenerationTask(taskId);
|
||||
if (task.status !== "succeeded") throw new Error("原生成任务尚未成功,无法重新加载资源");
|
||||
await applyGenerationTaskResult(node.id, task);
|
||||
} catch (error) {
|
||||
setNodes((current) => current.map((item) => (item.id === node.id ? { ...item, metadata: { ...item.metadata, status: "error", errorDetails: error instanceof Error ? error.message : "资源重新加载失败", resourceReloadAvailable: true } } : item)));
|
||||
}
|
||||
},
|
||||
[applyGenerationTaskResult, setNodes],
|
||||
);
|
||||
const reconcileImageBatchRootNode = useCallback((rootId: string) => {
|
||||
setNodes((current) => {
|
||||
const root = current.find((item) => item.id === rootId);
|
||||
@@ -2002,6 +2018,7 @@ function InfiniteCanvasPage() {
|
||||
onToggleBatch={toggleBatchExpanded}
|
||||
onSetBatchPrimary={setBatchPrimary}
|
||||
onRetry={retryCanvasNode}
|
||||
onReloadResource={reloadCanvasNodeResource}
|
||||
onOpenTaskDetails={openCanvasNodeTaskDetails}
|
||||
onOpenVersions={openCanvasNodeVersions}
|
||||
onViewImage={viewCanvasNodeImage}
|
||||
|
||||
@@ -124,6 +124,7 @@ export function useCanvasGenerationExecutor({
|
||||
taskCreatedAt: new Date().toISOString(),
|
||||
errorDetails: undefined,
|
||||
generationErrorCode: undefined,
|
||||
resourceReloadAvailable: undefined,
|
||||
failedPromptFingerprint: undefined,
|
||||
},
|
||||
}
|
||||
@@ -235,7 +236,7 @@ export function useCanvasGenerationExecutor({
|
||||
if (markSourceStatus)
|
||||
setNodes((current) =>
|
||||
current.map((node) =>
|
||||
node.id === nodeId ? { ...node, metadata: { ...node.metadata, prompt: statusPrompt, status: NODE_STATUS_LOADING, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } } : node,
|
||||
node.id === nodeId ? { ...node, metadata: { ...node.metadata, prompt: statusPrompt, status: NODE_STATUS_LOADING, errorDetails: undefined, generationErrorCode: undefined, resourceReloadAvailable: undefined, failedPromptFingerprint: undefined } } : node,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ export function useCanvasGenerationRetry({
|
||||
: undefined;
|
||||
|
||||
setRunningNodeId(node.id);
|
||||
setNodes((current) => current.map((item) => (item.id === node.id ? { ...item, metadata: { ...item.metadata, status: NODE_STATUS_LOADING, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } } : item)));
|
||||
setNodes((current) => current.map((item) => (item.id === node.id ? { ...item, metadata: { ...item.metadata, status: NODE_STATUS_LOADING, errorDetails: undefined, generationErrorCode: undefined, resourceReloadAvailable: undefined, failedPromptFingerprint: undefined } } : item)));
|
||||
const controller = startGenerationRequest(node.id, sourceNode.id, node.id);
|
||||
const retryContext = node.metadata?.taskId ? await createGenerationRetryContext(node.metadata.taskId, node.metadata.attemptGroupId) : {};
|
||||
const runAndConsumeRetry = async (input: Parameters<typeof runBackendCanvasGenerationTask>[0]) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState, type Dispatch, type SetStateA
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { App } from "antd";
|
||||
|
||||
import { applyGenerationTaskResultToNodes, generationTaskNodeId } from "@/lib/canvas/canvas-generation-task-sync";
|
||||
import { applyGenerationTaskResultToNodes, generationTaskCanReloadResource, generationTaskNodeId } from "@/lib/canvas/canvas-generation-task-sync";
|
||||
import { applyCanvasGenerationTaskNodeEffect, isCanvasGenerationDurableAckError } from "@/services/canvas-generation-consumer";
|
||||
import { consumeGenerationTaskNode, ensureCanvasNodeAsset } from "@/services/project-asset-sync";
|
||||
import { listGenerationTasks, listTaskLogs, queryGenerationTask, subscribeGenerationTasks, type GenerationTask, type TaskLog } from "@/services/api/task-center";
|
||||
@@ -120,6 +120,7 @@ export async function recoverCanvasGenerationTaskNode(input: {
|
||||
status: NODE_STATUS_SUCCESS,
|
||||
errorDetails: undefined,
|
||||
generationErrorCode: undefined,
|
||||
resourceReloadAvailable: undefined,
|
||||
failedPromptFingerprint: undefined,
|
||||
storyboard: { rows: result.rows, visibleColumns: cinematicStoryboardColumns(item.metadata?.storyboard?.visibleColumns), referenceNodeIds: item.metadata?.storyboard?.referenceNodeIds || [] },
|
||||
},
|
||||
@@ -261,7 +262,7 @@ export function useCanvasGeneration({ projectId, domainProjectId, projectLoaded,
|
||||
...node.metadata,
|
||||
...generationTaskMetadata(task),
|
||||
status: failed ? NODE_STATUS_ERROR : hasCompletedContent ? NODE_STATUS_SUCCESS : NODE_STATUS_LOADING,
|
||||
...(failure || { errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined }),
|
||||
...(failure || { errorDetails: undefined, generationErrorCode: undefined, resourceReloadAvailable: undefined, failedPromptFingerprint: undefined }),
|
||||
},
|
||||
};
|
||||
}),
|
||||
@@ -281,30 +282,37 @@ export function useCanvasGeneration({ projectId, domainProjectId, projectLoaded,
|
||||
|
||||
const applyGenerationTaskResult = useCallback(
|
||||
async (nodeId: string, task: GenerationTask) => {
|
||||
if (!task.outputs?.length && task.type === "canvas_text") {
|
||||
const applied = await applyGenerationTaskResultToNodes(nodesRef.current, task, nodeId);
|
||||
if (!applied.updated || !applied.node) throw new Error("画布中找不到对应任务节点");
|
||||
setNodes((current) => current.map((node) => (node.id === applied.nodeId ? applied.node! : node)));
|
||||
return;
|
||||
try {
|
||||
if (!task.outputs?.length && task.type === "canvas_text") {
|
||||
const applied = await applyGenerationTaskResultToNodes(nodesRef.current, task, nodeId);
|
||||
if (!applied.updated || !applied.node) throw new Error("画布中找不到对应任务节点");
|
||||
setNodes((current) => current.map((node) => (node.id === applied.nodeId ? applied.node! : node)));
|
||||
return;
|
||||
}
|
||||
await consumeGenerationTaskNode(
|
||||
task,
|
||||
nodeId,
|
||||
0,
|
||||
async ({ task: materialized, output, effectKey, signal }) => {
|
||||
await applyCanvasGenerationTaskNodeEffect({
|
||||
projectId,
|
||||
nodeId,
|
||||
task: materialized,
|
||||
output,
|
||||
effectKey,
|
||||
signal,
|
||||
nodesRef,
|
||||
setNodes,
|
||||
});
|
||||
},
|
||||
{ signal: consumerControllerRef.current.signal },
|
||||
);
|
||||
} catch (error) {
|
||||
if (generationTaskCanReloadResource(task)) {
|
||||
setNodes((current) => current.map((node) => (node.id === nodeId ? { ...node, metadata: { ...node.metadata, resourceReloadAvailable: true } } : node)));
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
await consumeGenerationTaskNode(
|
||||
task,
|
||||
nodeId,
|
||||
0,
|
||||
async ({ task: materialized, output, effectKey, signal }) => {
|
||||
await applyCanvasGenerationTaskNodeEffect({
|
||||
projectId,
|
||||
nodeId,
|
||||
task: materialized,
|
||||
output,
|
||||
effectKey,
|
||||
signal,
|
||||
nodesRef,
|
||||
setNodes,
|
||||
});
|
||||
},
|
||||
{ signal: consumerControllerRef.current.signal },
|
||||
);
|
||||
},
|
||||
[nodesRef, projectId, setNodes],
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user