refactor(pdfExport): 重构PDF导出Service以支持多语言和样式优化

- 替换原有翻译列表为翻译映射,避免重复查找,提升性能
- 统一使用Map<String, String>管理翻译文本,简化方法参数
- 增加多种字体和颜色定义,优化PDF中字体样式和文本颜色
- 改进字体加载方式,使用try-with-resources确保流关闭
- 采用HttpClient实例变量复用,提升图片下载效率和稳定性
- 重构PDF文档结构,拆分逻辑为多个私有方法,提高代码可读性
- 优化表格、段落格式及间距,提升PDF排版效果
- 修复空指针和空集合检查,保证运行时稳定性
- 增强单元格内容对齐和样式一致性
- 规范价格格式化,使用线程安全的DecimalFormat实例
- 替换旧版AtomicInteger计数为普通整数变量控制索引
- 调整方法调用链,确保PDF流正确关闭避免资源泄露
This commit is contained in:
曹鹏飞 2026-05-22 15:45:41 +08:00
parent 805f2474ba
commit fa252a23d4
1 changed files with 224 additions and 250 deletions

View File

@ -44,7 +44,6 @@ import java.text.DecimalFormat;
import java.time.Duration; import java.time.Duration;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@ -86,7 +85,18 @@ public class PdfExportService {
private final BaseFont bfChinese = getChineseFont(); private final BaseFont bfChinese = getChineseFont();
private final Font normalFont = new Font(bfChinese, 12); 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<DecimalFormat> decimalFormatLocal = ThreadLocal.withInitial(() -> new DecimalFormat("#,###.##"));
private final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
private byte[] defaultPic; private byte[] defaultPic;
@PostConstruct @PostConstruct
@ -103,19 +113,48 @@ public class PdfExportService {
response.setHeader("Pragma", "no-cache"); response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0); response.setDateHeader("Expires", 0);
List<WebComponentTranslateVO> translates = webComponentTranslateService.getListByCode("QuotationSystem", MultilingualUtil.getLanguage()); List<WebComponentTranslateVO> translateList = webComponentTranslateService.getListByCode("QuotationSystem", MultilingualUtil.getLanguage());
Map<String, String> translateMap = translateList.stream()
.collect(Collectors.toMap(WebComponentTranslateVO::getComponentCode, WebComponentTranslateVO::getValue, (a, b) -> a));
Document document = new Document(PageSize.A4); Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream()); PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
writer.setPageEvent(new HeaderFooterEvent("images/head.png")); 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<QuotationShoppingCartDTO> 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<String, String> translateMap) throws IOException {
Rectangle pageSize = document.getPageSize(); Rectangle pageSize = document.getPageSize();
//第一页
PdfPTable footerTable = new PdfPTable(1); PdfPTable footerTable = new PdfPTable(1);
footerTable.setTotalWidth(pageSize.getWidth() - document.leftMargin() - document.rightMargin()); footerTable.setTotalWidth(pageSize.getWidth() - document.leftMargin() - document.rightMargin());
footerTable.setLockedWidth(true); footerTable.setLockedWidth(true);
Chunk chunk1 = new Chunk(getText(translates, "title"), new Font(bfChinese, 28, Font.BOLD, new Color(0x3F, 0x3F, 0x3F))); Chunk chunk1 = new Chunk(getText(translateMap, "title"), titleFont);
chunk1.setUnderline(new Color(0xC0, 0x00, 0x00), 5f, 0f, -15f, 0f, PdfContentByte.LINE_CAP_BUTT); chunk1.setUnderline(redColor, 5f, 0f, -15f, 0f, PdfContentByte.LINE_CAP_BUTT);
PdfPCell cell1 = new PdfPCell(new Phrase(chunk1)); PdfPCell cell1 = new PdfPCell(new Phrase(chunk1));
cell1.setHorizontalAlignment(Element.ALIGN_RIGHT); cell1.setHorizontalAlignment(Element.ALIGN_RIGHT);
cell1.setVerticalAlignment(Element.ALIGN_MIDDLE); cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);
@ -124,18 +163,16 @@ public class PdfExportService {
cell1.setBorder(Rectangle.NO_BORDER); cell1.setBorder(Rectangle.NO_BORDER);
footerTable.addCell(cell1); footerTable.addCell(cell1);
PdfPCell cell2 = new PdfPCell(new Phrase(getText(translates, "QuotationDate") + "" PdfPCell cell2 = new PdfPCell(new Phrase(getText(translateMap, "QuotationDate") + ""
+ DateTimeUtil.format(order.getCreateTime(), "yyyy-MM-dd"), new Font(bfChinese, 15, Font.BOLD))); + DateTimeUtil.format(order.getCreateTime(), "yyyy-MM-dd"), boldFont15));
cell2.setHorizontalAlignment(Element.ALIGN_RIGHT); cell2.setHorizontalAlignment(Element.ALIGN_RIGHT);
cell2.setPaddingTop(40); cell2.setPaddingTop(40);
cell2.setBorder(Rectangle.NO_BORDER); cell2.setBorder(Rectangle.NO_BORDER);
footerTable.addCell(cell2); footerTable.addCell(cell2);
Phrase phrase = new Phrase(); Phrase phrase = new Phrase();
Chunk chunk2 = new Chunk(getText(translates, "Validity") + "", new Font(bfChinese, 15, Font.BOLD)); phrase.add(new Chunk(getText(translateMap, "Validity") + "", boldFont15));
phrase.add(chunk2); phrase.add(new Chunk(getText(translateMap, "Validity1"), redBoldFont15));
Chunk chunk3 = new Chunk(getText(translates, "Validity1"), new Font(bfChinese, 15, Font.BOLD, new Color(0xC0, 0x00, 0x00)));
phrase.add(chunk3);
PdfPCell cell3 = new PdfPCell(phrase); PdfPCell cell3 = new PdfPCell(phrase);
cell3.setHorizontalAlignment(Element.ALIGN_RIGHT); cell3.setHorizontalAlignment(Element.ALIGN_RIGHT);
cell3.setPaddingTop(3); cell3.setPaddingTop(3);
@ -143,39 +180,39 @@ public class PdfExportService {
footerTable.addCell(cell3); footerTable.addCell(cell3);
List<DictionaryItem> images = dictionaryItemService.getListByDictionaryCode("PDFCoverImage", MultilingualUtil.getLanguage()); List<DictionaryItem> images = dictionaryItemService.getListByDictionaryCode("PDFCoverImage", MultilingualUtil.getLanguage());
PdfPTable outerTable = new PdfPTable(images.size()); if (CollectionUtil.isNotEmpty(images)) {
outerTable.setWidthPercentage(100); PdfPTable outerTable = new PdfPTable(images.size());
List<Float> widths = new ArrayList<>(); outerTable.setWidthPercentage(100);
images.forEach(method -> { List<Float> widths = new ArrayList<>();
widths.add(1f); images.forEach(method -> widths.add(1f));
}); outerTable.setWidths(Floats.toArray(widths));
outerTable.setWidths(Floats.toArray(widths)); for (int i = 0; i < images.size(); i++) {
for (int i = 0; i < images.size(); i++) { PdfPCell cell = new PdfPCell();
PdfPCell cell = new PdfPCell(); cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setVerticalAlignment(Element.ALIGN_TOP);
cell.setVerticalAlignment(Element.ALIGN_TOP); if (i != 0) {
if (i != 0) { cell.setPaddingLeft(5);
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); PdfPCell cell4 = new PdfPCell(outerTable);
cell.setBorderColor(Color.WHITE); cell4.setPaddingTop(250);
cell.setBorderWidth(5f); cell4.setBorder(Rectangle.NO_BORDER);
Image image = Image.getInstance(downloadImage(images.get(i).getValue())); footerTable.addCell(cell4);
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 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.setPaddingTop(80);
cell5.setVerticalAlignment(Element.ALIGN_MIDDLE); cell5.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell5.setHorizontalAlignment(Element.ALIGN_CENTER); cell5.setHorizontalAlignment(Element.ALIGN_CENTER);
@ -183,160 +220,108 @@ public class PdfExportService {
footerTable.addCell(cell5); footerTable.addCell(cell5);
document.add(footerTable); document.add(footerTable);
document.add(Chunk.NEXTPAGE); }
//开始正文
String userName, userPhone, userEmail; private void addCustomerInfoTable(Document document, QuotationShoppingOrder order, Map<String, String> translateMap) {
String userName = "", userPhone = "", userEmail = "";
if (order.getCreateByType() == 0) { if (order.getCreateByType() == 0) {
AdminUser adminUser = adminUserService.getById(order.getCreateById()); AdminUser adminUser = adminUserService.getById(order.getCreateById());
userName = adminUser.getUserName(); if (Objects.nonNull(adminUser)) {
userPhone = adminUser.getPhone(); userName = adminUser.getUserName();
userEmail = adminUser.getEmail(); userPhone = adminUser.getPhone();
userEmail = adminUser.getEmail();
}
} else { } else {
AppUser appUser = appUserService.getById(order.getCreateById()); AppUser appUser = appUserService.getById(order.getCreateById());
userName = appUser.getName(); if (Objects.nonNull(appUser)) {
userPhone = appUser.getPhone(); userName = appUser.getName();
userEmail = appUser.getEmail(); userPhone = appUser.getPhone();
userEmail = appUser.getEmail();
}
} }
Font labelFont = new Font(bfChinese, 12, Font.BOLD); Font labelFont = new Font(bfChinese, 12, Font.BOLD);
Font valueFont = new Font(bfChinese, 12, Font.NORMAL); Font valueFont = new Font(bfChinese, 12, Font.NORMAL);
PdfPTable table = new PdfPTable(4); PdfPTable table = new PdfPTable(4);
table.setWidthPercentage(100); table.setWidthPercentage(100);
table.setWidths(new float[]{1.5f, 2f, 1.5f, 3f}); table.setWidths(new float[]{1.5f, 2f, 1.5f, 3f});
PdfPCell label1_1 = createLabelCell(getText(translates, "Customer") + "", labelFont); table.addCell(createLabelCell(getText(translateMap, "Customer") + "", labelFont));
table.addCell(label1_1); table.addCell(createValueCell(order.getCustomerName(), valueFont));
PdfPCell value1_1 = createValueCell(order.getCustomerName(), valueFont); table.addCell(createLabelCell(getText(translateMap, "Supplier") + "", labelFont));
table.addCell(value1_1); table.addCell(createValueCell(getText(translateMap, "nflg"), valueFont));
PdfPCell label1_2 = createLabelCell(getText(translates, "Supplier") + "", labelFont); table.addCell(createLabelCell(getText(translateMap, "ContactPerson") + "", labelFont));
table.addCell(label1_2); table.addCell(createValueCell(order.getContactPerson(), valueFont));
PdfPCell value1_2 = createValueCell(getText(translates, "nflg"), valueFont); table.addCell(createLabelCell(getText(translateMap, "ContactPerson") + "", labelFont));
table.addCell(value1_2); table.addCell(createValueCell(userName, valueFont));
PdfPCell label2_1 = createLabelCell(getText(translates, "ContactPerson") + "", labelFont); table.addCell(createLabelCell(getText(translateMap, "ContactInformation") + "", labelFont));
table.addCell(label2_1); table.addCell(createValueCell(order.getContactInformation(), valueFont));
PdfPCell value2_1 = createValueCell(order.getContactPerson(), valueFont); table.addCell(createLabelCell(getText(translateMap, "ContactInformation") + "", labelFont));
table.addCell(value2_1); table.addCell(createValueCell(userPhone, valueFont));
PdfPCell label2_2 = createLabelCell(getText(translates, "ContactPerson") + "", labelFont); table.addCell(createLabelCell(getText(translateMap, "Email") + "", labelFont));
table.addCell(label2_2); table.addCell(createValueCell(order.getContactEmail(), valueFont));
PdfPCell value2_2 = createValueCell(userName, valueFont); table.addCell(createLabelCell(getText(translateMap, "Email") + "", labelFont));
table.addCell(value2_2); table.addCell(createValueCell(userEmail, valueFont));
PdfPCell label3_1 = createLabelCell(getText(translates, "ContactInformation") + "", labelFont); table.addCell(createLabelCell(getText(translateMap, "Country") + "", labelFont));
table.addCell(label3_1); table.addCell(createValueCell(order.getContactCountry(), valueFont));
PdfPCell value3_1 = createValueCell(order.getContactInformation(), valueFont); table.addCell(createLabelCell(getText(translateMap, "QuotationNo") + "", labelFont));
table.addCell(value3_1); table.addCell(createValueCell(order.getNo(), valueFont));
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);
document.add(table); 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<QuotationShoppingCartDTO> 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<WebComponentTranslateVO> translates, String code) { private void addQuoteList(Document document, QuotationShoppingOrder order, Map<String, String> translateMap) {
return translates.stream() PdfPTable quoteTable = new PdfPTable(5);
.filter(translate -> StrUtil.equals(translate.getComponentCode(), code)) quoteTable.setWidthPercentage(100);
.findFirst() quoteTable.setWidths(new float[]{10f, 40f, 20f, 15f, 15f});
.map(WebComponentTranslateVO::getValue) quoteTable.addCell(createBJQDCell(getText(translateMap, "QuoteList"), 5, boldFont13));
.orElse(""); 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<QuotationShoppingCartDTO> 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<String, String> translateMap, String code) {
return translateMap.getOrDefault(code, "");
} }
private void addRandomAccessories(Document document, List<QuotationShoppingCartAccessory> accessories private void addRandomAccessories(Document document, List<QuotationShoppingCartAccessory> accessories
, List<WebComponentTranslateVO> translates) { , Map<String, String> translateMap) {
if (CollectionUtil.isNotEmpty(accessories)) { 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); PdfPTable table = new PdfPTable(5);
table.setWidthPercentage(100); table.setWidthPercentage(100);
table.setSpacingBefore(10f); table.setSpacingBefore(10f);
table.setWidths(new float[]{20f, 20f, 20f, 20f, 20f}); table.setWidths(new float[]{20f, 20f, 20f, 20f, 20f});
//表头 //表头
table.addCell(createBJQDCell(getText(translates, "MaterialNo"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "MaterialNo"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "MaterialName"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "MaterialName"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "Num"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Num"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "Price"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Price"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "TotalPrice"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "TotalPrice"), 1, normalFont));
accessories.forEach(accessory -> { accessories.forEach(accessory -> {
table.addCell(createBJQDCell(accessory.getMaterialNo(), 1, normalFont)); table.addCell(createBJQDCell(accessory.getMaterialNo(), 1, normalFont));
table.addCell(createBJQDCell(accessory.getName(), 1, normalFont)); table.addCell(createBJQDCell(accessory.getName(), 1, normalFont));
@ -348,20 +333,20 @@ public class PdfExportService {
} }
} }
private void addJJFF(Document document, List<QuotationShoppingCartService> services, List<WebComponentTranslateVO> translates) { private void addJJFF(Document document, List<QuotationShoppingCartService> services, Map<String, String> translateMap) {
if (CollectionUtil.isNotEmpty(services)) { 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); PdfPTable table = new PdfPTable(6);
table.setWidthPercentage(100); table.setWidthPercentage(100);
table.setSpacingBefore(10f); table.setSpacingBefore(10f);
table.setWidths(new float[]{15f, 15f, 15f, 15f, 20f, 20f}); table.setWidths(new float[]{15f, 15f, 15f, 15f, 20f, 20f});
//表头 //表头
table.addCell(createBJQDCell(getText(translates, "Numberofpeople"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Numberofpeople"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "Days"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Days"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "Price"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Price"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "TotalPrice"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "TotalPrice"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "Address"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Address"), 1, normalFont));
table.addCell(createBJQDCell(getText(translates, "Remark"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Remark"), 1, normalFont));
for (QuotationShoppingCartService service : services) { for (QuotationShoppingCartService service : services) {
table.addCell(createBJQDCell(String.valueOf(service.getUserNum()), 1, normalFont)); table.addCell(createBJQDCell(String.valueOf(service.getUserNum()), 1, normalFont));
table.addCell(createBJQDCell(String.valueOf(service.getDays()), 1, normalFont)); table.addCell(createBJQDCell(String.valueOf(service.getDays()), 1, normalFont));
@ -376,7 +361,10 @@ public class PdfExportService {
private void addPriceInfo(Document document, List<OrderDeliveryMethodDTO> deliveryMethods private void addPriceInfo(Document document, List<OrderDeliveryMethodDTO> deliveryMethods
, List<ModelConfigEffectiveDTO> parts, QuotationShoppingOrder order, QuotationShoppingCartDTO cart , List<ModelConfigEffectiveDTO> parts, QuotationShoppingOrder order, QuotationShoppingCartDTO cart
, List<WebComponentTranslateVO> translates) { , Map<String, String> translateMap) {
if (CollectionUtil.isEmpty(deliveryMethods)) {
return;
}
PdfPTable table = new PdfPTable(deliveryMethods.size() + 1); PdfPTable table = new PdfPTable(deliveryMethods.size() + 1);
table.setWidthPercentage(100); table.setWidthPercentage(100);
table.setKeepTogether(true); table.setKeepTogether(true);
@ -384,9 +372,7 @@ public class PdfExportService {
List<Float> widths = new ArrayList<>(); List<Float> widths = new ArrayList<>();
widths.add(20f); widths.add(20f);
Float width = 80f / deliveryMethods.size(); Float width = 80f / deliveryMethods.size();
deliveryMethods.forEach(method -> { deliveryMethods.forEach(method -> widths.add(width));
widths.add(width);
});
table.setWidths(Floats.toArray(widths)); table.setWidths(Floats.toArray(widths));
//列头 //列头
table.addCell(createBJQDCell("", 1, normalFont)); table.addCell(createBJQDCell("", 1, normalFont));
@ -394,33 +380,34 @@ public class PdfExportService {
table.addCell(createBJQDCell(method.getDeliveryMethodName() + " " + order.getContactCountry(), 1, normalFont)) table.addCell(createBJQDCell(method.getDeliveryMethodName() + " " + order.getContactCountry(), 1, normalFont))
); );
DictionaryItem currency = dictionaryItemService.getById(order.getCurrency()); 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(); BigDecimal totalPrice = cart.getStandardFee();
deliveryMethods.forEach(method -> deliveryMethods.forEach(method ->
table.addCell(createBJQDCell( table.addCell(createBJQDCell(
format(totalPrice.add(method.getDeliveryFee()).multiply(order.getExchangeRate())) + " " + currency.getValue() format(totalPrice.add(method.getDeliveryFee()).multiply(order.getExchangeRate())) + " " + currencyValue
, 1 , 1
, normalFont) , normalFont)
) )
); );
//可选配置 //可选配置
PdfPCell ckx = createBJQDCell(getText(translates, "OptionalConfiguration"), table.getNumberOfColumns(), normalFont); PdfPCell optionalCell = createBJQDCell(getText(translateMap, "OptionalConfiguration"), table.getNumberOfColumns(), normalFont);
ckx.setHorizontalAlignment(Element.ALIGN_LEFT); optionalCell.setHorizontalAlignment(Element.ALIGN_LEFT);
ckx.setPaddingLeft(25); optionalCell.setPaddingLeft(25);
table.addCell(ckx); table.addCell(optionalCell);
parts.forEach(part -> { parts.forEach(part -> {
table.addCell(createBJQDCell(part.getPartName(), 1, normalFont)); table.addCell(createBJQDCell(part.getPartName(), 1, normalFont));
deliveryMethods.forEach(method -> deliveryMethods.forEach(method ->
table.addCell(createBJQDCell( 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 , 1
, normalFont) , normalFont)
) )
); );
}); });
//合计 //合计
table.addCell(createBJQDCell(getText(translates, "Total"), 1, normalFont)); table.addCell(createBJQDCell(getText(translateMap, "Total"), 1, normalFont));
deliveryMethods.forEach(method -> deliveryMethods.forEach(method ->
table.addCell(createBJQDCell( table.addCell(createBJQDCell(
format( format(
@ -431,7 +418,7 @@ public class PdfExportService {
.reduce(BigDecimal.ZERO, BigDecimal::add) .reduce(BigDecimal.ZERO, BigDecimal::add)
) )
).add(method.getDeliveryFee()).multiply(order.getExchangeRate()) ).add(method.getDeliveryFee()).multiply(order.getExchangeRate())
) + " " + currency.getValue() ) + " " + currencyValue
, 1 , 1
, normalFont) , normalFont)
) )
@ -444,8 +431,9 @@ public class PdfExportService {
} }
private void addProductModelInfo(Document document, List<QuotationShoppingCartDTO> carts private void addProductModelInfo(Document document, List<QuotationShoppingCartDTO> carts
, QuotationShoppingOrder order, List<WebComponentTranslateVO> translates) throws IOException { , QuotationShoppingOrder order, Map<String, String> translateMap) throws IOException {
document.add(Chunk.NEXTPAGE); document.add(Chunk.NEXTPAGE);
List<OrderDeliveryMethodDTO> deliveryMethods = shoppingOrderDeliveryMethodService.getList(order.getId());
for (QuotationShoppingCartDTO cart : carts) { 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.getTypeName(), new Font(bfChinese, 18, Font.NORMAL))));
document.add(new Paragraph(new Chunk(cart.getModelNo(), new Font(bfChinese, 14, Font.BOLD)))); 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); image.scaleToFit(getMaxContentWidth(document), 1000f);
document.add(image); document.add(image);
ProductModelIntroItem introItem = productModelIntroItemService.getByModelId(cart.getModelId(), MultilingualUtil.getLanguage()); 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()); addHtmlContent(document, introItem.getDesc());
} }
//部件 //部件
@ -476,15 +464,15 @@ public class PdfExportService {
); );
}); });
//运输信息 //运输信息
Paragraph yscc = new Paragraph(); Paragraph shippingDimParagraph = new Paragraph();
yscc.add(new Chunk(getText(translates, "ShippingDimensions") + "", new Font(bfChinese, 13, Font.BOLD))); shippingDimParagraph.add(new Chunk(getText(translateMap, "ShippingDimensions") + "", boldFont13));
yscc.add(new Chunk(parts.stream() shippingDimParagraph.add(new Chunk(parts.stream()
.max(Comparator.comparingLong(ModelConfigEffectiveDTO::getShippingDimensionsValue)) .max(Comparator.comparingLong(ModelConfigEffectiveDTO::getShippingDimensionsValue))
.get() .map(ModelConfigEffectiveDTO::getShippingDimensions)
.getShippingDimensions() .orElse("")
, new Font(bfChinese, 13, Font.NORMAL))); , normalFont13));
yscc.setSpacingBefore(40f); shippingDimParagraph.setSpacingBefore(40f);
document.add(yscc); document.add(shippingDimParagraph);
BigDecimal weight = BigDecimal.ZERO; BigDecimal weight = BigDecimal.ZERO;
for (ModelConfigEffectiveDTO part : parts) { for (ModelConfigEffectiveDTO part : parts) {
if (Objects.nonNull(part.getWeight())) { if (Objects.nonNull(part.getWeight())) {
@ -493,45 +481,47 @@ public class PdfExportService {
} }
} }
} }
Paragraph zl = new Paragraph(); Paragraph weightParagraph = new Paragraph();
zl.add(new Chunk(getText(translates, "Weight") + "", new Font(bfChinese, 13, Font.BOLD))); weightParagraph.add(new Chunk(getText(translateMap, "Weight") + "", boldFont13));
zl.add(new Chunk(format(weight) + "kg", new Font(bfChinese, 13, Font.NORMAL))); weightParagraph.add(new Chunk(format(weight) + "kg", normalFont13));
document.add(zl); document.add(weightParagraph);
//质保服务 //质保服务
String zbfwText = cart.getWarrantyServiceDesc(); String warrantyText = cart.getWarrantyServiceDesc();
boolean iss = false; boolean hasDicWarranty = false;
if (Objects.nonNull(cart.getWarrantyServiceDicId())) { if (Objects.nonNull(cart.getWarrantyServiceDicId())) {
DictionaryItem warrantyService = dictionaryItemService.getByIdAndLanguage(cart.getWarrantyServiceDicId(), MultilingualUtil.getLanguage()); DictionaryItem warrantyService = dictionaryItemService.getByIdAndLanguage(cart.getWarrantyServiceDicId(), MultilingualUtil.getLanguage());
zbfwText = warrantyService.getName(); if (Objects.nonNull(warrantyService)) {
iss = true; warrantyText = warrantyService.getName();
hasDicWarranty = true;
}
} }
Paragraph zbfw = new Paragraph(); Paragraph warrantyParagraph = new Paragraph();
zbfw.add(new Chunk(getText(translates, "WarrantyService") + "", new Font(bfChinese, 13, Font.BOLD))); warrantyParagraph.add(new Chunk(getText(translateMap, "WarrantyService") + "", boldFont13));
if (iss) { if (hasDicWarranty) {
zbfw.add(new Chunk(zbfwText, new Font(bfChinese, 13, Font.NORMAL))); warrantyParagraph.add(new Chunk(warrantyText, normalFont13));
} else { } 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() addJJFF(document, shoppingCartServiceService.lambdaQuery()
.eq(QuotationShoppingCartService::getCartId, cart.getId()) .eq(QuotationShoppingCartService::getCartId, cart.getId())
.list(), translates .list(), translateMap
); );
//随机配件 //随机配件
addRandomAccessories(document, shoppingCartAccessoryService.lambdaQuery() addRandomAccessories(document, shoppingCartAccessoryService.lambdaQuery()
.eq(QuotationShoppingCartAccessory::getCartId, cart.getId()) .eq(QuotationShoppingCartAccessory::getCartId, cart.getId())
.list(), translates .list(), translateMap
); );
//价格信息 //价格信息
addPriceInfo(document addPriceInfo(document
, shoppingOrderDeliveryMethodService.getList(order.getId()) , deliveryMethods
, parts.stream() , parts.stream()
.filter(part -> part.getParentId() > 0 && part.getType() == 1 && Objects.equals(part.getOptionalType(), 0)) .filter(part -> part.getParentId() > 0 && part.getType() == 1 && Objects.equals(part.getOptionalType(), 0))
.collect(Collectors.toList()) .collect(Collectors.toList())
, order , order
, cart , cart
, translates); , translateMap);
} }
} }
@ -585,13 +575,6 @@ public class PdfExportService {
return cell; 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 { private void addHtmlContent(Document document, String html) throws IOException {
StyleSheet styles = new StyleSheet(); StyleSheet styles = new StyleSheet();
@ -654,25 +637,21 @@ public class PdfExportService {
private BaseFont getChineseFont() { private BaseFont getChineseFont() {
try { try {
// 1. classpath 获取字体流 String fontPath = "fonts/simhei.ttf";
String fontPath = "fonts/simhei.ttf"; // 对应 resources/fonts/simsun.ttf try (InputStream inputStream = QuotationApplication.class.getClassLoader().getResourceAsStream(fontPath)) {
InputStream inputStream = QuotationApplication.class.getClassLoader().getResourceAsStream(fontPath); if (inputStream == null) {
if (inputStream == null) { throw new RuntimeException("找不到字体文件: " + fontPath);
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) { } catch (Exception e) {
throw new RuntimeException("加载中文字体失败", e); throw new RuntimeException("加载中文字体失败", e);
} }
} }
private String format(BigDecimal price) { private String format(BigDecimal price) {
return df.format(price); return decimalFormatLocal.get().format(price);
} }
private byte[] downloadImage(String urlStr) { private byte[] downloadImage(String urlStr) {
@ -680,17 +659,12 @@ public class PdfExportService {
return defaultPic; return defaultPic;
} }
try { try {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10)) // 连接超时 10秒
.followRedirects(HttpClient.Redirect.NORMAL) // 自动处理重定向
.build();
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(urlStr)) .uri(URI.create(urlStr))
.timeout(Duration.ofSeconds(15)) // 读取超时 15秒 .timeout(Duration.ofSeconds(15))
// 某些图片服务器有防盗链可以模拟浏览器 User-Agent
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)") .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.build(); .build();
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
return response.body(); return response.body();
} else { } else {