package jnpf.bizcommon.audit.service; import jnpf.bizcommon.audit.entity.AuditEventEntity; import jnpf.audit.diff.AuditFieldDiff; import jnpf.bizcommon.audit.entity.model.AuditFieldDiffViewVO; import jnpf.bizcommon.audit.service.support.AuditQueryResponseSanitizer; import jnpf.bizcommon.audit.service.value.AuditFieldValueResolver; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** fieldDiffList 契约、日期/枚举展示、脱敏和无意义 diff 过滤探针。 */ public final class AuditFieldValueSemanticsProbe { private static int passed; private static int failed; public static void main(String[] args) { String formData = "{\"fields\":[" + "{\"__vModel__\":\"biz_status\",\"__config__\":{\"label\":\"状态H\"," + "\"noShow\":true,\"jnpfKey\":\"select\",\"dataType\":\"dictionary\"}," + "\"options\":[{\"enCode\":\"created\",\"fullName\":\"已创建\"}]," + "\"props\":{\"value\":\"enCode\",\"label\":\"fullName\"}}," + "{\"__vModel__\":\"shengchan_riqi\",\"__config__\":{\"label\":\"生产日期\"," + "\"jnpfKey\":\"datePicker\"},\"format\":\"yyyy-MM-dd\"}," + "{\"__vModel__\":\"jiance_xiangmu\",\"__config__\":{\"label\":\"检测项目\"," + "\"jnpfKey\":\"checkbox\",\"dataType\":\"dynamic\"},\"options\":[]}," + "{\"__vModel__\":\"external_status\",\"__config__\":{\"label\":\"外部状态\"," + "\"jnpfKey\":\"select\",\"dataType\":\"dictionary\"," + "\"dictionaryType\":\"bizStatus\"}}," + "{\"__vModel__\":\"approvalEvidence\",\"__config__\":{\"label\":\"审批凭证\"," + "\"jnpfKey\":\"signature\"}}] }"; Map published = AuditFormRegistry.parsePublishedFieldMetadata(formData); check("真实 __vModel__/__config__ 可解析", published.containsKey("biz_status"), published.toString()); check("隐藏表单标签不覆盖 COMMENT", published.get("biz_status").label() == null, String.valueOf(published.get("biz_status").label())); check("字典字段类型为 enum", "enum".equals(published.get("biz_status").valueType()), published.get("biz_status").valueType()); check("字典 options 固化展示快照", "已创建".equals( published.get("biz_status").optionDisplay("created")), "未解析"); check("动态多选引用类型明确", "multiReference".equals( published.get("jiance_xiangmu").valueType()), published.get("jiance_xiangmu").valueType()); check("无内联 options 的 dictionaryType 被保留", "bizStatus".equals(published.get("external_status").dictionaryType()), published.get("external_status").dictionaryType()); check("非约定字段名的签名控件由元数据标记脱敏", AuditFieldDiff.isCredentialMetadata(published.get("approvalevidence")), String.valueOf(published.get("approvalevidence").valueType())); Map comments = new LinkedHashMap<>(); comments.put("biz_status", new AuditFieldDiff.FieldMeta( "请验状态", null, null, "text", "input", null, Collections.emptyMap())); Map metadata = AuditFormRegistry.mergeFieldMetadata(comments, published); check("COMMENT 名称与发布表单类型按属性合并", "请验状态".equals( metadata.get("biz_status").label()) && "enum".equals(metadata.get("biz_status").valueType()), String.valueOf(metadata.get("biz_status").label())); Map eventLabels = new LinkedHashMap<>(); eventLabels.put("biz_status", new AuditFieldDiff.FieldMeta("状态H", "select", true)); Map mergedEvent = AuditFormRegistry.mergeFieldMetadata(metadata, eventLabels); check("隐藏字段的 listLog 标签不覆盖 COMMENT", "请验状态".equals(mergedEvent.get("biz_status").label()), mergedEvent.get("biz_status").label()); Map createRow = new LinkedHashMap<>(); createRow.put("flowId", ""); createRow.put("biz_status", "created"); createRow.put("external_status", "created"); createRow.put("approvalEvidence", "credential-from-control"); String produced = AuditFieldDiff.diff(null, createRow, Collections.emptySet(), metadata); check("CREATE 空值字段不生成 diff", !produced.contains("flowId"), produced); check("未来事件固化 valueType/componentType", produced.contains("\"valueType\":\"enum\"") && produced.contains("\"componentType\":\"select\""), produced); check("未来事件固化字典展示快照", produced.contains("\"newDisplay\":\"已创建\""), produced); check("未来事件固化 dictionaryType", produced.contains("\"dictionaryType\":\"bizStatus\""), produced); check("元数据签名控件写入 fieldDiffs 前已脱敏", !produced.contains("credential-from-control") && produced.contains(AuditFieldDiff.MASKED), produced); String snapshot = AuditFieldDiff.snapshot(createRow, Collections.emptySet(), metadata); check("元数据签名控件写入 dataSnapshot 前已脱敏", !snapshot.contains("credential-from-control") && snapshot.contains(AuditFieldDiff.MASKED), snapshot); String raw = "[" + "{\"field\":\"biz_status\",\"fieldName\":\"biz_status\",\"type\":0," + "\"oldData\":\"\",\"newData\":\"created\"}," + "{\"field\":\"shengchan_riqi\",\"fieldName\":\"shengchan_riqi\",\"type\":0," + "\"oldData\":\"\",\"newData\":\"1782835200000\"}," + "{\"field\":\"jianyan_biaozhun\",\"fieldName\":\"检验标准\",\"type\":0," + "\"oldData\":\"\",\"newData\":\"standard-1\"}," + "{\"field\":\"biz_sign\",\"fieldName\":\"biz_sign\",\"type\":0," + "\"oldData\":\"\",\"newData\":\"credential-raw\"}," + "{\"field\":\"flowId\",\"fieldName\":\"flowId\",\"type\":0," + "\"oldData\":\"\",\"newData\":\"\"}]"; metadata.put("shengchan_riqi", published.get("shengchan_riqi")); metadata.put("jianyan_biaozhun", new AuditFieldDiff.FieldMeta( "检验标准", "select", true, "reference", "select", null, Collections.emptyMap())); String sanitized = AuditQueryResponseSanitizer.sanitizeFieldDiffs(raw, metadata); check("兼容 fieldDiffs 过滤空到空", !sanitized.contains("flowId"), sanitized); check("兼容 fieldDiffs 仍脱敏签名", !sanitized.contains("credential-raw") && sanitized.contains(AuditFieldDiff.MASKED), sanitized); check("diffCount 使用有效项", AuditQueryResponseSanitizer.effectiveDiffCount(raw) == 4, String.valueOf(AuditQueryResponseSanitizer.effectiveDiffCount(raw))); String malformedItem = "[{\"oldData\":\"a\",\"newData\":\"b\"}]"; check("缺 field 的损坏项不计入 diffCount", AuditQueryResponseSanitizer.effectiveDiffCount(malformedItem) == 0, String.valueOf(AuditQueryResponseSanitizer.effectiveDiffCount(malformedItem))); String whitespaceChange = "[{\"field\":\"remark\",\"oldData\":\" A \",\"newData\":\"A\"}]"; check("非空文本的空白变化仍保留", AuditQueryResponseSanitizer.effectiveDiffCount(whitespaceChange) == 1, String.valueOf(AuditQueryResponseSanitizer.effectiveDiffCount(whitespaceChange))); AtomicInteger resolverCalls = new AtomicInteger(); AuditFieldValueResolver failingResolver = new AuditFieldValueResolver() { @Override public boolean supports(String targetTable) { return true; } @Override public void resolve(jnpf.bizcommon.audit.service.value.AuditFieldValueContext context, List fields) { throw new IllegalStateException("expected-probe-failure"); } }; AuditFieldValueResolver fakeResolver = new AuditFieldValueResolver() { @Override public boolean supports(String targetTable) { return "lims_qingyandan".equals(targetTable); } @Override public void resolve(jnpf.bizcommon.audit.service.value.AuditFieldValueContext context, List fields) { resolverCalls.incrementAndGet(); for (AuditFieldDiffViewVO field : fields) { if ("jianyan_biaozhun".equals(field.getField())) { field.setNewDisplay("GB2 · 阿莫西林胶囊"); } } } }; AuditEventEntity event = new AuditEventEntity(); event.setTargetTable("lims_qingyandan"); event.setTenantId("0"); event.setActionCode("CREATE"); AuditFieldDiffViewService viewService = new AuditFieldDiffViewService( Arrays.asList(failingResolver, fakeResolver), "Asia/Shanghai"); List views = viewService.build(event, sanitized, metadata); check("单个 resolver 失败不影响详情", !views.isEmpty(), String.valueOf(views.size())); check("结构化列表只调用一次批量 resolver", resolverCalls.get() == 1, String.valueOf(resolverCalls.get())); check("结构化列表项数与 diffCount 一致", views.size() == 4, String.valueOf(views.size())); check("type=0 明确转换 created", "created".equals(find(views, "biz_status").getChangeType()), find(views, "biz_status").getChangeType()); check("状态展示使用 options 快照", "已创建".equals(find(views, "biz_status").getNewDisplay()), find(views, "biz_status").getNewDisplay()); check("epoch 毫秒按业务时区格式化", "2026-07-01".equals( find(views, "shengchan_riqi").getNewDisplay()), find(views, "shengchan_riqi").getNewDisplay()); check("显式引用解析器提供标准展示", "GB2 · 阿莫西林胶囊".equals( find(views, "jianyan_biaozhun").getNewDisplay()), find(views, "jianyan_biaozhun").getNewDisplay()); String enriched = viewService.enrichForPersistence(event, sanitized, metadata); check("未来引用字段固化事件发生时展示快照", enriched.contains("\"newDisplay\":\"GB2 · 阿莫西林胶囊\""), enriched); check("展示快照增强保留旧 type 契约", enriched.contains("\"type\":0"), enriched); AuditFieldDiffViewVO sign = find(views, "biz_sign"); check("masked 结构化项不含原始值", Boolean.TRUE.equals(sign.getMasked()) && sign.getOldData() == null && sign.getNewData() == null && sign.getOldDisplay() == null && sign.getNewDisplay() == null, String.valueOf(sign)); String metadataSignRaw = "[{\"field\":\"approvalEvidence\",\"fieldName\":\"审批凭证\"," + "\"jnpfKey\":\"signature\",\"type\":1,\"oldData\":\"old-secret\"," + "\"newData\":\"new-secret\",\"oldDisplay\":\"old-secret\"," + "\"newDisplay\":\"new-secret\"}]"; String metadataSignSanitized = AuditQueryResponseSanitizer.sanitizeFieldDiffs( metadataSignRaw, metadata); List metadataSignViews = viewService.build( event, metadataSignSanitized, metadata); AuditFieldDiffViewVO metadataSign = find(metadataSignViews, "approvalEvidence"); check("查询侧按控件元数据脱敏非约定签名字段", !metadataSignSanitized.contains("secret") && Boolean.TRUE.equals(metadataSign.getMasked()) && metadataSign.getOldData() == null && metadataSign.getNewData() == null, metadataSignSanitized); String sanitizedSnapshot = AuditQueryResponseSanitizer.sanitizeJson( "{\"approvalEvidence\":\"snapshot-secret\"}", metadata); check("历史 dataSnapshot 按当前签名元数据响应级脱敏", !sanitizedSnapshot.contains("snapshot-secret") && sanitizedSnapshot.contains(AuditFieldDiff.MASKED), sanitizedSnapshot); String legacyTime = "[" + "{\"field\":\"legacyDate\",\"fieldName\":\"日期\",\"jnpfKey\":\"datePicker\"," + "\"type\":0,\"oldData\":\"\",\"newData\":\"1782835200000\"}," + "{\"field\":\"legacyTime\",\"fieldName\":\"时间\",\"jnpfKey\":\"timePicker\"," + "\"type\":0,\"oldData\":\"\",\"newData\":\"1782835200000\"}]"; AuditFieldDiffViewService noResolverService = new AuditFieldDiffViewService( Collections.emptyList(), "Asia/Shanghai"); List legacyTimeViews = noResolverService.build( event, legacyTime, Collections.emptyMap()); check("无当前元数据的 datePicker 仍格式化 epoch", "2026-07-01".equals(find(legacyTimeViews, "legacyDate").getNewDisplay()), find(legacyTimeViews, "legacyDate").getNewDisplay()); check("timePicker fallback 使用 datetime 而非 date", "datetime".equals(find(legacyTimeViews, "legacyTime").getValueType()) && "2026-07-01 00:00:00".equals( find(legacyTimeViews, "legacyTime").getNewDisplay()), find(legacyTimeViews, "legacyTime").getNewDisplay()); AuditFieldValueResolver partialFailure = new AuditFieldValueResolver() { @Override public boolean supports(String targetTable) { return true; } @Override public void resolve(jnpf.bizcommon.audit.service.value.AuditFieldValueContext context, List fields) { fields.get(0).setNewDisplay("不应保留的半成品"); throw new IllegalStateException("expected-after-mutation"); } }; AuditFieldDiffViewService atomicService = new AuditFieldDiffViewService( Collections.singletonList(partialFailure), "Asia/Shanghai"); List atomicViews = atomicService.build(event, "[{\"field\":\"remark\",\"fieldName\":\"备注\",\"type\":0," + "\"oldData\":\"\",\"newData\":\"原始值\"}]", Collections.emptyMap()); check("resolver 修改后抛错会原子回滚展示结果", "原始值".equals(find(atomicViews, "remark").getNewDisplay()), find(atomicViews, "remark").getNewDisplay()); System.out.println("=== 字段值语义断言:" + passed + " 通过 / " + failed + " 失败 ==="); System.exit(failed == 0 ? 0 : 1); } private static AuditFieldDiffViewVO find(List views, String field) { for (AuditFieldDiffViewVO view : views) { if (field.equals(view.getField())) { return view; } } throw new IllegalStateException("缺少字段:" + field); } 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 AuditFieldValueSemanticsProbe() { } }