From a6fdc5fc5efe39bf74a5946199f26c8da1fadd6c Mon Sep 17 00:00:00 2001 From: dongzi Date: Tue, 14 Jul 2026 11:38:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=95=88=E6=9E=9C=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/report/ReportBuilder.java | 13 ++-- .../cache/report/SkeletonAnnotator.java | 69 +++---------------- .../cache/report/ReportBuilderTest.java | 56 ++++++++------- 3 files changed, 50 insertions(+), 88 deletions(-) diff --git a/src/main/java/com/codechecker/cache/report/ReportBuilder.java b/src/main/java/com/codechecker/cache/report/ReportBuilder.java index 2952649..7b6b009 100644 --- a/src/main/java/com/codechecker/cache/report/ReportBuilder.java +++ b/src/main/java/com/codechecker/cache/report/ReportBuilder.java @@ -57,9 +57,9 @@ public class ReportBuilder { Set oldHighlight = SkeletonAnnotator.pathsForOldSkeleton(kc.getFieldDetails()); Set newHighlight = SkeletonAnnotator.pathsForNewSkeleton(kc.getFieldDetails()); String oldRendered = oldJson.isEmpty() - ? "" : SkeletonAnnotator.annotateAsCodeBlock(oldJson, oldHighlight); + ? "" : "“" + SkeletonAnnotator.annotateForWecom(oldJson, oldHighlight) + "”"; String newRendered = newJson.isEmpty() - ? "" : SkeletonAnnotator.annotateAsCodeBlock(newJson, newHighlight); + ? "" : "“" + SkeletonAnnotator.annotateForWecom(newJson, newHighlight) + "”"; if (oldJson.isEmpty() && !newJson.isEmpty()) { sb.append(" value 新增为 ").append(newRendered).append("\n\n"); } else if (!oldJson.isEmpty() && newJson.isEmpty()) { @@ -73,13 +73,14 @@ public class ReportBuilder { } /** - * Key 行:Key --> + 橙红色 key(企微 warning)。 - * 末尾加零宽空格,避免 key 中的 * 干扰后续 markdown。 + * Key 行:warning 颜色 + 行内代码 + 加粗。 + * 企微支持混用;代码样式可能略压过加粗,但颜色与代码块会稳定生效。 + * key 包在反引号内,避免路径中的 {@code *} 干扰 markdown。 */ private void appendKeyLine(StringBuilder sb, String keyPattern) { - sb.append("- Key --> ") + sb.append("- Key --> **`") .append(nvl(keyPattern)) - .append("\u200b\n"); + .append("`**\n"); } /** diff --git a/src/main/java/com/codechecker/cache/report/SkeletonAnnotator.java b/src/main/java/com/codechecker/cache/report/SkeletonAnnotator.java index 33a3bf4..a448770 100644 --- a/src/main/java/com/codechecker/cache/report/SkeletonAnnotator.java +++ b/src/main/java/com/codechecker/cache/report/SkeletonAnnotator.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Set; /** - * 在骨架 JSON 中为改动字段加企微 warning 标注。 + * 在骨架 JSON 中为改动字段加企微 warning 标注(仅改动片段染色,其余明文)。 *
    *
  • 删除字段 → 标在旧骨架
  • *
  • 新增字段 / 新增包装层 → 标在新骨架
  • @@ -66,15 +66,19 @@ final class SkeletonAnnotator { } /** - * 标注路径对应的 {@code "field":value} 片段,并包成可与代码块混排的企微 Markdown。 - * 企微代码块内不解析 font,故整段用反引号分段包裹,改动项单独用 warning+反引号。 + * 仅标注改动字段:其余正文保持普通文本(避免整段包反引号导致企微整段变红)。 + * 改动片段格式:{@code "field":value} */ - static String annotateAsCodeBlock(String json, Set highlightPaths) { + static String annotateForWecom(String json, Set highlightPaths) { if (json == null || json.isEmpty()) { - return "``"; + return ""; } - String annotated = annotate(json, highlightPaths); - return toMixedCodeMarkdown(annotated); + return annotate(json, highlightPaths); + } + + /** @deprecated 使用 {@link #annotateForWecom},勿再整段包代码块 */ + static String annotateAsCodeBlock(String json, Set highlightPaths) { + return annotateForWecom(json, highlightPaths); } static String annotate(String json, Set highlightPaths) { @@ -90,57 +94,6 @@ final class SkeletonAnnotator { return result; } - /** - * 将已含 {@code } 的字符串拆成:普通段走反引号代码块,改动段走 warning+反引号。 - */ - static String toMixedCodeMarkdown(String annotated) { - if (annotated == null || annotated.isEmpty()) { - return "``"; - } - if (!annotated.contains(FONT_OPEN)) { - return "`" + annotated + "`"; - } - StringBuilder out = new StringBuilder(); - int i = 0; - boolean inCode = false; - while (i < annotated.length()) { - int start = annotated.indexOf(FONT_OPEN, i); - if (start < 0) { - if (!inCode) { - out.append('`'); - inCode = true; - } - out.append(annotated.substring(i)); - out.append('`'); - return out.toString(); - } - if (start > i) { - if (!inCode) { - out.append('`'); - inCode = true; - } - out.append(annotated, i, start); - out.append('`'); - inCode = false; - } else if (inCode) { - out.append('`'); - inCode = false; - } - int contentStart = start + FONT_OPEN.length(); - int end = annotated.indexOf(FONT_CLOSE, contentStart); - if (end < 0) { - if (!inCode) { - out.append('`'); - } - out.append(annotated.substring(start)).append('`'); - return out.toString(); - } - String frag = annotated.substring(contentStart, end); - out.append(FONT_OPEN).append('`').append(frag).append('`').append(FONT_CLOSE); - i = end + FONT_CLOSE.length(); - } - return out.toString(); - } private static void addIfPresent(Set paths, String path) { if (path != null && !path.trim().isEmpty()) { diff --git a/src/test/java/com/codechecker/cache/report/ReportBuilderTest.java b/src/test/java/com/codechecker/cache/report/ReportBuilderTest.java index 80ffb73..254e904 100644 --- a/src/test/java/com/codechecker/cache/report/ReportBuilderTest.java +++ b/src/test/java/com/codechecker/cache/report/ReportBuilderTest.java @@ -16,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class ReportBuilderTest { @Test - void wecomMarkdownShowsSkeletonWithoutSeverityAndLocation() { + void onlyAddedFieldsHighlightedInNewSkeletonNotWholeJson() { CheckReport report = new CheckReport(); report.setRepository("jnpf-java-cloud"); report.setBranch("code/redis_change_detection_v1.0"); @@ -40,12 +40,14 @@ class ReportBuilderTest { report.getChanges().add(addedMsg); report.getChanges().add(addedErr); + String oldJson = "{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0}}"; + String newJson = "{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0}," + + "\"message\":\"\",\"lastError\":\"\"}"; + KeyStructureChange key = new KeyStructureChange(); key.setKeyPattern("saas:period-config:migration:current"); - key.setOldSkeletonJson( - "{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0}}"); - key.setNewSkeletonJson( - "{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0},\"message\":\"\",\"lastError\":\"\"}"); + key.setOldSkeletonJson(oldJson); + key.setNewSkeletonJson(newJson); key.setSeverity(Severity.P1); key.getFieldDetails().add(addedMsg); key.getFieldDetails().add(addedErr); @@ -53,20 +55,27 @@ class ReportBuilderTest { String md = new ReportBuilder("[缓存结构变更]").toMarkdown(report); - assertTrue(md.startsWith("## [缓存结构变更] jnpf-java-cloud")); - assertTrue(md.contains("- Key --> saas:period-config:migration:current\u200b")); - assertTrue(md.contains("value 值从")); - assertTrue(md.contains("变更为")); - // 新增字段仅在新骨架中 warning 标注 - assertTrue(md.contains("`\"message\":\"\"`")); - assertTrue(md.contains("`\"lastError\":\"\"`")); - // 旧骨架不应出现这两个字段的 warning + assertTrue(md.contains( + "- Key --> **`saas:period-config:migration:current`**")); + int oldSection = md.indexOf("value 值从"); int newSection = md.indexOf("变更为"); String oldPart = md.substring(oldSection, newSection); - assertFalse(oldPart.contains("`\"message\":\"\"`")); + String newPart = md.substring(newSection); + + // 旧骨架:无 warning,且不能整段被反引号包裹 + assertFalse(oldPart.contains("")); + assertFalse(oldPart.contains("`" + oldJson + "`")); + assertTrue(oldPart.contains(oldJson)); + + // 新骨架:仅新增字段带 warning,其余明文 + assertTrue(newPart.contains("\"message\":\"\"")); + assertTrue(newPart.contains("\"lastError\":\"\"")); + assertTrue(newPart.contains("\"taskId\":\"\"")); + assertFalse(newPart.contains("\"taskId\":\"\"")); + // 禁止整段代码块 + assertFalse(newPart.contains("`" + newJson)); assertFalse(md.contains("### P0")); - assertFalse(md.contains("位置")); } @Test @@ -83,11 +92,12 @@ class ReportBuilderTest { assertEquals(Collections.singleton("lastError"), oldPaths); assertTrue(newPaths.isEmpty()); - String oldMd = SkeletonAnnotator.annotateAsCodeBlock(oldJson, oldPaths); - String newMd = SkeletonAnnotator.annotateAsCodeBlock(newJson, newPaths); - assertTrue(oldMd.contains("`\"lastError\":\"\"`")); - assertFalse(newMd.contains("lastError")); - assertTrue(newMd.startsWith("`")); + String oldMd = SkeletonAnnotator.annotateForWecom(oldJson, oldPaths); + String newMd = SkeletonAnnotator.annotateForWecom(newJson, newPaths); + assertTrue(oldMd.contains("\"lastError\":\"\"")); + assertFalse(oldMd.contains("\"taskId\":\"\"")); + assertEquals(newJson, newMd); + assertFalse(newMd.startsWith("`")); } @Test @@ -114,11 +124,9 @@ class ReportBuilderTest { String console = new ReportBuilder("[缓存结构变更]").toConsole(report); assertTrue(console.contains("======== 字段明细 ========")); - assertTrue(console.contains("**位置**: Foo#bar:1")); assertTrue(console.contains("**删除字段**: x")); - assertTrue(console.contains("======== 企微 Markdown ========")); - assertTrue(console.contains("value 值从")); - assertTrue(console.contains("`\"x\":\"\"`")); + assertTrue(console.contains("\"x\":\"\"")); + assertFalse(console.contains("`{\"x\":\"\"}`")); } @Test