Java系统操作日志之数据修改变化记录

news2024/12/27 14:01:19

系统操作日志之数据修改变化记录

    • 前言
    • 效果图
    • 如何实现
    • 总结

相信大家在自己的系统中都有记录日志吧,像登录日志、操作日志等等
但是一般来说,大家都是获取操作了什么东西,包括它的参数,模块等等,但是你有关心过他的数据变化吗?

前言

如果你想优雅的设计操作日志模块,请参考 美团设计的操作日志模块
真的,阅读后感觉,优雅、实在是优雅至极。

效果图

在这里插入图片描述

如何实现

毋庸置疑,肯定需要两次查询,不然你怎么得到前后的数据呢?
你可以在修改前进行查询一次数据,然后再修改后查询数据,当然你是用切面的话当我没说!

这里给大家看下如何比对两次数据的变化,可以通过map来实现,上代码!

    @Async
    public String getPayInfoMapDiff(Map<String, ?> oldMap, Map<String, ?> newMap) {
        // 返回结果
        StringBuilder result = new StringBuilder();
        // 可以先判断key的总数是否相同,判断是删除了,还是新增了
        if (oldMap.size() > newMap.size()) {
            // 删除,找到少了哪些
            Set<String> diffKey = oldMap.keySet().stream().filter(s -> !newMap.containsKey(s)).collect(Collectors.toSet());
        } else {
            // 新增,增加了哪些
            Set<String> diffKey = newMap.keySet().stream().filter(s -> !oldMap.containsKey(s)).collect(Collectors.toSet());
            for (String key : diffKey) {
                Map<String, Object> value = JSON.parseObject(JSON.toJSONString(newMap.get(key)), Map.class);
                String context = "";
                if (value.get("paymentSingleMinCap") != null) {
                    context += String.format(I18nUtil.get("insert.info.log"), I18nUtil.get("paymentSingleMinCap"), value.getOrDefault("paymentSingleMinCap", ""));
                    context += String.format(I18nUtil.get("insert.info.log"), I18nUtil.get("paymentSingleMaxCap"), value.getOrDefault("paymentSingleMaxCap", ""));
                    context += String.format(I18nUtil.get("insert.info.log"), I18nUtil.get("paymentTotalCap"), value.getOrDefault("paymentTotalCap", ""));
                }
                context += String.format(I18nUtil.get("insert.info.log"), I18nUtil.get("acitve.status"), value.getOrDefault("active", "").equals(1) ? I18nUtil.get("active") : I18nUtil.get("inactive"));
                context += String.format(I18nUtil.get("insert.info.log"), I18nUtil.get("Rate"), value.getOrDefault("paymentRate", "") + "‰");
                result.append(I18nUtil.get(value.get("paymentName").toString())).append(" ").append(context);
            }
        }

        for (String key : newMap.keySet()) {
            for (String s : oldMap.keySet()) {
                String context = "";
                if (s.equals(key)) {
                    Map<String, Object> oldValue = JSON.parseObject(JSON.toJSONString(oldMap.get(key)), Map.class);
                    Map<String, Object> newValue = JSON.parseObject(JSON.toJSONString(newMap.get(key)), Map.class);
                    if (!oldValue.getOrDefault("paymentSingleMinCap", "").equals(newValue.getOrDefault("paymentSingleMinCap", ""))) {
                        context += String.format(I18nUtil.get("update.info.log"), I18nUtil.get("paymentSingleMinCap"), oldValue.getOrDefault("paymentSingleMinCap", ""), newValue.getOrDefault("paymentSingleMinCap", ""));
                    }
                    if (!oldValue.getOrDefault("paymentSingleMaxCap", "").equals(newValue.getOrDefault("paymentSingleMaxCap", ""))) {
                        context += String.format(I18nUtil.get("update.info.log"), I18nUtil.get("paymentSingleMaxCap"), oldValue.getOrDefault("paymentSingleMaxCap", ""), newValue.getOrDefault("paymentSingleMaxCap", ""));
                    }
                    if (!oldValue.getOrDefault("paymentTotalCap", "").equals(newValue.getOrDefault("paymentTotalCap", ""))) {
                        context += String.format(I18nUtil.get("update.info.log"), I18nUtil.get("paymentTotalCap"), oldValue.getOrDefault("paymentTotalCap", ""), newValue.getOrDefault("paymentTotalCap", ""));
                    }
                    if (!oldValue.get("active").equals(newValue.get("active"))) {
                        context += String.format(I18nUtil.get("update.info.log"), I18nUtil.get("acitve.status"), oldValue.getOrDefault("active", "").equals(1) ? I18nUtil.get("active") : I18nUtil.get("inactive"), newValue.getOrDefault("active", "").equals(1) ? I18nUtil.get("active") : I18nUtil.get("inactive"));
                    }
                    if (!oldValue.get("paymentRate").equals(newValue.get("paymentRate"))) {
                        context += String.format(I18nUtil.get("update.info.log"), I18nUtil.get("Rate"), oldValue.getOrDefault("paymentRate", "") + "‰", newValue.getOrDefault("paymentRate", "") + "‰");
                    }
                    if (!"".equals(context)) {
                        result.append(I18nUtil.get(newValue.get("paymentName").toString())).append(" ").append(context);
                    }
                }
            }

        }
        return "".equals(result.toString()) ? "无修改" : result.toString();


    }

    @Async
    public String getBaseInfoDiff(Map<String, Object> oldMap, Map<String, Object> newMap) {
        StringBuilder result = new StringBuilder();
        for (String key : newMap.keySet()) {
            StringBuilder context = new StringBuilder();
            if (key.contains("updateTime")) {
                continue;
            }
            if (key.contains("accountInfo")) {
                // 如果它是空的
                Map<String, Object> oldValue = new HashMap<>();
                if (!StringUtils.isBlank(oldMap.get(key).toString())){
                    oldValue = JSON.parseObject(oldMap.get(key).toString(), Map.class);
                }
                Map<String, Object> newValue = JSON.parseObject(newMap.get(key).toString(), Map.class);
                for (String innerKey : newValue.keySet()) {
                    StringBuilder msg = new StringBuilder();
                    // 说明是新增的
                    if (!oldValue.containsKey(innerKey)){
                        msg.append(String.format(I18nUtil.get("update.info.log"), I18nUtil.get(innerKey), I18nUtil.get(oldValue.getOrDefault(innerKey, "").toString()), I18nUtil.get(newValue.getOrDefault(innerKey, "").toString())));
                    }
                    for (String s : oldValue.keySet()) {
                        if (s.equals(innerKey) && !oldValue.getOrDefault(innerKey, "").toString().equals(newValue.getOrDefault(innerKey, "").toString())) {
                            msg.append(String.format(I18nUtil.get("update.info.log"), I18nUtil.get(innerKey), I18nUtil.get(oldValue.getOrDefault(innerKey, "").toString()), I18nUtil.get(newValue.getOrDefault(innerKey, "").toString())));
                        }
                    }
                    context.append(msg);
                }
            } else if ("companyInfo".equals(key)) {
                Map<String, Object> oldValue = JSON.parseObject(JSON.toJSONString(oldMap.get("companyInfo")), Map.class);
                Map<String, Object> newValue = JSON.parseObject(JSON.toJSONString(newMap.get("companyInfo")), Map.class);
                for (String innerKey : newValue.keySet()) {
                    StringBuilder mes = new StringBuilder();
                    for (String s : oldValue.keySet()) {
                        if (s.equals(innerKey) && !oldValue.getOrDefault(innerKey, "").toString().equals(newValue.getOrDefault(innerKey, "").toString())) {
                            mes.append(String.format(I18nUtil.get("update.info.log"), I18nUtil.get(innerKey), I18nUtil.get(oldValue.getOrDefault(innerKey, "").toString()), I18nUtil.get(newValue.getOrDefault(innerKey, "").toString())));
                        }
                    }
                    context.append(mes);
                }
            } else {
                for (String s : oldMap.keySet()) {
                    if (s.equals(key) && !newMap.getOrDefault(key, "").toString().equals(oldMap.getOrDefault(key, "").toString())) {
                        context.append(String.format(I18nUtil.get("update.info.log"), I18nUtil.get(key), I18nUtil.get(oldMap.getOrDefault(key, "").toString()), I18nUtil.get(newMap.getOrDefault(key, "").toString())));
                    }
                }
            }
            if (!"".equals(context.toString())) {
                result.append(context);
            }
        }
        return "".equals(result.toString()) ? "无修改" : result.toString();

    }


这个里面有许多东西可以改动,比如说i18n啊,以及对象中有JSON该如何转换,这些都是可以修改的,根据你的业务需要去进行修改即可,这里只是做一个基础版作为参考。

总结

这是最笨的方法,大家有更好的方法也可以评论告诉大家哦,谢谢!
其实把前后两个对象转为map或者json都是可以的,大家都可以试试
公司这个记录操作日志功能是没有用到切面的。

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

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

相关文章

06【保姆级】-GO语言的运算符

之前我学过C、Java、Python语言时总结的经验&#xff1a; 先建立整体框架&#xff0c;然后再去抠细节。先Know how&#xff0c;然后know why。先做出来&#xff0c;然后再去一点点研究&#xff0c;才会事半功倍。适当的囫囵吞枣。因为死抠某个知识点很浪费时间的。对于GO语言&a…

使用MCU上的I2C总线进行传感器应用

使用MCU上的I2C总线进行传感器应用是嵌入式系统开发中常见的任务&#xff0c;本文将介绍在MCU上实现I2C总线传感器应用的相关技术和流程。 首先&#xff0c;I2C&#xff08;Inter-Integrated Circuit&#xff09;总线是一种常用的串行通信协议&#xff0c;用于连接多个设备&am…

2023/11/8JAVA学习

多个条件也可以&&放在一块,支持链式编程 map()数据加工,一个对象转化为另一个 不能这样写 不会去重报错

java入门,记一次mysql函数使用

一、前言 记一次mysql函数使用&#xff0c;要求给一个字段进行拼接&#xff0c;然后MD5加密&#xff0c;再转换成大写。这里都是有现成的函数&#xff0c;所以记录下来 二、函数使用 1、拼接函数&#xff1a; concat(字符串1,字符串2) select concat(字符串1,字符串2); 2、…

Postman使用方法详细攻略

一、Postman背景介绍 用户在开发或者调试网络程序或者是网页B/S模式的程序的时候是需要一些方法来跟踪网页请求的&#xff0c;用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具。今天给大家介绍的这款网页调试工具不仅可以调试简单的css、html、脚本等简单的网…

APISpace IP归属地查询接口案例代码

1.IP归属地查询API 1.1 API接口简介 IP归属地查询API&#xff1a;根据IP地址查询归属地信息&#xff0c;包含国家、省、市、区县和运营商等信息。APISpace 提供了IPv4 和 IPv6 的IP归属地查询接口&#xff0c;并且包含了各种归属地精度查询的接口。 1.2 IPv4 IPv4归属地查询…

数据结构与算法-(11)---有序表(OrderedList)

&#x1f308;个人主页: Aileen_0v0 &#x1f525;系列专栏:PYTHON学习系列专栏 &#x1f4ab;"没有罗马,那就自己创造罗马~" 目录 知识回顾及总结 有序表的引入 ​编辑 实现有序表 1.有序表-类的构造方法 2.有序表-search方法的实现 3.有序表-add方法的实现…

电气元器件的型号,符号,接线认识(一)

目录 按钮开关 类型 文字与图形符号 常开常闭触点 接线图 中间继电器 实物图 文字与图形符号 接线图 交流接触器 实物图 文字与图形符号 接线图 开关电源 图形 文字与图形符号 接线图 断路器 实物图 类型 文字与图形符号 接线图 今天让我们来认识一下电路元…

ci-cd的流程

1、项目在gitlab上&#xff0c;从gitlab上使用git插件获取源码&#xff0c;构建成war包&#xff0c;所以使用tomcat作为运行环境 发布 &#xff1a;使用maven插件发布&#xff0c;使用ssh连接。

大众动力总成构建全程数字化的数电票管理平台

客户简介 上海大众动力总成有限公司成立于2005年4月29日&#xff0c;是德国大众在中国的全资投资公司大众汽车&#xff08;中国&#xff09;投资有限公司和上海汽车集团股份有限公司合资组建的发动机生产企业&#xff0c;是世界上加工工艺和技术水平最先进的发动机生产基地之一…

kubernetes集群编排(8)

k8s资源监控 资源限制 上传镜像 [rootk8s2 limit]# vim limit.yaml apiVersion: v1 kind: Pod metadata:name: memory-demo spec:containers:- name: memory-demoimage: stressargs:- --vm- "1"- --vm-bytes- 200Mresources:requests:memory: 50Milimits:memory: 100…

使用EvoMap/Three.js模拟无人机灯光秀

一、创建地图对象 首先我们需要创建一个EM.Map对象&#xff0c;该对象代表了一个地图实例&#xff0c;并设置id为"map"的文档元素作为地图的容器。 let map new EM.Map("map",{zoom:22.14,center:[8.02528, -29.27638, 0],pitch:71.507,roll:2.01,maxPit…

在linux中配置固定ip

1、在虚拟机中配置网络&#xff0c;改ONBOOTyes后默认直接DHCP自动获取ip&#xff0c;但是这种方法会导致ip改变。这个时候&#xff0c;我们需要配置固定ip 2、进入/etc/sysconfig/network-scripts查看ifcfg-ens33 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOdh…

Flink之状态管理

Flink状态管理 状态概述状态分类 键控、按键分区状态概述值状态 ValueState列表状态 ListStateMap状态 MapState归约状态 ReducingState聚合状态 Aggregating State 算子状态概述列表状态 ListState联合列表状态 UnionListState广播状态 Broadcast State 状态有效期 (TTL)概述S…

Kubernetes7大网络插件之Antrea

目录 前言 Antrea简介 Antrea安装使用 下载Antrea YAML文件 编辑YAML文件 应用YAML文件 等待安装完成 配置网络策略 测试 创建命名空间 部署两个Nginx Pod 定义网络策略 测试网络策略 前言 在现代容器化应用程序的世界中&#xff0c;容器编排平台Kubernetes已经成为…

Mybatis-Plus同时使用逻辑删除和唯一索引的问题及解决办法

1 问题背景 在开发中&#xff0c;我们经常会有逻辑删除和唯一索引同时使用的情况。但当使用mybatis plus时&#xff0c;如果同时使用逻辑删除和唯一索引&#xff0c;会报数据重复Duplicate entry的问题。 举例来说&#xff0c;有表user&#xff0c;建立唯一索引&#xff08;u…

配置Linux为无线路由器

配置Linux无线路由器 将Linux配置为无线路由器。使用hostapd&#xff0c;可以配置无线网卡为AP模式。 这里使用buildroot来生成这个工具。Wi-Fi模块使用的是 rt8188eus。 1. 内核配置 2. buildroot配置 开启 rt8188eus 驱动 3. 启动hostapd 系统启动后&#xff0c;会自动加…

前端特殊字符转码

前端特殊字符转码 建议 最好不要传名称&#xff0c;传ID 是在不行就用这个方法 name encodeURIComponent(name),

【LeetCode力扣】42.接雨水(困难)

目录 1、题目介绍 2、解题 2.1、解题思路 2.2、图解说明 2.3、解题代码 1、题目介绍 原题链接&#xff1a;42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,…

c语言练习第10周(6~10)

输入样例S -4.8 -8.0 -2.9 6.7 -7.0 2.6 6.5 1.7 1.9 5.6 -1.6 -6.3 -4.3 1.5 8.7 -0.3 5.4 -9.3 4.8 7.0 3.6 -8.3 -1.0 1.3 -9.9 9.7 -6.3 5.8 2.9 2.9 -7.7 4.9 -0.6 7.2 6.4 7.7 2.8 -5.8 -0.0 2.2 4.0 7.7 -3.0 -7.5 -3.5 9.7 …