diff --git a/apps/desktop/src-tauri/src/data/repositories/project_repository.rs b/apps/desktop/src-tauri/src/data/repositories/project_repository.rs index a6a1c12..ff3e1e4 100644 --- a/apps/desktop/src-tauri/src/data/repositories/project_repository.rs +++ b/apps/desktop/src-tauri/src/data/repositories/project_repository.rs @@ -18,7 +18,13 @@ impl ProjectRepository { /// 创建项目 pub fn create(&self, project: &Project) -> Result<()> { let conn = self.connection.lock().unwrap(); - conn.execute( + + + + // 开始事务 + let tx = conn.unchecked_transaction()?; + + let result = tx.execute( "INSERT INTO projects (id, name, path, description, created_at, updated_at, is_active) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)", [ @@ -28,9 +34,15 @@ impl ProjectRepository { project.description.as_deref().unwrap_or(""), project.created_at.to_rfc3339().as_str(), project.updated_at.to_rfc3339().as_str(), - project.is_active.to_string().as_str(), + if project.is_active { "1" } else { "0" }, ], )?; + + // 提交事务 + tx.commit()?; + + + Ok(()) } @@ -75,6 +87,16 @@ impl ProjectRepository { /// 获取所有活跃项目 pub fn find_all_active(&self) -> Result> { let conn = self.connection.lock().unwrap(); + + // 首先检查表是否存在 + let table_exists: i64 = conn.query_row( + "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='projects'", + [], + |row| row.get(0) + )?; + + + let mut stmt = conn.prepare( "SELECT id, name, path, description, created_at, updated_at, is_active FROM projects WHERE is_active = 1 ORDER BY updated_at DESC" @@ -89,6 +111,7 @@ impl ProjectRepository { projects.push(project?); } + Ok(projects) } @@ -137,12 +160,19 @@ impl ProjectRepository { fn row_to_project(&self, row: &Row) -> Result { let created_at_str: String = row.get(4)?; let updated_at_str: String = row.get(5)?; - let is_active_str: String = row.get(6)?; - + + // 尝试读取 is_active 字段,支持多种数据类型 + let is_active = match row.get::<_, rusqlite::types::Value>(6)? { + rusqlite::types::Value::Integer(i) => i != 0, + rusqlite::types::Value::Text(s) => s == "1" || s.to_lowercase() == "true", + rusqlite::types::Value::Real(f) => f != 0.0, + _ => true, // 默认为 true + }; + let created_at = DateTime::parse_from_rfc3339(&created_at_str) .map_err(|_| rusqlite::Error::InvalidColumnType(4, "created_at".to_string(), rusqlite::types::Type::Text))? .with_timezone(&Utc); - + let updated_at = DateTime::parse_from_rfc3339(&updated_at_str) .map_err(|_| rusqlite::Error::InvalidColumnType(5, "updated_at".to_string(), rusqlite::types::Type::Text))? .with_timezone(&Utc); @@ -157,7 +187,7 @@ impl ProjectRepository { description, created_at, updated_at, - is_active: is_active_str == "1" || is_active_str.to_lowercase() == "true", + is_active, }) } } diff --git a/apps/desktop/src-tauri/src/infrastructure/database.rs b/apps/desktop/src-tauri/src/infrastructure/database.rs index fdf59d0..fc27edb 100644 --- a/apps/desktop/src-tauri/src/infrastructure/database.rs +++ b/apps/desktop/src-tauri/src/infrastructure/database.rs @@ -13,21 +13,31 @@ impl Database { /// 遵循安全第一原则,确保数据库文件的安全存储 pub fn new() -> Result { let db_path = Self::get_database_path(); - + + // 打印数据库路径用于调试 + println!("Initializing database at: {}", db_path.display()); + // 确保数据库目录存在 if let Some(parent) = db_path.parent() { std::fs::create_dir_all(parent).map_err(|e| { + eprintln!("Failed to create database directory: {}", e); rusqlite::Error::SqliteFailure( rusqlite::ffi::Error::new(rusqlite::ffi::SQLITE_CANTOPEN), Some(format!("Failed to create database directory: {}", e)), ) })?; } - - let connection = Connection::open(db_path)?; - - // 启用外键约束 + + let connection = Connection::open(&db_path)?; + println!("Database connection established successfully"); + + // 配置数据库设置 connection.execute("PRAGMA foreign_keys = ON", [])?; + connection.pragma_update(None, "journal_mode", "DELETE")?; // 使用 DELETE 模式而不是 WAL + connection.pragma_update(None, "synchronous", "FULL")?; // 确保数据立即写入磁盘 + connection.pragma_update(None, "cache_size", "10000")?; // 增加缓存大小 + + println!("Database pragmas configured"); let database = Database { connection: Arc::new(Mutex::new(connection)), @@ -35,6 +45,9 @@ impl Database { // 初始化数据库表 database.initialize_tables()?; + + // 运行数据库迁移 + database.run_migrations()?; Ok(database) } @@ -92,13 +105,92 @@ impl Database { Ok(()) } + /// 运行数据库迁移 + fn run_migrations(&self) -> Result<()> { + let conn = self.connection.lock().unwrap(); + + // 修复 is_active 字段的数据类型问题 + println!("Running database migrations..."); + + // 检查是否需要迁移(如果有字符串类型的 is_active 值) + let needs_migration: i64 = conn.query_row( + "SELECT COUNT(*) FROM projects WHERE is_active = 'true' OR is_active = 'false'", + [], + |row| row.get(0) + ).unwrap_or(0); + + if needs_migration > 0 { + println!("Found {} rows that need migration", needs_migration); + + // 将 "true" 字符串转换为 1 + let updated_rows = conn.execute( + "UPDATE projects SET is_active = 1 WHERE is_active = 'true'", + [], + )?; + + if updated_rows > 0 { + println!("Updated {} rows: converted 'true' to 1", updated_rows); + } + + // 将 "false" 字符串转换为 0 + let updated_rows = conn.execute( + "UPDATE projects SET is_active = 0 WHERE is_active = 'false'", + [], + )?; + + if updated_rows > 0 { + println!("Updated {} rows: converted 'false' to 0", updated_rows); + } + } else { + // 如果所有项目的 is_active 都是 0,可能是之前的迁移错误,恢复为 1 + let all_inactive: i64 = conn.query_row( + "SELECT COUNT(*) FROM projects WHERE is_active = 0", + [], + |row| row.get(0) + ).unwrap_or(0); + + let total_projects: i64 = conn.query_row( + "SELECT COUNT(*) FROM projects", + [], + |row| row.get(0) + ).unwrap_or(0); + + if all_inactive == total_projects && total_projects > 0 { + println!("All {} projects are inactive, restoring to active state", total_projects); + let updated_rows = conn.execute( + "UPDATE projects SET is_active = 1", + [], + )?; + println!("Restored {} projects to active state", updated_rows); + } + } + + println!("Database migrations completed"); + Ok(()) + } + /// 获取数据库文件路径 /// 遵循安全存储原则,将数据库存储在应用数据目录 fn get_database_path() -> PathBuf { + // 优先使用应用数据目录 if let Some(data_dir) = dirs::data_dir() { - data_dir.join("mixvideo").join("mixvideo.db") + let app_dir = data_dir.join("mixvideo"); + // 确保目录存在 + if let Err(e) = std::fs::create_dir_all(&app_dir) { + eprintln!("Failed to create app data directory: {}", e); + // 如果创建失败,使用当前目录 + return PathBuf::from("mixvideo.db"); + } + app_dir.join("mixvideo.db") } else { - PathBuf::from(".").join("mixvideo.db") + // 备用方案:使用当前目录 + PathBuf::from("mixvideo.db") } } + + /// 获取数据库路径的调试信息 + pub fn get_database_path_info() -> String { + let path = Self::get_database_path(); + format!("Database path: {}", path.display()) + } } diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index e2597d7..6534ce9 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -30,7 +30,8 @@ pub fn run() { commands::system_commands::select_directory, commands::system_commands::get_app_info, commands::system_commands::validate_directory, - commands::system_commands::get_directory_name + commands::system_commands::get_directory_name, + commands::system_commands::get_database_info ]) .setup(|app| { // 初始化应用状态 diff --git a/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs index d7b2c2f..e805f74 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs @@ -59,10 +59,17 @@ pub async fn validate_directory(path: String) -> Result { #[command] pub async fn get_directory_name(path: String) -> Result { use std::path::Path; - + let path = Path::new(&path); match path.file_name() { Some(name) => Ok(name.to_string_lossy().to_string()), None => Err("无效的目录路径".to_string()), } } + +/// 获取数据库信息命令(调试用) +#[command] +pub async fn get_database_info() -> Result { + use crate::infrastructure::database::Database; + Ok(Database::get_database_path_info()) +}