From 441060e7c2daf5216b8b96900510b0d2ef52fe86 Mon Sep 17 00:00:00 2001 From: xfy Date: Mon, 8 Jun 2026 16:44:38 +0800 Subject: [PATCH] refactor: extract tag operations into api/tags.rs --- src/api/tags.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/api/tags.rs diff --git a/src/api/tags.rs b/src/api/tags.rs new file mode 100644 index 0000000..eb0f567 --- /dev/null +++ b/src/api/tags.rs @@ -0,0 +1,87 @@ +#![allow(clippy::unused_unit, deprecated, unused_imports)] + +use dioxus::prelude::*; + +#[cfg(feature = "server")] +use crate::api::utils::query_error; + +#[cfg(feature = "server")] +pub async fn set_post_tags( + client: &tokio_postgres::Client, + post_id: i32, + tags: &[String], +) -> Result<(), ServerFnError> { + // Remove existing tags + client + .execute("DELETE FROM post_tags WHERE post_id = $1", &[&post_id]) + .await + .map_err(|e| { + tracing::error!("delete tag links failed: {:?}", e); + ServerFnError::new(format!("删除标签关联失败: {}", e)) + })?; + + for tag_name in tags { + let tag_name = tag_name.trim(); + if tag_name.is_empty() { + continue; + } + + // Insert or get tag + let tag_id: i32 = { + let row = client + .query_opt( + "INSERT INTO tags (name) VALUES ($1) ON CONFLICT (name) DO NOTHING RETURNING id", + &[&tag_name], + ) + .await + .map_err(|e| { + tracing::error!("create tag failed: {:?}", e); + ServerFnError::new(format!("创建标签失败: {}", e)) + })?; + + match row { + Some(r) => r.get(0), + None => { + // Tag already exists, fetch its id + let row = client + .query_opt("SELECT id FROM tags WHERE name = $1", &[&tag_name]) + .await + .map_err(|e| { + tracing::error!("query tag failed: {:?}", e); + ServerFnError::new(format!("查询标签失败: {}", e)) + })?; + row.map(|r| r.get(0)) + .ok_or_else(|| ServerFnError::new(format!("标签不存在: {}", tag_name)))? + } + } + }; + + client + .execute( + "INSERT INTO post_tags (post_id, tag_id) VALUES ($1, $2)", + &[&post_id, &tag_id], + ) + .await + .map_err(|e| { + tracing::error!("link tag failed: {:?}", e); + ServerFnError::new(format!("关联标签失败: {}", e)) + })?; + } + + Ok(()) +} + +#[cfg(feature = "server")] +pub async fn get_post_tags(client: &tokio_postgres::Client, post_id: i32) -> Vec { + let rows = client + .query( + "SELECT t.name FROM tags t JOIN post_tags pt ON t.id = pt.tag_id WHERE pt.post_id = $1 ORDER BY t.name", + &[&post_id], + ) + .await; + + match rows { + Ok(rows) => rows.iter().map(|r| r.get(0)).collect(), + Err(_) => vec![], + } +}