This commit is contained in:
@@ -130,9 +130,9 @@ public class ReportBuilder {
|
||||
Set<String> oldHighlight = SkeletonAnnotator.pathsForOldSkeleton(kc.getFieldDetails());
|
||||
Set<String> newHighlight = SkeletonAnnotator.pathsForNewSkeleton(kc.getFieldDetails());
|
||||
String oldRendered = oldJson.isEmpty()
|
||||
? "" : "“" + SkeletonAnnotator.annotateForWecom(oldJson, oldHighlight) + "”";
|
||||
? "" : "“" + SkeletonAnnotator.annotateOldForWecom(oldJson, oldHighlight) + "”";
|
||||
String newRendered = newJson.isEmpty()
|
||||
? "" : "“" + SkeletonAnnotator.annotateForWecom(newJson, newHighlight) + "”";
|
||||
? "" : "“" + SkeletonAnnotator.annotateNewForWecom(newJson, newHighlight) + "”";
|
||||
if (oldJson.isEmpty() && !newJson.isEmpty()) {
|
||||
sb.append(" > **value 新增为:** ").append(newRendered).append("\n\n");
|
||||
} else if (!oldJson.isEmpty() && newJson.isEmpty()) {
|
||||
|
||||
@@ -10,16 +10,20 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 在骨架 JSON 中为改动字段加企微 warning 标注(仅改动片段染色,其余明文)。
|
||||
* 在骨架 JSON 中为改动字段加企微颜色标注(仅改动片段染色,其余明文)。
|
||||
* <ul>
|
||||
* <li>删除字段 → 标在旧骨架</li>
|
||||
* <li>新增字段 / 新增包装层 → 标在新骨架</li>
|
||||
* <li>路径迁移 → 旧路径标旧骨架、新路径标新骨架</li>
|
||||
* <li>删除字段 → 旧骨架,橙色 {@code warning}</li>
|
||||
* <li>新增字段 / 新增包装层 → 新骨架,绿色 {@code info}</li>
|
||||
* <li>路径迁移 → 旧路径橙、新路径绿</li>
|
||||
* </ul>
|
||||
*/
|
||||
final class SkeletonAnnotator {
|
||||
|
||||
private static final String FONT_OPEN = "<font color=\"warning\">";
|
||||
/** 企微橙:删除 / 旧侧变更 */
|
||||
static final String COLOR_REMOVE = "warning";
|
||||
/** 企微绿:新增 / 新侧变更 */
|
||||
static final String COLOR_ADD = "info";
|
||||
|
||||
private static final String FONT_CLOSE = "</font>";
|
||||
|
||||
private SkeletonAnnotator() {
|
||||
@@ -66,14 +70,32 @@ final class SkeletonAnnotator {
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅标注改动字段:其余正文保持普通文本(避免整段包反引号导致企微整段变红)。
|
||||
* 改动片段格式:{@code <font color="warning">"field":value</font>}
|
||||
* 标注旧骨架改动字段(删除 → 橙色)。
|
||||
*/
|
||||
static String annotateOldForWecom(String json, Set<String> highlightPaths) {
|
||||
return annotateForWecom(json, highlightPaths, COLOR_REMOVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标注新骨架改动字段(新增 → 绿色)。
|
||||
*/
|
||||
static String annotateNewForWecom(String json, Set<String> highlightPaths) {
|
||||
return annotateForWecom(json, highlightPaths, COLOR_ADD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅标注改动字段:其余正文保持普通文本。
|
||||
* 改动片段格式:{@code <font color="warning|info">"field":value</font>}
|
||||
*/
|
||||
static String annotateForWecom(String json, Set<String> highlightPaths) {
|
||||
return annotateForWecom(json, highlightPaths, COLOR_REMOVE);
|
||||
}
|
||||
|
||||
static String annotateForWecom(String json, Set<String> highlightPaths, String color) {
|
||||
if (json == null || json.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return annotate(json, highlightPaths);
|
||||
return annotate(json, highlightPaths, color);
|
||||
}
|
||||
|
||||
/** @deprecated 使用 {@link #annotateForWecom},勿再整段包代码块 */
|
||||
@@ -82,18 +104,27 @@ final class SkeletonAnnotator {
|
||||
}
|
||||
|
||||
static String annotate(String json, Set<String> highlightPaths) {
|
||||
return annotate(json, highlightPaths, COLOR_REMOVE);
|
||||
}
|
||||
|
||||
static String annotate(String json, Set<String> highlightPaths, String color) {
|
||||
if (json == null || json.isEmpty() || highlightPaths == null || highlightPaths.isEmpty()) {
|
||||
return json == null ? "" : json;
|
||||
}
|
||||
String fontOpen = fontOpen(color);
|
||||
String result = json;
|
||||
List<String> sorted = new ArrayList<>(highlightPaths);
|
||||
sorted.sort(Comparator.comparingInt(String::length).reversed());
|
||||
for (String path : sorted) {
|
||||
result = wrapOnePath(result, path);
|
||||
result = wrapOnePath(result, path, fontOpen);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String fontOpen(String color) {
|
||||
String c = color == null || color.isEmpty() ? COLOR_REMOVE : color;
|
||||
return "<font color=\"" + c + "\">";
|
||||
}
|
||||
|
||||
private static void addIfPresent(Set<String> paths, String path) {
|
||||
if (path != null && !path.trim().isEmpty()) {
|
||||
@@ -101,11 +132,10 @@ final class SkeletonAnnotator {
|
||||
}
|
||||
}
|
||||
|
||||
private static String wrapOnePath(String json, String path) {
|
||||
private static String wrapOnePath(String json, String path, String fontOpen) {
|
||||
if (path == null || path.isEmpty()) {
|
||||
return json;
|
||||
}
|
||||
// 已标注过则跳过(避免重复)
|
||||
String key = lastSegment(path);
|
||||
if (key.isEmpty()) {
|
||||
return json;
|
||||
@@ -114,8 +144,7 @@ final class SkeletonAnnotator {
|
||||
if (propStart < 0) {
|
||||
return json;
|
||||
}
|
||||
// 已在 font 内则跳过
|
||||
int fontBefore = json.lastIndexOf(FONT_OPEN, propStart);
|
||||
int fontBefore = json.lastIndexOf("<font color=\"", propStart);
|
||||
int fontCloseBefore = json.lastIndexOf(FONT_CLOSE, propStart);
|
||||
if (fontBefore > fontCloseBefore) {
|
||||
return json;
|
||||
@@ -127,11 +156,11 @@ final class SkeletonAnnotator {
|
||||
return json;
|
||||
}
|
||||
String frag = json.substring(propStart, valueEnd);
|
||||
if (frag.contains(FONT_OPEN)) {
|
||||
if (frag.contains("<font color=\"")) {
|
||||
return json;
|
||||
}
|
||||
return json.substring(0, propStart)
|
||||
+ FONT_OPEN + frag + FONT_CLOSE
|
||||
+ fontOpen + frag + FONT_CLOSE
|
||||
+ json.substring(valueEnd);
|
||||
}
|
||||
|
||||
@@ -169,7 +198,6 @@ final class SkeletonAnnotator {
|
||||
return -1;
|
||||
}
|
||||
} else if (c == '[') {
|
||||
// 数组:进入第一个元素对象
|
||||
int arrEnd = findMatchingBracket(json, valueStart);
|
||||
int elemObj = json.indexOf('{', valueStart);
|
||||
if (elemObj < 0 || elemObj >= arrEnd) {
|
||||
@@ -193,7 +221,6 @@ final class SkeletonAnnotator {
|
||||
if (idx < 0 || idx >= to) {
|
||||
return -1;
|
||||
}
|
||||
// 粗略校验:前面应是 { 或 ,
|
||||
int p = idx - 1;
|
||||
while (p >= from && Character.isWhitespace(json.charAt(p))) {
|
||||
p--;
|
||||
|
||||
@@ -71,23 +71,25 @@ class ReportBuilderTest {
|
||||
String oldPart = md.substring(oldSection, newSection);
|
||||
String newPart = md.substring(newSection);
|
||||
|
||||
// 旧骨架:无 warning,且不能整段被反引号包裹
|
||||
// 旧骨架:无染色,且不能整段被反引号包裹
|
||||
assertFalse(oldPart.contains("<font color=\"warning\">"));
|
||||
assertFalse(oldPart.contains("<font color=\"info\">"));
|
||||
assertFalse(oldPart.contains("`" + oldJson + "`"));
|
||||
assertTrue(oldPart.contains(oldJson));
|
||||
|
||||
// 新骨架:仅新增字段带 warning,其余明文
|
||||
assertTrue(newPart.contains("<font color=\"warning\">\"message\":\"\"</font>"));
|
||||
assertTrue(newPart.contains("<font color=\"warning\">\"lastError\":\"\"</font>"));
|
||||
// 新骨架:新增字段绿色 info,其余明文
|
||||
assertTrue(newPart.contains("<font color=\"info\">\"message\":\"\"</font>"));
|
||||
assertTrue(newPart.contains("<font color=\"info\">\"lastError\":\"\"</font>"));
|
||||
assertTrue(newPart.contains("\"taskId\":\"\""));
|
||||
assertFalse(newPart.contains("<font color=\"warning\">\"taskId\":\"\"</font>"));
|
||||
assertFalse(newPart.contains("<font color=\"info\">\"taskId\":\"\"</font>"));
|
||||
assertFalse(newPart.contains("<font color=\"warning\">"));
|
||||
// 禁止整段代码块
|
||||
assertFalse(newPart.contains("`" + newJson));
|
||||
assertFalse(md.contains("### P0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removedFieldsHighlightedOnlyInOldSkeleton() {
|
||||
void removedFieldsHighlightedOrangeInOldSkeleton() {
|
||||
SchemaChange removed = new SchemaChange(ChangeType.FIELD_REMOVED);
|
||||
removed.setFieldPath("lastError");
|
||||
removed.setMessage("删除字段 lastError");
|
||||
@@ -100,10 +102,11 @@ class ReportBuilderTest {
|
||||
assertEquals(Collections.singleton("lastError"), oldPaths);
|
||||
assertTrue(newPaths.isEmpty());
|
||||
|
||||
String oldMd = SkeletonAnnotator.annotateForWecom(oldJson, oldPaths);
|
||||
String newMd = SkeletonAnnotator.annotateForWecom(newJson, newPaths);
|
||||
String oldMd = SkeletonAnnotator.annotateOldForWecom(oldJson, oldPaths);
|
||||
String newMd = SkeletonAnnotator.annotateNewForWecom(newJson, newPaths);
|
||||
assertTrue(oldMd.contains("<font color=\"warning\">\"lastError\":\"\"</font>"));
|
||||
assertFalse(oldMd.contains("<font color=\"warning\">\"taskId\":\"\"</font>"));
|
||||
assertFalse(oldMd.contains("<font color=\"info\">"));
|
||||
assertEquals(newJson, newMd);
|
||||
assertFalse(newMd.startsWith("`"));
|
||||
}
|
||||
@@ -134,6 +137,7 @@ class ReportBuilderTest {
|
||||
assertTrue(console.contains("======== 字段明细 ========"));
|
||||
assertTrue(console.contains("**删除字段**: x"));
|
||||
assertTrue(console.contains("<font color=\"warning\">\"x\":\"\"</font>"));
|
||||
assertFalse(console.contains("<font color=\"info\">\"x\":\"\"</font>"));
|
||||
assertTrue(console.contains("> **位置**: `"));
|
||||
assertTrue(console.contains("> **类型**: `"));
|
||||
assertTrue(console.contains("> **value值由:**"));
|
||||
@@ -169,9 +173,10 @@ class ReportBuilderTest {
|
||||
String json = "{\"vo\":{\"dbName\":\"\",\"id\":\"\"},\"expiresAtMs\":0}";
|
||||
Set<String> paths = new LinkedHashSet<>();
|
||||
paths.add("vo.dbName");
|
||||
String out = SkeletonAnnotator.annotate(json, paths);
|
||||
assertTrue(out.contains("<font color=\"warning\">\"dbName\":\"\"</font>"));
|
||||
assertFalse(out.contains("<font color=\"warning\">\"id\":\"\"</font>"));
|
||||
String out = SkeletonAnnotator.annotate(json, paths, SkeletonAnnotator.COLOR_ADD);
|
||||
assertTrue(out.contains("<font color=\"info\">\"dbName\":\"\"</font>"));
|
||||
assertFalse(out.contains("<font color=\"info\">\"id\":\"\"</font>"));
|
||||
assertFalse(out.contains("<font color=\"warning\">\"dbName\":\"\"</font>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user