feat(wms): 优化库位拆分与合并逻辑
- 支持复用已删除的子库位,避免重复创建 - 统一处理新增与复用子库位的编码生成 - 完善合并时对子库位占用状态的判断逻辑 - 提取公共方法用于创建子库位和重置父库位状态 - 优化异常提示信息,提高可读性 - 调整事务控制范围,确保操作原子性
This commit is contained in:
@@ -434,22 +434,17 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService
|
|||||||
}
|
}
|
||||||
|
|
||||||
int splitType = Optional.ofNullable(bo.getSplitType()).orElse(0);
|
int splitType = Optional.ofNullable(bo.getSplitType()).orElse(0);
|
||||||
List<Long> Ids = Optional.ofNullable(bo.getSplitIds()).orElse(new ArrayList<>());
|
List<Long> splitIds = Optional.ofNullable(bo.getSplitIds()).orElse(new ArrayList<>());
|
||||||
|
|
||||||
|
|
||||||
// 合并所有待拆分的ID
|
|
||||||
Set<Long> splitIds = new HashSet<>();
|
|
||||||
splitIds.addAll(Ids);
|
|
||||||
|
|
||||||
if (splitIds.isEmpty()) {
|
if (splitIds.isEmpty()) {
|
||||||
throw new ServiceException("待拆分库位ID列表不能为空");
|
throw new ServiceException("待拆分库位ID列表不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据列标识查询该列的所有库位(第一层和第二层)
|
// 根据列标识查询该列的所有三级库位
|
||||||
List<WmsActualWarehouse> columnLocations = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
List<WmsActualWarehouse> columnLocations = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
||||||
.like(WmsActualWarehouse::getActualWarehouseCode, columnFlag)
|
.like(WmsActualWarehouse::getActualWarehouseCode, columnFlag)
|
||||||
.eq(WmsActualWarehouse::getDelFlag, 0)
|
.eq(WmsActualWarehouse::getDelFlag, 0)
|
||||||
.eq(WmsActualWarehouse::getActualWarehouseType, 3) // 只查询三级库位
|
.eq(WmsActualWarehouse::getActualWarehouseType, 3)
|
||||||
.orderByAsc(WmsActualWarehouse::getActualWarehouseCode));
|
.orderByAsc(WmsActualWarehouse::getActualWarehouseCode));
|
||||||
|
|
||||||
if (CollUtil.isEmpty(columnLocations)) {
|
if (CollUtil.isEmpty(columnLocations)) {
|
||||||
@@ -468,107 +463,121 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第一步:先创建所有子库位(不设置编码)
|
// 存储需要插入/更新的子库位
|
||||||
List<WmsActualWarehouse> toInsert = new ArrayList<>();
|
List<WmsActualWarehouse> toInsert = new ArrayList<>();
|
||||||
|
List<WmsActualWarehouse> toUpdate = new ArrayList<>();
|
||||||
|
|
||||||
// 1. 处理需要拆分的库位(每个拆分为2个子库位)
|
// 1. 处理需要拆分的库位(每个拆分为2个子库位)
|
||||||
for (WmsActualWarehouse location : splitLocations) {
|
for (WmsActualWarehouse location : splitLocations) {
|
||||||
// 生成第一个子库位
|
// 先查询该父库位下是否存在已逻辑删除的子库位(合并后保留的)
|
||||||
WmsActualWarehouse subLocation1 = new WmsActualWarehouse();
|
List<WmsActualWarehouse> existingChildren = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
||||||
subLocation1.setParentId(location.getActualWarehouseId());
|
.eq(WmsActualWarehouse::getParentId, location.getActualWarehouseId())
|
||||||
subLocation1.setActualWarehouseType(4L); // 四级节点
|
.eq(WmsActualWarehouse::getActualWarehouseType, 4)
|
||||||
subLocation1.setSortNo(Optional.ofNullable(location.getSortNo()).orElse(0L));
|
.eq(WmsActualWarehouse::getDelFlag, 1)); // 查询已逻辑删除的子库位
|
||||||
subLocation1.setIsEnabled(1);
|
|
||||||
subLocation1.setSplitStatus(1);
|
|
||||||
subLocation1.setSplitType(splitType);
|
|
||||||
toInsert.add(subLocation1);
|
|
||||||
|
|
||||||
// 生成第二个子库位
|
if (CollUtil.isNotEmpty(existingChildren) && existingChildren.size() == 2) {
|
||||||
WmsActualWarehouse subLocation2 = new WmsActualWarehouse();
|
// 复用原有子库位:恢复启用状态,更新拆分类型
|
||||||
subLocation2.setParentId(location.getActualWarehouseId());
|
for (WmsActualWarehouse child : existingChildren) {
|
||||||
subLocation2.setActualWarehouseType(4L); // 四级节点
|
child.setDelFlag(0); // 恢复未删除
|
||||||
subLocation2.setSortNo(Optional.ofNullable(location.getSortNo()).orElse(0L));
|
child.setIsEnabled(1);
|
||||||
subLocation2.setIsEnabled(1);
|
child.setSplitStatus(1);
|
||||||
subLocation2.setSplitStatus(1);
|
child.setSplitType(splitType);
|
||||||
subLocation2.setSplitType(splitType);
|
toUpdate.add(child);
|
||||||
toInsert.add(subLocation2);
|
}
|
||||||
|
} else {
|
||||||
|
// 无原有子库位,新建2个子库位(暂不设置编码)
|
||||||
|
WmsActualWarehouse sub1 = createSubLocation(location, splitType);
|
||||||
|
WmsActualWarehouse sub2 = createSubLocation(location, splitType);
|
||||||
|
toInsert.add(sub1);
|
||||||
|
toInsert.add(sub2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 处理剩余库位(每个拆分为1个子库位,保持原层级)
|
// 2. 处理剩余库位(每个拆分为1个子库位)
|
||||||
for (WmsActualWarehouse remainingLocation : remainingLocations) {
|
for (WmsActualWarehouse remainingLocation : remainingLocations) {
|
||||||
String baseCode = remainingLocation.getActualWarehouseCode();
|
// 先查询是否存在已逻辑删除的子库位
|
||||||
|
List<WmsActualWarehouse> existingChildren = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
||||||
|
.eq(WmsActualWarehouse::getParentId, remainingLocation.getActualWarehouseId())
|
||||||
|
.eq(WmsActualWarehouse::getActualWarehouseType, 4)
|
||||||
|
.eq(WmsActualWarehouse::getDelFlag, 1));
|
||||||
|
|
||||||
// 生成剩余库位的子库位
|
if (CollUtil.isNotEmpty(existingChildren)) {
|
||||||
WmsActualWarehouse remainingSubLocation = new WmsActualWarehouse();
|
// 复用原有子库位
|
||||||
remainingSubLocation.setParentId(remainingLocation.getActualWarehouseId());
|
WmsActualWarehouse child = existingChildren.get(0);
|
||||||
remainingSubLocation.setActualWarehouseType(4L); // 四级节点
|
child.setDelFlag(0);
|
||||||
remainingSubLocation.setSortNo(Optional.ofNullable(remainingLocation.getSortNo()).orElse(0L));
|
child.setIsEnabled(1);
|
||||||
remainingSubLocation.setIsEnabled(1);
|
child.setSplitStatus(1);
|
||||||
remainingSubLocation.setSplitStatus(1);
|
child.setSplitType(splitType);
|
||||||
remainingSubLocation.setSplitType(splitType);
|
toUpdate.add(child);
|
||||||
toInsert.add(remainingSubLocation);
|
} else {
|
||||||
|
// 新建子库位
|
||||||
|
WmsActualWarehouse sub = createSubLocation(remainingLocation, splitType);
|
||||||
|
toInsert.add(sub);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 验证总数
|
// 3. 验证总数
|
||||||
int expectedSplitSubLocations = splitLocations.size() * 2;
|
int expectedSplitSub = splitLocations.size() * 2;
|
||||||
int expectedRemainingSubLocations = remainingLocations.size() * 1;
|
int expectedRemainingSub = remainingLocations.size() * 1;
|
||||||
int expectedTotalSubLocations = expectedSplitSubLocations + expectedRemainingSubLocations;
|
int expectedTotal = expectedSplitSub + expectedRemainingSub;
|
||||||
int actualTotalSubLocations = toInsert.size();
|
// 实际总数 = 新增数 + 复用数
|
||||||
|
int actualTotal = toInsert.size() + toUpdate.size();
|
||||||
|
|
||||||
if (actualTotalSubLocations != expectedTotalSubLocations) {
|
if (actualTotal != expectedTotal) {
|
||||||
throw new ServiceException(String.format(
|
throw new ServiceException(String.format(
|
||||||
"子库位生成数量不匹配,期望%d个(拆分库位:%d个→%d个,剩余库位:%d个→%d个),实际生成%d个",
|
"子库位数量不匹配,期望%d个,实际新增%d个+复用%d个=%d个",
|
||||||
expectedTotalSubLocations,
|
expectedTotal, toInsert.size(), toUpdate.size(), actualTotal));
|
||||||
splitLocations.size(), expectedSplitSubLocations,
|
|
||||||
remainingLocations.size(), expectedRemainingSubLocations,
|
|
||||||
actualTotalSubLocations
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 批量插入子库位(先插入获取ID)
|
// 4. 批量插入新子库位
|
||||||
if (!toInsert.isEmpty()) {
|
if (!toInsert.isEmpty()) {
|
||||||
boolean ok = baseMapper.insertBatch(toInsert);
|
boolean insertOk = baseMapper.insertBatch(toInsert);
|
||||||
if (!ok) {
|
if (!insertOk) {
|
||||||
throw new ServiceException("新增子库位失败");
|
throw new ServiceException("新增子库位失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第二步:重新生成编码
|
// 5. 批量更新复用的子库位(恢复状态)
|
||||||
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()) {
|
if (!toUpdate.isEmpty()) {
|
||||||
for (WmsActualWarehouse item : toUpdate) {
|
for (WmsActualWarehouse child : toUpdate) {
|
||||||
baseMapper.updateById(item);
|
baseMapper.updateById(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 更新该列所有被拆分库位的状态
|
// 6. 统一生成/更新子库位编码(包含新增和复用的)
|
||||||
|
List<WmsActualWarehouse> allSubLocations = new ArrayList<>();
|
||||||
|
allSubLocations.addAll(toInsert);
|
||||||
|
allSubLocations.addAll(toUpdate);
|
||||||
|
|
||||||
|
// 按父库位排序(保持物理顺序)
|
||||||
|
allSubLocations.sort((a, b) -> {
|
||||||
|
Long parentA = a.getParentId();
|
||||||
|
Long parentB = b.getParentId();
|
||||||
|
int idxA = columnLocations.stream().map(WmsActualWarehouse::getActualWarehouseId).collect(Collectors.toList()).indexOf(parentA);
|
||||||
|
int idxB = columnLocations.stream().map(WmsActualWarehouse::getActualWarehouseId).collect(Collectors.toList()).indexOf(parentB);
|
||||||
|
return Integer.compare(idxA, idxB);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 生成规范编码
|
||||||
|
String prefix = columnFlag;
|
||||||
|
List<WmsActualWarehouse> codeToUpdate = new ArrayList<>();
|
||||||
|
for (int i = 0; i < allSubLocations.size(); i++) {
|
||||||
|
WmsActualWarehouse sub = allSubLocations.get(i);
|
||||||
|
int sequenceNum = (i / 2) + 1;
|
||||||
|
int layer = (i % 2) + 1;
|
||||||
|
String newCode = String.format("%s-X%02d-%d", prefix, sequenceNum, layer);
|
||||||
|
|
||||||
|
sub.setActualWarehouseCode(newCode);
|
||||||
|
sub.setActualWarehouseName(newCode);
|
||||||
|
codeToUpdate.add(sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量更新编码
|
||||||
|
for (WmsActualWarehouse sub : codeToUpdate) {
|
||||||
|
baseMapper.updateById(sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 更新父库位拆分状态
|
||||||
for (WmsActualWarehouse location : columnLocations) {
|
for (WmsActualWarehouse location : columnLocations) {
|
||||||
WmsActualWarehouse upd = new WmsActualWarehouse();
|
WmsActualWarehouse upd = new WmsActualWarehouse();
|
||||||
upd.setActualWarehouseId(location.getActualWarehouseId());
|
upd.setActualWarehouseId(location.getActualWarehouseId());
|
||||||
@@ -578,53 +587,83 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建子库位基础对象(通用方法)
|
||||||
|
*/
|
||||||
|
private WmsActualWarehouse createSubLocation(WmsActualWarehouse parent, int splitType) {
|
||||||
|
WmsActualWarehouse sub = new WmsActualWarehouse();
|
||||||
|
sub.setParentId(parent.getActualWarehouseId());
|
||||||
|
sub.setActualWarehouseType(4L);
|
||||||
|
sub.setSortNo(Optional.ofNullable(parent.getSortNo()).orElse(0L));
|
||||||
|
sub.setIsEnabled(1);
|
||||||
|
sub.setSplitStatus(1);
|
||||||
|
sub.setSplitType(splitType);
|
||||||
|
sub.setDelFlag(0);
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void mergeLocations(WmsActualWarehouseSplitBo bo) {
|
public void mergeLocations(WmsActualWarehouseSplitBo bo) {
|
||||||
if (bo == null || CollUtil.isEmpty(bo.getLocationIds())) {
|
if (bo == null || CollUtil.isEmpty(bo.getLocationIds())) {
|
||||||
throw new ServiceException("参数不能为空");
|
throw new ServiceException("参数不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WmsActualWarehouse> parents = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
List<WmsActualWarehouse> parents = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
||||||
.in(WmsActualWarehouse::getActualWarehouseId, bo.getLocationIds())
|
.in(WmsActualWarehouse::getActualWarehouseId, bo.getLocationIds())
|
||||||
.eq(WmsActualWarehouse::getDelFlag, 0));
|
.eq(WmsActualWarehouse::getDelFlag, 0));
|
||||||
for (WmsActualWarehouse p : parents) {
|
|
||||||
Integer st = Optional.ofNullable(p.getSplitStatus()).orElse(0);
|
for (WmsActualWarehouse parent : parents) {
|
||||||
if (st != 1) {
|
Integer splitStatus = Optional.ofNullable(parent.getSplitStatus()).orElse(0);
|
||||||
// 未拆分无需合并
|
if (splitStatus != 1) {
|
||||||
|
// 未拆分状态,无需处理
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询父库位下的所有四级子库位(包含已逻辑删除的)
|
||||||
List<WmsActualWarehouse> children = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
List<WmsActualWarehouse> children = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
|
||||||
.eq(WmsActualWarehouse::getParentId, p.getActualWarehouseId())
|
.eq(WmsActualWarehouse::getParentId, parent.getActualWarehouseId())
|
||||||
|
.eq(WmsActualWarehouse::getActualWarehouseType, 4)
|
||||||
.eq(WmsActualWarehouse::getDelFlag, 0));
|
.eq(WmsActualWarehouse::getDelFlag, 0));
|
||||||
|
|
||||||
if (CollUtil.isEmpty(children)) {
|
if (CollUtil.isEmpty(children)) {
|
||||||
// 无子节点,直接还原状态
|
// 无子库位,直接还原父库位状态
|
||||||
WmsActualWarehouse upd = new WmsActualWarehouse();
|
resetParentLocationStatus(parent);
|
||||||
upd.setActualWarehouseId(p.getActualWarehouseId());
|
|
||||||
upd.setSplitStatus(0);
|
|
||||||
upd.setSplitType(0);
|
|
||||||
baseMapper.updateById(upd);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 检查子库位 isEnabled 必须为1(未占用)
|
|
||||||
boolean anyOccupied = children.stream().anyMatch(c -> !Objects.equals(c.getIsEnabled(), 1));
|
// 检查子库位是否被占用(仅检查未删除的子库位)
|
||||||
|
boolean anyOccupied = children.stream()
|
||||||
|
.filter(c -> c.getDelFlag() == 0) // 只检查启用中的子库位
|
||||||
|
.anyMatch(c -> !Objects.equals(c.getIsEnabled(), 1));
|
||||||
|
|
||||||
if (anyOccupied) {
|
if (anyOccupied) {
|
||||||
throw new ServiceException("小库位被占用,不能合并:" + p.getActualWarehouseCode());
|
throw new ServiceException("小库位被占用,不能合并:" + parent.getActualWarehouseCode());
|
||||||
}
|
}
|
||||||
// 逻辑删除小库位
|
|
||||||
for (WmsActualWarehouse c : children) {
|
// 逻辑删除子库位(保留数据,用于后续拆分复用)
|
||||||
WmsActualWarehouse updC = new WmsActualWarehouse();
|
for (WmsActualWarehouse child : children) {
|
||||||
updC.setActualWarehouseId(c.getActualWarehouseId());
|
WmsActualWarehouse updChild = new WmsActualWarehouse();
|
||||||
updC.setDelFlag(1);
|
updChild.setActualWarehouseId(child.getActualWarehouseId());
|
||||||
baseMapper.updateById(updC);
|
updChild.setDelFlag(1); // 逻辑删除
|
||||||
|
updChild.setIsEnabled(0); // 禁用
|
||||||
|
baseMapper.updateById(updChild);
|
||||||
}
|
}
|
||||||
// 更新父状态
|
|
||||||
WmsActualWarehouse upd = new WmsActualWarehouse();
|
// 还原父库位状态
|
||||||
upd.setActualWarehouseId(p.getActualWarehouseId());
|
resetParentLocationStatus(parent);
|
||||||
upd.setSplitStatus(0);
|
|
||||||
upd.setSplitType(0);
|
|
||||||
baseMapper.updateById(upd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置父库位拆分状态(通用方法)
|
||||||
|
*/
|
||||||
|
private void resetParentLocationStatus(WmsActualWarehouse parent) {
|
||||||
|
WmsActualWarehouse upd = new WmsActualWarehouse();
|
||||||
|
upd.setActualWarehouseId(parent.getActualWarehouseId());
|
||||||
|
upd.setSplitStatus(0); // 还原为未拆分
|
||||||
|
upd.setSplitType(0); // 清空拆分类型
|
||||||
|
baseMapper.updateById(upd);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user