342 lines
8.6 KiB
TypeScript
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}`);
|
|
}
|
|
}
|
|
}
|