yggdrasil/api/comments/
update.rs1use crate::api::comments::types::*;
8use dioxus::prelude::*;
9
10#[server(ApproveComment, "/api")]
14pub async fn approve_comment(id: i64) -> Result<CommentResponse, ServerFnError> {
15 #[cfg(feature = "server")]
16 {
17 use crate::api::auth::get_current_admin_user;
18 use crate::api::error::AppError;
19 use crate::cache;
20 use crate::db::pool::get_conn;
21
22 let _admin = get_current_admin_user().await?;
23
24 let client = get_conn().await.map_err(AppError::db_conn)?;
25
26 let row = client
27 .query_opt(
28 "SELECT post_id, status FROM comments WHERE id = $1 AND deleted_at IS NULL",
29 &[&id],
30 )
31 .await
32 .map_err(AppError::query)?;
33
34 let post_id: i32 = match row {
35 Some(r) => r.get("post_id"),
36 None => {
37 return Ok(CommentResponse::error(
38 "not_found",
39 "评论不存在".to_string(),
40 ));
41 }
42 };
43
44 client
46 .execute(
47 "UPDATE comments SET status = 'approved', approved_at = NOW() WHERE id = $1",
48 &[&id],
49 )
50 .await
51 .map_err(AppError::query)?;
52
53 client
55 .execute(
56 "WITH RECURSIVE ancestors AS ( \
57 SELECT parent_id FROM comments WHERE id = $1 \
58 UNION ALL \
59 SELECT c.parent_id FROM comments c JOIN ancestors a ON c.id = a.parent_id WHERE a.parent_id IS NOT NULL \
60 ) \
61 UPDATE comments SET status = 'approved', approved_at = NOW() \
62 WHERE id IN (SELECT parent_id FROM ancestors WHERE parent_id IS NOT NULL) AND status = 'pending'",
63 &[&id],
64 )
65 .await
66 .map_err(AppError::query)?;
67
68 cache::invalidate_comments_by_post(post_id).await;
69 cache::invalidate_pending_count().await;
70
71 Ok(CommentResponse::ok("已通过".to_string()))
72 }
73 #[cfg(not(feature = "server"))]
74 unreachable!()
75}
76
77#[server(SpamComment, "/api")]
81pub async fn spam_comment(id: i64) -> Result<CommentResponse, ServerFnError> {
82 #[cfg(feature = "server")]
83 {
84 use crate::api::auth::get_current_admin_user;
85 use crate::api::error::AppError;
86 use crate::cache;
87 use crate::db::pool::get_conn;
88
89 let _admin = get_current_admin_user().await?;
90
91 let client = get_conn().await.map_err(AppError::db_conn)?;
92
93 let row = client
94 .query_opt(
95 "SELECT post_id, status FROM comments WHERE id = $1 AND deleted_at IS NULL",
96 &[&id],
97 )
98 .await
99 .map_err(AppError::query)?;
100
101 if let Some(r) = row {
102 let post_id: i32 = r.get("post_id");
103 let old_status: String = r.get("status");
104
105 client
106 .execute(
107 "UPDATE comments SET status = 'spam' WHERE id = $1 AND deleted_at IS NULL",
108 &[&id],
109 )
110 .await
111 .map_err(AppError::query)?;
112
113 if old_status == "approved" {
114 cache::invalidate_comments_by_post(post_id).await;
115 }
116 cache::invalidate_pending_count().await;
117 }
118
119 Ok(CommentResponse::ok("已标记为垃圾".to_string()))
120 }
121 #[cfg(not(feature = "server"))]
122 unreachable!()
123}
124
125#[server(TrashComment, "/api")]
129pub async fn trash_comment(id: i64) -> Result<CommentResponse, ServerFnError> {
130 #[cfg(feature = "server")]
131 {
132 use crate::api::auth::get_current_admin_user;
133 use crate::api::error::AppError;
134 use crate::cache;
135 use crate::db::pool::get_conn;
136
137 let _admin = get_current_admin_user().await?;
138
139 let client = get_conn().await.map_err(AppError::db_conn)?;
140
141 let row = client
142 .query_opt(
143 "SELECT post_id FROM comments WHERE id = $1 AND deleted_at IS NULL",
144 &[&id],
145 )
146 .await
147 .map_err(AppError::query)?;
148
149 if let Some(r) = row {
150 let post_id: i32 = r.get("post_id");
151
152 client
153 .execute(
154 "UPDATE comments SET status = 'trash', deleted_at = NOW() WHERE id = $1",
155 &[&id],
156 )
157 .await
158 .map_err(AppError::query)?;
159
160 cache::invalidate_comments_by_post(post_id).await;
161 cache::invalidate_pending_count().await;
162 }
163
164 Ok(CommentResponse::ok("已删除".to_string()))
165 }
166 #[cfg(not(feature = "server"))]
167 unreachable!()
168}
169
170#[server(BatchUpdateCommentStatus, "/api")]
175pub async fn batch_update_comment_status(
176 ids: Vec<i64>,
177 status: String,
178) -> Result<BatchStatusResponse, ServerFnError> {
179 #[cfg(feature = "server")]
180 {
181 use crate::api::auth::get_current_admin_user;
182 use crate::api::error::AppError;
183 use crate::cache;
184 use crate::db::pool::get_conn;
185
186 let _admin = get_current_admin_user().await?;
187
188 if !matches!(status.as_str(), "approved" | "spam" | "trash") {
190 return Ok(BatchStatusResponse {
191 success: false,
192 updated_count: 0,
193 message: "无效的状态".to_string(),
194 });
195 }
196
197 let client = get_conn().await.map_err(AppError::db_conn)?;
198
199 let post_ids: Vec<i32> = client
201 .query(
202 "SELECT DISTINCT post_id FROM comments WHERE id = ANY($1)",
203 &[&ids],
204 )
205 .await
206 .map_err(AppError::query)?
207 .iter()
208 .map(|r| r.get("post_id"))
209 .collect();
210
211 let result = if status == "trash" {
213 client
214 .execute(
215 "UPDATE comments SET status = $1, deleted_at = NOW() WHERE id = ANY($2)",
216 &[&status, &ids],
217 )
218 .await
219 .map_err(AppError::query)?
220 } else if status == "approved" {
221 client
222 .execute(
223 "UPDATE comments SET status = $1, approved_at = NOW() WHERE id = ANY($2)",
224 &[&status, &ids],
225 )
226 .await
227 .map_err(AppError::query)?
228 } else {
229 client
230 .execute(
231 "UPDATE comments SET status = $1 WHERE id = ANY($2)",
232 &[&status, &ids],
233 )
234 .await
235 .map_err(AppError::query)?
236 };
237
238 cache::invalidate_pending_count().await;
239 for pid in post_ids {
240 cache::invalidate_comments_by_post(pid).await;
241 }
242
243 Ok(BatchStatusResponse {
244 success: true,
245 updated_count: result as i64,
246 message: format!("已更新 {} 条评论", result),
247 })
248 }
249 #[cfg(not(feature = "server"))]
250 unreachable!()
251}