6c
2025-10-31 cfd0903dc0cfc7ffd39c2caa101f31a50441d39c
src/main/java/com/product/print/service/PrintRealizeService.java
@@ -10,9 +10,11 @@
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.TextRenderData;
import com.deepoove.poi.data.style.Style;
import com.google.common.collect.Lists;
import com.product.common.lang.StringUtils;
import com.product.core.cache.DataPoolCacheImpl;
import com.product.core.config.Global;
import com.product.core.entity.DataEntity;
import com.product.core.entity.DataTableEntity;
import com.product.core.entity.FieldSetEntity;
import com.product.core.exception.BaseException;
@@ -22,16 +24,21 @@
import com.product.print.config.CmnCode;
import com.product.print.config.CmnConst;
import com.product.print.service.ide.IPrintRealizeService;
import com.product.print.util.CustomPictureRenderPolicy;
import com.product.print.util.DynamicTableRenderPolicy;
import com.product.print.util.FlowOpinionRenderPolicy;
import com.product.print.util.TableEmptyHandler;
import com.product.tool.flow.service.FlowDetailService;
import com.product.util.BaseUtil;
import com.product.util.SystemParamReplace;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.io.File;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;
@@ -57,7 +64,7 @@
   public static void dataConvertCheckedData(FieldSetEntity fse) {
      TextRenderData selSymbol = new TextRenderData(CmnConst.PRINT_CHECKED_CHAR, new Style(CmnConst.PRINT_FONT, 14));
      TextRenderData unselSymbol = new TextRenderData(CmnConst.PRINT_UNCHECKED_CHAR, new Style(CmnConst.PRINT_FONT, 14));
      TextRenderData unselSymbol = new TextRenderData(CmnConst.PRINT_UNCHECKED_CHAR, new Style(CmnConst.PRINT_FONT, 18));
      //获取表单字段
      Object[] fields = fse.getMeta().getFields();
@@ -113,17 +120,32 @@
   /**
    * 打印传输pdf流到前端
    *
    * @param fse          打印数据
    * @param fseOrDte          打印数据
    * @param response     响应
    * @param isConvertPdf 是否转换为pdf
    * @throws BaseException
    */
   @Override
   public void print(FieldSetEntity fse, HttpServletResponse response, boolean isConvertPdf) throws BaseException {
      //打印配置
      FieldSetEntity printConfig = getPrintConfig(fse.getString("~" + CmnConst.PRINT_TEMP + "~"));
   public void print(DataEntity fseOrDte, HttpServletResponse response, boolean isConvertPdf) throws BaseException {
        FieldSetEntity fse;
        DataTableEntity dte;
        //打印配置
        FieldSetEntity printConfig;
        if (fseOrDte.isFieldsetEntity()) {
            fse = (FieldSetEntity) fseOrDte;
            printConfig = getPrintConfig(getPrintTemplateField(fse));
        } else if (fseOrDte.isDataTableEntity()) {
            dte = (DataTableEntity) fseOrDte;
            fse = dte.getFieldSetEntity(0);
            printConfig = getPrintConfig(getPrintTemplateField(fse));
        } else {
            throw new BaseException(CmnCode.GET_BEAN_FAIL);
        }
      //获取到替换后的文件路径 (pdf文件或者word文件)
      String tempPdfFilePath = replaceTemplateFileOut(printConfig, fse, isConvertPdf);
      String tempPdfFilePath = replaceTemplateFileOut(printConfig, fseOrDte, isConvertPdf);
        if (StringUtils.isEmpty(tempPdfFilePath)) {
            throw new BaseException(CmnCode.DEALT_FILE_PATH_IS_EMPTY);
        }
      //获取文件名
      String fileName = BaseUtil.ifNull(printConfig.getString(CmnConst.PRINT_FILE_NAME), printConfig.getString(CmnConst.PRINT_NAME));
      if (isConvertPdf) {
@@ -138,11 +160,27 @@
      // 设置自定义头Content-Disposition
      response.setHeader("Content-Disposition", "attachment;filename=" + URLEncodeUtil.encode(fileName));
      try (
            OutputStream out = response.getOutputStream();
      ) {
         FileUtil.writeToStream(new File(tempPdfFilePath), out);
      } catch (Exception e) {
      try (OutputStream out = response.getOutputStream();) {
            if (tempPdfFilePath.contains(",")) {
                // 更高效的方式:直接从文件合并到输出流
                PDFMergerUtility merger = new PDFMergerUtility();
                merger.setDestinationStream(response.getOutputStream());
                String[] tempPdfFilePathArr = tempPdfFilePath.split(",");
                for (int i = 0; i < tempPdfFilePathArr.length; i++) {
                    String singleTempPdfFilePath = tempPdfFilePathArr[i];
                    File file = new File(singleTempPdfFilePath);
                    if (file.exists()) {
                        merger.addSource(file); // 直接添加文件源
                    }
                }
                // 执行合并
                merger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
            } else {
                FileUtil.writeToStream(new File(tempPdfFilePath), out);
            }
        } catch (Exception e) {
         e.printStackTrace();
         throw new BaseException(CmnCode.PRINT_CONTENT_FAIL, e);
      } finally {
@@ -151,8 +189,23 @@
         System.out.println(absolutePath);
         file.delete();
      }
   }
    /**
     * 获取打印模板字段
     * @param fse
     * @return
     */
    private String getPrintTemplateField(FieldSetEntity fse) {
        if (FieldSetEntity.isEmpty(fse)) {
            return null;
        }
        String printTemplateField = fse.getString("~" + CmnConst.PRINT_TEMP + "~");
        if (StringUtils.isEmpty(printTemplateField)) {
            printTemplateField = fse.getString("~print_template~");
        }
        return printTemplateField;
    }
   /**
    * 获取打印配置
@@ -175,71 +228,97 @@
    * 替换模板文件并输出
    *
    * @param printConf    打印配置
    * @param fse          替换数据
    * @param fseOrDte          替换数据
    * @param isConvertPdf 是否转换为pdf
    * @return 替换后的文件路径
    * @throws BaseException 异常
    */
   private String replaceTemplateFileOut(FieldSetEntity printConf, FieldSetEntity fse, boolean isConvertPdf) throws BaseException {
   private String replaceTemplateFileOut(FieldSetEntity printConf, DataEntity fseOrDte, boolean isConvertPdf) throws BaseException {
        FieldSetEntity fse = null;
        DataTableEntity dte = null;
        if (fseOrDte.isFieldsetEntity()) {
            fse = (FieldSetEntity) fseOrDte;
        } else if (fseOrDte.isDataTableEntity()) {
            dte = (DataTableEntity) fseOrDte;
        } else {
            throw new BaseException(CmnCode.GET_BEAN_FAIL);
        }
        if (fse != null) {
            dte = new DataTableEntity();
            dte.addFieldSetEntity(fse);
        }
      Object[] fields = fse.getFields();
      for (int i = 0; i < fields.length; i++) {
         String field = fields[i].toString();
         FieldSetEntity metaEntity = fse.getMeta().getFieldMeta(field);
         String fieldType = metaEntity.getString("field_type");
         //判断是否拥有流程标识
         if ("flowsign".equals(fieldType)) {
            if (StringUtils.equalsAny(fse.getString(field), "1", "2")) {
               //流程办理中或办结 获取流程意见
               FieldSetEntity flowTask = getBaseDao().getFieldSetByFilter("product_sys_flow_task", "table_name=? and record_uuid=?", new Object[]{fse.getTableName(), fse.getUUID()}, false);
               if (flowTask != null) {
                  String taskUuid = flowTask.getString(CmnConst.UUID);
                  //获取流程意见
                  FlowDetailService flowDetailService = SpringUtil.getBean(FlowDetailService.class);
                  JSONArray opinion = flowDetailService.getHistoryInfo(taskUuid);
                  fse.setValue("~flow_opinion~", opinion);
               }
            }
            break;
         }
      }
        List<String> localTempPathList = Lists.newArrayList();
        if (!DataTableEntity.isEmpty(dte)) {
            for (int m = 0; m < dte.getRows(); m++) {
                fse = dte.getFieldSetEntity(m);
      // 打印模板附件uuid
      String template_uuid = printConf.getString(CmnConst.PRINT_TEMPLATE);
      // 获取打印模板
      File file = getTemplateFile(template_uuid);
      // 加载参照 打印时使用显示值 而不是实际值
      getBaseDao().loadPromptData(fse);
      // 文件名前缀部分
      Object tempKey = UUID.randomUUID();
      // 替换后的word临时路径
      String localTempPathWord = Global.getSystemConfig("temp.dir", "") + File.separator + "temp_print_" + tempKey + ".docx";
      replaceWord(localTempPathWord, file.getPath(), fse);
      file.delete();
                Object[] fields = fse.getFields();
                for (int i = 0; i < fields.length; i++) {
                    String field = fields[i].toString();
                    FieldSetEntity metaEntity = fse.getMeta().getFieldMeta(field);
                    if (metaEntity == null) {
                        continue;
                    }
                    String fieldType = metaEntity.getString("field_type");
                    //判断是否拥有流程标识
                    if ("flowsign".equals(fieldType)) {
                        if (StringUtils.equalsAny(fse.getString(field), "1", "2")) {
                            //流程办理中或办结 获取流程意见
                            FieldSetEntity flowTask = getBaseDao().getFieldSetByFilter("product_sys_flow_task", "table_name=? and record_uuid=?", new Object[]{fse.getTableName(), fse.getUUID()}, false);
                            if (flowTask != null) {
                                String taskUuid = flowTask.getString(CmnConst.UUID);
                                //获取流程意见
                                FlowDetailService flowDetailService = SpringUtil.getBean(FlowDetailService.class);
                                JSONArray opinion = flowDetailService.getHistoryInfo(taskUuid);
                                fse.setValue("~flow_opinion~", opinion);
                            }
                        }
                        break;
                    }
                }
      String replaceParams = SystemParamReplace.replaceParams(printConf.getString(CmnConst.PRINT_FILE_NAME), fse);
      printConf.setValue(CmnConst.PRINT_FILE_NAME, replaceParams);
      if (isConvertPdf) {
         try {
            // 替换后的pdf临时路径
            String localTempPathPdf = Global.getSystemConfig("temp.dir", "") + File.separator + "temp_print_" + tempKey + ".pdf";
            //检查文件是否存在不存则创建
            FileUtil.touch(localTempPathPdf);
            // 转换pdf
            PdfConcurrenceUtil.convertToPdf(localTempPathWord, localTempPathPdf, "docx");
            // 删除word临时文件
            File wordTemp = new File(localTempPathWord);
            if (wordTemp.exists()) {
               wordTemp.delete();
            }
            return localTempPathPdf;
         } catch (Exception e) {
            throw new BaseException(CmnCode.CONVERT_PDF_ERROR, e);
         } finally {
            FileUtil.del(localTempPathWord);
         }
      }
      return localTempPathWord;
                // 打印模板附件uuid
                String template_uuid = printConf.getString(CmnConst.PRINT_TEMPLATE);
                // 获取打印模板
                File file = getTemplateFile(template_uuid);
                // 加载参照 打印时使用显示值 而不是实际值
                getBaseDao().loadPromptData(fse);
                // 文件名前缀部分
                Object tempKey = UUID.randomUUID();
                // 替换后的word临时路径
                String localTempPathWord = Global.getSystemConfig("temp.dir", "") + File.separator + "temp_print_" + tempKey + ".docx";
                replaceWord(localTempPathWord, file.getPath(), fse);
                file.delete();
                String replaceParams = SystemParamReplace.replaceParams(Optional.ofNullable(printConf.getString(CmnConst.PRINT_FILE_NAME)).orElse(printConf.getString(CmnConst.PRINT_NAME)), fse);
                printConf.setValue(CmnConst.PRINT_FILE_NAME, replaceParams);
                if (isConvertPdf) {
                    try {
                        // 替换后的pdf临时路径
                        String localTempPathPdf = Global.getSystemConfig("temp.dir", "") + File.separator + "temp_print_" + tempKey + ".pdf";
                        //检查文件是否存在不存则创建
                        FileUtil.touch(localTempPathPdf);
                        // 转换pdf
                        PdfConcurrenceUtil.convertToPdf(localTempPathWord, localTempPathPdf, "docx");
                        // 删除word临时文件
                        File wordTemp = new File(localTempPathWord);
                        if (wordTemp.exists()) {
                            wordTemp.delete();
                        }
                        localTempPathList.add(localTempPathPdf);
                    } catch (Exception e) {
                        throw new BaseException(CmnCode.CONVERT_PDF_ERROR, e);
                    } finally {
                        FileUtil.del(localTempPathWord);
                    }
                } else {
                    localTempPathList.add(localTempPathWord);
                }
            }
        }
      return BaseUtil.collection2String(localTempPathList);
   }
   /**
@@ -260,17 +339,27 @@
      Map<String, Object> cloneValues = new HashMap(dataFse.getValues());
      //获取子表数据
      Map<String, DataTableEntity> subDataMap = dataFse.getSubData();
      ConfigureBuilder config = Configure.createDefault().builder();
      ConfigureBuilder config = Configure.builder();
      config.addPlugin('@', new FlowOpinionRenderPolicy(flowOpinion));
      config.addPlugin('&', new CustomPictureRenderPolicy());
      config.buildGrammerRegex("(#)?([\\w\\u4e00-\\u9fa5]+)(\\.?[\\w\\u4e00-\\u9fa5\\|]*)*(#)?");
      TableEmptyHandler tableEmptyHandler = new TableEmptyHandler();
      if (!CollectionUtil.isEmpty(subDataMap)) {
         for (Map.Entry<String, DataTableEntity> entry : subDataMap.entrySet()) {
            cloneValues.put(entry.getKey(), entry.getValue().getData().stream().map(item -> (Map<String, Object>) ((Map) item.getValues())).collect(Collectors.toList()));
            config.bind(entry.getKey(), new DynamicTableRenderPolicy(entry.getKey()));
            tableEmptyHandler.addTag(entry.getKey());
         }
      }
      if(flowOpinion!=null && flowOpinion.size()>0){
         cloneValues.put("lx_flow_opinion", flowOpinion);
         config.bind("lx_flow_opinion", new DynamicTableRenderPolicy("lx_flow_opinion"));
      if (flowOpinion != null) {
         if (!CollectionUtil.isEmpty(flowOpinion)) {
            cloneValues.put("lx_flow_opinion", flowOpinion);
            config.bind("lx_flow_opinion", new DynamicTableRenderPolicy("lx_flow_opinion"));
         }
         tableEmptyHandler.addTag("lx_flow_opinion");
      }
      config.setValidErrorHandler(tableEmptyHandler);
      try {
         //检查输出文件是否存在,不存在则创建
         FileUtil.touch(outPath);