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("系统异常") ) 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]) } // buildLogData 构建包含transactionId的日志数据 func (w *WestDexService) buildLogData(data map[string]interface{}, transactionID string) map[string]interface{} { if transactionID == "" { return data } logData := data if logData == nil { logData = make(map[string]interface{}) } logData["transaction_id"] = transactionID return logData } // 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, code, reqUrl, w.buildLogData(reqData, transactionID)) } jsonData, marshalErr := json.Marshal(reqData) if marshalErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, marshalErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 创建HTTP POST请求 req, newRequestErr := http.NewRequestWithContext(ctx, "POST", reqUrl, bytes.NewBuffer(jsonData)) if newRequestErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, newRequestErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 发送请求 client := &http.Client{} httpResp, clientDoErr := client.Do(req) if clientDoErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, clientDoErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } defer func(Body io.ReadCloser) { closeErr := Body.Close() if closeErr != nil { // 记录关闭错误 if w.logger != nil { w.logger.LogError(requestID, code, fmt.Errorf("关闭响应体失败: %w", closeErr), w.buildLogData(reqData, transactionID)) } } }(httpResp.Body) // 计算请求耗时 duration := time.Since(startTime) // 检查请求是否成功 if httpResp.StatusCode == 200 { // 读取响应体 bodyBytes, ReadErr := io.ReadAll(httpResp.Body) if ReadErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, ReadErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 记录响应日志 if w.logger != nil { w.logger.LogResponse(requestID, code, httpResp.StatusCode, bodyBytes, duration) } // 手动调用 json.Unmarshal 触发自定义的 UnmarshalJSON 方法 var westDexResp WestResp UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp) if UnmarshalErr != nil { err = UnmarshalErr if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } if westDexResp.Code != "00000" && westDexResp.Code != "200" && westDexResp.Code != "0" { if westDexResp.Data == "" { err = fmt.Errorf("%w: %s", ErrSystem, westDexResp.Message) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key) if DecryptErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, DecryptErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 记录业务错误日志 if w.logger != nil { w.logger.LogError(requestID, code, fmt.Errorf("%w: %s", ErrDatasource, westDexResp.Message), w.buildLogData(reqData, transactionID)) } // 记录性能日志(失败) // 注意:通用日志系统不包含性能日志功能 return decryptedData, fmt.Errorf("%w: %s", ErrDatasource, westDexResp.Message) } if westDexResp.Data == "" { err = fmt.Errorf("%w: %s", ErrSystem, westDexResp.Message) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key) if DecryptErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, DecryptErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 记录性能日志(成功) // 注意:通用日志系统不包含性能日志功能 return decryptedData, nil } // 记录HTTP错误 err = fmt.Errorf("%w: 西部请求失败Code: %d", ErrSystem, httpResp.StatusCode) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) // 注意:通用日志系统不包含性能日志功能 } 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, code, reqUrl, w.buildLogData(reqData, transactionID)) } jsonData, marshalErr := json.Marshal(reqData) if marshalErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, marshalErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 创建HTTP POST请求 req, newRequestErr := http.NewRequestWithContext(ctx, "POST", reqUrl, bytes.NewBuffer(jsonData)) if newRequestErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, newRequestErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 发送请求 client := &http.Client{} httpResp, clientDoErr := client.Do(req) if clientDoErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, clientDoErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } defer func(Body io.ReadCloser) { closeErr := Body.Close() if closeErr != nil { // 记录关闭错误 if w.logger != nil { w.logger.LogError(requestID, code, fmt.Errorf("关闭响应体失败: %w", closeErr), w.buildLogData(reqData, transactionID)) } } }(httpResp.Body) // 计算请求耗时 duration := time.Since(startTime) if httpResp.StatusCode == 200 { bodyBytes, ReadErr := io.ReadAll(httpResp.Body) if ReadErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, ReadErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 记录响应日志 if w.logger != nil { w.logger.LogResponse(requestID, code, httpResp.StatusCode, bodyBytes, duration) } var westDexResp G05HZ01WestResp UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp) if UnmarshalErr != nil { err = fmt.Errorf("%w: %s", ErrSystem, UnmarshalErr.Error()) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } if westDexResp.Code != "0000" { if westDexResp.Data == nil { err = fmt.Errorf("%w: %s", ErrSystem, westDexResp.Message) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } else { // 记录业务错误日志 if w.logger != nil { w.logger.LogError(requestID, code, fmt.Errorf("%w: %s", ErrSystem, string(westDexResp.Data)), w.buildLogData(reqData, transactionID)) } // 记录性能日志(失败) // 注意:通用日志系统不包含性能日志功能 return westDexResp.Data, fmt.Errorf("%w: %s", ErrSystem, string(westDexResp.Data)) } } if westDexResp.Data == nil { err = fmt.Errorf("%w: %s", ErrSystem, westDexResp.Message) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) } return nil, err } // 记录性能日志(成功) // 注意:通用日志系统不包含性能日志功能 return westDexResp.Data, nil } else { // 记录HTTP错误 err = fmt.Errorf("%w: 西部请求失败Code: %d", ErrSystem, httpResp.StatusCode) if w.logger != nil { w.logger.LogError(requestID, code, err, w.buildLogData(reqData, transactionID)) // 注意:通用日志系统不包含性能日志功能 } 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 }