init
This commit is contained in:
		
							
								
								
									
										32
									
								
								RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <parent> | ||||
|         <groupId>org.dromara</groupId> | ||||
|         <artifactId>ruoyi-common</artifactId> | ||||
|         <version>${revision}</version> | ||||
|     </parent> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|  | ||||
|     <artifactId>ruoyi-common-log</artifactId> | ||||
|  | ||||
|     <description> | ||||
|         ruoyi-common-log 日志记录 | ||||
|     </description> | ||||
|  | ||||
|     <dependencies> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-common-satoken</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-common-json</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|     </dependencies> | ||||
|  | ||||
| </project> | ||||
| @ -0,0 +1,48 @@ | ||||
| package org.dromara.common.log.annotation; | ||||
|  | ||||
| import org.dromara.common.log.enums.BusinessType; | ||||
| import org.dromara.common.log.enums.OperatorType; | ||||
|  | ||||
| import java.lang.annotation.*; | ||||
|  | ||||
| /** | ||||
|  * 自定义操作日志记录注解 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Target({ElementType.PARAMETER, ElementType.METHOD}) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Documented | ||||
| public @interface Log { | ||||
|     /** | ||||
|      * 模块 | ||||
|      */ | ||||
|     String title() default ""; | ||||
|  | ||||
|     /** | ||||
|      * 功能 | ||||
|      */ | ||||
|     BusinessType businessType() default BusinessType.OTHER; | ||||
|  | ||||
|     /** | ||||
|      * 操作人类别 | ||||
|      */ | ||||
|     OperatorType operatorType() default OperatorType.MANAGE; | ||||
|  | ||||
|     /** | ||||
|      * 是否保存请求的参数 | ||||
|      */ | ||||
|     boolean isSaveRequestData() default true; | ||||
|  | ||||
|     /** | ||||
|      * 是否保存响应的参数 | ||||
|      */ | ||||
|     boolean isSaveResponseData() default true; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * 排除指定的请求参数 | ||||
|      */ | ||||
|     String[] excludeParamNames() default {}; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,219 @@ | ||||
| package org.dromara.common.log.aspect; | ||||
|  | ||||
| import cn.hutool.core.lang.Dict; | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.apache.commons.lang3.time.StopWatch; | ||||
| 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.dromara.common.core.domain.model.LoginUser; | ||||
| import org.dromara.common.core.utils.ServletUtils; | ||||
| import org.dromara.common.core.utils.SpringUtils; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import org.dromara.common.json.utils.JsonUtils; | ||||
| import org.dromara.common.log.annotation.Log; | ||||
| import org.dromara.common.log.enums.BusinessStatus; | ||||
| import org.dromara.common.log.event.OperLogEvent; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.http.HttpMethod; | ||||
| import org.springframework.validation.BindingResult; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.Map; | ||||
| import java.util.StringJoiner; | ||||
|  | ||||
| /** | ||||
|  * 操作日志记录处理 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Slf4j | ||||
| @Aspect | ||||
| @AutoConfiguration | ||||
| public class LogAspect { | ||||
|  | ||||
|     /** | ||||
|      * 排除敏感属性字段 | ||||
|      */ | ||||
|     public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * 计时 key | ||||
|      */ | ||||
|     private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>(); | ||||
|  | ||||
|     /** | ||||
|      * 处理请求前执行 | ||||
|      */ | ||||
|     @Before(value = "@annotation(controllerLog)") | ||||
|     public void doBefore(JoinPoint joinPoint, Log controllerLog) { | ||||
|         StopWatch stopWatch = new StopWatch(); | ||||
|         KEY_CACHE.set(stopWatch); | ||||
|         stopWatch.start(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 处理完请求后执行 | ||||
|      * | ||||
|      * @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 { | ||||
|  | ||||
|             // *========数据库日志=========*// | ||||
|             OperLogEvent operLog = new OperLogEvent(); | ||||
|             operLog.setTenantId(LoginHelper.getTenantId()); | ||||
|             operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); | ||||
|             // 请求的地址 | ||||
|             String ip = ServletUtils.getClientIP(); | ||||
|             operLog.setOperIp(ip); | ||||
|             operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); | ||||
|             LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|             operLog.setOperName(loginUser.getUsername()); | ||||
|             operLog.setDeptName(loginUser.getDeptName()); | ||||
|  | ||||
|             if (e != null) { | ||||
|                 operLog.setStatus(BusinessStatus.FAIL.ordinal()); | ||||
|                 operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800)); | ||||
|             } | ||||
|             // 设置方法名称 | ||||
|             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); | ||||
|             // 设置消耗时间 | ||||
|             StopWatch stopWatch = KEY_CACHE.get(); | ||||
|             stopWatch.stop(); | ||||
|             operLog.setCostTime(stopWatch.getDuration().toMillis()); | ||||
|             // 发布事件保存数据库 | ||||
|             SpringUtils.context().publishEvent(operLog); | ||||
|         } catch (Exception exp) { | ||||
|             // 记录本地异常日志 | ||||
|             log.error("异常信息:{}", exp.getMessage()); | ||||
|         } finally { | ||||
|             KEY_CACHE.remove(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取注解中对方法的描述信息 用于Controller层注解 | ||||
|      * | ||||
|      * @param log     日志 | ||||
|      * @param operLog 操作日志 | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent 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() && ObjectUtil.isNotNull(jsonResult)) { | ||||
|             operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 3800)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取请求的参数,放到log中 | ||||
|      * | ||||
|      * @param operLog 操作日志 | ||||
|      * @throws Exception 异常 | ||||
|      */ | ||||
|     private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { | ||||
|         Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); | ||||
|         String requestMethod = operLog.getRequestMethod(); | ||||
|         if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) { | ||||
|             String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); | ||||
|             operLog.setOperParam(StringUtils.substring(params, 0, 3800)); | ||||
|         } else { | ||||
|             MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); | ||||
|             MapUtil.removeAny(paramsMap, excludeParamNames); | ||||
|             operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 参数拼装 | ||||
|      */ | ||||
|     private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) { | ||||
|         StringJoiner params = new StringJoiner(" "); | ||||
|         if (ArrayUtil.isEmpty(paramsArray)) { | ||||
|             return params.toString(); | ||||
|         } | ||||
|         for (Object o : paramsArray) { | ||||
|             if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { | ||||
|                 String str = JsonUtils.toJsonString(o); | ||||
|                 Dict dict = JsonUtils.parseMap(str); | ||||
|                 if (MapUtil.isNotEmpty(dict)) { | ||||
|                     MapUtil.removeAny(dict, EXCLUDE_PROPERTIES); | ||||
|                     MapUtil.removeAny(dict, excludeParamNames); | ||||
|                     str = JsonUtils.toJsonString(dict); | ||||
|                 } | ||||
|                 params.add(str); | ||||
|             } | ||||
|         } | ||||
|         return params.toString(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 判断是否需要过滤的对象。 | ||||
|      * | ||||
|      * @param o 对象信息。 | ||||
|      * @return 如果是需要过滤的对象,则返回true;否则返回false。 | ||||
|      */ | ||||
|     @SuppressWarnings("rawtypes") | ||||
|     public boolean isFilterObject(final Object o) { | ||||
|         Class<?> clazz = o.getClass(); | ||||
|         if (clazz.isArray()) { | ||||
|             return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); | ||||
|         } 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.values()) { | ||||
|                 return value instanceof MultipartFile; | ||||
|             } | ||||
|         } | ||||
|         return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse | ||||
|                || o instanceof BindingResult; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| package org.dromara.common.log.enums; | ||||
|  | ||||
| /** | ||||
|  * 操作状态 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| public enum BusinessStatus { | ||||
|     /** | ||||
|      * 成功 | ||||
|      */ | ||||
|     SUCCESS, | ||||
|  | ||||
|     /** | ||||
|      * 失败 | ||||
|      */ | ||||
|     FAIL, | ||||
| } | ||||
| @ -0,0 +1,58 @@ | ||||
| package org.dromara.common.log.enums; | ||||
|  | ||||
| /** | ||||
|  * 业务操作类型 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| public enum BusinessType { | ||||
|     /** | ||||
|      * 其它 | ||||
|      */ | ||||
|     OTHER, | ||||
|  | ||||
|     /** | ||||
|      * 新增 | ||||
|      */ | ||||
|     INSERT, | ||||
|  | ||||
|     /** | ||||
|      * 修改 | ||||
|      */ | ||||
|     UPDATE, | ||||
|  | ||||
|     /** | ||||
|      * 删除 | ||||
|      */ | ||||
|     DELETE, | ||||
|  | ||||
|     /** | ||||
|      * 授权 | ||||
|      */ | ||||
|     GRANT, | ||||
|  | ||||
|     /** | ||||
|      * 导出 | ||||
|      */ | ||||
|     EXPORT, | ||||
|  | ||||
|     /** | ||||
|      * 导入 | ||||
|      */ | ||||
|     IMPORT, | ||||
|  | ||||
|     /** | ||||
|      * 强退 | ||||
|      */ | ||||
|     FORCE, | ||||
|  | ||||
|     /** | ||||
|      * 生成代码 | ||||
|      */ | ||||
|     GENCODE, | ||||
|  | ||||
|     /** | ||||
|      * 清空数据 | ||||
|      */ | ||||
|     CLEAN, | ||||
| } | ||||
| @ -0,0 +1,23 @@ | ||||
| package org.dromara.common.log.enums; | ||||
|  | ||||
| /** | ||||
|  * 操作人类别 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| public enum OperatorType { | ||||
|     /** | ||||
|      * 其它 | ||||
|      */ | ||||
|     OTHER, | ||||
|  | ||||
|     /** | ||||
|      * 后台用户 | ||||
|      */ | ||||
|     MANAGE, | ||||
|  | ||||
|     /** | ||||
|      * 手机端用户 | ||||
|      */ | ||||
|     MOBILE | ||||
| } | ||||
| @ -0,0 +1,52 @@ | ||||
| package org.dromara.common.log.event; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 登录事件 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| public class LogininforEvent implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 租户ID | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 用户账号 | ||||
|      */ | ||||
|     private String username; | ||||
|  | ||||
|     /** | ||||
|      * 登录状态 0成功 1失败 | ||||
|      */ | ||||
|     private String status; | ||||
|  | ||||
|     /** | ||||
|      * 提示消息 | ||||
|      */ | ||||
|     private String message; | ||||
|  | ||||
|     /** | ||||
|      * 请求体 | ||||
|      */ | ||||
|     private HttpServletRequest request; | ||||
|  | ||||
|     /** | ||||
|      * 其他参数 | ||||
|      */ | ||||
|     private Object[] args; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,115 @@ | ||||
| package org.dromara.common.log.event; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.Date; | ||||
|  | ||||
| /** | ||||
|  * 操作日志事件 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| public class OperLogEvent implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 日志主键 | ||||
|      */ | ||||
|     private Long operId; | ||||
|  | ||||
|     /** | ||||
|      * 租户ID | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 操作模块 | ||||
|      */ | ||||
|     private String title; | ||||
|  | ||||
|     /** | ||||
|      * 业务类型(0其它 1新增 2修改 3删除) | ||||
|      */ | ||||
|     private Integer businessType; | ||||
|  | ||||
|     /** | ||||
|      * 业务类型数组 | ||||
|      */ | ||||
|     private Integer[] businessTypes; | ||||
|  | ||||
|     /** | ||||
|      * 请求方法 | ||||
|      */ | ||||
|     private String method; | ||||
|  | ||||
|     /** | ||||
|      * 请求方式 | ||||
|      */ | ||||
|     private String requestMethod; | ||||
|  | ||||
|     /** | ||||
|      * 操作类别(0其它 1后台用户 2手机端用户) | ||||
|      */ | ||||
|     private Integer operatorType; | ||||
|  | ||||
|     /** | ||||
|      * 操作人员 | ||||
|      */ | ||||
|     private String operName; | ||||
|  | ||||
|     /** | ||||
|      * 部门名称 | ||||
|      */ | ||||
|     private String deptName; | ||||
|  | ||||
|     /** | ||||
|      * 请求url | ||||
|      */ | ||||
|     private String operUrl; | ||||
|  | ||||
|     /** | ||||
|      * 操作地址 | ||||
|      */ | ||||
|     private String operIp; | ||||
|  | ||||
|     /** | ||||
|      * 操作地点 | ||||
|      */ | ||||
|     private String operLocation; | ||||
|  | ||||
|     /** | ||||
|      * 请求参数 | ||||
|      */ | ||||
|     private String operParam; | ||||
|  | ||||
|     /** | ||||
|      * 返回参数 | ||||
|      */ | ||||
|     private String jsonResult; | ||||
|  | ||||
|     /** | ||||
|      * 操作状态(0正常 1异常) | ||||
|      */ | ||||
|     private Integer status; | ||||
|  | ||||
|     /** | ||||
|      * 错误消息 | ||||
|      */ | ||||
|     private String errorMsg; | ||||
|  | ||||
|     /** | ||||
|      * 操作时间 | ||||
|      */ | ||||
|     private Date operTime; | ||||
|  | ||||
|     /** | ||||
|      * 消耗时间 | ||||
|      */ | ||||
|     private Long costTime; | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| org.dromara.common.log.aspect.LogAspect | ||||
		Reference in New Issue
	
	Block a user