refactor(api): 为 Response 类型添加构造器,消除 51 处样板
CreatePostResponse 新增 err()/ok()/ok_msg() 构造器,消除 posts/
下 34 处 { success, message, post_id, slug } 字面量构造。
CommentResponse 新增 error()/ok()/created() 构造器,消除 comments/
下 17 处 { success, message, error_code, comment_id, ... } 字面量构造。
构造器只在 server function body 内调用,WASM 端因 cfg gate 剥离了
调用点,故加 #[cfg_attr(not(feature="server"), allow(dead_code))]。
行为完全不变(505 测试全过),代码量净减约 300 行样板。
This commit is contained in:
parent
16caa7dd9e
commit
22750ef89e
@ -38,71 +38,29 @@ pub async fn create_comment(
|
||||
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_comment_limit(&ip) {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: msg,
|
||||
error_code: Some("rate_limited".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("rate_limited", msg));
|
||||
}
|
||||
}
|
||||
|
||||
// 蜜罐字段二次校验:禁用 JS 的机器人可能绕过前端拦截,这里作为服务端防线。
|
||||
if let Err(e) = validate_comment_honeypot(&honeypot) {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: e,
|
||||
error_code: Some("spam_detected".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("spam_detected", e));
|
||||
}
|
||||
|
||||
// 依次校验昵称、邮箱、网址与评论内容。
|
||||
if let Err(e) = validate_comment_name(&author_name) {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: e,
|
||||
error_code: Some("invalid_input".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("invalid_input", e));
|
||||
}
|
||||
if let Err(e) = validate_comment_email(&author_email) {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: e,
|
||||
error_code: Some("invalid_input".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("invalid_input", e));
|
||||
}
|
||||
if let Some(ref url) = author_url {
|
||||
if let Err(e) = validate_comment_url(url) {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: e,
|
||||
error_code: Some("invalid_input".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("invalid_input", e));
|
||||
}
|
||||
}
|
||||
if let Err(e) = validate_comment_content(&content_md) {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: e,
|
||||
error_code: Some("invalid_input".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("invalid_input", e));
|
||||
}
|
||||
|
||||
let mut client = get_conn().await.map_err(AppError::db_conn)?;
|
||||
@ -118,27 +76,13 @@ pub async fn create_comment(
|
||||
|
||||
match post_row {
|
||||
None => {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "文章不存在".to_string(),
|
||||
error_code: Some("post_not_found".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("post_not_found", "文章不存在".to_string()));
|
||||
}
|
||||
Some(row) => {
|
||||
let status: String = row.get("status");
|
||||
let deleted_at: Option<chrono::DateTime<chrono::Utc>> = row.get("deleted_at");
|
||||
if status != "published" || deleted_at.is_some() {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "文章不存在".to_string(),
|
||||
error_code: Some("post_not_found".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("post_not_found", "文章不存在".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,14 +100,7 @@ pub async fn create_comment(
|
||||
|
||||
match parent_row {
|
||||
None => {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "父评论不存在".to_string(),
|
||||
error_code: Some("parent_not_found".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("parent_not_found", "父评论不存在".to_string()));
|
||||
}
|
||||
Some(row) => {
|
||||
let parent_post_id: i32 = row.get("post_id");
|
||||
@ -171,36 +108,15 @@ pub async fn create_comment(
|
||||
let parent_depth: i32 = row.get("depth");
|
||||
|
||||
if parent_post_id != post_id {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "父评论不存在".to_string(),
|
||||
error_code: Some("parent_not_found".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("parent_not_found", "父评论不存在".to_string()));
|
||||
}
|
||||
if parent_status != "approved" {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "父评论未通过审核".to_string(),
|
||||
error_code: Some("parent_not_approved".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("parent_not_approved", "父评论未通过审核".to_string()));
|
||||
}
|
||||
|
||||
depth = parent_depth + 1;
|
||||
if depth > 20 {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "评论嵌套层级过深".to_string(),
|
||||
error_code: Some("too_deep".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("too_deep", "评论嵌套层级过深".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -259,14 +175,7 @@ pub async fn create_comment(
|
||||
if dup.is_some() {
|
||||
// 重复:回滚(释放 advisory 锁)后返回。
|
||||
tx.rollback().await.ok();
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "请勿重复提交".to_string(),
|
||||
error_code: Some("duplicate".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("duplicate", "请勿重复提交".to_string()));
|
||||
}
|
||||
|
||||
// 插入评论,默认状态为 pending,等待管理员审核。
|
||||
@ -305,14 +214,7 @@ pub async fn create_comment(
|
||||
cache::invalidate_comments_by_post(post_id).await;
|
||||
cache::invalidate_pending_count().await;
|
||||
|
||||
Ok(CommentResponse {
|
||||
success: true,
|
||||
message: "评论已提交,等待审核".to_string(),
|
||||
error_code: None,
|
||||
comment_id: Some(comment_id),
|
||||
avatar_url: Some(avatar_url),
|
||||
depth: Some(depth),
|
||||
})
|
||||
Ok(CommentResponse::created("评论已提交,等待审核".to_string(), comment_id, avatar_url, depth))
|
||||
}
|
||||
#[cfg(not(feature = "server"))]
|
||||
unreachable!()
|
||||
|
||||
@ -23,6 +23,52 @@ pub struct CommentResponse {
|
||||
pub depth: Option<i32>,
|
||||
}
|
||||
|
||||
/// 这些构造器只在 server function body 内调用,WASM 端因 cfg gate 剥离了调用点,
|
||||
/// 故对非 server 构建允许 dead_code。
|
||||
#[cfg_attr(not(feature = "server"), allow(dead_code))]
|
||||
impl CommentResponse {
|
||||
/// 构造失败响应,携带错误码(comment_id / avatar_url / depth 均为 None)。
|
||||
pub fn error(error_code: &str, message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
message: message.into(),
|
||||
error_code: Some(error_code.into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 构造成功响应(无关联数据,用于审核/删除等操作)。
|
||||
pub fn ok(message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: true,
|
||||
message: message.into(),
|
||||
error_code: None,
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 构造成功响应,携带新评论 id / 头像 / 深度(用于创建评论)。
|
||||
pub fn created(
|
||||
message: impl Into<String>,
|
||||
comment_id: i64,
|
||||
avatar_url: impl Into<String>,
|
||||
depth: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
success: true,
|
||||
message: message.into(),
|
||||
error_code: None,
|
||||
comment_id: Some(comment_id),
|
||||
avatar_url: Some(avatar_url.into()),
|
||||
depth: Some(depth),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 评论树响应:包含文章下的全部已审核评论。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CommentTreeResponse {
|
||||
|
||||
@ -34,14 +34,7 @@ pub async fn approve_comment(id: i64) -> Result<CommentResponse, ServerFnError>
|
||||
let post_id: i32 = match row {
|
||||
Some(r) => r.get("post_id"),
|
||||
None => {
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "评论不存在".to_string(),
|
||||
error_code: Some("not_found".into()),
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
});
|
||||
return Ok(CommentResponse::error("not_found", "评论不存在".to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
@ -72,14 +65,7 @@ pub async fn approve_comment(id: i64) -> Result<CommentResponse, ServerFnError>
|
||||
cache::invalidate_comments_by_post(post_id).await;
|
||||
cache::invalidate_pending_count().await;
|
||||
|
||||
Ok(CommentResponse {
|
||||
success: true,
|
||||
message: "已通过".to_string(),
|
||||
error_code: None,
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
})
|
||||
Ok(CommentResponse::ok("已通过".to_string()))
|
||||
}
|
||||
#[cfg(not(feature = "server"))]
|
||||
unreachable!()
|
||||
@ -127,14 +113,7 @@ pub async fn spam_comment(id: i64) -> Result<CommentResponse, ServerFnError> {
|
||||
cache::invalidate_pending_count().await;
|
||||
}
|
||||
|
||||
Ok(CommentResponse {
|
||||
success: true,
|
||||
message: "已标记为垃圾".to_string(),
|
||||
error_code: None,
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
})
|
||||
Ok(CommentResponse::ok("已标记为垃圾".to_string()))
|
||||
}
|
||||
#[cfg(not(feature = "server"))]
|
||||
unreachable!()
|
||||
@ -179,14 +158,7 @@ pub async fn trash_comment(id: i64) -> Result<CommentResponse, ServerFnError> {
|
||||
cache::invalidate_pending_count().await;
|
||||
}
|
||||
|
||||
Ok(CommentResponse {
|
||||
success: true,
|
||||
message: "已删除".to_string(),
|
||||
error_code: None,
|
||||
comment_id: None,
|
||||
avatar_url: None,
|
||||
depth: None,
|
||||
})
|
||||
Ok(CommentResponse::ok("已删除".to_string()))
|
||||
}
|
||||
#[cfg(not(feature = "server"))]
|
||||
unreachable!()
|
||||
|
||||
@ -37,22 +37,12 @@ pub async fn create_post(
|
||||
|
||||
// 标题不能为空。
|
||||
if title.trim().is_empty() {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "标题不能为空".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("标题不能为空".to_string()));
|
||||
}
|
||||
|
||||
// 内容不能为空。
|
||||
if content_md.trim().is_empty() {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "内容不能为空".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("内容不能为空".to_string()));
|
||||
}
|
||||
|
||||
// 确定基础 slug:用户传入时校验格式,否则由标题生成。
|
||||
@ -60,12 +50,7 @@ pub async fn create_post(
|
||||
Some(ref s) if !s.trim().is_empty() => {
|
||||
let s = s.trim();
|
||||
if !crate::api::slug::is_valid_slug(s) {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "slug 格式无效,只能包含字母、数字、连字符和下划线".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("slug 格式无效,只能包含字母、数字、连字符和下划线".to_string()));
|
||||
}
|
||||
s.to_string()
|
||||
}
|
||||
@ -157,21 +142,11 @@ pub async fn create_post(
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "创建成功".to_string(),
|
||||
post_id: Some(post_id),
|
||||
slug: Some(final_slug),
|
||||
})
|
||||
Ok(CreatePostResponse::ok("创建成功".to_string(), post_id, final_slug))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,12 +38,7 @@ pub async fn delete_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErr
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let Some(slug_row) = slug_row else {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不存在".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不存在".to_string()));
|
||||
};
|
||||
let slug: String = slug_row.get(0);
|
||||
|
||||
@ -66,12 +61,7 @@ pub async fn delete_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErr
|
||||
.map_err(AppError::tx)?;
|
||||
|
||||
if result == 0 {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不存在".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不存在".to_string()));
|
||||
}
|
||||
|
||||
tx.commit().await.map_err(AppError::tx)?;
|
||||
@ -87,21 +77,11 @@ pub async fn delete_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErr
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "删除成功".to_string(),
|
||||
post_id: Some(post_id),
|
||||
slug: Some(slug),
|
||||
})
|
||||
Ok(CreatePostResponse::ok("删除成功".to_string(), post_id, slug))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,12 +178,7 @@ pub async fn rebuild_post_content_html(
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let Some(row) = row else {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不存在".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不存在".to_string()));
|
||||
};
|
||||
|
||||
let content_md: String = row.get(0);
|
||||
@ -229,21 +224,11 @@ pub async fn rebuild_post_content_html(
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "重建成功".to_string(),
|
||||
post_id: Some(post_id),
|
||||
slug: Some(slug),
|
||||
})
|
||||
Ok(CreatePostResponse::ok("重建成功".to_string(), post_id, slug))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,12 +44,7 @@ pub async fn restore_post(post_id: i32) -> Result<CreatePostResponse, ServerFnEr
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let Some(row) = row else {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不在回收站".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不在回收站".to_string()));
|
||||
};
|
||||
|
||||
let current_slug: String = row.get("slug");
|
||||
@ -76,12 +71,7 @@ pub async fn restore_post(post_id: i32) -> Result<CreatePostResponse, ServerFnEr
|
||||
.map_err(AppError::tx)?;
|
||||
|
||||
if result == 0 {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不在回收站".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不在回收站".to_string()));
|
||||
}
|
||||
|
||||
tx.commit().await.map_err(AppError::tx)?;
|
||||
@ -98,22 +88,12 @@ pub async fn restore_post(post_id: i32) -> Result<CreatePostResponse, ServerFnEr
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "恢复成功".to_string(),
|
||||
post_id: Some(post_id),
|
||||
slug: Some(new_slug),
|
||||
})
|
||||
Ok(CreatePostResponse::ok("恢复成功".to_string(), post_id, new_slug))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,12 +120,7 @@ pub async fn purge_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErro
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let Some(slug_row) = slug_row else {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不在回收站".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不在回收站".to_string()));
|
||||
};
|
||||
let slug: String = slug_row.get(0);
|
||||
|
||||
@ -167,12 +142,7 @@ pub async fn purge_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErro
|
||||
.map_err(AppError::tx)?;
|
||||
|
||||
if result == 0 {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不在回收站".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不在回收站".to_string()));
|
||||
}
|
||||
|
||||
tx.commit().await.map_err(AppError::tx)?;
|
||||
@ -188,22 +158,12 @@ pub async fn purge_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErro
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "彻底删除成功".to_string(),
|
||||
post_id: Some(post_id),
|
||||
slug: Some(slug),
|
||||
})
|
||||
Ok(CreatePostResponse::ok("彻底删除成功".to_string(), post_id, slug))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,12 +175,7 @@ pub async fn batch_restore_posts(post_ids: Vec<i32>) -> Result<CreatePostRespons
|
||||
#[cfg(feature = "server")]
|
||||
{
|
||||
if post_ids.is_empty() {
|
||||
return Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "无操作".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::ok_msg("无操作".to_string()));
|
||||
}
|
||||
|
||||
let mut client = get_conn().await.map_err(AppError::db_conn)?;
|
||||
@ -301,22 +256,12 @@ pub async fn batch_restore_posts(post_ids: Vec<i32>) -> Result<CreatePostRespons
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
}
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: format!("已恢复 {restored} 篇"),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::ok_msg(format!("已恢复 {restored} 篇")))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,12 +273,7 @@ pub async fn batch_purge_posts(post_ids: Vec<i32>) -> Result<CreatePostResponse,
|
||||
#[cfg(feature = "server")]
|
||||
{
|
||||
if post_ids.is_empty() {
|
||||
return Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "无操作".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::ok_msg("无操作".to_string()));
|
||||
}
|
||||
|
||||
let mut client = get_conn().await.map_err(AppError::db_conn)?;
|
||||
@ -406,22 +346,12 @@ pub async fn batch_purge_posts(post_ids: Vec<i32>) -> Result<CreatePostResponse,
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
}
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: format!("已彻底删除 {result}/{total} 篇"),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::ok_msg(format!("已彻底删除 {result}/{total} 篇")))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,21 +422,11 @@ pub async fn empty_trash() -> Result<CreatePostResponse, ServerFnError> {
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
}
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: format!("已清空回收站({result} 篇)"),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::ok_msg(format!("已清空回收站({result} 篇)")))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,41 @@ pub struct CreatePostResponse {
|
||||
pub slug: Option<String>,
|
||||
}
|
||||
|
||||
/// 这些构造器只在 server function body 内调用,WASM 端因 cfg gate 剥离了调用点,
|
||||
/// 故对非 server 构建允许 dead_code。
|
||||
#[cfg_attr(not(feature = "server"), allow(dead_code))]
|
||||
impl CreatePostResponse {
|
||||
/// 构造失败响应(post_id / slug 均为 None)。
|
||||
pub fn err(message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
message: message.into(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 构造成功响应,携带新文章 id 与 slug。
|
||||
pub fn ok(message: impl Into<String>, post_id: i32, slug: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: true,
|
||||
message: message.into(),
|
||||
post_id: Some(post_id),
|
||||
slug: Some(slug.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 构造成功响应(无关联 id/slug,用于批量操作)。
|
||||
pub fn ok_msg(message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: true,
|
||||
message: message.into(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 文章列表响应。
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PostListResponse {
|
||||
|
||||
@ -84,12 +84,7 @@ pub async fn update_post(
|
||||
.is_some();
|
||||
|
||||
if !exists {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不存在或无权限".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不存在或无权限".to_string()));
|
||||
}
|
||||
|
||||
// 确定基础 slug:用户传入时校验格式,否则由标题生成。
|
||||
@ -97,12 +92,7 @@ pub async fn update_post(
|
||||
Some(ref s) if !s.trim().is_empty() => {
|
||||
let s = s.trim();
|
||||
if !crate::api::slug::is_valid_slug(s) {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "slug 格式无效".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("slug 格式无效".to_string()));
|
||||
}
|
||||
s.to_string()
|
||||
}
|
||||
@ -180,12 +170,7 @@ pub async fn update_post(
|
||||
.map_err(AppError::tx)?;
|
||||
|
||||
if updated == 0 {
|
||||
return Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "文章不存在或无权限".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
});
|
||||
return Ok(CreatePostResponse::err("文章不存在或无权限".to_string()));
|
||||
}
|
||||
|
||||
let tags_cleaned = clean_tags(&tags);
|
||||
@ -226,21 +211,11 @@ pub async fn update_post(
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "更新成功".to_string(),
|
||||
post_id: Some(post_id),
|
||||
slug: Some(final_slug),
|
||||
})
|
||||
Ok(CreatePostResponse::ok("更新成功".to_string(), post_id, final_slug))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
Ok(CreatePostResponse {
|
||||
success: false,
|
||||
message: "server only".to_string(),
|
||||
post_id: None,
|
||||
slug: None,
|
||||
})
|
||||
Ok(CreatePostResponse::err("server only".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user