/** * package utils * 作用:IP归属地查询工具 * 说明:使用第三方API查询IP归属地信息 */ package utils import ( "encoding/json" "fmt" "io" "net/http" "strings" "time" ) // IPLocationInfo IP归属地信息 type IPLocationInfo struct { Country string `json:"country"` // 国家 Region string `json:"region"` // 省份 City string `json:"city"` // 城市 ISP string `json:"isp"` // 运营商 FullLocation string `json:"full_location"` // 完整归属地 } // 缓存结构(简单内存缓存) var ( ipCache = make(map[string]*IPLocationInfo) cacheTimeout = 24 * time.Hour ) /** * GetIPLocation * 功能:获取IP归属地信息 * @param ip IP地址 * @returns 归属地信息字符串 */ func GetIPLocation(ip string) string { // 排除本地IP if isLocalIP(ip) { return "本地" } // 检查缓存 if info, ok := ipCache[ip]; ok { return info.FullLocation } // 查询IP归属地 info := queryIPLocation(ip) if info != nil { // 构建完整归属地字符串 parts := []string{} if info.Country != "" { parts = append(parts, info.Country) } if info.Region != "" { parts = append(parts, info.Region) } if info.City != "" { parts = append(parts, info.City) } if info.ISP != "" { parts = append(parts, info.ISP) } if len(parts) > 0 { info.FullLocation = strings.Join(parts, " ") } else { info.FullLocation = "未知" } // 存入缓存 ipCache[ip] = info return info.FullLocation } return "未知" } /** * isLocalIP * 功能:判断是否为本地IP */ func isLocalIP(ip string) bool { // 本地回环地址 if ip == "127.0.0.1" || ip == "localhost" || ip == "::1" { return true } // 内网地址 if strings.HasPrefix(ip, "192.168.") || strings.HasPrefix(ip, "10.") || strings.HasPrefix(ip, "172.16.") || strings.HasPrefix(ip, "172.17.") || strings.HasPrefix(ip, "172.18.") || strings.HasPrefix(ip, "172.19.") || strings.HasPrefix(ip, "172.20.") || strings.HasPrefix(ip, "172.21.") || strings.HasPrefix(ip, "172.22.") || strings.HasPrefix(ip, "172.23.") || strings.HasPrefix(ip, "172.24.") || strings.HasPrefix(ip, "172.25.") || strings.HasPrefix(ip, "172.26.") || strings.HasPrefix(ip, "172.27.") || strings.HasPrefix(ip, "172.28.") || strings.HasPrefix(ip, "172.29.") || strings.HasPrefix(ip, "172.30.") || strings.HasPrefix(ip, "172.31.") { return true } return false } /** * queryIPLocation * 功能:查询IP归属地(使用ip-api.com免费API) */ func queryIPLocation(ip string) *IPLocationInfo { // 使用ip-api.com免费API(限制:每分钟45次请求) url := fmt.Sprintf("http://ip-api.com/json/%s?lang=zh-CN&fields=status,message,country,regionName,city,isp", ip) client := &http.Client{ Timeout: 3 * time.Second, } resp, err := client.Get(url) if err != nil { return nil } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil } var result struct { Status string `json:"status"` Message string `json:"message"` Country string `json:"country"` Region string `json:"regionName"` City string `json:"city"` ISP string `json:"isp"` } if err := json.Unmarshal(body, &result); err != nil { return nil } if result.Status != "success" { return nil } return &IPLocationInfo{ Country: result.Country, Region: result.Region, City: result.City, ISP: result.ISP, } }