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 com.sun.mail.imap.IMAPStore; 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.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 collectMimeMultipart(Message message, String emailAddress, String path) throws Exception { List 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 = 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(DateUtil.format(message.getSentDate(), DateConst.YYYY_MM_DD_HH_MM_SS)); emailContentInfoDTOList.add(emailContentInfoDTO); } return emailContentInfoDTOList; } /** * 根据日期过滤邮件 * * @param messages 采集到的邮件 * @param startDate 邮件起始日期 * @param endDate 邮件截止日期 * @return 符合日期的邮件 */ public static List filterMessage(Message[] messages, Date startDate, Date endDate) { long startTime = System.currentTimeMillis(); List 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(" 文件名:{}, 报错堆栈:{}", fileName, ExceptionUtil.stacktraceToString(e)); return emailContentInfoDTO; } return emailContentInfoDTO; } /** * 判断邮件是否符合解析条件 * * @param subject 邮件主题 * @param emailTypeMap 邮件类型识别规则映射表 * @return 邮件类型:1-净值,2-估值表,3-定期报告,null代表不支持该邮件解析 */ public static Integer getEmailTypeBySubject(String subject, Map> emailTypeMap) { if (MapUtil.isEmpty(emailTypeMap)) { return null; } for (Map.Entry> 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)) { IMAPStore imapStore = (IMAPStore) session.getStore(protocol); imapStore.connect(mailboxInfoDTO.getHost(), mailboxInfoDTO.getAccount(), mailboxInfoDTO.getPassword()); // 网易邮箱需要带上身份标识,详情请看:https://www.hmail163.com/content/?404.html Map 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, props); logger.error("连接邮箱报错堆栈信息:{}", 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; } }