package cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.*; 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.MesQcIpqcLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO; import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum; import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService; import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService; import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcReportService; import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcLineService; import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcLineService; import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcLineService; import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcLineService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; @Tag(name = "管理后台 - MES 检验结果") @RestController @RequestMapping("/mes/qc/indicator-result") @Validated public class MesQcIndicatorResultController { @Resource private MesQcIndicatorResultService resultService; @Resource private MesQcReportService reportService; @Resource private MesQcIqcLineService iqcLineService; @Resource private MesQcIpqcLineService ipqcLineService; @Resource private MesQcOqcLineService oqcLineService; @Resource private MesQcRqcLineService rqcLineService; @Resource private MesQcIndicatorService indicatorService; @PostMapping("/create") @Operation(summary = "创建检验结果") @PreAuthorize("@ss.hasPermission('mes:qc-iqc:create')") public CommonResult createIndicatorResult(@Valid @RequestBody MesQcIndicatorResultSaveReqVO createReqVO) { return success(resultService.createIndicatorResult(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新检验结果") @PreAuthorize("@ss.hasPermission('mes:qc-iqc:update')") public CommonResult updateIndicatorResult(@Valid @RequestBody MesQcIndicatorResultSaveReqVO updateReqVO) { resultService.updateIndicatorResult(updateReqVO); return success(true); } @DeleteMapping("/delete") @Operation(summary = "删除检验结果") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('mes:qc-iqc:delete')") public CommonResult deleteIndicatorResult(@RequestParam("id") Long id) { resultService.deleteIndicatorResult(id); return success(true); } @GetMapping("/get-detail") @Operation(summary = "获得检验结果明细(含检测项模板)") @Parameters({ @Parameter(name = "id", description = "检验结果编号(编辑时传入;新增时不传)", example = "1024"), @Parameter(name = "qcId", description = "质检单ID", required = true, example = "1"), @Parameter(name = "qcType", description = "质检类型", required = true, example = "1") }) @PreAuthorize("@ss.hasPermission('mes:qc-iqc:query')") public CommonResult getIndicatorResultDetail( @RequestParam(value = "id", required = false) Long id, @RequestParam("qcId") Long qcId, @RequestParam("qcType") Integer qcType) { MesQcIndicatorResultRespVO respVO; List details; if (id != null) { // 编辑:加载已有结果主表 + 已有明细 MesQcIndicatorResultDO result = resultService.getIndicatorResult(id); respVO = BeanUtils.toBean(result, MesQcIndicatorResultRespVO.class); details = resultService.getIndicatorResultDetailListByResultId(id); } else { // 新增:返回空壳 RespVO,仅填充检测项模板 respVO = new MesQcIndicatorResultRespVO(); details = Collections.emptyList(); } respVO.setItems(buildDetailItemList(details, qcId, qcType)); return success(respVO); } @GetMapping("/page") @Operation(summary = "获得检验结果分页") @PreAuthorize("@ss.hasPermission('mes:qc-iqc:query')") public CommonResult> getIndicatorResultPage(@Valid MesQcIndicatorResultPageReqVO pageReqVO) { PageResult pageResult = resultService.getIndicatorResultPage(pageReqVO); // 前端列表只展示 code、sn、remark,无需关联查询 PageResult voPageResult = BeanUtils.toBean(pageResult, MesQcIndicatorResultRespVO.class); return success(voPageResult); } @GetMapping("/report") @Operation(summary = "获得检验报告单数据(打印用:表头 + 检验项树 + 平行测量 + 签字区)") @Parameters({ @Parameter(name = "qcId", description = "质检单ID", required = true, example = "1"), @Parameter(name = "qcType", description = "质检类型", required = true, example = "1"), @Parameter(name = "resultId", description = "检验结果(样品)编号,可不传;多样品时用于选择", example = "1") }) @PreAuthorize("@ss.hasPermission('mes:qc-iqc:query')") public CommonResult getQcReportData( @RequestParam("qcId") Long qcId, @RequestParam("qcType") Integer qcType, @RequestParam(value = "resultId", required = false) Long resultId) { return success(reportService.getQcReportData(qcId, qcType, resultId)); } // ==================== 拼接 VO ==================== /** * 构建明细 Item 树(支持分组层级 + 平行测量多批次) * *

返回树形列表:顶级为分组项或独立录入项,分组项 children 挂子项; * 每个录入项的平行测量值放在 measures(按 measureNo 升序); * 兼容字段 id/value/remark 取第一批值,供旧数据直接展示

* * @param details 已有结果明细(空列表表示获取空值模板) * @param qcId 质检单 ID * @param qcType 质检类型 */ private List buildDetailItemList( List details, Long qcId, Integer qcType) { // 1.1 获取检验单行的指标 ID 列表(知道有哪些检测指标) List indicatorIds; if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) { List lines = iqcLineService.getIqcLineListByIqcId(qcId); indicatorIds = CollUtil.isEmpty(lines) ? Collections.emptyList() : new ArrayList<>(convertSet(lines, MesQcIqcLineDO::getIndicatorId)); } else if (Objects.equals(qcType, MesQcTypeEnum.IPQC.getType())) { List lines = ipqcLineService.getIpqcLineListByIpqcId(qcId); indicatorIds = CollUtil.isEmpty(lines) ? Collections.emptyList() : new ArrayList<>(convertSet(lines, MesQcIpqcLineDO::getIndicatorId)); } else if (Objects.equals(qcType, MesQcTypeEnum.OQC.getType())) { List lines = oqcLineService.getOqcLineListByOqcId(qcId); indicatorIds = CollUtil.isEmpty(lines) ? Collections.emptyList() : new ArrayList<>(convertSet(lines, MesQcOqcLineDO::getIndicatorId)); } else if (Objects.equals(qcType, MesQcTypeEnum.RQC.getType())) { List lines = rqcLineService.getRqcLineListByRqcId(qcId); indicatorIds = CollUtil.isEmpty(lines) ? Collections.emptyList() : new ArrayList<>(convertSet(lines, MesQcRqcLineDO::getIndicatorId)); } else { throw new IllegalArgumentException("暂不支持 qcType=" + qcType); } if (CollUtil.isEmpty(indicatorIds)) { return Collections.emptyList(); } // 1.2 批量查询检测指标;子项的父分组未挂在行上时自动补全,保证树完整 Map indicatorMap = indicatorService.getIndicatorMap(new HashSet<>(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)); } // 1.3 已有明细分组(indicatorId -> 多批次列表) Map> detailMap = CollUtil.isEmpty(details) ? Collections.emptyMap() : details.stream().collect(Collectors.groupingBy(MesQcIndicatorResultDetailDO::getIndicatorId)); // 2. 构建节点 Map nodeMap = new LinkedHashMap<>(); for (Map.Entry entry : indicatorMap.entrySet()) { Long indicatorId = entry.getKey(); MesQcIndicatorDO indicator = entry.getValue(); MesQcIndicatorResultRespVO.Item vo = new MesQcIndicatorResultRespVO.Item() .setIndicatorId(indicatorId) .setIndicatorName(indicator.getName()) .setValueType(indicator.getResultType()) .setValueSpecification(indicator.getResultSpecification()) .setParentId(Objects.requireNonNullElse(indicator.getParentId(), 0L)) .setItemType(Objects.requireNonNullElse(indicator.getItemType(), MesQcIndicatorItemTypeEnum.RECORD.getType())) .setFormulaText(indicator.getFormulaText()) .setUnitText(indicator.getUnitText()) .setSortOrder(Objects.requireNonNullElse(indicator.getSortOrder(), 0)); List itemDetails = detailMap.getOrDefault(indicatorId, Collections.emptyList()); List measures = new ArrayList<>(itemDetails.size()); itemDetails.stream() .sorted(Comparator.comparing(d -> Objects.requireNonNullElse(d.getMeasureNo(), 1))) .forEach(detail -> measures.add(new MesQcIndicatorResultRespVO.Measure() .setDetailId(detail.getId()).setResultId(detail.getResultId()) .setMeasureNo(Objects.requireNonNullElse(detail.getMeasureNo(), 1)) .setValue(detail.getValue()).setRemark(detail.getRemark()))); vo.setMeasures(measures); // 兼容旧字段:取第一批测量值 if (CollUtil.isNotEmpty(measures)) { MesQcIndicatorResultRespVO.Measure first = measures.get(0); vo.setId(first.getDetailId()).setResultId(first.getResultId()) .setValue(first.getValue()).setRemark(first.getRemark()); } nodeMap.put(indicatorId, vo); } // 3. 挂树:父不在集合内的子项提升为顶级;同级按排序号 Comparator byOrder = Comparator.comparing(MesQcIndicatorResultRespVO.Item::getSortOrder) .thenComparing(MesQcIndicatorResultRespVO.Item::getIndicatorId); List roots = new ArrayList<>(); for (MesQcIndicatorResultRespVO.Item node : nodeMap.values()) { MesQcIndicatorResultRespVO.Item 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); } }); return roots; } }