Skip to content

Studio Bug Report: get_trace_profile 讀寫不一致

Date: 2026-07-27 Author: Studio Team Status: 🔴 待修復 Priority: High


問題描述

Studio 前端 rename face group 後,離開再回來選同一個檔案,名稱變回舊的。

影響

使用者 rename 的變更無法持久顯示。雖然 trace_profiles.name 已更新,但 get_trace_profile API 回傳的是舊的 tkg_nodes.label,導致前端 loadClusterNames() 用舊名覆蓋新名。


測試驗證

1. Rename

curl -X PUT "http://localhost:3002/api/v1/trace-profile/group" \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"file_uuid": "9cbeb112fcc5452e869755a539478061", "trace_ids": [1], "name": "SusanTest"}'
回傳:{"success": true, "updated_count": 1} ✅

2. 讀取單筆 profile(❌ 仍是舊名)

GET /api/v1/trace-profile?file_uuid=...&trace_id=1
回傳:{"name": "Person_0"} ← 錯誤,應為 "SusanTest"

3. 讀取 face groups(✅ 是新名)

GET /api/v1/file/{uuid}/face-groups
回傳:{"name": "SusanTest", "trace_ids": [1]} ← 正確


根因分析

寫入路徑(正確)

PUT /api/v1/trace-profile/group → update_trace_profile_group_handler(profile.rs:228):

UPDATE trace_profiles SET name = $1 WHERE file_uuid = $2 AND trace_id = $3
寫入 trace_profiles.name ✅

讀取路徑 — 單筆(❌ 錯誤)

GET /api/v1/trace-profile → get_trace_profile_handler(profile.rs:78-81):

SELECT label, external_id, properties FROM tkg_nodes
WHERE file_uuid = $1 AND node_type = 'face_track'
  AND (external_id = $2 OR external_id = $3)
讀取 tkg_nodes.label,沒有 JOIN trace_profiles ❌

讀取路徑 — 群組(✅ 正確)

GET /api/v1/file/{uuid}/face-groups → get_face_groups_handler(profile.rs:560-566):

SELECT COALESCE(tp.name, tn.label) as name, tn.properties
FROM tkg_nodes tn
LEFT JOIN trace_profiles tp ON tp.file_uuid = tn.file_uuid
    AND tp.trace_id = (tn.properties->>'trace_id')::int
WHERE tn.file_uuid = $1 AND tn.node_type = 'face_track'
使用 COALESCE(tp.name, tn.label),優先取 trace_profiles.name ✅


前端影響流程

User rename group → updateTraceProfileGroup() → PUT /trace-profile/group
                  → trace_profiles.name = "SusanTest" ✅

User refresh → loadClusterResults() → GET /face-groups
            → cluster.name = "SusanTest" ✅
            → loadClusterNames() → GET /trace-profile
                                 → profile.name = "Person_0" ❌ (tkg_nodes.label)
                                 → c.name = profile.name → "Person_0" ❌ 覆蓋了新名

建議修復

方案:改 get_trace_profile_handler 使用 JOIN

pub async fn get_trace_profile_handler(...) {
    let tkg_table = schema::table_name("tkg_nodes");
    let tp_table = schema::table_name("trace_profiles");

    let row: Option<(String, String, serde_json::Value)> = sqlx::query_as(&format!(
        "SELECT COALESCE(tp.name, tn.label) as name, tn.external_id, tn.properties \
         FROM {} tn \
         LEFT JOIN {} tp ON tp.file_uuid = tn.file_uuid \
             AND tp.trace_id = (tn.properties->>'trace_id')::int \
         WHERE tn.file_uuid = $1 AND tn.node_type = 'face_track' \
             AND (tn.external_id = $2 OR tn.external_id = $3)",
        tkg_table, tp_table
    ))
    .bind(&params.file_uuid)
    .bind(&external_id_face)
    .bind(&external_id_trace)
    .fetch_optional(state.db.pool())
    .await?;

    let (name, _ext_id, properties) = row.ok_or(StatusCode::NOT_FOUND)?;

    Ok(Json(TraceProfile {
        file_uuid: params.file_uuid,
        trace_id: params.trace_id,
        name,  // 改為使用 JOIN 後的 name
        // ... 其他欄位不變
    }))
}

一致性保證

API 讀取欄位 修復後
PUT /trace-profile/group 寫入 trace_profiles.name 不變
GET /trace-profile ~~tkg_nodes.label~~ → COALESCE(tp.name, tn.label) ✅ 一致
GET /file/{uuid}/face-groups COALESCE(tp.name, tn.label) 不變

相關文件

  • 18_profile.md — Profile API 文件
  • 19_people_api.md — People API 文件
  • 23_studio_face_group_name_fix.md — Studio face-groups JOIN 修復