package com.ruoyi.production.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.production.bean.dto.ProductionOrderDto; import com.ruoyi.production.bean.vo.ProductionOrderVo; import com.ruoyi.production.mapper.ProductionBomStructureMapper; import com.ruoyi.production.mapper.ProductionOperationTaskMapper; import com.ruoyi.production.mapper.ProductionOrderBomMapper; import com.ruoyi.production.mapper.ProductionOrderMapper; import com.ruoyi.production.mapper.ProductionOrderRoutingMapper; import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper; import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper; import com.ruoyi.production.mapper.ProductionProductMainMapper; import com.ruoyi.production.pojo.ProductionBomStructure; import com.ruoyi.production.pojo.ProductionOperationTask; import com.ruoyi.production.pojo.ProductionOrder; import com.ruoyi.production.pojo.ProductionOrderBom; import com.ruoyi.production.pojo.ProductionOrderRouting; import com.ruoyi.production.pojo.ProductionOrderRoutingOperation; import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam; import com.ruoyi.production.pojo.ProductionProductMain; import com.ruoyi.production.service.ProductionOrderService; import com.ruoyi.technology.mapper.TechnologyBomMapper; import com.ruoyi.technology.mapper.TechnologyBomStructureMapper; import com.ruoyi.technology.mapper.TechnologyRoutingMapper; import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper; import com.ruoyi.technology.mapper.TechnologyRoutingOperationParamMapper; import com.ruoyi.technology.pojo.TechnologyBom; import com.ruoyi.technology.pojo.TechnologyBomStructure; import com.ruoyi.technology.pojo.TechnologyRouting; import com.ruoyi.technology.pojo.TechnologyRoutingOperation; import com.ruoyi.technology.pojo.TechnologyRoutingOperationParam; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) @RequiredArgsConstructor public class ProductionOrderServiceImpl extends ServiceImpl implements ProductionOrderService { private final ProductionOrderRoutingMapper productionOrderRoutingMapper; private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final ProductionOrderBomMapper productionOrderBomMapper; private final ProductionBomStructureMapper productionBomStructureMapper; private final ProductionProductMainMapper productionProductMainMapper; private final TechnologyRoutingMapper technologyRoutingMapper; private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; private final TechnologyRoutingOperationParamMapper technologyRoutingOperationParamMapper; private final TechnologyBomMapper technologyBomMapper; private final TechnologyBomStructureMapper technologyBomStructureMapper; @Override public com.baomidou.mybatisplus.core.metadata.IPage pageProductionOrder(Page page, ProductionOrderDto dto) { Page entityPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); return this.page(entityPage, buildQueryWrapper(dto)).convert(item -> BeanUtil.copyProperties(item, ProductionOrderVo.class)); } @Override public List listProductionOrder(ProductionOrderDto dto) { return BeanUtil.copyToList(this.list(buildQueryWrapper(dto)), ProductionOrderVo.class); } @Override public ProductionOrderVo getProductionOrderInfo(Long id) { ProductionOrder item = this.getById(id); return item == null ? null : BeanUtil.copyProperties(item, ProductionOrderVo.class); } @Override public boolean saveProductionOrder(ProductionOrder productionOrder) { ProductionOrder oldOrder = productionOrder.getId() == null ? null : this.getById(productionOrder.getId()); if (productionOrder.getNpsNo() == null || productionOrder.getNpsNo().trim().isEmpty()) { productionOrder.setNpsNo(generateNextOrderNo()); } if (productionOrder.getCompleteQuantity() == null) { productionOrder.setCompleteQuantity(BigDecimal.ZERO); } boolean saved = this.saveOrUpdate(productionOrder); if (!saved) { return false; } boolean needSync = productionOrder.getTechnologyRoutingId() != null && (oldOrder == null || !Objects.equals(oldOrder.getTechnologyRoutingId(), productionOrder.getTechnologyRoutingId()) || productionOrderRoutingMapper.selectCount(Wrappers.lambdaQuery() .eq(ProductionOrderRouting::getProductionOrderId, productionOrder.getId())) == 0); if (needSync) { syncProductionOrderSnapshot(productionOrder.getId()); } return true; } @Override public boolean removeProductionOrder(List ids) { if (ids == null || ids.isEmpty()) { return false; } for (Long id : ids) { clearProductionSnapshot(id); } return this.removeByIds(ids); } @Override public int syncProductionOrderSnapshot(Long productionOrderId) { ProductionOrder productionOrder = this.getById(productionOrderId); if (productionOrder == null) { throw new ServiceException("Production order not found"); } if (productionOrder.getTechnologyRoutingId() == null) { throw new ServiceException("technologyRoutingId is required"); } TechnologyRouting technologyRouting = technologyRoutingMapper.selectById(productionOrder.getTechnologyRoutingId()); if (technologyRouting == null) { throw new ServiceException("Technology routing not found"); } clearProductionSnapshot(productionOrderId); ProductionOrderRouting orderRouting = new ProductionOrderRouting(); orderRouting.setProductionOrderId(productionOrder.getId()); orderRouting.setTechnologyRoutingId(technologyRouting.getId()); orderRouting.setProductModelId(technologyRouting.getProductModelId()); orderRouting.setProcessRouteCode(technologyRouting.getProcessRouteCode()); orderRouting.setDescription(technologyRouting.getDescription()); orderRouting.setBomId(technologyRouting.getBomId()); productionOrderRoutingMapper.insert(orderRouting); int syncedParamCount = 0; List routingOperations = technologyRoutingOperationMapper.selectList( Wrappers.lambdaQuery() .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()) .orderByAsc(TechnologyRoutingOperation::getDragSort) .orderByAsc(TechnologyRoutingOperation::getId)); for (TechnologyRoutingOperation sourceOperation : routingOperations) { ProductionOrderRoutingOperation targetOperation = new ProductionOrderRoutingOperation(); targetOperation.setProductionOrderId(productionOrder.getId()); targetOperation.setTechnologyRoutingOperationId(sourceOperation.getId()); targetOperation.setTechnologyRoutingId(orderRouting.getId()); targetOperation.setProductModelId(sourceOperation.getProductModelId()); targetOperation.setDragSort(sourceOperation.getDragSort()); targetOperation.setIsQuality(sourceOperation.getIsQuality()); productionOrderRoutingOperationMapper.insert(targetOperation); ProductionOperationTask task = new ProductionOperationTask(); task.setTechnologyRoutingOperationId(targetOperation.getId()); task.setProductionOrderId(productionOrder.getId()); task.setPlanQuantity(defaultDecimal(productionOrder.getQuantity())); task.setCompleteQuantity(BigDecimal.ZERO); task.setWorkOrderNo(generateNextTaskNo()); task.setStatus(1); productionOperationTaskMapper.insert(task); List sourceParams = technologyRoutingOperationParamMapper.selectList( Wrappers.lambdaQuery() .eq(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, sourceOperation.getId()) .orderByAsc(TechnologyRoutingOperationParam::getId)); for (TechnologyRoutingOperationParam sourceParam : sourceParams) { ProductionOrderRoutingOperationParam targetParam = new ProductionOrderRoutingOperationParam(); targetParam.setProductionOrderId(productionOrder.getId()); targetParam.setTechnologyRoutingOperationId(targetOperation.getId()); targetParam.setTechnologyRoutingOperationParamId(sourceParam.getId()); targetParam.setParamId(sourceParam.getParamId()); targetParam.setTechnologyOperationId(sourceParam.getTechnologyOperationId()); targetParam.setTechnologyOperationParamId(sourceParam.getTechnologyOperationParamId()); targetParam.setParamCode(sourceParam.getParamCode()); targetParam.setParamName(sourceParam.getParamName()); targetParam.setParamType(sourceParam.getParamType()); targetParam.setParamFormat(sourceParam.getParamFormat()); targetParam.setUnit(sourceParam.getUnit()); targetParam.setIsRequired(sourceParam.getIsRequired()); targetParam.setRemark(sourceParam.getRemark()); targetParam.setStandardValue(sourceParam.getStandardValue()); productionOrderRoutingOperationParamMapper.insert(targetParam); syncedParamCount++; } } syncProductionOrderBomSnapshot(productionOrder, technologyRouting); return syncedParamCount; } private void syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) { if (technologyRouting.getBomId() == null) { return; } TechnologyBom technologyBom = technologyBomMapper.selectById(technologyRouting.getBomId()); if (technologyBom == null) { throw new ServiceException("Technology BOM not found"); } List structureList = technologyBomStructureMapper.selectList( Wrappers.lambdaQuery() .eq(TechnologyBomStructure::getBomId, technologyBom.getId()) .orderByAsc(TechnologyBomStructure::getId)); TechnologyBomStructure root = structureList.stream().filter(item -> item.getParentId() == null).findFirst().orElse(null); ProductionOrderBom orderBom = new ProductionOrderBom(); orderBom.setProductionOrderId(productionOrder.getId()); orderBom.setBomId(Long.valueOf(technologyBom.getId())); orderBom.setProductModelId(root != null ? root.getProductModelId() : productionOrder.getProductModelId()); orderBom.setTechnologyOperationId(root == null ? null : root.getOperationId()); orderBom.setUnitQuantity(root != null && root.getUnitQuantity() != null ? root.getUnitQuantity() : BigDecimal.ONE); orderBom.setDemandedQuantity(root != null && root.getDemandedQuantity() != null ? root.getDemandedQuantity() : defaultDecimal(productionOrder.getQuantity())); orderBom.setUnit(root == null ? null : root.getUnit()); productionOrderBomMapper.insert(orderBom); Map idMap = new HashMap<>(); for (TechnologyBomStructure source : structureList) { ProductionBomStructure target = new ProductionBomStructure(); target.setProductionOrderId(productionOrder.getId()); target.setProductionOrderBomId(orderBom.getId()); target.setParentId(source.getParentId() == null ? null : idMap.get(source.getParentId())); target.setProductModelId(source.getProductModelId()); target.setTechnologyOperationId(source.getOperationId()); target.setUnitQuantity(source.getUnitQuantity()); target.setDemandedQuantity(source.getDemandedQuantity()); target.setUnit(source.getUnit()); productionBomStructureMapper.insert(target); idMap.put(source.getId(), target.getId()); } } private void clearProductionSnapshot(Long productionOrderId) { List taskIds = productionOperationTaskMapper.selectList( Wrappers.lambdaQuery() .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)) .stream().map(ProductionOperationTask::getId).collect(Collectors.toList()); if (!taskIds.isEmpty()) { boolean started = productionProductMainMapper.selectCount( Wrappers.lambdaQuery() .in(ProductionProductMain::getProductionOperationTaskId, taskIds)) > 0; if (started) { throw new ServiceException("Production order already started, snapshot cannot be regenerated"); } productionOperationTaskMapper.delete(Wrappers.lambdaQuery() .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)); } productionOrderRoutingOperationParamMapper.delete(Wrappers.lambdaQuery() .eq(ProductionOrderRoutingOperationParam::getProductionOrderId, productionOrderId)); productionOrderRoutingOperationMapper.delete(Wrappers.lambdaQuery() .eq(ProductionOrderRoutingOperation::getProductionOrderId, productionOrderId)); productionOrderRoutingMapper.delete(Wrappers.lambdaQuery() .eq(ProductionOrderRouting::getProductionOrderId, productionOrderId)); productionBomStructureMapper.delete(Wrappers.lambdaQuery() .eq(ProductionBomStructure::getProductionOrderId, productionOrderId)); productionOrderBomMapper.delete(Wrappers.lambdaQuery() .eq(ProductionOrderBom::getProductionOrderId, productionOrderId)); } private LambdaQueryWrapper buildQueryWrapper(ProductionOrderDto dto) { ProductionOrder query = dto == null ? new ProductionOrder() : dto; return Wrappers.lambdaQuery() .eq(query.getId() != null, ProductionOrder::getId, query.getId()) .eq(query.getSalesLedgerId() != null, ProductionOrder::getSalesLedgerId, query.getSalesLedgerId()) .eq(query.getProductModelId() != null, ProductionOrder::getProductModelId, query.getProductModelId()) .eq(query.getTechnologyRoutingId() != null, ProductionOrder::getTechnologyRoutingId, query.getTechnologyRoutingId()) .like(query.getNpsNo() != null && !query.getNpsNo().trim().isEmpty(), ProductionOrder::getNpsNo, query.getNpsNo()) .orderByDesc(ProductionOrder::getId); } private String generateNextOrderNo() { String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String prefix = "SC" + datePrefix; ProductionOrder latestOrder = this.getOne(Wrappers.lambdaQuery() .likeRight(ProductionOrder::getNpsNo, prefix) .orderByDesc(ProductionOrder::getNpsNo) .last("limit 1")); int sequence = 1; if (latestOrder != null && latestOrder.getNpsNo() != null && latestOrder.getNpsNo().startsWith(prefix)) { try { sequence = Integer.parseInt(latestOrder.getNpsNo().substring(prefix.length())) + 1; } catch (NumberFormatException ignored) { sequence = 1; } } return prefix + String.format("%04d", sequence); } private String generateNextTaskNo() { String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String prefix = "GD" + datePrefix; ProductionOperationTask lastTask = productionOperationTaskMapper.selectOne( Wrappers.lambdaQuery() .likeRight(ProductionOperationTask::getWorkOrderNo, prefix) .orderByDesc(ProductionOperationTask::getWorkOrderNo) .last("limit 1")); int sequence = 1; if (lastTask != null && lastTask.getWorkOrderNo() != null && lastTask.getWorkOrderNo().startsWith(prefix)) { try { sequence = Integer.parseInt(lastTask.getWorkOrderNo().substring(prefix.length())) + 1; } catch (NumberFormatException ignored) { sequence = 1; } } return prefix + String.format("%03d", sequence); } private BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } }