Commit fee18ff49c3d444ddaf904b1e22987f7287dce56

Authored by 房远帅
1 parent b6779167

订货单:改成导出pdf

... ... @@ -49,15 +49,15 @@ import javax.annotation.Resource;
49 49 import javax.servlet.http.HttpServletResponse;
50 50 import javax.validation.Valid;
51 51 import javax.validation.constraints.NotBlank;
52   -import java.io.FileNotFoundException;
53   -import java.io.IOException;
54   -import java.io.InputStream;
  52 +import java.io.*;
55 53 import java.math.BigDecimal;
56 54 import java.math.RoundingMode;
57 55 import java.net.URLEncoder;
  56 +import java.nio.file.Files;
58 57 import java.time.LocalDate;
59 58 import java.time.format.DateTimeFormatter;
60 59 import java.util.*;
  60 +import java.util.concurrent.TimeUnit;
61 61 import java.util.stream.Collectors;
62 62
63 63 /**
... ... @@ -358,13 +358,12 @@ public class PurchaseOrderInfoController extends DefaultBaseController {
358 358 public void printPurchaseOrder(@NotBlank(message = "id不能为空") String id, HttpServletResponse response) throws IOException {
359 359 PurchaseOrderInfo data = purchaseOrderInfoService.findById(id);
360 360
361   - // 设置响应头
362   - setupResponse(response, data.getOrderNo() + "-订货单打印.xlsx");
363   -
364 361 Wrapper<PurchaseOrderLine> purchaseOrderLineWrapper = Wrappers.lambdaQuery(PurchaseOrderLine.class)
365 362 .eq(PurchaseOrderLine::getPurchaseOrderId, id);
366 363 List<PurchaseOrderLine> purchaseOrderLineList = purchaseOrderLineService.list(purchaseOrderLineWrapper);
367 364
  365 + File tempExcel = null;
  366 + File pdfFile = null;
368 367
369 368 try {
370 369 // 加载模板文件
... ... @@ -467,21 +466,96 @@ public class PurchaseOrderInfoController extends DefaultBaseController {
467 466
468 467 processTemplate(workbook, dataMap);
469 468
470   - // 写入响应流
471   - workbook.write(response.getOutputStream());
472   - response.getOutputStream().flush();
  469 + // === 4. 写入临时 .xlsx 文件 ===
  470 + tempExcel = File.createTempFile("purchase_order_" + data.getOrderNo(), ".xlsx");
  471 + try (FileOutputStream fos = new FileOutputStream(tempExcel)) {
  472 + workbook.write(fos);
  473 + }
  474 +
473 475 } finally {
474 476 IOUtils.closeQuietly(workbook);
475 477 }
  478 + }
476 479
477   - } catch (FileNotFoundException e) {
478   - throw new RuntimeException("模板文件不存在: templates/purchaseOrderTemplate.xlsx", e);
479   - } catch (IOException e) {
480   - throw new RuntimeException("无法读取模板文件: templates/purchaseOrderTemplate.xlsx", e);
  480 + // === 5. 调用 LibreOffice 转 PDF ===
  481 + pdfFile = convertExcelToPdf(tempExcel);
  482 +
  483 + // === 6. 设置 PDF 响应头 ===
  484 + String fileName = data.getOrderNo() + "-订货单打印.pdf";
  485 + String encodedFileName;
  486 + try {
  487 + encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
  488 + } catch (UnsupportedEncodingException e) {
  489 + // UTF-8 是标准编码,理论上不会抛出,但 Java 8 要求处理
  490 + throw new RuntimeException("UTF-8 encoding not supported", e);
  491 + }
  492 + response.setContentType("application/pdf");
  493 + response.setCharacterEncoding("UTF-8");
  494 + response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
  495 + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
  496 +
  497 + // === 7. 输出 PDF 到浏览器 ===
  498 + try (InputStream pdfIn = new FileInputStream(pdfFile)) {
  499 + byte[] buffer = new byte[8192];
  500 + int len;
  501 + while ((len = pdfIn.read(buffer)) != -1) {
  502 + response.getOutputStream().write(buffer, 0, len);
  503 + }
  504 + response.getOutputStream().flush();
481 505 }
  506 +
482 507 } catch (Exception e) {
483   - log.error("标准合同模版打印: {}", e.getMessage(), e);
484   - throw e;
  508 + log.error("订货单导出 PDF 失败: {}", e.getMessage(), e);
  509 + throw new RuntimeException("订货单导出失败,请联系管理员", e);
  510 + } finally {
  511 + // === 8. 清理临时文件 ===
  512 + deleteQuietly(tempExcel);
  513 + deleteQuietly(pdfFile);
  514 + }
  515 + }
  516 +
  517 + private File convertExcelToPdf(File excelFile) throws IOException, InterruptedException {
  518 + if (!excelFile.exists()) {
  519 + throw new IllegalArgumentException("Excel 文件不存在: " + excelFile.getAbsolutePath());
  520 + }
  521 +
  522 + String pdfPath = excelFile.getAbsolutePath().replaceAll("\\.xlsx$", ".pdf");
  523 + File pdfFile = new File(pdfPath);
  524 +
  525 + // 使用绝对路径 /usr/bin/libreoffice(CentOS 7 默认位置)
  526 + String command = String.format(
  527 + "/usr/bin/libreoffice --headless --convert-to pdf --outdir %s %s",
  528 + excelFile.getParent(),
  529 + excelFile.getAbsolutePath()
  530 + );
  531 +
  532 + Process process = Runtime.getRuntime().exec(command);
  533 + boolean finished = process.waitFor(30, TimeUnit.SECONDS); // 超时 30 秒
  534 +
  535 + if (!finished) {
  536 + process.destroyForcibly();
  537 + throw new RuntimeException("LibreOffice 转换超时(>30秒)");
  538 + }
  539 +
  540 + int exitCode = process.exitValue();
  541 + if (exitCode != 0) {
  542 + throw new RuntimeException("LibreOffice 转换失败,退出码: " + exitCode);
  543 + }
  544 +
  545 + if (!pdfFile.exists()) {
  546 + throw new RuntimeException("PDF 未生成: " + pdfPath);
  547 + }
  548 +
  549 + return pdfFile;
  550 + }
  551 +
  552 + private void deleteQuietly(File file) {
  553 + if (file != null && file.exists()) {
  554 + try {
  555 + Files.delete(file.toPath());
  556 + } catch (IOException e) {
  557 + log.warn("无法删除临时文件: {}", file.getAbsolutePath(), e);
  558 + }
485 559 }
486 560 }
487 561
... ...