diff --git a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportAsnServiceImpl.java b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportAsnServiceImpl.java index f8f981e..83d0def 100644 --- a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportAsnServiceImpl.java +++ b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportAsnServiceImpl.java @@ -1,5 +1,6 @@ package com.youchain.basicdata.service.impl; +import cn.idev.excel.read.metadata.ReadSheet; import com.youchain.basicdata.domain.BillType; import com.youchain.basicdata.domain.Item; import com.youchain.basicdata.repository.*; @@ -21,7 +22,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import java.io.File; import java.sql.Timestamp; import java.util.*; import java.util.stream.Collectors; @@ -52,18 +52,18 @@ public class ImportAsnServiceImpl implements ImportAsnService { // String type = FileUtil.getFileType(suffix); // FileUtil.upload(file, properties.getPath().getPath() + type + File.separator); // 验证模板类型是否正确 - isValidTemplateType(file, templateType); + List readSheets = isValidTemplateType(file, templateType); // 根据模板类型进行不同的处理 switch (templateType) { case "template1": - baseImportTemplate(file); + baseImportTemplate(file, readSheets); break; case "template2": - bigItemTemplate(file); + bigItemTemplate(file, readSheets); break; case "template3": - invoicePackingTemplate(file); + invoicePackingTemplate(file, readSheets); break; default: throw new BadRequestException("不支持的模板类型: " + templateType); @@ -72,26 +72,25 @@ public class ImportAsnServiceImpl implements ImportAsnService { // 处理标准模板 @Transactional - public void baseImportTemplate(MultipartFile file) { + public void baseImportTemplate(MultipartFile file, List readSheets) { // 读取sheet数据 - List dataList = FastExcelUtil.readExcelData(file, BaseImport.class, 0, 1); + List dataList = FastExcelUtil.readExcelData(file, BaseImport.class, readSheets.get(0).getSheetNo(), 1); //批量导入 importAsnData(dataList); } //处理大物模板 - private void bigItemTemplate(MultipartFile file) { + private void bigItemTemplate(MultipartFile file, List readSheets) { // 获取发票号 FastExcelCellListener reader = new FastExcelCellListener() - .addTargetCell(5, 55); // invoiceNo - Map cellValues = FastExcelUtil.readExcelCellValues(file, reader); - String invoiceNo = cellValues.get("5-55"); - + .addTargetCell(6, 55); // invoiceNo + FastExcelUtil.readExcelCellValues(file, reader, readSheets.get(0).getSheetNo()); + String invoiceNo = reader.getCellValue(6, 55); // 读取两个sheet的数据 - List sheet1Data = FastExcelUtil.readExcelData(file, BaseImport.class, 0, 21); - List sheet2Data = FastExcelUtil.readExcelData(file, BaseImport.class, 1, 21); + List sheet1Data = FastExcelUtil.readExcelData(file, BaseImport.class, readSheets.get(0).getSheetNo(), 21); + List sheet2Data = FastExcelUtil.readExcelData(file, BaseImport.class, readSheets.get(1).getSheetNo(), 21); //大物数据处理 List dataList = bigItemDataProcess(invoiceNo, sheet1Data, sheet2Data); @@ -102,21 +101,20 @@ public class ImportAsnServiceImpl implements ImportAsnService { } // 处理发票箱单模板 - private void invoicePackingTemplate(MultipartFile file) { - // 获取发票号 - FastExcelCellListener reader = new FastExcelCellListener().addTargetCell(2, 11); // invoiceNo - Map cellValues = FastExcelUtil.readExcelCellValues(file, reader); - String invoiceNo = cellValues.get("2-11"); + private void invoicePackingTemplate(MultipartFile file, List readSheets) { + // 读取第二个sheet的invoiceNo号 + FastExcelCellListener reader = new FastExcelCellListener().addTargetCell(3, 11); // invoiceNo + FastExcelUtil.readExcelCellValues(file, reader, readSheets.get(1).getSheetNo());// + String invoiceNo = reader.getCellValue(3, 11); // 读取sheet数据 - List sheet1Data = FastExcelUtil.readExcelData(file, InvoicePackingImport.class, 0, 24); + List sheet1Data = FastExcelUtil.readExcelData(file, InvoicePackingImport.class, readSheets.get(0).getSheetNo(), 24); // 读取sheet数据 - List sheet2Data = FastExcelUtil.readExcelData(file, InvoicePackingImport.class, 1, 5); - + List sheet2Data = FastExcelUtil.readExcelData(file, InvoicePackingImport.class, readSheets.get(1).getSheetNo(), 5); //发票箱单数据处理 - List dataList = invoicePackingDataProcess(invoiceNo, sheet1Data,sheet2Data); + List dataList = invoicePackingDataProcess(invoiceNo, sheet1Data, sheet2Data); //批量导入 importAsnData(dataList); @@ -239,41 +237,56 @@ public class ImportAsnServiceImpl implements ImportAsnService { } //验证模板是否使用正确 - private void isValidTemplateType(MultipartFile file, String templateType) { + private List isValidTemplateType(MultipartFile file, String templateType) { // 根据模板类型进行不同的处理 if ("template1".equals(templateType)) { + List readSheets = FastExcelUtil.readSheets(file); + if (readSheets.size() != 1) { + throw new BadRequestException("标准导入模板中只能包含1个sheet目录"); + } List requiredColumns = Arrays.asList("INVOICE NO.", "BOI", "PO&LN NO.", "PART NO.", "DESCRIPTION", "Q'TY \n" + "(PCS)", "C/NO."); - List headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 0); + List headers = FastExcelUtil.readHeadContent(file, readSheets.get(0).getSheetNo(), 0); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, headers)) { throw new BadRequestException("标准导入模板不正确,请确认模板信息"); } - } else if ("template2".equals(templateType)){ + return readSheets; + } else if ("template2".equals(templateType)) { + List readSheets = FastExcelUtil.readSheets(file); + if (readSheets.size() != 2) { + throw new BadRequestException("大物导入模板中只能包含2个sheet目录"); + } List requiredColumns = Arrays.asList("BOI", "PO&LN NO.", "PART NO.", "DESCRIPTION", "Q'TY \n" + "(PCS)"); - List sheet1Headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 20); + List sheet1Headers = FastExcelUtil.readHeadContent(file, readSheets.get(0).getSheetNo(), 20); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, sheet1Headers)) { throw new BadRequestException("大物第1个Sheet目录模板不正确,请确认模板信息"); } List requiredColumns2 = Collections.singletonList("C/NO."); - List sheet2Headers = FastExcelUtil.readHeadContent(file, requiredColumns2, 1, 20); + List sheet2Headers = FastExcelUtil.readHeadContent(file, readSheets.get(1).getSheetNo(), 20); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns2, sheet2Headers)) { throw new BadRequestException("大物第2个Sheet目录模板不正确,请确认模板信息"); } - + return readSheets; } else if ("template3".equals(templateType)) { + List readSheets = FastExcelUtil.readSheets(file); + if (readSheets.size() != 2) { + throw new BadRequestException("发票箱单模板中只能包含2个sheet目录"); + } List requiredColumns = Collections.singletonList("(BOI)"); - List headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 23); + List headers = FastExcelUtil.readHeadContent(file, readSheets.get(0).getSheetNo(), 23); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, headers)) { throw new BadRequestException("发票箱单第1个Sheet目录模板不正确,请确认模板信息"); } List requiredColumns2 = Arrays.asList("A-PROS\n" + "CASE NO", "KMT NO. PO&LN NO.", "PART NO.", "DESCRIPTION", "Q'TY"); - List headers2 = FastExcelUtil.readHeadContent(file, requiredColumns2, 1, 4); + List headers2 = FastExcelUtil.readHeadContent(file, readSheets.get(1).getSheetNo(), 4); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns2, headers2)) { throw new BadRequestException("发票箱单第2个Sheet目录模板不正确,请确认模板信息"); } - + return readSheets; + } else { + throw new BadRequestException("模板类型错误"); } } diff --git a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportBomAccountServiceImpl.java b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportBomAccountServiceImpl.java index e23e6aa..bd6bdb1 100644 --- a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportBomAccountServiceImpl.java +++ b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportBomAccountServiceImpl.java @@ -322,7 +322,7 @@ public class ImportBomAccountServiceImpl implements ImportBomAccountService { "组顺/刻印号前2位", "台用量" ); - List headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 0); + List headers = FastExcelUtil.readHeadContent(file, 0, 0); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, headers)) { throw new BadRequestException("标准导入模板不正确,请确认模板信息"); } diff --git a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportItemServiceImpl.java b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportItemServiceImpl.java index bdc390d..e7c1f27 100644 --- a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportItemServiceImpl.java +++ b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportItemServiceImpl.java @@ -111,7 +111,7 @@ public class ImportItemServiceImpl implements ImportItemService { "高", "保管期限" ); - List headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 0); + List headers = FastExcelUtil.readHeadContent(file, 0, 0); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, headers)) { throw new BadRequestException("标准导入模板不正确,请确认模板信息"); } diff --git a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportPointServiceImpl.java b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportPointServiceImpl.java index 1be15f3..f1d017f 100644 --- a/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportPointServiceImpl.java +++ b/youchain-system/src/main/java/com/youchain/basicdata/service/impl/ImportPointServiceImpl.java @@ -127,7 +127,7 @@ public class ImportPointServiceImpl implements ImportPointService { "纳所", "标签类型" ); - List headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 0); + List headers = FastExcelUtil.readHeadContent(file, 0, 0); if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, headers)) { throw new BadRequestException("标准导入模板不正确,请确认模板信息"); } diff --git a/youchain-system/src/main/java/com/youchain/utils/FastExcelCellListener.java b/youchain-system/src/main/java/com/youchain/utils/FastExcelCellListener.java index 2886b4f..89647a1 100644 --- a/youchain-system/src/main/java/com/youchain/utils/FastExcelCellListener.java +++ b/youchain-system/src/main/java/com/youchain/utils/FastExcelCellListener.java @@ -2,42 +2,45 @@ package com.youchain.utils; import cn.idev.excel.context.AnalysisContext; import cn.idev.excel.read.listener.ReadListener; +import com.youchain.exception.BadRequestException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class FastExcelCellListener implements ReadListener> { - private final Map cellValues = new HashMap<>(); - private final Set targetCells = new HashSet<>(); - private boolean isCellRead = false; + private final Map cellValues = new ConcurrentHashMap<>(); + private final Set targetCells = new HashSet<>(); + private volatile boolean allCellsRead = false; /** * 添加目标单元格 - * @param row 行索引(从0开始) - * @param col 列索引(从0开始) + * + * @param row 行索引(从1开始,Excel格式) + * @param col 列索引(从1开始,Excel格式) * @return 当前监听器实例,支持链式调用 */ public FastExcelCellListener addTargetCell(int row, int col) { - targetCells.add(row + "-" + col); + if (row < 1 || col < 1) { + throw new BadRequestException("行列索引必须从1开始"); + } + targetCells.add(new CellPosition(row, col)); return this; } @Override - public void invoke(Map data, AnalysisContext context) { - if (isCellRead) { + public void invoke(Map rowData, AnalysisContext context) { + // 如果所有目标单元格已读取完毕,直接返回 + if (allCellsRead) { return; } - int currentRowIndex = context.readRowHolder().getRowIndex()-1; + // EasyExcel 的行索引从0开始,转换为Excel格式(从1开始) + int currentRowIndex = context.readRowHolder().getRowIndex() + 1; - for (String targetCell : targetCells) { - String[] parts = targetCell.split("-"); - int targetRow = Integer.parseInt(parts[0]); - int targetCol = Integer.parseInt(parts[1]); - - if (currentRowIndex == targetRow-1) { - String cellValue = data.get(targetCol-1); + // 检查当前行是否包含目标单元格 + for (CellPosition targetCell : targetCells) { + if (currentRowIndex == targetCell.row) { + // 列索引需要减1转换为0-based + String cellValue = rowData.get(targetCell.col - 1); if (cellValue != null) { cellValues.put(targetCell, cellValue); } @@ -46,7 +49,7 @@ public class FastExcelCellListener implements ReadListener> // 检查是否所有目标单元格都已读取 if (cellValues.size() == targetCells.size()) { - isCellRead = true; + allCellsRead = true; } } @@ -57,19 +60,56 @@ public class FastExcelCellListener implements ReadListener> /** * 获取指定单元格的值 - * @param row 行索引(从0开始) - * @param col 列索引(从0开始) - * @return 单元格值 + * + * @param row 行索引(从1开始,Excel格式) + * @param col 列索引(从1开始,Excel格式) + * @return 单元格值,如果不存在返回null */ public String getCellValue(int row, int col) { - return cellValues.get(row + "-" + col); + return cellValues.get(new CellPosition(row, col)); } /** - * 获取所有单元格值 - * @return 所有单元格值的Map + * 获取所有单元格值(字符串格式的key) + * + * @return 所有单元格值的Map,key为"row-col"格式 */ public Map getAllCellValues() { - return new HashMap<>(cellValues); + Map result = new HashMap<>(); + cellValues.forEach((pos, value) -> + result.put(pos.row + "-" + pos.col, value)); + return result; } + + /** + * 单元格位置封装类 + */ + public static class CellPosition { + private final int row; + private final int col; + + public CellPosition(int row, int col) { + this.row = row; + this.col = col; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CellPosition that = (CellPosition) o; + return row == that.row && col == that.col; + } + + @Override + public int hashCode() { + return Objects.hash(row, col); + } + + @Override + public String toString() { + return "(" + row + "," + col + ")"; + } + } + } diff --git a/youchain-system/src/main/java/com/youchain/utils/FastExcelUtil.java b/youchain-system/src/main/java/com/youchain/utils/FastExcelUtil.java index 8c26407..9bb5b81 100644 --- a/youchain-system/src/main/java/com/youchain/utils/FastExcelUtil.java +++ b/youchain-system/src/main/java/com/youchain/utils/FastExcelUtil.java @@ -1,7 +1,9 @@ package com.youchain.utils; +import cn.idev.excel.ExcelReader; import cn.idev.excel.ExcelWriter; import cn.idev.excel.FastExcel; +import cn.idev.excel.read.metadata.ReadSheet; import cn.idev.excel.write.metadata.WriteSheet; import cn.idev.excel.write.metadata.style.WriteCellStyle; import cn.idev.excel.write.metadata.style.WriteFont; @@ -12,13 +14,14 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.MediaTypeFactory; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -170,7 +173,7 @@ public class FastExcelUtil { } - public static List> readExcelData2(MultipartFile file, int headRowNumber) { + public static List> readExcelData2(MultipartFile file, int headRowNumber) { try { return FastExcel.read(file.getInputStream()) .sheet() @@ -188,10 +191,10 @@ public class FastExcelUtil { * @param file Excel文件 * @param reader 指定行和列 */ - public static Map readExcelCellValues(MultipartFile file, FastExcelCellListener reader) { + public static Map readExcelCellValues(MultipartFile file, FastExcelCellListener reader, int sheetNo) { try { FastExcel.read(file.getInputStream(), reader) - .sheet() + .sheet(sheetNo) .doReadSync(); } catch (IOException e) { throw new BadRequestException("数据格式存在问题,无法读取"); @@ -230,13 +233,9 @@ public class FastExcelUtil { /** * 验证 Excel 文件头部是否包含指定的列名 * - * @param requiredColumns 必需的列名列表 * @return 验证结果,true 表示包含所有必需列 */ - public static List readHeadContent(MultipartFile file, List requiredColumns, int sheetNo, int headRowNumber) { - if (requiredColumns.isEmpty()) { - return Collections.emptyList(); - } + public static List readHeadContent(MultipartFile file, int sheetNo, int headRowNumber) { try { //判断头部内容是否包含所有必填列 return readHeadContent(file.getInputStream(), sheetNo, headRowNumber); @@ -245,8 +244,48 @@ public class FastExcelUtil { } } - private static String readHeadContent(File file, int headRowNumber) { - return ""; + public static List readSheets(MultipartFile file) { + try (ExcelReader excelReader = FastExcel.read(file.getInputStream()).build()) { + // 获取所有Sheet + List allSheets = excelReader.excelExecutor().sheetList(); + + // 使用 POI 检测隐的sheet + Set hiddenSheetNames = getHiddenSheetNames(file); + + //过滤隐藏的sheet + return allSheets.stream() + .filter(sheet -> !hiddenSheetNames.contains(sheet.getSheetName())) + .collect(Collectors.toList()); + } catch (IOException e) { + log.error("读取Excel Sheet信息失败", e); + throw new BadRequestException("读取Excel Sheet信息失败"); + } + + } + + /** + * 获取隐藏的Sheet名称 + * + * @param file 文件 + */ + private static Set getHiddenSheetNames(MultipartFile file) { + Set hiddenSheets = new HashSet<>(); + + try (InputStream inputStream = file.getInputStream(); + Workbook workbook = WorkbookFactory.create(inputStream)) { + + int numberOfSheets = workbook.getNumberOfSheets(); + for (int i = 0; i < numberOfSheets; i++) { + if (workbook.isSheetHidden(i) || workbook.isSheetVeryHidden(i)) { + String sheetName = workbook.getSheetName(i); + hiddenSheets.add(sheetName); + } + } + } catch (Exception e) { + throw new BadRequestException("获取隐藏的Sheet名称失败"); + } + + return hiddenSheets; }