package com.ruoyi.quality.service.impl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.quality.dto.QualityTraceBatchOptionVo; import com.ruoyi.quality.dto.QualityTraceChainVo; import com.ruoyi.quality.dto.QualityTraceFinishedBatchVo; import com.ruoyi.quality.dto.QualityTraceForwardExportVo; import com.ruoyi.quality.dto.QualityTraceForwardVo; import com.ruoyi.quality.dto.QualityTraceInputVo; import com.ruoyi.quality.dto.QualityTraceInspectVo; import com.ruoyi.quality.dto.QualityTraceMaterialBatchVo; import com.ruoyi.quality.dto.QualityTraceOutboundVo; import com.ruoyi.quality.dto.QualityTracePickVo; import com.ruoyi.quality.dto.QualityTraceReportVo; import com.ruoyi.quality.dto.QualityTraceReverseExportVo; import com.ruoyi.quality.dto.QualityTraceReverseOrderVo; import com.ruoyi.quality.dto.QualityTraceReverseVo; import com.ruoyi.quality.dto.QualityTraceUnqualifiedVo; import com.ruoyi.quality.mapper.QualityTraceMapper; import com.ruoyi.quality.service.QualityTraceService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class QualityTraceServiceImpl implements QualityTraceService { private final QualityTraceMapper qualityTraceMapper; @Override public QualityTraceForwardVo forward(String batchNo) { String no = requireBatchNo(batchNo); QualityTraceForwardVo result = new QualityTraceForwardVo(); result.setBatchNo(no); //1.批号命中的生产入库记录(报工入库2 / 质检合格入库6) List inbound = qualityTraceMapper.listInboundByFinishedBatch(no); if (inbound.isEmpty()) { result.setOutbounds(qualityTraceMapper.listOutboundByBatch(no)); return result; } Map> inboundByOrder = inbound.stream() .filter(b -> b.getProductionOrderId() != null) .collect(Collectors.groupingBy(QualityTraceFinishedBatchVo::getProductionOrderId, LinkedHashMap::new, Collectors.toList())); List mainIds = distinct(inbound.stream().map(QualityTraceFinishedBatchVo::getProductMainId) .filter(Objects::nonNull).collect(Collectors.toList())); //2.报工节点(含订单信息) List reports = mainIds.isEmpty() ? new ArrayList<>() : qualityTraceMapper.listReportsByMainIds(mainIds); Map> reportsByOrder = reports.stream() .filter(r -> r.getProductionOrderId() != null) .collect(Collectors.groupingBy(QualityTraceReportVo::getProductionOrderId, LinkedHashMap::new, Collectors.toList())); Map orderOfMain = new LinkedHashMap<>(); reports.forEach(r -> orderOfMain.putIfAbsent(r.getProductMainId(), r.getProductionOrderId())); //3.投入物料(按报工单) List inputs = mainIds.isEmpty() ? new ArrayList<>() : qualityTraceMapper.listInputsByMainIds(mainIds); //4.领料批次(按订单),并用原料批号回溯供应商/采购/检验 List orderIds = new ArrayList<>(inboundByOrder.keySet()); List picks = orderIds.isEmpty() ? new ArrayList<>() : qualityTraceMapper.listPicksByOrderIds(orderIds); fillPickSource(picks); Map> picksByOrder = picks.stream() .filter(p -> p.getProductionOrderId() != null) .collect(Collectors.groupingBy(QualityTracePickVo::getProductionOrderId, LinkedHashMap::new, Collectors.toList())); //5.检验单与不合格处理 List inspects = mainIds.isEmpty() ? new ArrayList<>() : qualityTraceMapper.listInspectsByMainIds(mainIds); List inspectIds = distinct(inspects.stream().map(QualityTraceInspectVo::getInspectId) .filter(Objects::nonNull).collect(Collectors.toList())); List unqualified = inspectIds.isEmpty() ? new ArrayList<>() : qualityTraceMapper.listUnqualifiedByInspectIds(inspectIds); Map> unqualifiedByInspect = unqualified.stream() .filter(u -> u.getInspectId() != null) .collect(Collectors.groupingBy(QualityTraceUnqualifiedVo::getInspectId)); inspects.forEach(ins -> ins.setUnqualifiedList( unqualifiedByInspect.getOrDefault(ins.getInspectId(), new ArrayList<>()))); Map> inspectsByOrder = inspects.stream() .filter(i -> i.getProductMainId() != null && orderOfMain.get(i.getProductMainId()) != null) .collect(Collectors.groupingBy(i -> orderOfMain.get(i.getProductMainId()), LinkedHashMap::new, Collectors.toList())); Set chainOrderIds = new LinkedHashSet<>(inboundByOrder.keySet()); chainOrderIds.addAll(reportsByOrder.keySet()); for (Long orderId : chainOrderIds) { QualityTraceChainVo chain = new QualityTraceChainVo(); chain.setProductionOrderId(orderId); List orderReports = reportsByOrder.getOrDefault(orderId, new ArrayList<>()); if (!orderReports.isEmpty()) { QualityTraceReportVo first = orderReports.get(0); chain.setNpsNo(first.getNpsNo()); chain.setFinishedProduct(first.getFinishedProduct()); chain.setUnit(first.getUnit()); chain.setPlanQty(first.getPlanQty()); chain.setCompleteQty(first.getCompleteQty()); chain.setOrderStatus(first.getOrderStatus()); chain.setOrderCreateTime(first.getOrderCreateTime()); } chain.setBatches(inboundByOrder.getOrDefault(orderId, new ArrayList<>())); chain.setReports(orderReports); Set orderMainIds = orderReports.stream() .map(QualityTraceReportVo::getProductMainId).filter(Objects::nonNull) .collect(Collectors.toCollection(LinkedHashSet::new)); chain.setInputs(inputs.stream() .filter(i -> i.getProductMainId() != null && orderMainIds.contains(i.getProductMainId())) .collect(Collectors.toList())); chain.setPicks(picksByOrder.getOrDefault(orderId, new ArrayList<>())); chain.setInspects(inspectsByOrder.getOrDefault(orderId, new ArrayList<>())); chain.setUnqualifiedList(chain.getInspects().stream() .flatMap(i -> i.getUnqualifiedList().stream()).collect(Collectors.toList())); result.getChains().add(chain); } result.setOutbounds(qualityTraceMapper.listOutboundByBatch(no)); return result; } @Override public QualityTraceReverseVo reverse(String batchNo) { String no = requireBatchNo(batchNo); QualityTraceReverseVo result = new QualityTraceReverseVo(); result.setBatchNo(no); //1.批号入库/采购/检验来源 List materialInfo = qualityTraceMapper.listMaterialBatchInfoByBatches(Collections.singletonList(no)); if (!materialInfo.isEmpty()) { result.setMaterial(materialInfo.get(0)); } //2.使用该批号的领料明细 List picks = qualityTraceMapper.listPicksByBatchNo(no); if (picks.isEmpty()) { return result; } fillPickSource(picks); Map> picksByOrder = picks.stream() .filter(p -> p.getProductionOrderId() != null) .collect(Collectors.groupingBy(QualityTracePickVo::getProductionOrderId, LinkedHashMap::new, Collectors.toList())); List orderIds = new ArrayList<>(picksByOrder.keySet()); //3.订单报工节点与产出成品批号 List reports = qualityTraceMapper.listReportsByOrderIds(orderIds); Map> reportsByOrder = reports.stream() .filter(r -> r.getProductionOrderId() != null) .collect(Collectors.groupingBy(QualityTraceReportVo::getProductionOrderId, LinkedHashMap::new, Collectors.toList())); Map> batchesByOrder = qualityTraceMapper.listFinishedBatchesByOrderIds(orderIds).stream() .filter(b -> b.getProductionOrderId() != null) .collect(Collectors.groupingBy(QualityTraceFinishedBatchVo::getProductionOrderId, LinkedHashMap::new, Collectors.toList())); for (Long orderId : orderIds) { QualityTraceReverseOrderVo orderVo = new QualityTraceReverseOrderVo(); orderVo.setProductionOrderId(orderId); List orderReports = reportsByOrder.getOrDefault(orderId, new ArrayList<>()); if (!orderReports.isEmpty()) { QualityTraceReportVo first = orderReports.get(0); orderVo.setNpsNo(first.getNpsNo()); orderVo.setFinishedProduct(first.getFinishedProduct()); orderVo.setUnit(first.getUnit()); orderVo.setPlanQty(first.getPlanQty()); orderVo.setCompleteQty(first.getCompleteQty()); orderVo.setOrderStatus(first.getOrderStatus()); orderVo.setOrderCreateTime(first.getOrderCreateTime()); } List orderPicks = picksByOrder.getOrDefault(orderId, new ArrayList<>()); orderVo.setPickedQty(orderPicks.stream().map(p -> nvl(p.getQuantity())) .reduce(BigDecimal.ZERO, BigDecimal::add)); orderVo.setPicks(orderPicks); orderVo.setReports(orderReports); orderVo.setFinishedBatches(batchesByOrder.getOrDefault(orderId, new ArrayList<>())); result.getOrders().add(orderVo); } return result; } @Override public List forwardExportRows(String batchNo) { QualityTraceForwardVo trace = forward(batchNo); List rows = new ArrayList<>(); for (QualityTraceChainVo chain : trace.getChains()) { String operations = chain.getReports().stream() .map(r -> StringUtils.isBlank(r.getOperationName()) ? r.getWorkOrderNo() : r.getOperationName()) .filter(StringUtils::isNotBlank).distinct().collect(Collectors.joining("/")); String inspectResults = chain.getInspects().stream() .map(i -> (StringUtils.isBlank(i.getInspectTypeName()) ? "" : i.getInspectTypeName() + ":") + (StringUtils.isBlank(i.getCheckResult()) ? "未提交" : i.getCheckResult())) .collect(Collectors.joining("/")); String unqualifiedResults = chain.getUnqualifiedList().stream() .map(u -> (StringUtils.isBlank(u.getDealResult()) ? "待处理" : u.getDealResult()) + "x" + nvl(u.getQuantity()).stripTrailingZeros().toPlainString()) .collect(Collectors.joining("/")); if (chain.getPicks().isEmpty()) { QualityTraceForwardExportVo row = newForwardRow(trace.getBatchNo(), chain, operations, inspectResults, unqualifiedResults); rows.add(row); continue; } for (QualityTracePickVo pick : chain.getPicks()) { QualityTraceForwardExportVo row = newForwardRow(trace.getBatchNo(), chain, operations, inspectResults, unqualifiedResults); row.setMaterialBatchNo(pick.getBatchNo()); row.setMaterialName(pick.getMaterialName()); row.setMaterialModel(pick.getMaterialModel()); row.setPickQty(pick.getQuantity()); row.setSupplierName(pick.getSupplierName()); row.setMaterialCheckResult(pick.getCheckResult()); rows.add(row); } } return rows; } @Override public List reverseExportRows(String batchNo) { QualityTraceReverseVo trace = reverse(batchNo); List rows = new ArrayList<>(); QualityTraceMaterialBatchVo material = trace.getMaterial(); if (trace.getOrders().isEmpty()) { rows.add(newReverseRow(trace, material, null, null)); return rows; } for (QualityTraceReverseOrderVo order : trace.getOrders()) { String batchNos = order.getFinishedBatches().stream() .map(QualityTraceFinishedBatchVo::getBatchNo) .filter(StringUtils::isNotBlank).distinct().collect(Collectors.joining("/")); rows.add(newReverseRow(trace, material, order, batchNos)); } return rows; } private QualityTraceForwardExportVo newForwardRow(String batchNo, QualityTraceChainVo chain, String operations, String inspectResults, String unqualifiedResults) { QualityTraceForwardExportVo row = new QualityTraceForwardExportVo(); row.setBatchNo(batchNo); row.setNpsNo(chain.getNpsNo()); row.setFinishedProduct(chain.getFinishedProduct()); row.setCompleteQty(chain.getCompleteQty()); row.setOperations(operations); row.setInspectResults(inspectResults); row.setUnqualifiedResults(unqualifiedResults); return row; } private QualityTraceReverseExportVo newReverseRow(QualityTraceReverseVo trace, QualityTraceMaterialBatchVo material, QualityTraceReverseOrderVo order, String batchNos) { QualityTraceReverseExportVo row = new QualityTraceReverseExportVo(); row.setBatchNo(trace.getBatchNo()); if (material != null) { row.setMaterialName(material.getMaterialName()); row.setMaterialModel(material.getMaterialModel()); row.setUnit(material.getUnit()); row.setSupplierName(material.getSupplierName()); row.setContractNo(material.getContractNo()); row.setEntryDate(material.getEntryDate()); row.setMaterialCheckResult(material.getCheckResult()); } if (order != null) { row.setNpsNo(order.getNpsNo()); row.setFinishedProduct(order.getFinishedProduct()); row.setPickedQty(order.getPickedQty()); row.setFinishedBatchNos(batchNos); } return row; } @Override public List batchOptions(String type, String keyword, Integer limit) { String queryType = "material".equals(type) ? "material" : "finished"; int size = (limit == null || limit <= 0 || limit > 200) ? 50 : limit; return qualityTraceMapper.listBatchOptions(queryType, StringUtils.isBlank(keyword) ? null : keyword.trim(), size); } /** 用原料批号回溯入库来源,补全领料行的供应商/合同/检验结论 */ private void fillPickSource(List picks) { List batchNos = distinct(picks.stream().map(QualityTracePickVo::getBatchNo) .filter(StringUtils::isNotBlank).collect(Collectors.toList())); if (batchNos.isEmpty()) { return; } Map infoByBatch = new LinkedHashMap<>(); for (QualityTraceMaterialBatchVo info : qualityTraceMapper.listMaterialBatchInfoByBatches(batchNos)) { if (StringUtils.isNotBlank(info.getBatchNo())) { infoByBatch.putIfAbsent(info.getBatchNo(), info); } } for (QualityTracePickVo pick : picks) { QualityTraceMaterialBatchVo info = infoByBatch.get(pick.getBatchNo()); if (info == null) { continue; } pick.setSupplierName(info.getSupplierName()); pick.setContractNo(info.getContractNo()); pick.setCheckResult(info.getCheckResult()); if (StringUtils.isBlank(pick.getMaterialName())) { pick.setMaterialName(info.getMaterialName()); } if (StringUtils.isBlank(pick.getMaterialModel())) { pick.setMaterialModel(info.getMaterialModel()); } if (StringUtils.isBlank(pick.getUnit())) { pick.setUnit(info.getUnit()); } } } private String requireBatchNo(String batchNo) { if (StringUtils.isBlank(batchNo)) { throw new ServiceException("批号不能为空"); } return batchNo.trim(); } private List distinct(List ids) { return ids.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList()); } private BigDecimal nvl(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } }