Merge remote-tracking branch 'origin/feature/交易所接口'

This commit is contained in:
yuguojian 2025-07-04 11:05:39 +08:00
commit ace2872c2d
2 changed files with 252 additions and 0 deletions

201
jtt_tool/jtt_client.go Normal file
View File

@ -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
}

View File

@ -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)
}
})
}
}