云
6 小时以前 3edf8c4fc07f9784e2d04ee7a98692bb457749eb
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
@@ -5,11 +5,17 @@
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderProcessMapper;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -21,6 +27,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -28,7 +35,7 @@
/**
 * MES 生产工单工序 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -44,6 +51,15 @@
    private MesProProcessService processService;
    @Resource
    private MesProRouteProcessService routeProcessService;
    @Resource
    private MesProRouteProductService routeProductService;
    @Resource
    private MesWmWarehouseAreaService warehouseAreaService;
    @Resource
    @Lazy
    private MesProWorkOrderBomService workOrderBomService;
    @Resource
    private MesProWorkOrderBomMapper workOrderBomMapper;
    @Override
    public Long createWorkOrderProcess(MesProWorkOrderProcessSaveReqVO createReqVO) {
@@ -52,6 +68,12 @@
        // 2. 插入数据
        MesProWorkOrderProcessDO workOrderProcess = BeanUtils.toBean(createReqVO, MesProWorkOrderProcessDO.class);
        if (CollUtil.isNotEmpty(createReqVO.getOutputItemIds())) {
            workOrderProcess.setOutputItemId(
                createReqVO.getOutputItemIds().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(",")));
        }
        workOrderProcessMapper.insert(workOrderProcess);
        // 3. 重建工序链
@@ -67,7 +89,16 @@
        // 2. 更新数据
        MesProWorkOrderProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkOrderProcessDO.class);
        if (CollUtil.isNotEmpty(updateReqVO.getOutputItemIds())) {
            updateObj.setOutputItemId(
                updateReqVO.getOutputItemIds().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(",")));
        }
        workOrderProcessMapper.updateById(updateObj);
        // 报工仓库允许置空,单独更新
        workOrderProcessMapper.updateWarehouseById(updateReqVO.getId(), updateReqVO.getWarehouseId(),
                updateReqVO.getLocationId(), updateReqVO.getAreaId());
        // 3. 重建工序链
        rebuildProcessChain(updateReqVO.getWorkOrderId());
@@ -101,6 +132,24 @@
    }
    @Override
    public MesProWorkOrderProcessDO getWorkOrderProcessByWorkOrderIdAndProcessId(Long workOrderId, Long processId) {
        return workOrderProcessMapper.selectByWorkOrderIdAndProcessId(workOrderId, processId);
    }
    @Override
    public MesProWorkOrderProcessDO getLastWorkOrderProcessByWorkOrderId(Long workOrderId) {
        return workOrderProcessMapper.selectLastByWorkOrderId(workOrderId);
    }
    @Override
    public List<MesProWorkOrderProcessDO> getWorkOrderProcessListByWorkOrderIds(java.util.Collection<Long> workOrderIds) {
        if (CollUtil.isEmpty(workOrderIds)) {
            return Collections.emptyList();
        }
        return workOrderProcessMapper.selectListByWorkOrderIds(workOrderIds);
    }
    @Override
    public void deleteWorkOrderProcessByWorkOrderId(Long workOrderId) {
        workOrderProcessMapper.deleteByWorkOrderId(workOrderId);
    }
@@ -112,11 +161,15 @@
        if (CollUtil.isEmpty(routeProcessList)) {
            return Collections.emptyList();
        }
        // 工艺路线产品上配置的报工仓库作为工序默认值,下单时可再修改
        MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId);
        // 转换为工单工序 DO(不设置 workOrderId,前端保存时再设置)
        List<MesProWorkOrderProcessDO> processList = new ArrayList<>(routeProcessList.size());
        for (MesProRouteProcessDO routeProcess : routeProcessList) {
            processList.add(MesProWorkOrderProcessDO.builder()
                    .processId(routeProcess.getProcessId())
                    .paramTemplateId(routeProcess.getParamTemplateId())
                    .yieldRate(routeProcess.getYieldRate())
                    .sort(routeProcess.getSort())
                    .nextProcessId(routeProcess.getNextProcessId())
                    .linkType(routeProcess.getLinkType())
@@ -127,6 +180,9 @@
                    .checkFlag(routeProcess.getCheckFlag())
                    .backflushFlag(routeProcess.getBackflushFlag())
                    .outputItemId(routeProcess.getOutputItemId())
                    .warehouseId(routeProduct != null ? routeProduct.getWarehouseId() : null)
                    .locationId(routeProduct != null ? routeProduct.getLocationId() : null)
                    .areaId(routeProduct != null ? routeProduct.getAreaId() : null)
                    .remark(routeProcess.getRemark())
                    .build());
        }
@@ -167,16 +223,54 @@
            if (item.getId() == null) {
                // 新增
                MesProWorkOrderProcessDO process = BeanUtils.toBean(item, MesProWorkOrderProcessDO.class);
                if (CollUtil.isNotEmpty(item.getOutputItemIds())) {
                    process.setOutputItemId(item.getOutputItemIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
                }
                workOrderProcessMapper.insert(process);
            } else {
                // 更新
                MesProWorkOrderProcessDO updateObj = BeanUtils.toBean(item, MesProWorkOrderProcessDO.class);
                if (CollUtil.isNotEmpty(item.getOutputItemIds())) {
                    updateObj.setOutputItemId(item.getOutputItemIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
                }
                workOrderProcessMapper.updateById(updateObj);
                // 报工仓库允许置空,单独更新
                workOrderProcessMapper.updateWarehouseById(item.getId(), item.getWarehouseId(),
                        item.getLocationId(), item.getAreaId());
            }
        }
        // 4. 重建工序链
        // 4. 保存工单 BOM 投料(合并所有工序的 bomItems,按 itemId 去重)
        saveWorkOrderBomFromProcesses(workOrderId, list);
        // 5. 重建工序链
        rebuildProcessChain(workOrderId);
    }
    /**
     * 从工序列表中提取 BOM 投料,按工序分别保存到工单 BOM 表
     */
    private void saveWorkOrderBomFromProcesses(Long workOrderId, List<MesProWorkOrderProcessSaveReqVO> list) {
        workOrderBomService.deleteWorkOrderBomByWorkOrderId(workOrderId);
        List<MesProWorkOrderBomDO> bomList = new ArrayList<>();
        for (MesProWorkOrderProcessSaveReqVO item : list) {
            if (CollUtil.isNotEmpty(item.getBomItems())) {
                for (MesProWorkOrderProcessSaveReqVO.BomItem bomItem : item.getBomItems()) {
                    if (bomItem.getItemId() != null) {
                        bomList.add(MesProWorkOrderBomDO.builder()
                                .workOrderId(workOrderId)
                                .processId(item.getProcessId())
                                .itemId(bomItem.getItemId())
                                .quantity(bomItem.getQuantity())
                                .remark(bomItem.getRemark())
                                .build());
                    }
                }
            }
        }
        if (CollUtil.isNotEmpty(bomList)) {
            workOrderBomMapper.insertBatch(bomList);
        }
    }
    // ==================== 私有方法 ====================
@@ -208,7 +302,12 @@
                throw exception(PRO_WORK_ORDER_PROCESS_DUPLICATE);
            }
        }
        // 4. 校验关键工序唯一
        // 4. 校验报工仓库层级(仓库/库区/库位同时填写时校验归属关系)
        if (reqVO.getWarehouseId() != null && reqVO.getLocationId() != null && reqVO.getAreaId() != null) {
            warehouseAreaService.validateWarehouseAreaExists(reqVO.getWarehouseId(),
                    reqVO.getLocationId(), reqVO.getAreaId());
        }
        // 5. 校验关键工序唯一
        if (Boolean.TRUE.equals(reqVO.getKeyFlag())) {
            for (MesProWorkOrderProcessDO process : list) {
                if (reqVO.getId() != null && reqVO.getId().equals(process.getId())) {