import { z } from "zod"; export const storyTaskAttachmentSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("story-asset"), nodeId: z.string().min(1), asset: z.object({ id: z.string().min(1), name: z.string(), description: z.string(), imageUrl: z.string(), type: z.string(), }), }), z.object({ type: z.literal("story-prompts"), nodeId: z.string().min(1), rows: z .array(z.object({ id: z.string().min(1) }).catchall(z.unknown())) .min(1) .max(120), }), ]); export type StoryTaskAttachment = z.infer; export function requireStoryPromptResult(data: any, attachment?: StoryTaskAttachment) { if (attachment?.type !== "story-prompts") return; if ( !Array.isArray(data?.analysisRows) || data.analysisRows.length !== attachment.rows.length || data.analysisRows.some( (row: any) => !String(row.imagePrompt || "").trim() || !String(row.motionPrompt || "").trim(), ) ) throw Error("未返回完整镜头提示词,原内容已保留"); } export function mergeStoryTaskResult( draft: Record, attachment: StoryTaskAttachment, data: any, taskId: string, ) { if (draft.storyTaskResults?.[taskId]) return draft; let conflict = false; let patch: Record = {}; if (attachment.type === "story-asset") { let found = false; const url = data.images?.[0]?.originalUrl || data.images?.[0]?.thumbnailUrl; patch.storyAssets = (draft.storyAssets || []).map((asset: any) => { if (asset.id !== attachment.asset.id) return asset; found = true; if ( !url || ["name", "description", "type", "imageUrl"].some( (key) => asset[key] !== attachment.asset[key as keyof typeof attachment.asset], ) ) { conflict = true; return asset; } return { ...asset, imageUrl: url }; }); if (!found) conflict = true; } else { requireStoryPromptResult(data, attachment); const byId = new Map(attachment.rows.map((row, index) => [row.id, { row, index }])); let found = 0; patch.rows = (draft.rows || []).map((row: any) => { const expected = byId.get(row.id); if (!expected) return row; found++; if ( Object.keys(expected.row).some( (key) => JSON.stringify(row[key]) !== JSON.stringify(expected.row[key]), ) ) { conflict = true; return row; } const generated = data.analysisRows[expected.index]; return { ...row, imagePrompt: generated.imagePrompt, motionPrompt: generated.motionPrompt }; }); if (found !== attachment.rows.length) conflict = true; } return { ...draft, ...patch, storyTaskResults: { ...draft.storyTaskResults, [taskId]: { status: conflict ? "conflict" : "applied", message: conflict ? "生成期间内容有修改或删除,已保留原内容;新结果在画布的生成节点中。" : "生成结果已同步到脚本。", }, }, }; }