temp
This commit is contained in:
199
internal/shared/esign/http.go
Normal file
199
internal/shared/esign/http.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPClient e签宝HTTP客户端
|
||||
// 处理所有e签宝API的HTTP请求,包括签名生成、请求头设置等
|
||||
type HTTPClient struct {
|
||||
config *Config
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewHTTPClient 创建HTTP客户端
|
||||
func NewHTTPClient(config *Config) *HTTPClient {
|
||||
return &HTTPClient{
|
||||
config: config,
|
||||
client: &http.Client{Timeout: 30 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (h *HTTPClient) UpdateConfig(config *Config) {
|
||||
h.config = config
|
||||
}
|
||||
|
||||
// Request e签宝通用请求函数
|
||||
// 处理所有e签宝API的HTTP请求,包括签名生成、请求头设置等
|
||||
//
|
||||
// 参数说明:
|
||||
// - method: HTTP方法(GET、POST等)
|
||||
// - urlPath: API路径
|
||||
// - body: 请求体字节数组
|
||||
//
|
||||
// 返回: 响应体字节数组和错误信息
|
||||
func (h *HTTPClient) Request(method, urlPath string, body []byte) ([]byte, error) {
|
||||
// 生成签名所需参数
|
||||
timestamp := getCurrentTimestamp()
|
||||
nonce := generateNonce()
|
||||
date := getCurrentDate()
|
||||
|
||||
// 计算Content-MD5
|
||||
contentMD5 := ""
|
||||
if len(body) > 0 {
|
||||
contentMD5 = getContentMD5(body)
|
||||
}
|
||||
|
||||
// 根据Java示例,Headers为空字符串
|
||||
headers := ""
|
||||
|
||||
// 生成签名
|
||||
signature := generateSignature(h.config.AppSecret, method, "*/*", contentMD5, "application/json", date, headers, urlPath)
|
||||
|
||||
// 创建HTTP请求
|
||||
url := h.config.ServerURL + urlPath
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Content-MD5", contentMD5)
|
||||
req.Header.Set("Date", date)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
req.Header.Set("X-Tsign-Open-App-Id", h.config.AppID)
|
||||
req.Header.Set("X-Tsign-Open-Auth-Mode", "Signature")
|
||||
req.Header.Set("X-Tsign-Open-Ca-Timestamp", timestamp)
|
||||
req.Header.Set("X-Tsign-Open-Nonce", nonce)
|
||||
req.Header.Set("X-Tsign-Open-Ca-Signature", signature)
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
// 打印响应内容用于调试
|
||||
fmt.Printf("API响应状态码: %d\n", resp.StatusCode)
|
||||
fmt.Printf("API响应内容: %s\n", string(responseBody))
|
||||
|
||||
// 检查响应状态码
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("API请求失败,状态码: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return responseBody, nil
|
||||
}
|
||||
|
||||
// MarshalRequest 序列化请求数据为JSON
|
||||
//
|
||||
// 参数:
|
||||
// - data: 要序列化的数据
|
||||
//
|
||||
// 返回: JSON字节数组和错误信息
|
||||
func MarshalRequest(data interface{}) ([]byte, error) {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化请求数据失败: %v", err)
|
||||
}
|
||||
return jsonData, nil
|
||||
}
|
||||
|
||||
// UnmarshalResponse 反序列化响应数据
|
||||
//
|
||||
// 参数:
|
||||
// - responseBody: 响应体字节数组
|
||||
// - response: 目标响应结构体指针
|
||||
//
|
||||
// 返回: 错误信息
|
||||
func UnmarshalResponse(responseBody []byte, response interface{}) error {
|
||||
if err := json.Unmarshal(responseBody, response); err != nil {
|
||||
return fmt.Errorf("解析响应失败: %v,响应内容: %s", err, string(responseBody))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckResponseCode 检查API响应码
|
||||
//
|
||||
// 参数:
|
||||
// - code: 响应码
|
||||
// - message: 响应消息
|
||||
//
|
||||
// 返回: 错误信息
|
||||
func CheckResponseCode(code int, message string) error {
|
||||
if code != 0 {
|
||||
return fmt.Errorf("API调用失败: %s", message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortURLQueryParams 对URL查询参数按字典序(ASCII码)升序排序
|
||||
//
|
||||
// 参数:
|
||||
// - urlPath: 包含查询参数的URL路径
|
||||
//
|
||||
// 返回: 排序后的URL路径
|
||||
func sortURLQueryParams(urlPath string) string {
|
||||
// 检查是否包含查询参数
|
||||
if !strings.Contains(urlPath, "?") {
|
||||
return urlPath
|
||||
}
|
||||
|
||||
// 分离路径和查询参数
|
||||
parts := strings.SplitN(urlPath, "?", 2)
|
||||
if len(parts) != 2 {
|
||||
return urlPath
|
||||
}
|
||||
|
||||
basePath := parts[0]
|
||||
queryString := parts[1]
|
||||
|
||||
// 解析查询参数
|
||||
values, err := url.ParseQuery(queryString)
|
||||
if err != nil {
|
||||
// 如果解析失败,返回原始路径
|
||||
return urlPath
|
||||
}
|
||||
|
||||
// 获取所有参数键并排序
|
||||
var keys []string
|
||||
for key := range values {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// 重新构建查询字符串
|
||||
var sortedPairs []string
|
||||
for _, key := range keys {
|
||||
for _, value := range values[key] {
|
||||
sortedPairs = append(sortedPairs, key+"="+value)
|
||||
}
|
||||
}
|
||||
|
||||
// 组合排序后的查询参数
|
||||
sortedQueryString := strings.Join(sortedPairs, "&")
|
||||
|
||||
// 返回完整的URL路径
|
||||
if sortedQueryString != "" {
|
||||
return basePath + "?" + sortedQueryString
|
||||
}
|
||||
return basePath
|
||||
}
|
||||
Reference in New Issue
Block a user