update 优化操作日志

This commit is contained in:
AprilWind
2026-04-03 14:49:46 +08:00
parent ef3fa714bb
commit bcc11dcc12
11 changed files with 433 additions and 124 deletions

View File

@@ -1,7 +1,8 @@
package org.dromara.common.log.annotation;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.log.enums.OperatorType;
import org.dromara.common.log.enums.OperateChannel;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@@ -14,20 +15,44 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 模块
* 模块【已废弃,请使用 module】
*
* @deprecated 请使用 {@link #module()}
*/
@Deprecated
String title() default "";
/**
* 功能
* 业务模块名称(必填,如:用户管理、订单管理)
*/
String module() default "";
/**
* 操作功能名称(必填,如:新增用户、导出订单)
*/
String name() default "";
/**
* 操作描述(备注)
*/
String remark() default "";
/**
* 标签(用于检索/分类)
*/
String[] tags() default {};
/**
* 业务类型(查询/新增/修改/删除/导入/导出等)
*/
BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
* 操作渠道WEB / APP / MINI_APP / OPEN_API 等
*/
OperatorType operatorType() default OperatorType.MANAGE;
OperateChannel channel() default OperateChannel.WEB;
/**
* 是否保存请求的参数
@@ -39,7 +64,6 @@ public @interface Log {
*/
boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/

View File

@@ -4,6 +4,8 @@ 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 cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@@ -48,7 +50,7 @@ public class LogAspect {
/**
* 在目标方法执行前启动耗时统计。
*
* @param joinPoint 切点
* @param joinPoint 切点
* @param controllerLog 日志注解
*/
@Before(value = "@annotation(controllerLog)")
@@ -61,9 +63,9 @@ public class LogAspect {
/**
* 在目标方法正常返回后记录操作日志。
*
* @param joinPoint 切点
* @param joinPoint 切点
* @param controllerLog 日志注解
* @param jsonResult 返回结果
* @param jsonResult 返回结果
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
@@ -73,9 +75,9 @@ public class LogAspect {
/**
* 在目标方法抛出异常后记录操作日志。
*
* @param joinPoint 切点
* @param joinPoint 切点
* @param controllerLog 日志注解
* @param e 异常
* @param e 异常
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
@@ -85,35 +87,42 @@ public class LogAspect {
/**
* 组装并发布操作日志事件。
*
* @param joinPoint 切点
* @param joinPoint 切点
* @param controllerLog 日志注解
* @param e 异常信息
* @param jsonResult 返回结果
* @param e 异常信息
* @param jsonResult 返回结果
*/
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try {
// *========数据库日志=========*//
OperLogEvent operLog = new OperLogEvent();
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.setUsername(loginUser.getUsername());
operLog.setDeptName(loginUser.getDeptName());
operLog.setUserType(loginUser.getUserType());
operLog.setDeviceType(loginUser.getDeviceType());
operLog.setOperIp(ServletUtils.getClientIP());
HttpServletRequest request = ServletUtils.getRequest();
operLog.setOperUrl(StringUtils.substring(request.getRequestURI(), 0, 255));
UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
operLog.setBrowser(userAgent.getBrowser().getName());
operLog.setOs(userAgent.getOs().getName());
if (e != null) {
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800));
} else {
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// 设置请求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
operLog.setRequestMethod(request.getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间
@@ -133,19 +142,19 @@ public class LogAspect {
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint 切点
* @param log 日志
* @param operLog 操作日志
* @param joinPoint 切点
* @param log 日志
* @param operLog 操作日志
* @param jsonResult 返回结果
* @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());
operLog.setModule(StringUtils.isBlank(log.title()) ? log.module() : log.title());
operLog.setName(log.name());
operLog.setRemark(log.remark());
operLog.setTags(JsonUtils.toJsonString(log.tags()));
operLog.setBusinessType(log.businessType().getCode());
operLog.setChannel(log.channel().getCode());
// 是否需要保存request参数和值
if (log.isSaveRequestData()) {
// 获取参数的信息,传入到数据库中。
@@ -160,8 +169,8 @@ public class LogAspect {
/**
* 获取请求的参数放到log中
*
* @param joinPoint 切点
* @param operLog 操作日志
* @param joinPoint 切点
* @param operLog 操作日志
* @param excludeParamNames 排除参数名
* @throws Exception 异常
*/
@@ -181,7 +190,7 @@ public class LogAspect {
/**
* 将方法参数序列化为日志字符串。
*
* @param paramsArray 参数数组
* @param paramsArray 参数数组
* @param excludeParamNames 排除字段名
* @return 参数字符串
*/
@@ -242,6 +251,6 @@ public class LogAspect {
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
|| o instanceof BindingResult;
}
}

View File

@@ -1,58 +1,195 @@
package org.dromara.common.log.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 业务操作类型
*
* @author ruoyi
* @author AprilWind
*/
@Getter
@AllArgsConstructor
public enum BusinessType {
/**
* 其它
* 其它操作
*/
OTHER,
OTHER(0, "其它操作"),
/**
* 查询
*/
QUERY(1, "查询"),
/**
* 新增
*/
INSERT,
INSERT(2, "新增"),
/**
* 修改
*/
UPDATE,
UPDATE(3, "修改"),
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
DELETE(4, "删除"),
/**
* 导入
*/
IMPORT,
IMPORT(5, "导入"),
/**
* 强退
* 导出
*/
FORCE,
EXPORT(6, "导出"),
/**
* 生成代码
* 授权/赋权
*/
GENCODE,
GRANT(7, "授权"),
/**
* 清空数据
*/
CLEAN,
CLEAN(8, "清空数据"),
/**
* 强制退出(用户登出)
*/
FORCE_LOGOUT(9, "强制退出"),
/**
* 状态修改(启用/禁用/冻结/解冻)
*/
CHANGE_STATUS(10, "状态修改"),
/**
* 重置密码
*/
RESET_PASSWORD(11, "重置密码"),
/**
* 批量删除
*/
BATCH_DELETE(12, "批量删除"),
/**
* 批量导入
*/
BATCH_IMPORT(13, "批量导入"),
/**
* 批量导出
*/
BATCH_EXPORT(14, "批量导出"),
/**
* 批量操作(通用)
*/
BATCH_OPERATE(15, "批量操作"),
/**
* 审核操作(通过/驳回)
*/
AUDIT(16, "审核"),
/**
* 发布/上线
*/
PUBLISH(17, "发布"),
/**
* 撤回/下线
*/
REVOKE(18, "撤回"),
/**
* 同步数据
*/
SYNC(19, "同步数据"),
/**
* 生成数据(生成编码、生成报表等)
*/
GENERATE(20, "生成数据"),
/**
* 上传文件
*/
UPLOAD(21, "上传文件"),
/**
* 下载文件
*/
DOWNLOAD(22, "下载文件"),
/**
* 定时任务执行
*/
TASK_EXECUTE(23, "定时任务执行"),
/**
* 接口调用(第三方/开放API
*/
API_CALL(24, "接口调用"),
/**
* 登录
*/
LOGIN(25, "登录"),
/**
* 登出
*/
LOGOUT(26, "登出"),
/**
* 注册
*/
REGISTER(27, "注册"),
/**
* 作废/取消
*/
CANCEL(28, "作废/取消"),
/**
* 归档
*/
ARCHIVE(29, "归档"),
/**
* 配置修改
*/
CONFIG_UPDATE(30, "配置修改"),
/**
* 打印单据/报表
*/
PRINT(31, "打印"),
/**
* 复制数据/克隆
*/
COPY(32, "复制"),
/**
* 生成代码
*/
GENCODE(33, "生成代码");
/**
* 业务类型编码
*/
private final Integer code;
/**
* 业务类型描述
*/
private final String desc;
}

View File

@@ -0,0 +1,54 @@
package org.dromara.common.log.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 操作渠道
*
* @author AprilWind
*/
@Getter
@AllArgsConstructor
public enum OperateChannel {
/**
* 管理后台
*/
WEB(0, "管理后台"),
/**
* 移动端APP
*/
APP(1, "移动端APP"),
/**
* 小程序
*/
MINI_APP(2, "小程序"),
/**
* 开放接口
*/
OPEN_API(3, "开放接口"),
/**
* 定时任务
*/
TASK(4, "定时任务"),
/**
* 第三方调用
*/
THIRD(5, "第三方调用");
/**
* 操作渠道编码
*/
private final Integer code;
/**
* 操作渠道描述
*/
private final String desc;
}

View File

@@ -1,23 +0,0 @@
package org.dromara.common.log.enums;
/**
* 操作人类别
*
* @author ruoyi
*/
public enum OperatorType {
/**
* 其它
*/
OTHER,
/**
* 后台用户
*/
MANAGE,
/**
* 手机端用户
*/
MOBILE
}

View File

@@ -11,7 +11,6 @@ import java.time.LocalDateTime;
*
* @author Lion Li
*/
@Data
public class OperLogEvent implements Serializable {
@@ -19,24 +18,34 @@ public class OperLogEvent implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 日志主键
* 业务模块名称
*/
private Long operId;
private String module;
/**
* 操作模块
* 操作功能名称
*/
private String title;
private String name;
/**
* 业务类型0其它 1新增 2修改 3删除
* 操作描述(备注
*/
private String remark;
/**
* 标签(用于检索/分类)
*/
private String tags;
/**
* 业务类型
*/
private Integer businessType;
/**
* 业务类型数组
* 操作渠道
*/
private Integer[] businessTypes;
private Integer channel;
/**
* 请求方法
@@ -49,20 +58,20 @@ public class OperLogEvent implements Serializable {
private String requestMethod;
/**
* 操作类别0其它 1后台用户 2手机端用户
* 用户名
*/
private Integer operatorType;
/**
* 操作人员
*/
private String operName;
private String username;
/**
* 部门名称
*/
private String deptName;
/**
* 用户类型
*/
private String userType;
/**
* 请求url
*/
@@ -74,9 +83,19 @@ public class OperLogEvent implements Serializable {
private String operIp;
/**
* 操作地点
* 设备类型
*/
private String operLocation;
private String deviceType;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 请求参数

View File

@@ -83,7 +83,7 @@ public class SysUserOnlineController extends BaseController {
* @return 操作结果
*/
@SaCheckPermission("monitor:online:forceLogout")
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@Log(title = "在线用户", businessType = BusinessType.FORCE_LOGOUT)
@RepeatSubmit()
@DeleteMapping("/{tokenId}")
public R<Void> forceLogout(@PathVariable String tokenId) {
@@ -120,7 +120,7 @@ public class SysUserOnlineController extends BaseController {
* @param tokenId token值
* @return 操作结果
*/
@Log(title = "在线设备", businessType = BusinessType.FORCE)
@Log(title = "在线设备", businessType = BusinessType.FORCE_LOGOUT)
@RepeatSubmit()
@DeleteMapping("/myself/{tokenId}")
public R<Void> remove(@PathVariable("tokenId") String tokenId) {

View File

@@ -13,7 +13,6 @@ import java.time.LocalDateTime;
*
* @author Lion Li
*/
@Data
@TableName("sys_oper_log")
public class SysOperLog implements Serializable {
@@ -28,15 +27,35 @@ public class SysOperLog implements Serializable {
private Long operId;
/**
* 操作模块
* 业务模块名称
*/
private String title;
private String module;
/**
* 业务类型0其它 1新增 2修改 3删除
* 操作功能名称
*/
private String name;
/**
* 操作描述(备注)
*/
private String remark;
/**
* 标签(用于检索/分类)
*/
private String tags;
/**
* 业务类型
*/
private Integer businessType;
/**
* 操作渠道
*/
private Integer channel;
/**
* 请求方法
*/
@@ -53,15 +72,20 @@ public class SysOperLog implements Serializable {
private Integer operatorType;
/**
* 操作人员
* 用户名
*/
private String operName;
private String username;
/**
* 部门名称
*/
private String deptName;
/**
* 用户类型
*/
private String userType;
/**
* 请求url
*/
@@ -72,11 +96,26 @@ public class SysOperLog implements Serializable {
*/
private String operIp;
/**
* 设备类型
*/
private String deviceType;
/**
* 操作地点
*/
private String operLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 请求参数
*/

View File

@@ -35,22 +35,42 @@ public class SysOperLogBo implements Serializable {
private Long operId;
/**
* 模块标题
* 业务模块名称
*/
private String title;
private String module;
/**
* 业务类型0其它 1新增 2修改 3删除
* 操作功能名称
*/
private String name;
/**
* 操作描述(备注)
*/
private String remark;
/**
* 标签(用于检索/分类)
*/
private String tags;
/**
* 业务类型
*/
private Integer businessType;
/**
* 操作渠道
*/
private Integer channel;
/**
* 业务类型数组
*/
private Integer[] businessTypes;
/**
* 方法名称
* 请求方法
*/
private String method;
@@ -65,9 +85,9 @@ public class SysOperLogBo implements Serializable {
private Integer operatorType;
/**
* 操作人员
* 用户名
*/
private String operName;
private String username;
/**
* 部门名称
@@ -75,20 +95,40 @@ public class SysOperLogBo implements Serializable {
private String deptName;
/**
* 请求URL
* 用户类型
*/
private String userType;
/**
* 请求url
*/
private String operUrl;
/**
* 主机地址
* 操作地址
*/
private String operIp;
/**
* 设备类型
*/
private String deviceType;
/**
* 操作地点
*/
private String operLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 请求参数
*/

View File

@@ -1,5 +1,6 @@
package org.dromara.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -43,10 +44,11 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
@Async
@EventListener
public void recordOper(OperLogEvent operLogEvent) {
SysOperLogBo operLog = MapstructUtils.convert(operLogEvent, SysOperLogBo.class);
SysOperLog operLog = BeanUtil.copyProperties(operLogEvent, SysOperLog.class);
// 远程查询操作地点
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
insertOperlog(operLog);
operLog.setOperTime(LocalDateTime.now());
baseMapper.insert(operLog);
}
/**
@@ -76,7 +78,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
Map<String, Object> params = operLog.getParams();
return new LambdaQueryWrapper<SysOperLog>()
.like(StringUtils.isNotBlank(operLog.getOperIp()), SysOperLog::getOperIp, operLog.getOperIp())
.like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())
.like(StringUtils.isNotBlank(operLog.getModule()), SysOperLog::getModule, operLog.getModule())
.eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,
SysOperLog::getBusinessType, operLog.getBusinessType())
.func(f -> {
@@ -86,7 +88,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
})
.eq(operLog.getStatus() != null,
SysOperLog::getStatus, operLog.getStatus())
.like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName())
.like(StringUtils.isNotBlank(operLog.getUsername()), SysOperLog::getUsername, operLog.getUsername())
.between(params.get("beginTime") != null && params.get("endTime") != null,
SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime"));
}

View File

@@ -506,16 +506,24 @@ insert into sys_user_post values ('1', '1');
-- ----------------------------
create table sys_oper_log (
oper_id bigint(20) not null comment '日志主键',
title varchar(50) default '' comment '模块标题',
business_type int(2) default 0 comment '业务类型0其它 1新增 2修改 3删除',
method varchar(100) default '' comment '方法名称',
module varchar(50) default '' comment '业务模块名称',
name varchar(50) default '' comment '操作功能名称',
remark varchar(255) default '' comment '操作描述(备注)',
tags varchar(255) default '' comment '标签(用于检索/分类)',
business_type int(2) default 0 comment '业务类型',
channel int(1) default 0 comment '操作渠道',
method varchar(100) default '' comment '请求方法',
request_method varchar(10) default '' comment '请求方式',
operator_type int(1) default 0 comment '操作类别0其它 1后台用户 2手机端用户',
oper_name varchar(50) default '' comment '操作人员',
operator_type int(1) default 0 comment '操作类别',
username varchar(50) default '' comment '用户名',
dept_name varchar(50) default '' comment '部门名称',
oper_url varchar(255) default '' comment '请求URL',
oper_ip varchar(128) default '' comment '主机地址',
user_type varchar(20) default '' comment '用户类型',
oper_url varchar(255) default '' comment '请求url',
oper_ip varchar(128) default '' comment '操作地址',
device_type varchar(50) default '' comment '设备类型',
oper_location varchar(255) default '' comment '操作地点',
browser varchar(50) default '' comment '浏览器类型',
os varchar(50) default '' comment '操作系统',
oper_param varchar(4000) default '' comment '请求参数',
json_result varchar(4000) default '' comment '返回参数',
status int(1) default 0 comment '操作状态0正常 1异常',