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/title/AuditRecordTitleBatchProbe.java |  169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 169 insertions(+), 0 deletions(-)

diff --git a/jnpf-biz-common/jnpf-biz-common-audit/src/test/java/jnpf/bizcommon/audit/service/title/AuditRecordTitleBatchProbe.java b/jnpf-biz-common/jnpf-biz-common-audit/src/test/java/jnpf/bizcommon/audit/service/title/AuditRecordTitleBatchProbe.java
new file mode 100644
index 0000000..eb7a974
--- /dev/null
+++ b/jnpf-biz-common/jnpf-biz-common-audit/src/test/java/jnpf/bizcommon/audit/service/title/AuditRecordTitleBatchProbe.java
@@ -0,0 +1,169 @@
+package jnpf.bizcommon.audit.service.title;
+
+import jnpf.bizcommon.audit.entity.AuditEventEntity;
+import jnpf.audit.diff.AuditFieldDiff;
+import jnpf.bizcommon.audit.entity.model.AuditFieldDiffViewVO;
+import jnpf.bizcommon.audit.service.AuditFieldDiffViewService;
+import jnpf.bizcommon.audit.service.value.AuditFieldValueContext;
+import jnpf.bizcommon.audit.service.value.AuditFieldValueResolver;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏍囬娓叉煋銆屼富琛� + 鍏ㄩ儴瀛愯〃琛屼竴娆¤В鏋愩�嶇殑鎺㈤拡锛圓3锛�2026-08-03 Codex 缁堝 Low锛氭煡璇㈡斁澶э級銆�
+ *
+ * <p>婧愮爜鏂█鍙畧寰椾綇褰㈢姸锛堟湁娌℃湁 renderLines銆乥uild 璋冧簡鍑犳锛夛紝瀹堜笉浣�**瀵逛綅**鈥斺��
+ * 鍚堟壒涔嬪悗姣忚鐨勫睍绀哄�兼槸鎸変綅缃粠鍚屼竴鎵硅В鏋愮粨鏋滈噷鍒囧洖鍘荤殑锛屽垏閿欎竴鏍煎氨浼氭妸 A 琛岀殑鍊兼樉绀哄埌
+ * B 琛岀殑鏍囬涓婏紝鑰� {@code record_title} 鏄� append-only 瀛樺崄骞寸殑鏄庢枃鍒椼�傛墍浠ヨ繖浜涘繀椤昏窇璧锋潵楠岋細
+ * <ol>
+ *   <li>鏁翠唤鏍囬锛堜富 + N 涓瓙琛岋級**鍙В鏋愪竴娆�**锛屼笉鏄� 1 + N 娆★紱</li>
+ *   <li>姣忎釜瀛愯〃琛岀殑鏍囬鍚勮嚜瀵瑰簲鑷繁閭d竴琛岀殑鍊硷紙鍚勮鍒绘剰鍙栦笉鍚屽�硷紝閿欎綅绔嬪埢鐜板舰锛夛紱</li>
+ *   <li><b>涓棿鏌愯鏁磋涓虹┖</b>鏃惰琛屼笉浜у嚭鏍囬锛屼笖**涓嶆妸鍚庨潰鍑犺鐨勫浣嶅甫鍋�</b>鈥斺��
+ *       杩欐槸浣嶇疆鍒嗗彂鏈�瀹规槗閿欑殑鍦版柟锛堢┖琛岃础鐚� 0 涓В鏋愰」锛屽垏鐗囪竟鐣屽繀椤昏窡鐫�璧帮級銆�</li>
+ * </ol>
+ */
+public final class AuditRecordTitleBatchProbe {
+
+    private static int passed;
+    private static int failed;
+
+    public static void main(String[] args) throws Exception {
+        CountingResolver counter = new CountingResolver();
+        AuditFieldDiffViewService viewService = new AuditFieldDiffViewService(
+                Collections.<AuditFieldValueResolver>singletonList(counter), "Asia/Shanghai");
+        AuditRecordTitleBuilder builder = new AuditRecordTitleBuilder();
+        Field field = AuditRecordTitleBuilder.class.getDeclaredField("fieldDiffViewService");
+        field.setAccessible(true);
+        field.set(builder, viewService);
+
+        AuditTitleSpec spec = new AuditTitleSpec(
+                Arrays.asList("bian_hao", "beizhu"),
+                Collections.singletonList(new AuditTitleSpec.SubTableSpec(
+                        "tableField101", "lims_sub", Arrays.asList("xiang_mu", "jie_guo"))));
+
+        // 瀛愯〃绗� 2 琛岋紙涓嬫爣 1锛夊埢鎰忔暣琛屼负绌猴細瀹冭础鐚� 0 涓В鏋愰」锛屽悗闈袱琛岀殑鍒囩墖璧风偣蹇呴』璺熺潃鎸��
+        List<Map<String, Object>> subRows = new ArrayList<Map<String, Object>>();
+        subRows.add(row("xiang_mu", "椤圭洰A", "jie_guo", "缁撴灉A"));
+        subRows.add(row("xiang_mu", "", "jie_guo", ""));
+        subRows.add(row("xiang_mu", "椤圭洰C", "jie_guo", "缁撴灉C"));
+        subRows.add(row("xiang_mu", "椤圭洰D", "jie_guo", "缁撴灉D"));
+
+        Map<String, Object> main = row("bian_hao", "BH-001", "beizhu", "澶囨敞X");
+        main.put("tablefield101", subRows);
+
+        AuditRecordTitle title = builder.build(spec, main, context(), labels(),
+                Collections.<String>emptySet());
+
+        check("鏁翠唤鏍囬鍙В鏋愪竴娆★紙涓嶆槸 1 + N 娆★級", counter.calls == 1, "calls=" + counter.calls);
+        check("涓绘爣棰樻寜涓昏〃琛屾覆鏌�", "BH-001/澶囨敞X".equals(title.mainTitle()), title.mainTitle());
+        check("鏁磋涓虹┖鐨勫瓙琛ㄨ涓嶄骇鍑烘爣棰�", title.subTitles().size() == 3,
+                title.subTitles().toString());
+        // 瀵逛綅鏂█锛氱┖琛屼箣鍚庣殑涓よ蹇呴』杩樻槸鑷繁鐨勫�笺�傚垏鐗囪竟鐣屾病璺熺潃绌鸿璧扮殑璇濓紝
+        // 杩欓噷浼氳鍒般�岄」鐩瓺/缁撴灉D銆嶉《鍒扮 2 浣嶏紙鎴栬秺鐣屼涪琛岋級銆�
+        check("绌鸿涔嬪悗鐨勫瓙琛ㄨ涓嶉敊浣�",
+                Arrays.asList("椤圭洰A/缁撴灉A", "椤圭洰C/缁撴灉C", "椤圭洰D/缁撴灉D").equals(title.subTitles()),
+                title.subTitles().toString());
+        check("瀛愯〃鍚嶅瀹炲甫鍑�", "lims_sub".equals(title.subTable()), title.subTable());
+        check("瀛愯〃鏁版嵁鍙緱鏃朵笉鏍� unavailable", !title.subDataUnavailable(),
+                String.valueOf(title.subDataUnavailable()));
+
+        // 瑙f瀽鍣ㄧ湅鍒扮殑鏄�**鍚堝苟鍚庣殑涓�鏁存壒**锛氫富琛� 2 椤� + 瀛愯〃 3 琛� 脳 2 椤� = 8 椤广��
+        // 杩欐潯瀹堢殑鏄�岀湡鐨勫悎鎵逛簡銆嶈�屼笉鏄�岃皟浜嗕竴娆′絾鍙杺浜嗕富琛ㄣ�嶃��
+        check("涓�娆¤В鏋愭嬁鍒板叏閮� 8 涓瓧娈甸」", counter.lastSize == 8, "size=" + counter.lastSize);
+
+        // 鎺掗櫎鍒楀湪鍚堟壒璺緞涓婄収鏍风敓鏁堬紙绛惧悕鍒楃粷涓嶈兘杩涙爣棰樷�斺�攁ppend-only 瀛樺崄骞达級
+        AuditRecordTitle excluded = builder.build(spec, main, context(), labels(),
+                Collections.singleton("beizhu"));
+        check("excludedFields 鍦ㄥ悎鎵硅矾寰勪笂浠嶇劧鐢熸晥",
+                "BH-001".equals(excluded.mainTitle()), excluded.mainTitle());
+
+        Map<String, String> frontendDisplays = new LinkedHashMap<String, String>();
+        frontendDisplays.put("bian_hao", "鍓嶇鏄剧ず缂栧彿");
+        frontendDisplays.put("beizhu", "鍓嶇鏄剧ず澶囨敞");
+        frontendDisplays.put("tampered", "浼�犲瓧娈�");
+        AuditRecordTitle frontendTitle = builder.build(spec, main, context(), labels(),
+                Collections.singleton("beizhu"), frontendDisplays);
+        check("鍓嶇鏄剧ず鍊艰鐩栨湇鍔$瑙f瀽缁撴灉涓斾繚鎸侀厤缃『搴�",
+                "鍓嶇鏄剧ず缂栧彿".equals(frontendTitle.mainTitle()), frontendTitle.mainTitle());
+        check("鍓嶇鏄剧ず鍊间笉鑳界粫杩囨帓闄ゅ瓧娈�",
+                !frontendTitle.mainTitle().contains("鍓嶇鏄剧ず澶囨敞"), frontendTitle.mainTitle());
+        check("鏈厤缃殑鍓嶇瀛楁涓嶈兘杩涘叆鏍囬",
+                !frontendTitle.mainTitle().contains("浼�犲瓧娈�"), frontendTitle.mainTitle());
+
+        // 瀛愯〃閿己甯� = 鍒や笉浜嗭紝濡傚疄鏍� unavailable锛堝悎鎵归噸鏋勪笉璇ユ妸杩欎釜璇箟寮勪涪锛�
+        Map<String, Object> noSub = row("bian_hao", "BH-002", "beizhu", "澶囨敞Y");
+        AuditRecordTitle missing = builder.build(spec, noSub, context(), labels(),
+                Collections.<String>emptySet());
+        check("瀛愯〃鏁版嵁缂哄腑浠嶆爣 unavailable", missing.subDataUnavailable(),
+                String.valueOf(missing.subDataUnavailable()));
+        check("瀛愯〃缂哄腑鏃朵富鏍囬鐓у嚭", "BH-002/澶囨敞Y".equals(missing.mainTitle()), missing.mainTitle());
+
+        Map<String, Object> idRow = new LinkedHashMap<String, Object>();
+        idRow.put("F_ID", "ROW-001");
+        check("鏈惎鐢ㄦ爣棰樺瓧娈垫椂浼樺厛浣跨敤璁板綍 f_id",
+                "ROW-001".equals(builder.resolveMainTitle(AuditTitleSpec.EMPTY,
+                        AuditRecordTitle.EMPTY, idRow, "EVENT-001")), idRow.toString());
+        check("鍒涘缓鏁版嵁鏈洖濉� f_id 鏃朵娇鐢ㄤ簨浠� dataId",
+                "EVENT-002".equals(builder.resolveMainTitle(AuditTitleSpec.EMPTY,
+                        AuditRecordTitle.EMPTY, Collections.<String, Object>emptyMap(), "EVENT-002")), "");
+        check("宸插惎鐢ㄥ瓧娈典絾鍊间负绌烘椂涓嶄吉瑁呮垚 f_id",
+                builder.resolveMainTitle(spec, AuditRecordTitle.EMPTY, idRow, "EVENT-003") == null, "");
+
+        System.out.printf("[AuditRecordTitleBatchProbe] %d 閫氳繃 / %d 澶辫触%n", passed, failed);
+        if (failed > 0) {
+            System.exit(1);
+        }
+    }
+
+    private static AuditEventEntity context() {
+        AuditEventEntity event = new AuditEventEntity();
+        event.setTargetTable("lims_ceshi_huichuan");
+        event.setTenantId("0");
+        event.setActionCode("UPDATE");
+        return event;
+    }
+
+    private static Map<String, AuditFieldDiff.FieldMeta> labels() {
+        return Collections.emptyMap();
+    }
+
+    private static Map<String, Object> row(String k1, Object v1, String k2, Object v2) {
+        Map<String, Object> row = new LinkedHashMap<String, Object>();
+        row.put(k1, v1);
+        row.put(k2, v2);
+        return row;
+    }
+
+    /** 鍙暟璋冪敤娆℃暟涓庢壒閲忓ぇ灏忥紝涓嶆敼浠讳綍灞曠ず鍊硷紙鏀逛簡灏卞垎涓嶆竻鏄В鏋愬櫒杩樻槸鍒嗗彂鍑虹殑閿欙級銆� */
+    private static final class CountingResolver implements AuditFieldValueResolver {
+        private int calls;
+        private int lastSize;
+
+        @Override
+        public boolean supports(String targetTable) {
+            return true;
+        }
+
+        @Override
+        public void resolve(AuditFieldValueContext context, List<AuditFieldDiffViewVO> fields) {
+            calls++;
+            lastSize = fields == null ? -1 : fields.size();
+        }
+    }
+
+    private static void check(String name, boolean ok, String actual) {
+        if (ok) {
+            passed++;
+            System.out.println("  鉁� " + name);
+        } else {
+            failed++;
+            System.out.println("  鉂� " + name + " 鈥斺�� 瀹為檯锛�" + actual);
+        }
+    }
+}

--
Gitblit v1.8.0