From 126414e8753c27bf871e9b0a6b9b24a881d2206b Mon Sep 17 00:00:00 2001 From: "HUOJIN\\92525" <925259474@qq.com> Date: Sun, 7 Dec 2025 23:11:32 +0800 Subject: [PATCH] no message --- .../cpte/modules/base/mapper/PointMapper.java | 4 +- .../modules/base/mapper/xml/PointMapper.xml | 28 +-- .../modules/base/service/IPointService.java | 18 +- .../base/service/impl/PointServiceImpl.java | 15 +- .../modules/constant/enums/AreaTypeEnum.java | 4 +- .../conveyorLine/request/ScanTrayRequest.java | 2 - .../impl/IConveyorLineServiceImpl.java | 12 ++ .../inventory/mapper/InventoryMapper.java | 2 +- .../inventory/mapper/xml/InventoryMapper.xml | 56 ++++-- .../service/IInventoryLogService.java | 3 +- .../service/impl/InventoryLogServiceImpl.java | 4 +- .../service/impl/ISaiWmsServiceImpl.java | 2 +- .../service/impl/PickServiceImpl.java | 175 ++++++++++++++++-- .../modules/shipping/vo/InventoryScore.java | 21 +++ .../src/main/resources/application-dev.yml | 2 +- 15 files changed, 284 insertions(+), 64 deletions(-) create mode 100644 cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryScore.java diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/PointMapper.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/PointMapper.java index 0d61250..87a896a 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/PointMapper.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/PointMapper.java @@ -47,6 +47,6 @@ public interface PointMapper extends BaseMapper { @Select("SELECT COUNT(id) FROM base_point WHERE col_num = #{colNum} AND layer_num = #{layerNum} AND status = 1") int countOccupiedInSameColumn(@Param("colNum") String colNum, @Param("layerNum") String layerNum); - - + @Select("SELECT * FROM base_point WHERE col_num = #{colNum} AND layer_num = #{layerNum} ORDER BY row_num ASC") + List findByColAndLayer(@Param("colNum") String colNum, @Param("layerNum") String layerNum); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/xml/PointMapper.xml b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/xml/PointMapper.xml index 31c0bf0..dc69030 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/xml/PointMapper.xml +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/xml/PointMapper.xml @@ -22,18 +22,24 @@ JOIN base_area ba ON bp1.area_id = ba.id JOIN base_point bp2 ON bp1.col_num = bp2.col_num JOIN data_inventory inv ON bp2.id = inv.point_id - - AND inv.item_id = #{itemId} - + WHERE inv.item_id = #{itemId} + AND bp1.status = 0 + AND ba.area_code = #{areaCode} + + AND inv.prop_c1 = #{propC1} - - + + + AND (inv.prop_c1 IS NULL OR inv.prop_c1 = '') + + + + AND inv.wh_code = #{whCode} - - - AND ba.area_code = #{areaCode} - - AND bp1.status = 0 - + + + AND (inv.wh_code IS NULL OR inv.wh_code = '') + + \ No newline at end of file diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IPointService.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IPointService.java index 1077699..4951bc3 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IPointService.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IPointService.java @@ -80,5 +80,21 @@ public interface IPointService extends IService { */ String getElevatorPoint(String pointCode, String key); - Point queryToPoint(String pointCode, Integer status, String areaCode); + /** + * 通过巷道编码、层数查询库位信息 + * + * @param colNum 巷道编码 + * @return layerNum 层数 + */ + List findByColAndLayer(String colNum, String layerNum); + + /** + * 查询库位信息 + * + * @param pointCode 库位编码 + * @param status 状态 + * @param areaCode 库区编码 + * @return List + */ + List queryPoints(String pointCode, Integer status, String areaCode); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/PointServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/PointServiceImpl.java index 750ede4..e897236 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/PointServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/PointServiceImpl.java @@ -144,12 +144,13 @@ public class PointServiceImpl extends ServiceImpl implements } @Override - public Point queryToPoint(String pointCode, Integer status, String areaCode) { - Point dstPoint = null; - List dstPointList = pointMapper.queryPoints(pointCode, status, areaCode); - if (dstPointList.isEmpty()) { - throw new RuntimeException("【" + AreaTypeEnum.CPCCQ.getDesc() + "】无空闲库位"); - } - return dstPoint = dstPointList.get(0); + public List findByColAndLayer(String colNum, String layerNum) { + return pointMapper.findByColAndLayer(colNum, layerNum); } + + @Override + public List queryPoints(String pointCode, Integer status, String areaCode) { + return pointMapper.queryPoints(pointCode, status, areaCode); + } + } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AreaTypeEnum.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AreaTypeEnum.java index a38b35e..d8cf9a5 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AreaTypeEnum.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AreaTypeEnum.java @@ -15,9 +15,9 @@ public enum AreaTypeEnum { MJCCQ("MJCCQ", "模具存储区"), - RK_DOCK("RK_DOCK", "入库输送线接驳口"), + RK_DOCK("RK_DOCK", "入库工作站"), - CK_DOCK("CK_DOCK", "出库输送线接驳口"), + CK_DOCK("CK_DOCK", "出库工作站"), ; /** * 值 diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/request/ScanTrayRequest.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/request/ScanTrayRequest.java index 1c2aefe..df3799a 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/request/ScanTrayRequest.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/request/ScanTrayRequest.java @@ -7,12 +7,10 @@ import lombok.Data; @Data public class ScanTrayRequest { //托盘码 - @NotBlank(message = "托盘码不能为空") @JsonProperty("stockCode") private String stockCode; //工作站 - @NotBlank(message = "工作站不能为空") @JsonProperty("station") private String station; } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/service/impl/IConveyorLineServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/service/impl/IConveyorLineServiceImpl.java index a968d61..dabded2 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/service/impl/IConveyorLineServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/service/impl/IConveyorLineServiceImpl.java @@ -65,9 +65,21 @@ public class IConveyorLineServiceImpl implements IConveyorLineService { @Autowired private IAgvTaskService iAgvTaskService; + private void validateParams(ScanTrayRequest scanTrayRequest) { + if (StringUtils.isBlank(scanTrayRequest.getStockCode())) { + throw new RuntimeException("托盘码不能为空"); + } + if (StringUtils.isBlank(scanTrayRequest.getStation())) { + throw new RuntimeException("工作站不能为空"); + } + } + @Override @Transactional(rollbackFor = Exception.class) public void scanTray(ScanTrayRequest scanTrayRequest) { + //验证参数 + validateParams(scanTrayRequest); + //工作站 Point srcPoint = pointMapper.queryByPointCode(scanTrayRequest.getStation()); diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/InventoryMapper.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/InventoryMapper.java index 2476788..8ad558b 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/InventoryMapper.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/InventoryMapper.java @@ -35,7 +35,7 @@ public interface InventoryMapper extends BaseMapper { * @param whCodeList 外部仓库 * @return List */ - List queryInventoryWithLock(@Param("itemIds") List itemIds, @Param("propC1List") List propC1List, @Param("propC3List") List propC3List, @Param("whCodeList") List whCodeList); + List queryInventory(@Param("itemIds") List itemIds, @Param("propC1List") List propC1List, @Param("propC3List") List propC3List, @Param("whCodeList") List whCodeList); // 查询相邻库位(同一巷道,深度±3范围内) @Select("SELECT di.* " + diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/xml/InventoryMapper.xml b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/xml/InventoryMapper.xml index 9745df8..f18c0c4 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/xml/InventoryMapper.xml +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/mapper/xml/InventoryMapper.xml @@ -1,7 +1,7 @@ - SELECT * FROM data_inventory WHERE status in (1,2) AND quantity > 0 @@ -9,25 +9,41 @@ #{itemId} - - AND prop_c1 IN - - #{propC1} - - - - AND prop_c3 IN - - #{propC3} - - - - AND wh_code IN - - #{whCode} - - + + + AND prop_c1 IN + + #{propC1} + + + + AND (prop_c1 IS NULL OR prop_c1 = '') + + + + + + AND prop_c3 IN + + #{propC3} + + + + AND (prop_c3 IS NULL OR prop_c3 = '') + + + + + + AND wh_code IN + + #{whCode} + + + + AND (wh_code IS NULL OR wh_code = '') + + ORDER BY create_time - FOR UPDATE \ No newline at end of file diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/IInventoryLogService.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/IInventoryLogService.java index 0b2c110..7c77e00 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/IInventoryLogService.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/IInventoryLogService.java @@ -49,12 +49,13 @@ public interface IInventoryLogService extends IService { * 添加分配库存日志 * * @param inventory 库存 + * @param dstPointId 目标库位 * @param AllocatedQty 分配数量 * @param businessNo 业务单号 * @param businessDetailId 业务明细ID * @param description 描述 */ - void addAllocInventoryLog(Inventory inventory, BigDecimal AllocatedQty, String businessNo, Long businessDetailId, String description); + void addAllocInventoryLog(Inventory inventory, Long dstPointId, BigDecimal AllocatedQty, String businessNo, Long businessDetailId, String description); /** diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/impl/InventoryLogServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/impl/InventoryLogServiceImpl.java index 3fa792c..58444b2 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/impl/InventoryLogServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventoryLog/service/impl/InventoryLogServiceImpl.java @@ -61,10 +61,12 @@ public class InventoryLogServiceImpl extends ServiceImpl implements IP List whCodeList = pickMap.values().stream().map(Pick::getWhCode).filter(StringUtils::hasText).distinct().toList(); //查询库存 - List inventories = inventoryMapper.queryInventoryWithLock(itemIds, propC1List, propC3List, whCodeList); + List inventories = inventoryMapper.queryInventory(itemIds, propC1List, propC3List, whCodeList); if (CollectionUtils.isEmpty(inventories)) { String itemCodes = itemMap.values().stream().map(Item::getItemCode).collect(Collectors.joining(",")); errorMsgSet.add("【" + itemCodes + "】物料库存不足"); @@ -374,14 +376,14 @@ public class PickServiceImpl extends ServiceImpl implements IP continue; } - //获取输送线工作台点位,均衡分配点位任务-轮询方式 - Point toPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CK_DOCK.getValue(), GeneralConstant.CK_DOCK_TASK_INDEX); - //记录当前容器 - Long currStockId = 0L; - for (Inventory inventory : matchedInventories) { + //智能排序,优先分配移位最小的库位 + List scoredInventory = scoreInventories(matchedInventories); + + for (InventoryScore inventoryScore : scoredInventory) { if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) { break; } + Inventory inventory = inventoryScore.getInventory(); // 库存可用数量 BigDecimal availableQty = BigDecimalUtil.subtract(inventory.getQuantity(), inventory.getQueuedQty(), 0); if (availableQty.compareTo(BigDecimal.ZERO) <= 0) { @@ -405,20 +407,26 @@ public class PickServiceImpl extends ServiceImpl implements IP Point fromPoint = pointMap.get(inventory.getPointId()); //是否整托:0整托、1拆托 Integer izAll = availableQty.compareTo(allocateQty) == 0 ? 0 : 1; - //判断当前容器是否一致,同一个容器去同一个目标库位 - if (!currStockId.equals(stock.getId())) { - toPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CK_DOCK.getValue(), GeneralConstant.CK_DOCK_TASK_INDEX); - } + //目标库位 + Point toPoint = inventoryScore.getOutPoint(); //构建拣货任务存入集合批量新增 Task task = iTaskService.bulidTask(pick.getNo() + "_" + pickDetail.getLineNo(), TaskTypeEnum.PICK.getValue(), item, fromPoint, toPoint, stock, pick.getId(), pickDetail.getId(), inventory.getId(), allocateQty, izAll); createToTask.add(task); log.info("生成拣货任务:{}- 容器:{} - 库位:{} - 类型:{}- 分配数量:{}", task.getTaskNo(), stock.getStockCode(), fromPoint.getPointCode(), izAll, allocateQty); + + //移位任务 + if (CollectionUtils.isNotEmpty(inventoryScore.getMovePoints())) { + for (Point movePoint : inventoryScore.getMovePoints()) { + log.info("生成移位任务:原库位:{}", movePoint.getPointCode()); + } + } else { + log.info("无移位任务"); + } + //分配库存日志 - iInventoryLogService.addAllocInventoryLog(inventory, allocateQty, pick.getOrderNo(), pickDetail.getId(), pickDetail.getDescription()); + iInventoryLogService.addAllocInventoryLog(inventory, toPoint.getId(), allocateQty, pick.getOrderNo(), pickDetail.getId(), pickDetail.getDescription()); //更新未分配数量 unAllocatedQty = BigDecimalUtil.subtract(unAllocatedQty, allocateQty, 0); - //更新当前容器 - currStockId = stock.getId(); } //分配后仍有缺货,记录错误 if (unAllocatedQty.compareTo(BigDecimal.ZERO) > 0) { @@ -453,6 +461,145 @@ public class PickServiceImpl extends ServiceImpl implements IP return new ArrayList<>(errorMsgSet); } + /** + * 计算库存得分 + * + * @param matchedInventories 库存集合 + * @return List + */ + private List scoreInventories(List matchedInventories) { + // 批量查询库位 + List pointIds = matchedInventories.stream() + .map(Inventory::getPointId) + .toList(); + Map pointMap = iPointService.queryByPointIdsToMap(pointIds); + + // 按巷道和层分组 + Map> colLayerPointsMap = new HashMap<>(); + + for (Point point : pointMap.values()) { + String key = point.getColNum() + "-" + point.getLayerNum(); + if (colLayerPointsMap.containsKey(key)) { + continue; + } + List points = iPointService.findByColAndLayer(point.getColNum(), point.getLayerNum()); + colLayerPointsMap.put(key, points); + } + + //获取所以出库口的库位 + List outPoints = iPointService.queryPoints(null, null, AreaTypeEnum.CK_DOCK.getValue()); + + //获取优化后的库存 + return matchedInventories.stream() + .map(inventory -> { + Point currPoint = pointMap.get(inventory.getPointId()); + String key = currPoint.getColNum() + "-" + currPoint.getLayerNum(); + List points = colLayerPointsMap.get(key); + return calculateMoveCount(inventory, currPoint, points, outPoints); + }) + .sorted(Comparator.comparing(InventoryScore::getScore).reversed()) + .toList(); + } + + + /** + * 计算位移次数核心算法 + * + * @param inventory 库存 + * @param currPoint 当前库位 + * @param points 当前库位巷道的库位集合 + * @return 库位位移次数 + */ + private InventoryScore calculateMoveCount(Inventory inventory, Point currPoint, List points, List outPoints) { + double totalScore = 0.0; + + // 计算距离分数(权重30%) + Point bestPoint = getBestOutboundPoint(currPoint, outPoints); + double distanceScore = calculateClusterDistanceCost(currPoint, bestPoint); + totalScore += distanceScore * 0.3; + + // 目标库位的深度位转换为索引 + int targetIndex = Integer.parseInt(currPoint.getRowNum()) - 1; + + //双通道 + if (currPoint.getIzDoubleLane().equals(1)) { + // 计算左侧占用数 + int leftOccupied = 0; + List leftPoints = new ArrayList<>(); + for (int i = 0; i < targetIndex; i++) { + Point point = points.get(i); + if (point != null && point.getStatus().equals(CommonStatusEnum.USED.getValue())) { + leftOccupied++; + leftPoints.add(point); + } + } + + // 计算右侧占用数 + int rightOccupied = 0; + List rightPoints = new ArrayList<>(); + for (int i = targetIndex + 1; i < points.size(); i++) { + Point point = points.get(i); + if (point != null && point.getStatus().equals(CommonStatusEnum.USED.getValue())) { + rightOccupied++; + rightPoints.add(point); + } + } + //取两个集合中,元素最少的那个集合 + List movePoints = leftOccupied < rightOccupied ? leftPoints : rightPoints; + // 取最小值,移位越小分数越高 + int minOccupied = Math.min(leftOccupied, rightOccupied); + if (minOccupied == 0) { + minOccupied = 1; // 设置默认值防止除以0 + } + totalScore += 100.0 / minOccupied * 0.7; + return new InventoryScore(inventory, totalScore, bestPoint, movePoints); + } else { + // 单通道,计算目标位置左侧的占用数 + int leftOccupied = 0; + List movePoints = new ArrayList<>(); + for (int i = 0; i < targetIndex; i++) { + Point point = points.get(i); + if (point != null && point.getStatus().equals(CommonStatusEnum.USED.getValue())) { + leftOccupied++; + movePoints.add(point); + } + } + if (leftOccupied == 0) { + leftOccupied = 1; + } + totalScore += 100.0 / leftOccupied * 0.7; + return new InventoryScore(inventory, totalScore, bestPoint, movePoints); + } + } + + /** + * @param currPoint 当前库位 + * @param outPoints 出库口集合 + * @return 最佳出库口 + */ + private Point getBestOutboundPoint(Point currPoint, List outPoints) { + //获取距离最近的出库口 + return outPoints.stream() + .min(Comparator.comparingDouble(point -> + Math.abs(currPoint.getPositionX() - point.getPositionX()) + + Math.abs(currPoint.getPositionY() - point.getPositionY()))) + .orElse(null); + + } + + /** + * 计算距离评分 + * 基于出库口位置和库位坐标计算最短路径距离 + */ + private double calculateClusterDistanceCost(Point point, Point station) { + // 计算曼哈顿距离 + double distance = Math.abs(point.getPositionX() - station.getPositionX()) + Math.abs(point.getPositionY() - station.getPositionY()); + + // 距离越小分数越高 + return Math.max(0, 100 - (distance / 100.0)); + } + + @Override @Transactional(rollbackFor = Exception.class) public List cancelAllocate(List pickIds) { @@ -528,7 +675,7 @@ public class PickServiceImpl extends ServiceImpl implements IP @Override @Transactional(rollbackFor = Exception.class) - public void pickTask(List tasks,Point endPoint) { + public void pickTask(List tasks, Point endPoint) { if (CollectionUtils.isEmpty(tasks)) { throw new RuntimeException("无拣货任务"); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryScore.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryScore.java new file mode 100644 index 0000000..5502c1e --- /dev/null +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryScore.java @@ -0,0 +1,21 @@ +package org.cpte.modules.shipping.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.cpte.modules.base.entity.Point; +import org.cpte.modules.inventory.entity.Inventory; + +import java.util.List; + +@Data +@AllArgsConstructor +public class InventoryScore { + // 库存 + private Inventory inventory; + // 分数 + private double score; + //最佳出库口 + private Point outPoint; + //移位的库位 + private List movePoints; +} diff --git a/cpte-module-system/cpte-system-start/src/main/resources/application-dev.yml b/cpte-module-system/cpte-system-start/src/main/resources/application-dev.yml index 5a99c3f..4b71adf 100644 --- a/cpte-module-system/cpte-system-start/src/main/resources/application-dev.yml +++ b/cpte-module-system/cpte-system-start/src/main/resources/application-dev.yml @@ -178,7 +178,7 @@ mybatis-plus: table-underline: true configuration: # # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 - #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 返回类型为Map,显示null对应的字段 call-setters-on-nulls: true #jeecg专用配置