feat: Implement merge person functionality with associated request and repository methods
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
use axum::{
|
||||
Json, Router,
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::{get, post, put},
|
||||
Json, Router,
|
||||
};
|
||||
|
||||
use uuid::Uuid;
|
||||
@@ -12,8 +12,8 @@ use crate::{
|
||||
error::ApiError,
|
||||
middleware::auth::UserId,
|
||||
schema::{
|
||||
AssignFaceRequest, CreatePersonRequest, FaceRegionResponse, PersonResponse,
|
||||
SharePersonRequest, UpdatePersonRequest,
|
||||
AssignFaceRequest, CreatePersonRequest, FaceRegionResponse, MergePersonRequest,
|
||||
PersonResponse, SharePersonRequest, UpdatePersonRequest,
|
||||
},
|
||||
state::AppState,
|
||||
};
|
||||
@@ -23,11 +23,13 @@ pub fn people_routes() -> Router<AppState> {
|
||||
.route("/", get(list_people).post(create_person))
|
||||
.route(
|
||||
"/{person_id}",
|
||||
get(get_person)
|
||||
.put(update_person)
|
||||
.delete(delete_person),
|
||||
get(get_person).put(update_person).delete(delete_person),
|
||||
)
|
||||
.route("/{person_id}/share", post(share_person).delete(unshare_person))
|
||||
.route(
|
||||
"/{person_id}/share",
|
||||
post(share_person).delete(unshare_person),
|
||||
)
|
||||
.route("/{person_id}/merge", post(merge_person))
|
||||
}
|
||||
|
||||
pub fn face_routes() -> Router<AppState> {
|
||||
@@ -53,10 +55,7 @@ async fn get_person(
|
||||
UserId(user_id): UserId,
|
||||
Path(person_id): Path<Uuid>,
|
||||
) -> Result<Json<PersonResponse>, ApiError> {
|
||||
let person = state
|
||||
.person_service
|
||||
.get_person(person_id, user_id)
|
||||
.await?;
|
||||
let person = state.person_service.get_person(person_id, user_id).await?;
|
||||
Ok(Json(PersonResponse::from(person)))
|
||||
}
|
||||
|
||||
@@ -150,3 +149,16 @@ async fn assign_face_to_person(
|
||||
.await?;
|
||||
Ok(Json(FaceRegionResponse::from(face)))
|
||||
}
|
||||
|
||||
async fn merge_person(
|
||||
State(state): State<AppState>,
|
||||
UserId(user_id): UserId,
|
||||
Path(target_person_id): Path<Uuid>,
|
||||
Json(payload): Json<MergePersonRequest>,
|
||||
) -> Result<StatusCode, ApiError> {
|
||||
state
|
||||
.person_service
|
||||
.merge_people(target_person_id, payload.source_person_id, user_id)
|
||||
.await?;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
@@ -239,3 +239,8 @@ pub struct PublicAlbumBundleResponse {
|
||||
pub album: AlbumResponse,
|
||||
pub media: Vec<MediaResponse>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MergePersonRequest {
|
||||
pub source_person_id: Uuid,
|
||||
}
|
||||
|
||||
@@ -183,4 +183,36 @@ impl PersonService for PersonServiceImpl {
|
||||
.remove_share(person_id, target_user_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn merge_people(
|
||||
&self,
|
||||
target_person_id: Uuid,
|
||||
source_person_id: Uuid,
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<()> {
|
||||
if target_person_id == source_person_id {
|
||||
return Err(CoreError::Validation(
|
||||
"Cannot merge the same person".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.auth_service
|
||||
.check_permission(
|
||||
Some(user_id),
|
||||
authz::Permission::EditPerson(target_person_id),
|
||||
)
|
||||
.await?;
|
||||
self.auth_service
|
||||
.check_permission(
|
||||
Some(user_id),
|
||||
authz::Permission::EditPerson(source_person_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.face_repo
|
||||
.reassign_person(source_person_id, target_person_id)
|
||||
.await?;
|
||||
|
||||
self.person_repo.delete(source_person_id).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ pub trait FaceRegionRepository: Send + Sync {
|
||||
async fn find_by_id(&self, face_region_id: Uuid) -> CoreResult<Option<FaceRegion>>;
|
||||
async fn update_person_id(&self, face_region_id: Uuid, person_id: Uuid) -> CoreResult<()>;
|
||||
async fn delete(&self, face_region_id: Uuid) -> CoreResult<()>;
|
||||
async fn reassign_person(&self, old_person_id: Uuid, new_person_id: Uuid) -> CoreResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -106,6 +106,13 @@ pub trait PersonService: Send + Sync {
|
||||
target_user_id: Uuid,
|
||||
owner_id: Uuid,
|
||||
) -> CoreResult<()>;
|
||||
|
||||
async fn merge_people(
|
||||
&self,
|
||||
target_person_id: Uuid,
|
||||
source_person_id: Uuid,
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use libertas_core::{error::{CoreError, CoreResult}, models::FaceRegion, repositories::FaceRegionRepository};
|
||||
use libertas_core::{
|
||||
error::{CoreError, CoreResult},
|
||||
models::FaceRegion,
|
||||
repositories::FaceRegionRepository,
|
||||
};
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -125,4 +129,20 @@ impl FaceRegionRepository for PostgresFaceRegionRepository {
|
||||
.map_err(|e| CoreError::Database(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn reassign_person(&self, old_person_id: Uuid, new_person_id: Uuid) -> CoreResult<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE face_regions
|
||||
SET person_id = $1
|
||||
WHERE person_id = $2
|
||||
"#,
|
||||
new_person_id,
|
||||
old_person_id
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(|e| CoreError::Database(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user