package com.ruoyi.service.impl; import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.PdfBaseWriter; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.domain.vo.EnterpriseFilePdfVO; import com.ruoyi.domain.vo.InspectionReportPdfVO; import com.ruoyi.domain.vo.ReviewEnterpriseArchiveViewVO; import com.ruoyi.mapper.TaskMapper; import com.ruoyi.service.PdfTemplateManagementService; import com.ruoyi.service.ReviewEnterpriseArchiveService; import com.ruoyi.system.mapper.SysDictDataMapper; import io.minio.MinioClient; import io.minio.ObjectWriteArgs; import io.minio.PutObjectArgs; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.List; /** * PDF模板管理Service业务层处理 * @author gxk */ @Service @Transactional public class PdfTemplateManagementServiceImpl implements PdfTemplateManagementService { @Value("${minio.bucketName}") private String bucketName; @Value("${minio.url}") private String minioEndpoint; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Autowired private ReviewEnterpriseArchiveService reviewEnterpriseArchiveService; @Autowired private TaskMapper taskMapper; @Autowired private SysDictDataMapper dictDataMapper; /** * 检验报告PDF下载 * * @param taskId * @return */ @Override public String generateInspectionReport(Long taskId) throws Exception { PdfReader reader = null; PdfStamper ps = null; ByteArrayOutputStream bos = null; try { // 模板绝对路径--服务器 String fileName = "/template/receipt_template_02_27.pdf"; // 读取现有模板内容 reader = new PdfReader(fileName); // 创建输出流 bos = new ByteArrayOutputStream(); // 实例化PdfStamper准备编辑pdf内容 ps = new PdfStamper(reader, bos); // 获取表单所有元素 AcroFields fields = ps.getAcroFields(); // 设置具体字体格式的编码, 不设置的话中文可能不会显示 BaseFont bf = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); fields.addSubstitutionFont(bf); // 测试数据 InspectionReportPdfVO inspectionReportPdfVO = requireData(taskId); // 渲染数据 renderData(fields, inspectionReportPdfVO); //必须要调用这个,否则文档不会生成的 ps.setFormFlattening(true); ps.close(); }catch (Exception e){ e.printStackTrace(); }finally { if (bos != null){ bos.close(); } if(reader != null){ reader.close(); } } assert bos != null; return uploadMinio(bos, "检验报告_" + taskId); } /** * 企业留档文件PDF下载 * @param taskId * @return * @throws Exception */ @Override public String generateRetentionFile(Long taskId) throws Exception { // 活数据查询 EnterpriseFilePdfVO data = getEnterpriseFileData(taskId); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 新建 PdfBaseWriter document = new PdfBaseWriter(outputStream); // 打开 document.open(); // 内容 // 封面 document.setParagraph(data.getInspectionCenterName(), 12f, 0, 60,15f, 120f); document.setParagraph("企业留档文件", 30f, Element.ALIGN_CENTER, 0,15f, 180f); PdfPTable customTable = new PdfPTable(new float[]{15, 50}); customTable.setWidthPercentage(70); document.writeNoBorderCell("检验依据:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getInspectionBasis(), Element.ALIGN_LEFT, 1, 1, customTable); document.writeNoBorderCell("检验项目:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getInspectionItem(), Element.ALIGN_LEFT, 1, 1, customTable); document.writeNoBorderCell("创建时间:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getCreateTime(), Element.ALIGN_LEFT, 1, 1, customTable); document.writeNoBorderCell("委托单位名称:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getClientName(), Element.ALIGN_LEFT, 1, 1, customTable); document.addContent(customTable); document.setParagraph(data.getInspectionCenterName(), 20f, Element.ALIGN_CENTER, 0, 100f, 0f); // 下一页 document.newPage(); document.setParagraph(data.getInspectionCenterName(), 12f, 0, 30,15f, 20f); // 创建表格 List<String> list = Arrays.asList("文件名称", "文件目录", "标准章节", "标准要求", "审查要点"); float[] columnWidths = {30, 25, 25, 60, 60}; PdfPTable table = document.createWithHeaderTable(list, columnWidths); // 查询数据 List<ReviewEnterpriseArchiveViewVO> viewList = reviewEnterpriseArchiveService.view(taskId); for (ReviewEnterpriseArchiveViewVO view : viewList) { document.writeCell(view.getFileName(), Element.ALIGN_LEFT, 1, view.getItems().size(), table); view.getItems().forEach(item -> { document.writeCell(item.getCatalogue(), Element.ALIGN_LEFT, 1, 1, table); document.writeCell(item.getChapter(), Element.ALIGN_LEFT, 1, 1, table); document.writeCell(item.getStandard(), Element.ALIGN_LEFT, 1, 1, table); document.writeCell(item.getKeyPoint(), Element.ALIGN_LEFT, 1, 1, table); }); } document.addContent(table); // 最后结尾签字 PdfPTable signTable = new PdfPTable(new float[]{40, 15}); signTable.setWidthPercentage(100); signTable.setSpacingBefore(40); document.writeNoBorderCell("车企负责人签字:", Element.ALIGN_RIGHT, 1, 1, signTable); document.writeBottomCell("", Element.ALIGN_RIGHT, 1, 1, signTable); document.addContent(signTable); // 关闭 document.close(); // 上传到Minio return uploadMinio(outputStream, "企业留档文件_" + taskId); } /** * 获取企业留档文件数据 * @param taskId * @return */ private EnterpriseFilePdfVO getEnterpriseFileData(Long taskId) { EnterpriseFilePdfVO enterpriseFileData = taskMapper.selectEnterpriseFileData(taskId); enterpriseFileData.setInspectionItem(getInspectionItemByTaskItem(enterpriseFileData.getInspectionItem())); return enterpriseFileData; } /** * 获取当前项目的检验项目 * @return */ private String getInspectionItemByTaskItem(String inspectionItem) { List<SysDictData> dictDataList = dictDataMapper.selectDictDataByType("inspection_item"); List<String> list = Arrays.asList(inspectionItem.split(",")); StringBuilder taskItem = new StringBuilder(); for (String item : list) { Optional<SysDictData> first = dictDataList.stream().filter(data -> data.getDictValue().equals(item)).findFirst(); if (first.isPresent()) { SysDictData dictData = first.get(); taskItem.append(dictData.getDictLabel()).append("、"); } } return taskItem.substring(0, taskItem.length() - 1); } /** * 上传到minio * @param outputStream 输出流 * @param fileName 文件名 * @return minio存储地址 */ private String uploadMinio(ByteArrayOutputStream outputStream, String fileName) throws Exception { // 1.连接Minio MinioClient minioClient =MinioClient.builder() .endpoint(minioEndpoint) .credentials(accessKey, secretKey) .build(); // 2.上传 // 把os流转为is流 ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); String newFileName = assemblyPdfName(fileName); PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(bucketName) .object(newFileName) .contentType("application/pdf") .stream(inputStream, inputStream.available(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build(); minioClient.putObject(putObjectArgs); return newFileName; } /** * 拼接pdf文件名 * @param name * @return */ public String assemblyPdfName(String name) { return StringUtils.format("{}/{}.pdf", DateUtils.datePath(), name); } /** * 模拟数据 * @return */ private InspectionReportPdfVO requireData(Long taskId) { InspectionReportPdfVO inspectionReport = taskMapper.selectInspectionReportData(taskId); inspectionReport.setInspectionItem(getInspectionItemByTaskItem(inspectionReport.getInspectionItem())); return inspectionReport; } /** * 渲染数据 * @param fields */ private void renderData(AcroFields fields, InspectionReportPdfVO inspectionReportPdfVO) throws Exception { // 取出当前对象所有属性,并赋值到模板里 Class<? extends InspectionReportPdfVO> aClass = inspectionReportPdfVO.getClass(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field field : declaredFields) { String name = field.getName(); String key = name.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase(); field.setAccessible(true); Object o = field.get(inspectionReportPdfVO); if (o != null) { String value = o.toString(); fields.setField(key, value); } } } /** * 填充模板中的数据 * @param fields * @param data 是一个Map<String,String> 主要存储 key 表单模板中的单元格名 value为想要赋的值,遍历 * @param ps */ public void fillData(AcroFields fields, Map<String, String> data, PdfStamper ps) { try { for (String key : data.keySet()) { String value = data.get(key); if (key.contains("image")) { addImageToPdf(key, fields, ps, value); continue; } // 为字段赋值,注意字段名称是区分大小写的 fields.setField(key, value); } } catch (Exception e) { e.printStackTrace(); } } /** * 添加图片 * @param key 应为模板名 * @param form 动态字段 * @param stamper * @param filePath 本地图片路径 * @throws DocumentException * @throws IOException * @throws IOException */ private static void addImageToPdf(String key,AcroFields form, PdfStamper stamper, String filePath) throws DocumentException, IOException, IOException { // 通过图片域名获取所在页和坐标,左下角为起点 int pageNo = form.getFieldPositions(key).get(0).page; Rectangle signRect = form.getFieldPositions(key).get(0).position; float x = signRect.getLeft(); float y = signRect.getBottom(); // 读图片 Image image = Image.getInstance(filePath); // 获取操作的页面 PdfContentByte under = stamper.getOverContent(pageNo); // 根据域的大小缩放图片 image.scaleToFit(signRect.getWidth() * 2, signRect.getHeight()); // 添加图片并设置位置(个人通过此设置使得图片垂直水平居中,可参考,具体情况已实际为准) image.setAbsolutePosition(x, y); under.addImage(image); } /** * 通过属性名获取属性在对象里的值 * @param obj * @param propertyName * @return * @param <T> */ public static <T> T getPropertyValue(Object obj, String propertyName) { try { // 获取对象的类 Class<?> objClass = obj.getClass(); // 获取类的指定属性 Field field = objClass.getDeclaredField(propertyName); // 设置属性为可访问 field.setAccessible(true); // 获取属性的值 @SuppressWarnings("unchecked") T value = (T) field.get(obj); return value; } catch (Exception e) { e.printStackTrace(); return null; } } }