250 lines
7.0 KiB
Go
250 lines
7.0 KiB
Go
package visit
|
||
|
||
import (
|
||
"strings"
|
||
"unicode"
|
||
)
|
||
|
||
// friendlyAndroidName 将 UA 中的型号码映射为可读名称(品牌 + 营销名/型号)
|
||
func friendlyAndroidName(model string) string {
|
||
model = strings.TrimSpace(model)
|
||
if model == "" {
|
||
return "Android"
|
||
}
|
||
key := strings.ToUpper(model)
|
||
key = strings.ReplaceAll(key, " ", "")
|
||
|
||
if name, ok := androidModelNames[key]; ok {
|
||
return name
|
||
}
|
||
// 部分型号带前缀品牌词
|
||
if name, ok := androidModelNames[strings.ToUpper(strings.TrimSpace(model))]; ok {
|
||
return name
|
||
}
|
||
|
||
brand, rest := detectAndroidBrand(model)
|
||
if brand != "" {
|
||
if rest == "" || strings.EqualFold(rest, brand) {
|
||
return brand
|
||
}
|
||
// 已是「Xiaomi 14」这类可读名
|
||
if looksHumanDeviceName(model) {
|
||
if !strings.HasPrefix(model, brand) && !strings.Contains(model, brand) {
|
||
return brand + " " + model
|
||
}
|
||
return model
|
||
}
|
||
return brand + " " + rest
|
||
}
|
||
if looksHumanDeviceName(model) {
|
||
return model
|
||
}
|
||
return model
|
||
}
|
||
|
||
func looksHumanDeviceName(s string) bool {
|
||
hasLetter := false
|
||
hasSpaceOrCJK := false
|
||
for _, r := range s {
|
||
if unicode.Is(unicode.Han, r) {
|
||
return true
|
||
}
|
||
if r == ' ' {
|
||
hasSpaceOrCJK = true
|
||
}
|
||
if unicode.IsLetter(r) {
|
||
hasLetter = true
|
||
}
|
||
}
|
||
// Pixel 7 / Nexus 5X 等
|
||
return hasLetter && hasSpaceOrCJK
|
||
}
|
||
|
||
func detectAndroidBrand(model string) (brand, rest string) {
|
||
m := strings.TrimSpace(model)
|
||
upper := strings.ToUpper(m)
|
||
|
||
// 显式品牌前缀
|
||
prefixes := []struct {
|
||
prefix string
|
||
brand string
|
||
}{
|
||
{"HUAWEI ", "华为"},
|
||
{"HONOR ", "荣耀"},
|
||
{"XIAOMI ", "小米"},
|
||
{"REDMI ", "红米"},
|
||
{"OPPO ", "OPPO"},
|
||
{"VIVO ", "vivo"},
|
||
{"SAMSUNG ", "三星"},
|
||
{"ONEPLUS ", "一加"},
|
||
{"REALME ", "realme"},
|
||
{"MEIZU ", "魅族"},
|
||
{"GOOGLE ", "Google"},
|
||
{"PIXEL ", "Google"},
|
||
}
|
||
for _, p := range prefixes {
|
||
if strings.HasPrefix(upper, strings.ToUpper(p.prefix)) {
|
||
return p.brand, strings.TrimSpace(m[len(p.prefix):])
|
||
}
|
||
}
|
||
|
||
switch {
|
||
case strings.HasPrefix(upper, "SM-"), strings.HasPrefix(upper, "GT-"), strings.HasPrefix(upper, "SCH-"):
|
||
return "三星", m
|
||
case strings.HasPrefix(upper, "RMX"):
|
||
return "realme", m
|
||
case strings.HasPrefix(upper, "CPH"), strings.HasPrefix(upper, "PGFM"), strings.HasPrefix(upper, "PGBM"),
|
||
strings.HasPrefix(upper, "PGEM"), strings.HasPrefix(upper, "PHN"), strings.HasPrefix(upper, "PHW"),
|
||
strings.HasPrefix(upper, "PJU"), strings.HasPrefix(upper, "PJD"):
|
||
return "OPPO", m
|
||
case strings.HasPrefix(upper, "V2"), strings.HasPrefix(upper, "V1"), strings.HasPrefix(upper, "V3"):
|
||
// vivo / iQOO 国内型号多为 VxxxxA
|
||
if len(upper) >= 5 && unicode.IsDigit(rune(upper[1])) {
|
||
return "vivo", m
|
||
}
|
||
case strings.HasPrefix(upper, "NE2"), strings.HasPrefix(upper, "LE2"), strings.HasPrefix(upper, "KB2"),
|
||
strings.HasPrefix(upper, "PHB"), strings.HasPrefix(upper, "PHP"), strings.HasPrefix(upper, "CPH24"),
|
||
strings.HasPrefix(upper, "ONEPLUS"):
|
||
return "一加", m
|
||
case strings.HasPrefix(upper, "M20"), strings.HasPrefix(upper, "M21"), strings.HasPrefix(upper, "220"),
|
||
strings.HasPrefix(upper, "221"), strings.HasPrefix(upper, "222"), strings.HasPrefix(upper, "230"),
|
||
strings.HasPrefix(upper, "231"), strings.HasPrefix(upper, "240"), strings.HasPrefix(upper, "241"),
|
||
strings.HasPrefix(upper, "MI "), strings.HasPrefix(upper, "MI-"):
|
||
return "小米", m
|
||
case strings.Contains(upper, "REDMI"):
|
||
return "红米", m
|
||
case strings.HasSuffix(upper, "-AN00"), strings.HasSuffix(upper, "-AN10"), strings.HasSuffix(upper, "-AL00"),
|
||
strings.HasSuffix(upper, "-AL10"), strings.HasSuffix(upper, "-TL00"), strings.HasSuffix(upper, "-LND"):
|
||
// 华为/荣耀鸿蒙型号:XXX-AN00
|
||
if isHonorModel(upper) {
|
||
return "荣耀", m
|
||
}
|
||
return "华为", m
|
||
case strings.HasPrefix(upper, "PIXEL"):
|
||
return "Google", m
|
||
case strings.HasPrefix(upper, "NEXUS"):
|
||
return "Google", m
|
||
}
|
||
return "", m
|
||
}
|
||
|
||
func isHonorModel(upper string) bool {
|
||
honor := []string{
|
||
"ANY-", "REP-", "FNE-", "FRI-", "ANN-", "ALA-", "BRT-", "SDY-", "ELP-",
|
||
"VER-", "MAA-", "REA-", "NTP-", "GIA-", "CLS-", "DIO-", "LGE-", "CRT-",
|
||
}
|
||
for _, p := range honor {
|
||
if strings.HasPrefix(upper, p) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// 常见国内机型营销名(可持续补充;未命中仍走品牌+型号)
|
||
var androidModelNames = map[string]string{
|
||
// 三星
|
||
"SM-S9180": "三星 Galaxy S23 Ultra",
|
||
"SM-S9160": "三星 Galaxy S23+",
|
||
"SM-S9110": "三星 Galaxy S23",
|
||
"SM-S9280": "三星 Galaxy S24 Ultra",
|
||
"SM-S9260": "三星 Galaxy S24+",
|
||
"SM-S9210": "三星 Galaxy S24",
|
||
"SM-S9380": "三星 Galaxy S25 Ultra",
|
||
"SM-S9080": "三星 Galaxy S22 Ultra",
|
||
"SM-S9010": "三星 Galaxy S22",
|
||
"SM-G9980": "三星 Galaxy S21 Ultra",
|
||
"SM-G9910": "三星 Galaxy S21",
|
||
"SM-F9460": "三星 Galaxy Z Fold5",
|
||
"SM-F7310": "三星 Galaxy Z Flip5",
|
||
"SM-A5460": "三星 Galaxy A54",
|
||
"SM-A5560": "三星 Galaxy A55",
|
||
|
||
// 小米 / 红米
|
||
"24031PN0DC": "小米 14",
|
||
"23127PN0CC": "小米 14",
|
||
"2211133C": "小米 13",
|
||
"2210132C": "小米 13 Pro",
|
||
"2201123C": "小米 12",
|
||
"2201122C": "小米 12 Pro",
|
||
"M2011K2C": "小米 11",
|
||
"M2007J1SC": "小米 10 至尊纪念版",
|
||
"M2007J17C": "小米 10 青春版",
|
||
"2311DRK48C": "红米 K70",
|
||
"23113RKC6C": "红米 K70 Pro",
|
||
"23013RK75C": "红米 K60",
|
||
"22041216C": "红米 K50",
|
||
"24117RK2CC": "红米 K80",
|
||
"23078RKD5C": "红米 Note 12 Turbo",
|
||
"2312DRA50C": "红米 Note 13 Pro",
|
||
|
||
// 华为
|
||
"NOH-AN00": "华为 Mate 40 Pro",
|
||
"NOH-AL10": "华为 Mate 40 Pro",
|
||
"OCE-AN10": "华为 Mate 40",
|
||
"ANA-AN00": "华为 P40",
|
||
"ELS-AN00": "华为 P40 Pro",
|
||
"BRA-AL00": "华为 P60",
|
||
"ALN-AL00": "华为 Mate 60",
|
||
"ALN-AL80": "华为 Mate 60 Pro",
|
||
"LNA-AL00": "华为 Mate X5",
|
||
"ALT-AL10": "华为 Mate 70",
|
||
"PLA-AL10": "华为 Mate 70 Pro",
|
||
"ADT-AL00": "华为 Pura 70",
|
||
"ADY-AL00": "华为 Pura 70 Pro",
|
||
|
||
// 荣耀
|
||
"ANY-AN00": "荣耀 Magic5",
|
||
"REP-AN00": "荣耀 Magic5 Pro",
|
||
"FNE-AN00": "荣耀 90",
|
||
"FRI-AN00": "荣耀 Magic6",
|
||
"BVL-AN16": "荣耀 Magic6 Pro",
|
||
"MAA-AN10": "荣耀 200",
|
||
"CLK-AN00": "荣耀 100",
|
||
|
||
// OPPO
|
||
"PGEM10": "OPPO Find X6 Pro",
|
||
"PGFM10": "OPPO Find X6",
|
||
"PGBM10": "OPPO Find N2",
|
||
"PHN110": "OPPO Find X7 Ultra",
|
||
"PHY110": "OPPO Find X7",
|
||
"CPH2473": "OPPO Find X5 Pro",
|
||
"PJU110": "OPPO Reno12",
|
||
"PJV110": "OPPO Reno12 Pro",
|
||
|
||
// vivo / iQOO
|
||
"V2254A": "vivo X90 Pro+",
|
||
"V2241A": "vivo X90",
|
||
"V2227A": "vivo X90 Pro",
|
||
"V2309A": "vivo X100",
|
||
"V2324A": "vivo X100 Pro",
|
||
"V2307A": "iQOO 11",
|
||
"V2243A": "iQOO 10",
|
||
"V2329A": "iQOO 12",
|
||
"V2055A": "vivo S9",
|
||
"V2183A": "vivo S15",
|
||
|
||
// 一加
|
||
"PHB110": "一加 11",
|
||
"CPH2449": "一加 11",
|
||
"PJE110": "一加 12",
|
||
"PHP110": "一加 Ace 2",
|
||
"NE2210": "一加 10 Pro",
|
||
|
||
// realme
|
||
"RMX3562": "realme GT Neo3",
|
||
"RMX3370": "realme GT Neo2",
|
||
"RMX3706": "realme GT5",
|
||
"RMX3888": "realme GT5 Pro",
|
||
|
||
// Google
|
||
"PIXEL 9 PRO": "Google Pixel 9 Pro",
|
||
"PIXEL 9": "Google Pixel 9",
|
||
"PIXEL 8 PRO": "Google Pixel 8 Pro",
|
||
"PIXEL 8": "Google Pixel 8",
|
||
"PIXEL 7 PRO": "Google Pixel 7 Pro",
|
||
"PIXEL 7": "Google Pixel 7",
|
||
"PIXEL 6 PRO": "Google Pixel 6 Pro",
|
||
"PIXEL 6": "Google Pixel 6",
|
||
}
|