diff --git a/pom.xml b/pom.xml
index 01c8d42b..20ede4f7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,8 @@
1.2.4
3.4.1
3.13.6
+
+ 8.3.0
1.16.14
1.4.1.Final
@@ -88,20 +90,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -129,6 +117,11 @@
true
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
org.springframework.boot
@@ -174,6 +167,13 @@
${redisson.version}
+
+
+ org.apache.skywalking
+ apm-toolkit-trace
+ ${skywalking.version}
+
+
org.projectlombok
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
index 27d2ecf1..2c58eb9b 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
@@ -116,58 +116,6 @@ public class LogAspect {
}
}
- /**
- * 获取注解中对方法的描述信息 用于Controller层注解
- *
- * @param log 日志
- * @param operLog 操作日志
- * @throws Exception
- */
- public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception {
- // 设置action动作
- operLog.setBusinessType(log.businessType().ordinal());
- // 设置标题
- operLog.setTitle(log.title());
- // 设置操作人类别
- operLog.setOperatorType(log.operatorType().ordinal());
- // 是否需要保存request,参数和值
- if (log.isSaveRequestData()) {
- // 获取参数的信息,传入到数据库中。
- setRequestValue(joinPoint, operLog);
- }
- }
-
- /**
- * 获取请求的参数,放到log中
- *
- * @param operLog 操作日志
- * @throws Exception 异常
- */
- private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {
- String requestMethod = operLog.getRequestMethod();
- if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
- String params = argsArrayToString(joinPoint.getArgs());
- operLog.setOperParam(StringUtils.substring(params, 0, 2000));
- } else {
- Map, ?> paramsMap = (Map, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
- operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
- }
- }
-
- /**
- * 是否存在注解,如果存在就获取
- */
- private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
- Signature signature = joinPoint.getSignature();
- MethodSignature methodSignature = (MethodSignature) signature;
- Method method = methodSignature.getMethod();
-
- if (method != null) {
- return method.getAnnotation(Log.class);
- }
- return null;
- }
-
/**
* 参数拼装
*/
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java
deleted file mode 100644
index c74d629e..00000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.ruoyi.framework.manager;
-
-import java.util.TimerTask;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import com.ruoyi.common.utils.Threads;
-import com.ruoyi.common.utils.spring.SpringUtils;
-
-/**
- * 异步任务管理器
- *
- * @author ruoyi
- */
-public class AsyncManager {
- /**
- * 操作延迟10毫秒
- */
- private final int OPERATE_DELAY_TIME = 10;
-
- /**
- * 异步操作任务调度线程池
- */
- private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
-
- /**
- * 单例模式
- */
- private AsyncManager() {
- }
-
- private static AsyncManager me = new AsyncManager();
-
- public static AsyncManager me() {
- return me;
- }
-
- /**
- * 执行任务
- *
- * @param task 任务
- */
- public void execute(TimerTask task) {
- executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
- }
-
- /**
- * 停止任务线程池
- */
- public void shutdown() {
- Threads.shutdownAndAwaitTermination(executor);
- }
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java
deleted file mode 100644
index 7e5901ec..00000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.ruoyi.framework.manager;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.PreDestroy;
-
-/**
- * 确保应用退出时能关闭后台线程
- *
- * @author ruoyi
- */
-@Component
-public class ShutdownManager {
- private static final Logger logger = LoggerFactory.getLogger("sys-user");
-
- @PreDestroy
- public void destroy() {
- shutdownAsyncManager();
- }
-
- /**
- * 停止异步执行任务
- */
- private void shutdownAsyncManager() {
- try {
- logger.info("====关闭后台任务任务线程池====");
- AsyncManager.me().shutdown();
- } catch (Exception e) {
- logger.error(e.getMessage(), e);
- }
- }
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
deleted file mode 100644
index f13b50d5..00000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.ruoyi.framework.manager.factory;
-
-import java.util.TimerTask;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.utils.LogUtils;
-import com.ruoyi.common.utils.ServletUtils;
-import com.ruoyi.common.utils.ip.AddressUtils;
-import com.ruoyi.common.utils.ip.IpUtils;
-import com.ruoyi.common.utils.spring.SpringUtils;
-import com.ruoyi.system.domain.SysLogininfor;
-import com.ruoyi.system.domain.SysOperLog;
-import com.ruoyi.system.service.ISysLogininforService;
-import com.ruoyi.system.service.ISysOperLogService;
-import eu.bitwalker.useragentutils.UserAgent;
-
-/**
- * 异步工厂(产生任务用)
- *
- * @author ruoyi
- */
-public class AsyncFactory {
- private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
-
- /**
- * 记录登录信息
- *
- * @param username 用户名
- * @param status 状态
- * @param message 消息
- * @param args 列表
- * @return 任务task
- */
- public static TimerTask recordLogininfor(final String username, final String status, final String message,
- final Object... args) {
- final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
- final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
- return new TimerTask() {
- @Override
- public void run() {
- String address = AddressUtils.getRealAddressByIP(ip);
- StringBuilder s = new StringBuilder();
- s.append(LogUtils.getBlock(ip));
- s.append(address);
- s.append(LogUtils.getBlock(username));
- s.append(LogUtils.getBlock(status));
- s.append(LogUtils.getBlock(message));
- // 打印信息到日志
- sys_user_logger.info(s.toString(), args);
- // 获取客户端操作系统
- String os = userAgent.getOperatingSystem().getName();
- // 获取客户端浏览器
- String browser = userAgent.getBrowser().getName();
- // 封装对象
- SysLogininfor logininfor = new SysLogininfor();
- logininfor.setUserName(username);
- logininfor.setIpaddr(ip);
- logininfor.setLoginLocation(address);
- logininfor.setBrowser(browser);
- logininfor.setOs(os);
- logininfor.setMsg(message);
- // 日志状态
- if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) {
- logininfor.setStatus(Constants.SUCCESS);
- } else if (Constants.LOGIN_FAIL.equals(status)) {
- logininfor.setStatus(Constants.FAIL);
- }
- // 插入数据
- SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
- }
- };
- }
-
- /**
- * 操作日志记录
- *
- * @param operLog 操作日志信息
- * @return 任务task
- */
- public static TimerTask recordOper(final SysOperLog operLog) {
- return new TimerTask() {
- @Override
- public void run() {
- // 远程查询操作地点
- operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
- SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
- }
- };
- }
-}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/async/config/AsyncConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/async/config/AsyncConfiguration.java
new file mode 100644
index 00000000..b2c126c2
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/async/config/AsyncConfiguration.java
@@ -0,0 +1,9 @@
+package cn.iocoder.dashboard.framework.async.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@Configuration
+@EnableAsync(proxyTargetClass = true)
+public class AsyncConfiguration {
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/async/package-info.java b/src/main/java/cn/iocoder/dashboard/framework/async/package-info.java
new file mode 100644
index 00000000..37535899
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/async/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 异步执行,基于 Spring @Async 实现
+ */
+package cn.iocoder.dashboard.framework.async;
diff --git a/src/main/java/cn/iocoder/dashboard/framework/async/《芋道 Spring Boot 异步任务入门》.md b/src/main/java/cn/iocoder/dashboard/framework/async/《芋道 Spring Boot 异步任务入门》.md
new file mode 100644
index 00000000..b2ef210b
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/async/《芋道 Spring Boot 异步任务入门》.md
@@ -0,0 +1 @@
+
diff --git a/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/config/OperateLogConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/config/OperateLogConfiguration.java
new file mode 100644
index 00000000..ff9a35a7
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/config/OperateLogConfiguration.java
@@ -0,0 +1,15 @@
+package cn.iocoder.dashboard.framework.logger.operatelog.config;
+
+import cn.iocoder.dashboard.framework.logger.operatelog.core.aop.OperateLogAspect;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OperateLogConfiguration {
+
+ @Bean
+ public OperateLogAspect operateLogAspect() {
+ return new OperateLogAspect();
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java
new file mode 100644
index 00000000..ce61c77a
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java
@@ -0,0 +1,52 @@
+package cn.iocoder.dashboard.framework.logger.operatelog.core.annotations;
+
+import cn.iocoder.dashboard.modules.system.enums.logger.SysOperateLogTypeEnum;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OperateLog {
+
+ // ========== 模块字段 ==========
+
+ /**
+ * 操作模块
+ *
+ * 为空时,会尝试读取 {@link Api#value()} 属性
+ */
+ String module() default "";
+ /**
+ * 操作名
+ *
+ * 为空时,会尝试读取 {@link ApiOperation#value()} 属性
+ */
+ String name() default "";
+ /**
+ * 操作分类
+ *
+ * 实际并不是数组,因为枚举不能设置 null 作为默认值
+ */
+ SysOperateLogTypeEnum[] type() default {};
+
+ // ========== 开关字段 ==========
+
+ /**
+ * 是否记录操作日志
+ */
+ boolean enable() default true;
+ /**
+ * 是否记录方法参数
+ */
+ boolean logArgs() default true;
+ /**
+ * 是否记录方法结果的数据
+ */
+ boolean logResultData() default true;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/aop/OperateLogAspect.java b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/aop/OperateLogAspect.java
new file mode 100644
index 00000000..97a2d68a
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/aop/OperateLogAspect.java
@@ -0,0 +1,320 @@
+package cn.iocoder.dashboard.framework.logger.operatelog.core.aop;
+
+import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.service.OperateLogFrameworkService;
+import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
+import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogCreateReqVO;
+import cn.iocoder.dashboard.modules.system.enums.logger.SysOperateLogTypeEnum;
+import cn.iocoder.dashboard.util.servlet.ServletUtils;
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Maps;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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.reflect.MethodSignature;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.IntStream;
+
+import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
+import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.SUCCESS;
+
+/**
+ * 拦截使用 @ApiOperation 注解,如果满足条件,则生成操作日志。
+ * 满足如下任一条件,则会进行记录:
+ * 1. 使用 @ApiOperation + 非 @GetMapping
+ * 2. 使用 @OperateLog 注解
+ *
+ * 但是,如果声明 @OperateLog 注解时,将 enable 属性设置为 false 时,强制不记录。
+ *
+ * 为什么考虑使用 @ApiOperation 记录呢?避免有小伙伴忘记添加 @OperateLog 注解
+ *
+ * @author 芋道源码
+ */
+@Aspect
+@Slf4j
+public class OperateLogAspect {
+
+ @Resource
+ private OperateLogFrameworkService operateLogFrameworkService;
+
+ @Around("@annotation(apiOperation)")
+ public Object around(ProceedingJoinPoint joinPoint, ApiOperation apiOperation) throws Throwable {
+ // 可能也添加了 @ApiOperation 注解
+ OperateLog operateLog = getMethodAnnotation(joinPoint, OperateLog.class);
+ return around0(joinPoint, operateLog, apiOperation);
+ }
+
+ @Around("!@annotation(io.swagger.annotations.ApiOperation) && @annotation(operateLog)") // 兼容处理,只添加 @OperateLog 注解的情况
+ public Object around(ProceedingJoinPoint joinPoint, OperateLog operateLog) throws Throwable {
+ return around0(joinPoint, operateLog, null);
+ }
+
+ private Object around0(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation) throws Throwable {
+ // 记录开始时间
+ Date startTime = new Date();
+ try {
+ // 执行原有方法
+ Object result = joinPoint.proceed();
+ // 记录正常执行时的操作日志
+ this.log(joinPoint, operateLog, apiOperation, startTime, result, null);
+ return result;
+ } catch (Throwable exception) {
+ this.log(joinPoint, operateLog, apiOperation, startTime, null, exception);
+ throw exception;
+ }
+ }
+
+ private void log(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation,
+ Date startTime, Object result, Throwable exception) {
+ try {
+ // 判断不记录的情况
+ if (!isLogEnable(joinPoint, operateLog)) {
+ return;
+ }
+ // 真正记录操作日志
+ this.log0(joinPoint, operateLog, apiOperation, startTime, result, exception);
+ } catch (Throwable ex) {
+ log.error("[log][记录操作日志时,发生异常,其中参数是 joinPoint({}) operateLog({}) apiOperation({}) result({}) exception({}) ]",
+ joinPoint, operateLog, apiOperation, result, exception, ex);
+ }
+ }
+
+ private void log0(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation,
+ Date startTime, Object result, Throwable exception) {
+ SysOperateLogCreateReqVO operateLogVO = new SysOperateLogCreateReqVO();
+ // 补全通用字段
+ operateLogVO.setTraceId(TracerUtils.getTraceId());
+ operateLogVO.setStartTime(startTime);
+ // 补充用户信息
+ fillUserFields(operateLogVO);
+ // 补全模块信息
+ fillModuleFields(operateLogVO, joinPoint, operateLog, apiOperation);
+ // 补全请求信息
+ fillRequestFields(operateLogVO);
+ // 补全方法信息
+ fillMethodFields(operateLogVO, joinPoint, operateLog, startTime, result, exception);
+
+ // 异步记录日志
+ operateLogFrameworkService.createOperateLogAsync(operateLogVO);
+ }
+
+ private static void fillUserFields(SysOperateLogCreateReqVO operateLogVO) {
+ operateLogVO.setUserId(SecurityUtils.getLoginUserId());
+ }
+
+ private static void fillModuleFields(SysOperateLogCreateReqVO operateLogVO,
+ ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation) {
+ // module 属性
+ if (operateLog != null) {
+ operateLogVO.setModule(operateLog.module());
+ }
+ if (StrUtil.isEmpty(operateLogVO.getModule())) {
+ Api api = getClassAnnotation(joinPoint, Api.class);
+ if (api != null) {
+ operateLogVO.setModule(Optional.of(api.value())
+ .orElse(ArrayUtil.isEmpty(api.tags()) ? api.tags()[0] : null));
+ }
+ }
+ // name 属性
+ if (operateLog != null) {
+ operateLogVO.setName(operateLog.name());
+ }
+ if (StrUtil.isEmpty(operateLogVO.getName()) && apiOperation != null) {
+ operateLogVO.setName(apiOperation.value());
+ }
+ // type 属性
+ if (operateLog != null && ArrayUtil.isNotEmpty(operateLog.type())) {
+ operateLogVO.setType(operateLog.type()[0].getType());
+ }
+ if (operateLogVO.getType() == null) {
+ RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint));
+ SysOperateLogTypeEnum operateLogType = convertOperateLogType(requestMethod);
+ operateLogVO.setType(operateLogType != null ? operateLogType.getType() : null);
+ }
+ }
+
+ private static void fillRequestFields(SysOperateLogCreateReqVO operateLogVO) {
+ // 获得 Request 对象
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ if (!(requestAttributes instanceof ServletRequestAttributes)) {
+ return;
+ }
+ HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
+ // 补全请求信息
+ operateLogVO.setRequestMethod(request.getMethod());
+ operateLogVO.setRequestUrl(request.getRequestURI());
+ operateLogVO.setUserIp(ServletUtil.getClientIP(request));
+ operateLogVO.setUserAgent(ServletUtils.getUserAgent(request));
+ }
+
+ private static void fillMethodFields(SysOperateLogCreateReqVO operateLogVO,
+ ProceedingJoinPoint joinPoint, OperateLog operateLog,
+ Date startTime, Object result, Throwable exception) {
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ operateLogVO.setJavaMethod(methodSignature.toString());
+ if (operateLog == null || operateLog.logArgs()) {
+ operateLogVO.setJavaMethodArgs(obtainMethodArgs(joinPoint));
+ }
+ if (operateLog == null || operateLog.logResultData()) {
+ operateLogVO.setResultData(obtainResultData(result));
+ }
+ operateLogVO.setDuration((int) (System.currentTimeMillis() - startTime.getTime()));
+ // (正常)处理 resultCode 和 resultMsg 字段
+ if (result != null) {
+ if (result instanceof CommonResult) {
+ CommonResult> commonResult = (CommonResult>) result;
+ operateLogVO.setResultCode(commonResult.getCode());
+ operateLogVO.setResultMsg(commonResult.getMsg());
+ } else {
+ operateLogVO.setResultCode(SUCCESS.getCode());
+ }
+ }
+ // (异常)处理 resultCode 和 resultMsg 字段
+ if (exception != null) {
+ operateLogVO.setResultCode(INTERNAL_SERVER_ERROR.getCode());
+ operateLogVO.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
+ }
+ }
+
+ private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) {
+ // 有 @OperateLog 注解的情况下
+ if (operateLog != null) {
+ return operateLog.enable();
+ }
+ // 没有 @ApiOperation 注解的情况下,只记录 POST、PUT、DELETE 的情况
+ return obtainFirstLogRequestMethod(obtainRequestMethod(joinPoint)) != null;
+ }
+
+ private static RequestMethod obtainFirstLogRequestMethod(RequestMethod[] requestMethods) {
+ if (ArrayUtil.isEmpty(requestMethods)) {
+ return null;
+ }
+ return Arrays.stream(requestMethods).filter(requestMethod ->
+ requestMethod == RequestMethod.POST
+ || requestMethod == RequestMethod.PUT
+ || requestMethod == RequestMethod.DELETE)
+ .findFirst().orElse(null);
+ }
+
+ private static RequestMethod obtainFirstMatchRequestMethod(RequestMethod[] requestMethods) {
+ if (ArrayUtil.isEmpty(requestMethods)) {
+ return null;
+ }
+ // 优先,匹配最优的 POST、PUT、DELETE
+ RequestMethod result = obtainFirstLogRequestMethod(requestMethods);
+ if (result != null) {
+ return result;
+ }
+ // 然后,匹配次优的 GET
+ result = Arrays.stream(requestMethods).filter(requestMethod -> requestMethod == RequestMethod.GET)
+ .findFirst().orElse(null);
+ if (result != null) {
+ return result;
+ }
+ // 兜底,获得第一个
+ return requestMethods[0];
+ }
+
+ private static SysOperateLogTypeEnum convertOperateLogType(RequestMethod requestMethod) {
+ if (requestMethod == null) {
+ return null;
+ }
+ switch (requestMethod) {
+ case GET:
+ return SysOperateLogTypeEnum.GET;
+ case POST:
+ return SysOperateLogTypeEnum.CREATE;
+ case PUT:
+ return SysOperateLogTypeEnum.UPDATE;
+ case DELETE:
+ return SysOperateLogTypeEnum.DELETE;
+ default:
+ return SysOperateLogTypeEnum.OTHER;
+ }
+ }
+
+ private static RequestMethod[] obtainRequestMethod(ProceedingJoinPoint joinPoint) {
+ RequestMapping requestMapping = AnnotationUtils.getAnnotation( // 使用 Spring 的工具类,可以处理 @RequestMapping 别名注解
+ ((MethodSignature) joinPoint.getSignature()).getMethod(), RequestMapping.class);
+ return requestMapping != null ? requestMapping.method() : new RequestMethod[]{};
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ private static T getMethodAnnotation(ProceedingJoinPoint joinPoint, Class annotationClass) {
+ return ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(annotationClass);
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ private static T getClassAnnotation(ProceedingJoinPoint joinPoint, Class annotationClass) {
+ return ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaringClass().getAnnotation(annotationClass);
+ }
+
+ private static Map obtainMethodArgs(ProceedingJoinPoint joinPoint) {
+ // TODO 提升:参数脱敏和忽略
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ String[] argNames = methodSignature.getParameterNames();
+ Object[] argValues = joinPoint.getArgs();
+ // 拼接参数
+ Map args = Maps.newHashMapWithExpectedSize(argValues.length);
+ for (int i = 0; i < argNames.length; i++) {
+ String argName = argNames[i];
+ Object argValue = argValues[i];
+ // 被忽略时,标记为 ignore 字符串,避免和 null 混在一起
+ args.put(argName, !isIgnoreArgs(argValue) ? argValue : "[ignore]");
+ }
+ return args;
+ }
+
+ private static String obtainResultData(Object result) {
+ // TODO 提升:结果脱敏和忽略
+ if (result instanceof CommonResult) {
+ result = ((CommonResult>) result).getData();
+ }
+ return JSON.toJSONString(result);
+ }
+
+ private static boolean isIgnoreArgs(Object object) {
+ Class> clazz = object.getClass();
+ // 处理数组的情况
+ if (clazz.isArray()) {
+ return IntStream.range(0, Array.getLength(object))
+ .anyMatch(index -> isIgnoreArgs(Array.get(object, index)));
+ }
+ // 递归,处理数组、Collection、Map 的情况
+ if (Collection.class.isAssignableFrom(clazz)) {
+ return ((Collection>) object).stream()
+ .anyMatch((Predicate