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 }