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.common.conts.EmailTypeConst; import com.simuwang.base.pojo.dto.EmailContentInfoDTO; import com.simuwang.base.pojo.dto.MailboxInfoDTO; import com.sun.mail.imap.IMAPStore; import jakarta.activation.DataHandler; import jakarta.mail.*; import jakarta.mail.internet.*; import jakarta.mail.util.ByteArrayDataSource; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.mail.javamail.JavaMailSender; import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; 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"; String content = partContent.toString(); emailContentInfoDTO = collectTextPart(part, content, filePath, fileName); } else if ("BASE64DecoderStream".equals(contentClass)) { if (StrUtil.isNotBlank(part.getFileName())) { String fileName = MimeUtility.decodeText(part.getFileName()); if (!isSupportedFileType(fileName)) { continue; } emailContentInfoDTO.setFileName(fileName); String realPath = filePath + emailDate + fileName; File saveFile = new File(realPath); if (!saveFile.exists()) { if (!saveFile.getParentFile().exists()) { saveFile.getParentFile().mkdirs(); } FileUtil.saveFile(saveFile, part); } else { FileUtils.deleteQuietly(saveFile); FileUtil.saveFile(saveFile, part); } emailContentInfoDTO.setFilePath(realPath); } } 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"; String content = partContent.toString(); emailContentInfoDTO = collectTextPart(part, content, 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; } private static boolean isSupportedFileType(String fileName) { if (StrUtil.isBlank(fileName)) { return false; } return ExcelUtil.isZip(fileName) || ExcelUtil.isExcel(fileName) || ExcelUtil.isPdf(fileName) || ExcelUtil.isHTML(fileName); } /** * 根据日期过滤邮件 * * @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, String partContent, String filePath, String fileName) { EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO(); try { if ((part.getContentType().contains("text/html") || part.getContentType().contains("TEXT/HTML"))) { 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); } } try{ //获取邮件编码 String contentType = part.getContentType(); if(contentType.indexOf("charset=") != -1){ contentType = contentType.substring(contentType.indexOf("charset=")+8,contentType.length()); FileUtil.writeFile(new File(savePath), partContent.toString(),contentType); }else{ FileUtil.writeFile(savePath, partContent.toString()); } }catch (Exception e){ 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-定期报告 -> 兜底为净值类型 */ public static Integer getEmailTypeBySubject(String subject, Map> emailTypeMap) { if (MapUtil.isEmpty(emailTypeMap) || StrUtil.isBlank(subject)) { return EmailTypeConst.NAV_EMAIL_TYPE; } for (Map.Entry> emailTypeEntry : emailTypeMap.entrySet()) { for (String field : emailTypeEntry.getValue()) { if (subject.contains(field)) { return emailTypeEntry.getKey(); } } } return EmailTypeConst.NAV_EMAIL_TYPE; } 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; } public static void senEmail(MailboxInfoDTO mailboxInfoDTO, String emails, File file, JavaMailSender javaMailSender,String htmlText) throws Exception { logger.info("send email begin ........."); // 根据Session 构建邮件信息 // 创建一个配置文件,并保存 Properties props = new Properties(); // SMTP服务器连接信息 props.put("mail.smtp.host", mailboxInfoDTO.getHost()); props.put("mail.smtp.port", mailboxInfoDTO.getPort()); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enale", "true"); Session session = Session.getInstance(props,new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(mailboxInfoDTO.getAccount(),mailboxInfoDTO.getPassword()); } }); // 根据Session 构建邮件信息 MimeMessage message = new MimeMessage(session);; // 创建邮件发送者地址 Address from = new InternetAddress(mailboxInfoDTO.getAccount()); String[] emailArr = emails.split(";"); Address[] toArr = new Address[emailArr.length]; for (int idx = 0; idx < emailArr.length; idx++) { if (StringUtils.isNotEmpty(emailArr[idx])) { Address to = new InternetAddress(emailArr[idx]); toArr[idx] = to; } } message.setFrom(from); message.setRecipients(Message.RecipientType.TO, toArr); // 邮件主题 message.setSubject("产品净值补发"); // 邮件容器 MimeMultipart mimeMultiPart = new MimeMultipart(); // 设置HTML BodyPart bodyPart = new MimeBodyPart(); logger.info("组装 htmlText........."); // 邮件内容 bodyPart.setContent(htmlText, "text/html;charset=utf-8"); //设置附件 BodyPart filePart = new MimeBodyPart(); filePart.setFileName(file.getName()); filePart.setDataHandler( new DataHandler( new ByteArrayDataSource( Files.readAllBytes(Paths.get(file.getAbsolutePath())), "application/octet-stream"))); mimeMultiPart.addBodyPart(bodyPart); mimeMultiPart.addBodyPart(filePart); message.setContent(mimeMultiPart); message.setSentDate(new Date()); // 保存邮件 message.saveChanges(); // 发送邮件 javaMailSender.send(message); } }