当前位置: 首页 > ds >正文

Spring Boot + MyBatis-Plus实现操作日志记录

创建数据库表

CREATE TABLE `sys_operation_log` (`log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志ID',`operation_type` varchar(20) NOT NULL COMMENT '操作类型',`operation_module` varchar(50) NOT NULL COMMENT '操作模块',`operation_desc` varchar(200) DEFAULT NULL COMMENT '操作描述',`request_method` varchar(10) DEFAULT NULL COMMENT '请求方法',`request_url` varchar(500) DEFAULT NULL COMMENT '请求URL',`request_params` text DEFAULT NULL COMMENT '请求参数',`response_result` text DEFAULT NULL COMMENT '响应结果',`user_id` varchar(36) DEFAULT NULL COMMENT '操作用户ID',`user_name` varchar(50) DEFAULT NULL COMMENT '操作用户名',`user_ip` varchar(50) DEFAULT NULL COMMENT '用户IP',`user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理',`operation_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',`execution_time` bigint DEFAULT NULL COMMENT '执行耗时(ms)',`status` tinyint DEFAULT 1 COMMENT '操作状态(0失败 1成功)',`error_msg` text DEFAULT NULL COMMENT '错误信息',PRIMARY KEY (`log_id`),KEY `idx_user_id` (`user_id`),KEY `idx_operation_time` (`operation_time`),KEY `idx_module_type` (`operation_module`, `operation_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统操作日志表';

添加pom.xml依赖

<dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MyBatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>最新版本</version></dependency><!-- AOP --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

创建实体类

import com.baomidou.mybatisplus.annotation.*;import java.util.Date;@Data
@TableName("sys_operation_log")
public class SysOperationLog {@TableId(value = "log_id", type = IdType.AUTO)private Long logId;private String operationType;private String operationModule;private String operationDesc;private String requestMethod;private String requestUrl;private String requestParams;private String responseResult;private String userId;private String userName;private String userIp;private String userAgent;@TableField(fill = FieldFill.INSERT)private Date operationTime;private Long executionTime;private Integer status;private String errorMsg;
}

添加mapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface SysOperationLogMapper extends BaseMapper<SysOperationLog> {
}

创建自定义注解

import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Loggable {String operationType() default "";String module() default "";String description() default "";
}

创建AOP切面

import com.fasterxml.jackson.databind.ObjectMapper;
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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;@Aspect
@Component
@Slf4j
public class OperationLogAspect {private final SysOperationLogMapper logMapper;private final ObjectMapper objectMapper;public OperationLogAspect(SysOperationLogMapper logMapper, ObjectMapper objectMapper) {this.logMapper = logMapper;this.objectMapper = objectMapper;}@Around("@annotation(com.yourpackage.annotation.Loggable)")public Object logOperation(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();SysOperationLog operationLog = new SysOperationLog();Object result = null;try {// 获取注解信息MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Loggable loggable = method.getAnnotation(Loggable.class);// 填充日志基本信息operationLog.setOperationType(loggable.operationType());operationLog.setOperationModule(loggable.module());operationLog.setOperationDesc(loggable.description());operationLog.setOperationTime(new Date());// 获取请求信息ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes != null) {HttpServletRequest request = attributes.getRequest();operationLog.setRequestMethod(request.getMethod());operationLog.setRequestUrl(request.getRequestURI());operationLog.setUserIp(request.getRemoteAddr());operationLog.setUserAgent(request.getHeader("User-Agent"));}// 获取用户信息(根据你的系统实现)operationLog.setUserId("当前用户ID");operationLog.setUserName("当前用户名");// 获取请求参数Object[] args = joinPoint.getArgs();operationLog.setRequestParams(objectMapper.writeValueAsString(args));// 执行目标方法result = joinPoint.proceed();// 记录响应结果operationLog.setResponseResult(objectMapper.writeValueAsString(result));operationLog.setStatus(1);} catch (Exception e) {operationLog.setStatus(0);operationLog.setErrorMsg(e.getMessage());throw e;} finally {long endTime = System.currentTimeMillis();operationLog.setExecutionTime(endTime - startTime);// 异步保存日志(建议使用异步方式)logMapper.insert(operationLog);}return result;}
}

这是一个获取用户id和用户名的示例方法

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;@Component
public class UserInfoUtils {// 根据你的安全框架实现获取用户信息public String getCurrentUserId() {// 示例:从Spring Security获取return SecurityContextHolder.getContext().getAuthentication().getName();}public String getCurrentUsername() {// 实现获取用户名逻辑return "用户名";}
}

使用

@RestController
@RequestMapping("/api")
public class DemoController {@Loggable(operationType = "QUERY", module = "用户管理", description = "查询用户列表")@GetMapping("/users")public List<User> getUsers() {// 业务逻辑}
}

为了防止数据库越来越大,可以删除一年前的数据,我们可以通过数据库层面实现

1. 创建存储过程

DELIMITER $$CREATE PROCEDURE clean_old_logs()
BEGINDECLARE batch_size INT DEFAULT 1000;DECLARE deleted_rows INT DEFAULT 1;WHILE deleted_rows > 0 DODELETE FROM sys_operation_log WHERE operation_time < NOW() - INTERVAL 1 YEAR LIMIT batch_size;SET deleted_rows = ROW_COUNT();COMMIT;DO SLEEP(0.5); -- 防止锁表太久END WHILE;
END$$DELIMITER ;

2. 创建定时事件,一天一次

CREATE EVENT execute_clean_procedure
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP + INTERVAL 1 DAY
DO
BEGINCALL clean_old_logs();
END

http://www.xdnf.cn/news/8392.html

相关文章:

  • 绘制音频信号的各种频谱图,包括Mel频谱图、STFT频谱图等。它不仅能够绘制频谱图librosa.display.specshow
  • 知道不知道
  • 【SpringBoot】从零开始全面解析Spring IocDI (一)
  • table表格鼠标圈选数据并复制且自带html结构
  • 基于Python+YOLO模型的手势识别系统
  • 使用 FFmpeg 将视频转换为高质量 GIF(保留原始尺寸和帧率)
  • ​从非洲掘金到全球逐鹿,传音用本地化战略重塑出海路径
  • C++11-(3)
  • Android内存调优学习总结(OOM与ANR)
  • ARFoundation系列讲解 - 77 音频可视化
  • Science Robotics|仿生章鱼机器人问世:流体智能分级操控,抓豆腐、测触感全自动
  • API自动化与持续集成核心实战知识点!
  • uniapp 嵌入鸿蒙原生组件 具体步骤
  • 封装WPF中列表控件的剪贴板操作(附源码)
  • 如何通过用户体验设计提升Shopify独立站的信任度
  • 云原生安全核心:云安全责任共担模型(Shared Responsibility Model)详解
  • 解锁C++编辑距离:文本相似度的度量密码
  • 界面组件DevExpress WPF中文教程:Grid - 行和卡片
  • 扫描电镜:打开微观世界的“超维相机“
  • [实战]用户系统-2-完善登录和校验以及VIP
  • Python打卡第34天
  • PETR- Position Embedding Transformation for Multi-View 3D Object Detection
  • leetcode 61. Rotate List和86. Partition List
  • 【心海资源】黄金首饰价格查询单页源码
  • 如何在 Windows 11 或 10 上通过 PowerShell 安装 Docker Desktop
  • Centos7和Centos8版本功能对比
  • 系统性能分析基本概念(2):性能模型
  • ​​Resin-3.1.12-01 安装教程:详细步骤与配置指南(Linux环境)
  • 【运维实战】nginx版本升级
  • 我的世界模组开发——水平方向的方块(3)