فهرست منبع

feat:多份邮件分卷压缩包解压功能(合并完成,解压还是失败,查原因)

wangzaijun 1 ماه پیش
والد
کامیت
d4a5e5f4df

+ 5 - 5
mo-daq/src/main/java/com/smppw/modaq/application/api/ParseApi.java

@@ -32,9 +32,9 @@ public class ParseApi {
         return ResponseEntity.ok("success");
     }
 
-    @GetMapping("reparse")
-    public ResponseEntity<String> reparseReport(Integer emailId) {
-        this.service.reparseEmail(emailId);
-        return ResponseEntity.ok("success");
-    }
+//    @GetMapping("reparse")
+//    public ResponseEntity<String> reparseReport(Integer emailId) {
+//        this.service.reparseEmail(emailId);
+//        return ResponseEntity.ok("success");
+//    }
 }

+ 6 - 6
mo-daq/src/main/java/com/smppw/modaq/application/service/EmailParseApiService.java

@@ -25,12 +25,12 @@ public interface EmailParseApiService {
      */
     void parseEmail(MailboxInfoDTO mailboxInfoDTO, Date startDate, Date endDate, List<String> folderNames, List<Integer> emailTypes);
 
-    /**
-     * 重新解析指定邮件
-     *
-     * @param emailId 邮件id
-     */
-    void reparseEmail(Integer emailId);
+//    /**
+//     * 重新解析指定邮件
+//     *
+//     * @param emailId 邮件id
+//     */
+//    void reparseEmail(Integer emailId);
 //
 //
 //    /**

+ 50 - 63
mo-daq/src/main/java/com/smppw/modaq/application/service/EmailParseApiServiceImpl.java

@@ -2,25 +2,16 @@ package com.smppw.modaq.application.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.smppw.modaq.application.util.EmailUtil;
 import com.smppw.modaq.common.conts.DateConst;
 import com.smppw.modaq.domain.dto.EmailContentInfoDTO;
-import com.smppw.modaq.domain.dto.EmailZipFileDTO;
 import com.smppw.modaq.domain.dto.MailboxInfoDTO;
 import com.smppw.modaq.domain.entity.EmailFileInfoDO;
 import com.smppw.modaq.domain.entity.EmailParseInfoDO;
 import com.smppw.modaq.domain.entity.MailboxInfoDO;
-import com.smppw.modaq.domain.mapper.EmailFileInfoMapper;
-import com.smppw.modaq.domain.mapper.EmailParseInfoMapper;
 import com.smppw.modaq.domain.mapper.MailboxInfoMapper;
 import com.smppw.modaq.domain.service.EmailParseService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 
 import java.io.BufferedReader;
@@ -28,7 +19,6 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.util.Date;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author mozuwen
@@ -38,25 +28,22 @@ import java.util.Map;
 @Service
 public class EmailParseApiServiceImpl implements EmailParseApiService {
 
-    private static final Logger log = LoggerFactory.getLogger(EmailParseApiServiceImpl.class);
+//    private static final Logger log = LoggerFactory.getLogger(EmailParseApiServiceImpl.class);
 
     private final MailboxInfoMapper mailboxInfoMapper;
     private final EmailParseService emailParseService;
-    private final EmailParseInfoMapper emailParseInfoMapper;
-    private final EmailFileInfoMapper emailFileInfoMapper;
-    private final ThreadPoolTaskExecutor asyncExecutor;
+//    private final EmailParseInfoMapper emailParseInfoMapper;
+//    private final EmailFileInfoMapper emailFileInfoMapper;
+//    private final ThreadPoolTaskExecutor asyncExecutor;
 //    private final EmailTaskInfoMapper emailTaskInfoMapper;
 
     public EmailParseApiServiceImpl(MailboxInfoMapper mailboxInfoMapper,
-                                    EmailParseService emailParseService,
-                                    EmailParseInfoMapper emailParseInfoMapper,
-                                    EmailFileInfoMapper emailFileInfoMapper,
-                                    @Qualifier("asyncExecutor") ThreadPoolTaskExecutor asyncExecutor) {
+                                    EmailParseService emailParseService) {
         this.mailboxInfoMapper = mailboxInfoMapper;
         this.emailParseService = emailParseService;
-        this.emailParseInfoMapper = emailParseInfoMapper;
-        this.emailFileInfoMapper = emailFileInfoMapper;
-        this.asyncExecutor = asyncExecutor;
+//        this.emailParseInfoMapper = emailParseInfoMapper;
+//        this.emailFileInfoMapper = emailFileInfoMapper;
+//        this.asyncExecutor = asyncExecutor;
     }
 
     @Override
@@ -134,48 +121,48 @@ public class EmailParseApiServiceImpl implements EmailParseApiService {
 //        return emailTaskInfoDO;
 //    }
 
-    @Override
-    public void reparseEmail(Integer emailId) {
-        // 查询邮件信息
-        EmailParseInfoDO emailParseInfoDO = emailParseInfoMapper.queryById(emailId);
-        if (emailParseInfoDO == null) {
-            log.info("邮件不存在 ->邮件id:{}", emailId);
-            return;
-        }
-        //解析成功的邮件不再解析
-        if (emailParseInfoDO.getParseStatus() == 1) {
-            log.info("邮件解析状态为成功,不再解析 ->邮件id:{}", emailId);
-            return;
-        }
-        List<EmailFileInfoDO> emailFileInfoDOList = emailFileInfoMapper.queryByEmailId(emailId);
-        if (CollUtil.isEmpty(emailFileInfoDOList)) {
-            log.info("该邮件不存在附件 -> 邮件id:{}", emailId);
-            return;
-        }
-//        // 邮件字段识别映射表
-//        Map<String, List<String>> emailFieldMap = emailParseService.getEmailFieldMapping();
-//        // 邮件类型配置
-//        Map<Integer, List<String>> emailTypeMap = emailParseService.getEmailType();
-
-        // 解析流程
-        List<EmailContentInfoDTO> emailContentInfoDTOList = buildEmailContentInfoDTO(emailId, emailParseInfoDO, emailFileInfoDOList);
-
-//        List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
-        Map<EmailContentInfoDTO, List<EmailZipFileDTO>> emailZipFileMap = MapUtil.newHashMap();
-        asyncExecutor.execute(() -> {
-            for (EmailContentInfoDTO emailContentInfoDTO : emailContentInfoDTOList) {
-                try {
-                    List<EmailZipFileDTO> emailZipFiles = emailParseService.parseZipEmail(emailContentInfoDTO);
-                    emailZipFileMap.put(emailContentInfoDTO, emailZipFiles);
-//                    emailFundNavDTOList.addAll(fundNavDTOList);
-                } catch (Exception e) {
-                    log.error("重新解析邮件失败,邮件id:{},堆栈信息:{}", emailId, ExceptionUtil.stacktraceToString(e));
-                }
-            }
-            // 保存相关信息 -> 邮件信息表,邮件文件表,邮件净值表,邮件规模表,基金净值表
-            emailParseService.saveRelatedTable(null, emailParseInfoDO.getEmail(), emailZipFileMap);
-        });
-    }
+//    @Override
+//    public void reparseEmail(Integer emailId) {
+//        // 查询邮件信息
+//        EmailParseInfoDO emailParseInfoDO = emailParseInfoMapper.queryById(emailId);
+//        if (emailParseInfoDO == null) {
+//            log.info("邮件不存在 ->邮件id:{}", emailId);
+//            return;
+//        }
+//        //解析成功的邮件不再解析
+//        if (emailParseInfoDO.getParseStatus() == 1) {
+//            log.info("邮件解析状态为成功,不再解析 ->邮件id:{}", emailId);
+//            return;
+//        }
+//        List<EmailFileInfoDO> emailFileInfoDOList = emailFileInfoMapper.queryByEmailId(emailId);
+//        if (CollUtil.isEmpty(emailFileInfoDOList)) {
+//            log.info("该邮件不存在附件 -> 邮件id:{}", emailId);
+//            return;
+//        }
+////        // 邮件字段识别映射表
+////        Map<String, List<String>> emailFieldMap = emailParseService.getEmailFieldMapping();
+////        // 邮件类型配置
+////        Map<Integer, List<String>> emailTypeMap = emailParseService.getEmailType();
+//
+//        // 解析流程
+//        List<EmailContentInfoDTO> emailContentInfoDTOList = buildEmailContentInfoDTO(emailId, emailParseInfoDO, emailFileInfoDOList);
+//
+////        List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
+//        Map<EmailContentInfoDTO, List<EmailZipFileDTO>> emailZipFileMap = MapUtil.newHashMap();
+//        asyncExecutor.execute(() -> {
+//            for (EmailContentInfoDTO emailContentInfoDTO : emailContentInfoDTOList) {
+//                try {
+//                    List<EmailZipFileDTO> emailZipFiles = emailParseService.parseZipEmail(emailContentInfoDTO);
+//                    emailZipFileMap.put(emailContentInfoDTO, emailZipFiles);
+////                    emailFundNavDTOList.addAll(fundNavDTOList);
+//                } catch (Exception e) {
+//                    log.error("重新解析邮件失败,邮件id:{},堆栈信息:{}", emailId, ExceptionUtil.stacktraceToString(e));
+//                }
+//            }
+//            // 保存相关信息 -> 邮件信息表,邮件文件表,邮件净值表,邮件规模表,基金净值表
+//            emailParseService.saveRelatedTable(null, emailParseInfoDO.getEmail(), emailZipFileMap);
+//        });
+//    }
 //
 //    @Override
 //    public void reparseFile(List<Integer> fileIdList) {

+ 61 - 11
mo-daq/src/main/java/com/smppw/modaq/domain/service/EmailParseService.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import com.smppw.modaq.application.components.ReportParseUtils;
@@ -31,7 +32,6 @@ import com.smppw.modaq.domain.entity.EmailParseInfoDO;
 import com.smppw.modaq.domain.mapper.EmailFileInfoMapper;
 import com.smppw.modaq.domain.mapper.EmailParseInfoMapper;
 import com.smppw.modaq.infrastructure.util.ExcelUtil;
-import com.smppw.modaq.infrastructure.util.FileUtil;
 import jakarta.mail.*;
 import jakarta.mail.internet.MimeUtility;
 import jakarta.mail.search.ComparisonTerm;
@@ -44,8 +44,8 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StopWatch;
 
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.*;
@@ -126,6 +126,50 @@ public class EmailParseService {
                     DateUtil.format(startDate, DateConst.YYYY_MM_DD_HH_MM_SS), DateUtil.format(endDate, DateConst.YYYY_MM_DD_HH_MM_SS));
             return;
         }
+
+        // 有.z01的压缩包说明是单独发送的附件来提供的解密包
+        List<EmailContentInfoDTO> emailFileInfoList = emailContentMap.values().stream().flatMap(Collection::stream).toList();
+        for (Map.Entry<String, List<EmailContentInfoDTO>> next : emailContentMap.entrySet()) {
+            List<EmailContentInfoDTO> dtos = next.getValue();
+            if (CollUtil.isEmpty(dtos) || dtos.size() != 1) {
+                continue;
+            }
+            EmailContentInfoDTO dto = dtos.get(0);
+            if (!"z01".equals(FileUtil.extName(dto.getFileName()))) {
+                continue;
+            }
+            EmailContentInfoDTO zipDto = emailFileInfoList.stream()
+                    .filter(e -> StrUtil.equals(e.getEmailTitle(), dto.getEmailTitle()))
+                    .filter(e -> StrUtil.equals(e.getSenderEmail(), dto.getSenderEmail()))
+                    .filter(e -> StrUtil.equals(e.getEmailAddress(), dto.getEmailAddress()))
+                    .filter(e -> !e.getFileName().equals(dto.getFileName()))
+                    .filter(e -> e.getFileName().startsWith(FileUtil.mainName(dto.getFileName()))).findFirst().orElse(null);
+            if (zipDto == null) {
+                continue;
+            }
+
+            // 分卷合并
+            List<String> volumePath = ListUtil.list(true);
+            volumePath.add(dto.getFilePath());
+            volumePath.add(zipDto.getFilePath());
+            String mergePath = StrUtil.subBefore(zipDto.getFilePath(), ".zip", true) + "_merge.zip";
+            try (OutputStream mergedOutput = new BufferedOutputStream(new FileOutputStream(mergePath))) {
+                for (String volume : volumePath) {
+                    try (InputStream input = new BufferedInputStream(new FileInputStream(volume))) {
+                        byte[] buffer = new byte[1024 * 1024];  // 使用更大的缓冲区
+                        int bytesRead;
+                        while ((bytesRead = input.read(buffer)) != -1) {
+                            mergedOutput.write(buffer, 0, bytesRead);
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                log.error("邮件{} 分卷合并异常:{}", dto.getEmailTitle(), e.getMessage());
+                throw new RuntimeException(e);
+            }
+            FileUtil.rename(FileUtil.file(mergePath), zipDto.getFilePath(), true);
+        }
+
         for (Map.Entry<String, List<EmailContentInfoDTO>> emailEntry : emailContentMap.entrySet()) {
             List<EmailContentInfoDTO> emailContentInfoDTOList = emailEntry.getValue();
             if (CollUtil.isEmpty(emailContentInfoDTOList)) {
@@ -141,7 +185,7 @@ public class EmailParseService {
                     continue;
                 }
                 try {
-                    List<EmailZipFileDTO> fundNavDTOList = parseZipEmail(emailContentInfoDTO);
+                    List<EmailZipFileDTO> fundNavDTOList = this.parseZipEmail(emailContentInfoDTO);
                     emailZipFileMap.put(emailContentInfoDTO, fundNavDTOList);
                 } catch (IOException | ArchiveException e) {
                     log.error("压缩包解压失败:{}", ExceptionUtil.stacktraceToString(e));
@@ -198,16 +242,16 @@ public class EmailParseService {
         }
     }
 
-    public List<EmailZipFileDTO> parseZipEmail(EmailContentInfoDTO emailContentInfoDTO) throws Exception {
+    private List<EmailZipFileDTO> parseZipEmail(EmailContentInfoDTO emailContentInfoDTO) throws Exception {
         List<EmailZipFileDTO> resultList = ListUtil.list(false);
         Integer emailType = emailContentInfoDTO.getEmailType();
         String filepath = emailContentInfoDTO.getFilePath();
         String emailTitle = emailContentInfoDTO.getEmailTitle();
 
         if (ExcelUtil.isZip(filepath)) {
-            handleCompressedFiles(emailTitle, filepath, ".zip", emailType, resultList);
+            this.handleCompressedFiles(emailTitle, filepath, ".zip", emailType, resultList);
         } else if (ExcelUtil.isRAR(filepath)) {
-            handleCompressedFiles(emailTitle, filepath, ".rar", emailType, resultList);
+            this.handleCompressedFiles(emailTitle, filepath, ".rar", emailType, resultList);
         }
 
         // 文件中的类型判断
@@ -701,7 +745,7 @@ public class EmailParseService {
         String emailDateStr = DateUtil.format(sendDate, DateConst.YYYYMMDD);
         String filePath = path + File.separator + account + File.separator + emailDateStr + File.separator;
         String realPath = filePath + emailDate + fileName;
-        File saveFile = cn.hutool.core.io.FileUtil.file(realPath);
+        File saveFile = FileUtil.file(realPath);
         if (!saveFile.exists()) {
             if (!saveFile.getParentFile().exists()) {
                 boolean mkdirs = saveFile.getParentFile().mkdirs();
@@ -709,10 +753,10 @@ public class EmailParseService {
                     log.warn("file path mkdir failed.");
                 }
             }
-            FileUtil.saveFile(saveFile, part);
+            saveFile(saveFile, part);
         } else {
-            cn.hutool.core.io.FileUtil.del(saveFile);
-            FileUtil.saveFile(saveFile, part);
+            FileUtil.del(saveFile);
+            saveFile(saveFile, part);
         }
         EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
         emailContentInfoDTO.setFileName(fileName);
@@ -794,4 +838,10 @@ public class EmailParseService {
             throw new RuntimeException(e);
         }
     }
+
+    private static void saveFile(File saveFile, Part part) throws Exception {
+        try (InputStream is = part.getInputStream()) {
+            Files.copy(is, saveFile.toPath());
+        }
+    }
 }

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

@@ -37,24 +37,24 @@ public class MoDaqApplicationTests {
 
     @Test
     public void reportTest() {
-        MailboxInfoDTO emailInfoDTO = this.buildMailbox("***@simuwang.com", "***");
-        Date startDate = DateUtil.parse("2025-05-15 11:40:00", DateConst.YYYY_MM_DD_HH_MM_SS);
-        Date endDate = DateUtil.parse("2025-05-15 16:53:00", DateConst.YYYY_MM_DD_HH_MM_SS);
+        MailboxInfoDTO emailInfoDTO = this.buildMailbox("**@simuwang.com", "**");
+        Date startDate = DateUtil.parse("2025-05-16 11:35:00", DateConst.YYYY_MM_DD_HH_MM_SS);
+        Date endDate = DateUtil.parse("2025-05-16 11:36:00", DateConst.YYYY_MM_DD_HH_MM_SS);
         try {
             List<String> folderNames = ListUtil.list(false);
 //            folderNames.add("其他文件夹/报告公告");
             folderNames.add("INBOX");
             emailParseService.parseEmail(emailInfoDTO, startDate, endDate,
-                    folderNames, EmailTypeConst.REPORT_EMAIL_TYPES);
+                    folderNames, ListUtil.toList(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE));
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
-    @Test
-    public void testReparseEmail() {
-        emailParseApiService.reparseEmail(23);
-    }
+//    @Test
+//    public void testReparseEmail() {
+//        emailParseApiService.reparseEmail(23);
+//    }
 
 
     private MailboxInfoDTO buildMailbox(String account, String pwd) {