diff --git a/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/caseplatformcase/controller/CaseplatformCaseExcelController.java b/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/caseplatformcase/controller/CaseplatformCaseExcelController.java index f73f1b5..fab5de7 100644 --- a/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/caseplatformcase/controller/CaseplatformCaseExcelController.java +++ b/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/caseplatformcase/controller/CaseplatformCaseExcelController.java @@ -8,7 +8,7 @@ import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import net.lab1024.sa.admin.module.app.medicalrecord.service.MedicalRecordService; +import net.lab1024.sa.admin.module.business.caseplatformcase.converter.CustomExcelWriteHandler; import net.lab1024.sa.admin.module.business.caseplatformcase.domain.form.CaseplatformCaseQueryForm; import net.lab1024.sa.admin.module.business.caseplatformcase.domain.vo.EasyExcelCaseDetailVO; import net.lab1024.sa.admin.module.business.caseplatformcase.domain.vo.ExportExpertCaseExcelVo; @@ -19,15 +19,14 @@ import net.lab1024.sa.common.module.support.operatelog.annoation.OperateLog; import org.apache.poi.ss.formula.functions.T; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.streaming.SXSSFSheet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import java.io.FileOutputStream; import java.net.URLEncoder; import java.util.*; @@ -107,9 +106,6 @@ public class CaseplatformCaseExcelController { double totalTax = processedList.stream().mapToDouble(e -> Optional.ofNullable(e.getTaxAmount()).orElse(0.0)).sum(); double totalTotal = processedList.stream().mapToDouble(e -> Optional.ofNullable(e.getTotalAmount()).orElse(0.0)).sum(); - String filePath = "./人工肝诊疗专家劳务费.xlsx"; - FileOutputStream outputStream = new FileOutputStream(filePath); - // 字段标题 List> head = Arrays.asList( Collections.singletonList("序号"), @@ -145,11 +141,14 @@ public class CaseplatformCaseExcelController { contentStyle.setBorderLeft(BorderStyle.THIN); contentStyle.setBorderRight(BorderStyle.THIN); -// ExcelWriter writer = EasyExcel.write(outputStream, ExportExpertCaseExcelVo.class) + // 使用 CustomExcelWriteHandler 在正确的时机创建标题行和合计行 + CustomExcelWriteHandler customHandler = new CustomExcelWriteHandler(totalActual, totalTax, totalTotal, processedList.size()); + ExcelWriter writer = EasyExcel.write(response.getOutputStream(), ExportExpertCaseExcelVo.class) .head(head) .excelType(ExcelTypeEnum.XLSX) .registerWriteHandler(new HorizontalCellStyleStrategy(headStyle, contentStyle)) + .registerWriteHandler(customHandler) .build(); WriteSheet writeSheet = EasyExcel.writerSheet("劳务费明细") @@ -160,90 +159,130 @@ public class CaseplatformCaseExcelController { // 写数据 writer.write(processedList, writeSheet); - // 合并大标题 - Workbook workbook = (Workbook) writer.writeContext().writeWorkbookHolder().getWorkbook(); - Sheet sheet = workbook.getSheetAt(0); - sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, head.size() - 1)); - //Row titleRow = sheet.createRow(0); - Row titleRow = sheet.getRow(0); - if (titleRow == null) { - titleRow = sheet.createRow(0); - } - titleRow.setHeightInPoints(30); // 设置标题行高 - Cell titleCell = titleRow.createCell(0); - titleCell.setCellValue("人工肝诊疗病例征集项目专家劳务费表"); - CellStyle titleStyle = workbook.createCellStyle(); - Font font = workbook.createFont(); - font.setFontHeightInPoints((short) 16); - font.setBold(true); - titleStyle.setFont(font); - titleStyle.setAlignment(HorizontalAlignment.CENTER); - titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); - titleStyle.setBorderRight(BorderStyle.THIN); // 最后一格加右边框 - titleCell.setCellStyle(titleStyle); - - // === 设置行高 === - sheet.getRow(1).setHeightInPoints(25); // 表头行 - for (int i = 2; i <= processedList.size() + 2; i++) { - Row row = sheet.getRow(i); - if (row != null) { - row.setHeightInPoints(22); + // 设置标题和合计行(在数据写入后,finish 之前) + try { + Workbook workbook = writer.writeContext().writeWorkbookHolder().getWorkbook(); + Sheet sheet = workbook.getSheetAt(0); + + // 确保标题行存在并设置标题 + Row titleRow = sheet.getRow(0); + if (titleRow == null) { + titleRow = sheet.createRow(0); } - } - - // === 设置列宽 === - int[] columnWidths = { - 6, 25, 15, 15, 25, 10, 10, 30, 8, 8, 30, 25, 30, 15 - }; - for (int i = 0; i < columnWidths.length; i++) { - sheet.setColumnWidth(i, columnWidths[i] * 256); // 乘 256 为 Excel 单位 - } - - // 写合计行 - int totalRowIndex = processedList.size() + 2; // +1是数据从第3行开始,+2是表头行数 - Row totalRow = sheet.createRow(totalRowIndex); - totalRow.setHeightInPoints(22); - - // 合计行样式(黑底白字) - CellStyle totalStyle = workbook.createCellStyle(); - Font totalFont = workbook.createFont(); - totalFont.setBold(true); - totalFont.setColor(IndexedColors.WHITE.getIndex()); - totalStyle.setFont(totalFont); - totalStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); - totalStyle.setAlignment(HorizontalAlignment.CENTER); - totalStyle.setVerticalAlignment(VerticalAlignment.CENTER); - totalStyle.setBorderTop(BorderStyle.THIN); - totalStyle.setBorderBottom(BorderStyle.THIN); - totalStyle.setBorderLeft(BorderStyle.THIN); - totalStyle.setBorderRight(BorderStyle.THIN); - - // 合并前3列,写“合计” - sheet.addMergedRegion(new CellRangeAddress(totalRowIndex, totalRowIndex, 0, 5)); - Cell cell0 = totalRow.createCell(0); - cell0.setCellValue("合计"); - - // 设置边框样式 - CellStyle borderStyle = workbook.createCellStyle(); - borderStyle.setBorderBottom(BorderStyle.THIN); - borderStyle.setBorderTop(BorderStyle.THIN); - borderStyle.setBorderLeft(BorderStyle.THIN); - borderStyle.setBorderRight(BorderStyle.THIN); - borderStyle.setAlignment(HorizontalAlignment.CENTER); - - // 创建合计单元格并添加边框 - for (int i = 0; i < head.size(); i++) { - Cell cell = totalRow.getCell(i); - if (cell == null) { - cell = totalRow.createCell(i); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.getCell(0); + if (titleCell == null) { + titleCell = titleRow.createCell(0); } - cell.setCellStyle(borderStyle); - } + titleCell.setCellValue("人工肝诊疗病例征集项目专家劳务费表"); + + // 合并标题单元格 + sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, head.size() - 1)); + + // 设置标题样式 + CellStyle titleStyle = workbook.createCellStyle(); + Font titleFont = workbook.createFont(); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + titleStyle.setFont(titleFont); + titleStyle.setAlignment(HorizontalAlignment.CENTER); + titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + titleStyle.setBorderBottom(BorderStyle.THIN); + titleStyle.setBorderTop(BorderStyle.THIN); + titleStyle.setBorderLeft(BorderStyle.THIN); + titleStyle.setBorderRight(BorderStyle.THIN); + titleCell.setCellStyle(titleStyle); + + int totalRowIndex = 2 + processedList.size(); // 0=大标题,1=表头,2~1+size=数据,2+size=合计 + + Row totalRow = null; + // 如果是 SXSSFSheet,需要特殊处理 + if (sheet instanceof SXSSFSheet) { + SXSSFSheet sxssfSheet = (SXSSFSheet) sheet; + // 先刷新所有已写入的行到磁盘 + sxssfSheet.flushRows(); + // 然后尝试创建新行 + try { + totalRow = sxssfSheet.createRow(totalRowIndex); + } catch (IllegalArgumentException e) { + // 如果行已经存在,尝试获取它 + totalRow = sxssfSheet.getRow(totalRowIndex); + if (totalRow == null) { + // 如果获取失败,尝试使用随机访问窗口 + sxssfSheet.setRandomAccessWindowSize(-1); // 禁用随机访问窗口限制 + totalRow = sxssfSheet.createRow(totalRowIndex); + } + } + } else { + // 普通 Sheet,直接创建或获取 + totalRow = sheet.getRow(totalRowIndex); + if (totalRow == null) { + totalRow = sheet.createRow(totalRowIndex); + } + } + + if (totalRow == null) { + throw new RuntimeException("无法创建合计行"); + } + + totalRow.setHeightInPoints(22); - // 实发(列 G = index 6)、个税(I = 8)、应发(J = 9) - totalRow.getCell(6).setCellValue(totalActual); - totalRow.getCell(8).setCellValue(totalTax); - totalRow.getCell(9).setCellValue(totalTotal); + // 合计行样式(黑底白字) + CellStyle totalStyle = workbook.createCellStyle(); + Font totalFont = workbook.createFont(); + totalFont.setBold(true); + totalFont.setColor(IndexedColors.BLACK.getIndex()); + totalStyle.setFont(totalFont); + totalStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex()); + totalStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + totalStyle.setAlignment(HorizontalAlignment.CENTER); + totalStyle.setVerticalAlignment(VerticalAlignment.CENTER); + totalStyle.setBorderTop(BorderStyle.THIN); + totalStyle.setBorderBottom(BorderStyle.THIN); + totalStyle.setBorderLeft(BorderStyle.THIN); + totalStyle.setBorderRight(BorderStyle.THIN); + + // 合并前6列写"合计" + sheet.addMergedRegion(new CellRangeAddress(totalRowIndex, totalRowIndex, 0, 5)); + Cell totalCell = totalRow.createCell(0); + totalCell.setCellValue("合计"); + totalCell.setCellStyle(totalStyle); + + // 实发、个税、应发赋值+样式 + Cell actualCell = totalRow.createCell(6); + actualCell.setCellValue(totalActual); + actualCell.setCellStyle(totalStyle); + + Cell taxCell = totalRow.createCell(8); + taxCell.setCellValue(totalTax); + taxCell.setCellStyle(totalStyle); + + Cell totalAmtCell = totalRow.createCell(9); + totalAmtCell.setCellValue(totalTotal); + totalAmtCell.setCellStyle(totalStyle); + + // 其他列补充边框 + CellStyle borderStyle = workbook.createCellStyle(); + borderStyle.setBorderBottom(BorderStyle.THIN); + borderStyle.setBorderTop(BorderStyle.THIN); + borderStyle.setBorderLeft(BorderStyle.THIN); + borderStyle.setBorderRight(BorderStyle.THIN); + borderStyle.setAlignment(HorizontalAlignment.CENTER); + + for (int i = 0; i < head.size(); i++) { + if (i == 0 || i == 6 || i == 8 || i == 9) { + continue; + } + Cell cell = totalRow.getCell(i); + if (cell == null) { + cell = totalRow.createCell(i); + } + cell.setCellStyle(borderStyle); + } + } catch (Exception e) { + // 如果创建合计行失败,记录错误但不影响导出 + e.printStackTrace(); + } // 6通知浏览器以附件的形式下载处理,设置返回头要注意文件名有中文 String fileName = URLEncoder.encode("病例数据", "UTF-8").replaceAll("\\+", "%20"); @@ -252,8 +291,6 @@ public class CaseplatformCaseExcelController { response.setCharacterEncoding("utf-8"); writer.finish(); - outputStream.close(); - } catch (Exception e) { e.printStackTrace(); } diff --git a/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/caseplatformcase/converter/CustomExcelWriteHandler.java b/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/caseplatformcase/converter/CustomExcelWriteHandler.java new file mode 100644 index 0000000..6fb53de --- /dev/null +++ b/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/caseplatformcase/converter/CustomExcelWriteHandler.java @@ -0,0 +1,172 @@ +package net.lab1024.sa.admin.module.business.caseplatformcase.converter; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.streaming.SXSSFSheet; + +import java.io.IOException; +import java.util.List; + +public class CustomExcelWriteHandler implements WriteHandler { + private final double totalActual; + private final double totalTax; + private final double totalTotal; + private final int dataSize; + private boolean totalRowCreated = false; + private int processedDataRowCount = 0; // 已处理的数据行数 + // 缓存 Workbook(在 afterSheetCreate 中初始化) + private Workbook workbook; + + public CustomExcelWriteHandler(double totalActual, double totalTax, double totalTotal, int dataSize) { + this.totalActual = totalActual; + this.totalTax = totalTax; + this.totalTotal = totalTotal; + this.dataSize = dataSize; + } + + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + // 初始化 Workbook 缓存(关键:兼容低版本) + this.workbook = writeWorkbookHolder.getWorkbook(); + Sheet sheet = writeSheetHolder.getSheet(); + + // 1. 处理大标题(第0行) + Row titleRow = sheet.createRow(0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellValue("人工肝诊疗病例征集项目专家劳务费表"); + sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 13)); + // 大标题样式 + CellStyle titleStyle = workbook.createCellStyle(); + Font titleFont = workbook.createFont(); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + titleStyle.setFont(titleFont); + titleStyle.setAlignment(HorizontalAlignment.CENTER); + titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + titleStyle.setBorderBottom(BorderStyle.THIN); + titleStyle.setBorderTop(BorderStyle.THIN); + titleStyle.setBorderLeft(BorderStyle.THIN); + titleStyle.setBorderRight(BorderStyle.THIN); + titleCell.setCellStyle(titleStyle); + + // 2. 设置列宽 + int[] columnWidths = {6, 25, 15, 15, 25, 10, 10, 30, 8, 8, 30, 25, 30, 15}; + for (int i = 0; i < columnWidths.length; i++) { + sheet.setColumnWidth(i, columnWidths[i] * 256); + } + + // 3. 预设置表头行(第1行)样式和行高 + Row headRow = sheet.createRow(1); + headRow.setHeightInPoints(25); + } + + // 设置数据行行高,并在最后一行数据写入后创建合计行 + public void afterRowDispose(WriteSheetHolder writeSheetHolder, Row row, Integer relativeRowIndex, Boolean isHead) throws IOException { + // 设置数据行行高(排除表头行) + if (isHead == null || !isHead) { + processedDataRowCount++; + if (row.getRowNum() >= 2 && row.getRowNum() <= 1 + dataSize) { + row.setHeightInPoints(22); + } + + // 如果是最后一行数据,创建合计行 + if (!totalRowCreated && processedDataRowCount == dataSize && dataSize > 0) { + createTotalRow(writeSheetHolder.getSheet()); + } + } + } + + // 创建合计行的辅助方法 + private void createTotalRow(Sheet sheet) throws IOException { + // 修正合计行索引:0=大标题,1=表头,2~1+dataSize=数据,2+dataSize=合计 + int totalRowIndex = 2 + dataSize; + Row totalRow = sheet.createRow(totalRowIndex); + totalRow.setHeightInPoints(22); + + // 合计行样式(黑底白字) + CellStyle totalStyle = workbook.createCellStyle(); + Font totalFont = workbook.createFont(); + totalFont.setBold(true); + totalFont.setColor(IndexedColors.WHITE.getIndex()); + totalStyle.setFont(totalFont); + totalStyle.setFillForegroundColor(IndexedColors.BLACK.getIndex()); + totalStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + totalStyle.setAlignment(HorizontalAlignment.CENTER); + totalStyle.setVerticalAlignment(VerticalAlignment.CENTER); + totalStyle.setBorderTop(BorderStyle.THIN); + totalStyle.setBorderBottom(BorderStyle.THIN); + totalStyle.setBorderLeft(BorderStyle.THIN); + totalStyle.setBorderRight(BorderStyle.THIN); + + // 合并前6列写"合计" + sheet.addMergedRegion(new CellRangeAddress(totalRowIndex, totalRowIndex, 0, 5)); + Cell totalCell = totalRow.createCell(0); + totalCell.setCellValue("合计"); + totalCell.setCellStyle(totalStyle); + + // 实发、个税、应发赋值+样式 + Cell actualCell = totalRow.createCell(6); + actualCell.setCellValue(totalActual); + actualCell.setCellStyle(totalStyle); + + Cell taxCell = totalRow.createCell(8); + taxCell.setCellValue(totalTax); + taxCell.setCellStyle(totalStyle); + + Cell totalAmtCell = totalRow.createCell(9); + totalAmtCell.setCellValue(totalTotal); + totalAmtCell.setCellStyle(totalStyle); + + // 其他列补充边框 + CellStyle borderStyle = workbook.createCellStyle(); + borderStyle.setBorderBottom(BorderStyle.THIN); + borderStyle.setBorderTop(BorderStyle.THIN); + borderStyle.setBorderLeft(BorderStyle.THIN); + borderStyle.setBorderRight(BorderStyle.THIN); + borderStyle.setAlignment(HorizontalAlignment.CENTER); + + for (int i = 0; i < 14; i++) { + if (i == 0 || i == 6 || i == 8 || i == 9) { + continue; + } + Cell cell = totalRow.getCell(i); + if (cell == null) { + cell = totalRow.createCell(i); + } + cell.setCellStyle(borderStyle); + } + + // 标记合计行已创建 + totalRowCreated = true; + + // 强制刷新(兼容SXSSF) + if (sheet instanceof SXSSFSheet) { + ((SXSSFSheet) sheet).flushRows(); + } + } + + // 备用:在所有数据写入完成后创建合计行(如果 afterRowDispose 中没有创建) + public void afterSheetDispose(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) throws IOException { + // 如果合计行还未创建,则创建(备用方案) + if (!totalRowCreated && dataSize > 0) { + createTotalRow(writeSheetHolder.getSheet()); + } + } + + // 其他默认方法(无需修改) + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {} + public void beforeWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder) {} + public void beforeWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) {} + public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) {} + public void afterRowCreate(WriteSheetHolder writeSheetHolder, Row row, Integer relativeRowIndex, Boolean isHead) {} + public void beforeRowCreate(WriteSheetHolder writeSheetHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) {} + public void beforeRowDispose(WriteSheetHolder writeSheetHolder, Row row, Integer relativeRowIndex, Boolean isHead) {} + public void afterCellCreate(WriteSheetHolder writeSheetHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {} + public void afterCellDispose(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {} + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {} +} \ No newline at end of file