我们如何实现业务操作日志功能?

news2024/10/7 16:19:14

1. 需求

我们经常会有这样的需求,需要对关键的业务功能做操作日志记录,也就是用户在指定的时间操作了哪个功能,操作前后的数据记录,必要的时候可以一键回退,今天我就为大家实现这个的功能,让大家可以直接拿来用。

1.1 日志分类

  1. 系统日志:指的是程序执行过程中的关键步骤记录,根据实际输出的debug、info、warn、error等级别的执行记录信息,主要用来记录核心参数以及返回值,同时为了在出现问题的时候能够快速排查问题
  2. 操作日志:它是用户实际业务操作行为的记录,主要是为了分析用户的行为偏好,一方面用来对业务进行分析,开发人员也可以针对访问的频率去提高特定接口的性能。

2. 设计思路

首先我们得要分析具体要实现这个功能,都需要记录哪些信息,然后怎么要去实现这个功能?
具体功能点:

  1. 记录用户的业务操作行为,具体字段有:操作人、操作时间、操作功能、日志类型、操作内容描述、操作内容、操作前内容。
  2. 需要对外提供页面和管理功能,以便查询追溯和数据回滚。

我们首先要想到,要实现这个功能,最好的办法就是需要和业务逻辑进行解耦,因为它是一种业务辅助功能,同时是对所有业务的一种横向操作,那我们使用什么技术,是不是就呼之欲出了?最容易想到的就是AOP切面+自定义注解

2.1 实现步骤

1、首先定义操作日志注解,注解内定义一些属性,如操作功能名称、描述等;

2、将自定义注解标记在需要进行业务操作记录的方法上,一般查询不需要。

3、定义切入点,编写切面:切入点就是标记业务操作日志注解的目标方法;切面就是保存业务操作日志信息。

对了,在这里我想给大家说一下咱们经常用到的几个技术的区别,过滤器,拦截器,SpringAOP,这个也是我经常的困惑。

2.2 SpringAOP、过滤器、拦截器对比

在匹配中同一目标时,过滤器、拦截器、SpringAOP的执行优先级是:过滤器>拦截器>SpringAOP,执行顺序是先进后出,主要有如下区别:

  1. 过滤器(Filter)
    过滤器,就是起到过滤筛选作用的一种事物,只不过这里的过滤器过滤的对象是客户端访问的web资源。也可以理解为一种预处理手段,对资源进行拦截后,将其中我们认为的杂质(用户自己定义的)过滤,符合条件的放行,不符合的则拦截下来。
    过滤器常见的使用场景:统一设置编码,过滤敏感字符,登录校验,URL级别的访问权限控制,数据压缩。
    在这里插入图片描述
    2.拦截器(Interceptor)
    拦截器是springmvc提供的,类似于过滤器。主要用于拦截用户请求并作相应的处理。
    拦截器的使用场景: 日志记录,权限校验,登录校验,性能检测,经常会用在网关处。
    在这里插入图片描述
    3:AOP
    AOP拦截的是类的元数据(包、类、方法名、参数等),AOP针对具体的代码,能够实现更加复杂的业务逻辑。
    使用的场景:日志记录,性能统计,安全控制,事务处理,异常处理。

3. 具体实现

3.1 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.2 数据库表设计

create table if not exists bus_log
(
   id bigint auto_increment comment '自增id'
      primary key,
   bus_name varchar(100) null comment '业务名称',
   bus_descrip varchar(255) null comment '业务操作描述',
   oper_person varchar(100) null comment '操作人',
   oper_time datetime null comment '操作时间',
   ip_from varchar(50) null comment '操作来源ip',
   param_file varchar(255) null comment '操作参数报文文件'
)
comment '业务操作日志' default charset ='utf8';

3.3 代码实现

  1. 定义日志注解
/**
 * 业务日志注解
 * 可以作用在控制器或其他业务类上,用于描述当前类的功能;
 * 也可以用于方法上,用于描述当前方法的作用;
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface BusLog {
 
 
    /**
     * 功能名称
     * @return
     */
    String name() default "";
 
    /**
     * 功能描述
     * @return
     */
    String descrip() default "";
 
}
  1. 编写切面

主要步骤是:在环绕通知内执行过目标方法后,获取目标类、目标方法上的业务日志注解上的功能名称和功能描述, 把方法的参数报文写入到文件中,最后保存业务操作日志信息;

@Component
@Aspect
@Slf4j
public class BusLogAop implements Ordered {
    @Autowired
    private BusLogDao busLogDao;
 
    /**
     * 定义BusLogAop的切入点为标记@BusLog注解的方法
     */
    @Pointcut(value = "@annotation(com.wuk.BusLog)")
    public void pointcut() {
    }
 
    /**
     * 业务操作环绕通知
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {
        log.info("----BusAop 环绕通知 start");
        //执行目标方法
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //目标方法执行完成后,获取目标类、目标方法上的业务日志注解上的功能名称和功能描述
        Object target = proceedingJoinPoint.getTarget();
        Object[] args = proceedingJoinPoint.getArgs();
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        BusLog anno1 = target.getClass().getAnnotation(BusLog.class);
        BusLog anno2 = signature.getMethod().getAnnotation(BusLog.class);
        BusLogBean busLogBean = new BusLogBean();
        String logName = anno1.name();
        String logDescrip = anno2.descrip();
        busLogBean.setBusName(logName);
        busLogBean.setBusDescrip(logDescrip);
        busLogBean.setOperPerson("wuk");
        busLogBean.setOperTime(new Date());
        JsonMapper jsonMapper = new JsonMapper();
        String json = null;
        try {
            json = jsonMapper.writeValueAsString(args);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        //把参数报文写入到文件中
        OutputStream outputStream = null;
        try {
            String paramFilePath = System.getProperty("user.dir") + File.separator + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + ".log";
            outputStream = new FileOutputStream(paramFilePath);
            outputStream.write(json.getBytes(StandardCharsets.UTF_8));
            busLogBean.setParamFile(paramFilePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
 
            }
        }
        //保存业务操作日志信息
        this.busLogDao.insert(busLogBean);
        log.info("----BusAop 环绕通知 end");
        return result;
    }
 
    @Override
    public int getOrder() {
        return 1;
    }
}
  1. 业务接口添加注解
@RestController
@Slf4j
@RequestMapping("/person")
public class PersonController {
    @Autowired
    private IPersonService personService;
 
    @PostMapping
    @BusLog(name="添加人员信息",descrip = "添加人员信息")
    public Person add(@RequestBody Person person) {
        Person result = this.personService.registe(person);
        log.info("//增加person执行完成");
        return result;
    }
    
    @PutMapping
    @BusLog(name="修改人员信息",descrip = "修改人员信息")
    public void edit(@RequestBody Person person) {
         this.personService.update(person);
    }
    
    @DeleteMapping
    @BusLog(name="删除人员信息", descrip = "删除人员信息")
    public void delete(@PathVariable(name = "id") Integer id) {
         this.personService.delete(id);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

哈尔滨工业大学计算机考研分析

关注我们的微信公众号 姚哥计算机考研 更多详情欢迎咨询 哈尔滨工业大学&#xff08;A&#xff09;考研难度&#xff08;☆☆☆☆☆&#xff09; 哈尔滨工业大学计算机考研招生学院是计算学部、计算学部&#xff08;深圳&#xff09;和计算学部&#xff08;威海&#xff09;…

C++完成烧烤节管理系统

背景&#xff1a; 这次我们结合今年淄博烧烤做一个餐厅管理系统&#xff0c;具体需求如下&#xff0c;我们选择的是餐饮商家信息管理 问题描述&#xff1a; 淄博烧烤今年大火&#xff0c;“进淄赶烤”是大家最想干的事情&#xff0c;淄博烧烤大火特火的原因&#xff0c;火的…

C语言之文件的读写(1)

前面三部分已经给大家介绍过了&#xff0c;网址发给大家方便大家复习 打开方式如下&#xff1a; 文件使用方式 含义 如果指定文件不存在 “r”&#xff08;只读&#xff09; 为了输入数据&#xff0c;打开一个已经存在的文本文件 出错 “w”&#xff08;只写&#xff09; 为了输…

文心一言眼里的Java世界

目录 一、Java基础教程系列二、先听听文心一言怎么说&#xff1f;三、话不多说&#xff0c;开干。1、要有一个正确的Java学习路线&#xff0c;做一个细致的Java学习规划。2、学习资料推荐3、书中自有黄金屋&#xff0c;书中自有颜如玉4、自学周期推荐5、效率为先6、哪吒的学习方…

Redis 数据分布优化:如何应对数据倾斜?

Redis 核心技术与实战 笔记 作者&#xff1a; 蒋德钧 在切片集群中&#xff0c;数据会按照一定的分布规则分散到不同的实例上保存。比如&#xff0c;在使用 Redis Cluster 或 Codis 时&#xff0c;数据都会先按照 CRC 算法的计算值对 Slot&#xff08;逻辑槽&#xff09;取模&a…

Hi3861开发第一节:环境搭建,并顺利完成编译

本次教程在纯Windows下环境搭建&#xff01;&#xff01;! 1.DecEco Device Tool下载和安装 步骤一&#xff1a;下载devicetool-windows-tool-3.1.0.400.zip版&#xff0c;下载网址&#xff1a;https://device.harmonyos.c om/cn/develop/ide#download 步骤二&#xff1a;解压…

Win10连接网络打印机提示0x0000052e?

Win10连接网络打印机提示0x0000052e&#xff1f;Win10电脑中用户连接网络打印机的时候&#xff0c;出现了错误代码0x0000052e&#xff0c;导致用户无法正常使用网络打印机&#xff0c;这时候用户可以通过卸载最新补丁、替换系统文件并修改注册表等方法来解决问题。 方法一&…

Spring6 i18n国际化

随着互联网的发展&#xff0c;越来越多的企业和个人开始关注全球化的需求。在这个背景下&#xff0c;多语言支持成为了一个重要的课题。Spring框架作为一款优秀的Java开发框架&#xff0c;提供了丰富的i18N支持&#xff0c;能帮助搬砖工快速实现多语言应用。 1、i18n概述 国际…

【Android开发基础】计算器逻辑层代码补充

文章目录 一、引言二、设计1、案例2、算法设计 三、编码1、UI界面设计&#xff08;1&#xff09;按钮样式设计&#xff08;2&#xff09;主界面布局设计 2、编码&#xff08;1&#xff09;控件初始化&#xff08;2&#xff09;事件监听器 四、附件 一、引言 描述&#xff1a;关…

【深度学习】4-1 误差反向传播法 - 计算图链式法则反向传播

上一章中神经网络的学习是通过数值微分计算的神经网络的权重参数的梯度。数值微分虽然简单&#xff0c;也容易实现&#xff0c;但缺点是计算上比较费时间。本章将学习一个能够高效计算权重参数的梯度的方法一一误差反向传播法。 误差反向传播法能够高效计算权重参数的梯度的方法…

Angular 安装与创建第一项目

1. 下载nodejs 并且安装 https://nodejs.org/en 2. 打开命令窗口&#xff0c;验证是否安装成功 C:\Users\Harry>node -v v18.16.0C:\Users\Harry>npm -v 9.5.1 3. 安装Angular CLI C:\Users\Harry>npm install -g angular/cliadded 239 packages in 9s npm notic…

Python Anaconda创建虚拟环境及Pycharm使用虚拟环境

目录 前言 一、Anaconda与Pycharm 二、conda常用命令 三、Pycharm使用虚拟环境 总结 前言 我们在做开发任务时可能会创建多个项目&#xff0c;这些项目可能会依赖于不同的Python环境。比如有的用到Python3.6、有的用到Python3.7&#xff1b;有的用Pytorch开发、有的用Tens…

SpringBoot整合模板引擎Thymeleaf(4)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 概述 在之前的教程中&#xff0c;我们介绍了Thymeleaf的基础知识。在此&#xff0c;以案例形式详细介绍Thymeleaf的基本使用。 项目结构 要点概述&#xff1a; 1、在st…

性能优化往往成为 Android 高工的一道分水岭

不论是大厂小厂&#xff0c;对于Android开发者来说&#xff0c;性能优化往往成为了是否真正配得上高级开发的一道分水岭&#xff0c;性能优化也是如今大厂在招聘要求中作出要求&#xff0c;且会高频提问&#xff1a; Android的性能优化&#xff0c;主要从以下几个方面开展&…

论文解读|基于RealSense的三维散乱部件点云分割

原创 | 文 BFT机器人 01 摘要 本文提出了一种针对垃圾拾取系统中点云分割的算法。该算法使用低成本的深度相机RealSense获取点云数据&#xff0c;并对点云数据进行滤波处理和分割&#xff0c;最终将分割后的子块片段独立地连接起来&#xff0c;形成完整的工件模型。通过测试案…

spark 数据倾斜处理

spark优化总结: 一、spark 代码优 六大代码优化: 避免创建重复的RDD 尽可能复用同一个RDD 对多次使用的RDD进行持久化 尽量避免使用shuffle类算子 使用map-side预聚合的shuffle操作 使用高性能的算子 广播大变量 使用Kryo优化序列化性能 优化数据结构 使用高性能的库fa…

怎么通过电商数据分析选择好货源?

什么样的货源才算好货源&#xff1f;自然是拿货成本低、销售前景&#xff08;趋势&#xff09;好、利润度高、去库存快的。这就需要综合销售、库存、财务、采购等多部门环节的数据进行分析挖掘&#xff0c;最终才能找到符合需求的供货商以及商品清单。在这个过程中&#xff0c;…

vue3引入uview-plus3.0移动组件库

vue3引入uview-plus3.0移动组件库 引入流程 导入插件到项目 项目地址&#xff1a;https://ext.dcloud.net.cn/plugin?nameuview-plus 在main.js引入uview // main.js import uviewPlus from /uni_modules/uview-plus// #ifdef VUE3 import { createSSRApp } from vue expor…

红日ATTCK系列靶场(-)简记

Step 1>》信息收集 nmap 发现80、 3306 nmap -T4 -O 192.168.92.100 访问80端口 dirsearch(御剑)扫描 发现&#xff1a;/phpMyadmin Step 2 》漏洞利用 1.弱口令 http://192.168.92.100/phpMyadmin root/root 登录成功 2.getshell select basedir //查绝对路径 int…

Flink 学习三 Flink 流 process function API

Flink 学习三 Flink 流&process function API 1.Flink 多流操作 1.1.split 分流 (deprecated) 把一个数据流根据数据分成多个数据流 1.2 版本后移除 1.2.分流操作 (使用侧流输出) public class _02_SplitStream {public static void main(String[] args) throws Excep…