| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationMachineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationWorkerDO; |
| | | 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.mysql.pro.task.MesProTaskMapper; |
| | |
| | | import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService; |
| | | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationMachineService; |
| | | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| | | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationWorkerService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO; |
| | |
| | | import java.util.Set; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_SCHEDULE_INVALID; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_ALREADY_FINISHED; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskPauseRecordMapper; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | |
| | | /** |
| | | * MES 生产任务 Service 实现类 |
| | |
| | | @Service |
| | | @Validated |
| | | 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; |
| | |
| | | private MesDvMachineryService machineryService; |
| | | @Resource |
| | | private MesCalTeamService teamService; |
| | | @Resource |
| | | private MesMdWorkstationWorkerService workstationWorkerService; |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createTask(MesProTaskSaveReqVO createReqVO) { |
| | | scheduleGuard.lock(); |
| | | // 1. 校验关联数据存在 |
| | | MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId()); |
| | | workstationService.validateWorkstationExists(createReqVO.getWorkstationId()); |
| | | var station = workstationService.validateWorkstationExists(createReqVO.getWorkstationId()); |
| | | if (java.util.Objects.equals(workOrder.getType(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum.SELF.getType()) && !java.util.Objects.equals(station.getProcessId(), createReqVO.getProcessId())) { |
| | | throw exception(PRO_TASK_SCHEDULE_INVALID, "工作站与工序不匹配"); |
| | | } |
| | | routeService.validateRouteExists(createReqVO.getRouteId()); |
| | | processService.validateProcessExistsAndEnable(createReqVO.getProcessId()); |
| | | fillTeamInfo(createReqVO); |
| | |
| | | MesMdItemDO item = itemService.validateItemExists(itemId); |
| | | |
| | | // 3.1 构建任务 DO:自动填充客户信息 |
| | | List<Long> assigneeUserIds = createReqVO.getAssigneeUserIds(); |
| | | if (CollUtil.isEmpty(assigneeUserIds)) { |
| | | // 未指定报工人时默认取工作站配置的人员,避免一键排产生成的任务无人可报 |
| | | assigneeUserIds = getWorkstationWorkerUserIds(createReqVO.getWorkstationId()); |
| | | } |
| | | MesProTaskDO task = BeanUtils.toBean(createReqVO, MesProTaskDO.class) |
| | | .setItemId(itemId) // 确保使用计算后的 itemId |
| | | .setName(buildTaskName(item, createReqVO.getQuantity())) |
| | | .setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_TASK_CODE.getCode())) |
| | | .setAssigneeUserIds(joinUserIds(assigneeUserIds)) |
| | | .setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | if (task.getClientId() == null && workOrder.getClientId() != null) { |
| | | task.setClientId(workOrder.getClientId()); |
| | | } |
| | | // 2.2 插入 |
| | | scheduleGuard.validate(workOrder, task, null); |
| | | freezeTaskParamConfig(task); |
| | | taskMapper.insert(task); |
| | | synchronizeScheduledQuantity(workOrder, task.getRouteId()); |
| | | return task.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateTask(MesProTaskSaveReqVO updateReqVO) { |
| | | scheduleGuard.lock(); |
| | | MesProTaskDO task = taskMapper.selectOne(new LambdaQueryWrapperX<MesProTaskDO>() |
| | | .eq(MesProTaskDO::getId, updateReqVO.getId()).last("FOR UPDATE")); |
| | | if (task == null) throw exception(PRO_TASK_NOT_EXISTS); |
| | |
| | | } |
| | | BigDecimal quantity = updateReqVO.getQuantity() != null ? updateReqVO.getQuantity() : task.getQuantity(); |
| | | |
| | | MesProTaskDO candidate = BeanUtils.toBean(task, MesProTaskDO.class) |
| | | .setWorkOrderId(updateReqVO.getWorkOrderId() != null ? updateReqVO.getWorkOrderId() : task.getWorkOrderId()) |
| | | .setRouteId(updateReqVO.getRouteId() != null ? updateReqVO.getRouteId() : task.getRouteId()) |
| | | .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.validateWorkstationExists(candidate.getWorkstationId()); |
| | | if (java.util.Objects.equals(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()).getType(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum.SELF.getType()) && !java.util.Objects.equals(station.getProcessId(), candidate.getProcessId())) { |
| | | throw exception(PRO_TASK_SCHEDULE_INVALID, "工作站与工序不匹配"); |
| | | } |
| | | scheduleGuard.validate(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()), candidate, task.getId()); |
| | | // 2. 更新 |
| | | MesProTaskDO updateObj = BeanUtils.toBean(updateReqVO, MesProTaskDO.class) |
| | | .setName(buildTaskName(item, quantity)); |
| | | // 报工人为 null 表示不修改;空集合表示清空限制(所有人可报工) |
| | | if (updateReqVO.getAssigneeUserIds() != null) { |
| | | updateObj.setAssigneeUserIds(joinUserIds(updateReqVO.getAssigneeUserIds())); |
| | | } |
| | | if (updateReqVO.getProcessId() != null && !updateReqVO.getProcessId().equals(task.getProcessId())) { |
| | | if (feedbackMapper.selectCountByTaskId(task.getId()) > 0) throw exception(PRO_TASK_HAS_FEEDBACK); |
| | | freezeTaskParamConfig(updateObj); |
| | | } |
| | | taskMapper.updateById(updateObj); |
| | | synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()), candidate.getRouteId()); |
| | | if (!java.util.Objects.equals(task.getWorkOrderId(), candidate.getWorkOrderId())) synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId()); |
| | | } |
| | | |
| | | private void synchronizeScheduledQuantity(MesProWorkOrderDO order, Long routeId) { |
| | | if (!java.util.Objects.equals(order.getType(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum.SELF.getType()) || order.getQuantity() == null || routeId == null) return; |
| | | List<MesProRouteProcessDO> required = buildRouteProcessList(routeId, order.getId()); |
| | | List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(order.getId()); |
| | | BigDecimal scheduled = required.isEmpty() ? BigDecimal.ZERO : 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)); |
| | | } |
| | | |
| | | /** 从工序和模板复制定义,后续模板维护不改变已排产任务。 */ |
| | |
| | | if (team != null) { |
| | | reqVO.setTeamName(team.getName()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获得工作站配置岗位对应的员工编号 |
| | | */ |
| | | private List<Long> getWorkstationWorkerUserIds(Long workstationId) { |
| | | if (workstationId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<MesMdWorkstationWorkerDO> workers = workstationWorkerService |
| | | .getWorkstationWorkerListByWorkstationId(workstationId); |
| | | if (CollUtil.isEmpty(workers)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Set<Long> postIds = convertSet(workers, MesMdWorkstationWorkerDO::getPostId); |
| | | if (CollUtil.isEmpty(postIds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return convertList(adminUserApi.getUserListByPostIds(postIds), AdminUserRespDTO::getId); |
| | | } |
| | | |
| | | /** |
| | | * 将报工人用户编号列表拼接为逗号分隔字符串 |
| | | * |
| | | * @return null 表示不修改/不限制;空字符串表示清空限制 |
| | | */ |
| | | private String joinUserIds(List<Long> userIds) { |
| | | if (userIds == null) { |
| | | return null; |
| | | } |
| | | return userIds.stream().filter(java.util.Objects::nonNull).distinct() |
| | | .map(String::valueOf).collect(java.util.stream.Collectors.joining(",")); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteTask(Long id) { |
| | | // 1. 校验存在且非终态 |
| | | validateTaskNotFinished(id); |
| | | scheduleGuard.lock(); |
| | | MesProTaskDO task = validateTaskNotFinished(id); |
| | | // 2. 校验任务下无报工记录,避免删除后报工追溯断链 |
| | | if (feedbackMapper.selectCountByTaskId(id) > 0) { |
| | | throw exception(PRO_TASK_HAS_FEEDBACK); |
| | | } |
| | | // 3. 删除 |
| | | taskMapper.deleteById(id); |
| | | synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | @Override |
| | | public Map<Long, Long> getPendingFeedbackCountByWorkstation() { |
| | | List<Map<String, Object>> rows = taskMapper.selectPendingFeedbackCountByWorkstation(); |
| | | public Map<Long, Long> getPendingFeedbackCountByWorkstation(Long assigneeUserId) { |
| | | List<Map<String, Object>> rows = taskMapper.selectPendingFeedbackCountByWorkstation(assigneeUserId); |
| | | Map<Long, Long> result = new HashMap<>(rows.size()); |
| | | for (Map<String, Object> row : rows) { |
| | | Object workstationId = row.get("workstation_id"); |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public List<Long> autoGenerateTasks(MesProTaskAutoGenerateReqVO reqVO) { |
| | | scheduleGuard.lock(); |
| | | // 1. 校验工单已确认 |
| | | MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId()); |
| | | // 2. 确定工艺路线:优先请求指定 → 工单关联 → 产品关联 |
| | |
| | | .setStatus(MesProTaskStatusEnum.PREPARE.getStatus()) |
| | | .setClientId(workOrder.getClientId()) |
| | | .setColorCode(rp.getColorCode()); |
| | | scheduleGuard.validate(workOrder, task, null); |
| | | freezeTaskParamConfig(task); |
| | | taskMapper.insert(task); |
| | | taskIds.add(task.getId()); |