forked from 77media/video-flow
490 lines
15 KiB
TypeScript
490 lines
15 KiB
TypeScript
import { useState, useCallback, useMemo } from "react";
|
||
import { RoleEntity, AITextEntity } from "../domain/Entities";
|
||
import { RoleEditUseCase } from "../usecase/RoleEditUseCase";
|
||
import { getUploadToken, uploadToQiniu } from "@/api/common";
|
||
import {
|
||
checkShotVideoStatus,
|
||
} from "@/api/video_flow";
|
||
import { SaveEditUseCase } from "../usecase/SaveEditUseCase";
|
||
|
||
/**
|
||
* 角色服务Hook返回值接口
|
||
*/
|
||
interface UseRoleService {
|
||
// 响应式数据
|
||
/** 角色列表 */
|
||
roleList: RoleEntity[];
|
||
/** 当前选中的角色 */
|
||
selectedRole: RoleEntity | null;
|
||
/** 当前角色的AI文本 */
|
||
currentRoleText: string | null;
|
||
/** 角色图片URL */
|
||
roleImageUrl: string;
|
||
/** 用户角色库 */
|
||
userRoleLibrary: RoleEntity[];
|
||
// 操作方法
|
||
/** 获取角色列表 */
|
||
fetchRoleList: (projectId: string) => Promise<void>;
|
||
/** 选择角色 */
|
||
selectRole: (role: RoleEntity) => Promise<void>;
|
||
/** 优化AI文本 */
|
||
optimizeRoleText: (userSuggestion: string) => Promise<void>;
|
||
/** 修改AI文本 */
|
||
updateRoleText: (newContent: string) => Promise<void>;
|
||
/** 重新生成角色 */
|
||
regenerateRole: () => Promise<void>;
|
||
/** 获取用户角色库 */
|
||
fetchUserRoleLibrary: (generateText?: string) => Promise<void>;
|
||
/** 替换角色 */
|
||
replaceRoleWithLibrary: (replaceRoleId: string) => Promise<void>;
|
||
/** 上传图片并更新角色信息 */
|
||
uploadImageAndUpdateRole: (file: File) => Promise<void>;
|
||
/** 保存重新生成的角色到角色库 */
|
||
saveRoleToLibrary: () => Promise<void>;
|
||
/** 保存数据 */
|
||
saveData: () => Promise<void>;
|
||
/** 切换标签页回调函数 */
|
||
changeTabCallback: (callback: (isChange: Boolean) => void) => void;
|
||
}
|
||
|
||
/**
|
||
* 角色服务Hook
|
||
* 提供角色相关的所有响应式功能和业务逻辑
|
||
*/
|
||
export const useRoleServiceHook = (): UseRoleService => {
|
||
// 响应式状态
|
||
const [roleList, setRoleList] = useState<RoleEntity[]>([]);
|
||
const [selectedRole, setSelectedRole] = useState<RoleEntity | null>(null);
|
||
const [currentRoleText, setCurrentRoleText] = useState<string | null>(null);
|
||
const [userRoleLibrary, setUserRoleLibrary] = useState<RoleEntity[]>([]);
|
||
const [projectId, setProjectId] = useState<string>(""); // 添加项目ID状态
|
||
|
||
// UseCase实例 - 在角色选择时初始化
|
||
const [roleEditUseCase, setRoleEditUseCase] =
|
||
useState<RoleEditUseCase | null>(null);
|
||
|
||
// 计算属性
|
||
/**
|
||
* 角色图片URL
|
||
* @description 获取当前选中角色的图片URL
|
||
*/
|
||
const roleImageUrl = useMemo(() => {
|
||
return selectedRole?.imageUrl || "";
|
||
}, [selectedRole]);
|
||
|
||
/**
|
||
* 获取角色列表
|
||
* @description 根据项目ID获取所有角色列表
|
||
* @param projectId 项目ID
|
||
* @throws {Error} 当API调用失败时抛出错误
|
||
* @returns {Promise<void>} 获取完成后的Promise
|
||
*/
|
||
const fetchRoleList = useCallback(async (projectId: string) => {
|
||
try {
|
||
// 保存项目ID到状态
|
||
setProjectId(projectId);
|
||
|
||
// 初始化角色编辑UseCase实例
|
||
const newRoleEditUseCase = new RoleEditUseCase();
|
||
const roleList = await newRoleEditUseCase.getRoleList(projectId);
|
||
|
||
setRoleList(roleList);
|
||
setRoleEditUseCase(newRoleEditUseCase);
|
||
} catch (error) {
|
||
console.error("获取角色列表失败:", error);
|
||
throw error;
|
||
}
|
||
}, []);
|
||
|
||
/**
|
||
* 选择角色
|
||
* @description 根据角色ID选择角色,并初始化相关的UseCase实例
|
||
* @param roleId 角色ID
|
||
*/
|
||
const selectRole = useCallback(
|
||
async (role: RoleEntity) => {
|
||
console.log("selectRole", role);
|
||
setRoleList((prev) => prev.map((r) => (r.name === role.name ? role : r)));
|
||
setSelectedRole(role);
|
||
|
||
// 调用selectRole方法
|
||
roleEditUseCase!.selectRole(role);
|
||
},
|
||
[roleEditUseCase]
|
||
);
|
||
|
||
/**
|
||
* 优化AI文本
|
||
* @description 对当前角色的AI文本进行优化
|
||
* @throws {Error} 当没有可优化的文本内容或UseCase未初始化时抛出错误
|
||
* @returns {Promise<void>} 优化完成后的Promise
|
||
*/
|
||
const optimizeRoleText = useCallback(async () => {
|
||
if (!roleEditUseCase) {
|
||
throw new Error("角色编辑UseCase未初始化");
|
||
}
|
||
|
||
if (!currentRoleText) {
|
||
throw new Error("没有可优化的文本内容");
|
||
}
|
||
|
||
if (!selectedRole) {
|
||
throw new Error("没有选中的角色");
|
||
}
|
||
|
||
try {
|
||
const { optimizedDescription, keywords } =
|
||
await roleEditUseCase.optimizeRoleDescription(selectedRole);
|
||
setCurrentRoleText(optimizedDescription);
|
||
// 更新角色列表中的对应角色描述和标签
|
||
setRoleList((prev) =>
|
||
prev.map((role) =>
|
||
role.id === selectedRole?.id
|
||
? {
|
||
...role,
|
||
generateText: optimizedDescription,
|
||
tags: keywords.map((keyword) => ({
|
||
id: `tag_${Date.now()}_${Math.random()
|
||
.toString(36)
|
||
.substr(2, 9)}`,
|
||
/** 名称 */
|
||
name: keyword,
|
||
/** 内容 */
|
||
content: keyword,
|
||
})),
|
||
}
|
||
: role
|
||
)
|
||
);
|
||
|
||
// 更新当前选中角色
|
||
if (selectedRole) {
|
||
selectRole({
|
||
...selectedRole,
|
||
generateText: optimizedDescription,
|
||
tags: keywords.map((keyword) => ({
|
||
id: `tag_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||
/** 名称 */
|
||
name: keyword,
|
||
/** 内容 */
|
||
content: keyword,
|
||
})),
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error("优化角色文本失败:", error);
|
||
throw error;
|
||
}
|
||
}, [roleEditUseCase, currentRoleText, selectedRole, selectRole]);
|
||
|
||
/**
|
||
* 修改AI文本
|
||
* @description 手动修改当前角色的AI文本内容
|
||
* @param newContent 新的文本内容
|
||
* @throws {Error} 当没有可编辑的文本或UseCase未初始化时抛出错误
|
||
* @returns {Promise<void>} 修改完成后的Promise
|
||
*/
|
||
const updateRoleText = useCallback(
|
||
async (newContent: string) => {
|
||
if (!roleEditUseCase) {
|
||
throw new Error("角色编辑UseCase未初始化");
|
||
}
|
||
|
||
setCurrentRoleText(newContent);
|
||
|
||
// 更新角色列表中的对应角色描述
|
||
setRoleList((prev) =>
|
||
prev?.map((role) =>
|
||
role.id === selectedRole?.id
|
||
? { ...role, generateText: newContent }
|
||
: role
|
||
)
|
||
);
|
||
|
||
// 更新当前选中角色
|
||
if (selectedRole) {
|
||
selectRole({ ...selectedRole, generateText: newContent });
|
||
}
|
||
},
|
||
[roleEditUseCase, selectRole, selectedRole]
|
||
);
|
||
|
||
/**
|
||
* 重新生成角色
|
||
* @description 使用AI文本重新生成角色
|
||
* @throws {Error} 当缺少重新生成角色所需的数据或UseCase未初始化时抛出错误
|
||
* @returns {Promise<void>} 重新生成完成后的Promise
|
||
*/
|
||
const regenerateRole = useCallback(async () => {
|
||
if (!roleEditUseCase) {
|
||
throw new Error("角色编辑UseCase未初始化");
|
||
}
|
||
|
||
if (!selectedRole || !currentRoleText) {
|
||
throw new Error("缺少重新生成角色所需的数据");
|
||
}
|
||
|
||
if (!projectId) {
|
||
throw new Error("缺少项目ID,无法重新生成角色");
|
||
}
|
||
|
||
try {
|
||
const newRoleEntity = await roleEditUseCase.AIgenerateRole(
|
||
projectId,
|
||
selectedRole.name,
|
||
currentRoleText
|
||
);
|
||
selectRole(newRoleEntity);
|
||
|
||
// 更新角色列表
|
||
setRoleList((prev) =>
|
||
prev.map((role) =>
|
||
role.id === newRoleEntity.id ? newRoleEntity : role
|
||
)
|
||
);
|
||
} catch (error) {
|
||
console.error("重新生成角色失败:", error);
|
||
throw error;
|
||
}
|
||
}, [roleEditUseCase, selectedRole, currentRoleText, projectId, selectRole]);
|
||
|
||
/**
|
||
* 获取用户角色库
|
||
* @description 获取当前用户的角色库列表
|
||
* @throws {Error} 当API调用失败时抛出错误
|
||
* @returns {Promise<void>} 获取完成后的Promise
|
||
*/
|
||
const fetchUserRoleLibrary = useCallback(async (generateText?: string) => {
|
||
try {
|
||
let useCase = roleEditUseCase;
|
||
|
||
// 如果没有初始化RoleEditUseCase,创建一个新的实例
|
||
if (!useCase) {
|
||
useCase = new RoleEditUseCase();
|
||
setRoleEditUseCase(useCase);
|
||
}
|
||
|
||
// 从localStorage获取当前用户信息
|
||
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
||
|
||
if (!User.id) {
|
||
throw new Error("无法获取用户ID,请重新登录");
|
||
}
|
||
|
||
// 获取当前选中角色的描述
|
||
const userDescription =generateText|| selectedRole?.generateText || "";
|
||
|
||
const roleLibraryList = await useCase!.getRoleLibraryList(
|
||
User.id,
|
||
userDescription
|
||
);
|
||
setUserRoleLibrary(roleLibraryList);
|
||
} catch (error) {
|
||
console.error("获取用户角色库失败:", error);
|
||
throw error;
|
||
}
|
||
}, [roleEditUseCase, selectedRole]);
|
||
|
||
/**
|
||
* 替换角色
|
||
* @description 使用角色库中的角色替换当前角色,并重新获取角色详细数据
|
||
* @param replaceRoleId 替换的角色ID
|
||
* @throws {Error} 当未选择角色、API调用失败或UseCase未初始化时抛出错误
|
||
* @returns {Promise<void>} 替换完成后的Promise
|
||
*/
|
||
const replaceRoleWithLibrary = useCallback(
|
||
async (replaceRoleId: string) => {
|
||
if (!selectedRole) {
|
||
throw new Error("请先选择角色");
|
||
}
|
||
|
||
if (!roleEditUseCase) {
|
||
throw new Error("角色编辑UseCase未初始化");
|
||
}
|
||
|
||
try {
|
||
await roleEditUseCase.replaceRoleById(selectedRole.id, replaceRoleId);
|
||
|
||
// 更新角色列表
|
||
const libraryRole = userRoleLibrary.find(
|
||
(role) => role.id === replaceRoleId
|
||
);
|
||
if (libraryRole) {
|
||
const updatedRole = {
|
||
...selectedRole,
|
||
name: libraryRole.name,
|
||
generateText: libraryRole.generateText,
|
||
imageUrl: libraryRole.imageUrl,
|
||
fromDraft: false,
|
||
isChangeRole: true
|
||
};
|
||
|
||
selectRole(updatedRole);
|
||
setRoleList((prev) =>
|
||
prev.map((role) =>
|
||
role.id === selectedRole.id ? updatedRole : role
|
||
)
|
||
);
|
||
}
|
||
} catch (error) {
|
||
console.error("替换角色失败:", error);
|
||
throw error;
|
||
}
|
||
},
|
||
[selectedRole, roleEditUseCase, userRoleLibrary, selectRole]
|
||
);
|
||
|
||
/**
|
||
* 上传图片并更新角色信息
|
||
* @description 上传图片文件到七牛云存储,然后调用AI接口分析图片生成描述和高亮标签,最后更新当前选中角色的图片、描述和标签
|
||
* @param file 要上传的文件
|
||
* @returns {Promise<void>} 上传和分析完成后的Promise
|
||
*/
|
||
const uploadImageAndUpdateRole = useCallback(
|
||
async (file: File) => {
|
||
if (!selectedRole) {
|
||
throw new Error("请先选择要更新的角色");
|
||
}
|
||
|
||
if (!roleEditUseCase) {
|
||
throw new Error("角色编辑UseCase未初始化");
|
||
}
|
||
|
||
try {
|
||
// 1. 上传图片到七牛云
|
||
const { token } = await getUploadToken();
|
||
const imageUrl = await uploadToQiniu(file, token);
|
||
|
||
// 2. 调用用例中的图片分析方法
|
||
const updatedRole = await roleEditUseCase.analyzeImageAndUpdateRole(imageUrl, selectedRole);
|
||
|
||
// 3. 更新选中的角色
|
||
selectRole(updatedRole);
|
||
|
||
// 更新角色列表中的对应角色
|
||
setRoleList((prev) =>
|
||
prev.map((role) => (role.id === selectedRole.id ? updatedRole : role))
|
||
);
|
||
|
||
console.log("角色图片和描述更新成功:", updatedRole);
|
||
} catch (error) {
|
||
console.error("上传图片并分析失败:", error);
|
||
throw error;
|
||
}
|
||
},
|
||
[selectedRole, roleEditUseCase, selectRole]
|
||
);
|
||
|
||
/**
|
||
* 保存重新生成的角色到角色库
|
||
* @description 将当前选中的角色保存到角色库中
|
||
* @throws {Error} 当没有选中角色、UseCase未初始化或缺少项目ID时抛出错误
|
||
* @returns {Promise<void>} 保存完成后的Promise
|
||
*/
|
||
const saveRoleToLibrary = useCallback(async () => {
|
||
if (!selectedRole) {
|
||
throw new Error("请先选择要保存的角色");
|
||
}
|
||
|
||
if (!roleEditUseCase) {
|
||
throw new Error("角色编辑UseCase未初始化");
|
||
}
|
||
|
||
if (!projectId) {
|
||
throw new Error("缺少项目ID,无法保存角色到角色库");
|
||
}
|
||
|
||
try {
|
||
// 从localStorage获取当前用户信息
|
||
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
||
|
||
if (!User.id) {
|
||
throw new Error("无法获取用户ID,请重新登录");
|
||
}
|
||
|
||
// 调用保存重新生成角色到角色库的方法
|
||
const result = await roleEditUseCase.saveRegeneratedCharacterToLibrary(
|
||
selectedRole,
|
||
projectId,
|
||
String(User.id)
|
||
);
|
||
selectRole({
|
||
...selectedRole,
|
||
id: result.character_id,
|
||
fromDraft: true,
|
||
});
|
||
|
||
SaveEditUseCase.setCharacterId([
|
||
...SaveEditUseCase.characterId,
|
||
{
|
||
character_id: result.character_id,
|
||
name: selectedRole.name,
|
||
},
|
||
]);
|
||
console.log("角色保存到角色库成功:", result);
|
||
} catch (error) {
|
||
console.error("保存角色到角色库失败:", error);
|
||
throw error;
|
||
}
|
||
}, [selectedRole, roleEditUseCase, projectId]);
|
||
|
||
/**
|
||
* 保存数据
|
||
* @description 调用接口保存当前项目数据
|
||
* @throws {Error} 当缺少项目ID或API调用失败时抛出错误
|
||
* @returns {Promise<void>} 保存完成后的Promise
|
||
*/
|
||
const saveData = useCallback(async () => {
|
||
if (!projectId) {
|
||
throw new Error("缺少项目ID,无法保存数据");
|
||
}
|
||
|
||
try {
|
||
const result = await checkShotVideoStatus({
|
||
project_id: projectId,
|
||
});
|
||
|
||
if (!result.successful) {
|
||
throw new Error(`保存数据失败: ${result.message}`);
|
||
}
|
||
|
||
console.log("数据保存成功");
|
||
} catch (error) {
|
||
console.error("保存数据失败:", error);
|
||
throw error;
|
||
}
|
||
}, [projectId]);
|
||
|
||
/**
|
||
* @description 切换标签页回调函数,传入当前角色是否更改
|
||
* @param callback 回调函数,接收当前角色是否更改
|
||
*/
|
||
const changeTabCallback = useCallback((callback: (isChange: Boolean) => void) => {
|
||
|
||
// 执行回调函数,传入已更改的角色列表
|
||
callback(selectedRole!.isChangeRole);
|
||
}, [selectedRole]);
|
||
|
||
return {
|
||
// 响应式数据
|
||
roleList,
|
||
selectedRole,
|
||
currentRoleText,
|
||
roleImageUrl,
|
||
userRoleLibrary,
|
||
|
||
// 操作方法
|
||
fetchRoleList,
|
||
selectRole,
|
||
optimizeRoleText,
|
||
updateRoleText,
|
||
regenerateRole,
|
||
fetchUserRoleLibrary,
|
||
replaceRoleWithLibrary,
|
||
uploadImageAndUpdateRole,
|
||
saveRoleToLibrary,
|
||
saveData,
|
||
changeTabCallback,
|
||
};
|
||
};
|