| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.task.schedule; |
| | | |
| | | import cn.hutool.crypto.digest.DigestUtil; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| | | import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.*; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProBatchSchedulePreviewRespVO.*; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | 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.dal.mysql.pro.workorder.MesProWorkOrderMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.*; |
| | | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.*; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; |
| | | import cn.iocoder.yudao.module.mes.service.cal.holiday.MesCalHolidayService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Isolation; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.*; |
| | | import java.util.*; |
| | | 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.*; |
| | | |
| | | @Service |
| | | public class MesProBatchScheduleService { |
| | | @Resource private MesProTaskMapper taskMapper; |
| | | @Resource private MesProWorkOrderMapper workOrderMapper; |
| | | @Resource private MesDvMachineryMapper machineryMapper; |
| | | @Resource private MesMdWorkstationService workstationService; |
| | | @Resource private MesProWorkOrderProcessService workOrderProcessService; |
| | | @Resource private MesProRouteService routeService; |
| | | @Resource private MesProRouteProcessService routeProcessService; |
| | | @Resource private MesProRouteProductService routeProductService; |
| | | @Resource private MesProProcessService processService; |
| | | @Resource private MesMdItemService itemService; |
| | | @Resource private MesCalHolidayService holidayService; |
| | | @Resource @Lazy private cn.iocoder.yudao.module.mes.service.pd.processparamtemplate.MesPdProcessParamTemplateService paramTemplateService; |
| | | @Resource private MesProScheduleGuardService guard; |
| | | @Resource private CrmCustomerApi customerApi; |
| | | @Resource @Lazy private MesProTaskService taskService; |
| | | |
| | | private LocalDateTime now() { return LocalDateTime.now(ZoneId.of("Asia/Shanghai")).withNano(0); } |
| | | |
| | | public MesProBatchSchedulePreviewRespVO preview(MesProBatchScheduleReqVO request) { |
| | | validateRequest(request); |
| | | return build(request); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class, isolation = Isolation.SERIALIZABLE, timeout = 60) |
| | | public List<Long> execute(MesProBatchScheduleReqVO request) { |
| | | validateRequest(request); |
| | | if (request.getPreviewTime() == null || request.getPreviewHash() == null |
| | | || request.getPreviewTime().isAfter(now()) || request.getPreviewTime().plusMinutes(15).isBefore(now())) { |
| | | throw exception(PRO_TASK_SCHEDULE_PREVIEW_CHANGED); |
| | | } |
| | | guard.lock(); |
| | | workOrderMapper.lockScheduleOrders(request.getWorkOrderIds().stream().sorted().toList()); |
| | | var preview = build(request); |
| | | if (!preview.isCanExecute()) { |
| | | throw exception(PRO_TASK_SCHEDULE_INVALID, preview.getOrders().stream() |
| | | .filter(order -> order.getError() != null).map(OrderPlan::getError).collect(Collectors.joining("ï¼"))); |
| | | } |
| | | if (!Objects.equals(request.getPreviewHash(), preview.getPreviewHash())) { |
| | | throw exception(PRO_TASK_SCHEDULE_PREVIEW_CHANGED); |
| | | } |
| | | if (preview.getTasks().stream().anyMatch(task -> task.getStartTime().isBefore(now()))) { |
| | | throw exception(PRO_TASK_SCHEDULE_PREVIEW_CHANGED); |
| | | } |
| | | if (!preview.getWarnings().isEmpty() && !Boolean.TRUE.equals(request.getAllowLate())) { |
| | | throw exception(PRO_TASK_SCHEDULE_INVALID, "请å
确认é¢è§ä¸ç交æå»¶æé£é©"); |
| | | } |
| | | List<Long> ids = new ArrayList<>(); |
| | | for (var plan : preview.getTasks()) { |
| | | var task = new MesProTaskSaveReqVO(); |
| | | task.setWorkOrderId(plan.getWorkOrderId()); |
| | | task.setRouteId(plan.getRouteId()); |
| | | task.setProcessId(plan.getProcessId()); |
| | | task.setItemId(plan.getItemId()); |
| | | task.setParamTemplateId(plan.getParamTemplateId()); |
| | | task.setColorCode(plan.getColorCode()); |
| | | task.setWorkstationId(plan.getWorkstationId()); |
| | | task.setQuantity(plan.getQuantity()); |
| | | task.setDuration(plan.getDuration()); |
| | | task.setStartTime(plan.getStartTime()); |
| | | task.setEndTime(plan.getEndTime()); |
| | | task.setRemark("ä¸é®æäº§ï¼09:00â18:00ï¼æé¤å¨æ«åè忥"); |
| | | ids.add(taskService.createTask(task)); |
| | | } |
| | | // æ¯éå·¥åºåè¡¥é½åï¼å·¥åç累计æäº§æ°éåªè®°ä¸æ¬¡ï¼ä¸è½æå·¥åºæ°ç´¯å ã |
| | | for (var order : preview.getOrders()) { |
| | | workOrderMapper.updateById(new MesProWorkOrderDO().setId(order.getWorkOrderId()) |
| | | .setQuantityScheduled(order.getQuantity())); |
| | | } |
| | | return ids; |
| | | } |
| | | |
| | | private void validateRequest(MesProBatchScheduleReqVO request) { |
| | | var ids = request.getWorkOrderIds(); |
| | | if (ids == null || ids.isEmpty() || ids.size() > 30 || ids.stream().anyMatch(id -> id == null || id <= 0) |
| | | || new HashSet<>(ids).size() != ids.size()) { |
| | | throw exception(PRO_TASK_SCHEDULE_INVALID, "è¯·éæ©1è³30个ä¸åçææå·¥å"); |
| | | } |
| | | if (request.getStartTime() == null || request.getStartTime().isBefore(now().minusMinutes(15)) |
| | | || request.getStartTime().isAfter(now().plusDays(365))) { |
| | | throw exception(PRO_TASK_SCHEDULE_INVALID, "ææ©å¼å·¥æ¶é´åºä¸ºå½åæ¶é´è³æªæ¥ä¸å¹´ä»¥å
ï¼è¯·éæ°éæ©"); |
| | | } |
| | | } |
| | | |
| | | private MesProBatchSchedulePreviewRespVO build(MesProBatchScheduleReqVO request) { |
| | | var result = new MesProBatchSchedulePreviewRespVO(); |
| | | result.setPreviewTime(now()); |
| | | var orders = workOrderMapper.selectByIds(request.getWorkOrderIds()); |
| | | var orderMap = orders.stream().collect(Collectors.toMap(MesProWorkOrderDO::getId, order -> order)); |
| | | var stations = workstationService.getWorkstationListByStatus(0).stream() |
| | | .sorted(Comparator.comparing(MesMdWorkstationDO::getId)).toList(); |
| | | var machineMap = machineryMapper.selectList().stream().collect(Collectors.toMap(MesDvMachineryDO::getId, machine -> machine)); |
| | | var stationMachines = guard.machineIdsByStation(); |
| | | Map<Long, MesProTaskDO> allRelevantTasks = new TreeMap<>(); |
| | | taskMapper.selectSchedulingTasks().forEach(task -> allRelevantTasks.put(task.getId(), task)); |
| | | taskMapper.selectListByWorkOrderIds(request.getWorkOrderIds()).stream() |
| | | .filter(task -> !Objects.equals(task.getStatus(), MesProTaskStatusEnum.CANCELED.getStatus())) |
| | | .forEach(task -> allRelevantTasks.put(task.getId(), task)); |
| | | var existingTasks = new ArrayList<>(allRelevantTasks.values()); |
| | | var holidays = holidayService.getHolidayList(request.getStartTime().toLocalDate().atStartOfDay(), request.getStartTime().plusYears(1)); |
| | | Set<LocalDate> holidayDates = holidays.stream().filter(day -> Objects.equals(day.getType(), 2)) |
| | | .map(day -> day.getDay().toLocalDate()).collect(Collectors.toSet()); |
| | | var calendar = new MesProScheduleEngine.Calendar(holidayDates, request.getStartTime().toLocalDate().plusYears(1)); |
| | | List<MesProScheduleEngine.Busy> busy = new ArrayList<>(); |
| | | Set<Long> unknownStations = new HashSet<>(); |
| | | for (var task : existingTasks) { |
| | | if (MesProTaskStatusEnum.isEndStatus(task.getStatus()) || task.getWorkstationId() == null) continue; |
| | | if (task.getStartTime() == null || task.getEndTime() == null || !task.getEndTime().isAfter(task.getStartTime()) |
| | | || task.getEndTime().isBefore(now())) { |
| | | unknownStations.add(task.getWorkstationId()); |
| | | } else { |
| | | busy.add(new MesProScheduleEngine.Busy(task.getWorkstationId(), |
| | | stationMachines.getOrDefault(task.getWorkstationId(), Set.of()), task.getStartTime(), task.getEndTime())); |
| | | } |
| | | } |
| | | var sorted = orders.stream().sorted(Comparator.comparing(MesProWorkOrderDO::getRequestDate, |
| | | Comparator.nullsLast(Comparator.naturalOrder())).thenComparing(MesProWorkOrderDO::getId)).toList(); |
| | | List<Long> orderSequence = new ArrayList<>(); |
| | | Set<Long> visiting = new HashSet<>(); |
| | | for (var order : sorted) addChildrenFirst(order.getId(), orderMap, orderSequence, visiting); |
| | | for (Long id : request.getWorkOrderIds()) if (!orderMap.containsKey(id)) orderSequence.add(id); |
| | | Map<Long, LocalDateTime> ends = new HashMap<>(); |
| | | Map<Long, String> templateHashes = new TreeMap<>(); |
| | | final List<MesProScheduleEngine.Busy> existingBusy = List.copyOf(busy); |
| | | for (Long id : orderSequence) { |
| | | var order = orderMap.get(id); |
| | | var orderPlan = new OrderPlan(); |
| | | orderPlan.setWorkOrderId(id); |
| | | result.getOrders().add(orderPlan); |
| | | try { |
| | | if (order == null) throw new IllegalArgumentException("å·¥åä¸å卿已å é¤"); |
| | | orderPlan.setWorkOrderCode(order.getCode()); |
| | | orderPlan.setWorkOrderName(order.getName()); |
| | | orderPlan.setQuantity(order.getQuantity()); |
| | | orderPlan.setRequestDate(order.getRequestDate()); |
| | | if (!Objects.equals(order.getStatus(), MesProWorkOrderStatusEnum.CONFIRMED.getStatus()) |
| | | || !Objects.equals(order.getType(), MesProWorkOrderTypeEnum.SELF.getType())) { |
| | | throw new IllegalArgumentException("ä»
æ¯æå·²ç¡®è®¤çèªè¡ç产工å"); |
| | | } |
| | | if (order.getQuantity() == null || order.getQuantity().signum() <= 0 |
| | | || (order.getQuantityProduced() != null && order.getQuantityProduced().compareTo(order.getQuantity()) >= 0)) { |
| | | throw new IllegalArgumentException("å·¥åæ°éæ ææå·²çäº§å®æ"); |
| | | } |
| | | LocalDateTime earliest = calendar.nextWorkingTime(request.getStartTime()); |
| | | // ç¶å·¥åå¿
é¡»çåå·¥åï¼å䏿¹æ¬¡æä¾èµé¡ºåºæï¼æªçº³å
¥çæªå®æåå·¥å黿¢æäº§ã |
| | | for (var child : workOrderMapper.selectList(MesProWorkOrderDO::getParentId, id)) { |
| | | if (Objects.equals(child.getStatus(), MesProWorkOrderStatusEnum.FINISHED.getStatus())) continue; |
| | | if (!ends.containsKey(child.getId())) throw new IllegalArgumentException("è¯·å°æªå®æåå·¥å" + child.getCode() + "ä¸å¹¶éä¸å¹¶å
宿æäº§"); |
| | | if (ends.get(child.getId()).isAfter(earliest)) earliest = ends.get(child.getId()); |
| | | } |
| | | Long routeId = order.getRouteId(); |
| | | if (routeId == null) { |
| | | var relation = routeProductService.getRouteProductByItemId(order.getProductId()); |
| | | if (relation != null) routeId = relation.getRouteId(); |
| | | } |
| | | if (routeId == null) throw new IllegalArgumentException("æªé
置工èºè·¯çº¿ï¼è¯·å
ç»´æ¤å·¥åå·¥åºå产å路线"); |
| | | var route = routeService.validateRouteExists(routeId); |
| | | if (!Objects.equals(route.getStatus(), 0)) throw new IllegalArgumentException("å·¥èºè·¯çº¿å·²åç¨"); |
| | | List<MesProRouteProcessDO> processes = processes(id, routeId); |
| | | if (processes.isEmpty()) throw new IllegalArgumentException("没æå·¥åºï¼è¯·å
ç»´æ¤å·¥åå·¥åº"); |
| | | Set<Long> processIds = new HashSet<>(); |
| | | Set<Integer> sorts = new HashSet<>(); |
| | | Map<Long, Integer> processSorts = processes.stream().filter(process -> process.getProcessId() != null && process.getSort() != null) |
| | | .collect(Collectors.toMap(MesProRouteProcessDO::getProcessId, MesProRouteProcessDO::getSort, (first, duplicate) -> first)); |
| | | List<TaskPlan> plans = new ArrayList<>(); |
| | | Map<Long, BigDecimal> targetQuantities = calcTargetQuantities(processes, order.getQuantity()); |
| | | List<MesProScheduleEngine.Busy> trialBusy = new ArrayList<>(busy); |
| | | for (var process : processes) { |
| | | if (process.getProcessId() == null || process.getSort() == null || !processIds.add(process.getProcessId()) || !sorts.add(process.getSort())) { |
| | | throw new IllegalArgumentException("å·¥åºå顺åºå¿
须宿´ä¸ä¸è½éå¤ï¼è¯·å
ç»´æ¤å·¥åå·¥åº"); |
| | | } |
| | | if (process.getNextProcessId() != null && (!processSorts.containsKey(process.getNextProcessId()) |
| | | || processSorts.get(process.getNextProcessId()) <= process.getSort())) { |
| | | throw new IllegalArgumentException("å·¥åºè¡æ¥ä¸é¡ºåºä¸ä¸è´ï¼è¯·å
ç»´æ¤å·¥åå·¥åº"); |
| | | } |
| | | int prepareMinutes = process.getPrepareTime() == null ? 0 : process.getPrepareTime(); |
| | | int waitMinutes = process.getWaitTime() == null ? 0 : process.getWaitTime(); |
| | | if (prepareMinutes < 0 || waitMinutes < 0) throw new IllegalArgumentException("å·¥åºå夿¶é´åçå¾
æ¶é´ä¸è½ä¸ºè´æ°"); |
| | | if (process.getParamTemplateId() != null) { |
| | | paramTemplateService.validateProcessParamTemplateExists(process.getParamTemplateId()); |
| | | var details = paramTemplateService.getProcessParamTemplateDetailListByTemplateId(process.getParamTemplateId()); |
| | | if (details.isEmpty()) { |
| | | throw new IllegalArgumentException("å·¥åºåæ°æ¨¡æ¿æ²¡ææç»ï¼è¯·å
å®ååæ°æ¨¡æ¿"); |
| | | } |
| | | templateHashes.put(process.getParamTemplateId(), DigestUtil.sha256Hex(JsonUtils.toJsonString(details))); |
| | | } |
| | | processService.validateProcessExistsAndEnable(process.getProcessId()); |
| | | var processData = processService.getProcess(process.getProcessId()); |
| | | if (processData == null) throw new IllegalArgumentException("å·¥åºé
置已ååï¼è¯·éæ°ç»´æ¤åé¢è§"); |
| | | var ownTasks = existingTasks.stream().filter(task -> Objects.equals(task.getWorkOrderId(), id) |
| | | && Objects.equals(task.getProcessId(), process.getProcessId())).toList(); |
| | | if (ownTasks.stream().anyMatch(task -> task.getQuantity() == null || task.getQuantity().signum() <= 0)) { |
| | | throw new IllegalArgumentException("å½åå·¥åºå·²æä»»å¡æ°éæ æï¼è¯·å
ä¿®æ£åå²ä»»å¡æ°é"); |
| | | } |
| | | var assigned = ownTasks.stream().map(task -> task.getQuantity() == null ? BigDecimal.ZERO : task.getQuantity()).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | // åéå·¥åºææ¶ç忍çç®æ 产åºéï¼æ«é=订åéï¼åé¢åéé级æ¾å¤§ |
| | | var targetQuantity = targetQuantities.getOrDefault(process.getProcessId(), order.getQuantity()); |
| | | var remaining = targetQuantity.subtract(assigned); |
| | | if (remaining.signum() < 0) throw new IllegalArgumentException("å·¥åº" + processData.getName() + "å·²ææäº§æ°éè¶
éï¼è¯·å
æ ¸å¯¹"); |
| | | for (var task : ownTasks) { |
| | | if (!MesProTaskStatusEnum.isEndStatus(task.getStatus()) && (task.getStartTime() == null |
| | | || task.getEndTime() == null || !task.getEndTime().isAfter(task.getStartTime()) || task.getEndTime().isBefore(now()))) { |
| | | throw new IllegalArgumentException("å·¥åº" + processData.getName() + "å·²æä»»å¡æ¶é´ä¸å®æ´æå·²è¶
æ¶æªå®æï¼è¯·å
ç»´æ¤"); |
| | | } |
| | | } |
| | | LocalDateTime existingEnd = ownTasks.stream().map(task -> task.getEndTime() != null ? task.getEndTime() : task.getFinishDate()) |
| | | .filter(Objects::nonNull).max(Comparator.naturalOrder()).orElse(earliest); |
| | | if (remaining.signum() == 0) { |
| | | if (existingEnd.isAfter(earliest)) earliest = existingEnd; |
| | | earliest = earliest.plusMinutes(waitMinutes); |
| | | continue; |
| | | } |
| | | Long itemId = order.getProductId(); |
| | | if (process.getOutputItemId() != null && !process.getOutputItemId().isBlank()) { |
| | | String[] output = process.getOutputItemId().split(","); |
| | | if (output.length != 1) throw new IllegalArgumentException("å¤äº§åºå·¥åºè¯·ä½¿ç¨æå·¥æäº§ç¡®è®¤åäº§åºæ°é"); |
| | | itemId = Long.valueOf(output[0].trim()); |
| | | } |
| | | var item = itemService.validateItemExists(itemId); |
| | | if (!Objects.equals(item.getStatus(), 0)) throw new IllegalArgumentException("å·¥åºäº§åºäº§åå·²åç¨"); |
| | | TaskPlan best = null; |
| | | for (var station : stations) { |
| | | if (!Objects.equals(station.getProcessId(), process.getProcessId())) continue; |
| | | Set<Long> machineIds = stationMachines.getOrDefault(station.getId(), Set.of()); |
| | | if (unknownStations.contains(station.getId()) || machineIds.isEmpty()) continue; |
| | | if (unknownStations.stream().anyMatch(other -> !Collections.disjoint(machineIds, stationMachines.getOrDefault(other, Set.of())))) continue; |
| | | List<MesDvMachineryDO> machines = machineIds.stream().map(machineMap::get).filter(Objects::nonNull) |
| | | .filter(machine -> Objects.equals(machine.getStatus(), 1) || Objects.equals(machine.getStatus(), 2)).toList(); |
| | | if (machines.isEmpty() || machines.stream().anyMatch(machine -> machine.getProductionEfficiency() == null || machine.getProductionEfficiency().signum() <= 0)) continue; |
| | | Set<Long> usableIds = machines.stream().map(MesDvMachineryDO::getId).collect(Collectors.toCollection(TreeSet::new)); |
| | | if (machines.stream().anyMatch(machine -> Objects.equals(machine.getStatus(), 2) && existingBusy.stream().noneMatch(interval -> interval.machineryIds().contains(machine.getId())))) continue; |
| | | BigDecimal capacity = machines.stream().map(machine -> machine.getProductionEfficiency().multiply(BigDecimal.valueOf( |
| | | machine.getConveyorBelt() == null || machine.getConveyorBelt() <= 0 ? 1 : machine.getConveyorBelt()))) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | MesProScheduleEngine.Slot slot; |
| | | try { |
| | | int hours = Math.addExact(MesProScheduleEngine.duration(remaining, capacity), |
| | | Math.toIntExact(((long) prepareMinutes + 59) / 60)); |
| | | slot = MesProScheduleEngine.findSlotHours(station.getId(), machineIds, hours, earliest, trialBusy, calendar); |
| | | } catch (IllegalArgumentException | ArithmeticException unavailable) { |
| | | continue; // ä¸å°ä½äº§è½å·¥ä½ç«è¶
åºæäº§çªå£æ¶ï¼ä»ç»§ç»å°è¯å
¶ä»å·¥ä½ç«ã |
| | | } |
| | | if (best == null || slot.end().isBefore(best.getEndTime())) { |
| | | best = new TaskPlan(); |
| | | best.setWorkOrderId(id); best.setWorkOrderCode(order.getCode()); |
| | | best.setRouteId(routeId); best.setRouteName(route.getName()); |
| | | best.setProcessId(process.getProcessId()); best.setProcessName(processData.getName()); best.setSort(process.getSort()); |
| | | best.setItemId(itemId); best.setItemName(item.getName()); best.setParamTemplateId(process.getParamTemplateId()); best.setColorCode(process.getColorCode()); |
| | | best.setWorkstationId(station.getId()); best.setWorkstationName(station.getName()); |
| | | best.setMachineryIds(new ArrayList<>(usableIds)); best.setMachineryNames(machines.stream().map(MesDvMachineryDO::getName).collect(Collectors.joining("ã"))); |
| | | best.setHourlyCapacity(capacity); best.setQuantity(remaining); best.setDuration(slot.hours()); best.setStartTime(slot.start()); best.setEndTime(slot.end()); |
| | | } |
| | | } |
| | | if (best == null) throw new IllegalArgumentException("å·¥åº" + processData.getName() + "没æå¯ç¨å·¥ä½ç«åææè®¾å¤äº§è½ï¼æèµæºå卿ªç¥/è¶
æ¶ä»»å¡ï¼è¯·å
ç»´æ¤"); |
| | | plans.add(best); |
| | | trialBusy.add(new MesProScheduleEngine.Busy(best.getWorkstationId(), stationMachines.getOrDefault(best.getWorkstationId(), Set.of()), best.getStartTime(), best.getEndTime())); |
| | | earliest = best.getEndTime().isAfter(existingEnd) ? best.getEndTime() : existingEnd; |
| | | earliest = earliest.plusMinutes(waitMinutes); |
| | | } |
| | | if (plans.isEmpty()) throw new IllegalArgumentException("ææå·¥åºå·²å®ææäº§ï¼ä¸å¯éå¤æäº¤"); |
| | | if (result.getTasks().size() + plans.size() > 200) throw new IllegalArgumentException("䏿¬¡æå¤çæ200个任å¡ï¼è¯·æåå·¥åæ¹æ¬¡æäº§"); |
| | | result.getTasks().addAll(plans); busy = trialBusy; |
| | | orderPlan.setTaskCount(plans.size()); orderPlan.setEndTime(earliest); ends.put(id, earliest); |
| | | if (order.getRequestDate() != null && earliest.toLocalDate().isAfter(order.getRequestDate().toLocalDate())) { |
| | | orderPlan.setLate(true); result.getWarnings().add("å·¥å" + order.getCode() + "é¢è®¡å®ææ¶é´æäºäº¤æ"); |
| | | } |
| | | } catch (ServiceException | IllegalArgumentException | ArithmeticException error) { |
| | | orderPlan.setError((orderPlan.getWorkOrderCode() == null ? "å·¥å#" + id : orderPlan.getWorkOrderCode()) + "ï¼" + error.getMessage()); |
| | | } |
| | | } |
| | | result.setCanExecute(result.getOrders().stream().noneMatch(order -> order.getError() != null) && !result.getTasks().isEmpty()); |
| | | // ä¸å
å«çææ¶é´ï¼åä¸è¾å
¥ä¸ä¸å¡æ°æ®åºå¾å°ç¨³å®çé¢è§æçº¹ã |
| | | var fingerprint = new LinkedHashMap<String, Object>(); |
| | | fingerprint.put("startTime", request.getStartTime()); fingerprint.put("orders", result.getOrders()); |
| | | fingerprint.put("tasks", result.getTasks()); fingerprint.put("holidays", holidayDates.stream().sorted().toList()); |
| | | fingerprint.put("workOrders", orders.stream().sorted(Comparator.comparing(MesProWorkOrderDO::getId)).toList()); |
| | | fingerprint.put("paramTemplates", templateHashes); |
| | | result.setPreviewHash(DigestUtil.sha256Hex(JsonUtils.toJsonString(fingerprint))); |
| | | return result; |
| | | } |
| | | |
| | | private List<MesProRouteProcessDO> processes(Long id, Long routeId) { |
| | | var saved = workOrderProcessService.getWorkOrderProcessListByWorkOrderId(id); |
| | | List<MesProRouteProcessDO> result = new ArrayList<>(); |
| | | if (!saved.isEmpty()) { |
| | | for (var row : saved) result.add(new MesProRouteProcessDO().setProcessId(row.getProcessId()).setSort(row.getSort()) |
| | | .setParamTemplateId(row.getParamTemplateId()).setYieldRate(row.getYieldRate()).setOutputItemId(row.getOutputItemId()).setColorCode(row.getColorCode()) |
| | | .setPrepareTime(row.getPrepareTime()).setWaitTime(row.getWaitTime()).setNextProcessId(row.getNextProcessId()).setLinkType(row.getLinkType())); |
| | | } else result.addAll(routeProcessService.getRouteProcessListByRouteId(routeId)); |
| | | result.sort(Comparator.comparing(MesProRouteProcessDO::getSort, Comparator.nullsLast(Comparator.naturalOrder()))); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * æå·¥åºæ¶ç仿«éå¾å忍åéå·¥åºçç®æ äº§åºæ°éï¼æ«é=订åéï¼åé¢åéé¤ä»¥åæ¹å·¥åºç累计æ¶çååä¸åæ´ï¼ |
| | | * æ¶ç为空æ 1.0 å¤çã |
| | | */ |
| | | private Map<Long, BigDecimal> calcTargetQuantities(List<MesProRouteProcessDO> processes, BigDecimal orderQuantity) { |
| | | return MesProScheduleQuantityService.targetQuantities(processes, orderQuantity); |
| | | } |
| | | |
| | | private void addChildrenFirst(Long id, Map<Long, MesProWorkOrderDO> orders, List<Long> sequence, Set<Long> visiting) { |
| | | if (sequence.contains(id)) return; |
| | | if (!visiting.add(id)) throw exception(PRO_TASK_SCHEDULE_INVALID, "å·¥åç¶åå
³ç³»åå¨å¾ªç¯ï¼è¯·å
ä¿®å¤"); |
| | | orders.values().stream().filter(order -> Objects.equals(order.getParentId(), id)).sorted(Comparator.comparing(MesProWorkOrderDO::getId)) |
| | | .forEach(order -> addChildrenFirst(order.getId(), orders, sequence, visiting)); |
| | | visiting.remove(id); sequence.add(id); |
| | | } |
| | | } |