@@ -1,6 +1,7 @@ | |||||
package id_validator | |||||
package idvalidator | |||||
import ( | import ( | ||||
"errors" | |||||
"regexp" | "regexp" | ||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
@@ -8,29 +9,34 @@ import ( | |||||
) | ) | ||||
// 检查ID参数 | // 检查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})") | mustCompile := regexp.MustCompile("(.{6})(.{6})(.{3})") | ||||
subMatch := mustCompile.FindStringSubmatch(id) | |||||
subMatch := mustCompile.FindStringSubmatch(strings.ToLower(id)) | |||||
return map[string]string{ | return map[string]string{ | ||||
"body": subMatch[0], | "body": subMatch[0], | ||||
@@ -39,13 +45,16 @@ func GenerateShortType(id string) map[string]string { | |||||
"order": subMatch[3], | "order": subMatch[3], | ||||
"checkBit": "", | "checkBit": "", | ||||
"type": "15", | "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}))(.)") | mustCompile := regexp.MustCompile("((.{6})(.{8})(.{3}))(.)") | ||||
subMatch := mustCompile.FindStringSubmatch(id) | |||||
subMatch := mustCompile.FindStringSubmatch(strings.ToLower(id)) | |||||
return map[string]string{ | return map[string]string{ | ||||
"body": subMatch[1], | "body": subMatch[1], | ||||
@@ -54,14 +63,12 @@ func GenerateLongType(id string) map[string]string { | |||||
"order": subMatch[4], | "order": subMatch[4], | ||||
"checkBit": subMatch[5], | "checkBit": subMatch[5], | ||||
"type": "18", | "type": "18", | ||||
} | |||||
}, nil | |||||
} | } | ||||
// 检查地址码 | // 检查地址码 | ||||
func CheckAddressCode(addressCode string, birthdayCode string) bool { | 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 | 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 | |||||
} | } | ||||
// 检查顺序码 | // 检查顺序码 | ||||
@@ -5,7 +5,6 @@ package data | |||||
// 注1:台湾省、香港特别行政区和澳门特别行政区暂缺地市和区县信息 | // 注1:台湾省、香港特别行政区和澳门特别行政区暂缺地市和区县信息 | ||||
// 注2:每月发布的区划变更表是根据区划变更地的统计人员在统计信息系统更新后的情况所绘制,与区划变更文件发布的时间有一定的延迟性,但在每年的最后一次发布变更情况后与区划全年变更文件保持一致。 | // 注2:每月发布的区划变更表是根据区划变更地的统计人员在统计信息系统更新后的情况所绘制,与区划变更文件发布的时间有一定的延迟性,但在每年的最后一次发布变更情况后与区划全年变更文件保持一致。 | ||||
// Data Source: http://www.mca.gov.cn/article/sj/xzqh/ | // Data Source: http://www.mca.gov.cn/article/sj/xzqh/ | ||||
var AddressCode = map[int]string{ | var AddressCode = map[int]string{ | ||||
110000: "北京市", | 110000: "北京市", | ||||
110101: "东城区", | 110101: "东城区", | ||||
@@ -5,8 +5,7 @@ package data | |||||
// 注1:台湾省、香港特别行政区和澳门特别行政区暂缺地市和区县信息 | // 注1:台湾省、香港特别行政区和澳门特别行政区暂缺地市和区县信息 | ||||
// 注2:每月发布的区划变更表是根据区划变更地的统计人员在统计信息系统更新后的情况所绘制,与区划变更文件发布的时间有一定的延迟性,但在每年的最后一次发布变更情况后与区划全年变更文件保持一致。 | // 注2:每月发布的区划变更表是根据区划变更地的统计人员在统计信息系统更新后的情况所绘制,与区划变更文件发布的时间有一定的延迟性,但在每年的最后一次发布变更情况后与区划全年变更文件保持一致。 | ||||
// Data Source: http://www.mca.gov.cn/article/sj/xzqh/ | // 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: { | 110000: { | ||||
{ | { | ||||
@@ -1,7 +1,6 @@ | |||||
package data | package data | ||||
// 生肖信息 | // 生肖信息 | ||||
var ChineseZodiac = [12]string{ | var ChineseZodiac = [12]string{ | ||||
"子鼠", | "子鼠", | ||||
"丑牛", | "丑牛", | ||||
@@ -1,7 +1,6 @@ | |||||
package data | package data | ||||
// 星座信息 | // 星座信息 | ||||
var Constellation = [13]map[string]string{ | var Constellation = [13]map[string]string{ | ||||
1: { | 1: { | ||||
"name": "水瓶座", | "name": "水瓶座", | ||||
@@ -1,4 +1,4 @@ | |||||
package id_validator | |||||
package idvalidator | |||||
import ( | import ( | ||||
"id-validator/data" | "id-validator/data" | ||||
@@ -10,7 +10,7 @@ import ( | |||||
"time" | "time" | ||||
) | ) | ||||
// 生成 Bit 码 | |||||
// 生成Bit码 | |||||
func GeneratorCheckBit(body string) string { | func GeneratorCheckBit(body string) string { | ||||
// 位置加权 | // 位置加权 | ||||
var posWeight [19]float64 | var posWeight [19]float64 | ||||
@@ -19,13 +19,13 @@ func GeneratorCheckBit(body string) string { | |||||
posWeight[i] = float64(weight) | posWeight[i] = float64(weight) | ||||
} | } | ||||
// 累身份证号 body 部分与位置加权的积 | |||||
bodySum := 0 | |||||
// 累身份证号body部分与位置加权的积 | |||||
var bodySum int | |||||
bodyArray := strings.Split(body, "") | bodyArray := strings.Split(body, "") | ||||
count := len(bodyArray) | count := len(bodyArray) | ||||
for i := 0; i < count; i++ { | 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 { | func GeneratorAddressCode(address string) string { | ||||
var addressCodeInt int | |||||
addressCode := "" | |||||
for code, addressStr := range data.AddressCode { | for code, addressStr := range data.AddressCode { | ||||
if address == addressStr { | if address == addressStr { | ||||
addressCodeInt = code | |||||
addressCode = strconv.Itoa(code) | |||||
break | break | ||||
} | } | ||||
} | } | ||||
addressCode := strconv.Itoa(addressCodeInt) | |||||
classification := AddressCodeClassification(strconv.Itoa(addressCodeInt)) | |||||
classification := AddressCodeClassification(addressCode) | |||||
switch classification { | switch classification { | ||||
case "country": | case "country": | ||||
// addressCode = GetRandAddressCode("\\d{4}(?!00)[0-9]{2}$") | // addressCode = GetRandAddressCode("\\d{4}(?!00)[0-9]{2}$") | ||||
@@ -97,7 +95,6 @@ func AddressCodeClassification(addressCode string) string { | |||||
// 获取随机地址码 | // 获取随机地址码 | ||||
func GetRandAddressCode(pattern string) string { | func GetRandAddressCode(pattern string) string { | ||||
mustCompile := regexp.MustCompile(pattern) | mustCompile := regexp.MustCompile(pattern) | ||||
var keys []string | var keys []string | ||||
for key := range data.AddressCode { | for key := range data.AddressCode { | ||||
keyStr := strconv.Itoa(key) | keyStr := strconv.Itoa(key) | ||||
@@ -106,48 +103,55 @@ func GetRandAddressCode(pattern string) string { | |||||
} | } | ||||
} | } | ||||
// initialize global pseudo random generator | |||||
rand.Seed(time.Now().Unix()) | rand.Seed(time.Now().Unix()) | ||||
randKey := rand.Intn(len(keys)) | |||||
return keys[randKey] | |||||
return keys[rand.Intn(len(keys))] | |||||
} | } | ||||
// 生成出生日期码 | // 生成出生日期码 | ||||
func GeneratorBirthdayCode(birthday string) string { | 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()) | rand.Seed(time.Now().Unix()) | ||||
orderCode := rand.Intn(999-111) + 111 | orderCode := rand.Intn(999-111) + 111 | ||||
if sex != orderCode%2 { | if sex != orderCode%2 { | ||||
orderCode -= 1 | |||||
orderCode-- | |||||
} | } | ||||
return strconv.Itoa(orderCode) | return strconv.Itoa(orderCode) | ||||
@@ -1,10 +1,9 @@ | |||||
package id_validator | |||||
package idvalidator | |||||
import ( | import ( | ||||
"id-validator/data" | |||||
"strconv" | "strconv" | ||||
"strings" | "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字开头) | // 用于判断是否是港澳台居民居住证(8字开头) | ||||
firstCharacter := Substr(addressCode, 0, 1) | 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) | addressInfo["district"] = GetAddress(addressCode, birthdayCode) | ||||
@@ -36,22 +33,17 @@ func GetAddressInfo(addressCode string, birthdayCode string) map[string]string { | |||||
return addressInfo | return addressInfo | ||||
} | } | ||||
// 获取省市区地址码 | |||||
func GetAddress(addressCode string, birthdayCode string) string { | 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"]) | startYear, _ := strconv.Atoi(val["start_year"]) | ||||
if (key == 0 && yearStr < startYear) || yearStr >= startYear { | |||||
if (key == 0 && year < startYear) || year >= startYear { | |||||
address = val["address"] | address = val["address"] | ||||
} | } | ||||
} | } | ||||
@@ -91,7 +83,7 @@ func GetChineseZodiac(birthdayCode string) string { | |||||
// Substr 截取字符串 | // Substr 截取字符串 | ||||
func Substr(source string, start int, end int) string { | func Substr(source string, start int, end int) string { | ||||
var r = []rune(source) | |||||
r := []rune(source) | |||||
length := len(r) | length := len(r) | ||||
if start < 0 || end > length || start > end { | if start < 0 || end > length || start > end { |
@@ -1,4 +1,4 @@ | |||||
package id_validator | |||||
package idvalidator | |||||
import ( | import ( | ||||
"errors" | "errors" | ||||
@@ -8,12 +8,13 @@ import ( | |||||
// 验证身份证号合法性 | // 验证身份证号合法性 | ||||
func IsValid(id string) bool { | func IsValid(id string) bool { | ||||
if !CheckIdArgument(id) { | |||||
code, err := GenerateCode(id) | |||||
if err != nil { | |||||
return false | 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 | return false | ||||
} | } | ||||
@@ -22,10 +23,8 @@ func IsValid(id string) bool { | |||||
return true | 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{} | return map[string]string{} | ||||
} | } | ||||
code := GenerateType(id) | |||||
code, _ := GenerateCode(id) | |||||
addressInfo := GetAddressInfo(code["addressCode"], code["birthdayCode"]) | addressInfo := GetAddressInfo(code["addressCode"], code["birthdayCode"]) | ||||
// fmt.Println(addressInfo) | // fmt.Println(addressInfo) | ||||
@@ -67,15 +66,15 @@ func GetInfo(id string) map[string]string { | |||||
return info | 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) | addressCode := GeneratorAddressCode(address) | ||||
// 出生日期码 | // 出生日期码 | ||||
birthdayCode := GeneratorBirthdayCode(birthday) | birthdayCode := GeneratorBirthdayCode(birthday) | ||||
// fmt.Println(birthdayCode) | |||||
// 顺序码 | |||||
// 生成顺序码 | |||||
orderCode := GeneratorOrderCode(sex) | orderCode := GeneratorOrderCode(sex) | ||||
if !isEighteen { | if !isEighteen { | ||||
@@ -88,12 +87,13 @@ func FakeId(isEighteen bool, address string, birthday string, sex int) string { | |||||
} | } | ||||
// 15位升级18位号码 | // 15位升级18位号码 | ||||
func UpgradeId(id string) (string, error) { | |||||
func Upgrade(id string) (string, error) { | |||||
if !IsValid(id) { | if !IsValid(id) { | ||||
return "", errors.New("Not Valid ID card number.") | return "", errors.New("Not Valid ID card number.") | ||||
} | } | ||||
code := GenerateShortType(id) | |||||
code, _ := GenerateShortCode(id) | |||||
body := code["addressCode"] + code["birthdayCode"] + code["order"] | body := code["addressCode"] + code["birthdayCode"] + code["order"] | ||||
return body + GeneratorCheckBit(body), nil | return body + GeneratorCheckBit(body), nil | ||||