package entity import ( "regexp" "strings" ) var digitsOnly = regexp.MustCompile(`^\d+$`) // NormalizePhone returns E.164-style +86XXXXXXXXXXX for mainland mobile, or "" if invalid. func NormalizePhone(raw string) string { s := strings.TrimSpace(raw) s = strings.ReplaceAll(s, " ", "") s = strings.ReplaceAll(s, "-", "") if s == "" { return "" } if strings.HasPrefix(s, "+86") { s = s[3:] } else if strings.HasPrefix(s, "86") && len(s) > 11 { s = s[2:] } if len(s) != 11 || s[0] != '1' || !digitsOnly.MatchString(s) { return "" } return "+86" + s } // DetectCarrier maps a normalized +86 number to an operator bucket. func DetectCarrier(normalized string) Carrier { if len(normalized) < 4 { return CarrierUnknown } d := normalized d = strings.TrimPrefix(d, "+86") if len(d) < 3 { return CarrierUnknown } p3 := d[:3] switch p3 { case "134", "135", "136", "137", "138", "139", "147", "148", "150", "151", "152", "157", "158", "159", "172", "178", "182", "183", "184", "187", "188", "195", "197", "198": return CarrierMobile case "130", "131", "132", "145", "146", "155", "156", "166", "167", "171", "175", "176", "185", "186", "196": return CarrierUnicom case "133", "149", "153", "173", "174", "177", "180", "181", "189", "190", "191", "193", "199": return CarrierTelecom default: if strings.HasPrefix(p3, "14") || strings.HasPrefix(p3, "16") { return CarrierUnicom } return CarrierUnknown } }