Files
rond-with-you/.workbuddy/memory/2026-10-01.md
T
tamakyi ff46a9cbb8 统计页把「我的结论」与「访客点赞」拆成两块
用户问「数据统计处的推荐地点和踩雷地点只针对站长吗?应该分开,用户的点赞点踩单独做一块」。
他看到的没错:那张卡的**列表全是站长结论**(verdictPlaces = place_votes),访客票只在副标题里
露了个总数(👍 N · 👎 M)——既看不出哪几个地点被顶上去,也容易被当成两者合并的榜。

- 新增 votes.TopVisitorVoted:按访客票数列地点榜。HAVING 只算**至少有一票的那个方向**,
  否则 0 票的地点会把榜塞满;同票数按 visit_count DESC, p.id 排,刷新之间顺序不跳。
  票按坐标键挂,所以 join 回当前数据集的地点才定位得到(与 Tallies/verdictPlaces 同一口径)。
- 「推荐 & 踩雷(我的结论)」只讲站长,副标题只留「推荐 N · 踩雷 M」,标题与列表都改成「我」的口吻。
- 新增「访客点赞」卡:副标题总票数,两列列 👍/👎 票最多的地点并带票数,各有空态。
- 加载拆成两个 pageJob(verdicts / visitorvotes)并行,两块互不依赖就不该串着等。
- 两块共用同一个可见性开关(IsAdmin || Settings.MarksPublic),与地点页投票区一致。

用例(stats_votes_split_test.go)按卡片标题切出两段 HTML,断言「我标的地点只出现在站长块、
访客顶的地点只出现在访客块」,并核对两边各自的计数——比单看总数更能证明没串台。

⚠️ 又一次同一个坑:两张票表都按坐标键读、与数据集无关,place_visitor_votes 还没有 user_id、
不随删用户清理 ⇒ 上一轮跑剩的票会被算进来、票数越堆越多。新增 isolateVoteState 按 place_key
清两张表(前置 + cleanup 各一次)。

顺带订正 README:那里还写着站长结论「按 src_pk 存」,其实早就是坐标键了。
全量验收 PASS=338 FAIL=0 SKIP=0。
2026-10-01 01:47:48 +08:00

53 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-10-01
## 防复发:把「删数据集带走自建数据」做成结构性门禁(00:30)
用户放弃继续救那批旧图:「我只想避免类似的事件再次发生」。于是把这次的教训固化成三件东西
(`internal/web/dataset_ownership_test.go` + `schema.sql` 抬头 + README 新章节):
1. **归属铁律**:判断标准只有一句——**这份记录能不能从备份重新导入?**
能(places/visits/movements/weather/activities/tags/entity_raw)⇒ 可挂 CASCADE;
不能(place_images/place_notes/place_votes/topics)⇒ 只留 dataset_id 当出处、不许挂外键。
2. **两道门禁**:运行时查 `pg_constraint`(表必须在白名单里,反向也查)+ 静态扫 `schema.sql`
(新建的库走 CREATE TABLE,只看运行时有盲点)。做变异验证时确认了静态那条**真的会响**。
3. **体检常驻后台首页**:库里 N 张 / 本地盘上 M 个 / 落单 K 个(`imageHealth`),落单 >0 直接报警。
事故的本质不是「删数据集」本身,而是**行与对象悄悄对不上、没人发现**——让它当场可见。
变异验证的收获(比测试本身更有价值):
- 运行时门禁单独看**不够**:夹具每次跑迁移,迁移自己就把坏外键摘了 ⇒ 它永远不会在不健康的库上报警。
补了 `TestMigrationDropsStaleDatasetForeignKeys`:注入事故原样的外键再跑迁移,断言被摘掉。
- 这条自愈用例又抓出缺口:`place_notes`/`place_votes` 上同类外键**迁移并不会摘**
(原先只写了 place_images 与 topics 两句 DROP)⇒ 补成四张自建数据表全覆盖。
**部署出去的库要能自愈,DROP 必须覆盖全部同类表。**
- 静态扫描必须先跳过 `--` 注释行:规矩的说明文字里就写着 `REFERENCES datasets(id)`,
不跳过会把自己写的文档当成违规。
顺带订正:后台「删除数据集」的二次确认文案在专题改完后已说过时话(说专题会一起删),改成实话。
### 补记:迁移块排序的老规矩我又踩了一次(00:55)
把「摘外键」那五句 ALTER 收成一段放在 `topics` 之后,而 `place_images` 的 CREATE 在这段**之后**
⇒ 开发库(表已存在)毫无症状,**全新库起不来**:`ALTER TABLE place_images …` 报「关系不存在」。
正是记忆里那条老规矩:「迁移块必须收敛且排在依赖表之后(插在 place_images 前:开发库无症、新库起不来)」。
被 `TestFreshDatabaseServesAppQueries` 当场抓住(这条门存在的意义)。
改成整段排在 `place_images` 建表之后(它是五张里最后一张),并把这个坑写进 schema.sql 的注释里。
教训:**凡是 ALTER 某个表的迁移块,先确认那张表在文件里已经建过了**——
只在本机库上跑一遍是测不出来的,必须跑 fresh-database 用例。
## 统计页:站长结论与访客点赞拆成两块(01:40)
用户问「数据统计处的推荐地点和踩雷地点只针对站长吗?应该分开,用户的点赞点踩单独做一块」。
他看到的没错:那张卡的**列表全是站长结论**,访客票只在副标题露了个总数。
- 新增 `votes.TopVisitorVoted`(`HAVING` 只算至少有一票的方向,否则 0 票地点塞满榜;
同票按 `visit_count DESC, p.id` 保证顺序稳定)。
- 「推荐 & 踩雷(我的结论)」只讲站长;另起一张卡「访客点赞」,列 👍/👎 票最多的地点并带票数。
- 两个 pageJob 并行加载;两块共用 `IsAdmin || MarksPublic`(与地点页投票区一致)。
- 用例按卡片标题切 HTML 断言「A 只在站长块、B 只在访客块」,比数总数更能证明没串台。
⚠️ 又踩同一个坑:票表按坐标键跨数据集读、`place_visitor_votes` 还没有 user_id ⇒
上一轮跑剩的票会被算进来。新增 `isolateVoteState` 按 place_key 清两张表(前置 + cleanup)。
顺带订正 README:那里还写着站长结论「按 src_pk 存」,其实早就是坐标键了。