自动化立体仓库 - WMS系统
*
L
13 小时以前 dfed6fe975deaabb6c7ac424d7c0b7119543abba
*
1个文件已修改
3个文件已添加
947 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/GroupedLockerOptimizerUtils.java 466 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/MultiLockerOptimizerUtils.java 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/ToSortLineUtils.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java
@@ -12,6 +12,8 @@
import com.zy.asrs.service.impl.OrderDetlServiceImpl;
import com.zy.asrs.task.core.ReturnT;
import com.zy.asrs.task.handler.OrderToLineHandler;
import com.zy.asrs.utils.GroupedLockerOptimizerUtils;
import com.zy.asrs.utils.ToSortLineUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
@@ -35,7 +37,6 @@
    @Autowired
    private BasArmMastSignService basArmMastSignService;
    @Scheduled(cron = "0/3 * * * * ? ")
    private void orderToSortLine() {
        //获取未下发单据
@@ -47,94 +48,60 @@
        //遍历单据
        for (String orderNo : orderNos) {
            List<OrderDetl> orderDetls = orderDetlService.selectByOrderNo(orderNo);
            OrderToLine orderToLine = new OrderToLine();
            orderToLine.setOrderNo(orderNo);  //单据编号
            orderToLine.setCreateTime(System.currentTimeMillis());  //创建时间
            Long bindingTag = System.currentTimeMillis();//混搭标记
            List<OrderToLine.MatList> matLists = new ArrayList<>();
            List<OrderToLine.MatList> matListsRemainder = new ArrayList<>();
            List<OrderDetl> orderDetlsRemainder = new ArrayList<>();
            for (OrderDetl orderDetl : orderDetls) {
                Integer number =  basArmRulesService.getNumber(orderDetl.getWeight(),orderDetl.getVolume(),orderDetl.getManLength(),orderDetl.getWidth(),orderDetl.getHeight());
                if (number == null) {
                    BasArmRules basArmRules = new BasArmRules();
                    basArmRules.setMaterialHeight(orderDetl.getHeight());
                    basArmRules.setMaterialWeight(orderDetl.getWeight());
                    basArmRules.setMaterialLength(orderDetl.getManLength());
                    basArmRules.setMaterialWidth(orderDetl.getWidth());
                    basArmRulesService.insert(basArmRules);
                    return;
                } else if (number == 0){
                    return;
                }
                Double anfme = orderDetl.getAnfme();
                Double remainder = anfme % number;  //取余   余数混搭
//                remainder = 0D;
                if(!remainder.equals(0D)){
                    // 组装物料信息
                    OrderToLine.MatList matMix = new OrderToLine.MatList(
                            orderDetl.getMatnr(),  // matnr -> sku
                            orderDetl.getStandby1(),  //  supp -> po
                            orderDetl.getAnfme(),          //    余料 -> 剩余余料统一时间戳标记
                            orderDetl.getStandby2(),   //barcode -> upc 条码
                            1,
                            null,   //origin -> supplier 货源
                            bindingTag        //余料标记相同
                    );
//                    matListsRemainder.add(matMix);
//                    orderDetlsRemainder.add(orderDetl);
                    matLists.add(matMix);
                } else {
                    Double ctns = anfme - remainder;
                    // 组装物料信息
                    OrderToLine.MatList mat = new OrderToLine.MatList(
                            orderDetl.getMatnr(),  // matnr -> sku
                            orderDetl.getStandby1(),  //  supp -> po
                            orderDetl.getAnfme(),   //整料
                            orderDetl.getStandby2(),   //barcode -> upc
                            1,
                            null,   //origin -> supplier 货源
                            System.currentTimeMillis()
                    );
                    matLists.add(mat);
                }
            }
//            // 智能分组余料
//            if (matListsRemainder.size() > 0) {
//                groupRemainders(orderDetlsRemainder, orderDetlsRemainder.size(), orderDetls.size(), matLists);
//            }
            orderToLine.setMatList(matLists);
            try{
                ReturnT<String> returnT = orderToLineHandler.start(orderToLine);
                if (!returnT.isSuccess()) {
                    log.error("下发单据失败===>"+ JSON.toJSON(orderToLine));
                }
                try{
                    for (OrderToLine.MatList matList:orderToLine.getMatList()){
                        BasArmMastSign basArmMastSign = new BasArmMastSign();
                        basArmMastSign.setMatnr(matList.getSku());
                        basArmMastSign.setOrderNo(orderToLine.getOrderNo());
                        basArmMastSign.setSku(matList.getSku());
                        basArmMastSign.setPo(matList.getPo());
                        basArmMastSign.setUpc(matList.getUpc());
                        basArmMastSign.setSupplier(matList.getSupplier());
                        basArmMastSign.setStatus(0);
                        basArmMastSign.setCreateTime(matList.getBindingTags());
                        basArmMastSignService.insert(basArmMastSign);
                List<OrderDetl> orderDetls = orderDetlService.selectByOrderNo(orderNo);
                List<GroupedLockerOptimizerUtils.Item> items = new ArrayList<>();
                for (OrderDetl orderDetl:orderDetls){
                    Integer number =  basArmRulesService.getNumber(orderDetl.getWeight(),orderDetl.getVolume(),orderDetl.getManLength(),orderDetl.getWidth(),orderDetl.getHeight());
                    if (number == null) {
                        BasArmRules basArmRules = new BasArmRules();
                        basArmRules.setMaterialHeight(orderDetl.getHeight());
                        basArmRules.setMaterialWeight(orderDetl.getWeight());
                        basArmRules.setMaterialLength(orderDetl.getManLength());
                        basArmRules.setMaterialWidth(orderDetl.getWidth());
                        basArmRulesService.insert(basArmRules);
                        return;
                    } else if (number == 0){
                        return;
                    }
                }catch (Exception e){
                    String name = ToSortLineUtils.MergerParameter(orderDetl.getMatnr(),orderDetl.getStandby1(),orderDetl.getStandby2());
                    int maxCapacity = number;
                    int stock = orderDetl.getAnfme().intValue();
                    items.add(new GroupedLockerOptimizerUtils.Item(name, maxCapacity, stock));
                }
                OrderToLine orderToLine = new OrderToLine();
                orderToLine.setOrderNo(orderNo);  //单据编号
                orderToLine.setCreateTime(System.currentTimeMillis());  //创建时间
                OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLine(items, orderToLine);
                try{
                    ReturnT<String> returnT = orderToLineHandler.start(orderToLineR);
                    if (!returnT.isSuccess()) {
                        log.error("下发单据失败===>"+ JSON.toJSON(orderToLineR));
                    } else {
                        try{
                            for (OrderToLine.MatList matList:orderToLineR.getMatList()){
                                BasArmMastSign basArmMastSign = new BasArmMastSign();
                                basArmMastSign.setMatnr(matList.getSku());
                                basArmMastSign.setOrderNo(orderToLineR.getOrderNo());
                                basArmMastSign.setSku(matList.getSku());
                                basArmMastSign.setPo(matList.getPo());
                                basArmMastSign.setUpc(matList.getUpc());
                                basArmMastSign.setSupplier(matList.getSupplier());
                                basArmMastSign.setStatus(0);
                                basArmMastSign.setCreateTime(matList.getBindingTags());
                                basArmMastSignService.insert(basArmMastSign);
                            }
                        }catch (Exception e){}
                    }
                } catch (Exception e){
                    log.error("下发单据异常===>"+e.getMessage());
                }
            } catch (Exception e){
                log.error("下发单据异常===>"+e.getMessage());
                log.error("下发单据异常,跳转下一个订单===>"+e.getMessage());
            }
        }
    }
src/main/java/com/zy/asrs/utils/GroupedLockerOptimizerUtils.java
New file
@@ -0,0 +1,466 @@
package com.zy.asrs.utils;
import java.util.*;
public class GroupedLockerOptimizerUtils {
    public static class Item {
        String name;
        double unitSpace;
        int maxCapacity;
        int stock;
        int remaining;
        public Item(String name, int maxCapacity, int stock) {
            this.name = name;
            this.maxCapacity = maxCapacity;
            this.unitSpace = 1.0 / maxCapacity;
            this.stock = stock;
            this.remaining = stock;
        }
        @Override
        public String toString() {
            return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)";
        }
    }
    static class Locker {
        int id;
        double remainingSpace;
        Map<String, Integer> contents;
        Set<String> itemTypes; // 当前储物柜中的物品种类
        public Locker(int id) {
            this.id = id;
            this.remainingSpace = 1.0;
            this.contents = new HashMap<>();
            this.itemTypes = new HashSet<>();
        }
        public boolean canAdd(Item item, int quantity) {
            return remainingSpace >= quantity * item.unitSpace;
        }
        public void addItem(Item item, int quantity) {
            double spaceUsed = quantity * item.unitSpace;
            remainingSpace -= spaceUsed;
            contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity);
            itemTypes.add(item.name);
            item.remaining -= quantity;
        }
        // 检查是否已经包含该种类的物品
        public boolean containsItemType(String itemName) {
            return itemTypes.contains(itemName);
        }
        // 检查是否可以优先放置同种物品(已有同种物品且还有空间)
        public boolean canAddSameType(Item item) {
            return containsItemType(item.name) && canAdd(item, 1);
        }
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("储物柜").append(id).append(": ");
            sb.append(String.format("剩余空间%.4f", remainingSpace)).append("\n");
            for (Map.Entry<String, Integer> entry : contents.entrySet()) {
                sb.append("  ").append(entry.getKey()).append(": ").append(entry.getValue()).append("个\n");
            }
            return sb.toString();
        }
    }
    static class PackingSolution {
        List<Locker> lockers;
        int totalLockersUsed;
        double overallUtilization;
        int itemTypeChanges; // 物品种类切换次数(衡量集中度)
        public PackingSolution() {
            this.lockers = new ArrayList<>();
        }
        public void addLocker(Locker locker) {
            lockers.add(locker);
            totalLockersUsed = lockers.size();
        }
        public void calculateMetrics() {
            double totalUsedSpace = 0;
            itemTypeChanges = 0;
            String lastItemType = null;
            for (Locker locker : lockers) {
                totalUsedSpace += (1.0 - locker.remainingSpace);
                for (String itemType : locker.itemTypes) {
                    if (lastItemType != null && !lastItemType.equals(itemType)) {
                        itemTypeChanges++;
                    }
                    lastItemType = itemType;
                }
            }
            overallUtilization = totalUsedSpace / totalLockersUsed;
        }
        @Override
        public String toString() {
            calculateMetrics();
            StringBuilder sb = new StringBuilder();
            sb.append("=== 分组优化摆放方案 ===\n");
            sb.append("使用的储物柜数量: ").append(totalLockersUsed).append("个\n");
            sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n");
            sb.append("物品种类切换次数: ").append(itemTypeChanges).append("次(越少越集中)\n\n");
            for (Locker locker : lockers) {
                sb.append(locker.toString()).append("\n");
            }
            return sb.toString();
        }
    }
//    public static void main(String[] args) {
//        Scanner scanner = new Scanner(System.in);
//
//        System.out.println("请输入物品种类数量:");
//        int itemTypes = scanner.nextInt();
//        scanner.nextLine();
//
//        List<Item> items = new ArrayList<>();
//
//        for (int i = 0; i < itemTypes; i++) {
//            System.out.println("\n请输入第" + (i + 1) + "种物品的信息:");
//            System.out.print("物品名称: ");
//            String name = scanner.nextLine();
//
//            System.out.print("单种存放最大数量: ");
//            int maxCapacity = scanner.nextInt();
//
//            System.out.print("库存数量: ");
//            int stock = scanner.nextInt();
//            scanner.nextLine();
//
//            items.add(new Item(name, maxCapacity, stock));
//        }
//
//        // 使用分组优先算法
//        PackingSolution solution = packItemsWithGrouping(items);
//
//        System.out.println("\n" + solution);
//
//        scanner.close();
//    }
    public static void main(String[] args) {
        int itemTypes = 5;
        List<Item> items = new ArrayList<>();
        for (int i = 0; i < itemTypes; i++) {
            String name = i+"a";
            int maxCapacity = i*10;
            int stock = i*11;
            items.add(new Item(name, maxCapacity, stock));
        }
        // 使用分组优先算法
        PackingSolution solution = packItemsWithGrouping(items);
        for (Locker locker:solution.lockers) {
            for (String mantnr : locker.contents.keySet()){
                System.out.println(mantnr+"<===>"+locker.contents.get(mantnr));
            }
        }
        System.out.println("\n" + solution);
//        scanner.close();
    }
    /**
     * 分组优先装箱算法
     * 优先将同种物品放在一起,只有在不得已时才分开放置
     */
    public static PackingSolution packItemsWithGrouping(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
        // 第一阶段:优先尝试将每种物品完整放入一个储物柜
        for (Item item : itemsToPack) {
            if (item.remaining == 0) continue;
            double totalSpaceNeeded = item.remaining * item.unitSpace;
            // 如果所有物品能放入一个储物柜
            if (totalSpaceNeeded <= 1.0) {
                boolean placedInExisting = false;
                // 寻找已有储物柜(优先选择空的)
                for (Locker locker : solution.lockers) {
                    if (locker.contents.isEmpty() && locker.canAdd(item, item.remaining)) {
                        locker.addItem(item, item.remaining);
                        placedInExisting = true;
                        break;
                    }
                }
                // 如果没有空储物柜,创建新的
                if (!placedInExisting) {
                    Locker newLocker = new Locker(lockerId++);
                    newLocker.addItem(item, item.remaining);
                    solution.addLocker(newLocker);
                }
            }
        }
        // 第二阶段:处理剩余物品(无法完整放入一个储物柜的)
        for (Item item : itemsToPack) {
            while (item.remaining > 0) {
                // 优先尝试放入已有同种物品的储物柜
                Locker bestSameTypeLocker = null;
                double bestSameTypeSpace = -1;
                for (Locker locker : solution.lockers) {
                    if (locker.containsItemType(item.name) && locker.canAdd(item, 1)) {
                        if (locker.remainingSpace > bestSameTypeSpace) {
                            bestSameTypeLocker = locker;
                            bestSameTypeSpace = locker.remainingSpace;
                        }
                    }
                }
                if (bestSameTypeLocker != null) {
                    int maxCanAdd = (int) Math.floor(bestSameTypeLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    bestSameTypeLocker.addItem(item, actualAdd);
                    continue;
                }
                // 其次尝试放入空储物柜(优先集中放置)
                Locker emptyLocker = null;
                for (Locker locker : solution.lockers) {
                    if (locker.contents.isEmpty() && locker.canAdd(item, 1)) {
                        emptyLocker = locker;
                        break;
                    }
                }
                if (emptyLocker != null) {
                    int maxCanAdd = (int) Math.floor(emptyLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    emptyLocker.addItem(item, actualAdd);
                    continue;
                }
                // 最后尝试放入任何有空间的储物柜
                Locker bestLocker = null;
                double bestRemaining = Double.MAX_VALUE;
                for (Locker locker : solution.lockers) {
                    if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) {
                        bestLocker = locker;
                        bestRemaining = locker.remainingSpace;
                    }
                }
                if (bestLocker != null) {
                    int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    bestLocker.addItem(item, actualAdd);
                } else {
                    // 创建新储物柜
                    Locker newLocker = new Locker(lockerId++);
                    int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    newLocker.addItem(item, actualAdd);
                    solution.addLocker(newLocker);
                }
            }
        }
        return solution;
    }
    /**
     * 按种类顺序处理算法
     * 先处理一种物品,再处理下一种
     */
    public static PackingSolution packItemsByType(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
        // 按种类顺序处理
        for (Item item : itemsToPack) {
            while (item.remaining > 0) {
                // 优先使用当前正在处理该类物品的储物柜
                Locker currentTypeLocker = null;
                for (Locker locker : solution.lockers) {
                    if (locker.containsItemType(item.name) && locker.canAdd(item, 1)) {
                        currentTypeLocker = locker;
                        break;
                    }
                }
                if (currentTypeLocker != null) {
                    int maxCanAdd = (int) Math.floor(currentTypeLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    currentTypeLocker.addItem(item, actualAdd);
                    continue;
                }
                // 如果没有同种物品的储物柜,找空储物柜
                Locker emptyLocker = null;
                for (Locker locker : solution.lockers) {
                    if (locker.contents.isEmpty()) {
                        emptyLocker = locker;
                        break;
                    }
                }
                if (emptyLocker != null) {
                    int maxCanAdd = (int) Math.floor(emptyLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    emptyLocker.addItem(item, actualAdd);
                    continue;
                }
                // 创建新储物柜专门放这种物品
                Locker newLocker = new Locker(lockerId++);
                int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace);
                int actualAdd = Math.min(item.remaining, maxCanAdd);
                newLocker.addItem(item, actualAdd);
                solution.addLocker(newLocker);
            }
        }
        return solution;
    }
    /**
     * 比较不同算法的效果
     */
    public static void compareAlgorithms() {
        System.out.println("=== 算法比较 ===");
        List<Item> testItems = Arrays.asList(
                new Item("X", 20, 35),
                new Item("Y", 15, 28),
                new Item("Z", 34, 45),
                new Item("T", 25, 18)
        );
        System.out.println("测试物品: " + testItems);
        // 分组优先算法
        PackingSolution groupedSolution = packItemsWithGrouping(testItems);
        System.out.println("\n分组优先算法:");
        System.out.println("储物柜数量: " + groupedSolution.totalLockersUsed);
        System.out.println("种类切换次数: " + groupedSolution.itemTypeChanges);
        // 按种类顺序算法
        PackingSolution typeOrderSolution = packItemsByType(testItems);
        System.out.println("\n按种类顺序算法:");
        System.out.println("储物柜数量: " + typeOrderSolution.totalLockersUsed);
        System.out.println("种类切换次数: " + typeOrderSolution.itemTypeChanges);
        // 原始最佳适应算法(不分组)
        PackingSolution originalSolution = packItemsOriginal(testItems);
        System.out.println("\n原始最佳适应算法:");
        System.out.println("储物柜数量: " + originalSolution.totalLockersUsed);
        System.out.println("种类切换次数: " + originalSolution.itemTypeChanges);
    }
    /**
     * 原始最佳适应算法(不分组,作为对比)
     */
    public static PackingSolution packItemsOriginal(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
        // 按空间占用从大到小排序
        itemsToPack.sort((a, b) -> Double.compare(b.unitSpace, a.unitSpace));
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
        for (Item item : itemsToPack) {
            while (item.remaining > 0) {
                Locker bestLocker = null;
                double bestRemaining = Double.MAX_VALUE;
                for (Locker locker : solution.lockers) {
                    if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) {
                        bestLocker = locker;
                        bestRemaining = locker.remainingSpace;
                    }
                }
                if (bestLocker != null) {
                    int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    bestLocker.addItem(item, actualAdd);
                } else {
                    Locker newLocker = new Locker(lockerId++);
                    int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    newLocker.addItem(item, actualAdd);
                    solution.addLocker(newLocker);
                }
            }
        }
        return solution;
    }
    /**
     * 可视化展示储物柜使用情况
     */
//    public static void visualizeSolution(PackingSolution solution) {
//        System.out.println("=== 储物柜使用情况可视化 ===");
//
//        for (Locker locker : solution.lockers) {
//            System.out.println("储物柜 " + locker.id + ":");
//            for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) {
//                int barLength = (int) (entry.getValue() * 50 / (1.0 / 0.05)); // 简化可视化
//                String bar = "=".repeat(barLength);
//                System.out.printf("  %s: %s %d个\n", entry.getKey(), bar, entry.getValue());
//            }
//            System.out.printf("  剩余空间: %.2f%%\n", locker.remainingSpace * 100);
//            System.out.println();
//        }
//    }
    public static void visualizeSolution(PackingSolution solution) {
        System.out.println("=== 储物柜使用情况可视化 ===");
        for (Locker locker : solution.lockers) {
            System.out.println("储物柜 " + locker.id + ":");
            for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) {
                int barLength = (int) (entry.getValue() * 50 / (1.0 / 0.05));
                // 替代 String.repeat()
                StringBuilder barBuilder = new StringBuilder();
                for (int i = 0; i < barLength; i++) {
                    barBuilder.append("=");
                }
                String bar = barBuilder.toString();
                System.out.printf("  %s: %s %d个\n", entry.getKey(), bar, entry.getValue());
            }
            System.out.printf("  剩余空间: %.2f%%\n", locker.remainingSpace * 100);
            System.out.println();
        }
    }
}
src/main/java/com/zy/asrs/utils/MultiLockerOptimizerUtils.java
New file
@@ -0,0 +1,286 @@
package com.zy.asrs.utils;
import java.util.*;
public class MultiLockerOptimizerUtils {
    static class Item implements Comparable<Item> {
        String name;
        double unitSpace;
        int maxCapacity;
        int stock;
        int remaining; // 剩余未摆放的数量
        public Item(String name, int maxCapacity, int stock) {
            this.name = name;
            this.maxCapacity = maxCapacity;
            this.unitSpace = 1.0 / maxCapacity;
            this.stock = stock;
            this.remaining = stock;
        }
        // 按空间占用从大到小排序(优先放占用大的)
        @Override
        public int compareTo(Item other) {
            return Double.compare(other.unitSpace, this.unitSpace);
        }
        @Override
        public String toString() {
            return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)";
        }
    }
    static class Locker {
        int id;
        double remainingSpace;
        Map<String, Integer> contents;
        public Locker(int id) {
            this.id = id;
            this.remainingSpace = 1.0;
            this.contents = new HashMap<>();
        }
        // 尝试放入物品
        public boolean canAdd(Item item, int quantity) {
            return remainingSpace >= quantity * item.unitSpace;
        }
        // 放入物品
        public void addItem(Item item, int quantity) {
            double spaceUsed = quantity * item.unitSpace;
            remainingSpace -= spaceUsed;
            contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity);
            item.remaining -= quantity;
        }
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("储物柜").append(id).append(": ");
            sb.append(String.format("剩余空间%.4f", remainingSpace)).append("\n");
            for (Map.Entry<String, Integer> entry : contents.entrySet()) {
                sb.append("  ").append(entry.getKey()).append(": ").append(entry.getValue()).append("个\n");
            }
            return sb.toString();
        }
    }
    static class PackingSolution {
        List<Locker> lockers;
        int totalLockersUsed;
        double overallUtilization;
        public PackingSolution() {
            this.lockers = new ArrayList<>();
        }
        public void addLocker(Locker locker) {
            lockers.add(locker);
            totalLockersUsed = lockers.size();
        }
        public void calculateOverallUtilization() {
            double totalUsedSpace = 0;
            for (Locker locker : lockers) {
                totalUsedSpace += (1.0 - locker.remainingSpace);
            }
            overallUtilization = totalUsedSpace / totalLockersUsed;
        }
        @Override
        public String toString() {
            calculateOverallUtilization();
            StringBuilder sb = new StringBuilder();
            sb.append("=== 多储物柜摆放方案 ===\n");
            sb.append("使用的储物柜数量: ").append(totalLockersUsed).append("个\n");
            sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n\n");
            for (Locker locker : lockers) {
                sb.append(locker.toString()).append("\n");
            }
            return sb.toString();
        }
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入物品种类数量:");
        int itemTypes = scanner.nextInt();
        scanner.nextLine();
        List<Item> items = new ArrayList<>();
        for (int i = 0; i < itemTypes; i++) {
            System.out.println("\n请输入第" + (i + 1) + "种物品的信息:");
            System.out.print("物品名称: ");
            String name = scanner.nextLine();
            System.out.print("单种存放最大数量: ");
            int maxCapacity = scanner.nextInt();
            System.out.print("库存数量: ");
            int stock = scanner.nextInt();
            scanner.nextLine();
            items.add(new Item(name, maxCapacity, stock));
        }
        // 使用最佳适应递减算法
        PackingSolution solution = packItems(items);
        System.out.println("\n" + solution);
        scanner.close();
    }
    /**
     * 装箱算法:最佳适应递减(Best Fit Decreasing)
     * 这是装箱问题的经典启发式算法
     */
    public static PackingSolution packItems(List<Item> items) {
        // 复制物品列表,避免修改原始数据
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
        // 按物品大小从大到小排序(优先处理大物品)
        Collections.sort(itemsToPack);
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
        // 处理每种物品
        for (Item item : itemsToPack) {
            while (item.remaining > 0) {
                // 寻找最适合的现有储物柜
                Locker bestLocker = null;
                double bestRemaining = Double.MAX_VALUE;
                for (Locker locker : solution.lockers) {
                    if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) {
                        bestLocker = locker;
                        bestRemaining = locker.remainingSpace;
                    }
                }
                // 如果找到合适的储物柜
                if (bestLocker != null) {
                    int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    bestLocker.addItem(item, actualAdd);
                } else {
                    // 创建新储物柜
                    Locker newLocker = new Locker(lockerId++);
                    int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    newLocker.addItem(item, actualAdd);
                    solution.addLocker(newLocker);
                }
            }
        }
        return solution;
    }
    /**
     * 替代算法:首次适应递减(First Fit Decreasing)
     * 另一种常用的装箱启发式算法
     */
    public static PackingSolution packItemsFirstFit(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
        Collections.sort(itemsToPack);
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
        for (Item item : itemsToPack) {
            while (item.remaining > 0) {
                boolean placed = false;
                // 尝试放入现有储物柜
                for (Locker locker : solution.lockers) {
                    if (locker.canAdd(item, 1)) {
                        int maxCanAdd = (int) Math.floor(locker.remainingSpace / item.unitSpace);
                        int actualAdd = Math.min(item.remaining, maxCanAdd);
                        locker.addItem(item, actualAdd);
                        placed = true;
                        break;
                    }
                }
                // 如果没找到合适的储物柜,创建新的
                if (!placed) {
                    Locker newLocker = new Locker(lockerId++);
                    int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    newLocker.addItem(item, actualAdd);
                    solution.addLocker(newLocker);
                }
            }
        }
        return solution;
    }
    /**
     * 测试不同的装箱算法
     */
    public static void testDifferentAlgorithms() {
        System.out.println("=== 算法比较测试 ===");
        List<Item> testItems = Arrays.asList(
                new Item("X", 20, 30),
                new Item("Y", 15, 25),
                new Item("Z", 34, 40),
                new Item("T", 25, 15)
        );
        System.out.println("测试物品: " + testItems);
        // 测试最佳适应算法
        PackingSolution bestFitSolution = packItems(testItems);
        System.out.println("\n最佳适应算法结果:");
        System.out.println("使用储物柜: " + bestFitSolution.totalLockersUsed + "个");
        // 测试首次适应算法
        PackingSolution firstFitSolution = packItemsFirstFit(testItems);
        System.out.println("首次适应算法结果:");
        System.out.println("使用储物柜: " + firstFitSolution.totalLockersUsed + "个");
        // 显示最佳算法的详细方案
        System.out.println("\n最佳方案详情:");
        System.out.println(bestFitSolution);
    }
    /**
     * 验证是否所有物品都已摆放
     */
    public static boolean validateSolution(List<Item> originalItems, PackingSolution solution) {
        Map<String, Integer> totalPlaced = new HashMap<>();
        for (Locker locker : solution.lockers) {
            for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) {
                totalPlaced.put(entry.getKey(),
                        totalPlaced.getOrDefault(entry.getKey(), 0) + entry.getValue());
            }
        }
        for (Item item : originalItems) {
            if (totalPlaced.getOrDefault(item.name, 0) != item.stock) {
                System.out.println("验证失败: " + item.name + " 应放" + item.stock +
                        "个,实放" + totalPlaced.getOrDefault(item.name, 0) + "个");
                return false;
            }
        }
        System.out.println("验证成功: 所有物品都已正确摆放");
        return true;
    }
}
src/main/java/com/zy/asrs/utils/ToSortLineUtils.java
New file
@@ -0,0 +1,64 @@
package com.zy.asrs.utils;
import com.zy.asrs.entity.OrderDetl;
import com.zy.asrs.entity.param.OrderToLine;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class ToSortLineUtils {
    private static final String  sign_F = "|F|LABEL_";
    public static void main(String[] args) {
        int itemTypes = 5;
        List<GroupedLockerOptimizerUtils.Item> items = new ArrayList<>();
        for (int i = 0; i < itemTypes; i++) {
            String name = i+"a";
            int maxCapacity = i*10;
            int stock = i*11;
            items.add(new GroupedLockerOptimizerUtils.Item(name, maxCapacity, stock));
        }
        // 使用分组优先算法
        GroupedLockerOptimizerUtils.PackingSolution solution = GroupedLockerOptimizerUtils.packItemsWithGrouping(items);
        for (GroupedLockerOptimizerUtils.Locker locker:solution.lockers) {
            for (String mantnr : locker.contents.keySet()){
                System.out.println(mantnr+"<===>"+locker.contents.get(mantnr));
            }
        }
        System.out.println("\n" + solution);
    }
    public static String MergerParameter(String sku,String po,String upc){
        return sku+sign_F+po+sign_F+upc;
    }
    public static OrderToLine GetOrderToLine(List<GroupedLockerOptimizerUtils.Item> items,OrderToLine orderToLine){
        Long bindingTag = System.currentTimeMillis();//混搭标记
        List<OrderToLine.MatList> matLists = new ArrayList<>();
        // 使用分组优先算法
        GroupedLockerOptimizerUtils.PackingSolution solution = GroupedLockerOptimizerUtils.packItemsWithGrouping(items);
        for (GroupedLockerOptimizerUtils.Locker locker:solution.lockers) {
            for (String mantnr : locker.contents.keySet()){
                System.out.println(mantnr+"<===>"+locker.contents.get(mantnr));
                String[] split = mantnr.split(Pattern.quote(sign_F));
                OrderToLine.MatList mat = new OrderToLine.MatList(
                        split[0],  // matnr -> sku
                        split[1],  //  supp -> po -> s1
                        locker.contents.get(mantnr).doubleValue(),   //整料
                        split[2],   //barcode -> upc -> s2
                        1,
                        null,   //origin -> supplier 货源
                        bindingTag
                );
                matLists.add(mat);
            }
            bindingTag++;
        }
        orderToLine.setMatList(matLists);
        return orderToLine;
    }
}