Ver Fonte

feat:实现连接中金财富邮箱

mozuwen há 1 semana atrás
pai
commit
264bdd6a6c

+ 10 - 0
pom.xml

@@ -43,6 +43,16 @@
     <dependencyManagement>
         <dependencies>
             <dependency>
+                <groupId>commons-codec</groupId>
+                <artifactId>commons-codec</artifactId>
+                <version>1.18.0</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-io</groupId>
+                <artifactId>commons-io</artifactId>
+                <version>2.16.1</version>
+            </dependency>
+            <dependency>
                 <groupId>org.apache.httpcomponents</groupId>
                 <artifactId>httpclient</artifactId>
                 <version>${httpcomponents.version}</version>

+ 35 - 0
service-base/src/main/java/com/simuwang/base/pojo/CoreMailAttachmentDTO.java

@@ -0,0 +1,35 @@
+package com.simuwang.base.pojo;
+
+import lombok.Data;
+
+@Data
+public class CoreMailAttachmentDTO {
+    /**
+     * 附件id
+     */
+    private int id;
+    /**
+     * 附件名称
+     */
+    private String filename;
+    /**
+     * 附件类型
+     */
+    private String contentType;
+    /**
+     * 内容长度
+     */
+    private long contentLength;
+    /**
+     * 编码方式
+     */
+    private String encoding;
+    /**
+     * 附件名称
+     */
+    private int contentOffset;
+    /**
+     * 附件大小估算值
+     */
+    private int estimateSize;
+}

+ 154 - 24
service-daq/src/main/java/com/simuwang/daq/service/CoreMailApiService.java

@@ -4,16 +4,31 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
+import com.huawei.shade.org.apache.http.HttpEntity;
+import com.huawei.shade.org.apache.http.client.methods.CloseableHttpResponse;
+import com.huawei.shade.org.apache.http.client.methods.HttpGet;
+import com.huawei.shade.org.apache.http.client.methods.HttpPost;
+import com.huawei.shade.org.apache.http.entity.StringEntity;
+import com.huawei.shade.org.apache.http.impl.client.CloseableHttpClient;
+import com.huawei.shade.org.apache.http.impl.client.HttpClients;
+import com.huawei.shade.org.apache.http.util.EntityUtils;
+import com.simuwang.base.pojo.CoreMailAttachmentDTO;
 import com.simuwang.base.pojo.dos.*;
 import com.simuwang.daq.util.HttpClientUtil;
 import jakarta.validation.constraints.NotNull;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import javax.net.ssl.SSLContext;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -26,6 +41,9 @@ public class CoreMailApiService {
     @Value("${coremail.url}")
     private String coreMailUrl;
 
+    @Value("${email.file.path}")
+    private String navFilePath;
+
     /**
      * 登录获取token
      *
@@ -69,56 +87,168 @@ public class CoreMailApiService {
 
     public List<CoreMailFolderDTO> getAllFolders(String token, String cookie) {
         String url = coreMailUrl + "/coremail/s/json?func=mbox:getAllFolders&sid=" + token;
-        Map<String, String> requestParam = new HashMap<>();
-        requestParam.put("Cookie.Coremail", cookie);
-        requestParam.put("sid", token);
-        CoreMailFolderParam coreMailFolderParam = new CoreMailFolderParam();
-        coreMailFolderParam.setCoremail(cookie);
-
-        Map<String, Object> bodyMap = new HashMap<>();
+        Map<String, String> bodyMap = new HashMap<>();
         bodyMap.put("flush", "ture");
         bodyMap.put("stats", "false");
         bodyMap.put("threads", "false");
         bodyMap.put("order", "name");
-        bodyMap.put("Coremail", cookie);
         String bodyParam = JSONObject.toJSONString(bodyMap);
-        log.info("获取邮箱文件夹接口请求体 -> url:{}, requestParam:{}, bodyParam:{}", url, requestParam, bodyParam);
+        log.info("获取邮箱文件夹接口请求体 -> url:{}, bodyParam:{}", url, bodyParam);
+        String resp = null;
         try {
-            String resp = HttpUtil.get(url, bodyMap);
+            CloseableHttpClient httpClient = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost(url);
+            // 设置请求头
+            httpPost.setHeader("Cookie", "Coremail=" + cookie);
+            // 设置请求体
+            StringEntity entity = new StringEntity(bodyParam);
+            httpPost.setEntity(entity);
+            // 发送请求
+            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    resp = EntityUtils.toString(responseEntity);
+                }
+            }
             log.info("获取邮箱文件夹接口响应体:{}", resp);
             if (StrUtil.isBlank(resp)) {
                 log.warn("获取邮箱文件夹接口返回空响应");
                 return CollUtil.newArrayList();
             }
-
+            JSONObject jsonObject = JSONObject.parseObject(resp);
+            if (jsonObject == null) {
+                return null;
+            }
+            if (!"S_OK".equals(jsonObject.getString("code"))) {
+                log.error("获取邮箱文件夹失败, 返回码:{}", jsonObject.getString("code"));
+                return null;
+            }
+            String var = jsonObject.get("var") != null ? jsonObject.get("var").toString() : null;
+            if (var != null) {
+                return JSON.parseArray(var, CoreMailFolderDTO.class);
+            }
         } catch (Exception e) {
             log.error("获取邮箱文件夹接口异常:{}", ExceptionUtil.stacktraceToString(e));
         }
-        return ListUtil.toList(new CoreMailFolderDTO(1, "inbox"));
+        return ListUtil.toList(new CoreMailFolderDTO(1, "收件箱"));
     }
 
-    public List<CoreMailInfoDTO> getFolderMails(String token, Integer folderId) {
+    public List<CoreMailInfoDTO> getFolderMails(String token, String cookie, Integer folderId) {
         String url = coreMailUrl + "/coremail/s/json?func=mbox:listMessages&sid=" + token;
-        Map<String, String> requestParam = new HashMap<>();
-        requestParam.put("sid", token);
         CoreMailInfoParam coreMailInfoParam = new CoreMailInfoParam();
         coreMailInfoParam.setFid(folderId);
-        coreMailInfoParam.setFilter(new CoreMailInfoParam.FilterCondition("3:", "净值,业绩报酬,规模"));
+        coreMailInfoParam.setFilter(new CoreMailInfoParam.FilterCondition("3:", "净值"));
         String bodyParam = JSONObject.toJSONString(coreMailInfoParam);
-        log.info("获取文件夹邮件接口请求体 -> url:{}, requestParam:{}, bodyParam:{}", url, requestParam, bodyParam);
+        log.info("获取邮件接口请求体 -> url:{}, bodyParam:{}", url, bodyParam);
+        String resp = null;
         try {
-            String resp = HttpUtil.post(url, bodyParam);
-            log.info("获取文件夹邮件接口响应体:{}", resp);
+            CloseableHttpClient httpClient = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost(url);
+            // 设置请求头
+            httpPost.setHeader("Cookie", "Coremail=" + cookie);
+            // 设置请求体
+            StringEntity entity = new StringEntity(bodyParam);
+            httpPost.setEntity(entity);
+            // 发送请求
+            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    resp = EntityUtils.toString(responseEntity);
+                }
+            }
+            log.info("获取邮件接口响应体:{}", resp);
             if (StrUtil.isBlank(resp)) {
-                log.warn("获取文件夹邮件接口返回空响应");
+                log.warn("获取邮件接口返回空响应");
                 return CollUtil.newArrayList();
             }
-
+            JSONObject jsonObject = JSONObject.parseObject(resp);
+            if (jsonObject == null) {
+                return null;
+            }
+            if (!"S_OK".equals(jsonObject.getString("code"))) {
+                log.error("获取邮件失败, 返回码:{}", jsonObject.getString("code"));
+                return null;
+            }
+            String var = jsonObject.get("var") != null ? jsonObject.get("var").toString() : null;
+            if (var != null) {
+                return JSON.parseArray(var, CoreMailInfoDTO.class);
+            }
         } catch (Exception e) {
-
-            log.error("获取文件夹邮件接口异常:{}", ExceptionUtil.stacktraceToString(e));
+            log.error("获取邮件接口异常:{}", ExceptionUtil.stacktraceToString(e));
         }
+        return CollUtil.newArrayList();
+    }
 
+    public List<CoreMailAttachmentDTO> getAttachmentOfMail(String token, String cookie, String mid) {
+        String url = coreMailUrl +
+                "/coremail/s/json?func=mbox:readMessage&sid=" + token +
+                "&mid=" + mid;
+        String resp = null;
+        try {
+            CloseableHttpClient httpClient = HttpClients.createDefault();
+            HttpGet httpGet = new HttpGet(url);
+            // 设置请求头
+            httpGet.setHeader("Cookie", "Coremail=" + cookie);
+
+            // 发送请求
+            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    resp = EntityUtils.toString(responseEntity);
+                }
+            }
+            log.info("获取邮件附件接口响应体:{}", resp);
+            JSONObject jsonObject = JSONObject.parseObject(resp);
+            if (jsonObject == null) {
+                return CollUtil.newArrayList();
+            }
+            if (!"S_OK".equals(jsonObject.getString("code"))) {
+                log.error("获取邮件附件失败, 返回码:{}", jsonObject.getString("code"));
+                return CollUtil.newArrayList();
+            }
+            String var = jsonObject.get("var") != null ? jsonObject.get("var").toString() : null;
+            if (var == null) {
+                return CollUtil.newArrayList();
+            }
+            JSONObject varJsonObject = JSONObject.parseObject(var);
+            return varJsonObject.getJSONArray("attachments").toJavaList(CoreMailAttachmentDTO.class);
+        } catch (Exception e) {
+            log.error("获取邮件附件接口异常:{}", ExceptionUtil.stacktraceToString(e));
+        }
         return CollUtil.newArrayList();
     }
+
+    public File getFileFromAttachmentMail(String token, String cookie, String mid, int part, String fileName) {
+        String url = coreMailUrl +
+                "/coremail/s/json?func=mbox:getMessageData&sid=" + token +
+                "&mid=" + mid +
+                "&part=" + part;
+
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet httpGet = new HttpGet(url);
+            // 设置 Cookie 请求头
+            httpGet.setHeader("Cookie", "Coremail=" + cookie);
+
+            // 发送请求
+            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    InputStream content = responseEntity.getContent();
+                    // 将二进制数据流转成字节数组
+                    byte[] contentData = IOUtils.toByteArray(content);
+
+                    // 保存为文件
+                    File targetFile = new File(navFilePath + mid + "/" + fileName);
+                    FileUtils.writeByteArrayToFile(targetFile, contentData);
+
+                    log.info("文件已成功保存至: {}", targetFile.getAbsolutePath());
+                    return targetFile;
+                }
+            }
+        } catch (IOException e) {
+            log.error("保存附件时发生异常: {}", e.getMessage(), e);
+        }
+        return null;
+    }
+
 }

+ 75 - 14
service-daq/src/main/java/com/simuwang/daq/service/ReadEmailService.java

@@ -1,22 +1,27 @@
 package com.simuwang.daq.service;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
+import com.simuwang.base.common.conts.DateConst;
+import com.simuwang.base.pojo.CoreMailAttachmentDTO;
 import com.simuwang.base.pojo.dos.CoreMailCookieSidDTO;
 import com.simuwang.base.pojo.dos.CoreMailFolderDTO;
 import com.simuwang.base.pojo.dos.CoreMailInfoDTO;
 import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
 import com.simuwang.base.pojo.dto.MailboxInfoDTO;
-import org.jsoup.select.Collector;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
+import java.io.File;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 @Service
@@ -38,34 +43,90 @@ public class ReadEmailService {
      * @return 邮件信息
      */
     public Map<String, List<EmailContentInfoDTO>> readEmail(MailboxInfoDTO mailboxInfoDTO, Date startDate, Date endDate) {
+        Map<String, List<EmailContentInfoDTO>> emailKeyFileMap = MapUtil.newHashMap();
         String account = mailboxInfoDTO.getAccount();
         String password = mailboxInfoDTO.getPassword();
         // 获取token
         CoreMailCookieSidDTO cookieSidDTO = coreMailApiService.login(account, password);
         if (cookieSidDTO == null || cookieSidDTO.getSid() == null) {
             log.error("邮箱登录失败, 无法获取到token");
-            return MapUtil.newHashMap();
+            return emailKeyFileMap;
         }
         String token = cookieSidDTO.getSid();
         String cookie = cookieSidDTO.getCookie();
         // 获取邮箱文件夹(默认只读取收件箱Inbox的邮件)
         List<CoreMailFolderDTO> mailFolderList = coreMailApiService.getAllFolders(token, cookie);
-        Integer folderId = 1;
-        if (CollUtil.isNotEmpty(mailFolderList)) {
-            folderId = mailFolderList.stream().filter(e -> e.getName().equals("inbox"))
-                    .findFirst().map(CoreMailFolderDTO::getId).orElse(folderId);
-        }
-        // 获取文件夹的邮件id
-        List<CoreMailInfoDTO> coreMailInfoDTOList =  coreMailApiService.getFolderMails(token, folderId);
-        if(CollUtil.isEmpty(coreMailInfoDTOList)) {
+        Integer folderId = CollUtil.isEmpty(mailFolderList) ? 1 : mailFolderList.stream().filter(e -> e.getName().equals("收件箱"))
+                .findFirst().map(CoreMailFolderDTO::getId).orElse(1);
+        // 获取邮件
+        List<CoreMailInfoDTO> coreMailInfoDTOList = coreMailApiService.getFolderMails(token, cookie, folderId);
+        // 过滤符合解析要求的邮件
+        List<CoreMailInfoDTO> readEmailDTOList = coreMailInfoDTOList.stream()
+                .filter(e -> e.getFid().equals(folderId))
+                .filter(e -> filterBySubject(e.getSubject()))
+                .filter(e -> filterByEmailDate(e.getReceivedDate(), startDate, endDate)).toList();
+        if (CollUtil.isEmpty(readEmailDTOList)) {
             log.error("获取不到邮件");
-            return MapUtil.newHashMap();
+            return emailKeyFileMap;
         }
+        Map<String, CoreMailInfoDTO> emailIdInfoMap = coreMailInfoDTOList.stream()
+                .collect(Collectors.toMap(CoreMailInfoDTO::getId, e -> e));
         // 根据邮件id获取邮件内容
-        List<String> mailIdList = coreMailInfoDTOList.stream().map(CoreMailInfoDTO::getId).toList();
+        List<String> mailIdList = readEmailDTOList.stream().map(CoreMailInfoDTO::getId).toList();
+        for (String mailId : mailIdList) {
+            String mid = mailId.split(":")[1];
+            // 获取邮件附件id(一封邮件可能有多个附件)
+            List<CoreMailAttachmentDTO> attachmentDTOList = coreMailApiService.getAttachmentOfMail(token, cookie, mid);
+            if (CollUtil.isEmpty(attachmentDTOList)) {
+                continue;
+            }
+            String emailKey = UUID.randomUUID().toString().replace("-", "");
+            CoreMailInfoDTO coreMailInfoDTO = emailIdInfoMap.get(mailId);
+            List<EmailContentInfoDTO> contentInfoDTOList = CollUtil.newArrayList();
+            // 根据附件id下载附件
+            for (CoreMailAttachmentDTO coreMailAttachmentDTO : attachmentDTOList) {
+                int attachmentId = coreMailAttachmentDTO.getId();
+                String filename = coreMailAttachmentDTO.getFilename();
+                File file = coreMailApiService.getFileFromAttachmentMail(token, cookie, mid, attachmentId, filename);
+                if (file == null) {
+                    continue;
+                }
+                EmailContentInfoDTO contentInfoDTO = new EmailContentInfoDTO();
+                contentInfoDTO.setEmailAddress(account);
+                contentInfoDTO.setEmailTitle(coreMailInfoDTO.getSubject());
+                contentInfoDTO.setEmailDate(DateUtil.format(coreMailInfoDTO.getReceivedDate(), DateConst.YYYY_MM_DD_HH_MM_SS));
+                contentInfoDTO.setFileName(filename);
+                contentInfoDTO.setFilePath(file.getAbsolutePath());
+                contentInfoDTO.setSenderEmail(extractEmail(coreMailInfoDTO.getFrom()));
+                contentInfoDTO.setEmailType(1);
+                contentInfoDTOList.add(contentInfoDTO);
+            }
+            if (CollUtil.isNotEmpty(contentInfoDTOList)) {
+                emailKeyFileMap.put(emailKey, contentInfoDTOList);
+            }
+        }
+        return emailKeyFileMap;
+    }
 
+    private String extractEmail(String address) {
+        if(StrUtil.isEmpty(address)) {
+            return "";
+        }
+        Pattern pattern = Pattern.compile("<([^>]+)>");
+        Matcher matcher = pattern.matcher(address);
+        if (matcher.find()) {
+            return matcher.group(1);
+        } else {
+            return address.trim();
+        }
+    }
 
-        return MapUtil.newHashMap();
+    private boolean filterByEmailDate(Date receivedDate, Date startDate, Date endDate) {
+        return receivedDate != null && receivedDate.after(startDate) && receivedDate.before(endDate);
     }
 
+    private boolean filterBySubject(String subject) {
+        return StrUtil.isNotBlank(subject)
+                && (subject.contains("净值") || subject.contains("业绩报酬") || subject.contains("业绩提成"));
+    }
 }

+ 7 - 7
service-deploy/src/main/resources/application.yml

@@ -27,8 +27,8 @@ spring:
     data-daq:
       # 指定为HikariDataSource
       type: com.zaxxer.hikari.HikariDataSource
-      driver-class-name: com.alipay.oceanbase.obproxy.mysql.jdbc.Driver
-      url: jdbc:oceanbase://127.0.1:3306/competition?serverTimezone=Asia/Shanghai&useLegacyDatetimeCode=false&useSSL=false&nullNamePatternMatchesAll=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
+      driver-class-name: com.mysql.jdbc.Driver
+      url: jdbc:mysql://localhost:3306/competition?serverTimezone=Asia/Shanghai&useLegacyDatetimeCode=false&useSSL=false&nullNamePatternMatchesAll=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
       username: root
       password: Smppw@zj2025
       # hikari连接池配置 对应 HikariConfig 配置属性类
@@ -56,18 +56,18 @@ email-rule:
 
 email:
   file:
-    path: /home/wwwroot/shzq_dataapi/file/nav
+    path: D:\smppw\file\nav\
   parse:
     force-template-enable: false
 
 competition:
   # 大赛id
-  id: 48
-  name: 方华杯私募成长计划(第三届)
+  id: 1
+  name: 中金财富第四届“金选杯”私募鎏光同行计划
   # 输出方式:1-邮箱,2-指定目录.默认1
-  method: 1
+  method: 2
   # 输出目录(仅当 method 为 2 时有效)
-  directory: C:\\Users\\Administrator\\Desktop\\大赛工具需求\\净值采集结果\\
+  directory: D:\smppw\file\nav\output\
 
 # 配置
 simuwang:

+ 9 - 3
service-deploy/src/test/java/com/simuwang/CoreMailTest.java

@@ -8,6 +8,8 @@ import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 
+import java.util.Date;
+
 @SpringBootTest(classes = Application.class)
 public class CoreMailTest {
 
@@ -15,13 +17,17 @@ public class CoreMailTest {
     ReadEmailService readEmailService;
 
     @Test
-    public void testLogin(){
+    public void testRealMail(){
         MailboxInfoDTO mailboxInfoDTO = new MailboxInfoDTO();
         mailboxInfoDTO.setAccount("xi.hu@cs.cicc.com.cn");
         mailboxInfoDTO.setPassword("P_OGTY0J2aH");
-        String startDate = "2025-06-09";
+
+//        mailboxInfoDTO.setAccount("Jinxuancup@cicc.com.cn");
+//        mailboxInfoDTO.setPassword("fk*mYw823v");
+        String startDate = "2025-06-16";
         DateTime date = DateUtil.parse(startDate);
-        readEmailService.readEmail(mailboxInfoDTO, date, date);
+
+        readEmailService.readEmail(mailboxInfoDTO, date, new Date());
     }
 
 }

+ 3 - 3
service-manage/src/main/java/com/simuwang/manage/service/impl/EmailConfigServiceImpl.java

@@ -152,7 +152,7 @@ public class EmailConfigServiceImpl implements EmailConfigService {
         QuartzUtils.deleteScheduleJob(scheduler,mailboxInfoDO.getEmail(),groupName);
         List<Integer> idList = new ArrayList<>();
         idList.add(id);
-        Integer userId = UserUtils.getLoginUser().getUserId();
+        Integer userId = 0;
         emailConfigMapper.deleteEmailConfigByIds(idList,userId);
     }
     @Override
@@ -258,8 +258,8 @@ public class EmailConfigServiceImpl implements EmailConfigService {
         mailboxInfoDO.setProtocol(mailboxInfoVO.getProtocol());
         mailboxInfoDO.setOpenStatus(mailboxInfoVO.getOpenStatus());
         mailboxInfoDO.setUpdateTime(new Date());
-        mailboxInfoDO.setUpdaterId(UserUtils.getLoginUser().getUserId());
-        mailboxInfoDO.setCreatorId(UserUtils.getLoginUser().getUserId());
+         mailboxInfoDO.setUpdaterId(0);
+         mailboxInfoDO.setCreatorId(0);
         return mailboxInfoDO;
     }