feat: Implement merge person functionality with associated request and repository methods
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
|
Json, Router,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::{get, post, put},
|
routing::{get, post, put},
|
||||||
Json, Router,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -12,8 +12,8 @@ use crate::{
|
|||||||
error::ApiError,
|
error::ApiError,
|
||||||
middleware::auth::UserId,
|
middleware::auth::UserId,
|
||||||
schema::{
|
schema::{
|
||||||
AssignFaceRequest, CreatePersonRequest, FaceRegionResponse, PersonResponse,
|
AssignFaceRequest, CreatePersonRequest, FaceRegionResponse, MergePersonRequest,
|
||||||
SharePersonRequest, UpdatePersonRequest,
|
PersonResponse, SharePersonRequest, UpdatePersonRequest,
|
||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
@@ -23,11 +23,13 @@ pub fn people_routes() -> Router<AppState> {
|
|||||||
.route("/", get(list_people).post(create_person))
|
.route("/", get(list_people).post(create_person))
|
||||||
.route(
|
.route(
|
||||||
"/{person_id}",
|
"/{person_id}",
|
||||||
get(get_person)
|
get(get_person).put(update_person).delete(delete_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> {
|
pub fn face_routes() -> Router<AppState> {
|
||||||
@@ -53,10 +55,7 @@ async fn get_person(
|
|||||||
UserId(user_id): UserId,
|
UserId(user_id): UserId,
|
||||||
Path(person_id): Path<Uuid>,
|
Path(person_id): Path<Uuid>,
|
||||||
) -> Result<Json<PersonResponse>, ApiError> {
|
) -> Result<Json<PersonResponse>, ApiError> {
|
||||||
let person = state
|
let person = state.person_service.get_person(person_id, user_id).await?;
|
||||||
.person_service
|
|
||||||
.get_person(person_id, user_id)
|
|
||||||
.await?;
|
|
||||||
Ok(Json(PersonResponse::from(person)))
|
Ok(Json(PersonResponse::from(person)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,4 +148,17 @@ async fn assign_face_to_person(
|
|||||||
.assign_face_to_person(face_id, payload.person_id, user_id)
|
.assign_face_to_person(face_id, payload.person_id, user_id)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Json(FaceRegionResponse::from(face)))
|
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 album: AlbumResponse,
|
||||||
pub media: Vec<MediaResponse>,
|
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)
|
.remove_share(person_id, target_user_id)
|
||||||
.await
|
.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 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 update_person_id(&self, face_region_id: Uuid, person_id: Uuid) -> CoreResult<()>;
|
||||||
async fn delete(&self, face_region_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]
|
#[async_trait]
|
||||||
|
|||||||
@@ -106,6 +106,13 @@ pub trait PersonService: Send + Sync {
|
|||||||
target_user_id: Uuid,
|
target_user_id: Uuid,
|
||||||
owner_id: Uuid,
|
owner_id: Uuid,
|
||||||
) -> CoreResult<()>;
|
) -> CoreResult<()>;
|
||||||
|
|
||||||
|
async fn merge_people(
|
||||||
|
&self,
|
||||||
|
target_person_id: Uuid,
|
||||||
|
source_person_id: Uuid,
|
||||||
|
user_id: Uuid,
|
||||||
|
) -> CoreResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
use async_trait::async_trait;
|
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 sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -125,4 +129,20 @@ impl FaceRegionRepository for PostgresFaceRegionRepository {
|
|||||||
.map_err(|e| CoreError::Database(e.to_string()))?;
|
.map_err(|e| CoreError::Database(e.to_string()))?;
|
||||||
Ok(())
|
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