| | |
| | | |
| | | @Override |
| | | public IPage<ProductionOrderVo> pageProductionOrder(Page<ProductionOrderDto> page, ProductionOrderDto dto) { |
| | | // 分页查询生产订单 |
| | | Page<ProductionOrderVo> result = (Page<ProductionOrderVo>) baseMapper.pageProductionOrder(page, dto); |
| | | fillProductImages(result.getRecords()); |
| | | return result; |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOrderVo> listProductionOrder(ProductionOrderDto dto) { |
| | | // 查询生产订单列表 |
| | | List<ProductionOrderVo> records = baseMapper.listProductionOrder(dto); |
| | | fillProductImages(records); |
| | | return records; |
| | |
| | | |
| | | @Override |
| | | public ProductionOrderVo getProductionOrderInfo(Long id) { |
| | | // 获取生产订单详情 |
| | | ProductionOrderVo item = baseMapper.getProductionOrderInfo(id); |
| | | if (item == null) { |
| | | return null; |
| | |
| | | |
| | | @Override |
| | | public boolean saveProductionOrder(ProductionOrder productionOrder) { |
| | | // 保存生产订单 |
| | | ProductionOrder oldOrder = productionOrder.getId() == null ? null : this.getById(productionOrder.getId()); |
| | | // 下单入口统一补齐来源单据、计划和工艺信息,避免前端分别传多套字段。 |
| | | validateAndFillOrder(productionOrder, oldOrder); |
| | |
| | | |
| | | @Override |
| | | public boolean removeProductionOrder(List<Long> ids) { |
| | | // 删除生产订单 |
| | | if (ids == null || ids.isEmpty()) { |
| | | return false; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public Integer bindingRoute(ProductionOrderDto productionOrderDto) { |
| | | // 为订单绑定工艺路线 |
| | | if (productionOrderDto == null || productionOrderDto.getId() == null) { |
| | | throw new ServiceException("生产订单ID不能为空"); |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<ProductionPlanVo> getSource(Long id) { |
| | | // 查询订单关联来源计划 |
| | | ProductionOrder productionOrder = baseMapper.selectById(id); |
| | | if (productionOrder != null && productionOrder.getProductionPlanIds() != null) { |
| | | List<Long> planIds = parsePlanIds(productionOrder.getProductionPlanIds()); |
| | |
| | | |
| | | @Override |
| | | public int syncProductionOrderSnapshot(Long productionOrderId) { |
| | | // 同步订单工艺、工序、参数和BOM快照 |
| | | ProductionOrder productionOrder = this.getById(productionOrderId); |
| | | // 参数与前置条件校验 |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("生产订单不存在"); |
| | | } |
| | |
| | | clearProductionSnapshot(productionOrderId); |
| | | ProductionOrderBom orderBom = syncProductionOrderBomSnapshot(productionOrder, technologyRouting); |
| | | |
| | | //生产订单工艺路线表 |
| | | ProductionOrderRouting orderRouting = new ProductionOrderRouting(); |
| | | orderRouting.setProductionOrderId(productionOrder.getId()); |
| | | orderRouting.setTechnologyRoutingId(technologyRouting.getId()); |
| | |
| | | productionOrderRoutingMapper.insert(orderRouting); |
| | | |
| | | int syncedParamCount = 0; |
| | | // 查询并准备业务数据 |
| | | List<TechnologyRoutingOperation> routingOperations = technologyRoutingOperationMapper.selectList( |
| | | Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()) |
| | | .orderByDesc(TechnologyRoutingOperation::getDragSort) |
| | | .orderByDesc(TechnologyRoutingOperation::getId)); |
| | | Map<String, BigDecimal> operationDemandedQuantityMap = buildOperationDemandedQuantityMap(technologyRouting, productionOrder); |
| | | Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds( |
| | | // 遍历处理数据并组装结果 |
| | | routingOperations.stream() |
| | | .map(TechnologyRoutingOperation::getTechnologyOperationId) |
| | | .filter(Objects::nonNull) |
| | |
| | | targetOperation.setIsQuality(sourceOperation.getIsQuality()); |
| | | targetOperation.setOperationName(operationNameMap.get(sourceOperation.getTechnologyOperationId())); |
| | | targetOperation.setTechnologyOperationId(sourceOperation.getTechnologyOperationId()); |
| | | targetOperation.setType(sourceOperation.getType()); |
| | | productionOrderRoutingOperationMapper.insert(targetOperation); |
| | | |
| | | boolean isLastOperation = lastDragSort != null && Objects.equals(sourceOperation.getDragSort(), lastDragSort); |
| | |
| | | ProductionOperationTask task = new ProductionOperationTask(); |
| | | task.setProductionOrderRoutingOperationId(targetOperation.getId()); |
| | | task.setProductionOrderId(productionOrder.getId()); |
| | | task.setPlanQuantity(defaultDecimal(productionOrder.getQuantity())); |
| | | task.setPlanQuantity(resolveTaskPlanQuantity(sourceOperation, operationDemandedQuantityMap, productionOrder)); |
| | | task.setCompleteQuantity(BigDecimal.ZERO); |
| | | task.setWorkOrderNo(generateNextTaskNo()); |
| | | task.setStatus(2); |
| | |
| | | return syncedParamCount; |
| | | } |
| | | |
| | | private Map<String, BigDecimal> buildOperationDemandedQuantityMap(TechnologyRouting technologyRouting, |
| | | ProductionOrder productionOrder) { |
| | | if (technologyRouting == null || technologyRouting.getBomId() == null) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | BigDecimal orderQuantity = defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); |
| | | List<TechnologyBomStructure> bomStructures = technologyBomStructureMapper.selectList( |
| | | Wrappers.<TechnologyBomStructure>lambdaQuery() |
| | | .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId()) |
| | | .isNotNull(TechnologyBomStructure::getOperationId) |
| | | .orderByAsc(TechnologyBomStructure::getId)); |
| | | if (bomStructures.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | |
| | | Map<Long, TechnologyBomStructure> structureById = bomStructures.stream() |
| | | .filter(item -> item != null && item.getId() != null) |
| | | .collect(Collectors.toMap(TechnologyBomStructure::getId, item -> item, (left, right) -> left)); |
| | | Map<String, BigDecimal> demandedQuantityMap = new HashMap<>(); |
| | | for (TechnologyBomStructure bomStructure : bomStructures) { |
| | | if (bomStructure == null || bomStructure.getOperationId() == null) { |
| | | continue; |
| | | } |
| | | BigDecimal unitQuantity = bomStructure.getUnitQuantity(); |
| | | if (unitQuantity == null) { |
| | | continue; |
| | | } |
| | | Long outputProductModelId = resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId()); |
| | | String key = buildOperationDemandedQuantityKey(bomStructure.getOperationId(), outputProductModelId); |
| | | demandedQuantityMap.merge(key, unitQuantity.multiply(orderQuantity), BigDecimal::add); |
| | | } |
| | | return demandedQuantityMap; |
| | | } |
| | | |
| | | private BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation, |
| | | Map<String, BigDecimal> operationDemandedQuantityMap, |
| | | ProductionOrder productionOrder) { |
| | | if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) { |
| | | return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); |
| | | } |
| | | String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), sourceOperation.getProductModelId()); |
| | | BigDecimal planQuantity = operationDemandedQuantityMap.get(key); |
| | | return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); |
| | | } |
| | | |
| | | private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) { |
| | | return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); |
| | | } |
| | | |
| | | private Long resolveOutputProductModelId(TechnologyBomStructure bomStructure, |
| | | Map<Long, TechnologyBomStructure> structureById, |
| | | Long routingProductModelId) { |
| | | if (bomStructure == null) { |
| | | return routingProductModelId; |
| | | } |
| | | Long parentId = bomStructure.getParentId(); |
| | | if (parentId == null) { |
| | | return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId(); |
| | | } |
| | | TechnologyBomStructure parent = structureById.get(parentId); |
| | | if (parent != null && parent.getProductModelId() != null) { |
| | | return parent.getProductModelId(); |
| | | } |
| | | return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId(); |
| | | } |
| | | |
| | | private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) { |
| | | // 同步订单BOM快照结构 |
| | | if (technologyRouting.getBomId() == null) { |
| | | return null; |
| | | } |
| | |
| | | if (technologyBom == null) { |
| | | throw new ServiceException("工艺BOM不存在"); |
| | | } |
| | | // 查询并准备业务数据 |
| | | List<TechnologyBomStructure> structureList = technologyBomStructureMapper.selectList( |
| | | Wrappers.<TechnologyBomStructure>lambdaQuery() |
| | | .eq(TechnologyBomStructure::getBomId, technologyBom.getId()) |
| | | .orderByAsc(TechnologyBomStructure::getId)); |
| | | // 遍历处理数据并组装结果 |
| | | TechnologyBomStructure root = structureList.stream().filter(item -> item.getParentId() == null).findFirst().orElse(null); |
| | | BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity()); |
| | | |
| | |
| | | orderBom.setRemark(technologyBom.getRemark()); |
| | | orderBom.setBomNo(technologyBom.getBomNo()); |
| | | orderBom.setVersion(technologyBom.getVersion()); |
| | | // 持久化或输出处理结果 |
| | | productionOrderBomMapper.insert(orderBom); |
| | | |
| | | Map<Long, Long> idMap = new HashMap<>(); |
| | |
| | | } |
| | | |
| | | private void clearProductionSnapshot(Long productionOrderId) { |
| | | // 已产生领料记录后禁止重建,避免备料/投料依据与订单快照脱节。 |
| | | // 清理订单已生成的工艺与BOM快照数据 |
| | | boolean hasPickRecord = productionOrderPickRecordMapper.selectCount( |
| | | // 查询并准备业务数据 |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId)) > 0; |
| | | // 参数与前置条件校验 |
| | | if (hasPickRecord) { |
| | | throw new ServiceException("生产订单已存在领料记录,不能重新生成快照"); |
| | | } |
| | | List<Long> taskIds = productionOperationTaskMapper.selectList( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)) |
| | | // 遍历处理数据并组装结果 |
| | | .stream().map(ProductionOperationTask::getId).collect(Collectors.toList()); |
| | | if (!taskIds.isEmpty()) { |
| | | // 已有报工记录说明订单已开工,此时不允许再重建快照。 |
| | |
| | | } |
| | | |
| | | private LambdaQueryWrapper<ProductionOrder> buildQueryWrapper(ProductionOrderDto dto) { |
| | | // 按条件动态构建数据库查询条件 |
| | | ProductionOrder query = dto == null ? new ProductionOrder() : dto; |
| | | return Wrappers.<ProductionOrder>lambdaQuery() |
| | | .eq(query.getId() != null, ProductionOrder::getId, query.getId()) |
| | |
| | | } |
| | | |
| | | private String generateNextOrderNo() { |
| | | // 生成下一个生产订单号 |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String prefix = "SC" + datePrefix; |
| | | ProductionOrder latestOrder = this.getOne(Wrappers.<ProductionOrder>lambdaQuery() |
| | |
| | | } |
| | | |
| | | private String generateNextTaskNo() { |
| | | // 生成下一个生产工单号 |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String prefix = "GD" + datePrefix; |
| | | ProductionOperationTask lastTask = productionOperationTaskMapper.selectOne( |
| | |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | // 将空数量兜底为0,避免空指针异常 |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private void validateAndFillOrder(ProductionOrder productionOrder, ProductionOrder oldOrder) { |
| | | // 校验订单参数并补齐默认值 |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("生产订单不能为空"); |
| | | } |
| | |
| | | } |
| | | |
| | | private void fillFromProductionPlans(ProductionOrder productionOrder) { |
| | | // 从关联生产计划回填订单关键字段 |
| | | List<Long> planIds = parsePlanIds(productionOrder.getProductionPlanIds()); |
| | | // 参数与前置条件校验 |
| | | if (planIds.isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | if (productionPlans.size() != planIds.size()) { |
| | | throw new ServiceException("部分生产计划不存在"); |
| | | } |
| | | // 遍历处理数据并组装结果 |
| | | Map<Long, ProductionPlan> planMap = productionPlans.stream() |
| | | .collect(Collectors.toMap(ProductionPlan::getId, item -> item, (left, right) -> left)); |
| | | ProductionPlan mainPlan = planMap.get(planIds.get(0)); |
| | |
| | | } |
| | | |
| | | private void releaseProductionPlanIssueStatus(ProductionOrder productionOrder) { |
| | | // 回退生产计划下发状态 |
| | | if (productionOrder == null) { |
| | | return; |
| | | } |
| | |
| | | |
| | | //生产订单删除,生产计划的已下发数量对应变更 |
| | | private void updatePlanIssuedFlag(List<Long> planIds, BigDecimal remainingAssignedQuantity) { |
| | | // 更新计划下发标记和下发数量 |
| | | if (planIds == null || planIds.isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | |
| | | private BigDecimal resolveRemainingQuantity(ProductionPlan plan) { |
| | | // 计算当前计划或记录的剩余数量 |
| | | if (plan == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | |
| | | } |
| | | |
| | | private int resolvePlanStatus(BigDecimal requiredQuantity, BigDecimal issuedQuantity) { |
| | | // 根据需求量和下发量推导计划状态 |
| | | if (requiredQuantity == null || requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return 0; |
| | | } |
| | |
| | | } |
| | | |
| | | private List<Long> parsePlanIds(String productionPlanIds) { |
| | | // 将计划ID字符串解析为Long列表 |
| | | if (productionPlanIds == null || productionPlanIds.trim().isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | |
| | | } |
| | | |
| | | private String formatPlanIds(List<Long> planIds) { |
| | | // 将计划ID集合格式化为[1,2,3]字符串 |
| | | if (planIds == null || planIds.isEmpty()) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private LocalDate resolvePlanCompleteDate(ProductionPlan productionPlan) { |
| | | // 解析计划完成日期 |
| | | if (productionPlan == null) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private int compareDecimal(BigDecimal left, BigDecimal right) { |
| | | // 安全比较两个数量值大小 |
| | | return defaultDecimal(left).compareTo(defaultDecimal(right)); |
| | | } |
| | | |
| | | private void fillProductImages(List<ProductionOrderVo> records) { |
| | | // 填充产品图片 |
| | | if (records == null || records.isEmpty()) { |
| | | return; |
| | | } |
| | | // 遍历处理数据并组装结果 |
| | | List<Long> productModelIds = records.stream() |
| | | .map(ProductionOrderVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | |
| | | return; |
| | | } |
| | | |
| | | // 查询并准备业务数据 |
| | | List<StorageAttachment> attachments = storageAttachmentMapper.selectList( |
| | | Wrappers.<StorageAttachment>lambdaQuery() |
| | | .in(StorageAttachment::getRecordId, productModelIds) |
| | |
| | | } |
| | | |
| | | private StorageBlobVO toStorageBlobVO(StorageBlob blob) { |
| | | // 将存储文件对象转换为VO |
| | | StorageBlobVO vo = BeanUtil.copyProperties(blob, StorageBlobVO.class); |
| | | vo.setPreviewURL(fileUtil.buildSignedPreviewUrl(vo)); |
| | | vo.setDownloadURL(fileUtil.buildSignedDownloadUrl(vo)); |
| | |
| | | |
| | | @Override |
| | | public ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(ProductionOrderDto dto) { |
| | | // 获取工单订单报工质检明细 |
| | | Long productionOrderId = resolveProductionOrderId(dto); |
| | | ProductionOrderVo orderInfo = getProductionOrderInfo(productionOrderId); |
| | | // 参数与前置条件校验 |
| | | if (orderInfo == null) { |
| | | throw new ServiceException("生产订单不存在"); |
| | | } |
| | |
| | | new Page<ProductionOperationTaskVo>(1, -1), taskQuery); |
| | | List<ProductionOperationTaskVo> workOrderList = workOrderPage == null || workOrderPage.getRecords() == null |
| | | ? Collections.emptyList() |
| | | // 遍历处理数据并组装结果 |
| | | : workOrderPage.getRecords().stream() |
| | | .filter(Objects::nonNull) |
| | | .sorted(Comparator.comparing(ProductionOperationTaskVo::getId, Comparator.nullsLast(Comparator.naturalOrder()))) |
| | | .collect(Collectors.toList()); |
| | | if (workOrderList == null || workOrderList.isEmpty()) { |
| | | .toList(); |
| | | if (workOrderList.isEmpty()) { |
| | | detailVo.setWorkOrderList(Collections.emptyList()); |
| | | return detailVo; |
| | | } |
| | |
| | | .collect(Collectors.toList()); |
| | | List<ProductionProductMain> reportMainList = workOrderIdList.isEmpty() |
| | | ? Collections.emptyList() |
| | | // 查询并准备业务数据 |
| | | : productionProductMainMapper.selectList( |
| | | Wrappers.<ProductionProductMain>lambdaQuery() |
| | | .in(ProductionProductMain::getProductionOperationTaskId, workOrderIdList) |
| | |
| | | if (reportOutput == null) { |
| | | continue; |
| | | } |
| | | Long reportMainId = reportOutput.getProductionProductMainId() != null |
| | | ? reportOutput.getProductionProductMainId() |
| | | : reportOutput.getProductMainId(); |
| | | if (reportMainId == null) { |
| | | if (reportOutput.getProductionProductMainId() == null) { |
| | | continue; |
| | | } |
| | | Long reportMainId = reportOutput.getProductionProductMainId(); |
| | | reportOutputMap.computeIfAbsent(reportMainId, k -> new ArrayList<>()).add(reportOutput); |
| | | } |
| | | |
| | |
| | | |
| | | ProductionOrderWorkOrderDetailVo.ReportDetail reportDetail = new ProductionOrderWorkOrderDetailVo.ReportDetail(); |
| | | reportDetail.setReportMain(reportMain); |
| | | reportDetail.setWorkHour(reportMain.getWorkHour()); |
| | | reportDetail.setReportOutputList(reportOutputMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | reportDetail.setReportParamList(reportParamMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | reportDetailList.add(reportDetail); |
| | |
| | | inspectDetail.setReportId(reportMainId); |
| | | inspectDetail.setReportNo(reportMain.getProductNo()); |
| | | inspectDetail.setReportMain(reportMain); |
| | | inspectDetail.setWorkHour(reportMain.getWorkHour()); |
| | | inspectDetail.setInspect(inspect); |
| | | inspectDetail.setInspectParamList(inspectParamMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | | inspectDetail.setInspectFileList(inspectFileMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | |
| | | } |
| | | |
| | | private Long resolveProductionOrderId(ProductionOrderDto dto) { |
| | | // 从入参中解析生产订单ID并校验 |
| | | if (dto == null) { |
| | | throw new ServiceException("请传入生产订单ID或生产订单号"); |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOrderPickVo> pick(Long productionOrderId) { |
| | | // 查询订单领料、投料与退料明细 |
| | | if (productionOrderId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 查询并准备业务数据 |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( |
| | | Wrappers.<ProductionOrderBom>lambdaQuery() |
| | | .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) |
| | |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 遍历处理数据并组装结果 |
| | | List<Long> productModelIds = bomStructureList.stream() |
| | | .map(ProductionBomStructureVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | |
| | | |
| | | @Override |
| | | public int updateOrder(ProductionOrderDto productionOrderDto) { |
| | | // 更新生产订单主数据 |
| | | productionOrderDto.setStatus(5); |
| | | return baseMapper.updateById(productionOrderDto); |
| | | } |