wangzaijun 1 tháng trước cách đây
mục cha
commit
759cccdfa8

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

@@ -12,7 +12,7 @@ import com.smppw.modaq.common.exception.ReportParseException;
 import com.smppw.modaq.domain.dto.report.*;
 import com.smppw.modaq.domain.dto.report.*;
 import com.smppw.modaq.domain.entity.EmailFieldMappingDO;
 import com.smppw.modaq.domain.entity.EmailFieldMappingDO;
 import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
 import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
-import com.smppw.modaq.infrastructure.util.DateUtil;
+import com.smppw.modaq.infrastructure.util.DateUtils;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 
 
@@ -171,7 +171,7 @@ public abstract class AbstractReportParser<T extends ReportData> implements Repo
         reportInfo.setReportName(reportName);
         reportInfo.setReportName(reportName);
         reportInfo.setReportType(params.getReportType().name());
         reportInfo.setReportType(params.getReportType().name());
         String reportDate = ReportParseUtils.matchReportDate(params.getReportType(), reportName);
         String reportDate = ReportParseUtils.matchReportDate(params.getReportType(), reportName);
-        reportInfo.setReportDate(DateUtil.toDate(reportDate));
+        reportInfo.setReportDate(DateUtils.toDate(reportDate));
         return reportInfo;
         return reportInfo;
     }
     }
 }
 }

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

@@ -214,8 +214,8 @@ public class EmailParseApiServiceImpl implements EmailParseApiService {
 //    }
 //    }
 
 
 //    private EmailContentInfoDTO buildEmailContentInfoDTO(EmailInfoDTO emailInfoDTO) {
 //    private EmailContentInfoDTO buildEmailContentInfoDTO(EmailInfoDTO emailInfoDTO) {
-//        String emailDate = DateUtil.format(emailInfoDTO.getEmailDate(), DateConst.YYYY_MM_DD_HH_MM_SS);
-//        String parseDate = DateUtil.format(new Date(), DateConst.YYYY_MM_DD_HH_MM_SS);
+//        String emailDate = DateUtils.format(emailInfoDTO.getEmailDate(), DateConst.YYYY_MM_DD_HH_MM_SS);
+//        String parseDate = DateUtils.format(new Date(), DateConst.YYYY_MM_DD_HH_MM_SS);
 //        EmailContentInfoDTO contentInfoDTO = new EmailContentInfoDTO();
 //        EmailContentInfoDTO contentInfoDTO = new EmailContentInfoDTO();
 //        contentInfoDTO.setEmailId(emailInfoDTO.getId());
 //        contentInfoDTO.setEmailId(emailInfoDTO.getId());
 //        contentInfoDTO.setFileId(emailInfoDTO.getFileId());
 //        contentInfoDTO.setFileId(emailInfoDTO.getFileId());

+ 4 - 4
mo-daq/src/main/java/com/smppw/modaq/application/task/ParseSchedulerTask.java

@@ -34,8 +34,8 @@ public class ParseSchedulerTask {
 //        try {
 //        try {
 //            // 定期报告从 其他文件夹/报告公告 文件夹获取邮件
 //            // 定期报告从 其他文件夹/报告公告 文件夹获取邮件
 //            this.emailParseApiService.parseEmail(
 //            this.emailParseApiService.parseEmail(
-//                    DateUtil.parseDateTime("2025-05-15 11:15:00"),
-//                    DateUtil.parseDateTime("2025-05-15 15:25:00"),
+//                    DateUtils.parseDateTime("2025-05-15 11:15:00"),
+//                    DateUtils.parseDateTime("2025-05-15 15:25:00"),
 //                    ListUtil.of("其他文件夹/报告公告"), EmailTypeConst.REPORT_EMAIL_TYPES);
 //                    ListUtil.of("其他文件夹/报告公告"), EmailTypeConst.REPORT_EMAIL_TYPES);
 //        } catch (Exception e) {
 //        } catch (Exception e) {
 //            logger.error(ExceptionUtil.getMessage(e));
 //            logger.error(ExceptionUtil.getMessage(e));
@@ -44,8 +44,8 @@ public class ParseSchedulerTask {
 //        try {
 //        try {
 //            // 确认函从 INBOX 文件夹获取邮件
 //            // 确认函从 INBOX 文件夹获取邮件
 //            this.emailParseApiService.parseEmail(
 //            this.emailParseApiService.parseEmail(
-//                    DateUtil.parseDateTime("2025-05-15 11:44:00"),
-//                    DateUtil.parseDateTime("2025-05-15 16:32:00"),
+//                    DateUtils.parseDateTime("2025-05-15 11:44:00"),
+//                    DateUtils.parseDateTime("2025-05-15 16:32:00"),
 //                    null, ListUtil.of(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE));
 //                    null, ListUtil.of(EmailTypeConst.REPORT_LETTER_EMAIL_TYPE));
 //        } catch (Exception e) {
 //        } catch (Exception e) {
 //            logger.error(ExceptionUtil.getMessage(e));
 //            logger.error(ExceptionUtil.getMessage(e));

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

@@ -74,8 +74,8 @@ public class EmailUtil {
 //    public static List<EmailContentInfoDTO> collectMimeMultipart(Message message, String emailAddress, String path) throws Exception {
 //    public static List<EmailContentInfoDTO> collectMimeMultipart(Message message, String emailAddress, String path) throws Exception {
 //        List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
 //        List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
 //        String emailTitle = message.getSubject();
 //        String emailTitle = message.getSubject();
-//        String emailDate = DateUtil.format(message.getSentDate(), DateConst.YYYYMMDDHHMMSS24);
-//        String emailDateStr = DateUtil.format(message.getSentDate(), DateConst.YYYYMMDD);
+//        String emailDate = DateUtils.format(message.getSentDate(), DateConst.YYYYMMDDHHMMSS24);
+//        String emailDateStr = DateUtils.format(message.getSentDate(), DateConst.YYYYMMDD);
 //        String filePath = path + File.separator + emailAddress + File.separator + emailDateStr + File.separator;
 //        String filePath = path + File.separator + emailAddress + File.separator + emailDateStr + File.separator;
 //
 //
 //        MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
 //        MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
@@ -139,7 +139,7 @@ public class EmailUtil {
 //            }
 //            }
 //            emailContentInfoDTO.setEmailAddress(emailAddress);
 //            emailContentInfoDTO.setEmailAddress(emailAddress);
 //            emailContentInfoDTO.setEmailTitle(emailTitle);
 //            emailContentInfoDTO.setEmailTitle(emailTitle);
-//            emailContentInfoDTO.setEmailDate(DateUtil.format(message.getSentDate(), DateConst.YYYY_MM_DD_HH_MM_SS));
+//            emailContentInfoDTO.setEmailDate(DateUtils.format(message.getSentDate(), DateConst.YYYY_MM_DD_HH_MM_SS));
 //            emailContentInfoDTOList.add(emailContentInfoDTO);
 //            emailContentInfoDTOList.add(emailContentInfoDTO);
 //        }
 //        }
 //
 //

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

@@ -1,7 +1,7 @@
 package com.smppw.modaq.domain.dto.report;
 package com.smppw.modaq.domain.dto.report;
 
 
 import com.smppw.modaq.domain.entity.report.ReportFundInfoDO;
 import com.smppw.modaq.domain.entity.report.ReportFundInfoDO;
-import com.smppw.modaq.infrastructure.util.DateUtil;
+import com.smppw.modaq.infrastructure.util.DateUtils;
 import lombok.Getter;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.Setter;
 
 
@@ -117,7 +117,7 @@ public class ReportFundInfoDTO extends BaseReportDTO<ReportFundInfoDO> {
         entity.setFundManager(this.fundManager);
         entity.setFundManager(this.fundManager);
         entity.setFundName(this.fundName);
         entity.setFundName(this.fundName);
         entity.setFundStrategyDescription(this.fundStrategyDescription);
         entity.setFundStrategyDescription(this.fundStrategyDescription);
-        entity.setInceptionDate(DateUtil.toDate(this.inceptionDate));
+        entity.setInceptionDate(DateUtils.toDate(this.inceptionDate));
         entity.setIndustryTrend(this.industryTrend);
         entity.setIndustryTrend(this.industryTrend);
         entity.setInvestmentObjective(this.investmentObjective);
         entity.setInvestmentObjective(this.investmentObjective);
         entity.setLeverage(this.toBigDecimal(this.leverage));
         entity.setLeverage(this.toBigDecimal(this.leverage));
@@ -126,7 +126,7 @@ public class ReportFundInfoDTO extends BaseReportDTO<ReportFundInfoDO> {
         entity.setRegisterNumber(this.registerNumber);
         entity.setRegisterNumber(this.registerNumber);
         entity.setRiskReturnDesc(this.riskReturnDesc);
         entity.setRiskReturnDesc(this.riskReturnDesc);
         entity.setSecondaryBenchmark(this.secondaryBenchmark);
         entity.setSecondaryBenchmark(this.secondaryBenchmark);
-        entity.setDueDate(DateUtil.toDate(this.dueDate));
+        entity.setDueDate(DateUtils.toDate(this.dueDate));
         entity.setReviewed(Objects.equals("是", this.isReviewed) ? 1 : 0);
         entity.setReviewed(Objects.equals("是", this.isReviewed) ? 1 : 0);
         this.initEntity(entity);
         this.initEntity(entity);
         return entity;
         return entity;

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

@@ -1,7 +1,7 @@
 package com.smppw.modaq.domain.dto.report;
 package com.smppw.modaq.domain.dto.report;
 
 
 import com.smppw.modaq.domain.entity.report.ReportFundTransactionDO;
 import com.smppw.modaq.domain.entity.report.ReportFundTransactionDO;
-import com.smppw.modaq.infrastructure.util.DateUtil;
+import com.smppw.modaq.infrastructure.util.DateUtils;
 import lombok.Getter;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.Setter;
 
 
@@ -249,8 +249,8 @@ public class ReportFundTransactionDTO extends BaseReportDTO<ReportFundTransactio
         entity.setTransactionType(transactionType);
         entity.setTransactionType(transactionType);
         entity.setBusinessReason(businessReason);
         entity.setBusinessReason(businessReason);
         entity.setStatus(status);
         entity.setStatus(status);
-        entity.setHoldingDate(DateUtil.toDate(holdingDate));
-        entity.setApplyDate(DateUtil.toDate(applyDate));
+        entity.setHoldingDate(DateUtils.toDate(holdingDate));
+        entity.setApplyDate(DateUtils.toDate(applyDate));
         entity.setApplyAmount(this.toBigDecimal(applyAmount));
         entity.setApplyAmount(this.toBigDecimal(applyAmount));
         entity.setApplyShare(this.toBigDecimal(applyShare));
         entity.setApplyShare(this.toBigDecimal(applyShare));
         entity.setAmount(this.toBigDecimal(amount, BigDecimal.ZERO));
         entity.setAmount(this.toBigDecimal(amount, BigDecimal.ZERO));
@@ -266,7 +266,7 @@ public class ReportFundTransactionDTO extends BaseReportDTO<ReportFundTransactio
         entity.setLargeRedemptionType(largeRedemptionType);
         entity.setLargeRedemptionType(largeRedemptionType);
         entity.setRewardMark(rewardMark);
         entity.setRewardMark(rewardMark);
         entity.setHoldingDays(holdingDays);
         entity.setHoldingDays(holdingDays);
-        entity.setShareRegistryDate(DateUtil.toDate(shareRegistryDate));
+        entity.setShareRegistryDate(DateUtils.toDate(shareRegistryDate));
         // --- 以下是费用
         // --- 以下是费用
         entity.setFee(this.toBigDecimal(fee));
         entity.setFee(this.toBigDecimal(fee));
         entity.setInterest(this.toBigDecimal(interest));
         entity.setInterest(this.toBigDecimal(interest));
@@ -278,8 +278,8 @@ public class ReportFundTransactionDTO extends BaseReportDTO<ReportFundTransactio
         entity.setPerformanceFeeDiscounts(this.toBigDecimal(performanceFeeDiscounts));
         entity.setPerformanceFeeDiscounts(this.toBigDecimal(performanceFeeDiscounts));
         // --- 以下是分红
         // --- 以下是分红
         entity.setDividendType(dividendType);
         entity.setDividendType(dividendType);
-        entity.setDividendRegistryDate(DateUtil.toDate(dividendRegistryDate));
-        entity.setDividendPaymentDate(DateUtil.toDate(dividendPaymentDate));
+        entity.setDividendRegistryDate(DateUtils.toDate(dividendRegistryDate));
+        entity.setDividendPaymentDate(DateUtils.toDate(dividendPaymentDate));
         entity.setBaseShareDividend(this.toBigDecimal(baseShareDividend));
         entity.setBaseShareDividend(this.toBigDecimal(baseShareDividend));
         entity.setDividendMode(dividendMode);
         entity.setDividendMode(dividendMode);
         entity.setUnitDividend(this.toBigDecimal(unitDividend));
         entity.setUnitDividend(this.toBigDecimal(unitDividend));

+ 2 - 2
mo-daq/src/main/java/com/smppw/modaq/domain/dto/report/ReportNetReportDTO.java

@@ -1,7 +1,7 @@
 package com.smppw.modaq.domain.dto.report;
 package com.smppw.modaq.domain.dto.report;
 
 
 import com.smppw.modaq.domain.entity.report.ReportNetReportDO;
 import com.smppw.modaq.domain.entity.report.ReportNetReportDO;
-import com.smppw.modaq.infrastructure.util.DateUtil;
+import com.smppw.modaq.infrastructure.util.DateUtils;
 import lombok.Getter;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.Setter;
 
 
@@ -51,7 +51,7 @@ public class ReportNetReportDTO extends BaseReportLevelDTO<ReportNetReportDO> {
         ReportNetReportDO entity = new ReportNetReportDO();
         ReportNetReportDO entity = new ReportNetReportDO();
         entity.setFileId(this.getFileId());
         entity.setFileId(this.getFileId());
         entity.setLevel(this.getLevel());
         entity.setLevel(this.getLevel());
-        entity.setValuationDate(DateUtil.toDate(this.valuationDate));
+        entity.setValuationDate(DateUtils.toDate(this.valuationDate));
         entity.setCumulativeNav(this.toBigDecimal(this.cumulativeNavWithdrawal));
         entity.setCumulativeNav(this.toBigDecimal(this.cumulativeNavWithdrawal));
         entity.setEndTotalShares(this.toBigDecimal(this.assetShare));
         entity.setEndTotalShares(this.toBigDecimal(this.assetShare));
         entity.setFundAssetSize(this.toBigDecimal(this.assetNet));
         entity.setFundAssetSize(this.toBigDecimal(this.assetNet));

+ 52 - 37
mo-daq/src/main/java/com/smppw/modaq/domain/service/EmailParseService.java

@@ -31,14 +31,13 @@ import com.smppw.modaq.domain.entity.EmailFileInfoDO;
 import com.smppw.modaq.domain.entity.EmailParseInfoDO;
 import com.smppw.modaq.domain.entity.EmailParseInfoDO;
 import com.smppw.modaq.domain.mapper.EmailFileInfoMapper;
 import com.smppw.modaq.domain.mapper.EmailFileInfoMapper;
 import com.smppw.modaq.domain.mapper.EmailParseInfoMapper;
 import com.smppw.modaq.domain.mapper.EmailParseInfoMapper;
-import com.smppw.modaq.infrastructure.util.ExcelUtil;
+import com.smppw.modaq.infrastructure.util.ArchiveUtil;
 import com.smppw.modaq.infrastructure.util.PdfUtil;
 import com.smppw.modaq.infrastructure.util.PdfUtil;
 import jakarta.mail.*;
 import jakarta.mail.*;
 import jakarta.mail.internet.MimeUtility;
 import jakarta.mail.internet.MimeUtility;
 import jakarta.mail.search.ComparisonTerm;
 import jakarta.mail.search.ComparisonTerm;
 import jakarta.mail.search.ReceivedDateTerm;
 import jakarta.mail.search.ReceivedDateTerm;
 import jakarta.mail.search.SearchTerm;
 import jakarta.mail.search.SearchTerm;
-import org.apache.commons.compress.archivers.ArchiveException;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
@@ -144,9 +143,9 @@ public class EmailParseService {
                     continue;
                     continue;
                 }
                 }
                 try {
                 try {
-                    List<EmailZipFileDTO> fundNavDTOList = parseZipEmail(emailContentInfoDTO);
+                    List<EmailZipFileDTO> fundNavDTOList = this.parseZipEmail(emailContentInfoDTO);
                     emailZipFileMap.put(emailContentInfoDTO, fundNavDTOList);
                     emailZipFileMap.put(emailContentInfoDTO, fundNavDTOList);
-                } catch (IOException | ArchiveException e) {
+                } catch (IOException e) {
                     log.error("压缩包解压失败:{}", ExceptionUtil.stacktraceToString(e));
                     log.error("压缩包解压失败:{}", ExceptionUtil.stacktraceToString(e));
                     EmailParseInfoDO fail = buildEmailParseInfo(null, mailboxInfoDTO.getAccount(), emailContentInfoDTO);
                     EmailParseInfoDO fail = buildEmailParseInfo(null, mailboxInfoDTO.getAccount(), emailContentInfoDTO);
                     fail.setFailReason("压缩包解压失败");
                     fail.setFailReason("压缩包解压失败");
@@ -201,15 +200,15 @@ public class EmailParseService {
         }
         }
     }
     }
 
 
-    public List<EmailZipFileDTO> parseZipEmail(EmailContentInfoDTO emailContentInfoDTO) throws Exception {
+    public List<EmailZipFileDTO> parseZipEmail(EmailContentInfoDTO emailContentInfoDTO) throws IOException {
         List<EmailZipFileDTO> resultList = ListUtil.list(false);
         List<EmailZipFileDTO> resultList = ListUtil.list(false);
         Integer emailType = emailContentInfoDTO.getEmailType();
         Integer emailType = emailContentInfoDTO.getEmailType();
         String filepath = emailContentInfoDTO.getFilePath();
         String filepath = emailContentInfoDTO.getFilePath();
         String emailTitle = emailContentInfoDTO.getEmailTitle();
         String emailTitle = emailContentInfoDTO.getEmailTitle();
 
 
-        if (ExcelUtil.isZip(filepath)) {
+        if (ArchiveUtil.isZip(filepath)) {
             handleCompressedFiles(emailTitle, filepath, ".zip", emailType, resultList);
             handleCompressedFiles(emailTitle, filepath, ".zip", emailType, resultList);
-        } else if (ExcelUtil.isRAR(filepath)) {
+        } else if (ArchiveUtil.isRAR(filepath)) {
             handleCompressedFiles(emailTitle, filepath, ".rar", emailType, resultList);
             handleCompressedFiles(emailTitle, filepath, ".rar", emailType, resultList);
         }
         }
 
 
@@ -227,7 +226,7 @@ public class EmailParseService {
     }
     }
 
 
     private void handleCompressedFiles(String emailTitle, String filepath, String extension,
     private void handleCompressedFiles(String emailTitle, String filepath, String extension,
-                                       Integer emailType, List<EmailZipFileDTO> resultList) throws Exception {
+                                       Integer emailType, List<EmailZipFileDTO> resultList) throws IOException {
         String destPath = getDestinationPath(filepath, extension);
         String destPath = getDestinationPath(filepath, extension);
 
 
         File destFile = new File(destPath);
         File destFile = new File(destPath);
@@ -238,10 +237,10 @@ public class EmailParseService {
         }
         }
 
 
         List<String> extractedDirs;
         List<String> extractedDirs;
-        if (ExcelUtil.isZip(filepath)) {
-            extractedDirs = ExcelUtil.extractCompressedFiles(filepath, destPath);
-        } else if (ExcelUtil.isRAR(filepath)) {
-            extractedDirs = ExcelUtil.extractRar5(filepath, destPath);
+        if (ArchiveUtil.isZip(filepath)) {
+            extractedDirs = ArchiveUtil.extractCompressedFiles(filepath, destPath);
+        } else if (ArchiveUtil.isRAR(filepath)) {
+            extractedDirs = ArchiveUtil.extractRar5(filepath, destPath);
         } else {
         } else {
             return;
             return;
         }
         }
@@ -410,17 +409,21 @@ public class EmailParseService {
         Integer fileId = emailFileInfo.getId();
         Integer fileId = emailFileInfo.getId();
         String aiFileId = emailFileInfo.getAiFileId();
         String aiFileId = emailFileInfo.getAiFileId();
 
 
-//        // 首页和尾页转为png图片,首页用来识别基金名称和基金代码、尾页用来识别印章和联系人 todo
-//        List<String> images = null;
-//        try {
-//            String output = FileUtil.getParent(filepath, 1) + File.separator + "image";
-//            images = PdfUtil.convertFirstAndLastPagesToPng(filepath, FileUtil.file(output), 300);
-//            if (log.isInfoEnabled()) {
-//                log.info("报告[{}] 生成的图片地址是:{}", fileName, images);
-//            }
-//        } catch (Exception e) {
-//            log.warn("报告[{}] 生成图片失败:{}", fileName, ExceptionUtil.stacktraceToString(e));
-//        }
+        // 首页和尾页转为png图片,首页用来识别基金名称和基金代码、尾页用来识别印章和联系人
+        List<String> images = null;
+        try {
+            String output = FileUtil.getParent(filepath, 1) + File.separator + "image";
+            images = PdfUtil.convertFirstAndLastPagesToPng(filepath, FileUtil.file(output), 300);
+            if (log.isInfoEnabled()) {
+                log.info("报告[{}] 生成的图片地址是:{}", fileName, images);
+            }
+        } catch (Exception e) {
+            log.warn("报告[{}] 生成图片失败:{}", fileName, ExceptionUtil.stacktraceToString(e));
+        }
+        // todo 用图片来解析报告印章和联系人
+        if (CollUtil.isNotEmpty(images)) {
+            // do something ...
+        }
 
 
         // 不支持解析的格式文件
         // 不支持解析的格式文件
         boolean notSupportFile = false;
         boolean notSupportFile = false;
@@ -492,22 +495,34 @@ public class EmailParseService {
             }
             }
         }
         }
         // 保存报告解析结果
         // 保存报告解析结果
-        if (reportData != null) {
-            StopWatch writeWatch = new StopWatch();
-            writeWatch.start();
-            try {
-                ReportWriter<ReportData> instance = this.reportWriterFactory.getInstance(reportType);
-                instance.write(reportData);
-            } catch (Exception e) {
-                log.error("报告{}结果保存失败\n{}", fileName, ExceptionUtil.stacktraceToString(e));
-            } finally {
-                writeWatch.stop();
-                if (log.isInfoEnabled()) {
-                    log.info("报告{}解析结果保存完成,耗时{}ms", fileName, writeWatch.getTotalTimeMillis());
-                }
+        this.saveReportData(reportData, reportType, fileName);
+        return result;
+    }
+
+    /**
+     * 保存报告解析结果
+     *
+     * @param reportData 报告解析结果
+     * @param reportType 报告类型
+     * @param fileName   报告名称
+     */
+    private void saveReportData(ReportData reportData, ReportType reportType, String fileName) {
+        if (reportData == null) {
+            return;
+        }
+        StopWatch writeWatch = new StopWatch();
+        writeWatch.start();
+        try {
+            ReportWriter<ReportData> instance = this.reportWriterFactory.getInstance(reportType);
+            instance.write(reportData);
+        } catch (Exception e) {
+            log.error("报告{}结果保存失败\n{}", fileName, ExceptionUtil.stacktraceToString(e));
+        } finally {
+            writeWatch.stop();
+            if (log.isInfoEnabled()) {
+                log.info("报告{}解析结果保存完成,耗时{}ms", fileName, writeWatch.getTotalTimeMillis());
             }
             }
         }
         }
-        return result;
     }
     }
 
 
     private EmailFileInfoDO saveEmailFileInfo(Integer emailId, String fileName, String filePath) {
     private EmailFileInfoDO saveEmailFileInfo(Integer emailId, String fileName, String filePath) {

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

@@ -4,26 +4,28 @@ import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import com.smppw.modaq.common.enums.ReportType;
 import com.smppw.modaq.common.enums.ReportType;
-import net.sf.sevenzipjbinding.ExtractOperationResult;
-import net.sf.sevenzipjbinding.IInArchive;
-import net.sf.sevenzipjbinding.SevenZip;
-import net.sf.sevenzipjbinding.SevenZipException;
+import net.sf.sevenzipjbinding.*;
 import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
 import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
 import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
 import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
 import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
 import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
 import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.ArchiveEntry;
-import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.io.IOUtils;
 
 
 import java.io.*;
 import java.io.*;
 import java.nio.charset.Charset;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.List;
 import java.util.regex.Pattern;
 import java.util.regex.Pattern;
 
 
-public class ExcelUtil {
+public class ArchiveUtil {
     // 候选编码列表(按常见顺序排列)
     // 候选编码列表(按常见顺序排列)
     private static final List<String> CANDIDATE_ENCODINGS = Arrays.asList(
     private static final List<String> CANDIDATE_ENCODINGS = Arrays.asList(
             "GBK",      // 中文环境常用
             "GBK",      // 中文环境常用
@@ -50,27 +52,15 @@ public class ExcelUtil {
             {0x3000, 0x303F}     // 常用标点
             {0x3000, 0x303F}     // 常用标点
     };
     };
 
 
-    public static boolean isExcel(String fileName) {
-        return StrUtil.isNotBlank(fileName) && (fileName.endsWith("xls") || fileName.endsWith("xlsx") || fileName.endsWith("XLS") || fileName.endsWith("XLSX"));
-    }
-
-    public static boolean isPdf(String fileName) {
-        return StrUtil.isNotBlank(fileName) && (fileName.endsWith("pdf") || fileName.endsWith("PDF"));
-    }
-
     public static boolean isZip(String fileName) {
     public static boolean isZip(String fileName) {
         return StrUtil.isNotBlank(fileName) && (fileName.endsWith("zip") || fileName.endsWith("ZIP"));
         return StrUtil.isNotBlank(fileName) && (fileName.endsWith("zip") || fileName.endsWith("ZIP"));
     }
     }
 
 
-    public static boolean isHTML(String fileName) {
-        return StrUtil.isNotBlank(fileName) && fileName.endsWith("html");
-    }
-
     public static boolean isRAR(String fileName) {
     public static boolean isRAR(String fileName) {
         return StrUtil.isNotBlank(fileName) && (fileName.endsWith("rar") || fileName.endsWith("RAR"));
         return StrUtil.isNotBlank(fileName) && (fileName.endsWith("rar") || fileName.endsWith("RAR"));
     }
     }
 
 
-    public static List<String> extractCompressedFiles(String zipFilePath, String destFilePath) throws IOException, ArchiveException {
+    public static List<String> extractCompressedFiles(String zipFilePath, String destFilePath) throws IOException {
         File destFile = FileUtil.file(destFilePath);
         File destFile = FileUtil.file(destFilePath);
         if (!destFile.exists()) {
         if (!destFile.exists()) {
             Files.createDirectories(destFile.toPath());
             Files.createDirectories(destFile.toPath());
@@ -81,12 +71,15 @@ public class ExcelUtil {
             encoding = "GBK";
             encoding = "GBK";
         }
         }
 
 
-        return ZipUtil.decompressZip(zipFilePath, destFilePath, 2, encoding);
+        return decompressZip(zipFilePath, destFilePath, 2, encoding);
     }
     }
 
 
-    public static List<String> extractRar5(String rarFilePath, String outputDir) throws Exception {
-        // 初始化 SevenZipJBinding 本地库
-        SevenZip.initSevenZipFromPlatformJAR();
+    public static List<String> extractRar5(String rarFilePath, String outputDir) throws IOException {
+        try {
+            // 初始化 SevenZipJBinding 本地库
+            SevenZip.initSevenZipFromPlatformJAR();
+        } catch (SevenZipNativeInitializationException ignored) {
+        }
 
 
         RandomAccessFile randomAccessFile = null;
         RandomAccessFile randomAccessFile = null;
         IInArchive inArchive = null;
         IInArchive inArchive = null;
@@ -250,6 +243,114 @@ public class ExcelUtil {
         return true;
         return true;
     }
     }
 
 
+    /**
+     * 递归解压 ZIP 文件(含嵌套深度限制)
+     *
+     * @param zipFile   输入的 ZIP 文件
+     * @param outputDir 解压目标根目录
+     * @param maxDepth  最大嵌套深度(例如 3 表示允许 parent/nest1/nest2.zip)
+     * @return 所有解压后的文件路径(格式:parent.zip/nest1/file.txt)
+     */
+    public static List<String> decompressZip(String zipFile, String outputDir, int maxDepth, String encoding) throws IOException {
+        return decompressZip(FileUtil.file(zipFile), FileUtil.file(outputDir), maxDepth, encoding);
+    }
+
+    /**
+     * 递归解压 ZIP 文件(含嵌套深度限制)
+     *
+     * @param zipFile   输入的 ZIP 文件
+     * @param outputDir 解压目标根目录
+     * @param maxDepth  最大嵌套深度(例如 3 表示允许 parent.zip/nest1.zip/nest2.zip)
+     * @return 所有解压后的文件路径(格式:parent.zip/nest1.zip/file.txt)
+     */
+    public static List<String> decompressZip(File zipFile, File outputDir, int maxDepth, String encoding) throws IOException {
+        if (maxDepth < 0) {
+            throw new IllegalArgumentException("最大嵌套深度不能小于 0");
+        }
+        List<String> decompressedFiles = ListUtil.list(false);
+        decompressZipRecursive(zipFile, outputDir, "", 0, maxDepth, encoding, decompressedFiles);
+        return decompressedFiles;
+    }
+
+    /**
+     * 递归解压核心逻辑
+     */
+    private static void decompressZipRecursive(
+            File currentZip,
+            File rootOutputDir,
+            String nestedPath,
+            int currentDepth,
+            int maxDepth,
+            String encoding,
+            List<String> decompressedFiles) throws IOException {
+
+        // 1. 超过最大深度时停止处理嵌套 ZIP
+        if (currentDepth > maxDepth) {
+            return;
+        }
+
+        // 2. 创建当前 ZIP 的解压目录(跟压缩包目录已经处理过,就不要追加到文件目录中了)
+        String currentZipName = FileUtil.mainName(currentZip);
+        String currentNestedPath = nestedPath.isEmpty()
+                ? ""
+                : nestedPath + File.separator + currentZipName;
+        File currentOutputDir = new File(rootOutputDir, currentNestedPath);
+        FileUtil.mkdir(currentOutputDir);
+
+        // 3. 解压当前 ZIP,支持最多10个分卷的解压
+        try (ZipFile zip = ZipFile.builder().setFile(currentZip).setCharset(encoding).setMaxNumberOfDisks(10).get()) {
+            Enumeration<ZipArchiveEntry> entries = zip.getEntries();
+
+            while (entries.hasMoreElements()) {
+                ZipArchiveEntry entry = entries.nextElement();
+                String name = entry.getName();
+                if (name.startsWith("__MACOSX/")) {
+                    continue;
+                }
+
+                Path entryPath = Paths.get(currentOutputDir.getAbsolutePath(), name);
+
+                // 处理目录
+                if (entry.isDirectory()) {
+                    Files.createDirectories(entryPath);
+                    continue;
+                }
+
+                // 写入文件
+                Files.createDirectories(entryPath.getParent());
+                try (InputStream is = zip.getInputStream(entry);
+                     OutputStream os = new FileOutputStream(entryPath.toFile())) {
+                    IOUtils.copy(is, os);
+                }
+
+                // 4. 递归处理嵌套 ZIP(深度+1)
+                if (isZipFile(name) && currentDepth < maxDepth) {
+                    File nestedZipFile = entryPath.toFile();
+                    decompressZipRecursive(
+                            nestedZipFile,
+                            rootOutputDir,
+                            currentNestedPath,
+                            currentDepth + 1,  // 深度递增
+                            maxDepth,
+                            encoding,
+                            decompressedFiles
+                    );
+                    Files.delete(nestedZipFile.toPath());
+                } else {
+                    // 记录路径
+                    decompressedFiles.add(entryPath.toString());
+                }
+            }
+        }
+    }
+
+    /**
+     * 判断文件是否为 ZIP 格式
+     */
+    private static boolean isZipFile(String filepath) {
+        return filepath.toLowerCase().endsWith(".zip");
+    }
+
     public static void main(String[] args) throws Exception {
     public static void main(String[] args) throws Exception {
         String zipFilePath = "D:\\home\\wwwroot\\mo_report_file\\wangzaijun@simuwang.com\\20250321\\20250321143709排排网确认单.rar";
         String zipFilePath = "D:\\home\\wwwroot\\mo_report_file\\wangzaijun@simuwang.com\\20250321\\20250321143709排排网确认单.rar";
         String destFilePath = "D:\\home\\wwwroot\\mo_report_file\\wangzaijun@simuwang.com\\20250321";
         String destFilePath = "D:\\home\\wwwroot\\mo_report_file\\wangzaijun@simuwang.com\\20250321";
@@ -261,5 +362,10 @@ public class ExcelUtil {
 //        for (String s : fileList) {
 //        for (String s : fileList) {
 //            System.out.println(s);
 //            System.out.println(s);
 //        }
 //        }
+
+        String currentZip = "D:\\Documents\\新报告解析\\确认单\\20250514_份额及交易确认函_上海量魁私募基金管理有限公司_深圳市前海排排网基金销售有限责任公司_TA确认数据.zip";
+        List<String> files = decompressZip(currentZip, "D:\\Documents\\新报告解析\\确认单\\", 2, "utf-8");
+        System.out.println("解压后的文件路径:");
+        files.forEach(System.out::println);
     }
     }
 }
 }

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

@@ -1,12 +1,13 @@
 package com.smppw.modaq.infrastructure.util;
 package com.smppw.modaq.infrastructure.util;
 
 
 import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import com.smppw.modaq.common.conts.DateConst;
 import com.smppw.modaq.common.conts.DateConst;
 
 
 import java.util.Date;
 import java.util.Date;
 
 
-public class DateUtil {
+public class DateUtils {
     /**
     /**
      * 字符串转日期类型
      * 字符串转日期类型
      *
      *
@@ -21,7 +22,7 @@ public class DateUtil {
         input = input.replaceAll("-_/", "");
         input = input.replaceAll("-_/", "");
         try {
         try {
             // 日期格式化,支持格式如下:yyyy年MM月dd日、yyyy-MM-dd、yyyy/MM/dd、yyyy_MM_dd和yyyyMMdd
             // 日期格式化,支持格式如下:yyyy年MM月dd日、yyyy-MM-dd、yyyy/MM/dd、yyyy_MM_dd和yyyyMMdd
-            return cn.hutool.core.date.DateUtil.parse(input.trim(),
+            return DateUtil.parse(input.trim(),
                     DatePattern.CHINESE_DATE_PATTERN,
                     DatePattern.CHINESE_DATE_PATTERN,
                     DateConst.YYYYMMDD,
                     DateConst.YYYYMMDD,
                     DatePattern.NORM_DATE_PATTERN,
                     DatePattern.NORM_DATE_PATTERN,

+ 0 - 141
mo-daq/src/main/java/com/smppw/modaq/infrastructure/util/ZipUtil.java

@@ -1,141 +0,0 @@
-package com.smppw.modaq.infrastructure.util;
-
-import cn.hutool.core.collection.ListUtil;
-import cn.hutool.core.io.FileUtil;
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
-import org.apache.commons.compress.archivers.zip.ZipFile;
-import org.apache.commons.io.IOUtils;
-
-import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Enumeration;
-import java.util.List;
-
-public class ZipUtil {
-    /**
-     * 递归解压 ZIP 文件(含嵌套深度限制)
-     *
-     * @param zipFile   输入的 ZIP 文件
-     * @param outputDir 解压目标根目录
-     * @param maxDepth  最大嵌套深度(例如 3 表示允许 parent/nest1/nest2.zip)
-     * @return 所有解压后的文件路径(格式:parent.zip/nest1/file.txt)
-     */
-    public static List<String> decompressZip(String zipFile, String outputDir, int maxDepth, String encoding) throws IOException {
-        return decompressZip(FileUtil.file(zipFile), FileUtil.file(outputDir), maxDepth, encoding);
-    }
-
-    /**
-     * 递归解压 ZIP 文件(含嵌套深度限制)
-     *
-     * @param zipFile   输入的 ZIP 文件
-     * @param outputDir 解压目标根目录
-     * @param maxDepth  最大嵌套深度(例如 3 表示允许 parent.zip/nest1.zip/nest2.zip)
-     * @return 所有解压后的文件路径(格式:parent.zip/nest1.zip/file.txt)
-     */
-    public static List<String> decompressZip(File zipFile, File outputDir, int maxDepth, String encoding) throws IOException {
-        if (maxDepth < 0) {
-            throw new IllegalArgumentException("最大嵌套深度不能小于 0");
-        }
-        List<String> decompressedFiles = ListUtil.list(false);
-        decompressZipRecursive(zipFile, outputDir, "", 0, maxDepth, encoding, decompressedFiles);
-        return decompressedFiles;
-    }
-
-    /**
-     * 递归解压核心逻辑
-     */
-    private static void decompressZipRecursive(
-            File currentZip,
-            File rootOutputDir,
-            String nestedPath,
-            int currentDepth,
-            int maxDepth,
-            String encoding,
-            List<String> decompressedFiles) throws IOException {
-
-        // 1. 超过最大深度时停止处理嵌套 ZIP
-        if (currentDepth > maxDepth) {
-            return;
-        }
-
-        // 2. 创建当前 ZIP 的解压目录(跟压缩包目录已经处理过,就不要追加到文件目录中了)
-        String currentZipName = FileUtil.mainName(currentZip);
-        String currentNestedPath = nestedPath.isEmpty()
-                ? ""
-                : nestedPath + File.separator + currentZipName;
-        File currentOutputDir = new File(rootOutputDir, currentNestedPath);
-        FileUtil.mkdir(currentOutputDir);
-
-        // 3. 解压当前 ZIP,支持最多10个分卷的解压
-        try (ZipFile zip = ZipFile.builder().setFile(currentZip).setCharset(encoding).setMaxNumberOfDisks(10).get()) {
-            Enumeration<ZipArchiveEntry> entries = zip.getEntries();
-
-            while (entries.hasMoreElements()) {
-                ZipArchiveEntry entry = entries.nextElement();
-                String name = entry.getName();
-                if (name.startsWith("__MACOSX/")) {
-                    continue;
-                }
-
-                Path entryPath = Paths.get(currentOutputDir.getAbsolutePath(), name);
-
-                // 处理目录
-                if (entry.isDirectory()) {
-                    Files.createDirectories(entryPath);
-                    continue;
-                }
-
-                // 写入文件
-                Files.createDirectories(entryPath.getParent());
-                try (InputStream is = zip.getInputStream(entry);
-                     OutputStream os = new FileOutputStream(entryPath.toFile())) {
-                    IOUtils.copy(is, os);
-                }
-
-                // 4. 递归处理嵌套 ZIP(深度+1)
-                if (isZipFile(name) && currentDepth < maxDepth) {
-                    File nestedZipFile = entryPath.toFile();
-                    decompressZipRecursive(
-                            nestedZipFile,
-                            rootOutputDir,
-                            currentNestedPath,
-                            currentDepth + 1,  // 深度递增
-                            maxDepth,
-                            encoding,
-                            decompressedFiles
-                    );
-                    Files.delete(nestedZipFile.toPath());
-                } else {
-                    // 记录路径
-                    decompressedFiles.add(entryPath.toString());
-                }
-            }
-        }
-    }
-
-    /**
-     * 判断文件是否为 ZIP 格式
-     */
-    private static boolean isZipFile(String filepath) {
-        return filepath.toLowerCase().endsWith(".zip");
-    }
-
-    // 使用示例
-    public static void main(String[] args) throws Exception {
-//        List<String> files = decompressZip(
-//                new File("C:\\Users\\Administrator\\Desktop\\上海熙盛明诚私募基金管理有限公司-信披报告.zip"),
-//                new File("C:\\Users\\Administrator\\Desktop"),
-//                2,
-//                "UTF-8"
-//        );
-//        System.out.println("解压后的文件路径:");
-//        files.forEach(System.out::println);
-
-        String currentZip = "D:\\Documents\\新报告解析\\确认单\\20250514_份额及交易确认函_上海量魁私募基金管理有限公司_深圳市前海排排网基金销售有限责任公司_TA确认数据.zip";
-        List<String> files = decompressZip(currentZip, "D:\\Documents\\新报告解析\\确认单\\", 2, "utf-8");
-        System.out.println("解压后的文件路径:");
-        files.forEach(System.out::println);
-    }
-}