| | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.github.pagehelper.Page; |
| | | import com.ruoyi.common.annotation.Excel; |
| | | import com.ruoyi.common.config.RuoYiConfig; |
| | | import com.ruoyi.common.constant.Constants; |
| | | import com.ruoyi.common.core.page.PageDomain; |
| | | import com.ruoyi.common.core.redis.RedisCache; |
| | | import com.ruoyi.common.utils.PageUtils; |
| | | import com.ruoyi.common.utils.file.DownloadExportUtil; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import org.apache.poi.ss.usermodel.*; |
| | | import org.apache.poi.xssf.streaming.SXSSFWorkbook; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.OutputStream; |
| | | import java.lang.reflect.Field; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | import java.util.List; |
| | | import java.util.function.Function; |
| | | |
| | |
| | | return fileName; |
| | | } |
| | | |
| | | /** |
| | | * 导出两个不同sheet的数据到同一个Excel文件 |
| | | * |
| | | * @param clazz1 第一个sheet对应的实体类 |
| | | * @param clazz2 第二个sheet对应的实体类 |
| | | * @param fileName 文件名 |
| | | * @param sheetName1 第一个sheet名称 |
| | | * @param sheetName2 第二个sheet名称 |
| | | * @param dataList1 第一个sheet的数据列表 |
| | | * @param dataList2 第二个sheet的数据列表 |
| | | * @return 导出后的文件名 |
| | | */ |
| | | protected <T1, T2> String exportMultiSheetData(Class<T1> clazz1, Class<T2> clazz2, |
| | | String fileName, String sheetName1, String sheetName2, |
| | | List<T1> dataList1, List<T2> dataList2) { |
| | | |
| | | // 创建第一个sheet的ExcelUtil并导出数据 |
| | | ExcelUtil<T1> excelUtil1 = new ExcelUtil<>(clazz1); |
| | | excelUtil1.initialize(sheetName1, null, Excel.Type.EXPORT); |
| | | |
| | | if (dataList1 != null && !dataList1.isEmpty()) { |
| | | excelUtil1.exportExcel(dataList1); |
| | | } |
| | | |
| | | // 使用ExcelUtil的finishExport方法导出到临时文件 |
| | | String tempFileName = "temp_" + System.currentTimeMillis() + ".xlsx"; |
| | | excelUtil1.finishExport(tempFileName); |
| | | |
| | | try { |
| | | // 读取临时文件并添加第二个sheet |
| | | FileInputStream fis = new FileInputStream(getAbsoluteFile(tempFileName)); |
| | | SXSSFWorkbook workbook = new SXSSFWorkbook(new org.apache.poi.xssf.usermodel.XSSFWorkbook(fis)); |
| | | fis.close(); |
| | | |
| | | // 创建第二个sheet |
| | | Sheet sheet2 = workbook.createSheet(sheetName2); |
| | | |
| | | // 使用ExcelUtil的方式导出第二个sheet的数据 |
| | | if (dataList2 != null && !dataList2.isEmpty()) { |
| | | // 创建第二个sheet的ExcelUtil用于生成表头 |
| | | ExcelUtil<T2> excelUtil2 = new ExcelUtil<>(clazz2); |
| | | excelUtil2.initialize(sheetName2, null, Excel.Type.EXPORT); |
| | | |
| | | // 获取表头样式信息 |
| | | SXSSFWorkbook tempWorkbook = getWorkbookFromExcelUtil(excelUtil2); |
| | | Sheet tempSheet = tempWorkbook.getSheetAt(0); |
| | | Row headerRow = tempSheet.getRow(1); // 表头通常在第二行 |
| | | |
| | | // 复制表头到第二个sheet |
| | | if (headerRow != null) { |
| | | Row newHeaderRow = sheet2.createRow(0); |
| | | copyRow(headerRow, newHeaderRow); |
| | | } |
| | | |
| | | // 导出数据到第二个sheet |
| | | int startRow = 1; |
| | | for (T2 data : dataList2) { |
| | | Row dataRow = sheet2.createRow(startRow++); |
| | | exportDataRow(clazz2, data, dataRow); |
| | | } |
| | | |
| | | tempWorkbook.close(); |
| | | } |
| | | |
| | | // 完成导出 |
| | | finishMultiSheetExport(workbook, fileName); |
| | | |
| | | // 删除临时文件 |
| | | new File(getAbsoluteFile(tempFileName)).delete(); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("Failed to export multi-sheet data: {}", e.getMessage(), e); |
| | | throw new RuntimeException("多sheet导出失败", e); |
| | | } |
| | | |
| | | return fileName; |
| | | } |
| | | |
| | | /** |
| | | * 从ExcelUtil中获取工作簿对象 |
| | | */ |
| | | private <T> SXSSFWorkbook getWorkbookFromExcelUtil(ExcelUtil<T> excelUtil) { |
| | | try { |
| | | Field workbookField = ExcelUtil.class.getDeclaredField("workbook"); |
| | | workbookField.setAccessible(true); |
| | | return (SXSSFWorkbook) workbookField.get(excelUtil); |
| | | } catch (Exception e) { |
| | | logger.error("Failed to get workbook from ExcelUtil: {}", e.getMessage(), e); |
| | | throw new RuntimeException("获取工作簿失败", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将数据导出到指定的sheet |
| | | */ |
| | | private <T> void exportDataToSheet(Class<T> clazz, Sheet sheet, List<T> dataList) { |
| | | try { |
| | | // 创建临时ExcelUtil用于生成表头 |
| | | ExcelUtil<T> tempUtil = new ExcelUtil<>(clazz); |
| | | tempUtil.initialize("temp", null, Excel.Type.EXPORT); |
| | | |
| | | // 获取表头行 |
| | | SXSSFWorkbook tempWorkbook = getWorkbookFromExcelUtil(tempUtil); |
| | | Sheet tempSheet = tempWorkbook.getSheetAt(0); |
| | | Row headerRow = tempSheet.getRow(1); // 表头通常在第二行 |
| | | |
| | | // 复制表头到目标sheet |
| | | if (headerRow != null) { |
| | | Row newHeaderRow = sheet.createRow(0); |
| | | copyRow(headerRow, newHeaderRow); |
| | | } |
| | | |
| | | // 导出数据 |
| | | if (dataList != null && !dataList.isEmpty()) { |
| | | int startRow = 1; // 数据从第二行开始 |
| | | for (T data : dataList) { |
| | | Row dataRow = sheet.createRow(startRow++); |
| | | exportDataRow(clazz, data, dataRow); |
| | | } |
| | | } |
| | | |
| | | tempWorkbook.close(); |
| | | } catch (Exception e) { |
| | | logger.error("Failed to export data to sheet: {}", e.getMessage(), e); |
| | | throw new RuntimeException("导出数据到sheet失败", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 复制行数据 |
| | | */ |
| | | private void copyRow(Row sourceRow, Row targetRow) { |
| | | if (sourceRow == null || targetRow == null) return; |
| | | |
| | | for (int i = 0; i < sourceRow.getLastCellNum(); i++) { |
| | | Cell sourceCell = sourceRow.getCell(i); |
| | | Cell targetCell = targetRow.createCell(i); |
| | | |
| | | if (sourceCell != null) { |
| | | // 复制单元格样式 |
| | | targetCell.setCellStyle(sourceCell.getCellStyle()); |
| | | |
| | | // 复制单元格值 |
| | | switch (sourceCell.getCellType()) { |
| | | case STRING: |
| | | targetCell.setCellValue(sourceCell.getStringCellValue()); |
| | | break; |
| | | case NUMERIC: |
| | | targetCell.setCellValue(sourceCell.getNumericCellValue()); |
| | | break; |
| | | case BOOLEAN: |
| | | targetCell.setCellValue(sourceCell.getBooleanCellValue()); |
| | | break; |
| | | case FORMULA: |
| | | targetCell.setCellFormula(sourceCell.getCellFormula()); |
| | | break; |
| | | default: |
| | | targetCell.setCellValue(""); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 导出数据行 |
| | | */ |
| | | private <T> void exportDataRow(Class<T> clazz, T data, Row row) { |
| | | try { |
| | | ExcelUtil<T> tempUtil = new ExcelUtil<>(clazz); |
| | | tempUtil.initialize("temp", null, Excel.Type.EXPORT); |
| | | |
| | | // 获取字段信息 |
| | | Field[] fields = clazz.getDeclaredFields(); |
| | | int columnIndex = 0; |
| | | |
| | | for (Field field : fields) { |
| | | field.setAccessible(true); |
| | | Excel excel = field.getAnnotation(Excel.class); |
| | | |
| | | if (excel != null && excel.isExport()) { |
| | | Cell cell = row.createCell(columnIndex++); |
| | | Object value = field.get(data); |
| | | |
| | | if (value != null) { |
| | | cell.setCellValue(value.toString()); |
| | | } else { |
| | | cell.setCellValue(""); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | logger.error("Failed to export data row: {}", e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 完成多sheet导出 |
| | | */ |
| | | private void finishMultiSheetExport(SXSSFWorkbook workbook, String fileName) { |
| | | try (OutputStream out = Files.newOutputStream(Paths.get(getAbsoluteFile(fileName)))) { |
| | | workbook.write(out); |
| | | } catch (Exception e) { |
| | | logger.error("Failed to finish multi-sheet export: {}", e.getMessage(), e); |
| | | throw new RuntimeException("多sheet导出失败", e); |
| | | } finally { |
| | | try { |
| | | workbook.close(); |
| | | } catch (Exception e) { |
| | | logger.error("Failed to close workbook: {}", e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取文件的绝对路径 |
| | | */ |
| | | private String getAbsoluteFile(String fileName) { |
| | | String downloadPath = RuoYiConfig.getDownloadPath(); |
| | | File file = new File(downloadPath + fileName); |
| | | if (!file.exists()) { |
| | | file.getParentFile().mkdirs(); |
| | | } |
| | | return file.getAbsolutePath(); |
| | | } |
| | | |
| | | |
| | | } |