From fa252a23d4897d8fed3154d1d688ebc46a5fc378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E9=B9=8F=E9=A3=9E?= Date: Fri, 22 May 2026 15:45:41 +0800 Subject: [PATCH] =?UTF-8?q?refactor(pdfExport):=20=E9=87=8D=E6=9E=84PDF?= =?UTF-8?q?=E5=AF=BC=E5=87=BAService=E4=BB=A5=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E5=92=8C=E6=A0=B7=E5=BC=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 替换原有翻译列表为翻译映射,避免重复查找,提升性能 - 统一使用Map管理翻译文本,简化方法参数 - 增加多种字体和颜色定义,优化PDF中字体样式和文本颜色 - 改进字体加载方式,使用try-with-resources确保流关闭 - 采用HttpClient实例变量复用,提升图片下载效率和稳定性 - 重构PDF文档结构,拆分逻辑为多个私有方法,提高代码可读性 - 优化表格、段落格式及间距,提升PDF排版效果 - 修复空指针和空集合检查,保证运行时稳定性 - 增强单元格内容对齐和样式一致性 - 规范价格格式化,使用线程安全的DecimalFormat实例 - 替换旧版AtomicInteger计数为普通整数变量控制索引 - 调整方法调用链,确保PDF流正确关闭避免资源泄露 --- .../quotation/service/PdfExportService.java | 474 +++++++++--------- 1 file changed, 224 insertions(+), 250 deletions(-) diff --git a/nflg-mobilebroken-quotation/src/main/java/com/nflg/mobilebroken/quotation/service/PdfExportService.java b/nflg-mobilebroken-quotation/src/main/java/com/nflg/mobilebroken/quotation/service/PdfExportService.java index 87f68dd6..3ed82856 100644 --- a/nflg-mobilebroken-quotation/src/main/java/com/nflg/mobilebroken/quotation/service/PdfExportService.java +++ b/nflg-mobilebroken-quotation/src/main/java/com/nflg/mobilebroken/quotation/service/PdfExportService.java @@ -44,7 +44,6 @@ import java.text.DecimalFormat; import java.time.Duration; import java.util.*; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Slf4j @@ -86,7 +85,18 @@ public class PdfExportService { private final BaseFont bfChinese = getChineseFont(); private final Font normalFont = new Font(bfChinese, 12); - private final DecimalFormat df = new DecimalFormat("#,###.##"); + private final Font boldFont13 = new Font(bfChinese, 13, Font.BOLD); + private final Font normalFont13 = new Font(bfChinese, 13, Font.NORMAL); + private final Font boldFont15 = new Font(bfChinese, 15, Font.BOLD); + private final Font titleFont = new Font(bfChinese, 28, Font.BOLD, new Color(0x3F, 0x3F, 0x3F)); + private final Color redColor = new Color(0xC0, 0x00, 0x00); + private final Font redBoldFont20 = new Font(bfChinese, 20, Font.BOLD, redColor); + private final Font redBoldFont15 = new Font(bfChinese, 15, Font.BOLD, redColor); + private final ThreadLocal decimalFormatLocal = ThreadLocal.withInitial(() -> new DecimalFormat("#,###.##")); + private final HttpClient httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); private byte[] defaultPic; @PostConstruct @@ -103,19 +113,48 @@ public class PdfExportService { response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); - List translates = webComponentTranslateService.getListByCode("QuotationSystem", MultilingualUtil.getLanguage()); + List translateList = webComponentTranslateService.getListByCode("QuotationSystem", MultilingualUtil.getLanguage()); + Map translateMap = translateList.stream() + .collect(Collectors.toMap(WebComponentTranslateVO::getComponentCode, WebComponentTranslateVO::getValue, (a, b) -> a)); Document document = new Document(PageSize.A4); PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream()); writer.setPageEvent(new HeaderFooterEvent("images/head.png")); - document.open(); + try { + document.open(); + //第一页 + addCoverPage(document, order, translateMap); + document.add(Chunk.NEXTPAGE); + //客户信息 + addCustomerInfoTable(document, order, translateMap); + Paragraph spacer = new Paragraph(); + spacer.setSpacingBefore(40f); + document.add(spacer); + //报价清单 + addQuoteList(document, order, translateMap); + //机型信息 + List carts = quotationShoppingOrderService.getCarts(order.getId()); + addProductModelInfo(document, carts, order, translateMap); + //备注 + document.add(new Paragraph(new Chunk(order.getRemark(), normalFont))); + //付款方式 + Paragraph paymentParagraph = new Paragraph(); + paymentParagraph.add(new Chunk(getText(translateMap, "PaymentMethods") + ":", boldFont13)); + paymentParagraph.add(new Chunk(order.getPaymentMethod(), normalFont13)); + document.add(paymentParagraph); + } finally { + document.close(); + writer.close(); + } + } + + private void addCoverPage(Document document, QuotationShoppingOrder order, Map translateMap) throws IOException { Rectangle pageSize = document.getPageSize(); - //第一页 PdfPTable footerTable = new PdfPTable(1); footerTable.setTotalWidth(pageSize.getWidth() - document.leftMargin() - document.rightMargin()); footerTable.setLockedWidth(true); - Chunk chunk1 = new Chunk(getText(translates, "title"), new Font(bfChinese, 28, Font.BOLD, new Color(0x3F, 0x3F, 0x3F))); - chunk1.setUnderline(new Color(0xC0, 0x00, 0x00), 5f, 0f, -15f, 0f, PdfContentByte.LINE_CAP_BUTT); + Chunk chunk1 = new Chunk(getText(translateMap, "title"), titleFont); + chunk1.setUnderline(redColor, 5f, 0f, -15f, 0f, PdfContentByte.LINE_CAP_BUTT); PdfPCell cell1 = new PdfPCell(new Phrase(chunk1)); cell1.setHorizontalAlignment(Element.ALIGN_RIGHT); cell1.setVerticalAlignment(Element.ALIGN_MIDDLE); @@ -124,18 +163,16 @@ public class PdfExportService { cell1.setBorder(Rectangle.NO_BORDER); footerTable.addCell(cell1); - PdfPCell cell2 = new PdfPCell(new Phrase(getText(translates, "QuotationDate") + ":" - + DateTimeUtil.format(order.getCreateTime(), "yyyy-MM-dd"), new Font(bfChinese, 15, Font.BOLD))); + PdfPCell cell2 = new PdfPCell(new Phrase(getText(translateMap, "QuotationDate") + ":" + + DateTimeUtil.format(order.getCreateTime(), "yyyy-MM-dd"), boldFont15)); cell2.setHorizontalAlignment(Element.ALIGN_RIGHT); cell2.setPaddingTop(40); cell2.setBorder(Rectangle.NO_BORDER); footerTable.addCell(cell2); Phrase phrase = new Phrase(); - Chunk chunk2 = new Chunk(getText(translates, "Validity") + ":", new Font(bfChinese, 15, Font.BOLD)); - phrase.add(chunk2); - Chunk chunk3 = new Chunk(getText(translates, "Validity1"), new Font(bfChinese, 15, Font.BOLD, new Color(0xC0, 0x00, 0x00))); - phrase.add(chunk3); + phrase.add(new Chunk(getText(translateMap, "Validity") + ":", boldFont15)); + phrase.add(new Chunk(getText(translateMap, "Validity1"), redBoldFont15)); PdfPCell cell3 = new PdfPCell(phrase); cell3.setHorizontalAlignment(Element.ALIGN_RIGHT); cell3.setPaddingTop(3); @@ -143,39 +180,39 @@ public class PdfExportService { footerTable.addCell(cell3); List images = dictionaryItemService.getListByDictionaryCode("PDFCoverImage", MultilingualUtil.getLanguage()); - PdfPTable outerTable = new PdfPTable(images.size()); - outerTable.setWidthPercentage(100); - List widths = new ArrayList<>(); - images.forEach(method -> { - widths.add(1f); - }); - outerTable.setWidths(Floats.toArray(widths)); - for (int i = 0; i < images.size(); i++) { - PdfPCell cell = new PdfPCell(); - cell.setHorizontalAlignment(Element.ALIGN_CENTER); - cell.setVerticalAlignment(Element.ALIGN_TOP); - if (i != 0) { - cell.setPaddingLeft(5); + if (CollectionUtil.isNotEmpty(images)) { + PdfPTable outerTable = new PdfPTable(images.size()); + outerTable.setWidthPercentage(100); + List widths = new ArrayList<>(); + images.forEach(method -> widths.add(1f)); + outerTable.setWidths(Floats.toArray(widths)); + for (int i = 0; i < images.size(); i++) { + PdfPCell cell = new PdfPCell(); + cell.setHorizontalAlignment(Element.ALIGN_CENTER); + cell.setVerticalAlignment(Element.ALIGN_TOP); + if (i != 0) { + cell.setPaddingLeft(5); + } + cell.setBorder(Rectangle.NO_BORDER); + cell.setBorderColor(Color.WHITE); + cell.setBorderWidth(5f); + Image image = Image.getInstance(downloadImage(images.get(i).getValue())); + image.scaleToFit(125f, 1000f); + image.setAlignment(Element.ALIGN_CENTER); + Paragraph caption = new Paragraph(images.get(i).getName(), new Font(bfChinese, 12, Font.BOLD)); + caption.setAlignment(Element.ALIGN_CENTER); + caption.setSpacingBefore(4f); + cell.addElement(image); + cell.addElement(caption); + outerTable.addCell(cell); } - cell.setBorder(Rectangle.NO_BORDER); - cell.setBorderColor(Color.WHITE); - cell.setBorderWidth(5f); - Image image = Image.getInstance(downloadImage(images.get(i).getValue())); - image.scaleToFit(125f, 1000f); - image.setAlignment(Element.ALIGN_CENTER); - Paragraph caption = new Paragraph(images.get(i).getName(), new Font(bfChinese, 12, Font.BOLD)); - caption.setAlignment(Element.ALIGN_CENTER); - caption.setSpacingBefore(4f); - cell.addElement(image); - cell.addElement(caption); - outerTable.addCell(cell); + PdfPCell cell4 = new PdfPCell(outerTable); + cell4.setPaddingTop(250); + cell4.setBorder(Rectangle.NO_BORDER); + footerTable.addCell(cell4); } - PdfPCell cell4 = new PdfPCell(outerTable); - cell4.setPaddingTop(250); - cell4.setBorder(Rectangle.NO_BORDER); - footerTable.addCell(cell4); - PdfPCell cell5 = new PdfPCell(new Phrase(new Chunk(getText(translates, "more"), new Font(bfChinese, 20, Font.BOLD, new Color(0xC0, 0x00, 0x00))))); + PdfPCell cell5 = new PdfPCell(new Phrase(new Chunk(getText(translateMap, "more"), redBoldFont20))); cell5.setPaddingTop(80); cell5.setVerticalAlignment(Element.ALIGN_MIDDLE); cell5.setHorizontalAlignment(Element.ALIGN_CENTER); @@ -183,160 +220,108 @@ public class PdfExportService { footerTable.addCell(cell5); document.add(footerTable); - document.add(Chunk.NEXTPAGE); - //开始正文 - String userName, userPhone, userEmail; + } + + private void addCustomerInfoTable(Document document, QuotationShoppingOrder order, Map translateMap) { + String userName = "", userPhone = "", userEmail = ""; if (order.getCreateByType() == 0) { AdminUser adminUser = adminUserService.getById(order.getCreateById()); - userName = adminUser.getUserName(); - userPhone = adminUser.getPhone(); - userEmail = adminUser.getEmail(); + if (Objects.nonNull(adminUser)) { + userName = adminUser.getUserName(); + userPhone = adminUser.getPhone(); + userEmail = adminUser.getEmail(); + } } else { AppUser appUser = appUserService.getById(order.getCreateById()); - userName = appUser.getName(); - userPhone = appUser.getPhone(); - userEmail = appUser.getEmail(); + if (Objects.nonNull(appUser)) { + userName = appUser.getName(); + userPhone = appUser.getPhone(); + userEmail = appUser.getEmail(); + } } Font labelFont = new Font(bfChinese, 12, Font.BOLD); Font valueFont = new Font(bfChinese, 12, Font.NORMAL); PdfPTable table = new PdfPTable(4); table.setWidthPercentage(100); table.setWidths(new float[]{1.5f, 2f, 1.5f, 3f}); - PdfPCell label1_1 = createLabelCell(getText(translates, "Customer") + ":", labelFont); - table.addCell(label1_1); - PdfPCell value1_1 = createValueCell(order.getCustomerName(), valueFont); - table.addCell(value1_1); - PdfPCell label1_2 = createLabelCell(getText(translates, "Supplier") + ":", labelFont); - table.addCell(label1_2); - PdfPCell value1_2 = createValueCell(getText(translates, "nflg"), valueFont); - table.addCell(value1_2); - PdfPCell label2_1 = createLabelCell(getText(translates, "ContactPerson") + ":", labelFont); - table.addCell(label2_1); - PdfPCell value2_1 = createValueCell(order.getContactPerson(), valueFont); - table.addCell(value2_1); - PdfPCell label2_2 = createLabelCell(getText(translates, "ContactPerson") + ":", labelFont); - table.addCell(label2_2); - PdfPCell value2_2 = createValueCell(userName, valueFont); - table.addCell(value2_2); - PdfPCell label3_1 = createLabelCell(getText(translates, "ContactInformation") + ":", labelFont); - table.addCell(label3_1); - PdfPCell value3_1 = createValueCell(order.getContactInformation(), valueFont); - table.addCell(value3_1); - PdfPCell label3_2 = createLabelCell(getText(translates, "ContactInformation") + ":", labelFont); - table.addCell(label3_2); - PdfPCell value3_2 = createValueCell(userPhone, valueFont); - table.addCell(value3_2); - PdfPCell label4_1 = createLabelCell(getText(translates, "Email") + ":", labelFont); - table.addCell(label4_1); - PdfPCell value4_1 = createValueCell(order.getContactEmail(), valueFont); - table.addCell(value4_1); - PdfPCell label4_2 = createLabelCell(getText(translates, "Email") + ":", labelFont); - table.addCell(label4_2); - PdfPCell value4_2 = createValueCell(userEmail, valueFont); - table.addCell(value4_2); - PdfPCell label5_1 = createLabelCell(getText(translates, "Country") + ":", labelFont); - table.addCell(label5_1); - PdfPCell value5_1 = createValueCell(order.getContactCountry(), valueFont); - table.addCell(value5_1); - PdfPCell label5_2 = createLabelCell(getText(translates, "QuotationNo") + ":", labelFont); - table.addCell(label5_2); - PdfPCell value5_2 = createValueCell(order.getNo(), valueFont); - table.addCell(value5_2); + table.addCell(createLabelCell(getText(translateMap, "Customer") + ":", labelFont)); + table.addCell(createValueCell(order.getCustomerName(), valueFont)); + table.addCell(createLabelCell(getText(translateMap, "Supplier") + ":", labelFont)); + table.addCell(createValueCell(getText(translateMap, "nflg"), valueFont)); + table.addCell(createLabelCell(getText(translateMap, "ContactPerson") + ":", labelFont)); + table.addCell(createValueCell(order.getContactPerson(), valueFont)); + table.addCell(createLabelCell(getText(translateMap, "ContactPerson") + ":", labelFont)); + table.addCell(createValueCell(userName, valueFont)); + table.addCell(createLabelCell(getText(translateMap, "ContactInformation") + ":", labelFont)); + table.addCell(createValueCell(order.getContactInformation(), valueFont)); + table.addCell(createLabelCell(getText(translateMap, "ContactInformation") + ":", labelFont)); + table.addCell(createValueCell(userPhone, valueFont)); + table.addCell(createLabelCell(getText(translateMap, "Email") + ":", labelFont)); + table.addCell(createValueCell(order.getContactEmail(), valueFont)); + table.addCell(createLabelCell(getText(translateMap, "Email") + ":", labelFont)); + table.addCell(createValueCell(userEmail, valueFont)); + table.addCell(createLabelCell(getText(translateMap, "Country") + ":", labelFont)); + table.addCell(createValueCell(order.getContactCountry(), valueFont)); + table.addCell(createLabelCell(getText(translateMap, "QuotationNo") + ":", labelFont)); + table.addCell(createValueCell(order.getNo(), valueFont)); document.add(table); - Paragraph spacer = new Paragraph(); - spacer.setSpacingBefore(40f); - document.add(spacer); - //报价清单 - PdfPTable tableBJQD = new PdfPTable(5); - tableBJQD.setWidthPercentage(100); - tableBJQD.setWidths(new float[]{10f, 40f, 20f, 15f, 15f}); - PdfPCell headBJQD = createBJQDCell(getText(translates, "QuoteList"), 5, normalFont); - tableBJQD.addCell(headBJQD); - PdfPCell cellBJQDH1 = createBJQDCell(getText(translates, "Index"), 1, normalFont); - tableBJQD.addCell(cellBJQDH1); - PdfPCell cellBJQDH2 = createBJQDCell(getText(translates, "DeviceName"), 1, normalFont); - tableBJQD.addCell(cellBJQDH2); - PdfPCell cellBJQDH3 = createBJQDCell(getText(translates, "DeviceModel"), 1, normalFont); - tableBJQD.addCell(cellBJQDH3); - PdfPCell cellBJQDH4 = createBJQDCell(getText(translates, "Quantity"), 1, normalFont); - tableBJQD.addCell(cellBJQDH4); - PdfPCell cellBJQDH5 = createBJQDCell(getText(translates, "Price"), 1, normalFont); - tableBJQD.addCell(cellBJQDH5); - List carts = quotationShoppingOrderService.getCarts(order.getId()); - AtomicInteger i = new AtomicInteger(1); - carts.forEach(cart -> { - PdfPCell cellR1 = createBJQDCell(String.valueOf(i.getAndIncrement()), 1, normalFont); - tableBJQD.addCell(cellR1); - PdfPCell cellR2 = createBJQDCell(cart.getTypeName(), 1, normalFont); - tableBJQD.addCell(cellR2); - PdfPCell cellR3 = createBJQDCell(cart.getModelNo(), 1, normalFont); - tableBJQD.addCell(cellR3); - PdfPCell cellR4 = createBJQDCell("1", 1, normalFont); - tableBJQD.addCell(cellR4); - PdfPCell cellR5 = createBJQDCell(format(cart.getStandardFee()), 1, normalFont); - tableBJQD.addCell(cellR5); - }); - PdfPCell cellZBFF = createBJQDCell(getText(translates, "WarrantyService"), 4, normalFont); - tableBJQD.addCell(cellZBFF); - PdfPCell cellZBFF1 = createBJQDCell(format( - carts.stream() - .map(QuotationShoppingCartDTO::getWarrantyServiceFee) - .reduce(BigDecimal.ZERO, BigDecimal::add) - ), 1, normalFont); - tableBJQD.addCell(cellZBFF1); - PdfPCell cellJJFF = createBJQDCell(getText(translates, "HandoverService"), 4, normalFont); - tableBJQD.addCell(cellJJFF); - PdfPCell cellJJFF1 = createBJQDCell(format(carts.stream() - .map(QuotationShoppingCartDTO::getServiceFee) - .reduce(BigDecimal.ZERO, BigDecimal::add) - ), 1, normalFont); - tableBJQD.addCell(cellJJFF1); - PdfPCell cellBJ = createBJQDCell(getText(translates, "Parts"), 4, normalFont); - tableBJQD.addCell(cellBJ); - PdfPCell cellBJ1 = createBJQDCell(format(carts.stream() - .map(QuotationShoppingCartDTO::getAccessoryFee) - .reduce(BigDecimal.ZERO, BigDecimal::add) - ), 1, normalFont); - tableBJQD.addCell(cellBJ1); - PdfPCell cellBZ = createBJQDCell(order.getAdditionalNotes(), 5, normalFont); - cellBZ.setHorizontalAlignment(Element.ALIGN_LEFT); - tableBJQD.addCell(cellBZ); - document.add(tableBJQD); - //机型信息 - addProductModelInfo(document, carts, order, translates); - //备注 - document.add(new Paragraph(new Chunk(order.getRemark(), normalFont))); - //付款方式 - Paragraph fkfs = new Paragraph(); - fkfs.add(new Chunk(getText(translates, "PaymentMethods") + ":", new Font(bfChinese, 13, Font.BOLD))); - fkfs.add(new Chunk(order.getPaymentMethod(), new Font(bfChinese, 13, Font.NORMAL))); - document.add(fkfs); - - document.close(); } - private String getText(List translates, String code) { - return translates.stream() - .filter(translate -> StrUtil.equals(translate.getComponentCode(), code)) - .findFirst() - .map(WebComponentTranslateVO::getValue) - .orElse(""); + private void addQuoteList(Document document, QuotationShoppingOrder order, Map translateMap) { + PdfPTable quoteTable = new PdfPTable(5); + quoteTable.setWidthPercentage(100); + quoteTable.setWidths(new float[]{10f, 40f, 20f, 15f, 15f}); + quoteTable.addCell(createBJQDCell(getText(translateMap, "QuoteList"), 5, boldFont13)); + quoteTable.addCell(createBJQDCell(getText(translateMap, "Index"), 1, normalFont)); + quoteTable.addCell(createBJQDCell(getText(translateMap, "DeviceName"), 1, normalFont)); + quoteTable.addCell(createBJQDCell(getText(translateMap, "DeviceModel"), 1, normalFont)); + quoteTable.addCell(createBJQDCell(getText(translateMap, "Quantity"), 1, normalFont)); + quoteTable.addCell(createBJQDCell(getText(translateMap, "Price"), 1, normalFont)); + List carts = quotationShoppingOrderService.getCarts(order.getId()); + int index = 1; + for (QuotationShoppingCartDTO cart : carts) { + quoteTable.addCell(createBJQDCell(String.valueOf(index++), 1, normalFont)); + quoteTable.addCell(createBJQDCell(cart.getTypeName(), 1, normalFont)); + quoteTable.addCell(createBJQDCell(cart.getModelNo(), 1, normalFont)); + quoteTable.addCell(createBJQDCell("1", 1, normalFont)); + quoteTable.addCell(createBJQDCell(format(cart.getStandardFee()), 1, normalFont)); + } + quoteTable.addCell(createBJQDCell(getText(translateMap, "WarrantyService"), 4, normalFont)); + quoteTable.addCell(createBJQDCell(format( + carts.stream().map(QuotationShoppingCartDTO::getWarrantyServiceFee) + .reduce(BigDecimal.ZERO, BigDecimal::add)), 1, normalFont)); + quoteTable.addCell(createBJQDCell(getText(translateMap, "HandoverService"), 4, normalFont)); + quoteTable.addCell(createBJQDCell(format( + carts.stream().map(QuotationShoppingCartDTO::getServiceFee) + .reduce(BigDecimal.ZERO, BigDecimal::add)), 1, normalFont)); + quoteTable.addCell(createBJQDCell(getText(translateMap, "Parts"), 4, normalFont)); + quoteTable.addCell(createBJQDCell(format( + carts.stream().map(QuotationShoppingCartDTO::getAccessoryFee) + .reduce(BigDecimal.ZERO, BigDecimal::add)), 1, normalFont)); + PdfPCell notesCell = createBJQDCell(order.getAdditionalNotes(), 5, normalFont); + notesCell.setHorizontalAlignment(Element.ALIGN_LEFT); + quoteTable.addCell(notesCell); + document.add(quoteTable); + } + + private String getText(Map translateMap, String code) { + return translateMap.getOrDefault(code, ""); } private void addRandomAccessories(Document document, List accessories - , List translates) { + , Map translateMap) { if (CollectionUtil.isNotEmpty(accessories)) { - document.add(new Paragraph(new Chunk(getText(translates, "RandomAccessories"), new Font(bfChinese, 13, Font.BOLD)))); + document.add(new Paragraph(new Chunk(getText(translateMap, "RandomAccessories"), boldFont13))); PdfPTable table = new PdfPTable(5); table.setWidthPercentage(100); table.setSpacingBefore(10f); table.setWidths(new float[]{20f, 20f, 20f, 20f, 20f}); //表头 - table.addCell(createBJQDCell(getText(translates, "MaterialNo"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "MaterialName"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "Num"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "Price"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "TotalPrice"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "MaterialNo"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "MaterialName"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Num"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Price"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "TotalPrice"), 1, normalFont)); accessories.forEach(accessory -> { table.addCell(createBJQDCell(accessory.getMaterialNo(), 1, normalFont)); table.addCell(createBJQDCell(accessory.getName(), 1, normalFont)); @@ -348,20 +333,20 @@ public class PdfExportService { } } - private void addJJFF(Document document, List services, List translates) { + private void addJJFF(Document document, List services, Map translateMap) { if (CollectionUtil.isNotEmpty(services)) { - document.add(new Paragraph(new Chunk(getText(translates, "HandoverService"), new Font(bfChinese, 13, Font.BOLD)))); + document.add(new Paragraph(new Chunk(getText(translateMap, "HandoverService"), boldFont13))); PdfPTable table = new PdfPTable(6); table.setWidthPercentage(100); table.setSpacingBefore(10f); table.setWidths(new float[]{15f, 15f, 15f, 15f, 20f, 20f}); //表头 - table.addCell(createBJQDCell(getText(translates, "Numberofpeople"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "Days"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "Price"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "TotalPrice"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "Address"), 1, normalFont)); - table.addCell(createBJQDCell(getText(translates, "Remark"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Numberofpeople"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Days"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Price"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "TotalPrice"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Address"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Remark"), 1, normalFont)); for (QuotationShoppingCartService service : services) { table.addCell(createBJQDCell(String.valueOf(service.getUserNum()), 1, normalFont)); table.addCell(createBJQDCell(String.valueOf(service.getDays()), 1, normalFont)); @@ -376,7 +361,10 @@ public class PdfExportService { private void addPriceInfo(Document document, List deliveryMethods , List parts, QuotationShoppingOrder order, QuotationShoppingCartDTO cart - , List translates) { + , Map translateMap) { + if (CollectionUtil.isEmpty(deliveryMethods)) { + return; + } PdfPTable table = new PdfPTable(deliveryMethods.size() + 1); table.setWidthPercentage(100); table.setKeepTogether(true); @@ -384,9 +372,7 @@ public class PdfExportService { List widths = new ArrayList<>(); widths.add(20f); Float width = 80f / deliveryMethods.size(); - deliveryMethods.forEach(method -> { - widths.add(width); - }); + deliveryMethods.forEach(method -> widths.add(width)); table.setWidths(Floats.toArray(widths)); //列头 table.addCell(createBJQDCell("", 1, normalFont)); @@ -394,33 +380,34 @@ public class PdfExportService { table.addCell(createBJQDCell(method.getDeliveryMethodName() + " " + order.getContactCountry(), 1, normalFont)) ); DictionaryItem currency = dictionaryItemService.getById(order.getCurrency()); + String currencyValue = Objects.nonNull(currency) ? currency.getValue() : ""; //标准配置 - table.addCell(createBJQDCell(getText(translates, "StandardConfiguration"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "StandardConfiguration"), 1, normalFont)); BigDecimal totalPrice = cart.getStandardFee(); deliveryMethods.forEach(method -> table.addCell(createBJQDCell( - format(totalPrice.add(method.getDeliveryFee()).multiply(order.getExchangeRate())) + " " + currency.getValue() + format(totalPrice.add(method.getDeliveryFee()).multiply(order.getExchangeRate())) + " " + currencyValue , 1 , normalFont) ) ); //可选配置 - PdfPCell ckx = createBJQDCell(getText(translates, "OptionalConfiguration"), table.getNumberOfColumns(), normalFont); - ckx.setHorizontalAlignment(Element.ALIGN_LEFT); - ckx.setPaddingLeft(25); - table.addCell(ckx); + PdfPCell optionalCell = createBJQDCell(getText(translateMap, "OptionalConfiguration"), table.getNumberOfColumns(), normalFont); + optionalCell.setHorizontalAlignment(Element.ALIGN_LEFT); + optionalCell.setPaddingLeft(25); + table.addCell(optionalCell); parts.forEach(part -> { table.addCell(createBJQDCell(part.getPartName(), 1, normalFont)); deliveryMethods.forEach(method -> table.addCell(createBJQDCell( - format(part.getAmount().add(method.getDeliveryFee()).multiply(order.getExchangeRate())) + " " + currency.getValue() + format(part.getAmount().add(method.getDeliveryFee()).multiply(order.getExchangeRate())) + " " + currencyValue , 1 , normalFont) ) ); }); //合计 - table.addCell(createBJQDCell(getText(translates, "Total"), 1, normalFont)); + table.addCell(createBJQDCell(getText(translateMap, "Total"), 1, normalFont)); deliveryMethods.forEach(method -> table.addCell(createBJQDCell( format( @@ -431,7 +418,7 @@ public class PdfExportService { .reduce(BigDecimal.ZERO, BigDecimal::add) ) ).add(method.getDeliveryFee()).multiply(order.getExchangeRate()) - ) + " " + currency.getValue() + ) + " " + currencyValue , 1 , normalFont) ) @@ -444,8 +431,9 @@ public class PdfExportService { } private void addProductModelInfo(Document document, List carts - , QuotationShoppingOrder order, List translates) throws IOException { + , QuotationShoppingOrder order, Map translateMap) throws IOException { document.add(Chunk.NEXTPAGE); + List deliveryMethods = shoppingOrderDeliveryMethodService.getList(order.getId()); for (QuotationShoppingCartDTO cart : carts) { document.add(new Paragraph(new Chunk(cart.getTypeName(), new Font(bfChinese, 18, Font.NORMAL)))); document.add(new Paragraph(new Chunk(cart.getModelNo(), new Font(bfChinese, 14, Font.BOLD)))); @@ -453,7 +441,7 @@ public class PdfExportService { image.scaleToFit(getMaxContentWidth(document), 1000f); document.add(image); ProductModelIntroItem introItem = productModelIntroItemService.getByModelId(cart.getModelId(), MultilingualUtil.getLanguage()); - if (StrUtil.isNotBlank(introItem.getDesc())) { + if (Objects.nonNull(introItem) && StrUtil.isNotBlank(introItem.getDesc())) { addHtmlContent(document, introItem.getDesc()); } //部件 @@ -476,15 +464,15 @@ public class PdfExportService { ); }); //运输信息 - Paragraph yscc = new Paragraph(); - yscc.add(new Chunk(getText(translates, "ShippingDimensions") + ":", new Font(bfChinese, 13, Font.BOLD))); - yscc.add(new Chunk(parts.stream() + Paragraph shippingDimParagraph = new Paragraph(); + shippingDimParagraph.add(new Chunk(getText(translateMap, "ShippingDimensions") + ":", boldFont13)); + shippingDimParagraph.add(new Chunk(parts.stream() .max(Comparator.comparingLong(ModelConfigEffectiveDTO::getShippingDimensionsValue)) - .get() - .getShippingDimensions() - , new Font(bfChinese, 13, Font.NORMAL))); - yscc.setSpacingBefore(40f); - document.add(yscc); + .map(ModelConfigEffectiveDTO::getShippingDimensions) + .orElse("") + , normalFont13)); + shippingDimParagraph.setSpacingBefore(40f); + document.add(shippingDimParagraph); BigDecimal weight = BigDecimal.ZERO; for (ModelConfigEffectiveDTO part : parts) { if (Objects.nonNull(part.getWeight())) { @@ -493,45 +481,47 @@ public class PdfExportService { } } } - Paragraph zl = new Paragraph(); - zl.add(new Chunk(getText(translates, "Weight") + ":", new Font(bfChinese, 13, Font.BOLD))); - zl.add(new Chunk(format(weight) + "kg", new Font(bfChinese, 13, Font.NORMAL))); - document.add(zl); + Paragraph weightParagraph = new Paragraph(); + weightParagraph.add(new Chunk(getText(translateMap, "Weight") + ":", boldFont13)); + weightParagraph.add(new Chunk(format(weight) + "kg", normalFont13)); + document.add(weightParagraph); //质保服务 - String zbfwText = cart.getWarrantyServiceDesc(); - boolean iss = false; + String warrantyText = cart.getWarrantyServiceDesc(); + boolean hasDicWarranty = false; if (Objects.nonNull(cart.getWarrantyServiceDicId())) { DictionaryItem warrantyService = dictionaryItemService.getByIdAndLanguage(cart.getWarrantyServiceDicId(), MultilingualUtil.getLanguage()); - zbfwText = warrantyService.getName(); - iss = true; + if (Objects.nonNull(warrantyService)) { + warrantyText = warrantyService.getName(); + hasDicWarranty = true; + } } - Paragraph zbfw = new Paragraph(); - zbfw.add(new Chunk(getText(translates, "WarrantyService") + ":", new Font(bfChinese, 13, Font.BOLD))); - if (iss) { - zbfw.add(new Chunk(zbfwText, new Font(bfChinese, 13, Font.NORMAL))); + Paragraph warrantyParagraph = new Paragraph(); + warrantyParagraph.add(new Chunk(getText(translateMap, "WarrantyService") + ":", boldFont13)); + if (hasDicWarranty) { + warrantyParagraph.add(new Chunk(warrantyText, normalFont13)); } else { - zbfw.add(new Chunk(zbfwText + "," + format(cart.getWarrantyServiceFee()) + " CNY", new Font(bfChinese, 13, Font.NORMAL))); + warrantyParagraph.add(new Chunk(warrantyText + "," + format(cart.getWarrantyServiceFee()) + " CNY", normalFont13)); } - document.add(zbfw); + document.add(warrantyParagraph); //交机服务 addJJFF(document, shoppingCartServiceService.lambdaQuery() .eq(QuotationShoppingCartService::getCartId, cart.getId()) - .list(), translates + .list(), translateMap ); //随机配件 addRandomAccessories(document, shoppingCartAccessoryService.lambdaQuery() .eq(QuotationShoppingCartAccessory::getCartId, cart.getId()) - .list(), translates + .list(), translateMap ); //价格信息 addPriceInfo(document - , shoppingOrderDeliveryMethodService.getList(order.getId()) + , deliveryMethods , parts.stream() .filter(part -> part.getParentId() > 0 && part.getType() == 1 && Objects.equals(part.getOptionalType(), 0)) .collect(Collectors.toList()) , order , cart - , translates); + , translateMap); } } @@ -585,13 +575,6 @@ public class PdfExportService { return cell; } - private PdfPCell createPartTextCell(String name, String value, Font font) { - PdfPCell cell = new PdfPCell(new Phrase(name + ":" + value, font)); - cell.setBorder(Rectangle.NO_BORDER); - cell.setHorizontalAlignment(Element.ALIGN_LEFT); - cell.setVerticalAlignment(Element.ALIGN_MIDDLE); - return cell; - } private void addHtmlContent(Document document, String html) throws IOException { StyleSheet styles = new StyleSheet(); @@ -654,25 +637,21 @@ public class PdfExportService { private BaseFont getChineseFont() { try { - // 1. 从 classpath 获取字体流 - String fontPath = "fonts/simhei.ttf"; // 对应 resources/fonts/simsun.ttf - InputStream inputStream = QuotationApplication.class.getClassLoader().getResourceAsStream(fontPath); - if (inputStream == null) { - throw new RuntimeException("找不到字体文件: " + fontPath); + String fontPath = "fonts/simhei.ttf"; + try (InputStream inputStream = QuotationApplication.class.getClassLoader().getResourceAsStream(fontPath)) { + if (inputStream == null) { + throw new RuntimeException("找不到字体文件: " + fontPath); + } + byte[] fontBytes = inputStream.readAllBytes(); + return BaseFont.createFont("simhei.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, fontBytes, null); } - // 2. 将流转换为 byte 数组 (Java 9+ 写法) - byte[] fontBytes = inputStream.readAllBytes(); - inputStream.close(); - // 3. 使用 byte 数组创建 BaseFont - // 参数说明:字体名称(随便起), 编码, 嵌入PDF, 缓存, 字体字节流, PFB字节流(null) - return BaseFont.createFont("simhei.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, fontBytes, null); } catch (Exception e) { throw new RuntimeException("加载中文字体失败", e); } } private String format(BigDecimal price) { - return df.format(price); + return decimalFormatLocal.get().format(price); } private byte[] downloadImage(String urlStr) { @@ -680,17 +659,12 @@ public class PdfExportService { return defaultPic; } try { - HttpClient client = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(10)) // 连接超时 10秒 - .followRedirects(HttpClient.Redirect.NORMAL) // 自动处理重定向 - .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(urlStr)) - .timeout(Duration.ofSeconds(15)) // 读取超时 15秒 - // 某些图片服务器有防盗链,可以模拟浏览器 User-Agent + .timeout(Duration.ofSeconds(15)) .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)") .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); if (response.statusCode() == 200) { return response.body(); } else {