Merge remote-tracking branch 'origin/master'

master
陈江保 4 days ago
commit 8f74dff6ac
  1. 435
      dlp-drugtesting-biz/src/main/java/digital/laboratory/platform/inspection/service/impl/InspectRecordServiceImpl.java

@ -3,12 +3,13 @@ package digital.laboratory.platform.inspection.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.util.TableTools;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import digital.laboratory.platform.common.core.util.R;
import digital.laboratory.platform.common.feign.RemoteWord2PDFService;
@ -31,6 +32,7 @@ import digital.laboratory.platform.sys.entity.Drug;
import digital.laboratory.platform.sys.enums.entrust.EntrustBiologyType;
import feign.Response;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.springframework.beans.BeanUtils;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
@ -39,6 +41,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -163,7 +166,7 @@ public class InspectRecordServiceImpl implements InspectRecordService {
// 根据化合物名称分组
Map<String, List<TestRecordSampleData>> dataMap = dataList.stream()
.collect(Collectors.groupingBy(TestRecordSampleData::getCompoundName));
.collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName));
List<TestRecordSampleDataDocDTO> dataDtos = new ArrayList<>();
@ -265,20 +268,28 @@ public class InspectRecordServiceImpl implements InspectRecordService {
// 返回处理后的数据
data.put("dataDtos", dataDtos);
data.put("sampleSize", dataDtos.size() / 2);
data.put("inspectOpinion", this.buildInspectOpinion(dataList));
data.put("inspectOpinion", this.buildInspectOpinion(testRecordSampleDataService
.lambdaQuery()
.eq(TestRecordSampleData::getTestId, testRecord.getId())
.eq(TestRecordSampleData::getSampleType, "Analyte")
.list()));
return data;
}
public Map<String, Object> buildInVitroRecordData(EntrustInfo entrustInfo, TestRecord testRecord) {
Map<String, Object> data = new HashMap<>();
public Map<String, Object> buildInVitroRecordData(EntrustInfo entrustInfo, TestRecord testRecord, Map<String, Object> data) {
// 1 按化合物名称分组 TestRecordSampleData
Map<String, List<TestRecordSampleData>> compoundSampleDataMap = testRecordSampleDataService
List<TestRecordSampleData> dataList = testRecordSampleDataService
.lambdaQuery()
.eq(TestRecordSampleData::getTestId, testRecord.getId())
.list()
.list();
Map<String, List<TestRecordSampleData>> compoundSampleDataMap = dataList
.stream()
.collect(Collectors.groupingBy(TestRecordSampleData::getCompoundName));
.collect(Collectors.groupingBy(TestRecordSampleData::getCompoundCnName));
// 2 过滤出 "标准物质" 类别的试剂,并转换为 Map (key=药品名称, value=试剂VO)
List<String> reagentConsumablesList = testRecord.getReagentConsumablesList();
@ -306,35 +317,44 @@ public class InspectRecordServiceImpl implements InspectRecordService {
TestRecordSampleData stdSample = stdList.get(0);
// 4 获取 STD 样本的扩展数据 (以 MassToChargeRatio 为 key)
Map<BigDecimal, TestRecordSampleDataExpand> expandMap = testRecordSampledataExpandService.lambdaQuery()
Map<String, TestRecordSampleDataExpand> expandMap = testRecordSampledataExpandService.lambdaQuery()
.eq(TestRecordSampleDataExpand::getTestDataId, stdSample.getId())
.list()
.stream()
.collect(Collectors.toMap(TestRecordSampleDataExpand::getMassToChargeRatio, Function.identity()));
.collect(Collectors.toMap(
e -> e.getMassToChargeRatio().setScale(0, RoundingMode.FLOOR).toString(), // 保留整数部分
Function.identity()
));
// 5 获取当前化合物对应的试剂,提取特征离子
TestRecordReagentVO reagentVO = reagentMap.get(compoundName);
if (reagentVO == null || reagentVO.getDrug().getCharacteristicIons() == null) {
if (reagentVO == null || StringUtils.isBlank(reagentVO.getDrug().getCharacteristicIons())) {
continue; // 没有对应试剂或特征离子数据,跳过
}
String[] peaks = reagentVO.getDrug().getCharacteristicIons().split("、");
// 6 处理 "标样" 数据 (STD)
for (int i = 0; i < peaks.length; i++) {
String peak = (i == 0) ? peaks[i].substring(0, peaks[i].length() - 1) : peaks[i];
TestRecordSampleDataExpand expand = expandMap.get(new BigDecimal(peak));
TestRecordSampleDataExpand expand = expandMap.get(peak);
if (expand != null) {
TestRecordSampleDataDocDTO dto = new TestRecordSampleDataDocDTO();
BeanUtils.copyProperties(stdSample, dto);
dto.setName("标样");
dto.setPTargetRtTime(dto.getTargetRtTime().toString());
dto.setCompoundName(compoundName);
dto.setPTargetRtTime(Optional.ofNullable(dto.getTargetRtTime()).map(Object::toString).orElse("/"));
dto.setPRtTimeError("±1");
dto.setPRtTimeWithinError("/");
dto.setPMassToChargeRatio(i == 0 ? peak + "(基峰)" : peak);
dto.setPIonAbundanceRatio(expand.getIonAbundanceRatio().toString());
dto.setPIonAbundanceRatioError(expand.getIonAbundanceRatioError().toString());
dto.setPIonAbundanceRatioWithinError(expand.getIonAbundanceRatioWithinError());
dto.setPIonAbundanceRatio(Optional.ofNullable(expand.getIonAbundanceRatio())
.map(value -> value.setScale(2, RoundingMode.HALF_UP).toString())
.orElse("/"));
dto.setPIonAbundanceRatioError(Optional.ofNullable(expand.getIonAbundanceRatioError())
.map(value -> value.setScale(2, RoundingMode.HALF_UP).toString())
.orElse("/"));
dto.setPIonAbundanceRatioWithinError(Optional.ofNullable(expand.getIonAbundanceRatioWithinError()).filter(s -> !s.isEmpty()).orElse("/"));
dto.setPIsDetected("/");
dataDtos.add(dto);
}
@ -348,7 +368,7 @@ public class InspectRecordServiceImpl implements InspectRecordService {
dto.setPTargetRtTime("/");
dto.setPRtTimeError("/");
dto.setPRtTimeWithinError("/");
dto.setPMassToChargeRatio(peak.equals(peaks[0]) ? peak + "(基峰)" : peak);
dto.setPMassToChargeRatio(peak.equals(peaks[0]) ? peaks[0].substring(0, peaks[0].length() - 1) + "(基峰)" : peak);
dto.setPIonAbundanceRatio("/");
dto.setPIonAbundanceRatioError("/");
dto.setPIonAbundanceRatioWithinError(peak.equals(peaks[0]) ? "/" : "否");
@ -362,27 +382,37 @@ public class InspectRecordServiceImpl implements InspectRecordService {
analyteList.sort(testRecordSampleDataService.getSortBySampleNo()); // 按样本编号排序
for (TestRecordSampleData analyte : analyteList) {
Map<BigDecimal, TestRecordSampleDataExpand> analyteExpandMap = testRecordSampledataExpandService.lambdaQuery()
Map<String, TestRecordSampleDataExpand> analyteExpandMap = testRecordSampledataExpandService.lambdaQuery()
.eq(TestRecordSampleDataExpand::getTestDataId, analyte.getId())
.list()
.stream()
.collect(Collectors.toMap(TestRecordSampleDataExpand::getMassToChargeRatio, Function.identity()));
.collect(Collectors.toMap(
e -> e.getMassToChargeRatio().setScale(0, RoundingMode.FLOOR).toString(), // 保留整数部分
Function.identity()
));
for (int i = 0; i < peaks.length; i++) {
String peak = (i == 0) ? peaks[i].substring(0, peaks[i].length() - 1) : peaks[i];
TestRecordSampleDataExpand expand = analyteExpandMap.get(new BigDecimal(peak));
TestRecordSampleDataExpand expand = analyteExpandMap.get(peak);
if (expand != null) {
TestRecordSampleDataDocDTO dto = new TestRecordSampleDataDocDTO();
BeanUtils.copyProperties(analyte, dto);
dto.setPTargetRtTime(dto.getTargetRtTime().toString());
dto.setPRtTimeError(dto.getRtTimeWithinError());
dto.setPRtTimeWithinError(dto.getRtTimeWithinError());
dto.setPTargetRtTime(Optional.ofNullable(dto.getTargetRtTime()).map(Object::toString).orElse("/"));
dto.setPRtTimeError(Optional.ofNullable(dto.getRtTimeError())
.map(value -> new BigDecimal(value.toString()).setScale(2, RoundingMode.HALF_UP).toString())
.orElse("/"));
dto.setPRtTimeWithinError(Optional.ofNullable(dto.getRtTimeWithinError()).orElse("/"));
dto.setPMassToChargeRatio(i == 0 ? peak + "(基峰)" : peak);
dto.setPIonAbundanceRatio(expand.getIonAbundanceRatio().toString());
dto.setPIonAbundanceRatioError(expand.getIonAbundanceRatioError().toString());
dto.setPIonAbundanceRatioWithinError(expand.getIonAbundanceRatioWithinError());
dto.setPIsDetected(dto.getIsDetected() == 1 ? "是" : "否");
dto.setPIonAbundanceRatio(Optional.ofNullable(expand.getIonAbundanceRatio())
.map(value -> value.setScale(2, RoundingMode.HALF_UP).toString())
.orElse("/"));
dto.setPIonAbundanceRatioError(Optional.ofNullable(expand.getIonAbundanceRatioError())
.map(value -> value.setScale(2, RoundingMode.HALF_UP).toString())
.orElse("/"));
dto.setPIonAbundanceRatioWithinError(Optional.ofNullable(expand.getIonAbundanceRatioWithinError()).orElse("/"));
dto.setPIsDetected(dto.getIsDetected() != null && dto.getIsDetected() == 1 ? "是" : "否");
dto.setCompoundName(compoundName);
dataDtos.add(dto);
}
}
@ -392,6 +422,76 @@ public class InspectRecordServiceImpl implements InspectRecordService {
// 9 返回处理后的数据
data.put("dataDtos", dataDtos);
data.put("sampleSize", dataDtos.size());
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("\u2611")));
}
if (!notDetectedStrs.isEmpty()) {
data.put("notDetectedStrs", "阴性结果:" + "\n" + notDetectedStrs.stream()
.map(item -> "\u3000\u3000" + item)
.collect(Collectors.joining("\u2611")));
}
} 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;
}
@ -470,8 +570,7 @@ public class InspectRecordServiceImpl implements InspectRecordService {
* @return 上传后的文件路径
* @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();
ossFile.fileGet(templatePath, bos);
byte[] templateArray = bos.toByteArray();
@ -481,11 +580,26 @@ public class InspectRecordServiceImpl implements InspectRecordService {
XWPFTemplate template = XWPFTemplate.compile(bis, config).render(data);
NiceXWPFDocument document = template.getXWPFDocument();
// XWPFTable table = document.getTables().get(3);
// int sampleSize = (int) data.get("sampleSize");
// //合并单元格
//
// TableTools.mergeCellsVertically(table, 1, 1, sampleSize);
List<XWPFTable> tables = document.getTables();
XWPFTable table = tables.get(tables.size() - 1); // 获取最后一个表格
int sampleSize = (int) data.get("sampleSize");
// **处理合并单元格**
int startRow = 1; // 从第二行开始(索引从0开始,所以第二行是索引1)
int mergeStep = 4; // 每4行合并一次
// 指定需要合并的列(注意索引从0开始)
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) {
int endRow = Math.min(rowIndex + mergeStep - 1, table.getNumberOfRows() - 1); // 计算合并的终止行,防止越界
for (int col : mergeColumns) {
TableTools.mergeCellsVertically(table, col, rowIndex, endRow);
}
}
bis.close();
ByteArrayOutputStream fosWord = new ByteArrayOutputStream();
@ -494,12 +608,14 @@ public class InspectRecordServiceImpl implements InspectRecordService {
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;
}
/**
* 生成常规毒品的检验记录贵阳禁毒
*/
@ -509,8 +625,7 @@ public class InspectRecordServiceImpl implements InspectRecordService {
TestRecord testRecord = testRecordService.lambdaQuery()
.eq(TestRecord::getBusinessId, businessId)
.one();
Map<String, Object> data = this.buildInVitroRecordData(entrustInfo, testRecord);
docMap.putAll(data);
Map<String, Object> data = this.buildInVitroRecordData(entrustInfo, testRecord, docMap);
String templatePath = "/template" + "/贵阳禁毒常规毒品检验记录模板.docx";
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
Configure config = Configure.builder().
@ -528,7 +643,9 @@ public class InspectRecordServiceImpl implements InspectRecordService {
EntrustInfo entrustInfo = entrustInfoService.getById(businessId);
// 这里因为检验记录实体并未关联业务id, 只能先查询实验数据,在通过实验数据获取到检验记录信息
TestRecord testRecord = testRecordService.getTestRecordByBusinessId(entrustInfo.getId());
TestRecord testRecord = testRecordService.lambdaQuery()
.eq(TestRecord::getBusinessId, businessId)
.one();
// 封装数据到map中
Map<String, Object> docMap = buildCommonInspectRecordDocMap(entrustInfo, testRecord, "");
@ -561,28 +678,17 @@ public class InspectRecordServiceImpl implements InspectRecordService {
data.put("inspectYear", testRecord.getTestStartDate().getYear());
data.put("inspectMonth", testRecord.getTestStartDate().getMonthValue());
data.put("inspectDay", testRecord.getTestStartDate().getDayOfMonth());
String[] split = entrustInfo.getAcceptNo().split("-");
if (split.length == 2) {
data.put("acceptNo", "[" + split[0] + "]" + split[1] + "号");
}
data.put("acceptNo", entrustInfo.getAcceptNo());
// 检材性状描述和检验要求成分
List<SampleInfo> sampleInfoList = getSampleInfosByMaterialType(entrustInfo, materialType);
String materialCharacterDesc = buildMaterialCharacterDesc(sampleInfoList);
String targetObjectStr = sampleInfoList
.stream()
.flatMap(
sampleInfo -> {
String toJSONString = JSONArray.toJSONString(sampleInfo.getTargetObject());
List<TargetObject> drugLiteList = JSONArray.parseArray(toJSONString, TargetObject.class);
return drugLiteList.stream();
})
.collect(Collectors.toList()) // 得到转换好的筛查物对象
.stream()
.map(TargetObject::getName)
.collect(Collectors.toSet()) // 区筛查物对象名称并去重
.stream()
.collect(Collectors.joining("、")); // 拼接筛查物名称
// 检材性状描述
String materialCharacterDesc = buildMaterialCharacterDesc(sampleInfoService
.lambdaQuery()
.in(SampleInfo::getId, testRecord.getSampleTestList())
.list());
data.put("materialCharacterDesc", materialCharacterDesc);
data.put("materialIngredient", targetObjectStr);
// 获取仪器设备数据(批量查询,减少数据库压力)
List<String> deviceIdList = testRecord.getDeviceIdList();
@ -591,11 +697,19 @@ public class InspectRecordServiceImpl implements InspectRecordService {
: testRecordInstrumentService.list(Wrappers.<TestRecordInstrument>lambdaQuery()
.in(TestRecordInstrument::getId, deviceIdList));
data.put("instrumentName", CollectionUtils.isEmpty(instruments)
? "未找到仪器设备数据!"
: instruments.stream()
.map(i -> "\u3000\u3000" + i.getInstrumentName()) // 使用 Unicode 全角空格
.collect(Collectors.joining("\n")));
if (materialType.isEmpty()) {
data.put("instrumentName", CollectionUtils.isEmpty(instruments)
? "未找到仪器设备数据!"
: instruments.stream()
.map(i -> "\u3000\u3000\u2611" + i.getInstrumentName()) // 使用 Unicode 全角空格
.collect(Collectors.joining("\n")));
} else {
data.put("instrumentName", CollectionUtils.isEmpty(instruments)
? "未找到仪器设备数据!"
: instruments.stream()
.map(i -> "\u3000\u3000" + i.getInstrumentName()) // 使用 Unicode 全角空格
.collect(Collectors.joining("\n")));
}
// 设置使用的标准物质和试剂
@ -606,14 +720,18 @@ public class InspectRecordServiceImpl implements InspectRecordService {
if (references == null || references.isEmpty()) {
data.put("referenceMaterialName", "未找到试剂耗材数据!");
} else {
String firstStr = "\u2611" + " " + references.get(0).getReagentConsumableName() + "\n";
references.remove(references.get(0));
String referenceMaterialName = references.stream()
.map(reagent -> "\u2611" + " " + reagent.getReagentConsumableName())
.map(reagent -> "\u3000\u3000\u2611" + " " + reagent.getReagentConsumableName())
.collect(Collectors.joining("\n"));
data.put("referenceMaterialName", referenceMaterialName);
}
data.put("referenceMaterialName", firstStr + referenceMaterialName);
data.put("materialIngredient", references.stream()
.map(reagent -> reagent.getReagentConsumableName())
.collect(Collectors.joining("、")));
}
List<TestRecordReagent> reagents = testRecordReagentService.list(Wrappers.<TestRecordReagent>lambdaQuery()
.in(TestRecordReagent::getId, testRecord.getReagentConsumablesList())
.eq(TestRecordReagent::getCategory, "试剂"));
@ -657,136 +775,167 @@ public class InspectRecordServiceImpl implements InspectRecordService {
/**
* 构建检材性状描述
* 构建检材性状描述使用检材编号尾号并合并连续编号
* <p>
* 规则
* 1. **提取编号尾号** `2025007001` `1`
* 2. **按尾号排序**
* 3. **连续编号 & 物质性状相同时合并**
* 4. **只有一个检材时省略编号直接描述**
* 5. **输出格式示例**
* - `"1号至4号检材为粉状"`
* - `"检材为粉状"`仅有一个检材时
*
* @param sampleInfoList 样本信息列表
* @return 物料特性描述如果无法构建则返回null
* @return 物料特性描述如果列表为空则返回空字符串
*/
private String buildMaterialCharacterDesc(List<SampleInfo> sampleInfoList) {
if (CollUtil.isEmpty(sampleInfoList)) {
return StrUtil.EMPTY;
}
// 仅有一个样本时,直接返回
if (sampleInfoList.size() == 1) {
return "检材为" + sampleInfoList.get(0).getForm() + "。";
}
// 按尾号排序(提取编号尾号进行比较)
sampleInfoList.sort(Comparator.comparing(sample -> extractTailNumber(sample.getAcceptNo())));
// 按 form 分组
Map<String, List<SampleInfo>> sampleMapGroupByForm = sampleInfoList.stream()
.collect(Collectors.groupingBy(SampleInfo::getForm));
List<String> descriptions = new ArrayList<>();
int start = -1, prev = -1;
String prevForm = null;
List<String> mergedOrderNos = new ArrayList<>();
// 遍历排序后的检材编号,合并相邻且性状相同的编号
for (SampleInfo sample : sampleInfoList) {
int current = extractTailNumber(sample.getAcceptNo()); // 提取尾号
String form = sample.getForm();
if (start == -1) { // 初始化
start = prev = current;
prevForm = form;
} else if (current == prev + 1 && form.equals(prevForm)) {
// 只有连续尾号 + 相同性状才能合并
prev = current;
} else {
// 遇到新性状或不连续尾号,存储前一段描述
mergedOrderNos.add(formatRange(start, prev) + "检材为" + prevForm);
start = prev = current;
prevForm = form;
}
}
// 处理最后一组数据
mergedOrderNos.add(formatRange(start, prev) + "检材为" + prevForm);
// 所有样本 form 相同
if (sampleMapGroupByForm.size() == 1) {
String form = sampleMapGroupByForm.keySet().iterator().next(); // 取唯一的 key
String orderNoStr = sampleInfoList.stream()
.map(sample -> sample.getOrderNo() + "号")
.collect(Collectors.joining("、"));
return orderNoStr + "检材均为" + form + "。";
// **如果只有一个检材,省略编号**
if (mergedOrderNos.size() == 1 && sampleInfoList.size() == 1) {
return "检材为" + prevForm + "。";
}
// 存在多个 form 的情况
List<String> descriptions = new ArrayList<>();
sampleMapGroupByForm.forEach((form, value) -> {
String orderNoStr = value.stream()
.map(sample -> sample.getOrderNo() + "号")
.collect(Collectors.joining("、"));
descriptions.add(orderNoStr + "检材均为" + form);
});
return String.join(";", mergedOrderNos) + "。";
}
/**
* 提取检材编号的尾号即最后一个 `-` 之后的数值
*
* @param orderNo 检材编号格式2025-89-13
* @return 尾号示例返回 13
*/
private int extractTailNumber(String orderNo) {
String[] parts = orderNo.split("-");
if (parts.length > 0) {
return Integer.parseInt(parts[parts.length - 1]); // 取最后一段
}
return 0;
}
return String.join(";", descriptions) + "。"; // 用分号连接不同类型的描述
/**
* 格式化编号范围
* - 单个编号返回 "1号"
* - 连续编号返回 "1号至3号"
*
* @param start 起始编号
* @param end 结束编号
* @return 格式化的编号字符串如果 start == end返回单号
*/
private String formatRange(int start, int end) {
return (start == end) ? start + "号" : start + "号至" + end + "号";
}
public String buildInspectOpinion(List<TestRecordSampleData> dataList) {
// 1. 检查输入的dataList是否为空,如果为空直接返回当前数据。
if (dataList == null || dataList.isEmpty()) {
return "";
}
// 2. 初始化数据结构,用于分类和记录检出与未检出的物质
// - detectedMap 用来存储检出物质的样本信息,key为样本编号,value为检出的物质列表
// - notDetectedMap 用来存储未检出物质的样本信息,key为样本编号,value为未检出的物质列表
// - allCompoundNames 用来存储所有物质名称的集合,方便后续处理
Map<String, List<String>> detectedMap = new LinkedHashMap<>();
Map<String, List<String>> notDetectedMap = new LinkedHashMap<>();
Set<String> allCompoundNames = new HashSet<>();
// 3. 遍历dataList,按每个样本的检出状态(是否检出)对物质进行分类
for (TestRecordSampleData record : dataList) {
String sampleNo = record.getSampleNo(); // 获取样本编号
String compoundName = record.getCompoundName(); // 获取化合物名称
allCompoundNames.add(compoundName); // 将化合物名称添加到所有化合物名称集合中
String sampleNo = record.getSampleNo();
String compoundName = record.getCompoundCnName();
// 根据isDetected值将物质添加到相应的Map中
if (record.getIsDetected() == 1) {
detectedMap.computeIfAbsent(sampleNo, k -> new ArrayList<>()).add(compoundName); // 如果检出,将物质加入到检测的Map中
// 确保样本编号有效
if (sampleNo == null || compoundName == null || sampleNo.trim().isEmpty()) {
continue;
}
allCompoundNames.add(compoundName);
if (record.getIsDetected() != null && record.getIsDetected() == 1) {
detectedMap.computeIfAbsent(sampleNo, k -> new ArrayList<>()).add(compoundName);
} else {
notDetectedMap.computeIfAbsent(sampleNo, k -> new ArrayList<>()).add(compoundName); // 如果未检出,将物质加入到未检测的Map中
notDetectedMap.computeIfAbsent(sampleNo, k -> new ArrayList<>()).add(compoundName);
}
}
// 4. 定义一个lambda函数,提取样本编号中的流水号(例如:"S001" -> "1号")
// 目的是生成以“号”结尾的样本编号格式。
Function<String, String> extractSampleNumber = sampleNo -> sampleNo.substring(sampleNo.lastIndexOf('-') + 1) + "号";
Function<String, String> extractSampleNumber = sampleNo -> sampleNo.matches(".*\\d+")
? sampleNo.replaceAll(".*?(\\d+)$", "$1号")
: sampleNo;
// 5. 处理“检出”数据
// 先根据样本编号分组相同的检出物质,之后生成相应的语句描述
List<String> detectedSentences = new ArrayList<>(); // 存储“检出”物质的描述
Map<Set<String>, List<String>> groupedDetectedSamples = new LinkedHashMap<>(); // 用来存储按物质分组的样本编号
List<String> detectedSentences = new ArrayList<>();
Map<Set<String>, List<String>> groupedDetectedSamples = new LinkedHashMap<>();
// 遍历检测到的物质,将相同物质的样本编号进行分组
for (Map.Entry<String, List<String>> entry : detectedMap.entrySet()) {
Set<String> compoundSet = new HashSet<>(entry.getValue()); // 去重,保证每个物质只记录一次
groupedDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(entry.getKey())); // 根据物质的组合来分组样本编号
Set<String> compoundSet = new HashSet<>(entry.getValue());
groupedDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(entry.getKey()));
}
// 遍历每个分组,生成对应的描述
for (Map.Entry<Set<String>, List<String>> entry : groupedDetectedSamples.entrySet()) {
String sampleNumbers = String.join("、", entry.getValue()); // 将所有样本编号合并为字符串
String compounds = String.join("、", entry.getKey()); // 将所有检出的物质合并为字符串
String sampleNumbers = String.join("、", entry.getValue());
String compounds = String.join("、", entry.getKey());
// 如果多个样本检出相同的物质,描述中使用“均检出”,否则使用“检出”
if (entry.getValue().size() > 1) {
detectedSentences.add(String.format("从%s检材样品中均检出%s成分", sampleNumbers, compounds)); // 多个样本检出
detectedSentences.add(String.format("从%s检材样品中均检出%s成分", sampleNumbers, compounds));
} else {
detectedSentences.add(String.format("从%s检材样品中检出%s成分", sampleNumbers, compounds)); // 单个样本检出
detectedSentences.add(String.format("从%s检材样品中检出%s成分", sampleNumbers, compounds));
}
}
// 6. 处理“未检出”数据
// 先根据未检出的物质及样本编号进行分组,然后生成相应的描述
List<String> notDetectedSentences = new ArrayList<>(); // 存储“未检出”物质的描述
Map<Set<String>, List<String>> groupedNotDetectedSamples = new LinkedHashMap<>(); // 用来存储按物质分组的样本编号
List<String> notDetectedSentences = new ArrayList<>();
Map<Set<String>, List<String>> groupedNotDetectedSamples = new LinkedHashMap<>();
// 遍历未检出的物质,找到未检出的样本并将其分组
for (Map.Entry<String, List<String>> entry : notDetectedMap.entrySet()) {
String sampleNo = entry.getKey(); // 获取样本编号
if (detectedMap.containsKey(sampleNo)) {
continue; // 如果该样本已经出现在检出组中,跳过,不再处理
}
String sampleNo = entry.getKey();
Set<String> compoundSet = new HashSet<>(entry.getValue());
Set<String> compoundSet = new HashSet<>(entry.getValue()); // 获取未检出的物质,去重
groupedNotDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(sampleNo)); // 将未检出的物质分组
// 仅当该样本没有任何检出的物质时,才记录未检出
if (!detectedMap.containsKey(sampleNo)) {
groupedNotDetectedSamples.computeIfAbsent(compoundSet, k -> new ArrayList<>()).add(extractSampleNumber.apply(sampleNo));
}
}
// 遍历每个未检出物质的分组,生成描述
for (Map.Entry<Set<String>, List<String>> entry : groupedNotDetectedSamples.entrySet()) {
String sampleNumbers = String.join("、", entry.getValue()); // 将未检出的样本编号合并为字符串
String compounds = String.join("、", entry.getKey()); // 将未检出的物质合并为字符串
String sampleNumbers = String.join("、", entry.getValue());
String compounds = String.join("、", entry.getKey());
// 如果多个样本未检出相同的物质,描述中使用“均未检出”,否则使用“未检出”
if (entry.getValue().size() > 1) {
notDetectedSentences.add(String.format("从%s检材样品中均未检出%s成分", sampleNumbers, compounds)); // 多个样本未检出
notDetectedSentences.add(String.format("从%s检材样品中均未检出%s成分", sampleNumbers, compounds));
} else {
notDetectedSentences.add(String.format("从%s检材样品中未检出%s成分", sampleNumbers, compounds)); // 单个样本未检出
notDetectedSentences.add(String.format("从%s检材样品中未检出%s成分", sampleNumbers, compounds));
}
}
// 7. 将“检出”和“未检出”描述合并到最终结果中
List<String> finalSentences = new ArrayList<>(detectedSentences); // 先将检出物质的描述加入
finalSentences.addAll(notDetectedSentences); // 将未检出的描述添加到末尾
List<String> finalSentences = new ArrayList<>(detectedSentences);
finalSentences.addAll(notDetectedSentences);
// 8. 将最终的描述字符串添加到结果数据Map中,字段名为"detectionStr"返回结果
return String.join(";", finalSentences);
}
}

Loading…
Cancel
Save