| @ -0,0 +1,75 @@ | |||||
| package helper | |||||
| import ( | |||||
| "bytes" | |||||
| "crypto/aes" | |||||
| "crypto/cipher" | |||||
| "encoding/base64" | |||||
| ) | |||||
| // PKCS7Padding 对数据进行填充 | |||||
| func PKCS7Padding(data []byte, blockSize int) []byte { | |||||
| padding := blockSize - len(data)%blockSize | |||||
| padtext := bytes.Repeat([]byte{byte(padding)}, padding) | |||||
| return append(data, padtext...) | |||||
| } | |||||
| // PKCS7UnPadding 去除填充 | |||||
| func PKCS7UnPadding(data []byte) []byte { | |||||
| length := len(data) | |||||
| unpadding := int(data[length-1]) | |||||
| return data[:(length - unpadding)] | |||||
| } | |||||
| // EncryptAES 对数据进行 AES 加密 | |||||
| func EncryptAES(str, key_str string) (string, error) { | |||||
| plaintext := []byte(str) | |||||
| key := []byte(key_str) | |||||
| // 创建一个新的 AES 块 | |||||
| block, err := aes.NewCipher(key) | |||||
| if err != nil { | |||||
| return "", err | |||||
| } | |||||
| // 块大小 | |||||
| blockSize := block.BlockSize() | |||||
| // 对明文进行填充 | |||||
| plaintext = PKCS7Padding(plaintext, blockSize) | |||||
| // 创建一个 CBC 模式的加密块 | |||||
| ciphertext := make([]byte, len(plaintext)) | |||||
| mode := cipher.NewCBCEncrypter(block, key[:blockSize]) | |||||
| // 加密数据 | |||||
| mode.CryptBlocks(ciphertext, plaintext) | |||||
| // 将加密结果进行 Base64 编码 | |||||
| return base64.StdEncoding.EncodeToString(ciphertext), nil | |||||
| } | |||||
| // DecryptAES 对数据进行 AES 解密 | |||||
| func DecryptAES(ciphertext, key_str string) ([]byte, error) { | |||||
| key := []byte(key_str) | |||||
| // 对 Base64 编码的密文进行解码 | |||||
| ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| // 创建一个新的 AES 块 | |||||
| block, err := aes.NewCipher(key) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| // 块大小 | |||||
| blockSize := block.BlockSize() | |||||
| // 创建一个 CBC 模式的解密块 | |||||
| mode := cipher.NewCBCDecrypter(block, key[:blockSize]) | |||||
| // 解密数据 | |||||
| plaintext := make([]byte, len(ciphertextBytes)) | |||||
| mode.CryptBlocks(plaintext, ciphertextBytes) | |||||
| // 去除填充 | |||||
| plaintext = PKCS7UnPadding(plaintext) | |||||
| return plaintext, nil | |||||
| } | |||||
| @ -0,0 +1,21 @@ | |||||
| package helper | |||||
| import ( | |||||
| "testing" | |||||
| ) | |||||
| func Test_EncryptAES(t *testing.T) { | |||||
| // 要加密的明文 | |||||
| //str := `{"open_id":"op2kh5PTc2-4u3XaADjjbkxeXPn0","userName":"山野雾灯"}` | |||||
| // 密钥,AES 支持 16、24 或 32 字节的密钥 | |||||
| key := "0123456789shopef0123456789shopef" | |||||
| /*ret, err := EncryptAES(str, key) | |||||
| t.Log(err) | |||||
| t.Log(ret)*/ | |||||
| ret := "jpWI1PWjg9GKmSMRv8WiuL4CI/vZG3xgTfOiLrbY8ZK+T8poUgK7fFPkj7H2tGSHbB1A/ucOjjSwEvbQDYZfOw==" | |||||
| res, err := DecryptAES(ret, key) | |||||
| t.Log(err) | |||||
| t.Log(string(res)) | |||||
| } | |||||
| @ -0,0 +1,387 @@ | |||||
| package helper | |||||
| import ( | |||||
| "bytes" | |||||
| "encoding/base64" | |||||
| "encoding/hex" | |||||
| "errors" | |||||
| "git.tetele.net/tgo/crypter" | |||||
| "github.com/ZZMarquis/gm/sm2" | |||||
| "github.com/ZZMarquis/gm/sm3" | |||||
| jsoniter "github.com/json-iterator/go" | |||||
| "io/ioutil" | |||||
| "log" | |||||
| "math/big" | |||||
| "net/http" | |||||
| "reflect" | |||||
| "sort" | |||||
| "strings" | |||||
| "time" | |||||
| ) | |||||
| var json = jsoniter.ConfigCompatibleWithStandardLibrary | |||||
| // 合并map数据 | |||||
| func MergeMapData(x, y map[string]interface{}) map[string]interface{} { | |||||
| n := make(map[string]interface{}) | |||||
| for i, v := range x { | |||||
| for j, w := range y { | |||||
| if i == j { | |||||
| n[i] = w | |||||
| } else { | |||||
| if _, ok := n[i]; !ok { | |||||
| n[i] = v | |||||
| } | |||||
| if _, ok := n[j]; !ok { | |||||
| n[j] = w | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return n | |||||
| } | |||||
| // 针对传参为json或者数组类型的字段,进行字符串排序 | |||||
| func HttpBuildSort(data map[string]interface{}) string { | |||||
| keySlice := []string{} | |||||
| for k, _ := range data { | |||||
| keySlice = append(keySlice, k) | |||||
| } | |||||
| // 排序字符串 | |||||
| sort.Strings(keySlice) | |||||
| res := make(map[string]interface{}, 0) | |||||
| for _, value := range keySlice { | |||||
| data_val := ToStr(data[value]) | |||||
| if value != "" && data_val != "" { | |||||
| res[value] = data_val | |||||
| } | |||||
| } | |||||
| res_byte, _ := json.Marshal(res) | |||||
| return string(res_byte) | |||||
| } | |||||
| // body参数处理 | |||||
| func FormatBodyData(body map[string]interface{}) map[string]interface{} { | |||||
| data := map[string]interface{}{} | |||||
| if len(body) > 0 { | |||||
| for k, v := range body { | |||||
| typeOfA := reflect.TypeOf(v) | |||||
| if typeOfA.Kind().String() == "map" { | |||||
| build_str_format, _ := InterfaceToMapInterface(v) | |||||
| if len(build_str_format) < 1 { | |||||
| continue | |||||
| } | |||||
| // json键值对处理 | |||||
| data[k] = HttpBuildSort(build_str_format) | |||||
| // log.Println("排序后的数据: ", data[k]) | |||||
| } else if typeOfA.Kind().String() == "slice" { | |||||
| val_slice := []interface{}{} | |||||
| // json键值对处理 | |||||
| for _, val_inter := range v.([]interface{}) { | |||||
| val_map, _ := InterfaceToMapInterface(val_inter) | |||||
| each_val := HttpBuildSort(val_map) | |||||
| val_slice = append(val_slice, each_val) | |||||
| } | |||||
| if len(val_slice) < 1 { | |||||
| continue | |||||
| } | |||||
| data[k] = val_slice | |||||
| // log.Println("排序后的数据: ", data[k]) | |||||
| } else { | |||||
| data[k] = v | |||||
| } | |||||
| } | |||||
| } | |||||
| return data | |||||
| } | |||||
| // 字符串排序拼接 | |||||
| func CmbHttpBuildQuery(data map[string]interface{}, filter_sign bool) string { | |||||
| keySlice := []string{} | |||||
| // 剔除sign字段 | |||||
| for k, _ := range data { | |||||
| if filter_sign && k == "sign" { | |||||
| continue | |||||
| } | |||||
| keySlice = append(keySlice, k) | |||||
| } | |||||
| // 排序字符串 | |||||
| sort.Strings(keySlice) | |||||
| var query string | |||||
| for _, value := range keySlice { | |||||
| if value != "" && data[value] != "" { | |||||
| query += StringJoin(value, "=", ToStr(data[value]), "&") | |||||
| } | |||||
| } | |||||
| build_str := strings.Trim(query, "&") | |||||
| return build_str | |||||
| } | |||||
| // 生成签名sign--聚合支付 | |||||
| func SetSign(data map[string]interface{}, privateKey string) string { | |||||
| build_str := CmbHttpBuildQuery(data, true) | |||||
| priv := new(sm2.PrivateKey) | |||||
| priv.Curve = sm2.GetSm2P256V1() | |||||
| dBytes, _ := hex.DecodeString(privateKey) | |||||
| priv.D = new(big.Int).SetBytes(dBytes) | |||||
| signature, err := sm2.Sign(priv, []byte("1234567812345678"), []byte(build_str)) | |||||
| if err != nil { | |||||
| log.Println("国密加密异常", err) | |||||
| return "" | |||||
| } | |||||
| sign := base64.StdEncoding.EncodeToString(signature) | |||||
| return sign | |||||
| } | |||||
| // 创建请求头信息--聚合支付 | |||||
| func HeaderInfo(appid, secret, sign string) map[string]string { | |||||
| if appid == "" || secret == "" || sign == "" { | |||||
| return map[string]string{} | |||||
| } | |||||
| headerData := map[string]string{} | |||||
| headerData["appid"] = appid | |||||
| headerData["sign"] = sign | |||||
| headerData["secret"] = secret | |||||
| headerData["timestamp"] = ToStr(time.Now().Unix()) | |||||
| build_str := CmbHttpBuildQuery(MapStringToInterface(headerData), false) | |||||
| headerData["apisign"] = crypter.Md5Str(build_str) | |||||
| delete(headerData, "sign") | |||||
| delete(headerData, "secret") | |||||
| return headerData | |||||
| } | |||||
| // 接口验签--各客户端请求我们接口验签 | |||||
| func CheckSign(appid, app_secret, timeStamp, nonce, sign string, body map[string]interface{}) error { | |||||
| /*time_now := time.Now().Unix() | |||||
| if time_now-ToInt64(timeStamp) > 10 { | |||||
| return errors.New("请求时间超出范围,拒绝访问") | |||||
| }*/ | |||||
| sign_info := map[string]interface{}{ | |||||
| "appid": appid, | |||||
| "timestamp": timeStamp, | |||||
| "nonce": nonce, | |||||
| } | |||||
| if len(body) > 0 { | |||||
| body_format := FormatBodyDataNoList(body) | |||||
| sign_info = MergeMapData(sign_info, body_format) | |||||
| } | |||||
| str := CmbHttpBuildQuery(sign_info, false) + "&" + app_secret | |||||
| sign_str := crypter.Md5Str(str) | |||||
| if sign_str != sign { | |||||
| return errors.New("签名不正确") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // post 请求 | |||||
| func PostJsonData(url string, param interface{}, headerinfo map[string]string) ([]byte, error) { | |||||
| httpClient := &http.Client{} | |||||
| params, err := json.Marshal(param) | |||||
| if err != nil { | |||||
| return []byte(""), err | |||||
| } | |||||
| req, err := http.NewRequest("POST", url, bytes.NewBuffer(params)) | |||||
| if err != nil { | |||||
| return []byte(""), err | |||||
| } | |||||
| req.Header.Set("Content-Type", "application/json") | |||||
| if len(headerinfo) > 0 { | |||||
| for k, v := range headerinfo { | |||||
| req.Header[k] = []string{v} | |||||
| } | |||||
| } | |||||
| resp, err := httpClient.Do(req) | |||||
| if err != nil { | |||||
| return []byte(""), err | |||||
| } | |||||
| defer resp.Body.Close() | |||||
| body, err := ioutil.ReadAll(resp.Body) | |||||
| if err != nil { | |||||
| return []byte(""), err | |||||
| } | |||||
| return body, nil | |||||
| } | |||||
| func SetSignsm3(data map[string]interface{}) string { | |||||
| build_str := HttpBuildjson(data, false) | |||||
| h := sm3.New() | |||||
| h.Write([]byte(build_str)) | |||||
| sum := h.Sum(nil) | |||||
| sign := hex.EncodeToString(sum) | |||||
| return sign | |||||
| } | |||||
| func CashierHeaderInfoCompany(appid, secret, sign, private_key string) map[string]string { | |||||
| if appid == "" || secret == "" || sign == "" || private_key == "" { | |||||
| return map[string]string{} | |||||
| } | |||||
| headerData := map[string]string{} | |||||
| headerData["appid"] = appid | |||||
| headerData["sign"] = sign | |||||
| headerData["secret"] = secret | |||||
| headerData["timestamp"] = ToStr(time.Now().Unix()) | |||||
| headerData["apisign"] = SetSignhex(headerData, private_key) | |||||
| headerData["verify"] = "SM3withSM2" | |||||
| delete(headerData, "secret") | |||||
| return headerData | |||||
| } | |||||
| // 生成签名sign | |||||
| func SetSignhex(data map[string]string, privateKey string) string { | |||||
| build_str := CmbHttpBuildQuery(MapStringToInterface(data), false) | |||||
| priv := new(sm2.PrivateKey) | |||||
| priv.Curve = sm2.GetSm2P256V1() | |||||
| dBytes, _ := hex.DecodeString(privateKey) | |||||
| priv.D = new(big.Int).SetBytes(dBytes) | |||||
| sign, _ := sm2.Sign(priv, []byte("1234567812345678"), []byte(build_str)) | |||||
| sign = SM2Asn1ToRaw(sign) | |||||
| return hex.EncodeToString(sign) | |||||
| } | |||||
| func SM2Asn1ToRaw(data []byte) []byte { | |||||
| i := 3 | |||||
| ret := []byte{} | |||||
| if data[i] == 32 { | |||||
| i = i + 1 | |||||
| } else if data[i] == 33 { | |||||
| i = i + 2 | |||||
| } | |||||
| for k := 0; k < 32; k++ { | |||||
| ret = append(ret, data[i+k]) | |||||
| } | |||||
| i = i + 32 | |||||
| i = i + 1 | |||||
| if data[i] == 32 { | |||||
| i = i + 1 | |||||
| } else if data[i] == 33 { | |||||
| i = i + 2 | |||||
| } | |||||
| for k := 0; k < 32; k++ { | |||||
| ret = append(ret, data[i+k]) | |||||
| } | |||||
| return ret | |||||
| } | |||||
| // 字符串排序拼接 | |||||
| func HttpBuildjson(data map[string]interface{}, filter_sign bool) string { | |||||
| keySlice := []string{} | |||||
| res := map[string]interface{}{} | |||||
| // 剔除sign字段 | |||||
| for k, _ := range data { | |||||
| if filter_sign && k == "sign" { | |||||
| continue | |||||
| } | |||||
| keySlice = append(keySlice, k) | |||||
| } | |||||
| // 排序字符串 | |||||
| sort.Strings(keySlice) | |||||
| for _, value := range keySlice { | |||||
| if value != "" && data[value] != "" { | |||||
| res[value] = data[value] | |||||
| } | |||||
| } | |||||
| ss, _ := json.Marshal(res) | |||||
| return string(ss) | |||||
| } | |||||
| // body参数处理 | |||||
| func FormatBodyDataNoList(body map[string]interface{}) map[string]interface{} { | |||||
| data := map[string]interface{}{} | |||||
| if len(body) > 0 { | |||||
| for k, v := range body { | |||||
| typeOfA := reflect.TypeOf(v) | |||||
| if typeOfA.Kind().String() == "map" || typeOfA.Kind().String() == "slice" { | |||||
| continue | |||||
| } else { | |||||
| if typeOfA.Kind().String() == "string" { | |||||
| if ToStr(v) != "" { | |||||
| data[k] = v | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return data | |||||
| } | |||||
| // 聚合支付异步回调验签 | |||||
| func JhNotifyCheckSign(sign, public_key string, data map[string]string) bool { | |||||
| build_sort_str := CmbHttpBuildQuery(MapStringToInterface(data), true) | |||||
| pub_x := "" | |||||
| pub_y := "" | |||||
| // 私钥截取 | |||||
| if len(public_key) == 128 { | |||||
| pub_x = public_key[:64] | |||||
| pub_y = public_key[64:] | |||||
| } else if len(public_key) == 130 && (public_key[:2] == "04") { | |||||
| public_key = public_key[2:] | |||||
| pub_x = public_key[:64] | |||||
| pub_y = public_key[64:] | |||||
| } else { | |||||
| log.Println("异步验签公钥出错", public_key) | |||||
| return false | |||||
| } | |||||
| sign_b64, _ := base64.StdEncoding.DecodeString(sign) | |||||
| pub := new(sm2.PublicKey) | |||||
| pub.Curve = sm2.GetSm2P256V1() | |||||
| xBytes, _ := hex.DecodeString(pub_x) | |||||
| yBytes, _ := hex.DecodeString(pub_y) | |||||
| pub.X = new(big.Int).SetBytes(xBytes) | |||||
| pub.Y = new(big.Int).SetBytes(yBytes) | |||||
| result := sm2.Verify(pub, []byte("1234567812345678"), []byte(build_sort_str), sign_b64) | |||||
| if !result { | |||||
| return false | |||||
| } | |||||
| return true | |||||
| } | |||||
| @ -1,3 +1,10 @@ | |||||
| module git.tetele.net/tgo/helper | module git.tetele.net/tgo/helper | ||||
| go 1.14 | go 1.14 | ||||
| require ( | |||||
| git.tetele.net/tgo/crypter v0.2.2 | |||||
| github.com/ZZMarquis/gm v1.3.2 | |||||
| github.com/json-iterator/go v1.1.12 | |||||
| golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect | |||||
| ) | |||||
| @ -0,0 +1,28 @@ | |||||
| git.tetele.net/tgo/crypter v0.2.2 h1:YMQJh2Gj5Po4ZfelJUmXBKi01UbmtiSy3bmqRfnYQMo= | |||||
| git.tetele.net/tgo/crypter v0.2.2/go.mod h1:vfvRLZA8+lHNgNXneOcgvVhDyuv25ZRb+C6xHOmXNx0= | |||||
| github.com/ZZMarquis/gm v1.3.2 h1:lFtpzg5zeeVMZ/gKi0gtYcKLBEo9XTqsZDHDz6s3Gow= | |||||
| github.com/ZZMarquis/gm v1.3.2/go.mod h1:wWbjZYgruQVd7Bb8UkSN8ujU931kx2XUW6nZLCiDE0Q= | |||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||||
| 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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | |||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | |||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | |||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= | |||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | |||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | |||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | |||||
| 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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |||||
| github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | |||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |||||
| golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= | |||||
| golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | |||||
| golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | |||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | |||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||||
| @ -0,0 +1,39 @@ | |||||
| package helper | |||||
| import ( | |||||
| "testing" | |||||
| ) | |||||
| func Test_HttpBuildQuery(t *testing.T) { | |||||
| a := map[string]interface{}{"id": "11", "name": "8", "price": "2.3"} | |||||
| b := map[string]interface{}{"id": "4", "name": "7", "price": "2.8"} | |||||
| c := map[string]interface{}{"id": "3", "name": "9", "price": "2.1"} | |||||
| list := []map[string]interface{}{} | |||||
| list = append(list, a) | |||||
| list = append(list, b) | |||||
| list = append(list, c) | |||||
| //ret := HttpBuildQuery(list) | |||||
| ret, err := MapSort("desc", "id", "float64", list) | |||||
| if err != nil { | |||||
| } | |||||
| t.Log(ret) | |||||
| } | |||||
| func Test_SetMapFirstToUpper(t *testing.T) { | |||||
| a := map[string]interface{}{"id": "11", "name": "8", "a_c": 99, "price": "2.3", "cc_dd_ee": 22} | |||||
| ret := SetMapFirstToUpper(a) | |||||
| t.Log(ret) | |||||
| } | |||||
| func Test_MergeMaps(t *testing.T) { | |||||
| a := map[string]string{"a": "1", "b": "2", "c": "3"} | |||||
| b := map[string]string{"e": "5", "f": "6", "g": "7"} | |||||
| // c := map[string]string{"a": "0", "h": "8", "f": "10"} | |||||
| ret := MergeMaps(a, b) | |||||
| t.Log(ret) | |||||
| } | |||||
| @ -0,0 +1,44 @@ | |||||
| package helper | |||||
| import ( | |||||
| "crypto/rand" | |||||
| "math/big" | |||||
| ) | |||||
| /** | |||||
| * 获取int类型的区间随机数 | |||||
| */ | |||||
| func GetRandInt(min, max int) (r int, err error) { | |||||
| _r, err := GetRandInt64(int64(min), int64(max)) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| r = int(_r) | |||||
| return | |||||
| } | |||||
| /** | |||||
| * 获取int64类型的区间随机数 | |||||
| */ | |||||
| func GetRandInt64(min, max int64) (r int64, err error) { | |||||
| if min == max { | |||||
| r = min | |||||
| return | |||||
| } | |||||
| if min > max { | |||||
| max, min = min, max | |||||
| } | |||||
| result, err := rand.Int(rand.Reader, big.NewInt(max-min)) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| r = result.Int64() + min | |||||
| b, err := rand.Int(rand.Reader, big.NewInt(2)) | |||||
| if err == nil && b.Int64() == 1 { | |||||
| r += 1 | |||||
| } | |||||
| return | |||||
| } | |||||
| @ -0,0 +1,115 @@ | |||||
| /* | |||||
| * string functions | |||||
| */ | |||||
| package helper | |||||
| import ( | |||||
| "errors" | |||||
| "fmt" | |||||
| "sort" | |||||
| ) | |||||
| // 求差集 | |||||
| func Diff(slice1, slice2 []string) []string { | |||||
| m := make(map[string]int) | |||||
| nn := make([]string, 0) | |||||
| inter := Intersect(slice1, slice2) | |||||
| for _, v := range inter { | |||||
| m[v]++ | |||||
| } | |||||
| for _, value := range slice1 { | |||||
| times, _ := m[value] | |||||
| if times == 0 { | |||||
| nn = append(nn, value) | |||||
| } | |||||
| } | |||||
| return nn | |||||
| } | |||||
| // 求交集 | |||||
| func Intersect(slice1, slice2 []string) []string { | |||||
| m := make(map[string]int) | |||||
| nn := make([]string, 0) | |||||
| for _, v := range slice1 { | |||||
| m[v]++ | |||||
| } | |||||
| for _, v := range slice2 { | |||||
| times, _ := m[v] | |||||
| if times == 1 { | |||||
| nn = append(nn, v) | |||||
| } | |||||
| } | |||||
| return nn | |||||
| } | |||||
| // 求并集 | |||||
| func Union(slice1, slice2 []string) []string { | |||||
| m := make(map[string]int) | |||||
| for _, v := range slice1 { | |||||
| m[v]++ | |||||
| } | |||||
| for _, v := range slice2 { | |||||
| times, _ := m[v] | |||||
| if times == 0 { | |||||
| slice1 = append(slice1, v) | |||||
| } | |||||
| } | |||||
| return slice1 | |||||
| } | |||||
| func Unique(originals interface{}) (interface{}, error) { | |||||
| temp := map[string]struct{}{} | |||||
| switch slice := originals.(type) { | |||||
| case []string: | |||||
| result := make([]string, 0, len(originals.([]string))) | |||||
| for _, item := range slice { | |||||
| key := fmt.Sprint(item) | |||||
| if _, ok := temp[key]; !ok { | |||||
| temp[key] = struct{}{} | |||||
| result = append(result, item) | |||||
| } | |||||
| } | |||||
| return result, nil | |||||
| case []int64: | |||||
| result := make([]int64, 0, len(originals.([]int64))) | |||||
| for _, item := range slice { | |||||
| key := fmt.Sprint(item) | |||||
| if _, ok := temp[key]; !ok { | |||||
| temp[key] = struct{}{} | |||||
| result = append(result, item) | |||||
| } | |||||
| } | |||||
| return result, nil | |||||
| default: | |||||
| return nil, errors.New(fmt.Sprintf("Unknown type: %T", slice)) | |||||
| } | |||||
| } | |||||
| // 将切片字符串(数字型)排序 | |||||
| func StringSliceSort(str []string, sort_type string) ([]string, error) { | |||||
| int_arr := []int{} | |||||
| string_arr := []string{} | |||||
| if len(str) < 1 { | |||||
| return []string{}, nil | |||||
| } | |||||
| for _, val := range str { | |||||
| int_arr = append(int_arr, ToInt(val)) | |||||
| } | |||||
| if sort_type == "desc" { | |||||
| sort.Sort(sort.Reverse(sort.IntSlice(int_arr))) | |||||
| }else{ | |||||
| sort.Ints(int_arr) | |||||
| } | |||||
| for _, val_int := range int_arr { | |||||
| string_arr = append(string_arr, ToStr(val_int)) | |||||
| } | |||||
| return string_arr, nil | |||||
| } | |||||
| @ -0,0 +1,41 @@ | |||||
| package helper | |||||
| import ( | |||||
| "fmt" | |||||
| "testing" | |||||
| ) | |||||
| func Test_Diff(t *testing.T) { | |||||
| var aa = []string{"43", "33"} | |||||
| var bb = []string{"2", "33", "1"} | |||||
| cc := Diff(aa, bb) | |||||
| fmt.Println(cc) | |||||
| } | |||||
| func Test_Intersect(t *testing.T) { | |||||
| var aa = []string{"43", "33"} | |||||
| var bb = []string{"2", "33", "1"} | |||||
| cc := Intersect(aa, bb) | |||||
| fmt.Println(cc) | |||||
| } | |||||
| func Test_Union(t *testing.T) { | |||||
| var aa = []string{"43", "33"} | |||||
| var bb = []string{"2", "33", "1"} | |||||
| cc := Union(aa, bb) | |||||
| fmt.Println(cc) | |||||
| } | |||||
| func Test_Unique(t *testing.T) { | |||||
| var str = []string{"1", "2", "2"} | |||||
| ret, err := Unique(str) | |||||
| fmt.Println(ret, err) | |||||
| } | |||||
| func Test_StringSliceSort(t *testing.T) { | |||||
| var str = []string{"10", "2", "5"} | |||||
| ret, err := StringSliceSort(str,"asc") | |||||
| fmt.Println(ret, err) | |||||
| ret2, err := StringSliceSort(str,"desc") | |||||
| fmt.Println(ret2, err) | |||||
| } | |||||
| @ -0,0 +1,62 @@ | |||||
| package helper | |||||
| import ( | |||||
| "sync" | |||||
| "time" | |||||
| ) | |||||
| // Snowflake 结构体 | |||||
| type Snowflake struct { | |||||
| mu sync.Mutex | |||||
| startTime int64 // 起始时间戳,可以根据实际需求设置 | |||||
| machineID int64 // 机器ID | |||||
| sequenceNum int64 // 序列号 | |||||
| } | |||||
| // NewSnowflake 创建一个Snowflake实例 | |||||
| func NewSnowflake(machineID int64) *Snowflake { | |||||
| return &Snowflake{ | |||||
| startTime: getTimeStamp(), | |||||
| machineID: machineID, | |||||
| sequenceNum: 0, | |||||
| } | |||||
| } | |||||
| // Generate 生成一个唯一ID | |||||
| func (s *Snowflake) Generate() int64 { | |||||
| s.mu.Lock() | |||||
| defer s.mu.Unlock() | |||||
| currentTime := getTimeStamp() | |||||
| // 如果当前时间小于上一次生成ID的时间,说明时钟回拨,需要等待 | |||||
| if currentTime < s.startTime { | |||||
| time.Sleep(time.Duration(s.startTime - currentTime)) | |||||
| currentTime = getTimeStamp() | |||||
| } | |||||
| // 如果是同一毫秒内生成的ID,需要增加序列号 | |||||
| if currentTime == s.startTime { | |||||
| s.sequenceNum++ | |||||
| } else { | |||||
| s.sequenceNum = 0 | |||||
| s.startTime = currentTime | |||||
| } | |||||
| // 如果序列号超过了12位的最大值,等待下一毫秒再生成ID | |||||
| if s.sequenceNum >= 1<<12 { | |||||
| time.Sleep(time.Millisecond) | |||||
| s.startTime = getTimeStamp() | |||||
| s.sequenceNum = 0 | |||||
| } | |||||
| // 生成ID | |||||
| id := (currentTime-s.startTime)<<22 | (s.machineID << 12) | s.sequenceNum | |||||
| return id | |||||
| } | |||||
| // 获取当前时间戳(毫秒级) | |||||
| func getTimeStamp() int64 { | |||||
| return time.Now().UnixNano() / int64(time.Millisecond) | |||||
| } | |||||
| @ -0,0 +1,16 @@ | |||||
| package helper | |||||
| import ( | |||||
| "testing" | |||||
| ) | |||||
| func Test_snowflake(t *testing.T) { | |||||
| // 创建一个Snowflake实例 | |||||
| snowflake := NewSnowflake(169379266710) | |||||
| // 生成10个唯一ID并输出 | |||||
| for i := 0; i < 10; i++ { | |||||
| id := snowflake.Generate() | |||||
| t.Log(id) | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,149 @@ | |||||
| package helper | |||||
| import ( | |||||
| "errors" | |||||
| "fmt" | |||||
| "reflect" | |||||
| ) | |||||
| // 结构体泛型提取关键数据 | |||||
| // desk结构体, input提取数据源, columnKey提取的字段,indexKey提取的下标 | |||||
| func StructColumn(desk, input interface{}, columnKey, indexKey string) (err error) { | |||||
| deskValue := reflect.ValueOf(desk) | |||||
| if deskValue.Kind() != reflect.Ptr { | |||||
| return errors.New("desk must be ptr") | |||||
| } | |||||
| rv := reflect.ValueOf(input) | |||||
| if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array { | |||||
| return errors.New("input must be map slice or array") | |||||
| } | |||||
| rt := reflect.TypeOf(input) | |||||
| if rt.Elem().Kind() != reflect.Struct { | |||||
| return errors.New("input's elem must be struct") | |||||
| } | |||||
| if len(indexKey) > 0 { | |||||
| return structIndexColumn(desk, input, columnKey, indexKey) | |||||
| } | |||||
| return structColumn(desk, input, columnKey) | |||||
| } | |||||
| func structColumn(desk, input interface{}, columnKey string) (err error) { | |||||
| if len(columnKey) == 0 { | |||||
| return errors.New("columnKey cannot not be empty") | |||||
| } | |||||
| deskElemType := reflect.TypeOf(desk).Elem() | |||||
| if deskElemType.Kind() != reflect.Slice { | |||||
| return errors.New("desk must be slice") | |||||
| } | |||||
| rv := reflect.ValueOf(input) | |||||
| rt := reflect.TypeOf(input) | |||||
| var columnVal reflect.Value | |||||
| deskValue := reflect.ValueOf(desk) | |||||
| direct := reflect.Indirect(deskValue) | |||||
| for i := 0; i < rv.Len(); i++ { | |||||
| columnVal, err = findStructValByColumnKey(rv.Index(i), rt.Elem(), columnKey) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| if deskElemType.Elem().Kind() != columnVal.Kind() { | |||||
| return errors.New(fmt.Sprintf("your slice must be []%s", columnVal.Kind())) | |||||
| } | |||||
| direct.Set(reflect.Append(direct, columnVal)) | |||||
| } | |||||
| return | |||||
| } | |||||
| func findStructValByColumnKey(curVal reflect.Value, elemType reflect.Type, columnKey string) (columnVal reflect.Value, err error) { | |||||
| columnExist := false | |||||
| for i := 0; i < elemType.NumField(); i++ { | |||||
| curField := curVal.Field(i) | |||||
| if elemType.Field(i).Name == columnKey { | |||||
| columnExist = true | |||||
| columnVal = curField | |||||
| continue | |||||
| } | |||||
| } | |||||
| if !columnExist { | |||||
| return columnVal, errors.New(fmt.Sprintf("columnKey %s not found in %s's field", columnKey, elemType)) | |||||
| } | |||||
| return | |||||
| } | |||||
| func structIndexColumn(desk, input interface{}, columnKey, indexKey string) (err error) { | |||||
| deskValue := reflect.ValueOf(desk) | |||||
| if deskValue.Elem().Kind() != reflect.Map { | |||||
| return errors.New("desk must be map") | |||||
| } | |||||
| deskElem := deskValue.Type().Elem() | |||||
| if len(columnKey) == 0 && deskElem.Elem().Kind() != reflect.Struct { | |||||
| return errors.New(fmt.Sprintf("desk's elem expect struct, got %s", deskElem.Elem().Kind())) | |||||
| } | |||||
| rv := reflect.ValueOf(input) | |||||
| rt := reflect.TypeOf(input) | |||||
| elemType := rt.Elem() | |||||
| var indexVal, columnVal reflect.Value | |||||
| direct := reflect.Indirect(deskValue) | |||||
| mapReflect := reflect.MakeMap(deskElem) | |||||
| deskKey := deskValue.Type().Elem().Key() | |||||
| for i := 0; i < rv.Len(); i++ { | |||||
| curVal := rv.Index(i) | |||||
| indexVal, columnVal, err = findStructValByIndexKey(curVal, elemType, indexKey, columnKey) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| if deskKey.Kind() != indexVal.Kind() { | |||||
| return errors.New(fmt.Sprintf("cant't convert %s to %s, your map'key must be %s", indexVal.Kind(), deskKey.Kind(), indexVal.Kind())) | |||||
| } | |||||
| if len(columnKey) == 0 { | |||||
| mapReflect.SetMapIndex(indexVal, curVal) | |||||
| direct.Set(mapReflect) | |||||
| } else { | |||||
| if deskElem.Elem().Kind() != columnVal.Kind() { | |||||
| return errors.New(fmt.Sprintf("your map must be map[%s]%s", indexVal.Kind(), columnVal.Kind())) | |||||
| } | |||||
| mapReflect.SetMapIndex(indexVal, columnVal) | |||||
| direct.Set(mapReflect) | |||||
| } | |||||
| } | |||||
| return | |||||
| } | |||||
| func findStructValByIndexKey(curVal reflect.Value, elemType reflect.Type, indexKey, columnKey string) (indexVal, columnVal reflect.Value, err error) { | |||||
| indexExist := false | |||||
| columnExist := false | |||||
| for i := 0; i < elemType.NumField(); i++ { | |||||
| curField := curVal.Field(i) | |||||
| if elemType.Field(i).Name == indexKey { | |||||
| switch curField.Kind() { | |||||
| case reflect.String, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int, reflect.Float64, reflect.Float32: | |||||
| indexExist = true | |||||
| indexVal = curField | |||||
| default: | |||||
| return indexVal, columnVal, errors.New("indexKey must be int float or string") | |||||
| } | |||||
| } | |||||
| if elemType.Field(i).Name == columnKey { | |||||
| columnExist = true | |||||
| columnVal = curField | |||||
| continue | |||||
| } | |||||
| } | |||||
| if !indexExist { | |||||
| return indexVal, columnVal, errors.New(fmt.Sprintf("indexKey %s not found in %s's field", indexKey, elemType)) | |||||
| } | |||||
| if len(columnKey) > 0 && !columnExist { | |||||
| return indexVal, columnVal, errors.New(fmt.Sprintf("columnKey %s not found in %s's field", columnKey, elemType)) | |||||
| } | |||||
| return | |||||
| } | |||||
| @ -0,0 +1,47 @@ | |||||
| package helper | |||||
| import ( | |||||
| "testing" | |||||
| ) | |||||
| type User struct { | |||||
| ID int | |||||
| NAME string | |||||
| } | |||||
| type UserString struct { | |||||
| ID string | |||||
| NAME string | |||||
| } | |||||
| func Test_structColumn(t *testing.T) { | |||||
| user1 := UserString{ | |||||
| ID: "2", | |||||
| NAME: "zwk", | |||||
| } | |||||
| user2 := UserString{ | |||||
| ID: "3", | |||||
| NAME: "zzz", | |||||
| } | |||||
| var list3 []UserString | |||||
| list3 = append(list3, user1) | |||||
| list3 = append(list3, user2) | |||||
| t.Log(list3) | |||||
| t.Log("---------------------") | |||||
| var userMap map[string]string | |||||
| StructColumn(&userMap, list3, "NAME", "ID") | |||||
| t.Logf("%#v\n", userMap) | |||||
| t.Log("---------------------") | |||||
| var userMap1 map[int]UserString | |||||
| StructColumn(&userMap1, list3, "", "ID") | |||||
| t.Logf("%#v\n", userMap1) | |||||
| t.Log("---------------------") | |||||
| var userSlice []string | |||||
| StructColumn(&userSlice, list3, "ID", "") | |||||
| t.Logf("%#v\n", userSlice) | |||||
| t.Log("---------------------") | |||||
| } | |||||
| @ -0,0 +1,12 @@ | |||||
| package helper | |||||
| import ( | |||||
| "testing" | |||||
| ) | |||||
| func Test_GenerateUUID(t *testing.T) { | |||||
| uuid, err := GenerateUUID() | |||||
| t.Log(uuid) | |||||
| t.Log(err) | |||||
| } | |||||