From c9944fa15889ef97236960fcc9370a582cd86267 Mon Sep 17 00:00:00 2001
From: zwl <1051256694@qq.com>
Date: 星期二, 17 三月 2026 14:37:34 +0800
Subject: [PATCH] 1.wcs下发出库任务方式需更改,先采集数据再一次性下发:下发组batch和序号batch_seq 2.库位分配逻辑需优化,目前分配单个巷道 3.

---
 src/main/java/com/zy/api/controller/params/WorkTaskParams.java |    2 
 src/main/java/com/zy/asrs/utils/VersionUtils.java              |   14 
 src/main/java/com/zy/asrs/controller/OpenController.java       |   35 ++
 src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java   |  315 +++++++++++++++++--
 src/main/java/com/zy/asrs/task/WorkMastScheduler.java          |   42 ++
 src/main/java/com/zy/asrs/mapper/RowLastnoMapper.java          |    2 
 src/main/java/com/zy/common/web/WcsController.java             |    8 
 src/main/java/com/zy/common/service/CommonService.java         |  500 +++++++++++++++++++++++++++---
 src/main/java/com/zy/api/service/WcsApiService.java            |    9 
 9 files changed, 814 insertions(+), 113 deletions(-)

diff --git a/src/main/java/com/zy/api/controller/params/WorkTaskParams.java b/src/main/java/com/zy/api/controller/params/WorkTaskParams.java
index 6318ca0..9843501 100644
--- a/src/main/java/com/zy/api/controller/params/WorkTaskParams.java
+++ b/src/main/java/com/zy/api/controller/params/WorkTaskParams.java
@@ -46,7 +46,7 @@
     private Integer isSuplus;
 
     @ApiModelProperty("缁�")
-    private Integer batch;
+    private String batch;
 
     @ApiModelProperty("搴忓彿")
     private Integer batchSeq;
diff --git a/src/main/java/com/zy/api/service/WcsApiService.java b/src/main/java/com/zy/api/service/WcsApiService.java
index 34dedfa..1b1ed6f 100644
--- a/src/main/java/com/zy/api/service/WcsApiService.java
+++ b/src/main/java/com/zy/api/service/WcsApiService.java
@@ -21,6 +21,15 @@
     R pubWrkToWcs(WorkTaskParams params);
 
     /**
+     * 鎵归噺涓嬪彂浠诲姟鑷砏CS
+     * @author Ryan
+     * @date 2026/3/16 15:57
+     * @param paramsList
+     * @return com.core.common.R
+     */
+    R pubWrksToWcs(List<WorkTaskParams> paramsList);
+
+    /**
      * 鍫嗗灈鏈轰换鍔″畬鎴愮姸鎬佷笂鎶�
      * @author Ryan
      * @date 2026/1/10 16:29
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 534fbc6..791622b 100644
--- a/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
+++ b/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
@@ -75,37 +75,24 @@
      */
     @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("鎵樼洏鐮佷笉鑳戒负绌猴紒锛�");
+        WrkMast wrkMast = wrkMastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", params.getTaskNo()));
+        String validateMsg = validatePubTask(params, wrkMast);
+        if (!Cools.isEmpty(validateMsg)) {
+            return R.error(validateMsg);
         }
-        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 url = resolveTaskPath(params);
         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())) {
-            if ("Y".equalsIgnoreCase(wrkMast.getPauseMk())) {
-                return R.error("task paused");
-            }
-            if (requiresOutboundErpConfirm(wrkMast) && !"Y".equalsIgnoreCase(wrkMast.getPdcType())) {
-                return R.error("task not confirmed by erp");
-            }
-        }
         try {
             log.info("涓嬪彂鎼繍浠诲姟缁檞cs="+JSON.toJSONString(params));
             response = new HttpHandler.Builder()
                     .setUri(wcs_address)
                     .setPath(url)
+                    .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
+                    .setTimeout(10, TimeUnit.SECONDS)
                     .setJson(JSON.toJSONString(params))
                     .build()
                     .doPost();
@@ -114,21 +101,7 @@
             Integer code = jsonObject.getInteger("code");
 
             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();
@@ -137,6 +110,100 @@
             throw new RuntimeException(e);
         }
         return r;
+    }
+
+    @Override
+    public R pubWrksToWcs(List<WorkTaskParams> paramsList) {
+        if (paramsList == null || paramsList.isEmpty()) {
+            return R.error("浠诲姟涓嶈兘涓虹┖锛侊紒");
+        }
+
+        // 鍏堜竴娆℃�ф妸鏈壒浠诲姟瀵瑰簲鐨勫伐浣滄。鎹炲嚭鏉ワ紝閬垮厤寰幆鍐呴噸澶嶆煡搴撱��
+        Map<String, WrkMast> wrkMastMap = getWrkMastMap(paramsList);
+        Map<String, List<WorkTaskParams>> groupedTasks = new LinkedHashMap<>();
+        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;
+            }
+
+            // 鍒嗙粍涓婚敭 = 鎺ュ彛璺緞 + userNo銆�
+            // 杩欐牱鏃㈣兘淇濊瘉鍏ュ簱/鍑哄簱/绉诲簱涓嶄細娣峰彂锛屼篃鑳戒繚璇佺浉鍚� userNo 鐨勪换鍔′細鎵撳寘鍒板悓涓�娆� WCS 璇锋眰涓��
+            String groupKey = buildBatchGroupKey(params, wrkMast);
+            groupedTasks.computeIfAbsent(groupKey, key -> new ArrayList<>()).add(params);
+        }
+
+        if (groupedTasks.isEmpty()) {
+            return R.error(skipMsgs.isEmpty() ? "鏃犲彲涓嬪彂浠诲姟" : skipMsgs.get(0)).add(skipMsgs);
+        }
+
+        int successCount = 0;
+        List<String> failMsgs = new ArrayList<>();
+        for (List<WorkTaskParams> group : groupedTasks.values()) {
+            if (group == null || group.isEmpty()) {
+                continue;
+            }
+
+            // 鍚屼竴缁勫唴鐨勪换鍔$被鍨嬩竴鑷达紝鍥犳鍙栫涓�鏉″嵆鍙‘瀹氭湰缁勫簲璇ヨ皟鐢ㄥ摢涓� WCS 鎺ュ彛銆�
+            String path = resolveTaskPath(group.get(0));
+            Map<String, Object> payload = new HashMap<>();
+            // WCS 鎵归噺涓嬪彂鎶ユ枃缁熶竴浣跨敤 {"tasks":[...]} 缁撴瀯銆�
+            payload.put("tasks", 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);
+            }
+        }
+
+        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);
     }
 
     /**
@@ -206,6 +273,180 @@
         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) {
diff --git a/src/main/java/com/zy/asrs/controller/OpenController.java b/src/main/java/com/zy/asrs/controller/OpenController.java
index be2808f..deba829 100644
--- a/src/main/java/com/zy/asrs/controller/OpenController.java
+++ b/src/main/java/com/zy/asrs/controller/OpenController.java
@@ -14,6 +14,8 @@
 import com.zy.asrs.service.OpenService;
 import com.zy.asrs.service.WaitPakinService;
 import com.zy.asrs.service.WrkDetlService;
+import com.zy.asrs.service.WrkMastLogService;
+import com.zy.asrs.service.WrkMastService;
 import com.zy.common.model.DetlDto;
 import com.zy.common.model.LocDetlDto;
 import com.zy.common.model.enums.WorkNoType;
@@ -49,6 +51,10 @@
     private WaitPakinService waitPakinService;
     @Autowired
     private WrkDetlService wrkDetlService;
+    @Autowired
+    private WrkMastService wrkMastService;
+    @Autowired
+    private WrkMastLogService wrkMastLogService;
 
 //    @PostMapping("/order/matSync/default/v1")
 ////    @AppAuth(memo = "鍟嗗搧淇℃伅鍚屾鎺ュ彛")
@@ -466,6 +472,35 @@
 
     @PostMapping("/outOrder")
     public synchronized R outOrder (@RequestBody ArrayList<OutTaskParam> params){
+        if (Cools.isEmpty(params)) {
+            return R.error("璇锋眰鍙傛暟涓嶈兘涓虹┖");
+        }
+        Set<String> orderIds = new LinkedHashSet<>();
+        for (OutTaskParam outTaskParam : params) {
+            if (Cools.isEmpty(outTaskParam) || Cools.isEmpty(outTaskParam.getOrderId())) {
+                return R.error("鍑哄簱鍗曞彿涓嶈兘涓虹┖");
+            }
+            orderIds.add(outTaskParam.getOrderId());
+        }
+        if (!orderIds.isEmpty()) {
+            Set<String> existedOrderIds = new LinkedHashSet<>();
+            List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("user_no", orderIds));
+            for (WrkMast wrkMast : wrkMasts) {
+                if (!Cools.isEmpty(wrkMast.getUserNo())) {
+                    existedOrderIds.add(wrkMast.getUserNo());
+                }
+            }
+            List<WrkMastLog> wrkMastLogs = wrkMastLogService.selectList(new EntityWrapper<WrkMastLog>().in("user_no", orderIds));
+            for (WrkMastLog wrkMastLog : wrkMastLogs) {
+                if (!Cools.isEmpty(wrkMastLog.getUserNo())) {
+                    existedOrderIds.add(wrkMastLog.getUserNo());
+                }
+            }
+            if (!existedOrderIds.isEmpty()) {
+                return R.error("鍑哄簱鍗曞彿宸插瓨鍦ㄤ换鍔℃。鎴栦换鍔″巻鍙叉。锛�" + String.join("锛�", existedOrderIds));
+            }
+        }
+
         List<OutTaskParam> errorOutOrders = Lists.newArrayList();
         List<OutTaskParam> validOutOrders = Lists.newArrayList();
         for (OutTaskParam outTaskParam : params) {
diff --git a/src/main/java/com/zy/asrs/mapper/RowLastnoMapper.java b/src/main/java/com/zy/asrs/mapper/RowLastnoMapper.java
index f683f26..c627e86 100644
--- a/src/main/java/com/zy/asrs/mapper/RowLastnoMapper.java
+++ b/src/main/java/com/zy/asrs/mapper/RowLastnoMapper.java
@@ -13,7 +13,7 @@
 
     RowLastno findBySection(Integer row);
 
-    @Update("update asr_row_lastno set wrk_mk = 1 where whs_type = 1")
+    @Update("update asr_row_lastno set crn_qty = 1 where whs_type = 2")
     void updateInitWrkMk();
 
     @Update("update asr_row_lastno set wrk_mk = 1 where whs_type = #{whs}")
diff --git a/src/main/java/com/zy/asrs/task/WorkMastScheduler.java b/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
index d0a61ca..b61a4aa 100644
--- a/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
+++ b/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
@@ -17,6 +17,7 @@
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
@@ -57,35 +58,51 @@
     }
 
     /**
-     * 浠诲姟鑷姩涓嬪彂
+     * 浠诲姟鑷姩涓嬪彂銆�
+     * <p>
+     * 璋冨害鍣ㄥ彧璐熻矗浠庡伐浣滄。涓寫鍑衡�滃綋鍓嶅厑璁镐笅鍙戔�濈殑浠诲姟锛屽苟灏嗗叾杞崲鎴� WCS 鎺ュ彛闇�瑕佺殑鎶ユ枃缁撴瀯锛�
+     * 鐪熸鐨勬壒閲忓垎缁勩�佽皟鐢� WCS銆佷互鍙婁笅鍙戞垚鍔熷悗鐨勭姸鎬佹帹杩涢兘鏀惧湪 service 灞傜粺涓�澶勭悊銆�
+     * <p>
+     * 褰撳墠鎵归噺涓嬪彂鐨勫綊骞剁淮搴︽槸锛�
+     * 1. WCS鎺ュ彛璺緞锛堝叆搴�/鍑哄簱/绉诲簱涓嶈兘娣峰彂锛夛紱
+     * 2. work_mast.user_no锛堢浉鍚� userNo 鐨勪换鍔″繀椤绘斁鍒板悓涓�鎵规涓�璧蜂笂鎶ワ級銆�
      *
      * @author Ryan
      * @date 2026/1/10 14:42
      */
     @Scheduled(cron = "0/3 * * * * ? ")
     private void autoPubTasks() {
+        // 浠呭鐞嗗緟涓嬪彂/宸茬敓鎴愪笅鍙戝彿鐨勫伐浣滄。銆�
         List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("wrk_sts", Arrays.asList(1L, 11L)));
         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")) {
                 continue;
             }
+
+            // WMS 搴撲綅缂栫爜杞崲鎴� WCS 鍙瘑鍒殑搴撲綅缂栫爜銆�
             String wcsSourceLocNo = Cools.isEmpty(wrkMast.getSourceLocNo()) ? "" : Utils.WMSLocToWCSLoc(wrkMast.getSourceLocNo());
             String wcsLocNo = Cools.isEmpty(wrkMast.getLocNo()) ? "" : Utils.WMSLocToWCSLoc(wrkMast.getLocNo());
             WorkTaskParams params = new WorkTaskParams();
-            //鍑哄簱鍜岀Щ搴�
-            if(wrkMast.getIoType()==101&&!Cools.isEmpty(wrkMast.getStaNo())&& !wrkMast.getStaNo().equals("0")) {
+
+            // 101: 鍑哄簱銆傛澶� batch 瀛楁鎵胯浇 userNo锛屽悗缁� service 灞備細鎹鎶婄浉鍚� userNo 鐨勪换鍔″苟鍒颁竴鎵广��
+            if(wrkMast.getIoType()==101) {
                 params.setType("out")
                         .setTaskNo(wrkMast.getWrkNo()+"")
                         .setLocNo(wcsSourceLocNo)
                         .setStaNo(String.valueOf(wrkMast.getStaNo()))
                         .setTaskPri(wrkMast.getIoPri().intValue())
-                        .setBatch(1)
+                        .setBatch(wrkMast.getUserNo())
                         .setBatchSeq(wrkMast.getPltType())
                         .setBarcode(wrkMast.getBarcode());
+            // 2: 鍏ュ簱銆傚叆搴撴帴鍙d娇鐢� sourceStaNo + 鐩爣搴撲綅銆�
             }else if(wrkMast.getIoType()==2&& !Cools.isEmpty(wrkMast.getSourceStaNo())){
                 params.setType("in")
                         .setTaskNo(wrkMast.getWrkNo()+"")
@@ -93,6 +110,7 @@
                         .setLocNo(wcsLocNo)
                         .setTaskPri(wrkMast.getIoPri().intValue())
                         .setBarcode(wrkMast.getBarcode());
+            // 鍏朵綑璧扮Щ搴撴帴鍙o紝婧愬簱浣嶅拰鐩爣搴撲綅閮介渶瑕佸甫缁� WCS銆�
             } else {
                 params.setType("move")
                         .setTaskNo(wrkMast.getWrkNo()+"")
@@ -100,11 +118,17 @@
                         .setLocNo(wcsLocNo)
                         .setBarcode(wrkMast.getBarcode());
             }
-            R r = wcsApiService.pubWrkToWcs(params);
-            if (r.get("code").equals(200)){
-                break;
-            }
-        };
+            paramsList.add(params);
+        }
+        if (paramsList.isEmpty()) {
+            return;
+        }
+
+        // service 灞備細缁х画鎸夆�滄帴鍙h矾寰� + userNo鈥濆垎缁勫悗鍐嶆壒閲忎笂鎶ャ��
+        R r = wcsApiService.pubWrksToWcs(paramsList);
+        if (!r.get("code").equals(200)) {
+            log.warn("鎵归噺涓嬪彂浠诲姟鍒癢CS澶辫触, result={}", r);
+        }
     }
 
 }
diff --git a/src/main/java/com/zy/asrs/utils/VersionUtils.java b/src/main/java/com/zy/asrs/utils/VersionUtils.java
index bdf8dd8..3734e81 100644
--- a/src/main/java/com/zy/asrs/utils/VersionUtils.java
+++ b/src/main/java/com/zy/asrs/utils/VersionUtils.java
@@ -28,7 +28,19 @@
      **/
     public static boolean locMoveCheckLocTypeComplete(LocMast loc, LocTypeDto dto) {
         // 濡傛灉婧愬簱浣嶆槸楂樺簱浣嶏紝鐩爣搴撲綅鏄綆搴撲綅
-        return dto.getLocType1().equals(loc.getLocType1());
+        if (dto == null || loc == null) {
+            return false;
+        }
+        if (dto.getLocType1() != null && dto.getLocType1() > 0 && !dto.getLocType1().equals(loc.getLocType1())) {
+            return false;
+        }
+        if (dto.getLocType2() != null && dto.getLocType2() > 0 && !dto.getLocType2().equals(loc.getLocType2())) {
+            return false;
+        }
+        if (dto.getLocType3() != null && dto.getLocType3() > 0 && !dto.getLocType3().equals(loc.getLocType3())) {
+            return false;
+        }
+        return true;
     }
 
 }
diff --git a/src/main/java/com/zy/common/service/CommonService.java b/src/main/java/com/zy/common/service/CommonService.java
index 59f9d0c..6aa6b8c 100644
--- a/src/main/java/com/zy/common/service/CommonService.java
+++ b/src/main/java/com/zy/common/service/CommonService.java
@@ -42,6 +42,23 @@
 public class CommonService {
     private static final int MIN_SPARE_LOC_COUNT = 2;
 
+    private static class Run2AreaSearchResult {
+        private final LocMast locMast;
+        private final RowLastno rowLastno;
+        private final List<Integer> runnableCrnNos;
+
+        /**
+         * @param locMast 鍛戒腑鐨勭┖搴撲綅
+         * @param rowLastno 鍛戒腑搴撳尯瀵瑰簲鐨勮疆璇㈡父鏍囪褰�
+         * @param runnableCrnNos 褰撳墠搴撳尯鍙弬涓庤疆璇㈢殑鍫嗗灈鏈洪『搴�
+         */
+        private Run2AreaSearchResult(LocMast locMast, RowLastno rowLastno, List<Integer> runnableCrnNos) {
+            this.locMast = locMast;
+            this.rowLastno = rowLastno;
+            this.runnableCrnNos = runnableCrnNos;
+        }
+    }
+
     @Autowired
     private WrkMastService wrkMastService;
     @Autowired
@@ -138,6 +155,7 @@
     @Transactional
     public StartupDto getLocNo(Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto, List<Integer> recommendRows) {
         try {
+            locTypeDto = normalizeLocTypeDto(staDescId, findLocNoAttributeVo, locTypeDto);
             Integer whsType = Utils.GetWhsType(sourceStaNo);
             RowLastno rowLastno = rowLastnoService.selectById(whsType);
             RowLastnoType rowLastnoType = rowLastnoTypeService.selectById(rowLastno.getTypeId());
@@ -169,6 +187,58 @@
             log.error("绔欑偣={} 鏌ユ壘搴撲綅寮傚父", sourceStaNo, e);
             throw new CoolException("绔欑偣=" + sourceStaNo + " 鏌ユ壘搴撲綅澶辫触");
         }
+    }
+
+    /**
+     * 绌烘墭鐩樿瘑鍒鍒欙細
+     * 1. 浠ョ粍鎵樻。鐗╂枡缂栫爜 matnr=emptyPallet 涓轰富锛屼笉鍐嶄緷璧� ioType=10銆�
+     * 2. 淇濈暀 staDescId=10 鐨勫吋瀹瑰垽鏂紝閬垮厤鏃ч摼璺繕鏈垏鎹㈡椂琛屼负绐佸彉銆�
+     */
+    private boolean isEmptyPalletRequest(Integer staDescId, FindLocNoAttributeVo findLocNoAttributeVo) {
+        if (findLocNoAttributeVo != null && "emptyPallet".equalsIgnoreCase(findLocNoAttributeVo.getMatnr())) {
+            return true;
+        }
+        return staDescId != null && staDescId == 10;
+    }
+
+    /**
+     * 缁熶竴鏁寸悊鍏ュ簱瑙勬牸锛岄伩鍏嶄笉鍚屽叆鍙d紶鍏ョ殑 locType 涓嶄竴鑷淬��
+     *
+     * 绌烘墭鐩樼殑搴撲綅绛栫暐鏈変袱娈碉細
+     * 1. 棣栬疆鍙檺鍒� loc_type2=1锛岃〃绀轰紭鍏堟壘绐勫簱浣嶃��
+     * 2. 棣栬疆涓嶉檺鍒� loc_type1锛岄珮浣庝綅閮藉厑璁稿弬涓庢悳绱€��
+     *
+     * 杩欐牱鍋氱殑鍘熷洜鏄幇鍦哄彛寰勫凡缁忔敼鎴愨�滃厛鎵剧獎搴撲綅鈥濓紝鑰屼笉鏄�滃厛鎵句綆浣嶇獎搴撲綅鈥濄��
+     * 鍥犳杩欓噷浼氫富鍔ㄦ竻绌� locType1锛岄槻姝㈣绔欑偣榛樿鍊煎甫鎴愪綆浣嶄紭鍏堛��
+     */
+    private LocTypeDto normalizeLocTypeDto(Integer staDescId, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto) {
+        if (!isEmptyPalletRequest(staDescId, findLocNoAttributeVo)) {
+            return locTypeDto;
+        }
+        if (findLocNoAttributeVo != null && Cools.isEmpty(findLocNoAttributeVo.getMatnr())) {
+            findLocNoAttributeVo.setMatnr("emptyPallet");
+        }
+        LocTypeDto normalizedLocTypeDto = locTypeDto == null ? new LocTypeDto() : locTypeDto;
+        // 绌烘墭鐩橀杞笉闄愬埗楂樹綆浣嶏紝鍙繚鐣欌�滅獎搴撲綅浼樺厛鈥濈殑绾︽潫銆�
+        normalizedLocTypeDto.setLocType1(null);
+        normalizedLocTypeDto.setLocType2((short) 1);
+        return normalizedLocTypeDto;
+    }
+
+    private Wrapper<LocMast> applyLocTypeFilters(Wrapper<LocMast> wrapper, LocTypeDto locTypeDto, boolean includeLocType1) {
+        if (wrapper == null || locTypeDto == null) {
+            return wrapper;
+        }
+        if (includeLocType1 && locTypeDto.getLocType1() != null && locTypeDto.getLocType1() > 0) {
+            wrapper.eq("loc_type1", locTypeDto.getLocType1());
+        }
+        if (locTypeDto.getLocType2() != null && locTypeDto.getLocType2() > 0) {
+            wrapper.eq("loc_type2", locTypeDto.getLocType2());
+        }
+        if (locTypeDto.getLocType3() != null && locTypeDto.getLocType3() > 0) {
+            wrapper.eq("loc_type3", locTypeDto.getLocType3());
+        }
+        return wrapper;
     }
 
     private Integer resolvePreferredArea(Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo) {
@@ -345,14 +415,14 @@
             if (row == null) {
                 continue;
             }
-            List<LocMast> locMasts = locMastService.selectList(new EntityWrapper<LocMast>()
+            Wrapper<LocMast> wrapper = new EntityWrapper<LocMast>()
                     .eq("row1", row)
                     .ge("bay1", startBay)
                     .le("bay1", endBay)
-                    .eq("loc_sts", "O")
-                    .eq("loc_type1", locTypeDto.getLocType1())
-                    .orderBy("lev1", true)
-                    .orderBy("bay1", true));
+                    .eq("loc_sts", "O");
+            applyLocTypeFilters(wrapper, locTypeDto, true);
+            wrapper.orderBy("lev1", true).orderBy("bay1", true);
+            List<LocMast> locMasts = locMastService.selectList(wrapper);
             for (LocMast candidate : locMasts) {
                 if (!VersionUtils.locMoveCheckLocTypeComplete(candidate, locTypeDto)) {
                     continue;
@@ -468,6 +538,268 @@
         return orderedCrnNos;
     }
 
+    private Integer getCrnStartRow(RowLastno rowLastno, Integer crnNo) {
+        if (rowLastno == null || crnNo == null) {
+            return null;
+        }
+        Integer rowSpan = getCrnRowSpan(rowLastno.getTypeId());
+        if (rowSpan == null || rowSpan <= 0) {
+            return null;
+        }
+        int startCrnNo = rowLastno.getsCrnNo() == null ? 1 : rowLastno.getsCrnNo();
+        int startRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow();
+        if (crnNo < startCrnNo) {
+            return null;
+        }
+        return startRow + (crnNo - startCrnNo) * rowSpan;
+    }
+
+    /**
+     * 鍒ゆ柇鏌愬彴鍫嗗灈鏈烘槸鍚﹀彲浠ュ弬涓� run2 鍏ュ簱鎵句綅銆�
+     *
+     * routeRequired=true:
+     * 鏅�氬叆搴撳繀椤诲悓鏃舵弧瓒宠澶囧彲鍏ャ�佽澶囨棤鏁呴殰銆佸苟涓斿綋鍓嶆簮绔欏埌璇ュ爢鍨涙満瀛樺湪鏈夋晥鐩爣绔欒矾寰勩��
+     *
+     * routeRequired=false:
+     * 绌烘墭鐩樿法搴撳尯鎵句綅鏃讹紝鍙牎楠岃澶囦富妗c�佹晠闅滃拰鍙叆鐘舵�侊紝涓嶆妸 sta_desc 璺緞褰撴垚鎷︽埅鏉′欢銆�
+     * 杩欐牱鍋氭槸涓轰簡鍏堟弧瓒斥�滆兘鎵惧埌搴撲綅鈥濓紝鐩爣绔欒矾寰勭敱鍚庣画涓绘暟鎹ˉ榻愩��
+     */
+    private boolean canRun2CrnAcceptPakin(RowLastno rowLastno, Integer staDescId, Integer sourceStaNo, Integer crnNo) {
+        return canRun2CrnAcceptPakin(rowLastno, staDescId, sourceStaNo, crnNo, true);
+    }
+
+    private boolean canRun2CrnAcceptPakin(RowLastno rowLastno, Integer staDescId, Integer sourceStaNo, Integer crnNo, boolean routeRequired) {
+        if (crnNo == null) {
+            return false;
+        }
+        BasCrnp basCrnp = basCrnpService.selectById(crnNo);
+        if (Cools.isEmpty(basCrnp)) {
+            return false;
+        }
+        if (!"Y".equals(basCrnp.getInEnable())) {
+            return false;
+        }
+        if (basCrnp.getCrnSts() != null && basCrnp.getCrnSts() != 3) {
+            return false;
+        }
+        if (basCrnp.getCrnErr() != null && basCrnp.getCrnErr() != 0) {
+            return false;
+        }
+        if (!routeRequired) {
+            return true;
+        }
+        if (!Utils.BooleanWhsTypeSta(rowLastno, staDescId)) {
+            return true;
+        }
+        StaDesc staDesc = staDescService.selectOne(new EntityWrapper<StaDesc>()
+                .eq("type_no", staDescId)
+                .eq("stn_no", sourceStaNo)
+                .eq("crn_no", crnNo));
+        if (Cools.isEmpty(staDesc)) {
+            return false;
+        }
+        BasDevp targetSta = basDevpService.selectById(staDesc.getCrnStn());
+        return !Cools.isEmpty(targetSta) && "Y".equals(targetSta.getAutoing());
+    }
+
+    /**
+     * 鎸夋棦瀹氳疆璇㈤『搴忚繃婊ゅ嚭鐪熸鍙弬涓庢湰娆℃壘浣嶇殑鍫嗗灈鏈哄垪琛ㄣ��
+     */
+    private List<Integer> getOrderedRunnableRun2CrnNos(RowLastno rowLastno, Integer staDescId, Integer sourceStaNo, List<Integer> orderedCrnNos) {
+        return getOrderedRunnableRun2CrnNos(rowLastno, staDescId, sourceStaNo, orderedCrnNos, true);
+    }
+
+    private List<Integer> getOrderedRunnableRun2CrnNos(RowLastno rowLastno, Integer staDescId, Integer sourceStaNo, List<Integer> orderedCrnNos, boolean routeRequired) {
+        List<Integer> runnableCrnNos = new ArrayList<>();
+        if (Cools.isEmpty(orderedCrnNos)) {
+            return runnableCrnNos;
+        }
+        for (Integer candidateCrnNo : orderedCrnNos) {
+            if (canRun2CrnAcceptPakin(rowLastno, staDescId, sourceStaNo, candidateCrnNo, routeRequired)) {
+                runnableCrnNos.add(candidateCrnNo);
+            }
+        }
+        return runnableCrnNos;
+    }
+
+    /**
+     * 鏍规嵁鏈鍛戒腑鐨勫爢鍨涙満锛屾妸杞娓告爣鎺ㄨ繘鍒颁笅涓�鍙板彲鍙備笌杞鐨勫爢鍨涙満銆�
+     */
+    private int getNextRun2CurrentRow(RowLastno rowLastno, List<Integer> runnableCrnNos, Integer selectedCrnNo, int currentRow) {
+        if (Cools.isEmpty(runnableCrnNos) || selectedCrnNo == null) {
+            return getNextRun2CurrentRow(rowLastno, currentRow);
+        }
+        int index = runnableCrnNos.indexOf(selectedCrnNo);
+        if (index < 0) {
+            return getNextRun2CurrentRow(rowLastno, currentRow);
+        }
+        Integer nextCrnNo = runnableCrnNos.get((index + 1) % runnableCrnNos.size());
+        Integer nextRow = getCrnStartRow(rowLastno, nextCrnNo);
+        return nextRow == null ? getNextRun2CurrentRow(rowLastno, currentRow) : nextRow;
+    }
+
+    /**
+     * 鏋勯�犵┖鎵樼洏璺ㄥ簱鍖烘悳绱㈤『搴忥細
+     * 鍏堝綋鍓嶅簱鍖猴紝鍐嶄緷娆¤ˉ瓒冲叾瀹冨簱鍖猴紝閬垮厤閲嶅銆�
+     */
+    private List<Integer> buildAreaSearchOrder(Integer preferredArea) {
+        LinkedHashSet<Integer> areaOrder = new LinkedHashSet<>();
+        if (preferredArea != null && preferredArea >= 1 && preferredArea <= 3) {
+            areaOrder.add(preferredArea);
+        }
+        for (int area = 1; area <= 3; area++) {
+            areaOrder.add(area);
+        }
+        return new ArrayList<>(areaOrder);
+    }
+
+    /**
+     * 鏍规嵁搴撳尯鍙栬搴撳尯瀵瑰簲鐨勮疆璇㈣褰曘��
+     * 褰撳墠搴撻噷 1/2/3 搴撳尯姝eソ澶嶇敤浜� asr_row_lastno 鐨� whs_type 涓婚敭锛屽洜姝よ繖閲岀洿鎺ユ寜 area 鍙栥��
+     * 濡傛灉缂轰富鏁版嵁锛屽氨鍥為��鍒板綋鍓嶆簮绔欐墍鍦ㄤ粨鐨� rowLastno锛岄伩鍏嶇洿鎺ョ┖鎸囬拡銆�
+     */
+    private RowLastno getAreaRowLastno(Integer area, RowLastno defaultRowLastno) {
+        if (area != null && area > 0) {
+            RowLastno areaRowLastno = rowLastnoService.selectById(area);
+            if (!Cools.isEmpty(areaRowLastno)) {
+                return areaRowLastno;
+            }
+        }
+        return defaultRowLastno;
+    }
+
+    /**
+     * 绌烘墭鐩� run2 涓撶敤鎼滅储閾捐矾銆�
+     *
+     * 鎵ц椤哄簭锛�
+     * 1. 鍏堟寜绔欑偣缁戝畾搴撳尯鎵� loc_type2=1銆�
+     * 2. 褰撳墠搴撳尯娌℃湁锛屽啀鎸夊叾瀹冨簱鍖虹户缁壘 loc_type2=1銆�
+     * 3. 姣忎釜搴撳尯鍐呴儴閮芥寜璇ュ簱鍖鸿嚜宸辩殑 rowLastno/currentRow 鍋氳疆璇㈠潎鍒嗐��
+     *
+     * 杩欓噷鏁呮剰涓嶅鐢ㄦ櫘閫� run2 鐨勨�滄帹鑽愭帓 -> 褰撳墠搴撳尯鎺� -> 鍏跺畠鎺掆�濋�昏緫锛�
+     * 鍥犱负绌烘墭鐩樼殑涓氬姟鍙e緞宸茬粡鍒囨崲鎴愨�滄寜搴撳尯鎵惧爢鍨涙満鈥濓紝涓嶆槸鎸夋帹鑽愭帓鎵惧贩閬撱��
+     */
+    private Run2AreaSearchResult findEmptyPalletRun2AreaLoc(RowLastno defaultRowLastno, Integer staDescId, Integer sourceStaNo,
+                                                            StartupDto startupDto, Integer preferredArea, LocTypeDto locTypeDto) {
+        for (Integer area : buildAreaSearchOrder(preferredArea)) {
+            RowLastno areaRowLastno = getAreaRowLastno(area, defaultRowLastno);
+            if (Cools.isEmpty(areaRowLastno)) {
+                continue;
+            }
+            RowLastnoType areaRowLastnoType = rowLastnoTypeService.selectById(areaRowLastno.getTypeId());
+            if (Cools.isEmpty(areaRowLastnoType)) {
+                continue;
+            }
+            Integer areaStartCrnNo = resolveRun2CrnNo(areaRowLastno);
+            List<Integer> orderedAreaCrnNos = getOrderedCrnNos(areaRowLastno, areaStartCrnNo);
+            // 绌烘墭鐩樿法搴撳尯鏃跺彧绛涜澶囦富妗e拰鏁呴殰鐘舵�侊紝涓嶅己渚濊禆 sta_desc銆�
+            List<Integer> runnableAreaCrnNos = getOrderedRunnableRun2CrnNos(areaRowLastno, staDescId, sourceStaNo, orderedAreaCrnNos, false);
+            List<Integer> candidateCrnNos = Cools.isEmpty(runnableAreaCrnNos) ? orderedAreaCrnNos : runnableAreaCrnNos;
+            if (Cools.isEmpty(candidateCrnNos)) {
+                continue;
+            }
+            LocMast locMast = findRun2EmptyLocByCrnNos(areaRowLastno, areaRowLastnoType, candidateCrnNos, locTypeDto,
+                    staDescId, sourceStaNo, startupDto, area, "empty-pallet-area-" + area, false);
+            if (!Cools.isEmpty(locMast)) {
+                return new Run2AreaSearchResult(locMast, areaRowLastno, candidateCrnNos);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 绌烘墭鐩樺懡涓簱浣嶅悗锛屾寜鍛戒腑搴撳尯鍥炲啓璇ュ簱鍖鸿嚜宸辩殑杞娓告爣銆�
+     * 杩欐牱 A/B/C 涓変釜搴撳尯浼氬垎鍒淮鎶ゅ悇鑷殑鈥滀笅涓�鍙板爢鍨涙満鈥濓紝涓嶄細浜掔浉瑕嗙洊銆�
+     */
+    private void advanceEmptyPalletRun2Cursor(Run2AreaSearchResult searchResult, LocMast locMast) {
+        if (searchResult == null || searchResult.rowLastno == null || locMast == null) {
+            return;
+        }
+        RowLastno updateRowLastno = searchResult.rowLastno;
+        int updateCurRow = updateRowLastno.getCurrentRow() == null || updateRowLastno.getCurrentRow() == 0
+                ? (updateRowLastno.getsRow() == null ? 1 : updateRowLastno.getsRow())
+                : updateRowLastno.getCurrentRow();
+        updateCurRow = getNextRun2CurrentRow(updateRowLastno, searchResult.runnableCrnNos, locMast.getCrnNo(), updateCurRow);
+        updateRowLastno.setCurrentRow(updateCurRow);
+        rowLastnoService.updateById(updateRowLastno);
+    }
+
+    /**
+     * 鏅�� run2 鐨勬帹鑽愭帓浼樺厛闃舵銆�
+     *
+     * 鎺ㄨ崘鎺掑彧瀵规櫘閫氱墿鏂欑敓鏁堬紝绌烘墭鐩樺凡缁忓垏鎹㈡垚鈥滄寜搴撳尯杞鍫嗗灈鏈衡�濈殑瑙勫垯锛�
+     * 鍥犳杩欓噷浼氱洿鎺ヨ烦杩囩┖鎵樼洏璇锋眰锛岄伩鍏� row 鍙傛暟鎶婄┖鎵樼洏閲嶆柊甯﹀洖鏃ч�昏緫銆�
+     */
+    private LocMast findRun2RecommendLoc(RowLastno rowLastno, RowLastnoType rowLastnoType, boolean emptyPalletRequest,
+                                         List<Integer> recommendRows, LocTypeDto locTypeDto, Integer staDescId,
+                                         Integer sourceStaNo, StartupDto startupDto, Integer preferredArea,
+                                         List<Integer> triedCrnNos) {
+        if (emptyPalletRequest) {
+            return null;
+        }
+        List<Integer> recommendCrnNos = mapRowsToCrnNos(rowLastno, recommendRows);
+        if (Cools.isEmpty(recommendCrnNos)) {
+            return null;
+        }
+        LocMast locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, recommendCrnNos, locTypeDto,
+                staDescId, sourceStaNo, startupDto, preferredArea, "recommend");
+        triedCrnNos.addAll(recommendCrnNos);
+        return locMast;
+    }
+
+    /**
+     * 鏅�氱墿鏂� run2 鎵句綅涓绘祦绋嬨��
+     *
+     * 鎵ц椤哄簭锛�
+     * 1. 鍏堢湅绔欑偣鏄惁閰嶇疆浜嗏�滃爢鍨涙満 + 搴撲綅绫诲瀷鈥濅紭鍏堢骇銆�
+     * 2. 娌¢厤搴撳尯鏃讹紝鐩存帴鎸夊綋鍓� run2 杞椤哄簭鎵句綅銆�
+     * 3. 閰嶄簡搴撳尯鏃讹紝鍏堝湪褰撳墠搴撳尯鎵撅紝鍐嶅洖閫�鍒板叾瀹冨簱鍖恒��
+     */
+    private LocMast findNormalRun2Loc(RowLastno rowLastno, RowLastnoType rowLastnoType, Integer sourceStaNo,
+                                      Integer staDescId, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto,
+                                      StartupDto startupDto, Integer preferredArea, List<Integer> orderedCrnNos,
+                                      List<Integer> triedCrnNos) {
+        List<Map<String, Integer>> stationCrnLocTypes = Utils.getStationStorageAreaName(
+                sourceStaNo,
+                locTypeDto == null || locTypeDto.getLocType1() == null ? null : locTypeDto.getLocType1().intValue(),
+                findLocNoAttributeVo == null ? null : findLocNoAttributeVo.getMatnr());
+        if (!Cools.isEmpty(stationCrnLocTypes)) {
+            return findRun2EmptyLocByCrnLocTypeEntries(rowLastno, rowLastnoType, stationCrnLocTypes,
+                    locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "station-priority");
+        }
+        if (preferredArea == null) {
+            List<Integer> defaultCrnNos = new ArrayList<>(orderedCrnNos);
+            defaultCrnNos.removeAll(triedCrnNos);
+            return findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, defaultCrnNos, locTypeDto,
+                    staDescId, sourceStaNo, startupDto, preferredArea, "default");
+        }
+        List<Integer> preferredCrnNos = filterCrnNosByRows(rowLastno, orderedCrnNos, getRun2AreaRows(preferredArea, rowLastno));
+        preferredCrnNos.removeAll(triedCrnNos);
+        LocMast locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, preferredCrnNos, locTypeDto,
+                staDescId, sourceStaNo, startupDto, preferredArea, "preferred-area");
+        if (!Cools.isEmpty(locMast)) {
+            return locMast;
+        }
+        List<Integer> fallbackCrnNos = filterCrnNosByRows(rowLastno, orderedCrnNos, getRun2FallbackRows(rowLastno));
+        fallbackCrnNos.removeAll(triedCrnNos);
+        fallbackCrnNos.removeAll(preferredCrnNos);
+        return findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, fallbackCrnNos, locTypeDto,
+                staDescId, sourceStaNo, startupDto, preferredArea, "fallback-area");
+    }
+
+    /**
+     * 鏅�氱墿鏂欏懡涓簱浣嶅悗锛屾部鐢� run2 鍘熸湁鐨勫叏浠撹疆璇㈡父鏍囨帹杩涙柟寮忋��
+     */
+    private void advanceNormalRun2Cursor(RowLastno rowLastno, int curRow) {
+        if (rowLastno == null) {
+            return;
+        }
+        int updateCurRow = curRow == 0 ? (rowLastno.getsRow() == null ? 1 : rowLastno.getsRow()) : curRow;
+        updateCurRow = getNextRun2CurrentRow(rowLastno, updateCurRow);
+        rowLastno.setCurrentRow(updateCurRow);
+        rowLastnoService.updateById(rowLastno);
+    }
+
     private List<Integer> filterCrnNosByRows(RowLastno rowLastno, List<Integer> orderedCrnNos, List<Integer> rows) {
         if (Cools.isEmpty(rows)) {
             return new ArrayList<>(orderedCrnNos);
@@ -552,9 +884,26 @@
                 JSON.toJSONString(locTypeDto));
     }
 
+    /**
+     * 鎸夌粰瀹氬爢鍨涙満椤哄簭渚濇鎵剧┖搴撲綅銆�
+     *
+     * 杩欓噷鍚屾椂鎵挎媴涓夌被杩囨护锛�
+     * 1. 璁惧渚ц繃婊わ細鍫嗗灈鏈烘晠闅滄垨涓嶅瓨鍦ㄦ椂鐩存帴璺宠繃銆�
+     * 2. 璺緞渚ц繃婊わ細褰撳墠婧愮珯鍒拌鍫嗗灈鏈虹洰鏍囩珯鏃犺矾寰勬椂璺宠繃銆�
+     * 3. 搴撲綅渚ц繃婊わ細鏃犵┖搴撲綅鎴栧簱浣嶈鏍间笉鍖归厤鏃惰烦杩囥��
+     *
+     * 瀵圭┖鎵樼洏鏉ヨ锛宑andidateCrnNos 鐢� run2 鐨勮疆璇㈤『搴忕敓鎴愶紝鍥犳澶╃劧鍏峰鈥滃潎鍒嗗埌姣忎釜鍫嗗灈鏈衡�濈殑鏁堟灉銆�
+     */
     private LocMast findRun2EmptyLocByCrnNos(RowLastno rowLastno, RowLastnoType rowLastnoType, List<Integer> candidateCrnNos,
                                              LocTypeDto locTypeDto, Integer staDescId, Integer sourceStaNo, StartupDto startupDto,
                                              Integer preferredArea, String stage) {
+        return findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, candidateCrnNos, locTypeDto,
+                staDescId, sourceStaNo, startupDto, preferredArea, stage, true);
+    }
+
+    private LocMast findRun2EmptyLocByCrnNos(RowLastno rowLastno, RowLastnoType rowLastnoType, List<Integer> candidateCrnNos,
+                                             LocTypeDto locTypeDto, Integer staDescId, Integer sourceStaNo, StartupDto startupDto,
+                                             Integer preferredArea, String stage, boolean routeRequired) {
         if (Cools.isEmpty(candidateCrnNos)) {
             log.warn("run2 skip empty candidate list. stage={}, sourceStaNo={}, preferredArea={}, spec={}",
                     stage, sourceStaNo, preferredArea, JSON.toJSONString(locTypeDto));
@@ -570,16 +919,15 @@
                 continue;
             }
             Integer targetStaNo = resolveTargetStaNo(rowLastno, staDescId, sourceStaNo, candidateCrnNo);
-            if (Utils.BooleanWhsTypeSta(rowLastno, staDescId) && targetStaNo == null) {
+            if (routeRequired && Utils.BooleanWhsTypeSta(rowLastno, staDescId) && targetStaNo == null) {
                 routeBlockedCrns.add(candidateCrnNo);
                 continue;
             }
             Wrapper<LocMast> openWrapper = new EntityWrapper<LocMast>()
                     .eq("crn_no", candidateCrnNo)
-                    .eq("loc_sts", "O")
-                    .eq("loc_type1", locTypeDto.getLocType1())
-                    .orderBy("lev1")
-                    .orderBy("bay1");
+                    .eq("loc_sts", "O");
+            applyLocTypeFilters(openWrapper, locTypeDto, true);
+            openWrapper.orderBy("lev1").orderBy("bay1");
             LocMast anyOpenLoc = locMastService.selectOne(openWrapper);
             if (Cools.isEmpty(anyOpenLoc)) {
                 noEmptyCrns.add(candidateCrnNo);
@@ -587,13 +935,9 @@
             }
             Wrapper<LocMast> wrapper = new EntityWrapper<LocMast>()
                     .eq("crn_no", candidateCrnNo)
-                    .eq("loc_sts", "O")
-                    .eq("loc_type1", locTypeDto.getLocType1())
-                    .orderBy("lev1")
-                    .orderBy("bay1");
-            if (locTypeDto != null && locTypeDto.getLocType1() != null) {
-                wrapper.eq("loc_type1", locTypeDto.getLocType1());
-            }
+                    .eq("loc_sts", "O");
+            applyLocTypeFilters(wrapper, locTypeDto, true);
+            wrapper.orderBy("lev1").orderBy("bay1");
             LocMast candidateLoc = locMastService.selectOne(wrapper);
             if (Cools.isEmpty(candidateLoc) || (locTypeDto != null && !VersionUtils.locMoveCheckLocTypeComplete(candidateLoc, locTypeDto))) {
                 locTypeBlockedCrns.add(candidateCrnNo);
@@ -716,6 +1060,7 @@
         if (candidateLocType1 != null) {
             wrapper.eq("loc_type1", candidateLocType1);
         }
+        applyLocTypeFilters(wrapper, locTypeDto, false);
         // 鍗曚几鍫嗗灈鏈烘寜灞傘�佸垪閫掑椤哄簭鎵剧涓�涓┖搴撲綅銆�
         if (rowLastnoType != null && rowLastnoType.getType() != null && (rowLastnoType.getType() == 1 || rowLastnoType.getType() == 2)) {
             wrapper.orderBy("lev1", true).orderBy("bay1", true);
@@ -859,11 +1204,8 @@
         }
         Wrapper<LocMast> wrapper = new EntityWrapper<LocMast>()
                 .in("row1", searchRows)
-                .eq("loc_sts", "O")
-                .eq("whs_type", rowLastnoType.getType().longValue());
-        if (locTypeDto != null && locTypeDto.getLocType1() != null) {
-            wrapper.eq("loc_type1", locTypeDto.getLocType1());
-        }
+                .eq("loc_sts", "O");
+        applyLocTypeFilters(wrapper, locTypeDto, true);
         return locMastService.selectCount(wrapper);
     }
 
@@ -1054,6 +1396,44 @@
         compatibleLocTypeDto.setLocType3(locTypeDto.getLocType3());
         compatibleLocTypeDto.setSiteId(locTypeDto.getSiteId());
         return compatibleLocTypeDto;
+    }
+
+    /**
+     * 绌烘墭鐩樺吋瀹瑰洖閫�瑙勫垯锛�
+     * 褰撻杞� loc_type2=1 娌℃湁鍛戒腑绌哄簱浣嶆椂锛屽厑璁搁��鍖栧埌楂樹綅绌哄簱浣� loc_type1=2銆�
+     *
+     * 娉ㄦ剰杩欓噷涓嶄細缁х画淇濈暀 locType2=1銆�
+     * 涔熷氨鏄绗簩杞槸鈥滈珮浣嶄紭鍏堝厹搴曗�濓紝鑰屼笉鏄�滈珮浣嶇獎搴撲綅鍏滃簳鈥濄��
+     * 杩欐槸鎸夌収鐜板満鏈�鏂板彛寰勫疄鐜扮殑锛氱獎搴撲綅浼樺厛锛岀獎搴撲綅娌℃湁鏃跺啀鎵鹃珮浣嶇┖搴撲綅銆�
+     */
+    private LocTypeDto buildEmptyPalletCompatibleLocTypeDto(LocTypeDto locTypeDto) {
+        if (locTypeDto == null || locTypeDto.getLocType2() == null || locTypeDto.getLocType2() != 1) {
+            return null;
+        }
+        LocTypeDto compatibleLocTypeDto = new LocTypeDto();
+        compatibleLocTypeDto.setLocType1((short) 2);
+        compatibleLocTypeDto.setLocType3(locTypeDto.getLocType3());
+        compatibleLocTypeDto.setSiteId(locTypeDto.getSiteId());
+        return compatibleLocTypeDto;
+    }
+
+    /**
+     * 缁熶竴灏佽鎵惧簱浣嶅け璐ュ悗鐨勫吋瀹归噸璇曢『搴忋��
+     *
+     * 绌烘墭鐩橈細
+     * 鍏堟寜 loc_type2=1 鏌ユ壘锛屽け璐ュ悗閫�鍒� loc_type1=2銆�
+     *
+     * 闈炵┖鎵樼洏锛�
+     * 缁存寔鍘熻鍒欙紝浣庝綅澶辫触鍚庡啀鍚戦珮浣嶅吋瀹广��
+     */
+    private LocTypeDto buildRetryCompatibleLocTypeDto(Integer staDescId, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto) {
+        if (isEmptyPalletRequest(staDescId, findLocNoAttributeVo)) {
+            LocTypeDto emptyPalletCompatibleLocTypeDto = buildEmptyPalletCompatibleLocTypeDto(locTypeDto);
+            if (emptyPalletCompatibleLocTypeDto != null) {
+                return emptyPalletCompatibleLocTypeDto;
+            }
+        }
+        return buildUpwardCompatibleLocTypeDto(locTypeDto);
     }
 
 
@@ -1261,9 +1641,9 @@
                 times = times + 1;
                 return getLocNoRun(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, times);
             }
-            LocTypeDto compatibleLocTypeDto = buildUpwardCompatibleLocTypeDto(locTypeDto);
+            LocTypeDto compatibleLocTypeDto = buildRetryCompatibleLocTypeDto(staDescId, findLocNoAttributeVo, locTypeDto);
             if (compatibleLocTypeDto != null) {
-                log.warn("locType1 upward compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
+                log.warn("locType compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
                 return getLocNoRun(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, 0);
             }
             log.error("No empty location found. spec={}, times={}", JSON.toJSONString(locTypeDto), times);
@@ -1285,6 +1665,14 @@
         return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, null, times);
     }
 
+    /**
+     * run2 鍏ュ簱鎵句綅涓绘祦绋嬨��
+     *
+     * 褰撳墠鏂规硶鍙繚鐣欌�滅粍缁囨祦绋嬧�濆拰鈥滅粺涓�鏀跺彛鈥濈殑鑱岃矗锛屽叿浣撶瓥鐣ユ媶鎴愮嫭绔嬫柟娉曪細
+     * 1. 鏅�氱墿鏂欙細鎺ㄨ崘鎺掍紭鍏� -> 绔欑偣浼樺厛搴撳尯/鍫嗗灈鏈� -> 鍏跺畠搴撳尯銆�
+     * 2. 绌烘墭鐩橈細浼樺厛搴撳尯 loc_type2=1 -> 鍏跺畠搴撳尯 loc_type2=1 -> loc_type1=2 鍏煎銆�
+     * 3. 鍛戒腑搴撲綅鍚庡垎鍒洖鍐欐櫘閫氱墿鏂欐父鏍囨垨绌烘墭鐩樺簱鍖烘父鏍囥��
+     */
     public StartupDto getLocNoRun2(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, List<Integer> recommendRows, int times) {
 
         int crnNo = 0;
@@ -1309,36 +1697,26 @@
         curRow = rowLastno.getCurrentRow();
         crnNo = resolveRun2CrnNo(rowLastno);
         Integer preferredArea = findLocNoAttributeVo.getOutArea();
+        boolean emptyPalletRequest = isEmptyPalletRequest(staDescId, findLocNoAttributeVo);
+        Run2AreaSearchResult emptyPalletAreaSearchResult = null;
 
         List<Integer> orderedCrnNos = getOrderedCrnNos(rowLastno, crnNo);
         List<Integer> triedCrnNos = new ArrayList<>();
-        List<Integer> recommendCrnNos = mapRowsToCrnNos(rowLastno, recommendRows);
-        if (!Cools.isEmpty(recommendCrnNos)) {
-            locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, recommendCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "recommend");
-            triedCrnNos.addAll(recommendCrnNos);
-        }
+        locMast = findRun2RecommendLoc(rowLastno, rowLastnoType, emptyPalletRequest, recommendRows, locTypeDto,
+                staDescId, sourceStaNo, startupDto, preferredArea, triedCrnNos);
         if (Cools.isEmpty(locMast)) {
-            List<Map<String, Integer>> stationCrnLocTypes = Utils.getStationStorageAreaName(
-                    sourceStaNo,
-                    locTypeDto == null || locTypeDto.getLocType1() == null ? null : locTypeDto.getLocType1().intValue(),
-                    findLocNoAttributeVo == null ? null : findLocNoAttributeVo.getMatnr());
-            if (!Cools.isEmpty(stationCrnLocTypes)) {
-                locMast = findRun2EmptyLocByCrnLocTypeEntries(rowLastno, rowLastnoType, stationCrnLocTypes,
-                        locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "station-priority");
-            } else if (preferredArea == null) {
-                List<Integer> defaultCrnNos = new ArrayList<>(orderedCrnNos);
-                defaultCrnNos.removeAll(triedCrnNos);
-                locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, defaultCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "default");
-            } else {
-                List<Integer> preferredCrnNos = filterCrnNosByRows(rowLastno, orderedCrnNos, getRun2AreaRows(preferredArea, rowLastno));
-                preferredCrnNos.removeAll(triedCrnNos);
-                locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, preferredCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "preferred-area");
-                if (Cools.isEmpty(locMast)) {
-                    List<Integer> fallbackCrnNos = filterCrnNosByRows(rowLastno, orderedCrnNos, getRun2FallbackRows(rowLastno));
-                    fallbackCrnNos.removeAll(triedCrnNos);
-                    fallbackCrnNos.removeAll(preferredCrnNos);
-                    locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, fallbackCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "fallback-area");
+            if (emptyPalletRequest) {
+                // 绌烘墭鐩樺崟鐙寜搴撳尯杞锛�
+                // 1. 褰撳墠搴撳尯鍏堟壘 loc_type2=1
+                // 2. 褰撳墠搴撳尯娌℃湁锛屽啀鎵惧叾浠栧簱鍖� loc_type2=1
+                // 3. 鍏ㄩ儴 narrow 閮芥病鏈夋椂锛屽啀閫�鍒� loc_type1=2
+                emptyPalletAreaSearchResult = findEmptyPalletRun2AreaLoc(rowLastno, staDescId, sourceStaNo, startupDto, preferredArea, locTypeDto);
+                if (!Cools.isEmpty(emptyPalletAreaSearchResult)) {
+                    locMast = emptyPalletAreaSearchResult.locMast;
                 }
+            } else {
+                locMast = findNormalRun2Loc(rowLastno, rowLastnoType, sourceStaNo, staDescId, findLocNoAttributeVo,
+                        locTypeDto, startupDto, preferredArea, orderedCrnNos, triedCrnNos);
             }
         }
 
@@ -1346,21 +1724,21 @@
             crnNo = locMast.getCrnNo();
             nearRow = locMast.getRow1();
         }
-        if (curRow == 0) {
-            curRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow();
+        if (emptyPalletRequest) {
+            advanceEmptyPalletRun2Cursor(emptyPalletAreaSearchResult, locMast);
+        } else {
+            advanceNormalRun2Cursor(rowLastno, curRow);
         }
-        curRow = getNextRun2CurrentRow(rowLastno, curRow);
-        rowLastno.setCurrentRow(curRow);
-        rowLastnoService.updateById(rowLastno);
 
         if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) {
-            if (times < rowCount * 2) {
+            if (!emptyPalletRequest && times < rowCount * 2) {
                 times = times + 1;
                 return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, recommendRows, times);
             }
-            LocTypeDto compatibleLocTypeDto = buildUpwardCompatibleLocTypeDto(locTypeDto);
+            LocTypeDto compatibleLocTypeDto = buildRetryCompatibleLocTypeDto(staDescId, findLocNoAttributeVo, locTypeDto);
             if (compatibleLocTypeDto != null) {
-                log.warn("locType1 upward compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
+                // 绗竴杞叏閮ㄥ爢鍨涙満閮芥病鎵惧埌鏃讹紝鍐嶈繘鍏ヨ鏍煎吋瀹归噸璇曪紝涓嶅湪鍗曚釜鍫嗗灈鏈哄唴灞�閮ㄩ��鍖栥��
+                log.warn("locType compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
                 return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, recommendRows, 0);
             }
             log.error("No empty location found. spec={}, times={}, preferredArea={}, nearRow={}", JSON.toJSONString(locTypeDto), times, preferredArea, nearRow);
@@ -1821,9 +2199,9 @@
                 times = times + 1;
                 return getLocNoRun4(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, times);
             }
-            LocTypeDto compatibleLocTypeDto = buildUpwardCompatibleLocTypeDto(locTypeDto);
+            LocTypeDto compatibleLocTypeDto = buildRetryCompatibleLocTypeDto(staDescId, findLocNoAttributeVo, locTypeDto);
             if (compatibleLocTypeDto != null) {
-                log.warn("locType1 upward compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
+                log.warn("locType compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
                 return getLocNoRun4(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, 0);
             }
             log.error("No empty location found. spec={}, times={}", JSON.toJSONString(locTypeDto), times);
@@ -1968,9 +2346,9 @@
                 times = times + 1;
                 return getLocNoRun5(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, recommendRows, times);
             }
-            LocTypeDto compatibleLocTypeDto = buildUpwardCompatibleLocTypeDto(locTypeDto);
+            LocTypeDto compatibleLocTypeDto = buildRetryCompatibleLocTypeDto(staDescId, findLocNoAttributeVo, locTypeDto);
             if (compatibleLocTypeDto != null) {
-                log.warn("locType1 upward compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
+                log.warn("locType compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto));
                 return getLocNoRun5(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, recommendRows, 0);
             }
             log.error("No empty location found. spec={}, times={}", JSON.toJSONString(locTypeDto), times);
diff --git a/src/main/java/com/zy/common/web/WcsController.java b/src/main/java/com/zy/common/web/WcsController.java
index 8f099ca..e56dfd1 100644
--- a/src/main/java/com/zy/common/web/WcsController.java
+++ b/src/main/java/com/zy/common/web/WcsController.java
@@ -158,8 +158,10 @@
         StartupDto dto = null;
         switch (param.getIoType()) {
             case 1://婊℃墭鐩樺叆搴�
+                dto = startupFullPutStoreAgv(param.getSourceStaNo(), param.getBarcode(), locTypeDto, param.getOutArea(), 1);
+                break;
             case 10://绌烘墭鐩樺叆搴�
-                dto = startupFullPutStoreAgv(param.getSourceStaNo(), param.getBarcode(), locTypeDto, param.getOutArea());
+                dto = startupFullPutStoreAgv(param.getSourceStaNo(), param.getBarcode(), locTypeDto, param.getOutArea(), 10);
                 break;
 //                dto = emptyPlateIn(param.getSourceStaNo(), locTypeDto, param.getBarcode());
 //                break;
@@ -396,14 +398,14 @@
      * 鍏ㄦ澘鍏ュ簱AGV
      */
     @Transactional
-    public StartupDto startupFullPutStoreAgv(Integer devpNo, String barcode, LocTypeDto locTypeDto, Integer outArea) {
+    public StartupDto startupFullPutStoreAgv(Integer devpNo, String barcode, LocTypeDto locTypeDto, Integer outArea, Integer staDescId) {
         // 婧愮珯鐐圭姸鎬佹娴�
 //        BasDevp sourceStaNo = basDevpService.checkSiteStatus(devpNo, true);
         // 妫�绱㈠簱浣�
         FindLocNoAttributeVo findLocNoAttributeVo = new FindLocNoAttributeVo();
         findLocNoAttributeVo.setOutArea(outArea);
 //        FindLocNoAttributeVo findLocNoAttributeVo = new FindLocNoAttributeVo(waitPakins.get(0));
-        StartupDto dto = commonService.getLocNo(1, devpNo, findLocNoAttributeVo, locTypeDto);
+        StartupDto dto = commonService.getLocNo(staDescId, devpNo, findLocNoAttributeVo, locTypeDto);
         // 鏇存柊鐩爣搴撲綅鐘舵��
         Date now = new Date();
         if (dto == null) {

--
Gitblit v1.9.1