This commit is contained in:
guanguans 2021-01-11 17:55:24 +08:00
parent cf2ada4860
commit 26d7f5f009
9 changed files with 113 additions and 109 deletions

View File

@ -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 { func CheckIDArgument(id string) bool {
return len(GenerateType(id)) != 0 _, err := GenerateCode(id)
return err == nil
} }
// 生成数据 // 生成数据
func GenerateType(id string) map[string]string { func GenerateCode(id string) (map[string]string, error) {
lowerId := strings.ToLower(id) length := len(id)
if length == 15 {
if len(lowerId) == 15 { return GenerateShortCode(id)
return GenerateShortType(id)
} }
if len(lowerId) == 18 { if length == 18 {
return GenerateLongType(id) 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 GetAddressInfo(addressCode, birthdayCode)["province"] != ""
return addressInfo["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
}
return error == nil _, err := time.Parse("20060102", birthdayCode)
return err == nil
} }
// 检查顺序码 // 检查顺序码

View File

@ -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: "东城区",

View File

@ -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][]map[string]string{
var AddressCodeTimeline = map[int][4]map[string]string{
110000: { 110000: {
{ {

View File

@ -1,7 +1,6 @@
package data package data
// 生肖信息 // 生肖信息
var ChineseZodiac = [12]string{ var ChineseZodiac = [12]string{
"子鼠", "子鼠",
"丑牛", "丑牛",

View File

@ -1,7 +1,6 @@
package data package data
// 星座信息 // 星座信息
var Constellation = [13]map[string]string{ var Constellation = [13]map[string]string{
1: { 1: {
"name": "水瓶座", "name": "水瓶座",

View File

View File

@ -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 部分与位置加权的积 // 累身份证号body部分与位置加权的积
bodySum := 0 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]) bodySub, _ := strconv.Atoi(bodyArray[i])
bodySum += bodySubStr * int(posWeight[18-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")) year := DatePipelineHandle(DatePad(Substr(birthday, 0, 4), "year"), "year")
month, _ := strconv.Atoi(DatePad(Substr(birthday, 4, 6), "month")) month := DatePipelineHandle(DatePad(Substr(birthday, 4, 6), "month"), "month")
day, _ := strconv.Atoi(DatePad(Substr(birthday, 6, 8), "day")) day := DatePipelineHandle(DatePad(Substr(birthday, 6, 8), "day"), "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")
}
return year + month + day
}
// 日期处理
func DatePipelineHandle(date string, category string) string {
dateInt, _ := strconv.Atoi(date)
switch category {
case "year":
nowYear := time.Now().Year() nowYear := time.Now().Year()
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
if year < 1800 || year > nowYear { if dateInt < 1800 || dateInt > nowYear {
randYear := rand.Intn(nowYear-1950) + 1950 randDate := rand.Intn(nowYear-1950) + 1950
year, _ = strconv.Atoi(DatePad(strconv.Itoa(randYear), "year")) date = strconv.Itoa(randDate)
} }
if month < 1 || month > 12 { case "month":
randMonth := rand.Intn(12-1) + 1 if dateInt < 1 || dateInt > 12 {
month, _ = strconv.Atoi(DatePad(strconv.Itoa(randMonth), "month")) randDate := rand.Intn(12-1) + 1
} date = strconv.Itoa(randDate)
if day < 1 || day > 31 {
randDay := rand.Intn(28-1) + 1
day, _ = strconv.Atoi(DatePad(strconv.Itoa(randDay), "day"))
} }
birthdayStr := DatePad(strconv.Itoa(year), "year") + DatePad(strconv.Itoa(month), "month") + DatePad(strconv.Itoa(day), "day") case "day":
_, error := time.Parse("20060102", birthdayStr) if dateInt < 1 || dateInt > 31 {
if error != nil { randDate := rand.Intn(28-1) + 1
randYear := rand.Intn(nowYear-1950) + 1950 date = strconv.Itoa(randDate)
year, _ = strconv.Atoi(DatePad(strconv.Itoa(randYear), "year")) }
randMonth := rand.Intn(12-1) + 1
month, _ = strconv.Atoi(DatePad(strconv.Itoa(randMonth), "month"))
randDay := rand.Intn(28-1) + 1
day, _ = strconv.Atoi(DatePad(strconv.Itoa(randDay), "day"))
} }
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)

View File

@ -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(Substr(addressCode, 0, 2)+"0000", birthdayCode)
addressInfo["province"] = GetAddress(provinceAddressCode, 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(Substr(addressCode, 0, 4)+"00", birthdayCode)
addressInfo["city"] = GetAddress(cityAddressCode, 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 address := ""
addressCodeInt, _ := strconv.Atoi(addressCode)
addressCodeStr, _ := strconv.Atoi(addressCode) year, _ := strconv.Atoi(Substr(birthdayCode, 0, 4))
addressCodeTimeline := data.AddressCodeTimeline[addressCodeStr] for key, val := range data.AddressCodeTimeline[addressCodeInt] {
// if len(val) == 0 {
year := Substr(birthdayCode, 0, 4) // continue
yearStr, _ := strconv.Atoi(year) // }
for key, val := range addressCodeTimeline {
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 {

View File

@ -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"] == GeneratorCheckBit(code["body"])
return code["checkBit"] == checkBit
} }
// 获取身份证信息 // 获取身份证信息
@ -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