|
|
@ -344,6 +344,30 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
return getNumericCellValueOrDefault(row, columnIndex); |
|
|
return getNumericCellValueOrDefault(row, columnIndex); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 判断单元格值是否有效 |
|
|
|
|
|
* 无效值包括:空值、"0"、Excel错误(#REF!、#N/A、#VALUE!等) |
|
|
|
|
|
* |
|
|
|
|
|
* @param cellValue 单元格字符串值 |
|
|
|
|
|
* @return true=有效值,false=无效值 |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean isValidCellValue(String cellValue) { |
|
|
|
|
|
// 1. 空值或空字符串 |
|
|
|
|
|
if (StringUtils.isBlank(cellValue)) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 值为"0" |
|
|
|
|
|
if ("0".equals(cellValue.trim())) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 3. Excel错误值(以#开头,如#REF!、#N/A、#VALUE!、#DIV/0!、#NAME?、#NUM!、#NULL!等) |
|
|
|
|
|
return !cellValue.trim().startsWith("#"); |
|
|
|
|
|
|
|
|
|
|
|
// 其他情况视为有效值 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 解析Excel第一行,建立列名到列索引的映射关系 |
|
|
* 解析Excel第一行,建立列名到列索引的映射关系 |
|
|
* |
|
|
* |
|
|
@ -409,18 +433,21 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
|
|
|
|
|
|
// 获取第三行(表头行) |
|
|
// 获取第三行(表头行) |
|
|
XSSFRow headerRow = sheet.getRow(2); |
|
|
XSSFRow headerRow = sheet.getRow(2); |
|
|
|
|
|
|
|
|
// 验证第一列第3行必须是"PO Rec. Date"且不能为空 |
|
|
|
|
|
if (headerRow == null || headerRow.getCell(0) == null |
|
|
|
|
|
|| StringUtils.isBlank(getStringCellValue(headerRow, 0)) |
|
|
|
|
|
|| !getStringCellValue(headerRow, 0).trim().equals("PO Rec. Date")) { |
|
|
|
|
|
log.warn("Sheet {} 的第一列第3行不是 'PO Rec. Date' 或为空,跳过该Sheet", sheet.getSheetName()); |
|
|
|
|
|
continue; // 跳过该Sheet |
|
|
|
|
|
|
|
|
if (headerRow == null) { |
|
|
|
|
|
log.warn("Sheet {} 的第3行为空,跳过该Sheet", sheet.getSheetName()); |
|
|
|
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 解析表头行(第3行),获取列索引映射 |
|
|
// 解析表头行(第3行),获取列索引映射 |
|
|
Map<String, Integer> columnMap = parseExcelHeader(headerRow); |
|
|
Map<String, Integer> columnMap = parseExcelHeader(headerRow); |
|
|
|
|
|
|
|
|
|
|
|
// 获取Cargo Ready Date列索引 |
|
|
|
|
|
Integer cargoReadyDateIdx = columnMap.get("Cargo Ready Date"); |
|
|
|
|
|
if (cargoReadyDateIdx == null) { |
|
|
|
|
|
log.warn("Sheet {} 中未找到 'Cargo Ready Date' 列,跳过该Sheet", sheet.getSheetName()); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 遍历每一行(从第四行开始,即索引3) |
|
|
// 遍历每一行(从第四行开始,即索引3) |
|
|
for (int j = 3; j < rows; j++) { |
|
|
for (int j = 3; j < rows; j++) { |
|
|
// 获得该行 |
|
|
// 获得该行 |
|
|
@ -429,10 +456,11 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 检查第一列,如果没有值或者值是空字符串则跳过该行 |
|
|
|
|
|
if (row.getCell(0).getCellType() == CellType.BLANK || row.getCell(0).getCellType() == CellType.ERROR |
|
|
|
|
|
|| row.getCell(0) == null || StringUtils.isBlank(getStringCellValue(row, 0))) { |
|
|
|
|
|
log.debug("第{}行第一列为空,跳过该行", j+1); |
|
|
|
|
|
|
|
|
// 检查Cargo Ready Date列,如果没有值则跳过该行 |
|
|
|
|
|
if (row.getCell(cargoReadyDateIdx) == null |
|
|
|
|
|
|| row.getCell(cargoReadyDateIdx).getCellType() == CellType.BLANK |
|
|
|
|
|
|| row.getCell(cargoReadyDateIdx).getCellType() == CellType.ERROR) { |
|
|
|
|
|
log.debug("第{}行的Cargo Ready Date列为空,跳过该行", j+1); |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -443,11 +471,7 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 必填字段验证 |
|
|
|
|
|
Integer cargoReadyDateIdx = columnMap.get("Cargo Ready Date"); |
|
|
|
|
|
if (row.getCell(cargoReadyDateIdx) == null) { |
|
|
|
|
|
throw new RuntimeException("第" + (j+1) + "行的Cargo Ready Date不能为空!"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 必填字段验证 - Cargo Ready Date已经在上面检查过了 |
|
|
|
|
|
|
|
|
Integer poIdx = columnMap.get("PO#"); |
|
|
Integer poIdx = columnMap.get("PO#"); |
|
|
if (row.getCell(poIdx) == null) { |
|
|
if (row.getCell(poIdx) == null) { |
|
|
@ -495,7 +519,7 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
LocalDate localDate = parseDateCell(row.getCell(cargoReadyDateIdx)); |
|
|
LocalDate localDate = parseDateCell(row.getCell(cargoReadyDateIdx)); |
|
|
String formatted = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
|
|
String formatted = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
|
|
readDate = DateUtils.getDateByParten(formatted, "yyyy-MM-dd"); |
|
|
readDate = DateUtils.getDateByParten(formatted, "yyyy-MM-dd"); |
|
|
} catch (ParseException e) { |
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
throw new RuntimeException("第" + (j+1) + "行的ReadyDate格式有误!"); |
|
|
throw new RuntimeException("第" + (j+1) + "行的ReadyDate格式有误!"); |
|
|
} |
|
|
} |
|
|
task.setReadyDate(readDate); |
|
|
task.setReadyDate(readDate); |
|
|
@ -509,16 +533,20 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
task.setCurrency(getStringCellValue(row, currencyIdx)); |
|
|
task.setCurrency(getStringCellValue(row, currencyIdx)); |
|
|
task.setTp(getNumericCellValueOrDefault(row, tpIdx)); |
|
|
task.setTp(getNumericCellValueOrDefault(row, tpIdx)); |
|
|
|
|
|
|
|
|
// 处理CMC Invoice:如果CMC Invoice为空则取Shipping Number,如果两者都为空则报错 |
|
|
|
|
|
|
|
|
// 处理CMC Invoice:如果CMC Invoice为空/0/Excel错误则取Shipping Number,如果两者都为空则报错 |
|
|
String cmcInvoiceValue = getStringCellValueSafe(row, columnMap, "CMC Invoice"); |
|
|
String cmcInvoiceValue = getStringCellValueSafe(row, columnMap, "CMC Invoice"); |
|
|
String shippingNumberValue = getStringCellValueSafe(row, columnMap, "Shipping Number"); |
|
|
String shippingNumberValue = getStringCellValueSafe(row, columnMap, "Shipping Number"); |
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(cmcInvoiceValue) && StringUtils.isBlank(shippingNumberValue)) { |
|
|
|
|
|
|
|
|
// 判断CMC Invoice是否为有效值(排除空值、"0"、Excel错误) |
|
|
|
|
|
boolean isCmcInvoiceValid = isValidCellValue(cmcInvoiceValue); |
|
|
|
|
|
boolean isShippingNumberValid = isValidCellValue(shippingNumberValue); |
|
|
|
|
|
|
|
|
|
|
|
if (!isCmcInvoiceValid && !isShippingNumberValid) { |
|
|
throw new RuntimeException("第" + (j+1) + "行的CMC Invoice和Shipping Number不能同时为空!"); |
|
|
throw new RuntimeException("第" + (j+1) + "行的CMC Invoice和Shipping Number不能同时为空!"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 如果CMC Invoice为空,则使用Shipping Number |
|
|
|
|
|
if (StringUtils.isBlank(cmcInvoiceValue)) { |
|
|
|
|
|
|
|
|
// 如果CMC Invoice无效(空/0/错误),则使用Shipping Number |
|
|
|
|
|
if (!isCmcInvoiceValid) { |
|
|
task.setCmcInvoice(shippingNumberValue); |
|
|
task.setCmcInvoice(shippingNumberValue); |
|
|
} else { |
|
|
} else { |
|
|
task.setCmcInvoice(cmcInvoiceValue); |
|
|
task.setCmcInvoice(cmcInvoiceValue); |
|
|
@ -705,6 +733,41 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
if (cell == null || cell.getCellType() == CellType.BLANK) { |
|
|
if (cell == null || cell.getCellType() == CellType.BLANK) { |
|
|
return ""; |
|
|
return ""; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
CellType cellType = cell.getCellType(); |
|
|
|
|
|
|
|
|
|
|
|
// 处理公式类型 - 获取缓存结果 |
|
|
|
|
|
if (cellType == CellType.FORMULA) { |
|
|
|
|
|
switch (cell.getCachedFormulaResultType()) { |
|
|
|
|
|
case NUMERIC: |
|
|
|
|
|
// 公式结果是数值类型 - 直接获取数值并转换为字符串 |
|
|
|
|
|
// 检查是否是日期格式 |
|
|
|
|
|
if (DateUtil.isCellDateFormatted(cell)) { |
|
|
|
|
|
DataFormatter formatter = new DataFormatter(); |
|
|
|
|
|
return formatter.formatCellValue(cell); |
|
|
|
|
|
} else { |
|
|
|
|
|
// 普通数值,转换为字符串(去除小数点后的零) |
|
|
|
|
|
double numericValue = cell.getNumericCellValue(); |
|
|
|
|
|
// 如果是整数,去掉.0 |
|
|
|
|
|
if (numericValue == Math.floor(numericValue)) { |
|
|
|
|
|
return String.valueOf((long) numericValue); |
|
|
|
|
|
} else { |
|
|
|
|
|
return String.valueOf(numericValue); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
case STRING: |
|
|
|
|
|
// 公式结果是字符串类型 |
|
|
|
|
|
return cell.getStringCellValue(); |
|
|
|
|
|
case BLANK: |
|
|
|
|
|
return ""; |
|
|
|
|
|
case BOOLEAN: |
|
|
|
|
|
return String.valueOf(cell.getBooleanCellValue()); |
|
|
|
|
|
default: |
|
|
|
|
|
return ""; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 处理其他类型使用DataFormatter统一格式化 |
|
|
DataFormatter formatter = new DataFormatter(); |
|
|
DataFormatter formatter = new DataFormatter(); |
|
|
return formatter.formatCellValue(cell); |
|
|
return formatter.formatCellValue(cell); |
|
|
} |
|
|
} |
|
|
@ -714,21 +777,43 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
if (cell == null || cell.getCellType() == CellType.BLANK) { |
|
|
if (cell == null || cell.getCellType() == CellType.BLANK) { |
|
|
return null; |
|
|
return null; |
|
|
} |
|
|
} |
|
|
DataFormatter formatter = new DataFormatter(); |
|
|
|
|
|
String cellValueAsString = formatter.formatCellValue(cell); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CellType cellType = cell.getCellType(); |
|
|
|
|
|
|
|
|
|
|
|
// 处理数值类型 |
|
|
|
|
|
if (cellType == CellType.NUMERIC) { |
|
|
|
|
|
return (int) Math.round(cell.getNumericCellValue()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 处理字符串类型 |
|
|
|
|
|
if (cellType == CellType.STRING) { |
|
|
try { |
|
|
try { |
|
|
// 尝试将字符串转换为整数 |
|
|
|
|
|
return Integer.parseInt(cellValueAsString); |
|
|
|
|
|
|
|
|
return Integer.parseInt(cell.getStringCellValue().trim()); |
|
|
} catch (NumberFormatException e) { |
|
|
} catch (NumberFormatException e) { |
|
|
// 如果转换失败,尝试将数字值四舍五入到最接近的整数 |
|
|
|
|
|
if (cell.getCellType() == CellType.NUMERIC) { |
|
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 处理公式类型 - 获取缓存结果 |
|
|
|
|
|
if (cellType == CellType.FORMULA) { |
|
|
|
|
|
switch (cell.getCachedFormulaResultType()) { |
|
|
|
|
|
case NUMERIC: |
|
|
return (int) Math.round(cell.getNumericCellValue()); |
|
|
return (int) Math.round(cell.getNumericCellValue()); |
|
|
|
|
|
case STRING: |
|
|
|
|
|
try { |
|
|
|
|
|
return Integer.parseInt(cell.getStringCellValue().trim()); |
|
|
|
|
|
} catch (NumberFormatException e) { |
|
|
|
|
|
return -1; |
|
|
} |
|
|
} |
|
|
// 如果单元格既不是数字也不是可解析的字符串,则返回-1 |
|
|
|
|
|
|
|
|
default: |
|
|
return -1; |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 其他类型返回-1 |
|
|
|
|
|
return -1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex) { |
|
|
private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex) { |
|
|
Cell cell = row.getCell(columnIndex); |
|
|
Cell cell = row.getCell(columnIndex); |
|
|
if (cell == null || cell.getCellType() == CellType.BLANK) { |
|
|
if (cell == null || cell.getCellType() == CellType.BLANK) { |
|
|
@ -753,6 +838,9 @@ public class CoDelServiceImpl implements CoDelService { |
|
|
return formulaValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 |
|
|
return formulaValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 |
|
|
case STRING: |
|
|
case STRING: |
|
|
try { |
|
|
try { |
|
|
|
|
|
if (cell.getStringCellValue() == null || cell.getStringCellValue().isEmpty()) { |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
BigDecimal stringValue = new BigDecimal(cell.getStringCellValue()); |
|
|
BigDecimal stringValue = new BigDecimal(cell.getStringCellValue()); |
|
|
return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 |
|
|
return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 |
|
|
} catch (NumberFormatException e) { |
|
|
} catch (NumberFormatException e) { |
|
|
|