同步容错:服务器删除日历本后 404 不再导致整个账号同步失败

- DavClient 新增 DavHttpError(带 HTTP 状态码),REPORT/PUT/DELETE 统一抛出
- 同步引擎对单本 404 跳过并自动从账号移除(先按 href 清行再迁移序号),
  全部日历本均 404 时保留账号配置并给出明确提示
- 推送阶段单条失败不再中断整个账号(401 凭据失效仍立即抛出)
- 编辑账号保存与自动移除都在序号迁移前先删被移除日历本的行,
  修复行混入其它日历本 calKey 的老问题(实测「读书」988 行混进「轻舟」)
- applyRemote 刷新行的 href:日历本被删除重建后集合 URL 变化,
  旧行不再残留死地址(编辑推送 404 / 永久卡待推送)
- pruneOrphanRows 追加 href 维度兜底清扫(跳过 dirty 行)
This commit is contained in:
2026-09-29 08:46:01 +08:00
parent 8b5519bc31
commit d0b8720c41
4 changed files with 227 additions and 19 deletions
@@ -224,6 +224,24 @@ struct EditAccountPage {
target.calendarColors = selectedItems.map((c: EditCalendarItem): string => c.color);
LogUtil.write(`编辑账号保存:id=${target.id},新勾选 ${selectedItems.length}/${this.calendarList.length} 个日历本`);
// 按 href 求出「旧序号 → 新序号」,迁移已落库日程到正确的 calKey
// ⭐ 迁移前必须先删掉被取消勾选日历本的行(按 href):
// 序号收窄后,被取消本的旧 calKey 会被前面位移下来的本"接管",
// 不先删的话旧行会与新主的行混在同一个 calKey 里(2026-09-29 实测:
// 服务器删除「读书」后重选,其 988 行混进「轻舟」的 calKey,镜像一直复活幽灵数据)。
const newHrefSet: Set<string> = new Set<string>();
for (const c of newHrefs) {
newHrefSet.add(c);
}
const removedHrefs: string[] = [];
for (const h of oldHrefs) {
if (!newHrefSet.has(h)) {
removedHrefs.push(h);
}
}
if (removedHrefs.length > 0) {
const pruned: number = await EventDb.pruneByHrefs(context, target.id, removedHrefs);
LogUtil.write(`编辑账号保存:取消勾选/已失效 ${removedHrefs.length} 个日历本,清理本地日程 ${pruned} 行`);
}
const remapPairs: Array<[string, string]> = [];
for (let oi: number = 0; oi < oldHrefs.length; oi++) {
const oldNorm: string = DavClient.normalizeHref(oldHrefs[oi]);