Explorar o código

feat:新增月报和周报解析功能--开始

wangzaijun hai 1 mes
pai
achega
d16855660d
Modificáronse 27 ficheiros con 630 adicións e 1042 borrados
  1. 10 12
      mo-daq/src/main/java/com/smppw/modaq/application/components/CustomPDFTextStripper.java
  2. 37 35
      mo-daq/src/main/java/com/smppw/modaq/application/components/ReportParseUtils.java
  3. 39 26
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ReportParserConstant.java
  4. 0 2
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/ReportParserFactory.java
  5. 0 143
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDAnnuallyReportParser.java
  6. 65 58
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDMonthlyReportParser.java
  7. 0 266
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDQuarterlyReportParser.java
  8. 39 0
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDWeeklyReportParser.java
  9. 0 14
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/AnnuallyReportWriter.java
  10. 25 25
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/MonthlyReportWriter.java
  11. 0 62
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/QuarterlyReportWriter.java
  12. 6 4
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/ReportWriterConstant.java
  13. 25 0
      mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/WeeklyReportWriter.java
  14. 163 173
      mo-daq/src/main/java/com/smppw/modaq/application/util/EmailUtil.java
  15. 3 3
      mo-daq/src/main/java/com/smppw/modaq/common/enums/ReportParserFileType.java
  16. 4 3
      mo-daq/src/main/java/com/smppw/modaq/common/enums/ReportType.java
  17. 50 50
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/QuartzBean.java
  18. 0 18
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/AnnuallyReportData.java
  19. 31 11
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/BaseReportDTO.java
  20. 25 25
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/MonthlyReportData.java
  21. 0 41
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/QuarterlyReportData.java
  22. 5 3
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/ReportFundTransactionDTO.java
  23. 23 0
      mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/WeeklyReportData.java
  24. 71 54
      mo-daq/src/main/java/com/smppw/modaq/domain/service/EmailParseService.java
  25. 2 2
      mo-daq/src/main/java/com/smppw/modaq/infrastructure/util/ExcelUtil.java
  26. 3 8
      mo-daq/src/main/java/com/smppw/modaq/infrastructure/util/FileUtil.java
  27. 4 4
      mo-daq/src/test/java/com/smppw/modaq/MoDaqApplicationTests.java

+ 10 - 12
mo-daq/src/main/java/com/smppw/modaq/application/components/CustomPDFTextStripper.java

@@ -21,32 +21,30 @@ public class CustomPDFTextStripper extends PDFTextStripper {
     @Override
     protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
         // 水印文字基本都是有角度的,统计有旋转角度的文字高度
-        List<Float> heights = ListUtil.list(false);
+        List<TextPosition> rotationTexts = ListUtil.list(false);
+        // 去除水印后的文字
+        List<String> newTexts = ListUtil.list(false);
         for (TextPosition textPosition : textPositions) {
             float[][] values = textPosition.getTextMatrix().getValues();
             double degrees = Math.toDegrees(Math.atan2(values[0][1], values[0][0]));
+            // 如果旋转角度除90的余数大于0.1就说明是水印文字
             if (degrees % 90. > 0.1) {
-                heights.add(textPosition.getHeight());
+                rotationTexts.add(textPosition);
+                newTexts.add(WATERMARK_REPLACE);
+            } else {
+                newTexts.add(textPosition.getUnicode());
             }
         }
         // 集合为空表示text的内容没有水印影响,直接输出该内容
-        if (CollUtil.isEmpty(heights)) {
+        if (CollUtil.isEmpty(rotationTexts)) {
             super.writeString(text);
             return;
         }
         // 如果全是水印文字则直接去除
-        if (textPositions.size() == heights.size()) {
+        if (textPositions.size() == rotationTexts.size()) {
             super.writeString(WATERMARK_REPLACE);
             return;
         }
-        // 否则去除水印(文字没有旋转角度,并且水印字体大小没有包含当前文字时说明是正常文字;否则识别为水印并用特殊符号代替)
-        List<String> newTexts = ListUtil.list(false);
-        for (TextPosition textPosition : textPositions) {
-            float[][] values = textPosition.getTextMatrix().getValues();
-            double degrees = Math.toDegrees(Math.atan2(values[0][1], values[0][0]));
-            float height = textPosition.getHeight();
-            newTexts.add(degrees % 90. <= 0.1 && !heights.contains(height) ? textPosition.getUnicode() : WATERMARK_REPLACE);
-        }
         super.writeString(String.join(StrUtil.EMPTY, newTexts));
     }
 }

+ 37 - 35
mo-daq/src/main/java/com/smppw/modaq/application/components/ReportParseUtils.java

@@ -21,7 +21,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 
 
 public final class ReportParseUtils {
@@ -203,35 +202,35 @@ public final class ReportParseUtils {
         return StrUtil.isBlank(fieldValue) ? null : fieldValue;
     }
 
-    /**
-     * 匹配分级基金名称(并且把母基金追加到第一行)
-     *
-     * @param text 文本内容
-     * @return /
-     */
-    public static List<String> matchTieredFund(String text) {
-        List<String> matches = ListUtil.list(false);
-        if (StrUtil.isBlank(text)) {
-            matches.add("母基金");
-            return matches;
-        }
-        // 使用正则表达式查找匹配项
-        Pattern pattern = Pattern.compile("[A-F]级|基金[A-F]");
-        Matcher matcher = pattern.matcher(text);
-        // 收集所有匹配项
-        while (matcher.find()) {
-            matches.add(matcher.group());
-        }
-        // 提取字母并按字母顺序排序
-        List<String> levels = matches.stream()
-                .map(s -> s.replaceAll("[^A-F]", ""))
-                .distinct()
-                .sorted()
-                .map(letter -> letter + "级")
-                .collect(Collectors.toList());
-        levels.add(0, "母基金");
-        return levels;
-    }
+//    /**
+//     * 匹配分级基金名称(并且把母基金追加到第一行)
+//     *
+//     * @param text 文本内容
+//     * @return /
+//     */
+//    public static List<String> matchTieredFund(String text) {
+//        List<String> matches = ListUtil.list(false);
+//        if (StrUtil.isBlank(text)) {
+//            matches.add("母基金");
+//            return matches;
+//        }
+//        // 使用正则表达式查找匹配项
+//        Pattern pattern = Pattern.compile("[A-F]级|基金[A-F]");
+//        Matcher matcher = pattern.matcher(text);
+//        // 收集所有匹配项
+//        while (matcher.find()) {
+//            matches.add(matcher.group());
+//        }
+//        // 提取字母并按字母顺序排序
+//        List<String> levels = matches.stream()
+//                .map(s -> s.replaceAll("[^A-F]", ""))
+//                .distinct()
+//                .sorted()
+//                .map(letter -> letter + "级")
+//                .collect(Collectors.toList());
+//        levels.add(0, "母基金");
+//        return levels;
+//    }
 
     /**
      * 匹配报告日期
@@ -295,12 +294,15 @@ public final class ReportParseUtils {
     public static ReportType matchReportType(String string) {
         // 类型识别---先识别季度报告,没有季度再识别年度报告,最后识别月报
         ReportType reportType = null;
-        if (StrUtil.containsAny(string, ReportType.QUARTERLY.getPatterns())) {
-            reportType = ReportType.QUARTERLY;
-        } else if (StrUtil.containsAny(string, ReportType.ANNUALLY.getPatterns())) {
-            reportType = ReportType.ANNUALLY;
-        } else if (StrUtil.containsAny(string, ReportType.MONTHLY.getPatterns())) {
+//        if (StrUtil.containsAny(string, ReportType.QUARTERLY.getPatterns())) {
+//            reportType = ReportType.QUARTERLY;
+//        } else if (StrUtil.containsAny(string, ReportType.ANNUALLY.getPatterns())) {
+//            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;
         }

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

@@ -17,20 +17,25 @@ public final class ReportParserConstant {
     // 交易流水确认函解析
     public static final String PARSER_PDF_LETTER = "report-parser:pdf:letter";
 
+    public static final String PARSER_PDF_WEEKLY = "report-parser:pdf:weekly";
+    //    public static final String PARSER_WORD_WEEKLY = "report-parser:word:weekly";
+    public static final String PARSER_EXCEL_WEEKLY = "report-parser:excel:weekly";
+//    public static final String PARSER_PYTHON_WEEKLY = "report-parser:python:weekly";
+
     public static final String PARSER_PDF_MONTHLY = "report-parser:pdf:monthly";
-    public static final String PARSER_WORD_MONTHLY = "report-parser:word:monthly";
+    //    public static final String PARSER_WORD_MONTHLY = "report-parser:word:monthly";
     public static final String PARSER_EXCEL_MONTHLY = "report-parser:excel:monthly";
-    public static final String PARSER_PYTHON_MONTHLY = "report-parser:python:monthly";
-
-    public static final String PARSER_PDF_QUARTERLY = "report-parser:pdf:quarterly";
-    public static final String PARSER_WORD_QUARTERLY = "report-parser:word:quarterly";
-    public static final String PARSER_EXCEL_QUARTERLY = "report-parser:excel:quarterly";
-    public static final String PARSER_PYTHON_QUARTERLY = "report-parser:python:quarterly";
+//    public static final String PARSER_PYTHON_MONTHLY = "report-parser:python:monthly";
 
-    public static final String PARSER_PDF_ANNUALLY = "report-parser:pdf:annually";
-    public static final String PARSER_WORD_ANNUALLY = "report-parser:word:annually";
-    public static final String PARSER_EXCEL_ANNUALLY = "report-parser:excel:annually";
-    public static final String PARSER_PYTHON_ANNUALLY = "report-parser:python:annually";
+//    public static final String PARSER_PDF_QUARTERLY = "report-parser:pdf:quarterly";
+//    public static final String PARSER_WORD_QUARTERLY = "report-parser:word:quarterly";
+//    public static final String PARSER_EXCEL_QUARTERLY = "report-parser:excel:quarterly";
+//    public static final String PARSER_PYTHON_QUARTERLY = "report-parser:python:quarterly";
+//
+//    public static final String PARSER_PDF_ANNUALLY = "report-parser:pdf:annually";
+//    public static final String PARSER_WORD_ANNUALLY = "report-parser:word:annually";
+//    public static final String PARSER_EXCEL_ANNUALLY = "report-parser:excel:annually";
+//    public static final String PARSER_PYTHON_ANNUALLY = "report-parser:python:annually";
 
     static {
         // 交易流水确认函解析
@@ -38,26 +43,34 @@ public final class ReportParserConstant {
 
         REPORT_PARSER_BEAN_MAP.put(ReportType.MONTHLY,
                 Map.of(ReportParserFileType.PDF, PARSER_PDF_MONTHLY,
-                        ReportParserFileType.WORD, PARSER_WORD_MONTHLY,
-                        ReportParserFileType.EXCEL, PARSER_EXCEL_MONTHLY,
+//                        ReportParserFileType.WORD, PARSER_WORD_MONTHLY,
+                        ReportParserFileType.EXCEL, PARSER_EXCEL_MONTHLY
 
-                        ReportParserFileType.PYTHON, PARSER_PYTHON_MONTHLY
+//                        ReportParserFileType.PYTHON, PARSER_PYTHON_MONTHLY
                 ));
 
-        REPORT_PARSER_BEAN_MAP.put(ReportType.QUARTERLY,
-                Map.of(ReportParserFileType.PDF, PARSER_PDF_QUARTERLY,
-                        ReportParserFileType.WORD, PARSER_WORD_QUARTERLY,
-                        ReportParserFileType.EXCEL, PARSER_EXCEL_QUARTERLY,
+        REPORT_PARSER_BEAN_MAP.put(ReportType.WEEKLY,
+                Map.of(ReportParserFileType.PDF, PARSER_PDF_WEEKLY,
+//                        ReportParserFileType.WORD, PARSER_WORD_WEEKLY,
+                        ReportParserFileType.EXCEL, PARSER_EXCEL_WEEKLY
 
-                        ReportParserFileType.PYTHON, PARSER_PYTHON_QUARTERLY
+//                        ReportParserFileType.PYTHON, PARSER_PYTHON_WEEKLY
                 ));
 
-        REPORT_PARSER_BEAN_MAP.put(ReportType.ANNUALLY,
-                Map.of(ReportParserFileType.PDF, PARSER_PDF_ANNUALLY,
-                        ReportParserFileType.WORD, PARSER_WORD_ANNUALLY,
-                        ReportParserFileType.EXCEL, PARSER_EXCEL_ANNUALLY,
-
-                        ReportParserFileType.PYTHON, PARSER_PYTHON_ANNUALLY
-                ));
+//        REPORT_PARSER_BEAN_MAP.put(ReportType.QUARTERLY,
+//                Map.of(ReportParserFileType.PDF, PARSER_PDF_QUARTERLY,
+//                        ReportParserFileType.WORD, PARSER_WORD_QUARTERLY,
+//                        ReportParserFileType.EXCEL, PARSER_EXCEL_QUARTERLY,
+//
+//                        ReportParserFileType.PYTHON, PARSER_PYTHON_QUARTERLY
+//                ));
+//
+//        REPORT_PARSER_BEAN_MAP.put(ReportType.ANNUALLY,
+//                Map.of(ReportParserFileType.PDF, PARSER_PDF_ANNUALLY,
+//                        ReportParserFileType.WORD, PARSER_WORD_ANNUALLY,
+//                        ReportParserFileType.EXCEL, PARSER_EXCEL_ANNUALLY,
+//
+//                        ReportParserFileType.PYTHON, PARSER_PYTHON_ANNUALLY
+//                ));
     }
 }

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

@@ -12,8 +12,6 @@ import java.util.Map;
 
 @Component
 public class ReportParserFactory {
-    private static final ReportParser<? extends ReportData> DEFAULT = (ReportParser<ReportData>) params -> null;
-
     private static final Map<String, ReportParser<? extends ReportData>> REPORT_WRITER_MAP = MapUtil.newHashMap(32);
 
     public ReportParserFactory(Map<String, ReportParser<? extends ReportData>> components) {

+ 0 - 143
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDAnnuallyReportParser.java

@@ -1,143 +0,0 @@
-//package com.smppw.modaq.infrastructure.components.report.parser.pdf;
-//
-//import cn.hutool.core.collection.CollUtil;
-//import cn.hutool.core.collection.ListUtil;
-//import cn.hutool.core.map.MapUtil;
-//import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
-//import com.smppw.modaq.infrastructure.components.ReportParseUtils;
-//import com.smppw.modaq.infrastructure.components.report.parser.ReportParserConstant;
-//import com.smppw.modaq.infrastructure.dto.report.AnnuallyReportData;
-//import com.smppw.modaq.infrastructure.dto.report.ReportFundInfoDTO;
-//import com.smppw.modaq.infrastructure.dto.report.ReportParserParams;
-//import org.springframework.stereotype.Component;
-//import technology.tabula.Table;
-//
-//import java.util.List;
-//import java.util.Map;
-//import java.util.Set;
-//import java.util.function.Function;
-//
-///**
-// * @author wangzaijun
-// * @date 2024/10/10 17:34
-// * @description 年报解析逻辑:基本信息被拆分为多个表格,财务报表未解析
-// */
-//@Component(ReportParserConstant.PARSER_PDF_ANNUALLY)
-//public class PDAnnuallyReportParser extends PDQuarterlyReportParser<AnnuallyReportData> {
-//    private List<Table> fundInfoTables;
-//
-//    public PDAnnuallyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
-//        super(fieldMappingMapper);
-//    }
-//
-//    @Override
-//    public String getParser() {
-//        return ReportParserConstant.PARSER_PDF_ANNUALLY;
-//    }
-//
-//    @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) {
-//                this.fundInfoTables.add(table);
-//                continue;
-//            }
-//            // 用表格的第一列的数据判断是否主要财务指标数据
-//            List<String> texts = this.getTableColTexts(table, 0);
-//            if (CollUtil.containsAny(texts, ReportParseUtils.FINANCIAL_INDICATORS_COLUMN_NAMES)) {
-//                this.financialIndicatorsTables.add(table);
-//                continue;
-//            }
-//            int colCount = table.getColCount();
-//            if (colCount == 2) {
-//                // 用表格的第一列的数据判断是否份额变动记录
-//                if (CollUtil.containsAny(texts, ReportParseUtils.SHARE_CHANGE_COLUMN_NAMES)) {
-//                    this.shareChangeTables.add(table);
-//                }
-//            } else if (colCount == 4) {
-//                // 用表格的第二列的数据判断是否行业配置数据(内地)
-//                texts = this.getTableColTexts(table, 1);
-//                if (CollUtil.containsAny(texts, ReportParseUtils.INDUSTRY_COLUMN_NAMES)) {
-//                    this.investmentIndustryTables.add(table);
-//                }
-//            } else if (colCount == 3) {
-//                // 用表格的第一列的数据判断是否行业配置数据(港股通)
-//                if (CollUtil.containsAny(texts, ReportParseUtils.INDUSTRY_COLUMN_NAMES)) {
-//                    this.investmentIndustryTables.add(table);
-//                    continue;
-//                }
-//                // 资产配置表格识别(兼容跨页的表格)获取表格中第二列的所有文字,判断所有文字中包含"股权投资"等字符串
-//                texts = this.getTableColTexts(table, 1);
-//                Set<String> keys = ReportParseUtils.ASSET_ALLOCATION_TYPE_MAPPER.keySet();
-//                if (CollUtil.containsAny(texts, keys)) {
-//                    this.assetAllocationTables.add(table);
-//                }
-//            }
-//        }
-//    }
-//
-//    @Override
-//    protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
-//        Map<String, Object> fundInfoMap = MapUtil.newHashMap(32);
-//        for (Table table : this.fundInfoTables) {
-//            Map<String, Object> temp = this.parseFundInfo(table);
-//            fundInfoMap.putAll(temp);
-//        }
-//        ReportFundInfoDTO info = new ReportFundInfoDTO(params.getFileId());
-//        this.buildInfo(fundInfoMap, info);
-//        return info;
-//    }
-//
-//    @Override
-//    protected AnnuallyReportData buildReportData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo,
-//                                                 List<ReportShareChangeDTO> shareChanges, List<ReportFinancialIndicatorsDTO> financialIndicators,
-//                                                 List<ReportAssetAllocationDTO> assetAllocations, List<ReportInvestmentIndustryDTO> investmentIndustries) {
-//        AnnuallyReportData reportData = new AnnuallyReportData(reportInfo, fundInfo);
-//        reportData.setShareChange(shareChanges);
-//        reportData.setFinancialIndicators(financialIndicators);
-//        reportData.setAssetAllocation(assetAllocations);
-//        reportData.setInvestmentIndustry(investmentIndustries);
-//        return reportData;
-//    }
-//
-//    @Override
-//    protected void cleaningReportData(AnnuallyReportData reportData) {
-//        // todo 数据清洗
-//    }
-//
-//    protected List<ReportFinancialIndicatorsDTO> buildFinancialIndicatorsInfo(Integer fileId, Function<Table, Map<String, Object>> function) {
-//        List<ReportFinancialIndicatorsDTO> dtos = ListUtil.list(false);
-//        // 分级基金
-//        List<String> levels = ReportParseUtils.matchTieredFund(String.join(",", this.textList));
-//        // 假设这里可能存在分级基金,不存在表格跨页
-//        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 = ReportParseUtils.cleaningValue(table.getCell(0, j).getText());
-//                infoMap.put("年度", year);
-//                for (int i = 0; i < table.getRowCount(); i++) {
-//                    String columnName = ReportParseUtils.cleaningValue(table.getCell(i, 0).getText());
-//                    if (columnName == null) {
-//                        continue;
-//                    }
-//                    String value = ReportParseUtils.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);
-//            }
-//        }
-//        return dtos;
-//    }
-//}

+ 65 - 58
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDMonthlyReportParser.java

@@ -1,55 +1,62 @@
-//package com.smppw.modaq.infrastructure.components.report.parser.pdf;
-//
-//import cn.hutool.core.map.MapUtil;
-//import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
-//import com.smppw.modaq.infrastructure.components.report.parser.ReportParserConstant;
-//import com.smppw.modaq.infrastructure.dto.report.MonthlyReportData;
-//import com.smppw.modaq.infrastructure.dto.report.ReportBaseInfoDTO;
-//import com.smppw.modaq.infrastructure.dto.report.ReportFundInfoDTO;
-//import org.springframework.stereotype.Component;
-//import technology.tabula.RectangularTextContainer;
-//import technology.tabula.Table;
-//
-//import java.util.List;
-//import java.util.Map;
-//
-///**
-// * @author wangzaijun
-// * @date 2024/9/11 16:19
-// * @description pdf格式的月报解析
-// */
-//@Component(ReportParserConstant.PARSER_PDF_MONTHLY)
-//public class PDMonthlyReportParser extends AbstractPDReportParser<MonthlyReportData> {
-////    private List<Table> extNavTables;
-//
-//    public PDMonthlyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
-//        super(fieldMappingMapper);
-//    }
-//
-//    @Override
-//    public String getParser() {
-//        return ReportParserConstant.PARSER_PDF_MONTHLY;
-//    }
-//
-//    @Override
-//    protected void initTableInfo(List<Table> tables) {
-////        // 这里初始化
-////        this.extNavTables = ListUtil.list(true);
-////        // 一般月报是固定的模板,4列表格是基金基本信息,其他5列的表格是月净值
-////        for (Table table : tables) {
-////            int colCount = table.getColCount();
-////            int rowCount = table.getRowCount();
-////            if (colCount == 0 && rowCount == 0) {
-////                continue;
-////            }
-////            if (colCount == 4) {
-////                this.fundInfoTable = table;
-////            } else if (colCount >= 5) {
-////                this.extNavTables.add(table);
-////            }
-////        }
-//    }
-//
+package com.smppw.modaq.application.components.report.parser.pdf;
+
+import com.smppw.modaq.application.components.report.parser.ReportParserConstant;
+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.mapper.EmailFieldMappingMapper;
+import org.springframework.stereotype.Component;
+import technology.tabula.Table;
+
+import java.util.List;
+
+/**
+ * @author wangzaijun
+ * @date 2024/9/11 16:19
+ * @description pdf格式的月报解析
+ */
+@Component(ReportParserConstant.PARSER_PDF_MONTHLY)
+public class PDMonthlyReportParser extends AbstractPDReportParser<MonthlyReportData> {
+//    private List<Table> extNavTables;
+
+    public PDMonthlyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
+        super(fieldMappingMapper);
+    }
+
+    @Override
+    public String getParser() {
+        return ReportParserConstant.PARSER_PDF_MONTHLY;
+    }
+
+    @Override
+    protected void initTableInfo(List<Table> tables) {
+//        // 这里初始化
+//        this.extNavTables = ListUtil.list(true);
+//        // 一般月报是固定的模板,4列表格是基金基本信息,其他5列的表格是月净值
+//        for (Table table : tables) {
+//            int colCount = table.getColCount();
+//            int rowCount = table.getRowCount();
+//            if (colCount == 0 && rowCount == 0) {
+//                continue;
+//            }
+//            if (colCount == 4) {
+//                this.fundInfoTable = table;
+//            } else if (colCount >= 5) {
+//                this.extNavTables.add(table);
+//            }
+//        }
+    }
+
+    @Override
+    protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
+        return null;
+    }
+
+    @Override
+    protected MonthlyReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo) {
+        return null;
+    }
 //    @Override
 //    protected Map<String, Object> parseFundInfo(Table fundInfoTable) {
 //        // 月报的基金基本信息是四列的表格
@@ -81,9 +88,9 @@
 ////        reportData.setNetReport(dtos);
 //        return reportData;
 //    }
-//
-//    @Override
-//    protected void cleaningReportData(MonthlyReportData reportData) {
-//        // todo 数据清洗
-//    }
-//}
+
+    @Override
+    protected void cleaningReportData(MonthlyReportData reportData) {
+        // todo 数据清洗
+    }
+}

+ 0 - 266
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDQuarterlyReportParser.java

@@ -1,266 +0,0 @@
-//package com.smppw.modaq.infrastructure.components.report.parser.pdf;
-//
-//import cn.hutool.core.collection.CollUtil;
-//import cn.hutool.core.collection.ListUtil;
-//import cn.hutool.core.map.MapUtil;
-//import cn.hutool.core.util.StrUtil;
-//import com.simuwang.base.mapper.EmailFieldMappingMapper;
-//import com.simuwang.base.pojo.dto.report.*;
-//import com.simuwang.daq.components.ReportParseUtils;
-//import com.simuwang.daq.components.report.parser.ReportParserConstant;
-//import org.springframework.stereotype.Component;
-//import technology.tabula.RectangularTextContainer;
-//import technology.tabula.Table;
-//
-//import java.awt.geom.Rectangle2D;
-//import java.util.Comparator;
-//import java.util.List;
-//import java.util.Map;
-//import java.util.Set;
-//import java.util.function.Function;
-//
-///**
-// * @author wangzaijun
-// * @date 2024/9/29 17:53
-// * @description pdf格式的季报解析逻辑
-// */
-//@Component(ReportParserConstant.PARSER_PDF_QUARTERLY)
-//public class PDQuarterlyReportParser<T extends QuarterlyReportData> extends AbstractPDReportParser<T> {
-//    protected List<Table> financialIndicatorsTables;
-//    protected List<Table> shareChangeTables;
-//    protected List<Table> assetAllocationTables;
-//    protected List<Table> investmentIndustryTables;
-//
-//    public PDQuarterlyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
-//        super(fieldMappingMapper);
-//    }
-//
-//    @Override
-//    public String getParser() {
-//        return ReportParserConstant.PARSER_PDF_QUARTERLY;
-//    }
-//
-//    @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();
-//            if (colCount == 0 && rowCount == 0) {
-//                continue;
-//            }
-//            if (rowCount == 13 && colCount == 2) {
-//                this.fundInfoTable = table;
-//            } else if (colCount == 2) {
-//                // 用表格的第一列的数据判断是否份额变动记录
-//                List<String> texts = this.getTableColTexts(table, 0);
-//                // 主要财务指标或份额变动
-//                if (CollUtil.containsAny(texts, ReportParseUtils.SHARE_CHANGE_COLUMN_NAMES)) {
-//                    this.shareChangeTables.add(table);
-//                } else if (CollUtil.containsAny(texts, ReportParseUtils.FINANCIAL_INDICATORS_COLUMN_NAMES)) {
-//                    this.financialIndicatorsTables.add(table);
-//                }
-//            } else if (colCount == 4) {
-//                // 行业配置
-//                this.investmentIndustryTables.add(table);
-//            } else if (colCount == 3) {
-//                // 用表格的第一列单元格判断是否资产配置表
-//                List<String> texts = this.getTableColTexts(table, 0);
-//                if (CollUtil.containsAny(texts, ReportParseUtils.INDUSTRY_COLUMN_NAMES)) {
-//                    this.investmentIndustryTables.add(table);
-//                } else {
-//                    texts = this.getTableColTexts(table, 1);
-//                    Set<String> keys = ReportParseUtils.ASSET_ALLOCATION_TYPE_MAPPER.keySet();
-//                    if (CollUtil.containsAny(texts, keys)) {
-//                        this.assetAllocationTables.add(table);
-//                    }
-//                }
-//            }
-//        }
-//    }
-//
-//    @Override
-//    protected Map<String, Object> parseFundInfo(Table fundInfoTable) {
-//        // 季报和年报的基金基本信息是两列的表格
-//        Map<String, Object> baseInfoMap = MapUtil.newHashMap(32);
-//        for (int i = 0; i < fundInfoTable.getRows().size(); i++) {
-//            @SuppressWarnings("all")
-//            List<RectangularTextContainer> cols = fundInfoTable.getRows().get(i);
-//            for (int j = 0; j < 1; j++) {
-//                baseInfoMap.put(cols.get(j).getText(), cols.get(j + 1).getText());
-//            }
-//        }
-//        return baseInfoMap;
-//    }
-//
-//    @Override
-//    protected T parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo) {
-//        Integer fileId = reportInfo.getFileId();
-//        // 表格转换数据获取函数
-//        Function<Table, Map<String, Object>> function = t -> {
-//            Map<String, Object> extInfoMap = MapUtil.newHashMap(16);
-//            for (int i = 0; i < t.getRowCount(); i++) {
-//                String key = t.getCell(i, 0).getText();
-//                String value = t.getCell(i, 1).getText();
-//                extInfoMap.put(key, value);
-//            }
-//            return extInfoMap;
-//        };
-//        // 份额变动
-//        List<ReportShareChangeDTO> shareChanges = this.buildLevelDto(fileId, this.shareChangeTables,
-//                ReportShareChangeDTO.class, function);
-//        // 主要财务指标
-//        List<ReportFinancialIndicatorsDTO> financialIndicators = this.buildFinancialIndicatorsInfo(fileId, function);
-//        // 资产配置
-//        List<ReportAssetAllocationDTO> assetAllocations = this.buildAssetAllocationInfo(fileId);
-//        // 行业配置
-//        List<ReportInvestmentIndustryDTO> investmentIndustries = this.buildInvestmentIndustryInfo(fileId);
-//        // 返回数据构建
-//        return this.buildReportData(reportInfo, fundInfo, shareChanges, financialIndicators, assetAllocations, investmentIndustries);
-//    }
-//
-//    /**
-//     * 主要财务指标数据构建(包括分级基金,并且一个表格可能跨页)
-//     *
-//     * @param fileId   文件id
-//     * @param function 字段映射关系
-//     * @return /
-//     */
-//    protected List<ReportFinancialIndicatorsDTO> buildFinancialIndicatorsInfo(Integer fileId, Function<Table, Map<String, Object>> function) {
-//        return this.buildLevelDto(fileId, this.financialIndicatorsTables, ReportFinancialIndicatorsDTO.class, function);
-//    }
-//
-//    /**
-//     * 子类重写,放在cast异常
-//     *
-//     * @param reportInfo           报告基本信息
-//     * @param fundInfo             基金基本信息
-//     * @param shareChanges         份额变动
-//     * @param financialIndicators  基本财务指标
-//     * @param assetAllocations     资产配置
-//     * @param investmentIndustries 行业配置
-//     * @return /
-//     */
-//    protected T buildReportData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo,
-//                                List<ReportShareChangeDTO> shareChanges,
-//                                List<ReportFinancialIndicatorsDTO> financialIndicators,
-//                                List<ReportAssetAllocationDTO> assetAllocations,
-//                                List<ReportInvestmentIndustryDTO> investmentIndustries) {
-//        QuarterlyReportData reportData = new QuarterlyReportData(reportInfo, fundInfo);
-//        reportData.setShareChange(shareChanges);
-//        reportData.setFinancialIndicators(financialIndicators);
-//        reportData.setAssetAllocation(assetAllocations);
-//        reportData.setInvestmentIndustry(investmentIndustries);
-//        @SuppressWarnings("unchecked")
-//        T t = (T) reportData;
-//        return t;
-//    }
-//
-//    @Override
-//    protected void cleaningReportData(T reportData) {
-//        // todo 数据清洗
-//    }
-//
-//    /**
-//     * 构建基金行业配置解析数据
-//     *
-//     * @return /
-//     */
-//    private List<ReportInvestmentIndustryDTO> buildInvestmentIndustryInfo(Integer fileId) {
-//        List<ReportInvestmentIndustryDTO> dtos = ListUtil.list(false);
-//        for (Table table : this.investmentIndustryTables) {
-//            int colCount = table.getColCount();
-//            // 投资地区: 1-境内, 2-港股通
-//            int investType = colCount == 4 ? 1 : 2;
-//            int j = colCount == 4 ? 1 : 0;
-//            // 按行遍历
-//            for (int i = 0; i < table.getRowCount(); i++) {
-//                String text = ReportParseUtils.cleaningValue(table.getCell(i, 0).getText());
-//                if (StrUtil.containsAny(text, "序号", "行业类别")) {
-//                    continue;
-//                }
-//                String industryName = ReportParseUtils.cleaningValue(table.getCell(i, j).getText());
-//                if (StrUtil.isBlank(industryName) || !ReportParseUtils.INDUSTRY_COLUMN_NAMES.contains(industryName)) {
-//                    continue;
-//                }
-//                ReportInvestmentIndustryDTO dto = new ReportInvestmentIndustryDTO(fileId);
-//                dto.setInvestType(investType);
-//                dto.setIndustryName(industryName);
-//                dto.setMarketValue(ReportParseUtils.cleaningValue(table.getCell(i, j + 1).getText()));
-//                dto.setRatio(ReportParseUtils.cleaningValue(table.getCell(i, j + 2).getText()));
-//                dtos.add(dto);
-//            }
-//        }
-//        return dtos;
-//    }
-//
-//    /**
-//     * 构建基金资产配置解析数据
-//     *
-//     * @param fileId 文件id
-//     * @return /
-//     */
-//    private List<ReportAssetAllocationDTO> buildAssetAllocationInfo(Integer fileId) {
-//        List<ReportAssetAllocationDTO> dtos = ListUtil.list(false);
-//        for (Table table : this.assetAllocationTables) {
-//            // 按行遍历
-//            for (@SuppressWarnings("all") List<RectangularTextContainer> row : table.getRows()) {
-//                // x坐标升序(防止部分行乱序问题)
-//                row.sort(Comparator.comparing(Rectangle2D.Float::getX));
-//                // 金额、市值,有时是 “备注#金额”的格式
-//                String marketValueAndRemark = ReportParseUtils.cleaningValue(row.get(2).getText());
-//                // 资产明细
-//                String detail = ReportParseUtils.cleaningValue(row.get(1).getText(), false);
-//                if (!ReportParseUtils.ASSET_ALLOCATION_TYPE_MAPPER.containsKey(detail)) {
-//                    continue;
-//                }
-//                // 大类
-//                String assetType = ReportParseUtils.ASSET_ALLOCATION_TYPE_MAPPER.get(detail);
-//                if (StrUtil.contains(marketValueAndRemark, "#")) {
-//                    // 有#表示有备注,而且可能有多个,多个用分号分隔的.
-//                    List<String> marketValueAndRemarks = StrUtil.split(marketValueAndRemark, ";");
-//                    for (String mr : marketValueAndRemarks) {
-//                        if (StrUtil.isBlank(mr)) {
-//                            continue;
-//                        }
-//                        List<String> mrs = StrUtil.split(mr, "#");
-//                        ReportAssetAllocationDTO dto = new ReportAssetAllocationDTO(fileId);
-//                        dto.setAssetType(assetType);
-//                        dto.setAssetDetails(detail);
-//                        dto.setMarketValue(mrs.get(1));
-//                        dto.setRemark(mrs.get(0));
-//                        dtos.add(dto);
-//                    }
-//                } else {
-//                    ReportAssetAllocationDTO dto = new ReportAssetAllocationDTO(fileId);
-//                    dto.setAssetType(assetType);
-//                    dto.setAssetDetails(detail);
-//                    dto.setMarketValue(marketValueAndRemark);
-//                    dtos.add(dto);
-//                }
-//            }
-//        }
-//        return dtos;
-//    }
-//
-//    /**
-//     * 获取表格指定列的所有文字内容
-//     *
-//     * @param table 表格
-//     * @param col   指定列
-//     * @return /
-//     */
-//    protected List<String> getTableColTexts(Table table, Integer col) {
-//        List<String> details = ListUtil.list(false);
-//        for (@SuppressWarnings("all") List<RectangularTextContainer> row : table.getRows()) {
-//            String detail = ReportParseUtils.cleaningValue(row.get(col).getText(), false);
-//            if (StrUtil.isNotBlank(detail)) {
-//                details.add(detail);
-//            }
-//        }
-//        return details;
-//    }
-//}

+ 39 - 0
mo-daq/src/main/java/com/smppw/modaq/application/components/report/parser/pdf/PDWeeklyReportParser.java

@@ -0,0 +1,39 @@
+package com.smppw.modaq.application.components.report.parser.pdf;
+
+import com.smppw.modaq.application.components.report.parser.ReportParserConstant;
+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;
+import technology.tabula.Table;
+
+import java.util.List;
+
+@Component(ReportParserConstant.PARSER_PDF_WEEKLY)
+public class PDWeeklyReportParser extends AbstractPDReportParser<WeeklyReportData>{
+    public PDWeeklyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
+        super(fieldMappingMapper);
+    }
+
+    @Override
+    public String getParser() {
+        return ReportParserConstant.PARSER_PDF_WEEKLY;
+    }
+
+    @Override
+    protected void initTableInfo(List<Table> tables) {
+
+    }
+
+    @Override
+    protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
+        return null;
+    }
+
+    @Override
+    protected WeeklyReportData parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo) {
+        return null;
+    }
+}

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

@@ -1,14 +0,0 @@
-//package com.smppw.modaq.infrastructure.components.report.writer;
-//
-//import com.simuwang.base.mapper.report.*;
-//import com.simuwang.base.pojo.dto.report.AnnuallyReportData;
-//import org.springframework.stereotype.Component;
-//
-//@Component(ReportWriterConstant.WRITER_ANNUALLY)
-//public class AnnuallyReportWriter extends QuarterlyReportWriter<AnnuallyReportData> {
-//    public AnnuallyReportWriter(ReportBaseInfoMapper baseInfoMapper, ReportFundInfoMapper fundInfoMapper,
-//                                ReportShareChangeMapper shareChangeMapper, ReportAssetAllocationMapper assetAllocationMapper,
-//                                ReportInvestmentIndustryMapper investmentIndustryMapper, ReportFinancialIndicatorMapper financialIndicatorMapper) {
-//        super(baseInfoMapper, fundInfoMapper, shareChangeMapper, assetAllocationMapper, investmentIndustryMapper, financialIndicatorMapper);
-//    }
-//}

+ 25 - 25
mo-daq/src/main/java/com/smppw/modaq/application/components/report/writer/MonthlyReportWriter.java

@@ -1,25 +1,25 @@
-//package com.smppw.modaq.infrastructure.components.report.writer;
-//
-//import com.smppw.modaq.domain.mapper.ReportBaseInfoMapper;
-//import com.smppw.modaq.domain.mapper.ReportFundInfoMapper;
-//import com.smppw.modaq.infrastructure.dto.report.MonthlyReportData;
-//import org.springframework.stereotype.Component;
-//
-//@Component(ReportWriterConstant.WRITER_MONTHLY)
-//public class MonthlyReportWriter extends AbstractReportWriter<MonthlyReportData> {
-//
-//    public MonthlyReportWriter(ReportBaseInfoMapper baseInfoMapper,
-//                               ReportFundInfoMapper fundInfoMapper) {
-//        super(baseInfoMapper, fundInfoMapper);
-//    }
-//
-//    @Override
-//    protected void writeExtData(MonthlyReportData reportData) {
-////        List<ReportNetReportDTO> netReport = reportData.getNetReport();
-////        if (CollUtil.isNotEmpty(netReport)) {
-////            List<ReportNetReportDO> entityList = netReport.stream()
-////                    .map(ReportNetReportDTO::toEntity).collect(Collectors.toList());
-////            this.netReportMapper.insert(entityList);
-////        }
-//    }
-//}
+package com.smppw.modaq.application.components.report.writer;
+
+import com.smppw.modaq.domain.dto.report.MonthlyReportData;
+import com.smppw.modaq.domain.mapper.ReportBaseInfoMapper;
+import com.smppw.modaq.domain.mapper.ReportFundInfoMapper;
+import org.springframework.stereotype.Component;
+
+@Component(ReportWriterConstant.WRITER_MONTHLY)
+public class MonthlyReportWriter extends AbstractReportWriter<MonthlyReportData> {
+
+    public MonthlyReportWriter(ReportBaseInfoMapper baseInfoMapper,
+                               ReportFundInfoMapper fundInfoMapper) {
+        super(baseInfoMapper, fundInfoMapper);
+    }
+
+    @Override
+    protected void writeExtData(MonthlyReportData reportData) {
+//        List<ReportNetReportDTO> netReport = reportData.getNetReport();
+//        if (CollUtil.isNotEmpty(netReport)) {
+//            List<ReportNetReportDO> entityList = netReport.stream()
+//                    .map(ReportNetReportDTO::toEntity).collect(Collectors.toList());
+//            this.netReportMapper.insert(entityList);
+//        }
+    }
+}

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

@@ -1,62 +0,0 @@
-//package com.smppw.modaq.infrastructure.components.report.writer;
-//
-//import cn.hutool.core.collection.CollUtil;
-//import com.simuwang.base.mapper.report.*;
-//import com.simuwang.base.pojo.dos.report.ReportAssetAllocationDO;
-//import com.simuwang.base.pojo.dos.report.ReportFinancialIndicatorsDO;
-//import com.simuwang.base.pojo.dos.report.ReportInvestmentIndustryDO;
-//import com.simuwang.base.pojo.dos.report.ReportShareChangeDO;
-//import com.simuwang.base.pojo.dto.report.*;
-//import org.springframework.stereotype.Component;
-//
-//import java.util.List;
-//import java.util.stream.Collectors;
-//
-//@Component(ReportWriterConstant.WRITER_QUARTERLY)
-//public class QuarterlyReportWriter<T extends QuarterlyReportData> extends AbstractReportWriter<T> {
-//    private final ReportShareChangeMapper shareChangeMapper;
-//    private final ReportAssetAllocationMapper assetAllocationMapper;
-//    private final ReportInvestmentIndustryMapper investmentIndustryMapper;
-//    private final ReportFinancialIndicatorMapper financialIndicatorMapper;
-//
-//    public QuarterlyReportWriter(ReportBaseInfoMapper baseInfoMapper, ReportFundInfoMapper fundInfoMapper,
-//                                 ReportShareChangeMapper shareChangeMapper, ReportAssetAllocationMapper assetAllocationMapper,
-//                                 ReportInvestmentIndustryMapper investmentIndustryMapper, ReportFinancialIndicatorMapper financialIndicatorMapper) {
-//        super(baseInfoMapper, fundInfoMapper);
-//        this.shareChangeMapper = shareChangeMapper;
-//        this.assetAllocationMapper = assetAllocationMapper;
-//        this.investmentIndustryMapper = investmentIndustryMapper;
-//        this.financialIndicatorMapper = financialIndicatorMapper;
-//    }
-//
-//    @Override
-//    protected void writeExtData(QuarterlyReportData reportData) {
-//        List<ReportShareChangeDTO> shareChange = reportData.getShareChange();
-//        if (CollUtil.isNotEmpty(shareChange)) {
-//            List<ReportShareChangeDO> entityList = shareChange.stream()
-//                    .map(ReportShareChangeDTO::toEntity).collect(Collectors.toList());
-//            this.shareChangeMapper.insert(entityList);
-//        }
-//
-//        List<ReportAssetAllocationDTO> assetAllocation = reportData.getAssetAllocation();
-//        if (CollUtil.isNotEmpty(assetAllocation)) {
-//            List<ReportAssetAllocationDO> entityList = assetAllocation.stream()
-//                    .map(ReportAssetAllocationDTO::toEntity).collect(Collectors.toList());
-//            this.assetAllocationMapper.insert(entityList);
-//        }
-//
-//        List<ReportFinancialIndicatorsDTO> financialIndicators = reportData.getFinancialIndicators();
-//        if (CollUtil.isNotEmpty(financialIndicators)) {
-//            List<ReportFinancialIndicatorsDO> entityList = financialIndicators.stream()
-//                    .map(ReportFinancialIndicatorsDTO::toEntity).collect(Collectors.toList());
-//            this.financialIndicatorMapper.insert(entityList);
-//        }
-//
-//        List<ReportInvestmentIndustryDTO> investmentIndustry = reportData.getInvestmentIndustry();
-//        if (CollUtil.isNotEmpty(investmentIndustry)) {
-//            List<ReportInvestmentIndustryDO> entityList = investmentIndustry.stream()
-//                    .map(ReportInvestmentIndustryDTO::toEntity).collect(Collectors.toList());
-//            this.investmentIndustryMapper.insert(entityList);
-//        }
-//    }
-//}

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

@@ -9,15 +9,17 @@ public final class ReportWriterConstant {
     public static final Map<ReportType, String> REPORT_TYPE_BEAN_MAP = MapUtil.newHashMap(8);
 
     static final String WRITER_LETTER = "report-writer:letter";
+    static final String WRITER_WEEKLY = "report-writer:weekly";
     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 final String WRITER_QUARTERLY = "report-writer:quarterly";
+//    static final String WRITER_ANNUALLY = "report-writer:annually";
 
     static {
         REPORT_TYPE_BEAN_MAP.put(ReportType.LETTER, WRITER_LETTER);
 
+        REPORT_TYPE_BEAN_MAP.put(ReportType.WEEKLY, WRITER_WEEKLY);
         REPORT_TYPE_BEAN_MAP.put(ReportType.MONTHLY, WRITER_MONTHLY);
-        REPORT_TYPE_BEAN_MAP.put(ReportType.QUARTERLY, WRITER_QUARTERLY);
-        REPORT_TYPE_BEAN_MAP.put(ReportType.ANNUALLY, WRITER_ANNUALLY);
+//        REPORT_TYPE_BEAN_MAP.put(ReportType.QUARTERLY, WRITER_QUARTERLY);
+//        REPORT_TYPE_BEAN_MAP.put(ReportType.ANNUALLY, WRITER_ANNUALLY);
     }
 }

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

@@ -0,0 +1,25 @@
+package com.smppw.modaq.application.components.report.writer;
+
+import com.smppw.modaq.domain.dto.report.WeeklyReportData;
+import com.smppw.modaq.domain.mapper.ReportBaseInfoMapper;
+import com.smppw.modaq.domain.mapper.ReportFundInfoMapper;
+import org.springframework.stereotype.Component;
+
+@Component(ReportWriterConstant.WRITER_WEEKLY)
+public class WeeklyReportWriter extends AbstractReportWriter<WeeklyReportData> {
+
+    public WeeklyReportWriter(ReportBaseInfoMapper baseInfoMapper,
+                              ReportFundInfoMapper fundInfoMapper) {
+        super(baseInfoMapper, fundInfoMapper);
+    }
+
+    @Override
+    protected void writeExtData(WeeklyReportData reportData) {
+//        List<ReportNetReportDTO> netReport = reportData.getNetReport();
+//        if (CollUtil.isNotEmpty(netReport)) {
+//            List<ReportNetReportDO> entityList = netReport.stream()
+//                    .map(ReportNetReportDTO::toEntity).collect(Collectors.toList());
+//            this.netReportMapper.insert(entityList);
+//        }
+    }
+}

+ 163 - 173
mo-daq/src/main/java/com/smppw/modaq/application/util/EmailUtil.java

@@ -1,29 +1,17 @@
 package com.smppw.modaq.application.util;
 
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.mail.JakartaUserPassAuthenticator;
-import com.smppw.modaq.common.conts.DateConst;
 import com.smppw.modaq.common.conts.EmailTypeConst;
-import com.smppw.modaq.domain.dto.EmailContentInfoDTO;
 import com.smppw.modaq.domain.dto.MailboxInfoDTO;
-import com.smppw.modaq.infrastructure.util.ExcelUtil;
-import com.smppw.modaq.infrastructure.util.FileUtil;
 import com.sun.mail.imap.IMAPStore;
-import jakarta.mail.Message;
-import jakarta.mail.MessagingException;
 import jakarta.mail.Session;
 import jakarta.mail.Store;
-import jakarta.mail.internet.MimeBodyPart;
-import jakarta.mail.internet.MimeMultipart;
-import jakarta.mail.internet.MimeUtility;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -41,100 +29,100 @@ public class EmailUtil {
     private static final String POP3 = "pop3";
     private static final String IMAP = "imap";
 
-    /**
-     * 采集邮件(多消息体)信息
-     *
-     * @param message      邮件
-     * @param emailAddress 邮箱地址
-     * @param path         存储路径
-     * @return 从邮箱采集到的信息
-     * @throws Exception 异常信息
-     */
-    public static List<EmailContentInfoDTO> collectMimeMultipart(Message message, String emailAddress, String path) throws Exception {
-        List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
-        String emailTitle = message.getSubject();
-        String emailDate = DateUtil.format(message.getSentDate(), DateConst.YYYYMMDDHHMMSS24);
-        String emailDateStr = DateUtil.format(message.getSentDate(), DateConst.YYYYMMDD);
-        String filePath = path + File.separator + emailAddress + File.separator + emailDateStr + File.separator;
-
-        MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
-        int length = mimeMultipart.getCount();
-        // 遍历邮件消息体 (我这里不要html正文)
-        for (int i = 0; i < length; i++) {
-            EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
-            MimeBodyPart part = (MimeBodyPart) mimeMultipart.getBodyPart(i);
-            Object partContent = part.getContent();
-            String contentClass = part.getContent().getClass().getSimpleName();
-            // 1.邮件正文
-            switch (contentClass) {
-                case "String" -> {
-                    // 文件名 = 邮件主题 + 邮件日期
-                    String fileName = emailTitle + "_" + emailDate + ".html";
-                    String content = partContent.toString();
-                    emailContentInfoDTO = collectTextPart(part, content, filePath, fileName);
-                }
-                case "BASE64DecoderStream" -> {
-                    if (StrUtil.isNotBlank(part.getFileName())) {
-                        String fileName = MimeUtility.decodeText(part.getFileName());
-                        if (isSupportedFileType(fileName)) {
-                            continue;
-                        }
-                        emailContentInfoDTO.setFileName(fileName);
-
-                        String realPath = filePath + emailDate + fileName;
-
-                        File saveFile = cn.hutool.core.io.FileUtil.file(realPath);
-                        if (!saveFile.exists()) {
-                            if (!saveFile.getParentFile().exists()) {
-                                saveFile.getParentFile().mkdirs();
-                            }
-                            FileUtil.saveFile(saveFile, part);
-                        } else {
-                            cn.hutool.core.io.FileUtil.del(saveFile);
-                            FileUtil.saveFile(saveFile, part);
-                        }
-                        emailContentInfoDTO.setFilePath(realPath);
-                    }
-                }
-                case "MimeMultipart" -> {
-                    MimeMultipart contentPart = (MimeMultipart) partContent;
-                    int length2 = contentPart.getCount();
-                    for (int i2 = 0; i2 < length2; i2++) {
-                        part = (MimeBodyPart) contentPart.getBodyPart(i2);
-                        partContent = part.getContent();
-                        contentClass = partContent.getClass().getSimpleName();
-                        if ("String".equals(contentClass)) {
-                            // 文件名 = 邮件主题 + 邮件日期
-                            String fileName = emailTitle + "_" + emailDate + ".html";
-                            String content = partContent.toString();
-                            emailContentInfoDTO = collectTextPart(part, content, filePath, fileName);
-                        }
-                    }
-                }
-            }
-            String filepath = emailContentInfoDTO.getFilePath();
-            if (emailContentInfoDTO.getEmailContent() == null && filepath == null) {
-                continue;
-            }
-            emailContentInfoDTO.setEmailAddress(emailAddress);
-            emailContentInfoDTO.setEmailTitle(emailTitle);
-            emailContentInfoDTO.setEmailDate(DateUtil.format(message.getSentDate(), DateConst.YYYY_MM_DD_HH_MM_SS));
-            emailContentInfoDTOList.add(emailContentInfoDTO);
-        }
-
-        return emailContentInfoDTOList;
-    }
+//    /**
+//     * 采集邮件(多消息体)信息
+//     *
+//     * @param message      邮件
+//     * @param emailAddress 邮箱地址
+//     * @param path         存储路径
+//     * @return 从邮箱采集到的信息
+//     * @throws Exception 异常信息
+//     */
+//    public static List<EmailContentInfoDTO> collectMimeMultipart(Message message, String emailAddress, String path) throws Exception {
+//        List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
+//        String emailTitle = message.getSubject();
+//        String emailDate = DateUtil.format(message.getSentDate(), DateConst.YYYYMMDDHHMMSS24);
+//        String emailDateStr = DateUtil.format(message.getSentDate(), DateConst.YYYYMMDD);
+//        String filePath = path + File.separator + emailAddress + File.separator + emailDateStr + File.separator;
+//
+//        MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
+//        int length = mimeMultipart.getCount();
+//        // 遍历邮件消息体 (我这里不要html正文)
+//        for (int i = 0; i < length; i++) {
+//            EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
+//            MimeBodyPart part = (MimeBodyPart) mimeMultipart.getBodyPart(i);
+//            Object partContent = part.getContent();
+//            String contentClass = part.getContent().getClass().getSimpleName();
+//            // 1.邮件正文
+//            switch (contentClass) {
+//                case "String" -> {
+//                    // 文件名 = 邮件主题 + 邮件日期
+//                    String fileName = emailTitle + "_" + emailDate + ".html";
+//                    String content = partContent.toString();
+//                    emailContentInfoDTO = collectTextPart(part, content, filePath, fileName);
+//                }
+//                case "BASE64DecoderStream" -> {
+//                    if (StrUtil.isNotBlank(part.getFileName())) {
+//                        String fileName = MimeUtility.decodeText(part.getFileName());
+//                        if (isSupportedFileType(fileName)) {
+//                            continue;
+//                        }
+//                        emailContentInfoDTO.setFileName(fileName);
+//
+//                        String realPath = filePath + emailDate + fileName;
+//
+//                        File saveFile = cn.hutool.core.io.FileUtil.file(realPath);
+//                        if (!saveFile.exists()) {
+//                            if (!saveFile.getParentFile().exists()) {
+//                                saveFile.getParentFile().mkdirs();
+//                            }
+//                            FileUtil.saveFile(saveFile, part);
+//                        } else {
+//                            cn.hutool.core.io.FileUtil.del(saveFile);
+//                            FileUtil.saveFile(saveFile, part);
+//                        }
+//                        emailContentInfoDTO.setFilePath(realPath);
+//                    }
+//                }
+//                case "MimeMultipart" -> {
+//                    MimeMultipart contentPart = (MimeMultipart) partContent;
+//                    int length2 = contentPart.getCount();
+//                    for (int i2 = 0; i2 < length2; i2++) {
+//                        part = (MimeBodyPart) contentPart.getBodyPart(i2);
+//                        partContent = part.getContent();
+//                        contentClass = partContent.getClass().getSimpleName();
+//                        if ("String".equals(contentClass)) {
+//                            // 文件名 = 邮件主题 + 邮件日期
+//                            String fileName = emailTitle + "_" + emailDate + ".html";
+//                            String content = partContent.toString();
+//                            emailContentInfoDTO = collectTextPart(part, content, filePath, fileName);
+//                        }
+//                    }
+//                }
+//            }
+//            String filepath = emailContentInfoDTO.getFilePath();
+//            if (emailContentInfoDTO.getEmailContent() == null && filepath == null) {
+//                continue;
+//            }
+//            emailContentInfoDTO.setEmailAddress(emailAddress);
+//            emailContentInfoDTO.setEmailTitle(emailTitle);
+//            emailContentInfoDTO.setEmailDate(DateUtil.format(message.getSentDate(), DateConst.YYYY_MM_DD_HH_MM_SS));
+//            emailContentInfoDTOList.add(emailContentInfoDTO);
+//        }
+//
+//        return emailContentInfoDTOList;
+//    }
 
 //    private static List<EmailContentInfoDTO> zipFile(String filepath) {
 //        return null;
 //    }
 
-    private static boolean isSupportedFileType(String fileName) {
-        if (StrUtil.isBlank(fileName)) {
-            return true;
-        }
-        return !ExcelUtil.isZip(fileName) && !ExcelUtil.isExcel(fileName) && !ExcelUtil.isPdf(fileName) && !ExcelUtil.isHTML(fileName) && !ExcelUtil.isRAR(fileName);
-    }
+//    private static boolean isSupportedFileType(String fileName) {
+//        if (StrUtil.isBlank(fileName)) {
+//            return true;
+//        }
+//        return !ExcelUtil.isZip(fileName) && !ExcelUtil.isExcel(fileName) && !ExcelUtil.isPdf(fileName) && !ExcelUtil.isHTML(fileName) && !ExcelUtil.isRAR(fileName);
+//    }
 
 //    /**
 //     * 根据日期过滤邮件
@@ -163,78 +151,78 @@ public class EmailUtil {
 //        return messageList;
 //    }
 
-    /**
-     * 采集邮件正文
-     *
-     * @param part        邮件消息体
-     * @param partContent 邮件消息内筒
-     * @param filePath    文件路径
-     * @param fileName    文件名
-     * @return 采集到邮件正文(html格式包含table标签)
-     */
-    public static EmailContentInfoDTO collectTextPart(MimeBodyPart part, String partContent, String filePath, String fileName) {
-        EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
-        try {
-            if ((part.getContentType().contains("text/html") || part.getContentType().contains("TEXT/HTML"))) {
-                emailContentInfoDTO.setEmailContent(partContent);
-                String savePath = filePath + fileName;
-                File saveFile = new File(savePath);
-                if (!saveFile.exists()) {
-                    if (!saveFile.getParentFile().exists()) {
-                        saveFile.getParentFile().mkdirs();
-                        saveFile.getParentFile().setExecutable(true);
-                    }
-                }
-                //获取邮件编码
-                String contentType = part.getContentType();
-                String html = partContent.toString();
-                try {
-                    if (contentType.contains("charset=")) {
-                        contentType = contentType.substring(contentType.indexOf("charset=") + 8).replaceAll("\"", "");
-                        html = html.replace("charset=" + contentType.toLowerCase(), "charset=UTF-8");
-                        html = html.replace("charset=" + contentType.toUpperCase(), "charset=UTF-8");
-                    }
-                    if (savePath.contains(":")) {
-                        savePath = savePath.replaceAll(":", "");
-                    }
-                    cn.hutool.core.io.FileUtil.writeUtf8String(html, new File(savePath));
-                } catch (Exception e) {
-                    logger.error(e.getMessage(), e);
-                }
-                emailContentInfoDTO.setFileName(fileName);
-                emailContentInfoDTO.setFilePath(savePath);
-            } else {
-                try {
-                    if (part.getFileName() == null) {
-                        return emailContentInfoDTO;
-                    }
-                    String fileName1 = MimeUtility.decodeText(part.getFileName());
-                    if (isSupportedFileType(fileName1)) {
-                        return emailContentInfoDTO;
-                    }
-                    emailContentInfoDTO.setFileName(fileName1);
-                    String realPath = filePath + fileName1;
-                    File saveFile = new File(realPath);
-                    if (!saveFile.exists()) {
-                        if (!saveFile.getParentFile().exists()) {
-                            saveFile.getParentFile().mkdirs();
-                        }
-                        FileUtil.saveFile(saveFile, part);
-                    } else {
-                        cn.hutool.core.io.FileUtil.del(saveFile);
-                        FileUtil.saveFile(saveFile, part);
-                    }
-                    emailContentInfoDTO.setFilePath(realPath);
-                } catch (Exception e) {
-                    return emailContentInfoDTO;
-                }
-            }
-        } catch (MessagingException e) {
-            logger.info("邮件正文采集失败 -> 文件名:{}, 报错堆栈:{}", fileName, ExceptionUtil.stacktraceToString(e));
-            return emailContentInfoDTO;
-        }
-        return emailContentInfoDTO;
-    }
+//    /**
+//     * 采集邮件正文
+//     *
+//     * @param part        邮件消息体
+//     * @param partContent 邮件消息内筒
+//     * @param filePath    文件路径
+//     * @param fileName    文件名
+//     * @return 采集到邮件正文(html格式包含table标签)
+//     */
+//    public static EmailContentInfoDTO collectTextPart(MimeBodyPart part, String partContent, String filePath, String fileName) {
+//        EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
+//        try {
+//            if ((part.getContentType().contains("text/html") || part.getContentType().contains("TEXT/HTML"))) {
+//                emailContentInfoDTO.setEmailContent(partContent);
+//                String savePath = filePath + fileName;
+//                File saveFile = new File(savePath);
+//                if (!saveFile.exists()) {
+//                    if (!saveFile.getParentFile().exists()) {
+//                        saveFile.getParentFile().mkdirs();
+//                        saveFile.getParentFile().setExecutable(true);
+//                    }
+//                }
+//                //获取邮件编码
+//                String contentType = part.getContentType();
+//                String html = partContent.toString();
+//                try {
+//                    if (contentType.contains("charset=")) {
+//                        contentType = contentType.substring(contentType.indexOf("charset=") + 8).replaceAll("\"", "");
+//                        html = html.replace("charset=" + contentType.toLowerCase(), "charset=UTF-8");
+//                        html = html.replace("charset=" + contentType.toUpperCase(), "charset=UTF-8");
+//                    }
+//                    if (savePath.contains(":")) {
+//                        savePath = savePath.replaceAll(":", "");
+//                    }
+//                    cn.hutool.core.io.FileUtil.writeUtf8String(html, new File(savePath));
+//                } catch (Exception e) {
+//                    logger.error(e.getMessage(), e);
+//                }
+//                emailContentInfoDTO.setFileName(fileName);
+//                emailContentInfoDTO.setFilePath(savePath);
+//            } else {
+//                try {
+//                    if (part.getFileName() == null) {
+//                        return emailContentInfoDTO;
+//                    }
+//                    String fileName1 = MimeUtility.decodeText(part.getFileName());
+//                    if (isSupportedFileType(fileName1)) {
+//                        return emailContentInfoDTO;
+//                    }
+//                    emailContentInfoDTO.setFileName(fileName1);
+//                    String realPath = filePath + fileName1;
+//                    File saveFile = new File(realPath);
+//                    if (!saveFile.exists()) {
+//                        if (!saveFile.getParentFile().exists()) {
+//                            saveFile.getParentFile().mkdirs();
+//                        }
+//                        FileUtil.saveFile(saveFile, part);
+//                    } else {
+//                        cn.hutool.core.io.FileUtil.del(saveFile);
+//                        FileUtil.saveFile(saveFile, part);
+//                    }
+//                    emailContentInfoDTO.setFilePath(realPath);
+//                } catch (Exception e) {
+//                    return emailContentInfoDTO;
+//                }
+//            }
+//        } catch (MessagingException e) {
+//            logger.info("邮件正文采集失败 -> 文件名:{}, 报错堆栈:{}", fileName, ExceptionUtil.stacktraceToString(e));
+//            return emailContentInfoDTO;
+//        }
+//        return emailContentInfoDTO;
+//    }
 
     /**
      * 判断邮件是否符合解析条件
@@ -306,6 +294,8 @@ public class EmailUtil {
             props.put("mail.imap.starttls.enable", "true");
             props.put("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
             props.put("mail.imap.socketFactory.fallback", "false");
+//            // 设置数据块大小为 64k(可调整)
+//            props.put("mail.imap.fetchsize", "65536");
         }
         return props;
     }

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

@@ -9,9 +9,9 @@ import java.util.Arrays;
  */
 public enum ReportParserFileType {
     PDF("pdf"),
-    WORD("docx,doc"),
-    EXCEL("xlsx,xls"),
-    PYTHON("python");
+//    WORD("docx,doc"),
+    EXCEL("xlsx,xls");
+//    PYTHON("python");
 
     private final String suffix;
 

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

@@ -5,9 +5,10 @@ import lombok.Getter;
 @Getter
 public enum ReportType {
     LETTER(-1, "交易流水确认函", new String[]{"确认单", "确认函", "确认"}),
-    MONTHLY(0, "月", new String[]{"月", "月度", "月报"}),
-    QUARTERLY(1, "季", new String[]{"季", "季度", "季报"}),
-    ANNUALLY(2, "年", new String[]{"年度", "年报"});
+    WEEKLY(4, "周", new String[]{"周", "周度", "周报"}),
+    MONTHLY(0, "月", new String[]{"月", "月度", "月报"});
+//    QUARTERLY(1, "季", new String[]{"季", "季度", "季报"}),
+//    ANNUALLY(2, "年", new String[]{"年度", "年报"});
 
     private final int type;
     private final String label;

+ 50 - 50
mo-daq/src/main/java/com/smppw/modaq/domain/dto/QuartzBean.java

@@ -1,50 +1,50 @@
-package com.smppw.modaq.domain.dto;
-
-/**
- * FileName: QuartzBean
- * Author:   chenjianhua
- * Date:     2024/9/17 10:26
- * Description: ${DESCRIPTION}
- */
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-public class QuartzBean implements Serializable {
-    /**
-     * 任务id
-     */
-    private String id;
-
-    /**
-     * 任务名称
-     */
-    private String jobName;
-
-    /**
-     * 任务执行类
-     */
-    private String jobClass;
-
-    /**
-     * 组名
-     */
-    private String groupName;
-
-    /**
-     * 任务 参数信息
-     */
-    private String jobParam;
-
-    /**
-     * 任务状态 启动还是暂停
-     */
-    private Integer status;
-
-    /**
-     * 任务运行时间表达式
-     */
-    private String cronExpression;
-}
+//package com.smppw.modaq.domain.dto;
+//
+///**
+// * FileName: QuartzBean
+// * Author:   chenjianhua
+// * Date:     2024/9/17 10:26
+// * Description: ${DESCRIPTION}
+// */
+//
+//import lombok.Data;
+//
+//import java.io.Serializable;
+//
+//@Data
+//public class QuartzBean implements Serializable {
+//    /**
+//     * 任务id
+//     */
+//    private String id;
+//
+//    /**
+//     * 任务名称
+//     */
+//    private String jobName;
+//
+//    /**
+//     * 任务执行类
+//     */
+//    private String jobClass;
+//
+//    /**
+//     * 组名
+//     */
+//    private String groupName;
+//
+//    /**
+//     * 任务 参数信息
+//     */
+//    private String jobParam;
+//
+//    /**
+//     * 任务状态 启动还是暂停
+//     */
+//    private Integer status;
+//
+//    /**
+//     * 任务运行时间表达式
+//     */
+//    private String cronExpression;
+//}

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

@@ -1,18 +0,0 @@
-//package com.smppw.modaq.infrastructure.dto.report;
-//
-//import com.simuwang.base.common.enums.ReportType;
-//import lombok.Getter;
-//import lombok.Setter;
-//
-//@Setter
-//@Getter
-//public class AnnuallyReportData extends QuarterlyReportData {
-//    public AnnuallyReportData(ReportBaseInfoDTO baseInfo, ReportFundInfoDTO fundInfo) {
-//        super(baseInfo, fundInfo);
-//    }
-//
-//    @Override
-//    public ReportType getReportType() {
-//        return ReportType.ANNUALLY;
-//    }
-//}

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

@@ -4,6 +4,7 @@ import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import com.smppw.modaq.common.conts.Constants;
+import com.smppw.modaq.common.conts.DateConst;
 import com.smppw.modaq.domain.entity.report.BaseReportDO;
 import lombok.Getter;
 import lombok.Setter;
@@ -59,31 +60,50 @@ public abstract class BaseReportDTO<T extends BaseReportDO> implements Serializa
             return null;
         }
         try {
-            // 日期格式化,支持三种格式:yyyy年MM月dd日、yyyy-MM-dd和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", "yyyyMMdd");
+                    DatePattern.CHINESE_DATE_PATTERN,
+                    DatePattern.NORM_DATE_PATTERN,
+                    "yyyy/MM/dd",
+                    DateConst.YYYYMMDD);
         } catch (Exception ignored) {
         }
         return null;
     }
 
+    protected BigDecimal toBigDecimal(String input) {
+        return this.toBigDecimal(input, null);
+    }
+
     /**
-     * 字符串转数字,如果数据没有或者转换失败则用0填充
+     * 字符串转数字,如果数据没有或者转换失败则用defaultValue字段填充
      *
-     * @param input 待转换的字符串
+     * @param input        待转换的字符串
+     * @param defaultValue 为null或者数字转换失败的默认值
      * @return /
      */
-    protected BigDecimal toBigDecimal(String input) {
+    protected BigDecimal toBigDecimal(String input, BigDecimal defaultValue) {
         if (StrUtil.isBlank(input)) {
-            return BigDecimal.ZERO;
+            return defaultValue;
         }
         try {
-            // 替换掉非正常的正负小数字符
-            String cleanedInput = input.trim().replaceAll("[^\\s" + "[-+]?\\d*.+" + "]", "");
-            // 创建BigDecimal对象
+            // 1. 清理输入,保留有效字符(数字、正负号、小数点、科学计数法符号、千分位逗号)
+            String cleanedInput = input.trim().replaceAll("[^\\d.,\\-+Ee]", "");
+            // 2. 移除所有空格(包括中间的空格)
+            cleanedInput = cleanedInput.replaceAll("\\s+", "");
+
+            // 3. 处理千分位逗号(替换为"",因为千分位逗号在最终数字中无效)
+            cleanedInput = cleanedInput.replaceAll(",", "");
+
+            // 4. 处理科学计数法中的符号(如 E 或 e 后的符号)
+            // 例如:"1.23E+4" → "1.23E4"(可选,但 Java 的 BigDecimal 可直接处理)
+
+            // 5. 验证最终格式是否合法(可选,但可能影响性能)
+            // 这里直接交给 BigDecimal 处理,捕获异常即可
+
             return new BigDecimal(cleanedInput);
-        } catch (NumberFormatException ignored) {
+        } catch (NumberFormatException e) {
+            return defaultValue;
         }
-        return BigDecimal.ZERO;
     }
 }

+ 25 - 25
mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/MonthlyReportData.java

@@ -1,25 +1,25 @@
-//package com.smppw.modaq.infrastructure.dto.report;
-//
-//import com.smppw.modaq.common.enums.ReportType;
-//import lombok.Getter;
-//import lombok.Setter;
-//
-//@Setter
-//@Getter
-//public class MonthlyReportData extends ReportData {
-////    private List<ReportNetReportDTO> netReport;
-//
-//    public MonthlyReportData(ReportBaseInfoDTO baseInfo, ReportFundInfoDTO fundInfo) {
-//        super(baseInfo, fundInfo);
-//    }
-//
-//    @Override
-//    public ReportType getReportType() {
-//        return ReportType.MONTHLY;
-//    }
-//
-//    @Override
-//    public String toString() {
-//        return super.toString();
-//    }
-//}
+package com.smppw.modaq.domain.dto.report;
+
+import com.smppw.modaq.common.enums.ReportType;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class MonthlyReportData extends ReportData {
+//    private List<ReportNetReportDTO> netReport;
+
+    public MonthlyReportData(ReportBaseInfoDTO baseInfo, ReportFundInfoDTO fundInfo) {
+        super(baseInfo, fundInfo);
+    }
+
+    @Override
+    public ReportType getReportType() {
+        return ReportType.MONTHLY;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString();
+    }
+}

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

@@ -1,41 +0,0 @@
-//package com.smppw.modaq.infrastructure.dto.report;
-//
-//import com.simuwang.base.common.enums.ReportType;
-//import lombok.Getter;
-//import lombok.Setter;
-//
-//import java.util.List;
-//
-///**
-// * @author wangzaijun
-// * @date 2024/9/26 17:24
-// * @description 季报
-// */
-//@Setter
-//@Getter
-//public class QuarterlyReportData extends ReportData {
-//    private List<ReportAssetAllocationDTO> assetAllocation;
-//    private List<ReportFinancialIndicatorsDTO> financialIndicators;
-//    private List<ReportInvestmentIndustryDTO> investmentIndustry;
-//    private List<ReportShareChangeDTO> shareChange;
-//
-//    public QuarterlyReportData(ReportBaseInfoDTO baseInfo, ReportFundInfoDTO fundInfo) {
-//        super(baseInfo, fundInfo);
-//    }
-//
-//    @Override
-//    public ReportType getReportType() {
-//        return ReportType.QUARTERLY;
-//    }
-//
-//    @Override
-//    public String toString() {
-//        return "{" +
-//                super.toString() +
-//                ", assetAllocation=" + assetAllocation +
-//                ", financialIndicators=" + financialIndicators +
-//                ", investmentIndustry=" + investmentIndustry +
-//                ", shareChange=" + shareChange +
-//                '}';
-//    }
-//}

+ 5 - 3
mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/ReportFundTransactionDTO.java

@@ -4,6 +4,8 @@ import com.smppw.modaq.domain.entity.report.ReportFundTransactionDO;
 import lombok.Getter;
 import lombok.Setter;
 
+import java.math.BigDecimal;
+
 @Setter
 @Getter
 public class ReportFundTransactionDTO extends BaseReportDTO<ReportFundTransactionDO> {
@@ -250,9 +252,9 @@ public class ReportFundTransactionDTO extends BaseReportDTO<ReportFundTransactio
         entity.setApplyDate(this.toDate(applyDate));
         entity.setApplyAmount(this.toBigDecimal(applyAmount));
         entity.setApplyShare(this.toBigDecimal(applyShare));
-        entity.setAmount(this.toBigDecimal(amount));
-        entity.setShare(this.toBigDecimal(share));
-        entity.setNetAmount(this.toBigDecimal(netAmount));
+        entity.setAmount(this.toBigDecimal(amount, BigDecimal.ZERO));
+        entity.setShare(this.toBigDecimal(share, BigDecimal.ZERO));
+        entity.setNetAmount(this.toBigDecimal(netAmount, BigDecimal.ZERO));
         entity.setNav(this.toBigDecimal(nav));
         entity.setConfirmationRatio(this.toBigDecimal(confirmationRatio));
         entity.setTaConfirmationNumber(taConfirmationNumber);

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

@@ -0,0 +1,23 @@
+package com.smppw.modaq.domain.dto.report;
+
+import com.smppw.modaq.common.enums.ReportType;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+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();
+    }
+}

+ 71 - 54
mo-daq/src/main/java/com/smppw/modaq/domain/service/EmailParseService.java

@@ -32,8 +32,7 @@ import com.smppw.modaq.domain.mapper.EmailParseInfoMapper;
 import com.smppw.modaq.infrastructure.util.ExcelUtil;
 import com.smppw.modaq.infrastructure.util.FileUtil;
 import jakarta.mail.*;
-import jakarta.mail.internet.MimeMessage;
-import jakarta.mail.internet.MimeMultipart;
+import jakarta.mail.internet.MimeUtility;
 import jakarta.mail.search.ComparisonTerm;
 import jakarta.mail.search.ReceivedDateTerm;
 import jakarta.mail.search.SearchTerm;
@@ -46,7 +45,6 @@ import org.springframework.util.StopWatch;
 
 import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.*;
@@ -81,7 +79,6 @@ public class EmailParseService {
                              EmailFileInfoMapper emailFileInfoMapper,
                              ReportParserFactory reportParserFactory,
                              ReportWriterFactory reportWriterFactory) {
-//        this.emailFieldMapper = emailFieldMapper;
         this.emailParseInfoMapper = emailParseInfoMapper;
         this.emailFileInfoMapper = emailFileInfoMapper;
         this.reportParserFactory = reportParserFactory;
@@ -417,12 +414,6 @@ public class EmailParseService {
 
     public Map<Integer, List<String>> getEmailType() {
         Map<Integer, List<String>> emailTypeMap = MapUtil.newHashMap(3, true);
-//        EmailTypeRuleDO emailTypeRuleDO = emailTypeRuleMapper.getEmailTypeRule();
-//        String nav = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getNav()) ? emailTypeRuleDO.getNav() : emailRuleConfig.getNav();
-//        String valuation = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getValuation()) ? emailTypeRuleDO.getValuation() : emailRuleConfig.getValuation();
-//        String report = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getReport()) ? emailTypeRuleDO.getReport() : emailRuleConfig.getReport();
-//        emailTypeMap.put(EmailTypeConst.VALUATION_EMAIL_TYPE, Arrays.stream(valuation.split(",")).toList());
-//        emailTypeMap.put(EmailTypeConst.NAV_EMAIL_TYPE, Arrays.stream(nav.split(",")).toList());
 //        emailTypeMap.put(EmailTypeConst.REPORT_EMAIL_TYPE, ListUtil.toList("月报", "周报", "月度报告"));
         emailTypeMap.put(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE,
                 ListUtil.toList("确认单", "确认函", "交易确认数据", "赎回确认", "申购确认", "分红确认", "确认表"));
@@ -454,8 +445,7 @@ public class EmailParseService {
             return MapUtil.newHashMap();
         }
         Map<String, List<EmailContentInfoDTO>> emailMessageMap = MapUtil.newHashMap();
-        for (Message message1 : messages) {
-            MimeMessage message = (MimeMessage) message1;
+        for (Message message : messages) {
             List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
             String uuidKey = UUID.randomUUID().toString().replaceAll("-", "");
             Integer emailType;
@@ -475,36 +465,16 @@ public class EmailParseService {
                 }
                 log.info("邮件采集成功 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
                 Object content = message.getContent();
-                // 1.邮件为MIME多部分消息体:可能既有邮件又有正文
-                if (content instanceof MimeMultipart) {
-                    emailContentInfoDTOList = EmailUtil.collectMimeMultipart(message, mailboxInfoDTO.getAccount(), path);
-                }
-                // 2.邮件只有正文
-                if (content instanceof String) {
-                    EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
-                    emailContentInfoDTO.setEmailContent(content.toString());
-                    emailContentInfoDTO.setEmailDate(emailDateStr);
-                    emailContentInfoDTO.setEmailTitle(message.getSubject());
-                    String fileName = message.getSubject() + DateUtil.format(emailDate, DateConst.YYYYMMDDHHMMSS24);
-                    String filePath = path + mailboxInfoDTO.getAccount() + File.separator + DateUtil.format(emailDate, DateConst.YYYY_MM_DD) + File.separator + fileName + ".html";
-                    File saveFile = new File(filePath);
-                    saveFile.setReadable(true);
-                    if (!saveFile.exists()) {
-                        if (!saveFile.getParentFile().exists()) {
-                            Files.createDirectories(saveFile.getParentFile().toPath());
-                            saveFile.getParentFile().setExecutable(true);
-                        }
-                    }
-                    FileUtil.writeFile(filePath, content.toString());
-                    emailContentInfoDTO.setFilePath(filePath);
-                    emailContentInfoDTO.setFileName(fileName);
-                    emailContentInfoDTOList.add(emailContentInfoDTO);
+
+                if (content instanceof Multipart multipart) {
+                    this.reMultipart(mailboxInfoDTO.getAccount(), message.getSubject(), emailDate, multipart, emailContentInfoDTOList);
+                } else if (content instanceof Part part) {
+                    this.rePart(mailboxInfoDTO.getAccount(), message.getSubject(), emailDate, part, emailContentInfoDTOList);
+                } else {
+                    log.warn("不支持的邮件数据 {}", message.getSubject());
                 }
+
                 if (CollUtil.isNotEmpty(emailContentInfoDTOList)) {
-//                    // 估值表或定期报告邮件不展示正文html文件
-//                    if (emailType.equals(EmailTypeConst.VALUATION_EMAIL_TYPE) || emailType.equals(EmailTypeConst.REPORT_EMAIL_TYPE)) {
-//                        emailContentInfoDTOList = emailContentInfoDTOList.stream().filter(e -> !ExcelUtil.isHTML(e.getFilePath())).toList();
-//                    }
                     emailContentInfoDTOList.forEach(e -> {
                         e.setEmailType(emailType);
                         e.setSenderEmail(senderEmail);
@@ -520,8 +490,55 @@ public class EmailParseService {
         return emailMessageMap;
     }
 
-    private String getSenderEmail(MimeMessage message) {
-        Address[] senderAddress = null;
+    private void rePart(String account, String subject, Date sendDate, Part part, List<EmailContentInfoDTO> emailContentInfoDTOList) throws Exception {
+        String disposition = part.getDisposition();
+        if (disposition != null && (disposition.equals(Part.ATTACHMENT) || disposition.equals(Part.INLINE))) {
+            String emailDate = DateUtil.format(sendDate, DateConst.YYYYMMDDHHMMSS24);
+            String emailDateStr = DateUtil.format(sendDate, DateConst.YYYYMMDD);
+            String filePath = path + File.separator + account + File.separator + emailDateStr + File.separator;
+
+            EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
+
+            String fileName = MimeUtility.decodeText(part.getFileName());
+            emailContentInfoDTO.setFileName(fileName);
+
+            String realPath = filePath + emailDate + fileName;
+
+            File saveFile = cn.hutool.core.io.FileUtil.file(realPath);
+            if (!saveFile.exists()) {
+                if (!saveFile.getParentFile().exists()) {
+                    boolean mkdirs = saveFile.getParentFile().mkdirs();
+                    if (!mkdirs) {
+                        log.warn("file path mkdir failed.");
+                    }
+                }
+                FileUtil.saveFile(saveFile, part);
+            } else {
+                cn.hutool.core.io.FileUtil.del(saveFile);
+                FileUtil.saveFile(saveFile, part);
+            }
+            emailContentInfoDTO.setFilePath(realPath);
+
+            emailContentInfoDTO.setEmailAddress(account);
+            emailContentInfoDTO.setEmailTitle(subject);
+            emailContentInfoDTO.setEmailDate(DateUtil.format(sendDate, DateConst.YYYY_MM_DD_HH_MM_SS));
+            emailContentInfoDTOList.add(emailContentInfoDTO);
+        }
+    }
+
+    private void reMultipart(String account, String subject, Date emailDate, Multipart multipart, List<EmailContentInfoDTO> emailContentInfoDTOList) throws Exception {
+        for (int i = 0; i < multipart.getCount(); i++) {
+            Part bodyPart = multipart.getBodyPart(i);
+            if (bodyPart.getContent() instanceof Multipart mp) {
+                this.reMultipart(account, subject, emailDate, mp, emailContentInfoDTOList);
+            } else {
+                this.rePart(account, subject, emailDate, bodyPart, emailContentInfoDTOList);
+            }
+        }
+    }
+
+    private String getSenderEmail(Message message) {
+        Address[] senderAddress;
         try {
             senderAddress = message.getFrom();
             if (senderAddress == null || senderAddress.length == 0) {
@@ -536,24 +553,24 @@ public class EmailParseService {
                     break;
                 }
             }
-            log.info("发件人地址:" + address + "========================senderAddress size:" + senderAddress.length);
+            log.info("发件人地址:{}========================senderAddress size:{}", address, senderAddress.length);
             // 正则表达式匹配邮件地址
             Pattern pattern = Pattern.compile("<(\\S+)>");
             Matcher matcher = pattern.matcher(address);
             if (matcher.find()) {
                 return matcher.group(1);
             }
-            //说明匹配不到,直接获取sender
-            Address sender = message.getSender();
-            if (sender == null) {
-                return address;
-            }
-            String senderEmail = sender.toString();
-            log.info("senderEmail:" + senderEmail + "====================");
-            if (senderEmail.contains("<") && senderEmail.contains(">") && senderEmail.indexOf("<") < senderEmail.indexOf(">")) {
-                senderEmail = senderEmail.substring(senderEmail.indexOf("<") + 1, senderEmail.length() - 1);
-            }
-            return senderEmail;
+//            //说明匹配不到,直接获取sender
+//            Address sender = message.getSender();
+//            if (sender == null) {
+//                return address;
+//            }
+//            String senderEmail = sender.toString();
+//            log.info("senderEmail:" + senderEmail + "====================");
+//            if (senderEmail.contains("<") && senderEmail.contains(">") && senderEmail.indexOf("<") < senderEmail.indexOf(">")) {
+//                senderEmail = senderEmail.substring(senderEmail.indexOf("<") + 1, senderEmail.length() - 1);
+//            }
+//            return senderEmail;
         } catch (MessagingException e) {
             log.error(e.getMessage(), e);
         }

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

@@ -186,7 +186,7 @@ public class ExcelUtil {
 
     private static String extractItem(ISimpleInArchiveItem item, String outputDir) throws SevenZipException {
         String filePath = outputDir + File.separator + item.getPath();
-        File outputFile = new File(filePath);
+        File outputFile = FileUtil.file(filePath);
 
         // 创建父目录
         File parentDir = outputFile.getParentFile();
@@ -211,7 +211,7 @@ public class ExcelUtil {
         } catch (IOException e) {
             throw new SevenZipException("文件操作失败", e);
         }
-        return filePath;
+        return outputFile.getAbsolutePath();
     }
 
     public static void main(String[] args) throws Exception {

+ 3 - 8
mo-daq/src/main/java/com/smppw/modaq/infrastructure/util/FileUtil.java

@@ -6,6 +6,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.*;
+import java.nio.file.Files;
 
 public class FileUtil {
 
@@ -56,14 +57,8 @@ public class FileUtil {
     }
 
     public static void saveFile(File saveFile, Part part) throws Exception {
-        BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(saveFile) );
-        byte[] buff = new byte[2048];
-        InputStream is = part.getInputStream();
-        int ret = 0;
-        while( (ret = is.read(buff)) > 0 ){
-            bos.write(buff, 0, ret);
+        try (InputStream is = part.getInputStream()) {
+            Files.copy(is, saveFile.toPath());
         }
-        bos.close();
-        is.close();
     }
 }

+ 4 - 4
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-03-05 10:40:00", DateConst.YYYY_MM_DD_HH_MM_SS);
-        Date endDate = DateUtil.parse("2025-03-05 11:42:05", DateConst.YYYY_MM_DD_HH_MM_SS);
+        MailboxInfoDTO emailInfoDTO = this.buildMailbox("wangzaijun157@163.com", "AMn3Trh4UTgVMaEx");
+        Date startDate = DateUtil.parse("2025-03-27 14:40:00", DateConst.YYYY_MM_DD_HH_MM_SS);
+        Date endDate = DateUtil.parse("2025-03-27 19:42:05", DateConst.YYYY_MM_DD_HH_MM_SS);
         try {
             emailParseService.parseEmail(emailInfoDTO, startDate, endDate);
         } catch (Exception e) {
@@ -55,7 +55,7 @@ public class MoDaqApplicationTests {
         emailInfoDTO.setUserId(1);
         emailInfoDTO.setAccount(account);
         emailInfoDTO.setPassword(pwd);
-        emailInfoDTO.setHost("imap.exmail.qq.com");
+        emailInfoDTO.setHost("imap.163.com");
         emailInfoDTO.setPort("993");
         emailInfoDTO.setProtocol("imap");
         return emailInfoDTO;