From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-biz-common/jnpf-biz-common-audit/src/test/java/jnpf/bizcommon/audit/service/impl/AuditEventIngestMergeProbe.java |  325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 325 insertions(+), 0 deletions(-)

diff --git a/jnpf-biz-common/jnpf-biz-common-audit/src/test/java/jnpf/bizcommon/audit/service/impl/AuditEventIngestMergeProbe.java b/jnpf-biz-common/jnpf-biz-common-audit/src/test/java/jnpf/bizcommon/audit/service/impl/AuditEventIngestMergeProbe.java
new file mode 100644
index 0000000..0fdd56a
--- /dev/null
+++ b/jnpf-biz-common/jnpf-biz-common-audit/src/test/java/jnpf/bizcommon/audit/service/impl/AuditEventIngestMergeProbe.java
@@ -0,0 +1,325 @@
+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("琛ㄥ崟鍏堝埌鏃惰ˉ鍏ユ帴鍙e瓧娈�", formThenApi.contains("\"field\":\"quyang_status\""));
+        check("琛ㄥ崟鏈夋晥鍊间笉琚帴鍙h鐩�",
+                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("琛ㄥ崟绌哄�煎厑璁告帴鍙hˉ鍏�", hasDiff(filled, "unit", "鍗曚綅", "", "绮�"));
+        check("琛ㄥ崟闆跺�煎厑璁告帴鍙hˉ鍏�", 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("蹇収绌哄�煎拰闆跺�煎厑璁告帴鍙hˉ鍏�", snapshotHasValue(mergedSnapshot, "amount", "3")
+                && snapshotHasValue(mergedSnapshot, "unit", "绮�"));
+        check("蹇収琛ュ叆琛ㄥ崟缂哄け瀛楁骞朵娇鐢ㄦ帴鍙f渶鏂板��",
+                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("琛ㄥ崟鍜屾帴鍙h姹傚厛鍚庝笉褰卞搷蹇収鍩哄噯",
+                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<String> 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<Map> diffs = JSON.parseArray(json, Map.class);
+        for (Map<String, Object> 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<Map> 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<String, Object> snapshot = JSON.parseObject(json, Map.class);
+        return expected.equals(String.valueOf(snapshot.get(field)));
+    }
+
+    private static Set<String> 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<String> protectedFields(String... fields) {
+        LinkedHashSet<String> result = new LinkedHashSet<>();
+        java.util.Collections.addAll(result, fields);
+        return result;
+    }
+
+    private AuditEventIngestMergeProbe() {
+    }
+}

--
Gitblit v1.8.0