feat: 项目整体命名修改cache-schema-checker
All checks were successful
缓存序列化结构检查 / cache-schema-check (push) Has been skipped
All checks were successful
缓存序列化结构检查 / cache-schema-check (push) Has been skipped
This commit is contained in:
32
src/main/java/com/codechecker/cache/diff/ChangeType.java
vendored
Normal file
32
src/main/java/com/codechecker/cache/diff/ChangeType.java
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.codechecker.cache.diff;
|
||||
|
||||
/**
|
||||
* 结构变更类型及其默认严重级别。
|
||||
*/
|
||||
public enum ChangeType {
|
||||
FIELD_REMOVED(Severity.P0, "字段删除"),
|
||||
TYPE_CHANGED(Severity.P0, "字段类型变更"),
|
||||
WRAPPER_ADDED(Severity.P0, "新增包装层"),
|
||||
FIELD_PATH_MOVED(Severity.P0, "字段路径迁移"),
|
||||
FIELD_ADDED(Severity.P1, "新增字段"),
|
||||
KEY_PATTERN_CHANGED(Severity.P1, "Key 模式变更"),
|
||||
WRITE_POINT_REMOVED(Severity.P1, "删除写入点"),
|
||||
WRITE_POINT_ADDED(Severity.P2, "新增写入点"),
|
||||
LOW_CONFIDENCE(Severity.P2, "低置信度结构变更");
|
||||
|
||||
private final Severity defaultSeverity;
|
||||
private final String label;
|
||||
|
||||
ChangeType(Severity defaultSeverity, String label) {
|
||||
this.defaultSeverity = defaultSeverity;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public Severity getDefaultSeverity() {
|
||||
return defaultSeverity;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
85
src/main/java/com/codechecker/cache/diff/SchemaChange.java
vendored
Normal file
85
src/main/java/com/codechecker/cache/diff/SchemaChange.java
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package com.codechecker.cache.diff;
|
||||
|
||||
/**
|
||||
* 一条结构变更记录。
|
||||
*/
|
||||
public class SchemaChange {
|
||||
|
||||
private Severity severity;
|
||||
private ChangeType changeType;
|
||||
private String keyPattern;
|
||||
private String writeLocation;
|
||||
private String fieldPath;
|
||||
private String oldValue;
|
||||
private String newValue;
|
||||
private String message;
|
||||
|
||||
public SchemaChange(ChangeType changeType) {
|
||||
this.changeType = changeType;
|
||||
this.severity = changeType.getDefaultSeverity();
|
||||
}
|
||||
|
||||
public Severity getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public void setSeverity(Severity severity) {
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
public ChangeType getChangeType() {
|
||||
return changeType;
|
||||
}
|
||||
|
||||
public void setChangeType(ChangeType changeType) {
|
||||
this.changeType = changeType;
|
||||
}
|
||||
|
||||
public String getKeyPattern() {
|
||||
return keyPattern;
|
||||
}
|
||||
|
||||
public void setKeyPattern(String keyPattern) {
|
||||
this.keyPattern = keyPattern;
|
||||
}
|
||||
|
||||
public String getWriteLocation() {
|
||||
return writeLocation;
|
||||
}
|
||||
|
||||
public void setWriteLocation(String writeLocation) {
|
||||
this.writeLocation = writeLocation;
|
||||
}
|
||||
|
||||
public String getFieldPath() {
|
||||
return fieldPath;
|
||||
}
|
||||
|
||||
public void setFieldPath(String fieldPath) {
|
||||
this.fieldPath = fieldPath;
|
||||
}
|
||||
|
||||
public String getOldValue() {
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public void setOldValue(String oldValue) {
|
||||
this.oldValue = oldValue;
|
||||
}
|
||||
|
||||
public String getNewValue() {
|
||||
return newValue;
|
||||
}
|
||||
|
||||
public void setNewValue(String newValue) {
|
||||
this.newValue = newValue;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
151
src/main/java/com/codechecker/cache/diff/SchemaDiffer.java
vendored
Normal file
151
src/main/java/com/codechecker/cache/diff/SchemaDiffer.java
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
package com.codechecker.cache.diff;
|
||||
|
||||
import com.codechecker.cache.schema.FieldSchema;
|
||||
import com.codechecker.cache.schema.JsonType;
|
||||
import com.codechecker.cache.schema.TypeSchema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 对比两个版本的 {@link TypeSchema},输出结构变更列表。基于叶子字段路径集合进行差异分析。
|
||||
*/
|
||||
public class SchemaDiffer {
|
||||
|
||||
/**
|
||||
* @return 结构变更列表(不含 keyPattern/location 上下文,由调用方补充)
|
||||
*/
|
||||
public List<SchemaChange> diff(TypeSchema oldSchema, TypeSchema newSchema) {
|
||||
List<SchemaChange> changes = new ArrayList<>();
|
||||
|
||||
Map<String, JsonType> oldLeaves = leaves(oldSchema);
|
||||
Map<String, JsonType> newLeaves = leaves(newSchema);
|
||||
|
||||
Set<String> removed = new LinkedHashSet<>(oldLeaves.keySet());
|
||||
removed.removeAll(newLeaves.keySet());
|
||||
Set<String> added = new LinkedHashSet<>(newLeaves.keySet());
|
||||
added.removeAll(oldLeaves.keySet());
|
||||
|
||||
// 类型变更(同路径)
|
||||
for (String path : oldLeaves.keySet()) {
|
||||
if (newLeaves.containsKey(path)) {
|
||||
JsonType oldType = oldLeaves.get(path);
|
||||
JsonType newType = newLeaves.get(path);
|
||||
if (oldType != newType) {
|
||||
SchemaChange c = new SchemaChange(ChangeType.TYPE_CHANGED);
|
||||
c.setFieldPath(path);
|
||||
c.setOldValue(oldType.name());
|
||||
c.setNewValue(newType.name());
|
||||
c.setMessage("字段 " + path + " 类型由 " + oldType + " 变为 " + newType);
|
||||
changes.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 路径迁移检测(removed 的路径是某 added 路径的后缀)
|
||||
List<String[]> moves = new ArrayList<>();
|
||||
Set<String> matchedRemoved = new LinkedHashSet<>();
|
||||
Set<String> matchedAdded = new LinkedHashSet<>();
|
||||
for (String r : removed) {
|
||||
for (String a : added) {
|
||||
if (matchedAdded.contains(a)) {
|
||||
continue;
|
||||
}
|
||||
if (isSuffix(a, r)) {
|
||||
moves.add(new String[]{r, a});
|
||||
matchedRemoved.add(r);
|
||||
matchedAdded.add(a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 包装层检测:多个迁移共享同一新前缀
|
||||
Map<String, Integer> prefixCount = new LinkedHashMap<>();
|
||||
for (String[] move : moves) {
|
||||
String a = move[1];
|
||||
if (a.contains(".")) {
|
||||
String prefix = a.substring(0, a.indexOf('.'));
|
||||
prefixCount.merge(prefix, 1, Integer::sum);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, Integer> e : prefixCount.entrySet()) {
|
||||
if (e.getValue() >= 2) {
|
||||
SchemaChange c = new SchemaChange(ChangeType.WRAPPER_ADDED);
|
||||
c.setFieldPath(e.getKey());
|
||||
c.setNewValue(e.getKey());
|
||||
c.setMessage("新增包装层 " + e.getKey() + ",原顶层字段被下移至该层(影响 " + e.getValue() + " 个字段)");
|
||||
changes.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
for (String[] move : moves) {
|
||||
SchemaChange c = new SchemaChange(ChangeType.FIELD_PATH_MOVED);
|
||||
c.setFieldPath(move[1]);
|
||||
c.setOldValue(move[0]);
|
||||
c.setNewValue(move[1]);
|
||||
c.setMessage("字段路径迁移:" + move[0] + " → " + move[1]);
|
||||
changes.add(c);
|
||||
}
|
||||
|
||||
// 剩余删除
|
||||
for (String r : removed) {
|
||||
if (matchedRemoved.contains(r)) {
|
||||
continue;
|
||||
}
|
||||
SchemaChange c = new SchemaChange(ChangeType.FIELD_REMOVED);
|
||||
c.setFieldPath(r);
|
||||
c.setOldValue(oldLeaves.get(r).name());
|
||||
c.setMessage("删除字段 " + r);
|
||||
changes.add(c);
|
||||
}
|
||||
|
||||
// 剩余新增
|
||||
for (String a : added) {
|
||||
if (matchedAdded.contains(a)) {
|
||||
continue;
|
||||
}
|
||||
SchemaChange c = new SchemaChange(ChangeType.FIELD_ADDED);
|
||||
c.setFieldPath(a);
|
||||
c.setNewValue(newLeaves.get(a).name());
|
||||
c.setMessage("新增字段 " + a);
|
||||
changes.add(c);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private Map<String, JsonType> leaves(TypeSchema schema) {
|
||||
Map<String, JsonType> result = new LinkedHashMap<>();
|
||||
for (FieldSchema f : schema.getFields().values()) {
|
||||
if (f.getJsonType() != JsonType.OBJECT && f.getJsonType() != JsonType.ARRAY) {
|
||||
result.put(f.getPath(), f.getJsonType());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 full 的按段后缀是否等于 suffix(如 vo.dbName 的后缀是 dbName)。
|
||||
*/
|
||||
private boolean isSuffix(String full, String suffix) {
|
||||
if (full.equals(suffix)) {
|
||||
return false;
|
||||
}
|
||||
String[] fullSeg = full.split("\\.");
|
||||
String[] sufSeg = suffix.split("\\.");
|
||||
if (sufSeg.length >= fullSeg.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i <= sufSeg.length; i++) {
|
||||
if (!fullSeg[fullSeg.length - i].equals(sufSeg[sufSeg.length - i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
10
src/main/java/com/codechecker/cache/diff/Severity.java
vendored
Normal file
10
src/main/java/com/codechecker/cache/diff/Severity.java
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.codechecker.cache.diff;
|
||||
|
||||
/**
|
||||
* 变更严重级别。
|
||||
*/
|
||||
public enum Severity {
|
||||
P0,
|
||||
P1,
|
||||
P2
|
||||
}
|
||||
Reference in New Issue
Block a user