一、業務需求
需要將業務表每次更新操作的前後記錄進行儲存,寫入更新歷史表中
方便使用者查閱該業務記錄發生的歷史變化
二、程式碼實現
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); } }