云
6 小时以前 e09f103259fa602382bb5ce2a03361a6397af46a
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -76,6 +76,12 @@
public class MesProTaskServiceImpl implements MesProTaskService {
    @Resource
    private cn.iocoder.yudao.module.mes.service.pro.task.schedule.MesProScheduleGuardService scheduleGuard;
    @Resource
    private cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderMapper scheduleWorkOrderMapper;
    @Resource
    private MesProTaskMapper taskMapper;
    @Resource
@@ -124,10 +130,15 @@
    private MesDvMachineryService machineryService;
    @Override
    @Transactional(rollbackFor = Exception.class, isolation = org.springframework.transaction.annotation.Isolation.SERIALIZABLE)
    public Long createTask(MesProTaskSaveReqVO createReqVO) {
        scheduleGuard.lock();
        // 1. 校验关联数据存在
        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId());
        workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(createReqVO.getWorkOrderId());
        var station = workstationService.validateWorkstationExistsAndEnable(createReqVO.getWorkstationId());
        if (!java.util.Objects.equals(station.getProcessId(), createReqVO.getProcessId())) {
            throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_SCHEDULE_INVALID, "工作站与工序不匹配");
        }
        routeService.validateRouteExists(createReqVO.getRouteId());
        processService.validateProcessExistsAndEnable(createReqVO.getProcessId());
@@ -153,6 +164,7 @@
        if (task.getClientId() == null && workOrder.getClientId() != null) {
            task.setClientId(workOrder.getClientId());
        }
        scheduleGuard.validate(workOrder, task, null);
        // 2.2 插入
        taskMapper.insert(task);
        // 2.2.1 参数快照:优先使用模板;无模板时回退到工序主数据绑定的工艺参数
@@ -160,6 +172,7 @@
        // 2.3 保存指定报工人员
        updateTaskAssignees(task.getId(), createReqVO.getAssigneeUserIds());
        synchronizeScheduledQuantity(workOrder, task.getRouteId());
        return task.getId();
    }
@@ -180,9 +193,11 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class, isolation = org.springframework.transaction.annotation.Isolation.SERIALIZABLE)
    public void updateTask(MesProTaskSaveReqVO updateReqVO) {
        scheduleGuard.lock();
        // 1.1 校验存在
        MesProTaskDO task = validateTaskExists(updateReqVO.getId());
        MesProTaskDO task = validateTaskNotFinished(updateReqVO.getId());
        // 1.2 校验关联数据存在
        if (updateReqVO.getWorkOrderId() != null) {
            workOrderService.validateWorkOrderExists(updateReqVO.getWorkOrderId());
@@ -204,6 +219,19 @@
        }
        BigDecimal quantity = updateReqVO.getQuantity() != null ? updateReqVO.getQuantity() : task.getQuantity();
        MesProTaskDO candidate = BeanUtils.toBean(task, MesProTaskDO.class)
                .setWorkOrderId(updateReqVO.getWorkOrderId() != null ? updateReqVO.getWorkOrderId() : task.getWorkOrderId())
                .setProcessId(updateReqVO.getProcessId() != null ? updateReqVO.getProcessId() : task.getProcessId())
                .setWorkstationId(updateReqVO.getWorkstationId() != null ? updateReqVO.getWorkstationId() : task.getWorkstationId())
                .setQuantity(quantity)
                .setStartTime(updateReqVO.getStartTime() != null ? updateReqVO.getStartTime() : task.getStartTime())
                .setEndTime(updateReqVO.getEndTime() != null ? updateReqVO.getEndTime() : task.getEndTime());
        var station = workstationService.validateWorkstationExistsAndEnable(candidate.getWorkstationId());
        if (!java.util.Objects.equals(station.getProcessId(), candidate.getProcessId())) {
            throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_SCHEDULE_INVALID, "工作站与工序不匹配");
        }
        scheduleGuard.validate(workOrderService.validateWorkOrderConfirmed(candidate.getWorkOrderId()), candidate, task.getId());
        // 2. 更新
        MesProTaskDO updateObj = BeanUtils.toBean(updateReqVO, MesProTaskDO.class)
                .setName(buildTaskName(item, quantity));
@@ -211,6 +239,26 @@
        
        // 3. 更新指定报工人员(比对更新)
        updateTaskAssignees(task.getId(), updateReqVO.getAssigneeUserIds());
        synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()),
                updateReqVO.getRouteId() != null ? updateReqVO.getRouteId() : task.getRouteId());
        if (!java.util.Objects.equals(task.getWorkOrderId(), candidate.getWorkOrderId())) {
            synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId());
        }
    }
    /** 工单排产量按各工序累计排产量的最小值计算,任务修改/删除后同步,避免无法补排。 */
    private void synchronizeScheduledQuantity(MesProWorkOrderDO order, Long routeId) {
        List<MesProRouteProcessDO> required = buildRouteProcessList(routeId, order.getId());
        List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(order.getId());
        BigDecimal scheduled = order.getQuantity();
        for (var process : required) {
            BigDecimal amount = tasks.stream().filter(task -> java.util.Objects.equals(task.getProcessId(), process.getProcessId())
                            && !java.util.Objects.equals(task.getStatus(), MesProTaskStatusEnum.CANCELED.getStatus()))
                    .map(task -> task.getQuantity() == null ? BigDecimal.ZERO : task.getQuantity())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            scheduled = scheduled.min(amount);
        }
        scheduleWorkOrderMapper.updateById(new MesProWorkOrderDO().setId(order.getId()).setQuantityScheduled(scheduled));
    }
    private void updateTaskAssignees(Long taskId, List<Long> assigneeUserIds) {
@@ -302,9 +350,11 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTask(Long id) {
        scheduleGuard.lock();
        // 1. 校验存在且非终态
        validateTaskNotFinished(id);
        MesProTaskDO task = validateTaskNotFinished(id);
        // 2. 校验任务下无报工记录,避免删除后报工追溯断链
        if (feedbackMapper.selectCountByTaskId(id) > 0) {
            throw exception(PRO_TASK_HAS_FEEDBACK);
@@ -313,6 +363,7 @@
        taskMapper.deleteById(id);
        // 4. 删除关联的人员
        taskAssigneeMapper.deleteByTaskId(id);
        synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId());
    }
    @Override
@@ -485,6 +536,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> autoGenerateTasks(MesProTaskAutoGenerateReqVO reqVO) {
        scheduleGuard.lock();
        // 1. 校验工单已确认
        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
        // 2. 确定工艺路线:优先请求指定 → 工单关联 → 产品关联
@@ -516,6 +568,7 @@
                    .setParamTemplateId(rp.getParamTemplateId())
                    .setClientId(workOrder.getClientId())
                    .setColorCode(rp.getColorCode());
            scheduleGuard.validate(workOrder, task, null);
            taskMapper.insert(task);
            // 参数快照:优先使用模板;无模板时回退到工序主数据绑定的工艺参数
            createTaskParamRecordWithFallback(task);