diff --git a/jtt_tool/jtt_client.go b/jtt_tool/jtt_client.go new file mode 100644 index 0000000..5bf8dae --- /dev/null +++ b/jtt_tool/jtt_client.go @@ -0,0 +1,201 @@ +package jtt_tool + +import ( + "bytes" + "crypto" + "crypto/md5" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "github.com/pkg/errors" + "io" + "log" + "net/http" + "reflect" + "sort" + "strings" + "time" +) + +type JttClient struct { + AppId string + ApiUrl string + PublicKey string + PrivateKey string +} + +func NewJttClient(appId, apiUrl, publicKey, privateKey string) *JttClient { + return &JttClient{ + AppId: appId, + ApiUrl: apiUrl, + PublicKey: publicKey, + PrivateKey: privateKey, + } +} + +func (j *JttClient) FindUserForTokenMessage(address string) (res *FindUserForTokenMessageRes, err error) { + if address == "" { + return nil, fmt.Errorf("address is empty") + } + fmt.Println("address", address) + + paramMap := make(map[string]any) + + paramMap["appId"] = j.AppId + paramMap["timestamp"] = time.Now().Unix() + paramMap["publicKey"] = j.PublicKey + paramMap["address"] = address + + postRes, err := j.JttPost("/findUserForTokenMessage", paramMap) + if err != nil { + return + } + + err = json.Unmarshal(postRes, &res) + if err != nil { + err = fmt.Errorf("转换FindUserForTokenMessageRes结构体失败: %s", string(postRes)) + return + } + + if res == nil || res.Code != 200 { + err = fmt.Errorf("查询交易所数据失败: %w, res: %+v , 地址:%s", err, res, address) + return + } + + return + +} + +type FindUserForTokenMessageRes struct { + Code int `json:"code"` + Data *Data `json:"data"` +} + +type Data struct { + UserTokenList []*UserToken `json:"userTokenList"` +} + +type UserToken struct { + Code int `json:"code"` + Mes string `json:"mes"` + TokenNum float64 `json:"tokenNum"` + WalletAddress string `json:"walletAddress"` +} + +func (j *JttClient) JttPost(url string, paramMap map[string]any) (res []byte, err error) { + + bodyByte, _ := json.Marshal(paramMap) + + req, err := http.NewRequest(http.MethodPost, j.ApiUrl+url, bytes.NewBuffer(bodyByte)) + if err != nil { + return + } + + req.Header.Set("Content-Type", "application/json") + sign, err := j.ToSign(paramMap) + if err != nil { + return + } + paramMap["sign"] = sign + + // 创建 HTTP 客户端 + client := &http.Client{} + + // 发送请求 + resp, err := client.Do(req) + //log.Printf("WPSPost resp: %+v\n", resp) + if err != nil { + log.Printf("发送请求失败: %+v\n", err) + return + } + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + // 读取响应体 + res, err = io.ReadAll(resp.Body) + if err != nil { + return + } + + fmt.Printf("res: %s\n", string(res)) + + if !json.Valid(res) { + return nil, errors.New("响应体不是有效的JSON格式") + } + + return +} + +func (s *JttClient) ToSign(reqData map[string]interface{}) (sign []byte, errs error) { + sortedStr, err := s.GetSortedStr(reqData) + if err != nil { + return + } + privateKeyStr := "-----BEGIN PRIVATE KEY-----\r\n" + s.PrivateKey + "\r\n-----END PRIVATE KEY-----" + sign, err = s.Sign(sortedStr, privateKeyStr) + return +} + +func (s *JttClient) Sign(signData string, privateKeyStr string) (sign []byte, errs error) { + hashedMessage := md5.Sum([]byte(signData)) + if privateKeyStr == "" { + return nil, errors.New("private key is empty") + } + + block, _ := pem.Decode([]byte(privateKeyStr)) + privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + sign, err = rsa.SignPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), crypto.MD5, hashedMessage[:]) + if err != nil { + return nil, err + } + sign = []byte(base64.StdEncoding.EncodeToString(sign)) + return +} + +func (s *JttClient) GetSortedStr(data any) (str string, err error) { + tmp := map[string]interface{}{} + if reflect.ValueOf(data).Kind() == reflect.Struct { + jsStr, _ := json.Marshal(data) + _ = json.Unmarshal(jsStr, &tmp) + } else { + ok := false + tmp, ok = data.(map[string]interface{}) + if !ok { + err = errors.New("data type error") + } + } + + keys := []string{} + for key := range tmp { + keys = append(keys, key) + } + + sort.Strings(keys) + sortedParams := []string{} + + for _, key := range keys { + value := tmp[key] + if key == "sign" { + continue + } + + switch v := value.(type) { + case int, uint, int16, int32, int64: + sortedParams = append(sortedParams, fmt.Sprintf("%s=%d", key, v)) + case float64, float32: + sortedParams = append(sortedParams, fmt.Sprintf("%s=%f", key, v)) + default: + sortedParams = append(sortedParams, key+"="+value.(string)) + } + } + str = strings.Join(sortedParams, "&") + return +} diff --git a/jtt_tool/jtt_client_test.go b/jtt_tool/jtt_client_test.go new file mode 100644 index 0000000..4279f5b --- /dev/null +++ b/jtt_tool/jtt_client_test.go @@ -0,0 +1,51 @@ +package jtt_tool + +import ( + "log" + "testing" +) + +func TestJttClient_FindUserForTokenMessage(t *testing.T) { + type fields struct { + AppId string + ApiUrl string + PublicKey string + PrivateKey string + } + type args struct { + address string + } + tests := []struct { + name string + fields fields + args args + wantRes *FindUserForTokenMessageRes + wantErr bool + }{ + { + name: "test1", + fields: fields{}, + args: args{ + address: "0x123456", + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + h := &JttClient{ + AppId: test.fields.AppId, + ApiUrl: test.fields.ApiUrl, + PublicKey: test.fields.PublicKey, + PrivateKey: test.fields.PrivateKey, + } + + gotRes, err := h.FindUserForTokenMessage(test.args.address) + + log.Println(gotRes, err) + + if (err != nil) != test.wantErr { + t.Errorf("FindUserForTokenMessage() error = %v, wantErr %v", err, test.wantErr) + } + }) + } +}