package jnpf.bizcommon.audit.service.impl; import com.alibaba.fastjson.JSON; import jnpf.audit.AuditConsts; import jnpf.bizcommon.audit.service.support.AuditDiffKey; import jnpf.bizcommon.audit.service.support.AuditEventCategories; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** 写入侧三分类与 field_diffs 端点组合纯 JVM 探针。 */ public final class AuditEventIngestMergeProbe { private static int passed; private static int failed; public static void main(String[] args) { check("业务事件分类", AuditEventCategories.BUSINESS.equals( AuditEventCategories.resolve(AuditConsts.TYPE_DATA_CHANGE, null))); check("显式复审签名分类", AuditEventCategories.REVIEW_SIGN.equals( AuditEventCategories.resolve(AuditConsts.TYPE_E_SIGNATURE, "{\"signatureRole\":\"REVIEW\"}"))); check("历史 fuhe_sign 分类", AuditEventCategories.REVIEW_SIGN.equals( AuditEventCategories.resolve(AuditConsts.TYPE_E_SIGNATURE, "{\"signField\":\"fuhe_sign\"}"))); check("普通签名默认主签名", AuditEventCategories.PRIMARY_SIGN.equals( AuditEventCategories.resolve(AuditConsts.TYPE_E_SIGNATURE, "{}"))); String existing = "[{\"field\":\"status\",\"fieldName\":\"状态\"," + "\"oldData\":\"draft\",\"newData\":\"submitted\"}," + "{\"field\":\"amount\",\"oldData\":1,\"newData\":2}]"; String later = "[{\"field\":\"status\",\"fieldName\":\"业务状态\"," + "\"oldData\":\"submitted\",\"newData\":\"approved\"}]"; String mergedLater = AuditEventServiceImpl.mergeFieldDiffs(existing, later, false, true); check("同字段保留最早旧值", mergedLater.contains("\"oldData\":\"draft\"")); check("同字段使用最晚新值", mergedLater.contains("\"newData\":\"approved\"")); check("不同字段组成并集", mergedLater.contains("\"field\":\"amount\"")); check("较晚元数据覆盖展示名", mergedLater.contains("\"fieldName\":\"业务状态\"")); String earlier = "[{\"field\":\"status\",\"oldData\":\"created\"," + "\"newData\":\"draft\"}]"; String mergedEarlier = AuditEventServiceImpl.mergeFieldDiffs(existing, earlier, true, false); check("乱序到达仍使用真实最早旧值", mergedEarlier.contains("\"oldData\":\"created\"")); check("乱序到达不覆盖当前最晚新值", mergedEarlier.contains("\"newData\":\"submitted\"")); String formCreate = "[{\"field\":\"chanpin_mingcheng\",\"fieldName\":\"产品名称\"," + "\"oldData\":\"\",\"newData\":\"红细胞\"}," + "{\"field\":\"biz_status\",\"fieldName\":\"业务状态\"," + "\"oldData\":\"\",\"newData\":\"created\",\"newDisplay\":\"已创建\"}]"; String apiSubmit = "[{\"field\":\"biz_status\",\"fieldName\":\"biz_status\"," + "\"oldData\":\"created\",\"newData\":\"confirmed\"}," + "{\"field\":\"quyang_status\",\"fieldName\":\"quyang_status\"," + "\"oldData\":\"\",\"newData\":\"pending_fetch\"}]"; String formThenApi = AuditEventServiceImpl.mergeFieldDiffs( formCreate, apiSubmit, false, true, true, false, protectedFields("chanpin_mingcheng", "biz_status")); check("表单先到时保留表单初始值", hasDiff(formThenApi, "biz_status", "业务状态", "", "created")); check("表单先到时补入接口字段", formThenApi.contains("\"field\":\"quyang_status\"")); check("表单有效值不被接口覆盖", diffHasValue(formThenApi, "biz_status", "newDisplay", "已创建")); check("表单先到时字段顺序以表单为准", formThenApi.indexOf("chanpin_mingcheng") < formThenApi.indexOf("quyang_status")); String apiThenForm = AuditEventServiceImpl.mergeFieldDiffs( apiSubmit, formCreate, true, false, false, true, protectedFields("chanpin_mingcheng", "biz_status")); check("表单后到时仍保留表单初始值", hasDiff(apiThenForm, "biz_status", "业务状态", "", "created")); check("表单后到时仍补入接口字段", apiThenForm.contains("\"field\":\"quyang_status\"")); check("表单后到时重新按表单字段排序", apiThenForm.indexOf("chanpin_mingcheng") < apiThenForm.indexOf("quyang_status")); String formEmpty = "[{\"field\":\"unit\",\"fieldName\":\"单位\"," + "\"oldData\":\"\",\"newData\":\"\"}," + "{\"field\":\"amount\",\"fieldName\":\"数量\"," + "\"oldData\":\"\",\"newData\":\"0\"}]"; String apiFilled = "[{\"field\":\"unit\",\"oldData\":\"\",\"newData\":\"粒\"}," + "{\"field\":\"amount\",\"oldData\":\"0\",\"newData\":\"2\"}]"; String filled = AuditEventServiceImpl.mergeFieldDiffs( formEmpty, apiFilled, true, false, true, false, protectedFields()); check("表单空值允许接口补入", hasDiff(filled, "unit", "单位", "", "粒")); check("表单零值允许接口补入", hasDiff(filled, "amount", "数量", "", "2")); String olderApiIgnored = AuditEventServiceImpl.mergeFieldDiffs( filled, apiFilled.replace("\"newData\":\"2\"", "\"newData\":\"1\""), false, false, true, false, protectedFields()); check("乱序到达的旧接口不覆盖较新字段补充值", hasDiff(olderApiIgnored, "amount", "数量", "", "2")); String childForm = "[{\"field\":\"tableField1\",\"jnpfKey\":\"table\"," + "\"chidField\":[{\"prop\":\"name\",\"label\":\"名称\"}]," + "\"chidData\":[{\"name\":\"新值\",\"jnpf_old_name\":\"旧值\",\"jnpf_type\":1}]}]"; String childApi = "[{\"field\":\"tableField1\",\"oldData\":\"api-old\",\"newData\":\"api-new\"}]"; String childMerged = AuditEventServiceImpl.mergeFieldDiffs( childForm, childApi, false, true, true, false, protectedFields("tableField1")); check("表单子表明细不被接口同名字段覆盖", childMerged.contains("\"chidData\"") && !childMerged.contains("api-new")); String formSnapshot = "{\"name\":\"陈皮\",\"amount\":\"0\",\"unit\":\"\"," + "\"biz_status\":\"created\"}"; String apiSnapshot1 = "{\"name\":\"接口名称\",\"amount\":\"2\",\"unit\":\"粒\"," + "\"biz_status\":\"confirmed\",\"api_field\":\"first\"}"; String apiSnapshot2 = "{\"amount\":\"3\",\"api_field\":\"latest\"}"; String mergedSnapshot = AuditEventServiceImpl.mergeFormFirstSnapshots( formSnapshot, apiSnapshot1, apiSnapshot2); check("快照保留表单有效值", snapshotHasValue(mergedSnapshot, "name", "陈皮") && snapshotHasValue(mergedSnapshot, "biz_status", "created")); check("快照空值和零值允许接口补入", snapshotHasValue(mergedSnapshot, "amount", "3") && snapshotHasValue(mergedSnapshot, "unit", "粒")); check("快照补入表单缺失字段并使用接口最新值", snapshotHasValue(mergedSnapshot, "api_field", "latest")); check("业务展示来源层0表单优先于层2接口", AuditEventServiceImpl.sourcePriority(AuditEventCategories.BUSINESS, 0) > AuditEventServiceImpl.sourcePriority(AuditEventCategories.BUSINESS, 2)); String apiThenFormSnapshot = AuditEventServiceImpl.mergeSnapshots( apiSnapshot1, formSnapshot, false, false, true, false, true, formSnapshot); String formThenApiSnapshot = AuditEventServiceImpl.mergeSnapshots( formSnapshot, apiSnapshot1, true, true, true, true, false, formSnapshot); check("表单和接口请求先后不影响快照基准", JSON.parseObject(apiThenFormSnapshot).equals(JSON.parseObject(formThenApiSnapshot))); String newerApiKept = AuditEventServiceImpl.mergeSnapshots( mergedSnapshot, apiSnapshot1, false, false, true, true, false, formSnapshot); check("乱序到达的旧接口不覆盖较新补充值", snapshotHasValue(newerApiKept, "amount", "3") && snapshotHasValue(newerApiKept, "api_field", "latest")); String noFormSnapshot = AuditEventServiceImpl.mergeSnapshots( apiSnapshot2, apiSnapshot1, false, false, false, false, false, null); check("无表单场景继续按较新事件选择快照", JSON.parseObject(apiSnapshot2).equals(JSON.parseObject(noFormSnapshot))); String scopedDiffs = "[{\"field\":\"name\",\"newData\":\"主表\"}," + "{\"field\":\"ghost\",\"newData\":\"不存在\"}," + "{\"field\":\"tableField1\",\"jnpfKey\":\"table\"," + "\"targetTable\":\"child_table\"," + "\"chidField\":[{\"prop\":\"item_name\",\"label\":\"名称\"}," + "{\"prop\":\"ghost_child\",\"label\":\"非法\"}]," + "\"chidData\":[{\"item_name\":\"A\",\"jnpf_old_item_name\":\"\"," + "\"ghost_child\":\"X\",\"jnpf_old_ghost_child\":\"Y\",\"jnpf_type\":0}]}]"; String filteredDiffs = AuditEventServiceImpl.filterFieldDiffs( scopedDiffs, "main_table", table -> columns(table)); check("过滤主表不存在字段", diffCount(filteredDiffs, "ghost") == 0 && diffCount(filteredDiffs, "name") == 1); check("子表字段只按对应子表列过滤", filteredDiffs.contains("item_name") && !filteredDiffs.contains("ghost_child") && filteredDiffs.contains("\"targetTable\":\"child_table\"")); String sameNameDifferentTables = AuditEventServiceImpl.mergeFieldDiffs( "[{\"field\":\"name\",\"targetTable\":\"main_table\",\"newData\":\"主\"}]", "[{\"field\":\"name\",\"targetTable\":\"child_table\",\"newData\":\"子\"}]", false, true); check("主表和子表同名字段不互相覆盖", diffCount(sameNameDifferentTables, "name") == 2); String firstBatchRow = batchRowDiff("row-1", "draft", "done"); String batchMerged = firstBatchRow; for (int i = 2; i <= 10; i++) { batchMerged = AuditEventServiceImpl.mergeFieldDiffs( batchMerged, batchRowDiff("row-" + i, "pending-" + i, "done"), false, true); } boolean allTargetIdsRetained = true; for (int i = 1; i <= 10; i++) { allTargetIdsRetained &= batchMerged.contains("\"targetId\":\"row-" + i + "\""); } check("批量操作10条记录的同字段按 targetId 全部保留", diffCount(batchMerged, "status") == 10 && allTargetIdsRetained); String sameBatchRowLater = "[{\"field\":\"status\",\"targetTable\":\"lims_task\"," + "\"targetId\":\"row-1\",\"oldData\":\"done\",\"newData\":\"archived\"}]"; String sameRowMerged = AuditEventServiceImpl.mergeFieldDiffs( firstBatchRow, sameBatchRowLater, false, true); check("同一 targetId 的同字段仍合并时间端点", diffCount(sameRowMerged, "status") == 1 && sameRowMerged.contains("\"oldData\":\"draft\"") && sameRowMerged.contains("\"newData\":\"archived\"")); String legacyBatchMerged = AuditEventServiceImpl.mergeFieldDiffs( "[{\"field\":\"status\",\"targetTable\":\"lims_task\"," + "\"oldData\":\"draft\",\"newData\":\"done\"}]", "[{\"field\":\"status\",\"targetTable\":\"lims_task\"," + "\"oldData\":\"done\",\"newData\":\"archived\"}]", false, true); check("旧日志缺少 targetId 时保持原合并规则", diffCount(legacyBatchMerged, "status") == 1); String upgradedLegacy = AuditEventServiceImpl.scopeFieldDiffs( "[{\"field\":\"status\",\"targetTable\":\"lims_task\"," + "\"oldData\":\"draft\",\"newData\":\"done\"}]", "lims_task", "row-1"); String legacyRetryMerged = AuditEventServiceImpl.mergeFieldDiffs( upgradedLegacy, sameBatchRowLater, false, true); check("旧日志与带 targetId 的迟到事件不会生成重复字段", diffCount(legacyRetryMerged, "status") == 1 && legacyRetryMerged.contains("\"newData\":\"archived\"")); String scopedSingle = AuditEventServiceImpl.scopeFieldDiffs( "[{\"field\":\"status\",\"oldData\":\"a\",\"newData\":\"b\"}]", "lims_task", "row-1"); check("单记录事件自动补充 targetTable 和 targetId", scopedSingle.contains("\"targetTable\":\"lims_task\"") && scopedSingle.contains("\"targetId\":\"row-1\"")); String explicitlyScoped = AuditEventServiceImpl.scopeFieldDiffs( batchRowDiff("row-2", "pending", "done"), "other_table", "other-row"); check("批量差异显式 targetId 不被顶层首条记录覆盖", explicitlyScoped.contains("\"targetTable\":\"lims_task\"") && explicitlyScoped.contains("\"targetId\":\"row-2\"") && !explicitlyScoped.contains("other-row")); String formScopedProtection = AuditEventServiceImpl.mergeFieldDiffs( "[{\"field\":\"name\",\"targetTable\":\"main_table\",\"newData\":\"表单主表\"}," + "{\"field\":\"name\",\"targetTable\":\"child_table\",\"newData\":\"\"}]", "[{\"field\":\"name\",\"targetTable\":\"main_table\",\"newData\":\"接口主表\"}," + "{\"field\":\"name\",\"targetTable\":\"child_table\",\"newData\":\"接口子表\"}]", false, true, true, false, protectedFields(AuditDiffKey.of("main_table", "name"))); check("表单保护字段按物理表隔离", diffHasScopedValue(formScopedProtection, "main_table", "name", "表单主表") && diffHasScopedValue(formScopedProtection, "child_table", "name", "接口子表")); Set normalizedLegacyKeys = AuditEventServiceImpl.normalizedProtectedFields( Arrays.asList("main_table" + Character.MIN_VALUE + "name")); String persistedKeys = JSON.toJSONString(normalizedLegacyKeys); check("旧 NUL 复合键写回前转换为可打印格式", normalizedLegacyKeys.contains(AuditDiffKey.of("main_table", "name")) && AuditDiffKey.isScoped(AuditDiffKey.of("main_table", "name")) && !AuditDiffKey.isScoped("@table:2147483647:x:name") && persistedKeys.indexOf(Character.MIN_VALUE) < 0 && !persistedKeys.contains("\\u0000")); System.out.println("=== 写入合并断言:" + passed + " 通过 / " + failed + " 失败 ==="); System.exit(failed == 0 ? 0 : 1); } private static void check(String name, boolean ok) { if (ok) { passed++; System.out.println(" [PASS] " + name); } else { failed++; System.out.println(" [FAIL] " + name); } } @SuppressWarnings("unchecked") private static boolean hasDiff(String json, String field, String fieldName, String oldData, String newData) { List diffs = JSON.parseArray(json, Map.class); for (Map diff : diffs) { if (field.equals(String.valueOf(diff.get("field"))) && fieldName.equals(String.valueOf(diff.get("fieldName"))) && oldData.equals(String.valueOf(diff.get("oldData"))) && newData.equals(String.valueOf(diff.get("newData")))) { return true; } } return false; } private static boolean diffHasValue(String json, String field, String key, String expected) { List diffs = JSON.parseArray(json, Map.class); for (Map diff : diffs) { if (field.equals(String.valueOf(diff.get("field")))) { return expected.equals(String.valueOf(diff.get(key))); } } return false; } private static boolean snapshotHasValue(String json, String field, String expected) { Map snapshot = JSON.parseObject(json, Map.class); return expected.equals(String.valueOf(snapshot.get(field))); } private static Set columns(String table) { if ("main_table".equals(table)) { return protectedFields("id", "name"); } if ("child_table".equals(table)) { return protectedFields("f_id", "item_name"); } return protectedFields(); } private static int diffCount(String json, String field) { int count = 0; for (Map diff : JSON.parseArray(json, Map.class)) { if (field.equals(String.valueOf(diff.get("field")))) { count++; } } return count; } private static String batchRowDiff(String targetId, String oldData, String newData) { return "[{\"field\":\"status\",\"targetTable\":\"lims_task\",\"targetId\":\"" + targetId + "\",\"oldData\":\"" + oldData + "\",\"newData\":\"" + newData + "\"}]"; } private static boolean diffHasScopedValue(String json, String table, String field, String expected) { for (Map diff : JSON.parseArray(json, Map.class)) { if (table.equals(String.valueOf(diff.get("targetTable"))) && field.equals(String.valueOf(diff.get("field")))) { return expected.equals(String.valueOf(diff.get("newData"))); } } return false; } private static LinkedHashSet protectedFields(String... fields) { LinkedHashSet result = new LinkedHashSet<>(); java.util.Collections.addAll(result, fields); return result; } private AuditEventIngestMergeProbe() { } }