update code level

This commit is contained in:
lychang
2026-03-25 14:50:50 +08:00
parent 6b2d44ca78
commit 4ff83492f1
3 changed files with 180 additions and 235 deletions

View File

@@ -1,9 +1,7 @@
use sqlx::{SqlitePool, sqlite::SqlitePoolOptions, Row, Error as SqlxError};
use sqlx::{SqlitePool, sqlite::SqlitePoolOptions, Row};
use crate::config::Config;
use crate::models::*;
use uuid::Uuid;
use std::time::Duration;
use tokio::time::sleep;
pub struct Database {
pub pool: SqlitePool,
@@ -61,102 +59,55 @@ impl Database {
Ok(db)
}
/// 带重试机制的数据库操作执行器
async fn execute_with_retry<F, T>(&self, operation: F, operation_name: &str) -> Result<T, sqlx::Error>
where
F: Fn() -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, sqlx::Error>> + Send>>,
{
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),
Err(e) => {
// 检查是否是死锁或超时错误
if self.is_retryable_error(&e) && attempt < MAX_RETRIES {
let delay_ms = BASE_DELAY_MS * 2u64.pow(attempt);
eprintln!("⚠️ {} 操作失败 (尝试 {}): {}, 等待 {}ms 后重试",
operation_name, attempt + 1, e, delay_ms);
sleep(Duration::from_millis(delay_ms)).await;
} else {
eprintln!("❌ {} 操作失败 (尝试 {}): {}", operation_name, attempt + 1, e);
return Err(e);
}
}
}
}
unreachable!()
}
/// 判断错误是否可重试
fn is_retryable_error(&self, error: &SqlxError) -> bool {
match error {
SqlxError::Database(db_err) => {
// SQLite 死锁错误码
if let Some(code) = db_err.code() {
code == "5" || // SQLITE_BUSY
code == "6" || // SQLITE_LOCKED
code == "261" // SQLITE_BUSY_SNAPSHOT
} else {
false
}
}
SqlxError::PoolTimedOut => true,
_ => false,
}
}
pub async fn get_albums(&self, page: Option<usize>, limit: Option<usize>) -> Result<Vec<AlbumInfo>, sqlx::Error> {
let page = page.unwrap_or(1);
let limit = limit.unwrap_or(50); // 减少默认加载数量,提高响应速度
let offset = (page - 1) * limit;
let albums = sqlx::query(
r#"
SELECT
id,
title,
document,
description,
cover_image_id,
image_count,
created_at
FROM albums
ORDER BY created_at DESC
LIMIT ? OFFSET ?
"#
)
.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::<String, _>("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::<String, _>("created_at"),
});
}
Ok(result)
}
// pub async fn get_albums(&self, page: Option<usize>, limit: Option<usize>) -> Result<Vec<AlbumInfo>, sqlx::Error> {
// let page = page.unwrap_or(1);
// let limit = limit.unwrap_or(50); // 减少默认加载数量,提高响应速度
// let offset = (page - 1) * limit;
//
// let albums = sqlx::query(
// r#"
// SELECT
// id,
// title,
// document,
// description,
// cover_image_id,
// image_count,
// created_at
// FROM albums
// ORDER BY created_at DESC
// LIMIT ? OFFSET ?
// "#
// )
// .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::<String, _>("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::<String, _>("created_at"),
// });
// }
//
// Ok(result)
// }
// 相册操作
pub async fn get_all_albums(&self) -> Result<Vec<AlbumInfo>, sqlx::Error> {
@@ -202,58 +153,58 @@ impl Database {
Ok(result)
}
pub async fn search_albums(&self, query: &str, page: Option<usize>, limit: Option<usize>) -> Result<Vec<AlbumInfo>, sqlx::Error> {
let search_pattern = format!("%{}%", query);
let page = page.unwrap_or(1);
let limit = limit.unwrap_or(50); // 减少默认加载数量,提高响应速度
let offset = (page - 1) * limit;
let albums = sqlx::query(
r#"
SELECT
id,
title,
document,
description,
cover_image_id,
image_count,
created_at
FROM albums
WHERE title LIKE ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
"#
)
.bind(&search_pattern)
.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::<String, _>("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::<String, _>("created_at"),
});
}
Ok(result)
}
// pub async fn search_albums(&self, query: &str, page: Option<usize>, limit: Option<usize>) -> Result<Vec<AlbumInfo>, sqlx::Error> {
// let search_pattern = format!("%{}%", query);
// let page = page.unwrap_or(1);
// let limit = limit.unwrap_or(50); // 减少默认加载数量,提高响应速度
// let offset = (page - 1) * limit;
//
// let albums = sqlx::query(
// r#"
// SELECT
// id,
// title,
// document,
// description,
// cover_image_id,
// image_count,
// created_at
// FROM albums
// WHERE title LIKE ?
// ORDER BY created_at DESC
// LIMIT ? OFFSET ?
// "#
// )
// .bind(&search_pattern)
// .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::<String, _>("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::<String, _>("created_at"),
// });
// }
//
// Ok(result)
// }
pub async fn get_album_with_images(&self, album_id: Uuid) -> Result<Option<AlbumWithImages>, sqlx::Error> {
// 获取相册基本信息
@@ -370,50 +321,50 @@ impl Database {
}
// 图片操作
pub async fn add_image(&self, image: CreateImage) -> Result<Uuid, sqlx::Error> {
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
)
.bind(id.to_string())
.bind(image.album_id.to_string())
.bind(image.filename)
.bind(image.original_url)
.bind(image.title)
.bind(image.description)
.bind(image.file_size)
.bind(image.width)
.bind(image.height)
.bind(image.mime_type)
.bind(&now)
.execute(&self.pool)
.await?;
// 更新相册的图片数量
self.update_album_image_count(image.album_id).await?;
// 如果这是相册的第一张图片,设置为封面
let image_count = sqlx::query_scalar::<_, i64>(
"SELECT COUNT(*) FROM images WHERE album_id = ?"
)
.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 = ?"
)
.bind(id.to_string())
.bind(image.album_id.to_string())
.execute(&self.pool)
.await?;
}
Ok(id)
}
// pub async fn add_image(&self, image: CreateImage) -> Result<Uuid, sqlx::Error> {
// 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
// )
// .bind(id.to_string())
// .bind(image.album_id.to_string())
// .bind(image.filename)
// .bind(image.original_url)
// .bind(image.title)
// .bind(image.description)
// .bind(image.file_size)
// .bind(image.width)
// .bind(image.height)
// .bind(image.mime_type)
// .bind(&now)
// .execute(&self.pool)
// .await?;
//
// // 更新相册的图片数量
// self.update_album_image_count(image.album_id).await?;
//
// // 如果这是相册的第一张图片,设置为封面
// let image_count = sqlx::query_scalar::<_, i64>(
// "SELECT COUNT(*) FROM images WHERE album_id = ?"
// )
// .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 = ?"
// )
// .bind(id.to_string())
// .bind(image.album_id.to_string())
// .execute(&self.pool)
// .await?;
// }
//
// Ok(id)
// }
/// 更新相册的图片数量
pub async fn update_album_image_count(&self, album_id: Uuid) -> Result<(), sqlx::Error> {
@@ -569,38 +520,38 @@ impl Database {
Ok(())
}
pub async fn delete_image(&self, image_id: Uuid) -> Result<bool, sqlx::Error> {
// 先获取图片所属的相册ID
let album_id_result = sqlx::query_scalar::<_, String>(
r#"
SELECT album_id FROM images WHERE id = ?
"#
)
.bind(image_id.to_string())
.fetch_optional(&self.pool)
.await?;
let result = sqlx::query(
r#"
DELETE FROM images
WHERE id = ?
"#
)
.bind(image_id.to_string())
.execute(&self.pool)
.await?;
// 如果删除成功且找到了相册ID,更新相册图片数量
if result.rows_affected() > 0 {
if let Some(album_id_str) = album_id_result {
if let Ok(album_id) = Uuid::parse_str(&album_id_str) {
self.update_album_image_count(album_id).await?;
}
}
}
Ok(result.rows_affected() > 0)
}
// pub async fn delete_image(&self, image_id: Uuid) -> Result<bool, sqlx::Error> {
// // 先获取图片所属的相册ID
// let album_id_result = sqlx::query_scalar::<_, String>(
// r#"
// SELECT album_id FROM images WHERE id = ?
// "#
// )
// .bind(image_id.to_string())
// .fetch_optional(&self.pool)
// .await?;
//
// let result = sqlx::query(
// r#"
// DELETE FROM images
// WHERE id = ?
// "#
// )
// .bind(image_id.to_string())
// .execute(&self.pool)
// .await?;
//
// // 如果删除成功且找到了相册ID,更新相册图片数量
// if result.rows_affected() > 0 {
// if let Some(album_id_str) = album_id_result {
// if let Ok(album_id) = Uuid::parse_str(&album_id_str) {
// self.update_album_image_count(album_id).await?;
// }
// }
// }
//
// Ok(result.rows_affected() > 0)
// }
pub async fn update_album_cover(&self, album_id: Uuid, cover_image_id: Option<Uuid>) -> Result<(), sqlx::Error> {
let cover_id_str = cover_image_id.map(|id| id.to_string());

View File

@@ -99,7 +99,7 @@ pub struct FavoriteStatus {
#[derive(Debug, Clone)]
pub struct AlbumsCache {
pub albums: Vec<AlbumInfo>,
pub search_index: std::collections::HashMap<String, Vec<AlbumInfo>>,
pub search_index: HashMap<String, Vec<AlbumInfo>>,
pub last_updated: chrono::DateTime<chrono::Utc>,
}
@@ -107,7 +107,7 @@ impl AlbumsCache {
pub fn new() -> Self {
Self {
albums: Vec::new(),
search_index: std::collections::HashMap::new(),
search_index: HashMap::new(),
last_updated: chrono::Utc::now(),
}
}