|
|
|
|
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.ding.utils.SecurityUtils;
|
|
|
|
|
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.apache.commons.lang3.StringUtils;
|
|
|
|
|
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.aspectj.lang.reflect.MethodSignature;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
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
|
|
|
|
|
String userId = SecurityUtils.getUserId();
|
|
|
|
|
if (StringUtils.isNotBlank(userId)) {
|
|
|
|
|
operationLogDTO.setCreatorId(userId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 收集方法和注解信息
|
|
|
|
|
*/
|
|
|
|
|
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<LoginDTO>) businessResult).getData();
|
|
|
|
|
if (data != null && data.getToken() != null) {
|
|
|
|
|
String userId = jwtUtils.parseToken(data.getToken()).getSubject();
|
|
|
|
|
operationLogDTO.setCreatorId(userId);
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("解析登录Token获取用户ID失败", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 异步保存操作日志
|
|
|
|
|
*/
|
|
|
|
|
private void saveOperationLogAsync(OperationLogDTO operationLogDTO) {
|
|
|
|
|
try {
|
|
|
|
|
// 调用异步保存方法
|
|
|
|
|
operationLogApplicationService.saveOperationLogAsync(operationLogDTO);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("提交异步保存日志任务失败", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|