Commit Graph
3 Commits
Author SHA1 Message Date
tamakyi 6b665b6056 图片恢复:坐标来源全部合并,认不出时说清已查过哪些来源
真数据实测(扫描 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。
2026-10-01 00:11:04 +08:00
tamakyi 770e752a7a 图片恢复支持对象存储(R2);专题也改成归人不归数据集
R2 上的图怎么救:库里的行没了就只剩桶里有那些键,不列桶无从得知它们叫什么。
给 internal/media 加了 ListObjects(ListObjectsV2 + SigV4 + 自动翻页 + XML 解析):
- 路径式访问、请求只到桶;查询串必须**先百分号编码再签名**(canonicalQuery 按原样取
  RawQuery,两边得是同一份字节,prefix 里的 / 要写成 %2F)。
- ⚠️ R2 的 API Token 默认只给对象读写,没有 s3:ListBucket 就 403。列桶失败**不让整个
  恢复失败**:本地那半照救,原因进 Warnings、页面用红色提示条显示(否则站长会以为恢复完了)。
- 候选对象 = 本地目录 ∪ 桶里 .webp(同键以本地为准);行的 storage 按「对象实际在哪」写,
  bytes 用桶里报的 Size。另加一条 warning:挂回了却没配 s3_public_base ⇒ 行有了页面空白,
  这比「没恢复」更难懂。

专题(用户拍板「一并做」):topics.dataset_id 同样带 CASCADE,删数据集带走专题,而专题
只存在这张表里、没有恢复来源。外键摘掉,所有查询 dataset_id → user_id(loadTopics、
topicPage、保存/删除/开关、pageBase.HasTopics);**内容仍照当前生效数据集算**
(topicContent 不动)——变的是「这份定义归谁」。Dataset 补 UserID 供前台按属主定位
用户级数据;管理专题不再要求有生效数据集。

导出链路未改动:place_images / topics / Dataset.UserID 都不参与 .rondbackup 导出,
全量验收(含导出保真、ZRAW 夹取、编号对齐那批)PASS=331 FAIL=0 SKIP=0。
2026-09-30 23:31:17 +08:00
tamakyi 496c992e5e 图片不再随数据集删除:加「图片挂载恢复」,place_images 摘掉外键
用户报「之前上传的照片找不到了,应该是因为清了应用数据换基线」。查下来与 App 侧无关,
是站点自己两套口径打架:

- place_images.dataset_id 带 ON DELETE CASCADE,而读取按 place_key 坐标键、跨数据集。
- 于是删掉旧数据集时图片**行**被级联删了,**文件却留在 media 目录里**。
- 实测(本机):datasets 只剩 155 号、place_images 0 行、盘上 4 个 .webp 好端端躺着。
  同一批附属数据里备注(17)与结论(3+1)因为早就迁到坐标键,毫发无损——只有图片倒霉。

三件事:

1) 恢复:后台首页「图片挂载恢复 → 扫描并恢复」(POST /admin/images/recover,
   internal/web/image_recovery.go)。只插行、不删文件、不看数据集,按 object_key 判重可反复点。
   认地点两处来源:数据集还在就查它的 places;数据集已删就读它留下的 baselines/<id>.sqlite
   还原「地点号 → 坐标」。对象键两种历史布局都认(<ds>/<YYYYMM>/<srcPK>-<hash>.webp 与
   <ds>/<srcPK>/<hash>.webp);判据先看文件名有没有 `<数字>-` 前缀,再看中间目录——
   地点号也可能刚好 6 位,只看位数会认错。

2) 防复发:CREATE TABLE place_images 去掉外键 + 幂等 DROP CONSTRAINT;删图接口不再按
   dataset_id 过滤(数据集删了也得能删图);「删除数据集」的二次确认文案改成实话。

3) 报告同类陷阱(未改):topics 仍带 CASCADE,专题随数据集一起删且没有恢复来源。

测试踩到的坑:图片「按坐标键跨数据集读」+「按 object_key 判重」都不看数据集,用例之间会
互相污染两处——盘上文件(报「应当 2 张、实际 10 张」)与库里的行(Already:1 让兜底分支
根本没跑到,用例差点假绿)。加 isolateImageState(换空 DataDir + 按 place_key 清历史行),
并给夹具补收尾清理(外键没了必须自己清)。

新增用例 4 条;验收:./scripts/verify.sh 全量 PASS=323 FAIL=0 SKIP=0。
2026-09-30 19:45:51 +08:00