|
|
@ -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( |
|
|
|
.in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) |
|
|
|
Wrappers.<TestRecordReagent>lambdaQuery() |
|
|
|
.eq(TestRecordReagent::getCategory, "标准物质")) |
|
|
|
.in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) |
|
|
|
.stream() |
|
|
|
.eq(TestRecordReagent::getCategory, "标准物质")) |
|
|
|
.map(item -> item.getId()) |
|
|
|
.stream().map(TestRecordReagent::getId).collect(Collectors.toList())); |
|
|
|
.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 { |
|
|
|
|
|
|
|
Map<Integer, List<TestRecordSampleData>> isDetectedMap = testRecordSampleDataService |
|
|
|
|
|
|
|
.lambdaQuery() |
|
|
|
|
|
|
|
.eq(TestRecordSampleData::getTestId, testRecord.getId()) |
|
|
|
|
|
|
|
.eq(TestRecordSampleData::getSampleType, "Analyte") |
|
|
|
|
|
|
|
.list() |
|
|
|
|
|
|
|
.stream() |
|
|
|
|
|
|
|
.filter(Objects::nonNull) // 避免空值
|
|
|
|
|
|
|
|
.collect(Collectors.groupingBy(TestRecordSampleData::getIsDetected)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<String> isDetectedStrs = new ArrayList<>(); |
|
|
|
|
|
|
|
List<String> notDetectedStrs = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (Map.Entry<Integer, List<TestRecordSampleData>> entry : isDetectedMap.entrySet()) { |
|
|
|
|
|
|
|
if (entry.getKey() != null && entry.getValue() != null && !entry.getValue().isEmpty()) { |
|
|
|
|
|
|
|
List<TestRecordSampleData> value = entry.getValue(); |
|
|
|
|
|
|
|
Map<String, List<TestRecordSampleData>> map = value.stream() |
|
|
|
|
|
|
|
.filter(Objects::nonNull) |
|
|
|
|
|
|
|
.filter(item -> item.getCompoundName() != null && item.getSampleNo() != null) |
|
|
|
|
|
|
|
.collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (Map.Entry<String, List<TestRecordSampleData>> listEntry : map.entrySet()) { |
|
|
|
|
|
|
|
if (listEntry.getKey() != null && listEntry.getValue() != null && !listEntry.getValue().isEmpty()) { |
|
|
|
|
|
|
|
List<TestRecordSampleData> entryValue = listEntry.getValue(); |
|
|
|
|
|
|
|
entryValue.sort(testRecordSampleDataService.getSortBySampleNo()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String collect = entryValue.stream() |
|
|
|
|
|
|
|
.map(item -> { |
|
|
|
|
|
|
|
String[] parts = item.getSampleNo().split("-"); |
|
|
|
|
|
|
|
return (parts.length > 2) ? parts[2] + "号" : item.getSampleNo(); // 预防数组越界
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect(Collectors.joining("、")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String description; |
|
|
|
|
|
|
|
if (entry.getKey() == 1) { |
|
|
|
|
|
|
|
description = "☑ " + collect + "样品空白对照无干扰,在相同条件下进行测定时,样品溶液中目标物与浓度相近的标准工作溶液中的目标物相比,色谱峰保留时间一致、质谱特征离子一致,各离子丰度比相对偏差符合规定范围,判定样品中检出" + listEntry.getKey() + "。"; |
|
|
|
|
|
|
|
isDetectedStrs.add(description); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
description = "☑ " + collect + "样品空白对照无干扰,样品溶液中未检出符合阳性结果评价要求的色谱峰,标准溶液空白无干扰,判定样品中为未检出" + listEntry.getKey() + "。"; |
|
|
|
|
|
|
|
notDetectedStrs.add(description); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!isDetectedStrs.isEmpty()) { |
|
|
|
|
|
|
|
data.put("isDetectedStrs", "阳性结果:" + "\n" + isDetectedStrs.stream() |
|
|
|
|
|
|
|
.map(item -> "\u3000\u3000" + item) |
|
|
|
|
|
|
|
.collect(Collectors.joining("\n"))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!notDetectedStrs.isEmpty()) { |
|
|
|
|
|
|
|
data.put("notDetectedStrs", "阴性结果:" + "\n" + notDetectedStrs.stream() |
|
|
|
|
|
|
|
.map(item -> "\u3000\u3000" + item) |
|
|
|
|
|
|
|
.collect(Collectors.joining("\n"))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1️⃣0 添加检测结果与分析结果判定
|
|
|
|
List<TestRecordSampleData> sampleDataList = 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(); |
|
|
|
|
|
|
|
|
|
|
|
// 添加非空校验,防止传入 `null` 进入 `buildInspectOpinion`
|
|
|
|
data.put("type", "常规毒品"); |
|
|
|
data.put("inspectOpinion", sampleDataList.size() > 0 ? this.buildInspectOpinion(sampleDataList) : ""); |
|
|
|
data.put("inspectOpinion", sampleDataList.size() > 0 ? this.buildInspectOpinion(sampleDataList) : ""); |
|
|
|
|
|
|
|
this.processDetectionResults(data, testRecord); |
|
|
|
|
|
|
|
|
|
|
|
return data; |
|
|
|
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)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<String> isDetectedStr = new ArrayList<>(); |
|
|
|
|
|
|
|
List<String> notDetectedStr = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历检测结果,生成描述
|
|
|
|
|
|
|
|
for (Map.Entry<Integer, List<TestRecordSampleData>> entry : isDetectedMap.entrySet()) { |
|
|
|
|
|
|
|
List<TestRecordSampleData> samples = entry.getValue(); |
|
|
|
|
|
|
|
if (samples == null || samples.isEmpty()) { |
|
|
|
|
|
|
|
continue; // 如果样本数据为空,跳过
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 按化合物名称分组样本
|
|
|
|
|
|
|
|
Map<String, List<TestRecordSampleData>> compoundSampleMap = samples.stream() |
|
|
|
|
|
|
|
.filter(sample -> sample.getCompoundCnName() != null && sample.getSampleNo() != null) |
|
|
|
|
|
|
|
.collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (Map.Entry<String, List<TestRecordSampleData>> compoundEntry : compoundSampleMap.entrySet()) { |
|
|
|
|
|
|
|
String compoundName = compoundEntry.getKey(); |
|
|
|
|
|
|
|
List<TestRecordSampleData> compoundSamples = compoundEntry.getValue(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (compoundSamples == null || compoundSamples.isEmpty()) { |
|
|
|
|
|
|
|
continue; // 如果化合物名称或样本为空,跳过
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 按 `sampleNo` 排序样本
|
|
|
|
|
|
|
|
compoundSamples.sort(testRecordSampleDataService.getSortBySampleNo()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 拼接样本编号
|
|
|
|
|
|
|
|
String sampleNos = compoundSamples.stream() |
|
|
|
|
|
|
|
.map(sample -> { |
|
|
|
|
|
|
|
String[] parts = sample.getSampleNo().split("-"); |
|
|
|
|
|
|
|
return (parts.length > 2) ? parts[2] + "号" : sample.getSampleNo(); // 防止数组越界
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect(Collectors.joining("、")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 生成描述
|
|
|
|
|
|
|
|
String description; |
|
|
|
|
|
|
|
if (entry.getKey() == 1) { |
|
|
|
|
|
|
|
description = String.format( |
|
|
|
|
|
|
|
"☑ %s样品空白对照无干扰,在相同条件下进行测定时,样品溶液中目标物与浓度相近的标准工作溶液中的目标物相比," + |
|
|
|
|
|
|
|
"色谱峰保留时间一致、质谱特征离子一致,各离子丰度比相对偏差符合规定范围,判定样品中检出%s。", |
|
|
|
|
|
|
|
sampleNos, compoundName); |
|
|
|
|
|
|
|
isDetectedStr.add(description); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
description = String.format( |
|
|
|
|
|
|
|
"☑ %s样品空白对照无干扰,样品溶液中未检出符合阳性结果评价要求的色谱峰,标准溶液空白无干扰,判定样品中未检出%s。", |
|
|
|
|
|
|
|
sampleNos, compoundName); |
|
|
|
|
|
|
|
notDetectedStr.add(description); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将阳性结果和阴性结果放入数据 Map 中
|
|
|
|
|
|
|
|
if (!isDetectedStr.isEmpty()) { |
|
|
|
|
|
|
|
data.put("isDetectedStrs", "阳性结果:" + "\n" + isDetectedStr.stream() |
|
|
|
|
|
|
|
.map(item -> " " + item) // 使用全角空格缩进
|
|
|
|
|
|
|
|
.collect(Collectors.joining("\n"))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!notDetectedStr.isEmpty()) { |
|
|
|
|
|
|
|
data.put("notDetectedStrs", "阴性结果:" + "\n" + notDetectedStr.stream() |
|
|
|
|
|
|
|
.map(item -> " " + item) // 使用全角空格缩进
|
|
|
|
|
|
|
|
.collect(Collectors.joining("\n"))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 📌 构建离子对和碰撞能量信息,并存入数据 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(); |
|
|
|
|
|
|
|
|
|
|
|
XWPFTemplate template = XWPFTemplate.compile(bis, config).render(data); |
|
|
|
try (ByteArrayInputStream bis = new ByteArrayInputStream(templateArray); |
|
|
|
NiceXWPFDocument document = template.getXWPFDocument(); |
|
|
|
ByteArrayOutputStream fosWord = new ByteArrayOutputStream()) { |
|
|
|
List<XWPFTable> tables = document.getTables(); |
|
|
|
|
|
|
|
XWPFTable table = tables.get(tables.size() - 1); // 获取最后一个表格
|
|
|
|
// 📌 渲染模板数据
|
|
|
|
|
|
|
|
XWPFTemplate template = XWPFTemplate.compile(bis, config).render(data); |
|
|
|
|
|
|
|
NiceXWPFDocument document = template.getXWPFDocument(); |
|
|
|
|
|
|
|
List<XWPFTable> tables = document.getTables(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 📌 读取数据类型并安全处理
|
|
|
|
|
|
|
|
String type = (String) data.getOrDefault("type", ""); // 避免空指针
|
|
|
|
|
|
|
|
XWPFTable table = tables.get(tables.size() - 1); // 获取最后一个表格
|
|
|
|
|
|
|
|
|
|
|
|
String type = (String) data.get("type"); |
|
|
|
if ("常规毒品".equals(type)) { |
|
|
|
|
|
|
|
mergeRegularDrugTable(table); |
|
|
|
|
|
|
|
} else if ("尿液".equals(type) || "毛发".equals(type)) { |
|
|
|
|
|
|
|
mergeNonRegularDrugTable(table); |
|
|
|
|
|
|
|
mergeAdditionalTable(type, tables); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (StringUtils.isNotBlank(type) && type.equals("inVitro")) { |
|
|
|
// 📌 写入文档流
|
|
|
|
int startRow = 1; // 从第二行开始(表头是第一行,索引从0开始)
|
|
|
|
template.write(fosWord); |
|
|
|
// **处理合并单元格**
|
|
|
|
template.close(); |
|
|
|
int mergeStep = 4; // 每4行合并一次
|
|
|
|
document.close(); |
|
|
|
int[] mergeColumns = {0, 1, 2, 3, 4, 9}; // 合并的列:第1、2、3、4、5、10列,索引分别是 0, 1, 2, 3, 4, 9
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历表格数据行
|
|
|
|
// 📌 构建文件存储路径
|
|
|
|
for (int rowIndex = startRow; rowIndex < table.getNumberOfRows(); rowIndex += mergeStep) { |
|
|
|
ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray()); |
|
|
|
int endRow = Math.min(rowIndex + mergeStep - 1, table.getNumberOfRows() - 1); // 计算合并的终止行,防止越界
|
|
|
|
String path = TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + entrustId + "/" + "贵阳生物样本检验记录.docx"; |
|
|
|
for (int col : mergeColumns) { |
|
|
|
ossFile.fileSave(path, fisWord); |
|
|
|
TableTools.mergeCellsVertically(table, col, rowIndex, endRow); |
|
|
|
|
|
|
|
} |
|
|
|
return path; // 📌 返回 OSS 文件路径
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 📌 合并常规毒品检验记录检测数据部分表格的单元格 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void mergeRegularDrugTable(XWPFTable table) { |
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
int endRow = Math.min(rowIndex + mergeStep - 1, table.getNumberOfRows() - 1); // 防止越界
|
|
|
|
|
|
|
|
for (int col : mergeColumns) { |
|
|
|
|
|
|
|
TableTools.mergeCellsVertically(table, col, rowIndex, endRow); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} |
|
|
|
// **处理合并单元格**
|
|
|
|
} |
|
|
|
int startRow = 1; // 从第二行开始(表头是第一行,索引从0开始)
|
|
|
|
|
|
|
|
String currentCompoundName = null; |
|
|
|
/** |
|
|
|
int mergeStartRow = -1; |
|
|
|
* 📌 合并生物样本检验记录检测数据部分表格的单元格 |
|
|
|
|
|
|
|
*/ |
|
|
|
for (int rowIndex = startRow; rowIndex < table.getNumberOfRows(); rowIndex++) { |
|
|
|
private void mergeNonRegularDrugTable(XWPFTable table) { |
|
|
|
XWPFTableRow row = table.getRow(rowIndex); |
|
|
|
int startRow = 1; |
|
|
|
XWPFTableCell cell = row.getCell(1); // 获取第二列的单元格(索引1)
|
|
|
|
String currentCompoundName = null; |
|
|
|
String compoundName = cell.getText().trim(); |
|
|
|
int mergeStartRow = -1; |
|
|
|
|
|
|
|
|
|
|
|
if (currentCompoundName == null || !currentCompoundName.equals(compoundName)) { |
|
|
|
for (int rowIndex = startRow; rowIndex < table.getNumberOfRows(); rowIndex++) { |
|
|
|
// 如果当前化合物名称与上一行不同,则结束之前的合并
|
|
|
|
XWPFTableRow row = table.getRow(rowIndex); |
|
|
|
if (mergeStartRow >= 0 && rowIndex > mergeStartRow + 1) { |
|
|
|
XWPFTableCell cell = row.getCell(1); // 🔹 第二列(索引1)
|
|
|
|
TableTools.mergeCellsVertically(table, 1, mergeStartRow, rowIndex - 1); // 合并第二列(列索引1)
|
|
|
|
String compoundName = cell.getText().trim(); |
|
|
|
} |
|
|
|
|
|
|
|
currentCompoundName = compoundName; |
|
|
|
if (!compoundName.equals(currentCompoundName)) { |
|
|
|
mergeStartRow = rowIndex; |
|
|
|
if (mergeStartRow >= 0 && rowIndex > mergeStartRow + 1) { |
|
|
|
|
|
|
|
TableTools.mergeCellsVertically(table, 1, mergeStartRow, rowIndex - 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
currentCompoundName = compoundName; |
|
|
|
|
|
|
|
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); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 📌 处理毛发或尿液表格目标物定性离子对、碰撞能量 (CE)等参数表格的合并逻辑 |
|
|
|
|
|
|
|
* 📌 因为毛发和尿液的配置表格数量不同,因此下标不同 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void mergeAdditionalTable(String type, List<XWPFTable> tables) { |
|
|
|
|
|
|
|
if (!"毛发".equals(type) && !"尿液".equals(type)) { |
|
|
|
|
|
|
|
return; // 如果不是毛发或尿液类型,则不处理
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
System.out.println("总共的表格数量:" + tables.size()); |
|
|
|
int tableIndex = "毛发".equals(type) ? 2 : 1; |
|
|
|
XWPFTable xwpfTable = tables.get(2); |
|
|
|
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,59 +968,104 @@ 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; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 格式化编号范围: |
|
|
|
* 对样品编号进行排序 |
|
|
|
* - 单个编号:返回 "1号" |
|
|
|
|
|
|
|
* - 连续编号:返回 "1号至3号" |
|
|
|
|
|
|
|
* |
|
|
|
* |
|
|
|
* @param start 起始编号 |
|
|
|
* @param sampleNumbers 样品编号列表,例如 ["3号", "1号", "2号"] |
|
|
|
* @param end 结束编号 |
|
|
|
* @return 排序后的样品编号列表,例如 ["1号", "2号", "3号"] |
|
|
|
* @return 格式化的编号字符串(如果 start == end,返回单号) |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private String formatRange(int start, int end) { |
|
|
|
private List<String> sortSampleNumbers(List<String> sampleNumbers) { |
|
|
|
return (start == end) ? start + "号" : start + "号至" + end + "号"; |
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String formatSampleNumbers(List<String> sampleNumbers) { |
|
|
|
/** |
|
|
|
List<Integer> numbers = sampleNumbers.stream() |
|
|
|
* 格式化编号范围: |
|
|
|
|
|
|
|
* - 单个编号:返回 "1号" |
|
|
|
|
|
|
|
* - 连续编号:返回 "1号至3号" |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param sortedSampleNumbers 已排序的样品编号列表 |
|
|
|
|
|
|
|
* @return 格式化的编号字符串 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private String formatSampleNumbers(List<String> sortedSampleNumbers) { |
|
|
|
|
|
|
|
List<Integer> numbers = sortedSampleNumbers.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); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|