package com.ruoyi.common.core.service;
|
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
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;
|
|
/**
|
* 通用服务
|
*/
|
@Service
|
public abstract class BaseService<M extends BaseMapper<T>,T> extends ServiceImpl<M, T> {
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
@Autowired
|
private RedisCache redisCache;
|
/**
|
* 通用异步导出方法
|
*
|
* @param exportKey 导出任务的唯一标识
|
* @param sheetName 工作表名称
|
* @param dataFetcher 数据获取函数,接受分页参数返回数据列表
|
*/
|
@Async
|
public void export(Class c, String exportKey, String sheetName, Function<Integer, List<T>> dataFetcher) {
|
String fileName = ExcelUtil.encodeFileName(sheetName);
|
|
// 设置当前任务为“下载中”状态
|
DownloadExportUtil.deleteDownloadFile(redisCache, exportKey, "0");
|
|
try {
|
// 执行导出并获取文件名
|
fileName = exportData( c,fileName, sheetName, dataFetcher);
|
// 设置下载完成状态
|
DownloadExportUtil.setDownloadFile(redisCache, exportKey, fileName);
|
logger.info("Export completed for key: {}, file: {}", exportKey, fileName);
|
} catch (Exception e) {
|
logger.error("Export failed for key: {}, error: {}", exportKey, e.getMessage(), e);
|
DownloadExportUtil.deleteDownloadFile(redisCache, exportKey, "1"); // 设置失败状态
|
throw e;
|
}
|
}
|
|
/**
|
* 通用分页导出逻辑
|
*
|
* @param fileName 文件名
|
* @param sheetName 工作表名称
|
* @param dataFetcher 数据获取函数
|
* @return 导出后的文件名
|
*/
|
protected String exportData(Class c, String fileName, String sheetName,
|
Function<Integer, List<T>> dataFetcher) {
|
ExcelUtil<T> excelUtil = new ExcelUtil<>(c);
|
excelUtil.initialize(sheetName, null, Excel.Type.EXPORT);
|
|
int pageNum = 1;
|
boolean hasMoreData = true;
|
|
while (hasMoreData) {
|
List<T> pageData = dataFetcher.apply(pageNum);
|
|
if (pageData != null && !pageData.isEmpty()) {
|
// 导出当前页的数据
|
excelUtil.exportExcel(pageData);
|
pageNum++;
|
} else {
|
// 没有数据时退出
|
hasMoreData = false;
|
}
|
|
}
|
|
excelUtil.finishExport(fileName);
|
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();
|
}
|
|
|
}
|