diff --git a/api/video_flow.ts b/api/video_flow.ts index e5c2a78..f1661e4 100644 --- a/api/video_flow.ts +++ b/api/video_flow.ts @@ -7,8 +7,8 @@ import { RoleEntity, SceneEntity, VideoSegmentEntity, - TagEntity, } from "@/app/service/domain/Entities"; +import { TagValueObject } from "@/app/service/domain/valueObject"; import { ContentItem, LensType, @@ -252,8 +252,8 @@ export const getVideoJson = async (data: { export const regenerateRole = async (request: { /** 角色提示词 */ prompt: string; - /** 标签类型列表 */ - tagTypes: (number | string)[]; + /** 标签列表 */ + tagTypes: TagValueObject[]; /** 角色ID(可选,如果重新生成现有角色) */ roleId?: string; }): Promise> => { @@ -318,7 +318,7 @@ export const getRoleData = async (request: { /** AI文本数据 */ text: AITextEntity; /** 标签列表 */ - tags: TagEntity[]; + tags: TagValueObject[]; }> > => { return post>("/movie/get_role_data", request); @@ -358,7 +358,7 @@ export const updateTag = async (request: { tagId: string; /** 新的标签内容 */ content: string | number; -}): Promise> => { +}): Promise> => { return post>("/movie/update_tag", request); }; @@ -384,8 +384,8 @@ export const updateText = async (request: { export const regenerateScene = async (request: { /** 场景提示词 */ prompt: string; - /** 标签类型列表 */ - tagTypes: (number | string)[]; + /** 标签列表 */ + tagTypes: TagValueObject[]; /** 场景ID(可选,如果重新生成现有场景) */ sceneId?: string; }): Promise> => { @@ -419,7 +419,7 @@ export const getSceneData = async (request: { /** AI文本数据 */ text: AITextEntity; /** 标签列表 */ - tags: TagEntity[]; + tags: TagValueObject[]; }> > => { return post>("/movie/get_scene_data", request); @@ -493,7 +493,7 @@ export const getShotData = async (request: { /** AI文本数据 */ text: AITextEntity; /** 标签列表 */ - tags: TagEntity[]; + tags: TagValueObject[]; }> > => { return post>("/movie/get_shot_data", request); diff --git a/app/service/Interaction/RoleService.ts b/app/service/Interaction/RoleService.ts index 173a8e4..8914963 100644 --- a/app/service/Interaction/RoleService.ts +++ b/app/service/Interaction/RoleService.ts @@ -1,5 +1,6 @@ import { useState, useCallback, useMemo } from 'react'; -import { RoleEntity, TagEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities'; +import { RoleEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities'; +import { TagValueObject } from '../domain/valueObject'; import { RoleItem, TagItem, TextItem } from '../domain/Item'; import { RoleEditUseCase } from '../usecase/RoleEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase'; diff --git a/app/service/Interaction/SceneService.ts b/app/service/Interaction/SceneService.ts index 134feee..71b66d6 100644 --- a/app/service/Interaction/SceneService.ts +++ b/app/service/Interaction/SceneService.ts @@ -1,5 +1,5 @@ import { useState, useCallback, useMemo } from 'react'; -import { SceneEntity, TagEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities'; +import { SceneEntity, TagValueObject, AITextEntity, VideoSegmentEntity } from '../domain/Entities'; import { SceneItem, TagItem, TextItem } from '../domain/Item'; import { SceneEditUseCase } from '../usecase/SceneEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase'; diff --git a/app/service/Interaction/ScriptService.ts b/app/service/Interaction/ScriptService.ts index 9071141..3e2d780 100644 --- a/app/service/Interaction/ScriptService.ts +++ b/app/service/Interaction/ScriptService.ts @@ -9,8 +9,6 @@ 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"; diff --git a/app/service/Interaction/ShotService.ts b/app/service/Interaction/ShotService.ts index 653bbe0..6eb393c 100644 --- a/app/service/Interaction/ShotService.ts +++ b/app/service/Interaction/ShotService.ts @@ -2,6 +2,7 @@ import { useState, useCallback } from "react"; import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase"; import { VideoSegmentEntity } from "../domain/Entities"; import { LensType } from "../domain/valueObject"; +import { getUploadToken, uploadToQiniu } from "@/api/common"; /** * 视频片段服务Hook接口 @@ -40,6 +41,8 @@ export interface UseShotService { addNewLens: () => void; /** 删除指定镜头 */ deleteLens: (lensName: string) => void; + /** 获取视频当前帧并上传到七牛云 */ + filterRole: (video: HTMLVideoElement) => Promise; } /** @@ -165,9 +168,9 @@ export const useShotService = (): UseShotService => { * 中断当前操作 */ const abortOperation = useCallback((): void => { - vidoEditUseCase.abortOperation(); + // vidoEditUseCase.abortOperation(); setLoading(false); - }, [vidoEditUseCase]); + }, []); /** * 设置选中的视频片段 @@ -244,6 +247,54 @@ export const useShotService = (): UseShotService => { }); }, [selectedSegment]); + /** + * 获取视频当前帧的画面,上传到七牛云,并返回七牛云的图片地址,然后调用接口识别出里面的人物信息,返回人物信息 + * @param video HTML视频元素 + * @returns Promise 七牛云的图片地址 + */ + const filterRole = useCallback(async (video: HTMLVideoElement): Promise => { + try { + // 创建canvas元素来截取视频帧 + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + if (!ctx) { + throw new Error('无法获取canvas上下文'); + } + + // 设置canvas尺寸为视频尺寸 + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + + // 将当前视频帧绘制到canvas上 + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + + // 将canvas转换为blob + const blob = await new Promise((resolve, reject) => { + canvas.toBlob((blob) => { + if (blob) { + resolve(blob); + } else { + reject(new Error('无法将canvas转换为blob')); + } + }, 'image/png'); + }); + + // 创建File对象 + const file = new File([blob], `frame_${Date.now()}.png`, { type: 'image/png' }); + + // 获取上传token + const { token } = await getUploadToken(); + + // 上传到七牛云 + const imageUrl = await uploadToQiniu(file, token); + + return imageUrl; + } catch (error) { + console.error('获取视频帧失败:', error); + throw error; + } + }, []); return { // 响应式状态 loading, @@ -257,5 +308,6 @@ export const useShotService = (): UseShotService => { setSelectedSegment: setSelectedSegmentHandler, addNewLens, deleteLens, + filterRole, }; }; diff --git a/app/service/domain/Entities.ts b/app/service/domain/Entities.ts index b0d80cc..8d99bf8 100644 --- a/app/service/domain/Entities.ts +++ b/app/service/domain/Entities.ts @@ -3,7 +3,7 @@ * 所有实体都应该实现这些基础接口 */ -import { ContentItem, LensType } from "./valueObject"; +import { ContentItem, LensType, TagValueObject } from "./valueObject"; /** * 基础实体接口 @@ -34,24 +34,12 @@ export interface AITextEntity extends BaseEntity { export interface RoleEntity extends BaseEntity { /** 角色名称 */ name: string; - /** 角色提示词Id */ - generateTextId: string; + /** 角色提示词 */ + generateText: string; /**角色标签 */ - tagIds: string[]; + tagIds: TagValueObject[]; /** 角色图片URL */ imageUrl: string; - /** 角色是否已存储 */ - isStored: boolean; -} - -/** - * 标签实体接口 - */ -export interface TagEntity extends BaseEntity { - /** 标签名称 */ - name: string; - /** 内容标签类型 */ - content: number | string; } /** @@ -63,9 +51,9 @@ export interface SceneEntity extends BaseEntity { /** 场景图片URL */ imageUrl: string; /** 场景标签 */ - tagIds: string[]; - /** 场景提示词Id */ - generateTextId: string; + tagIds: TagValueObject[]; + /** 场景提示词 */ + generateText: string; } /** diff --git a/app/service/domain/Item.ts b/app/service/domain/Item.ts index a8f7239..758347a 100644 --- a/app/service/domain/Item.ts +++ b/app/service/domain/Item.ts @@ -2,7 +2,7 @@ import { BaseEntity, AITextEntity, RoleEntity, - TagEntity, + TagValueObject, SceneEntity, VideoSegmentEntity } from './Entities'; @@ -87,11 +87,11 @@ export class RoleItem extends EditItem { /** * 标签可编辑项 */ -export class TagItem extends EditItem { +export class TagItem extends EditItem { type: ItemType.TEXT = ItemType.TEXT; constructor( - entity: TagEntity, + entity: TagValueObject, metadata: Record = {} ) { super(entity, metadata); diff --git a/app/service/domain/valueObject.ts b/app/service/domain/valueObject.ts index 36d8b06..c798ed5 100644 --- a/app/service/domain/valueObject.ts +++ b/app/service/domain/valueObject.ts @@ -79,6 +79,17 @@ export class LensType { } } +/** + * 标签实体接口 + */ +export interface TagValueObject { + /** 标签名称 */ + name: string; + /** 内容标签类型 */ + content: number | string; +} + + /** * 这是一个符合DDD概念的值对象,代表故事的核心细节。 * 它封装了故事的四个关键元素:梗概、分类、主角和框架内容。 diff --git a/app/service/test/Scene.test.ts b/app/service/test/Scene.test.ts index 1b7fb0d..6d9e840 100644 --- a/app/service/test/Scene.test.ts +++ b/app/service/test/Scene.test.ts @@ -3,7 +3,7 @@ import { SceneEditUseCase } from '../usecase/SceneEditUseCase'; import { TextEditUseCase } from '../usecase/TextEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase'; import { SceneItem, TextItem, TagItem } from '../domain/Item'; -import { SceneEntity, AITextEntity, TagEntity, VideoSegmentEntity, ShotStatus } from '../domain/Entities'; +import { SceneEntity, AITextEntity, TagValueObject, VideoSegmentEntity, ShotStatus } from '../domain/Entities'; // Mock API模块 jest.mock('@/api/video_flow', () => ({ @@ -53,7 +53,7 @@ describe('SceneService 业务逻辑测试', () => { disableEdit: false, }; - const mockTagEntity1: TagEntity = { + const mockTagValueObject1: TagValueObject = { id: 'tag1', name: '场景标签1', content: '场景标签内容1', @@ -62,7 +62,7 @@ describe('SceneService 业务逻辑测试', () => { disableEdit: false, }; - const mockTagEntity2: TagEntity = { + const mockTagValueObject2: TagValueObject = { id: 'tag2', name: '场景标签2', content: '场景标签内容2', @@ -167,7 +167,7 @@ describe('SceneService 业务逻辑测试', () => { successful: true, data: { text: mockTextEntity, - tags: [mockTagEntity1, mockTagEntity2], + tags: [mockTagValueObject1, mockTagValueObject2], }, }); @@ -176,7 +176,7 @@ describe('SceneService 业务逻辑测试', () => { expect(getSceneData).toHaveBeenCalledWith({ sceneId: 'scene1' }); expect(result.successful).toBe(true); expect(result.data.text).toEqual(mockTextEntity); - expect(result.data.tags).toEqual([mockTagEntity1, mockTagEntity2]); + expect(result.data.tags).toEqual([mockTagValueObject1, mockTagValueObject2]); }); }); @@ -202,10 +202,10 @@ describe('SceneService 业务逻辑测试', () => { }); it('应该成功修改标签内容', async () => { - const updatedTagEntity = { ...mockTagEntity1, content: '更新后的场景标签' }; + const updatedTagValueObject = { ...mockTagValueObject1, content: '更新后的场景标签' }; (updateTag as jest.Mock).mockResolvedValue({ successful: true, - data: updatedTagEntity, + data: updatedTagValueObject, }); const result = await updateTag({ @@ -319,14 +319,14 @@ describe('SceneService 业务逻辑测试', () => { successful: true, data: { text: mockTextEntity, - tags: [mockTagEntity1, mockTagEntity2], + tags: [mockTagValueObject1, mockTagValueObject2], }, }); const sceneDataResult = await getSceneData({ sceneId: 'scene1' }); expect(sceneDataResult.successful).toBe(true); expect(sceneDataResult.data.text).toEqual(mockTextEntity); - expect(sceneDataResult.data.tags).toEqual([mockTagEntity1, mockTagEntity2]); + expect(sceneDataResult.data.tags).toEqual([mockTagValueObject1, mockTagValueObject2]); // 模拟用户操作:修改场景提示词 const updatedTextEntity = { ...mockTextEntity, content: '修改后的场景提示词' }; @@ -356,10 +356,10 @@ describe('SceneService 业务逻辑测试', () => { expect(optimizedContentResult).toBe(optimizedContent); // 模拟用户操作:修改标签 - const updatedTagEntity = { ...mockTagEntity1, content: '修改后的标签内容' }; + const updatedTagValueObject = { ...mockTagValueObject1, content: '修改后的标签内容' }; (updateTag as jest.Mock).mockResolvedValue({ successful: true, - data: updatedTagEntity, + data: updatedTagValueObject, }); const updateTagResult = await updateTag({ @@ -435,7 +435,7 @@ describe('SceneService 业务逻辑测试', () => { successful: true, data: { text: mockTextEntity, - tags: [mockTagEntity1], + tags: [mockTagValueObject1], }, }); @@ -612,7 +612,7 @@ describe('SceneService 业务逻辑测试', () => { }); it('TagEditUseCase应该正确初始化', () => { - const tagItem = new TagItem(mockTagEntity1); + const tagItem = new TagItem(mockTagValueObject1); const useCase = new TagEditUseCase(tagItem); expect(TagEditUseCase).toHaveBeenCalledWith(tagItem); @@ -637,11 +637,11 @@ describe('SceneService 业务逻辑测试', () => { expect(textItem.disableEdit).toBe(false); }); - it('TagItem应该正确包装TagEntity', () => { - const tagItem = new TagItem(mockTagEntity1); + it('TagItem应该正确包装TagValueObject', () => { + const tagItem = new TagItem(mockTagValueObject1); - expect(TagItem).toHaveBeenCalledWith(mockTagEntity1); - expect(tagItem.entity).toEqual(mockTagEntity1); + expect(TagItem).toHaveBeenCalledWith(mockTagValueObject1); + expect(tagItem.entity).toEqual(mockTagValueObject1); expect(tagItem.disableEdit).toBe(false); }); }); @@ -695,7 +695,7 @@ describe('SceneService 业务逻辑测试', () => { }); it('应该验证标签实体的完整性', () => { - const tagItem = new TagItem(mockTagEntity1); + const tagItem = new TagItem(mockTagValueObject1); expect(tagItem.entity.id).toBe('tag1'); expect(tagItem.entity.name).toBe('场景标签1'); diff --git a/app/service/usecase/RoleEditUseCase.ts b/app/service/usecase/RoleEditUseCase.ts index 15a8c37..cb2898f 100644 --- a/app/service/usecase/RoleEditUseCase.ts +++ b/app/service/usecase/RoleEditUseCase.ts @@ -1,90 +1,191 @@ -import { RoleEntity, AITextEntity, TagEntity } from '../domain/Entities'; -import { RoleItem, TagItem, TextItem } from '../domain/Item'; -import { regenerateRole, applyRoleToShots, getRoleData } from '@/api/video_flow'; +import { RoleEntity } from '../domain/Entities'; +import { TagValueObject } from '../domain/valueObject'; +import { + applyRoleToShots, + getRoleList, + getUserRoleLibrary, + getRoleData, + regenerateRole, + getRoleShots, + replaceRole +} from '@/api/video_flow'; +import { TagEditUseCase } from './TagEditUseCase'; /** * 角色图编辑用例 * 负责角色图内容的初始化、修改和优化 */ export class RoleEditUseCase { - constructor(private roleItem: RoleItem) { + roleList: RoleEntity[] = []; + selectedRole: RoleEntity | null = null; + selectedRoleTags:TagEditUseCase = new TagEditUseCase([]); + roleLibraryList: RoleEntity[] = []; + constructor() { } - /** - * @description: 重新生成角色 - * @param {TextItem} prompt - * @param {TagItem[]} tags - * @return {*} + * 获取当前项目角色列表 + * @param projectId 项目ID + * @returns Promise 角色列表 */ - async AIgenerateRole(prompt: TextItem, tags: TagItem[]): Promise { - const promptText = prompt.entity.content; - const tagList = tags.map((tag) => tag.entity.content); - - // 调用重新生成角色接口 - const response = await regenerateRole({ - roleId: this.roleItem.entity.id||'', - prompt: promptText, - tagTypes: tagList, - }); - - if (response.successful) { - const roleEntity = response.data; - this.roleItem.setEntity(roleEntity); - return roleEntity; - } else { - throw new Error(`重新生成角色失败: ${response.message}`); + async getRoleList(projectId: string): Promise { + try { + const response = await getRoleList({ projectId }); + if (response.successful) { + return response.data; + } else { + throw new Error(response.message || '获取角色列表失败'); + } + } catch (error) { + console.error('获取角色列表失败:', error); + throw error; } } + /** + * 获取角色库的角色列表 + * @returns Promise 角色库列表 + */ + async getRoleLibraryList(): Promise { + try { + const response = await getUserRoleLibrary(); + if (response.successful) { + return response.data; + } else { + throw new Error(response.message || '获取角色库失败'); + } + } catch (error) { + console.error('获取角色库失败:', error); + throw error; + } + } + + /** + * 修改某个标签内容 + * @param tagName 标签名称 + * @param newContent 新内容 + */ + async updateTag(tagName: string, newContent: string | number): Promise { + await this.selectedRoleTags.updateTag(tagName, newContent); + } + + /** + * 选中某个角色作为当前活跃角色 + * @param roleId 角色ID + */ + async selectRole(roleId: string): Promise { + try { + // 从已获取的角色列表中查找对应的角色实体 + const roleEntity = this.roleList.find(role => role.id === roleId); + if (roleEntity) { + this.selectedRole = roleEntity; + // 获取角色数据以获取标签信息 + const response = await getRoleData({ roleId }); + if (response.successful) { + this.selectedRoleTags = new TagEditUseCase(response.data.tags); + } else { + throw new Error(response.message || '获取角色标签数据失败'); + } + } else { + throw new Error('未找到对应的角色实体,请先获取角色列表'); + } + } catch (error) { + console.error('选择角色失败:', error); + throw error; + } + + } + + /** + * 重新生成角色 + * @param prompt 角色提示词 + * @param tags 标签列表 + * @returns Promise 重新生成的角色 + */ + async AIgenerateRole(prompt: string, tags: TagValueObject[]): Promise { + try { + // 直接使用当前角色的ID,不做任何处理 + const response = await regenerateRole({ + prompt, + tagTypes: tags, // 直接传递完整的标签列表给后端,让后端处理 + roleId: this.selectedRole?.id + }); + + if (response.successful) { + return response.data; + } else { + throw new Error(response.message || '重新生成角色失败'); + } + } catch (error) { + console.error('重新生成角色失败:', error); + throw error; + } + } /** * 应用此角色到指定分镜 * @param shotIds 分镜ID列表 + * @param roleId 角色ID * @returns 应用结果 */ - async applyRole(shotIds: string[]) { - const roleId = this.roleItem.entity.id; - return await applyRoleToShots({ - roleId, - shotIds, - }); + async applyRole(shotIds: string[], roleId: string) { + try { + const response = await applyRoleToShots({ + roleId, + shotIds, + }); + + if (response.successful) { + return response.data; + } else { + throw new Error(response.message || '应用角色到分镜失败'); + } + } catch (error) { + console.error('应用角色到分镜失败:', error); + throw error; + } } /** - * 重新获取当前角色的数据 - * @description 从服务器重新获取当前角色的AI文本和标签数据,并更新当前实体 - * @returns Promise<{ text: AITextEntity; tags: TagEntity[] }> 角色相关的AI文本和标签数据 - * @throws {Error} 当API调用失败时抛出错误 + * 获取角色应用到的分镜列表 + * @param roleId 角色ID + * @returns 分镜列表和应用状态 */ - async refreshRoleData(): Promise<{ text: AITextEntity; tags: TagEntity[] }> { - const roleId = this.roleItem.entity.id; - - if (!roleId) { - throw new Error('角色ID不存在,无法获取角色数据'); - } - - const response = await getRoleData({ - roleId: roleId - }); - - if (response.successful) { - // 更新当前角色的实体数据 - const { text, tags } = response.data; - - // 更新角色实体中的相关字段 - const updatedRoleEntity = { - ...this.roleItem.entity, - generateTextId: text.id, // 更新AI文本ID - tagIds: tags.map(tag => tag.id), // 更新标签ID列表 - updatedAt: Date.now(), // 更新时间戳 - }; - - // 更新当前UseCase中的实体 - this.roleItem.setEntity(updatedRoleEntity); - - return response.data; - } else { - throw new Error(`获取角色数据失败: ${response.message}`); + async getRoleShotsList(roleId: string) { + try { + const response = await getRoleShots({ roleId }); + if (response.successful) { + return response.data; + } else { + throw new Error(response.message || '获取角色分镜列表失败'); + } + } catch (error) { + console.error('获取角色分镜列表失败:', error); + throw error; } } + + /** + * 替换角色 + * @param currentRoleId 当前角色ID + * @param replaceRoleId 替换的角色ID + * @returns 替换结果 + */ + async replaceRoleById(currentRoleId: string, replaceRoleId: string) { + try { + const response = await replaceRole({ + currentRoleId, + replaceRoleId, + }); + + if (response.successful) { + return response.data; + } else { + throw new Error(response.message || '替换角色失败'); + } + } catch (error) { + console.error('替换角色失败:', error); + throw error; + } + } + } diff --git a/app/service/usecase/SceneEditUseCase.ts b/app/service/usecase/SceneEditUseCase.ts index 34198c7..78a05da 100644 --- a/app/service/usecase/SceneEditUseCase.ts +++ b/app/service/usecase/SceneEditUseCase.ts @@ -1,4 +1,4 @@ -import { SceneEntity, AITextEntity, TagEntity } from '../domain/Entities'; +import { SceneEntity, AITextEntity } from '../domain/Entities'; import { SceneItem, TagItem, TextItem } from '../domain/Item'; import { regenerateScene, applySceneToShots, getSceneData } from '@/api/video_flow'; @@ -53,10 +53,10 @@ export class SceneEditUseCase { /** * 重新获取当前场景的数据 * @description 从服务器重新获取当前场景的AI文本和标签数据,并更新当前实体 - * @returns Promise<{ text: AITextEntity; tags: TagEntity[] }> 场景相关的AI文本和标签数据 + * @returns Promise<{ text: AITextEntity; tags: TagValueObject[] }> 场景相关的AI文本和标签数据 * @throws {Error} 当API调用失败时抛出错误 */ - async refreshSceneData(): Promise<{ text: AITextEntity; tags: TagEntity[] }> { + async refreshSceneData(): Promise<{ text: AITextEntity; tags: TagValueObject[] }> { const sceneId = this.sceneItem.entity.id; if (!sceneId) { diff --git a/app/service/usecase/ShotEditUsecase.ts b/app/service/usecase/ShotEditUsecase.ts index 64603d9..f604fd1 100644 --- a/app/service/usecase/ShotEditUsecase.ts +++ b/app/service/usecase/ShotEditUsecase.ts @@ -12,7 +12,6 @@ import { */ export class VideoSegmentEditUseCase { private loading: boolean = false; - private abortController: AbortController | null = null; /** * @description 获取视频片段列表 @@ -55,9 +54,6 @@ export class VideoSegmentEditUseCase { try { this.loading = true; - // 创建新的中断控制器 - this.abortController = new AbortController(); - const response = await regenerateShot({ shotId, shotPrompt, @@ -71,15 +67,8 @@ export class VideoSegmentEditUseCase { return response.data; } catch (error) { - if (this.abortController?.signal.aborted) { - console.log("视频片段重新生成被中断"); - throw new Error("操作被中断"); - } console.error("重新生成视频片段失败:", error); throw error; - } finally { - this.loading = false; - this.abortController = null; } } @@ -117,15 +106,7 @@ export class VideoSegmentEditUseCase { this.loading = false; } } - /** - * @description 中断当前操作 - */ - abortOperation(): void { - if (this.abortController) { - this.abortController.abort(); - this.loading = false; - } - } + /** * @description 获取加载状态 diff --git a/app/service/usecase/TagEditUseCase.ts b/app/service/usecase/TagEditUseCase.ts index 756a987..8e3e5d6 100644 --- a/app/service/usecase/TagEditUseCase.ts +++ b/app/service/usecase/TagEditUseCase.ts @@ -1,40 +1,81 @@ -import { TagItem } from '../domain/Item'; -import { updateTag } from '@/api/video_flow'; + +import { TagValueObject } from '../domain/valueObject'; /** * 标签编辑用例 * 负责标签内容的初始化、修改和优化 */ export class TagEditUseCase { - constructor(private readonly tagItem: TagItem) { - this.tagItem = tagItem; + constructor(public tagList: TagValueObject[]) { } /** * 修改标签内容 + * @param tagName 标签名称 * @param newContent 新内容 - * @returns 更新后的标签项 */ - async updateTag(newContent: string|number): Promise { - if (!this.tagItem) { - throw new Error('标签项未初始化'); - } - - if (this.tagItem.entity.disableEdit) { - throw new Error('标签项已禁用编辑'); - } - // 请求更新接口 - const response = await updateTag({ - tagId: this.tagItem.entity.id, - content: newContent - }); - - if (response.successful) { - this.tagItem.setEntity(response.data); - return this.tagItem; - } else { - throw new Error(`修改标签失败: ${response.message}`); + async updateTag(tagName: string, newContent: string | number): Promise { + const tag = this.tagList.find(tag => tag.name === tagName); + if (tag) { + tag.content = newContent; } } + /** + * 新增标签 + * @param tagName 标签名称 + * @param content 标签内容 + */ + async addTag(tagName: string, content: string | number = ""): Promise { + // 检查标签是否已存在 + const existingTag = this.tagList.find(tag => tag.name === tagName); + if (existingTag) { + throw new Error(`标签 "${tagName}" 已存在`); + } + + // 创建新标签 + const newTag: TagValueObject = { + name: tagName, + content: content + }; + + this.tagList.push(newTag); + } + + /** + * 删除指定标签 + * @param tagName 要删除的标签名称 + */ + async deleteTag(tagName: string): Promise { + const tagIndex = this.tagList.findIndex(tag => tag.name === tagName); + if (tagIndex === -1) { + throw new Error(`标签 "${tagName}" 不存在`); + } + + this.tagList.splice(tagIndex, 1); + } + + /** + * 清空所有标签 + */ + async clearAllTags(): Promise { + this.tagList.length = 0; + } + + /** + * 获取所有标签 + * @returns 标签列表 + */ + getTagList(): TagValueObject[] { + return [...this.tagList]; + } + + /** + * 根据标签名称获取标签 + * @param tagName 标签名称 + * @returns 标签对象或undefined + */ + getTagByName(tagName: string): TagValueObject | undefined { + return this.tagList.find(tag => tag.name === tagName); + } } diff --git a/app/service/usecase/TextEditUseCase.ts b/app/service/usecase/TextEditUseCase.ts deleted file mode 100644 index 73eefb9..0000000 --- a/app/service/usecase/TextEditUseCase.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { TextItem } from '../domain/Item'; -import { updateText } from '@/api/video_flow'; - -/** - * 文本编辑用例 - * 负责单个文本内容的初始化、修改和优化 - */ -export class TextEditUseCase { - constructor(private readonly textItem: TextItem) { - this.textItem = textItem; - } - /** - * 修改文本内容 - * @param newContent 新内容 - * @returns 更新后的文本项 - */ - async updateText(newContent: string): Promise { - if (!this.textItem) { - throw new Error('文本项未初始化,请先调用 initializeText'); - } - - if (this.textItem.entity.disableEdit) { - throw new Error('文本项已禁用编辑'); - } - - const response = await updateText({ - textId: this.textItem.entity.id, - content: newContent - }); - - if (response.successful) { - this.textItem.setEntity(response.data); - return this.textItem; - } else { - throw new Error(`修改文案失败: ${response.message}`); - } - } - - /** - * 获取优化后的文案内容 - * @param optimizationOptions 优化选项 - * @returns 优化后的内容 - */ - async getOptimizedContent( - ): Promise { - if (!this.textItem) { - throw new Error('没有内容可优化'); - } - return this.textItem.entity.content; - } - -} - -