From 79eb9321e21e873a373c10480f8066cbc0407eb8 Mon Sep 17 00:00:00 2001
From: cl <1442464845@qq.com>
Date: 星期六, 04 四月 2026 00:30:31 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/lggslasrs' into lggslasrs

---
 src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java |  822 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 763 insertions(+), 59 deletions(-)

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 6ec1185..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;
@@ -9,6 +10,9 @@
 import com.zy.api.controller.params.ReceviceTaskParams;
 import com.zy.api.controller.params.StopOutTaskParams;
 import com.zy.api.controller.params.WorkTaskParams;
+import com.zy.api.entity.CrnProtocol;
+import com.zy.api.entity.DeviceStatusVo;
+import com.zy.api.entity.StationProtocol;
 import com.zy.api.service.WcsApiService;
 import com.zy.asrs.entity.*;
 import com.zy.asrs.service.*;
@@ -24,21 +28,29 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.IOException;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 @Slf4j
 @Service
 public class WcsApiServiceImpl implements WcsApiService {
+    private static final Long WCS_SYNC_USER = 9999L;
+    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
@@ -64,8 +76,20 @@
 
     @Value("${wcs.address.stopOutTask}")
     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
     private CommonService commonService;
+    @Autowired
+    private BasDevpService basDevpService;
+    @Autowired
+    private BasCrnpService basCrnpService;
+    @Autowired
+    private ApiLogService apiLogService;
 
 
     /**
@@ -78,63 +102,409 @@
      */
     @Override
     public R pubWrkToWcs(WorkTaskParams params) {
-        if (Objects.isNull(params.getTaskNo())) {
-            return R.error("浠诲姟鍙蜂笉鑳戒负绌猴紒锛�");
+        if (params == null) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
         }
-        if (Objects.isNull(params.getBarcode())) {
-            return R.error("鎵樼洏鐮佷笉鑳戒负绌猴紒锛�");
-        }
-        if (Objects.isNull(params.getLocNo())) {
-            return R.error("鐩爣搴撲綅涓嶈兘涓虹┖锛侊紒");
-        }
-        String url = createInTask;
-        if (!Objects.isNull(params.getType()) && params.getType().equals("out")) {
-            url = getWcs_address;
-        }else if (!Objects.isNull(params.getType()) && params.getType().equals("move")) {
-            url = createLocMoveTask;
-        }
-        String response;
-        R r = R.ok();
         WrkMast wrkMast = wrkMastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", params.getTaskNo()));
-        if (!Objects.isNull(wrkMast) && "out".equalsIgnoreCase(params.getType()) && "Y".equalsIgnoreCase(wrkMast.getPauseMk())) {
-            return R.error("task paused");
+        String validateMsg = validatePubTask(params, wrkMast);
+        if (!Cools.isEmpty(validateMsg)) {
+            return R.error(validateMsg);
         }
+        String url = resolveTaskPath(params);
+        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()
                     .setUri(wcs_address)
                     .setPath(url)
-                    .setJson(JSON.toJSONString(params))
+                    .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
+                    .setTimeout(10, TimeUnit.SECONDS)
+                    .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) {
-                if (!Objects.isNull(wrkMast)) {
-                    if (wrkMast.getIoType()==1 || wrkMast.getIoType()==10) {
-                        wrkMast.setWrkSts(2L);
-                        wrkMast.setModiTime(new Date());
-                        wrkMastService.updateById(wrkMast);
-                    }else if(wrkMast.getIoType()==2){
-                        wrkMast.setWrkSts(2L);
-                        wrkMast.setModiTime(new Date());
-                        wrkMastService.updateById(wrkMast);
-                    }else if (wrkMast.getIoType()==101 || wrkMast.getIoType()==110) {
-                        wrkMast.setWrkSts(12L);
-                        wrkMast.setModiTime(new Date());
-                        wrkMastService.updateById(wrkMast);
-                    }
-                }
+                updateWrkMastAfterPublish(wrkMast);
                 //TODO 涓婃姤鏄惁鎴愬姛
             }else {
                 r =R.error();
             }
         } catch (IOException e) {
+            wcsThrown = e;
             throw new RuntimeException(e);
+        } finally {
+            logWcsToApiLog(url, requestJson, response, wcsThrown, wcsBizOk);
         }
         return r;
+    }
+
+    @Override
+    public R pubWrksToWcs(List<WorkTaskParams> paramsList) {
+        if (paramsList == null || paramsList.isEmpty()) {
+            return R.error("浠诲姟涓嶈兘涓虹┖锛侊紒");
+        }
+
+        Map<String, WrkMast> wrkMastMap = getWrkMastMap(paramsList);
+        List<WorkTaskParams> accepted = new ArrayList<>();
+        List<String> skipMsgs = new ArrayList<>();
+
+        for (WorkTaskParams params : paramsList) {
+            if (params == null) {
+                skipMsgs.add("浠诲姟涓嶈兘涓虹┖锛侊紒");
+                continue;
+            }
+            WrkMast wrkMast = wrkMastMap.get(params.getTaskNo());
+            String validateMsg = validatePubTask(params, wrkMast);
+            if (!Cools.isEmpty(validateMsg)) {
+                skipMsgs.add(buildTaskMsg(params, validateMsg));
+                continue;
+            }
+            accepted.add(params);
+        }
+
+        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<>();
+        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;
+            }
+
+            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<>();
+        result.put("successCount", successCount);
+        result.put("skipCount", skipMsgs.size());
+        result.put("failCount", failMsgs.size());
+        if (!skipMsgs.isEmpty()) {
+            result.put("skipMsgs", skipMsgs);
+        }
+        if (!failMsgs.isEmpty()) {
+            result.put("failMsgs", failMsgs);
+        }
+
+        if (successCount == 0) {
+            String msg = !failMsgs.isEmpty() ? failMsgs.get(0) : (skipMsgs.isEmpty() ? "WCS涓嬪彂浠诲姟澶辫触" : skipMsgs.get(0));
+            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();
     }
 
     /**
@@ -176,17 +546,10 @@
                 if (!wrkMastService.updateById(mast)) {
                     throw new CoolException("浠诲姟鐘舵�佷慨鏀瑰け璐ワ紒锛�");
                 }
+                //wcs浠诲姟鍙栨秷鎺ュ彛
             } else if (params.getMsgType().equals("task_cancel")) {
-                if (mast.getIoType() != null && mast.getIoType() > 100 && mast.getWrkSts() < 14) {
-                    mast.setPauseMk("Y");
-                    mast.setUpdMk("WCS_CANCELLED");
-                    mast.setManuType("WCS_CANCELLED");
-                    mast.setModiTime(new Date());
-                    if (!wrkMastService.updateById(mast)) {
-                        throw new CoolException("task cancel update fail");
-                    }
-                }
-            }else if (params.getMsgType().equals("task_arrive")) {
+                workService.cancelWrkMast(String.valueOf(mast.getWrkNo()), 9955L);
+            } else if (params.getMsgType().equals("task_arrive")) {
                 //鍒拌揪鐩殑鍦�
                 //濡傛灉鍑哄簱浠诲姟鏄法鍖哄垯闇�瑕佺敓鎴愭柊鐨勫叆搴撲换鍔″叆搴�
                 if(!Cools.isEmpty(mast.getLocNo())){
@@ -206,34 +569,375 @@
         }
         return R.ok();
     }
+
     @Override
-    public R pauseOutTasks(StopOutTaskParams params) {
-        if (params == null || params.getTasks() == null || params.getTasks().isEmpty()) {
-            return R.ok("no tasks to stop");
+    public R syncDeviceStatusFromWcs(boolean logOnFailure) {
+        if (!Boolean.parseBoolean(String.valueOf(switchValue))) {
+            return R.ok("WCS寮�鍏冲叧闂�");
+        }
+        String response = null;
+        try {
+            response = requestDeviceStatusFromWcs();
+            JSONObject jsonObject = JSON.parseObject(response == null ? "{}" : response);
+            Integer code = jsonObject.getInteger("code");
+            if (!Objects.equals(code, 200)) {
+                String msg = jsonObject.getString("msg");
+                return R.error(Cools.isEmpty(msg) ? "鑾峰彇WCS璁惧鐘舵�佸け璐�" : msg);
+            }
+            JSONObject data = jsonObject.getJSONObject("data");
+            DeviceStatusVo deviceStatusVo = data == null
+                    ? new DeviceStatusVo()
+                    : JSON.parseObject(data.toJSONString(), DeviceStatusVo.class);
+
+            int stationCount = syncStationStatus(deviceStatusVo.getStationList());
+            int crnCount = syncCrnStatus(deviceStatusVo.getCrnList());
+
+            Map<String, Object> result = new LinkedHashMap<>();
+            result.put("stationCount", stationCount);
+            result.put("crnCount", crnCount);
+            log.info("鍚屾WCS璁惧鐘舵�佹垚鍔�, stationCount={}, crnCount={}", stationCount, crnCount);
+            return R.ok("鍚屾鎴愬姛").add(result);
+        } catch (Exception e) {
+            if (logOnFailure) {
+                log.error("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+            } else {
+                log.debug("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+            }
+            return R.error("鍚屾WCS璁惧鐘舵�佸け璐�: " + e.getMessage());
+        }
+    }
+
+    private boolean requiresOutboundErpConfirm(WrkMast wrkMast) {
+        Integer ioType = wrkMast == null ? null : wrkMast.getIoType();
+        return ioType != null && (ioType == 101 || ioType == 103 || ioType == 104 || ioType == 107 || ioType == 110);
+    }
+
+    /**
+     * 鏍¢獙鍗曟潯浠诲姟鏄惁婊¤冻涓嬪彂鍓嶆彁銆�
+     * <p>
+     * 杩欓噷鏃㈡牎楠屾帴鍙e繀濉」锛屼篃鏍¢獙涓氬姟绾︽潫锛屼緥濡傦細
+     * 1. 鍑哄簱浠诲姟鏄惁琚殏鍋滐紱
+     * 2. 闇�瑕� ERP 纭鐨勫嚭搴撲换鍔℃槸鍚﹀凡纭銆�
+     */
+    private String validatePubTask(WorkTaskParams params, WrkMast wrkMast) {
+        if (params == null) {
+            return "鍙傛暟涓嶈兘涓虹┖锛侊紒";
+        }
+        if (Cools.isEmpty(params.getTaskNo())) {
+            return "浠诲姟鍙蜂笉鑳戒负绌猴紒锛�";
+        }
+        if (Cools.isEmpty(params.getBarcode())) {
+            return "鎵樼洏鐮佷笉鑳戒负绌猴紒锛�";
+        }
+        if (Cools.isEmpty(params.getLocNo())) {
+            return "鐩爣搴撲綅涓嶈兘涓虹┖锛侊紒";
+        }
+        if (!Objects.isNull(wrkMast) && "out".equalsIgnoreCase(params.getType())) {
+            if ("Y".equalsIgnoreCase(wrkMast.getPauseMk())) {
+                return "task paused";
+            }
+            if (requiresOutboundErpConfirm(wrkMast) && !"Y".equalsIgnoreCase(wrkMast.getPdcType())) {
+                return "task not confirmed by erp";
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 鎸変换鍔$被鍨嬮�夋嫨 WCS 鎺ュ彛鍦板潃銆�
+     * in -> 鍏ュ簱鎺ュ彛
+     * out -> 鍑哄簱鎺ュ彛
+     * move -> 绉诲簱鎺ュ彛
+     */
+    private String resolveTaskPath(WorkTaskParams params) {
+        if (!Objects.isNull(params.getType()) && params.getType().equals("out")) {
+            return getWcs_address;
+        }
+        if (!Objects.isNull(params.getType()) && params.getType().equals("move")) {
+            return createLocMoveTask;
+        }
+        return createInTask;
+    }
+
+    /**
+     * WCS 涓嬪彂鎴愬姛鍚庢帹杩涙湰鍦板伐浣滄。鐘舵�併��
+     * <p>
+     * 杩欓噷鍙鐞嗏�滃凡涓嬪彂鈥濊繖涓�灞傜姸鎬侊紝涓嶅鐞嗚澶囨墽琛屽畬鎴愮姸鎬侊紱
+     * 璁惧鎵ц瀹屾垚渚濈劧浠� WCS 鍥炲啓涓哄噯銆�
+     */
+    private void updateWrkMastAfterPublish(WrkMast wrkMast) {
+        if (Objects.isNull(wrkMast)) {
+            return;
+        }
+        if (wrkMast.getIoType()==1 || wrkMast.getIoType()==10) {
+            wrkMast.setWrkSts(2L);
+            wrkMast.setModiTime(new Date());
+            wrkMastService.updateById(wrkMast);
+        }else if(wrkMast.getIoType()==2){
+            wrkMast.setWrkSts(2L);
+            wrkMast.setModiTime(new Date());
+            wrkMastService.updateById(wrkMast);
+        }else if (wrkMast.getIoType()==101 || wrkMast.getIoType()==110) {
+            wrkMast.setWrkSts(12L);
+            wrkMast.setModiTime(new Date());
+            wrkMastService.updateById(wrkMast);
+        }
+    }
+
+    /**
+     * 鎶婃湰娆″緟涓嬪彂鐨� taskNo 鎵归噺鏄犲皠鎴愬伐浣滄。锛屼緵鍚庣画鏍¢獙銆佹寜 userNo 鍒嗙粍銆佺姸鎬佸洖鍐欏鐢ㄣ��
+     */
+    private Map<String, WrkMast> getWrkMastMap(List<WorkTaskParams> paramsList) {
+        List<String> taskNos = paramsList.stream()
+                .filter(Objects::nonNull)
+                .map(WorkTaskParams::getTaskNo)
+                .filter(taskNo -> !Cools.isEmpty(taskNo))
+                .distinct()
+                .collect(Collectors.toList());
+        if (taskNos.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("wrk_no", taskNos));
+        if (wrkMasts == null || wrkMasts.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        return wrkMasts.stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toMap(mast -> String.valueOf(mast.getWrkNo()), mast -> mast, (left, right) -> left, LinkedHashMap::new));
+    }
+
+    /**
+     * 鏋勯�犳壒閲忎笅鍙戠殑鍒嗙粍閿��
+     * <p>
+     * 鍒嗙粍瑙勫垯锛�
+     * 1. 鍏堟寜鎺ュ彛璺緞鍖哄垎锛岄伩鍏嶄笉鍚屼换鍔$被鍨嬫贩鐢ㄥ悓涓�涓� WCS 鎺ュ彛锛�
+     * 2. 鍐嶆寜 userNo 鍖哄垎锛岀‘淇濈浉鍚� userNo 鐨勪换鍔′竴璧蜂笂鎶ャ��
+     * <p>
+     * 姝e父鎯呭喌涓� userNo 鍙栬嚜 work_mast.user_no锛�
+     * 濡傛灉褰撳墠娌℃煡鍒板伐浣滄。锛屽垯鍥為��鍒拌姹傞噷鐨� batch 瀛楁锛屼繚璇佸吋瀹瑰凡鏈夎皟鐢ㄣ��
+     */
+    private String buildBatchGroupKey(WorkTaskParams params, WrkMast wrkMast) {
+        String path = resolveTaskPath(params);
+        String userNo = wrkMast == null ? null : wrkMast.getUserNo();
+        if (Cools.isEmpty(userNo)) {
+            userNo = params.getBatch();
+        }
+        if (Cools.isEmpty(userNo)) {
+            userNo = "_NO_USER_";
+        }
+        return path + "#" + userNo;
+    }
+
+    /**
+     * 灏嗕竴缁勪笟鍔″弬鏁拌浆鎹㈡垚 WCS 鎵归噺鎺ュ彛鐨� tasks 鏁扮粍銆�
+     */
+    private List<Map<String, Object>> buildTaskPayloads(List<WorkTaskParams> tasks) {
+        List<Map<String, Object>> payloads = new ArrayList<>();
+        for (WorkTaskParams task : tasks) {
+            payloads.add(buildTaskPayload(task));
+        }
+        return payloads;
+    }
+
+    /**
+     * 缁勮鍗曟潯浠诲姟鐨� WCS 璇锋眰浣撱��
+     * 鍙斁褰撳墠浠诲姟绫诲瀷瀹為檯闇�瑕佺殑瀛楁锛涚┖瀛楁涓嶉�忎紶锛岄伩鍏嶇粰 WCS 閫犳垚姝т箟銆�
+     */
+    private Map<String, Object> buildTaskPayload(WorkTaskParams params) {
+        Map<String, Object> task = new LinkedHashMap<>();
+        if (!Cools.isEmpty(params.getTaskNo())) {
+            task.put("taskNo", params.getTaskNo());
+        }
+        if (!Cools.isEmpty(params.getLocNo())) {
+            task.put("locNo", params.getLocNo());
+        }
+        if (!Cools.isEmpty(params.getSourceLocNo())) {
+            task.put("sourceLocNo", params.getSourceLocNo());
+        }
+        if (!Cools.isEmpty(params.getSourceStaNo())) {
+            task.put("sourceStaNo", params.getSourceStaNo());
+        }
+        if (!Cools.isEmpty(params.getBarcode())) {
+            task.put("barcode", params.getBarcode());
+        }
+        if (!Objects.isNull(params.getTaskPri())) {
+            task.put("taskPri", params.getTaskPri());
+        }
+        if (!Cools.isEmpty(params.getStaNo())) {
+            task.put("staNo", params.getStaNo());
+        }
+        if (!Cools.isEmpty(params.getBatch())) {
+            task.put("batch", params.getBatch());
+        }
+        if (!Objects.isNull(params.getBatchSeq())) {
+            task.put("batchSeq", params.getBatchSeq());
+        }
+        return task;
+    }
+
+    /**
+     * 鏋勯�犺烦杩�/澶辫触淇℃伅鏃剁粺涓�甯︿笂 taskNo锛屼究浜庢帓鏌ュ叿浣撴槸鍝竴鏉″伐浣滄。鏈涓嬪彂銆�
+     */
+    private String buildTaskMsg(WorkTaskParams params, String msg) {
+        if (params == null || Cools.isEmpty(params.getTaskNo())) {
+            return msg;
+        }
+        return "taskNo=" + params.getTaskNo() + ", msg=" + msg;
+    }
+
+    @Override
+    public R pauseOutTasks(List<HashMap<String,Object>> params) {
+        if (params == null || params.size() == 0) {
+            return R.ok("鏃犱换鍔¢渶瑕佸彇娑�");
         }
         if (!Boolean.parseBoolean(String.valueOf(switchValue))) {
-            return R.ok("wcs switch off");
+            return R.ok("WCS寮�鍏冲叧闂�");
         }
-        String response;
+        HashMap<String,Object> map = new  HashMap<>();
+        map.put("taskList", params);
+        String requestJson = JSON.toJSONString(map);
+        String response = null;
+        Throwable wcsThrown = null;
+        boolean wcsBizOk = false;
         try {
+            log.info("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟, request={}", requestJson);
             response = new HttpHandler.Builder()
                     .setUri(wcs_address)
                     .setPath(stopOutTask)
-                    .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
+//                    .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 == 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 stop out task failed" : msg);
+                throw new CoolException(Cools.isEmpty(msg) ? "WCS鍙栨秷鍑哄簱浠诲姟澶辫触" : msg);
             }
+            return R.ok(Cools.isEmpty(jsonObject.getString("msg")) ? "鎿嶄綔鎴愬姛" : jsonObject.getString("msg"));
         } catch (IOException e) {
-            throw new CoolException("call WCS stop out task fail: " + e.getMessage());
+            wcsThrown = e;
+            throw new CoolException("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟澶辫触: " + e.getMessage());
+        } finally {
+            logWcsToApiLog(stopOutTask, requestJson, response, wcsThrown, wcsBizOk);
         }
-        return R.ok();
+    }
+
+    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)
+                .setPath(getDeviceStatus)
+                .setTimeout(10, TimeUnit.SECONDS);
+        String method = Cools.isEmpty(deviceStatusMethod) ? "POST" : deviceStatusMethod.trim().toUpperCase(Locale.ROOT);
+        if ("POST".equals(method)) {
+            return builder.setJson("{}").build().doPost();
+        }
+        return builder.build().doGet();
+    }
+
+    private int syncStationStatus(List<StationProtocol> stationList) {
+        if (stationList == null || stationList.isEmpty()) {
+            return 0;
+        }
+        int count = 0;
+        Date now = new Date();
+        for (StationProtocol stationProtocol : stationList) {
+            if (stationProtocol == null || stationProtocol.getStationId() == null) {
+                continue;
+            }
+            BasDevp basDevp = basDevpService.selectById(stationProtocol.getStationId());
+            boolean isNew = Objects.isNull(basDevp);
+            if (isNew) {
+                basDevp = new BasDevp();
+                basDevp.setDevNo(stationProtocol.getStationId());
+                basDevp.setAppeUser(WCS_SYNC_USER);
+                basDevp.setAppeTime(now);
+            }
+            basDevp.setInEnable(toFlag(stationProtocol.isInEnable()));
+            basDevp.setOutEnable(toFlag(stationProtocol.isOutEnable()));
+            basDevp.setAutoing(toFlag(stationProtocol.isAutoing()));
+            basDevp.setLoading(toFlag(stationProtocol.isLoading()));
+            basDevp.setCanining(toFlag(stationProtocol.isEnableIn()));
+            basDevp.setCanouting(toFlag(!stationProtocol.isRunBlock()));
+            basDevp.setWrkNo(defaultZero(stationProtocol.getTaskNo()));
+            basDevp.setBarcode(normalizeText(stationProtocol.getBarcode()));
+            basDevp.setGrossWt(stationProtocol.getWeight() == null ? 0D : stationProtocol.getWeight());
+            basDevp.setModiUser(WCS_SYNC_USER);
+            basDevp.setModiTime(now);
+            if (isNew) {
+                if (!basDevpService.insert(basDevp)) {
+                    throw new CoolException("鏂板绔欑偣鐘舵�佸け璐�, stationId=" + stationProtocol.getStationId());
+                }
+            } else if (!basDevpService.updateById(basDevp)) {
+                throw new CoolException("鏇存柊绔欑偣鐘舵�佸け璐�, stationId=" + stationProtocol.getStationId());
+            }
+            count++;
+        }
+        return count;
+    }
+
+    private int syncCrnStatus(List<CrnProtocol> crnList) {
+        if (crnList == null || crnList.isEmpty()) {
+            return 0;
+        }
+        int count = 0;
+        Date now = new Date();
+        for (CrnProtocol crnProtocol : crnList) {
+            if (crnProtocol == null || crnProtocol.getCrnNo() == null) {
+                continue;
+            }
+            BasCrnp basCrnp = basCrnpService.selectById(crnProtocol.getCrnNo());
+            boolean isNew = Objects.isNull(basCrnp);
+            if (isNew) {
+                basCrnp = new BasCrnp();
+                basCrnp.setCrnNo(crnProtocol.getCrnNo());
+                basCrnp.setInEnable(YES);
+                basCrnp.setOutEnable(YES);
+                basCrnp.setAppeUser(WCS_SYNC_USER);
+                basCrnp.setAppeTime(now);
+            }
+            // crn_sts 鏈湴琛ㄥ瓨鐨勬槸鈥滃爢鍨涙満妯″紡(鎵嬪姩/鑷姩/鐢佃剳)鈥濓紝鍥犳蹇呴』鍐� mode锛屼笉鑳藉啓 status銆�
+            basCrnp.setCrnSts(defaultZero(crnProtocol.getMode()));
+            basCrnp.setWrkNo(defaultZero(crnProtocol.getTaskNo()));
+            basCrnp.setCrnErr(crnProtocol.getAlarm() == null ? 0L : Long.valueOf(crnProtocol.getAlarm()));
+            basCrnp.setModiUser(WCS_SYNC_USER);
+            basCrnp.setModiTime(now);
+            if (isNew) {
+                if (!basCrnpService.insert(basCrnp)) {
+                    throw new CoolException("鏂板鍫嗗灈鏈虹姸鎬佸け璐�, crnNo=" + crnProtocol.getCrnNo());
+                }
+            } else if (!basCrnpService.updateById(basCrnp)) {
+                throw new CoolException("鏇存柊鍫嗗灈鏈虹姸鎬佸け璐�, crnNo=" + crnProtocol.getCrnNo());
+            }
+            count++;
+        }
+        return count;
+    }
+
+    private Integer defaultZero(Integer value) {
+        return value == null ? 0 : value;
+    }
+
+    private String normalizeText(String value) {
+        return Cools.isEmpty(value) ? "" : value;
+    }
+
+    private String toFlag(boolean value) {
+        return value ? YES : NO;
     }
 
 }

--
Gitblit v1.9.1