Browse Source

feat:邮件解析-相关文件定义

mozuwen 7 months ago
parent
commit
6ab3c7584d
37 changed files with 1763 additions and 179 deletions
  1. 55 2
      service-base/pom.xml
  2. 12 0
      service-base/src/main/java/com/simuwang/base/common/conts/DateConst.java
  3. 20 0
      service-base/src/main/java/com/simuwang/base/common/conts/EmailTypeConst.java
  4. 246 0
      service-base/src/main/java/com/simuwang/base/common/util/EmailUtil.java
  5. 136 0
      service-base/src/main/java/com/simuwang/base/common/util/ExcelUtil.java
  6. 68 0
      service-base/src/main/java/com/simuwang/base/common/util/FileUtil.java
  7. 12 0
      service-base/src/main/java/com/simuwang/base/common/util/StringUtil.java
  8. 0 1
      service-base/src/main/java/com/simuwang/base/config/DataSourceAutoConfig.java
  9. 41 0
      service-base/src/main/java/com/simuwang/base/config/EmailRuleConfig.java
  10. 0 168
      service-base/src/main/java/com/simuwang/base/dataobject/RzFundNavDO.java
  11. 0 1
      service-base/src/main/java/com/simuwang/base/dataobject/package-info.java
  12. 18 0
      service-base/src/main/java/com/simuwang/base/mapper/EmailFieldMappingMapper.java
  13. 16 0
      service-base/src/main/java/com/simuwang/base/mapper/EmailTypeRuleMapper.java
  14. 18 0
      service-base/src/main/java/com/simuwang/base/mapper/MailboxInfoMapper.java
  15. 0 1
      service-base/src/main/java/com/simuwang/base/mapper/package-info.java
  16. 53 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFieldMappingDO.java
  17. 58 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFileInfoDO.java
  18. 79 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFundAssetDO.java
  19. 84 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFundNavDO.java
  20. 73 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/EmailParseInfoDO.java
  21. 58 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/EmailTypeRuleDO.java
  22. 93 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/MailboxInfoDO.java
  23. 59 0
      service-base/src/main/java/com/simuwang/base/pojo/dto/EmailContentInfoDTO.java
  24. 42 0
      service-base/src/main/java/com/simuwang/base/pojo/dto/EmailFundNavDTO.java
  25. 38 0
      service-base/src/main/java/com/simuwang/base/pojo/dto/MailboxInfoDTO.java
  26. 21 0
      service-base/src/main/resources/mapper/EmailFieldMappingMapper.xml
  27. 22 0
      service-base/src/main/resources/mapper/EmailTypeRuleMapper.xml
  28. 30 0
      service-base/src/main/resources/mapper/MailBoxInfoMapper.xml
  29. 4 1
      service-daq/pom.xml
  30. 0 1
      service-daq/src/main/java/com/simuwang/daq/manager/package-info.java
  31. 17 0
      service-daq/src/main/java/com/simuwang/daq/service/AbstractEmailParser.java
  32. 188 0
      service-daq/src/main/java/com/simuwang/daq/service/EmailParseService.java
  33. 26 0
      service-daq/src/main/java/com/simuwang/daq/service/EmailParserFactory.java
  34. 125 0
      service-daq/src/main/java/com/simuwang/daq/service/NavEmailParser.java
  35. 0 1
      service-daq/src/main/java/com/simuwang/daq/service/package-info.java
  36. 8 3
      service-deploy/src/main/resources/application.yml
  37. 43 0
      service-deploy/src/main/test/java/com/simuwang/datadaq/DataTrusteeApplicationTests.java

+ 55 - 2
service-base/pom.xml

@@ -23,7 +23,13 @@
         </dependency>
         <dependency>
             <groupId>com.baomidou</groupId>
-            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <artifactId>mybatis-plus</artifactId>
+            <version>3.5.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>3.0.3</version>
         </dependency>
 
         <!-- 私有库 -->
@@ -53,7 +59,54 @@
         </dependency>
         <dependency>
             <groupId>cn.hutool</groupId>
-            <artifactId>hutool-json</artifactId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.31</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>jakarta.mail</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.eclipse.angus</groupId>
+                    <artifactId>angus-mail</artifactId>
+                </exclusion>
+            </exclusions>
+            <version>2.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.angus</groupId>
+            <artifactId>angus-mail</artifactId>
+            <version>2.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.18.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>3.17</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.jexcelapi</groupId>
+            <artifactId>jxl</artifactId>
+            <version>2.6.12</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>3.17</version>
         </dependency>
     </dependencies>
 </project>

+ 12 - 0
service-base/src/main/java/com/simuwang/base/common/conts/DateConst.java

@@ -0,0 +1,12 @@
+package com.simuwang.base.common.conts;
+
+public class DateConst {
+
+    public final static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+    public final static String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
+    public final static String YYYY_MM_DD = "yyyy-MM-dd";
+    public final static String YYYYMMDD = "yyyyMMdd";
+    public final static String YYYYMMDDHHMMSS = "yyyyMMddHHmmssSSS";
+    public final static String YYYYMMDDHHMMSS24 = "yyyyMMddHHmmss";
+
+}

+ 20 - 0
service-base/src/main/java/com/simuwang/base/common/conts/EmailTypeConst.java

@@ -0,0 +1,20 @@
+package com.simuwang.base.common.conts;
+
+public class EmailTypeConst {
+
+    /**
+     * 净值邮件类型
+     */
+    public final static Integer NAV_EMAIL_TYPE = 1;
+
+    /**
+     * 估值表邮件类型
+     */
+    public final static Integer VALUATION_EMAIL_TYPE = 2;
+
+    /**
+     * 定期报告邮件类型
+     */
+    public final static Integer REPORT_EMAIL_TYPE = 3;
+
+}

+ 246 - 0
service-base/src/main/java/com/simuwang/base/common/util/EmailUtil.java

@@ -0,0 +1,246 @@
+package com.simuwang.base.common.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.simuwang.base.common.conts.DateConst;
+import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
+import com.simuwang.base.pojo.dto.MailboxInfoDTO;
+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.apache.commons.io.FileUtils;
+import org.eclipse.angus.mail.imap.IMAPSSLStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author mozuwen
+ * @date 2024-09-04
+ * @description 邮件解析工具
+ */
+public class EmailUtil {
+
+    private static final Logger logger = LoggerFactory.getLogger(EmailUtil.class);
+
+    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 + "/" + emailAddress + "/" + emailDateStr + "/";
+
+        MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
+        int length = mimeMultipart.getCount();
+        // 遍历邮件消息体
+        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.邮件正文
+            if ("String".equals(contentClass)) {
+                // 文件名 = 邮件主题 + 邮件日期
+                String fileName = emailTitle + "_" + emailDate + ".html";
+                emailContentInfoDTO = collectTextPart(part, partContent, filePath, fileName);
+            } else if ("BASE64DecoderStream".equals(contentClass)) {
+                if (StrUtil.isNotBlank(part.getFileName())) {
+                    String fileName = emailDate + MimeUtility.decodeText(part.getFileName());
+                    emailContentInfoDTO.setFileName(fileName);
+
+                    File savefile = new File(filePath + fileName);
+                    if (!savefile.exists()) {
+                        if (!savefile.getParentFile().exists()) {
+                            savefile.getParentFile().mkdirs();
+                        }
+                        FileUtil.saveFile(savefile, part);
+                    } else {
+                        FileUtils.deleteQuietly(savefile);
+                        FileUtil.saveFile(savefile, part);
+                    }
+                    emailContentInfoDTO.setFilePath(filePath + fileName);
+                }
+            } else if ("MimeMultipart".equals(contentClass)) {
+                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";
+                        emailContentInfoDTO = collectTextPart(part, partContent, filePath, fileName);
+                    }
+                }
+            }
+            if (emailContentInfoDTO.getEmailContent() == null && emailContentInfoDTO.getFilePath() == null) {
+                continue;
+            }
+            emailContentInfoDTO.setEmailAddress(emailAddress);
+            emailContentInfoDTO.setEmailTitle(emailTitle);
+            emailContentInfoDTO.setEmailDate((emailDate));
+            emailContentInfoDTOList.add(emailContentInfoDTO);
+        }
+
+        return emailContentInfoDTOList;
+    }
+
+    /**
+     * 根据日期过滤邮件
+     *
+     * @param messages  采集到的邮件
+     * @param startDate 邮件起始日期
+     * @param endDate   邮件截止日期
+     * @return 符合日期的邮件
+     */
+    public static List<Message> filterMessage(Message[] messages, Date startDate, Date endDate) {
+        long startTime = System.currentTimeMillis();
+        List<Message> messageList = CollUtil.newArrayList();
+        if (messages == null) {
+            return messageList;
+        }
+        for (Message message : messages) {
+            try {
+                if (message.getSentDate().compareTo(startDate) >= 0 && message.getSentDate().compareTo(endDate) <= 0) {
+                    messageList.add(message);
+                }
+            } catch (MessagingException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        logger.info("根据日期过滤邮件耗时 -> {}ms", (System.currentTimeMillis() - startTime));
+        return messageList;
+    }
+
+    /**
+     * 采集邮件正文
+     *
+     * @param part        邮件消息体
+     * @param partContent 邮件消息内筒
+     * @param filePath    文件路径
+     * @param fileName    文件名
+     * @return 采集到邮件正文(html格式包含table标签)
+     */
+    public static EmailContentInfoDTO collectTextPart(MimeBodyPart part, Object partContent, String filePath, String fileName) {
+        EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
+        try {
+            if ((part.getContentType().contains("text/html") || part.getContentType().contains("TEXT/HTML"))) {
+                if (partContent.toString().contains("<table")) {
+                    emailContentInfoDTO.setEmailContent(partContent.toString());
+                    String savePath = filePath + fileName;
+                    File savefile = new File(savePath);
+                    if (!savefile.exists()) {
+                        if (!savefile.getParentFile().exists()) {
+                            savefile.getParentFile().mkdirs();
+                            savefile.getParentFile().setExecutable(true);
+                        }
+                        FileUtil.writeFile(savePath, partContent.toString());
+                        emailContentInfoDTO.setFileName(fileName);
+                        emailContentInfoDTO.setFilePath(savePath);
+                    }
+                }
+            }
+        } catch (MessagingException e) {
+            logger.info("邮件正文采集失败 -> 文件名:{}, 报错堆栈:{}", fileName, ExceptionUtil.stacktraceToString(e));
+            return emailContentInfoDTO;
+        }
+        return emailContentInfoDTO;
+    }
+
+    /**
+     * 判断邮件是否符合解析条件
+     *
+     * @param subject      邮件主题
+     * @param emailTypeMap 邮件类型识别规则映射表
+     * @return 邮件类型:1-净值,2-估值表,3-定期报告,null代表不支持该邮件解析
+     */
+    public static Integer getEmailTypeBySubject(String subject, Map<Integer, List<String>> emailTypeMap) {
+        if (MapUtil.isEmpty(emailTypeMap)) {
+            return null;
+        }
+        for (Map.Entry<Integer, List<String>> emailTypeEntry : emailTypeMap.entrySet()) {
+            for (String field : emailTypeEntry.getValue()) {
+                if (subject.contains(field)) {
+                    return emailTypeEntry.getKey();
+                }
+            }
+        }
+        return null;
+    }
+
+    public static Store getStoreNew(MailboxInfoDTO mailboxInfoDTO) {
+        // 配置连接邮件服务器参数
+        Properties props = getMailProps(mailboxInfoDTO);
+        // 创建Session实例对象
+        Session session = Session.getInstance(props, new JakartaUserPassAuthenticator(mailboxInfoDTO.getAccount(), mailboxInfoDTO.getPassword()));
+        Store store;
+        try {
+            String protocol = mailboxInfoDTO.getProtocol().equals(IMAP) ? "imaps" : "pop3";
+            if (mailboxInfoDTO.getProtocol().contains(IMAP)) {
+                IMAPSSLStore imapStore = (IMAPSSLStore) session.getStore(protocol);
+                imapStore.connect(mailboxInfoDTO.getHost(), mailboxInfoDTO.getAccount(), mailboxInfoDTO.getPassword());
+                // 网易邮箱需要带上身份标识,详情请看:https://www.hmail163.com/content/?404.html
+                Map<String, String> clientParams = new HashMap<>(2);
+                clientParams.put("name", "my-imap");
+                clientParams.put("version", "1.0");
+                imapStore.id(clientParams);
+                return imapStore;
+            } else {
+                store = session.getStore(protocol);
+                store.connect(mailboxInfoDTO.getHost(), mailboxInfoDTO.getAccount(), mailboxInfoDTO.getPassword());
+                return store;
+            }
+        } catch (Exception e) {
+            logger.error("用户:{},邮箱:{} 无法连接到邮箱,连接邮箱报错堆栈信息:{}", mailboxInfoDTO.getUserId(), mailboxInfoDTO.getAccount(), ExceptionUtil.stacktraceToString(e));
+            return null;
+        }
+    }
+
+    public static Properties getMailProps(MailboxInfoDTO mailboxInfoDTO) {
+        Properties props = new Properties();
+        if (mailboxInfoDTO.getProtocol().equalsIgnoreCase(POP3)) {
+            props.put("mail.pop3.host", mailboxInfoDTO.getHost());
+            props.put("mail.pop3.user", mailboxInfoDTO.getAccount());
+            props.put("mail.pop3.socketFactory", mailboxInfoDTO.getPort());
+            props.put("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+            props.put("mail.pop3.port", mailboxInfoDTO.getPort());
+            props.put("mail.store.protocol", mailboxInfoDTO.getProtocol());
+        }
+        if (mailboxInfoDTO.getProtocol().equalsIgnoreCase(IMAP)) {
+            props.put("mail.store.protocol", "imaps");
+            props.put("mail.imap.host", mailboxInfoDTO.getHost());
+            props.put("mail.imap.port", mailboxInfoDTO.getPort());
+            props.put("mail.imaps.ssl.enable", "true");
+            props.put("mail.imaps.ssl.trust", "*");
+            props.put("mail.imap.auth", "true");
+            props.put("mail.imap.starttls.enable", "true");
+            props.put("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+            props.put("mail.imap.socketFactory.fallback", "false");
+        }
+        return props;
+    }
+
+}

+ 136 - 0
service-base/src/main/java/com/simuwang/base/common/util/ExcelUtil.java

@@ -0,0 +1,136 @@
+package com.simuwang.base.common.util;
+
+import cn.hutool.core.util.StrUtil;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+
+public class ExcelUtil {
+
+    private static final Logger logger = LoggerFactory.getLogger(ExcelUtil.class);
+
+    public static boolean isExcel(String name) {
+        return name.endsWith("xls") || name.endsWith("xlsx");
+    }
+
+    public static boolean isPdf(String name) {
+        return name.endsWith("pdf") || name.endsWith("PDF");
+    }
+
+    public static boolean isZip(String filePath) {
+        return filePath.endsWith("zip") || filePath.endsWith("ZIP");
+    }
+
+    public static Sheet getSheet(File file, int sheetIndex) {
+        if (file == null || !file.exists()) {
+            return null;
+        }
+        InputStream is = file2InStream(file);
+        String[] arr = file.getName().split("\\.");
+        String ext = arr[arr.length - 1];
+        Sheet sheet = null;
+        try {
+            sheet = getSheet(is, ext, sheetIndex);
+        } catch (Exception e) {
+            logger.error(e.getMessage());
+            if (e.getMessage().contains("XSSF instead of HSSF")) {
+                is = file2InStream(file);
+                try {
+                    sheet = getSheet(is, "xlsx", sheetIndex);
+                } catch (IOException e1) {
+                    e1.printStackTrace();
+                }
+            } else if (e.getMessage().contains("HSSF instead of XSSF")) {
+                is = file2InStream(file);
+                try {
+                    sheet = getSheet(is, "xls", sheetIndex);
+                } catch (IOException e1) {
+                    e1.printStackTrace();
+                }
+            } else if ("xls".equals(ext) && e.getMessage().contains("left 4 bytes remaining still to be read")) {
+                file = changeXls(file);
+                is = file2InStream(file);
+                try {
+                    sheet = getSheet(is, "xls", sheetIndex);
+                    logger.info("文件转换成功!");
+                } catch (IOException e1) {
+                    e1.printStackTrace();
+                }
+            } else {
+                sheet = null;
+            }
+        }
+        return sheet;
+    }
+
+    public static Sheet getSheet(InputStream is, String ext, int sheetIndex) throws IOException {
+        if (ext == null) {
+            ext = "xls";
+        }
+        ext = ext.toLowerCase();
+        Sheet sheet = null;
+        if ("xls".equals(ext)) {
+            Workbook wb = new HSSFWorkbook(is);
+            sheet = getSheet(wb, sheetIndex, null);
+        } else if ("xlsx".equals(ext)) {
+            Workbook wb = new XSSFWorkbook(is);
+            sheet = getSheet(wb, sheetIndex, null);
+        }
+        return sheet;
+    }
+
+    private static Sheet getSheet(Workbook wb, int sheetIndex, String sheetName) throws IOException {
+        if (wb != null) {
+            Sheet sheet = null;
+            if (sheetIndex > -1) {
+                sheet = wb.getSheetAt(sheetIndex);
+            } else if (StrUtil.isNotBlank(sheetName) && !"null".equals(sheetName.toLowerCase())) {
+                sheet = wb.getSheet(sheetName);
+            }
+            wb.close();
+            // 同时会把文件输入流关闭
+            return sheet;
+        }
+        return null;
+    }
+
+    public static InputStream file2InStream(File file) {
+        if (!file.exists()) {
+            return null;
+        } else {
+            FileInputStream is = null;
+
+            try {
+                is = new FileInputStream(file);
+            } catch (FileNotFoundException var3) {
+                var3.printStackTrace();
+            }
+
+            return is;
+        }
+    }
+
+    public static File changeXls(File file) {
+        try {
+            jxl.Workbook workbook = jxl.Workbook.getWorkbook(file);
+            String fileName = "copy-" + file.getName();
+            File file2 = new File(file.getParent() + "/" + fileName);
+            if (file2.exists()) {
+                return file2;
+            }
+            jxl.write.WritableWorkbook wwb = jxl.Workbook.createWorkbook(file2, workbook);
+            wwb.write();
+            wwb.close();
+            workbook.close();
+            return file2;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

+ 68 - 0
service-base/src/main/java/com/simuwang/base/common/util/FileUtil.java

@@ -0,0 +1,68 @@
+package com.simuwang.base.common.util;
+
+import jakarta.mail.Part;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+
+public class FileUtil {
+
+    private static final Logger logger = LoggerFactory.getLogger(FileUtil.class);
+
+    public static void writeFile(String fileName, String content) {
+        writeFile(new File(fileName), content, "UTF-8");
+    }
+
+    public static void writeFile(File file, String content, String charsets) {
+        writeFile(file, content, charsets, false);
+    }
+
+    public static void writeFile(File file, String content, String charsets, boolean append) {
+        Writer fw = null;
+        String fileName = file.getAbsolutePath();
+
+        try {
+            File folder = file.getParentFile();
+            if (!folder.exists()) {
+                folder.setExecutable(true, false);
+                folder.setReadable(true, false);
+                folder.mkdirs();
+            }
+
+            if (append) {
+                fw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charsets));
+            } else {
+                fw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charsets));
+            }
+
+            fw.write(content);
+            fw.flush();
+        } catch (IOException var15) {
+            logger.error("写入失败!file=" + fileName);
+            var15.printStackTrace();
+        } finally {
+            try {
+                if (fw != null) {
+                    fw.close();
+                }
+            } catch (IOException var14) {
+                logger.error("写入的IO关闭失败!file= " + fileName);
+                var14.printStackTrace();
+            }
+
+        }
+    }
+
+    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);
+        }
+        bos.close();
+        is.close();
+    }
+}

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

@@ -0,0 +1,12 @@
+package com.simuwang.base.common.util;
+
+public class StringUtil {
+
+    public static String retainChineseCharacters(String value) {
+        // 正则表达式匹配中文字符
+        String regex = "[^\\u4e00-\\u9fa5]";
+        // 使用空字符串替换所有非中文字符
+        return value.replaceAll(regex, "");
+    }
+
+}

+ 0 - 1
service-base/src/main/java/com/simuwang/base/config/DataSourceAutoConfig.java

@@ -1,6 +1,5 @@
 package com.simuwang.base.config;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
 import com.baomidou.mybatisplus.core.MybatisConfiguration;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;

+ 41 - 0
service-base/src/main/java/com/simuwang/base/config/EmailRuleConfig.java

@@ -0,0 +1,41 @@
+package com.simuwang.base.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "email-rule")
+public class EmailRuleConfig {
+
+    private String nav;
+
+    private String valuation;
+
+    private String report;
+
+    public String getNav() {
+        return nav;
+    }
+
+    public void setNav(String nav) {
+        this.nav = nav;
+    }
+
+    public String getValuation() {
+        return valuation;
+    }
+
+    public void setValuation(String valuation) {
+        this.valuation = valuation;
+    }
+
+    public String getReport() {
+        return report;
+    }
+
+    public void setReport(String report) {
+        this.report = report;
+    }
+
+
+}

+ 0 - 168
service-base/src/main/java/com/simuwang/base/dataobject/RzFundNavDO.java

@@ -1,168 +0,0 @@
-package com.simuwang.base.dataobject;
-
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import java.math.BigDecimal;
-import java.util.Date;
-
-@TableName("rz_fund_nav")
-public class RzFundNavDO {
-    /**
-     * 主键Id
-     */
-    @TableId(value = "id")
-    private Integer id;
-    /**
-     * 基金id(CF开头)
-     */
-    @TableField(value = "fund_id")
-    private String fundId;
-    /**
-     * 净值日期
-     */
-    @TableField(value = "price_date")
-    private Date priceDate;
-    /**
-     * 单位净值
-     */
-    @TableField(value = "nav")
-    private BigDecimal nav;
-    /**
-     * 累计单位净值
-     */
-    @TableField(value = "cumulative_nav_withdrawal")
-    private BigDecimal cumulativeNavWithdrawal;
-    /**
-     * 复权净值
-     */
-    @TableField(value = "cumulative_nav")
-    private BigDecimal cumulativeNav;
-    /**
-     * 净值来源:1-托管接入,2-邮件解析,3-手动填写
-     */
-    @TableField(value = "source")
-    private Integer source;
-    /**
-     * 记录的有效性;1-有效;0-无效
-     */
-    @TableField(value = "isvalid")
-    private Integer isvalid;
-    /**
-     * 创建者Id
-     */
-    @TableField(value = "creatorid")
-    private Integer creatorId;
-    /**
-     * 创建时间
-     */
-    @TableField(value = "createtime")
-    private Date createTime;
-    /**
-     * 修改者Id
-     */
-    @TableField(value = "updaterid")
-    private Integer updaterId;
-    /**
-     * 修改时间
-     */
-    @TableField(value = "updatetime")
-    private Date updateTime;
-
-    public Integer getId() {
-        return id;
-    }
-
-    public void setId(Integer id) {
-        this.id = id;
-    }
-
-    public String getFundId() {
-        return fundId;
-    }
-
-    public void setFundId(String fundId) {
-        this.fundId = fundId;
-    }
-
-    public Date getPriceDate() {
-        return priceDate;
-    }
-
-    public void setPriceDate(Date priceDate) {
-        this.priceDate = priceDate;
-    }
-
-    public BigDecimal getNav() {
-        return nav;
-    }
-
-    public void setNav(BigDecimal nav) {
-        this.nav = nav;
-    }
-
-    public BigDecimal getCumulativeNavWithdrawal() {
-        return cumulativeNavWithdrawal;
-    }
-
-    public void setCumulativeNavWithdrawal(BigDecimal cumulativeNavWithdrawal) {
-        this.cumulativeNavWithdrawal = cumulativeNavWithdrawal;
-    }
-
-    public BigDecimal getCumulativeNav() {
-        return cumulativeNav;
-    }
-
-    public void setCumulativeNav(BigDecimal cumulativeNav) {
-        this.cumulativeNav = cumulativeNav;
-    }
-
-    public Integer getSource() {
-        return source;
-    }
-
-    public void setSource(Integer source) {
-        this.source = source;
-    }
-
-    public Integer getIsvalid() {
-        return isvalid;
-    }
-
-    public void setIsvalid(Integer isvalid) {
-        this.isvalid = isvalid;
-    }
-
-    public Integer getCreatorId() {
-        return creatorId;
-    }
-
-    public void setCreatorId(Integer creatorId) {
-        this.creatorId = creatorId;
-    }
-
-    public Date getCreateTime() {
-        return createTime;
-    }
-
-    public void setCreateTime(Date createTime) {
-        this.createTime = createTime;
-    }
-
-    public Integer getUpdaterId() {
-        return updaterId;
-    }
-
-    public void setUpdaterId(Integer updaterId) {
-        this.updaterId = updaterId;
-    }
-
-    public Date getUpdateTime() {
-        return updateTime;
-    }
-
-    public void setUpdateTime(Date updateTime) {
-        this.updateTime = updateTime;
-    }
-}

+ 0 - 1
service-base/src/main/java/com/simuwang/base/dataobject/package-info.java

@@ -1 +0,0 @@
-package com.simuwang.base.dataobject;

+ 18 - 0
service-base/src/main/java/com/simuwang/base/mapper/EmailFieldMappingMapper.java

@@ -0,0 +1,18 @@
+package com.simuwang.base.mapper;
+
+import com.simuwang.base.pojo.dos.EmailFieldMappingDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface EmailFieldMappingMapper {
+
+    /**
+     * 获取净值文件字段识别映射配置
+     *
+     * @return 净值文件字段识别映射配置
+     */
+    List<EmailFieldMappingDO> getEmailFieldMapping();
+
+}

+ 16 - 0
service-base/src/main/java/com/simuwang/base/mapper/EmailTypeRuleMapper.java

@@ -0,0 +1,16 @@
+package com.simuwang.base.mapper;
+
+import com.simuwang.base.pojo.dos.EmailTypeRuleDO;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface EmailTypeRuleMapper {
+
+    /**
+     * 获取邮箱类型配置规则
+     *
+     * @return 邮箱类型配置规则
+     */
+    EmailTypeRuleDO getEmailTypeRule();
+
+}

+ 18 - 0
service-base/src/main/java/com/simuwang/base/mapper/MailboxInfoMapper.java

@@ -0,0 +1,18 @@
+package com.simuwang.base.mapper;
+
+import com.simuwang.base.pojo.dos.MailboxInfoDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface MailboxInfoMapper {
+
+    /**
+     * 查询配置邮箱信息
+     *
+     * @return 配置邮箱信息
+     */
+    List<MailboxInfoDO> listMailboxInfo();
+
+}

+ 0 - 1
service-base/src/main/java/com/simuwang/base/mapper/package-info.java

@@ -1 +0,0 @@
-package com.simuwang.base.mapper;

+ 53 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFieldMappingDO.java

@@ -0,0 +1,53 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("email_field_mapping")
+public class EmailFieldMappingDO {
+    /**
+     * 主键Id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 字段编码
+     */
+    @TableField(value = "code")
+    private String code;
+    /**
+     * 字段(多个以英文逗号隔开)
+     */
+    @TableField(value = "name")
+    private String name;
+    /**
+     * 记录的有效性;1-有效;0-无效;
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 创建时间,默认第一次创建的getdate()时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 58 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFileInfoDO.java

@@ -0,0 +1,58 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("email_file_info")
+public class EmailFileInfoDO {
+    /**
+     * 主键Id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 邮件id(email_parse_info.id)
+     */
+    @TableField(value = "email_id")
+    private Integer emailId;
+    /**
+     * 附件名称
+     */
+    @TableField(value = "file_name")
+    private String fileName;
+    /**
+     * 附件路径
+     */
+    @TableField(value = "file_path")
+    private String filePath;
+    /**
+     * 记录的有效性;1-有效;0-无效;
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 创建时间,默认第一次创建的getdate()时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 79 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFundAssetDO.java

@@ -0,0 +1,79 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("email_fund_asset")
+public class EmailFundAssetDO {
+    /**
+     * 主键Id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 基金id
+     */
+    @TableField(value = "fund_id")
+    private String fundId;
+    /**
+     * 邮件解析的基金名称
+     */
+    @TableField(value = "fund_name")
+    private String fundName;
+    /**
+     * 邮件解析的备案编码
+     */
+    @TableField(value = "register_number")
+    private String registerNumber;
+    /**
+     * 规模日期
+     */
+    @TableField(value = "price_date")
+    private Date priceDate;
+    /**
+     * 资产份额
+     */
+    @TableField(value = "asset_share")
+    private BigDecimal assetShare;
+    /**
+     * 资产总值
+     */
+    @TableField(value = "asset_total")
+    private BigDecimal assetTotal;
+    /**
+     * 资产净值(基金规模)
+     */
+    @TableField(value = "asset_net")
+    private BigDecimal assetNet;
+    /**
+     * 记录的有效性;1-有效;0-无效
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 创建时间,默认第一次创建的getdate()时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 84 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFundNavDO.java

@@ -0,0 +1,84 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("email_fund_nav")
+public class EmailFundNavDO {
+    /**
+     * 主键Id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 邮件id(email_parse_info.id)
+     */
+    @TableField(value = "email_id")
+    private Integer emailId;
+    /**
+     * 邮件解析的基金名称
+     */
+    @TableField(value = "fund_name")
+    private String fundName;
+    /**
+     * 邮件解析的备案编码
+     */
+    @TableField(value = "register_number")
+    private String registerNumber;
+    /**
+     * 净值日期
+     */
+    @TableField(value = "price_date")
+    private Date priceDate;
+    /**
+     * 单位净值
+     */
+    @TableField(value = "nav")
+    private BigDecimal nav;
+    /**
+     * 累计单位净值
+     */
+    @TableField(value = "cumulative_nav")
+    private BigDecimal cumulativeNav;
+    /**
+     * 是否入库 0-没有,1-有
+     */
+    @TableField(value = "is_stored")
+    private Integer isStored;
+    /**
+     * 异常情况:1-无异常,2-净值缺失,3-未匹配基金,4-净值<=0,5-资产净值<=0
+     */
+    @TableField(value = "exception_status")
+    private Integer exceptionStatus;
+    /**
+     * 记录的有效性;1-有效;0-无效;
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 创建时间,默认第一次创建的getdate()时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 73 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/EmailParseInfoDO.java

@@ -0,0 +1,73 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("email_parse_info")
+public class EmailParseInfoDO {
+    /**
+     * 主键Id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 邮箱地址
+     */
+    @TableField(value = "email")
+    private String email;
+    /**
+     * 邮箱日期
+     */
+    @TableField(value = "email_date")
+    private String emailDate;
+    /**
+     * 解析日期
+     */
+    @TableField(value = "parse_date")
+    private String parseDate;
+    /**
+     * 邮件主题
+     */
+    @TableField(value = "email_title")
+    private String emailTitle;
+    /**
+     * 邮件类型,1-净值,2-估值表,3-定期报告
+     */
+    @TableField(value = "email_type")
+    private Integer emailType;
+    /**
+     * 解析状态
+     */
+    @TableField(value = "parse_status")
+    private Integer parseStatus;
+    /**
+     * 记录的有效性;1-有效;0-无效;
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 创建时间,默认第一次创建的getdate()时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 58 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/EmailTypeRuleDO.java

@@ -0,0 +1,58 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("email_type_rule")
+public class EmailTypeRuleDO {
+    /**
+     * 主键Id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 净值类型识别规则(多个以英文逗号隔开)
+     */
+    @TableField(value = "nav")
+    private String nav;
+    /**
+     * 估值表类型识别规则(多个以英文逗号隔开)
+     */
+    @TableField(value = "valuation")
+    private String valuation;
+    /**
+     * 报告类型识别规则(多个以英文逗号隔开)
+     */
+    @TableField(value = "report")
+    private String report;
+    /**
+     * 记录的有效性;1-有效;0-无效;
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 创建时间,默认第一次创建的getdate()时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 93 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/MailboxInfoDO.java

@@ -0,0 +1,93 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("mailbox_info")
+public class MailboxInfoDO {
+    /**
+     * 主键Id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 用户id
+     */
+    @TableField(value = "user_id")
+    private Integer userId;
+    /**
+     * 邮箱类型:1-QQ邮箱,2-腾讯企业邮箱,3-网易邮箱,4-新浪邮箱,99-其他
+     */
+    @TableField(value = "type")
+    private Integer type;
+    /**
+     * 邮箱账号
+     */
+    @TableField(value = "email")
+    private String email;
+    /**
+     * 邮箱密码
+     */
+    @TableField(value = "password")
+    private String password;
+    /**
+     * 协议
+     */
+    @TableField(value = "protocol")
+    private String protocol;
+    /**
+     * 收件服务器
+     */
+    @TableField(value = "server")
+    private String server;
+    /**
+     * 端口
+     */
+    @TableField(value = "port")
+    private Integer port;
+    /**
+     * cron表达式
+     */
+    @TableField(value = "cron")
+    private String cron;
+    /**
+     * 是否开启,0-不开启,1-开启
+     */
+    @TableField(value = "open_status")
+    private Integer openStatus;
+    /**
+     * 备注信息
+     */
+    @TableField(value = "description")
+    private String description;
+    /**
+     * 记录的有效性;1-有效;0-无效;
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 创建时间,默认第一次创建的getdate()时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 59 - 0
service-base/src/main/java/com/simuwang/base/pojo/dto/EmailContentInfoDTO.java

@@ -0,0 +1,59 @@
+package com.simuwang.base.pojo.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+@Data
+public class EmailContentInfoDTO implements Serializable, Cloneable {
+
+    private static final long serialVersionUID = 202104140906313753L;
+
+    /**
+     * 邮箱地址
+     */
+    private String emailAddress;
+
+    /**
+     * 邮件主题
+     */
+    private String emailTitle;
+
+    /**
+     * 邮件日期:yyyyMMdd HH:mm:ss
+     */
+    private String emailDate;
+
+    /**
+     * 解析时间
+     */
+    private String parseDate;
+
+    /**
+     * 附件名称
+     */
+    private String fileName;
+
+    /**
+     * 附件地址
+     */
+    private String filePath;
+
+    /**
+     * 文件类型:1-净值文件,2-估值表文件,3-定期报告
+     */
+    private Integer emailType;
+
+    /**
+     * 邮件内容
+     */
+    private String emailContent;
+
+    /**
+     * 收件人
+     */
+    private String toEmail;
+
+}
+

+ 42 - 0
service-base/src/main/java/com/simuwang/base/pojo/dto/EmailFundNavDTO.java

@@ -0,0 +1,42 @@
+package com.simuwang.base.pojo.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class EmailFundNavDTO {
+    /**
+     * 邮件解析的基金名称
+     */
+    private String fundName;
+    /**
+     * 邮件解析的备案编码
+     */
+    private String registerNumber;
+    /**
+     * 净值日期
+     */
+    private Date priceDate;
+    /**
+     * 单位净值
+     */
+    private BigDecimal nav;
+    /**
+     * 累计单位净值
+     */
+    private BigDecimal cumulativeNav;
+    /**
+     * 资产份额
+     */
+    private BigDecimal assetShare;
+    /**
+     * 资产总值
+     */
+    private BigDecimal assetTotal;
+    /**
+     * 资产净值(基金规模)
+     */
+    private BigDecimal assetNet;
+}

+ 38 - 0
service-base/src/main/java/com/simuwang/base/pojo/dto/MailboxInfoDTO.java

@@ -0,0 +1,38 @@
+package com.simuwang.base.pojo.dto;
+
+import lombok.Data;
+
+@Data
+public class MailboxInfoDTO {
+
+    /**
+     * 用户id
+     */
+    private Integer userId;
+
+    /**
+     * 邮箱账号
+     */
+    private String account;
+
+    /**
+     * 邮箱密码
+     */
+    private String password;
+
+    /**
+     * 邮箱地址
+     */
+    private String host;
+
+    /**
+     * 端口
+     */
+    private String port;
+
+    /**
+     * 协议
+     */
+    private String protocol;
+
+}

+ 21 - 0
service-base/src/main/resources/mapper/EmailFieldMappingMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.simuwang.base.mapper.EmailFieldMappingMapper">
+    <resultMap id="BaseResultMap" type="com.simuwang.base.pojo.dos.EmailFieldMappingDO">
+        <id column="id" property="id"/>
+        <result column="code" property="code"/>
+        <result column="name" property="name"/>
+        <result column="isvalid" property="isvalid"/>
+        <result column="creatorid" property="creatorId"/>
+        <result column="createtime" property="createTime"/>
+        <result column="updaterid" property="updaterId"/>
+        <result column="updatetime" property="updateTime"/>
+    </resultMap>
+
+    <select id="getEmailFieldMapping" resultMap="BaseResultMap">
+        select *
+        from PPW_EMAIL.email_field_mapping
+        where isvalid = 1
+    </select>
+
+</mapper>

+ 22 - 0
service-base/src/main/resources/mapper/EmailTypeRuleMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.simuwang.base.mapper.EmailTypeRuleMapper">
+    <resultMap id="BaseResultMap" type="com.simuwang.base.pojo.dos.EmailTypeRuleDO">
+        <id column="id" property="id"/>
+        <result column="nav" property="nav"/>
+        <result column="valuation" property="valuation"/>
+        <result column="report" property="report"/>
+        <result column="isvalid" property="isvalid"/>
+        <result column="creatorid" property="creatorId"/>
+        <result column="createtime" property="createTime"/>
+        <result column="updaterid" property="updaterId"/>
+        <result column="updatetime" property="updateTime"/>
+    </resultMap>
+
+    <select id="getEmailTypeRule" resultMap="BaseResultMap">
+        select *
+        from PPW_EMAIL.email_type_rule
+        where isvalid = 1 limit 1
+    </select>
+
+</mapper>

+ 30 - 0
service-base/src/main/resources/mapper/MailBoxInfoMapper.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.simuwang.base.mapper.MailboxInfoMapper">
+    <resultMap id="BaseResultMap" type="com.simuwang.base.pojo.dos.MailboxInfoDO">
+        <id column="id" property="id"/>
+        <result column="user_id" property="userId"/>
+        <result column="type" property="type"/>
+        <result column="email" property="email"/>
+        <result column="password" property="password"/>
+        <result column="protocol" property="protocol"/>
+        <result column="server" property="server"/>
+        <result column="port" property="port"/>
+        <result column="cron" property="cron"/>
+        <result column="open_status" property="openStatus"/>
+        <result column="description" property="description"/>
+        <result column="creatorid" property="creatorId"/>
+        <result column="createtime" property="createTime"/>
+        <result column="updaterid" property="updaterId"/>
+        <result column="updatetime" property="updateTime"/>
+        <result column="isvalid" property="isvalid"/>
+    </resultMap>
+
+    <select id="listMailboxInfo" resultMap="BaseResultMap">
+        select *
+        from PPW_EMAIL.mailbox_info
+        where isvalid = 1
+          and open_status = 1
+    </select>
+
+</mapper>

+ 4 - 1
service-daq/pom.xml

@@ -13,6 +13,9 @@
     <description>数据采集</description>
 
     <dependencies>
-
+        <dependency>
+            <groupId>com.simuwang</groupId>
+            <artifactId>service-base</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 0 - 1
service-daq/src/main/java/com/simuwang/daq/manager/package-info.java

@@ -1 +0,0 @@
-package com.simuwang.daq.manager;

+ 17 - 0
service-daq/src/main/java/com/simuwang/daq/service/AbstractEmailParser.java

@@ -0,0 +1,17 @@
+package com.simuwang.daq.service;
+
+import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
+import com.simuwang.base.pojo.dto.EmailFundNavDTO;
+
+import java.util.List;
+import java.util.Map;
+
+public abstract class AbstractEmailParser {
+
+    public boolean isSupport(Integer emailType) {
+        return false;
+    }
+
+    public abstract List<EmailFundNavDTO> parse(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap);
+
+}

+ 188 - 0
service-daq/src/main/java/com/simuwang/daq/service/EmailParseService.java

@@ -0,0 +1,188 @@
+package com.simuwang.daq.service;
+
+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 com.simuwang.base.common.conts.DateConst;
+import com.simuwang.base.common.conts.EmailTypeConst;
+import com.simuwang.base.common.util.EmailUtil;
+import com.simuwang.base.common.util.FileUtil;
+import com.simuwang.base.config.EmailRuleConfig;
+import com.simuwang.base.mapper.EmailFieldMappingMapper;
+import com.simuwang.base.mapper.EmailTypeRuleMapper;
+import com.simuwang.base.pojo.dos.EmailFieldMappingDO;
+import com.simuwang.base.pojo.dos.EmailTypeRuleDO;
+import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
+import com.simuwang.base.pojo.dto.EmailFundNavDTO;
+import com.simuwang.base.pojo.dto.MailboxInfoDTO;
+import jakarta.mail.Folder;
+import jakarta.mail.Message;
+import jakarta.mail.Store;
+import jakarta.mail.internet.MimeMultipart;
+import jakarta.mail.search.ComparisonTerm;
+import jakarta.mail.search.ReceivedDateTerm;
+import jakarta.mail.search.SearchTerm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author mozuwen
+ * @date 2024-09-04
+ * @description 邮件解析服务
+ */
+@Service
+public class EmailParseService {
+
+    private static final Logger logger = LoggerFactory.getLogger(EmailParseService.class);
+
+    private final EmailTypeRuleMapper emailTypeRuleMapper;
+    private final EmailRuleConfig emailRuleConfig;
+    private final EmailFieldMappingMapper emailFieldMapper;
+    private final EmailParserFactory emailParserFactory;
+
+    public EmailParseService(EmailTypeRuleMapper emailTypeRuleMapper, EmailRuleConfig emailRuleConfig,
+                             EmailFieldMappingMapper emailFieldMapper, EmailParserFactory emailParserFactory) {
+        this.emailTypeRuleMapper = emailTypeRuleMapper;
+        this.emailRuleConfig = emailRuleConfig;
+        this.emailFieldMapper = emailFieldMapper;
+        this.emailParserFactory = emailParserFactory;
+    }
+
+    /**
+     * 解析指定邮箱指定时间范围内的邮件
+     *
+     * @param mailboxInfoDTO 邮箱配置信息
+     * @param startDate      邮件起始日期(yyyy-MM-dd HH:mm:ss)
+     * @param endDate        邮件截止日期(yyyy-MM-dd HH:mm:ss, 为null,将解析邮件日期小于等于startDate的当天邮件)
+     */
+    public void parseEmail(MailboxInfoDTO mailboxInfoDTO, Date startDate, Date endDate) {
+        // 邮件类型配置
+        Map<Integer, List<String>> emailTypeMap = getEmailType();
+        // 邮件字段识别映射表
+        Map<String, List<String>> emailFieldMap = getEmailFieldMapping();
+        Map<String, List<EmailContentInfoDTO>> emailContentMap;
+        try {
+            emailContentMap = realEmail(mailboxInfoDTO, emailTypeMap, startDate, endDate);
+        } catch (Exception e) {
+            logger.info("采集邮件失败 -> 邮箱配置信息:{},堆栈信息:{}", mailboxInfoDTO, ExceptionUtil.stacktraceToString(e));
+            return;
+        }
+        for (Map.Entry<String, List<EmailContentInfoDTO>> emailEntry : emailContentMap.entrySet()) {
+            List<EmailContentInfoDTO> emailContentInfoDTOList = emailEntry.getValue();
+            for (EmailContentInfoDTO emailContentInfoDTO : emailContentInfoDTOList) {
+                List<EmailFundNavDTO> emailFundNavDTOList = parseEmailContent(emailContentInfoDTO, emailFieldMap);
+
+            }
+        }
+    }
+
+    private List<EmailFundNavDTO> parseEmailContent(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
+        Integer emailType = emailContentInfoDTO.getEmailType();
+        AbstractEmailParser emailParser = emailParserFactory.getInstance(emailType);
+        return emailParser.parse(emailContentInfoDTO, emailFieldMap);
+    }
+
+    public Map<String, List<String>> getEmailFieldMapping() {
+        List<EmailFieldMappingDO> emailFieldMappingDOList = emailFieldMapper.getEmailFieldMapping();
+        return emailFieldMappingDOList.stream()
+                .collect(Collectors.toMap(EmailFieldMappingDO::getCode, v -> Arrays.stream(v.getName().split(",")).toList()));
+    }
+
+    public Map<Integer, List<String>> getEmailType() {
+        Map<Integer, List<String>> emailTypeMap = MapUtil.newHashMap(3);
+        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.NAV_EMAIL_TYPE, Arrays.stream(nav.split(",")).toList());
+        emailTypeMap.put(EmailTypeConst.VALUATION_EMAIL_TYPE, Arrays.stream(valuation.split(",")).toList());
+        emailTypeMap.put(EmailTypeConst.REPORT_EMAIL_TYPE, Arrays.stream(report.split(",")).toList());
+        return emailTypeMap;
+    }
+
+    /**
+     * 读取邮件
+     *
+     * @param mailboxInfoDTO 邮箱配置信息
+     * @param emailTypeMap   邮件类型识别规则映射表
+     * @param startDate      邮件起始日期
+     * @param endDate        邮件截止日期(为null,将解析邮件日期小于等于startDate的当天邮件)
+     * @return 读取到的邮件信息
+     * @throws Exception 异常信息
+     */
+    private Map<String, List<EmailContentInfoDTO>> realEmail(MailboxInfoDTO mailboxInfoDTO, Map<Integer, List<String>> emailTypeMap, Date startDate, Date endDate) throws Exception {
+        Store store = EmailUtil.getStoreNew(mailboxInfoDTO);
+        if (store == null) {
+            return MapUtil.newHashMap();
+        }
+        // 默认读取收件箱的邮件
+        Folder folder = store.getFolder("INBOX");
+        folder.open(Folder.READ_ONLY);
+        // 获取邮件日期大于等于startDate的邮件(搜索条件只支持按天)
+        SearchTerm startDateTerm = new ReceivedDateTerm(ComparisonTerm.GE, startDate);
+        Message[] messages = folder.search(startDateTerm);
+        String path = "/data/file/";
+        Map<String, List<EmailContentInfoDTO>> emailMessageMap = MapUtil.newHashMap();
+        for (Message message : messages) {
+            List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
+            String uuidKey = UUID.randomUUID().toString().replaceAll("-", "");
+            Integer emailType;
+            try {
+                Date emailDate = message.getSentDate();
+                boolean isParseConditionSatisfied = emailDate == null || (endDate == null && emailDate.compareTo(startDate) > 0)
+                        || emailDate.compareTo(startDate) < 0 || (endDate != null && emailDate.compareTo(endDate) > 0);
+                if (isParseConditionSatisfied) {
+                    continue;
+                }
+                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);
+                    continue;
+                }
+                logger.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);
+                    String fileName = message.getSubject() + DateUtil.format(emailDate, DateConst.YYYYMMDDHHMMSS24);
+                    String filePath = path + mailboxInfoDTO.getAccount() + "/" + fileName + ".html";
+                    File savefile = new File(path + filePath);
+                    savefile.setReadable(true);
+                    if (!savefile.exists()) {
+                        if (!savefile.getParentFile().exists()) {
+                            savefile.getParentFile().mkdirs();
+                            savefile.getParentFile().setExecutable(true);
+                        }
+                        FileUtil.writeFile(filePath, content.toString());
+                        emailContentInfoDTO.setFilePath(filePath);
+                    }
+                    emailContentInfoDTOList.add(emailContentInfoDTO);
+                }
+                if (CollUtil.isNotEmpty(emailContentInfoDTOList)) {
+                    emailContentInfoDTOList.forEach(e -> e.setEmailType(emailType));
+                    emailMessageMap.put(uuidKey, emailContentInfoDTOList);
+                }
+            } catch (Exception e) {
+                logger.error("获取邮箱的邮件报错,堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
+            }
+        }
+        folder.close(false);
+        store.close();
+        return emailMessageMap;
+    }
+
+}

+ 26 - 0
service-daq/src/main/java/com/simuwang/daq/service/EmailParserFactory.java

@@ -0,0 +1,26 @@
+package com.simuwang.daq.service;
+
+import cn.hutool.core.map.MapUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Component
+public class EmailParserFactory {
+
+    private static final Map<String, AbstractEmailParser> ABSTRACT_PARSER_MAP = MapUtil.newHashMap();
+
+    public EmailParserFactory(Map<String, AbstractEmailParser> abstractParserMap) {
+        ABSTRACT_PARSER_MAP.putAll(abstractParserMap);
+    }
+
+    public AbstractEmailParser getInstance(Integer emailType) {
+        for (Map.Entry<String, AbstractEmailParser> serviceEntry : ABSTRACT_PARSER_MAP.entrySet()) {
+            AbstractEmailParser parser = serviceEntry.getValue();
+            if (parser.isSupport(emailType)) {
+                return parser;
+            }
+        }
+        return null;
+    }
+}

+ 125 - 0
service-daq/src/main/java/com/simuwang/daq/service/NavEmailParser.java

@@ -0,0 +1,125 @@
+package com.simuwang.daq.service;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+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.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.springframework.stereotype.Component;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author mozuwen
+ * @date 2024-09-04
+ * @description 净值邮件解析器
+ */
+@Component
+public class NavEmailParser extends AbstractEmailParser {
+
+    @Override
+    public boolean isSupport(Integer emailType) {
+        return EmailTypeConst.NAV_EMAIL_TYPE.equals(emailType);
+    }
+
+    @Override
+    public List<EmailFundNavDTO> parse(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
+        List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
+        String emailContent = emailContentInfoDTO.getEmailContent();
+        // 1.解析邮件正文
+        if (StrUtil.isNotBlank(emailContent)) {
+            emailFundNavDTOList = parseEmailContent(emailContent, emailFieldMap);
+        }
+        // 2.解析邮件excel附件
+        if (StrUtil.isNotBlank(emailContentInfoDTO.getFilePath()) && ExcelUtil.isExcel(emailContentInfoDTO.getFileName())) {
+            List<EmailFundNavDTO> fundNavDTOList = parseExcelFile(emailContentInfoDTO.getFilePath(), emailFieldMap);
+            emailFundNavDTOList.addAll(fundNavDTOList);
+        }
+        return emailFundNavDTOList;
+    }
+
+    /**
+     * 解析邮件excel附件
+     *
+     * @param filePath      邮件excel附件地址
+     * @param emailFieldMap 邮件字段识别规则映射表
+     * @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);
+        Map<String, Pair<Integer, Integer>> fieldPositionMap = getFieldPosition(sheet, emailFieldMap);
+        if (MapUtil.isEmpty(fieldPositionMap)) {
+
+        }
+        return fundNavDTOList;
+    }
+
+
+    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);
+            int lastCellNum = sheetRow.getLastCellNum();
+            for (int cellNum = 0; cellNum < lastCellNum; cellNum++) {
+                String cellValue = sheetRow.getCell(cellNum).getStringCellValue();
+                String field = fieldMatch(cellValue, emailFieldMap);
+                if (StrUtil.isNotBlank(field) && !fieldPositionMap.containsKey(field)) {
+                    fieldPositionMap.put(field, new Pair<>(rowNum, cellNum));
+                }
+            }
+        }
+        return fieldPositionMap;
+    }
+
+    public String fieldMatch(String cellValue, Map<String, List<String>> emailFieldMap) {
+        if (StrUtil.isBlank(cellValue)) {
+            return null;
+        }
+        String newCellValue = StringUtil.retainChineseCharacters(cellValue);
+        for (Map.Entry<String, List<String>> fieldEntry : emailFieldMap.entrySet()) {
+            List<String> fieldList = fieldEntry.getValue();
+            for (String field : fieldList) {
+                if (newCellValue.equals(field)) {
+                    return fieldEntry.getKey();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 解析邮件正文
+     *
+     * @param emailContent  邮件excel附件地址
+     * @param emailFieldMap 邮件字段识别规则映射表
+     * @return 解析到的净值数据
+     */
+    private List<EmailFundNavDTO> parseEmailContent(String emailContent, Map<String, List<String>> emailFieldMap) {
+        List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
+        Document doc = Jsoup.parse(emailContent);
+        Element table = doc.select("table").first();
+        Elements rows = table.select("tr");
+        for (Element row : rows) {
+            Elements cells = row.select("td");
+            int cellSize = cells.size();
+            // todo 后续补充
+        }
+        return fundNavDTOList;
+    }
+
+}

+ 0 - 1
service-daq/src/main/java/com/simuwang/daq/service/package-info.java

@@ -1 +0,0 @@
-package com.simuwang.daq.service;

+ 8 - 3
service-deploy/src/main/resources/application.yml

@@ -22,9 +22,9 @@ spring:
       # 指定为HikariDataSource
       type: com.zaxxer.hikari.HikariDataSource
       driver-class-name: dm.jdbc.driver.DmDriver
-      url: jdbc:dm://39.108.170.90:5236/?schema=RZ_COMBINATION_MASTER
-      username: RZ_DMPPWUSER_PRO
-      password: Sowh!_44821
+      url: jdbc:dm://192.168.1.39:5236/?schema=PPW_EMAIL
+      username: SYSDBA
+      password: Dmppw2024
       # hikari连接池配置 对应 HikariConfig 配置属性类
       hikari:
         pool-name: HikariCP-daq
@@ -38,3 +38,8 @@ spring:
         connection-timeout: 300000
         # keepalive time
         keepalive-time: 60000
+
+email-rule:
+  nav: "净值,业绩报酬,规模"
+  valuation: "估值表"
+  report: "月报,年报,季报,报告(待确认)信息披露报告,证券类月报"

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

@@ -0,0 +1,43 @@
+package com.simuwang.datadaq;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.map.MapUtil;
+import com.simuwang.base.common.conts.DateConst;
+import com.simuwang.base.pojo.dto.MailboxInfoDTO;
+import com.simuwang.daq.service.EmailParseService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+class DataTrusteeApplicationTests {
+
+    @Autowired
+    EmailParseService emailParseService;
+
+    @Test
+    public void test() {
+        MailboxInfoDTO emailInfoDTO = new MailboxInfoDTO();
+        emailInfoDTO.setUserId(2395446);
+        emailInfoDTO.setAccount("mozuwen@simuwang.com");
+        emailInfoDTO.setPassword("Mzw@0306");
+//        emailInfoDTO.setAccount("fofservice@simuwang.com");
+//        emailInfoDTO.setPassword("cJH@2021");
+        emailInfoDTO.setHost("imap.exmail.qq.com");
+        emailInfoDTO.setPort("993");
+        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);
+        try {
+            emailParseService.parseEmail(emailInfoDTO, startDate, endDate);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}