This commit is contained in:
Mrx
2026-05-29 15:56:40 +08:00
parent 49e17e4ab1
commit 16ab0d2c45

View File

@@ -3,7 +3,6 @@ package nuoer
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/md5"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -19,17 +18,19 @@ const defaultRequestTimeout = 4 * time.Second
// queryBillingAPIKeys 查询计费接口:数据未查得(busiCode=1000)仍按成功计费,返回空数据 // queryBillingAPIKeys 查询计费接口:数据未查得(busiCode=1000)仍按成功计费,返回空数据
var queryBillingAPIKeys = map[string]struct{}{ var queryBillingAPIKeys = map[string]struct{}{
"idRiskTagV106": {}, // 身份风险V106 "idRiskTagV106": {}, // 身份风险V106 idRiskTagV106
"personalLawsuit_cv2": {}, // 企业诉讼定制版 "personalLawsuit_cv2": {}, // 企业诉讼定制版 personallawsuit cv2
"personalLawsuit_cv1": {}, // 个人诉讼定制版 "personalLawsuit_cv1": {}, // 个人诉讼定制版personalLawsuit_cv1
"loanRiskTagV11": {}, // 借贷意向查询 "loanRiskTagV11": {}, // 借贷意向查询loanRiskTagV11
"loanRiskTagV5": {}, // 风险变量V5 "loanRiskTagV5": {}, // 风险变量V5loanRiskTagV5
"loanRiskTagV12": {}, // 特殊名单 "loanRiskTagV12": {}, // 特殊名单 loanRiskTagV12
"blackListV121_3_1": {}, // 债务逾期黑名单V3_1 "blackListV121_3_1": {}, // 债务逾期黑名单V3_1 blackListV121 3 1
"blackListV110": {}, // 特殊名单V110 "blackListV110": {}, // 特殊名单V110 blackListV110
"zhiTongModelG": {}, // 智瞳-通用版 "zhitong_ultra_v4_score": {}, // 智瞳分尊享版 zhitong ultra v4 score
"zhitong_ultra_v4_score": {}, // 智瞳分尊享版 "zhixiangScore": {}, // 智享分 zhixiangScore
"zhixiangScore": {}, // 智享分 "loanRiskTagV8": {}, // 风险变量V8 loanRiskTagV8
"loanRiskTagV9": {}, // 风险变量V9 loanRiskTagV9
"loanRiskTagV10": {}, // 风险变量V10 loanRiskTagV10
} }
func isQueryBillingAPIKey(apiKey string) bool { func isQueryBillingAPIKey(apiKey string) bool {
@@ -75,10 +76,18 @@ func NewNuoerService(url, appID, appSecret string, timeout time.Duration, logger
} }
} }
func (s *NuoerService) generateRequestID() string { func (s *NuoerService) logResponse(transactionID, apiKey string, statusCode int, duration time.Duration, seqNo string) {
timestamp := time.Now().UnixNano() if s.logger == nil {
hash := md5.Sum([]byte(fmt.Sprintf("%d_%s", timestamp, s.config.AppID))) return
return fmt.Sprintf("nuoer_%x", hash[:8]) }
s.logger.LogResponse(seqNo, transactionID, apiKey, statusCode, duration)
}
func (s *NuoerService) logError(transactionID, apiKey, seqNo string, err error, payload interface{}) {
if s.logger == nil {
return
}
s.logger.LogError(seqNo, transactionID, apiKey, err, payload)
} }
func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body map[string]string) (*nuoerResponse, error) { func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body map[string]string) (*nuoerResponse, error) {
@@ -90,7 +99,6 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
requestURL += apiPath requestURL += apiPath
} }
requestID := s.generateRequestID()
startTime := time.Now() startTime := time.Now()
var transactionID string var transactionID string
@@ -109,24 +117,20 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
} }
if s.logger != nil { if s.logger != nil {
s.logger.LogRequest(requestID, transactionID, apiKey, requestURL) s.logger.LogRequest("", transactionID, apiKey, requestURL)
} }
bodyBytes, err := json.Marshal(requestPayload) bodyBytes, err := json.Marshal(requestPayload)
if err != nil { if err != nil {
err = errors.Join(ErrSystem, err) err = errors.Join(ErrSystem, err)
if s.logger != nil { s.logError(transactionID, apiKey, "", err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
req, err := http.NewRequestWithContext(ctx, http.MethodPost, requestURL, bytes.NewBuffer(bodyBytes)) req, err := http.NewRequestWithContext(ctx, http.MethodPost, requestURL, bytes.NewBuffer(bodyBytes))
if err != nil { if err != nil {
err = errors.Join(ErrSystem, err) err = errors.Join(ErrSystem, err)
if s.logger != nil { s.logError(transactionID, apiKey, "", err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
@@ -135,9 +139,7 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
err = wrapHTTPError(err) err = wrapHTTPError(err)
if s.logger != nil { s.logError(transactionID, apiKey, "", err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -145,86 +147,72 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
respBody, err := io.ReadAll(resp.Body) respBody, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
err = errors.Join(ErrSystem, err) err = errors.Join(ErrSystem, err)
if s.logger != nil { s.logError(transactionID, apiKey, "", err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
if s.logger != nil { duration := time.Since(startTime)
s.logger.LogResponse(requestID, transactionID, apiKey, resp.StatusCode, time.Since(startTime))
}
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
err = errors.Join(ErrDatasource, fmt.Errorf("HTTP状态码 %d", resp.StatusCode)) err = errors.Join(ErrDatasource, fmt.Errorf("HTTP状态码 %d", resp.StatusCode))
if s.logger != nil { s.logError(transactionID, apiKey, "", err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
var nuoerResp nuoerResponse var nuoerResp nuoerResponse
if err := json.Unmarshal(respBody, &nuoerResp); err != nil { if err := json.Unmarshal(respBody, &nuoerResp); err != nil {
err = errors.Join(ErrSystem, fmt.Errorf("响应解析失败: %w", err)) err = errors.Join(ErrSystem, fmt.Errorf("响应解析失败: %w", err))
if s.logger != nil { s.logError(transactionID, apiKey, "", err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
if nuoerResp.Code != CodeSuccess { if nuoerResp.Code != CodeSuccess {
if nuoerResp.Code == BusiCodeNotFound && isQueryBillingAPIKey(apiKey) { if nuoerResp.Code == BusiCodeNotFound && isQueryBillingAPIKey(apiKey) {
nuoerResp.Data = map[string]interface{}{} nuoerResp.Data = map[string]interface{}{}
s.logResponse(transactionID, apiKey, resp.StatusCode, duration, nuoerResp.SeqNo)
return &nuoerResp, nil return &nuoerResp, nil
} }
nuoerErr := NewNuoerError(nuoerResp.Code, nuoerResp.Msg) nuoerErr := NewNuoerError(nuoerResp.Code, nuoerResp.Msg)
err = errors.Join(GetErrByPlatformCode(nuoerResp.Code), nuoerErr) err = errors.Join(GetErrByPlatformCode(nuoerResp.Code), nuoerErr)
if s.logger != nil { s.logError(transactionID, apiKey, nuoerResp.SeqNo, nuoerErr, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, nuoerErr, requestPayload)
}
return nil, err return nil, err
} }
if nuoerResp.Data == nil { if nuoerResp.Data == nil {
err = errors.Join(ErrSystem, errors.New("响应 data 为空")) err = errors.Join(ErrSystem, errors.New("响应 data 为空"))
if s.logger != nil { s.logError(transactionID, apiKey, nuoerResp.SeqNo, err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
busiCode, busiMsg, ok := parseDataBusiInfo(nuoerResp.Data) busiCode, busiMsg, ok := parseDataBusiInfo(nuoerResp.Data)
if !ok { if !ok {
err = errors.Join(ErrSystem, errors.New("响应 data 无法解析 busiCode")) err = errors.Join(ErrSystem, errors.New("响应 data 无法解析 busiCode"))
if s.logger != nil { s.logError(transactionID, apiKey, nuoerResp.SeqNo, err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
if busiCode != BusiCodeSuccess { if busiCode != BusiCodeSuccess {
if busiCode == BusiCodeNotFound && isQueryBillingAPIKey(apiKey) { if busiCode == BusiCodeNotFound && isQueryBillingAPIKey(apiKey) {
nuoerResp.Data = map[string]interface{}{} nuoerResp.Data = map[string]interface{}{}
s.logResponse(transactionID, apiKey, resp.StatusCode, duration, nuoerResp.SeqNo)
return &nuoerResp, nil return &nuoerResp, nil
} }
busiErr := NewNuoerBusiError(busiCode, busiMsg) busiErr := NewNuoerBusiError(busiCode, busiMsg)
err = errors.Join(GetErrByBusiCode(busiCode), busiErr) err = errors.Join(GetErrByBusiCode(busiCode), busiErr)
if s.logger != nil { s.logError(transactionID, apiKey, nuoerResp.SeqNo, busiErr, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, busiErr, requestPayload)
}
return nil, err return nil, err
} }
cleanedData, err := stripBusiMetaFromData(nuoerResp.Data) cleanedData, err := stripBusiMetaFromData(nuoerResp.Data)
if err != nil { if err != nil {
err = errors.Join(ErrSystem, fmt.Errorf("响应 data 清理失败: %w", err)) err = errors.Join(ErrSystem, fmt.Errorf("响应 data 清理失败: %w", err))
if s.logger != nil { s.logError(transactionID, apiKey, nuoerResp.SeqNo, err, requestPayload)
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload)
}
return nil, err return nil, err
} }
nuoerResp.Data = cleanedData nuoerResp.Data = cleanedData
s.logResponse(transactionID, apiKey, resp.StatusCode, duration, nuoerResp.SeqNo)
return &nuoerResp, nil return &nuoerResp, nil
} }