ソースを参照

fix:管理人观点和周报解析

wangzaijun 4 日 前
コミット
d7ebfd4660
19 ファイル変更189 行追加128 行削除
  1. 2 0
      mo-daq/src/main/java/com/smppw/modaq/application/components/ReportParseUtils.java
  2. 6 0
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ReportParserConstant.java
  3. 1 1
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ReportParserFactory.java
  4. 0 5
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AIAnnuallyReportParser.java
  5. 0 12
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AIMonthlyReportParser.java
  6. 2 2
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AiOtherReportParser.java
  7. 0 5
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AIQuarterlyReportParser.java
  8. 41 0
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AIWeeklyReportParser.java
  9. 40 28
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ai/AbstractAIReportParser.java
  10. 4 0
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/ReportWriterConstant.java
  11. 5 0
      mo-daq/src/main/java/com/smppw/modaq/common/conts/EmailTypeConst.java
  12. 19 0
      mo-daq/src/main/java/com/smppw/modaq/common/dto/FilenamePathDTO.java
  13. 6 3
      mo-daq/src/main/java/com/smppw/modaq/common/enums/ReportType.java
  14. 3 1
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/EmailZipFileDTO.java
  15. 2 1
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/BaseReportDTO.java
  16. 19 0
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/WeeklyReportData.java
  17. 23 13
      mo-daq/src/main/java/com/smppw/modaq/domain/service/EmailParseService.java
  18. 13 54
      mo-daq/src/main/java/com/smppw/modaq/infrastructure/util/ExcelUtil.java
  19. 3 3
      mo-daq/src/test/java/com/smppw/modaq/MoDaqApplicationTests.java

+ 2 - 0
mo-daq/src/main/java/com/smppw/modaq/application/components/ReportParseUtils.java

@@ -327,6 +327,8 @@ public final class ReportParseUtils {
             reportType = ReportType.MONTHLY;
         } else if (StrUtil.containsAny(string, ReportType.LETTER.getPatterns())) {
             reportType = ReportType.LETTER;
+        } else if (StrUtil.containsAny(string, ReportType.WEEKLY.getPatterns())) {
+            reportType = ReportType.WEEKLY;
         } else if (StrUtil.containsAny(string, ReportType.OTHER.getPatterns())) {
             reportType = ReportType.OTHER;
         }

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

@@ -17,6 +17,9 @@ public final class ReportParserConstant {
     // 其他报告
     public static final String PARSER_AI_OTHER = "report-parser:ai:other";
 
+    // 周报
+    public static final String PARSER_AI_WEEKLY = "report-parser:ai:weekly";
+
     // 交易流水确认函解析
     public static final String PARSER_PDF_LETTER = "report-parser:pdf:letter";
     public static final String PARSER_AI_LETTER = "report-parser:ai:letter";
@@ -37,6 +40,9 @@ public final class ReportParserConstant {
         // 其他报告解析
         REPORT_PARSER_BEAN_MAP.put(ReportType.OTHER, Map.of(ReportParserFileType.AI, PARSER_AI_OTHER));
 
+        // 周报
+        REPORT_PARSER_BEAN_MAP.put(ReportType.WEEKLY, Map.of(ReportParserFileType.AI, PARSER_AI_WEEKLY));
+
         // 交易流水确认函解析
         REPORT_PARSER_BEAN_MAP.put(ReportType.LETTER,
                 Map.of(

+ 1 - 1
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ReportParserFactory.java

@@ -5,7 +5,6 @@ import com.smppw.modaq.common.enums.ReportParseStatus;
 import com.smppw.modaq.common.enums.ReportParserFileType;
 import com.smppw.modaq.common.enums.ReportType;
 import com.smppw.modaq.common.exception.NotSupportReportException;
-import com.smppw.modaq.common.exception.ReportParseException;
 import com.smppw.modaq.domain.dto.report.ReportData;
 import org.springframework.stereotype.Component;
 
@@ -23,6 +22,7 @@ public class ReportParserFactory {
     public <T extends ReportData> ReportParser<T> getInstance(ReportType reportType, ReportParserFileType reportParserFileType) {
         String beanName = ReportParserConstant.REPORT_PARSER_BEAN_MAP.getOrDefault(reportType, MapUtil.empty()).get(reportParserFileType);
         ReportParser<? extends ReportData> reportParser = REPORT_WRITER_MAP.get(beanName);
+        // 不支持的解析格式
         if (reportParser == null) {
             throw new NotSupportReportException(ReportParseStatus.NO_SUPPORT_TEMPLATE);
         }

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

@@ -26,11 +26,6 @@ public class AIAnnuallyReportParser extends AbstractAIReportParser<AnnuallyRepor
     }
 
     @Override
-    protected void handleAiResult(String result) throws ReportParseException {
-
-    }
-
-    @Override
     protected AnnuallyReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo,
                                                         ReportFundInfoDTO fundInfo) throws ReportParseException {
         return null;

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

@@ -1,8 +1,6 @@
 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;
@@ -31,16 +29,6 @@ public class AIMonthlyReportParser extends AbstractAIReportParser<MonthlyReportD
     }
 
     @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 {

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

@@ -13,8 +13,8 @@ import org.springframework.stereotype.Component;
  * 其他格式的报告(只解析报告基本信息、基金基本信息)
  */
 @Component(ReportParserConstant.PARSER_AI_OTHER)
-public class AiOtherReportParser extends AbstractAIReportParser<ReportData> {
-    public AiOtherReportParser(EmailFieldMappingMapper fieldMappingMapper) {
+public class AIOtherReportParser extends AbstractAIReportParser<ReportData> {
+    public AIOtherReportParser(EmailFieldMappingMapper fieldMappingMapper) {
         super(fieldMappingMapper);
     }
 

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

@@ -26,11 +26,6 @@ public class AIQuarterlyReportParser extends AbstractAIReportParser<QuarterlyRep
     }
 
     @Override
-    protected void handleAiResult(String result) throws ReportParseException {
-
-    }
-
-    @Override
     protected QuarterlyReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo,
                                                          ReportFundInfoDTO fundInfo) throws ReportParseException {
         return null;

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

@@ -0,0 +1,41 @@
+package com.smppw.modaq.application.components.report.parser.ai;
+
+import com.smppw.modaq.application.components.report.parser.ReportParserConstant;
+import com.smppw.modaq.common.exception.ReportParseException;
+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.WeeklyReportData;
+import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
+import org.springframework.stereotype.Component;
+
+/**
+ * 管理人周报AI解析器
+ */
+@Component(ReportParserConstant.PARSER_AI_WEEKLY)
+public class AIWeeklyReportParser extends AbstractAIReportParser<WeeklyReportData> {
+    public AIWeeklyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
+        super(fieldMappingMapper);
+    }
+
+    @Override
+    protected String prompt() {
+        return "识别文件中的基金名称、基金管理人和报告日期,如果日期是区间段则取截止日期,如果无法识别就返回空字符串,结果用json返回";
+    }
+
+    @Override
+    protected boolean isSupportAIParse() {
+        return true;
+    }
+
+    @Override
+    protected WeeklyReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo,
+                                                      ReportFundInfoDTO fundInfo) throws ReportParseException {
+        return new WeeklyReportData(reportInfo, fundInfo);
+    }
+
+    @Override
+    protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
+        return this.buildDto(params.getFileId(), ReportFundInfoDTO.class, this.allInfoMap);
+    }
+}

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

@@ -40,33 +40,10 @@ public abstract class AbstractAIReportParser<T extends ReportData> extends Abstr
         }
         // 初始化
         this.init();
-        String filename = params.getFilename();
-        String filepath = params.getFilepath();
-        Map<String, Object> paramsMap = MapUtil.newHashMap(4);
-        paramsMap.put("filepath", filepath);
-        paramsMap.put("file_id", params.getAiFileId());
-        String prompt = this.prompt();
-        if (StrUtil.isNotBlank(prompt)) {
-            paramsMap.put("user_msg", prompt);
-        }
-        String body = null;
-        try {
-            body = HttpUtil.get(this.aiParserUrl, paramsMap);
-            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;
-        } catch (Exception e) {
-            this.logger.warn("报告{} 在AI解析时报错:{}", filename, ExceptionUtil.stacktraceToString(e));
-            throw new ReportParseException(ReportParseStatus.AI_NOT_FOUND);
-        }
-        T reportData = this.buildReportData(params, filename);
+        // 解析文件内容,并把文件内容解构到 allInfoMap 对象中
+        this.parseFileContent(params);
+        // 解构话返回解析数据
+        T reportData = this.buildReportData(params, params.getFilename());
         if (reportData != null) {
             reportData.setAiFileId(this.aiFileId);
             reportData.setAiParse(true);
@@ -101,7 +78,14 @@ public abstract class AbstractAIReportParser<T extends ReportData> extends Abstr
      *
      * @param result ai解析结果
      */
-    protected abstract void handleAiResult(String result) throws ReportParseException;
+    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 void init() {
@@ -110,4 +94,32 @@ public abstract class AbstractAIReportParser<T extends ReportData> extends Abstr
         this.aiFileId = null;
         this.allInfoMap = MapUtil.newHashMap(128);
     }
+
+    private void parseFileContent(ReportParserParams params) {
+        String filename = params.getFilename();
+        Map<String, Object> paramsMap = MapUtil.newHashMap(4);
+        paramsMap.put("filepath", params.getFilepath());
+        paramsMap.put("file_id", params.getAiFileId());
+        String prompt = this.prompt();
+        if (StrUtil.isNotBlank(prompt)) {
+            paramsMap.put("user_msg", prompt);
+        }
+        String body = null;
+        try {
+            body = HttpUtil.get(this.aiParserUrl, paramsMap);
+            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;
+        } catch (Exception e) {
+            this.logger.warn("报告{} 在AI解析时报错:{}", filename, ExceptionUtil.stacktraceToString(e));
+            throw new ReportParseException(ReportParseStatus.AI_NOT_FOUND);
+        }
+    }
 }

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

@@ -10,6 +10,8 @@ public final class ReportWriterConstant {
 
     static final String WRITER_OTHER = "report-writer:other";
 
+    static final String WRITER_WEEKLY = "report-writer:weekly";
+
     static final String WRITER_LETTER = "report-writer:letter";
 
     static final String WRITER_MONTHLY = "report-writer:monthly";
@@ -19,6 +21,8 @@ public final class ReportWriterConstant {
     static {
         REPORT_TYPE_BEAN_MAP.put(ReportType.OTHER, WRITER_OTHER);
 
+        REPORT_TYPE_BEAN_MAP.put(ReportType.WEEKLY, WRITER_WEEKLY);
+
         REPORT_TYPE_BEAN_MAP.put(ReportType.LETTER, WRITER_LETTER);
 
         REPORT_TYPE_BEAN_MAP.put(ReportType.MONTHLY, WRITER_MONTHLY);

+ 5 - 0
mo-daq/src/main/java/com/smppw/modaq/common/conts/EmailTypeConst.java

@@ -26,4 +26,9 @@ public class EmailTypeConst {
      * 除定期报告和确认函的其他类型的报告
      */
     public final static Integer REPORT_OTHER_TYPE = 5;
+
+    /**
+     * 管理人周报(区别于定期报告)
+     */
+    public final static Integer REPORT_WEEKLy_TYPE = 6;
 }

+ 19 - 0
mo-daq/src/main/java/com/smppw/modaq/common/dto/FilenamePathDTO.java

@@ -0,0 +1,19 @@
+package com.smppw.modaq.common.dto;
+
+public class FilenamePathDTO {
+    private final String filename;
+    private final String filepath;
+
+    public FilenamePathDTO(String filename, String filepath) {
+        this.filename = filename;
+        this.filepath = filepath;
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public String getFilepath() {
+        return filepath;
+    }
+}

+ 6 - 3
mo-daq/src/main/java/com/smppw/modaq/common/enums/ReportType.java

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

+ 3 - 1
mo-daq/src/main/java/com/smppw/modaq/domain/dto/EmailZipFileDTO.java

@@ -6,12 +6,14 @@ import lombok.Getter;
 @Getter
 public class EmailZipFileDTO {
     private final String filename;
+    private final String originalName;
     private final String filepath;
     private final Integer emailType;
 
-    public EmailZipFileDTO(String filepath, Integer emailType) {
+    public EmailZipFileDTO(String filepath, String originalName, Integer emailType) {
         this.filepath = filepath;
         this.emailType = emailType;
+        this.originalName = originalName;
         this.filename = FileUtil.getName(filepath);
     }
 }

+ 2 - 1
mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/BaseReportDTO.java

@@ -60,11 +60,12 @@ public abstract class BaseReportDTO<T extends BaseReportDO> implements Serializa
             return null;
         }
         try {
-            // 日期格式化,支持格式如下:yyyy年MM月dd日、yyyy-MM-dd、yyyy/MM/dd和yyyyMMdd
+            // 日期格式化,支持格式如下:yyyy年MM月dd日、yyyy-MM-dd、yyyy/MM/dd、yyyy_MM_dd和yyyyMMdd
             return DateUtil.parse(input.trim(),
                     DatePattern.CHINESE_DATE_PATTERN,
                     DatePattern.NORM_DATE_PATTERN,
                     "yyyy/MM/dd",
+                    "yyyy_MM_dd",
                     DateConst.YYYYMMDD);
         } catch (Exception ignored) {
         }

+ 19 - 0
mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/WeeklyReportData.java

@@ -0,0 +1,19 @@
+package com.smppw.modaq.domain.dto.report;
+
+import com.smppw.modaq.common.enums.ReportType;
+
+public class WeeklyReportData extends ReportData {
+    public WeeklyReportData(ReportBaseInfoDTO baseInfo, ReportFundInfoDTO fundInfo) {
+        super(baseInfo, fundInfo);
+    }
+
+    @Override
+    public ReportType getReportType() {
+        return ReportType.WEEKLY;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString();
+    }
+}

+ 23 - 13
mo-daq/src/main/java/com/smppw/modaq/domain/service/EmailParseService.java

@@ -15,6 +15,7 @@ import com.smppw.modaq.application.util.EmailUtil;
 import com.smppw.modaq.common.conts.DateConst;
 import com.smppw.modaq.common.conts.EmailParseStatusConst;
 import com.smppw.modaq.common.conts.EmailTypeConst;
+import com.smppw.modaq.common.dto.FilenamePathDTO;
 import com.smppw.modaq.common.enums.ReportParseStatus;
 import com.smppw.modaq.common.enums.ReportParserFileType;
 import com.smppw.modaq.common.enums.ReportType;
@@ -175,7 +176,7 @@ public class EmailParseService {
             }
         }
 
-        List<String> extractedDirs;
+        List<FilenamePathDTO> extractedDirs;
         if (ExcelUtil.isZip(filepath)) {
             extractedDirs = ExcelUtil.extractCompressedFiles(filepath, destPath);
         } else if (ExcelUtil.isRAR(filepath)) {
@@ -183,7 +184,8 @@ public class EmailParseService {
         } else {
             return;
         }
-        for (String dir : extractedDirs) {
+        for (FilenamePathDTO dto : extractedDirs) {
+            String dir = dto.getFilepath();
             // 如果邮件类型不满足解析条件则重新根据文件名判断
             if (!Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType)
                     && !Objects.equals(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE, emailType)) {
@@ -194,13 +196,13 @@ public class EmailParseService {
                 String[] subDirs = file.list();
                 if (subDirs != null) {
                     for (String subDir : subDirs) {
-                        resultList.add(new EmailZipFileDTO(subDir, emailType));
+                        resultList.add(new EmailZipFileDTO(subDir, dto.getFilename(), emailType));
                     }
                 } else {
                     log.warn("目录 {} 下无文件", dir);
                 }
             } else {
-                resultList.add(new EmailZipFileDTO(dir, emailType));
+                resultList.add(new EmailZipFileDTO(dir, dto.getFilename(), emailType));
             }
         }
     }
@@ -232,9 +234,9 @@ public class EmailParseService {
             List<EmailZipFileDTO> zipFiles = entry.getValue();
             if (CollUtil.isNotEmpty(zipFiles)) {
                 for (EmailZipFileDTO zipFile : zipFiles) {
-                    EmailFileInfoDO emailFile = saveEmailFileInfo(emailId, null, zipFile.getFilename(), zipFile.getFilepath(), null);
-                    // 解析结果(可以从python获取或者自行解析)并保存报告
-                    ParseResult<ReportData> parseResult = this.parseReportAndHandleResult(emailFile.getId(), zipFile.getFilename(),
+                    EmailFileInfoDO emailFile = saveEmailFileInfo(emailId, null, zipFile.getOriginalName(), zipFile.getFilepath(), null);
+                    // 解析并保存报告
+                    ParseResult<ReportData> parseResult = this.parseReportAndHandleResult(emailFile.getId(), zipFile.getOriginalName(),
                             zipFile.getFilepath(), zipFile.getEmailType(), emailFile.getAiFileId());
                     dataList.add(parseResult);
                 }
@@ -242,7 +244,7 @@ public class EmailParseService {
                 String fileName = emailContentInfoDTO.getFileName();
                 EmailFileInfoDO emailFile = saveEmailFileInfo(emailId, emailContentInfoDTO.getFileId(), fileName,
                         emailContentInfoDTO.getFilePath(), emailContentInfoDTO.getAiFileId());
-                // 解析结果(可以从python获取或者自行解析)并保存报告
+                // 解析并保存报告
                 ParseResult<ReportData> parseResult = this.parseReportAndHandleResult(emailFile.getId(), fileName,
                         emailContentInfoDTO.getFilePath(), emailContentInfoDTO.getEmailType(), emailFile.getAiFileId());
                 dataList.add(parseResult);
@@ -275,6 +277,7 @@ public class EmailParseService {
         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_WEEKLy_TYPE, emailType)
                 && !Objects.equals(EmailTypeConst.REPORT_OTHER_TYPE, emailType);
         if (reportFlag || StrUtil.isBlank(fileName) || fileName.endsWith(".html")) {
             result.setStatus(ReportParseStatus.NOT_A_REPORT.getCode());
@@ -308,13 +311,14 @@ public class EmailParseService {
             result.setMsg(StrUtil.format(ReportParseStatus.NOT_A_REPORT.getMsg(), fileName));
             return result;
         }
+        // 不支持解析的格式文件
+        boolean notSupportFile = false;
         // 解析报告
         ReportData reportData = null;
-        boolean notSupportFile = false;
         StopWatch parserWatch = new StopWatch();
         parserWatch.start();
         try {
-            if (StrUtil.isBlank(aiFileId) && reportType != ReportType.OTHER) {
+            if (StrUtil.isBlank(aiFileId) && reportType != ReportType.OTHER && reportType != ReportType.WEEKLY) {
                 ReportParserParams params = ReportParserParams.builder().fileId(fileId).filename(fileName).filepath(filepath)
                         .registerNumber(registerNumber).reportType(reportType).build();
                 ReportParser<ReportData> instance = this.reportParserFactory.getInstance(reportType, fileType);
@@ -323,9 +327,9 @@ public class EmailParseService {
                 result.setMsg("报告解析成功");
                 result.setData(reportData);
             } else {
-                if (reportType == ReportType.OTHER) {
+                if (reportType == ReportType.OTHER || reportType == ReportType.WEEKLY) {
                     if (log.isInfoEnabled()) {
-                        log.info("报告{} 是其他类型,直接用AI解析器解析", fileName);
+                        log.info("报告{} 是周报或其他类型,直接用AI解析器解析", fileName);
                     }
                 } else {
                     if (log.isInfoEnabled()) {
@@ -356,8 +360,12 @@ public class EmailParseService {
                 try {
                     reportData = instance.parse(params);
                     result.setStatus(1);
-                    result.setMsg("报告解析成功");
+                    result.setMsg("报告解析成功--AI");
                     result.setData(reportData);
+                } catch (ReportParseException e) {
+                    log.error("AI解析失败:{}", StrUtil.format(e.getMsg(), fileName));
+                    result.setStatus(e.getCode());
+                    result.setMsg(StrUtil.format(e.getMsg(), fileName));
                 } catch (Exception e) {
                     log.error("AI解析错误:{}", ExceptionUtil.stacktraceToString(e));
                     result.setStatus(ReportParseStatus.PARSE_FAIL.getCode());
@@ -461,6 +469,8 @@ public class EmailParseService {
                 ListUtil.toList(ReportType.LETTER.getPatterns()));
         emailTypeMap.put(EmailTypeConst.REPORT_OTHER_TYPE,
                 ListUtil.toList(ReportType.OTHER.getPatterns()));
+        emailTypeMap.put(EmailTypeConst.REPORT_WEEKLy_TYPE,
+                ListUtil.toList(ReportType.WEEKLY.getPatterns()));
         return emailTypeMap;
     }
 

+ 13 - 54
mo-daq/src/main/java/com/smppw/modaq/infrastructure/util/ExcelUtil.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.util.StrUtil;
+import com.smppw.modaq.common.dto.FilenamePathDTO;
 import net.sf.sevenzipjbinding.ExtractOperationResult;
 import net.sf.sevenzipjbinding.IInArchive;
 import net.sf.sevenzipjbinding.SevenZip;
@@ -47,8 +48,8 @@ public class ExcelUtil {
         return StrUtil.isNotBlank(fileName) && (fileName.endsWith("rar") || fileName.endsWith("RAR"));
     }
 
-    public static List<String> extractCompressedFiles(String zipFilePath, String destFilePath) throws IOException, ArchiveException {
-        List<String> filePathList = CollUtil.newArrayList();
+    public static List<FilenamePathDTO> extractCompressedFiles(String zipFilePath, String destFilePath) throws IOException, ArchiveException {
+        List<FilenamePathDTO> filePathList = CollUtil.newArrayList();
 
         File destFile = FileUtil.file(destFilePath);
         if (!destFile.exists()) {
@@ -77,7 +78,7 @@ public class ExcelUtil {
                 } else {
                     try (FileOutputStream fos = new FileOutputStream(entryFile)) {
                         IOUtils.copy(ais, fos);
-                        filePathList.add(entryFile.getPath());
+                        filePathList.add(new FilenamePathDTO(entryFile.getName(), entryFile.getPath()));
                     }
                 }
             }
@@ -94,50 +95,8 @@ public class ExcelUtil {
         return filePathList;
     }
 
-//    public static List<String> extractRar(String inputFilePath, String outputDirPath) throws IOException {
-//        List<String> fileList = new ArrayList<>();
-//        // 创建Archive对象,用于读取rar压缩文件格式
-//        try {
-//            String zipFilename = FileUtil.getName(inputFilePath);
-//            Archive archive = new Archive(new FileInputStream(inputFilePath));
-//            // 读取压缩文件中的所有子目录或子文件(FileHeader对象)
-//            List<FileHeader> fileHeaderList = archive.getFileHeaders();
-//            int i = 1;
-//            // 遍历子目录和子文件
-//            for (FileHeader fd : fileHeaderList) {
-//                String fileName = fd.getFileName();
-//                String ext = FileUtil.extName(fileName);
-//                fileName = zipFilename + "_" + i + "." + ext;
-//                i++;
-//                File f = FileUtil.file(outputDirPath + File.separator + fileName);
-//                if (fd.isDirectory()) {
-//                    // 创建新子目录
-//                    Files.createDirectories(f.toPath());
-//                } else {
-//                    // 创建新子文件
-//                    Files.createFile(f.toPath());
-//                    // 获取压缩包中的子文件输出流
-//                    InputStream in = archive.getInputStream(fd);
-//                    // 复制文件输入流至新子文件
-//                    FileUtil.copyFile(in, f);
-//                    fileList.add(f.getAbsolutePath());
-//                }
-//            }
-//        } catch (RarException e) {
-//            try {
-//                List<File> extract = Junrar.extract(new File(inputFilePath), new File(outputDirPath));
-//                for (File file : extract) {
-//                    fileList.add(file.getAbsolutePath());
-//                }
-//            } catch (RarException ex) {
-//                throw new RuntimeException(ex);
-//            }
-//        }
-//        return fileList;
-//    }
-
-    public static List<String> extractSplitZip(String zipFilePath, String destFilePath) throws IOException {
-        List<String> resultList = ListUtil.list(false);
+    public static List<FilenamePathDTO> extractSplitZip(String zipFilePath, String destFilePath) throws IOException {
+        List<FilenamePathDTO> resultList = ListUtil.list(false);
         File file = new File(zipFilePath);
         try (ZipFile zipFile = ZipFile.builder().setFile(file).get()) {
             Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
@@ -148,21 +107,21 @@ public class ExcelUtil {
                     Path path = Paths.get(destFilePath, entry.getName());
                     FileUtil.del(path);
                     Files.copy(is, path);
-                    resultList.add(path.toAbsolutePath().toString());
+                    resultList.add(new FilenamePathDTO(entry.getName(), path.toAbsolutePath().toString()));
                 }
             }
         }
         return resultList;
     }
 
-    public static List<String> extractRar5(String rarFilePath, String outputDir) throws Exception {
+    public static List<FilenamePathDTO> extractRar5(String rarFilePath, String outputDir) throws Exception {
         // 初始化 SevenZipJBinding 本地库
         SevenZip.initSevenZipFromPlatformJAR();
 
         RandomAccessFile randomAccessFile = null;
         IInArchive inArchive = null;
 
-        List<String> resultList = ListUtil.list(false);
+        List<FilenamePathDTO> resultList = ListUtil.list(false);
         try {
             // 打开 RAR 文件
             randomAccessFile = new RandomAccessFile(rarFilePath, "r");
@@ -172,7 +131,7 @@ public class ExcelUtil {
             ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
             for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                 if (!item.isFolder()) {
-                    resultList.add(extractItem(item, outputDir));
+                    resultList.add(new FilenamePathDTO(item.getPath(), extractItem(item, outputDir)));
                 }
             }
         } finally {
@@ -220,9 +179,9 @@ public class ExcelUtil {
     public static void main(String[] args) throws Exception {
         String zipFilePath = "D:\\home\\wwwroot\\mo_report_file\\wangzaijun@simuwang.com\\20250321\\20250321143709排排网确认单.rar";
         String destFilePath = "D:\\home\\wwwroot\\mo_report_file\\wangzaijun@simuwang.com\\20250321";
-        List<String> strings = extractRar5(zipFilePath, destFilePath);
-        for (String string : strings) {
-
+        List<FilenamePathDTO> strings = extractRar5(zipFilePath, destFilePath);
+        for (FilenamePathDTO string : strings) {
+            System.out.println(string);
         }
 //        List<String> fileList = extractCompressedFiles(zipFilePath, destFilePath);
 //        for (String s : fileList) {

+ 3 - 3
mo-daq/src/test/java/com/smppw/modaq/MoDaqApplicationTests.java

@@ -34,9 +34,9 @@ public class MoDaqApplicationTests {
 
     @Test
     public void reportTest() {
-        MailboxInfoDTO emailInfoDTO = this.buildMailbox("xx@simuwang.com", "**");
-        Date startDate = DateUtil.parse("2025-04-15 14:40:00", DateConst.YYYY_MM_DD_HH_MM_SS);
-        Date endDate = DateUtil.parse("2025-04-15 19:42:05", DateConst.YYYY_MM_DD_HH_MM_SS);
+        MailboxInfoDTO emailInfoDTO = this.buildMailbox("wangzaijun@simuwang.com", "WZJ2twy1314");
+        Date startDate = DateUtil.parse("2025-04-24 08:40:00", DateConst.YYYY_MM_DD_HH_MM_SS);
+        Date endDate = DateUtil.parse("2025-04-24 19:42:05", DateConst.YYYY_MM_DD_HH_MM_SS);
         try {
             emailParseService.parseEmail(emailInfoDTO, startDate, endDate);
         } catch (Exception e) {