Skip to main content

cc_switch_lib/database/dao/
failover.rs

1//! 故障转移队列 DAO
2//!
3//! 管理代理模式下的故障转移队列(基于 providers 表的 in_failover_queue 字段)
4
5use crate::database::{lock_conn, Database};
6use crate::error::AppError;
7use crate::provider::Provider;
8use serde::{Deserialize, Serialize};
9
10/// 故障转移队列条目(简化版,用于前端展示)
11#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(rename_all = "camelCase")]
13pub struct FailoverQueueItem {
14    pub provider_id: String,
15    pub provider_name: String,
16    pub sort_index: Option<usize>,
17}
18
19impl Database {
20    /// 获取故障转移队列(按 sort_index 排序)
21    pub fn get_failover_queue(&self, app_type: &str) -> Result<Vec<FailoverQueueItem>, AppError> {
22        let conn = lock_conn!(self.conn);
23
24        let mut stmt = conn
25            .prepare(
26                "SELECT id, name, sort_index
27                 FROM providers
28                 WHERE app_type = ?1 AND in_failover_queue = 1
29                 ORDER BY COALESCE(sort_index, 999999), id ASC",
30            )
31            .map_err(|e| AppError::Database(e.to_string()))?;
32
33        let items = stmt
34            .query_map([app_type], |row| {
35                Ok(FailoverQueueItem {
36                    provider_id: row.get(0)?,
37                    provider_name: row.get(1)?,
38                    sort_index: row.get(2)?,
39                })
40            })
41            .map_err(|e| AppError::Database(e.to_string()))?
42            .collect::<Result<Vec<_>, _>>()
43            .map_err(|e| AppError::Database(e.to_string()))?;
44
45        Ok(items)
46    }
47
48    /// 获取故障转移队列中的供应商(完整 Provider 信息,按顺序)
49    pub fn get_failover_providers(&self, app_type: &str) -> Result<Vec<Provider>, AppError> {
50        let all_providers = self.get_all_providers(app_type)?;
51
52        let result: Vec<Provider> = all_providers
53            .into_values()
54            .filter(|p| p.in_failover_queue)
55            .collect();
56
57        Ok(result)
58    }
59
60    /// 添加供应商到故障转移队列
61    pub fn add_to_failover_queue(&self, app_type: &str, provider_id: &str) -> Result<(), AppError> {
62        let conn = lock_conn!(self.conn);
63
64        conn.execute(
65            "UPDATE providers SET in_failover_queue = 1 WHERE id = ?1 AND app_type = ?2",
66            rusqlite::params![provider_id, app_type],
67        )
68        .map_err(|e| AppError::Database(e.to_string()))?;
69
70        Ok(())
71    }
72
73    /// 从故障转移队列中移除供应商
74    pub fn remove_from_failover_queue(
75        &self,
76        app_type: &str,
77        provider_id: &str,
78    ) -> Result<(), AppError> {
79        let conn = lock_conn!(self.conn);
80
81        // 1. 从队列中移除
82        conn.execute(
83            "UPDATE providers SET in_failover_queue = 0 WHERE id = ?1 AND app_type = ?2",
84            rusqlite::params![provider_id, app_type],
85        )
86        .map_err(|e| AppError::Database(e.to_string()))?;
87
88        // 2. 清除该供应商的健康状态(退出队列后不再需要健康监控)
89        conn.execute(
90            "DELETE FROM provider_health WHERE provider_id = ?1 AND app_type = ?2",
91            rusqlite::params![provider_id, app_type],
92        )
93        .map_err(|e| AppError::Database(e.to_string()))?;
94
95        log::info!("已从故障转移队列移除供应商 {provider_id} ({app_type}), 并清除其健康状态");
96
97        Ok(())
98    }
99
100    /// 清空故障转移队列
101    pub fn clear_failover_queue(&self, app_type: &str) -> Result<(), AppError> {
102        let conn = lock_conn!(self.conn);
103
104        conn.execute(
105            "UPDATE providers SET in_failover_queue = 0 WHERE app_type = ?1",
106            [app_type],
107        )
108        .map_err(|e| AppError::Database(e.to_string()))?;
109
110        Ok(())
111    }
112
113    /// 检查供应商是否在故障转移队列中
114    pub fn is_in_failover_queue(
115        &self,
116        app_type: &str,
117        provider_id: &str,
118    ) -> Result<bool, AppError> {
119        let conn = lock_conn!(self.conn);
120
121        let in_queue: bool = conn
122            .query_row(
123                "SELECT in_failover_queue FROM providers WHERE id = ?1 AND app_type = ?2",
124                rusqlite::params![provider_id, app_type],
125                |row| row.get(0),
126            )
127            .unwrap_or(false);
128
129        Ok(in_queue)
130    }
131
132    /// 获取可添加到故障转移队列的供应商(不在队列中的)
133    pub fn get_available_providers_for_failover(
134        &self,
135        app_type: &str,
136    ) -> Result<Vec<Provider>, AppError> {
137        let all_providers = self.get_all_providers(app_type)?;
138
139        let available: Vec<Provider> = all_providers
140            .into_values()
141            .filter(|p| !p.in_failover_queue)
142            .collect();
143
144        Ok(available)
145    }
146}