无关风月
2025-03-06 40158f61b8bf3f07c3ebc928ccd8e5ccbc2e22ff
Merge branch 'master' of https://gitee.com/xiaochen991015/xizang
4个文件已修改
1个文件已添加
681 ■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java 218 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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);
        }
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;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java
New file
@@ -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);
    }
}
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());
//        }
//    }
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);