update code level
This commit is contained in:
@@ -323,47 +323,26 @@ function updateAlbumMeta() {
|
||||
|
||||
let chips = [];
|
||||
|
||||
// 添加人名标签
|
||||
if (currentAlbum.person_name_cleaned) {
|
||||
chips.push({
|
||||
text: currentAlbum.person_name_cleaned,
|
||||
type: 'person',
|
||||
category: 'person',
|
||||
query: currentAlbum.person_name_cleaned
|
||||
// 添加人名标签(从 search.db 返回的 persons 数组)
|
||||
if (currentAlbum.persons && Array.isArray(currentAlbum.persons)) {
|
||||
currentAlbum.persons.forEach(name => {
|
||||
chips.push({
|
||||
text: name,
|
||||
type: 'person',
|
||||
category: 'person',
|
||||
query: name
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 添加标签(支持多种格式)
|
||||
if (currentAlbum.tags) {
|
||||
let tagList = [];
|
||||
try {
|
||||
const parsed = JSON.parse(currentAlbum.tags);
|
||||
if (Array.isArray(parsed)) {
|
||||
parsed.forEach(item => {
|
||||
if (Array.isArray(item)) {
|
||||
tagList.push({ text: item[0], category: item[1] || 'default' });
|
||||
} else if (typeof item === 'string') {
|
||||
tagList.push({ text: item, category: getTagCategory(item) });
|
||||
} else if (item.tag || item.name) {
|
||||
const t = item.tag || item.name;
|
||||
const c = item.category || item.type || getTagCategory(t);
|
||||
tagList.push({ text: t, category: c });
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
currentAlbum.tags.split(',').forEach(tag => {
|
||||
const t = tag.trim();
|
||||
if (t) tagList.push({ text: t, category: getTagCategory(t) });
|
||||
});
|
||||
}
|
||||
|
||||
tagList.forEach(t => {
|
||||
// 添加标签(从 search.db 返回的 tags 数组)
|
||||
if (currentAlbum.tags && Array.isArray(currentAlbum.tags)) {
|
||||
currentAlbum.tags.forEach(tag => {
|
||||
chips.push({
|
||||
text: t.text,
|
||||
text: tag,
|
||||
type: 'tag',
|
||||
category: t.category,
|
||||
query: t.text
|
||||
category: getTagCategory(tag),
|
||||
query: tag
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ impl Database {
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Some(AlbumWithImages { album, images: images.clone(), image_count: images.len() as i64 }))
|
||||
Ok(Some(AlbumWithImages { album, images: images.clone(), image_count: images.len() as i64, tags: vec![], persons: vec![] }))
|
||||
}
|
||||
|
||||
pub async fn get_all_albums_full(&self) -> Vec<AlbumMeta> {
|
||||
|
||||
@@ -30,6 +30,8 @@ pub struct AlbumWithImages {
|
||||
pub album: Album,
|
||||
pub images: Vec<super::image::Image>,
|
||||
pub image_count: i64,
|
||||
pub tags: Vec<String>,
|
||||
pub persons: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -79,10 +79,26 @@ pub async fn get_random_album(cache: web::Data<Arc<RwLock<AlbumsCache>>>) -> imp
|
||||
HttpResponse::Ok().json(&c.albums[idx])
|
||||
}
|
||||
|
||||
pub async fn get_album_detail(db: web::Data<Arc<Database>>, path: web::Path<String>) -> impl Responder {
|
||||
pub async fn get_album_detail(
|
||||
db: web::Data<Arc<Database>>,
|
||||
search_db: web::Data<Arc<SearchDb>>,
|
||||
path: web::Path<String>,
|
||||
) -> impl Responder {
|
||||
let id = match Uuid::parse_str(&path.into_inner()) { Ok(id) => id, Err(_) => return HttpResponse::BadRequest().json(serde_json::json!({"error": "无效ID"})) };
|
||||
let id_str = id.to_string();
|
||||
match db.get_album_with_images(id).await {
|
||||
Ok(Some(album)) => HttpResponse::Ok().json(album),
|
||||
Ok(Some(mut album)) => {
|
||||
// 从 search.db 查询该相册的 tags 和 persons
|
||||
let tag_rows: Vec<String> = sqlx::query_scalar(
|
||||
r#"SELECT t.name FROM album_tags at JOIN tags t ON at.tag_id = t.id WHERE at.album_id = ?"#
|
||||
).bind(&id_str).fetch_all(&search_db.pool).await.unwrap_or_default();
|
||||
let person_rows: Vec<String> = sqlx::query_scalar(
|
||||
r#"SELECT p.name FROM album_persons ap JOIN persons p ON ap.person_id = p.id WHERE ap.album_id = ?"#
|
||||
).bind(&id_str).fetch_all(&search_db.pool).await.unwrap_or_default();
|
||||
album.tags = tag_rows;
|
||||
album.persons = person_rows;
|
||||
HttpResponse::Ok().json(album)
|
||||
}
|
||||
Ok(None) => HttpResponse::NotFound().json(serde_json::json!({"error": "相册不存在"})),
|
||||
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": format!("{}", e)})),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use crate::cache::PersonCache;
|
||||
use crate::db::Database;
|
||||
use crate::search_db::SearchDb;
|
||||
use crate::models::*;
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
use sqlx::Row;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// GET /api/people — 从内存缓存获取所有人名列表
|
||||
pub async fn get_people(cache: web::Data<Arc<RwLock<PersonCache>>>) -> impl Responder {
|
||||
@@ -35,21 +38,53 @@ pub async fn search_people(
|
||||
})
|
||||
}
|
||||
|
||||
/// GET /api/people/{name} — 从 search.db 查找人物详情(缓存查人物信息,search.db 查相册列表)
|
||||
/// GET /api/people/{name} — 查找人物详情
|
||||
/// 人物基本信息从内存缓存获取,相册列表从 imager.db 查询
|
||||
pub async fn get_person_detail(
|
||||
db: web::Data<Arc<Database>>,
|
||||
search_db: web::Data<Arc<SearchDb>>,
|
||||
cache: web::Data<Arc<RwLock<PersonCache>>>,
|
||||
path: web::Path<String>,
|
||||
) -> impl Responder {
|
||||
let name = path.into_inner();
|
||||
let c = cache.read().await;
|
||||
// 在内存缓存中查找人物
|
||||
let found = c.search(&name);
|
||||
let p = found.into_iter().find(|p| p.name.to_lowercase() == name.to_lowercase());
|
||||
match p {
|
||||
Some(p) => {
|
||||
// 相册列表从 search.db 获取
|
||||
let albums = search_db.get_person_albums(p.id).await;
|
||||
// 从 search.db 获取相册 ID 列表
|
||||
let album_ids = search_db.get_person_album_ids(p.id).await;
|
||||
// 从 imager.db 查询相册详情
|
||||
let albums = if album_ids.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let placeholders: Vec<String> = (0..album_ids.len()).map(|_| "?".to_string()).collect();
|
||||
let sql = format!(
|
||||
"SELECT id, title, document, description, cover_image_id, image_count, created_at \
|
||||
FROM albums WHERE id IN ({}) ORDER BY created_at DESC",
|
||||
placeholders.join(", ")
|
||||
);
|
||||
let mut query = sqlx::query(&sql);
|
||||
for id in &album_ids {
|
||||
query = query.bind(id);
|
||||
}
|
||||
query.fetch_all(&db.pool).await.unwrap_or_default()
|
||||
.iter().map(|r| {
|
||||
let id_str: String = r.get("id");
|
||||
AlbumInfo {
|
||||
id: Uuid::parse_str(&id_str).unwrap_or_else(|_| Uuid::nil()),
|
||||
title: r.get("title"),
|
||||
document: r.get("document"),
|
||||
description: r.get("description"),
|
||||
cover_image_id: r.get("cover_image_id"),
|
||||
image_count: r.get("image_count"),
|
||||
created_at: r.get("created_at"),
|
||||
tags: vec![],
|
||||
persons: vec![],
|
||||
}
|
||||
}).collect()
|
||||
};
|
||||
|
||||
HttpResponse::Ok().json(PersonWithAlbums {
|
||||
album_count: albums.len(),
|
||||
person: Person {
|
||||
|
||||
@@ -273,30 +273,11 @@ impl SearchDb {
|
||||
})
|
||||
}
|
||||
|
||||
/// 获取人物的所有相册
|
||||
pub async fn get_person_albums(&self, person_id: i64) -> Vec<AlbumInfo> {
|
||||
let rows = sqlx::query(
|
||||
r#"SELECT a.id, a.title, a.cover_image_id, a.image_count, a.created_at
|
||||
FROM albums a
|
||||
JOIN album_persons ap ON a.id = ap.album_id
|
||||
WHERE ap.person_id = ?
|
||||
ORDER BY a.created_at DESC"#
|
||||
).bind(person_id).fetch_all(&self.pool).await.unwrap_or_default();
|
||||
|
||||
rows.iter().map(|r| {
|
||||
let id_str: String = r.get("id");
|
||||
AlbumInfo {
|
||||
id: Uuid::parse_str(&id_str).unwrap_or_else(|_| Uuid::nil()),
|
||||
title: r.get("title"),
|
||||
document: String::new(),
|
||||
description: None,
|
||||
cover_image_id: r.get("cover_image_id"),
|
||||
image_count: r.get("image_count"),
|
||||
created_at: r.get("created_at"),
|
||||
tags: vec![],
|
||||
persons: vec![],
|
||||
}
|
||||
}).collect()
|
||||
/// 获取人物的所有相册 ID(从 search.db 的 album_persons 关联表查询)
|
||||
pub async fn get_person_album_ids(&self, person_id: i64) -> Vec<String> {
|
||||
sqlx::query_scalar::<_, String>(
|
||||
"SELECT album_id FROM album_persons WHERE person_id = ?"
|
||||
).bind(person_id).fetch_all(&self.pool).await.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// 获取人物的 top 标签(用于详情页)
|
||||
|
||||
Reference in New Issue
Block a user