package com.project.operation.aop; import com.project.base.domain.result.Result; import com.project.ding.domain.dto.LoginDTO; import com.project.ding.utils.JwtUtils; import com.project.operation.annotation.OperationLog; import com.project.operation.application.impl.OperationLogApplicationService; import com.project.operation.domain.dto.OperationLogDTO; import com.project.operation.domain.enums.ModuleEnum; import com.project.operation.domain.service.impl.description.DescriptionManager; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.lang.reflect.Method; /** * 操作日志AOP切面 */ @Slf4j @Aspect @Component @RequiredArgsConstructor public class OperationLogAspect { @Autowired private JwtUtils jwtUtils; @Autowired private OperationLogApplicationService operationLogApplicationService; @Autowired private DescriptionManager descriptionManager; // 定义切点,拦截所有添加@OperationLog注解的方法 @Pointcut("@annotation(com.project.operation.annotation.OperationLog)") public void pointcutOperationLog() {} // 环绕通知,在方法执行前后拦截,收集日志(核心改造) @Around("pointcutOperationLog()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { //初始化日志DTO OperationLogDTO operationLogDTO = new OperationLogDTO(); long startTime = System.currentTimeMillis(); Object businessResult = null; try { //收集请求上下文(IP、用户ID等) collectRequestContext(operationLogDTO); //收集方法和注解信息 collectMethodAndAnnotationInfo(joinPoint, operationLogDTO); //执行业务方法 businessResult = joinPoint.proceed(); //获取用户ID handleLoginSceneUserId(operationLogDTO, joinPoint, businessResult); operationLogDTO.setResult(0); } catch (Exception e) { operationLogDTO.setResult(1); operationLogDTO.setException(e.getMessage()); throw e; } finally { operationLogDTO.setCostTime(System.currentTimeMillis() - startTime); saveOperationLogAsync(operationLogDTO); } return businessResult; } /** * 收集请求上下文(IP、用户ID等) */ private void collectRequestContext(OperationLogDTO operationLogDTO) { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (requestAttributes == null) { return; } HttpServletRequest request = requestAttributes.getRequest(); // 收集客户端IP operationLogDTO.setClientIp(request.getRemoteAddr()); // 收集登录用户ID Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() != null) { Object principal = authentication.getPrincipal(); try { Long userId = Long.parseLong(principal.toString()); operationLogDTO.setCreatorId(userId); } catch (NumberFormatException e) { log.warn("解析用户ID失败,principal:{}", principal, e); } } } /** * 收集方法和注解信息 */ private void collectMethodAndAnnotationInfo(ProceedingJoinPoint joinPoint, OperationLogDTO operationLogDTO) throws Exception { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 方法全路径:包名+类名+方法名 operationLogDTO.setMethod(method.getDeclaringClass().getName() + "." + method.getName()); // 获取注解信息 OperationLog annotation = method.getAnnotation(OperationLog.class); operationLogDTO.setModule(annotation.module()); ModuleEnum moduleEnum = ModuleEnum.findByValue(annotation.module()); if (moduleEnum != null) { descriptionManager.process(moduleEnum.name(), joinPoint.getArgs(), operationLogDTO, method.getName()); } else { operationLogDTO.setAction(annotation.action()); operationLogDTO.setDescription(annotation.description()); } } /** * 处理登录场景的用户ID(从JWT Token解析) */ private void handleLoginSceneUserId(OperationLogDTO operationLogDTO, ProceedingJoinPoint joinPoint, Object businessResult) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String methodFullName = signature.getMethod().getDeclaringClass().getName() + "." + signature.getMethod().getName(); // 仅登录方法处理JWT解析 if (methodFullName.contains("login") && businessResult instanceof Result) { try { LoginDTO data = ((Result) businessResult).getData(); if (data != null && data.getToken() != null) { String userId = jwtUtils.parseToken(data.getToken()).getSubject(); operationLogDTO.setCreatorId(Long.parseLong(userId)); } } catch (Exception e) { log.error("解析登录Token获取用户ID失败", e); } } } /** * 异步保存操作日志 */ private void saveOperationLogAsync(OperationLogDTO operationLogDTO) { try { // 调用异步保存方法 operationLogApplicationService.saveOperationLogAsync(operationLogDTO); } catch (Exception e) { log.error("提交异步保存日志任务失败", e); } } }