换基线:把「底座换代」做进后台(站点数据一行不动)
导出是「底座为底 + 只覆盖托管表」,所以底座必须是手机导出的包。底座跟着数据集走, 手机重新导出后想换代,以前只能整包重新导入成新数据集(站点补录的行、主题、备注都得重收拾)。 新动作 /admin/baseline(后台首页加入口)就是给同一份数据的底座换代用的。 三道闸,顺序不能挪: 1. 重合判据:新包里的到访 UUID 与站点侧带 UUID 的到访比。一条都对不上直接拒 (连 force 也不给过)——干跑只验结构,一份不相干的包照样导得出,但换下去站点 每行都在新底座里认不出身份,全按新行插到最大编号之后,真机上就是「这些到访 全变成新的」。重合率不到一半要显式确认;判据没过不当场报错了事:包已经收进 uploads,页面给一个「我确认,强制替换」按钮,不必重传几十 MB。 2. 干跑导出:底座缺表/缺实体编号在这步挡住,不会出现「换完了才发现导不出、 原来那份已经不知去哪」。 3. 旧底座留档 + 记档,换错了可一键回滚。 两个坑: - 留档名不能只用秒级时间戳。「替换后紧接着回滚」落在同一秒时,回滚那次 rename 会把 刚生成的留档覆盖掉,于是回滚回了个寂寞(TestRollbackBaselineRestoresPrevious 当场红)。 改用 baselineBackupName():20060102-150405 + 冲突自增。 - ensureBaseline 必须先看记档。只认 datasets.sha256(= 最初那份包)的话,底座一丢就静默 重建回旧包,上一次换基线白做且毫无提示。抽出 ensureBaselineTo(ctx, dsID, dst) 共用; 「重建」入口写临时文件再 rename,不做破坏性删除。 回归用例 7 条(internal/web/baseline_swap_test.go):成功路径断言「换底座前后导出包里 ZVISIT 的 UUID 集合逐条一致」——编号会换、身份不该换,谁在底座里没对上立刻亮; 记档优先重建带反证(清掉记档后同一条路必须走不通);部分重合需确认、完全对不上 force 也不给过、非包文件第一步就挡住、回滚还原且当前那份也留档、页面渲染到底。 验收:./scripts/verify.sh 全量 PASS=318 FAIL=0 SKIP=0。
This commit is contained in:
@@ -162,3 +162,33 @@ V2(+3 个站内新建地点)、V3(手机里也有的行按手机原值重
|
||||
(反忽略六行写法 + 只放行 .md 的理由 + `git check-ignore --no-index` 打靶复核 +
|
||||
`MAX_MEMORY_CHARS=1e4` 按字符判定 + 只有 MEMORY.md 参与注入 + CRLF 归一与
|
||||
「`grep -c $'\r'` 在 Git Bash 里会全命中」这个假信号)。
|
||||
|
||||
## 「换基线」做进后台(2026-09-30 18:55)
|
||||
|
||||
上一轮用户手工走的换基线闭环,这轮做成了后台动作:`/admin/baseline`(后台首页加了入口)。
|
||||
**站点数据一行不动,只换导出底稿**——这正是「底座必须是手机包」这条铁律的可操作形态。
|
||||
|
||||
三道闸,顺序不可挪(写在 `internal/web/baseline.go` 的文件头注释里):
|
||||
1. **重合判据**:拿新包里的到访 UUID 与站点侧带 UUID 的到访比(站点补录行本来就没 UUID,不计入分母)。
|
||||
`matched==0` ⇒ 硬拒,**force 也不给过**(干跑只验结构,一份不相干的包照样导得出,但换下去站点每行都
|
||||
认不出身份 ⇒ 全按新行插到最大编号之后 ⇒ 真机上「这些到访全变成新的」)。`matched*2 < withUUID` ⇒
|
||||
要显式确认;判据没过不当场报错了事——包已收好,页面给一个 `?pending=<sha>` 的「强制替换」按钮,不必重传 20MB。
|
||||
2. **干跑导出**(`ExportRondbackup(..., tmp, io.Discard)`):底座缺表/缺实体编号在这步就挡住,
|
||||
不会出现「换完了才发现导不出、原来那份已经不知去哪」。
|
||||
3. **旧底座留档 + 记档**:`datasets.baseline_name/baseline_sha256/baseline_at`。
|
||||
|
||||
踩到两个坑,都写进了 details 卅三:
|
||||
- ⚠️ **留档名不能只用秒级时间戳**:「替换后紧接着回滚」落在同一秒时,回滚那次 rename 会把刚生成的
|
||||
留档覆盖掉,于是回滚「回了个寂寞」——`TestRollbackBaselineRestoresPrevious` 当场红给我看。改用
|
||||
`baselineBackupName()`(`20060102-150405` + 冲突自增)。
|
||||
- ⚠️ **`ensureBaseline` 必须先看记档**:只认 `datasets.sha256`(= 最初那份包)的话,底座一丢就静默
|
||||
重建回旧包,换基线白做且毫无提示。共用实现 `ensureBaselineTo(ctx, dsID, dst)`;「重建」入口写临时
|
||||
文件再 rename,不做破坏性删除。用例里带了**反证**(清掉记档后同一条路必须走不通),免得被别的兜底弄绿。
|
||||
- 另一个小坑:算摘要的落盘要在 `uploads` **之外**,否则 `findUploadBySha` 会把自己认成「已存在」。
|
||||
|
||||
第一次跑定点用例时还红了一条 `换底座后导出包里的到访数 824 != 站点 823`——那是**我的断言写错了**:
|
||||
导出条数与站点条数本来就不严格相等(导入会跳过内容重复的行),既有用例都只做「导出前后相对比较」。
|
||||
改成更锋利的判据:换底座前后导出包里 `ZVISIT` 的 **UUID 集合逐条一致**——编号会换、身份不该换。
|
||||
|
||||
验收:`./scripts/verify.sh` 全量 **PASS=318 FAIL=0 SKIP=0**(10m15s)。
|
||||
新增用例 7 条(`internal/web/baseline_swap_test.go`)。
|
||||
|
||||
Reference in New Issue
Block a user