地点补全:按地名与坐标猜活动与类别,只填空

后台新增「地点补全」页,把「名下到访没设活动」和「类别为空」的地点列出来,
给出建议、由站长确认后再写。判断分三层:

- 本地关键词表:零延迟、离线,命中即高置信(「星巴克咖啡」→ 奶茶和甜甜水)
- 高德周边搜索:按坐标问「这里是什么 POI」,比按名字搜准;
  拿到的类型再映射成活动与 MapKit 类别
- 人工:都猜不出的留空,站长自己填;填了列表里没有的名字会自动建成新活动类型

两条底线:**只填空**(已有活动或类别的一律不动);**活动按地点批量设**,
一次把它名下所有没设活动的到访一起填上,否则同一个地点会在统计里分裂成两种活动。

类别写的是 Apple MapKit 的枚举名(真机确认用过 12 种),不是高德分类。

规则上踩过两个坑,都写进注释了:匹配前要先掐掉括号里的地址(否则
「尚优居客栈(大理古城步行街店)」会命中「步行街」),并且按词长优先
(否则「马鞍山公园」会命中「山」);单字关键词一律不要,误命中率太高。
This commit is contained in:
tamakyi
2026-09-19 16:31:01 +08:00
parent 30c502ce7b
commit 6c061a0548
8 changed files with 1012 additions and 3 deletions
+4
View File
@@ -56,6 +56,7 @@ var pageFiles = map[string]struct {
"admin/fog-mask": {[]string{"templates/layout.html", "templates/admin/fog-mask.html"}, "layout"},
"admin/fog-preview": {[]string{"templates/layout.html", "templates/admin/fog-preview.html"}, "layout"},
"admin/backup-preview": {[]string{"templates/layout.html", "templates/admin/backup-preview.html"}, "layout"},
"admin/fillup": {[]string{"templates/layout.html", "templates/admin/fillup.html"}, "layout"},
"admin/notes": {[]string{"templates/layout.html", "templates/admin/notes.html"}, "layout"},
"admin/edit": {[]string{"templates/layout.html", "templates/admin/edit.html"}, "layout"},
"admin/topics": {[]string{"templates/layout.html", "templates/admin/topics.html"}, "layout"},
@@ -279,6 +280,9 @@ func (s *Server) routes() {
s.mux.HandleFunc("GET /admin/export/places.geojson", s.requireAdmin(s.exportGeoJSON))
s.mux.HandleFunc("GET /admin/export/places.gpx", s.requireAdmin(s.exportGPX))
s.mux.HandleFunc("GET /admin/export/rondbackup", s.requireAdmin(s.exportRondbackup))
s.mux.HandleFunc("GET /admin/fillup", s.requireAdmin(s.adminFillupPage))
s.mux.HandleFunc("POST /admin/fillup/apply", s.requireAdmin(s.adminFillupApply))
s.mux.HandleFunc("POST /admin/fillup/amap", s.requireAdmin(s.adminFillupAmap))
s.mux.HandleFunc("GET /admin/settings", s.requireAdmin(s.settingsPage))
s.mux.HandleFunc("POST /admin/settings", s.requireAdmin(s.settingsSubmit))
s.mux.HandleFunc("GET /admin/password", s.requireAdmin(s.passwordPage))