huminmin
5 天以前 de4ca61aa27d7f284a03aebd801bcc3d6a75bc6a
修改生产订单中的工艺路线项目是否生产后,更新生产工单数据
已添加1个文件
已修改6个文件
299 ■■■■■ 文件已修改
src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOperationTaskMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
@@ -44,4 +44,7 @@
    @Schema(description = "结束日期")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate endDate;
    @Schema(description = "是否生产")
    private Integer isProduction;
}
src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
@@ -39,7 +39,7 @@
    @PostMapping("/updateRouteItem")
    @Operation(summary = "修改生产订单的工艺路线详情")
    public R updateRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) {
        return R.ok(productionOrderRoutingOperationService.updateById(productionOrderRoutingOperation));
        return R.ok(productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation));
    }
    @DeleteMapping("/deleteRouteItem/{id}")
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
@@ -8,6 +8,8 @@
    R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
    R updateRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
    R deleteRouteItem(Long id);
    int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -23,6 +23,7 @@
import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.production.service.ProductionBomStructureService;
import com.ruoyi.production.util.TaskPlanQuantityUtil;
import com.ruoyi.technology.mapper.TechnologyOperationMapper;
import com.ruoyi.technology.mapper.TechnologyOperationParamMapper;
import com.ruoyi.technology.mapper.TechnologyParamMapper;
@@ -262,7 +263,7 @@
                .filter(item -> item != null && item.getId() != null)
                .collect(Collectors.toMap(ProductionOrderRoutingOperation::getId, item -> item, (left, right) -> left));
        // Keep task plan quantities aligned with the same order BOM snapshot demand used during snapshot creation.
        Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId);
        Map<String, BigDecimal> demandedQuantityMap = TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(structureList, rootProductModelId);
        for (ProductionOperationTask task : taskList) {
            if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) {
                continue;
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java
@@ -4,21 +4,15 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.util.TaskPlanQuantityUtil;
import com.ruoyi.technology.mapper.*;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.ProductionOrderRoutingOperationService;
import com.ruoyi.production.service.ProductionProductMainService;
import com.ruoyi.technology.mapper.TechnologyOperationParamMapper;
import com.ruoyi.technology.mapper.TechnologyParamMapper;
import com.ruoyi.technology.pojo.TechnologyOperationParam;
import com.ruoyi.technology.pojo.TechnologyParam;
import com.ruoyi.technology.pojo.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -26,8 +20,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
@@ -42,6 +35,11 @@
    private final TechnologyOperationParamMapper technologyOperationParamMapper;
    private final TechnologyParamMapper technologyParamMapper;
    private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
    private final ProductionOrderBomMapper productionOrderBomMapper;
    private final ProductionBomStructureMapper productionBomStructureMapper;
    private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
    @Override
    public R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) {
@@ -102,6 +100,109 @@
            productionOperationTaskMapper.insert(productionOperationTask);
        }
        return R.ok();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) {
        Long operationId = productionOrderRoutingOperation.getId();
        // æ›´æ–°å·¥è‰ºè·¯çº¿å·¥åº
        productionOrderRoutingOperationMapper.updateById(productionOrderRoutingOperation);
        // é‡æ–°æŸ¥è¯¢å®Œæ•´è®°å½•(前端可能没有传递所有字段,如 productionOrderId)
        ProductionOrderRoutingOperation updatedOperation = productionOrderRoutingOperationMapper.selectById(operationId);
        if (updatedOperation == null) {
            throw new ServiceException("工艺路线工序不存在");
        }
        // æŸ¥è¯¢æ˜¯å¦å­˜åœ¨å·¥å•
        ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectOne(
                new LambdaQueryWrapper<ProductionOperationTask>()
                        .eq(ProductionOperationTask::getProductionOrderRoutingOperationId, operationId)
                        .last("limit 1"));
        // æ ¹æ®æ˜¯å¦éœ€è¦ç”Ÿäº§è¿›è¡Œå¤„理
        Boolean isProduction = updatedOperation.getIsProduction();
        if (Boolean.TRUE.equals(isProduction)) {
            // éœ€è¦ç”Ÿäº§ï¼šæ£€æŸ¥å·¥å•是否存在,不存在则生成
            if (productionOperationTask == null) {
                ProductionOperationTask task = new ProductionOperationTask();
                task.setProductionOrderRoutingOperationId(updatedOperation.getId());
                task.setProductionOrderId(updatedOperation.getProductionOrderId());
                // èŽ·å–ç”Ÿäº§è®¢å•
                ProductionOrder productionOrder = productionOrderMapper.selectById(updatedOperation.getProductionOrderId());
                if (productionOrder == null) {
                    throw new ServiceException("生产订单不存在");
                }
                // èŽ·å–è®¢å•BOM
                ProductionOrderBom orderBom = productionOrderBomMapper.selectOne(
                        Wrappers.<ProductionOrderBom>lambdaQuery()
                                .eq(ProductionOrderBom::getProductionOrderId, productionOrder.getId()));
                // ç¡®å®šæ ¹äº§å“è§„æ ¼ID
                Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null
                        ? orderBom.getProductModelId()
                        : productionOrder.getProductModelId();
                // èŽ·å–BOM结构列表
                List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null
                        ? Collections.emptyList()
                        : productionBomStructureMapper.selectList(
                        Wrappers.<ProductionBomStructure>lambdaQuery()
                                .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId())
                                .orderByAsc(ProductionBomStructure::getId));
                // æž„建工序需求量映射
                Map<String, BigDecimal> operationDemandedQuantityMap =
                        TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
                // èŽ·å–å·¥è‰ºè·¯çº¿å·¥åºï¼ˆç”¨äºŽè®¡ç®—è®¡åˆ’æ•°é‡ï¼‰
                TechnologyRoutingOperation sourceOperation = technologyRoutingOperationMapper.selectById(
                        updatedOperation.getTechnologyRoutingOperationId());
                // å°†åŽŸæ¥çš„ç§æœ‰æ–¹æ³•æ›¿æ¢ä¸ºè°ƒç”¨å·¥å…·ç±»
                BigDecimal planQuantity = TaskPlanQuantityUtil.resolveTaskPlanQuantity(
                        sourceOperation,
                        operationDemandedQuantityMap,
                        productionOrder,
                        rootProductModelId);
                task.setPlanQuantity(planQuantity);
                task.setCompleteQuantity(BigDecimal.ZERO);
                task.setWorkOrderNo(generateNextTaskNo());
                task.setStatus(2);
                productionOperationTaskMapper.insert(task);
            }
        } else {
            // ä¸éœ€è¦ç”Ÿäº§ï¼šæ£€æŸ¥å·¥å•是否存在
            if (productionOperationTask != null) {
                validateTaskCanRemove(productionOperationTask);
                // æ²¡æœ‰æŠ¥å·¥ï¼Œåˆ™åˆ é™¤å·¥å•
                productionOperationTaskMapper.deleteById(productionOperationTask.getId());
            }
        }
        return R.ok();
    }
    private void validateTaskCanRemove(ProductionOperationTask task) {
        if (task == null || task.getId() == null) {
            return;
        }
        if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) {
            throw new ServiceException("工序已产生报工记录,无法根据 BOM å˜æ›´åˆ é™¤å¯¹åº”工序快照");
        }
        long reportCount = productionProductMainMapper.selectCount(
                Wrappers.<ProductionProductMain>lambdaQuery()
                        .eq(ProductionProductMain::getProductionOperationTaskId, task.getId()));
        if (reportCount > 0) {
            throw new ServiceException("工序已产生报工记录,无法根据 BOM å˜æ›´åˆ é™¤å¯¹åº”工单");
        }
    }
    private BigDecimal defaultDecimal(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }
    @Override
@@ -182,4 +283,24 @@
        }
        return 0;
    }
    private String generateNextTaskNo() {
        // ç”Ÿæˆä¸‹ä¸€ä¸ªç”Ÿäº§å·¥å•号
        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String prefix = "GD" + datePrefix;
        ProductionOperationTask lastTask = productionOperationTaskMapper.selectOne(
                Wrappers.<ProductionOperationTask>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);
    }
}
src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,137 @@
package com.ruoyi.production.util;
import com.ruoyi.production.pojo.ProductionBomStructure;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.technology.pojo.TechnologyRoutingOperation;
import lombok.experimental.UtilityClass;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
 * å·¥å•计划数量计算工具类
 */
@UtilityClass
public class TaskPlanQuantityUtil {
    /**
     * è®¡ç®—工单计划数量(使用 TechnologyRoutingOperation)
     */
    public BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation,
                                              Map<String, BigDecimal> operationDemandedQuantityMap,
                                              ProductionOrder productionOrder,
                                              Long rootProductModelId) {
        if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) {
            return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
        }
        Long outputProductModelId = sourceOperation.getProductModelId() != null
                ? sourceOperation.getProductModelId()
                : rootProductModelId;
        String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId);
        BigDecimal planQuantity = operationDemandedQuantityMap.get(key);
        return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
    }
    /**
     * è®¡ç®—工单计划数量(使用 ProductionOrderRoutingOperation)
     */
    public BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
                                              Map<String, BigDecimal> demandedQuantityMap,
                                              BigDecimal orderQuantity,
                                              Long rootProductModelId) {
        if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) {
            return orderQuantity;
        }
        Long outputProductModelId = routingOperation.getProductModelId() != null
                ? routingOperation.getProductModelId()
                : rootProductModelId;
        String key = buildOperationDemandedQuantityKey(routingOperation.getTechnologyOperationId(), outputProductModelId);
        BigDecimal planQuantity = demandedQuantityMap.get(key);
        return planQuantity != null ? planQuantity : orderQuantity;
    }
    /**
     * æž„建工序需求量映射表
     */
    public Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures, Long rootProductModelId) {
        if (bomStructures == null || bomStructures.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<Long, ProductionBomStructure> structureById = new HashMap<>();
        for (ProductionBomStructure item : bomStructures) {
            if (item != null && item.getId() != null) {
                structureById.put(item.getId(), item);
            }
        }
        Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
        Set<String> mergedOutputNodeKeySet = new HashSet<>();
        for (ProductionBomStructure bomStructure : bomStructures) {
            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
                continue;
            }
            ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
            Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
            if (outputProductModelId == null) {
                continue;
            }
            String mergedOutputNodeKey = buildOperationOutputNodeKey(bomStructure.getTechnologyOperationId(),
                    outputNode == null ? null : outputNode.getId(), outputProductModelId);
            if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) {
                continue;
            }
            BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity());
            String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
            demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add);
        }
        return demandedQuantityMap;
    }
    /**
     * æž„建工序需求量key
     */
    public String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) {
        return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
    }
    /**
     * æž„建输出节点key
     */
    public String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) {
        return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId);
    }
    /**
     * è§£æžå·¥åºè¾“出节点
     */
    public ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure,
                                                             Map<Long, ProductionBomStructure> structureById) {
        if (bomStructure == null) {
            return null;
        }
        if (bomStructure.getParentId() == null) {
            return bomStructure;
        }
        ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
        return parent != null ? parent : bomStructure;
    }
    /**
     * è§£æžè¾“出产品规格ID
     */
    public Long resolveOutputProductModelId(ProductionBomStructure outputNode, Long rootProductModelId) {
        if (outputNode == null) {
            return rootProductModelId;
        }
        return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
    }
    /**
     * é»˜è®¤BigDecimal值
     */
    public BigDecimal defaultDecimal(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }
}
src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
@@ -64,6 +64,9 @@
            <if test="c != null and c.status != null">
                and pot.status = #{c.status}
            </if>
            <if test="c != null and c.isProduction != null">
                and poro.is_production = #{c.isProduction}
            </if>
            <if test="c != null and c.workOrderNo != null and c.workOrderNo != ''">
                and pot.work_order_no like concat('%', #{c.workOrderNo}, '%')
            </if>