✨ 批量导入日期宽容解析:兼容 2026-08-02 与 2026-8-2 等书写变体
- blade-common 新增 LenientDateParser(宽容解析:- / . 分隔符、补零与否、 时间可省略秒/分;拒绝两位年份、日在前、无分隔符) - 新增 LenientDateStringConverter / LenientDateTimeStringConverter: Excel 数值日期转文本透传,文本解析留待服务层以进入失败明细流程 - 8 个裸走 fastexcel 内置转换的导入 DTO(保险/年检/事故/其他费用/运单/ 币种/用户生日/设备台账)日期字段改为 String 承载,服务层统一经 LenientDateParser 解析,失败报「<列名> 日期格式无法识别:<原值>」 - MaintenancePlanDateTimeConverter / TireReplacementDateStringConverter 内部委托公共解析器,6 个 DTO 注解零改动 - 运输计划、运单批量、运输对账、换胎 4 处手动解析点收敛至同一工具类 - UserMapper 导出 SQL 的生日改 DATE_FORMAT 输出,保持导出格式不变 口径见根工作区 docs/import-spec.md(weicw/tms-erp#1)。 回归:port-terminal 45 断言、violation-record 40 断言(含新增不补零 用例 09)、waybill-import 48 断言、insurance-record 新增用例 6 断言, 全部通过。
This commit is contained in:
@@ -22,6 +22,10 @@
|
|||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-starter-loadbalancer</artifactId>
|
<artifactId>blade-starter-loadbalancer</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-starter-log</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-core-auto</artifactId>
|
<artifactId>blade-core-auto</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.excel;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入宽容日期解析器(口径见根工作区 docs/import-spec.md)。
|
||||||
|
* <p>
|
||||||
|
* 解析规则:
|
||||||
|
* <ul>
|
||||||
|
* <li>分隔符 {@code -}、{@code /}、{@code .} 均接受;补零与否均可(2026-08-02 ≡ 2026-8-2)</li>
|
||||||
|
* <li>日期时间:日期部分同上,时间 HH:mm:ss,可省略秒或秒+分(2026-8-2 12:3 可解析)</li>
|
||||||
|
* <li>拒绝:两位年份(26-8-2)、日在前(2/8/2026)、无分隔符(20260802)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public final class LenientDateParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期部分:4 位年 + 分隔符 + 1~2 位月 + 同一分隔符 + 1~2 位日(年在前,拒绝两位年份与日在前)。
|
||||||
|
*/
|
||||||
|
private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4})([-/.])(\\d{1,2})\\2(\\d{1,2})");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间部分:1~2 位时[:1~2 位分[:1~2 位秒]],逐级可省略。
|
||||||
|
*/
|
||||||
|
private static final Pattern TIME_PATTERN = Pattern.compile("(\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2}))?)?");
|
||||||
|
|
||||||
|
private LenientDateParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期文本,失败返回 {@code null}。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @return 日期;无法识别时返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDate parseDateOrNull(String value) {
|
||||||
|
String normalized = normalize(value);
|
||||||
|
if (normalized == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Matcher matcher = DATE_PATTERN.matcher(normalized);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return LocalDate.of(Integer.parseInt(matcher.group(1)),
|
||||||
|
Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(4)));
|
||||||
|
} catch (NumberFormatException | java.time.DateTimeException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期文本;允许携带合法时间部分并截断(如 2026-9-1 8:0:0 按 2026-09-01 解析,
|
||||||
|
* 与 fastexcel 内置 LocalDate 转换的既有宽容度保持一致),失败返回 {@code null}。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @return 日期;无法识别时返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDate parseDateLenientlyOrNull(String value) {
|
||||||
|
LocalDate date = parseDateOrNull(value);
|
||||||
|
if (date != null) {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
LocalDateTime dateTime = parseDateTimeOrNull(value);
|
||||||
|
return dateTime == null ? null : dateTime.toLocalDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期时间文本;纯日期按当日零点处理,失败返回 {@code null}。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @return 日期时间;无法识别时返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDateTime parseDateTimeOrNull(String value) {
|
||||||
|
String normalized = normalize(value);
|
||||||
|
if (normalized == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 拆出日期与时间两部分;中间允许 1 个及以上空白或小写 t(2026-8-2t12:3 亦接受)。
|
||||||
|
String[] parts = normalized.split("[ \\t]+|(?<=\\d)t", 2);
|
||||||
|
if (parts.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalDate date = parseDateOrNull(parts[0]);
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (parts.length == 1) {
|
||||||
|
return date.atStartOfDay();
|
||||||
|
}
|
||||||
|
Matcher matcher = TIME_PATTERN.matcher(parts[1]);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int hour = Integer.parseInt(matcher.group(1));
|
||||||
|
int minute = matcher.group(2) == null ? 0 : Integer.parseInt(matcher.group(2));
|
||||||
|
int second = matcher.group(3) == null ? 0 : Integer.parseInt(matcher.group(3));
|
||||||
|
return LocalDateTime.of(date, LocalTime.of(hour, minute, second));
|
||||||
|
} catch (NumberFormatException | java.time.DateTimeException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期文本,失败抛出携带口径文案的 {@link org.springblade.core.log.exception.ServiceException}。
|
||||||
|
* 空白与 {@code null} 返回 {@code null}(可选字段由业务校验决定是否必填)。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @param columnName 导入模板列名(用于失败原因文案与失败明细标红定位)
|
||||||
|
* @return 日期
|
||||||
|
*/
|
||||||
|
public static LocalDate parseDate(String value, String columnName) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalDate date = parseDateLenientlyOrNull(value);
|
||||||
|
if (date == null) {
|
||||||
|
throw unrecognizedDate(value, columnName);
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期时间文本,失败抛出携带口径文案的 {@link org.springblade.core.log.exception.ServiceException}。
|
||||||
|
* 空白与 {@code null} 返回 {@code null}(可选字段由业务校验决定是否必填)。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @param columnName 导入模板列名(用于失败原因文案与失败明细标红定位)
|
||||||
|
* @return 日期时间
|
||||||
|
*/
|
||||||
|
public static LocalDateTime parseDateTime(String value, String columnName) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalDateTime dateTime = parseDateTimeOrNull(value);
|
||||||
|
if (dateTime == null) {
|
||||||
|
throw unrecognizedDate(value, columnName);
|
||||||
|
}
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按口径构造「日期格式无法识别」错误。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @param columnName 导入模板列名
|
||||||
|
* @return 业务异常
|
||||||
|
*/
|
||||||
|
public static org.springblade.core.log.exception.ServiceException unrecognizedDate(String value, String columnName) {
|
||||||
|
String displayName = columnName == null || columnName.isBlank() ? "日期" : columnName;
|
||||||
|
return new org.springblade.core.log.exception.ServiceException(
|
||||||
|
displayName + " 日期格式无法识别:" + (value == null ? "" : value.trim()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 归一化输入:去首尾空白,跳过空白值。
|
||||||
|
*/
|
||||||
|
private static String normalize(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String normalized = value.trim();
|
||||||
|
return normalized.isEmpty() ? null : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.excel;
|
||||||
|
|
||||||
|
import cn.idev.excel.converters.Converter;
|
||||||
|
import cn.idev.excel.enums.CellDataTypeEnum;
|
||||||
|
import cn.idev.excel.metadata.GlobalConfiguration;
|
||||||
|
import cn.idev.excel.metadata.data.ReadCellData;
|
||||||
|
import cn.idev.excel.metadata.data.WriteCellData;
|
||||||
|
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
||||||
|
import cn.idev.excel.util.DateUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入宽容日期转换器(String 承载):Excel 数值日期(序列号)转 ISO 文本,文本原样透传。
|
||||||
|
* <p>
|
||||||
|
* 文本的宽容解析由服务层调用 {@link LenientDateParser} 完成,不在此处抛错——
|
||||||
|
* 转换器阶段抛出的异常会被 fastexcel 包装成 ExcelDataConvertException 直接中断读取,
|
||||||
|
* 无法进入导入失败明细流程。口径见根工作区 docs/import-spec.md。
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public class LenientDateStringConverter implements Converter<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> supportJavaTypeKey() {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CellDataTypeEnum supportExcelTypeKey() {
|
||||||
|
return CellDataTypeEnum.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
if (cellData.getType() == CellDataTypeEnum.NUMBER) {
|
||||||
|
LocalDate date = DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
|
||||||
|
globalConfiguration.getUse1904windowing()).toLocalDate();
|
||||||
|
return date.toString();
|
||||||
|
}
|
||||||
|
return cellData.getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
return new WriteCellData<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.excel;
|
||||||
|
|
||||||
|
import cn.idev.excel.converters.Converter;
|
||||||
|
import cn.idev.excel.enums.CellDataTypeEnum;
|
||||||
|
import cn.idev.excel.metadata.GlobalConfiguration;
|
||||||
|
import cn.idev.excel.metadata.data.ReadCellData;
|
||||||
|
import cn.idev.excel.metadata.data.WriteCellData;
|
||||||
|
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
||||||
|
import cn.idev.excel.util.DateUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入宽容日期时间转换器(String 承载):Excel 数值日期(序列号)转
|
||||||
|
* {@code yyyy-MM-dd HH:mm:ss} 文本,文本原样透传。
|
||||||
|
* <p>
|
||||||
|
* 文本的宽容解析由服务层调用 {@link LenientDateParser} 完成,不在此处抛错——
|
||||||
|
* 转换器阶段抛出的异常会被 fastexcel 包装成 ExcelDataConvertException 直接中断读取,
|
||||||
|
* 无法进入导入失败明细流程。口径见根工作区 docs/import-spec.md。
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public class LenientDateTimeStringConverter implements Converter<String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数值日期序列号转文本的输出格式,与导入模板展示格式保持一致。
|
||||||
|
*/
|
||||||
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> supportJavaTypeKey() {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CellDataTypeEnum supportExcelTypeKey() {
|
||||||
|
return CellDataTypeEnum.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
if (cellData.getType() == CellDataTypeEnum.NUMBER) {
|
||||||
|
LocalDateTime dateTime = DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
|
||||||
|
globalConfiguration.getUse1904windowing());
|
||||||
|
return dateTime.format(DATE_TIME_FORMATTER);
|
||||||
|
}
|
||||||
|
return cellData.getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
return new WriteCellData<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+4
-5
@@ -35,7 +35,6 @@ import lombok.Data;
|
|||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 币种汇率 Excel
|
* 币种汇率 Excel
|
||||||
@@ -62,8 +61,8 @@ public class CurrencyExcel implements Serializable {
|
|||||||
@ExcelProperty("汇率")
|
@ExcelProperty("汇率")
|
||||||
private BigDecimal exchangeRate;
|
private BigDecimal exchangeRate;
|
||||||
|
|
||||||
@ExcelProperty("生效日期")
|
@ExcelProperty(value = "生效日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate effectiveDate;
|
private String effectiveDate;
|
||||||
|
|
||||||
@ExcelProperty("状态")
|
@ExcelProperty("状态")
|
||||||
private String statusName;
|
private String statusName;
|
||||||
@@ -71,8 +70,8 @@ public class CurrencyExcel implements Serializable {
|
|||||||
@ExcelProperty("来源")
|
@ExcelProperty("来源")
|
||||||
private String dataSource;
|
private String dataSource;
|
||||||
|
|
||||||
@ExcelProperty("失效日期")
|
@ExcelProperty(value = "失效日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate expiryDate;
|
private String expiryDate;
|
||||||
|
|
||||||
@ExcelProperty("备注")
|
@ExcelProperty("备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|||||||
+2
-3
@@ -34,7 +34,6 @@ import lombok.Data;
|
|||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserExcel
|
* UserExcel
|
||||||
@@ -102,7 +101,7 @@ public class UserExcel implements Serializable {
|
|||||||
private String postName;
|
private String postName;
|
||||||
|
|
||||||
@ColumnWidth(20)
|
@ColumnWidth(20)
|
||||||
@ExcelProperty("生日")
|
@ExcelProperty(value = "生日", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private Date birthday;
|
private String birthday;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="exportUser" resultType="org.springblade.system.excel.UserExcel">
|
<select id="exportUser" resultType="org.springblade.system.excel.UserExcel">
|
||||||
SELECT id, tenant_id, user_type, account, name, real_name, email, phone, birthday, role_id, dept_id, post_id FROM blade_user ${ew.customSqlSegment}
|
SELECT id, tenant_id, user_type, account, name, real_name, email, phone, DATE_FORMAT(birthday, '%Y-%m-%d') AS birthday, role_id, dept_id, post_id FROM blade_user ${ew.customSqlSegment}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectCustomerOptions" resultType="java.util.HashMap">
|
<select id="selectCustomerOptions" resultType="java.util.HashMap">
|
||||||
|
|||||||
+2
@@ -125,6 +125,8 @@ public class CurrencyServiceImpl extends BaseServiceImpl<CurrencyMapper, Currenc
|
|||||||
CurrencyExcel excel = data.get(index);
|
CurrencyExcel excel = data.get(index);
|
||||||
try {
|
try {
|
||||||
Currency currency = Objects.requireNonNull(BeanUtil.copyProperties(excel, Currency.class));
|
Currency currency = Objects.requireNonNull(BeanUtil.copyProperties(excel, Currency.class));
|
||||||
|
currency.setEffectiveDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getEffectiveDate(), "生效日期"));
|
||||||
|
currency.setExpiryDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getExpiryDate(), "失效日期"));
|
||||||
currency.setDataSource(SOURCE_BATCH);
|
currency.setDataSource(SOURCE_BATCH);
|
||||||
currency.setStatus(STATUS_ENABLED);
|
currency.setStatus(STATUS_ENABLED);
|
||||||
prepare(currency, SOURCE_BATCH);
|
prepare(currency, SOURCE_BATCH);
|
||||||
|
|||||||
+3
@@ -689,6 +689,9 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
|||||||
*/
|
*/
|
||||||
private User buildImportUser(UserExcel userExcel, String tenantId) {
|
private User buildImportUser(UserExcel userExcel, String tenantId) {
|
||||||
User user = Objects.requireNonNull(BeanUtil.copyProperties(userExcel, User.class));
|
User user = Objects.requireNonNull(BeanUtil.copyProperties(userExcel, User.class));
|
||||||
|
// 宽容解析生日文本(2026-8-2 等写法),User.birthday 为 java.util.Date 需转换
|
||||||
|
java.time.LocalDate birthday = org.springblade.common.excel.LenientDateParser.parseDate(userExcel.getBirthday(), "生日");
|
||||||
|
user.setBirthday(birthday == null ? null : java.sql.Date.valueOf(birthday));
|
||||||
user.setTenantId(tenantId);
|
user.setTenantId(tenantId);
|
||||||
user.setUserType(Func.toInt(DictCache.getKey(DictEnum.USER_TYPE, userExcel.getUserTypeName()), 1));
|
user.setUserType(Func.toInt(DictCache.getKey(DictEnum.USER_TYPE, userExcel.getUserTypeName()), 1));
|
||||||
user.setDeptId(Func.toStrWithEmpty(SysCache.getDeptIds(tenantId, userExcel.getDeptName()), StringPool.EMPTY));
|
user.setDeptId(Func.toStrWithEmpty(SysCache.getDeptIds(tenantId, userExcel.getDeptName()), StringPool.EMPTY));
|
||||||
|
|||||||
+2
-3
@@ -36,7 +36,6 @@ import lombok.Data;
|
|||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事故记录 Excel
|
* 事故记录 Excel
|
||||||
@@ -60,8 +59,8 @@ public class AccidentRecordExcel implements Serializable {
|
|||||||
@ExcelProperty("*车牌号/船号")
|
@ExcelProperty("*车牌号/船号")
|
||||||
private String vehicleNo;
|
private String vehicleNo;
|
||||||
|
|
||||||
@ExcelProperty("*事故发生日期")
|
@ExcelProperty(value = "*事故发生日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate accidentDate;
|
private String accidentDate;
|
||||||
|
|
||||||
@ExcelProperty("事故发生地点")
|
@ExcelProperty("事故发生地点")
|
||||||
private String accidentLocation;
|
private String accidentLocation;
|
||||||
|
|||||||
+4
-5
@@ -35,7 +35,6 @@ import lombok.Data;
|
|||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 年检记录 Excel
|
* 年检记录 Excel
|
||||||
@@ -59,11 +58,11 @@ public class AnnualInspectionRecordExcel implements Serializable {
|
|||||||
@ExcelProperty("*车牌号/船号")
|
@ExcelProperty("*车牌号/船号")
|
||||||
private String vehicleNo;
|
private String vehicleNo;
|
||||||
|
|
||||||
@ExcelProperty("*检测评定日期")
|
@ExcelProperty(value = "*检测评定日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate inspectionAssessmentDate;
|
private String inspectionAssessmentDate;
|
||||||
|
|
||||||
@ExcelProperty("*有效期截止日")
|
@ExcelProperty(value = "*有效期截止日", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate validUntilDate;
|
private String validUntilDate;
|
||||||
|
|
||||||
@ExcelProperty("*车辆技术等级")
|
@ExcelProperty("*车辆技术等级")
|
||||||
private String vehicleTechnicalLevel;
|
private String vehicleTechnicalLevel;
|
||||||
|
|||||||
+1
-3
@@ -7,12 +7,10 @@ package org.springblade.transport.excel;
|
|||||||
|
|
||||||
import cn.idev.excel.annotation.ExcelIgnore;
|
import cn.idev.excel.annotation.ExcelIgnore;
|
||||||
import cn.idev.excel.annotation.ExcelProperty;
|
import cn.idev.excel.annotation.ExcelProperty;
|
||||||
import cn.idev.excel.annotation.format.DateTimeFormat;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备台账 Excel
|
* 设备台账 Excel
|
||||||
@@ -29,7 +27,7 @@ public class EquipmentLedgerExcel implements Serializable {
|
|||||||
@ExcelProperty("设备品牌") private String equipmentBrand;
|
@ExcelProperty("设备品牌") private String equipmentBrand;
|
||||||
@ExcelProperty("设备类型") private String equipmentType;
|
@ExcelProperty("设备类型") private String equipmentType;
|
||||||
@ExcelProperty("规格型号") private String specificationModel;
|
@ExcelProperty("规格型号") private String specificationModel;
|
||||||
@ExcelProperty("出厂日期") @DateTimeFormat("yyyy-MM-dd") private LocalDate factoryDate;
|
@ExcelProperty(value = "出厂日期", converter = org.springblade.common.excel.LenientDateStringConverter.class) private String factoryDate;
|
||||||
@ExcelProperty("备注") private String remark;
|
@ExcelProperty("备注") private String remark;
|
||||||
@ExcelIgnore private String errorMessage;
|
@ExcelIgnore private String errorMessage;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -35,7 +35,6 @@ import lombok.Data;
|
|||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保险记录 Excel
|
* 保险记录 Excel
|
||||||
@@ -68,11 +67,11 @@ public class InsuranceRecordExcel implements Serializable {
|
|||||||
@ExcelProperty("*保单号")
|
@ExcelProperty("*保单号")
|
||||||
private String policyNo;
|
private String policyNo;
|
||||||
|
|
||||||
@ExcelProperty("*开始日期")
|
@ExcelProperty(value = "*开始日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate startDate;
|
private String startDate;
|
||||||
|
|
||||||
@ExcelProperty("*结束日期")
|
@ExcelProperty(value = "*结束日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate endDate;
|
private String endDate;
|
||||||
|
|
||||||
@ExcelProperty("保额")
|
@ExcelProperty("保额")
|
||||||
private BigDecimal insuredAmount;
|
private BigDecimal insuredAmount;
|
||||||
@@ -83,8 +82,8 @@ public class InsuranceRecordExcel implements Serializable {
|
|||||||
@ExcelProperty("发票号")
|
@ExcelProperty("发票号")
|
||||||
private String invoiceNo;
|
private String invoiceNo;
|
||||||
|
|
||||||
@ExcelProperty("开票日期")
|
@ExcelProperty(value = "开票日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate invoiceDate;
|
private String invoiceDate;
|
||||||
|
|
||||||
@ExcelProperty("备注")
|
@ExcelProperty("备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|||||||
+11
-15
@@ -33,22 +33,20 @@ import cn.idev.excel.metadata.data.WriteCellData;
|
|||||||
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
||||||
import cn.idev.excel.util.DateUtils;
|
import cn.idev.excel.util.DateUtils;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.format.DateTimeParseException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保养记录日期转换器,兼容日期文本、日期时间文本和 Excel 数值日期。
|
* 保养记录日期转换器,兼容日期文本、日期时间文本和 Excel 数值日期。
|
||||||
|
* <p>
|
||||||
|
* 文本解析委托公共宽容解析器(支持 2026-8-2、2026/8/2 等写法,口径见根工作区
|
||||||
|
* docs/import-spec.md);无法识别的文本按既有行为抛出转换异常,
|
||||||
|
* 文案带原值,与「日期格式无法识别」口径一致。
|
||||||
*
|
*
|
||||||
* @author Chill
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
public class MaintenancePlanDateTimeConverter implements Converter<LocalDateTime> {
|
public class MaintenancePlanDateTimeConverter implements Converter<LocalDateTime> {
|
||||||
|
|
||||||
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
|
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
|
||||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
||||||
private static final DateTimeFormatter DATE_TIME_MINUTE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
|
||||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> supportJavaTypeKey() {
|
public Class<?> supportJavaTypeKey() {
|
||||||
@@ -71,16 +69,14 @@ public class MaintenancePlanDateTimeConverter implements Converter<LocalDateTime
|
|||||||
if (value == null || value.trim().isEmpty()) {
|
if (value == null || value.trim().isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String normalizedValue = value.trim();
|
LocalDateTime parsed = org.springblade.common.excel.LenientDateParser.parseDateTimeOrNull(value);
|
||||||
try {
|
if (parsed != null) {
|
||||||
return LocalDateTime.parse(normalizedValue, DATE_TIME_FORMATTER);
|
return parsed;
|
||||||
} catch (DateTimeParseException ignored) {
|
|
||||||
try {
|
|
||||||
return LocalDateTime.parse(normalizedValue, DATE_TIME_MINUTE_FORMATTER);
|
|
||||||
} catch (DateTimeParseException ignoredMinute) {
|
|
||||||
return LocalDate.parse(normalizedValue, DATE_FORMATTER).atStartOfDay();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 无法识别的文本:与既有行为一致抛出转换异常(ExcelDataConvertException 是 RuntimeException,
|
||||||
|
// 会带上本文案一路抛到 Controller,由全局异常处理返回给前端提示)。
|
||||||
|
throw new cn.idev.excel.exception.ExcelDataConvertException(-1, -1, cellData, contentProperty,
|
||||||
|
value.trim() + " 日期格式无法识别");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+2
-3
@@ -16,7 +16,6 @@ import lombok.Data;
|
|||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 其他费用记录 Excel
|
* 其他费用记录 Excel
|
||||||
@@ -40,8 +39,8 @@ public class OtherExpenseRecordExcel implements Serializable {
|
|||||||
@ExcelProperty("*车牌号/船号")
|
@ExcelProperty("*车牌号/船号")
|
||||||
private String vehicleNo;
|
private String vehicleNo;
|
||||||
|
|
||||||
@ExcelProperty("*费用日期")
|
@ExcelProperty(value = "*费用日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate expenseDate;
|
private String expenseDate;
|
||||||
|
|
||||||
@ExcelProperty("*费用类型")
|
@ExcelProperty("*费用类型")
|
||||||
private String expenseType;
|
private String expenseType;
|
||||||
|
|||||||
+4
@@ -38,6 +38,10 @@ import java.time.format.DateTimeFormatter;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 换胎日期文本转换器,保留非法日期原值并兼容 Excel 数值日期。
|
* 换胎日期文本转换器,保留非法日期原值并兼容 Excel 数值日期。
|
||||||
|
* <p>
|
||||||
|
* 数值日期序列号转 ISO 文本,文本原样透传;文本的宽容解析由服务层委托
|
||||||
|
* {@link org.springblade.common.excel.LenientDateParser} 完成(支持 2026-8-2 等写法,
|
||||||
|
* 口径见根工作区 docs/import-spec.md)。
|
||||||
*
|
*
|
||||||
* @author Chill
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
|
|||||||
+8
-8
@@ -108,10 +108,10 @@ public class WaybillExcel implements Serializable {
|
|||||||
private String escortPhone;
|
private String escortPhone;
|
||||||
@ExcelProperty("里程(km)")
|
@ExcelProperty("里程(km)")
|
||||||
private BigDecimal mileage;
|
private BigDecimal mileage;
|
||||||
@ExcelProperty("预计发货日期")
|
@ExcelProperty(value = "预计发货日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate estimatedStartTime;
|
private String estimatedStartTime;
|
||||||
@ExcelProperty("预计完成日期")
|
@ExcelProperty(value = "预计完成日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate estimatedEndTime;
|
private String estimatedEndTime;
|
||||||
@ExcelProperty("单价")
|
@ExcelProperty("单价")
|
||||||
private BigDecimal unitPrice;
|
private BigDecimal unitPrice;
|
||||||
@ExcelProperty("计价单位")
|
@ExcelProperty("计价单位")
|
||||||
@@ -126,10 +126,10 @@ public class WaybillExcel implements Serializable {
|
|||||||
private String businessStatus;
|
private String businessStatus;
|
||||||
@ExcelProperty("数据来源")
|
@ExcelProperty("数据来源")
|
||||||
private String dataSource;
|
private String dataSource;
|
||||||
@ExcelProperty("开始日期")
|
@ExcelProperty(value = "开始日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate startDate;
|
private String startDate;
|
||||||
@ExcelProperty("结束日期")
|
@ExcelProperty(value = "结束日期", converter = org.springblade.common.excel.LenientDateStringConverter.class)
|
||||||
private LocalDate endDate;
|
private String endDate;
|
||||||
@ExcelProperty("计划名称")
|
@ExcelProperty("计划名称")
|
||||||
private String planName;
|
private String planName;
|
||||||
@ExcelProperty("多联总单")
|
@ExcelProperty("多联总单")
|
||||||
|
|||||||
+1
@@ -94,6 +94,7 @@ public class AccidentRecordServiceImpl extends BaseServiceImpl<AccidentRecordMap
|
|||||||
AccidentRecordExcel excel = data.get(index);
|
AccidentRecordExcel excel = data.get(index);
|
||||||
try {
|
try {
|
||||||
AccidentRecord accidentRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, AccidentRecord.class));
|
AccidentRecord accidentRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, AccidentRecord.class));
|
||||||
|
accidentRecord.setAccidentDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getAccidentDate(), "事故发生日期"));
|
||||||
prepare(accidentRecord);
|
prepare(accidentRecord);
|
||||||
List<String> validationErrors = validateImportAccidentRecord(accidentRecord);
|
List<String> validationErrors = validateImportAccidentRecord(accidentRecord);
|
||||||
if (Func.isNotEmpty(validationErrors)) {
|
if (Func.isNotEmpty(validationErrors)) {
|
||||||
|
|||||||
+2
@@ -113,6 +113,8 @@ public class AnnualInspectionRecordServiceImpl extends BaseServiceImpl<AnnualIns
|
|||||||
AnnualInspectionRecordExcel excel = data.get(index);
|
AnnualInspectionRecordExcel excel = data.get(index);
|
||||||
try {
|
try {
|
||||||
AnnualInspectionRecord annualInspectionRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, AnnualInspectionRecord.class));
|
AnnualInspectionRecord annualInspectionRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, AnnualInspectionRecord.class));
|
||||||
|
annualInspectionRecord.setInspectionAssessmentDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getInspectionAssessmentDate(), "检测评定日期"));
|
||||||
|
annualInspectionRecord.setValidUntilDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getValidUntilDate(), "有效期截止日"));
|
||||||
prepare(annualInspectionRecord);
|
prepare(annualInspectionRecord);
|
||||||
List<String> validationErrors = validateImportAnnualInspectionRecord(annualInspectionRecord);
|
List<String> validationErrors = validateImportAnnualInspectionRecord(annualInspectionRecord);
|
||||||
if (Func.isNotEmpty(validationErrors)) {
|
if (Func.isNotEmpty(validationErrors)) {
|
||||||
|
|||||||
+1
@@ -91,6 +91,7 @@ public class EquipmentLedgerServiceImpl extends BaseServiceImpl<EquipmentLedgerM
|
|||||||
EquipmentLedgerExcel excel = data.get(index);
|
EquipmentLedgerExcel excel = data.get(index);
|
||||||
try {
|
try {
|
||||||
EquipmentLedger equipmentLedger = Objects.requireNonNull(BeanUtil.copyProperties(excel, EquipmentLedger.class));
|
EquipmentLedger equipmentLedger = Objects.requireNonNull(BeanUtil.copyProperties(excel, EquipmentLedger.class));
|
||||||
|
equipmentLedger.setFactoryDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getFactoryDate(), "出厂日期"));
|
||||||
prepare(equipmentLedger);
|
prepare(equipmentLedger);
|
||||||
if (equipmentLedger.getId() == null && Func.isEmpty(equipmentLedger.getEquipmentCode())) {
|
if (equipmentLedger.getId() == null && Func.isEmpty(equipmentLedger.getEquipmentCode())) {
|
||||||
equipmentLedger.setEquipmentCode(nextEquipmentCode(importEquipmentCodes));
|
equipmentLedger.setEquipmentCode(nextEquipmentCode(importEquipmentCodes));
|
||||||
|
|||||||
+3
@@ -119,6 +119,9 @@ public class InsuranceRecordServiceImpl extends BaseServiceImpl<InsuranceRecordM
|
|||||||
InsuranceRecordExcel excel = data.get(index);
|
InsuranceRecordExcel excel = data.get(index);
|
||||||
try {
|
try {
|
||||||
InsuranceRecord insuranceRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, InsuranceRecord.class));
|
InsuranceRecord insuranceRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, InsuranceRecord.class));
|
||||||
|
insuranceRecord.setStartDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getStartDate(), "开始日期"));
|
||||||
|
insuranceRecord.setEndDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getEndDate(), "结束日期"));
|
||||||
|
insuranceRecord.setInvoiceDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getInvoiceDate(), "开票日期"));
|
||||||
prepare(insuranceRecord);
|
prepare(insuranceRecord);
|
||||||
List<String> validationErrors = validateImportInsuranceRecord(insuranceRecord);
|
List<String> validationErrors = validateImportInsuranceRecord(insuranceRecord);
|
||||||
if (Func.isNotEmpty(insuranceRecord.getVehicleType()) && Func.isNotEmpty(insuranceRecord.getInsuranceType()) && Func.isNotEmpty(insuranceRecord.getPolicyNo())) {
|
if (Func.isNotEmpty(insuranceRecord.getVehicleType()) && Func.isNotEmpty(insuranceRecord.getInsuranceType()) && Func.isNotEmpty(insuranceRecord.getPolicyNo())) {
|
||||||
|
|||||||
+1
@@ -75,6 +75,7 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl<OtherExpenseR
|
|||||||
OtherExpenseRecordExcel excel = data.get(index);
|
OtherExpenseRecordExcel excel = data.get(index);
|
||||||
try {
|
try {
|
||||||
OtherExpenseRecord otherExpenseRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, OtherExpenseRecord.class));
|
OtherExpenseRecord otherExpenseRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, OtherExpenseRecord.class));
|
||||||
|
otherExpenseRecord.setExpenseDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getExpenseDate(), "费用日期"));
|
||||||
otherExpenseRecord.setDataSource("批量导入");
|
otherExpenseRecord.setDataSource("批量导入");
|
||||||
prepare(otherExpenseRecord);
|
prepare(otherExpenseRecord);
|
||||||
List<String> validationErrors = validateImportOtherExpenseRecord(otherExpenseRecord);
|
List<String> validationErrors = validateImportOtherExpenseRecord(otherExpenseRecord);
|
||||||
|
|||||||
+3
-6
@@ -43,8 +43,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.format.DateTimeParseException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -64,7 +62,6 @@ public class TireReplacementRecordServiceImpl extends BaseServiceImpl<TireReplac
|
|||||||
private static final int REMARK_MAX_LENGTH = 500;
|
private static final int REMARK_MAX_LENGTH = 500;
|
||||||
private static final int ATTACHMENTS_MAX_LENGTH = 1000;
|
private static final int ATTACHMENTS_MAX_LENGTH = 1000;
|
||||||
private static final int MONEY_SCALE = 2;
|
private static final int MONEY_SCALE = 2;
|
||||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<TireReplacementRecordVO> selectTireReplacementRecordPage(IPage<TireReplacementRecordVO> page, TireReplacementRecordVO tireReplacementRecord) {
|
public IPage<TireReplacementRecordVO> selectTireReplacementRecordPage(IPage<TireReplacementRecordVO> page, TireReplacementRecordVO tireReplacementRecord) {
|
||||||
@@ -144,9 +141,9 @@ public class TireReplacementRecordServiceImpl extends BaseServiceImpl<TireReplac
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return LocalDate.parse(normalizedValue, DATE_FORMATTER);
|
return org.springblade.common.excel.LenientDateParser.parseDate(normalizedValue, "换胎时间");
|
||||||
} catch (DateTimeParseException exception) {
|
} catch (Exception exception) {
|
||||||
validationErrors.add("换胎时间格式不正确,请使用yyyy-MM-dd格式并填写有效日期");
|
validationErrors.add(exception.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -510,11 +510,12 @@ public class TransportPlanServiceImpl extends BaseServiceImpl<TransportPlanMappe
|
|||||||
if (Func.isEmpty(value)) {
|
if (Func.isEmpty(value)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
// 宽容解析:接受 2026-8-2、2026/8/2 等写法(口径见根工作区 docs/import-spec.md)。
|
||||||
return LocalDate.parse(value.trim(), DateTimeFormatter.ISO_LOCAL_DATE);
|
LocalDate date = org.springblade.common.excel.LenientDateParser.parseDateLenientlyOrNull(value);
|
||||||
} catch (Exception exception) {
|
if (date == null) {
|
||||||
throw new ServiceException(fieldName + "格式必须为 YYYY-MM-DD");
|
throw new ServiceException(fieldName + " 日期格式无法识别:" + value.trim());
|
||||||
}
|
}
|
||||||
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateImportLength(String value, int maxLength, String fieldName) {
|
private void validateImportLength(String value, int maxLength, String fieldName) {
|
||||||
|
|||||||
+5
-8
@@ -60,7 +60,6 @@ import java.math.BigDecimal;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -1346,14 +1345,12 @@ public class TransportReconciliationServiceImpl
|
|||||||
|
|
||||||
private LocalDateTime parseTimeNullable(String value, String field) {
|
private LocalDateTime parseTimeNullable(String value, String field) {
|
||||||
if (Func.isEmpty(value)) return null;
|
if (Func.isEmpty(value)) return null;
|
||||||
for (String pattern : List.of("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-M-d HH:mm:ss", "yyyy-M-d HH:mm",
|
// 宽容解析:统一委托公共解析器,接受 2026-8-2 12:3 等写法(口径见根工作区 docs/import-spec.md)。
|
||||||
"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/M/d HH:mm:ss", "yyyy/M/d HH:mm")) {
|
LocalDateTime result = org.springblade.common.excel.LenientDateParser.parseDateTimeOrNull(value);
|
||||||
try { return LocalDateTime.parse(value.trim(), DateTimeFormatter.ofPattern(pattern)); } catch (DateTimeParseException ignored) { }
|
if (result == null) {
|
||||||
|
throw new ServiceException(field + " 日期格式无法识别:" + value.trim());
|
||||||
}
|
}
|
||||||
for (String pattern : List.of("yyyy-MM-dd", "yyyy-M-d", "yyyy/MM/dd", "yyyy/M/d")) {
|
return result;
|
||||||
try { return LocalDate.parse(value.trim(), DateTimeFormatter.ofPattern(pattern)).atStartOfDay(); } catch (DateTimeParseException ignored) { }
|
|
||||||
}
|
|
||||||
throw new ServiceException(field + "格式应为yyyy-MM-dd HH:mm:ss或yyyy-MM-dd");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String matchKey(TransportReconciliationInternal row) {
|
private String matchKey(TransportReconciliationInternal row) {
|
||||||
|
|||||||
+10
-10
@@ -52,7 +52,6 @@ import java.io.IOException;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -70,7 +69,6 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImportBatchMapper, WaybillImportBatch> implements IWaybillImportBatchService {
|
public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImportBatchMapper, WaybillImportBatch> implements IWaybillImportBatchService {
|
||||||
|
|
||||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
||||||
/** 批量导入状态仅保留草稿与导入完成两种。 */
|
/** 批量导入状态仅保留草稿与导入完成两种。 */
|
||||||
private static final String STATUS_DRAFT = "draft";
|
private static final String STATUS_DRAFT = "draft";
|
||||||
private static final String STATUS_COMPLETED = "completed";
|
private static final String STATUS_COMPLETED = "completed";
|
||||||
@@ -467,11 +465,12 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImport
|
|||||||
throw new ServiceException(fieldName + "不能为空");
|
throw new ServiceException(fieldName + "不能为空");
|
||||||
}
|
}
|
||||||
String text = String.valueOf(value).trim();
|
String text = String.valueOf(value).trim();
|
||||||
try {
|
// 宽容解析:接受 2026-8-2、2026-9-1 8:0:0 等写法(口径见根工作区 docs/import-spec.md)。
|
||||||
return text.length() == 10 ? LocalDate.parse(text) : LocalDate.parse(text, DATE_TIME_FORMATTER);
|
LocalDate date = org.springblade.common.excel.LenientDateParser.parseDateLenientlyOrNull(text);
|
||||||
} catch (DateTimeParseException exception) {
|
if (date == null) {
|
||||||
throw new ServiceException(fieldName + "格式必须为 YYYY-MM-DD HH:mm:ss");
|
throw new ServiceException(fieldName + " 日期格式无法识别:" + text);
|
||||||
}
|
}
|
||||||
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal defaultQuantity(BigDecimal quantity) {
|
private BigDecimal defaultQuantity(BigDecimal quantity) {
|
||||||
@@ -951,12 +950,13 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImport
|
|||||||
}
|
}
|
||||||
|
|
||||||
String text = String.valueOf(value).trim();
|
String text = String.valueOf(value).trim();
|
||||||
try {
|
// 宽容解析:接受 2026-8-2、2026-9-1 8:0:0 等写法(口径见根工作区 docs/import-spec.md)。
|
||||||
return text.length() == 10 ? LocalDate.parse(text) : LocalDate.parse(text, DATE_TIME_FORMATTER);
|
LocalDate date = org.springblade.common.excel.LenientDateParser.parseDateLenientlyOrNull(text);
|
||||||
} catch (DateTimeParseException exception) {
|
if (date == null) {
|
||||||
errors.add(fieldName + "格式必须为日期格式(YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss)");
|
errors.add(fieldName + " 日期格式无法识别:" + text);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> loadTransportTypeOptions() {
|
private Map<String, String> loadTransportTypeOptions() {
|
||||||
|
|||||||
+4
@@ -885,6 +885,10 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
WaybillExcel excel = data.get(index);
|
WaybillExcel excel = data.get(index);
|
||||||
try {
|
try {
|
||||||
Waybill waybill = Objects.requireNonNull(BeanUtil.copyProperties(excel, Waybill.class));
|
Waybill waybill = Objects.requireNonNull(BeanUtil.copyProperties(excel, Waybill.class));
|
||||||
|
waybill.setEstimatedStartTime(org.springblade.common.excel.LenientDateParser.parseDate(excel.getEstimatedStartTime(), "预计发货日期"));
|
||||||
|
waybill.setEstimatedEndTime(org.springblade.common.excel.LenientDateParser.parseDate(excel.getEstimatedEndTime(), "预计完成日期"));
|
||||||
|
waybill.setStartDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getStartDate(), "开始日期"));
|
||||||
|
waybill.setEndDate(org.springblade.common.excel.LenientDateParser.parseDate(excel.getEndDate(), "结束日期"));
|
||||||
waybill.setDataSource("批量导入");
|
waybill.setDataSource("批量导入");
|
||||||
waybill.setCreateTime(null);
|
waybill.setCreateTime(null);
|
||||||
waybill.setUpdateTime(null);
|
waybill.setUpdateTime(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user