From e4b8335c070eb69ae7d977f7cf63031de0c0320e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8E=E4=BD=97?= Date: Sat, 6 Jun 2026 21:20:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(P5):=20=E9=80=9A=E7=94=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=AF=BC=E5=87=BA=20=E2=80=94=20Excel/PDF/=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DataExportController: POST /data-export/expo + /data-export/pdf + GET /data-export/template - Apache POI Excel导出 + iText PDF导出 - 支持自定义列名+数据+模板下载 - 后端编译通过,前端构建通过 --- .../healthlink-his-application/pom.xml | 15 ++ .../controller/DataExportController.java | 157 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/system/controller/DataExportController.java diff --git a/healthlink-his-server/healthlink-his-application/pom.xml b/healthlink-his-server/healthlink-his-application/pom.xml index e00a6c82d..c483bd924 100755 --- a/healthlink-his-server/healthlink-his-application/pom.xml +++ b/healthlink-his-server/healthlink-his-application/pom.xml @@ -17,6 +17,21 @@ + + + org.apache.poi + poi-ooxml + + + + com.itextpdf + itextpdf + + + com.itextpdf + itext-asian + + org.springframework.boot spring-boot-starter-actuator diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/system/controller/DataExportController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/system/controller/DataExportController.java new file mode 100644 index 000000000..b0c41042f --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/system/controller/DataExportController.java @@ -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 params, + HttpServletResponse response) throws IOException { + String filename = (String) params.getOrDefault("filename", "export"); + List> data = (List>) params.get("data"); + List> columns = (List>) 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 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 params, + HttpServletResponse response) throws IOException { + String filename = (String) params.getOrDefault("filename", "export"); + String title = (String) params.getOrDefault("title", "数据导出"); + List> data = (List>) params.get("data"); + List> columns = (List>) 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 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 rowData : data) { + for (Map 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(); + } +}