本方法类需要引用
lombok包 和 swagger包
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.*;
import java.util.*;
/**
* 检查对象变化
*
* @description: 检查对象变化、对比对象差异
* @author: 吐鲁番大队长
* @create: 时间2023-11-06 15:38
* @since 资金二期
**/
@Slf4j
public class CheckObjectUtil {
public static List<String> needFilterFiledName = Arrays.asList("serialVersionUID", "id", "updateTime", "createTime",
"updateUserId", "createUserId", "updateUser", "createUser", "pageNo", "pageSize", "tenantCode", "eiconBankBranchId",
"cnapsBranchId", "bankAccountId");
public static String ELEMENT_DATA = "elementData";
/**
* 检查对象变化
*
* @param oldObj 修改前对象
* @param newObj 修改后对象
* @return Pair.left 旧值集合 Pair.right 新值集合
* @description: 检查两个为同一个Class的对象的属性变化,最终生成两个List<br>
* List内容为:" {@link ApiModelProperty}中的{@link ApiModelProperty#value() value}[没有注解取属性字段名]:属性值" <br>
* eg:"id:1"
*/
public static <T> Pair<List<String>, List<String>> checkChange(T oldObj, T newObj) {
Class<?> changeClass = oldObj.getClass();
Field[] fields = changeClass.getDeclaredFields();
List<String> newValues = new ArrayList<>();
List<String> oldValues = new ArrayList<>();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
if (needFilterFiledName.contains(name)) {
continue;
}
try {
changeClass.getDeclaredField(name);
} catch (NoSuchFieldException e) {
log.error("CheckServicesImpl.checkChange获取属性异常:", e);
continue;
}
try {
Object oldfieldValue = field.get(oldObj);
Object newfieldValue = field.get(newObj);
// 如果属性是自定义对象类型,则递归对比其属性,对比完跳出当前循环,进行下一个属性对比
if (oldfieldValue != null && !field.getType().isPrimitive() && field.getType().getName().startsWith("czb.asset.manage.payment.center.")) {
Pair<List<String>, List<String>> listPair = checkChange(oldfieldValue, newfieldValue);
oldValues.addAll(listPair.getKey());
newValues.addAll(listPair.getValue());
continue;
}
ApiModelProperty annotation = null;
field.setAccessible(true);
Object oldFieldValue = Objects.nonNull(field.get(oldObj)) ? field.get(oldObj) : StringUtils.EMPTY;
Object newFieldValue = Objects.nonNull(field.get(newObj)) ? field.get(newObj) : StringUtils.EMPTY;
if (field.isAnnotationPresent(ApiModelProperty.class)) {
annotation = field.getAnnotation(ApiModelProperty.class);
}
String changePre = Objects.nonNull(annotation) && StringUtils.isNotBlank(annotation.value()) ? annotation.value() : name;
if (!oldFieldValue.equals(newFieldValue)) {
oldValues.add(changePre + ":" + oldFieldValue);
newValues.add(changePre + ":" + newFieldValue);
}
} catch (IllegalAccessException e) {
log.error("CheckServicesImpl.checkChange属性比较异常:", e);
}
}
return Pair.of(oldValues, newValues);
}
/**
* 检查对象变化-按子模块划分
*
* @param oldObj 修改前对象
* @param newObj 修改后对象
* @param uniqueColumn List属性对象的唯一标识,默认为"id",uniqueColumn存在 通过uniqueColumn来匹配新老数据。
* @return {@link Map Pair.left} { {@linkplain String Map.key} 子模块名称 {@link List Map.value} 旧值集合} <br/>
* {@link Map Pair.right} { {@linkplain String Map.key} 子模块名称 {@link List Map.value} 新值集合}
* @description: 检查两个为同一个Class的对象的属性变化,最终生成两个Map<br>
* Map内容为:" < {@link ApiModelProperty}中的{@link ApiModelProperty#notes() notes}[默认为空字符串] ,List< {@link ApiModelProperty}中的{@link ApiModelProperty#value() value}[没有注解取属性字段名]:属性值> >" <br>
* <li><p>eg:</p> {test:{"账户性质(新)":}, "其他":{"银行账号":123, "账户大额联系人":ls} }</li>
* <li>不支持 空字符串、null 互相比较</li>
* <li>不支持List属性中嵌套List</li><br>
*/
public static <T> Pair<Map<String, List<String>>, Map<String, List<String>>> checkChangeToMap(T oldObj, T newObj, String uniqueColumn) {
Class<?> changeClass;
if (Objects.isNull(oldObj) && Objects.isNull(newObj)) {
throw new RuntimeException("参数有误请检查");
}
if(!"Object".equals(oldObj.getClass().getSimpleName())){
changeClass = oldObj.getClass();
}else if(!"Object".equals(newObj.getClass().getSimpleName())){
changeClass = newObj.getClass();
}else {
return Pair.of(new HashMap<>(), new HashMap<>());
}
Field[] fields = changeClass.getDeclaredFields();
Map<String, List<String>> newValuesMap = new HashMap<>();
Map<String, List<String>> oldValuesMap = new HashMap<>();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
if (needFilterFiledName.contains(name)) {
continue;
}
try {
changeClass.getDeclaredField(name);
} catch (NoSuchFieldException e) {
log.error("CheckServicesImpl.checkChangeToMap获取属性异常:", e);
continue;
}
try {
if(field.get(oldObj) == null && field.get(newObj) == null){
continue;
}
Class<?> fieldClass = Objects.isNull(field.get(oldObj)) ? field.get(newObj).getClass() : field.get(oldObj).getClass();
// 如果属性是自定义对象类型,则递归对比其属性,对比完跳出当前循环,进行下一个属性对比
if (!field.getType().isPrimitive() && field.getType().getName().startsWith("czb.asset.manage.payment.center.")) {
// 使用默认构造函数创建 ArrayList 实例
Object oldFieldValue = Objects.isNull(field.get(oldObj)) ? fieldClass.getConstructor().newInstance() : field.get(oldObj);
Object newFieldValue = Objects.isNull(field.get(newObj)) ? fieldClass.getConstructor().newInstance() : field.get(newObj);
Pair<Map<String, List<String>>, Map<String, List<String>>> mapPair = checkChangeToMap(oldFieldValue, newFieldValue, uniqueColumn);
oldValuesMap.putAll(mapPair.getKey());
newValuesMap.putAll(mapPair.getValue());
continue;
}
// 如果属性是List
if (List.class.isAssignableFrom(fieldClass)) {
Pair<Map<String, List<String>>, Map<String, List<String>>> compareListPair = compareList(field.get(oldObj), field.get(newObj), fieldClass, uniqueColumn);
oldValuesMap.putAll(compareListPair.getKey());
newValuesMap.putAll(compareListPair.getValue());
continue;
}
// 如果属性非自定义对象
ApiModelProperty annotation = null;
if (field.isAnnotationPresent(ApiModelProperty.class)) {
annotation = field.getAnnotation(ApiModelProperty.class);
}
Object oldFieldValue = Objects.nonNull(field.get(oldObj)) ? field.get(oldObj) : StringUtils.EMPTY;
Object newFieldValue = Objects.nonNull(field.get(newObj)) ? field.get(newObj) : StringUtils.EMPTY;
String notes = Objects.nonNull(annotation) ? annotation.notes() : StringUtils.EMPTY;
String changePre = Objects.nonNull(annotation) && StringUtils.isNotBlank(annotation.value()) ? annotation.value() : field.getName();
if (!oldFieldValue.equals(newFieldValue)) {
List<String> oldValues = oldValuesMap.getOrDefault(notes, new ArrayList<>());
oldValues.add(changePre + ":" + oldFieldValue);
oldValuesMap.put(notes, oldValues);
List<String> newValues = newValuesMap.getOrDefault(notes, new ArrayList<>());
newValues.add(changePre + ":" + newFieldValue);
newValuesMap.put(notes, newValues);
}
} catch (IllegalAccessException e) {
log.error("CheckServicesImpl.checkChangeToMap属性比较异常:", e);
} catch (InvocationTargetException | InstantiationException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
return Pair.of(oldValuesMap, newValuesMap);
}
/**
* 比较List变化
*
* @param oldObj 旧数据List
* @param newObj 新数据List
* @param fieldClass ArrayList.class
* @param uniqueColumn List中属性的唯一标识,默认为"id",uniqueColumn存在 通过uniqueColumn来匹配新老数据。
* @return Pair.left 旧值集合 Pair.right 新值集合
*/
private static <T> Pair<Map<String, List<String>>, Map<String, List<String>>> compareList(T oldObj, T newObj, Class<?> fieldClass, String uniqueColumn) {
Pair<Map<String, List<String>>, Map<String, List<String>>> result = Pair.of(new HashMap<>(), new HashMap<>());
try {
Field fieldList = fieldClass.getDeclaredField(ELEMENT_DATA);
fieldList.setAccessible(true);
Object[] oldElementData = (Object[]) (oldObj == null || fieldList.get(oldObj) == null ? new Object[0] : fieldList.get(oldObj));
Object[] newElementData = (Object[]) (newObj == null || fieldList.get(newObj) == null ? new Object[0] : fieldList.get(newObj));
oldElementData = removeNullElement(oldElementData);
newElementData = removeNullElement(newElementData);
Object[] compareData = oldElementData.length > newElementData.length ? oldElementData : newElementData;
// 如果数组不为 null 且不为空,获取第一个元素的类型
for (int i = 0; compareData.length > 0 && i < compareData.length; i++) {
Class<?> changeClass = compareData[0].getClass();
Constructor<?> constructor = changeClass.getConstructor();
Object defaultObject = constructor.newInstance();
Object newElement = (newElementData.length == 0 || newElementData.length <= i || newElementData[i] == null)
? defaultObject : newElementData[i];
Object oldElement = (oldElementData.length == 0 || oldElementData.length <= i || oldElementData[i] == null)
? defaultObject : oldElementData[i];
Pair<List<String>, List<String>> checkChange = checkChange(oldElement, newElement);
if(CollectionUtil.isEmpty(checkChange.getKey()) && CollectionUtil.isEmpty(checkChange.getValue())) {
continue;
}
ApiModel annotation = null;
if (changeClass.isAnnotationPresent(ApiModel.class)) {
annotation = changeClass.getAnnotation(ApiModel.class);
}
String preKey = Objects.nonNull(annotation) && StringUtils.isNotBlank(annotation.value()) ? annotation.value() : changeClass.getSimpleName();
String key = preKey + "[" + (i + 1) + "]";
result.getKey().put(key, checkChange.getKey());
result.getValue().put(key, checkChange.getValue());
}
} catch (NoSuchMethodException | InstantiationException | NoSuchFieldException e) {
log.error("比较List数据异常:", e);
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException("集合中对象属性缺少构造函数");
} catch (InvocationTargetException e) {
throw new RuntimeException("构造实例化异常,请检查对象是构造函数内部错误");
}
return result;
}
/**
* 校验参数是否为空
*
* @param t 被校验对象
* @return errorMessage-错误信息。没有错误返回null
* <li>返回参数列子: 企业名称[BankAccountBase.companyName]不能为空</li>
* @description: 校验前端请求对象中的属性是否为空。
* 如果{@link ApiModelProperty}中的{@link ApiModelProperty#required() required}为true,则校验改属性是否为空,如果为空返回错误信息。
* <li>只会校验包地址开头为czb.asset.manage.payment.center.的类</li>
*/
public static <T> String checkObjectParam(T t) {
Class<?> tClass = t.getClass();
Field[] fields = tClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Object fieldValue = field.get(t);
// 如果属性是自定义对象类型,则递归查询其属性
if (fieldValue != null && !field.getType().isPrimitive() && field.getType().getName().startsWith("com.xxx.")) {
String errorMessage = checkObjectParam(fieldValue);
if (StringUtils.isNotBlank(errorMessage)) {
return errorMessage;
}
continue;
}
ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
if (Objects.isNull(field.get(t)) && annotation.required()) {
return annotation.value() + "[" + tClass.getSimpleName() + "." + field.getName() + "]不能为空";
}
} catch (IllegalAccessException e) {
throw new RuntimeException("参数解析异常," + tClass.getSimpleName() + "." + field.getName(), e);
}
}
return null;
}
public static Object[] removeNullElement(Object[] array) {
int count = 0;
for (Object value : array) {
if (value != null) {
count++;
}
}
Object[] newArray = new Object[count];
int index = 0;
for (Object value : array) {
if (value != null) {
newArray[index++] = value;
}
}
return newArray;
}
}