Compare commits
8 Commits
geoip/v1.1
...
bank_card/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b29f09e37 | ||
|
|
363a18b1c8 | ||
|
|
e90588b9ca | ||
|
|
713975e7f1 | ||
|
|
2ab920982a | ||
|
|
3153ff149f | ||
|
|
578cf26ee8 | ||
|
|
ef08927a50 |
@@ -3,7 +3,7 @@ module github.com/tx7do/go-utils/bank_card
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/mattn/go-sqlite3 v1.14.25
|
||||
github.com/mattn/go-sqlite3 v1.14.28
|
||||
github.com/stretchr/testify v1.10.0
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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.25 h1:rszkIulEvxqZ8JfFG4yWEZh5u9qAKeSOdea67p8kk6s=
|
||||
github.com/mattn/go-sqlite3 v1.14.25/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
|
||||
21
crypto/README.md
Normal file
21
crypto/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 加解密算法
|
||||
|
||||
## bcrypt
|
||||
|
||||
bcrypt是一个由美国计算机科学家尼尔斯·普罗沃斯(Niels Provos)以及大卫·马齐耶(David Mazières)根据Blowfish加密算法所设计的密码散列函数,于1999年在USENIX中展示[1]。实现中bcrypt会使用一个加盐的流程以防御彩虹表攻击,同时bcrypt还是适应性函数,它可以借由增加迭代之次数来抵御日益增进的电脑运算能力透过暴力法破解。
|
||||
|
||||
由bcrypt加密的文件可在所有支持的操作系统和处理器上进行转移。它的口令必须是8至56个字符,并将在内部被转化为448位的密钥。然而,所提供的所有字符都具有十分重要的意义。密码越强大,数据就越安全。
|
||||
|
||||
除了对数据进行加密,默认情况下,bcrypt在删除数据之前将使用随机数据三次覆盖原始输入文件,以阻挠可能会获得计算机数据的人恢复数据的尝试。如果您不想使用此功能,可设置禁用此功能。
|
||||
|
||||
具体来说,bcrypt使用美国密码学家保罗·柯切尔的算法实现。随bcrypt一起发布的源代码对原始版本作了略微改动。
|
||||
|
||||
bcrypt哈希由多个部分组成。这些部分用于确定创建哈希的设置,从而可以在不需要任何其他信息的情况下对其进行验证。
|
||||
|
||||
```text
|
||||
$2a$10$ygWrRwHCzg2GUpz0UK40kuWAGva121VkScpcdMNsDCih2U/bL2qYy
|
||||
```
|
||||
|
||||
- $2a$:Prefix 表示使用bcrypt的算法版本。
|
||||
- 10$:Cost factor 表示加密的复杂度,值越大,计算时间越长。
|
||||
- ygWrRwHCzg2GUpz0UK40kuWAGva121VkScpcdMNsDCih2U/bL2qYy:Salt 和 Hash,前22个字符是盐值,后面的字符是哈希值。
|
||||
74
crypto/aes.go
Normal file
74
crypto/aes.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
|
||||
"math/rand/v2"
|
||||
)
|
||||
|
||||
// DefaultAESKey 默认AES密钥(16字节)
|
||||
var DefaultAESKey = []byte("f51d66a73d8a0927")
|
||||
|
||||
// GenerateAESKey 生成AES密钥
|
||||
func GenerateAESKey(length int) ([]byte, error) {
|
||||
if length != 16 && length != 24 && length != 32 {
|
||||
return nil, fmt.Errorf("invalid key length: %d, must be 16, 24, or 32 bytes", length)
|
||||
}
|
||||
key := make([]byte, length)
|
||||
src := rand.ChaCha8{}
|
||||
_, err := src.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// PKCS5Padding 填充明文
|
||||
func PKCS5Padding(plaintext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(plaintext)%blockSize
|
||||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(plaintext, padText...)
|
||||
}
|
||||
|
||||
// PKCS5UnPadding 去除填充数据
|
||||
func PKCS5UnPadding(origData []byte) []byte {
|
||||
length := len(origData)
|
||||
unpadding := int(origData[length-1])
|
||||
return origData[:(length - unpadding)]
|
||||
}
|
||||
|
||||
// AesEncrypt AES加密
|
||||
func AesEncrypt(origData, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//AES分组长度为128位,所以blockSize=16,单位字节
|
||||
blockSize := block.BlockSize()
|
||||
origData = PKCS5Padding(origData, blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
|
||||
crypted := make([]byte, len(origData))
|
||||
blockMode.CryptBlocks(crypted, origData)
|
||||
return crypted, nil
|
||||
}
|
||||
|
||||
// AesDecrypt AES解密
|
||||
func AesDecrypt(crypted, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//AES分组长度为128位,所以blockSize=16,单位字节
|
||||
blockSize := block.BlockSize()
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
|
||||
origData := make([]byte, len(crypted))
|
||||
blockMode.CryptBlocks(origData, crypted)
|
||||
origData = PKCS5UnPadding(origData)
|
||||
return origData, nil
|
||||
}
|
||||
50
crypto/aes_test.go
Normal file
50
crypto/aes_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecryptAES(t *testing.T) {
|
||||
//key的长度必须是16、24或者32字节,分别用于选择AES-128, AES-192, or AES-256
|
||||
aesKey, _ := GenerateAESKey(16)
|
||||
aesKey = DefaultAESKey
|
||||
|
||||
plainText := []byte("cloud123456")
|
||||
encryptText, err := AesEncrypt(plainText, aesKey)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
pass64 := base64.StdEncoding.EncodeToString(encryptText)
|
||||
fmt.Printf("加密后:%v\n", pass64)
|
||||
|
||||
bytesPass, err := base64.StdEncoding.DecodeString(pass64)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
decryptText, err := AesDecrypt(bytesPass, aesKey)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("解密后:%s\n", decryptText)
|
||||
assert.Equal(t, plainText, decryptText)
|
||||
}
|
||||
|
||||
func TestGenerateAESKey_ValidLengths(t *testing.T) {
|
||||
lengths := []int{16, 24, 32}
|
||||
for _, length := range lengths {
|
||||
key, err := GenerateAESKey(length)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, length, len(key))
|
||||
t.Logf("%d : %x\n", length, string(key))
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,102 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// DefaultCost 最小值=4 最大值=31 默认值=10
|
||||
var DefaultCost = 10
|
||||
|
||||
// HashPassword 加密密码
|
||||
func HashPassword(password string) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
||||
// Prefix + Cost + Salt + Hashed Text
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), DefaultCost)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
// CheckPasswordHash 校验密码
|
||||
func CheckPasswordHash(password, hash string) bool {
|
||||
// HashPasswordWithSalt 对密码进行加盐哈希处理
|
||||
func HashPasswordWithSalt(password, salt string) (string, error) {
|
||||
// 将密码和盐组合
|
||||
combined := []byte(password + salt)
|
||||
|
||||
// 计算哈希值
|
||||
hash := sha256.Sum256(combined)
|
||||
|
||||
// 将哈希值转换为十六进制字符串
|
||||
return hex.EncodeToString(hash[:]), nil
|
||||
}
|
||||
|
||||
// VerifyPassword 验证密码是否正确
|
||||
func VerifyPassword(password, hash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// VerifyPasswordWithSalt 验证密码是否正确
|
||||
func VerifyPasswordWithSalt(password, salt, hashedPassword string) bool {
|
||||
// 对输入的密码和盐进行哈希处理
|
||||
newHash, _ := HashPasswordWithSalt(password, salt)
|
||||
// 比较哈希值是否相同
|
||||
return newHash == hashedPassword
|
||||
}
|
||||
|
||||
// GenerateSalt 生成指定长度的盐
|
||||
func GenerateSalt(length int) (string, error) {
|
||||
salt := make([]byte, length)
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(salt), nil
|
||||
}
|
||||
|
||||
// DecryptAES AES解密函数
|
||||
func DecryptAES(cipherText, key string) (string, error) {
|
||||
// 将密文从Base64解码
|
||||
cipherData, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 创建AES块
|
||||
block, err := aes.NewCipher([]byte(key))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 检查密文长度是否为块大小的倍数
|
||||
if len(cipherData)%aes.BlockSize != 0 {
|
||||
return "", fmt.Errorf("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
// 创建CBC解密器
|
||||
iv := cipherData[:aes.BlockSize] // 提取IV
|
||||
cipherData = cipherData[aes.BlockSize:]
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
|
||||
// 解密
|
||||
plainText := make([]byte, len(cipherData))
|
||||
mode.CryptBlocks(plainText, cipherData)
|
||||
|
||||
// 去除填充
|
||||
plainText = pkcs7Unpad(plainText)
|
||||
|
||||
return string(plainText), nil
|
||||
}
|
||||
|
||||
// pkcs7Unpad PKCS7填充去除
|
||||
func pkcs7Unpad(data []byte) []byte {
|
||||
length := len(data)
|
||||
padding := int(data[length-1])
|
||||
return data[:length-padding]
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ package crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHashPassword(t *testing.T) {
|
||||
@@ -13,13 +14,15 @@ func TestHashPassword(t *testing.T) {
|
||||
fmt.Println(hash)
|
||||
}
|
||||
|
||||
func TestCheckPasswordHash(t *testing.T) {
|
||||
func TestVerifyPassword(t *testing.T) {
|
||||
text := "123456"
|
||||
|
||||
// Prefix + Cost + Salt + Hashed Text
|
||||
hash3 := "$2a$10$ygWrRwHCzg2GUpz0UK40kuWAGva121VkScpcdMNsDCih2U/bL2qYy"
|
||||
bMatched := CheckPasswordHash(text, hash3)
|
||||
bMatched := VerifyPassword(text, hash3)
|
||||
assert.True(t, bMatched)
|
||||
|
||||
bMatched = CheckPasswordHash(text, hash3)
|
||||
bMatched = VerifyPassword(text, hash3)
|
||||
assert.True(t, bMatched)
|
||||
}
|
||||
|
||||
|
||||
12
entgo/go.mod
12
entgo/go.mod
@@ -11,13 +11,13 @@ require (
|
||||
github.com/go-kratos/kratos/v2 v2.8.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tx7do/go-utils v1.1.14
|
||||
github.com/tx7do/go-utils v1.1.16
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.32.0 // indirect
|
||||
ariga.io/atlas v0.32.1 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||
@@ -33,7 +33,7 @@ require (
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sony/sonyflake v1.2.0 // indirect
|
||||
github.com/sony/sonyflake v1.2.1 // indirect
|
||||
github.com/zclconf/go-cty v1.16.2 // indirect
|
||||
github.com/zclconf/go-cty-yaml v1.1.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
@@ -41,9 +41,9 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
||||
28
entgo/go.sum
28
entgo/go.sum
@@ -1,5 +1,5 @@
|
||||
ariga.io/atlas v0.32.0 h1:y+77nueMrExLiKlz1CcPKh/nU7VSlWfBbwCShsJyvCw=
|
||||
ariga.io/atlas v0.32.0/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w=
|
||||
ariga.io/atlas v0.32.1 h1:cVCxz6D2SRvlKn/1Yne+WaZEFBjji7lR4DklgP+6Py0=
|
||||
ariga.io/atlas v0.32.1/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w=
|
||||
entgo.io/contrib v0.6.0 h1:xfo4TbJE7sJZWx7BV7YrpSz7IPFvS8MzL3fnfzZjKvQ=
|
||||
entgo.io/contrib v0.6.0/go.mod h1:3qWIseJ/9Wx2Hu5zVh15FDzv7d/UvKNcYKdViywWCQg=
|
||||
entgo.io/ent v0.14.4 h1:/DhDraSLXIkBhyiVoJeSshr4ZYi7femzhj6/TckzZuI=
|
||||
@@ -53,8 +53,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ=
|
||||
github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
||||
github.com/sony/sonyflake v1.2.1 h1:Jzo4abS84qVNbYamXZdrZF1/6TzNJjEogRfXv7TsG48=
|
||||
github.com/sony/sonyflake v1.2.1/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
|
||||
@@ -79,16 +79,16 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package entgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/tx7do/go-utils/stringcase"
|
||||
"strings"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
|
||||
"github.com/go-kratos/kratos/v2/encoding"
|
||||
|
||||
"github.com/tx7do/go-utils/stringcase"
|
||||
)
|
||||
|
||||
type FilterOp int
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/go-kratos/kratos/v2/encoding"
|
||||
_ "github.com/go-kratos/kratos/v2/encoding/json"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/go-kratos/kratos/v2 v2.8.4
|
||||
github.com/oschwald/geoip2-golang v1.11.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/text v0.23.0
|
||||
golang.org/x/text v0.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -16,7 +16,7 @@ require (
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
||||
12
geoip/go.sum
12
geoip/go.sum
@@ -17,12 +17,12 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
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=
|
||||
|
||||
6
go.mod
6
go.mod
@@ -8,10 +8,10 @@ require (
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gosimple/slug v1.15.0
|
||||
github.com/sony/sonyflake v1.2.0
|
||||
github.com/sony/sonyflake v1.2.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||
golang.org/x/crypto v0.38.0
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
||||
|
||||
12
go.sum
12
go.sum
@@ -24,14 +24,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
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/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ=
|
||||
github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
||||
github.com/sony/sonyflake v1.2.1 h1:Jzo4abS84qVNbYamXZdrZF1/6TzNJjEogRfXv7TsG48=
|
||||
github.com/sony/sonyflake v1.2.1/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
27
rand/rand.go
27
rand/rand.go
@@ -1,16 +1,16 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/tx7do/go-utils/math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
var rnd = rand.New(rand.NewSource(Seed(UnixNanoSeed)))
|
||||
|
||||
func init() {
|
||||
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
if rnd != nil {
|
||||
rnd = rand.New(rand.NewSource(Seed(UnixNanoSeed)))
|
||||
}
|
||||
}
|
||||
|
||||
func Float32() float32 {
|
||||
@@ -59,18 +59,17 @@ func RandomInt64(min, max int64) int64 {
|
||||
|
||||
// RandomString 随机字符串,包含大小写字母和数字
|
||||
func RandomString(l int) string {
|
||||
if l <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
bytes := make([]byte, l)
|
||||
for i := 0; i < l; i++ {
|
||||
x := Intn(3)
|
||||
switch x {
|
||||
case 0:
|
||||
bytes[i] = byte(RandomInt(65, 90)) //大写字母
|
||||
case 1:
|
||||
bytes[i] = byte(RandomInt(97, 122))
|
||||
case 2:
|
||||
bytes[i] = byte(Intn(10))
|
||||
}
|
||||
bytes[i] = charset[Intn(len(charset))]
|
||||
}
|
||||
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
|
||||
@@ -208,11 +208,13 @@ func TestRandomInt64_ZeroRange(t *testing.T) {
|
||||
func TestRandomString_LengthZero(t *testing.T) {
|
||||
result := RandomString(0)
|
||||
assert.Equal(t, "", result)
|
||||
t.Logf("LengthZero: %s", result)
|
||||
}
|
||||
|
||||
func TestRandomString_PositiveLength(t *testing.T) {
|
||||
result := RandomString(10)
|
||||
assert.Len(t, result, 10)
|
||||
t.Logf("PositiveLength: %s", result)
|
||||
}
|
||||
|
||||
func TestRandomString_ContainsOnlyValidCharacters(t *testing.T) {
|
||||
@@ -220,6 +222,7 @@ func TestRandomString_ContainsOnlyValidCharacters(t *testing.T) {
|
||||
for _, char := range result {
|
||||
assert.True(t, (char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9'))
|
||||
}
|
||||
t.Logf("ContainsOnlyValidCharacters: %s", result)
|
||||
}
|
||||
|
||||
func TestRandomString_NegativeLength(t *testing.T) {
|
||||
|
||||
179
rand/randomizer.go
Normal file
179
rand/randomizer.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"github.com/tx7do/go-utils/math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type Randomizer struct {
|
||||
rnd *rand.Rand
|
||||
}
|
||||
|
||||
func NewRandomizer(seedType SeedType) *Randomizer {
|
||||
return &Randomizer{
|
||||
rnd: rand.New(rand.NewSource(Seed(seedType))),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Randomizer) Float32() float32 {
|
||||
return r.rnd.Float32()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Float64() float64 {
|
||||
return r.rnd.Float64()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int() int {
|
||||
return r.rnd.Int()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int31() int32 {
|
||||
return r.rnd.Int31()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int63() int64 {
|
||||
return r.rnd.Int63()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Uint32() uint32 {
|
||||
return r.rnd.Uint32()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Uint64() uint64 {
|
||||
return r.rnd.Uint64()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Intn(n int) int {
|
||||
return r.rnd.Intn(n)
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int31n(n int32) int32 {
|
||||
return r.rnd.Int31n(n)
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int63n(n int64) int64 {
|
||||
return r.rnd.Int63n(n)
|
||||
}
|
||||
|
||||
// RangeInt 根据区间产生随机数
|
||||
func (r *Randomizer) RangeInt(min, max int) int {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Intn(max-min+1)
|
||||
}
|
||||
|
||||
// RangeInt32 根据区间产生随机数
|
||||
func (r *Randomizer) RangeInt32(min, max int32) int32 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Int31n(max-min+1)
|
||||
}
|
||||
|
||||
// RangeInt64 根据区间产生随机数
|
||||
func (r *Randomizer) RangeInt64(min, max int64) int64 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Int63n(max-min+1)
|
||||
}
|
||||
|
||||
// RangeUint 根据区间产生随机数
|
||||
func (r *Randomizer) RangeUint(min, max uint) uint {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + uint(r.Intn(int(max-min+1)))
|
||||
}
|
||||
|
||||
// RangeUint32 根据区间产生随机数
|
||||
func (r *Randomizer) RangeUint32(min, max uint32) uint32 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + uint32(r.Int31n(int32(max-min+1)))
|
||||
}
|
||||
|
||||
// RangeUint64 根据区间产生随机数
|
||||
func (r *Randomizer) RangeUint64(min, max uint64) uint64 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + uint64(r.Int63n(int64(max-min+1)))
|
||||
}
|
||||
|
||||
// RangeFloat32 根据区间产生随机数
|
||||
func (r *Randomizer) RangeFloat32(min, max float32) float32 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Float32()*(max-min)
|
||||
}
|
||||
|
||||
// RangeFloat64 根据区间产生随机数
|
||||
func (r *Randomizer) RangeFloat64(min, max float64) float64 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Float64()*(max-min)
|
||||
}
|
||||
|
||||
// RandomString 随机字符串,包含大小写字母和数字
|
||||
func (r *Randomizer) RandomString(l int) string {
|
||||
bytes := make([]byte, l)
|
||||
for i := 0; i < l; i++ {
|
||||
x := r.Intn(3)
|
||||
switch x {
|
||||
case 0:
|
||||
bytes[i] = byte(r.RangeInt(65, 90)) //大写字母
|
||||
case 1:
|
||||
bytes[i] = byte(r.RangeInt(97, 122))
|
||||
case 2:
|
||||
bytes[i] = byte(r.Intn(10))
|
||||
}
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// WeightedChoice 根据权重随机,返回对应选项的索引,O(n)
|
||||
func (r *Randomizer) WeightedChoice(weightArray []int) int {
|
||||
if weightArray == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
total := math.SumInt(weightArray)
|
||||
rv := r.Int63n(total)
|
||||
for i, v := range weightArray {
|
||||
if rv < int64(v) {
|
||||
return i
|
||||
}
|
||||
rv -= int64(v)
|
||||
}
|
||||
|
||||
return len(weightArray) - 1
|
||||
}
|
||||
|
||||
// NonWeightedChoice 根据权重随机,返回对应选项的索引,O(n). 权重大于等于0
|
||||
func (r *Randomizer) NonWeightedChoice(weightArray []int) int {
|
||||
if weightArray == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
for i, weight := range weightArray {
|
||||
if weight < 0 {
|
||||
weightArray[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
total := math.SumInt(weightArray)
|
||||
rv := r.Int63n(total)
|
||||
for i, v := range weightArray {
|
||||
if rv < int64(v) {
|
||||
return i
|
||||
}
|
||||
rv -= int64(v)
|
||||
}
|
||||
|
||||
return len(weightArray) - 1
|
||||
}
|
||||
86
rand/randomizer_test.go
Normal file
86
rand/randomizer_test.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRandomizer_RangeUint32_MinEqualsMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint32(5, 5)
|
||||
assert.Equal(t, uint32(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint32_MinGreaterThanMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint32(10, 5)
|
||||
assert.Equal(t, uint32(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint32_PositiveRange(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
for i := 0; i < 1000; i++ {
|
||||
result := r.RangeUint32(1, 10)
|
||||
assert.True(t, result >= 1)
|
||||
assert.True(t, result <= 10)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint64_MinEqualsMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint64(5, 5)
|
||||
assert.Equal(t, uint64(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint64_MinGreaterThanMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint64(10, 5)
|
||||
assert.Equal(t, uint64(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint64_PositiveRange(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
for i := 0; i < 1000; i++ {
|
||||
result := r.RangeUint64(1, 10)
|
||||
assert.True(t, result >= 1)
|
||||
assert.True(t, result <= 10)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomizer_WeightedChoice_EmptyArray(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.WeightedChoice([]int{})
|
||||
assert.Equal(t, -1, result)
|
||||
}
|
||||
|
||||
func TestRandomizer_WeightedChoice_AllZeroWeights(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.WeightedChoice([]int{0, 0, 0})
|
||||
assert.Equal(t, 2, result)
|
||||
}
|
||||
|
||||
func TestRandomizer_WeightedChoice_MixedWeights(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
weightArray := []int{1, 0, 3, 0, 2}
|
||||
counts := make([]int, len(weightArray))
|
||||
for i := 0; i < 1000; i++ {
|
||||
choice := r.WeightedChoice(weightArray)
|
||||
counts[choice]++
|
||||
}
|
||||
assert.Greater(t, counts[0], 0)
|
||||
assert.Greater(t, counts[2], 0)
|
||||
assert.Greater(t, counts[4], 0)
|
||||
}
|
||||
|
||||
func TestRandomizer_RandomString_LengthZero(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RandomString(0)
|
||||
assert.Equal(t, "", result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RandomString_CorrectLength(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
length := 50
|
||||
result := r.RandomString(length)
|
||||
assert.Equal(t, length, len(result))
|
||||
}
|
||||
99
rand/seeder.go
Normal file
99
rand/seeder.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
cryptoRand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"golang.org/x/exp/rand"
|
||||
"hash/maphash"
|
||||
mathRand "math/rand"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SeedType string
|
||||
|
||||
const (
|
||||
UnixNanoSeed SeedType = "UnixNano"
|
||||
MapHashSeed SeedType = "MapHash"
|
||||
CryptoRandSeed SeedType = "CryptoRand"
|
||||
RandomStringSeed SeedType = "RandomString"
|
||||
)
|
||||
|
||||
type Seeder struct {
|
||||
seedType SeedType
|
||||
}
|
||||
|
||||
func NewSeeder(seedType SeedType) *Seeder {
|
||||
return &Seeder{
|
||||
seedType: seedType,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Seeder) UnixNano() int64 {
|
||||
// 获取当前时间戳
|
||||
timestamp := time.Now().UnixNano()
|
||||
|
||||
// 生成一个随机数
|
||||
var randomBytes [8]byte
|
||||
_, err := rand.Read(randomBytes[:])
|
||||
if err != nil {
|
||||
panic("failed to generate random bytes")
|
||||
}
|
||||
randomPart := int64(binary.LittleEndian.Uint64(randomBytes[:]))
|
||||
|
||||
// 获取 Goroutine ID(或其他唯一标识)
|
||||
goroutineID := int64(runtime.NumGoroutine())
|
||||
|
||||
// 结合时间戳、随机数和 Goroutine ID
|
||||
seed := timestamp ^ randomPart ^ goroutineID
|
||||
|
||||
return seed
|
||||
}
|
||||
|
||||
func (r *Seeder) MapHash() int64 {
|
||||
return int64(new(maphash.Hash).Sum64())
|
||||
}
|
||||
|
||||
func (r *Seeder) CryptoRand() int64 {
|
||||
var b [8]byte
|
||||
_, err := cryptoRand.Read(b[:])
|
||||
if err != nil {
|
||||
panic("cannot seed math/rand package with cryptographically secure random number generator")
|
||||
}
|
||||
seed := int64(binary.LittleEndian.Uint64(b[:]))
|
||||
return seed
|
||||
}
|
||||
|
||||
var Alpha = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
||||
|
||||
func (r *Seeder) RandomString() int64 {
|
||||
const size = 8
|
||||
buf := make([]byte, size)
|
||||
for i := 0; i < size; i++ {
|
||||
buf[i] = Alpha[mathRand.Intn(len(Alpha))]
|
||||
}
|
||||
seed := int64(binary.LittleEndian.Uint64(buf[:]))
|
||||
|
||||
return seed
|
||||
}
|
||||
|
||||
func (r *Seeder) Seed() int64 {
|
||||
switch r.seedType {
|
||||
default:
|
||||
fallthrough
|
||||
case UnixNanoSeed:
|
||||
return r.UnixNano()
|
||||
case MapHashSeed:
|
||||
return r.MapHash()
|
||||
case CryptoRandSeed:
|
||||
return r.CryptoRand()
|
||||
case RandomStringSeed:
|
||||
return r.RandomString()
|
||||
}
|
||||
}
|
||||
|
||||
// Seed generates a seed based on the specified SeedType.
|
||||
func Seed(seedType SeedType) int64 {
|
||||
randomizer := NewSeeder(seedType)
|
||||
return randomizer.Seed()
|
||||
}
|
||||
80
rand/seeder_test.go
Normal file
80
rand/seeder_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnixNanoSeed(t *testing.T) {
|
||||
seeder := NewSeeder(UnixNanoSeed)
|
||||
|
||||
var seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := seeder.Seed()
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("UnixNano Seed", len(seeds))
|
||||
}
|
||||
|
||||
func TestMapHashSeed(t *testing.T) {
|
||||
seeder := NewSeeder(MapHashSeed)
|
||||
|
||||
var seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := seeder.Seed()
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("MapHash Seed", len(seeds))
|
||||
}
|
||||
|
||||
func TestCryptoRandSeed(t *testing.T) {
|
||||
seeder := NewSeeder(CryptoRandSeed)
|
||||
|
||||
var seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := seeder.Seed()
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("CryptoRand Seed", len(seeds))
|
||||
}
|
||||
|
||||
func TestRandomStringSeed(t *testing.T) {
|
||||
seeder := NewSeeder(RandomStringSeed)
|
||||
|
||||
var seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := seeder.Seed()
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("RandomString Seed", len(seeds))
|
||||
}
|
||||
|
||||
func TestSeed(t *testing.T) {
|
||||
var seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := Seed(UnixNanoSeed)
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("UnixNano Seed", len(seeds))
|
||||
|
||||
seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := Seed(MapHashSeed)
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("MapHash Seed", len(seeds))
|
||||
|
||||
seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := Seed(CryptoRandSeed)
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("CryptoRand Seed", len(seeds))
|
||||
|
||||
seeds = make(map[int64]bool)
|
||||
for i := 0; i < 100000; i++ {
|
||||
seed := Seed(RandomStringSeed)
|
||||
seeds[seed] = true
|
||||
}
|
||||
fmt.Println("RandomString Seed", len(seeds))
|
||||
}
|
||||
10
tag.bat
10
tag.bat
@@ -1,9 +1,9 @@
|
||||
git tag v1.1.14
|
||||
git tag v1.1.17
|
||||
|
||||
git tag bank_card/v1.1.4
|
||||
git tag geoip/v1.1.4
|
||||
git tag bank_card/v1.1.5
|
||||
git tag geoip/v1.1.5
|
||||
|
||||
git tag entgo/v1.1.25
|
||||
git tag gorm/v1.1.4
|
||||
git tag entgo/v1.1.26
|
||||
git tag gorm/v1.1.5
|
||||
|
||||
git push origin --tags
|
||||
|
||||
@@ -165,3 +165,18 @@ func DurationpbToNumber[T int | int8 | int16 | int32 | int64 | uint | uint8 | ui
|
||||
secondsWithPrecision := T(seconds / timePrecision.Seconds())
|
||||
return &secondsWithPrecision
|
||||
}
|
||||
|
||||
func DurationToDurationpb(duration *time.Duration) *durationpb.Duration {
|
||||
if duration == nil {
|
||||
return nil
|
||||
}
|
||||
return durationpb.New(*duration)
|
||||
}
|
||||
|
||||
func DurationpbToDuration(duration *durationpb.Duration) *time.Duration {
|
||||
if duration == nil {
|
||||
return nil
|
||||
}
|
||||
d := duration.AsDuration()
|
||||
return &d
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user