package com.zsElectric.boot.charging.aspect; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.zsElectric.boot.charging.entity.ThirdPartyApiLog; import com.zsElectric.boot.charging.service.ThirdPartyApiLogService; import com.zsElectric.boot.common.util.electric.ChargingUtil; import com.zsElectric.boot.common.util.electric.RequestParmsEntity; import com.zsElectric.boot.common.util.electric.ResponseParmsEntity; 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.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * 第三方接口日志记录切面 * 拦截 LinkDataController 和 ChargingBusinessController 的所有方法,自动记录请求和响应日志 * * @author system * @since 2025-01-02 */ @Slf4j @Aspect @Component @RequiredArgsConstructor public class ThirdPartyApiLogAspect { private final ThirdPartyApiLogService thirdPartyApiLogService; private final ObjectMapper objectMapper; private final ChargingUtil chargingUtil; /** 需要保留的关键请求头 */ private static final Set IMPORTANT_HEADERS = new HashSet<>(Arrays.asList( "content-type", "authorization", "content-length" )); /** * 定义切入点: 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); LocalDateTime now = LocalDateTime.now(); apiLog.setCreatedTime(now); apiLog.setUpdatedTime(now); if (request != null) { // 请求信息 apiLog.setRequestMethod(request.getMethod()); apiLog.setRequestUrl(request.getRequestURL().toString()); apiLog.setInterfaceName(request.getRequestURI()); apiLog.setInterfaceDescription(getInterfaceDescription(request.getRequestURI())); apiLog.setClientIp(getClientIp(request)); apiLog.setUserAgent(request.getHeader("User-Agent")); // 请求头(只保留关键信息) apiLog.setRequestHeaders(getSimplifiedHeaders(request)); // 请求参数 Map parameterMap = request.getParameterMap(); if (!parameterMap.isEmpty()) { // 简化参数格式,单值的数组转为简单字符串 Map simplifiedParams = new HashMap<>(); for (Map.Entry entry : parameterMap.entrySet()) { String[] values = entry.getValue(); if (values != null && values.length == 1) { simplifiedParams.put(entry.getKey(), values[0]); } else { simplifiedParams.put(entry.getKey(), values); } } apiLog.setRequestParams(objectMapper.writeValueAsString(simplifiedParams)); } // 请求体(从切点参数中获取) 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)) { // 序列化时忽略null值 ObjectMapper cleanMapper = objectMapper.copy(); cleanMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); apiLog.setRequestBody(cleanMapper.writeValueAsString(arg)); // 尝试提取业务字段 extractBusinessFields(arg, apiLog); // 尝试解密请求数据(针对 RequestParmsEntity 类型) decryptRequestData(arg, apiLog); break; } } } } // 执行目标方法 Object result = joinPoint.proceed(); // 记录响应 long endTime = System.currentTimeMillis(); apiLog.setResponseTime(endTime - startTime); apiLog.setResponseStatus(200); apiLog.setIsSuccess(1); if (result != null) { // 序列化时忽略null值 ObjectMapper cleanMapper = objectMapper.copy(); cleanMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); apiLog.setResponseBody(cleanMapper.writeValueAsString(result)); // 解密响应数据(针对 ResponseParmsEntity 类型) decryptResponseData(result, apiLog); } // 异步保存日志 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; } } /** * 解密请求数据 * 针对 LinkDataController 接收的 RequestParmsEntity 类型参数进行解密 */ private void decryptRequestData(Object requestBody, ThirdPartyApiLog apiLog) { try { if (requestBody instanceof RequestParmsEntity) { RequestParmsEntity requestParms = (RequestParmsEntity) requestBody; String encryptedData = requestParms.getData(); if (StrUtil.isNotBlank(encryptedData)) { // 使用 ChargingUtil 解密(与业务代码保持一致) String decryptedData = chargingUtil.decryptData(encryptedData); apiLog.setDecryptedRequestData(decryptedData); log.debug("请求数据解密成功: {}", decryptedData); } } } catch (Exception e) { log.warn("解密请求数据失败: {}", e.getMessage()); } } /** * 解密响应数据 * 针对返回的 ResponseParmsEntity 类型响应进行解密 */ private void decryptResponseData(Object responseBody, ThirdPartyApiLog apiLog) { try { if (responseBody instanceof ResponseParmsEntity) { ResponseParmsEntity responseParms = (ResponseParmsEntity) responseBody; String encryptedData = responseParms.getData(); if (StrUtil.isNotBlank(encryptedData)) { // 使用 ChargingUtil 解密(与业务代码保持一致) String decryptedData = chargingUtil.decryptData(encryptedData); apiLog.setDecryptedResponseData(decryptedData); log.debug("响应数据解密成功: {}", decryptedData); } } } catch (Exception e) { log.warn("解密响应数据失败: {}", e.getMessage()); } } /** * 提取业务字段 */ private void extractBusinessFields(Object requestBody, ThirdPartyApiLog apiLog) { try { String json = objectMapper.writeValueAsString(requestBody); Map 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 getSimplifiedHeaders(HttpServletRequest request) { try { Map headers = new HashMap<>(); for (String headerName : IMPORTANT_HEADERS) { String headerValue = request.getHeader(headerName); if (StrUtil.isNotBlank(headerValue)) { // 对于 authorization 只保留类型,不记录完整 token if ("authorization".equalsIgnoreCase(headerName) && headerValue.length() > 20) { headers.put(headerName, headerValue.substring(0, 20) + "..."); } else { headers.put(headerName, headerValue); } } } return headers.isEmpty() ? null : objectMapper.writeValueAsString(headers); } catch (Exception e) { log.error("获取请求头失败: {}", e.getMessage()); return null; } } /** * 获取接口中文说明 * 根据接口路径返回对应的中文说明 */ private String getInterfaceDescription(String uri) { if (StrUtil.isBlank(uri)) { return null; } // LinkDataController 接口说明 if (uri.contains("/query_token")) { return "获取token"; } else if (uri.contains("/notification_start_charge_result")) { return "推送启动充电结果"; } else if (uri.contains("/notification_equip_charge_status")) { return "推送充电状态"; } else if (uri.contains("/notification_stop_charge_result")) { return "推送停止充电结果"; } else if (uri.contains("/notification_charge_order_info")) { return "推送充电订单信息"; } else if (uri.contains("/notification_stationStatus")) { return "设备状态变化推送"; } // ChargingBusinessController 接口说明 else if (uri.contains("/queryEquipBusinessPolicy")) { return "查询业务策略信息"; } else if (uri.contains("/queryEquipAuth")) { return "请求设备认证"; } else if (uri.contains("/queryStationsInfo")) { return "查询充电站信息"; } else if (uri.contains("/queryStationStatus")) { return "设备接口状态查询"; } else if (uri.contains("/startCharging")) { return "请求启动充电"; } else if (uri.contains("/queryChargingStatus")) { return "查询充电状态"; } else if (uri.contains("/stopCharging")) { return "请求停止充电"; } 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(); } }