xuhy
2026-01-17 767afe483f7bde1c438deb17e97ec44eb9ff2e58
检测信息导出
2个文件已添加
1个文件已修改
991 ■■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TSysInspectionController.java 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java 403 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java 366 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TSysInspectionController.java
@@ -8,10 +8,13 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.model.TCrmClinic;
import com.ruoyi.system.model.TSysAppUser;
import com.ruoyi.system.model.TSysInspection;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.TCrmClinicService;
import com.ruoyi.system.service.TSysAppUserService;
import com.ruoyi.system.service.TSysInspectionService;
import com.ruoyi.web.controller.tool.WordUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,9 +23,7 @@
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.*;
/**
 * <p>
@@ -41,12 +42,16 @@
    private final TokenService tokenService;
    private final TCrmClinicService crmClinicService;
    private final ISysUserService sysUserService;
    private final TSysAppUserService sysAppUserService;
    private final WordUtil wordUtil;
    @Autowired
    public TSysInspectionController(TSysInspectionService sysInspectionService, TokenService tokenService, TCrmClinicService crmClinicService, ISysUserService sysUserService) {
    public TSysInspectionController(TSysInspectionService sysInspectionService, TokenService tokenService, TCrmClinicService crmClinicService, ISysUserService sysUserService, TSysAppUserService sysAppUserService, WordUtil wordUtil) {
        this.sysInspectionService = sysInspectionService;
        this.tokenService = tokenService;
        this.crmClinicService = crmClinicService;
        this.sysUserService = sysUserService;
        this.sysAppUserService = sysAppUserService;
        this.wordUtil = wordUtil;
    }
//    /**
@@ -143,5 +148,214 @@
        return R.ok(sysInspectionService.removeByIds(ids));
    }
    @Log(title = "用户检测信息管理信息-导出检测信息", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "导出检测信息")
    @GetMapping(value = "/exportInspection")
    public R<String> exportInspection(@RequestParam String id) {
        TSysInspection sysInspection = sysInspectionService.getById(id);
        // 查询诊所信息
        TCrmClinic crmClinic = crmClinicService.getById(sysInspection.getClinicId());
        if(Objects.isNull(crmClinic)){
            return R.fail("诊所信息不存在");
        }
        Map<String, Object> templateParam = new HashMap<>();
        fill(templateParam, "clinicName", crmClinic.getClinicName());
        // 住院次数
        if(StringUtils.hasLength(sysInspection.getHospitalizationCount())){
            fill(templateParam, "hospitalizationCount", sysInspection.getHospitalizationCount());
        }
        // 住院编号
        if(StringUtils.hasLength(sysInspection.getHospitalizationNumber())){
            fill(templateParam, "hospitalizationNumber", sysInspection.getHospitalizationNumber());
        }
        // 姓名
        if(StringUtils.hasLength(sysInspection.getNickName())){
            fill(templateParam, "nickName", sysInspection.getNickName());
        }
        // 出生地
        if(StringUtils.hasLength(sysInspection.getBirthplace())){
            fill(templateParam, "birthplace", sysInspection.getBirthplace());
        }
        // 性别
        if(Objects.nonNull(sysInspection.getSex())){
            if(sysInspection.getSex() == 1){
                fill(templateParam, "sex", "男");
            }else{
                fill(templateParam, "sex", "女");
            }
        }
        // 民族
        if(StringUtils.hasLength(sysInspection.getNation())){
            fill(templateParam, "nation", sysInspection.getNation());
        }
        // 年龄
        if(Objects.nonNull(sysInspection.getAge())){
            fill(templateParam, "age", sysInspection.getAge());
        }
        // 入院日期
        if(StringUtils.hasLength(sysInspection.getAdmissionDate())){
            fill(templateParam, "admissionDate", sysInspection.getAdmissionDate());
        }
        // 婚姻状况
        if(Objects.nonNull(sysInspection.getMaritalStatus())){
            fill(templateParam, "maritalStatus", sysInspection.getMaritalStatus());
        }
        // 记录日期
        if(StringUtils.hasLength(sysInspection.getRecordDate())){
            fill(templateParam, "recordDate", sysInspection.getRecordDate());
        }
        // 职业
        if(StringUtils.hasLength(sysInspection.getOccupation())){
            fill(templateParam, "occupation", sysInspection.getOccupation());
        }
        // 病史陈述者
        if(StringUtils.hasLength(sysInspection.getHistoryNarrator())){
            fill(templateParam, "historyNarrator", sysInspection.getHistoryNarrator());
        }
        // 主诉
        if(StringUtils.hasLength(sysInspection.getChiefComplaint())){
            fill(templateParam, "chiefComplaint", sysInspection.getChiefComplaint());
        }
        // 现病史
        if(StringUtils.hasLength(sysInspection.getPresentHistory())){
            fill(templateParam, "presentHistory", sysInspection.getPresentHistory());
        }
        // 既往史
        if(StringUtils.hasLength(sysInspection.getPastMedicalHistory())){
            fill(templateParam, "pastMedicalHistory", sysInspection.getPastMedicalHistory());
        }
        // 月经史
        if(StringUtils.hasLength(sysInspection.getMenstrualHistory())){
            fill(templateParam, "menstrualHistory", sysInspection.getMenstrualHistory());
        }
        // 婚育史
        if(StringUtils.hasLength(sysInspection.getObstetricHistory())){
            fill(templateParam, "obstetricHistory", sysInspection.getObstetricHistory());
        }
        // 家族史
        if(StringUtils.hasLength(sysInspection.getFamilyHistory())){
            fill(templateParam, "familyHistory", sysInspection.getFamilyHistory());
        }
        // 体温
        if(StringUtils.hasLength(sysInspection.getBodyTemperature())){
            fill(templateParam, "bodyTemperature", sysInspection.getBodyTemperature());
        }
        // 脉搏
        if(StringUtils.hasLength(sysInspection.getPulse())){
            fill(templateParam, "pulse", sysInspection.getPulse());
        }
        // 呼吸
        if(StringUtils.hasLength(sysInspection.getBreathe())){
            fill(templateParam, "breathe", sysInspection.getBreathe());
        }
        // 血压
        if(StringUtils.hasLength(sysInspection.getBloodPressure())){
            fill(templateParam, "bloodPressure", sysInspection.getBloodPressure());
        }
        // 一般情况
        if(StringUtils.hasLength(sysInspection.getGeneralSituation())){
            fill(templateParam, "generalSituation", sysInspection.getGeneralSituation());
        }
        String url = wordUtil.generatePdf(
                "/usr/local/project/template/",
                "病历-样例.docx",
                templateParam,
                "病历-"+sysInspection.getNickName(),
                "/usr/local/project/template/");
        // windows
//        String url = wordUtil.generatePdf(
//                "E:\\WXWork\\1688857451467088\\Cache\\File\\2026-01\\",
//                "病历-样例.docx",
//                templateParam,
//                "病历-样例-temp",
//                "E:\\WXWork\\1688857451467088\\Cache\\File\\2026-01\\");
        return R.ok(url);
    }
    @Log(title = "用户检测信息管理信息-导出处方信息", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "导出处方信息")
    @GetMapping(value = "/exportPrescription")
    public R<String> exportPrescription(@RequestParam String id) {
        TSysInspection sysInspection = sysInspectionService.getById(id);
        // 查询诊所信息
        TCrmClinic crmClinic = crmClinicService.getById(sysInspection.getClinicId());
        if(Objects.isNull(crmClinic)){
            return R.fail("诊所信息不存在");
        }
        Map<String, Object> templateParam = new HashMap<>();
        fill(templateParam, "clinicName", crmClinic.getClinicName());
        // 住院编号
        if(StringUtils.hasLength(sysInspection.getPrescriptionNumber())){
            fill(templateParam, "prescriptionNumber", sysInspection.getPrescriptionNumber());
        }
        // 科室
        if(StringUtils.hasLength(sysInspection.getDepartmentName())){
            fill(templateParam, "departmentName", sysInspection.getDepartmentName());
        }
        // 处方时间
        if(StringUtils.hasLength(sysInspection.getPrescriptionTime())){
            fill(templateParam, "prescriptionTime", sysInspection.getPrescriptionTime());
        }
        // 姓名
        if(StringUtils.hasLength(sysInspection.getNickName())){
            fill(templateParam, "nickName", sysInspection.getNickName());
        }
        // 性别
        if(Objects.nonNull(sysInspection.getSex())){
            if(sysInspection.getSex() == 1){
                fill(templateParam, "sex", "男");
            }else{
                fill(templateParam, "sex", "女");
            }
        }
        // 年龄
        if(Objects.nonNull(sysInspection.getAge())){
            fill(templateParam, "age", sysInspection.getAge());
        }
        // 门诊病历号
        if(StringUtils.hasLength(sysInspection.getId())){
            fill(templateParam, "id", sysInspection.getId());
        }
        // 单位或家庭住址
        if(Objects.nonNull(sysInspection.getHomeAddress())){
            fill(templateParam, "homeAddress", sysInspection.getHomeAddress());
        }
        // 临床诊断及证型
        if(StringUtils.hasLength(sysInspection.getClinicalDiagnosis())){
            fill(templateParam, "clinicalDiagnosis", sysInspection.getClinicalDiagnosis());
        }
        // 处方内容
        if(StringUtils.hasLength(sysInspection.getPrescriptionContent())){
            fill(templateParam, "prescriptionContent", sysInspection.getPrescriptionContent());
        }
        // 备注
        if(StringUtils.hasLength(sysInspection.getRemarks())){
            fill(templateParam, "remarks", sysInspection.getRemarks());
        }
        String url = wordUtil.generatePdf(
                "/usr/local/project/template/",
                "处方单-样例.docx",
                templateParam,
                "处方单-"+sysInspection.getNickName(),
                "/usr/local/project/template/");
        // windows
//        String url = wordUtil.generatePdf(
//                "E:\\WXWork\\1688857451467088\\Cache\\File\\2026-01\\",
//                "处方单-样例.docx",
//                templateParam,
//                "处方单-样例-temp",
//                "E:\\WXWork\\1688857451467088\\Cache\\File\\2026-01\\");
        return R.ok(url);
    }
    private void fill(Map<String, Object> templateParam, String key, Object value) {
        if (com.ruoyi.common.utils.StringUtils.isEmpty(key)){
            throw new RuntimeException("key不能为空");
        }
        templateParam.put("${"+key+"}", value != null ? value : "");
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java
New file
@@ -0,0 +1,403 @@
package com.ruoyi.web.controller.tool;
import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import com.ruoyi.system.utils.util.ObsUploadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class PdfUtils {
    public  String wordToPdf(String url,String filePath, String fileName) {
        try {
            DocumentType documentType = DocumentType.DOC;
            if(url.contains(".docx")){
                documentType = DocumentType.DOCX;
            }
            if(url.contains(".doc")){
                documentType = DocumentType.DOC;
            }
            if(url.contains(".xlsx")){
                documentType = DocumentType.XLSX;
            }else {
                if(url.contains(".xls")){
                    documentType = DocumentType.XLS;
                }
            }
            InputStream inputStream = new URL(url).openStream();
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            IConverter converter = LocalConverter.builder().build();
            converter.convert(inputStream)
                    .as(documentType)
                    .to(stream)
                    .as(DocumentType.PDF).execute();
            //上传图片
            byte2File(stream.toByteArray(),filePath + "/pdf",fileName.substring(0,fileName.lastIndexOf(".")) + ".pdf");
            MultipartFile multipartFile = convertToMultipartFile(stream,fileName.substring(0,fileName.lastIndexOf(".")) );
            String s = ObsUploadUtil.obsUploadQrCode(multipartFile,fileName.substring(0,fileName.lastIndexOf(".")) + ".pdf");
            stream.close();
            inputStream.close();
            return s;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * word 转 pdf
     *
     */
    public String wordToPdf(String filePath, String fileName) {
        try {
            // 确保路径正确性
            String inputFile = new File(filePath, fileName).getAbsolutePath();
            String outputDir = new File(filePath, "pdf").getAbsolutePath();
            // 创建输出目录
            new File(outputDir).mkdirs();
            // 使用完整的转换参数
            List<String> command = Arrays.asList(
                    "/usr/bin/libreoffice",  // 使用完整路径
                    "--headless",
                    "--norestore",
                    "--convert-to",
                    "pdf:writer_pdf_Export:PDFExport{" +
                            "EmbedStandardFonts=1;" +
                            "EmbedFonts=1;" +
                            "EmbedOnlyUsedFonts=0;" +
                            "UseTaggedPDF=1" +
                            "}",
                    "--outdir",
                    outputDir,
                    inputFile
            );
            // 创建进程构建器
            ProcessBuilder pb = new ProcessBuilder(command);
            // 设置环境变量
            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");
            // 重定向错误流到标准输出
            pb.redirectErrorStream(true);
            // 启动进程
            Process process = pb.start();
            // 读取输出
            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");
                    System.out.println(line);
                }
            }
            // 等待进程完成,设置超时
            if (!process.waitFor(120, TimeUnit.SECONDS)) {
                process.destroyForcibly();
                throw new RuntimeException("转换超时");
            }
            int exitCode = process.exitValue();
            if (exitCode != 0) {
                throw new RuntimeException("转换失败,退出码:" + exitCode + "\n输出:" + output);
            }
            // 检查生成的PDF文件
            String pdfFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
            File pdfFile = new File(outputDir, pdfFileName);
            if (!pdfFile.exists() || pdfFile.length() == 0) {
                throw new RuntimeException("PDF文件未生成或为空");
            }
            String absolutePath = pdfFile.getAbsolutePath();
            MultipartFile file = convertFileToMultipartFile(pdfFile);
//            String s = tencentCosUtil.upLoadFile(multipartFile,"/wordToPdf");
            String url = ObsUploadUtil.obsUpload(file);
            return url;
        } catch (Exception e) {
            throw new RuntimeException("PDF转换失败: " + e.getMessage(), e);
        }
    }
    public MultipartFile convertFileToMultipartFile(File file) throws IOException {
        // 读取文件内容到字节数组
        byte[] fileContent = Files.readAllBytes(file.toPath());
        // 创建 MultipartFile 对象
        MultipartFile multipartFile = new MockMultipartFile(
                file.getName(),       // 文件名
                file.getName(),       // 原始文件名
                "application/pdf",  // 内容类型,根据实际情况调整
                fileContent           // 文件内容
        );
        return multipartFile;
    }
    // 在使用前检查和配置环境
    public static void setupEnvironment() {
        try {
            // 1. 检查LibreOffice安装
            checkLibreOffice();
            // 2. 检查和安装字体
            installFonts();
            // 3. 配置字体
            configureFonts();
            // 4. 验证环境变量
            checkEnvironment();
        } catch (Exception e) {
            throw new RuntimeException("环境设置失败: " + e.getMessage(), e);
        }
    }
    private static void checkLibreOffice() throws IOException, InterruptedException {
        Process process = Runtime.getRuntime().exec("which libreoffice");
        if (process.waitFor() != 0) {
            throw new RuntimeException("LibreOffice未安装");
        }
    }
    private static void installFonts() throws IOException, InterruptedException {
        // 创建字体安装脚本
        String scriptContent =
                "#!/bin/bash\n" +
                        "apt-get update\n" +
                        "apt-get install -y fonts-wqy-zenhei fonts-wqy-microhei fonts-arphic-ukai fonts-arphic-uming\n" +
                        "fc-cache -fv\n";
        File script = new File("/tmp/install_fonts.sh");
        Files.write(script.toPath(), scriptContent.getBytes());
        script.setExecutable(true);
        // 执行脚本
        Process process = Runtime.getRuntime().exec("sudo /tmp/install_fonts.sh");
        process.waitFor();
        // 清理脚本
        script.delete();
    }
    private static void configureFonts() throws IOException {
        // 创建字体配置文件
        String fontConfig =
                "<?xml version=\"1.0\"?>\n" +
                        "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n" +
                        "<fontconfig>\n" +
                        "  <match target=\"pattern\">\n" +
                        "    <test name=\"family\"><string>serif</string></test>\n" +
                        "    <edit name=\"family\" mode=\"prepend\">\n" +
                        "      <string>WenQuanYi Zen Hei</string>\n" +
                        "    </edit>\n" +
                        "  </match>\n" +
                        "  <match target=\"pattern\">\n" +
                        "    <test name=\"family\"><string>sans-serif</string></test>\n" +
                        "    <edit name=\"family\" mode=\"prepend\">\n" +
                        "      <string>WenQuanYi Zen Hei</string>\n" +
                        "    </edit>\n" +
                        "  </match>\n" +
                        "</fontconfig>";
        // 写入配置文件
        File configFile = new File(System.getProperty("user.home") + "/.fonts.conf");
        Files.write(configFile.toPath(), fontConfig.getBytes());
    }
    private static void checkEnvironment() {
        // 检查环境变量
        String[] requiredVars = {"LANG", "LC_ALL", "LANGUAGE"};
        for (String var : requiredVars) {
            String value = System.getenv(var);
            if (value == null || !value.contains("zh_CN")) {
                System.err.println("警告: " + var + " 环境变量未正确设置");
            }
        }
    }
    public static MultipartFile convertToMultipartFile(ByteArrayOutputStream baos, String fileName) throws IOException {
        // 创建一个临时文件
        File tempFile = File.createTempFile(fileName, ".pdf");
        // 将ByteArrayOutputStream中的数据写入临时文件
        try (FileOutputStream fos = new FileOutputStream(tempFile)) {
            baos.writeTo(fos);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 创建一个MultipartFile对象
        return new MockMultipartFile(
                fileName + ".pdf",          // 参数名称
                fileName + ".pdf", // 文件名
                "application/pdf", // 内容类型
                Files.readAllBytes(tempFile.toPath()) // 文件内容
        );
    }
    /**
     * file转byte
     */
    public static byte[] file2byte(File file){
        byte[] buffer = null;
        try{
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1)
            {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }
        catch (IOException e){
            e.printStackTrace();
        }
        return buffer;
    }
    /**
     * byte 转file
     */
    public static File byte2File(byte[] buf, String filePath, String fileName){
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        OutputStreamWriter osw = null;
        File file = null;
        try{
            File dir = new File(filePath+"/");
            if (!dir.exists()){
                dir.mkdirs();
            }
            file = new File(filePath +File.separator + fileName);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
//            osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            bos.write(buf);
        }catch (Exception e){
            e.printStackTrace();
        }
        finally{
            if (bos != null){
                try{
                    bos.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (fos != null){
                try{
                    fos.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
        return file;
    }
    /**
     * multipartFile转File
     **/
    public static File multipartFile2File(MultipartFile multipartFile){
        File file = null;
        if (multipartFile != null){
            try {
                file= File.createTempFile("tmp", null);
                multipartFile.transferTo(file);
                System.gc();
                file.deleteOnExit();
            }catch (Exception e){
                e.printStackTrace();
                log.warn("multipartFile转File发生异常:"+e);
            }
        }
        return file;
    }
    public static void main(String[] args) {
//        String url = "file:///E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09\\专业技术工作总结.docx";
//        String filePath = "E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09";
//        String fileName = "专业技术工作总结.docx";
//        String s = wordToPdf(url, filePath, fileName);
//        System.err.println(s);
//        String url = "file:///F:\\测试动态列表Word.doc";
////        String filePath = "E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09";
////        String fileName = "专业技术工作总结.docx";4
//        String filePath = "F:\\";
//
//        String s = wordToPdf(url, filePath, "测试动态列表Word.doc");
//        System.err.println(s);
        // TODO Auto-generated method stub
//        HashMap<String, Object> paramMap = new HashMap<>();
//        paramMap.put("CorpID", "SCZT006959");
//        paramMap.put("Pwd", "123456");
//        paramMap.put("Mobile", "19522115070");
//        paramMap.put("Cell", "");
//        paramMap.put("SendTime", "");
//        paramMap.put("Content", java.net.URLEncoder.encode("你好,这是测试短信发送。【职评网】"));
//        String result3 = HttpUtil.post("http://sdk2.028lk.com/sdk2/BatchSend2.aspx", paramMap);
//        if (result3 == null) {
//            result3 = "";
//        }
//        result3 = StrUtil.trim(result3);
//        log.info("返回结果:" + result3);
//
//        if (result3.matches("^\\d+$") && !result3.equals("0")) {
//            return;
//        }
    }
    public String test(String fileName){
        // windows
//        String url = "file:///E:\\WXWork\\1688857451467088\\Cache\\File\\2026-01\\"+fileName;
//        String filePath = "E:\\WXWork\\1688857451467088\\Cache\\File\\2026-01";
//        String s = wordToPdf(url,filePath, fileName);
        // linux
        String filePath = "/usr/local/project/template/";
        String result = wordToPdf(filePath, fileName);
        log.info("word转pdf返回结果:" + result);
        return result;
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java
New file
@@ -0,0 +1,366 @@
package com.ruoyi.web.controller.tool;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.utils.util.ObsUploadUtil;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Component
public class WordUtil {
    /**
     * 基于模板生成 Word 文档
     *
     * @param basePackagePath  resources 目录下模板所在包路径
     * @param templateFileName 模板文件名
     * @param templateParam    模板参数
     * @param fileName         文件名
     */
    @Resource
    private PdfUtils pdfUtils;
    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);
            XWPFParagraph pic = document.createParagraph();
            Base64.Decoder decoder = Base64.getDecoder();
            // 处理段落
            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.err.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 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);
    }
    public String generate(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);
            }
            MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent);
            String url = ObsUploadUtil.obsUploadQrCode(mockMultipartFile,"/wordGenerate");
            return url;
        } catch (IOException | TemplateException e) {
            log.error("生成Word文档异常,异常原因:{}", e.getMessage(), e);
            throw new RuntimeException("生成Word文档异常,异常原因:" + e.getMessage());
        }
    }
    public String generatePdf(String basePackagePath, String templateFileName, Map<String,Object> templateParam, String fileName, String saveDirectory) {
        try {
            fillTemplate(basePackagePath+templateFileName, saveDirectory+fileName+".docx", templateParam);
            String filePath = saveDirectory + fileName + ".docx";
            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(fileName + ".docx");
//            MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent);
//            String s = ObsUploadUtil.obsUpload(mockMultipartFile);
            return test;
        } catch (IOException e) {
            log.error("生成pdf异常,异常原因:{}", e.getMessage(), e);
            throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage());
        }
    }
    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 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());
//        }
//    }
}