diff --git a/apps/desktop/src-tauri/src/data/repositories/workflow_template_repository.rs b/apps/desktop/src-tauri/src/data/repositories/workflow_template_repository.rs index cfd746d..2432e79 100644 --- a/apps/desktop/src-tauri/src/data/repositories/workflow_template_repository.rs +++ b/apps/desktop/src-tauri/src/data/repositories/workflow_template_repository.rs @@ -474,42 +474,65 @@ impl WorkflowTemplateRepository { // 解析工作流类型 let type_str: String = row.get("type")?; - let workflow_type: WorkflowType = serde_json::from_str(&type_str) - .map_err(|e| rusqlite::Error::FromSqlConversionFailure( - 0, rusqlite::types::Type::Text, Box::new(e) - ))?; + let workflow_type: WorkflowType = WorkflowType::from(type_str); let description: Option = row.get("description")?; - // 解析JSON字段 + // 解析JSON字段,处理空字符串的情况 let comfyui_workflow_json_str: String = row.get("comfyui_workflow_json")?; - let comfyui_workflow_json: serde_json::Value = serde_json::from_str(&comfyui_workflow_json_str) - .map_err(|e| rusqlite::Error::FromSqlConversionFailure( - 0, rusqlite::types::Type::Text, Box::new(e) - ))?; + let comfyui_workflow_json: serde_json::Value = if comfyui_workflow_json_str.trim().is_empty() { + serde_json::Value::Object(serde_json::Map::new()) + } else { + serde_json::from_str(&comfyui_workflow_json_str) + .map_err(|e| rusqlite::Error::FromSqlConversionFailure( + 0, rusqlite::types::Type::Text, Box::new(e) + ))? + }; let ui_config_json_str: String = row.get("ui_config_json")?; - let ui_config_json: serde_json::Value = serde_json::from_str(&ui_config_json_str) - .map_err(|e| rusqlite::Error::FromSqlConversionFailure( - 0, rusqlite::types::Type::Text, Box::new(e) - ))?; + let ui_config_json: serde_json::Value = if ui_config_json_str.trim().is_empty() { + serde_json::Value::Object(serde_json::Map::new()) + } else { + serde_json::from_str(&ui_config_json_str) + .map_err(|e| rusqlite::Error::FromSqlConversionFailure( + 0, rusqlite::types::Type::Text, Box::new(e) + ))? + }; let execution_config_json: Option = row.get::<_, Option>("execution_config_json")? - .map(|s| serde_json::from_str(&s)) + .map(|s| { + if s.trim().is_empty() { + Ok(serde_json::Value::Object(serde_json::Map::new())) + } else { + serde_json::from_str(&s) + } + }) .transpose() .map_err(|e| rusqlite::Error::FromSqlConversionFailure( 0, rusqlite::types::Type::Text, Box::new(e) ))?; let input_schema_json: Option = row.get::<_, Option>("input_schema_json")? - .map(|s| serde_json::from_str(&s)) + .map(|s| { + if s.trim().is_empty() { + Ok(serde_json::Value::Object(serde_json::Map::new())) + } else { + serde_json::from_str(&s) + } + }) .transpose() .map_err(|e| rusqlite::Error::FromSqlConversionFailure( 0, rusqlite::types::Type::Text, Box::new(e) ))?; let output_schema_json: Option = row.get::<_, Option>("output_schema_json")? - .map(|s| serde_json::from_str(&s)) + .map(|s| { + if s.trim().is_empty() { + Ok(serde_json::Value::Object(serde_json::Map::new())) + } else { + serde_json::from_str(&s) + } + }) .transpose() .map_err(|e| rusqlite::Error::FromSqlConversionFailure( 0, rusqlite::types::Type::Text, Box::new(e) diff --git a/apps/desktop/src-tauri/src/tests/basic_tests.rs b/apps/desktop/src-tauri/src/tests/basic_tests.rs index 59b2fe8..6c2fbd6 100644 --- a/apps/desktop/src-tauri/src/tests/basic_tests.rs +++ b/apps/desktop/src-tauri/src/tests/basic_tests.rs @@ -130,9 +130,177 @@ fn test_json_operations() { "value": 42, "active": true }); - + assert!(json_value.is_object()); assert_eq!(json_value["name"], "test"); assert_eq!(json_value["value"], 42); assert_eq!(json_value["active"], true); } + +#[tokio::test] +async fn test_database_connection() { + use crate::infrastructure::database::Database; + + // 测试数据库连接 + let database = Database::new().expect("应该能够创建数据库连接"); + + // 运行迁移 + database.run_migrations().expect("应该能够运行数据库迁移"); + + // 测试查询工作流模板表 + let conn = database.get_connection(); + let conn = conn.lock().unwrap(); + + // 检查表是否存在 + let mut stmt = conn.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='workflow_templates'") + .expect("应该能够准备查询语句"); + + let table_exists = stmt.exists([]).expect("应该能够检查表是否存在"); + assert!(table_exists, "workflow_templates 表应该存在"); + + // 检查表结构 + let mut stmt = conn.prepare("PRAGMA table_info(workflow_templates)") + .expect("应该能够准备表结构查询"); + + let column_count = stmt.query_map([], |row| { + Ok(row.get::<_, String>(1)?) // 获取列名 + }).expect("应该能够查询表结构") + .collect::, _>>() + .expect("应该能够收集列信息") + .len(); + + assert!(column_count > 0, "workflow_templates 表应该有列"); + println!("数据库连接测试通过,workflow_templates 表有 {} 列", column_count); +} + +#[tokio::test] +async fn test_workflow_template_repository() { + use crate::data::repositories::workflow_template_repository::WorkflowTemplateRepository; + use crate::infrastructure::database::Database; + use std::sync::Arc; + + // 创建数据库和仓库 + let database = Arc::new(Database::new().expect("应该能够创建数据库")); + database.run_migrations().expect("应该能够运行迁移"); + + // 首先检查数据库中的原始数据 + let conn = database.get_connection(); + let conn = conn.lock().unwrap(); + + // 检查表中是否有数据 + let count: i64 = conn.query_row("SELECT COUNT(*) FROM workflow_templates", [], |row| { + row.get(0) + }).expect("应该能够查询记录数量"); + + println!("数据库中有 {} 条工作流模板记录", count); + + if count > 0 { + // 检查第一条记录的所有字段 + let mut stmt = conn.prepare("SELECT * FROM workflow_templates LIMIT 1") + .expect("应该能够准备查询语句"); + + let result = stmt.query_row([], |row| { + println!("第一条记录的所有字段:"); + for i in 0..row.as_ref().column_count() { + let column_name = row.as_ref().column_name(i).unwrap_or("unknown"); + match row.get_ref(i) { + Ok(value) => { + match value { + rusqlite::types::ValueRef::Null => println!(" {}: NULL", column_name), + rusqlite::types::ValueRef::Integer(v) => println!(" {}: {}", column_name, v), + rusqlite::types::ValueRef::Real(v) => println!(" {}: {}", column_name, v), + rusqlite::types::ValueRef::Text(v) => { + let text = std::str::from_utf8(v).unwrap_or("invalid utf8"); + if text.len() > 100 { + println!(" {}: {} chars, preview: {:?}", column_name, text.len(), &text[..100]); + } else { + println!(" {}: {:?}", column_name, text); + } + }, + rusqlite::types::ValueRef::Blob(v) => println!(" {}: blob {} bytes", column_name, v.len()), + } + }, + Err(e) => println!(" {}: ERROR - {}", column_name, e), + } + } + Ok(()) + }); + + if let Err(e) = result { + println!("查询第一条记录失败: {}", e); + } + } + + drop(conn); // 释放连接 + + // 现在测试手动解析第一条记录 + if count > 0 { + let conn = database.get_connection(); + let conn = conn.lock().unwrap(); + + let mut stmt = conn.prepare("SELECT * FROM workflow_templates LIMIT 1") + .expect("应该能够准备查询语句"); + + let result = stmt.query_row([], |row| { + println!("开始手动解析记录..."); + + // 逐个字段解析,找出问题所在 + let id: Option = Some(row.get("id")?); + println!("✓ ID: {:?}", id); + + let name: String = row.get("name")?; + println!("✓ Name: {}", name); + + let base_name: String = row.get("base_name")?; + println!("✓ Base name: {}", base_name); + + let version: String = row.get("version")?; + println!("✓ Version: {}", version); + + let type_str: String = row.get("type")?; + println!("✓ Type: {}", type_str); + + let description: Option = row.get("description")?; + println!("✓ Description: {:?}", description); + + // 测试JSON字段解析 + let comfyui_workflow_json_str: String = row.get("comfyui_workflow_json")?; + println!("✓ ComfyUI JSON string length: {}", comfyui_workflow_json_str.len()); + + // 尝试解析JSON + match serde_json::from_str::(&comfyui_workflow_json_str) { + Ok(json) => println!("✓ ComfyUI JSON parsed successfully"), + Err(e) => println!("✗ ComfyUI JSON parse error: {}", e), + } + + let ui_config_json_str: String = row.get("ui_config_json")?; + println!("✓ UI Config JSON string length: {}", ui_config_json_str.len()); + + match serde_json::from_str::(&ui_config_json_str) { + Ok(json) => println!("✓ UI Config JSON parsed successfully"), + Err(e) => println!("✗ UI Config JSON parse error: {}", e), + } + + Ok(()) + }); + + if let Err(e) = result { + println!("手动解析失败: {}", e); + } + + drop(conn); + } + + let repo = WorkflowTemplateRepository::new(database); + + // 测试查询所有模板(应该返回空列表而不是错误) + match repo.find_all(None) { + Ok(templates) => { + println!("成功查询工作流模板,数量: {}", templates.len()); + // 空列表是正常的,因为还没有插入数据 + }, + Err(e) => { + panic!("查询工作流模板失败: {}", e); + } + } +}