import { useState, useCallback, useMemo } from 'react'; import { RoleEntity, TagEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities'; import { RoleItem, TagItem, TextItem, ShotItem } from '../domain/Item'; import { RoleEditUseCase } from '../usecase/RoleEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase'; import { TextEditUseCase } from '../usecase/TextEditUseCase'; import { getRoleShots, getRoleData, getRoleList, getUserRoleLibrary, replaceRole } from '@/api/video_flow'; /** * 分镜选择项接口 */ interface ShotSelectionItem { /** 分镜ID */ id: string; /** 分镜名称 */ name: string; /** 是否已选中 */ selected: boolean; /** 是否已应用角色 */ applied: boolean; /** 分镜数据 */ shot: VideoSegmentEntity; } /** * 角色服务Hook返回值接口 */ interface UseRoleService { // 响应式数据 /** 角色列表 */ roleList: RoleItem[]; /** 当前选中的角色 */ selectedRole: RoleItem | null; /** 当前角色的AI文本 */ currentRoleText: TextItem | null; /** 当前角色的标签列表 */ currentRoleTags: TagItem[]; /** 角色图片URL */ roleImageUrl: string; /** 分镜选择列表 */ shotSelectionList: ShotSelectionItem[]; /** 是否全选分镜 */ isAllShotsSelected: boolean; /** 已选中的分镜数量 */ selectedShotsCount: number; /** 用户角色库 */ userRoleLibrary: RoleItem[]; // 操作方法 /** 获取角色列表 */ fetchRoleList: (projectId: string) => Promise; /** 选择角色 */ selectRole: (roleId: string) => void; /** 初始化当前选中角色的AI文本和标签数据 */ initializeRoleData: () => Promise; /** 优化AI文本 */ optimizeRoleText: () => Promise; /** 修改AI文本 */ updateRoleText: (newContent: string) => Promise; /** 修改标签内容 */ updateTagContent: (tagId: string, newContent: string | number) => Promise; /** 重新生成角色 */ regenerateRole: () => Promise; /** 获取角色出现的分镜列表 */ fetchRoleShots: () => Promise; /** 切换全选与全不选 */ toggleSelectAllShots: () => void; /** 选择/取消选择单个分镜 */ toggleShotSelection: (shotId: string) => void; /** 应用角色到选中的分镜 */ applyRoleToSelectedShots: () => Promise; /** 获取用户角色库 */ fetchUserRoleLibrary: () => Promise; /** 替换角色 */ replaceRoleWithLibrary: (replaceRoleId: string) => Promise; } /** * 角色服务Hook * 提供角色相关的所有响应式功能和业务逻辑 */ export const useRoleServiceHook = (): UseRoleService => { // 响应式状态 const [roleList, setRoleList] = useState([]); const [selectedRole, setSelectedRole] = useState(null); const [currentRoleText, setCurrentRoleText] = useState(null); const [currentRoleTags, setCurrentRoleTags] = useState([]); const [shotSelectionList, setShotSelectionList] = useState([]); const [userRoleLibrary, setUserRoleLibrary] = useState([]); // UseCase实例 - 在角色选择时初始化 const [roleEditUseCase, setRoleEditUseCase] = useState(null); const [textEditUseCase, setTextEditUseCase] = useState(null); const [tagEditUseCases, setTagEditUseCases] = useState>(new Map()); // 计算属性 /** * 角色图片URL * @description 获取当前选中角色的图片URL */ const roleImageUrl = useMemo(() => { return selectedRole?.entity.imageUrl || ''; }, [selectedRole]); /** * 是否全选分镜 * @description 判断是否所有分镜都被选中 */ const isAllShotsSelected = useMemo(() => { return shotSelectionList.length > 0 && shotSelectionList.every(shot => shot.selected); }, [shotSelectionList]); /** * 已选中的分镜数量 * @description 获取当前选中的分镜数量 */ const selectedShotsCount = useMemo(() => { return shotSelectionList.filter(shot => shot.selected).length; }, [shotSelectionList]); /** * 获取角色列表 * @description 根据项目ID获取所有角色列表 * @param projectId 项目ID * @throws {Error} 当API调用失败时抛出错误 * @returns {Promise} 获取完成后的Promise */ const fetchRoleList = useCallback(async (projectId: string) => { try { const response = await getRoleList({ projectId: projectId }); if (response.successful) { const roleItems = response.data.map(role => new RoleItem(role)); setRoleList(roleItems); } else { throw new Error(`获取角色列表失败: ${response.message}`); } } catch (error) { console.error('获取角色列表失败:', error); throw error; } }, []); /** * 选择角色 * @description 根据角色ID选择角色,并初始化相关的UseCase实例 * @param roleId 角色ID */ const selectRole = useCallback(async (roleId: string) => { const role = roleList.find(r => r.entity.id === roleId); if (role) { setSelectedRole(role); // 初始化角色编辑UseCase实例 setRoleEditUseCase(new RoleEditUseCase(role)); // 清空文本和标签相关状态 setTextEditUseCase(null); setTagEditUseCases(new Map()); setCurrentRoleText(null); setCurrentRoleTags([]); await initializeRoleData(); } }, [roleList]); /** * 初始化角色数据 * @description 初始化当前选中角色的AI文本和标签数据 * @throws {Error} 当未选择角色或API调用失败时抛出错误 * @returns {Promise} 初始化完成后的Promise */ const initializeRoleData = useCallback(async () => { if (!selectedRole) { throw new Error('请先选择角色'); } try { const response = await getRoleData({ roleId: selectedRole.entity.id }); if (response.successful) { const { text, tags } = response.data; const textItem = new TextItem(text); const tagItems = tags.map(tag => new TagItem(tag)); // 设置当前角色的AI文本和标签 setCurrentRoleText(textItem); setCurrentRoleTags(tagItems); // 初始化文本UseCase setTextEditUseCase(new TextEditUseCase(textItem)); // 初始化标签UseCase const newTagEditUseCases = new Map(); tagItems.forEach(tag => { newTagEditUseCases.set(tag.entity.id, new TagEditUseCase(tag)); }); setTagEditUseCases(newTagEditUseCases); } else { throw new Error(`获取角色数据失败: ${response.message}`); } } catch (error) { console.error('初始化角色数据失败:', error); throw error; } }, [selectedRole]); /** * 优化AI文本 * @description 对当前角色的AI文本进行优化,无文本时不可进行优化 * @throws {Error} 当没有可优化的文本内容或UseCase未初始化时抛出错误 * @returns {Promise} 优化完成后的Promise */ const optimizeRoleText = useCallback(async () => { if (!textEditUseCase) { throw new Error('文本编辑UseCase未初始化'); } if (!currentRoleText || !currentRoleText.entity.content) { throw new Error('没有可优化的文本内容'); } const optimizedContent = await textEditUseCase.getOptimizedContent(); // 更新文本内容 const updatedTextItem = await textEditUseCase.updateText(optimizedContent); setCurrentRoleText(updatedTextItem); }, [textEditUseCase, currentRoleText]); /** * 修改AI文本 * @description 手动修改当前角色的AI文本内容 * @param newContent 新的文本内容 * @throws {Error} 当没有可编辑的文本或UseCase未初始化时抛出错误 * @returns {Promise} 修改完成后的Promise */ const updateRoleText = useCallback(async (newContent: string) => { if (!textEditUseCase) { throw new Error('文本编辑UseCase未初始化'); } if (!currentRoleText) { throw new Error('没有可编辑的文本'); } const updatedTextItem = await textEditUseCase.updateText(newContent); setCurrentRoleText(updatedTextItem); }, [textEditUseCase, currentRoleText]); /** * 修改标签内容 * @description 修改指定标签的内容 * @param tagId 标签ID * @param newContent 新的标签内容 * @throws {Error} 当标签不存在或UseCase未初始化时抛出错误 * @returns {Promise} 修改完成后的Promise */ const updateTagContent = useCallback(async (tagId: string, newContent: string | number) => { const tagEditUseCase = tagEditUseCases.get(tagId); if (!tagEditUseCase) { throw new Error(`标签编辑UseCase未初始化,标签ID: ${tagId}`); } const updatedTagItem = await tagEditUseCase.updateTag(newContent); // 更新标签列表 setCurrentRoleTags(prev => prev.map(tag => tag.entity.id === tagId ? updatedTagItem : tag ) ); }, [tagEditUseCases]); /** * 重新生成角色 * @description 使用AI文本和标签重新生成角色 * @throws {Error} 当缺少重新生成角色所需的数据或UseCase未初始化时抛出错误 * @returns {Promise} 重新生成完成后的Promise */ const regenerateRole = useCallback(async () => { if (!roleEditUseCase) { throw new Error('角色编辑UseCase未初始化'); } if (!selectedRole || !currentRoleText || currentRoleTags.length === 0) { throw new Error('缺少重新生成角色所需的数据'); } const newRoleEntity = await roleEditUseCase.AIgenerateRole(currentRoleText, currentRoleTags); // 更新角色 const newRoleItem = new RoleItem(newRoleEntity); setSelectedRole(newRoleItem); // 更新角色列表 setRoleList(prev => prev.map(role => role.entity.id === newRoleEntity.id ? newRoleItem : role ) ); }, [roleEditUseCase, selectedRole, currentRoleText, currentRoleTags]); /** * 获取角色出现的分镜列表 * @description 获取当前角色应用到的分镜列表,包括已应用状态 * @throws {Error} 当未选择角色或API调用失败时抛出错误 * @returns {Promise} 获取完成后的Promise */ const fetchRoleShots = useCallback(async () => { if (!selectedRole) { throw new Error('请先选择角色'); } try { const response = await getRoleShots({ roleId: selectedRole.entity.id }); if (response.successful) { const { shots, appliedShotIds } = response.data; const shotSelectionItems: ShotSelectionItem[] = shots.map(shot => ({ id: shot.id, name: shot.name, selected: false, applied: appliedShotIds.includes(shot.id), // 根据API返回的已应用列表判断 shot })); setShotSelectionList(shotSelectionItems); } else { throw new Error(`获取角色分镜列表失败: ${response.message}`); } } catch (error) { console.error('获取角色分镜列表失败:', error); throw error; } }, [selectedRole]); /** * 切换全选与全不选 * @description 如果当前是全选状态则全不选,否则全选 */ const toggleSelectAllShots = useCallback(() => { setShotSelectionList(prev => { const isAllSelected = prev.length > 0 && prev.every(shot => shot.selected); return prev.map(shot => ({ ...shot, selected: !isAllSelected })); }); }, []); /** * 选择/取消选择单个分镜 * @description 切换指定分镜的选择状态 * @param shotId 分镜ID */ const toggleShotSelection = useCallback((shotId: string) => { setShotSelectionList(prev => prev.map(shot => shot.id === shotId ? { ...shot, selected: !shot.selected } : shot ) ); }, []); /** * 应用角色到选中的分镜 * @description 将当前角色应用到选中的分镜,并更新应用状态 * @throws {Error} 当未选择角色、未选择分镜或UseCase未初始化时抛出错误 * @returns {Promise} 应用完成后的Promise */ const applyRoleToSelectedShots = useCallback(async () => { if (!roleEditUseCase) { throw new Error('角色编辑UseCase未初始化'); } if (!selectedRole) { throw new Error('请先选择角色'); } const selectedShotIds = shotSelectionList .filter(shot => shot.selected) .map(shot => shot.id); if (selectedShotIds.length === 0) { throw new Error('请先选择要应用的分镜'); } await roleEditUseCase.applyRole(selectedShotIds); // 更新分镜列表,标记已应用 setShotSelectionList(prev => prev.map(shot => selectedShotIds.includes(shot.id) ? { ...shot, applied: true, selected: false } : shot ) ); }, [roleEditUseCase, selectedRole, shotSelectionList]); /** * 获取用户角色库 * @description 获取当前用户的角色库列表 * @throws {Error} 当API调用失败时抛出错误 * @returns {Promise} 获取完成后的Promise */ const fetchUserRoleLibrary = useCallback(async () => { try { const response = await getUserRoleLibrary(); if (response.successful) { const roleItems = response.data.map(role => new RoleItem(role)); setUserRoleLibrary(roleItems); } else { throw new Error(`获取用户角色库失败: ${response.message}`); } } catch (error) { console.error('获取用户角色库失败:', error); throw error; } }, []); /** * 替换角色 * @description 使用角色库中的角色替换当前角色,并重新获取角色详细数据 * @param replaceRoleId 替换的角色ID * @throws {Error} 当未选择角色、API调用失败或UseCase未初始化时抛出错误 * @returns {Promise} 替换完成后的Promise */ const replaceRoleWithLibrary = useCallback(async (replaceRoleId: string) => { if (!selectedRole) { throw new Error('请先选择角色'); } if (!roleEditUseCase) { throw new Error('角色编辑UseCase未初始化'); } try { const response = await replaceRole({ currentRoleId: selectedRole.entity.id, replaceRoleId: replaceRoleId }); if (response.successful) { // 重新获取当前角色的详细数据 await initializeRoleData(); } else { throw new Error(`替换角色失败: ${response.message}`); } } catch (error) { console.error('替换角色失败:', error); throw error; } }, [selectedRole, roleEditUseCase, initializeRoleData]); return { // 响应式数据 /** 角色列表 */ roleList, /** 当前选中的角色 */ selectedRole, /** 当前角色的AI文本 */ currentRoleText, /** 当前角色的标签列表 */ currentRoleTags, /** 角色图片URL */ roleImageUrl, /** 分镜选择列表 */ shotSelectionList, /** 是否全选分镜 */ isAllShotsSelected, /** 已选中的分镜数量 */ selectedShotsCount, /** 用户角色库 */ userRoleLibrary, // 操作方法 /** 获取角色列表 */ fetchRoleList, /** 选择角色 */ selectRole, /** 初始化当前选中角色的AI文本和标签数据 */ initializeRoleData, /** 优化AI文本 */ optimizeRoleText, /** 修改AI文本 */ updateRoleText, /** 修改标签内容 */ updateTagContent, /** 重新生成角色 */ regenerateRole, /** 获取角色出现的分镜列表 */ fetchRoleShots, /** 切换全选与全不选 */ toggleSelectAllShots, /** 选择/取消选择单个分镜 */ toggleShotSelection, /** 应用角色到选中的分镜 */ applyRoleToSelectedShots, /** 获取用户角色库 */ fetchUserRoleLibrary, /** 替换角色 */ replaceRoleWithLibrary, }; };