fix(excel): 解决合并单元格数据导入异常问题

- 引入 StringUtils 处理各种空白字符和占位符
- 修改数量和件数字段从 BigDecimal/Integer 改为 String 类型读取
- 在 Service 层添加 parseDecimal 和 parseInteger 方法处理数值转换
- 优化异常处理逻辑,支持合并单元格中的点号、横线等占位符
- 更新注释说明合并单元格的数据处理方式变更
This commit is contained in:
2026-06-27 15:36:45 +08:00
parent e326d8df0b
commit 2802972df2
3 changed files with 67 additions and 9 deletions

View File

@@ -10,7 +10,9 @@ import java.math.BigDecimal;
* 采购计划到货导入模板(对应到货 Excel一行一卷 * 采购计划到货导入模板(对应到货 Excel一行一卷
* *
* 列:日期 牌号 规格 卷号 单卷重量 车号 数量 件数 销售 钢厂到站 * 列:日期 牌号 规格 卷号 单卷重量 车号 数量 件数 销售 钢厂到站
* 注:车号/数量/件数 为合并单元格,仅首行有值,导入时向下填充。 * 注:车号/数量/件数 为合并单元格,仅首行有值
* 数量/件数 使用 String 类型读取以避开 EasyExcel 对合并空白格的数值转换异常,
* 在 Service 层统一 parse 并校验。
* *
* @author klp * @author klp
* @date 2026-06-22 * @date 2026-06-22
@@ -37,11 +39,13 @@ public class ErpPurchasePlanDeliveryImportVo {
@ExcelProperty("车号") @ExcelProperty("车号")
private String truckNo; private String truckNo;
/** 整车总重(合并单元格,仅首行有值;读为 String 以兼容空白/点号等占位符) */
@ExcelProperty("数量") @ExcelProperty("数量")
private BigDecimal truckWeight; private String truckWeight;
/** 整车件数(合并单元格,仅首行有值;读为 String 以兼容空白/点号等占位符) */
@ExcelProperty("件数") @ExcelProperty("件数")
private Integer pieceCount; private String pieceCount;
@ExcelProperty("销售") @ExcelProperty("销售")
private String salesCode; private String salesCode;

View File

@@ -5,6 +5,7 @@ import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.excel.exception.ExcelDataConvertException;
import com.klp.common.exception.ServiceException; import com.klp.common.exception.ServiceException;
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryImportVo; import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryImportVo;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -58,8 +59,8 @@ public class ErpDeliveryExcelListener extends AnalysisEventListener<ErpPurchaseP
} }
if (exception instanceof ExcelDataConvertException) { if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException e = (ExcelDataConvertException) exception; ExcelDataConvertException e = (ExcelDataConvertException) exception;
// 合并单元格(车号/数量/件数 多行共用)会在被合并的下方行留下空数值格, // 合并单元格会在被合并的下方行留下空白/点号等占位符;
// 转换为数字会抛此异常——空格直接忽略(交由 Service 向下填充),只有真有非数字内容才报错。 // 数量/件数已改为 String 读取,此处仅处理单卷重量等仍为数值型的列
if (isBlankCell(e)) { if (isBlankCell(e)) {
return; return;
} }
@@ -72,7 +73,7 @@ public class ErpDeliveryExcelListener extends AnalysisEventListener<ErpPurchaseP
// 转换类异常不抛出,继续解析以收集所有错误行 // 转换类异常不抛出,继续解析以收集所有错误行
} }
/** 判断转换失败的单元格是否本就是空(合并单元格留下的空格),空则忽略不报错 */ /** 判断转换失败的单元格是否空(合并单元格残留的空白/占位符) */
private boolean isBlankCell(ExcelDataConvertException e) { private boolean isBlankCell(ExcelDataConvertException e) {
try { try {
com.alibaba.excel.metadata.data.CellData<?> cd = e.getCellData(); com.alibaba.excel.metadata.data.CellData<?> cd = e.getCellData();
@@ -83,7 +84,18 @@ public class ErpDeliveryExcelListener extends AnalysisEventListener<ErpPurchaseP
return true; return true;
} }
String s = cd.getStringValue(); String s = cd.getStringValue();
return s == null || s.trim().isEmpty(); // StringUtils.isBlank 覆盖全角空格、不间断空格等 Unicode 空白
// 额外忽略 "." 等 Excel 合并单元格常见占位符
if (StringUtils.isBlank(s)) {
return true;
}
if (s != null) {
String t = s.trim();
if (t.isEmpty() || ".".equals(t) || "".equals(t) || "/".equals(t) || "-".equals(t)) {
return true;
}
}
return false;
} catch (Throwable t) { } catch (Throwable t) {
return false; return false;
} }

View File

@@ -416,8 +416,9 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
// 车号:每卷归属的车(向下填充) // 车号:每卷归属的车(向下填充)
d.setTruckNo(StringUtils.isNotBlank(row.getTruckNo()) ? row.getTruckNo() : lastTruckNo); d.setTruckNo(StringUtils.isNotBlank(row.getTruckNo()) ? row.getTruckNo() : lastTruckNo);
// 整车总重 / 件数:整车合计,仅该车首行有值,不向下填充 // 整车总重 / 件数:整车合计,仅该车首行有值,不向下填充
d.setTruckWeight(convertWeight(row.getTruckWeight(), kgConverted)); // 数量/件数 作为 String 读取以兼容合并单元格的空白/点号等占位符,在此 parse
d.setPieceCount(row.getPieceCount()); d.setTruckWeight(convertWeight(parseDecimal(row.getTruckWeight()), kgConverted));
d.setPieceCount(parseInteger(row.getPieceCount()));
d.setSalesCode(row.getSalesCode()); d.setSalesCode(row.getSalesCode());
d.setArrivalStation(row.getArrivalStation()); d.setArrivalStation(row.getArrivalStation());
deliveryMapper.insert(d); deliveryMapper.insert(d);
@@ -458,6 +459,47 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
return kgConverted ? v.divide(KG_TO_T, 3, RoundingMode.HALF_UP) : v; return kgConverted ? v.divide(KG_TO_T, 3, RoundingMode.HALF_UP) : v;
} }
/**
* 将数量列 String 转为 BigDecimal兼容合并单元格中的空白、点号、横线等占位符。
* 无法解析时返回 null由 Service 按合并单元格语义(仅首行有值)处理。
*/
private static BigDecimal parseDecimal(String s) {
if (StringUtils.isBlank(s)) {
return null;
}
String t = s.trim();
// 合并单元格常见占位符:半角/全角点、横线、斜线
if (".".equals(t) || "".equals(t) || "-".equals(t) || "--".equals(t)
|| "/".equals(t) || "\\".equals(t) || "".equals(t)) {
return null;
}
try {
return new BigDecimal(t);
} catch (NumberFormatException e) {
return null;
}
}
/**
* 将件数列 String 转为 Integer兼容合并单元格中的空白、点号、横线等占位符。
* 无法解析时返回 null。
*/
private static Integer parseInteger(String s) {
if (StringUtils.isBlank(s)) {
return null;
}
String t = s.trim();
if (".".equals(t) || "".equals(t) || "-".equals(t) || "--".equals(t)
|| "/".equals(t) || "\\".equals(t) || "".equals(t)) {
return null;
}
try {
return new BigDecimal(t).intValue();
} catch (NumberFormatException e) {
return null;
}
}
@Override @Override
public List<ErpPurchasePlanDeliveryVo> queryDeliveryList(Long planId) { public List<ErpPurchasePlanDeliveryVo> queryDeliveryList(Long planId) {
return deliveryMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchasePlanDelivery.class) return deliveryMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchasePlanDelivery.class)