From 1cf3dd24403e930471dcc6be53436cf0ba6013e9 Mon Sep 17 00:00:00 2001
From: sy200 <2063514638@qq.com>
Date: Tue, 23 Jul 2024 16:20:30 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 46 ++++
pom.xml | 27 +++
.../com/muyu/common/log/annotation/Log.java | 46 ++++
.../com/muyu/common/log/aspect/LogAspect.java | 220 ++++++++++++++++++
.../muyu/common/log/enums/BusinessStatus.java | 18 ++
.../muyu/common/log/enums/BusinessType.java | 58 +++++
.../muyu/common/log/enums/OperatorType.java | 23 ++
.../log/filter/PropertyPreExcludeFilter.java | 20 ++
.../common/log/service/AsyncLogService.java | 27 +++
...ot.autoconfigure.AutoConfiguration.imports | 2 +
10 files changed, 487 insertions(+)
create mode 100644 .gitignore
create mode 100644 pom.xml
create mode 100644 src/main/java/com/muyu/common/log/annotation/Log.java
create mode 100644 src/main/java/com/muyu/common/log/aspect/LogAspect.java
create mode 100644 src/main/java/com/muyu/common/log/enums/BusinessStatus.java
create mode 100644 src/main/java/com/muyu/common/log/enums/BusinessType.java
create mode 100644 src/main/java/com/muyu/common/log/enums/OperatorType.java
create mode 100644 src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java
create mode 100644 src/main/java/com/muyu/common/log/service/AsyncLogService.java
create mode 100644 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..09bdfea
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,46 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### JRebel ###
+rebel.xml
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..27ab5a5
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ com.muyu
+ cloud-common
+ 3.6.3
+
+ 4.0.0
+
+ cloud-common-log
+
+
+ cloud-common-log日志记录
+
+
+
+
+
+
+ com.muyu
+ cloud-common-security
+
+
+
+
diff --git a/src/main/java/com/muyu/common/log/annotation/Log.java b/src/main/java/com/muyu/common/log/annotation/Log.java
new file mode 100644
index 0000000..ac6394e
--- /dev/null
+++ b/src/main/java/com/muyu/common/log/annotation/Log.java
@@ -0,0 +1,46 @@
+package com.muyu.common.log.annotation;
+
+import com.muyu.common.log.enums.BusinessType;
+import com.muyu.common.log.enums.OperatorType;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义操作日志记录注解
+ *
+ * @author muyu
+ */
+@Target({ElementType.PARAMETER, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Log {
+ /**
+ * 模块
+ */
+ public String title () default "";
+
+ /**
+ * 功能
+ */
+ public BusinessType businessType () default BusinessType.OTHER;
+
+ /**
+ * 操作人类别
+ */
+ public OperatorType operatorType () default OperatorType.MANAGE;
+
+ /**
+ * 是否保存请求的参数
+ */
+ public boolean isSaveRequestData () default true;
+
+ /**
+ * 是否保存响应的参数
+ */
+ public boolean isSaveResponseData () default true;
+
+ /**
+ * 排除指定的请求参数
+ */
+ public String[] excludeParamNames () default {};
+}
diff --git a/src/main/java/com/muyu/common/log/aspect/LogAspect.java b/src/main/java/com/muyu/common/log/aspect/LogAspect.java
new file mode 100644
index 0000000..f82ffab
--- /dev/null
+++ b/src/main/java/com/muyu/common/log/aspect/LogAspect.java
@@ -0,0 +1,220 @@
+package com.muyu.common.log.aspect;
+
+import com.alibaba.fastjson2.JSON;
+import com.muyu.common.core.utils.ServletUtils;
+import com.muyu.common.core.utils.StringUtils;
+import com.muyu.common.core.utils.ip.IpUtils;
+import com.muyu.common.log.annotation.Log;
+import com.muyu.common.log.enums.BusinessStatus;
+import com.muyu.common.log.filter.PropertyPreExcludeFilter;
+import com.muyu.common.log.service.AsyncLogService;
+import com.muyu.common.security.utils.SecurityUtils;
+import com.muyu.common.system.domain.SysOperLog;
+import org.apache.commons.lang3.ArrayUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * 操作日志记录处理
+ *
+ * @author muyu
+ */
+@Aspect
+@Component
+public class LogAspect {
+ /**
+ * 排除敏感属性字段
+ */
+ public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"};
+ private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
+ /**
+ * 计算操作消耗时间
+ */
+ private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time");
+
+ @Autowired
+ private AsyncLogService asyncLogService;
+
+ /**
+ * 处理请求前执行
+ */
+ @Before(value = "@annotation(controllerLog)")
+ public void boBefore (JoinPoint joinPoint, Log controllerLog) {
+ TIME_THREADLOCAL.set(System.currentTimeMillis());
+ }
+
+ /**
+ * 处理完请求后执行
+ *
+ * @param joinPoint 切点
+ */
+ @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
+ public void doAfterReturning (JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
+ handleLog(joinPoint, controllerLog, null, jsonResult);
+ }
+
+ /**
+ * 拦截异常操作
+ *
+ * @param joinPoint 切点
+ * @param e 异常
+ */
+ @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
+ public void doAfterThrowing (JoinPoint joinPoint, Log controllerLog, Exception e) {
+ handleLog(joinPoint, controllerLog, e, null);
+ }
+
+ protected void handleLog (final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
+ try {
+ // *========数据库日志=========*//
+ SysOperLog operLog = new SysOperLog();
+ operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
+ // 请求的地址
+ String ip = IpUtils.getIpAddr();
+ operLog.setOperIp(ip);
+ operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
+ String username = SecurityUtils.getUsername();
+ if (StringUtils.isNotBlank(username)) {
+ operLog.setOperName(username);
+ }
+
+ if (e != null) {
+ operLog.setStatus(BusinessStatus.FAIL.ordinal());
+ operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
+ }
+ // 设置方法名称
+ String className = joinPoint.getTarget().getClass().getName();
+ String methodName = joinPoint.getSignature().getName();
+ operLog.setMethod(className + "." + methodName + "()");
+ // 设置请求方式
+ operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
+ // 处理设置注解上的参数
+ getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
+ // 设置消耗时间
+ operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
+ // 保存数据库
+ asyncLogService.saveSysLog(operLog);
+ } catch (Exception exp) {
+ // 记录本地异常日志
+ log.error("异常信息:{}", exp.getMessage());
+ exp.printStackTrace();
+ } finally {
+ TIME_THREADLOCAL.remove();
+ }
+ }
+
+ /**
+ * 获取注解中对方法的描述信息 用于Controller层注解
+ *
+ * @param log 日志
+ * @param operLog 操作日志
+ *
+ * @throws Exception
+ */
+ public void getControllerMethodDescription (JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) 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.excludeParamNames());
+ }
+ // 是否需要保存response,参数和值
+ if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) {
+ operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
+ }
+ }
+
+ /**
+ * 获取请求的参数,放到log中
+ *
+ * @param operLog 操作日志
+ *
+ * @throws Exception 异常
+ */
+ private void setRequestValue (JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception {
+ String requestMethod = operLog.getRequestMethod();
+ Map, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
+ if (StringUtils.isEmpty(paramsMap)
+ && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) {
+ String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
+ operLog.setOperParam(StringUtils.substring(params, 0, 2000));
+ } else {
+ operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
+ }
+ }
+
+ /**
+ * 参数拼装
+ */
+ private String argsArrayToString (Object[] paramsArray, String[] excludeParamNames) {
+ String params = "";
+ if (paramsArray != null && paramsArray.length > 0) {
+ for (Object o : paramsArray) {
+ if (StringUtils.isNotNull(o) && !isFilterObject(o)) {
+ try {
+ String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
+ params += jsonObj.toString() + " ";
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+ return params.trim();
+ }
+
+ /**
+ * 忽略敏感属性
+ */
+ public PropertyPreExcludeFilter excludePropertyPreFilter (String[] excludeParamNames) {
+ return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
+ }
+
+ /**
+ * 判断是否需要过滤的对象。
+ *
+ * @param o 对象信息。
+ *
+ * @return 如果是需要过滤的对象,则返回true;否则返回false。
+ */
+ @SuppressWarnings("rawtypes")
+ public boolean isFilterObject (final Object o) {
+ Class> clazz = o.getClass();
+ if (clazz.isArray()) {
+ return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+ } else if (Collection.class.isAssignableFrom(clazz)) {
+ Collection collection = (Collection) o;
+ for (Object value : collection) {
+ return value instanceof MultipartFile;
+ }
+ } else if (Map.class.isAssignableFrom(clazz)) {
+ Map map = (Map) o;
+ for (Object value : map.entrySet()) {
+ Map.Entry entry = (Map.Entry) value;
+ return entry.getValue() instanceof MultipartFile;
+ }
+ }
+ return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+ || o instanceof BindingResult;
+ }
+}
diff --git a/src/main/java/com/muyu/common/log/enums/BusinessStatus.java b/src/main/java/com/muyu/common/log/enums/BusinessStatus.java
new file mode 100644
index 0000000..45e9713
--- /dev/null
+++ b/src/main/java/com/muyu/common/log/enums/BusinessStatus.java
@@ -0,0 +1,18 @@
+package com.muyu.common.log.enums;
+
+/**
+ * 操作状态
+ *
+ * @author muyu
+ */
+public enum BusinessStatus {
+ /**
+ * 成功
+ */
+ SUCCESS,
+
+ /**
+ * 失败
+ */
+ FAIL,
+}
diff --git a/src/main/java/com/muyu/common/log/enums/BusinessType.java b/src/main/java/com/muyu/common/log/enums/BusinessType.java
new file mode 100644
index 0000000..2e928c7
--- /dev/null
+++ b/src/main/java/com/muyu/common/log/enums/BusinessType.java
@@ -0,0 +1,58 @@
+package com.muyu.common.log.enums;
+
+/**
+ * 业务操作类型
+ *
+ * @author muyu
+ */
+public enum BusinessType {
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 新增
+ */
+ INSERT,
+
+ /**
+ * 修改
+ */
+ UPDATE,
+
+ /**
+ * 删除
+ */
+ DELETE,
+
+ /**
+ * 授权
+ */
+ GRANT,
+
+ /**
+ * 导出
+ */
+ EXPORT,
+
+ /**
+ * 导入
+ */
+ IMPORT,
+
+ /**
+ * 强退
+ */
+ FORCE,
+
+ /**
+ * 生成代码
+ */
+ GENCODE,
+
+ /**
+ * 清空数据
+ */
+ CLEAN,
+}
diff --git a/src/main/java/com/muyu/common/log/enums/OperatorType.java b/src/main/java/com/muyu/common/log/enums/OperatorType.java
new file mode 100644
index 0000000..645777f
--- /dev/null
+++ b/src/main/java/com/muyu/common/log/enums/OperatorType.java
@@ -0,0 +1,23 @@
+package com.muyu.common.log.enums;
+
+/**
+ * 操作人类别
+ *
+ * @author muyu
+ */
+public enum OperatorType {
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 后台用户
+ */
+ MANAGE,
+
+ /**
+ * 手机端用户
+ */
+ MOBILE
+}
diff --git a/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java b/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java
new file mode 100644
index 0000000..2245256
--- /dev/null
+++ b/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java
@@ -0,0 +1,20 @@
+package com.muyu.common.log.filter;
+
+import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
+
+/**
+ * 排除JSON敏感属性
+ *
+ * @author muyu
+ */
+public class PropertyPreExcludeFilter extends SimplePropertyPreFilter {
+ public PropertyPreExcludeFilter () {
+ }
+
+ public PropertyPreExcludeFilter addExcludes (String... filters) {
+ for (int i = 0 ; i < filters.length ; i++) {
+ this.getExcludes().add(filters[i]);
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/com/muyu/common/log/service/AsyncLogService.java b/src/main/java/com/muyu/common/log/service/AsyncLogService.java
new file mode 100644
index 0000000..46e1cdf
--- /dev/null
+++ b/src/main/java/com/muyu/common/log/service/AsyncLogService.java
@@ -0,0 +1,27 @@
+package com.muyu.common.log.service;
+
+import com.muyu.common.core.constant.SecurityConstants;
+import com.muyu.common.system.remote.RemoteLogService;
+import com.muyu.common.system.domain.SysOperLog;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+/**
+ * 异步调用日志服务
+ *
+ * @author muyu
+ */
+@Service
+public class AsyncLogService {
+ @Autowired
+ private RemoteLogService remoteLogService;
+
+ /**
+ * 保存系统日志记录
+ */
+ @Async
+ public void saveSysLog (SysOperLog sysOperLog) throws Exception {
+ remoteLogService.saveLog(sysOperLog, SecurityConstants.INNER);
+ }
+}
diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..e46e773
--- /dev/null
+++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,2 @@
+com.muyu.common.log.service.AsyncLogService
+com.muyu.common.log.aspect.LogAspect