|
|
@@ -0,0 +1,240 @@
|
|
|
+package com.zsElectric.boot.charging.aspect;
|
|
|
+
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.zsElectric.boot.charging.entity.ThirdPartyApiLog;
|
|
|
+import com.zsElectric.boot.charging.service.ThirdPartyApiLogService;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
+import org.aspectj.lang.annotation.Around;
|
|
|
+import org.aspectj.lang.annotation.Aspect;
|
|
|
+import org.aspectj.lang.annotation.Pointcut;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.web.context.request.RequestContextHolder;
|
|
|
+import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Enumeration;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 第三方接口日志记录切面
|
|
|
+ * 拦截 LinkDataController 和 ChargingBusinessController 的所有方法,自动记录请求和响应日志
|
|
|
+ *
|
|
|
+ * @author system
|
|
|
+ * @since 2025-01-02
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Aspect
|
|
|
+@Component
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class ThirdPartyApiLogAspect {
|
|
|
+
|
|
|
+ private final ThirdPartyApiLogService thirdPartyApiLogService;
|
|
|
+ private final ObjectMapper objectMapper;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 定义切入点: LinkDataController 的所有方法
|
|
|
+ */
|
|
|
+ @Pointcut("execution(* com.zsElectric.boot.charging.controller.LinkDataController.*(..))")
|
|
|
+ public void linkDataControllerPointcut() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 定义切入点: ChargingBusinessController 的所有方法
|
|
|
+ */
|
|
|
+ @Pointcut("execution(* com.zsElectric.boot.charging.controller.ChargingBusinessController.*(..))")
|
|
|
+ public void chargingBusinessControllerPointcut() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 环绕通知: 记录 LinkDataController 接口日志
|
|
|
+ */
|
|
|
+ @Around("linkDataControllerPointcut()")
|
|
|
+ public Object aroundLinkDataController(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
|
+ return recordApiLog(joinPoint, "LinkDataController", 2); // 2-接收推送
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 环绕通知: 记录 ChargingBusinessController 接口日志
|
|
|
+ */
|
|
|
+ @Around("chargingBusinessControllerPointcut()")
|
|
|
+ public Object aroundChargingBusinessController(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
|
+ return recordApiLog(joinPoint, "ChargingBusinessController", 1); // 1-请求出去
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录接口日志的通用方法
|
|
|
+ */
|
|
|
+ private Object recordApiLog(ProceedingJoinPoint joinPoint, String controllerName, Integer logType) throws Throwable {
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
+ ThirdPartyApiLog apiLog = new ThirdPartyApiLog();
|
|
|
+
|
|
|
+ // 获取 HttpServletRequest
|
|
|
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
|
+ HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 设置基本信息
|
|
|
+ apiLog.setLogType(logType);
|
|
|
+ apiLog.setControllerName(controllerName);
|
|
|
+ apiLog.setCreatedTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ if (request != null) {
|
|
|
+ // 请求信息
|
|
|
+ apiLog.setRequestMethod(request.getMethod());
|
|
|
+ apiLog.setRequestUrl(request.getRequestURL().toString());
|
|
|
+ apiLog.setInterfaceName(request.getRequestURI());
|
|
|
+ apiLog.setClientIp(getClientIp(request));
|
|
|
+ apiLog.setUserAgent(request.getHeader("User-Agent"));
|
|
|
+
|
|
|
+ // 请求头
|
|
|
+ apiLog.setRequestHeaders(getRequestHeaders(request));
|
|
|
+
|
|
|
+ // 请求参数
|
|
|
+ Map<String, String[]> parameterMap = request.getParameterMap();
|
|
|
+ if (!parameterMap.isEmpty()) {
|
|
|
+ apiLog.setRequestParams(objectMapper.writeValueAsString(parameterMap));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 请求体(从切点参数中获取)
|
|
|
+ Object[] args = joinPoint.getArgs();
|
|
|
+ if (args != null && args.length > 0) {
|
|
|
+ for (Object arg : args) {
|
|
|
+ if (arg != null && !(arg instanceof HttpServletRequest) && !(arg instanceof jakarta.servlet.http.HttpServletResponse)) {
|
|
|
+ apiLog.setRequestBody(objectMapper.writeValueAsString(arg));
|
|
|
+
|
|
|
+ // 尝试提取业务字段
|
|
|
+ extractBusinessFields(arg, apiLog);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行目标方法
|
|
|
+ Object result = joinPoint.proceed();
|
|
|
+
|
|
|
+ // 记录响应
|
|
|
+ long endTime = System.currentTimeMillis();
|
|
|
+ apiLog.setResponseTime(endTime - startTime);
|
|
|
+ apiLog.setResponseStatus(200);
|
|
|
+ apiLog.setIsSuccess(1);
|
|
|
+
|
|
|
+ if (result != null) {
|
|
|
+ apiLog.setResponseBody(objectMapper.writeValueAsString(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 异步保存日志
|
|
|
+ thirdPartyApiLogService.saveLogAsync(apiLog);
|
|
|
+
|
|
|
+ return result;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 记录异常
|
|
|
+ long endTime = System.currentTimeMillis();
|
|
|
+ apiLog.setResponseTime(endTime - startTime);
|
|
|
+ apiLog.setResponseStatus(500);
|
|
|
+ apiLog.setIsSuccess(0);
|
|
|
+ apiLog.setErrorMessage(e.getMessage());
|
|
|
+
|
|
|
+ // 异步保存日志
|
|
|
+ thirdPartyApiLogService.saveLogAsync(apiLog);
|
|
|
+
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提取业务字段
|
|
|
+ */
|
|
|
+ private void extractBusinessFields(Object requestBody, ThirdPartyApiLog apiLog) {
|
|
|
+ try {
|
|
|
+ String json = objectMapper.writeValueAsString(requestBody);
|
|
|
+ Map<String, Object> map = objectMapper.readValue(json, Map.class);
|
|
|
+
|
|
|
+ // 提取运营商ID
|
|
|
+ if (map.containsKey("OperatorID")) {
|
|
|
+ apiLog.setOperatorId(String.valueOf(map.get("OperatorID")));
|
|
|
+ } else if (map.containsKey("operatorID")) {
|
|
|
+ apiLog.setOperatorId(String.valueOf(map.get("operatorID")));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取充电站ID
|
|
|
+ if (map.containsKey("StationID")) {
|
|
|
+ apiLog.setStationId(String.valueOf(map.get("StationID")));
|
|
|
+ } else if (map.containsKey("stationID")) {
|
|
|
+ apiLog.setStationId(String.valueOf(map.get("stationID")));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取充电桩ID
|
|
|
+ if (map.containsKey("ConnectorID")) {
|
|
|
+ apiLog.setConnectorId(String.valueOf(map.get("ConnectorID")));
|
|
|
+ } else if (map.containsKey("connectorID")) {
|
|
|
+ apiLog.setConnectorId(String.valueOf(map.get("connectorID")));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取业务流水号
|
|
|
+ if (map.containsKey("StartChargeSeq")) {
|
|
|
+ apiLog.setBusinessSeq(String.valueOf(map.get("StartChargeSeq")));
|
|
|
+ } else if (map.containsKey("startChargeSeq")) {
|
|
|
+ apiLog.setBusinessSeq(String.valueOf(map.get("startChargeSeq")));
|
|
|
+ } else if (map.containsKey("EquipAuthSeq")) {
|
|
|
+ apiLog.setBusinessSeq(String.valueOf(map.get("EquipAuthSeq")));
|
|
|
+ } else if (map.containsKey("equipAuthSeq")) {
|
|
|
+ apiLog.setBusinessSeq(String.valueOf(map.get("equipAuthSeq")));
|
|
|
+ } else if (map.containsKey("EquipBizSeq")) {
|
|
|
+ apiLog.setBusinessSeq(String.valueOf(map.get("EquipBizSeq")));
|
|
|
+ } else if (map.containsKey("equipBizSeq")) {
|
|
|
+ apiLog.setBusinessSeq(String.valueOf(map.get("equipBizSeq")));
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.debug("提取业务字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取请求头信息
|
|
|
+ */
|
|
|
+ private String getRequestHeaders(HttpServletRequest request) {
|
|
|
+ try {
|
|
|
+ Map<String, String> headers = new HashMap<>();
|
|
|
+ Enumeration<String> headerNames = request.getHeaderNames();
|
|
|
+ while (headerNames.hasMoreElements()) {
|
|
|
+ String headerName = headerNames.nextElement();
|
|
|
+ headers.put(headerName, request.getHeader(headerName));
|
|
|
+ }
|
|
|
+ return objectMapper.writeValueAsString(headers);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("获取请求头失败: {}", e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取客户端真实IP
|
|
|
+ */
|
|
|
+ private String getClientIp(HttpServletRequest request) {
|
|
|
+ String ip = request.getHeader("X-Forwarded-For");
|
|
|
+ if (StrUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
|
|
+ // 多次反向代理后会有多个IP值,第一个为真实IP
|
|
|
+ int index = ip.indexOf(',');
|
|
|
+ if (index != -1) {
|
|
|
+ return ip.substring(0, index);
|
|
|
+ }
|
|
|
+ return ip;
|
|
|
+ }
|
|
|
+
|
|
|
+ ip = request.getHeader("X-Real-IP");
|
|
|
+ if (StrUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
|
|
+ return ip;
|
|
|
+ }
|
|
|
+
|
|
|
+ return request.getRemoteAddr();
|
|
|
+ }
|
|
|
+}
|