Skip to content

🔍 对象差异对比工具(ObjectCompareHelper)

📖 功能介绍

TIP

ObjectCompareHelper 是一个强大的对象差异对比工具类,支持通过注解配置字段对比规则,提供字典转换、格式化、自定义解析等功能,适用于数据变更记录、审计日志等场景。

🛠️ 核心功能

1. 对象对比功能

功能说明支持类型
字段值对比检测对象字段值变化基本类型、对象类型
字典转换将编码值转换为可读文本String、Integer等
格式化显示按指定格式显示值Date、Number等
自定义解析通过自定义解析器处理特殊值任意类型

📝 注解说明

@CompareField 注解

用于标记需要参与对比的字段,并配置对比规则和显示格式。

java
/**
 * 对象字段对比注解
 * @author: fallsea
 * @version 1.0
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CompareField {
    
    /**
     * 字段中文描述名称
     */
    String name();
    
    /**
     * 是否参与对比,默认true
     */
    boolean compare() default true;
    
    /**
     * 字典类型,用于值转换显示
     */
    String dictType() default "";
    
    /**
     * 单位,如:元、个、天等
     */
    String unit() default "";
    
    /**
     * 日期格式化,默认yyyy-MM-dd HH:mm:ss
     */
    String dateFormat() default "yyyy-MM-dd HH:mm:ss";
    
    /**
     * 时区,默认为系统时区
     * 支持格式:
     * 1. 时区ID:如 "Asia/Shanghai", "UTC", "GMT+8"
     * 2. 空字符串表示使用系统默认时区
     */
    String timeZone() default "";
    
    /**
     * 数字格式化,如保留小数位数
     */
    String numberFormat() default "";
    
    /**
     * 排序权重,数字越小越靠前
     */
    int order() default 0;
    
    /**
     * 自定义解析器类
     * 如果指定了自定义解析器,将优先使用自定义解析器进行值转换
     */
    Class<? extends ValueParser> parser() default ValueParser.class;
}

注解属性详解

属性类型默认值说明示例
nameString必填字段中文描述名称"用户状态"
comparebooleantrue是否参与对比true/false
dictTypeString""字典类型编码"user_status"
unitString""显示单位"元"、"个"、"天"
dateFormatString"yyyy-MM-dd HH:mm:ss"日期格式"yyyy-MM-dd"
timeZoneString""时区设置"Asia/Shanghai"
numberFormatString""数字格式"#.##"
orderint0排序权重1, 2, 3...
parserClassValueParser.class自定义解析器CustomParser.class

💡 使用示例

1. 基础对比示例

java
public class User {
    @CompareField(name = "用户名", order = 1)
    private String username;
    
    @CompareField(name = "用户状态", dictType = "user_status", order = 2)
    private Integer status;
    
    @CompareField(name = "余额", unit = "元", numberFormat = "#.##", order = 3)
    private BigDecimal balance;
    
    @CompareField(name = "创建时间", dateFormat = "yyyy-MM-dd HH:mm:ss", 
                  timeZone = "Asia/Shanghai", order = 4)
    private Date createTime;
    
    @CompareField(compare = false) // 不参与对比
    private String password;
    
    // getter/setter...
}

2. 字典转换示例

字典服务接口

对象对比工具支持通过字典服务进行值转换,需要实现以下接口:

java
/**
 * 字典服务接口
 * @author: fallsea
 * @version 1.0
 */
public interface ObjectCompareDictService {
    
    /**
     * 根据字典类型和值获取显示文本
     * @param dictType 字典类型
     * @param value 字典值
     * @return 显示文本
     */
    String getDictText(String dictType, Object value);
}

字典服务实现示例

java
@Service
public class ObjectCompareDictServiceImpl implements ObjectCompareDictService {
    
    
    @Override
    public String getDictText(String dictType, Object value) {
        
        // 返回字典文本,如果找不到则返回原值
        return "";
    }
    
}

使用示例

java
// 用户状态字典配置
// user_status: 1-正常, 2-禁用, 3-锁定

User oldUser = new User();
oldUser.setStatus(1); // 正常

User newUser = new User();
newUser.setStatus(2); // 禁用

// 对比结果会显示:
// 用户状态: 正常 → 禁用
List<CompareResult> results = ObjectCompareHelper.compare(oldUser, newUser);

3. 自定义解析器示例

java
// 自定义解析器
public class StatusParser implements ValueParser {
    @Override
    public String parse(Object value, CompareField field) {
        if (value instanceof Integer) {
            Integer status = (Integer) value;
            switch (status) {
                case 1: return "激活";
                case 2: return "停用";
                default: return "未知";
            }
        }
        return String.valueOf(value);
    }
}

// 使用自定义解析器
@CompareField(name = "账户状态", parser = StatusParser.class)
private Integer accountStatus;

4. 复杂对象对比

java
public class Order {
    @CompareField(name = "订单号", order = 1)
    private String orderNo;
    
    @CompareField(name = "订单状态", dictType = "order_status", order = 2)
    private String status;
    
    @CompareField(name = "订单金额", unit = "元", numberFormat = "#,##0.00", order = 3)
    private BigDecimal amount;
    
    @CompareField(name = "下单时间", dateFormat = "yyyy-MM-dd HH:mm", 
                  timeZone = "Asia/Shanghai", order = 4)
    private LocalDateTime orderTime;
    
    @CompareField(name = "有效天数", unit = "天", order = 5)
    private Integer validDays;
    
    // getter/setter...
}

// 使用示例
Order oldOrder = new Order();
oldOrder.setAmount(new BigDecimal("1000.50"));
oldOrder.setStatus("PENDING");

Order newOrder = new Order();
newOrder.setAmount(new BigDecimal("1200.80"));
newOrder.setStatus("CONFIRMED");

List<CompareResult> results = ObjectCompareHelper.compare(oldOrder, newOrder);
// 输出:
// 订单状态: 待确认 → 已确认
// 订单金额: 1,000.50元 → 1,200.80元

🔧 高级功能

1. 时区处理

java
@CompareField(name = "创建时间", 
              dateFormat = "yyyy-MM-dd HH:mm:ss", 
              timeZone = "Asia/Shanghai")
private Date createTime;

@CompareField(name = "更新时间", 
              dateFormat = "yyyy-MM-dd HH:mm:ss", 
              timeZone = "UTC")
private Date updateTime;

2. 数字格式化

java
@CompareField(name = "价格", unit = "元", numberFormat = "#,##0.00")
private BigDecimal price;

@CompareField(name = "百分比", unit = "%", numberFormat = "#.##")
private Double percentage;

@CompareField(name = "数量", unit = "个", numberFormat = "#,###")
private Integer quantity;

3. 排序控制

java
public class Product {
    @CompareField(name = "商品名称", order = 1)
    private String name;
    
    @CompareField(name = "商品价格", order = 2, unit = "元")
    private BigDecimal price;
    
    @CompareField(name = "商品状态", order = 3, dictType = "product_status")
    private Integer status;
    
    @CompareField(name = "创建时间", order = 10)
    private Date createTime;
}

⚠️ 注意事项

  1. 注解配置

    • name 属性为必填项
    • 字典类型需要在系统中预先配置
    • 自定义解析器需要实现 ValueParser 接口
  2. 性能考虑

    • 避免对大量对象进行频繁对比
    • 字典查询建议使用缓存
    • 自定义解析器避免复杂计算
  3. 数据类型支持

    • 基本数据类型及其包装类
    • 日期时间类型(Date、LocalDateTime等)
    • 数字类型(BigDecimal、Double等)
    • 字符串类型
  4. 字典配置

    • 确保字典类型在系统中存在
    • 字典值应覆盖所有可能的字段值
    • 建议使用缓存提高查询性能

💡 最佳实践

1. 实体类配置

java
@Entity
public class UserProfile {
    @CompareField(name = "用户ID", order = 1)
    private Long userId;
    
    @CompareField(name = "真实姓名", order = 2)
    private String realName;
    
    @CompareField(name = "性别", dictType = "gender", order = 3)
    private String gender;
    
    @CompareField(name = "年龄", unit = "岁", order = 4)
    private Integer age;
    
    @CompareField(name = "认证状态", dictType = "auth_status", order = 5)
    private Integer authStatus;
    
    @CompareField(name = "最后登录", dateFormat = "yyyy-MM-dd HH:mm", 
                  timeZone = "Asia/Shanghai", order = 6)
    private Date lastLoginTime;
    
    @CompareField(compare = false) // 敏感信息不参与对比
    private String idCard;
}

2. 对比结果处理

java
public class AuditService {
    
    public void recordChanges(Object oldObj, Object newObj, String operator) {
        List<CompareResult> results = ObjectCompareHelper.compare(oldObj, newObj);
        
        for (CompareResult result : results) {
            AuditLog log = new AuditLog();
            log.setFieldName(result.getFieldName());
            log.setOldValue(result.getOldValue());
            log.setNewValue(result.getNewValue());
            log.setOperator(operator);
            log.setOperateTime(new Date());
            
            auditLogRepository.save(log);
        }
    }
}

3. 自定义解析器最佳实践

java
public class MoneyParser implements ValueParser {
    @Override
    public String parse(Object value, CompareField field) {
        if (value == null) {
            return "0.00" + field.unit();
        }
        
        if (value instanceof BigDecimal) {
            BigDecimal money = (BigDecimal) value;
            DecimalFormat df = new DecimalFormat("#,##0.00");
            return df.format(money) + field.unit();
        }
        
        return String.valueOf(value) + field.unit();
    }
}