From cade85d576662a595773fdd7313a8d73d3146392 Mon Sep 17 00:00:00 2001 From: brian Date: Sun, 22 Jun 2025 18:27:54 +0800 Subject: [PATCH] feat: tag api --- src/api/mod.rs | 3 +- src/api/tag.rs | 170 +++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/model/http_body/mod.rs | 3 +- src/model/http_body/tag.rs | 14 +++ 5 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 src/api/tag.rs create mode 100644 src/model/http_body/tag.rs diff --git a/src/api/mod.rs b/src/api/mod.rs index 03d6eae..cb1aa61 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,2 +1,3 @@ pub mod book; -pub mod category; \ No newline at end of file +pub mod category; +pub mod tag; \ No newline at end of file diff --git a/src/api/tag.rs b/src/api/tag.rs new file mode 100644 index 0000000..e129107 --- /dev/null +++ b/src/api/tag.rs @@ -0,0 +1,170 @@ +use axum::routing::{get, post}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + Json, Router, +}; +use axum_macros::debug_handler; + +use crate::middleware::auth::Claims; +use crate::model::db::tag::ActiveModel as TagActiveModel; +use crate::model::db::tag::Column as TagColumn; +use crate::model::db::tag::Model as TagModel; +use crate::model::db::prelude::Tag; +use crate::model::http_body::tag::{TagInfo, TagItem}; +use crate::model::http_body::common::SimpleResponse; +use crate::AppState; +use sea_orm::sqlx::types::chrono::Local; +use sea_orm::{entity::*, query::*}; +use sea_orm::{ColumnTrait}; + +pub fn get_nest_handlers() -> Router { + Router::new() + .route("/{id}/update",post(update_tag)) + .route("/{id}",get(get_tag_by_id)) + .route("/", post(create_tag).get(get_all_tags)) +} + +// handlers +// +#[debug_handler] +async fn get_all_tags( + state: State, + claims: Claims, +) -> Result>, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let all_tags = Tag::find() + .filter(TagColumn::Uid.eq(uid)) + .all(&state.conn) + .await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + + let mut tags: Vec = Vec::new(); + for b in all_tags { + let tag_resp = TagItem { + id: b.id.into(), + name: b.name, + }; + tags.push(tag_resp); + } + Ok(Json(tags)) +} + +#[debug_handler] +async fn get_tag_by_id( + Path(id): Path, + state: State, + claims: Claims, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let tag_query = Tag::find() + .filter(TagColumn::Uid.eq(uid)) + .filter(TagColumn::Id.eq(id)) + .one(&state.conn) + .await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + + let tag_resp: TagItem; + match tag_query { + Some(b) => { + tag_resp = TagItem { + id: b.id.into(), + name: b.name, + }; + } + _ => { + return Err((StatusCode::NOT_FOUND, "not_found".to_string())); + } + } + Ok(Json(tag_resp)) +} + +#[debug_handler] +async fn create_tag( + state: State, + claims: Claims, + Json(payload): Json, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + + let tag = TagActiveModel { + name: Set(payload.name.clone().to_owned()), + uid: Set(uid.to_owned()), + ..Default::default() + }; + + let res = Tag::insert(tag).exec(&state.conn).await; + let mut err_code: i64 = 0; + let mut msg: String; + match res { + Ok(_) => { + err_code = 0; + msg = "ok".to_owned(); + } + Err(e) => { + err_code = 0; + msg = e.to_string(); + } + } + + let resp = SimpleResponse { + code: err_code, + message: msg, + }; + + Ok(Json(resp)) +} + +#[debug_handler] +async fn update_tag( + Path(id): Path, + state: State, + claims: Claims, + Json(payload): Json, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + + let exist_tag = Tag::find() + .filter(TagColumn::Uid.eq(uid)) + .filter(TagColumn::Id.eq(id)) + .one(&state.conn) + .await; + let tag: TagModel; + let mut resp = SimpleResponse { + code: 0, + message: "ok".to_owned(), + }; + match exist_tag { + Ok(b) => match b { + Some(bk) => { + tag = bk; + } + _ => return Err((StatusCode::NOT_FOUND, "not_found".to_string())), + }, + Err(_) => { + resp.code = 1; + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "connection_error".to_string(), + )); + } + } + + let mut tag_active_model: TagActiveModel = tag.into(); + tag_active_model.name = Set(payload.name.clone()); + tag_active_model.updated_at = Set(Local::now().naive_utc()); + let update_res = tag_active_model.update(&state.conn).await; + match update_res { + Ok(_) => { + resp.code = 0; + resp.message = "ok".to_owned(); + } + Err(_) => { + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "tag_update_fail".to_string(), + )); + } + } + Ok(Json(resp)) +} diff --git a/src/main.rs b/src/main.rs index 6155d09..9d64f64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,6 +107,7 @@ async fn start_server(config: &Config) { let app = Router::new() .nest("/api/v1/book", api::book::get_nest_handlers()) .nest("/api/v1/category", api::category::get_nested_handlers()) + .nest("/api/v1/tag", api::tag::get_nest_handlers()) .with_state(state) .layer(global_layer); let host = config.service.host.clone(); diff --git a/src/model/http_body/mod.rs b/src/model/http_body/mod.rs index 9a4a27c..ef2967f 100644 --- a/src/model/http_body/mod.rs +++ b/src/model/http_body/mod.rs @@ -1,3 +1,4 @@ pub mod book; pub mod common; -pub mod category; \ No newline at end of file +pub mod category; +pub mod tag; \ No newline at end of file diff --git a/src/model/http_body/tag.rs b/src/model/http_body/tag.rs new file mode 100644 index 0000000..ab143e4 --- /dev/null +++ b/src/model/http_body/tag.rs @@ -0,0 +1,14 @@ +use serde::{Serialize, Deserialize}; +use super::common::{number_stringify, OptionalI64}; + +#[derive(Serialize)] +pub struct TagItem { + #[serde(with="number_stringify")] + pub id: OptionalI64, + pub name: String, +} + +#[derive(Serialize,Deserialize)] +pub struct TagInfo { + pub name: String, +}