package com.ruoyi.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.hash.Hash; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.ArrayListMultimap; import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import com.itextpdf.text.pdf.codec.Base64; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.PdfBaseWriter; import com.ruoyi.domain.Book; import com.ruoyi.domain.vo.PdfTestVO; import com.ruoyi.service.BookService; import com.ruoyi.mapper.BookMapper; import org.apache.skywalking.apm.toolkit.trace.Tag; import org.apache.skywalking.apm.toolkit.trace.Tags; import org.apache.skywalking.apm.toolkit.trace.Trace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.*; import java.util.List; import java.util.stream.IntStream; /** * @author wangfei * @description 针对表【t_book(书籍)】的数据库操作Service实现 * @createDate 2023-12-05 14:50:11 */ @Service public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService{ @Autowired private BookMapper bookMapper; @Trace @Tags({@Tag(key = "param", value = "arg[0]"), @Tag(key = "result", value = "returnedObj")}) @Override public List<Book> selectBookList(String name) { return bookMapper.selectBookList(name); } @Override public List<Book> selectBookListException(String name) { if(1 == 1) { throw new ServiceException("测试异常处理"); } return null; } @Override public Document generateItextPdfDocument(OutputStream os) throws Exception { // 创建一个A4大小的PDF PdfBaseWriter document = new PdfBaseWriter(os); // 打开 document.open(); // PDF文档属性 document.addTitle("标题(测试基本PDF导出)"); document.addAuthor("作者"); document.addSubject("主题(基本PDF导出)"); document.addKeywords("关键字(基本PDF导出)"); document.addCreator("谁创建的(xxx有限公司)"); document.addCreationDate(); // 测试标题加文本内容 // 添加第一个标题 Chapter chapterOne = document.setTitle1("模拟标题1", 1); // 添加第一个标题下的第一个二级标题 document.setTitle2(chapterOne, "标题1-1"); // 添加内容 document.writeText("石油是指气态、液态和固态的烃类混合物,具有天然的产状。石油又分为原油、天然气、天然气液及天然焦油等形式,但习惯上仍将“石油”作为“原油”的定义用。"); // 添加第一个标题下的第二个二级标题 document.setTitle2(chapterOne, "标题1-2"); document.writeText("石油是一种黏稠的、深褐色液体,被称为“工业的血液”。地壳上层部分地区有石油储存。主要成分是各种烷烃、环烷烃、芳香烃的混合物。是地质勘探的主要对象之一。"); // 添加第二个标题 Chapter chapterTwo = document.setTitle1("模拟标题2", 2); document.setTitle2(chapterTwo, "标题2-1"); document.writeText("石油的成油机理有生物沉积变油和石化油两种学说,前者较广为接受,认为石油是古代海洋或湖泊中的生物经过漫长的演化形成,属于生物沉积变油,不可再生;后者认为石油是由地壳内本身的碳生成,与生物无关,可再生。石油主要被用来作为燃油和汽油,也是许多化学工业产品,如溶液、化肥、杀虫剂和塑料等的原料。"); document.setTitle2(chapterTwo, "标题2-2"); document.writeText("2023年2月16日,中国石油和化学工业联合会发布数据称,中国炼油产能已超过美国,成为世界第一炼油大国。2023年2月16日,中国石油和化学工业联合会发布数据称,中国炼油产能已超过美国,成为世界第一炼油大国。"); // 测试自定义段(可当标题、正文使用) document.add(document.getParagraph(1, "Paragraph")); // 测试自定义段方向 Paragraph paragraph = document.getParagraph(2, "左对齐"); paragraph.setAlignment(Element.ALIGN_RIGHT); document.add(paragraph); // 测试(List)列 List<String> list = new ArrayList<>(); list.add("第一行"); list.add("第二行"); list.add("第三行"); document.writeList(list, true); // 测试(Map)列 Map<String, String> map = new HashMap<>(); map.put("甲方", "可填入动态公司"); map.put("乙方", "可填入动态公司"); map.put("时间", String.valueOf(DateUtil.date())); map.put("地点", "可填入动态地点"); document.writeList(map, false); // 测试带表头的表格 ArrayList<LinkedHashMap<String, Object>> rows = new ArrayList<>(); for (int i = 0;i < 10;i ++) { LinkedHashMap<String, Object> row = new LinkedHashMap<>(); row.put("姓名", "张三"+i); row.put("年龄", 23 + i); row.put("成绩", 88.32 + i); row.put("是否合格", true); row.put("考试日期", DateUtil.date()); rows.add(row); } document.writeTable(rows); // 测试自定义表格 PdfPTable table = new PdfPTable(new float[]{40, 150}); table.setWidthPercentage(100); // 上下边距 table.setSpacingBefore(10); table.setSpacingAfter(10); document.writeCell("电话:",1, 1, table); document.writeCell("地址:", 1, 1, table); document.writeCell("手机号:", 1, 1, table); // 一个cell图文混合 List<String> list2 = new ArrayList<>(); list2.add("哈哈哈"); list2.add("C:/Users/gxk/Pictures/Saved Pictures/nvm.png"); list2.add("啊啊啊"); list2.add( "C:/Users/gxk/Pictures/Saved Pictures/123.jpg"); document.writeTextAndImageCell(list2, table); // 把表格添加进入PDF document.add(table); // 图片 Image image = Image.getInstance("C:/Users/gxk/Pictures/Saved Pictures/nvm.png"); image.setAlignment(Element.ALIGN_CENTER); // 缩放 image.scalePercent(60); document.add(image); // 关闭 document.close(); return document; } /** * 测试生成PDF - API方式渲染动态表格 * @param os * @return * @throws Exception */ @Override public Document testGeneratePDF(OutputStream os) throws Exception { // 动态数据 List<PdfTestVO> pdfTestVOS = dynamicTabularData(); // 创建PDF PdfBaseWriter document = new PdfBaseWriter(os); // 打开 document.open(); // 添加内容 document.add(document.getParagraph(1, "xxx调查表")); Chapter title1 = document.setTitle1("动态测试表格", 1); // 动态表格 addTable(pdfTestVOS, document, title1); // 关闭 document.close(); return document; } private void addTable(List<PdfTestVO> pdfTestVOS, PdfBaseWriter document, Chapter chapter) { IntStream.range(0, pdfTestVOS.size()).forEach(index -> { PdfTestVO pdfTestVO = pdfTestVOS.get(index); try { document.setTitle2(chapter, pdfTestVO.getTestItem()); // 生成表格 PdfPTable table = new PdfPTable(new float[]{40, 150}); table.setTotalWidth(500); table.setLockedWidth(true); table.setHorizontalAlignment(Element.ALIGN_CENTER); table.getDefaultCell().setBorder(1); // 添加行 table.addCell(document.createCenterCell("测试编号")); table.addCell(document.createLeftCell(pdfTestVO.getTestNumber())); table.addCell(document.createCenterCell("样本编号")); table.addCell(document.createLeftCell(pdfTestVO.getSampleNumber())); table.addCell(document.createCenterCell("检测项目")); table.addCell(document.createLeftCell(pdfTestVO.getTestItem())); table.addCell(document.createCenterCell("风险等级")); table.addCell(document.createLeftCell(pdfTestVO.getRiskLevel())); table.addCell(document.createCenterCell("测试方法")); table.addCell(document.createLeftCell(pdfTestVO.getTestMethod())); table.addCell(document.createCenterCell("测试结果")); table.addCell(document.createLeftCell(pdfTestVO.getTestResult())); table.addCell(document.createCenterCell("漏洞危害")); table.addCell(document.createLeftCell(pdfTestVO.getVulnerabilityHazard())); table.addCell(document.createCenterCell("测试详情")); PdfPCell cell = new PdfPCell(); cell.addElement(document.createTextInImageCell(pdfTestVO.getTestDetails())); Image img = Image.getInstance("C:/Users/gxk/Pictures/Saved Pictures/nvm.png"); img.scaleToFit(100, 100); cell.addElement(img); cell.addElement(document.createTextInImageCell("车辆采用白名单策略,需客户端设置IP地址为192.168.69.71/24才可以与之通信,车辆IP地址为192.168.69.8。")); Image imgTwo = Image.getInstance("C:/Users/gxk/Pictures/Saved Pictures/123.jpg"); img.scaleToFit(100, 100); cell.addElement(imgTwo); cell.addElement(document.createTextInImageCell("请依次完成以下操作:\n\n1)使用电动螺丝刀拆开车机外壳,注意不要造成物理性破坏;\n\n2)肉眼观察板子上是否有明显的JTAG调试接口,需要注意CPU附近的点和引脚的各种信息;\n\n注:JTAG通常主要由以下4个针脚组成:\n\n①TCK ... Test Clock\n\n②TMS ... Test Mode Select \n\n③TDI ... Test Data In\n\n④TDO ... Test Data Out\n\n3)通过五倍光学放大镜仔细查看PCB板上的信息,找到带有JTAG、TCK、TMS、TDI、TDO等相关的信息的点位,比如下图:\n\nhttp://10.12.48.80:8089/file_server/api_v1/downloadSysFile?key=e45566a4ebf14eb299d60b180473f9c84c&ts=2024_03_05&token=7f5ac770a013f7ce74205992257f5d9b&tmp=false&inline=true\n\n4)将测试过程中的 PCB 板照片等作为测试证明材料上传至平台。\n\n<br>\n\n**请根据PCB板上是否存在明显的JTAG调试接口对节点结果进行判断。**)")); Image imgThird = Image.getInstance("http://49.232.167.247:22038/vehicle-quality-review-oss/2023/12/19/1702891760057_20231219095525A001.jpg"); img.scaleToFit(100, 100); cell.addElement(imgThird); table.addCell(cell); table.addCell(document.createCenterCell("修复建议")); table.addCell(document.createLeftCell(pdfTestVO.getRepairSuggestion())); document.add(table); } catch (DocumentException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } }); } /** * 生成测试数据 * @return */ private List<PdfTestVO> dynamicTabularData() { List<PdfTestVO> dataList = new ArrayList<>(); PdfTestVO dos = PdfTestVO.builder() .testNumber("EH3-Vehicle-PT-001") .sampleNumber("LNNAJDDU4PD490747") .testItem("DOS攻击") .riskLevel("无风险") .testMethod("1.使用奇瑞设备接入整车OBD口; \n" + "2.使用电脑连接OBD口; \n" + "3.使用电脑打开OBD软件; \n" + "4.点击软件中的“诊断”按钮;") .testResult("对车辆以太网DOS攻击后,车辆无异常,车辆以太网服务未受到影响,测试通过。") .vulnerabilityHazard("拒绝车辆正常的服务访问,影响车辆的正常运行") .testDetails("1.使用奇瑞设备接入整车OBD口;") .repairSuggestion("无").build(); dataList.add(dos); PdfTestVO blur = PdfTestVO.builder() .testNumber("EH3-Vehicle-PT-002") .sampleNumber("LNNAJDDU4PD490747") .testItem("模糊测试") .riskLevel("无风险") .testMethod("1.使用奇瑞设备接入整车OBD口;\n" + "2.对Doip协议进行模糊测试,尝试影响以太网诊断的可用性,若车辆未出现异常则测试通过,否则不通过。") .testResult("车辆模糊过程中无异常,未发现未知漏洞,测试通过。") .vulnerabilityHazard("模糊测试可能导致车辆异常。") .testDetails("1.使用奇瑞设备接入整车OBD口;") .repairSuggestion("无").build(); dataList.add(blur); return dataList; } @Override public void generateTempPDF(HttpServletResponse response) throws Exception { // PdfReader reader = null; // PdfStamper ps = null; // OutputStream fos = null; // ByteArrayOutputStream bos = null; // Base64.InputStream fin = null; // ServletOutputStream out = null; // try { // // 模板绝对路径--服务器 // String fileName = "/template/receipt_template.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); // // // 动态添加所需要的数据,key跟模板中对应文本域名称一致 // Map<String, String> map = new HashMap<>(); // map.put("code", "1234567890fjdksjfdsfdsffdsjfdssssssttttttttttttttt"); // map.put("table_image", "C:/Users/gxk/Pictures/Saved Pictures/nvm.png"); // // // 渲染 // fillData(fields, map, ps); // // //必须要调用这个,否则文档不会生成的 // ps.setFormFlattening(true); // if(ps != null){ // ps.close(); // } // //生成pdf路径存放的路径 // fos = response.getOutputStream(); // fos.write(bos.toByteArray()); // // }catch (Exception e){ // e.printStackTrace(); // }finally { // if(fos!=null){ // fos.flush(); // fos.close(); // } // if (bos != null){ // bos.close(); // } // if(reader != null){ // reader.close(); // } // } } /** * 填充模板中的数据 * @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); } }