fix: 同步引擎重写——外键重映射、自然键去重、墓碑传播;服务端冲突比较修正(双设备测试 12/12)
This commit is contained in:
+47
-3
@@ -14,6 +14,15 @@ const ITER = 150000
|
||||
|
||||
const TABLES = ['project', 'work_log', 'log_item', 'attendance', 'weekly_note', 'setting', 'memo', 'credential', 'transaction']
|
||||
|
||||
// 自然键:同一逻辑记录在两台设备上独立创建时的去重依据
|
||||
const NATURAL_KEYS = {
|
||||
project: 'name',
|
||||
work_log: 'date',
|
||||
attendance: 'date',
|
||||
weekly_note: 'week_start',
|
||||
setting: 'key'
|
||||
}
|
||||
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true })
|
||||
|
||||
// ---------- 存储辅助 ----------
|
||||
@@ -221,21 +230,56 @@ async function handle(req, res) {
|
||||
if (!userData.tables) userData.tables = {}
|
||||
if (!userData.tombstones) userData.tombstones = {}
|
||||
|
||||
// 应用推送的变更(新者胜)
|
||||
// 应用推送的变更(新者胜 + 自然键去重)
|
||||
const incomingTables = body.tables || {}
|
||||
let changed = 0
|
||||
const now = new Date().toISOString()
|
||||
for (const t of TABLES) {
|
||||
const rows = incomingTables[t] || []
|
||||
userData.tables[t] = userData.tables[t] || {}
|
||||
const nk = NATURAL_KEYS[t] || null
|
||||
for (const row of rows) {
|
||||
if (!row || !row.uuid) continue
|
||||
const clean = {}
|
||||
for (const k of Object.keys(row)) if (k !== 'id') clean[k] = row[k]
|
||||
// 自然键冲突:另一 uuid 的记录已占用同一自然键
|
||||
let conflictUuid = null
|
||||
if (nk) {
|
||||
const nkVal = String(clean[nk] || '')
|
||||
if (nkVal) {
|
||||
for (const u2 of Object.keys(userData.tables[t])) {
|
||||
if (u2 === row.uuid) continue
|
||||
if (String(userData.tables[t][u2][nk] || '') === nkVal) { conflictUuid = u2; break }
|
||||
}
|
||||
}
|
||||
}
|
||||
const existing = userData.tables[t][row.uuid]
|
||||
if (!existing || String(clean.updated_at || '') >= String(existing.updated_at || '')) {
|
||||
userData.tables[t][row.uuid] = clean
|
||||
if (conflictUuid) {
|
||||
// 与自然键冲突行比较新者(而非与同 uuid 旧行)
|
||||
const conflictRow = userData.tables[t][conflictUuid]
|
||||
const incomingNewer = String(clean.updated_at || '') >= String(conflictRow.updated_at || '')
|
||||
if (!incomingNewer) {
|
||||
// 本记录较旧:加墓碑让推送端删除它
|
||||
userData.tombstones[t] = userData.tombstones[t] || {}
|
||||
userData.tombstones[t][row.uuid] = now
|
||||
changed++
|
||||
continue
|
||||
}
|
||||
// 本记录较新:删除冲突副本并加墓碑通知其他端
|
||||
if (t === 'work_log') {
|
||||
// 冲突的工作日志被删时,其日志条目改挂到幸存者
|
||||
for (const iu of Object.keys(userData.tables.log_item || {})) {
|
||||
const it = userData.tables.log_item[iu]
|
||||
if (String(it.wuuid || '') === conflictUuid) { it.wuuid = row.uuid; changed++ }
|
||||
}
|
||||
}
|
||||
delete userData.tables[t][conflictUuid]
|
||||
userData.tombstones[t] = userData.tombstones[t] || {}
|
||||
userData.tombstones[t][conflictUuid] = now
|
||||
changed++
|
||||
}
|
||||
userData.tables[t][row.uuid] = clean
|
||||
changed++
|
||||
}
|
||||
}
|
||||
// 应用墓碑
|
||||
|
||||
Reference in New Issue
Block a user