use rusqlite::{params, Connection}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CloudProduct { pub id: String, pub vendor: String, pub product_type: String, pub name: String, pub region: Option, pub instance_id: Option, pub endpoint: Option, pub expire_at: Option, pub status: String, pub config_data: String, pub notes: Option, pub sort_order: i32, pub created_at: Option, pub updated_at: Option, } #[derive(Debug, Deserialize)] pub struct SaveCloudProductInput { pub vendor: String, pub product_type: String, pub name: String, pub region: Option, pub instance_id: Option, pub endpoint: Option, pub expire_at: Option, pub status: String, pub config_data: Option, pub notes: Option, pub sort_order: Option, } fn row_to_cloud_product(row: &rusqlite::Row<'_>) -> rusqlite::Result { Ok(CloudProduct { id: row.get(0)?, vendor: row.get(1)?, product_type: row.get(2)?, name: row.get(3)?, region: row.get(4)?, instance_id: row.get(5)?, endpoint: row.get(6)?, expire_at: row.get(7)?, status: row.get(8)?, config_data: row.get(9)?, notes: row.get(10)?, sort_order: row.get(11)?, created_at: row.get(12)?, updated_at: row.get(13)?, }) } const SELECT_COLS: &str = "id, vendor, product_type, name, region, instance_id, endpoint, expire_at, \ status, config_data, notes, sort_order, created_at, updated_at"; pub fn list_cloud_products(conn: &Connection) -> Vec { let sql = format!( "SELECT {SELECT_COLS} FROM cloud_products ORDER BY vendor, sort_order, name" ); let mut stmt = match conn.prepare(&sql) { Ok(s) => s, Err(_) => return vec![], }; stmt.query_map([], row_to_cloud_product) .map(|iter| iter.filter_map(|r| r.ok()).collect()) .unwrap_or_default() } fn get_cloud_product_by_id(conn: &Connection, id: &str) -> Option { let sql = format!("SELECT {SELECT_COLS} FROM cloud_products WHERE id = ?1"); conn.query_row(&sql, params![id], row_to_cloud_product).ok() } pub fn create_cloud_product(conn: &Connection, input: &SaveCloudProductInput) -> Result { let id = format!("cp-{}", uuid::Uuid::new_v4()); let config_data = input.config_data.as_deref().unwrap_or("{}"); conn.execute( "INSERT INTO cloud_products (id, vendor, product_type, name, region, instance_id, endpoint, expire_at, status, config_data, notes, sort_order) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12)", params![ id, input.vendor, input.product_type, input.name, input.region, input.instance_id, input.endpoint, input.expire_at, input.status, config_data, input.notes, input.sort_order.unwrap_or(0) ], ) .map_err(|e| e.to_string())?; get_cloud_product_by_id(conn, &id).ok_or_else(|| "创建后查询失败".into()) } pub fn update_cloud_product(conn: &Connection, id: &str, input: &SaveCloudProductInput) -> Result { let config_data = input.config_data.as_deref().unwrap_or("{}"); let rows = conn.execute( "UPDATE cloud_products SET vendor=?1, product_type=?2, name=?3, region=?4, instance_id=?5, endpoint=?6, expire_at=?7, status=?8, config_data=?9, notes=?10, sort_order=?11, updated_at=datetime('now') WHERE id=?12", params![ input.vendor, input.product_type, input.name, input.region, input.instance_id, input.endpoint, input.expire_at, input.status, config_data, input.notes, input.sort_order.unwrap_or(0), id ], ) .map_err(|e| e.to_string())?; if rows == 0 { return Err(format!("云产品 {} 不存在", id)); } get_cloud_product_by_id(conn, id).ok_or_else(|| "更新后查询失败".into()) } pub fn delete_cloud_product_fn(conn: &Connection, id: &str) -> Result<(), String> { conn.execute("DELETE FROM cloud_products WHERE id=?1", params![id]) .map_err(|e| e.to_string())?; Ok(()) } // ── Tauri commands ─────────────────────────────────────────────────────────── #[tauri::command] pub fn get_cloud_products() -> Result, String> { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; Ok(list_cloud_products(&conn)) } #[tauri::command] pub fn add_cloud_product(input: SaveCloudProductInput) -> Result { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; create_cloud_product(&conn, &input) } #[tauri::command] pub fn edit_cloud_product(id: String, input: SaveCloudProductInput) -> Result { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; update_cloud_product(&conn, &id, &input) } #[tauri::command] pub fn delete_cloud_product(id: String) -> Result<(), String> { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; delete_cloud_product_fn(&conn, &id) }