From f5304c1f31979d9ea7f66a9d31fca47af8b86bd7 Mon Sep 17 00:00:00 2001 From: "HUOJIN\\92525" <925259474@qq.com> Date: Sun, 16 Nov 2025 20:23:29 +0800 Subject: [PATCH] no message --- .../cpte/modules/base/mapper/AreaMapper.java | 9 - .../cpte/modules/base/mapper/ItemMapper.java | 9 - .../cpte/modules/base/mapper/PointMapper.java | 9 - .../cpte/modules/base/mapper/StockMapper.java | 9 - .../modules/base/service/IItemService.java | 7 + .../modules/base/service/IPointService.java | 8 + .../modules/base/service/IStockService.java | 9 + .../base/service/impl/ItemServiceImpl.java | 24 +- .../base/service/impl/PointServiceImpl.java | 24 +- .../base/service/impl/StockServiceImpl.java | 23 +- .../inventory/mapper/InventoryMapper.java | 15 +- .../inventory/mapper/xml/InventoryMapper.xml | 29 +- .../inventory/service/IInventoryService.java | 2 + .../service/impl/InventoryServiceImpl.java | 12 + .../service/impl/InventoryLogServiceImpl.java | 3 +- .../cpte/modules/quartz/job/AllocateJob.java | 84 ++++ .../cpte/modules/quartz/job/TesAgvJob.java | 3 + .../receive/mapper/AsnDetailMapper.java | 3 + .../receive/service/impl/AsnServiceImpl.java | 54 ++- .../saiWms/controller/SaiWmsController.java | 4 +- .../saiWms/request/OutboundRequest.java | 1 - .../service/impl/ISaiWmsServiceImpl.java | 2 +- .../modules/shipping/entity/PickDetail.java | 1 + .../shipping/mapper/PickDetailMapper.java | 10 + .../shipping/service/IPickService.java | 77 ++-- .../service/impl/PickServiceImpl.java | 401 +++++++++++++++++- .../shipping/vo/InventoryGroupKey.java | 27 ++ .../system/controller/SysLogController.java | 2 +- 28 files changed, 739 insertions(+), 122 deletions(-) create mode 100644 cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/quartz/job/AllocateJob.java create mode 100644 cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryGroupKey.java diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/AreaMapper.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/AreaMapper.java index 295c6bd..fdf3b5e 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/AreaMapper.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/AreaMapper.java @@ -22,13 +22,4 @@ public interface AreaMapper extends BaseMapper { */ @Select("select * from base_area where area_code = #{areaCode}") Area queryByAreaCode(@Param("areaCode") String areaCode); - - /** - * 通过库区编码查询库区信息 - * - * @param areaCodes 库区编号集合 - * @return List - */ - @Select("select * from base_area where area_code in (#{areaCodes})") - List queryByAreaCodes(@Param("areaCodes") List areaCodes); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/ItemMapper.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/ItemMapper.java index 5119691..05bae38 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/ItemMapper.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/ItemMapper.java @@ -23,13 +23,4 @@ public interface ItemMapper extends BaseMapper { */ @Select("select * from base_item where item_code = #{itemCode}") Item queryByItemCode(@Param("itemCode") String itemCode); - - /** - * 通过物料编码查询物料信息 - * - * @param itemCodes 物料编号集合 - * @return List - */ - @Select("select * from base_item where item_code in (#{itemCodes})") - List queryByItemCodes(@Param("itemCodes") List itemCodes); } 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 b3719a2..be8aaef 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 @@ -23,15 +23,6 @@ public interface PointMapper extends BaseMapper { @Select("select * from base_point where point_code = #{pointCode}") Point queryByPointCode(@Param("pointCode") String pointCode); - /** - * 通过库位编码查询库位信息 - * - * @param pointCodes 库位编号集合 - * @return List - */ - @Select("select * from base_point where point_code in (#{pointCodes})") - List queryByPointCodes(@Param("pointCodes") List pointCodes); - /** * 查询库位信息 * diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/StockMapper.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/StockMapper.java index f22e649..3e52af1 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/StockMapper.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/mapper/StockMapper.java @@ -22,13 +22,4 @@ public interface StockMapper extends BaseMapper { */ @Select("select * from base_stock where stock_code = #{stockCode}") Stock queryByStockCode(@Param("stockCode") String stockCode); - - /** - * 通过容器编码查询容器信息 - * - * @param stockCodes 容器编号集合 - * @return List - */ - @Select("select * from base_stock where stock_code in (#{stockCodes})") - List queryByStockCodes(@Param("stockCodes") List stockCodes); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IItemService.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IItemService.java index 264d9c7..4002298 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IItemService.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IItemService.java @@ -36,5 +36,12 @@ public interface IItemService extends IService { */ Map queryByItemCodesToMap(List itemCodes); + /** + * 根据物料ID集合查询物料信息 + * + * @param itemIds 物料ID集合 + * @return Map + */ + Map queryByItemIdsToMap(List itemIds); } 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 48a4253..977cf4b 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 @@ -34,4 +34,12 @@ public interface IPointService extends IService { * @return Map */ Map queryByPointCodesToMap(List pointCodes); + + /** + * 根据库位ID集合查询库位信息 + * + * @param pointIds 库位集合 + * @return Map + */ + Map queryByPointIdsToMap(List pointIds); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IStockService.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IStockService.java index 0e546ab..97199c5 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IStockService.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/IStockService.java @@ -37,4 +37,13 @@ public interface IStockService extends IService { */ Map queryByStockCodesToMap(List stockCodes); + /** + * 根据容器ID集合查询容器信息 + * + * @param stockIds 容器ID集合 + * @return Map + */ + Map queryByStockIdsToMap(List stockIds); + + } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/ItemServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/ItemServiceImpl.java index 134d523..8f6b87a 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/ItemServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/ItemServiceImpl.java @@ -55,7 +55,15 @@ public class ItemServiceImpl extends ServiceImpl implements II * @return List */ public List queryByItemCodes(List itemCodes) { - return itemMapper.queryByItemCodes(itemCodes); + if (CollectionUtils.isEmpty(itemCodes)) { + return Collections.emptyList(); + } + //查询物料 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(Item::getItemCode, itemCodes); + queryWrapper.eq(Item::getDelFlag, 0); + queryWrapper.eq(Item::getIzActive, 1); + return itemMapper.selectList(queryWrapper); } /** @@ -77,4 +85,18 @@ public class ItemServiceImpl extends ServiceImpl implements II } return itemMap; } + + @Override + public Map queryByItemIdsToMap(List itemIds) { + if (CollectionUtils.isEmpty(itemIds)) { + return Collections.emptyMap(); + } + List itemList = this.listByIds(itemIds); + //封装成map + Map itemMap = Maps.newHashMap(); + for (Item item : itemList) { + itemMap.put(item.getId(), item); + } + return itemMap; + } } 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 f4a90ce..44a0d03 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 @@ -6,6 +6,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.cpte.modules.base.entity.Area; import org.cpte.modules.base.entity.Point; +import org.cpte.modules.base.entity.Stock; import org.cpte.modules.base.mapper.AreaMapper; import org.cpte.modules.base.mapper.PointMapper; import org.cpte.modules.base.service.IAreaService; @@ -62,7 +63,15 @@ public class PointServiceImpl extends ServiceImpl implements * @return List */ public List queryByPointCodes(List pointCodes) { - return pointMapper.queryByPointCodes(pointCodes); + if (CollectionUtils.isEmpty(pointCodes)) { + return Collections.emptyList(); + } + //查询库位 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(Point::getPointCode, pointCodes); + queryWrapper.eq(Point::getDelFlag, 0); + queryWrapper.eq(Point::getIzActive, 1); + return pointMapper.selectList(queryWrapper); } /** @@ -84,4 +93,17 @@ public class PointServiceImpl extends ServiceImpl implements } return PointMap; } + + @Override + public Map queryByPointIdsToMap(List pointIds) { + if (CollectionUtils.isEmpty(pointIds)) { + return Collections.emptyMap(); + } + List pointList = this.listByIds(pointIds); + Map pointMap = Maps.newHashMap(); + for (Point point : pointList) { + pointMap.put(point.getId(), point); + } + return pointMap; + } } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/StockServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/StockServiceImpl.java index ca33022..676ae15 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/StockServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/base/service/impl/StockServiceImpl.java @@ -59,7 +59,15 @@ public class StockServiceImpl extends ServiceImpl implements * @return List */ public List queryByStockCodes(List stockCodes) { - return stockMapper.queryByStockCodes(stockCodes); + if (CollectionUtils.isEmpty(stockCodes)) { + return Collections.emptyList(); + } + //查询容器 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(Stock::getStockCode, stockCodes); + queryWrapper.eq(Stock::getDelFlag, 0); + queryWrapper.eq(Stock::getIzActive, 1); + return stockMapper.selectList(queryWrapper); } /** @@ -81,4 +89,17 @@ public class StockServiceImpl extends ServiceImpl implements } return stockMap; } + + @Override + public Map queryByStockIdsToMap(List stockIds) { + if (CollectionUtils.isEmpty(stockIds)) { + return Collections.emptyMap(); + } + List stockList = this.listByIds(stockIds); + Map stockMap = Maps.newHashMap(); + for (Stock stock : stockList) { + stockMap.put(stock.getId(), stock); + } + return stockMap; + } } 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 aa09efc..881a4aa 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 @@ -1,12 +1,13 @@ package org.cpte.modules.inventory.mapper; -import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.cpte.modules.inventory.entity.Inventory; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import java.util.List; + /** * @Description: 库存表 * @author: cpte @@ -23,4 +24,16 @@ public interface InventoryMapper extends BaseMapper { */ @Select("select * from data_inventory where stock_id = #{stockId} and quantity>0 for update") Inventory queryByStockId(@Param("stockId") Long stockId); + + /** + * 查询库存列表 + * + * @param itemIds 物料ID + * @param propC1List 批次号 + * @param propC3List 外部库存状态 + * @param whCodeList 外部仓库 + * @return List + */ + List queryInventoryWithLock(@Param("itemIds") List itemIds, @Param("propC1List") List propC1List, @Param("propC3List") List propC3List, @Param("whCodeList") List whCodeList); + } 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 36265d3..6358ea0 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,5 +1,32 @@ - + \ No newline at end of file diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/IInventoryService.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/IInventoryService.java index 4146c0a..eaa396d 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/IInventoryService.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/IInventoryService.java @@ -7,6 +7,8 @@ import org.cpte.modules.receive.entity.AsnDetail; import org.cpte.modules.receive.entity.ReceiveRecord; import java.math.BigDecimal; +import java.util.List; +import java.util.Map; /** * @Description: 库存表 diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/impl/InventoryServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/impl/InventoryServiceImpl.java index e944274..adeba64 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/impl/InventoryServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/inventory/service/impl/InventoryServiceImpl.java @@ -1,5 +1,9 @@ package org.cpte.modules.inventory.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.commons.collections4.CollectionUtils; +import org.cpte.modules.base.entity.Area; +import org.cpte.modules.base.mapper.AreaMapper; import org.cpte.modules.constant.enums.InventoryStatusEnum; import org.cpte.modules.inventory.entity.Inventory; import org.cpte.modules.inventory.mapper.InventoryMapper; @@ -15,6 +19,8 @@ import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.Date; +import java.util.List; +import java.util.Map; /** * @Description: 库存表 @@ -24,6 +30,12 @@ import java.util.Date; */ @Service public class InventoryServiceImpl extends ServiceImpl implements IInventoryService { + + @Autowired + private AreaMapper areaMapper; + @Autowired + private InventoryMapper inventoryMapper; + @Override @Transactional(rollbackFor = Exception.class) public Inventory createInventory(Long stockId, BigDecimal receivedQty, Asn asn, AsnDetail asnDetail, ReceiveRecord receiveRecord) { 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 b696c40..e264280 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 @@ -62,11 +62,10 @@ public class InventoryLogServiceImpl extends ServiceImpl processedCache = new ConcurrentHashMap<>(); + + // 缓存最大大小,防止内存溢出 + private static final int MAX_CACHE_SIZE = 1000; + + @Override + public void execute(JobExecutionContext jobExecutionContext) { + // 查询未分配或者部分分配的出库明细 + List pickDetailList = pickDetailMapper.queryUnallocatedPickDetail(); + if (pickDetailList.isEmpty()) { + return; + } + // 分配出库明细 + long startTime = System.currentTimeMillis(); + List resultMsg = iPickService.allocatePickDetail2(pickDetailList); + long endTime = System.currentTimeMillis(); + log.info("分配出库明细耗时:{}ms", endTime - startTime); + if (CollectionUtils.isNotEmpty(resultMsg)) { + // 生成缓存键 + String cacheKey = generateCacheKey(resultMsg); + + // 检查是否已经处理过相同的内容 + if (!processedCache.containsKey(cacheKey)) { + // 新内容,记录日志 + baseCommonService.addLog("出库任务分配:" + "\n" + cacheKey, CommonConstant.LOG_TYPE_2, CommonConstant.OPERATE_TYPE_1); + + // 添加到缓存 + processedCache.put(cacheKey, true); + + // 控制缓存大小 + if (processedCache.size() > MAX_CACHE_SIZE) { + // 移除一部分旧缓存(简单实现,可以按需优化) + processedCache.clear(); + } + } + } + } + + /** + * 生成缓存键 - 基于分配结果生成唯一标识 + */ + private String generateCacheKey(List resultMsg) { + // 对结果进行排序后拼接,确保相同内容生成相同键 + return resultMsg.stream() + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.joining("\n")); + } +} diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/quartz/job/TesAgvJob.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/quartz/job/TesAgvJob.java index fcdb8d4..87b2742 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/quartz/job/TesAgvJob.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/quartz/job/TesAgvJob.java @@ -26,6 +26,9 @@ public class TesAgvJob implements Job { public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { // 查询待执行任务 List agvTaskList = agvTaskMapper.queryAgvTaskList(AgvStatusEnum.CREATED.getValue(), AgvVendorEnum.TES.getValue()); + if (agvTaskList.isEmpty()) { + return; + } String taskSubmitUrl = "http://localhost:8000/cpte-wms/tes/apiv2/newMovePodTask"; for (AgvTask agvTask : agvTaskList) { try { diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/mapper/AsnDetailMapper.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/mapper/AsnDetailMapper.java index 64702ca..5ac980a 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/mapper/AsnDetailMapper.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/mapper/AsnDetailMapper.java @@ -42,4 +42,7 @@ public interface AsnDetailMapper extends BaseMapper { */ @Select("select * from data_asn_detail where stock_id = #{stockId} and status = #{status} for update") AsnDetail queryByStockCode(@Param("stockId") Long stockId, @Param("status") Integer status); + + @Select("select MAX(line_no) from data_asn_detail where asn_id = #{asnId} ") + Integer queryMaxLineNoByAsnId(@Param("asnId") Long asnId); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/service/impl/AsnServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/service/impl/AsnServiceImpl.java index dbad152..877f37b 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/service/impl/AsnServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/receive/service/impl/AsnServiceImpl.java @@ -5,11 +5,9 @@ import org.cpte.modules.base.entity.Point; import org.cpte.modules.base.entity.Stock; import org.cpte.modules.base.mapper.PointMapper; import org.cpte.modules.base.mapper.StockMapper; -import org.cpte.modules.base.service.IStockService; import org.cpte.modules.constant.enums.AsnStatusEnum; import org.cpte.modules.constant.enums.CommonStatusEnum; import org.cpte.modules.inventory.entity.Inventory; -import org.cpte.modules.inventory.mapper.InventoryMapper; import org.cpte.modules.inventory.service.IInventoryService; import org.cpte.modules.inventoryLog.service.IInventoryLogService; import org.cpte.modules.receive.entity.Asn; @@ -46,7 +44,6 @@ public class AsnServiceImpl extends ServiceImpl implements IAsnS private StockMapper stockMapper; @Autowired private PointMapper pointMapper; - @Autowired private AsnMapper asnMapper; @Autowired @@ -62,15 +59,22 @@ public class AsnServiceImpl extends ServiceImpl implements IAsnS @Transactional(rollbackFor = Exception.class) public void saveMain(Asn asn, List asnDetailList) { asnMapper.insert(asn); - if (asnDetailList != null && !asnDetailList.isEmpty()) { - AtomicInteger lineNoCounter = new AtomicInteger(1); - for (AsnDetail entity : asnDetailList) { - if (entity.getLineNo() == null || entity.getLineNo() == 0) { - entity.setLineNo(lineNoCounter.getAndIncrement()); - } - entity.setAsnId(asn.getId()); - asnDetailMapper.insert(entity); + + if (asnDetailList == null || asnDetailList.isEmpty()) { + throw new RuntimeException("请新增入库明细"); + } + + if(asnDetailList.size()>1){ + throw new RuntimeException("入库明细只允许新增一条"); + } + + AtomicInteger lineNoCounter = new AtomicInteger(1); + for (AsnDetail entity : asnDetailList) { + if (entity.getLineNo() == null || entity.getLineNo() == 0) { + entity.setLineNo(lineNoCounter.getAndIncrement()); } + entity.setAsnId(asn.getId()); + asnDetailMapper.insert(entity); } //刷新入库单 refreshAsn(asn, asnDetailList); @@ -114,20 +118,28 @@ public class AsnServiceImpl extends ServiceImpl implements IAsnS @Override @Transactional(rollbackFor = Exception.class) public void updateMain(Asn asn, List asnDetailList) { - // 直接更新主表 asnMapper.updateById(asn); - // 更新明细表 - 只更新传入的明细数据 - if (asnDetailList != null && !asnDetailList.isEmpty()) { - for (AsnDetail entity : asnDetailList) { - entity.setAsnId(asn.getId()); - // 直接更新,而不是删除后重新插入 - if (entity.getId() != null) { - asnDetailMapper.updateById(entity); - } - } + if (asnDetailList == null || asnDetailList.isEmpty()) { + throw new RuntimeException("请新增入库明细"); } + if(asnDetailList.size()>1){ + throw new RuntimeException("入库明细只允许新增一条"); + } + + AtomicInteger lineNoCounter = new AtomicInteger(1); + for (AsnDetail entity : asnDetailList) { + if (entity.getLineNo() == null || entity.getLineNo() == 0) { + entity.setLineNo(lineNoCounter.getAndIncrement()); + } + entity.setAsnId(asn.getId()); + if (entity.getId() != null) { + asnDetailMapper.updateById(entity); + } else { + asnDetailMapper.insert(entity); + } + } // 刷新入库单状态 refreshAsn(asn, asnDetailList); diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/controller/SaiWmsController.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/controller/SaiWmsController.java index df1c27b..1cb7a6a 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/controller/SaiWmsController.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/controller/SaiWmsController.java @@ -36,7 +36,7 @@ public class SaiWmsController { @IgnoreAuth public Result inBoundTask(@RequestBody @Valid InboundRequest inboundRequest) { iSaiWmsService.inBoundTask(inboundRequest); - return Result.OK("下发成功!"); + return Result.OK("操作成功!"); } /** @@ -50,6 +50,6 @@ public class SaiWmsController { @IgnoreAuth public Result outBoundTask(@RequestBody @Valid OutboundRequest outboundRequest) { iSaiWmsService.outBoundTask(outboundRequest); - return Result.OK("下发成功!"); + return Result.OK("操作成功!"); } } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/request/OutboundRequest.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/request/OutboundRequest.java index 62344d7..6c1acf1 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/request/OutboundRequest.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/request/OutboundRequest.java @@ -39,7 +39,6 @@ public class OutboundRequest { private Integer type; // 生产车间 - @NotBlank(message = "生产车间") @JsonProperty("Enterprise") private String enterprise; diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/service/impl/ISaiWmsServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/service/impl/ISaiWmsServiceImpl.java index d8f9a43..f32861b 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/service/impl/ISaiWmsServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/saiWms/service/impl/ISaiWmsServiceImpl.java @@ -140,7 +140,7 @@ public class ISaiWmsServiceImpl implements ISaiWmsService { //获取不存在的物料 List notExitItemCodes = itemCodes.stream().filter(itemCode -> !exitItemMap.containsKey(itemCode)).toList(); if (!notExitItemCodes.isEmpty()) { - throw new RuntimeException("系统无【" + notExitItemCodes + "】物料,请维护"); + throw new RuntimeException("系统无" + notExitItemCodes + "物料,请维护"); } // 创建出库单和明细 diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/entity/PickDetail.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/entity/PickDetail.java index 4afe5ef..4d55156 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/entity/PickDetail.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/entity/PickDetail.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableLogic; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToEmptyObjectSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/mapper/PickDetailMapper.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/mapper/PickDetailMapper.java index 16a4618..00f59a4 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/mapper/PickDetailMapper.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/mapper/PickDetailMapper.java @@ -1,6 +1,8 @@ package org.cpte.modules.shipping.mapper; import java.util.List; + +import org.apache.ibatis.annotations.Select; import org.cpte.modules.shipping.entity.PickDetail; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; @@ -28,4 +30,12 @@ public interface PickDetailMapper extends BaseMapper { * @return List */ public List selectByMainId(@Param("mainId") Long mainId); + + @Select("select * from data_pick_detail where status = 1 " + + "union all " + + "select * from data_pick_detail where status = 2 ") + List queryUnallocatedPickDetail(); + + @Select("select MAX(line_no) from data_pick_detail where pick_id = #{pickId} ") + Integer queryMaxLineNoByPickId(@Param("pickId") Long pickId); } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/IPickService.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/IPickService.java index 70d0819..c663893 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/IPickService.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/IPickService.java @@ -3,6 +3,7 @@ package org.cpte.modules.shipping.service; import org.cpte.modules.shipping.entity.PickDetail; import org.cpte.modules.shipping.entity.Pick; import com.baomidou.mybatisplus.extension.service.IService; + import java.io.Serializable; import java.util.Collection; import java.util.List; @@ -10,39 +11,53 @@ import java.util.List; /** * @Description: 出库单 * @author: cpte - * @Date: 2025-11-14 + * @Date: 2025-11-14 * @Version: V1.0 */ public interface IPickService extends IService { - /** - * 添加一对多 - * - * @param pick - * @param pickDetailList - */ - public void saveMain(Pick pick,List pickDetailList) ; - - /** - * 修改一对多 - * - * @param pick - * @param pickDetailList - */ - public void updateMain(Pick pick,List pickDetailList); - - /** - * 删除一对多 - * - * @param id - */ - public void delMain (Long id); - - /** - * 批量删除一对多 - * - * @param idList - */ - public void delBatchMain (Collection idList); - + /** + * 添加一对多 + * + * @param pick + * @param pickDetailList + */ + public void saveMain(Pick pick, List pickDetailList); + + /** + * 修改一对多 + * + * @param pick + * @param pickDetailList + */ + public void updateMain(Pick pick, List pickDetailList); + + /** + * 删除一对多 + * + * @param id + */ + public void delMain(Long id); + + /** + * 批量删除一对多 + * + * @param idList + */ + public void delBatchMain(Collection idList); + + /** + * 分配出库单明细 + * + * @param pickDetails 出库单明细集合 + */ + List allocatePickDetail(List pickDetails); + + /** + * 分配出库单明细 + * + * @param pickDetails 出库单明细集合 + */ + List allocatePickDetail2(List pickDetails); + } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/impl/PickServiceImpl.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/impl/PickServiceImpl.java index 787efb8..eefdfbb 100644 --- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/impl/PickServiceImpl.java +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/service/impl/PickServiceImpl.java @@ -1,15 +1,30 @@ package org.cpte.modules.shipping.service.impl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.shiro.lang.util.StringUtils; +import org.cpte.modules.base.entity.Item; +import org.cpte.modules.base.entity.Point; +import org.cpte.modules.base.entity.Stock; +import org.cpte.modules.base.service.IItemService; +import org.cpte.modules.base.service.IPointService; +import org.cpte.modules.base.service.IStockService; import org.cpte.modules.constant.enums.AsnStatusEnum; +import org.cpte.modules.constant.enums.InventoryStatusEnum; import org.cpte.modules.constant.enums.PickStatusEnum; -import org.cpte.modules.receive.entity.Asn; -import org.cpte.modules.receive.entity.AsnDetail; +import org.cpte.modules.inventory.entity.Inventory; +import org.cpte.modules.inventory.mapper.InventoryMapper; +import org.cpte.modules.inventory.service.IInventoryService; +import org.cpte.modules.inventoryLog.service.IInventoryLogService; import org.cpte.modules.shipping.entity.Pick; import org.cpte.modules.shipping.entity.PickDetail; import org.cpte.modules.shipping.mapper.PickDetailMapper; import org.cpte.modules.shipping.mapper.PickMapper; import org.cpte.modules.shipping.service.IPickService; +import org.cpte.modules.shipping.vo.InventoryGroupKey; import org.cpte.modules.utils.BigDecimalUtil; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.modules.base.service.BaseCommonService; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; @@ -17,10 +32,9 @@ import org.springframework.transaction.annotation.Transactional; import java.io.Serializable; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; -import java.util.Collection; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; /** * @Description: 出库单 @@ -29,31 +43,72 @@ import java.util.concurrent.atomic.AtomicInteger; * @Version: V1.0 */ @Service +@Slf4j public class PickServiceImpl extends ServiceImpl implements IPickService { @Autowired private PickMapper pickMapper; @Autowired private PickDetailMapper pickDetailMapper; + @Autowired + private InventoryMapper inventoryMapper; + @Autowired + private IItemService iItemService; + @Autowired + private IStockService iStockService; + @Autowired + private IPointService iPointService; + @Autowired + private IInventoryLogService iInventoryLogService; + @Autowired + private BaseCommonService baseCommonService; + + /** + * 获取出库单Map + * + * @param pickIds 出库单id + * @return 出库单Map + */ + private Map queryByPickIdsToMap(List pickIds) { + Map pickMap = new HashMap<>(); + List pickList = pickMapper.selectByIds(pickIds); + for (Pick pick : pickList) { + pickMap.put(pick.getId(), pick); + } + return pickMap; + } + @Override @Transactional(rollbackFor = Exception.class) public void saveMain(Pick pick, List pickDetailList) { pickMapper.insert(pick); - if (pickDetailList != null && !pickDetailList.isEmpty()) { - AtomicInteger lineNoCounter = new AtomicInteger(1); - for (PickDetail entity : pickDetailList) { - if (entity.getLineNo() == null || entity.getLineNo() == 0) { - entity.setLineNo(lineNoCounter.getAndIncrement()); - } - entity.setPickId(pick.getId()); - pickDetailMapper.insert(entity); + + if (pickDetailList == null || pickDetailList.isEmpty()) { + throw new RuntimeException("请新增出库明细"); + } + + // 获取当前出库单下已存在的最大序号 + Integer maxLineNo = pickDetailMapper.queryMaxLineNoByPickId(pick.getId()); + AtomicInteger lineNoCounter = new AtomicInteger((maxLineNo != null) ? maxLineNo + 1 : 1); + + for (PickDetail entity : pickDetailList) { + if (entity.getLineNo() == null || entity.getLineNo() == 0) { + entity.setLineNo(lineNoCounter.getAndIncrement()); } + entity.setPickId(pick.getId()); + pickDetailMapper.insert(entity); } //刷新出库单 refreshPick(pick, pickDetailList); } + /** + * 刷新出库单状态 + * + * @param pick 出库单 + * @param pickDetails 出库明细 + */ public synchronized void refreshPick(Pick pick, List pickDetails) { if (pickDetails == null) { pickDetails = new ArrayList<>(); @@ -103,17 +158,41 @@ public class PickServiceImpl extends ServiceImpl implements IP @Override @Transactional(rollbackFor = Exception.class) public void updateMain(Pick pick, List pickDetailList) { - // 直接更新主表 pickMapper.updateById(pick); - // 更新明细表 - 只更新传入的明细数据 - if (pickDetailList != null && !pickDetailList.isEmpty()) { - for (PickDetail entity : pickDetailList) { - entity.setPickId(pick.getId()); - // 直接更新,而不是删除后重新插入 - if (entity.getId() != null) { - pickDetailMapper.updateById(entity); - } + if (pickDetailList == null || pickDetailList.isEmpty()) { + throw new RuntimeException("请新增出库明细"); + } + + // 删除多余的出库明细 + List exitPickDetailList = pickDetailMapper.selectByMainId(pick.getId()); + if (exitPickDetailList.size() > pickDetailList.size()) { + List pickDetailIds = pickDetailList.stream() + .map(PickDetail::getId) + .toList(); + //两个集合的差集id 需要删除 + List deleteIds = exitPickDetailList.stream() + .filter(e -> e.getId() != null && !pickDetailIds.contains(e.getId())) + .map(PickDetail::getId) + .toList(); + pickDetailMapper.deleteByIds(deleteIds); + //添加删除日志 + baseCommonService.addLog(pick.getNo() + "任务号,删除明细:" + deleteIds, CommonConstant.LOG_TYPE_2, CommonConstant.OPERATE_TYPE_4); + } + + // 获取当前出库单下已存在的最大序号 + Integer maxLineNo = pickDetailMapper.queryMaxLineNoByPickId(pick.getId()); + AtomicInteger lineNoCounter = new AtomicInteger((maxLineNo != null) ? maxLineNo + 1 : 1); + + for (PickDetail entity : pickDetailList) { + if (entity.getLineNo() == null || entity.getLineNo() == 0) { + entity.setLineNo(lineNoCounter.getAndIncrement()); + } + entity.setPickId(pick.getId()); + if (entity.getId() != null) { + pickDetailMapper.updateById(entity); + } else { + pickDetailMapper.insert(entity); } } @@ -150,4 +229,282 @@ public class PickServiceImpl extends ServiceImpl implements IP } } + @Override + @Transactional(rollbackFor = Exception.class) + public List allocatePickDetail(List pickDetails) { + List errorMsgList = new ArrayList<>(); + + //查询出库单 + List pickIds = pickDetails.stream().map(PickDetail::getPickId).distinct().toList(); + Map pickMap = queryByPickIdsToMap(pickIds); + + //获取不为null、空字符的批次号 + List propC1List = pickDetails.stream().map(PickDetail::getPropC1).filter(Objects::nonNull).filter(e -> !e.isEmpty()).distinct().toList(); + + //获取不为null、空字符的外部库存状态 + List propC3List = pickDetails.stream().map(PickDetail::getPropC3).filter(Objects::nonNull).filter(e -> !e.isEmpty()).distinct().toList(); + + + //获取不为null、空字符的仓库代码 + List whCodeList = pickMap.values().stream().map(Pick::getWhCode).filter(Objects::nonNull).filter(e -> !e.isEmpty()).distinct().toList(); + + + //查询需要分配的物料 + List itemIds = pickDetails.stream().map(PickDetail::getItemId).distinct().toList(); + Map itemMap = iItemService.queryByItemIdsToMap(itemIds); + + //根据物料、批次号、外部库存状态、外部仓库代码批量查询库存 + List inventories = inventoryMapper.queryInventoryWithLock(itemIds, propC1List, propC3List, whCodeList); + if (inventories.isEmpty()) { + List itemCodes = itemMap.values().stream().map(Item::getItemCode).toList(); + errorMsgList.add(itemCodes + "物料库存不足"); + return errorMsgList; + } + + //容器 + List stockIds = inventories.stream().map(Inventory::getStockId).distinct().toList(); + Map stockMap = iStockService.queryByStockIdsToMap(stockIds); + + //目标库位 + List pointIds = inventories.stream().map(Inventory::getPointId).distinct().toList(); + Map pointMap = iPointService.queryByPointIdsToMap(pointIds); + + List updateToInventory = new ArrayList<>(); + List updateToPickDetail = new ArrayList<>(); + + for (PickDetail pickDetail : pickDetails) { + Pick pick = pickMap.get(pickDetail.getPickId()); + Item item = itemMap.get(pickDetail.getItemId()); + + //待分配数量 + BigDecimal unAllocatedQty = BigDecimalUtil.subtract(pickDetail.getOrderQty(), pickDetail.getAllocatedQty(), 0); + // 如果已经满足需求则跳过 + if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) { + continue; + } + + for (Inventory inventory : inventories) { + // 筛选符合要求的库存记录 + if (!inventory.getItemId().equals(pickDetail.getItemId())) { + continue; + } + // 匹配批次号(如果有指定) + if (pickDetail.getPropC1() != null && !pickDetail.getPropC1().isEmpty()) { + if (!pickDetail.getPropC1().equals(inventory.getPropC1())) { + continue; + } + } + // 匹配库存状态(如果有指定) + if (pickDetail.getPropC3() != null && !pickDetail.getPropC3().isEmpty()) { + if (!pickDetail.getPropC3().equals(inventory.getPropC3())) { + continue; + } + } + // 匹配仓库 + if (!pick.getWhCode().equals(inventory.getWhCode())) { + continue; + } + //库存可用数量 + BigDecimal availableQty = BigDecimalUtil.subtract(inventory.getQuantity(), inventory.getQueuedQty(), 0); + if (availableQty.compareTo(BigDecimal.ZERO) <= 0) { + continue; + } + + BigDecimal allocateQty = unAllocatedQty.compareTo(availableQty) < 0 ? unAllocatedQty : availableQty; + + // 更新库存占用数量 + inventory.setQueuedQty(BigDecimalUtil.add(inventory.getQueuedQty(), allocateQty, 0)); + inventory.setStatus(InventoryStatusEnum.ALLOCATED.getValue()); + updateToInventory.add(inventory); + + // 更新明细分配数量 + BigDecimal fpQty = BigDecimalUtil.add(pickDetail.getAllocatedQty(), allocateQty, 0); + pickDetail.setAllocatedQty(fpQty); + + // 更新明细状态 + if (fpQty.compareTo(pickDetail.getOrderQty()) >= 0) { + pickDetail.setStatus(PickStatusEnum.ASSIGNED.getValue()); + } else { + pickDetail.setStatus(PickStatusEnum.PARTIAL.getValue()); + } + updateToPickDetail.add(pickDetail); + + //容器 + Stock stock = stockMap.get(inventory.getStockId()); + + //库位 + Point point = pointMap.get(inventory.getPointId()); + + //TODO 后续补充生成Task任务 + //判断是否整托 + String tuo = "拆托"; + if (inventory.getQuantity().compareTo(allocateQty) == 0) { + tuo = "整托"; + } + log.info("生成任务: {} - {} - {}", stock.getStockCode(), point.getPointCode(), tuo); + + unAllocatedQty = BigDecimalUtil.subtract(unAllocatedQty, allocateQty, 0); + + //分配库存日志 + iInventoryLogService.addAllocInventoryLog(inventory, allocateQty, pick.getOrderNo(), pickDetail.getId(), pickDetail.getDescription()); + + // 如果已完全分配,则跳出内层循环 + if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) { + break; + } + } + // 若仍有未分配的数量,说明库存不足 + if (unAllocatedQty.compareTo(BigDecimal.ZERO) > 0) { + errorMsgList.add(item.getItemCode() + "物料库存不足"); + continue; + } + + } + + //批量操作 + if (CollectionUtils.isNotEmpty(updateToInventory)) { + inventoryMapper.updateById(updateToInventory); + } + if (CollectionUtils.isNotEmpty(updateToPickDetail)) { + pickDetailMapper.updateById(updateToPickDetail); + } + + //刷新出库单 + for (Pick pick : pickMap.values()) { + refreshPick(pick, pickDetailMapper.selectByMainId(pick.getId())); + } + return errorMsgList; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public List allocatePickDetail2(List pickDetails) { + // 错误信息去重(LinkedHashSet保证顺序和唯一) + Set errorMsgSet = new LinkedHashSet<>(); + + // -------------------------- 1. 查询关联数据(批量查询,减少DB交互)-------------------------- + //获取出库单 + List pickIds = pickDetails.stream().map(PickDetail::getPickId).distinct().toList(); + Map pickMap = queryByPickIdsToMap(pickIds); + + //获取物料 + List itemIds = pickDetails.stream().map(PickDetail::getItemId).distinct().toList(); + Map itemMap = iItemService.queryByItemIdsToMap(itemIds); + + //筛选查询库存的条件(非空去重)批次、外部库存状态 、外部仓库 + List propC1List = pickDetails.stream().map(PickDetail::getPropC1).filter(StringUtils::hasText).distinct().toList(); + List propC3List = pickDetails.stream().map(PickDetail::getPropC3).filter(StringUtils::hasText).distinct().toList(); + List whCodeList = pickMap.values().stream().map(Pick::getWhCode).filter(StringUtils::hasText).distinct().toList(); + + //查询库存 + List inventories = inventoryMapper.queryInventoryWithLock(itemIds, propC1List, propC3List, whCodeList); + if (CollectionUtils.isEmpty(inventories)) { + String itemCodes = itemMap.values().stream().map(Item::getItemCode).collect(Collectors.joining(",")); + errorMsgSet.add("【" + itemCodes + "】物料库存不足"); + return new ArrayList<>(errorMsgSet); + } + + //获取容器 + List stockIds = inventories.stream().map(Inventory::getStockId).distinct().toList(); + Map stockMap = iStockService.queryByStockIdsToMap(stockIds); + + //获取目标库位 + List pointIds = inventories.stream().map(Inventory::getPointId).distinct().toList(); + Map pointMap = iPointService.queryByPointIdsToMap(pointIds); + + // -------------------------- 2. 库存分组排序(优化匹配效率)-------------------------- + // 按「物料ID+批次+库存状态+仓库」分组,且按创建时间升序(FIFO分配规则) + Map> inventoryGroupMap = inventories.stream() + .sorted(Comparator.comparing(Inventory::getCreateTime)) + .collect(Collectors.groupingBy(inv -> InventoryGroupKey.of( + inv.getItemId(), + inv.getPropC1(), + inv.getPropC3(), + inv.getWhCode() + ))); + + // -------------------------- 3. 创建更新列表--------------------------- + List updateToInventory = new ArrayList<>(); + List updateToPickDetail = new ArrayList<>(); + + // -------------------------- 4. 循环分配库存 ------------------- + for (PickDetail pickDetail : pickDetails) { + //出库单 + Pick pick = pickMap.get(pickDetail.getPickId()); + //物料 + Item item = itemMap.get(pickDetail.getItemId()); + //未分配数量 + BigDecimal unAllocatedQty = BigDecimalUtil.subtract(pickDetail.getOrderQty(), pickDetail.getAllocatedQty(), 0); + if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) { + continue; + } + //匹配目标库存(通过分组key快速定位) + InventoryGroupKey groupKey = InventoryGroupKey.of( + pickDetail.getItemId(), + pickDetail.getPropC1(), + pickDetail.getPropC3(), + pick.getWhCode() + ); + List matchedInventories = inventoryGroupMap.getOrDefault(groupKey, Collections.emptyList()); + if (CollectionUtils.isEmpty(matchedInventories)) { + errorMsgSet.add(String.format("物料【%s】无匹配库存(物料ID:%s,批次:%s,库存状态:%s,仓库:%s)", + item.getItemCode(), item.getId(), pickDetail.getPropC1(), pickDetail.getPropC3(), pick.getWhCode())); + continue; + } + for (Inventory inventory : matchedInventories) { + if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) { + break; + } + // 库存可用数量 + BigDecimal availableQty = BigDecimalUtil.subtract(inventory.getQuantity(), inventory.getQueuedQty(), 0); + if (availableQty.compareTo(BigDecimal.ZERO) <= 0) { + continue; + } + //本次分配数量(取最小值) + BigDecimal allocateQty = unAllocatedQty.min(availableQty); + // 更新库存占用数量 + inventory.setQueuedQty(BigDecimalUtil.add(inventory.getQueuedQty(), allocateQty, 0)); + inventory.setStatus(InventoryStatusEnum.ALLOCATED.getValue()); + updateToInventory.add(inventory); + // 更新明细分配数量和状态 + BigDecimal fpQty = BigDecimalUtil.add(pickDetail.getAllocatedQty(), allocateQty, 0); + pickDetail.setAllocatedQty(fpQty); + Integer status = fpQty.compareTo(pickDetail.getOrderQty()) >= 0 ? PickStatusEnum.ASSIGNED.getValue() : PickStatusEnum.PARTIAL.getValue(); + pickDetail.setStatus(status); + updateToPickDetail.add(pickDetail); + //容器 + Stock stock = stockMap.get(inventory.getStockId()); + //库位 + Point point = pointMap.get(inventory.getPointId()); + String tuoType = inventory.getQuantity().compareTo(allocateQty) == 0 ? "整托" : "拆托"; + log.info("生成拣货任务: 容器:{} - 库位:{} - 类型:{}- 分配数量:{}", stock.getStockCode(), point.getPointCode(), tuoType, allocateQty); + //分配库存日志 + iInventoryLogService.addAllocInventoryLog(inventory, allocateQty, pick.getOrderNo(), pickDetail.getId(), pickDetail.getDescription()); + //更新未分配数量 + unAllocatedQty = BigDecimalUtil.subtract(unAllocatedQty, allocateQty, 0); + } + //分配后仍有缺货,记录错误 + if (unAllocatedQty.compareTo(BigDecimal.ZERO) > 0) { + errorMsgSet.add(String.format("物料【%s】库存不足(需求:%s,已分配:%s,缺货:%s)", + item.getItemCode(), pickDetail.getOrderQty(), + BigDecimalUtil.subtract(pickDetail.getOrderQty(), unAllocatedQty, 0), + unAllocatedQty)); + } + } + + // -------------------------- 5. 批量更新(减少DB交互)-------------------------- + if (CollectionUtils.isNotEmpty(updateToInventory)) { + inventoryMapper.updateById(updateToInventory); + } + if (CollectionUtils.isNotEmpty(updateToPickDetail)) { + pickDetailMapper.updateById(updateToPickDetail); + } + + // -------------------------- 6. 刷新出库单状态 -------------------------- + for (Pick pick : pickMap.values()) { + refreshPick(pick, pickDetailMapper.selectByMainId(pick.getId())); + } + return new ArrayList<>(errorMsgSet); + } + } diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryGroupKey.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryGroupKey.java new file mode 100644 index 0000000..f85fa1f --- /dev/null +++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/shipping/vo/InventoryGroupKey.java @@ -0,0 +1,27 @@ +package org.cpte.modules.shipping.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode +public class InventoryGroupKey { + private Long itemId; + private String propC1; // 批次号 + private String propC3; // 库存状态 + private String whCode; // 仓库代码 + + public static InventoryGroupKey of(Long itemId, String propC1, String propC3, String whCode) { + return new InventoryGroupKey( + itemId, + StringUtils.defaultIfBlank(propC1, ""), // 空值统一为"",避免分组不一致 + StringUtils.defaultIfBlank(propC3, ""), + StringUtils.defaultIfBlank(whCode, "") + ); + } +} diff --git a/cpte-module-system/cpte-system-biz/src/main/java/org/jeecg/modules/system/controller/SysLogController.java b/cpte-module-system/cpte-system-biz/src/main/java/org/jeecg/modules/system/controller/SysLogController.java index 9aaea3e..4794696 100644 --- a/cpte-module-system/cpte-system-biz/src/main/java/org/jeecg/modules/system/controller/SysLogController.java +++ b/cpte-module-system/cpte-system-biz/src/main/java/org/jeecg/modules/system/controller/SysLogController.java @@ -64,7 +64,7 @@ public class SysLogController extends JeecgController { //日志关键词 String keyWord = req.getParameter("keyWord"); if(oConvertUtils.isNotEmpty(keyWord)) { - queryWrapper.like("log_content",keyWord); + queryWrapper.like("log_content",keyWord).or().like("request_param",keyWord); } //TODO 过滤逻辑处理 //TODO begin、end逻辑处理