fix: 修复异步导入业务逻辑和loading状态

问题修复:
1. 异步导入没有调用完整业务逻辑 - 只创建了Material记录,没有处理元数据、场景检测、视频切分
2. 导入动画一闪而逝 - 缺少实际的处理时间
3. 后台无素材处理日志 - 简化版本跳过了所有业务处理

解决方案:
1. 替换简化处理函数为完整业务逻辑处理
2. 调用MaterialService的完整处理流程
3. 添加详细的处理日志和进度事件
4. 添加适当延迟确保用户看到loading动画
5. 修复前端事件处理逻辑

技术改进:
- process_single_file_with_full_logic: 完整的素材处理流程
- 元数据提取、场景检测、视频切分全流程
- 详细的tracing日志输出
- 实时进度事件发送
- 正确的错误处理和状态更新
This commit is contained in:
imeepos 2025-07-13 23:57:56 +08:00
parent 91062ccf4c
commit 44b4084eb9
2 changed files with 152 additions and 15 deletions

View File

@ -80,7 +80,6 @@ async fn import_materials_with_tauri_events(
config: MaterialProcessingConfig,
app_handle: tauri::AppHandle,
) -> Result<MaterialImportResult, anyhow::Error> {
use anyhow::anyhow;
use std::path::Path;
use std::time::Instant;
use tracing::{info, warn, error, debug};
@ -113,6 +112,9 @@ async fn import_materials_with_tauri_events(
"progress_percentage": 0.0
}));
// 添加小延迟确保用户能看到loading动画
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
// 异步处理每个文件
for (index, file_path) in request.file_paths.iter().enumerate() {
debug!(file_path = %file_path, "异步处理文件");
@ -127,12 +129,13 @@ async fn import_materials_with_tauri_events(
"progress_percentage": (index as f64 / result.total_files as f64) * 100.0
}));
// 处理单个文件
match process_single_file_simple(
// 处理单个文件 - 使用完整的业务逻辑
match process_single_file_with_full_logic(
Arc::clone(&repository),
&request.project_id,
file_path,
&config,
app_handle.clone(),
).await {
Ok(Some(material)) => {
info!(
@ -186,19 +189,23 @@ async fn import_materials_with_tauri_events(
Ok(result)
}
/// 简化的单文件处理函数
async fn process_single_file_simple(
/// 使用完整业务逻辑的单文件处理函数
async fn process_single_file_with_full_logic(
repository: Arc<MaterialRepository>,
project_id: &str,
file_path: &str,
_config: &MaterialProcessingConfig,
config: &MaterialProcessingConfig,
app_handle: tauri::AppHandle,
) -> Result<Option<Material>, anyhow::Error> {
use anyhow::anyhow;
use std::path::Path;
use std::fs;
use tokio::task;
use crate::data::models::material::{Material, MaterialType};
use crate::data::models::material::{Material, MaterialType, ProcessingStatus};
use crate::business::errors::error_utils;
use tracing::{info, warn, error, debug};
debug!(file_path = %file_path, "开始处理单个文件");
// 验证文件路径
error_utils::validate_file_path(file_path)
@ -218,6 +225,7 @@ async fn process_single_file_simple(
// 检查是否已存在相同的文件
if repository.exists_by_md5(project_id, &md5_hash)? {
warn!(file_path = %file_path, "文件已存在,跳过");
return Ok(None); // 跳过重复文件
}
@ -235,17 +243,148 @@ async fn process_single_file_simple(
let material_type = MaterialType::from_extension(extension);
// 创建素材对象
let material = Material::new(
let mut material = Material::new(
project_id.to_string(),
file_name.clone(),
file_path.to_string(),
file_size,
md5_hash,
material_type,
material_type.clone(),
);
// 保存到数据库
repository.create(&material)?;
info!(material_id = %material.id, file_name = %file_name, "素材创建成功");
// 发送处理进度事件
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
"material_id": material.id,
"file_name": file_name,
"stage": "开始处理",
"progress_percentage": 0.0
}));
// 如果启用自动处理,则进行完整的业务处理
if config.auto_process.unwrap_or(true) {
// 更新状态为处理中
MaterialService::update_material_status(
&repository,
&material.id,
ProcessingStatus::Processing,
None,
)?;
// 1. 提取元数据
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
"material_id": material.id,
"file_name": file_name,
"stage": "提取元数据",
"progress_percentage": 25.0
}));
let original_path = material.original_path.clone();
let material_type_clone = material.material_type.clone();
match task::spawn_blocking(move || {
MaterialService::extract_metadata(&original_path, &material_type_clone)
}).await? {
Ok(metadata) => {
material.set_metadata(metadata);
repository.update(&material)?;
info!(material_id = %material.id, "元数据提取成功");
}
Err(e) => {
error!(material_id = %material.id, error = %e, "元数据提取失败");
MaterialService::update_material_status(
&repository,
&material.id,
ProcessingStatus::Failed,
Some(format!("元数据提取失败: {}", e)),
)?;
return Err(e);
}
}
// 2. 场景检测(如果是视频且启用了场景检测)
if matches!(material.material_type, MaterialType::Video) && config.enable_scene_detection {
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
"material_id": material.id,
"file_name": file_name,
"stage": "场景检测",
"progress_percentage": 50.0
}));
let original_path = material.original_path.clone();
let threshold = config.scene_detection_threshold;
match task::spawn_blocking(move || {
MaterialService::detect_video_scenes(&original_path, threshold)
}).await? {
Ok(scene_detection) => {
info!(material_id = %material.id, scenes = scene_detection.scenes.len(), "场景检测成功");
material.set_scene_detection(scene_detection);
repository.update(&material)?;
}
Err(e) => {
// 场景检测失败不应该导致整个处理失败
warn!(material_id = %material.id, error = %e, "场景检测失败");
}
}
}
// 3. 检查是否需要切分视频
let should_segment = material.needs_segmentation(config.max_segment_duration) ||
(matches!(material.material_type, MaterialType::Video) && material.scene_detection.is_some());
if should_segment {
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
"material_id": material.id,
"file_name": file_name,
"stage": "视频切分",
"progress_percentage": 75.0
}));
let repository_clone = Arc::clone(&repository);
let material_clone = material.clone();
let config_clone = config.clone();
match task::spawn_blocking(move || {
MaterialService::segment_video(&repository_clone, &material_clone, &config_clone)
}).await? {
Ok(_) => {
info!(material_id = %material.id, "视频切分完成");
}
Err(e) => {
error!(material_id = %material.id, error = %e, "视频切分失败");
MaterialService::update_material_status(
&repository,
&material.id,
ProcessingStatus::Failed,
Some(format!("视频切分失败: {}", e)),
)?;
return Err(e);
}
}
}
// 标记为完成
MaterialService::update_material_status(
&repository,
&material.id,
ProcessingStatus::Completed,
None,
)?;
// 发送处理完成事件
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
"material_id": material.id,
"file_name": file_name,
"stage": "处理完成",
"progress_percentage": 100.0
}));
info!(material_id = %material.id, "素材处理完成");
}
Ok(Some(material))
}

View File

@ -183,13 +183,11 @@ export const MaterialImportDialog: React.FC<MaterialImportDialogProps> = ({
console.log('开始异步导入:', request);
// 使用异步导入,进度更新通过事件监听器处理
const result = await importMaterialsAsync(request);
// 使用异步导入,进度更新和完成状态完全通过事件监听器处理
await importMaterialsAsync(request);
// 如果没有通过事件监听器收到完成事件,手动处理完成状态
console.log('异步导入完成:', result);
setStep('complete');
onImportComplete(result);
// 注意:不在这里设置完成状态,完全依赖事件监听器
console.log('异步导入命令执行完成,等待事件通知');
} catch (error) {
console.error('导入失败:', error);
setStep('configure'); // 返回配置步骤