瀏覽代碼

feat:邮件解析-实现解析净值文件excel

mozuwen 7 月之前
父節點
當前提交
ce517decaf

+ 14 - 0
service-base/src/main/java/com/simuwang/base/common/conts/EmailFieldConst.java

@@ -0,0 +1,14 @@
+package com.simuwang.base.common.conts;
+
+public class EmailFieldConst {
+
+    public final static String PRICE_DATE = "priceDate";
+    public final static String FUND_NAME = "fundName";
+    public final static String REGISTER_NUMBER = "registerNumber";
+    public final static String NAV = "nav";
+    public final static String CUMULATIVE_NAV_WITHDRAWAL = "cumulativeNavWithdrawal";
+    public final static String VIRTUAL_NAV = "virtualNav";
+    public final static String ASSET_SHARE = "assetShare";
+    public final static String ASSET_NET = "assetNet";
+
+}

+ 28 - 0
service-base/src/main/java/com/simuwang/base/common/util/StringUtil.java

@@ -1,7 +1,15 @@
 package com.simuwang.base.common.util;
 
+import cn.hutool.core.util.StrUtil;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.List;
+
 public class StringUtil {
 
+    private static final List<String> DATE_FORMATS = Arrays.asList("yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd");
+
     public static String retainChineseCharacters(String value) {
         // 正则表达式匹配中文字符
         String regex = "[^\\u4e00-\\u9fa5]";
@@ -9,4 +17,24 @@ public class StringUtil {
         return value.replaceAll(regex, "");
     }
 
+    public static boolean isValidDate(String dateString) {
+        if (StrUtil.isBlank(dateString)) {
+            return false;
+        }
+        dateString = dateString.replaceAll("年", "-").replaceAll("月", "-");
+        for (String format : DATE_FORMATS) {
+            SimpleDateFormat sdf = new SimpleDateFormat(format);
+            // 设置严格模式
+            sdf.setLenient(false);
+            try {
+                sdf.parse(dateString);
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return false;
+    }
+
+
 }

+ 8 - 11
service-base/src/main/java/com/simuwang/base/pojo/dto/EmailFundNavDTO.java

@@ -2,9 +2,6 @@ package com.simuwang.base.pojo.dto;
 
 import lombok.Data;
 
-import java.math.BigDecimal;
-import java.util.Date;
-
 @Data
 public class EmailFundNavDTO {
     /**
@@ -18,25 +15,25 @@ public class EmailFundNavDTO {
     /**
      * 净值日期
      */
-    private Date priceDate;
+    private String priceDate;
     /**
      * 单位净值
      */
-    private BigDecimal nav;
+    private String nav;
     /**
      * 累计单位净值
      */
-    private BigDecimal cumulativeNav;
+    private String cumulativeNavWithdrawal;
     /**
-     * 资产份额
+     * 虚拟净值
      */
-    private BigDecimal assetShare;
+    private String virtualNav;
     /**
-     * 资产总值
+     * 资产份额
      */
-    private BigDecimal assetTotal;
+    private String assetShare;
     /**
      * 资产净值(基金规模)
      */
-    private BigDecimal assetNet;
+    private String assetNet;
 }

+ 22 - 8
service-daq/src/main/java/com/simuwang/daq/service/EmailParseService.java

@@ -40,7 +40,7 @@ import java.util.stream.Collectors;
 @Service
 public class EmailParseService {
 
-    private static final Logger logger = LoggerFactory.getLogger(EmailParseService.class);
+    private static final Logger log = LoggerFactory.getLogger(EmailParseService.class);
 
     private final EmailTypeRuleMapper emailTypeRuleMapper;
     private final EmailRuleConfig emailRuleConfig;
@@ -71,19 +71,33 @@ public class EmailParseService {
         try {
             emailContentMap = realEmail(mailboxInfoDTO, emailTypeMap, startDate, endDate);
         } catch (Exception e) {
-            logger.info("采集邮件失败 -> 邮箱配置信息:{},堆栈信息:{}", mailboxInfoDTO, ExceptionUtil.stacktraceToString(e));
+            log.info("采集邮件失败 -> 邮箱配置信息:{},堆栈信息:{}", mailboxInfoDTO, ExceptionUtil.stacktraceToString(e));
             return;
         }
         for (Map.Entry<String, List<EmailContentInfoDTO>> emailEntry : emailContentMap.entrySet()) {
             List<EmailContentInfoDTO> emailContentInfoDTOList = emailEntry.getValue();
+            if (CollUtil.isEmpty(emailContentInfoDTOList)) {
+                log.warn("未采集到正文或附件");
+            }
+            List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
             for (EmailContentInfoDTO emailContentInfoDTO : emailContentInfoDTOList) {
-                List<EmailFundNavDTO> emailFundNavDTOList = parseEmailContent(emailContentInfoDTO, emailFieldMap);
-
+                try {
+                    List<EmailFundNavDTO> fundNavDTOList = parseEmail(emailContentInfoDTO, emailFieldMap);
+                    emailFundNavDTOList.addAll(fundNavDTOList);
+                } catch (Exception e) {
+                    log.error("堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
+                }
             }
+            if (CollUtil.isEmpty(emailFundNavDTOList)) {
+                log.warn("未解析到净值 -> 邮件主题:{},邮件日期:{}", emailContentInfoDTOList.get(0).getEmailTitle(), emailContentInfoDTOList.get(0).getEmailDate());
+                continue;
+            }
+            // todo 邮件信息表,邮件文件表,邮件净值表,邮件规模表,基金净值表
+
         }
     }
 
-    private List<EmailFundNavDTO> parseEmailContent(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
+    private List<EmailFundNavDTO> parseEmail(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
         Integer emailType = emailContentInfoDTO.getEmailType();
         AbstractEmailParser emailParser = emailParserFactory.getInstance(emailType);
         return emailParser.parse(emailContentInfoDTO, emailFieldMap);
@@ -144,10 +158,10 @@ public class EmailParseService {
                 emailType = EmailUtil.getEmailTypeBySubject(message.getSubject(), emailTypeMap);
                 String emailDateStr = DateUtil.format(emailDate, DateConst.YYYY_MM_DD_HH_MM_SS);
                 if (emailType == null) {
-                    logger.info("邮件不满足解析条件 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
+                    log.info("邮件不满足解析条件 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
                     continue;
                 }
-                logger.info("邮件采集成功 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
+                log.info("邮件采集成功 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
                 Object content = message.getContent();
                 // 1.邮件为MIME多部分消息体:可能既有邮件又有正文
                 if (content instanceof MimeMultipart) {
@@ -177,7 +191,7 @@ public class EmailParseService {
                     emailMessageMap.put(uuidKey, emailContentInfoDTOList);
                 }
             } catch (Exception e) {
-                logger.error("获取邮箱的邮件报错,堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
+                log.error("获取邮箱的邮件报错,堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
             }
         }
         folder.close(false);

+ 231 - 3
service-daq/src/main/java/com/simuwang/daq/service/NavEmailParser.java

@@ -1,25 +1,32 @@
 package com.simuwang.daq.service;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Pair;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
+import com.simuwang.base.common.conts.DateConst;
+import com.simuwang.base.common.conts.EmailFieldConst;
 import com.simuwang.base.common.conts.EmailTypeConst;
 import com.simuwang.base.common.util.ExcelUtil;
 import com.simuwang.base.common.util.StringUtil;
 import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
 import com.simuwang.base.pojo.dto.EmailFundNavDTO;
+import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import java.io.File;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 /**
  * @author mozuwen
@@ -29,6 +36,13 @@ import java.util.Map;
 @Component
 public class NavEmailParser extends AbstractEmailParser {
 
+    private static final Logger log = LoggerFactory.getLogger(AbstractEmailParser.class);
+
+    private static final Integer ROW_DIRECTION_TYPE = 1;
+    private static final Integer COLUMN_DIRECTION_TYPE = 2;
+
+    private static final int MAX_COLUMN = 20;
+
     @Override
     public boolean isSupport(Integer emailType) {
         return EmailTypeConst.NAV_EMAIL_TYPE.equals(emailType);
@@ -58,27 +72,234 @@ public class NavEmailParser extends AbstractEmailParser {
      * @return 解析到的净值数据
      */
     private List<EmailFundNavDTO> parseExcelFile(String filePath, Map<String, List<String>> emailFieldMap) {
-        List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
         File file = new File(filePath);
         Sheet sheet = ExcelUtil.getSheet(file, 0);
+        // 1.找到表头所在位置
         Map<String, Pair<Integer, Integer>> fieldPositionMap = getFieldPosition(sheet, emailFieldMap);
         if (MapUtil.isEmpty(fieldPositionMap)) {
+            log.warn("找不到文件表头字段 -> 文件:{}", filePath);
+            return CollUtil.newArrayList();
+        }
+        // 2.解析sheet中的净值数据
+        return parseSheetData(sheet, fieldPositionMap);
+    }
 
+    private List<EmailFundNavDTO> parseSheetData(Sheet sheet, Map<String, Pair<Integer, Integer>> fieldPositionMap) {
+        List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
+        // 通过表头所在位置判断是行数据还是列数据
+        Integer dataDirectionType = detectDataDirection(fieldPositionMap);
+        // 数据起始行,起始列
+        int initRow = dataDirectionType.equals(ROW_DIRECTION_TYPE) ? fieldPositionMap.values().stream().map(Pair::getKey).max(Integer::compareTo).orElse(0)
+                : fieldPositionMap.values().stream().map(Pair::getKey).min(Integer::compareTo).orElse(0);
+        int initColumn = fieldPositionMap.values().stream().map(Pair::getValue).min(Integer::compareTo).orElse(0);
+        if (dataDirectionType.equals(ROW_DIRECTION_TYPE)) {
+            // 表头字段-列号映射关系
+            Map<String, Integer> fieldColumnMap = getFieldRow(fieldPositionMap);
+            int lastRowNum = sheet.getLastRowNum();
+            // 遍历可能的数据行
+            for (int rowNum = initRow + 1; rowNum <= lastRowNum; rowNum++) {
+                Row sheetRow = sheet.getRow(rowNum);
+                Optional.ofNullable(readSheetRowData(sheetRow, fieldColumnMap)).ifPresent(fundNavDTOList::add);
+            }
         }
+        if (dataDirectionType.equals(COLUMN_DIRECTION_TYPE)) {
+            // 表头字段-行号映射关系
+            Map<Integer, String> fieldRowMap = getRowField(fieldPositionMap);
+            int lastRow = fieldPositionMap.values().stream().map(Pair::getKey).max(Integer::compareTo).orElse(0);
+            // 遍历每一列
+            for (int columnNum = initColumn + 1; columnNum < MAX_COLUMN; columnNum++) {
+                Map<String, String> fieldValueMap = MapUtil.newHashMap();
+                for (int rowNum = initRow; rowNum <= lastRow; rowNum++) {
+                    Row row = sheet.getRow(rowNum);
+                    Cell cell = row.getCell(columnNum);
+                    if (cell == null) {
+                        continue;
+                    }
+                    fieldValueMap.put(fieldRowMap.get(rowNum), cell.getStringCellValue());
+                }
+                Optional.ofNullable(buildEmailFundNavDTO(fieldValueMap)).ifPresent(fundNavDTOList::add);
+            }
+        }
+
+        // 兼容净值日期为空的情况
+        addPriceDateIfMiss(fundNavDTOList, getPriceDateFromSheet(sheet, initRow));
         return fundNavDTOList;
     }
 
+    private void addPriceDateIfMiss(List<EmailFundNavDTO> fundNavDTOList, String priceDate) {
+        if (fundNavDTOList.stream().map(EmailFundNavDTO::getPriceDate).allMatch(StrUtil::isBlank)) {
+            fundNavDTOList.forEach(e -> e.setPriceDate(priceDate));
+        }
+    }
+
+    private String getPriceDateFromSheet(Sheet sheet, Integer maxRowNum) {
+        Map<Integer, String> priceDateMap = MapUtil.newHashMap();
+        for (int rowNum = 0; rowNum < maxRowNum; rowNum++) {
+            Row row = sheet.getRow(rowNum);
+            if (row == null) {
+                continue;
+            }
+            int lastCellNum = row.getLastCellNum();
+            for (int columnNum = 0; columnNum < lastCellNum; columnNum++) {
+                Cell cell = row.getCell(columnNum);
+                if (cell == null) {
+                    continue;
+                }
+                String cellValue = cell.getStringCellValue();
+                if (StrUtil.isNotBlank(cellValue) && cellValue.contains("截至")) {
+                    int index = cellValue.indexOf("截至");
+                    String date = cellValue.substring(index + 2, index + 2 + 10);
+                    if (StrUtil.isNotBlank(date)) {
+                        date = date.replaceAll("年", "-").replaceAll("月", "-");
+                    }
+                    priceDateMap.put(1, date);
+                    continue;
+                }
+                boolean isValidDate = StringUtil.isValidDate(cellValue);
+                if (isValidDate) {
+                    String date = cellValue.replaceAll("年", "-").replaceAll("月", "-");
+                    priceDateMap.put(2, date);
+                }
+            }
+        }
+        if (MapUtil.isNotEmpty(priceDateMap)) {
+            Integer key = priceDateMap.keySet().stream().min(Integer::compareTo).orElse(null);
+            return priceDateMap.get(key);
+        }
+        return null;
+    }
+
+    private EmailFundNavDTO buildEmailFundNavDTO(Map<String, String> fieldValueMap) {
+        if (MapUtil.isEmpty(fieldValueMap) || fieldValueMap.values().stream().allMatch(StrUtil::isBlank)) {
+            return null;
+        }
+        EmailFundNavDTO fundNavDTO = new EmailFundNavDTO();
+        fundNavDTO.setFundName(fieldValueMap.get(EmailFieldConst.FUND_NAME));
+        fundNavDTO.setRegisterNumber(fieldValueMap.get(EmailFieldConst.REGISTER_NUMBER));
+        fundNavDTO.setPriceDate(fieldValueMap.get(EmailFieldConst.PRICE_DATE));
+        fundNavDTO.setNav(fieldValueMap.get(EmailFieldConst.NAV));
+        fundNavDTO.setCumulativeNavWithdrawal(fieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL));
+        String assetNet = fieldValueMap.get(EmailFieldConst.ASSET_NET);
+        if (StrUtil.isBlank(assetNet) || !StrUtil.isNumeric(assetNet)) {
+            assetNet = null;
+        }
+        fundNavDTO.setAssetNet(assetNet);
+        String assetShares = fieldValueMap.get(EmailFieldConst.ASSET_NET);
+        if (StrUtil.isBlank(assetShares) || !StrUtil.isNumeric(assetShares)) {
+            assetShares = null;
+        }
+        fundNavDTO.setAssetShare(assetShares);
+        return fundNavDTO;
+    }
+
 
+    private EmailFundNavDTO readSheetRowData(Row sheetRow, Map<String, Integer> columnFieldMap) {
+        if (sheetRow == null) {
+            return null;
+        }
+        String nav = columnFieldMap.get(EmailFieldConst.NAV) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.NAV)) != null
+                ? sheetRow.getCell(columnFieldMap.get(EmailFieldConst.NAV)).getStringCellValue() : null;
+        String cumulativeNavWithdrawal = columnFieldMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL)) != null ?
+                sheetRow.getCell(columnFieldMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL)).getStringCellValue() : null;
+        if (StrUtil.isBlank(nav) || StrUtil.isBlank(cumulativeNavWithdrawal)) {
+            return null;
+        }
+        EmailFundNavDTO emailFundNavDTO = new EmailFundNavDTO();
+        String priceDate = columnFieldMap.get(EmailFieldConst.PRICE_DATE) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PRICE_DATE)) != null ?
+                sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PRICE_DATE)).getStringCellValue() : null;
+        if (StrUtil.isNotBlank(priceDate) && !priceDate.contains("-")) {
+            // 处理日期yyyyMMdd格式 -> 转成yyyy-MM-dd
+            priceDate = DateUtil.format(DateUtil.parse(priceDate, DateConst.YYYYMMDD), DateConst.YYYY_MM_DD);
+        }
+        emailFundNavDTO.setPriceDate(priceDate);
+        String fundName = columnFieldMap.get(EmailFieldConst.FUND_NAME) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.FUND_NAME)).getStringCellValue() != null ?
+                sheetRow.getCell(columnFieldMap.get(EmailFieldConst.FUND_NAME)).getStringCellValue() : null;
+        emailFundNavDTO.setFundName(fundName);
+        String registerNumber = columnFieldMap.get(EmailFieldConst.REGISTER_NUMBER) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.REGISTER_NUMBER)) != null ?
+                sheetRow.getCell(columnFieldMap.get(EmailFieldConst.REGISTER_NUMBER)).getStringCellValue() : null;
+        emailFundNavDTO.setRegisterNumber(registerNumber);
+        emailFundNavDTO.setNav(nav);
+        emailFundNavDTO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
+        String virtualNav = columnFieldMap.get(EmailFieldConst.VIRTUAL_NAV) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.VIRTUAL_NAV)) != null ?
+                sheetRow.getCell(columnFieldMap.get(EmailFieldConst.VIRTUAL_NAV)).getStringCellValue() : null;
+        emailFundNavDTO.setVirtualNav(virtualNav);
+        String assetNet = columnFieldMap.get(EmailFieldConst.ASSET_NET) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_NET)) != null ?
+                sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_NET)).getStringCellValue() : null;
+        if (StrUtil.isBlank(assetNet) || !StrUtil.isNumeric(assetNet)) {
+            assetNet = null;
+        }
+        emailFundNavDTO.setAssetNet(assetNet);
+        String assetShares = columnFieldMap.get(EmailFieldConst.ASSET_SHARE) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_SHARE)) != null ?
+                sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_SHARE)).getStringCellValue() : null;
+        if (StrUtil.isBlank(assetShares) || !StrUtil.isNumeric(assetShares)) {
+            assetShares = null;
+        }
+        emailFundNavDTO.setAssetShare(assetShares);
+        return emailFundNavDTO;
+    }
+
+    private Map<String, Integer> getFieldRow(Map<String, Pair<Integer, Integer>> fieldPositionMap) {
+        // 考虑日期字段识别逻辑的问题
+        long rowNumCount = fieldPositionMap.values().stream().map(Pair::getKey).distinct().count();
+        if (rowNumCount > 1) {
+            // 存在合并单元格的方式 -> 日期字段所在位置可能会存在错误
+            fieldPositionMap.remove(EmailFieldConst.PRICE_DATE);
+        }
+        Map<String, Integer> fieldRowMap = MapUtil.newHashMap();
+        for (Map.Entry<String, Pair<Integer, Integer>> fieldPositionEntry : fieldPositionMap.entrySet()) {
+            String field = fieldPositionEntry.getKey();
+            Integer column = fieldPositionEntry.getValue().getValue();
+            fieldRowMap.put(field, column);
+        }
+        return fieldRowMap;
+    }
+
+    private Map<Integer, String> getRowField(Map<String, Pair<Integer, Integer>> fieldPositionMap) {
+        Map<Integer, String> fieldRowMap = MapUtil.newHashMap();
+        for (Map.Entry<String, Pair<Integer, Integer>> fieldPositionEntry : fieldPositionMap.entrySet()) {
+            String field = fieldPositionEntry.getKey();
+            Integer column = fieldPositionEntry.getValue().getKey();
+            fieldRowMap.put(column, field);
+        }
+        return fieldRowMap;
+    }
+
+    /**
+     * 通过表头所在位置判断是行数据还是列数据
+     *
+     * @param fieldPositionMap excel中表头所在的位置
+     * @return 行方向-1,,列方向-2
+     */
+    private Integer detectDataDirection(Map<String, Pair<Integer, Integer>> fieldPositionMap) {
+        long count = fieldPositionMap.values().stream().map(Pair::getValue).distinct().count();
+        return count == 1 ? COLUMN_DIRECTION_TYPE : ROW_DIRECTION_TYPE;
+    }
+
+    /**
+     * 找出excel中表头所在的位置
+     *
+     * @param sheet         表格工作簿
+     * @param emailFieldMap 邮件字段识别规则映射表
+     * @return excel中表头所在的位置(行, 列)
+     */
     private Map<String, Pair<Integer, Integer>> getFieldPosition(Sheet sheet, Map<String, List<String>> emailFieldMap) {
         Map<String, Pair<Integer, Integer>> fieldPositionMap = MapUtil.newHashMap(8);
         int lastRowNum = sheet.getLastRowNum();
         for (int rowNum = 0; rowNum < lastRowNum; rowNum++) {
             Row sheetRow = sheet.getRow(rowNum);
+            if (sheetRow == null) {
+                continue;
+            }
             int lastCellNum = sheetRow.getLastCellNum();
             for (int cellNum = 0; cellNum < lastCellNum; cellNum++) {
-                String cellValue = sheetRow.getCell(cellNum).getStringCellValue();
+                Cell cell = sheetRow.getCell(cellNum);
+                if (cell == null) {
+                    continue;
+                }
+                String cellValue = cell.getStringCellValue();
                 String field = fieldMatch(cellValue, emailFieldMap);
-                if (StrUtil.isNotBlank(field) && !fieldPositionMap.containsKey(field)) {
+                // todo 考虑份额基金净值文件格式
+                if (StrUtil.isNotBlank(field)) {
                     fieldPositionMap.put(field, new Pair<>(rowNum, cellNum));
                 }
             }
@@ -86,6 +307,13 @@ public class NavEmailParser extends AbstractEmailParser {
         return fieldPositionMap;
     }
 
+    /**
+     * 判断单元格值是否为表头字段
+     *
+     * @param cellValue     单元格值
+     * @param emailFieldMap 邮件字段识别规则映射表
+     * @return 表头对应的标识
+     */
     public String fieldMatch(String cellValue, Map<String, List<String>> emailFieldMap) {
         if (StrUtil.isBlank(cellValue)) {
             return null;

+ 2 - 2
service-deploy/src/main/test/java/com/simuwang/datadaq/DataTrusteeApplicationTests.java

@@ -32,8 +32,8 @@ class DataTrusteeApplicationTests {
         emailInfoDTO.setProtocol("imap");
         Map<Integer, List<String>> emailTypeMap = MapUtil.newHashMap();
         emailTypeMap.put(1, List.of("净值"));
-        Date startDate = DateUtil.parse("2024-09-05 11:00:00", DateConst.YYYY_MM_DD_HH_MM_SS);
-        Date endDate = DateUtil.parse("2024-09-05 12:00:00", DateConst.YYYY_MM_DD_HH_MM_SS);
+        Date startDate = DateUtil.parse("2024-09-06 18:40:00", DateConst.YYYY_MM_DD_HH_MM_SS);
+        Date endDate = DateUtil.parse("2024-09-06 19:00:00", DateConst.YYYY_MM_DD_HH_MM_SS);
         try {
             emailParseService.parseEmail(emailInfoDTO, startDate, endDate);
         } catch (Exception e) {