peter
2026-01-09 1a65e52a171e95296615b178088b599927122a58
修改
2 files modified
4 files added
2 files deleted
462 ■■■■■ changed files
pom.xml 27 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/controller/protol/UserController.java 21 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/IUserAgreementService.java 24 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java 390 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/zczq.png patch | view | raw | blame | history
src/main/resources/templates/中原证券商业核心信息保密协议.docx patch | view | raw | blame | history
src/main/resources/templates/中原证券股票分成协议(电子正式版).doc patch | view | raw | blame | history
src/main/resources/templates/中原证券股票分成协议(电子正式版).pdf patch | view | raw | blame | history
pom.xml
@@ -195,6 +195,33 @@
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- Apache POI for Word processing -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>4.1.2</version>
        </dependency>
        <!-- iText for PDF generation -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13.2</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
    </dependencies>
src/main/java/com/nq/controller/protol/UserController.java
@@ -14,6 +14,7 @@
import java.math.BigDecimal;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.nq.utils.SymmetricCryptoUtil;
@@ -60,6 +61,9 @@
    @Autowired
    StockMapper stockMapper;
    @Autowired
    IUserAgreementService iUserAgreementService;
    //添加到自选股
    @RequestMapping({"addOption.do"})
@@ -466,4 +470,21 @@
              return serverResponse;
          }
    /**
     * 生成用户协议PDF
     */
    @RequestMapping({"generateAgreementPdf.do"})
    @ResponseBody
    public ServerResponse generateAgreementPdf(HttpServletRequest request) {
        return this.iUserAgreementService.generateAgreementPdf(request);
    }
    /**
     * 查看用户协议PDF
     */
    @RequestMapping({"viewAgreementPdf.do"})
    public void viewAgreementPdf(HttpServletRequest request, HttpServletResponse response) {
        this.iUserAgreementService.viewAgreementPdf(request, response);
    }
}
src/main/java/com/nq/service/IUserAgreementService.java
New file
@@ -0,0 +1,24 @@
package com.nq.service;
import com.nq.common.ServerResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
public interface IUserAgreementService {
    /**
     * 生成用户协议PDF
     * @param request
     * @return PDF文件路径
     */
    ServerResponse generateAgreementPdf(HttpServletRequest request);
    /**
     * 查看用户协议PDF
     * @param request
     * @param response
     * @return
     */
    void viewAgreementPdf(HttpServletRequest request, HttpServletResponse response);
}
src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java
New file
@@ -0,0 +1,390 @@
package com.nq.service.impl;
import com.nq.common.ServerResponse;
import com.nq.pojo.User;
import com.nq.service.IUserAgreementService;
import com.nq.utils.PropertiesUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
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;
@Service("iUserAgreementService")
public class UserAgreementServiceImpl implements IUserAgreementService {
    private static final Logger log = LoggerFactory.getLogger(UserAgreementServiceImpl.class);
    @Autowired
    private com.nq.service.IUserService iUserService;
    @Override
    public ServerResponse generateAgreementPdf(HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                return ServerResponse.createByErrorMsg("用户未登录");
            }
            if (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");
            File dir = new File(pdfDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            File pdfFile = new File(dir, pdfFileName);
            if (pdfFile.exists()) {
                return ServerResponse.createBySuccess(pdfFileName);
            }
            // 获取模板文件路径
            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()) {
                return ServerResponse.createByErrorMsg("PDF模板文件不存在");
            }
            // 从PDF模板生成新的PDF,替换占位符
            generatePdfFromTemplate(pdfTemplate, pdfFile, user);
            return ServerResponse.createBySuccess(pdfFileName);
        } 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));
        // 设置中文字体
        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);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        String currentDate = sdf.format(new Date());
        // 准备替换的数据
        String realName = user.getRealName() != null ? user.getRealName() : "";
        String idCard = user.getIdCard() != null ? user.getIdCard() : "";
        String address = user.getRegAddress() != null ? user.getRegAddress() : "";
        // 获取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上查找并替换占位符
            replacePlaceholdersInPdf(canvas, pageSize, pageText, realName, idCard, address, currentDate, font,pageNum);
        }
        stamper.close();
        reader.close();
    }
    /**
     * 在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模板的具体布局调整坐标
        float margin = 50;
        float lineHeight = 14;
        float startY = pageSize.getHeight() - 194;
        if(pageNum == 1){
            replacePlaceholder(canvas, "", realName, margin + 75, startY, 0, font);
            startY -= lineHeight;
            replacePlaceholder(canvas, "", address, margin + 75, startY-2, 0, font);
            startY -= lineHeight;
            replacePlaceholder(canvas, "", idCard, margin + 95, startY-2, 0, font);
        }
        if(pageNum == 3){
            replacePlaceholder(canvas, "", currentDate, margin + 60, startY+32, 0, font);
            replacePlaceholder(canvas, "", currentDate, margin + 60, startY, 0, font);
        }
    }
    /**
     * 替换单个占位符
     */
    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();
        // 添加新文本
        ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
                new Phrase(value, font), x, y, 0);
    }
    @Override
    public void viewAgreementPdf(HttpServletRequest request, HttpServletResponse response) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户未登录");
                return;
            }
            String pdfDir = PropertiesUtil.getProperty("agreement.pdf.dir", System.getProperty("user.dir") + "/agreement_pdf");
            String pdfFileName = "agreement_" + user.getId() + ".pdf";
            File pdfFile = new File(pdfDir, pdfFileName);
            if (!pdfFile.exists()) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND, "协议文件不存在,请先生成协议");
                return;
            }
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "inline; filename=" + pdfFileName);
            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();
        } catch (Exception e) {
            log.error("查看用户协议PDF失败", e);
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "查看PDF失败");
            } catch (IOException ex) {
                log.error("发送错误响应失败", ex);
            }
        }
    }
}
src/main/resources/templates/zczq.png
src/main/resources/templates/中原证券商业核心信息保密协议.docx
Binary files differ
src/main/resources/templates/中原证券股票分成协议(电子正式版).doc
Binary files differ
src/main/resources/templates/中原证券股票分成协议(电子正式版).pdf
Binary files differ