diff --git a/laws-module-system/pom.xml b/laws-module-system/pom.xml index ca924543..99ef1c0b 100644 --- a/laws-module-system/pom.xml +++ b/laws-module-system/pom.xml @@ -83,6 +83,12 @@ pdfbox 2.0.27 + + org.dom4j + dom4j + 2.1.3 + compile + diff --git a/laws-module-system/src/main/java/com/jero/modules/util/SysOnlyOfficePdfUtil.java b/laws-module-system/src/main/java/com/jero/modules/util/SysOnlyOfficePdfUtil.java new file mode 100644 index 00000000..84e0bce7 --- /dev/null +++ b/laws-module-system/src/main/java/com/jero/modules/util/SysOnlyOfficePdfUtil.java @@ -0,0 +1,238 @@ +package com.jero.modules.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.jero.common.exception.JeroBootException; +import com.jero.common.util.CommonUtils; +import com.jero.common.util.IntekeyUtils; +import com.jero.common.util.MinioUtil; +import com.jero.common.util.oConvertUtils; +import com.jero.modules.oss.entity.OSSFile; +import com.jero.modules.oss.service.IOSSFileService; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author: Mzaxd + * @Date: 2024/4/25 9:08 + */ +@Slf4j +@Component +public class SysOnlyOfficePdfUtil { + + @Resource + private IOSSFileService ossFileService; + + @Value("${file.downloadUrl}") + private String downloadFileUrl; + + @Value("${onlyoffice.toUrl}") + private String onlyOfficeUrl; + + @Value("${onlyoffice.transferPDfUrl}") + private String transferPDfUrl; + + @Value(value = "${jero.uploadType}") + private String uploadType; + + @Value("#{'${download.enable}'}") + private boolean enable; + + @Value("${file.path}") + private String filePath;//文件存储路径 + + private static final int TIMEOUT = 60000; // 通用超时设置 + + @SneakyThrows + public void convertAndSavePdfFile(String fileName, String originFileId, String onlineFilePath) { + String key = UUID.randomUUID().toString(); + log.info("转换的请求参数:onlyOfficeUrl:{}, filePdfName:{}, key:{}, originFileId:{}, onlineFilePath:{}", onlyOfficeUrl, fileName, key, originFileId, onlineFilePath); + // 获取onlyOffice转换后文件的url + String fileUrl = convertFile(fileName, key, onlineFilePath); + log.info("文件转换后PDF的URL: {}", fileUrl); + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + // 根据url获取转换后的文件 + fetchAndSaveFile(httpClient, fileName, fileUrl, originFileId); + } // 自动关闭httpClient + } + + private CloseableHttpClient createHttpClient() { + RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(TIMEOUT) + .setConnectionRequestTimeout(TIMEOUT) + .setConnectTimeout(TIMEOUT) + .build(); + + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + return httpClientBuilder.setDefaultRequestConfig(requestConfig).build(); + } + + @SneakyThrows + private String convertFile(String fileName, String key, String onlineFilePath) { + Map parameters = new HashMap<>(); + parameters.put("async", false); + parameters.put("filetype", "docx"); + parameters.put("key", key); + parameters.put("outputtype", "pdf"); + parameters.put("title", fileName + ".pdf"); + parameters.put("url", downloadFileUrl + onlineFilePath); + log.info("转换文件参数: {}", parameters); + JSONObject requestJson = new JSONObject(parameters); + String data = requestJson.toString(); + + try (CloseableHttpClient httpClient = createHttpClient()) { + String url = "http://" + onlyOfficeUrl + transferPDfUrl; + log.info("访问onlyOfficeUrl:{}", url); + HttpPost httpPost = new HttpPost(url); + httpPost.setEntity(new StringEntity(data, "UTF-8")); + httpPost.setHeader("Content-type", "application/json; charset=UTF-8"); + + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + return handleResponse(response); + } + } + } + + @SneakyThrows + private void fetchAndSaveFile(CloseableHttpClient httpClient, String fileName, String fileUrl, String originFileId) { + String pdfFileName = fileName + ".pdf"; + HttpGet get = new HttpGet(fileUrl); + try (CloseableHttpResponse response = httpClient.execute(get)) { + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new JeroBootException("Failed to fetch the file: " + response); + } + HttpEntity entity = response.getEntity(); + if (entity != null) { + try (InputStream content = entity.getContent()) { + MultipartFile file = new MockMultipartFile(pdfFileName, pdfFileName, ContentType.APPLICATION_OCTET_STREAM.toString(), content); + // 保存文件逻辑 + String savePath = CommonUtils.upload(file, "convert", uploadType); + if (oConvertUtils.isNotEmpty(savePath)) { + // 如果有的话,删除原来的pdf版本 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(OSSFile::getBindId, originFileId); + ossFileService.remove(lambdaQueryWrapper); + + // 上传成功 进行数据库存储 + OSSFile ossFile = new OSSFile(); + // 文件名 + ossFile.setFileName(pdfFileName); + ossFile.setUrl(savePath); + ossFile.setBindId(originFileId); + ossFileService.save(ossFile); + } + } + } + } + } + + @SneakyThrows + private String handleResponse(CloseableHttpResponse response) { + HttpEntity entity = response.getEntity(); + if (entity != null) { + String responseData = EntityUtils.toString(entity, "UTF-8"); + return analysisXML(responseData); + } + return null; + } + + @SneakyThrows + public static String analysisXML(String xmlString) { + log.info("OnlyOffice返回xmlString:{}", xmlString); + Document document = DocumentHelper.parseText(xmlString); + Element root = document.getRootElement(); + return root.elementText("FileUrl").replaceAll("&", "&"); + } + + public List docFileTransferPdf(String fileIds) { + List resultList = new ArrayList<>(); + // 获取所有需要生成pdf版本的文件 + // 先获取所有有url的文件记录 + LambdaQueryWrapper ossWrapper = new LambdaQueryWrapper<>(); + ossWrapper.in(StrUtil.isNotBlank(fileIds), OSSFile::getId, Arrays.asList(fileIds.split(","))); + ossWrapper.isNotNull(OSSFile::getUrl); + List ossFileList = ossFileService.list(ossWrapper); + if (CollUtil.isEmpty(ossFileList)) { + return null; + } + // 筛选出后缀为doc或docx的文件 + List docFileList = ossFileList.stream() + .filter(o -> o.getUrl().endsWith("doc") || o.getUrl().endsWith("docx")) + .collect(Collectors.toList()); + // 筛选出已经转换过的pdf文件 + Map> alreadyTransferFileMap = ossFileList.stream() + .filter(o -> StrUtil.isNotBlank(o.getBindId())) + .collect(Collectors.groupingBy(OSSFile::getBindId)); + for (OSSFile ossFile : docFileList){ + List existPdfFileList = alreadyTransferFileMap.get(ossFile.getId()); + // 已经转换过的不再重复转换 + if (CollUtil.isNotEmpty(existPdfFileList)) { + continue; + } + String docFileName = ossFile.getFileName(); + log.info("开始处理文件名为 {} 的文件转换工作", docFileName); + // 根据文件url获取文件 + InputStream isDecrypt; + try (InputStream is = MinioUtil.download(ossFile.getUrl())) { + isDecrypt = is; + // 文件解密 + if (enable) { + isDecrypt = IntekeyUtils.autoDecryptInputStreamFile(isDecrypt, ossFile.getFileName()); + } + // 转换为pdf + String pdfFileName = docFileName.substring(0, docFileName.lastIndexOf(".")); + String targetPath = "convert/" + ossFile.getFileName(); + File targetFile = new File(filePath + targetPath); + try (OutputStream outputStream = Files.newOutputStream(targetFile.toPath())) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = isDecrypt.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } catch (IOException e) { + log.error("文件写入失败:" + e.getMessage()); + } + this.convertAndSavePdfFile(pdfFileName, ossFile.getId(), targetPath); + // 将下载到本地的文件删除 + FileUtil.del(targetFile); + log.info("文件名为 {} 的文件转换成功", docFileName); + resultList.add(ossFile); + } catch (Exception e) { + log.error("文件名为 {} 的文件转换失败,失败原因{}", docFileName, e.getMessage()); + } + } + return resultList; + } + +} diff --git a/laws-modules/src/main/java/com/jero/modules/activiti/process/esRevisionAdvice/listener/ProcessESRevisionEndListener.java b/laws-modules/src/main/java/com/jero/modules/activiti/process/esRevisionAdvice/listener/ProcessESRevisionEndListener.java index 703ed6a2..34f28333 100644 --- a/laws-modules/src/main/java/com/jero/modules/activiti/process/esRevisionAdvice/listener/ProcessESRevisionEndListener.java +++ b/laws-modules/src/main/java/com/jero/modules/activiti/process/esRevisionAdvice/listener/ProcessESRevisionEndListener.java @@ -12,10 +12,10 @@ import com.jero.modules.activiti.process.esRevisionAdvice.service.EsRevisionAdvi import com.jero.modules.activiti.process.esRevisionAdvice.service.EsRevisionService; import com.jero.modules.laws.common.constant.FieldCommon; import com.jero.modules.laws.common.service.ILawsCommonService; -import com.jero.modules.laws.enterprise.service.EnterpriseStandardPlanService; import com.jero.modules.oss.entity.OSSFile; import com.jero.modules.oss.service.IOSSFileService; import com.jero.modules.tag.enums.TableNameEnum; +import com.jero.modules.util.SysOnlyOfficePdfUtil; import lombok.SneakyThrows; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.ExecutionListener; @@ -60,12 +60,12 @@ public class ProcessESRevisionEndListener implements ExecutionListener { @Resource private EsRevisionAdviceService revisionAdviceService; + @Resource + private SysOnlyOfficePdfUtil sysOnlyOfficePdfUtil; + @Value(value = "${jero.uploadType}") private String uploadType; - @Resource - private EnterpriseStandardPlanService planService; - @SneakyThrows @Override public void notify(DelegateExecution delegateExecution) { @@ -105,7 +105,12 @@ public class ProcessESRevisionEndListener implements ExecutionListener { variables.put(FieldCommon.ID, standardId); String onEditFile = data.getOnEditFile(); // 发布稿 - variables.put(FieldCommon.FILE_PUBLISH_OF_ORIGINAL, onEditFile); + List ossFileList = sysOnlyOfficePdfUtil.docFileTransferPdf(onEditFile); + if (ossFileList != null && !ossFileList.isEmpty()) { + variables.put(FieldCommon.FILE_PUBLISH_OF_ORIGINAL, onEditFile + "," + ossFileList.get(0).getId()); + } else { + variables.put(FieldCommon.FILE_PUBLISH_OF_ORIGINAL, onEditFile); + } return variables; }