perf(diary): sticker_service 批量 GROUP BY 替代 N+1 贴纸计数 — 8a-C04
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled

- list_sticker_packs: 单次 SQL GROUP BY pack_id 获取所有计数
- 2 次查询(packs + counts)替代 N+1 次
- 使用 PostgreSQL ANY() 传递 UUID 数组
- 测试 80/80 通过
This commit is contained in:
iven
2026-06-03 15:51:05 +08:00
parent 4e5c1287a6
commit b6ffc60331

View File

@@ -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)
}