liyong
5 天以前 536774eea8902efc088c5b904ff75bbe0af33418
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -1,14 +1,16 @@
package cn.iocoder.yudao.module.mes.service.pro.feedback;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
@@ -17,8 +19,8 @@
import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService;
@@ -31,6 +33,8 @@
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -53,7 +57,7 @@
    @Resource
    private MesProRouteProcessService routeProcessService;
    @Resource
    private MesMdWorkstationService workstationService;
    private MesProRouteProductBomService routeProductBomService;
    @Resource
    @Lazy // 避免循环依赖
    private MesProTaskService taskService;
@@ -102,6 +106,19 @@
    @Override
    public MesProFeedbackDO getFeedback(Long id) {
        return feedbackMapper.selectById(id);
    }
    @Override
    public List<MesProFeedbackDO> getFeedbackListByTaskIds(Collection<Long> taskIds) {
        if (CollUtil.isEmpty(taskIds)) {
            return Collections.emptyList();
        }
        return feedbackMapper.selectListByTaskIds(taskIds);
    }
    @Override
    public List<MesProFeedbackDO> getFeedbackListByTaskId(Long taskId) {
        return feedbackMapper.selectListByTaskId(taskId);
    }
    @Override
@@ -154,10 +171,20 @@
            throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity());
        }
        // 3. 物料消耗:根据工序 BOM 生成消耗记录并执行扣减
        MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback);
        if (itemConsume != null) {
            itemConsumeService.finishItemConsume(itemConsume.getId());
        // 3. 物料消耗:倒冲工序按 BOM 自动扣料;非倒冲工序跳过(物料走手工领料/投料)
        boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag());
        if (backflushFlag) {
            // 3.1 校验倒冲工序必须配置 BOM 消耗,避免静默不扣料
            List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList(
                    feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId());
            if (CollUtil.isEmpty(boms)) {
                throw exception(PRO_FEEDBACK_BACKFLUSH_BOM_REQUIRED);
            }
            // 3.2 生成消耗记录并执行扣减(BOM × 报工数量,FIFO 扣线边库)
            MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback);
            if (itemConsume != null) {
                itemConsumeService.finishItemConsume(itemConsume.getId());
            }
        }
        // 4. 关键工序:生成产出单,并根据是否需要检验决定入库方式
@@ -174,6 +201,12 @@
            MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false);
            productProduceService.finishProductProduce(produce.getId());
            updateTaskAndWorkOrderByFeedback(feedback);
        } else {
            // 4.3 非关键工序:只更新任务的已生产数量(工单不更新,工单只看关键工序产出)
            taskService.updateProducedQuantity(feedback.getTaskId(),
                    feedback.getFeedbackQuantity(),
                    ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO),
                    ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO));
        }
        // 5. 非关键工序 / 关键非质检工序:直接完成(清零 uncheckQuantity 防止 !key+check 留脏数据)
@@ -246,23 +279,30 @@
    /**
     * 校验报工单的业务数据(创建 & 修改共用)
     *
     * <p>以生产任务为核心:前端只需传 taskId,workOrderId、routeId、processId、itemId、workstationId
     * 从任务自动填充,无需前端重复传入。
     *
     * @param reqVO 报工请求
     * @return 关联的生产任务
     */
    private MesProTaskDO validateFeedbackData(MesProFeedbackSaveReqVO reqVO) {
        // 1. 校验工作站存在
        MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId());
        if (ObjUtil.notEqual(workstation.getProcessId(), reqVO.getProcessId())) {
            throw exception(PRO_WORKSTATION_PROCESS_MISMATCH);
        }
        // 1. 校验任务存在且未终态
        MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
        // 2.1 校验工艺路线 + 工序配置有效
        // 2. 从任务自动填充关联字段(前端无需传入,后端统一从任务获取)
        reqVO.setWorkOrderId(task.getWorkOrderId());
        reqVO.setRouteId(task.getRouteId());
        reqVO.setProcessId(task.getProcessId());
        reqVO.setItemId(task.getItemId());
        reqVO.setWorkstationId(task.getWorkstationId());
        // 3. 校验工艺路线 + 工序配置有效
        MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
                reqVO.getRouteId(), reqVO.getProcessId());
                task.getRouteId(), task.getProcessId());
        if (routeProcess == null) {
            throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
        }
        // 2.2 校验数量
        // 4.1 校验数量
        boolean checkFlag = Boolean.TRUE.equals(routeProcess.getCheckFlag());
        if (checkFlag) {
            // 需要检验:只需填报工数量,且必须 > 0
@@ -279,33 +319,7 @@
            }
        }
        // 3. 校验工单已确认
        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
        if (ObjUtil.notEqual(workOrder.getProductId(), reqVO.getItemId())) {
            throw exception(PRO_WORK_ORDER_PRODUCT_MISMATCH);
        }
        // 4. 校验任务存在且未终态(已完成/已取消),并返回任务用于冗余 itemId
        MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
        validateTaskRelation(task, workstation, workOrder, reqVO);
        return task;
    }
    private void validateTaskRelation(MesProTaskDO task, MesMdWorkstationDO workstation,
                                      MesProWorkOrderDO workOrder, MesProFeedbackSaveReqVO reqVO) {
        if (ObjUtil.notEqual(task.getWorkOrderId(), workOrder.getId())) {
            throw exception(PRO_TASK_WORK_ORDER_MISMATCH);
        }
        if (ObjUtil.notEqual(task.getWorkstationId(), workstation.getId())) {
            throw exception(PRO_TASK_WORKSTATION_MISMATCH);
        }
        if (ObjUtil.notEqual(task.getRouteId(), reqVO.getRouteId())
                || ObjUtil.notEqual(task.getProcessId(), reqVO.getProcessId())) {
            throw exception(PRO_TASK_ROUTE_PROCESS_MISMATCH);
        }
        if (ObjUtil.notEqual(task.getItemId(), reqVO.getItemId())) {
            throw exception(PRO_TASK_ITEM_MISMATCH);
        }
    }
    @Override