From 1a0bdd8df58435ec37e9d8345e67cd092902b5e4 Mon Sep 17 00:00:00 2001
From: cl <1442464845@qq.com>
Date: 星期六, 04 四月 2026 00:27:18 +0800
Subject: [PATCH] 序号控制

---
 src/main/java/com/zy/api/controller/WcsApiController.java               |    4 
 src/main/java/com/zy/asrs/service/WrkMastService.java                   |    5 
 src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java            |    7 
 src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java |   48 ++++
 src/main/java/com/zy/asrs/controller/OpenController.java                |   40 +++
 src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java            |  411 ++++++++++++++++++++++++++++++++++++-----
 src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java          |   32 +++
 src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java                  |    7 
 src/main/java/com/zy/asrs/mapper/WrkMastMapper.java                     |    6 
 src/main/java/com/zy/asrs/task/WorkMastScheduler.java                   |   14 
 src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java             |    7 
 src/main/java/com/zy/api/service/WcsApiService.java                     |    9 
 12 files changed, 516 insertions(+), 74 deletions(-)

diff --git a/src/main/java/com/zy/api/controller/WcsApiController.java b/src/main/java/com/zy/api/controller/WcsApiController.java
index 0c86a2d..68997fc 100644
--- a/src/main/java/com/zy/api/controller/WcsApiController.java
+++ b/src/main/java/com/zy/api/controller/WcsApiController.java
@@ -15,6 +15,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.List;
 import java.util.Objects;
 
@@ -48,10 +49,11 @@
 //    @ManagerAuth
     @ApiOperation("璁惧鎵ц鐘舵�佸洖鍐�-wcs浠诲姟瀹屾垚鍥炲啓")
     @PostMapping("/openapi/report")
-    public R receviceTaskFromWcs(@RequestBody ReceviceTaskParams params) {
+    public R receviceTaskFromWcs(@RequestBody ReceviceTaskParams params, HttpServletRequest request) {
         if (Objects.isNull(params)) {
             return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
         }
+        request.setAttribute("cache", params);
         return wcsApiService.receviceTaskFromWcs(params);
 
     }
diff --git a/src/main/java/com/zy/api/service/WcsApiService.java b/src/main/java/com/zy/api/service/WcsApiService.java
index a820563..6a2d08c 100644
--- a/src/main/java/com/zy/api/service/WcsApiService.java
+++ b/src/main/java/com/zy/api/service/WcsApiService.java
@@ -44,7 +44,14 @@
      * @date 2026/3/21 11:30
      * @return com.core.common.R
      */
-    R syncDeviceStatusFromWcs();
+    default R syncDeviceStatusFromWcs() {
+        return syncDeviceStatusFromWcs(true);
+    }
+
+    /**
+     * @param logOnFailure false 鏃朵笉杈撳嚭 ERROR 绾уけ璐ユ棩蹇楋紙渚涘畾鏃朵换鍔¢檷鍣級
+     */
+    R syncDeviceStatusFromWcs(boolean logOnFailure);
 
     /**
      * batch pause out tasks
diff --git a/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java b/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
index 3c89459..5d658fb 100644
--- a/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
+++ b/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
@@ -1,6 +1,7 @@
 package com.zy.api.service.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.mapper.EntityWrapper;
 import com.core.common.Cools;
@@ -38,10 +39,18 @@
     private static final String YES = "Y";
     private static final String NO = "N";
 
+    /** 鍚屼竴 WCS 璺緞銆佸悓涓�鍗曞彿涓嬩竴缁勪笅鍙戠殑浠诲姟鏉℃暟涓婇檺 */
+    private static final int WCS_PUB_BATCH_SIZE = 20;
+
+    /** 涓夋柟鎺ュ彛缁熻锛氭湰绯荤粺璋冪敤 WCS 鐨� namespace 绾﹀畾 */
+    private static final String NS_WMS_TO_WCS = "鏈郴缁熻姹俉CS";
+
     @Autowired
     private LocMastService locMastService;
     @Autowired
     private WrkMastService wrkMastService;
+    @Autowired
+    private WrkMastLogService wrkMastLogService;
     @Autowired
     private WorkService workService;
     @Autowired
@@ -69,6 +78,8 @@
     private String stopOutTask;
     @Value("${wcs.address.getDeviceStatus:/openapi/getDeviceStatus}")
     private String getDeviceStatus;
+    @Value("${wcs.address.queryTask:/openapi/queryTask}")
+    private String queryTaskPath;
     @Value("${wcs.status-sync.method:GET}")
     private String deviceStatusMethod;
     @Autowired
@@ -77,6 +88,8 @@
     private BasDevpService basDevpService;
     @Autowired
     private BasCrnpService basCrnpService;
+    @Autowired
+    private ApiLogService apiLogService;
 
 
     /**
@@ -98,8 +111,11 @@
             return R.error(validateMsg);
         }
         String url = resolveTaskPath(params);
-        String response;
+        String requestJson = JSON.toJSONString(params);
+        String response = null;
         R r = R.ok();
+        Throwable wcsThrown = null;
+        boolean wcsBizOk = false;
         try {
             log.info("涓嬪彂鎼繍浠诲姟缁檞cs="+JSON.toJSONString(params));
             response = new HttpHandler.Builder()
@@ -107,12 +123,13 @@
                     .setPath(url)
                     .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
                     .setTimeout(10, TimeUnit.SECONDS)
-                    .setJson(JSON.toJSONString(params))
+                    .setJson(requestJson)
                     .build()
                     .doPost();
             JSONObject jsonObject = JSON.parseObject(response);
             log.info("涓嬪彂浠诲姟缁檞cs鐨勮繑鍥炲��="+response);
             Integer code = jsonObject.getInteger("code");
+            wcsBizOk = code != null && code == 200;
 
             if (code==200) {
                 updateWrkMastAfterPublish(wrkMast);
@@ -121,7 +138,10 @@
                 r =R.error();
             }
         } catch (IOException e) {
+            wcsThrown = e;
             throw new RuntimeException(e);
+        } finally {
+            logWcsToApiLog(url, requestJson, response, wcsThrown, wcsBizOk);
         }
         return r;
     }
@@ -132,9 +152,8 @@
             return R.error("浠诲姟涓嶈兘涓虹┖锛侊紒");
         }
 
-        // 鍏堜竴娆℃�ф妸鏈壒浠诲姟瀵瑰簲鐨勫伐浣滄。鎹炲嚭鏉ワ紝閬垮厤寰幆鍐呴噸澶嶆煡搴撱��
         Map<String, WrkMast> wrkMastMap = getWrkMastMap(paramsList);
-        Map<String, List<WorkTaskParams>> groupedTasks = new LinkedHashMap<>();
+        List<WorkTaskParams> accepted = new ArrayList<>();
         List<String> skipMsgs = new ArrayList<>();
 
         for (WorkTaskParams params : paramsList) {
@@ -148,58 +167,64 @@
                 skipMsgs.add(buildTaskMsg(params, validateMsg));
                 continue;
             }
-
-            // 鍒嗙粍涓婚敭 = 鎺ュ彛璺緞 + userNo銆�
-            // 杩欐牱鏃㈣兘淇濊瘉鍏ュ簱/鍑哄簱/绉诲簱涓嶄細娣峰彂锛屼篃鑳戒繚璇佺浉鍚� userNo 鐨勪换鍔′細鎵撳寘鍒板悓涓�娆� WCS 璇锋眰涓��
-            String groupKey = buildBatchGroupKey(params, wrkMast);
-            groupedTasks.computeIfAbsent(groupKey, key -> new ArrayList<>()).add(params);
+            accepted.add(params);
         }
 
-        if (groupedTasks.isEmpty()) {
+        if (accepted.isEmpty()) {
             return R.error(skipMsgs.isEmpty() ? "鏃犲彲涓嬪彂浠诲姟" : skipMsgs.get(0)).add(skipMsgs);
         }
 
+        accepted = filterOutboundByContiguousPlt(accepted, wrkMastMap, skipMsgs);
+        if (accepted.isEmpty()) {
+            return R.error(skipMsgs.isEmpty() ? "鏃犲彲涓嬪彂浠诲姟" : skipMsgs.get(0)).add(skipMsgs);
+        }
+
+        accepted.sort(pubWcsSortComparator(wrkMastMap));
+        List<List<WorkTaskParams>> chunks = buildPubChunks(accepted, wrkMastMap);
+
         int successCount = 0;
         List<String> failMsgs = new ArrayList<>();
-        for (List<WorkTaskParams> group : groupedTasks.values()) {
-            if (group == null || group.isEmpty()) {
+        List<WorkTaskParams> lastSentChunk = null;
+        String skipGroupKey = null;
+
+        for (List<WorkTaskParams> chunk : chunks) {
+            if (chunk == null || chunk.isEmpty()) {
+                continue;
+            }
+            WorkTaskParams head = chunk.get(0);
+            WrkMast headMast = wrkMastMap.get(head.getTaskNo());
+            String key = buildBatchGroupKey(head, headMast);
+
+            if (skipGroupKey != null && skipGroupKey.equals(key)) {
                 continue;
             }
 
-            // 鍚屼竴缁勫唴鐨勪换鍔$被鍨嬩竴鑷达紝鍥犳鍙栫涓�鏉″嵆鍙‘瀹氭湰缁勫簲璇ヨ皟鐢ㄥ摢涓� WCS 鎺ュ彛銆�
-            String path = resolveTaskPath(group.get(0));
-            Map<String, Object> payload = new HashMap<>();
-            // WCS 鎵归噺涓嬪彂鎶ユ枃缁熶竴浣跨敤 {"tasks":[...]} 缁撴瀯銆�
-            payload.put("taskList", buildTaskPayloads(group));
-            String response = null;
-            try {
-                log.info("鎵归噺涓嬪彂鎼繍浠诲姟缁檞cs={}", JSON.toJSONString(payload));
-                response = new HttpHandler.Builder()
-                        .setUri(wcs_address)
-                        .setPath(path)
-//                        .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
-                        .setTimeout(10, TimeUnit.SECONDS)
-                        .setJson(JSON.toJSONString(payload))
-                        .build()
-                        .doPost();
-                JSONObject jsonObject = JSON.parseObject(response == null ? "{}" : response);
-                log.info("鎵归噺涓嬪彂浠诲姟缁檞cs鐨勮繑鍥炲��={}", response);
-                Integer code = jsonObject.getInteger("code");
-                if (code != null && code == 200) {
-                    successCount += group.size();
-                    // 鍙湁鏁寸粍涓嬪彂鎴愬姛锛屾墠鍥炲啓鏈湴宸ヤ綔妗g姸鎬侊紝閬垮厤 WMS/WCS 鐘舵�佸垎鍙夈��
-                    for (WorkTaskParams params : group) {
-                        updateWrkMastAfterPublish(wrkMastMap.get(params.getTaskNo()));
-                    }
-                } else {
-                    String msg = jsonObject.getString("msg");
-                    failMsgs.add("path=" + path + ", msg=" + (Cools.isEmpty(msg) ? "WCS涓嬪彂浠诲姟澶辫触" : msg));
-                    log.error("鎵归噺涓嬪彂浠诲姟缁檞cs澶辫触, path:{}, request:{}, response:{}", path, JSON.toJSONString(payload), response);
-                }
-            } catch (IOException e) {
-                failMsgs.add("path=" + path + ", msg=" + e.getMessage());
-                log.error("鎵归噺涓嬪彂浠诲姟缁檞cs寮傚父, path:{}, request:{}, response:{}", path, JSON.toJSONString(payload), response, e);
+            if (!outboundChunkPredecessorPltReady(chunk, wrkMastMap)) {
+                skipGroupKey = key;
+                continue;
             }
+
+            if (lastSentChunk != null) {
+                WorkTaskParams lastHead = lastSentChunk.get(0);
+                String lastKey = buildBatchGroupKey(lastHead, wrkMastMap.get(lastHead.getTaskNo()));
+                if (lastKey.equals(key)) {
+                    if (!sameOrderNextChunkAllowed(lastSentChunk)) {
+                        skipGroupKey = key;
+                        continue;
+                    }
+                }
+                if (!sleepOneMinuteBeforeNextChunk()) {
+                    break;
+                }
+            }
+
+            int ok = postWcsBatchChunk(chunk, wrkMastMap, failMsgs);
+            if (ok <= 0) {
+                skipGroupKey = key;
+                continue;
+            }
+            successCount += ok;
+            lastSentChunk = chunk;
         }
 
         Map<String, Object> result = new HashMap<>();
@@ -218,6 +243,268 @@
             return R.error(msg).add(result);
         }
         return R.ok(failMsgs.isEmpty() && skipMsgs.isEmpty() ? "鎿嶄綔鎴愬姛" : "閮ㄥ垎浠诲姟涓嬪彂鎴愬姛").add(result);
+    }
+
+    private int postWcsBatchChunk(List<WorkTaskParams> chunk, Map<String, WrkMast> wrkMastMap, List<String> failMsgs) {
+        if (chunk == null || chunk.isEmpty()) {
+            return 0;
+        }
+        String path = resolveTaskPath(chunk.get(0));
+        Map<String, Object> payload = new HashMap<>();
+        payload.put("taskList", buildTaskPayloads(chunk));
+        String requestJson = JSON.toJSONString(payload);
+        String response = null;
+        Throwable wcsThrown = null;
+        boolean wcsBizOk = false;
+        try {
+            log.info("鎵归噺涓嬪彂鎼繍浠诲姟缁檞cs={}", requestJson);
+            response = new HttpHandler.Builder()
+                    .setUri(wcs_address)
+                    .setPath(path)
+                    .setTimeout(60, TimeUnit.SECONDS)
+                    .setJson(requestJson)
+                    .build()
+                    .doPost();
+            JSONObject jsonObject = JSON.parseObject(response == null ? "{}" : response);
+            log.info("鎵归噺涓嬪彂浠诲姟缁檞cs鐨勮繑鍥炲��={}", response);
+            Integer code = jsonObject.getInteger("code");
+            wcsBizOk = code != null && code == 200;
+            if (wcsBizOk) {
+                for (WorkTaskParams params : chunk) {
+                    updateWrkMastAfterPublish(wrkMastMap.get(params.getTaskNo()));
+                }
+                return chunk.size();
+            }
+            String msg = jsonObject.getString("msg");
+            failMsgs.add("path=" + path + ", msg=" + (Cools.isEmpty(msg) ? "WCS涓嬪彂浠诲姟澶辫触" : msg));
+            log.error("鎵归噺涓嬪彂浠诲姟缁檞cs澶辫触, path:{}, request:{}, response:{}", path, requestJson, response);
+        } catch (IOException e) {
+            wcsThrown = e;
+            failMsgs.add("path=" + path + ", msg=" + e.getMessage());
+            log.error("鎵归噺涓嬪彂浠诲姟缁檞cs寮傚父, path:{}, request:{}, response:{}", path, requestJson, response, e);
+        } finally {
+            logWcsToApiLog(path, requestJson, response, wcsThrown, wcsBizOk);
+        }
+        return 0;
+    }
+
+    /**
+     * 鍑哄簱锛氫粎褰撳崟鍙枫�佸簭鍙峰潎鏈夋晥鏃跺仛璺冲彿鏍¢獙锛涘崟鍙风┖鎴栧簭鍙锋棤鏁堜粛涓嬪彂銆傚叆搴�/绉诲簱涓嶅鐞嗐��
+     */
+    private List<WorkTaskParams> filterOutboundByContiguousPlt(List<WorkTaskParams> accepted, Map<String, WrkMast> wrkMastMap, List<String> skipMsgs) {
+        Map<String, Integer> reachCache = new HashMap<>();
+        List<WorkTaskParams> kept = new ArrayList<>();
+        for (WorkTaskParams p : accepted) {
+            if (!"out".equalsIgnoreCase(p.getType())) {
+                kept.add(p);
+                continue;
+            }
+            WrkMast w = wrkMastMap.get(p.getTaskNo());
+            String userNo = sortUserNoForPub(p, w);
+            Integer plt = sortPltForPub(p, w);
+            if (Cools.isEmpty(userNo) || plt == null || plt <= 0) {
+                kept.add(p);
+                continue;
+            }
+            int maxReach = reachCache.computeIfAbsent(userNo, wrkMastService::outboundSeqMaxContiguousPlt);
+            if (plt > maxReach) {
+                skipMsgs.add(buildTaskMsg(p, "鍑哄簱搴忓彿璺冲彿锛岃烦杩�"));
+                continue;
+            }
+            kept.add(p);
+        }
+        return kept;
+    }
+
+    private List<List<WorkTaskParams>> buildPubChunks(List<WorkTaskParams> accepted, Map<String, WrkMast> wrkMastMap) {
+        List<List<WorkTaskParams>> chunks = new ArrayList<>();
+        int index = 0;
+        while (index < accepted.size()) {
+            WorkTaskParams head = accepted.get(index);
+            WrkMast headMast = wrkMastMap.get(head.getTaskNo());
+            String headGroupKey = buildBatchGroupKey(head, headMast);
+            List<WorkTaskParams> chunk = new ArrayList<>();
+            while (index < accepted.size() && chunk.size() < WCS_PUB_BATCH_SIZE) {
+                WorkTaskParams cur = accepted.get(index);
+                WrkMast curMast = wrkMastMap.get(cur.getTaskNo());
+                if (!headGroupKey.equals(buildBatchGroupKey(cur, curMast))) {
+                    break;
+                }
+                chunk.add(cur);
+                index++;
+            }
+            chunks.add(chunk);
+        }
+        return chunks;
+    }
+
+    private boolean sleepOneMinuteBeforeNextChunk() {
+        try {
+            TimeUnit.MINUTES.sleep(1);
+            return true;
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            log.warn("鎵归噺涓嬪彂WCS缁勯棿绛夊緟琚腑鏂�", e);
+            return false;
+        }
+    }
+
+    /**
+     * 鍚屽崟涓嬩竴缁勶細浼樺厛 WCS queryTask锛涘け璐ユ垨鏃犳暟鎹垯涓昏〃宸查潪 11 鎴栧凡杩涘巻鍙茶〃銆�
+     */
+    private boolean sameOrderNextChunkAllowed(List<WorkTaskParams> lastSentChunk) {
+        if (lastSentChunk == null || lastSentChunk.isEmpty()) {
+            return false;
+        }
+        if (!Boolean.parseBoolean(String.valueOf(switchValue))) {
+            return true;
+        }
+        WorkTaskParams last = lastSentChunk.get(lastSentChunk.size() - 1);
+        if (last != null && !Cools.isEmpty(last.getTaskNo()) && wcsQueryTaskShowsTask(last.getTaskNo())) {
+            return true;
+        }
+        if (last != null && !Cools.isEmpty(last.getTaskNo())) {
+            log.info("WCS queryTask 鏃犳暟鎹垨澶辫触锛屽洖閫� WMS 涓昏〃/鍘嗗彶鏍¢獙, taskNo={}", last.getTaskNo());
+        }
+        return previousChunkTasksReleasedInWms(lastSentChunk);
+    }
+
+    private boolean wcsQueryTaskShowsTask(String taskNo) {
+        Map<String, Object> body = new HashMap<>();
+        body.put("taskNo", taskNo);
+        try {
+            String response = new HttpHandler.Builder()
+                    .setUri(wcs_address)
+                    .setPath(queryTaskPath)
+                    .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
+                    .setTimeout(60, TimeUnit.SECONDS)
+                    .setJson(JSON.toJSONString(body))
+                    .build()
+                    .doPost();
+            JSONObject jo = JSON.parseObject(response == null ? "{}" : response);
+            Integer code = jo.getInteger("code");
+            return code != null && code == 200 && queryTaskDataNonEmpty(jo.get("data"));
+        } catch (IOException e) {
+            log.warn("WCS queryTask 寮傚父, taskNo={}", taskNo, e);
+            return false;
+        }
+    }
+
+    /**
+     * 涓婁竴缁勬瘡鏉★細涓昏〃鏃犲垯鐪嬪巻鍙茶〃锛涗富琛ㄦ湁鍒� wrk_sts 涓嶈兘浠嶄负 11銆�
+     */
+    private boolean previousChunkTasksReleasedInWms(List<WorkTaskParams> chunk) {
+        for (WorkTaskParams p : chunk) {
+            if (p == null || Cools.isEmpty(p.getTaskNo())) {
+                return false;
+            }
+            Integer wrkNo = Integer.valueOf(p.getTaskNo());
+            WrkMast m = wrkMastService.selectById(wrkNo);
+            if (m == null) {
+                int logCnt = wrkMastLogService.selectCount(new EntityWrapper<WrkMastLog>().eq("wrk_no", wrkNo));
+                if (logCnt <= 0) {
+                    return false;
+                }
+            } else if (m.getWrkSts() != null && Objects.equals(m.getWrkSts(), 11L)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 鍑哄簱姣忕粍涓嬪彂鍓嶏細鏈粍鏈夋湁鏁堟渶灏忓簭鍙蜂笖&gt;1 鏃讹紝鍙牎楠屻�屾渶灏忓簭鍙�-1銆嶄竴妗o紱搴忓彿鍏ㄦ棤鍒欒烦杩囨湰鏉′欢銆�
+     */
+    private boolean outboundChunkPredecessorPltReady(List<WorkTaskParams> chunk, Map<String, WrkMast> wrkMastMap) {
+        if (chunk == null || chunk.isEmpty()) {
+            return true;
+        }
+        WorkTaskParams head = chunk.get(0);
+        if (!"out".equalsIgnoreCase(head.getType())) {
+            return true;
+        }
+        WrkMast headMast = wrkMastMap.get(head.getTaskNo());
+        String userNo = sortUserNoForPub(head, headMast);
+        if (Cools.isEmpty(userNo)) {
+            return true;
+        }
+        int minPlt = Integer.MAX_VALUE;
+        for (WorkTaskParams p : chunk) {
+            if (!"out".equalsIgnoreCase(p.getType())) {
+                continue;
+            }
+            Integer plt = sortPltForPub(p, wrkMastMap.get(p.getTaskNo()));
+            if (plt != null && plt > 0 && plt < minPlt) {
+                minPlt = plt;
+            }
+        }
+        if (minPlt == Integer.MAX_VALUE || minPlt <= 1) {
+            return true;
+        }
+        return outboundPltSlotReleasedInWms(userNo, minPlt - 1);
+    }
+
+    private boolean outboundPltSlotReleasedInWms(String userNo, int pltType) {
+        List<WrkMast> rows = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+                .eq("user_no", userNo)
+                .eq("io_type", 101)
+                .eq("plt_type", pltType));
+        if (rows != null && !rows.isEmpty()) {
+            for (WrkMast m : rows) {
+                if (m != null && m.getWrkSts() != null && Objects.equals(m.getWrkSts(), 11L)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        int logCnt = wrkMastLogService.selectCount(new EntityWrapper<WrkMastLog>()
+                .eq("user_no", userNo)
+                .eq("io_type", 101)
+                .eq("plt_type", pltType));
+        return logCnt > 0;
+    }
+
+    private static boolean queryTaskDataNonEmpty(Object data) {
+        if (data == null) {
+            return false;
+        }
+        if (data instanceof JSONArray) {
+            return !((JSONArray) data).isEmpty();
+        }
+        if (data instanceof Collection) {
+            return !((Collection<?>) data).isEmpty();
+        }
+        if (data instanceof String) {
+            String s = (String) data;
+            if (Cools.isEmpty(s)) {
+                return false;
+            }
+            JSONArray arr = JSON.parseArray(s);
+            return arr != null && !arr.isEmpty();
+        }
+        return true;
+    }
+
+    private Comparator<WorkTaskParams> pubWcsSortComparator(Map<String, WrkMast> wrkMastMap) {
+        return Comparator
+                .comparing((WorkTaskParams p) -> Optional.ofNullable(p.getType()).orElse(""), String.CASE_INSENSITIVE_ORDER)
+                .thenComparing(p -> sortUserNoForPub(p, wrkMastMap.get(p.getTaskNo())), Comparator.nullsLast(String::compareTo))
+                .thenComparing(p -> sortPltForPub(p, wrkMastMap.get(p.getTaskNo())), Comparator.nullsLast(Integer::compareTo));
+    }
+
+    private static String sortUserNoForPub(WorkTaskParams p, WrkMast wrkMast) {
+        String userNo = wrkMast == null ? null : wrkMast.getUserNo();
+        if (Cools.isEmpty(userNo)) {
+            userNo = p.getBatch();
+        }
+        return Cools.isEmpty(userNo) ? null : userNo;
+    }
+
+    private static Integer sortPltForPub(WorkTaskParams p, WrkMast wrkMast) {
+        if (wrkMast != null && wrkMast.getPltType() != null) {
+            return wrkMast.getPltType();
+        }
+        return p.getBatchSeq();
     }
 
     /**
@@ -284,7 +571,7 @@
     }
 
     @Override
-    public R syncDeviceStatusFromWcs() {
+    public R syncDeviceStatusFromWcs(boolean logOnFailure) {
         if (!Boolean.parseBoolean(String.valueOf(switchValue))) {
             return R.ok("WCS寮�鍏冲叧闂�");
         }
@@ -311,7 +598,11 @@
             log.info("鍚屾WCS璁惧鐘舵�佹垚鍔�, stationCount={}, crnCount={}", stationCount, crnCount);
             return R.ok("鍚屾鎴愬姛").add(result);
         } catch (Exception e) {
-            log.error("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+            if (logOnFailure) {
+                log.error("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+            } else {
+                log.debug("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+            }
             return R.error("鍚屾WCS璁惧鐘舵�佸け璐�: " + e.getMessage());
         }
     }
@@ -504,30 +795,48 @@
         }
         HashMap<String,Object> map = new  HashMap<>();
         map.put("taskList", params);
-        String response;
+        String requestJson = JSON.toJSONString(map);
+        String response = null;
+        Throwable wcsThrown = null;
+        boolean wcsBizOk = false;
         try {
-            log.info("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟, request={}", JSON.toJSONString(params));
+            log.info("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟, request={}", requestJson);
             response = new HttpHandler.Builder()
                     .setUri(wcs_address)
                     .setPath(stopOutTask)
 //                    .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
                     .setTimeout(10, TimeUnit.SECONDS)
-                    .setJson(JSON.toJSONString(map))
+                    .setJson(requestJson)
                     .build()
                     .doPost();
             JSONObject jsonObject = JSON.parseObject(response == null ? "{}" : response);
             log.info("WCS鍙栨秷鍑哄簱浠诲姟杩斿洖, response={}", response);
             Integer code = jsonObject.getInteger("code");
-            if (code == null || !Objects.equals(code, 200)) {
+            wcsBizOk = code != null && Objects.equals(code, 200);
+            if (!wcsBizOk) {
                 String msg = jsonObject.getString("msg");
                 throw new CoolException(Cools.isEmpty(msg) ? "WCS鍙栨秷鍑哄簱浠诲姟澶辫触" : msg);
             }
             return R.ok(Cools.isEmpty(jsonObject.getString("msg")) ? "鎿嶄綔鎴愬姛" : jsonObject.getString("msg"));
         } catch (IOException e) {
+            wcsThrown = e;
             throw new CoolException("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟澶辫触: " + e.getMessage());
+        } finally {
+            logWcsToApiLog(stopOutTask, requestJson, response, wcsThrown, wcsBizOk);
         }
     }
 
+    private void logWcsToApiLog(String path, String requestJson, String response, Throwable thrown, boolean wcsBizOk) {
+        String fullUrl = (wcs_address == null ? "" : wcs_address) + (path == null ? "" : path);
+        boolean success = thrown == null && wcsBizOk;
+        String resp = response == null ? "" : response;
+        if (thrown != null && Cools.isEmpty(resp)) {
+            resp = thrown.getMessage() == null ? "" : thrown.getMessage();
+        }
+        apiLogService.save(NS_WMS_TO_WCS, fullUrl, "-", "-",
+                requestJson == null ? "" : requestJson, resp, success);
+    }
+
     private String requestDeviceStatusFromWcs() throws IOException {
         HttpHandler.Builder builder = new HttpHandler.Builder()
                 .setUri(wcs_address)
diff --git a/src/main/java/com/zy/asrs/controller/OpenController.java b/src/main/java/com/zy/asrs/controller/OpenController.java
index ca3fb5e..14571f4 100644
--- a/src/main/java/com/zy/asrs/controller/OpenController.java
+++ b/src/main/java/com/zy/asrs/controller/OpenController.java
@@ -57,6 +57,8 @@
     private MatService matService;
 
     @Autowired
+    private LocMastService locMastService;
+    @Autowired
     private ReportQueryMapper reportQueryMapper;
 //    @PostMapping("/order/matSync/default/v1")
 ////    @AppAuth(memo = "鍟嗗搧淇℃伅鍚屾鎺ュ彛")
@@ -486,6 +488,38 @@
             }
             orderIds.add(outTaskParam.getOrderId());
         }
+
+        Map<String, List<OutTaskParam>> linesByOrder = new LinkedHashMap<>();
+        for (OutTaskParam outTaskParam : params) {
+            linesByOrder.computeIfAbsent(outTaskParam.getOrderId(), k -> new ArrayList<>()).add(outTaskParam);
+        }
+        for (Map.Entry<String, List<OutTaskParam>> entry : linesByOrder.entrySet()) {
+            String oid = entry.getKey();
+            List<OutTaskParam> lines = entry.getValue();
+            List<Integer> seqs = new ArrayList<>(lines.size());
+            for (OutTaskParam line : lines) {
+                if (line.getSeq() == null) {
+                    return R.error("鍑哄簱鍗曘��" + oid + "銆嶅簭鍙蜂笉鑳戒负绌�");
+                }
+                seqs.add(line.getSeq());
+            }
+            Collections.sort(seqs);
+            for (int i = 0; i < seqs.size(); i++) {
+                if (!String.valueOf(seqs.get(i)).equals(String.valueOf(i + 1))) {
+                    return R.error("鍑哄簱鍗曘��" + oid + "銆嶅簭鍙蜂笉杩炵画");
+                }
+            }
+        }
+
+        Set<String> seenPallet = new LinkedHashSet<>();
+        for (OutTaskParam outTaskParam : params) {
+            String pid = outTaskParam.getPalletId();
+            String palletKey = pid == null ? "" : pid;
+            if (!seenPallet.add(palletKey)) {
+                return R.error("鎵樼洏鍙烽噸澶嶏細" + (Cools.isEmpty(pid) ? "锛堢┖锛�" : pid));
+            }
+        }
+
 //        if (!orderIds.isEmpty()) {
 //            Set<String> existedOrderIds = new LinkedHashSet<>();
 //            List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("user_no", orderIds));
@@ -518,9 +552,9 @@
         }
 
         for (OutTaskParam outTaskParam : validOutOrders) {
-            R r = openService.outOrder(outTaskParam,validOutOrders.size());
-            if (!r.get("code").equals(200)){
-                return r;
+            LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_sts", "F").eq("barcode", outTaskParam.getPalletId()));
+            if (locMast == null) {
+                throw new CoolException("娌℃湁鎵惧埌鎵樼洏鐮�=" + outTaskParam.getPalletId() + "瀵瑰簲鐨勫簱浣�");
             }
         }
 
diff --git a/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java b/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java
index 27eaca7..7afe961 100644
--- a/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java
+++ b/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java
@@ -6,6 +6,7 @@
 import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import org.springframework.stereotype.Repository;
 
 import java.util.List;
@@ -19,6 +20,12 @@
     int save(Integer workNo);
 
     /**
+     * 鍑哄簱 101 涓嬭鍗曞彿宸插嚭鐜扮殑 plt_type锛堝巻鍙茶〃锛�
+     */
+    @Select("SELECT plt_type FROM asr_wrk_mast_log WHERE io_type = 101 AND plt_type IS NOT NULL AND plt_type > 0 AND user_no = #{userNo}")
+    List<Integer> listOutboundPltTypesByUserNo(@Param("userNo") String userNo);
+
+    /**
      * 鏌ヨ搴撳瓨绉诲姩娴佹按璁板綍
      */
     List<InventoryFlowDto> inventoryFlowList(@Param("curr") Integer curr,@Param("limit") Integer limit, @Param("param") Map<String, Object> param);
diff --git a/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java b/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java
index f593fa3..9eeac0e 100644
--- a/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java
+++ b/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java
@@ -27,4 +27,10 @@
             , @Param("standby1")String standby1, @Param("standby2")String standby2, @Param("standby3")String standby3
             , @Param("boxType1")String boxType1, @Param("boxType2")String boxType2, @Param("boxType3")String boxType3, @Param("crnNo") Integer crnNo);
 
+    /**
+     * 鍑哄簱 101 涓嬭鍗曞彿宸插嚭鐜扮殑 plt_type锛堜富琛級
+     */
+    @Select("SELECT plt_type FROM asr_wrk_mast WHERE io_type = 101 AND plt_type IS NOT NULL AND plt_type > 0 AND user_no = #{userNo}")
+    List<Integer> listOutboundPltTypesByUserNo(@Param("userNo") String userNo);
+
 }
diff --git a/src/main/java/com/zy/asrs/service/WrkMastService.java b/src/main/java/com/zy/asrs/service/WrkMastService.java
index 258d5dc..030e590 100644
--- a/src/main/java/com/zy/asrs/service/WrkMastService.java
+++ b/src/main/java/com/zy/asrs/service/WrkMastService.java
@@ -29,4 +29,9 @@
 
     WrkMast selectWrkMast(Integer workNo,String barcode);
 
+    /**
+     * 鍑哄簱(io_type=101)鍚屼竴 user_no锛氫富琛ㄤ笌鍘嗗彶琛ㄥ苟闆嗕笅锛岃嚜 1 璧疯繛缁瓨鍦ㄧ殑鏈�澶� plt_type銆�
+     */
+    int outboundSeqMaxContiguousPlt(String userNo);
+
 }
diff --git a/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
index 13256d9..36dae29 100644
--- a/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -33,7 +33,6 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import javax.rmi.CORBA.Util;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -428,6 +427,10 @@
             for (WrkMast wrkMast : activeTasks) {
                 HashMap<String,Object> hashMap = new HashMap<>();
                 hashMap.put("taskNo", wrkMast.getWrkNo());
+                if (!Cools.isEmpty(wrkMast) && wrkMast.getWrkSts() ==11L) {
+                    workService.cancelWrkMast(wrkMast.getWrkNo()+"", 9955L);
+                    continue;
+                }
                 taskList.add(hashMap);
             }
             wcsApiService.pauseOutTasks(taskList);
@@ -1300,7 +1303,7 @@
     public R outOrder(OutTaskParam param,int count) {
         LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_sts", "F").eq("barcode", param.getPalletId()));
         if (locMast == null) {
-            return R.error("娌℃湁鎵惧埌鎵樼洏鐮�=" + param.getPalletId() + "瀵瑰簲鐨勫簱浣�");
+            throw new CoolException("娌℃湁鎵惧埌鎵樼洏鐮�=" + param.getPalletId() + "瀵瑰簲鐨勫簱浣�");
         }
         Integer ioType = 101;
         // 鑾峰彇璺緞
diff --git a/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java
index 9911553..82374e7 100644
--- a/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java
@@ -5,16 +5,23 @@
 import com.core.common.Cools;
 import com.zy.asrs.entity.WrkMast;
 import com.zy.asrs.entity.result.FindLocNoAttributeVo;
+import com.zy.asrs.mapper.WrkMastLogMapper;
 import com.zy.asrs.mapper.WrkMastMapper;
 import com.zy.asrs.service.WrkMastService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 @Slf4j
 @Service("wrkMastService")
 public class WrkMastServiceImpl extends ServiceImpl<WrkMastMapper, WrkMast> implements WrkMastService {
+
+    @Autowired
+    private WrkMastLogMapper wrkMastLogMapper;
 
     @Override
     public int getWorkingMast(Integer devpNo) {
@@ -63,4 +70,29 @@
     public WrkMast selectWrkMast(Integer workNo, String barcode) {
         return this.baseMapper.selectWrkMast(workNo, barcode);
     }
+
+    @Override
+    public int outboundSeqMaxContiguousPlt(String userNo) {
+        List<Integer> fromMast = baseMapper.listOutboundPltTypesByUserNo(userNo);
+        List<Integer> fromLog = wrkMastLogMapper.listOutboundPltTypesByUserNo(userNo);
+        Set<Integer> filled = new HashSet<>();
+        addPositivePlt(fromMast, filled);
+        addPositivePlt(fromLog, filled);
+        int h = 0;
+        while (filled.contains(h + 1)) {
+            h++;
+        }
+        return h;
+    }
+
+    private static void addPositivePlt(List<Integer> list, Set<Integer> target) {
+        if (list == null) {
+            return;
+        }
+        for (Integer p : list) {
+            if (p != null && p > 0) {
+                target.add(p);
+            }
+        }
+    }
 }
diff --git a/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java b/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java
index 901adb0..db6497d 100644
--- a/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java
+++ b/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java
@@ -20,6 +20,9 @@
     @Value("${wcs.status-sync.enabled:true}")
     private Boolean enabled;
 
+    @Value("${wcs.status-sync.log-on-failure:true}")
+    private Boolean logOnFailure;
+
     @Scheduled(
             initialDelayString = "${wcs.status-sync.initial-delay:10000}",
             fixedDelayString = "${wcs.status-sync.fixed-delay:5000}"
@@ -28,8 +31,8 @@
         if (!Boolean.TRUE.equals(enabled)) {
             return;
         }
-        R result = wcsApiService.syncDeviceStatusFromWcs();
-        if (!Objects.equals(result.get("code"), 200)) {
+        R result = wcsApiService.syncDeviceStatusFromWcs(Boolean.TRUE.equals(logOnFailure));
+        if (Boolean.TRUE.equals(logOnFailure) && !Objects.equals(result.get("code"), 200)) {
             log.warn("杞鍚屾WCS璁惧鐘舵�佸け璐�, result={}", result);
         }
     }
diff --git a/src/main/java/com/zy/asrs/task/WorkMastScheduler.java b/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
index b61a4aa..5af485f 100644
--- a/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
+++ b/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
@@ -5,7 +5,6 @@
 import com.core.common.R;
 import com.zy.api.controller.params.WorkTaskParams;
 import com.zy.api.service.WcsApiService;
-import com.zy.asrs.entity.LocMast;
 import com.zy.asrs.entity.WrkMast;
 import com.zy.asrs.service.WrkMastService;
 import com.zy.asrs.task.core.ReturnT;
@@ -19,7 +18,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
@@ -70,20 +68,20 @@
      * @author Ryan
      * @date 2026/1/10 14:42
      */
-    @Scheduled(cron = "0/3 * * * * ? ")
+    @Scheduled(cron = "0/10 * * * * ? ")
     private void autoPubTasks() {
         // 浠呭鐞嗗緟涓嬪彂/宸茬敓鎴愪笅鍙戝彿鐨勫伐浣滄。銆�
-        List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("wrk_sts", Arrays.asList(1L, 11L)));
+        List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("wrk_sts", Arrays.asList(1L, 11L))
+                .orderBy("user_no", true)
+                .orderBy("plt_type", true));
         if (wrkMasts.isEmpty()) {
             return;
         }
 
-        // 鎵撴暎椤哄簭锛岄伩鍏嶅浐瀹氭帓搴忎笅鍚屼竴鎵逛换鍔¢暱鏈熷崰鐢ㄨ皟搴︽満浼氥��
-        Collections.shuffle(wrkMasts);
         List<WorkTaskParams> paramsList = new ArrayList<>();
         for (WrkMast wrkMast : wrkMasts) {
             // 鍑哄簱绫讳换鍔★紙ioType > 100锛夐粯璁ら渶瑕� ERP 纭锛涙湭纭鐨勪换鍔″湪杩欓噷鐩存帴璺宠繃銆�
-            if (wrkMast.getIoType()>100&& !wrkMast.getPdcType().equals("Y")) {
+            if (wrkMast.getIoType() > 100 && !"Y".equalsIgnoreCase(wrkMast.getPdcType())) {
                 continue;
             }
 
@@ -103,7 +101,7 @@
                         .setBatchSeq(wrkMast.getPltType())
                         .setBarcode(wrkMast.getBarcode());
             // 2: 鍏ュ簱銆傚叆搴撴帴鍙d娇鐢� sourceStaNo + 鐩爣搴撲綅銆�
-            }else if(wrkMast.getIoType()==2&& !Cools.isEmpty(wrkMast.getSourceStaNo())){
+            } else if (wrkMast.getIoType() == 2 && !Cools.isEmpty(wrkMast.getSourceStaNo())) {
                 params.setType("in")
                         .setTaskNo(wrkMast.getWrkNo()+"")
                         .setSourceStaNo(String.valueOf(wrkMast.getSourceStaNo()))
diff --git a/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java b/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java
index 231666b..21b7f8d 100644
--- a/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java
+++ b/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java
@@ -6,6 +6,7 @@
 import com.zy.integration.iot.util.IotMqttSslUtils;
 import com.zy.iot.config.IotProperties;
 import com.zy.iot.entity.IotTopicConfig;
+import com.zy.iot.service.IotDbConfigService;
 import lombok.extern.slf4j.Slf4j;
 import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
 import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
@@ -33,13 +34,15 @@
     @Autowired
     private IotProperties iotProperties;
     @Autowired
+    private IotDbConfigService iotDbConfigService;
+    @Autowired
     private IotInboundMessageHandler inboundMessageHandler;
 
     private volatile MqttClient mqttClient;
 
     @PostConstruct
     public void init() {
-        if (!iotProperties.isEnabled()) {
+        if (!iotDbConfigService.isMqttEnabled()) {
             return;
         }
         try {
@@ -68,8 +71,33 @@
      * 鎳掑姞杞藉缓杩烇紝棣栨鍙戦�佹垨鍚姩鏃惰嚜鍔ㄨ繛鎺ュ苟琛ヨ闃呬富棰樸��
      */
     @Override
+    public synchronized void reconnectFromDbConfig() {
+        iotDbConfigService.refreshCache();
+        try {
+            if (mqttClient != null) {
+                try {
+                    if (mqttClient.isConnected()) {
+                        mqttClient.disconnect();
+                    }
+                    mqttClient.close();
+                } catch (Exception e) {
+                    log.warn("IoT MQTT disconnect before reconnect", e);
+                }
+                mqttClient = null;
+            }
+            if (!iotDbConfigService.isMqttEnabled()) {
+                log.info("IoT MQTT 宸茬敱閰嶇疆鍏抽棴锛屾湭寤虹珛杩炴帴");
+                return;
+            }
+            ensureConnected();
+        } catch (Exception e) {
+            log.error("IoT MQTT 閲嶈繛澶辫触", e);
+        }
+    }
+
+    @Override
     public synchronized void ensureConnected() throws Exception {
-        if (!iotProperties.isEnabled()) {
+        if (!iotDbConfigService.isMqttEnabled()) {
             return;
         }
         if (!hasRequiredConfig()) {
@@ -84,6 +112,8 @@
         }
         mqttClient.connect(buildOptions());
         subscribeTopics();
+        log.info("IoT MQTT 宸茶繛鎺� serverURI={} clientId={} tls={}",
+                iotProperties.getServerUri(), iotProperties.getResolvedClientId(), iotProperties.isTlsEnabled());
     }
 
     /**
@@ -91,7 +121,7 @@
      */
     @Override
     public void publish(String topic, String payload) throws Exception {
-        if (!iotProperties.isEnabled() || Cools.isEmpty(topic) || payload == null) {
+        if (!iotDbConfigService.isMqttEnabled() || Cools.isEmpty(topic) || payload == null) {
             return;
         }
         ensureConnected();
@@ -101,6 +131,7 @@
         MqttMessage mqttMessage = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8));
         mqttMessage.setQos(1);
         mqttMessage.setRetained(false);
+        log.info("IoT MQTT 鍙戦�� topic={} payload={}", topic, payload);
         mqttClient.publish(topic, mqttMessage);
     }
 
@@ -135,7 +166,9 @@
 
             @Override
             public void messageArrived(String topic, MqttMessage message) {
-                inboundMessageHandler.handleMessage(topic, new String(message.getPayload(), StandardCharsets.UTF_8));
+                String body = new String(message.getPayload(), StandardCharsets.UTF_8);
+                log.info("IoT MQTT 鎺ユ敹 topic={} payload={}", topic, body);
+                inboundMessageHandler.handleMessage(topic, body);
             }
 
             @Override
@@ -146,7 +179,10 @@
             public void connectComplete(boolean reconnect, String serverURI) {
                 try {
                     subscribeTopics();
-                    log.info("iot mqtt connected, reconnect={}, serverURI={}", reconnect, serverURI);
+                    if (reconnect) {
+                        log.info("IoT MQTT 鑷姩閲嶈繛鎴愬姛 serverURI={} clientId={}",
+                                serverURI, iotProperties.getResolvedClientId());
+                    }
                 } catch (Exception e) {
                     log.error("subscribe iot topics failed", e);
                 }
@@ -178,7 +214,7 @@
         if (mqttClient == null || !mqttClient.isConnected()) {
             return;
         }
-        IotTopicConfig topics = iotProperties.getTopics();
+        IotTopicConfig topics = iotDbConfigService.getEffectiveTopics();
         subscribe(topics.getEgressStow());
         subscribe(topics.getEgressPick());
         subscribe(topics.getEgressFeedback());

--
Gitblit v1.9.1