Skip to content

Commit 2529cdf

Browse files
committed
feat(database): 引入数据库连接错误类型并优化错误处理
- 新增 ConnectionError 类型用于封装数据库连接错误 - 修改 establish_connection 函数返回 Result 类型而非直接 panic - 为 SQLite、MySQL 和 PostgreSQL 连接添加详细的错误信息 - 在应用启动时处理数据库连接和迁移失败的情况 - CLI 和 WebAPI 模块中实现更优雅的错误提示和返回机制 - 添加日志记录以便更好地追踪数据库相关错误
1 parent 8998fca commit 2529cdf

File tree

4 files changed

+108
-31
lines changed

4 files changed

+108
-31
lines changed

file_classification_cli/src/main.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ fn main() -> Result<(), Box<dyn Error>> {
3434
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
3535
let database_type = std::env::var("DATABASE_TYPE").expect("DATABASE_TYPE must be set");
3636

37-
let mut conn = database::establish_connection(&database_url, &database_type);
37+
let mut conn = match database::establish_connection(&database_url, &database_type) {
38+
Ok(conn) => conn,
39+
Err(e) => {
40+
eprintln!("数据库连接失败: {}", e);
41+
return Err(Box::new(e));
42+
}
43+
};
3844

3945
// 运行待处理的数据库迁移
4046
if let Err(e) = database::run_pending_migrations(&mut conn, &database_type) {

file_classification_core/src/utils/database.rs

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,42 @@
66
77
pub use diesel::{Connection, QueryResult};
88
use diesel::RunQueryDsl;
9+
use std::fmt;
10+
11+
/// 数据库连接错误类型
12+
#[derive(Debug)]
13+
pub struct ConnectionError {
14+
pub message: String,
15+
pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
16+
}
17+
18+
impl fmt::Display for ConnectionError {
19+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20+
write!(f, "{}", self.message)
21+
}
22+
}
23+
24+
impl std::error::Error for ConnectionError {
25+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
26+
self.source.as_ref().map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
27+
}
28+
}
29+
30+
impl ConnectionError {
31+
pub fn new(message: &str) -> Self {
32+
ConnectionError {
33+
message: message.to_string(),
34+
source: None,
35+
}
36+
}
37+
38+
pub fn with_source(message: &str, source: Box<dyn std::error::Error + Send + Sync>) -> Self {
39+
ConnectionError {
40+
message: message.to_string(),
41+
source: Some(source),
42+
}
43+
}
44+
}
945

1046
/// 通用数据库连接枚举
1147
///
@@ -34,57 +70,75 @@ pub enum AnyConnection {
3470
/// - `database_type`: 数据库类型
3571
///
3672
/// 返回值:
37-
/// 成功时返回封装好的数据库连接对象,失败时会 panic 并输出错误信息
38-
pub fn establish_connection(database_url: &str, database_type: &str) -> AnyConnection {
73+
/// 成功时返回封装好的数据库连接对象,失败时返回 ConnectionError 错误
74+
pub fn establish_connection(database_url: &str, database_type: &str) -> Result<AnyConnection, ConnectionError> {
3975
// 根据数据库类型建立相应的连接
4076
match database_type {
4177
#[cfg(feature = "sqlite")]
4278
"sqlite" => {
43-
let mut conn = AnyConnection::Sqlite(
44-
diesel::SqliteConnection::establish(database_url)
45-
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)),
46-
);
79+
let conn = diesel::SqliteConnection::establish(database_url)
80+
.map_err(|e| ConnectionError::with_source(
81+
&format!("Error connecting to SQLite database at {}", database_url),
82+
Box::new(e)
83+
))?;
84+
85+
let mut any_conn = AnyConnection::Sqlite(conn);
4786

4887
// 关闭外键约束检查
4988
diesel::sql_query("PRAGMA foreign_keys = OFF")
50-
.execute(&mut conn)
51-
.expect("Error executing PRAGMA foreign_keys = OFF");
89+
.execute(&mut any_conn)
90+
.map_err(|e| ConnectionError::with_source(
91+
"Error executing PRAGMA foreign_keys = OFF",
92+
Box::new(e)
93+
))?;
5294

53-
conn
95+
Ok(any_conn)
5496
},
5597

5698
#[cfg(feature = "mysql")]
5799
"mysql" => {
58-
let mut conn = AnyConnection::Mysql(
59-
diesel::MysqlConnection::establish(database_url)
60-
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)),
61-
);
100+
let conn = diesel::MysqlConnection::establish(database_url)
101+
.map_err(|e| ConnectionError::with_source(
102+
&format!("Error connecting to MySQL database at {}", database_url),
103+
Box::new(e)
104+
))?;
105+
106+
let mut any_conn = AnyConnection::Mysql(conn);
62107

63108
// 关闭外键约束检查
64109
diesel::sql_query("SET FOREIGN_KEY_CHECKS = 0")
65-
.execute(&mut conn)
66-
.expect("Error executing SET FOREIGN_KEY_CHECKS = 0");
110+
.execute(&mut any_conn)
111+
.map_err(|e| ConnectionError::with_source(
112+
"Error executing SET FOREIGN_KEY_CHECKS = 0",
113+
Box::new(e)
114+
))?;
67115

68-
conn
116+
Ok(any_conn)
69117
},
70118

71119
#[cfg(feature = "postgres")]
72120
"postgres" => {
73-
let mut conn = AnyConnection::Postgresql(
74-
diesel::PgConnection::establish(database_url)
75-
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)),
76-
);
121+
let conn = diesel::PgConnection::establish(database_url)
122+
.map_err(|e| ConnectionError::with_source(
123+
&format!("Error connecting to PostgreSQL database at {}", database_url),
124+
Box::new(e)
125+
))?;
126+
127+
let mut any_conn = AnyConnection::Postgresql(conn);
77128

78129
// 关闭外键约束检查
79130
diesel::sql_query("SET session_replication_role = 'replica'")
80-
.execute(&mut conn)
81-
.expect("Error executing SET session_replication_role = 'replica'");
131+
.execute(&mut any_conn)
132+
.map_err(|e| ConnectionError::with_source(
133+
"Error executing SET session_replication_role = 'replica'",
134+
Box::new(e)
135+
))?;
82136

83-
conn
137+
Ok(any_conn)
84138
},
85139

86-
// 不支持的数据库类型直接 panic
87-
_ => panic!("Unsupported database type: {} or feature not enabled", database_type),
140+
// 不支持的数据库类型
141+
_ => Err(ConnectionError::new(&format!("Unsupported database type: {} or feature not enabled", database_type))),
88142
}
89143
}
90144

file_classification_webapi/src/bin/file_classification_webapi.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,30 @@ mod utils;
44
use crate::utils::logger::setup_logger;
55
use crate::utils::server;
66
use crate::utils::app_config::AppConfig;
7+
use log::error;
78

89
#[actix_web::main]
910
async fn main() -> std::io::Result<()> {
1011
// 初始化日志记录器
11-
setup_logger().expect("日志系统初始化失败");
12+
if let Err(e) = setup_logger() {
13+
eprintln!("日志系统初始化失败: {}", e);
14+
return Err(std::io::Error::new(std::io::ErrorKind::Other, "日志系统初始化失败"));
15+
}
1216

1317
// 加载应用配置
14-
let config = AppConfig::load().expect("应用配置加载失败");
18+
let config = match AppConfig::load() {
19+
Ok(config) => config,
20+
Err(e) => {
21+
error!("应用配置加载失败: {}", e);
22+
return Err(std::io::Error::new(std::io::ErrorKind::Other, "应用配置加载失败"));
23+
}
24+
};
1525

1626
// 运行数据库迁移
17-
config.run_migrations().expect("数据库迁移失败");
27+
if let Err(e) = config.run_migrations() {
28+
error!("数据库迁移失败: {}", e);
29+
return Err(std::io::Error::new(std::io::ErrorKind::Other, "数据库迁移失败"));
30+
}
1831

1932
// 启动服务器
2033
server::start_server(config).await

file_classification_webapi/src/bin/utils/app_config.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::env;
22
use log;
33
use file_classification_common::env_loader::load_env_file;
4-
use file_classification_core::utils::database::{establish_connection, run_pending_migrations};
4+
use file_classification_core::utils::database::{establish_connection, run_pending_migrations, ConnectionError};
55
use std::error::Error;
66

77
#[derive(Clone)]
@@ -44,7 +44,11 @@ impl AppConfig {
4444
}
4545

4646
pub fn run_migrations(&self) -> Result<(), Box<dyn Error>> {
47-
let mut conn = establish_connection(&self.database_url, &self.database_type);
47+
let mut conn = establish_connection(&self.database_url, &self.database_type)
48+
.map_err(|e| {
49+
log::error!("数据库连接失败: {}", e);
50+
e
51+
})?;
4852
if let Err(e) = run_pending_migrations(&mut conn, &self.database_type) {
4953
log::error!("数据库迁移失败: {}", e);
5054
return Err(e);

0 commit comments

Comments
 (0)