diff --git a/src-tauri/src/commands/database.rs b/src-tauri/src/commands/database.rs index b22bd26..fc75c19 100644 --- a/src-tauri/src/commands/database.rs +++ b/src-tauri/src/commands/database.rs @@ -4,6 +4,8 @@ use std::sync::Arc; use tauri::State; use crate::db::connection::{create_postgres_connection, create_sqlite_connection}; +use crate::db::connection::DatabaseType; +use crate::db::migration::run_migration; use crate::services::permission::PermissionLevel; use crate::state::AppState; @@ -119,6 +121,9 @@ pub async fn db_switch_connection( let conn = create_postgres_connection(&connection_string) .await .map_err(|e| e.to_string())?; + run_migration(&conn, DatabaseType::PostgreSQL) + .await + .map_err(|e| e.to_string())?; let state_guard = state.read(); let mut db_guard = state_guard.db.write(); @@ -138,6 +143,9 @@ pub async fn db_switch_connection( let conn = create_sqlite_connection(&path) .await .map_err(|e| e.to_string())?; + run_migration(&conn, DatabaseType::SQLite) + .await + .map_err(|e| e.to_string())?; let state_guard = state.read(); let mut db_guard = state_guard.db.write(); @@ -189,7 +197,24 @@ pub async fn db_sync( let state_guard = state.read(); let db_guard = state_guard.db.read(); if let Some(conn) = db_guard.as_ref() { - match conn.ping().await { + let settings = state_guard.settings.read(); + let status_json = settings.get_value(crate::services::settings::SettingsKey::PgConnectionStatus); + let db_type = match status_json { + crate::services::settings::SettingsValue::Json(json) => json + .get("type") + .and_then(|t| t.as_str()) + .map(|s| s.to_string()) + .unwrap_or_else(|| "sqlite".to_string()), + _ => "sqlite".to_string(), + }; + + let migration_result = if db_type == "postgresql" { + run_migration(conn, DatabaseType::PostgreSQL).await + } else { + run_migration(conn, DatabaseType::SQLite).await + }; + + match migration_result { Ok(_) => Ok(IpcResponse::success(SyncResult { success: true, message: Some("Database synchronized successfully".to_string()), diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c9e06f0..b3b853a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -6,6 +6,8 @@ pub mod state; pub mod utils; use crate::db::connection::create_sqlite_connection; +use crate::db::connection::DatabaseType; +use crate::db::migration::run_migration; use tauri::{ image::Image, menu::{Menu, MenuItem}, @@ -51,6 +53,10 @@ fn setup_database(app: &mut App) -> Result<(), Box> { tauri::async_runtime::spawn(async move { match create_sqlite_connection(&db_path_str).await { Ok(conn) => { + if let Err(e) = run_migration(&conn, DatabaseType::SQLite).await { + eprintln!("Failed to run sqlite migration: {}", e); + return; + } let state = handle.state::(); let state_guard = state.write(); let mut db_guard = state_guard.db.write(); diff --git a/src/components/OOBE/OOBE.tsx b/src/components/OOBE/OOBE.tsx index 01d699e..994a7e5 100644 --- a/src/components/OOBE/OOBE.tsx +++ b/src/components/OOBE/OOBE.tsx @@ -98,6 +98,20 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { const primaryColor = workingTheme?.config?.tdesign?.brandColor || "#1677FF" const isDark = workingTheme?.mode === "dark" + const showOobeMessage = ( + type: "success" | "error" | "warning" | "info", + content: string + ) => { + messageApi.open({ + type, + content, + style: { + marginTop: 8, + zIndex: 12000, + }, + }) + } + useEffect(() => { if (!currentTheme) return const base = deepClone(currentTheme) @@ -186,7 +200,7 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { }) if (newStudents.length === 0) { - messageApi.warning(t("oobe.steps.students.studentExists")) + showOobeMessage("warning", t("oobe.steps.students.studentExists")) return } @@ -237,14 +251,15 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { .map((name) => ({ name: name.trim() })) if (newStudents.length > 0) { setStudents([...students, ...newStudents]) - messageApi.success( + showOobeMessage( + "success", t("oobe.steps.students.importSuccess", { count: newStudents.length }) ) } else { - messageApi.info(t("oobe.steps.students.noNewStudents")) + showOobeMessage("info", t("oobe.steps.students.noNewStudents")) } } catch { - messageApi.error(t("oobe.steps.students.parseFailed")) + showOobeMessage("error", t("oobe.steps.students.parseFailed")) } } else if (ext === "xlsx") { try { @@ -266,17 +281,18 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { .map((name) => ({ name })) if (newStudents.length > 0) { setStudents([...students, ...newStudents]) - messageApi.success( + showOobeMessage( + "success", t("oobe.steps.students.importSuccess", { count: newStudents.length }) ) } else { - messageApi.info(t("oobe.steps.students.noNewStudents")) + showOobeMessage("info", t("oobe.steps.students.noNewStudents")) } } catch { - messageApi.error(t("oobe.steps.students.parseFailed")) + showOobeMessage("error", t("oobe.steps.students.parseFailed")) } } else { - messageApi.error(t("oobe.steps.students.unsupportedFormat")) + showOobeMessage("error", t("oobe.steps.students.unsupportedFormat")) } }, [students, messageApi] @@ -319,6 +335,12 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { } } + const syncRes = await (window as any).api.dbSync() + ensureSuccess(syncRes, t("common.error")) + if (!syncRes?.data?.success) { + throw new Error(syncRes?.data?.message || t("common.error")) + } + if (workingTheme) { const exists = themes.some((t) => t.id === workingTheme.id) if (!exists) { @@ -355,10 +377,10 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { const res = await (window as any).api.setSetting("is_wizard_completed", true) ensureSuccess(res, "failed") - messageApi.success(t("common.success")) + showOobeMessage("success", t("common.success")) onComplete() } catch (e: any) { - messageApi.error(e?.message || t("common.error")) + showOobeMessage("error", e?.message || t("common.error")) } finally { setLoading(false) }