|
|
|
@ -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) { |
|
|
|
|