字段修改匹配规则优化
All checks were successful
类变更检测 / class-change-check (push) Successful in 14s

This commit is contained in:
2026-06-08 16:16:39 +08:00
parent ba1cc6aa4e
commit e367d4257f
4 changed files with 172 additions and 23 deletions

View File

@@ -5,37 +5,146 @@ import com.aicheck.model.FieldInfo;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 对比新旧字段列表,产出新增/删除/类型修改(纯注释变更忽略)。
* 对比新旧字段列表,产出新增/删除/类型修改/重命名(纯注释变更忽略)。
*/
public class FieldDiffEngine {
/** 按字段名对比,仅类型变化记为 MODIFIED */
/**
* 按字段名对比;删除+新增且说明匹配时合并为重命名。
* 输出顺序:按新字段声明顺序,未配对的删除字段置于末尾。
*/
public List<FieldChange> diff(List<FieldInfo> oldFields, List<FieldInfo> newFields) {
Map<String, FieldInfo> oldMap = toMap(oldFields);
Map<String, FieldInfo> newMap = toMap(newFields);
List<FieldChange> changes = new ArrayList<>();
for (Map.Entry<String, FieldInfo> entry : newMap.entrySet()) {
FieldInfo oldField = oldMap.get(entry.getKey());
FieldInfo newField = entry.getValue();
List<FieldChange> modified = new ArrayList<>();
List<FieldInfo> added = new ArrayList<>();
List<FieldInfo> removed = new ArrayList<>();
for (FieldInfo newField : newFields) {
FieldInfo oldField = oldMap.get(newField.getName());
if (oldField == null) {
changes.add(FieldChange.added(newField));
added.add(newField);
} else if (!oldField.getType().equals(newField.getType())) {
changes.add(FieldChange.modified(oldField, newField, buildTypeDetail(oldField, newField)));
modified.add(FieldChange.modified(oldField, newField, buildTypeDetail(oldField, newField)));
}
// 仅 @Schema / 注释文案变化:不纳入字段变更
}
for (Map.Entry<String, FieldInfo> entry : oldMap.entrySet()) {
if (!newMap.containsKey(entry.getKey())) {
changes.add(FieldChange.removed(entry.getValue()));
for (FieldInfo oldField : oldFields) {
if (!newMap.containsKey(oldField.getName())) {
removed.add(oldField);
}
}
return changes;
List<FieldChange> renamed = pairRenames(removed, added);
return mergeInOrder(newFields, renamed, modified, added, removed);
}
/**
* 将删除+新增配对为字段重命名。
* 优先:说明相同且类型相同;其次:说明相同但类型不同(重命名+改类型)。
*/
private List<FieldChange> pairRenames(List<FieldInfo> removed, List<FieldInfo> added) {
List<FieldChange> renames = new ArrayList<>();
Set<FieldInfo> matchedRemoved = new LinkedHashSet<>();
Set<FieldInfo> matchedAdded = new LinkedHashSet<>();
for (FieldInfo oldField : removed) {
FieldInfo pair = findRenamePair(oldField, added, matchedAdded, true);
if (pair == null) {
pair = findRenamePair(oldField, added, matchedAdded, false);
}
if (pair != null) {
renames.add(FieldChange.renamed(oldField, pair));
matchedRemoved.add(oldField);
matchedAdded.add(pair);
}
}
removed.removeIf(matchedRemoved::contains);
added.removeIf(matchedAdded::contains);
return renames;
}
private FieldInfo findRenamePair(FieldInfo removed, List<FieldInfo> added,
Set<FieldInfo> excluded, boolean requireSameType) {
for (FieldInfo candidate : added) {
if (excluded.contains(candidate)) {
continue;
}
if (!descriptionsMatch(removed, candidate)) {
continue;
}
if (requireSameType && !removed.getType().equals(candidate.getType())) {
continue;
}
if (!requireSameType && removed.getType().equals(candidate.getType())) {
continue;
}
return candidate;
}
return null;
}
/** 说明相同(非空)或双方均为空时视为匹配 */
private boolean descriptionsMatch(FieldInfo oldField, FieldInfo newField) {
String oldDesc = normalizeDescription(oldField.getDescription());
String newDesc = normalizeDescription(newField.getDescription());
if (oldDesc.isEmpty() && newDesc.isEmpty()) {
return true;
}
if (oldDesc.isEmpty() || newDesc.isEmpty()) {
return false;
}
return oldDesc.equals(newDesc);
}
private String normalizeDescription(String description) {
return description == null ? "" : description.trim();
}
/** 按新字段声明顺序合并各变更类型 */
private List<FieldChange> mergeInOrder(List<FieldInfo> newFields, List<FieldChange> renamed,
List<FieldChange> modified, List<FieldInfo> added,
List<FieldInfo> removed) {
Map<String, FieldChange> renamedByNewName = new LinkedHashMap<>();
for (FieldChange change : renamed) {
renamedByNewName.put(change.getFieldName(), change);
}
Map<String, FieldChange> modifiedByName = new LinkedHashMap<>();
for (FieldChange change : modified) {
modifiedByName.put(change.getFieldName(), change);
}
Set<String> emitted = new LinkedHashSet<>();
List<FieldChange> result = new ArrayList<>();
for (FieldInfo newField : newFields) {
String name = newField.getName();
if (renamedByNewName.containsKey(name)) {
result.add(renamedByNewName.get(name));
emitted.add(name);
} else if (modifiedByName.containsKey(name)) {
result.add(modifiedByName.get(name));
emitted.add(name);
} else if (added.stream().anyMatch(f -> f.getName().equals(name))) {
result.add(FieldChange.added(newField));
emitted.add(name);
}
}
for (FieldInfo oldField : removed) {
result.add(FieldChange.removed(oldField));
}
return result;
}
/** 字段列表转 LinkedHashMap保持声明顺序 */