许鹏程
2023-06-29 69da583c3a1ebb923c5023c5e2b42f094ccb1b53
poi、easyexcel、poi-tl升级 ,合并空调中的报表、数据源模块
已添加1个文件
已修改6个文件
已删除1个文件
1024 ■■■■ 文件已修改
pom.xml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/config/CmnCode.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/config/CmnConst.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/controller/PrintRealizeController.java 102 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/service/HackLoopTableRenderPolicy.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/service/PrintRealizeService.java 506 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/service/ide/IPrintRealizeService.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/print/util/DynamicTableRenderPolicy.java 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -33,47 +33,10 @@
            <groupId>com.lx</groupId>
            <artifactId>product-server-admin</artifactId>
        </dependency>
        <!--        <dependency>-->
        <!--            <groupId>org.apache.poi</groupId>-->
        <!--            <artifactId>poi</artifactId>-->
        <!--            <version>5.1.0</version>-->
        <!--        </dependency>-->
        <!--        &lt;!&ndash; https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml &ndash;&gt;-->
        <!--        <dependency>-->
        <!--            <groupId>org.apache.poi</groupId>-->
        <!--            <artifactId>poi-ooxml</artifactId>-->
        <!--            <version>5.1.0</version>-->
        <!--        </dependency>-->
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.4.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml-schemas</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
            <version>1.10.5</version>
        </dependency>
    </dependencies>
</project>
src/main/java/com/product/print/config/CmnCode.java
@@ -24,6 +24,12 @@
    PRINT_CONFIG_NOT_EXIST("模板配置不存在", 11),
    //动态表格设置错误
    DYNAMIC_TABLE_SETTING_ERROR("动态表格设置错误", 12),
    //替换模板内容错误
    REPLACE_TEMPLATE_CONTENT_ERROR("替换模板内容错误", 13),
    //转换pdf错误
    CONVERT_PDF_ERROR("转换pdf错误", 14),
    //格中没有找到结束标识{{$~end~}}
    NOT_FIND_END_FLAG("格中没有找到结束标识{{$~end~}}", 15),
    ;
    private String text;
src/main/java/com/product/print/config/CmnConst.java
@@ -1,32 +1,30 @@
package com.product.print.config;
import com.product.core.config.CoreConst;
/**
 * @ClassName CmnConst
 * @Description æ‰“印模块常量
 * @Author cheng
 * @Date 2021/11/30 15:39
 */
public class CmnConst {
public class CmnConst extends CoreConst {
    public static final String CPAGE = "cpage"; // é¡µæ•°
    public static final String PAGESIZE = "pagesize"; // æ¯é¡µæ¡æ•°
    public static final String UUID = "uuid";
    public static final String PRINT_TEMP = "print_temp";
    public static final String PRINT_TEMPLATE = "print_template";
    //打印配置表
    public static final String TABLE_PRINT_CONFIG = "product_sys_print_config";
    public static final String PRINT_TEMP = "print_temp";
    public static final String PRINT_TEMPLATE = "print_template";
    //打印配置表
    public static final String TABLE_PRINT_CONFIG = "product_sys_print_config";
    public static final String PRINT_NAME="print_name";
    public static final String PRINT_FILE_NAME="print_file_name";
    public static final String DICT_VALUE="dict_value";
    public static final String DICT_LABEL="dict_label";
    public static final String FIELD_REFERECE="field_reference";
    public static final String _SAVE_VALUE="_save_value";
    public static final String PRINT_FONT="Wingdings 2";
    public static final String PRINT_CHECKED_CHAR="☑";
    public static final String PRINT_UNCHECKED_CHAR="□";
    public static final String PRINT_NAME = "print_name";
    public static final String PRINT_FILE_NAME = "print_file_name";
    public static final String DICT_VALUE = "dict_value";
    public static final String DICT_LABEL = "dict_label";
    public static final String FIELD_REFERECE = "field_reference";
    public static final String _SAVE_VALUE = "_save_value";
    public static final String PRINT_FONT = "Wingdings 2";
    public static final String PRINT_CHECKED_CHAR = "☑";
    public static final String PRINT_UNCHECKED_CHAR = "□";
}
src/main/java/com/product/print/controller/PrintRealizeController.java
@@ -28,63 +28,63 @@
@RequestMapping("/api/print/realize")
public class PrintRealizeController extends AbstractBaseController {
    @Autowired
    IPrintRealizeService printRealizeService;
    @Autowired
    IPrintRealizeService printRealizeService;
    @PostMapping("/print/{version}")
    @ApiVersion(1)
    public String print(HttpServletRequest request, HttpServletResponse response) {
        try {
            FieldSetEntity fse = null;
            Object bean = request.getAttribute(CoreConst.API_POST_REQUEST_DATA);
            if (bean != null) {
                RequestParameterEntity reqp = (RequestParameterEntity) bean;
                fse = reqp.getFormData();
            }
            if (bean == null || fse == null) {
                return this.error(CmnCode.SYSTEM_FORM_NODATA);
            }
    @PostMapping("/print/{version}")
    @ApiVersion(1)
    public String print(HttpServletRequest request, HttpServletResponse response) {
        try {
            FieldSetEntity fse = null;
            Object bean = request.getAttribute(CoreConst.API_POST_REQUEST_DATA);
            if (bean != null) {
                RequestParameterEntity reqp = (RequestParameterEntity) bean;
                fse = reqp.getFormData();
            }
            if (bean == null || fse == null) {
                return this.error(CmnCode.SYSTEM_FORM_NODATA);
            }
//            if (!CmnConst.TABLE_PRINT_CONFIG.equals(fse.getTableName())) {
//                return error(CmnCode.SYSTEM_TABLE_NODATA);
//            }
            if (StringUtils.isEmpty(CmnConst.PRINT_TEMP)) {
                return error(CmnCode.SYSTEM_FORM_COUNT);
            }
            printRealizeService.print(fse, response);
            return OK();
        } catch (BaseException e) {
            return error(e);
        } catch (Exception e) {
            return error(CmnCode.GET_PRINT_CONFIG_LIST_FAIL, e);
        }
    }
    @PostMapping("/printWord/{version}")
    @ApiVersion(1)
    public String printWord(HttpServletRequest request, HttpServletResponse response) {
        try {
            FieldSetEntity fse = null;
            Object bean = request.getAttribute(CoreConst.API_POST_REQUEST_DATA);
            if (bean != null) {
                RequestParameterEntity reqp = (RequestParameterEntity) bean;
                fse = reqp.getFormData();
            }
            if (bean == null || fse == null) {
                return this.error(CmnCode.SYSTEM_FORM_NODATA);
            }
            if (StringUtils.isEmpty(CmnConst.PRINT_TEMP)) {
                return error(CmnCode.SYSTEM_FORM_COUNT);
            }
            printRealizeService.print(fse, response, true);
            return OK();
        } catch (BaseException e) {
            return error(e);
        } catch (Exception e) {
            return error(CmnCode.GET_PRINT_CONFIG_LIST_FAIL, e);
        }
    }
    @PostMapping("/printWord/{version}")
    @ApiVersion(1)
    public String printWord(HttpServletRequest request, HttpServletResponse response) {
        try {
            FieldSetEntity fse = null;
            Object bean = request.getAttribute(CoreConst.API_POST_REQUEST_DATA);
            if (bean != null) {
                RequestParameterEntity reqp = (RequestParameterEntity) bean;
                fse = reqp.getFormData();
            }
            if (bean == null || fse == null) {
                return this.error(CmnCode.SYSTEM_FORM_NODATA);
            }
//            if (!CmnConst.TABLE_PRINT_CONFIG.equals(fse.getTableName())) {
//                return error(CmnCode.SYSTEM_TABLE_NODATA);
//            }
            if (StringUtils.isEmpty(CmnConst.PRINT_TEMP)) {
                return error(CmnCode.SYSTEM_FORM_COUNT);
            }
            printRealizeService.printWord(fse, response);
            return OK();
        } catch (BaseException e) {
            return error(e);
        } catch (Exception e) {
            return error(CmnCode.GET_PRINT_CONFIG_LIST_FAIL, e);
        }
    }
            if (StringUtils.isEmpty(CmnConst.PRINT_TEMP)) {
                return error(CmnCode.SYSTEM_FORM_COUNT);
            }
            printRealizeService.print(fse, response, false);
            return OK();
        } catch (BaseException e) {
            return error(e);
        } catch (Exception e) {
            return error(CmnCode.GET_PRINT_CONFIG_LIST_FAIL, e);
        }
    }
}
src/main/java/com/product/print/service/HackLoopTableRenderPolicy.java
ÎļþÒÑɾ³ý
src/main/java/com/product/print/service/PrintRealizeService.java
@@ -1,7 +1,11 @@
package com.product.print.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.net.URLEncodeUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.TextRenderData;
import com.deepoove.poi.data.style.Style;
import com.product.common.lang.StringUtils;
@@ -16,16 +20,14 @@
import com.product.print.config.CmnCode;
import com.product.print.config.CmnConst;
import com.product.print.service.ide.IPrintRealizeService;
import com.product.print.util.DynamicTableRenderPolicy;
import com.product.util.BaseUtil;
import com.product.util.SystemParamReplace;
import com.product.util.config.SystemParamSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
@@ -40,300 +42,220 @@
public class PrintRealizeService extends AbstractBaseService implements IPrintRealizeService {
    @Autowired
    FileManagerService fileManagerService;
/*
    public static void main(String[] args) {
        Map<String, Object> t = new HashMap<>();
        Integer a = 1;
        t.put("test", 1);
        t.put("test2", true);
        t.put("test3", 0.11);
        Map<String, String> cc = (Map<String, String>) (Map) t;
        System.out.println(cc);
    }
*/
/*
    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new HashMap<>();
        List<Map<String, String>> subMapList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Map<String, String> subMap = new HashMap<>();
            subMap.put("a1", "品名——" + (i + 1));
            subMap.put("a2", "单位——" + (i + 1));
            subMap.put("a3", "数量——" + (i + 1));
            subMap.put("a4", "用途——" + (i + 1));
            subMapList.add(subMap);
        }
        map.put("BGYPLYB_SUB", subMapList);
        HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy();//创建一个列表的规则
        Configure config = Configure.newBuilder().customPolicy("BGYPLYB_SUB", policy).customPolicy("test_table", policy).build();
        File targetFile = new File("C:\\Users\\cheng\\Desktop\\replaceWord.docx");
        File file = new File("C:\\Users\\cheng\\Desktop\\办公用品领用表.docx");
        if (targetFile.exists()) {
            targetFile.delete();
        }
        targetFile.createNewFile();
        try (FileOutputStream is = new FileOutputStream(targetFile);
        ) {
            XWPFTemplate render = XWPFTemplate.compile(file, config).render(map);
            render.write(is);
            render.close();
        }
    }
*/
    /**
     *     word打印复选框处理
     * @param fs
     */
    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));
        //获取表单字段
        Object[] fields = fse.getMeta().getFields();
        if (fields != null) {
            for(int i = 0; i < fields.length; ++i) {
                //获取保存的数据值
                String dataSaveValue = fse.getString(fields[i] + CmnConst._SAVE_VALUE);
                if (!BaseUtil.strIsNull(dataSaveValue)) {
                    //获取每个字段的meta信息
                    FieldSetEntity meta = fse.getMeta().getFieldMeta(fields[i].toString());
                    if (meta != null && meta.getString(CmnConst.FIELD_REFERECE) != null && meta.getString(CmnConst.FIELD_REFERECE).indexOf("《")>-1) {
                        //数据对应参照信息
                        DataTableEntity dictInfos = getMetaAndCacheDictInfo(meta);
                        if (!BaseUtil.dataTableIsEmpty(dictInfos)) {
                            for (int j = 0; j < dictInfos.getRows(); j++) {
                                //获取每个参照对应值
                                String dict_value = dictInfos.getFieldSetEntity(j).getString(CmnConst.DICT_VALUE);
                                if (dataSaveValue.indexOf(dict_value)>-1) {
                                    fse.setValue(fields[i]+"_" + dict_value, selSymbol);
                                }else {
                                    fse.setValue(fields[i]+"_" + dict_value, unselSymbol);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /**
     *     èŽ·å–å­—æ®µå¯¹åº”æ•°æ®å­—å…¸ä¿¡æ¯
     * @param fieldMate
     * @return
     */
    public static DataTableEntity getMetaAndCacheDictInfo(FieldSetEntity fieldMate) {
        if (fieldMate != null && fieldMate.getString(CmnConst.FIELD_REFERECE) != null) {
            int a = fieldMate.getString(CmnConst.FIELD_REFERECE).indexOf("《");
            int b = fieldMate.getString(CmnConst.FIELD_REFERECE).indexOf("》");
            if (b > 1 && a == 0) {
                return DataPoolCacheImpl.getInstance().getCacheData("数据字典配置信息", new String[]{fieldMate.getString(CmnConst.FIELD_REFERECE).substring(a + 1, b)});
            }
        }
        return null;
    }
    @Override
    public void printWord(FieldSetEntity fse, HttpServletResponse response) throws BaseException {
        // æ‰“印配置uuid
        String print_uuid = fse.getString("~" + CmnConst.PRINT_TEMP + "~");
        //查询打印配置
        FieldSetEntity fieldSetEntity = getBaseDao().getFieldSetEntity(CmnConst.TABLE_PRINT_CONFIG, print_uuid, false);
        if (fieldSetEntity == null || StringUtils.isEmpty(fieldSetEntity.getString(CmnConst.PRINT_TEMPLATE))) {
            throw new BaseException(CmnCode.PRINT_CONFIG_NOT_EXIST.getValue(), CmnCode.PRINT_CONFIG_NOT_EXIST.getText());
        }
        // æ‰“印模板附件uuid
        String template_uuid = fieldSetEntity.getString(CmnConst.PRINT_TEMPLATE);
        // èŽ·å–æ‰“å°æ¨¡æ¿
        File file = getFile(template_uuid);
        // åŠ è½½å‚ç…§ æ‰“印时使用显示值 è€Œä¸æ˜¯å®žé™…值
        getBaseDao().loadPromptData(fse);
        // æ–‡ä»¶åå‰ç¼€éƒ¨åˆ†
        Object tempKey = UUID.randomUUID();
        // æ›¿æ¢åŽçš„word临时路径
        String localTempPathWord = Global.getSystemConfig("temp.dir", "") + File.separator + "temp_print_" + tempKey + ".docx";
        File wordTemp = new File(localTempPathWord);
        try {
            new File(Global.getSystemConfig("temp.dir", "")).mkdirs();
            wordTemp.createNewFile();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(CmnCode.PRINT_CONTENT_FAIL.getValue(), CmnCode.PRINT_CONTENT_FAIL.getText() + (e.getMessage() != null ? e.getMessage() : ""));
        }
        //将模和当前fse的values放入进行替换
        try (FileOutputStream is = new FileOutputStream(wordTemp);
             OutputStream out = response.getOutputStream()) {
            Map<String, DataTableEntity> subData = fse.getSubData();
    @Autowired
    FileManagerService fileManagerService;
            Configure.ConfigureBuilder configureBuilder = null;
            if (subData != null && subData.size() > 0) {
                for (Map.Entry<String, DataTableEntity> vv :
                        subData.entrySet()) {
                    getBaseDao().loadPromptData(vv.getValue());
                    if (!DataTableEntity.isEmpty(vv.getValue())) {
                        List<Map> collect = vv.getValue().getData().stream().map(item -> (Map<String, String>) ((Map) item.getValues())).collect(Collectors.toList());
                        fse.setValue(vv.getKey(), collect);
                    } else {
                        fse.setValue(vv.getKey(), new ArrayList<>());
                    }
                    if (configureBuilder == null) {
                        configureBuilder = Configure.newBuilder();
                    }
                    configureBuilder.customPolicy(vv.getKey(), new HackLoopTableRenderPolicy());
                }
            }
            //复选框处理
            dataConvertCheckedData(fse);
            XWPFTemplate render = XWPFTemplate.compile(file, configureBuilder == null ? Configure.createDefault() : configureBuilder.build()).render(fse.getValues());
            render.write(is);
            render.close();
            response.setContentType("multipart/form-data");
            response.setHeader("Access-Control-Allow-Origin", "*"); // å…è®¸æ‰€æœ‰
            //设置响应
            response.setContentType("application/octet-stream;charset=UTF-8");
            // å°†å“åº”头中的Content-Disposition暴露出来,不然前端获取不到
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            String fileName = null;
            if(!BaseUtil.strIsNull(fieldSetEntity.getString(CmnConst.PRINT_FILE_NAME))) {
                fileName = SystemParamReplace.replaceParams(fieldSetEntity.getString(CmnConst.PRINT_FILE_NAME), fse);
            }else {
                fileName = fieldSetEntity.getString(CmnConst.PRINT_NAME);
    /**
     * word打印复选框处理
     *
     * @param fse
     */
    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));
        //获取表单字段
        Object[] fields = fse.getMeta().getFields();
        if (fields == null) {
            return;
        }
        for (int i = 0; i < fields.length; ++i) {
            //获取保存的数据值
            String dataSaveValue = fse.getString(fields[i] + CmnConst._SAVE_VALUE);
            if (BaseUtil.strIsNull(dataSaveValue)) {
                continue;
            }
            fileName+=".docx";
            // åœ¨å“åº”头中的Content-Disposition里设置文件名称
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            InputStream isPdf = new FileInputStream(wordTemp);
            int len;
            byte[] b = new byte[1024];
            // è¾“出
            while ((len = isPdf.read(b)) > 0) {
                out.write(b, 0, len);
            }
            isPdf.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(CmnCode.PRINT_CONTENT_FAIL.getValue(), CmnCode.PRINT_CONTENT_FAIL.getText() + (e.getMessage() != null ? e.getMessage() : ""));
        } finally {
            //删除word临时文件
            if (wordTemp.exists()) {
                System.out.println(wordTemp.getPath());
//                wordTemp.delete();
            }
            if (file != null) {
                file.delete();
            }
        }
            //获取每个字段的meta信息
            FieldSetEntity meta = fse.getMeta().getFieldMeta(fields[i].toString());
            if (meta == null || meta.getString(CmnConst.FIELD_REFERECE) == null || meta.getString(CmnConst.FIELD_REFERECE).indexOf("《") == -1) {
                continue;
            }
            //数据对应参照信息
            DataTableEntity dictInfos = getMetaAndCacheDictInfo(meta);
            if (BaseUtil.dataTableIsEmpty(dictInfos)) {
                continue;
            }
            for (int j = 0; j < dictInfos.getRows(); j++) {
                //获取每个参照对应值
                String dict_value = dictInfos.getFieldSetEntity(j).getString(CmnConst.DICT_VALUE);
                if (dataSaveValue.indexOf(dict_value) > -1) {
                    fse.setValue(fields[i] + "_" + dict_value, selSymbol);
                    continue;
                }
                fse.setValue(fields[i] + "_" + dict_value, unselSymbol);
            }
        }
    }
    }
    @Override
    public void print(FieldSetEntity fse, HttpServletResponse response) throws BaseException {
        // æ‰“印配置uuid
        String print_uuid = fse.getString("~" + CmnConst.PRINT_TEMP + "~");
        //查询打印配置
        FieldSetEntity fieldSetEntity = getBaseDao().getFieldSetEntity(CmnConst.TABLE_PRINT_CONFIG, print_uuid, false);
        if (fieldSetEntity == null || StringUtils.isEmpty(fieldSetEntity.getString(CmnConst.PRINT_TEMPLATE))) {
            throw new BaseException(CmnCode.PRINT_CONFIG_NOT_EXIST.getValue(), CmnCode.PRINT_CONFIG_NOT_EXIST.getText());
        }
        // æ‰“印模板附件uuid
        String template_uuid = fieldSetEntity.getString(CmnConst.PRINT_TEMPLATE);
        // èŽ·å–æ‰“å°æ¨¡æ¿
        File file = getFile(template_uuid);
        // åŠ è½½å‚ç…§ æ‰“印时使用显示值 è€Œä¸æ˜¯å®žé™…值
        getBaseDao().loadPromptData(fse);
        // æ–‡ä»¶åå‰ç¼€éƒ¨åˆ†
        Object tempKey = UUID.randomUUID();
        // æ›¿æ¢åŽçš„word临时路径
        String localTempPathWord = Global.getSystemConfig("temp.dir", "") + File.separator + "temp_print_" + tempKey + ".docx";
        // è½¬æ¢pdf的临时路径
        String localTempPathPdf = Global.getSystemConfig("temp.dir", "") + File.separator + "temp_print_" + tempKey + ".pdf";
        File wordTemp = new File(localTempPathWord);
        File pdfTemp = null;
        try {
            new File(Global.getSystemConfig("temp.dir", "")).mkdirs();
            wordTemp.createNewFile();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(CmnCode.PRINT_CONTENT_FAIL.getValue(), CmnCode.PRINT_CONTENT_FAIL.getText() + (e.getMessage() != null ? e.getMessage() : ""));
        }
        //将模和当前fse的values放入进行替换
        try (FileOutputStream is = new FileOutputStream(wordTemp);
             OutputStream out = response.getOutputStream()) {
            Map<String, DataTableEntity> subData = fse.getSubData();
    /**
     * èŽ·å–å­—æ®µå¯¹åº”æ•°æ®å­—å…¸ä¿¡æ¯
     *
     * @param fieldMate
     * @return
     */
    public static DataTableEntity getMetaAndCacheDictInfo(FieldSetEntity fieldMate) {
        if (fieldMate != null && fieldMate.getString(CmnConst.FIELD_REFERECE) != null) {
            int a = fieldMate.getString(CmnConst.FIELD_REFERECE).indexOf("《");
            int b = fieldMate.getString(CmnConst.FIELD_REFERECE).indexOf("》");
            if (b > 1 && a == 0) {
                return DataPoolCacheImpl.getInstance().getCacheData("数据字典配置信息", new String[]{fieldMate.getString(CmnConst.FIELD_REFERECE).substring(a + 1, b)});
            }
        }
        return null;
    }
            Configure.ConfigureBuilder configureBuilder = null;
            if (subData != null && subData.size() > 0) {
                for (Map.Entry<String, DataTableEntity> vv :
                        subData.entrySet()) {
                    getBaseDao().loadPromptData(vv.getValue());
                    if (!DataTableEntity.isEmpty(vv.getValue())) {
                        List<Map> collect = vv.getValue().getData().stream().map(item -> (Map<String, String>) ((Map) item.getValues())).collect(Collectors.toList());
                        fse.setValue(vv.getKey(), collect);
                    } else {
                        fse.setValue(vv.getKey(), new ArrayList<>());
                    }
                    if (configureBuilder == null) {
                        configureBuilder = Configure.newBuilder();
                    }
                    configureBuilder.customPolicy(vv.getKey(), new HackLoopTableRenderPolicy());
                }
            }
            XWPFTemplate render = XWPFTemplate.compile(file, configureBuilder == null ? Configure.createDefault() : configureBuilder.build()).render(fse.getValues());
            render.write(is);
            render.close();
            response.setContentType("multipart/form-data");
            response.setHeader("Content-Disposition", "attachment;");
            // ä½¿ç”¨openOffice è½¬æ¢ä¸ºpdf
            pdfTemp = PdfConcurrenceUtil.convertToPdf(wordTemp.getPath(), localTempPathPdf,"docx");
            InputStream isPdf = new FileInputStream(pdfTemp);
            int len;
            byte[] b = new byte[1024];
            // è¾“出
            while ((len = isPdf.read(b)) > 0) {
                out.write(b, 0, len);
            }
            isPdf.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(CmnCode.PRINT_CONTENT_FAIL.getValue(), CmnCode.PRINT_CONTENT_FAIL.getText() + (e.getMessage() != null ? e.getMessage() : ""));
        } finally {
            // åˆ é™¤ä¸´æ—¶æ–‡ä»¶
            if (pdfTemp != null && pdfTemp.exists()) {
                pdfTemp.delete();
            }
            if (wordTemp.exists()) {
                wordTemp.delete();
            }
            if (file != null) {
                file.delete();
            }
        }
    /**
     * æ‰“印传输pdf流到前端
     *
     * @param fse          æ‰“印数据
     * @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 + "~"));
        //获取到替换后的文件路径 ï¼ˆpdf文件或者word文件)
        String tempPdfFilePath = replaceTemplateFileOut(printConfig, fse, isConvertPdf);
        //获取文件名
        String fileName = BaseUtil.ifNull(printConfig.getString(CmnConst.PRINT_FILE_NAME), printConfig.getString(CmnConst.PRINT_NAME));
        if (isConvertPdf) {
            fileName = fileName + ".pdf";
        } else {
            fileName = fileName + ".docx";
        }
        //设置响应
        response.setContentType("application/octet-stream;charset=UTF-8");
        // å°†å“åº”头中的Content-Disposition暴露出来,不然前端获取不到
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        // è®¾ç½®è‡ªå®šä¹‰å¤´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) {
            e.printStackTrace();
            throw new BaseException(CmnCode.PRINT_CONTENT_FAIL, e);
        } finally {
            //删除pdf临时文件
            FileUtil.del(tempPdfFilePath);
        }
    private File getFile(String template_uuid) throws BaseException {
        try {
            return fileManagerService.getFile(template_uuid);
        } catch (BaseException e) {
            e.printStackTrace();
            //获取打印模板错误
            throw new BaseException(CmnCode.GET_PRINT_TEMPLATE_FILE_FAIL.getValue(), CmnCode.GET_PRINT_TEMPLATE_FILE_FAIL.getText());
        }
    }
    }
    /**
     * èŽ·å–æ‰“å°é…ç½®
     *
     * @param uuid æ‰“印配置uuid
     * @return
     * @throws BaseException
     */
    public FieldSetEntity getPrintConfig(String uuid) throws BaseException {
        //查询打印配置
        FieldSetEntity fieldSetEntity = getBaseDao().getFieldSetEntity(CmnConst.TABLE_PRINT_CONFIG, uuid, false);
        if (fieldSetEntity == null || StringUtils.isEmpty(fieldSetEntity.getString(CmnConst.PRINT_TEMPLATE))) {
            throw new BaseException(CmnCode.PRINT_CONFIG_NOT_EXIST.getValue(), CmnCode.PRINT_CONFIG_NOT_EXIST.getText());
        }
        return fieldSetEntity;
    }
    /**
     * æ›¿æ¢æ¨¡æ¿æ–‡ä»¶å¹¶è¾“出
     *
     * @param printConf    æ‰“印配置
     * @param fse          æ›¿æ¢æ•°æ®
     * @param isConvertPdf æ˜¯å¦è½¬æ¢ä¸ºpdf
     * @return æ›¿æ¢åŽçš„æ–‡ä»¶è·¯å¾„
     * @throws BaseException å¼‚常
     */
    private String replaceTemplateFileOut(FieldSetEntity printConf, FieldSetEntity fse, boolean isConvertPdf) throws BaseException {
        // æ‰“印模板附件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();
        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;
    }
    /**
     * æ›¿æ¢word模板
     *
     * @param outPath      è¾“出路径
     * @param templatePath æ¨¡æ¿è·¯å¾„
     * @param dataFse      æ›¿æ¢æ•°æ®
     */
    public static void replaceWord(String outPath, String templatePath, FieldSetEntity dataFse) {
        //转换数据为复选框
        dataConvertCheckedData(dataFse);
        //克隆一份values
        Map<String, Object> cloneValues = new HashMap(dataFse.getValues());
        //获取子表数据
        Map<String, DataTableEntity> subDataMap = dataFse.getSubData();
        ConfigureBuilder config = Configure.createDefault().builder();
        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()));
            }
        }
        try {
            //检查输出文件是否存在,不存在则创建
            FileUtil.touch(outPath);
            System.out.println(cloneValues);
            XWPFTemplate.compile(templatePath, config.build()).render(cloneValues).writeToFile(outPath);
        } catch (Exception e) {
            e.printStackTrace();
            FileUtil.del(outPath);
            throw new BaseException(CmnCode.REPLACE_TEMPLATE_CONTENT_ERROR, e);
        }
    }
    /**
     * èŽ·å–æ¨¡æ¿é™„ä»¶
     *
     * @param templateUid æ¨¡æ¿é™„ä»¶uuid
     * @return
     * @throws BaseException
     */
    private File getTemplateFile(String templateUid) throws BaseException {
        try {
            return fileManagerService.getFile(templateUid);
        } catch (BaseException e) {
            e.printStackTrace();
            //获取打印模板错误
            throw new BaseException(CmnCode.GET_PRINT_TEMPLATE_FILE_FAIL.getValue(), CmnCode.GET_PRINT_TEMPLATE_FILE_FAIL.getText());
        }
    }
}
src/main/java/com/product/print/service/ide/IPrintRealizeService.java
@@ -14,8 +14,13 @@
 */
public interface IPrintRealizeService {
    void printWord(FieldSetEntity fse, HttpServletResponse response) throws BaseException;
    void print(FieldSetEntity fse, HttpServletResponse response) throws BaseException;
    /**
     * æ‰“印
     * @param fse æ‰“印数据
     * @param response  å“åº”
     * @param isConvertPdf  æ˜¯å¦è½¬æ¢ä¸ºpdf
     * @throws BaseException
     */
    void print(FieldSetEntity fse, HttpServletResponse response,boolean isConvertPdf) throws BaseException;
}
src/main/java/com/product/print/util/DynamicTableRenderPolicy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,177 @@
package com.product.print.util;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.render.RenderContext;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.TableTools;
import com.product.common.lang.StringUtils;
import com.product.core.exception.BaseException;
import com.product.print.config.CmnCode;
import org.apache.poi.xwpf.usermodel.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @Author cheng
 * @Date 2023/6/27 15:50
 * @Desc
 */
public class DynamicTableRenderPolicy extends com.deepoove.poi.policy.DynamicTableRenderPolicy {
    private String replaceKey;
    private final String indexKey = "~index~";
    public DynamicTableRenderPolicy(String replaceKey) {
        this.replaceKey = replaceKey;
    }
    private XWPFRun run;
    @Override
    public void doRender(RenderContext<Object> context) throws Exception {
        RunTemplate runTemplate = (RunTemplate) context.getEleTemplate();
        XWPFRun run = runTemplate.getRun();
        try {
            if (!TableTools.isInsideTable(run)) {
                throw new IllegalStateException(
                        "The template tag " + runTemplate.getSource() + " must be inside a table");
            }
            XWPFTableCell cell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
            XWPFTable table = cell.getTableRow().getTable();
            render(table, context.getData());
        } catch (Exception e) {
            throw new RenderException("Dynamic render table error:" + e.getMessage(), e);
        }
    }
    @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中的内容
        //获取最后一行
        XWPFTableRow xwpfTableRow = rows.get(rows.size() - 1);
        String tableExpression = "{{" + this.replaceKey + "}}";
        //获取表格起始行和结束行
        int startRowIndex = -1;
        int endRowIndex = -1;
        rows:
        for (int i = 0; i < rows.size(); i++) {
            XWPFTableRow row = rows.get(i);
            for (int j = 0; j < row.getTableCells().size(); j++) {
                if (startRowIndex > -1 && endRowIndex > -1) {
                    break rows;
                }
                XWPFTableCell cell = row.getTableCells().get(j);
                String text = cell.getText();
                if (tableExpression.equals(text)) {
                    startRowIndex = i;
                }
                if ("{{$~end~}}".equals(text)) {
                    endRowIndex = i;
                }
            }
        }
        //表格起始行不在第一行且没有找到结束标记
        if (startRowIndex > 0 && endRowIndex == -1) {
            throw new BaseException(CmnCode.NOT_FIND_END_FLAG);
        }
        if (startRowIndex == 0 && endRowIndex > 0) {
            //删除结束标记所在行
            xwpfTable.removeRow(endRowIndex);
        }
        //读取最后行每个单元格的值,调用getReplaceKey方法获取表达式中的值
        String[] fieldNames = new String[xwpfTableRow.getTableCells().size()];
//        for (int i = 0; i < xwpfTableRow.getTableCells().size(); i++) {
//            XWPFTableCell cell = xwpfTableRow.getTableCells().get(i);
//            String text = cell.getText();
//            String replaceKey = getReplaceKey(text);
//            //获取表达式中的值
//            if (StringUtils.isEmpty(replaceKey)) {
//                //设置单元格为空值
//                replaceKey = "";
//            }
//            fieldNames[i] = replaceKey;
//        }
//        //读取完毕后删除最后一行
//        xwpfTable.removeRow(rows.size() - 1);
        //获取所有的行判断单元格是否有值没有就删除该行
//        List<XWPFTableRow> tableRows = xwpfTable.getRows();
//        List<XWPFTableRow> deleteRows = new ArrayList<>();
//        for (int i = 0; i < tableRows.size(); i++) {
//            XWPFTableRow row = tableRows.get(i);
//            boolean flag = false;
//            for (int j = 0; j < row.getTableCells().size(); j++) {
//                XWPFTableCell cell = row.getTableCells().get(j);
//                if (StringUtils.isNotEmpty(cell.getText())) {
//                    flag = true;
//                    break;
//                }
//            }
//            if (!flag) {
//                deleteRows.add(row);
//            }
//        }
//        //遍历要删除的行获取所在的下标进行删除
//        for (int i = 0; i < deleteRows.size(); i++) {
//            XWPFTableRow row = deleteRows.get(i);
//            int index = xwpfTable.getRows().indexOf(row);
//            xwpfTable.removeRow(index);
//        }
//        //遍历数据集合,每个map对应一行数据
//        for (int i = 0; i < subTableData.size(); i++) {
//            Map<String, Object> map = subTableData.get(i);
//            //创建一行
//            XWPFTableRow row = xwpfTable.createRow();
//            //遍历字段每个字段创建一个单元格
//            for (int j = 0; j < fieldNames.length; j++) {
//                //当前单元格
//                XWPFTableCell cell;
//                //判断row中第j个单元格是否存在
//                if (row.getTableCells().size() > j) {
//                    cell = row.getTableCells().get(j);
//                } else {
//                    cell = row.createCell();
//                }
//                //设置单元格的值从map中取出
//                //判断是否是序号列
//                if (indexKey.equals(fieldNames[j])) {
//                    cell.setText(String.valueOf(i + 1));
//                    continue;
//                }
//                Object value = map.get(fieldNames[j]);
//                if (value == null) {
//                    value = "";
//                }
//                //设置单元格的值
//                cell.setText(value.toString());
//            }
//        }
    }
    /**
     * è§£æžè¡¨è¾¾å¼ä¸­çš„值获取 ä»¥{{$开头,以}}结尾的字符串取出$后面的值截止到}}
     */
    private String getReplaceKey(String text) {
        //使用正则解析表达式中的值获取 ä»¥{{$开头,以}}结尾的字符串取出$后面的值截止到}}
        Pattern pattern = Pattern.compile("\\{\\{\\$(.*?)\\}\\}");
        Matcher matcher = pattern.matcher(text);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }
}