刘光辉
15 小时以前 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package jnpf.bizcommon.audit.service;
 
import jnpf.audit.diff.AuditFieldDiff;
import jnpf.bizcommon.audit.entity.AuditEventEntity;
import jnpf.bizcommon.audit.entity.model.AuditEventListVO;
import jnpf.bizcommon.audit.entity.model.AuditOperationGroupRow;
import jnpf.bizcommon.audit.service.impl.AuditQueryServiceImpl;
import jnpf.bizcommon.audit.service.support.AuditEventCategories;
import jnpf.bizcommon.audit.service.support.AuditQueryResponseSanitizer;
 
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
 
/** 发布表单/COMMENT fallback 与查询脱敏纯 JVM 探针。 */
public final class AuditQuerySemanticsProbe {
 
    private static int passed;
    private static int failed;
 
    public static void main(String[] args) {
        String fields = "[{\"vModel\":\"status\",\"config\":{\"label\":\"表单状态\","
                + "\"jnpfKey\":\"select\",\"dataType\":\"dynamic\",\"children\":["
                + "{\"vModel\":\"child_name\",\"config\":{\"label\":\"子项名称\","
                + "\"jnpfKey\":\"input\",\"dataType\":\"static\"}}]}},"
                + "{\"vModel\":\"sampleCode\",\"config\":{\"label\":\"样品编码\","
                + "\"jnpfKey\":\"input\",\"dataType\":\"static\"}}]";
        String formData = "{\"fields\":" + quote(fields) + "}";
        Map<String, AuditFieldDiff.FieldMeta> published =
                AuditFormRegistry.parsePublishedFieldMetadata(formData);
        check("发布表单解析 vModel -> label", "表单状态".equals(published.get("status").label()),
                String.valueOf(published));
        check("发布表单递归解析 children", "子项名称".equals(published.get("child_name").label()),
                String.valueOf(published));
        check("动态 select 标记 nameModified", Boolean.TRUE.equals(published.get("status").nameModified()),
                String.valueOf(published.get("status").nameModified()));
        Map<String, Object> camelCaseRow = new LinkedHashMap<>();
        camelCaseRow.put("sampleCode", "S-001");
        String camelCaseDiff = AuditFieldDiff.diff(
                Collections.emptyMap(), camelCaseRow, Collections.emptySet(), published);
        check("camelCase vModel 可匹配小写元数据键",
                camelCaseDiff.contains("\"fieldName\":\"样品编码\""), camelCaseDiff);
 
        Map<String, AuditFieldDiff.FieldMeta> comments = new LinkedHashMap<>();
        comments.put("status", new AuditFieldDiff.FieldMeta("COMMENT 状态", null, null));
        comments.put("comment_only", new AuditFieldDiff.FieldMeta("COMMENT 专属字段", null, null));
        Map<String, AuditFieldDiff.FieldMeta> merged =
                AuditFormRegistry.mergeFieldMetadata(comments, published);
        check("发布表单名称优先于 COMMENT", "表单状态".equals(merged.get("status").label()),
                merged.get("status").label());
        check("表单缺失时使用 COMMENT fallback",
                "COMMENT 专属字段".equals(merged.get("comment_only").label()),
                merged.get("comment_only").label());
 
        Map<String, AuditFieldDiff.FieldMeta> event = new LinkedHashMap<>();
        event.put("status", new AuditFieldDiff.FieldMeta("事件当时状态", "select", true));
        Map<String, AuditFieldDiff.FieldMeta> finalMeta =
                AuditFormRegistry.mergeFieldMetadata(merged, event);
        check("事件 listLog 优先于当前发布表单",
                "事件当时状态".equals(finalMeta.get("status").label()), finalMeta.get("status").label());
 
        String diffs = "[{\"field\":\"status\",\"fieldName\":\"status\",\"oldData\":\"a\",\"newData\":\"b\"},"
                + "{\"field\":\"unknown_col\",\"fieldName\":\"unknown_col\",\"oldData\":\"a\",\"newData\":\"b\"},"
                + "{\"field\":\"biz_sign\",\"fieldName\":\"biz_sign\",\"oldData\":\"secret-1\",\"newData\":\"secret-2\"}]";
        String sanitized = AuditQueryResponseSanitizer.sanitizeFieldDiffs(diffs, finalMeta);
        check("历史技术名按当前元数据装饰", sanitized.contains("\"fieldName\":\"事件当时状态\""), sanitized);
        check("元数据和 COMMENT 都缺失时回落数据库字段名",
                sanitized.contains("\"fieldName\":\"unknown_col\""), sanitized);
        check("biz_sign 在历史 diff 中不暴露", !sanitized.contains("secret-1")
                && !sanitized.contains("secret-2") && sanitized.contains("电子签名已关联"), sanitized);
 
        String json = "{\"biz_sign\":\"credential\",\"nested\":{\"approveSignId\":\"credential-2\","
                + "\"targetId\":\"business-id\"}}";
        String safeJson = AuditQueryResponseSanitizer.sanitizeJson(json);
        check("extra/snapshot 深度脱敏签名凭证", !safeJson.contains("credential")
                && safeJson.contains("business-id"), safeJson);
        String childDiff = "[{\"field\":\"tableField1\",\"jnpfKey\":\"table\","
                + "\"chidField\":[{\"prop\":\"item_name\",\"label\":\"项目\"},"
                + "{\"prop\":\"review_sign\",\"label\":\"复审签名\",\"jnpfKey\":\"sign\"}],"
                + "\"chidData\":[{\"item_name\":\"含量\",\"review_sign\":\"child-secret-new\","
                + "\"jnpf_old_review_sign\":\"child-secret-old\",\"jnpf_type\":1}]}]";
        String safeChildDiff = AuditQueryResponseSanitizer.sanitizeFieldDiffs(
                childDiff, Collections.emptyMap());
        check("子表签名列的新旧凭证均脱敏且普通列保留",
                safeChildDiff != null && !safeChildDiff.contains("child-secret")
                        && safeChildDiff.contains("含量") && safeChildDiff.contains("\"masked\":true"),
                safeChildDiff);
        check("lims_sign targetId 不返回普通查询",
                AuditQueryResponseSanitizer.hidesTargetId("LIMS_SIGN"), "未命中");
 
        String missing = AuditQueryResponseSanitizer.sanitizeFieldDiffs(
                "[{\"field\":\"x\",\"fieldName\":\"x\"}]", Collections.emptyMap());
        check("空元数据回落数据库字段名", missing != null && missing.contains("\"fieldName\":\"x\""), missing);
 
        Map<String, AuditFieldDiff.FieldMeta> databaseMeta = new LinkedHashMap<>();
        databaseMeta.put("biz_status", new AuditFieldDiff.FieldMeta("业务状态", null, null));
        databaseMeta.put("creator_time", new AuditFieldDiff.FieldMeta(null, null, null));
        String historical = AuditQueryResponseSanitizer.sanitizeFieldDiffs(
                "[{\"field\":\"bizStatus\",\"fieldName\":\"bizStatus\"},"
                        + "{\"field\":\"creatorTime\",\"fieldName\":\"creatorTime\"}]",
                databaseMeta);
        check("历史 Java 属性名可映射数据库备注", historical.contains("\"fieldName\":\"业务状态\""), historical);
        check("历史 Java 属性名在备注为空时回落数据库列名",
                historical.contains("\"fieldName\":\"creator_time\""), historical);
 
        String repeatedFields = "[{\"field\":\"status\",\"oldData\":\"a\",\"newData\":\"b\"},"
                + "{\"field\":\"status\",\"oldData\":\"b\",\"newData\":\"c\"},"
                + "{\"field\":\"amount\",\"oldData\":1,\"newData\":2}]";
        check("重复字段按字段并集计数",
                AuditQueryResponseSanitizer.effectiveDiffCount(repeatedFields) == 2,
                String.valueOf(AuditQueryResponseSanitizer.effectiveDiffFields(repeatedFields)));
 
        String sameFieldAcrossTables = "[{\"targetTable\":\"main_table\",\"field\":\"status\","
                + "\"oldData\":\"a\",\"newData\":\"b\"},"
                + "{\"targetTable\":\"child_table\",\"field\":\"status\","
                + "\"oldData\":\"x\",\"newData\":\"y\"},"
                + "{\"targetTable\":\"child_table\",\"field\":\"status\","
                + "\"oldData\":\"y\",\"newData\":\"z\"}]";
        check("主子表同名字段按物理表分别计数",
                AuditQueryResponseSanitizer.effectiveDiffCount(sameFieldAcrossTables) == 2,
                String.valueOf(AuditQueryResponseSanitizer.effectiveDiffFields(sameFieldAcrossTables)));
 
        String batchSameField = "[{\"targetTable\":\"lims_task\",\"targetId\":\"row-1\","
                + "\"field\":\"status\",\"oldData\":\"a\",\"newData\":\"b\"},"
                + "{\"targetTable\":\"lims_task\",\"targetId\":\"row-2\","
                + "\"field\":\"status\",\"oldData\":\"x\",\"newData\":\"b\"}]";
        check("批量逐记录差异的摘要仍按表和字段计为一个",
                AuditQueryResponseSanitizer.effectiveDiffCount(batchSameField) == 1,
                String.valueOf(AuditQueryResponseSanitizer.effectiveDiffFields(batchSameField)));
 
        verifyOperationDisplayGrouping();
 
        System.out.println("=== 查询语义断言:" + passed + " 通过 / " + failed + " 失败 ===");
        System.exit(failed == 0 ? 0 : 1);
    }
 
    @SuppressWarnings("unchecked")
    private static void verifyOperationDisplayGrouping() {
        AuditEventEntity business = event(101L, AuditEventCategories.BUSINESS, "BIZ_ACTION",
                "[{\"field\":\"status\",\"oldData\":\"a\",\"newData\":\"b\"}]");
        AuditEventEntity primary = event(102L, AuditEventCategories.PRIMARY_SIGN, "E_SIGNATURE",
                "[{\"field\":\"sign_a\"},{\"field\":\"sign_b\"}]");
        AuditEventEntity review = event(103L, AuditEventCategories.REVIEW_SIGN, "E_SIGNATURE", null);
        AuditOperationGroupRow group = new AuditOperationGroupRow();
        group.setGid("op-display-1");
 
        try {
            Method method = AuditQueryServiceImpl.class.getDeclaredMethod(
                    "buildOperationRows", List.class, List.class);
            method.setAccessible(true);
            List<AuditEventListVO> rows = (List<AuditEventListVO>) method.invoke(
                    new AuditQueryServiceImpl(), Collections.singletonList(group),
                    Arrays.asList(primary, business, review));
            AuditEventListVO main = rows.isEmpty() ? null : rows.get(0);
            check("operationId 列表只返回 BUSINESS 主行",
                    rows.size() == 1 && main != null && Long.valueOf(101L).equals(main.getId()),
                    String.valueOf(rows));
            check("主签名与复审签名作为两条关联明细",
                    main != null && main.getSubEvents() != null && main.getSubEvents().size() == 2
                            && "PRIMARY".equals(main.getSubEvents().get(0).getSignatureRole())
                            && "REVIEW".equals(main.getSubEvents().get(1).getSignatureRole()),
                    main == null ? "null" : String.valueOf(main.getSubEvents()));
            check("查询层不累加签名差异数",
                    main != null && Integer.valueOf(1).equals(main.getDiffCount()),
                    main == null ? "null" : String.valueOf(main.getDiffCount()));
        } catch (ReflectiveOperationException ex) {
            throw new AssertionError("无法执行 operationId 展示组装探针", ex);
        }
    }
 
    private static AuditEventEntity event(Long id, String category, String eventType, String fieldDiffs) {
        AuditEventEntity event = new AuditEventEntity();
        event.setId(id);
        event.setOperationId("op-display-1");
        event.setClientEventId("client-" + id);
        event.setEventCategory(category);
        event.setEventType(eventType);
        event.setFieldDiffs(fieldDiffs);
        return event;
    }
 
    private static String quote(String value) {
        return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
    }
 
    private static void check(String name, boolean ok, String detail) {
        if (ok) {
            passed++;
            System.out.println("  [PASS] " + name);
        } else {
            failed++;
            System.out.println("  [FAIL] " + name + " -- " + detail);
        }
    }
 
    private AuditQuerySemanticsProbe() {
    }
}