/** * 素材匹配服务 * 遵循前端开发规范的服务层设计 */ import { invoke } from '@tauri-apps/api/core'; import { MaterialMatchingRequest, MaterialMatchingResult, ProjectMaterialMatchingStats, TemplateBindingMatchingValidation, MatchingError, MatchingErrorType } from '../types/materialMatching'; export class MaterialMatchingService { /** * 执行素材匹配 */ static async executeMatching(request: MaterialMatchingRequest): Promise { try { return await invoke('execute_material_matching', { request }); } catch (error) { throw this.handleMatchingError(error); } } /** * 获取项目的素材匹配统计信息 */ static async getProjectMaterialStats(projectId: string): Promise { try { return await invoke('get_project_material_stats_for_matching', { projectId }); } catch (error) { throw this.handleMatchingError(error); } } /** * 验证模板绑定是否可以进行素材匹配 */ static async validateTemplateBinding(bindingId: string): Promise { try { return await invoke('validate_template_binding_for_matching', { bindingId }); } catch (error) { throw this.handleMatchingError(error); } } /** * 检查项目是否准备好进行素材匹配 */ static async checkProjectReadiness(projectId: string): Promise<{ isReady: boolean; issues: string[]; stats: ProjectMaterialMatchingStats; }> { const stats = await this.getProjectMaterialStats(projectId); const issues: string[] = []; // 检查是否有素材 if (stats.total_materials === 0) { issues.push('项目中没有素材,请先导入素材'); } // 检查是否有已分类的片段 if (stats.classified_segments === 0) { issues.push('没有已分类的素材片段,请先进行AI分类'); } // 检查分类率是否足够 if (stats.classification_rate < 0.5) { issues.push(`分类率较低 (${(stats.classification_rate * 100).toFixed(1)}%),建议提高分类覆盖率`); } // 检查是否有可用的分类类别 if (stats.available_categories.length === 0) { issues.push('没有可用的AI分类类别'); } return { isReady: issues.length === 0, issues, stats }; } /** * 预估匹配结果 */ static async estimateMatchingResult(request: MaterialMatchingRequest): Promise<{ estimated_matches: number; estimated_failures: number; estimated_success_rate: number; potential_issues: string[]; }> { // 获取项目统计信息 const stats = await this.getProjectMaterialStats(request.project_id); // 验证模板绑定 const validation = await this.validateTemplateBinding(request.binding_id); const potential_issues: string[] = []; // 基于统计信息估算匹配结果 let estimated_matches = Math.min( validation.matchable_segments, Math.floor(stats.classified_segments * 0.8) // 假设80%的已分类片段可以匹配 ); let estimated_failures = validation.total_segments - estimated_matches; // 检查潜在问题 if (stats.available_models < 2) { potential_issues.push('模特数量较少,可能影响匹配多样性'); estimated_matches = Math.floor(estimated_matches * 0.7); } if (stats.classification_rate < 0.7) { potential_issues.push('分类率较低,可能导致匹配失败'); estimated_matches = Math.floor(estimated_matches * 0.8); } if (stats.available_categories.length < 3) { potential_issues.push('可用分类类别较少'); estimated_matches = Math.floor(estimated_matches * 0.9); } estimated_failures = validation.total_segments - estimated_matches; const estimated_success_rate = validation.total_segments > 0 ? estimated_matches / validation.total_segments : 0; return { estimated_matches, estimated_failures, estimated_success_rate, potential_issues }; } /** * 格式化匹配统计信息为显示文本 */ static formatMatchingStats(result: MaterialMatchingResult): { summary: string; details: string[]; } { const { statistics } = result; const summary = `匹配完成:${statistics.matched_segments}/${statistics.total_segments} 个片段 (${Math.min(statistics.success_rate * 100, 100).toFixed(1)}%)`; const details = [ `成功匹配:${statistics.matched_segments} 个片段`, `匹配失败:${statistics.failed_segments} 个片段`, `使用素材:${statistics.used_materials} 个`, `涉及模特:${statistics.used_models} 个`, `成功率:${Math.min(statistics.success_rate * 100, 100).toFixed(1)}%` ]; return { summary, details }; } /** * 获取匹配失败原因的分类统计 */ static analyzeFailureReasons(result: MaterialMatchingResult): { [reason: string]: { count: number; segments: string[]; }; } { const analysis: { [reason: string]: { count: number; segments: string[] } } = {}; result.failed_segments.forEach(failure => { if (!analysis[failure.failure_reason]) { analysis[failure.failure_reason] = { count: 0, segments: [] }; } analysis[failure.failure_reason].count++; analysis[failure.failure_reason].segments.push(failure.track_segment_name); }); return analysis; } /** * 生成匹配改进建议 */ static generateImprovementSuggestions(result: MaterialMatchingResult, stats: ProjectMaterialMatchingStats): string[] { const suggestions: string[] = []; // 基于失败原因生成建议 const failureAnalysis = this.analyzeFailureReasons(result); Object.keys(failureAnalysis).forEach(reason => { const count = failureAnalysis[reason].count; if (reason.includes('没有找到分类')) { suggestions.push(`有 ${count} 个片段因缺少对应分类而匹配失败,建议增加相关分类的素材`); } else if (reason.includes('时长要求')) { suggestions.push(`有 ${count} 个片段因时长不足而匹配失败,建议导入更长的素材片段`); } else if (reason.includes('可用素材')) { suggestions.push(`有 ${count} 个片段因没有可用素材而匹配失败,建议增加更多素材`); } }); // 基于统计信息生成建议 if (stats.classification_rate < 0.8) { suggestions.push('建议对更多素材进行AI分类以提高匹配成功率'); } if (stats.available_models < 3) { suggestions.push('建议增加更多模特以提高匹配多样性'); } if (result.statistics.success_rate < 0.6) { suggestions.push('匹配成功率较低,建议检查模板的匹配规则设置'); } return suggestions; } /** * 处理匹配错误 */ private static handleMatchingError(error: any): MatchingError { let errorType: MatchingErrorType = MatchingErrorType.NoClassifiedSegments; let message = '素材匹配失败'; if (typeof error === 'string') { message = error; if (error.includes('模板不存在')) { errorType = MatchingErrorType.TemplateNotFound; } else if (error.includes('项目不存在')) { errorType = MatchingErrorType.ProjectNotFound; } else if (error.includes('没有已分类')) { errorType = MatchingErrorType.NoClassifiedSegments; } else if (error.includes('时长不足')) { errorType = MatchingErrorType.InsufficientDuration; } } else if (error?.message) { message = error.message; } return { type: errorType, message, details: error }; } }