Stage 8: 完成 SQLite 数据库存储集成

 新功能:
- 实现 DatabaseUserStore 结构体,支持 SQLite 数据库操作
- 创建 UserStore trait 抽象层,支持多种存储后端
- 添加数据库连接和表初始化功能
- 支持通过环境变量配置数据库 URL

🔧 技术改进:
- 使用 SQLx 进行异步数据库操作
- 实现 trait-based 存储架构,便于扩展
- 添加完整的 CRUD 数据库操作
- 支持数据库错误处理和类型转换

🧪 测试验证:
- 所有 API 端点正常工作
- 数据持久化到 SQLite 文件
- 用户创建、查询、登录功能完整
- 重复用户名检查正常工作

📁 文件变更:
- 新增: src/storage/database.rs - SQLite 存储实现
- 新增: src/storage/traits.rs - 存储抽象 trait
- 更新: src/storage/mod.rs - 导出新模块
- 更新: src/routes/mod.rs - 支持 trait 对象
- 更新: src/handlers/user.rs - 使用 trait 抽象
- 更新: src/main.rs - 数据库初始化逻辑
- 更新: Cargo.toml - 添加 SQLx 依赖
- 新增: .env - 数据库配置文件

🎯 学习目标达成:
- 掌握 Rust 数据库集成
- 理解 trait 抽象设计模式
- 学习异步数据库操作
- 实践配置管理
This commit is contained in:
2025-08-04 20:02:20 +08:00
parent 02540f17d3
commit c18f345475
10 changed files with 359 additions and 46 deletions

243
src/storage/database.rs Normal file
View File

@@ -0,0 +1,243 @@
//! SQLite 数据库存储实现
use async_trait::async_trait;
use sqlx::{SqlitePool, Row};
use uuid::Uuid;
use chrono::{DateTime, Utc};
use crate::models::user::User;
use crate::utils::errors::ApiError;
use crate::storage::UserStore;
/// SQLite 用户存储
#[derive(Clone)]
pub struct DatabaseUserStore {
pool: SqlitePool,
}
impl DatabaseUserStore {
/// 创建新的数据库存储实例
pub fn new(pool: SqlitePool) -> Self {
Self { pool }
}
/// 从数据库 URL 创建新的数据库存储实例
pub async fn from_url(database_url: &str) -> Result<Self, ApiError> {
let pool = SqlitePool::connect(database_url)
.await
.map_err(|e| ApiError::InternalError(format!("无法连接到数据库: {}", e)))?;
let store = Self::new(pool);
store.init_tables().await?;
Ok(store)
}
/// 初始化数据库表
pub async fn init_tables(&self) -> Result<(), ApiError> {
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
email TEXT NOT NULL,
password_hash TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
"#,
)
.execute(&self.pool)
.await
.map_err(|e| ApiError::InternalError(format!("数据库初始化错误: {}", e)))?;
Ok(())
}
/// 创建用户
async fn create_user_impl(&self, user: User) -> Result<User, ApiError> {
let result = sqlx::query(
r#"
INSERT INTO users (id, username, email, password_hash, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
"#,
)
.bind(user.id.to_string())
.bind(&user.username)
.bind(&user.email)
.bind(&user.password_hash)
.bind(user.created_at.to_rfc3339())
.bind(user.updated_at.to_rfc3339())
.execute(&self.pool)
.await;
match result {
Ok(_) => Ok(user),
Err(sqlx::Error::Database(db_err)) if db_err.is_unique_violation() => {
Err(ApiError::Conflict("用户名已存在".to_string()))
}
Err(e) => Err(ApiError::InternalError(format!("数据库错误: {}", e))),
}
}
/// 根据 ID 获取用户
async fn get_user_impl(&self, id: &Uuid) -> Result<Option<User>, ApiError> {
let result = sqlx::query(
"SELECT id, username, email, password_hash, created_at, updated_at FROM users WHERE id = ?"
)
.bind(id.to_string())
.fetch_optional(&self.pool)
.await;
match result {
Ok(Some(row)) => {
let user = User {
id: Uuid::parse_str(&row.get::<String, _>("id"))
.map_err(|e| ApiError::InternalError(format!("UUID 解析错误: {}", e)))?,
username: row.get("username"),
email: row.get("email"),
password_hash: row.get("password_hash"),
created_at: DateTime::parse_from_rfc3339(&row.get::<String, _>("created_at"))
.map_err(|e| ApiError::InternalError(format!("时间解析错误: {}", e)))?
.with_timezone(&Utc),
updated_at: DateTime::parse_from_rfc3339(&row.get::<String, _>("updated_at"))
.map_err(|e| ApiError::InternalError(format!("时间解析错误: {}", e)))?
.with_timezone(&Utc),
};
Ok(Some(user))
}
Ok(None) => Ok(None),
Err(e) => Err(ApiError::InternalError(format!("数据库错误: {}", e))),
}
}
/// 根据用户名获取用户
async fn get_user_by_username_impl(&self, username: &str) -> Result<Option<User>, ApiError> {
let result = sqlx::query(
"SELECT id, username, email, password_hash, created_at, updated_at FROM users WHERE username = ?"
)
.bind(username)
.fetch_optional(&self.pool)
.await;
match result {
Ok(Some(row)) => {
let user = User {
id: Uuid::parse_str(&row.get::<String, _>("id"))
.map_err(|e| ApiError::InternalError(format!("UUID 解析错误: {}", e)))?,
username: row.get("username"),
email: row.get("email"),
password_hash: row.get("password_hash"),
created_at: DateTime::parse_from_rfc3339(&row.get::<String, _>("created_at"))
.map_err(|e| ApiError::InternalError(format!("时间解析错误: {}", e)))?
.with_timezone(&Utc),
updated_at: DateTime::parse_from_rfc3339(&row.get::<String, _>("updated_at"))
.map_err(|e| ApiError::InternalError(format!("时间解析错误: {}", e)))?
.with_timezone(&Utc),
};
Ok(Some(user))
}
Ok(None) => Ok(None),
Err(e) => Err(ApiError::InternalError(format!("数据库错误: {}", e))),
}
}
/// 获取所有用户
async fn list_users_impl(&self) -> Result<Vec<User>, ApiError> {
let result = sqlx::query(
"SELECT id, username, email, password_hash, created_at, updated_at FROM users ORDER BY created_at DESC"
)
.fetch_all(&self.pool)
.await;
match result {
Ok(rows) => {
let mut users = Vec::new();
for row in rows {
let user = User {
id: Uuid::parse_str(&row.get::<String, _>("id"))
.map_err(|e| ApiError::InternalError(format!("UUID 解析错误: {}", e)))?,
username: row.get("username"),
email: row.get("email"),
password_hash: row.get("password_hash"),
created_at: DateTime::parse_from_rfc3339(&row.get::<String, _>("created_at"))
.map_err(|e| ApiError::InternalError(format!("时间解析错误: {}", e)))?
.with_timezone(&Utc),
updated_at: DateTime::parse_from_rfc3339(&row.get::<String, _>("updated_at"))
.map_err(|e| ApiError::InternalError(format!("时间解析错误: {}", e)))?
.with_timezone(&Utc),
};
users.push(user);
}
Ok(users)
}
Err(e) => Err(ApiError::InternalError(format!("数据库错误: {}", e))),
}
}
/// 更新用户
async fn update_user_impl(&self, id: &Uuid, updated_user: User) -> Result<Option<User>, ApiError> {
let result = sqlx::query(
r#"
UPDATE users
SET username = ?, email = ?, updated_at = ?
WHERE id = ?
"#,
)
.bind(&updated_user.username)
.bind(&updated_user.email)
.bind(updated_user.updated_at.to_rfc3339())
.bind(id.to_string())
.execute(&self.pool)
.await;
match result {
Ok(query_result) => {
if query_result.rows_affected() > 0 {
Ok(Some(updated_user))
} else {
Ok(None)
}
}
Err(e) => Err(ApiError::InternalError(format!("数据库错误: {}", e))),
}
}
/// 删除用户
async fn delete_user_impl(&self, id: &Uuid) -> Result<bool, ApiError> {
let result = sqlx::query("DELETE FROM users WHERE id = ?")
.bind(id.to_string())
.execute(&self.pool)
.await;
match result {
Ok(query_result) => Ok(query_result.rows_affected() > 0),
Err(e) => Err(ApiError::InternalError(format!("数据库错误: {}", e))),
}
}
}
#[async_trait]
impl UserStore for DatabaseUserStore {
async fn create_user(&self, user: User) -> Result<User, ApiError> {
self.create_user_impl(user).await
}
async fn get_user(&self, id: &Uuid) -> Result<Option<User>, ApiError> {
self.get_user_impl(id).await
}
async fn get_user_by_username(&self, username: &str) -> Result<Option<User>, ApiError> {
self.get_user_by_username_impl(username).await
}
async fn list_users(&self) -> Result<Vec<User>, ApiError> {
self.list_users_impl().await
}
async fn update_user(&self, id: &Uuid, updated_user: User) -> Result<Option<User>, ApiError> {
self.update_user_impl(id, updated_user).await
}
async fn delete_user(&self, id: &Uuid) -> Result<bool, ApiError> {
self.delete_user_impl(id).await
}
}