From 59fc40af97443e35989a46fdd59c49d67a0f3fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B5=B7=E8=88=AA?= <11918452+yang-haihang@user.noreply.gitee.com> Date: Thu, 12 Mar 2026 09:19:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=80=81=E6=A3=80=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/CheckoutResultExcelDTO.java | 6 + ...rustMaterialCheckoutResultServiceImpl.java | 64 ++++-- .../impl/ExcelOperationServiceImpl.java | 207 +++++++++++++----- 3 files changed, 201 insertions(+), 76 deletions(-) diff --git a/src/main/java/digital/laboratory/platform/entrustment/dto/CheckoutResultExcelDTO.java b/src/main/java/digital/laboratory/platform/entrustment/dto/CheckoutResultExcelDTO.java index d98bd61..c2a5e3c 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/dto/CheckoutResultExcelDTO.java +++ b/src/main/java/digital/laboratory/platform/entrustment/dto/CheckoutResultExcelDTO.java @@ -4,6 +4,9 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import java.time.LocalDate; +import java.time.LocalDateTime; + @Data @ApiModel(value = "CheckoutResultExcelDTO", description = "存储要导出的检出结果excel数据DTO对象") public class CheckoutResultExcelDTO { @@ -14,6 +17,9 @@ public class CheckoutResultExcelDTO { @ApiModelProperty("送检日期") private String deliverTime; + @ApiModelProperty("受理时间") + private LocalDateTime acceptTime; + @ApiModelProperty("送检单位") private String clientOrgName; diff --git a/src/main/java/digital/laboratory/platform/entrustment/service/impl/EntrustMaterialCheckoutResultServiceImpl.java b/src/main/java/digital/laboratory/platform/entrustment/service/impl/EntrustMaterialCheckoutResultServiceImpl.java index 5f2e5ce..5af51a7 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/service/impl/EntrustMaterialCheckoutResultServiceImpl.java +++ b/src/main/java/digital/laboratory/platform/entrustment/service/impl/EntrustMaterialCheckoutResultServiceImpl.java @@ -189,7 +189,7 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl checkoutResultExcelDTOS) throws IOException { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); - // 表头导入 + + // 表头 List head = buildExcelHead(); - // 算上表头的行数 + int rowCount = checkoutResultExcelDTOS.size() + 1; + for (int i = 0; i < rowCount; i++) { + + Row row = sheet.createRow(i); + + // 第一行:表头 if (i == 0) { - Row row = sheet.createRow(i); for (int j = 0; j < head.size(); j++) { row.createCell(j).setCellValue(head.get(j)); } - } else { - // 开始写入具体数据内容 - Row row = sheet.createRow(i); - CheckoutResultExcelDTO excelDTO = checkoutResultExcelDTOS.get(i - 1); - row.createCell(0).setCellValue(excelDTO.getOrder()); - row.createCell(1).setCellValue(excelDTO.getDeliverTime()); - row.createCell(2).setCellValue(excelDTO.getClientOrgName()); - row.createCell(3).setCellValue(excelDTO.getProvinceCollectPlace()); - row.createCell(4).setCellValue(excelDTO.getCityCollectPlace()); - row.createCell(5).setCellValue(excelDTO.getAcceptNo()); - row.createCell(6).setCellValue(excelDTO.getOrderNo()); - row.createCell(7).setCellValue(excelDTO.getTypeName()); - row.createCell(8).setCellValue(excelDTO.getColor()); - row.createCell(9).setCellValue(excelDTO.getFormName()); - row.createCell(10).setCellValue(excelDTO.getQualitativeResult()); - row.createCell(11).setCellValue(excelDTO.getQuantitativeResult()); - row.createCell(12).setCellValue(excelDTO.getOtherResult()); - row.createCell(13).setCellValue(excelDTO.getRemark()); + continue; } + + // 数据填充 + CheckoutResultExcelDTO excelDTO = checkoutResultExcelDTOS.get(i - 1); + + row.createCell(0).setCellValue(getValue(excelDTO.getOrder())); + row.createCell(1).setCellValue(getValue(excelDTO.getDeliverTime())); // 已经是 String + row.createCell(2).setCellValue(getValue(excelDTO.getClientOrgName())); + row.createCell(3).setCellValue(getValue(excelDTO.getProvinceCollectPlace())); + row.createCell(4).setCellValue(getValue(excelDTO.getCityCollectPlace())); + row.createCell(5).setCellValue(getValue(excelDTO.getAcceptNo())); + row.createCell(6).setCellValue(getValue(excelDTO.getOrderNo())); + row.createCell(7).setCellValue(getValue(excelDTO.getTypeName())); + row.createCell(8).setCellValue(getValue(excelDTO.getColor())); + row.createCell(9).setCellValue(getValue(excelDTO.getFormName())); + row.createCell(10).setCellValue(getValue(excelDTO.getQualitativeResult())); + row.createCell(11).setCellValue(getValue(excelDTO.getQuantitativeResult())); + row.createCell(12).setCellValue(getValue(excelDTO.getOtherResult())); + row.createCell(13).setCellValue(getValue(excelDTO.getRemark())); } + // 输出到流 workbook.write(response.getOutputStream()); } + private String getValue(Object obj) { + return obj == null || obj.toString().isEmpty() ? "无" : obj.toString(); + } + + private List buildExcelHead() { List headList = new ArrayList<>(); headList.add("序号"); @@ -366,6 +378,7 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl processExcelDataToMaterialEntity(List> data, Entrustment entrustment, Map drugLiteMap, int orderNo, CaseEvent cj) { + private List processExcelDataToMaterialEntity( + List> data, + Entrustment entrustment, + Map drugLiteMap, + int orderNo, + CaseEvent cj) { + List entrustmentIdentificationMaterialList = new ArrayList<>(); - for (Map datum : data) { - // 构建检材对象 - EntrustmentIdentificationMaterial material = new EntrustmentIdentificationMaterial(); - material.setId(IdWorker.get32UUID().toUpperCase()); - material.setEntrustmentId(entrustment.getId()); - material.setCaseId(entrustment.getCaseId()); - material.setName(datum.get("疑似物种类")); - material.setRtSampleQuantity(Integer.valueOf(datum.get("留存样个数"))); - material.setQuantity(new BigDecimal(datum.get("重量/体积"))); - material.setUnit(datum.get("单位")); - String formDesc = datum.get("性状描述"); - material.setForm(formDesc); - material.setFormName(formDesc); - // 提取颜色 - if (StrUtil.isNotBlank(formDesc) && formDesc.contains("色")) { - // 截取“色”之前的部分(包含“色”) - material.setColor(formDesc.substring(0, formDesc.indexOf("色") + 1)); - } - material.setDrawTime(LocalDate.parse(datum.get("提取时间"), DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay()); - material.setDrawPlace(datum.get("提取地点")); + List errorMessages = new ArrayList<>(); // 用于收集错误信息 - // 检测结果解析 - String[] drugs = datum.get("筛查目标物").split("、"); - buildMatchedDrugs(drugLiteMap, material, drugs); - // 设置委托类型 - material.setType(String.valueOf(entrustment.getEntrustmentType())); - if (entrustment.getEntrustmentType() == 0) { - material.setTypeName("常规毒品"); - } else { - material.setTypeName("生物样本"); - material.setBiologyType(EntrustBiologyType.isExist(formDesc).getDesc()); + for (int i = 0; i < data.size(); i++) { + Map datum = data.get(i); + int rowNum = i + 1; // 实际行号用于报错提示 + + try { + // 1. 基础数据校验 + validateField(datum, "疑似物种类", rowNum); + validateField(datum, "提取时间", rowNum); + + EntrustmentIdentificationMaterial material = new EntrustmentIdentificationMaterial(); + material.setId(IdWorker.get32UUID().toUpperCase()); + material.setEntrustmentId(entrustment.getId()); + material.setCaseId(entrustment.getCaseId()); + + material.setName(datum.get("疑似物种类")); + + // 2. 数值转换校验 (防止 Integer.valueOf 报错) + material.setRtSampleQuantity(convertToInt(datum.get("留存样个数"), "留存样个数", rowNum)); + material.setQuantity(convertToBigDecimal(datum.get("重量/体积"), "重量/体积", rowNum)); + material.setUnit(datum.get("单位")); + + // 3. 增强日期解析 (支持 yyyy-MM-dd 和 EEE MMM dd ... 格式) + String drawTimeStr = datum.get("提取时间"); + material.setDrawTime(flexibleDateParse(drawTimeStr, rowNum)); + + // 4. 原有业务逻辑 + String formDesc = datum.getOrDefault("性状描述", ""); + material.setForm(formDesc); + material.setFormName(formDesc); + if (StrUtil.isNotBlank(formDesc) && formDesc.contains("色")) { + material.setColor(formDesc.substring(0, formDesc.indexOf("色") + 1)); + } + material.setDrawPlace(datum.get("提取地点")); + + String screeningObj = datum.getOrDefault("筛查目标物", ""); + String[] drugs = screeningObj.split("、"); + buildMatchedDrugs(drugLiteMap, material, drugs); + + material.setType(String.valueOf(entrustment.getEntrustmentType())); + if (entrustment.getEntrustmentType() == 0) { + material.setTypeName("常规毒品"); + } else { + material.setTypeName("生物样本"); + material.setBiologyType(EntrustBiologyType.isExist(formDesc).getDesc()); + } + + material.setOrderNo(orderNo); + material.setImEntrustNumber(String.valueOf(orderNo)); + orderNo++; + + material.setPackComplete("完整".equals(datum.get("包装信息"))); + + if (StrUtil.isNotBlank(datum.get("年龄"))) { + material.setMaterialAge(convertToInt(datum.get("年龄"), "年龄", rowNum)); + } + + material.setBiologyGender(datum.get("性别")); + material.setAnalysisOption(AnalysisOptionEnums.fromDesc(datum.get("检验项目")).getCode()); + + entrustmentIdentificationMaterialService.setMaterialIdentificationNo(material, cj); + material.setSample1No(sampleService.getNewSampleNo(material.getImNo(), 1)); + + entrustmentIdentificationMaterialList.add(material); + + } catch (Exception e) { + errorMessages.add("第" + rowNum + "行处理失败: " + e.getMessage()); } - material.setOrderNo(orderNo); - material.setImEntrustNumber(String.valueOf(orderNo)); - orderNo++; - String packageInfo = datum.get("包装信息"); - if (packageInfo.equals("完整")) { - material.setPackComplete(true); - } else { - material.setPackComplete(false); + } + + // 5. 如果有错误,抛出异常或返回给前端 + if (!errorMessages.isEmpty()) { + throw new RuntimeException("Excel导入校验失败:\n" + String.join("\n", errorMessages)); + } + + return entrustmentIdentificationMaterialList; + } + + /** + * 极度灵活的日期解析器:支持 yyyy-MM-dd, yyyy/MM/dd, UTC格式, 以及时间戳 + */ + private LocalDateTime flexibleDateParse(String dateStr, int rowNum) { + if (StrUtil.isBlank(dateStr)) return null; + + dateStr = dateStr.trim(); + + // 1. 尝试解析常见的数字分割格式 (2025-10-10, 2025/10/10, 2025.10.10) + String standardizedDate = dateStr.replace("/", "-").replace(".", "-"); + if (standardizedDate.matches("\\d{4}-\\d{1,2}-\\d{1,2}.*")) { + try { + // 取前10位进行日期转换 + String datePart = standardizedDate.split(" ")[0]; + // 考虑月日可能是一位数的情况,这里用 DateTimeFormatter 更稳妥 + DateTimeFormatter df = new DateTimeFormatterBuilder() + .appendPattern("yyyy-") + .appendValue(ChronoField.MONTH_OF_YEAR) + .appendLiteral("-") + .appendValue(ChronoField.DAY_OF_MONTH) + .toFormatter(); + return LocalDate.parse(datePart, df).atStartOfDay(); + } catch (Exception e) { + // 如果正则匹配但解析失败,继续往下走 } - String age = datum.get("年龄"); - if (StrUtil.isNotBlank(age)) { - material.setMaterialAge(Integer.valueOf(age)); + } + + // 2. 尝试解析 UTC 格式 (Fri Oct 10 00:00:00 UTC 2025) + if (dateStr.contains("UTC") || dateStr.matches("^[a-zA-Z].*")) { + try { + DateTimeFormatter utcFormatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US); + return ZonedDateTime.parse(dateStr, utcFormatter).toLocalDateTime(); + } catch (Exception e) { + // 继续尝试 } - material.setBiologyGender(datum.get("性别")); - material.setAnalysisOption(AnalysisOptionEnums.fromDesc(datum.get("检验项目")).getCode()); - entrustmentIdentificationMaterialService.setMaterialIdentificationNo(material, cj); - material.setSample1No(sampleService.getNewSampleNo(material.getImNo(), 1)); + } - entrustmentIdentificationMaterialList.add(material); + // 3. 尝试解析纯数字时间戳 (部分 Excel 导入框架会把日期转为 Excel 序列天数或 Unix 时间戳) + if (dateStr.matches("^\\d+$")) { + try { + long val = Long.parseLong(dateStr); + // 如果数字很小,可能是 Excel 的天数格式 (比如 45678) + if (val < 100000) { + return LocalDateTime.of(1899, 12, 30, 0, 0).plusDays(val); + } + // 否则视为秒级或毫秒级时间戳 + return LocalDateTime.ofInstant(Instant.ofEpochMilli(val > 10000000000L ? val : val * 1000), ZoneId.systemDefault()); + } catch (Exception e) { + // 继续尝试 + } } - return entrustmentIdentificationMaterialList; + + // 如果所有尝试都失败了,给用户清晰的反馈 + throw new IllegalArgumentException("日期格式[" + dateStr + "]无法识别。支持格式: 2025-10-10, 2025/10/10 或标准系统格式"); + } + + // 辅助转换方法... + private void validateField(Map map, String key, int row) { + if (StrUtil.isBlank(map.get(key))) throw new IllegalArgumentException(key + "不能为空"); + } + + private Integer convertToInt(String val, String fieldName, int row) { + try { return StrUtil.isBlank(val) ? 0 : Integer.valueOf(val); } + catch (Exception e) { throw new IllegalArgumentException(fieldName + "数字格式错误"); } + } + + private BigDecimal convertToBigDecimal(String val, String fieldName, int row) { + try { return StrUtil.isBlank(val) ? BigDecimal.ZERO : new BigDecimal(val); } + catch (Exception e) { throw new IllegalArgumentException(fieldName + "数值格式错误"); } } /**