【Java】比較業務實體資訊變化的工具類

emdzz發表於2024-06-29

一、業務需求

需要將業務表每次更新操作的前後記錄進行儲存,寫入更新歷史表中

方便使用者查閱該業務記錄發生的歷史變化

二、程式碼實現

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);
    }
}

  

相關文章