package jnpf.bizcommon.audit.service;
import jnpf.audit.diff.AuditFieldDiff;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* 层 0 {@code field_diffs} **展示元数据**的断言探针(D1 前置条件 ④)。
*
*
为什么必须有它,且必须是**纯 JVM**:这块的错法是「静默地少一个 key」——
* 端到端跑不出红,只会在前端把字典字段的存储原值当明文渲染出来,
* 而那种问题要等到有人盯着页面看才发现。D1 正是这么发现旧实现少了四个键的。
*
*
两个方向都要钉住,缺一不可:
*
* - 有元数据时键要出现且值要对——否则补了等于没补;
* - 没有元数据时键要「整个不出现」(层 1 恒传 null)——否则层 1 的 190 条既有断言
* 会因形状变化而红,而它的语义一点没变。这是本次改动**唯一**的回归面,
* 所以断言写在这里而不是靠「应该不影响」的推断。
*
*
* 退出码:0 = 全绿 / 1 = 有断言失败。不连库、不起容器。
*/
public final class AuditL0FieldMetaProbe {
private static int pass = 0;
private static int fail = 0;
private static void ok(String name) {
System.out.println(" [PASS] " + name);
pass++;
}
private static void bad(String name, String detail) {
System.out.println(" [FAIL] " + name + " —— " + detail);
fail++;
}
private static void assertContains(String name, String json, String needle) {
if (json != null && json.contains(needle)) {
ok(name);
} else {
bad(name, "期望出现 " + needle + ",实际=" + json);
}
}
private static void assertAbsent(String name, String json, String needle) {
if (json == null || !json.contains(needle)) {
ok(name);
} else {
bad(name, "期望**不**出现 " + needle + ",实际=" + json);
}
}
private static Map row(Object... kv) {
Map m = new LinkedHashMap<>();
for (int i = 0; i < kv.length; i += 2) {
m.put(String.valueOf(kv[i]), kv[i + 1]);
}
return m;
}
private static Set masked(String... cols) {
return new LinkedHashSet<>(Arrays.asList(cols));
}
public static void main(String[] args) {
System.out.println("=== 层 0 field_diffs 展示元数据(jnpfKey / nameModified)===");
Map before = row("qingyan_leixing", "CP", "beizhu", "旧");
Map after = row("qingyan_leixing", "YL", "beizhu", "新");
// ── A 组:层 1 路径(meta=null)—— 新键必须「整个不出现」 ────────────────────
// 这一组是本次改动唯一的回归面:层 1 没有表单模型、恒传 null,
// 若无条件补上 "jnpfKey":null 之类,它的 190 条既有断言会因形状变化而红。
String noMeta = AuditFieldDiff.diff(before, after, masked());
assertAbsent("A1 层1 路径(3 参重载)不出现 jnpfKey 键", noMeta, "jnpfKey");
assertAbsent("A2 层1 路径(3 参重载)不出现 nameModified 键", noMeta, "nameModified");
assertContains("A3 层1 路径 fieldName 仍回落成字段名", noMeta, "\"fieldName\":\"qingyan_leixing\"");
String nullMeta = AuditFieldDiff.diff(before, after, masked(), null);
assertAbsent("A4 显式传 null meta(4 参重载)同样不出现 jnpfKey", nullMeta, "jnpfKey");
if (noMeta.equals(nullMeta)) {
ok("A5 3 参与 4 参传 null 的输出逐字符相同(层1 换重载也不会漂)");
} else {
bad("A5 3 参与 4 参传 null 输出应相同", noMeta + " vs " + nullMeta);
}
// ── B 组:有元数据 —— 键要出现、值要对 ──────────────────────────────────
Map meta = new LinkedHashMap<>();
meta.put("qingyan_leixing", new AuditFieldDiff.FieldMeta("请验类型", "select", Boolean.TRUE));
meta.put("beizhu", new AuditFieldDiff.FieldMeta("备注", "textarea", Boolean.FALSE));
String withMeta = AuditFieldDiff.diff(before, after, masked(), meta);
assertContains("B1 带 jnpfKey", withMeta, "\"jnpfKey\":\"select\"");
assertContains("B2 带 nameModified=true(字典字段前端只显示「已修改」)", withMeta, "\"nameModified\":true");
assertContains("B3 nameModified=false 也如实带上(不是省略)", withMeta, "\"nameModified\":false");
assertContains("B4 中文标签仍然生效", withMeta, "\"fieldName\":\"请验类型\"");
// ── C 组:元数据缺项时逐键降级,不整块丢 ───────────────────────────────
Map partial = new LinkedHashMap<>();
partial.put("qingyan_leixing", new AuditFieldDiff.FieldMeta("请验类型", null, null));
String partialJson = AuditFieldDiff.diff(before, after, masked(), partial);
assertContains("C1 只有标签时 fieldName 生效", partialJson, "\"fieldName\":\"请验类型\"");
assertAbsent("C2 jnpfKey 为 null 时该键不出现(不是 \"jnpfKey\":null)", partialJson, "jnpfKey");
assertAbsent("C3 nameModified 为 null 时该键不出现", partialJson, "nameModified");
// 同一次 diff 里另一个字段完全没登记 → 只影响它自己
assertContains("C4 未登记的字段回落成字段名,不受同批其他字段影响", partialJson, "\"fieldName\":\"beizhu\"");
// ── D 组:与脱敏共存 ───────────────────────────────────────────────────
// 两者管的是不同的事:脱敏 = 值不记;nameModified = 值不展示。同时成立时
// 前端显示「已修改」而不是 `***`,语义更准,故**脱敏列上照样带 nameModified**。
Map maskedMeta = new LinkedHashMap<>();
maskedMeta.put("sign_password", new AuditFieldDiff.FieldMeta("签名密码", "password", Boolean.TRUE));
String maskedJson = AuditFieldDiff.diff(
row("sign_password", "old"), row("sign_password", "new"),
masked("sign_password"), maskedMeta);
assertContains("D1 脱敏列的值仍是占位符", maskedJson, "\"oldData\":\"***\"");
assertContains("D2 脱敏列照样带 nameModified(脱敏管不记值、它管不展示)", maskedJson, "\"nameModified\":true");
assertContains("D3 脱敏列 type 恒为 1(B5 评审 M-3,存在性也不给)", maskedJson, "\"type\":1");
// ── E 组:子表控件 —— 本层判不了,必须能被识别出来留痕 ──────────────────
if (AuditFieldDiff.JNPF_KEY_TABLE.equals("table")) {
ok("E1 子表控件常量与前端 DataLogList.vue 的判据字面量一致('table')");
} else {
bad("E1 子表控件常量应为 'table'", AuditFieldDiff.JNPF_KEY_TABLE);
}
System.out.println();
System.out.println("=== 层 0 展示元数据断言:" + pass + " 通过 / " + fail + " 失败 ===");
System.exit(fail == 0 ? 0 : 1);
}
private AuditL0FieldMetaProbe() {
}
}