diff --git a/checker.go b/checker.go index b42fd91..ca2cece 100644 --- a/checker.go +++ b/checker.go @@ -1,6 +1,7 @@ -package id_validator +package idvalidator import ( + "errors" "regexp" "strconv" "strings" @@ -8,29 +9,34 @@ import ( ) // 检查ID参数 -func CheckIdArgument(id string) bool { - return len(GenerateType(id)) != 0 +func CheckIDArgument(id string) bool { + _, err := GenerateCode(id) + + return err == nil } // 生成数据 -func GenerateType(id string) map[string]string { - lowerId := strings.ToLower(id) - - if len(lowerId) == 15 { - return GenerateShortType(id) +func GenerateCode(id string) (map[string]string, error) { + length := len(id) + if length == 15 { + return GenerateShortCode(id) } - if len(lowerId) == 18 { - return GenerateLongType(id) + if length == 18 { + return GenerateLongCode(id) } - return map[string]string{} + return map[string]string{}, errors.New("Invalid ID card number length.") } // 生成短数据 -func GenerateShortType(id string) map[string]string { +func GenerateShortCode(id string) (map[string]string, error) { + if len(id) != 15 { + return map[string]string{}, errors.New("Invalid ID card number length.") + } + mustCompile := regexp.MustCompile("(.{6})(.{6})(.{3})") - subMatch := mustCompile.FindStringSubmatch(id) + subMatch := mustCompile.FindStringSubmatch(strings.ToLower(id)) return map[string]string{ "body": subMatch[0], @@ -39,13 +45,16 @@ func GenerateShortType(id string) map[string]string { "order": subMatch[3], "checkBit": "", "type": "15", - } + }, nil } // 生成长数据 -func GenerateLongType(id string) map[string]string { +func GenerateLongCode(id string) (map[string]string, error) { + if len(id) != 18 { + return map[string]string{}, errors.New("Invalid ID card number length.") + } mustCompile := regexp.MustCompile("((.{6})(.{8})(.{3}))(.)") - subMatch := mustCompile.FindStringSubmatch(id) + subMatch := mustCompile.FindStringSubmatch(strings.ToLower(id)) return map[string]string{ "body": subMatch[1], @@ -54,14 +63,12 @@ func GenerateLongType(id string) map[string]string { "order": subMatch[4], "checkBit": subMatch[5], "type": "18", - } + }, nil } // 检查地址码 func CheckAddressCode(addressCode string, birthdayCode string) bool { - addressInfo := GetAddressInfo(addressCode, birthdayCode) - - return addressInfo["province"] != "" + return GetAddressInfo(addressCode, birthdayCode)["province"] != "" } // 检查出生日期码 @@ -71,9 +78,14 @@ func CheckBirthdayCode(birthdayCode string) bool { return false } - _, error := time.Parse("20060102", birthdayCode) + nowYear := time.Now().Year() + if year > nowYear { + return false + } + + _, err := time.Parse("20060102", birthdayCode) - return error == nil + return err == nil } // 检查顺序码 diff --git a/data/address_code.go b/data/address_code.go index 050881c..80c47b5 100644 --- a/data/address_code.go +++ b/data/address_code.go @@ -5,7 +5,6 @@ package data // 注1:台湾省、香港特别行政区和澳门特别行政区暂缺地市和区县信息 // 注2:每月发布的区划变更表是根据区划变更地的统计人员在统计信息系统更新后的情况所绘制,与区划变更文件发布的时间有一定的延迟性,但在每年的最后一次发布变更情况后与区划全年变更文件保持一致。 // Data Source: http://www.mca.gov.cn/article/sj/xzqh/ - var AddressCode = map[int]string{ 110000: "北京市", 110101: "东城区", diff --git a/data/address_code_timeline.go b/data/address_code_timeline.go index 9aed2c1..55573e1 100644 --- a/data/address_code_timeline.go +++ b/data/address_code_timeline.go @@ -5,8 +5,7 @@ package data // 注1:台湾省、香港特别行政区和澳门特别行政区暂缺地市和区县信息 // 注2:每月发布的区划变更表是根据区划变更地的统计人员在统计信息系统更新后的情况所绘制,与区划变更文件发布的时间有一定的延迟性,但在每年的最后一次发布变更情况后与区划全年变更文件保持一致。 // Data Source: http://www.mca.gov.cn/article/sj/xzqh/ - -var AddressCodeTimeline = map[int][4]map[string]string{ +var AddressCodeTimeline = map[int][]map[string]string{ 110000: { { diff --git a/data/chinese_zodiac.go b/data/chinese_zodiac.go index 6d8a705..e726a34 100644 --- a/data/chinese_zodiac.go +++ b/data/chinese_zodiac.go @@ -1,7 +1,6 @@ package data // 生肖信息 - var ChineseZodiac = [12]string{ "子鼠", "丑牛", diff --git a/data/constellation.go b/data/constellation.go index d77721b..3fb412d 100644 --- a/data/constellation.go +++ b/data/constellation.go @@ -1,7 +1,6 @@ package data // 星座信息 - var Constellation = [13]map[string]string{ 1: { "name": "水瓶座", diff --git a/docs/.gitkeep b/docs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/generator.go b/generator.go index 8e1e454..59e3fa5 100644 --- a/generator.go +++ b/generator.go @@ -1,4 +1,4 @@ -package id_validator +package idvalidator import ( "id-validator/data" @@ -10,7 +10,7 @@ import ( "time" ) -// 生成 Bit 码 +// 生成Bit码 func GeneratorCheckBit(body string) string { // 位置加权 var posWeight [19]float64 @@ -19,13 +19,13 @@ func GeneratorCheckBit(body string) string { posWeight[i] = float64(weight) } - // 累身份证号 body 部分与位置加权的积 - bodySum := 0 + // 累身份证号body部分与位置加权的积 + var bodySum int bodyArray := strings.Split(body, "") count := len(bodyArray) for i := 0; i < count; i++ { - bodySubStr, _ := strconv.Atoi(bodyArray[i]) - bodySum += bodySubStr * int(posWeight[18-i]) + bodySub, _ := strconv.Atoi(bodyArray[i]) + bodySum += bodySub * int(posWeight[18-i]) } // 生成校验码 @@ -38,17 +38,15 @@ func GeneratorCheckBit(body string) string { // 生成地址码 func GeneratorAddressCode(address string) string { - var addressCodeInt int + addressCode := "" for code, addressStr := range data.AddressCode { if address == addressStr { - addressCodeInt = code + addressCode = strconv.Itoa(code) break } } - addressCode := strconv.Itoa(addressCodeInt) - - classification := AddressCodeClassification(strconv.Itoa(addressCodeInt)) + classification := AddressCodeClassification(addressCode) switch classification { case "country": // addressCode = GetRandAddressCode("\\d{4}(?!00)[0-9]{2}$") @@ -97,7 +95,6 @@ func AddressCodeClassification(addressCode string) string { // 获取随机地址码 func GetRandAddressCode(pattern string) string { mustCompile := regexp.MustCompile(pattern) - var keys []string for key := range data.AddressCode { keyStr := strconv.Itoa(key) @@ -106,48 +103,55 @@ func GetRandAddressCode(pattern string) string { } } - // initialize global pseudo random generator rand.Seed(time.Now().Unix()) - randKey := rand.Intn(len(keys)) - return keys[randKey] + return keys[rand.Intn(len(keys))] } // 生成出生日期码 func GeneratorBirthdayCode(birthday string) string { - year, _ := strconv.Atoi(DatePad(Substr(birthday, 0, 4), "year")) - month, _ := strconv.Atoi(DatePad(Substr(birthday, 4, 6), "month")) - day, _ := strconv.Atoi(DatePad(Substr(birthday, 6, 8), "day")) + year := DatePipelineHandle(DatePad(Substr(birthday, 0, 4), "year"), "year") + month := DatePipelineHandle(DatePad(Substr(birthday, 4, 6), "month"), "month") + day := DatePipelineHandle(DatePad(Substr(birthday, 6, 8), "day"), "day") - nowYear := time.Now().Year() - rand.Seed(time.Now().Unix()) - if year < 1800 || year > nowYear { - randYear := rand.Intn(nowYear-1950) + 1950 - year, _ = strconv.Atoi(DatePad(strconv.Itoa(randYear), "year")) - } - if month < 1 || month > 12 { - randMonth := rand.Intn(12-1) + 1 - month, _ = strconv.Atoi(DatePad(strconv.Itoa(randMonth), "month")) - } - if day < 1 || day > 31 { - randDay := rand.Intn(28-1) + 1 - day, _ = strconv.Atoi(DatePad(strconv.Itoa(randDay), "day")) + birthday = year + month + day + _, error := time.Parse("20060102", birthday) + // example: 195578 + if error != nil { + year = DatePad(year, "year") + month = DatePad(month, "month") + day = DatePad(day, "day") } - birthdayStr := DatePad(strconv.Itoa(year), "year") + DatePad(strconv.Itoa(month), "month") + DatePad(strconv.Itoa(day), "day") - _, error := time.Parse("20060102", birthdayStr) - if error != nil { - randYear := rand.Intn(nowYear-1950) + 1950 - year, _ = strconv.Atoi(DatePad(strconv.Itoa(randYear), "year")) + return year + month + day +} - randMonth := rand.Intn(12-1) + 1 - month, _ = strconv.Atoi(DatePad(strconv.Itoa(randMonth), "month")) +// 日期处理 +func DatePipelineHandle(date string, category string) string { + dateInt, _ := strconv.Atoi(date) + + switch category { + case "year": + nowYear := time.Now().Year() + rand.Seed(time.Now().Unix()) + if dateInt < 1800 || dateInt > nowYear { + randDate := rand.Intn(nowYear-1950) + 1950 + date = strconv.Itoa(randDate) + } + case "month": + if dateInt < 1 || dateInt > 12 { + randDate := rand.Intn(12-1) + 1 + date = strconv.Itoa(randDate) + } - randDay := rand.Intn(28-1) + 1 - day, _ = strconv.Atoi(DatePad(strconv.Itoa(randDay), "day")) + case "day": + if dateInt < 1 || dateInt > 31 { + randDate := rand.Intn(28-1) + 1 + date = strconv.Itoa(randDate) + } } - return DatePad(strconv.Itoa(year), "year") + DatePad(strconv.Itoa(month), "month") + DatePad(strconv.Itoa(day), "day") + return date } // 生成顺序码 @@ -155,7 +159,7 @@ func GeneratorOrderCode(sex int) string { rand.Seed(time.Now().Unix()) orderCode := rand.Intn(999-111) + 111 if sex != orderCode%2 { - orderCode -= 1 + orderCode-- } return strconv.Itoa(orderCode) diff --git a/helper.go b/helpers.go similarity index 72% rename from helper.go rename to helpers.go index 06dd2ed..f1008b4 100644 --- a/helper.go +++ b/helpers.go @@ -1,10 +1,9 @@ -package id_validator +package idvalidator import ( + "id-validator/data" "strconv" "strings" - - "id-validator/data" ) // 检查地址码 @@ -16,8 +15,7 @@ func GetAddressInfo(addressCode string, birthdayCode string) map[string]string { } // 省级信息 - provinceAddressCode := Substr(addressCode, 0, 2) + "0000" - addressInfo["province"] = GetAddress(provinceAddressCode, birthdayCode) + addressInfo["province"] = GetAddress(Substr(addressCode, 0, 2)+"0000", birthdayCode) // 用于判断是否是港澳台居民居住证(8字开头) firstCharacter := Substr(addressCode, 0, 1) @@ -27,8 +25,7 @@ func GetAddressInfo(addressCode string, birthdayCode string) map[string]string { } // 市级信息 - cityAddressCode := Substr(addressCode, 0, 4) + "00" - addressInfo["city"] = GetAddress(cityAddressCode, birthdayCode) + addressInfo["city"] = GetAddress(Substr(addressCode, 0, 4)+"00", birthdayCode) // 县级信息 addressInfo["district"] = GetAddress(addressCode, birthdayCode) @@ -36,22 +33,17 @@ func GetAddressInfo(addressCode string, birthdayCode string) map[string]string { return addressInfo } +// 获取省市区地址码 func GetAddress(addressCode string, birthdayCode string) string { - var address string - - addressCodeStr, _ := strconv.Atoi(addressCode) - addressCodeTimeline := data.AddressCodeTimeline[addressCodeStr] - - year := Substr(birthdayCode, 0, 4) - yearStr, _ := strconv.Atoi(year) - - for key, val := range addressCodeTimeline { - if len(val) == 0 { - continue - } - + address := "" + addressCodeInt, _ := strconv.Atoi(addressCode) + year, _ := strconv.Atoi(Substr(birthdayCode, 0, 4)) + for key, val := range data.AddressCodeTimeline[addressCodeInt] { + // if len(val) == 0 { + // continue + // } startYear, _ := strconv.Atoi(val["start_year"]) - if (key == 0 && yearStr < startYear) || yearStr >= startYear { + if (key == 0 && year < startYear) || year >= startYear { address = val["address"] } } @@ -91,7 +83,7 @@ func GetChineseZodiac(birthdayCode string) string { // Substr 截取字符串 func Substr(source string, start int, end int) string { - var r = []rune(source) + r := []rune(source) length := len(r) if start < 0 || end > length || start > end { diff --git a/id_validator.go b/id_validator.go index f96d7d8..8477c3a 100644 --- a/id_validator.go +++ b/id_validator.go @@ -1,4 +1,4 @@ -package id_validator +package idvalidator import ( "errors" @@ -8,12 +8,13 @@ import ( // 验证身份证号合法性 func IsValid(id string) bool { - if !CheckIdArgument(id) { + code, err := GenerateCode(id) + if err != nil { return false } - code := GenerateType(id) - if !CheckAddressCode(code["addressCode"], code["birthdayCode"]) || !CheckBirthdayCode(code["birthdayCode"]) || !CheckOrderCode(code["order"]) { + // 检查顺序码、生日码、地址码 + if !CheckOrderCode(code["order"]) || !CheckBirthdayCode(code["birthdayCode"]) || !CheckAddressCode(code["addressCode"], code["birthdayCode"]) { return false } @@ -22,10 +23,8 @@ func IsValid(id string) bool { return true } - // 验证:校验码 - checkBit := GeneratorCheckBit(code["body"]) - - return code["checkBit"] == checkBit + // 校验码 + return code["checkBit"] == GeneratorCheckBit(code["body"]) } // 获取身份证信息 @@ -35,7 +34,7 @@ func GetInfo(id string) map[string]string { return map[string]string{} } - code := GenerateType(id) + code, _ := GenerateCode(id) addressInfo := GetAddressInfo(code["addressCode"], code["birthdayCode"]) // fmt.Println(addressInfo) @@ -67,15 +66,15 @@ func GetInfo(id string) map[string]string { return info } -// 生成假数据 -func FakeId(isEighteen bool, address string, birthday string, sex int) string { +// 生成假身份证号码 +func Fake(isEighteen bool, address string, birthday string, sex int) string { // 生成地址码 addressCode := GeneratorAddressCode(address) // 出生日期码 birthdayCode := GeneratorBirthdayCode(birthday) - // fmt.Println(birthdayCode) - // 顺序码 + + // 生成顺序码 orderCode := GeneratorOrderCode(sex) if !isEighteen { @@ -88,12 +87,13 @@ func FakeId(isEighteen bool, address string, birthday string, sex int) string { } // 15位升级18位号码 -func UpgradeId(id string) (string, error) { +func Upgrade(id string) (string, error) { if !IsValid(id) { return "", errors.New("Not Valid ID card number.") } - code := GenerateShortType(id) + code, _ := GenerateShortCode(id) + body := code["addressCode"] + code["birthdayCode"] + code["order"] return body + GeneratorCheckBit(body), nil