Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 79 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ private String name;
```

##### 手动忽略
调用工具类DiffUtil 时传递排除比较的字段名。
调用工具类DiffUtils 时传递排除比较的字段名。

```java
DiffResult diffResult = DiffUtil.diff(obj1, obj2, "name");
DiffResult diffResult = DiffUtils.diff(obj1, obj2, "name");
```

#### 自定义比较器
Expand All @@ -51,6 +51,17 @@ public class MyComparator implements DiffComparable {
private String name;
```

#### 内置比较器
库内置了几个可直接复用的比较器,无需自己实现:

- `DefaultDiffComparator`:默认比较器,使用 `equals` 比较。
- `BigDecimalEffectiveDiffComparator`:`BigDecimal` 忽略尾零的有效值比较,`1.0` 与 `1.00` 视为相等。

```java
@DiffCompare(using = BigDecimalEffectiveDiffComparator.class)
private BigDecimal money;
```

#### 自定义类型属性
支持自定义成员变量需要使用注解标识。会自动解析到属性
可以加注解`@DiffBean`来标识。
Expand Down Expand Up @@ -155,7 +166,69 @@ public class DateTimeDiffFormatter implements DiffFormatter {
使用示例代码详见:`com.openquartz.javaobjdiff.test.DiffUtilTest`

#### 脱敏支持
使用注解可以支持对应的属性脱敏。\
`@EmailDataMask`可以支持邮箱脱敏。\
`@IdCardDataMask`可以支持身份证号脱敏。\
`@MobileDataMask`可以支持手机号脱敏。
对于手机号、邮箱、身份证号等敏感字段,在输出差异结果时支持自动脱敏展示,避免敏感信息在 diff 结果中明文泄露。

支持以下三种内置脱敏注解:

| 注解 | 适用字段 | 脱敏规则 | 示例 |
| --- | --- | --- | --- |
| `@EmailDataMask` | 邮箱 | 前缀仅保留首字母,其余用 `*` 替代,保留 `@` 及域名 | `wangtest1111@126.com` → `***********w@126.com` |
| `@MobileDataMask` | 手机号 / 座机 | 手机号保留前 3 位与后 4 位;座机保留区号与后 4 位 | `17000000056` → `170****0056`;`010-12345678` → `010-****5678` |
| `@IdCardDataMask` | 身份证号 | 保留前 6 位与后 4 位(仅对 18 位生效) | `321556199301152110` → `321556********2110` |

用法示例:

```java
public class User {
@MobileDataMask
private String mobile;

@EmailDataMask
private String email;

@IdCardDataMask
private String idCard;
}
```

diff 输出示例:

```
User[mobile=170****0056, email=***********w@126.com, idCard=321556********2110] -> User[mobile=171****0056, email=*********l@126.com, idCard=301556********2110]
```

> **注意**:脱敏仅作用于 `DiffResult.toString()` / `Diff.getFormatValue()` 等**输出展示**环节,字段的比较逻辑仍基于原始值。两个对象该字段原始值不同,即使脱敏后的展示串相同,仍会被判定为有差异。

#### 结构化 diff 结果
`DiffUtils.diff(...)` 返回格式化的字符串;如果需要以编程方式获取结构化结果,使用 `DiffUtils.diffResult(...)`:

```java
DiffResult<Person> result = DiffUtils.diffResult(person1, person2);

boolean diff = result.isDiff(); // 是否存在差异
int count = result.getNumberOfDiffs(); // 差异数量
List<Diff<?>> diffs = result.getDiffs(); // 差异列表(不可修改)

for (Diff<?> d : result) { // DiffResult 实现了 Iterable<Diff<?>>
String field = d.getFieldName(); // 字段路径,如 address.country
String name = d.getActualName(); // 展示名(别名优先)
Object left = d.getLeft(); // 左侧(旧)值
Object right = d.getRight(); // 右侧(新)值
Object formatLeft = d.getFormatValue(left); // 格式化/脱敏后的展示值
}
```

#### 输出前缀与自定义样式
```java
// 为输出中的所有字段添加前缀
String diff = DiffUtils.diff(source, target, "user", "excludeField1", "excludeField2");

// 使用自定义 ToStringStyle
String diff = DiffUtils.diff(source, target, new MyToStringStyle(), "excludeField1");
```

#### 其他行为说明
- `static`、`transient` 字段不参与比较。
- 字段反射结果按 `Class` 缓存,频繁比较同一类型的对象时性能更好。
- 默认使用 `equals` / 引用相等进行比较,可通过 `@DiffIgnore`、`@DiffCompare`、`@DiffBean`、`@DiffFormat` 等注解覆盖默认行为。