feat: integrate new merge_groups API from Core Team
Replace merge_trace implementation with merge_groups API Changes: 1. src/api/index.ts: - Add merge_groups API definition - POST /api/v1/file/:file_uuid/groups/merge - Parameters: file_uuid, source_groups[], target_group_name 2. src/views/PeopleView.vue: - Rewrite executeMerge() function - Use group names instead of trace IDs - Single API call for all groups - Remove trace-level loop 3. src/views/PeopleView.vue: - Delete updateClusterAfterMerge() function - No longer needed with new API Benefits: - ✅ Atomic operation (single API call) - ✅ Correct merge behavior (groups, not traces) - ✅ Better performance (1 call vs N calls) - ✅ Data consistency guaranteed API behavior: - Merge N groups into 1 target group - All traces from source groups merged to target - Source groups deleted after merge - Returns traces_merged count Core API: Implemented by Core Team (2026-07-25) Status: Ready for testing
This commit is contained in:
@@ -359,6 +359,17 @@ case 'search_keyword': {
|
|||||||
case 'merge_trace': {
|
case 'merge_trace': {
|
||||||
return { url: `/api/v1/file/${a.file_uuid}/trace/${a.source_id}/merge/${a.target_id}`, method: 'POST' }
|
return { url: `/api/v1/file/${a.file_uuid}/trace/${a.source_id}/merge/${a.target_id}`, method: 'POST' }
|
||||||
}
|
}
|
||||||
|
case 'merge_groups': {
|
||||||
|
return {
|
||||||
|
url: `/api/v1/file/${a.fileUuid}/groups/merge`,
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
file_uuid: a.fileUuid,
|
||||||
|
source_groups: a.sourceGroups,
|
||||||
|
target_group_name: a.targetGroupName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
case 'bind_face': {
|
case 'bind_face': {
|
||||||
const bindBody: any = { file_uuid: a.fileUuid }
|
const bindBody: any = { file_uuid: a.fileUuid }
|
||||||
if (a.faceId) bindBody.face_id = a.faceId
|
if (a.faceId) bindBody.face_id = a.faceId
|
||||||
|
|||||||
@@ -1725,18 +1725,10 @@ async function executeMerge(targetGroupId: string) {
|
|||||||
console.log('[executeMerge] targetGroup:', targetGroup)
|
console.log('[executeMerge] targetGroup:', targetGroup)
|
||||||
if (!targetGroup) return
|
if (!targetGroup) return
|
||||||
|
|
||||||
const targetTraceId = targetGroup.trace_ids?.[0]
|
const targetGroupName = targetGroup.name
|
||||||
console.log('[executeMerge] targetTraceId:', targetTraceId, typeof targetTraceId)
|
console.log('[executeMerge] targetGroupName:', targetGroupName)
|
||||||
if (!targetTraceId) {
|
if (!targetGroupName) {
|
||||||
alert('Target group has no traces')
|
alert('Target group has no name')
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetTrace = clusterTracesMap.value[String(targetTraceId)]
|
|
||||||
console.log('[executeMerge] targetTrace:', targetTrace)
|
|
||||||
const targetFileUuid = targetTrace?.file_uuid
|
|
||||||
if (!targetFileUuid) {
|
|
||||||
alert('Target trace has no file_uuid')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1745,71 +1737,48 @@ async function executeMerge(targetGroupId: string) {
|
|||||||
const sourceGroups = clusterAsPending.value.filter((c: any) => sourceGroupIds.includes(c.identity_uuid))
|
const sourceGroups = clusterAsPending.value.filter((c: any) => sourceGroupIds.includes(c.identity_uuid))
|
||||||
console.log('[executeMerge] sourceGroups:', sourceGroups.length)
|
console.log('[executeMerge] sourceGroups:', sourceGroups.length)
|
||||||
|
|
||||||
let totalTraces = 0
|
const sourceGroupNames = sourceGroups.map(g => g.name).filter(Boolean)
|
||||||
let validTraces = 0
|
console.log('[executeMerge] sourceGroupNames:', sourceGroupNames)
|
||||||
for (const g of sourceGroups) {
|
|
||||||
for (const tid of g.trace_ids || []) {
|
|
||||||
totalTraces++
|
|
||||||
const t = clusterTracesMap.value[String(tid)]
|
|
||||||
if (t?.file_uuid) validTraces++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[executeMerge] totalTraces:', totalTraces, 'validTraces:', validTraces)
|
if (sourceGroupNames.length === 0) {
|
||||||
|
alert('No valid source groups to merge')
|
||||||
if (validTraces === 0) {
|
|
||||||
alert('No valid traces to merge (all missing file_uuid)')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirm(`Merge ${validTraces}/${totalTraces} trace(s) from ${sourceGroups.length} group(s)?`)) return
|
if (!confirm(`Merge ${sourceGroupNames.length} group(s) into "${targetGroupName}"?`)) return
|
||||||
|
|
||||||
operationLoading.value = true
|
operationLoading.value = true
|
||||||
operationMessage.value = `Merging ${validTraces} trace(s)...`
|
operationMessage.value = `Merging ${sourceGroupNames.length} group(s)...`
|
||||||
|
|
||||||
let successCount = 0
|
try {
|
||||||
let failCount = 0
|
console.log('[executeMerge] calling merge_groups API:', {
|
||||||
|
fileUuid: selectedFileUuid.value,
|
||||||
|
sourceGroups: sourceGroupNames,
|
||||||
|
targetGroupName: targetGroupName
|
||||||
|
})
|
||||||
|
|
||||||
for (const g of sourceGroups) {
|
const result = await apiCall('merge_groups', {
|
||||||
for (const srcTraceId of g.trace_ids || []) {
|
fileUuid: selectedFileUuid.value,
|
||||||
const srcTrace = clusterTracesMap.value[String(srcTraceId)]
|
sourceGroups: sourceGroupNames,
|
||||||
const srcFileUuid = srcTrace?.file_uuid
|
targetGroupName: targetGroupName
|
||||||
if (!srcFileUuid) {
|
})
|
||||||
console.warn('[merge] skipping trace without file_uuid:', srcTraceId)
|
|
||||||
failCount++
|
console.log('[executeMerge] result:', result)
|
||||||
continue
|
|
||||||
}
|
if (result?.success) {
|
||||||
if (srcFileUuid !== targetFileUuid) {
|
console.log('[executeMerge] success, traces_merged:', result.traces_merged)
|
||||||
console.warn('[merge] skipping cross-file trace:', srcTraceId, srcFileUuid, '!=', targetFileUuid)
|
await loadClusterResults()
|
||||||
failCount++
|
clearSelection()
|
||||||
continue
|
} else {
|
||||||
}
|
alert(`Merge failed: ${result?.error || 'Unknown error'}`)
|
||||||
try {
|
|
||||||
console.log('[executeMerge] calling merge_trace:', {
|
|
||||||
file_uuid: srcFileUuid,
|
|
||||||
source_id: srcTraceId,
|
|
||||||
target_id: targetTraceId
|
|
||||||
})
|
|
||||||
await apiCall('merge_trace', {
|
|
||||||
file_uuid: srcFileUuid,
|
|
||||||
source_id: srcTraceId,
|
|
||||||
target_id: targetTraceId
|
|
||||||
})
|
|
||||||
successCount++
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Merge trace failed:', srcTraceId, e)
|
|
||||||
failCount++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[executeMerge] error:', e)
|
||||||
|
alert(`Merge failed: ${e}`)
|
||||||
|
} finally {
|
||||||
|
operationLoading.value = false
|
||||||
|
operationMessage.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[executeMerge] completed:', successCount, 'success,', failCount, 'failed')
|
|
||||||
|
|
||||||
operationLoading.value = false
|
|
||||||
operationMessage.value = ''
|
|
||||||
|
|
||||||
clearSelection()
|
|
||||||
await loadClusterResults()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function batchMoveFaces() {
|
async function batchMoveFaces() {
|
||||||
|
|||||||
Reference in New Issue
Block a user