| src/main/java/com/zy/asrs/controller/ApiLogController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/task/handler/ArmRulesHandler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/task/handler/OrderToLineHandler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/utils/MultiLockerOptimizerUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/utils/OptimizedLockerPacking3Utils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/utils/ToSortLineUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/utils/param/ItemUtilParam.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/common/config/ControllerResAdvice.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/webapp/static/js/apiLog/apiLog.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/webapp/views/apiLog/apiLog.html | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/main/java/com/zy/asrs/controller/ApiLogController.java
@@ -56,7 +56,11 @@ wrapper.ge(entry.getKey(), DateUtils.convert(dates[0])); wrapper.le(entry.getKey(), DateUtils.convert(dates[1])); } else { wrapper.like(entry.getKey(), val); if (entry.getKey().equals("url")){ wrapper.like(entry.getKey(), val).or().like("client_ip", val); } else { wrapper.like(entry.getKey(), val); } } } } src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java
@@ -5,13 +5,10 @@ import com.zy.asrs.entity.*; import com.zy.asrs.entity.param.OrderToLine; import com.zy.asrs.service.*; 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.OptimizedLockerPackingUtils; import com.zy.asrs.utils.OrderInAndOutUtil; import com.zy.asrs.utils.ToSortLineUtils; import com.zy.asrs.utils.param.ItemUtilParam; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; @@ -48,12 +45,10 @@ for (String orderNo : orderNos) { try{ List<OrderDetlPakin> orderDetlPakinList = orderDetlPakinService.selectList(new EntityWrapper<OrderDetlPakin>().eq("order_no",orderNo)); // List<GroupedLockerOptimizerUtils.Item> items = new ArrayList<>(); if (orderDetlPakinList.size()<1){ continue; } List<OptimizedLockerPackingUtils.Item> items = new ArrayList<>(); List<ItemUtilParam.Item> items = new ArrayList<>(); for (OrderDetlPakin orderDetl:orderDetlPakinList){ Integer number = basArmRulesService.getNumber(orderDetl.getWeight(),orderDetl.getVolume(),orderDetl.getManLength(),orderDetl.getWidth(),orderDetl.getHeight()); if (number == null) { @@ -70,20 +65,18 @@ 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)); items.add(new OptimizedLockerPackingUtils.Item(name, maxCapacity, stock)); items.add(new ItemUtilParam.Item(name, maxCapacity, stock)); } OrderToLine orderToLine = new OrderToLine(); orderToLine.setOrderNo(orderNo); //单据编号 orderToLine.setCreateTime(System.currentTimeMillis()); //创建时间 // OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLineGro(items, orderToLine); OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLineOpt(items, orderToLine); OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLine(items, orderToLine,"Opt3"); try{ ReturnT<String> returnT = orderToLineHandler.start(orderToLineR); if (!returnT.isSuccess()) { log.error("下发单据失败===>"+ JSON.toJSON(orderToLineR)); // log.error("下发单据失败===>"+ JSON.toJSON(orderToLineR)); } else { try{ for (OrderToLine.MatList matList:orderToLineR.getMatList()){ src/main/java/com/zy/asrs/task/handler/ArmRulesHandler.java
@@ -2,13 +2,12 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.core.common.Cools; import com.core.common.DateUtils; import com.core.common.SpringUtils; import com.core.exception.CoolException; import com.zy.asrs.entity.BasArmRules; import com.zy.asrs.entity.DocType; import com.zy.asrs.entity.Order; import com.zy.asrs.entity.OrderDetl; import com.zy.asrs.entity.*; import com.zy.asrs.entity.param.ArmPrecomputeParam; import com.zy.asrs.service.ApiLogService; import com.zy.asrs.service.BasArmRulesService; @@ -79,20 +78,40 @@ throw new CoolException("获取码垛数量失败"); } } catch (Exception e) { log.error("fail", e); return FAIL.setMsg(e.getMessage()); try{ log.error("fail==>获取码垛数量:"+e.getLocalizedMessage()); } catch (Exception e1){ } // return FAIL.setMsg(e.getLocalizedMessage()); return FAIL; // log.error("fail", e); // return FAIL.setMsg(e.getMessage()); } finally { try { // 保存接口日志 apiLogService.save( "获取码垛数量", URL + QuantityOfPalletizing, null, "127.0.0.1", JSON.toJSONString(armPrecomputeParam), response, success ); if (success){ // 保存接口日志 apiLogService.save( "获取码垛数量", URL +"/"+ QuantityOfPalletizing, null, "127.0.0.1", JSON.toJSONString(armPrecomputeParam), response, success ); } else { beforeBodyWriteCallApiLogSave( "获取码垛数量", URL +"/"+ QuantityOfPalletizing, null, "127.0.0.1", JSON.toJSONString(armPrecomputeParam), response, success ); } } catch (Exception e) { log.error("", e); } } }catch (Exception e){ @@ -100,5 +119,30 @@ } return SUCCESS; } public void beforeBodyWriteCallApiLogSave(String name, String url, String appkey, String ip, String request, String response, boolean success) { ApiLog apiLog = apiLogService.selectOne(new EntityWrapper<ApiLog>() .eq("namespace", name) .eq("request", request) .eq("response", response) .eq("result", success? 1:0) .orderBy("create_time", false) ); if (!Cools.isEmpty(apiLog)){ long parseLong = Long.parseLong(apiLog.getTimestamp()); if (new Date().getTime()-parseLong<5*1000*60){ return; } } // 保存接口日志 apiLogService.save( name, url, appkey, ip, request, response, success ); } } src/main/java/com/zy/asrs/task/handler/OrderToLineHandler.java
@@ -2,7 +2,11 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.core.common.Cools; import com.core.common.SpringUtils; import com.core.exception.CoolException; import com.zy.asrs.entity.ApiLog; import com.zy.asrs.entity.param.ArmPrecomputeParam; import com.zy.asrs.entity.param.OrderToLine; import com.zy.asrs.service.ApiLogService; @@ -12,12 +16,14 @@ import com.zy.asrs.task.AbstractHandler; import com.zy.asrs.task.core.ReturnT; import com.zy.common.utils.HttpHandler; import com.zy.common.utils.IpTools; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -61,20 +67,37 @@ throw new CoolException("下发单据失败"); } } catch (Exception e) { log.error("fail", e); return FAIL.setMsg(e.getMessage()); try{ log.error("fail==>下发单据至分拣线:"+e.getLocalizedMessage()); } catch (Exception e1){ } // return FAIL.setMsg(e.getLocalizedMessage()); return FAIL; } finally { try { // 保存接口日志 apiLogService.save( "下发单据至分拣线", URL + Path, null, "127.0.0.1", JSON.toJSONString(orderToline), response, success ); if (success){ // 保存接口日志 apiLogService.save( "下发单据至分拣线", URL +"/"+ Path, null, "127.0.0.1", JSON.toJSONString(orderToline), response, success ); } else { beforeBodyWriteCallApiLogSave( "下发单据至分拣线", URL +"/"+ Path, null, "127.0.0.1", JSON.toJSONString(orderToline), response, success ); } } catch (Exception e) { log.error("", e); } } }catch (Exception e){ @@ -82,4 +105,30 @@ } return SUCCESS; } public void beforeBodyWriteCallApiLogSave(String name, String url, String appkey, String ip, String request, String response, boolean success) { ApiLog apiLog = apiLogService.selectOne(new EntityWrapper<ApiLog>() .eq("namespace", name) .eq("response", response) .eq("result", success? 1:0) .orderBy("create_time", false) ); if (!Cools.isEmpty(apiLog)){ long parseLong = Long.parseLong(apiLog.getTimestamp()); if (new Date().getTime()-parseLong<5*1000*60){ return; } } // 保存接口日志 apiLogService.save( name, url, appkey, ip, request, response, success ); } } src/main/java/com/zy/asrs/utils/MultiLockerOptimizerUtils.java
@@ -1,4 +1,7 @@ package com.zy.asrs.utils; import com.core.common.SnowflakeIdWorker; import com.core.common.SpringUtils; import java.util.*; public class MultiLockerOptimizerUtils { @@ -33,12 +36,19 @@ static class Locker { int id; double remainingSpace; long bindingTags; 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<>(); SnowflakeIdWorker snowflakeIdWorker = SpringUtils.getBean(SnowflakeIdWorker.class); // this.bindingTags = System.currentTimeMillis(); this.bindingTags = snowflakeIdWorker.nextId(); } // 尝试放入物品 src/main/java/com/zy/asrs/utils/OptimizedLockerPacking3Utils.java
New file @@ -0,0 +1,737 @@ package com.zy.asrs.utils; import com.core.common.SnowflakeIdWorker; import com.core.common.SpringUtils; import java.util.*; public class OptimizedLockerPacking3Utils { public static class Item { String name; double unitSpace; int maxCapacity; int stock; int allocated; // 改为已分配数量,避免直接修改remaining导致的混乱 public Item(String name, int maxCapacity, int stock) { this.name = name; this.maxCapacity = maxCapacity; this.unitSpace = 1.0 / maxCapacity; this.stock = stock; this.allocated = 0; } public int getRemaining() { return stock - allocated; } public boolean allocate(int quantity) { if (allocated + quantity <= stock) { allocated += quantity; return true; } return false; } @Override public String toString() { return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)"; } } static class Locker { int id; double remainingSpace; long bindingTags; 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<>(); SnowflakeIdWorker snowflakeIdWorker = SpringUtils.getBean(SnowflakeIdWorker.class); this.bindingTags = snowflakeIdWorker.nextId(); } public boolean canAdd(Item item, int quantity) { return remainingSpace >= quantity * item.unitSpace; } public boolean addItem(Item item, int quantity) { // 验证分配数量 if (!item.allocate(quantity)) { return false; } double spaceUsed = quantity * item.unitSpace; if (spaceUsed > remainingSpace + 1e-6) { // 回滚分配 item.allocated -= quantity; return false; } remainingSpace -= spaceUsed; contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity); itemTypes.add(item.name); return true; } public boolean containsItemType(String itemName) { return itemTypes.contains(itemName); } @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; int pureLockers; 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; pureLockers = 0; String lastItemType = null; for (Locker locker : lockers) { totalUsedSpace += (1.0 - locker.remainingSpace); if (locker.itemTypes.size() == 1) { pureLockers++; } 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(pureLockers).append("个\n"); sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n"); sb.append("物品种类切换次数: ").append(itemTypeChanges).append("次\n\n"); List<Locker> pureLockersList = new ArrayList<>(); List<Locker> mixedLockersList = new ArrayList<>(); for (Locker locker : lockers) { if (locker.itemTypes.size() == 1) { pureLockersList.add(locker); } else { mixedLockersList.add(locker); } } sb.append("【纯种类储物柜】:\n"); for (Locker locker : pureLockersList) { sb.append(locker.toString()).append("\n"); } sb.append("【混合储物柜】:\n"); for (Locker locker : mixedLockersList) { sb.append(locker.toString()).append("\n"); } return sb.toString(); } } /** * 余料物品辅助类 */ private static class RemainderItem { Item item; int quantity; RemainderItem(Item item, int quantity) { this.item = item; this.quantity = quantity; } @Override public String toString() { return item.name + "(" + quantity + "个)"; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; RemainderItem that = (RemainderItem) obj; return quantity == that.quantity && item.name.equals(that.item.name); } @Override public int hashCode() { return Objects.hash(item.name, quantity); } } 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 = optimizedPacking(items); System.out.println("\n" + solution); scanner.close(); } /** * 优化装箱算法:修复数量管理问题 */ public static PackingSolution optimizedPacking(List<Item> items) { // 验证输入 if (items == null || items.isEmpty()) { throw new IllegalArgumentException("物品列表不能为空"); } // 创建物品副本用于处理 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; // 第一阶段:对每种物品进行集中处理,分配完整储物柜 Map<String, Integer> remainders = new HashMap<>(); for (Item item : itemsToPack) { if (item.getRemaining() == 0) continue; int fullLockersNeeded = item.getRemaining() / item.maxCapacity; int remainder = item.getRemaining() % item.maxCapacity; // 分配完整储物柜 for (int i = 0; i < fullLockersNeeded; i++) { Locker locker = new Locker(lockerId++); if (locker.addItem(item, item.maxCapacity)) { solution.addLocker(locker); } else { System.out.println("警告: 无法分配完整储物柜给物品 " + item.name); } } if (remainder > 0) { remainders.put(item.name, remainder); } } // 第二阶段:优化余料分配 if (!remainders.isEmpty()) { uniformRemainderProcessing(remainders, itemsToPack, solution, lockerId); } // 验证结果 if (!validateSolution(items, solution)) { System.out.println("警告: 数量分配验证失败"); } return solution; } /** * 修复后的均匀余料处理算法 */ private static void uniformRemainderProcessing(Map<String, Integer> remainders, List<Item> items, PackingSolution solution, int startLockerId) { int lockerId = startLockerId; // 将余料转换为可处理的物品列表 List<RemainderItem> remainderItems = new ArrayList<>(); for (Item item : items) { Integer remainder = remainders.get(item.name); if (remainder != null && remainder > 0 && item.getRemaining() > 0) { remainderItems.add(new RemainderItem(item, Math.min(remainder, item.getRemaining()))); } } // 按物品单位空间从大到小排序 remainderItems.sort((a, b) -> Double.compare(b.item.unitSpace, a.item.unitSpace)); // 使用修复后的余料处理算法 List<Locker> remainderLockers = fixedRemainderArrangement(remainderItems, solution, lockerId); // 将最优解添加到解决方案中 for (Locker locker : remainderLockers) { solution.addLocker(locker); } } /** * 修复后的余料排列方案 */ private static List<Locker> fixedRemainderArrangement(List<RemainderItem> remainderItems, PackingSolution solution, int startLockerId) { if (remainderItems.isEmpty()) { return new ArrayList<>(); } // 创建副本避免修改原始数据 List<RemainderItem> remaining = new ArrayList<>(); for (RemainderItem ri : remainderItems) { if (ri.quantity > 0) { remaining.add(new RemainderItem(ri.item, ri.quantity)); } } List<Locker> lockers = new ArrayList<>(); int lockerId = startLockerId; // 主要策略:创建高利用率储物柜 while (!remaining.isEmpty()) { Locker locker = createOptimizedLocker(remaining, lockerId); if (locker != null) { lockers.add(locker); lockerId++; // 移除已处理完的物品 Iterator<RemainderItem> iterator = remaining.iterator(); while (iterator.hasNext()) { RemainderItem ri = iterator.next(); if (ri.quantity == 0) { iterator.remove(); } } double utilization = (1.0 - locker.remainingSpace) * 100; System.out.println("创建储物柜,利用率: " + String.format("%.2f", utilization) + "%"); } else { break; } } // 处理剩余物品(如果有) if (!remaining.isEmpty()) { System.out.println("使用标准算法处理剩余 " + remaining.size() + " 种物品"); List<Locker> standardLockers = standardBestFit(remaining, lockerId); lockers.addAll(standardLockers); } return lockers; } /** * 创建优化储物柜(修复数量验证) */ private static Locker createOptimizedLocker(List<RemainderItem> remaining, int lockerId) { if (remaining.isEmpty()) { return null; } Locker locker = new Locker(lockerId); double targetUtilization = 0.85; double minUtilization = 0.3; // 最低利用率阈值 // 寻找最佳组合 List<RemainderItem> bestCombination = findValidCombination(remaining, targetUtilization); if (bestCombination.isEmpty()) { // 尝试达到最低利用率 bestCombination = findMinUtilizationCombination(remaining, minUtilization); } if (bestCombination.isEmpty()) { return null; } // 分配组合到储物柜 boolean success = allocateValidCombination(remaining, locker, bestCombination); if (!success || locker.contents.isEmpty()) { return null; } // 检查利用率是否可接受 double utilization = 1.0 - locker.remainingSpace; if (utilization < minUtilization) { // 回滚分配 rollbackAllocation(remaining, locker); return null; } return locker; } /** * 寻找有效组合(修复数量验证) */ private static List<RemainderItem> findValidCombination(List<RemainderItem> items, double targetUtilization) { List<RemainderItem> bestCombination = new ArrayList<>(); double[] bestDiff = {Double.MAX_VALUE}; // 过滤有效物品 List<RemainderItem> availableItems = new ArrayList<>(); for (RemainderItem ri : items) { if (ri.quantity > 0 && ri.item.getRemaining() > 0) { availableItems.add(ri); } } if (availableItems.isEmpty()) { return bestCombination; } // 使用DFS搜索有效组合 validDfsCombination(availableItems, 0, 0.0, targetUtilization, new ArrayList<>(), bestCombination, bestDiff); return bestCombination; } /** * 有效的DFS组合搜索 */ private static void validDfsCombination(List<RemainderItem> items, int index, double currentSpace, double targetUtilization, List<RemainderItem> current, List<RemainderItem> bestCombination, double[] bestDiff) { // 检查当前组合的有效性 if (!current.isEmpty()) { double diff = Math.abs(currentSpace - targetUtilization); if (currentSpace <= 1.0 + 1e-6 && diff < bestDiff[0] && isCombinationValid(current)) { bestCombination.clear(); for (RemainderItem ri : current) { bestCombination.add(new RemainderItem(ri.item, ri.quantity)); } bestDiff[0] = diff; if (diff < 0.01) { return; } } } if (index >= items.size() || currentSpace > 1.0 + 1e-6) { return; } RemainderItem currentItem = items.get(index); // 不选择当前物品 validDfsCombination(items, index + 1, currentSpace, targetUtilization, current, bestCombination, bestDiff); // 选择当前物品 if (currentItem.quantity > 0) { int maxCanAdd = Math.min(currentItem.quantity, (int) Math.floor((1.0 - currentSpace) / currentItem.item.unitSpace)); maxCanAdd = Math.min(maxCanAdd, currentItem.item.getRemaining()); // 重要修复:不超过剩余数量 for (int qty = 1; qty <= maxCanAdd; qty++) { current.add(new RemainderItem(currentItem.item, qty)); validDfsCombination(items, index + 1, currentSpace + qty * currentItem.item.unitSpace, targetUtilization, current, bestCombination, bestDiff); current.remove(current.size() - 1); } } } /** * 检查组合是否有效(修复数量验证) */ private static boolean isCombinationValid(List<RemainderItem> combination) { Map<String, Integer> totalQuantities = new HashMap<>(); for (RemainderItem ri : combination) { int currentTotal = totalQuantities.getOrDefault(ri.item.name, 0); totalQuantities.put(ri.item.name, currentTotal + ri.quantity); // 检查是否超过物品剩余数量 if (currentTotal + ri.quantity > getRemainingQuantity(ri.item)) { return false; } } return true; } /** * 获取物品剩余数量 */ private static int getRemainingQuantity(Item item) { return item.stock - item.allocated; } /** * 寻找达到最低利用率的组合 */ private static List<RemainderItem> findMinUtilizationCombination(List<RemainderItem> items, double minUtilization) { List<RemainderItem> combination = new ArrayList<>(); double currentSpace = 0.0; // 按单位空间排序 List<RemainderItem> sortedItems = new ArrayList<>(items); sortedItems.sort((a, b) -> Double.compare(b.item.unitSpace, a.item.unitSpace)); for (RemainderItem ri : sortedItems) { if (ri.quantity > 0 && currentSpace < minUtilization) { int maxCanAdd = Math.min(ri.quantity, (int) Math.floor((1.0 - currentSpace) / ri.item.unitSpace)); maxCanAdd = Math.min(maxCanAdd, ri.item.getRemaining()); // 重要修复 if (maxCanAdd > 0) { combination.add(new RemainderItem(ri.item, maxCanAdd)); currentSpace += maxCanAdd * ri.item.unitSpace; } } } if (currentSpace >= minUtilization) { return combination; } return new ArrayList<>(); } /** * 分配有效组合到储物柜(修复数量验证) */ private static boolean allocateValidCombination(List<RemainderItem> remaining, Locker locker, List<RemainderItem> combination) { // 验证组合中的所有物品都有足够的数量 for (RemainderItem combItem : combination) { boolean found = false; for (RemainderItem remItem : remaining) { if (remItem.item.name.equals(combItem.item.name)) { found = true; if (combItem.quantity > remItem.quantity) { return false; } if (combItem.quantity > remItem.item.getRemaining()) { return false; } break; } } if (!found) { return false; } } // 实际分配 for (RemainderItem combItem : combination) { for (RemainderItem remItem : remaining) { if (remItem.item.name.equals(combItem.item.name)) { if (locker.addItem(combItem.item, combItem.quantity)) { remItem.quantity -= combItem.quantity; } else { return false; } break; } } } return true; } /** * 回滚分配 */ private static void rollbackAllocation(List<RemainderItem> remaining, Locker locker) { for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) { String itemName = entry.getKey(); int quantity = entry.getValue(); for (RemainderItem ri : remaining) { if (ri.item.name.equals(itemName)) { ri.quantity += quantity; ri.item.allocated -= quantity; // 回滚分配数量 break; } } } } /** * 标准最佳适应算法 */ private static List<Locker> standardBestFit(List<RemainderItem> remainderItems, int startLockerId) { List<Locker> lockers = new ArrayList<>(); int lockerId = startLockerId; // 创建副本 List<RemainderItem> remaining = new ArrayList<>(); for (RemainderItem ri : remainderItems) { if (ri.quantity > 0) { remaining.add(new RemainderItem(ri.item, ri.quantity)); } } // 按单位空间排序 remaining.sort((a, b) -> Double.compare(b.item.unitSpace, a.item.unitSpace)); for (RemainderItem ri : remaining) { while (ri.quantity > 0 && ri.item.getRemaining() > 0) { Locker bestLocker = null; double bestRemainingSpace = Double.MAX_VALUE; // 寻找最佳储物柜 for (Locker locker : lockers) { if (locker.canAdd(ri.item, 1) && locker.remainingSpace < bestRemainingSpace) { bestLocker = locker; bestRemainingSpace = locker.remainingSpace; } } if (bestLocker != null) { int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / ri.item.unitSpace); int actualAdd = Math.min(ri.quantity, maxCanAdd); actualAdd = Math.min(actualAdd, ri.item.getRemaining()); // 重要修复 if (actualAdd > 0 && bestLocker.addItem(ri.item, actualAdd)) { ri.quantity -= actualAdd; } else { break; } } else { // 创建新储物柜 Locker newLocker = new Locker(lockerId++); int maxCanAdd = (int) Math.floor(1.0 / ri.item.unitSpace); int actualAdd = Math.min(ri.quantity, maxCanAdd); actualAdd = Math.min(actualAdd, ri.item.getRemaining()); // 重要修复 if (actualAdd > 0 && newLocker.addItem(ri.item, actualAdd)) { lockers.add(newLocker); ri.quantity -= actualAdd; } else { break; } } } } return lockers; } /** * 验证解决方案(修复验证逻辑) */ private static boolean validateSolution(List<Item> originalItems, PackingSolution solution) { Map<String, Integer> allocatedQuantities = new HashMap<>(); Map<String, Integer> originalQuantities = new HashMap<>(); // 记录原始数量 for (Item item : originalItems) { originalQuantities.put(item.name, item.stock); } // 计算分配数量 for (Locker locker : solution.lockers) { for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) { String itemName = entry.getKey(); int quantity = entry.getValue(); allocatedQuantities.put(itemName, allocatedQuantities.getOrDefault(itemName, 0) + quantity); } } boolean valid = true; for (Map.Entry<String, Integer> entry : originalQuantities.entrySet()) { String itemName = entry.getKey(); int originalQty = entry.getValue(); int allocatedQty = allocatedQuantities.getOrDefault(itemName, 0); if (originalQty != allocatedQty) { System.out.println("验证失败: " + itemName + " 原始数量=" + originalQty + ", 分配数量=" + allocatedQty); valid = false; } } if (valid) { System.out.println("数量验证通过"); } return valid; } /** * 性能测试和比较 */ public static void performanceTest() { System.out.println("=== 性能测试 ==="); // 测试数据 List<Item> testItems = Arrays.asList( new Item("X", 20, 87), new Item("Y", 15, 63), new Item("Z", 34, 125), new Item("T", 25, 48), new Item("U", 40, 92) ); System.out.println("测试物品数量: " + testItems.size()); int totalItems = testItems.stream().mapToInt(item -> item.stock).sum(); System.out.println("总物品数量: " + totalItems); long startTime = System.currentTimeMillis(); PackingSolution solution = optimizedPacking(testItems); long endTime = System.currentTimeMillis(); System.out.println("\n优化算法结果:"); System.out.println("处理时间: " + (endTime - startTime) + "ms"); System.out.println("储物柜数量: " + solution.totalLockersUsed); System.out.println("纯种类储物柜: " + solution.pureLockers); System.out.println("空间利用率: " + String.format("%.2f", solution.overallUtilization * 100) + "%"); } } src/main/java/com/zy/asrs/utils/ToSortLineUtils.java
@@ -1,7 +1,7 @@ package com.zy.asrs.utils; import com.zy.asrs.entity.OrderDetl; import com.zy.asrs.entity.param.OrderToLine; import com.zy.asrs.utils.param.ItemUtilParam; import java.util.ArrayList; import java.util.List; @@ -36,34 +36,29 @@ return sku+sign_F+po+sign_F+upc; } public static OrderToLine GetOrderToLineGro(List<GroupedLockerOptimizerUtils.Item> items,OrderToLine orderToLine){ 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 货源 locker.bindingTags ); matLists.add(mat); } public static OrderToLine GetOrderToLine(List<ItemUtilParam.Item> items, OrderToLine orderToLine,String sign){ switch (sign){ case "Opt": List<OptimizedLockerPackingUtils.Item> itemsOpt = new ArrayList<>(); for (ItemUtilParam.Item item:items){ itemsOpt.add(new OptimizedLockerPackingUtils.Item(item.getName(),item.getMaxCapacity(),item.getStock())); } return GetOrderToLineOpt(itemsOpt,orderToLine); case "Opt3": List<OptimizedLockerPacking3Utils.Item> itemsOpt3 = new ArrayList<>(); for (ItemUtilParam.Item item:items){ itemsOpt3.add(new OptimizedLockerPacking3Utils.Item(item.getName(),item.getMaxCapacity(),item.getStock())); } return GetOrderToLineOpt3(itemsOpt3,orderToLine); default: return null; } orderToLine.setMatList(matLists); return orderToLine; } public static OrderToLine GetOrderToLineOpt(List<OptimizedLockerPackingUtils.Item> items,OrderToLine orderToLine){ List<OrderToLine.MatList> matLists = new ArrayList<>(); // 使用分组优先算法 OptimizedLockerPackingUtils.PackingSolution packingSolution = OptimizedLockerPackingUtils.optimizedPacking(items); // System.out.println("\n" + packingSolution); for (OptimizedLockerPackingUtils.Locker locker:packingSolution.lockers) { for (String mantnr : locker.contents.keySet()){ System.out.println(mantnr+"<===>"+locker.contents.get(mantnr)); @@ -83,4 +78,30 @@ orderToLine.setMatList(matLists); return orderToLine; } public static OrderToLine GetOrderToLineOpt3(List<OptimizedLockerPacking3Utils.Item> items,OrderToLine orderToLine){ List<OrderToLine.MatList> matLists = new ArrayList<>(); // 使用分组优先算法 OptimizedLockerPacking3Utils.PackingSolution packingSolution = OptimizedLockerPacking3Utils.optimizedPacking(items); // System.out.println("\n" + packingSolution); for (OptimizedLockerPacking3Utils.Locker locker:packingSolution.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 货源 locker.bindingTags ); matLists.add(mat); } } orderToLine.setMatList(matLists); return orderToLine; } } src/main/java/com/zy/asrs/utils/param/ItemUtilParam.java
@@ -2,24 +2,78 @@ import lombok.Data; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @Data public class ItemUtilParam { String name; double unitSpace; int maxCapacity; int stock; int remaining; public ItemUtilParam(String name, int maxCapacity, int stock) { this.name = name; this.maxCapacity = maxCapacity; this.unitSpace = 1.0 / maxCapacity; this.stock = stock; this.remaining = stock; @Data 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 + "个)"; } } @Override public String toString() { return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)"; static class Locker { int id; double remainingSpace; long bindingTags; 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<>(); this.bindingTags = System.currentTimeMillis(); } public boolean canAdd(ItemUtilParam.Item item, int quantity) { return remainingSpace >= quantity * item.unitSpace; } public void addItem(ItemUtilParam.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); } @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(); } } } src/main/java/com/zy/common/config/ControllerResAdvice.java
@@ -85,7 +85,6 @@ ApiLog apiLog = apiLogService.selectOne(new EntityWrapper<ApiLog>() .eq("namespace", name) .eq("request", request) .eq("response", response) .eq("result", success? 1:0) .orderBy("create_time", false) src/main/webapp/static/js/apiLog/apiLog.js
@@ -21,24 +21,25 @@ cellMinWidth: 50, height: 'full-120', cols: [[ {type: 'checkbox'} // {type: 'checkbox'} // ,{field: 'id', align: 'center',title: 'ID'} // ,{field: 'uuid', align: 'center',title: '日志编号'} ,{field: 'namespace', align: 'center',title: '名称空间'} ,{field: 'url', align: 'center',title: '表单ID'} // , {field: 'namespace', align: 'center',title: '名称空间',hide: false} ,{field: 'url', align: 'center',title: '接口地址',hide: false} ,{field: 'appkey', align: 'center',title: '平台密钥',hide: true} // ,{field: 'timestamp', align: 'center',title: '时间戳'} ,{field: 'clientIp', align: 'center',title: 'URL',hide: true} ,{field: 'request', align: 'center',title: '请求内容'} ,{field: 'response', align: 'center',title: '操作内容'} // ,{field: 'err', align: 'center',title: '异常内容'} ,{field: 'result$', align: 'center',title: '结果', templet: '#resTpl', width: 80} ,{field: 'timestamp', align: 'center',title: '时间戳',hide: true} ,{field: 'clientIp', align: 'center',title: '客户端IP',hide: false} ,{field: 'request', align: 'center',title: '请求内容',hide: false} ,{field: 'response', align: 'center',title: '操作内容',hide: false} // ,{field: 'err', align: 'center',title: '异常内容',hide: true} ,{field: 'result$', align: 'center',title: '结果', templet: '#resTpl', width: 80,hide: false} // ,{field: 'status$', align: 'center',title: '状态'} ,{field: 'createTime$', align: 'center',title: '添加时间'} ,{field: 'createTime$', align: 'center',title: '添加时间',hide: false} // ,{field: 'updateTime$', align: 'center',title: '修改时间'} ,{field: 'memo', align: 'center',title: '备注', hide: true} ,{fixed: 'right', title:'操作', align: 'center', toolbar: '#operate', width: 80} ,{fixed: 'right', title:'操作', align: 'center', toolbar: '#operate', width: 80, hide: true} ]], request: { pageName: 'curr', src/main/webapp/views/apiLog/apiLog.html
@@ -24,6 +24,12 @@ </div> </div> <div class="layui-inline"> <label class="layui-form-label">接口地址: </label> <div class="layui-input-inline"> <input class="layui-input" type="text" name="url" placeholder="请输入接口地址" autocomplete="off"> </div> </div> <div class="layui-inline"> <label class="layui-form-label">请求内容:</label> <div class="layui-input-inline"> <input class="layui-input" type="text" name="request" placeholder="请输入(订单号或品号)" autocomplete="off"> @@ -47,7 +53,7 @@ <script type="text/html" id="toolbar"> <div class="layui-btn-container"> <!-- <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">新增</button>--> <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">删除</button> <!-- <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">删除</button>--> <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">导出</button> </div> </script>