【比较两个对象所有属性值的变化,并输出变化值】

本方法类需要引用
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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值