From c3c72ce8bd318dd8fdb3caee67205ca1c27439c3 Mon Sep 17 00:00:00 2001 From: imeepos Date: Mon, 21 Jul 2025 20:07:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E5=AE=9E=E6=97=B6=E8=BF=9B=E5=BA=A6=E9=80=9A?= =?UTF-8?q?=E8=AE=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复问题: - 一键匹配进度条没有逐步递增,只在开始和结束时更新 实现内容: 1. 后端进度事件发送: - 在事件总线中添加BatchMatchingProgress事件类型 - 在批量匹配服务中集成Tauri事件发送 - 在每个模板匹配开始时发送实时进度事件 2. 前端进度事件监听: - 修改BatchMatchingService支持事件监听 - 添加batch_matching_progress事件监听器 - 实时更新进度条状态 3. 事件通讯机制: - 使用Tauri的emit系统发送事件到前端 - 前端通过listen监听实时进度更新 - 确保进度条能够逐步递增显示 技术细节: - 后端:使用app_handle.emit()发送进度事件 - 前端:使用listen()监听batch_matching_progress事件 - 进度计算:基于当前轮数、绑定索引和总绑定数 现在一键匹配过程中进度条会实时更新,用户可以看到匹配的实际进展。 --- .../services/material_matching_service.rs | 31 ++++++++++++++++-- .../src-tauri/src/data/models/material.rs | 10 +----- .../src-tauri/src/infrastructure/event_bus.rs | 32 +++++++++++++++++++ .../infrastructure/tolerant_json_parser.rs | 2 +- .../commands/material_matching_commands.rs | 5 +-- apps/desktop/src/pages/ProjectDetails.tsx | 2 +- .../src/services/batchMatchingService.ts | 31 +++++++++++++++++- 7 files changed, 96 insertions(+), 17 deletions(-) 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 bd1e68c..cdce1d3 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 @@ -16,6 +16,8 @@ use crate::data::repositories::{ use crate::business::services::template_service::TemplateService; use crate::business::services::template_matching_result_service::TemplateMatchingResultService; use crate::infrastructure::filename_utils::FilenameUtils; +use crate::infrastructure::event_bus::EventBusManager; +use tauri::Emitter; use anyhow::{Result, anyhow}; use serde::{Serialize, Deserialize}; use std::collections::{HashMap, HashSet}; @@ -28,6 +30,7 @@ pub struct MaterialMatchingService { template_service: Arc, video_classification_repo: Arc, matching_result_service: Option>, + event_bus: Arc, } /// 素材匹配请求 @@ -160,6 +163,7 @@ impl MaterialMatchingService { template_service, video_classification_repo, matching_result_service: None, + event_bus: Arc::new(EventBusManager::new()), } } @@ -177,6 +181,7 @@ impl MaterialMatchingService { template_service, video_classification_repo, matching_result_service: Some(matching_result_service), + event_bus: Arc::new(EventBusManager::new()), } } @@ -785,11 +790,17 @@ impl MaterialMatchingService { /// 执行一键匹配 - 遍历项目的所有活跃模板绑定并逐一匹配 pub async fn batch_match_all_templates(&self, request: BatchMatchingRequest, database: Arc) -> Result { // 调用优化的循环匹配方法 - self.batch_match_all_templates_optimized(request, database).await + self.batch_match_all_templates_optimized(request, database, None).await + } + + /// 执行一键匹配(带事件发送) + pub async fn batch_match_all_templates_with_events(&self, request: BatchMatchingRequest, database: Arc, app_handle: Option) -> Result { + // 调用优化的循环匹配方法 + self.batch_match_all_templates_optimized(request, database, app_handle).await } /// 优化的一键匹配 - 循环匹配模板直到失败(无法完整匹配模板 -- 素材不够用) - pub async fn batch_match_all_templates_optimized(&self, request: BatchMatchingRequest, database: Arc) -> Result { + pub async fn batch_match_all_templates_optimized(&self, request: BatchMatchingRequest, database: Arc, app_handle: Option) -> Result { let start_time = std::time::Instant::now(); // 获取项目的所有活跃模板绑定 @@ -884,9 +895,23 @@ impl MaterialMatchingService { } // 逐一尝试匹配每个模板绑定 - for binding_detail in &active_bindings { + for (binding_index, binding_detail) in active_bindings.iter().enumerate() { let binding_start_time = std::time::Instant::now(); + // 发送进度事件 + if let Some(ref handle) = app_handle { + let current_binding_index = (total_rounds - 1) as usize * active_bindings.len() + binding_index + 1; + let _ = handle.emit("batch_matching_progress", serde_json::json!({ + "project_id": request.project_id, + "current_binding_index": current_binding_index, + "total_bindings": active_bindings.len() * 100, // 估算总数 + "current_template_name": binding_detail.template_name, + "completed_bindings": successful_matches, + "failed_bindings": failed_matches, + "elapsed_time_ms": start_time.elapsed().as_millis() as u64, + })); + } + let matching_request = MaterialMatchingRequest { project_id: request.project_id.clone(), template_id: binding_detail.binding.template_id.clone(), diff --git a/apps/desktop/src-tauri/src/data/models/material.rs b/apps/desktop/src-tauri/src/data/models/material.rs index d2c846d..68b5e46 100644 --- a/apps/desktop/src-tauri/src/data/models/material.rs +++ b/apps/desktop/src-tauri/src/data/models/material.rs @@ -382,17 +382,12 @@ impl MaterialSegment { /// 检查片段是否满足最小时长要求 pub fn meets_duration_requirement(&self, required_duration: f64) -> bool { - let meets = self.duration >= required_duration; - println!(" 📏 时长要求检查: 素材{:.3}s >= 要求{:.3}s = {}", - self.duration, required_duration, meets); - meets + self.duration >= required_duration } /// 计算与目标时长的匹配度(越接近越好,返回0.0-1.0) pub fn duration_match_score(&self, target_duration: f64) -> f64 { if self.duration < target_duration { - println!(" 📊 匹配评分: 素材{:.3}s < 目标{:.3}s,时长不足 = 0.0", - self.duration, target_duration); return 0.0; // 时长不足,不匹配 } @@ -405,9 +400,6 @@ impl MaterialSegment { } else { 0.3 // 超出50%以上,低匹配度 }; - - println!(" 📊 匹配评分: 素材{:.3}s vs 目标{:.3}s,超出比例{:.1}% = {:.3}", - self.duration, target_duration, excess_ratio * 100.0, score); score } } diff --git a/apps/desktop/src-tauri/src/infrastructure/event_bus.rs b/apps/desktop/src-tauri/src/infrastructure/event_bus.rs index c285ab0..87747e6 100644 --- a/apps/desktop/src-tauri/src/infrastructure/event_bus.rs +++ b/apps/desktop/src-tauri/src/infrastructure/event_bus.rs @@ -73,6 +73,16 @@ pub enum DataEvent { stage: String, // "metadata", "scene_detection", "video_splitting" progress_percentage: f64, }, + /// 批量匹配进度事件 + BatchMatchingProgress { + project_id: String, + current_binding_index: u32, + total_bindings: u32, + current_template_name: Option, + completed_bindings: u32, + failed_bindings: u32, + elapsed_time_ms: u64, + }, } /// UI事件 @@ -293,6 +303,28 @@ impl EventBusManager { progress_percentage, })).await } + + /// 发布批量匹配进度事件 + pub async fn publish_batch_matching_progress( + &self, + project_id: String, + current_binding_index: u32, + total_bindings: u32, + current_template_name: Option, + completed_bindings: u32, + failed_bindings: u32, + elapsed_time_ms: u64, + ) -> Result<(), String> { + self.event_bus.publish(Event::Data(DataEvent::BatchMatchingProgress { + project_id, + current_binding_index, + total_bindings, + current_template_name, + completed_bindings, + failed_bindings, + elapsed_time_ms, + })).await + } } impl Default for EventBusManager { diff --git a/apps/desktop/src-tauri/src/infrastructure/tolerant_json_parser.rs b/apps/desktop/src-tauri/src/infrastructure/tolerant_json_parser.rs index 343797d..4cfd2be 100644 --- a/apps/desktop/src-tauri/src/infrastructure/tolerant_json_parser.rs +++ b/apps/desktop/src-tauri/src/infrastructure/tolerant_json_parser.rs @@ -710,7 +710,7 @@ impl TolerantJsonParser { } // 如果值部分是一个被引号包围的字符串 - if (value_part.starts_with('"') && value_part.len() > 1) { + if value_part.starts_with('"') && value_part.len() > 1 { // 寻找匹配的结束引号,考虑转义字符 let mut end_pos = None; let mut chars = value_part[1..].char_indices(); diff --git a/apps/desktop/src-tauri/src/presentation/commands/material_matching_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/material_matching_commands.rs index 3fb0ec6..9c58b18 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/material_matching_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/material_matching_commands.rs @@ -265,6 +265,7 @@ pub struct TemplateBindingMatchingValidation { pub async fn batch_match_all_templates( request: BatchMatchingRequest, state: State<'_, crate::app_state::AppState>, + app_handle: tauri::AppHandle, ) -> Result { let database = state.get_database(); @@ -295,8 +296,8 @@ pub async fn batch_match_all_templates( matching_result_service, ); - // 执行一键匹配 - matching_service.batch_match_all_templates(request, database) + // 执行一键匹配(带事件发送) + matching_service.batch_match_all_templates_with_events(request, database, Some(app_handle)) .await .map_err(|e| e.to_string()) } diff --git a/apps/desktop/src/pages/ProjectDetails.tsx b/apps/desktop/src/pages/ProjectDetails.tsx index 2ba5757..2090e32 100644 --- a/apps/desktop/src/pages/ProjectDetails.tsx +++ b/apps/desktop/src/pages/ProjectDetails.tsx @@ -549,7 +549,7 @@ export const ProjectDetails: React.FC = () => { setShowBatchMatchingProgressDialog(true); // 设置进度回调 - BatchMatchingService.setProgressCallback((progress: BatchMatchingProgress) => { + await BatchMatchingService.setProgressCallback((progress: BatchMatchingProgress) => { setBatchMatchingProgress(progress); // 当匹配完成时,显示结果对话框 diff --git a/apps/desktop/src/services/batchMatchingService.ts b/apps/desktop/src/services/batchMatchingService.ts index 38fd384..a3e5cb6 100644 --- a/apps/desktop/src/services/batchMatchingService.ts +++ b/apps/desktop/src/services/batchMatchingService.ts @@ -4,6 +4,7 @@ */ import { invoke } from '@tauri-apps/api/core'; +import { listen, UnlistenFn } from '@tauri-apps/api/event'; import { BatchMatchingRequest, BatchMatchingResult, @@ -14,12 +15,34 @@ import { export class BatchMatchingService { // 进度回调函数类型 static progressCallback: ((progress: BatchMatchingProgress) => void) | null = null; + // 事件监听器 + static progressEventUnlisten: UnlistenFn | null = null; /** * 设置进度回调函数 */ - static setProgressCallback(callback: (progress: BatchMatchingProgress) => void) { + static async setProgressCallback(callback: (progress: BatchMatchingProgress) => void) { this.progressCallback = callback; + + // 设置事件监听器 + try { + this.progressEventUnlisten = await listen('batch_matching_progress', (event: any) => { + const progressData = event.payload; + if (this.progressCallback) { + this.progressCallback({ + status: BatchMatchingProgressStatus.InProgress, + current_binding_index: progressData.current_binding_index, + total_bindings: progressData.total_bindings, + current_template_name: progressData.current_template_name, + completed_bindings: progressData.completed_bindings, + failed_bindings: progressData.failed_bindings, + elapsed_time_ms: progressData.elapsed_time_ms, + }); + } + }); + } catch (error) { + console.error('设置批量匹配进度事件监听器失败:', error); + } } /** @@ -27,6 +50,12 @@ export class BatchMatchingService { */ static clearProgressCallback() { this.progressCallback = null; + + // 清除事件监听器 + if (this.progressEventUnlisten) { + this.progressEventUnlisten(); + this.progressEventUnlisten = null; + } } /**