package com.mdgyl.common.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mdgyl.common.constants.BasicBusinessConstants;
import com.mdgyl.common.enums.OperateActionEnum;
import com.mdgyl.common.util.vo.ChangeInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* description:
* author: wuyc
* createTime: 2023-12-25 09:36:15
*/
@Slf4j
public class SupplierChangeUtils {
public static void main(String[] args) {
}
/**
* 通过反射机制获取有改动的value、List值
*
* @param oldClazz 旧的对象
* @param newClazz 新的对象
* @return 改动记录列表
*/
public static <T> List<ChangeInfoVO> getChangeList(T oldClazz, T newClazz) {
Field[] oldFieldArr = oldClazz.getClass().getDeclaredFields();
Field[] newFieldArr = newClazz.getClass().getDeclaredFields();
List<ChangeInfoVO> changeList = new ArrayList<>();
IntStream.range(0, oldFieldArr.length).forEach(index -> {
Field oldField = oldFieldArr[index];
String fieldName = oldField.getName();
Object oldValue = getFieldValue(oldClazz, oldField);
Object newValue = getFieldValue(newClazz, newFieldArr[index]);
ChangeInfoVO changeInfoVO = ChangeInfoVO.buildChangeInfo(fieldName, oldValue, newValue);
if (Objects.nonNull(changeInfoVO)) {
changeList.add(changeInfoVO);
}
});
return changeList;
}
/**
* 通过反射机制获取有改动的value、List值
*
* @param oldClazz 旧的对象
* @param newClazz 新的对象
* @param compareFieldList 对比的列
* @return 改动记录列表
*/
public static <T> List<ChangeInfoVO> getChangeList(T oldClazz, T newClazz, List<String> compareFieldList) {
if (CollectionUtils.isEmpty(compareFieldList)) {
return getChangeList(oldClazz, newClazz);
}
List<ChangeInfoVO> changeList = new ArrayList<>();
for (String field : compareFieldList) {
try {
// 获取旧的值
Field oldFiled = oldClazz.getClass().getDeclaredField(field);
oldFiled.setAccessible(true);
Object oldValue = oldFiled.get(oldClazz);
// 获取新的值
Field newFiled = oldClazz.getClass().getDeclaredField(field);
newFiled.setAccessible(true);
Object newValue = newFiled.get(newClazz);
ChangeInfoVO changeInfoVO = ChangeInfoVO.buildChangeInfo(field, oldValue, newValue);
if (Objects.nonNull(changeInfoVO)) {
changeList.add(changeInfoVO);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return changeList;
}
/**
* 通过反射机制获取有改动的value、List值
*
* @param oldClazzList 旧的对象
* @param newClazzList 新的对象
* @return 改动记录列表
*/
public static <T> List<ChangeInfoVO> getListChangeList(List<T> oldClazzList, List<T> newClazzList) {
// 旧数组为空、是新增
if (CollectionUtils.isEmpty(oldClazzList)) {
return ListUtils.mapToList(newClazzList, data -> ChangeInfoVO.buildBatchAddOrDeleteVO(null, data, OperateActionEnum.ADD.getActionType()));
}
// 新数组为空、是删除
if (CollectionUtils.isEmpty(oldClazzList)) {
return ListUtils.mapToList(newClazzList, data -> ChangeInfoVO.buildBatchAddOrDeleteVO(data, null, OperateActionEnum.DELETE.getActionType()));
}
// 新旧数组都不为空,可能是新增、编辑、删除
List<ChangeInfoVO> editDeleteList = getEditOrDeleteList(oldClazzList, newClazzList);
List<ChangeInfoVO> addList = getAddList(newClazzList);
editDeleteList.addAll(addList);
return editDeleteList;
}
/**
* 获取新增的list
*
* @param newList 新数据集合
* @return 新增的集合
*/
private static <T> List<ChangeInfoVO> getAddList(List<T> newList) {
return newList.stream().map(data -> {
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(data));
if (jsonObject.getLong(BasicBusinessConstants.ID) != null) {
return null;
} else {
return ChangeInfoVO.buildBatchAddOrDeleteVO(null, data, OperateActionEnum.ADD.getActionType());
}
}).filter(Objects::nonNull).collect(Collectors.toList());
}
/**
* 获取集合数据编辑、新增的变更集合
*
* @param oldList 旧集合
* @param newList 新集合
* @return 改变的集合
*/
private static <T> List<ChangeInfoVO> getEditOrDeleteList(List<T> oldList, List<T> newList) {
return oldList.stream().map(data -> {
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(data));
Long id = jsonObject.getLong(BasicBusinessConstants.ID);
Object editObject = ListUtils.filterMapFirst(newList, editData -> getEditObject(id, editData), Objects::nonNull);
if (Objects.nonNull(editObject)) {
return ChangeInfoVO.buildBatchEditVO(id, OperateActionEnum.EDIT.getActionType(), getChangeList(data, editObject));
} else {
return ChangeInfoVO.buildBatchAddOrDeleteVO(data, null, OperateActionEnum.DELETE.getActionType());
}
}).collect(Collectors.toList());
}
/**
* 获取编辑的object
*
* @param id 行id
* @param changeObject 有改动的object
* @return 编辑的object
*/
private static Object getEditObject(Long id, Object changeObject) {
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(changeObject));
if (jsonObject.getLong(BasicBusinessConstants.ID) != null && id.equals(jsonObject.getLong(BasicBusinessConstants.ID))) {
return changeObject;
}
return null;
}
/**
* 获取field列的属性值
*
* @param clazz 获取对象
* @param field 获取的列
* @return value
*/
private static <T> Object getFieldValue(T clazz, Field field) {
field.setAccessible(true);
try {
return field.get(clazz);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
代码主要实现了通过反射机制比较两个对象(或两个对象列表)之间的差异,并生成一个包含变更信息的列表。下面是对每个方法和相关逻辑的详细分析:
-
getChangeList(T oldClazz, T newClazz)
:- 功能:比较两个对象的所有字段,生成字段变更信息的列表。
- 实现:通过反射获取旧对象和新对象的字段,比较它们的值,并生成
ChangeInfoVO
对象。如果字段值有变动,则将变更信息添加到结果列表中。 - 问题:在获取新值时直接使用
newFieldArr[index]
,这可能导致字段顺序不一致的问题。最好使用字段名称来获取值,以避免顺序问题。
-
getChangeList(T oldClazz, T newClazz, List<String> compareFieldList)
:- 功能:比较指定字段的变化,生成字段变更信息的列表。
- 实现:根据提供的字段列表获取旧对象和新对象的指定字段的值,并比较它们。如果字段值有变动,则将变更信息添加到结果列表中。
- 问题:如果字段不存在或反射获取值出错,方法会捕获并打印异常,但不会处理这些异常。可以考虑将异常处理逻辑改为更健壮的方式。
-
getListChangeList(List<T> oldClazzList, List<T> newClazzList)
:- 功能:比较两个对象列表,生成变更信息的列表,包括新增、编辑和删除。
- 实现:分别处理新增和删除,调用
getEditOrDeleteList
方法处理编辑和删除,并合并结果。 - 问题:在旧列表为空的情况下,方法使用
ListUtils.mapToList
生成新增信息,但具体实现取决于ListUtils
的实现,可能需要检查其正确性。
-
getAddList(List<T> newList)
:- 功能:获取新增的对象。
- 实现:将每个对象转换为 JSON 格式,然后根据 ID 判断是否为新增对象。如果 ID 为
null
,则认为对象是新增的。 - 问题:如果 ID 不存在可能需要更详细的判断逻辑,确保新增判断的准确性。
-
getEditOrDeleteList(List<T> oldList, List<T> newList)
:- 功能:获取编辑或删除的对象。
- 实现:检查旧列表中的每个对象是否在新列表中存在,如果存在则检查是否有编辑;如果不存在则标记为删除。
- 问题:这个方法的实现依赖于
ListUtils.filterMapFirst
,需要确保它能正确处理数据过滤和映射。
-
getEditObject(Long id, Object changeObject)
:- 功能:根据 ID 获取编辑的对象。
- 实现:将对象转换为 JSON 格式,然后根据 ID 判断对象是否需要编辑。
- 问题:确保 ID 的比较准确且 JSON 转换不会丢失重要信息。
-
getFieldValue(T clazz, Field field)
:- 功能:通过反射获取字段的值。
- 实现:设置字段可访问,然后获取字段值。如果出错则捕获异常并返回
null
。 - 问题:异常处理可能需要更细致的错误日志记录或处理。
整体来看,代码实现了基本的对象比较和变更记录功能。为了增强代码的健壮性和可维护性,可以考虑对异常处理、字段获取逻辑、以及依赖于其他工具类的方法进行详细检查和改进。
package com.mdgyl.common.util.vo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mdgyl.common.constants.BasicBusinessConstants;
import com.mdgyl.common.enums.OperateActionEnum;
import lombok.Data;
import java.util.List;
import java.util.Objects;
/**
* description:
* author: wuyc
* createTime: 2023-12-25 09:38:16
*/
@Data
public class ChangeInfoVO {
/**
* 改变的字段
*/
private String field;
/**
* 操作类型 新增、编辑、删除
*/
private String operateType;
/**
* 唯一id
*/
private String uniqueId;
/**
* 子数据
*/
private List<ChangeInfoVO> childList;
/**
* 改变之前的值
*/
private Object oldValue;
/**
* 改变之后的值
*/
private Object newValue;
/**
* 构建改变的值
*
* @param fieldName 列表
* @param oldValue 旧值
* @param newValue 新值
* @return ChangeInfoVO
*/
public static ChangeInfoVO buildChangeInfo(String fieldName, Object oldValue, Object newValue) {
// 新旧值都为空,说明无改变
if ((Objects.isNull(oldValue) && Objects.isNull(newValue))) {
return null;
}
ChangeInfoVO changeInfoVO = new ChangeInfoVO();
changeInfoVO.setField(fieldName);
changeInfoVO.setOldValue(oldValue);
changeInfoVO.setNewValue(newValue);
if (Objects.isNull(oldValue)) {
changeInfoVO.setOperateType(OperateActionEnum.ADD.getActionType());
} else if (Objects.isNull(newValue)) {
changeInfoVO.setOperateType(OperateActionEnum.DELETE.getActionType());
} else if (!oldValue.equals(newValue)) {
changeInfoVO.setOperateType(OperateActionEnum.EDIT.getActionType());
} else {
return null;
}
return changeInfoVO;
}
/**
* @param uniqueId 唯一id
* @param operateType 操作类型
* @param childList 有改动记录列表的子列表
* @return ChangeInfoVO
*/
public static ChangeInfoVO buildBatchEditVO(Long uniqueId, String operateType, List<ChangeInfoVO> childList) {
ChangeInfoVO changeInfoVO = new ChangeInfoVO();
changeInfoVO.setOperateType(operateType);
changeInfoVO.setUniqueId(uniqueId.toString());
changeInfoVO.setChildList(childList);
return changeInfoVO;
}
/**
* 批量删除、新增构建改变记录
*
* @param oldObject 旧object
* @param newObject 新object
* @param operateType 操作类型
* @return ChangeInfoVO
*/
public static ChangeInfoVO buildBatchAddOrDeleteVO(Object oldObject, Object newObject, String operateType) {
ChangeInfoVO changeInfoVO = new ChangeInfoVO();
changeInfoVO.setOperateType(operateType);
// 如果是删除的时候,需要设置旧的行id
if (Objects.nonNull(oldObject)) {
String oldJsonStr = JSON.toJSONString(oldObject);
changeInfoVO.setOldValue(oldJsonStr);
JSONObject jsonObject = JSON.parseObject(oldJsonStr);
changeInfoVO.setUniqueId(jsonObject.getString(BasicBusinessConstants.ID));
}
changeInfoVO.setNewValue(Objects.isNull(newObject) ? null : JSON.toJSONString(newObject));
return changeInfoVO;
}
}