java导出pdf(水印、页眉、表格、背景样式、页脚、饼图、柱图)

发布于:2024-04-27 ⋅ 阅读:(53) ⋅ 点赞:(0)

controller层

@PostMapping("/queryRiskReport1")
    public Object queryRiskReport1(@RequestBody RiskBo riskBo)throws Exception {
        String corpKey = riskBo.getCorpKey();
        String reportNo = "18073201";
        String beginDate=riskBo.getBeginDate();
        String endDate=riskBo.getEndDate();
        SysUser user = SecurityUtils.getLoginUser().getUser();
        String deptName = user.getDept().getDeptName();
        ChangeRiskIntoVo changeRiskIntoVo = new ChangeRiskIntoVo();
        String ancestors = user.getDept().getAncestors();
        if(ancestors.contains(",")){
            String[] split = ancestors.split(",");
            changeRiskIntoVo.setDeptPid(split[0]);
        }else{
            changeRiskIntoVo.setDeptPid(ancestors);
        }
        changeRiskIntoVo.setCorpKey(corpKey);
        Boolean aBoolean = riskHistoryInfoService.queryAuth(changeRiskIntoVo);

        String queryDate = DateUtils.getLastYears(1) + "至"+ DateUtils.getLastYears(0);//查询数据期限
        String currentTime = DateUtils.getDateFormatter(new Date());
        long currentTimeMillis = System.currentTimeMillis();
        String path = pdfResourceFolder + "risk_report" + File.separator;
        String pdfTemp = tempPdfFolder + "risk_report" + File.separator;
        File file = new File(path);
        if (!file.exists()) file.mkdirs();
        File file1 = new File(pdfTemp);
        if (!file1.exists()) file1.mkdirs();
        String pathUrl = path + user.getUserId() + "-" + currentTimeMillis + ".PDF";
        String tmpPath1 = pdfTemp + user.getUserId() + "-" + currentTimeMillis + "1.PDF";
        String tmpPath2 = pdfTemp + user.getUserId() + "-" + currentTimeMillis + "2.PDF";
        String tmpPath3 = pdfTemp + user.getUserId() + "-" + currentTimeMillis + "3.PDF";
        String tmpPath4 = pdfTemp + user.getUserId() + "-" + currentTimeMillis + "4.PDF";
        RiskReportTemplate1 t=new RiskReportTemplate1();
        String htmlStr= t.createReportTemplate(riskReportService.queryRiskReport1(corpKey,beginDate,endDate), queryDate, user.getUserName(), deptName, reportNo,aBoolean,beginDate,endDate).toString();
        PdfUtils.htmp2pdf("客户风险分析报告", htmlStr,tmpPath1, false, false);
        ReportUtils2.addImageWatermark(tmpPath1, tmpPath2, 1, 0);
        ReportUtils2.pdf_yemei(deptName, tmpPath2, tmpPath3);
        t.waterMark(tmpPath3,tmpPath4, user.getUserName(), currentTime, user.getDept().getDeptName());
        t.addPageNum(tmpPath4, pathUrl);
        return Result.success(pathUrl);
    }

pdf模板RiskReportTemplate1(页脚、水印)

package com.ruoyi.business.bankInterface.controller.risk.model1;

import com.alibaba.fastjson2.JSONObject;
import com.itextpdf.text.*;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.ruoyi.business.bankInterface.ConstantConfig;
import com.ruoyi.business.bankInterface.controller.risk.PdfUtils;
import com.ruoyi.business.bankInterface.controller.risk.utils.ReportUtils2;
import com.ruoyi.business.bankInterface.controller.tax.utils.DateUtils;
import com.ruoyi.business.bankInterface.controller.tax.utils.pdf.PdfNumPageEventTax;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
public class RiskReportTemplate1 {

    public static void main(String[]args)throws IOException {
        StringBuffer buffer = new StringBuffer();
        BufferedReader bf = new BufferedReader(new FileReader("D:\\test\\RiskReport1.json"));
        String s = null;
        while((s = bf.readLine()) != null){ buffer.append(s.trim()); } bf.close();
        RiskModel2 model = JSONObject.parseObject(buffer.toString(),RiskModel2.class);
        RiskReportTemplate1 t=new RiskReportTemplate1();
        String htmlStr= t.createReportTemplate(model, "2021-01-01 "+"至"+" 2024-01-31", "周伯通","中国银行苏州分行","18073201",true,"","").toString();

        PdfUtils.htmp2pdf("njfh", htmlStr,"D:\\test\\2123456789001.pdf", false, false);
        ReportUtils2.addImageWatermark("D:\\test\\2123456789001.pdf", "D:\\test\\2123456789002.pdf", 1, 0);
        ReportUtils2.pdf_yemei("中国银行苏州分行", "D:\\test\\2123456789002.pdf", "D:\\test\\2123456789.pdf");
        waterMark("D:\\test\\2123456789.pdf","D:\\test\\21234567891.pdf", "测试用户", "2024-03-29", "中国银行");
        addPageNum("D:\\test\\21234567891.pdf", "D:\\test\\212345678915.pdf");
    }


    /** 征信报告-征信报告模板生成 */
    public StringBuffer createReportTemplate(RiskModel2 riskModel2, String authDate, String person, String bankName, String reportNo,Boolean aBoolean,String beginDate,String endDate
    ) {
        String begin = authDate.split("至")[0];
        String end = authDate.split("至")[1];
        StringBuffer template = new StringBuffer();
        template.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /><title>客户风险分析报告</title></head>");
        template.append("<body><div>");
        //获取当前时间
        String curentDateStr = DateUtils.getDateFormatter(new Date());
        //首页
        template.append(getFirstHtml(riskModel2.getK1().getQymc(), riskModel2.getK1().getTyshxydm(),curentDateStr, authDate, bankName, person, reportNo,beginDate,endDate));
        /** 1.企业概览 */
        template.append(ReportUtils2.title("企业概览", 1));
        /** 1.1.基本信息 */
        template.append(K1.getHtml(riskModel2.getK1()));
        /** 1.2.企业信用评分 */
        template.append(K1.score(riskModel2.getK1().getScore()));
        /** 2.公司治理 */
        template.append(ReportUtils2.title("公司治理", 1));
        template.append(K3.getHtml1(riskModel2.getK3List()));
        template.append(ReportUtils2.title("风险信息", 1));
        template.append(K4.getHtml1(riskModel2.getK4List()));
        template.append(K5.getHtml(riskModel2.getK5List()));
        /** 3.1.经营风险 */
        template.append(K4.getHtml2(riskModel2.getK4_1List()));
        /** 3.1.1.经营资质 */
        template.append(K3.getHtml2(riskModel2.getK3_1List()));
        /** 3.1.2.运营风险 */
        template.append(K6.getHtml1(riskModel2.getK6List(),aBoolean));
        /** 3.1.3.司法风险 */
        template.append(K7.getHtml(riskModel2.getK7List(), riskModel2.getK7_1List(),aBoolean));
        /** 3.1.4.行政处罚 */
        template.append(K8.getHtml(riskModel2.getK8List(),aBoolean));
        /** 3.2.财务风险 */
        template.append(K4.getHtml3(riskModel2.getK4_2List()));
        /** 3.2.1.偿债能力 */
        template.append(K6.getHtml2(riskModel2.getK6_1List(), "偿债能力", "本风险监测时段中,暂未监测到本企业存在异常。",aBoolean));
        /** 3.2.2.盈利能力 */
        template.append(K6.getHtml2(riskModel2.getK6_2List(), "盈利能力", "本风险监测时段中,暂未监测到本企业存在异常。",aBoolean));
        /** 3.2.3.营运能力 */
        template.append(K6.getHtml2(riskModel2.getK6_3List(), "营运能力", "本风险监测时段中,暂未监测到本企业存在异常。",aBoolean));
        /** 3.3.授用信风险 */
        template.append(ReportUtils2.title("授用信风险", 2));
        /** 3.3.1.履约风险 */
        template.append(K6.getHtml2(riskModel2.getK6_4List(), "履约风险", "本风险监测时段中,暂未监测到本企业存在异常。",aBoolean));
        /** 3.3.2.过度授信风险 */
        template.append(K6.getHtml2(riskModel2.getK6_5List(), "过度授信风险", "本风险监测时段中,暂未监测到本企业存在异常。",aBoolean));
        /** 3.3.3.企业非银融资 */
        template.append(K6.getHtml2(riskModel2.getK6_6List(), "企业非银融资", "本风险监测时段中,暂未监测到本企业存在异常。",aBoolean));
        /** 3.3.4.其他授信异常 */
        template.append(K6.getHtml2(riskModel2.getK6_7List(), "其他授信异常", "本风险监测时段中,暂未监测到本企业存在异常。",aBoolean));
        /** 3.4.关联风险 */
        template.append(ReportUtils2.title("关联风险", 2));
        /** 3.4.1.工商信息监测 */
        template.append(K6.getHtml2(riskModel2.getK6_8List(), "工商信息监测", "本风险监测时段中,未监测到本企业存在企业关联人(企业)发生破产清算注销风险。",aBoolean));
        /** 3.4.2.司法信息监测 */
        template.append(K9.getHtml(riskModel2.getK9List(),aBoolean));
        /** 3.4.3.履约风险(征信) */
        template.append(K6.getHtml2(riskModel2.getK6_9List(), "履约风险(征信)", "本风险监测时段中,未监测到本企业存在关联征信五级不良、关联企业在他行未结清业务发生逾期、关联企业非银机构授信异常风险。",aBoolean));
        /** 3.5.名单监测 */
        template.append(ReportUtils2.title("名单监测", 2));
        /** 3.5.1.黑名单 */
        //template.append(K10.getHtml(riskModel2.getK10List()));
        /** 3.6.系统性风险 */
        template.append(ReportUtils2.title("系统性风险", 2));
        /** 3.6.1.行业风险 */
        template.append(K6.getHtml3(riskModel2.getK6_10List(),aBoolean));



        template.append("</div>");
        template.append("</body>");
        template.append("</html>");
        return template;
    }

    /**
     *  首页
     * @param companyName              企业名称
     * @param companyCode              统一社会信用代码
     * @param time                      报告生成日期,如:2021-01-01
     * @param dataRetrievalCycle      数据周期,如:2021-01-01 至 2024-01-31
     * @param bankName                 金融机构名称
     * @param person                   查询人员
     * @param reportNo                 报告编号
     */
    public static StringBuffer getFirstHtml(String companyName, String companyCode, String time, String dataRetrievalCycle
            , String bankName, String person, String reportNo,String beginDate,String endDate) {
        StringBuffer firstHtml = new StringBuffer();
        firstHtml.append("<div align=\"left\"><img style=\"height:30px;width:280px\" src=\"").append(ConstantConfig.RISK_IMAGE + "苏州征信-logo.png").append("\"/></div>");
        firstHtml.append("<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>");
        //查询人员信息
        firstHtml.append("<div>");
        firstHtml.append("<div align=\"right\" style=\"font-size:30px; font-weight: 900; color:#005BAB;\">").append(companyName).append("</div><br/>");
        firstHtml.append("<div align=\"right\" style=\"font-size:18px; font-weight: 500; color:#808080;\">统一社会信用代码:").append(companyCode).append("</div>");
        firstHtml.append("</div>");
        firstHtml.append("<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>");
        firstHtml.append("<div>");
        firstHtml.append("<div align=\"left\" style=\"font-size:16px; font-weight: 100; color:#808080;\">查询人员: ").append(person).append("</div>");
        firstHtml.append("<div align=\"left\" style=\"font-size:16px; font-weight: 100; color:#808080;\">查询机构: ").append(bankName).append("</div>");
        firstHtml.append("<div align=\"left\" style=\"font-size:16px; font-weight: 100; color:#808080;\">风险周期: ").append(beginDate+"至"+endDate).append("</div>");
        firstHtml.append("<div align=\"left\" style=\"font-size:16px; font-weight: 100; color:#808080;\">报告生成时间: ").append(time).append("</div>");
        firstHtml.append("</div><br/><br/><br/><br/><br/>");
        firstHtml.append("<div>");
        firstHtml.append("<div align=\"right\" style=\"font-size:20px; font-weight: 500; color:#4169E1;\">报告编号:").append(reportNo).append("</div>");
        firstHtml.append("</div><br/><br/><br/>");
        return firstHtml;
    }

    /**
     * 生成页脚
     * @param srcPdfPath 源文件
     * @param tagetPdfPath  加工后文件
     */
    public static void addPageNum(String srcPdfPath, String tagetPdfPath) {
        try {
            // 输出文件 流
            FileOutputStream fos = new FileOutputStream(tagetPdfPath);

            // 读取 源PDF文件,进行一页一页复制,才能触发 添加页码的  页面监听事件
            PdfReader reader = new PdfReader(srcPdfPath);
            // 获取 源文件总页数
            int num = reader.getNumberOfPages();

            // 新建文档,默认A4大小
            Document document = new Document(PageSize.A4);
            PdfWriter writer = PdfWriter.getInstance(document, fos);
            // 设置页面监听事件,必须在open方法前
            writer.setPageEvent(new PdfNumPageEventTax(num));
            document.open();
            // PDF内容体
            PdfContentByte pdfContent = writer.getDirectContent();
            //System.out.println("总页数:" + num);
            // 页面数是从1开始的
            for (int i = 1; i <= num; i++) {
                document.newPage();
                // 设置空页码进行展示
                writer.setPageEmpty(false);
                PdfImportedPage page = writer.getImportedPage(reader, i);
                // 复制好的页面,添加到内容去,触发事件监听
                pdfContent.addTemplate(page, 0, 3);
            }
            document.close();
            reader.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static int interval = -5;

    //给pdf加水印
    public static void waterMark(String inputFile, String outputFile, String userName, String time,String bankName) {
        PdfReader reader = null;
        PdfStamper stamper = null;
        FileOutputStream outputStream = null;
        try {
            String waterMarkName = "长三角征信链";
            reader = new PdfReader(inputFile);
            outputStream = new FileOutputStream(outputFile);
            stamper = new PdfStamper(reader, outputStream);

            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",   BaseFont.EMBEDDED);

            Rectangle pageRect = null;
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.15f);//控制水印深浅
            gs.setStrokeOpacity(0.4f);
            int total = reader.getNumberOfPages() + 1;

            JLabel label = new JLabel();
            FontMetrics metrics;
            int textH = 0;
            int textW = 0;
            label.setText(waterMarkName);
            metrics = label.getFontMetrics(label.getFont());
            textH = metrics.getHeight();
            textW = metrics.stringWidth(label.getText());

            PdfContentByte under;
            for (int i = 1; i < total; i++) {
                if(i==1 || i==total-1) continue;
                pageRect = reader.getPageSizeWithRotation(i);
                under = stamper.getOverContent(i);
                under.saveState();
                under.setGState(gs);
                under.beginText();
                under.setFontAndSize(base, 18);//控制水印字体大小
                // 水印文字成30度角倾斜
                // 获取每一页的高度、宽度
                float pageHeight = pageRect.getHeight();
                float pageWidth = pageRect.getWidth();

                // 根据纸张大小多次添加, 水印文字成30度角倾斜
                for (int height = interval + textH; height < pageHeight; height = height + textH * 8) {
                    for (int width = interval + textW; width < pageWidth + textW; width = width + textW * 6) {
                        // 将分段的字段进行输出编写
                        under.setFontAndSize(base, 12);//控制水印字体大小
                        under.showTextAligned(Element.ALIGN_LEFT, "查询人员:"+userName, width - textW, height -textH , 30);
                        under.setFontAndSize(base, 12);//控制水印字体大小
                        under.showTextAligned(Element.ALIGN_LEFT, "查询时间:"+time, width - textW+12, height -textH*2 , 30);
                        under.setFontAndSize(base, 12);//控制水印字体大小
                        under.showTextAligned(Element.ALIGN_LEFT, "查询机构:"+bankName, width - textW+24, height -textH*3 , 30);
                        under.setFontAndSize(base, 12);//控制水印字体大小
                        under.showTextAligned(Element.ALIGN_LEFT, "苏州企业征信服务有限公司", width - textW+36, height -textH*4 , 30);
                    }
                }

                under.endText();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(stamper != null){
                    stamper.close();
                }
                if(outputStream != null){
                    outputStream.close();
                }
                if(reader != null){
                    reader.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

//    /** 文本内容 content:文本内容;fontSize:字体大小;color:字体颜色;backgroundColor:背景色 */
//    public static StringBuffer title(String content, int fontSize, int fontWeight){
//        StringBuffer description = new StringBuffer();
//        description.append("<div align=\"left\" style=\"");
//        if(fontWeight > 0) description.append("font-weight:").append(fontWeight).append(";");
//        if(fontSize > 0) description.append("font-size:").append(fontSize).append("px;");
//        description.append("line-height:").append(1.8).append(";");
//        description.append("letter-spacing:").append(1.8).append("px;");
//        description.append("\">");
//        description.append(content).append("</div>");
//        return description;
//    }

//    /** 文本内容 content:文本内容;fontSize:字体大小;color:字体颜色;backgroundColor:背景色 */
//    public static StringBuffer description(String content, int fontSize, String color, String backgroundColor, int fontWeight){
//        StringBuffer description = new StringBuffer();
        description.append("<br/>");
//        description.append("<div align=\"left\" style=\"");
//        if(fontWeight > 0) description.append("font-weight:").append(fontWeight).append(";");
//        if(fontSize > 0) description.append("font-size:").append(fontSize).append("px;");
//        if(StringUtils.isNotBlank(color)) description.append("color:").append(color).append(";");
//        if(StringUtils.isNotBlank(color)) description.append("padding-left:12px;padding-right:12px;padding-bottom:12px;background-color:").append(backgroundColor).append(";");
//        description.append("line-height:").append(1.8).append(";");
//        description.append("letter-spacing:").append(1.8).append("px;");
//        description.append("\">");
//        description.append("&nbsp;&nbsp;");
//        description.append(content).append("</div>");
        description.append("<br/>");
//        return description;
//    }
//
//    /**
//     * 加载图片(柱状图/折线图)
//     */
//    public static StringBuffer getImgOneHtml(String title, List<String> xData, List<Double> yDataList, List<Color> colorList, String yUnit, int type,
//                                             int width, int height, double SpaceFill, StringBuffer tempHtml) {
//        if (xData == null || xData.size() == 0) return tempHtml;
//        String path = new HistogramChar<>().getChart2(title, xData, yDataList, colorList, yUnit, type,
//                width, height, SpaceFill);
//        tempHtml.append("<div align=\"center\"><img style=\"height:262px;width:700px\" src=\"").append(path).append("\"/></div>");
//        return tempHtml;
//    }
//
//    /**
//     * 加载图片(扇形图)
//     */
//    public static StringBuffer getImgPieHtml(List<String> keyList, List<Double> rDataList, Boolean isTitleVisible, String title,
//                                             Boolean isLegendVisible, RectangleEdge rectangleEdge, int flag, StringBuffer tempHtml) {
//        if (rDataList == null || rDataList.size() == 0) return tempHtml;
//        tempHtml.append("<div align=\"center\">");
//        String path = Pie2Chart.pieChart(keyList, rDataList, isTitleVisible, title, isLegendVisible, rectangleEdge, flag);
//        tempHtml.append("<img style=\"width:700px\" src=\"").append(path).append("\"/>");
//        tempHtml.append("</div>");
//        return tempHtml;
//    }
}

多线程导出pdf数据

public RiskModel2 queryRiskReport1(String corpKey,String beginDate,String endDate){
        RiskModel2 riskModel2 = new RiskModel2();
        /** 1.基本信息 */
        CompletableFuture<K1> k1 = CompletableFuture.supplyAsync(() ->
        {K1 k_1 = riskReportMapper.queryK1(corpKey);
            k_1.setTyshxydm(corpKey);
        return k_1;}, creditReportExecutor);
        /** 2.公司治理 */
        CompletableFuture<List<K3>> k3List = CompletableFuture.supplyAsync(() ->
        {
            List<K3> k_3List = riskReportMapper.queryK3List(corpKey, Arrays.asList("R001","R002","R003"),beginDate,endDate);
            return k_3List;
        }, creditReportExecutor);
        /** 3.风险信息:1级风险类型分布 */
        CompletableFuture<List<K4>> k4List = CompletableFuture.supplyAsync(() ->
        {
            List<K4> k_4List = riskReportMapper.queryK4List(corpKey,beginDate,endDate);
            return k_4List;
        }, creditReportExecutor);
        /** 3.风险信息:1级风险类型分布 */
        CompletableFuture<List<K5>> k5List = CompletableFuture.supplyAsync(() ->
        {
            List<K5> k_5List = riskReportMapper.queryK5List(corpKey,beginDate,endDate);
            return k_5List;
        }, creditReportExecutor);
        /** 3.1.经营风险 */
        CompletableFuture<List<K4>> k4_1List = CompletableFuture.supplyAsync(() ->
        {
            List<K4> k_4List = riskReportMapper.queryK4_1List(corpKey, "经营风险",beginDate,endDate);
            return k_4List;
        }, creditReportExecutor);
        /** 3.1.1.经营资质 */
        CompletableFuture<List<K3>> k3_1List = CompletableFuture.supplyAsync(() ->
        {
            List<K3> k_3List = riskReportMapper.queryK3List(corpKey, Arrays.asList("R004","R005"),beginDate,endDate);
            return k_3List;
        }, creditReportExecutor);
        /** 3.1.2.运营风险 */
        CompletableFuture<List<K6>> k6List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "运营风险",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.1.3.司法风险 */
        CompletableFuture<List<K7>> k7_1List = CompletableFuture.supplyAsync(() ->
        {
            List<K7> k_7List = riskReportMapper.queryK7_1List(corpKey, "司法风险",beginDate,endDate);
            return k_7List;
        }, creditReportExecutor);
        CompletableFuture<List<K7>> k7List = CompletableFuture.supplyAsync(() ->
        {
            List<K7> k_7List = riskReportMapper.queryK7List(corpKey, "司法风险",beginDate,endDate);
            return k_7List;
        }, creditReportExecutor);
        /** 3.1.4.行政处罚 */
        CompletableFuture<List<K8>> k8List = CompletableFuture.supplyAsync(() ->
        {
            List<K8> k_8List = riskReportMapper.queryK8List(corpKey, "行政处罚",beginDate,endDate);
            return k_8List;
        }, creditReportExecutor);
        /** 3.2 财务风险 */
        CompletableFuture<List<K4>> k4_2List = CompletableFuture.supplyAsync(() ->
        {
            List<K4> k_4List = riskReportMapper.queryK4_1List(corpKey, "财务风险",beginDate,endDate);
            return k_4List;
        }, creditReportExecutor);
        /** 3.2.1 偿债能力 */
        CompletableFuture<List<K6>> k6_1List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "偿债能力",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.2.2盈利能力 */
        CompletableFuture<List<K6>> k6_2List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "盈利能力",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.2.3营运能力 */
        CompletableFuture<List<K6>> k6_3List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "营运能力",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.3.1履约风险 */
        CompletableFuture<List<K6>> k6_4List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "履约风险",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.3.2 过度授信风险 */
        CompletableFuture<List<K6>> k6_5List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "过度授信风险",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.3.3 企业非银融资 */
        CompletableFuture<List<K6>> k6_6List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "企业非银融资",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.3.4 其他授信异常 */
        CompletableFuture<List<K6>> k6_7List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "其他授信异常",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.4.1 工商信息监测(暂无数据) */
        /** 3.4.2 司法信息监测 */
        CompletableFuture<List<K9>> k9List = CompletableFuture.supplyAsync(() ->
        {
            List<K9> k9_1List = riskReportMapper.queryK9List(corpKey, "司法信息监测",beginDate,endDate);
            return k9_1List;
        }, creditReportExecutor);
        /** 3.4.3 履约风险(征信) */
        CompletableFuture<List<K6>> k6_9List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "履约风险(征信)",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);
        /** 3.5.1 黑名单 */
        CompletableFuture<List<K10>> k10List = CompletableFuture.supplyAsync(() ->
        {
            List<K10> k10_1List = riskReportMapper.queryK10List(corpKey, "黑名单",beginDate,endDate);
            return k10_1List;
        }, creditReportExecutor);
        /** 3.6 系统性风险 */

        /** 3.6 系统性风险 */
        CompletableFuture<List<K6>> k6_10List = CompletableFuture.supplyAsync(() ->
        {
            List<K6> k_6List = riskReportMapper.queryK6List(corpKey, "行业风险",beginDate,endDate);
            return k_6List;
        }, creditReportExecutor);

        CompletableFuture<Void> allOf = CompletableFuture.allOf(k1, k3List, k4List, k5List, k3_1List, k4_1List, k6List, k7_1List, k7List
                , k4_2List, k8List, k6_1List, k6_2List, k6_3List, k6_4List, k6_5List, k6_6List, k6_7List, k9List, k6_9List, k10List, k6_10List);
        try {
            allOf.get();
            riskModel2 = RiskModel2.builder().k1(k1.get()).k3List(k3List.get()).k4List(k4List.get()).k5List(k5List.get()).
                    k3_1List(k3_1List.get()).k4_1List(k4_1List.get()).k6List(k6List.get()).k7_1List(k7_1List.get()).k7List(k7List.get()).k4_2List(k4_2List.get()).k8List(k8List.get())
                    .k6_1List(k6_1List.get()).k6_1List(k6_1List.get()).k6_2List(k6_2List.get()).k6_3List(k6_3List.get()).k6_4List(k6_4List.get())
                    .k6_5List(k6_5List.get()).k6_6List(k6_6List.get()).k6_7List(k6_7List.get()).k9List(k9List.get()).k6_9List(k6_9List.get()).k6_10List(k6_10List.get()).k10List(k10List.get()).build();
        } catch (InterruptedException |ExecutionException e) {
            e.printStackTrace();
            log.error("查询企业{}风险报告出错",corpKey);
        }
        return riskModel2;
    }

pdf导出工具类(页眉、图片底纹、柱图、饼图、表格、一级二级标题)

package com.ruoyi.business.bankInterface.controller.risk.utils;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.ruoyi.business.bankInterface.ConstantConfig;
import com.ruoyi.business.bankInterface.controller.risk.utils.chart.HistogramChar;
import com.ruoyi.business.bankInterface.controller.risk.utils.chart.Pie2Chart;
import com.ruoyi.business.bankInterface.controller.tax.utils.annotation.ReportField;
import com.ruoyi.business.bankInterface.controller.tax.utils.annotation.ReportFieldAnnotation;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.jfree.ui.RectangleEdge;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.List;

public class ReportUtils2 {
    private static String baseUrl = ConstantConfig.IMAGE_TEMP_FOLDER;

    //列表数据
    public static StringBuffer listHtml(Class<?> clazz, String jsonStr, StringBuffer template, int tableType, int dataType){
        if(StringUtils.isBlank(jsonStr) || "{}".equals(jsonStr) || "[]".equals(jsonStr)) return template;
        Map<String, Object> data = null;
        if(dataType == 1){
            /** 数据类型是list */
            data = ReportUtils2.changeObjectList(clazz, jsonStr);
        }else{
            /** 数据类型是对象 */
            data = ReportUtils2.changeObject(clazz, jsonStr);
        }
        if(data == null || data.size() == 0) return template;
        List<ReportField> reportFieldList = (List<ReportField>)data.get("nameList");
        List<Map<String,Object>> valueList = (List<Map<String,Object>>)data.get("valueList");
        if(tableType == 1){//列表排列
            template.append(ReportUtils2.listHtml(reportFieldList, valueList));
        }else if(tableType == 3){//横着单列标题排列
            template.append(ReportUtils2.oneTitleHtml(reportFieldList, valueList));
        }else{//横着两列标题排列
            template.append(ReportUtils2.twoTitleHtml(reportFieldList, valueList));
        }
        return template;
    }

    //jsonArrStr支持的对象中可以有key:value和最多两个List<String>,且其中一个List<String>为yearList
    public static Map<String, Object> changeObjectList(Class<?> clazz, String jsonArrStr) {
        // 获取带有特定注解的字段
        Set<Field> annotatedFields = getAnnotatedFields(clazz, ReportFieldAnnotation.class);
        List<ReportField> reportFieldList = new ArrayList<>();
        List<JSONObject> objectList = JSONArray.parseArray(jsonArrStr, JSONObject.class);
        List<Map<String, String>> valueList = new ArrayList<>(); Map<String, String> value = null;
        int flag = 0;
        for(JSONObject jsonObject : objectList){
            value = new HashMap<>();
            for (Field field : annotatedFields) {
                ReportField reportField = new ReportField();
                ReportFieldAnnotation annotation = field.getAnnotation(ReportFieldAnnotation.class);
                int dataType = annotation.dataType();//0:字符串,1:List<String>
                String fieldName = field.getName();
                Object vObj = jsonObject.get(field.getName());
                if(dataType == 0){//字符串类型
                    if(flag == 0) {
                        reportField.setDesc(annotation.desc()); reportField.setName(fieldName); reportField.setSort(annotation.sort());
                        reportField.setWidth(annotation.width()); reportField.setNum(annotation.num()); reportFieldList.add(reportField);
                    }
                    //value.put(fieldName, Objects.isNull(vObj) ? "" : vObj.toString());

                    value.put(fieldName, Objects.isNull(vObj) ? "" : vObj.toString().replaceAll("<", "").replaceAll(">", ""));
                }else if(dataType == 1){//List<String>类型的时候字段名取yearList的值
                    if("yearList".equals(field.getName())) continue;
                    Object yearListObj = jsonObject.get("yearList");
                    if(Objects.isNull(yearListObj) || Objects.isNull(vObj)) continue;
                    List<String> yearList = JSONArray.parseArray(JSONObject.toJSONString(yearListObj), String.class);
                    List<String> vList = JSONArray.parseArray(JSONObject.toJSONString(vObj), String.class);
                    for(int i=0; i<yearList.size(); i++){
                        String year = yearList.get(i);
                        if(flag == 0) {reportField = new ReportField(); reportField.setDesc(year); reportField.setName(year); reportField.setSort(annotation.sort()); reportField.setWidth(annotation.width()); reportField.setNum(annotation.num()); reportFieldList.add(reportField);}
                        String v = vList.get(i);
                        value.put(year, v == null ? "" : v.replaceAll("<", "").replaceAll(">", ""));
                    }
                }
            }
            valueList.add(value); flag++;
        }
        reportFieldList.sort((t1, t2) -> { Integer counts = t1.getSort(); Integer counts1 = t2.getSort(); return counts.compareTo(counts1); });
        Map<String, Object> res = new HashMap<>();
        res.put("nameList", reportFieldList);
        res.put("valueList", valueList);
        return res;
    }

    //jsonStr只支持key:value
    public static Map<String, Object> changeObject(Class<?> clazz, String jsonStr) {
        //获取带有特定注解的字段
        Set<Field> annotatedFields = getAnnotatedFields(clazz, ReportFieldAnnotation.class);
        List<ReportField> reportFieldList = new ArrayList<>();
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        List<Map<String, String>> valueList = new ArrayList<>(); Map<String, String> value = new HashMap<>();
        for (Field field : annotatedFields) {
            ReportField reportField = new ReportField();
            ReportFieldAnnotation annotation = field.getAnnotation(ReportFieldAnnotation.class);
            int dataType = annotation.dataType();//0:字符串,1:List<String>
            String fieldName = field.getName();
            Object vObj = jsonObject.get(fieldName);
            if(dataType == 0){//字符串类型
                reportField.setDesc(annotation.desc()); reportField.setName(fieldName); reportField.setSort(annotation.sort()); reportField.setWidth(annotation.width()); reportField.setNum(annotation.num()); reportFieldList.add(reportField);
                value.put(fieldName, Objects.isNull(vObj) ? "" : vObj.toString().replaceAll("<", "").replaceAll(">", ""));
            }else if(dataType == 1){//List<String>类型的时候字段名取yearList的值
                Object yearListObj = jsonObject.get("yearList");
                if(Objects.isNull(yearListObj) || Objects.isNull(vObj)) continue;
                List<String> yearList = JSONArray.parseArray(JSONObject.toJSONString(yearListObj), String.class);
                List<String> vList = JSONArray.parseArray(JSONObject.toJSONString(vObj), String.class);
                for(int i=0; i<yearList.size(); i++){
                    String year = yearList.get(i); String v = vList.get(i);
                    reportField.setDesc(year); reportField.setName(year); reportField.setSort(annotation.sort()); reportField.setWidth(annotation.width()); reportField.setNum(annotation.num()); reportFieldList.add(reportField);
                    value.put(year, v == null ? "" : v.replaceAll("<", "").replaceAll(">", ""));
                }
            }
        }
        valueList.add(value);
        Map<String, Object> res = new HashMap<>();
        reportFieldList.sort((t1, t2) -> { Integer counts = t1.getSort(); Integer counts1 = t2.getSort(); return counts.compareTo(counts1); });
        res.put("nameList", reportFieldList);
        res.put("valueList", valueList);
        return res;
    }

    /** 获取有该注解的字段 */
    public static Set<Field> getAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotation) {
        Set<Field> fields = new HashSet<>();
        Class<?> superClass = clazz;
        while (superClass != Object.class) {
            for (Field field : superClass.getDeclaredFields()) {
                if (field.isAnnotationPresent(annotation)) {
                    fields.add(field);
                }
            }
            superClass = superClass.getSuperclass();
        }
        return fields;
    }

    /** 个性化数据-列表数据展示 reportFieldList:标题行 value:标题行对应的字段对应的值 */
    public static StringBuffer listHtml(List<ReportField> reportFieldList, List<Map<String,Object>> value){
        List<String> keyList = new ArrayList<>();//存放标题行对应的字段
        StringBuffer tempHtml= new StringBuffer();
        tempHtml.append("<div class=\"table_box_b\">");
        tempHtml.append("<table width=\"100%\" cellspacing=\"0\">");
        tempHtml.append("<tbody>");
        /** -----------------------------标题列start----------------------------------- */
        tempHtml.append("<tr>"); int j=1; int size = reportFieldList.size();
        for(ReportField reportField : reportFieldList){
            if(j==size){
                tempHtml.append("<th height=\"30px\" width=\"").append(reportField.getWidth()).append("%\" nowrap=\"nowrap\" align=\"center\" style=\"").append(ReportLabelUtils.TH_FONT).append("border-bottom: 1px solid #F4F4F6;border-top:1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
            } else if(j==1){
                tempHtml.append("<th height=\"30px\" width=\"").append(reportField.getWidth()).append("%\" nowrap=\"nowrap\" align=\"center\" style=\"").append(ReportLabelUtils.TH_FONT).append("border-top:1px solid #F4F4F6;border-left: 1px solid #F4F4F6;border-bottom: 1px solid #F4F4F6;border-right:1px solid #FFFFFF;\">").append(reportField.getDesc()).append("</th>");
            }else{
                tempHtml.append("<th height=\"30px\" width=\"").append(reportField.getWidth()).append("%\" nowrap=\"nowrap\" align=\"center\" style=\"").append(ReportLabelUtils.TH_FONT).append("border-bottom: 1px solid #F4F4F6;border-right: 1px solid #FFFFFF;border-top:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
            }
            keyList.add(reportField.getName()); j++;
        }
        tempHtml.append("</tr>");
        /** -----------------------------标题列end----------------------------------- */
        /** -----------------------------数据列start----------------------------------- */
        if(value != null && value.size() > 0){
            for(Map<String, Object> vMap : value) {
                tempHtml.append("<tr>"); int keySize = keyList.size(); int flag = 1;
                for (String key : keyList) {
                    if(keySize==flag){
                        tempHtml.append("<td height=\"30px\" nowrap=\"nowrap\" align=\"center\" style=\"").append(ReportLabelUtils.TD_FONT).append("border-left:1px solid #F4F4F6; border-bottom:1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(key)).append("</td>");
                    }else{
                        tempHtml.append("<td height=\"30px\" nowrap=\"nowrap\" align=\"center\" style=\"").append(ReportLabelUtils.TD_FONT).append("border-left:1px solid #F4F4F6; border-bottom:1px solid #F4F4F6;\">").append(vMap.get(key)).append("</td>");
                    }
                    flag++;
                }
                tempHtml.append("</tr>");
            }
        }
        /** -----------------------------数据列end----------------------------------- */
        tempHtml.append("</tbody></table></div>");
        return tempHtml;
    }

    /** 个性化数据-横着两列标题排列 reportFieldList:标题行 value:标题行对应的字段对应的值 */
    public static StringBuffer twoTitleHtml(List<ReportField> reportFieldList, List<Map<String,Object>> value){
        StringBuffer tempHtml= new StringBuffer();
        for(Map<String, Object> vMap : value){
            tempHtml.append("<div class=\"table_box_b\">");
            tempHtml.append("<table width=\"100%\" cellspacing=\"0\" style=\"border-right:1px solid #cdd3e0; border-top:1px solid #cdd3e0; \">");
            tempHtml.append("<tbody>");
            int flag = 1;
            for(ReportField reportField : reportFieldList){
                if(flag == 1 && flag == reportFieldList.size()){
                    tempHtml.append("<tr>");
                    tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"background-color:#EAF2FA; color:#000000; font-size:10px;font-weight: 500;border-top:1px solid #F4F4F6;border-bottom: 1px solid #F4F4F6;border-left: 1px solid #F4F4F6;border-right:1px solid #F4F4F6; \">").append(reportField.getDesc()).append("</th>");
                    tempHtml.append("<td height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"left\" style=\"font-size:10px;padding:5px 5px; line-height:12px;border-top:1px solid #F4F4F6;border-bottom: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(reportField.getName())).append("</td>");
                    tempHtml.append("</tr>");
                }else if(flag == 1 || flag == 2){
                    if(flag == 1){
                        tempHtml.append("<tr>");
                        tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"background-color:#EAF2FA; color:#000000; font-size:10px;font-weight: 500;border-top:1px solid #F4F4F6;border-bottom: 1px solid #FFFFFF;border-left: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
                        tempHtml.append("<td height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"left\" style=\"font-size:10px;padding:5px 5px; line-height:12px;border-top:1px solid #F4F4F6;border-bottom: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(reportField.getName())).append("</td>");
                    }else{
                        tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"background-color:#EAF2FA; color:#000000; font-size:10px;font-weight: 500;border-top:1px solid #F4F4F6;border-bottom: 1px solid #FFFFFF;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
                        tempHtml.append("<td height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"left\" style=\"font-size:10px;padding:5px 5px; line-height:12px;border-top:1px solid #F4F4F6;border-bottom: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(reportField.getName())).append("</td>");
                        tempHtml.append("</tr>");
                    }
                } else{
                    if(flag%2 == 1 && flag == reportFieldList.size()){
                        tempHtml.append("<tr>");
                        tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"background-color:#EAF2FA; color:#000000; font-size:10px;font-weight: 500;border-bottom: 1px solid #F4F4F6;border-left: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
                        tempHtml.append("<td height=\"30px\" nowrap=\"nowrap\" align=\"left\" style=\"font-size:10px;;padding:5px 5px; line-height:12px;border-bottom:1px solid #F4F4F6;border-right:1px solid #F4F4F6;\" colspan=\"3\">").append(vMap.get(reportField.getName())).append("</td>");
                        tempHtml.append("</tr>");
                    }else if(flag%2 == 0){
                        tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"background-color:#EAF2FA; color:#000000; font-size:10px;font-weight: 500;border-bottom: 1px solid #FFFFFF;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
                        tempHtml.append("<td height=\"30px\" nowrap=\"nowrap\" align=\"left\" style=\"font-size:10px;;padding:5px 5px; line-height:12px;border-bottom:1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(reportField.getName())).append("</td>");
                        tempHtml.append("</tr>");
                    }else{
                        tempHtml.append("<tr>");
                        tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"background-color:#EAF2FA; color:#000000; font-size:10px;font-weight: 500;border-bottom: 1px solid #FFFFFF;border-left: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
                        tempHtml.append("<td height=\"30px\" width=\"30%\" nowrap=\"nowrap\" align=\"left\" style=\"font-size:10px;;padding:5px 5px; line-height:12px;border-bottom:1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(reportField.getName())).append("</td>");
                    }
                }
                flag++;
            }
            tempHtml.append("</tbody></table></div>");
        }
        return tempHtml;
    }

    /** 个性化数据-横着单列标题排列 reportFieldList:标题行 value:标题行对应的字段对应的值 */
    public static StringBuffer oneTitleHtml(List<ReportField> reportFieldList, List<Map<String,Object>> value){
        StringBuffer tempHtml= new StringBuffer();
        for(Map<String, Object> vMap : value){
            tempHtml.append("<div class=\"table_box_b\">");
            tempHtml.append("<table width=\"100%\" border=\"0\" cellspacing=\"0\" style=\"border-right:1px solid #cdd3e0; border-top:1px solid #cdd3e0;\">");
            tempHtml.append("<tbody>");
            int flag = 1;
            for(ReportField reportField : reportFieldList){
                if(flag == 1){
                    tempHtml.append("<tr>");
                    tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"").append(ReportLabelUtils.TH_FONT).append("border-top:1px solid #F4F4F6;border-bottom: 1px solid #F4F4F6;border-left: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
                    tempHtml.append("<td height=\"30px\" width=\"75%\" nowrap=\"nowrap\" align=\"left\" style=\"").append(ReportLabelUtils.TD_FONT).append("border-top:1px solid #F4F4F6;border-bottom:1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(reportField.getName())).append("</td>");
                    tempHtml.append("</tr>");
                }else{
                    tempHtml.append("<tr>");
                    tempHtml.append("<th height=\"30px\" width=\"25%\" nowrap=\"nowrap\" align=\"center\" style=\"").append(ReportLabelUtils.TH_FONT).append("border-bottom: 1px solid #F4F4F6;border-left: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(reportField.getDesc()).append("</th>");
                    tempHtml.append("<td height=\"30px\" width=\"75%\" nowrap=\"nowrap\" align=\"left\" style=\"").append(ReportLabelUtils.TD_FONT).append("border-bottom: 1px solid #F4F4F6;border-right:1px solid #F4F4F6;\">").append(vMap.get(reportField.getName())).append("</td>");
                    tempHtml.append("</tr>");
                }
                flag++;
            }
            tempHtml.append("</tbody></table></div>");
        }
        return tempHtml;
    }

    /** 文本内容 content:文本内容;level:标题等级 */
    public static StringBuffer title(String content, int level){
        StringBuffer description = new StringBuffer();
        if(level == 1) {
            description.append("<br/><div align=\"left\"><img style=\"height:30px;width:520px;\" src=\"").append(forTitle1(content)).append("\"/></div>");
        }
        if(level == 2) {
            description.append("<br/><div align=\"left\"><img style=\"height:30px;width:520px;\" src=\"").append(forTitle2(content)).append("\"/></div>");
        }
        if(level == 3) {
            description.append("<br/><div>");
            description.append("<img style=\"height: 15px; width:10px\" src=\"").append(ConstantConfig.RISK_IMAGE + "三级标题.jpg").append("\"/>&thinsp; <span style=\"font-weight: 900;color:#005BAB;\">").append(content).append("</span>");
            description.append("</div>");
        }
        return description;
    }

    /** 文本内容 content:文本内容;fontSize:字体大小;color:字体颜色;backgroundColor:背景色 */
    public static StringBuffer description(String content, int fontSize, String color, String backgroundColor, int fontWeight){
        StringBuffer description = new StringBuffer();
//        description.append("<br/>");
        description.append("<div align=\"left\" style=\"");
        if(fontWeight > 0) description.append("font-weight:").append(fontWeight).append(";");
        if(fontSize > 0) description.append("font-size:").append(fontSize).append("px;");
        if(StringUtils.isNotBlank(color)) description.append("color:").append(color).append(";");
        if(StringUtils.isNotBlank(color)) description.append("padding-left:12px;padding-right:12px;padding-bottom:12px;background-color:").append(backgroundColor).append(";");
        description.append("line-height:").append(1.5).append(";");
        description.append("letter-spacing:").append(1.2).append("px;");
        description.append("\">");
        description.append("&nbsp;&nbsp;");
        description.append(content).append("</div>");
//        description.append("<br/>");
        return description;
    }

    /**
     * 加载图片(柱状图/折线图)
     */
    public static StringBuffer getImgOneHtml(String title, List<String> xData, List<Double> yDataList, List<Color> colorList, String yUnit, int type,
                                             int width, int height, double SpaceFill, StringBuffer tempHtml) {
        if (xData == null || xData.size() == 0) return tempHtml;
        String path = new HistogramChar<>().getChart2(title, xData, yDataList, colorList, yUnit, type,
                width, height, SpaceFill);
        tempHtml.append("<div align=\"center\"><img style=\"height:262px;width:700px\" src=\"").append(path).append("\"/></div>");
        return tempHtml;
    }

    /**
     * 加载图片(扇形图)
     */
    public static StringBuffer getImgPieHtml(List<String> keyList, List<Double> rDataList, Boolean isTitleVisible, String title,
                                             Boolean isLegendVisible, RectangleEdge rectangleEdge, int flag, StringBuffer tempHtml) {
        if (rDataList == null || rDataList.size() == 0) return tempHtml;
        tempHtml.append("<div align=\"center\">");
        String path = Pie2Chart.pieChart(keyList, rDataList, isTitleVisible, title, isLegendVisible, rectangleEdge, flag);
        tempHtml.append("<img style=\"width:700px\" src=\"").append(path).append("\"/>");
        tempHtml.append("</div>");
        return tempHtml;
    }

    /**
     * 加图片做pdf首页的底纹
     * @param srcPdfPath 源文件
     * @param tagetPdfPath  加工后文件
     * @param pageNum        页码
     * @param type           底纹类型:0:客户风险分析报告;1:客群风险分析报告
     */
    public static void addImageWatermark(String srcPdfPath, String tagetPdfPath, int pageNum, int type) {
        try {
            // 加载PDF文件
            PdfReader reader = new PdfReader(srcPdfPath);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tagetPdfPath));
            int total = reader.getNumberOfPages();
            if(pageNum < 0) pageNum = total + pageNum;
            // 首页-头部-底纹
            com.itextpdf.text.Image watermark = com.itextpdf.text.Image.getInstance(ConstantConfig.RISK_IMAGE + "首页-头部-底纹.png");
            watermark.setAbsolutePosition(-2, 705); // 设置底纹位置
            watermark.scaleAbsolute(597, 135); // 设置底纹大小
            // 首页-中间-底纹
            com.itextpdf.text.Image watermark1 = com.itextpdf.text.Image.getInstance(ConstantConfig.RISK_IMAGE + "首页-中间-底纹.png");
            watermark1.setAbsolutePosition(-2, 155); // 设置底纹位置
            watermark1.scaleAbsolute(597, 700); // 设置底纹大小
            // 文字 - 客群风险分析报告/客户风险分析报告
            com.itextpdf.text.Image watermark_word1 = com.itextpdf.text.Image.getInstance(ConstantConfig.RISK_IMAGE + (type==0 ? "客户风险分析报告" : "客群风险分析报告") +".png");
            watermark_word1.setAbsolutePosition(200, 515); // 设置底纹位置
            watermark_word1.scaleAbsolute(360, 40); // 设置底纹大小
            // 首页-矩形-底纹
            com.itextpdf.text.Image watermark_line1 = com.itextpdf.text.Image.getInstance(ConstantConfig.RISK_IMAGE + "首页-矩形-底纹.png");
            watermark_line1.setAbsolutePosition(200, 485); // 设置底纹位置
            watermark_line1.scaleAbsolute(360, 2); // 设置底纹大小
            // 首页-分割线-底纹
            com.itextpdf.text.Image watermark_line2 = com.itextpdf.text.Image.getInstance(ConstantConfig.RISK_IMAGE + "首页-分割线-底纹.png");
            watermark_line2.setAbsolutePosition(-2, 153); // 设置底纹位置
            watermark_line2.scaleAbsolute(597, 1); // 设置底纹大小
            // 首页-底部-底纹
            com.itextpdf.text.Image watermark2 = com.itextpdf.text.Image.getInstance(ConstantConfig.RISK_IMAGE + "首页-底部-底纹.png");
            watermark2.setAbsolutePosition(-2, -2); // 设置底纹位置
            watermark2.scaleAbsolute(597, 215); // 设置底纹大小
            // 将底纹添加到首页页面
            PdfContentByte content = stamper.getOverContent(pageNum);
            content.addImage(watermark);
            content.addImage(watermark1);
            content.addImage(watermark_word1);
            content.addImage(watermark_line1);
            content.addImage(watermark_line2);
            content.addImage(watermark2);
            // 获取原始PDF内容并添加到新的PDF中
            PdfImportedPage page = stamper.getImportedPage(reader, pageNum);
            int rotation = reader.getPageRotation(1);
            if (rotation == 90 || rotation == 270) {
                content.addTemplate(page, 0, -1, 1, 0, 0, 610);
            } else {
                content.addTemplate(page, 1, 0, 0, 1, 0, 0);
            }
            // 关闭PDF文件
            stamper.close();
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 加图片做pdf页眉
     * @param pdfFilePath 源文件
     * @param toFdfPath  加工后文件
     */
    public static void pdf_yemei(String companyName, String pdfFilePath, String toFdfPath) {
        try {
            String NoFirstYeMeiImage = baseUrl + companyName + "合并后图片.png";
            getImageAndChar2(ConstantConfig.RISK_IMAGE + "非首页-页眉.jpg", NoFirstYeMeiImage, companyName);
            // 加载PDF文件
            PdfReader reader = new PdfReader(pdfFilePath);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(toFdfPath));
            int total = reader.getNumberOfPages();
            int pageNum = 1;
            while(pageNum < total) {
                pageNum++;
                if (pageNum <= 1) continue;
                // 非首页-页眉
                com.itextpdf.text.Image yemei = com.itextpdf.text.Image.getInstance(NoFirstYeMeiImage);
                yemei.setAbsolutePosition(0, 807); // 设置底纹位置
                yemei.scaleAbsolute(597, 33); // 设置底纹大小
                // 将底纹添加到首页页面
                PdfContentByte content = stamper.getOverContent(pageNum);
                content.addImage(yemei);
                // 获取原始PDF内容并添加到新的PDF中
                PdfImportedPage page = stamper.getImportedPage(reader, pageNum);
                int rotation = reader.getPageRotation(1);
                if (rotation == 90 || rotation == 270) {
                    content.addTemplate(page, 0, -1, 1, 0, 0, 610);
                } else {
                    content.addTemplate(page, 1, 0, 0, 1, 0, 0);
                }
            }
            // 关闭PDF文件
            stamper.close();
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** 页眉专用 -- 合并文字和图片,生成一个新的图片 */
    public static void getImageAndChar2(String srcImagePath, String toImagePath, String content){
        try {
            // 读取第一张图片
            BufferedImage image1 = ImageIO.read(new File(srcImagePath));
            /** 创建一个新的BufferedImage,其宽度为两张图片的宽度之和,高度为两张图片中较高的那张图片的高度 */
            BufferedImage mergedImage = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
            /** 创建Graphics2D对象以在新的合并图像上绘图 */
            Graphics2D g2d = mergedImage.createGraphics();
            g2d.drawImage(image1, 0, 0, null);
            /** 设置字体和颜色 */
            Font font = new Font("黑体", Font.BOLD, 70); g2d.setFont(font); g2d.setColor(Color.white);
            // 在合并图像上绘制汉字
            String text = content; // 这里可以替换为您想要显示的汉字
            int x = 270; // 汉字的起始位置,可以根据需要调整
            int y = 90; // 汉字的垂直位置,可以根据需要调整
            for (int i = 0; i < text.length(); i++) {
                char c = text.charAt(i);
                g2d.drawString(String.valueOf(c), x + i * 70, y); // 在每个汉字之间添加一些间距,可以根据需要调整间距大小
            }
            // 释放此图形的上下文以及它使用的所有系统资源
            g2d.dispose();
            // 将合并后的图像写入文件
            ImageIO.write(mergedImage, "png", new File(toImagePath));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** 1级标题专用 -- 合并文字和图片,生成一个新的图片 */
    public static String forTitle1(String content){
        String toImagePath = baseUrl + System.currentTimeMillis();
        try {
            // 读取第一张图片
            BufferedImage image1 = ImageIO.read(new File(ConstantConfig.RISK_IMAGE + "一级标题.jpg"));
            /** 创建一个新的BufferedImage,其宽度为两张图片的宽度之和,高度为两张图片中较高的那张图片的高度 */
            BufferedImage mergedImage = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
            /** 创建Graphics2D对象以在新的合并图像上绘图 */
            Graphics2D g2d = mergedImage.createGraphics();
            g2d.drawImage(image1, 0, 0, null);
            /** 设置字体和颜色 */
            Font font = new Font("黑体", Font.BOLD, 50); g2d.setFont(font); g2d.setColor(new Color(0, 91, 171));
            // 在合并图像上绘制汉字
            String text = content; // 这里可以替换为您想要显示的汉字
            int x = 240; // 汉字的起始位置,可以根据需要调整
            int y = 60; // 汉字的垂直位置,可以根据需要调整
            for (int i = 0; i < text.length(); i++) {
                char c = text.charAt(i);
                g2d.drawString(String.valueOf(c), x + i * 55, y); // 在每个汉字之间添加一些间距,可以根据需要调整间距大小
            }
            // 释放此图形的上下文以及它使用的所有系统资源
            g2d.dispose();
            // 将合并后的图像写入文件
            ImageIO.write(mergedImage, "jpg", new File(toImagePath));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return toImagePath;
    }

    /** 2级标题专用 -- 合并文字和图片,生成一个新的图片 */
    public static String forTitle2(String content){
        String toImagePath = baseUrl + System.currentTimeMillis();
        try {
            // 读取第一张图片
            BufferedImage image1 = ImageIO.read(new File(ConstantConfig.RISK_IMAGE + "二级标题.jpg"));
            /** 创建一个新的BufferedImage,其宽度为两张图片的宽度之和,高度为两张图片中较高的那张图片的高度 */
            BufferedImage mergedImage = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
            /** 创建Graphics2D对象以在新的合并图像上绘图 */
            Graphics2D g2d = mergedImage.createGraphics();
            g2d.drawImage(image1, 0, 0, null);
            /** 设置字体和颜色 */
            Font font = new Font("黑体", Font.BOLD, 40); g2d.setFont(font); g2d.setColor(Color.white);
            // 在合并图像上绘制汉字
            String text = content; // 这里可以替换为您想要显示的汉字
            int x = 50; // 汉字的起始位置,可以根据需要调整
            int y = 50; // 汉字的垂直位置,可以根据需要调整
            for (int i = 0; i < text.length(); i++) {
                char c = text.charAt(i);
                g2d.drawString(String.valueOf(c), x + i * 45, y); // 在每个汉字之间添加一些间距,可以根据需要调整间距大小
            }
            // 释放此图形的上下文以及它使用的所有系统资源
            g2d.dispose();
            // 将合并后的图像写入文件
            ImageIO.write(mergedImage, "jpg", new File(toImagePath));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return toImagePath;
    }

}

实体类

package com.ruoyi.business.bankInterface.controller.risk.model1;

import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.business.bankInterface.controller.risk.utils.ReportLabelUtils;
import com.ruoyi.business.bankInterface.controller.risk.utils.ReportUtils2;
import com.ruoyi.business.bankInterface.controller.tax.utils.annotation.ReportFieldAnnotation;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 公司治理
 */
@Data
public class K3 {
    @ReportFieldAnnotation(desc = "变更时间", sort = 1)
    private String bgsj;
    @ReportFieldAnnotation(desc = "变更事项", sort = 2)
    private String bgsx;
    @ReportFieldAnnotation(desc = "变更前", sort = 3)
    private String bgq;
    @ReportFieldAnnotation(desc = "变更后", sort = 4)
    private String bgh;
    @ReportFieldAnnotation(desc = "风险等级", sort = 5)
    private String fxdj;
    private String fxxl;

    public String getBgq() {
        if(StringUtils.isNotBlank(bgq)){
            int start = bgq.indexOf("(");
            int end = bgq.indexOf(")");
            if(start > 0 && end > 0){
                bgq = bgq.substring(0, start)+bgq.substring(end+1);
            }
        }
        return bgq;
    }

    public String getBgh() {
        if(StringUtils.isNotBlank(bgh)){
            int start = bgh.indexOf("(");
            int end = bgh.indexOf(")");
            if(start > 0 && end > 0){
                bgh = bgh.substring(0, start)+bgh.substring(end+1);
            }
        }
        return bgh;
    }

    public String getFxdj() {
        if(StringUtils.isNotBlank(fxdj)){
            if("红".equals(fxdj)) fxdj = "严重警告";
            if("橙".equals(fxdj)) fxdj = "警告";
            if("黄".equals(fxdj)) fxdj = "关注";
            if("提示".equals(fxdj)) fxdj = "提示";
        }
        return fxdj;
    }

    /** 公司治理 */
    public static StringBuffer getHtml1(List<K3> k3List){
        StringBuffer k3Html = new StringBuffer();
        if(k3List == null || k3List.size() == 0){
            k3Html.append(ReportUtils2.description("监测本公司近一年的注册资本、企业股东或高管、企业法定代表人变更情况,皆未发现有异常更改记录。",  12, ReportLabelUtils.NO_FX_COLOR, ReportLabelUtils.NO_FX_BACKGROUND_COLOR, 0));
        }else{
            k3Html.append(ReportUtils2.description("监测本公司近一年的注册资本、企业股东或高管、企业法定代表人变更情况,发现以下风险:",  12, ReportLabelUtils.FX_COLOR, ReportLabelUtils.FX_BACKGROUND_COLOR, 0));
            ReportUtils2.listHtml(K3.class, JSONArray.toJSONString(changeK3List(k3List)), k3Html, 1, 1);
        }
        return k3Html;
    }

    /** 经营资质 */
    public static StringBuffer getHtml2(List<K3> k3List){
        StringBuffer k3Html = new StringBuffer();
        k3Html.append(ReportUtils2.title("经营资质", 3));
        if(k3List == null || k3List.size() == 0){
            k3Html.append(ReportUtils2.description("本风险监测时段中,未监测到企业存在经营资质风险。",  12, ReportLabelUtils.NO_FX_COLOR, ReportLabelUtils.NO_FX_BACKGROUND_COLOR, 0));
        }else{
            StringBuffer text = new StringBuffer();
            text.append("在本风险监测时段中,监测到"+k3List.size()+"条经营资质风险,其中");
            if (k3List.stream().filter(item->"R004".equals(item.getFxxl())).count()>0)
                text.append("企业工商信息变更风险"+k3List.stream().filter(item->"R004".equals(item.getFxxl())).count()+
                        "条。");
            if (k3List.stream().filter(item->"R005".equals(item.getFxxl())).count()>0)
                text.append("企业经营期限到期或状态为‘非存续’风险"+k3List.stream().filter(item->"R005".equals(item.getFxxl())).count()+
                        "条。");

            k3Html.append(ReportUtils2.description(
//                    "本风险监测时段中,监测到企业存在"+k3List.size()+"条经营资质风险,其中企业工商信息变更风险"+
//                    k3List.stream().filter(item ->"R004".equals(item.getFxxl())).count()+"条,企业经营期限到期或状态为‘非存续’风险"+
//                    k3List.stream().filter(item ->"R005".equals(item.getFxxl())).count()+"条。"
                    text.toString()
                    ,  12, ReportLabelUtils.FX_COLOR, ReportLabelUtils.FX_BACKGROUND_COLOR, 0));
            ReportUtils2.listHtml(K3.class, JSONArray.toJSONString(k3List), k3Html, 1, 1);
        }
        return k3Html;
    }

    /** 剔除数据 */
    private static List<K3> changeK3List(List<K3> k3List){
        if(k3List == null || k3List.size() == 0) return k3List;
        List<K3> k3NewList = new ArrayList<>();
        for(K3 k3 : k3List){
            /** 剔除掉企业注册资金非减少的数据 */
            try {
                if("企业注册资本抽离".equals(k3.getBgsx()) && Double.valueOf(k3.getBgq()) <= Double.valueOf(k3.getBgh())) continue;
                k3NewList.add(k3);
            }catch (Exception e){

            }

        }
        return k3NewList;
    }

   
}

pdf工具类(根据html生成pdf文件)

package com.ruoyi.business.bankInterface.controller.risk;

import com.itextpdf.text.*;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.ruoyi.business.bankInterface.controller.tax.utils.DateUtils;
import com.ruoyi.business.bankInterface.domain.utils.AsianFontProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.List;

public class PdfUtils {
//    public static final String imgPrefixUrl="/home/cc/data/images/";
        public static final String imgPrefixUrl="D:\\test\\images\\";
    private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);

    //根据html生成pdf文件
//    public static void htmp2pdf(String creditName, String pdfName,String template){
//        Document document = null;
//        PdfWriter pdfWriter = null;
//        try {
//            document = new Documapi/v1/financial/queryFinancialDataent(PageSize.A4);
//            pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(pdfName));
//            document.open();
//            document.addAuthor("Soren");
//            document.addCreator("Soren");
//            document.addCreationDate();
//            document.addTitle("测试生成pdf");
//            XMLWorkerHelper helper = XMLWorkerHelper.getInstance();
//            //读取模板文件并且写入String中
//            InputStream in = new FileInputStream(template);
//            InputStreamReader isr = new InputStreamReader(in, "utf-8");
//            BufferedReader brd = new BufferedReader(isr);
//            StringBuffer sbf = new StringBuffer();
//            String line = "";
//            while((line=brd.readLine())!=null) {
//                sbf.append(line);
//            }
//            String htmlTemplete = sbf.toString();
//
//            //此处替换特定字符或者拼接html代码的处理
//            htmlTemplete = htmlTemplete.replaceAll("###TITLE###", "长三角征信链企业征信报告");
//            htmlTemplete = htmlTemplete.replaceAll("###INTRODUCE###", "该报告来源:"+creditName);
//            htmlTemplete = htmlTemplete.replaceAll("###date###", "日期:"+DateUtils.convertYMD(new Date()));
            htmlTemplete = htmlTemplete.replaceAll("###date###", "日期:"+UUID.randomUUID().toString());
//            //模板不支持svg格式图片
//            helper.parseXHtml(pdfWriter, document, new ByteArrayInputStream(htmlTemplete.getBytes("UTF-8")), Charset.forName("utf8"), new AsianFontProvider());
//            logger.info("根据html生成pdf文件完成!");
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        } catch (DocumentException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }finally {
//            if(document!=null)
//                //如果不关闭则会生成空的破损的pdf
//                document.close();
//        }
//        return;
//    }

    //根据html生成pdf文件
//    public static void htmppdf(String htmlStr, String pdfName, Boolean isTrue){
//        Document document = null;
//        PdfWriter pdfWriter = null;
//        try {
//            document = new Document(PageSize.A4);
//            pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(pdfName));
//            document.open();
//            document.addAuthor("Soren");
//            document.addCreator("Soren");
//            document.addCreationDate();
//            document.addTitle("测试生成pdf");
//            XMLWorkerHelper helper = XMLWorkerHelper.getInstance();
//            //模板不支持svg格式图片
//            helper.parseXHtml(pdfWriter, document, new ByteArrayInputStream(htmlStr.getBytes("UTF-8")), Charset.forName("utf8"), new AsianFontProvider());
//            logger.info("根据html生成pdf文件完成!");
//            if(isTrue){
//                //先把文件名字更换为临时名字
//                String temName = pdfName.substring(0, pdfName.lastIndexOf(".")) + "temp" + pdfName.substring(pdfName.lastIndexOf("."));//临时文件名
//                renameFile(pdfName, temName);
//                //文件加水印,输出原文件名
//                waterMark(temName,
//                        pdfName);
//            }
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        } catch (DocumentException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }finally {
//            if(document!=null)
//                //如果不关闭则会生成空的破损的pdf
//                document.close();
//        }
//        return;
//    }
    /*
     * 合并pdf文件
     * @param files 要合并文件数组(绝对路径如{ "e:\\1.pdf", "e:\\2.pdf" ,
     * "e:\\3.pdf"}),合并的顺序按照数组中的先后顺序,如2.pdf合并在1.pdf后。
     * @param newfile 合并后新产生的文件绝对路径,如 e:\\temp\\tempNew.pdf,
     * @return boolean 合并成功返回true;否则,返回false
     *
     */
    public static boolean mergePdfFiles(List<String> files, String newfile) {
        boolean retValue = false;
        Document document = null;
        OutputStream os = null;
        PdfReader reader1 = null;
        PdfCopy copy = null;
        PdfReader reader = null;
        try {
            reader1 = new PdfReader(files.get(0));
            document = new Document(reader1.getPageSize(1));
            os = new FileOutputStream(newfile);
            copy = new PdfCopy(document, os);
            document.open();
            for (int i = 0; i < files.size(); i++) {
                reader = new PdfReader(files.get(i));
                int n = reader.getNumberOfPages();
                for (int j = 1; j <= n; j++) {
                    document.newPage();
                    PdfImportedPage page = copy.getImportedPage(reader, j);
                    copy.addPage(page);
                }
            }
            retValue = true;
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("合并文件.error:"+e.getMessage());
        } finally {
            try {
                if(reader != null){
                    reader.close();
                }
                if(copy != null){
                    copy.close();
                }
                if(os != null){
                    os.close();
                }
                if(reader1 != null){
                    reader1.close();
                }
                if(document != null){
                    document.close();
                }
            }catch (Exception e){
                e.printStackTrace();
                logger.info(e.getMessage());
            }

        }
        logger.info("合并pdf文件完成!");
        return retValue;
    }

    /*
     * pdf文件是否加密授权(加密文件不可更改。加一层水印,如果加成功,表示不是加密文件)
     */
    public static boolean isEncryption(String oFile) {
        boolean retValue = false;
        PdfReader reader = null;
        PdfStamper stamper = null;
        try {
            String waterMarkName = "长三角征信链";
            reader = new PdfReader(oFile);
            stamper = new PdfStamper(reader, new FileOutputStream(
                    oFile+"123"));

            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",   BaseFont.EMBEDDED);
            Rectangle pageRect = null;
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.2f);//控制水印深浅
            gs.setStrokeOpacity(0.4f);
            int total = reader.getNumberOfPages() + 1;
            JLabel label = new JLabel();
            FontMetrics metrics;
            int textH = 0;
            int textW = 0;
            label.setText(waterMarkName);
            metrics = label.getFontMetrics(label.getFont());
            textH = metrics.getHeight();
            textW = metrics.stringWidth(label.getText());

            PdfContentByte under;
            for (int i = 1; i < total; i++) {
                pageRect = reader.getPageSizeWithRotation(i);
                under = stamper.getOverContent(i);
                under.saveState();
                under.setGState(gs);
                under.beginText();
                under.setFontAndSize(base, 15);//控制水印字体大小
                // 水印文字成30度角倾斜
                //你可以随心所欲的改你自己想要的角度
                for (int height = interval + textH; height < pageRect.getHeight();
                     height = height + textH*5) {
                    for (int width = interval + textW; width < pageRect.getWidth() + textW;
                         width = width + textW*2) {
                        under.showTextAligned(Element.ALIGN_LEFT
                                , waterMarkName, width - textW,
                                height - textH, 30);
                    }
                }
                // 添加水印文字
                under.endText();
            }
        } catch (Exception e) {
            retValue = true;
        }finally {
            try {
                //一定不要忘记关闭流
                if(stamper != null){
                    stamper.close();
                }
                if(reader != null){
                    reader.close();
                }
            }catch (Exception e){
                e.printStackTrace();
                logger.info(e.getMessage());
            }
        }
        return retValue;
    }


    /**文件转换为字节数组
     * @param file
     * @return
     */
    public static byte[] fileToByteArray(File file) {
        byte[] imagebs = null;
        FileInputStream fis = null;
        ByteArrayOutputStream baos = null;
        try {
            //读取输入的文件====文件输入流
            fis = new FileInputStream(file);
            //字节数组输出流  在内存中创建一个字节数组缓冲区,所有输出流的数据都会保存在字符数组缓冲区中
            baos = new ByteArrayOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            //将文件读入到字节数组中
            while ((len = fis.read(buffer)) != -1) {
                // 将指定字节数组中从偏移量 off 开始的 len 个字节写入此字节数组输出流。
                baos.write(buffer, 0, len);
            }
            imagebs = baos.toByteArray();//当前输出流的拷贝  拷贝到指定的字节数组中


            baos.close();
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                //一定不要忘记关闭流
                if(baos != null){
                    baos.close();
                }
                if(fis != null){
                    fis.close();
                }
            }catch (Exception e){
                e.printStackTrace();
                logger.info(e.getMessage());
            }

        }

        return imagebs;

    }

    /**根据byte[] 数组生成文件  (在本地)
     * @param bfile  字节数组
     * @param filePath 文件路径
     */
    public static void getFile(byte[] bfile, String filePath) {
        BufferedOutputStream bos = null;  //带缓冲得文件输出流
        FileOutputStream fos = null;      //文件输出流
        File file = null;
        try {
            String path = filePath.substring(0,filePath.lastIndexOf(File.separator));
            File dir = new File(path);
            if(!dir.exists()){//判断文件目录是否存在
                dir.mkdirs();
            }
            file = new File(filePath);  //文件路径+文件名
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bfile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     * 给文件重命名
     */
    public static void renameFile(String oldname,String newname){
        logger.info("重命名文件开始oldname=" + oldname + "&newname=" + newname);
        if(!oldname.equals(newname)){//新的文件名和以前文件名不同时,才有必要进行重命名
            File oldfile=new File(oldname);
            File newfile=new File(newname);
            if(!oldfile.exists()){
                logger.info("重命名文件不存在!");
                return;//重命名文件不存在
            }
            if(newfile.exists())//若在该目录下已经有一个文件和新文件名相同,则不允许重命名
                logger.info(newname+"已经存在!");
            else{
                oldfile.renameTo(newfile);
            }
        }else{
            logger.info("新文件名和旧文件名相同...");
        }
    }
    private static int interval = -5;
    //给pdf加水印
    public static void waterMark(String inputFile,
                                 String outputFile,
                                 String userName,
                                 String time
    ) {
        PdfReader reader = null;
        PdfStamper stamper = null;
        FileOutputStream outputStream = null;
        try {
            String waterMarkName = "长三角";
            reader = new PdfReader(inputFile);
            outputStream = new FileOutputStream(outputFile);
            stamper = new PdfStamper(reader, outputStream);

            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",   BaseFont.EMBEDDED);

            Rectangle pageRect = null;
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.15f);//控制水印深浅
            gs.setStrokeOpacity(0.4f);
            int total = reader.getNumberOfPages() + 1;

            JLabel label = new JLabel();
            FontMetrics metrics;
            int textH = 0;
            int textW = 0;
            label.setText(waterMarkName);
            metrics = label.getFontMetrics(label.getFont());
            textH = metrics.getHeight();
            textW = metrics.stringWidth(label.getText());

            PdfContentByte under;
            for (int i = 1; i < total; i++) {
                pageRect = reader.getPageSizeWithRotation(i);
                under = stamper.getOverContent(i);
                under.saveState();
                under.setGState(gs);
                under.beginText();
                under.setFontAndSize(base, 18);//控制水印字体大小
                // 水印文字成30度角倾斜
                // 获取每一页的高度、宽度
                float pageHeight = pageRect.getHeight();
                float pageWidth = pageRect.getWidth();

                // 根据纸张大小多次添加, 水印文字成30度角倾斜
                for (int height = interval + textH; height < pageHeight; height = height + textH * 8) {
                    for (int width = interval + textW; width < pageWidth + textW; width = width + textW * 3) {
                        // 将分段的字段进行输出编写
                        under.setFontAndSize(base, 18);//控制水印字体大小
                        under.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height -textH , 30);
                        under.setFontAndSize(base, 12);//控制水印字体大小
                        under.showTextAligned(Element.ALIGN_LEFT, userName, width - textW+12, height -textH*2 , 30);
                        under.setFontAndSize(base, 12);//控制水印字体大小
                        under.showTextAligned(Element.ALIGN_LEFT, time, width - textW+24, height -textH*3 , 30);
                    }
                }

                under.endText();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(stamper != null){
                    stamper.close();
                }
                if(outputStream != null){
                    outputStream.close();
                }
                if(reader != null){
                    reader.close();
                }
            }catch (Exception e){
                e.printStackTrace();
                logger.info(e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
//        renameFile("D:\\test\\123.pdf","D:\\test\\模板.pdf");
        //根据html生成pdf
//        PdfUtils.htmp2pdf("苏信", "D:\\test\\1234.pdf","D:\\test\\template(3).html");
//        PdfUtils.htmp2pdf("<br/>苏州征信、江苏省南京分融服务信用平台、安徽征信", "D:\\test\\123.pdf","D:\\test\\first.html");
//        D:\test\template(1).html

        //合并pdf文件
//        List<String> files = new ArrayList<>();
//        files.add("D:\\test\\123.pdf");
//        files.add("D:\\test\\1234.pdf");
//        String savepath = "D:\\test\\xiaomingnew.pdf";
//        Boolean bool = mergePdfFiles(files, savepath);
//        //给pdf加水印
//        waterMark("D:\\test\\xiaomingnew.pdf", "D:\\test\\666.pdf", "xoads", "2021");
//        String pdfName = "/d:cdc.cdsc.csdcs";
//        String begin = pdfName.substring(0, pdfName.lastIndexOf("."));
//        String endStr = pdfName.substring(pdfName.lastIndexOf("."));
//        System.out.println("begin="+begin);
        System.out.println("endStr=" + "xxx.pdf".substring(0, "xxx.pdf".length() - 4));


//        try {
//            //合并pdf文件
//            Boolean bool = PdfUtils.mergePdfFiles(Arrays.asList("D:\\test\\666.pdf","D:\\test\\666.pdf"), "D:\\test\\777.pdf");
//        }catch (Exception e){
//        }


    }

    //解压文件
    public static void zipToFile(String sourceFile, String toFolder) throws Exception {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        ZipFile zfile = null;
        try {
            String toDisk = toFolder;//接收解压后的存放路径
            zfile = new ZipFile(sourceFile);//连接待解压文件
            logger.info("要解压的文件是:"+ zfile.getName());
            Enumeration zList = zfile.entries();//得到zip包里的所有元素
            ZipEntry ze = null;
            byte[] buf = new byte[1024];
            while (zList.hasMoreElements()) {
                ze = (ZipEntry) zList.nextElement();
                if (ze.isDirectory()) {
                    logger.info("打开zip文件里的文件夹:"+ ze.getName() +"skipped...");
                    continue;
                }
                logger.info("zip包里的文件:"+ ze.getName() +" "+"大小为:" + ze.getSize() +"KB");
                //以ZipEntry为参数得到一个InputStream,并写到OutputStream中
                outputStream = new BufferedOutputStream(
                        new FileOutputStream(getRealFileName(toDisk, ze.getName())));
                inputStream = new BufferedInputStream(zfile.getInputStream(ze));
                int readLen = 0;
                while ((readLen = inputStream.read(buf, 0, 1024)) != -1) {
                    outputStream.write(buf, 0, readLen);
                }
                inputStream.close();
                outputStream.close();
                logger.info("已经解压出:"+ ze.getName());
            }
            zfile.close();
        }catch (Exception e){
            e.printStackTrace();
            logger.info(e.getMessage());
        }finally {
            try {
                if(inputStream != null){
                    inputStream.close();
                }
                if(outputStream != null){
                    outputStream.close();
                }
                if(zfile != null){
                    zfile.close();
                }

            }catch (Exception e){
                e.printStackTrace();
                logger.info(e.getMessage());
            }
        }

    }

    private static File getRealFileName(String zippath, String absFileName){
        String[] dirs = absFileName.split("/", absFileName.length());
        File ret = new File(zippath);// 创建文件对象
        if (dirs.length > 1) {
            for (int i = 0; i < dirs.length - 1; i++) {
                ret = new File(ret, dirs[i]);
            }
        }
        if (!ret.exists()) {// 检测文件是否存在
            ret.mkdirs();// 创建此抽象路径名指定的目录
        }
        ret = new File(ret, dirs[dirs.length - 1]);// 根据 ret 抽象路径名和 child 路径名字符串创建一个新 File 实例
        return ret;
    }


    //根据html生成pdf文件
    public static void htmp2pdf(String creditName, String htmlTemplate,String pdfName,boolean hasBackground,boolean isTrue){
        String path = pdfName.substring(0,pdfName.lastIndexOf(File.separator));
        File dir = new File(path);
        if(!dir.exists()){//判断文件目录是否存在
            dir.mkdirs();
        }
        Document document = null;
        PdfWriter pdfWriter = null;
        FileOutputStream fileOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        try {
            document = new Document(PageSize.A4);
            fileOutputStream = new FileOutputStream(pdfName);
            pdfWriter = PdfWriter.getInstance(document, fileOutputStream);

            document.open();
            document.addAuthor("Soren");
            document.addCreator("Soren");
            document.addCreationDate();
            document.addTitle("测试生成pdf");
            if(hasBackground){
                Image image = Image.getInstance(imgPrefixUrl+"background.jpg");
                /* 设置图片的位置 */
                image.setAbsolutePosition(0, 80);
                /* 设置图片的大小 */
                image.scaleAbsolute(595, 842);
                document.add(image);
            }

            //此处替换特定字符或者拼接html代码的处理
            htmlTemplate = htmlTemplate.replaceAll("###TITLE###", "长三角报告");
            htmlTemplate = htmlTemplate.replaceAll("###INTRODUCE###", "该报告来源:"+creditName);
            htmlTemplate = htmlTemplate.replaceAll("###date###", "日期:"+ DateUtils.convertYMD(new Date()));
            XMLWorkerHelper helper = XMLWorkerHelper.getInstance();

            //模板不支持svg格式图片
            byteArrayInputStream = new ByteArrayInputStream(htmlTemplate.getBytes("UTF-8"));
            helper.parseXHtml(pdfWriter, document, byteArrayInputStream, Charset.forName("utf8"), new AsianFontProvider());
            logger.info("pdf文件转换完成!");
//            if(isTrue){
//                //先把文件名字更换为临时名字
//                String begin = pdfName.substring(0, pdfName.lastIndexOf("."));
//                String endStr = pdfName.substring(pdfName.lastIndexOf("."));
//                String temName = begin + "temp" + endStr;
//                renameFile(pdfName, temName);
//                //文件加水印,输出原文件名
//                waterMark(temName,
//                        pdfName);
//            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {

                if(document!=null){
                    document.close();
                }
                if(fileOutputStream!=null){
                    fileOutputStream.close();
                }if(byteArrayInputStream!=null){
                    byteArrayInputStream.close();
                }
                if(pdfWriter!=null){
                    pdfWriter.close();
                }
            }catch (Exception e){
                e.printStackTrace();
                logger.info(e.getMessage());
            }


        }
    }

    /**
     * 删除单个文件
     *
     * @param fileName
     *            要删除的文件的文件名
     * @return 单个文件删除成功返回true,否则返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
        if (file.exists() && file.isFile()) {
            if (file.delete()) {
                logger.info("删除单个文件" + fileName + "成功!");
                return true;
            } else {
                logger.info("删除单个文件" + fileName + "失败!");
                return false;
            }
        } else {
            logger.info("删除单个文件失败:" + fileName + "不存在!");
            return false;
        }
    }
}

页脚工具类

package com.ruoyi.business.bankInterface.controller.tax.utils.pdf;

import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.*;

/**
 *  @description 针对页码生成的PDF事件监听
 *  @author myw
 *  @date 2023-4-10 20:24:21
 **/
public class PdfNumPageEventTax extends PdfPageEventHelper {

    private int total;

    PdfNumPageEventTax(){

    }

    public PdfNumPageEventTax(int num){
        this.total = num;
    }

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        try {
            if(writer.getPageNumber() ==1) return;
            // PDF文档内容
            PdfContentByte pdfContent = writer.getDirectContent();
            pdfContent.saveState();
            pdfContent.beginText();
            int footerFontSize = 10;
            // 解决页码中文无法显示 或者 显示为乱码的问题
            // 但是必须引入jar包 itext-asian-5.2.0.jar
            BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
            Font fontDetail = new Font(baseFont, footerFontSize, Font.NORMAL);
            pdfContent.setFontAndSize(baseFont, footerFontSize);
            // 页脚的页码 展示
            String footerNum = String.format("第%d页 共"+total+"页", writer.getPageNumber());
            Phrase phrase = new Phrase(footerNum, fontDetail);
            // 页码的 横轴 坐标 居中
            float x = ( document.left() + document.right() ) / 2 +10;
            // 页码的 纵轴 坐标
            float y = document.bottom(-20) ;
            // 添加文本内容,进行展示页码
            ColumnText.showTextAligned(pdfContent, Element.ALIGN_CENTER, phrase, x, y, 0);
            pdfContent.endText();
            pdfContent.restoreState();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onStartPage(PdfWriter writer, Document document) {
//        try {
//            // PDF文档内容
//            PdfContentByte pdfContent = writer.getDirectContent();
//            pdfContent.saveState();
//            pdfContent.beginText();
//            int footerFontSize = 10;
//            // 解决页码中文无法显示 或者 显示为乱码的问题
//            // 但是必须引入jar包 itext-asian-5.2.0.jar
//            BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
//            Font fontDetail = new Font(baseFont, footerFontSize, Font.NORMAL);
//            pdfContent.setFontAndSize(baseFont, footerFontSize);
//            // 页脚的页码 展示
//            String footerNum = String.format("第%d页,共"+total+"页", writer.getPageNumber());
//            Phrase phrase = new Phrase(footerNum, fontDetail);
//            // 页码的 横轴 坐标 居中
//            float x = ( document.left() + document.right() ) / 2 +10;
//            // 页码的 纵轴 坐标
//            float y = document.bottom(-20) ;
//            // 添加文本内容,进行展示页码
//            ColumnText.showTextAligned(pdfContent, Element.ALIGN_CENTER, phrase, x, y, 0);
//            pdfContent.endText();
//            pdfContent.restoreState();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        super.onStartPage(writer, document);
    }
}

柱图工具类

package com.ruoyi.business.bankInterface.controller.risk.utils.chart;

import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.business.bankInterface.ConstantConfig;
import com.ruoyi.business.bankInterface.controller.risk.utils.ColorEnum;
import org.apache.commons.lang3.StringUtils;
import org.knowm.xchart.BitmapEncoder;
import org.knowm.xchart.CategoryChart;
import org.knowm.xchart.CategoryChartBuilder;
import org.knowm.xchart.CategorySeries;
import org.knowm.xchart.internal.chartpart.Chart;
import org.knowm.xchart.style.Styler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 柱状图工具类
 */
public class HistogramChar <C extends Chart<?, ?>> {

  private static final Logger logger = LoggerFactory.getLogger(HistogramChar.class);
  private static String baseUrl = ConstantConfig.IMAGE_TEMP_FOLDER;

  public static void main(String[] args) throws IOException {
    List<String> titleList = JSONArray.parseArray("[\"严重警告\", \"警告\", \"关注\", \"提示\"]", String.class);
    List<Double> yData1 = JSONArray.parseArray("[6.0, 3.0, 1.0, 5.0]", Double.class);
    //柱状图
    String zhuImg = new HistogramChar().getChart2("风险等级分布", titleList, yData1, ColorEnum.lColorList2, "%", 0,
            700, 263, 0.2);

    logger.info("柱状图" + zhuImg);
  }

  public String getChart(String title, List<String> titleList, List<String> xData, List<List<Double>> yDataList, List<Color> colorList, String yUnit, int type,
                                int width, int height, double SpaceFill) {
    File file = new File(baseUrl);
    if (!file.exists()){
      file.mkdirs();
    }
    try {
      String path = baseUrl + titleList.get(0)+ System.currentTimeMillis()+".png";
      CategoryChart chart = new CategoryChartBuilder().width(width).height(height).theme(Styler.ChartTheme.Matlab).build();
      chart = chartTitle(chart, title);
      chart.getStyler().setPlotGridLinesVisible(true);//图表中垂直网格线的可见性
      chart.getStyler().setPlotGridLinesColor(new Color(184, 200,240));//设置图表中垂直网格线颜色
      chart.getStyler().setPlotBorderColor(new Color(236, 238,240));//设置图表中绘图区域的边框的颜色
      chart.getStyler().setLegendPosition(Styler.LegendPosition.OutsideS);//设置图例(Legend)的位置
      chart.getStyler().setLegendLayout(Styler.LegendLayout.Horizontal);//设置图例(Legend)的排列
      chart.getStyler().setXAxisLabelRotation(0);//设置X轴的字旋转角度
      if(xData != null && xData.size() > 6){
        chart.getStyler().setXAxisLabelRotation(40);//设置X轴的字旋转角度
      }
      chart.setYAxisGroupTitle(0, yUnit);//设置Y轴组的标题
      chart.getStyler().setYAxisGroupPosition(0, Styler.YAxisPosition.Left);//设置Y轴组的显示位置
      // 设置字体大小
      chart.getStyler().setLegendFont(new Font("宋体", Font.BOLD, 25));//设置图例的字体样式
      chart.getStyler().setAxisTitleFont(new Font("宋体", Font.BOLD, 25));//设置坐标轴标题的字体样式
      chart.getStyler().setLabelsFont(new Font("宋体", Font.BOLD, 25));//设置图表中标签的字体样式
      chart.getStyler().setLegendBorderColor(Color.white);
      chart.getStyler().setAvailableSpaceFill(SpaceFill);//设置图表可用空间的填充方式
      chart.getStyler().setYAxisDecimalPattern("#");//设置Y轴的小数点格式//避免科学计数法
      chart.getStyler().setChartPadding(10);//设置图表的内边距
      chart.getStyler().setPlotBorderVisible(true);//设置图表中绘图区域的边框是否可见
//      chart.getStyler().setYAxisMin(0.0);
      chart.getStyler().setAxisTickMarksColor(Color.white);//设置图表中刻度标记的颜色
      //柱状图
      if(type == 0 && titleList != null && titleList.size() > 0){
        chart.getStyler().setOverlapped(false);//设置图表中数据系列的重叠方式
        for(int i=0; i < titleList.size(); i++){
          CategorySeries series1 = chart.addSeries(titleList.get(i), xData, yDataList.get(i));
          series1.setChartCategorySeriesRenderStyle(CategorySeries.CategorySeriesRenderStyle.Bar);
          series1.setFillColor(colorList.get(i));
          series1.setYAxisGroup(0);
        }
      }

      /** 折线图 */
      if(type == 1 && titleList != null && titleList.size() > 0){
        chart.getStyler().setOverlapped(true);//设置图表中数据系列的重叠方式
        for(int i=0; i < titleList.size(); i++){
          CategorySeries series2 = chart.addSeries(titleList.get(i), xData, yDataList.get(i));
          series2.setChartCategorySeriesRenderStyle(CategorySeries.CategorySeriesRenderStyle.Line);
          series2.setYAxisGroup(0);
          series2.setLineColor(colorList.get(i));
          series2.setMarkerColor(colorList.get(i));
        }
      }
      BitmapEncoder.saveBitmap(chart, path, BitmapEncoder.BitmapFormat.PNG);
      return path;
    }catch (Exception e){
      e.printStackTrace();
    }
    return null;
  }

  /**
   *
   * @param title
   * @param xDataList       X轴数据列表/线名字列表
   * @param yDataList   Y轴数据列表
   * @param colorList   颜色列表
   * @param yUnit       Y轴单位名字
   * @param type        图形类型,0:柱状图,1:折线图
   * @param width       图片宽度
   * @param height      图片高度
   * @param SpaceFill
   * @return
   */
  public String getChart2(String title, List<String> xDataList, List<Double> yDataList, List<Color> colorList, String yUnit, int type,
                         int width, int height, double SpaceFill) {
    File file = new File(baseUrl);
    if (!file.exists()){
      file.mkdirs();
    }
    try {
      String path = baseUrl + xDataList.get(0)+ System.currentTimeMillis()+".png";
      CategoryChart chart = new CategoryChartBuilder().width(width).height(height).theme(Styler.ChartTheme.Matlab).build();
      chart = chartTitle(chart, title);/** 标题配置 */
      chart = chartLegend(chart);/** 图例配置 */
      chart = chartGridOrBorder(chart);/** 网格或边框配置 */
      chart = chartXY(chart, xDataList.size() > 6 ? true : false, yUnit);/** 坐标轴相关配置 */
      chart.getStyler().setAvailableSpaceFill(SpaceFill);//设置图表可用空间的填充方式
      chart.getStyler().setOverlapped(true);//设置图表中数据系列的重叠方式
      //柱状图
      if(type == 0 && xDataList != null && xDataList.size() > 0){
        for(int i=0; i < xDataList.size(); i++){
          List<Double> yData = new ArrayList<>();
          for(int y=0; y < yDataList.size(); y++){
            if(y==i){
              yData.add(yDataList.get(i));
            }else{
              yData.add(0.0);
            }
          }
          CategorySeries series1 = chart.addSeries(xDataList.get(i), xDataList, yData);
          series1.setChartCategorySeriesRenderStyle(CategorySeries.CategorySeriesRenderStyle.Bar);
          series1.setFillColor(colorList.get(i));
          series1.setYAxisGroup(0);
        }
      }
      BitmapEncoder.saveBitmap(chart, path, BitmapEncoder.BitmapFormat.PNG);
      return path;
    }catch (Exception e){
      e.printStackTrace();
    }
    return null;
  }

  /** 标题配置 */
  private CategoryChart chartTitle(CategoryChart chart, String title){
    if(StringUtils.isNotBlank(title)){
      chart.setTitle(title);
      chart.getStyler().setChartTitleFont(new Font("宋体", Font.BOLD, 25));//设置标题字体
      chart.getStyler().setChartTitleBoxBackgroundColor(new Color(234,242,250));
      chart.getStyler().setChartTitleBoxVisible(true);
      chart.getStyler().setChartTitlePadding(15);
    }
    return chart;
  }

  /** 图例配置 */
  private CategoryChart chartLegend(CategoryChart chart){
    chart.getStyler().setLegendPosition(Styler.LegendPosition.OutsideS);//设置图例(Legend)的位置
    chart.getStyler().setLegendLayout(Styler.LegendLayout.Horizontal);//设置图例(Legend)的排列
    chart.getStyler().setLegendFont(new Font("宋体", Font.PLAIN, 20));//设置图例的字体样式
    chart.getStyler().setLegendBorderColor(Color.white);
    return chart;
  }

  /** 网格或边框配置 */
  private CategoryChart chartGridOrBorder(CategoryChart chart){
    chart.getStyler().setPlotGridLinesVisible(true);//图表中垂直网格线的可见性
    chart.getStyler().setPlotGridLinesColor(new Color(184, 200,240));//设置图表中垂直网格线颜色
    chart.getStyler().setPlotBorderColor(new Color(236, 238,240));//设置图表中绘图区域的边框的颜色
    chart.getStyler().setPlotBorderVisible(true);//设置图表中绘图区域的边框是否可见
    chart.getStyler().setChartPadding(10);//设置图表的内边距
    return chart;
  }

  /** X轴相关配置 */
  private CategoryChart chartXY(CategoryChart chart, Boolean isRotation, String yUnit){
    chart.getStyler().setAxisTickMarksColor(Color.white);//设置图表中刻度标记的颜色
    chart.getStyler().setAxisTitleFont(new Font("宋体", Font.PLAIN, 25));//设置坐标轴标题的字体样式
    chart.getStyler().setAxisTickLabelsFont(new Font("宋体", Font.PLAIN, 20));//设置坐标轴的字体样式
    /** X轴相关配置 */
    chart.getStyler().setXAxisLabelRotation(0);//设置X轴的字旋转角度
    if(isRotation){
      chart.getStyler().setXAxisLabelRotation(40);//设置X轴的字旋转角度
    }
    /** Y轴相关配置 */
    chart.setYAxisGroupTitle(0, yUnit);//设置Y轴组的标题
    chart.getStyler().setYAxisGroupPosition(0, Styler.YAxisPosition.Left);//设置Y轴组的显示位置
    chart.getStyler().setYAxisDecimalPattern("#");//设置Y轴的小数点格式//避免科学计数法
//    chart.getStyler().setYAxisMin(0.6);
    return chart;
  }
}

饼图工具类

package com.ruoyi.business.bankInterface.controller.risk.utils.chart;

import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.business.bankInterface.ConstantConfig;
import com.ruoyi.business.bankInterface.controller.risk.utils.ColorEnum;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.PieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlot;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.ui.RectangleEdge;

import java.awt.*;
import java.io.File;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.List;

public class Pie2Chart {

    private static String baseUrl = ConstantConfig.IMAGE_TEMP_FOLDER;

    public static void main(String[] args) throws Exception{
        List<String> keyList = Arrays.asList("无任何司法异常", "涉及刑事诉讼且为被告人", "涉重大经济纠纷", "涉及经济纠纷", "涉及劳动纠纷",
                "近一年立案数量大于3", "当前存在执行公开信息", "新增查封信息", "失信被执行");
        List<Double> rDataList = JSONArray.parseArray("[40, 15, 1, 8, 17, 5, 10, 2, 2]", Double.class);
        Boolean isTitleVisible = true;//标题是否显示
        String title = "司法风险分布";
        Boolean isLegendVisible = false;//图例是否显示
        RectangleEdge rectangleEdge = RectangleEdge.BOTTOM;//图例位置
        String path = pieChart(keyList, rDataList, isTitleVisible, title, isLegendVisible, rectangleEdge, 1);
        System.out.println(path);
    }

    public static String pieChart(List<String> keyList, List<Double> rDataList, Boolean isTitleVisible, String title,
                                  Boolean isLegendVisible, RectangleEdge rectangleEdge, int flag){
        String path = baseUrl + System.currentTimeMillis()+".png";
        try {
            // 创建数据集
            DefaultPieDataset dataset = new DefaultPieDataset();
            for(int i=0; i<keyList.size(); i++){
                dataset.setValue(keyList.get(i), rDataList.get(i));
            }
            // 创建饼图
            JFreeChart chart = ChartFactory.createPieChart(
                    title,  // 图表标题
                    dataset,           // 数据集
                    true,             // 是否显示图例
                    false,
                    false
            );
            chart = chartLegend(chart, isLegendVisible, rectangleEdge);
            chart = chartTitle(chart, isTitleVisible);
            chart = chartPiePlot(keyList, chart, flag);
            ChartUtilities.saveChartAsPNG(new File(path), chart, 700, 350);
        }catch (Exception e){
         e.printStackTrace();
        }
        return path;
    }

    private static JFreeChart chartLegend(JFreeChart chart, Boolean isLegendVisible, RectangleEdge rectangleEdge){
        if(isLegendVisible){
            /** 图例显示 */
            Font font = new Font("宋体", Font.PLAIN, 12);
            chart.getLegend().setItemFont(font);// 设置图例类别字体
            chart.getLegend().setBorder(0, 0, 0, 0);//设置图例边框
            chart.getLegend().setMargin(10, 30, 10, 0);//设置图例边框内边框
            chart.getLegend().setPosition(rectangleEdge);//配置图例位置

        }else{
            /** 图例不显示 */
            chart.getLegend().setVisible(false);
        }
        return chart;
    }

    /** 标题配置 */
    private static JFreeChart chartTitle(JFreeChart chart, Boolean isTitleVisible){
        if(isTitleVisible){
            /** 标题显示 */
            //如果不使用Font,中文将显示不出来
            Font titleFont = new Font("宋体", Font.BOLD, 25);
            chart.getTitle().setFont(titleFont); // 设置标题字体
            chart.getTitle().setMargin(10, 30, 10, 0);
            chart.getTitle().setBackgroundPaint(Color.WHITE);
        }
        return chart;
    }

    /** PiePlot配置 */
    private static JFreeChart chartPiePlot(List<String> keyList, JFreeChart chart, int flag){
        PiePlot piePlot = (PiePlot)chart.getPlot();
        for(int i=0; i<keyList.size(); i++){
            piePlot.setSectionPaint(keyList.get(i), ColorEnum.zhColorList.get(i));
        }
        piePlot.setBackgroundPaint(Color.WHITE);// 例如,设置为白色背景
        piePlot.setShadowPaint(Color.WHITE);//饼图的阴影颜色
        piePlot.setOutlinePaint(Color.WHITE);//数据区的边界线条颜色
        if(flag == 0){
            /** 展示key+百分比 */
            PieSectionLabelGenerator labelGenerator = new StandardPieSectionLabelGenerator(
                    "{0}\n{2}", // 标签格式,其中 {0} 是扇区键,{1} 是扇区值,{2} 是百分比
                    new DecimalFormat("0"), // 用于格式化数值的 DecimalFormat 对象
                    new DecimalFormat("0%") // 用于格式化百分比的 DecimalFormat 对象
            );
            piePlot.setLabelGenerator(labelGenerator);
        }else if(flag == 1){
            /** 展示百分比 */
            PieSectionLabelGenerator labelGenerator = new StandardPieSectionLabelGenerator(
                    "{2}", // 标签格式,其中 {0} 是扇区键,{1} 是扇区值,{2} 是百分比
                    new DecimalFormat("0"), // 用于格式化数值的 DecimalFormat 对象
                    new DecimalFormat("0%") // 用于格式化百分比的 DecimalFormat 对象
            );
            piePlot.setLabelGenerator(labelGenerator);
        }else if(flag == 2){
            /** 展示百分比 */
            PieSectionLabelGenerator labelGenerator = new StandardPieSectionLabelGenerator(
                    "{0}\n企业占比{1}%", // 标签格式,其中 {0} 是扇区键,{1} 是扇区值,{2} 是百分比
                    new DecimalFormat("0"), // 用于格式化数值的 DecimalFormat 对象
                    new DecimalFormat("0%")// 用于格式化数值的 DecimalFormat 对象
            );
            piePlot.setLabelGenerator(labelGenerator);
        }else{
            /** 不展示内容 */
            piePlot.setLabelGenerator(null);
        }
        /** 配置标签 */
        Font font = new Font("宋体", Font.PLAIN, 12);
        piePlot.setLabelFont(font);/** 分类标签的字体 */
        piePlot.setIgnoreNullValues(true);//忽略空值
        piePlot.setLegendItemShape(new Rectangle(10, 10));
        piePlot.setLabelOutlinePaint(Color.WHITE);/** 分类标签的边框颜色 */
        piePlot.setLabelShadowPaint(Color.WHITE);/** 分类标签的阴影颜色 */
        piePlot.setLabelBackgroundPaint(Color.WHITE);/** 分类标签的背景颜色 */
        piePlot.setLabelLinkStroke(new BasicStroke(0.5F)); //
        piePlot.setLabelLinkPaint(Color.lightGray);
        piePlot.setLabelGap(0.01);// 调整标签与饼图边缘的间隙,以便更好地对齐
        return chart;
    }
}


package com.ruoyi.business.bankInterface.controller.risk.utils.chart;

import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.business.bankInterface.ConstantConfig;
import com.ruoyi.business.bankInterface.controller.risk.utils.ColorEnum;
import org.apache.commons.lang3.StringUtils;
import org.knowm.xchart.BitmapEncoder;
import org.knowm.xchart.PieChart;
import org.knowm.xchart.PieChartBuilder;
import org.knowm.xchart.PieSeries;
import org.knowm.xchart.internal.chartpart.Chart;
import org.knowm.xchart.style.PieStyler;
import org.knowm.xchart.style.Styler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class PieChar <C extends Chart<?, ?>> {

  private static final Logger logger = LoggerFactory.getLogger(PieChar.class);

  protected static String baseUrl = ConstantConfig.IMAGE_TEMP_FOLDER;

  public static void main(String[] args) throws IOException {
    List<String> titleList = Arrays.asList("无任何司法异常", "涉及刑事诉讼且为被告人", "涉重大经济纠纷", "涉及经济纠纷", "涉及劳动纠纷",
            "近一年立案数量大于3", "当前存在执行公开信息", "新增查封信息", "失信被执行");
    List<Double> rDataList = JSONArray.parseArray("[40, 15, 1, 8, 17, 5, 10, 2, 2]", Double.class);
    String hImg = new PieChar().getChart("司法风险分布",titleList, rDataList, ColorEnum.zhColorList, false, Styler.LegendPosition.OutsideE, Styler.LegendLayout.Vertical);
    logger.info("环形图" + hImg);
  }

  /**
   * 饼状图/环图
   * @param title
   * @param titleList 模块名
   * @param rData 数据量
   * @param colorList 模块的颜色集合
   * @param legendVisible 图例是否可见,true:可见,false:不可见
   * @param legendPosition 设置图例(Legend)的位置  OutsideE:左侧 OutsideS:
   * @param legendLayout 设置图例(Legend)的排列 Vertical:竖着排列 Horizontal:横着排列
   * @return
   */
  public String getChart(String title, List<String> titleList, List<Double> rData, List<Color> colorList,
                         Boolean legendVisible, Styler.LegendPosition legendPosition, Styler.LegendLayout legendLayout){
    File file = new File(baseUrl);
    if (!file.exists()){
      file.mkdirs();
    }
    try {
      String path = baseUrl + titleList.get(0)+ System.currentTimeMillis()+".png";
      PieChart chart = new PieChartBuilder().width(1200).height(450).theme(Styler.ChartTheme.Matlab).build();
      chart = chartTitle(chart, title);
      /** 配置图例:是否可见、位置、排列*/
      chart = legendVisible(chart, legendVisible, legendPosition, legendLayout);
      chart = chartLabels(chart, true);
      chart.getStyler().setSliceBorderWidth(0.3);//设置饼图中每个扇区(Slice)的边框宽度
      chart.getStyler().setSliceBorderWidth(2);//环形图各个块的间距
      chart.getStyler().setDefaultSeriesRenderStyle(PieSeries.PieSeriesRenderStyle.Pie);//设置是环还是扇形
      chart.getStyler().setLabelType(PieStyler.LabelType.Percentage);//展示哪些内容
      chart.getStyler().setPlotContentSize(0.8);//设置饼图绘图内容的大小
      chart.getStyler().setSumVisible(false);
      chart.getStyler().setPlotBorderVisible(false);//设置饼图绘图边框的可见性
      for(int i=0; i < titleList.size(); i++){
        PieSeries series =chart.addSeries(titleList.get(i), rData.get(i));
        series.setFillColor(colorList.get(i));
//        series.setShowInLegend(true);
      }
      BitmapEncoder.saveBitmap(chart, path, BitmapEncoder.BitmapFormat.PNG);
      return path;
    }catch (Exception e){
      e.printStackTrace();
    }
    return null;
  }


  private PieChart legendVisible(PieChart chart, Boolean legendVisible, Styler.LegendPosition legendPosition, Styler.LegendLayout legendLayout){
//      chart.getStyler().setLegendPadding(100);//设置图例(Legend)的内边距
//      chart.getStyler().setLegendSeriesLineLength(200);//设置图例系列线条的长度
    chart.getStyler().setLegendBorderColor(Color.white);//设置图例边框颜色:白色
    chart.getStyler().setLegendVisible(false);//默认图例不可见
    if(legendVisible){
      chart.getStyler().setLegendVisible(true);//图例可见
      chart.getStyler().setLegendPosition(legendPosition);//设置图例(Legend)的位置
      chart.getStyler().setLegendLayout(legendLayout);//设置图例(Legend)的排列
      chart.getStyler().setLegendFont(new Font("宋体", Font.PLAIN, 12));//设置图例的字体样式
    }
    return chart;
  }

  private PieChart chartTitle(PieChart chart, String title){
    if(StringUtils.isNotBlank(title)){
      chart.setTitle(title);
      chart.getStyler().setChartTitleFont(new Font("宋体", Font.BOLD, 25));//设置标题字体
      chart.getStyler().setChartTitleBoxBackgroundColor(new Color(234,242,250));
      chart.getStyler().setChartTitleBoxVisible(true);
      chart.getStyler().setChartTitlePadding(15);
    }
    return chart;
  }

  private PieChart chartLabels(PieChart chart, Boolean labelsVisible){
    chart.getStyler().setLabelsDistance(0.8);
    chart.getStyler().setLabelsVisible(false);//设置饼图中每个扇区(Slice)的标签是否可见
    if(labelsVisible){
      chart.getStyler().setLabelsFont(new Font("宋体", Font.CENTER_BASELINE, 12));
      chart.getStyler().setLabelsFontColor(Color.black);
      chart.getStyler().setLabelsVisible(true);//设置饼图中每个扇区(Slice)的标签是否可见
      chart.getStyler().setLabelsFontColorAutomaticEnabled(true);//设置成false的时候,有个白色的图列字变成了指定的颜色
      chart.getStyler().setLabelsFontColorAutomaticLight(Color.black);
      chart.getStyler().setLabelsFontColorAutomaticDark(Color.orange);
      chart.getStyler().setForceAllLabelsVisible(true);//控制饼图中所有扇区的标签是否必须可见
    }
    return chart;
  }
}