|
|
|
@ -35,14 +35,12 @@ import java.io.IOException; |
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.net.URLEncoder; |
|
|
|
import java.net.URLEncoder; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.time.LocalDate; |
|
|
|
import java.time.*; |
|
|
|
import java.time.LocalDateTime; |
|
|
|
|
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
|
|
|
|
import java.time.format.DateTimeFormatterBuilder; |
|
|
|
|
|
|
|
import java.time.temporal.ChronoField; |
|
|
|
import java.time.temporal.ChronoUnit; |
|
|
|
import java.time.temporal.ChronoUnit; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.*; |
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.Optional; |
|
|
|
|
|
|
|
import java.util.function.Function; |
|
|
|
import java.util.function.Function; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
|
|
@ -417,61 +415,162 @@ public class ExcelOperationServiceImpl implements ExcelOperationService { |
|
|
|
* @param cj 案件信息对象 |
|
|
|
* @param cj 案件信息对象 |
|
|
|
* @return |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private List<EntrustmentIdentificationMaterial> processExcelDataToMaterialEntity(List<Map<String, String>> data, Entrustment entrustment, Map<String, DrugLite> drugLiteMap, int orderNo, CaseEvent cj) { |
|
|
|
private List<EntrustmentIdentificationMaterial> processExcelDataToMaterialEntity( |
|
|
|
|
|
|
|
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<>(); // 用于收集错误信息
|
|
|
|
// 构建检材对象
|
|
|
|
|
|
|
|
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("提取地点")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检测结果解析
|
|
|
|
for (int i = 0; i < data.size(); i++) { |
|
|
|
String[] drugs = datum.get("筛查目标物").split("、"); |
|
|
|
Map<String, String> datum = data.get(i); |
|
|
|
buildMatchedDrugs(drugLiteMap, material, drugs); |
|
|
|
int rowNum = i + 1; // 实际行号用于报错提示
|
|
|
|
// 设置委托类型
|
|
|
|
|
|
|
|
material.setType(String.valueOf(entrustment.getEntrustmentType())); |
|
|
|
try { |
|
|
|
if (entrustment.getEntrustmentType() == 0) { |
|
|
|
// 1. 基础数据校验
|
|
|
|
material.setTypeName("常规毒品"); |
|
|
|
validateField(datum, "疑似物种类", rowNum); |
|
|
|
} else { |
|
|
|
validateField(datum, "提取时间", rowNum); |
|
|
|
material.setTypeName("生物样本"); |
|
|
|
|
|
|
|
material.setBiologyType(EntrustBiologyType.isExist(formDesc).getDesc()); |
|
|
|
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++; |
|
|
|
// 5. 如果有错误,抛出异常或返回给前端
|
|
|
|
String packageInfo = datum.get("包装信息"); |
|
|
|
if (!errorMessages.isEmpty()) { |
|
|
|
if (packageInfo.equals("完整")) { |
|
|
|
throw new RuntimeException("Excel导入校验失败:\n" + String.join("\n", errorMessages)); |
|
|
|
material.setPackComplete(true); |
|
|
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
material.setPackComplete(false); |
|
|
|
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<String, String> 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 + "数值格式错误"); } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
|