feat: Phase 1 handover - schema migration, correction mechanism, API fixes

Schema changes: dev.chunks->dev.chunk, remove old_chunk_id/chunk_index
Correction: asr-1.json format, generate/apply scripts
API: 37/37 endpoints fixed and tested
Docs: HANDOVER_V2.0.md for M4
This commit is contained in:
Accusys
2026-05-11 07:03:22 +08:00
parent ef894a44ad
commit 39ba5ddf76
147 changed files with 19843 additions and 3053 deletions

View File

@@ -25,6 +25,8 @@ pub fn trace_agent_routes() -> Router<crate::api::server::AppState> {
struct TracesRequest {
min_faces: Option<i64>,
sort_by: Option<String>,
page: Option<i64>,
page_size: Option<i64>,
limit: Option<i64>,
min_confidence: Option<f64>,
max_confidence: Option<f64>,
@@ -49,6 +51,8 @@ struct TracesResponse {
file_uuid: String,
total_traces: i64,
total_faces: i64,
page: i64,
page_size: i64,
traces: Vec<TraceInfo>,
}
@@ -59,7 +63,11 @@ async fn list_traces_sorted(
) -> Result<Json<TracesResponse>, (StatusCode, String)> {
let min_faces = req.min_faces.unwrap_or(1);
let sort = req.sort_by.as_deref().unwrap_or("first_appearance");
let limit = req.limit.unwrap_or(500);
let page = req.page.unwrap_or(1).max(1);
let page_size = req.page_size.unwrap_or(50).max(1).min(500);
let hard_limit = req.limit.unwrap_or(500);
let effective_limit = hard_limit.min(page_size);
let db_offset = (page - 1) * page_size;
let min_confidence = req.min_confidence.unwrap_or(0.0);
let max_confidence = req.max_confidence.unwrap_or(1.0);
@@ -92,11 +100,11 @@ async fn list_traces_sorted(
AVG(confidence) AS avg_confidence
FROM dev.face_detections
WHERE file_uuid = $1 AND trace_id IS NOT NULL
AND confidence >= $4 AND confidence <= $5
AND confidence >= $5 AND confidence <= $6
GROUP BY trace_id
HAVING COUNT(*) >= $2
ORDER BY {}
LIMIT $3
LIMIT $3 OFFSET $4
) tt
LEFT JOIN LATERAL (
SELECT id FROM dev.face_detections
@@ -111,7 +119,8 @@ async fn list_traces_sorted(
sqlx::query_as(&query)
.bind(&file_uuid)
.bind(min_faces)
.bind(limit)
.bind(effective_limit)
.bind(db_offset)
.bind(min_confidence)
.bind(max_confidence)
.fetch_all(state.db.pool())
@@ -146,6 +155,8 @@ async fn list_traces_sorted(
file_uuid,
total_traces,
total_faces,
page,
page_size,
traces,
}))
}
@@ -154,6 +165,8 @@ async fn list_traces_sorted(
#[derive(Debug, Deserialize)]
struct TraceFacesQuery {
page: Option<i64>,
page_size: Option<i64>,
limit: Option<i64>,
offset: Option<i64>,
interpolate: Option<bool>,
@@ -194,7 +207,14 @@ async fn list_trace_faces(
Query(q): Query<TraceFacesQuery>,
) -> Result<Json<TraceFacesResponse>, (StatusCode, String)> {
let limit = q.limit.unwrap_or(200).min(1000);
let offset = q.offset.unwrap_or(0);
// Support both page/page_size and offset; page/page_size takes precedence
let offset = if q.page.is_some() || q.page_size.is_some() {
let p = q.page.unwrap_or(1).max(1);
let ps = q.page_size.unwrap_or(200).max(1).min(1000);
(p - 1) * ps
} else {
q.offset.unwrap_or(0)
};
let interpolate = q.interpolate.unwrap_or(false);
let fps: f64 =
@@ -206,7 +226,7 @@ async fn list_trace_faces(
.unwrap_or(24.0);
let total_detected: i64 = sqlx::query_scalar(
"SELECT COUNT(*) FROM dev.face_detections WHERE file_uuid = $1 AND trace_id = $2"
"SELECT COUNT(*) FROM dev.face_detections WHERE file_uuid = $1 AND trace_id = $2",
)
.bind(&file_uuid)
.bind(trace_id)
@@ -214,21 +234,28 @@ async fn list_trace_faces(
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let rows: Vec<(i32, i32, Option<i32>, Option<i32>, Option<i32>, Option<i32>, f32)> =
sqlx::query_as(
"SELECT id, frame_number, x, y, width, height, confidence
let rows: Vec<(
i32,
i32,
Option<i32>,
Option<i32>,
Option<i32>,
Option<i32>,
f32,
)> = sqlx::query_as(
"SELECT id, frame_number, x, y, width, height, confidence
FROM dev.face_detections
WHERE file_uuid = $1 AND trace_id = $2
ORDER BY frame_number ASC
LIMIT $3 OFFSET $4"
)
.bind(&file_uuid)
.bind(trace_id)
.bind(limit)
.bind(offset)
.fetch_all(state.db.pool())
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
LIMIT $3 OFFSET $4",
)
.bind(&file_uuid)
.bind(trace_id)
.bind(limit)
.bind(offset)
.fetch_all(state.db.pool())
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let mut faces: Vec<TraceFaceItem> = Vec::new();