Skip to main content

client_core/
database.rs

1use crate::db::DuckDbManager;
2use anyhow::Result;
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::{path::Path, sync::Arc};
6use uuid::Uuid;
7
8/// 数据库管理器 - DuckDB适配器
9#[derive(Debug, Clone)]
10pub struct Database {
11    manager: Arc<DuckDbManager>,
12}
13
14/// 客户端身份信息
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ClientIdentity {
17    pub id: i64,
18    pub client_uuid: Uuid,
19    pub created_at: DateTime<Utc>,
20}
21
22/// 备份记录
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct BackupRecord {
25    pub id: i64,
26    pub file_path: String,
27    pub service_version: String,
28    pub backup_type: BackupType,
29    pub status: BackupStatus,
30    pub created_at: DateTime<Utc>,
31}
32
33/// 备份类型
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub enum BackupType {
36    Manual,
37    PreUpgrade,
38}
39
40/// 备份状态
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42pub enum BackupStatus {
43    Completed,
44    Failed,
45}
46
47/// 计划任务
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct ScheduledTask {
50    pub id: i64,
51    pub task_type: TaskType,
52    pub target_version: String,
53    pub scheduled_at: DateTime<Utc>,
54    pub status: TaskStatus,
55    pub details: Option<String>,
56    pub created_at: DateTime<Utc>,
57    pub completed_at: Option<DateTime<Utc>>,
58}
59
60/// 任务类型
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub enum TaskType {
63    ServiceUpgrade,
64}
65
66/// 任务状态
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub enum TaskStatus {
69    Pending,
70    InProgress,
71    Completed,
72    Failed,
73    Cancelled,
74}
75
76impl Database {
77    /// 连接到数据库
78    pub async fn connect<P: AsRef<Path>>(db_path: P) -> Result<Self> {
79        let manager = DuckDbManager::new(db_path).await?;
80        Ok(Database {
81            manager: Arc::new(manager),
82        })
83    }
84
85    /// 连接到内存数据库 (主要用于测试,生产环境建议使用connect()以确保数据持久化)
86    pub async fn connect_memory() -> Result<Self> {
87        let manager = DuckDbManager::new_memory().await?;
88        Ok(Database {
89            manager: Arc::new(manager),
90        })
91    }
92
93    /// 显式初始化数据库(只应在 nuwax-cli init 时调用)
94    pub async fn init_database(&self) -> Result<()> {
95        self.manager.init_database().await?;
96        // 标记数据库为已初始化
97        self.manager.mark_database_initialized().await?;
98        Ok(())
99    }
100
101    /// 检查数据库是否已经初始化
102    pub async fn is_database_initialized(&self) -> Result<bool> {
103        self.manager.is_database_initialized().await
104    }
105
106    /// 运行数据库迁移 (DuckDB版本中此方法为空操作,因为表在初始化时自动创建)
107    pub async fn run_migrations(&self) -> Result<()> {
108        // DuckDB版本中,表结构在初始化时自动创建,所以这里不需要做任何事情
109        Ok(())
110    }
111
112    /// 获取或创建客户端 UUID
113    pub async fn get_or_create_client_uuid(&self) -> Result<Uuid> {
114        self.manager.get_or_create_client_uuid().await
115    }
116
117    /// 从数据库获取客户端UUID
118    pub async fn get_client_uuid(&self) -> Result<Option<Uuid>> {
119        if let Some(uuid_str) = self.manager.get_config("client_uuid").await? {
120            Ok(Some(Uuid::parse_str(&uuid_str)?))
121        } else {
122            Ok(None)
123        }
124    }
125
126    /// 设置客户端UUID
127    pub async fn set_client_uuid(&self, uuid: &Uuid) -> Result<()> {
128        self.manager
129            .set_config("client_uuid", &uuid.to_string())
130            .await
131    }
132
133    /// 更新客户端ID(服务端返回的ID)
134    pub async fn update_client_id(&self, client_id: &str) -> Result<()> {
135        self.manager.set_config("client_id", client_id).await
136    }
137
138    /// 获取客户端ID(服务端返回的ID)
139    pub async fn get_client_id(&self) -> Result<Option<String>> {
140        self.manager.get_config("client_id").await
141    }
142
143    /// 获取用于API请求的客户端标识(只使用服务端返回的client_id)
144    pub async fn get_api_client_id(&self) -> Result<Option<String>> {
145        // 只使用服务端返回的client_id,不使用本地UUID
146        // 因为服务端不认识本地UUID,会导致401错误
147        self.get_client_id().await
148    }
149
150    /// 通用配置项获取
151    pub async fn get_config(&self, key: &str) -> Result<Option<String>> {
152        self.manager.get_config(key).await
153    }
154
155    /// 通用配置项设置
156    pub async fn set_config(&self, key: &str, value: &str) -> Result<()> {
157        self.manager.set_config(key, value).await
158    }
159
160    /// 获取客户端身份信息 (兼容性方法,DuckDB版本中简化实现)
161    pub async fn get_client_identity(&self) -> Result<Option<ClientIdentity>> {
162        if let Some(uuid) = self.get_client_uuid().await? {
163            // 从配置中获取创建时间,如果不存在则使用当前时间
164            let created_at =
165                if let Some(created_at_str) = self.get_config("client_created_at").await? {
166                    DateTime::parse_from_rfc3339(&created_at_str)
167                        .map(|dt| dt.with_timezone(&Utc))
168                        .unwrap_or_else(|_| Utc::now())
169                } else {
170                    let now = Utc::now();
171                    // 保存创建时间
172                    let _ = self
173                        .set_config("client_created_at", &now.to_rfc3339())
174                        .await;
175                    now
176                };
177
178            Ok(Some(ClientIdentity {
179                id: 1, // 固定ID,因为只有一个客户端身份
180                client_uuid: uuid,
181                created_at,
182            }))
183        } else {
184            Ok(None)
185        }
186    }
187
188    /// 创建备份记录
189    pub async fn create_backup_record(
190        &self,
191        file_path: String,
192        service_version: String,
193        backup_type: BackupType,
194        status: BackupStatus,
195    ) -> Result<i64> {
196        let backup_type_str = match backup_type {
197            BackupType::Manual => "manual",
198            BackupType::PreUpgrade => "pre-upgrade",
199        };
200
201        let status_str = match status {
202            BackupStatus::Completed => "completed",
203            BackupStatus::Failed => "failed",
204        };
205
206        self.manager
207            .create_backup_record(file_path, service_version, backup_type_str, status_str)
208            .await
209    }
210
211    /// 获取所有备份记录
212    pub async fn get_all_backups(&self) -> Result<Vec<BackupRecord>> {
213        let duckdb_backups = self.manager.get_all_backups().await?;
214
215        let mut backups = Vec::new();
216        for backup in duckdb_backups {
217            let backup_type = match backup.backup_type.as_str() {
218                "manual" => BackupType::Manual,
219                "pre-upgrade" => BackupType::PreUpgrade,
220                _ => BackupType::Manual,
221            };
222
223            let status = match backup.status.as_str() {
224                "completed" => BackupStatus::Completed,
225                "failed" => BackupStatus::Failed,
226                _ => BackupStatus::Failed,
227            };
228
229            backups.push(BackupRecord {
230                id: backup.id,
231                file_path: backup.file_path,
232                service_version: backup.service_version,
233                backup_type,
234                status,
235                created_at: backup.created_at,
236            });
237        }
238
239        Ok(backups)
240    }
241
242    /// 根据 ID 获取备份记录
243    pub async fn get_backup_by_id(&self, id: i64) -> Result<Option<BackupRecord>> {
244        if let Some(backup) = self.manager.get_backup_by_id(id).await? {
245            let backup_type = match backup.backup_type.as_str() {
246                "manual" => BackupType::Manual,
247                "pre-upgrade" => BackupType::PreUpgrade,
248                _ => BackupType::Manual,
249            };
250
251            let status = match backup.status.as_str() {
252                "completed" => BackupStatus::Completed,
253                "failed" => BackupStatus::Failed,
254                _ => BackupStatus::Failed,
255            };
256
257            Ok(Some(BackupRecord {
258                id: backup.id,
259                file_path: backup.file_path,
260                service_version: backup.service_version,
261                backup_type,
262                status,
263                created_at: backup.created_at,
264            }))
265        } else {
266            Ok(None)
267        }
268    }
269
270    /// 创建计划任务
271    pub async fn create_scheduled_task(
272        &self,
273        task_type: TaskType,
274        target_version: String,
275        scheduled_at: DateTime<Utc>,
276    ) -> Result<i64> {
277        let task_type_str = match task_type {
278            TaskType::ServiceUpgrade => "SERVICE_UPGRADE",
279        };
280
281        let id = self
282            .manager
283            .create_scheduled_task(task_type_str, target_version, scheduled_at)
284            .await?;
285
286        Ok(id)
287    }
288
289    /// 获取待执行的任务
290    pub async fn get_pending_tasks(&self) -> Result<Vec<ScheduledTask>> {
291        let duckdb_tasks = self.manager.get_pending_tasks().await?;
292
293        let mut tasks = Vec::new();
294        for task in duckdb_tasks {
295            let task_type = match task.task_type.as_str() {
296                "SERVICE_UPGRADE" => TaskType::ServiceUpgrade,
297                _ => TaskType::ServiceUpgrade,
298            };
299
300            let status = match task.status.as_str() {
301                "PENDING" => TaskStatus::Pending,
302                "IN_PROGRESS" => TaskStatus::InProgress,
303                "COMPLETED" => TaskStatus::Completed,
304                "FAILED" => TaskStatus::Failed,
305                "CANCELLED" => TaskStatus::Cancelled,
306                _ => TaskStatus::Pending,
307            };
308
309            tasks.push(ScheduledTask {
310                id: task.id,
311                task_type,
312                target_version: task.target_version,
313                scheduled_at: task.scheduled_at,
314                status,
315                details: task.details,
316                created_at: task.created_at,
317                completed_at: task.completed_at,
318            });
319        }
320
321        Ok(tasks)
322    }
323
324    /// 更新任务状态
325    pub async fn update_task_status(
326        &self,
327        task_id: i64,
328        status: TaskStatus,
329        details: Option<String>,
330    ) -> Result<()> {
331        let status_str = match status {
332            TaskStatus::Pending => "PENDING",
333            TaskStatus::InProgress => "IN_PROGRESS",
334            TaskStatus::Completed => "COMPLETED",
335            TaskStatus::Failed => "FAILED",
336            TaskStatus::Cancelled => "CANCELLED",
337        };
338
339        self.manager
340            .update_task_status(task_id, status_str, details)
341            .await
342    }
343
344    /// 删除备份记录
345    pub async fn delete_backup_record(&self, backup_id: i64) -> Result<()> {
346        self.manager.delete_backup_record(backup_id).await
347    }
348
349    /// 更新备份文件路径
350    pub async fn update_backup_file_path(&self, backup_id: i64, new_path: String) -> Result<()> {
351        self.manager
352            .update_backup_file_path(backup_id, new_path)
353            .await
354    }
355
356    /// 批量更新备份文件路径(用于存储目录迁移)
357    pub async fn update_all_backup_paths(&self, old_prefix: &str, new_prefix: &str) -> Result<()> {
358        let backups = self.get_all_backups().await?;
359
360        for backup in backups {
361            if backup.file_path.starts_with(old_prefix) {
362                let new_path = backup.file_path.replace(old_prefix, new_prefix);
363                self.update_backup_file_path(backup.id, new_path).await?;
364            }
365        }
366
367        Ok(())
368    }
369}