perf(diary): sticker_service 批量 GROUP BY 替代 N+1 贴纸计数 — 8a-C04
- list_sticker_packs: 单次 SQL GROUP BY pack_id 获取所有计数 - 2 次查询(packs + counts)替代 N+1 次 - 使用 PostgreSQL ANY() 传递 UUID 数组 - 测试 80/80 通过
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
// 贴纸服务 — 贴纸包与贴纸管理
|
// 贴纸服务 — 贴纸包与贴纸管理
|
||||||
|
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, PaginatorTrait,
|
ActiveModelTrait, ColumnTrait, ConnectionTrait, DatabaseConnection, EntityTrait,
|
||||||
QueryFilter, QueryOrder, Set,
|
PaginatorTrait, QueryFilter, QueryOrder, Set,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ pub struct StickerService;
|
|||||||
impl StickerService {
|
impl StickerService {
|
||||||
/// 获取贴纸包列表
|
/// 获取贴纸包列表
|
||||||
///
|
///
|
||||||
/// 返回所有可用的贴纸包,按分类和名称排序。
|
/// 使用 SQL GROUP BY 批量获取贴纸计数,替代逐包 COUNT 查询。
|
||||||
|
/// 性能: 2 次查询(packs + counts)替代 N+1 次。
|
||||||
pub async fn list_sticker_packs(
|
pub async fn list_sticker_packs(
|
||||||
tenant_id: Uuid,
|
tenant_id: Uuid,
|
||||||
category: Option<String>,
|
category: Option<String>,
|
||||||
@@ -36,24 +37,47 @@ impl StickerService {
|
|||||||
.all(db)
|
.all(db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut result = Vec::with_capacity(packs.len());
|
// 批量获取所有贴纸包的贴纸计数 — 单次 SQL GROUP BY(替代 N+1 查询)
|
||||||
for pack in packs {
|
let pack_ids: Vec<Uuid> = packs.iter().map(|p| p.id).collect();
|
||||||
let sticker_count = sticker::Entity::find()
|
let count_map: std::collections::HashMap<Uuid, i64> = if !pack_ids.is_empty() {
|
||||||
.filter(sticker::Column::PackId.eq(pack.id))
|
let sql = r#"
|
||||||
.filter(sticker::Column::DeletedAt.is_null())
|
SELECT pack_id, COUNT(*) AS count
|
||||||
.count(db)
|
FROM stickers
|
||||||
.await? as i32;
|
WHERE pack_id = ANY($1)
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
GROUP BY pack_id
|
||||||
|
"#;
|
||||||
|
|
||||||
result.push(StickerPackResp {
|
let stmt = sea_orm::Statement::from_sql_and_values(
|
||||||
|
sea_orm::DatabaseBackend::Postgres,
|
||||||
|
sql,
|
||||||
|
[pack_ids.into()],
|
||||||
|
);
|
||||||
|
|
||||||
|
let rows = db.query_all(stmt).await?;
|
||||||
|
rows.into_iter()
|
||||||
|
.filter_map(|row| {
|
||||||
|
let pack_id: Uuid = row.try_get_by_index::<Uuid>(0).ok()?;
|
||||||
|
let count: i64 = row.try_get_by_index::<i64>(1).ok()?;
|
||||||
|
Some((pack_id, count))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
std::collections::HashMap::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let result: Vec<StickerPackResp> = packs
|
||||||
|
.into_iter()
|
||||||
|
.map(|pack| StickerPackResp {
|
||||||
id: pack.id,
|
id: pack.id,
|
||||||
name: pack.name,
|
name: pack.name,
|
||||||
description: pack.description,
|
description: pack.description,
|
||||||
cover_image_url: pack.thumbnail_url,
|
cover_image_url: pack.thumbnail_url,
|
||||||
sticker_count,
|
sticker_count: *count_map.get(&pack.id).unwrap_or(&0) as i32,
|
||||||
is_free: pack.is_free,
|
is_free: pack.is_free,
|
||||||
category: pack.category,
|
category: pack.category,
|
||||||
});
|
})
|
||||||
}
|
.collect();
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user