feat: bank card.

This commit is contained in:
tx7do
2023-10-17 23:06:53 +08:00
parent d402e4bd0b
commit 733bfb6199
17 changed files with 2632 additions and 164 deletions

13
bank_card/README.md Normal file
View File

@@ -0,0 +1,13 @@
# 银行卡BIN查询银行
## 参考资料
- [China UnionPay Bank Card BIN Checker](https://github.com/hexindai/bcbc)
- [CommonUtilLibrary](https://github.com/AbrahamCaiJin/CommonUtilLibrary/blob/master/CommonUtil/src/main/java/com/jingewenku/abrahamcaijin/commonutil/BankCheck.java)
- [Luhn algorithm - wikipedia](https://en.wikipedia.org/wiki/Luhn_algorithm)
- [Luhn algorithm - geeksforgeeks](https://www.geeksforgeeks.org/luhn-algorithm/)
- [Luhns algorithm to validate credit / debit card Numbers](https://medium.com/@akshaymohite/luhns-algorithm-to-validate-credit-debit-card-numbers-1952e6c7a9d0)
- [干货丨银行卡号编码规则及其应用](https://www.woshipm.com/pd/371041.html)
- [giraffe-lib](https://github.com/giraffe-lib/bankInfo/blob/main/src/map.js)
- [BankCards](https://github.com/geekgao/BankCards/blob/master/bankcode.py)
- [bcbc](https://github.com/hexindai/bcbc)

BIN
bank_card/bank_card.db Normal file

Binary file not shown.

View File

@@ -1,4 +1,4 @@
package common
package bank_card
import "strings"
@@ -629,58 +629,6 @@ var BANKNAME = []string{
"玉溪市商业银行·红塔卡",
}
// IsValidLuhn 使用Luhn算法校验银行卡号码
// @see https://en.wikipedia.org/wiki/Luhn_algorithm
// @see https://www.geeksforgeeks.org/luhn-algorithm/
// @see https://medium.com/@akshaymohite/luhns-algorithm-to-validate-credit-debit-card-numbers-1952e6c7a9d0
// @see https://www.woshipm.com/pd/371041.html
func IsValidLuhn(cardNo string) bool {
length := len(cardNo)
if length == 0 {
return false
}
if !isNumberString(cardNo) {
return false
}
sum := 0
second := false
for i := length - 1; i >= 0; i-- {
d := cardNo[i] - '0'
if second == true {
d = d * 2
}
sum += int(d) / 10
sum += int(d) % 10
second = !second
}
return sum%10 == 0
}
func isNumberString(s string) bool {
length := len(s)
for i := 0; i < length; i++ {
if s[i] < '0' || s[i] > '9' {
return false
}
}
return true
}
// IsValidBankCardNo 是否合法的银行卡号
func IsValidBankCardNo(cardNo string) bool {
length := len(cardNo)
if length < 12 || length > 19 {
return false
}
return IsValidLuhn(cardNo)
}
// GetNameOfBank 通过银行卡 的前六位确定 判断银行开户行及卡种
// @see https://github.com/AbrahamCaiJin/CommonUtilLibrary/blob/master/CommonUtil/src/main/java/com/jingewenku/abrahamcaijin/commonutil/BankCheck.java
func GetNameOfBank(cardNo string) string {

View File

@@ -1,4 +1,4 @@
package common
package bank_card
import (
"fmt"
@@ -7,106 +7,6 @@ import (
"github.com/stretchr/testify/assert"
)
func TestIsValidLuhn(t *testing.T) {
var validBankCards = []string{
"79927398713",
"6226095711989751",
"49927398716",
"1234567812345670",
"356938035643809",
"1111222233334444",
"374652346956782346957823694857692364857368475368",
"6228480402564890018",
"6228480402637874213",
"6228481552887309119",
"6228480801416266113",
"6228481698729890079",
"6222081106004039591",
}
var invalidBankCards = []string{
"49927398717",
"1234567812345678",
"314143525252",
"534618613411236",
"1234567879855431",
"374652346956782346957823694857692364857387456834",
"79927398710",
"79927398711",
"79927398711",
"79927398712",
"79927398714",
"79927398715",
"79927398716",
"79927398717",
"79927398718",
"79927398719",
"621661280000447287",
}
for _, w := range validBankCards {
t.Run("valid bank card: "+w, func(t *testing.T) {
assert.True(t, IsValidLuhn(w))
})
}
for _, w := range invalidBankCards {
t.Run("invalid bank card: "+w, func(t *testing.T) {
assert.False(t, IsValidLuhn(w))
})
}
}
func TestIsValidBankCardNo(t *testing.T) {
var validBankCards = []string{
"6226095711989751",
"6228480402564890018",
"6228480402637874213",
"6228481552887309119",
"6228480801416266113",
"6228481698729890079",
"6222081106004039591",
"1234567812345670",
"356938035643809",
"1111222233334444",
}
var invalidBankCards = []string{
"49927398717",
"1234567812345678",
"314143525252",
"534618613411236",
"1234567879855431",
"374652346956782346957823694857692364857387456834",
"79927398710",
"79927398711",
"79927398711",
"79927398712",
"79927398714",
"79927398715",
"79927398716",
"79927398717",
"79927398718",
"79927398719",
"621661280000447287",
"79927398713",
"49927398716",
"374652346956782346957823694857692364857368475368",
}
for _, w := range validBankCards {
t.Run("valid bank card: "+w, func(t *testing.T) {
assert.True(t, IsValidBankCardNo(w))
})
}
for _, w := range invalidBankCards {
t.Run("invalid bank card: "+w, func(t *testing.T) {
assert.False(t, IsValidBankCardNo(w))
})
}
}
func TestGetNameOfBank(t *testing.T) {
var validBankCards = []string{
"6226095711989751",

1710
bank_card/bin.csv Normal file

File diff suppressed because it is too large Load Diff

161
bank_card/database.go Normal file
View File

@@ -0,0 +1,161 @@
package bank_card
import (
"context"
"database/sql"
_ "embed"
"fmt"
"log"
"github.com/mattn/go-sqlite3"
_ "github.com/mattn/go-sqlite3"
)
//go:embed bank_card.db
var embeddedDatabase []byte
type Database struct {
db *sql.DB
}
func NewDatabase() *Database {
return &Database{}
}
// OpenFromFile 从文件打开数据库
func (d *Database) OpenFromFile() {
db, err := sql.Open("sqlite3", "bank_card.db")
if err != nil {
panic("failed to connect database")
}
d.db = db
d.initTables()
}
// OpenFromEmbed 从内嵌文件打开数据库
func (d *Database) OpenFromEmbed() error {
db, err := sql.Open("sqlite3", "file::memory:?mode=ro&cache=shared")
if err != nil {
panic("failed to connect database")
return err
}
conn, err := db.Conn(context.Background())
if err != nil {
log.Fatal(err)
return err
}
defer conn.Close()
if err = conn.Raw(func(raw interface{}) error {
return raw.(*sqlite3.SQLiteConn).Deserialize(embeddedDatabase, "")
}); err != nil {
log.Fatal(err)
return err
}
conn.Close()
d.db = db
return nil
}
func (d *Database) Close() {
if d.db != nil {
_ = d.db.Close()
}
}
// initTables 创建表
func (d *Database) initTables() {
strSql := `
CREATE TABLE IF NOT EXISTS bank_cards
(
bin INTEGER PRIMARY KEY,
bank_code TEXT,
card_name TEXT,
card_type TEXT,
card_length INTEGER
);
CREATE TABLE IF NOT EXISTS banks
(
id INTEGER PRIMARY KEY autoincrement,
bank_code TEXT,
bank_name TEXT
);
`
_, err := d.db.Exec(strSql)
if err != nil {
log.Println("initial database table failed:", err)
}
}
func (d *Database) insertDataToBankTable(data *Bank) {
strSql := fmt.Sprintf("INSERT INTO banks (bank_code, bank_name) VALUES ('%s', '%s');", data.BankCode, data.BankName)
_, err := d.db.Exec(strSql)
if err != nil {
log.Println(err)
}
}
func (d *Database) insertDataToBankCardTable(data *BankCard) {
strSql := fmt.Sprintf("INSERT INTO bank_cards (bin, bank_code, card_name, card_type, card_length) VALUES (%d, '%s', '%s', '%s', %d);",
data.BIN, data.BankCode, data.CardName, data.CardType, data.CardLength)
_, err := d.db.Exec(strSql)
if err != nil {
log.Println(err)
}
}
// queryBank 查询银行信息
func (d *Database) queryBank(bankCode string) *Bank {
strSql := fmt.Sprintf("SELECT * FROM banks WHERE bank_code = '%s' LIMIT 1;", bankCode)
rows, err := d.db.Query(strSql)
if err != nil {
log.Fatal("query bank failed:", err)
return nil
}
defer rows.Close()
rows.Next()
var bank Bank
if err = rows.Scan(&bank.Id, &bank.BankCode, &bank.BankName); err != nil {
log.Fatal("scan bank failed:", err)
return nil
}
//fmt.Printf("bank = %v\n", bank)
return &bank
}
// queryBankCard 查询银行卡信息
func (d *Database) queryBankCard(bin uint32) *BankCard {
strSql := fmt.Sprintf("SELECT * FROM bank_cards WHERE bin = '%d' LIMIT 1;", bin)
rows, err := d.db.Query(strSql)
if err != nil {
log.Fatal("query bank card failed:", err)
return nil
}
defer rows.Close()
rows.Next()
var bankCard BankCard
if err = rows.Scan(&bankCard.BIN, &bankCard.BankCode, &bankCard.CardName, &bankCard.CardType, &bankCard.CardLength); err != nil {
log.Fatal("scan bank card failed:", err)
return nil
}
rows.Close()
bank := d.queryBank(bankCard.BankCode)
if bank != nil {
bankCard.BankName = bank.BankName
}
//fmt.Printf("bankCard = %v\n", bankCard)
return &bankCard
}

163
bank_card/database_test.go Normal file
View File

@@ -0,0 +1,163 @@
package bank_card
import (
"encoding/csv"
"fmt"
"github.com/stretchr/testify/assert"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
func TestImportBankName(t *testing.T) {
db := NewDatabase()
db.OpenFromFile()
defer db.Close()
file, err := os.Open("name.csv")
if err != nil {
fmt.Println("Read file err, err =", err)
return
}
defer file.Close()
fileinfo, err := file.Stat()
if err != nil {
fmt.Println(err)
return
}
filesize := fileinfo.Size()
buffer := make([]byte, filesize)
_, err = file.Read(buffer)
if err != nil {
fmt.Println(err)
return
}
r := csv.NewReader(strings.NewReader(string(buffer)))
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if len(record) == 2 && len(record[0]) > 0 && len(record[1]) > 0 {
db.insertDataToBankTable(&Bank{BankCode: record[0], BankName: record[1]})
}
fmt.Println(record)
}
}
func TestImportBankCard(t *testing.T) {
db := NewDatabase()
db.OpenFromFile()
defer db.Close()
file, err := os.Open("bin.csv")
if err != nil {
fmt.Println("Read file err, err =", err)
return
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
fmt.Println(err)
return
}
filesize := fileInfo.Size()
buffer := make([]byte, filesize)
_, err = file.Read(buffer)
if err != nil {
fmt.Println(err)
return
}
r := csv.NewReader(strings.NewReader(string(buffer)))
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if len(record) == 4 && len(record[0]) > 0 && len(record[1]) > 0 && len(record[2]) > 0 && len(record[3]) > 0 {
bin, _ := strconv.Atoi(record[0])
cardLength, _ := strconv.Atoi(record[3])
db.insertDataToBankCardTable(&BankCard{
BIN: uint32(bin),
BankCode: record[1],
CardType: record[2],
CardLength: uint32(cardLength),
})
}
fmt.Println(record)
}
}
func TestImportBankCardSingle(t *testing.T) {
db := NewDatabase()
db.OpenFromFile()
defer db.Close()
binStr := "620114|620187|620046"
strs := strings.Split(binStr, "|")
var bins []uint32
for _, str := range strs {
bin, _ := strconv.Atoi(str)
bins = append(bins, uint32(bin))
}
bankCode := "ABC"
cardType := "DC"
cardLength := 13 + len(strs[0])
for _, data := range bins {
bankData := &BankCard{
BIN: data,
BankCode: bankCode,
CardType: cardType,
CardLength: uint32(cardLength),
}
db.insertDataToBankCardTable(bankData)
fmt.Println(bankData)
}
}
func TestOpenFromEmbed(t *testing.T) {
db := NewDatabase()
defer db.Close()
err := db.OpenFromEmbed()
assert.Nil(t, err)
var bank *Bank
bank = db.queryBank("ABC")
assert.NotNil(t, bank)
assert.Equal(t, bank.BankCode, "ABC")
assert.Equal(t, bank.BankName, "中国农业银行")
var bankCard *BankCard
bankCard = db.queryBankCard(620114)
assert.NotNil(t, bankCard)
assert.Equal(t, bankCard.BankCode, "ICBC")
assert.Equal(t, bankCard.BankName, "中国工商银行")
assert.Equal(t, bankCard.CardType, "PC")
assert.Equal(t, bankCard.CardName, "")
assert.Equal(t, bankCard.CardLength, uint32(19))
}

14
bank_card/go.mod Normal file
View File

@@ -0,0 +1,14 @@
module github.com/tx7do/kratos-utils/bank_card
go 1.20
require (
github.com/mattn/go-sqlite3 v1.14.17
github.com/stretchr/testify v1.8.4
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

12
bank_card/go.sum Normal file
View File

@@ -0,0 +1,12 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

42
bank_card/models.go Normal file
View File

@@ -0,0 +1,42 @@
package bank_card
type CardType string
const (
CardTypeDC CardType = "DC" // 储蓄卡
CardTypeCC CardType = "CC" // 信用卡
CardTypeSCC CardType = "SCC" // 准贷记卡
CardTypePC CardType = "PC" // 预付费卡
)
// Bank 银行信息
type Bank struct {
Id uint32 `gorm:"primarykey,column:id"` // ID
BankCode string `gorm:"uniqueIndex,column:bank_code"` // 银行简称
BankName string `gorm:"column:bank_name"` // 银行名称
}
// BankCard 银行卡信息
type BankCard struct {
BIN uint32 `gorm:"primarykey,column:bin"` // 银行识别码
BankCode string `gorm:"column:bank_code"` // 银行代码
BankName string // 银行名称
CardType string `gorm:"column:card_type"` // 银行卡类型
CardName string `gorm:"column:card_name"` // 银行卡名称
CardLength uint32 `gorm:"column:card_length"` // 银行卡号长度
}
// CardTypeName 将卡类型转为类型名
func (b *BankCard) CardTypeName() string {
switch CardType(b.CardType) {
case CardTypeDC:
return "储蓄卡"
case CardTypeCC:
return "信用卡"
case CardTypeSCC:
return "准贷记卡"
case CardTypePC:
return "预付费卡"
}
return ""
}

299
bank_card/name.csv Normal file
View File

@@ -0,0 +1,299 @@
ABC,中国农业银行
AEON,AEON银行
ARCU,安徽省农村信用社
ASCB,鞍山银行
AYCB,安阳银行
BANKWF,潍坊银行
BCDMMA,澳门商业银行
BDCBANK,保定银行
BEAHK,东亚银行有限公司
BGB,广西北部湾银行
BHB,河北银行
BJBANK,北京银行
BJRCB,北京农村商业银行
BNU,大西洋银行
BOC,中国银行
BOCD,承德银行
BOCFCB,中银富登村镇银行
BOCY,朝阳银行
BOCZ,沧州银行
BOD,东莞银行
BODD,丹东银行
BOHAIB,渤海银行
BOHN,海南省农村信用社
BOJZ,锦州银行
BOL,洛阳银行
BOP,平顶山银行
BOQH,青海银行
BOQZ,泉州银行
BOSH,新韩银行(中国)
BOSZ,苏州银行
BOYK,营口银行
BOZK,周口银行
BSB,包商银行
BZMD,驻马店银行
CAB,宝鸡商行
CABANK,长安银行
CBBQS,城市商业银行资金清算中心
CBKF,开封市商业银行
CCB,中国建设银行
CCQTGB,重庆三峡银行
CDB,国家开发银行
CDCB,成都银行
CDRCB,成都农商银行
CEB,中国光大银行
CEP,中银通商务支付有限公司
CGNB,南充市商业银行
CHB,创兴银行有限公司
CIB,兴业银行
CITI,花旗银行(中国)
CITIC,中信银行
CJCCB,苏州长江商业银行
CMB,招商银行
CMBC,中国民生银行
COMM,交通银行
COMMHK,交通银行香港分行
CQBANK,重庆银行
CRCBANK,重庆农村商业银行
CSC,CSC借记卡
CSCB,长沙银行
CSRCB,常熟农商银行
CYBC,集友银行
CYS,中国银盛
CZBANK,浙商银行
CZCB,浙江稠州商业银行
CZCCB,长治市商业银行
CZRCB,常州农村信用联社
DAQINGB,龙江银行
DBS,星展银行
DBSCN,星展银行
DLB,大连银行
DRCBCL,东莞农村商业银行
DSB,大新银行
DSBCN,大新银行(中国)
DSBHK,大新银行(香港)
DTCCB,大同银行
DYCB,德阳商业银行
DYCCB,东营市商业银行
DZBANK,德州银行
DZCBANK,达州银行
EGBANK,恒丰银行
ERB,龙岗鼎业村镇银行
ESRCB,恩施农村商业银行
FBBANK,富邦华一银行
FDB,富滇银行
FJHXBC,福建海峡银行
FJNX,福建省农村信用社联合社
FMBANK,吉林丰满惠民村镇银行
FSCB,抚顺银行
FSCCB,抚顺银行股份有限公司
FXCB,阜新银行
GCB,广州银行
GDB,广东发展银行
GDRCC,广东省农村信用社联合社
GHB,广东华兴银行
GLBANK,桂林银行
GRCB,广州农商银行
GSBANK,甘肃银行
GSRCU,甘肃省农村信用社
GXHMVB,长春高新惠民村镇银行
GXRCU,广西省农村信用社
GYCB,贵阳市商业银行
GZB,赣州银行
GZRCU,贵州省农村信用社
H3CB,内蒙古银行
HANABANK,韩亚银行
HBC,湖北银行
HBHSBANK,湖北银行黄石分行
HBRCU,河北省农村信用社
HBYCBANK,湖北银行宜昌分行
HDBANK,邯郸银行
HDHMCB,惠东惠民村镇银行
HKB,汉口银行
HKBEA,东亚银行
HLDB,葫芦岛市商业银行
HLDCCB,葫芦岛市商业银行
HLJRCU,黑龙江省农村信用社
HNRCC,湖南省农村信用社
HNRCU,河南省农村信用社
HRBANK,哈尔滨银行
HRXJB,华融湘江银行
HSB,恒生银行
HSBANK,徽商银行
HSBC,汇丰银行(中国)
HSBK,衡水银行
HURCB,湖北省农村信用社
HXBANK,华夏银行
HZCB,杭州银行
HZCCB,湖州市商业银行
ICBC,中国工商银行
JHBANK,金华银行
JINCHB,晋城银行
JJBANK,九江银行
JLBANK,吉林银行
JLRCU,吉林农信
JNBANK,济宁银行
JRCB,江苏江阴农村商业银行
JSB,晋商银行
JSBANK,江苏银行
JSRCU,江苏省农村信用联合社
JXBANK,嘉兴银行
JXRCU,江西省农村信用社
JZBANK,晋中市商业银行
JZCBANK,焦作市商业银行
KLB,昆仑银行
KMRCU,昆明农村信用联合社
KORLABANK,库尔勒市商业银行
KSRB,昆山农村商业银行
KSRCB,昆山农信社
LANGFB,廊坊银行
LNRCC,辽宁省农村信用社
LSB,临商银行
LSBANK,莱商银行
LSBC,临商银行
LSCCB,乐山市商业银行
LSZRCB,凉山州商业银行
LUZBANK,泸州市商业银行
LYBANK,洛阳银行
LYCB,辽阳市商业银行
LZCCB,柳州银行
LZYH,兰州银行
MTBANK,浙江民泰商业银行
MYBANK,绵阳市商业银行
NBBANK,宁波银行
NBYZ,鄞州银行
NCB,江西银行
NCBANK,宁波通商银行
NDHB,宁波东海银行
NHB,南海农商银行
NHQS,农信银清算中心
NJCB,南京银行
NMGNXS,内蒙古农村信用社
NTCCB,南通商业银行
NXBANK,宁夏银行
NXRCU,宁夏黄河农村商业银行
NYBANK,广东南粤银行
NYCB,南阳村镇银行
NYNB,广东南粤银行
OCBC,华侨银行(中国)
ORBANK,鄂尔多斯银行
PLCCB,平凉市商业银行
PSBC,中国邮政储蓄银行
PZBANK,盘锦市商业银行
PZHCCB,攀枝花市商业银行
QDCCB,青岛银行
QDRCB,青岛农村商业银行
QHDBANK,秦皇岛银行
QHDCCB,秦皇岛市商业银行
QHRC,青海农村信用社
QJCCB,曲靖市商业银行
QLBANK,齐鲁银行
QLCZYH,齐鲁村镇银行
RBOZ,珠海华润银行
RXCB,榆次融信村镇银行
RZB,日照银行
RZCCB,日照银行
SCB,渣打银行
SCBHK,渣打银行(香港)
SCCB,三门峡银行
SCRCU,四川省农村信用社
SDEB,顺德农商银行
SDRCU,山东省农村信用社
SHBANK,上海银行
SHCBHK,上海商业银行(香港)
SHRCB,上海农村商业银行
SJBANK,盛京银行
SLH,湖南农村信用社联合社
SLRCB,双流诚民村镇银行
SNCCB,遂宁市商业银行
SPABANK,平安银行
SPDB,上海浦东发展银行
SRBANK,上饶银行
SRCB,深圳农村商业银行
SXCB,绍兴银行
SXRCCU,陕西信合
SXRCU,山西省农村信用社
SZSBK,石嘴山银行
TACCB,泰安市商业银行
TCCB,天津银行
TCRCB,江苏太仓农村商业银行
TFB,大丰银行有限公司
TJBHB,天津滨海农村商业银行
TLBANK,铁岭银行
TRCB,天津农商银行
TSBANK,唐山银行
TZCB,台州银行
URB,联合村镇银行
URMQCCB,乌鲁木齐市商业银行
WHB,永亨银行
WHBANK,乌海银行
WHBMA,澳门永亨银行
WHCCB,威海市商业银行
WHRCB,武汉农村商业银行
WJRCB,吴江农商银行
WLB,永隆银行
WLBHK,永隆银行
WOORI,友利银行(中国)
WRCB,无锡农村商业银行
WXCCB,无锡市商业银行
WZCB,温州银行
XABANK,西安银行
XCYH,许昌银行
XFRCB,咸丰常农商村镇银行
XHCZYH,新华村镇银行
XHRCB,新会农商银行
XJRCU,新疆农村信用社
XLBANK,中山小榄村镇银行
XMBANK,厦门银行
XMCCB,厦门市商业银行
XTB,邢台银行
XXBANK,新乡银行
XYBANK,信阳银行
YACCB,雅安市商业银行
YBCCB,宜宾市商业银行
YCCB,宜昌市商业银行
YCCCB,盐城商行
YDNSCZYH,尧都农商村镇银行
YDRCB,尧都农商行
YKYHCCB,营口沿海银行
YNRCC,云南省农村信用社
YQCCB,阳泉银行
YTBANK,烟台银行
YXCCB,玉溪市商业银行
YZBANK,银座银行
ZBCB,齐商银行
ZGCCB,自贡市商业银行
ZHCCB,珠海华润银行股份有限公司
ZJCCB,镇江市商业银行
ZJKCCB,张家口市商业银行
ZJNX,浙江省农村信用社联合社
ZJTLCB,浙江泰隆商业银行
ZRCBANK,张家港农村商业银行
ZYB,中原银行
ZYCBANK,遵义市商业银行
ZZBANK,郑州银行
ZZCCB,郑州商业银行
1 ABC 中国农业银行
2 AEON AEON银行
3 ARCU 安徽省农村信用社
4 ASCB 鞍山银行
5 AYCB 安阳银行
6 BANKWF 潍坊银行
7 BCDMMA 澳门商业银行
8 BDCBANK 保定银行
9 BEAHK 东亚银行有限公司
10 BGB 广西北部湾银行
11 BHB 河北银行
12 BJBANK 北京银行
13 BJRCB 北京农村商业银行
14 BNU 大西洋银行
15 BOC 中国银行
16 BOCD 承德银行
17 BOCFCB 中银富登村镇银行
18 BOCY 朝阳银行
19 BOCZ 沧州银行
20 BOD 东莞银行
21 BODD 丹东银行
22 BOHAIB 渤海银行
23 BOHN 海南省农村信用社
24 BOJZ 锦州银行
25 BOL 洛阳银行
26 BOP 平顶山银行
27 BOQH 青海银行
28 BOQZ 泉州银行
29 BOSH 新韩银行(中国)
30 BOSZ 苏州银行
31 BOYK 营口银行
32 BOZK 周口银行
33 BSB 包商银行
34 BZMD 驻马店银行
35 CAB 宝鸡商行
36 CABANK 长安银行
37 CBBQS 城市商业银行资金清算中心
38 CBKF 开封市商业银行
39 CCB 中国建设银行
40 CCQTGB 重庆三峡银行
41 CDB 国家开发银行
42 CDCB 成都银行
43 CDRCB 成都农商银行
44 CEB 中国光大银行
45 CEP 中银通商务支付有限公司
46 CGNB 南充市商业银行
47 CHB 创兴银行有限公司
48 CIB 兴业银行
49 CITI 花旗银行(中国)
50 CITIC 中信银行
51 CJCCB 苏州长江商业银行
52 CMB 招商银行
53 CMBC 中国民生银行
54 COMM 交通银行
55 COMMHK 交通银行香港分行
56 CQBANK 重庆银行
57 CRCBANK 重庆农村商业银行
58 CSC CSC借记卡
59 CSCB 长沙银行
60 CSRCB 常熟农商银行
61 CYBC 集友银行
62 CYS 中国银盛
63 CZBANK 浙商银行
64 CZCB 浙江稠州商业银行
65 CZCCB 长治市商业银行
66 CZRCB 常州农村信用联社
67 DAQINGB 龙江银行
68 DBS 星展银行
69 DBSCN 星展银行
70 DLB 大连银行
71 DRCBCL 东莞农村商业银行
72 DSB 大新银行
73 DSBCN 大新银行(中国)
74 DSBHK 大新银行(香港)
75 DTCCB 大同银行
76 DYCB 德阳商业银行
77 DYCCB 东营市商业银行
78 DZBANK 德州银行
79 DZCBANK 达州银行
80 EGBANK 恒丰银行
81 ERB 龙岗鼎业村镇银行
82 ESRCB 恩施农村商业银行
83 FBBANK 富邦华一银行
84 FDB 富滇银行
85 FJHXBC 福建海峡银行
86 FJNX 福建省农村信用社联合社
87 FMBANK 吉林丰满惠民村镇银行
88 FSCB 抚顺银行
89 FSCCB 抚顺银行股份有限公司
90 FXCB 阜新银行
91 GCB 广州银行
92 GDB 广东发展银行
93 GDRCC 广东省农村信用社联合社
94 GHB 广东华兴银行
95 GLBANK 桂林银行
96 GRCB 广州农商银行
97 GSBANK 甘肃银行
98 GSRCU 甘肃省农村信用社
99 GXHMVB 长春高新惠民村镇银行
100 GXRCU 广西省农村信用社
101 GYCB 贵阳市商业银行
102 GZB 赣州银行
103 GZRCU 贵州省农村信用社
104 H3CB 内蒙古银行
105 HANABANK 韩亚银行
106 HBC 湖北银行
107 HBHSBANK 湖北银行黄石分行
108 HBRCU 河北省农村信用社
109 HBYCBANK 湖北银行宜昌分行
110 HDBANK 邯郸银行
111 HDHMCB 惠东惠民村镇银行
112 HKB 汉口银行
113 HKBEA 东亚银行
114 HLDB 葫芦岛市商业银行
115 HLDCCB 葫芦岛市商业银行
116 HLJRCU 黑龙江省农村信用社
117 HNRCC 湖南省农村信用社
118 HNRCU 河南省农村信用社
119 HRBANK 哈尔滨银行
120 HRXJB 华融湘江银行
121 HSB 恒生银行
122 HSBANK 徽商银行
123 HSBC 汇丰银行(中国)
124 HSBK 衡水银行
125 HURCB 湖北省农村信用社
126 HXBANK 华夏银行
127 HZCB 杭州银行
128 HZCCB 湖州市商业银行
129 ICBC 中国工商银行
130 JHBANK 金华银行
131 JINCHB 晋城银行
132 JJBANK 九江银行
133 JLBANK 吉林银行
134 JLRCU 吉林农信
135 JNBANK 济宁银行
136 JRCB 江苏江阴农村商业银行
137 JSB 晋商银行
138 JSBANK 江苏银行
139 JSRCU 江苏省农村信用联合社
140 JXBANK 嘉兴银行
141 JXRCU 江西省农村信用社
142 JZBANK 晋中市商业银行
143 JZCBANK 焦作市商业银行
144 KLB 昆仑银行
145 KMRCU 昆明农村信用联合社
146 KORLABANK 库尔勒市商业银行
147 KSRB 昆山农村商业银行
148 KSRCB 昆山农信社
149 LANGFB 廊坊银行
150 LNRCC 辽宁省农村信用社
151 LSB 临商银行
152 LSBANK 莱商银行
153 LSBC 临商银行
154 LSCCB 乐山市商业银行
155 LSZRCB 凉山州商业银行
156 LUZBANK 泸州市商业银行
157 LYBANK 洛阳银行
158 LYCB 辽阳市商业银行
159 LZCCB 柳州银行
160 LZYH 兰州银行
161 MTBANK 浙江民泰商业银行
162 MYBANK 绵阳市商业银行
163 NBBANK 宁波银行
164 NBYZ 鄞州银行
165 NCB 江西银行
166 NCBANK 宁波通商银行
167 NDHB 宁波东海银行
168 NHB 南海农商银行
169 NHQS 农信银清算中心
170 NJCB 南京银行
171 NMGNXS 内蒙古农村信用社
172 NTCCB 南通商业银行
173 NXBANK 宁夏银行
174 NXRCU 宁夏黄河农村商业银行
175 NYBANK 广东南粤银行
176 NYCB 南阳村镇银行
177 NYNB 广东南粤银行
178 OCBC 华侨银行(中国)
179 ORBANK 鄂尔多斯银行
180 PLCCB 平凉市商业银行
181 PSBC 中国邮政储蓄银行
182 PZBANK 盘锦市商业银行
183 PZHCCB 攀枝花市商业银行
184 QDCCB 青岛银行
185 QDRCB 青岛农村商业银行
186 QHDBANK 秦皇岛银行
187 QHDCCB 秦皇岛市商业银行
188 QHRC 青海农村信用社
189 QJCCB 曲靖市商业银行
190 QLBANK 齐鲁银行
191 QLCZYH 齐鲁村镇银行
192 RBOZ 珠海华润银行
193 RXCB 榆次融信村镇银行
194 RZB 日照银行
195 RZCCB 日照银行
196 SCB 渣打银行
197 SCBHK 渣打银行(香港)
198 SCCB 三门峡银行
199 SCRCU 四川省农村信用社
200 SDEB 顺德农商银行
201 SDRCU 山东省农村信用社
202 SHBANK 上海银行
203 SHCBHK 上海商业银行(香港)
204 SHRCB 上海农村商业银行
205 SJBANK 盛京银行
206 SLH 湖南农村信用社联合社
207 SLRCB 双流诚民村镇银行
208 SNCCB 遂宁市商业银行
209 SPABANK 平安银行
210 SPDB 上海浦东发展银行
211 SRBANK 上饶银行
212 SRCB 深圳农村商业银行
213 SXCB 绍兴银行
214 SXRCCU 陕西信合
215 SXRCU 山西省农村信用社
216 SZSBK 石嘴山银行
217 TACCB 泰安市商业银行
218 TCCB 天津银行
219 TCRCB 江苏太仓农村商业银行
220 TFB 大丰银行有限公司
221 TJBHB 天津滨海农村商业银行
222 TLBANK 铁岭银行
223 TRCB 天津农商银行
224 TSBANK 唐山银行
225 TZCB 台州银行
226 URB 联合村镇银行
227 URMQCCB 乌鲁木齐市商业银行
228 WHB 永亨银行
229 WHBANK 乌海银行
230 WHBMA 澳门永亨银行
231 WHCCB 威海市商业银行
232 WHRCB 武汉农村商业银行
233 WJRCB 吴江农商银行
234 WLB 永隆银行
235 WLBHK 永隆银行
236 WOORI 友利银行(中国)
237 WRCB 无锡农村商业银行
238 WXCCB 无锡市商业银行
239 WZCB 温州银行
240 XABANK 西安银行
241 XCYH 许昌银行
242 XFRCB 咸丰常农商村镇银行
243 XHCZYH 新华村镇银行
244 XHRCB 新会农商银行
245 XJRCU 新疆农村信用社
246 XLBANK 中山小榄村镇银行
247 XMBANK 厦门银行
248 XMCCB 厦门市商业银行
249 XTB 邢台银行
250 XXBANK 新乡银行
251 XYBANK 信阳银行
252 YACCB 雅安市商业银行
253 YBCCB 宜宾市商业银行
254 YCCB 宜昌市商业银行
255 YCCCB 盐城商行
256 YDNSCZYH 尧都农商村镇银行
257 YDRCB 尧都农商行
258 YKYHCCB 营口沿海银行
259 YNRCC 云南省农村信用社
260 YQCCB 阳泉银行
261 YTBANK 烟台银行
262 YXCCB 玉溪市商业银行
263 YZBANK 银座银行
264 ZBCB 齐商银行
265 ZGCCB 自贡市商业银行
266 ZHCCB 珠海华润银行股份有限公司
267 ZJCCB 镇江市商业银行
268 ZJKCCB 张家口市商业银行
269 ZJNX 浙江省农村信用社联合社
270 ZJTLCB 浙江泰隆商业银行
271 ZRCBANK 张家港农村商业银行
272 ZYB 中原银行
273 ZYCBANK 遵义市商业银行
274 ZZBANK 郑州银行
275 ZZCCB 郑州商业银行

54
bank_card/validator.go Normal file
View File

@@ -0,0 +1,54 @@
package bank_card
// IsValidLuhn 使用Luhn算法校验银行卡号码
// @see https://en.wikipedia.org/wiki/Luhn_algorithm
// @see https://www.geeksforgeeks.org/luhn-algorithm/
// @see https://medium.com/@akshaymohite/luhns-algorithm-to-validate-credit-debit-card-numbers-1952e6c7a9d0
// @see https://www.woshipm.com/pd/371041.html
func IsValidLuhn(cardNo string) bool {
length := len(cardNo)
if length == 0 {
return false
}
if !isNumberString(cardNo) {
return false
}
sum := 0
second := false
for i := length - 1; i >= 0; i-- {
d := cardNo[i] - '0'
if second == true {
d = d * 2
}
sum += int(d) / 10
sum += int(d) % 10
second = !second
}
return sum%10 == 0
}
// IsValidBankCardNo 是否合法的银行卡号
func IsValidBankCardNo(cardNo string) bool {
length := len(cardNo)
if length < 12 || length > 19 {
return false
}
return IsValidLuhn(cardNo)
}
// isNumberString 验证字符是数字
func isNumberString(s string) bool {
length := len(s)
for i := 0; i < length; i++ {
if s[i] < '0' || s[i] > '9' {
return false
}
}
return true
}

106
bank_card/validator_test.go Normal file
View File

@@ -0,0 +1,106 @@
package bank_card
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestIsValidLuhn(t *testing.T) {
var validBankCards = []string{
"79927398713",
"6226095711989751",
"49927398716",
"1234567812345670",
"356938035643809",
"1111222233334444",
"374652346956782346957823694857692364857368475368",
"6228480402564890018",
"6228480402637874213",
"6228481552887309119",
"6228480801416266113",
"6228481698729890079",
"6222081106004039591",
}
var invalidBankCards = []string{
"49927398717",
"1234567812345678",
"314143525252",
"534618613411236",
"1234567879855431",
"374652346956782346957823694857692364857387456834",
"79927398710",
"79927398711",
"79927398711",
"79927398712",
"79927398714",
"79927398715",
"79927398716",
"79927398717",
"79927398718",
"79927398719",
"621661280000447287",
}
for _, w := range validBankCards {
t.Run("valid bank card: "+w, func(t *testing.T) {
assert.True(t, IsValidLuhn(w))
})
}
for _, w := range invalidBankCards {
t.Run("invalid bank card: "+w, func(t *testing.T) {
assert.False(t, IsValidLuhn(w))
})
}
}
func TestIsValidBankCardNo(t *testing.T) {
var validBankCards = []string{
"6226095711989751",
"6228480402564890018",
"6228480402637874213",
"6228481552887309119",
"6228480801416266113",
"6228481698729890079",
"6222081106004039591",
"1234567812345670",
"356938035643809",
"1111222233334444",
}
var invalidBankCards = []string{
"49927398717",
"1234567812345678",
"314143525252",
"534618613411236",
"1234567879855431",
"374652346956782346957823694857692364857387456834",
"79927398710",
"79927398711",
"79927398711",
"79927398712",
"79927398714",
"79927398715",
"79927398716",
"79927398717",
"79927398718",
"79927398719",
"621661280000447287",
"79927398713",
"49927398716",
"374652346956782346957823694857692364857368475368",
}
for _, w := range validBankCards {
t.Run("valid bank card: "+w, func(t *testing.T) {
assert.True(t, IsValidBankCardNo(w))
})
}
for _, w := range invalidBankCards {
t.Run("invalid bank card: "+w, func(t *testing.T) {
assert.False(t, IsValidBankCardNo(w))
})
}
}

18
geoip/go.mod Normal file
View File

@@ -0,0 +1,18 @@
module github.com/tx7do/kratos-utils/geoip
go 1.20
require (
github.com/go-kratos/kratos/v2 v2.7.0
github.com/oschwald/geoip2-golang v1.9.0
github.com/stretchr/testify v1.8.4
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/oschwald/maxminddb-golang v1.11.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.10.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

23
geoip/go.sum Normal file
View File

@@ -0,0 +1,23 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-kratos/kratos/v2 v2.7.0 h1:9DaVgU9YoHPb/BxDVqeVlVCMduRhiSewG3xE+e9ZAZ8=
github.com/go-kratos/kratos/v2 v2.7.0/go.mod h1:CPn82O93OLHjtnbuyOKhAG5TkSvw+mFnL32c4lZFDwU=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

7
go.mod
View File

@@ -5,9 +5,7 @@ go 1.20
require (
entgo.io/contrib v0.4.5
entgo.io/ent v0.12.4
github.com/go-kratos/kratos/v2 v2.7.0
github.com/google/uuid v1.3.1
github.com/oschwald/geoip2-golang v1.9.0
github.com/sony/sonyflake v1.2.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.13.0
@@ -24,16 +22,19 @@ require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/hashicorp/hcl/v2 v2.18.0 // indirect
github.com/jhump/protoreflect v1.15.2 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/zclconf/go-cty v1.14.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

18
go.sum
View File

@@ -11,10 +11,9 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-kratos/kratos/v2 v2.7.0 h1:9DaVgU9YoHPb/BxDVqeVlVCMduRhiSewG3xE+e9ZAZ8=
github.com/go-kratos/kratos/v2 v2.7.0/go.mod h1:CPn82O93OLHjtnbuyOKhAG5TkSvw+mFnL32c4lZFDwU=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
@@ -30,21 +29,25 @@ github.com/hashicorp/hcl/v2 v2.18.0 h1:wYnG7Lt31t2zYkcquwgKo6MWXzRUDIeIVU5naZwHL
github.com/hashicorp/hcl/v2 v2.18.0/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/jhump/protoreflect v1.15.2 h1:7YppbATX94jEt9KLAc5hICx4h6Yt3SaavhQRsIUEHP0=
github.com/jhump/protoreflect v1.15.2/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ=
github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
@@ -67,8 +70,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 h1:9JucMWR7sPvCxUFd6UsOUNmA5kCcWOfORaT3tpAsKQs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
@@ -76,5 +79,6 @@ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=