Compare commits

..

No commits in common. "main" and "v1.0.0" have entirely different histories.
main ... v1.0.0

10 changed files with 76 additions and 174 deletions

View File

@ -5,8 +5,3 @@ All notable changes to `guanguans/id-validator` will be documented in this file
## 1.0.0 - 2021-01-21 ## 1.0.0 - 2021-01-21
* Initial release. * Initial release.
## 1.0.1 - 2021-01-27
* Change the access control of some functions.
* Add bench testing.

View File

@ -1,6 +1,5 @@
# note: call scripts from /scripts # note: call scripts from /scripts
GOCMD=GO111MODULE=on go GOCMD=GO111MODULE=on go
LOCALCMD=/usr/local
linters-install: linters-install:
@golangci-lint --version >/dev/null 2>&1 || { \ @golangci-lint --version >/dev/null 2>&1 || { \
@ -9,16 +8,12 @@ linters-install:
} }
lint: linters-install lint: linters-install
$(LOCALCMD)/bin/golangci-lint run $(PWD)/bin/golangci-lint run
fmt:
$(GOCMD) fmt ./...
vet:
$(GOCMD) vet ./.
test: test:
$(GOCMD) test -cover -race ./... $(GOCMD) test -cover -race ./...
bench: bench:
$(GOCMD) test -bench=. -benchmem ./... $(GOCMD) test -bench=. -benchmem ./...
.PHONY: test lint linters-install

View File

@ -81,7 +81,7 @@ func main() {
## Testing ## Testing
``` bash ``` bash
$ make test $ go test
``` ```
## Changelog ## Changelog

View File

@ -86,7 +86,7 @@ func main() {
## 测试 ## 测试
``` bash ``` bash
$ make test $ go test
``` ```
## 变更日志 ## 变更日志

View File

@ -1,88 +0,0 @@
package idvalidator
import (
"testing"
)
func BenchmarkIsValid(b *testing.B) {
benchmarks := []struct {
name string
id string
}{
{id: "440308199901101512"},
{id: "610104620927690"},
{id: "810000199408230021"},
{id: "830000199201300022"},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
IsValid(bm.name)
}
})
}
}
func BenchmarkGetInfo(b *testing.B) {
benchmarks := []struct {
name string
id string
}{
{id: "440308199901101512"},
{id: "610104620927690"},
{id: "810000199408230021"},
{id: "830000199201300022"},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
GetInfo(bm.name)
}
})
}
}
func BenchmarkFakeId(b *testing.B) {
for i := 0; i < b.N; i++ {
FakeId()
}
}
func BenchmarkFakeRequireId(b *testing.B) {
benchmarks := []struct {
name string
isEighteen bool
address string
birthday string
sex int
}{
{isEighteen: false, address: "浙江省", birthday: "20000101", sex: 1},
{isEighteen: true, address: "浙江省", birthday: "20000101", sex: 0},
{isEighteen: true, address: "台湾省", birthday: "20000101", sex: 0},
{isEighteen: true, address: "香港特别行政区", birthday: "20000101", sex: 0},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
FakeRequireId(bm.isEighteen, bm.address, bm.birthday, bm.sex)
}
})
}
}
func BenchmarkUpgradeId(b *testing.B) {
benchmarks := []struct {
name string
id string
}{
{id: "610104620927690"},
{id: "61010462092769"},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
UpgradeId(bm.id)
}
})
}
}

View File

@ -9,28 +9,28 @@ import (
) )
// 检查ID参数 // 检查ID参数
func checkIdArgument(id string) bool { func CheckIdArgument(id string) bool {
_, err := generateCode(id) _, err := GenerateCode(id)
return err == nil return err == nil
} }
// 生成数据 // 生成数据
func generateCode(id string) (map[string]string, error) { func GenerateCode(id string) (map[string]string, error) {
length := len(id) length := len(id)
if length == 15 { if length == 15 {
return generateShortCode(id) return GenerateShortCode(id)
} }
if length == 18 { if length == 18 {
return generateLongCode(id) return GenerateLongCode(id)
} }
return map[string]string{}, errors.New("Invalid ID card number length.") return map[string]string{}, errors.New("Invalid ID card number length.")
} }
// 生成短数据 // 生成短数据
func generateShortCode(id string) (map[string]string, error) { func GenerateShortCode(id string) (map[string]string, error) {
if len(id) != 15 { if len(id) != 15 {
return map[string]string{}, errors.New("Invalid ID card number length.") return map[string]string{}, errors.New("Invalid ID card number length.")
} }
@ -49,7 +49,7 @@ func generateShortCode(id string) (map[string]string, error) {
} }
// 生成长数据 // 生成长数据
func generateLongCode(id string) (map[string]string, error) { func GenerateLongCode(id string) (map[string]string, error) {
if len(id) != 18 { if len(id) != 18 {
return map[string]string{}, errors.New("Invalid ID card number length.") return map[string]string{}, errors.New("Invalid ID card number length.")
} }
@ -67,13 +67,13 @@ func generateLongCode(id string) (map[string]string, error) {
} }
// 检查地址码 // 检查地址码
func checkAddressCode(addressCode string, birthdayCode string) bool { func CheckAddressCode(addressCode string, birthdayCode string) bool {
return getAddressInfo(addressCode, birthdayCode)["province"] != "" return GetAddressInfo(addressCode, birthdayCode)["province"] != ""
} }
// 检查出生日期码 // 检查出生日期码
func checkBirthdayCode(birthdayCode string) bool { func CheckBirthdayCode(birthdayCode string) bool {
year, _ := strconv.Atoi(substr(birthdayCode, 0, 4)) year, _ := strconv.Atoi(Substr(birthdayCode, 0, 4))
if year < 1800 { if year < 1800 {
return false return false
} }
@ -89,6 +89,6 @@ func checkBirthdayCode(birthdayCode string) bool {
} }
// 检查顺序码 // 检查顺序码
func checkOrderCode(orderCode string) bool { func CheckOrderCode(orderCode string) bool {
return len(orderCode) == 3 return len(orderCode) == 3
} }

View File

@ -11,7 +11,7 @@ import (
) )
// 生成Bit码 // 生成Bit码
func generatorCheckBit(body string) string { func GeneratorCheckBit(body string) string {
// 位置加权 // 位置加权
var posWeight [19]float64 var posWeight [19]float64
for i := 2; i < 19; i++ { for i := 2; i < 19; i++ {
@ -37,7 +37,7 @@ func generatorCheckBit(body string) string {
} }
// 生成地址码 // 生成地址码
func generatorAddressCode(address string) string { func GeneratorAddressCode(address string) string {
addressCode := "" addressCode := ""
for code, addressStr := range data.AddressCode { for code, addressStr := range data.AddressCode {
if address == addressStr { if address == addressStr {
@ -46,45 +46,45 @@ func generatorAddressCode(address string) string {
} }
} }
classification := addressCodeClassification(addressCode) 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}$")
addressCode = getRandAddressCode("\\d{4}(?)[0-9]{2}$") addressCode = GetRandAddressCode("\\d{4}(?)[0-9]{2}$")
case "province": case "province":
provinceCode := substr(addressCode, 0, 2) provinceCode := Substr(addressCode, 0, 2)
// pattern := "^" + provinceCode + "\\d{2}(?!00)[0-9]{2}$" // pattern := "^" + provinceCode + "\\d{2}(?!00)[0-9]{2}$"
pattern := "^" + provinceCode + "\\d{2}(?)[0-9]{2}$" pattern := "^" + provinceCode + "\\d{2}(?)[0-9]{2}$"
addressCode = getRandAddressCode(pattern) addressCode = GetRandAddressCode(pattern)
case "city": case "city":
cityCode := substr(addressCode, 0, 4) cityCode := Substr(addressCode, 0, 4)
// pattern := "^" + cityCode + "(?!00)[0-9]{2}$" // pattern := "^" + cityCode + "(?!00)[0-9]{2}$"
pattern := "^" + cityCode + "(?)[0-9]{2}$" pattern := "^" + cityCode + "(?)[0-9]{2}$"
addressCode = getRandAddressCode(pattern) addressCode = GetRandAddressCode(pattern)
} }
return addressCode return addressCode
} }
// 地址码分类 // 地址码分类
func addressCodeClassification(addressCode string) string { func AddressCodeClassification(addressCode string) string {
// 全国 // 全国
if addressCode == "" { if addressCode == "" {
return "country" return "country"
} }
// 港澳台 // 港澳台
if substr(addressCode, 0, 1) == "8" { if Substr(addressCode, 0, 1) == "8" {
return "special" return "special"
} }
// 省级 // 省级
if substr(addressCode, 2, 6) == "0000" { if Substr(addressCode, 2, 6) == "0000" {
return "province" return "province"
} }
// 市级 // 市级
if substr(addressCode, 4, 6) == "00" { if Substr(addressCode, 4, 6) == "00" {
return "city" return "city"
} }
@ -93,12 +93,12 @@ 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)
if mustCompile.MatchString(keyStr) && substr(keyStr, 4, 6) != "00" { if mustCompile.MatchString(keyStr) && Substr(keyStr, 4, 6) != "00" {
keys = append(keys, keyStr) keys = append(keys, keyStr)
} }
} }
@ -109,25 +109,25 @@ func getRandAddressCode(pattern string) string {
} }
// 生成出生日期码 // 生成出生日期码
func generatorBirthdayCode(birthday string) string { func GeneratorBirthdayCode(birthday string) string {
year := datePipelineHandle(datePad(substr(birthday, 0, 4), "year"), "year") year := DatePipelineHandle(DatePad(Substr(birthday, 0, 4), "year"), "year")
month := datePipelineHandle(datePad(substr(birthday, 4, 6), "month"), "month") month := DatePipelineHandle(DatePad(Substr(birthday, 4, 6), "month"), "month")
day := datePipelineHandle(datePad(substr(birthday, 6, 8), "day"), "day") day := DatePipelineHandle(DatePad(Substr(birthday, 6, 8), "day"), "day")
birthday = year + month + day birthday = year + month + day
_, error := time.Parse("20060102", birthday) _, error := time.Parse("20060102", birthday)
// example: 195578 // example: 195578
if error != nil { if error != nil {
year = datePad(year, "year") year = DatePad(year, "year")
month = datePad(month, "month") month = DatePad(month, "month")
day = datePad(day, "day") day = DatePad(day, "day")
} }
return year + month + day return year + month + day
} }
// 日期处理 // 日期处理
func datePipelineHandle(date string, category string) string { func DatePipelineHandle(date string, category string) string {
dateInt, _ := strconv.Atoi(date) dateInt, _ := strconv.Atoi(date)
switch category { switch category {
@ -155,7 +155,7 @@ func datePipelineHandle(date string, category string) string {
} }
// 生成顺序码 // 生成顺序码
func generatorOrderCode(sex int) string { 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 {
@ -166,7 +166,7 @@ func generatorOrderCode(sex int) string {
} }
// 日期补全 // 日期补全
func datePad(date string, category string) string { func DatePad(date string, category string) string {
padLength := 2 padLength := 2
if category == "year" { if category == "year" {
padLength = 4 padLength = 4

2
go.mod
View File

@ -1,4 +1,4 @@
module golib.gaore.com/GaoreGo/id-validator module github.com/guanguans/id-validator
go 1.14 go 1.14

View File

@ -6,8 +6,8 @@ import (
"strings" "strings"
) )
// 获取地址信息 // 检查地址码
func getAddressInfo(addressCode string, birthdayCode string) map[string]string { func GetAddressInfo(addressCode string, birthdayCode string) map[string]string {
addressInfo := map[string]string{ addressInfo := map[string]string{
"province": "", "province": "",
"city": "", "city": "",
@ -15,29 +15,29 @@ func getAddressInfo(addressCode string, birthdayCode string) map[string]string {
} }
// 省级信息 // 省级信息
addressInfo["province"] = getAddress(substr(addressCode, 0, 2)+"0000", birthdayCode) addressInfo["province"] = GetAddress(Substr(addressCode, 0, 2)+"0000", birthdayCode)
// 用于判断是否是港澳台居民居住证8字开头 // 用于判断是否是港澳台居民居住证8字开头
firstCharacter := substr(addressCode, 0, 1) firstCharacter := Substr(addressCode, 0, 1)
// 港澳台居民居住证无市级、县级信息 // 港澳台居民居住证无市级、县级信息
if firstCharacter == "8" { if firstCharacter == "8" {
return addressInfo return addressInfo
} }
// 市级信息 // 市级信息
addressInfo["city"] = getAddress(substr(addressCode, 0, 4)+"00", birthdayCode) addressInfo["city"] = GetAddress(Substr(addressCode, 0, 4)+"00", birthdayCode)
// 县级信息 // 县级信息
addressInfo["district"] = getAddress(addressCode, birthdayCode) addressInfo["district"] = GetAddress(addressCode, birthdayCode)
return addressInfo return addressInfo
} }
// 获取省市区地址码 // 获取省市区地址码
func getAddress(addressCode string, birthdayCode string) string { func GetAddress(addressCode string, birthdayCode string) string {
address := "" address := ""
addressCodeInt, _ := strconv.Atoi(addressCode) addressCodeInt, _ := strconv.Atoi(addressCode)
year, _ := strconv.Atoi(substr(birthdayCode, 0, 4)) year, _ := strconv.Atoi(Substr(birthdayCode, 0, 4))
for key, val := range data.AddressCodeTimeline[addressCodeInt] { for key, val := range data.AddressCodeTimeline[addressCodeInt] {
// if len(val) == 0 { // if len(val) == 0 {
// continue // continue
@ -52,9 +52,9 @@ func getAddress(addressCode string, birthdayCode string) string {
} }
// 获取星座信息 // 获取星座信息
func getConstellation(birthdayCode string) string { func GetConstellation(birthdayCode string) string {
monthStr := substr(birthdayCode, 4, 6) monthStr := Substr(birthdayCode, 4, 6)
dayStr := substr(birthdayCode, 6, 8) dayStr := Substr(birthdayCode, 6, 8)
month, _ := strconv.Atoi(monthStr) month, _ := strconv.Atoi(monthStr)
day, _ := strconv.Atoi(dayStr) day, _ := strconv.Atoi(dayStr)
startDate := data.Constellation[month]["start_date"] startDate := data.Constellation[month]["start_date"]
@ -72,17 +72,17 @@ func getConstellation(birthdayCode string) string {
} }
// 获取生肖信息 // 获取生肖信息
func getChineseZodiac(birthdayCode string) string { func GetChineseZodiac(birthdayCode string) string {
// 子鼠 // 子鼠
start := 1900 start := 1900
end, _ := strconv.Atoi(substr(birthdayCode, 0, 4)) end, _ := strconv.Atoi(Substr(birthdayCode, 0, 4))
key := (end - start) % 12 key := (end - start) % 12
return data.ChineseZodiac[key] return data.ChineseZodiac[key]
} }
// substr 截取字符串 // Substr 截取字符串
func substr(source string, start int, end int) string { func Substr(source string, start int, end int) string {
r := []rune(source) r := []rune(source)
length := len(r) length := len(r)

View File

@ -9,7 +9,7 @@ import (
) )
// 身份证信息 // 身份证信息
type idInfo struct { type IdInfo struct {
AddressCode int AddressCode int
Abandoned int Abandoned int
Address string Address string
@ -24,13 +24,13 @@ type idInfo struct {
// 验证身份证号合法性 // 验证身份证号合法性
func IsValid(id string) bool { func IsValid(id string) bool {
code, err := generateCode(id) code, err := GenerateCode(id)
if err != nil { if err != nil {
return false return false
} }
// 检查顺序码、生日码、地址码 // 检查顺序码、生日码、地址码
if !checkOrderCode(code["order"]) || !checkBirthdayCode(code["birthdayCode"]) || !checkAddressCode(code["addressCode"], code["birthdayCode"]) { if !CheckOrderCode(code["order"]) || !CheckBirthdayCode(code["birthdayCode"]) || !CheckAddressCode(code["addressCode"], code["birthdayCode"]) {
return false return false
} }
@ -40,21 +40,21 @@ func IsValid(id string) bool {
} }
// 校验码 // 校验码
return code["checkBit"] == generatorCheckBit(code["body"]) return code["checkBit"] == GeneratorCheckBit(code["body"])
} }
// 获取身份证信息 // 获取身份证信息
func GetInfo(id string) (idInfo, error) { func GetInfo(id string) (IdInfo, error) {
// 验证有效性 // 验证有效性
if !IsValid(id) { if !IsValid(id) {
return idInfo{}, errors.New("Not Valid ID card number.") return IdInfo{}, errors.New("Not Valid ID card number.")
} }
code, _ := generateCode(id) code, _ := GenerateCode(id)
addressCode, _ := strconv.Atoi(code["addressCode"]) addressCode, _ := strconv.Atoi(code["addressCode"])
// 地址信息 // 地址信息
addressInfo := getAddressInfo(code["addressCode"], code["birthdayCode"]) addressInfo := GetAddressInfo(code["addressCode"], code["birthdayCode"])
var addressTree []string var addressTree []string
for _, val := range addressInfo { for _, val := range addressInfo {
addressTree = append(addressTree, val) addressTree = append(addressTree, val)
@ -79,14 +79,14 @@ func GetInfo(id string) (idInfo, error) {
// 长度 // 长度
length, _ := strconv.Atoi(code["type"]) length, _ := strconv.Atoi(code["type"])
return idInfo{ return IdInfo{
AddressCode: addressCode, AddressCode: addressCode,
Abandoned: abandoned, Abandoned: abandoned,
Address: addressInfo["province"] + addressInfo["city"] + addressInfo["district"], Address: addressInfo["province"] + addressInfo["city"] + addressInfo["district"],
AddressTree: addressTree, AddressTree: addressTree,
Birthday: birthday, Birthday: birthday,
Constellation: getConstellation(code["birthdayCode"]), Constellation: GetConstellation(code["birthdayCode"]),
ChineseZodiac: getChineseZodiac(code["birthdayCode"]), ChineseZodiac: GetChineseZodiac(code["birthdayCode"]),
Sex: sex, Sex: sex,
Length: length, Length: length,
CheckBit: code["checkBit"], CheckBit: code["checkBit"],
@ -105,21 +105,21 @@ func FakeId() string {
// sex 性别1为男性0为女性 // sex 性别1为男性0为女性
func FakeRequireId(isEighteen bool, address string, birthday string, sex int) string { func FakeRequireId(isEighteen bool, address string, birthday string, sex int) string {
// 生成地址码 // 生成地址码
addressCode := generatorAddressCode(address) addressCode := GeneratorAddressCode(address)
// 出生日期码 // 出生日期码
birthdayCode := generatorBirthdayCode(birthday) birthdayCode := GeneratorBirthdayCode(birthday)
// 生成顺序码 // 生成顺序码
orderCode := generatorOrderCode(sex) orderCode := GeneratorOrderCode(sex)
if !isEighteen { if !isEighteen {
return addressCode + substr(birthdayCode, 2, 8) + orderCode return addressCode + Substr(birthdayCode, 2, 8) + orderCode
} }
body := addressCode + birthdayCode + orderCode body := addressCode + birthdayCode + orderCode
return body + generatorCheckBit(body) return body + GeneratorCheckBit(body)
} }
// 15位升级18位号码 // 15位升级18位号码
@ -128,9 +128,9 @@ func UpgradeId(id string) (string, error) {
return "", errors.New("Not Valid ID card number.") return "", errors.New("Not Valid ID card number.")
} }
code, _ := generateShortCode(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
} }