wujianwei
2026-01-07 3135ac10ab738063bb5df39b3c5cc0f143d11061
common/src/main/java/com/ruoyi/common/core/service/BaseService.java
@@ -4,18 +4,27 @@
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;
@@ -89,6 +98,230 @@
        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();
    }
}
}