From 0a9c9bf4fa908d9d0bbdd910b70fcd0650e0df1d Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期一, 06 四月 2026 22:46:30 +0800
Subject: [PATCH] #出库输送执行

---
 src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java |  151 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/main/resources/application.yml                                            |    9 +++
 2 files changed, 158 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java b/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
index f72ce6a..b86e1f5 100644
--- a/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
+++ b/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
@@ -21,6 +21,7 @@
 import com.zy.core.model.command.StationCommand;
 import com.zy.core.model.protocol.StationProtocol;
 import com.zy.core.move.StationMoveCoordinator;
+import com.zy.core.move.StationMoveSession;
 import com.zy.core.thread.StationThread;
 import com.zy.core.utils.station.model.DispatchLimitConfig;
 import com.zy.core.utils.station.model.LoadGuardState;
@@ -28,6 +29,7 @@
 import com.zy.core.utils.station.model.OutOrderDispatchDecision;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.beans.factory.annotation.Value;
 
 import java.util.Date;
 import java.util.List;
@@ -38,6 +40,9 @@
 public class StationOutboundDispatchProcessor {
 
     private static final int PENDING_DISPATCH_EXPIRE_SECONDS = 60 * 10;
+    private static final long DEFAULT_PENDING_DISCOVER_TIMEOUT_MS = 60_000L;
+    private static final long DEFAULT_PENDING_SESSION_PROTECT_MS = 90_000L;
+    private static final long DEFAULT_RECENT_DISPATCH_PROTECT_MS = 60_000L;
 
     @Autowired
     private WrkMastService wrkMastService;
@@ -57,6 +62,15 @@
     private StationDispatchLoadSupport stationDispatchLoadSupport;
     @Autowired
     private StationOutboundDecisionSupport stationOutboundDecisionSupport;
+    @Autowired
+    private StationDispatchRuntimeStateSupport stationDispatchRuntimeStateSupport;
+
+    @Value("${station.outbound.pending-discover-timeout-seconds:60}")
+    private long pendingDiscoverTimeoutSeconds;
+    @Value("${station.outbound.pending-session-protect-seconds:90}")
+    private long pendingSessionProtectSeconds;
+    @Value("${station.outbound.recent-dispatch-protect-seconds:60}")
+    private long recentDispatchProtectSeconds;
 
     public void crnStationOutExecute() {
         try {
@@ -119,11 +133,22 @@
                 } catch (Exception ignore) {
                     createdAt = System.currentTimeMillis();
                 }
-                if (System.currentTimeMillis() - createdAt < 15_000L) {
+                long pendingDiscoverTimeoutMs = resolvePendingDiscoverTimeoutMs();
+                if (System.currentTimeMillis() - createdAt < pendingDiscoverTimeoutMs) {
+                    return;
+                }
+
+                PendingDispatchGuardResult guardResult = evaluatePendingDispatchGuard(wrkMast, pendingStationObjModel, pendingStationThread);
+                if (guardResult.keepPending()) {
+                    News.info("杈撻�佺珯鐐圭瓑寰呮簮绔欐墽琛岃秴鏃跺悗缁х画淇濇寔绛夊緟銆傚伐浣滃彿={}锛屾簮绔�={}锛屽師鍥�={}",
+                            wrkMast.getWrkNo(),
+                            pendingStationObjModel.getStationId(),
+                            guardResult.reason());
+                    markPendingDispatch(wrkMast.getWrkNo());
                     return;
                 }
                 clearPendingDispatch(wrkMast.getWrkNo());
-                News.warn("杈撻�佺珯鐐规墽琛岃秴鏃讹紝宸查噴鏀鹃噸璇曡祫鏍笺�傚伐浣滃彿={}", wrkMast.getWrkNo());
+                News.warn("杈撻�佺珯鐐规墽琛岀‘璁よ秴鏃讹紝涓旀湭鍙戠幇鏈夋晥娲诲姩閾捐矾锛屽凡閲婃斁閲嶈瘯璧勬牸銆傚伐浣滃彿={}", wrkMast.getWrkNo());
             }
 
             StationObjModel stationObjModel = getOutboundSourceStation(wrkMast);
@@ -330,4 +355,126 @@
         }
         redisUtil.del(RedisKeyType.STATION_OUT_PENDING_DISPATCH_.key + wrkNo);
     }
+
+    private PendingDispatchGuardResult evaluatePendingDispatchGuard(WrkMast wrkMast,
+                                                                    StationObjModel stationObjModel,
+                                                                    StationThread stationThread) {
+        if (wrkMast == null || wrkMast.getWrkNo() == null || stationObjModel == null || stationObjModel.getStationId() == null) {
+            return PendingDispatchGuardResult.release("missing-runtime-context");
+        }
+        if (hasActiveMoveSession(wrkMast.getWrkNo(), stationObjModel.getStationId())) {
+            return PendingDispatchGuardResult.keep("active-session");
+        }
+        if (hasRecentSuccessfulDispatch(wrkMast.getWrkNo(), stationObjModel.getStationId())) {
+            return PendingDispatchGuardResult.keep("recent-successful-dispatch");
+        }
+        if (taskExistsInBuffer(stationThread, wrkMast.getWrkNo())) {
+            return PendingDispatchGuardResult.keep("task-buffer-detected");
+        }
+        return PendingDispatchGuardResult.release("no-active-chain");
+    }
+
+    private boolean hasActiveMoveSession(Integer wrkNo, Integer sourceStationId) {
+        if (wrkNo == null || wrkNo <= 0 || stationMoveCoordinator == null || sourceStationId == null) {
+            return false;
+        }
+        StationMoveSession session = stationMoveCoordinator.loadSession(wrkNo);
+        if (session == null || !session.isActive()) {
+            return false;
+        }
+        if (!Objects.equals(sourceStationId, session.getDispatchStationId())) {
+            return false;
+        }
+        long now = System.currentTimeMillis();
+        Long lastIssuedAt = session.getLastIssuedAt();
+        if (lastIssuedAt != null && now - lastIssuedAt <= resolvePendingSessionProtectMs()) {
+            return true;
+        }
+        Long updatedAt = session.getUpdatedAt();
+        return updatedAt != null && now - updatedAt <= resolvePendingSessionProtectMs();
+    }
+
+    private boolean hasRecentSuccessfulDispatch(Integer wrkNo, Integer sourceStationId) {
+        if (stationDispatchRuntimeStateSupport == null) {
+            return false;
+        }
+        return stationDispatchRuntimeStateSupport.hasRecentIssuedMoveCommand(
+                wrkNo,
+                sourceStationId,
+                resolveRecentDispatchProtectMs()
+        );
+    }
+
+    private boolean taskExistsInBuffer(StationThread stationThread, Integer wrkNo) {
+        if (stationThread == null || wrkNo == null || wrkNo <= 0) {
+            return false;
+        }
+        Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
+        if (statusMap == null || statusMap.isEmpty()) {
+            return false;
+        }
+        for (StationProtocol stationProtocol : statusMap.values()) {
+            if (stationProtocol == null || stationProtocol.getTaskBufferItems() == null) {
+                continue;
+            }
+            if (containsTaskNo(stationProtocol, wrkNo)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean containsTaskNo(StationProtocol stationProtocol, Integer wrkNo) {
+        if (stationProtocol == null || stationProtocol.getTaskBufferItems() == null || wrkNo == null) {
+            return false;
+        }
+        return stationProtocol.getTaskBufferItems().stream()
+                .filter(Objects::nonNull)
+                .anyMatch(item -> Objects.equals(wrkNo, item.getTaskNo()));
+    }
+
+    private long resolvePendingDiscoverTimeoutMs() {
+        return resolveMillis(pendingDiscoverTimeoutSeconds, DEFAULT_PENDING_DISCOVER_TIMEOUT_MS);
+    }
+
+    private long resolvePendingSessionProtectMs() {
+        return resolveMillis(pendingSessionProtectSeconds, DEFAULT_PENDING_SESSION_PROTECT_MS);
+    }
+
+    private long resolveRecentDispatchProtectMs() {
+        return resolveMillis(recentDispatchProtectSeconds, DEFAULT_RECENT_DISPATCH_PROTECT_MS);
+    }
+
+    private long resolveMillis(long seconds, long defaultMs) {
+        if (seconds <= 0L) {
+            return defaultMs;
+        }
+        return seconds * 1000L;
+    }
+
+    private static class PendingDispatchGuardResult {
+        private final boolean keepPending;
+        private final String reason;
+
+        private PendingDispatchGuardResult(boolean keepPending, String reason) {
+            this.keepPending = keepPending;
+            this.reason = reason;
+        }
+
+        public static PendingDispatchGuardResult keep(String reason) {
+            return new PendingDispatchGuardResult(true, reason);
+        }
+
+        public static PendingDispatchGuardResult release(String reason) {
+            return new PendingDispatchGuardResult(false, reason);
+        }
+
+        public boolean keepPending() {
+            return keepPending;
+        }
+
+        public String reason() {
+            return reason;
+        }
+    }
 }
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index a3e0365..a8cf2d4 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -147,3 +147,12 @@
     enabled: false
     thresholdMs: 50
     sampleRate: 1.0
+
+station:
+  outbound:
+    # 鍑哄簱鍛戒护鍏ヨ澶囨墽琛岄摼璺悗锛岀瓑寰呰澶囩姸鎬佸洖鏄句换鍔″彿鐨勮秴鏃舵椂闂�
+    pending-discover-timeout-seconds: 60
+    # 宸插瓨鍦ㄦ椿鍔ㄨ矾鐢变細璇濇椂锛岀户缁繚鎸� pending 鐨勪繚鎶ゆ椂闂�
+    pending-session-protect-seconds: 90
+    # 鏈�杩戝凡缁忔垚鍔熷啓杩囧悓婧愮珯 MOVE 鍛戒护鏃讹紝缁х画淇濇寔 pending 鐨勪繚鎶ゆ椂闂�
+    recent-dispatch-protect-seconds: 60

--
Gitblit v1.9.1