From c37ea6c60b5103ea2a70057c0862fdd5e04b1f3a Mon Sep 17 00:00:00 2001 From: root Date: Tue, 18 Aug 2026 21:21:42 +0800 Subject: [PATCH] =?utf8?q?fix:=20=E6=94=AF=E6=8C=81=E8=BF=9B=E5=BA=A6?= =?utf8?q?=E6=92=A4=E9=94=80-=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=88=A0=E9=99=A4?= =?utf8?q?=E5=B7=B2=E7=A1=AE=E8=AE=A4=E8=BF=9B=E5=BA=A6=E5=9B=9E=E5=86=99?= =?utf8?q?=E6=9C=AC=E5=9C=B0;=20=E5=88=A0=E9=99=A4G54=E5=A4=8D=E4=B9=A01?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit - 新增 deleteProgress(); sync pull 在拉取成功后, 对本地非 dirty 且服务端已不存在的进度执行本地删除 仅当 serverEvents 非空时执行, 防止服务端异常空响应误删; 绝不删 dirty(未推送)记录 - 删除 study_logs 中 G54 review1(id 401), 该进度记为未执行; 下次同步自动从本地移除 --- backend/db/bcd.db | Bin 9461760 -> 9469952 bytes frontend/src/db.js | 6 ++++++ frontend/src/sync.js | 16 ++++++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/backend/db/bcd.db b/backend/db/bcd.db index da4027c10f43ed2e3d215d7291dfb237cddc3485..0c595cd6882368c0d7f236c6154a5a38efc36cbd 100644 GIT binary patch delta 1857 zcmZwGeQXnT9LMqdUaxd_zkAq#4QR*KA&fn=Tko#j#(=?iaH5Qjhw&gAFkmtmAY%g@ zFxoN3Hn_nSG)H`jS&YUQqJ*%{gcuP?)I=gmNJxyLDEnEt*1AC>+m@vwySn< zjo^$p7hI_HW;xSlcc&^Gd9qXR5_3#0XkTABkYQ)79#o_WdrQ3e_ar_l~h}&Z&EQ(cb zaI8+mYUktn^1lyg6w;=oy>uUKbY{dBJsex)Y~f0=vuRg{#_Ih4+#-)^lW&nvH5?~g z!)bpmM8oA)k28EyoWW@iv>&vuwNJE*+FP2f^=Td24sE@*RGX_6Y1vw;M(S_s9rcEK zRUJ{^R?n*a>Or+dtyinn3N@(ashMi3Dku+?d&+I)hVrR$NqJv+Q#q?xNpGnH;_(zb6mNFUfuKA$hmlD6f*MBP4;wY8Vf>G&L?6Y)7nJ@MJ3NPITvV0_kTZ+s5WuJ~L`&GEU2 zw!~*YZHUcN+uD7!E`FhqR>tQ7S{A((vVNv;37wLKNyN9c@7vlDqW(k#=~yC6I-CfD zIufB%Ya#-)GZDqKEfGbuIT3zph=mus`e<$9QX#EKL;+PtM@lUR9|+Q5($CQ}>11*m zbUZnA>av+>x$R#3d+cvaOA|~Vwb%_o%Pp7%)MyunERQ3f>g|e<<(_8Fq~*!$XVTK- N%%wy2xmnhgzX5x5V~qd+ delta 1416 zcmY+?Nlz1T7=ZDoJ9*-aSrEk0c~hU2QK0gI&m3YxPlnE(Su&}A&#rK zhU>V2o4AF3+{PW;#Q+8|gkg-}9`54-9%2-a@EBtl#{?2cViHrB#uLn77IS!tc|3EM zqtEky1~Pm;S*aB|ftdYA23)<{&X>GQJy*1m`0z-zEUwZwiZ(oQuWz(kUOIbv?81^P zy+E{~`28F42bLwzQ+1U*^Xe*@cgCp`YHiAFs;-n7udb9QN@rLyt&FBAQ_5%=GMTz! zNmBX9Fy+7JMP$O6pRhCcnTCvePqSo9S+a`daq6BakG#&5QKzfdF8p_+)Opuy=ai&E zLx#N2kU{lenG85T+4Jg%-1h2-^s9APZaKw$YF#1My}ClKsdY%MI{nm%)SHw`uUD5# zk6H(%TN%xe#FWtp%azoXGU@WJl*whKvr45?8O@+v%F>xyE2_w#wXKI1Pf^!m^M diff --git a/frontend/src/db.js b/frontend/src/db.js index 9eccef7..325fcbb 100644 --- a/frontend/src/db.js +++ b/frontend/src/db.js @@ -112,6 +112,12 @@ export async function clearDirty(group_id, op_type) { }); } +// 删除某条进度(用于服务端已删除记录回写本地, 实现撤销/未执行) +export async function deleteProgress(group_id, op_type) { + const key = `${group_id}:${op_type}`; + return singleReq('progress', 'readwrite', s => s.delete(key)); +} + // ---------- meta store ---------- export async function getMeta(key) { diff --git a/frontend/src/sync.js b/frontend/src/sync.js index a7c30ca..d349a2e 100644 --- a/frontend/src/sync.js +++ b/frontend/src/sync.js @@ -1,12 +1,12 @@ // 离线优先 + 自动同步 统一入口 -// 数据规则 (布尔并集): (group,op) 任一方完成即完成; 不删本地、不比时间 +// 数据规则 (布尔并集): (group,op) 任一方完成即完成; 云端删除已确认进度会回写本地(撤销) // dirty = "这条本地完成记录尚未确认上传到云端" // syncNow: 上传本地 dirty → 拉云端并集合并 → 更新 lastSyncAt → 重算 learnable // triggerAutoSync: 幂等并发锁, 供首页/联网恢复/完成学习 调用, 失败时置 failed import { api } from './api'; import { - getDirtyProgress, clearDirty, getAllProgress, putProgress, + getDirtyProgress, clearDirty, getAllProgress, putProgress, deleteProgress, putWords, setMeta, getMeta, setLearnableGroups, getAllWordsFromIDB, getWordsCount, } from './db'; @@ -78,6 +78,18 @@ export async function syncNow() { } } + // 阶段2b: 服务端已删除(且本地已确认/非 dirty)的进度, 本地同步删除。 + // 仅在成功拉取到非空集合时才执行, 避免服务端异常返回空时误删本地进度; + // 仅删非 dirty 记录(本地未推送完成的进度绝不删, 保护离线工作)。 + if (serverEvents.length > 0) { + const serverKeys = new Set(serverEvents.map(e => `${e.group_id}:${e.op_type}`)); + for (const p of localAll) { + if (!p.dirty && !serverKeys.has(p.key)) { + await deleteProgress(p.group_id, p.op_type); + } + } + } + // 阶段3: 首次 warmup 词库 (仅 IDB 空时, 避免每次重拉 1852 词) if ((await getWordsCount()) === 0) { const allWords = await api.getAllWords(); -- 2.43.0