diff --git a/apps/desktop/src-tauri/src/business/services/material_matching_service.rs b/apps/desktop/src-tauri/src/business/services/material_matching_service.rs index a570817..3141c94 100644 --- a/apps/desktop/src-tauri/src/business/services/material_matching_service.rs +++ b/apps/desktop/src-tauri/src/business/services/material_matching_service.rs @@ -294,22 +294,30 @@ impl MaterialMatchingService { let matching_duration_ms = start_time.elapsed().as_millis() as u64; - // 如果配置了结果保存服务,则自动保存结果 - if let Some(result_service) = &self.matching_result_service { - match result_service.save_matching_result( - &matching_result, - result_name, - description, - matching_duration_ms, - ).await { - Ok(saved_result) => { - return Ok((matching_result, Some(saved_result))); - } - Err(e) => { - // 保存失败时记录错误但不影响匹配结果的返回 - eprintln!("保存匹配结果失败: {}", e); + // 判断匹配是否成功:没有失败的片段 + let is_matching_successful = matching_result.failed_segments.is_empty(); + + // 只有匹配完全成功时才保存到数据库 + if is_matching_successful { + // 如果配置了结果保存服务,则自动保存结果 + if let Some(result_service) = &self.matching_result_service { + match result_service.save_matching_result( + &matching_result, + result_name, + description, + matching_duration_ms, + ).await { + Ok(saved_result) => { + return Ok((matching_result, Some(saved_result))); + } + Err(e) => { + // 保存失败时记录错误但不影响匹配结果的返回 + eprintln!("保存匹配结果失败: {}", e); + } } } + } else { + println!("⚠️ 匹配失败,不保存到数据库。失败片段数: {}", matching_result.failed_segments.len()); } Ok((matching_result, None)) @@ -734,26 +742,55 @@ impl MaterialMatchingService { match self.match_materials_with_used_segments(matching_request, result_name, &global_used_segment_ids).await { Ok((matching_result, saved_result, newly_used_segments)) => { - round_successful_matches += 1; - successful_matches += 1; + // 检查匹配是否完全成功(没有失败的片段) + let is_fully_successful = matching_result.failed_segments.is_empty(); - // 更新全局已使用片段列表 - global_used_segment_ids.extend(newly_used_segments); + if is_fully_successful { + round_successful_matches += 1; + successful_matches += 1; - matching_results.push(BatchMatchingItemResult { - binding_id: binding_detail.binding.id.clone(), - template_id: binding_detail.binding.template_id.clone(), - template_name: binding_detail.template_name.clone(), - binding_name: binding_detail.binding.binding_name.clone(), - status: BatchMatchingItemStatus::Success, - matching_result: Some(matching_result), - saved_result_id: saved_result.map(|r| r.id), - error_message: None, - duration_ms: binding_start_time.elapsed().as_millis() as u64, - round_number: total_rounds, - attempts_count: 1, - failure_reason: None, - }); + // 更新全局已使用片段列表 + global_used_segment_ids.extend(newly_used_segments); + + matching_results.push(BatchMatchingItemResult { + binding_id: binding_detail.binding.id.clone(), + template_id: binding_detail.binding.template_id.clone(), + template_name: binding_detail.template_name.clone(), + binding_name: binding_detail.binding.binding_name.clone(), + status: BatchMatchingItemStatus::Success, + matching_result: Some(matching_result), + saved_result_id: saved_result.map(|r| r.id), + error_message: None, + duration_ms: binding_start_time.elapsed().as_millis() as u64, + round_number: total_rounds, + attempts_count: 1, + failure_reason: None, + }); + } else { + // 部分匹配失败,视为失败 + round_failed_matches += 1; + failed_matches += 1; + + let failure_reason = format!("部分匹配失败:{} 个片段匹配成功,{} 个片段匹配失败", + matching_result.matches.len(), + matching_result.failed_segments.len() + ); + + matching_results.push(BatchMatchingItemResult { + binding_id: binding_detail.binding.id.clone(), + template_id: binding_detail.binding.template_id.clone(), + template_name: binding_detail.template_name.clone(), + binding_name: binding_detail.binding.binding_name.clone(), + status: BatchMatchingItemStatus::Failed, + matching_result: Some(matching_result), // 仍然返回结果用于分析 + saved_result_id: None, // 但不保存到数据库 + error_message: Some(failure_reason.clone()), + duration_ms: binding_start_time.elapsed().as_millis() as u64, + round_number: total_rounds, + attempts_count: 1, + failure_reason: Some(failure_reason), + }); + } } Err(error) => { round_failed_matches += 1; @@ -990,12 +1027,18 @@ impl MaterialMatchingService { let matched_segments = matches.len() as u32; let failed_segments_count = failed_segments.len() as u32; let fixed_segments_count = fixed_segments.len() as u32; - let success_rate = if total_segments > 0 { - (matched_segments + fixed_segments_count) as f64 / total_segments as f64 + + // 修正成功率计算:只考虑需要匹配的片段 + let matchable_segments = total_segments - fixed_segments_count; + let success_rate = if matchable_segments > 0 { + matched_segments as f64 / matchable_segments as f64 } else { - 0.0 + 1.0 // 如果没有需要匹配的片段,成功率为100% }; + // 判断匹配是否成功:所有需要匹配的片段都成功匹配 + let is_matching_successful = failed_segments_count == 0; + // 创建匹配结果 let matching_result = MaterialMatchingResult { binding_id: request.binding_id.clone(), @@ -1003,7 +1046,7 @@ impl MaterialMatchingService { project_id: request.project_id.clone(), matches, statistics: MatchingStatistics { - total_segments, + total_segments: matchable_segments, // 只统计需要匹配的片段 matched_segments, failed_segments: failed_segments_count, success_rate, @@ -1013,21 +1056,30 @@ impl MaterialMatchingService { failed_segments, }; - // 保存匹配结果到数据库 - let saved_result = if let Some(result_service) = &self.matching_result_service { - let saved = result_service.save_matching_result( - &matching_result, - result_name, - None, - 0, // 匹配耗时,这里简化为0 - ).await?; + // 只有匹配完全成功时才保存到数据库和记录资源使用 + let (saved_result, final_used_segments) = if is_matching_successful { + // 保存匹配结果到数据库 + let saved_result = if let Some(result_service) = &self.matching_result_service { + let saved = result_service.save_matching_result( + &matching_result, + result_name, + None, + 0, // 匹配耗时,这里简化为0 + ).await?; - Some(saved) + Some(saved) + } else { + None + }; + + (saved_result, local_used_segment_ids) } else { - None + // 匹配失败时不保存到数据库,也不记录资源使用 + println!("⚠️ 匹配失败,不保存到数据库。失败片段数: {}, 成功片段数: {}", failed_segments_count, matched_segments); + (None, HashSet::new()) }; - Ok((matching_result, saved_result, local_used_segment_ids)) + Ok((matching_result, saved_result, final_used_segments)) } /// 计算批量匹配汇总信息 diff --git a/apps/desktop/src/components/BatchMatchingResultDialog.tsx b/apps/desktop/src/components/BatchMatchingResultDialog.tsx index fb6f893..ee6e888 100644 --- a/apps/desktop/src/components/BatchMatchingResultDialog.tsx +++ b/apps/desktop/src/components/BatchMatchingResultDialog.tsx @@ -125,7 +125,7 @@ export const BatchMatchingResultDialog: React.FC lines.push(` 失败原因: ${item.failure_reason}`); } if (item.matching_result) { - lines.push(` 成功率: ${(item.matching_result.statistics?.success_rate * 100 || 0).toFixed(1)}%`); + lines.push(` 成功率: ${Math.min((item.matching_result.statistics?.success_rate * 100 || 0), 100).toFixed(1)}%`); } lines.push(''); }); @@ -335,7 +335,7 @@ export const BatchMatchingResultDialog: React.FC

耗时: {formatDuration(item.duration_ms)}

{item.matching_result && (

- 成功率: {(item.matching_result.statistics?.success_rate * 100 || 0).toFixed(1)}% + 成功率: {Math.min((item.matching_result.statistics?.success_rate * 100 || 0), 100).toFixed(1)}%

)} diff --git a/apps/desktop/src/components/BatchMatchingSummaryCard.tsx b/apps/desktop/src/components/BatchMatchingSummaryCard.tsx index a10f0d3..4a68db7 100644 --- a/apps/desktop/src/components/BatchMatchingSummaryCard.tsx +++ b/apps/desktop/src/components/BatchMatchingSummaryCard.tsx @@ -145,7 +145,7 @@ export const BatchMatchingSummaryCard: React.FC =
平均成功率: - {(result.summary.average_success_rate * 100).toFixed(1)}% + {Math.min(result.summary.average_success_rate * 100, 100).toFixed(1)}%
{result.summary.best_matching_template && ( diff --git a/apps/desktop/src/components/TemplateMatchingResultCard.tsx b/apps/desktop/src/components/TemplateMatchingResultCard.tsx index 09c5c0f..d7a6aa7 100644 --- a/apps/desktop/src/components/TemplateMatchingResultCard.tsx +++ b/apps/desktop/src/components/TemplateMatchingResultCard.tsx @@ -138,8 +138,8 @@ export const TemplateMatchingResultCard: React.FC {/* 成功率 */}
-
- {result.success_rate.toFixed(1)}% +
+ {Math.min(result.success_rate * 100, 100).toFixed(1)}%
成功率
diff --git a/apps/desktop/src/components/TemplateMatchingResultDetailModal.tsx b/apps/desktop/src/components/TemplateMatchingResultDetailModal.tsx index 416ee47..fabef38 100644 --- a/apps/desktop/src/components/TemplateMatchingResultDetailModal.tsx +++ b/apps/desktop/src/components/TemplateMatchingResultDetailModal.tsx @@ -291,7 +291,7 @@ export const TemplateMatchingResultDetailModal: React.FC
- {matching_result.success_rate.toFixed(1)}% + {Math.min(matching_result.success_rate * 100, 100).toFixed(1)}%
成功率
diff --git a/apps/desktop/src/components/TemplateMatchingResultStatsPanel.tsx b/apps/desktop/src/components/TemplateMatchingResultStatsPanel.tsx index fa31847..dbb5271 100644 --- a/apps/desktop/src/components/TemplateMatchingResultStatsPanel.tsx +++ b/apps/desktop/src/components/TemplateMatchingResultStatsPanel.tsx @@ -167,8 +167,8 @@ export const TemplateMatchingResultStatsPanel: React.FC
平均成功率 - - {statistics.average_success_rate.toFixed(1)}% + + {Math.min(statistics.average_success_rate * 100, 100).toFixed(1)}%
diff --git a/apps/desktop/src/services/materialMatchingService.ts b/apps/desktop/src/services/materialMatchingService.ts index 5769ee7..9b39342 100644 --- a/apps/desktop/src/services/materialMatchingService.ts +++ b/apps/desktop/src/services/materialMatchingService.ts @@ -153,14 +153,14 @@ export class MaterialMatchingService { } { const { statistics } = result; - const summary = `匹配完成:${statistics.matched_segments}/${statistics.total_segments} 个片段 (${(statistics.success_rate * 100).toFixed(1)}%)`; - + 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} 个`, - `成功率:${(statistics.success_rate * 100).toFixed(1)}%` + `成功率:${Math.min(statistics.success_rate * 100, 100).toFixed(1)}%` ]; return { summary, details };