Browse Source

Excel导入报错定位到具体sheet和行列

java8
han\hanst 1 month ago
parent
commit
8629f2be4f
  1. 120
      src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java

120
src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java

@ -345,7 +345,7 @@ public class CoDelServiceImpl implements CoDelService {
}
/**
* 判断单元格值是否有效
* 判断单元格值是否有效通用版本
* 无效值包括空值"0"Excel错误#REF!#N/A#VALUE!
*
* @param cellValue 单元格字符串值
@ -357,27 +357,48 @@ public class CoDelServiceImpl implements CoDelService {
return false;
}
String trimmedValue = cellValue.trim();
// 2. 值为"0"
if ("0".equals(cellValue.trim())) {
if ("0".equals(trimmedValue)) {
return false;
}
// 3. Excel错误值#开头#REF!#N/A#VALUE!#DIV/0!#NAME?#NUM!#NULL!
return !cellValue.trim().startsWith("#");
return !trimmedValue.startsWith("#");
// 其他情况视为有效值
}
/**
* 判断CMC Invoice是否有效
* 无效值包括空值"0"Excel错误长度超过20位
*
* @param cellValue 单元格字符串值
* @return true=有效值false=无效值
*/
private boolean isValidCmcInvoice(String cellValue) {
// 先进行通用验证
if (!isValidCellValue(cellValue)) {
return false;
}
// CMC Invoice特殊验证长度不能超过20位
String trimmedValue = cellValue.trim();
return trimmedValue.length() <= 20;
}
/**
* 解析Excel第一行建立列名到列索引的映射关系
*
* @param headerRow 第一行表头行
* @param sheetName Sheet名称用于错误提示
* @return 列名到列索引的映射Map
*/
private Map<String, Integer> parseExcelHeader(XSSFRow headerRow) {
private Map<String, Integer> parseExcelHeader(XSSFRow headerRow, String sheetName) {
Map<String, Integer> columnIndexMap = new HashMap<>();
if (headerRow == null) {
throw new RuntimeException("Excel表头行不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 表头行不能为空!");
}
// 遍历第一行的所有单元格建立列名到索引的映射
@ -411,7 +432,7 @@ public class CoDelServiceImpl implements CoDelService {
}
if (!missingColumns.isEmpty()) {
throw new RuntimeException("Excel表头缺少必填列: " + String.join(", ", missingColumns));
throw new RuntimeException("Sheet [" + sheetName + "] 表头缺少必填列: " + String.join(", ", missingColumns));
}
return columnIndexMap;
@ -422,31 +443,34 @@ public class CoDelServiceImpl implements CoDelService {
for (int s = 0; s < workbook.getNumberOfSheets(); s++) {
// 读取工作表
XSSFSheet sheet = workbook.getSheetAt(s);
// 剔除空行
deleteEmptyRow(sheet);
// 获取行数
int rows = sheet.getPhysicalNumberOfRows();
String sheetName = sheet.getSheetName();
if (rows < 4) {
continue; // 至少需要前2行表头行第3行和一行数据第4行
}
try {
// 剔除空行
deleteEmptyRow(sheet);
// 获取行数
int rows = sheet.getPhysicalNumberOfRows();
// 获取第三行表头行
XSSFRow headerRow = sheet.getRow(2);
if (headerRow == null) {
log.warn("Sheet {} 的第3行为空,跳过该Sheet", sheet.getSheetName());
continue;
}
if (rows < 4) {
continue; // 至少需要前2行表头行第3行和一行数据第4行
}
// 获取第三行表头行
XSSFRow headerRow = sheet.getRow(2);
if (headerRow == null) {
log.warn("Sheet [{}] 的第3行为空,跳过该Sheet", sheetName);
continue;
}
// 解析表头行第3行获取列索引映射
Map<String, Integer> columnMap = parseExcelHeader(headerRow);
// 解析表头行第3行获取列索引映射
Map<String, Integer> columnMap = parseExcelHeader(headerRow, sheetName);
// 获取Cargo Ready Date列索引
Integer cargoReadyDateIdx = columnMap.get("Cargo Ready Date");
if (cargoReadyDateIdx == null) {
log.warn("Sheet {} 中未找到 'Cargo Ready Date' 列,跳过该Sheet", sheet.getSheetName());
continue;
}
// 获取Cargo Ready Date列索引
Integer cargoReadyDateIdx = columnMap.get("Cargo Ready Date");
if (cargoReadyDateIdx == null) {
log.warn("Sheet [{}] 中未找到 'Cargo Ready Date' 列,跳过该Sheet", sheetName);
continue;
}
// 遍历每一行从第四行开始即索引3
for (int j = 3; j < rows; j++) {
@ -475,37 +499,37 @@ public class CoDelServiceImpl implements CoDelService {
Integer poIdx = columnMap.get("PO#");
if (row.getCell(poIdx) == null) {
throw new RuntimeException("第" + (j+1) + "行的PO#不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [PO#] 列不能为空!");
}
Integer pnIdx = columnMap.get("PN");
if (row.getCell(pnIdx) == null) {
throw new RuntimeException("第" + (j+1) + "行的PN不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [PN] 列不能为空!");
}
Integer qtyIdx = columnMap.get("Qty (pcs)");
if (row.getCell(qtyIdx) == null) {
throw new RuntimeException("第" + (j+1) + "行的Qty不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [Qty (pcs)] 列不能为空!");
}
Integer destinationIdx = columnMap.get("Destination");
if (row.getCell(destinationIdx) == null) {
throw new RuntimeException("第" + (j+1) + "行的Destination不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [Destination] 列不能为空!");
}
Integer shippingModeIdx = columnMap.get("Shipping Mode");
if (row.getCell(shippingModeIdx) == null) {
throw new RuntimeException("第" + (j+1) + "行的Shipping Mode不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [Shipping Mode] 列不能为空!");
}
Integer currencyIdx = columnMap.get("Currency");
if (row.getCell(currencyIdx) == null) {
throw new RuntimeException("第" + (j+1) + "行的Currency不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [Currency] 列不能为空!");
}
Integer tpIdx = columnMap.get("TP");
if (row.getCell(tpIdx) == null) {
throw new RuntimeException("第" + (j+1) + "行的TP不能为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [TP] 列不能为空!");
}
// 创建对象并为对象赋值
@ -520,7 +544,7 @@ public class CoDelServiceImpl implements CoDelService {
String formatted = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
readDate = DateUtils.getDateByParten(formatted, "yyyy-MM-dd");
} catch (Exception e) {
throw new RuntimeException("第" + (j+1) + "行的ReadyDate格式有误!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [Cargo Ready Date] 列格式有误! " + e.getMessage());
}
task.setReadyDate(readDate);
@ -533,19 +557,20 @@ public class CoDelServiceImpl implements CoDelService {
task.setCurrency(getStringCellValue(row, currencyIdx));
task.setTp(getNumericCellValueOrDefault(row, tpIdx));
// 处理CMC Invoice如果CMC Invoice为空/0/Excel错误则取Shipping Number如果两者都为空则报错
// 处理CMC Invoice如果CMC Invoice为空/0/Excel错误/超过20位则取Shipping Number如果两者都为空则报错
String cmcInvoiceValue = getStringCellValueSafe(row, columnMap, "CMC Invoice");
String shippingNumberValue = getStringCellValueSafe(row, columnMap, "Shipping Number");
// 判断CMC Invoice是否为有效值排除空值"0"Excel错误
boolean isCmcInvoiceValid = isValidCellValue(cmcInvoiceValue);
// 判断CMC Invoice是否为有效值排除空值"0"Excel错误长度超过20位
boolean isCmcInvoiceValid = isValidCmcInvoice(cmcInvoiceValue);
// 判断Shipping Number是否为有效值排除空值"0"Excel错误允许超过20位
boolean isShippingNumberValid = isValidCellValue(shippingNumberValue);
if (!isCmcInvoiceValid && !isShippingNumberValid) {
throw new RuntimeException("第" + (j+1) + "行的CMC Invoice和Shipping Number不能同时为空!");
throw new RuntimeException("Sheet [" + sheetName + "] 第" + (j+1) + "行的 [CMC Invoice] [Shipping Number] 列不能同时为空!");
}
// 如果CMC Invoice无效/0/错误则使用Shipping Number
// 如果CMC Invoice无效/0/错误/超过20位则使用Shipping Number
if (!isCmcInvoiceValid) {
task.setCmcInvoice(shippingNumberValue);
} else {
@ -601,6 +626,18 @@ public class CoDelServiceImpl implements CoDelService {
// 物料存在性检查已移至批量处理阶段此处不再单独检查
excelList.add(task);
}
} catch (Exception e) {
// 捕获并重新抛出异常确保包含Sheet信息
String errorMsg = e.getMessage();
if (errorMsg != null && errorMsg.contains("Sheet [")) {
// 异常消息已包含Sheet信息直接抛出
throw e;
} else {
// 异常消息不包含Sheet信息添加Sheet信息后抛出
throw new RuntimeException("Sheet [" + sheetName + "] 处理失败: " + errorMsg, e);
}
}
}
}
@ -825,6 +862,9 @@ public class CoDelServiceImpl implements CoDelService {
return value.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数
case STRING:
try {
if (cell.getStringCellValue() == null || cell.getStringCellValue().isEmpty()) {
return null;
}
BigDecimal stringValue = new BigDecimal(cell.getStringCellValue());
return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数
} catch (NumberFormatException e) {

Loading…
Cancel
Save