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 |
|
tamakyi
|
52a82631da
|
换基线:说清「新底座里多出来的到访不会进导出包」,并把它钉成用例
写文案时去核这条,先把一条写错的断言纠正了:导出条数本来就不等于站点条数。
tableWriter.finish() 是两段:先删「基线里有、PG 已没有」的行(known − seen),
再把「导入时进不了 PG 但留档还在」的行(w.skipped × raws)按原样补回——否则
「导入→导出」一趟会凭空少数据。实测 823 条到访的数据集导出包里是 824 条,差值正常。
(第一版成功路径用例断言「导出 == 站点」,红在 824 != 823 就是这个原因;已改成更锋利的
「换底座前后导出包里 ZVISIT 的 UUID 集合逐条一致」。)
同一段代码定下了换基线的语义:新底座里站点没有的行会被清掉(进 known 没进 seen)。
所以换一份更新的手机包**不会**把手机新记录带进导出包,要带得先走「分析差异」合并。
页面提示与 flash 文案都写明了这点,并新增用例
TestSwapBaselineDoesNotCarryRowsTheSiteDoesNotHave 钉住它(站内补一条到访 → 导成包 →
从站内删掉 → 换底座 → 断言导出里既没有那条的派生 UUID、UUID 集合也与换之前逐条一致)。
验收:./scripts/verify.sh 全量 PASS=319 FAIL=0 SKIP=0。
|
2026-09-30 19:15:48 +08:00 |
|
tamakyi
|
6ff6f944a1
|
换基线:把「底座换代」做进后台(站点数据一行不动)
导出是「底座为底 + 只覆盖托管表」,所以底座必须是手机导出的包。底座跟着数据集走,
手机重新导出后想换代,以前只能整包重新导入成新数据集(站点补录的行、主题、备注都得重收拾)。
新动作 /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。
|
2026-09-30 19:00:18 +08:00 |
|
tamakyi
|
11e00a5b60
|
记忆:记下本次抽出的跨项目技能 workbuddy-memory-versioning
|
2026-09-30 18:32:14 +08:00 |
|
tamakyi
|
3f1b737a78
|
记忆:把 .workbuddy/memory 纳入版本控制
用户要求这份记忆跟着仓库走,而不是只留在本机。
.gitignore 里挡路的是 `.workbuddy/`。改成反忽略六行,并且**只放行 .md**:
别用 `!.workbuddy/memory/**`——它会把 memory 下任何文件都放行,本项目真发生过
`web.test.exe` 混进暂存。复核用 `git check-ignore -q --no-index` 逐条打靶:
memory/*.png、memory/sub/x.md、memory/MEMORY.md.bak、plans/a.md 全 IGNORED,
17 个 .md 全 included;`git add -An .workbuddy/` 恰好 16 个文件。
其余 .workbuddy 内容(计划、本地设置、临时产物)继续忽略。记忆里没有真凭据
(唯一命中是 2026-09-14.md 里的 `password=rond-admin`,那是项目默认口令)。
顺带做两件整理:
1. 取证了注入上限。以前按「字节数」判断 MEMORY.md 是否超限是错的:
WorkBuddy 的 `getWorkingMemoryContent()` 用 `MAX_MEMORY_CHARS = 1e4` 比 JS
`content.length`,即**字符数**(中文 1 字算 1);超了 substring 截断、尾部静默丢失。
本 details 与每日日志都不参与注入,只有 MEMORY.md 受约束。
现已从 6604 字符收到上限的 66%,并把这条上限写进索引抬头。
2. 把「备份合并」「导出/恢复三条铁律」两段长文迁进 MEMORY-details.md 的卅一/卅二节,
索引只留关键词指针。行尾统一为 LF(仓库 .gitattributes 是 `* text=auto eol=lf`)。
|
2026-09-30 18:31:08 +08:00 |
|