代码推送
This commit is contained in:
107
internal/geoip/geoip.go
Normal file
107
internal/geoip/geoip.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ipLocation 缓存的 IP 归属地信息
|
||||
type ipLocation struct {
|
||||
Location string
|
||||
FetchedAt time.Time
|
||||
}
|
||||
|
||||
// Service IP 归属地查询服务(基于 ip-api.com 免费接口 + 内存缓存)
|
||||
type Service struct {
|
||||
cache sync.Map
|
||||
}
|
||||
|
||||
// NewService 创建 IP 归属地查询服务
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
// ipApiResponse ip-api.com 返回结构
|
||||
type ipApiResponse struct {
|
||||
Status string `json:"status"`
|
||||
Country string `json:"country"`
|
||||
RegionName string `json:"regionName"`
|
||||
City string `json:"city"`
|
||||
ISP string `json:"isp"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
// Lookup 查询 IP 归属地,带内存缓存(24小时有效)
|
||||
func (s *Service) Lookup(ip string) string {
|
||||
// 本地/内网 IP 直接返回
|
||||
if ip == "127.0.0.1" || ip == "::1" || ip == "" ||
|
||||
strings.HasPrefix(ip, "192.168.") ||
|
||||
strings.HasPrefix(ip, "10.") ||
|
||||
strings.HasPrefix(ip, "172.") {
|
||||
return "本地网络"
|
||||
}
|
||||
|
||||
// 检查缓存(24小时有效)
|
||||
if v, ok := s.cache.Load(ip); ok {
|
||||
loc := v.(*ipLocation)
|
||||
if time.Since(loc.FetchedAt) < 24*time.Hour {
|
||||
return loc.Location
|
||||
}
|
||||
s.cache.Delete(ip)
|
||||
}
|
||||
|
||||
// 查询 ip-api.com(免费接口,限制 45 次/分钟)
|
||||
location := s.queryIPAPI(ip)
|
||||
if location == "" {
|
||||
return "未知"
|
||||
}
|
||||
|
||||
// 写入缓存
|
||||
s.cache.Store(ip, &ipLocation{
|
||||
Location: location,
|
||||
FetchedAt: time.Now(),
|
||||
})
|
||||
|
||||
return location
|
||||
}
|
||||
|
||||
// queryIPAPI 调用 ip-api.com 查询
|
||||
func (s *Service) queryIPAPI(ip string) string {
|
||||
client := &http.Client{Timeout: 3 * time.Second}
|
||||
url := fmt.Sprintf("http://ip-api.com/json/%s?lang=zh-CN", ip)
|
||||
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result ipApiResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if result.Status != "success" {
|
||||
return ""
|
||||
}
|
||||
|
||||
parts := []string{}
|
||||
if result.Country != "" {
|
||||
parts = append(parts, result.Country)
|
||||
}
|
||||
if result.RegionName != "" {
|
||||
parts = append(parts, result.RegionName)
|
||||
}
|
||||
if result.City != "" {
|
||||
parts = append(parts, result.City)
|
||||
}
|
||||
if result.ISP != "" {
|
||||
parts = append(parts, result.ISP)
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
Reference in New Issue
Block a user