SpringBoot使用Pio-tl动态填写合同(文档)

2023-11-20

poi-tl(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档。 poi-tl官方网址

项目中有需求需要动态填充交易合同,因此想到了使用poi-tl技术来实现

一、引入依赖
 <!-- 生成word并且导出 -->
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.5.0</version>
        </dependency>
        <!--  上面需要的依赖-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.9.1</version>
        </dependency>
二、填充合同
1、金额转换工具类
public class MoneyUtils {

    private static final String UNIT = "万千佰拾亿千佰拾万千佰拾元角分";
    private static final String DIGIT = "零壹贰叁肆伍陆柒捌玖";
    private static final double MAX_VALUE = 9999999999999.99D;
    public static String change(double v) {
        if (v < 0 || v > MAX_VALUE){
            return "参数非法!";
        }
        long l = Math.round(v * 100);
        if (l == 0){
            return "零元整";
        }
        String strValue = l + "";
        // i用来控制数
        int i = 0;
        // j用来控制单位
        int j = UNIT.length() - strValue.length();
        String rs = "";
        boolean isZero = false;
        for (; i < strValue.length(); i++, j++) {
            char ch = strValue.charAt(i);
            if (ch == '0') {
                isZero = true;
                if (UNIT.charAt(j) == '亿' || UNIT.charAt(j) == '万' || UNIT.charAt(j) == '元') {
                    rs = rs + UNIT.charAt(j);
                    isZero = false;
                }
            } else {
                if (isZero) {
                    rs = rs + "零";
                    isZero = false;
                }
                rs = rs + DIGIT.charAt(ch - '0') + UNIT.charAt(j);
            }
        }
        if (!rs.endsWith("分")) {
            rs = rs + "整";
        }
        rs = rs.replaceAll("亿万", "亿");
        return rs;
    }

    public static void main(String[] args){
        System.out.println(MoneyUtils.change(12356789.9845));
    }

}
2、电子合同生成相关配置及其配置类
# 电子合同生成相关配置
contract:
  #生产环境合同模板存放路径
  prodTemplateUrl: https://xxxxxx-cn-beijing.aliyuncs.com/templets/64042201/20221116/cd9aeb196edd46aeacc607c18d1c481d/xxxxxx活牛交易市场肉牛买卖合同模板.docx
  #生产环境合同临时存放路径(应用服务器路径)
  prodContractPath: /ekode/offlineFile/
  #测试环境合同模板存放路径
  devTemplateUrl: https://xxxxxx-cn-beijing.aliyuncs.com/templets/64042201/20221116/cd9aeb196edd46aeacc607c18d1c481d/xxxxxx活牛交易市场肉牛买卖合同模板.docx
  #测试环境合同临时存放路径(应用服务器路径)
  devContractPath: C://Users//xxxxxx//Desktop//临时文件//
  #合同名后缀
  fileNameSuffix: 肉牛买卖合同
@Component
@ConfigurationProperties(prefix = "contract")
public class ContractConfig {

    private String prodTemplateUrl;
    private String prodContractPath;
    private String devTemplateUrl;
    private String devContractPath;
    private String fileNameSuffix;


    public String getProdTemplateUrl() {
        return prodTemplateUrl;
    }

    public void setProdTemplateUrl(String prodTemplateUrl) {
        this.prodTemplateUrl = prodTemplateUrl;
    }

    public String getProdContractPath() {
        return prodContractPath;
    }

    public void setProdContractPath(String prodContractPath) {
        this.prodContractPath = prodContractPath;
    }

    public String getDevTemplateUrl() {
        return devTemplateUrl;
    }

    public void setDevTemplateUrl(String devTemplateUrl) {
        this.devTemplateUrl = devTemplateUrl;
    }

    public String getDevContractPath() {
        return devContractPath;
    }

    public void setDevContractPath(String devContractPath) {
        this.devContractPath = devContractPath;
    }

    public String getFileNameSuffix() {
        return fileNameSuffix;
    }

    public void setFileNameSuffix(String fileNameSuffix) {
        this.fileNameSuffix = fileNameSuffix;
    }
}

3、word生成工具类
public class WordUtils {

     private static Logger logger = LoggerFactory.getLogger(WordUtils.class);

    /**
     * @param templatePath 文档模板存放路径
     * @param createFileDir 生成的文档存档地址
     * @param createFileName 生成的文件名(不带格式后缀)
     * @param paramsMap   需要填充的内容
     * @return
     */
    public static String createWord(String templatePath, String createFileDir, String createFileName, Map<String, Object> paramsMap) throws IOException {
        logger.info("【动态填充文档公共方法】被操作文档:{},填充后新文档存放地址:{},填充后新文档名称:{}," +
                "填充字段内容:{}", JSON.toJSONString(templatePath),JSON.toJSONString(createFileDir),
                JSON.toJSONString(createFileName),JSON.toJSONString(paramsMap));
        Assert.notNull(templatePath,"文档模板存放路径不能为空");
        Assert.notNull(createFileDir,"生成的文档存档地址不能为空");
        Assert.notNull(createFileName,"生成的文件名不能为空");
        Assert.notNull(paramsMap,"需要填充的内容不能为空");
        //生成的文件格式
        String formatSuffix = ".docx";
        //拼接后的文件名
        String fileName = createFileName + formatSuffix;
        logger.info("生成合同的文件名(带后缀)为:{}",JSON.toJSONString(fileName));
        //生成文件存放路径
        if(!createFileDir.endsWith("/")){
            createFileDir = createFileDir + File.separator;
        }
        File dir = new File(createFileDir);
        if(!dir.exists()){
            dir.mkdirs();
            logger.info("生成合同时存储文件的路径{}不存在,已自动创建文件夹",createFileDir);
        }
        //生成的文件全路径
        String filePath = createFileDir + fileName;
        logger.info("生成合同的存放路径(绝对路径):{}",JSON.toJSONString(filePath));
        XWPFTemplate template = null;
        //poi-tl使用网络文件作为合同模板,需要转换为文件流动态填写
        if(templatePath.contains("https://xxxxxx-cn-beijing.aliyuncs.com")){
            URL url = new URL(templatePath);
            InputStream in = url.openStream();
            //渲染表格
            HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy();
            //此list是步骤四中动态传入的数据列表
            Configure config = Configure.newBuilder().bind("list", policy).build();
            template = XWPFTemplate.compile(in,config).render(paramsMap);
        }else {
        //hetong muban wei bendi wenjian 
            template = XWPFTemplate.compile(templatePath).render(paramsMap);
        }
        //将填写后的内容写入生成的合同中
        template.writeToFile(filePath);
        template.close();
        logger.info("【生成的电子合同本地存放路径为】:{}",JSON.toJSONString(filePath));
        return filePath;
    }
   }

4、组装合同填充的内容
Map<String,Object> paramsMap = new HashMap<>();
        paramsMap.put("bianHao",bianHao);
        paramsMap.put("jfName",contractDTO.getJfName());
        paramsMap.put("yfName",contractDTO.getYfName());
        paramsMap.put("zongJia",contractDTO.getPrice());
        paramsMap.put("dingJin",contractDTO.getDingJin());
        paramsMap.put("yuKuan",contractDTO.getWeiKuan());
        paramsMap.put("yuKuanJieQingDate",contractDTO.getJiaoFuDateEnd()+"日");
        paramsMap.put("jiaoFuDateStart",contractDTO.getJiaoFuDateStart()+"日");
        paramsMap.put("jiaoFuDateEnd",contractDTO.getJiaoFuDateEnd()+"日");
        paramsMap.put("gaoZhiDay",contractDTO.getGaoZhiDay());
        paramsMap.put("jiaoFuDiDian",contractDTO.getJiaoFuDiDian());
        paramsMap.put("yunShuFangShi",contractDTO.getYunShuFangShi());
        paramsMap.put("jfwyJin",contractDTO.getJfwyJin());
        paramsMap.put("jfwyDay",contractDTO.getJfwyDay());
        //牛只列表
        List<Map<String,Object>> cattleList=new ArrayList<Map<String,Object>>();
        for (int i = 0; i < contractDTO.getCattleLists().size(); i++) {
            Map<String,Object> cattleMap = new HashMap<>();
            cattleMap.put("index",i+1);
            cattleMap.put("eartagNo",contractDTO.getCattleLists().get(i).getEartagNo());
            cattleMap.put("pz",contractDTO.getCattleLists().get(i).getVarieties());
            cattleMap.put("sex",contractDTO.getCattleLists().get(i).getGender());
            cattleMap.put("ms",contractDTO.getCattleLists().get(i).getFulColor());
            cattleMap.put("tz",contractDTO.getCattleLists().get(i).getSecondWeight());
            cattleMap.put("jiaGe",contractDTO.getCattleLists().get(i).getFinalPrice());
            cattleList.add(cattleMap);
        }
         //金额中文大写
        String zongJiaChinese = MoneyUtils.change(Double.valueOf(contractDTO.getPrice()));
        paramsMap.put("rmb",zongJiaChinese);
        paramsMap.put("list",cattleList);
       //根据日期创建文件夹
        String nowDate = DateUtils.dateTimeNow("yyyyMMdd");
        //获取当前运行环境是生产还是测试
        String active = SpringUtils.getActiveProfile();
        String templatePath = null;  //模板存放路径  OSS地址
        String createFileDir = null; //生成的合同临时存放路径
        //生产环境
        if("prod".equals(active)){
            templatePath = contractConfig.getDevTemplateUrl();
            createFileDir = contractConfig.getProdContractPath() + nowDate + "/";
        }
        //测试环境
        if("dev".equals(active)){
            templatePath = contractConfig.getDevTemplateUrl();
            createFileDir = contractConfig.getDevContractPath() + nowDate + "//";
        }
       //生成的合同名
        String createFileName = contractDTO.getJfName() + "_" +
                contractDTO.getYfName() + "_" + nowDate + "_" + contractConfig.getFileNameSuffix();
        logger.info("--------------【开始动态填充电子合同】--------------");
        InputStream is = null;
        String ossPath = null;
        String wordPath = null;
        try {
            wordPath = WordUtils.createWord(templatePath,createFileDir,createFileName,paramsMap);
        } catch (IOException e) {
            logger.error("生成合同异常,异常信息:{}",JSON.toJSONString(e.getMessage()));
            e.printStackTrace();
            return null;
        }
三、效果预览

在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot使用Pio-tl动态填写合同(文档) 的相关文章

  • JNI入门基础

    环境安装 想要使用jni进行ndk开发 我们首先要安装下面这些工具 否则直接从入门到放弃 下载ndk支持 在Android studio中下载上图中框选的两个工具 版本号自己任意选一个 下载完成之后 Android Studio就拥有了进行

随机推荐

  • 类模板函数特化(专用化)specialization of …… after instantiation

    http stackoverflow com questions 7774188 explicit specialization after instantiation http blog csdn net xcysuccess3 arti
  • [ASP.NET MVC 小牛之路]05 - 使用 Ninject

    在 ASP NET MVC 小牛之路 系列上一篇文章 依赖注入 DI 和Ninject 的末尾提到了在ASP NET MVC中使用Ninject要做的两件事情 续这篇文章之后 本文将用一个实际的示例来演示Ninject在ASP NET MV
  • Spring Boot框架

    目录 一 Spring Boot 1 Spring Boot优点 2 创建Spring Boot 3 Spring Boot配置文件 1 配置文件的作用 2 properties文件格式 3 yml文件的格式 4 properties vs
  • 程序员升职记 全关卡攻略&通俗思路 Human Resource Machine

    程序员升职记 全过关方法 通俗思路 博主本着能过就过的思想 写出的解答必然不是最优解 但是可以给大家提供一点思路来参考 其中17和22的解答整理自网络 特别是17的解答 要比博主的原解答巧妙不少 1 收发室 模拟程序输入输出 HUMAN R
  • 深度学习大模型训练--分布式 deepspeed PipeLine Parallelism 源码解析

    deepspeed PipeLine Parallelism 源码解析 basic concept PipeDream abstract 1F1B 4 steps Code comprehension in deepspeed prepar
  • 再论FreeRTOS中的configTOTAL_HEAP_SIZE

    关于任务栈和系统栈的基础知识 可以参考之前的随笔 FreeRTOS 任务栈大小确定及其溢出检测 这里再次说明 define configTOTAL HEAP SIZE size t 17 1024 这个宏 官方文档解释 configTOTA
  • powervm虚拟化分析

    powervm是IBM推出的适用于power系列服务器的虚拟化技术 有其独特的功能和技术 本文和大家一起探讨一下 首先power是ibm处理器的名字 也常常用来标识ibm服务器的型号 常见的power7 power8小型机就是指期cpu是p
  • 启锐 588 打印机每次打印都流出一部分,没有重新切换纸张

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 588 488识别纸张 一 打印机关机 数据线拔掉 二 把纸拿出来 开机之后盖上盖子 三 然后把纸从机器后面塞进去让机器自动吸纸 四 然后长按打印机上面蓝色的按键 听到滴的
  • Java基础之随机生成数字和字母

    原文地址 http blog csdn net yaodong y article details 8115250 字母与数字的ASCII码 目 前计算机中用得最广泛的 字符集及其编码 是由美国国家标准局 ANSI 制定的ASCII码 Am
  • OpenGL视图变换及gluLookAt

    视图变换 即相机变换 其作用是把相机放在指定位置并使其对准场景 该变换是针对相机的变换 不会影响到模型 视图变换决定了相机的位置与方向 因此可以通过视图变换来改变相机位置与方向 从而达到从各个不同的位置与角度来观察同一个物体的情形 进行视图
  • SHAP显示原始特征

    1 问题描述 SHAP用于特征解释 对于机器学习方法往往需要对原始特征进行编码 而SHAP在绘制单个样本时 会显示每个特征及其取值 而这个取值已经是编码后的 通常无法确定其含义 如 下图所示的拍卖公司 城市和作者信息 预期达到的效果 2 实
  • 【西瓜书】4-决策树

    文章目录 4 1 基本流程 4 2 划分 4 2 1 信息增益 ID3 4 2 2 信息增益率 C 45 4 2 3 基尼指数 CART 4 3 剪枝处理 4 4 连续与缺失值 4 4 2 连续值处理 4 4 1 缺失值处理 4 5 多变量
  • Anchor是什么?

    1 选择性搜索 Selective Search 先介绍一下传统的人脸识别算法 是怎么检测出图片中的人脸的 以下图为例 如果我们要检测图中小女孩的人脸位置 一个比较简单暴力的方法就是滑窗 我们使用不同大小 不同长宽比的候选框在整幅图像上进行
  • crmeb重新安装_手动安装教程 · CRMEB 知识付费版 帮助文档 · 看云

    手动安装 1 创建数据库 倒入数据库文件 数据库文件目录 public install zhishifufei sql 2 修改数据库连接文件 配置文件路径 application database php 数据库类型 type gt my
  • vagrant启动openshift

    1 Install Vagrant 2 Install VirtualBox Ex yum install VirtualBox from the RPM Fusion repository 3 In your bashrc file or
  • 元胞自动机算法汇总含matlab代码_数学建模(十三)

    元胞自动机理论 许多复杂的问题都可以通过元胞自动机来建立模型 元胞自动机实质上是定义在一个具有离散 有限状态的元胞组成的元胞空间上 并按照一定的局部规则 在离散的时间维度上演化的动力学系统 元胞又可称为单元 细胞 是元胞自动机的最基本的组成
  • 【hortonworks/registry】registry 如何添加新的类型 支持 json

    1 概述 hortonworks registry 支持json 但是要自己扩展 有相关接口 支持基本类型 支持自定义对象类型 支持集合类型 map array null 支持嵌套结构 registry支持的数据类型有好几种 其中有Avro
  • STM32F103C8T6+PWM+DMA驱动 WS2812灯带

    STM32 PWM DMA驱动 WS2812灯带 文章目录 1 理论 2代码 理论 1 WS2812参考数据手册 https wenku baidu com view 0925958fba68a98271fe910ef12d2af90342
  • 基于Matlab卡尔曼滤波的IMU和GPS组合导航数据融合(附上源码+数据)

    本文介绍了如何使用Matlab实现惯性测量单元 IMU 和全球定位系统 GPS 组合导航数据融合的卡尔曼滤波算法 通过将IMU和GPS的测量数据进行融合 可以提高导航系统的精度和鲁棒性 我们将详细介绍卡尔曼滤波的原理和实现步骤 并给出源码
  • SpringBoot使用Pio-tl动态填写合同(文档)

    poi tl poi template language 是Word模板引擎 使用Word模板和数据创建很棒的Word文档 poi tl官方网址 项目中有需求需要动态填充交易合同 因此想到了使用poi tl技术来实现 一 引入依赖