peter
2026-01-11 5f20fc02cb6873ceabde9f34cf62967bbd92d82f
src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java
@@ -15,12 +15,27 @@
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.web.multipart.MultipartFile;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;
import com.itextpdf.text.pdf.parser.SimpleTextExtractionStrategy;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.rendering.ImageType;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Color;
import java.awt.FontMetrics;
import com.nq.pojo.ContractImage;
import com.nq.dao.ContractImageMapper;
import com.alibaba.fastjson.JSON;
@Service("iUserAgreementService")
public class UserAgreementServiceImpl implements IUserAgreementService {
@@ -29,20 +44,27 @@
    @Autowired
    private com.nq.service.IUserService iUserService;
    @Autowired
    private ContractImageMapper contractImageMapper;
    @Override
// 在generateAgreementPdf方法中修改返回逻辑
    public ServerResponse generateAgreementPdf(HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            user = (User) iUserService.findByUserId(user.getId()).getData();
            if (user == null) {
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            if (StringUtils.isBlank(user.getRealName()) || StringUtils.isBlank(user.getIdCard()) || StringUtils.isBlank(user.getRegAddress())) {
                return ServerResponse.createByErrorMsg("用户信息不完整,请先完成实名认证");
            if (user.getIsActive()!=2 || StringUtils.isBlank(user.getRealName()) || StringUtils.isBlank(user.getIdCard()) || StringUtils.isBlank(user.getRegAddress())) {
                return ServerResponse.createByErrorMsg("请先完成实名认证");
            }
            // 检查是否已生成PDF
            String pdfDir = PropertiesUtil.getProperty("agreement.pdf.dir", System.getProperty("user.dir") + "/agreement_pdf");
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            File dir = new File(pdfDir);
            if (!dir.exists()) {
                dir.mkdirs();
@@ -50,299 +72,285 @@
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            File pdfFile = new File(dir, pdfFileName);
            // 构建完整的PDF访问URL
//            String pdfUrl = PropertiesUtil.getProperty("pdf.server.http.prefix") + "/agreement_" + user.getId() + ".pdf";
            // 如果PDF已存在,删除后重新生成,确保数据是最新的
            if (pdfFile.exists()) {
                return ServerResponse.createBySuccess(pdfFileName);
                log.info("PDF文件已存在,删除后重新生成: {}", pdfFile.getAbsolutePath());
                boolean deleted = pdfFile.delete();
                if (!deleted) {
                    log.warn("删除旧PDF文件失败: {}", pdfFile.getAbsolutePath());
                }
            }
            // 获取模板文件路径
            String templatePath = this.getClass().getResource("/templates").getPath();
            if (templatePath.startsWith("/") && System.getProperty("os.name").toLowerCase().contains("windows")) {
                templatePath = templatePath.substring(1);
            }
            templatePath = templatePath.replaceAll("%20", " ");
            if (templatePath.contains("file:")) {
                templatePath = templatePath.substring(templatePath.indexOf("file:") + 5);
            }
            if (templatePath.contains("!")) {
                templatePath = templatePath.substring(0, templatePath.indexOf("!"));
            }
            // 使用PDF模板文件
            File pdfTemplate = new File(templatePath, "中原证券股票分成协议(电子正式版).pdf");
            if (!pdfTemplate.exists()) {
            // 从资源中读取PDF模板文件
            File pdfTemplate = getTemplateFile("中原证券股票分成协议(电子正式版).pdf");
            if (pdfTemplate == null || !pdfTemplate.exists()) {
                return ServerResponse.createByErrorMsg("PDF模板文件不存在");
            }
            // 从PDF模板生成新的PDF,替换占位符
            generatePdfFromTemplate(pdfTemplate, pdfFile, user);
            return ServerResponse.createBySuccess(pdfFileName);
            // 将PDF转换为图片并返回图片路径数组
            List<String> imageUrls = convertPdfToImages(pdfFile, "contract", user.getId());
            return ServerResponse.createBySuccess(imageUrls);
        } catch (Exception e) {
            log.error("生成用户协议PDF失败", e);
            return ServerResponse.createByErrorMsg("生成PDF失败:" + e.getMessage());
        }
    }
    public static void main(String[] args) {
        generateAgreementPdf11();
    }
    public static void generateAgreementPdf11() {
        try {
            // 检查是否已生成PDF
            String pdfDir = PropertiesUtil.getProperty("agreement.pdf.dir", System.getProperty("user.dir") + "/agreement_pdf");
            File dir = new File(pdfDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            String pdfFileName = "agreement_111.pdf";
            File pdfFile = new File(dir, pdfFileName);
            // 获取模板文件路径
            String templatePath = "D:/work/A-stock/src/main/resources/templates";
            if (templatePath.startsWith("/") && System.getProperty("os.name").toLowerCase().contains("windows")) {
                templatePath = templatePath.substring(1);
            }
            templatePath = templatePath.replaceAll("%20", " ");
            if (templatePath.contains("file:")) {
                templatePath = templatePath.substring(templatePath.indexOf("file:") + 5);
            }
            if (templatePath.contains("!")) {
                templatePath = templatePath.substring(0, templatePath.indexOf("!"));
            }
            // 使用PDF模板文件
            File pdfTemplate = new File(templatePath, "中原证券股票分成协议(电子正式版).pdf");
            if (!pdfTemplate.exists()) {
                System.out.println("PDF模板文件不存在");
            }
            // 从PDF模板生成新的PDF,替换占位符
            generatePdfFromTemplate1(pdfTemplate, pdfFile);
        } catch (Exception e) {
            log.error("生成用户协议PDF失败", e);
        }
    }
    public static void generatePdfFromTemplate1(File templatePdf, File outputPdf) throws Exception {
        PdfReader reader = new PdfReader(new FileInputStream(templatePdf));
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPdf));
        // 设置中文字体
        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Font font = new Font(baseFont, 12, Font.NORMAL);
//        Font boldFont = new Font(baseFont, 12, Font.BOLD);
//        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
//        Font font = new Font(baseFont, 5, Font.BOLD);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        String currentDate = sdf.format(new Date());
        // 准备替换的数据
        String realName = "陈大雷";
        String idCard = "433123198802456635";
        String address = "广东省清远市三八街道明星之城18栋2单元1506";
        // 获取PDF页数
        int pageCount = reader.getNumberOfPages();
        // 在每一页上查找并替换占位符
        for (int pageNum = 1; pageNum <= pageCount; pageNum++) {
            PdfContentByte canvas = stamper.getOverContent(pageNum);
            Rectangle pageSize = reader.getPageSize(pageNum);
            // 提取PDF文本查找占位符位置
            String pageText = PdfTextExtractor.getTextFromPage(reader, pageNum, new SimpleTextExtractionStrategy());
            // 在PDF上查找并替换占位符
            replacePlaceholdersInPdf1(canvas, pageSize, pageText, realName, idCard, address, currentDate, font,pageNum);
            // 添加签名
            if(pageNum == 3){
                // 添加签名
                addHeaderImageToPdf(canvas, templatePdf.getParent(), pageSize);
            }
        }
        stamper.close();
        reader.close();
    }
    /**
     * 在PDF上替换占位符(使用文本覆盖方式)
     */
    public static void replacePlaceholdersInPdf1(PdfContentByte canvas, Rectangle pageSize, String pageText,
                                          String realName, String idCard, String address,
                                          String currentDate, Font font,int pageNum) throws Exception {
        // 查找占位符在文本中的位置,然后估算在PDF中的坐标
        // 由于PDF坐标系统复杂,这里使用固定位置替换
        // 实际使用时需要根据PDF模板的具体布局调整坐标
        float margin = 50;
        float lineHeight = 14;
        float startY = pageSize.getHeight() - 194;
        if(pageNum == 1){
            replacePlaceholder1(canvas, "", realName, margin + 75, startY, 0, font);
            startY -= lineHeight;
            replacePlaceholder1(canvas, "", address, margin + 75, startY-2, 0, font);
            startY -= lineHeight;
            replacePlaceholder1(canvas, "", idCard, margin + 95, startY-2, 0, font);
        }
        if(pageNum == 3){
            replacePlaceholder1(canvas, "", currentDate, margin + 60, startY+32, 0, font);
            replacePlaceholder1(canvas, "", currentDate, margin + 60, startY, 0, font);
        }
//        if (pageText.contains("${jcurrentDate}")) {
//            replacePlaceholder1(canvas, "${jcurrentDate}", currentDate, margin + 100, startY, 150, font);
//            startY -= lineHeight;
//        }
//
//        if (pageText.contains("${ucurrentDate}")) {
//            replacePlaceholder1(canvas, "${ucurrentDate}", currentDate, margin + 100, startY, 300, font);
//        }
    }
    /**
     * 在PDF左上角添加图片
     */
    public static void addHeaderImageToPdf(PdfContentByte canvas, String templatePath, Rectangle pageSize) throws Exception {
        // 获取图片路径
        String imagePath = templatePath + File.separator + "zczq.png";
        File imageFile = new File(imagePath);
        // 如果使用相对路径找不到,尝试从resources获取
        if (!imageFile.exists()) {
            String resourcePath = "D:/work/A-stock/src/main/resources/templates";
            if (resourcePath.startsWith("/") && System.getProperty("os.name").toLowerCase().contains("windows")) {
                resourcePath = resourcePath.substring(1);
            }
            resourcePath = resourcePath.replaceAll("%20", " ");
            if (resourcePath.contains("file:")) {
                resourcePath = resourcePath.substring(resourcePath.indexOf("file:") + 5);
            }
            if (resourcePath.contains("!")) {
                resourcePath = resourcePath.substring(0, resourcePath.indexOf("!"));
            }
            imageFile = new File(resourcePath);
        }
        if (imageFile.exists()) {
            Image image = Image.getInstance(imageFile.getAbsolutePath());
            // 设置图片大小
            image.scaleToFit(100, 100);
            // 设置图片位置(左上角)
            image.setAbsolutePosition(370, pageSize.getHeight() - 194);
            canvas.addImage(image);
        } else {
            log.warn("未找到图片文件: {}", imagePath);
        }
    }
    /**
     * 替换单个占位符
     */
    public static void replacePlaceholder1(PdfContentByte canvas, String placeholder, String value,
                                    float x, float y, float width, Font font) {
        // 添加白色背景覆盖占位符区域
        canvas.saveState();
        canvas.setColorFill(BaseColor.WHITE);
        canvas.rectangle(x - 5, y - 15, width, 20);
        canvas.fill();
        canvas.restoreState();
        // 添加新文本
        ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
                new Phrase(value, font), x, y, 0);
    }
    /**
     * 从PDF模板生成新的PDF,替换占位符
     */
    private void generatePdfFromTemplate(File templatePdf, File outputPdf, User user) throws Exception {
        PdfReader reader = new PdfReader(new FileInputStream(templatePdf));
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPdf));
        log.info("开始生成PDF,模板路径: {}, 输出路径: {}, 用户: {}",
                templatePdf.getAbsolutePath(), outputPdf.getAbsolutePath(), user.getId());
        // 设置中文字体
        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Font font = new Font(baseFont, 12, Font.NORMAL);
        Font boldFont = new Font(baseFont, 12, Font.BOLD);
        PdfReader reader = null;
        PdfStamper stamper = null;
        FileOutputStream fos = null;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        String currentDate = sdf.format(new Date());
        try {
            reader = new PdfReader(new FileInputStream(templatePdf));
            fos = new FileOutputStream(outputPdf);
            stamper = new PdfStamper(reader, fos);
        // 准备替换的数据
        String realName = user.getRealName() != null ? user.getRealName() : "";
        String idCard = user.getIdCard() != null ? user.getIdCard() : "";
        String address = user.getRegAddress() != null ? user.getRegAddress() : "";
            // 设置中文字体,使用EMBEDDED确保字体嵌入PDF,避免转图片时乱码
            // 使用itext-asian库提供的中文字体,必须使用EMBEDDED模式
            BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
            Font font = new Font(baseFont, 12, Font.NORMAL);
        // 获取PDF页数
        int pageCount = reader.getNumberOfPages();
            log.info("中文字体已加载: {}", baseFont.getPostscriptFontName());
        // 在每一页上查找并替换占位符
        for (int pageNum = 1; pageNum <= pageCount; pageNum++) {
            PdfContentByte canvas = stamper.getOverContent(pageNum);
            Rectangle pageSize = reader.getPageSize(pageNum);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
            String currentDate = sdf.format(new Date());
            // 提取PDF文本查找占位符位置
            String pageText = PdfTextExtractor.getTextFromPage(reader, pageNum, new SimpleTextExtractionStrategy());
            // 准备替换的数据
            String realName = user.getRealName() != null ? user.getRealName() : "";
            String idCard = user.getIdCard() != null ? user.getIdCard() : "";
            String address = user.getRegAddress() != null ? user.getRegAddress() : "";
            // 在PDF上查找并替换占位符
            replacePlaceholdersInPdf(canvas, pageSize, pageText, realName, idCard, address, currentDate, font,pageNum);
            log.info("PDF生成数据 - 姓名: {}, 身份证: {}, 地址: {}", realName, idCard, address);
            // 获取PDF页数
            int pageCount = reader.getNumberOfPages();
            log.info("PDF模板页数: {}", pageCount);
            // 在每一页上查找并替换占位符
            for (int pageNum = 1; pageNum <= pageCount; pageNum++) {
                PdfContentByte canvas = stamper.getOverContent(pageNum);
                Rectangle pageSize = reader.getPageSize(pageNum);
                // 提取PDF文本查找占位符位置
                String pageText = PdfTextExtractor.getTextFromPage(reader, pageNum, new SimpleTextExtractionStrategy());
                // 在PDF上查找并替换占位符
                replacePlaceholdersInPdf(canvas, pageSize, pageText, realName, idCard, address, currentDate, font, pageNum);
                log.info("已处理PDF第{}页", pageNum);
            }
            stamper.close();
            stamper = null;
            reader.close();
            reader = null;
            fos.close();
            fos = null;
            // 验证生成的文件
            if (outputPdf.exists()) {
                long fileSize = outputPdf.length();
                log.info("PDF生成完成,文件路径: {}, 文件大小: {} 字节", outputPdf.getAbsolutePath(), fileSize);
                if (fileSize == 0) {
                    throw new Exception("生成的PDF文件大小为0");
                }
            } else {
                throw new Exception("PDF文件生成失败,文件不存在");
            }
        } catch (Exception e) {
            log.error("生成PDF失败", e);
            throw e;
        } finally {
            if (stamper != null) {
                try {
                    stamper.close();
                } catch (Exception e) {
                    log.error("关闭stamper失败", e);
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {
                    log.error("关闭reader失败", e);
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (Exception e) {
                    log.error("关闭输出流失败", e);
                }
            }
        }
        stamper.close();
        reader.close();
    }
    /**
     * 在PDF上替换占位符(使用文本覆盖方式)
     * 在PDF上替换占位符(使用透明背景图片方式)
     */
    private void replacePlaceholdersInPdf(PdfContentByte canvas, Rectangle pageSize, String pageText,
                                         String realName, String idCard, String address,
                                         String currentDate, Font font,int pageNum) throws Exception {
        // 查找占位符在文本中的位置,然后估算在PDF中的坐标
        // 由于PDF坐标系统复杂,这里使用固定位置替换
        // 实际使用时需要根据PDF模板的具体布局调整坐标
                                          String realName, String idCard, String address,
                                          String currentDate, Font font,int pageNum) throws Exception {
        float margin = 50;
        float lineHeight = 14;
        float startY = pageSize.getHeight() - 194;
        float startY = pageSize.getHeight() - 204;
        if(pageNum == 1){
            replacePlaceholder(canvas, "", realName, margin + 75, startY, 0, font);
            // 生成透明背景的姓名图片并插入
            File nameImage = createTextImage(realName, font);
            insertImageToPdf(canvas, nameImage, margin + 75, startY);
            startY -= lineHeight;
            replacePlaceholder(canvas, "", address, margin + 75, startY-2, 0, font);
            // 生成透明背景的地址图片并插入
            File addressImage = createTextImage(address, font);
            insertImageToPdf(canvas, addressImage, margin + 75, startY-2);
            startY -= lineHeight;
            replacePlaceholder(canvas, "", idCard, margin + 95, startY-2, 0, font);
            // 生成透明背景的身份证号图片并插入
            File idCardImage = createTextImage(idCard, font);
            insertImageToPdf(canvas, idCardImage, margin + 95, startY-2);
        }
        if(pageNum == 3){
            replacePlaceholder(canvas, "", currentDate, margin + 60, startY+32, 0, font);
            replacePlaceholder(canvas, "", currentDate, margin + 60, startY, 0, font);
            // 生成透明背景的日期图片并插入
            File dateImage1 = createTextImage(currentDate, font);
            insertImageToPdf(canvas, dateImage1, margin + 60, startY+32);
            File dateImage2 = createTextImage(currentDate, font);
            insertImageToPdf(canvas, dateImage2, margin + 60, startY);
        }
    }
    /**
     * 替换单个占位符
     * 创建透明背景的文本图片
     */
    private void replacePlaceholder(PdfContentByte canvas, String placeholder, String value,
                                   float x, float y, float width, Font font) {
        // 添加白色背景覆盖占位符区域
        canvas.saveState();
        canvas.setColorFill(BaseColor.WHITE);
        canvas.rectangle(x - 5, y - 15, width, 20);
        canvas.fill();
        canvas.restoreState();
    private File createTextImage(String text, Font font) throws Exception {
        if (StringUtils.isBlank(text)) {
            text = "";
        }
        // 添加新文本
        ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
                new Phrase(value, font), x, y, 0);
        // 创建临时文件
        String tempDir = PropertiesUtil.getProperty("loca.pdf.dir");
        File dir = new File(tempDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String tempFileName = "text_" + System.currentTimeMillis() + "_" + Thread.currentThread().getId() + ".png";
        File imageFile = new File(dir, tempFileName);
        // 创建透明背景的BufferedImage (TYPE_INT_ARGB 默认透明)
        BufferedImage image = new BufferedImage(500, 50, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = image.createGraphics();
        // 启用透明背景支持
        g2d.setComposite(java.awt.AlphaComposite.SrcOver);
        // 设置高质量渲染
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        // 设置字体和颜色
        try {
            java.awt.Font awtFont = new java.awt.Font("SimSun", java.awt.Font.PLAIN, 12);
            g2d.setFont(awtFont);
        } catch (Exception e) {
            log.warn("无法加载SimSun字体,使用默认字体", e);
            g2d.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 12));
        }
        // 使用深黑色,RGB(0, 0, 0) 完全不透明
        g2d.setColor(new Color(0, 0, 0, 255));
        // 绘制文本
        FontMetrics fm = g2d.getFontMetrics();
        int textWidth = fm.stringWidth(text);
        int textHeight = fm.getHeight();
        // 调整图片大小以适应文本
        if (textWidth > 0 && textHeight > 0) {
            // TYPE_INT_ARGB 类型默认透明背景,不需要清除
            BufferedImage resizedImage = new BufferedImage(textWidth + 20, textHeight + 10, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2dResized = resizedImage.createGraphics();
            // 启用透明背景支持
            g2dResized.setComposite(java.awt.AlphaComposite.SrcOver);
            // 设置高质量渲染
            g2dResized.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2dResized.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
            g2dResized.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2dResized.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            // 设置字体
            try {
                java.awt.Font awtFont = new java.awt.Font("SimSun", java.awt.Font.PLAIN, 12);
                g2dResized.setFont(awtFont);
            } catch (Exception e) {
                g2dResized.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 12));
            }
            // 使用深黑色,RGB(0, 0, 0) 完全不透明
            g2dResized.setColor(new Color(0, 0, 0, 255));
            // 绘制文本
            g2dResized.drawString(text, 10, textHeight);
            g2dResized.dispose();
            // 保存图片
            ImageIO.write(resizedImage, "PNG", imageFile);
            image = resizedImage;
        } else {
            // 如果文本为空,创建一个小的透明图片
            BufferedImage emptyImage = new BufferedImage(50, 20, BufferedImage.TYPE_INT_ARGB);
            ImageIO.write(emptyImage, "PNG", imageFile);
            image = emptyImage;
        }
        g2d.dispose();
        log.info("已创建透明背景文本图片: {}, 文本: {}", imageFile.getAbsolutePath(), text);
        return imageFile;
    }
    /**
     * 将图片插入到PDF指定位置
     */
    private void insertImageToPdf(PdfContentByte canvas, File imageFile, float x, float y) throws Exception {
        if (imageFile == null || !imageFile.exists()) {
            log.warn("图片文件不存在: {}", imageFile);
            return;
        }
        try {
            Image image = Image.getInstance(imageFile.getAbsolutePath());
            image.setAbsolutePosition(x, y);
            canvas.addImage(image);
            log.info("图片已插入到PDF,位置: ({}, {})", x, y);
            // 删除临时图片文件
            try {
                imageFile.delete();
            } catch (Exception e) {
                log.warn("删除临时图片文件失败: {}", imageFile.getAbsolutePath(), e);
            }
        } catch (Exception e) {
            log.error("插入图片到PDF失败: {}", imageFile.getAbsolutePath(), e);
            throw e;
        }
    }
    @Override
@@ -354,7 +362,7 @@
                return;
            }
            String pdfDir = PropertiesUtil.getProperty("agreement.pdf.dir", System.getProperty("user.dir") + "/agreement_pdf");
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
@@ -363,28 +371,555 @@
                return;
            }
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "inline; filename=" + pdfFileName);
            List<String> imageUrls = convertPdfToImages(pdfFile, "contract", user.getId());
            FileInputStream fis = new FileInputStream(pdfFile);
            OutputStream os = response.getOutputStream();
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            fis.close();
            os.flush();
            os.close();
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(ServerResponse.createBySuccess(imageUrls)));
            response.getWriter().flush();
        } catch (Exception e) {
            log.error("查看用户协议PDF失败", e);
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "查看PDF失败");
                if (!response.isCommitted()) {
                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "查看PDF失败: " + e.getMessage());
                }
            } catch (IOException ex) {
                log.error("发送错误响应失败", ex);
            }
        }
    }
    @Override
    public ServerResponse saveAgreementSignature(MultipartFile signatureFile, HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            if (signatureFile == null || signatureFile.isEmpty()) {
                return ServerResponse.createByErrorMsg("签名图片不能为空");
            }
            String signatureDir = PropertiesUtil.getProperty("loca.pdf.dir");
            File dir = new File(signatureDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            String signatureFileName = "signature_" + user.getId() + ".png";
            File signatureFileLocal = new File(dir, signatureFileName);
            signatureFile.transferTo(signatureFileLocal);
            log.info("用户{}的签名图片已保存: {}", user.getId(), signatureFileLocal.getAbsolutePath());
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            if (pdfFile.exists()) {
                addSignatureToPdf(pdfFile, signatureFileLocal, user);
                List<String> imageUrls = convertPdfToImages(pdfFile, "contract", user.getId());
                String imagePrefix = PropertiesUtil.getProperty("pdf.server.http.prefix");
                long timestamp = System.currentTimeMillis();
                for (int i = 0; i < imageUrls.size(); i++) {
                    String imageName;
                    if (i == 0) {
                        imageName = "contract_" + timestamp + ".png";
                    } else {
                        imageName = "contract_" + timestamp + "_" + (i + 1) + ".png";
                    }
                    String imagePath = imagePrefix + "/" + imageName;
                    ContractImage contractImage = new ContractImage();
                    contractImage.setUserId(user.getId());
                    contractImage.setContractType("contract");
                    contractImage.setAddress(imagePath);
                    contractImage.setAddTime(new Date());
                    contractImageMapper.insert(contractImage);
                }
                iUserService.updateSignedContract(user.getId(), "Y");
            }
            return ServerResponse.createBySuccessMsg("签名保存成功");
        } catch (Exception e) {
            log.error("保存用户协议签名失败", e);
            return ServerResponse.createByErrorMsg("保存签名失败:" + e.getMessage());
        }
    }
    /**
     * 在PDF第三页插入签名图片
     */
    private void addSignatureToPdf(File pdfFile, File signatureImage, User user) throws Exception {
        PdfReader reader = new PdfReader(new FileInputStream(pdfFile));
        File tempPdf = new File(pdfFile.getParent(), "temp_" + pdfFile.getName());
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tempPdf));
        int pageCount = reader.getNumberOfPages();
        // 在第三页插入签名图片
        if (pageCount >= 3) {
            PdfContentByte canvas = stamper.getOverContent(3);
            Rectangle pageSize = reader.getPageSize(3);
            // 添加签名图片
            if (signatureImage.exists()) {
                Image image = Image.getInstance(signatureImage.getAbsolutePath());
                // 设置图片大小(根据实际需要调整)
                image.scaleToFit(100, 100);
                // 设置图片位置(使用与addHeaderImageToPdf相同的坐标)
                image.setAbsolutePosition(370, pageSize.getHeight() - 194);
                canvas.addImage(image);
                log.info("签名图片已插入到PDF第三页,位置: (370, {})", pageSize.getHeight() - 194);
            }
        }
        stamper.close();
        reader.close();
        // 替换原PDF文件
        if (tempPdf.exists()) {
            pdfFile.delete();
            tempPdf.renameTo(pdfFile);
            log.info("PDF文件已更新,签名图片已插入");
        }
    }
    @Override
    public ServerResponse generateAgreementContractPdf(HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            user = (User) iUserService.findByUserId(user.getId()).getData();
            if (user == null) {
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            if (StringUtils.isBlank(user.getRealName()) || StringUtils.isBlank(user.getIdCard()) || StringUtils.isBlank(user.getRegAddress())) {
                return ServerResponse.createByErrorMsg("用户信息不完整,请先完成实名认证");
            }
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            File dir = new File(pdfDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            String pdfFileName = "contract_" + user.getId() + ".pdf";
            File pdfFile = new File(dir, pdfFileName);
            String pdfUrl = PropertiesUtil.getProperty("pdf.server.http.prefix") + "/contract_" + user.getId() + ".pdf";
            if (pdfFile.exists()) {
                boolean deleted = pdfFile.delete();
                if (!deleted) {
                    log.warn("删除旧PDF文件失败: {}", pdfFile.getAbsolutePath());
                }
            }
            // 从资源中读取PDF模板文件
            File pdfTemplate = getTemplateFile("中原证券商业核心信息保密协议.pdf");
            if (pdfTemplate == null || !pdfTemplate.exists()) {
                return ServerResponse.createByErrorMsg("PDF模板文件不存在");
            }
            generateContractPdfFromTemplate(pdfTemplate, pdfFile, user);
            // 将PDF转换为图片并返回图片路径数组
            List<String> imageUrls = convertPdfToImages(pdfFile, "agreement", user.getId());
            return ServerResponse.createBySuccess(imageUrls);
        } catch (Exception e) {
            log.error("生成用户合同PDF失败", e);
            return ServerResponse.createByErrorMsg("生成PDF失败:" + e.getMessage());
        }
    }
    @Override
    public void viewAgreementContractPdf(HttpServletRequest request, HttpServletResponse response) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户未登录");
                return;
            }
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            String pdfFileName = "contract_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            if (!pdfFile.exists()) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND, "合同文件不存在,请先生成合同");
                return;
            }
            List<String> imageUrls = convertPdfToImages(pdfFile, "agreement", user.getId());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(ServerResponse.createBySuccess(imageUrls)));
            response.getWriter().flush();
        } catch (Exception e) {
            log.error("查看用户合同PDF失败", e);
            try {
                if (!response.isCommitted()) {
                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "查看PDF失败: " + e.getMessage());
                }
            } catch (IOException ex) {
                log.error("发送错误响应失败", ex);
            }
        }
    }
    @Override
    public ServerResponse saveAgreementContractSignature(MultipartFile signatureFile, HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            if (signatureFile == null || signatureFile.isEmpty()) {
                return ServerResponse.createByErrorMsg("签名图片不能为空");
            }
            String signatureDir = PropertiesUtil.getProperty("loca.pdf.dir");
            File dir = new File(signatureDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            String signatureFileName = "contract_signature_" + user.getId() + ".png";
            File signatureFileLocal = new File(dir, signatureFileName);
            signatureFile.transferTo(signatureFileLocal);
            log.info("用户{}的合同签名图片已保存: {}", user.getId(), signatureFileLocal.getAbsolutePath());
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            String pdfFileName = "contract_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            if (pdfFile.exists()) {
                addSignatureToContractPdf(pdfFile, signatureFileLocal, user);
                List<String> imageUrls = convertPdfToImages(pdfFile, "agreement", user.getId());
                String imagePrefix = PropertiesUtil.getProperty("pdf.server.http.prefix");
                long timestamp = System.currentTimeMillis();
                for (int i = 0; i < imageUrls.size(); i++) {
                    String imageName;
                    if (i == 0) {
                        imageName = "agreement_" + timestamp + ".png";
                    } else {
                        imageName = "agreement_" + timestamp + "_" + (i + 1) + ".png";
                    }
                    String imagePath = imagePrefix + "/" + imageName;
                    ContractImage contractImage = new ContractImage();
                    contractImage.setUserId(user.getId());
                    contractImage.setContractType("agreement");
                    contractImage.setAddress(imagePath);
                    contractImage.setAddTime(new Date());
                    contractImageMapper.insert(contractImage);
                }
                iUserService.updateSignedAgreement(user.getId(), "Y");
            }
            return ServerResponse.createBySuccessMsg("签名保存成功");
        } catch (Exception e) {
            log.error("保存用户合同签名失败", e);
            return ServerResponse.createByErrorMsg("保存签名失败:" + e.getMessage());
        }
    }
    private void generateContractPdfFromTemplate(File templatePdf, File outputPdf, User user) throws Exception {
        PdfReader reader = new PdfReader(new FileInputStream(templatePdf));
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPdf));
        // 设置中文字体,使用EMBEDDED确保字体嵌入PDF,避免转图片时乱码
        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
        Font font = new Font(baseFont, 12, Font.NORMAL);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        String currentDate = sdf.format(new Date());
        int pageCount = reader.getNumberOfPages();
        for (int pageNum = 1; pageNum <= pageCount; pageNum++) {
            PdfContentByte canvas = stamper.getOverContent(pageNum);
            Rectangle pageSize = reader.getPageSize(pageNum);
            replacePlaceholdersInContractPdf(canvas, pageSize, currentDate, font, pageNum);
        }
        stamper.close();
        reader.close();
    }
    private void replacePlaceholdersInContractPdf(PdfContentByte canvas, Rectangle pageSize, String currentDate, Font font, int pageNum) throws Exception {
        float margin = 50;
        float startY = pageSize.getHeight() - 364;
        if(pageNum == 2){
            // 生成透明背景的日期图片并插入
            File dateImage = createTextImage(currentDate, font);
            insertImageToPdf(canvas, dateImage, margin + 320, startY);
        }
    }
    private void addSignatureToContractPdf(File pdfFile, File signatureImage, User user) throws Exception {
        PdfReader reader = new PdfReader(new FileInputStream(pdfFile));
        File tempPdf = new File(pdfFile.getParent(), "temp_" + pdfFile.getName());
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tempPdf));
        int pageCount = reader.getNumberOfPages();
        if (pageCount >= 2) {
            PdfContentByte canvas = stamper.getOverContent(2);
            Rectangle pageSize = reader.getPageSize(2);
            if (signatureImage.exists()) {
                Image image = Image.getInstance(signatureImage.getAbsolutePath());
                image.scaleToFit(100, 100);
                image.setAbsolutePosition(435, pageSize.getHeight() - 288);
                canvas.addImage(image);
                log.info("签名图片已插入到合同PDF第二页");
            }
        }
        stamper.close();
        reader.close();
        if (tempPdf.exists()) {
            pdfFile.delete();
            tempPdf.renameTo(pdfFile);
            log.info("合同PDF文件已更新,签名图片已插入");
        }
    }
    /**
     * 从固定目录获取模板文件
     */
    private File getTemplateFile(String templateFileName) {
        try {
            String templateDir = PropertiesUtil.getProperty("pdf.template.dir");
            if (StringUtils.isBlank(templateDir)) {
                log.error("PDF模板目录配置不存在: pdf.template.dir");
                return null;
            }
            File dir = new File(templateDir);
            if (!dir.exists()) {
                log.error("PDF模板目录不存在: {}", templateDir);
                return null;
            }
            File templateFile = new File(dir, templateFileName);
            if (!templateFile.exists()) {
                log.error("PDF模板文件不存在: {}", templateFile.getAbsolutePath());
                return null;
            }
            log.info("使用模板文件: {}", templateFile.getAbsolutePath());
            return templateFile;
        } catch (Exception e) {
            log.error("获取模板文件失败: {}", templateFileName, e);
            return null;
        }
    }
    public static void main(String[] args) {
        try {
            String pdfDir = "D:/work/A-stock/agreement_pdf";
            String pdfFileName = "agreement_" + 19998 + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            convertPdfToImages1(pdfFile,"agreement");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 将PDF转换为图片
     */
    public static void convertPdfToImages1(File pdfFile, String contractType) throws Exception {
        List<String> imageUrls = new ArrayList<>();
        String imagePrefix = "http://localhost:8099";
        String pdfDir = "D:/work/A-stock/agreement_pdf";
        long timestamp = System.currentTimeMillis();
        PDDocument document = null;
        try {
            document = PDDocument.load(pdfFile);
            // 创建PDFRenderer,使用高质量渲染
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            int pageCount = document.getNumberOfPages();
            log.info("PDF总页数: {}", pageCount);
            for (int page = 0; page < pageCount; page++) {
                // 使用RGB模式渲染,DPI设置为300确保文字清晰
                // ImageType.RGB可以更好地处理中文字体
                BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
                // 对图片进行优化处理,确保文字清晰
                BufferedImage optimizedImage = optimizeImage1(image);
                String imageFileName = contractType + "_" + timestamp + ".png";
                if (page > 0) {
                    imageFileName = contractType + "_" + timestamp + "_" + (page + 1) + ".png";
                }
                File imageFile = new File(pdfDir, imageFileName);
                ImageIO.write(optimizedImage, "png", imageFile);
                String imageUrl = imagePrefix + "/imgs/" + imageFileName;
                imageUrls.add(imageUrl);
                log.info("PDF第{}页已转换为图片: {}", page + 1, imageUrl);
            }
        } finally {
            if (document != null) {
                document.close();
            }
        }
//        return imageUrls;
    }
    public static BufferedImage optimizeImage1(BufferedImage originalImage) {
        int width = originalImage.getWidth();
        int height = originalImage.getHeight();
        BufferedImage optimizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = optimizedImage.createGraphics();
        // 设置高质量渲染
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.drawImage(originalImage, 0, 0, null);
        g2d.dispose();
        return optimizedImage;
    }
    /**
     * 将PDF转换为图片
     */
    private List<String> convertPdfToImages(File pdfFile, String contractType, Integer userId) throws Exception {
        List<String> imageUrls = new ArrayList<>();
        String imagePrefix = PropertiesUtil.getProperty("pdf.server.http.prefix");
        String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
        long timestamp = System.currentTimeMillis();
        PDDocument document = null;
        try {
            document = PDDocument.load(pdfFile);
            // 创建PDFRenderer,使用高质量渲染
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            int pageCount = document.getNumberOfPages();
            log.info("PDF总页数: {}", pageCount);
            for (int page = 0; page < pageCount; page++) {
                // 使用RGB模式渲染,DPI设置为300确保文字清晰
                // ImageType.RGB可以更好地处理中文字体
                BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
                // 对图片进行优化处理,确保文字清晰
                BufferedImage optimizedImage = optimizeImage(image);
                String imageFileName = contractType + "_" + timestamp + ".png";
                if (page > 0) {
                    imageFileName = contractType + "_" + timestamp + "_" + (page + 1) + ".png";
                }
                File imageFile = new File(pdfDir, imageFileName);
                ImageIO.write(optimizedImage, "png", imageFile);
                String imageUrl = imagePrefix + "/imgs/" + imageFileName;
                imageUrls.add(imageUrl);
                log.info("PDF第{}页已转换为图片: {}", page + 1, imageUrl);
            }
        } finally {
            if (document != null) {
                document.close();
            }
        }
        return imageUrls;
    }
    /**
     * 优化图片,确保文字清晰
     */
    private BufferedImage optimizeImage(BufferedImage originalImage) {
        int width = originalImage.getWidth();
        int height = originalImage.getHeight();
        BufferedImage optimizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = optimizedImage.createGraphics();
        // 设置高质量渲染
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.drawImage(originalImage, 0, 0, null);
        g2d.dispose();
        return optimizedImage;
    }
    @Override
    public ServerResponse getContractImages(HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            List<ContractImage> images = contractImageMapper.selectByUserIdAndType(user.getId(), "contract");
            List<String> imageUrls = new ArrayList<>();
            for (ContractImage image : images) {
                imageUrls.add(image.getAddress());
            }
            return ServerResponse.createBySuccess(imageUrls);
        } catch (Exception e) {
            log.error("查询合同图片列表失败", e);
            return ServerResponse.createByErrorMsg("查询失败:" + e.getMessage());
        }
    }
    @Override
    public ServerResponse getAgreementImages(HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            List<ContractImage> images = contractImageMapper.selectByUserIdAndType(user.getId(), "agreement");
            List<String> imageUrls = new ArrayList<>();
            for (ContractImage image : images) {
                imageUrls.add(image.getAddress());
            }
            return ServerResponse.createBySuccess(imageUrls);
        } catch (Exception e) {
            log.error("查询保密协议图片列表失败", e);
            return ServerResponse.createByErrorMsg("查询失败:" + e.getMessage());
        }
    }
}