This commit is contained in:
Mrx
2026-05-26 16:17:09 +08:00
parent 760c5812ee
commit f10c5dd626
20 changed files with 598 additions and 10 deletions

View File

@@ -6,6 +6,7 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
@@ -85,6 +86,8 @@ type HuiboConfig struct {
AESKey string
WorkOrderCode string
ProductCode string
BaseURL2 string // CallAPI2 使用的 URL
AppCode2 string // CallAPI2 使用的 AppCode
}
type HuiboService struct {
@@ -101,10 +104,22 @@ type responseWrapper struct {
} `json:"data"`
}
// CallAPI2Response CallAPI2 的响应结构体
type CallAPI2Response struct {
Code string `json:"code"`
Data map[string]interface{} `json:"data"`
Msg string `json:"msg"`
}
func NewHuiboService(config HuiboConfig, logger *external_logger.ExternalServiceLogger) *HuiboService {
return &HuiboService{config: config, logger: logger}
}
// GetConfig 获取汇博配置
func (s *HuiboService) GetConfig() HuiboConfig {
return s.config
}
// CallEducationBackgroundDetailed 教育背景(详细)查询
func (s *HuiboService) CallEducationBackgroundDetailed(ctx context.Context, name, idCard, authPDFBase64 string) ([]byte, error) {
requestID := s.generateRequestID()
@@ -440,3 +455,158 @@ func randomDigits(n int) string {
}
return string(b)
}
// MD5Encrypt 使用配置的 AppKey 进行 MD5 加密
func (s *HuiboService) MD5Encrypt(data string) string {
h := md5.New()
h.Write([]byte(data + s.config.AppKey))
return fmt.Sprintf("%x", h.Sum(nil))
}
// CallAPI2 通用 HTTP 调用方法,返回原始响应 JSON
func (s *HuiboService) CallAPI2(ctx context.Context, pcode string, requestData map[string]interface{}) ([]byte, error) {
startTime := time.Now()
transactionID := ""
if v, ok := ctx.Value("transaction_id").(string); ok {
transactionID = v
}
if s.logger != nil {
s.logger.LogRequest("", transactionID, "huibo_callapi2", s.config.BaseURL2)
}
if strings.TrimSpace(s.config.BaseURL2) == "" {
return nil, errors.Join(ErrSystem, errors.New("汇博配置不完整BaseURL2为空"))
}
if strings.TrimSpace(s.config.AppCode2) == "" {
return nil, errors.Join(ErrSystem, errors.New("汇博配置不完整AppCode2为空"))
}
reqJSON, err := json.Marshal(requestData)
if err != nil {
return nil, errors.Join(ErrSystem, fmt.Errorf("请求参数序列化失败: %w", err))
}
// 构建 curl 命令的 headers
headers := map[string]string{
"AppCode": s.config.AppCode2,
"pcode": pcode,
"Content-Type": "application/json",
"X-ORDER-CODE": s.config.XOrderCode,
}
// 生成包含请求体的 curl 命令用于日志记录
curlCmd := generateCurlCommandWithBody("POST", s.config.BaseURL2, headers, string(reqJSON))
// 创建 HTTP 请求
req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.config.BaseURL2, bytes.NewBuffer(reqJSON))
if err != nil {
return nil, errors.Join(ErrSystem, fmt.Errorf("创建HTTP请求失败: %w", err))
}
// req.Header.Set(headerAuthorization, s.config.AppID+"::"+s.config.AppKey)
// req.Header.Set(headerWorkOrderCode, s.config.WorkOrderCode)
// req.Header.Set(headerOrderCode, s.config.XOrderCode)
// req.Header.Set(headerSecretIDHdr, s.config.SecretID)
// req.Header.Set(headerAESKeyHdr, s.config.AESKey)
// req.Header.Set("Content-Type", writer.FormDataContentType())
// 设置请求头
req.Header.Set("AppCode", s.config.AppCode2)
req.Header.Set("pcode", pcode)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ORDER-CODE", s.config.XOrderCode)
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(req)
if err != nil {
if s.logger != nil {
s.logger.LogErrorWithFields("汇博 CallAPI2 HTTP 请求失败",
zap.String("url", s.config.BaseURL2),
zap.String("pcode", pcode),
zap.String("curl", curlCmd),
zap.Error(err),
)
}
return nil, errors.Join(ErrDatasource, err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
if s.logger != nil {
s.logger.LogErrorWithFields("汇博 CallAPI2 读取响应体失败",
zap.String("url", s.config.BaseURL2),
zap.Int("http_status", resp.StatusCode),
zap.Error(err),
)
}
return nil, errors.Join(ErrSystem, fmt.Errorf("读取响应体失败: %w", err))
}
// 解析响应以检查业务状态码
var response CallAPI2Response
if err := json.Unmarshal(respBody, &response); err != nil {
if s.logger != nil {
s.logger.LogErrorWithFields("汇博 CallAPI2 响应解析失败",
zap.String("url", s.config.BaseURL2),
zap.String("pcode", pcode),
zap.Error(err),
)
}
return nil, errors.Join(ErrDatasource, fmt.Errorf("响应解析失败: %w", err))
}
// 根据业务状态码进行处理
switch response.Code {
case CallAPI2StatusSuccess:
// 查询成功
if s.logger != nil {
s.logger.LogInfo(
"汇博 CallAPI2 查询成功",
zap.String("pcode", pcode),
zap.String("code", response.Code),
zap.String("transaction_id", transactionID),
)
}
case CallAPI2StatusNoData:
// 查询成功,无数据
if s.logger != nil {
s.logger.LogInfo(
"汇博 CallAPI2 查询成功但无数据",
zap.String("pcode", pcode),
zap.String("code", response.Code),
zap.String("transaction_id", transactionID),
)
}
default:
// 其他错误状态码
message := GetCallAPI2StatusMessage(response.Code)
if s.logger != nil {
s.logger.LogErrorWithFields("汇博 CallAPI2 业务状态异常",
zap.String("url", s.config.BaseURL2),
zap.String("pcode", pcode),
zap.String("code", response.Code),
zap.String("message", message),
)
}
return nil, errors.Join(ErrDatasource, fmt.Errorf("业务状态异常(code=%s,msg=%s)", response.Code, message))
}
// 记录 curl 命令和响应
if s.logger != nil {
s.logger.LogInfo(
"汇博 CallAPI2 请求响应",
zap.String("curl", curlCmd),
zap.String("response_body", string(respBody)),
zap.String("transaction_id", transactionID),
)
}
if s.logger != nil {
s.logger.LogResponse("", transactionID, "huibo_callapi2", http.StatusOK, time.Since(startTime))
}
return respBody, nil
}