From 3ba87bd507538c0548b14f9c42fbd1a42f70a385 Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Sat, 20 Dec 2025 09:55:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(wms):=20=E9=87=8D=E6=9E=84=E5=BA=93?= =?UTF-8?q?=E4=BD=8D=E6=8B=86=E5=88=86=E9=80=BB=E8=BE=91=E5=B9=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=88=97=E6=A0=87=E8=AF=86=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改查询排序字段为实际库位编码 - 新增列标识参数用于指定拆分列 - 支持按列标识批量拆分库位 - 优化拆分逻辑,支持不同拆分类型 - 增加拆分数量验证机制 - 完善异常处理和参数校验 - 调整实体类字段约束和结构 --- .../domain/bo/WmsActualWarehouseSplitBo.java | 14 +- .../impl/WmsActualWarehouseServiceImpl.java | 236 +++++++++++------- 2 files changed, 155 insertions(+), 95 deletions(-) diff --git a/klp-wms/src/main/java/com/klp/domain/bo/WmsActualWarehouseSplitBo.java b/klp-wms/src/main/java/com/klp/domain/bo/WmsActualWarehouseSplitBo.java index ee83ba6d..90048c9e 100644 --- a/klp-wms/src/main/java/com/klp/domain/bo/WmsActualWarehouseSplitBo.java +++ b/klp-wms/src/main/java/com/klp/domain/bo/WmsActualWarehouseSplitBo.java @@ -15,7 +15,6 @@ public class WmsActualWarehouseSplitBo { /** * 需要操作的三级库位ID集合(大库位ID) */ - @NotEmpty private List locationIds; /** @@ -25,7 +24,18 @@ public class WmsActualWarehouseSplitBo { private Integer action; /** - * 拆分类型:目前仅支持 0=列拆分 + * 拆分类型:0=(1列拆2小库位)1=(1列拆3小库位) */ private Integer splitType = 0; + + /** + * 列标识(如F3A1表示第一列,F3A2表示第二列) + */ + @NotEmpty + private String columnFlag; + + /** + * 待拆分ID列表 + */ + private List splitIds; } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsActualWarehouseServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsActualWarehouseServiceImpl.java index 65bb20c2..168994ba 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsActualWarehouseServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsActualWarehouseServiceImpl.java @@ -42,7 +42,7 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService * 查询实际库区/库位自关联 */ @Override - public WmsActualWarehouseVo queryById(Long actualWarehouseId){ + public WmsActualWarehouseVo queryById(Long actualWarehouseId) { return baseMapper.selectVoById(actualWarehouseId); } @@ -136,7 +136,7 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService Wrappers.lambdaQuery() .in(WmsActualWarehouse::getParentId, splitParentIds) .eq(WmsActualWarehouse::getDelFlag, 0) - .orderByAsc(WmsActualWarehouse::getSortNo, WmsActualWarehouse::getActualWarehouseId) + .orderByAsc(WmsActualWarehouse::getActualWarehouseCode, WmsActualWarehouse::getActualWarehouseId) ); Map> childrenMap = children.stream() .collect(Collectors.groupingBy(WmsActualWarehouseVo::getParentId, LinkedHashMap::new, Collectors.toList())); @@ -237,6 +237,7 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService }); return roots; } + @Override public List queryTreeExcludeLevelThree(WmsActualWarehouseBo bo) { if (bo == null) { @@ -387,7 +388,7 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService /** * 保存前的数据校验 */ - private void validEntityBeforeSave(WmsActualWarehouse entity){ + private void validEntityBeforeSave(WmsActualWarehouse entity) { //TODO 做一些数据校验,如唯一约束 } @@ -413,7 +414,7 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - if(isValid){ + if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; @@ -422,106 +423,155 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService @Override @Transactional(rollbackFor = Exception.class) public void splitLocations(WmsActualWarehouseSplitBo bo) { - if (bo == null || CollUtil.isEmpty(bo.getLocationIds())) { + if (bo == null) { throw new ServiceException("参数不能为空"); } - int splitType = Optional.ofNullable(bo.getSplitType()).orElse(0); - // 仅允许列拆分(1拆2)的类型,防止非法拆分类型 - if (splitType != 0) { - throw new ServiceException("暂仅支持列拆分(1拆2)"); - } - // 查询父库位并校验有效性 - List locationIds = bo.getLocationIds(); - List parents = baseMapper.selectList(Wrappers.lambdaQuery() - .in(WmsActualWarehouse::getActualWarehouseId, locationIds) - .eq(WmsActualWarehouse::getDelFlag, 0)); - // 校验无效ID并明确提示 - Set existIds = parents.stream().map(WmsActualWarehouse::getActualWarehouseId).collect(Collectors.toSet()); - List invalidIds = locationIds.stream().filter(id -> !existIds.contains(id)).collect(Collectors.toList()); - if (!invalidIds.isEmpty()) { - throw new ServiceException("存在无效库位ID:" + invalidIds); - } - // 检查占用与状态:仅依据 isEnabled=1 为未占用,可拆分 - for (WmsActualWarehouse p : parents) { - Integer st = Optional.ofNullable(p.getSplitStatus()).orElse(0); - if (st == 1) { - throw new ServiceException("库位已拆分:" + p.getActualWarehouseCode()); - } - if (!Objects.equals(p.getIsEnabled(), 1)) { - throw new ServiceException("库位被占用或禁用,不能拆分:" + p.getActualWarehouseCode()); - } - } - List toInsert = new ArrayList<>(); - for (WmsActualWarehouse p : parents) { - String base = p.getActualWarehouseCode(); - // 先查找是否存在历史子库位(逻辑删除状态) - List historyChildren = baseMapper.selectList( - Wrappers.lambdaQuery() - .eq(WmsActualWarehouse::getParentId, p.getActualWarehouseId()) - .in(WmsActualWarehouse::getActualWarehouseCode, Arrays.asList(base + "-1", base + "-2")) - .eq(WmsActualWarehouse::getDelFlag, 1) - ); - if (CollUtil.isNotEmpty(historyChildren)) { - // 复活历史子库位,避免新建,保持原ID和二维码 - for (WmsActualWarehouse his : historyChildren) { - WmsActualWarehouse revive = new WmsActualWarehouse(); - revive.setActualWarehouseId(his.getActualWarehouseId()); - revive.setDelFlag(0); - revive.setSplitStatus(1); - revive.setSplitType(splitType); - revive.setIsEnabled(1); - baseMapper.updateById(revive); - } - // 如果只找到了一个历史子库位,另一个需要新建 - if (historyChildren.size() < 2) { - Set found = historyChildren.stream().map(WmsActualWarehouse::getActualWarehouseCode).collect(Collectors.toSet()); - String miss = !found.contains(base + "-1") ? base + "-1" : base + "-2"; - WmsActualWarehouse c = new WmsActualWarehouse(); - c.setParentId(p.getActualWarehouseId()); - c.setActualWarehouseType(4L); // 设置为四级节点 - c.setActualWarehouseCode(miss); - c.setActualWarehouseName(miss); - c.setSortNo(Optional.ofNullable(p.getSortNo()).orElse(0L)); - c.setIsEnabled(1); - c.setSplitStatus(1); - c.setSplitType(splitType); - toInsert.add(c); - } - } else { - // 无历史记录,则正常新建两个 - WmsActualWarehouse c1 = new WmsActualWarehouse(); - c1.setParentId(p.getActualWarehouseId()); - c1.setActualWarehouseType(4L); // 设置为四级节点 - c1.setActualWarehouseCode(base + "-1"); - c1.setActualWarehouseName(base + "-1"); - c1.setSortNo(Optional.ofNullable(p.getSortNo()).orElse(0L)); - c1.setIsEnabled(1); - c1.setSplitStatus(1); - c1.setSplitType(splitType); - toInsert.add(c1); - WmsActualWarehouse c2 = new WmsActualWarehouse(); - c2.setParentId(p.getActualWarehouseId()); - c2.setActualWarehouseType(4L); // 设置为四级节点 - c2.setActualWarehouseCode(base + "-2"); - c2.setActualWarehouseName(base + "-2"); - c2.setSortNo(Optional.ofNullable(p.getSortNo()).orElse(0L)); - c2.setIsEnabled(1); - c2.setSplitStatus(1); - c2.setSplitType(splitType); - toInsert.add(c2); + // 参数验证 + String columnFlag = bo.getColumnFlag(); + if (StringUtils.isBlank(columnFlag)) { + throw new ServiceException("列标识不能为空"); + } + + int splitType = Optional.ofNullable(bo.getSplitType()).orElse(0); + List Ids = Optional.ofNullable(bo.getSplitIds()).orElse(new ArrayList<>()); + + + // 合并所有待拆分的ID + Set splitIds = new HashSet<>(); + splitIds.addAll(Ids); + + if (splitIds.isEmpty()) { + throw new ServiceException("待拆分库位ID列表不能为空"); + } + + // 根据列标识查询该列的所有库位(第一层和第二层) + List columnLocations = baseMapper.selectList(Wrappers.lambdaQuery() + .like(WmsActualWarehouse::getActualWarehouseCode, columnFlag) + .eq(WmsActualWarehouse::getDelFlag, 0) + .eq(WmsActualWarehouse::getActualWarehouseType, 3) // 只查询三级库位 + .orderByAsc(WmsActualWarehouse::getActualWarehouseCode)); + + if (CollUtil.isEmpty(columnLocations)) { + throw new ServiceException("未找到列标识为 " + columnFlag + " 的库位"); + } + + // 分离需要拆分的库位和剩余库位 + List splitLocations = new ArrayList<>(); + List remainingLocations = new ArrayList<>(); + + for (WmsActualWarehouse location : columnLocations) { + if (splitIds.contains(location.getActualWarehouseId())) { + splitLocations.add(location); + } else { + remainingLocations.add(location); } } + + // 第一步:先创建所有子库位(不设置编码) + List toInsert = new ArrayList<>(); + + // 1. 处理需要拆分的库位(每个拆分为2个子库位) + for (WmsActualWarehouse location : splitLocations) { + // 生成第一个子库位 + WmsActualWarehouse subLocation1 = new WmsActualWarehouse(); + subLocation1.setParentId(location.getActualWarehouseId()); + subLocation1.setActualWarehouseType(4L); // 四级节点 + subLocation1.setSortNo(Optional.ofNullable(location.getSortNo()).orElse(0L)); + subLocation1.setIsEnabled(1); + subLocation1.setSplitStatus(1); + subLocation1.setSplitType(splitType); + toInsert.add(subLocation1); + + // 生成第二个子库位 + WmsActualWarehouse subLocation2 = new WmsActualWarehouse(); + subLocation2.setParentId(location.getActualWarehouseId()); + subLocation2.setActualWarehouseType(4L); // 四级节点 + subLocation2.setSortNo(Optional.ofNullable(location.getSortNo()).orElse(0L)); + subLocation2.setIsEnabled(1); + subLocation2.setSplitStatus(1); + subLocation2.setSplitType(splitType); + toInsert.add(subLocation2); + } + + // 2. 处理剩余库位(每个拆分为1个子库位,保持原层级) + for (WmsActualWarehouse remainingLocation : remainingLocations) { + String baseCode = remainingLocation.getActualWarehouseCode(); + + // 生成剩余库位的子库位 + WmsActualWarehouse remainingSubLocation = new WmsActualWarehouse(); + remainingSubLocation.setParentId(remainingLocation.getActualWarehouseId()); + remainingSubLocation.setActualWarehouseType(4L); // 四级节点 + remainingSubLocation.setSortNo(Optional.ofNullable(remainingLocation.getSortNo()).orElse(0L)); + remainingSubLocation.setIsEnabled(1); + remainingSubLocation.setSplitStatus(1); + remainingSubLocation.setSplitType(splitType); + toInsert.add(remainingSubLocation); + } + + // 3. 验证总数 + int expectedSplitSubLocations = splitLocations.size() * 2; + int expectedRemainingSubLocations = remainingLocations.size() * 1; + int expectedTotalSubLocations = expectedSplitSubLocations + expectedRemainingSubLocations; + int actualTotalSubLocations = toInsert.size(); + + if (actualTotalSubLocations != expectedTotalSubLocations) { + throw new ServiceException(String.format( + "子库位生成数量不匹配,期望%d个(拆分库位:%d个→%d个,剩余库位:%d个→%d个),实际生成%d个", + expectedTotalSubLocations, + splitLocations.size(), expectedSplitSubLocations, + remainingLocations.size(), expectedRemainingSubLocations, + actualTotalSubLocations + )); + } + + // 4. 批量插入子库位(先插入获取ID) if (!toInsert.isEmpty()) { boolean ok = baseMapper.insertBatch(toInsert); if (!ok) { - throw new ServiceException("新增小库位失败"); + throw new ServiceException("新增子库位失败"); } } - // 更新父节点拆分状态 - for (WmsActualWarehouse p : parents) { + + // 第二步:重新生成编码 + String prefix = columnFlag; + List toUpdate = new ArrayList<>(); + + // 1. 先把所有子库位按父库位排序 + List sortedSubLocations = new ArrayList<>(toInsert); + sortedSubLocations.sort((a, b) -> { + // 按父库位在原列中的顺序排序 + Long parentA = a.getParentId(); + Long parentB = b.getParentId(); + int indexA = columnLocations.stream().map(WmsActualWarehouse::getActualWarehouseId).collect(Collectors.toList()).indexOf(parentA); + int indexB = columnLocations.stream().map(WmsActualWarehouse::getActualWarehouseId).collect(Collectors.toList()).indexOf(parentB); + return Integer.compare(indexA, indexB); + }); + + // 2. 遍历排序后的子库位,每2个为一组生成编码(序号递增,后缀固定1、2) + for (int i = 0; i < sortedSubLocations.size(); i++) { + WmsActualWarehouse sub = sortedSubLocations.get(i); + int sequenceNum = (i / 2) + 1; // 每2个子库位对应一个序号(0→1,1→1,2→2,3→2...) + int layer = (i % 2) + 1; // 每组内固定1、2后缀(0→1,1→2,2→1,3→2...) + + // 生成编码:前缀-两位序号-层级(如F2B1-X01-1、F2B1-X01-2、F2B1-X02-1...) + String newCode = String.format("%s-X%02d-%d", prefix, sequenceNum, layer); + sub.setActualWarehouseCode(newCode); + sub.setActualWarehouseName(newCode); + toUpdate.add(sub); + } + + // 3. 批量更新编码 + if (!toUpdate.isEmpty()) { + for (WmsActualWarehouse item : toUpdate) { + baseMapper.updateById(item); + } + } + + // 6. 更新该列所有被拆分库位的状态 + for (WmsActualWarehouse location : columnLocations) { WmsActualWarehouse upd = new WmsActualWarehouse(); - upd.setActualWarehouseId(p.getActualWarehouseId()); + upd.setActualWarehouseId(location.getActualWarehouseId()); upd.setSplitStatus(1); upd.setSplitType(splitType); baseMapper.updateById(upd);