|
@@ -0,0 +1,306 @@
|
|
|
+package com.qmth.sop.business.aspect;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.qmth.boot.api.exception.ApiException;
|
|
|
+import com.qmth.sop.business.annotation.EditKey;
|
|
|
+import com.qmth.sop.business.annotation.OperationLog;
|
|
|
+import com.qmth.sop.business.bean.dto.LogArgsDto;
|
|
|
+import com.qmth.sop.business.entity.SysLog;
|
|
|
+import com.qmth.sop.business.entity.SysPrivilege;
|
|
|
+import com.qmth.sop.business.entity.SysRole;
|
|
|
+import com.qmth.sop.business.entity.SysUser;
|
|
|
+import com.qmth.sop.business.service.SysLogService;
|
|
|
+import com.qmth.sop.business.service.SysPrivilegeService;
|
|
|
+import com.qmth.sop.business.service.SysUserRoleService;
|
|
|
+import com.qmth.sop.common.contant.SystemConstant;
|
|
|
+import com.qmth.sop.common.enums.LogTypeEnum;
|
|
|
+import com.qmth.sop.common.enums.PrivilegeEnum;
|
|
|
+import com.qmth.sop.common.util.ResultUtil;
|
|
|
+import com.qmth.sop.common.util.ServletUtil;
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
+import org.aspectj.lang.JoinPoint;
|
|
|
+import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
+import org.aspectj.lang.annotation.*;
|
|
|
+import org.aspectj.lang.reflect.MethodSignature;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.validation.BeanPropertyBindingResult;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @Description: 日志操作记录拦截aop
|
|
|
+ * @Author: CaoZixuan
|
|
|
+ * @Date: 2023-08-03
|
|
|
+ */
|
|
|
+@Aspect
|
|
|
+@Component
|
|
|
+public class LogAspect {
|
|
|
+ @Resource
|
|
|
+ private SysLogService sysLogService;
|
|
|
+ @Resource
|
|
|
+ private SysPrivilegeService sysPrivilegeService;
|
|
|
+ @Resource
|
|
|
+ private SysUserRoleService sysUserRoleService;
|
|
|
+
|
|
|
+ private final static Logger log = LoggerFactory.getLogger(LogAspect.class);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 日志注解切入点
|
|
|
+ */
|
|
|
+ @Pointcut("@annotation(com.qmth.sop.business.annotation.OperationLog)")
|
|
|
+ public void operationLog() {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 环绕增强,操作日志环绕切入
|
|
|
+ *
|
|
|
+ * @param joinPoint joinPoint
|
|
|
+ * @return return
|
|
|
+ * @throws Throwable Throwable
|
|
|
+ */
|
|
|
+ @Around(value = "operationLog()")
|
|
|
+ public Object aroundOperationLogPoint(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
|
+ Object res = null;
|
|
|
+ long beginTime = System.currentTimeMillis();
|
|
|
+ long endTime = 0;
|
|
|
+ String runStatus = "成功";
|
|
|
+ try {
|
|
|
+ res = joinPoint.proceed();
|
|
|
+ endTime = System.currentTimeMillis();
|
|
|
+ return res;
|
|
|
+ } catch (Exception e) {
|
|
|
+ endTime = System.currentTimeMillis();
|
|
|
+ res = "Exception: " + e.getMessage();
|
|
|
+ runStatus = "失败";
|
|
|
+ log.error(SystemConstant.LOG_ERROR, e);
|
|
|
+ if (e instanceof ApiException) {
|
|
|
+ return ResultUtil.error((ApiException) e, e.getMessage());
|
|
|
+ } else {
|
|
|
+ return ResultUtil.error(e.getMessage());
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ try {
|
|
|
+ //方法执行完成后增加日志
|
|
|
+ addOperationLog(joinPoint, res, endTime - beginTime, runStatus);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("LogAspect 操作失败:" + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录日志操作
|
|
|
+ *
|
|
|
+ * @param joinPoint joinPoint
|
|
|
+ * @param res 返回值
|
|
|
+ * @param time 方法执行时间
|
|
|
+ * @param runStatus 方法执行状态
|
|
|
+ */
|
|
|
+ private void addOperationLog(JoinPoint joinPoint, Object res, long time, String runStatus) throws IllegalAccessException {
|
|
|
+ SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
|
|
|
+ String userName = requestUser.getLoginName();
|
|
|
+ HttpServletRequest request = ServletUtil.getRequest();
|
|
|
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
|
|
+ Long privilegeId = ServletUtil.getCurrentPrivilegeId();
|
|
|
+ Object[] args = joinPoint.getArgs();
|
|
|
+ String[] paramsName = signature.getParameterNames();
|
|
|
+
|
|
|
+ SysLog sysLog = new SysLog();
|
|
|
+
|
|
|
+ // 方法中的信息
|
|
|
+ sysLog.setId(SystemConstant.getDbUuid());
|
|
|
+
|
|
|
+ sysLog.setUrl(request.getServletPath());
|
|
|
+ sysLog.setArgs(this.getTranslateArgs(paramsName, args));
|
|
|
+ sysLog.setRunTime(time);
|
|
|
+ sysLog.setRunStatus(runStatus);
|
|
|
+ sysLog.setUserId(requestUser.getId());
|
|
|
+ sysLog.setUserName(userName);
|
|
|
+ sysLog.setCreateTime(System.currentTimeMillis());
|
|
|
+
|
|
|
+ // 注解中的信息
|
|
|
+ OperationLog annotation = signature.getMethod().getAnnotation(OperationLog.class);
|
|
|
+ if (annotation != null) {
|
|
|
+ List<SysRole> sysRoleList = sysUserRoleService.listRoleByUserId(requestUser.getId());
|
|
|
+ String detail = annotation.detail();
|
|
|
+ if (detail != null && detail.length() > 0) {
|
|
|
+ detail = getDetail(((MethodSignature) joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs(), annotation);
|
|
|
+ } else {
|
|
|
+ detail = autoCreateOperationLogDetailByUrlAndPrivilegeId(request.getServletPath(), privilegeId);
|
|
|
+ }
|
|
|
+ if (CollectionUtils.isNotEmpty(sysRoleList)) {
|
|
|
+ String roleNames = sysRoleList.stream().map(SysRole::getName).collect(Collectors.joining(","));
|
|
|
+ detail = "[" + roleNames + "] " + detail;
|
|
|
+ }
|
|
|
+ sysLog.setContent(detail);
|
|
|
+
|
|
|
+ LogTypeEnum logType = annotation.logType();
|
|
|
+ if (LogTypeEnum.EDIT.equals(logType)) {
|
|
|
+ logType = this.getAddOrUpdateByParam(args);
|
|
|
+ }
|
|
|
+
|
|
|
+ sysLog.setType(logType);
|
|
|
+ }
|
|
|
+ // 保存操作日志
|
|
|
+ sysLogService.save(sysLog);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getTranslateArgs(String[] paramsName, Object[] args) {
|
|
|
+ List<LogArgsDto> logArgsDtoList = new ArrayList<>();
|
|
|
+ for (int i = 0; i < args.length; i++) {
|
|
|
+ if (Objects.nonNull(args[i]) && (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse || args[i] instanceof CommonsMultipartFile || args[i] instanceof MultipartFile || args[i] instanceof BeanPropertyBindingResult)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ LogArgsDto logArgsDto = new LogArgsDto();
|
|
|
+ logArgsDto.setArg(args[i]);
|
|
|
+ logArgsDto.setParamName(paramsName[i]);
|
|
|
+ logArgsDtoList.add(logArgsDto);
|
|
|
+ }
|
|
|
+ return JSON.toJSONString(logArgsDtoList);
|
|
|
+ }
|
|
|
+
|
|
|
+ private LogTypeEnum getAddOrUpdateByParam(Object[] args) throws IllegalAccessException {
|
|
|
+ for (Object arg : args) {
|
|
|
+ if (Objects.nonNull(arg) && (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse || arg instanceof MultipartFile || arg instanceof BeanPropertyBindingResult)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Class<?> clazz = arg.getClass();
|
|
|
+ Field[] fields = clazz.getDeclaredFields();
|
|
|
+ for (Field field : fields) {
|
|
|
+ EditKey editKey = field.getAnnotation(EditKey.class);
|
|
|
+ if (editKey != null) {
|
|
|
+ // 捕捉到编辑对象的id
|
|
|
+ field.setAccessible(true);
|
|
|
+ Object obj = field.get(arg);
|
|
|
+ if (Objects.nonNull(obj)) {
|
|
|
+ return LogTypeEnum.UPDATE;
|
|
|
+ } else {
|
|
|
+ return LogTypeEnum.ADD;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Class<?> supperClazz = clazz.getSuperclass();
|
|
|
+ if (Objects.nonNull(supperClazz)) {
|
|
|
+ Field[] supperFields = supperClazz.getDeclaredFields();
|
|
|
+ for (Field field : supperFields) {
|
|
|
+ EditKey editKey = field.getAnnotation(EditKey.class);
|
|
|
+ if (editKey != null) {
|
|
|
+ // 捕捉到编辑对象的id
|
|
|
+ field.setAccessible(true);
|
|
|
+ Object obj = field.get(arg);
|
|
|
+ if (Objects.nonNull(obj)) {
|
|
|
+ return LogTypeEnum.UPDATE;
|
|
|
+ } else {
|
|
|
+ return LogTypeEnum.ADD;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return LogTypeEnum.EDIT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对当前登录用户和占位符处理
|
|
|
+ *
|
|
|
+ * @param argNames 方法参数名称数组
|
|
|
+ * @param args 方法参数数组
|
|
|
+ * @param annotation 注解信息
|
|
|
+ * @return 返回处理后的描述
|
|
|
+ */
|
|
|
+ private String getDetail(String[] argNames, Object[] args, OperationLog annotation) {
|
|
|
+ SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
|
|
|
+
|
|
|
+
|
|
|
+ Map<Object, Object> map = new HashMap<>();
|
|
|
+ for (int i = 0; i < argNames.length; i++) {
|
|
|
+ if (Objects.nonNull(args[i]) && (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse || args[i] instanceof CommonsMultipartFile || args[i] instanceof MultipartFile || args[i] instanceof BeanPropertyBindingResult)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ map.put(argNames[i], args[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ String detail = annotation.detail();
|
|
|
+ try {
|
|
|
+ detail = "'" + requestUser.getRealName() + "'=》" + annotation.detail();
|
|
|
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
|
|
|
+ Object k = entry.getKey();
|
|
|
+ Object v = entry.getValue();
|
|
|
+ detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return detail;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据权限url和所在菜单的权限id自动创建操作日志详情
|
|
|
+ *
|
|
|
+ * @param url url
|
|
|
+ * @param privilegeId 所属菜单的权限id
|
|
|
+ * @return 操作日志详情
|
|
|
+ */
|
|
|
+ private String autoCreateOperationLogDetailByUrlAndPrivilegeId(String url, Long privilegeId) {
|
|
|
+ String result = "";
|
|
|
+ SysPrivilege menu = sysPrivilegeService.getById(privilegeId);
|
|
|
+ SysPrivilege urlP = sysPrivilegeService.getOne(new QueryWrapper<SysPrivilege>()
|
|
|
+ .lambda()
|
|
|
+ .select(SysPrivilege::getName)
|
|
|
+ .eq(SysPrivilege::getUrl, url)
|
|
|
+ .eq(SysPrivilege::getType, PrivilegeEnum.URL));
|
|
|
+ if (Objects.nonNull(menu) && Objects.nonNull(urlP)) {
|
|
|
+ result = result + menu.getName() + SystemConstant.CATALOG_LINK + urlP.getName();
|
|
|
+ } else if (Objects.nonNull(menu)) {
|
|
|
+ result = result + menu.getName();
|
|
|
+ } else if (Objects.nonNull(urlP)) {
|
|
|
+ result = result + urlP.getName();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Before(value = "operationLog()")
|
|
|
+ public void doBeforeAdvice(JoinPoint joinPoint) {
|
|
|
+ log.info("进入方法前执行.....");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理完请求,返回内容
|
|
|
+ *
|
|
|
+ * @param ret ret
|
|
|
+ */
|
|
|
+ @AfterReturning(returning = "ret", pointcut = "operationLog()")
|
|
|
+ public void doAfterReturning(Object ret) {
|
|
|
+ log.info("方法的返回值 : " + ret);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 后置异常通知
|
|
|
+ */
|
|
|
+ @AfterThrowing(value = "operationLog()")
|
|
|
+ public void throwss(JoinPoint jp) {
|
|
|
+ log.info("方法异常时执行.....");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
|
|
|
+ */
|
|
|
+ @After(value = "operationLog()")
|
|
|
+ public void after(JoinPoint jp) {
|
|
|
+ log.info("方法最后执行.....");
|
|
|
+ }
|
|
|
+}
|