package ip_tool import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "time" "github.com/pkg/errors" ) type HuaChenIpClient struct { AppCode string Host string cache ICacheAdapter } func NewHuaChenIpClient(appCode string, cache ICacheAdapter) *HuaChenIpClient { return &HuaChenIpClient{ AppCode: appCode, Host: "https://c2ba.api.huachen.cn", cache: cache, } } func (h *HuaChenIpClient) GetIpInfo(ip string) (res *ApiResult, err error) { if ip == "" { return nil, errors.New("请输入ip地址") } // 设置参数 params := url.Values{} params.Add("ip", ip) // 拼接URL var fullURL string fullURL, err = url.JoinPath(h.Host, "ip") if err != nil { return nil, errors.Wrapf(err, "获取ip:%s信息失败,拼接路径错误", ip) } // 拼接参数 fullURL = fmt.Sprintf("%s?%s", fullURL, params.Encode()) // 创建HTTP客户端 client := &http.Client{} // 创建请求 var req *http.Request req, err = http.NewRequest(http.MethodGet, fullURL, nil) if err != nil { return nil, errors.Wrapf(err, "获取ip:%s信息失败,创建请求失败", ip) } // 设置请求头 req.Header.Add("Authorization", "APPCODE "+h.AppCode) req.Header.Add("Content-Type", "application/json") // 发送请求 client.Timeout = 5 * time.Second var resp *http.Response resp, err = client.Do(req) if err != nil { return nil, errors.Wrapf(err, "获取ip:%s信息失败,发送请求失败", ip) } defer func(Body io.ReadCloser) { err = Body.Close() if err != nil { log.Printf("获取ip:%s信息,关闭响应体失败: %+v\n", ip, err) } }(resp.Body) // 读取响应体 var body []byte body, err = io.ReadAll(resp.Body) if err != nil { return nil, errors.Wrapf(err, "获取ip:%s信息失败,读取响应体失败", ip) } // 解析JSON响应 var apiResult ApiResult err = json.Unmarshal(body, &apiResult) if err != nil { return nil, errors.Wrapf(err, "获取ip:%s信息失败,解析JSON响应失败,%s", ip, string(body)) } // 检查API返回状态 if apiResult.Ret != 200 { return &apiResult, errors.New(fmt.Sprintf("获取ip:%s信息失败,%+v", ip, apiResult)) } return &apiResult, nil } func (h *HuaChenIpClient) GetIpInfoFormCache(ctx context.Context, ip string, ttl time.Duration) (res *ApiResult, err error) { getCache, err := h.cache.Get(ctx, h.ipKey(ip)) if err != nil { return nil, errors.Wrapf(err, "获取缓存失败,ip:%s", ip) } if getCache != nil { return getCache, nil } res, err = h.GetIpInfo(ip) if err != nil { return nil, errors.Wrapf(err, "获取ip:%s信息失败,", ip) } var infoJson []byte infoJson, err = json.Marshal(res) if err != nil { return nil, errors.Wrapf(err, "无法将IP信息转换为JSON,ip:%s", ip) } err = h.cache.Set(ctx, h.ipKey(ip), string(infoJson), ttl) if err != nil { return nil, errors.Wrapf(err, "缓存ip:%s信息失败,", ip) } return } // ipKey 生成Redis key func (h *HuaChenIpClient) ipKey(ip string) string { return fmt.Sprintf("ip:%s", ip) } type ApiResult struct { Ret int `json:"ret"` Msg string `json:"msg"` Data IpInfo `json:"data"` LogId string `json:"log_id"` } type IpInfo struct { Ip string `json:"ip"` LongIp string `json:"long_ip"` Isp string `json:"isp"` Area string `json:"area"` RegionId string `json:"region_id"` Region string `json:"region"` CityId string `json:"city_id"` City string `json:"city"` District string `json:"district"` DistrictId string `json:"district_id"` CountryId string `json:"country_id"` Country string `json:"country"` Lat string `json:"lat"` Lng string `json:"lng"` }