feat: 排行榜不再展示任何IP,用户列显示地区(ip2region v4 离线库)+浏览器(UA解析);IP仅后台保存,页面上方显示IP+地区

This commit is contained in:
dsh
2026-08-17 23:10:02 -04:00
parent 780e84e67e
commit d8917a666a
9 changed files with 229 additions and 83 deletions
+124
View File
@@ -0,0 +1,124 @@
package geo
import (
"log"
"os"
"strings"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
)
// Geo 基于 ip2region v4 离线库的 IP 地区解析器(纯本地,无外部依赖)
type Geo struct {
searcher *xdb.Searcher
}
// New 按顺序尝试数据文件路径加载;全部失败时返回可用但禁用的实例(地区解析优雅降级)
func New(paths ...string) *Geo {
var dbPath string
for _, p := range paths {
if _, err := os.Stat(p); err == nil {
dbPath = p
break
}
}
if dbPath == "" {
log.Printf("[geo] 未找到 ip2region 数据文件(尝试: %v),地区解析已禁用", paths)
return &Geo{}
}
handle, err := os.Open(dbPath)
if err != nil {
log.Printf("[geo] 打开 %s 失败: %v", dbPath, err)
return &Geo{}
}
defer handle.Close()
header, err := xdb.LoadHeader(handle)
if err != nil {
log.Printf("[geo] 读取 %s 头部失败: %v", dbPath, err)
return &Geo{}
}
version, err := xdb.VersionFromHeader(header)
if err != nil {
log.Printf("[geo] 识别 %s 版本失败: %v", dbPath, err)
return &Geo{}
}
// 整库载入内存,查询性能最好(约 11MB)
buff, err := xdb.LoadContentFromFile(dbPath)
if err != nil {
log.Printf("[geo] 加载 %s 数据失败: %v", dbPath, err)
return &Geo{}
}
searcher, err := xdb.NewWithBuffer(version, buff)
if err != nil {
log.Printf("[geo] 初始化搜索器失败: %v", err)
return &Geo{}
}
log.Printf("[geo] ip2region 已加载(%.0f MB%s", float64(len(buff))/1024/1024, dbPath)
return &Geo{searcher: searcher}
}
// Search 返回友好地区名(如 "广东省深圳市 电信"、"新加坡");解析失败返回 ""
func (g *Geo) Search(ip string) string {
if g == nil || g.searcher == nil || ip == "" {
return ""
}
region, err := g.searcher.Search(ip)
if err != nil {
return ""
}
return Format(region)
}
// Format 把 ip2region v4 原始格式 "国家|省|市|ISP|国家码" 转为友好显示
// 例:中国|江苏省|南京市|0|CN → "江苏省南京市";中国|上海|上海市|电信|CN → "上海市 电信"
// United States|California|0|Google LLC|US → "United States California · Google LLC"
func Format(region string) string {
parts := strings.Split(region, "|")
if len(parts) < 5 {
return region
}
country, province, city, isp := parts[0], parts[1], parts[2], parts[3]
if country == "" || country == "0" || country == "Reserved" {
return "" // 保留地址/内网/IPv6 不在 v4 库中
}
if isp == "0" {
isp = ""
}
if country == "中国" {
loc := ""
switch {
case province != "" && province != "0" && city != "" && city != "0":
if city == province || strings.Contains(city, province) {
loc = city // 直辖市:"上海市"
} else {
loc = province + city // "江苏省南京市"
}
case province != "" && province != "0":
loc = province
default:
loc = "中国"
}
if isp != "" {
loc += " " + isp
}
return loc
}
// 国外:国家 + 州/市(英文)
loc := country
switch {
case province != "" && province != "0":
loc += " " + province
case city != "" && city != "0":
loc += " " + city
}
if isp != "" {
loc += " · " + isp
}
return loc
}