package westdex import ( "bytes" "context" "crypto/md5" "encoding/json" "errors" "fmt" "io" "net/http" "strconv" "time" "tyapi-server/internal/shared/crypto" "tyapi-server/internal/shared/external_logger" ) var ( ErrDatasource = errors.New("数据源异常") ErrSystem = errors.New("系统异常") ErrNotFound = errors.New("查询为空") ) type WestResp struct { Message string `json:"message"` Code string `json:"code"` Data string `json:"data"` ID string `json:"id"` ErrorCode *int `json:"error_code"` Reason string `json:"reason"` } type G05HZ01WestResp struct { Message string `json:"message"` Code string `json:"code"` Data json.RawMessage `json:"data"` ID string `json:"id"` ErrorCode *int `json:"error_code"` Reason string `json:"reason"` } type WestConfig struct { Url string Key string SecretID string SecretSecondID string } type WestDexService struct { config WestConfig logger *external_logger.ExternalServiceLogger } // NewWestDexService 是一个构造函数,用于初始化 WestDexService func NewWestDexService(url, key, secretID, secretSecondID string, logger *external_logger.ExternalServiceLogger) *WestDexService { return &WestDexService{ config: WestConfig{ Url: url, Key: key, SecretID: secretID, SecretSecondID: secretSecondID, }, logger: logger, } } // generateRequestID 生成请求ID func (w *WestDexService) generateRequestID() string { timestamp := time.Now().UnixNano() hash := md5.Sum([]byte(fmt.Sprintf("%d_%s", timestamp, w.config.Key))) return fmt.Sprintf("westdex_%x", hash[:8]) } // buildRequestURL 构建请求URL func (w *WestDexService) buildRequestURL(code string) string { timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) return fmt.Sprintf("%s/%s/%s?timestamp=%s", w.config.Url, w.config.SecretID, code, timestamp) } // CallAPI 调用西部数据的 API func (w *WestDexService) CallAPI(ctx context.Context, code string, reqData map[string]interface{}) (resp []byte, err error) { startTime := time.Now() requestID := w.generateRequestID() // 从ctx中获取transactionId var transactionID string if ctxTransactionID, ok := ctx.Value("transaction_id").(string); ok { transactionID = ctxTransactionID } // 构建请求URL reqUrl := w.buildRequestURL(code) // 记录请求日志 if w.logger != nil { w.logger.LogRequest(requestID, transactionID, code, reqUrl, reqData) } jsonData, marshalErr := json.Marshal(reqData) if marshalErr != nil { err = errors.Join(ErrSystem, marshalErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } // 创建HTTP POST请求 req, newRequestErr := http.NewRequestWithContext(ctx, "POST", reqUrl, bytes.NewBuffer(jsonData)) if newRequestErr != nil { err = errors.Join(ErrSystem, newRequestErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 发送请求 client := &http.Client{} httpResp, clientDoErr := client.Do(req) if clientDoErr != nil { err = errors.Join(ErrSystem, clientDoErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } defer func(Body io.ReadCloser) { closeErr := Body.Close() if closeErr != nil { // 记录关闭错误 if w.logger != nil { w.logger.LogError(requestID, transactionID, code, errors.Join(ErrSystem, fmt.Errorf("关闭响应体失败: %w", closeErr)), reqData) } } }(httpResp.Body) // 计算请求耗时 duration := time.Since(startTime) // 检查请求是否成功 if httpResp.StatusCode == 200 { // 读取响应体 bodyBytes, ReadErr := io.ReadAll(httpResp.Body) if ReadErr != nil { err = errors.Join(ErrSystem, ReadErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } // 手动调用 json.Unmarshal 触发自定义的 UnmarshalJSON 方法 var westDexResp WestResp UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp) if UnmarshalErr != nil { err = errors.Join(ErrSystem, UnmarshalErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } // 记录响应日志,包含响应ID if w.logger != nil { w.logger.LogResponseWithID(requestID, transactionID, code, httpResp.StatusCode, bodyBytes, duration, westDexResp.ID) } if westDexResp.Code != "00000" && westDexResp.Code != "200" && westDexResp.Code != "0" { if westDexResp.Data == "" { err = errors.Join(ErrSystem, fmt.Errorf(westDexResp.Message)) if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, err, reqData, westDexResp.ID) } return nil, err } decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key) if DecryptErr != nil { err = errors.Join(ErrSystem, DecryptErr) if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, err, reqData, westDexResp.ID) } return nil, err } // 记录业务错误日志,包含响应ID if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, errors.Join(ErrDatasource, fmt.Errorf(westDexResp.Message)), reqData, westDexResp.ID) } // 记录性能日志(失败) // 注意:通用日志系统不包含性能日志功能 return decryptedData, errors.Join(ErrDatasource, fmt.Errorf(westDexResp.Message)) } if westDexResp.Data == "" { err = errors.Join(ErrSystem, fmt.Errorf(westDexResp.Message)) if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, err, reqData, westDexResp.ID) } return nil, err } decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key) if DecryptErr != nil { err = errors.Join(ErrSystem, DecryptErr) if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, err, reqData, westDexResp.ID) } return nil, err } // 记录性能日志(成功) // 注意:通用日志系统不包含性能日志功能 return decryptedData, nil } // 记录HTTP错误 err = errors.Join(ErrSystem, fmt.Errorf("西部请求失败Code: %d", httpResp.StatusCode)) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) // 注意:通用日志系统不包含性能日志功能 } return nil, err } // G05HZ01CallAPI 调用西部数据的 G05HZ01 API func (w *WestDexService) G05HZ01CallAPI(ctx context.Context, code string, reqData map[string]interface{}) (resp []byte, err error) { startTime := time.Now() requestID := w.generateRequestID() // 从ctx中获取transactionId var transactionID string if ctxTransactionID, ok := ctx.Value("transaction_id").(string); ok { transactionID = ctxTransactionID } // 构建请求URL reqUrl := fmt.Sprintf("%s/%s/%s?timestamp=%d", w.config.Url, w.config.SecretSecondID, code, time.Now().UnixNano()/int64(time.Millisecond)) // 记录请求日志 if w.logger != nil { w.logger.LogRequest(requestID, transactionID, code, reqUrl, reqData) } jsonData, marshalErr := json.Marshal(reqData) if marshalErr != nil { err = errors.Join(ErrSystem, marshalErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } // 创建HTTP POST请求 req, newRequestErr := http.NewRequestWithContext(ctx, "POST", reqUrl, bytes.NewBuffer(jsonData)) if newRequestErr != nil { err = errors.Join(ErrSystem, newRequestErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 发送请求 client := &http.Client{} httpResp, clientDoErr := client.Do(req) if clientDoErr != nil { err = errors.Join(ErrSystem, clientDoErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } defer func(Body io.ReadCloser) { closeErr := Body.Close() if closeErr != nil { // 记录关闭错误 if w.logger != nil { w.logger.LogError(requestID, transactionID, code, errors.Join(ErrSystem, fmt.Errorf("关闭响应体失败: %w", closeErr)), reqData) } } }(httpResp.Body) // 计算请求耗时 duration := time.Since(startTime) if httpResp.StatusCode == 200 { bodyBytes, ReadErr := io.ReadAll(httpResp.Body) if ReadErr != nil { err = errors.Join(ErrSystem, ReadErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } var westDexResp G05HZ01WestResp UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp) if UnmarshalErr != nil { err = errors.Join(ErrSystem, UnmarshalErr) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) } return nil, err } // 记录响应日志,包含响应ID if w.logger != nil { w.logger.LogResponseWithID(requestID, transactionID, code, httpResp.StatusCode, bodyBytes, duration, westDexResp.ID) } if westDexResp.Code != "0000" { if westDexResp.Data == nil || westDexResp.Code == "1404" { err = errors.Join(ErrNotFound, fmt.Errorf(westDexResp.Message)) if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, err, reqData, westDexResp.ID) } return nil, err } else { // 记录业务错误日志,包含响应ID if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, errors.Join(ErrSystem, fmt.Errorf(string(westDexResp.Data))), reqData, westDexResp.ID) } // 记录性能日志(失败) // 注意:通用日志系统不包含性能日志功能 return westDexResp.Data, errors.Join(ErrSystem, fmt.Errorf(string(westDexResp.Data))) } } if westDexResp.Data == nil { err = errors.Join(ErrSystem, fmt.Errorf(westDexResp.Message)) if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, err, reqData, westDexResp.ID) } return nil, err } // 记录性能日志(成功) // 注意:通用日志系统不包含性能日志功能 return westDexResp.Data, nil } else { // 记录HTTP错误 err = errors.Join(ErrSystem, fmt.Errorf("西部请求失败Code: %d", httpResp.StatusCode)) if w.logger != nil { w.logger.LogError(requestID, transactionID, code, err, reqData) // 注意:通用日志系统不包含性能日志功能 } return nil, err } } func (w *WestDexService) Encrypt(data string) (string, error) { encryptedValue, err := crypto.WestDexEncrypt(data, w.config.Key) if err != nil { return "", ErrSystem } return encryptedValue, nil } func (w *WestDexService) Md5Encrypt(data string) string { result := Md5Encrypt(data) return result } func (w *WestDexService) GetConfig() WestConfig { return w.config }