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-audit-sdk/src/main/java/jnpf/audit/sdk/AuditTxHolder.java |  210 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 210 insertions(+), 0 deletions(-)

diff --git a/jnpf-biz-common/jnpf-audit-sdk/src/main/java/jnpf/audit/sdk/AuditTxHolder.java b/jnpf-biz-common/jnpf-audit-sdk/src/main/java/jnpf/audit/sdk/AuditTxHolder.java
new file mode 100644
index 0000000..4723ed6
--- /dev/null
+++ b/jnpf-biz-common/jnpf-audit-sdk/src/main/java/jnpf/audit/sdk/AuditTxHolder.java
@@ -0,0 +1,210 @@
+package jnpf.audit.sdk;
+
+import jnpf.audit.AuditApiConsts;
+import jnpf.audit.model.AuditEventDTO;
+import jnpf.util.RandomUtil;
+import org.slf4j.MDC;
+import org.springframework.transaction.support.TransactionSynchronization;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 浜嬪姟绾у璁$紦鍐� holder锛坰pec 搂3.3/搂5.5 濂戠害鐨勬渶灏忓疄鐜帮級銆�
+ *
+ * <p>鑱岃矗锛氭妸鍚屼竴浜嬪姟鍐呮彁浜ょ殑瀹¤浜嬩欢缂撳啿鍒� afterCommit 鎵嶅紓姝ュ彂鍑猴紙鍥炴粴涓嶅彂锛夛紱operationId 鐨�
+ * 鐢熸垚涓� MDC 鍐欏叆缁熶竴褰掓湰绫诲崟婧愮鐞嗭紙缁堥獙#6锛歝ollector 鍏堝啓 MDC 浼氱牬鍧� previousMdc 璇箟锛夈��
+ *
+ * <p><b>operationId 璇箟锛圡2 spec 搂2锛�</b>锛氬綊灞炴渶澶栧眰鐨勩�屾搷浣滀綔鐢ㄥ煙銆嶏紙{@link #beginOperation}
+ * 杩斿洖鐨� OperationScope锛夛紝浣滅敤鍩熷唴鐨勫叏閮ㄤ簨鍔♀�斺�斿惈 REQUIRES_NEW 瀛愪簨鍔♀�斺�斿叡浜悓涓�涓� id锛�
+ * 娌℃湁浣滅敤鍩熸椂閫�鍖栦负姣忎釜浜嬪姟鑷繁涓�涓� id銆傜紦鍐蹭粛鎸変簨鍔″垝鍒嗭紙鍥炴粴涓嶅彂鐨勮涔変笉鍙橈級锛�
+ * 鍙槸銆岀紦鍐茶韩浠姐�嶄笌銆屾搷浣滆韩浠姐�嶈嚜姝よВ鑰︺��
+ *
+ * <p>鍏ㄧ被鍙湁涓�鏉� id 瑙勫垯锛�<b>鍐冲畾 id 鏃跺厛璇� MDC锛屾湁鍊煎垯娌跨敤锛屾棤鍊兼墠鐢熸垚</b>鈥斺��
+ * {@link #submit} 鏃犱簨鍔″垎鏀�亄@link #beginOperation} 鏃犱簨鍔″垎鏀�亄@link #createBuffer}
+ * 涓夊涓�鑷达紝鏃犱緥澶栥�傝法浜嬪姟锛圧EQUIRES_NEW锛夌殑娌跨敤鍦ㄦ寕璧风偣鐢变袱鏉¢�斿緞鎵挎帴锛�
+ * {@code previousMdc} 闈� null 鏃堕潬瀹冩湰韬紝涓� null 鏃堕潬 {@code TxAuditBuffer.scopeOwned}鈥斺��
+ * 涓よ�呯殑绛変环鎬т緷璧栦笅闈� {@link #createBuffer} 澶勬爣鏄庣殑涓嶅彉閲忋��
+ *
+ * <p>鏈被鐨勬椂搴忔敞閲婂搴斾簨鍔$骇缂撳啿鐨勫苟鍙戞椂搴忓绾︼紝鍧囩粡杩囬獙璇佲�斺�旈噸鏋勬椂涓嶈闅忔墜鍒犳敼锛�
+ * 姣忎竴澶勯兘瀵瑰簲涓�涓湡瀹炲瓨鍦ㄨ繃鐨勭珵鎬佸満鏅��
+ * 鍥炲綊瀹堝崼锛歿@code bash tasks/audit-opid-checks.sh}锛堝崄涓�椤瑰苟鍙戞椂搴忕煩闃碉級銆�
+ * 骞冲彴闆姳宸ュ叿缁忕幇鍦烘牳瀹炰负 {@link RandomUtil#uuId()}锛�= {@code String.valueOf(YitIdHelper.nextId())}锛夈��
+ */
+public class AuditTxHolder {
+    private static final Object RESOURCE_KEY = new Object();
+
+    /** 浜嬪姟绾х紦鍐诧細{operationId + 涓嶅彲鍙樹簨浠跺垪琛▆锛汳DC 淇濆瓨/鎭㈠闃茬嚎绋嬫睜涓插彿锛堣瘎瀹�#6锛� */
+    static class TxAuditBuffer {
+        final String operationId;
+        final String previousMdc;                       // 杩涘叆鍓嶇殑 MDC 鏃у�硷紝afterCompletion 鎭㈠
+        final List<AuditEventDTO> events = new ArrayList<>();
+        /**
+         * 浣滅敤鍩熸槸鍚�**缁忕敱 {@link #beginOperation} 鐨勪簨鍔″垎鏀�**鎸傚湪鏈� buffer 涓娿��
+         *
+         * <p><b>娉ㄦ剰瀹冧笉绛変簬銆屾湰 buffer 鐨� id 褰掑睘鏌愪釜浣滅敤鍩熴��</b>锛氬舰鎬佺敳锛堜綔鐢ㄥ煙鍦ㄤ簨鍔″寮�鍚��
+         * buffer 鐢� {@link #submit} 鍦ㄤ簨鍔″唴鍒涘缓锛変笅 id 鍚屾牱褰掑睘璇ヤ綔鐢ㄥ煙锛屼絾鏈爣璁颁负 false鈥斺��
+         * 閭g褰㈡�佺殑娌跨敤鐢� {@code previousMdc} 鎵挎帴锛屼笉璧版湰鏍囪銆傛湰鏍囪鍙礋璐hˉ涓�
+         * {@code previousMdc == null} 閭d竴鏍笺��
+         *
+         * <p>涓轰粈涔堥渶瑕佸畠锛氫綔鐢ㄥ煙鐢变簨鍔″垎鏀嚜宸卞垱寤烘椂 previousMdc 鎭掍负 null锛屾鏃惰嫢鎸傝捣鐐规寜
+         * previousMdc 鎭㈠浼氭妸 MDC 娓呯┖銆佸唴灞傚瓙浜嬪姟鍙︾敓鎴� id 鈫� 鍚屼竴娆℃搷浣滃垎鍙�
+         * 锛坰pec 搂1.3锛涜繖姝f槸浠婃棩 lims 鍥涘鐨勪簨鍔″舰鎬侊級銆�
+         *
+         * <p>闈� final锛氫綔鐢ㄥ煙鍙兘**鏅氫簬** buffer 鍑虹幇锛堝眰 1 鍏堝缓 buffer锛岄殢鍚庢墠杩涘叆鏍囨敞鏂规硶锛�
+         * spec 搂4 褰㈡�佷竵锛夛紝鏁� beginOperation 鐨勫垱寤轰笌澶嶇敤涓ゆ潯璺緞閮借缃綅銆�
+         * buffer 缁戝湪 TSM 浜嬪姟璧勬簮涓娿�佺嚎绋嬪唴鐙崰锛屾棤骞跺彂鍐欓闄┿��
+         */
+        boolean scopeOwned;
+        TxAuditBuffer(String operationId, String previousMdc) {
+            this.operationId = operationId; this.previousMdc = previousMdc;
+        }
+    }
+
+    /** operationId 鐨勭敓鎴愪笌 MDC 鍐欏叆缁熶竴褰掓湰绫荤鐞嗭紙缁堥獙#6锛歝ollector 鍏堝啓 MDC 浼氱牬鍧� previousMdc 璇箟锛� */
+    public static void submit(AuditEventDTO event, AuditEventPublisher publisher) {
+        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
+            String outerMdc = MDC.get(AuditApiConsts.MDC_OPERATION_ID);
+            String opId = event.getOperationId() != null ? event.getOperationId()
+                    : (outerMdc != null ? outerMdc : RandomUtil.uuId());   // 骞冲彴闆姳宸ュ叿锛岀被鍚嶅鐓х幇鍦�
+            publisher.publishAsync(Collections.singletonList(event.toBuilder().operationId(opId).build()));
+            if (outerMdc == null) {
+                MDC.remove(AuditApiConsts.MDC_OPERATION_ID);   // 鑷繁娌″啓杩囧氨淇濇寔骞插噣
+            }
+            return;
+        }
+        TxAuditBuffer buffer = (TxAuditBuffer) TransactionSynchronizationManager.getResource(RESOURCE_KEY);
+        if (buffer == null) {
+            buffer = createBuffer(publisher);
+        }
+        buffer.events.add(event.toBuilder().operationId(buffer.operationId).build());
+    }
+
+    /** 鎿嶄綔浣滅敤鍩燂細close() 鍙湪**鏃犱簨鍔�**鍒嗘敮闇�瑕佹仮澶� MDC锛涙湁浜嬪姟鏃剁敱 afterCompletion 缁熶竴鎭㈠銆� */
+    public interface OperationScope extends AutoCloseable {
+        String operationId();
+        @Override void close();   // 瑕嗗啓鎺� AutoCloseable 鐨� throws Exception锛岃皟鐢ㄦ柟鏃犻渶 catch
+    }
+
+    /**
+     * getOrCreate 璇箟锛圡2 濂戠害鍏戠幇锛孧1 宸插湪 currentOperationId 鐨� javadoc 棰勫憡锛夛細
+     * 杩涘叆涓氬姟鏂规硶鏃跺嵆纭繚銆屾湰浜嬪姟鏈� operationId 涓斿凡鍐欏叆 MDC銆嶏紝渚涘唴灞傛棫 BizLogAspect 璇诲彇銆�
+     *
+     * <p>涓轰粈涔堜笉鑳介潬 {@link #submit}锛歴ubmit 鍙戠敓鍦� proceed() 涔嬪悗锛岃�屽唴灞傛棫鍒囬潰鍐欐棩蹇楁洿鏃╋紝
+     * 閭f椂 MDC 杩樻病鍊� 鈫� A/B 閰嶄笉涓婂銆�
+     *
+     * <p>MDC 鐢熷懡鍛ㄦ湡浠嶉攣鍦ㄦ湰绫讳竴澶勶紙鍗曟簮濂戠害锛夛細鏈変簨鍔¤蛋 afterCompletion 鎭㈠锛�
+     * 鏃犱簨鍔$敱杩斿洖鐨� scope.close() 鎭㈠鈥斺�斾袱鏉¤矾寰勯兘淇濆瓨杩涘叆鍓嶆棫鍊硷紝闃茬嚎绋嬫睜澶嶇敤涓插彿銆�
+     */
+    public static OperationScope beginOperation(AuditEventPublisher publisher) {
+        return beginOperation(publisher, null);
+    }
+
+    /**
+     * 浣跨敤鍙俊涓氬姟鍑瘉娲剧敓鐨勬樉寮忓叧鑱旈敭寮�鍚綔鐢ㄥ煙锛涘凡鏈夊灞備綔鐢ㄥ煙鏃朵粛浠ュ灞備负鍑嗐��
+     */
+    public static OperationScope beginOperation(AuditEventPublisher publisher, String preferredOperationId) {
+        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
+            final String previousMdc = MDC.get(AuditApiConsts.MDC_OPERATION_ID);
+            final String opId = previousMdc != null ? previousMdc
+                    : (preferredOperationId != null ? preferredOperationId : RandomUtil.uuId());
+            MDC.put(AuditApiConsts.MDC_OPERATION_ID, opId);
+            return new OperationScope() {
+                @Override public String operationId() { return opId; }
+                @Override public void close() { restoreMdc(previousMdc); }
+            };
+        }
+        TxAuditBuffer buffer = (TxAuditBuffer) TransactionSynchronizationManager.getResource(RESOURCE_KEY);
+        if (buffer == null) {
+            buffer = createBuffer(publisher, preferredOperationId);
+        }
+        // 浣滅敤鍩熸爣璁帮細鍒涘缓涓庡鐢ㄤ袱鏉¤矾寰勯兘瑕佺疆锛坰pec 搂4 褰㈡�佷竵鈥斺�斿眰 1 鍙兘鍏堝缓浜� buffer锛�
+        // 浣滅敤鍩熸櫄浜庡畠鍑虹幇锛涘彧鍦ㄥ垱寤鸿矾寰勭疆浣嶄細璁╄褰㈡�佺殑 REQUIRES_NEW 鍐呭眰閲嶆柊鍒嗗弶锛�
+        buffer.scopeOwned = true;
+        final String opId = buffer.operationId;
+        return new OperationScope() {
+            @Override public String operationId() { return opId; }
+            @Override public void close() { /* 鏈変簨鍔★細MDC 鐢� afterCompletion 鎭㈠锛屾澶勫埢鎰� no-op */ }
+        };
+    }
+
+    private static TxAuditBuffer createBuffer(final AuditEventPublisher publisher) {
+        return createBuffer(publisher, null);
+    }
+
+    private static TxAuditBuffer createBuffer(final AuditEventPublisher publisher, String preferredOperationId) {
+        // 鍏抽敭鏃跺簭锛氬厛淇濆瓨澶栧眰鏃у�硷紝鍐嶅喅瀹氭湰浜嬪姟 operationId 骞跺啓 MDC锛堢粓楠�#6锛�
+        String previousMdc = MDC.get(AuditApiConsts.MDC_OPERATION_ID);
+        // M2 璇箟锛坰pec 搂2锛夛細operationId 褰掑睘鏈�澶栧眰鎿嶄綔浣滅敤鍩熲�斺�擬DC 宸叉湁鍊煎嵆浠h〃澶栧眰宸叉湁
+        // 浣滅敤鍩燂紝涓�寰嬫部鐢紱鏃犲�兼墠鐢熸垚銆備笌 submit(:42)/beginOperation(:77) 涓ゆ潯鏃犱簨鍔″垎鏀殑
+        // 鍐欐硶鍦ㄦ鎷夐綈锛屽叏绫诲彧鏈変竴鏉¤鍒欙細"鍏堢湅 MDC锛屾湁鍒欐部鐢�"銆�
+        //
+        // 鈿狅笍 鏈纭珛涓�鏉�**鍏ㄧ被渚濊禆鐨勪笉鍙橀噺**锛氬嚒鏈柟娉曚骇鍑虹殑 buffer锛�
+        //        previousMdc != null  鉄�  operationId == previousMdc
+        // suspend 鐨勪笁鐩紙scopeOwned ? operationId : previousMdc锛夋鏄潬瀹冭涓ゆ敮娈婇�斿悓褰掆�斺��
+        // previousMdc 闈� null 鏃朵袱鏀彇鍊煎繀鐒剁浉绛夛紝鏁呬笁鐩彧鍦� previousMdc == null 鏃舵墠鐪熸鍒嗗矓
+        // 锛堝嵆琛屼负鍙樻洿鍗婂緞 = spec 搂4 褰㈡�佷笝/涓�/鎴婏級銆�**鑻ュ皢鏉ョ粰 id 鍐冲畾閫昏緫鍔犳梺璺紙渚嬪鍏佽璋冪敤鏂�
+        // 鏄惧紡浼犲叆 id锛夎�屾墦鐮磋涓嶅彉閲忥紝suspend 鐨勭浜屾敮浼氶潤榛樺け鍘绘部鐢ㄨ兘鍔涒�斺�旀敼杩欓噷鍏堝幓鐪� suspend銆�**
+        final TxAuditBuffer created = new TxAuditBuffer(
+                previousMdc != null ? previousMdc
+                        : (preferredOperationId != null ? preferredOperationId : RandomUtil.uuId()), previousMdc);
+        MDC.put(AuditApiConsts.MDC_OPERATION_ID, created.operationId);
+        TransactionSynchronizationManager.bindResource(RESOURCE_KEY, created);
+        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+            private TxAuditBuffer suspended;
+            @Override public void suspend() {   // REQUIRES_NEW 鍐呭眰寮�濮嬶細TSM 涓� MDC 鍚屾鎸傝捣
+                suspended = (TxAuditBuffer) TransactionSynchronizationManager.unbindResource(RESOURCE_KEY);
+                // spec 搂4锛屾寜 (scopeOwned, previousMdc) 鍥涚粍鍚堣锛�
+                //   (true , null) 褰㈡�佷笝/涓�/鎴� 鈫� 鎭㈠ operationId锛屽唴灞傛部鐢� 鈫愩�愭湰娆″敮涓�琚敼鍙樼殑缁勫悎銆�
+                //   (true , X   ) 鈫� 鎭㈠ operationId锛屼絾鐢� createBuffer 鐨勪笉鍙橀噺 X == operationId锛�
+                //                   涓庤蛋绗簩鏀瓑浠凤紝鏃犺涓哄彉鍖�
+                //   (false, X   ) 褰㈡�佺敳 鈫� 鎭㈠ previousMdc = X锛屽唴灞傜収鏍锋部鐢紙浣滅敤鍩熷瓨鍦ㄤ絾涓嶇疆鏍囪锛�
+                //   (false, null) 褰㈡�佷箼 鈫� 鎭㈠ null锛圡DC 娓呯┖锛夛紝鍐呭眰鍙︾敓鎴� id
+                // 鍚庝袱鏍奸�愬瓧淇濈暀 M1銆岀粓楠�#6锛氬彧瑙g粦璧勬簮涓嶅姩 MDC 鐨勮瘽锛屽唴灞備細鐪嬪埌澶栧眰 opId銆嶇殑鍘熻涓�
+                // 锛圫5 鏂█瀹堜箣锛夛紱绗竴鏍兼槸鏈**鏈夋剰瑕嗙洊**璇ョ粨璁衡�斺�旂粓楠�#6 鏄湪銆屼竴涓簨鍔′竴涓� id銆�
+                // 璇箟涓嬩綔鍑虹殑锛孧2 寮曞叆 beginOperation 鍚庤涔夋敼涓恒�屼竴娆$敤鎴锋搷浣滀竴涓� id銆嶃��
+                // 鏀瑰姩鍓嶅厛璇� spec 搂2 涓� createBuffer 澶勬爣鏄庣殑涓嶅彉閲忋��
+                restoreMdc(suspended.scopeOwned ? suspended.operationId : suspended.previousMdc);
+            }
+            @Override public void resume() {
+                TransactionSynchronizationManager.bindResource(RESOURCE_KEY, suspended);
+                MDC.put(AuditApiConsts.MDC_OPERATION_ID, suspended.operationId);
+            }
+            @Override public void afterCommit() {
+                if (created.events.isEmpty()) {
+                    return;   // beginOperation 寤轰簡缂撳啿浣嗘湰娆℃病鏈変簨浠讹紙濡傛敞瑙f柟娉曞唴鏃犲璁″姩浣滐級锛屼笉绌哄彂
+                }
+                publisher.publishAsync(new ArrayList<>(created.events));   // 鍥炴粴涓嶅彂
+            }
+            @Override public void afterCompletion(int status) {
+                if (TransactionSynchronizationManager.hasResource(RESOURCE_KEY)) {
+                    TransactionSynchronizationManager.unbindResource(RESOURCE_KEY);   // 闃叉硠婕�
+                }
+                restoreMdc(created.previousMdc);   // 鎭㈠澶栧眰鍊� / 娓呯悊闃茬嚎绋嬫睜涓插彿
+            }
+        });
+        return created;
+    }
+
+    private static void restoreMdc(String previous) {
+        if (previous != null) {
+            MDC.put(AuditApiConsts.MDC_OPERATION_ID, previous);
+        } else {
+            MDC.remove(AuditApiConsts.MDC_OPERATION_ID);
+        }
+    }
+
+    /**
+     * 璇绘帴鍙o紙缁堥獙鍥涜疆鈶狅紝M1 鍙0鏄庝笉娑堣垂锛夛細杩斿洖褰撳墠浜嬪姟宸叉湁鐨� operationId锛�
+     * 鏃犳椿璺冪紦鍐茶繑鍥� null锛�**鏈柟娉曚笉瑙﹀彂鍒涘缓**锛堢粓楠屼簲杞憼锛歫avadoc 涓嶆壙璇鸿秴鍑轰唬鐮佽兘鍔涚殑璇箟锛夈��
+     * M2 璁捐濂戠害锛欯AuditLog aspect 涓嶅緱鑷鐢熸垚 operationId鈥斺�旇嫢闇�瑕� getOrCreate 璇箟锛�
+     * M2 鏃跺湪鏈被鏂板瀵瑰簲鏂规硶锛岀敱 holder 鍗曟簮鐢熸垚鍚庡啓 MDC 渚涙棫 BizLogAspect 璇诲彇锛�
+     * aspect 涓� holder 鍙屾簮鐢熸垚浼氱牬鍧� A/B 绮剧‘閰嶅銆�
+     */
+    public static String currentOperationId() {
+        TxAuditBuffer buffer = (TxAuditBuffer) TransactionSynchronizationManager.getResource(RESOURCE_KEY);
+        return buffer != null ? buffer.operationId : null;
+    }
+}

--
Gitblit v1.8.0