feat(wms): 重构库位拆分逻辑并支持列标识拆分
- 修改查询排序字段为实际库位编码 - 新增列标识参数用于指定拆分列 - 支持按列标识批量拆分库位 - 优化拆分逻辑,支持不同拆分类型 - 增加拆分数量验证机制 - 完善异常处理和参数校验 - 调整实体类字段约束和结构
This commit is contained in:
@@ -15,7 +15,6 @@ public class WmsActualWarehouseSplitBo {
|
||||
/**
|
||||
* 需要操作的三级库位ID集合(大库位ID)
|
||||
*/
|
||||
@NotEmpty
|
||||
private List<Long> 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<Long> splitIds;
|
||||
}
|
||||
|
||||
@@ -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.<WmsActualWarehouse>lambdaQuery()
|
||||
.in(WmsActualWarehouse::getParentId, splitParentIds)
|
||||
.eq(WmsActualWarehouse::getDelFlag, 0)
|
||||
.orderByAsc(WmsActualWarehouse::getSortNo, WmsActualWarehouse::getActualWarehouseId)
|
||||
.orderByAsc(WmsActualWarehouse::getActualWarehouseCode, WmsActualWarehouse::getActualWarehouseId)
|
||||
);
|
||||
Map<Long, List<WmsActualWarehouseVo>> 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<WmsActualWarehouseTreeVo> 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<Long> 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<Long> locationIds = bo.getLocationIds();
|
||||
List<WmsActualWarehouse> parents = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
||||
.in(WmsActualWarehouse::getActualWarehouseId, locationIds)
|
||||
.eq(WmsActualWarehouse::getDelFlag, 0));
|
||||
// 校验无效ID并明确提示
|
||||
Set<Long> existIds = parents.stream().map(WmsActualWarehouse::getActualWarehouseId).collect(Collectors.toSet());
|
||||
List<Long> 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<WmsActualWarehouse> toInsert = new ArrayList<>();
|
||||
for (WmsActualWarehouse p : parents) {
|
||||
String base = p.getActualWarehouseCode();
|
||||
// 先查找是否存在历史子库位(逻辑删除状态)
|
||||
List<WmsActualWarehouse> historyChildren = baseMapper.selectList(
|
||||
Wrappers.<WmsActualWarehouse>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<String> 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<Long> Ids = Optional.ofNullable(bo.getSplitIds()).orElse(new ArrayList<>());
|
||||
|
||||
|
||||
// 合并所有待拆分的ID
|
||||
Set<Long> splitIds = new HashSet<>();
|
||||
splitIds.addAll(Ids);
|
||||
|
||||
if (splitIds.isEmpty()) {
|
||||
throw new ServiceException("待拆分库位ID列表不能为空");
|
||||
}
|
||||
|
||||
// 根据列标识查询该列的所有库位(第一层和第二层)
|
||||
List<WmsActualWarehouse> columnLocations = baseMapper.selectList(Wrappers.<WmsActualWarehouse>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<WmsActualWarehouse> splitLocations = new ArrayList<>();
|
||||
List<WmsActualWarehouse> remainingLocations = new ArrayList<>();
|
||||
|
||||
for (WmsActualWarehouse location : columnLocations) {
|
||||
if (splitIds.contains(location.getActualWarehouseId())) {
|
||||
splitLocations.add(location);
|
||||
} else {
|
||||
remainingLocations.add(location);
|
||||
}
|
||||
}
|
||||
|
||||
// 第一步:先创建所有子库位(不设置编码)
|
||||
List<WmsActualWarehouse> 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<WmsActualWarehouse> toUpdate = new ArrayList<>();
|
||||
|
||||
// 1. 先把所有子库位按父库位排序
|
||||
List<WmsActualWarehouse> 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);
|
||||
|
||||
Reference in New Issue
Block a user