数据库下载地址:https://github.com/P3TERX/GeoLite.mmdb?tab=readme-ov-file
package addr
import (
"fmt"
"net/netip"
"github.com/oschwald/maxminddb-golang/v2"
)
var db *maxminddb.Reader
func init() {
var err error
db, err = maxminddb.Open("GeoLite2-City.mmdb")
if err != nil {
panic(err)
}
}
// Close 关闭数据库连接
func Close() {
if db != nil {
db.Close()
}
}
func IsInternalIP(addr netip.Addr) bool {
if addr.IsLoopback() {
return true
}
// 处理 IPv6 地址
if !addr.Is4() {
return false
}
ip4 := addr.As4()
return (ip4[0] == 192 && ip4[1] == 168) ||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 32) ||
(ip4[0] == 10) ||
(ip4[0] == 169 && ip4[1] == 254)
}
func GetAddr(ip string) string {
addr, err := netip.ParseAddr(ip)
if err != nil {
return "错误的地址"
}
if IsInternalIP(addr) {
return "内网IP"
}
var record struct {
City struct {
Names map[string]string `maxminddb:"names"`
} `maxminddb:"city"`
Subdivisions []struct {
Names map[string]string `maxminddb:"names"`
} `maxminddb:"subdivisions"`
Country struct {
Names map[string]string `maxminddb:"names"`
} `maxminddb:"country"`
}
// 使用 db.Lookup 获取 Result
lookup := db.Lookup(addr)
if err := lookup.Decode(&record); err != nil {
return "错误的地址"
}
// 获取国家名称作为后备选项
country := record.Country.Names["zh-CN"]
if country == "" {
return "未知地址"
}
// 获取省份名称
var province string
if len(record.Subdivisions) > 0 {
province = record.Subdivisions[0].Names["zh-CN"]
}
// 获取城市名称
city := record.City.Names["zh-CN"]
// 根据可用信息构建地址字符串
if province != "" && city != "" {
return fmt.Sprintf("%s-%s", province, city)
} else if province != "" {
return province
} else if city != "" {
return fmt.Sprintf("%s-%s", country, city)
}
return country
}
使用百度API
package addr
import (
"encoding/json"
"fmt"
"net/http"
"net/netip"
"github.com/zeromicro/go-zero/core/logx"
)
// BaiduAPIResponse 是百度 IP 归属地 API 的返回结构
type BaiduAPIResponse struct {
Status string `json:"status"`
Data []struct {
Location string `json:"location"`
} `json:"data"`
}
// GetAddr 使用百度 API 查询 IP 归属地
func GetAddr(ip string) string {
addr, err := netip.ParseAddr(ip)
if err != nil {
logx.Info("错误IP")
return "错误IP"
}
if IsInternalIP(addr) {
return "内网IP"
}
// 调用百度 API
url := fmt.Sprintf("https://opendata.baidu.com/api.php?query=%s&co=&resource_id=6006&oe=utf8", ip)
resp, err := http.Get(url)
if err != nil {
logx.Errorf("查询 IP 归属地失败: %v", err)
return "查询失败"
}
defer resp.Body.Close()
var result BaiduAPIResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
logx.Errorf("解析 API 响应失败: %v", err)
return "解析失败"
}
if result.Status != "0" || len(result.Data) == 0 {
logx.Errorf("API 返回错误: status=%s", result.Status)
return "API 错误"
}
// 返回归属地信息
return result.Data[0].Location
}
// IsInternalIP 判断是否为内网 IP
func IsInternalIP(addr netip.Addr) bool {
if addr.IsLoopback() {
return true
}
// 处理 IPv6 地址
if !addr.Is4() {
return false
}
ip4 := addr.As4()
return (ip4[0] == 192 && ip4[1] == 168) ||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 32) ||
(ip4[0] == 10) ||
(ip4[0] == 169 && ip4[1] == 254)
}