first release
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// backfillNeed 是被判定为「信息不全」的地点:地址全空(区域黑名单匹配不上、统计里也没城市),
|
||||
// 或者名字为空(地图上显示成「未命名」)。
|
||||
type backfillNeed struct {
|
||||
ID int64
|
||||
Lat, Lon float64
|
||||
Name string
|
||||
Province, City, Distr string
|
||||
}
|
||||
|
||||
// backfillPlaceAddresses 给缺地址 / 没名字的地点补上反查结果。
|
||||
//
|
||||
// 只填空着的列,不覆盖已有值——宁可少改。名字为空时用最近的 POI 命名(和 rond 自己的做法一致),
|
||||
// 周边 200m 没有收录就退回街道 / 乡镇;再取不到就只补地址,名字保持空。
|
||||
// limit 用来兜住高德配额;返回值 filled 是补成功的条数,remain 是还剩下的候选数。
|
||||
func (s *Server) backfillPlaceAddresses(ctx context.Context, datasetID int64, limit int) (filled, remain int, err error) {
|
||||
if s.cfg.GeocodeKey == "" {
|
||||
return 0, 0, nil // 没配 key,这个功能整体关闭
|
||||
}
|
||||
rows, err := s.db.QueryContext(ctx, `SELECT id, lat, lon, COALESCE(name,''),
|
||||
COALESCE(province,''), COALESCE(city,''), COALESCE(district,'')
|
||||
FROM places
|
||||
WHERE dataset_id=$1 AND lat IS NOT NULL AND lon IS NOT NULL
|
||||
AND (COALESCE(city,'')='' OR COALESCE(name,'')='')
|
||||
ORDER BY id`, datasetID)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
var todo []backfillNeed
|
||||
for rows.Next() {
|
||||
var t backfillNeed
|
||||
if err := rows.Scan(&t.ID, &t.Lat, &t.Lon, &t.Name, &t.Province, &t.City, &t.Distr); err != nil {
|
||||
rows.Close()
|
||||
return 0, 0, err
|
||||
}
|
||||
todo = append(todo, t)
|
||||
}
|
||||
rows.Close()
|
||||
if err := rows.Err(); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if len(todo) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
if limit > 0 && len(todo) > limit {
|
||||
remain = len(todo) - limit
|
||||
todo = todo[:limit]
|
||||
}
|
||||
|
||||
for _, t := range todo {
|
||||
if ctx.Err() != nil {
|
||||
break
|
||||
}
|
||||
_, prov, city, dist, street, town, ok := s.amapRegeo(ctx, t.Lat, t.Lon)
|
||||
if !ok {
|
||||
remain++
|
||||
continue
|
||||
}
|
||||
// 名字为空:优先用最近的 POI,退而用街道 / 乡镇
|
||||
name := t.Name
|
||||
if strings.TrimSpace(name) == "" {
|
||||
if poi, _, hit := s.amapNearestPOI(ctx, t.Lat, t.Lon); hit {
|
||||
name = poi
|
||||
} else if street != "" {
|
||||
name = street
|
||||
} else if town != "" {
|
||||
name = town
|
||||
}
|
||||
}
|
||||
// 只填空着的列
|
||||
newProv, newCity, newDist := t.Province, t.City, t.Distr
|
||||
if newProv == "" {
|
||||
newProv = prov
|
||||
}
|
||||
if newCity == "" {
|
||||
newCity = city
|
||||
}
|
||||
if newDist == "" {
|
||||
newDist = dist
|
||||
}
|
||||
if name == t.Name && newProv == t.Province && newCity == t.City && newDist == t.Distr {
|
||||
continue // 反查也没给出新信息
|
||||
}
|
||||
if _, err := s.db.ExecContext(ctx, `UPDATE places SET
|
||||
name = COALESCE(NULLIF($2,''), name),
|
||||
province = COALESCE(NULLIF($3,''), province),
|
||||
city = COALESCE(NULLIF($4,''), city),
|
||||
district = COALESCE(NULLIF($5,''), district)
|
||||
WHERE id=$1`, t.ID, name, newProv, newCity, newDist); err != nil {
|
||||
return filled, remain, err
|
||||
}
|
||||
filled++
|
||||
}
|
||||
return filled, remain, nil
|
||||
}
|
||||
|
||||
// backfillAfterImport 是导入后的自动补全:只处理少量,别让上传等太久。
|
||||
func (s *Server) backfillAfterImport(ctx context.Context, datasetID int64) string {
|
||||
filled, remain, err := s.backfillPlaceAddresses(ctx, datasetID, 20)
|
||||
if err != nil {
|
||||
log.Printf("导入后补全地址失败: %v", err)
|
||||
return ""
|
||||
}
|
||||
if filled == 0 && remain == 0 {
|
||||
return ""
|
||||
}
|
||||
msg := ""
|
||||
if filled > 0 {
|
||||
msg = ";已自动反查补全 " + strconv.Itoa(filled) + " 个缺地址的地点"
|
||||
}
|
||||
if remain > 0 {
|
||||
msg += "(还有 " + strconv.Itoa(remain) + " 个可在后台点「补全缺失地址」继续)"
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// adminBackfillPlaces 手动把当前数据集里缺地址 / 没名字的地点补全一遍。
|
||||
func (s *Server) adminBackfillPlaces(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
ds, err := s.datasetFor(ctx)
|
||||
if err != nil || ds == nil {
|
||||
s.adminFlash(w, r, "err", "还没有导入任何备份")
|
||||
return
|
||||
}
|
||||
if s.cfg.GeocodeKey == "" {
|
||||
s.adminFlash(w, r, "err", "没有配置 geocode_key(高德 Web 服务密钥),无法反查地址")
|
||||
return
|
||||
}
|
||||
filled, remain, err := s.backfillPlaceAddresses(ctx, ds.ID, 100)
|
||||
if err != nil {
|
||||
s.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case filled == 0 && remain == 0:
|
||||
s.adminFlash(w, r, "ok", "没有需要补全的地点:地址与名称都齐全")
|
||||
case remain > 0:
|
||||
s.adminFlash(w, r, "ok", "已补全 "+strconv.Itoa(filled)+" 个地点,还剩 "+strconv.Itoa(remain)+" 个,再点一次继续")
|
||||
default:
|
||||
s.adminFlash(w, r, "ok", "已补全 "+strconv.Itoa(filled)+" 个地点的地址 / 名称")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user