Explorar el Código

1.债权分析报告生成雏形开发

GouGengquan hace 4 meses
padre
commit
5d3524ffd3

+ 10 - 0
biz-base/src/main/java/com/dayou/controller/AssetsReportController.java

@@ -120,5 +120,15 @@ public class AssetsReportController {
         return Result.build(assetsReportService.generateEquipmentReport(reportId));
     }
 
+    /**
+     * 生成债权分析报告word文件
+     * @param reportId 报告id
+     * @return Boolean
+     */
+    @GetMapping("/generateCRReport/{reportId}")
+    public Result<Boolean> generateCRReport(@PathVariable Long reportId) throws Exception {
+        return Result.build(assetsReportService.generateCRReport(reportId));
+    }
+
 }
 

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

@@ -1,10 +1,12 @@
 package com.dayou;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.excel.ExcelReader;
 import com.alibaba.excel.annotation.ExcelProperty;
 import com.alibaba.excel.read.listener.PageReadListener;
 import com.alibaba.excel.read.metadata.ReadSheet;
+import com.dayou.dto.report.cr.CRReportBaseInfoDTO;
 import com.dayou.utils.EasyExcelUtil;
 import com.dayou.utils.ExcelConvertToHtmlUtil;
 import lombok.Data;
@@ -18,9 +20,11 @@ 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.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 public class ExcelSimpleTests {
 

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

@@ -1,14 +1,17 @@
 package com.dayou.utils;
 
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.excel.ExcelWriter;
 import com.alibaba.excel.write.metadata.WriteSheet;
+import com.dayou.exception.BusinessException;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
@@ -16,6 +19,8 @@ import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
 
+import static com.dayou.result.Status.SYSTEM_ERROR;
+
 
 public class EasyExcelUtil {
 
@@ -336,4 +341,86 @@ public class EasyExcelUtil {
         return valueMap;
     }
 
+    /**
+     * 已知列下标,通过目标值获取行下标
+     *
+     * @param columnIndex 列下标
+     * @param targetValue 目标值
+     * @param filePath    excel文件
+     * @param sheetName   工作表名
+     * @return Integer 返回行下标
+     */
+    public static Integer hasColumnIndexGetRowIndexByValue(int columnIndex, String targetValue, String filePath, String sheetName) {
+        try (FileInputStream fis = new FileInputStream(filePath);
+            Workbook workbook = new XSSFWorkbook(fis)) {
+            Sheet sheet = workbook.getSheet(sheetName); // 获取工作表
+            int rowIndex = -1; // 初始化行号为-1,表示未找到
+
+            // 遍历每一行
+            for (Row row : sheet) {
+                Cell cell = row.getCell(columnIndex); // 获取指定列的单元格
+                if (cell != null) {
+                    String cellValue = cell.getStringCellValue();
+                    if (ObjectUtil.isNotEmpty(cellValue) && targetValue.equals(cellValue.replaceAll(" ",""))) { // 检查是否匹配目标值
+                        rowIndex = row.getRowNum(); // 获取行号
+                        break; // 找到后退出循环
+                    }
+                }
+            }
+            return rowIndex;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 通过单元格下标获取单元格的值
+     *
+     * @param rowIndex    行下标
+     * @param columnIndex 列下标
+     * @param filePath    excel文件
+     * @param sheetName   工作表名
+     * @return String 返回单元格内容
+     */
+    public static String getCellValueByIndex(int rowIndex, int columnIndex, String filePath, String sheetName) {
+        String cellValue = null;
+        try (FileInputStream fis = new FileInputStream(filePath);
+             Workbook workbook = new XSSFWorkbook(fis)) {
+            Sheet sheet = workbook.getSheet(sheetName);
+            Row row = sheet.getRow(rowIndex); // 获取指定行
+            if (row != null) {
+                Cell cell = row.getCell(columnIndex); // 获取指定列的单元格
+                if (cell != null) {
+                    // 根据单元格类型获取值
+                    switch (cell.getCellType()) {
+                        case STRING:
+                            cellValue = cell.getStringCellValue();
+                            break;
+                        case NUMERIC:
+                            if (DateUtil.isCellDateFormatted(cell)) {
+                                cellValue = String.valueOf(cell.getDateCellValue());
+                            } else {
+                                cellValue = String.valueOf(cell.getNumericCellValue());
+                            }
+                            break;
+                        case BOOLEAN:
+                            cellValue = String.valueOf(cell.getBooleanCellValue());
+                            break;
+                        case FORMULA:
+                            cellValue = String.valueOf(cell.getNumericCellValue());
+                            break;
+                    }
+                } else {
+                    throw new BusinessException(sheetName + "中查找的单元格不存在", SYSTEM_ERROR.getCode().toString());
+                }
+            } else {
+                throw new BusinessException(sheetName + "中查找的行不存在!", SYSTEM_ERROR.getCode().toString());
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return cellValue;
+    }
+
 }

+ 8 - 0
dao/src/main/java/com/dayou/mapper/AssetsReportMapper.java

@@ -1,5 +1,6 @@
 package com.dayou.mapper;
 
+import com.dayou.bo.CRReportFillBO;
 import com.dayou.bo.EqptReportFillBO;
 import com.dayou.dto.report.ReportBaseInfoDTO;
 import com.dayou.entity.AssetsReport;
@@ -64,6 +65,13 @@ public interface AssetsReportMapper extends BaseMapper<AssetsReport> {
     EqptReportFillBO getEqptReportFillBO(@Param("reportId") Long reportId);
 
     /**
+     * 根据报告id获取生成债权分析报告word文档所需信息
+     * @param reportId 报告id
+     * @return CRReportFillBO
+     */
+    CRReportFillBO getCRReportFillBO(@Param("reportId") Long reportId);
+
+    /**
      * 获取新增DocumentProduction所需的报告信息
      * @param reportId 报告id
      * @return AssetsReportVO

+ 8 - 0
dao/src/main/resources/mapper/AssetsReportMapper.xml

@@ -93,6 +93,14 @@
         AND report.id = #{reportId}
     </select>
 
+    <!--根据报告id获取生成债权分析报告word文档所需信息-->
+    <select id="getCRReportFillBO" resultType="com.dayou.bo.CRReportFillBO">
+        SELECT production_no AS productionNo, project_id, calculate_id
+        FROM assets_report AS report
+        WHERE report.delete_status = 0
+          AND report.id = #{reportId}
+    </select>
+
     <!--获取新增DocumentProduction所需的报告信息-->
     <select id="getReportInfoForDoc" resultType="com.dayou.vo.AssetsReportVO">
         SELECT report.id AS id,

+ 87 - 0
domain/src/main/java/com/dayou/bo/CRReportFillBO.java

@@ -1,10 +1,97 @@
 package com.dayou.bo;
 
+import com.dayou.dto.report.cr.CRReportBaseInfoDTO;
 import lombok.Data;
 
+import java.math.BigDecimal;
+
 @Data
 public class CRReportFillBO {
 
+    /**
+     * 项目id
+     */
+    private Long projectId;
+
+    /**
+     * 测算表id
+     */
+    private Long calculateId;
+
+    /**
+     * 产品号
+     */
+    private String productionNo;
+
+    /**
+     * 中文日期
+     */
+    private String chineseReportDate;
+
+    /**
+     * 产权持有人拼接后的字符串(多个的情况)
+     */
+    private String ownerCompanyName;
+
+    /**
+     * 贷款本金
+     */
+    private BigDecimal loanPrincipal;
+
+    /**
+     * 贷款本金(千分位格式)
+     */
+    private String loanPrincipalStr;
+
+    /**
+     * 应收利息(含罚息)
+     */
+    private BigDecimal interest;
+
+    /**
+     * 应收利息(含罚息)(千分位格式)
+     */
+    private String interestStr;
+
+    /**
+     * 其他费用
+     */
+    private BigDecimal otherExpenses;
+
+    /**
+     * 其他费用(千分位格式)
+     */
+    private String otherExpensesStr;
+
+    /**
+     * 各项债权合计
+     */
+    private BigDecimal CRTotal;
+
+    /**
+     * 各项债权合计(千分位格式)
+     */
+    private String CRTotalStr;
+
+    /**
+     * 债权综合受偿比率
+     */
+    private String compositeRecoveryRatio;
+
+    /**
+     * 债权清算价值合计
+     */
+    private BigDecimal totalLiquidationValue;
+
+    /**
+     * 债权清算价值合计(千分位格式)
+     */
+    private String totalLiquidationValueStr;
+
+    /**
+     * 债权分析报告基础信息
+     */
+    private CRReportBaseInfoDTO reportBaseInfo;
 
 
 }

+ 251 - 0
domain/src/main/java/com/dayou/dto/report/cr/CRReportBaseInfoDTO.java

@@ -0,0 +1,251 @@
+package com.dayou.dto.report.cr;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class CRReportBaseInfoDTO {
+
+    /**
+     * 债务人
+     */
+    private String debtor;
+
+    /**
+     * 评估目的
+     */
+    private String purpose;
+
+    /**
+     * 评估基准日
+     */
+    private String valuationBasisDate;
+
+    /**
+     * 评估报告日
+     */
+    private String reportDate;
+
+    /**
+     * 报告有效期
+     */
+    private String reportValidity;
+
+    /**
+     * 委托人概况
+     */
+    private ConsignorInfo consignorInfo;
+
+    /**
+     * 债务人信息集合
+     */
+    private List<DebtorInfo> debtorInfoList;
+
+    /**
+     * 保证人(自然人)集合
+     */
+    private List<PersonGuarantor> personGuarantorList;
+
+    /**
+     * 保证人(企业单位)集合
+     */
+    private List<EnterpriseGuarantor> enterpriseGuarantorList;
+
+    /**
+     * 委托人概况
+     */
+    @Data
+    public static class ConsignorInfo {
+
+        /**
+         * 公司名称
+         */
+        private String consignorCompanyName;
+
+        /**
+         * 营业场所
+         */
+        private String businessLocation;
+
+        /**
+         * 统一社会信用代码
+         */
+        private String consignorUnifiedSocialCreditCode;
+
+        /**
+         * 类型
+         */
+        private String consignorType;
+
+        /**
+         * 负责人
+         */
+        private String consignorLegalRepresentative;
+
+        /**
+         * 成立日期
+         */
+        private String consignorEstablishmentDate;
+
+        /**
+         * 经营范围
+         */
+        private String consignorBusinessScope;
+
+    }
+
+    /**
+     * 债务人信息
+     */
+    @Data
+    public static class DebtorInfo {
+
+        /**
+         * 序号
+         */
+        private Integer sort;
+
+        /**
+         * 公司名称
+         */
+        private String debtorName;
+
+        /**
+         * 统一社会信用代码
+         */
+        private String debtorUnifiedSocialCreditCode;
+
+        /**
+         * 住所
+         */
+        private String debtorResidence;
+
+        /**
+         * 注册资本
+         */
+        private String debtorRegisteredCapital;
+
+        /**
+         * 法定代表人
+         */
+        private String debtorLegalRepresentative;
+
+        /**
+         * 企业性质
+         */
+        private String debtorType;
+
+        /**
+         * 成立日期
+         */
+        private String debtorEstablishmentDate;
+
+        /**
+         * 营业期限
+         */
+        private String debtorBusinessOperationPeriod;
+
+        /**
+         * 经营范围
+         */
+        private String debtorBusinessScope;
+
+    }
+
+    /**
+     * 保证人(自然人类型)
+     */
+    @Data
+    public static class  PersonGuarantor {
+
+        /**
+         * 序号
+         */
+        private Integer sort;
+
+        /**
+         * 保证人
+         */
+        private String guarantorName;
+
+        /**
+         * 民族
+         */
+        private String guarantorEthnicGroup;
+
+        /**
+         * 出生年月
+         */
+        private String guarantorDateOfBirth;
+
+        /**
+         * 身份证号码
+         */
+        private String guarantorIdentificationNumber;
+
+        /**
+         * 住址
+         */
+        private String guarantorResidence;
+
+    }
+
+    /**
+     * 保证人(企业单位)
+     */
+    @Data
+    public static class EnterpriseGuarantor {
+
+        /**
+         * 序号
+         */
+        private Integer sort;
+
+        /**
+         * 保证人
+         */
+        private String guarantorName;
+
+        /**
+         * 统一社会信用代码
+         */
+        private String guarantorUnifiedSocialCreditCode;
+
+        /**
+         * 类型
+         */
+        private String guarantorType;
+
+        /**
+         * 注册资本
+         */
+        private String guarantorRegisteredCapital;
+
+        /**
+         * 法定代表人
+         */
+        private String guarantorLegalRepresentative;
+
+        /**
+         * 成立日期
+         */
+        private String guarantorEstablishmentDate;
+
+        /**
+         * 营业期限
+         */
+        private String guarantorBusinessOperationPeriod;
+
+        /**
+         * 住所
+         */
+        private String guarantorResidence;
+
+        /**
+         * 经营范围
+         */
+        private String guarantorBusinessScope;
+    }
+
+}

+ 18 - 0
domain/src/main/java/com/dayou/dto/report/cr/DebtorInfoStrDTO.java

@@ -0,0 +1,18 @@
+package com.dayou.dto.report.cr;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+/**
+ * excel里面每一个债务人信息在一个单元格,这个类用来装读取到的所有字符串信息
+ */
+@Data
+public class DebtorInfoStrDTO {
+
+    /**
+     * DebtorInfo字符串(所有债务人信息在一个excel单元格中,先读出来再分割)
+     */
+    @ExcelProperty("企业基本信息")
+    private String infoStr;
+
+}

+ 24 - 0
domain/src/main/java/com/dayou/dto/report/cr/GuarantorInfoStr.java

@@ -0,0 +1,24 @@
+package com.dayou.dto.report.cr;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+/**
+ * excel里面每一个保证人人信息在一个单元格,这个类用来装读取到的所有字符串信息
+ */
+@Data
+public class GuarantorInfoStr {
+
+    /**
+     * 保证人类型(企业/自然人)
+     */
+    @ExcelProperty("保证人类型")
+    private String guarantorType;
+
+    /**
+     * DebtorInfo字符串(所有债务人信息在一个excel单元格中,先读出来再分割)
+     */
+    @ExcelProperty("保证企业基本信息")
+    private String infoStr;
+
+}

+ 36 - 0
domain/src/main/java/com/dayou/enums/CRReportTmplCode.java

@@ -0,0 +1,36 @@
+package com.dayou.enums;
+
+public enum CRReportTmplCode {
+
+    ANALYSIS_MAIN("ANALYSIS_MAIN", "债权分析报告-框架-主模板"),
+    ANALYSIS_COVER("ANALYSIS_COVER", "债权分析报告-封面-段落模板"),
+    ANALYSIS_CATALOGUE("ANALYSIS_CATALOGUE", "债权分析报告-目录-段落模板"),
+    ANALYSIS_DETAIL("ANALYSIS_DETAIL", "债权分析报告-正文-段落模板"),
+    ANALYSIS_DIGEST("ANALYSIS_DIGEST", "债权分析报告-摘要-段落模板"),
+    ANALYSIS_DEBTOR("ANALYSIS_DEBTOR", "债权分析报告-债务人信息-段落模板"),
+    ANALYSIS_PERSON_GUARANTOR("ANALYSIS_PERSON_GUARANTOR", "债权分析报告-自然人保证人-段落模板"),
+    ANALYSIS_ENTERPRISE_GUARANTOR("ANALYSIS_ENTERPRISE_GUARANTOR", "债权分析报告-企业保证人-段落模板");
+
+    private String code;
+
+    private String name;
+
+    CRReportTmplCode(String code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return name + ":" + code;
+    }
+    
+}

+ 7 - 0
service/src/main/java/com/dayou/service/AssetsReportService.java

@@ -65,4 +65,11 @@ public interface AssetsReportService extends IService<AssetsReport> {
          */
         Boolean generateEquipmentReport(Long reportId) throws Exception;
 
+        /**
+         * 生成债权分析报告word文件
+         * @param reportId 报告id
+         * @return Boolean
+         */
+        Boolean generateCRReport(Long reportId) throws Exception;
+
 }

+ 351 - 14
service/src/main/java/com/dayou/service/impl/AssetsReportServiceImpl.java

@@ -2,27 +2,33 @@ package com.dayou.service.impl;
 
 import cn.dev33.satoken.stp.StpUtil;
 import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelReader;
+import com.alibaba.excel.read.listener.PageReadListener;
+import com.alibaba.excel.read.metadata.ReadSheet;
 import com.aspose.words.Document;
 import com.aspose.words.DocumentBuilder;
 import com.aspose.words.ImportFormatMode;
+import com.dayou.bo.CRReportFillBO;
 import com.dayou.bo.EqptReportFillBO;
 import com.dayou.config.FileNetConfig;
 import com.dayou.dto.report.ReportBaseInfoDTO;
+import com.dayou.dto.report.cr.CRReportBaseInfoDTO;
+import com.dayou.dto.report.cr.DebtorInfoStrDTO;
+import com.dayou.dto.report.cr.GuarantorInfoStr;
 import com.dayou.dto.report.equipment.EqptReportBaseInfoDTO;
 import com.dayou.entity.AssetsReport;
 import com.dayou.entity.DocumentProduction;
 import com.dayou.entity.TmplAssetReport;
 import com.dayou.entity.TmplAssetReportSection;
+import com.dayou.exception.BusinessException;
 import com.dayou.mapper.AssetsReportMapper;
 import com.dayou.mapper.TmplAssetReportMapper;
 import com.dayou.mapper.TmplAssetReportSectionMapper;
 import com.dayou.service.AssetsReportService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.dayou.service.DocumentProductionService;
-import com.dayou.utils.ArabicToChineseUtil;
-import com.dayou.utils.AsposeWordUtil;
-import com.dayou.utils.DataUtil;
-import com.dayou.utils.DateToChinese;
+import com.dayou.utils.*;
 import com.dayou.vo.AssetsReportVO;
 import com.dayou.vo.report.AssetsReportProgressVO;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -30,18 +36,23 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.io.*;
+import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.text.DecimalFormat;
-import java.util.Date;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
 import org.springframework.transaction.annotation.Transactional;
 
+import static com.dayou.enums.CRReportTmplCode.*;
+import static com.dayou.enums.DocumentType.CR_REPORT;
 import static com.dayou.enums.DocumentType.EQPT_REPORT;
 import static com.dayou.enums.EqptReportTmplCode.*;
+import static com.dayou.utils.EasyExcelUtil.getCellValueByIndex;
+import static com.dayou.utils.EasyExcelUtil.hasColumnIndexGetRowIndexByValue;
 
 /**
  * <p>
@@ -91,7 +102,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
         assetsReport.setCreateUserId(StpUtil.getLoginIdAsLong());
         if (ObjectUtil.isNull(assetsReport.getId())) {
             return this.save(assetsReport);
-        }else {
+        } else {
             return this.updateById(assetsReport);
         }
     }
@@ -109,6 +120,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
 
     /**
      * 根据项目id获取未完成报告的进度信息
+     *
      * @param projectId 项目id
      * @return AssetsReportProgressVO
      */
@@ -119,6 +131,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
 
     /**
      * 更新报告基础信息
+     *
      * @param reportBaseInfoDTO dto
      * @return Boolean
      */
@@ -134,6 +147,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
 
     /**
      * 根据报告id获取报告基础信息
+     *
      * @param reportId 报告id
      * @return String
      */
@@ -144,6 +158,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
 
     /**
      * 生成机器设备报告word文件
+     *
      * @param reportId 报告id
      * @return Boolean
      */
@@ -160,16 +175,16 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
         }
         // 将账面、评估的原值、净值处理成千分位格式
         DecimalFormat decimalFormat = new DecimalFormat("#,##0.00");
-        if(eqptReportFillBO.getBookOriginalValue() != null) {
+        if (eqptReportFillBO.getBookOriginalValue() != null) {
             eqptReportFillBO.setBookOriginalValueStr(decimalFormat.format(eqptReportFillBO.getBookOriginalValue()));
         }
-        if(eqptReportFillBO.getBookNetValue() != null) {
+        if (eqptReportFillBO.getBookNetValue() != null) {
             eqptReportFillBO.setBookNetValueStr(decimalFormat.format(eqptReportFillBO.getBookNetValue()));
         }
-        if(eqptReportFillBO.getEvaluateOriginalValue() != null) {
+        if (eqptReportFillBO.getEvaluateOriginalValue() != null) {
             eqptReportFillBO.setEvaluateOriginalValueStr(decimalFormat.format(eqptReportFillBO.getEvaluateOriginalValue().setScale(0, RoundingMode.HALF_UP)));
         }
-        if(eqptReportFillBO.getEvaluateNetValue() != null) {
+        if (eqptReportFillBO.getEvaluateNetValue() != null) {
             eqptReportFillBO.setEvaluateNetValueStr(decimalFormat.format(eqptReportFillBO.getEvaluateNetValue().setScale(0, RoundingMode.HALF_UP)));
         }
 
@@ -204,7 +219,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
 
 
         // 第二步:设置摘要内容
-        // 获取正文
+        // 获取摘要模板
         TmplAssetReportSection digestTmpl = reportSectionMapper.getTmplByCode(DIGEST.getCode());
         // 封面模板文件位置
         String digestTmplPath = fileNetConfig.getBaseDir() + digestTmpl.getSectionFileUrl() + digestTmpl.getSectionFileName();
@@ -216,7 +231,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
         InputStream digestIs = new ByteArrayInputStream(resultdigestByte);
 
         // 第三步:设置正文内容
-        // 获取正文
+        // 获取正文模板
         TmplAssetReportSection detailTmpl = reportSectionMapper.getTmplByCode(DETAIL.getCode());
         // 封面模板文件位置
         String detailTmplPath = fileNetConfig.getBaseDir() + detailTmpl.getSectionFileUrl() + detailTmpl.getSectionFileName();
@@ -242,7 +257,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
         String consignorTmplPath = fileNetConfig.getBaseDir() + consignorTmpl.getSectionFileUrl() + consignorTmpl.getSectionFileName();
         // 插入时会导致先插入的排在后插入的后面,所以把顺序反转一下
         for (int i = eqptReportFillBO.getEqptReportBaseInfo().getConsignorInfos().size(); i > 0; i--) {
-            EqptReportBaseInfoDTO.ConsignorInfo consignorInfo = eqptReportFillBO.getEqptReportBaseInfo().getConsignorInfos().get(i -1);
+            EqptReportBaseInfoDTO.ConsignorInfo consignorInfo = eqptReportFillBO.getEqptReportBaseInfo().getConsignorInfos().get(i - 1);
             consignorInfo.setSort(i);
             consignorInfo.setSortUppercase(ArabicToChineseUtil.int2chineseNum(i));
             // 读取模板文件
@@ -357,7 +372,7 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
         attachmentsIs.close();
 
         // 更新报告进度
-        assetsReportMapper.updateReportProgress(reportId,"FINISHED");
+        assetsReportMapper.updateReportProgress(reportId, "FINISHED");
 
         // 保存文档信息到数据库
         // 设置文档信息
@@ -382,4 +397,326 @@ public class AssetsReportServiceImpl extends ServiceImpl<AssetsReportMapper, Ass
                 .setBusinessSubId(reportId);
         return documentProductionService.saveDocumentProduction(documentProduction);
     }
+
+    /**
+     * 生成债权分析报告
+     *
+     * @param reportId 报告id
+     * @return Boolean
+     */
+    @Override
+    public Boolean generateCRReport(Long reportId) throws Exception {
+        // 获取与设置基础报告信息
+        ObjectMapper objectMapper = new ObjectMapper();
+        CRReportFillBO crReportFillBO = assetsReportMapper.getCRReportFillBO(reportId);
+        CRReportBaseInfoDTO crReportBaseInfoDTO = objectMapper.readValue(getReportBaseInfo(reportId), CRReportBaseInfoDTO.class);
+        // 获取测算表
+        DocumentProduction calculateDoc = documentProductionService.getDocProdByBusinessId(crReportFillBO.getProjectId(), crReportFillBO.getCalculateId());
+        // 读取债务人信息
+        List<DebtorInfoStrDTO> debtorInfoStrList = new ArrayList<>();
+        ExcelReader calculateReader = EasyExcel.read(fileNetConfig.getBaseDir() + calculateDoc.getDocUrl()).build();
+        ReadSheet readDebtor = EasyExcel.readSheet("2.债务人分析明细表").head(DebtorInfoStrDTO.class).registerReadListener(new PageReadListener<DebtorInfoStrDTO>(debtorInfoStrList::addAll)).headRowNumber(4).build();
+        calculateReader.read(readDebtor);
+        // 去掉空的数据(excel表中因为有合计行等等,所以可能读出来空行)
+        debtorInfoStrList = debtorInfoStrList.stream()
+                .filter(obj -> obj.getInfoStr() != null)
+                .collect(Collectors.toList());
+        // 处理债务人信息
+        List<CRReportBaseInfoDTO.DebtorInfo> debtorInfoList = new ArrayList<>();
+        for (DebtorInfoStrDTO infoStr : debtorInfoStrList) {
+            CRReportBaseInfoDTO.DebtorInfo debtorInfo = new CRReportBaseInfoDTO.DebtorInfo();
+            String debtorStr = infoStr.getInfoStr();
+            debtorInfo.setDebtorName(debtorStr.substring(debtorStr.indexOf("公司名称:") + "公司名称:".length(), debtorStr.indexOf("\n")));
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorUnifiedSocialCreditCode(debtorStr.substring(debtorStr.indexOf("统一社会信用代码:") + "统一社会信用代码:".length(), debtorStr.indexOf("\n")));
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorResidence(debtorStr.substring(debtorStr.indexOf("住所:") + "住所:".length(), debtorStr.indexOf("\n")));
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorRegisteredCapital(debtorStr.substring(debtorStr.indexOf("注册资本:") + "注册资本:".length(), debtorStr.indexOf("\n")));
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorLegalRepresentative(debtorStr.substring(debtorStr.indexOf("法定代表人:") + "法定代表人:".length(), debtorStr.indexOf("\n")));
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorType(debtorStr.substring(debtorStr.indexOf("企业类型:") + "企业类型:".length(), debtorStr.indexOf("\n")));
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorEstablishmentDate(debtorStr.substring(debtorStr.indexOf("成立日期:") + "成立日期:".length(), debtorStr.indexOf("\n")));
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorBusinessOperationPeriod(debtorStr.indexOf("营业期限:") > 0 ? debtorStr.substring(debtorStr.indexOf("营业期限:") + "营业期限:".length(), debtorStr.indexOf("\n")) : null);
+            debtorStr = debtorStr.replaceFirst("\n", "");
+            debtorInfo.setDebtorBusinessScope(debtorStr.substring(debtorStr.indexOf("经营范围:") + "经营范围:".length()));
+            // 设置债务人信息
+            debtorInfoList.add(debtorInfo);
+        }
+        crReportBaseInfoDTO.setDebtorInfoList(debtorInfoList);
+
+        // 读取债务人信息
+        List<GuarantorInfoStr> guarantorInfoStrList = new ArrayList<>();
+        ReadSheet readGuarantor = EasyExcel.readSheet("3.保证人分析明细表").head(GuarantorInfoStr.class).registerReadListener(new PageReadListener<GuarantorInfoStr>(guarantorInfoStrList::addAll)).headRowNumber(4).build();
+        calculateReader.read(readGuarantor);
+        // 去掉空的数据(excel表中因为有合计行等等,所以可能读出来空行)
+        guarantorInfoStrList = guarantorInfoStrList.stream()
+                .filter(obj -> obj.getInfoStr() != null)
+                .collect(Collectors.toList());
+        // 处理保证人信息
+        List<CRReportBaseInfoDTO.PersonGuarantor> personGuarantorList = new ArrayList<>();
+        List<CRReportBaseInfoDTO.EnterpriseGuarantor> enterpriseGuarantorList = new ArrayList<>();
+        for (GuarantorInfoStr infoStr : guarantorInfoStrList) {
+            if (ObjectUtil.isNull(infoStr.getGuarantorType())) {
+                throw new BusinessException("债权测算表_3.保证人分析明细表_保证人类型有为空的行,请检查后并重新上传后再试!");
+            }
+            if (infoStr.getGuarantorType().equals("企业")) {
+                CRReportBaseInfoDTO.EnterpriseGuarantor enterpriseGuarantor = new CRReportBaseInfoDTO.EnterpriseGuarantor();
+                String guarantorStr = infoStr.getInfoStr();
+                enterpriseGuarantor.setGuarantorName(guarantorStr.substring(guarantorStr.indexOf("公司名称:") + "公司名称:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorUnifiedSocialCreditCode(guarantorStr.substring(guarantorStr.indexOf("统一社会信用代码:") + "统一社会信用代码:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorType(guarantorStr.substring(guarantorStr.indexOf("企业类型:") + "企业类型:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorRegisteredCapital(guarantorStr.substring(guarantorStr.indexOf("注册资本:") + "注册资本:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorLegalRepresentative(guarantorStr.substring(guarantorStr.indexOf("法定代表人:") + "法定代表人:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorEstablishmentDate(guarantorStr.substring(guarantorStr.indexOf("成立日期:") + "成立日期:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorBusinessOperationPeriod(guarantorStr.indexOf("营业期限:") > 0 ? guarantorStr.substring(guarantorStr.indexOf("营业期限:") + "营业期限:".length(), guarantorStr.indexOf("\n")) : null);
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorResidence(guarantorStr.substring(guarantorStr.indexOf("住所:") + "住所:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                enterpriseGuarantor.setGuarantorBusinessScope(guarantorStr.substring(guarantorStr.indexOf("经营范围:") + "经营范围:".length()));
+                // 设置保证人信息
+                enterpriseGuarantorList.add(enterpriseGuarantor);
+            } else if (infoStr.getGuarantorType().equals("自然人")) {
+                CRReportBaseInfoDTO.PersonGuarantor personGuarantor = new CRReportBaseInfoDTO.PersonGuarantor();
+                String guarantorStr = infoStr.getInfoStr();
+                personGuarantor.setGuarantorName(guarantorStr.substring(guarantorStr.indexOf("姓名:") + "姓名:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                personGuarantor.setGuarantorEthnicGroup(guarantorStr.substring(guarantorStr.indexOf("民族:") + "民族:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                personGuarantor.setGuarantorDateOfBirth(guarantorStr.substring(guarantorStr.indexOf("出生年月:") + "出生年月:".length(), guarantorStr.indexOf("\n")));
+                guarantorStr = guarantorStr.replaceFirst("\n", "");
+                personGuarantor.setGuarantorIdentificationNumber(guarantorStr.substring(guarantorStr.indexOf("身份证号码:") + "身份证号码:".length(), guarantorStr.indexOf("\n")));
+                personGuarantor.setGuarantorResidence(guarantorStr.substring(guarantorStr.indexOf("住址:") + "住址:".length()));
+                // 设置保证人信息
+                personGuarantorList.add(personGuarantor);
+            }
+        }
+        crReportBaseInfoDTO.setPersonGuarantorList(personGuarantorList);
+        crReportBaseInfoDTO.setEnterpriseGuarantorList(enterpriseGuarantorList);
+
+        crReportFillBO.setReportBaseInfo(crReportBaseInfoDTO);
+
+        // 将评估报告日转换为中文并设置到中文评估报告日中
+        crReportFillBO.setChineseReportDate(DateToChinese.dateStrConvertChinese(crReportFillBO.getReportBaseInfo().getReportDate()));
+
+        // 获取汇总表合计行的下标(用来获取债权汇总表的合计信息)
+        Integer rowindex = hasColumnIndexGetRowIndexByValue(1,"合计",fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总表");
+        if (ObjectUtil.isNotNull(rowindex)) {
+            // 千分位格式处理
+            DecimalFormat decimalFormat = new DecimalFormat("#,##0.00");
+            // 设置贷款本金
+            crReportFillBO.setLoanPrincipal(new BigDecimal(getCellValueByIndex(rowindex, 2, fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总表")));
+            crReportFillBO.setLoanPrincipalStr(decimalFormat.format(crReportFillBO.getLoanPrincipal()));
+            // 设置应收利息(含罚息)
+            crReportFillBO.setInterest(new BigDecimal(getCellValueByIndex(rowindex, 3, fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总表")));
+            crReportFillBO.setInterestStr(decimalFormat.format(crReportFillBO.getInterest()));
+            // 设置其他费用
+            crReportFillBO.setOtherExpenses(new BigDecimal(getCellValueByIndex(rowindex, 4, fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总表")));
+            crReportFillBO.setOtherExpensesStr(decimalFormat.format(crReportFillBO.getOtherExpenses()));
+            // 设置各项债权合计
+            crReportFillBO.setCRTotal(new BigDecimal(getCellValueByIndex(rowindex, 5, fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总表")));
+            crReportFillBO.setCRTotalStr(decimalFormat.format(crReportFillBO.getCRTotal()));
+
+            // 设置债权清算价值
+            crReportFillBO.setTotalLiquidationValue(new BigDecimal(getCellValueByIndex(rowindex, 11, fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总表")));
+            crReportFillBO.setTotalLiquidationValueStr(decimalFormat.format(crReportFillBO.getTotalLiquidationValue()));
+        }
+
+        // 获取汇总万元表合计行的下标
+        Integer allSheetRowIndex = hasColumnIndexGetRowIndexByValue(1,"合计",fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总万元表");
+        if (ObjectUtil.isNotNull(allSheetRowIndex)) {
+            // 设置债权综合受偿比率
+            double compositeRecoveryRatioDouble = Double.parseDouble(getCellValueByIndex(allSheetRowIndex, 13, fileNetConfig.getBaseDir() + calculateDoc.getDocUrl(), "汇总万元表"));
+            crReportFillBO.setCompositeRecoveryRatio(String.valueOf(compositeRecoveryRatioDouble * 100));
+        }
+
+        // 第一步:设置封面
+        // 获取封面模板
+        TmplAssetReportSection coverTmpl = reportSectionMapper.getTmplByCode(ANALYSIS_COVER.getCode());
+        // 封面模板文件位置
+        String coverTmplPath = fileNetConfig.getBaseDir() + coverTmpl.getSectionFileUrl() + coverTmpl.getSectionFileName();
+        // 读取模板文件
+        byte[] coverTmplByte = Files.readAllBytes(Paths.get(coverTmplPath));
+        // 获取填充数据后的文件
+        byte[] resultCoverByte = AsposeWordUtil.fillWordDataByMap(coverTmplByte, DataUtil.objToMap(crReportFillBO));
+        // 将封面的字节流转成输入流以供后续使用
+        InputStream coverIs = new ByteArrayInputStream(resultCoverByte);
+
+        // 获取摘要模板
+        TmplAssetReportSection digestTmpl = reportSectionMapper.getTmplByCode(ANALYSIS_DIGEST.getCode());
+        // 封面模板文件位置
+        String digestTmplPath = fileNetConfig.getBaseDir() + digestTmpl.getSectionFileUrl() + digestTmpl.getSectionFileName();
+        // 读取模板文件
+        byte[] digestTmplByte = Files.readAllBytes(Paths.get(digestTmplPath));
+        // 获取填充数据后的文件
+        byte[] resultdigestByte = AsposeWordUtil.fillWordDataByMap(digestTmplByte, DataUtil.objToMap(crReportFillBO));
+        // 将摘要的字节流转成输入流以供后续使用
+        InputStream digestIs = new ByteArrayInputStream(resultdigestByte);
+
+        // 第三步:设置正文内容
+        // 获取正文模板
+        TmplAssetReportSection detailTmpl = reportSectionMapper.getTmplByCode(ANALYSIS_DETAIL.getCode());
+        // 封面模板文件位置
+        String detailTmplPath = fileNetConfig.getBaseDir() + detailTmpl.getSectionFileUrl() + detailTmpl.getSectionFileName();
+        // 读取模板文件
+        byte[] detailTmplByte = Files.readAllBytes(Paths.get(detailTmplPath));
+        // 获取填充数据后的文件
+        byte[] resultDetailByte = AsposeWordUtil.fillWordDataByMap(detailTmplByte, DataUtil.objToMap(crReportFillBO));
+        // 将正文的字节流转成输入流以供后续使用
+        InputStream detailIs = new ByteArrayInputStream(resultDetailByte);
+
+        // 将正文读取出来
+        Document detailDoc = new Document(detailIs);
+
+        // 第四步:设置债务人信息到正文
+        // 获取债务人信息模板
+        TmplAssetReportSection debtorTmpl = reportSectionMapper.getTmplByCode(ANALYSIS_DEBTOR.getCode());
+        // 债务人信息模板文件位置
+        String debtorTmplPath = fileNetConfig.getBaseDir() + debtorTmpl.getSectionFileUrl() + debtorTmpl.getSectionFileName();
+        // 插入时会导致先插入的排在后插入的后面,所以把顺序反转一下
+        for (int i = crReportFillBO.getReportBaseInfo().getDebtorInfoList().size(); i > 0; i--) {
+            CRReportBaseInfoDTO.DebtorInfo debtorInfo = crReportFillBO.getReportBaseInfo().getDebtorInfoList().get(i - 1);
+            debtorInfo.setSort(i);
+            // 读取模板文件
+            byte[] debtorTmplByte = Files.readAllBytes(Paths.get(debtorTmplPath));
+            // 获取填充数据后的文件
+            byte[] resultConsignorByte = AsposeWordUtil.fillWordDataByDomain(debtorTmplByte, debtorInfo);
+            InputStream debtorIs = new ByteArrayInputStream(resultConsignorByte);
+            detailDoc = AsposeWordUtil.insertDocumentAfterBookMark(detailDoc, new Document(debtorIs), "insertDebtor", false, false);
+            debtorIs.close();
+        }
+
+        // 第五步:设置保证人信息到正文
+        // 获取企业保证人信息模板
+        TmplAssetReportSection enterpriseGuarantorTmpl = reportSectionMapper.getTmplByCode(ANALYSIS_ENTERPRISE_GUARANTOR.getCode());
+        // 企业保证人信息模板文件位置
+        String enterpriseGuarantorTmplPath = fileNetConfig.getBaseDir() + enterpriseGuarantorTmpl.getSectionFileUrl() + enterpriseGuarantorTmpl.getSectionFileName();
+        // 插入时会导致先插入的排在后插入的后面,所以把顺序反转一下
+        int enterpriseCount = crReportFillBO.getReportBaseInfo().getEnterpriseGuarantorList().size();
+        for (int i = enterpriseCount; i > 0; i--) {
+            CRReportBaseInfoDTO.EnterpriseGuarantor enterpriseGuarantor = crReportFillBO.getReportBaseInfo().getEnterpriseGuarantorList().get(i - 1);
+            enterpriseGuarantor.setSort(i);
+            // 读取模板文件
+            byte[] enterpriseGuarantorTmplByte = Files.readAllBytes(Paths.get(enterpriseGuarantorTmplPath));
+            // 获取填充数据后的文件
+            byte[] resultEnterpriseGuarantorByte = AsposeWordUtil.fillWordDataByDomain(enterpriseGuarantorTmplByte, enterpriseGuarantor);
+            InputStream enterpriseGuarantorIs = new ByteArrayInputStream(resultEnterpriseGuarantorByte);
+            detailDoc = AsposeWordUtil.insertDocumentAfterBookMark(detailDoc, new Document(enterpriseGuarantorIs), "insertEnterpriseGuarantor", false, false);
+            enterpriseGuarantorIs.close();
+        }
+        // 获取自然人保证人信息模板
+        TmplAssetReportSection personGuarantorTmpl = reportSectionMapper.getTmplByCode(ANALYSIS_PERSON_GUARANTOR.getCode());
+        // 自然人保证人信息模板文件位置
+        String personGuarantorTmplPath = fileNetConfig.getBaseDir() + personGuarantorTmpl.getSectionFileUrl() + personGuarantorTmpl.getSectionFileName();
+        // 插入时会导致先插入的排在后插入的后面,所以把顺序反转一下(企业和自然人用同一个序号排序)
+        for (int i = crReportFillBO.getReportBaseInfo().getEnterpriseGuarantorList().size() + enterpriseCount; i > enterpriseCount; i--) {
+            CRReportBaseInfoDTO.PersonGuarantor personGuarantor = crReportFillBO.getReportBaseInfo().getPersonGuarantorList().get(enterpriseCount - 1);
+            personGuarantor.setSort(i);
+            // 读取模板文件
+            byte[] personGuarantorTmplByte = Files.readAllBytes(Paths.get(personGuarantorTmplPath));
+            // 获取填充数据后的文件
+            byte[] resultPersonGuarantorByte = AsposeWordUtil.fillWordDataByDomain(personGuarantorTmplByte, personGuarantor);
+            InputStream personGuarantorIs = new ByteArrayInputStream(resultPersonGuarantorByte);
+            detailDoc = AsposeWordUtil.insertDocumentAfterBookMark(detailDoc, new Document(personGuarantorIs), "insertPersonGuarantor", false, false);
+            personGuarantorIs.close();
+        }
+
+        // 第六步:设置主体框架
+        // 获取框架模板
+        TmplAssetReport mainTmpl = tmplAssetReportMapper.getTmplByCode(ANALYSIS_MAIN.getCode());
+        // 框架生成后文件位置
+        String docUrl = fileNetConfig.getAssetOutputReportPath() + System.currentTimeMillis() + "_债权分析报告.docx";
+        String mainPath = fileNetConfig.getBaseDir() + docUrl;
+        // 框架模板文件位置
+        String mainTmplPath = fileNetConfig.getBaseDir() + mainTmpl.getFileUrl() + mainTmpl.getFileName();
+
+        // 读取模板文件
+        byte[] mainTmplByte = Files.readAllBytes(Paths.get(mainTmplPath));
+        // 获取填充数据后的文件
+        byte[] resultMainByte = AsposeWordUtil.fillWordDataByMap(mainTmplByte, DataUtil.objToMap(crReportFillBO));
+        InputStream mainIs = new ByteArrayInputStream(resultMainByte);
+        // 将框架读取出来
+        Document mainDoc = new Document(mainIs);
+        // 将摘要插入到框架
+        AsposeWordUtil.insertDocumentAfterBookMark(mainDoc, new Document(digestIs), "insertDigest", false, false);
+        // 将正文插入到框架
+        AsposeWordUtil.insertDocumentAfterBookMark(mainDoc, detailDoc, "insertDetail", false, false);
+
+        // 获取目录模板
+        TmplAssetReportSection catalogueTmpl = reportSectionMapper.getTmplByCode(ANALYSIS_CATALOGUE.getCode());
+        // 目录模板文件位置
+        String catalogueTmplPath = fileNetConfig.getBaseDir() + catalogueTmpl.getSectionFileUrl() + catalogueTmpl.getSectionFileName();
+        // 将框架读取出来
+        Document catalogueDoc = new Document(catalogueTmplPath);
+        // 将目录插入到框架
+        AsposeWordUtil.insertDocumentAfterBookMark(mainDoc, catalogueDoc, "insertCatalogue", false, false);
+
+        // 第七步:删除预留书签插入位置的空白行
+        DocumentBuilder mainBuilder = new DocumentBuilder(mainDoc);
+        mainBuilder.moveToBookmark("insertDetail");
+        mainBuilder.getCurrentParagraph().remove();
+        mainBuilder.moveToBookmark("insertDigest");
+        mainBuilder.getCurrentParagraph().remove();
+        mainBuilder.moveToBookmark("insertCatalogue");
+        mainBuilder.getCurrentParagraph().remove();
+        mainBuilder.moveToBookmark("insertDebtor");
+        mainBuilder.getCurrentParagraph().remove();
+        mainBuilder.moveToBookmark("insertEnterpriseGuarantor");
+        mainBuilder.getCurrentParagraph().remove();
+        mainBuilder.moveToBookmark("insertPersonGuarantor");
+        mainBuilder.getCurrentParagraph().remove();
+
+        // 第八步:生成并保存文档
+        // 将封面和文档进行拼接
+        Document coverDoc = new Document(coverIs);
+        coverDoc.appendDocument(mainDoc, ImportFormatMode.KEEP_SOURCE_FORMATTING);
+        // 更新目录页码
+        AsposeWordUtil.updateCatalogueLink(coverDoc);
+
+        // 开启word的修订模式(修改留痕)
+        coverDoc.setTrackRevisions(true);
+
+        coverDoc.save(mainPath);
+
+        // 关闭各个段落的流
+        coverIs.close();
+        digestIs.close();
+        detailIs.close();
+        mainIs.close();
+
+        // 更新报告进度
+        assetsReportMapper.updateReportProgress(reportId, "FINISHED");
+
+        // 保存文档信息到数据库
+        // 设置文档信息
+        AssetsReportVO assetsReportVO = assetsReportMapper.getReportInfoForDoc(reportId);
+        DocumentProduction documentProduction = documentProductionService.getDocProdByBusinessId(assetsReportVO.getProjectId(), reportId);
+
+        if (ObjectUtil.isNotNull(documentProduction)) { // 判断文档信息是否为空,不为空说明以前生成过文档,则更新文档版本号
+            documentProduction.setDocVersion(documentProduction.getDocVersion() + 1);
+        } else { // 为空重新new对象
+            documentProduction = new DocumentProduction();
+        }
+        // 设置文档信息
+        documentProduction.setBusinessType("ASSETS")
+                .setBusinessId(assetsReportVO.getProjectId())
+                .setDocType(CR_REPORT.getName())
+                .setDocName(assetsReportVO.getReportName() + ".docx")
+                .setConsignor(assetsReportVO.getPrincipal())
+                .setDocUrl(docUrl)
+                .setCreateUserId(StpUtil.getLoginIdAsLong())
+                .setBusinessCate(assetsReportVO.getProjectTypeName())
+                .setDocNo(ObjectUtil.isNotNull(crReportFillBO.getProductionNo()) ? crReportFillBO.getProductionNo() : "")
+                .setBusinessSubId(reportId);
+        return documentProductionService.saveDocumentProduction(documentProduction);
+    }
 }