一、准备pom依赖
com.itextpdf
html2pdf
1.0.2
org.freemarker
freemarker
2.3.28
二、准备sparePartsProcurePoCatalogTemplate.html
相对路径:templates\attrchs\sparePartsProcurePoCatalogTemplate.html
订单时间:${orderTime!} |
订单编号:${orderCode!} |
需方(甲方):${partyAName!} |
供方(乙方):${partyBName!} |
授权代表:${demandAgent!} |
授权代表:${supplierAgent!} |
地址:${demandAddress!} |
地址:${supplierAddress!} |
联系方式:${demandContactsStyle!} |
联系方式:${supplierContactsStyle!} |
经双方友好协议确认,根据下列条款订立本合同: |
一、内容:(甲方向乙方订购以下货物): |
序号 |
零配件SKU名称 |
零配件SKU编码 |
适用车型 |
品牌 |
零配件型号 |
单位 |
数量 |
含税单价(元) |
含税金额(元) |
收货人 |
收货人联系方式 |
收货地址 |
预估交期 |
<#list a as detail >
${detail.sequence} |
${detail.skuName} |
${detail.skuCode} |
${detail.brandModelName} |
${detail.brandName} |
${detail.modelName} |
${detail.productUnitName} |
${detail.purchaseQuantity} |
${detail.unitPriceIncludeTax} |
${detail.includeTotalAmount} |
${detail.receivedPerson} |
${detail.receivedPhone} |
${detail.receivedAddress} |
${detail.estimateTime} |
#list>
货款合计(大写):(含税费及运费) |
${totalAmountStr!} |
小写合计 |
${totalAmount!} |
备注 |
${data.remark} |
二、本订单未约定事宜以关联采购合同编号: ${data.contractCode!}为准。 |
三、其他约定 |
1)收货允差天数:正三负零,即提前三天范围内交货是可以接受的,禁止晚交货。 |
2)供方在收到订单后,请确认品名规格、数量、交期并于1个工作日内签回。 |
3)本订单未约定的内容以框架合同为准;本订单约定的内容与框架合同不一致的,以本订单为准。 |
4)本订单中的物料明细不在关联框架合同和补充协议的《供应商产品价格确认表》中的,关于该物料的相关约束使用本订单关联框架合同所有的条款约定 。 |
5)本合同正本一式贰份,甲乙双方各执壹份,经双方盖章生效;甲方通过指定邮箱向乙方下达的订单与原合同具有同等效力。 |
甲方 |
乙方 |
甲 方(盖章): ${partyAName!} |
乙 方(盖章): ${partyBName!} |
授权代表(签字): |
授权代表(签字): |
三、准备转换代码
/**
* 生成 PDF 文件
*
* @param procurePoDTO
*/
public void generatePdfFile(SparePartsProcurePoDTO procurePoDTO,SSOUser ssoUser) {
this.generatePoCatalogPdfToOss(procurePoDTO,ssoUser);
}
/**
* @Description 生成pdf到阿里云
* @Param procurePoDTO
* @Return void
* @Date 2023/10/20 10:24
* @Author lwp
*/
public String generatePoCatalogPdfToOss(SparePartsProcurePoDTO procurePoDTO,SSOUser ssoUser) {
SparePartsProcurePo sparePartsProcurePo = procurePoDTO.getSparePartsProcurePo();
String orderCode = sparePartsProcurePo.getOrderCode();
String htmlStr = this.getHtmlFromPoCatalog(procurePoDTO);
String url = "";
//渲染pdf
File file = null;
String name = AliOSSUtil.generateUUIDName(".pdf");
try {
File fileDir = new File(System.getProperty("user.dir") + "/pdf/");
if (!fileDir.exists()) {
fileDir.mkdirs();
}
file = new File(fileDir.getPath() + "/" + name);
file.createNewFile();
log.info("采购订单:{} 创建临时文件 {} 是否创建成功:{}", orderCode, file.getPath(), file.exists());
} catch (IOException e) {
log.error("采购订单:{} PDF生成失败", orderCode, e);
}
try (FileOutputStream fos = new FileOutputStream(file)) {
PdfSupport.convertStringToPdf(htmlStr, fos);
log.info("采购订单:{} 上传文件 {} 到阿里云OSS,文件大小 {} byte", orderCode, file.getName(), file.length());
url = aliossClient.uploadFile(file, "/procurePo/basic/pdf/"+name);
log.info("采购订单:{} PDFURL:{} 上传到阿里云OSS成功", orderCode, url);
if (Objects.nonNull(ssoUser)) {
uploadExcel(AliOSSHelper.getAccessUrl(url), "易维&四川泰瑞达新能源汽车有限公司-采购订单", ssoUser);
}
log.info("采购订单:{} PDFURL:{} 数据库更新成功", orderCode, url);
} catch (IOException e) {
log.error("采购订单:{} PDF生成失败", orderCode, e);
} finally {
if (file.exists()) {
file.delete();
}
}
return url;
}
/**
* @Description 解析生成html格式
* @Param procurePoDTO
* @Return java.lang.String
* @Date 2023/10/20 10:25
* @Author lwp
*/
public String getHtmlFromPoCatalog(SparePartsProcurePoDTO procurePoDTO) {
SpareParsProcurePoCatalogTemplateData data = buildData(procurePoDTO);
Map params = new HashMap<>();
params.put("orderTime",data.getOrderTime());// 订单时间
params.put("orderCode", data.getOrderCode());// 订单编号
params.put("partyAName", data.getPartyAName());// 需方(甲方)
params.put("partyBName", data.getPartyBName());// 供方(乙方)
params.put("demandAgent", data.getDemandAgent());// 甲方授权代表
params.put("supplierAgent", data.getSupplierAgent());// 乙方授权代表
params.put("demandAddress", data.getDemandAddress());// 甲方地址
params.put("supplierAddress", data.getSupplierAddress());// 乙方地址
params.put("demandContactsStyle", data.getDemandContactsStyle());// 甲方联系方式
params.put("supplierContactsStyle", data.getSupplierContactsStyle());// 乙方联系方式
params.put("totalAmountStr", data.getTotalAmountStr());// 货款合计(大写):(含税费及运费)
params.put("totalPurchaseQuantity", data.getTotalPurchaseQuantity());// 总数量
params.put("totalAmount", data.getTotalAmount());// 小写合计
params.put("contractCode", data.getContractCode());// 合同编码
params.put("paymentMethodName",data.getPaymentMethodName()); //结算方式
params.put("invoiceTypeName",data.getInvoiceTypeName()); //发票类型
params.put("a", data.getDetails());// 采购订单明细列表
return FreemarkerHelper.renderHtml("attrchs/sparePartsProcurePoCatalogTemplate.html", params);
}
/**
* 上传到导出记录
* @param url
* @param fileName
*/
public void uploadExcel(String url, String fileName, SSOUser ssoUser){
Date date = new Date();
ExportRecord exportRecord = new ExportRecord();
exportRecord.setFileUrl(url);
exportRecord.setFileName(fileName);
exportRecord.setModule(10);
exportRecord.setModuleName("零配件采购订单");
//处理完成
exportRecord.setStatus(2);
exportRecord.setSource(0);
exportRecord.setFileClassification(1);
exportRecord.setRequestMethod("GET");
exportRecord.setServerName("dst-operate-basic-api");
exportRecord.setFileType(FileTypeConstant.PDF);
exportRecord.setCreateUserId(ssoUser.getId());
exportRecord.setCreateUserName(ssoUser.getRealname());
exportRecord.setCreateTime(date);
exportRecord.setUpdateUserId(ssoUser.getId());
exportRecord.setUpdateUserName(ssoUser.getRealname());
exportRecord.setUpdateTime(date);
exportRecordService.save(exportRecord);
}
四、转换工具 FreemarkerHelper
package com.dst.common.service;
import freemarker.cache.URLTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class FreemarkerHelper {
private static Configuration config;
static {
config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
config.setClassicCompatible(true);
config.setLocalizedLookup(false);
config.setDateFormat("yyyy-MM-dd");
config.setDateTimeFormat("yyyy-MM-dd HH:mm:ss");
config.setDefaultEncoding("UTF-8");
}
public static String renderHtml(String tplName, Map dataModel){
String htmlStr = "";
try {
config.setClassLoaderForTemplateLoading(FreemarkerHelper.class.getClassLoader(),"/templates/");
Template tpl = config.getTemplate(tplName);
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
tpl.process(dataModel,writer);
htmlStr = stringWriter.toString();
} catch (IOException | TemplateException e) {
log.error("【renderHtml】处理html异常:",e);
}
return htmlStr;
}
public static String renderHtmlFromUrl(String tplUrl, Map dataModel){
String htmlStr = "";
try {
config.setTemplateLoader(new RemoteTemplateLoader());
Template tpl = config.getTemplate(tplUrl);
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
tpl.process(dataModel,writer);
htmlStr = stringWriter.toString();
} catch (IOException | TemplateException e) {
log.error("【renderHtmlFromUrl】处理html异常:",e);
}
return htmlStr;
}
static class RemoteTemplateLoader extends URLTemplateLoader{
@Override
protected URL getURL(String name) {
URL url = null;
try {
url = new URL(name);
} catch (MalformedURLException e) {
log.error("【getURL】获取URL异常:",e);
}
return url;
}
}
public static void main(String[] args) throws Exception {
/*URL url = new URL("http://dst2.oss-cn-shenzhen.aliyuncs.com/test_contract_template_af3404a51225458da3ae5a587076f1cf");
URLConnection connection = url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
reader.lines().forEach(line -> System.out.println(line));*/
Map dataModel = new HashMap<>();
dataModel.put("myDate",new Date());
String result = renderHtml("test.html",dataModel);
System.out.println(result);
// System.out.println(InetAddress.getByName("dst2.oss-cn-shenzhen.aliyuncs.com").getHostAddress());
}
}
五、HTML转PDF工具类PdfSupport
package com.dst.common.service;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.attach.ITagWorkerFactory;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.font.FontProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
public abstract class PdfSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(PdfSupport.class);
public static FontProgram simsun;
public static FontProgram awesome;
public static ImageData logo;
public static FontProvider fontProvider;
public static FontProvider createFontProvider() {
if (fontProvider == null){
fontProvider = new FontProvider();
fontProvider.addStandardPdfFonts();
fontProvider.addFont(simsun);
fontProvider.addFont(awesome);
}
return fontProvider;
}
protected static ConverterProperties createConverterProperties(){
ConverterProperties prop = new ConverterProperties();
prop.setFontProvider(createFontProvider());
// prop.setBaseUri("/static");
return prop;
}
public static void convertToPdf(String htmlStr, OutputStream pdfStream, ConverterProperties converterProperties) throws IOException {
HtmlConverter.convertToPdf(htmlStr,pdfStream,converterProperties);
}
public static void convertStringToPdf(String htmlStr, OutputStream pdfStream, ITagWorkerFactory tagWorkerFactory) throws IOException {
ConverterProperties converterProperties = createConverterProperties();
converterProperties.setTagWorkerFactory(tagWorkerFactory);
HtmlConverter.convertToPdf(htmlStr,pdfStream,converterProperties);
}
public static void convertStringToPdf(String htmlStr, OutputStream pdfStream) throws IOException {
HtmlConverter.convertToPdf(htmlStr,pdfStream,createConverterProperties());
}
public static PdfDocument createPdfDocument(OutputStream outputStream) throws Exception {
return new PdfDocument(new PdfWriter(outputStream));
}
public static Document convertToDocument(String html, PdfDocument pdfDoc, ConverterProperties prop) throws IOException {
return HtmlConverter.convertToDocument(html,pdfDoc,prop);
}
public void outFile(String fileName, String htmlStr,HttpServletResponse response) {
try {
String value = "attachment;filename=" + new String((fileName + System.currentTimeMillis() + ".pdf").getBytes("gb2312"), "ISO8859-1");
response.setHeader("Content-Disposition", value);
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");//设置头
response.setDateHeader("Expires", 0);//设置日期头
OutputStream out = response.getOutputStream();
convertToPdf(htmlStr,out);
out.flush();
} catch (Exception e) {
LOGGER.error("OutputStream error", e);
}
}
protected abstract void convertToPdf(String htmlStr, OutputStream out) throws Exception;
}
六、转换实体类
package com.dstcar.entitys.sparepartsprocurepo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpareParsProcurePoCatalogTemplateData implements Serializable {
/** 采购订单号 规则:CGDD+日期+流水号,按日期累加 **/
private String orderCode;
/** 订单日期 **/
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private String orderTime;
/** 甲方名称 **/
private String partyAName;
/** 乙方名称 **/
private String partyBName;
/** 需方授权代表 **/
private String demandAgent;
/** 供方授权代表 **/
private String supplierAgent;
/** 需方地址 **/
private String demandAddress;
/** 供方地址 **/
private String supplierAddress;
/** 需方联系方式 **/
private String demandContactsStyle;
/** 供方联系方式 **/
private String supplierContactsStyle;
/** 结算方式:1月结30天|2月结60天|3月结90天|4先款后货(字典:spare_parts_payment_method) **/
private String paymentMethodName;
/** 发票类型:1增值税发票2普通发票(字典:spare_parts_po_invoice_type) **/
private String invoiceTypeName;
/** 订单总额(产品明细含税总额合计【含税总额(含税单价unitPriceIncludeTax * 采购数量purchaseQuantity)】) **/
private BigDecimal totalAmount;
/**
* 订单总额大写文字
*/
private String totalAmountStr;
/**
* 总的采购数量
*/
private BigDecimal totalPurchaseQuantity;
/** 备注 **/
private String remark;
/** 关联合同号 **/
private String contractCode;
/** po订单明细 */
private List details;
}
package com.dstcar.entitys.sparepartsprocurepo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpareParsProcurePoCatalogSkuTemplateData implements Serializable {
/**
* 序号
*/
private Integer sequence;
/** 零配件sku名称 **/
private String skuName;
/** 零部件SKU编码 **/
private String skuCode;
/**
* 适用车型名称
* 备注:适用车型下拉框选择‘通用’时该字段为空
* 适用车型下拉框选择‘部分车型适用’时该字段翻译(适用车型里面的品牌+型号拼接而成)
*/
private String brandModelName;
/**
* 零配件品牌名称
*/
private String brandName;
/**
* 零配件型号名称
*/
private String modelName;
/** 计量单位 */
private String productUnitName;
/** 采购数量 **/
private BigDecimal purchaseQuantity;
/** 含税单价 **/
private BigDecimal unitPriceIncludeTax;
/** 含税总额(含税单价*采购数量) **/
private BigDecimal includeTotalAmount;
/** 收货人 **/
private String receivedPerson;
/** 收货人联系电话 **/
private String receivedPhone;
/** 收货人地址 **/
private String receivedAddress;
/** 预估交期 **/
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private String estimateTime;
}
七、最终转换效果