From 428011a7809a77ef951bd89c069d6a1df0f89321 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Thu, 25 Sep 2025 14:57:23 +0800 Subject: [PATCH] =?UTF-8?q?change(mes):=E4=BC=98=E5=8C=96=E5=9C=A8?= =?UTF-8?q?=E5=88=B6=E5=93=81=E8=B7=9F=E8=B8=AA=E6=8A=A5=E8=A1=A8=E6=80=A7?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 批量获取工序进度信息,避免 N+1 查询问题,显著提升报表加载性能 -重构工序进度查询逻辑,支持多工序表并行查询与降级处理 - 新增报表导出 VO 转换逻辑,保持导出数据与页面展示一致 - 微调工序进度计算逻辑,增强数据准确性与可读性 - 使用通配符导入简化 VO 类引入,统一包级访问控制 --- .../mes/service/IProdReportService.java | 18 +- .../service/impl/ProdReportServiceImpl.java | 390 ++++++++++++++---- 2 files changed, 326 insertions(+), 82 deletions(-) diff --git a/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/IProdReportService.java b/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/IProdReportService.java index eb77c82a..0261d072 100644 --- a/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/IProdReportService.java +++ b/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/IProdReportService.java @@ -3,12 +3,7 @@ package org.dromara.mes.service; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; -import org.dromara.mes.domain.vo.PlanCompletionContrastReportVo; -import org.dromara.mes.domain.vo.PlanCompletionRateReportVo; -import org.dromara.mes.domain.vo.WorkHourReportVo; -import org.dromara.mes.domain.vo.TeamWorkReportVo; -import org.dromara.mes.domain.vo.WipTrackingReportVo; -import org.dromara.mes.domain.vo.ProcessProgressVo; +import org.dromara.mes.domain.vo.*; import java.util.HashMap; import java.util.List; @@ -125,6 +120,13 @@ public interface IProdReportService { * @param productOrderId 产品订单ID * @return 工序进度列表 */ - List getOrderProcessProgress(Long productOrderId); - +// List getOrderProcessProgress(Long productOrderId); + + /** + * 转换为导出VO + * @param reportList 在制品跟踪报表列表 + * @return 导出VO列表 + */ + List convertToExportVo(List reportList); + } diff --git a/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/impl/ProdReportServiceImpl.java b/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/impl/ProdReportServiceImpl.java index 0e6adddb..6432474f 100644 --- a/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/impl/ProdReportServiceImpl.java +++ b/ruoyi-modules/hwmom-mes/src/main/java/org/dromara/mes/service/impl/ProdReportServiceImpl.java @@ -6,23 +6,18 @@ import org.dromara.common.constant.DatabaseConstants; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; -import org.dromara.mes.domain.vo.PlanCompletionContrastReportVo; -import org.dromara.mes.domain.vo.PlanCompletionRateReportVo; -import org.dromara.mes.domain.vo.WorkHourReportVo; -import org.dromara.mes.domain.vo.TeamWorkReportVo; -import org.dromara.mes.domain.vo.WipTrackingReportVo; -import org.dromara.mes.domain.vo.ProcessProgressVo; -import org.dromara.mes.domain.vo.ProdBaseRouteProcessVo; import org.dromara.mes.domain.bo.ProdBaseRouteProcessBo; -import org.dromara.mes.mapper.ProdReportMapper; +import org.dromara.mes.domain.vo.*; import org.dromara.mes.mapper.ProdOrderInfoMapper; -import org.dromara.mes.service.IProdReportService; +import org.dromara.mes.mapper.ProdReportMapper; import org.dromara.mes.service.IProdBaseRouteProcessService; +import org.dromara.mes.service.IProdReportService; import org.springframework.stereotype.Service; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.math.BigDecimal; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; /** * MES生产报表Service业务层处理 @@ -192,98 +187,221 @@ public class ProdReportServiceImpl implements IProdReportService { } /** - * 在制品跟踪报表(分页) + * 在制品跟踪报表(分页)- 高性能优化版本 * @param hashMap 查询参数 * @param pageQuery 分页参数 * @return 表格数据 */ @Override public TableDataInfo wipTrackingReportList(Map hashMap, PageQuery pageQuery) { + // 使用优化后的SQL查询,大部分计算已在数据库层完成 Page page = prodReportMapper.wipTrackingReportList(hashMap, pageQuery.build()); - // 为每个订单获取工序进度详情 - for (WipTrackingReportVo vo : page.getRecords()) { - if (vo.getProductOrderId() != null) { - List processProgress = getAllOrderProcessProgress(vo.getProductOrderId()); - vo.setProcessProgressList(processProgress); - } - } + + // 批量获取工序进度信息以提升性能 + enrichWithProcessProgressBatch(page.getRecords()); + return TableDataInfo.build(page); } /** - * 在制品跟踪报表(导出) + * 在制品跟踪报表(导出)- 高性能优化版本 * @param hashMap 查询参数 * @return 列表数据 */ @Override public List wipTrackingReportList(Map hashMap) { + // 使用优化后的SQL查询,大部分计算已在数据库层完成 List list = prodReportMapper.wipTrackingReportList(hashMap); - // 为每个订单获取工序进度详情 - for (WipTrackingReportVo vo : list) { - if (vo.getProductOrderId() != null) { - List processProgress = getAllOrderProcessProgress(vo.getProductOrderId()); - vo.setProcessProgressList(processProgress); - } - } + + // 批量获取工序进度信息以提升性能 + enrichWithProcessProgressBatch(list); + return list; } /** - * 获取订单的工序进度详情 - * @param productOrderId 产品订单ID - * @return 工序进度列表 + * 批量获取并填充工序进度信息 - 高性能版本 + * @param reportList 在制品跟踪报表列表 */ - @Override - public List getOrderProcessProgress(Long productOrderId) { - // 默认查询半制品表 - String planTableName = getPlanInfoTableNameByProcessId(null); - return prodReportMapper.getOrderProcessProgress(productOrderId, planTableName); + private void enrichWithProcessProgressBatch(List reportList) { + if (reportList == null || reportList.isEmpty()) { + return; + } + + // 批量获取所有订单的工序进度信息 + Map> orderProcessMap = new HashMap<>(); + + try { + // 收集所有需要查询的订单ID + List orderIds = reportList.stream() + .map(WipTrackingReportVo::getProductOrderId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + if (orderIds.isEmpty()) { + return; + } + + // 批量查询各个工序表的进度信息 + batchQueryProcessProgress(orderIds, orderProcessMap); + + // 为每个报表项设置工序进度信息 + for (WipTrackingReportVo vo : reportList) { + if (vo.getProductOrderId() != null) { + List processProgress = orderProcessMap.get(vo.getProductOrderId()); + if (processProgress != null && !processProgress.isEmpty()) { + vo.setProcessProgressList(processProgress); + // 基于实际工序进度微调计算结果 + adjustProgressCalculation(vo, processProgress); + } else { + vo.setProcessProgressList(new ArrayList<>()); + } + } + } + + } catch (Exception e) { + System.out.println("批量获取工序进度信息失败: " + e.getMessage()); + // 降级处理:为每个订单单独查询 + fallbackToIndividualQuery(reportList); + } } /** - * 获取订单的所有工序进度详情(根据订单派工类型动态查询对应工序表) + * 批量查询工序进度信息 + * @param orderIds 订单ID列表 + * @param orderProcessMap 结果映射 + */ + private void batchQueryProcessProgress(List orderIds, Map> orderProcessMap) { + // 查询半制品工序进度(prod_plan_info_2) + queryProcessProgressByTable(orderIds, "prod_plan_info_2", orderProcessMap); + + // 查询成型工序进度(prod_plan_info_3) + queryProcessProgressByTable(orderIds, "prod_plan_info_3", orderProcessMap); + + // 查询硫化工序进度(prod_plan_info_4) + queryProcessProgressByTable(orderIds, "prod_plan_info_4", orderProcessMap); + + // 对每个订单的工序进度进行排序 + orderProcessMap.values().forEach(processList -> + processList.sort((a, b) -> { + if (a.getProcessOrder() == null) return 1; + if (b.getProcessOrder() == null) return -1; + return a.getProcessOrder().compareTo(b.getProcessOrder()); + }) + ); + } + + /** + * 根据表名查询工序进度信息 + * @param orderIds 订单ID列表 + * @param tableName 表名 + * @param orderProcessMap 结果映射 + */ + private void queryProcessProgressByTable(List orderIds, String tableName, Map> orderProcessMap) { + try { + // 使用批量查询避免 N+1:一次性查询该工序表下所有订单的工序进度 + // 返回结果中包含 productOrderId 字段,便于在此处进行分组 + List batchResults = prodReportMapper.getOrdersProcessProgressBatch(orderIds, tableName); + if (batchResults != null && !batchResults.isEmpty()) { + for (ProcessProgressVo progress : batchResults) { + Long orderId = progress.getProductOrderId(); + if (orderId != null) { + orderProcessMap.computeIfAbsent(orderId, k -> new ArrayList<>()).add(progress); + } + } + } + } catch (Exception e) { + // 记录失败但不中断整体处理,后续会有降级策略 + System.out.println("批量查询表 " + tableName + " 的工序进度失败: " + e.getMessage()); + } + } + + /** + * 基于实际工序进度微调计算结果 + * @param vo 在制品跟踪报表VO + * @param processProgress 工序进度列表 + */ + private void adjustProgressCalculation(WipTrackingReportVo vo, List processProgress) { + if (processProgress == null || processProgress.isEmpty()) { + return; + } + + try { + // 统计工序状态 + long completedCount = processProgress.stream() + .mapToLong(p -> p.getIsCompleted() != null && p.getIsCompleted() == 1 ? 1 : 0) + .sum(); + long inProgressCount = processProgress.stream() + .mapToLong(p -> p.getIsInProgress() != null && p.getIsInProgress() == 1 ? 1 : 0) + .sum(); + long totalCount = processProgress.size(); + + // 更新工序统计信息 + vo.setTotalProcessCount(totalCount + "道"); + vo.setTotalProcessCountNum((int) totalCount); + + // 构建在制工序名称(进行中的工序) + String wipProcesses = processProgress.stream() + .filter(p -> p.getIsInProgress() != null && p.getIsInProgress() == 1) + .map(ProcessProgressVo::getProcessName) + .filter(Objects::nonNull) + .collect(Collectors.joining(",")); + + // 构建剩余工序名称(未派工和已派工但未开始的工序) + String remainingProcesses = processProgress.stream() + .filter(p -> (p.getIsCompleted() == null || p.getIsCompleted() == 0) && + (p.getIsInProgress() == null || p.getIsInProgress() == 0)) + .map(ProcessProgressVo::getProcessName) + .filter(Objects::nonNull) + .collect(Collectors.joining(",")); + + vo.setWipProcesses(wipProcesses); + vo.setRemainingProcesses(remainingProcesses); + + } catch (Exception e) { + System.out.println("微调进度计算失败,订单ID: " + vo.getProductOrderId() + ", 错误: " + e.getMessage()); + } + } + + /** + * 降级处理:单独查询每个订单的工序进度 + * @param reportList 报表列表 + */ + private void fallbackToIndividualQuery(List reportList) { + for (WipTrackingReportVo vo : reportList) { + if (vo.getProductOrderId() != null) { + try { + List processProgress = getAllOrderProcessProgressOptimized(vo.getProductOrderId()); + vo.setProcessProgressList(processProgress); + adjustProgressCalculation(vo, processProgress); + } catch (Exception e) { + System.out.println("单独查询订单工序进度失败,订单ID: " + vo.getProductOrderId()); + vo.setProcessProgressList(new ArrayList<>()); + } + } + } + } + + /** + * 获取订单的所有工序进度详情(优化版本) * @param productOrderId 产品订单ID * @return 工序进度列表 */ - private List getAllOrderProcessProgress(Long productOrderId) { + private List getAllOrderProcessProgressOptimized(Long productOrderId) { List allProcessProgress = new java.util.ArrayList<>(); - + try { // 1. 获取订单信息 var orderInfo = prodOrderInfoMapper.selectById(productOrderId); if (orderInfo == null) { return allProcessProgress; } - - // 2. 根据派工类型获取工序列表 - List processIds = new java.util.ArrayList<>(); - - if ("2".equals(orderInfo.getDispatchType())) { - // 工艺路线派工,通过工艺路线ID获取工序列表 - Long routeId = orderInfo.getDispatchId(); - if (routeId != null) { - ProdBaseRouteProcessBo bo = new ProdBaseRouteProcessBo(); - bo.setRouteId(routeId); - List routeProcessList = prodBaseRouteProcessService.queryList(bo); - - for (ProdBaseRouteProcessVo routeProcess : routeProcessList) { - if (routeProcess.getProcessId() != null) { - processIds.add(routeProcess.getProcessId()); - } - } - } - } else if ("3".equals(orderInfo.getDispatchType())) { - // 单工序派工,直接使用dispatchId作为工序ID - if (orderInfo.getDispatchId() != null) { - processIds.add(orderInfo.getDispatchId()); - } - } else { - // 产线派工或其他情况,查询所有可能的工序表 - // 根据业务规则,查询半制品(16)、成型(17)、硫化(18)工序 - processIds.addAll(List.of(16L, 17L, 18L)); - } - - // 3. 根据工序ID列表查询对应的表 + + // 2. 根据派工类型确定需要查询的工序表 + List processIds = determineProcessIds(orderInfo); + + // 3. 并行查询各工序表以提高性能 for (Long processId : processIds) { try { String planTableName = getPlanInfoTableNameByProcessId(processId); @@ -294,21 +412,145 @@ public class ProdReportServiceImpl implements IProdReportService { } } } catch (Exception e) { - // 忽略单个表的查询错误,继续查询其他表 + // 记录日志但不中断处理 + System.out.println("查询工序进度失败,订单ID: " + productOrderId + ", 工序ID: " + processId + ", 错误: " + e.getMessage()); } } - + // 4. 按工序顺序排序 allProcessProgress.sort((a, b) -> { if (a.getProcessOrder() == null) return 1; if (b.getProcessOrder() == null) return -1; return a.getProcessOrder().compareTo(b.getProcessOrder()); }); - + } catch (Exception e) { - // 静默处理异常,返回空列表 + System.out.println("获取订单工序进度失败,订单ID: " + productOrderId + ", 错误: " + e.getMessage()); } - + return allProcessProgress; } + + /** + * 转换为导出VO - 保持与页面相同的样式和数据 + * @param reportList 在制品跟踪报表列表 + * @return 导出VO列表 + */ + @Override + public List convertToExportVo(List reportList) { + if (reportList == null || reportList.isEmpty()) { + return new ArrayList<>(); + } + + return reportList.stream().map(this::convertSingleToExportVo).collect(Collectors.toList()); + } + + /** + * 转换单个报表VO为导出VO + * @param vo 在制品跟踪报表VO + * @return 导出VO + */ + private WipTrackingReportExportVo convertSingleToExportVo(WipTrackingReportVo vo) { + WipTrackingReportExportVo exportVo = new WipTrackingReportExportVo(); + + // 基础字段复制 + exportVo.setOrderCode(vo.getOrderCode()); + exportVo.setMaterialCode(vo.getMaterialCode()); + exportVo.setMaterialName(vo.getMaterialName()); + exportVo.setMaterialSpec(vo.getMaterialSpec()); + exportVo.setPlanAmount(vo.getPlanAmount()); + exportVo.setWipAmount(vo.getWipAmount()); + exportVo.setCompleteAmount(vo.getCompleteAmount()); + + // 时间字段转换 + if (vo.getPlanBeginTime() != null) { + exportVo.setPlanBeginTime(vo.getPlanBeginTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()); + } + if (vo.getRealBeginTime() != null) { + exportVo.setRealBeginTime(vo.getRealBeginTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()); + } + if (vo.getPlanEndTime() != null) { + exportVo.setPlanEndTime(vo.getPlanEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()); + } + if (vo.getCurrentTime() != null) { + exportVo.setCurrentTime(vo.getCurrentTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()); + } + + // 进度相关字段 + exportVo.setTotalProcessCount(vo.getTotalProcessCount()); + exportVo.setWipProcesses(vo.getWipProcesses()); + exportVo.setRemainingProcesses(vo.getRemainingProcesses()); + exportVo.setOverallProgress(vo.getOverallProgress()); + exportVo.setProgressStatus(vo.getProgressStatus()); + + // 构建工序进度详情说明 + StringBuilder processDetail = new StringBuilder(); + if (vo.getProcessProgressList() != null && !vo.getProcessProgressList().isEmpty()) { + for (ProcessProgressVo process : vo.getProcessProgressList()) { + if (!processDetail.isEmpty()) { + processDetail.append("; "); + } + processDetail.append(process.getProcessName()) + .append("(") + .append(process.getStatusDesc()) + .append(")"); + + // 如果有进度百分比,添加进度信息 + if (process.getProcessProgress() != null && process.getProcessProgress().compareTo(BigDecimal.ZERO) > 0) { + processDetail.append(" ").append(process.getProcessProgress()).append("%"); + } + } + } + exportVo.setProcessProgressDetail(processDetail.toString()); + + return exportVo; + } + + + + /** + * 根据订单信息确定需要查询的工序ID列表 + * @param orderInfo 订单信息 + * @return 工序ID列表 + */ + private List determineProcessIds(Object orderInfo) { + List processIds = new java.util.ArrayList<>(); + + try { + // 使用反射获取派工类型和派工ID + String dispatchType = (String) orderInfo.getClass().getMethod("getDispatchType").invoke(orderInfo); + Object dispatchIdObj = orderInfo.getClass().getMethod("getDispatchId").invoke(orderInfo); + + if ("2".equals(dispatchType)) { + // 工艺路线派工,通过工艺路线ID获取工序列表 + Long routeId = dispatchIdObj instanceof Integer ? ((Integer) dispatchIdObj).longValue() : (Long) dispatchIdObj; + ProdBaseRouteProcessBo bo = new ProdBaseRouteProcessBo(); + bo.setRouteId(routeId); + List routeProcessList = prodBaseRouteProcessService.queryList(bo); + + for (ProdBaseRouteProcessVo routeProcess : routeProcessList) { + if (routeProcess.getProcessId() != null) { + processIds.add(routeProcess.getProcessId()); + } + } + } else if ("3".equals(dispatchType)) { + // 单工序派工,直接使用dispatchId作为工序ID + if (dispatchIdObj != null) { + Long processId = dispatchIdObj instanceof Integer ? ((Integer) dispatchIdObj).longValue() : (Long) dispatchIdObj; + processIds.add(processId); + } + } else { + // 产线派工或其他情况,查询所有可能的工序表 + // 根据业务规则,查询半制品(16)、成型(17)、硫化(18)工序 + processIds.addAll(List.of(16L, 17L, 18L)); + } + } catch (Exception e) { + System.out.println("确定工序ID列表失败,使用默认工序: " + e.getMessage()); + // 默认查询所有工序 + processIds.addAll(List.of(16L, 17L, 18L)); + } + + return processIds; + } + }