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/common/service/CommonService.java |  500 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 439 insertions(+), 61 deletions(-)

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);

--
Gitblit v1.9.1