// Copyright 2019 getensh.com. All rights reserved. // Use of this source code is governed by getensh.com. package check import ( pb_v1 "adm-task/pb/v1" "context" "fmt" "regexp" "strconv" "strings" "time" "unicode" "unicode/utf8" "google.golang.org/grpc/status" ) const ( VINLEN = 17 PLATELEN = 6 PLATENEWLEN = 7 SF = "京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新" ) var RUNMODE = "prod" func SetRunmode(runMode string) { RUNMODE = runMode } var vinMap = map[string]int{ "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 1, "B": 2, "C": 3, "D": 4, "E": 5, "F": 6, "G": 7, "H": 8, "J": 1, "K": 2, "L": 3, "M": 4, "N": 5, "P": 7, "R": 9, "S": 2, "T": 3, "U": 4, "V": 5, "W": 6, "X": 7, "Y": 8, "Z": 9, } var vinMatrix = []int{8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2} func checkVin(vin string) bool { var oldVin9 int32 total := 0 for i, v := range vin { if i == 8 { oldVin9 = v continue } r := rune(v) total = total + vinMatrix[i]*vinMap[string(r)] } left := total % 11 newVin9 := "" if left == 10 { newVin9 = "X" } else { newVin9 = fmt.Sprintf("%d", left) } r := rune(oldVin9) if newVin9 == string(r) { return true } else { return false } } func CheckVinFormat(vin string) (string, error) { vin = strings.TrimSpace(vin) vin = strings.ToUpper(vin) /*if len(vin) != VINLEN { return vin, status.Error(20003, "请求参数格式不对,车架号不为17位") }*/ vinLen := len(vin) strconv.Itoa(vinLen) r, _ := regexp.Compile(`[0-9A-Z]{` + strconv.Itoa(vinLen) + `}`) if r.MatchString(vin) { if vinLen == 17 && strings.HasPrefix(vin, "L") { check := checkVin(vin) if check { return vin, nil } else { return vin, status.Error(20003, "vin码错误") } } return vin, nil } return vin, status.Error(20003, "请求参数格式不对,车架号包含特殊字符") } func CheckVinFormat17Bit(vin string) (string, error) { vin = strings.TrimSpace(vin) vin = strings.ToUpper(vin) if len(vin) != VINLEN { return vin, status.Error(20003, "请求参数格式不对,车架号不为17位") } r, _ := regexp.Compile(`[0-9A-Z]{17}`) if r.MatchString(vin) { return vin, nil } return vin, status.Error(20003, "请求参数格式不对,车架号包含特殊字符") } func IsCommonPlateType(plateType string) bool { switch plateType { case "51": return true case "52": return true case "01": return true case "02": return true default: return false } } func isUnknowPlateType(plateType string) bool { switch plateType { case "02": return false case "03": return false case "04": return false case "15": return false case "16": return false case "23": return false case "24": return false case "26": return false case "27": return false default: return true } } func getPlateType(plateNo string) string { switch { case strings.HasPrefix(plateNo, "使"): return "03" case strings.HasSuffix(plateNo, "使"): return "03" // 领馆汽车 case strings.HasSuffix(plateNo, "领"): return "04" case strings.HasSuffix(plateNo, "挂"): return "15" case strings.HasSuffix(plateNo, "学"): return "16" case strings.HasSuffix(plateNo, "警"): return "23" case strings.HasSuffix(plateNo, "港"): return "26" case strings.HasSuffix(plateNo, "澳"): return "27" default: return "02" } } func isSupportPlateType(plateType string) bool { switch plateType { case "01": return true case "02": return true case "03": return true case "04": return true case "05": return true case "06": return true case "07": return true case "08": return true case "09": return true case "10": return true case "11": return true case "12": return true case "13": return true case "14": return true case "15": return true case "16": return true case "17": return true case "20": return true case "21": return true case "22": return true case "23": return true case "24": return true case "25": return true case "26": return true case "27": return true case "51": return true case "52": return true case "99": return true default: return false } return false } func isContainSpecialCharactor(plateNumber []byte) bool { if strings.Contains(string(plateNumber), "I") || strings.Contains(string(plateNumber), "O") { return true } if getPlateType(string(plateNumber)) != "02" { return false } for _, v := range plateNumber { if (v >= 48 && v <= 57) || (v >= 65 && v <= 90) || (v >= 97 && v <= 122) { continue } return true } return false } func ParsePlate(plate string) (string, string) { var index int for i, r := range plate { if !unicode.Is(unicode.Scripts["Han"], r) { index = i break } } sf := plate[0:index] hphm := plate[index:] return sf, hphm } func CheckPlateNoFormat(plateNo *string, plateType string) (string, error) { *plateNo = strings.TrimSpace(*plateNo) *plateNo = strings.ToUpper(*plateNo) if RUNMODE != "prod" { if strings.HasPrefix(*plateNo, "测") { return plateType, nil } } if plateType != "" { if !isSupportPlateType(plateType) { return "", status.Error(20003, "参数错误,不支持的车牌类型") } } // 判断车牌号码合法性 sf, plateNumber := ParsePlate(*plateNo) if len(sf) == 0 { if strings.HasSuffix(*plateNo, "使") { return "03", nil } return plateType, status.Error(20003, "请求参数格式不对,车牌号码格式不正确") } else if sf != "使" { if !strings.Contains(SF, sf) { return plateType, status.Error(20003, "请求参数格式不对,不支持的省份") } } // 检查车牌是否包含特殊字符 if isContainSpecialCharactor([]byte(plateNumber)) { return plateType, status.Error(20003, "请求参数格式不对,车牌号码包含特殊字符") } plateLen := utf8.RuneCountInString(plateNumber) if plateType == "" && plateLen == PLATENEWLEN { if plateNumber[1] > 64 && plateNumber[1] < 91 { plateType = "52" } else if plateNumber[PLATENEWLEN-1] > 64 && plateNumber[PLATENEWLEN-1] < 91 { plateType = "51" } else { return plateType, status.Error(20003, "请求参数格式不对,新能源汽车车牌格式不正确") } } count := 0 for _, v := range plateNumber { if 64 < v && v < 91 { count++ } } if strings.HasPrefix(*plateNo, "使") || strings.HasSuffix(*plateNo, "使") { if plateType == "" { return "03", nil } else { if plateType != "03" { return "", status.Error(20003, "请求参数格式不对,车牌和车牌类型不匹配") } return plateType, nil } } else if strings.HasSuffix(*plateNo, "领") { if plateType == "" { return "04", nil } else { if plateType != "04" { return "", status.Error(20003, "请求参数格式不对,车牌和车牌类型不匹配") } return plateType, nil } } else if plateType == "51" || plateType == "52" { if count > 3 { return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码超过3个字母") } else if plateLen != PLATENEWLEN { return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码长度不正确") } else { if plateNumber[1] > 64 && plateNumber[1] < 91 { if plateType != "52" { return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码和车牌类型不匹配") } } else if plateNumber[PLATENEWLEN-1] > 64 && plateNumber[PLATENEWLEN-1] < 91 { if plateType != "51" { return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码和车牌类型不匹配") } } else { return plateType, status.Error(20003, "请求参数格式不对,新能源汽车车牌格式不正确") } } } else { if count > 3 { return plateType, status.Error(20003, "请求参数格式不对,车牌号码超过3个字母") } else if plateLen != PLATELEN { return plateType, status.Error(20003, "请求参数格式不对,车牌号码长度不正确") } } if strings.Contains("0123456789", plateNumber[0:1]) { return plateType, status.Error(20003, "请求参数格式不对,号牌号码第一位不能为数字") } if plateType == "51" || plateType == "52" { return plateType, nil } if plateType != "" && isUnknowPlateType(plateType) { return plateType, nil } plateTypeNew := getPlateType(*plateNo) if plateType != "" && plateTypeNew != plateType { return plateType, status.Error(20003, "请求参数格式不对,车牌号码和车牌类型不匹配") } // 不返回默认 return "", nil } func GetPlateTypeByColor(plateNo, plateColor string) string { switch plateColor { case "0": // 蓝色 return "02" case "1": //黄色 01 ,15,16 if strings.HasSuffix(plateNo, "挂") { return "15" } else if strings.HasSuffix(plateNo, "学") { return "16" } return "01" case "2": // 黑色 03,04,26,27 if strings.HasSuffix(plateNo, "使") || strings.HasPrefix(plateNo, "使") { return "03" } else if strings.HasSuffix(plateNo, "领") { return "04" } else if strings.HasSuffix(plateNo, "港") { return "26" } else if strings.HasSuffix(plateNo, "澳") { return "27" } return "" case "3": //白色 return "23" case "4": //渐变绿色 return "52" case "5": //黄绿渐变色 return "51" case "6": //蓝白渐变色 return "" case "9": //未确定 return "" default: return "" } } func GetPlateColorByPlateType(plateType string) string { switch plateType { case "01": return "1" case "02": return "0" case "03": return "2" case "04": return "2" case "08": return "0" case "09": return "2" case "10": return "2" case "13": return "1" case "15": return "1" case "16": return "1" case "17": return "1" case "23": return "3" case "24": return "3" case "26": return "2" case "27": return "2" case "51": return "5" case "52": return "4" default: return "" } } var ( idCertMatrix = []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2} idCertVerifyCode = []byte("10X98765432") socialCreditMatrix = []int{1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28} socialCreditMap = map[int]int{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15, 'G': 16, 'H': 17, 'J': 18, 'K': 19, 'L': 20, 'M': 21, 'N': 22, 'P': 23, 'Q': 24, 'R': 25, 'T': 26, 'U': 27, 'W': 28, 'X': 29, 'Y': 30} socialCreditMapKeys = []int{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'W', 'X', 'Y'} socialCreditMapValues = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30} ) func CheckIDCert(str string) (realIdCert string, res bool) { defer func() { if r := recover(); r != nil { res = false } }() idCert := strings.ToUpper(str) idCert = strings.TrimSpace(idCert) ret := idCert if len(idCert) != 18 { return "", false } year, err := strconv.Atoi(idCert[6:10]) if err != nil { return "", false } if !(1900 < year && year < 2100) { return "", false } check := 0 lastLetter := 0 for index, value := range []byte(idCert) { if index == 17 { lastLetter = int(value) break } if !(value >= 48 && value <= 57) { return "", false } v := value - 48 check = check + idCertMatrix[index]*int(v) } if !((lastLetter >= 48 && lastLetter <= 57) || lastLetter == 'X') { return "", false } timeLayout := "20060102" //转化所需模板 loc, _ := time.LoadLocation("Local") //重要:获取时区 _, err = time.ParseInLocation(timeLayout, idCert[6:14], loc) //使用模板在对应时区转化为time.time类型 if err != nil { return "", false } verifyCode := int(idCertVerifyCode[check%11]) if lastLetter == verifyCode { return ret, true } return "", false } func CheckFormat(ctx context.Context, req *pb_v1.CheckFormatRequest) (reply *pb_v1.CheckFormatReply, err error) { reply = &pb_v1.CheckFormatReply{} if req.Vin != "" { reply.Vin, err = CheckVinFormat(req.Vin) if err != nil { return reply, err } } if req.Idcert != "" { idcert, isOk := CheckIDCert(req.Idcert) if !isOk { return reply, status.Error(20003, "请求参数格式不对,身份证错误") } reply.Idcert = idcert } if req.PlateNo != "" { reply.PlateType, err = CheckPlateNoFormat(&req.PlateNo, req.PlateType) if err != nil { return reply, err } reply.PlateNo = req.PlateNo } return reply, nil }