//! 文章统计接口。 //! //! 返回文章总数、草稿数、已发布数与回收站(软删除)数量,供管理后台仪表盘与 //! 文章列表页使用,结果缓存。Dioxus server function,注册在 `/api` 路径下。 //! 仅在 `feature = "server"` 启用的服务端构建中查询数据库。 use dioxus::prelude::*; #[cfg(feature = "server")] use super::helpers::get_current_admin_user; use super::types::PostStatsResponse; #[cfg(feature = "server")] use crate::api::error::AppError; #[cfg(feature = "server")] use crate::db::pool::get_conn; #[cfg(feature = "server")] use crate::models::post::PostStats; /// 获取文章统计信息。 /// /// 需要 admin 权限;优先命中缓存,未命中时通过单次条件聚合查询同时统计 /// 未删除文章总数、草稿数、已发布数与回收站(软删除)数量。 #[server(GetPostStats, "/api")] pub async fn get_post_stats() -> Result { let _user = get_current_admin_user().await?; #[cfg(feature = "server")] { if let Some(cached) = crate::cache::get_post_stats().await { return Ok(PostStatsResponse { stats: cached }); } let client = get_conn().await.map_err(AppError::db_conn)?; // 通过单次条件聚合查询同时统计总数、草稿数、已发布数与回收站数量。 let row = client .query_one( "SELECT 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 = 'published') AS published, COUNT(*) FILTER (WHERE deleted_at IS NOT NULL) AS trash FROM posts", &[], ) .await .map_err(AppError::query)?; let stats = PostStats { total: row.get("total"), drafts: row.get("drafts"), published: row.get("published"), trash: row.get("trash"), }; crate::cache::set_post_stats(stats.clone()).await; Ok(PostStatsResponse { stats }) } #[cfg(not(feature = "server"))] { Ok(PostStatsResponse { stats: PostStats { total: 0, drafts: 0, published: 0, trash: 0, }, }) } }