feat(stats): add trash count to PostStats for posts page badge
为合并回收站到 /admin/posts 的 tab 设计铺路:PostStats 新增 trash 字段, 统计 deleted_at IS NOT NULL 的文章数,供文章列表页「回收站」tab 角标使用。 - stats.rs: 条件聚合查询增加 trash 列,server/非 server 构造同步补字段 - models/post.rs: PostStats 加 pub trash: i64 - cache.rs: post_stats_cache_roundtrip 测试 fixture 补 trash 字段
This commit is contained in:
parent
ebeec2a295
commit
9c3f2242ba
@ -1,7 +1,7 @@
|
|||||||
//! 文章统计接口。
|
//! 文章统计接口。
|
||||||
//!
|
//!
|
||||||
//! 返回文章总数、草稿数与已发布数,供管理后台仪表盘使用,结果缓存。
|
//! 返回文章总数、草稿数、已发布数与回收站(软删除)数量,供管理后台仪表盘与
|
||||||
//! Dioxus server function,注册在 `/api` 路径下。
|
//! 文章列表页使用,结果缓存。Dioxus server function,注册在 `/api` 路径下。
|
||||||
//! 仅在 `feature = "server"` 启用的服务端构建中查询数据库。
|
//! 仅在 `feature = "server"` 启用的服务端构建中查询数据库。
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
@ -19,7 +19,7 @@ use crate::models::post::PostStats;
|
|||||||
/// 获取文章统计信息。
|
/// 获取文章统计信息。
|
||||||
///
|
///
|
||||||
/// 需要 admin 权限;优先命中缓存,未命中时通过单次条件聚合查询同时统计
|
/// 需要 admin 权限;优先命中缓存,未命中时通过单次条件聚合查询同时统计
|
||||||
/// 未删除文章总数、草稿数与已发布数。
|
/// 未删除文章总数、草稿数、已发布数与回收站(软删除)数量。
|
||||||
#[server(GetPostStats, "/api")]
|
#[server(GetPostStats, "/api")]
|
||||||
pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
|
pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
|
||||||
let _user = get_current_admin_user().await?;
|
let _user = get_current_admin_user().await?;
|
||||||
@ -32,13 +32,14 @@ pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
|
|||||||
|
|
||||||
let client = get_conn().await.map_err(AppError::db_conn)?;
|
let client = get_conn().await.map_err(AppError::db_conn)?;
|
||||||
|
|
||||||
// 通过单次条件聚合查询同时统计总数、草稿数与已发布数。
|
// 通过单次条件聚合查询同时统计总数、草稿数、已发布数与回收站数量。
|
||||||
let row = client
|
let row = client
|
||||||
.query_one(
|
.query_one(
|
||||||
"SELECT
|
"SELECT
|
||||||
COUNT(*) FILTER (WHERE deleted_at IS NULL) AS total,
|
COUNT(*) FILTER (WHERE deleted_at IS NULL) AS total,
|
||||||
COUNT(*) FILTER (WHERE deleted_at IS NULL AND status = 'draft') AS drafts,
|
COUNT(*) FILTER (WHERE deleted_at IS NULL AND status = 'draft') AS drafts,
|
||||||
COUNT(*) FILTER (WHERE deleted_at IS NULL AND status = 'published') AS published
|
COUNT(*) FILTER (WHERE deleted_at IS NULL AND status = 'published') AS published,
|
||||||
|
COUNT(*) FILTER (WHERE deleted_at IS NOT NULL) AS trash
|
||||||
FROM posts",
|
FROM posts",
|
||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
@ -49,6 +50,7 @@ pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
|
|||||||
total: row.get("total"),
|
total: row.get("total"),
|
||||||
drafts: row.get("drafts"),
|
drafts: row.get("drafts"),
|
||||||
published: row.get("published"),
|
published: row.get("published"),
|
||||||
|
trash: row.get("trash"),
|
||||||
};
|
};
|
||||||
crate::cache::set_post_stats(stats.clone()).await;
|
crate::cache::set_post_stats(stats.clone()).await;
|
||||||
Ok(PostStatsResponse { stats })
|
Ok(PostStatsResponse { stats })
|
||||||
@ -61,6 +63,7 @@ pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
|
|||||||
total: 0,
|
total: 0,
|
||||||
drafts: 0,
|
drafts: 0,
|
||||||
published: 0,
|
published: 0,
|
||||||
|
trash: 0,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -736,6 +736,7 @@ mod tests {
|
|||||||
total: 10,
|
total: 10,
|
||||||
drafts: 3,
|
drafts: 3,
|
||||||
published: 7,
|
published: 7,
|
||||||
|
trash: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
set_post_stats(stats.clone()).await;
|
set_post_stats(stats.clone()).await;
|
||||||
|
|||||||
@ -187,6 +187,8 @@ pub struct PostStats {
|
|||||||
pub drafts: i64,
|
pub drafts: i64,
|
||||||
/// 已发布数量。
|
/// 已发布数量。
|
||||||
pub published: i64,
|
pub published: i64,
|
||||||
|
/// 回收站(软删除)数量。
|
||||||
|
pub trash: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user