# java-property-comparer **Repository Path**: shagang/java-property-comparer ## Basic Information - **Project Name**: java-property-comparer - **Description**: Java数据修改日志解析工具,对用户业务操作记录时,需要记录到具体某个字段变更, 例如:收货地址发生变更:详细地址由【"西红门2栋603"】修改成【"西红门2栋600"】 - **Primary Language**: Java - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 7 - **Created**: 2022-09-06 - **Last Updated**: 2024-08-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 对象对比差异解析工具 ## 一、项目背景 对用户业务操作记录时,需要记录到具体某个字段变更, `例如:收货地址发生变更:详细地址由【"西红门2栋603"】修改成【"西红门2栋600"】` 如果采用纯手动判断修改前修改后的值是否变化,则需要写如下判断: ```java if (val != newVal) { print("收货地址发生变更:详细地址由【val】修改成【newVal】") } ``` 当需要判断的属性逐渐增多,则会冗余很多这种逻辑代码。故想利用Java反射来对比对象,从而对象间的获取差异。 ## 二、实现效果 ```java // 测试用例CompareTest DiffResult diff = DiffUtil.resolve(GoodsOrder.class, getBefore(), getAfter()); log.info("是否有差异:{}", diff.isHasDiff()); log.info("差异内容:{}", diff.getDiffs()); ``` ```text true 订单编号由【"10086"】修改成【"10086001"】 自动配送由【"是"】修改成【"否"】 下单日期由【"2022-04-05"】修改成【"2022-05-05"】 订单有效时间由【"2022-03-30" - "2022-04-30"】修改成【"2022-04-30" - "2022-05-30"】 收货地址发生变更:联系人由【"肯德基"】修改成【"肯德基001"】,详细地址由【"西红门2栋603"】修改成【"西红门2栋600"】 商品列表发生变更: 商品列表,新增行【"小米手机"】 商品列表,删除行【"华为手机"】 商品列表,删除行【"苹果手机"】 商品列表,更新行【"三星手机"】,变更内容: 商品名称由【"三星手机"】修改成【"三星手机001"】,购买数量由【"20"】修改成【"22.22"】 ``` ## 三、接入 ### 3.1 注解说明 #### 3.1.1 @DiffKey 标识此属性会发生变化,无此注解属性,不记录差异 #### 3.1.2 @UnionKey 用户列表对象,标识列表明细对象的唯一标识(可对多个属性配置) 用户判断列表明细对象,是否发生,新增、修改、删除动作 #### 3.1.3 @UnionDisplayKey 用户列表对象新增、修改、删除动作时展示的名称(可对多个属性配置) #### 3.1.4 @BooleanFormat 对Boolean类型属性格式化 #### 3.1.5 @DateFormat 对Date类型属性格式化 #### 3.1.6 @LocalDateFormat 对LocalDate类型属性格式化 #### 3.1.7 @LocalDateTimeFormat 对LocalDateTime类型属性格式化 ### 3.2 完整示例 ```java /** * 订单收货地址 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class OrderAddress { @DiffKey(name = "手机号") private String phone; @DiffKey(name = "联系人") private String name; @DiffKey(name = "详细地址") private String address; } ``` ```java /** * 商品明细 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class GoodsItem { @UnionKey @UnionDisplayKey @DiffKey(name = "商品编码") private String code; @UnionDisplayKey @DiffKey(name = "商品名称") private String name; @DiffKey(name = "购买数量") private BigDecimal count; @DiffKey(name = "购买金额(元)") private BigDecimal amount; } ``` ```java /** * 商品订单 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class GoodsOrder { @DiffKey(name = "订单编号") private String orderNo; @BooleanFormat @DiffKey(name = "自动配送") private boolean autoDelivery; @LocalDateTimeFormat(pattern = "yyyy-MM-dd") @DiffKey(name = "有效开始时间", type = DiffConstant.COMBINATION_PROPERTY, combinationName = "订单有效时间") private LocalDateTime startTime; @LocalDateTimeFormat(pattern = "yyyy-MM-dd") @DiffKey(name = "有效结束时间", type = DiffConstant.COMBINATION_PROPERTY, combinationName = "订单有效时间") private LocalDateTime endTime; @DiffKey(name = "商品列表", type = DiffConstant.ARRAY_PROPERTY, subCls = GoodsItem.class, whenArrayFullProperties = false) private List items; @DiffKey(name = "收货地址", type = DiffConstant.OBJECT_PROPERTY) private OrderAddress address; @LocalDateFormat(pattern = "yyyy-MM-dd") @DiffKey(name = "下单日期") private LocalDate orderDate; @DateFormat(pattern = "yyyy-MM-dd HH") @DiffKey(name = "支付时间") private Date paymentTime; } ``` ## 四、拓展 ### 4.1 自定义属性格式化文案 在`/resourcesMETA-INF/services`目录下新建`com.jumper.property.comparer.pattern.DiffPattern`文件 可以按需重写文案规则,`${val}`修饰变量,重写保证原来的变量同时存在 ```java @SPI public class CustomDiffPattern extends DefaultDiffPattern { @Override public String getPropertyDiffPattern() { return "${property}值由原值【${before}】变成修改后的值【${after}】"; } } ``` 重写属性规则后输出差异结果: ```text // 修改前输出 自动配送由【"是"】修改成【"否"】 // 修改后输出 自动配送值由原值【"是"】变成修改后的值【"否"】 ``` 其他需重写规则参见:DefaultDiffPattern ## 五、后续支持 1. 支持`spring-boot-starter`依赖,可直接集成SpringBoot+Mybatis写入业务日志 2. 支持变更日志模板接口,自动写入业务日志,无需关注细节