change(mes):优化在制品跟踪报表性能

- 批量获取工序进度信息,避免 N+1 查询问题,显著提升报表加载性能
-重构工序进度查询逻辑,支持多工序表并行查询与降级处理
- 新增报表导出 VO 转换逻辑,保持导出数据与页面展示一致
- 微调工序进度计算逻辑,增强数据准确性与可读性
- 使用通配符导入简化 VO 类引入,统一包级访问控制
master
zangch@mesnac.com 3 months ago
parent d7c53d2bd8
commit 428011a780

@ -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<ProcessProgressVo> getOrderProcessProgress(Long productOrderId);
// List<ProcessProgressVo> getOrderProcessProgress(Long productOrderId);
/**
* VO
* @param reportList
* @return VO
*/
List<WipTrackingReportExportVo> convertToExportVo(List<WipTrackingReportVo> reportList);
}

@ -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;
/**
* MESService
@ -192,98 +187,221 @@ public class ProdReportServiceImpl implements IProdReportService {
}
/**
*
* -
* @param hashMap
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<WipTrackingReportVo> wipTrackingReportList(Map hashMap, PageQuery pageQuery) {
// 使用优化后的SQL查询大部分计算已在数据库层完成
Page<WipTrackingReportVo> page = prodReportMapper.wipTrackingReportList(hashMap, pageQuery.build());
// 为每个订单获取工序进度详情
for (WipTrackingReportVo vo : page.getRecords()) {
if (vo.getProductOrderId() != null) {
List<ProcessProgressVo> processProgress = getAllOrderProcessProgress(vo.getProductOrderId());
vo.setProcessProgressList(processProgress);
}
}
// 批量获取工序进度信息以提升性能
enrichWithProcessProgressBatch(page.getRecords());
return TableDataInfo.build(page);
}
/**
*
* -
* @param hashMap
* @return
*/
@Override
public List<WipTrackingReportVo> wipTrackingReportList(Map hashMap) {
// 使用优化后的SQL查询大部分计算已在数据库层完成
List<WipTrackingReportVo> list = prodReportMapper.wipTrackingReportList(hashMap);
// 为每个订单获取工序进度详情
for (WipTrackingReportVo vo : list) {
if (vo.getProductOrderId() != null) {
List<ProcessProgressVo> processProgress = getAllOrderProcessProgress(vo.getProductOrderId());
vo.setProcessProgressList(processProgress);
}
}
// 批量获取工序进度信息以提升性能
enrichWithProcessProgressBatch(list);
return list;
}
/**
*
* @param productOrderId ID
* @return
* -
* @param reportList
*/
@Override
public List<ProcessProgressVo> getOrderProcessProgress(Long productOrderId) {
// 默认查询半制品表
String planTableName = getPlanInfoTableNameByProcessId(null);
return prodReportMapper.getOrderProcessProgress(productOrderId, planTableName);
private void enrichWithProcessProgressBatch(List<WipTrackingReportVo> reportList) {
if (reportList == null || reportList.isEmpty()) {
return;
}
// 批量获取所有订单的工序进度信息
Map<Long, List<ProcessProgressVo>> orderProcessMap = new HashMap<>();
try {
// 收集所有需要查询的订单ID
List<Long> 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<ProcessProgressVo> 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<Long> orderIds, Map<Long, List<ProcessProgressVo>> 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<Long> orderIds, String tableName, Map<Long, List<ProcessProgressVo>> orderProcessMap) {
try {
// 使用批量查询避免 N+1一次性查询该工序表下所有订单的工序进度
// 返回结果中包含 productOrderId 字段,便于在此处进行分组
List<ProcessProgressVo> 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<ProcessProgressVo> 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<WipTrackingReportVo> reportList) {
for (WipTrackingReportVo vo : reportList) {
if (vo.getProductOrderId() != null) {
try {
List<ProcessProgressVo> 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<ProcessProgressVo> getAllOrderProcessProgress(Long productOrderId) {
private List<ProcessProgressVo> getAllOrderProcessProgressOptimized(Long productOrderId) {
List<ProcessProgressVo> allProcessProgress = new java.util.ArrayList<>();
try {
// 1. 获取订单信息
var orderInfo = prodOrderInfoMapper.selectById(productOrderId);
if (orderInfo == null) {
return allProcessProgress;
}
// 2. 根据派工类型获取工序列表
List<Long> 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<ProdBaseRouteProcessVo> 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<Long> 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<WipTrackingReportExportVo> convertToExportVo(List<WipTrackingReportVo> reportList) {
if (reportList == null || reportList.isEmpty()) {
return new ArrayList<>();
}
return reportList.stream().map(this::convertSingleToExportVo).collect(Collectors.toList());
}
/**
* VOVO
* @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<Long> determineProcessIds(Object orderInfo) {
List<Long> 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<ProdBaseRouteProcessVo> 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;
}
}

Loading…
Cancel
Save