peter
2026-01-11 9c638d29f43035bb224996f3183a58d761cd526e
src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java
@@ -33,6 +33,7 @@
import java.awt.RenderingHints;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.GraphicsEnvironment;
import com.nq.pojo.ContractImage;
import com.nq.dao.ContractImageMapper;
import com.alibaba.fastjson.JSON;
@@ -70,11 +71,8 @@
                dir.mkdirs();
            }
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            String pdfFileName = "contract_" + 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()) {
@@ -109,7 +107,7 @@
     */
    private void generatePdfFromTemplate(File templatePdf, File outputPdf, User user) throws Exception {
        log.info("开始生成PDF,模板路径: {}, 输出路径: {}, 用户: {}",
                templatePdf.getAbsolutePath(), outputPdf.getAbsolutePath(), user.getId());
        templatePdf.getAbsolutePath(), outputPdf.getAbsolutePath(), user.getId());
        PdfReader reader = null;
        PdfStamper stamper = null;
@@ -235,6 +233,110 @@
    }
    /**
     * 从资源文件加载字体
     */
    private java.awt.Font loadFontFromResource(String fontPath, int size) {
        try {
            InputStream fontStream = this.getClass().getClassLoader().getResourceAsStream(fontPath);
            if (fontStream != null) {
                java.awt.Font font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, fontStream);
                font = font.deriveFont(java.awt.Font.PLAIN, size);
                fontStream.close();
                log.info("成功从资源文件加载字体: {}", fontPath);
                return font;
            }
        } catch (Exception e) {
            log.warn("从资源文件加载字体失败: {}, 错误: {}", fontPath, e.getMessage());
        }
        return null;
    }
    /**
     * 从系统字体目录加载字体文件
     */
    private java.awt.Font loadFontFromFile(String fontPath, int size) {
        try {
            File fontFile = new File(fontPath);
            if (fontFile.exists()) {
                java.awt.Font font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, fontFile);
                font = font.deriveFont(java.awt.Font.PLAIN, size);
                log.info("成功从文件加载字体: {}", fontPath);
                return font;
            }
        } catch (Exception e) {
            log.warn("从文件加载字体失败: {}, 错误: {}", fontPath, e.getMessage());
        }
        return null;
    }
    /**
     * 获取可用的中文字体
     */
    private java.awt.Font getChineseFont(int size) {
        // 1. 优先从资源文件加载字体(如果项目中有字体文件)
        java.awt.Font font = loadFontFromResource("fonts/simsun.ttf", size);
        if (font != null) return font;
        font = loadFontFromResource("fonts/simhei.ttf", size);
        if (font != null) return font;
        // 2. 从系统字体目录加载(Linux常见字体路径)
        String[] systemFontPaths = {
            "/usr/share/fonts/chinese/simsun.ttf",           // CentOS/RHEL
            "/usr/share/fonts/truetype/wqy/wqy-microhei.ttf", // 文泉驿
            "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", // Noto字体
            "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", // DejaVu
            "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", // Liberation
            "/usr/share/fonts/chinese/SimSun.ttf",
            "/usr/share/fonts/chinese/SimHei.ttf",
            "/usr/share/fonts/truetype/arphic/uming.ttc",    // 文鼎字体
            "/usr/share/fonts/truetype/arphic/ukai.ttc"
        };
        for (String fontPath : systemFontPaths) {
            font = loadFontFromFile(fontPath, size);
            if (font != null) return font;
        }
        // 3. 按优先级尝试加载系统已安装的字体
        String[] fontNames = {
            "SimSun",           // 宋体 (Windows)
            "SimHei",           // 黑体 (Windows)
            "Microsoft YaHei",  // 微软雅黑 (Windows)
            "STSong",           // 华文宋体
            "STHeiti",          // 华文黑体
            "WenQuanYi Micro Hei", // 文泉驿微米黑 (Linux)
            "WenQuanYi Zen Hei",   // 文泉驿正黑 (Linux)
            "Noto Sans CJK SC",    // Noto字体 (Linux)
            "Droid Sans Fallback", // Android字体 (Linux)
            "AR PL UMing CN",       // 文鼎字体 (Linux)
            "AR PL UKai CN"         // 文鼎字体 (Linux)
        };
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] availableFonts = ge.getAvailableFontFamilyNames();
        // 查找可用的中文字体
        for (String fontName : fontNames) {
            for (String availableFont : availableFonts) {
                if (availableFont.equals(fontName)) {
                    try {
                        font = new java.awt.Font(fontName, java.awt.Font.PLAIN, size);
                        log.info("成功加载系统字体: {}", fontName);
                        return font;
                    } catch (Exception e) {
                        log.warn("加载字体{}失败: {}", fontName, e.getMessage());
                    }
                }
            }
        }
        // 如果找不到中文字体,尝试使用逻辑字体
        log.warn("未找到可用的中文字体,使用SERIF逻辑字体,可能出现乱码");
        return new java.awt.Font(java.awt.Font.SERIF, java.awt.Font.PLAIN, size);
    }
    /**
     * 创建透明背景的文本图片
     */
    private File createTextImage(String text, Font font) throws Exception {
@@ -265,14 +367,10 @@
        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));
        }
        // 使用可用的中文字体
        java.awt.Font awtFont = getChineseFont(12);
        g2d.setFont(awtFont);
        // 使用深黑色,RGB(0, 0, 0) 完全不透明
        g2d.setColor(new Color(0, 0, 0, 255));
@@ -296,13 +394,8 @@
            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));
            }
            // 使用相同的中文字体
            g2dResized.setFont(awtFont);
            // 使用深黑色,RGB(0, 0, 0) 完全不透明
            g2dResized.setColor(new Color(0, 0, 0, 255));
            
@@ -354,37 +447,28 @@
    }
    @Override
    public void viewAgreementPdf(HttpServletRequest request, HttpServletResponse response) {
    public ServerResponse viewAgreementPdf(HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户未登录");
                return;
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            if (!pdfFile.exists()) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND, "协议文件不存在,请先生成协议");
                return;
            // 从数据库查询已签名的图片
            List<ContractImage> contractImages = contractImageMapper.selectByUserIdAndType(user.getId(), "contract");
            List<String> imageUrls = new ArrayList<>();
            if (contractImages != null && !contractImages.isEmpty()) {
                for (ContractImage image : contractImages) {
                    imageUrls.add(image.getAddress());
                }
                log.info("从数据库查询到用户{}的{}张已签名合同图片", user.getId(), imageUrls.size());
            }
            List<String> imageUrls = convertPdfToImages(pdfFile, "contract", user.getId());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(ServerResponse.createBySuccess(imageUrls)));
            response.getWriter().flush();
            return ServerResponse.createBySuccess(imageUrls);
        } 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);
            }
            return ServerResponse.createByErrorMsg("查看PDF失败: " + e.getMessage());
        }
    }
@@ -414,34 +498,21 @@
            log.info("用户{}的签名图片已保存: {}", user.getId(), signatureFileLocal.getAbsolutePath());
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            String pdfFileName = "contract_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            //删之前的图片
            deleteFilesWithPattern(pdfDir,"contract_" + user.getId());
            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;
                for(String imageUrl : imageUrls) {
                    ContractImage contractImage = new ContractImage();
                    contractImage.setUserId(user.getId());
                    contractImage.setContractType("contract");
                    contractImage.setAddress(imagePath);
                    contractImage.setAddress(imageUrl);
                    contractImage.setAddTime(new Date());
                    contractImageMapper.insert(contractImage);
                }
                iUserService.updateSignedContract(user.getId(), "Y");
            }
            return ServerResponse.createBySuccessMsg("签名保存成功");
@@ -498,7 +569,7 @@
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            if (StringUtils.isBlank(user.getRealName()) || StringUtils.isBlank(user.getIdCard()) || StringUtils.isBlank(user.getRegAddress())) {
            if (user.getIsActive()!=2 || StringUtils.isBlank(user.getRealName()) || StringUtils.isBlank(user.getIdCard()) || StringUtils.isBlank(user.getRegAddress())) {
                return ServerResponse.createByErrorMsg("用户信息不完整,请先完成实名认证");
            }
@@ -508,10 +579,8 @@
                dir.mkdirs();
            }
            String pdfFileName = "contract_" + user.getId() + ".pdf";
            String pdfFileName = "agreement_" + 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();
@@ -547,16 +616,16 @@
                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<ContractImage> contractImages = contractImageMapper.selectByUserIdAndType(user.getId(), "agreement");
            List<String> imageUrls = new ArrayList<>();
            if (contractImages != null && !contractImages.isEmpty()) {
                for (ContractImage image : contractImages) {
                    imageUrls.add(image.getAddress());
                }
                log.info("从数据库查询到用户{}的{}张已签名保密协议图片", user.getId(), imageUrls.size());
            }
            List<String> imageUrls = convertPdfToImages(pdfFile, "agreement", user.getId());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(ServerResponse.createBySuccess(imageUrls)));
@@ -591,7 +660,7 @@
                dir.mkdirs();
            }
            String signatureFileName = "contract_signature_" + user.getId() + ".png";
            String signatureFileName = "agreement_signature_" + user.getId() + ".png";
            File signatureFileLocal = new File(dir, signatureFileName);
            signatureFile.transferTo(signatureFileLocal);
@@ -599,34 +668,22 @@
            log.info("用户{}的合同签名图片已保存: {}", user.getId(), signatureFileLocal.getAbsolutePath());
            String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir");
            String pdfFileName = "contract_" + user.getId() + ".pdf";
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            //删之前的图片
            deleteFilesWithPattern(pdfDir,"agreement_" + user.getId());
            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;
                for (String imageUrl : imageUrls) {
                    ContractImage contractImage = new ContractImage();
                    contractImage.setUserId(user.getId());
                    contractImage.setContractType("agreement");
                    contractImage.setAddress(imagePath);
                    contractImage.setAddress(imageUrl);
                    contractImage.setAddTime(new Date());
                    contractImageMapper.insert(contractImage);
                }
                iUserService.updateSignedAgreement(user.getId(), "Y");
            }
@@ -662,7 +719,7 @@
    private void replacePlaceholdersInContractPdf(PdfContentByte canvas, Rectangle pageSize, String currentDate, Font font, int pageNum) throws Exception {
        float margin = 50;
        float startY = pageSize.getHeight() - 364;
        float startY = pageSize.getHeight() - 374;
        if(pageNum == 2){
            // 生成透明背景的日期图片并插入
            File dateImage = createTextImage(currentDate, font);
@@ -730,85 +787,6 @@
            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转换为图片
     */
@@ -836,15 +814,15 @@
                // 对图片进行优化处理,确保文字清晰
                BufferedImage optimizedImage = optimizeImage(image);
                String imageFileName = contractType + "_" + timestamp + ".png";
                String imageFileName = contractType + "_" +userId +"_" + timestamp + ".png";
                if (page > 0) {
                    imageFileName = contractType + "_" + timestamp + "_" + (page + 1) + ".png";
                    imageFileName = contractType+ "_" + userId + "_" + timestamp + "_" + (page + 1) + ".png";
                }
                File imageFile = new File(pdfDir, imageFileName);
                ImageIO.write(optimizedImage, "png", imageFile);
                String imageUrl = imagePrefix + "/imgs/" + imageFileName;
                String imageUrl = imagePrefix + "/" + imageFileName;
                imageUrls.add(imageUrl);
                log.info("PDF第{}页已转换为图片: {}", page + 1, imageUrl);
@@ -922,4 +900,23 @@
            return ServerResponse.createByErrorMsg("查询失败:" + e.getMessage());
        }
    }
    public void deleteFilesWithPattern(String dirPath, String userId) {
        File directory = new File(dirPath);
        if (!directory.exists() || !directory.isDirectory()) {
            System.out.println("目录不存在或路径错误: " + dirPath);
            return;
        }
        File[] files = directory.listFiles((dir, name) -> name.contains(userId + "_"));
        if (files != null) {
            for (File file : files) {
                if (file.delete()) {
                    System.out.println("已删除文件: " + file.getName());
                } else {
                    System.out.println("删除失败: " + file.getName());
                }
            }
        }
    }
}