package t3_union import ( "context" "encoding/json" "errors" "fmt" "math/rand" "net/http" "sort" "strings" "time" "github.com/zeromicro/go-zero/core/logx" "gitee.com/chengdu-lenntc/third-platform-sdk/client" "gitee.com/chengdu-lenntc/third-platform-sdk/util" ) // T3UnionApi 调用第三方平台的api // Api defines the interface of t3_union api type T3UnionApi interface { // Sign 签名 Sign(methodType string, url string, data map[string]interface{}) string // GenerateLink 生成短链 GenerateLink(ctx context.Context, req GenerateLinkRequest) (*GenerateLinkData, error) // GenerateCode 生成二维码 GenerateCode(ctx context.Context, req GenerateCodeRequest) (*GenerateCodeData, error) // GeneratePoster 生成推广海报 GeneratePoster(ctx context.Context, req GeneratePosterRequest) (*GeneratePosterData, error) // QueryOrderList 查询订单列表 QueryOrderList(ctx context.Context, req QueryOrderListRequest) (*QueryOrderData, error) } type t3UnionApiImpl struct { log logx.Logger client *Client } func newT3UnionApiImpl(log logx.Logger, client *Client) T3UnionApi { return &t3UnionApiImpl{ log: log, client: client, } } func (a *t3UnionApiImpl) buildHeader(methodType string, uri string, data map[string]any) map[string]string { // 随机数 tn := time.Now() src := rand.NewSource(tn.Unix()) randNum := rand.New(src).Int31n(10000) nonce := util.Md5String(fmt.Sprintf("%d_nonce_%d", tn.UnixMicro(), randNum)) headers := map[string]string{ XT3Nonce: nonce, // 请求标识,10分钟内保证唯一性 XT3Timestamp: fmt.Sprintf("%d", time.Now().UnixMilli()), // 请求发起时间戳(毫秒),有效时1分钟 XT3Version: "V1", // 版本(固定值传"V1") XT3Key: a.client.authConfig.AppKey, // 使用前联系分配账号(APP Key) XT3SignatureMethod: "MD5", // 固定值传 "MD5" } headers[XT3Signature] = a.getSign(methodType, uri, headers, data) // 在获得签名后赋值 headers[XT3SignatureHeaders] = "x-t3-nonce,x-t3-timestamp,x-t3-version,x-t3-key,x-t3-signature-method" // 固定传值 return headers } func (a *t3UnionApiImpl) getSign(methodType string, uri string, headers map[string]string, data map[string]any) string { // key排序 arr := sort.StringSlice{} for k := range headers { if k != XT3Signature && k != XT3SignatureHeaders { arr = append(arr, k) } } arr.Sort() // 参数拼接 var build strings.Builder build.WriteString(fmt.Sprintf("%s%s", methodType, uri)) for _, k := range arr { build.WriteString(fmt.Sprintf("%s:%v", k, headers[k])) } // 请求参数body转json string后用MD5进行加密 dataByte, _ := json.Marshal(data) build.WriteString(util.Md5String(string(dataByte))) build.WriteString(a.client.authConfig.AppSecret) return util.Md5String(build.String()) } // Sign 签名 func (a *t3UnionApiImpl) Sign(methodType string, uri string, dataMap map[string]any) string { headers := a.buildHeader(methodType, uri, dataMap) if sign, ok := headers[XT3Signature]; ok { return sign } return "" } // GenerateLink 生成短链 func (a *t3UnionApiImpl) GenerateLink(ctx context.Context, req GenerateLinkRequest) (*GenerateLinkData, error) { args := util.StructToMap(req) headers := a.buildHeader(http.MethodPost, GetLinkUri, args) for k, v := range a.client.headers { headers[k] = v } request := &client.HttpRequest{Headers: headers, BodyArgs: args} response := new(GenerateLinkResponse) if err := a.client.HttpPost(GetLinkUrl, request, &client.HttpResponse{Result: response}); err != nil { return nil, err } if !response.Success || response.ErrCode != 0 { a.log.WithFields(logx.LogField{Key: "data", Value: map[string]any{"req": req, "resp": response}}). Errorf("[t3UnionApiImpl][GenerateLink] response result error: %s", response.Msg) return nil, errors.New(response.Msg) } return response.Data, nil } // GenerateCode 生成二维码 func (a *t3UnionApiImpl) GenerateCode(ctx context.Context, req GenerateCodeRequest) (*GenerateCodeData, error) { args := util.StructToMap(req) headers := a.buildHeader(http.MethodPost, GetMiniCodeUri, args) for k, v := range a.client.headers { headers[k] = v } request := &client.HttpRequest{Headers: headers, BodyArgs: args} response := new(GenerateCodeResponse) if err := a.client.HttpPost(GetMiniCodeUrl, request, &client.HttpResponse{Result: response}); err != nil { return nil, err } if !response.Success || response.ErrCode != 0 { a.log.WithFields(logx.LogField{Key: "data", Value: map[string]any{"req": req, "resp": response}}). Errorf("[t3UnionApiImpl][GenerateLink] response result error: %s", response.Msg) return nil, errors.New(response.Msg) } return response.Data, nil } // GeneratePoster 生成推广海报 func (a *t3UnionApiImpl) GeneratePoster(ctx context.Context, req GeneratePosterRequest) (*GeneratePosterData, error) { args := util.StructToMap(req) headers := a.buildHeader(http.MethodPost, GetPosterUri, args) for k, v := range a.client.headers { headers[k] = v } request := &client.HttpRequest{Headers: headers, BodyArgs: args} response := new(GeneratePosterResponse) if err := a.client.HttpPost(GetPosterUrl, request, &client.HttpResponse{Result: response}); err != nil { return nil, err } if !response.Success || response.ErrCode != 0 { a.log.WithFields(logx.LogField{Key: "data", Value: map[string]any{"req": req, "resp": response}}). Errorf("[t3UnionApiImpl][GenerateLink] response result error: %s", response.Msg) return nil, errors.New(response.Msg) } return response.Data, nil } // QueryOrderList 查询订单列表 func (a *t3UnionApiImpl) QueryOrderList(ctx context.Context, req QueryOrderListRequest) (*QueryOrderData, error) { args := util.StructToMap(req) headers := a.buildHeader(http.MethodPost, GetOrderListUri, args) for k, v := range a.client.headers { headers[k] = v } request := &client.HttpRequest{Headers: headers, BodyArgs: args} response := new(QueryOrderListResponse) if err := a.client.HttpPost(GetOrderListUrl, request, &client.HttpResponse{Result: response}); err != nil { return nil, err } if !response.Success || response.ErrCode != 0 { a.log.WithFields(logx.LogField{Key: "data", Value: map[string]any{"req": req, "resp": response}}). Errorf("[t3UnionApiImpl][GenerateLink] response result error: %s", response.Msg) return nil, errors.New(response.Msg) } return response.Data, nil }