feat: 新增对 未知key 的处理逻辑
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:
@@ -135,19 +135,18 @@ public class SchemaCheckAnalyzer {
|
||||
oldSchema.getConfidence(), newSchema.getConfidence());
|
||||
enrich(changes, nw, confidence);
|
||||
allChanges.addAll(changes);
|
||||
mergeKeyChange(keyChanges, nw.getResolvedKeyPattern(), changes,
|
||||
mergeKeyChange(keyChanges, nw, changes,
|
||||
skeletonRenderer.render(oldSchema, protectedPaths(changes),
|
||||
SkeletonJsonRenderer.DEFAULT_MAX_LEN),
|
||||
skeletonRenderer.render(newSchema, protectedPaths(changes),
|
||||
SkeletonJsonRenderer.DEFAULT_MAX_LEN));
|
||||
} else if (fileChanged) {
|
||||
SchemaChange c = new SchemaChange(ChangeType.WRITE_POINT_ADDED);
|
||||
c.setKeyPattern(nw.getResolvedKeyPattern());
|
||||
c.setWriteLocation(nw.location());
|
||||
c.setMessage("新增缓存写入点,value 类型: " + shortType(nw.getResolvedValueType()));
|
||||
fillFromWritePoint(c, nw);
|
||||
c.setMessage("新增缓存写入点,value 类型: " + displayType(nw));
|
||||
allChanges.add(c);
|
||||
TypeSchema newSchema = extractorNew.extract(nw.getResolvedValueType(), nw.isRootArray());
|
||||
mergeKeyChange(keyChanges, nw.getResolvedKeyPattern(),
|
||||
mergeKeyChange(keyChanges, nw,
|
||||
Collections.singletonList(c),
|
||||
"",
|
||||
skeletonRenderer.render(newSchema, protectedPaths(
|
||||
@@ -161,12 +160,11 @@ public class SchemaCheckAnalyzer {
|
||||
if (!newSigs.contains(ow.signature())
|
||||
&& !isKeyIgnored(ow.getResolvedKeyPattern()) && !isWriterIgnored(ow)) {
|
||||
SchemaChange c = new SchemaChange(ChangeType.WRITE_POINT_REMOVED);
|
||||
c.setKeyPattern(ow.getResolvedKeyPattern());
|
||||
c.setWriteLocation(ow.location());
|
||||
c.setMessage("删除缓存写入点,原 value 类型: " + shortType(ow.getResolvedValueType()));
|
||||
fillFromWritePoint(c, ow);
|
||||
c.setMessage("删除缓存写入点,原 value 类型: " + displayType(ow));
|
||||
allChanges.add(c);
|
||||
TypeSchema oldSchema = extractorOld.extract(ow.getResolvedValueType(), ow.isRootArray());
|
||||
mergeKeyChange(keyChanges, ow.getResolvedKeyPattern(),
|
||||
mergeKeyChange(keyChanges, ow,
|
||||
Collections.singletonList(c),
|
||||
skeletonRenderer.render(oldSchema, protectedPaths(
|
||||
Collections.singletonList(c)),
|
||||
@@ -178,19 +176,31 @@ public class SchemaCheckAnalyzer {
|
||||
}
|
||||
|
||||
List<SchemaChange> finalChanges = postProcess(allChanges);
|
||||
// 按最终字段明细重新对齐 key 摘要(过滤被 suppress/dedup 掉的)
|
||||
Map<String, KeyStructureChange> finalKeys = filterKeyChanges(keyChanges, finalChanges);
|
||||
return buildReport(oldSha, newSha, finalChanges, finalKeys);
|
||||
}
|
||||
|
||||
private void mergeKeyChange(Map<String, KeyStructureChange> keyChanges, String keyPattern,
|
||||
private void mergeKeyChange(Map<String, KeyStructureChange> keyChanges, WritePoint wp,
|
||||
List<SchemaChange> changes, String oldSkeleton, String newSkeleton) {
|
||||
String key = keyPattern == null ? "<unknown>" : keyPattern;
|
||||
KeyStructureChange kc = keyChanges.computeIfAbsent(key, k -> {
|
||||
String aggKey = aggregationKey(wp);
|
||||
KeyStructureChange kc = keyChanges.computeIfAbsent(aggKey, k -> {
|
||||
KeyStructureChange n = new KeyStructureChange();
|
||||
n.setKeyPattern(k);
|
||||
n.setKeyPattern(wp.getResolvedKeyPattern());
|
||||
n.setKeyExpression(wp.getKeyExpression());
|
||||
n.setWriteLocation(wp.location());
|
||||
n.setValueType(displayType(wp));
|
||||
n.setKeyUnresolved(isUnresolvedKey(wp.getResolvedKeyPattern()));
|
||||
return n;
|
||||
});
|
||||
if (kc.getWriteLocation() == null || kc.getWriteLocation().isEmpty()) {
|
||||
kc.setWriteLocation(wp.location());
|
||||
}
|
||||
if (kc.getValueType() == null || kc.getValueType().isEmpty()) {
|
||||
kc.setValueType(displayType(wp));
|
||||
}
|
||||
if (kc.getKeyExpression() == null || kc.getKeyExpression().isEmpty()) {
|
||||
kc.setKeyExpression(wp.getKeyExpression());
|
||||
}
|
||||
kc.getFieldDetails().addAll(changes);
|
||||
for (SchemaChange c : changes) {
|
||||
kc.raiseSeverity(c.getSeverity());
|
||||
@@ -209,6 +219,41 @@ public class SchemaCheckAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
/** 已解析 key 按模式聚合;未解析按「位置+表达式」拆分,避免串单。 */
|
||||
private String aggregationKey(WritePoint wp) {
|
||||
String pattern = wp.getResolvedKeyPattern();
|
||||
if (!isUnresolvedKey(pattern)) {
|
||||
return pattern == null ? "<unknown>" : pattern;
|
||||
}
|
||||
return "unknown|" + nvl(wp.location()) + "|" + nvl(wp.getKeyExpression());
|
||||
}
|
||||
|
||||
private boolean isUnresolvedKey(String keyPattern) {
|
||||
return keyPattern == null
|
||||
|| keyPattern.isEmpty()
|
||||
|| "unknown-key".equals(keyPattern)
|
||||
|| "<unknown>".equals(keyPattern);
|
||||
}
|
||||
|
||||
private String displayType(WritePoint wp) {
|
||||
String simple = shortType(wp.getResolvedValueType());
|
||||
if (wp.isRootArray()) {
|
||||
return "List<" + simple + ">";
|
||||
}
|
||||
return simple;
|
||||
}
|
||||
|
||||
private void fillFromWritePoint(SchemaChange c, WritePoint wp) {
|
||||
c.setKeyPattern(wp.getResolvedKeyPattern());
|
||||
c.setKeyExpression(wp.getKeyExpression());
|
||||
c.setWriteLocation(wp.location());
|
||||
c.setValueType(displayType(wp));
|
||||
}
|
||||
|
||||
private String nvl(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
|
||||
private Set<String> protectedPaths(List<SchemaChange> changes) {
|
||||
Set<String> paths = new LinkedHashSet<>();
|
||||
for (SchemaChange c : changes) {
|
||||
@@ -230,48 +275,64 @@ public class SchemaCheckAnalyzer {
|
||||
|
||||
private Map<String, KeyStructureChange> filterKeyChanges(
|
||||
Map<String, KeyStructureChange> keyChanges, List<SchemaChange> finalChanges) {
|
||||
Set<String> liveKeys = new HashSet<>();
|
||||
Set<String> liveDedup = new HashSet<>();
|
||||
for (SchemaChange c : finalChanges) {
|
||||
liveKeys.add(c.getKeyPattern() == null ? "<unknown>" : c.getKeyPattern());
|
||||
liveDedup.add(changeDedupKey(c));
|
||||
}
|
||||
Map<String, KeyStructureChange> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, KeyStructureChange> e : keyChanges.entrySet()) {
|
||||
if (liveKeys.contains(e.getKey())) {
|
||||
KeyStructureChange kc = e.getValue();
|
||||
// 仅保留最终明细中仍存在的字段变更
|
||||
List<SchemaChange> retained = new ArrayList<>();
|
||||
Set<String> finalDedup = new HashSet<>();
|
||||
for (SchemaChange c : finalChanges) {
|
||||
String k = c.getKeyPattern() == null ? "<unknown>" : c.getKeyPattern();
|
||||
if (!k.equals(e.getKey())) {
|
||||
continue;
|
||||
}
|
||||
String dedupKey = c.getChangeType() + "|" + c.getKeyPattern() + "|"
|
||||
+ c.getWriteLocation() + "|" + c.getFieldPath();
|
||||
if (finalDedup.add(dedupKey)) {
|
||||
retained.add(c);
|
||||
}
|
||||
KeyStructureChange kc = e.getValue();
|
||||
List<SchemaChange> retained = new ArrayList<>();
|
||||
Set<String> seen = new HashSet<>();
|
||||
for (SchemaChange c : kc.getFieldDetails()) {
|
||||
String dk = changeDedupKey(c);
|
||||
if (liveDedup.contains(dk) && seen.add(dk)) {
|
||||
retained.add(c);
|
||||
}
|
||||
kc.getFieldDetails().clear();
|
||||
kc.getFieldDetails().addAll(retained);
|
||||
Severity max = Severity.P2;
|
||||
for (SchemaChange c : retained) {
|
||||
if (c.getSeverity() != null && c.getSeverity().ordinal() < max.ordinal()) {
|
||||
max = c.getSeverity();
|
||||
}
|
||||
}
|
||||
kc.setSeverity(max);
|
||||
result.put(e.getKey(), kc);
|
||||
}
|
||||
if (retained.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
kc.getFieldDetails().clear();
|
||||
kc.getFieldDetails().addAll(retained);
|
||||
// 从明细回填通用展示字段(若聚合时未带上)
|
||||
for (SchemaChange c : retained) {
|
||||
if ((kc.getWriteLocation() == null || kc.getWriteLocation().isEmpty())
|
||||
&& c.getWriteLocation() != null) {
|
||||
kc.setWriteLocation(c.getWriteLocation());
|
||||
}
|
||||
if ((kc.getValueType() == null || kc.getValueType().isEmpty())
|
||||
&& c.getValueType() != null) {
|
||||
kc.setValueType(c.getValueType());
|
||||
}
|
||||
if ((kc.getKeyExpression() == null || kc.getKeyExpression().isEmpty())
|
||||
&& c.getKeyExpression() != null) {
|
||||
kc.setKeyExpression(c.getKeyExpression());
|
||||
}
|
||||
}
|
||||
Severity max = Severity.P2;
|
||||
for (SchemaChange c : retained) {
|
||||
if (c.getSeverity() != null && c.getSeverity().ordinal() < max.ordinal()) {
|
||||
max = c.getSeverity();
|
||||
}
|
||||
}
|
||||
kc.setSeverity(max);
|
||||
result.put(e.getKey(), kc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String changeDedupKey(SchemaChange c) {
|
||||
return c.getChangeType() + "|" + c.getKeyPattern() + "|"
|
||||
+ c.getWriteLocation() + "|" + c.getFieldPath();
|
||||
}
|
||||
|
||||
private void enrich(List<SchemaChange> changes, WritePoint wp, double confidence) {
|
||||
boolean lowConfidence = confidence < config.getDetection().getMinConfidence();
|
||||
String type = displayType(wp);
|
||||
for (SchemaChange c : changes) {
|
||||
c.setKeyPattern(wp.getResolvedKeyPattern());
|
||||
c.setWriteLocation(wp.location());
|
||||
fillFromWritePoint(c, wp);
|
||||
c.setValueType(type);
|
||||
if (lowConfidence) {
|
||||
c.setSeverity(Severity.P2);
|
||||
c.setMessage(c.getMessage() + "(低置信度,建议人工确认)");
|
||||
|
||||
Reference in New Issue
Block a user