一款通用的数据编辑历史记录工具,用来解决用户编辑业务数据留痕的问题

本文主要是介绍一款通用的数据编辑历史记录工具,用来解决用户编辑业务数据留痕的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为了只在业务代码里只写一行代码我容易吗我? 最终使用效果:

        // 记录编辑信息UserEditRecordBee.BASE_USER_EDIT.record(user);// 编辑用户userMapper.updateById(user);

UserEditRecordBee定义:

public enum UserEditRecordBee implements RecordBee {BASE_USER_EDIT("编辑用户基本信息");private String recordName;@Overridepublic String recordType() {return this.name().toLowerCase();}@Overridepublic String recordName() {return this.recordName;}
}

RecordBee 定义:

public interface RecordBee {String recordType();String recordName();default String idFieldName() {return "id";}default <T> void record(T newEntity) {Object id = ZYBeanUtils.getProperty(newEntity, idFieldName());if (ZYStrUtils.isNotNull(id)) {ZYEditRecordHelper.record(recordType(), String.valueOf(id), newEntity, recordName());}}default <T> void record(T newEntity, String businessId) {ZYEditRecordHelper.record(recordType(), businessId, newEntity, recordName());}default <T> void record(String businessId, T newEntity, T oldEntity) {ZYEditRecordHelper.record(recordType(), businessId, newEntity, oldEntity, recordName());}default <T> void record(String businessId, T newEntity, T oldEntity, String recordUserId, String recordUserName) {ZYEditRecordHelper.record(recordType(), businessId, newEntity, oldEntity, recordName(), recordUserId, recordUserName);}
}

ZYEditRecordHelper定义:

// InterestedClassAware接口参考本人博文:
// https://blog.csdn.net/qq_37148232/article/details/131821497?spm=1001.2014.3001.5501
// EasyApplicationRunner 接口参考本人博文:
// https://blog.csdn.net/qq_37148232/article/details/122296078?spm=1001.2014.3001.5501
public class ZYEditRecordHelper implements InterestedClassAware, EasyApplicationRunner {private static final Map<String, BaseMapper<?>> MAPPER_CONTAINER = new HashMap<>();private static final Map<String, Class<?>> MAPPER__CLASS_CONTAINER = new HashMap<>();private static EditRecordMapper editRecordMapper;private static EditRecordDetailMapper editRecordDetailMapper;@Autowiredpublic ZYEditRecordHelper(EditRecordMapper editRecordMapper, EditRecordDetailMapper editRecordDetailMapper) {ZYEditRecordHelper.editRecordMapper = editRecordMapper;ZYEditRecordHelper.editRecordDetailMapper = editRecordDetailMapper;}public static <T> void record(String recordType, String businessId, T newEntity, String recordName) {if (ZYStrUtils.isAnyNull(recordType, businessId, newEntity)) {return;}Class<?> aClass = newEntity.getClass();String entityClassName = aClass.getName();// 从缓存中找到entity对应的那个BaseMapperBaseMapper<?> baseMapper = MAPPER_CONTAINER.get(entityClassName);if (null == baseMapper) {return;}if (ZYStrUtils.isNotNull(businessId)) {// 利用BaseMapper获取到旧的数据记录行。T oldEntity = (T) baseMapper.selectById(businessId);// 记录新旧记录record(recordType, businessId, newEntity, oldEntity, recordName);}}public static <T> void record(String recordType, String businessId, T newEntity, T oldEntity, String recordName) {if (ZYStrUtils.isAnyNull(recordType, businessId, newEntity, oldEntity)) {return;}// 处理下是否有登录用户,没有默认系统修改LoginUser loginAreaUser = ZYUserHelper.getLoginAreaUser();String editUserId = "system";String editUserName = "system";if (null != loginAreaUser) {editUserId = loginAreaUser.getId();editUserName = loginAreaUser.getUserName();}// 处理编辑记录record(recordType, businessId, newEntity, oldEntity, recordName, editUserId, editUserName);}public static <T> void record(String recordType, String businessId, T newEntity, T oldEntity, String recordName, String recordUserId, String recordUserName) {if (ZYStrUtils.isAnyNull(recordType, businessId, newEntity, oldEntity, recordUserId)) {return;}// 编辑记录idString recordId = SnowFlake.nextKey();// 构建编辑详情List<EditRecordDetail> details = RecordDetailBuilder.buildEditRecordDetail(recordId, recordName, newEntity, oldEntity);// 保存记录if (!details.isEmpty()) {// 构建编辑记录EditRecord editRecord = new EditRecord();editRecord.setRecordType(recordType);editRecord.setBusinessId(businessId);editRecord.setId(recordId);editRecord.setRecordName(recordName);editRecord.setRecordUserId(recordUserId);editRecord.setRecordUserName(recordUserName);editRecord.setRecordTime(System.currentTimeMillis());// 保存记录editRecordMapper.insert(editRecord);editRecordDetailMapper.insertSplitBatch(details, 100);}}@Overridepublic boolean match(AnnotationMetadata annotationMetadata) {// 收集带@Mapper注解的return annotationMetadata.hasAnnotation(Mapper.class.getName());}@Overridepublic void setClasses(Set<Class<?>> classes) {for (Class<?> mapperClass : classes) {// 查找泛型上面的实体实名称Class<?> entityClass = ZYSpringUtils.findGenericClass(mapperClass, FIRST);if (null != entityClass) {MAPPER__CLASS_CONTAINER.put(entityClass.getName(), mapperClass);}}}@Overridepublic void doBusiness() {// 容器启动完成后,实体类对象的BaseMapper bean对象。缓存到map中MAPPER__CLASS_CONTAINER.forEach((entityName, mapperClass) -> {Object mapper = ZYSpringUtils.getBean(mapperClass);if (null != mapper) {MAPPER_CONTAINER.put(entityName, (BaseMapper<?>) mapper);}});}
}

RecordDetailBuilder,修改信息构建器,(与本人所有项目业务有关系,一些代码可能会没用)定义:

public class RecordDetailBuilder {public static <T> List<EditRecordDetail> buildEditRecordDetail(String recordId, String recordName, T newEntity, T oldEntity) {EntityWrapper<T> wrapper = EntityWrapper.ofCompareEntity(recordId, newEntity, oldEntity);// 解析对比不成功Tif (!wrapper.isNecessaryRecord()) {return Collections.emptyList();}// 如果是基本类型,直接new一个对象就可以了if (wrapper.isBaseClass()) {EditRecordDetail editRecordDetail = new EditRecordDetail();editRecordDetail.setId(SnowFlake.nextKey());editRecordDetail.setRecordId(recordId);editRecordDetail.setEditField(recordName);editRecordDetail.setEditFieldName(recordName);editRecordDetail.setNewValue(NameExchanger.singleExchange(newEntity));editRecordDetail.setOldValue(NameExchanger.singleExchange(oldEntity));return Collections.singletonList(editRecordDetail);} else {List<String> fieldNames = wrapper.getFieldNames();// 移除指定的不用记录的字段List<String> excludeFields = ExcludeFieldProvider.findExclude(wrapper.getEntityClass());if (ZYListUtils.isNotEmptyList(excludeFields)) {fieldNames.removeIf(excludeFields::contains);}return ZYListUtils.list2list(fieldNames, fieldName -> doBuildDetail(wrapper, fieldName));}}private static <T> EditRecordDetail doBuildDetail(EntityWrapper<T> wrapper, String fieldName) {EntityField entityField = wrapper.findEntityField(fieldName);if (null == entityField) {return null;}if (entityField.isExclude()) {return null;}// 基本字段不记录if (entityField.isBaseField()) {return null;}// 比对是否需要记录详情Object newFieldValue = wrapper.findNewFieldValue(fieldName);Object oldFieldValue = wrapper.findOldFieldValue(fieldName);if (!necessaryToRecord(newFieldValue, oldFieldValue)) {return null;}EditRecordDetail editRecordDetail = new EditRecordDetail();editRecordDetail.setId(SnowFlake.nextKey());editRecordDetail.setRecordId(wrapper.getRecordId());String newValue = NameExchanger.exchangeValue(entityField, newFieldValue);String oldValue = NameExchanger.exchangeValue(entityField, oldFieldValue);editRecordDetail.setNewValue(newValue);editRecordDetail.setOldValue(oldValue);editRecordDetail.setEditField(fieldName);editRecordDetail.setEditFieldName(entityField.getChinaName());return editRecordDetail;}private static boolean necessaryToRecord(Object newFieldValue, Object oldFieldValue) {if (ZYStrUtils.isAllNull(newFieldValue, oldFieldValue)) {return false;}if (ZYStrUtils.isAnyNull(newFieldValue, oldFieldValue)) {if (isDict(newFieldValue, oldFieldValue)) {return !toCheckDictIsAllNull(newFieldValue, oldFieldValue);} else {return true;}}if (newFieldValue instanceof DictColumn) {DictColumn newDict = (DictColumn) newFieldValue;DictColumn oldDict = (DictColumn) oldFieldValue;String newDictValue = newDict.getValue();String oldDictValue = oldDict.getValue();return necessaryToRecord(newDictValue, oldDictValue);}return !ZYBoolUtils.ignoreEquals(newFieldValue, oldFieldValue);}private static boolean toCheckDictIsAllNull(Object newFieldValue, Object oldFieldValue) {boolean isEmptyDictNew = null == newFieldValue || ZYStrUtils.isNull(((DictColumn) newFieldValue).getValue());boolean isEmptyDictOld = null == oldFieldValue || ZYStrUtils.isNull(((DictColumn) oldFieldValue).getValue());return isEmptyDictNew && isEmptyDictOld;}private static boolean isDict(Object newFieldValue, Object oldFieldValue) {boolean isDict = false;if (null != newFieldValue) {isDict = newFieldValue instanceof DictColumn;} else if (null != oldFieldValue) {isDict = oldFieldValue instanceof DictColumn;}return isDict;}
}

EntityWrapper,包装和预处理下编辑记录的新旧对象信息,定义

public class EntityWrapper<T> {private final static Map<String, Map<String, EntityField>> FIELD_CACHE = new HashMap<>();// 初始不成功private boolean necessaryRecord;// 是基本类型private boolean isBaseClass;private String recordId;// 字段新的属性值private Map<String, Object> newProperties;// 字段旧的属性值private Map<String, Object> oldProperties;private Map<String, EntityField> entityFields;private List<String> fieldNames = new ArrayList<>();private Class<?> entityClass;public EntityWrapper(String recordId, T newEntity, T oldEntity) {// 任一为空,不用记录this.necessaryRecord = null != newEntity && null != oldEntity;if (!necessaryRecord) {return;}// 记录idthis.recordId = recordId;// 是基本类型this.entityClass = newEntity.getClass();this.isBaseClass = isBaseClass(entityClass);if (isBaseClass) {return;}// 字段新的属性值this.newProperties = ZYBeanUtils.beanToMap(newEntity, false, false);if (this.newProperties.isEmpty()) {this.necessaryRecord = false;return;}// 字段旧的属性值this.oldProperties = ZYBeanUtils.beanToMap(oldEntity, false, false);if (this.oldProperties.isEmpty()) {this.necessaryRecord = false;return;}// 字段的属性定义this.entityFields = resolveEntityField(entityClass);this.fieldNames.addAll(entityFields.keySet());}private boolean isBaseClass(Class<?> entityClass) {return String.class.isAssignableFrom(entityClass)|| Date.class.isAssignableFrom(entityClass)|| Integer.class.isAssignableFrom(entityClass)|| Long.class.isAssignableFrom(entityClass)|| Boolean.class.isAssignableFrom(entityClass)|| Double.class.isAssignableFrom(entityClass);}public static <T> EntityWrapper<T> ofCompareEntity(String recordId, T newEntity, T oldEntity) {return new EntityWrapper<>(recordId, newEntity, oldEntity);}public EntityField findEntityField(String fieldName) {return entityFields.get(fieldName);}public Object findNewFieldValue(String fieldName) {return newProperties.get(fieldName);}public Object findOldFieldValue(String fieldName) {return oldProperties.get(fieldName);}private static Map<String, EntityField> resolveEntityField(Class<?> newClass) {String className = newClass.getName();Map<String, EntityField> entityFieldContainer = FIELD_CACHE.get(className);if (null == entityFieldContainer) {Map<String, Field> fieldMap = ZYReflectUtils.getFieldMap(newClass);entityFieldContainer = ZYMapUtils.map2mapValue(fieldMap, EntityField::new);FIELD_CACHE.put(className, entityFieldContainer);}return entityFieldContainer;}
}

NameExchanger,记录编辑记录时,用来将外键、字典、日期等转换成中文名称的转换器(与本人所有项目业务有关系,一些代码可能会没用)定义:

public class NameExchanger {public static String singleExchange(Object value) {return ZYStrUtils.isNotNull(value) ? String.valueOf(value) : "";}public static String exchangeValue(EntityField entityField, Object value) {if (ZYStrUtils.isNull(value)) {return "";}if (value instanceof String && !entityField.isJoin()) {return String.valueOf(value);}if (value instanceof Float || value instanceof Double) {BigDecimal decimal = new BigDecimal(String.valueOf(value));DecimalFormat decimalFormat = new DecimalFormat("#.##");return decimalFormat.format(decimal.doubleValue());}if (value instanceof Date) {return ZYDateUtils.formart((Date) value, "yyyy-MM-dd");}if (value instanceof Integer) {if (entityField.isIntBoolean()) {return ZYBoolUtils.int2bool((Integer) value) ? "是" : "否";} else {return String.valueOf(value);}}if (value instanceof Boolean) {boolean valueBool = (boolean) value;return valueBool ? "是" : "否";}// 是Long类型日期if (value instanceof Long) {if (entityField.isLongDate()) {return ZYDateUtils.formart((Long) value, "yyyy-MM-dd");} else {return String.valueOf(value);}}// 是关联字段if (entityField.isJoin()) {JoinNameProvider provider = ZYSpringUtils.getBean(entityField.getJoinBeanName(), JoinNameProvider.class);return null != provider ? provider.findJoinName(String.valueOf(value)) : "";}// 字典String dictName = exchangeDict(entityField, value);if (ZYStrUtils.isNotNull(dictName)) {return dictName;}return "";}private static String exchangeDict(EntityField entityField, Object value) {if (entityField.isDict() && value instanceof DictColumn) {DictColumn dictColumn = (DictColumn) value;String dictValue = dictColumn.getValue();String label = dictColumn.getLabel();if (ZYStrUtils.isNotNull(label)) {return label;} else if (ZYStrUtils.isNotNull(dictValue)) {return ZYDictHelper.dictName(entityField.dictType, dictValue);} else {return "";}}if (entityField.isDict() && value instanceof String) {return ZYDictHelper.dictName(entityField.dictType, String.valueOf(value));}if (value instanceof DictColumn) {DictColumn dictColumn = (DictColumn) value;String dictValue = dictColumn.getValue();String label = dictColumn.getLabel();if (ZYStrUtils.isNotNull(label)) {return label;} else if (ZYStrUtils.isNotNull(dictValue)) {return ZYDictHelper.dictName(ZYStrUtils.camelToUnderline(entityField.getFieldName()), dictValue);} else {return "";}}return null;}
}

这篇关于一款通用的数据编辑历史记录工具,用来解决用户编辑业务数据留痕的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/566343

相关文章

SpringBoot UserAgentUtils获取用户浏览器的用法

《SpringBootUserAgentUtils获取用户浏览器的用法》UserAgentUtils是于处理用户代理(User-Agent)字符串的工具类,一般用于解析和处理浏览器、操作系统以及设备... 目录介绍效果图依赖封装客户端工具封装IP工具实体类获取设备信息入库介绍UserAgentUtils

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处

Spring Boot中JSON数值溢出问题从报错到优雅解决办法

《SpringBoot中JSON数值溢出问题从报错到优雅解决办法》:本文主要介绍SpringBoot中JSON数值溢出问题从报错到优雅的解决办法,通过修改字段类型为Long、添加全局异常处理和... 目录一、问题背景:为什么我的接口突然报错了?二、为什么会发生这个错误?1. Java 数据类型的“容量”限制

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

关于MongoDB图片URL存储异常问题以及解决

《关于MongoDB图片URL存储异常问题以及解决》:本文主要介绍关于MongoDB图片URL存储异常问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录MongoDB图片URL存储异常问题项目场景问题描述原因分析解决方案预防措施js总结MongoDB图

SpringBoot项目中报错The field screenShot exceeds its maximum permitted size of 1048576 bytes.的问题及解决

《SpringBoot项目中报错ThefieldscreenShotexceedsitsmaximumpermittedsizeof1048576bytes.的问题及解决》这篇文章... 目录项目场景问题描述原因分析解决方案总结项目场景javascript提示:项目相关背景:项目场景:基于Spring

解决Maven项目idea找不到本地仓库jar包问题以及使用mvn install:install-file

《解决Maven项目idea找不到本地仓库jar包问题以及使用mvninstall:install-file》:本文主要介绍解决Maven项目idea找不到本地仓库jar包问题以及使用mvnin... 目录Maven项目idea找不到本地仓库jar包以及使用mvn install:install-file基

Spring 请求之传递 JSON 数据的操作方法

《Spring请求之传递JSON数据的操作方法》JSON就是一种数据格式,有自己的格式和语法,使用文本表示一个对象或数组的信息,因此JSON本质是字符串,主要负责在不同的语言中数据传递和交换,这... 目录jsON 概念JSON 语法JSON 的语法JSON 的两种结构JSON 字符串和 Java 对象互转

最详细安装 PostgreSQL方法及常见问题解决

《最详细安装PostgreSQL方法及常见问题解决》:本文主要介绍最详细安装PostgreSQL方法及常见问题解决,介绍了在Windows系统上安装PostgreSQL及Linux系统上安装Po... 目录一、在 Windows 系统上安装 PostgreSQL1. 下载 PostgreSQL 安装包2.