This commit is contained in:
2026-07-25 14:18:14 +08:00
parent cc2513652e
commit fa55098520
7 changed files with 216 additions and 58 deletions

View File

@@ -14,8 +14,6 @@ import (
"time"
"tyapi-server/internal/shared/external_logger"
"go.uber.org/zap"
)
const (
@@ -39,8 +37,10 @@ type ServiceConfig struct {
ClientID string
ClientSecret string
Timeout time.Duration
// Debug 开发环境开启时输出可复现 curl 与完整响应体,便于数据源排查
// Debug 开发环境开启时写入单一排查文件curl + response不重复打 request/response 日志
Debug bool
// DebugLogDir 排查日志目录,文件名为 dev_curl.log
DebugLogDir string
}
// YuyuechaService 愉悦查 OpenAPI 客户端
@@ -127,11 +127,13 @@ func (s *YuyuechaService) doRequest(ctx context.Context, method, path string, bo
baseURL := strings.TrimSuffix(s.config.BaseURL, "/")
reqURL := baseURL + path
if s.logger != nil {
// dev 排查走单一文件,避免 request/response/info 重复打点
if !s.config.Debug && s.logger != nil {
s.logger.LogRequest(requestID, transactionID, path, reqURL)
}
signHeaders, err := SignHeaders(s.config.ClientID, s.config.ClientSecret, method, path, bodyBytes)
// nonce 与本地 request_id 保持一致,后续仅凭 request_id 即可对接上游排查
signHeaders, err := SignHeaders(s.config.ClientID, s.config.ClientSecret, method, path, bodyBytes, requestID)
if err != nil {
err = errors.Join(ErrSystem, err)
s.logError(requestID, transactionID, path, err, map[string]interface{}{"request_params": logParams})
@@ -172,11 +174,8 @@ func (s *YuyuechaService) doRequest(ctx context.Context, method, path string, bo
resp, err := client.Do(req)
if err != nil {
err = wrapHTTPError(err)
errParams := map[string]interface{}{"request_params": logParams}
if curlCmd != "" {
errParams["curl"] = curlCmd
}
s.logError(requestID, transactionID, path, err, errParams)
s.logDebugExchange(requestID, transactionID, path, curlCmd, 0, time.Since(startTime), nil, err.Error())
s.logError(requestID, transactionID, path, err, map[string]interface{}{"request_params": logParams})
return nil, err
}
defer resp.Body.Close()
@@ -185,45 +184,34 @@ func (s *YuyuechaService) doRequest(ctx context.Context, method, path string, bo
raw, err := io.ReadAll(resp.Body)
if err != nil {
err = errors.Join(ErrSystem, err)
errParams := map[string]interface{}{"request_params": logParams}
if curlCmd != "" {
errParams["curl"] = curlCmd
}
s.logError(requestID, transactionID, path, err, errParams)
s.logDebugExchange(requestID, transactionID, path, curlCmd, resp.StatusCode, duration, nil, err.Error())
s.logError(requestID, transactionID, path, err, map[string]interface{}{"request_params": logParams})
return nil, err
}
s.logDebugCurlAndResponse(requestID, transactionID, path, curlCmd, resp.StatusCode, duration, raw)
s.logDebugExchange(requestID, transactionID, path, curlCmd, resp.StatusCode, duration, raw, "")
if resp.StatusCode != http.StatusOK {
mapped := mapHTTPStatusError(resp.StatusCode, raw)
errParams := map[string]interface{}{
s.logError(requestID, transactionID, path, mapped, map[string]interface{}{
"request_params": logParams,
"response_body": truncateForLog(string(raw), maxLogResponseBodyLen),
"http_status": resp.StatusCode,
}
if curlCmd != "" {
errParams["curl"] = curlCmd
}
s.logError(requestID, transactionID, path, mapped, errParams)
})
return nil, mapped
}
if s.logger != nil {
if !s.config.Debug && s.logger != nil {
s.logger.LogResponse(requestID, transactionID, path, resp.StatusCode, duration)
}
var outer APIResponse
if err := json.Unmarshal(raw, &outer); err != nil {
parseErr := errors.Join(ErrSystem, fmt.Errorf("响应解析失败: %w", err))
errParams := map[string]interface{}{
s.logError(requestID, transactionID, path, parseErr, map[string]interface{}{
"request_params": logParams,
"response_body": truncateForLog(string(raw), maxLogResponseBodyLen),
}
if curlCmd != "" {
errParams["curl"] = curlCmd
}
s.logError(requestID, transactionID, path, parseErr, errParams)
})
return nil, parseErr
}
@@ -235,14 +223,10 @@ func (s *YuyuechaService) doRequest(ctx context.Context, method, path string, bo
msg = *outer.ErrCode
}
mapped := errors.Join(ErrDatasource, errors.New(msg))
errParams := map[string]interface{}{
s.logError(requestID, transactionID, path, mapped, map[string]interface{}{
"request_params": logParams,
"response_body": truncateForLog(string(raw), maxLogResponseBodyLen),
}
if curlCmd != "" {
errParams["curl"] = curlCmd
}
s.logError(requestID, transactionID, path, mapped, errParams)
})
return nil, mapped
}
@@ -329,18 +313,18 @@ func (s *YuyuechaService) logError(requestID, transactionID, apiCode string, err
}
}
// logDebugCurlAndResponse 仅在 Debug开发环境输出完整 curl 与响应体
func (s *YuyuechaService) logDebugCurlAndResponse(requestID, transactionID, apiCode, curlCmd string, statusCode int, duration time.Duration, raw []byte) {
if !s.config.Debug || s.logger == nil || curlCmd == "" {
func (s *YuyuechaService) logDebugExchange(requestID, transactionID, apiCode, curlCmd string, statusCode int, duration time.Duration, raw []byte, errMsg string) {
if !s.config.Debug {
return
}
s.logger.LogInfo("愉悦查请求响应(dev)",
zap.String("request_id", requestID),
zap.String("transaction_id", transactionID),
zap.String("api_code", apiCode),
zap.Int("http_status", statusCode),
zap.Duration("duration", duration),
zap.String("curl", curlCmd),
zap.String("response_body", string(raw)),
)
writeDebugExchange(s.config.DebugLogDir, debugExchangeRecord{
RequestID: requestID,
TransactionID: transactionID,
API: apiCode,
Status: statusCode,
Duration: duration,
Curl: curlCmd,
ResponseBody: raw,
Error: errMsg,
})
}