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 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; @Service("iUserAgreementService") public class UserAgreementServiceImpl implements IUserAgreementService { private static final Logger log = LoggerFactory.getLogger(UserAgreementServiceImpl.class); @Autowired private com.nq.service.IUserService iUserService; @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("用户信息不完整,请先完成实名认证"); } // 检查是否已生成PDF String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); File dir = new File(pdfDir); if (!dir.exists()) { dir.mkdirs(); } 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()) { log.info("PDF文件已存在,删除后重新生成: {}", pdfFile.getAbsolutePath()); 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模板文件不存在"); } // 从PDF模板生成新的PDF,替换占位符 generatePdfFromTemplate(pdfTemplate, pdfFile, user); return ServerResponse.createBySuccess(pdfUrl); } catch (Exception e) { log.error("生成用户协议PDF失败", e); return ServerResponse.createByErrorMsg("生成PDF失败:" + e.getMessage()); } } /** * 从PDF模板生成新的PDF,替换占位符 */ private void generatePdfFromTemplate(File templatePdf, File outputPdf, User user) throws Exception { log.info("开始生成PDF,模板路径: {}, 输出路径: {}, 用户: {}", templatePdf.getAbsolutePath(), outputPdf.getAbsolutePath(), user.getId()); PdfReader reader = null; PdfStamper stamper = null; FileOutputStream fos = null; try { reader = new PdfReader(new FileInputStream(templatePdf)); fos = new FileOutputStream(outputPdf); stamper = new PdfStamper(reader, fos); // 设置中文字体 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() : ""; 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); } } } } /** * 在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) { FileInputStream fis = null; OutputStream os = null; try { User user = iUserService.getCurrentUser(request); if (user == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户未登录"); return; } String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); String pdfFileName = "agreement_" + user.getId() + ".pdf"; File pdfFile = new File(pdfDir, pdfFileName); log.info("查看PDF文件,路径: {}, 是否存在: {}, 文件大小: {}", pdfFile.getAbsolutePath(), pdfFile.exists(), pdfFile.exists() ? pdfFile.length() : 0); if (!pdfFile.exists()) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "协议文件不存在,请先生成协议"); return; } long fileLength = pdfFile.length(); if (fileLength == 0) { log.error("PDF文件大小为0: {}", pdfFile.getAbsolutePath()); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "PDF文件为空"); return; } // 验证PDF文件头 FileInputStream checkFis = new FileInputStream(pdfFile); byte[] header = new byte[4]; checkFis.read(header); checkFis.close(); String headerStr = new String(header); if (!headerStr.startsWith("%PDF")) { log.error("文件不是有效的PDF格式,文件头: {}", headerStr); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "PDF文件格式错误"); return; } log.info("PDF文件头验证通过: {}", headerStr); response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "inline; filename=\"" + new String(pdfFileName.getBytes("UTF-8"), "ISO-8859-1") + "\""); response.setContentLengthLong(fileLength); response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); fis = new FileInputStream(pdfFile); os = response.getOutputStream(); byte[] buffer = new byte[8192]; int bytesRead; long totalBytes = 0; while ((bytesRead = fis.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); totalBytes += bytesRead; } log.info("PDF文件传输完成,总字节数: {}", totalBytes); os.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); } } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { log.error("关闭文件流失败", e); } } if (os != null) { try { os.close(); } catch (IOException e) { log.error("关闭输出流失败", e); } } } } @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()); // 重新生成PDF并插入签名图片 String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); String pdfFileName = "agreement_" + user.getId() + ".pdf"; File pdfFile = new File(pdfDir, pdfFileName); if (pdfFile.exists()) { // 在PDF第三页插入签名图片 addSignatureToPdf(pdfFile, signatureFileLocal, user); // 构建PDF访问地址 String pdfUrl = PropertiesUtil.getProperty("pdf.server.http.prefix") + "/agreement_" + user.getId() + ".pdf"; // 更新用户表的签合同标记(保存PDF地址) iUserService.updateSignedContract(user.getId(),pdfUrl); } 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); return ServerResponse.createBySuccess(pdfUrl); } catch (Exception e) { log.error("生成用户合同PDF失败", e); return ServerResponse.createByErrorMsg("生成PDF失败:" + e.getMessage()); } } @Override public void viewAgreementContractPdf(HttpServletRequest request, HttpServletResponse response) { FileInputStream fis = null; OutputStream os = null; 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; } long fileLength = pdfFile.length(); if (fileLength == 0) { log.error("PDF文件大小为0: {}", pdfFile.getAbsolutePath()); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "PDF文件为空"); return; } FileInputStream checkFis = new FileInputStream(pdfFile); byte[] header = new byte[4]; checkFis.read(header); checkFis.close(); String headerStr = new String(header); if (!headerStr.startsWith("%PDF")) { log.error("文件不是有效的PDF格式,文件头: {}", headerStr); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "PDF文件格式错误"); return; } response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "inline; filename=\"" + new String(pdfFileName.getBytes("UTF-8"), "ISO-8859-1") + "\""); response.setContentLengthLong(fileLength); response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); fis = new FileInputStream(pdfFile); os = response.getOutputStream(); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } os.flush(); } catch (Exception e) { log.error("查看用户合同PDF失败", e); try { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "查看PDF失败: " + e.getMessage()); } catch (IOException ex) { log.error("发送错误响应失败", ex); } } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { log.error("关闭文件流失败", e); } } if (os != null) { try { os.close(); } catch (IOException e) { log.error("关闭输出流失败", e); } } } } @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); String pdfUrl = PropertiesUtil.getProperty("pdf.server.http.prefix") + "/contract_" + user.getId() + ".pdf"; iUserService.updateSignedAgreement(user.getId(), pdfUrl); } 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)); BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_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){ replacePlaceholder(canvas, "", currentDate, margin + 320, startY, 0, font); } } 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; } } }