From cc33bc5ef26d1508c685756a4d8d4ea663295709 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sun, 20 Sep 2026 06:27:33 +0800 Subject: [PATCH 01/14] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E6=B8=AF?= =?UTF-8?q?=E5=8F=A3=E7=A0=81=E5=A4=B4=E6=89=B9=E9=87=8F=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=BA=8C=E7=BA=A7=E9=A1=BA=E5=BA=8F=E4=B8=8E?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E5=9B=9E=E6=BB=9A=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对应 BUG.xlsx 序号 1 与序号 2。 序号 1(同表混装一二级数据): - 改为两阶段导入,先落库港口(一级)再落库码头(二级), 消除对 Excel 行序的依赖;码头优先引用本批次新增的港口 序号 2(全失败即整批回滚): - 逐行收集错误,只要有一行失败就抛异常触发整批回滚,不再部分成功 - 失败明细按【原表全部行】返回,未出错的行不标注,用户只需改错行 附带修复: - 父港口找不到时改抛专用 ParentPortNotFoundException: 上级港口声明在本文件里、但那一行自身校验失败时,不再连带给码头行报错, 避免用户看到"两行都错"的假象;父港口确实漏填时仍照常报错 - 批内重复编码在落库前预检并定位到行,两行都标注原因 - 父港口区域信息为空(历史数据)时保留码头自身填写的值, 此前会被覆盖成空值导致码头无法导入 --- .../controller/PortTerminalController.java | 17 +- .../system/excel/ImportFailureException.java | 60 +++++ .../service/impl/PortTerminalServiceImpl.java | 247 +++++++++++++++++- 3 files changed, 310 insertions(+), 14 deletions(-) create mode 100644 blade-service/blade-system/src/main/java/org/springblade/system/excel/ImportFailureException.java diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/PortTerminalController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/PortTerminalController.java index a34396b..8d7a0dd 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/PortTerminalController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/PortTerminalController.java @@ -43,6 +43,8 @@ import org.springblade.core.tenant.annotation.NonDS; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.Func; +import org.springblade.common.excel.ImportFailureExcelUtil; +import org.springblade.system.excel.ImportFailureException; import org.springblade.system.excel.PortTerminalExcel; import org.springblade.system.excel.PortTerminalImporter; import org.springblade.system.pojo.entity.PortTerminal; @@ -167,14 +169,23 @@ public class PortTerminalController extends BladeController { if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) { return R.fail("请上传 .xls,.xlsx 标准格式文件"); } - List failureList = portTerminalService.importPortTerminal(ExcelUtil.read(file, PortTerminalExcel.class)); - if (Func.isNotEmpty(failureList)) { - org.springblade.common.excel.ImportFailureExcelUtil.export(response, "港口码头主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, PortTerminalExcel.class); + try { + portTerminalService.importPortTerminal(ExcelUtil.read(file, PortTerminalExcel.class)); + } catch (ImportFailureException exception) { + // 全失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 + exportFailure(response, exception.getFailureList()); return null; } return R.success("操作成功"); } + /** + * 导出导入失败明细,内容为原表全部数据并在末尾追加失败原因列。 + */ + private void exportFailure(HttpServletResponse response, List failureList) { + ImportFailureExcelUtil.export(response, "港口码头主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, PortTerminalExcel.class); + } + /** * 导出港口码头主数据 */ diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/ImportFailureException.java b/blade-service/blade-system/src/main/java/org/springblade/system/excel/ImportFailureException.java new file mode 100644 index 0000000..0277ec0 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/excel/ImportFailureException.java @@ -0,0 +1,60 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.system.excel; + +import org.springblade.core.log.exception.ServiceException; + +import java.io.Serial; +import java.util.List; + +/** + * 导入失败异常,携带失败明细用于导出原表并标注错误。 + *

+ * 批量导入采用「全失败即整批回滚」语义:任一行校验失败都会抛出本异常触发事务回滚, + * 失败明细在抛异常前已收集完毕,因此回滚不影响明细的内容。 + * + * @author Chill + */ +public class ImportFailureException extends ServiceException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 失败明细,包含原表全部数据,错误行已标注错误原因。 + */ + private final transient List failureList; + + public ImportFailureException(List failureList) { + super("导入失败,已回滚全部数据"); + this.failureList = failureList; + } + + public List getFailureList() { + return failureList; + } + +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java index 07aaf8a..0551c2d 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java @@ -34,6 +34,7 @@ import org.springblade.core.log.exception.ServiceException; import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.core.tool.utils.BeanUtil; import org.springblade.core.tool.utils.Func; +import org.springblade.system.excel.ImportFailureException; import org.springblade.system.excel.PortTerminalExcel; import org.springblade.system.mapper.PortTerminalMapper; import org.springblade.system.pojo.entity.PortTerminal; @@ -44,12 +45,18 @@ import org.springblade.system.service.IRegionService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.Serial; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.regex.Pattern; /** @@ -133,8 +140,88 @@ public class PortTerminalServiceImpl extends BaseServiceImpl errorList = new ArrayList<>(); + // 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。 + List failureList = new ArrayList<>(); + // 批内已占用的编码,用于识别文件内重复数据。 + Map occupiedCodeMap = new HashMap<>(); + // 本文件声明的一级数据(港口)编码集合。 + // 用于区分两种情况:父港口"漏填"与"父行自身校验失败"。 + // 后者说明父行已被标红、用户只需改那一行,因此不再连带给码头行报错。 + Set declaredPortCodeSet = new HashSet<>(); + // 两阶段导入:先落库一级数据(港口),再落库二级数据(码头),消除对 Excel 行序的依赖。 + Map batchPortMap = new LinkedHashMap<>(); + List portIndexList = new ArrayList<>(); + List terminalIndexList = new ArrayList<>(); + splitByCategory(data, portIndexList, terminalIndexList, occupiedCodeMap, declaredPortCodeSet, failureList); + importPorts(data, portIndexList, batchPortMap, failureList); + importTerminals(data, terminalIndexList, batchPortMap, declaredPortCodeSet, failureList); + if (Func.isNotEmpty(failureList)) { + // 抛出携带失败明细的异常,触发事务回滚。 + // 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。 + throw new ImportFailureException(data); + } + return failureList; + } + + /** + * 按类型拆分数据行,并完成与阶段无关的基础校验(类型、编码格式、批内重复)。 + * + * @param data 导入数据 + * @param portIndexList 港口行下标 + * @param terminalIndexList 码头行下标 + * @param occupiedCodeMap 编码占用情况,值为首次出现的下标 + * @param declaredPortCodeSet 本文件声明的港口编码(含编码格式不合法的行) + * @param failureList 失败明细 + */ + private void splitByCategory(List data, List portIndexList, List terminalIndexList, + Map occupiedCodeMap, Set declaredPortCodeSet, + List failureList) { for (int index = 0; index < data.size(); index++) { + PortTerminalExcel excel = data.get(index); + String category = trimToEmpty(excel.getCategory()); + boolean categoryValid = CATEGORY_PORT.equals(category) || CATEGORY_TERMINAL.equals(category); + if (!categoryValid) { + failureList.add(buildFailure(data, index, "类型只能为港口或码头")); + continue; + } + // 只要类型是港口就登记为"本文件已声明",即使它的编码格式不合法: + // 这样引用它的码头行不会被连带报错,用户只需修正这一个港口行。 + if (CATEGORY_PORT.equals(category)) { + String declaredCode = trimToEmpty(excel.getCode()).toUpperCase(Locale.ROOT); + if (Func.isNotEmpty(declaredCode)) { + declaredPortCodeSet.add(declaredCode); + } + } + String code = trimToEmpty(excel.getCode()).toUpperCase(Locale.ROOT); + boolean codeValid = CATEGORY_PORT.equals(category) + ? PORT_CODE_PATTERN.matcher(code).matches() + : TERMINAL_CODE_PATTERN.matcher(code).matches(); + if (!codeValid) { + String message = CATEGORY_PORT.equals(category) ? "港口编码为5位大写字母" : "码头编码格式为港口编码-码头标识"; + failureList.add(buildFailure(data, index, message)); + continue; + } + // 批内重复:两行都需要标记,由用户决定保留哪一行。 + if (occupiedCodeMap.containsKey(code)) { + markFailure(data, occupiedCodeMap.get(code), "编码 " + code + " 在文件中重复出现"); + failureList.add(buildFailure(data, index, "编码 " + code + " 在文件中重复出现")); + continue; + } + occupiedCodeMap.put(code, index); + if (CATEGORY_PORT.equals(category)) { + portIndexList.add(index); + } else { + terminalIndexList.add(index); + } + } + } + + /** + * 阶段一:导入港口(一级数据),同时登记到批次内存映射,供码头引用。 + */ + private void importPorts(List data, List portIndexList, Map batchPortMap, + List failureList) { + for (Integer index : portIndexList) { PortTerminalExcel excel = data.get(index); try { PortTerminal portTerminal = Objects.requireNonNull(BeanUtil.copyProperties(excel, PortTerminal.class)); @@ -143,13 +230,105 @@ public class PortTerminalServiceImpl extends BaseServiceImpl data, List terminalIndexList, Map batchPortMap, + Set declaredPortCodeSet, List failureList) { + for (Integer index : terminalIndexList) { + PortTerminalExcel excel = data.get(index); + try { + PortTerminal portTerminal = Objects.requireNonNull(BeanUtil.copyProperties(excel, PortTerminal.class)); + portTerminal.setDataSource(SOURCE_BATCH); + portTerminal.setStatus(STATUS_ENABLED); + prepareTerminal(portTerminal, batchPortMap); + validate(portTerminal); + save(portTerminal); + } catch (ParentPortNotFoundException exception) { + // 上级港口就声明在本文件里,只是那一行自己校验失败(已被标红)。 + // 此时码头行本身没有问题,不再连带报错,避免用户看到"两行都错"的假象。 + if (!declaredPortCodeSet.contains(trimToEmpty(excel.getParentCode()).toUpperCase(Locale.ROOT))) { + failureList.add(buildFailure(data, index, resolveMessage(exception))); + } + } catch (Exception exception) { + failureList.add(buildFailure(data, index, resolveMessage(exception))); + } + } + } + + /** + * 码头导入:上级港口优先匹配本批次新增的港口,其次由 prepare 回查数据库。 + */ + private void prepareTerminal(PortTerminal portTerminal, Map batchPortMap) { + String parentCode = trimToEmpty(portTerminal.getParentCode()).toUpperCase(Locale.ROOT); + PortTerminal parent = Func.isEmpty(parentCode) ? null : batchPortMap.get(parentCode); + if (Func.isNotEmpty(parent)) { + applyParent(portTerminal, parent); + // 父信息已由本批次港口回填,无需再回查数据库。 + prepare(portTerminal, SOURCE_BATCH, true); + return; + } + portTerminal.setParentCode(parentCode); + prepare(portTerminal, SOURCE_BATCH); + } + + /** + * 将上级港口信息回填到码头,保证码头不会出现没有港口的数据。 + */ + private void applyParent(PortTerminal portTerminal, PortTerminal parent) { + portTerminal.setParentId(parent.getId()); + portTerminal.setParentCode(parent.getCode()); + portTerminal.setParentName(parent.getName()); + // 与回查数据库保持一致:父港口区域信息为空时保留码头自身填写的值。 + if (Func.isNotEmpty(parent.getCountry())) { + portTerminal.setCountry(parent.getCountry()); + } + if (Func.isNotEmpty(parent.getCity())) { + portTerminal.setCity(parent.getCity()); + } + if (Func.isNotEmpty(parent.getDistrictCode())) { + portTerminal.setDistrictCode(parent.getDistrictCode()); + portTerminal.setRegionCode(parent.getDistrictCode()); + } else if (Func.isNotEmpty(portTerminal.getDistrictCode())) { + portTerminal.setRegionCode(portTerminal.getDistrictCode()); + } + if (Func.isNotEmpty(parent.getDistrictName())) { + portTerminal.setDistrictName(parent.getDistrictName()); + } + } + + /** + * 构造失败明细,行号按 Excel 中的实际行号(表头占第 1 行)推算。 + */ + private PortTerminalExcel buildFailure(List data, int index, String message) { + PortTerminalExcel excel = data.get(index); + markFailure(data, index, message); + return excel; + } + + /** + * 仅标注错误原因,不重复加入失败明细集合。 + */ + private void markFailure(List data, int index, String message) { + if (index >= 0 && index < data.size()) { + data.get(index).setErrorMessage("第" + (index + 2) + "行:" + message); + } + } + + /** + * 解析异常信息,非业务异常统一提示导入失败。 + */ + private String resolveMessage(Exception exception) { + return exception instanceof ServiceException ? exception.getMessage() : "导入失败"; } @Override @@ -174,6 +353,17 @@ public class PortTerminalServiceImpl extends BaseServiceImpl + * 与普通业务异常区分开,是因为调用方需要判断:这个父港口到底是"用户漏填了", + * 还是"父港口那一行就在本文件里、只是它自己校验失败"。后者不该连带给码头行报错。 + * + * @author Chill + */ + private static class ParentPortNotFoundException extends ServiceException { + + @Serial + private static final long serialVersionUID = 1L; + + ParentPortNotFoundException(String message) { + super(message); + } + + } + } From 9668f6059a7e859118fac69af96f0a94c44b992e Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sun, 20 Sep 2026 16:32:31 +0800 Subject: [PATCH 02/14] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=96=87=E4=BB=B6=E5=88=9B=E5=BB=BA=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E4=B8=8E=E6=9B=B4=E6=96=B0=E6=97=B6=E9=97=B4=E5=88=97=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 导出模型的 createTime/updateTime 由 LocalDateTime 改为 java.util.Date,与 BaseEntity 审计字段类型保持一致,避免 BeanUtil.copyProperties 因类型不兼容而静默跳过 - 覆盖车/船务管理 12 个页面导出模型及基础数据等 11 个导出模型,并为新增 Date 字段补齐 @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - 运单导入显式清空审计时间,避免把导入文件中的创建/更新时间带入实体 --- .../transport/excel/AccidentRecordExportExcel.java | 6 +++--- .../excel/AnnualInspectionRecordExportExcel.java | 6 +++--- .../springblade/transport/excel/CommonAddressExcel.java | 9 ++++++--- .../transport/excel/CommonCargoExportExcel.java | 9 ++++++--- .../springblade/transport/excel/CommonRouteExcel.java | 9 ++++++--- .../springblade/transport/excel/ContractManageExcel.java | 9 ++++++--- .../transport/excel/EtcRecordExportExcel.java | 5 +++-- .../transport/excel/InsuranceRecordExportExcel.java | 6 +++--- .../springblade/transport/excel/LoadingManageExcel.java | 9 ++++++--- .../transport/excel/MaintenancePlanExportExcel.java | 6 +++--- .../transport/excel/MaintenanceRecordExcel.java | 5 +++-- .../transport/excel/MileageRecordExportExcel.java | 6 +++--- .../transport/excel/OilElectricRecordExportExcel.java | 5 +++-- .../transport/excel/OtherExpenseRecordExportExcel.java | 6 +++--- .../springblade/transport/excel/ProcessConfigExcel.java | 9 ++++++--- .../springblade/transport/excel/ProjectApplyExcel.java | 8 ++++++-- .../transport/excel/ShippingTemplateExcel.java | 9 ++++++--- .../transport/excel/TemporaryCreditLimitExcel.java | 9 ++++++--- .../excel/TireReplacementRecordExportExcel.java | 6 +++--- .../excel/TransportChangeRecordExportExcel.java | 6 +++--- .../springblade/transport/excel/TransportPlanExcel.java | 9 ++++++--- .../transport/excel/ViolationRecordExcel.java | 5 +++-- .../org/springblade/transport/excel/WaybillExcel.java | 9 ++++++--- .../transport/service/impl/WaybillServiceImpl.java | 2 ++ 24 files changed, 104 insertions(+), 64 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AccidentRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AccidentRecordExportExcel.java index bf0102a..40d22ab 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AccidentRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AccidentRecordExportExcel.java @@ -37,7 +37,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 事故记录导出 Excel @@ -83,14 +83,14 @@ public class AccidentRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AnnualInspectionRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AnnualInspectionRecordExportExcel.java index a87ac07..3b9a297 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AnnualInspectionRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/AnnualInspectionRecordExportExcel.java @@ -36,7 +36,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 年检记录导出 Excel @@ -83,14 +83,14 @@ public class AnnualInspectionRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonAddressExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonAddressExcel.java index 420cdca..bcc700d 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonAddressExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonAddressExcel.java @@ -24,6 +24,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelIgnore; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -32,7 +33,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; -import java.time.LocalDateTime; +import java.util.Date; /** * 常用地址 Excel @@ -88,9 +89,11 @@ public class CommonAddressExcel implements Serializable { private String remark; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonCargoExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonCargoExportExcel.java index b9bf5e6..de8be2b 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonCargoExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonCargoExportExcel.java @@ -24,6 +24,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.format.NumberFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; @@ -33,7 +34,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; -import java.time.LocalDateTime; +import java.util.Date; /** * 常用货物导出 Excel @@ -96,9 +97,11 @@ public class CommonCargoExportExcel implements Serializable { private String deptName; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonRouteExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonRouteExcel.java index 31b2b76..34fb45e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonRouteExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CommonRouteExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -30,7 +31,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.time.LocalDateTime; +import java.util.Date; /** * 常用线路 Excel @@ -75,8 +76,10 @@ public class CommonRouteExcel implements Serializable { @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ContractManageExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ContractManageExcel.java index 853fa58..39aba2a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ContractManageExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ContractManageExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -31,7 +32,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 合同管理 Excel @@ -88,8 +89,10 @@ public class ContractManageExcel implements Serializable { @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/EtcRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/EtcRecordExportExcel.java index 0283274..1400ed0 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/EtcRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/EtcRecordExportExcel.java @@ -11,6 +11,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.Date; /** * ETC记录导出 Excel @@ -50,14 +51,14 @@ public class EtcRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/InsuranceRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/InsuranceRecordExportExcel.java index 77702c1..ad1f0e7 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/InsuranceRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/InsuranceRecordExportExcel.java @@ -36,7 +36,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 保险记录导出 Excel @@ -83,14 +83,14 @@ public class InsuranceRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/LoadingManageExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/LoadingManageExcel.java index 5687407..7995345 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/LoadingManageExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/LoadingManageExcel.java @@ -6,6 +6,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelIgnore; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -13,7 +14,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.time.LocalDateTime; +import java.util.Date; /** * 配载管理 Excel @@ -54,9 +55,11 @@ public class LoadingManageExcel implements Serializable { @ExcelProperty("数据来源") private String dataSource; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; @ExcelProperty("过程节点") private String currentProcessNode; @ExcelProperty("状态") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenancePlanExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenancePlanExportExcel.java index 4d6666f..a3c8a84 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenancePlanExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenancePlanExportExcel.java @@ -11,7 +11,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import java.io.Serial; -import java.time.LocalDateTime; +import java.util.Date; /** * 车辆保养记录导出 Excel @@ -26,12 +26,12 @@ public class MaintenancePlanExportExcel extends MaintenancePlanExcel { @ExcelProperty(value = "创建时间", index = 12) @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty(value = "更新人", index = 13) private String updateUserName; @ExcelProperty(value = "更新时间", index = 14) @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java index 6361d71..854a537 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java @@ -38,6 +38,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.Date; /** * 车辆维修记录 Excel @@ -97,14 +98,14 @@ public class MaintenanceRecordExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("里程单位") private String mileageUnit; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MileageRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MileageRecordExportExcel.java index be5b248..55be035 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MileageRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MileageRecordExportExcel.java @@ -36,7 +36,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; -import java.time.LocalDateTime; +import java.util.Date; /** * 里程记录导出 Excel @@ -75,14 +75,14 @@ public class MileageRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OilElectricRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OilElectricRecordExportExcel.java index e844b9c..3db7279 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OilElectricRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OilElectricRecordExportExcel.java @@ -11,6 +11,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.Date; /** * 油电记录导出 Excel @@ -61,14 +62,14 @@ public class OilElectricRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OtherExpenseRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OtherExpenseRecordExportExcel.java index cbf4b79..1dfe171 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OtherExpenseRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/OtherExpenseRecordExportExcel.java @@ -11,7 +11,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 其他费用记录导出 Excel @@ -43,14 +43,14 @@ public class OtherExpenseRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProcessConfigExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProcessConfigExcel.java index cd565dc..efc663a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProcessConfigExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProcessConfigExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -30,7 +31,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.time.LocalDateTime; +import java.util.Date; /** * 过程配置 Excel @@ -69,8 +70,10 @@ public class ProcessConfigExcel implements Serializable { @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java index 25238f7..2229dcb 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -33,6 +34,7 @@ import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.Date; /** * 项目立项 Excel @@ -119,8 +121,10 @@ public class ProjectApplyExcel implements Serializable { @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ShippingTemplateExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ShippingTemplateExcel.java index 2731bbe..6457383 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ShippingTemplateExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ShippingTemplateExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -30,7 +31,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.time.LocalDateTime; +import java.util.Date; /** * 发货模板 Excel @@ -59,8 +60,10 @@ public class ShippingTemplateExcel implements Serializable { @ExcelProperty("备注") private String remark; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TemporaryCreditLimitExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TemporaryCreditLimitExcel.java index 03ceb9b..5d58a54 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TemporaryCreditLimitExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TemporaryCreditLimitExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -32,7 +33,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 临时额度申请 Excel @@ -81,8 +82,10 @@ public class TemporaryCreditLimitExcel implements Serializable { @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TireReplacementRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TireReplacementRecordExportExcel.java index e2e2a32..26009e8 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TireReplacementRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TireReplacementRecordExportExcel.java @@ -37,7 +37,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 换胎记录导出 Excel @@ -76,14 +76,14 @@ public class TireReplacementRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportChangeRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportChangeRecordExportExcel.java index d4f4ce9..f737cd2 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportChangeRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportChangeRecordExportExcel.java @@ -28,7 +28,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.time.LocalDateTime; +import java.util.Date; /** * 变更记录导出 Excel @@ -57,14 +57,14 @@ public class TransportChangeRecordExportExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanExcel.java index 955d0b0..c5d4e7e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; import cn.idev.excel.annotation.write.style.HeadRowHeight; @@ -31,7 +32,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; /** * 运输计划 Excel @@ -92,8 +93,10 @@ public class TransportPlanExcel implements Serializable { @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordExcel.java index 7abf349..7b95877 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordExcel.java @@ -37,6 +37,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.Date; /** * 违章记录 Excel @@ -95,14 +96,14 @@ public class ViolationRecordExcel implements Serializable { @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; + private Date createTime; @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; + private Date updateTime; @ExcelIgnore private String errorMessage; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java index 3a68f3e..09e60ef 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java @@ -23,6 +23,7 @@ package org.springblade.transport.excel; import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.format.DateTimeFormat; import cn.idev.excel.annotation.ExcelIgnore; import cn.idev.excel.annotation.write.style.ColumnWidth; import cn.idev.excel.annotation.write.style.ContentRowHeight; @@ -33,7 +34,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Date; import java.time.LocalDate; /** @@ -151,9 +152,11 @@ public class WaybillExcel implements Serializable { @ExcelProperty("更新人") private String updateUserName; @ExcelProperty("创建时间") - private LocalDateTime createTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date createTime; @ExcelProperty("更新时间") - private LocalDateTime updateTime; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + private Date updateTime; @ExcelIgnore private String errorMessage; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java index 934c257..56ad900 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java @@ -150,6 +150,8 @@ public class WaybillServiceImpl extends BaseServiceImpl try { Waybill waybill = Objects.requireNonNull(BeanUtil.copyProperties(excel, Waybill.class)); waybill.setDataSource("批量导入"); + waybill.setCreateTime(null); + waybill.setUpdateTime(null); waybill.setCarrierJson(buildImportCarrierJson(waybill)); submit(waybill); } catch (Exception exception) { From 98697af6df3b274dc618c2a1c91080dfc3cff8f1 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sun, 20 Sep 2026 17:51:29 +0800 Subject: [PATCH 03/14] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E8=BF=9D?= =?UTF-8?q?=E7=AB=A0=E8=AE=B0=E5=BD=95=E5=AF=BC=E5=85=A5=E7=9A=84=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=B8=8E=E4=BA=8B=E9=A1=B9=E5=AD=97=E6=AE=B5=E6=B7=B7?= =?UTF-8?q?=E6=B7=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 导入模型将合并的"类型/事项"列拆分为"*类型"与"*事项"两列,与导出模型保持一致 - 校验与清空逻辑改为按车船类型严格匹配:车辆只允许填类型,船舶只允许填事项,误填对侧字段直接报错而非静默丢弃 - 将清空对侧字段的时机从 prepare 移到 validate 之后,避免用户误填内容被静默丢弃 - 提取 CAR/SHIP 常量,替换散落的"车辆"/"船舶"字面量 --- .../excel/ViolationRecordImportExcel.java | 7 +++- .../impl/ViolationRecordServiceImpl.java | 39 ++++++++++++------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordImportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordImportExcel.java index f3e7f6b..4eb3502 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordImportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ViolationRecordImportExcel.java @@ -60,8 +60,11 @@ public class ViolationRecordImportExcel implements Serializable { @ExcelProperty("*驾驶人") private String driverName; - @ExcelProperty("*类型/事项") - private String violationTypeOrItem; + @ExcelProperty("*类型") + private String violationType; + + @ExcelProperty("*事项") + private String violationItem; @ExcelProperty("*时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ViolationRecordServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ViolationRecordServiceImpl.java index ed9dfe9..4d99a4f 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ViolationRecordServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ViolationRecordServiceImpl.java @@ -67,6 +67,8 @@ public class ViolationRecordServiceImpl extends BaseServiceImpl selectViolationRecordPage(IPage page, ViolationRecordVO violationRecord) { @@ -81,6 +83,7 @@ public class ViolationRecordServiceImpl extends BaseServiceImpl + * 必须在 validate 之后执行:校验需要看到用户填了什么, + * 若提前清空,误填的内容会被静默丢弃,用户无从察觉。 + */ + private void clearIrrelevantField(ViolationRecord violationRecord) { + if (CAR.equals(violationRecord.getVehicleType())) { violationRecord.setViolationItem(null); } else { violationRecord.setViolationType(null); } - if (UNPROCESSED.equals(violationRecord.getProcessStatus())) { - violationRecord.setProcessResult(null); - } } private void validate(ViolationRecord violationRecord) { if (Func.isEmpty(violationRecord.getVehicleType())) { throw new ServiceException("车船类型不能为空"); } - if (!"车辆".equals(violationRecord.getVehicleType()) && !"船舶".equals(violationRecord.getVehicleType())) { + if (!CAR.equals(violationRecord.getVehicleType()) && !SHIP.equals(violationRecord.getVehicleType())) { throw new ServiceException("车船类型不正确"); } if (Func.isEmpty(violationRecord.getVehicleNo())) { @@ -155,12 +162,18 @@ public class ViolationRecordServiceImpl extends BaseServiceImpl Date: Sun, 20 Sep 2026 19:31:58 +0800 Subject: [PATCH 04/14] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E6=9C=AA?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=20MinIO=20=E6=97=B6=20transport=20=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E6=97=A0=E6=B3=95=E5=90=AF=E5=8A=A8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 凭证图片 MinIO 客户端此前为无条件装配,缺少 file.storage.minio.endpoint 配置时会在启动阶段直接抛 IllegalArgumentException,导致整个 blade-transport 服务起不来,并在关闭失败上下文时连带抛出 nacosGracefulShutdownDelegate 的 BeanCreationNotAllowedException, 掩盖了真正的失败原因。 - VoucherMinioConfig 增加 @ConditionalOnProperty,未配置 endpoint 时不注册客户端 - ProcessConfigController、VoucherManageServiceImpl 改用 ObjectProvider 可选注入,避免构造器注入因 bean 缺失而启动失败 - 新增 minioClient() 访问器,在真正调用时给出"未配置"的明确提示, 与已有 validateMinioConfig() 的提示风格保持一致 未配置 MinIO 时服务可正常启动,凭证相关接口仅在使用时提示配置缺失; 已配置 MinIO 时行为不变。 --- .../transport/config/VoucherMinioConfig.java | 6 ++++ .../controller/ProcessConfigController.java | 25 ++++++++++--- .../impl/VoucherManageServiceImpl.java | 35 +++++++++++++------ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/config/VoucherMinioConfig.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/config/VoucherMinioConfig.java index 5c0545b..08ed5e3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/config/VoucherMinioConfig.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/config/VoucherMinioConfig.java @@ -6,14 +6,20 @@ package org.springblade.transport.config; import io.minio.MinioClient; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 凭证图片 MinIO 客户端配置。 + *

* 连接参数由 Nacos 的 file.storage.minio 配置提供。 + * 未配置 {@code file.storage.minio.endpoint} 时不注册该客户端, + * 以免凭证上传功能缺失配置导致整个 blade-transport 服务无法启动; + * 此时凭证相关接口会在调用时给出明确提示,而非启动即失败。 */ @Configuration +@ConditionalOnProperty(prefix = "file.storage.minio", name = "endpoint") public class VoucherMinioConfig { @Bean diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ProcessConfigController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ProcessConfigController.java index 8df54de..c0cb3d3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ProcessConfigController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ProcessConfigController.java @@ -52,6 +52,7 @@ import org.springblade.transport.pojo.entity.Waybill; import org.springblade.transport.pojo.vo.BusinessRemoveResultVO; import org.springblade.transport.pojo.vo.ProcessConfigVO; import org.springblade.transport.service.IProcessConfigService; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -83,20 +84,34 @@ public class ProcessConfigController extends BladeController { private final VoucherFileMapper voucherFileMapper; private final VoucherImageMapper voucherImageMapper; private final VoucherManageMapper voucherManageMapper; - private final MinioClient minioClient; + private final ObjectProvider minioClientProvider; @Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}") private String minioBucketName; public ProcessConfigController(IProcessConfigService processConfigService, WaybillMapper waybillMapper, VoucherFileMapper voucherFileMapper, VoucherImageMapper voucherImageMapper, VoucherManageMapper voucherManageMapper, - MinioClient minioClient) { + ObjectProvider minioClientProvider) { this.processConfigService = processConfigService; this.waybillMapper = waybillMapper; this.voucherFileMapper = voucherFileMapper; this.voucherImageMapper = voucherImageMapper; this.voucherManageMapper = voucherManageMapper; - this.minioClient = minioClient; + this.minioClientProvider = minioClientProvider; + } + + /** + * 获取 MinIO 客户端。 + *

+ * 未配置 file.storage.minio.endpoint 时该客户端不会被注册, + * 此时凭证预览地址无法生成,抛出明确提示而非启动即失败。 + */ + private MinioClient minioClient() { + MinioClient minioClient = minioClientProvider.getIfAvailable(); + if (minioClient == null) { + throw new IllegalStateException("Nacos 未配置 file.storage.minio.endpoint,凭证文件功能不可用"); + } + return minioClient; } @GetMapping("/detail") @@ -147,7 +162,7 @@ public class ProcessConfigController extends BladeController { result.put("waybillNo", image.getWaybillNo()); result.put("objectKey", image.getObjectKey()); try { - result.put("url", minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() + result.put("url", minioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() .method(Method.GET).bucket(minioBucketName).object(image.getObjectKey()) .expiry(1, TimeUnit.HOURS).build())); } catch (Exception e) { @@ -270,7 +285,7 @@ public class ProcessConfigController extends BladeController { result.put("objectKey", image.objectKey()); result.put("matched", image.matched()); try { - result.put("url", minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() + result.put("url", minioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() .method(Method.GET).bucket(minioBucketName).object(image.objectKey()) .expiry(1, TimeUnit.HOURS).build())); } catch (Exception exception) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/VoucherManageServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/VoucherManageServiceImpl.java index 3b4d519..6eb2e8b 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/VoucherManageServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/VoucherManageServiceImpl.java @@ -42,6 +42,7 @@ import io.minio.PutObjectArgs; import io.minio.RemoveObjectArgs; import io.minio.http.Method; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Value; import java.time.format.DateTimeFormatter; @@ -79,14 +80,14 @@ public class VoucherManageServiceImpl extends BaseServiceImpl minioClientProvider; private final String minioBucketName; private final String minioRootDirectory; public VoucherManageServiceImpl(VoucherWaybillBatchMapper voucherWaybillBatchMapper, IProjectApplyService projectApplyService, IWaybillService waybillService, VoucherImageMapper voucherImageMapper, VoucherFileMapper voucherFileMapper, - ApplicationEventPublisher eventPublisher, MinioClient minioClient, + ApplicationEventPublisher eventPublisher, ObjectProvider minioClientProvider, @Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}") String minioBucketName, @Value("${file.storage.minio.root-directory:${minio.root-directory:}}") String minioRootDirectory) { this.voucherWaybillBatchMapper = voucherWaybillBatchMapper; @@ -95,7 +96,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl candidateObjectKeys = resolvedObjectKey.equals(objectKey) ? List.of(objectKey) : List.of(resolvedObjectKey, objectKey); for (String candidateObjectKey : candidateObjectKeys) { - try (InputStream source = minioClient.getObject(GetObjectArgs.builder().bucket(minioBucketName).object(candidateObjectKey).build()); + try (InputStream source = minioClient().getObject(GetObjectArgs.builder().bucket(minioBucketName).object(candidateObjectKey).build()); OutputStream target = Files.newOutputStream(archivePath)) { source.transferTo(target); return archivePath; @@ -427,7 +428,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl + * 未配置 file.storage.minio.endpoint 时该客户端不会被注册, + * 此处给出与 {@link #validateMinioConfig()} 一致的明确提示。 + */ + private MinioClient minioClient() { + MinioClient minioClient = minioClientProvider.getIfAvailable(); + if (minioClient == null) { + throw new ServiceException("Nacos 未配置 file.storage.minio.endpoint,凭证文件功能不可用"); + } + return minioClient; + } + private String buildObjectKey(Long voucherId, String waybillNo, String plateNo, String entryName) { String objectKey = voucherId + "/" + waybillNo + "/" + safePathPart(plateNo) + "/" + safeArchivePath(entryName); if (Func.isEmpty(minioRootDirectory)) { @@ -1127,7 +1142,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl Date: Sun, 20 Sep 2026 20:31:02 +0800 Subject: [PATCH 05/14] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E7=BB=B4?= =?UTF-8?q?=E4=BF=AE=E8=AE=B0=E5=BD=95=E6=89=B9=E9=87=8F=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=A9=BA=E7=99=BD=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MaintenanceRecordExcel 的 createTime/updateTime 字段类型为 java.util.Date, 但仍绑定了 MaintenancePlanDateTimeConverter(Converter), 导出写单元格时 Date 强转 LocalDateTime 抛 ClassCastException, 写流中断导致下载到损坏的 xlsx,打开后一片空白 - 移除这两个 Date 审计字段上的 converter,仅保留 @DateTimeFormat("yyyy-MM-dd HH:mm:ss"), 与 AccidentRecordExportExcel 等同模块导出模型写法保持一致 - 维修时间、出厂时间仍为 LocalDateTime,转换器保留不变,导入解析不受影响 --- .../springblade/transport/excel/MaintenanceRecordExcel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java index 7d96042..db5f3e9 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExcel.java @@ -96,14 +96,14 @@ public class MaintenanceRecordExcel implements Serializable { @NumberFormat("0.00") private BigDecimal mileage; - @ExcelProperty(value = "创建时间", converter = MaintenancePlanDateTimeConverter.class) + @ExcelProperty("创建时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") private Date createTime; @ExcelProperty("更新人") private String updateUserName; - @ExcelProperty(value = "更新时间", converter = MaintenancePlanDateTimeConverter.class) + @ExcelProperty("更新时间") @DateTimeFormat("yyyy-MM-dd HH:mm:ss") private Date updateTime; From 37309b24f96eff187fd54859aa6143447b740284 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sun, 20 Sep 2026 20:34:56 +0800 Subject: [PATCH 06/14] =?UTF-8?q?:bug:=20=E7=BB=B4=E4=BF=AE=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=AF=BC=E5=87=BA=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84=E2=80=9C=E5=AF=BC=E5=87=BA=E5=A4=B1=E8=B4=A5=E5=8E=9F?= =?UTF-8?q?=E5=9B=A0=E2=80=9D=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MaintenanceRecordExportExcel 仅为把父类 @ExcelIgnore 的 errorMessage 重新暴露成 第 18 列“导出失败原因”而存在,正式导出该列恒为空白,属遗留冗余 - 删除该字段与对应 import,导出回归 17 列,与导入模板字段顺序一致 - 导入失败明细导出不依赖本类:ImportFailureExcelUtil 自行按字段构建列并过滤 errorMessage,再反射读取失败原因追加“导入失败原因”列,逻辑不受影响 --- .../transport/excel/MaintenanceRecordExportExcel.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExportExcel.java index c65060e..8f5a55e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExportExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/MaintenanceRecordExportExcel.java @@ -25,7 +25,6 @@ */ package org.springblade.transport.excel; -import cn.idev.excel.annotation.ExcelProperty; import lombok.Data; import lombok.EqualsAndHashCode; @@ -42,7 +41,4 @@ public class MaintenanceRecordExportExcel extends MaintenanceRecordExcel { @Serial private static final long serialVersionUID = 1L; - @ExcelProperty(value = "导出失败原因", index = 17) - private String errorMessage; - } From b02aff8ff64466707485480b3a064bfd07537a88 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Mon, 21 Sep 2026 04:19:55 +0800 Subject: [PATCH 07/14] =?UTF-8?q?:sparkles:=20=E8=BF=90=E5=8D=95=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E6=94=AF=E6=8C=81=E5=A4=9A=E8=B4=A7=E7=89=A9=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E4=B8=8E=E9=85=8D=E8=BD=BD=E8=B7=AF=E7=BA=BF=E4=B8=B2?= =?UTF-8?q?=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 同一运单标识号的多行货物合并为一条运单,各行转为货物明细写入 goodsJson,数量与其他费用合计按行累加 - 新增组内一致性校验:发货地址、到货地址、数量单位、配载标识号不一致时报错,避免无法合并的数据静默落库 - 配载单路线按导入顺序串联,上一票到货地与下一票发货地重合时去重写入途经地,到货地取末票 --- .../impl/LoadingManageServiceImpl.java | 37 ++++- .../impl/WaybillImportBatchServiceImpl.java | 139 +++++++++++++++++- 2 files changed, 169 insertions(+), 7 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java index dfbd4de..d956a87 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java @@ -174,6 +174,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl waybills) { + // 按顺序收集全部节点:首票发货地、每票到货地;节点与相邻前一点相同则重合跳过 + List nodes = new ArrayList<>(); + for (Waybill waybill : waybills) { + String departure = TransportBusinessSupport.trimToNull(waybill.getDepartureAddress()); + String arrival = TransportBusinessSupport.trimToNull(waybill.getArrivalAddress()); + appendRouteNode(nodes, departure); + appendRouteNode(nodes, arrival); + } + // 途经点 = 去掉首尾(首票发货地、末票到货地)后的中间节点 + List transitNodes = nodes.size() > 2 ? nodes.subList(1, nodes.size() - 1) : List.of(); + if (Func.isEmpty(transitNodes)) { + return null; + } + return String.join(" - ", transitNodes); + } + + private void appendRouteNode(List nodes, String address) { + if (Func.isEmpty(address)) { + return; + } + if (!nodes.isEmpty() && nodes.get(nodes.size() - 1).equals(address)) { + // 与上一节点相同视为同一地点,重合不重复 + return; + } + nodes.add(address); + } + @Override @Transactional(rollbackFor = Exception.class) public BusinessRemoveResultVO removeLoadingManage(String ids) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java index f944cb1..9f0259d 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java @@ -19,6 +19,7 @@ import org.springblade.core.tool.utils.BeanUtil; import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.WebUtil; +import org.springblade.core.tool.jackson.JsonUtil; import org.springblade.system.cache.DictCache; import org.springblade.system.cache.DictBizCache; import org.springblade.system.cache.UserCache; @@ -55,6 +56,7 @@ import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -184,20 +186,36 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl> rows = Func.isEmpty(request.getRows()) ? List.of() : request.getRows(); + // 逐行构建运单(未落库),构建失败时报出对应行号 + List rowWaybills = new ArrayList<>(); + for (int index = 0; index < rows.size(); index++) { + try { + rowWaybills.add(buildWaybill(rows.get(index), batch, request.getCarrierContractId(), draft)); + } catch (Exception exception) { + throw new ServiceException("第" + (index + 1) + "行" + (draft ? "保存" : "导入") + "失败:" + exception.getMessage()); + } + } + // 同一运单标识号的多货物行合并为一条运单,各行转为货物明细 + Map waybillByRow = new HashMap<>(); + Set skipRows = new HashSet<>(); + mergeMultiCargoRows(rows, rowWaybills, waybillByRow, skipRows); + List waybills = new ArrayList<>(); Map> loadingWaybills = new TreeMap<>(); for (int index = 0; index < rows.size(); index++) { + if (skipRows.contains(index)) continue; + Waybill waybill = waybillByRow.getOrDefault(index, rowWaybills.get(index)); + // 配载标识号必须取自行数据:运单落库(prepareForSave)会把新建运单的 loadingNo 置空 + String loadingIdentifier = stringValue(rows.get(index), "loadingIdentifier", "配载标识号"); try { - String loadingIdentifier = stringValue(rows.get(index), "loadingIdentifier", "配载标识号"); - Waybill waybill = buildWaybill(rows.get(index), batch, request.getCarrierContractId(), draft); if (draft) waybillService.saveDraft(waybill); else waybillService.submit(waybill); - waybills.add(waybill); - if (!draft && IMPORT_TYPE_WAYBILL.equals(batch.getImportType()) && Func.isNotEmpty(loadingIdentifier)) { - loadingWaybills.computeIfAbsent(loadingIdentifier, key -> new ArrayList<>()).add(waybill); - } } catch (Exception exception) { throw new ServiceException("第" + (index + 1) + "行" + (draft ? "保存" : "导入") + "失败:" + exception.getMessage()); } + waybills.add(waybill); + if (!draft && IMPORT_TYPE_WAYBILL.equals(batch.getImportType()) && Func.isNotEmpty(loadingIdentifier)) { + loadingWaybills.computeIfAbsent(loadingIdentifier, key -> new ArrayList<>()).add(waybill); + } } loadingWaybills.forEach(loadingManageService::createFromImportedWaybills); batch.setWaybillCount(waybills.size()); @@ -218,6 +236,69 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl> rows, List rowWaybills, + Map waybillByRow, Set skipRows) { + Map> groups = new LinkedHashMap<>(); + for (int index = 0; index < rowWaybills.size(); index++) { + String relationNo = rowWaybills.get(index).getRelationNo(); + if (Func.isEmpty(relationNo)) continue; + groups.computeIfAbsent(relationNo, key -> new ArrayList<>()).add(index); + } + for (List groupRows : groups.values()) { + if (groupRows.size() <= 1) continue; + Waybill primary = rowWaybills.get(groupRows.get(0)); + List> goodsList = new ArrayList<>(); + for (Integer rowIndex : groupRows) { + Waybill current = rowWaybills.get(rowIndex); + if (!rowIndex.equals(groupRows.get(0))) { + // 主行字段保留首行值,数量等数值字段累加同组其余各行 + mergeNumericFields(primary, current); + skipRows.add(rowIndex); + waybillByRow.remove(rowIndex); + } + goodsList.add(buildGoodsItem(rows.get(rowIndex), current)); + } + primary.setGoodsJson(JsonUtil.toJson(goodsList)); + } + } + + /** 合并多货物行时累加数量、其他费用等可加数值字段;为空的字段跳过。 */ + private void mergeNumericFields(Waybill primary, Waybill current) { + primary.setQuantity(sumNullable(primary.getQuantity(), current.getQuantity())); + primary.setOtherFeeTotal(sumNullable(primary.getOtherFeeTotal(), current.getOtherFeeTotal())); + } + + private BigDecimal sumNullable(BigDecimal first, BigDecimal second) { + if (first == null) return second; + if (second == null) return first; + return first.add(second); + } + + /** 由导入行构建一条货物明细,字段与运单详情货物编辑结构保持一致。 */ + private Map buildGoodsItem(Map row, Waybill waybill) { + Map goods = new LinkedHashMap<>(); + putIfNotEmpty(goods, "cargoName", waybill.getCargoName()); + putIfNotEmpty(goods, "cargoType", waybill.getCargoType()); + putIfNotEmpty(goods, "specification", waybill.getSpecification()); + putIfNotEmpty(goods, "model", waybill.getModel()); + putIfNotEmpty(goods, "packageType", stringValue(row, "packageType", "包装")); + if (waybill.getQuantity() != null) { + goods.put("quantity", waybill.getQuantity().toPlainString()); + } + putIfNotEmpty(goods, "quantityUnit", waybill.getQuantityUnit()); + putIfNotEmpty(goods, "remark", waybill.getRemark()); + return goods; + } + + private void putIfNotEmpty(Map goods, String key, String value) { + if (Func.isNotEmpty(value)) goods.put(key, value); + } + @Override public IPage page(IPage page, WaybillImportBatchRequest request) { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() @@ -523,6 +604,9 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl row, int rowIndex, + Map> waybillIdentifierMap, List> allRows, List errors) { + String waybillIdentifier = stringValue(row, "waybillIdentifier", "同一运单标识号"); + if (Func.isEmpty(waybillIdentifier)) { + return; + } + List sameIdentifierRows = waybillIdentifierMap.get(waybillIdentifier); + if (sameIdentifierRows == null || sameIdentifierRows.size() <= 1) { + return; + } + compareGroupValue(rowIndex, sameIdentifierRows, allRows, "departureAddress", "发货地址", errors); + compareGroupValue(rowIndex, sameIdentifierRows, allRows, "arrivalAddress", "到货地址", errors); + compareGroupValue(rowIndex, sameIdentifierRows, allRows, "quantityUnit", "数量单位", errors); + compareGroupValue(rowIndex, sameIdentifierRows, allRows, "loadingIdentifier", "配载标识号", errors); + } + + /** 比较同一运单标识号组内其余行的字段值,不一致时记录错误;空值不参与比较。 */ + private void compareGroupValue(int rowIndex, List sameIdentifierRows, List> allRows, + String field, String fieldName, List errors) { + String currentValue = normalizeCompareValue(stringValue(allRows.get(rowIndex), field, fieldName)); + if (Func.isEmpty(currentValue)) { + return; + } + for (Integer otherRowIndex : sameIdentifierRows) { + if (otherRowIndex <= rowIndex) { + continue; + } + String otherValue = normalizeCompareValue(stringValue(allRows.get(otherRowIndex), field, fieldName)); + if (Func.isNotEmpty(otherValue) && !currentValue.equals(otherValue)) { + errors.add("同一运单标识号下," + fieldName + "必须一致"); + return; + } + } + } + + private String normalizeCompareValue(String value) { + return Func.isEmpty(value) ? null : value.replaceAll("\\s+", ""); + } + private LocalDate parseDateForValidation(Object value, String fieldName, List errors) { if (value == null || String.valueOf(value).isBlank()) { return null; From 8485efe00d2bcfd9c288e834475bb2482ee514a2 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Mon, 21 Sep 2026 05:21:27 +0800 Subject: [PATCH 08/14] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E5=A4=B1=E8=B4=A5=E6=98=8E=E7=BB=86=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=8C=89=E5=88=97=E6=A0=87=E7=BA=A2=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4e1a162 移除了按列名匹配逻辑后只剩失败原因列标红。 现恢复并修正误标红:命中片段被更长片段覆盖时只保留更长的列 (避免原因含「车船类型」时把子串「类型」列一起标红)。 --- .../common/excel/ImportFailureExcelUtil.java | 159 +++++++++++++++++- 1 file changed, 151 insertions(+), 8 deletions(-) diff --git a/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java index 70d7183..e297cb5 100644 --- a/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java +++ b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java @@ -35,6 +35,7 @@ import cn.idev.excel.write.metadata.holder.WriteTableHolder; import jakarta.servlet.http.HttpServletResponse; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Workbook; @@ -46,9 +47,11 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * 导入失败明细 Excel 导出工具类 @@ -95,7 +98,7 @@ public class ImportFailureExcelUtil { * @param excelClass 原导入 Excel 类型 */ public static void export(HttpServletResponse response, String fileName, String sheetName, List data, Class excelClass) { - exportFailureReasonOnly(response, fileName, sheetName, data, excelClass); + export(response, fileName, sheetName, data, excelClass, true); } /** @@ -109,6 +112,16 @@ public class ImportFailureExcelUtil { */ public static void exportFailureReasonOnly(HttpServletResponse response, String fileName, String sheetName, List data, Class excelClass) { + export(response, fileName, sheetName, data, excelClass, false); + } + + /** + * 导出导入失败明细 + * + * @param markErrorColumns 是否按失败原因里出现的列名,标红对应字段单元格 + */ + private static void export(HttpServletResponse response, String fileName, String sheetName, + List data, Class excelClass, boolean markErrorColumns) { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); String encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8); @@ -118,7 +131,7 @@ public class ImportFailureExcelUtil { List> rows = buildRows(data, excelFields); try { FastExcel.write(response.getOutputStream()) - .registerWriteHandler(new ImportFailureCellStyleHandler(excelFields.size())) + .registerWriteHandler(new ImportFailureCellStyleHandler(excelFields, rows, markErrorColumns)) .head(head) .sheet(sheetName) .doWrite(rows); @@ -204,15 +217,60 @@ public class ImportFailureExcelUtil { throw new NoSuchFieldException(String.join(",", fieldNames)); } + /** + * 文本归一化:去掉空白、星号与中英文标点并统一小写 + *

+ * 目的是让「失败原因里写的列名」与「表头列名」能直接做包含匹配, + * 不受「*」「/」「:」等写法差异影响。 + */ + private static String normalize(String value) { + return value == null ? "" : value.replaceAll("[\\s*_::,,。;;()()\\[\\]【】<>《》//、-]", "").toLowerCase(); + } + + /** + * 取某一列用于定位的候选关键词 + *

+ * 包含表头列名、字段名,以及列名按分隔符拆出的片段 + * (如「车牌号/船号」拆出「车牌号」「船号」,失败原因只写其中一段时也能定位)。 + */ + private static List columnKeywords(Field field) { + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + String[] value = excelProperty.value(); + String columnName = value.length == 0 ? field.getName() : value[0]; + List keywords = new ArrayList<>(); + keywords.add(columnName); + keywords.add(field.getName()); + keywords.addAll(Arrays.asList(columnName.replace("*", "").split("[//、()()\\s]+"))); + return keywords.stream() + .map(ImportFailureExcelUtil::normalize) + .filter(keyword -> keyword.length() >= 2) + .distinct() + .toList(); + } + private static class ImportFailureCellStyleHandler implements CellWriteHandler { private final int failureReasonColumnIndex; + private final boolean markErrorColumns; + private final List> rows; + private final Map> redColumnsByRow = new HashMap<>(); private final Map redStyleCache = new HashMap<>(); + private final Map redFillStyleCache = new HashMap<>(); private final Map noWrapStyleCache = new HashMap<>(); private final Map columnWidthCache = new HashMap<>(); - private ImportFailureCellStyleHandler(int failureReasonColumnIndex) { - this.failureReasonColumnIndex = failureReasonColumnIndex; + private ImportFailureCellStyleHandler(List excelFields, List> rows, boolean markErrorColumns) { + this.failureReasonColumnIndex = excelFields.size(); + this.markErrorColumns = markErrorColumns; + this.rows = rows; + if (markErrorColumns) { + List> columnKeywords = excelFields.stream() + .map(ImportFailureExcelUtil::columnKeywords) + .toList(); + for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) { + redColumnsByRow.put(rowIndex, resolveRedColumns(rows.get(rowIndex), columnKeywords)); + } + } } @Override @@ -230,14 +288,95 @@ public class ImportFailureExcelUtil { return; } adjustColumnWidth(cell); - if (cell.getColumnIndex() == failureReasonColumnIndex) { - markRed(cell); + if (relativeRowIndex == null || relativeRowIndex < 0 || relativeRowIndex >= rows.size()) { + return; + } + if (shouldMarkRed(relativeRowIndex, cell.getColumnIndex())) { + markRed(cell, isBlankValue(rows.get(relativeRowIndex), cell.getColumnIndex())); } } - private void markRed(Cell cell) { + /** + * 计算某一行需要标红的列 + *

+ * 失败原因里出现的列名即视为出错列;但当某个命中片段被另一列更长的命中 + * 片段完全覆盖时(如原因「车船类型不能为空」同时命中「车船类型」和它的 + * 子串「类型」),只保留更长的那一列,避免把无关列一起标红。 + */ + private Set resolveRedColumns(List row, List> columnKeywords) { + Set redColumns = new LinkedHashSet<>(); + redColumns.add(failureReasonColumnIndex); + String failureReason = normalize(String.valueOf(row.get(failureReasonColumnIndex))); + if (failureReason.isEmpty()) { + return redColumns; + } + List matches = new ArrayList<>(); + for (int column = 0; column < columnKeywords.size(); column++) { + for (String keyword : columnKeywords.get(column)) { + int fromIndex = 0; + while (true) { + int index = failureReason.indexOf(keyword, fromIndex); + if (index < 0) { + break; + } + matches.add(new int[]{index, index + keyword.length(), column}); + fromIndex = index + 1; + } + } + } + for (int[] match : matches) { + if (!isSubsumed(match, matches)) { + redColumns.add(match[2]); + } + } + return redColumns; + } + + /** + * 判断该命中片段是否被另一列更长的命中片段完全覆盖 + */ + private boolean isSubsumed(int[] match, List matches) { + int matchLength = match[1] - match[0]; + for (int[] other : matches) { + if (other[2] == match[2]) { + continue; + } + if ((other[1] - other[0]) > matchLength && other[0] <= match[0] && other[1] >= match[1]) { + return true; + } + } + return false; + } + + private boolean shouldMarkRed(int rowIndex, int columnIndex) { + if (columnIndex == failureReasonColumnIndex) { + return true; + } + if (!markErrorColumns) { + return false; + } + Set redColumns = redColumnsByRow.get(rowIndex); + return redColumns != null && redColumns.contains(columnIndex); + } + + private boolean isBlankValue(List row, int columnIndex) { + if (columnIndex < 0 || columnIndex >= row.size()) { + return true; + } + Object value = row.get(columnIndex); + return value == null || String.valueOf(value).isBlank(); + } + + /** + * 标红单元格 + *

+ * 单元格为空时只改字体颜色屏幕上什么也看不到(「必填项为空」正是这种情况), + * 因此对空单元格额外加浅红底纹,保证用户能定位到是哪一列出错。 + */ + private void markRed(Cell cell, boolean blankValue) { CellStyle currentStyle = cell.getCellStyle(); - CellStyle redStyle = redStyleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> { + Map styleCache = blankValue ? redFillStyleCache : redStyleCache; + CellStyle redStyle = styleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> { Workbook workbook = cell.getSheet().getWorkbook(); CellStyle newStyle = workbook.createCellStyle(); newStyle.cloneStyleFrom(currentStyle); @@ -245,6 +384,10 @@ public class ImportFailureExcelUtil { font.setColor(IndexedColors.RED.getIndex()); newStyle.setFont(font); newStyle.setWrapText(true); + if (blankValue) { + newStyle.setFillForegroundColor(IndexedColors.ROSE.getIndex()); + newStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + } return newStyle; }); cell.setCellStyle(redStyle); From 4c96583701a79070ed3a8cdd5b59dbb0ddb46804 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Mon, 21 Sep 2026 05:34:59 +0800 Subject: [PATCH 09/14] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E7=A0=81?= =?UTF-8?q?=E5=A4=B4=E5=AF=BC=E5=85=A5=E7=88=B6=E6=B8=AF=E5=8F=A3=E5=8F=96?= =?UTF-8?q?=E5=88=97=E4=B8=8D=E4=B8=80=E8=87=B4=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E9=9D=99=E9=BB=98=E4=B8=A2=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit importTerminals 中 setParentCode 取「港口编码」列,而抑制连带报错时 却用「上级港口编码」判断父行是否已声明。用户若把完整编码填入 「码头编码」并让「港口编码」留空,父港口解析失败的异常会被误判为 「父行已声明」而吞掉,码头被静默丢弃且接口返回成功。 - splitByCategory 增加码头行「港口编码」非空校验,明确报错 - 抑制逻辑改为与 setParentCode 取同一列 --- .../system/service/impl/PortTerminalServiceImpl.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java index e572dff..892f4d6 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java @@ -186,6 +186,13 @@ public class PortTerminalServiceImpl extends BaseServiceImpl Date: Mon, 21 Sep 2026 05:45:49 +0800 Subject: [PATCH 10/14] =?UTF-8?q?:zap:=20=E7=B2=BE=E7=AE=80=E8=BF=90?= =?UTF-8?q?=E5=8D=95=E5=AF=BC=E5=85=A5=E8=BD=A6=E7=89=8C=E5=8F=B7=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E7=9A=84=E5=A4=B1=E8=B4=A5=E5=8E=9F=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一次车牌号错误原来最多会叠出「长度不能少于7位」「首位必须是省份简称、 第二位必须是英文字母」「格式不正确」三条原因,导出的失败明细里这一格很长。 现将公路运输下的必填、长度与格式统一为一条「车牌号校验不通过」。 - 校验强度不变:长度 7/8 位本就由 VEHICLE_NO_PATTERN 蕴含,省份简称白名单照旧 - 保留「车牌号」字样,失败明细仍能按列名拆词匹配并标红车牌号列;车牌为空时 走空单元格红底纹分支,比原来只有文字更容易定位 - 未改动「同一配载标识号下,车牌号不一致」,该原因由 tests/fixtures/waybill-import 用例 04 断言 --- .../impl/WaybillImportBatchServiceImpl.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java index 9f0259d..b81a5b8 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java @@ -655,21 +655,13 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl 8) { - errors.add("车牌号长度不能超过8位"); - } - if (!VEHICLE_PROVINCE_PATTERN.matcher(vehicleNo).lookingAt()) { - errors.add("车牌号首位必须是省份简称,第二位必须是英文字母"); - } - if (!VEHICLE_NO_PATTERN.matcher(vehicleNo).matches()) { - errors.add("车牌号格式不正确,应为首位省份简称、次位英文字母、总长度7或8位"); + // 必填、长度与格式统一为一条简短原因,避免多条规则叠加导致失败原因过长; + // 文案保留“车牌号”字样,导入失败明细仍能据此定位到车牌号列并标红。 + // VEHICLE_NO_PATTERN 已约束总长度为 7 或 8 位,故不再单独判断长度。 + if (Func.isEmpty(vehicleNo) + || !VEHICLE_PROVINCE_PATTERN.matcher(vehicleNo).lookingAt() + || !VEHICLE_NO_PATTERN.matcher(vehicleNo).matches()) { + errors.add("车牌号校验不通过"); } } From 37d0ebc21e8a1c8bdd60bc90ab606e5692fd517f Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Mon, 21 Sep 2026 05:52:31 +0800 Subject: [PATCH 11/14] =?UTF-8?q?:zap:=20=E5=AF=BC=E5=85=A5=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=98=8E=E7=BB=86=E5=8E=BB=E6=8E=89=E7=A9=BA=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=A0=BC=E5=BA=95=E7=BA=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 标红统一只改红色字体,不再对空单元格加浅红底纹(IndexedColors.ROSE)。 底纹原本是为「必填项为空时红字在屏幕上看不见」而加,但它超出 AGENTS.md §6.11「仅出错字段/列使用红色文字」的口径,视觉上也偏重。 - markRed(Cell, boolean) 简化为 markRed(Cell),删除 redFillStyleCache 与 isBlankValue - 空单元格的红色字体样式照旧写入 styles.xml,api-violation-xlsx-cols.js 按字体色判定,回归用例 03/04 不受影响 - 影响所有使用 ImportFailureExcelUtil 的导入模块(20+ 个) --- .../common/excel/ImportFailureExcelUtil.java | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java index e297cb5..6b0ad40 100644 --- a/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java +++ b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java @@ -35,7 +35,6 @@ import cn.idev.excel.write.metadata.holder.WriteTableHolder; import jakarta.servlet.http.HttpServletResponse; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Workbook; @@ -255,7 +254,6 @@ public class ImportFailureExcelUtil { private final List> rows; private final Map> redColumnsByRow = new HashMap<>(); private final Map redStyleCache = new HashMap<>(); - private final Map redFillStyleCache = new HashMap<>(); private final Map noWrapStyleCache = new HashMap<>(); private final Map columnWidthCache = new HashMap<>(); @@ -292,7 +290,7 @@ public class ImportFailureExcelUtil { return; } if (shouldMarkRed(relativeRowIndex, cell.getColumnIndex())) { - markRed(cell, isBlankValue(rows.get(relativeRowIndex), cell.getColumnIndex())); + markRed(cell); } } @@ -359,24 +357,15 @@ public class ImportFailureExcelUtil { return redColumns != null && redColumns.contains(columnIndex); } - private boolean isBlankValue(List row, int columnIndex) { - if (columnIndex < 0 || columnIndex >= row.size()) { - return true; - } - Object value = row.get(columnIndex); - return value == null || String.valueOf(value).isBlank(); - } - /** * 标红单元格 *

- * 单元格为空时只改字体颜色屏幕上什么也看不到(「必填项为空」正是这种情况), - * 因此对空单元格额外加浅红底纹,保证用户能定位到是哪一列出错。 + * 统一只改字体颜色,不加底纹;空单元格的红色字体在屏幕上不可见, + * 这类错误依靠失败原因列的文字定位。 */ - private void markRed(Cell cell, boolean blankValue) { + private void markRed(Cell cell) { CellStyle currentStyle = cell.getCellStyle(); - Map styleCache = blankValue ? redFillStyleCache : redStyleCache; - CellStyle redStyle = styleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> { + CellStyle redStyle = redStyleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> { Workbook workbook = cell.getSheet().getWorkbook(); CellStyle newStyle = workbook.createCellStyle(); newStyle.cloneStyleFrom(currentStyle); @@ -384,10 +373,6 @@ public class ImportFailureExcelUtil { font.setColor(IndexedColors.RED.getIndex()); newStyle.setFont(font); newStyle.setWrapText(true); - if (blankValue) { - newStyle.setFillForegroundColor(IndexedColors.ROSE.getIndex()); - newStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); - } return newStyle; }); cell.setCellStyle(redStyle); From ddfa52a2ac14f403b3b92b9c67dbf30ca5363e83 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Mon, 21 Sep 2026 06:24:31 +0800 Subject: [PATCH 12/14] =?UTF-8?q?:zap:=20=E6=94=B6=E7=AA=84=E8=BF=90?= =?UTF-8?q?=E8=BE=93=E6=96=B9=E5=BC=8F=E6=9E=9A=E4=B8=BE=E4=B8=BA=E5=85=AC?= =?UTF-8?q?=E8=B7=AF/=E9=93=81=E8=B7=AF/=E6=B0=B4=E8=B7=AF/=E8=88=AA?= =?UTF-8?q?=E7=A9=BA=E5=9B=9B=E7=A7=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 运输计划导入校验白名单去掉「公路整车、公路配载/零担、跨境海运」 运单批量导入字典兜底值去掉多余枚举,仅保留四种运输方式 --- .../transport/service/impl/TransportPlanServiceImpl.java | 4 ++-- .../transport/service/impl/WaybillImportBatchServiceImpl.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java index 9c574de..619a915 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java @@ -416,9 +416,9 @@ public class TransportPlanServiceImpl extends BaseServiceImpl validTransportTypes = List.of("公路整车", "公路配载/零担", "铁路运输", "水路运输", "跨境海运", "航空运输"); + List validTransportTypes = List.of("公路运输", "铁路运输", "水路运输", "航空运输"); if (!validTransportTypes.contains(transportType)) { - errors.add("运输类型需系统枚举值(公路整车、公路配载/零担、铁路运输、水路运输、跨境海运、航空运输)"); + errors.add("运输类型需系统枚举值(公路运输、铁路运输、水路运输、航空运输)"); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java index b81a5b8..73cd624 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java @@ -980,8 +980,8 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl defaults = List.of("公路运输", "铁路运输", "水路运输", "航空运输", - "公路整车", "公路配载/零担", "铁路整车", "铁路零担", "水路", "航空", "多式联运", "管道运输", "其他"); + // 运输方式仅支持公路、铁路、水路、航空四种 + List defaults = List.of("公路运输", "铁路运输", "水路运输", "航空运输"); defaults.forEach(value -> options.put(value, value)); } return options; From fd6563f53b61cb58f3b8dc69eea64f481bd5fd99 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Mon, 21 Sep 2026 09:47:50 +0800 Subject: [PATCH 13/14] =?UTF-8?q?:sparkles:=20=E6=89=B9=E9=87=8F=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E6=97=A5=E6=9C=9F=E5=AE=BD=E5=AE=B9=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=EF=BC=9A=E5=85=BC=E5=AE=B9=202026-08-02=20=E4=B8=8E=202026-8-2?= =?UTF-8?q?=20=E7=AD=89=E4=B9=A6=E5=86=99=E5=8F=98=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 断言, 全部通过。 --- blade-common/pom.xml | 4 + .../common/excel/LenientDateParser.java | 198 ++++++++++++++++++ .../excel/LenientDateStringConverter.java | 75 +++++++ .../excel/LenientDateTimeStringConverter.java | 82 ++++++++ .../system/excel/CurrencyExcel.java | 9 +- .../springblade/system/excel/UserExcel.java | 5 +- .../springblade/system/mapper/UserMapper.xml | 2 +- .../service/impl/CurrencyServiceImpl.java | 2 + .../system/service/impl/UserServiceImpl.java | 3 + .../transport/excel/AccidentRecordExcel.java | 5 +- .../excel/AnnualInspectionRecordExcel.java | 9 +- .../transport/excel/EquipmentLedgerExcel.java | 4 +- .../transport/excel/InsuranceRecordExcel.java | 13 +- .../MaintenancePlanDateTimeConverter.java | 26 +-- .../excel/OtherExpenseRecordExcel.java | 5 +- .../TireReplacementDateStringConverter.java | 4 + .../transport/excel/WaybillExcel.java | 16 +- .../impl/AccidentRecordServiceImpl.java | 1 + .../AnnualInspectionRecordServiceImpl.java | 2 + .../impl/EquipmentLedgerServiceImpl.java | 1 + .../impl/InsuranceRecordServiceImpl.java | 3 + .../impl/OtherExpenseRecordServiceImpl.java | 1 + .../TireReplacementRecordServiceImpl.java | 9 +- .../impl/TransportPlanServiceImpl.java | 9 +- .../TransportReconciliationServiceImpl.java | 13 +- .../impl/WaybillImportBatchServiceImpl.java | 20 +- .../service/impl/WaybillServiceImpl.java | 4 + 27 files changed, 444 insertions(+), 81 deletions(-) create mode 100644 blade-common/src/main/java/org/springblade/common/excel/LenientDateParser.java create mode 100644 blade-common/src/main/java/org/springblade/common/excel/LenientDateStringConverter.java create mode 100644 blade-common/src/main/java/org/springblade/common/excel/LenientDateTimeStringConverter.java diff --git a/blade-common/pom.xml b/blade-common/pom.xml index 5fb0c22..0879b91 100644 --- a/blade-common/pom.xml +++ b/blade-common/pom.xml @@ -22,6 +22,10 @@ org.springblade blade-starter-loadbalancer + + org.springblade + blade-starter-log + org.springblade blade-core-auto diff --git a/blade-common/src/main/java/org/springblade/common/excel/LenientDateParser.java b/blade-common/src/main/java/org/springblade/common/excel/LenientDateParser.java new file mode 100644 index 0000000..750c101 --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/excel/LenientDateParser.java @@ -0,0 +1,198 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * 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)。 + *

+ * 解析规则: + *

    + *
  • 分隔符 {@code -}、{@code /}、{@code .} 均接受;补零与否均可(2026-08-02 ≡ 2026-8-2)
  • + *
  • 日期时间:日期部分同上,时间 HH:mm:ss,可省略秒或秒+分(2026-8-2 12:3 可解析)
  • + *
  • 拒绝:两位年份(26-8-2)、日在前(2/8/2026)、无分隔符(20260802)
  • + *
+ * + * @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; + } + +} diff --git a/blade-common/src/main/java/org/springblade/common/excel/LenientDateStringConverter.java b/blade-common/src/main/java/org/springblade/common/excel/LenientDateStringConverter.java new file mode 100644 index 0000000..970dde7 --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/excel/LenientDateStringConverter.java @@ -0,0 +1,75 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * 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 文本,文本原样透传。 + *

+ * 文本的宽容解析由服务层调用 {@link LenientDateParser} 完成,不在此处抛错—— + * 转换器阶段抛出的异常会被 fastexcel 包装成 ExcelDataConvertException 直接中断读取, + * 无法进入导入失败明细流程。口径见根工作区 docs/import-spec.md。 + * + * @author Chill + */ +public class LenientDateStringConverter implements Converter { + + @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); + } + +} diff --git a/blade-common/src/main/java/org/springblade/common/excel/LenientDateTimeStringConverter.java b/blade-common/src/main/java/org/springblade/common/excel/LenientDateTimeStringConverter.java new file mode 100644 index 0000000..bcb070a --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/excel/LenientDateTimeStringConverter.java @@ -0,0 +1,82 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * 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} 文本,文本原样透传。 + *

+ * 文本的宽容解析由服务层调用 {@link LenientDateParser} 完成,不在此处抛错—— + * 转换器阶段抛出的异常会被 fastexcel 包装成 ExcelDataConvertException 直接中断读取, + * 无法进入导入失败明细流程。口径见根工作区 docs/import-spec.md。 + * + * @author Chill + */ +public class LenientDateTimeStringConverter implements Converter { + + /** + * 数值日期序列号转文本的输出格式,与导入模板展示格式保持一致。 + */ + 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); + } + +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java b/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java index 941b7eb..b59573d 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java @@ -35,7 +35,6 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; -import java.time.LocalDate; /** * 币种汇率 Excel @@ -62,8 +61,8 @@ public class CurrencyExcel implements Serializable { @ExcelProperty("汇率") private BigDecimal exchangeRate; - @ExcelProperty("生效日期") - private LocalDate effectiveDate; + @ExcelProperty(value = "生效日期", converter = org.springblade.common.excel.LenientDateStringConverter.class) + private String effectiveDate; @ExcelProperty("状态") private String statusName; @@ -71,8 +70,8 @@ public class CurrencyExcel implements Serializable { @ExcelProperty("来源") private String dataSource; - @ExcelProperty("失效日期") - private LocalDate expiryDate; + @ExcelProperty(value = "失效日期", converter = org.springblade.common.excel.LenientDateStringConverter.class) + private String expiryDate; @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/UserExcel.java b/blade-service/blade-system/src/main/java/org/springblade/system/excel/UserExcel.java index 1dd71ac..306e144 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/excel/UserExcel.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/excel/UserExcel.java @@ -34,7 +34,6 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.util.Date; /** * UserExcel @@ -102,7 +101,7 @@ public class UserExcel implements Serializable { private String postName; @ColumnWidth(20) - @ExcelProperty("生日") - private Date birthday; + @ExcelProperty(value = "生日", converter = org.springblade.common.excel.LenientDateStringConverter.class) + private String birthday; } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserMapper.xml b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserMapper.xml index 6a4dd47..70dd2e9 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserMapper.xml +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserMapper.xml @@ -95,7 +95,7 @@