Files
go-web-gin/utils/location.go

107 lines
4.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"context"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"time"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
// LocationResponse represents the response from IP location API
type LocationResponse struct {
IP string `json:"ip"`
Pro string `json:"pro"`
ProCode string `json:"proCode"`
City string `json:"city"`
CityCode string `json:"cityCode"`
Region string `json:"region"`
RegionCode string `json:"regionCode"`
Addr string `json:"addr"`
RegionNames string `json:"regionNames"`
Err string `json:"err"`
}
// userAgents is a list of common browser user agents for random selection
var userAgents = []string{
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
}
// randomUserAgent returns a random user agent string
func randomUserAgent() string {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
return userAgents[r.Intn(len(userAgents))]
}
// GetLocation 获取IP的地理位置信息
// 调用太平洋网络IP地址查询API返回省份和城市
func GetLocation(ctx context.Context, ip string) (province string, city string, err error) {
url := fmt.Sprintf("https://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true", ip)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return "", "", fmt.Errorf("create request failed: %w", err)
}
// Set random user agent to simulate browser
req.Header.Set("User-Agent", randomUserAgent())
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Referer", "https://whois.pconline.com.cn/")
client := &http.Client{
Timeout: 5 * time.Second,
}
resp, err := client.Do(req)
if err != nil {
return "", "", fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", "", fmt.Errorf("request failed with status: %d", resp.StatusCode)
}
// API返回的是GBK编码需要转换为UTF-8
utf8Reader := transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())
body, err := io.ReadAll(utf8Reader)
if err != nil {
return "", "", fmt.Errorf("read response failed: %w", err)
}
var location LocationResponse
if err := json.Unmarshal(body, &location); err != nil {
return "", "", fmt.Errorf("parse response failed: %w", err)
}
// Check for error in response
if location.Err != "" {
return "", "", fmt.Errorf("API error: %s", location.Err)
}
return location.Pro, location.City, nil
}
// GetLocationWithCache 获取IP的地理位置信息带缓存
// 如果缓存中有则直接返回否则调用API并缓存结果
func GetLocationWithCache(ctx context.Context, ip string, cache func(key string, fetch func() (string, string, error)) (string, string, error)) (province string, city string, err error) {
return cache(ip, func() (string, string, error) {
return GetLocation(ctx, ip)
})
}