合并:重复地点不再反复判「备份新增」,到访的活动改按身份比
- DiffPlaces 的 byCoord/byName 改为多值索引 + take() 逐条认领未用项:真机一个坐标下会有 多条同名同坐标的地点(实测「柳州站」3 条),单槽位时同一坐标最多只配上 2 条,第 3 条 永远判「备份新增」⇒ 每次合并都再插一条重复地点、差异永远归不了零(实测 368→438→439)。 修完:地点新增 70→67、合并后再比对归零、再点合并直接「没有要写入的改动」。 - 到访的「活动」由按名称比改为按身份(ZUID_)比,两边都缺 UUID 才退回名称:16 个活动改名 曾把 355 条到访全变成同因冲突,且逐条选「用实机」只写回 activity_id(映射回同一个活动) 改不掉名字。loadStoredVisits 为此多 JOIN 一次 entity_raw 取 ZACTIVITY.ZUID_。 - 回归用例 4 条(重复地点一一配上、活动改名不算冲突、换活动仍要报、缺 UUID 退回名称)+ 「合并后地点差异归零」断言 3 处;两处改动都做过变异验证。 - 实测(09-28 备份并进 09-14 数据集):到访冲突 253→6,合并两次地点数 438→439 变为 435→435。
This commit is contained in:
@@ -291,3 +291,101 @@ func TestDiffPlacesMatchesByNameWhenCoordMoved(t *testing.T) {
|
||||
t.Fatalf("应报出坐标差异: %+v", d.Changed)
|
||||
}
|
||||
}
|
||||
|
||||
// 真机一个坐标下会有好几条同名同坐标的地点(实测「柳州站」同一个坐标 3 条,备份里这类分组共 10 组)。
|
||||
// 用单槽位 map 时,同一坐标最多只能配上一条,第 3 条永远判「备份新增」→ 每次合并都再插一条
|
||||
// 重复地点、差异永远归不了零。
|
||||
func TestDiffPlacesAlignsDuplicateNameCoordGroups(t *testing.T) {
|
||||
loc := func(pk int) Location {
|
||||
return Location{SrcPK: pk, Name: "柳州站", Lat: 24.307787, Lon: 109.388309}
|
||||
}
|
||||
sp := func(pk int) StoredPlace {
|
||||
return StoredPlace{SrcPK: pk, Key: PlaceCoordKey(24.307787, 109.388309),
|
||||
Name: "柳州站", Lat: 24.307787, Lon: 109.388309}
|
||||
}
|
||||
b := &Backup{Locations: []Location{loc(41), loc(42), loc(43)}}
|
||||
|
||||
d := DiffPlaces([]StoredPlace{sp(11), sp(12), sp(13)}, b)
|
||||
if len(d.Added) != 0 || len(d.Removed) != 0 || len(d.Changed) != 0 {
|
||||
t.Fatalf("两边各 3 条同名同坐标应一一配上: +%d -%d ~%d", len(d.Added), len(d.Removed), len(d.Changed))
|
||||
}
|
||||
if d.Same != 3 {
|
||||
t.Fatalf("应配上 3 条,实际 %d", d.Same)
|
||||
}
|
||||
|
||||
// 反过来:站内真的少两条时,仍要老老实实报出「备份新增 2」——
|
||||
// 多值配对不能变成「只要有同名同坐标就一律不算新增」。
|
||||
d = DiffPlaces([]StoredPlace{sp(11)}, b)
|
||||
if len(d.Added) != 2 || len(d.Removed) != 0 || d.Same != 1 {
|
||||
t.Fatalf("站内只有 1 条时应报新增 2: +%d -%d same=%d", len(d.Added), len(d.Removed), d.Same)
|
||||
}
|
||||
}
|
||||
|
||||
// 活动改了名(UUID 还在):那是活动层的差异,不该在挂着它的每条到访上重复报一遍。
|
||||
// 实测真机 16 个活动改名会把 355 条到访全变成假冲突,而且逐条选「用实机」也改不掉活动名。
|
||||
func TestDiffVisitsIgnoresActivityRename(t *testing.T) {
|
||||
b := &Backup{
|
||||
Visits: []Visit{{SrcPK: 5, Arrival: tp("2026-09-01 10:00"), ActivitySrcPK: ip(7)}},
|
||||
Activities: []Activity{{SrcPK: 7, Name: "吃饭"}},
|
||||
VisitUUID: map[int]string{5: uuidA},
|
||||
ActivityUUID: map[int]string{7: uuidC},
|
||||
}
|
||||
stored := []StoredVisit{{
|
||||
SrcPK: 1, UUID: uuidA, Arrival: tp("2026-09-01 10:00"),
|
||||
ActivityName: "餐厅", ActivityUUID: uuidC,
|
||||
}}
|
||||
d := DiffVisits(stored, b)
|
||||
if len(d.Changed) != 0 {
|
||||
t.Fatalf("同一个活动改名不该算到访冲突: %+v", d.Changed[0].Fields)
|
||||
}
|
||||
if d.Same != 1 {
|
||||
t.Fatalf("应算作一致,实际 same=%d", d.Same)
|
||||
}
|
||||
}
|
||||
|
||||
// 换成了另一个活动才是真差异。
|
||||
func TestDiffVisitsReportsActivityChange(t *testing.T) {
|
||||
b := &Backup{
|
||||
Visits: []Visit{{SrcPK: 5, Arrival: tp("2026-09-01 10:00"), ActivitySrcPK: ip(7)}},
|
||||
Activities: []Activity{{SrcPK: 7, Name: "吃饭"}, {SrcPK: 8, Name: "散步"}},
|
||||
VisitUUID: map[int]string{5: uuidA},
|
||||
ActivityUUID: map[int]string{7: uuidC, 8: uuidB},
|
||||
}
|
||||
stored := []StoredVisit{{
|
||||
SrcPK: 1, UUID: uuidA, Arrival: tp("2026-09-01 10:00"),
|
||||
ActivityName: "散步", ActivityUUID: uuidB,
|
||||
}}
|
||||
d := DiffVisits(stored, b)
|
||||
if len(d.Changed) != 1 {
|
||||
t.Fatalf("换了活动该报冲突,实际 %d 条", len(d.Changed))
|
||||
}
|
||||
f := d.Changed[0].Fields
|
||||
if len(f) != 1 || f[0].Label != "活动" || f[0].From != "散步" || f[0].To != "吃饭" {
|
||||
t.Fatalf("活动差异不对: %+v", f)
|
||||
}
|
||||
}
|
||||
|
||||
// 站点自建的活动没有 UUID,退不回身份就按名称比(老数据要保持原样)。
|
||||
func TestDiffVisitsActivityWithoutUUIDFallsBackToName(t *testing.T) {
|
||||
b := &Backup{
|
||||
Visits: []Visit{{SrcPK: 5, Arrival: tp("2026-09-01 10:00"), ActivitySrcPK: ip(7)}},
|
||||
Activities: []Activity{{SrcPK: 7, Name: "吃饭"}},
|
||||
VisitUUID: map[int]string{5: uuidA},
|
||||
ActivityUUID: map[int]string{7: uuidC},
|
||||
}
|
||||
// 名称一致 → 不报
|
||||
same := []StoredVisit{{
|
||||
SrcPK: 1, UUID: uuidA, Arrival: tp("2026-09-01 10:00"), ActivityName: "吃饭",
|
||||
}}
|
||||
if d := DiffVisits(same, b); len(d.Changed) != 0 {
|
||||
t.Fatalf("名称相同时不该报冲突: %+v", d.Changed[0].Fields)
|
||||
}
|
||||
// 名称不同 → 报
|
||||
diff := []StoredVisit{{
|
||||
SrcPK: 1, UUID: uuidA, Arrival: tp("2026-09-01 10:00"), ActivityName: "散步",
|
||||
}}
|
||||
d := DiffVisits(diff, b)
|
||||
if len(d.Changed) != 1 || d.Changed[0].Fields[0].Label != "活动" {
|
||||
t.Fatalf("没有 UUID 时应退回按名称比: %+v", d.Changed)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user