From 2d88274c3aa8fc181fadf801b9d314e6d77b5898 Mon Sep 17 00:00:00 2001 From: imeepos Date: Mon, 14 Jul 2025 21:50:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD=E7=9A=84=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复问题: - 修复数据库状态格式不一致导致的列表显示错误 - 修复单个导入完成后统计信息显示为0的问题 - 修复日期时间解析错误导致的panic问题 - 修复所有unwrap()调用导致的潜在崩溃 技术改进: - 统一使用Debug格式保存和查询import_status - 改进日期解析支持多种格式(RFC3339和SQLite格式) - 优化进度监控逻辑,保留最后有效统计数据 - 完善错误处理,避免锁中毒和解析错误 功能完善: - 模板导入完成后正确显示'已完成'状态 - 统计信息准确显示成功/失败数量 - 进度监控稳定运行,无无限轮询问题 --- .../services/template_import_service.rs | 9 +-- .../src/business/services/template_service.rs | 66 +++++++++++++++---- .../template/ImportProgressModal.tsx | 30 +++++---- apps/desktop/src/pages/TemplateManagement.tsx | 2 +- 4 files changed, 72 insertions(+), 35 deletions(-) diff --git a/apps/desktop/src-tauri/src/business/services/template_import_service.rs b/apps/desktop/src-tauri/src/business/services/template_import_service.rs index cbc26ef..f33ae79 100644 --- a/apps/desktop/src-tauri/src/business/services/template_import_service.rs +++ b/apps/desktop/src-tauri/src/business/services/template_import_service.rs @@ -478,14 +478,7 @@ impl TemplateImportService { let conn = self.database.get_connection(); let conn = conn.lock().map_err(|e| anyhow!("获取数据库连接失败: {}", e))?; - let status_str = match status { - ImportStatus::Pending => "pending", - ImportStatus::Parsing => "parsing", - ImportStatus::Uploading => "uploading", - ImportStatus::Processing => "processing", - ImportStatus::Completed => "completed", - ImportStatus::Failed => "failed", - }; + let status_str = format!("{:?}", status); conn.execute( "UPDATE templates SET import_status = ?, updated_at = datetime('now') WHERE id = ?", diff --git a/apps/desktop/src-tauri/src/business/services/template_service.rs b/apps/desktop/src-tauri/src/business/services/template_service.rs index 0923e08..5e1c627 100644 --- a/apps/desktop/src-tauri/src/business/services/template_service.rs +++ b/apps/desktop/src-tauri/src/business/services/template_service.rs @@ -399,12 +399,34 @@ impl TemplateService { tracks: Vec::new(), // 稍后填充 import_status, source_file_path: row.get("source_file_path")?, - created_at: chrono::DateTime::parse_from_rfc3339(&row.get::<_, String>("created_at")?) - .map_err(|_e| rusqlite::Error::InvalidColumnType(0, "created_at".to_string(), rusqlite::types::Type::Text))? - .with_timezone(&chrono::Utc), - updated_at: chrono::DateTime::parse_from_rfc3339(&row.get::<_, String>("updated_at")?) - .map_err(|_e| rusqlite::Error::InvalidColumnType(0, "updated_at".to_string(), rusqlite::types::Type::Text))? - .with_timezone(&chrono::Utc), + created_at: { + let created_at_str: String = row.get("created_at")?; + chrono::DateTime::parse_from_rfc3339(&created_at_str) + .or_else(|_| { + // 尝试解析SQLite格式的日期 + chrono::NaiveDateTime::parse_from_str(&created_at_str, "%Y-%m-%d %H:%M:%S") + .map(|dt| dt.and_utc().fixed_offset()) + }) + .map_err(|e| { + eprintln!("❌ 解析created_at失败: '{}', 错误: {}", created_at_str, e); + rusqlite::Error::InvalidColumnType(0, "created_at".to_string(), rusqlite::types::Type::Text) + })? + .with_timezone(&chrono::Utc) + }, + updated_at: { + let updated_at_str: String = row.get("updated_at")?; + chrono::DateTime::parse_from_rfc3339(&updated_at_str) + .or_else(|_| { + // 尝试解析SQLite格式的日期 + chrono::NaiveDateTime::parse_from_str(&updated_at_str, "%Y-%m-%d %H:%M:%S") + .map(|dt| dt.and_utc().fixed_offset()) + }) + .map_err(|e| { + eprintln!("❌ 解析updated_at失败: '{}', 错误: {}", updated_at_str, e); + rusqlite::Error::InvalidColumnType(0, "updated_at".to_string(), rusqlite::types::Type::Text) + })? + .with_timezone(&chrono::Utc) + }, is_active: row.get("is_active")?, }) } @@ -447,12 +469,32 @@ impl TemplateService { height: row.get::<_, Option>("height")?.map(|h| h as u32), upload_status, metadata: row.get("metadata")?, - created_at: chrono::DateTime::parse_from_rfc3339(&row.get::<_, String>("created_at")?) - .unwrap() - .with_timezone(&chrono::Utc), - updated_at: chrono::DateTime::parse_from_rfc3339(&row.get::<_, String>("updated_at")?) - .unwrap() - .with_timezone(&chrono::Utc), + created_at: { + let created_at_str: String = row.get("created_at")?; + chrono::DateTime::parse_from_rfc3339(&created_at_str) + .or_else(|_| { + chrono::NaiveDateTime::parse_from_str(&created_at_str, "%Y-%m-%d %H:%M:%S") + .map(|dt| dt.and_utc().fixed_offset()) + }) + .map_err(|e| { + eprintln!("❌ 解析素材created_at失败: '{}', 错误: {}", created_at_str, e); + rusqlite::Error::InvalidColumnType(0, "created_at".to_string(), rusqlite::types::Type::Text) + })? + .with_timezone(&chrono::Utc) + }, + updated_at: { + let updated_at_str: String = row.get("updated_at")?; + chrono::DateTime::parse_from_rfc3339(&updated_at_str) + .or_else(|_| { + chrono::NaiveDateTime::parse_from_str(&updated_at_str, "%Y-%m-%d %H:%M:%S") + .map(|dt| dt.and_utc().fixed_offset()) + }) + .map_err(|e| { + eprintln!("❌ 解析素材updated_at失败: '{}', 错误: {}", updated_at_str, e); + rusqlite::Error::InvalidColumnType(0, "updated_at".to_string(), rusqlite::types::Type::Text) + })? + .with_timezone(&chrono::Utc) + }, }) } diff --git a/apps/desktop/src/components/template/ImportProgressModal.tsx b/apps/desktop/src/components/template/ImportProgressModal.tsx index ead044e..1e8ec9c 100644 --- a/apps/desktop/src/components/template/ImportProgressModal.tsx +++ b/apps/desktop/src/components/template/ImportProgressModal.tsx @@ -15,6 +15,7 @@ export const ImportProgressModal: React.FC = ({ onComplete, }) => { const [progress, setProgress] = useState(null); + const [lastValidProgress, setLastValidProgress] = useState(null); const { getImportProgress } = useTemplateStore(); useEffect(() => { @@ -28,23 +29,24 @@ export const ImportProgressModal: React.FC = ({ const progressData = await getImportProgress(templateId); if (!isMounted) return; - setProgress(progressData); + // 如果有有效的进度数据,保存它并更新显示 + if (progressData) { + setProgress(progressData); + setLastValidProgress(progressData); + } // 如果进度数据为空(已被清除),说明导入已完成,停止轮询 if (!progressData) { - // 设置一个完成状态的进度对象 - const completedProgress: ImportProgress = { - template_id: templateId, - template_name: "模板", - status: ImportStatus.Completed, - total_materials: 0, - uploaded_materials: 0, - failed_materials: 0, - current_operation: "导入完成", - error_message: undefined, - progress_percentage: 100 - }; - setProgress(completedProgress); + // 使用最后一次有效的进度数据,但更新状态为完成 + if (lastValidProgress) { + const completedProgress: ImportProgress = { + ...lastValidProgress, + status: ImportStatus.Completed, + current_operation: "导入完成", + progress_percentage: 100 + }; + setProgress(completedProgress); + } // 清除轮询 if (interval) { diff --git a/apps/desktop/src/pages/TemplateManagement.tsx b/apps/desktop/src/pages/TemplateManagement.tsx index a9241b3..cf6a1b4 100644 --- a/apps/desktop/src/pages/TemplateManagement.tsx +++ b/apps/desktop/src/pages/TemplateManagement.tsx @@ -45,7 +45,7 @@ const TemplateManagement: React.FC = () => { page: currentPage, page_size: pageSize, }); - + console.log(response) setTemplates(response.templates); setTotalPages(Math.ceil(response.total / pageSize)); } catch (error) {