fix: MMDB中文路径解决方案 + 聚类优先使用最新GeoIP数据

- geoip.py: maxminddb C库不支持Windows中文路径,复制MMDB到ASCII临时目录
- views.py: _cluster_network 优先用 geoip_lookup()(MMDB) 而非上传时缓存(text)
- 效果: IP地理坐标从 ~55城市粗定位 → 全球百万级精确城市定位

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
TianXuan Developer
2026-07-23 23:59:27 +08:00
parent 2e93bcad2e
commit 8ba99292b7
2 changed files with 19 additions and 4 deletions
+16 -2
View File
@@ -12,6 +12,7 @@ import bisect
import gzip import gzip
import logging import logging
import shutil import shutil
import tempfile
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
@@ -107,11 +108,24 @@ def _load_mmdb() -> None:
try: try:
import maxminddb import maxminddb
_mmdb_city = maxminddb.open_database(city_path)
# maxminddb C extension cannot handle non-ASCII Windows paths.
# Workaround: copy MMDB files to a temp dir with ASCII-only path.
_tmp_dir = Path(tempfile.gettempdir()) / 'tianxuan_geoip'
_tmp_dir.mkdir(parents=True, exist_ok=True)
_tmp_city = _tmp_dir / 'GeoLite2-City.mmdb'
if not _tmp_city.exists() or _tmp_city.stat().st_size != city_path.stat().st_size:
logger.info('[GEOIP] copying MMDB to ASCII-safe temp dir …')
shutil.copy2(city_path, _tmp_city)
_mmdb_city = maxminddb.open_database(str(_tmp_city))
logger.info('[GEOIP] loaded GeoLite2-City.mmdb (MMDB format)') logger.info('[GEOIP] loaded GeoLite2-City.mmdb (MMDB format)')
if asn_path.exists(): if asn_path.exists():
_mmdb_asn = maxminddb.open_database(asn_path) _tmp_asn = _tmp_dir / 'GeoLite2-ASN.mmdb'
if not _tmp_asn.exists() or _tmp_asn.stat().st_size != asn_path.stat().st_size:
shutil.copy2(asn_path, _tmp_asn)
_mmdb_asn = maxminddb.open_database(str(_tmp_asn))
logger.info('[GEOIP] loaded GeoLite2-ASN.mmdb (ISP/ORG)') logger.info('[GEOIP] loaded GeoLite2-ASN.mmdb (ISP/ORG)')
except Exception as e: except Exception as e:
logger.warning('[GEOIP] failed to open MMDB: %s', e) logger.warning('[GEOIP] failed to open MMDB: %s', e)
+3 -2
View File
@@ -792,11 +792,12 @@ def _cluster_network(entry: dict, df: pl.DataFrame, meta: dict,
logger.info('Network clustering: %d unique IPs from %d records', n_nodes, len(df)) logger.info('Network clustering: %d unique IPs from %d records', n_nodes, len(df))
# ── 2. GeoIP 解析所有 IP ─────────────────────────────────────────── # ── 2. GeoIP 解析所有 IP ───────────────────────────────────────────
# MMDB优先(高精度),upload时的geo_cache兜底(低精度文本回退)
node_geo = {} # ip → {lat, lon, city, country, isp, org} node_geo = {} # ip → {lat, lon, city, country, isp, org}
for ip in unique_ips: for ip in unique_ips:
info = geo_cache.get(ip) info = geoip_lookup(ip) # 优先用当前加载的MMDB数据库
if not info: if not info:
info = geoip_lookup(ip) info = geo_cache.get(ip) # 回退到上传时的缓存
if info: if info:
node_geo[ip] = info node_geo[ip] = info