【Java】比较业务实体信息变化的工具类

 

一、业务需求

需要将业务表每次更新操作的前后记录进行保存,写入更新历史表中

方便用户查阅该业务记录发生的历史变化

 

二、代码实现

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;


/**
 * 判断实体记录是否变更工具类
 */
public class DtoChangeUtil {

    public static <Dto> void dtoCompare(Dto beforeDto, Dto afterDto, Consumer<CompareInfo> consumer) {
        Class<?> dtoClass = beforeDto.getClass();

        List<Field> requiredFields = Arrays.stream(dtoClass.getDeclaredFields())
                .filter(f -> Objects.nonNull(f.getAnnotation(CompareRequire.class)))
                .collect(Collectors.toList());

        for (Field requiredField : requiredFields) {
            requiredField.setAccessible(true);
            String labelName = requiredField.getAnnotation(CompareRequire.class).labelName();
            String fieldName = requiredField.getName();
            Class<?> fieldType = requiredField.getType();
            try {
                Object beforeVal = requiredField.get(beforeDto);
                Object afterVal = requiredField.get(afterDto);

                boolean[] isEquals = compareFieldIsEquals(beforeVal, afterVal, fieldType);
                // if (isEquals[0]) continue;
                consumer.accept(CompareInfo.builder()
                        .labelName(labelName)
                        .fieldName(fieldName)
                        .isEquals(isEquals[0])
                        .before(isEquals[1] ? "" : beforeVal.toString())
                        .after(isEquals[2] ? "" : afterVal.toString())
                        .build());

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 判断该字段前后值是否相等
     * @param beforeVal 之前值
     * @param afterVal 之后值
     * @param fieldType 类型
     * @return [是否相等, before是否空值, after是否空值]
     */
    private static boolean[] compareFieldIsEquals(Object beforeVal, Object afterVal, Class<?> fieldType) {
        boolean isEmptyBeforeVal = Objects.isNull(beforeVal);
        boolean isEmptyAfterVal = Objects.isNull(afterVal);
        if (isEmptyBeforeVal && isEmptyAfterVal) return new boolean[] { true, true, true };
        else if (isEmptyBeforeVal && !isEmptyAfterVal) return new boolean[] { false, true, false };
        else if (!isEmptyBeforeVal && isEmptyAfterVal) return new boolean[] { false, false, true };

        boolean isString = String.class.equals(fieldType);
        boolean isDate = Date.class.equals(fieldType);
        boolean isBigDecimal = BigDecimal.class.equals(fieldType);
        boolean isBoolean = boolean.class.equals(fieldType) || Boolean.class.equals(fieldType);
        boolean isByte = byte.class.equals(fieldType) || Byte.class.equals(fieldType);
        boolean isChar = char.class.equals(fieldType) || Character.class.equals(fieldType);
        boolean isShort = short.class.equals(fieldType) || Short.class.equals(fieldType);
        boolean isInt = int.class.equals(fieldType) || Integer.class.equals(fieldType);
        boolean isLong = long.class.equals(fieldType) || Long.class.equals(fieldType);
        boolean isFloat = float.class.equals(fieldType) || Float.class.equals(fieldType);
        boolean isDouble = double.class.equals(fieldType) || Double.class.equals(fieldType);
        /* 其他类型以此类推 */
        if (isString || isDate) return new boolean[] { beforeVal.equals(afterVal), false, false };
        int compareResult = 0;
        if (isBigDecimal) compareResult = ((BigDecimal) beforeVal).compareTo((BigDecimal) afterVal);
        if (isBoolean) compareResult = ((Boolean) beforeVal).compareTo((Boolean) afterVal);
        if (isByte) compareResult = ((Byte) beforeVal).compareTo((Byte) afterVal);
        if (isChar) compareResult = ((Character) beforeVal).compareTo((Character) afterVal);
        if (isShort) compareResult = ((Short) beforeVal).compareTo((Short) afterVal);
        if (isInt) compareResult = ((Integer) beforeVal).compareTo((Integer) afterVal);
        if (isLong) compareResult = ((Long) beforeVal).compareTo((Long) afterVal);
        if (isFloat) compareResult = ((Float) beforeVal).compareTo((Float) afterVal);
        if (isDouble) compareResult = ((Double) beforeVal).compareTo((Double) afterVal);
        return new boolean[] { compareResult == 0, false, false };
    }

    /**
     * 需要比较注解:
     * 对实体类需要进行比较的字段进行注解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public static @interface CompareRequire {
        String labelName(); /* 字段业务名称 */
    }

    /**
     * 需要比较注解:
     * 比较信息
     */
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static final class CompareInfo {
        private String labelName;
        private boolean isEquals;
        private String fieldName;
        private String before;
        private String after;
    }

    /**
     * 测试用例,模拟同一条记录,表单提交的和数据库保存的内容发生了哪些变化
     * 仅注解了[需要比较]的字段才会处理
     */
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    static class TestDemo {
        private Integer id;
        @CompareRequire(labelName = "业务编号")
        private String code;
        @CompareRequire(labelName = "业务时间")
        private Date busTime;
        @CompareRequire(labelName = "操作用户")
        private String userName;
    }
    public static void main(String[] args) {
        Date date = new Date();
        TestDemo t1 = TestDemo.builder().id(1).busTime(new Date(date.getTime() + 1000)).code("1234").userName("admin").build();
        TestDemo t2 = TestDemo.builder().id(1).busTime(new Date(date.getTime() - 3000)).code("5678").userName("admin").build();
        dtoCompare(t1, t2, System.out::println);
    }
}

  

posted @ 2024-06-29 03:37  emdzz  阅读(2)  评论(0编辑  收藏  举报