重构视频流和交互服务,使用 TagValueObject 替代 TagEntity。更新相关方法和接口以保持一致性。移除未使用的 TextEditUseCase。增强角色和场景编辑功能。

This commit is contained in:
海龙 2025-08-08 20:21:23 +08:00
parent 067295a2bc
commit 46bda04605
14 changed files with 340 additions and 221 deletions

View File

@ -7,8 +7,8 @@ import {
RoleEntity, RoleEntity,
SceneEntity, SceneEntity,
VideoSegmentEntity, VideoSegmentEntity,
TagEntity,
} from "@/app/service/domain/Entities"; } from "@/app/service/domain/Entities";
import { TagValueObject } from "@/app/service/domain/valueObject";
import { import {
ContentItem, ContentItem,
LensType, LensType,
@ -252,8 +252,8 @@ export const getVideoJson = async (data: {
export const regenerateRole = async (request: { export const regenerateRole = async (request: {
/** 角色提示词 */ /** 角色提示词 */
prompt: string; prompt: string;
/** 标签类型列表 */ /** 标签列表 */
tagTypes: (number | string)[]; tagTypes: TagValueObject[];
/** 角色ID可选如果重新生成现有角色 */ /** 角色ID可选如果重新生成现有角色 */
roleId?: string; roleId?: string;
}): Promise<ApiResponse<RoleEntity>> => { }): Promise<ApiResponse<RoleEntity>> => {
@ -318,7 +318,7 @@ export const getRoleData = async (request: {
/** AI文本数据 */ /** AI文本数据 */
text: AITextEntity; text: AITextEntity;
/** 标签列表 */ /** 标签列表 */
tags: TagEntity[]; tags: TagValueObject[];
}> }>
> => { > => {
return post<ApiResponse<any>>("/movie/get_role_data", request); return post<ApiResponse<any>>("/movie/get_role_data", request);
@ -358,7 +358,7 @@ export const updateTag = async (request: {
tagId: string; tagId: string;
/** 新的标签内容 */ /** 新的标签内容 */
content: string | number; content: string | number;
}): Promise<ApiResponse<TagEntity>> => { }): Promise<ApiResponse<TagValueObject>> => {
return post<ApiResponse<any>>("/movie/update_tag", request); return post<ApiResponse<any>>("/movie/update_tag", request);
}; };
@ -384,8 +384,8 @@ export const updateText = async (request: {
export const regenerateScene = async (request: { export const regenerateScene = async (request: {
/** 场景提示词 */ /** 场景提示词 */
prompt: string; prompt: string;
/** 标签类型列表 */ /** 标签列表 */
tagTypes: (number | string)[]; tagTypes: TagValueObject[];
/** 场景ID可选如果重新生成现有场景 */ /** 场景ID可选如果重新生成现有场景 */
sceneId?: string; sceneId?: string;
}): Promise<ApiResponse<SceneEntity>> => { }): Promise<ApiResponse<SceneEntity>> => {
@ -419,7 +419,7 @@ export const getSceneData = async (request: {
/** AI文本数据 */ /** AI文本数据 */
text: AITextEntity; text: AITextEntity;
/** 标签列表 */ /** 标签列表 */
tags: TagEntity[]; tags: TagValueObject[];
}> }>
> => { > => {
return post<ApiResponse<any>>("/movie/get_scene_data", request); return post<ApiResponse<any>>("/movie/get_scene_data", request);
@ -493,7 +493,7 @@ export const getShotData = async (request: {
/** AI文本数据 */ /** AI文本数据 */
text: AITextEntity; text: AITextEntity;
/** 标签列表 */ /** 标签列表 */
tags: TagEntity[]; tags: TagValueObject[];
}> }>
> => { > => {
return post<ApiResponse<any>>("/movie/get_shot_data", request); return post<ApiResponse<any>>("/movie/get_shot_data", request);

View File

@ -1,5 +1,6 @@
import { useState, useCallback, useMemo } from 'react'; 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 { RoleItem, TagItem, TextItem } from '../domain/Item';
import { RoleEditUseCase } from '../usecase/RoleEditUseCase'; import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
import { TagEditUseCase } from '../usecase/TagEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase';

View File

@ -1,5 +1,5 @@
import { useState, useCallback, useMemo } from 'react'; 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 { SceneItem, TagItem, TextItem } from '../domain/Item';
import { SceneEditUseCase } from '../usecase/SceneEditUseCase'; import { SceneEditUseCase } from '../usecase/SceneEditUseCase';
import { TagEditUseCase } from '../usecase/TagEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase';

View File

@ -9,8 +9,6 @@ import { ScriptEditUseCase, ScriptEditKey } from "../usecase/ScriptEditUseCase";
import { import {
getProjectScript, getProjectScript,
abortVideoTask, abortVideoTask,
pausePlanFlow,
resumePlanFlow,
} from "../../../api/video_flow"; } from "../../../api/video_flow";
import { parseScriptBlock } from "../domain/service"; import { parseScriptBlock } from "../domain/service";
import { ScriptBlock } from "@/components/script-renderer/types"; import { ScriptBlock } from "@/components/script-renderer/types";

View File

@ -2,6 +2,7 @@ import { useState, useCallback } from "react";
import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase"; import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
import { VideoSegmentEntity } from "../domain/Entities"; import { VideoSegmentEntity } from "../domain/Entities";
import { LensType } from "../domain/valueObject"; import { LensType } from "../domain/valueObject";
import { getUploadToken, uploadToQiniu } from "@/api/common";
/** /**
* Hook接口 * Hook接口
@ -40,6 +41,8 @@ export interface UseShotService {
addNewLens: () => void; addNewLens: () => void;
/** 删除指定镜头 */ /** 删除指定镜头 */
deleteLens: (lensName: string) => void; deleteLens: (lensName: string) => void;
/** 获取视频当前帧并上传到七牛云 */
filterRole: (video: HTMLVideoElement) => Promise<string>;
} }
/** /**
@ -165,9 +168,9 @@ export const useShotService = (): UseShotService => {
* *
*/ */
const abortOperation = useCallback((): void => { const abortOperation = useCallback((): void => {
vidoEditUseCase.abortOperation(); // vidoEditUseCase.abortOperation();
setLoading(false); setLoading(false);
}, [vidoEditUseCase]); }, []);
/** /**
* *
@ -244,6 +247,54 @@ export const useShotService = (): UseShotService => {
}); });
}, [selectedSegment]); }, [selectedSegment]);
/**
* ,
* @param video HTML视频元素
* @returns Promise<string>
*/
const filterRole = useCallback(async (video: HTMLVideoElement): Promise<string> => {
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<Blob>((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 { return {
// 响应式状态 // 响应式状态
loading, loading,
@ -257,5 +308,6 @@ export const useShotService = (): UseShotService => {
setSelectedSegment: setSelectedSegmentHandler, setSelectedSegment: setSelectedSegmentHandler,
addNewLens, addNewLens,
deleteLens, deleteLens,
filterRole,
}; };
}; };

View File

@ -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 { export interface RoleEntity extends BaseEntity {
/** 角色名称 */ /** 角色名称 */
name: string; name: string;
/** 角色提示词Id */ /** 角色提示词 */
generateTextId: string; generateText: string;
/**角色标签 */ /**角色标签 */
tagIds: string[]; tagIds: TagValueObject[];
/** 角色图片URL */ /** 角色图片URL */
imageUrl: string; imageUrl: string;
/** 角色是否已存储 */
isStored: boolean;
}
/**
*
*/
export interface TagEntity extends BaseEntity {
/** 标签名称 */
name: string;
/** 内容标签类型 */
content: number | string;
} }
/** /**
@ -63,9 +51,9 @@ export interface SceneEntity extends BaseEntity {
/** 场景图片URL */ /** 场景图片URL */
imageUrl: string; imageUrl: string;
/** 场景标签 */ /** 场景标签 */
tagIds: string[]; tagIds: TagValueObject[];
/** 场景提示词Id */ /** 场景提示词 */
generateTextId: string; generateText: string;
} }
/** /**

View File

@ -2,7 +2,7 @@ import {
BaseEntity, BaseEntity,
AITextEntity, AITextEntity,
RoleEntity, RoleEntity,
TagEntity, TagValueObject,
SceneEntity, SceneEntity,
VideoSegmentEntity VideoSegmentEntity
} from './Entities'; } from './Entities';
@ -87,11 +87,11 @@ export class RoleItem extends EditItem<RoleEntity> {
/** /**
* *
*/ */
export class TagItem extends EditItem<TagEntity> { export class TagItem extends EditItem<TagValueObject> {
type: ItemType.TEXT = ItemType.TEXT; type: ItemType.TEXT = ItemType.TEXT;
constructor( constructor(
entity: TagEntity, entity: TagValueObject,
metadata: Record<string, any> = {} metadata: Record<string, any> = {}
) { ) {
super(entity, metadata); super(entity, metadata);

View File

@ -79,6 +79,17 @@ export class LensType {
} }
} }
/**
*
*/
export interface TagValueObject {
/** 标签名称 */
name: string;
/** 内容标签类型 */
content: number | string;
}
/** /**
* DDD概念的值对象 * DDD概念的值对象
* *

View File

@ -3,7 +3,7 @@ import { SceneEditUseCase } from '../usecase/SceneEditUseCase';
import { TextEditUseCase } from '../usecase/TextEditUseCase'; import { TextEditUseCase } from '../usecase/TextEditUseCase';
import { TagEditUseCase } from '../usecase/TagEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase';
import { SceneItem, TextItem, TagItem } from '../domain/Item'; 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模块 // Mock API模块
jest.mock('@/api/video_flow', () => ({ jest.mock('@/api/video_flow', () => ({
@ -53,7 +53,7 @@ describe('SceneService 业务逻辑测试', () => {
disableEdit: false, disableEdit: false,
}; };
const mockTagEntity1: TagEntity = { const mockTagValueObject1: TagValueObject = {
id: 'tag1', id: 'tag1',
name: '场景标签1', name: '场景标签1',
content: '场景标签内容1', content: '场景标签内容1',
@ -62,7 +62,7 @@ describe('SceneService 业务逻辑测试', () => {
disableEdit: false, disableEdit: false,
}; };
const mockTagEntity2: TagEntity = { const mockTagValueObject2: TagValueObject = {
id: 'tag2', id: 'tag2',
name: '场景标签2', name: '场景标签2',
content: '场景标签内容2', content: '场景标签内容2',
@ -167,7 +167,7 @@ describe('SceneService 业务逻辑测试', () => {
successful: true, successful: true,
data: { data: {
text: mockTextEntity, text: mockTextEntity,
tags: [mockTagEntity1, mockTagEntity2], tags: [mockTagValueObject1, mockTagValueObject2],
}, },
}); });
@ -176,7 +176,7 @@ describe('SceneService 业务逻辑测试', () => {
expect(getSceneData).toHaveBeenCalledWith({ sceneId: 'scene1' }); expect(getSceneData).toHaveBeenCalledWith({ sceneId: 'scene1' });
expect(result.successful).toBe(true); expect(result.successful).toBe(true);
expect(result.data.text).toEqual(mockTextEntity); 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 () => { it('应该成功修改标签内容', async () => {
const updatedTagEntity = { ...mockTagEntity1, content: '更新后的场景标签' }; const updatedTagValueObject = { ...mockTagValueObject1, content: '更新后的场景标签' };
(updateTag as jest.Mock).mockResolvedValue({ (updateTag as jest.Mock).mockResolvedValue({
successful: true, successful: true,
data: updatedTagEntity, data: updatedTagValueObject,
}); });
const result = await updateTag({ const result = await updateTag({
@ -319,14 +319,14 @@ describe('SceneService 业务逻辑测试', () => {
successful: true, successful: true,
data: { data: {
text: mockTextEntity, text: mockTextEntity,
tags: [mockTagEntity1, mockTagEntity2], tags: [mockTagValueObject1, mockTagValueObject2],
}, },
}); });
const sceneDataResult = await getSceneData({ sceneId: 'scene1' }); const sceneDataResult = await getSceneData({ sceneId: 'scene1' });
expect(sceneDataResult.successful).toBe(true); expect(sceneDataResult.successful).toBe(true);
expect(sceneDataResult.data.text).toEqual(mockTextEntity); expect(sceneDataResult.data.text).toEqual(mockTextEntity);
expect(sceneDataResult.data.tags).toEqual([mockTagEntity1, mockTagEntity2]); expect(sceneDataResult.data.tags).toEqual([mockTagValueObject1, mockTagValueObject2]);
// 模拟用户操作:修改场景提示词 // 模拟用户操作:修改场景提示词
const updatedTextEntity = { ...mockTextEntity, content: '修改后的场景提示词' }; const updatedTextEntity = { ...mockTextEntity, content: '修改后的场景提示词' };
@ -356,10 +356,10 @@ describe('SceneService 业务逻辑测试', () => {
expect(optimizedContentResult).toBe(optimizedContent); expect(optimizedContentResult).toBe(optimizedContent);
// 模拟用户操作:修改标签 // 模拟用户操作:修改标签
const updatedTagEntity = { ...mockTagEntity1, content: '修改后的标签内容' }; const updatedTagValueObject = { ...mockTagValueObject1, content: '修改后的标签内容' };
(updateTag as jest.Mock).mockResolvedValue({ (updateTag as jest.Mock).mockResolvedValue({
successful: true, successful: true,
data: updatedTagEntity, data: updatedTagValueObject,
}); });
const updateTagResult = await updateTag({ const updateTagResult = await updateTag({
@ -435,7 +435,7 @@ describe('SceneService 业务逻辑测试', () => {
successful: true, successful: true,
data: { data: {
text: mockTextEntity, text: mockTextEntity,
tags: [mockTagEntity1], tags: [mockTagValueObject1],
}, },
}); });
@ -612,7 +612,7 @@ describe('SceneService 业务逻辑测试', () => {
}); });
it('TagEditUseCase应该正确初始化', () => { it('TagEditUseCase应该正确初始化', () => {
const tagItem = new TagItem(mockTagEntity1); const tagItem = new TagItem(mockTagValueObject1);
const useCase = new TagEditUseCase(tagItem); const useCase = new TagEditUseCase(tagItem);
expect(TagEditUseCase).toHaveBeenCalledWith(tagItem); expect(TagEditUseCase).toHaveBeenCalledWith(tagItem);
@ -637,11 +637,11 @@ describe('SceneService 业务逻辑测试', () => {
expect(textItem.disableEdit).toBe(false); expect(textItem.disableEdit).toBe(false);
}); });
it('TagItem应该正确包装TagEntity', () => { it('TagItem应该正确包装TagValueObject', () => {
const tagItem = new TagItem(mockTagEntity1); const tagItem = new TagItem(mockTagValueObject1);
expect(TagItem).toHaveBeenCalledWith(mockTagEntity1); expect(TagItem).toHaveBeenCalledWith(mockTagValueObject1);
expect(tagItem.entity).toEqual(mockTagEntity1); expect(tagItem.entity).toEqual(mockTagValueObject1);
expect(tagItem.disableEdit).toBe(false); expect(tagItem.disableEdit).toBe(false);
}); });
}); });
@ -695,7 +695,7 @@ describe('SceneService 业务逻辑测试', () => {
}); });
it('应该验证标签实体的完整性', () => { it('应该验证标签实体的完整性', () => {
const tagItem = new TagItem(mockTagEntity1); const tagItem = new TagItem(mockTagValueObject1);
expect(tagItem.entity.id).toBe('tag1'); expect(tagItem.entity.id).toBe('tag1');
expect(tagItem.entity.name).toBe('场景标签1'); expect(tagItem.entity.name).toBe('场景标签1');

View File

@ -1,90 +1,191 @@
import { RoleEntity, AITextEntity, TagEntity } from '../domain/Entities'; import { RoleEntity } from '../domain/Entities';
import { RoleItem, TagItem, TextItem } from '../domain/Item'; import { TagValueObject } from '../domain/valueObject';
import { regenerateRole, applyRoleToShots, getRoleData } from '@/api/video_flow'; import {
applyRoleToShots,
getRoleList,
getUserRoleLibrary,
getRoleData,
regenerateRole,
getRoleShots,
replaceRole
} from '@/api/video_flow';
import { TagEditUseCase } from './TagEditUseCase';
/** /**
* *
* *
*/ */
export class RoleEditUseCase { 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 projectId ID
* @param {TagItem[]} tags * @returns Promise<RoleEntity[]>
* @return {*}
*/ */
async AIgenerateRole(prompt: TextItem, tags: TagItem[]): Promise<RoleEntity> { async getRoleList(projectId: string): Promise<RoleEntity[]> {
const promptText = prompt.entity.content; try {
const tagList = tags.map((tag) => tag.entity.content); const response = await getRoleList({ projectId });
if (response.successful) {
// 调用重新生成角色接口 return response.data;
const response = await regenerateRole({ } else {
roleId: this.roleItem.entity.id||'', throw new Error(response.message || '获取角色列表失败');
prompt: promptText, }
tagTypes: tagList, } catch (error) {
}); console.error('获取角色列表失败:', error);
throw error;
if (response.successful) {
const roleEntity = response.data;
this.roleItem.setEntity(roleEntity);
return roleEntity;
} else {
throw new Error(`重新生成角色失败: ${response.message}`);
} }
} }
/**
*
* @returns Promise<RoleEntity[]>
*/
async getRoleLibraryList(): Promise<RoleEntity[]> {
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<void> {
await this.selectedRoleTags.updateTag(tagName, newContent);
}
/**
*
* @param roleId ID
*/
async selectRole(roleId: string): Promise<void> {
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<RoleEntity>
*/
async AIgenerateRole(prompt: string, tags: TagValueObject[]): Promise<RoleEntity> {
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 shotIds ID列表
* @param roleId ID
* @returns * @returns
*/ */
async applyRole(shotIds: string[]) { async applyRole(shotIds: string[], roleId: string) {
const roleId = this.roleItem.entity.id; try {
return await applyRoleToShots({ const response = await applyRoleToShots({
roleId, roleId,
shotIds, shotIds,
}); });
if (response.successful) {
return response.data;
} else {
throw new Error(response.message || '应用角色到分镜失败');
}
} catch (error) {
console.error('应用角色到分镜失败:', error);
throw error;
}
} }
/** /**
* *
* @description AI文本和标签数据 * @param roleId ID
* @returns Promise<{ text: AITextEntity; tags: TagEntity[] }> AI文本和标签数据 * @returns
* @throws {Error} API调用失败时抛出错误
*/ */
async refreshRoleData(): Promise<{ text: AITextEntity; tags: TagEntity[] }> { async getRoleShotsList(roleId: string) {
const roleId = this.roleItem.entity.id; try {
const response = await getRoleShots({ roleId });
if (!roleId) { if (response.successful) {
throw new Error('角色ID不存在无法获取角色数据'); return response.data;
} } else {
throw new Error(response.message || '获取角色分镜列表失败');
const response = await getRoleData({ }
roleId: roleId } catch (error) {
}); console.error('获取角色分镜列表失败:', error);
throw error;
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}`);
} }
} }
/**
*
* @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;
}
}
} }

View File

@ -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 { SceneItem, TagItem, TextItem } from '../domain/Item';
import { regenerateScene, applySceneToShots, getSceneData } from '@/api/video_flow'; import { regenerateScene, applySceneToShots, getSceneData } from '@/api/video_flow';
@ -53,10 +53,10 @@ export class SceneEditUseCase {
/** /**
* *
* @description AI文本和标签数据 * @description AI文本和标签数据
* @returns Promise<{ text: AITextEntity; tags: TagEntity[] }> AI文本和标签数据 * @returns Promise<{ text: AITextEntity; tags: TagValueObject[] }> AI文本和标签数据
* @throws {Error} API调用失败时抛出错误 * @throws {Error} API调用失败时抛出错误
*/ */
async refreshSceneData(): Promise<{ text: AITextEntity; tags: TagEntity[] }> { async refreshSceneData(): Promise<{ text: AITextEntity; tags: TagValueObject[] }> {
const sceneId = this.sceneItem.entity.id; const sceneId = this.sceneItem.entity.id;
if (!sceneId) { if (!sceneId) {

View File

@ -12,7 +12,6 @@ import {
*/ */
export class VideoSegmentEditUseCase { export class VideoSegmentEditUseCase {
private loading: boolean = false; private loading: boolean = false;
private abortController: AbortController | null = null;
/** /**
* @description * @description
@ -55,9 +54,6 @@ export class VideoSegmentEditUseCase {
try { try {
this.loading = true; this.loading = true;
// 创建新的中断控制器
this.abortController = new AbortController();
const response = await regenerateShot({ const response = await regenerateShot({
shotId, shotId,
shotPrompt, shotPrompt,
@ -71,15 +67,8 @@ export class VideoSegmentEditUseCase {
return response.data; return response.data;
} catch (error) { } catch (error) {
if (this.abortController?.signal.aborted) {
console.log("视频片段重新生成被中断");
throw new Error("操作被中断");
}
console.error("重新生成视频片段失败:", error); console.error("重新生成视频片段失败:", error);
throw error; throw error;
} finally {
this.loading = false;
this.abortController = null;
} }
} }
@ -117,15 +106,7 @@ export class VideoSegmentEditUseCase {
this.loading = false; this.loading = false;
} }
} }
/**
* @description
*/
abortOperation(): void {
if (this.abortController) {
this.abortController.abort();
this.loading = false;
}
}
/** /**
* @description * @description

View File

@ -1,40 +1,81 @@
import { TagItem } from '../domain/Item';
import { updateTag } from '@/api/video_flow'; import { TagValueObject } from '../domain/valueObject';
/** /**
* *
* *
*/ */
export class TagEditUseCase { export class TagEditUseCase {
constructor(private readonly tagItem: TagItem) { constructor(public tagList: TagValueObject[]) {
this.tagItem = tagItem;
} }
/** /**
* *
* @param tagName
* @param newContent * @param newContent
* @returns
*/ */
async updateTag(newContent: string|number): Promise<TagItem> { async updateTag(tagName: string, newContent: string | number): Promise<void> {
if (!this.tagItem) { const tag = this.tagList.find(tag => tag.name === tagName);
throw new Error('标签项未初始化'); if (tag) {
} tag.content = newContent;
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}`);
} }
} }
/**
*
* @param tagName
* @param content
*/
async addTag(tagName: string, content: string | number = ""): Promise<void> {
// 检查标签是否已存在
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<void> {
const tagIndex = this.tagList.findIndex(tag => tag.name === tagName);
if (tagIndex === -1) {
throw new Error(`标签 "${tagName}" 不存在`);
}
this.tagList.splice(tagIndex, 1);
}
/**
*
*/
async clearAllTags(): Promise<void> {
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);
}
} }

View File

@ -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<TextItem> {
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<string> {
if (!this.textItem) {
throw new Error('没有内容可优化');
}
return this.textItem.entity.content;
}
}