//! Who API - 身份識別與 ID 映射接口 (Video-Scoped) use axum::{ extract::State, http::StatusCode, response::Json, routing::{get, post}, Router, }; use serde::{Deserialize, Serialize}; use crate::core::db::Database; // --- Request / Response Structures --- #[derive(Debug, Deserialize)] pub struct WhoQuery { pub face_id: Option, pub speaker_id: Option, pub uuid: Option, pub chunk_id: Option, } #[derive(Debug, Deserialize)] pub struct WhoCandidatesRequest { pub query: String, pub video_uuid: Option, pub limit: Option, } #[derive(Debug, Deserialize)] pub struct DefinePersonRequest { pub uuid: String, pub identity_id: Option, pub name: String, pub face_ids: Option>, pub speaker_ids: Option>, } #[derive(Debug, Serialize)] pub struct WhoIdentity { pub identity_id: i32, pub uuid: String, pub name: String, pub tags: Option>, pub face_ids: Vec, pub speaker_ids: Vec, } // --- API Handlers --- /// GET /api/v1/who pub async fn get_who_identity( State(state): State, axum::extract::Query(query): axum::extract::Query, ) -> Result, (StatusCode, Json)> { let db = &state.db; // Priority 1: Query by Chunk (UUID + Chunk ID) if let (Some(uuid), Some(chunk_id)) = (&query.uuid, &query.chunk_id) { let info = db .get_who_info_by_chunk(uuid, chunk_id) .await .map_err(|e| { ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": e.to_string() })), ) })?; return Ok(Json(info)); } // Priority 2: List all for a specific UUID if let Some(uuid) = &query.uuid { // TODO: Implement list_all_persons(uuid) return Ok(Json(serde_json::json!({ "message": "List all pending" }))); } Err(( StatusCode::BAD_REQUEST, Json(serde_json::json!({ "error": "Missing uuid" })), )) } /// POST /api/v1/who/candidates /// Search person_identities table for n8n workflow pub async fn get_who_candidates( State(state): State, Json(req): Json, ) -> Result, (StatusCode, Json)> { let db = &state.db; let limit = req.limit.unwrap_or(20); let query_str = format!("%{}%", req.query); let results = db .search_person_candidates(&query_str, &req.video_uuid, limit) .await .map_err(|e| { ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": e.to_string() })), ) })?; // Format for n8n let response = serde_json::json!({ "query": req.query, "items": results, "total": results.len() }); Ok(Json(response)) } /// POST /api/v1/who pub async fn define_person( State(state): State, Json(req): Json, ) -> Result, (StatusCode, Json)> { let db = &state.db; let identity = db .create_or_update_person( &req.uuid, req.identity_id, req.name.clone(), req.face_ids.unwrap_or_default(), req.speaker_ids.unwrap_or_default(), ) .await .map_err(|e| { ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": e.to_string() })), ) })?; Ok(Json(identity)) } // --- Router Setup --- pub fn who_routes() -> Router { Router::new() .route("/api/v1/who", get(get_who_identity).post(define_person)) .route("/api/v1/who/candidates", post(get_who_candidates)) }