《尚品甄选》:后台系统——通过面向切面编程AOP,实现记录日志功能

文章目录

  • 一、记录日志的意义
  • 二、日志数据表结构
  • 三、记录日志思想
  • 四、切面类环境搭建
  • 五、保存日志数据

一、记录日志的意义

后台管理系统记录操作日志的意义非常重要,主要体现在以下几个方面:

  • 安全性:操作日志可以记录管理员操作行为,以此来监控和防止管理员滥用权限或进行其他不当操作。如果后台管理系统没有记录操作日志,那么一旦出现不当操作,就无法对其进行追踪和定位,造成不可估量的安全风险。
  • 追溯性:操作日志可以帮助管理员及时发现问题,并可以通过日志进行快速定位和处理。例如某个用户投诉自己的订单异常,管理员可以直接通过查询该订单的操作日志,找到问题所在并进行修改或解决。

因此,后台管理系统记录操作日志,对于维护系统的安全稳定性、保障客户数据的完整性和隐私性、提高系统及时响应和处理能力等方面具有重要意义,是保障企业正常运营和客户满意度的重要手段。

二、日志数据表结构

根据项目的需要,日志表结构设计如下:

CREATE TABLE `sys_oper_log` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
  `title` varchar(50) DEFAULT '' COMMENT '模块标题',
  `method` varchar(100) DEFAULT '' COMMENT '方法名称',
  `request_method` varchar(10) DEFAULT '' COMMENT '请求方式',
  `operator_type` varchar(20) DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
  `oper_name` varchar(50) DEFAULT '' COMMENT '操作人员',
  `oper_url` varchar(255) DEFAULT '' COMMENT '请求URL',
  `oper_ip` varchar(128) DEFAULT '' COMMENT '主机地址',
  `oper_param` varchar(2000) DEFAULT '' COMMENT '请求参数',
  `json_result` varchar(2000) DEFAULT '' COMMENT '返回参数',
  `status` int DEFAULT '0' COMMENT '操作状态(0正常 1异常)',
  `error_msg` varchar(2000) DEFAULT '' COMMENT '错误消息',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:可用 1:不可用)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8mb3 COMMENT='操作日志记录'

三、记录日志思想

AOP记录日志的主要优点:

  • 低侵入性:AOP记录日志不需要修改原有的业务逻辑代码,只需要新增一个切面即可。

  • 统一管理:通过AOP记录日志可以将各个模块中需要记录日志的部分进行统一管理,降低了代码重复度,提高了代码可维护性和可扩展性。

  • 提升效率:通过引入AOP记录日志,可以避免手动编写日志记录代码,减少了开发人员的工作量,提升了开发效率。

  • 安全性:通过AOP记录日志,可以收集系统的操作日志,帮助管理员及时发现问题并进行调整,从而提高系统的安全性。

AOP记录日志的整体思想
1、基于自定义注解来确定切入点(优势:可以通过自定义注解携带一些变化的参数,比如模块名称、操作类别)
2、基于环绕通知来完成日志记录

四、切面类环境搭建

1、创建日志模块并引入依赖
在这里插入图片描述

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

2、自定义MyLog注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    /**
     * 模块名称
     */
    public String title();

    /*
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /*
     * 业务类型(0其它 1新增 2修改 3删除)
     */
    public int businessType();

    /*
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;

    /*
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;
}

3、自定义注解中用到了枚举类OperatorType,定义如下:

public enum OperatorType {
    /*
     * 其他
     */
    OTHER,
    /*
     * 后台用户
     */
    MANAGE,
    /*
     * 手机端用户
     */
    MOBILE
}

4、定义一个切面类,并且在该切面类中提供一个环绕通知方法,代码如下所示:

@Aspect
@Component
@Slf4j
public class LogAspect {
    @Autowired
    private AsyncOperLogService asyncOperLogService;
    @Around(value = "@annotation(sysLog)")
    public Object doAroundAdvice(ProceedingJoinPoint joinPoint, MyLog sysLog) {
        // 构建前置参数
        SysOperLog sysOperLog = new SysOperLog();
        LogUtil.beforeHandleLog(sysLog, joinPoint, sysOperLog);
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
            // 执行业务方法
            LogUtil.afterHandlLog(sysLog, proceed, sysOperLog, 0, null);
            // 构建响应结果参数
        } catch (Throwable e) {
            // 业务方法执行产生异常
            e.printStackTrace();                                // 打印异常信息
            LogUtil.afterHandlLog(sysLog, proceed, sysOperLog, 1, e.getMessage());
            throw new RuntimeException();
        }
        // 保存日志数据
        asyncOperLogService.saveSysOperLog(sysOperLog);
        // 返回执行结果
        return proceed;
    }
}

5、定义注解EnableLogAspect

想让LogAspect这个切面类在其他的业务服务中进行使用,那么就需要该切面类纳入到Spring容器中。Spring Boot默认会扫描和启动类所在包相同包中的bean以及子包中的bean。而LogAspect切面类不满足扫描条件,因此无法直接在业务服务中进行使用。那么此时可以通过自定义注解进行实现,代码如下所示:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(value = LogAspect.class)
public @interface EnableLogAspect {
    /*
     * 通过Import注解导入日志切面类到Spring容器中
     */
}

五、保存日志数据

切面类环境搭建好后,我们在后台系统中开始使用。

首先在后台系统模块中引入日志模块的依赖坐标:
在这里插入图片描述
然后编写AsyncOperLogService的实现类,用来保存日志数据到数据库

@Service
public class AsyncOperLogServiceImpl implements AsyncOperLogService {
    @Autowired
    private SysOperLogMapper sysOperLogMapper;
    @Override
    public void saveSysOperLog(SysOperLog sysOperLog) {
        sysOperLogMapper.insert(sysOperLog);
    }
}

最后编写SQL语句:

@Mapper
public interface SysOperLogMapper {
    @Insert("insert into sys_oper_log values " +
            "(#{id}, #{title}, #{method}, #{requestMethod}, #{operatorType}, #{operName},\n" +
            "#{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, now(), now(), 0)")
    void insert(SysOperLog sysOperLog);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/472415.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【每日一题】数组元素的最小非零乘积

文章目录 Tag题目来源解题思路方法一&#xff1a;贪心 写在最后 Tag 【贪心】【快速幂】【数组】【2024-03-20】 题目来源 1969. 数组元素的最小非零乘积 解题思路 方法一&#xff1a;贪心 前言 我们首先来思考一个简单的问题&#xff1a;假设给定三个整数 a a a&#xf…

JMeter 并发测试和持续性压测详解

并发测试和持续性压测都是评估系统性能的常用方法&#xff0c;它们可以帮助开发人员发现并解决系统中的性能问题。本文来详细介绍下。 概念 并发测试&#xff1a; 旨在评估系统在同时处理多个用户请求时的性能。在这种 测试 中&#xff0c;系统会暴露于一定数量的用户负载下&…

CodeWhisperer插件

一、前言 产品官网地址&#xff1a;What is CodeWhisperer? - CodeWhisperer Amazon CodeWhisperer 是一个通用的、由机器学习驱动的代码生成器&#xff0c;可实时为您提供代码建议。在您编写代码时&#xff0c;CodeWhisperer 会根据您现有的代码和注释自动生成建议。您的个…

一招让你的Mac重获新生,CleanMyMac助你轻松清理无用垃圾!

一招让你的Mac重获新生&#xff0c;CleanMyMac助你轻松清理无用垃圾&#xff01; 告别卡顿&#xff0c;让你的Mac跑得更快更稳&#xff01; 在当今这个快节奏的生活中&#xff0c;我们的工作和生活早已离不开电脑。特别是对于Mac用户来说&#xff0c;一台轻巧、快捷、稳定的Mac…

代码随想录算法训练营Day51 ||leetCode 309.最佳买卖股票时机含冷冻期 || 714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 需要新添加状态 class Solution { public:int maxProfit(vector<int>& prices) {int n prices.size();if (n 0) return 0;vector<vector<int>> dp(n, vector<int>(4, 0));dp[0][0] - prices[0]; // 持股票for (i…

[.NET项目实战] Elsa开源工作流组件应用(三):实战演练

补充 之前的文章简单介绍了工作流和Elsa工作流库&#xff0c;这里再补充说明两点 工作流的使用场景非常广泛&#xff0c;几乎涵盖了所有需要进行业务流程自动化管理的领域。 学习一个开源库&#xff0c;最简单的方法就是看源码&#xff0c;Elsa的工作流引擎源码非常简单易懂&…

【Flutter 面试题】讲一讲 Dart 的一些重要概念?

【Flutter 面试题】讲一讲 Dart 的一些重要概念&#xff1f; 文章目录 写在前面口述回答补充说明完整代码运行结果详细说明 写在前面 &#x1f64b; 关于我 &#xff0c;小雨青年 &#x1f449; CSDN博客专家&#xff0c;GitChat专栏作者&#xff0c;阿里云社区专家博主&#…

【Leetcode每日一题】 递归 - 反转链表(难度⭐)(36)

1. 题目解析 题目链接&#xff1a;206. 反转链表 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 一、递归函数的核心任务 递归函数的主要职责是接受一个链表的头指针&#xff0c;并返回该链表逆序后的新头结点。递归…

mysql迁移达梦数据库 Java踩坑合集

达梦数据库踩坑合集 文章目录 安装达梦设置大小写不敏感Spring boot引入达梦驱动&#xff08;两种方式&#xff09;将jar包打入本地maven仓库使用国内maven仓库&#xff08;阿里云镜像&#xff09; 达梦驱动yml配置springboot mybatis-plus整合达梦,如何避免指定数据库名&…

如何在Windows系统使用VS Code制作游戏网页并实现无公网IP远程访问

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通过cpolar内网穿透发布到公网&#xff0c;分…

YZ系列工具之YZ08:窗体加载图片后进行放大查看

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套一部VBA手册&#xff0c;教程分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的…

Linux学习总结下

vim\vi编辑器 什么是vi\vim编辑器&#xff1f; 1、vi、vim编辑器&#xff0c;就是命令模式下的文本编辑器&#xff0c;用来编辑文件 2、vim是vi的升级版&#xff0c;一般用vim即可&#xff0c;包含vi所有功能 基础命令&#xff1f; vi 文件路径 vim 文件路径 运行模式 …

二、yocto 集成ros2(基于raspberrypi 4B)

yocto 集成ros2 yocto 集成ros21. 下载ros layer2. 编译集成ros3. 功能验证 yocto 集成ros2 本篇文章为基于raspberrypi 4B单板的yocto实战系列的第二篇文章。 一、yocto 编译raspberrypi 4B并启动 本节我们将ros2机器人操作系统移植到我们的yocto系统里面。 1. 下载ros laye…

LLM如何处理长上下文:Lost in the middle

论文地址&#xff1a;Lost in the Middle: How Language Models Use Long Contexts 论文总结&#xff1a;写prompt的时候&#xff0c;需要注意内容的顺序&#xff0c;把重要的信息放在最前面或者最后面。 大型语言模型大有用处&#xff0c;在设计 prompt 方面&#xff0c;人们…

当OKR无法按时完成或达成时,应如何进行调整?

在企业管理中&#xff0c;OKR&#xff08;Objectives and Key Results&#xff0c;目标与关键成果&#xff09;作为一种有效的管理工具&#xff0c;被广泛用于设定和跟踪目标。然而&#xff0c;在实际执行过程中&#xff0c;OKR无法按时完成或达成的情况时有发生。面对这种情况…

IO多分复用

#include<myhead.h> #define SER_PORT 8888 //服务器端口号 #define SER_IP "192.168.65.131" //服务器IPint main(int argc, const char *argv[]) {//1、创建一个套接字int sfd -1;sfd socket(AF_INET, SOCK_STREAM, 0); //参数1&#xff1a;…

高效快捷的快递查询助手,让您随时随地掌握包裹最新状态

面对一堆快递单号&#xff0c;您是否还在手忙脚乱地逐个复制粘贴到网上查询物流信息&#xff1f;是否还在为如何保存查询好的物流信息而犯愁&#xff1f;别担心&#xff0c;固乔快递查询助手来帮您解决这些烦恼&#xff01; 固乔快递查询助手是一款功能强大的快递单号查询与管理…

【Linux Day17 Libevent库】

Libevent 1.介绍 Libevent 是一个轻量级的开源高性能网络库&#xff0c;有几个显著的亮点&#xff1a; 事件驱动&#xff08;event-driven&#xff09;&#xff0c;高性能;轻量级&#xff0c;专注于网络&#xff0c;不如 ACE 那么臃肿庞大&#xff1b;线程安全。Libevent 使…

Java IO流之Netty实现聊天通信功能

文章目录 1 Netty1.1 概要设计1.1.1 技术选型1.1.2 数据库设计1.1.3 通信设计1.1.3.1 报文协议格式1.1.3.2 报文交互场景 1.2 Netty简单示例1.2.1 pom.xml1.2.2 发送和接收1.2.3 示例说明1.2.3.1 线程阻塞问题1.2.3.2 服务端和接收端 EventLoopGroup 1.3 Netty中handler概述1.4…

python中字典相关知识点总结

1.字典的定义 字典&#xff1a;在Python中&#xff0c;字典是一系列键-值对。每个键都与一个值相关联&#xff0c;程序员可以通过键来访问与之相关联的值。 实际举例&#xff1a; student{name:xincun,age:18} 通过实例我们可以发现&#xff0c;键-值对是两个相关联的值。指…