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

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

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

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

前言

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

效果图

在这里插入图片描述

如何实现

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

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

    @Asyncpublic 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();}@Asyncpublic 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.hqwc.cn/news/164314.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯每日一题2023.11.6

取位数 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 由题意我们知道len中为现阶段长度&#xff0c;如果其与k相等也就是找到了正确的位数&#xff0c;否则就调用递归来进行搜索&#xff0c;每次搜索一位数。 #include <stdio.h> // 求x用10进制表示时的数位长度 int …

最长非递减子序列,Python实现

from time import time from bisect import bisect from random import choices, seed from itertools import combinationsdef func1(seq):# 暴力穷举&#xff0c;从最长的子序列开始查找&#xff0c;大约耗时5小时for n in range(len(seq)-1, 0, -1): # 依次查找长度为len(se…

四阶龙格库塔与元胞自动机

龙格库塔法参考&#xff1a; 【精选】四阶龙格库塔算法及matlab代码_四阶龙格库塔法matlab_漫道长歌行的博客-CSDN博客 龙格库塔算法 Runge Kutta Method及其Matlab代码_龙格库塔法matlab_Lzh_023016的博客-CSDN博客 元胞自动机参考&#xff1a; 元胞自动机&#xff1a;森林…

密度聚类与层次聚类

大家好&#xff0c;我是带我去滑雪&#xff01; 密度聚类&#xff08;Density-based Clustering&#xff09;和层次聚类&#xff08;Hierarchical Clustering&#xff09;是两种不同的聚类方法&#xff0c;用于将数据集中的数据点分组成簇。 目录 一、密度聚类 &#xff08;…

面试题:线上MySQL的自增id用尽怎么办?

文章目录 前言表定义自增值idInnoDB系统自增row_idXidInnodb trx_id InnoDB数据可见性的核心思想为什么要加248&#xff1f;为何只读事务不分配trx_id&#xff1f;thread_id 总结 前言 MySQL的自增id都定义了初始值&#xff0c;然后不断加步长。虽然自然数没有上限&#xff0c…

【广州华锐互动】气象卫星监测AR互动教学软件为气象学习带来更多乐趣

由VR制作公司广州华锐互动开发的气象卫星监测AR互动教学软件是一款结合了增强现实(AR)技术与气象监测技术的教育软件。它通过直观、互动的方式&#xff0c;帮助学生更好地理解和掌握气象监测的基本知识和技能。本文将从气象卫星监测AR互动教学软件的应用场景、优势分析、实际意…

怎样选择文件外发控制系统,让数据实现高效安全交换?

制造型企业都非常重视其知识产权&#xff08;IP&#xff09;的安全性&#xff0c;尤其是其最有价值的产品设计数据的安全问题。基于复杂的供应链生态&#xff0c;每天可能要与几十家甚至上百家供应商及合作伙伴进行数据交换。不管是一级还是二级供应商&#xff0c;合作伙伴还是…

pycharm pro v2023.2.4(Python开发)

PyCharm是一种Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;PyCharm提供了强大的功能&#xff0c;包括语法突出显示、智能代码完成、代码检查、自动重构和调试等特性&#xff0c;这些都可以帮助Python开发人员更加高效地编写代码。 PyCharm Pro是PyCharm的高级版…

Ripro-V5 6.4最新版 不限域名无限搭建(授权激活文件)

RiPro主题全新V5版本&#xff0c;是一个优秀且功能强大、易于管理、现代化的WordPress虚拟资源商城主题。支持首页模块化布局和WP原生小工具模块化首页可拖拽设置&#xff0c;让您的网站设计体验更加舒适。同时支持了高级筛选、自带会员生态系统、超全支付接口等众多功能&#…

百度上线“文心一言”付费版本,AI聊天机器人市场竞争加剧

原创 | 文 BFT机器人 百度不愧是我国AI技术领域的先行者&#xff0c;每年致力于人工智能领域取得技术产品的突破和创新。据爆料称&#xff0c;百度的文心一言有突破了新境界&#xff0c;开创了文心大模型4.0会员版本。从线上的to C产品到试水商业化&#xff0c;百度都是争先走…

接口测试工具

接口测试的重要性 接口测试&#xff1a; 直接对后端服务的测试&#xff0c;是服务端性能测试的基础&#xff0c;是测试工程师的必备技能。 接口测试的概念 接口&#xff1a;系统之间数据交互的通道 接口测试&#xff1a;校验接口响应数据与预期数据是否一致 接口信息解析 …

elform-item动态prop

先来看看我这个变态而又复杂的需求&#xff01; 目前自定义表单的前端开发越来越热&#xff0c;开发人员封装好成熟的组件&#xff0c;用户直接拖动生成自己的页面&#xff01;这样的特点就是&#xff1a; 页面中显示的东西&#xff0c;完全是自定义组合的而不是固定的&#…