package cn.iocoder.yudao.module.mes.service.qc.indicatorresult; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcQcReportRespVO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO; import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum; import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService; import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcLineService; import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService; import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcLineService; import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService; import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcLineService; import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService; import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcLineService; import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; /** * MES 检验报告单 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesQcReportServiceImpl implements MesQcReportService { @Resource private MesQcIndicatorResultService resultService; @Resource private MesQcIndicatorService indicatorService; @Resource private MesQcIqcService iqcService; @Resource private MesQcIqcLineService iqcLineService; @Resource @Lazy private MesQcIpqcService ipqcService; @Resource @Lazy private MesQcIpqcLineService ipqcLineService; @Resource @Lazy private MesQcOqcService oqcService; @Resource @Lazy private MesQcOqcLineService oqcLineService; @Resource @Lazy private MesQcRqcService rqcService; @Resource @Lazy private MesQcRqcLineService rqcLineService; @Resource private MesMdItemService itemService; @Resource private AdminUserApi adminUserApi; @Override public MesQcQcReportRespVO getQcReportData(Long qcId, Integer qcType, Long resultId) { MesQcQcReportRespVO report = new MesQcQcReportRespVO(); report.setQcType(qcType); // 1. 主表信息(按类型取公共字段) Long itemId = null; List lineIndicatorIds = new ArrayList<>(); Map lineInfoMap = new LinkedHashMap<>(); if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) { MesQcIqcDO doc = iqcService.validateIqcExists(qcId); itemId = doc.getItemId(); report.setDocCode(doc.getCode()).setDocName(doc.getName()) .setBatchNo(doc.getVendorBatch()).setBatchQuantity(doc.getReceivedQuantity()) .setSamplingTime(doc.getInspectDate() != null ? doc.getInspectDate() : doc.getReceiveDate()) .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo()) .setExecuteStandard(doc.getExecuteStandard()) .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId()) .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult()) .setInspectDate(doc.getInspectDate()).setDocCreateTime(doc.getCreateTime()) .setRemark(doc.getRemark()); List lines = iqcLineService.getIqcLineListByIqcId(qcId); for (MesQcIqcLineDO line : lines) { lineIndicatorIds.add(line.getIndicatorId()); lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(), line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold())); } } else if (Objects.equals(qcType, MesQcTypeEnum.IPQC.getType())) { MesQcIpqcDO doc = ipqcService.validateIpqcExists(qcId); itemId = doc.getItemId(); report.setDocCode(doc.getCode()).setDocName(doc.getName()) .setBatchNo(doc.getSourceDocCode()).setBatchQuantity(doc.getCheckQuantity()) .setSamplingTime(doc.getInspectDate()) .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo()) .setExecuteStandard(doc.getExecuteStandard()) .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId()) .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult()) .setInspectDate(doc.getInspectDate()).setDocCreateTime(doc.getCreateTime()) .setRemark(doc.getRemark()); List lines = ipqcLineService.getIpqcLineListByIpqcId(qcId); for (MesQcIpqcLineDO line : lines) { lineIndicatorIds.add(line.getIndicatorId()); lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(), line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold())); } } else if (Objects.equals(qcType, MesQcTypeEnum.OQC.getType())) { MesQcOqcDO doc = oqcService.validateOqcExists(qcId); itemId = doc.getItemId(); report.setDocCode(doc.getCode()).setDocName(doc.getName()) .setBatchNo(doc.getBatchCode()).setBatchQuantity(doc.getOutQuantity()) .setSamplingTime(doc.getInspectDate() != null ? doc.getInspectDate() : doc.getOutDate()) .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo()) .setExecuteStandard(doc.getExecuteStandard()) .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId()) .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult()) .setInspectDate(doc.getInspectDate()).setDocCreateTime(doc.getCreateTime()) .setRemark(doc.getRemark()); List lines = oqcLineService.getOqcLineListByOqcId(qcId); for (MesQcOqcLineDO line : lines) { lineIndicatorIds.add(line.getIndicatorId()); lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(), line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold())); } } else if (Objects.equals(qcType, MesQcTypeEnum.RQC.getType())) { MesQcRqcDO doc = rqcService.validateRqcExists(qcId); itemId = doc.getItemId(); report.setDocCode(doc.getCode()).setDocName(doc.getName()) .setBatchNo(doc.getBatchCode()).setBatchQuantity(doc.getCheckQuantity()) .setSamplingTime(doc.getInspectDate()) .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo()) .setExecuteStandard(doc.getExecuteStandard()) .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId()) .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult()) .setInspectDate(doc.getInspectDate()).setDocCreateTime(doc.getCreateTime()) .setRemark(doc.getRemark()); List lines = rqcLineService.getRqcLineListByRqcId(qcId); for (MesQcRqcLineDO line : lines) { lineIndicatorIds.add(line.getIndicatorId()); lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(), line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold())); } } else { throw new IllegalArgumentException("暂不支持 qcType=" + qcType); } // 2. 样品列表与选中的检验结果 List results = resultService.getIndicatorResultListByQcIdAndType(qcId, qcType); Long effectiveResultId = resultId; if (effectiveResultId == null && CollUtil.isNotEmpty(results)) { effectiveResultId = results.stream() .min(Comparator.comparing(MesQcIndicatorResultDO::getId)) .map(MesQcIndicatorResultDO::getId).orElse(null); } // 3. 指标(含父分组自动补全) Set indicatorIds = new HashSet<>(lineIndicatorIds); Map indicatorMap = indicatorService.getIndicatorMap(indicatorIds); Set missingParentIds = indicatorMap.values().stream() .filter(ind -> ind.getParentId() != null && ind.getParentId() > 0 && !indicatorMap.containsKey(ind.getParentId())) .map(MesQcIndicatorDO::getParentId).collect(Collectors.toSet()); if (CollUtil.isNotEmpty(missingParentIds)) { indicatorMap.putAll(indicatorService.getIndicatorMap(missingParentIds)); } // 4. 检验结果明细(按 indicatorId 分组、measureNo 升序) Map> detailMap = Collections.emptyMap(); if (effectiveResultId != null) { List details = resultService.getIndicatorResultDetailListByResultId(effectiveResultId); detailMap = details.stream().collect(Collectors.groupingBy(MesQcIndicatorResultDetailDO::getIndicatorId)); } // 5. 组装检验项树 List roots = buildReportItems(indicatorMap, lineInfoMap, detailMap, report); report.setItems(roots); // 6. 表头关联数据:物料、人员 if (itemId != null) { MesMdItemDO item = itemService.getItemMap(Collections.singleton(itemId)).get(itemId); if (item != null) { report.setItemCode(item.getCode()).setItemName(item.getName()) .setItemSpecification(item.getSpecification()); } } Set userIds = new HashSet<>(); userIds.add(report.getInspectorUserId()); userIds.add(report.getAuditUserId()); userIds.add(report.getApproveUserId()); userIds.removeIf(Objects::isNull); if (CollUtil.isNotEmpty(userIds)) { Map userMap = adminUserApi.getUserMap(userIds); if (report.getInspectorUserId() != null && userMap.get(report.getInspectorUserId()) != null) { report.setInspectorName(userMap.get(report.getInspectorUserId()).getNickname()); } if (report.getAuditUserId() != null && userMap.get(report.getAuditUserId()) != null) { report.setAuditName(userMap.get(report.getAuditUserId()).getNickname()); } if (report.getApproveUserId() != null && userMap.get(report.getApproveUserId()) != null) { report.setApproveName(userMap.get(report.getApproveUserId()).getNickname()); } } return report; } /** * 组装报告检验项树;顺带收集全局 measureNos 列头 */ private List buildReportItems( Map indicatorMap, Map lineInfoMap, Map> detailMap, MesQcQcReportRespVO report) { Map nodeMap = new LinkedHashMap<>(); Set measureNoSet = new HashSet<>(); for (Long indicatorId : indicatorMap.keySet()) { MesQcIndicatorDO indicator = indicatorMap.get(indicatorId); MesQcQcReportRespVO.ReportItem node = new MesQcQcReportRespVO.ReportItem(); node.setIndicatorId(indicator.getId()); node.setIndicatorCode(indicator.getCode()); node.setIndicatorName(indicator.getName()); node.setParentId(Objects.requireNonNullElse(indicator.getParentId(), 0L)); node.setItemType(Objects.requireNonNullElse(indicator.getItemType(), MesQcIndicatorItemTypeEnum.RECORD.getType())); node.setFormulaText(indicator.getFormulaText()); node.setUnitText(indicator.getUnitText()); node.setSortOrder(Objects.requireNonNullElse(indicator.getSortOrder(), 0)); LineInfo lineInfo = lineInfoMap.get(indicator.getId()); if (lineInfo != null) { node.setCheckMethod(lineInfo.checkMethod); node.setStandardValue(lineInfo.standardValue); node.setThresholdMin(lineInfo.thresholdMin); node.setThresholdMax(lineInfo.thresholdMax); } List details = detailMap.getOrDefault(indicator.getId(), Collections.emptyList()); List measures = new ArrayList<>(details.size()); details.stream() .sorted(Comparator.comparing(d -> Objects.requireNonNullElse(d.getMeasureNo(), 1))) .forEach(d -> { MesQcQcReportRespVO.ReportMeasure measure = new MesQcQcReportRespVO.ReportMeasure(); measure.setMeasureNo(Objects.requireNonNullElse(d.getMeasureNo(), 1)); measure.setValue(d.getValue()); measure.setRemark(d.getRemark()); measures.add(measure); measureNoSet.add(measure.getMeasureNo()); }); node.setMeasures(measures); nodeMap.put(indicatorId, node); } // 挂树:父不在集合内的子项提升为顶级 List roots = new ArrayList<>(); Comparator byOrder = Comparator.comparing(MesQcQcReportRespVO.ReportItem::getSortOrder) .thenComparing(MesQcQcReportRespVO.ReportItem::getIndicatorId); for (MesQcQcReportRespVO.ReportItem node : nodeMap.values()) { MesQcQcReportRespVO.ReportItem parent = node.getParentId() > 0 ? nodeMap.get(node.getParentId()) : null; if (parent == null) { roots.add(node); } else { if (parent.getChildren() == null) { parent.setChildren(new ArrayList<>()); } parent.getChildren().add(node); } } roots.sort(byOrder); roots.forEach(root -> { if (root.getChildren() != null) { root.getChildren().sort(byOrder); } }); // 列头:至少一列 if (measureNoSet.isEmpty()) { report.setMeasureNos(Collections.singletonList(1)); } else { report.setMeasureNos(new ArrayList<>(measureNoSet).stream().sorted().collect(Collectors.toList())); } return roots; } /** * 检验单行的判定信息(四类 line 的公共字段快照) */ private record LineInfo(String checkMethod, BigDecimal standardValue, BigDecimal thresholdMin, BigDecimal thresholdMax) { } }