修改并重构日志记录

This commit is contained in:
wangxiu 2026-07-10 13:24:49 +08:00
parent f9bfc0a6ce
commit d89bae0bd0
6 changed files with 150 additions and 38 deletions

View File

@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.*; import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -77,7 +78,8 @@ public class GenericApiClient {
if (respBody == null) { if (respBody == null) {
throw new ApiCallException("NULL_RESPONSE", "Empty response body", "") throw new ApiCallException("NULL_RESPONSE", "Empty response body", "")
.withRequest(url, reqHeadersJson); .withRequest(url, reqHeadersJson)
.withStatusCode(httpStatus);
} }
return new ApiCallResult(requestId, url, reqHeadersJson, reqBodyJson, return new ApiCallResult(requestId, url, reqHeadersJson, reqBodyJson,
@ -105,8 +107,11 @@ public class GenericApiClient {
long elapsed = System.currentTimeMillis() - start; long elapsed = System.currentTimeMillis() - start;
log.error("Third-party HTTP error after {} attempts: url={} requestId={}", log.error("Third-party HTTP error after {} attempts: url={} requestId={}",
attempt, url, requestId, e); attempt, url, requestId, e);
Integer statusCode = e instanceof HttpStatusCodeException
? ((HttpStatusCodeException) e).getRawStatusCode() : null;
throw new ApiCallException("HTTP_ERROR", e.getMessage(), "") throw new ApiCallException("HTTP_ERROR", e.getMessage(), "")
.withRequest(url, reqHeadersJson); .withRequest(url, reqHeadersJson)
.withStatusCode(statusCode);
} }
} }
} }

View File

@ -6,6 +6,7 @@ public class ApiCallException extends RuntimeException {
private final String responseBody; private final String responseBody;
private String reqUrl; private String reqUrl;
private String reqHeaders; private String reqHeaders;
private Integer httpStatusCode;
public ApiCallException(String code, String message, String responseBody) { public ApiCallException(String code, String message, String responseBody) {
super(message); super(message);
@ -19,8 +20,14 @@ public class ApiCallException extends RuntimeException {
return this; return this;
} }
public ApiCallException withStatusCode(Integer httpStatusCode) {
this.httpStatusCode = httpStatusCode;
return this;
}
public String getCode() { return code; } public String getCode() { return code; }
public String getResponseBody() { return responseBody; } public String getResponseBody() { return responseBody; }
public String getReqUrl() { return reqUrl; } public String getReqUrl() { return reqUrl; }
public String getReqHeaders() { return reqHeaders; } public String getReqHeaders() { return reqHeaders; }
public Integer getHttpStatusCode() { return httpStatusCode; }
} }

View File

@ -1,7 +1,42 @@
package com.lyxh.api.docking.service; package com.lyxh.api.docking.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.lyxh.api.docking.client.ApiCallResult;
import com.lyxh.api.docking.entity.ApiInvokeLog; import com.lyxh.api.docking.entity.ApiInvokeLog;
import com.lyxh.api.docking.entity.ThirdPartyConfig;
import com.lyxh.api.docking.exception.ApiCallException;
public interface ApiInvokeLogService extends IService<ApiInvokeLog> { public interface ApiInvokeLogService extends IService<ApiInvokeLog> {
/**
* 调用成功的日志记录
* @param config 三方API配置
* @param thirdPartyCode 三方标识
* @param apiCode 接口标识
* @param weOrderNo 系统订单号
* @param csOrderNo 客户订单号
* @param dsOrderNo 三方订单号
* @param result 三方响应结果
* @param errorMessage 错误信息
*/
void saveInvokeLog(ThirdPartyConfig config, String thirdPartyCode, String apiCode,
String weOrderNo, String csOrderNo, String dsOrderNo,
ApiCallResult result, String errorMessage);
/**
* 调用失败的日志记录
* @param configGroup
* @param thirdPartyCode 三方标识
* @param apiCode 接口标识
* @param requestBody 三方相应
* @param weOrderNo 系统订单号
* @param csOrderNo 客户订单号
* @param e 系统异常
*/
void logError(String configGroup, String thirdPartyCode, String apiCode,
Object requestBody, String weOrderNo, String csOrderNo,
ApiCallException e);
} }

View File

@ -1,11 +1,8 @@
package com.lyxh.api.docking.service; package com.lyxh.api.docking.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lyxh.api.docking.client.ApiCallResult; import com.lyxh.api.docking.client.ApiCallResult;
import com.lyxh.api.docking.client.GenericApiClient; import com.lyxh.api.docking.client.GenericApiClient;
import com.lyxh.api.docking.entity.ApiCacheData; import com.lyxh.api.docking.entity.ApiCacheData;
import com.lyxh.api.docking.entity.ApiInvokeLog;
import com.lyxh.api.docking.entity.ThirdPartyConfig; import com.lyxh.api.docking.entity.ThirdPartyConfig;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -34,8 +31,6 @@ public class GenericApiService {
private ApiCacheDataService cacheDataService; private ApiCacheDataService cacheDataService;
@Autowired @Autowired
private ApiInvokeLogService invokeLogService; private ApiInvokeLogService invokeLogService;
@Autowired
private ObjectMapper objectMapper;
/** /**
* 执行第三方 API 调用(含缓存、日志),返回响应体原始 JSON * 执行第三方 API 调用(含缓存、日志),返回响应体原始 JSON
@ -84,8 +79,8 @@ public class GenericApiService {
? dsOrderNoExtractor.apply(respBodyJson) ? dsOrderNoExtractor.apply(respBodyJson)
: null; : null;
// 5. 记录调用日志 // 5. 记录调用日志(成功)
saveInvokeLog(config, thirdPartyCode, apiCode, weOrderNo, csOrderNo, dsOrderNo, result, null); invokeLogService.saveInvokeLog(config, thirdPartyCode, apiCode, weOrderNo, csOrderNo, dsOrderNo, result, null);
// 6. 写缓存 // 6. 写缓存
if (isCacheEnabled(config) && cacheKey != null) { if (isCacheEnabled(config) && cacheKey != null) {
@ -109,28 +104,16 @@ public class GenericApiService {
return config.getEnableCache() != null && config.getEnableCache() == 1; return config.getEnableCache() != null && config.getEnableCache() == 1;
} }
private void saveInvokeLog(ThirdPartyConfig config, String thirdPartyCode, String apiCode, /**
String weOrderNo, String csOrderNo, String dsOrderNo, * 记录缓存数据
ApiCallResult result, String errorMessage) { * @param config 三方API配置
ApiInvokeLog logEntity = new ApiInvokeLog(); * @param apiCode 接口标识
logEntity.setConfigId(config.getId()); * @param cacheKey 缓存键
logEntity.setThirdPartyCode(thirdPartyCode); * @param cacheValue 缓存值
logEntity.setApiCode(apiCode); * @param weOrderNo 系统订单号
logEntity.setCsOrderNo(csOrderNo); * @param csOrderNo 客户订单号
logEntity.setWeOrderNo(weOrderNo); * @param dsOrderNo 三方订单号
logEntity.setDsOrderNo(dsOrderNo); */
logEntity.setReqUrl(result.getUrl());
logEntity.setReqMethod("POST");
logEntity.setReqHeaders(result.getReqHeadersJson());
logEntity.setReqBody(result.getReqBodyJson());
logEntity.setRespStatusCode(result.getHttpStatusCode());
logEntity.setRespBody(result.getRespBodyJson());
logEntity.setCostMillis((int) result.getCostMillis());
logEntity.setErrorMessage(errorMessage);
invokeLogService.save(logEntity);
log.info("Invoke log saved: weOrderNo={}", weOrderNo);
}
private void saveCache(ThirdPartyConfig config, String apiCode, String cacheKey, private void saveCache(ThirdPartyConfig config, String apiCode, String cacheKey,
String cacheValue, String weOrderNo, String csOrderNo, String dsOrderNo) { String cacheValue, String weOrderNo, String csOrderNo, String dsOrderNo) {
Duration ttl = Duration.ofDays(config.getCacheTtlDays()); Duration ttl = Duration.ofDays(config.getCacheTtlDays());
@ -140,11 +123,4 @@ public class GenericApiService {
log.info("Cache saved: weOrderNo={} apiCode={} cacheKey={}", weOrderNo, apiCode, cacheKey); log.info("Cache saved: weOrderNo={} apiCode={} cacheKey={}", weOrderNo, apiCode, cacheKey);
} }
private String toJson(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
return "{}";
}
}
} }

View File

@ -28,6 +28,9 @@ public class PaymentProfileService {
@Autowired @Autowired
private GenericApiService genericApiService; private GenericApiService genericApiService;
@Autowired
private ApiInvokeLogService invokeLogService;
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@ -65,6 +68,15 @@ public class PaymentProfileService {
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new ApiCallException("PARSE_ERROR", "Failed to parse Fortispay response: " + e.getMessage(), null); throw new ApiCallException("PARSE_ERROR", "Failed to parse Fortispay response: " + e.getMessage(), null);
} catch (ApiCallException e) { } catch (ApiCallException e) {
// 插入日志 (调用三方api失败)
invokeLogService.logError(
API_TYPE.getConfigGroup(),
API_TYPE.getThirdPartyCode(),
API_TYPE.getApiCode(),
ftRequest,
weOrderNo,
xRequestId,
e);
throw e; throw e;
} }
} }

View File

@ -1,13 +1,90 @@
package com.lyxh.api.docking.service.impl; package com.lyxh.api.docking.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lyxh.api.docking.client.ApiCallResult;
import com.lyxh.api.docking.entity.ApiInvokeLog; import com.lyxh.api.docking.entity.ApiInvokeLog;
import com.lyxh.api.docking.entity.ThirdPartyConfig;
import com.lyxh.api.docking.exception.ApiCallException;
import com.lyxh.api.docking.mapper.ApiInvokeLogMapper; import com.lyxh.api.docking.mapper.ApiInvokeLogMapper;
import com.lyxh.api.docking.service.ApiInvokeLogService; import com.lyxh.api.docking.service.ApiInvokeLogService;
import com.lyxh.api.docking.service.ThirdPartyConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
public class ApiInvokeLogServiceImpl public class ApiInvokeLogServiceImpl
extends ServiceImpl<ApiInvokeLogMapper, ApiInvokeLog> extends ServiceImpl<ApiInvokeLogMapper, ApiInvokeLog>
implements ApiInvokeLogService { implements ApiInvokeLogService {
private static final Logger log = LoggerFactory.getLogger(ApiInvokeLogService.class);
@Autowired
private ThirdPartyConfigService configService;
@Autowired
private ObjectMapper objectMapper;
@Override
public void saveInvokeLog(ThirdPartyConfig config, String thirdPartyCode, String apiCode, String weOrderNo
, String csOrderNo, String dsOrderNo, ApiCallResult result, String errorMessage) {
saveInvokeLogInternal(config.getId(), thirdPartyCode, apiCode, weOrderNo, csOrderNo, dsOrderNo,
result.getUrl(), result.getReqHeadersJson(), result.getReqBodyJson(),
result.getHttpStatusCode(), result.getRespBodyJson(), (int) result.getCostMillis(),
errorMessage);
}
@Override
public void logError(String configGroup, String thirdPartyCode, String apiCode, Object requestBody, String weOrderNo
, String csOrderNo, ApiCallException e) {
ThirdPartyConfig config = configService.getEnabledConfig(configGroup, thirdPartyCode, apiCode);
Long configId = config != null ? config.getId() : null;
String reqBodyJson = toJson(requestBody);
String errorMessage = "[" + e.getCode() + "] " + e.getMessage();
saveInvokeLogInternal(
configId,
thirdPartyCode, apiCode, weOrderNo, csOrderNo, null, // dsOrderNo = null
e.getReqUrl(), e.getReqHeaders(), reqBodyJson,
e.getHttpStatusCode(), null, null, // respBody, costMillis 为空
errorMessage
);
}
private String toJson(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
return "{}";
}
}
private void saveInvokeLogInternal(Long configId, String thirdPartyCode, String apiCode, String weOrderNo
, String csOrderNo, String dsOrderNo, String reqUrl, String reqHeadersJson, String reqBodyJson
, Integer httpStatusCode, String respBodyJson, Integer costMillis, String errorMessage) {
ApiInvokeLog logEntity = new ApiInvokeLog();
logEntity.setConfigId(configId);
logEntity.setThirdPartyCode(thirdPartyCode);
logEntity.setApiCode(apiCode);
logEntity.setCsOrderNo(csOrderNo);
logEntity.setWeOrderNo(weOrderNo);
logEntity.setDsOrderNo(dsOrderNo);
logEntity.setReqUrl(reqUrl);
logEntity.setReqMethod("POST");
logEntity.setReqHeaders(reqHeadersJson);
logEntity.setReqBody(reqBodyJson);
logEntity.setRespStatusCode(httpStatusCode);
logEntity.setRespBody(respBodyJson);
logEntity.setCostMillis(costMillis);
logEntity.setErrorMessage(errorMessage);
this.save(logEntity);
log.info("Invoke log saved: weOrderNo={}", weOrderNo);
}
} }