feat(wms): 实现库位拆分与合并功能

- 新增库位拆分状态和类型字段,支持大库位拆分为两个小库位
- 新增拆分与合并接口,支持将三级库位进行拆分或合并操作
- 在查询时自动展开已拆分库位的子节点,显示其两个小库位
- 实现拆分时复用历史子库位逻辑,避免重复创建相同编码库位
- 添加拆分与合并时的业务校验,确保库位状态合法才可操作
- 提供 WmsActualWarehouseSplitBo 用于接收拆分/合并请求参数
- 完善实体类和 VO 类对拆分状态及类型的映射支持
- 更新 Mapper XML 配置以支持新增的拆分相关数据库字段
This commit is contained in:
2025-12-19 13:53:36 +08:00
parent c5f9885a99
commit 7e415eb56e
8 changed files with 286 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional;
import com.klp.domain.bo.WmsActualWarehouseBo;
import com.klp.domain.bo.WmsActualWarehouseHierarchyBo;
import com.klp.domain.bo.WmsActualWarehouseBatchGenerateBo;
import com.klp.domain.bo.WmsActualWarehouseSplitBo;
import com.klp.domain.vo.WmsActualWarehouseTreeVo;
import com.klp.domain.vo.WmsActualWarehouseImportVo;
import com.klp.domain.vo.WmsActualWarehouseVo;
@@ -124,6 +125,36 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService
return list;
}
// 如果当前查询的是某二级父节点下的三级库位,需要将被拆分的大库位替换为其两个子库位
if (bo.getParentId() != null) {
List<Long> splitParentIds = list.stream()
.filter(v -> Optional.ofNullable(v.getSplitStatus()).orElse(0) == 1)
.map(WmsActualWarehouseVo::getActualWarehouseId)
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(splitParentIds)) {
List<WmsActualWarehouseVo> children = baseMapper.selectVoList(
Wrappers.<WmsActualWarehouse>lambdaQuery()
.in(WmsActualWarehouse::getParentId, splitParentIds)
.eq(WmsActualWarehouse::getDelFlag, 0)
.orderByAsc(WmsActualWarehouse::getSortNo, WmsActualWarehouse::getActualWarehouseId)
);
Map<Long, List<WmsActualWarehouseVo>> childrenMap = children.stream()
.collect(Collectors.groupingBy(WmsActualWarehouseVo::getParentId, LinkedHashMap::new, Collectors.toList()));
List<WmsActualWarehouseVo> expanded = new ArrayList<>(list.size() + children.size());
for (WmsActualWarehouseVo v : list) {
if (Optional.ofNullable(v.getSplitStatus()).orElse(0) == 1) {
List<WmsActualWarehouseVo> ch = childrenMap.get(v.getActualWarehouseId());
if (CollUtil.isNotEmpty(ch)) {
expanded.addAll(ch);
continue;
}
}
expanded.add(v);
}
list = expanded;
}
}
// 仅对被占用的位置isEnabled=0补充钢卷信息
List<Long> occupiedAwIds = list.stream()
.filter(it -> it.getIsEnabled() != null && it.getIsEnabled() == 0)
@@ -386,5 +417,162 @@ public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService
return baseMapper.deleteBatchIds(ids) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void splitLocations(WmsActualWarehouseSplitBo bo) {
if (bo == null || CollUtil.isEmpty(bo.getLocationIds())) {
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(p.getActualWarehouseType());
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(p.getActualWarehouseType());
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(p.getActualWarehouseType());
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);
}
}
if (!toInsert.isEmpty()) {
boolean ok = baseMapper.insertBatch(toInsert);
if (!ok) {
throw new ServiceException("新增小库位失败");
}
}
// 更新父节点拆分状态
for (WmsActualWarehouse p : parents) {
WmsActualWarehouse upd = new WmsActualWarehouse();
upd.setActualWarehouseId(p.getActualWarehouseId());
upd.setSplitStatus(1);
upd.setSplitType(splitType);
baseMapper.updateById(upd);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void mergeLocations(WmsActualWarehouseSplitBo bo) {
if (bo == null || CollUtil.isEmpty(bo.getLocationIds())) {
throw new ServiceException("参数不能为空");
}
List<WmsActualWarehouse> parents = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
.in(WmsActualWarehouse::getActualWarehouseId, bo.getLocationIds())
.eq(WmsActualWarehouse::getDelFlag, 0));
for (WmsActualWarehouse p : parents) {
Integer st = Optional.ofNullable(p.getSplitStatus()).orElse(0);
if (st != 1) {
// 未拆分无需合并
continue;
}
List<WmsActualWarehouse> children = baseMapper.selectList(Wrappers.<WmsActualWarehouse>lambdaQuery()
.eq(WmsActualWarehouse::getParentId, p.getActualWarehouseId())
.eq(WmsActualWarehouse::getDelFlag, 0));
if (CollUtil.isEmpty(children)) {
// 无子节点,直接还原状态
WmsActualWarehouse upd = new WmsActualWarehouse();
upd.setActualWarehouseId(p.getActualWarehouseId());
upd.setSplitStatus(0);
upd.setSplitType(0);
baseMapper.updateById(upd);
continue;
}
// 检查子库位 isEnabled 必须为1未占用
boolean anyOccupied = children.stream().anyMatch(c -> !Objects.equals(c.getIsEnabled(), 1));
if (anyOccupied) {
throw new ServiceException("小库位被占用,不能合并:" + p.getActualWarehouseCode());
}
// 逻辑删除小库位
for (WmsActualWarehouse c : children) {
WmsActualWarehouse updC = new WmsActualWarehouse();
updC.setActualWarehouseId(c.getActualWarehouseId());
updC.setDelFlag(1);
baseMapper.updateById(updC);
}
// 更新父状态
WmsActualWarehouse upd = new WmsActualWarehouse();
upd.setActualWarehouseId(p.getActualWarehouseId());
upd.setSplitStatus(0);
upd.setSplitType(0);
baseMapper.updateById(upd);
}
}
}