update code level
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1231,6 +1231,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"image",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -15,3 +15,4 @@ uuid = { version = "1.0", features = ["v4", "serde"] }
|
||||
image = { version = "0.24", default-features = false, features = ["jpeg", "png", "gif", "webp"] }
|
||||
reqwest = { version = "0.11", default-features = false, features = ["blocking","rustls-tls"] }
|
||||
config = "0.13"
|
||||
rand = "0.8"
|
||||
@@ -1228,6 +1228,51 @@ body {
|
||||
color: var(--medium-gray);
|
||||
}
|
||||
|
||||
/* 搜索提示样式 */
|
||||
.search-hint {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: var(--text-light);
|
||||
font-size: 1.2rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.search-hint .fa-search {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--medium-gray);
|
||||
}
|
||||
|
||||
.search-hint h3 {
|
||||
color: var(--text-dark);
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.search-hint p {
|
||||
margin-bottom: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.search-hint ul {
|
||||
list-style: none;
|
||||
text-align: left;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.search-hint li {
|
||||
margin-bottom: 10px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.search-hint .btn-primary {
|
||||
margin-top: 10px;
|
||||
padding: 8px 16px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 响应式媒体查询 - 移动端优化 */
|
||||
@media (max-width: 1024px) {
|
||||
.container {
|
||||
|
||||
142
assets/js/app.js
142
assets/js/app.js
@@ -263,52 +263,89 @@ async function loadAlbums(reset = false) {
|
||||
}
|
||||
|
||||
// 搜索相册
|
||||
async function searchAlbums() {
|
||||
// 防抖搜索函数
|
||||
function debouncedSearch() {
|
||||
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
|
||||
|
||||
|
||||
if (searchTerm === '') {
|
||||
// 显示所有相册,重置分页
|
||||
loadAlbums(true);
|
||||
// 显示所有已加载的相册
|
||||
displayAlbums(allAlbums);
|
||||
updateStats();
|
||||
return;
|
||||
}
|
||||
|
||||
// 在当前已加载的数据集中搜索
|
||||
const filteredAlbums = searchInLoadedAlbums(searchTerm);
|
||||
|
||||
if (filteredAlbums.length > 0) {
|
||||
// 如果当前已加载的数据中有匹配项,直接显示
|
||||
displayAlbums(filteredAlbums);
|
||||
document.getElementById('stats').innerHTML = `在当前页面中找到 ${filteredAlbums.length} 个包含 "${searchTerm}" 的相册`;
|
||||
} else {
|
||||
// 如果没有找到匹配项,显示提示信息
|
||||
showSearchHint(searchTerm);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// 重置搜索状态
|
||||
currentPage = 1;
|
||||
allAlbums = [];
|
||||
hasMoreAlbums = true;
|
||||
// 在当前已加载的数据集中搜索
|
||||
function searchInLoadedAlbums(searchTerm) {
|
||||
if (!allAlbums.length) return [];
|
||||
|
||||
return allAlbums.filter(album => {
|
||||
// 搜索标题和描述
|
||||
const titleMatch = album.title.toLowerCase().includes(searchTerm);
|
||||
const descriptionMatch = album.description && album.description.toLowerCase().includes(searchTerm);
|
||||
|
||||
return titleMatch || descriptionMatch;
|
||||
});
|
||||
}
|
||||
|
||||
// 显示搜索提示
|
||||
function showSearchHint(searchTerm) {
|
||||
const container = document.getElementById('albums-container');
|
||||
container.innerHTML = `
|
||||
<div class="search-hint">
|
||||
<i class="fas fa-search"></i>
|
||||
<h3>未在当前页面找到 "${searchTerm}"</h3>
|
||||
<p>尝试:</p>
|
||||
<ul>
|
||||
<li>继续滚动加载更多相册</li>
|
||||
<li>使用更精确的搜索词</li>
|
||||
<li><button class="btn-primary" onclick="performServerSearch('${searchTerm}')">在全站搜索 "${searchTerm}"</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('stats').innerHTML = `在当前页面中未找到包含 "${searchTerm}" 的相册`;
|
||||
}
|
||||
|
||||
// 执行服务器端搜索(全站搜索)
|
||||
async function performServerSearch(searchTerm) {
|
||||
try {
|
||||
// 显示搜索中的骨架屏
|
||||
showSkeleton(4);
|
||||
|
||||
const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(searchTerm)}&page=${currentPage}&limit=${albumsPerPage}`);
|
||||
const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(searchTerm)}&page=1&limit=${albumsPerPage}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const filteredAlbums = await response.json();
|
||||
allAlbums = filteredAlbums;
|
||||
const serverResults = await response.json();
|
||||
|
||||
// 检查是否还有更多数据
|
||||
if (filteredAlbums.length < albumsPerPage) {
|
||||
hasMoreAlbums = false;
|
||||
if (serverResults.length === 0) {
|
||||
showError(`没有找到包含 "${searchTerm}" 的相册`);
|
||||
return;
|
||||
}
|
||||
|
||||
displayAlbums(filteredAlbums);
|
||||
|
||||
if (filteredAlbums.length === 0) {
|
||||
document.getElementById('stats').innerHTML = `没有找到包含 "${searchTerm}" 的相册`;
|
||||
} else {
|
||||
document.getElementById('stats').innerHTML = `找到 ${filteredAlbums.length} 个包含 "${searchTerm}" 的相册`;
|
||||
}
|
||||
|
||||
// 显示服务器搜索结果
|
||||
displayAlbums(serverResults);
|
||||
document.getElementById('stats').innerHTML = `在全站找到 ${serverResults.length} 个包含 "${searchTerm}" 的相册`;
|
||||
|
||||
// 初始化图片懒加载
|
||||
initLazyLoading();
|
||||
|
||||
currentPage++;
|
||||
} catch (error) {
|
||||
console.error('搜索失败:', error);
|
||||
showError('搜索失败,请重试');
|
||||
console.error('全站搜索失败:', error);
|
||||
showError('全站搜索失败,请重试');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,6 +447,9 @@ function preloadNextBatchImages() {
|
||||
|
||||
// 更新统计信息
|
||||
function updateStats(albums) {
|
||||
if (!albums) {
|
||||
albums = allAlbums;
|
||||
}
|
||||
const totalImages = albums.reduce((sum, album) => sum + album.image_count, 0);
|
||||
document.getElementById('stats').innerHTML = `共发现 ${albums.length} 个相册,包含 ${totalImages} 张图片`;
|
||||
}
|
||||
@@ -545,23 +585,47 @@ optimizeScrollPerformance();
|
||||
|
||||
// 随机打开相册
|
||||
function openRandomAlbum() {
|
||||
if (allAlbums.length === 0) {
|
||||
alert('暂无相册数据,请稍后再试');
|
||||
return;
|
||||
}
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * allAlbums.length);
|
||||
const randomAlbum = allAlbums[randomIndex];
|
||||
|
||||
// 添加随机动画效果
|
||||
const randomButton = document.querySelector('.random-button');
|
||||
randomButton.style.transform = 'scale(0.95)';
|
||||
setTimeout(() => {
|
||||
randomButton.style.transform = '';
|
||||
}, 200);
|
||||
randomButton.disabled = true;
|
||||
|
||||
// 显示加载状态
|
||||
const originalText = randomButton.textContent;
|
||||
randomButton.textContent = '随机加载中...';
|
||||
|
||||
// 跳转到随机相册
|
||||
window.location.href = `/album?id=${randomAlbum.id}`;
|
||||
// 调用新的随机相册API
|
||||
fetch(`${API_BASE}/album/random`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
throw new Error('暂无相册数据,请稍后再试');
|
||||
}
|
||||
throw new Error('获取随机相册失败');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(randomAlbum => {
|
||||
// 恢复按钮状态
|
||||
setTimeout(() => {
|
||||
randomButton.style.transform = '';
|
||||
randomButton.disabled = false;
|
||||
randomButton.textContent = originalText;
|
||||
}, 200);
|
||||
|
||||
// 跳转到随机相册
|
||||
window.location.href = `/album?id=${randomAlbum.id}`;
|
||||
})
|
||||
.catch(error => {
|
||||
// 恢复按钮状态
|
||||
setTimeout(() => {
|
||||
randomButton.style.transform = '';
|
||||
randomButton.disabled = false;
|
||||
randomButton.textContent = originalText;
|
||||
}, 200);
|
||||
|
||||
alert(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
// 显示收藏夹
|
||||
|
||||
45
src/main.rs
45
src/main.rs
@@ -9,6 +9,8 @@ use actix_files as afs;
|
||||
use actix_web::{App, HttpResponse, HttpServer, web, middleware};
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
use tokio::time::{interval, Duration};
|
||||
use crate::models::AlbumsCache;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
@@ -50,6 +52,47 @@ async fn main() -> std::io::Result<()> {
|
||||
// 初始化图片处理器
|
||||
let image_processor = Arc::new(image_processor::ImageProcessor::new(config.storage.clone()));
|
||||
|
||||
// 初始化相册内存缓存
|
||||
let albums_cache = Arc::new(tokio::sync::RwLock::new(AlbumsCache::new()));
|
||||
|
||||
// 初始加载相册数据到缓存
|
||||
let initial_cache = Arc::clone(&albums_cache);
|
||||
let initial_db = Arc::clone(&db);
|
||||
tokio::spawn(async move {
|
||||
match initial_db.get_all_albums().await {
|
||||
Ok(albums) => {
|
||||
let mut cache = initial_cache.write().await;
|
||||
cache.update(albums);
|
||||
println!("✅ 初始相册缓存加载完成");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ 初始缓存加载失败: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 设置定时刷新缓存任务(每30分钟)
|
||||
let refresh_cache = Arc::clone(&albums_cache);
|
||||
let refresh_db = Arc::clone(&db);
|
||||
tokio::spawn(async move {
|
||||
let mut interval = interval(Duration::from_secs(30 * 60)); // 30分钟
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
match refresh_db.get_all_albums().await {
|
||||
Ok(albums) => {
|
||||
let mut cache = refresh_cache.write().await;
|
||||
cache.update(albums);
|
||||
println!("🔄 相册缓存定时刷新完成");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ 缓存刷新失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
println!("API接口:");
|
||||
println!(" - GET /api/albums - 获取所有相册");
|
||||
println!(" - GET /api/search?q=关键词 - 搜索相册");
|
||||
@@ -80,9 +123,11 @@ async fn main() -> std::io::Result<()> {
|
||||
.wrap(middleware::Logger::default())
|
||||
.app_data(web::Data::new(Arc::clone(&db)))
|
||||
.app_data(web::Data::new(Arc::clone(&image_processor)))
|
||||
.app_data(web::Data::new(Arc::clone(&albums_cache)))
|
||||
// API路由
|
||||
.route("/api/albums", web::get().to(routes::get_albums))
|
||||
.route("/api/search", web::get().to(routes::search_albums))
|
||||
.route("/api/album/random", web::get().to(routes::get_random_album))
|
||||
.route("/api/album/{id}", web::get().to(routes::get_album_detail))
|
||||
.route("/api/albums", web::post().to(routes::create_album))
|
||||
.route("/api/thumbnail/{id}", web::get().to(routes::get_thumbnail))
|
||||
|
||||
@@ -34,7 +34,7 @@ pub struct AlbumWithImages {
|
||||
pub image_count: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct AlbumInfo {
|
||||
pub id: Uuid,
|
||||
pub title: String,
|
||||
@@ -91,4 +91,86 @@ pub struct FavoriteWithImage {
|
||||
pub struct FavoriteStatus {
|
||||
pub is_favorited: bool,
|
||||
pub favorite_id: Option<Uuid>,
|
||||
}
|
||||
|
||||
// 内存缓存结构
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AlbumsCache {
|
||||
pub albums: Vec<AlbumInfo>,
|
||||
pub search_index: std::collections::HashMap<String, Vec<AlbumInfo>>,
|
||||
pub last_updated: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
impl AlbumsCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
albums: Vec::new(),
|
||||
search_index: std::collections::HashMap::new(),
|
||||
last_updated: chrono::Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_expired(&self) -> bool {
|
||||
let now = chrono::Utc::now();
|
||||
let duration = now - self.last_updated;
|
||||
duration.num_minutes() >= 30 // 30分钟过期
|
||||
}
|
||||
|
||||
pub fn update(&mut self, albums: Vec<AlbumInfo>) {
|
||||
self.albums = albums.clone();
|
||||
self.last_updated = chrono::Utc::now();
|
||||
|
||||
// 构建搜索索引
|
||||
self.search_index.clear();
|
||||
for album in &self.albums {
|
||||
let title_lower = album.title.to_lowercase();
|
||||
|
||||
// 为每个单词创建索引
|
||||
for word in title_lower.split_whitespace() {
|
||||
if word.len() >= 2 { // 只索引长度>=2的单词
|
||||
self.search_index
|
||||
.entry(word.to_string())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(album.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// 为完整标题创建索引
|
||||
self.search_index
|
||||
.entry(title_lower.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(album.clone());
|
||||
}
|
||||
|
||||
println!("✅ 相册缓存已更新,共 {} 个相册,{} 个搜索索引项",
|
||||
self.albums.len(), self.search_index.len());
|
||||
}
|
||||
|
||||
pub fn search(&self, query: &str) -> Vec<AlbumInfo> {
|
||||
let query_lower = query.to_lowercase();
|
||||
let mut results = Vec::new();
|
||||
let mut seen_ids = std::collections::HashSet::new();
|
||||
|
||||
// 搜索完整匹配
|
||||
if let Some(matches) = self.search_index.get(&query_lower) {
|
||||
for album in matches {
|
||||
if seen_ids.insert(album.id) {
|
||||
results.push(album.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索单词匹配
|
||||
for word in query_lower.split_whitespace() {
|
||||
if let Some(matches) = self.search_index.get(word) {
|
||||
for album in matches {
|
||||
if seen_ids.insert(album.id) {
|
||||
results.push(album.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
}
|
||||
173
src/routes.rs
173
src/routes.rs
@@ -2,38 +2,19 @@ use crate::database::Database;
|
||||
use crate::image_processor::ImageProcessor;
|
||||
use crate::models::*;
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
use crate::image_processor;
|
||||
|
||||
pub async fn get_albums(
|
||||
db: web::Data<Arc<Database>>,
|
||||
query: web::Query<std::collections::HashMap<String, String>>,
|
||||
) -> impl Responder {
|
||||
let page = query.get("page").and_then(|s| s.parse::<usize>().ok());
|
||||
let limit = query.get("limit").and_then(|s| s.parse::<usize>().ok());
|
||||
|
||||
match db.get_albums(page, limit).await {
|
||||
Ok(albums) => HttpResponse::Ok().json(albums),
|
||||
Err(e) => {
|
||||
eprintln!("获取相册列表失败: {}", e);
|
||||
HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": "获取相册列表失败"
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收藏功能API路由
|
||||
|
||||
// 获取用户的收藏列表
|
||||
pub async fn get_favorites(
|
||||
db: web::Data<Arc<Database>>,
|
||||
) -> impl Responder {
|
||||
pub async fn get_favorites(db: web::Data<Arc<Database>>) -> impl Responder {
|
||||
// 这里使用固定用户ID,实际项目中应该从认证信息中获取
|
||||
let user_id = "default_user".to_string();
|
||||
|
||||
|
||||
match db.get_user_favorites(&user_id).await {
|
||||
Ok(favorites) => HttpResponse::Ok().json(favorites),
|
||||
Err(e) => {
|
||||
@@ -58,10 +39,10 @@ pub async fn check_favorite_status(
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 这里使用固定用户ID,实际项目中应该从认证信息中获取
|
||||
let user_id = "default_user".to_string();
|
||||
|
||||
|
||||
match db.get_favorite_status(&user_id, image_id).await {
|
||||
Ok(status) => HttpResponse::Ok().json(status),
|
||||
Err(e) => {
|
||||
@@ -80,7 +61,7 @@ pub async fn add_favorite(
|
||||
) -> impl Responder {
|
||||
// 这里使用固定用户ID,实际项目中应该从认证信息中获取
|
||||
let user_id = "default_user".to_string();
|
||||
|
||||
|
||||
match db.add_favorite(&user_id, favorite_data.into_inner()).await {
|
||||
Ok(favorite_id) => HttpResponse::Created().json(serde_json::json!({
|
||||
"id": favorite_id,
|
||||
@@ -108,7 +89,7 @@ pub async fn remove_favorite(
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
match db.remove_favorite(favorite_id).await {
|
||||
Ok(_) => HttpResponse::Ok().json(serde_json::json!({
|
||||
"message": "取消收藏成功"
|
||||
@@ -135,10 +116,10 @@ pub async fn remove_favorite_by_image(
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 这里使用固定用户ID,实际项目中应该从认证信息中获取
|
||||
let user_id = "default_user".to_string();
|
||||
|
||||
|
||||
match db.remove_favorite_by_image(&user_id, image_id).await {
|
||||
Ok(_) => HttpResponse::Ok().json(serde_json::json!({
|
||||
"message": "取消收藏成功"
|
||||
@@ -198,35 +179,106 @@ pub async fn get_original_image(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_albums(
|
||||
albums_cache: web::Data<Arc<RwLock<crate::models::AlbumsCache>>>,
|
||||
query: web::Query<HashMap<String, String>>,
|
||||
) -> impl Responder {
|
||||
let page = query.get("page").and_then(|s| s.parse::<usize>().ok()).unwrap_or(1);
|
||||
let limit = query.get("limit").and_then(|s| s.parse::<usize>().ok()).unwrap_or(50);
|
||||
let offset = (page - 1) * limit;
|
||||
|
||||
// 从内存缓存获取数据
|
||||
let cache = albums_cache.read().await;
|
||||
let total_albums = cache.albums.len();
|
||||
|
||||
// 应用分页
|
||||
let start = offset.min(total_albums);
|
||||
let end = (offset + limit).min(total_albums);
|
||||
let paginated_albums = if start < end {
|
||||
cache.albums[start..end].to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
drop(cache); // 释放读锁
|
||||
|
||||
HttpResponse::Ok().json(paginated_albums)
|
||||
}
|
||||
|
||||
pub async fn search_albums(
|
||||
db: web::Data<Arc<Database>>,
|
||||
albums_cache: web::Data<Arc<RwLock<crate::models::AlbumsCache>>>,
|
||||
query: web::Query<std::collections::HashMap<String, String>>,
|
||||
) -> impl Responder {
|
||||
let q = query.get("q").map(|s| s.as_str()).unwrap_or("");
|
||||
let page = query.get("page").and_then(|s| s.parse::<usize>().ok());
|
||||
let limit = query.get("limit").and_then(|s| s.parse::<usize>().ok());
|
||||
let page = query
|
||||
.get("page")
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
.unwrap_or(1);
|
||||
let limit = query
|
||||
.get("limit")
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
.unwrap_or(50);
|
||||
let offset = (page - 1) * limit;
|
||||
|
||||
if q.is_empty() {
|
||||
return match db.get_albums(page, limit).await {
|
||||
Ok(albums) => HttpResponse::Ok().json(albums),
|
||||
Err(e) => {
|
||||
eprintln!("获取相册列表失败: {}", e);
|
||||
HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": "获取相册列表失败"
|
||||
}))
|
||||
}
|
||||
// 如果没有搜索词,返回所有相册(带分页)
|
||||
let cache = albums_cache.read().await;
|
||||
let total_albums = cache.albums.len();
|
||||
|
||||
// 应用分页
|
||||
let start = offset.min(total_albums);
|
||||
let end = (offset + limit).min(total_albums);
|
||||
let paginated_albums = if start < end {
|
||||
cache.albums[start..end].to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
drop(cache);
|
||||
return HttpResponse::Ok().json(paginated_albums);
|
||||
}
|
||||
|
||||
match db.search_albums(q, page, limit).await {
|
||||
Ok(albums) => HttpResponse::Ok().json(albums),
|
||||
Err(e) => {
|
||||
eprintln!("搜索相册失败: {}", e);
|
||||
HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": "搜索失败"
|
||||
}))
|
||||
}
|
||||
// 从内存缓存搜索
|
||||
let cache = albums_cache.read().await;
|
||||
let mut search_results = cache.search(q);
|
||||
|
||||
// 应用分页到搜索结果
|
||||
let total_results = search_results.len();
|
||||
let start = offset.min(total_results);
|
||||
let end = (offset + limit).min(total_results);
|
||||
let paginated_results = if start < end {
|
||||
search_results.drain(start..end).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
drop(cache);
|
||||
|
||||
HttpResponse::Ok().json(paginated_results)
|
||||
}
|
||||
|
||||
pub async fn get_random_album(
|
||||
albums_cache: web::Data<Arc<RwLock<crate::models::AlbumsCache>>>,
|
||||
) -> impl Responder {
|
||||
// 从内存缓存获取数据
|
||||
let cache = albums_cache.read().await;
|
||||
|
||||
if cache.albums.is_empty() {
|
||||
drop(cache);
|
||||
return HttpResponse::NotFound().json(serde_json::json!({
|
||||
"error": "没有可用的相册"
|
||||
}));
|
||||
}
|
||||
|
||||
// 使用随机数生成器选择一个随机相册
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
let random_index = rng.gen_range(0..cache.albums.len());
|
||||
let random_album = cache.albums[random_index].clone();
|
||||
|
||||
drop(cache);
|
||||
|
||||
HttpResponse::Ok().json(random_album)
|
||||
}
|
||||
|
||||
pub async fn get_album_detail(
|
||||
@@ -320,7 +372,10 @@ pub async fn get_thumbnail(
|
||||
match fs::read(&original_path) {
|
||||
Ok(data) => {
|
||||
// 快速生成小尺寸缩略图(不进行复杂处理)
|
||||
match image_processor.generate_fast_thumbnail(&data, &image.id.to_string()).await {
|
||||
match image_processor
|
||||
.generate_fast_thumbnail(&data, &image.id.to_string())
|
||||
.await
|
||||
{
|
||||
Ok(thumbnail_data) => {
|
||||
// 异步保存高质量缩略图到文件(不阻塞响应)
|
||||
let config_clone = image_processor.config.clone();
|
||||
@@ -330,11 +385,18 @@ pub async fn get_thumbnail(
|
||||
let thumbnail_path = config_clone
|
||||
.thumbnails_dir
|
||||
.join(filename_clone.to_string());
|
||||
|
||||
|
||||
// 生成高质量缩略图并保存
|
||||
if let Ok(high_quality_data) = ImageProcessor::new(config_clone)
|
||||
.generate_thumbnail_from_memory(&original_data_clone, &filename_clone.to_string()).await {
|
||||
if let Err(e) = fs::write(&thumbnail_path, &high_quality_data) {
|
||||
.generate_thumbnail_from_memory(
|
||||
&original_data_clone,
|
||||
&filename_clone.to_string(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Err(e) =
|
||||
fs::write(&thumbnail_path, &high_quality_data)
|
||||
{
|
||||
eprintln!("保存高质量缩略图失败: {}", e);
|
||||
}
|
||||
}
|
||||
@@ -350,8 +412,11 @@ pub async fn get_thumbnail(
|
||||
Err(e) => {
|
||||
eprintln!("快速缩略图生成失败: {}", e);
|
||||
// 如果快速生成失败,返回原始图片的小尺寸版本
|
||||
let small_data = image_processor.resize_image_to_max(&data, 800).await;
|
||||
HttpResponse::Ok().content_type(image.mime_type).body(small_data)
|
||||
let small_data =
|
||||
image_processor.resize_image_to_max(&data, 800).await;
|
||||
HttpResponse::Ok()
|
||||
.content_type(image.mime_type)
|
||||
.body(small_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user