新增快手平台和对应的接口

This commit is contained in:
2026-06-01 14:08:17 +08:00
parent 15db71b7ba
commit 812693caae
14 changed files with 1529 additions and 185 deletions

View File

@@ -3,13 +3,16 @@ package sync
import (
"bytes"
"context"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"net/url"
"sort"
"strings"
"time"
@@ -24,8 +27,9 @@ type ApiResult struct {
// ApiClient 通用 API 客户端
type ApiClient struct {
config *PlatformConfig
client *http.Client
config *PlatformConfig
client *http.Client
rateLimiter <-chan time.Time // 限流 ticker
}
// NewApiClient 创建客户端
@@ -34,10 +38,17 @@ func NewApiClient(config *PlatformConfig) *ApiClient {
if config.RequestTimeoutMs > 0 {
timeout = time.Duration(config.RequestTimeoutMs) * time.Millisecond
}
return &ApiClient{
ac := &ApiClient{
config: config,
client: &http.Client{Timeout: timeout},
}
// 初始化限流
if config.RateLimitPerMinute > 0 {
interval := time.Minute / time.Duration(config.RateLimitPerMinute)
ac.rateLimiter = time.Tick(interval)
logrus.Infof("限流已启用: %d 次/分钟, 间隔 %v", config.RateLimitPerMinute, interval)
}
return ac
}
// Get 发送 GET 请求(无参数)
@@ -85,6 +96,15 @@ func (c *ApiClient) doRequest(ctx context.Context, method, path string, body int
}
func (c *ApiClient) execute(ctx context.Context, method, path string, body interface{}, paramsInQuery bool) (*ApiResult, error) {
// 限流等待
if c.rateLimiter != nil {
select {
case <-c.rateLimiter:
case <-ctx.Done():
return nil, ctx.Err()
}
}
start := time.Now()
fullURL := c.config.GetApiUrl(path)
@@ -104,6 +124,8 @@ func (c *ApiClient) execute(ctx context.Context, method, path string, body inter
}
}
// 计算签名并追加(如快手 API 的 MD5 签名)
fullURL = c.applySignature(fullURL, body, paramsInQuery)
logrus.Infof("请求 URL: %s", fullURL)
req, err := http.NewRequestWithContext(ctx, method, fullURL, reqBody)
@@ -192,6 +214,7 @@ func (c *ApiClient) applyAuthURL(rawURL string) string {
for k, v := range eq {
val := fmt.Sprintf("%v", v)
val = strings.ReplaceAll(val, "{timestamp}", fmt.Sprintf("%d", time.Now().Unix()))
val = strings.ReplaceAll(val, "{timestamp_ms}", fmt.Sprintf("%d", time.Now().UnixMilli()))
val = strings.ReplaceAll(val, "{nonce}", generateNonce())
extraParams[k] = val
}
@@ -250,3 +273,58 @@ func generateNonce() string {
r, _ := rand.Int(rand.Reader, big.NewInt(10000))
return fmt.Sprintf("%012d%04d", nanoPart, r.Int64())
}
// applySignature 计算签名并追加到 URL支持快手等平台的 MD5 签名)
func (c *ApiClient) applySignature(rawURL string, body interface{}, paramsInQuery bool) string {
cfg := c.config.AuthConfig
if cfg == nil {
return rawURL
}
signAlgo, _ := cfg["sign_algorithm"].(string)
if signAlgo == "" {
return rawURL
}
appSecret, _ := cfg["app_secret"].(string)
if appSecret == "" && c.config.AppSecret != "" {
appSecret = c.config.AppSecret
}
if appSecret == "" {
return rawURL
}
parsed, _ := url.Parse(rawURL)
q := parsed.Query()
// 收集所有参数并按 key 排序
keys := make([]string, 0, len(q))
for k := range q {
if k == "sign" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var signStr string
for _, k := range keys {
signStr += k + "=" + q.Get(k) + "&"
}
signStr += "key=" + appSecret
var sign string
switch signAlgo {
case "md5":
h := md5.Sum([]byte(signStr))
sign = hex.EncodeToString(h[:])
case "md5_upper":
h := md5.Sum([]byte(signStr))
sign = strings.ToUpper(hex.EncodeToString(h[:]))
default:
return rawURL
}
q.Set("sign", sign)
parsed.RawQuery = q.Encode()
return parsed.String()
}