feat: md渲染优化

This commit is contained in:
2026-07-15 14:40:00 +08:00
parent a03e1b4819
commit 4dd4944107
3 changed files with 108 additions and 33 deletions

View File

@@ -1,5 +1,6 @@
package com.codechecker.cache.report;
import com.codechecker.cache.diff.ChangeType;
import com.codechecker.cache.diff.SchemaChange;
import com.codechecker.cache.diff.Severity;
@@ -14,7 +15,7 @@ import java.util.Set;
/**
* 将 {@link CheckReport} 渲染为企微 Markdown / 控制台文本。
* <ul>
* <li>企微:按 key 展示位置/类型/序列化骨架变更,不含字段明细,不分 P0/P1/P2</li>
* <li>企微:按 key 展示位置/类型/序列化骨架变更;类型变更另附摘要行,不分 P0/P1/P2</li>
* <li>未解析 key 展示源码表达式 + 灰色「key 无法解析」提示</li>
* <li>多 key 优先拼成一条;超过企微上限则按 key 拆成多条</li>
* <li>CI先打字段明细再完整输出企微 Markdown拆分后的每条</li>
@@ -129,22 +130,84 @@ public class ReportBuilder {
String newJson = nvl(kc.getNewSkeletonJson());
Set<String> oldHighlight = SkeletonAnnotator.pathsForOldSkeleton(kc.getFieldDetails());
Set<String> newGreen = SkeletonAnnotator.pathsForNewSkeleton(kc.getFieldDetails());
Set<String> newOrange = SkeletonAnnotator.pathsForTypeChanged(kc.getFieldDetails());
Set<String> typeGray = SkeletonAnnotator.pathsForTypeChanged(kc.getFieldDetails());
String oldRendered = oldJson.isEmpty()
? "" : "" + SkeletonAnnotator.annotateOldForWecom(oldJson, oldHighlight) + "";
? "" : "" + SkeletonAnnotator.annotateOldForWecom(oldJson, oldHighlight, typeGray) + "";
String newRendered = newJson.isEmpty()
? "" : "" + SkeletonAnnotator.annotateNewForWecom(newJson, newGreen, newOrange) + "";
? "" : "" + SkeletonAnnotator.annotateNewForWecom(newJson, newGreen, typeGray) + "";
if (oldJson.isEmpty() && !newJson.isEmpty()) {
sb.append(" > **value 新增为:** ").append(newRendered).append("\n\n");
sb.append(" > **value 新增为:** ").append(newRendered).append('\n');
} else if (!oldJson.isEmpty() && newJson.isEmpty()) {
sb.append(" > **value 原结构:** ").append(oldRendered).append("(已删除写入)\n\n");
sb.append(" > **value 原结构:** ").append(oldRendered).append("(已删除写入)\n");
} else {
sb.append(" > **value值由** ").append(oldRendered).append('\n');
sb.append(" > **变更为:** ").append(newRendered).append("\n\n");
sb.append(" > **变更为:** ").append(newRendered).append('\n');
}
appendTypeChangeSummary(sb, kc.getFieldDetails());
sb.append('\n');
return sb.toString();
}
/**
* 类型变更单独一行摘要,避免仅靠骨架颜色/占位难以识别。
* 例:{@code > **类型变更**: amount <font color="warning">BigDecimal → Integer</font>}
*/
private void appendTypeChangeSummary(StringBuilder sb, List<SchemaChange> details) {
if (details == null || details.isEmpty()) {
return;
}
List<String> parts = new ArrayList<>();
for (SchemaChange c : details) {
if (c == null || c.getChangeType() != ChangeType.TYPE_CHANGED) {
continue;
}
String path = nvl(c.getFieldPath());
if (path.isEmpty()) {
continue;
}
String oldType = displayJavaType(c.getOldValue());
String newType = displayJavaType(c.getNewValue());
StringBuilder part = new StringBuilder();
// 字段名用普通文本(避免反引号被企微渲染成色块)
part.append(path);
if (!oldType.isEmpty() || !newType.isEmpty()) {
part.append(" <font color=\"warning\">")
.append(oldType.isEmpty() ? "?" : oldType)
.append("")
.append(newType.isEmpty() ? "?" : newType)
.append("</font>");
}
parts.add(part.toString());
}
if (parts.isEmpty()) {
return;
}
sb.append(" > **类型变更**: ");
for (int i = 0; i < parts.size(); i++) {
if (i > 0) {
sb.append("");
}
sb.append(parts.get(i));
}
sb.append('\n');
}
/** 将 {@code BigDecimal/NUMBER} 转为展示用 {@code BigDecimal}。 */
private static String displayJavaType(String typeLabel) {
if (typeLabel == null) {
return "";
}
String s = typeLabel.trim();
if (s.isEmpty()) {
return "";
}
int slash = s.indexOf('/');
if (slash > 0) {
s = s.substring(0, slash).trim();
}
return s;
}
/**
* Key 行:展示解析后的模式或未解析时的源码表达式;未解析时追加灰色提示。
* 反引号仅包裹 key 文本,避免与加粗/颜色嵌套冲突。
@@ -160,6 +223,7 @@ public class ReportBuilder {
sb.append("- Key --> `").append(keyText).append('`');
if (unresolved) {
sb.append(" <font color=\"comment\">key 无法解析)</font>");
// sb.append(" key 无法解析)");
}
sb.append('\n');
}

View File

@@ -15,22 +15,24 @@ import java.util.Set;
* <li>删除字段 → 旧骨架,橙色 {@code warning}</li>
* <li>新增字段 / 新增包装层 → 新骨架,绿色 {@code info}</li>
* <li>路径迁移 → 旧路径橙、新路径绿</li>
* <li>类型变更 → 新旧骨架均为色 {@code warning}(提高识别度)</li>
* <li>类型变更(修改类)→ 新旧骨架均为色 {@code comment}</li>
* </ul>
*/
final class SkeletonAnnotator {
/** 企微橙:删除 / 类型变更 / 旧侧变更 */
/** 企微橙:删除 / 路径迁移旧侧 */
static final String COLOR_REMOVE = "warning";
/** 企微绿:新增 / 新侧路径迁移 */
static final String COLOR_ADD = "info";
/** 企微灰:类型变更(修改类) */
static final String COLOR_TYPE = "comment";
private static final String FONT_CLOSE = "</font>";
private SkeletonAnnotator() {
}
/** 旧骨架应橙色标注的路径:删除、路径迁移旧侧类型变更。 */
/** 旧骨架应橙色标注的路径:删除、路径迁移旧侧(不含类型变更。 */
static Set<String> pathsForOldSkeleton(List<SchemaChange> details) {
Set<String> paths = new LinkedHashSet<>();
if (details == null) {
@@ -40,8 +42,7 @@ final class SkeletonAnnotator {
if (c == null || c.getChangeType() == null) {
continue;
}
if (c.getChangeType() == ChangeType.FIELD_REMOVED
|| c.getChangeType() == ChangeType.TYPE_CHANGED) {
if (c.getChangeType() == ChangeType.FIELD_REMOVED) {
addIfPresent(paths, c.getFieldPath());
} else if (c.getChangeType() == ChangeType.FIELD_PATH_MOVED) {
addIfPresent(paths, c.getOldValue());
@@ -73,7 +74,7 @@ final class SkeletonAnnotator {
return paths;
}
/** 新骨架应橙色标注的路径:类型变更(与旧侧同色,便于识别)。 */
/** 类型变更路径:新旧骨架均用灰色标注。 */
static Set<String> pathsForTypeChanged(List<SchemaChange> details) {
Set<String> paths = new LinkedHashSet<>();
if (details == null) {
@@ -88,30 +89,35 @@ final class SkeletonAnnotator {
}
/**
* 标注旧骨架改动字段(删除 / 类型变更 → 橙色)
* 标注旧骨架:删除等橙色;类型变更灰色
*/
static String annotateOldForWecom(String json, Set<String> highlightPaths) {
return annotateForWecom(json, highlightPaths, COLOR_REMOVE);
static String annotateOldForWecom(String json, Set<String> orangePaths) {
return annotateOldForWecom(json, orangePaths, null);
}
static String annotateOldForWecom(String json, Set<String> orangePaths, Set<String> grayPaths) {
String result = annotateForWecom(json, grayPaths, COLOR_TYPE);
return annotateForWecom(result, orangePaths, COLOR_REMOVE);
}
/**
* 标注新骨架:类型变更先着橙色,其余新增绿色。
* 标注新骨架:类型变更灰色,新增绿色。
*/
static String annotateNewForWecom(String json, Set<String> greenPaths) {
return annotateNewForWecom(json, greenPaths, null);
}
/**
* 标注新骨架改动字段(类型变更 → 色;新增 / 迁移新侧 → 绿色)。
* 标注新骨架改动字段(类型变更 → 色;新增 / 迁移新侧 → 绿色)。
*/
static String annotateNewForWecom(String json, Set<String> greenPaths, Set<String> orangePaths) {
String result = annotateForWecom(json, orangePaths, COLOR_REMOVE);
static String annotateNewForWecom(String json, Set<String> greenPaths, Set<String> grayTypePaths) {
String result = annotateForWecom(json, grayTypePaths, COLOR_TYPE);
return annotateForWecom(result, greenPaths, COLOR_ADD);
}
/**
* 仅标注改动字段:其余正文保持普通文本。
* 改动片段格式:{@code <font color="warning|info">"field":value</font>}
* 改动片段格式:{@code <font color="warning|info|comment">"field":value</font>}
*/
static String annotateForWecom(String json, Set<String> highlightPaths) {
return annotateForWecom(json, highlightPaths, COLOR_REMOVE);