刘光辉
13 小时以前 34981c30a78e8bbd7791131059a9210f9928b62c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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 前置条件 ④)。
 *
 * <p>为什么必须有它,且必须是**纯 JVM**:这块的错法是「静默地少一个 key」——
 * 端到端跑不出红,只会在前端把字典字段的存储原值当明文渲染出来,
 * 而那种问题要等到有人盯着页面看才发现。D1 正是这么发现旧实现少了四个键的。
 *
 * <p>两个方向都要钉住,缺一不可:
 * <ul>
 *   <li><b>有元数据时键要出现且值要对</b>——否则补了等于没补;</li>
 *   <li><b>没有元数据时键要「整个不出现」</b>(层 1 恒传 null)——否则层 1 的 190 条既有断言
 *       会因形状变化而红,而它的语义一点没变。这是本次改动**唯一**的回归面,
 *       所以断言写在这里而不是靠「应该不影响」的推断。</li>
 * </ul>
 *
 * <p>退出码: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<String, Object> row(Object... kv) {
        Map<String, Object> 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<String> masked(String... cols) {
        return new LinkedHashSet<>(Arrays.asList(cols));
    }
 
    public static void main(String[] args) {
        System.out.println("=== 层 0 field_diffs 展示元数据(jnpfKey / nameModified)===");
 
        Map<String, Object> before = row("qingyan_leixing", "CP", "beizhu", "旧");
        Map<String, Object> 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<String, AuditFieldDiff.FieldMeta> 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<String, AuditFieldDiff.FieldMeta> 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<String, AuditFieldDiff.FieldMeta> 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() {
    }
}