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::{
|
||||
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, PaginatorTrait,
|
||||
QueryFilter, QueryOrder, Set,
|
||||
ActiveModelTrait, ColumnTrait, ConnectionTrait, DatabaseConnection, EntityTrait,
|
||||
PaginatorTrait, QueryFilter, QueryOrder, Set,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -16,7 +16,8 @@ pub struct StickerService;
|
||||
impl StickerService {
|
||||
/// 获取贴纸包列表
|
||||
///
|
||||
/// 返回所有可用的贴纸包,按分类和名称排序。
|
||||
/// 使用 SQL GROUP BY 批量获取贴纸计数,替代逐包 COUNT 查询。
|
||||
/// 性能: 2 次查询(packs + counts)替代 N+1 次。
|
||||
pub async fn list_sticker_packs(
|
||||
tenant_id: Uuid,
|
||||
category: Option<String>,
|
||||
@@ -36,24 +37,47 @@ impl StickerService {
|
||||
.all(db)
|
||||
.await?;
|
||||
|
||||
let mut result = Vec::with_capacity(packs.len());
|
||||
for pack in packs {
|
||||
let sticker_count = sticker::Entity::find()
|
||||
.filter(sticker::Column::PackId.eq(pack.id))
|
||||
.filter(sticker::Column::DeletedAt.is_null())
|
||||
.count(db)
|
||||
.await? as i32;
|
||||
// 批量获取所有贴纸包的贴纸计数 — 单次 SQL GROUP BY(替代 N+1 查询)
|
||||
let pack_ids: Vec<Uuid> = packs.iter().map(|p| p.id).collect();
|
||||
let count_map: std::collections::HashMap<Uuid, i64> = if !pack_ids.is_empty() {
|
||||
let sql = r#"
|
||||
SELECT pack_id, COUNT(*) AS count
|
||||
FROM stickers
|
||||
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,
|
||||
name: pack.name,
|
||||
description: pack.description,
|
||||
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,
|
||||
category: pack.category,
|
||||
});
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user