refactor(posts): use precise cache invalidation in create/update/delete

This commit is contained in:
xfy 2026-06-17 16:37:20 +08:00
parent c528936abb
commit 82b070e7ac
3 changed files with 43 additions and 14 deletions

View File

@ -139,17 +139,14 @@ pub async fn create_post(
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
// 写入成功后失效文章列表、标签与统计缓存。 // 写入成功后按粒度失效相关缓存。
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_lists();
crate::cache::invalidate_all_tags(); crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats(); crate::cache::invalidate_post_stats();
// 失效按 slug 缓存,避免之前缓存的 404 继续命中。 // 失效按 slug 缓存,避免之前缓存的 404 继续命中。
crate::cache::invalidate_post_by_slug(&final_slug).await; crate::cache::invalidate_post_by_slug(&final_slug).await;
// 失效该文章涉及的所有标签下文章列表缓存。
// 失效该文章涉及的所有标签缓存。 crate::cache::invalidate_tag_posts_for(&tags_cleaned).await;
for tag_name in &tags_cleaned {
crate::cache::invalidate_posts_by_tag(tag_name).await;
}
Ok(CreatePostResponse { Ok(CreatePostResponse {
success: true, success: true,

View File

@ -27,6 +27,34 @@ pub async fn delete_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErr
{ {
let client = get_conn().await.map_err(AppError::db_conn)?; let client = get_conn().await.map_err(AppError::db_conn)?;
// 在软删除前查询 slug 与标签,用于后续精准失效缓存。
let slug_row = client
.query_opt(
"SELECT slug FROM posts WHERE id = $1 AND deleted_at IS NULL",
&[&post_id],
)
.await
.map_err(AppError::query)?;
let Some(slug_row) = slug_row else {
return Ok(CreatePostResponse {
success: false,
message: "文章不存在".to_string(),
post_id: None,
slug: None,
});
};
let slug: String = slug_row.get(0);
let tag_rows = client
.query(
"SELECT t.name FROM tags t JOIN post_tags pt ON t.id = pt.tag_id WHERE pt.post_id = $1",
&[&post_id],
)
.await
.map_err(AppError::query)?;
let tags: Vec<String> = tag_rows.iter().map(|r| r.get(0)).collect();
// 软删除:仅影响未被删除的文章。 // 软删除:仅影响未被删除的文章。
let result = client let result = client
.execute( .execute(
@ -45,14 +73,18 @@ pub async fn delete_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErr
}); });
} }
// 删除后所有文章相关缓存均失效。 // 删除后按影响范围精准失效缓存。
crate::cache::invalidate_all_post_caches(); crate::cache::invalidate_post_lists();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_post_by_slug(&slug).await;
crate::cache::invalidate_tag_posts_for(&tags).await;
Ok(CreatePostResponse { Ok(CreatePostResponse {
success: true, success: true,
message: "删除成功".to_string(), message: "删除成功".to_string(),
post_id: Some(post_id), post_id: Some(post_id),
slug: None, slug: Some(slug),
}) })
} }

View File

@ -198,17 +198,17 @@ pub async fn update_post(
// 失效文章列表、标签、当前 slug 与统计缓存。 // 失效文章列表、标签、当前 slug 与统计缓存。
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_lists();
crate::cache::invalidate_all_tags(); crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_by_slug(&final_slug).await;
crate::cache::invalidate_post_stats(); crate::cache::invalidate_post_stats();
crate::cache::invalidate_post_by_slug(&final_slug).await;
// 合并旧标签与新标签,统一失效标签下的文章列表缓存。 // 合并旧标签与新标签,统一失效标签下的文章列表缓存。
let all_tags_to_invalidate: std::collections::HashSet<String> = old_tags let all_tags_to_invalidate: Vec<String> = old_tags
.into_iter() .into_iter()
.chain(tags_for_invalidation.into_iter()) .chain(tags_for_invalidation.into_iter())
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect(); .collect();
for tag_name in &all_tags_to_invalidate { crate::cache::invalidate_tag_posts_for(&all_tags_to_invalidate).await;
crate::cache::invalidate_posts_by_tag(tag_name).await;
}
// 若 slug 发生变更,额外失效旧 slug 缓存。 // 若 slug 发生变更,额外失效旧 slug 缓存。
if let Some(ref old) = old_slug { if let Some(ref old) = old_slug {