| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -128,22 +128,32 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        System.out.println(String.format("转换为 PDF 结束")); | 
					 | 
					 | 
					 | 
					        System.out.println(String.format("转换为 PDF 结束")); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 构建体内检验记录数据 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param entrustInfo  委托信息对象 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param materialType 材料类型(如血液、尿液等) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @return 包含体内检验记录的Map数据 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @throws Exception 如果未找到检验记录,则抛出异常 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    public Map<String, Object> buildInVivoRecordData(EntrustInfo entrustInfo, String materialType) throws Exception { | 
					 | 
					 | 
					 | 
					    public Map<String, Object> buildInVivoRecordData(EntrustInfo entrustInfo, String materialType) throws Exception { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 获取检验记录信息
 | 
					 | 
					 | 
					 | 
					        // 1️⃣ 获取当前委托业务对应的检验记录信息
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        TestRecord testRecord = testRecordService.getTestRecordByBusinessId(entrustInfo.getId()); | 
					 | 
					 | 
					 | 
					        TestRecord testRecord = testRecordService.getTestRecordByBusinessId(entrustInfo.getId()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 2️⃣ 如果找不到对应的检验记录,则抛出异常
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (testRecord == null) { | 
					 | 
					 | 
					 | 
					        if (testRecord == null) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            throw new Exception("未找到检验记录信息"); | 
					 | 
					 | 
					 | 
					            throw new Exception("未找到检验记录信息"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 生成基础数据
 | 
					 | 
					 | 
					 | 
					        // 3️⃣ 生成基础数据,构建通用的检验记录文档数据
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        HashMap<String, Object> data = buildCommonInspectRecordDocMap(entrustInfo, testRecord, materialType); | 
					 | 
					 | 
					 | 
					        HashMap<String, Object> data = buildCommonInspectRecordDocMap(entrustInfo, testRecord, materialType); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 获取样品数据
 | 
					 | 
					 | 
					 | 
					        // 4️⃣ 查询该检验记录下的所有样品数据
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<TestRecordSampleData> dataList = testRecordSampleDataService.list( | 
					 | 
					 | 
					 | 
					        List<TestRecordSampleData> dataList = testRecordSampleDataService.list( | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                Wrappers.<TestRecordSampleData>lambdaQuery().eq(TestRecordSampleData::getTestId, testRecord.getId())); | 
					 | 
					 | 
					 | 
					                Wrappers.<TestRecordSampleData>lambdaQuery().eq(TestRecordSampleData::getTestId, testRecord.getId())); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 5️⃣ 如果没有样品数据,则直接返回空数据
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (CollectionUtils.isEmpty(dataList)) { | 
					 | 
					 | 
					 | 
					        if (CollectionUtils.isEmpty(dataList)) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            data.put("dataDtos", Collections.emptyList()); | 
					 | 
					 | 
					 | 
					            data.put("dataDtos", Collections.emptyList()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            data.put("sampleSize", 0); | 
					 | 
					 | 
					 | 
					            data.put("sampleSize", 0); | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -151,41 +161,48 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            return data; | 
					 | 
					 | 
					 | 
					            return data; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 批量查询所有样品扩展数据,避免多次查询数据库
 | 
					 | 
					 | 
					 | 
					        // 6️⃣ 批量查询所有样品的扩展数据,避免多次查询数据库,提高效率
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Map<String, List<TestRecordSampleDataExpand>> dataExpandMap = testRecordSampledataExpandService.list( | 
					 | 
					 | 
					 | 
					        Map<String, List<TestRecordSampleDataExpand>> dataExpandMap = testRecordSampledataExpandService.list( | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                Wrappers.<TestRecordSampleDataExpand>lambdaQuery() | 
					 | 
					 | 
					 | 
					                Wrappers.<TestRecordSampleDataExpand>lambdaQuery() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        .in(TestRecordSampleDataExpand::getTestDataId, dataList.stream().map(TestRecordSampleData::getId).collect(Collectors.toList())) | 
					 | 
					 | 
					 | 
					                        .in(TestRecordSampleDataExpand::getTestDataId, dataList.stream().map(TestRecordSampleData::getId).collect(Collectors.toList())) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ).stream().collect(Collectors.groupingBy(TestRecordSampleDataExpand::getTestDataId)); | 
					 | 
					 | 
					 | 
					        ).stream().collect(Collectors.groupingBy(TestRecordSampleDataExpand::getTestDataId)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 根据化合物名称分组
 | 
					 | 
					 | 
					 | 
					        // 7️⃣ 按化合物名称进行分组,确保同一化合物的数据在一起处理
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Map<String, List<TestRecordSampleData>> dataMap = dataList.stream() | 
					 | 
					 | 
					 | 
					        Map<String, List<TestRecordSampleData>> dataMap = dataList.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName)); | 
					 | 
					 | 
					 | 
					                .collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 8️⃣ 获取标准品列表,并按照其中的化合物顺序进行排序
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        String referenceMaterialName = (String) data.get("referenceMaterialName"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        List<String> collect = Arrays.stream(referenceMaterialName.split("\n"))  // 按换行拆分
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .map(line -> line.replaceFirst("^\u3000\u3000\u2611 ", "") // 去掉前缀 "  ✔ "
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                        .replaceFirst("^\u2611 ", "")) // 去掉前缀 "✔ "
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .collect(Collectors.toList()); // 转换为列表
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 9️⃣ 用于存储最终的检验数据
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<TestRecordSampleDataDocDTO> dataDtos = new ArrayList<>(); | 
					 | 
					 | 
					 | 
					        List<TestRecordSampleDataDocDTO> dataDtos = new ArrayList<>(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 遍历化合物组
 | 
					 | 
					 | 
					 | 
					        // 🔟 遍历化合物列表,按照标准品顺序构建数据
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (Map.Entry<String, List<TestRecordSampleData>> entry : dataMap.entrySet()) { | 
					 | 
					 | 
					 | 
					        for (String compoundName : collect) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String compoundName = entry.getKey(); | 
					 | 
					 | 
					 | 
					            List<TestRecordSampleData> list = dataMap.get(compoundName); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            List<TestRecordSampleData> list = entry.getValue(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 添加空白数据
 | 
					 | 
					 | 
					 | 
					            // 10.1️⃣ 先添加一个空白样品数据
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            TestRecordSampleDataDocDTO blankVo = new TestRecordSampleDataDocDTO(); | 
					 | 
					 | 
					 | 
					            TestRecordSampleDataDocDTO blankVo = new TestRecordSampleDataDocDTO(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setName("空白" + materialType); | 
					 | 
					 | 
					 | 
					            blankVo.setName("空白" + materialType); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setCompoundName(compoundName); | 
					 | 
					 | 
					 | 
					            blankVo.setCompoundName(compoundName); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setPTargetRtTime("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					            blankVo.setPTargetRtTime("/"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setPRtTimeError("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					            blankVo.setPRtTimeError("/"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setPRtTimeWithinError("否"); // 默认"否"
 | 
					 | 
					 | 
					 | 
					            blankVo.setPRtTimeWithinError("否"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setPIonAbundanceRatio("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					            blankVo.setPIonAbundanceRatio("/"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setPIonAbundanceRatioError("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					            blankVo.setPIonAbundanceRatioError("/"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setPIonAbundanceRatioWithinError("否"); // 默认"否"
 | 
					 | 
					 | 
					 | 
					            blankVo.setPIonAbundanceRatioWithinError("否"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            blankVo.setPIsDetected("否"); // 默认"否"
 | 
					 | 
					 | 
					 | 
					            blankVo.setPIsDetected("否"); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            dataDtos.add(blankVo); | 
					 | 
					 | 
					 | 
					            dataDtos.add(blankVo); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 根据样品类型分组
 | 
					 | 
					 | 
					 | 
					            // 10.2️⃣ 按样品类型(STD:标准样品,Analyte:检材样品)分组
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            Map<String, List<TestRecordSampleData>> map = list.stream() | 
					 | 
					 | 
					 | 
					            Map<String, List<TestRecordSampleData>> map = list.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .collect(Collectors.groupingBy(TestRecordSampleData::getSampleType)); | 
					 | 
					 | 
					 | 
					                    .collect(Collectors.groupingBy(TestRecordSampleData::getSampleType)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 处理标准样品(STD)
 | 
					 | 
					 | 
					 | 
					            // 11️⃣ 处理标准样品(STD)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (map.containsKey("STD")) { | 
					 | 
					 | 
					 | 
					            if (map.containsKey("STD")) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                TestRecordSampleData std = map.get("STD").get(0); | 
					 | 
					 | 
					 | 
					                TestRecordSampleData std = map.get("STD").get(0); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                TestRecordSampleDataDocDTO stdVo = new TestRecordSampleDataDocDTO(); | 
					 | 
					 | 
					 | 
					                TestRecordSampleDataDocDTO stdVo = new TestRecordSampleDataDocDTO(); | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -196,22 +213,22 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    for (TestRecordSampleDataExpand expand : expandList) { | 
					 | 
					 | 
					 | 
					                    for (TestRecordSampleDataExpand expand : expandList) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        if (!expand.getBasePeak()) { | 
					 | 
					 | 
					 | 
					                        if (!expand.getBasePeak()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            stdVo.setPIonAbundanceRatio(expand.getIonAbundanceRatio() != null ? expand.getIonAbundanceRatio().setScale(2, RoundingMode.HALF_UP).toString() : "/"); | 
					 | 
					 | 
					 | 
					                            stdVo.setPIonAbundanceRatio(expand.getIonAbundanceRatio() != null ? expand.getIonAbundanceRatio().setScale(2, RoundingMode.HALF_UP).toString() : "/"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            stdVo.setPIonAbundanceRatioError("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					                            stdVo.setPIonAbundanceRatioError("/"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            stdVo.setPIonAbundanceRatioWithinError("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					                            stdVo.setPIonAbundanceRatioWithinError("/"); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            break; // 只取第一个符合条件的扩展数据
 | 
					 | 
					 | 
					 | 
					                            break; // 只取第一个符合条件的扩展数据
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        } | 
					 | 
					 | 
					 | 
					                        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    } | 
					 | 
					 | 
					 | 
					                    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                } | 
					 | 
					 | 
					 | 
					                } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                stdVo.setName("空白" + materialType + "加标"); | 
					 | 
					 | 
					 | 
					                stdVo.setName("空白" + materialType + "加标"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                stdVo.setPTargetRtTime(std.getTargetRtTime() != null ? String.format("%.2f", std.getTargetRtTime()) : "/"); | 
					 | 
					 | 
					 | 
					                stdVo.setPTargetRtTime(std.getTargetRtTime() != null ? String.format("%.2f", std.getTargetRtTime()) : "/"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                stdVo.setPRtTimeError("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					                stdVo.setPRtTimeError("/"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                stdVo.setPRtTimeWithinError("/"); // 设置空值为"/"
 | 
					 | 
					 | 
					 | 
					                stdVo.setPRtTimeWithinError("/"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                stdVo.setPIsDetected("是"); // 默认"是"
 | 
					 | 
					 | 
					 | 
					                stdVo.setPIsDetected("是"); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                stdVo.setCompoundName(compoundName); | 
					 | 
					 | 
					 | 
					                stdVo.setCompoundName(compoundName); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                dataDtos.add(stdVo); | 
					 | 
					 | 
					 | 
					                dataDtos.add(stdVo); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 处理检材样品(Analyte)
 | 
					 | 
					 | 
					 | 
					            // 12️⃣ 处理检材样品(Analyte)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (map.containsKey("Analyte")) { | 
					 | 
					 | 
					 | 
					            if (map.containsKey("Analyte")) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                List<TestRecordSampleData> analyte = map.get("Analyte"); | 
					 | 
					 | 
					 | 
					                List<TestRecordSampleData> analyte = map.get("Analyte"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                analyte.sort(testRecordSampleDataService.getSortBySampleNo()); | 
					 | 
					 | 
					 | 
					                analyte.sort(testRecordSampleDataService.getSortBySampleNo()); | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -227,24 +244,7 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    vo.setPRtTimeWithinError(item.getRtTimeWithinError() != null ? item.getRtTimeWithinError().toString() : "/"); | 
					 | 
					 | 
					 | 
					                    vo.setPRtTimeWithinError(item.getRtTimeWithinError() != null ? item.getRtTimeWithinError().toString() : "/"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    vo.setPIsDetected(item.getIsDetected() != null && item.getIsDetected() == 1 ? "是" : "否"); | 
					 | 
					 | 
					 | 
					                    vo.setPIsDetected(item.getIsDetected() != null && item.getIsDetected() == 1 ? "是" : "否"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    List<TestRecordSampleDataExpand> expandList = dataExpandMap.get(item.getId()); | 
					 | 
					 | 
					 | 
					                    vo.setName((dataList.stream().collect(Collectors.groupingBy(TestRecordSampleData::getSampleNo)).keySet().size() == 1) ? "检材样品" : (i + 1) + "号检材样品"); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    if (expandList != null) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        for (TestRecordSampleDataExpand expand : expandList) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            if (!expand.getBasePeak()) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                vo.setPIonAbundanceRatio(expand.getIonAbundanceRatio() != null ? expand.getIonAbundanceRatio().setScale(2, RoundingMode.HALF_UP).toString() : "/"); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                vo.setPIonAbundanceRatioError(expand.getIonAbundanceRatioError() != null ? expand.getIonAbundanceRatioError().setScale(2, RoundingMode.HALF_UP).toString() : "/"); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                vo.setPIonAbundanceRatioWithinError(expand.getIonAbundanceRatioWithinError() != null ? expand.getIonAbundanceRatioWithinError() : "/"); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                break; // 只取第一个符合条件的扩展数据
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    // 重新命名样品
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    vo.setName((dataList | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            .stream() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            .collect(Collectors.groupingBy(TestRecordSampleData::getSampleNo)) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            .keySet() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            .size() == 1) ? "检材样品" : (i + 1) + "号检材样品"); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    vo.setCompoundName(compoundName); | 
					 | 
					 | 
					 | 
					                    vo.setCompoundName(compoundName); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    dataVOS.add(vo); | 
					 | 
					 | 
					 | 
					                    dataVOS.add(vo); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                } | 
					 | 
					 | 
					 | 
					                } | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -252,28 +252,25 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 加入图谱序号值
 | 
					 | 
					 | 
					 | 
					        // 13️⃣ 为所有数据项添加序号
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        int indexNum = 1; | 
					 | 
					 | 
					 | 
					        int indexNum = 1; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (int i = 0; i < dataDtos.size(); i++) { | 
					 | 
					 | 
					 | 
					        for (TestRecordSampleDataDocDTO dto : dataDtos) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            dataDtos.get(i).setIndexNum(String.valueOf(indexNum++)); | 
					 | 
					 | 
					 | 
					            dto.setIndexNum(String.valueOf(indexNum++)); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.buildIonPairAndCE(data, testRecordReagentService | 
					 | 
					 | 
					 | 
					        // 14️⃣ 构建离子对和CE值数据
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .list(Wrappers.<TestRecordReagent>lambdaQuery() | 
					 | 
					 | 
					 | 
					        this.buildIonPairAndCE(data, testRecordReagentService.list( | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                        Wrappers.<TestRecordReagent>lambdaQuery() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) | 
					 | 
					 | 
					 | 
					                                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                .eq(TestRecordReagent::getCategory, "标准物质")) | 
					 | 
					 | 
					 | 
					                                .eq(TestRecordReagent::getCategory, "标准物质")) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .stream() | 
					 | 
					 | 
					 | 
					                .stream().map(TestRecordReagent::getId).collect(Collectors.toList())); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .map(item -> item.getId()) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .collect(Collectors.toList())); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 返回处理后的数据
 | 
					 | 
					 | 
					 | 
					        // 15️⃣ 返回最终处理的数据
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("dataDtos", dataDtos); | 
					 | 
					 | 
					 | 
					        data.put("dataDtos", dataDtos); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("type", "inVivo"); | 
					 | 
					 | 
					 | 
					        data.put("type", materialType); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("inspectOpinion", this.buildInspectOpinion(testRecordSampleDataService | 
					 | 
					 | 
					 | 
					        data.put("inspectOpinion", this.buildInspectOpinion(testRecordSampleDataService.lambdaQuery() | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .lambdaQuery() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getSampleType, "Analyte") | 
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getSampleType, "Analyte").list())); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .list())); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return data; | 
					 | 
					 | 
					 | 
					        return data; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -425,116 +422,163 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 9️⃣  返回处理后的数据
 | 
					 | 
					 | 
					 | 
					        // 9️⃣  返回处理后的数据
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("dataDtos", dataDtos); | 
					 | 
					 | 
					 | 
					        data.put("dataDtos", dataDtos); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("sampleSize", dataDtos.size()); | 
					 | 
					 | 
					 | 
					        data.put("sampleSize", dataDtos.size()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("type", "inVitro"); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        try { | 
					 | 
					 | 
					 | 
					        // 1️⃣0 添加检测结果与分析结果判定
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            Map<Integer, List<TestRecordSampleData>> isDetectedMap = testRecordSampleDataService | 
					 | 
					 | 
					 | 
					        List<TestRecordSampleData> sampleDataList = testRecordSampleDataService | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .lambdaQuery() | 
					 | 
					 | 
					 | 
					                .lambdaQuery() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getSampleType, "Analyte") | 
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getSampleType, "Analyte") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .list() | 
					 | 
					 | 
					 | 
					                .list(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .stream() | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .filter(Objects::nonNull) // 避免空值
 | 
					 | 
					 | 
					 | 
					        data.put("type", "常规毒品"); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        data.put("inspectOpinion", sampleDataList.size() > 0 ? this.buildInspectOpinion(sampleDataList) : ""); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        this.processDetectionResults(data, testRecord); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        return data; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 📌 处理检测结果并格式化 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param data       存储检测结果的 Map | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param testRecord 当前检测记录 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    public void processDetectionResults(Map<String, Object> data, TestRecord testRecord) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 查询所有 "Analyte" 类型的样本数据
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        List<TestRecordSampleData> analyteDataList = testRecordSampleDataService | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .lambdaQuery() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getSampleType, "Analyte") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .list(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        if (analyteDataList.isEmpty()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            return;  // 如果没有数据,直接返回
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 按 `isDetected` 分组,1 为检出,0 为未检出
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        Map<Integer, List<TestRecordSampleData>> isDetectedMap = analyteDataList.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .filter(Objects::nonNull) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .collect(Collectors.groupingBy(TestRecordSampleData::getIsDetected)); | 
					 | 
					 | 
					 | 
					                .collect(Collectors.groupingBy(TestRecordSampleData::getIsDetected)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            List<String> isDetectedStrs = new ArrayList<>(); | 
					 | 
					 | 
					 | 
					        List<String> isDetectedStr = new ArrayList<>(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            List<String> notDetectedStrs = new ArrayList<>(); | 
					 | 
					 | 
					 | 
					        List<String> notDetectedStr = new ArrayList<>(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 遍历检测结果,生成描述
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (Map.Entry<Integer, List<TestRecordSampleData>> entry : isDetectedMap.entrySet()) { | 
					 | 
					 | 
					 | 
					        for (Map.Entry<Integer, List<TestRecordSampleData>> entry : isDetectedMap.entrySet()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                if (entry.getKey() != null && entry.getValue() != null && !entry.getValue().isEmpty()) { | 
					 | 
					 | 
					 | 
					            List<TestRecordSampleData> samples = entry.getValue(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    List<TestRecordSampleData> value = entry.getValue(); | 
					 | 
					 | 
					 | 
					            if (samples == null || samples.isEmpty()) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    Map<String, List<TestRecordSampleData>> map = value.stream() | 
					 | 
					 | 
					 | 
					                continue;  // 如果样本数据为空,跳过
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            .filter(Objects::nonNull) | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            .filter(item -> item.getCompoundName() != null && item.getSampleNo() != null) | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // 按化合物名称分组样本
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            Map<String, List<TestRecordSampleData>> compoundSampleMap = samples.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                    .filter(sample -> sample.getCompoundCnName() != null && sample.getSampleNo() != null) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName)); | 
					 | 
					 | 
					 | 
					                    .collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    for (Map.Entry<String, List<TestRecordSampleData>> listEntry : map.entrySet()) { | 
					 | 
					 | 
					 | 
					            for (Map.Entry<String, List<TestRecordSampleData>> compoundEntry : compoundSampleMap.entrySet()) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        if (listEntry.getKey() != null && listEntry.getValue() != null && !listEntry.getValue().isEmpty()) { | 
					 | 
					 | 
					 | 
					                String compoundName = compoundEntry.getKey(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            List<TestRecordSampleData> entryValue = listEntry.getValue(); | 
					 | 
					 | 
					 | 
					                List<TestRecordSampleData> compoundSamples = compoundEntry.getValue(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            entryValue.sort(testRecordSampleDataService.getSortBySampleNo()); | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                if (compoundSamples == null || compoundSamples.isEmpty()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                    continue;  // 如果化合物名称或样本为空,跳过
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            String collect = entryValue.stream() | 
					 | 
					 | 
					 | 
					                // 按 `sampleNo` 排序样本
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                    .map(item -> { | 
					 | 
					 | 
					 | 
					                compoundSamples.sort(testRecordSampleDataService.getSortBySampleNo()); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                        String[] parts = item.getSampleNo().split("-"); | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                        return (parts.length > 2) ? parts[2] + "号" : item.getSampleNo(); // 预防数组越界
 | 
					 | 
					 | 
					 | 
					                // 拼接样本编号
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                String sampleNos = compoundSamples.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                        .map(sample -> { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                            String[] parts = sample.getSampleNo().split("-"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                            return (parts.length > 2) ? parts[2] + "号" : sample.getSampleNo();  // 防止数组越界
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        }) | 
					 | 
					 | 
					 | 
					                        }) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        .collect(Collectors.joining("、")); | 
					 | 
					 | 
					 | 
					                        .collect(Collectors.joining("、")); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                // 生成描述
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                String description; | 
					 | 
					 | 
					 | 
					                String description; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                if (entry.getKey() == 1) { | 
					 | 
					 | 
					 | 
					                if (entry.getKey() == 1) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                description = "☑ " + collect + "样品空白对照无干扰,在相同条件下进行测定时,样品溶液中目标物与浓度相近的标准工作溶液中的目标物相比,色谱峰保留时间一致、质谱特征离子一致,各离子丰度比相对偏差符合规定范围,判定样品中检出" + listEntry.getKey() + "。"; | 
					 | 
					 | 
					 | 
					                    description = String.format( | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                isDetectedStrs.add(description); | 
					 | 
					 | 
					 | 
					                            "☑ %s样品空白对照无干扰,在相同条件下进行测定时,样品溶液中目标物与浓度相近的标准工作溶液中的目标物相比," + | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                                    "色谱峰保留时间一致、质谱特征离子一致,各离子丰度比相对偏差符合规定范围,判定样品中检出%s。", | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                            sampleNos, compoundName); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                    isDetectedStr.add(description); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                } else { | 
					 | 
					 | 
					 | 
					                } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                description = "☑ " + collect + "样品空白对照无干扰,样品溶液中未检出符合阳性结果评价要求的色谱峰,标准溶液空白无干扰,判定样品中为未检出" + listEntry.getKey() + "。"; | 
					 | 
					 | 
					 | 
					                    description = String.format( | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                notDetectedStrs.add(description); | 
					 | 
					 | 
					 | 
					                            "☑ %s样品空白对照无干扰,样品溶液中未检出符合阳性结果评价要求的色谱峰,标准溶液空白无干扰,判定样品中未检出%s。", | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            } | 
					 | 
					 | 
					 | 
					                            sampleNos, compoundName); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        } | 
					 | 
					 | 
					 | 
					                    notDetectedStr.add(description); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                } | 
					 | 
					 | 
					 | 
					                } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (!isDetectedStrs.isEmpty()) { | 
					 | 
					 | 
					 | 
					        // 将阳性结果和阴性结果放入数据 Map 中
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                data.put("isDetectedStrs", "阳性结果:" + "\n" + isDetectedStrs.stream() | 
					 | 
					 | 
					 | 
					        if (!isDetectedStr.isEmpty()) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        .map(item -> "\u3000\u3000" + item) | 
					 | 
					 | 
					 | 
					            data.put("isDetectedStrs", "阳性结果:" + "\n" + isDetectedStr.stream() | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                    .map(item -> "  " + item)  // 使用全角空格缩进
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .collect(Collectors.joining("\n"))); | 
					 | 
					 | 
					 | 
					                    .collect(Collectors.joining("\n"))); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (!notDetectedStrs.isEmpty()) { | 
					 | 
					 | 
					 | 
					        if (!notDetectedStr.isEmpty()) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                data.put("notDetectedStrs", "阴性结果:" + "\n" + notDetectedStrs.stream() | 
					 | 
					 | 
					 | 
					            data.put("notDetectedStrs", "阴性结果:" + "\n" + notDetectedStr.stream() | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        .map(item -> "\u3000\u3000" + item) | 
					 | 
					 | 
					 | 
					                    .map(item -> "  " + item)  // 使用全角空格缩进
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .collect(Collectors.joining("\n"))); | 
					 | 
					 | 
					 | 
					                    .collect(Collectors.joining("\n"))); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } catch (Exception e) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<TestRecordSampleData> sampleDataList = testRecordSampleDataService | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .lambdaQuery() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .eq(TestRecordSampleData::getSampleType, "Analyte") | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .list(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					// 添加非空校验,防止传入 `null` 进入 `buildInspectOpinion`
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("inspectOpinion", sampleDataList.size() > 0 ? this.buildInspectOpinion(sampleDataList) : ""); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return data; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 📌 构建离子对和碰撞能量信息,并存入数据 Map | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param data          存储结果的 Map | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param reagentIdList 试剂 ID 列表 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    public void buildIonPairAndCE(Map<String, Object> data, List<String> reagentIdList) { | 
					 | 
					 | 
					 | 
					    public void buildIonPairAndCE(Map<String, Object> data, List<String> reagentIdList) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<IonPairAndCEVO> ionPairAndCEVOS = new ArrayList<>(); | 
					 | 
					 | 
					 | 
					        List<IonPairAndCEVO> ionPairAndCEVOS = new ArrayList<>(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 遍历 reagentIdList
 | 
					 | 
					 | 
					 | 
					        // 🔄 遍历 reagentIdList 获取试剂信息
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (String id : reagentIdList) { | 
					 | 
					 | 
					 | 
					        for (String id : reagentIdList) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            TestRecordReagentVO vo = testRecordReagentService.getVOById(id); | 
					 | 
					 | 
					 | 
					            TestRecordReagentVO vo = testRecordReagentService.getVOById(id); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (vo == null) { | 
					 | 
					 | 
					 | 
					            if (vo == null) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                continue;  // 如果 vo 为空,跳过当前循环
 | 
					 | 
					 | 
					 | 
					                continue;  // ⏭️ 如果 vo 为空,跳过当前循环
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            Drug drug = vo.getDrug(); | 
					 | 
					 | 
					 | 
					            Drug drug = vo.getDrug(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (drug == null) { | 
					 | 
					 | 
					 | 
					            if (drug == null) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                continue;  // 如果 drug 为空,跳过当前循环
 | 
					 | 
					 | 
					 | 
					                continue;  // ⏭️ 如果 drug 为空,跳过当前循环
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 添加 ionPairAndCEVO1 和 ionPairAndCEVO2
 | 
					 | 
					 | 
					 | 
					            // 📌 创建主产物离子信息并添加到列表
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            ionPairAndCEVOS.add(createIonPairAndCEVO(drug.getName(), drug.getMainProductIon(), | 
					 | 
					 | 
					 | 
					            ionPairAndCEVOS.add(createIonPairAndCEVO(drug.getName(), drug.getMainProductIon(), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    drug.getMainDeclusteringPotential(), drug.getMainCollisionEnergy())); | 
					 | 
					 | 
					 | 
					                    drug.getMainDeclusteringPotential(), drug.getMainCollisionEnergy())); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // 📌 创建次产物离子信息并添加到列表
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            ionPairAndCEVOS.add(createIonPairAndCEVO(drug.getName(), drug.getMinorProductIon(), | 
					 | 
					 | 
					 | 
					            ionPairAndCEVOS.add(createIonPairAndCEVO(drug.getName(), drug.getMinorProductIon(), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    drug.getMinorDeclusteringPotential(), drug.getMinorCollisionEnergy())); | 
					 | 
					 | 
					 | 
					                    drug.getMinorDeclusteringPotential(), drug.getMinorCollisionEnergy())); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 将结果放入数据 map 中
 | 
					 | 
					 | 
					 | 
					        // 📌 将结果放入数据 map 中,供后续使用
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("ionPairAndCEVOS", ionPairAndCEVOS); | 
					 | 
					 | 
					 | 
					        data.put("ionPairAndCEVOS", ionPairAndCEVOS); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        data.put("ionPairAndCEVOSize", ionPairAndCEVOS.size() / 2); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // 创建 IonPairAndCEVO 对象的辅助方法
 | 
					 | 
					 | 
					 | 
					    // 创建 IonPairAndCEVO 对象的辅助方法
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 📌 创建 IonPairAndCEVO 对象并初始化属性 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param compoundName          化合物名称 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param productIon            产物离子 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param declusteringPotential 去簇电位(DP) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param collisionEnergy       碰撞能量(CE) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @return 初始化后的 IonPairAndCEVO 对象 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    private IonPairAndCEVO createIonPairAndCEVO(String compoundName, String productIon, | 
					 | 
					 | 
					 | 
					    private IonPairAndCEVO createIonPairAndCEVO(String compoundName, String productIon, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                Integer declusteringPotential, Integer collisionEnergy) { | 
					 | 
					 | 
					 | 
					                                                Integer declusteringPotential, Integer collisionEnergy) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        IonPairAndCEVO ionPairAndCEVO = new IonPairAndCEVO(); | 
					 | 
					 | 
					 | 
					        IonPairAndCEVO ionPairAndCEVO = new IonPairAndCEVO(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setCompoundName(compoundName); | 
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setCompoundName(compoundName); // 设置化合物名称
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setProductIon(productIon); | 
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setProductIon(productIon); // 设置产物离子
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setDeclusteringPotential(declusteringPotential); | 
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setDeclusteringPotential(declusteringPotential); // 设置去簇电位(DP)
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setCollisionEnergy(collisionEnergy); | 
					 | 
					 | 
					 | 
					        ionPairAndCEVO.setCollisionEnergy(collisionEnergy); // 设置碰撞能量(CE)
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return ionPairAndCEVO; | 
					 | 
					 | 
					 | 
					        return ionPairAndCEVO; // 返回创建的对象
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -556,7 +600,6 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } else { | 
					 | 
					 | 
					 | 
					        } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            templatePath = "/template" + "/贵阳生物样本尿液检验记录模板.docx"; | 
					 | 
					 | 
					 | 
					            templatePath = "/template" + "/贵阳生物样本尿液检验记录模板.docx"; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        System.out.println(data); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); | 
					 | 
					 | 
					 | 
					        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Configure config = Configure.builder(). | 
					 | 
					 | 
					 | 
					        Configure config = Configure.builder(). | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                bind("dataDtos", policy) | 
					 | 
					 | 
					 | 
					                bind("dataDtos", policy) | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -565,106 +608,134 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    /** | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * 根据委托信息、模板路径、配置和数据构建文档文件并上传到OSS | 
					 | 
					 | 
					 | 
					     * 📄 构建 Word 文档并上传至 OSS | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @param entrustId    委托id | 
					 | 
					 | 
					 | 
					     * @param entrustId    委托 ID | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @param templatePath 模板文件路径 | 
					 | 
					 | 
					 | 
					     * @param templatePath 模板文件路径 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @param config       配置信息 | 
					 | 
					 | 
					 | 
					     * @param config       配置对象 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @param data         要填充到模板中的数据 | 
					 | 
					 | 
					 | 
					     * @param data         数据映射 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @return 上传后的文件路径 | 
					 | 
					 | 
					 | 
					     * @return 上传到 OSS 的文件路径 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @throws Exception 可能抛出的异常 | 
					 | 
					 | 
					 | 
					     * @throws Exception 可能抛出的异常 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     */ | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    private String buildDocFileAndUploadToOss(String entrustId, String templatePath, Configure config, Map<String, Object> data) throws Exception { | 
					 | 
					 | 
					 | 
					    private String buildDocFileAndUploadToOss(String entrustId, String templatePath, Configure config, Map<String, Object> data) throws Exception { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 📌 读取模板文件
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 
					 | 
					 | 
					 | 
					        ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ossFile.fileGet(templatePath, bos); | 
					 | 
					 | 
					 | 
					        ossFile.fileGet(templatePath, bos); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        byte[] templateArray = bos.toByteArray(); | 
					 | 
					 | 
					 | 
					        byte[] templateArray = bos.toByteArray(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        bos.close(); | 
					 | 
					 | 
					 | 
					        bos.close(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        try (ByteArrayInputStream bis = new ByteArrayInputStream(templateArray); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					             ByteArrayOutputStream fosWord = new ByteArrayOutputStream()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // 📌 渲染模板数据
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            XWPFTemplate template = XWPFTemplate.compile(bis, config).render(data); | 
					 | 
					 | 
					 | 
					            XWPFTemplate template = XWPFTemplate.compile(bis, config).render(data); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            NiceXWPFDocument document = template.getXWPFDocument(); | 
					 | 
					 | 
					 | 
					            NiceXWPFDocument document = template.getXWPFDocument(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            List<XWPFTable> tables = document.getTables(); | 
					 | 
					 | 
					 | 
					            List<XWPFTable> tables = document.getTables(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // 📌 读取数据类型并安全处理
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            String type = (String) data.getOrDefault("type", ""); // 避免空指针
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            XWPFTable table = tables.get(tables.size() - 1); // 获取最后一个表格
 | 
					 | 
					 | 
					 | 
					            XWPFTable table = tables.get(tables.size() - 1); // 获取最后一个表格
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            if ("常规毒品".equals(type)) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                mergeRegularDrugTable(table); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            } else if ("尿液".equals(type) || "毛发".equals(type)) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                mergeNonRegularDrugTable(table); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                mergeAdditionalTable(type, tables); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // 📌 写入文档流
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            template.write(fosWord); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            template.close(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            document.close(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // 📌 构建文件存储路径
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            String path = TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + entrustId + "/" + "贵阳生物样本检验记录.docx"; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            ossFile.fileSave(path, fisWord); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        String type = (String) data.get("type"); | 
					 | 
					 | 
					 | 
					            return path; // 📌 返回 OSS 文件路径
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (StringUtils.isNotBlank(type) && type.equals("inVitro")) { | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            int startRow = 1; // 从第二行开始(表头是第一行,索引从0开始)
 | 
					 | 
					 | 
					 | 
					     * 📌 合并常规毒品检验记录检测数据部分表格的单元格 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // **处理合并单元格**
 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            int mergeStep = 4; // 每4行合并一次
 | 
					 | 
					 | 
					 | 
					    private void mergeRegularDrugTable(XWPFTable table) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            int[] mergeColumns = {0, 1, 2, 3, 4, 9}; // 合并的列:第1、2、3、4、5、10列,索引分别是 0, 1, 2, 3, 4, 9
 | 
					 | 
					 | 
					 | 
					        int startRow = 1; // 🔹 从第二行开始(索引从0开始)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        int mergeStep = 4; // 🔹 每 4 行合并一次
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        int[] mergeColumns = {0, 1, 2, 3, 4, 9}; // 🔹 需要合并的列
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 遍历表格数据行
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (int rowIndex = startRow; rowIndex < table.getNumberOfRows(); rowIndex += mergeStep) { | 
					 | 
					 | 
					 | 
					        for (int rowIndex = startRow; rowIndex < table.getNumberOfRows(); rowIndex += mergeStep) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                int endRow = Math.min(rowIndex + mergeStep - 1, table.getNumberOfRows() - 1); // 计算合并的终止行,防止越界
 | 
					 | 
					 | 
					 | 
					            int endRow = Math.min(rowIndex + mergeStep - 1, table.getNumberOfRows() - 1); // 防止越界
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            for (int col : mergeColumns) { | 
					 | 
					 | 
					 | 
					            for (int col : mergeColumns) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                TableTools.mergeCellsVertically(table, col, rowIndex, endRow); | 
					 | 
					 | 
					 | 
					                TableTools.mergeCellsVertically(table, col, rowIndex, endRow); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } else { | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // **处理合并单元格**
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            int startRow = 1; // 从第二行开始(表头是第一行,索引从0开始)
 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 📌 合并生物样本检验记录检测数据部分表格的单元格 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    private void mergeNonRegularDrugTable(XWPFTable table) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        int startRow = 1; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        String currentCompoundName = null; | 
					 | 
					 | 
					 | 
					        String currentCompoundName = null; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        int mergeStartRow = -1; | 
					 | 
					 | 
					 | 
					        int mergeStartRow = -1; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (int rowIndex = startRow; rowIndex < table.getNumberOfRows(); rowIndex++) { | 
					 | 
					 | 
					 | 
					        for (int rowIndex = startRow; rowIndex < table.getNumberOfRows(); rowIndex++) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            XWPFTableRow row = table.getRow(rowIndex); | 
					 | 
					 | 
					 | 
					            XWPFTableRow row = table.getRow(rowIndex); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                XWPFTableCell cell = row.getCell(1); // 获取第二列的单元格(索引1)
 | 
					 | 
					 | 
					 | 
					            XWPFTableCell cell = row.getCell(1); // 🔹 第二列(索引1)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String compoundName = cell.getText().trim(); | 
					 | 
					 | 
					 | 
					            String compoundName = cell.getText().trim(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                if (currentCompoundName == null || !currentCompoundName.equals(compoundName)) { | 
					 | 
					 | 
					 | 
					            if (!compoundName.equals(currentCompoundName)) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    // 如果当前化合物名称与上一行不同,则结束之前的合并
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                if (mergeStartRow >= 0 && rowIndex > mergeStartRow + 1) { | 
					 | 
					 | 
					 | 
					                if (mergeStartRow >= 0 && rowIndex > mergeStartRow + 1) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        TableTools.mergeCellsVertically(table, 1, mergeStartRow, rowIndex - 1); // 合并第二列(列索引1)
 | 
					 | 
					 | 
					 | 
					                    TableTools.mergeCellsVertically(table, 1, mergeStartRow, rowIndex - 1); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                } | 
					 | 
					 | 
					 | 
					                } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                currentCompoundName = compoundName; | 
					 | 
					 | 
					 | 
					                currentCompoundName = compoundName; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                mergeStartRow = rowIndex; | 
					 | 
					 | 
					 | 
					                mergeStartRow = rowIndex; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 处理最后一组合并(循环结束后可能残留未合并的区域)
 | 
					 | 
					 | 
					 | 
					        // 📌 处理最后一组合并
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (mergeStartRow >= 0 && mergeStartRow < table.getNumberOfRows() - 1) { | 
					 | 
					 | 
					 | 
					        if (mergeStartRow >= 0 && mergeStartRow < table.getNumberOfRows() - 1) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            TableTools.mergeCellsVertically(table, 1, mergeStartRow, table.getNumberOfRows() - 1); | 
					 | 
					 | 
					 | 
					            TableTools.mergeCellsVertically(table, 1, mergeStartRow, table.getNumberOfRows() - 1); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        System.out.println("总共的表格数量:" + tables.size()); | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        XWPFTable xwpfTable = tables.get(2); | 
					 | 
					 | 
					 | 
					     * 📌 处理毛发或尿液表格目标物定性离子对、碰撞能量 (CE)等参数表格的合并逻辑 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 📌 因为毛发和尿液的配置表格数量不同,因此下标不同 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    private void mergeAdditionalTable(String type, List<XWPFTable> tables) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        if (!"毛发".equals(type) && !"尿液".equals(type)) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            return; // 如果不是毛发或尿液类型,则不处理
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        int tableIndex = "毛发".equals(type) ? 2 : 1; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        if (tables.size() <= tableIndex) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            return; // 避免索引越界
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        XWPFTable xwpfTable = tables.get(tableIndex); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<XWPFTableRow> rows = xwpfTable.getRows(); | 
					 | 
					 | 
					 | 
					        List<XWPFTableRow> rows = xwpfTable.getRows(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // 跳过第一行(表头),从第二行开始处理
 | 
					 | 
					 | 
					 | 
					        for (int i = 1; i < rows.size() - 1; i++) { // 🔹 从第二行开始
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (int i = 1; i < rows.size() - 1; i++) { // 注意:i 从 1 开始
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            XWPFTableRow currentRow = rows.get(i); | 
					 | 
					 | 
					 | 
					            XWPFTableRow currentRow = rows.get(i); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            XWPFTableRow nextRow = rows.get(i + 1); | 
					 | 
					 | 
					 | 
					            XWPFTableRow nextRow = rows.get(i + 1); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 获取当前行和下一行的化合物名称
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String currentCompoundName = currentRow.getCell(0).getText().trim(); | 
					 | 
					 | 
					 | 
					            String currentCompoundName = currentRow.getCell(0).getText().trim(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String nextCompoundName = nextRow.getCell(0).getText().trim(); | 
					 | 
					 | 
					 | 
					            String nextCompoundName = nextRow.getCell(0).getText().trim(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // 如果当前行和下一行的化合物名称相同,则合并它们的第一列
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (currentCompoundName.equals(nextCompoundName)) { | 
					 | 
					 | 
					 | 
					            if (currentCompoundName.equals(nextCompoundName)) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                // 合并单元格
 | 
					 | 
					 | 
					 | 
					                // 📌 合并单元格
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                currentRow.getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge.RESTART); | 
					 | 
					 | 
					 | 
					                currentRow.getCell(0).getCTTc().addNewTcPr().addNewVMerge() | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                nextRow.getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge.CONTINUE); | 
					 | 
					 | 
					 | 
					                        .setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge.RESTART); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                nextRow.getCell(0).getCTTc().addNewTcPr().addNewVMerge() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                        .setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge.CONTINUE); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                // 删除下一行的第一个单元格的内容(可选)
 | 
					 | 
					 | 
					 | 
					                // 📌 清空被合并行的内容
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                nextRow.getCell(0).setText(""); | 
					 | 
					 | 
					 | 
					                nextRow.getCell(0).setText(""); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        bis.close(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ByteArrayOutputStream fosWord = new ByteArrayOutputStream(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        template.write(fosWord); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        template.close(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray()); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        fosWord.close(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        document.close(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        String path = TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + entrustId + "/" + "贵阳生物样本检验记录.docx"; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ossFile.fileSave(path, fisWord); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return path; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -769,22 +840,23 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) | 
					 | 
					 | 
					 | 
					                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .eq(TestRecordReagent::getCategory, "标准物质")); | 
					 | 
					 | 
					 | 
					                .eq(TestRecordReagent::getCategory, "标准物质")); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (references == null || references.isEmpty()) { | 
					 | 
					 | 
					 | 
					        if (references == null || references.isEmpty()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            data.put("referenceMaterialName", "未找到标准物质数据!"); | 
					 | 
					 | 
					 | 
					            data.put("referenceMaterialName", "未找到标准物质数据!"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } else { | 
					 | 
					 | 
					 | 
					        } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String referenceMaterialName = ""; | 
					 | 
					 | 
					 | 
					            String referenceMaterialName = ""; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (references.size() == 1) { | 
					 | 
					 | 
					 | 
					            if (references.size() == 1) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                referenceMaterialName = "\u2611" + " " + references.get(0).getReagentConsumableName(); | 
					 | 
					 | 
					 | 
					                referenceMaterialName = "\u2611" + " " + references.get(0).getReagentConsumableName(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } else { | 
					 | 
					 | 
					 | 
					            } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                TestRecordReagent firstReagent = references.get(0); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                String firstStr = "\u2611" + " " + references.get(0).getReagentConsumableName() + "\n"; | 
					 | 
					 | 
					 | 
					                String firstStr = "\u2611" + " " + references.get(0).getReagentConsumableName() + "\n"; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                references.remove(references.get(0)); | 
					 | 
					 | 
					 | 
					                references.remove(references.get(0)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                referenceMaterialName = firstStr + references.stream() | 
					 | 
					 | 
					 | 
					                referenceMaterialName = firstStr + references.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        .map(reagent -> "\u3000\u3000\u2611" + " " + reagent.getReagentConsumableName()) | 
					 | 
					 | 
					 | 
					                        .map(reagent -> "\u3000\u3000\u2611" + " " + reagent.getReagentConsumableName()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        .collect(Collectors.joining("\n")); | 
					 | 
					 | 
					 | 
					                        .collect(Collectors.joining("\n")); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                references.add(0, firstReagent); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            data.put("referenceMaterialName", referenceMaterialName); | 
					 | 
					 | 
					 | 
					            data.put("referenceMaterialName", referenceMaterialName); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            data.put("materialIngredient", references.stream() | 
					 | 
					 | 
					 | 
					            data.put("materialIngredient", references.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .map(reagent -> reagent.getReagentConsumableName()) | 
					 | 
					 | 
					 | 
					                    .map(reagent -> reagent.getReagentConsumableName()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    .collect(Collectors.joining("、"))); | 
					 | 
					 | 
					 | 
					                    .collect(Collectors.joining("、"))); | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -896,10 +968,29 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     */ | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    private int extractTailNumber(String orderNo) { | 
					 | 
					 | 
					 | 
					    private int extractTailNumber(String orderNo) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        String[] parts = orderNo.split("-"); | 
					 | 
					 | 
					 | 
					        String[] parts = orderNo.split("-"); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (parts.length > 0) { | 
					 | 
					 | 
					 | 
					        return (parts.length > 0) ? Integer.parseInt(parts[parts.length - 1]) : 0; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            return Integer.parseInt(parts[parts.length - 1]); // 取最后一段
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return 0; | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 对样品编号进行排序 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param sampleNumbers 样品编号列表,例如 ["3号", "1号", "2号"] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @return 排序后的样品编号列表,例如 ["1号", "2号", "3号"] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    private List<String> sortSampleNumbers(List<String> sampleNumbers) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        return sampleNumbers.stream() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .sorted(Comparator.comparingInt(this::extractNumber)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                .collect(Collectors.toList()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 从样品编号中提取数值部分 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param sampleNumber 样品编号,例如 "12号" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @return 提取出的数字,例如 12 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    private int extractNumber(String sampleNumber) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        return sampleNumber.matches("\\d+号") ? Integer.parseInt(sampleNumber.replace("号", "")) : Integer.MAX_VALUE; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    /** | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -907,48 +998,74 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * - 单个编号:返回 "1号" | 
					 | 
					 | 
					 | 
					     * - 单个编号:返回 "1号" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * - 连续编号:返回 "1号至3号" | 
					 | 
					 | 
					 | 
					     * - 连续编号:返回 "1号至3号" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @param start 起始编号 | 
					 | 
					 | 
					 | 
					     * @param sortedSampleNumbers 已排序的样品编号列表 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @param end   结束编号 | 
					 | 
					 | 
					 | 
					     * @return 格式化的编号字符串 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     * @return 格式化的编号字符串(如果 start == end,返回单号) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					     */ | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    private String formatRange(int start, int end) { | 
					 | 
					 | 
					 | 
					    private String formatSampleNumbers(List<String> sortedSampleNumbers) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return (start == end) ? start + "号" : start + "号至" + end + "号"; | 
					 | 
					 | 
					 | 
					        List<Integer> numbers = sortedSampleNumbers.stream() | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    private String formatSampleNumbers(List<String> sampleNumbers) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<Integer> numbers = sampleNumbers.stream() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .map(s -> s.replace("号", "")) // 移除"号"字
 | 
					 | 
					 | 
					 | 
					                .map(s -> s.replace("号", "")) // 移除"号"字
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .map(Integer::parseInt) // 转换为整数
 | 
					 | 
					 | 
					 | 
					                .map(Integer::parseInt) // 转换为整数
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .sorted() // 排序
 | 
					 | 
					 | 
					 | 
					                .sorted() // 排序
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                .collect(Collectors.toList()); | 
					 | 
					 | 
					 | 
					                .collect(Collectors.toList()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (numbers.size() > 1 && numbers.get(numbers.size() - 1) - numbers.get(0) == numbers.size() - 1) { | 
					 | 
					 | 
					 | 
					        List<String> formattedList = new ArrayList<>(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            return numbers.get(0) + "至" + numbers.get(numbers.size() - 1) + "号"; | 
					 | 
					 | 
					 | 
					        int start = numbers.get(0), prev = start; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for (int i = 1; i < numbers.size(); i++) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            int current = numbers.get(i); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            if (current != prev + 1) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                formattedList.add(formatRange(start, prev)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                start = current; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            prev = current; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        formattedList.add(formatRange(start, prev)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return String.join("、", sampleNumbers); | 
					 | 
					 | 
					 | 
					        return String.join("、", formattedList); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 生成编号范围的字符串 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param start 起始编号 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param end   结束编号 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @return 生成的范围字符串,例如 "1号至3号" 或 "5号" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    private String formatRange(int start, int end) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        return (start == end) ? start + "号" : start + "号至" + end + "号"; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    /** | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * 构建检测意见,根据样品的检出与未检出情况,生成描述性文本。 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @param dataList 检测记录数据列表,每个对象包含样品编号、化合物名称及是否检出标识 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     * @return 格式化的检测意见字符串 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					     */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    public String buildInspectOpinion(List<TestRecordSampleData> dataList) { | 
					 | 
					 | 
					 | 
					    public String buildInspectOpinion(List<TestRecordSampleData> dataList) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 1. **处理空数据情况**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (dataList == null || dataList.isEmpty()) { | 
					 | 
					 | 
					 | 
					        if (dataList == null || dataList.isEmpty()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            return ""; | 
					 | 
					 | 
					 | 
					            return ""; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Map<String, List<String>> detectedMap = new LinkedHashMap<>(); | 
					 | 
					 | 
					 | 
					        // 2. **定义存储结构**
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Map<String, List<String>> notDetectedMap = new LinkedHashMap<>(); | 
					 | 
					 | 
					 | 
					        Map<String, List<String>> detectedMap = new LinkedHashMap<>();  // 记录检出化合物的样品编号映射(样品编号 -> 该样品检出的化合物列表)
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Set<String> allCompoundNames = new HashSet<>(); | 
					 | 
					 | 
					 | 
					        Map<String, List<String>> notDetectedMap = new LinkedHashMap<>();  // 记录未检出化合物的样品编号映射(样品编号 -> 该样品未检出的化合物列表)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        Set<String> allCompoundNames = new HashSet<>();  // 存储所有涉及的化合物名称(用于后续分析)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 3. **遍历数据列表,整理样品的检出与未检出情况**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (TestRecordSampleData record : dataList) { | 
					 | 
					 | 
					 | 
					        for (TestRecordSampleData record : dataList) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String sampleNo = record.getSampleNo(); | 
					 | 
					 | 
					 | 
					            String sampleNo = record.getSampleNo();  // 样品编号
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String compoundName = record.getCompoundCnName(); | 
					 | 
					 | 
					 | 
					            String compoundName = record.getCompoundCnName();  // 化合物名称
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // **跳过无效数据**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (sampleNo == null || compoundName == null || sampleNo.trim().isEmpty()) { | 
					 | 
					 | 
					 | 
					            if (sampleNo == null || compoundName == null || sampleNo.trim().isEmpty()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                continue; | 
					 | 
					 | 
					 | 
					                continue; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // **记录化合物名称**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            allCompoundNames.add(compoundName); | 
					 | 
					 | 
					 | 
					            allCompoundNames.add(compoundName); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // **分类存储检出与未检出数据**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (record.getIsDetected() != null && record.getIsDetected() == 1) { | 
					 | 
					 | 
					 | 
					            if (record.getIsDetected() != null && record.getIsDetected() == 1) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                detectedMap.computeIfAbsent(sampleNo, k -> new ArrayList<>()).add(compoundName); | 
					 | 
					 | 
					 | 
					                detectedMap.computeIfAbsent(sampleNo, k -> new ArrayList<>()).add(compoundName); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } else { | 
					 | 
					 | 
					 | 
					            } else { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -956,56 +1073,70 @@ public class InspectRecordServiceImpl implements InspectRecordService { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 4. **定义样品编号格式化方法**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 目标是将形如 "A12"、"B34" 这样的样品编号转换成 "12号"、"34号"
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Function<String, String> extractSampleNumber = sampleNo -> sampleNo.matches(".*\\d+") | 
					 | 
					 | 
					 | 
					        Function<String, String> extractSampleNumber = sampleNo -> sampleNo.matches(".*\\d+") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                ? sampleNo.replaceAll(".*?(\\d+)$", "$1号") | 
					 | 
					 | 
					 | 
					                ? sampleNo.replaceAll(".*?(\\d+)$", "$1号")  // 只提取最后的数字并添加 "号"
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                : sampleNo; | 
					 | 
					 | 
					 | 
					                : sampleNo; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 5. **构建检出化合物的描述**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<String> detectedSentences = new ArrayList<>(); | 
					 | 
					 | 
					 | 
					        List<String> detectedSentences = new ArrayList<>(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Map<Set<String>, List<String>> groupedDetectedSamples = new LinkedHashMap<>(); | 
					 | 
					 | 
					 | 
					        Map<Set<String>, List<String>> groupedDetectedSamples = new LinkedHashMap<>();  // 用于按相同化合物组合分组样品
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // **分组整理检出样品**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (Map.Entry<String, List<String>> entry : detectedMap.entrySet()) { | 
					 | 
					 | 
					 | 
					        for (Map.Entry<String, List<String>> entry : detectedMap.entrySet()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            Set<String> compoundSet = new HashSet<>(entry.getValue()); | 
					 | 
					 | 
					 | 
					            Set<String> compoundSet = new HashSet<>(entry.getValue());  // 将检出的化合物集合化(去重)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            groupedDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(entry.getKey())); | 
					 | 
					 | 
					 | 
					            groupedDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(entry.getKey())); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // **遍历分组后的数据,生成描述文本**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (Map.Entry<Set<String>, List<String>> entry : groupedDetectedSamples.entrySet()) { | 
					 | 
					 | 
					 | 
					        for (Map.Entry<Set<String>, List<String>> entry : groupedDetectedSamples.entrySet()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String sampleNumbers = formatSampleNumbers(entry.getValue()); // 这里也应用连续编号格式化
 | 
					 | 
					 | 
					 | 
					            List<String> sortedSampleNumbers = sortSampleNumbers(entry.getValue());  // 对样品编号排序
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String compounds = String.join("、", entry.getKey()); | 
					 | 
					 | 
					 | 
					            String sampleNumbers = formatSampleNumbers(sortedSampleNumbers);  // 处理编号连续情况
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            String compounds = String.join("、", entry.getKey());  // 多个化合物用 "、" 连接
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (entry.getValue().size() > 1) { | 
					 | 
					 | 
					 | 
					            if (sortedSampleNumbers.size() > 1) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                detectedSentences.add(String.format("从%s检材样品中均检出%s成分", sampleNumbers, compounds)); | 
					 | 
					 | 
					 | 
					                detectedSentences.add(String.format("从%s检材样品中均检出%s成分", sampleNumbers, compounds)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } else { | 
					 | 
					 | 
					 | 
					            } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                detectedSentences.add(String.format("从%s检材样品中检出%s成分", sampleNumbers, compounds)); | 
					 | 
					 | 
					 | 
					                detectedSentences.add(String.format("从%s检材样品中检出%s成分", sampleNumbers, compounds)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 6. **构建未检出化合物的描述**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<String> notDetectedSentences = new ArrayList<>(); | 
					 | 
					 | 
					 | 
					        List<String> notDetectedSentences = new ArrayList<>(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Map<Set<String>, List<String>> groupedNotDetectedSamples = new LinkedHashMap<>(); | 
					 | 
					 | 
					 | 
					        Map<Set<String>, List<String>> groupedNotDetectedSamples = new LinkedHashMap<>();  // 用于按相同化合物组合分组未检出样品
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // **分组整理未检出样品**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (Map.Entry<String, List<String>> entry : notDetectedMap.entrySet()) { | 
					 | 
					 | 
					 | 
					        for (Map.Entry<String, List<String>> entry : notDetectedMap.entrySet()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String sampleNo = entry.getKey(); | 
					 | 
					 | 
					 | 
					            String sampleNo = entry.getKey(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            Set<String> compoundSet = new HashSet<>(entry.getValue()); | 
					 | 
					 | 
					 | 
					            Set<String> compoundSet = new HashSet<>(entry.getValue()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // 只有当该样品编号未出现在 detectedMap(即没有检出任何化合物)时,才记录未检出的情况
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (!detectedMap.containsKey(sampleNo)) { | 
					 | 
					 | 
					 | 
					            if (!detectedMap.containsKey(sampleNo)) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                groupedNotDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(sampleNo)); | 
					 | 
					 | 
					 | 
					                groupedNotDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(sampleNo)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // **遍历分组后的数据,生成描述文本**
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (Map.Entry<Set<String>, List<String>> entry : groupedNotDetectedSamples.entrySet()) { | 
					 | 
					 | 
					 | 
					        for (Map.Entry<Set<String>, List<String>> entry : groupedNotDetectedSamples.entrySet()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String sampleNumbers = formatSampleNumbers(entry.getValue()); // 这里也应用连续编号格式化
 | 
					 | 
					 | 
					 | 
					            List<String> sortedSampleNumbers = sortSampleNumbers(entry.getValue());  // 对样品编号排序
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            String compounds = String.join("、", entry.getKey()); | 
					 | 
					 | 
					 | 
					            String sampleNumbers = formatSampleNumbers(sortedSampleNumbers);  // 处理编号连续情况
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            String compounds = String.join("、", entry.getKey());  // 多个化合物用 "、" 连接
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (entry.getValue().size() > 1) { | 
					 | 
					 | 
					 | 
					            if (sortedSampleNumbers.size() > 1) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                notDetectedSentences.add(String.format("从%s检材样品中均未检出%s成分", sampleNumbers, compounds)); | 
					 | 
					 | 
					 | 
					                notDetectedSentences.add(String.format("从%s检材样品中均未检出%s成分", sampleNumbers, compounds)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } else { | 
					 | 
					 | 
					 | 
					            } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                notDetectedSentences.add(String.format("从%s检材样品中未检出%s成分", sampleNumbers, compounds)); | 
					 | 
					 | 
					 | 
					                notDetectedSentences.add(String.format("从%s检材样品中未检出%s成分", sampleNumbers, compounds)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        List<String> finalSentences = new ArrayList<>(detectedSentences); | 
					 | 
					 | 
					 | 
					        // 7. **最终结果合并**
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        finalSentences.addAll(notDetectedSentences); | 
					 | 
					 | 
					 | 
					        List<String> finalSentences = new ArrayList<>(detectedSentences);  // 先添加检出的描述
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        finalSentences.addAll(notDetectedSentences);  // 再添加未检出的描述
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // 使用 ";" 连接所有描述,并返回最终的检测意见字符串
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return String.join(";", finalSentences); | 
					 | 
					 | 
					 | 
					        return String.join(";", finalSentences); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
					 | 
					 | 
					
  |