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-lims/jnpf-lims-biz/src/test/java/jnpf/limsService/support/LimsSignUsageWiringProbe.java |  302 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 302 insertions(+), 0 deletions(-)

diff --git a/jnpf-lims/jnpf-lims-biz/src/test/java/jnpf/limsService/support/LimsSignUsageWiringProbe.java b/jnpf-lims/jnpf-lims-biz/src/test/java/jnpf/limsService/support/LimsSignUsageWiringProbe.java
new file mode 100644
index 0000000..ae8bd91
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-biz/src/test/java/jnpf/limsService/support/LimsSignUsageWiringProbe.java
@@ -0,0 +1,302 @@
+package jnpf.limsService.support;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Stream;
+
+/** SIGN_USED 涓氬姟鍏ュ彛銆佹ā鍧楄涔夊拰鎶樺彔鎺ョ嚎鐨勬簮鐮佺骇鍥炲綊鎺㈤拡銆� */
+public final class LimsSignUsageWiringProbe {
+
+    private static int passed;
+    private static int failed;
+
+    public static void main(String[] args) throws Exception {
+        String qingyandanController = read("jnpf-lims/jnpf-lims-controller/src/main/java/"
+                + "jnpf/limsController/LimsQingyandanController.java");
+        String quyangController = read("jnpf-lims/jnpf-lims-controller/src/main/java/"
+                + "jnpf/limsController/LimsQuyangController.java");
+        String jieyangController = read("jnpf-lims/jnpf-lims-controller/src/main/java/"
+                + "jnpf/limsController/LimsJieyangController.java");
+        String huanjingController = read("jnpf-lims/jnpf-lims-controller/src/main/java/"
+                + "jnpf/limsController/LimsHuanjingJianceController.java");
+        String qingyandanService = read("jnpf-lims/jnpf-lims-biz/src/main/java/"
+                + "jnpf/limsService/impl/LimsQingyandanServiceImpl.java");
+        String signService = read("jnpf-lims/jnpf-lims-biz/src/main/java/"
+                + "jnpf/limsService/impl/LimsSignServiceImpl.java");
+        String signOpType = read("jnpf-lims/jnpf-lims-entity/src/main/java/"
+                + "jnpf/limsEntity/SignOpType.java");
+        String qingyandanEntity = read("jnpf-lims/jnpf-lims-entity/src/main/java/"
+                + "jnpf/limsEntity/LimsQingyandanEntity.java");
+
+        // 2026-07-31 retarget锛堝師涓� checkConsumption("璇烽獙鍗曟彁浜�", qingyandanController,
+        //   "QINGYANDAN_SUBMIT")锛夛細鏃ф柇瑷�鏌ャ�宻ubmit 涔嬪悗璋冪敤浜� recordSignUsage銆嶏紝
+        //   鑰� D8 鏄庣‘琛ㄥ崟璺緞**鍋滃啓浜嬩欢銆佷繚鐣欏師瀛愯棰�**銆傝涔夊彉浜嗭紝鍒ゆ嵁璺熺潃鍙橈紱
+        //   璁ら杩欎欢浜�**蹇呴』缁х画鏈変汉瀹�**锛堥槻閲嶆斁鎶ゆ爮锛屽垹浜嗗悓涓�绛惧悕鍙弽澶嶄娇鐢級锛屾晠鏄� retarget 涓嶆槸鍒犻櫎銆�
+        checkQingyandanSubmitClaim(qingyandanController);
+        checkQingyandanFreshSign(qingyandanController);
+        checkCancelConsumption(qingyandanController);
+        checkConsumption("鍙栨牱", quyangController, "QUYANG_FETCH");
+        checkConsumption("鎺ユ牱", jieyangController, "JIEYANG_RECEIVE");
+        checkHuanjingFailureBoundary(huanjingController, "public R<String> quyangJilu(",
+                "limsHuanjingJianceService.quyangJilu", "HUANJING_QUYANG");
+        checkHuanjingFailureBoundary(huanjingController, "public R<String> terminateQuyangJilu(",
+                "limsHuanjingJianceService.terminateQuyangJilu", "HUANJING_QUYANG_TERMINATE");
+
+        // 鈹屸攢 2026-07-31 鍒犻櫎锛岀暀鐥曞涓嬶紙涓嶆槸鏈変汉鍒犳柇瑷�鎹㈢豢锛夆攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+        // 鈹� 鏇剧粡鏂█锛歝heck("璇烽獙鍗曟彁浜� Layer 2 涓庢秷璐逛簨浠跺叡浜嬪姟",                        鈹�
+        // 鈹�             annotatedBusinessAction(qingyandanService,                      鈹�
+        // 鈹�                     "action = \"SUBMIT\"", "public void submit("),          鈹�
+        // 鈹�             "submit 缂哄皯 @AuditLog");                                       鈹�
+        // 鈹�   鍗筹細LimsQingyandanServiceImpl.submit() 蹇呴』甯� @AuditLog(action="SUBMIT")銆傗攤
+        // 鈹�                                                                            鈹�
+        // 鈹� 涓轰粈涔堣鍙栦唬锛歴pec D7 宸叉妸涓ゅ SUBMIT 鍩嬬偣鏁村潡鍒犻櫎鈥斺�斿鎴峰叧娉ㄧ殑鏄粬鑷繁瑙﹀彂   鈹�
+        // 鈹�   鐨勫姩浣滐紙琛ㄥ崟鐨勬柊澧�/淇敼锛夛紝閭e凡鐢卞眰 0 璁板綍锛泂ubmit() 鏄〃鍗曚繚瀛樹箣鍚庣殑鍚庣   鈹�
+        // 鈹�   鍐呴儴缂栨帓锛屽啀璁颁竴鏉°�岃楠屽崟 鎻愪氦銆嶅彧浼氳鍒楄〃鍑虹幇鐢ㄦ埛娌″仛杩囩殑鍔ㄤ綔琛屻��         鈹�
+        // 鈹�   鎵�浠ヨ繖鏉℃柇瑷�鐨�**鍓嶆彁**锛坰ubmit 鏈夊眰 2 浜嬩欢锛変笉澶嶅瓨鍦紝涓嶆槸瑕嗙洊闈涪澶便��      鈹�
+        // 鈹�                                                                            鈹�
+        // 鈹� 璋佹帴鏇匡細tasks/lims-sign-usage-checks.sh G 缁勩�孲UBMIT 鍩嬬偣宸插垹锛堟伆濂� 0 澶勶級銆嶏紝鈹�
+        // 鈹�   鍚屼竴涓綅缃粠銆屽繀椤诲湪銆嶅�掕浆鎴愩�屽繀椤讳笉鍦ㄣ�嶏紝涓斿仛杩囧弻鍚戝彉寮傞獙璇侊紙鍔犲洖鏉ヤ篃绾級銆� 鈹�
+        // 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+        check("鍙栨牱 Layer 2 涓庢秷璐逛簨浠跺叡浜嬪姟",
+                annotatedBusinessAction(qingyandanService, "action = \"FETCH\"", "public void fetch("),
+                "fetch 缂哄皯 @AuditLog");
+        check("鎺ユ牱 Layer 2 涓庢秷璐逛簨浠跺叡浜嬪姟",
+                annotatedBusinessAction(qingyandanService, "action = \"RECEIVE\"", "public void jieyangReceive("),
+                "jieyangReceive 缂哄皯 @AuditLog");
+        check("璇烽獙鍗曟挙鍥炰娇鐢ㄧǔ瀹氱鍚嶆搷浣滅爜",
+                signOpType.contains("QINGYANDAN_CANCEL(\"qingyandan_cancel\", \"璇烽獙鍗曟挙鍥瀄")"),
+                "缂哄皯 QINGYANDAN_CANCEL 绋冲畾 code");
+        checkCancelAffectedGuard(qingyandanService);
+        checkQingyandanEntityContract(qingyandanEntity);
+        check("SIGN_USED 鍐欏叆 bizModule",
+                signService.contains(".bizModule(group.getBizModule())"),
+                "AuditEventDTO builder 鏈啓 bizModule");
+        String publishSignUsed = methodBody(signService, "private void publishSignUsedEvents(");
+        check("SIGN_USED 灏嗙鍚� note 鍐欏叆 reason",
+                publishSignUsed.contains(".reason(sign.getNote())"),
+                "SIGN_USED AuditEventDTO 鏈妸 lims_sign.note 鏄犲皠鍒� reason");
+        checkSignClaimGuard(signService);
+        check("鍏ㄩ儴绛惧悕浣跨敤鐩爣鏄惧紡澹版槑 bizModule", allTargetsDeclareBizModule(),
+                "瀛樺湪鏈0鏄� bizModule 鐨� LimsSignUsageTarget.builder() 鍧�");
+
+        System.out.println("=== SIGN_USED 鎺ョ嚎鏂█锛�" + passed + " 閫氳繃 / " + failed + " 澶辫触 ===");
+        System.exit(failed == 0 ? 0 : 1);
+    }
+
+    /**
+     * 鍙栨牱 / 鎺ユ牱浠嶈蛋鏈柟娉曪紝璇箟鏈彉锛堟垚鍔熷悗浠嶅簲鍐� SIGN_USED锛夛紝2026-07-31 <b>涓�瀛楁湭鍔�</b>銆�
+     * 璇烽獙鍗曟彁浜ゅ凡 retarget 鍒� {@link #checkQingyandanSubmitClaim}銆�
+     */
+    private static void checkConsumption(String label, String source, String opType) {
+        check(label + "鏍¢獙绛惧悕", source.contains("SignOpType." + opType), opType + " 鏈帴鍏�");
+        int operation = source.indexOf("SignOpType." + opType);
+        int record = source.indexOf("recordSignUsage", operation);
+        check(label + "鎴愬姛鍚庤褰� SIGN_USED", operation >= 0 && record > operation,
+                opType + " 鍚庢湭璋冪敤 recordSignUsage");
+    }
+
+    /**
+     * 璇烽獙鍗曟彁浜わ細D8 涔嬪悗<b>鍙仛鍘熷瓙璁ら銆佷笉鍐嶅啓 SIGN_USED</b>銆�
+     *
+     * <p>鍙栦唬鍘熸潵鐨� {@code checkConsumption("璇烽獙鍗曟彁浜�", ...)} 璋冪敤銆傞櫎璇箟澶栬繕淇帀浜嗛偅杈圭殑
+     * <b>鑼冨洿缂洪櫡</b>锛歿@code source.indexOf("recordSignUsage", operation)} 鏄粠鏌愪綅缃線鍚�
+     * <b>鍦ㄦ暣涓枃浠堕噷</b>鎵撅紝鍛戒腑鐨勫叾瀹炴槸绉佹湁鏂规硶浣撳唴閭h {@code limsSignService.recordSignUsage(...)}锛�
+     * 涓� submit 绔偣娌℃湁浠讳綍缁戝畾鍏崇郴鈥斺�旀妸 submit 閲岀殑璋冪敤鏁磋鍒犳帀锛岄偅鏉℃柇瑷�鐓ф牱缁裤��
+     * 鏈柟娉曟妸鎼滅储鑼冨洿<b>澶瑰湪 submit 绔偣鏂规硶浣撳唴</b>锛屾敼鍧� submit 鎵嶄細绾€��
+     *
+     * <p>瀹堢殑鏄�<b>璁ら</b>涓嶆槸浜嬩欢锛氳棰嗘槸銆岀鍚嶅崟娆℃秷璐广�嶇殑闃查噸鏀炬姢鏍忥紙ed4d908锛夛紝
+     * 鍒犳帀绛変簬鍚屼竴涓鍚嶅彲浠ュ弽澶嶄娇鐢ㄣ�侱8 鍋滅殑鍙槸浜嬩欢銆�
+     *
+     * <p>鈿狅笍 鍙栨牱锛坽@code QUYANG_FETCH}锛�/ 鎺ユ牱锛坽@code JIEYANG_RECEIVE}锛夎涔夋湭鍙橈紝
+     * 浠嶈蛋 {@link #checkConsumption} 涓斾竴瀛楁湭鍔ㄣ�傚畠浠部鐢ㄧ殑鏁存枃浠� indexOf 鏈夊悓鏍风殑鑼冨洿缂洪櫡锛�
+     * 浣嗘湰鏈熶笉鍦ㄨ寖鍥村唴锛屽凡璁板叆 Task 8 鎶ュ憡寰呭鐞嗏�斺�斾笉鍦ㄨ繖閲岄『鎵嬫敼锛屾槸涓轰簡璁�
+     * 銆屽彇鏍� / 鎺ユ牱娌¤鏀瑰埌銆嶅湪 diff 涓婁竴鐪煎彲瑙併��
+     */
+    private static void checkQingyandanSubmitClaim(String source) {
+        String block = methodBody(source, "public R<String> qingyandanSubmit(");
+        // 鍒ゆ嵁鍐欐垚**瀹屾暣璋冪敤**鑰屼笉鏄厜涓�涓� SignOpType.QINGYANDAN_SUBMIT锛�
+        // 璁ら閭h涔熷甫鐫�鍚屼竴涓灇涓惧悕锛屽彧鏌ユ灇涓惧悕鐨勮瘽锛屾妸鏍¢獙鏀规垚浼� null 鐓ф牱缁匡紙鍙樺紓 N3 瀹炴祴锛夈��
+        int verify = block.indexOf(
+                "requireQingyandanSign(qingyandan, formData, SignOpType.QINGYANDAN_SUBMIT)");
+        int business = block.indexOf("limsQingyandanService.submit(", verify);
+        int claim = block.indexOf("claimQingyandanSignUsageOnly(", business);
+        check("璇烽獙鍗曟彁浜ゆ寜 QINGYANDAN_SUBMIT 鏍¢獙绛惧悕", verify >= 0,
+                "submit 绔偣鏈寜 QINGYANDAN_SUBMIT 鏍¢獙绛惧悕");
+        // 椤哄簭涔熼拤姝伙細璁ら蹇呴』鍦ㄤ笟鍔″啓搴�**涔嬪悗**銆傛彁鍓嶈棰� = 涓氬姟澶辫触鏃剁鍚嶅凡琚秷璐癸紝
+        // 姝f槸 ed4d908 淇帀鐨勯偅涓� bug銆�
+        check("璇烽獙鍗曟彁浜ゆ牎楠岀鍚嶅悗鎵ц銆佷笟鍔℃垚鍔熷悗鎵嶅師瀛愯棰嗭紙涓嶅啀鍐� SIGN_USED锛�",
+                verify >= 0 && business > verify && claim > business,
+                "submit 绔偣浣撳唴鏈湪涓氬姟鎴愬姛鍚庤皟鐢� claimQingyandanSignUsageOnly"
+                        + " 鈥斺�� 闃查噸鏀炬姢鏍忎涪澶辨垨椤哄簭棰犲�掞紙ed4d908 鍥炲綊锛�");
+        check("璇烽獙鍗曟彁浜や笉鍐嶈蛋鍙戜簨浠惰矾寰�",
+                !block.isEmpty()
+                        && !block.contains("recordQingyandanSignUsage(")
+                        && !block.contains("limsSignService.recordSignUsage("),
+                "submit 绔偣浠嶅啓 SIGN_USED锛屼笌灞� 0 鎹鍚嶅瓧娈佃ˉ鍐欑殑绛惧悕浜嬩欢閲嶅锛圖8锛�");
+    }
+
+    /** 鎸夋柟娉曠鍚嶅す鍑烘柟娉曚綋锛堝埌鍚岀缉杩涚殑鏀跺熬澶ф嫭鍙凤級锛涘彇涓嶅埌鏃惰繑鍥炵┖涓诧紝璁╂柇瑷�鎸夌孩澶勭悊銆� */
+    private static String methodBody(String source, String signature) {
+        int start = source.indexOf(signature);
+        if (start < 0) {
+            return "";
+        }
+        int end = source.indexOf("\n    }", start);
+        return end > start ? source.substring(start, end) : "";
+    }
+
+    private static void checkCancelConsumption(String source) {
+        int cancelCase = source.indexOf("case \"cancel\":");
+        int operation = source.indexOf("SignOpType.QINGYANDAN_CANCEL", cancelCase);
+        int cancel = source.indexOf("limsQingyandanService.cancel", operation);
+        int record = source.indexOf("recordQingyandanSignUsage", cancel);
+        check("璇烽獙鍗曟挙鍥炰粠璇锋眰璇诲彇绛惧悕",
+                source.contains("params.get(\"biz_sign\")") && source.contains("params.get(\"id\")"),
+                "鎾ゅ洖鏈鍙� biz_sign 鎴栧吋瀹� id");
+        check("璇烽獙鍗曟挙鍥炴牎楠岀鍚嶅悗鎵ц涓旀垚鍔熷悗璁板綍 SIGN_USED",
+                cancelCase >= 0 && operation > cancelCase && cancel > operation && record > cancel,
+                "鎾ゅ洖鐨勭鍚嶆牎楠屻�佷笟鍔℃洿鏂般�丼IGN_USED 椤哄簭閿欒");
+    }
+
+    private static void checkQingyandanFreshSign(String source) {
+        int start = source.indexOf("private LimsSignEntity requireQingyandanSign(");
+        int end = source.indexOf("private LimsSignEntity requireQingyandanActionSign(", start);
+        String block = start >= 0 && end > start ? source.substring(start, end) : "";
+        check("璇烽獙鍗曟彁浜ゅ拰缂栬緫蹇呴』鎼哄甫鏈绛惧悕",
+                block.contains("request.getBizSign()") && !block.contains("persisted.getBizSign()"),
+                "璇锋眰缂虹鍚嶆椂浠嶄細澶嶇敤涓氬姟琛ㄤ腑鐨勫巻鍙� biz_sign");
+    }
+
+    private static void checkHuanjingFailureBoundary(String source,
+                                                      String method,
+                                                      String businessCall,
+                                                      String opType) {
+        int methodAt = source.indexOf(method);
+        int nextMethod = source.indexOf("\n    public ", methodAt + method.length());
+        String block = methodAt >= 0
+                ? source.substring(methodAt, nextMethod > methodAt ? nextMethod : source.length())
+                : "";
+        int validation = block.indexOf("limsSignService.requireValidSign(");
+        int catchAt = block.indexOf("catch (DataException e)", validation);
+        int catchClose = block.indexOf("\n        }", catchAt);
+        int business = block.indexOf(businessCall, catchClose);
+        int record = block.indexOf("recordHuanjingSignUsage", business);
+        check("鐜鐩戞祴 " + opType + " 鍙崟鑾风鍚嶆牎楠屽紓甯�",
+                validation >= 0 && catchAt > validation && catchClose > catchAt
+                        && business > catchClose && record > business,
+                "涓氬姟鏇存柊鎴� SIGN_USED 浠嶅湪 DataException 鎹曡幏鑼冨洿鍐�");
+    }
+
+    private static void checkCancelAffectedGuard(String source) {
+        int start = source.indexOf("public boolean cancel(");
+        int end = source.indexOf("\n    @Override", start);
+        String block = start >= 0 && end > start ? source.substring(start, end) : "";
+        int conditionalUpdate = block.indexOf("update.eq(\"biz_status\", BizStatus.CONFIRMED.getCode())");
+        int zeroReturn = block.indexOf("return false;");
+        int softDelete = block.indexOf("softDeleteJianyanXiangRecords");
+        check("璇烽獙鍗曟挙鍥炴寜 confirmed 鐘舵�佸師瀛愭洿鏂�",
+                block.contains("activeRowUpdate(qingyandan.getId())") && conditionalUpdate >= 0,
+                "cancel 缂哄皯鐘舵�佹垨鏈垹闄ゆ潯浠�");
+        check("affected=0 涓嶆墽琛屾挙鍥炲壇浣滅敤",
+                zeroReturn > conditionalUpdate && softDelete > zeroReturn,
+                "cancel 鍦ㄧ‘璁ゆ洿鏂板懡涓墠鎵ц浜嗚蒋鍒犻櫎");
+        check("affected=0 涓嶇敓鎴愪笟鍔′簨浠�",
+                source.substring(Math.max(0, source.lastIndexOf("@AuditLog(", start)), start)
+                        .contains("#result ? #qingyandan.jianyanLiushuihao : null"),
+                "cancel 鐨� @AuditLog 鏈寜杩斿洖鍊艰烦杩� 0 琛屼簨浠�");
+    }
+
+    private static void checkQingyandanEntityContract(String entity) {
+        String[] fields = {"qingyan_fenlei", "zhouqi", "zhouqi_danwei"};
+        for (String field : fields) {
+            check("瀹炰綋鍖呭惈璇烽獙瀛楁 " + field,
+                    entity.contains("@TableField(\"" + field + "\")"),
+                    field + " 鐨勫疄浣撳绾︿笉瀹屾暣");
+        }
+    }
+
+    private static void checkSignClaimGuard(String source) {
+        int requireStart = source.indexOf("public LimsSignEntity requireValidSign(");
+        int recordStart = source.indexOf("public void recordSignUsage(", requireStart);
+        String requireBlock = requireStart >= 0 && recordStart > requireStart
+                ? source.substring(requireStart, recordStart) : "";
+        int claim = source.indexOf("claimSignUsage(sign, opType, usageJson)", recordStart);
+        int event = source.indexOf("auditClientProvider.ifAvailable", recordStart);
+        check("绛惧悕鏍¢獙涓嶆彁鍓嶅啓搴撹棰�",
+                !requireBlock.contains("updateById(") && !requireBlock.contains("this.update("),
+                "requireValidSign 浠嶄細鍦ㄤ笟鍔℃垚鍔熷墠鍐欑鍚�");
+        check("宸叉秷璐圭鍚嶄笉鍙法涓氬姟鍛ㄦ湡澶嶇敤",
+                requireBlock.contains("sign.getExtraJson()")
+                        && requireBlock.contains("鐢靛瓙绛惧悕宸茶浣跨敤锛岃閲嶆柊绛惧悕")
+                        && source.contains("w.isNull(\"extra_json\").or().eq(\"extra_json\", \"\")")
+                        && !source.contains(".or().eq(\"extra_json\", usageJson)"),
+                "extra_json 宸叉湁鍊兼椂浠嶅彲鑳藉啀娆℃秷璐瑰悓涓�绛惧悕");
+        check("绛惧悕鎴愬姛鍚庡厛鍘熷瓙璁ら鍐嶈褰曚簨浠�",
+                claim > recordStart && event > claim
+                        && source.contains("w.isNull(\"op_type\")")
+                        && source.contains("w.isNull(\"data_id\")")
+                        && source.contains("w.isNull(\"extra_json\")"),
+                "SIGN_USED 缂哄皯璁ら鍐茬獊鏉′欢鎴栦簨浠堕『搴忛敊璇�");
+        check("绛惧悕璁ら澶辫触鏍囪浜嬪姟鍥炴粴",
+                source.contains("currentTransactionStatus().setRollbackOnly()")
+                        && source.contains("throw signUsageFailure("),
+                "controller 鎹曡幏 DataException 鏃朵簨鍔″彲鑳界户缁彁浜�");
+    }
+
+    private static boolean annotatedBusinessAction(String source, String action, String method) {
+        int methodAt = source.indexOf(method);
+        int actionAt = source.lastIndexOf(action, methodAt);
+        int annotationAt = source.lastIndexOf("@AuditLog(", methodAt);
+        return methodAt >= 0 && annotationAt >= 0 && actionAt > annotationAt
+                && methodAt - annotationAt < 600;
+    }
+
+    private static boolean allTargetsDeclareBizModule() throws IOException {
+        Path root = Paths.get("jnpf-lims");
+        try (Stream<Path> files = Files.walk(root)) {
+            return files.filter(path -> path.toString().contains("/src/main/java/")
+                            && path.toString().endsWith(".java"))
+                    .allMatch(LimsSignUsageWiringProbe::targetBlocksDeclareBizModule);
+        }
+    }
+
+    private static boolean targetBlocksDeclareBizModule(Path path) {
+        try {
+            String source = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+            int from = 0;
+            while (true) {
+                int builder = source.indexOf("LimsSignUsageTarget.builder()", from);
+                if (builder < 0) {
+                    return true;
+                }
+                int build = source.indexOf(".build()", builder);
+                if (build < 0 || !source.substring(builder, build).contains(".bizModule(")) {
+                    return false;
+                }
+                from = build + 8;
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("璇诲彇婧愮爜澶辫触: " + path, e);
+        }
+    }
+
+    private static String read(String path) throws IOException {
+        return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
+    }
+
+    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 LimsSignUsageWiringProbe() {
+    }
+}

--
Gitblit v1.8.0