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

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

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

        // 记录编辑信息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

相关文章

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

mysql出现ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)的解决方法

《mysql出现ERROR2003(HY000):Can‘tconnecttoMySQLserveron‘localhost‘(10061)的解决方法》本文主要介绍了mysql出现... 目录前言:第一步:第二步:第三步:总结:前言:当你想通过命令窗口想打开mysql时候发现提http://www.cpp

MySQL大表数据的分区与分库分表的实现

《MySQL大表数据的分区与分库分表的实现》数据库的分区和分库分表是两种常用的技术方案,本文主要介绍了MySQL大表数据的分区与分库分表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. mysql大表数据的分区1.1 什么是分区?1.2 分区的类型1.3 分区的优点1.4 分

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1