自动化立体仓库 - WMS系统
src/main/java/com/zy/asrs/utils/Utils.java
@@ -29,6 +29,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Locale;
/**
@@ -131,98 +132,220 @@
    }
    public static Integer getStationStorageArea(Integer stationId) {
    public static List<Integer> getStationStorageAreas(Integer stationId) {
        if (stationId == null || stationId <= 0) {
            return null;
            return new ArrayList<>();
        }
        BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class);
        BasDevp station = basDevpService.selectById(stationId);
        if (station == null) {
            return new ArrayList<>();
        }
        return parseStorageAreas(station.getArea());
    }
    public static Integer getStationStorageArea(Integer stationId) {
        List<Integer> storageAreas = getStationStorageAreas(stationId);
        if (Cools.isEmpty(storageAreas)) {
            return null;
        }
        return parseStorageArea(station.getArea());
        return storageAreas.get(0);
    }
    /**
     * 生成入库找库位时的堆垛机优先顺序。
     *
     * <p>处理规则:
     * 1. 先根据入库站点查询所属库区。
     * 2. 先提取该库区内的堆垛机,并按可用空库位过滤不可用堆垛机。
     * 3. 当 {@code locType1 = 1} 时,先返回低库位堆垛机,再把同批堆垛机的高库位追加到后面。
     * 4. 对不存在、故障、不可入以及无空库位的堆垛机直接剔除。
     *
     * <p>这里是只读排序工具,不再写回 {@code asr_row_lastno.crn_qty}。
     * {@code crn_qty} 在主数据里表示“堆垛机数量”,不能再被拿来当轮询游标,否则会把整仓配置写坏。
     * <p>当前语义已经切换为“站点第一优先池 + 第二优先池”的配置预览,
     * 不再沿用旧的 area 顺序。
     *
     * <p>返回结果中的每一项格式为:
     * {@code {crnNo: 堆垛机号, locType1: 库位高低类型}}
     * {@code {pool: 优先池编号, seq: 池内顺序, crnNo: 堆垛机号}}
     *
     * @param stationId 入库站点
     * @param locType1  目标库位高低类型,1=低库位,2=高库位
     * @param matnr     物料编码,传入 {@code emptyPallet} 时使用空板排序规则
     * @return 按优先级排好序的堆垛机列表
     * @param locType1  保留兼容参数,当前不参与排序
     * @param matnr     保留兼容参数,当前不参与排序
     * @return 按优先池顺序排好的堆垛机列表
     */
    public static List<Map<String, Integer>> getStationStorageAreaName(Integer stationId, Integer locType1, String matnr) {
        List<Map<String, Integer>> result = new ArrayList<>();
        // 先定位入库站点所属库区。
        Integer storageArea = getStationStorageArea(stationId);
        Integer whsType = GetWhsType(stationId);
        if (storageArea == null || whsType == null || whsType <= 0) {
        BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class);
        BasDevp station = basDevpService.selectById(stationId);
        if (station == null) {
            return result;
        }
        RowLastnoService rowLastnoService = SpringUtils.getBean(RowLastnoService.class);
        RowLastno rowLastno = rowLastnoService.selectById(whsType);
        if (rowLastno == null) {
            return result;
        }
        BasCrnpService basCrnpService = SpringUtils.getBean(BasCrnpService.class);
        LocMastService locMastService = SpringUtils.getBean(LocMastService.class);
        boolean emptyPallet = "emptyPallet".equalsIgnoreCase(matnr);
        // 先取当前库区对应的堆垛机。
        List<Integer> preferredCrnNos = getAreaCrnNos(storageArea, rowLastno);
        List<Integer> preferredAvailableCrnNos = getAvailableCrnNos(preferredCrnNos, locType1, emptyPallet, basCrnpService, locMastService);
        appendCrnLocTypeEntries(result, preferredAvailableCrnNos, locType1, emptyPallet, locMastService);
        appendCrnPoolEntries(result, 1, distinctCrnNos(station.getInFirstCrnCsv()));
        appendCrnPoolEntries(result, 2, distinctCrnNos(station.getInSecondCrnCsv()));
        return result;
    }
    private static void appendCrnLocTypeEntries(List<Map<String, Integer>> result, List<Integer> crnNos, Integer locType1, boolean emptyPallet, LocMastService locMastService) {
        Short normalizedLocType1 = normalizeLocType1(locType1);
        if (normalizedLocType1 == null) {
            appendCrnLocTypeEntries(result, crnNos, (short) 1, emptyPallet, locMastService);
            appendCrnLocTypeEntries(result, crnNos, (short) 2, emptyPallet, locMastService);
    public static List<Integer> parseCrnNos(String csv) {
        List<Integer> crnNos = new ArrayList<>();
        if (Cools.isEmpty(csv)) {
            return crnNos;
        }
        String normalized = csv.replace(",", ",")
                .replace(";", ",")
                .replace("、", ",")
                .replaceAll("\\s+", "");
        if (normalized.isEmpty()) {
            return crnNos;
        }
        for (String segment : normalized.split("[,;]")) {
            if (segment == null || segment.isEmpty()) {
                continue;
            }
            Integer crnNo = safeParseInt(segment);
            if (crnNo == null || crnNo <= 0) {
                throw new CoolException("堆垛机号格式错误:" + segment);
            }
            crnNos.add(crnNo);
        }
        return crnNos;
    }
    public static List<Integer> distinctCrnNos(String csv) {
        return distinctCrnNos(parseCrnNos(csv));
    }
    public static List<Integer> distinctCrnNos(List<Integer> crnNos) {
        List<Integer> result = new ArrayList<>();
        if (Cools.isEmpty(crnNos)) {
            return result;
        }
        LinkedHashSet<Integer> orderedCrnNos = new LinkedHashSet<>();
        for (Integer crnNo : crnNos) {
            if (crnNo == null || crnNo <= 0) {
                continue;
            }
            orderedCrnNos.add(crnNo);
        }
        result.addAll(orderedCrnNos);
        return result;
    }
    public static String normalizeCrnCsv(String csv) {
        return normalizeCrnCsv(parseCrnNos(csv));
    }
    public static String normalizeCrnCsv(List<Integer> crnNos) {
        return joinCrnNos(distinctCrnNos(crnNos));
    }
    public static String joinCrnNos(List<Integer> crnNos) {
        if (Cools.isEmpty(crnNos)) {
            return "";
        }
        StringBuilder builder = new StringBuilder();
        for (Integer crnNo : crnNos) {
            if (crnNo == null || crnNo <= 0) {
                continue;
            }
            if (builder.length() > 0) {
                builder.append(",");
            }
            builder.append(crnNo);
        }
        return builder.toString();
    }
    private static void appendCrnPoolEntries(List<Map<String, Integer>> result, int pool, List<Integer> crnNos) {
        if (result == null || Cools.isEmpty(crnNos)) {
            return;
        }
        appendCrnLocTypeEntries(result, crnNos, normalizedLocType1, emptyPallet, locMastService);
        if (normalizedLocType1 == 1) {
            appendCrnLocTypeEntries(result, crnNos, (short) 2, emptyPallet, locMastService);
        int seq = 1;
        for (Integer crnNo : crnNos) {
            if (crnNo == null || crnNo <= 0) {
                continue;
            }
            Map<String, Integer> item = new LinkedHashMap<>();
            item.put("pool", pool);
            item.put("seq", seq++);
            item.put("crnNo", crnNo);
            result.add(item);
        }
    }
    private static void appendCrnLocTypeEntries(List<Map<String, Integer>> result, List<Integer> crnNos, Short targetLocType1, boolean emptyPallet, LocMastService locMastService) {
    public static List<Integer> parseStorageAreas(String area) {
        List<Integer> areas = new ArrayList<>();
        if (Cools.isEmpty(area)) {
            return areas;
        }
        LinkedHashSet<Integer> orderedAreas = new LinkedHashSet<>();
        String normalized = area.replace(",", ",")
                .replace(";", ";")
                .replace("、", ",")
                .replaceAll("\\s+", "");
        if (normalized.isEmpty()) {
            return areas;
        }
        for (String segment : normalized.split("[,;]")) {
            if (segment == null || segment.isEmpty()) {
                continue;
            }
            Integer areaNo = parseStorageArea(segment);
            if (areaNo != null) {
                orderedAreas.add(areaNo);
            }
        }
        areas.addAll(orderedAreas);
        return areas;
    }
    public static String formatStorageArea(Integer area) {
        if (area == null) {
            return "";
        }
        switch (area) {
            case 1:
                return "A库区";
            case 2:
                return "B库区";
            case 3:
                return "C库区";
            default:
                return String.valueOf(area);
        }
    }
    private static void appendCrnLocTypeEntries(List<Map<String, Integer>> result, List<Integer> crnNos, Integer locType1,
                                                boolean emptyPallet, LocMastService locMastService, Integer area) {
        Short normalizedLocType1 = normalizeLocType1(locType1);
        if (normalizedLocType1 == null) {
            appendCrnLocTypeEntries(result, crnNos, (short) 1, emptyPallet, locMastService, area);
            appendCrnLocTypeEntries(result, crnNos, (short) 2, emptyPallet, locMastService, area);
            return;
        }
        appendCrnLocTypeEntries(result, crnNos, normalizedLocType1, emptyPallet, locMastService, area);
        if (normalizedLocType1 == 1) {
            appendCrnLocTypeEntries(result, crnNos, (short) 2, emptyPallet, locMastService, area);
        }
    }
    private static void appendCrnLocTypeEntries(List<Map<String, Integer>> result, List<Integer> crnNos, Short targetLocType1,
                                                boolean emptyPallet, LocMastService locMastService, Integer area) {
        if (targetLocType1 == null || Cools.isEmpty(crnNos)) {
            return;
        }
        for (Integer crnNo : crnNos) {
            if (!hasAvailableLoc(crnNo, targetLocType1, emptyPallet, locMastService) || containsCrnLocType(result, crnNo, targetLocType1)) {
            if (!hasAvailableLoc(crnNo, targetLocType1, emptyPallet, locMastService) || containsCrnLocType(result, area, crnNo, targetLocType1)) {
                continue;
            }
            Map<String, Integer> item = new LinkedHashMap<>();
            item.put("area", area);
            item.put("crnNo", crnNo);
            item.put("locType1", targetLocType1.intValue());
            result.add(item);
        }
    }
    private static boolean containsCrnLocType(List<Map<String, Integer>> result, Integer crnNo, Short locType1) {
    private static boolean containsCrnLocType(List<Map<String, Integer>> result, Integer area, Integer crnNo, Short locType1) {
        for (Map<String, Integer> item : result) {
            if (item == null) {
                continue;
            }
            if (crnNo.equals(item.get("crnNo")) && locType1.intValue() == item.get("locType1")) {
            if (crnNo.equals(item.get("crnNo"))
                    && locType1.intValue() == item.get("locType1")
                    && Objects.equals(area, item.get("area"))) {
                return true;
            }
        }
@@ -297,8 +420,6 @@
    private static Short normalizeLocType1(Integer locType1) {
        if (locType1 == null || (locType1 != 1 && locType1 != 2)) {
            return null;
        } else {
            locType1 = 2;
        }
        return locType1.shortValue();
    }