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() {
|
}
|
}
|