package com.ruoyi.technology.service.impl; 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.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 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.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) @RequiredArgsConstructor public class TechnologyRoutingServiceImpl extends ServiceImpl implements TechnologyRoutingService { private final TechnologyRoutingMapper technologyRoutingMapper; 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 pageTechnologyRouting(Page 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) { 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 public boolean removeTechnologyRouting(List ids) { if (ids == null || ids.isEmpty()) { return false; } // 判断是否有生产订单绑定该工艺路线 Long count = productionOrderRoutingMapper.selectCount( Wrappers.lambdaQuery() .in(ProductionOrderRouting::getTechnologyRoutingId, ids)); if (count > 0) { throw new ServiceException("该工艺路线已绑定生产订单,无法删除!"); } List routingOperationList = technologyRoutingOperationMapper.selectList( Wrappers.lambdaQuery() .in(TechnologyRoutingOperation::getTechnologyRoutingId, ids)); if (!routingOperationList.isEmpty()) { List routingOperationIdList = routingOperationList.stream().map(TechnologyRoutingOperation::getId).collect(Collectors.toList()); technologyRoutingOperationParamMapper.delete(Wrappers.lambdaQuery() .in(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, routingOperationIdList)); } technologyRoutingOperationMapper.delete(Wrappers.lambdaQuery() .in(TechnologyRoutingOperation::getTechnologyRoutingId, ids)); return this.removeByIds(ids); } /** * 按当前选择的 BOM 重建工艺路线工序: * 1. 先删除该工艺路线下已有工序及参数快照 * 2. 读取扁平 BOM 明细中的 operation_id * 3. 按 BOM 中首次出现的顺序对工序去重,每道工序生成一条路线工序 * 4. 每条路线工序的产出规格固定为工艺路线的成品规格 */ private void syncRoutingOperationsByBom(TechnologyRouting technologyRouting) { List oldRoutingOperationList = technologyRoutingOperationMapper.selectList( Wrappers.lambdaQuery() .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())); if (!oldRoutingOperationList.isEmpty()) { List oldRoutingOperationIdList = oldRoutingOperationList.stream().map(TechnologyRoutingOperation::getId).collect(Collectors.toList()); technologyRoutingOperationParamMapper.delete(Wrappers.lambdaQuery() .in(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, oldRoutingOperationIdList)); technologyRoutingOperationMapper.delete(Wrappers.lambdaQuery() .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())); } if (technologyRouting.getBomId() == null) { return; } List bomStructures = technologyBomStructureMapper.selectList( Wrappers.lambdaQuery() .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId()) .isNotNull(TechnologyBomStructure::getOperationId) .orderByAsc(TechnologyBomStructure::getId) ); if (bomStructures.isEmpty()) { throw new ServiceException("bom产品结构为空!"); } // 扁平 BOM 中多个物料可以指向同一道工序,这里只按工序去重 Set uniqueOperationIdList = new LinkedHashSet<>(); for (TechnologyBomStructure bomStructure : bomStructures) { uniqueOperationIdList.add(bomStructure.getOperationId()); } int dragSort = 1; for (Long operationId : uniqueOperationIdList) { TechnologyRoutingOperation routingOperation = new TechnologyRoutingOperation(); routingOperation.setTechnologyRoutingId(technologyRouting.getId()); routingOperation.setProductModelId(technologyRouting.getProductModelId()); routingOperation.setTechnologyOperationId(operationId); routingOperation.setDragSort(dragSort++); TechnologyOperation technologyOperation = getOperation(operationId); 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(), operationId); } } private void syncRoutingOperationParams(Long technologyRoutingOperationId, Long technologyOperationId) { List operationParamList = technologyOperationParamMapper.selectList( Wrappers.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); return "GYLX" + dateStr + idStr; } private boolean isNotBlank(String value) { return value != null && !value.trim().isEmpty(); } }