From 646b2db73b0b184f04fe1876c13e5f8bc3e68277 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sat, 2 Aug 2025 10:30:42 +0800 Subject: [PATCH 1/3] BLOG-105 feat: implement label management features including creation, retrieval, and updating of labels --- backend/Cargo.lock | 1 + backend/feature/post/Cargo.toml | 2 + backend/feature/post/src/adapter/delivery.rs | 3 + .../src/adapter/delivery/color_request_dto.rs | 30 ++++++ .../adapter/delivery/color_response_dto.rs | 7 ++ .../delivery/create_label_request_dto.rs | 20 ++++ .../src/adapter/delivery/post_controller.rs | 74 ++++++++++++- .../delivery/update_label_request_dto.rs | 20 ++++ backend/feature/post/src/adapter/gateway.rs | 2 + .../src/adapter/gateway/color_db_mapper.rs | 11 ++ .../src/adapter/gateway/label_db_mapper.rs | 10 ++ .../src/adapter/gateway/label_db_service.rs | 13 +++ .../adapter/gateway/label_repository_impl.rs | 50 +++++++++ .../feature/post/src/application/gateway.rs | 1 + .../application/gateway/label_repository.rs | 11 ++ .../feature/post/src/application/use_case.rs | 3 + .../use_case/create_label_use_case.rs | 30 ++++++ .../use_case/get_all_labels_use_case.rs | 30 ++++++ .../use_case/update_label_use_case.rs | 31 ++++++ .../feature/post/src/domain/entity/color.rs | 1 + .../feature/post/src/domain/entity/label.rs | 1 + backend/feature/post/src/framework/db.rs | 2 + .../src/framework/db/label_db_service_impl.rs | 102 ++++++++++++++++++ .../post/src/framework/db/label_record.rs | 19 ++++ backend/feature/post/src/framework/web.rs | 3 + .../src/framework/web/create_label_handler.rs | 35 ++++++ .../framework/web/get_all_labels_handler.rs | 28 +++++ .../post/src/framework/web/post_api_doc.rs | 10 +- .../post/src/framework/web/post_web_routes.rs | 10 +- .../src/framework/web/update_label_handler.rs | 46 ++++++++ backend/server/src/container.rs | 33 +++++- 31 files changed, 631 insertions(+), 8 deletions(-) create mode 100644 backend/feature/post/src/adapter/delivery/color_request_dto.rs create mode 100644 backend/feature/post/src/adapter/delivery/create_label_request_dto.rs create mode 100644 backend/feature/post/src/adapter/delivery/update_label_request_dto.rs create mode 100644 backend/feature/post/src/adapter/gateway/label_db_service.rs create mode 100644 backend/feature/post/src/adapter/gateway/label_repository_impl.rs create mode 100644 backend/feature/post/src/application/gateway/label_repository.rs create mode 100644 backend/feature/post/src/application/use_case/create_label_use_case.rs create mode 100644 backend/feature/post/src/application/use_case/get_all_labels_use_case.rs create mode 100644 backend/feature/post/src/application/use_case/update_label_use_case.rs create mode 100644 backend/feature/post/src/framework/db/label_db_service_impl.rs create mode 100644 backend/feature/post/src/framework/db/label_record.rs create mode 100644 backend/feature/post/src/framework/web/create_label_handler.rs create mode 100644 backend/feature/post/src/framework/web/get_all_labels_handler.rs create mode 100644 backend/feature/post/src/framework/web/update_label_handler.rs diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 678fc45..08f59a2 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2232,6 +2232,7 @@ version = "0.2.0" dependencies = [ "actix-web", "async-trait", + "auth", "chrono", "log", "serde", diff --git a/backend/feature/post/Cargo.toml b/backend/feature/post/Cargo.toml index dc63b3e..73d6eff 100644 --- a/backend/feature/post/Cargo.toml +++ b/backend/feature/post/Cargo.toml @@ -11,3 +11,5 @@ log.workspace = true serde.workspace = true sqlx.workspace = true utoipa.workspace = true + +auth.workspace = true diff --git a/backend/feature/post/src/adapter/delivery.rs b/backend/feature/post/src/adapter/delivery.rs index 344245b..c9d70c0 100644 --- a/backend/feature/post/src/adapter/delivery.rs +++ b/backend/feature/post/src/adapter/delivery.rs @@ -1,6 +1,9 @@ +pub mod color_request_dto; pub mod color_response_dto; +pub mod create_label_request_dto; pub mod label_response_dto; pub mod post_controller; pub mod post_info_query_dto; pub mod post_info_response_dto; pub mod post_response_dto; +pub mod update_label_request_dto; diff --git a/backend/feature/post/src/adapter/delivery/color_request_dto.rs b/backend/feature/post/src/adapter/delivery/color_request_dto.rs new file mode 100644 index 0000000..f6838a3 --- /dev/null +++ b/backend/feature/post/src/adapter/delivery/color_request_dto.rs @@ -0,0 +1,30 @@ +use serde::Deserialize; +use utoipa::ToSchema; + +use crate::domain::entity::color::Color; + +#[derive(Deserialize, ToSchema)] +pub struct ColorRequestDto { + #[schema(maximum = 255)] + pub red: u8, + + #[schema(maximum = 255)] + pub green: u8, + + #[schema(maximum = 255)] + pub blue: u8, + + #[schema(maximum = 255)] + pub alpha: u8, +} + +impl ColorRequestDto { + pub fn into_entity(self) -> Color { + Color { + red: self.red, + green: self.green, + blue: self.blue, + alpha: self.alpha, + } + } +} diff --git a/backend/feature/post/src/adapter/delivery/color_response_dto.rs b/backend/feature/post/src/adapter/delivery/color_response_dto.rs index 274f95a..7e64681 100644 --- a/backend/feature/post/src/adapter/delivery/color_response_dto.rs +++ b/backend/feature/post/src/adapter/delivery/color_response_dto.rs @@ -5,9 +5,16 @@ use crate::domain::entity::color::Color; #[derive(Serialize, ToSchema)] pub struct ColorResponseDto { + #[schema(maximum = 255)] pub red: u8, + + #[schema(maximum = 255)] pub green: u8, + + #[schema(maximum = 255)] pub blue: u8, + + #[schema(maximum = 255)] pub alpha: u8, } diff --git a/backend/feature/post/src/adapter/delivery/create_label_request_dto.rs b/backend/feature/post/src/adapter/delivery/create_label_request_dto.rs new file mode 100644 index 0000000..ed6f6ba --- /dev/null +++ b/backend/feature/post/src/adapter/delivery/create_label_request_dto.rs @@ -0,0 +1,20 @@ +use serde::Deserialize; +use utoipa::ToSchema; + +use crate::{adapter::delivery::color_request_dto::ColorRequestDto, domain::entity::label::Label}; + +#[derive(Deserialize, ToSchema)] +pub struct CreateLabelRequestDto { + pub name: String, + pub color: ColorRequestDto, +} + +impl CreateLabelRequestDto { + pub fn into_entity(self) -> Label { + Label { + id: -1, + name: self.name, + color: self.color.into_entity(), + } + } +} diff --git a/backend/feature/post/src/adapter/delivery/post_controller.rs b/backend/feature/post/src/adapter/delivery/post_controller.rs index 454be2a..1b94cb5 100644 --- a/backend/feature/post/src/adapter/delivery/post_controller.rs +++ b/backend/feature/post/src/adapter/delivery/post_controller.rs @@ -3,17 +3,25 @@ use std::sync::Arc; use async_trait::async_trait; use crate::{ - adapter::delivery::post_info_query_dto::PostQueryDto, + adapter::delivery::{ + create_label_request_dto::CreateLabelRequestDto, post_info_query_dto::PostQueryDto, + update_label_request_dto::UpdateLabelRequestDto, + }, application::{ error::post_error::PostError, use_case::{ + create_label_use_case::CreateLabelUseCase, + get_all_labels_use_case::GetAllLabelsUseCase, get_all_post_info_use_case::GetAllPostInfoUseCase, - get_full_post_use_case::GetFullPostUseCase, + get_full_post_use_case::GetFullPostUseCase, update_label_use_case::UpdateLabelUseCase, }, }, }; -use super::{post_info_response_dto::PostInfoResponseDto, post_response_dto::PostResponseDto}; +use super::{ + label_response_dto::LabelResponseDto, post_info_response_dto::PostInfoResponseDto, + post_response_dto::PostResponseDto, +}; #[async_trait] pub trait PostController: Send + Sync { @@ -23,21 +31,43 @@ pub trait PostController: Send + Sync { ) -> Result, PostError>; async fn get_post_by_id(&self, id: i32) -> Result; + + async fn create_label( + &self, + label: CreateLabelRequestDto, + ) -> Result; + + async fn update_label( + &self, + id: i32, + label: UpdateLabelRequestDto, + ) -> Result; + + async fn get_all_labels(&self) -> Result, PostError>; } pub struct PostControllerImpl { get_all_post_info_use_case: Arc, get_full_post_use_case: Arc, + create_label_use_case: Arc, + update_label_use_case: Arc, + get_all_labels_use_case: Arc, } impl PostControllerImpl { pub fn new( get_all_post_info_use_case: Arc, get_full_post_use_case: Arc, + create_label_use_case: Arc, + update_label_use_case: Arc, + get_all_labels_use_case: Arc, ) -> Self { Self { get_all_post_info_use_case, get_full_post_use_case, + create_label_use_case, + update_label_use_case, + get_all_labels_use_case, } } } @@ -68,4 +98,42 @@ impl PostController for PostControllerImpl { result.map(PostResponseDto::from) } + + async fn create_label( + &self, + label: CreateLabelRequestDto, + ) -> Result { + let mut label_entity = label.into_entity(); + let id = self + .create_label_use_case + .execute(label_entity.clone()) + .await?; + + label_entity.id = id; + Ok(LabelResponseDto::from(label_entity)) + } + + async fn update_label( + &self, + id: i32, + label: UpdateLabelRequestDto, + ) -> Result { + let label_entity = label.into_entity(id); + self.update_label_use_case + .execute(label_entity.clone()) + .await?; + + Ok(LabelResponseDto::from(label_entity)) + } + + async fn get_all_labels(&self) -> Result, PostError> { + let result = self.get_all_labels_use_case.execute().await; + + result.map(|labels| { + labels + .into_iter() + .map(|label| LabelResponseDto::from(label)) + .collect() + }) + } } diff --git a/backend/feature/post/src/adapter/delivery/update_label_request_dto.rs b/backend/feature/post/src/adapter/delivery/update_label_request_dto.rs new file mode 100644 index 0000000..aad9854 --- /dev/null +++ b/backend/feature/post/src/adapter/delivery/update_label_request_dto.rs @@ -0,0 +1,20 @@ +use serde::Deserialize; +use utoipa::ToSchema; + +use crate::{adapter::delivery::color_request_dto::ColorRequestDto, domain::entity::label::Label}; + +#[derive(Deserialize, ToSchema)] +pub struct UpdateLabelRequestDto { + pub name: String, + pub color: ColorRequestDto, +} + +impl UpdateLabelRequestDto { + pub fn into_entity(self, id: i32) -> Label { + Label { + id, + name: self.name, + color: self.color.into_entity(), + } + } +} diff --git a/backend/feature/post/src/adapter/gateway.rs b/backend/feature/post/src/adapter/gateway.rs index 38d8fbf..aa4960d 100644 --- a/backend/feature/post/src/adapter/gateway.rs +++ b/backend/feature/post/src/adapter/gateway.rs @@ -1,5 +1,7 @@ pub mod color_db_mapper; pub mod label_db_mapper; +pub mod label_db_service; +pub mod label_repository_impl; pub mod post_db_mapper; pub mod post_db_service; pub mod post_info_db_mapper; diff --git a/backend/feature/post/src/adapter/gateway/color_db_mapper.rs b/backend/feature/post/src/adapter/gateway/color_db_mapper.rs index 9143702..1fece3b 100644 --- a/backend/feature/post/src/adapter/gateway/color_db_mapper.rs +++ b/backend/feature/post/src/adapter/gateway/color_db_mapper.rs @@ -14,3 +14,14 @@ impl ColorMapper { } } } + +impl From for ColorMapper { + fn from(color: Color) -> Self { + let value: u32 = ((color.red as u32) << 24) + | ((color.green as u32) << 16) + | ((color.blue as u32) << 8) + | (color.alpha as u32); + + Self { value } + } +} diff --git a/backend/feature/post/src/adapter/gateway/label_db_mapper.rs b/backend/feature/post/src/adapter/gateway/label_db_mapper.rs index 6e7adf2..04da505 100644 --- a/backend/feature/post/src/adapter/gateway/label_db_mapper.rs +++ b/backend/feature/post/src/adapter/gateway/label_db_mapper.rs @@ -15,3 +15,13 @@ impl LabelMapper { } } } + +impl From