feat(P5): 通用数据导出 — Excel/PDF/模板下载

- DataExportController: POST /data-export/expo + /data-export/pdf + GET /data-export/template
- Apache POI Excel导出 + iText PDF导出
- 支持自定义列名+数据+模板下载
- 后端编译通过,前端构建通过
This commit is contained in:
2026-06-06 21:20:55 +08:00
parent 2e2dc6e9d5
commit e4b8335c07
2 changed files with 172 additions and 0 deletions

View File

@@ -17,6 +17,21 @@
<dependencies>
<!-- Data Export: Excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- Data Export: PDF -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>

View File

@@ -0,0 +1,157 @@
package com.healthlink.his.web.system.controller;
import com.core.common.core.domain.R;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* 通用数据导出 Controller — Excel/PDF
*/
@RestController
@RequestMapping("/data-export")
@Slf4j
public class DataExportController {
@SuppressWarnings("unchecked")
@PostMapping("/excel")
public void exportExcel(
@RequestBody Map<String, Object> params,
HttpServletResponse response) throws IOException {
String filename = (String) params.getOrDefault("filename", "export");
List<Map<String, Object>> data = (List<Map<String, Object>>) params.get("data");
List<Map<String, String>> columns = (List<Map<String, String>>) params.get("columns");
if (data == null) data = Collections.emptyList();
if (columns == null) columns = Collections.emptyList();
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet1");
CellStyle headerStyle = workbook.createCellStyle();
org.apache.poi.ss.usermodel.Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Row headerRow = sheet.createRow(0);
for (int i = 0; i < columns.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(columns.get(i).getOrDefault("label", columns.get(i).get("field")));
cell.setCellStyle(headerStyle);
}
for (int r = 0; r < data.size(); r++) {
Row row = sheet.createRow(r + 1);
Map<String, Object> rowData = data.get(r);
for (int c = 0; c < columns.size(); c++) {
Cell cell = row.createCell(c);
String field = columns.get(c).get("field");
Object value = rowData.get(field);
if (value == null) cell.setCellValue("");
else if (value instanceof Number) cell.setCellValue(((Number) value).doubleValue());
else cell.setCellValue(value.toString());
}
}
for (int i = 0; i < columns.size(); i++) sheet.autoSizeColumn(i);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(filename + ".xlsx", StandardCharsets.UTF_8));
try (OutputStream os = response.getOutputStream()) { workbook.write(os); }
workbook.close();
log.info("Excel导出完成: {}, 共{}条", filename, data.size());
}
@SuppressWarnings("unchecked")
@PostMapping("/pdf")
public void exportPdf(
@RequestBody Map<String, Object> params,
HttpServletResponse response) throws IOException {
String filename = (String) params.getOrDefault("filename", "export");
String title = (String) params.getOrDefault("title", "数据导出");
List<Map<String, Object>> data = (List<Map<String, Object>>) params.get("data");
List<Map<String, String>> columns = (List<Map<String, String>>) params.get("columns");
if (data == null) data = Collections.emptyList();
if (columns == null) columns = Collections.emptyList();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(filename + ".pdf", StandardCharsets.UTF_8));
try {
com.itextpdf.text.Document document = new com.itextpdf.text.Document();
com.itextpdf.text.pdf.PdfWriter.getInstance(document, response.getOutputStream());
document.open();
com.itextpdf.text.Font titleFont = com.itextpdf.text.FontFactory.getFont(
com.itextpdf.text.FontFactory.HELVETICA, 18, com.itextpdf.text.Font.BOLD);
document.add(new com.itextpdf.text.Paragraph(title, titleFont));
document.add(new com.itextpdf.text.Paragraph(" "));
if (!columns.isEmpty() && !data.isEmpty()) {
com.itextpdf.text.pdf.PdfPTable table = new com.itextpdf.text.pdf.PdfPTable(columns.size());
table.setWidthPercentage(100);
com.itextpdf.text.Font hdrFont = com.itextpdf.text.FontFactory.getFont(
com.itextpdf.text.FontFactory.HELVETICA, 10, com.itextpdf.text.Font.BOLD);
for (Map<String, String> col : columns) {
com.itextpdf.text.pdf.PdfPCell cell = new com.itextpdf.text.pdf.PdfPCell(
new com.itextpdf.text.Paragraph(col.getOrDefault("label", col.get("field")), hdrFont));
cell.setBackgroundColor(com.itextpdf.text.BaseColor.LIGHT_GRAY);
table.addCell(cell);
}
com.itextpdf.text.Font dataFont = com.itextpdf.text.FontFactory.getFont(
com.itextpdf.text.FontFactory.HELVETICA, 9);
for (Map<String, Object> rowData : data) {
for (Map<String, String> col : columns) {
Object value = rowData.get(col.get("field"));
table.addCell(new com.itextpdf.text.Paragraph(
value != null ? value.toString() : "", dataFont));
}
}
document.add(table);
}
document.add(new com.itextpdf.text.Paragraph(" "));
com.itextpdf.text.Font footerFont = com.itextpdf.text.FontFactory.getFont(
com.itextpdf.text.FontFactory.HELVETICA, 8, com.itextpdf.text.Font.ITALIC);
document.add(new com.itextpdf.text.Paragraph(
"导出时间: " + new Date() + "" + data.size() + "条记录", footerFont));
document.close();
} catch (Exception e) {
log.error("PDF导出失败", e);
throw new IOException("PDF导出失败: " + e.getMessage());
}
log.info("PDF导出完成: {}, 共{}条", filename, data.size());
}
@GetMapping("/template")
public void downloadTemplate(
@RequestParam("filename") String filename,
@RequestParam("columns") String columnsCsv,
HttpServletResponse response) throws IOException {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("模板");
CellStyle headerStyle = workbook.createCellStyle();
org.apache.poi.ss.usermodel.Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
String[] cols = columnsCsv.split(",");
Row headerRow = sheet.createRow(0);
for (int i = 0; i < cols.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(cols[i].trim());
cell.setCellStyle(headerStyle);
sheet.autoSizeColumn(i);
}
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(filename + "_template.xlsx", StandardCharsets.UTF_8));
try (OutputStream os = response.getOutputStream()) { workbook.write(os); }
workbook.close();
}
}