对接修复

This commit is contained in:
海龙 2025-08-07 14:08:16 +08:00
parent 6df87250fb
commit 86bb1bf4c1
4 changed files with 270 additions and 164 deletions

View File

@ -699,6 +699,8 @@ export const createMovieProjectV1 = async (request: {
mode: "auto" | "manual";
/** 分辨率720p | 1080p | 4k */
resolution: "720p" | "1080p" | "4k";
/** 语言 */
language: string;
}) => {
return post<ApiResponse<{
/** 项目ID */

View File

@ -1,6 +1,17 @@
import { useState, useCallback, Dispatch, SetStateAction, useMemo } from "react";
import { ScriptEditUseCase,ScriptEditKey } from "../usecase/ScriptEditUseCase";
import { getProjectScript, abortVideoTask, pausePlanFlow, resumePlanFlow } from "../../../api/video_flow";
import {
useState,
useCallback,
Dispatch,
SetStateAction,
useMemo,
} from "react";
import { ScriptEditUseCase, ScriptEditKey } from "../usecase/ScriptEditUseCase";
import {
getProjectScript,
abortVideoTask,
pausePlanFlow,
resumePlanFlow,
} from "../../../api/video_flow";
import { parseScriptBlock } from "../domain/service";
import { ScriptBlock } from "@/components/script-renderer/types";
@ -48,7 +59,17 @@ export interface UseScriptService {
/** 中断视频任务 */
abortVideoTask: () => Promise<void>;
/** 聚焦处理函数 */
focusHandler: (field: 'synopsis' | 'categories' | 'protagonist' | 'incitingIncident' | 'problem' | 'conflict' | 'stakes' | 'characterArc') => Promise<void>;
focusHandler: (
field:
| "synopsis"
| "categories"
| "protagonist"
| "incitingIncident"
| "problem"
| "conflict"
| "stakes"
| "characterArc"
) => Promise<void>;
/** 增强剧本 */
enhanceScript: () => Promise<void>;
/** 设置AI优化要求 */
@ -72,6 +93,18 @@ export interface UseScriptService {
/** 设置人物弧线完成 */
setCharacterArc: Dispatch<SetStateAction<string>>;
/** 设置项目ID */
setProjectId: Dispatch<SetStateAction<string>>;
/** 设置计划ID */
setPlanId: Dispatch<SetStateAction<string>>;
/** 创建项目 */
createMovieProjectV1: (
idea: string,
userId: string,
mode: "auto" | "manual",
resolution: string,
language: string
) => Promise<void>;
}
/**
@ -96,22 +129,103 @@ export const useScriptService = (): UseScriptService => {
const [focusedField, setFocusedField] = useState<string>("");
// UseCase实例
const [scriptEditUseCase, setScriptEditUseCase] = useState<ScriptEditUseCase | null>(null);
const [scriptEditUseCase, setScriptEditUseCase] = useState<ScriptEditUseCase>(
new ScriptEditUseCase("")
);
/**
*
* @param idea
*/
const generateScriptFromIdea = useCallback(async (idea: string): Promise<void> => {
try {
setLoading(true);
const generateScriptFromIdea = useCallback(
async (idea: string,project_id?:string): Promise<void> => {
try {
setLoading(true);
if(project_id){
setProjectId(project_id);
}
// 调用AI生成剧本
await scriptEditUseCase.generateScript(idea, (content) => {
// 获取解析后的故事详情
const storyDetails = scriptEditUseCase.getStoryDetails();
setSynopsis(storyDetails.synopsis || "");
setCategories(storyDetails.categories || []);
setProtagonist(storyDetails.protagonist || "");
setIncitingIncident(storyDetails.incitingIncident || "");
setProblem(storyDetails.problem || "");
setConflict(storyDetails.conflict || "");
setStakes(storyDetails.stakes || "");
setCharacterArc(storyDetails.characterArc || "");
});
// 创建新的剧本编辑用例
const newScriptEditUseCase = new ScriptEditUseCase('');
setScriptEditUseCase(newScriptEditUseCase);
// 自动保存剧本到项目
await scriptEditUseCase.saveScript((projectId||project_id) as string);
} catch (error) {
console.error("生成剧本失败:", error);
throw error;
} finally {
setLoading(false);
}
},
[]
);
const createMovieProjectV1 = useCallback(
async (
idea: string,
userId: string,
mode: "auto" | "manual",
resolution: string,
language: string
): Promise<void> => {
try {
setLoading(true);
// 剧本生成完成后,自动创建项目
const projectData = await scriptEditUseCase.createProject(
idea,
userId,
mode as "auto" | "manual",
resolution as "720p" | "1080p" | "4k",
language
);
setProjectId(projectData.project_id);
setPlanId(projectData.plan_id);
} catch (error) {
console.error("创建项目失败:", error);
throw error;
} finally {
setLoading(false);
}
},
[scriptEditUseCase]
);
/**
* ID初始化已有剧本
* @param projectId ID
*/
const initializeFromProject = useCallback(
async (projectId: string): Promise<void> => {
try {
setLoading(true);
// 设置项目ID
setProjectId(projectId);
// 调用API获取项目剧本数据
const response = await getProjectScript({ project_id: projectId });
if (!response.successful) {
throw new Error(response.message || "获取项目剧本失败");
}
const { generated_script } = response.data;
// 创建新的剧本编辑用例并初始化数据
const newScriptEditUseCase = new ScriptEditUseCase(generated_script);
setScriptEditUseCase(newScriptEditUseCase);
// 调用AI生成剧本
await newScriptEditUseCase.generateScript(idea, (content) => {
// 获取解析后的故事详情
const storyDetails = newScriptEditUseCase.getStoryDetails();
setSynopsis(storyDetails.synopsis || "");
@ -122,72 +236,15 @@ export const useScriptService = (): UseScriptService => {
setConflict(storyDetails.conflict || "");
setStakes(storyDetails.stakes || "");
setCharacterArc(storyDetails.characterArc || "");
});
// 剧本生成完成后,自动创建项目
const projectData = await newScriptEditUseCase.createProject(
idea,
JSON.parse(localStorage.getItem('currentUser') || '{}').id,
"auto",
"720p"
);
setProjectId(projectData.project_id);
setPlanId(projectData.plan_id);
// 自动保存剧本到项目
await newScriptEditUseCase.saveScript(projectData.project_id);
} catch (error) {
console.error('生成剧本失败:', error);
throw error;
} finally {
setLoading(false);
}
}, []);
/**
* ID初始化已有剧本
* @param projectId ID
*/
const initializeFromProject = useCallback(async (projectId: string): Promise<void> => {
try {
setLoading(true);
// 设置项目ID
setProjectId(projectId);
// 调用API获取项目剧本数据
const response = await getProjectScript({ project_id: projectId });
if (!response.successful) {
throw new Error(response.message || '获取项目剧本失败');
} catch (error) {
console.error("初始化项目剧本失败:", error);
throw error;
} finally {
setLoading(false);
}
const { generated_script } = response.data;
// 创建新的剧本编辑用例并初始化数据
const newScriptEditUseCase = new ScriptEditUseCase(generated_script);
setScriptEditUseCase(newScriptEditUseCase);
// 获取解析后的故事详情
const storyDetails = newScriptEditUseCase.getStoryDetails();
setSynopsis(storyDetails.synopsis || "");
setCategories(storyDetails.categories || []);
setProtagonist(storyDetails.protagonist || "");
setIncitingIncident(storyDetails.incitingIncident || "");
setProblem(storyDetails.problem || "");
setConflict(storyDetails.conflict || "");
setStakes(storyDetails.stakes || "");
setCharacterArc(storyDetails.characterArc || "");
} catch (error) {
console.error('初始化项目剧本失败:', error);
throw error;
} finally {
setLoading(false);
}
}, []);
},
[]
);
/**
*
@ -220,7 +277,6 @@ export const useScriptService = (): UseScriptService => {
}
await scriptEditUseCase.applyScript(projectId, planId);
} catch (error) {
console.error("应用剧本失败:", error);
throw error;
@ -234,7 +290,6 @@ export const useScriptService = (): UseScriptService => {
*/
const abortVideoTaskHandler = useCallback(async (): Promise<void> => {
try {
if (!projectId || !planId) {
throw new Error("项目ID或计划ID未设置");
}
@ -242,7 +297,7 @@ export const useScriptService = (): UseScriptService => {
// 调用中断视频任务API
const response = await abortVideoTask({
project_id: projectId,
plan_id: planId
plan_id: planId,
});
if (!response.successful) {
@ -250,7 +305,6 @@ export const useScriptService = (): UseScriptService => {
}
console.log("视频任务中断成功");
} catch (error) {
console.error("中断视频任务失败:", error);
throw error;
@ -258,85 +312,113 @@ export const useScriptService = (): UseScriptService => {
}, [projectId, planId]);
// 封装的setter函数同时更新hook状态和scriptEditUseCase中的值对象
const setSynopsisWrapper = useCallback((value: SetStateAction<string>) => {
const newValue = typeof value === 'function' ? value(synopsis) : value;
setSynopsis(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('synopsis', newValue);
}
}, [synopsis, scriptEditUseCase]);
const setSynopsisWrapper = useCallback(
(value: SetStateAction<string>) => {
const newValue = typeof value === "function" ? value(synopsis) : value;
setSynopsis(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("synopsis", newValue);
}
},
[synopsis, scriptEditUseCase]
);
const setCategoriesWrapper = useCallback((value: SetStateAction<string[]>) => {
const newValue = typeof value === 'function' ? value(categories) : value;
setCategories(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('categories', newValue);
}
}, [categories, scriptEditUseCase]);
const setCategoriesWrapper = useCallback(
(value: SetStateAction<string[]>) => {
const newValue = typeof value === "function" ? value(categories) : value;
setCategories(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("categories", newValue);
}
},
[categories, scriptEditUseCase]
);
const setProtagonistWrapper = useCallback((value: SetStateAction<string>) => {
const newValue = typeof value === 'function' ? value(protagonist) : value;
setProtagonist(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('protagonist', newValue);
}
}, [protagonist, scriptEditUseCase]);
const setProtagonistWrapper = useCallback(
(value: SetStateAction<string>) => {
const newValue = typeof value === "function" ? value(protagonist) : value;
setProtagonist(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("protagonist", newValue);
}
},
[protagonist, scriptEditUseCase]
);
const setIncitingIncidentWrapper = useCallback((value: SetStateAction<string>) => {
const newValue = typeof value === 'function' ? value(incitingIncident) : value;
setIncitingIncident(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('incitingIncident', newValue);
}
}, [incitingIncident, scriptEditUseCase]);
const setIncitingIncidentWrapper = useCallback(
(value: SetStateAction<string>) => {
const newValue =
typeof value === "function" ? value(incitingIncident) : value;
setIncitingIncident(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("incitingIncident", newValue);
}
},
[incitingIncident, scriptEditUseCase]
);
const setProblemWrapper = useCallback((value: SetStateAction<string>) => {
const newValue = typeof value === 'function' ? value(problem) : value;
setProblem(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('problem', newValue);
}
}, [problem, scriptEditUseCase]);
const setProblemWrapper = useCallback(
(value: SetStateAction<string>) => {
const newValue = typeof value === "function" ? value(problem) : value;
setProblem(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("problem", newValue);
}
},
[problem, scriptEditUseCase]
);
const setConflictWrapper = useCallback((value: SetStateAction<string>) => {
const newValue = typeof value === 'function' ? value(conflict) : value;
setConflict(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('conflict', newValue);
}
}, [conflict, scriptEditUseCase]);
const setConflictWrapper = useCallback(
(value: SetStateAction<string>) => {
const newValue = typeof value === "function" ? value(conflict) : value;
setConflict(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("conflict", newValue);
}
},
[conflict, scriptEditUseCase]
);
const setStakesWrapper = useCallback((value: SetStateAction<string>) => {
const newValue = typeof value === 'function' ? value(stakes) : value;
setStakes(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('stakes', newValue);
}
}, [stakes, scriptEditUseCase]);
const setStakesWrapper = useCallback(
(value: SetStateAction<string>) => {
const newValue = typeof value === "function" ? value(stakes) : value;
setStakes(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("stakes", newValue);
}
},
[stakes, scriptEditUseCase]
);
const setCharacterArcWrapper = useCallback((value: SetStateAction<string>) => {
const newValue = typeof value === 'function' ? value(characterArc) : value;
setCharacterArc(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField('characterArc', newValue);
}
}, [characterArc, scriptEditUseCase]);
const setCharacterArcWrapper = useCallback(
(value: SetStateAction<string>) => {
const newValue =
typeof value === "function" ? value(characterArc) : value;
setCharacterArc(newValue);
if (scriptEditUseCase) {
scriptEditUseCase.updateStoryField("characterArc", newValue);
}
},
[characterArc, scriptEditUseCase]
);
/**
*
*/
const focusHandler = useCallback(async (field: ScriptEditKey): Promise<void> => {
try {
// 如果当前已经有聚焦的字段,先处理暂停/继续逻辑
const focusHandler = useCallback(
async (field: ScriptEditKey): Promise<void> => {
try {
// 如果当前已经有聚焦的字段,先处理暂停/继续逻辑
// 设置新的聚焦字段
setFocusedField(field);
} catch (error) {
console.error("聚焦处理失败:", error);
throw error;
}
}, []);
// 设置新的聚焦字段
setFocusedField(field);
} catch (error) {
console.error("聚焦处理失败:", error);
throw error;
}
},
[]
);
/**
*
@ -372,7 +454,6 @@ export const useScriptService = (): UseScriptService => {
if (projectId) {
await scriptEditUseCase.saveScript(projectId);
}
} catch (error) {
console.error("增强剧本失败:", error);
throw error;
@ -382,18 +463,35 @@ export const useScriptService = (): UseScriptService => {
}, [scriptEditUseCase, synopsis, focusedField, aiOptimizing, projectId]);
// 在ScriptService中添加一个方法来获取渲染数据
const scriptBlocksMemo = useMemo((): ScriptBlock[] => {
return [
parseScriptBlock('synopsis', 'Logline', synopsis || ''),
parseScriptBlock('categories', 'GENRE', categories.join(', ') || ''),
parseScriptBlock('protagonist', 'Core Identity', protagonist || ''),
parseScriptBlock('incitingIncident', 'The Inciting Incident', incitingIncident || ''),
parseScriptBlock('problem', 'The Problem & New Goal', problem || ''),
parseScriptBlock('conflict', 'Conflict & Obstacles', conflict || ''),
parseScriptBlock('stakes', 'The Stakes', stakes || ''),
parseScriptBlock('characterArc', 'Character Arc Accomplished', characterArc || '')
];
}, [synopsis, categories, protagonist, incitingIncident, problem, conflict, stakes, characterArc]);
const scriptBlocksMemo = useMemo((): ScriptBlock[] => {
return [
parseScriptBlock("synopsis", "Logline", synopsis || ""),
parseScriptBlock("categories", "GENRE", categories.join(", ") || ""),
parseScriptBlock("protagonist", "Core Identity", protagonist || ""),
parseScriptBlock(
"incitingIncident",
"The Inciting Incident",
incitingIncident || ""
),
parseScriptBlock("problem", "The Problem & New Goal", problem || ""),
parseScriptBlock("conflict", "Conflict & Obstacles", conflict || ""),
parseScriptBlock("stakes", "The Stakes", stakes || ""),
parseScriptBlock(
"characterArc",
"Character Arc Accomplished",
characterArc || ""
),
];
}, [
synopsis,
categories,
protagonist,
incitingIncident,
problem,
conflict,
stakes,
characterArc,
]);
return {
// 响应式状态
@ -419,6 +517,9 @@ const scriptBlocksMemo = useMemo((): ScriptBlock[] => {
focusHandler,
enhanceScript,
setAiOptimizing,
setProjectId,
setPlanId,
createMovieProjectV1,
// 封装的set函数
setSynopsis: setSynopsisWrapper,
setCategories: setCategoriesWrapper,

View File

@ -38,7 +38,8 @@ describe("ScriptService 业务逻辑测试", () => {
script,
"user123",
"auto",
"720p"
"720p",
"en"
);
expect(createRes.project_id).toBeDefined();
projectId = createRes.project_id;

View File

@ -126,7 +126,8 @@ export class ScriptEditUseCase {
prompt: string,
userId: string ,
mode: "auto" | "manual" = "auto",
resolution: "720p" | "1080p" | "4k" = "720p"
resolution: "720p" | "1080p" | "4k" = "720p",
language: string
) {
try {
// 调用创建项目API
@ -135,6 +136,7 @@ export class ScriptEditUseCase {
user_id: userId,
mode,
resolution,
language,
});
if (!response.successful) {