feat: 实现一键匹配实时进度通讯
修复问题: - 一键匹配进度条没有逐步递增,只在开始和结束时更新 实现内容: 1. 后端进度事件发送: - 在事件总线中添加BatchMatchingProgress事件类型 - 在批量匹配服务中集成Tauri事件发送 - 在每个模板匹配开始时发送实时进度事件 2. 前端进度事件监听: - 修改BatchMatchingService支持事件监听 - 添加batch_matching_progress事件监听器 - 实时更新进度条状态 3. 事件通讯机制: - 使用Tauri的emit系统发送事件到前端 - 前端通过listen监听实时进度更新 - 确保进度条能够逐步递增显示 技术细节: - 后端:使用app_handle.emit()发送进度事件 - 前端:使用listen()监听batch_matching_progress事件 - 进度计算:基于当前轮数、绑定索引和总绑定数 现在一键匹配过程中进度条会实时更新,用户可以看到匹配的实际进展。
This commit is contained in:
parent
70e8669ace
commit
c3c72ce8bd
|
|
@ -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<TemplateService>,
|
||||
video_classification_repo: Arc<VideoClassificationRepository>,
|
||||
matching_result_service: Option<Arc<TemplateMatchingResultService>>,
|
||||
event_bus: Arc<EventBusManager>,
|
||||
}
|
||||
|
||||
/// 素材匹配请求
|
||||
|
|
@ -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<crate::infrastructure::database::Database>) -> Result<BatchMatchingResult> {
|
||||
// 调用优化的循环匹配方法
|
||||
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<crate::infrastructure::database::Database>, app_handle: Option<tauri::AppHandle>) -> Result<BatchMatchingResult> {
|
||||
// 调用优化的循环匹配方法
|
||||
self.batch_match_all_templates_optimized(request, database, app_handle).await
|
||||
}
|
||||
|
||||
/// 优化的一键匹配 - 循环匹配模板直到失败(无法完整匹配模板 -- 素材不够用)
|
||||
pub async fn batch_match_all_templates_optimized(&self, request: BatchMatchingRequest, database: Arc<crate::infrastructure::database::Database>) -> Result<BatchMatchingResult> {
|
||||
pub async fn batch_match_all_templates_optimized(&self, request: BatchMatchingRequest, database: Arc<crate::infrastructure::database::Database>, app_handle: Option<tauri::AppHandle>) -> Result<BatchMatchingResult> {
|
||||
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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
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<String>,
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<BatchMatchingResult, String> {
|
||||
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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -549,7 +549,7 @@ export const ProjectDetails: React.FC = () => {
|
|||
setShowBatchMatchingProgressDialog(true);
|
||||
|
||||
// 设置进度回调
|
||||
BatchMatchingService.setProgressCallback((progress: BatchMatchingProgress) => {
|
||||
await BatchMatchingService.setProgressCallback((progress: BatchMatchingProgress) => {
|
||||
setBatchMatchingProgress(progress);
|
||||
|
||||
// 当匹配完成时,显示结果对话框
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue