8.6 KiB
2026-10-12
专题的「迷雾显示区域」改成一组圆(多圆 + 地图点选)
用户提出两点:① 显示区不该只有一个圆,应该能圈多个;② 应该在地图上点选, 而不是让他手填经纬度和半径。
方案(先出清单获批,分批落地)
先摸清了现状:单圆硬编码在 6 处(topics 三列 / HasArea / topicAreaCond /
fitParams / 后台三个输入框 / topic.html 的 fogArea)。同时发现项目里已经有
一套现成的地图选圆交互(/admin/fog-mask 那个页面:点选圆心 + 拖拽 + 半径可调 +
多圆列表),可以直接抄它的模式。而 fog_masks 是另一个语义(圈内迷雾对访客隐藏),
用户明确这轮不动它。
三个岔路口问过用户,都选了推荐项:只改专题(不做站点级)、后台做成专题表单内嵌地图 (不是独立管理页)、不动遮罩区。
原计划 B1(存 areas)/B2(查询认多圆)之间会留一个「存了但只认第一个圆」的陷阱状态, 所以合并成一批:数据层与查询层一起做,后台先用兼容桥(仍手填单圆,落进 areas)。
落地(三个提交)
1bb35db后端与查询层:topics.areasjsonb 数组 + 存量回填;Topic.AreaList()归一(改为空格时退回老的三列,未回填的老行与直接构造 Topic 的调用方都继续工作);topicAreaCond改成 N 个圆的 OR;fitParams改用所有圆的并集外接矩形。44763df前台渲染:clip-path从circle()换成path()(多子路径、同绕向 ⇒ nonzero 下自然并集);拼路径抽成fog-clip.js以便跑 node 单测;顺带清掉已死的fitCircle;verify.sh挂进前端单测。- 待办(B3):后台地图编辑器还没做(专题表单目前仍是三个手填输入框)—— 这是用户最初诉求的第二半,下一批做。
关键取舍
- 为什么不单开一张表:圆是专题定义的组成部分、与专题一起读写、没有独立生命周期;
单开表还要过一遍「能不能从备份重导」的归属口径、改
dataset_ownership_test白名单、 注意迁移块顺序。jsonb 一次全绕开。 - 废弃列保留并镜像第一个圆:回滚旧二进制时退化成「第一个圆」总比「没有区域」强。
fitCircle删掉:多圆表达不了「圆心 + 半径」,留着就是两套并行的贴合口径。
只在这轮踩到的坑
- CSSOM 会把
clip-path规范化:style.clipPath读回来被补上空格 (path("M 405 296 a 87 87 …")),且位置不固定(296 a87)、第一个子路径尾部 还可能多个空格。想解析回几何必须每处分隔都用\s*/\s+容忍,别写死 —— 我连着写错两版正则(先是Z$卡在尾部空格)。 --clip截图的pad=10(×scale)又咬了一次:这次用自校准绕开的 —— 用单圆掩码反解出 scale 与 offset(实测 2.0 / 19.2),不再手算元素坐标。
后台的显示区编辑器(B3,用户诉求的后半)
三个手填输入框(中心纬度/经度/半径)换成地图 + 圆列表:点地图加圆、拖圆心移动、
拖圆边改半径、列表里可改半径/定位/删除、可一次清空;改动立刻写进隐藏字段
areas(JSON) 与 crs,随专题表单一起提交。复用 /admin/fog-mask 那套约定
(/api/map-config 取底图、底图坐标系随表单带上、后端换算成 GCJ-02),不另起炉灶。
实测抓到的 bug(探针一跑就现形)
/admin/topics 没设 p.HasMap = true ⇒ 布局不引入 Leaflet。表现极隐蔽:
脚本加载了、rondTopicAreas 也在,就是 window.L 永远 undefined,init 静默返回、
控制台不报错、页面看着像「什么都没发生」。已加守卫
TestTopicsAdminPageLoadsLeaflet(按函数体截取后断言含 HasMap = true)。
记忆里那条「带地图页须 p.HasMap=true」是真的会咬人。
两个设计上的取舍
- areas 的初值渲染进隐藏字段(
value="{{.Form.AreasJSON}}"),JS 也从隐藏字段读初值, 而不是另传一份。这样脚本没加载上时,表单提交的仍是服务端渲染的原值 —— 否则一次保存就把已有显示区静默清空。守卫TestTopicAdminUsesMapEditorNotCoordInputs钉住了「初值来源」与「旧输入框必须没了」。 - 上限两边各写一份(前端
MAX_AREAS/MIN_KM/MAX_KMvs 后端 Go 常量),并加守卫TestTopicAreaLimitsMatchBetweenGoAndJS直接比对数字。不一致的表现很别扭: 前端放行、后端拒绝,用户看到一句红字却不知道改哪里。
小坑
- 半径输入框套了一层定宽容器:站点全局 input 规则是
width:100%且带 10 个:not()(特异性 (0,11,1)),直接给 input 写宽度压不过。 topic-areas.js只把纯函数(校验/序列化/增删改/几何换算)暴露出来供 node 单测, 另加一个最小测试钩子api._map供无头验证(同window.rondMap._state惯例)—— 不给把手就只能真拿鼠标点地图,CDP 下很难稳定复现。
gofmt / 行尾
gofmt -l 报的整文件重写有两层原因,已一并处理:
① Go 1.19 起 gofmt 会重排文档注释里的缩进列表(我写的 // ① … 被要求改成 tab 缩进块);
② 工作区是 CRLF 而 .gitattributes 声明 eol=lf。
gofmt -w 归一后 git diff 没有额外噪声 —— 说明 blob 本来就是 LF,CRLF 只是检出产物,
之前担心的「模板 CRLF 与 .gitattributes 冲突」不是仓库内容问题。
记忆压缩
MEMORY.md 压到 8919(原 9285):合并「归属口径/防复发」与「跨备份/附属数据」两节
(同属归属)、若干长条目的理由下沉 details(规矩一字未删)、收紧自己新加的那条。
12 节 → 11 节。
修 bug:半径输入框被浏览器原生校验误判
用户反馈「输入半径总是提示『请输入一个有效的值,最接近的两个有效值为 XX 和 XX』」。
根因:<input type="number"> 的 step 是相对 min 递增的,不是「step 的整数倍」。
min=0.05 + step=0.5 ⇒ 合法值只有 0.05 / 0.55 / 1.05 …,于是 3、3.5、7.25 全被判非法。
关键是这是浏览器原生提示:JS 与后端都拦不到,我们自己的校验根本没机会说话。
实测对照(同一元素只换 step):
step=0.5 → 0.05✓ 1✗ 3✗ 3.5✗ 7.25✗ 13.37✗;step=0.05 → 13.37 仍✗;step=any → 全✓。
采用 step="any",保留 min/max(真实边界 + spinner 范围),把校验交给 JS/后端。
加了一条通配守卫(TestNumberInputStepNeverMismatchesMin):扫所有模板的数字框,
凡「有 min 且有数值 step」就要求 min 是 step 的整数倍(或 step="any");
JS 动态建的输入框模板扫不到,另用 TestTopicAreaRadiusInputAllowsAnyStep 钉住。
变异验证过(step 改回 0.5 ⇒ 守卫立刻红)。全站其它数字框(radius_m min=50/step=50)
本来就合法,未动。
顺带:把 admin/topics.html 的工作区行尾归一到 LF。git add 之后零改动 ⇒
再次确认 blob 本来就是 LF,CRLF 只是检出产物(那条 CRLF 警告是 stat 缓存假象)。
推送这个仓库要认证(非交互环境会卡住)
用户让「提交一次」时本地其实已经全部提交完了(每批做完就提交),工作区干净;
唯一没做的是推送。结果 git push origin master 卡了 7 分钟没有任何输出。
诊断(下次直接照这个查):
- 远端
git.shiroko.one的 读是匿名的(upload-packHTTP 200)、 写要认证(receive-packHTTP 401)⇒ 所以git fetch一切正常、唯独 push 卡住。 tasklist里能看到git-credential-helper-selector(有可见窗口,标题CredentialHelperSelector),git config credential.helper=helper-selector+ PortableGit 自带的git-credential-manager.exe。本机从未推过这个仓库 ⇒ 触发选择器弹窗等人。- 判据:
netstat里到远端的连接根本没建立(ESTABLISHED 全是本机 postgres), 说明停在认证之前,不是网络慢。 - ⚠️
git push | tail -N会把进度缓冲住,看起来「一片空白」——排查时别用管道。
处置:结束掉 push 那几个进程(不动本地提交,也不会留 .git/*.lock),
由用户在自己的终端里推(交互式提示看得见)。
结论:以后不要在这个环境里代跑 git push,直接把命令交给用户。