Преглед изворни кода

feat:报告解析的状态记录

wangzaijun пре 6 месеци
родитељ
комит
495158e367

+ 31 - 0
service-base/src/main/java/com/simuwang/base/common/exception/ReportParseException.java

@@ -0,0 +1,31 @@
+package com.simuwang.base.common.exception;
+
+import com.smppw.common.pojo.enums.status.StatusCode;
+
+/**
+ * @author wangzaijun
+ * @date 2024/10/11 14:10
+ * @description 报告解析的异常
+ */
+public class ReportParseException extends RuntimeException {
+    private final int code;
+    private final String msg;
+
+    public ReportParseException(StatusCode statusCode) {
+        this(statusCode.getCode(), statusCode.getMsg());
+    }
+
+    public ReportParseException(int code, String msg) {
+        super(msg);
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 2 - 2
service-base/src/main/java/com/simuwang/base/pojo/dto/report/PythonResult.java

@@ -6,11 +6,11 @@ import lombok.Setter;
 /**
  * @author wangzaijun
  * @date 2024/10/10 14:08
- * @description python接口请求的返回结构
+ * @description 报告解析结果
  */
 @Setter
 @Getter
-public class PythonResult<T extends ReportData> {
+public class ParseResult<T extends ReportData> {
     private Integer status;
 
     private String msg;

+ 30 - 0
service-base/src/main/java/com/simuwang/base/pojo/dto/report/ReportParseStatus.java

@@ -0,0 +1,30 @@
+package com.simuwang.base.pojo.dto.report;
+
+import com.smppw.common.pojo.enums.status.StatusCode;
+
+public enum ReportParseStatus implements StatusCode {
+    PARSE_FAIL(21000, "定期报告解析错误:{}"),
+    NOT_A_REPORT(21001, "不是定期报告"),
+    REPORT_IS_SCAN(21002, "报告为扫描件"),
+    NO_SUPPORT_TEMPLATE(21003, "不支持的报告文件格式"),
+    NOT_A_FIXED_FORMAT(21004, "不是基协统一格式"),
+    PARSE_FUND_INFO_FAIL(21010, "没有解析到报告中的基金基本信息"),
+    ;
+    private final int code;
+    private final String msg;
+
+    ReportParseStatus(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    @Override
+    public int getCode() {
+        return this.code;
+    }
+
+    @Override
+    public String getMsg() {
+        return this.msg;
+    }
+}

+ 2 - 2
service-daq/src/main/java/com/simuwang/daq/components/PythonReportConverter.java

@@ -22,8 +22,8 @@ import java.util.Set;
  */
 public class PythonReportConverter {
     @SuppressWarnings("unchecked")
-    public static <T extends ReportData> PythonResult<T> convert(JSONObject jsonObject, ReportType type) {
-        PythonResult<T> result = new PythonResult<>();
+    public static <T extends ReportData> ParseResult<T> convert(JSONObject jsonObject, ReportType type) {
+        ParseResult<T> result = new ParseResult<>();
         if (jsonObject == null) {
             return result;
         }

+ 2 - 1
service-daq/src/main/java/com/simuwang/daq/components/report/parser/ReportParser.java

@@ -1,5 +1,6 @@
 package com.simuwang.daq.components.report.parser;
 
+import com.simuwang.base.common.exception.ReportParseException;
 import com.simuwang.base.pojo.dto.report.ReportData;
 import com.simuwang.base.pojo.dto.report.ReportParserParams;
 
@@ -28,5 +29,5 @@ public interface ReportParser<T extends ReportData> {
      * @return 解析结果
      * @throws IOException 文件io异常
      */
-    T parse(ReportParserParams params) throws IOException;
+    T parse(ReportParserParams params) throws IOException, ReportParseException;
 }

+ 7 - 1
service-daq/src/main/java/com/simuwang/daq/components/report/parser/ReportParserFactory.java

@@ -3,7 +3,9 @@ package com.simuwang.daq.components.report.parser;
 import cn.hutool.core.map.MapUtil;
 import com.simuwang.base.common.enums.ReportParserFileType;
 import com.simuwang.base.common.enums.ReportType;
+import com.simuwang.base.common.exception.ReportParseException;
 import com.simuwang.base.pojo.dto.report.ReportData;
+import com.simuwang.base.pojo.dto.report.ReportParseStatus;
 import org.springframework.stereotype.Component;
 
 import java.util.Map;
@@ -21,6 +23,10 @@ public class ReportParserFactory {
     @SuppressWarnings("unchecked")
     public <T extends ReportData> ReportParser<T> getInstance(ReportType reportType, ReportParserFileType reportParserFileType) {
         String beanName = ReportParserConstant.REPORT_PARSER_BEAN_MAP.getOrDefault(reportType, MapUtil.empty()).get(reportParserFileType);
-        return (ReportParser<T>) REPORT_WRITER_MAP.getOrDefault(beanName, DEFAULT);
+        ReportParser<? extends ReportData> reportParser = REPORT_WRITER_MAP.get(beanName);
+        if (reportParser == null) {
+            throw new ReportParseException(ReportParseStatus.NO_SUPPORT_TEMPLATE);
+        }
+        return (ReportParser<T>) reportParser;
     }
 }

+ 37 - 12
service-daq/src/main/java/com/simuwang/daq/components/report/parser/pdf/AbstractPDReportParser.java

@@ -1,11 +1,12 @@
 package com.simuwang.daq.components.report.parser.pdf;
 
 import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import com.simuwang.base.common.conts.Constants;
 import com.simuwang.base.common.enums.ReportType;
-import com.simuwang.base.common.exception.APIException;
+import com.simuwang.base.common.exception.ReportParseException;
 import com.simuwang.base.mapper.EmailFieldMappingMapper;
 import com.simuwang.base.pojo.dto.report.*;
 import com.simuwang.daq.components.CustomPDFTextStripper;
@@ -49,7 +50,10 @@ public abstract class AbstractPDReportParser<T extends ReportData> extends Abstr
     }
 
     @Override
-    public T parse(ReportParserParams params) throws IOException {
+    public T parse(ReportParserParams params) throws IOException, ReportParseException {
+        // 先初始化为null
+        this.fundInfoTable = null;
+        this.textList = null;
         // 初始化
         this.init();
         // 解析报告和表格
@@ -60,6 +64,13 @@ public abstract class AbstractPDReportParser<T extends ReportData> extends Abstr
             String text = stripper.getText(document).replace(Constants.WATERMARK_REPLACE, StrUtil.EMPTY);
             this.textList = StrUtil.split(text, System.lineSeparator());
             this.textList.removeIf(StrUtil::isBlank);
+            if (this.textList.isEmpty()) {
+                throw new ReportParseException(ReportParseStatus.REPORT_IS_SCAN);
+            }
+            // 报告名称和类型一般在第一第二行
+            if (this.matchReportType(this.textList.get(0)) == null && this.matchReportType(this.textList.get(1)) == null) {
+                throw new ReportParseException(ReportParseStatus.NOT_A_REPORT);
+            }
             // 解析所有表格(单元格字符去水印)
             List<Table> tables = ListUtil.list(true);
             SpreadsheetExtractionAlgorithm extractionAlgorithm = new SpreadsheetExtractionAlgorithm();
@@ -69,17 +80,27 @@ public abstract class AbstractPDReportParser<T extends ReportData> extends Abstr
                 Page page = pageIterator.next();
                 tables.addAll(extractionAlgorithm.extract(page));
             }
+            if (tables.isEmpty()) {
+                throw new ReportParseException(ReportParseStatus.REPORT_IS_SCAN);
+            }
             this.initTableInfo(tables);
         }
-        // 报告基本信息
-        ReportBaseInfoDTO reportInfo = this.buildReportInfo(params);
-        // 解析报告中主体基金的基本信息
-        ReportFundInfoDTO reportFundInfo = this.buildFundInfo(params);
-        // 解析其他表格信息并且设置结果字段
-        T reportData = this.parseExtInfoAndSetData(reportInfo, reportFundInfo);
-        // 数据清洗后返回
-        this.cleaningReportData(reportData);
-        return reportData;
+        try {
+            // 报告基本信息
+            ReportBaseInfoDTO reportInfo = this.buildReportInfo(params);
+            // 解析报告中主体基金的基本信息
+            ReportFundInfoDTO reportFundInfo = this.buildFundInfo(params);
+            // 解析其他表格信息并且设置结果字段
+            T reportData = this.parseExtInfoAndSetData(reportInfo, reportFundInfo);
+            // 数据清洗后返回
+            this.cleaningReportData(reportData);
+            return reportData;
+        } catch (ReportParseException e) {
+            throw e;
+        } catch (Exception e) {
+            this.logger.warn("报告解析错误:{}", ExceptionUtil.stacktraceToString(e));
+            throw new ReportParseException(ReportParseStatus.NOT_A_FIXED_FORMAT);
+        }
     }
 
     /**
@@ -98,7 +119,7 @@ public abstract class AbstractPDReportParser<T extends ReportData> extends Abstr
     protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
         Table fundInfoTable = this.fundInfoTable;
         if (fundInfoTable == null) {
-            throw new APIException("未解析到基本信息表格");
+            throw new ReportParseException(ReportParseStatus.PARSE_FUND_INFO_FAIL);
         }
         // 基金基本信息映射
         return this.buildDto(params.getFileId(), fundInfoTable, ReportFundInfoDTO.class, this::parseFundInfo);
@@ -235,12 +256,14 @@ public abstract class AbstractPDReportParser<T extends ReportData> extends Abstr
         Pattern pat3 = Pattern.compile("(2\\d{3})年年度");  // 2023年年度
         Pattern pat4 = Pattern.compile("(\\d{4})年(\\d{1,2})月");  // 2023年12月
         Pattern pat5 = Pattern.compile("\\d{4}\\d{2}\\d{2}");  // 20231231
+        Pattern pat6 = Pattern.compile("(2\\d{3})年度");  // 2023年度
         // 创建Matcher对象
         Matcher matcher1 = pat1.matcher(string);
         Matcher matcher2 = pat2.matcher(string);
         Matcher matcher3 = pat3.matcher(string);
         Matcher matcher4 = pat4.matcher(string);
         Matcher matcher5 = pat5.matcher(string);
+        Matcher matcher6 = pat6.matcher(string);
         // 尝试匹配
         if (matcher1.find()) {
             String year = matcher1.group(1);
@@ -258,6 +281,8 @@ public abstract class AbstractPDReportParser<T extends ReportData> extends Abstr
             return matcher5.group();
         } else if (matcher3.find()) {
             return matcher3.group(1) + "-12-31";
+        } else if (matcher6.find()) {
+            return matcher6.group(1) + "-12-31";
         } else if (matcher4.find()) {
             String year = matcher4.group(1);
             String month = matcher4.group(2);

+ 20 - 10
service-daq/src/main/java/com/simuwang/daq/components/report/parser/pdf/PDAnnuallyReportParser.java

@@ -32,7 +32,7 @@ public class PDAnnuallyReportParser extends PDQuarterlyReportParser<AnnuallyRepo
         FINANCIAL_INDICATORS_COLUMN_NAMES.add("基金份额累计净值增长率");
     }
 
-    private final List<Table> fundInfoTables = ListUtil.list(true);
+    private List<Table> fundInfoTables;
 
     public PDAnnuallyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
         super(fieldMappingMapper);
@@ -45,6 +45,12 @@ public class PDAnnuallyReportParser extends PDQuarterlyReportParser<AnnuallyRepo
 
     @Override
     protected void initTableInfo(List<Table> tables) {
+        // 初始化
+        this.fundInfoTables = ListUtil.list(true);
+        this.financialIndicatorsTables = ListUtil.list(true);
+        this.shareChangeTables = ListUtil.list(true);
+        this.assetAllocationTables = ListUtil.list(true);
+        this.investmentIndustryTables = ListUtil.list(true);
         for (int i = 0; i < tables.size(); i++) {
             Table table = tables.get(i);
             if (i <= 1) {
@@ -120,11 +126,15 @@ public class PDAnnuallyReportParser extends PDQuarterlyReportParser<AnnuallyRepo
 
     private List<ReportFinancialIndicatorsDTO> buildFinancialIndicatorsInfo(Integer fileId) {
         List<ReportFinancialIndicatorsDTO> dtos = ListUtil.list(false);
-        // 这里不存在分级基金,可能会存在表格跨页
-        int colCount = this.financialIndicatorsTables.get(0).getColCount();
-        for (int j = 1; j < colCount; j++) {
-            Map<String, Object> infoMap = MapUtil.newHashMap(16);
-            for (Table table : this.financialIndicatorsTables) {
+        // 分级基金
+        List<String> levels = this.matchTieredFund(String.join(",", this.textList));
+        levels.add(0, "母基金");
+        // 假设这里可能存在分级基金,不存在表格跨页
+        for (int k = 0; k < this.financialIndicatorsTables.size(); k++) {
+            Table table = this.financialIndicatorsTables.get(k);
+            int colCount = table.getColCount();
+            for (int j = 1; j < colCount; j++) {
+                Map<String, Object> infoMap = MapUtil.newHashMap(16);
                 String year = this.cleaningValue(table.getCell(0, j).getText());
                 infoMap.put("年度", year);
                 for (int i = 0; i < table.getRowCount(); i++) {
@@ -135,11 +145,11 @@ public class PDAnnuallyReportParser extends PDQuarterlyReportParser<AnnuallyRepo
                     String value = this.cleaningValue(table.getCell(i, j).getText());
                     infoMap.put(columnName, value);
                 }
+                ReportFinancialIndicatorsDTO dto = new ReportFinancialIndicatorsDTO(fileId);
+                this.buildInfo(infoMap, dto);
+                dto.setLevel(levels.get(k));
+                dtos.add(dto);
             }
-            ReportFinancialIndicatorsDTO dto = new ReportFinancialIndicatorsDTO(fileId);
-            this.buildInfo(infoMap, dto);
-            dto.setLevel("母基金");
-            dtos.add(dto);
         }
         return dtos;
     }

+ 8 - 8
service-daq/src/main/java/com/simuwang/daq/components/report/parser/pdf/PDQuarterlyReportParser.java

@@ -72,11 +72,10 @@ public class PDQuarterlyReportParser<T extends QuarterlyReportData> extends Abst
         SHARE_CHANGE_COLUMN_NAMES.add("报告期期间基金总申购份额");
     }
 
-    protected final List<Table> financialIndicatorsTables = ListUtil.list(true);
-    protected final List<Table> shareChangeTables = ListUtil.list(true);
-    protected final List<Table> assetAllocationTables = ListUtil.list(true);
-    protected final List<Table> investmentIndustryTables = ListUtil.list(true);
-    private final List<Table> navPerformanceTables = ListUtil.list(true);
+    protected List<Table> financialIndicatorsTables;
+    protected List<Table> shareChangeTables;
+    protected List<Table> assetAllocationTables;
+    protected List<Table> investmentIndustryTables;
 
     public PDQuarterlyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
         super(fieldMappingMapper);
@@ -89,6 +88,10 @@ public class PDQuarterlyReportParser<T extends QuarterlyReportData> extends Abst
 
     @Override
     protected void initTableInfo(List<Table> tables) {
+        this.financialIndicatorsTables = ListUtil.list(true);
+        this.shareChangeTables = ListUtil.list(true);
+        this.assetAllocationTables = ListUtil.list(true);
+        this.investmentIndustryTables = ListUtil.list(true);
         for (Table table : tables) {
             int colCount = table.getColCount();
             int rowCount = table.getRowCount();
@@ -97,9 +100,6 @@ public class PDQuarterlyReportParser<T extends QuarterlyReportData> extends Abst
             }
             if (rowCount == 13 && colCount == 2) {
                 this.fundInfoTable = table;
-            } else if (colCount == 5) {
-                // 净值表现(未入库)
-                this.navPerformanceTables.add(table);
             } else if (colCount == 2) {
                 // 用表格的第一列的数据判断是否份额变动记录
                 List<String> texts = this.getTableColTexts(table, 0);

+ 5 - 3
service-daq/src/main/java/com/simuwang/daq/components/report/parser/py/AbstractPyReportParser.java

@@ -5,10 +5,11 @@ import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONUtil;
 import com.simuwang.base.common.enums.ReportType;
+import com.simuwang.base.common.exception.ReportParseException;
 import com.simuwang.base.config.DaqProperties;
 import com.simuwang.base.mapper.FundInfoMapper;
 import com.simuwang.base.pojo.dos.FundAndCompanyInfoDO;
-import com.simuwang.base.pojo.dto.report.PythonResult;
+import com.simuwang.base.pojo.dto.report.ParseResult;
 import com.simuwang.base.pojo.dto.report.ReportData;
 import com.simuwang.base.pojo.dto.report.ReportParserParams;
 import com.simuwang.daq.components.PythonReportConverter;
@@ -37,7 +38,7 @@ public abstract class AbstractPyReportParser<T extends ReportData> implements Re
     }
 
     @Override
-    public T parse(ReportParserParams params) throws IOException {
+    public T parse(ReportParserParams params) throws IOException, ReportParseException {
         Boolean enablePyParser = this.properties.getEnablePyParser();
         if (!enablePyParser) {
             this.logger.error("The python report parser is unavailable!");
@@ -61,9 +62,10 @@ public abstract class AbstractPyReportParser<T extends ReportData> implements Re
             }
         }
         String body = HttpUtil.post(pyBaseUrl + api, JSONUtil.toJsonStr(params));
-        PythonResult<T> result = PythonReportConverter.convert(JSONUtil.parseObj(body), reportType);
+        ParseResult<T> result = PythonReportConverter.convert(JSONUtil.parseObj(body), reportType);
         if (!Objects.equals(1, result.getStatus())) {
             this.logger.error("报告{} 解析失败:{}", params, result.getMsg());
+            throw new ReportParseException(result.getStatus(), result.getMsg());
         }
         return result.getData();
     }

+ 29 - 13
service-daq/src/main/java/com/simuwang/daq/service/EmailParseService.java

@@ -11,6 +11,7 @@ import cn.hutool.core.util.StrUtil;
 import com.simuwang.base.common.conts.*;
 import com.simuwang.base.common.enums.ReportParserFileType;
 import com.simuwang.base.common.enums.ReportType;
+import com.simuwang.base.common.exception.ReportParseException;
 import com.simuwang.base.common.util.EmailUtil;
 import com.simuwang.base.common.util.ExcelUtil;
 import com.simuwang.base.common.util.FileUtil;
@@ -21,7 +22,9 @@ import com.simuwang.base.pojo.dos.*;
 import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
 import com.simuwang.base.pojo.dto.EmailFundNavDTO;
 import com.simuwang.base.pojo.dto.MailboxInfoDTO;
+import com.simuwang.base.pojo.dto.report.ParseResult;
 import com.simuwang.base.pojo.dto.report.ReportData;
+import com.simuwang.base.pojo.dto.report.ReportParseStatus;
 import com.simuwang.base.pojo.dto.report.ReportParserParams;
 import com.simuwang.base.pojo.valuation.CmValuationTableAttribute;
 import com.simuwang.daq.components.report.parser.ReportParser;
@@ -178,7 +181,7 @@ public class EmailParseService {
         emailId = saveEmailParseInfo(emailParseInfoDO);
 
         // python 报告解析接口结果
-        List<ReportData> dataList = ListUtil.list(false);
+        List<ParseResult<ReportData>> dataList = ListUtil.list(false);
         for (Map.Entry<EmailContentInfoDTO, List<EmailFundNavDTO>> fileNameNavEntry : fileNameNavMap.entrySet()) {
             // 保存邮件文件表
             EmailContentInfoDTO emailContentInfoDTO = fileNameNavEntry.getKey();
@@ -198,10 +201,8 @@ public class EmailParseService {
             }
             if (Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType)) {
                 // 解析结果(可以从python获取或者自行解析)并保存报告
-                ReportData data = this.parseReportAndHandleResult(fileId, emailContentInfoDTO);
-                if (data != null) {
-                    dataList.add(data);
-                }
+                ParseResult<ReportData> parseResult = this.parseReportAndHandleResult(fileId, emailContentInfoDTO);
+                dataList.add(parseResult);
             }
             for (EmailFundNavDTO fundNavDTO : fundNavDTOList) {
                 // 设置净值数据的解析状态
@@ -215,11 +216,6 @@ public class EmailParseService {
         // 更新邮件解析结果 -> 当【净值日期】和【备案编码/基金名称】能正常解读,即识别为【成功】
         long successNavCount = fileNameNavMap.values().stream().flatMap(List::stream).filter(e -> e != null && StrUtil.isBlank(e.getFailReason())).count();
         emailParseStatus = successNavCount >= 1 ? EmailParseStatusConst.SUCCESS : EmailParseStatusConst.FAIL;
-        // 报告邮件有一条成功就表示整体成功
-        if (Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType) && CollUtil.isNotEmpty(dataList)) {
-            long count = dataList.size();
-            emailParseStatus = count >= 1 ? EmailParseStatusConst.SUCCESS : EmailParseStatusConst.FAIL;
-        }
         String failReason = null;
         if (emailParseStatus == EmailParseStatusConst.FAIL) {
             // 邮件解析失败时 -> 保存失败原因
@@ -227,6 +223,14 @@ public class EmailParseService {
             List<EmailFundNavDTO> navDTOList = fileNameNavMap.values().stream().flatMap(List::stream).toList();
             failReason = hasPdfFile == 1 && CollUtil.isEmpty(navDTOList) ? "无法从pdf文件中获取到数据" : navDTOList.stream().map(EmailFundNavDTO::getFailReason).distinct().collect(Collectors.joining("/"));
         }
+        // 报告邮件有一条失败就表示整个邮件解析失败
+        if (Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType) && CollUtil.isNotEmpty(dataList)) {
+            failReason = dataList.stream().filter(e -> !Objects.equals(1, e.getStatus()))
+                    .findFirst().map(ParseResult::getMsg).orElse(null);
+            if (failReason != null) {
+                emailParseStatus = EmailParseStatusConst.FAIL;
+            }
+        }
         emailParseInfoMapper.updateParseStatus(emailId, emailParseStatus, failReason);
     }
 
@@ -364,11 +368,14 @@ public class EmailParseService {
         }).collect(Collectors.toList());
     }
 
-    private ReportData parseReportAndHandleResult(int fileId, EmailContentInfoDTO emailContentInfoDTO) {
+    private ParseResult<ReportData> parseReportAndHandleResult(int fileId, EmailContentInfoDTO emailContentInfoDTO) {
+        ParseResult<ReportData> result = new ParseResult<>();
         String fileName = emailContentInfoDTO.getFileName();
         Integer emailType = emailContentInfoDTO.getEmailType();
         if (!Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType) || StrUtil.isBlank(fileName)) {
-            return null;
+            result.setStatus(ReportParseStatus.NOT_A_REPORT.getCode());
+            result.setMsg(ReportParseStatus.NOT_A_REPORT.getMsg());
+            return result;
         }
         Pattern pattern = Pattern.compile("S(?:[A-Z]{0}[0-9]{5}|[A-Z][0-9]{4}|[A-Z]{2}[0-9]{3}|[A-Z]{3}[0-9]{2})");
         Matcher matcher = pattern.matcher(fileName);
@@ -401,8 +408,14 @@ public class EmailParseService {
                     .filepath(emailContentInfoDTO.getFilePath()).registerNumber(registerNumber).build();
             ReportParser<ReportData> instance = this.reportParserFactory.getInstance(reportType, fileType);
             reportData = instance.parse(params);
+        } catch (ReportParseException e) {
+            log.error("报告{}解析失败\n{}", params, e.getMsg());
+            result.setStatus(e.getCode());
+            result.setMsg(e.getMsg());
         } catch (Exception e) {
             log.error("报告{}解析失败\n{}", params, ExceptionUtil.stacktraceToString(e));
+            result.setStatus(ReportParseStatus.PARSE_FAIL.getCode());
+            result.setMsg(StrUtil.format(ReportParseStatus.PARSE_FAIL.getMsg(), e.getMessage()));
         } finally {
             parserWatch.stop();
             if (log.isInfoEnabled()) {
@@ -425,7 +438,10 @@ public class EmailParseService {
                 }
             }
         }
-        return reportData;
+        result.setStatus(1);
+        result.setMsg("报告解析成功");
+        result.setData(reportData);
+        return result;
     }
 
     private void saveNavAndAssetNet(Integer fileId, List<EmailFundNavDTO> fundNavDTOList, Date parseDate) {

+ 1 - 1
service-deploy/src/test/java/com/simuwang/ApplicationTest.java

@@ -46,7 +46,7 @@ public class ApplicationTest {
     public void reportTest() {
         MailboxInfoDTO emailInfoDTO = this.buildMailbox();
         Date startDate = DateUtil.parse("2024-10-11 08:30:30", DateConst.YYYY_MM_DD_HH_MM_SS);
-        Date endDate = DateUtil.parse("2024-10-11 19:59:30", DateConst.YYYY_MM_DD_HH_MM_SS);
+        Date endDate = DateUtil.parse("2024-10-11 09:59:30", DateConst.YYYY_MM_DD_HH_MM_SS);
         try {
             emailParseService.parseEmail(emailInfoDTO, startDate, endDate);
         } catch (Exception e) {