更新了导出毒品计算Excel会因为重名报错的Bug
This commit is contained in:
+156
-48
@@ -72,6 +72,11 @@ import java.util.stream.Collectors;
|
|||||||
public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<EntrustMaterialCheckoutResultMapper, EntrustMaterialCheckoutResult>
|
public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<EntrustMaterialCheckoutResultMapper, EntrustMaterialCheckoutResult>
|
||||||
implements EntrustMaterialCheckoutResultService {
|
implements EntrustMaterialCheckoutResultService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excel 工作表名最大长度(OOXML 规范:1~31 字符)
|
||||||
|
*/
|
||||||
|
private static final int MAX_SHEET_NAME_LENGTH = 31;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private EntrustmentService entrustmentService;
|
private EntrustmentService entrustmentService;
|
||||||
|
|
||||||
@@ -688,6 +693,9 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
*/
|
*/
|
||||||
private List<String> getOriginalIdentificationList(List<String> oldResults) {
|
private List<String> getOriginalIdentificationList(List<String> oldResults) {
|
||||||
List<String> allResults = new ArrayList<>();
|
List<String> allResults = new ArrayList<>();
|
||||||
|
if (CollUtil.isEmpty(oldResults)) {
|
||||||
|
return allResults;
|
||||||
|
}
|
||||||
for (String oldResult : oldResults) {
|
for (String oldResult : oldResults) {
|
||||||
if (StringUtils.isNotBlank(oldResult)) {
|
if (StringUtils.isNotBlank(oldResult)) {
|
||||||
if ("委托".equals(oldResult)) {
|
if ("委托".equals(oldResult)) {
|
||||||
@@ -751,39 +759,34 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 提取所有涉及的毒品名称(去重)
|
// 4. 提取所有涉及的化合物名称,并按"归一化名称"归并分组
|
||||||
Set<String> drugNames = new HashSet<>();
|
// 字典里用 "氢可酮*" 标注管制药品,业务上与 "氢可酮" 是同一化合物,这里合并为一组
|
||||||
|
// key = 归一化名称(统计表展示、工作表名都用它),value = 库里的各种原始写法(查询要用原始写法)
|
||||||
|
Map<String, Set<String>> groupedDrugNames = new LinkedHashMap<>();
|
||||||
for (EntrustMaterialCheckoutResult result : newResultList) {
|
for (EntrustMaterialCheckoutResult result : newResultList) {
|
||||||
List<DrugLite> drugLites = DrugLiteConvert.getDrugLites(result.getQualitativeResult());
|
collectDrugNames(groupedDrugNames, result.getQualitativeResult());
|
||||||
for (DrugLite drugLite : drugLites) {
|
if (isMetabolite) {
|
||||||
drugNames.add(drugLite.getName());
|
collectDrugNames(groupedDrugNames, result.getMetaboliteResult());
|
||||||
}
|
|
||||||
if (isMetabolite && StringUtils.isNotBlank(result.getMetaboliteResult())) {
|
|
||||||
List<DrugLite> drugLites1 = DrugLiteConvert.getDrugLites(result.getMetaboliteResult());
|
|
||||||
for (DrugLite drugLite : drugLites1) {
|
|
||||||
if (!drugNames.contains(drugLite.getName())) {
|
|
||||||
drugNames.add(drugLite.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 处理不同毒品名称的数据
|
// 5. 处理不同毒品名称的数据
|
||||||
Map<String, List<EntrustmentIdentificationMaterialVO>> resultMap = new HashMap<>();
|
Map<String, List<EntrustmentIdentificationMaterialVO>> resultMap = new HashMap<>();
|
||||||
|
|
||||||
for (String drugName : drugNames) {
|
for (Map.Entry<String, Set<String>> drugGroup : groupedDrugNames.entrySet()) {
|
||||||
// 5.1 查询所有含有该毒品的检验结果
|
String drugName = drugGroup.getKey();
|
||||||
|
Set<String> rawDrugNames = drugGroup.getValue();
|
||||||
|
|
||||||
|
// 5.1 查询所有含有该毒品的检验结果(组内每种原始写法各查一次,再按检材 id 去重合并)
|
||||||
|
Map<String, EntrustMaterialCheckoutResult> checkoutResultMap = new LinkedHashMap<>();
|
||||||
|
for (String rawDrugName : rawDrugNames) {
|
||||||
List<EntrustMaterialCheckoutResult> resultList = this.list(Wrappers.<EntrustMaterialCheckoutResult>lambdaQuery()
|
List<EntrustMaterialCheckoutResult> resultList = this.list(Wrappers.<EntrustMaterialCheckoutResult>lambdaQuery()
|
||||||
.and(queryWrapper -> queryWrapper
|
.and(queryWrapper -> queryWrapper
|
||||||
.like(EntrustMaterialCheckoutResult::getMetaboliteResult, drugName)
|
.like(EntrustMaterialCheckoutResult::getMetaboliteResult, rawDrugName)
|
||||||
.or()
|
.or()
|
||||||
.like(EntrustMaterialCheckoutResult::getQualitativeResult, drugName))
|
.like(EntrustMaterialCheckoutResult::getQualitativeResult, rawDrugName))
|
||||||
.in(EntrustMaterialCheckoutResult::getEntrustId, entrustIdList));
|
.in(EntrustMaterialCheckoutResult::getEntrustId, entrustIdList));
|
||||||
|
|
||||||
if (resultList.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMetabolite) {
|
if (isMetabolite) {
|
||||||
resultList.forEach(item -> {
|
resultList.forEach(item -> {
|
||||||
if (StringUtils.isNotBlank(item.getMetaboliteResult())) {
|
if (StringUtils.isNotBlank(item.getMetaboliteResult())) {
|
||||||
@@ -795,9 +798,15 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为 Map,方便后续查找
|
// 同一检材可能同时命中组内的多个原始写法,按检材 id 去重
|
||||||
Map<String, EntrustMaterialCheckoutResult> checkoutResultMap = resultList.stream()
|
for (EntrustMaterialCheckoutResult item : resultList) {
|
||||||
.collect(Collectors.toMap(EntrustMaterialCheckoutResult::getId, Function.identity()));
|
checkoutResultMap.putIfAbsent(item.getId(), item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkoutResultMap.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 5.2 获取相关的鉴定材料
|
// 5.2 获取相关的鉴定材料
|
||||||
Set<String> materialIdSet = checkoutResultMap.keySet();
|
Set<String> materialIdSet = checkoutResultMap.keySet();
|
||||||
@@ -862,6 +871,46 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集化合物名称并按"归一化名称"归并。
|
||||||
|
*
|
||||||
|
* @param grouped 归并结果:key = 归一化名称,value = 库里的各种原始写法
|
||||||
|
* @param drugResultJson 检出结果 JSON(qualitative_result / metabolite_result)
|
||||||
|
*/
|
||||||
|
private void collectDrugNames(Map<String, Set<String>> grouped, String drugResultJson) {
|
||||||
|
if (StringUtils.isBlank(drugResultJson)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (DrugLite drugLite : DrugLiteConvert.getDrugLites(drugResultJson)) {
|
||||||
|
String rawName = drugLite.getName();
|
||||||
|
if (StringUtils.isBlank(rawName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
grouped.computeIfAbsent(normalizeDrugName(rawName), key -> new LinkedHashSet<>()).add(rawName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 化合物名称归一化。
|
||||||
|
* <p>
|
||||||
|
* 化合物字典里用星号标注管制药品(如 "氢可酮*",共 52 条),业务上它与不带星号的
|
||||||
|
* "氢可酮" 是同一化合物,所以导出时按下述规则归并:去掉星号 + 去首尾空白。
|
||||||
|
* 归并后同一个化合物在统计表中只占一行、在工作簿中只占一张工作表。
|
||||||
|
* <p>
|
||||||
|
* 注意:只影响"化合物归并/工作表名",明细行"检出结果"列仍按原始名称输出。
|
||||||
|
*
|
||||||
|
* @param drugName 原始化合物名称
|
||||||
|
* @return 归一化后的名称
|
||||||
|
*/
|
||||||
|
private static String normalizeDrugName(String drugName) {
|
||||||
|
if (StringUtils.isBlank(drugName)) {
|
||||||
|
return drugName;
|
||||||
|
}
|
||||||
|
String normalized = drugName.replace("*", "").replace("*", "").trim();
|
||||||
|
// 名称本身全是星号时归一化会变空,退回原始名称,避免出现空的分组键
|
||||||
|
return normalized.isEmpty() ? drugName : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出Excel
|
* 导出Excel
|
||||||
*
|
*
|
||||||
@@ -897,9 +946,17 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
|
|
||||||
Workbook workbook = new XSSFWorkbook();
|
Workbook workbook = new XSSFWorkbook();
|
||||||
|
|
||||||
|
// 建表阶段(工作表名、单元格填充)全部包起来:万一某条数据仍触发异常,
|
||||||
|
// 也返回可读的失败提示,而不是 500 并且把已经拼好的表一起丢掉
|
||||||
|
try {
|
||||||
createDataSheet(workbook, entrustType, resultData);
|
createDataSheet(workbook, entrustType, resultData);
|
||||||
createStatisticsSheet(workbook, resultDataMap, oldResults, entrustType, startTime, endTime);
|
createStatisticsSheet(workbook, resultDataMap, oldResults, entrustType, startTime, endTime);
|
||||||
createDrugSheets(workbook, resultDataMap, entrustType);
|
createDrugSheets(workbook, resultDataMap, entrustType);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("导出检出结果Excel失败: entrustType={}, startTime={}, endTime={}",
|
||||||
|
entrustType, startTime, endTime, e);
|
||||||
|
return R.failed("导出失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
workbook.write(response.getOutputStream());
|
workbook.write(response.getOutputStream());
|
||||||
}
|
}
|
||||||
@@ -908,13 +965,13 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
|
|
||||||
private void createDataSheet(Workbook workbook, Integer entrustType, List<EntrustmentIdentificationMaterialVO> resultData) {
|
private void createDataSheet(Workbook workbook, Integer entrustType, List<EntrustmentIdentificationMaterialVO> resultData) {
|
||||||
// 确定表名
|
// 确定表名
|
||||||
String sheetName = (entrustType == 0) ? "常规毒品" : "生物样本";
|
String sheetName = isBiologySample(entrustType) ? "生物样本" : "常规毒品";
|
||||||
Sheet sheet = workbook.createSheet(sheetName);
|
Sheet sheet = workbook.createSheet(safeSheetName(workbook, sheetName));
|
||||||
|
|
||||||
// 定义表头
|
// 定义表头
|
||||||
String[] inVivoHeaders = {"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "年龄", "性别", "委托单位", "受理日期", "检出结果"};
|
String[] inVivoHeaders = {"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "年龄", "性别", "委托单位", "受理日期", "检出结果"};
|
||||||
String[] inVitroHeaders = {"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "委托单位", "受理日期", "检出结果"};
|
String[] inVitroHeaders = {"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "委托单位", "受理日期", "检出结果"};
|
||||||
String[] headers = (entrustType == 0) ? inVitroHeaders : inVivoHeaders;
|
String[] headers = isBiologySample(entrustType) ? inVivoHeaders : inVitroHeaders;
|
||||||
|
|
||||||
// 创建表头行
|
// 创建表头行
|
||||||
Row headerRow = sheet.createRow(0);
|
Row headerRow = sheet.createRow(0);
|
||||||
@@ -942,7 +999,7 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createStatisticsSheet(Workbook workbook, Map<String, List<EntrustmentIdentificationMaterialVO>> resultDataMap, List<String> oldResults, Integer entrustType, LocalDateTime startTime, LocalDateTime endTime) {
|
private void createStatisticsSheet(Workbook workbook, Map<String, List<EntrustmentIdentificationMaterialVO>> resultDataMap, List<String> oldResults, Integer entrustType, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
Sheet sheet = workbook.createSheet("统计");
|
Sheet sheet = workbook.createSheet(safeSheetName(workbook, "统计"));
|
||||||
|
|
||||||
String[] headers = {"化合物种类", "检出数量", "总数量", "检出率"};
|
String[] headers = {"化合物种类", "检出数量", "总数量", "检出率"};
|
||||||
|
|
||||||
@@ -965,19 +1022,24 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
row.createCell(0).setCellValue(drugName);
|
row.createCell(0).setCellValue(drugName);
|
||||||
row.createCell(1).setCellValue(materials.size());
|
row.createCell(1).setCellValue(materials.size());
|
||||||
row.createCell(2).setCellValue(size);
|
row.createCell(2).setCellValue(size);
|
||||||
row.createCell(3).setCellValue(String.format("%.2f%%", materials.size() * 100.0 / size));
|
row.createCell(3).setCellValue((size == null || size == 0)
|
||||||
|
? "0.00%"
|
||||||
|
: String.format("%.2f%%", materials.size() * 100.0 / size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDrugSheets(Workbook workbook, Map<String, List<EntrustmentIdentificationMaterialVO>> resultDataMap, Integer entrustType) {
|
private void createDrugSheets(Workbook workbook, Map<String, List<EntrustmentIdentificationMaterialVO>> resultDataMap, Integer entrustType) {
|
||||||
for (Map.Entry<String, List<EntrustmentIdentificationMaterialVO>> entry : resultDataMap.entrySet()) {
|
for (Map.Entry<String, List<EntrustmentIdentificationMaterialVO>> entry : resultDataMap.entrySet()) {
|
||||||
// String drugName = entry.getKey().replaceAll("[^\u4e00-\u9fa5a-zA-Z0-9]", "");
|
// 工作表名必须净化:化合物字典里存在 "氢可酮*" 这类带 * 的名称(共 52 条)以及含 [ ] 的
|
||||||
String drugName = entry.getKey();
|
// IUPAC 名称,直接建表会抛 IllegalArgumentException 导致整份报表导出失败。
|
||||||
Sheet sheet = workbook.createSheet(drugName);
|
// 注意只清洗"工作表名",单元格里的检出结果仍保留原始名称。
|
||||||
|
String sheetName = safeSheetName(workbook, entry.getKey());
|
||||||
|
Sheet sheet = workbook.createSheet(sheetName);
|
||||||
|
|
||||||
// 定义表头
|
// 定义表头
|
||||||
String[] headers = (entrustType == 0) ? new String[]{"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "委托单位", "受理日期", "检出结果"}
|
String[] headers = isBiologySample(entrustType)
|
||||||
: new String[]{"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "年龄", "性别", "委托单位", "受理日期", "检出结果"};
|
? new String[]{"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "年龄", "性别", "委托单位", "受理日期", "检出结果"}
|
||||||
|
: new String[]{"检材类型", "检材编号", "检材名称", "检材性状", "检材质量", "委托单位", "受理日期", "检出结果"};
|
||||||
|
|
||||||
// 创建表头行
|
// 创建表头行
|
||||||
Row headerRow = sheet.createRow(0);
|
Row headerRow = sheet.createRow(0);
|
||||||
@@ -995,6 +1057,50 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 委托类型是否为"生物样本"
|
||||||
|
*
|
||||||
|
* @param entrustType 0=常规毒品, 1=生物样本
|
||||||
|
* @return true=生物样本,false(含 entrustType 为空)=常规毒品
|
||||||
|
*/
|
||||||
|
private static boolean isBiologySample(Integer entrustType) {
|
||||||
|
return entrustType != null && entrustType == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成合法且唯一的 Excel 工作表名。
|
||||||
|
* <p>
|
||||||
|
* Excel/OOXML 规范要求工作表名:不能包含 {@code : \ / ? * [ ]} 这 7 个字符、长度 1~31、不能为空、不能重名
|
||||||
|
* (POI 判重不区分大小写,校验不通过会抛 IllegalArgumentException)。
|
||||||
|
* 化合物字典里本身就有带 * 的名称(如 "氢可酮*",共 52 条)和含 [ ] 的 IUPAC 名称,
|
||||||
|
* 所以这里统一做"过滤非法字符 -> 截断 31 字符 -> 空名兜底 -> 重名加后缀(2)(3)…"。
|
||||||
|
*
|
||||||
|
* @param workbook 目标工作簿
|
||||||
|
* @param rawName 原始名称(化合物名称)
|
||||||
|
* @return 可直接用于 createSheet 的合法且唯一的工作表名
|
||||||
|
*/
|
||||||
|
private String safeSheetName(Workbook workbook, String rawName) {
|
||||||
|
String base = StrUtil.isBlank(rawName)
|
||||||
|
? ""
|
||||||
|
: rawName.replaceAll("[\\[\\]:*?/\\\\\\x00-\\x1f\\x7f]", "").trim();
|
||||||
|
if (base.isEmpty()) {
|
||||||
|
base = "未命名";
|
||||||
|
}
|
||||||
|
if (base.length() > MAX_SHEET_NAME_LENGTH) {
|
||||||
|
base = base.substring(0, MAX_SHEET_NAME_LENGTH);
|
||||||
|
}
|
||||||
|
String name = base;
|
||||||
|
int index = 2;
|
||||||
|
while (workbook.getSheet(name) != null) {
|
||||||
|
String suffix = "(" + index++ + ")";
|
||||||
|
String cut = base.length() + suffix.length() > MAX_SHEET_NAME_LENGTH
|
||||||
|
? base.substring(0, MAX_SHEET_NAME_LENGTH - suffix.length())
|
||||||
|
: base;
|
||||||
|
name = cut + suffix;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
private void fillRowData(Row row, EntrustmentIdentificationMaterialVO vo, Integer entrustType) {
|
private void fillRowData(Row row, EntrustmentIdentificationMaterialVO vo, Integer entrustType) {
|
||||||
row.createCell(0).setCellValue(vo.getOldIdentificationResult());
|
row.createCell(0).setCellValue(vo.getOldIdentificationResult());
|
||||||
row.createCell(1).setCellValue(vo.getAcceptNo());
|
row.createCell(1).setCellValue(vo.getAcceptNo());
|
||||||
@@ -1002,8 +1108,9 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
row.createCell(3).setCellValue(vo.getFormName());
|
row.createCell(3).setCellValue(vo.getFormName());
|
||||||
row.createCell(4).setCellValue(vo.getQuantity() + vo.getUnit());
|
row.createCell(4).setCellValue(vo.getQuantity() + vo.getUnit());
|
||||||
|
|
||||||
if (entrustType == 1) {
|
if (isBiologySample(entrustType)) {
|
||||||
row.createCell(5).setCellValue(vo.getMaterialAge() != null || vo.getMaterialAge() != 0 ? vo.getMaterialAge().toString() + "岁" : "未知");
|
Integer materialAge = vo.getMaterialAge();
|
||||||
|
row.createCell(5).setCellValue(materialAge != null && materialAge != 0 ? materialAge + "岁" : "未知");
|
||||||
row.createCell(6).setCellValue(StringUtils.isNotBlank(vo.getBiologyGender()) ? vo.getBiologyGender() : "未知");
|
row.createCell(6).setCellValue(StringUtils.isNotBlank(vo.getBiologyGender()) ? vo.getBiologyGender() : "未知");
|
||||||
row.createCell(7).setCellValue(vo.getOrgName());
|
row.createCell(7).setCellValue(vo.getOrgName());
|
||||||
row.createCell(8).setCellValue(LocalDateTimeUtil.format(vo.getAcceptTime(), "yyyy-MM-dd"));
|
row.createCell(8).setCellValue(LocalDateTimeUtil.format(vo.getAcceptTime(), "yyyy-MM-dd"));
|
||||||
@@ -1052,23 +1159,24 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<Entrus
|
|||||||
wrapper.ge(Entrustment::getAcceptTime, queryStartTime);
|
wrapper.ge(Entrustment::getAcceptTime, queryStartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<String> results = new ArrayList<>();
|
// 鉴定情况过滤条件("委托" 等价于首次/补充/重新鉴定)
|
||||||
for (String oldResult : oldResults) {
|
List<String> results = getOriginalIdentificationList(oldResults);
|
||||||
if (oldResult.equals("委托")) {
|
|
||||||
results.add("首次鉴定");
|
|
||||||
results.add("补充鉴定");
|
|
||||||
results.add("重新鉴定");
|
|
||||||
} else results.add(oldResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询符合条件的 Entrustment 记录
|
// 查询符合条件的 Entrustment 记录
|
||||||
List<Entrustment> entrusts = entrustmentService.list(wrapper
|
LambdaQueryWrapper<Entrustment> entrustWrapper = wrapper
|
||||||
.eq(Entrustment::getEntrustmentType, entrustType)
|
.eq(Entrustment::getEntrustmentType, entrustType)
|
||||||
.ge(Entrustment::getStatus, EntrustStatusConstants.ENTRUST_STATUS_ACCEPTED.getStatus())
|
.ge(Entrustment::getStatus, EntrustStatusConstants.ENTRUST_STATUS_ACCEPTED.getStatus());
|
||||||
.in(Entrustment::getOldIdentificationResult, results)
|
// 注意:鉴定情况为空时不能拼 in 条件,否则 MyBatis-Plus 会生成 "IN ()" 造成 SQL 语法错误
|
||||||
);
|
if (CollUtil.isNotEmpty(results)) {
|
||||||
|
entrustWrapper.in(Entrustment::getOldIdentificationResult, results);
|
||||||
|
}
|
||||||
|
List<Entrustment> entrusts = entrustmentService.list(entrustWrapper);
|
||||||
|
|
||||||
List<String> entrustIds = entrusts.stream().map(Entrustment::getId).collect(Collectors.toList());
|
List<String> entrustIds = entrusts.stream().map(Entrustment::getId).collect(Collectors.toList());
|
||||||
|
// 没有委托时直接返回 0,同样避免 "IN ()"
|
||||||
|
if (CollUtil.isEmpty(entrustIds)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return entrustmentIdentificationMaterialService.lambdaQuery()
|
return entrustmentIdentificationMaterialService.lambdaQuery()
|
||||||
.in(EntrustmentIdentificationMaterial::getEntrustmentId, entrustIds)
|
.in(EntrustmentIdentificationMaterial::getEntrustmentId, entrustIds)
|
||||||
|
|||||||
+32
-4
@@ -1661,15 +1661,43 @@ public class EntrustmentIdentificationMaterialServiceImpl extends ServiceImpl<En
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<EntrustmentIdentificationMaterialVO> sortVoByAcceptNo(List<EntrustmentIdentificationMaterialVO> materials) {
|
public List<EntrustmentIdentificationMaterialVO> sortVoByAcceptNo(List<EntrustmentIdentificationMaterialVO> materials) {
|
||||||
materials.sort(Comparator.comparing((EntrustmentIdentificationMaterial m) -> {
|
if (materials == null || materials.isEmpty()) {
|
||||||
String[] parts = m.getAcceptNo().split("-");
|
return materials;
|
||||||
return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), Integer.parseInt(parts[2])};
|
}
|
||||||
}, Comparator.comparingInt((int[] arr) -> arr[0]) // 按年份排序
|
materials.sort(Comparator.comparing((EntrustmentIdentificationMaterial m) -> parseAcceptNoSortKey(m.getAcceptNo()),
|
||||||
|
Comparator.comparingInt((int[] arr) -> arr[0]) // 按年份排序
|
||||||
.thenComparingInt(arr -> arr[1]) // 按流水号排序
|
.thenComparingInt(arr -> arr[1]) // 按流水号排序
|
||||||
.thenComparingInt(arr -> arr[2]))); // 按子流水号排序
|
.thenComparingInt(arr -> arr[2]))); // 按子流水号排序
|
||||||
return materials;
|
return materials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把受理编号(形如 "2025-3-7")解析成排序键 {年, 流水号, 子流水号}。
|
||||||
|
* <p>
|
||||||
|
* 编号为空或格式不符合预期时返回最大值排到最后,避免排序过程中抛
|
||||||
|
* NullPointerException / NumberFormatException 导致接口报错。
|
||||||
|
*
|
||||||
|
* @param acceptNo 受理编号
|
||||||
|
* @return 排序键
|
||||||
|
*/
|
||||||
|
private static int[] parseAcceptNoSortKey(String acceptNo) {
|
||||||
|
if (StrUtil.isNotBlank(acceptNo)) {
|
||||||
|
String[] parts = acceptNo.trim().split("-");
|
||||||
|
if (parts.length == 3) {
|
||||||
|
try {
|
||||||
|
return new int[]{
|
||||||
|
Integer.parseInt(parts[0].trim()),
|
||||||
|
Integer.parseInt(parts[1].trim()),
|
||||||
|
Integer.parseInt(parts[2].trim())
|
||||||
|
};
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
// 走下面的兜底分支
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
/**
|
/**
|
||||||
* 根据嫌疑人信息生成委托检材信息
|
* 根据嫌疑人信息生成委托检材信息
|
||||||
|
|||||||
+70
-15
@@ -362,26 +362,68 @@ public class ExcelOperationServiceImpl implements ExcelOperationService {
|
|||||||
*/
|
*/
|
||||||
private List<EntrustmentIdentificationMaterial> processHairExcelDataToMaterialEntity(List<Map<String, String>> data, Entrustment entrustment, Map<String, DrugLite> drugLiteMap, int orderNo, CaseEvent cj) {
|
private List<EntrustmentIdentificationMaterial> processHairExcelDataToMaterialEntity(List<Map<String, String>> data, Entrustment entrustment, Map<String, DrugLite> drugLiteMap, int orderNo, CaseEvent cj) {
|
||||||
List<EntrustmentIdentificationMaterial> entrustmentIdentificationMaterialList = new ArrayList<>();
|
List<EntrustmentIdentificationMaterial> entrustmentIdentificationMaterialList = new ArrayList<>();
|
||||||
for (Map<String, String> datum : data) {
|
List<String> errorMessages = new ArrayList<>();
|
||||||
// 构建检材对象
|
for (int i = 0; i < data.size(); i++) {
|
||||||
|
Map<String, String> datum = data.get(i);
|
||||||
|
int rowNum = i + 2; // Excel行号(数据从第2行开始)
|
||||||
|
String idNum = null;
|
||||||
|
String name = null;
|
||||||
|
try {
|
||||||
|
name = datum.get("姓名");
|
||||||
|
// ---------- 1. 姓名必填校验 ----------
|
||||||
|
if (StrUtil.isBlank(name)) {
|
||||||
|
throw new IllegalArgumentException("姓名不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 2. 身份证号校验 ----------
|
||||||
|
idNum = datum.get("身份证号");
|
||||||
|
if (StrUtil.isBlank(idNum)) {
|
||||||
|
throw new IllegalArgumentException("身份证号不能为空");
|
||||||
|
}
|
||||||
|
if (idNum.length() != 18) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"身份证号[%s]长度不正确(当前%d位,应为18位),请检查身份证号是否完整",
|
||||||
|
idNum, idNum.length()));
|
||||||
|
}
|
||||||
|
// 性别解析
|
||||||
|
String gender = this.getSexByIdNum(idNum);
|
||||||
|
if (StrUtil.isBlank(gender)) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"身份证号[%s]性别解析失败,请检查第17位是否为数字0~9", idNum));
|
||||||
|
}
|
||||||
|
// 年龄解析
|
||||||
|
String ageStr = this.getAgeByIdNum(idNum);
|
||||||
|
if (StrUtil.isBlank(ageStr)) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"身份证号[%s]出生日期解析失败,请检查第7~14位是否为合法日期(yyyyMMdd格式),如19900520",
|
||||||
|
idNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 3. 检测结果必填校验 ----------
|
||||||
|
String testResult = datum.get("检测结果");
|
||||||
|
if (StrUtil.isBlank(testResult)) {
|
||||||
|
throw new IllegalArgumentException("检测结果不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 4. 采样单位校验 ----------
|
||||||
|
String drawPlace = datum.get("采样单位");
|
||||||
|
if (StrUtil.isBlank(drawPlace)) {
|
||||||
|
throw new IllegalArgumentException("采样单位不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 构建检材对象 ----------
|
||||||
EntrustmentIdentificationMaterial material = new EntrustmentIdentificationMaterial();
|
EntrustmentIdentificationMaterial material = new EntrustmentIdentificationMaterial();
|
||||||
material.setEntrustmentId(entrustment.getId());
|
material.setEntrustmentId(entrustment.getId());
|
||||||
material.setCaseId(entrustment.getCaseId());
|
material.setCaseId(entrustment.getCaseId());
|
||||||
material.setName(datum.get("姓名") + "毛发");
|
material.setName(name + "毛发");
|
||||||
material.setRemark(datum.get("身份证号"));
|
material.setRemark(idNum);
|
||||||
|
material.setBiologyGender(gender);
|
||||||
// 解析身份证号
|
material.setMaterialAge(Integer.valueOf(ageStr));
|
||||||
String idNum = datum.get("身份证号");
|
material.setDrawPlace(drawPlace);
|
||||||
if (idNum != null && idNum.length() == 18) {
|
|
||||||
material.setBiologyGender(this.getSexByIdNum(idNum));
|
|
||||||
material.setMaterialAge(Integer.valueOf(this.getAgeByIdNum(idNum)));
|
|
||||||
}
|
|
||||||
|
|
||||||
material.setDrawPlace(datum.get("采样单位"));
|
|
||||||
material.setId(IdWorker.get32UUID().toUpperCase());
|
material.setId(IdWorker.get32UUID().toUpperCase());
|
||||||
|
|
||||||
// 检测结果解析
|
// 检测结果解析
|
||||||
String[] drugs = datum.get("检测结果").split("、");
|
String[] drugs = testResult.split("、");
|
||||||
buildMatchedDrugs(drugLiteMap, material, drugs);
|
buildMatchedDrugs(drugLiteMap, material, drugs);
|
||||||
|
|
||||||
// 设置检材默认属性
|
// 设置检材默认属性
|
||||||
@@ -404,6 +446,17 @@ public class ExcelOperationServiceImpl implements ExcelOperationService {
|
|||||||
material.setSample1No(sampleService.getNewSampleNo(material.getImNo(), 1));
|
material.setSample1No(sampleService.getNewSampleNo(material.getImNo(), 1));
|
||||||
|
|
||||||
entrustmentIdentificationMaterialList.add(material);
|
entrustmentIdentificationMaterialList.add(material);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
String idInfo = StrUtil.isNotBlank(idNum) ? (" 证件号[" + idNum + "]") : "";
|
||||||
|
String nameInfo = StrUtil.isNotBlank(name) ? name : "未知";
|
||||||
|
errorMessages.add(String.format("第%d行 姓名[%s]%s: %s", rowNum, nameInfo, idInfo, e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一次性汇总所有错误返回给用户
|
||||||
|
if (!errorMessages.isEmpty()) {
|
||||||
|
throw new CheckedException("Excel导入校验失败,共" + errorMessages.size() + "处错误:\n" + String.join("\n", errorMessages));
|
||||||
}
|
}
|
||||||
return entrustmentIdentificationMaterialList;
|
return entrustmentIdentificationMaterialList;
|
||||||
}
|
}
|
||||||
@@ -708,7 +761,7 @@ public class ExcelOperationServiceImpl implements ExcelOperationService {
|
|||||||
int genderCode = Integer.parseInt(idNumber.substring(16, 17));
|
int genderCode = Integer.parseInt(idNumber.substring(16, 17));
|
||||||
return genderCode % 2 == 0 ? "女" : "男";
|
return genderCode % 2 == 0 ? "女" : "男";
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
log.warn("身份证号[{}] 性别解析失败: {}", idNumber, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
@@ -726,6 +779,8 @@ public class ExcelOperationServiceImpl implements ExcelOperationService {
|
|||||||
return String.valueOf(age);
|
return String.valueOf(age);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
log.warn("身份证号[{}] 年龄解析失败(出生日期[{}-{}]): {}", idNumber,
|
||||||
|
idNumber.substring(6, 10), idNumber.substring(10, 14), e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
Reference in New Issue
Block a user