浏览代码

1.word、excel生成工具类编写

GouGengquan 9 月之前
父节点
当前提交
26ecc76020

+ 0 - 150
biz-base/src/test/java/com/dayou/BaseApplicationTests.java

@@ -2,21 +2,9 @@ package com.dayou;
 
 import cn.hutool.core.lang.Console;
 import cn.hutool.http.HttpRequest;
-import com.alibaba.excel.EasyExcel;
-import com.alibaba.excel.ExcelWriter;
-import com.alibaba.excel.write.metadata.WriteSheet;
-import org.apache.poi.ss.usermodel.*;
-import org.apache.poi.ss.util.CellRangeAddressList;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 
-import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.Map;
-
 @SpringBootTest
 class BaseApplicationTests {
 
@@ -34,142 +22,4 @@ class BaseApplicationTests {
         Console.log(result);
     }
 
-    /**
-     * 测试使用EasyExcel填充值到带公式的excel模板
-     */
-    // @Test
-    void testEasyExcel(){
-        // 模板文件路径
-        String templateFilePath = "E:\\test\\input.xlsx";
-        // 输出文件路径
-        String outputPath = "E:\\test\\output.xlsx";
-
-        // 创建一个Map来存储要填充的数据
-        Map<String, Object> dataMap = new HashMap<>();
-        dataMap.put("a", 13.7);
-        dataMap.put("b", 46.9);
-
-        // 使用EasyExcel填充数据
-        ExcelWriter excelWriter = EasyExcel.write(outputPath)
-                .withTemplate(templateFilePath)
-                .inMemory(true) // 启用内存模式
-                .build();
-        WriteSheet writeSheet = EasyExcel.writerSheet().build();
-        excelWriter.fill(dataMap, writeSheet); // 填充数据
-
-        // 由于填充后不会自动更新公式值,所以此处需要手动更新,大文件慎用(可能会导致内存溢出)
-        Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
-        workbook.getCreationHelper().createFormulaEvaluator().evaluateAll(); // 强制计算公式
-
-        excelWriter.finish();
-    }
-
-    /**
-     * 测试使用poi合并Excel文件
-     * 大概就是读取模板文件的行信息(包括数据,样式等等),再写入到目标文件中
-     */
-    // @Test
-    void testExcelMerge(){
-        String sourceExcelPath = "E:\\test\\source.xlsx"; // 源Excel文件路径
-        String targetExcelPath = "E:\\test\\target.xlsx"; // 目标Excel文件路径
-        int targetSheetIndex = 0; // 目标sheet索引
-        int targetRowNum = 4; // 目标行索引
-
-        // 加载源Excel工作簿
-        try (InputStream inputStream = Files.newInputStream(Paths.get(sourceExcelPath))) {
-            Workbook sourceWorkbook = new XSSFWorkbook(inputStream);
-            Sheet sourceSheet = sourceWorkbook.getSheetAt(0); // 读取第一个sheet
-
-            // 打开已存在的Excel工作簿
-            Workbook targetWorkbook = WorkbookFactory.create(Files.newInputStream(Paths.get(targetExcelPath)));
-            Sheet targetSheet = targetWorkbook.getSheetAt(targetSheetIndex); // 获取目标sheet
-
-            // 复制样式映射
-            Map<Short, Short> styleMap = copyCellStyle(sourceWorkbook, targetWorkbook);
-
-            // 复制行和单元格
-            for (Row sourceRow : sourceSheet) {
-                Row targetRow = targetSheet.createRow(targetRowNum + sourceRow.getRowNum());
-                for (Cell sourceCell : sourceRow) {
-                    Cell targetCell = targetRow.createCell(sourceCell.getColumnIndex());
-                    copyCell(sourceCell, targetCell, targetWorkbook, styleMap);
-                }
-            }
-
-            // 复制数据验证
-            copyDataValidations(sourceSheet, targetSheet);
-
-            // 保存目标Excel工作簿
-            try (FileOutputStream outputStream = new FileOutputStream(targetExcelPath)) {
-                targetWorkbook.write(outputStream);
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * 复制单元格样式
-     * @param srcBook
-     * @param desBook
-     * @return
-     */
-    private static Map<Short, Short> copyCellStyle(Workbook srcBook, Workbook desBook) {
-        Map<Short, Short> styleMap = new HashMap<>();
-        for (short i = 0; i < srcBook.getNumCellStyles(); i++) {
-            CellStyle srcStyle = srcBook.getCellStyleAt(i);
-            CellStyle desStyle = desBook.createCellStyle();
-            desStyle.cloneStyleFrom(srcStyle);
-            styleMap.put(srcStyle.getIndex(), desStyle.getIndex());
-        }
-        return styleMap;
-    }
-
-    /**
-     * 复制单元格
-     * @param srcCell
-     * @param desCell
-     * @param targetWorkbook
-     * @param styleMap
-     */
-    private static void copyCell(Cell srcCell, Cell desCell, Workbook targetWorkbook, Map<Short, Short> styleMap) {
-        // 判断单元格值类型
-        if (srcCell.getCellType() == CellType.NUMERIC) {
-            desCell.setCellValue(srcCell.getNumericCellValue());
-        } else if (srcCell.getCellType() == CellType.STRING) {
-            desCell.setCellValue(srcCell.getStringCellValue());
-        } else if (srcCell.getCellType() == CellType.BOOLEAN) {
-            desCell.setCellValue(srcCell.getBooleanCellValue());
-        } else if (srcCell.getCellType() == CellType.BLANK) {
-            desCell.setBlank();
-        } else if (srcCell.getCellType() == CellType.FORMULA) {
-            desCell.setCellFormula(srcCell.getCellFormula());
-        }
-
-        // 复制样式
-        desCell.setCellStyle(targetWorkbook.getCellStyleAt(styleMap.get(srcCell.getCellStyle().getIndex())));
-    }
-
-    /**
-     * 复制数据验证
-     * @param sourceSheet
-     * @param targetSheet
-     */
-    private static void copyDataValidations(Sheet sourceSheet, Sheet targetSheet) {
-        for (DataValidation validation : sourceSheet.getDataValidations()) {
-            // 设置要设置数据验证的单元格坐标
-            CellRangeAddressList regions = new CellRangeAddressList(validation.getRegions().getCellRangeAddress(0).getFirstRow() + 4,
-                    validation.getRegions().getCellRangeAddress(0).getLastRow() + 4,
-                    validation.getRegions().getCellRangeAddress(0).getFirstColumn(),
-                    validation.getRegions().getCellRangeAddress(0).getLastColumn());
-            // 获取数据源的数据验证信息
-            DataValidationHelper validationHelper = targetSheet.getDataValidationHelper();
-            DataValidationConstraint constraint = validation.getValidationConstraint();
-            // 新建数据验证
-            DataValidation newValidation = validationHelper.createValidation(constraint, regions);
-            // 在目标excel中设置数据验证
-            targetSheet.addValidationData(newValidation);
-        }
-    }
-
 }

+ 166 - 0
biz-base/src/test/java/com/dayou/ExcelSimpleTests.java

@@ -0,0 +1,166 @@
+package com.dayou;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.dayou.utils.EasyExcelUtil;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.Test;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ExcelSimpleTests {
+
+    /**
+     * 测试使用EasyExcel填充值到带公式的excel模板
+     */
+     @Test
+    void testEasyExcel(){
+        // 模板文件路径
+        String templateFilePath = "E:\\test\\input.xlsx";
+        // 输出文件路径
+        String outputPath = "E:\\test\\output.xlsx";
+
+        // 创建一个Map来存储要填充的数据
+        // map中的key是在excel文件设置占位符,比如这里的key是a,那么在excel中的占位就写作 {a}
+        Map<String, Object> dataMap = new HashMap<>();
+        dataMap.put("a", 13.7);
+        dataMap.put("b", 79.78);
+
+        EasyExcelUtil.fillExcelDataByMap(templateFilePath, outputPath, dataMap, true);
+
+        /*// 使用EasyExcel填充数据
+        ExcelWriter excelWriter = EasyExcel.write(outputPath)
+                .withTemplate(templateFilePath)
+                .inMemory(true) // 启用内存模式
+                .build();
+        WriteSheet writeSheet = EasyExcel.writerSheet().build();
+        excelWriter.fill(dataMap, writeSheet); // 填充数据
+
+        // 由于填充后不会自动更新公式值,所以此处需要手动更新,大文件慎用(可能会导致内存溢出)
+        Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
+        workbook.getCreationHelper().createFormulaEvaluator().evaluateAll(); // 强制计算公式
+
+        excelWriter.finish();*/
+    }
+
+    /**
+     * 测试使用poi合并Excel文件
+     * 大概就是读取模板文件的行信息(包括数据,样式等等),再写入到目标文件中
+     */
+     @Test
+    void testExcelMerge() throws IOException {
+        String sourceExcelPath = "E:\\test\\source.xlsx"; // 源Excel文件路径
+        String targetExcelPath = "E:\\test\\target.xlsx"; // 目标Excel文件路径
+        int targetSheetIndex = 0; // 目标sheet索引
+        int targetRowNum = 4; // 目标行索引
+
+         Workbook workbook = EasyExcelUtil.mergeExcel(sourceExcelPath, targetExcelPath, targetSheetIndex, targetRowNum, 0);
+         workbook.write(Files.newOutputStream(Paths.get("E:\\test\\merge.xlsx")));
+
+        // 加载源Excel工作簿
+/*        try (InputStream inputStream = Files.newInputStream(Paths.get(sourceExcelPath))) {
+            Workbook sourceWorkbook = new XSSFWorkbook(inputStream);
+            Sheet sourceSheet = sourceWorkbook.getSheetAt(0); // 读取第一个sheet
+
+            // 打开已存在的Excel工作簿
+            Workbook targetWorkbook = WorkbookFactory.create(Files.newInputStream(Paths.get(targetExcelPath)));
+            Sheet targetSheet = targetWorkbook.getSheetAt(targetSheetIndex); // 获取目标sheet
+
+            // 复制样式映射
+            Map<Short, Short> styleMap = copyCellStyle(sourceWorkbook, targetWorkbook);
+
+            // 复制行和单元格
+            for (Row sourceRow : sourceSheet) {
+                Row targetRow = targetSheet.createRow(targetRowNum + sourceRow.getRowNum());
+                for (Cell sourceCell : sourceRow) {
+                    Cell targetCell = targetRow.createCell(sourceCell.getColumnIndex());
+                    copyCell(sourceCell, targetCell, targetWorkbook, styleMap);
+                }
+            }
+
+            // 复制数据验证
+            copyDataValidations(sourceSheet, targetSheet);
+
+            // 保存目标Excel工作簿
+            try (FileOutputStream outputStream = new FileOutputStream("E:\\test\\merge.xlsx")) {
+                targetWorkbook.write(outputStream);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }*/
+    }
+
+    /**
+     * 复制单元格样式
+     * @param srcBook
+     * @param desBook
+     * @return
+     */
+    private static Map<Short, Short> copyCellStyle(Workbook srcBook, Workbook desBook) {
+        Map<Short, Short> styleMap = new HashMap<>();
+        for (short i = 0; i < srcBook.getNumCellStyles(); i++) {
+            CellStyle srcStyle = srcBook.getCellStyleAt(i);
+            CellStyle desStyle = desBook.createCellStyle();
+            desStyle.cloneStyleFrom(srcStyle);
+            styleMap.put(srcStyle.getIndex(), desStyle.getIndex());
+        }
+        return styleMap;
+    }
+
+    /**
+     * 复制单元格
+     * @param srcCell
+     * @param desCell
+     * @param targetWorkbook
+     * @param styleMap
+     */
+    private static void copyCell(Cell srcCell, Cell desCell, Workbook targetWorkbook, Map<Short, Short> styleMap) {
+        // 判断单元格值类型
+        if (srcCell.getCellType() == CellType.NUMERIC) {
+            desCell.setCellValue(srcCell.getNumericCellValue());
+        } else if (srcCell.getCellType() == CellType.STRING) {
+            desCell.setCellValue(srcCell.getStringCellValue());
+        } else if (srcCell.getCellType() == CellType.BOOLEAN) {
+            desCell.setCellValue(srcCell.getBooleanCellValue());
+        } else if (srcCell.getCellType() == CellType.BLANK) {
+            desCell.setBlank();
+        } else if (srcCell.getCellType() == CellType.FORMULA) {
+            desCell.setCellFormula(srcCell.getCellFormula());
+        }
+
+        // 复制样式
+        desCell.setCellStyle(targetWorkbook.getCellStyleAt(styleMap.get(srcCell.getCellStyle().getIndex())));
+    }
+
+    /**
+     * 复制数据验证(下拉框)
+     * @param sourceSheet
+     * @param targetSheet
+     */
+    private static void copyDataValidations(Sheet sourceSheet, Sheet targetSheet) {
+        for (DataValidation validation : sourceSheet.getDataValidations()) {
+            // 设置要设置数据验证的单元格坐标
+            CellRangeAddressList regions = new CellRangeAddressList(validation.getRegions().getCellRangeAddress(0).getFirstRow() + 4,
+                    validation.getRegions().getCellRangeAddress(0).getLastRow() + 4,
+                    validation.getRegions().getCellRangeAddress(0).getFirstColumn(),
+                    validation.getRegions().getCellRangeAddress(0).getLastColumn());
+            // 获取数据源的数据验证信息
+            DataValidationHelper validationHelper = targetSheet.getDataValidationHelper();
+            DataValidationConstraint constraint = validation.getValidationConstraint();
+            // 新建数据验证
+            DataValidation newValidation = validationHelper.createValidation(constraint, regions);
+            // 在目标excel中设置数据验证
+            targetSheet.addValidationData(newValidation);
+        }
+    }
+
+}

+ 375 - 0
biz-base/src/test/java/com/dayou/WordSimpleTests.java

@@ -0,0 +1,375 @@
+package com.dayou;
+
+import com.aspose.words.*;
+import com.aspose.words.net.System.Data.DataRow;
+import com.aspose.words.net.System.Data.DataTable;
+import com.dayou.utils.AsposeWordUtil;
+import lombok.Builder;
+import lombok.Data;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 测试类引用的常用方法已经封装在AsposeWordUtil工具类
+ * 该测试类操作的文档以债权报告为例
+ * 参考文档:reference.aspose.com/words/zh/java/com.aspose.words/documentbuilder/
+ * -----------------------------------------------------------------------------
+ * 1.aspose动态填充是采取的更新域(域替换),目录也是域,动态填充时有目录的话,目录样式会被更新掉,无法设置,所以动态填充时不能有目录
+ * 2.aspose设置的页码无法选择从哪一页开始计数,所以页眉页脚与页码需要在模板中提前设置好新增页面的页眉页脚要同上一页页面。无论是拼接上去的word还是插入进去的word都可以顺利继承页眉页脚
+ * 3.段落模板设置的页眉页脚,用将段落模板插入到某个位置是无效的,页眉页脚带不过去
+ * 4.由于目录的页码与正文内容多数情况采取的不同样式,如果将目录与正文分开拼接,会导致页码样式丢失,最好是目录和一部分正文内容在一起(如像债权一样将目录和声明作为一个段落模板)。
+ * 5.该案例过程大概是 填充封面内容-->将带有不同页码样式的目录与声明拼接到封面后(声明后最好跟上一页带有页眉页脚的空白页面,确保插入的word同步到页眉页脚)-->对段落模板进行值填充-->向声明后的的空白页面插入段落模板-->更新目录页码
+ */
+public class WordSimpleTests {
+
+    /**
+     * 这里的属性名字要和word设置的域名一致
+     */
+    @Data
+    @Builder
+    public static class SimpleWordData {
+        private String name1;
+
+        private String name2;
+
+        private String productionNo;
+
+        private String date;
+
+        private List<SimpleWordTableData> contractList;
+    }
+
+    @Data
+    @Builder
+    public static class SimpleWordTableData {
+        private String id;
+
+        private String pledger;
+
+        private String contractNum;
+
+        private String guarantyStyle;
+
+        private String amount;
+    }
+
+    /**
+     * 测试向word模板中动态填充数据
+     * 此处案例是设置封面
+     * @throws IOException
+     */
+    @Test
+    void testWordFilling() throws IOException {
+        // 设置信息
+        SimpleWordData wordData = SimpleWordData.builder()
+                .name1("大连银行股份有限公司成都分行")
+                .name2("四川宏淼科技发展有限公司债权")
+                .productionNo("川友评报字(2020)第0123-1号")
+                .date("二〇二〇年四月三十日")
+                .build();
+
+        // 读取模板文件
+        byte[] tmplWordByte = Files.readAllBytes(Paths.get("E:\\test\\input.doc"));
+
+        // 获取填充数据后的文件(AsposeWordUtil是工具类,放在common中)
+        byte[] resultWordByte = AsposeWordUtil.fillWordDataByDomain(tmplWordByte, wordData);
+
+        // 处理该二进制文件并保存
+        File resultFile = new File("E:\\test\\output.doc");
+        FileOutputStream fos = new FileOutputStream(resultFile);
+        fos.write(resultWordByte);
+        fos.close();
+    }
+
+    /**
+     * 测试拼接文档
+     * 此处案例是拼接目录与声明的内容
+     */
+    @Test
+    void testAppendWord(){
+        try {
+            //加载源文档
+            Document doc1 = new Document("E:\\test\\output.doc"); // 封面
+            Document doc2 = new Document("E:\\test\\catalogue.doc"); // 目录与声明
+
+            //将第二个文档的内容附加到第一个文档
+            doc1.appendDocument(doc2, ImportFormatMode.KEEP_SOURCE_FORMATTING);
+            //保存合并的文档
+            doc1.save("E:\\test\\output.doc");
+        } catch (Exception e) {
+            System.out.println("An error occurred: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 测试向word模板中动态填充数据
+     * 此处案例是设置正文内容
+     * @throws IOException
+     */
+    @Test
+    void testWordFillSection() throws IOException {
+        // 设置信息
+        SimpleWordTableData data1 = SimpleWordTableData.builder().id("1").pledger("张三").contractNum("合同阿巴阿巴阿巴阿巴").guarantyStyle("担保").amount("1000万").build();
+        SimpleWordTableData data2 = SimpleWordTableData.builder().id("2").pledger("李四").contractNum("合同呜呼呜呼呜呼芜湖").guarantyStyle("抵押").amount("200028万").build();
+        List<SimpleWordTableData> contractList = Arrays.asList(data1,data2);
+        SimpleWordData wordData = SimpleWordData.builder()
+                .name1("大连银行股份有限公司成都分行")
+                .name2("四川宏淼科技发展有限公司债权")
+                .productionNo("川友评报字(2020)第0123-1号")
+                .date("二〇二〇年四月三十日")
+                .contractList(contractList)
+                .build();
+
+        // 读取模板文件
+        byte[] tmplWordByte = Files.readAllBytes(Paths.get("E:\\test\\section.doc"));
+
+        // 获取填充数据后的文件(AsposeWordUtil是工具类,放在common中)
+        byte[] resultWordByte = AsposeWordUtil.fillWordDataByDomain(tmplWordByte, wordData);
+
+        // 处理该二进制文件并保存
+        File resultFile = new File("E:\\test\\section_output.doc");
+        FileOutputStream fos = new FileOutputStream(resultFile);
+        fos.write(resultWordByte);
+        fos.close();
+    }
+
+    @Test
+    public void getStudentCourseDataTable() throws Exception {
+        DataTable dataTable = new DataTable("StudentCourse");
+        dataTable.getColumns().add("CourseName");
+        dataTable.getColumns().add("CourseName02");
+        for (int i = 0; i < 3; i++) {
+            DataRow datarow = dataTable.newRow();
+            dataTable.getRows().add(datarow);
+            datarow.set(0, "Course " + i);
+            datarow.set(1, "Name " + i);
+        }
+
+        System.out.println(dataTable);
+    }
+
+    /**
+     * 测试向word指定位置插入另一个word
+     * 实测该方法无法插入页眉页脚
+     * 此处案例是将正文内容插入到拼接好目录与声明的报告中
+     * @throws Exception
+     */
+    @Test
+    void testInsertWord() throws Exception {
+
+        //设置书签,指定文档拼接的位置(书签需要在模板中提前设置好,相当于标识符)
+        String bookmark = "insertSection";
+
+        // 主word文件
+        Document mainDoc = new Document("E:\\test\\output.doc");
+
+        // 需要插入的word文件
+        Document subDoc = new Document("E:\\test\\section_output.doc");
+
+        // 是否纵向纸张
+/*        boolean isPortrait = true;
+
+        // Aspose的文档构造器
+        DocumentBuilder builder = new DocumentBuilder(mainDoc);
+            // 获取书签信息
+            BookmarkCollection bms = mainDoc.getRange().getBookmarks();
+            Bookmark bm = bms.get(bookmark);
+            // 判断书签是否为null
+            if (bm != null) {
+
+                // 移动光标到书签的位置
+                builder.moveToBookmark(bookmark, true, false);
+                // 在文档中插入段落分隔符(会在书签位置换行)
+                builder.writeln();
+                Node insertAfterNode = builder.getCurrentParagraph().getPreviousSibling();
+                // 在分隔符插入word
+                AsposeWordUtil.insertDocumentAfterNode(insertAfterNode, mainDoc, subDoc);
+            }
+            //设置纸张大小
+            builder.getPageSetup().setPaperSize(PaperSize.A4);
+
+            // 判断是横向还是纵向纸张
+            if (isPortrait) {
+                //纵向
+                builder.getPageSetup().setOrientation(Orientation.PORTRAIT);
+                builder.insertBreak(BreakType.SECTION_BREAK_NEW_PAGE);
+            } else {
+                //横向
+                builder.getPageSetup().setOrientation(Orientation.LANDSCAPE);
+                builder.insertBreak(BreakType.SECTION_BREAK_NEW_PAGE);
+            }*/
+            mainDoc = AsposeWordUtil.insertDocumentAfterBookMark(mainDoc, subDoc, bookmark, false);
+            // 保存文件
+            mainDoc.save("E:\\test\\success_output.doc");
+    }
+
+    /**
+     * 更新目录页码
+     * 此处案例是更新目录页码
+     */
+    @Test
+    void testUpdateCatalogueLink() throws Exception {
+        Document doc = new Document("E:\\test\\success_output.doc");
+        // 还有一种方式是 doc.updateFields(); 方法,该方法会导致目录样式被更新掉。方便,但是不建议使用
+        for (Field field : doc.getRange().getFields()){
+            if (field.getType() == FieldType.FIELD_TOC)
+            {
+                FieldToc toc = (FieldToc)field;
+                toc.updatePageNumbers(); //定位toc只更新页码
+            } else{
+                field.update();
+            }
+        }
+        doc.save("E:\\test\\success_output.doc");
+    }
+
+    /**
+     * (该方法实测不是很好用,建议先在模板设置好页眉页脚页码等相关设置)
+     * 设置Word页眉
+     * 不能先设置页脚,需要先设置页码后才能设置页脚内容
+     * 否则提前移动光标到页脚会报错:Cannot insert a node of this type at this location.
+     * @throws Exception
+     */
+//    @Test
+    void setHeaderFooter() throws Exception {
+        Document mainDoc = new Document("E:\\test\\output.docx");
+        DocumentBuilder builder = new DocumentBuilder(mainDoc);
+        // 为true表示文档的奇数页和偶数页有不同的页眉和页脚
+        builder.getPageSetup().setOddAndEvenPagesHeaderFooter(false);
+
+        /*
+        * 设置样式前,先移动光标!
+        * 先移动光标!
+        * 先移动光标!
+        * 先移动光标!
+        */
+
+        // 移动光标到页眉
+        builder.moveToHeaderFooter(HeaderFooterType.HEADER_PRIMARY);
+
+        // 跳过第一页
+        builder.getPageSetup().setDifferentFirstPageHeaderFooter(true);
+
+        // 内容居中
+        builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
+        //设置页眉上下边距(用的pt(磅)为单位)
+        builder.getPageSetup().setHeaderDistance(45.3536);
+
+        // 添加页眉线
+        Border borderHeader = builder.getParagraphFormat().getBorders().getBottom();
+        // 设置阴影
+        borderHeader.setShadow(true);
+        // 设置间距
+        borderHeader.setDistanceFromText(2);
+        // 设置页眉线风格(此处单线)
+        borderHeader.setLineStyle(LineStyle.SINGLE);
+
+        // 字体宋体
+        builder.getFont().setName("宋体");
+        // 是否加粗
+        builder.getFont().setBold(false);
+        // 字号(9是小五字号)
+        builder.getFont().setSize(9);
+        // 设置页头内容
+        builder.write("大连银行股份有限公司成都分行拟处置不良资产所涉及的四川宏淼科技发展有限公司");
+        builder.writeln();
+        builder.write("债权价值分析报告·正文");
+
+/*        // 移动光标到页脚
+        builder.moveToHeaderFooter(HeaderFooterType.FOOTER_PRIMARY);
+
+        builder.getPageSetup().setFooterDistance(28.3465);
+
+        // 添加页脚线
+        Border borderFooter = builder.getParagraphFormat().getBorders().getTop();
+        // 设置阴影
+        borderFooter.setShadow(true);
+        // 设置间距
+        borderFooter.setDistanceFromText(0);
+        // 设置页眉线风格(此处单线)
+        borderFooter.setLineStyle(LineStyle.SINGLE);
+
+        // 字体宋体
+        builder.getFont().setName("宋体");
+        // 是否加粗
+        builder.getFont().setBold(false);
+        // 字号(9是小五字号)
+        builder.getFont().setSize(9);
+
+        // 设置页脚内容
+        builder.write("四川大友房地产土地资产评估有限公司");*/
+
+        mainDoc.save("E:\\test\\output.docx");
+    }
+
+    /**
+     * 设置页脚与页码(该方法实测不是很好用,建议先在模板设置好页眉页脚页码等相关设置)
+     * @throws Exception
+     */
+//    @Test
+    void setPageNumber() throws Exception {
+        Document doc = new Document("E:\\test\\output.docx");
+        DocumentBuilder builder = new DocumentBuilder(doc);
+
+        //创建页脚 页码
+/*        HeaderFooter footer = new HeaderFooter(doc, HeaderFooterType.FOOTER_PRIMARY);
+        doc.getFirstSection().getHeadersFooters().add(footer);*/
+
+//        builder.getPageSetup().setPageStartingNumber(10); 这玩意没用,看文档没看出来到底用在什么地方的
+
+        //页脚段落
+/*        Paragraph footerpara = new Paragraph(doc);
+        footerpara.getParagraphFormat().setAlignment(ParagraphAlignment.LEFT);
+        Run footerparaRun = new Run(doc);
+        footerparaRun.getFont().setName("Arial Narrow");
+        footerparaRun.getFont().setSize(9.0);//小5号字体
+        footerpara.appendChild(footerparaRun);
+        footerpara.appendField(FieldType.FIELD_PAGE, true);//当前页码
+        footerpara.appendChild(footerparaRun);
+        footer.appendChild(footerpara);*/
+
+        // 移动光标到页脚
+        builder.moveToHeaderFooter(HeaderFooterType.FOOTER_PRIMARY);
+
+        // 跳过第一页(该设置对当前页码无效,跳过首页之后,第二页的页码依旧是2)
+        builder.getPageSetup().setDifferentFirstPageHeaderFooter(true);
+
+        builder.getPageSetup().setFooterDistance(28.3465);
+
+        // 添加页脚线
+        Border borderFooter = builder.getParagraphFormat().getBorders().getTop();
+        // 设置阴影
+        borderFooter.setShadow(true);
+        // 设置间距
+        borderFooter.setDistanceFromText(2);
+        // 设置页眉线风格(此处单线)
+        borderFooter.setLineStyle(LineStyle.SINGLE);
+
+        // 字体宋体
+        builder.getFont().setName("宋体");
+        // 是否加粗
+        builder.getFont().setBold(false);
+        // 字号(9是小五字号)
+        builder.getFont().setSize(9);
+
+        // 设置页脚内容
+        builder.write("四川大友房地产土地资产评估有限公司\t\t\t");
+
+        doc.getFirstSection().getPageSetup().setSectionStart(SectionStart.CONTINUOUS);
+        doc.getFirstSection().getPageSetup().setPageStartingNumber(2);
+
+//        builder.insertField("PAGE");
+
+        doc.save("E:\\test\\output.docx");
+    }
+
+}

+ 5 - 0
common/pom.xml

@@ -75,6 +75,11 @@
             <artifactId>commons-io</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.aspose</groupId>
+            <artifactId>aspose-words</artifactId>
+        </dependency>
+
 
     </dependencies>
     <dependencyManagement>

+ 324 - 0
common/src/main/java/com/dayou/utils/AsposeWordUtil.java

@@ -0,0 +1,324 @@
+package com.dayou.utils;
+
+import com.aspose.words.*;
+import com.aspose.words.net.System.Data.DataRow;
+import com.aspose.words.net.System.Data.DataTable;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.WritableRaster;
+import java.beans.PropertyDescriptor;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class AsposeWordUtil {
+
+    private AsposeWordUtil() {
+    }
+
+    /**
+     * 调整bufferedimage大小
+     * @param source BufferedImage 原始image
+     * @param targetW int  目标宽
+     * @param targetH int  目标高
+     * @param flag boolean 是否同比例调整
+     * @return BufferedImage  返回新image
+     */
+    public static BufferedImage resizeBufferedImage(BufferedImage source, int targetW, int targetH, boolean flag) {
+        int type = source.getType();
+        BufferedImage target = null;
+        double sx = (double) targetW / source.getWidth();
+        double sy = (double) targetH / source.getHeight();
+        if (flag && sx > sy) {
+            sx = sy;
+            targetW = (int) (sx * source.getWidth());
+        } else if(flag && sx <= sy){
+            sy = sx;
+            targetH = (int) (sy * source.getHeight());
+        }
+        if (type == BufferedImage.TYPE_CUSTOM) {
+            ColorModel cm = source.getColorModel();
+            WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);
+            boolean alphaPremultiplied = cm.isAlphaPremultiplied();
+            target = new BufferedImage(cm, raster, alphaPremultiplied, null);
+        } else {
+            target = new BufferedImage(targetW, targetH, type);
+        }
+        Graphics2D g = target.createGraphics();
+        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+        g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
+        g.dispose();
+        return target;
+    }
+
+
+    /**
+     * 填充 word 模板(object数据格式)
+     * @param modelWordByte word模版二进制文件
+     * @param obj     要填充的数据
+     * @return 组合数据之后的word二进制
+     */
+    public static byte[] fillWordDataByDomain(byte[] modelWordByte, Object obj) {
+        try {
+            Class<?> aClass = obj.getClass();
+            Field[] fields = aClass.getDeclaredFields();
+            Map<String, Object> data = new HashMap<>(fields.length);
+            for (Field field : fields) {
+                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), aClass);
+                Method method = pd.getReadMethod();
+                String key = field.getName();
+                Object value = method.invoke(obj);
+                if (value != null) {
+                    data.put(key, value);
+                }
+            }
+            return fillWordDataByMap(modelWordByte, data);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return new byte[0];
+        }
+    }
+
+
+    /**
+     * 填充 word 模板(map数据格式)
+     * @param file word二进制
+     * @param data 要填充的数据
+     * @return 组合数据之后的word二进制
+     */
+    public static byte[] fillWordDataByMap(byte[] file, Map<String, Object> data) throws Exception {
+        byte[] ret = null;
+        if (data == null || data.isEmpty()) {
+            return ret;
+        }
+
+        try (InputStream is = new ByteArrayInputStream(file);
+             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            Document doc = new Document(is);
+            DocumentBuilder builder = new DocumentBuilder(doc);
+            Map<String, String> toData = new HashMap<>();
+            for (Map.Entry<String, Object> en : data.entrySet()) {
+                String key = en.getKey();
+                Object value = en.getValue();
+
+                if (value instanceof List) {
+                    //写入表数据
+                    DataTable dataTable = fillListData((List) value, key);
+                    doc.getMailMerge().executeWithRegions(dataTable);
+                }
+
+                if (value instanceof BufferedImage) {
+                    builder.moveToMergeField(key);
+                    builder.insertImage((BufferedImage) value);
+                }
+
+                String valueStr = String.valueOf(en.getValue());
+                if (value == null || value.equals("null")) {
+                    continue;
+                }
+
+                toData.put(key, valueStr);
+            }
+
+            String[] fieldNames = new String[toData.size()];
+            String[] values = new String[toData.size()];
+
+            int i = 0;
+            for (Map.Entry<String, String> entry : toData.entrySet()) {
+                fieldNames[i] = entry.getKey();
+                values[i] = entry.getValue();
+                i++;
+            }
+
+            //合并数据
+            doc.getMailMerge().execute(fieldNames, values);
+            doc.save(out, SaveOptions.createSaveOptions(SaveFormat.DOCX));
+            ret = out.toByteArray();
+        }
+
+        return ret;
+    }
+
+    /**
+     * 封装 list 数据到 word 模板中(word表格)
+     * @param list      数据
+     * @param tableName 表格列表变量名称
+     * @return word表格数据DataTable
+     */
+    private static DataTable fillListData(List<Object> list, String tableName) throws Exception {
+
+        //创建DataTable
+        DataTable dataTable = new DataTable(tableName);
+        // 获取类的Class对象
+        Class<?> clazz = list.get(0).getClass();
+
+        // 获取类的所有属性
+        Field[] fields = clazz.getDeclaredFields();
+
+        // 遍历集合对象类的所有属性并添加为DataTable的字段
+        for (Field field : fields) {
+            // 添加字段到DataTable,并将属性名作为字段名
+            dataTable.getColumns().add(field.getName());
+        }
+        // 循环对象集合
+        for (Object obj : list) {
+            //创建DataRow,封装该行数据
+            DataRow dataRow = dataTable.newRow();
+            // 向dataRow中添加对象每个属性的值
+            for (int i = 0; i < fields.length; i++) {
+                fields[i].setAccessible(true);
+                // 通过下标设置(我们的类有几个属性,DataTable就创建了几个字段,所以这里可以直接通过下标设置值,因为创建字段的顺序和我们设置值的顺序一样)
+                dataRow.set(i, fields[i].get(obj));
+                // 还可以通过字段名设置(如下)
+                //dataRow.set(fields[i].getName(), fields[i].get(obj));
+            }
+            dataTable.getRows().add(dataRow);
+        }
+        return dataTable;
+    }
+
+    /**
+     * @Description 向书签后插入文档
+     * @param mainDoc 主文档
+     * @param tobeInserted 拼接的文档
+     * @param isPortrait 纸张方向
+     * @param bookmark 书签
+     */
+    public static Document insertDocumentAfterBookMark(Document mainDoc, Document tobeInserted, String bookmark, boolean isPortrait)
+            throws Exception {
+        if (mainDoc == null) {
+            return null;
+        } else if (tobeInserted == null) {
+            return mainDoc;
+        } else {
+            //构建新文档
+            DocumentBuilder mainDocBuilder = new DocumentBuilder(mainDoc);
+            // 判断书签是否为空
+            if (bookmark != null && !bookmark.isEmpty()) { // 书签位置插入
+                //获取到书签
+                BookmarkCollection bms = mainDoc.getRange().getBookmarks();
+                Bookmark bm = bms.get(bookmark);
+                if (bm != null) {
+                    // 移动光标到书签
+                    mainDocBuilder.moveToBookmark(bookmark, true, false);
+                    // 在文档中插入段落分隔符
+                    mainDocBuilder.writeln();
+                    // 设置纸张方向
+//                    if (isPortrait) {
+//                        //纵向
+//                        mainDocBuilder.getPageSetup().setOrientation(Orientation.PORTRAIT);
+//                        mainDocBuilder.insertBreak(BreakType.SECTION_BREAK_NEW_PAGE);
+//                    } else {
+//                        //横向
+//                        mainDocBuilder.getPageSetup().setOrientation(Orientation.LANDSCAPE);
+//                        mainDocBuilder.insertBreak(BreakType.SECTION_BREAK_NEW_PAGE);
+//                    }
+                    //获取到插入的位置
+                    Node insertAfterNode = mainDocBuilder.getCurrentParagraph().getPreviousSibling();
+
+                    insertDocumentAfterNode(insertAfterNode, mainDoc, tobeInserted);
+                }
+            } else { // 文档末尾拼接
+                appendDoc(mainDoc, tobeInserted, true);
+            }
+            return mainDoc;
+        }
+    }
+
+    /**
+     * @Description 在指定节点后插入word文档
+     * @param insertAfterNode 插入的位置
+     * @param mainDoc 主word文件
+     * @param srcDoc 引用的word文件
+     */
+    public static void insertDocumentAfterNode(Node insertAfterNode, Document mainDoc, Document srcDoc) throws Exception {
+        // Aspose定义的nodeType 8 和 5 指的是段落或者表格,插入word应该只能在这两个位置插入,具体没测试
+        if (insertAfterNode.getNodeType() != 8 & insertAfterNode.getNodeType() != 5) {
+            throw new Exception("The destination node should be either a paragraph or table.");
+        } else {
+            CompositeNode dstStory = insertAfterNode.getParentNode();
+
+            while (null != srcDoc.getLastSection().getBody().getLastParagraph()
+                    && !srcDoc.getLastSection().getBody().getLastParagraph().hasChildNodes()) {
+                // 移除节点
+                srcDoc.getLastSection().getBody().getLastParagraph().remove();
+            }
+
+            // 将节点从srcDoc文档中导入
+            NodeImporter importer = new NodeImporter(srcDoc, mainDoc, 1);
+            int sectCount = srcDoc.getSections().getCount();
+
+
+            for (int sectIndex = 0; sectIndex < sectCount; ++sectIndex) {
+                Section srcSection = srcDoc.getSections().get(sectIndex);
+                int nodeCount = srcSection.getBody().getChildNodes().getCount();
+
+                for (int nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
+                    Node srcNode = srcSection.getBody().getChildNodes().get(nodeIndex);
+                    Node newNode = importer.importNode(srcNode, true);
+                    dstStory.insertAfter(newNode, insertAfterNode);
+                    insertAfterNode = newNode;
+                }
+            }
+        }
+    }
+
+    /**
+     * @Description 文档拼接
+     * @param dstDoc
+     * @param srcDoc
+     * @param includeSection
+     */
+    private static void appendDoc(Document dstDoc, Document srcDoc, boolean includeSection) throws Exception {
+        if (includeSection) {
+            for (Section srcSection : srcDoc.getSections()) {
+                Node dstNode = dstDoc.importNode(srcSection, true, 0);
+                dstDoc.appendChild(dstNode);
+            }
+        } else {
+            Node node = dstDoc.getLastSection().getBody().getLastParagraph();
+            if (node == null) {
+                node = new Paragraph(srcDoc);
+                dstDoc.getLastSection().getBody().appendChild(node);
+            }
+
+            if (node.getNodeType() != 8 & node.getNodeType() != 5) {
+                throw new Exception("Use appendDoc(dstDoc, srcDoc, true) instead of appendDoc(dstDoc, srcDoc, false)");
+            }
+
+            insertDocumentAfterNode(node, dstDoc, srcDoc);
+        }
+    }
+
+    /**
+     * 更新文档的目录页码
+     * @param doc 文档
+     * @return Document
+     * @throws Exception 抛出异常
+     */
+    public static Document updateCatalogueLink(Document doc) throws Exception {
+        // 这里的Field和反射的重名了
+        // 循环整个目录并更新页码
+        for (com.aspose.words.Field field : doc.getRange().getFields()){
+            if (field.getType() == FieldType.FIELD_TOC)
+            {
+                FieldToc toc = (FieldToc)field;
+                //定位toc就只更新页码
+                toc.updatePageNumbers();
+            } else{
+                // 更新目录(会掉样式)
+                field.update();
+            }
+        }
+        return doc;
+    }
+}

+ 145 - 0
common/src/main/java/com/dayou/utils/EasyExcelUtil.java

@@ -0,0 +1,145 @@
+package com.dayou.utils;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+public class EasyExcelUtil {
+
+    /**
+     * 填充动态数据到Excel
+     * @param inputFilePath 模板文件路径
+     * @param outputFilePath 输出文件路径
+     * @param dataMap 数据map
+     * @param evaluate 是否计算公式(大文件慎用,可能会导致内存溢出)
+     */
+    public static void fillExcelDataByMap(String inputFilePath, String outputFilePath, Map<String, Object> dataMap, boolean evaluate) {
+        // 使用EasyExcel填充数据
+        ExcelWriter excelWriter = EasyExcel.write(outputFilePath)
+                .withTemplate(inputFilePath)
+                .inMemory(true) // 启用内存模式
+                .build();
+        WriteSheet writeSheet = EasyExcel.writerSheet().build();
+        excelWriter.fill(dataMap, writeSheet); // 填充数据
+
+        // 由于填充后不会自动更新公式值,所以需要手动更新
+        if (evaluate){
+            Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
+            workbook.getCreationHelper().createFormulaEvaluator().evaluateAll(); // 强制计算公式
+        }
+        excelWriter.finish();
+    }
+
+    /**
+     * 合并Excel文件
+     * @param sourceExcelPath 源文件路径(段落模板)
+     * @param targetExcelPath 目标文件路径(主模板)
+     * @param targetSheetIndex 目标sheet索引(要插入的sheet)
+     * @param targetRowNum 目标行索引(要插入的行)
+     * @param sourceSheetIndex 源文件被引用的sheet
+     * @return Workbook 返回工作簿
+     * @throws IOException 抛出IO异常
+     */
+    public static Workbook mergeExcel(String sourceExcelPath, String targetExcelPath, int targetSheetIndex, int targetRowNum, int sourceSheetIndex) throws IOException {
+        // 获取源Excel
+        InputStream inputStream = Files.newInputStream(Paths.get(sourceExcelPath));
+        Workbook sourceWorkbook = new XSSFWorkbook(inputStream);
+        Sheet sourceSheet = sourceWorkbook.getSheetAt(sourceSheetIndex);
+
+        // 打开已存在的Excel工作簿
+        Workbook targetWorkbook = WorkbookFactory.create(Files.newInputStream(Paths.get(targetExcelPath)));
+        Sheet targetSheet = targetWorkbook.getSheetAt(targetSheetIndex); // 获取目标sheet
+
+        // 复制样式映射
+        Map<Short, Short> styleMap = copyCellStyle(sourceWorkbook, targetWorkbook);
+
+        // 复制行和单元格
+        for (Row sourceRow : sourceSheet) {
+            Row targetRow = targetSheet.createRow(targetRowNum + sourceRow.getRowNum());
+            for (Cell sourceCell : sourceRow) {
+                Cell targetCell = targetRow.createCell(sourceCell.getColumnIndex());
+                copyCell(sourceCell, targetCell, targetWorkbook, styleMap);
+            }
+        }
+
+        // 复制数据验证
+        copyDataValidations(sourceSheet, targetSheet);
+
+        return targetWorkbook;
+    }
+
+    /**
+     * 复制单元格样式
+     * @param srcBook 源工作簿
+     * @param desBook 目标工作簿
+     * @return Map<Short, Short> 样式
+     */
+    private static Map<Short, Short> copyCellStyle(Workbook srcBook, Workbook desBook) {
+        Map<Short, Short> styleMap = new HashMap<>();
+        for (short i = 0; i < srcBook.getNumCellStyles(); i++) {
+            CellStyle srcStyle = srcBook.getCellStyleAt(i);
+            CellStyle desStyle = desBook.createCellStyle();
+            desStyle.cloneStyleFrom(srcStyle);
+            styleMap.put(srcStyle.getIndex(), desStyle.getIndex());
+        }
+        return styleMap;
+    }
+
+    /**
+     * 复制单元格
+     * @param srcCell 源单元格
+     * @param desCell 目标单元格
+     * @param targetWorkbook 目标工作簿
+     * @param styleMap 样式
+     */
+    private static void copyCell(Cell srcCell, Cell desCell, Workbook targetWorkbook, Map<Short, Short> styleMap) {
+        // 判断单元格值类型
+        if (srcCell.getCellType() == CellType.NUMERIC) {
+            desCell.setCellValue(srcCell.getNumericCellValue());
+        } else if (srcCell.getCellType() == CellType.STRING) {
+            desCell.setCellValue(srcCell.getStringCellValue());
+        } else if (srcCell.getCellType() == CellType.BOOLEAN) {
+            desCell.setCellValue(srcCell.getBooleanCellValue());
+        } else if (srcCell.getCellType() == CellType.BLANK) {
+            desCell.setBlank();
+        } else if (srcCell.getCellType() == CellType.FORMULA) {
+            desCell.setCellFormula(srcCell.getCellFormula());
+        }
+
+        // 复制样式
+        desCell.setCellStyle(targetWorkbook.getCellStyleAt(styleMap.get(srcCell.getCellStyle().getIndex())));
+    }
+
+    /**
+     * 复制数据验证(下拉框)
+     * @param sourceSheet 源Sheet
+     * @param targetSheet 目标Sheet
+     */
+    private static void copyDataValidations(Sheet sourceSheet, Sheet targetSheet) {
+        for (DataValidation validation : sourceSheet.getDataValidations()) {
+            // 设置要设置数据验证的单元格坐标
+            CellRangeAddressList regions = new CellRangeAddressList(validation.getRegions().getCellRangeAddress(0).getFirstRow() + 4,
+                    validation.getRegions().getCellRangeAddress(0).getLastRow() + 4,
+                    validation.getRegions().getCellRangeAddress(0).getFirstColumn(),
+                    validation.getRegions().getCellRangeAddress(0).getLastColumn());
+            // 获取数据源的数据验证信息
+            DataValidationHelper validationHelper = targetSheet.getDataValidationHelper();
+            DataValidationConstraint constraint = validation.getValidationConstraint();
+            // 新建数据验证
+            DataValidation newValidation = validationHelper.createValidation(constraint, regions);
+            // 在目标excel中设置数据验证
+            targetSheet.addValidationData(newValidation);
+        }
+    }
+
+}

+ 8 - 0
pom.xml

@@ -36,6 +36,7 @@
         <poi.vsersion>5.2.3</poi.vsersion>
         <poi-ooxml.version>5.2.3</poi-ooxml.version>
         <commons-io.version>2.15.0</commons-io.version>
+        <aspose-words.version>20.12</aspose-words.version>
     </properties>
 
     <dependencyManagement>
@@ -136,6 +137,13 @@
                 <version>${commons-io.version}</version>
             </dependency>
 
+            <!-- 文档处理,在线文档:https://reference.aspose.com/tutorials/words/zh/java/ -->
+            <dependency>
+                <groupId>com.aspose</groupId>
+                <artifactId>aspose-words</artifactId>
+                <version>${aspose-words.version}</version>
+            </dependency>
+
         </dependencies>
     </dependencyManagement>
 

+ 13 - 1
sql/update_sql.sql

@@ -163,4 +163,16 @@ CREATE TABLE `certificate_land_use` (
                                                    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
                                                    `delete_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '删除状态',
                                                    PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '权证-国有土地使用证' ROW_FORMAT = Dynamic;
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '权证-国有土地使用证' ROW_FORMAT = Dynamic;
+
+/**
+  日期:2024-10-23
+  修改人:苟耕铨
+  未更新到test-env
+ */
+ # 删除资产测算信息表中关于文档信息的字段
+ALTER TABLE assets_calculate DROP COLUMN calculate_file_name;
+ALTER TABLE assets_calculate DROP COLUMN calculate_file_url;
+# 模板信息表修改
+ALTER TABLE tmpl_asset_calculate ADD COLUMN has_section BIT(1) NOT NULL DEFAULT 0 COMMENT '是否有段落模板';
+ALTER TABLE tmpl_asset_report ADD COLUMN has_section BIT(1) NOT NULL DEFAULT 0 COMMENT '是否有段落模板';