6c
10 天以前 cfd0903dc0cfc7ffd39c2caa101f31a50441d39c
已送审保养打印;
列表批量打印
已修改6个文件
591 ■■■■■ 文件已修改
doc/print.md 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/config/CmnCode.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/service/PrintRealizeService.java 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/service/ide/IPrintRealizeService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/util/CustomPictureRenderPolicy.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/util/DynamicTableRenderPolicy.java 313 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/print.md
@@ -26,7 +26,7 @@
### 子表标签
        {{$~index~}}: 固定写法,输出子表行号
        {{$~i~}}: 固定写法,输出子表行号
        {{$字段名}}:输出子表中指定字段的值
### ![img.png](img.png)
src/main/java/com/product/print/config/CmnCode.java
@@ -32,6 +32,9 @@
    NOT_FIND_START_FLAG("表格中没有找到起始下标", 15),
    //子表格中没有找到对应的字段
    NOT_FIND_CHILD_TABLE_FIELD("子表格中没有找到对应的字段", 16),
    GET_BEAN_FAIL("获取bean失败", 17),
    DEALT_FILE_PATH_IS_EMPTY("处理后的文件路径为空", 18),
    EXISTS_FILE_RECORD_BUT_NO_FILE("存在文件记录,但是未找到对应文件", 19),
    ;
    private String text;
src/main/java/com/product/print/service/PrintRealizeService.java
@@ -10,10 +10,11 @@
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.TextRenderData;
import com.deepoove.poi.data.style.Style;
import com.deepoove.poi.render.RenderContext;
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;
@@ -30,17 +31,15 @@
import com.product.tool.flow.service.FlowDetailService;
import com.product.util.BaseUtil;
import com.product.util.SystemParamReplace;
import org.apache.poi.xwpf.usermodel.XWPFTable;
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.File;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
@@ -121,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) {
@@ -146,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 {
@@ -159,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;
    }
    /**
     * 获取打印配置
@@ -183,75 +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);
            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;
            }
        }
        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";
                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;
                    }
                }
        replaceWord(localTempPathWord, file.getPath(), fse);
        file.delete();
                // 打印模板附件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";
        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();
                }
                return localTempPathPdf;
            } catch (Exception e) {
                throw new BaseException(CmnCode.CONVERT_PDF_ERROR, e);
            } finally {
                FileUtil.del(localTempPathWord);
            }
        }
        return localTempPathWord;
                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);
    }
    /**
@@ -274,18 +341,6 @@
        Map<String, DataTableEntity> subDataMap = dataFse.getSubData();
        ConfigureBuilder config = Configure.builder();
        config.addPlugin('@', new FlowOpinionRenderPolicy(flowOpinion));
        config.addPlugin('$', new com.deepoove.poi.policy.DynamicTableRenderPolicy() {
            @Override
            public void doRender(RenderContext<Object> context) throws Exception {
                return;
            }
            @Override
            public void render(XWPFTable table, Object data) throws Exception {
                return;
            }
        });
        config.addPlugin('&', new CustomPictureRenderPolicy());
        config.buildGrammerRegex("(#)?([\\w\\u4e00-\\u9fa5]+)(\\.?[\\w\\u4e00-\\u9fa5\\|]*)*(#)?");
        TableEmptyHandler tableEmptyHandler = new TableEmptyHandler();
src/main/java/com/product/print/service/ide/IPrintRealizeService.java
@@ -1,6 +1,6 @@
package com.product.print.service.ide;
import com.product.core.entity.FieldSetEntity;
import com.product.core.entity.DataEntity;
import com.product.core.exception.BaseException;
import javax.servlet.http.HttpServletResponse;
@@ -16,11 +16,10 @@
    /**
     * 打印
     * @param fse 打印数据
     * @param fseOrDte 打印数据
     * @param response  响应
     * @param isConvertPdf  是否转换为pdf
     * @throws BaseException
     */
    void print(FieldSetEntity fse, HttpServletResponse response,boolean isConvertPdf) throws BaseException;
    void print(DataEntity fseOrDte, HttpServletResponse response, boolean isConvertPdf) throws BaseException;
}
src/main/java/com/product/print/util/CustomPictureRenderPolicy.java
@@ -8,9 +8,12 @@
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.policy.PictureRenderPolicy;
import com.deepoove.poi.render.RenderContext;
import com.product.common.lang.StringUtils;
import com.product.core.dao.BaseDao;
import com.product.core.entity.FieldSetEntity;
import com.product.core.spring.context.SpringMVCContextHolder;
import com.product.file.service.FileManagerService;
import com.product.print.config.CmnCode;
import java.io.File;
import java.io.FileInputStream;
@@ -31,9 +34,29 @@
    public PictureRenderData cast(Object source) throws Exception {
        FileManagerService fileManagerService = SpringUtil.getBean(FileManagerService.class);
        String templateUid = source.toString();
        if (StringUtils.isEmpty(templateUid)) {
            return null;
        }
        int width = 0;
        int height = 0;
        if (templateUid.contains(" ")) {
            String[] infoArr = templateUid.split(" ");
            templateUid = infoArr[0];
            width = Integer.parseInt(infoArr[1]);
            height = Integer.parseInt(infoArr[2]);
        }
        BaseDao baseDao = SpringUtil.getBean(BaseDao.class);
        FieldSetEntity attachmentFse = baseDao.getFieldSetEntity("product_sys_attachments", templateUid, false);
        fileManagerService.getFile(templateUid);
        // 遇错跳过,默认使用透明背景不拦截,记录日志到文件
        try {
            fileManagerService.getFile(templateUid);
        } catch (Exception e) {
            attachmentFse = baseDao.getFieldSetEntityByFilter("product_sys_attachments", "attachment_title=?", new Object[]{"sys_sp_deal_透明背景"}, false);
            fileManagerService.getFile(attachmentFse.getUUID());
            SpringMVCContextHolder.getSystemLogger().error(String.format("%s-附件uuid:%s", CmnCode.EXISTS_FILE_RECORD_BUT_NO_FILE.getText(), templateUid));
        }
        String fileName = attachmentFse.getString("file_name");
        String type = fileName.substring(fileName.lastIndexOf(".") + 1);
        File file = fileManagerService.getFile(attachmentFse);
@@ -47,7 +70,11 @@
        } else if ("svg".equals(type)) {
            imgType = PictureType.SVG;
        }
        return converter.convert(Pictures.ofStream(new FileInputStream(file), imgType).create());
        if (width > 0 && height > 0) {
            return converter.convert(Pictures.ofStream(new FileInputStream(file), imgType).size(width, height).create());
        } else {
            return converter.convert(Pictures.ofStream(new FileInputStream(file), imgType).create());
        }
    }
    @Override
src/main/java/com/product/print/util/DynamicTableRenderPolicy.java
@@ -1,12 +1,12 @@
package com.product.print.util;
import cn.hutool.core.collection.CollectionUtil;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.render.RenderContext;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.MetaTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.TableTools;
import com.google.common.collect.Maps;
import com.product.common.lang.StringUtils;
import com.product.core.exception.BaseException;
import com.product.print.config.CmnCode;
@@ -17,7 +17,9 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -30,11 +32,10 @@
    private String replaceKey;
    private final String indexKey = "~index~";
    private final String indexKey = "~i~";
    public DynamicTableRenderPolicy(String replaceKey) {
        this.replaceKey = replaceKey;
    }
    private List<MetaTemplate> metaTemplateList;
@@ -64,159 +65,173 @@
    @Override
    public void render(XWPFTable xwpfTable, Object data) throws Exception {
        if (xwpfTable == null) {
            return;
        }
        List<Map<String, Object>> subTableData = (List<Map<String, Object>>) data;
        List<XWPFTableRow> rows = xwpfTable.getRows();
        //读取rows中的内容
        String tableExpression = "{{" + this.replaceKey + "}}";
        //获取表格起始行和结束行
        int startRowIndex = -1;
        rows:
        for (int i = 0; i < rows.size(); i++) {
            XWPFTableRow row = rows.get(i);
            for (int j = 0; j < row.getTableCells().size(); j++) {
                XWPFTableCell cell = row.getTableCells().get(j);
                String text = cell.getText();
                if (tableExpression.equals(text)) {
                    startRowIndex = i;
                    break rows;
                }
        try {
            if (xwpfTable == null) {
                return;
            }
            List<Map<String, Object>> subTableData = (List<Map<String, Object>>) data;
            List<XWPFTableRow> rows = xwpfTable.getRows();
            //读取rows中的内容
            String tableExpression = "{{" + this.replaceKey + "}}";
            //获取表格起始行和结束行
            int startRowIndex = -1;
            outer: for (int i = 0; i < rows.size(); i++) {
                XWPFTableRow row = rows.get(i);
                for (int j = 0; j < row.getTableCells().size(); j++) {
                    XWPFTableCell cell = row.getTableCells().get(j);
                    String text = cell.getText();
                    if (tableExpression.equals(text)) {
                        startRowIndex = i;
                        break outer;
                    }
            }
        }
        if (startRowIndex == -1) {
            throw new BaseException(CmnCode.NOT_FIND_START_FLAG);
        }
                }
            }
            if (startRowIndex == -1) {
                throw new BaseException(CmnCode.NOT_FIND_START_FLAG);
            }
        //在表格中查找子表字段以{{$开头}}以}}结尾的内容
        String regex = "\\{\\{\\$[a-zA-Z0-9_]+\\}\\}";
        Pattern pattern = Pattern.compile(regex);
        //字段所在行
        XWPFTableRow fieldRow = null;
        for (int i = startRowIndex; i < rows.size(); i++) {
            XWPFTableRow row = rows.get(i);
            for (int j = 0; j < row.getTableCells().size(); j++) {
                XWPFTableCell cell = row.getTableCells().get(j);
                String text = cell.getText();
                Matcher matcher = pattern.matcher(text);
                if (matcher.find()) {
                    fieldRow = row;
                    i = rows.size();
                    break;
                }
            }
        }
        if (fieldRow == null) {
            throw new BaseException(CmnCode.NOT_FIND_CHILD_TABLE_FIELD);
        }
            //在表格中查找子表字段以{{$开头}}以}}结尾的内容
            String regex = "\\{\\{\\$[a-zA-Z0-9_]+\\}\\}";
            Pattern pattern = Pattern.compile(regex);
            //字段所在行
            XWPFTableRow fieldRow = null;
            for (int i = startRowIndex; i < rows.size(); i++) {
                XWPFTableRow row = rows.get(i);
                for (int j = 0; j < row.getTableCells().size(); j++) {
                    XWPFTableCell cell = row.getTableCells().get(j);
                    String text = cell.getText();
                    Matcher matcher = pattern.matcher(text);
                    if (matcher.find()) {
                        fieldRow = row;
                        i = rows.size();
                        break;
                    }
                }
            }
            if (fieldRow == null) {
                throw new BaseException(CmnCode.NOT_FIND_CHILD_TABLE_FIELD);
            }
        //读取最后行每个单元格的值,调用getReplaceKey方法获取表达式中的值
        String[] fieldNames = new String[fieldRow.getTableCells().size()];
        for (int i = 0; i < fieldRow.getTableCells().size(); i++) {
            XWPFTableCell cell = fieldRow.getTableCells().get(i);
            String text = cell.getText();
            String replaceKey = getReplaceKey(text);
            //获取表达式中的值
            if (StringUtils.isEmpty(replaceKey)) {
                //设置单元格为空值
                replaceKey = "";
            }
            fieldNames[i] = replaceKey;
        }
            //读取最后行每个单元格的值,调用getReplaceKey方法获取表达式中的值
            String[] fieldNames = new String[fieldRow.getTableCells().size()];
            for (int i = 0; i < fieldRow.getTableCells().size(); i++) {
                XWPFTableCell cell = fieldRow.getTableCells().get(i);
                String text = cell.getText();
                String replaceKey = getReplaceKey(text);
                //获取表达式中的值
                if (StringUtils.isEmpty(replaceKey)) {
                    //设置单元格为空值
                    replaceKey = "";
                }
                fieldNames[i] = replaceKey;
            }
        if (subTableData != null && subTableData.size() > 0) {
            for (int i = 0; i < subTableData.size(); i++) {
                Map<String, Object> map = subTableData.get(i);
                XWPFTableRow row;
                if (i == 0) {
                    row = fieldRow;
                } else {
                    int fieldRowIndex = xwpfTable.getRows().indexOf(fieldRow);
                    //创建一行在fieldRowIndex下面
                    row = xwpfTable.insertNewTableRow(fieldRowIndex + i);
                    PrintPoiUtil.copyTableRow(row, fieldRow);
                }
                //遍历字段每个字段创建一个单元格
                for (int j = 0; j < fieldNames.length; j++) {
                    //当前单元格
                    XWPFTableCell cell = row.getCell(j);
                    //清空单元格内容
                    XWPFParagraph xwpfParagraph = cell.addParagraph();
                    int index = cell.getParagraphs().indexOf(xwpfParagraph);
                    PrintPoiUtil.copyParagraph(xwpfParagraph, cell.getParagraphs().get(0));
                    //设置段落文字对齐方式
                    xwpfParagraph.setAlignment(cell.getParagraphs().get(0).getAlignment());
                    //删除所有的run
                    for (int k = xwpfParagraph.getRuns().size() - 1; k >= 0; k--) {
                        xwpfParagraph.removeRun(k);
                    }
                    XWPFRun xwpfRun = xwpfParagraph.createRun();
                    PrintPoiUtil.copyRun(xwpfRun, cell.getParagraphs().get(0).getRuns().get(0));
                    //删除cell中的段落但排除新增的段落
                    for (int k = cell.getParagraphs().size() - 1; k >= 0; k--) {
                        if (k < index) {
                            cell.removeParagraph(k);
                        }
                    }
                    //判断是否是序号列
                    if (indexKey.equals(fieldNames[j])) {
                        cell.setText(String.valueOf(i + 1));
                        continue;
                    }
                    Object value = map.get(fieldNames[j]);
                    if (value == null) {
                        value = "";
                    }
//                    if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldNames[j])) {
//                        //将流程节点处理人加到意见栏中
//                        value = "[" + map.get("actual_person") + "] " + value;
//                    }
                    xwpfRun.setText(value.toString(), 0);
                    if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldNames[j])) {
                        if (!StringUtils.isEmpty(map.get("sign_attachment_uuid"))) {
                            //意见框 插入签名图片到单元格右下角位置
                            xwpfParagraph = cell.addParagraph();
                            xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT);
                            XWPFRun run = xwpfParagraph.createRun();
                            String signAttachmentBase64 = map.get("sign_attachment_uuid").toString();
                            //将base64转换字节流
                            byte[] bytes = Base64.getDecoder().decode(signAttachmentBase64.split(",")[1]);
                            //将字节流转换为输入流
                            InputStream inputStream = new ByteArrayInputStream(bytes);
                            try {
                                //换行插入图片
                                run.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, "sign.png", Units.toEMU(50), Units.toEMU(20));
                                run.addBreak();
                                run.setText("" + map.get("deal_time").toString().substring(0, 16));
                                inputStream.close();
                            } catch (InvalidFormatException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            xwpfParagraph = cell.addParagraph();
                            xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT);
                            XWPFRun run = xwpfParagraph.createRun();
                            run.setText((String) map.get("actual_person"));
                            run.addBreak();
                            run.setText(map.get("deal_time").toString().substring(0, 16));
                        }
            if (subTableData != null && subTableData.size() > 0) {
                int rowCount;
                for (int i = 0, count = 0; i < subTableData.size(); i += rowCount, count++) {
                    rowCount = 1;
                    Map<String, Object> map = subTableData.get(i);
                    XWPFTableRow row;
                    if (i == 0) {
                        row = fieldRow;
                    } else {
                        int fieldRowIndex = xwpfTable.getRows().indexOf(fieldRow);
                        //创建一行在fieldRowIndex下面
                        row = xwpfTable.insertNewTableRow(fieldRowIndex + count);
                        PrintPoiUtil.copyTableRow(row, fieldRow);
                    }
                    //遍历字段每个字段创建一个单元格
                    for (int j = 0; j < fieldNames.length; j++) {
                        String fieldName = fieldNames[j];
                        if (fieldName.contains(" ")) {
                            int curIndex = Integer.parseInt(fieldName.substring(fieldName.indexOf(" ") + 1));
                            if (curIndex > 1 && curIndex > rowCount) {
                                map = i + 1 == subTableData.size() ? Maps.newHashMap() : subTableData.get(i + 1);
                                rowCount = curIndex;
                            }
                            fieldName = fieldName.substring(0, fieldName.indexOf(" "));
                        }
                        //当前单元格
                        XWPFTableCell cell = row.getCell(j);
                        //清空单元格内容
                        XWPFParagraph xwpfParagraph = cell.addParagraph();
                        int index = cell.getParagraphs().indexOf(xwpfParagraph);
                        PrintPoiUtil.copyParagraph(xwpfParagraph, cell.getParagraphs().get(0));
                        //设置段落文字对齐方式
                        xwpfParagraph.setAlignment(cell.getParagraphs().get(0).getAlignment());
                        //删除所有的run
                        for (int k = xwpfParagraph.getRuns().size() - 1; k >= 0; k--) {
                            xwpfParagraph.removeRun(k);
                        }
                        XWPFRun xwpfRun = xwpfParagraph.createRun();
                        PrintPoiUtil.copyRun(xwpfRun, cell.getParagraphs().get(0).getRuns().get(0));
                        //删除cell中的段落但排除新增的段落
                        for (int k = cell.getParagraphs().size() - 1; k >= 0; k--) {
                            if (k < index) {
                                cell.removeParagraph(k);
                            }
                        }
                        //判断是否是序号列
                        if (indexKey.equals(fieldName)) {
                            if (map.isEmpty()) {
                                xwpfRun.setText("", 0);
                            } else {
                                xwpfRun.setText(String.valueOf(i + rowCount), 0);
                            }
                            continue;
                        }
                        Object value = map.get(fieldName);
                        if (value == null) {
                            value = "";
                        }
                        xwpfRun.setText(value.toString(), 0);
                        if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldName)) {
                            if (!StringUtils.isEmpty(map.get("sign_attachment_uuid"))) {
                                //意见框 插入签名图片到单元格右下角位置
                                xwpfParagraph = cell.addParagraph();
                                xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT);
                                XWPFRun run = xwpfParagraph.createRun();
                                String signAttachmentBase64 = map.get("sign_attachment_uuid").toString();
                                //将base64转换字节流
                                byte[] bytes = Base64.getDecoder().decode(signAttachmentBase64.split(",")[1]);
                                //将字节流转换为输入流
                                InputStream inputStream = new ByteArrayInputStream(bytes);
                                try {
                                    //换行插入图片
                                    run.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, "sign.png", Units.toEMU(50), Units.toEMU(20));
                                    run.addBreak();
                                    run.setText("" + map.get("deal_time").toString().substring(0, 16));
                                    inputStream.close();
                                } catch (InvalidFormatException e) {
                                    e.printStackTrace();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                xwpfParagraph = cell.addParagraph();
                                xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT);
                                XWPFRun run = xwpfParagraph.createRun();
                                run.setText((String) map.get("actual_person"));
                                run.addBreak();
                                run.setText(map.get("deal_time").toString().substring(0, 16));
                            }
                    }
                        }
                }
            }
        } else {
                    }
                }
            } else {
//            删除fieldRow
            int index = xwpfTable.getRows().indexOf(fieldRow);
            xwpfTable.removeRow(index);
        }
        //删除起始行
        xwpfTable.removeRow(startRowIndex);
                int index = xwpfTable.getRows().indexOf(fieldRow);
                xwpfTable.removeRow(index);
            }
            //删除起始行
            xwpfTable.removeRow(startRowIndex);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**