This commit is contained in:
@@ -57,9 +57,9 @@ public class ReportBuilder {
|
|||||||
Set<String> oldHighlight = SkeletonAnnotator.pathsForOldSkeleton(kc.getFieldDetails());
|
Set<String> oldHighlight = SkeletonAnnotator.pathsForOldSkeleton(kc.getFieldDetails());
|
||||||
Set<String> newHighlight = SkeletonAnnotator.pathsForNewSkeleton(kc.getFieldDetails());
|
Set<String> newHighlight = SkeletonAnnotator.pathsForNewSkeleton(kc.getFieldDetails());
|
||||||
String oldRendered = oldJson.isEmpty()
|
String oldRendered = oldJson.isEmpty()
|
||||||
? "" : SkeletonAnnotator.annotateAsCodeBlock(oldJson, oldHighlight);
|
? "" : "“" + SkeletonAnnotator.annotateForWecom(oldJson, oldHighlight) + "”";
|
||||||
String newRendered = newJson.isEmpty()
|
String newRendered = newJson.isEmpty()
|
||||||
? "" : SkeletonAnnotator.annotateAsCodeBlock(newJson, newHighlight);
|
? "" : "“" + SkeletonAnnotator.annotateForWecom(newJson, newHighlight) + "”";
|
||||||
if (oldJson.isEmpty() && !newJson.isEmpty()) {
|
if (oldJson.isEmpty() && !newJson.isEmpty()) {
|
||||||
sb.append(" value 新增为 ").append(newRendered).append("\n\n");
|
sb.append(" value 新增为 ").append(newRendered).append("\n\n");
|
||||||
} else if (!oldJson.isEmpty() && newJson.isEmpty()) {
|
} else if (!oldJson.isEmpty() && newJson.isEmpty()) {
|
||||||
@@ -73,13 +73,14 @@ public class ReportBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key 行:Key --> + 橙红色 key(企微 warning)。
|
* Key 行:warning 颜色 + 行内代码 + 加粗。
|
||||||
* 末尾加零宽空格,避免 key 中的 * 干扰后续 markdown。
|
* 企微支持混用;代码样式可能略压过加粗,但颜色与代码块会稳定生效。
|
||||||
|
* key 包在反引号内,避免路径中的 {@code *} 干扰 markdown。
|
||||||
*/
|
*/
|
||||||
private void appendKeyLine(StringBuilder sb, String keyPattern) {
|
private void appendKeyLine(StringBuilder sb, String keyPattern) {
|
||||||
sb.append("- Key --> <font color=\"warning\">")
|
sb.append("- Key --> <font color=\"warning\">**`")
|
||||||
.append(nvl(keyPattern))
|
.append(nvl(keyPattern))
|
||||||
.append("\u200b</font>\n");
|
.append("`**</font>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在骨架 JSON 中为改动字段加企微 warning 标注。
|
* 在骨架 JSON 中为改动字段加企微 warning 标注(仅改动片段染色,其余明文)。
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>删除字段 → 标在旧骨架</li>
|
* <li>删除字段 → 标在旧骨架</li>
|
||||||
* <li>新增字段 / 新增包装层 → 标在新骨架</li>
|
* <li>新增字段 / 新增包装层 → 标在新骨架</li>
|
||||||
@@ -66,15 +66,19 @@ final class SkeletonAnnotator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标注路径对应的 {@code "field":value} 片段,并包成可与代码块混排的企微 Markdown。
|
* 仅标注改动字段:其余正文保持普通文本(避免整段包反引号导致企微整段变红)。
|
||||||
* 企微代码块内不解析 font,故整段用反引号分段包裹,改动项单独用 warning+反引号。
|
* 改动片段格式:{@code <font color="warning">"field":value</font>}
|
||||||
*/
|
*/
|
||||||
static String annotateAsCodeBlock(String json, Set<String> highlightPaths) {
|
static String annotateForWecom(String json, Set<String> highlightPaths) {
|
||||||
if (json == null || json.isEmpty()) {
|
if (json == null || json.isEmpty()) {
|
||||||
return "``";
|
return "";
|
||||||
}
|
}
|
||||||
String annotated = annotate(json, highlightPaths);
|
return annotate(json, highlightPaths);
|
||||||
return toMixedCodeMarkdown(annotated);
|
}
|
||||||
|
|
||||||
|
/** @deprecated 使用 {@link #annotateForWecom},勿再整段包代码块 */
|
||||||
|
static String annotateAsCodeBlock(String json, Set<String> highlightPaths) {
|
||||||
|
return annotateForWecom(json, highlightPaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String annotate(String json, Set<String> highlightPaths) {
|
static String annotate(String json, Set<String> highlightPaths) {
|
||||||
@@ -90,57 +94,6 @@ final class SkeletonAnnotator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 将已含 {@code <font>} 的字符串拆成:普通段走反引号代码块,改动段走 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<String> paths, String path) {
|
private static void addIfPresent(Set<String> paths, String path) {
|
||||||
if (path != null && !path.trim().isEmpty()) {
|
if (path != null && !path.trim().isEmpty()) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
class ReportBuilderTest {
|
class ReportBuilderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void wecomMarkdownShowsSkeletonWithoutSeverityAndLocation() {
|
void onlyAddedFieldsHighlightedInNewSkeletonNotWholeJson() {
|
||||||
CheckReport report = new CheckReport();
|
CheckReport report = new CheckReport();
|
||||||
report.setRepository("jnpf-java-cloud");
|
report.setRepository("jnpf-java-cloud");
|
||||||
report.setBranch("code/redis_change_detection_v1.0");
|
report.setBranch("code/redis_change_detection_v1.0");
|
||||||
@@ -40,12 +40,14 @@ class ReportBuilderTest {
|
|||||||
report.getChanges().add(addedMsg);
|
report.getChanges().add(addedMsg);
|
||||||
report.getChanges().add(addedErr);
|
report.getChanges().add(addedErr);
|
||||||
|
|
||||||
|
String oldJson = "{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0}}";
|
||||||
|
String newJson = "{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0},"
|
||||||
|
+ "\"message\":\"\",\"lastError\":\"\"}";
|
||||||
|
|
||||||
KeyStructureChange key = new KeyStructureChange();
|
KeyStructureChange key = new KeyStructureChange();
|
||||||
key.setKeyPattern("saas:period-config:migration:current");
|
key.setKeyPattern("saas:period-config:migration:current");
|
||||||
key.setOldSkeletonJson(
|
key.setOldSkeletonJson(oldJson);
|
||||||
"{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0}}");
|
key.setNewSkeletonJson(newJson);
|
||||||
key.setNewSkeletonJson(
|
|
||||||
"{\"taskId\":\"\",\"status\":\"\",\"progress\":{\"totalTenants\":0},\"message\":\"\",\"lastError\":\"\"}");
|
|
||||||
key.setSeverity(Severity.P1);
|
key.setSeverity(Severity.P1);
|
||||||
key.getFieldDetails().add(addedMsg);
|
key.getFieldDetails().add(addedMsg);
|
||||||
key.getFieldDetails().add(addedErr);
|
key.getFieldDetails().add(addedErr);
|
||||||
@@ -53,20 +55,27 @@ class ReportBuilderTest {
|
|||||||
|
|
||||||
String md = new ReportBuilder("[缓存结构变更]").toMarkdown(report);
|
String md = new ReportBuilder("[缓存结构变更]").toMarkdown(report);
|
||||||
|
|
||||||
assertTrue(md.startsWith("## [缓存结构变更] jnpf-java-cloud"));
|
assertTrue(md.contains(
|
||||||
assertTrue(md.contains("- Key --> <font color=\"warning\">saas:period-config:migration:current\u200b</font>"));
|
"- Key --> <font color=\"warning\">**`saas:period-config:migration:current`**</font>"));
|
||||||
assertTrue(md.contains("value 值从"));
|
|
||||||
assertTrue(md.contains("变更为"));
|
|
||||||
// 新增字段仅在新骨架中 warning 标注
|
|
||||||
assertTrue(md.contains("<font color=\"warning\">`\"message\":\"\"`</font>"));
|
|
||||||
assertTrue(md.contains("<font color=\"warning\">`\"lastError\":\"\"`</font>"));
|
|
||||||
// 旧骨架不应出现这两个字段的 warning
|
|
||||||
int oldSection = md.indexOf("value 值从");
|
int oldSection = md.indexOf("value 值从");
|
||||||
int newSection = md.indexOf("变更为");
|
int newSection = md.indexOf("变更为");
|
||||||
String oldPart = md.substring(oldSection, newSection);
|
String oldPart = md.substring(oldSection, newSection);
|
||||||
assertFalse(oldPart.contains("<font color=\"warning\">`\"message\":\"\"`</font>"));
|
String newPart = md.substring(newSection);
|
||||||
|
|
||||||
|
// 旧骨架:无 warning,且不能整段被反引号包裹
|
||||||
|
assertFalse(oldPart.contains("<font color=\"warning\">"));
|
||||||
|
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>"));
|
||||||
|
assertTrue(newPart.contains("\"taskId\":\"\""));
|
||||||
|
assertFalse(newPart.contains("<font color=\"warning\">\"taskId\":\"\"</font>"));
|
||||||
|
// 禁止整段代码块
|
||||||
|
assertFalse(newPart.contains("`" + newJson));
|
||||||
assertFalse(md.contains("### P0"));
|
assertFalse(md.contains("### P0"));
|
||||||
assertFalse(md.contains("位置"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -83,11 +92,12 @@ class ReportBuilderTest {
|
|||||||
assertEquals(Collections.singleton("lastError"), oldPaths);
|
assertEquals(Collections.singleton("lastError"), oldPaths);
|
||||||
assertTrue(newPaths.isEmpty());
|
assertTrue(newPaths.isEmpty());
|
||||||
|
|
||||||
String oldMd = SkeletonAnnotator.annotateAsCodeBlock(oldJson, oldPaths);
|
String oldMd = SkeletonAnnotator.annotateForWecom(oldJson, oldPaths);
|
||||||
String newMd = SkeletonAnnotator.annotateAsCodeBlock(newJson, newPaths);
|
String newMd = SkeletonAnnotator.annotateForWecom(newJson, newPaths);
|
||||||
assertTrue(oldMd.contains("<font color=\"warning\">`\"lastError\":\"\"`</font>"));
|
assertTrue(oldMd.contains("<font color=\"warning\">\"lastError\":\"\"</font>"));
|
||||||
assertFalse(newMd.contains("lastError"));
|
assertFalse(oldMd.contains("<font color=\"warning\">\"taskId\":\"\"</font>"));
|
||||||
assertTrue(newMd.startsWith("`"));
|
assertEquals(newJson, newMd);
|
||||||
|
assertFalse(newMd.startsWith("`"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -114,11 +124,9 @@ class ReportBuilderTest {
|
|||||||
|
|
||||||
String console = new ReportBuilder("[缓存结构变更]").toConsole(report);
|
String console = new ReportBuilder("[缓存结构变更]").toConsole(report);
|
||||||
assertTrue(console.contains("======== 字段明细 ========"));
|
assertTrue(console.contains("======== 字段明细 ========"));
|
||||||
assertTrue(console.contains("**位置**: Foo#bar:1"));
|
|
||||||
assertTrue(console.contains("**删除字段**: x"));
|
assertTrue(console.contains("**删除字段**: x"));
|
||||||
assertTrue(console.contains("======== 企微 Markdown ========"));
|
assertTrue(console.contains("<font color=\"warning\">\"x\":\"\"</font>"));
|
||||||
assertTrue(console.contains("value 值从"));
|
assertFalse(console.contains("`{\"x\":\"\"}`"));
|
||||||
assertTrue(console.contains("<font color=\"warning\">`\"x\":\"\"`</font>"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user