});
}
+// 删除某条进度(用于服务端已删除记录回写本地, 实现撤销/未执行)
+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) {
// 离线优先 + 自动同步 统一入口
-// æ\95°æ\8d®è§\84å\88\99 (å¸\83å°\94å¹¶é\9b\86): (group,op) ä»»ä¸\80æ\96¹å®\8cæ\88\90å\8d³å®\8cæ\88\90; ä¸\8då\88 æ\9c¬å\9c°ã\80\81ä¸\8dæ¯\94æ\97¶é\97´
+// æ\95°æ\8d®è§\84å\88\99 (å¸\83å°\94å¹¶é\9b\86): (group,op) ä»»ä¸\80æ\96¹å®\8cæ\88\90å\8d³å®\8cæ\88\90; äº\91端å\88 é\99¤å·²ç¡®è®¤è¿\9b度ä¼\9aå\9b\9eå\86\99æ\9c¬å\9c°(æ\92¤é\94\80)
// 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';
}
}
+ // 阶段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();