From 8a7156128ba2383b10f4c727a0cbb8d44f6f7211 Mon Sep 17 00:00:00 2001 From: lychang <632945494@qq.com> Date: Mon, 23 Mar 2026 03:26:06 +0800 Subject: [PATCH] update code level --- src/database.rs | 206 +++++++++++++++++++++++++++++------------------- src/import.rs | 2 +- src/routes.rs | 4 +- 3 files changed, 127 insertions(+), 85 deletions(-) diff --git a/src/database.rs b/src/database.rs index 95215db..3b10fd5 100644 --- a/src/database.rs +++ b/src/database.rs @@ -18,20 +18,20 @@ impl Database { } else { url }; - + // 确保数据目录存在 if let Some(parent) = std::path::Path::new(file_path).parent() { if !parent.exists() { std::fs::create_dir_all(parent)?; } } - + // 如果数据库文件不存在,创建一个空文件 if !std::path::Path::new(file_path).exists() { std::fs::File::create(file_path)?; println!("创建新的数据库文件: {}", file_path); } - + let pool = SqlitePoolOptions::new() .max_connections(10) // 增加连接数 .min_connections(2) // 保持最小连接数 @@ -40,7 +40,7 @@ impl Database { .max_lifetime(std::time::Duration::from_secs(1800)) // 增加最大生命周期 .connect(url) .await?; - + // 在迁移之前设置SQLite性能优化参数 // 这些PRAGMA语句必须在事务外执行 sqlx::query("PRAGMA journal_mode=WAL;").execute(&pool).await?; @@ -50,14 +50,14 @@ impl Database { sqlx::query("PRAGMA mmap_size=268435456;").execute(&pool).await?; sqlx::query("PRAGMA temp_store=MEMORY;").execute(&pool).await?; sqlx::query("PRAGMA foreign_keys=ON;").execute(&pool).await?; - + // 创建表 sqlx::migrate!("./migrations").run(&pool).await?; - + Ok(Database { pool }) } - + /// 带重试机制的数据库操作执行器 async fn execute_with_retry(&self, operation: F, operation_name: &str) -> Result where @@ -65,7 +65,7 @@ impl Database { { const MAX_RETRIES: u32 = 3; const BASE_DELAY_MS: u64 = 100; - + for attempt in 0..=MAX_RETRIES { match operation().await { Ok(result) => return Ok(result), @@ -73,7 +73,7 @@ impl Database { // 检查是否是死锁或超时错误 if self.is_retryable_error(&e) && attempt < MAX_RETRIES { let delay_ms = BASE_DELAY_MS * 2u64.pow(attempt); - eprintln!("⚠️ {} 操作失败 (尝试 {}): {}, 等待 {}ms 后重试", + eprintln!("⚠️ {} 操作失败 (尝试 {}): {}, 等待 {}ms 后重试", operation_name, attempt + 1, e, delay_ms); sleep(Duration::from_millis(delay_ms)).await; } else { @@ -83,7 +83,7 @@ impl Database { } } } - + unreachable!() } @@ -94,7 +94,7 @@ impl Database { // SQLite 死锁错误码 if let Some(code) = db_err.code() { code == "5" || // SQLITE_BUSY - code == "6" || // SQLITE_LOCKED + code == "6" || // SQLITE_LOCKED code == "261" // SQLITE_BUSY_SNAPSHOT } else { false @@ -104,16 +104,14 @@ impl Database { _ => false, } } - - // 相册操作 - pub async fn get_all_albums(&self, page: Option, limit: Option) -> Result, sqlx::Error> { + pub async fn get_albums(&self, page: Option, limit: Option) -> Result, sqlx::Error> { let page = page.unwrap_or(1); let limit = limit.unwrap_or(200); let offset = (page - 1) * limit; - + let albums = sqlx::query( r#" - SELECT + SELECT a.id, a.title, a.document, @@ -128,11 +126,11 @@ impl Database { LIMIT ? OFFSET ? "# ) - .bind(limit as i64) - .bind(offset as i64) - .fetch_all(&self.pool) - .await?; - + .bind(limit as i64) + .bind(offset as i64) + .fetch_all(&self.pool) + .await?; + let mut result = Vec::new(); for row in albums { let id_str = row.get::("id"); @@ -143,7 +141,7 @@ impl Database { continue; // 跳过无效的记录 } }; - + result.push(AlbumInfo { id, title: row.get("title"), @@ -154,16 +152,60 @@ impl Database { created_at: row.get::("created_at"), }); } - + Ok(result) } - + // 相册操作 + pub async fn get_all_albums(&self) -> Result, sqlx::Error> { + + let albums = sqlx::query( + r#" + SELECT + id, + title, + document, + description, + cover_image_id, + 0 as image_count, + created_at + FROM albums + ORDER BY document DESC + "# + ) + .fetch_all(&self.pool) + .await?; + + let mut result = Vec::new(); + for row in albums { + let id_str = row.get::("id"); + let id = match Uuid::parse_str(&id_str) { + Ok(uuid) => uuid, + Err(e) => { + eprintln!("无效的UUID格式: {} - 错误: {}", id_str, e); + continue; // 跳过无效的记录 + } + }; + + result.push(AlbumInfo { + id, + title: row.get("title"), + document: row.get("document"), + description: row.get("description"), + cover_image_id: row.get("cover_image_id"), + image_count: row.get("image_count"), + created_at: row.get::("created_at"), + }); + } + + Ok(result) + } + pub async fn search_albums(&self, query: &str, page: Option, limit: Option) -> Result, sqlx::Error> { let search_pattern = format!("%{}%", query); let page = page.unwrap_or(1); let limit = limit.unwrap_or(200); let offset = (page - 1) * limit; - + let albums = sqlx::query( r#" SELECT @@ -188,7 +230,7 @@ impl Database { .bind(offset as i64) .fetch_all(&self.pool) .await?; - + let mut result = Vec::new(); for row in albums { let id_str = row.get::("id"); @@ -199,7 +241,7 @@ impl Database { continue; // 跳过无效的记录 } }; - + result.push(AlbumInfo { id, title: row.get("title"), @@ -210,10 +252,10 @@ impl Database { created_at: row.get::("created_at"), }); } - + Ok(result) } - + pub async fn get_album_with_images(&self, album_id: Uuid) -> Result, sqlx::Error> { // 获取相册基本信息 let album_row = sqlx::query( @@ -222,7 +264,7 @@ impl Database { .bind(album_id.to_string()) .fetch_optional(&self.pool) .await?; - + if let Some(row) = album_row { let id_str = row.get::("id"); let id = match Uuid::parse_str(&id_str) { @@ -232,7 +274,7 @@ impl Database { return Ok(None); } }; - + let cover_image_id = match row.get::, _>("cover_image_id") { Some(s) => match Uuid::parse_str(&s) { Ok(uuid) => Some(uuid), @@ -243,7 +285,7 @@ impl Database { }, None => None }; - + let album = Album { id, title: row.get("title"), @@ -252,7 +294,7 @@ impl Database { created_at: row.get::("created_at"), updated_at: row.get::("updated_at"), }; - + // 获取相册中的图片 let image_rows = sqlx::query( "SELECT * FROM images WHERE album_id = ?" @@ -260,7 +302,7 @@ impl Database { .bind(album_id.to_string()) .fetch_all(&self.pool) .await?; - + let mut images = Vec::new(); for row in image_rows { let image_id_str = row.get::("id"); @@ -271,7 +313,7 @@ impl Database { continue; } }; - + let album_id_str = row.get::("album_id"); let album_id = match Uuid::parse_str(&album_id_str) { Ok(uuid) => uuid, @@ -280,7 +322,7 @@ impl Database { continue; } }; - + images.push(Image { id: image_id, album_id, @@ -295,9 +337,9 @@ impl Database { created_at: row.get::("created_at"), }); } - + let image_count = images.len() as i64; - + Ok(Some(AlbumWithImages { album, images, @@ -307,11 +349,11 @@ impl Database { Ok(None) } } - + pub async fn create_album(&self, album: CreateAlbum) -> Result { let id = Uuid::new_v4(); let now = chrono::Utc::now().to_rfc3339(); - + sqlx::query( "INSERT INTO albums (id, title, document, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)" ) @@ -323,15 +365,15 @@ impl Database { .bind(&now) .execute(&self.pool) .await?; - + Ok(id) } - + // 图片操作 pub async fn add_image(&self, image: CreateImage) -> Result { let id = Uuid::new_v4(); let now = chrono::Utc::now().to_rfc3339(); - + sqlx::query( "INSERT INTO images (id, album_id, filename, original_url, title, description, file_size, width, height, mime_type, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ) @@ -348,7 +390,7 @@ impl Database { .bind(&now) .execute(&self.pool) .await?; - + // 如果这是相册的第一张图片,设置为封面 let image_count = sqlx::query_scalar::<_, i64>( "SELECT COUNT(*) FROM images WHERE album_id = ?" @@ -356,7 +398,7 @@ impl Database { .bind(image.album_id.to_string()) .fetch_one(&self.pool) .await?; - + if image_count == 1 { sqlx::query( "UPDATE albums SET cover_image_id = ? WHERE id = ?" @@ -366,7 +408,7 @@ impl Database { .execute(&self.pool) .await?; } - + Ok(id) } @@ -378,17 +420,17 @@ impl Database { // 由于生命周期问题,简化实现,不使用重试机制 let now = chrono::Utc::now().to_rfc3339(); let mut ids = Vec::new(); - + // 保存第一个相册ID用于封面设置 let first_album_id = images.first().map(|img| img.album_id); - + // 使用事务确保批量插入的原子性 let mut tx = self.pool.begin().await?; - + for image in &images { let id = Uuid::new_v4(); ids.push(id); - + sqlx::query( "INSERT INTO images (id, album_id, filename, original_url, title, description, file_size, width, height, mime_type, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ) @@ -406,10 +448,10 @@ impl Database { .execute(&mut *tx) .await?; } - + // 提交事务 tx.commit().await?; - + // 批量设置封面图片(只对每个相册的第一批图片设置) if let Some(album_id) = first_album_id { // 检查相册是否已有图片 @@ -419,7 +461,7 @@ impl Database { .bind(album_id.to_string()) .fetch_one(&self.pool) .await?; - + // 如果这是相册的第一批图片,设置第一张为封面 if existing_count == images.len() as i64 { if let Some(first_id) = ids.first() { @@ -433,7 +475,7 @@ impl Database { } } } - + Ok(ids) } @@ -445,33 +487,33 @@ impl Database { .bind(album_id.to_string()) .fetch_one(&self.pool) .await?; - + Ok(count) } /// 删除相册及其关联的图片 pub async fn delete_album(&self, album_id: Uuid) -> Result<(), sqlx::Error> { let mut tx = self.pool.begin().await?; - + // 删除相册关联的图片 sqlx::query("DELETE FROM images WHERE album_id = ?") .bind(album_id.to_string()) .execute(&mut *tx) .await?; - + // 删除相册 sqlx::query("DELETE FROM albums WHERE id = ?") .bind(album_id.to_string()) .execute(&mut *tx) .await?; - + tx.commit().await?; Ok(()) } pub async fn update_album_cover(&self, album_id: Uuid, cover_image_id: Option) -> Result<(), sqlx::Error> { let cover_id_str = cover_image_id.map(|id| id.to_string()); - + sqlx::query( "UPDATE albums SET cover_image_id = ?, updated_at = datetime('now') WHERE id = ?" ) @@ -479,7 +521,7 @@ impl Database { .bind(album_id.to_string()) .execute(&self.pool) .await?; - + Ok(()) } @@ -490,7 +532,7 @@ impl Database { .bind(image_id.to_string()) .fetch_optional(&self.pool) .await?; - + if let Some(row) = row { let image_id_str = row.get::("id"); let image_id = match Uuid::parse_str(&image_id_str) { @@ -500,7 +542,7 @@ impl Database { return Ok(None); } }; - + let album_id_str = row.get::("album_id"); let album_id = match Uuid::parse_str(&album_id_str) { Ok(uuid) => uuid, @@ -509,7 +551,7 @@ impl Database { return Ok(None); } }; - + Ok(Some(Image { id: image_id, album_id, @@ -527,9 +569,9 @@ impl Database { Ok(None) } } - + // 收藏功能数据库操作 - + // 获取用户的收藏列表 pub async fn get_user_favorites(&self, user_id: &str) -> Result, sqlx::Error> { let rows = sqlx::query( @@ -562,7 +604,7 @@ impl Database { .bind(user_id) .fetch_all(&self.pool) .await?; - + let mut favorites = Vec::new(); for row in rows { let favorite_id_str = row.get::("favorite_id"); @@ -573,7 +615,7 @@ impl Database { continue; } }; - + let image_id_str = row.get::("image_id"); let image_id = match Uuid::parse_str(&image_id_str) { Ok(uuid) => uuid, @@ -582,7 +624,7 @@ impl Database { continue; } }; - + let album_id_str = row.get::("album_id"); let album_id = match Uuid::parse_str(&album_id_str) { Ok(uuid) => uuid, @@ -591,7 +633,7 @@ impl Database { continue; } }; - + let favorite = Favorite { id: favorite_id, user_id: row.get("user_id"), @@ -599,7 +641,7 @@ impl Database { album_id, created_at: row.get::("favorite_created_at"), }; - + let image = Image { id: image_id, album_id, @@ -613,17 +655,17 @@ impl Database { mime_type: row.get("mime_type"), created_at: row.get::("image_created_at"), }; - + favorites.push(FavoriteWithImage { favorite, image, album_title: row.get("album_title"), }); } - + Ok(favorites) } - + // 检查图片是否被用户收藏 pub async fn get_favorite_status(&self, user_id: &str, image_id: Uuid) -> Result { let row = sqlx::query( @@ -633,7 +675,7 @@ impl Database { .bind(image_id.to_string()) .fetch_optional(&self.pool) .await?; - + if let Some(row) = row { let favorite_id_str = row.get::("id"); let favorite_id = match Uuid::parse_str(&favorite_id_str) { @@ -643,7 +685,7 @@ impl Database { None } }; - + Ok(FavoriteStatus { is_favorited: true, favorite_id, @@ -655,12 +697,12 @@ impl Database { }) } } - + // 添加收藏 pub async fn add_favorite(&self, user_id: &str, favorite_data: CreateFavorite) -> Result { let favorite_id = Uuid::new_v4(); let now = chrono::Utc::now().to_rfc3339(); - + sqlx::query( "INSERT INTO favorites (id, user_id, image_id, album_id, created_at) VALUES (?, ?, ?, ?, ?)" ) @@ -671,10 +713,10 @@ impl Database { .bind(&now) .execute(&self.pool) .await?; - + Ok(favorite_id) } - + // 移除收藏(通过收藏ID) pub async fn remove_favorite(&self, favorite_id: Uuid) -> Result<(), sqlx::Error> { sqlx::query( @@ -683,10 +725,10 @@ impl Database { .bind(favorite_id.to_string()) .execute(&self.pool) .await?; - + Ok(()) } - + // 移除收藏(通过用户ID和图片ID) pub async fn remove_favorite_by_image(&self, user_id: &str, image_id: Uuid) -> Result<(), sqlx::Error> { sqlx::query( @@ -696,7 +738,7 @@ impl Database { .bind(image_id.to_string()) .execute(&self.pool) .await?; - + Ok(()) } } \ No newline at end of file diff --git a/src/import.rs b/src/import.rs index 1aab9cc..29107c5 100644 --- a/src/import.rs +++ b/src/import.rs @@ -26,7 +26,7 @@ pub async fn import_existing_data(config: &Config, db: Arc) -> Result< } // 获取数据库中现有的相册列表 - let existing_albums = db.get_all_albums(None, None).await?; + let existing_albums = db.get_all_albums().await?; let existing_titles: Vec = existing_albums.iter() .map(|album| album.document.to_lowercase()) .collect(); diff --git a/src/routes.rs b/src/routes.rs index 5a2bfe8..e47deee 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -13,7 +13,7 @@ pub async fn get_albums( let page = query.get("page").and_then(|s| s.parse::().ok()); let limit = query.get("limit").and_then(|s| s.parse::().ok()); - match db.get_all_albums(page, limit).await { + match db.get_albums(page, limit).await { Ok(albums) => HttpResponse::Ok().json(albums), Err(e) => { eprintln!("获取相册列表失败: {}", e); @@ -206,7 +206,7 @@ pub async fn search_albums( let limit = query.get("limit").and_then(|s| s.parse::().ok()); if q.is_empty() { - return match db.get_all_albums(page, limit).await { + return match db.get_albums(page, limit).await { Ok(albums) => HttpResponse::Ok().json(albums), Err(e) => { eprintln!("获取相册列表失败: {}", e);