250 lines
5.3 KiB
Go
250 lines
5.3 KiB
Go
package core
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"gitee.com/lansexiongdi/ymt/utils"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// Params request params
|
|
type Params struct {
|
|
// AppId app id
|
|
AppId string `json:"app_id"`
|
|
// SignType sign type
|
|
SignType SignType `json:"sign_type"`
|
|
// Timestamp 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
|
|
Timestamp string `json:"timestamp"`
|
|
// Sign
|
|
Sign string `json:"sign"`
|
|
// Ciphertext
|
|
Ciphertext string `json:"ciphertext"`
|
|
}
|
|
|
|
// Core structure
|
|
type Core struct {
|
|
// HttpClient http client
|
|
HttpClient *http.Client
|
|
// Config config
|
|
Config *Config
|
|
// CryptographySuite
|
|
CryptographySuite *CryptographySuite
|
|
}
|
|
|
|
type Option func(*Core)
|
|
|
|
// WithHttpClient sets the http client
|
|
func WithHttpClient(client *http.Client) Option {
|
|
return func(s *Core) {
|
|
s.HttpClient = client
|
|
}
|
|
}
|
|
|
|
// NewCore creates a new Core instance
|
|
func NewCore(c *Config, o ...Option) (*Core, error) {
|
|
|
|
if err := c.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
core := &Core{
|
|
HttpClient: http.DefaultClient,
|
|
Config: c,
|
|
}
|
|
for _, f := range o {
|
|
f(core)
|
|
}
|
|
|
|
crs, err := c.CryptographySuite()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
core.CryptographySuite = crs
|
|
|
|
return core, nil
|
|
}
|
|
|
|
// GetBizSignStr gets the biz sign str Go 版本是 1.15 或更高版本
|
|
func (c *Core) GetBizSignStr(request any) (plaintext string, err error) {
|
|
|
|
kvs := utils.SortStruct(request)
|
|
|
|
kvm := make(map[string]any, len(kvs))
|
|
var order []string
|
|
|
|
for _, kv := range kvs {
|
|
kvm[kv.Key] = kv.Value
|
|
order = append(order, kv.Key)
|
|
}
|
|
|
|
orderedMap := make(map[string]any)
|
|
for _, key := range order {
|
|
orderedMap[key] = kvm[key]
|
|
}
|
|
|
|
// 将 orderedMap 转换成 JSON 字符串,保持顺序
|
|
kvmBytes, err := json.Marshal(orderedMap)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(kvmBytes), nil
|
|
}
|
|
|
|
// GetCiphertext gets the ciphertext
|
|
func (c *Core) GetCiphertext(request any) (string, error) {
|
|
|
|
plaintext, err := c.GetBizSignStr(request)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
ciphertext, err := c.CryptographySuite.Cipher.Encode(plaintext)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return ciphertext, nil
|
|
}
|
|
|
|
// BuildParams gets the params
|
|
func (c *Core) BuildParams(request Request) (*Params, error) {
|
|
|
|
if err := request.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ciphertext, err := c.GetCiphertext(request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
timestamps := time.Now().Format(time.DateTime)
|
|
dataToSign := c.Config.AppID + timestamps + ciphertext
|
|
|
|
signature, err := c.CryptographySuite.Signer.Sign(dataToSign)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Params{
|
|
AppId: c.Config.AppID,
|
|
SignType: c.Config.SignType,
|
|
Timestamp: timestamps,
|
|
Sign: signature,
|
|
Ciphertext: ciphertext,
|
|
}, nil
|
|
}
|
|
|
|
// BuildAnyApiParams gets the params
|
|
func (c *Core) BuildAnyApiParams(bizContent any) (*Params, error) {
|
|
|
|
ciphertext, err := c.GetCiphertext(bizContent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
timestamps := time.Now().Format(time.DateTime)
|
|
dataToSign := c.Config.AppID + timestamps + ciphertext
|
|
|
|
signature, err := c.CryptographySuite.Signer.Sign(dataToSign)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Params{
|
|
AppId: c.Config.AppID,
|
|
SignType: c.Config.SignType,
|
|
Timestamp: timestamps,
|
|
Sign: signature,
|
|
Ciphertext: ciphertext,
|
|
}, nil
|
|
}
|
|
|
|
// Verify verifies the params
|
|
func (c *Core) Verify(timestamp, ciphertext, sign string) bool {
|
|
dataToSign := c.Config.AppID + timestamp + ciphertext
|
|
return c.CryptographySuite.Verifier.Verify(dataToSign, sign)
|
|
}
|
|
|
|
func (c *Core) GetHeaders(p *Params) http.Header {
|
|
|
|
h := http.Header{}
|
|
|
|
h.Set("Content-Type", "application/json")
|
|
h.Set("Version", "1.0")
|
|
h.Set("Appid", c.Config.AppID)
|
|
h.Set("Sign-Type", string(c.Config.SignType))
|
|
|
|
h.Set("Timestamp", p.Timestamp)
|
|
h.Set("Sign", p.Sign)
|
|
|
|
return h
|
|
}
|
|
|
|
// GetRequestBody gets the request body
|
|
func (c *Core) GetRequestBody(_ context.Context, request Request) (http.Header, []byte, error) {
|
|
|
|
p, err := c.BuildParams(request)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
h := c.GetHeaders(p)
|
|
|
|
reqBodyBytes, err := json.Marshal(p)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return h, reqBodyBytes, nil
|
|
}
|
|
|
|
// Post sends the request and Analysis the response
|
|
func (c *Core) Post(ctx context.Context, method string, request Request) (http.Header, *http.Response, []byte, error) {
|
|
|
|
h, reqBodyBytes, err := c.GetRequestBody(ctx, request)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
httpResponse, body, err := c.Request(ctx, h, http.MethodPost, c.Config.BaseURL+method, reqBodyBytes)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
return h, httpResponse, body, nil
|
|
}
|
|
|
|
// Request sends the request and Analysis the response
|
|
func (c *Core) Request(ctx context.Context, h http.Header, method, url string, body []byte) (*http.Response, []byte, error) {
|
|
|
|
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewBuffer(body))
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
|
}
|
|
req.Header = h
|
|
|
|
resp, err := c.HttpClient.Do(req)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("sending HTTP request failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
bodyBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, nil, fmt.Errorf("HTTP status code: %d", resp.StatusCode)
|
|
}
|
|
|
|
return resp, bodyBytes, nil
|
|
}
|