//! 评论审核状态批量查询。 //! //! 用于前端轮询刚刚提交的评论是否已通过审核,支持传入多个 id。 //! 仅在 `feature = "server"` 启用的服务端构建中查询数据库。 use dioxus::prelude::*; /// 单个评论的待审核状态结果。 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PendingStatusItem { /// 评论 id。 pub id: i64, /// 当前状态:approved / pending / rejected / spam / trash / gone。 pub status: String, } /// 查询一组评论的当前审核状态。 /// /// 如果某 id 不存在,则返回状态 `"gone"`。 /// Dioxus server function,注册在 `/api` 路径下。 #[server(CheckPendingStatus, "/api")] pub async fn check_pending_status(ids: Vec) -> Result, ServerFnError> { // 仅在服务端构建中执行 SQL 查询。 #[cfg(feature = "server")] { use crate::api::error::AppError; use crate::db::pool::get_conn; // 空列表直接返回空结果,避免无意义的数据库查询。 if ids.is_empty() { return Ok(vec![]); } // 限流防高速遍历枚举评论状态(L3)。本接口供访客轮询自己刚提交的评论 // 审核状态,故不加 admin 鉴权;但 strict 限流(对 unknown IP 降级宽松桶) // 足以阻止批量枚举。 if let Some(ctx) = dioxus::fullstack::FullstackContext::current() { let parts = ctx.parts_mut(); let ip = crate::api::rate_limit::get_client_ip(&parts.headers); if let Err(_msg) = crate::api::rate_limit::check_strict_limit(&ip) { return Err(ServerFnError::new("请求过于频繁,请稍后再试")); } } let client = get_conn().await.map_err(AppError::db_conn)?; let rows = client .query( "SELECT id, status FROM comments WHERE id = ANY($1)", &[&ids], ) .await .map_err(AppError::query)?; // 将查询结果收集为 HashMap,便于后续按传入顺序补齐缺失项。 let found: std::collections::HashMap = rows .iter() .map(|r| (r.get::<_, i64>(0), r.get::<_, String>(1))) .collect(); let result: Vec = ids .into_iter() .map(|id| { let status = found .get(&id) .cloned() .unwrap_or_else(|| "gone".to_string()); PendingStatusItem { id, status } }) .collect(); Ok(result) } #[cfg(not(feature = "server"))] unreachable!() }