| | |
| | | package com.ruoyi.technology.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.technology.mapper.TechnologyRoutingMapper; |
| | | import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper; |
| | | import com.ruoyi.technology.pojo.TechnologyRouting; |
| | | import com.ruoyi.technology.pojo.TechnologyRoutingOperation; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.technology.bean.dto.TechnologyRoutingDto; |
| | | import com.ruoyi.technology.bean.vo.TechnologyRoutingVo; |
| | | import com.ruoyi.production.mapper.ProductionOrderRoutingMapper; |
| | | import com.ruoyi.production.pojo.ProductionOrderRouting; |
| | | import com.ruoyi.technology.mapper.*; |
| | | import com.ruoyi.technology.pojo.*; |
| | | import com.ruoyi.technology.service.TechnologyRoutingService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Comparator; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @RequiredArgsConstructor |
| | | public class TechnologyRoutingServiceImpl extends ServiceImpl<TechnologyRoutingMapper, TechnologyRouting> implements TechnologyRoutingService { |
| | | |
| | | @Autowired |
| | | private TechnologyRoutingMapper technologyRoutingMapper; |
| | | private final TechnologyRoutingMapper technologyRoutingMapper; |
| | | |
| | | @Autowired |
| | | private TechnologyRoutingOperationMapper technologyRoutingOperationMapper; |
| | | private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; |
| | | |
| | | private final TechnologyBomStructureMapper technologyBomStructureMapper; |
| | | |
| | | private final TechnologyOperationMapper technologyOperationMapper; |
| | | private final TechnologyOperationParamMapper technologyOperationParamMapper; |
| | | private final TechnologyParamMapper technologyParamMapper; |
| | | private final TechnologyRoutingOperationParamMapper technologyRoutingOperationParamMapper; |
| | | |
| | | private final ProductionOrderRoutingMapper productionOrderRoutingMapper; |
| | | |
| | | @Override |
| | | public IPage<TechnologyRouting> pageTechnologyRouting(Page<TechnologyRouting> page, TechnologyRouting technologyRouting) { |
| | | LambdaQueryWrapper<TechnologyRouting> queryWrapper = Wrappers.lambdaQuery(); |
| | | queryWrapper.eq(technologyRouting.getId() != null, TechnologyRouting::getId, technologyRouting.getId()) |
| | | .eq(technologyRouting.getProductModelId() != null, TechnologyRouting::getProductModelId, technologyRouting.getProductModelId()) |
| | | .eq(technologyRouting.getBomId() != null, TechnologyRouting::getBomId, technologyRouting.getBomId()) |
| | | .like(isNotBlank(technologyRouting.getProcessRouteCode()), TechnologyRouting::getProcessRouteCode, technologyRouting.getProcessRouteCode()) |
| | | .like(isNotBlank(technologyRouting.getDescription()), TechnologyRouting::getDescription, technologyRouting.getDescription()) |
| | | .orderByDesc(TechnologyRouting::getId); |
| | | return this.page(page, queryWrapper); |
| | | public IPage<TechnologyRoutingVo> pageTechnologyRouting(Page<TechnologyRoutingDto> page, TechnologyRoutingDto technologyRoutingDto) { |
| | | return technologyRoutingMapper.pageTechnologyRouting(page, technologyRoutingDto); |
| | | } |
| | | |
| | | @Override |
| | | public TechnologyRoutingVo getTechnologyRoutingInfo(Long id) { |
| | | TechnologyRouting technologyRouting = this.getById(id); |
| | | if (technologyRouting == null) { |
| | | return null; |
| | | } |
| | | return BeanUtil.copyProperties(technologyRouting, TechnologyRoutingVo.class); |
| | | } |
| | | |
| | | @Override |
| | | public Long saveTechnologyRouting(TechnologyRouting technologyRouting) { |
| | | this.save(technologyRouting); |
| | | if (!isNotBlank(technologyRouting.getProcessRouteCode())) { |
| | | technologyRouting.setProcessRouteCode(buildProcessRouteCode(technologyRouting.getId())); |
| | | technologyRoutingMapper.updateById(technologyRouting); |
| | | } |
| | | String code = OrderUtils.countTodayByCreateTime(technologyRoutingMapper, "GYLX", "process_route_code"); |
| | | technologyRouting.setProcessRouteCode(code); |
| | | technologyRoutingMapper.insert(technologyRouting); |
| | | // 带入bom产品结构 |
| | | syncRoutingOperationsByBom(technologyRouting); |
| | | return technologyRouting.getId(); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateTechnologyRouting(TechnologyRouting technologyRouting) { |
| | | boolean updated = this.updateById(technologyRouting); |
| | | if (updated) { |
| | | TechnologyRouting currentRouting = this.getById(technologyRouting.getId()); |
| | | syncRoutingOperationsByBom(currentRouting); |
| | | } |
| | | return updated; |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (ids == null || ids.isEmpty()) { |
| | | return false; |
| | | } |
| | | // 判断是否有生产订单绑定该工艺路线 |
| | | Long count = productionOrderRoutingMapper.selectCount( |
| | | Wrappers.<ProductionOrderRouting>lambdaQuery() |
| | | .in(ProductionOrderRouting::getTechnologyRoutingId, ids)); |
| | | if (count > 0) { |
| | | throw new ServiceException("该工艺路线已绑定生产订单,无法删除!"); |
| | | } |
| | | List<TechnologyRoutingOperation> routingOperationList = technologyRoutingOperationMapper.selectList( |
| | | Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .in(TechnologyRoutingOperation::getTechnologyRoutingId, ids)); |
| | | if (!routingOperationList.isEmpty()) { |
| | | List<Long> routingOperationIdList = routingOperationList.stream().map(TechnologyRoutingOperation::getId).collect(Collectors.toList()); |
| | | technologyRoutingOperationParamMapper.delete(Wrappers.<TechnologyRoutingOperationParam>lambdaQuery() |
| | | .in(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, routingOperationIdList)); |
| | | } |
| | | technologyRoutingOperationMapper.delete(Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .in(TechnologyRoutingOperation::getTechnologyRoutingId, ids)); |
| | | return this.removeByIds(ids); |
| | | } |
| | | |
| | | /** |
| | | * 按当前选择的 BOM 重建工艺路线工序: |
| | | * 1. 先删除该工艺路线下已有工序 |
| | | * 2. 读取 BOM 结构中的 operation_id |
| | | * 3. 按 BOM 出现顺序去重后重新写入工艺路线工序表 |
| | | */ |
| | | private void syncRoutingOperationsByBom(TechnologyRouting technologyRouting) { |
| | | List<TechnologyRoutingOperation> oldRoutingOperationList = technologyRoutingOperationMapper.selectList( |
| | | Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())); |
| | | if (!oldRoutingOperationList.isEmpty()) { |
| | | List<Long> oldRoutingOperationIdList = oldRoutingOperationList.stream().map(TechnologyRoutingOperation::getId).collect(Collectors.toList()); |
| | | technologyRoutingOperationParamMapper.delete(Wrappers.<TechnologyRoutingOperationParam>lambdaQuery() |
| | | .in(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, oldRoutingOperationIdList)); |
| | | technologyRoutingOperationMapper.delete(Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())); |
| | | } |
| | | if (technologyRouting.getBomId() == null) { |
| | | return; |
| | | } |
| | | |
| | | List<TechnologyBomStructure> bomStructures = technologyBomStructureMapper.selectList( |
| | | Wrappers.<TechnologyBomStructure>lambdaQuery() |
| | | .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId()) |
| | | .orderByAsc(TechnologyBomStructure::getId) |
| | | ); |
| | | if (bomStructures.isEmpty()) { |
| | | throw new ServiceException("bom产品结构为空!"); |
| | | } |
| | | |
| | | Map<Long, TechnologyBomStructure> structureById = new HashMap<>(); |
| | | Map<Long, List<TechnologyBomStructure>> childrenByParentId = new LinkedHashMap<>(); |
| | | List<TechnologyBomStructure> rootList = new ArrayList<>(); |
| | | for (TechnologyBomStructure bomStructure : bomStructures) { |
| | | if (bomStructure == null) { |
| | | continue; |
| | | } |
| | | if (bomStructure.getId() != null) { |
| | | structureById.put(bomStructure.getId(), bomStructure); |
| | | } |
| | | if (bomStructure.getParentId() == null || bomStructure.getParentId() == 0L) { |
| | | rootList.add(bomStructure); |
| | | } else { |
| | | childrenByParentId.computeIfAbsent(bomStructure.getParentId(), key -> new ArrayList<>()).add(bomStructure); |
| | | } |
| | | } |
| | | |
| | | // 后序遍历BOM |
| | | List<TechnologyBomStructure> orderedOperationNodes = new ArrayList<>(); |
| | | Set<Long> visitingIds = new HashSet<>(); |
| | | for (TechnologyBomStructure rootNode : rootList) { |
| | | collectOperationNodePostOrder(rootNode, childrenByParentId, orderedOperationNodes, visitingIds); |
| | | } |
| | | if (orderedOperationNodes.isEmpty()) { |
| | | throw new ServiceException("bom产品结构未配置消耗工序!"); |
| | | } |
| | | |
| | | // 同一层级(同一父节点)内相同工序合并为一道工序,例如同层的多个压接件合并成一道压接; |
| | | // 合并后的位置取该工序最后一次出现的位置,确保其全部输入物料的工序都在它之前 |
| | | Map<String, Integer> lastPositionByKey = new HashMap<>(); |
| | | Map<String, TechnologyBomStructure> operationNodeByKey = new LinkedHashMap<>(); |
| | | for (int i = 0; i < orderedOperationNodes.size(); i++) { |
| | | TechnologyBomStructure bomStructure = orderedOperationNodes.get(i); |
| | | String dedupKey = buildBomOperationDedupKey(bomStructure); |
| | | lastPositionByKey.put(dedupKey, i); |
| | | operationNodeByKey.putIfAbsent(dedupKey, bomStructure); |
| | | } |
| | | List<String> sortedDedupKeys = new ArrayList<>(operationNodeByKey.keySet()); |
| | | sortedDedupKeys.sort(Comparator.comparingInt(lastPositionByKey::get)); |
| | | |
| | | int dragSort = 1; |
| | | for (String dedupKey : sortedDedupKeys) { |
| | | TechnologyBomStructure bomStructure = operationNodeByKey.get(dedupKey); |
| | | TechnologyRoutingOperation routingOperation = new TechnologyRoutingOperation(); |
| | | routingOperation.setTechnologyRoutingId(technologyRouting.getId()); |
| | | routingOperation.setProductModelId(resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId())); |
| | | routingOperation.setTechnologyOperationId(bomStructure.getOperationId()); |
| | | routingOperation.setDragSort(dragSort++); |
| | | TechnologyOperation technologyOperation = getOperation(bomStructure.getOperationId()); |
| | | routingOperation.setIsQuality(technologyOperation != null ? technologyOperation.getIsQuality() : null); |
| | | routingOperation.setIsProduction(technologyOperation != null ? technologyOperation.getIsProduction() : null); |
| | | routingOperation.setType(technologyOperation != null ? technologyOperation.getType() : null); |
| | | technologyRoutingOperationMapper.insert(routingOperation); |
| | | syncRoutingOperationParams(routingOperation.getId(), bomStructure.getOperationId()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 后序遍历 BOM |
| | | */ |
| | | private void collectOperationNodePostOrder(TechnologyBomStructure node, |
| | | Map<Long, List<TechnologyBomStructure>> childrenByParentId, |
| | | List<TechnologyBomStructure> orderedOperationNodes, |
| | | Set<Long> visitingIds) { |
| | | if (node == null || node.getId() == null) { |
| | | return; |
| | | } |
| | | if (!visitingIds.add(node.getId())) { |
| | | return; |
| | | } |
| | | List<TechnologyBomStructure> children = childrenByParentId.get(node.getId()); |
| | | if (children != null && !children.isEmpty()) { |
| | | for (TechnologyBomStructure child : children) { |
| | | collectOperationNodePostOrder(child, childrenByParentId, orderedOperationNodes, visitingIds); |
| | | } |
| | | } |
| | | visitingIds.remove(node.getId()); |
| | | if (node.getOperationId() != null) { |
| | | orderedOperationNodes.add(node); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 去重键:同一父节点 + 相同工序(只在同一层级内合并,不同层级、不同分支的相同工序不会合并)。 |
| | | */ |
| | | private String buildBomOperationDedupKey(TechnologyBomStructure bomStructure) { |
| | | Long operationId = bomStructure == null ? null : bomStructure.getOperationId(); |
| | | Long parentId = bomStructure == null ? null : bomStructure.getParentId(); |
| | | return operationId + "#" + (parentId == null ? "root" : String.valueOf(parentId)); |
| | | } |
| | | |
| | | /** |
| | | * 工序产出产品 = 父节点的产品(本行物料由父节点层级的消耗工序消耗,产出的是上一层产品, |
| | | * 而不是本行物料自身);仅当父节点缺失或父节点没有产品时,才回退到本节点自身产品、再回退到工艺路线产品。 |
| | | */ |
| | | private Long resolveOutputProductModelId(TechnologyBomStructure bomStructure, |
| | | Map<Long, TechnologyBomStructure> structureById, |
| | | Long routingProductModelId) { |
| | | if (bomStructure == null) { |
| | | return routingProductModelId; |
| | | } |
| | | Long parentId = bomStructure.getParentId(); |
| | | if (parentId != null && parentId != 0L && structureById != null) { |
| | | TechnologyBomStructure parent = structureById.get(parentId); |
| | | if (parent != null && parent.getProductModelId() != null) { |
| | | return parent.getProductModelId(); |
| | | } |
| | | } |
| | | if (bomStructure.getProductModelId() != null) { |
| | | return bomStructure.getProductModelId(); |
| | | } |
| | | return routingProductModelId; |
| | | } |
| | | |
| | | private void syncRoutingOperationParams(Long technologyRoutingOperationId, Long technologyOperationId) { |
| | | List<TechnologyOperationParam> operationParamList = technologyOperationParamMapper.selectList( |
| | | Wrappers.<TechnologyOperationParam>lambdaQuery() |
| | | .eq(TechnologyOperationParam::getTechnologyOperationId, technologyOperationId) |
| | | .orderByAsc(TechnologyOperationParam::getId)); |
| | | if (operationParamList.isEmpty()) { |
| | | return; |
| | | } |
| | | for (TechnologyOperationParam operationParam : operationParamList) { |
| | | TechnologyParam technologyParam = technologyParamMapper.selectById(operationParam.getTechnologyParamId()); |
| | | if (technologyParam == null) { |
| | | continue; |
| | | } |
| | | TechnologyRoutingOperationParam snapshot = new TechnologyRoutingOperationParam(); |
| | | snapshot.setTechnologyRoutingOperationId(technologyRoutingOperationId); |
| | | snapshot.setTechnologyOperationParamId(operationParam.getId()); |
| | | snapshot.setTechnologyOperationId(operationParam.getTechnologyOperationId()); |
| | | snapshot.setParamId(technologyParam.getId()); |
| | | snapshot.setParamCode(technologyParam.getParamCode()); |
| | | snapshot.setParamName(technologyParam.getParamName()); |
| | | snapshot.setParamType(technologyParam.getParamType()); |
| | | snapshot.setParamFormat(technologyParam.getParamFormat()); |
| | | snapshot.setUnit(technologyParam.getUnit()); |
| | | snapshot.setIsRequired(technologyParam.getIsRequired()); |
| | | snapshot.setRemark(technologyParam.getRemark()); |
| | | snapshot.setStandardValue(operationParam.getStandardValue()); |
| | | technologyRoutingOperationParamMapper.insert(snapshot); |
| | | } |
| | | } |
| | | |
| | | private TechnologyOperation getOperation(Long operationId) { |
| | | if (operationId == null) { |
| | | return null; |
| | | } |
| | | return technologyOperationMapper.selectById(operationId); |
| | | } |
| | | |
| | | private String buildProcessRouteCode(Long id) { |
| | | String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String idStr = String.format("%06d", id); |