From 88269bf3e002c59cd6b5e16c585dc4790d952fbe Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Thu, 24 Sep 2026 16:59:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(generate):=20Step5=E3=80=8C=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E3=80=8D=E6=8C=89=E9=92=AE=E8=A1=A5=E4=B8=8A=20finali?= =?UTF-8?q?ze=20=E8=B0=83=E7=94=A8=E2=80=94=E2=80=94confirm=20=E5=90=8E?= =?UTF-8?q?=E5=86=8D=20finalize=20=E6=89=8D=E7=9C=9F=E6=AD=A3=E5=85=A5?= =?UTF-8?q?=E5=BA=93=E6=88=90=E5=93=81=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/api/generation/finalize.ts | 26 ++++++++++++++++++ apps/web/src/pages/generate/GeneratePage.tsx | 29 ++++++++++++++++---- 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/api/generation/finalize.ts diff --git a/apps/web/src/api/generation/finalize.ts b/apps/web/src/api/generation/finalize.ts new file mode 100644 index 000000000..249254cc9 --- /dev/null +++ b/apps/web/src/api/generation/finalize.ts @@ -0,0 +1,26 @@ +import apiClient from "../client" + +/** #2024 Step5 「完成」入库 —— 将 awaiting_cover 任务正式写入成品库 */ +export interface FinalizeGenerationRequest { + /** 用户选定的封面图片 URL;为空则使用任务默认封面(自动截帧/智能封面) */ + cover_url?: string +} + +export interface FinalizeGenerationResponse { + video_id: string + cover_url: string + file_url: string + /** success=新建成功;already_finalized=幂等返回已有记录 */ + status: string +} + +export const finalizeGeneration = async ( + taskId: string, + params: FinalizeGenerationRequest = {}, +): Promise => { + const response = await apiClient.post( + `/generation/tasks/${taskId}/finalize`, + params, + ) + return response.data +} diff --git a/apps/web/src/pages/generate/GeneratePage.tsx b/apps/web/src/pages/generate/GeneratePage.tsx index c93fc64bf..3824bd1a0 100644 --- a/apps/web/src/pages/generate/GeneratePage.tsx +++ b/apps/web/src/pages/generate/GeneratePage.tsx @@ -22,6 +22,7 @@ import { useGenerateFormState } from "./hooks/useGenerateFormState" import { useStepNavigation } from "./hooks/useStepNavigation" import { useGenerateVideo } from "./hooks/useGenerateVideo" import { confirmGeneration } from "@/api/generation/confirm" +import { finalizeGeneration } from "@/api/generation/finalize" import { useBatchVariantPlans } from "./hooks/useBatchVariantPlans" import { useTitleStyleUpdaters } from "./hooks/useStep4Title/useTitleStyleUpdaters" @@ -411,7 +412,7 @@ const GeneratePage: React.FC = () => { /* ── 最终成片(单视频) ── */ const finalVideo = generatedVideos[0] - /* ── Step5 完成:调用 confirm 入库 + 跳转 ── */ + /* ── Step5 完成:先 confirm(同步标题/封面到任务)再 finalize(正式入库成品库) ── */ const handleFinish = useCallback(async () => { if (finishing) return // 校验:单视频必须已生成;批量必须所有已选视频有封面或确认跳过 @@ -436,24 +437,42 @@ const GeneratePage: React.FC = () => { ? [finalVideo.generation_task_id] : [] - // 单视频/批量:为每个任务调用 confirm(传入封面) + // 第一步:confirm(同步封面+标题,把 is_preview 翻 false,任务进入/停留在 awaiting_cover) + let confirmedTaskIds: string[] = [] if (isBatch && previewCovers.length > 0) { - await Promise.all( + const results = await Promise.all( taskIds.map(async (taskId, idx) => { const coverUrl = previewCovers[idx] || "" - return confirmGeneration(taskId, { + const resp = await confirmGeneration(taskId, { cover_url: coverUrl || undefined, custom_title: previewTitles[idx] || titleSettings.title || "", }) + return resp.items?.[0]?.id || taskId }), ) + confirmedTaskIds = results } else if (finalVideo?.generation_task_id) { const coverUrl = coverSettings.thumbnail_url || coverSettings.upload_url || "" - await confirmGeneration(finalVideo.generation_task_id, { + const resp = await confirmGeneration(finalVideo.generation_task_id, { cover_url: coverUrl || undefined, custom_title: titleSettings.title || "", }) + confirmedTaskIds = [resp.items?.[0]?.id || finalVideo.generation_task_id] + } else { + confirmedTaskIds = taskIds } + + // 第二步:finalize(真正入成品库,生成 GeneratedVideo 记录,任务推进到 completed) + // 批量每个任务独立 finalize;单视频只 finalize 当前这一个 + const coverUrlList = isBatch + ? confirmedTaskIds.map((_, idx) => previewCovers[idx] || "") + : [coverSettings.thumbnail_url || coverSettings.upload_url || ""] + await Promise.all( + confirmedTaskIds.map((tid, idx) => + finalizeGeneration(tid, { cover_url: coverUrlList[idx] || undefined }), + ), + ) + hide() message.success("已保存到视频库") navigate("/app/products")