图片恢复:坐标来源全部合并,认不出时说清已查过哪些来源

真数据实测(扫描 40 个对象、只挂回 7 张)暴露了第一版的漏洞:
报错「数据集 #1 里没有编号 389 的地点」——来源能读出来,却不是上传那会儿的编号空间。

根因:srcPKCoords 是「第一个能读到的来源就返回」,而**换基线会覆盖 baselines/<id>.sqlite**,
当前那份未必是当年上传时用的编号;旧编号往往还躺在同一份底座的 .bak 留档里。
教训很直白:**「能读出数据」不等于「读对了那一版」**。

改了三处:
1. 来源全部读入合并(先读的优先):库里 places → entity_raw 里 ZLOCATION 的整行留档
   (站点上删掉的地点,places 没了、留档可能还在)→ baselines/<id>.sqlite 及其每一份 .bak-*。
   读不动的文件跳过换下一个,不再「第一份成功就返回」。
2. 同一个编号保留多组候选坐标,逐个试到能在当前数据集里对上为止
   (站点纠过偏、或不同来源算出的坐标不一样,只认第一组会白白认不出)。
3. 认不出要能排查:理由写明「已查来源 …」与试过的坐标;提示条按**数据集归并**上报
   (「数据集 #1:31 张(编号 389、390…),已查来源 …」),不再刷「另有 31 张」;
   完整清单(前 30 条)进服务端日志。

新增用例:当前底座缺编号、换基线留档里才有(正是用户遇到的形状)⇒ 必须救得回;
以及「真的认不出」时要带出已查来源。全量验收 PASS=332 FAIL=0 SKIP=0。
This commit is contained in:
tamakyi
2026-10-01 00:11:04 +08:00
parent 770e752a7a
commit 6b665b6056
6 changed files with 333 additions and 56 deletions
+18
View File
@@ -259,3 +259,21 @@ R2 的 Token 默认没有 `s3:ListBucket` ⇒ 403。列不动时**不让整个
用例除「删数据集后专题还在」外,还直接查 `pg_constraint` 确认外键确实不在——只看行为可能被别的原因弄绿。
**导出链路没碰**:`place_images` / `topics` / `Dataset.UserID` 都不参与 `.rondbackup` 导出。
### 再补(23:50):认不出 33 张 —— 「能读出数据」≠「读对了那一版」
用户跑到真数据上:扫描 40 个对象(对象存储 19),只挂回 7 张,**认不出 33 张**,
报错「数据集 #1 里没有编号 389 的地点」。
根因:我的 `srcPKCoords` 是「第一个能读到的来源就返回」,而**换基线会覆盖 `baselines/<id>.sqlite`** ⇒
当前那份未必是上传那会儿的编号空间,旧编号很可能还在同一份底座的 `.bak` 留档里。
改了三处:
1. 来源**全部读入合并**:库里 places → `entity_raw` 里 ZLOCATION 的整行留档 → `baselines/<id>.sqlite`
及**每一份 `.bak-*`**(读不动的跳过,不再第一份成功就返回)。
2. 同一编号**保留多组候选坐标**,逐个试到能在当前数据集里对上(站点纠过偏/来源不一致的情况)。
3. 认不出要能排查:理由写明「已查来源 …」与试过的坐标;提示条**按数据集归并**上报,
不再刷「另有 31 张」;完整清单(前 30 条)进服务端日志。
可复用的教训:从「可能被覆盖过的留档」里还原编号/坐标时,必须把历史版本一起纳入,
并在失败信息里列出已查过的来源——否则用户只看到「认不出」,无从下手。
+22
View File
@@ -659,3 +659,25 @@ Core Data 元数据、索引、持久化历史、`ZRAWVISIT` 采样点、`ZTRIP*
**不影响导出**:这轮动的 `place_images` / `topics` / `Dataset.UserID` 都不参与 `.rondbackup` 导出
(导出只读 datasets/places/visits/movements/weather/entity_raw)。
### ⚠️ 三十四再补:坐标来源不能「第一个能读的就算了」(2026-09-30 23:50,用户实测)
第一版上线后用戶反馈:**扫描 40 个对象,只挂回 7 张,认不出 33 张**,
报错是「数据集 #1 里没有编号 389 的地点」——即来源能读出来,但**不是上传那会儿的编号空间**。
根因:`srcPKCoords` 是「第一个能读到的来源就返回」,而 **换基线会覆盖 `baselines/<id>.sqlite`**,
当前那份未必是当年上传时用的编号;旧编号往往还躺在同一份底座的 `.bak` 留档里。
改成三件事:
1. **所有来源全部读进来合并**(先读的优先):库里 `places` → `entity_raw` 里 ZLOCATION 的整行留档
(站点上删掉的地点,places 没了、留档可能还在)→ `baselines/<id>.sqlite` 及其**每一份 `.bak-*`**。
读不动的文件跳过换下一个,不再「第一份成功就返回」。
2. **同一个编号保留多组候选坐标**,逐个试到能在当前数据集里对上为止:
站点纠过偏、或不同来源算出的坐标不一样,只认第一组会白白认不出。
3. **认不出要能排查**:`unresolvedImage` 带上 `Dataset / SrcPK / Note`,理由里写明
「已查来源:库里 places / 原始行留档 / 155.sqlite / …」与试过的坐标;
提示条按**数据集归并**上报(「数据集 #1:31 张(编号 389、390…),已查来源 …」),
不再刷「另有 31 张」;完整清单(前 30 条)进服务端日志。
教训(可复用):**「能读出数据」不等于「读对了那一版」**。凡是从「可能被覆盖过的留档」里还原编号/坐标,
都要把历史版本一起纳入,并且在失败信息里列出已查过的来源 —— 否则用户只能看到「认不出」,无从下手。
+1 -1
View File
@@ -55,7 +55,7 @@
## 跨备份 / 附属数据(details 十九~廿一、卅四)
- 预演按 `VisitUUID`/`ActivityUUID` 对齐、只读;`raw #>> '{ZIDENTIFIER_,$blob}'` 是 base64 须转 hex 比;地点只认坐标(`PlaceCoordKey` 6 位)。
- 附属数据(备注/投票/图片)主键=`place_key` 坐标键(非 dataset+src_pk);Go 与 SQL 两套须逐位一致。
- ⚠️ **附属数据不能挂数据集外键**:`place_images` 曾按 `dataset_id` CASCADE ⇒ 删旧数据集=删图片行、**文件却留在盘上**(读取按坐标键、行按数据集级联,两套口径打架)。已摘掉外键;恢复走后台「图片挂载恢复」,认地点靠旧数据集留下的 `baselines/<id>.sqlite`(**别清 baselines 目录**)。`image_recovery_test.go`(details 卅四)
- ⚠️ **附属数据不能挂数据集外键**:`place_images` 曾按 `dataset_id` CASCADE ⇒ 删旧数据集=删图片行、**文件却留在盘上**(读取按坐标键、行按数据集级联,两套口径打架)。已摘掉外键;恢复走后台「图片挂载恢复」,认地点靠旧数据集留下的 `baselines/<id>.sqlite`(**别清 baselines 目录**);⚠️ 来源必须**全部读进来合并**(places → entity_raw 留档 → 该 sqlite 及**每份 `.bak`**)——换基线会覆盖底座文件,只看当前那份会整片认不出(实测 40 张只挂回 7 张)。`image_recovery_test.go`(details 卅四)
- ⚠️ 恢复**同时扫对象存储**(R2/S3):`media.ListObjects`(ListObjectsV2 + SigV4,查询串先编码再签),
需 `s3:ListBucket` 权限;列不动时本地照救 + warning。**专题同理**:`topics` 已改成归人不归数据集
(`user_id` 过滤,内容仍照生效数据集算)。用户级数据靠 `Dataset.UserID` 定位。(details 卅四)