From 5a42a0fc5af481e3a09694cf3db7178fa7afca9c Mon Sep 17 00:00:00 2001 From: lapuda <577732344@qq.com> Date: Tue, 21 Jul 2026 16:37:24 +0800 Subject: [PATCH] fix: parse string datetime cells in external import --- .../ExternalJobExcelParser.java | 37 ++++++++++++++++++- .../ExternalJobExcelParserTest.java | 29 ++++++++++++--- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobExcelParser.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobExcelParser.java index fc6a8af..497137a 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobExcelParser.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobExcelParser.java @@ -1,6 +1,7 @@ package com.ruoyi.cms.externalimport; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.Row; @@ -11,11 +12,14 @@ import org.apache.poi.ss.usermodel.WorkbookFactory; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 解析外部岗位模板。此解析器明确处理 Excel 日期单元格,替代旧导入逻辑中日期全部丢失的行为。 @@ -24,6 +28,7 @@ public class ExternalJobExcelParser { private static final String[] REQUIRED_HEADERS = { "Aca112", "Acb22a", "SalaryLow", "SalaryHight", "Aae397", "AAB004", "ORG", "ACE760", "Collect_time" }; + private static final Pattern TEXT_DATE_PATTERN = Pattern.compile("^\\s*(\\d{4})\\D+(\\d{1,2})\\D+(\\d{1,2}).*$"); private final DataFormatter formatter = new DataFormatter(); private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); @@ -114,11 +119,39 @@ public class ExternalJobExcelParser { if (cell == null) { return ""; } - if (DateUtil.isCellDateFormatted(cell)) { + if (isNumericDateCell(cell)) { Date value = cell.getDateCellValue(); return value == null ? "" : dateFormat.format(value); } - return formatter.formatCellValue(cell).trim(); + return normalizeTextDate(formatter.formatCellValue(cell)); + } + + /** + * DateUtil.isCellDateFormatted 会对 STRING 单元格读取数值,导致 POI 抛出类型不匹配异常。 + * 只有数值型或数值结果公式才允许进入 Excel 日期格式判断。 + */ + private boolean isNumericDateCell(Cell cell) { + CellType type = cell.getCellType(); + if (type == CellType.NUMERIC) { + return DateUtil.isCellDateFormatted(cell); + } + return type == CellType.FORMULA + && cell.getCachedFormulaResultType() == CellType.NUMERIC + && DateUtil.isCellDateFormatted(cell); + } + + /** 将 2026-07-14 00:00:00、2026/7/14、2026年7月14日 等文本统一为 ISO 日期。 */ + private String normalizeTextDate(String value) { + String text = value == null ? "" : value.trim(); + Matcher matcher = TEXT_DATE_PATTERN.matcher(text); + if (!matcher.matches()) { + return text; + } + try { + return LocalDate.of(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3))).toString(); + } catch (Exception ex) { + return text; + } } private boolean isBlank(Row row) { diff --git a/ruoyi-bussiness/src/test/java/com/ruoyi/cms/externalimport/ExternalJobExcelParserTest.java b/ruoyi-bussiness/src/test/java/com/ruoyi/cms/externalimport/ExternalJobExcelParserTest.java index d0926d2..2caf399 100644 --- a/ruoyi-bussiness/src/test/java/com/ruoyi/cms/externalimport/ExternalJobExcelParserTest.java +++ b/ruoyi-bussiness/src/test/java/com/ruoyi/cms/externalimport/ExternalJobExcelParserTest.java @@ -16,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; class ExternalJobExcelParserTest { @Test void parsesExcelDatesAndLocationFallback() throws Exception { - byte[] data = workbookBytes(); + byte[] data = workbookBytes(false); List rows = new ExternalJobExcelParser().parse(new ByteArrayInputStream(data)); assertEquals(1, rows.size()); @@ -27,7 +27,16 @@ class ExternalJobExcelParserTest { assertEquals("Java 工程师", row.getJobTitle()); } - private byte[] workbookBytes() throws Exception { + @Test + void parsesStringDateTimeCellsWithoutTreatingThemAsNumericDates() throws Exception { + List rows = new ExternalJobExcelParser().parse(new ByteArrayInputStream(workbookBytes(true))); + + assertEquals(1, rows.size()); + assertEquals("2026-07-14", rows.get(0).getPostingDate()); + assertEquals("2026-07-20", rows.get(0).getCollectDate()); + } + + private byte[] workbookBytes(boolean useStringDateTimes) throws Exception { String[] headers = {"Aca112", "Acb22a", "SalaryLow", "SalaryHight", "Aae397", "AAB004", "ORG", "ACE760", "Collect_time", "City"}; try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { Row header = workbook.createSheet("合并数据").createRow(0); @@ -41,13 +50,21 @@ class ExternalJobExcelParserTest { data.createCell(1).setCellValue("负责平台开发"); data.createCell(2).setCellValue(5000); data.createCell(3).setCellValue(10000); - data.createCell(4).setCellValue(Date.from(LocalDate.of(2026, 7, 14).atStartOfDay(java.time.ZoneId.systemDefault()).toInstant())); - data.getCell(4).setCellStyle(dateStyle); + if (useStringDateTimes) { + data.createCell(4).setCellValue("2026-07-14 00:00:00"); + } else { + data.createCell(4).setCellValue(Date.from(LocalDate.of(2026, 7, 14).atStartOfDay(java.time.ZoneId.systemDefault()).toInstant())); + data.getCell(4).setCellStyle(dateStyle); + } data.createCell(5).setCellValue("示例企业"); data.createCell(6).setCellValue("智联招聘"); data.createCell(7).setCellValue("https://www.zhaopin.com/job/42"); - data.createCell(8).setCellValue(Date.from(LocalDate.of(2026, 7, 20).atStartOfDay(java.time.ZoneId.systemDefault()).toInstant())); - data.getCell(8).setCellStyle(dateStyle); + if (useStringDateTimes) { + data.createCell(8).setCellValue("2026-07-20 00:00:00"); + } else { + data.createCell(8).setCellValue(Date.from(LocalDate.of(2026, 7, 20).atStartOfDay(java.time.ZoneId.systemDefault()).toInstant())); + data.getCell(8).setCellStyle(dateStyle); + } data.createCell(9).setCellValue("石河子市"); workbook.write(output); return output.toByteArray();