mixvideo-v2/apps/desktop/src/services/materialModelBindingService.ts

342 lines
8.6 KiB
TypeScript

/**
* 素材-模特绑定服务
* 遵循前端开发规范的服务层设计
*/
import { invoke } from '@tauri-apps/api/core';
import { Material } from '../types/material';
import { Model } from '../types/model';
export interface MaterialModelBindingStats {
total_materials: number;
bound_materials: number;
unbound_materials: number;
binding_rate: number;
}
export interface ModelMaterialStatistics {
total_materials: number;
video_count: number;
audio_count: number;
image_count: number;
total_size: number;
}
export interface MaterialUpdateRequest {
model_id?: string;
name?: string;
}
export class MaterialModelBindingService {
/**
* 绑定素材到模特
*/
static async bindMaterialToModel(materialId: string, modelId: string): Promise<void> {
await invoke('associate_material_to_model', {
materialId,
modelId,
});
}
/**
* 解除素材与模特的绑定
*/
static async unbindMaterialFromModel(materialId: string): Promise<void> {
await invoke('disassociate_material_from_model', {
materialId,
});
}
/**
* 批量绑定素材到模特
*/
static async batchBindMaterialsToModel(materialIds: string[], modelId: string): Promise<void> {
await invoke('batch_bind_materials_to_model', {
materialIds,
modelId,
});
}
/**
* 批量解除素材与模特的绑定
*/
static async batchUnbindMaterialsFromModel(materialIds: string[]): Promise<void> {
await invoke('batch_unbind_materials_from_model', {
materialIds,
});
}
/**
* 切换素材的模特绑定
*/
static async switchMaterialModel(materialId: string, newModelId: string): Promise<void> {
await invoke('switch_material_model', {
materialId,
newModelId,
});
}
/**
* 获取模特的所有素材
*/
static async getMaterialsByModel(modelId: string): Promise<Material[]> {
return await invoke<Material[]>('get_materials_by_model_id', {
modelId,
});
}
/**
* 获取未绑定模特的素材
*/
static async getUnboundMaterials(projectId?: string): Promise<Material[]> {
return await invoke<Material[]>('get_unassociated_materials', {
projectId: projectId || null,
});
}
/**
* 获取模特的素材统计信息
*/
static async getModelMaterialStatistics(modelId: string): Promise<ModelMaterialStatistics> {
return await invoke<ModelMaterialStatistics>('get_model_material_statistics', {
modelId,
});
}
/**
* 获取项目中模特绑定统计信息
*/
static async getProjectModelBindingStats(projectId: string): Promise<MaterialModelBindingStats> {
return await invoke<MaterialModelBindingStats>('get_project_model_binding_stats', {
projectId,
});
}
/**
* 获取全局模特绑定统计信息
*/
static async getGlobalModelBindingStats(): Promise<MaterialModelBindingStats> {
return await invoke<MaterialModelBindingStats>('get_global_model_binding_stats');
}
/**
* 更新素材信息
*/
static async updateMaterial(id: string, updates: MaterialUpdateRequest): Promise<void> {
await invoke('update_material', {
id,
updates,
});
}
/**
* 获取所有模特
*/
static async getAllModels(): Promise<Model[]> {
return await invoke<Model[]>('get_all_models');
}
/**
* 搜索模特
*/
static async searchModels(query: string): Promise<Model[]> {
return await invoke<Model[]>('search_models', {
query,
});
}
/**
* 获取项目的所有素材
*/
static async getProjectMaterials(projectId: string): Promise<Material[]> {
return await invoke<Material[]>('get_project_materials', {
projectId,
});
}
/**
* 获取所有素材
*/
static async getAllMaterials(): Promise<Material[]> {
return await invoke<Material[]>('get_all_materials');
}
/**
* 根据过滤条件获取素材
*/
static async getMaterialsByFilter(filter: {
projectId?: string;
modelId?: string;
bound?: boolean;
search?: string;
}): Promise<Material[]> {
let materials: Material[] = [];
// 首先根据主要过滤条件获取素材
if (filter.modelId) {
materials = await this.getMaterialsByModel(filter.modelId);
} else if (filter.bound === false) {
materials = await this.getUnboundMaterials(filter.projectId);
} else if (filter.projectId) {
materials = await this.getProjectMaterials(filter.projectId);
// 如果指定了绑定状态过滤
if (filter.bound === true) {
materials = materials.filter(m => m.model_id);
}
} else {
// 如果没有指定项目,获取所有素材
materials = await this.getAllMaterials();
// 如果指定了绑定状态过滤
if (filter.bound === true) {
materials = materials.filter(m => m.model_id);
} else if (filter.bound === false) {
materials = materials.filter(m => !m.model_id);
}
}
// 应用搜索过滤
if (filter.search && filter.search.trim()) {
const searchLower = filter.search.toLowerCase();
materials = materials.filter(m =>
m.name.toLowerCase().includes(searchLower)
);
}
return materials;
}
/**
* 验证模特是否存在
*/
static async validateModelExists(modelId: string): Promise<boolean> {
try {
const model = await invoke<Model | null>('get_model_by_id', { id: modelId });
return model !== null;
} catch {
return false;
}
}
/**
* 验证素材是否存在
*/
static async validateMaterialExists(materialId: string): Promise<boolean> {
try {
const material = await invoke<Material | null>('get_material_by_id', { id: materialId });
return material !== null;
} catch {
return false;
}
}
/**
* 获取绑定操作的建议
*/
static async getBindingSuggestions(_materialId: string): Promise<Model[]> {
try {
// 这里可以实现基于素材内容的模特推荐逻辑
// 暂时返回所有活跃的模特
const allModels = await this.getAllModels();
return allModels.filter(model => model.is_active);
} catch {
return [];
}
}
/**
* 批量操作验证
*/
static async validateBatchOperation(materialIds: string[], modelId?: string): Promise<{
validMaterials: string[];
invalidMaterials: string[];
modelValid: boolean;
}> {
const validMaterials: string[] = [];
const invalidMaterials: string[] = [];
let modelValid = true;
// 验证素材
for (const materialId of materialIds) {
const exists = await this.validateMaterialExists(materialId);
if (exists) {
validMaterials.push(materialId);
} else {
invalidMaterials.push(materialId);
}
}
// 验证模特
if (modelId) {
modelValid = await this.validateModelExists(modelId);
}
return {
validMaterials,
invalidMaterials,
modelValid,
};
}
/**
* 获取绑定历史统计
*/
static async getBindingHistory(_days: number = 30): Promise<{
daily_bindings: Array<{ date: string; count: number }>;
total_operations: number;
most_active_model: string | null;
}> {
// 这里需要后端支持绑定历史统计
// 暂时返回模拟数据
return {
daily_bindings: [],
total_operations: 0,
most_active_model: null,
};
}
/**
* 导出绑定关系
*/
static async exportBindingRelations(format: 'json' | 'csv' = 'json'): Promise<string> {
try {
// 获取所有绑定关系
const allModels = await this.getAllModels();
const bindingData = [];
for (const model of allModels) {
const materials = await this.getMaterialsByModel(model.id);
for (const material of materials) {
bindingData.push({
model_id: model.id,
model_name: model.name,
model_stage_name: model.stage_name,
material_id: material.id,
material_name: material.name,
material_type: material.material_type,
created_at: material.created_at,
});
}
}
if (format === 'json') {
return JSON.stringify(bindingData, null, 2);
} else {
// CSV 格式
const headers = Object.keys(bindingData[0] || {});
const csvContent = [
headers.join(','),
...bindingData.map(row =>
headers.map(header => `"${row[header as keyof typeof row] || ''}"`).join(',')
)
].join('\n');
return csvContent;
}
} catch (error) {
throw new Error(`导出绑定关系失败: ${error}`);
}
}
}