| | |
| | | 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; |
| | |
| | | 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> |
| | |
| | | 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; |
| | | } |
| | | |
| | | // /** |
| | |
| | | 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 : ""); |
| | | } |
| | | |
| | | } |
| | | |
| New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | 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()); |
| | | // } |
| | | // } |
| | | |
| | | |
| | | |
| | | |
| | | } |