feat: 1、企微通知中隐藏,CI中打印字段明细,另外将整体输出到企微中的md,在CI控制台中打印出来
All checks were successful
Redis序列化结构检查 / redis-schema-check (push) Has been skipped

2、整段截断,具体改动的字段不可截断
      3、不再区分P0\P1\P2
This commit is contained in:
2026-07-14 10:43:38 +08:00
parent ea7bcaff2f
commit 7a8ca7ce0b
8 changed files with 752 additions and 27 deletions

View File

@@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class ReportBuilderTest {
@Test
void markdownTemplateWithoutModeAndSummary() {
void wecomMarkdownShowsSkeletonWithoutSeverityAndLocation() {
CheckReport report = new CheckReport();
report.setRepository("jnpf-java-cloud");
report.setBranch("code/redis_change_detection_v1.0");
@@ -19,25 +19,61 @@ class ReportBuilderTest {
report.setNewSha("67c8a6eb");
report.setModifier("dongzi");
report.setModifyTime("2026-07-13 16:54:17");
report.setMode("notify");
SchemaChange change = new SchemaChange(ChangeType.FIELD_REMOVED);
change.setSeverity(Severity.P0);
change.setKeyPattern("saas:period-config:migration:current");
change.setWriteLocation("SaasPeriodConfigMigrationRedisSupport#putCurrent:41");
change.setMessage("删除字段 lastError1");
report.getChanges().add(change);
SchemaChange detail = new SchemaChange(ChangeType.WRAPPER_ADDED);
detail.setSeverity(Severity.P0);
detail.setKeyPattern("tenant:db:content:*");
detail.setWriteLocation("Helper#put:10");
detail.setFieldPath("vo");
detail.setMessage("新增包装层 vo");
report.getChanges().add(detail);
KeyStructureChange key = new KeyStructureChange();
key.setKeyPattern("tenant:db:content:*");
key.setOldSkeletonJson("{\"dbName\":\"\",\"linkList\":[{\"id\":\"\"}]}");
key.setNewSkeletonJson("{\"vo\":{\"dbName\":\"\",\"linkList\":[{\"id\":\"\"}]},\"expiresAtMs\":0}");
key.setSeverity(Severity.P0);
report.getKeyChanges().add(key);
String md = new ReportBuilder("[Redis数据结构变更]").toMarkdown(report);
assertTrue(md.startsWith("## [Redis数据结构变更] jnpf-java-cloud"));
assertTrue(md.contains("> **分支**: code/redis_change_detection_v1.0"));
assertTrue(md.contains("> **提交**: cedd161c → 67c8a6eb"));
assertTrue(md.contains("> **提交人**: dongzi"));
assertTrue(md.contains("> **时间**: 2026-07-13 16:54:17"));
assertTrue(md.contains(" - **位置**: SaasPeriodConfigMigrationRedisSupport#putCurrent:41"));
assertTrue(md.contains(" - **删除字段**: lastError1"));
assertTrue(md.contains("`tenant:db:content:*`"));
assertTrue(md.contains("value 值从 “{\"dbName\":\"\""));
assertTrue(md.contains("变更为 “{\"vo\":"));
assertFalse(md.contains("### P0"));
assertFalse(md.contains("位置"));
assertFalse(md.contains("模式"));
assertFalse(md.contains("汇总"));
}
@Test
void consoleContainsFieldDetailsAndWecomMarkdown() {
CheckReport report = new CheckReport();
report.setRepository("demo");
report.setOldSha("aaa");
report.setNewSha("bbb");
SchemaChange detail = new SchemaChange(ChangeType.FIELD_REMOVED);
detail.setSeverity(Severity.P0);
detail.setKeyPattern("k1");
detail.setWriteLocation("Foo#bar:1");
detail.setMessage("删除字段 x");
report.getChanges().add(detail);
KeyStructureChange key = new KeyStructureChange();
key.setKeyPattern("k1");
key.setOldSkeletonJson("{\"x\":\"\"}");
key.setNewSkeletonJson("{}");
report.getKeyChanges().add(key);
String console = new ReportBuilder("[Redis数据结构变更]").toConsole(report);
assertTrue(console.contains("======== 字段明细 ========"));
assertTrue(console.contains("**位置**: Foo#bar:1"));
assertTrue(console.contains("**删除字段**: x"));
assertTrue(console.contains("======== 企微 Markdown ========"));
assertTrue(console.contains("value 值从"));
}
}

View File

@@ -0,0 +1,83 @@
package com.codechecker.redis.schema;
import com.codechecker.redis.TestSupport;
import com.codechecker.redis.detector.RedisWritePointDetector;
import com.codechecker.redis.detector.WritePoint;
import com.codechecker.redis.diff.SchemaChange;
import com.codechecker.redis.diff.SchemaDiffer;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SkeletonJsonRendererTest {
@Test
void rendersTenantVoThenEnvelopeSkeletons() {
String tenantVo = TestSupport.fixture("fixtures/tenant/TenantVO.txt");
String tenantLink = TestSupport.fixture("fixtures/tenant/TenantLinkModel.txt");
String helperOld = TestSupport.fixture("fixtures/tenant/HelperOld.txt");
String helperNew = TestSupport.fixture("fixtures/tenant/HelperNew.txt");
Set<String> patterns = new HashSet<>();
patterns.add("W01");
patterns.add("W02");
patterns.add("W03");
SourceIndex oldIndex = new SourceIndex();
oldIndex.addSource(tenantVo);
oldIndex.addSource(tenantLink);
oldIndex.addSource(helperOld);
SourceIndex newIndex = new SourceIndex();
newIndex.addSource(tenantVo);
newIndex.addSource(tenantLink);
newIndex.addSource(helperNew);
WritePoint oldWp = new RedisWritePointDetector(oldIndex, patterns)
.detect("Helper.java", helperOld).get(0);
WritePoint newWp = new RedisWritePointDetector(newIndex, patterns)
.detect("Helper.java", helperNew).get(0);
TypeSchema oldSchema = new JavaSchemaExtractor(oldIndex, 8)
.extract(oldWp.getResolvedValueType(), oldWp.isRootArray());
TypeSchema newSchema = new JavaSchemaExtractor(newIndex, 8)
.extract(newWp.getResolvedValueType(), newWp.isRootArray());
List<SchemaChange> changes = new SchemaDiffer().diff(oldSchema, newSchema);
Set<String> protectedPaths = changes.stream()
.map(SchemaChange::getFieldPath)
.filter(p -> p != null && !p.isEmpty())
.collect(Collectors.toCollection(HashSet::new));
changes.stream()
.filter(c -> c.getOldValue() != null && c.getOldValue().contains("."))
.forEach(c -> protectedPaths.add(c.getOldValue()));
// also plain old paths without dot
changes.forEach(c -> {
if (c.getOldValue() != null && !c.getOldValue().isEmpty()
&& c.getOldValue().indexOf(' ') < 0) {
protectedPaths.add(c.getOldValue());
}
});
SkeletonJsonRenderer renderer = new SkeletonJsonRenderer();
String oldJson = renderer.render(oldSchema);
String newJson = renderer.render(newSchema);
assertTrue(oldJson.contains("\"dbName\":\"\""));
assertTrue(oldJson.contains("\"linkList\":["));
assertTrue(newJson.contains("\"vo\":{"));
assertTrue(newJson.contains("\"expiresAtMs\":0"));
assertTrue(newJson.contains("\"dbName\":\"\""));
String truncated = renderer.render(newSchema, protectedPaths, 80);
assertTrue(truncated.contains("expiresAtMs") || truncated.contains("vo"),
"截断后仍应保留改动相关字段: " + truncated);
assertEquals(true, truncated.length() >= 10);
}
}