package com.klp.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.klp.common.exception.ServiceException; import com.klp.common.utils.StringUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; 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; import com.klp.domain.WmsActualWarehouse; import com.klp.mapper.WmsActualWarehouseMapper; import com.klp.service.IWmsActualWarehouseService; import com.klp.domain.WmsMaterialCoil; import com.klp.mapper.WmsMaterialCoilMapper; import java.util.*; import java.util.stream.Collectors; /** * 实际库区/库位自关联Service业务层处理 * * @author klp * @date 2025-11-24 */ @RequiredArgsConstructor @Service public class WmsActualWarehouseServiceImpl implements IWmsActualWarehouseService { private final WmsActualWarehouseMapper baseMapper; private final WmsMaterialCoilMapper wmsMaterialCoilMapper; /** * 查询实际库区/库位自关联 */ @Override public WmsActualWarehouseVo queryById(Long actualWarehouseId){ return baseMapper.selectVoById(actualWarehouseId); } @Override @Transactional(rollbackFor = Exception.class) public int batchGenerateLocations(WmsActualWarehouseBatchGenerateBo bo) { if (bo == null) { throw new ServiceException("参数不能为空"); } Integer rows = bo.getRowCount(); Integer cols = bo.getColumnCount(); Integer layers = bo.getLayerCount(); String prefix = bo.getPrefix(); Long parentId = bo.getParentId(); if (layers == null) { layers = 2; } if (rows == null || cols == null || rows < 1 || cols < 1) { throw new ServiceException("行/列必须为正整数"); } if (layers < 1) { throw new ServiceException("层必须为正整数"); } if (StringUtils.isBlank(prefix)) { throw new ServiceException("前缀不能为空"); } List codes = new ArrayList<>(rows * cols * layers); for (int l = 1; l <= layers; l++) { int rowsForLayer = (l == 2) ? Math.max(rows - 1, 1) : rows; for (int c = 1; c <= cols; c++) { for (int r = 1; r <= rowsForLayer; r++) { String rStr = r < 10 ? ("0" + r) : String.valueOf(r); String code = prefix + c + '-' + rStr + '-' + l; codes.add(code); } } } // 去重:过滤同父级下已存在的编码 List exists = baseMapper.selectList(Wrappers.lambdaQuery() .eq(WmsActualWarehouse::getParentId, parentId) .in(!codes.isEmpty(), WmsActualWarehouse::getActualWarehouseCode, codes)); Set existCodes = exists.stream().map(WmsActualWarehouse::getActualWarehouseCode).collect(Collectors.toSet()); List toInsert = new ArrayList<>(); for (String code : codes) { if (existCodes.contains(code)) { continue; } WmsActualWarehouse e = new WmsActualWarehouse(); e.setParentId(parentId); e.setActualWarehouseType(3L); e.setActualWarehouseCode(code); e.setActualWarehouseName(code); e.setSortNo(0L); e.setIsEnabled(1); toInsert.add(e); } if (toInsert.isEmpty()) { return 0; } boolean ok = baseMapper.insertBatch(toInsert); if (!ok) { throw new ServiceException("批量生成失败"); } return toInsert.size(); } /** * 查询实际库区/库位自关联列表 */ @Override public List queryList(WmsActualWarehouseBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); List list = baseMapper.selectVoList(lqw); if (CollUtil.isEmpty(list)) { return list; } // 如果当前查询的是某二级父节点下的三级库位,需要将被拆分的大库位替换为其两个子库位 if (bo.getParentId() != null) { List splitParentIds = list.stream() .filter(v -> Optional.ofNullable(v.getSplitStatus()).orElse(0) == 1) .map(WmsActualWarehouseVo::getActualWarehouseId) .collect(Collectors.toList()); if (CollUtil.isNotEmpty(splitParentIds)) { List children = baseMapper.selectVoList( Wrappers.lambdaQuery() .in(WmsActualWarehouse::getParentId, splitParentIds) .eq(WmsActualWarehouse::getDelFlag, 0) .orderByAsc(WmsActualWarehouse::getSortNo, WmsActualWarehouse::getActualWarehouseId) ); Map> childrenMap = children.stream() .collect(Collectors.groupingBy(WmsActualWarehouseVo::getParentId, LinkedHashMap::new, Collectors.toList())); List expanded = new ArrayList<>(list.size() + children.size()); for (WmsActualWarehouseVo v : list) { if (Optional.ofNullable(v.getSplitStatus()).orElse(0) == 1) { List ch = childrenMap.get(v.getActualWarehouseId()); if (CollUtil.isNotEmpty(ch)) { expanded.addAll(ch); continue; } } expanded.add(v); } list = expanded; } } // 仅对被占用的位置(isEnabled=0)补充钢卷信息 List occupiedAwIds = list.stream() .filter(it -> it.getIsEnabled() != null && it.getIsEnabled() == 0) .map(WmsActualWarehouseVo::getActualWarehouseId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); if (CollUtil.isEmpty(occupiedAwIds)) { return list; } // 查询当前在库(del_flag=0, status=0)并且占用这些库位的钢卷,仅选择必要列 List coils = wmsMaterialCoilMapper.selectList( Wrappers.lambdaQuery() .select( WmsMaterialCoil::getActualWarehouseId, WmsMaterialCoil::getCoilId, WmsMaterialCoil::getEnterCoilNo, WmsMaterialCoil::getCurrentCoilNo, WmsMaterialCoil::getSupplierCoilNo ) .in(WmsMaterialCoil::getActualWarehouseId, occupiedAwIds) .eq(WmsMaterialCoil::getDelFlag, 0) .eq(WmsMaterialCoil::getStatus, 0) ); if (CollUtil.isEmpty(coils)) { return list; } // 同一库位若有多条记录,选择最新一条(按更新顺序无字段,这里按主键最大) Map coilByAwId = coils.stream() .collect(Collectors.toMap( WmsMaterialCoil::getActualWarehouseId, c -> c, (a, b) -> a.getCoilId() != null && b.getCoilId() != null && a.getCoilId() > b.getCoilId() ? a : b )); list.forEach(it -> { if (it.getIsEnabled() != null && it.getIsEnabled() == 0) { WmsMaterialCoil c = coilByAwId.get(it.getActualWarehouseId()); if (c != null) { it.setEnterCoilNo(c.getEnterCoilNo()); it.setCurrentCoilNo(c.getCurrentCoilNo()); it.setSupplierCoilNo(c.getSupplierCoilNo()); } } }); return list; } @Override public List queryTree(WmsActualWarehouseBo bo) { if (bo == null) { bo = new WmsActualWarehouseBo(); } LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(StringUtils.isNotBlank(bo.getActualWarehouseCode()), WmsActualWarehouse::getActualWarehouseCode, bo.getActualWarehouseCode()); wrapper.like(StringUtils.isNotBlank(bo.getActualWarehouseName()), WmsActualWarehouse::getActualWarehouseName, bo.getActualWarehouseName()); wrapper.eq(bo.getActualWarehouseType() != null, WmsActualWarehouse::getActualWarehouseType, bo.getActualWarehouseType()); wrapper.eq(bo.getIsEnabled() != null, WmsActualWarehouse::getIsEnabled, bo.getIsEnabled()); wrapper.orderByAsc(WmsActualWarehouse::getSortNo, WmsActualWarehouse::getActualWarehouseId); List flatList = baseMapper.selectVoList(wrapper); Map nodeMap = new LinkedHashMap<>(); flatList.forEach(item -> { WmsActualWarehouseTreeVo node = new WmsActualWarehouseTreeVo(); BeanUtil.copyProperties(item, node); nodeMap.put(node.getActualWarehouseId(), node); }); List roots = new ArrayList<>(); nodeMap.values().forEach(node -> { Long parentId = Optional.ofNullable(node.getParentId()).orElse(0L); if (parentId == 0 || !nodeMap.containsKey(parentId)) { roots.add(node); } else { nodeMap.get(parentId).getChildren().add(node); } }); return roots; } @Override public List queryTreeExcludeLevelThree(WmsActualWarehouseBo bo) { if (bo == null) { bo = new WmsActualWarehouseBo(); } // 设置查询条件,排除三级节点(actualWarehouseType=3) LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(StringUtils.isNotBlank(bo.getActualWarehouseCode()), WmsActualWarehouse::getActualWarehouseCode, bo.getActualWarehouseCode()); wrapper.like(StringUtils.isNotBlank(bo.getActualWarehouseName()), WmsActualWarehouse::getActualWarehouseName, bo.getActualWarehouseName()); wrapper.eq(bo.getActualWarehouseType() != null, WmsActualWarehouse::getActualWarehouseType, bo.getActualWarehouseType()); wrapper.eq(bo.getIsEnabled() != null, WmsActualWarehouse::getIsEnabled, bo.getIsEnabled()); // 排除三级节点(类型为3的节点) wrapper.ne(WmsActualWarehouse::getActualWarehouseType, 3); // 四级节点也排除 wrapper.ne(WmsActualWarehouse::getActualWarehouseType, 4); wrapper.orderByAsc(WmsActualWarehouse::getSortNo, WmsActualWarehouse::getActualWarehouseId); List flatList = baseMapper.selectVoList(wrapper); Map nodeMap = new LinkedHashMap<>(); flatList.forEach(item -> { WmsActualWarehouseTreeVo node = new WmsActualWarehouseTreeVo(); BeanUtil.copyProperties(item, node); nodeMap.put(node.getActualWarehouseId(), node); }); List roots = new ArrayList<>(); nodeMap.values().forEach(node -> { Long parentId = Optional.ofNullable(node.getParentId()).orElse(0L); if (parentId == 0 || !nodeMap.containsKey(parentId)) { roots.add(node); } else { nodeMap.get(parentId).getChildren().add(node); } }); return roots; } private LambdaQueryWrapper buildQueryWrapper(WmsActualWarehouseBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getParentId() != null, WmsActualWarehouse::getParentId, bo.getParentId()); lqw.eq(StringUtils.isNotBlank(bo.getActualWarehouseCode()), WmsActualWarehouse::getActualWarehouseCode, bo.getActualWarehouseCode()); lqw.like(StringUtils.isNotBlank(bo.getActualWarehouseName()), WmsActualWarehouse::getActualWarehouseName, bo.getActualWarehouseName()); lqw.eq(bo.getActualWarehouseType() != null, WmsActualWarehouse::getActualWarehouseType, bo.getActualWarehouseType()); lqw.eq(bo.getSortNo() != null, WmsActualWarehouse::getSortNo, bo.getSortNo()); lqw.eq(bo.getIsEnabled() != null, WmsActualWarehouse::getIsEnabled, bo.getIsEnabled()); return lqw; } /** * 新增实际库区/库位自关联 */ @Override public Boolean insertByBo(WmsActualWarehouseBo bo) { WmsActualWarehouse add = BeanUtil.toBean(bo, WmsActualWarehouse.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setActualWarehouseId(add.getActualWarehouseId()); } return flag; } /** * 修改实际库区/库位自关联 */ @Override public Boolean updateByBo(WmsActualWarehouseBo bo) { WmsActualWarehouse update = BeanUtil.toBean(bo, WmsActualWarehouse.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } @Override @Transactional(rollbackFor = Exception.class) public List createHierarchy(WmsActualWarehouseHierarchyBo bo) { if (bo.getLevels() == null || bo.getLevels().isEmpty()) { throw new ServiceException("层级数据不能为空"); } List sortedLevels = bo.getLevels().stream() .sorted(Comparator.comparing(WmsActualWarehouseHierarchyBo.HierarchyLevel::getLevel)) .collect(Collectors.toList()); Long parentId = 0L; List createdIds = new ArrayList<>(); for (WmsActualWarehouseHierarchyBo.HierarchyLevel level : sortedLevels) { Integer levelValue = level.getLevel(); if (levelValue == null || levelValue < 1 || levelValue > 3) { throw new ServiceException("层级必须在1-3之间"); } if (level.getActualWarehouseId() != null) { WmsActualWarehouse existing = baseMapper.selectById(level.getActualWarehouseId()); if (existing == null) { throw new ServiceException("指定的节点不存在:" + level.getActualWarehouseId()); } if (!Objects.equals(Optional.ofNullable(existing.getParentId()).orElse(0L), parentId)) { throw new ServiceException("节点不属于当前父级,无法复用"); } parentId = existing.getActualWarehouseId(); continue; } if (StringUtils.isBlank(level.getActualWarehouseCode()) || StringUtils.isBlank(level.getActualWarehouseName())) { throw new ServiceException("编码与名称不能为空"); } WmsActualWarehouse duplicate = baseMapper.selectOne(Wrappers.lambdaQuery() .eq(WmsActualWarehouse::getParentId, parentId) .and(wrapper -> wrapper.eq(WmsActualWarehouse::getActualWarehouseCode, level.getActualWarehouseCode()) .or() .eq(WmsActualWarehouse::getActualWarehouseName, level.getActualWarehouseName()))); if (duplicate != null) { parentId = duplicate.getActualWarehouseId(); continue; } WmsActualWarehouse entity = new WmsActualWarehouse(); entity.setParentId(parentId); entity.setActualWarehouseType(levelValue.longValue()); entity.setActualWarehouseCode(level.getActualWarehouseCode()); entity.setActualWarehouseName(level.getActualWarehouseName()); entity.setSortNo(Optional.ofNullable(level.getSortNo()).orElse(0L)); entity.setIsEnabled(Optional.ofNullable(level.getIsEnabled()).orElse(1)); entity.setRemark(level.getRemark()); baseMapper.insert(entity); parentId = entity.getActualWarehouseId(); createdIds.add(parentId); } return createdIds; } @Override @Transactional(rollbackFor = Exception.class) public void importHierarchy(List importList) { if (CollUtil.isEmpty(importList)) { throw new ServiceException("导入数据不能为空"); } for (WmsActualWarehouseImportVo row : importList) { List levels = new ArrayList<>(); appendLevel(levels, 1, row.getLevelOneCode(), row.getLevelOneName(), row.getLevelOneSort()); appendLevel(levels, 2, row.getLevelTwoCode(), row.getLevelTwoName(), row.getLevelTwoSort()); appendLevel(levels, 3, row.getLevelThreeCode(), row.getLevelThreeName(), row.getLevelThreeSort()); if (levels.isEmpty()) { continue; } WmsActualWarehouseHierarchyBo bo = new WmsActualWarehouseHierarchyBo(); bo.setLevels(levels); createHierarchy(bo); } } /** * 保存前的数据校验 */ private void validEntityBeforeSave(WmsActualWarehouse entity){ //TODO 做一些数据校验,如唯一约束 } private void appendLevel(List levels, int level, String code, String name, Long sortNo) { if (StringUtils.isBlank(code) || StringUtils.isBlank(name)) { return; } WmsActualWarehouseHierarchyBo.HierarchyLevel item = new WmsActualWarehouseHierarchyBo.HierarchyLevel(); item.setLevel(level); item.setActualWarehouseCode(code.trim()); item.setActualWarehouseName(name.trim()); item.setSortNo(Optional.ofNullable(sortNo).orElse(0L)); item.setIsEnabled(1); levels.add(item); } /** * 批量删除实际库区/库位自关联 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } 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 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); } } 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 parents = baseMapper.selectList(Wrappers.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 children = baseMapper.selectList(Wrappers.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); } } }