//! 故障转移队列 DAO //! //! 管理代理模式下的故障转移队列(基于 providers 表的 in_failover_queue 字段) use crate::database::{lock_conn, Database}; use crate::error::AppError; use crate::provider::Provider; use serde::{Deserialize, Serialize}; /// 故障转移队列条目(简化版,用于前端展示) #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FailoverQueueItem { pub provider_id: String, pub provider_name: String, pub sort_index: Option, } impl Database { /// 获取故障转移队列(按 sort_index 排序) pub fn get_failover_queue(&self, app_type: &str) -> Result, AppError> { let conn = lock_conn!(self.conn); let mut stmt = conn .prepare( "SELECT id, name, sort_index FROM providers WHERE app_type = ?1 AND in_failover_queue = 1 ORDER BY COALESCE(sort_index, 999999), id ASC", ) .map_err(|e| AppError::Database(e.to_string()))?; let items = stmt .query_map([app_type], |row| { Ok(FailoverQueueItem { provider_id: row.get(0)?, provider_name: row.get(1)?, sort_index: row.get(2)?, }) }) .map_err(|e| AppError::Database(e.to_string()))? .collect::, _>>() .map_err(|e| AppError::Database(e.to_string()))?; Ok(items) } /// 获取故障转移队列中的供应商(完整 Provider 信息,按顺序) pub fn get_failover_providers(&self, app_type: &str) -> Result, AppError> { let all_providers = self.get_all_providers(app_type)?; let result: Vec = all_providers .into_values() .filter(|p| p.in_failover_queue) .collect(); Ok(result) } /// 添加供应商到故障转移队列 pub fn add_to_failover_queue(&self, app_type: &str, provider_id: &str) -> Result<(), AppError> { let conn = lock_conn!(self.conn); conn.execute( "UPDATE providers SET in_failover_queue = 1 WHERE id = ?1 AND app_type = ?2", rusqlite::params![provider_id, app_type], ) .map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } /// 从故障转移队列中移除供应商 pub fn remove_from_failover_queue( &self, app_type: &str, provider_id: &str, ) -> Result<(), AppError> { let conn = lock_conn!(self.conn); // 1. 从队列中移除 conn.execute( "UPDATE providers SET in_failover_queue = 0 WHERE id = ?1 AND app_type = ?2", rusqlite::params![provider_id, app_type], ) .map_err(|e| AppError::Database(e.to_string()))?; // 2. 清除该供应商的健康状态(退出队列后不再需要健康监控) conn.execute( "DELETE FROM provider_health WHERE provider_id = ?1 AND app_type = ?2", rusqlite::params![provider_id, app_type], ) .map_err(|e| AppError::Database(e.to_string()))?; log::info!("已从故障转移队列移除供应商 {provider_id} ({app_type}), 并清除其健康状态"); Ok(()) } /// 清空故障转移队列 pub fn clear_failover_queue(&self, app_type: &str) -> Result<(), AppError> { let conn = lock_conn!(self.conn); conn.execute( "UPDATE providers SET in_failover_queue = 0 WHERE app_type = ?1", [app_type], ) .map_err(|e| AppError::Database(e.to_string()))?; Ok(()) } /// 检查供应商是否在故障转移队列中 pub fn is_in_failover_queue( &self, app_type: &str, provider_id: &str, ) -> Result { let conn = lock_conn!(self.conn); let in_queue: bool = conn .query_row( "SELECT in_failover_queue FROM providers WHERE id = ?1 AND app_type = ?2", rusqlite::params![provider_id, app_type], |row| row.get(0), ) .unwrap_or(false); Ok(in_queue) } /// 获取可添加到故障转移队列的供应商(不在队列中的) pub fn get_available_providers_for_failover( &self, app_type: &str, ) -> Result, AppError> { let all_providers = self.get_all_providers(app_type)?; let available: Vec = all_providers .into_values() .filter(|p| !p.in_failover_queue) .collect(); Ok(available) } }