From 1a65e52a171e95296615b178088b599927122a58 Mon Sep 17 00:00:00 2001
From: peter <14100000001@qq.com>
Date: Fri, 09 Jan 2026 18:22:00 +0800
Subject: [PATCH] 修改
---
/dev/null | 0
src/main/java/com/nq/service/IUserAgreementService.java | 24 +++
src/main/resources/templates/zczq.png | 0
src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java | 390 ++++++++++++++++++++++++++++++++++++++++++++++++
src/main/java/com/nq/controller/protol/UserController.java | 21 ++
pom.xml | 27 +++
src/main/resources/templates/中原证券股票分成协议(电子正式版).pdf | 0
7 files changed, 462 insertions(+), 0 deletions(-)
diff --git a/pom.xml b/pom.xml
index 3abcf03..23d8072 100644
--- a/pom.xml
+++ b/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>
diff --git a/src/main/java/com/nq/controller/protol/UserController.java b/src/main/java/com/nq/controller/protol/UserController.java
index 7752741..7f6e69e 100644
--- a/src/main/java/com/nq/controller/protol/UserController.java
+++ b/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);
+ }
+
}
diff --git a/src/main/java/com/nq/service/IUserAgreementService.java b/src/main/java/com/nq/service/IUserAgreementService.java
new file mode 100644
index 0000000..5fb9592
--- /dev/null
+++ b/src/main/java/com/nq/service/IUserAgreementService.java
@@ -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);
+}
diff --git a/src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java b/src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java
new file mode 100644
index 0000000..da1149c
--- /dev/null
+++ b/src/main/java/com/nq/service/impl/UserAgreementServiceImpl.java
@@ -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);
+ }
+ }
+ }
+}
diff --git a/src/main/resources/templates/zczq.png b/src/main/resources/templates/zczq.png
new file mode 100644
index 0000000..59a384a
--- /dev/null
+++ b/src/main/resources/templates/zczq.png
Binary files differ
diff --git "a/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\345\225\206\344\270\232\346\240\270\345\277\203\344\277\241\346\201\257\344\277\235\345\257\206\345\215\217\350\256\256.docx" "b/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\345\225\206\344\270\232\346\240\270\345\277\203\344\277\241\346\201\257\344\277\235\345\257\206\345\215\217\350\256\256.docx"
deleted file mode 100644
index 63f7db5..0000000
--- "a/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\345\225\206\344\270\232\346\240\270\345\277\203\344\277\241\346\201\257\344\277\235\345\257\206\345\215\217\350\256\256.docx"
+++ /dev/null
Binary files differ
diff --git "a/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\350\202\241\347\245\250\345\210\206\346\210\220\345\215\217\350\256\256\357\274\210\347\224\265\345\255\220\346\255\243\345\274\217\347\211\210\357\274\211.doc" "b/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\350\202\241\347\245\250\345\210\206\346\210\220\345\215\217\350\256\256\357\274\210\347\224\265\345\255\220\346\255\243\345\274\217\347\211\210\357\274\211.doc"
deleted file mode 100644
index 8c3ffd5..0000000
--- "a/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\350\202\241\347\245\250\345\210\206\346\210\220\345\215\217\350\256\256\357\274\210\347\224\265\345\255\220\346\255\243\345\274\217\347\211\210\357\274\211.doc"
+++ /dev/null
Binary files differ
diff --git "a/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\350\202\241\347\245\250\345\210\206\346\210\220\345\215\217\350\256\256\357\274\210\347\224\265\345\255\220\346\255\243\345\274\217\347\211\210\357\274\211.pdf" "b/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\350\202\241\347\245\250\345\210\206\346\210\220\345\215\217\350\256\256\357\274\210\347\224\265\345\255\220\346\255\243\345\274\217\347\211\210\357\274\211.pdf"
new file mode 100644
index 0000000..d520fc0
--- /dev/null
+++ "b/src/main/resources/templates/\344\270\255\345\216\237\350\257\201\345\210\270\350\202\241\347\245\250\345\210\206\346\210\220\345\215\217\350\256\256\357\274\210\347\224\265\345\255\220\346\255\243\345\274\217\347\211\210\357\274\211.pdf"
Binary files differ
--
Gitblit v1.9.3