修改并重构日志记录
This commit is contained in:
parent
f9bfc0a6ce
commit
d89bae0bd0
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 "{}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue