Files
jiang13-forum/service/geo_zh.go
freefire 429956c594 feat: 管理端网站监控,浏览量写入独立 monitor.db
请求日志按日 JSONL;page_views 不进主库,避免统计数据撑大 jiang13.db。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 07:55:45 +08:00

259 lines
7.2 KiB
Go
Raw 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 service
import (
"strconv"
"strings"
)
// countryZh ISO2 → 中文国名(展示用;存储仍用 ISO
var countryZh = map[string]string{
"CN": "中国", "HK": "中国香港", "MO": "中国澳门", "TW": "中国台湾",
"US": "美国", "JP": "日本", "KR": "韩国", "SG": "新加坡",
"GB": "英国", "DE": "德国", "FR": "法国", "RU": "俄罗斯",
"CA": "加拿大", "AU": "澳大利亚", "IN": "印度", "TH": "泰国",
"VN": "越南", "MY": "马来西亚", "ID": "印度尼西亚", "PH": "菲律宾",
"NL": "荷兰", "IT": "意大利", "ES": "西班牙", "BR": "巴西",
"MX": "墨西哥", "SE": "瑞典", "CH": "瑞士", "PL": "波兰",
"TR": "土耳其", "SA": "沙特阿拉伯", "AE": "阿联酋", "NZ": "新西兰",
"IE": "爱尔兰", "BE": "比利时", "AT": "奥地利", "NO": "挪威",
"DK": "丹麦", "FI": "芬兰", "PT": "葡萄牙", "CZ": "捷克",
"UA": "乌克兰", "IL": "以色列", "ZA": "南非", "AR": "阿根廷",
}
type cnRegionMeta struct {
Name string
ISO string
}
// cnRegionMap 中国省级英文名/别名 → 中文名 + 地图 ISO
var cnRegionMap = map[string]cnRegionMeta{
"beijing": {Name: "北京", ISO: "BJ"},
"peking": {Name: "北京", ISO: "BJ"},
"tianjin": {Name: "天津", ISO: "TJ"},
"shanghai": {Name: "上海", ISO: "SH"},
"chongqing": {Name: "重庆", ISO: "CQ"},
"chungking": {Name: "重庆", ISO: "CQ"},
"hebei": {Name: "河北", ISO: "HE"},
"shanxi": {Name: "山西", ISO: "SX"},
"liaoning": {Name: "辽宁", ISO: "LN"},
"jilin": {Name: "吉林", ISO: "JL"},
"heilongjiang": {Name: "黑龙江", ISO: "HL"},
"jiangsu": {Name: "江苏", ISO: "JS"},
"zhejiang": {Name: "浙江", ISO: "ZJ"},
"anhui": {Name: "安徽", ISO: "AH"},
"fujian": {Name: "福建", ISO: "FJ"},
"jiangxi": {Name: "江西", ISO: "JX"},
"shandong": {Name: "山东", ISO: "SD"},
"henan": {Name: "河南", ISO: "HA"},
"hubei": {Name: "湖北", ISO: "HB"},
"hunan": {Name: "湖南", ISO: "HN"},
"guangdong": {Name: "广东", ISO: "GD"},
"guangxi": {Name: "广西", ISO: "GX"},
"hainan": {Name: "海南", ISO: "HI"},
"sichuan": {Name: "四川", ISO: "SC"},
"guizhou": {Name: "贵州", ISO: "GZ"},
"yunnan": {Name: "云南", ISO: "YN"},
"xizang": {Name: "西藏", ISO: "XZ"},
"tibet": {Name: "西藏", ISO: "XZ"},
"shaanxi": {Name: "陕西", ISO: "SN"},
"shaanxi province": {Name: "陕西", ISO: "SN"},
"gansu": {Name: "甘肃", ISO: "GS"},
"qinghai": {Name: "青海", ISO: "QH"},
"ningxia": {Name: "宁夏", ISO: "NX"},
"xinjiang": {Name: "新疆", ISO: "XJ"},
"inner mongolia": {Name: "内蒙古", ISO: "NM"},
"nei mongol": {Name: "内蒙古", ISO: "NM"},
"hong kong": {Name: "香港", ISO: "HK"},
"macau": {Name: "澳门", ISO: "MO"},
"macao": {Name: "澳门", ISO: "MO"},
"taiwan": {Name: "台湾", ISO: "TW"},
}
// cityZh 常见城市英文名 → 中文
var cityZh = map[string]string{
"beijing": "北京", "peking": "北京",
"shanghai": "上海",
"chongqing": "重庆", "chungking": "重庆",
"tianjin": "天津",
"hangzhou": "杭州",
"shenzhen": "深圳",
"guangzhou": "广州", "canton": "广州",
"chengdu": "成都",
"wuhan": "武汉",
"xi'an": "西安", "xian": "西安",
"nanjing": "南京", "nanking": "南京",
"suzhou": "苏州",
"qingdao": "青岛",
"dalian": "大连",
"ningbo": "宁波",
"xiamen": "厦门", "amoy": "厦门",
"changsha": "长沙",
"zhengzhou": "郑州",
"jinan": "济南",
"harbin": "哈尔滨",
"shenyang": "沈阳",
"kunming": "昆明",
"nanning": "南宁",
"urumqi": "乌鲁木齐",
"lhasa": "拉萨",
"hohhot": "呼和浩特",
"taipei": "台北",
"hong kong": "香港",
}
// asnOrgZh 常见 ASN / AS 组织名 → 中文运营商
var asnOrgZh = map[uint]string{
4134: "中国电信",
4812: "中国电信",
4837: "中国联通",
4808: "中国联通",
9808: "中国移动",
56040: "中国移动",
56041: "中国移动",
56042: "中国移动",
4538: "教育网",
23910: "教育网",
58539: "教育网",
7497: "教育网",
}
var asnOrgKeywordZh = []struct {
kw string
zh string
}{
{"chinanet", "中国电信"},
{"china telecom", "中国电信"},
{"chinatelecom", "中国电信"},
{"unicom", "中国联通"},
{"china unicom", "中国联通"},
{"mobile", "中国移动"},
{"china mobile", "中国移动"},
{"cernet", "教育网"},
{"education", "教育网"},
}
// CountryLabelZh ISO2 → 中文国名(未知则返回原码)
func CountryLabelZh(iso string) string {
iso = strings.ToUpper(strings.TrimSpace(iso))
if iso == "" {
return ""
}
if v, ok := countryZh[iso]; ok {
return v
}
return iso
}
// ApplyGeoZh 就地补充中文省/市/运营商country 保持 ISO2
func ApplyGeoZh(g *GeoInfo) {
if g == nil {
return
}
g.Country = normalizeCountryCode(g.Country)
if g.Country == "CN" || g.Country == "HK" || g.Country == "MO" || g.Country == "TW" {
mapCNRegionCity(g)
}
if g.ASN > 0 {
if zh, ok := asnOrgZh[g.ASN]; ok {
g.ASOrg = zh
} else if g.ASOrg != "" {
g.ASOrg = mapASOrgZh(g.ASOrg)
}
} else if g.ASOrg != "" {
g.ASOrg = mapASOrgZh(g.ASOrg)
}
g.Region = truncateRunes(strings.TrimSpace(g.Region), 64)
g.City = truncateRunes(strings.TrimSpace(g.City), 64)
g.ASOrg = truncateRunes(strings.TrimSpace(g.ASOrg), 128)
}
func mapCNRegionCity(g *GeoInfo) {
if g.RegionISO == "" {
if meta, ok := lookupCNRegion(g.Region); ok {
g.Region = meta.Name
g.RegionISO = meta.ISO
}
} else if g.Region != "" {
if meta, ok := lookupCNRegion(g.Region); ok {
if g.RegionISO == "" {
g.RegionISO = meta.ISO
}
g.Region = meta.Name
}
} else if g.RegionISO != "" {
for _, meta := range cnRegionMap {
if meta.ISO == strings.ToUpper(g.RegionISO) {
g.Region = meta.Name
break
}
}
}
if g.City != "" {
if zh, ok := lookupCityZh(g.City); ok {
g.City = zh
}
}
// 直辖市BIN 有时只给城市不给省
if g.Region == "" && g.City != "" {
switch g.City {
case "北京", "上海", "天津", "重庆":
g.Region = g.City
if meta, ok := cnRegionMap[strings.ToLower(g.City)]; ok {
g.RegionISO = meta.ISO
}
}
}
}
func lookupCNRegion(name string) (cnRegionMeta, bool) {
key := strings.ToLower(strings.TrimSpace(name))
if key == "" {
return cnRegionMeta{}, false
}
if meta, ok := cnRegionMap[key]; ok {
return meta, true
}
// 去掉常见后缀再试
for _, suffix := range []string{" province", " sheng", " autonomous region", " municipality"} {
if strings.HasSuffix(key, suffix) {
if meta, ok := cnRegionMap[strings.TrimSuffix(key, suffix)]; ok {
return meta, true
}
}
}
return cnRegionMeta{}, false
}
func lookupCityZh(name string) (string, bool) {
key := strings.ToLower(strings.TrimSpace(name))
if zh, ok := cityZh[key]; ok {
return zh, true
}
return "", false
}
func mapASOrgZh(org string) string {
l := strings.ToLower(strings.TrimSpace(org))
if l == "" {
return org
}
for _, rule := range asnOrgKeywordZh {
if strings.Contains(l, rule.kw) {
return rule.zh
}
}
// "AS4134 Chinanet" 等形式
if strings.HasPrefix(l, "as") {
num := strings.TrimPrefix(l, "as")
if i := strings.IndexByte(num, ' '); i >= 0 {
num = num[:i]
}
if n, err := strconv.ParseUint(num, 10, 32); err == nil {
if zh, ok := asnOrgZh[uint(n)]; ok {
return zh
}
}
}
return org
}