//! 供应商数据访问对象 //! //! 提供供应商(Provider)的 CRUD 操作。 use crate::database::{lock_conn, Database}; use crate::error::AppError; use crate::provider::{Provider, ProviderMeta}; use indexmap::IndexMap; use rusqlite::params; use std::collections::HashMap; impl Database { /// 获取指定应用类型的所有供应商 pub fn get_all_providers( &self, app_type: &str, ) -> Result, AppError> { let conn = lock_conn!(self.conn); let mut stmt = conn.prepare( "SELECT id, name, settings_config, website_url, category, created_at, sort_index, notes, icon, icon_color, meta, in_failover_queue FROM providers WHERE app_type = ?1 ORDER BY COALESCE(sort_index, 999999), created_at ASC, id ASC" ).map_err(|e| AppError::Database(e.to_string()))?; let provider_iter = stmt .query_map(params![app_type], |row| { let id: String = row.get(0)?; let name: String = row.get(1)?; let settings_config_str: String = row.get(2)?; let website_url: Option = row.get(3)?; let category: Option = row.get(4)?; let created_at: Option = row.get(5)?; let sort_index: Option = row.get(6)?; let notes: Option = row.get(7)?; let icon: Option = row.get(8)?; let icon_color: Option = row.get(9)?; let meta_str: String = row.get(10)?; let in_failover_queue: bool = row.get(11)?; let settings_config = serde_json::from_str(&settings_config_str).unwrap_or(serde_json::Value::Null); let meta: ProviderMeta = serde_json::from_str(&meta_str).unwrap_or_default(); Ok(( id, Provider { id: "".to_string(), // Placeholder, set below name, settings_config, website_url, category, created_at, sort_index, notes, meta: Some(meta), icon, icon_color, in_failover_queue, }, )) }) .map_err(|e| AppError::Database(e.to_string()))?; let mut providers = IndexMap::new(); for provider_res in provider_iter { let (id, mut provider) = provider_res.map_err(|e| AppError::Database(e.to_string()))?; provider.id = id.clone(); // 加载 endpoints let mut stmt_endpoints = conn.prepare( "SELECT url, added_at FROM provider_endpoints WHERE provider_id = ?1 AND app_type = ?2 ORDER BY added_at ASC, url ASC" ).map_err(|e| AppError::Database(e.to_string()))?; let endpoints_iter = stmt_endpoints .query_map(params![id, app_type], |row| { let url: String = row.get(0)?; let added_at: Option = row.get(1)?; Ok(( url, crate::settings::CustomEndpoint { url: "".to_string(), added_at: added_at.unwrap_or(0), last_used: None, }, )) }) .map_err(|e| AppError::Database(e.to_string()))?; let mut custom_endpoints = HashMap::new(); for ep_res in endpoints_iter { let (url, mut ep) = ep_res.map_err(|e| AppError::Database(e.to_string()))?; ep.url = url.clone(); custom_endpoints.insert(url, ep); } if let Some(meta) = &mut provider.meta { meta.custom_endpoints = custom_endpoints; } providers.insert(id, provider); } Ok(providers) } /// 获取当前激活的供应商 ID pub fn get_current_provider(&self, app_type: &str) -> Result, AppError> { let conn = lock_conn!(self.conn); let mut stmt = conn .prepare("SELECT id FROM providers WHERE app_type = ?1 AND is_current = 1 LIMIT 1") .map_err(|e| AppError::Database(e.to_string()))?; let mut rows = stmt .query(params![app_type]) .map_err(|e| AppError::Database(e.to_string()))?; if let Some(row) = rows.next().map_err(|e| AppError::Database(e.to_string()))? { Ok(Some( row.get(0).map_err(|e| AppError::Database(e.to_string()))?, )) } else { Ok(None) } } /// 根据 ID 获取单个供应商 pub fn get_provider_by_id( &self, id: &str, app_type: &str, ) -> Result, AppError> { let conn = lock_conn!(self.conn); let result = conn.query_row( "SELECT name, settings_config, website_url, category, created_at, sort_index, notes, icon, icon_color, meta, in_failover_queue FROM providers WHERE id = ?1 AND app_type = ?2", params![id, app_type], |row| { let name: String = row.get(0)?; let settings_config_str: String = row.get(1)?; let website_url: Option = row.get(2)?; let category: Option = row.get(3)?; let created_at: Option = row.get(4)?; let sort_index: Option = row.get(5)?; let notes: Option = row.get(6)?; let icon: Option = row.get(7)?; let icon_color: Option = row.get(8)?; let meta_str: String = row.get(9)?; let in_failover_queue: bool = row.get(10)?; let settings_config = serde_json::from_str(&settings_config_str).unwrap_or(serde_json::Value::Null); let meta: ProviderMeta = serde_json::from_str(&meta_str).unwrap_or_default(); Ok(Provider { id: id.to_string(), name, settings_config, website_url, category, created_at, sort_index, notes, meta: Some(meta), icon, icon_color, in_failover_queue, }) }, ); match result { Ok(provider) => Ok(Some(provider)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), Err(e) => Err(AppError::Database(e.to_string())), } } /// 保存供应商(新增或更新) /// /// 注意:更新模式下不同步 endpoints,因为编辑模式下端点通过单独的 API 管理 /// (add_custom_endpoint / remove_custom_endpoint),避免覆盖用户的修改。 pub fn save_provider(&self, app_type: &str, provider: &Provider) -> Result<(), AppError> { let mut conn = lock_conn!(self.conn); let tx = conn .transaction() .map_err(|e| AppError::Database(e.to_string()))?; // 处理 meta:取出 endpoints 以便单独处理 let mut meta_clone = provider.meta.clone().unwrap_or_default(); let endpoints = std::mem::take(&mut meta_clone.custom_endpoints); // 检查是否存在(用于判断新增/更新,以及保留 is_current 和 in_failover_queue) let existing: Option<(bool, bool)> = tx .query_row( "SELECT is_current, in_failover_queue FROM providers WHERE id = ?1 AND app_type = ?2", params![provider.id, app_type], |row| Ok((row.get(0)?, row.get(1)?)), ) .ok(); let is_update = existing.is_some(); let (is_current, in_failover_queue) = existing.unwrap_or((false, provider.in_failover_queue)); if is_update { // 更新模式:使用 UPDATE 避免触发 ON DELETE CASCADE tx.execute( "UPDATE providers SET name = ?1, settings_config = ?2, website_url = ?3, category = ?4, created_at = ?5, sort_index = ?6, notes = ?7, icon = ?8, icon_color = ?9, meta = ?10, is_current = ?11, in_failover_queue = ?12 WHERE id = ?13 AND app_type = ?14", params![ provider.name, serde_json::to_string(&provider.settings_config).map_err(|e| { AppError::Database(format!("Failed to serialize settings_config: {e}")) })?, provider.website_url, provider.category, provider.created_at, provider.sort_index, provider.notes, provider.icon, provider.icon_color, serde_json::to_string(&meta_clone).map_err(|e| AppError::Database(format!( "Failed to serialize meta: {e}" )))?, is_current, in_failover_queue, provider.id, app_type, ], ) .map_err(|e| AppError::Database(e.to_string()))?; } else { // 新增模式:使用 INSERT tx.execute( "INSERT INTO providers ( id, app_type, name, settings_config, website_url, category, created_at, sort_index, notes, icon, icon_color, meta, is_current, in_failover_queue ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)", params![ provider.id, app_type, provider.name, serde_json::to_string(&provider.settings_config) .map_err(|e| AppError::Database(format!("Failed to serialize settings_config: {e}")))?, provider.website_url, provider.category, provider.created_at, provider.sort_index, provider.notes, provider.icon, provider.icon_color, serde_json::to_string(&meta_clone) .map_err(|e| AppError::Database(format!("Failed to serialize meta: {e}")))?, is_current, in_failover_queue, ], ) .map_err(|e| AppError::Database(e.to_string()))?; // 只有新增时才同步 endpoints for (url, endpoint) in endpoints { tx.execute( "INSERT INTO provider_endpoints (provider_id, app_type, url, added_at) VALUES (?1, ?2, ?3, ?4)", params![provider.id, app_type, url, endpoint.added_at], ) .map_err(|e| AppError::Database(e.to_string()))?; } } tx.commit().map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } /// 删除供应商 pub fn delete_provider(&self, app_type: &str, id: &str) -> Result<(), AppError> { let conn = lock_conn!(self.conn); conn.execute( "DELETE FROM providers WHERE id = ?1 AND app_type = ?2", params![id, app_type], ) .map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } /// 设置当前供应商 pub fn set_current_provider(&self, app_type: &str, id: &str) -> Result<(), AppError> { let mut conn = lock_conn!(self.conn); let tx = conn .transaction() .map_err(|e| AppError::Database(e.to_string()))?; // 重置所有为 0 tx.execute( "UPDATE providers SET is_current = 0 WHERE app_type = ?1", params![app_type], ) .map_err(|e| AppError::Database(e.to_string()))?; // 设置新的当前供应商 tx.execute( "UPDATE providers SET is_current = 1 WHERE id = ?1 AND app_type = ?2", params![id, app_type], ) .map_err(|e| AppError::Database(e.to_string()))?; tx.commit().map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } /// 更新供应商的 settings_config(仅更新配置,不改变其他字段) pub fn update_provider_settings_config( &self, app_type: &str, provider_id: &str, settings_config: &serde_json::Value, ) -> Result<(), AppError> { let conn = lock_conn!(self.conn); conn.execute( "UPDATE providers SET settings_config = ?1 WHERE id = ?2 AND app_type = ?3", params![ serde_json::to_string(settings_config).map_err(|e| AppError::Database(format!( "Failed to serialize settings_config: {e}" )))?, provider_id, app_type ], ) .map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } /// 添加自定义端点 pub fn add_custom_endpoint( &self, app_type: &str, provider_id: &str, url: &str, ) -> Result<(), AppError> { let conn = lock_conn!(self.conn); let added_at = chrono::Utc::now().timestamp_millis(); conn.execute( "INSERT INTO provider_endpoints (provider_id, app_type, url, added_at) VALUES (?1, ?2, ?3, ?4)", params![provider_id, app_type, url, added_at], ).map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } /// 移除自定义端点 pub fn remove_custom_endpoint( &self, app_type: &str, provider_id: &str, url: &str, ) -> Result<(), AppError> { let conn = lock_conn!(self.conn); conn.execute( "DELETE FROM provider_endpoints WHERE provider_id = ?1 AND app_type = ?2 AND url = ?3", params![provider_id, app_type, url], ) .map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } }