feat: implement retrieval of posts by semantic ID and update related use cases

This commit is contained in:
SquidSpirit 2025-10-12 15:42:48 +08:00
parent 6efd941395
commit 43582af2a5
10 changed files with 133 additions and 27 deletions

View File

@ -15,8 +15,9 @@ use crate::{
create_label_use_case::CreateLabelUseCase, create_post_use_case::CreatePostUseCase,
get_all_labels_use_case::GetAllLabelsUseCase,
get_all_post_info_use_case::GetAllPostInfoUseCase,
get_full_post_use_case::GetFullPostUseCase, update_label_use_case::UpdateLabelUseCase,
update_post_use_case::UpdatePostUseCase,
get_post_by_id_use_case::GetPostByIdUseCase,
get_post_by_sementic_id_use_case::GetPostBySemanticIdUseCase,
update_label_use_case::UpdateLabelUseCase, update_post_use_case::UpdatePostUseCase,
},
},
};
@ -34,9 +35,9 @@ pub trait PostController: Send + Sync {
user_id: Option<i32>,
) -> Result<Vec<PostInfoResponseDto>, PostError>;
async fn get_post_by_id(
async fn get_post_by_id_or_semantic_id(
&self,
id: i32,
id_or_semantic_id: &str,
user_id: Option<i32>,
) -> Result<PostResponseDto, PostError>;
@ -69,7 +70,8 @@ pub trait PostController: Send + Sync {
pub struct PostControllerImpl {
get_all_post_info_use_case: Arc<dyn GetAllPostInfoUseCase>,
get_full_post_use_case: Arc<dyn GetFullPostUseCase>,
get_post_by_id_use_case: Arc<dyn GetPostByIdUseCase>,
get_post_by_semantic_id_use_case: Arc<dyn GetPostBySemanticIdUseCase>,
create_post_use_case: Arc<dyn CreatePostUseCase>,
update_post_use_case: Arc<dyn UpdatePostUseCase>,
create_label_use_case: Arc<dyn CreateLabelUseCase>,
@ -80,7 +82,8 @@ pub struct PostControllerImpl {
impl PostControllerImpl {
pub fn new(
get_all_post_info_use_case: Arc<dyn GetAllPostInfoUseCase>,
get_full_post_use_case: Arc<dyn GetFullPostUseCase>,
get_post_by_id_use_case: Arc<dyn GetPostByIdUseCase>,
get_post_by_semantic_id_use_case: Arc<dyn GetPostBySemanticIdUseCase>,
create_post_use_case: Arc<dyn CreatePostUseCase>,
update_post_use_case: Arc<dyn UpdatePostUseCase>,
create_label_use_case: Arc<dyn CreateLabelUseCase>,
@ -89,7 +92,8 @@ impl PostControllerImpl {
) -> Self {
Self {
get_all_post_info_use_case,
get_full_post_use_case,
get_post_by_id_use_case,
get_post_by_semantic_id_use_case,
create_post_use_case,
update_post_use_case,
create_label_use_case,
@ -97,6 +101,29 @@ impl PostControllerImpl {
get_all_labels_use_case,
}
}
async fn get_post_by_id(
&self,
id: i32,
user_id: Option<i32>,
) -> Result<PostResponseDto, PostError> {
let result = self.get_post_by_id_use_case.execute(id, user_id).await;
result.map(PostResponseDto::from)
}
async fn get_post_by_semantic_id(
&self,
semantic_id: &str,
user_id: Option<i32>,
) -> Result<PostResponseDto, PostError> {
let result = self
.get_post_by_semantic_id_use_case
.execute(semantic_id, user_id)
.await;
result.map(PostResponseDto::from)
}
}
#[async_trait]
@ -121,14 +148,17 @@ impl PostController for PostControllerImpl {
})
}
async fn get_post_by_id(
async fn get_post_by_id_or_semantic_id(
&self,
id: i32,
id_or_semantic_id: &str,
user_id: Option<i32>,
) -> Result<PostResponseDto, PostError> {
let result = self.get_full_post_use_case.execute(id, user_id).await;
result.map(PostResponseDto::from)
if let Ok(id) = id_or_semantic_id.parse::<i32>() {
self.get_post_by_id(id, user_id).await
} else {
let semantic_id = id_or_semantic_id;
self.get_post_by_semantic_id(semantic_id, user_id).await
}
}
async fn create_label(

View File

@ -14,4 +14,5 @@ pub trait PostDbService: Send + Sync {
async fn get_post_by_id(&self, id: i32) -> Result<PostMapper, PostError>;
async fn create_post(&self, post: PostMapper, label_ids: &[i32]) -> Result<i32, PostError>;
async fn update_post(&self, post: PostMapper, label_ids: &[i32]) -> Result<(), PostError>;
async fn get_id_by_semantic_id(&self, semantic_id: &str) -> Result<i32, PostError>;
}

View File

@ -84,4 +84,8 @@ impl PostRepository for PostRepositoryImpl {
.update_post(post_mapper, label_ids)
.await
}
async fn get_id_by_semantic_id(&self, semantic_id: &str) -> Result<i32, PostError> {
self.post_db_service.get_id_by_semantic_id(semantic_id).await
}
}

View File

@ -11,4 +11,5 @@ pub trait PostRepository: Send + Sync {
async fn get_post_by_id(&self, id: i32) -> Result<Post, PostError>;
async fn create_post(&self, post: Post, label_ids: &[i32]) -> Result<i32, PostError>;
async fn update_post(&self, post: Post, label_ids: &[i32]) -> Result<(), PostError>;
async fn get_id_by_semantic_id(&self, semantic_id: &str) -> Result<i32, PostError>;
}

View File

@ -2,6 +2,7 @@ pub mod create_label_use_case;
pub mod create_post_use_case;
pub mod get_all_labels_use_case;
pub mod get_all_post_info_use_case;
pub mod get_full_post_use_case;
pub mod get_post_by_id_use_case;
pub mod get_post_by_sementic_id_use_case;
pub mod update_label_use_case;
pub mod update_post_use_case;

View File

@ -8,7 +8,7 @@ use crate::{
};
#[async_trait]
pub trait GetFullPostUseCase: Send + Sync {
pub trait GetPostByIdUseCase: Send + Sync {
async fn execute(&self, id: i32, user_id: Option<i32>) -> Result<Post, PostError>;
}
@ -23,7 +23,7 @@ impl GetFullPostUseCaseImpl {
}
#[async_trait]
impl GetFullPostUseCase for GetFullPostUseCaseImpl {
impl GetPostByIdUseCase for GetFullPostUseCaseImpl {
async fn execute(&self, id: i32, user_id: Option<i32>) -> Result<Post, PostError> {
let post = self.post_repository.get_post_by_id(id).await?;

View File

@ -0,0 +1,45 @@
use std::sync::Arc;
use async_trait::async_trait;
use crate::{
application::{
error::post_error::PostError, gateway::post_repository::PostRepository,
use_case::get_post_by_id_use_case::GetPostByIdUseCase,
},
domain::entity::post::Post,
};
#[async_trait]
pub trait GetPostBySemanticIdUseCase: Send + Sync {
async fn execute(&self, semantic_id: &str, user_id: Option<i32>) -> Result<Post, PostError>;
}
pub struct GetPostIdBySemanticIdUseCaseImpl {
post_repository: Arc<dyn PostRepository>,
get_post_by_id_use_case: Arc<dyn GetPostByIdUseCase>,
}
impl GetPostIdBySemanticIdUseCaseImpl {
pub fn new(
post_repository: Arc<dyn PostRepository>,
get_post_by_id_use_case: Arc<dyn GetPostByIdUseCase>,
) -> Self {
Self {
post_repository,
get_post_by_id_use_case,
}
}
}
#[async_trait]
impl GetPostBySemanticIdUseCase for GetPostIdBySemanticIdUseCaseImpl {
async fn execute(&self, semantic_id: &str, user_id: Option<i32>) -> Result<Post, PostError> {
let id = self
.post_repository
.get_id_by_semantic_id(semantic_id)
.await?;
self.get_post_by_id_use_case.execute(id, user_id).await
}
}

View File

@ -307,4 +307,23 @@ impl PostDbService for PostDbServiceImpl {
Ok(())
}
async fn get_id_by_semantic_id(&self, semantic_id: &str) -> Result<i32, PostError> {
let id = sqlx::query_scalar!(
r#"
SELECT id
FROM post
WHERE semantic_id = $1 AND deleted_time IS NULL
"#,
semantic_id,
)
.fetch_optional(&self.db_pool)
.await
.map_err(|e| PostError::Unexpected(DatabaseError(e).into()))?;
match id {
Some(id) => Ok(id),
None => Err(PostError::NotFound),
}
}
}

View File

@ -11,8 +11,8 @@ use crate::{
get,
path = "/post/{id}",
tag = "post",
summary = "Get post by ID",
description = "Only authenticated users can access unpublished posts.",
summary = "Get post by ID or semantic ID",
description = "Only authenticated users can access unpublished posts. Accepts either numeric ID or semantic ID.",
responses (
(status = 200, body = PostResponseDto),
(status = 404, description = "Post not found")
@ -20,12 +20,12 @@ use crate::{
)]
pub async fn get_post_by_id_handler(
post_controller: web::Data<dyn PostController>,
path: web::Path<i32>,
path: web::Path<String>,
user_id: Option<UserId>,
) -> impl Responder {
let id = path.into_inner();
let id_or_semantic_id = path.into_inner();
let result = post_controller
.get_post_by_id(id, user_id.map(|id| id.get()))
.get_post_by_id_or_semantic_id(&id_or_semantic_id, user_id.map(|id| id.get()))
.await;
match result {

View File

@ -36,13 +36,12 @@ use post::{
},
},
application::use_case::{
create_label_use_case::CreateLabelUseCaseImpl,
create_post_use_case::CreatePostUseCaseImpl,
create_label_use_case::CreateLabelUseCaseImpl, create_post_use_case::CreatePostUseCaseImpl,
get_all_labels_use_case::GetAllLabelsUseCaseImpl,
get_all_post_info_use_case::GetAllPostInfoUseCaseImpl,
get_full_post_use_case::GetFullPostUseCaseImpl,
update_label_use_case::UpdateLabelUseCaseImpl,
update_post_use_case::UpdatePostUseCaseImpl,
get_post_by_id_use_case::GetFullPostUseCaseImpl,
get_post_by_sementic_id_use_case::GetPostIdBySemanticIdUseCaseImpl,
update_label_use_case::UpdateLabelUseCaseImpl, update_post_use_case::UpdatePostUseCaseImpl,
},
framework::db::{
label_db_service_impl::LabelDbServiceImpl, post_db_service_impl::PostDbServiceImpl,
@ -97,7 +96,12 @@ impl Container {
let get_all_post_info_use_case =
Arc::new(GetAllPostInfoUseCaseImpl::new(post_repository.clone()));
let get_full_post_use_case = Arc::new(GetFullPostUseCaseImpl::new(post_repository.clone()));
let get_post_by_id_use_case =
Arc::new(GetFullPostUseCaseImpl::new(post_repository.clone()));
let get_post_by_semantic_id_use_case = Arc::new(GetPostIdBySemanticIdUseCaseImpl::new(
post_repository.clone(),
get_post_by_id_use_case.clone(),
));
let create_post_use_case = Arc::new(CreatePostUseCaseImpl::new(post_repository.clone()));
let update_post_use_case = Arc::new(UpdatePostUseCaseImpl::new(post_repository.clone()));
let create_label_use_case = Arc::new(CreateLabelUseCaseImpl::new(label_repository.clone()));
@ -107,7 +111,8 @@ impl Container {
let post_controller = Arc::new(PostControllerImpl::new(
get_all_post_info_use_case,
get_full_post_use_case,
get_post_by_id_use_case,
get_post_by_semantic_id_use_case,
create_post_use_case,
update_post_use_case,
create_label_use_case,