From 40158f61b8bf3f07c3ebc928ccd8e5ccbc2e22ff Mon Sep 17 00:00:00 2001 From: 无关风月 <443237572@qq.com> Date: 星期四, 06 三月 2025 19:22:55 +0800 Subject: [PATCH] Merge branch 'master' of https://gitee.com/xiaochen991015/xizang --- ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java | 54 ++-- ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java | 87 +++++++ ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java | 313 +++++++++++++++++++++++-- ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java | 9 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java | 218 +++++++++++++++-- 5 files changed, 585 insertions(+), 96 deletions(-) diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java index 493393b..58f8f56 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java @@ -298,46 +298,46 @@ THouse tHouse = houseService.getById(contract.getHouseId()); Map<String, Object> templateParam = new HashMap<>(5); - templateParam.put("partyOneName", contract.getPartyOneName()); - templateParam.put("partyTwoName", contract.getPartyTwoName()); - templateParam.put("houseAddress", tHouse.getHouseAddress()); - templateParam.put("houseArea", tHouse.getHouseArea()+"m²"); + templateParam.put("${partyOneName}", contract.getPartyOneName()); + templateParam.put("${partyTwoName}", contract.getPartyTwoName()); + templateParam.put("${houseAddress}", tHouse.getHouseAddress()); + templateParam.put("${houseArea}", tHouse.getHouseArea()+"m²"); long between = ChronoUnit.DAYS.between(contract.getStartTime(), contract.getStartPayTime())+1; - templateParam.put("day", between); - templateParam.put("endTimeFree", DateUtils.localDateTimeToStringYear(contract.getStartPayTime().plusDays(1))); - templateParam.put("startPayTime", DateUtils.localDateTimeToStringYear(contract.getStartPayTime())); - templateParam.put("startTime", DateUtils.localDateTimeToStringYear(contract.getStartTime())); - templateParam.put("endTime", DateUtils.localDateTimeToStringYear(contract.getEndTime())); - templateParam.put("monthRent", "¥¥"+contract.getMonthRent()+"元"); - templateParam.put("monthRentString", "人民币"+NumberToChineseUtils.numberToChinese(contract.getMonthRent().setScale(2, BigDecimal.ROUND_DOWN).doubleValue())); + templateParam.put("${day}", between); + templateParam.put("${endTimeFree}", DateUtils.localDateTimeToStringYear(contract.getStartPayTime().plusDays(1))); + templateParam.put("${startPayTime}", DateUtils.localDateTimeToStringYear(contract.getStartPayTime())); + templateParam.put("${startTime}", DateUtils.localDateTimeToStringYear(contract.getStartTime())); + templateParam.put("${endTime}", DateUtils.localDateTimeToStringYear(contract.getEndTime())); + templateParam.put("${monthRent}", "¥"+contract.getMonthRent()+"元"); + templateParam.put("${monthRentString}", "人民币"+NumberToChineseUtils.numberToChinese(contract.getMonthRent().setScale(2, BigDecimal.ROUND_DOWN).doubleValue())); String totalYear = Objects.nonNull(contract.getTotalYear())?contract.getTotalYear().toString():""; - templateParam.put("totalYear", "¥¥"+totalYear+"元"); + templateParam.put("${totalYear}", "¥"+totalYear+"元"); String totalYearString = StringUtils.isNotEmpty(totalYear)?NumberToChineseUtils.numberToChinese(contract.getTotalYear().setScale(2, BigDecimal.ROUND_DOWN).doubleValue()):""; - templateParam.put("totalYearString", "人民币"+totalYearString); - templateParam.put("payType", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年"); + templateParam.put("${totalYearString}", "人民币"+totalYearString); + templateParam.put("${payType}", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年"); if(firstBill!=null){ - templateParam.put("firstRent", "¥"+(firstBill.getPayableFeesMoney())+"元"); + templateParam.put("${firstRent}", "¥"+(firstBill.getPayableFeesMoney())+"元"); }else{ - templateParam.put("firstRent", ""); + templateParam.put("${firstRent}", ""); } - templateParam.put("firstRentString", "人民币"+NumberToChineseUtils.numberToChinese((contract.getPayType().equals("1")?contract.getMonthRent():contract.getPayType().equals("2")?contract.getMonthRent().multiply(new BigDecimal("3")):contract.getMonthRent().multiply(new BigDecimal("12")).setScale(2,BigDecimal.ROUND_DOWN)).doubleValue())); - templateParam.put("nextPayTime", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年"); - templateParam.put("deposit", "¥"+contract.getDeposit()+"元"); - templateParam.put("depositString", NumberToChineseUtils.numberToChinese(contract.getDeposit().setScale(2, BigDecimal.ROUND_DOWN).doubleValue())); - templateParam.put("partyOnePerson", contract.getPartyOnePerson()); - templateParam.put("partyOnePhone", contract.getPartyOnePhone()); - templateParam.put("partyTwoPerson", contract.getPartyTwoPerson()); - templateParam.put("partyTwoPhone", contract.getPartyTwoPhone()); + templateParam.put("${firstRentString}", "人民币"+NumberToChineseUtils.numberToChinese((contract.getPayType().equals("1")?contract.getMonthRent():contract.getPayType().equals("2")?contract.getMonthRent().multiply(new BigDecimal("3")):contract.getMonthRent().multiply(new BigDecimal("12")).setScale(2,BigDecimal.ROUND_DOWN)).doubleValue())); + templateParam.put("${nextPayTime}", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年"); + templateParam.put("${deposit}", "¥"+contract.getDeposit()+"元"); + templateParam.put("${depositString}", NumberToChineseUtils.numberToChinese(contract.getDeposit().setScale(2, BigDecimal.ROUND_DOWN).doubleValue())); + templateParam.put("${partyOnePerson}", contract.getPartyOnePerson()); + templateParam.put("${partyOnePhone}", contract.getPartyOnePhone()); + templateParam.put("${partyTwoPerson}", contract.getPartyTwoPerson()); + templateParam.put("${partyTwoPhone}", contract.getPartyTwoPhone()); // 验收时间 TCheckAcceptRecord tCheckAcceptRecord = checkAcceptRecordService.lambdaQuery().eq(TCheckAcceptRecord::getContractId, contract.getId()).last("limit 1").one(); if (tCheckAcceptRecord!=null &&tCheckAcceptRecord.getCheckTime()!=null ){ - templateParam.put("checkTime", DateUtils.localDateTimeToStringYear(tCheckAcceptRecord.getCheckTime())); + templateParam.put("${checkTime}", DateUtils.localDateTimeToStringYear(tCheckAcceptRecord.getCheckTime())); }else{ - templateParam.put("checkTime", ""); + templateParam.put("${checkTime}", ""); } - String url = wordUtil.generatePdf("/template", "1_yzj_租赁合同.xml", templateParam, "租赁合同", "E:\\"); + String url = wordUtil.generatePdf("/usr/local/project/file/", "1_yzj_租赁合同.docx", templateParam, "租赁合同", "/usr/local/project/file/"); res.add(url); } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java index 9c274d7..172bfd6 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java @@ -4,6 +4,7 @@ import com.documents4j.api.IConverter; import com.documents4j.job.LocalConverter; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.xwpf.usermodel.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockMultipartFile; import org.springframework.stereotype.Component; @@ -11,7 +12,11 @@ import java.io.*; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; @Slf4j @@ -22,7 +27,6 @@ /** * word 转 pdf * - * @param url */ // public String wordToPdf(String url,String filePath, String fileName) { // try { @@ -62,48 +66,192 @@ // return null; // } - public String wordToPdf(String url, String filePath, String fileName) { + public String wordToPdf(String filePath, String fileName) { try { - DocumentType documentType = DocumentType.DOC; - if (url.contains(".docx")) { - documentType = DocumentType.DOCX; - } else if (url.contains(".doc")) { - documentType = DocumentType.DOC; - } else if (url.contains(".xlsx")) { - documentType = DocumentType.XLSX; - } else if (url.contains(".xls")) { - documentType = DocumentType.XLS; - } + // 1. 首先确保输入文件是UTF-8编码 + String inputFile = filePath + fileName; + String outputDir = filePath + "/pdf"; + String outputFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf"; - // Ensure the URL has a protocol part - if (!url.startsWith("file://") && !url.startsWith("http://") && !url.startsWith("https://")) { - url = "file://" + url; - } + // 2. 创建临时文件用于转换 + String tempDocx = createTempFileWithEncoding(inputFile); - // 使用LibreOffice进行转换 - ProcessBuilder pb = new ProcessBuilder( - "soffice", - "--headless", - "--convert-to", "pdf", - "--outdir", new File(filePath+ "/pdf"+fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf").getParent(), - filePath+fileName - ); + // 3. 使用更详细的LibreOffice转换参数 + List<String> command = new ArrayList<>(); + command.add("/usr/bin/soffice"); + command.add("--headless"); + command.add("--convert-to"); + command.add("pdf:writer_pdf_Export:PDFExport{'EmbedStandardFonts':true}"); + command.add("--outdir"); + command.add(outputDir); + command.add(tempDocx); + + ProcessBuilder pb = new ProcessBuilder(command); + + // 4. 设置更完整的环境变量 + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "zh_CN.UTF-8"); + env.put("LANG", "zh_CN.UTF-8"); + env.put("LANGUAGE", "zh_CN.UTF-8"); + env.put("PYTHONIOENCODING", "utf8"); + env.put("JAVA_TOOL_OPTIONS", "-Dfile.encoding=UTF-8"); + + // 5. 执行转换 Process process = pb.start(); - // 等待转换完成 - int exitCode = process.waitFor(); - if (exitCode == 0) { - System.out.println("PDF转换成功!"); - } else { - System.out.println("PDF转换失败!"); + // 6. 读取输出 + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } } - return ""; + + // 7. 等待进程完成 + int exitCode = process.waitFor(); + + // 8. 清理临时文件 + new File(tempDocx).delete(); + + if (exitCode == 0) { + return outputDir + "/" + outputFileName; + } else { + throw new RuntimeException("转换失败: " + output.toString()); + } + } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("Failed to generate PDF: " + e.getMessage(), e); + throw new RuntimeException("PDF转换失败: " + e.getMessage(), e); + } + } + private static String createTempFileWithEncoding(String inputFile) { + try { + // 1. 读取原始文档 + XWPFDocument doc = new XWPFDocument(new FileInputStream(inputFile)); + + // 2. 修改文档字体和编码 + for (XWPFParagraph paragraph : doc.getParagraphs()) { + for (XWPFRun run : paragraph.getRuns()) { + // 设置中文字体 + run.setFontFamily("WenQuanYi Zen Hei"); + run.setFontFamily("WenQuanYi Zen Hei", XWPFRun.FontCharRange.eastAsia); + + // 确保文本是UTF-8编码 + String text = run.getText(0); + if (text != null) { + text = new String(text.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + run.setText(text, 0); + } + } + } + + // 3. 处理表格中的文本 + for (XWPFTable table : doc.getTables()) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + for (XWPFRun run : paragraph.getRuns()) { + run.setFontFamily("WenQuanYi Zen Hei"); + run.setFontFamily("WenQuanYi Zen Hei", XWPFRun.FontCharRange.eastAsia); + + String text = run.getText(0); + if (text != null) { + text = new String(text.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + run.setText(text, 0); + } + } + } + } + } + } + + // 4. 保存为临时文件 + String tempFile = inputFile + ".temp.docx"; + try (FileOutputStream out = new FileOutputStream(tempFile)) { + doc.write(out); + } + + return tempFile; + + } catch (Exception e) { + throw new RuntimeException("处理文档编码失败: " + e.getMessage(), e); } } + // 在转换之前执行的环境检查 + private static void preConversionCheck() { + try { + // 1. 检查并安装必要的字体 + installRequiredFonts(); + + // 2. 验证LibreOffice安装 + verifyLibreOfficeInstallation(); + + // 3. 设置字体配置 + setupFontConfig(); + + } catch (Exception e) { + throw new RuntimeException("环境检查失败: " + e.getMessage(), e); + } + } + + private static void installRequiredFonts() { + try { + ProcessBuilder pb = new ProcessBuilder( + "sudo", "apt-get", "install", "-y", + "fonts-wqy-zenhei", + "fonts-wqy-microhei", + "fonts-arphic-ukai", + "fonts-arphic-uming" + ); + pb.inheritIO(); + Process p = pb.start(); + p.waitFor(); + } catch (Exception e) { + System.err.println("警告: 安装字体失败 - " + e.getMessage()); + } + } + + private static void verifyLibreOfficeInstallation() { + try { + Process p = Runtime.getRuntime().exec("soffice --version"); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(p.getInputStream()))) { + String version = reader.readLine(); + if (version == null || !version.contains("LibreOffice")) { + throw new RuntimeException("LibreOffice未正确安装"); + } + } + } catch (Exception e) { + throw new RuntimeException("LibreOffice检查失败: " + e.getMessage()); + } + } + + private static void setupFontConfig() { + try { + String fontConfig = + "<?xml version='1.0'?>\n" + + "<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>\n" + + "<fontconfig>\n" + + " <match target=\"pattern\">\n" + + " <test name=\"family\"><string>SimSun</string></test>\n" + + " <edit name=\"family\" mode=\"assign\" binding=\"same\">\n" + + " <string>WenQuanYi Zen Hei</string>\n" + + " </edit>\n" + + " </match>\n" + + "</fontconfig>"; + + String userHome = System.getProperty("user.home"); + File fontConfigFile = new File(userHome + "/.fonts.conf"); + + try (FileWriter writer = new FileWriter(fontConfigFile)) { + writer.write(fontConfig); + } + } catch (Exception e) { + System.err.println("警告: 设置字体配置失败 - " + e.getMessage()); + } + } public static MultipartFile convertToMultipartFile(ByteArrayOutputStream baos, String fileName) throws IOException { // 创建一个临时文件 @@ -251,12 +399,12 @@ } public String test(String fileName){ - String url = "file:///usr/local/project/file/"+fileName; +// String url = "file:///usr/local/project/file/"+fileName; // String filePath = "E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09"; // String fileName = "专业技术工作总结.docx";4 String filePath = "/usr/local/project/file/"; - String s = wordToPdf(url, filePath, fileName); + String s = wordToPdf(filePath, fileName); System.err.println(s); return s; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java new file mode 100644 index 0000000..6b32fe3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java @@ -0,0 +1,87 @@ +package com.ruoyi.web.controller.tool; + +import org.apache.poi.xwpf.usermodel.*; +import java.io.*; +import java.util.*; + +public class WordTemplateProcessor { + + public static void fillTemplate(String templatePath, String outputPath,Map<String, String> dataMap) { + try { + // 读取模板文件 + FileInputStream fis = new FileInputStream(templatePath); + XWPFDocument document = new XWPFDocument(fis); + + // 替换段落中的标记 + for (XWPFParagraph paragraph : document.getParagraphs()) { + replaceParagraph(paragraph, dataMap); + } + + // 替换表格中的标记 + for (XWPFTable table : document.getTables()) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + replaceParagraph(paragraph, dataMap); + } + } + } + } + + // 保存文件 + FileOutputStream fos = new FileOutputStream(outputPath); + document.write(fos); + + // 关闭资源 + fos.close(); + fis.close(); + document.close(); + + System.out.println("模板填充完成!文件保存在: " + outputPath); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void replaceParagraph(XWPFParagraph paragraph, Map<String, String> dataMap) { + String paragraphText = paragraph.getText(); + for (Map.Entry<String, String> entry : dataMap.entrySet()) { + if (paragraphText.contains(entry.getKey())) { + List<XWPFRun> runs = paragraph.getRuns(); + TextSegment found = paragraph.searchText(entry.getKey(), new PositionInParagraph()); + if (found != null) { + // 替换文本 + int beginRun = found.getBeginRun(); + int endRun = found.getEndRun(); + + if (beginRun >= 0 && endRun >= 0) { + // 删除原有runs + for (int runPos = beginRun; runPos <= endRun; runPos++) { + paragraph.removeRun(runPos); + } + // 创建新run + XWPFRun newRun = paragraph.insertNewRun(beginRun); + newRun.setText(entry.getValue()); + // 复制原有格式 + if (runs.size() > 0 && runs.get(0) != null) { + XWPFRun styleRun = runs.get(0); + newRun.setFontFamily(styleRun.getFontFamily()); + newRun.setFontSize(styleRun.getFontSize()); + newRun.setBold(styleRun.isBold()); + newRun.setItalic(styleRun.isItalic()); + } + } + } + } + } + } + + public static void main(String[] args) { + + String templatePath = "/path/to/template.docx"; + String outputPath = "/path/to/output.docx"; + +// fillTemplate(templatePath, outputPath, user); + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java index ce14b96..60d821a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java @@ -6,6 +6,7 @@ import freemarker.template.TemplateException; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.xwpf.usermodel.*; import org.springframework.mock.web.MockMultipartFile; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -15,7 +16,9 @@ import java.io.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.Map; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Slf4j @Component @@ -110,35 +113,38 @@ } - public String generatePdf(String basePackagePath, String templateFileName, Object templateParam, String fileName, String saveDirectory) { + public String generatePdf(String basePackagePath, String templateFileName, Map<String,Object> templateParam, String fileName, String saveDirectory) { try { + + fillTemplate(basePackagePath+templateFileName, saveDirectory+fileName+".docx", templateParam); + // 创建 Freemarker 的 Configuration 对象,设置默认的不兼容改进选项 - Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); - configuration.setDefaultEncoding("utf-8"); - // 设置模板加载器,加载模板文件 - configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath)); - Template t = configuration.getTemplate(templateFileName, "utf-8"); - - // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题 -// String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8"); - String encodedFileName =fileName ; - - // 定义保存文件的路径 - File saveDir = new File(saveDirectory); - if (!saveDir.exists()) { - saveDir.mkdirs(); - } - - // 定义文件名 - String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc"; - - // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB - Writer out = new BufferedWriter(new FileWriter(filePath), 10240); - - // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中 - t.process(templateParam, out); - out.close(); - +// Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); +// configuration.setDefaultEncoding("utf-8"); +// // 设置模板加载器,加载模板文件 +// configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath)); +// Template t = configuration.getTemplate(templateFileName, "utf-8"); +// +// // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题 +//// String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8"); +// String encodedFileName =fileName ; +// +// // 定义保存文件的路径 +// File saveDir = new File(saveDirectory); +// if (!saveDir.exists()) { +// saveDir.mkdirs(); +// } +// +// // 定义文件名 +// String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc"; +// +// // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB +// Writer out = new BufferedWriter(new FileWriter(filePath), 10240); +// +// // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中 +// t.process(templateParam, out); +// out.close(); + String filePath = saveDirectory + File.separator + fileName + ".docx"; File file = new File(filePath); // 检查文件是否存在 @@ -152,16 +158,263 @@ fis.read(fileContent); } - String test = pdfUtils.test(encodedFileName + ".doc"); + String test = pdfUtils.test(fileName + ".docx"); // MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent); // String s = ObsUploadUtil.obsUpload(mockMultipartFile); return test; - } catch (IOException | TemplateException e) { + } catch (IOException e) { log.error("生成pdf异常,异常原因:{}", e.getMessage(), e); throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage()); } } + public static void fillTemplate(String templatePath, String outputPath,Map<String, Object> dataMap) { + try (FileInputStream fis = new FileInputStream(templatePath)) { + // 设置默认编码为UTF-8 + System.setProperty("file.encoding", "UTF-8"); + + XWPFDocument document = new XWPFDocument(fis); + + // 处理段落 + for (XWPFParagraph paragraph : document.getParagraphs()) { + replaceParagraph(paragraph, dataMap); + } + + // 处理表格 + for (XWPFTable table : document.getTables()) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + replaceParagraph(paragraph, dataMap); + } + } + } + } + + // 使用UTF-8编码保存文件 + try (FileOutputStream fos = new FileOutputStream(outputPath)) { + document.write(fos); + } + + System.out.println("模板填充完成!文件保存在: " + outputPath); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void replaceParagraph(XWPFParagraph paragraph, Map<String, Object> dataMap) { + // 获取段落中所有runs + List<XWPFRun> runs = paragraph.getRuns(); + if (runs == null || runs.isEmpty()) return; + + // 首先合并所有runs的文本,以便正确识别占位符 + StringBuilder fullText = new StringBuilder(); + for (XWPFRun run : runs) { + String text = run.getText(0); + if (text != null) { + fullText.append(text); + } + } + + String paragraphText = fullText.toString(); + + // 使用正则表达式查找所有占位符,包括括号内的 + Pattern pattern = Pattern.compile("\\$\\{[^}]+\\}|\\([^)]*\\$\\{[^}]+\\}[^)]*\\)"); + Matcher matcher = pattern.matcher(paragraphText); + + List<ReplacementInfo> replacements = new ArrayList<>(); + + // 收集所有需要替换的信息 + while (matcher.find()) { + String matched = matcher.group(); + int start = matcher.start(); + int end = matcher.end(); + + // 找出涉及到的runs + int startRun = -1; + int endRun = -1; + int currentPos = 0; + + for (int i = 0; i < runs.size(); i++) { + XWPFRun run = runs.get(i); + String runText = run.getText(0); + if (runText == null) continue; + + int runLength = runText.length(); + if (startRun == -1 && currentPos + runLength > start) { + startRun = i; + } + if (currentPos + runLength >= end) { + endRun = i; + break; + } + currentPos += runLength; + } + + if (startRun != -1 && endRun != -1) { + // 处理括号内的占位符 + String replacement = processPlaceholder(matched, dataMap); + replacements.add(new ReplacementInfo(startRun, endRun, matched, replacement)); + } + } + + // 从后向前替换,避免位置变化影响 + Collections.sort(replacements, (a, b) -> b.startRun - a.startRun); + + for (ReplacementInfo info : replacements) { + replaceRunRange(paragraph, info); + } + } + + private static String processPlaceholder(String text, Map<String, Object> dataMap) { + // 处理括号内的占位符 + Pattern placeholderPattern = Pattern.compile("\\$\\{([^}]+)\\}"); + Matcher matcher = placeholderPattern.matcher(text); + + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + String placeholder = matcher.group(0); // 完整的占位符 + String key = matcher.group(1); // 占位符中的键 + String replacement = Objects.nonNull(dataMap.get("${" + key + "}"))?String.valueOf(dataMap.get("${" + key + "}")):""; + + if (replacement != null) { + // 如果在括号内,保留括号 + if (text.startsWith("(") && text.endsWith(")")) { + matcher.appendReplacement(result, replacement); + } else { + matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); + } + } + } + matcher.appendTail(result); + + return result.toString(); + } + + private static class ReplacementInfo { + int startRun; + int endRun; + String originalText; + String replacementText; + + ReplacementInfo(int startRun, int endRun, String originalText, String replacementText) { + this.startRun = startRun; + this.endRun = endRun; + this.originalText = originalText; + this.replacementText = replacementText; + } + } + + private static void replaceRunRange(XWPFParagraph paragraph, ReplacementInfo info) { + List<XWPFRun> runs = paragraph.getRuns(); + + // 保存第一个run的样式 + XWPFRun styleRun = runs.get(info.startRun); + RunStyle style = new RunStyle(styleRun); + + // 删除范围内的所有runs + for (int i = info.endRun; i >= info.startRun; i--) { + paragraph.removeRun(i); + } + + // 创建新的run并设置文本 + XWPFRun newRun = paragraph.insertNewRun(info.startRun); + newRun.setText(info.replacementText); + + // 应用样式 + style.applyStyle(newRun); + } + + // 用于保存和恢复运行样式的辅助类 + private static class RunStyle { + String fontFamily; + int fontSize; + boolean bold; + boolean italic; + String color; + UnderlinePatterns underline; + + RunStyle(XWPFRun run) { + this.fontFamily = run.getFontFamily(); + this.fontSize = run.getFontSize(); + this.bold = run.isBold(); + this.italic = run.isItalic(); + this.color = run.getColor(); + this.underline = run.getUnderline(); + } + + void applyStyle(XWPFRun run) { + if (fontFamily != null) { + run.setFontFamily(fontFamily); + run.setFontFamily(fontFamily, XWPFRun.FontCharRange.eastAsia); + } + if (fontSize != -1) { + run.setFontSize(fontSize); + } + run.setBold(bold); + run.setItalic(italic); + if (color != null) { + run.setColor(color); + } + if (underline != null) { + run.setUnderline(underline); + } + } + } + +// public String generatePdf(String basePackagePath, String templateFileName, Object templateParam, String fileName, String saveDirectory) { +// try { +// // 创建 Freemarker 的 Configuration 对象,设置默认的不兼容改进选项 +// Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); +// configuration.setDefaultEncoding("utf-8"); +// // 设置模板加载器,加载模板文件 +// configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath)); +// Template t = configuration.getTemplate(templateFileName, "utf-8"); +// +// // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题 +//// String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8"); +// String encodedFileName =fileName ; +// +// // 定义保存文件的路径 +// File saveDir = new File(saveDirectory); +// if (!saveDir.exists()) { +// saveDir.mkdirs(); +// } +// +// // 定义文件名 +// String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc"; +// +// // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB +// Writer out = new BufferedWriter(new FileWriter(filePath), 10240); +// +// // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中 +// t.process(templateParam, out); +// out.close(); +// +// File file = new File(filePath); +// +// // 检查文件是否存在 +// if (!file.exists()) { +// throw new FileNotFoundException("文件不存在: " + filePath); +// } +// +// // 读取文件内容 +// byte[] fileContent = new byte[(int) file.length()]; +// try (FileInputStream fis = new FileInputStream(file)) { +// fis.read(fileContent); +// } +// +// String test = pdfUtils.test(encodedFileName + ".doc"); +//// MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent); +//// String s = ObsUploadUtil.obsUpload(mockMultipartFile); +// return test; +// } catch (IOException | TemplateException e) { +// log.error("生成pdf异常,异常原因:{}", e.getMessage(), e); +// throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage()); +// } +// } + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java index 984f88e..f4e9e68 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java @@ -17,6 +17,7 @@ import com.ruoyi.system.vo.TCheckAcceptRecordVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import java.util.List; @@ -40,10 +41,10 @@ PageInfo<TCheckAcceptRecordVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); List<TCheckAcceptRecordVO> list = this.baseMapper.pageList(query,pageInfo); list.forEach(item -> { - item.setCleanSituation(item.getCleanSituation()!=null?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getCleanSituation())):""); - item.setOverallSituation(item.getOverallSituation()!=null?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getOverallSituation())):""); - item.setDeviceSituation(item.getDeviceSituation()!=null?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getDeviceSituation())):""); - item.setFurnitureSituation(item.getDeviceSituation()!=null?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getFurnitureSituation())):""); + item.setCleanSituation(StringUtils.hasLength(item.getCleanSituation()) ?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getCleanSituation())):""); + item.setOverallSituation(StringUtils.hasLength(item.getOverallSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getOverallSituation())):""); + item.setDeviceSituation(StringUtils.hasLength(item.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getDeviceSituation())):""); + item.setFurnitureSituation(StringUtils.hasLength(item.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getFurnitureSituation())):""); }); pageInfo.setRecords(list); -- Gitblit v1.7.1