Ver Fonte

feat:AI解析观点报告的基本信息就好

wangzaijun há 5 dias atrás
pai
commit
b32768cf3f

+ 24 - 61
mo-daq/src/main/java/com/smppw/modaq/application/components/ReportParseUtils.java

@@ -236,6 +236,28 @@ public final class ReportParseUtils {
     }
 
     /**
+     * 分级基金名称
+     *
+     * @param text 文本内容
+     * @return /
+     */
+    public static String matchFundLevel(String text) {
+        // 使用正则表达式查找匹配项
+        Pattern pattern = Pattern.compile("[A-F]级|基金[A-F]");
+        Matcher matcher = pattern.matcher(text);
+        String result = null;
+        while (matcher.find()) {
+            result = matcher.group();
+        }
+        if (StrUtil.isBlank(result)) {
+            result = "母基金";
+        } else {
+            result = result.replaceAll("[^A-F]", "") + "级";
+        }
+        return result;
+    }
+
+    /**
      * 匹配报告日期
      *
      * @param string 文本内容
@@ -303,10 +325,10 @@ public final class ReportParseUtils {
             reportType = ReportType.ANNUALLY;
         } else if (StrUtil.containsAny(string, ReportType.MONTHLY.getPatterns())) {
             reportType = ReportType.MONTHLY;
-//        } else if (StrUtil.containsAny(string, ReportType.WEEKLY.getPatterns())) {
-//            reportType = ReportType.WEEKLY;
         } else if (StrUtil.containsAny(string, ReportType.LETTER.getPatterns())) {
             reportType = ReportType.LETTER;
+        } else if (StrUtil.containsAny(string, ReportType.OTHER.getPatterns())) {
+            reportType = ReportType.OTHER;
         }
         return reportType;
     }
@@ -322,65 +344,6 @@ public final class ReportParseUtils {
         return String.format("%02d", Integer.parseInt(number));
     }
 
-//    public static GenerationResult callWithMessage() throws ApiException, NoApiKeyException, InputRequiredException {
-//        Generation gen = new Generation();
-//        Message systemMsg = Message.builder()
-//                .role(Role.SYSTEM.getValue())
-//                .content("You are a helpful assistant.")
-//                .build();
-//        Message userMsg = Message.builder()
-//                .role(Role.USER.getValue())
-//                .content("你是谁?")
-//                .build();
-//        GenerationParam param = GenerationParam.builder()
-//                // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
-//                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
-//                // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
-//                .model("qwen-plus")
-//                .messages(Arrays.asList(systemMsg, userMsg))
-//                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
-//                .build();
-//        return gen.call(param);
-//    }
-//
-//    public static void simpleMultiModalConversationCall()
-//            throws ApiException, NoApiKeyException, UploadFileException {
-//        MultiModalConversation conv = new MultiModalConversation();
-//        Map<String, Object> map = new HashMap<>();
-//        map.put("image", "./流水1.jpg");
-//        map.put("max_pixels", "1003520");
-//        map.put("min_pixels", "3136");
-//        MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
-//                .content(Arrays.asList(
-//                        map,
-//                        // 目前为保证模型效果,模型内部会统一使用"Read all the text in the image."作为text的值,用户输入的文本不会生效。
-//                        Collections.singletonMap("text", "Read all the text in the image."))).build();
-//        MultiModalConversationParam param = MultiModalConversationParam.builder()
-//                // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
-//                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
-//                // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
-//                .model("qwen-vl-ocr")
-//                .message(userMessage)
-//                .build();
-//        MultiModalConversationResult result = conv.call(param);
-//        System.out.println(JsonUtils.toJson(result));
-//    }
-//
-//    public static void main(String[] args) throws IOException {
-////        try {
-////            GenerationResult result = callWithMessage();
-////            System.out.println(result.getOutput().getChoices().get(0).getMessage().getContent());
-////        } catch (ApiException | NoApiKeyException | InputRequiredException e) {
-////            System.err.println("错误信息:"+e.getMessage());
-////            System.out.println("请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code");
-////        }
-//        try {
-//            simpleMultiModalConversationCall();
-//        } catch (ApiException | NoApiKeyException | UploadFileException e) {
-//            System.out.println(e.getMessage());
-//        }
-//    }
-
     public static void main(String[] args) throws IOException, ReportParseException {
 //        String filepath = "C:\\Users\\Administrator\\Desktop\\tmp\\(1)投资者交易确认函【申购】_【SZF635】佳岳国债增强私募证券投资基金_20250217_任军.pdf";
 //        String filepath = "C:\\Users\\Administrator\\Desktop\\tmp\\CP080A_优美利赢胜价值1号私募投资基金A_20250217_邓辉_申购确认_20250217131352.pdf";

+ 12 - 3
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ReportParserConstant.java

@@ -14,6 +14,9 @@ import java.util.Map;
 public final class ReportParserConstant {
     public static final Map<ReportType, Map<ReportParserFileType, String>> REPORT_PARSER_BEAN_MAP = MapUtil.newHashMap(8);
 
+    // 其他报告
+    public static final String PARSER_AI_OTHER = "report-parser:ai:other";
+
     // 交易流水确认函解析
     public static final String PARSER_PDF_LETTER = "report-parser:pdf:letter";
     public static final String PARSER_AI_LETTER = "report-parser:ai:letter";
@@ -31,6 +34,9 @@ public final class ReportParserConstant {
     public static final String PARSER_AI_ANNUALLY = "report-parser:ai:annually";
 
     static {
+        // 其他报告解析
+        REPORT_PARSER_BEAN_MAP.put(ReportType.OTHER, Map.of(ReportParserFileType.AI, PARSER_AI_OTHER));
+
         // 交易流水确认函解析
         REPORT_PARSER_BEAN_MAP.put(ReportType.LETTER,
                 Map.of(
@@ -40,21 +46,24 @@ public final class ReportParserConstant {
 
         // 月度报告
         REPORT_PARSER_BEAN_MAP.put(ReportType.MONTHLY,
-                Map.of(ReportParserFileType.PDF, PARSER_PDF_MONTHLY,
+                Map.of(
+                        ReportParserFileType.PDF, PARSER_PDF_MONTHLY,
                         ReportParserFileType.EXCEL, PARSER_EXCEL_MONTHLY,
                         ReportParserFileType.AI, PARSER_AI_MONTHLY
                 ));
 
         // 季度报告
         REPORT_PARSER_BEAN_MAP.put(ReportType.QUARTERLY,
-                Map.of(ReportParserFileType.PDF, PARSER_PDF_QUARTERLY,
+                Map.of(
+                        ReportParserFileType.PDF, PARSER_PDF_QUARTERLY,
                         ReportParserFileType.EXCEL, PARSER_EXCEL_QUARTERLY,
                         ReportParserFileType.AI, PARSER_AI_QUARTERLY
                 ));
 
         // 年度报告
         REPORT_PARSER_BEAN_MAP.put(ReportType.ANNUALLY,
-                Map.of(ReportParserFileType.PDF, PARSER_PDF_ANNUALLY,
+                Map.of(
+                        ReportParserFileType.PDF, PARSER_PDF_ANNUALLY,
                         ReportParserFileType.EXCEL, PARSER_EXCEL_ANNUALLY,
                         ReportParserFileType.AI, PARSER_AI_ANNUALLY
                 ));

+ 7 - 16
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AILetterReportParser.java

@@ -2,7 +2,6 @@ package com.smppw.modaq.application.components.report.parser.ai;
 
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.smppw.modaq.application.components.report.parser.ReportParserConstant;
@@ -29,14 +28,8 @@ public class AILetterReportParser extends AbstractAIReportParser<LetterReportDat
     @Override
     protected void handleAiResult(String result) throws ReportParseException {
         try {
-            JSONObject jsonResult = JSONUtil.parseObj(result);
-            this.aiFileId = MapUtil.getStr(jsonResult, "file_id");
-            String content = StrUtil.split(jsonResult.getStr("content"), "```").get(1);
-            String aiParserContent = "{" + StrUtil.subAfter(content, "{", false) + "}";
-            if (StrUtil.isNotBlank(aiParserContent)) {
-                JSONObject jsonObject = JSONUtil.parseObj(aiParserContent);
-                this.infoMap.putAll(flattenMap(jsonObject, ListUtil.list(false)));
-            }
+            JSONObject jsonObject = JSONUtil.parseObj(result);
+            this.allInfoMap.putAll(flattenMap(jsonObject, ListUtil.list(false)));
         } catch (Exception e) {
             throw new ReportParseException(ReportParseStatus.PARSE_HANDLE_FAIL);
         }
@@ -46,24 +39,22 @@ public class AILetterReportParser extends AbstractAIReportParser<LetterReportDat
     protected LetterReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo) throws ReportParseException {
         Integer fileId = reportInfo.getFileId();
         if (this.logger.isInfoEnabled()) {
-            this.logger.info("文件{} 解析内容是:{}", fileId, this.infoMap);
+            this.logger.info("文件{} 解析内容是:{}", fileId, this.allInfoMap);
         }
         // 投资者信息
-        ReportInvestorInfoDTO investorInfo = this.buildDto(fileId, ReportInvestorInfoDTO.class, this.infoMap);
+        ReportInvestorInfoDTO investorInfo = this.buildDto(fileId, ReportInvestorInfoDTO.class, this.allInfoMap);
         // 交易流水
-        ReportFundTransactionDTO fundTransaction = this.buildDto(fileId, ReportFundTransactionDTO.class, this.infoMap);
+        ReportFundTransactionDTO fundTransaction = this.buildDto(fileId, ReportFundTransactionDTO.class, this.allInfoMap);
         // 构建结果数据
         LetterReportData reportData = new LetterReportData(reportInfo, fundInfo);
-        reportData.setInvestorInfo(investorInfo);
         reportData.setFundTransaction(fundTransaction);
-        reportData.setAiParse(true);
-        reportData.setAiFileId(this.aiFileId);
+        reportData.setInvestorInfo(investorInfo);
         return reportData;
     }
 
     @Override
     protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
-        return this.buildDto(params.getFileId(), ReportFundInfoDTO.class, this.infoMap);
+        return this.buildDto(params.getFileId(), ReportFundInfoDTO.class, this.allInfoMap);
     }
 
     @SuppressWarnings("unchecked")

+ 42 - 8
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AIMonthlyReportParser.java

@@ -1,14 +1,19 @@
 package com.smppw.modaq.application.components.report.parser.ai;
 
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.smppw.modaq.application.components.ReportParseUtils;
 import com.smppw.modaq.application.components.report.parser.ReportParserConstant;
+import com.smppw.modaq.common.enums.ReportParseStatus;
 import com.smppw.modaq.common.exception.ReportParseException;
-import com.smppw.modaq.domain.dto.report.MonthlyReportData;
-import com.smppw.modaq.domain.dto.report.ReportBaseInfoDTO;
-import com.smppw.modaq.domain.dto.report.ReportFundInfoDTO;
-import com.smppw.modaq.domain.dto.report.ReportParserParams;
+import com.smppw.modaq.domain.dto.report.*;
 import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
+import java.util.Map;
+
 @Component(ReportParserConstant.PARSER_AI_MONTHLY)
 public class AIMonthlyReportParser extends AbstractAIReportParser<MonthlyReportData> {
     public AIMonthlyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
@@ -22,22 +27,51 @@ public class AIMonthlyReportParser extends AbstractAIReportParser<MonthlyReportD
 
     @Override
     protected String prompt() {
-        return "";
+        return "识别文件中的基金概况、净值信息表格数据,要求准确识别金额等小数的位数,去掉金额单位、英文和多余的空格,结果用json返回";
     }
 
     @Override
     protected void handleAiResult(String result) throws ReportParseException {
-
+        try {
+            JSONObject jsonObject = JSONUtil.parseObj(result);
+            this.allInfoMap.putAll(jsonObject);
+        } catch (Exception e) {
+            throw new ReportParseException(ReportParseStatus.PARSE_HANDLE_FAIL);
+        }
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     protected MonthlyReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo,
                                                        ReportFundInfoDTO fundInfo) throws ReportParseException {
-        return null;
+        List<ReportNetReportDTO> dtos = ListUtil.list(true);
+        for (Map.Entry<String, Object> entry : this.allInfoMap.entrySet()) {
+            Object netInfo = entry.getValue();
+            if (netInfo == null) {
+                continue;
+            }
+            Map<String, Object> netInfoMap = (Map<String, Object>) netInfo;
+            ReportNetReportDTO dto = this.buildDto(reportInfo.getFileId(), ReportNetReportDTO.class, netInfoMap);
+            if (dto == null) {
+                continue;
+            }
+            dto.setLevel(ReportParseUtils.matchFundLevel(entry.getKey()));
+            dtos.add(dto);
+        }
+        MonthlyReportData reportData = new MonthlyReportData(reportInfo, fundInfo);
+        reportData.setNetReport(dtos);
+        return reportData;
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
-        return null;
+        // 获取并移除基金概况信息
+        Object fundInfo = this.allInfoMap.remove("基金概况");
+        if (fundInfo == null) {
+            throw new ReportParseException(ReportParseStatus.PARSE_FUND_INFO_FAIL, params.getFilename());
+        }
+        Map<String, Object> fundInfoMap = (Map<String, Object>) fundInfo;
+        return this.buildDto(params.getFileId(), ReportFundInfoDTO.class, fundInfoMap);
     }
 }

+ 17 - 4
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AbstractAIReportParser.java

@@ -4,6 +4,8 @@ import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.smppw.modaq.application.components.report.parser.AbstractReportParser;
 import com.smppw.modaq.common.enums.ReportParseStatus;
 import com.smppw.modaq.common.exception.ReportParseException;
@@ -25,7 +27,7 @@ public abstract class AbstractAIReportParser<T extends ReportData> extends Abstr
 
     protected String aiFileId;
 
-    protected Map<String, Object> infoMap;
+    protected Map<String, Object> allInfoMap;
 
     public AbstractAIReportParser(EmailFieldMappingMapper fieldMappingMapper) {
         super(fieldMappingMapper);
@@ -50,7 +52,13 @@ public abstract class AbstractAIReportParser<T extends ReportData> extends Abstr
         String body = null;
         try {
             body = HttpUtil.get(this.aiParserUrl, paramsMap);
-            this.handleAiResult(body);
+            JSONObject jsonResult = JSONUtil.parseObj(body);
+            this.aiFileId = MapUtil.getStr(jsonResult, "file_id");
+            String content = StrUtil.split(jsonResult.getStr("content"), "```").get(1);
+            String aiParserContent = "{" + StrUtil.subAfter(content, "{", false) + "}";
+            if (StrUtil.isNotBlank(aiParserContent)) {
+                this.handleAiResult(aiParserContent);
+            }
         } catch (ReportParseException e) {
             this.logger.warn("{} ai解析失败,解析结果{},错误原因:{}", filename, body, ExceptionUtil.stacktraceToString(e));
             throw e;
@@ -58,7 +66,12 @@ public abstract class AbstractAIReportParser<T extends ReportData> extends Abstr
             this.logger.warn("报告{} 在AI解析时报错:{}", filename, ExceptionUtil.stacktraceToString(e));
             throw new ReportParseException(ReportParseStatus.AI_NOT_FOUND);
         }
-        return this.buildReportData(params, filename);
+        T reportData = this.buildReportData(params, filename);
+        if (reportData != null) {
+            reportData.setAiFileId(this.aiFileId);
+            reportData.setAiParse(true);
+        }
+        return reportData;
     }
 
 
@@ -95,6 +108,6 @@ public abstract class AbstractAIReportParser<T extends ReportData> extends Abstr
         super.init();
         // 先初始化为null
         this.aiFileId = null;
-        this.infoMap = MapUtil.newHashMap(128);
+        this.allInfoMap = MapUtil.newHashMap(128);
     }
 }

+ 51 - 0
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AiOtherReportParser.java

@@ -0,0 +1,51 @@
+package com.smppw.modaq.application.components.report.parser.ai;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.smppw.modaq.application.components.report.parser.ReportParserConstant;
+import com.smppw.modaq.common.enums.ReportParseStatus;
+import com.smppw.modaq.common.exception.ReportParseException;
+import com.smppw.modaq.domain.dto.report.*;
+import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
+import org.springframework.stereotype.Component;
+
+/**
+ * 其他格式的报告(只解析报告基本信息、基金基本信息)
+ */
+@Component(ReportParserConstant.PARSER_AI_OTHER)
+public class AiOtherReportParser extends AbstractAIReportParser<ReportData> {
+    public AiOtherReportParser(EmailFieldMappingMapper fieldMappingMapper) {
+        super(fieldMappingMapper);
+    }
+
+    @Override
+    protected String prompt() {
+        return "识别文件中的基金名称、基金管理人、基金托管人和报告日期,如果无法识别就返回空字符,结果用json返回";
+    }
+
+    @Override
+    protected boolean isSupportAIParse() {
+        return true;
+    }
+
+    @Override
+    protected void handleAiResult(String result) throws ReportParseException {
+        try {
+            JSONObject jsonObject = JSONUtil.parseObj(result);
+            this.allInfoMap.putAll(jsonObject);
+        } catch (Exception e) {
+            throw new ReportParseException(ReportParseStatus.PARSE_HANDLE_FAIL);
+        }
+    }
+
+    @Override
+    protected ReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo,
+                                                ReportFundInfoDTO fundInfo) throws ReportParseException {
+        return new MonthlyReportData(reportInfo, fundInfo);
+    }
+
+    @Override
+    protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
+        return this.buildDto(params.getFileId(), ReportFundInfoDTO.class, this.allInfoMap);
+    }
+}

+ 7 - 5
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/AbstractPDReportParser.java

@@ -203,11 +203,13 @@ public abstract class AbstractPDReportParser<T extends ReportData> extends Abstr
         for (Map.Entry<Integer, List<Table>> entry : spanningPageTableMap.entrySet()) {
             List<Table> spanningPageShareChangeTables = entry.getValue();
             Table master = spanningPageShareChangeTables.get(0);
-            Table slave = spanningPageShareChangeTables.get(1);
-            int rowCount = master.getRowCount();
-            for (int j = 0; j < slave.getRowCount(); j++) {
-                for (int k = 0; k < slave.getColCount(); k++) {
-                    master.add(slave.getCell(j, k), rowCount + j, k);
+            if (spanningPageShareChangeTables.size() == 2) {
+                Table slave = spanningPageShareChangeTables.get(1);
+                int rowCount = master.getRowCount();
+                for (int j = 0; j < slave.getRowCount(); j++) {
+                    for (int k = 0; k < slave.getColCount(); k++) {
+                        master.add(slave.getCell(j, k), rowCount + j, k);
+                    }
                 }
             }
             tables.add(entry.getKey(), master);

+ 18 - 0
mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/OtherReportWriter.java

@@ -0,0 +1,18 @@
+package com.smppw.modaq.application.components.report.writer;
+
+import com.smppw.modaq.domain.dto.report.ReportData;
+import com.smppw.modaq.domain.mapper.report.ReportBaseInfoMapper;
+import com.smppw.modaq.domain.mapper.report.ReportFundInfoMapper;
+import org.springframework.stereotype.Component;
+
+@Component(ReportWriterConstant.WRITER_OTHER)
+public class OtherReportWriter extends AbstractReportWriter<ReportData> {
+    public OtherReportWriter(ReportBaseInfoMapper baseInfoMapper, ReportFundInfoMapper fundInfoMapper) {
+        super(baseInfoMapper, fundInfoMapper);
+    }
+
+    @Override
+    protected void writeExtData(ReportData reportData) {
+        // 没有数据要保存
+    }
+}

+ 5 - 0
mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/ReportWriterConstant.java

@@ -8,12 +8,17 @@ import java.util.Map;
 public final class ReportWriterConstant {
     public static final Map<ReportType, String> REPORT_TYPE_BEAN_MAP = MapUtil.newHashMap(8);
 
+    static final String WRITER_OTHER = "report-writer:other";
+
     static final String WRITER_LETTER = "report-writer:letter";
+
     static final String WRITER_MONTHLY = "report-writer:monthly";
     static final String WRITER_QUARTERLY = "report-writer:quarterly";
     static final String WRITER_ANNUALLY = "report-writer:annually";
 
     static {
+        REPORT_TYPE_BEAN_MAP.put(ReportType.OTHER, WRITER_OTHER);
+
         REPORT_TYPE_BEAN_MAP.put(ReportType.LETTER, WRITER_LETTER);
 
         REPORT_TYPE_BEAN_MAP.put(ReportType.MONTHLY, WRITER_MONTHLY);

+ 1 - 1
mo-daq/src/main/java/com/smppw/modaq/common/enums/ReportParseStatus.java

@@ -4,7 +4,7 @@ public enum ReportParseStatus implements StatusCode {
     AI_NOT_FOUND(20009, "AI资源找不到"),
     NO_SUPPORT_AI(20010, "报告[{}]不支持AI解析"),
     PARSE_FAIL(21000, "定期报告或交易确认单解析错误:{}"),
-    NOT_A_REPORT(21001, "[{}]不是定期报告或交易确认单"),
+    NOT_A_REPORT(21001, "[{}]不是支持的报告格式"),
     REPORT_IS_SCAN(21002, "报告[{}]为扫描件"),
     NO_SUPPORT_TEMPLATE(21003, "报告[{}]是不支持的文件格式"),
     NOT_A_FIXED_FORMAT(21004, "报告[{}]不是基协统一格式"),

+ 8 - 2
mo-daq/src/main/java/com/smppw/modaq/common/enums/ReportType.java

@@ -4,10 +4,16 @@ import lombok.Getter;
 
 @Getter
 public enum ReportType {
-    OTHER(-2, "其他报告", new String[]{"观点", "分红公告", "周报", "预警公告"}),
-    LETTER(-1, "交易流水确认函", new String[]{"确认单", "确认函", "确认"}),
+    OTHER(-2, "其他报告", new String[]{"公告", "通知", "告知函", "意见征询函", "说明函",
+            "清算报告", "邀请函", "观点", "预警", "复核函", "提醒", "投研报告", "周报", "公示"}),
+
+    LETTER(-1, "交易流水确认函", new String[]{"确认单", "确认函", "交易确认数据",
+            "赎回确认", "申购确认", "分红确认", "确认表", "交易确认", "确认"}),
+
     MONTHLY(0, "月", new String[]{"月", "月度", "月报"}),
+
     QUARTERLY(1, "季", new String[]{"季", "季度", "季报"}),
+
     ANNUALLY(2, "年", new String[]{"年度", "年报"}),
     ;
 

+ 15 - 6
mo-daq/src/main/java/com/smppw/modaq/domain/service/EmailParseService.java

@@ -274,7 +274,8 @@ public class EmailParseService {
                                                                String filepath, Integer emailType, String aiFileId) {
         ParseResult<ReportData> result = new ParseResult<>();
         boolean reportFlag = !Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType)
-                && !Objects.equals(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE, emailType);
+                && !Objects.equals(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE, emailType)
+                && !Objects.equals(EmailTypeConst.REPORT_OTHER_TYPE, emailType);
         if (reportFlag || StrUtil.isBlank(fileName) || fileName.endsWith(".html")) {
             result.setStatus(ReportParseStatus.NOT_A_REPORT.getCode());
             result.setMsg(StrUtil.format(ReportParseStatus.NOT_A_REPORT.getMsg(), fileName));
@@ -291,7 +292,7 @@ public class EmailParseService {
         if (Objects.equals(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE, emailType)) {
             reportType = ReportType.LETTER;
         }
-        // 解析器--如果开启python解析则直接调用python接口,否则根据文件后缀获取对应解析器
+        // 解析器--根据文件后缀获取对应解析器,解析不了就用AI来解析
         ReportParserFileType fileType;
         String fileSuffix = StrUtil.subAfter(fileName, ".", true);
         fileType = ReportParserFileType.getBySuffix(fileSuffix);
@@ -313,7 +314,7 @@ public class EmailParseService {
         StopWatch parserWatch = new StopWatch();
         parserWatch.start();
         try {
-            if (StrUtil.isBlank(aiFileId)) {
+            if (StrUtil.isBlank(aiFileId) && reportType != ReportType.OTHER) {
                 ReportParserParams params = ReportParserParams.builder().fileId(fileId).filename(fileName).filepath(filepath)
                         .registerNumber(registerNumber).reportType(reportType).build();
                 ReportParser<ReportData> instance = this.reportParserFactory.getInstance(reportType, fileType);
@@ -322,8 +323,14 @@ public class EmailParseService {
                 result.setMsg("报告解析成功");
                 result.setData(reportData);
             } else {
-                if (log.isInfoEnabled()) {
-                    log.info("报告{} 是已经存在ai解析记录,上传过文件{},直接跳转到AI解析器进行解析", fileName, fileId);
+                if (reportType == ReportType.OTHER) {
+                    if (log.isInfoEnabled()) {
+                        log.info("报告{} 是其他类型,直接用AI解析器解析", fileName);
+                    }
+                } else {
+                    if (log.isInfoEnabled()) {
+                        log.info("报告{} 是已经存在ai解析记录,上传过文件{},直接跳转到AI解析器进行解析", fileName, fileId);
+                    }
                 }
             }
         } catch (ReportParseException e) {
@@ -451,7 +458,9 @@ public class EmailParseService {
         emailTypeMap.put(EmailTypeConst.REPORT_EMAIL_TYPE,
                 ListUtil.toList("月报", "月度报告", "季报", "季度报告", "年报", "年度报告"));
         emailTypeMap.put(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE,
-                ListUtil.toList("确认单", "确认函", "交易确认数据", "赎回确认", "申购确认", "分红确认", "确认表", "交易确认", "确认"));
+                ListUtil.toList(ReportType.LETTER.getPatterns()));
+        emailTypeMap.put(EmailTypeConst.REPORT_OTHER_TYPE,
+                ListUtil.toList(ReportType.OTHER.getPatterns()));
         return emailTypeMap;
     }