|
@@ -1,340 +0,0 @@
|
|
|
-package cn.kdan.listener;
|
|
|
-
|
|
|
-import cn.hutool.json.JSONArray;
|
|
|
-import cn.hutool.json.JSONObject;
|
|
|
-import cn.hutool.json.JSONUtil;
|
|
|
-import cn.kdan.dto.MattermostCreatePost;
|
|
|
-import cn.kdan.entity.PersistentData;
|
|
|
-import cn.kdan.enums.ClientFeedbackEnum;
|
|
|
-import cn.kdan.enums.EmailSubjectEnum;
|
|
|
-import cn.kdan.service.PersistentDataService;
|
|
|
-import cn.kdan.utils.EmailUtil;
|
|
|
-import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.beans.factory.annotation.Value;
|
|
|
-import org.springframework.core.io.FileSystemResource;
|
|
|
-import org.springframework.http.*;
|
|
|
-import org.springframework.stereotype.Component;
|
|
|
-import org.springframework.util.CollectionUtils;
|
|
|
-import org.springframework.util.LinkedMultiValueMap;
|
|
|
-import org.springframework.util.MultiValueMap;
|
|
|
-import org.springframework.util.StringUtils;
|
|
|
-import org.springframework.web.client.RestTemplate;
|
|
|
-
|
|
|
-import javax.mail.*;
|
|
|
-import javax.mail.search.*;
|
|
|
-import java.io.File;
|
|
|
-import java.io.IOException;
|
|
|
-import java.text.ParseException;
|
|
|
-import java.text.SimpleDateFormat;
|
|
|
-import java.util.*;
|
|
|
-
|
|
|
-/**
|
|
|
- * @author ComPDF-WPH 2024-06-18
|
|
|
- **/
|
|
|
-@Slf4j
|
|
|
-@Component
|
|
|
-public class EmailListener {
|
|
|
-
|
|
|
- private final RestTemplate restTemplate = new RestTemplate();
|
|
|
- private final PersistentDataService persistentDataService;
|
|
|
-
|
|
|
- @Value("${workflow.customer_email.host}")
|
|
|
- private String host;
|
|
|
- @Value("${workflow.customer_email.port}")
|
|
|
- private String port;
|
|
|
- @Value("${workflow.customer_email.user}")
|
|
|
- private String user;
|
|
|
- @Value("${workflow.customer_email.appPassword}")
|
|
|
- private String appPassword;
|
|
|
- @Value("${workflow.customer_email.folder}")
|
|
|
- private String folderStr;
|
|
|
-
|
|
|
- @Value("${workflow.customer_email.authorization}")
|
|
|
- private String authorization;
|
|
|
- @Value("${workflow.customer_email.channelId}")
|
|
|
- private String channelId;
|
|
|
- @Value("${workflow.customer_email.fileUrl}")
|
|
|
- private String fileUrl;
|
|
|
- @Value("${workflow.customer_email.postUrl}")
|
|
|
- private String postUrl;
|
|
|
-
|
|
|
- public EmailListener(PersistentDataService persistentDataService) {
|
|
|
- this.persistentDataService = persistentDataService;
|
|
|
- }
|
|
|
-
|
|
|
- // 每小时执行一次,cron 表达式:"0 0 * * * ?"
|
|
|
-// @Scheduled(cron = "0 0 * * * ?")
|
|
|
- public void executeTask(Map<String, Object> requestBody) {
|
|
|
- assert requestBody != null;
|
|
|
- log.info("请求参数: {}", requestBody.toString());
|
|
|
- String filterFolderStr = "";
|
|
|
- // 获取上次更新时间
|
|
|
- PersistentData persistentData = persistentDataService.getById(1);
|
|
|
- // 获取当前时间
|
|
|
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
- String nowTime = dateFormat.format(new Date());
|
|
|
-
|
|
|
- String startTime = persistentData.getDataValue();
|
|
|
- String endTime = nowTime;
|
|
|
- String remindPersonnel = "@suvi @dinglingui @wanjun @zhoujiesheng @huangyaqing @yangyang @nicole ";
|
|
|
- if (null != requestBody) {
|
|
|
- if (null != requestBody.get("filterFolderStr")) {
|
|
|
- filterFolderStr = requestBody.get("filterFolderStr").toString();
|
|
|
- }
|
|
|
- if (null != requestBody.get("remindPersonnel")) {
|
|
|
- remindPersonnel = requestBody.get("remindPersonnel").toString();
|
|
|
- }
|
|
|
- if (!StringUtils.isEmpty(requestBody.get("startTime"))) {
|
|
|
- startTime = requestBody.get("startTime").toString();
|
|
|
- // 检查时间格式
|
|
|
- try {
|
|
|
- dateFormat.parse(requestBody.get("startTime").toString());
|
|
|
- } catch (ParseException e) {
|
|
|
- log.error("时间格式错误,请检查时间格式是否正确");
|
|
|
- throw new RuntimeException("时间格式错误,请检查时间格式是否正确");
|
|
|
- }
|
|
|
- }
|
|
|
- if (!StringUtils.isEmpty(requestBody.get("endTime"))) {
|
|
|
- endTime = requestBody.get("endTime").toString();
|
|
|
- // 检查时间格式
|
|
|
- try {
|
|
|
- dateFormat.parse(requestBody.get("endTime").toString());
|
|
|
- } catch (ParseException e) {
|
|
|
- log.error("时间格式错误,请检查时间格式是否正确");
|
|
|
- throw new RuntimeException("时间格式错误,请检查时间格式是否正确");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- log.info("开始时间: {}, 结束时间: {}", startTime, endTime);
|
|
|
- // 判断开始时间是否大于等于结束时间
|
|
|
- try {
|
|
|
- if (dateFormat.parse(startTime).after(dateFormat.parse(endTime))) {
|
|
|
- log.error("开始时间不能大于结束时间");
|
|
|
- throw new RuntimeException("开始时间不能大于结束时间");
|
|
|
- }
|
|
|
- } catch (ParseException e) {
|
|
|
- log.error("时间格式错误,请检查时间格式是否正确");
|
|
|
- throw new RuntimeException("时间格式错误,请检查时间格式是否正确");
|
|
|
- }
|
|
|
- log.info("开始读取收件箱: {}", new Date());
|
|
|
- // 设置邮件服务器属性
|
|
|
- Properties properties = new Properties();
|
|
|
- properties.put("mail.store.protocol", "imaps");
|
|
|
- properties.put("mail.imap.host", host);
|
|
|
- properties.put("mail.imap.port", port);
|
|
|
- properties.put("mail.imap.starttls.enable", "true");
|
|
|
- properties.put("mail.imap.ssl.trust", "*");
|
|
|
- properties.put("mail.imap.ssl.enable", "true");
|
|
|
- Session session = Session.getInstance(properties);
|
|
|
-// session.setDebug(true);
|
|
|
- if (null == filterFolderStr) {
|
|
|
- filterFolderStr = "";
|
|
|
- }
|
|
|
- String[] split = filterFolderStr.split(";");
|
|
|
- List<String> filterFolderList = Arrays.asList(split);
|
|
|
- try {
|
|
|
- // 获取邮箱存储
|
|
|
- Store store = session.getStore("imap");
|
|
|
- // 使用应用专用密码
|
|
|
- store.connect(user, appPassword);
|
|
|
- // 获取所有文件夹
|
|
|
-// Folder[] folders = store.getDefaultFolder().list();
|
|
|
-// System.out.println(folders.length);
|
|
|
- // 打开收件箱,用户反馈,INBOX
|
|
|
- Folder folder = store.getFolder(folderStr);
|
|
|
- extracted(folder, folderStr, filterFolderList, remindPersonnel, startTime, endTime);
|
|
|
- log.info("读取完成,关闭收件箱,:{}", folder.getName());
|
|
|
- store.close();
|
|
|
- } catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- persistentData.setDataValue(endTime);
|
|
|
- log.info("更新处理时间时间,{}", persistentData.getDataValue());
|
|
|
- persistentDataService.updateById(persistentData);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 读取邮件文件夹
|
|
|
- * @param folder 邮件文件夹
|
|
|
- * @param folderStr 当前文件夹
|
|
|
- * @param filterFolderList 过滤文件夹
|
|
|
- * @param remindPersonnel @人员
|
|
|
- * @param extractTime 提取时间
|
|
|
- * @throws MessagingException
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- private void extracted(Folder folder, String folderStr, List<String> filterFolderList, String remindPersonnel, String extractTime, String endTime) throws MessagingException, IOException, ParseException {
|
|
|
- // 判断收件箱是否包含文件夹,存在则循坏和递归读取子目录
|
|
|
- log.info("读取收件箱:{}", folderStr);
|
|
|
- if (folder.getType() != Folder.HOLDS_MESSAGES) {
|
|
|
- Folder[] subfolders = folder.list();
|
|
|
- log.info("收件箱[{}]子级文件夹数量: {}", folderStr, subfolders.length);
|
|
|
- for (Folder subfolder : subfolders) {
|
|
|
- String folderString = folderStr + "-" + subfolder.getName();
|
|
|
- if (filterFolderList.contains(folderString)) {
|
|
|
- log.info("过滤文件夹:{};跳过文件夹读取,{}", filterFolderList, folderString);
|
|
|
- continue;
|
|
|
- }
|
|
|
- extracted(subfolder, folderString, filterFolderList, remindPersonnel, extractTime, endTime);
|
|
|
- }
|
|
|
- }
|
|
|
- // 打开收件箱
|
|
|
- folder.open(Folder.READ_ONLY);
|
|
|
- Message[] totalMessages = folder.getMessages();
|
|
|
- log.info("打开收件箱完成,{}收件箱总计邮件数: {}", folderStr, totalMessages.length);
|
|
|
- // 过滤时间,获取上次提取的时间 和 提取前一天的时间
|
|
|
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
- Date extractDateTime = dateFormat.parse(extractTime);
|
|
|
-
|
|
|
- Calendar calendar = Calendar.getInstance();
|
|
|
- calendar.setTime(extractDateTime);
|
|
|
- // 美国时区-12小时
|
|
|
-// calendar.add(Calendar.HOUR_OF_DAY, -12);
|
|
|
-// Date startTime = calendar.getTime();
|
|
|
-// log.info("提取开始时间: {}", dateFormat.format(startTime));
|
|
|
- calendar.add(Calendar.DAY_OF_MONTH, -1);
|
|
|
- Date folderTime = calendar.getTime();
|
|
|
- log.info("提取前一天时间: {}", dateFormat.format(folderTime));
|
|
|
-
|
|
|
- // 获取上次提取前一天到现在的邮件
|
|
|
- ReceivedDateTerm searchTerm = new ReceivedDateTerm(ComparisonTerm.GE, folderTime);
|
|
|
- Message[] messages = folder.search(searchTerm);
|
|
|
- log.info("上次提取前到现在收件箱邮件数: {}", messages.length);
|
|
|
- if(messages.length <= 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
- // 遍历条件获取到的邮件
|
|
|
- for (Message message : messages) {
|
|
|
- // 处理查找到的邮件
|
|
|
- String subject = message.getSubject();
|
|
|
- log.info("处理邮件,MessageNumber:{},subject:{}", message.getMessageNumber(), subject);
|
|
|
- if (StringUtils.isEmpty(subject)) {
|
|
|
- log.warn("邮件主题为空,跳过");
|
|
|
- continue;
|
|
|
- }
|
|
|
- // 过滤来自 @google.com 邮箱服务器
|
|
|
- String from = EmailUtil.extractTargetEmail(message.getFrom()[0].toString());
|
|
|
- log.info("发件人:{}", from);
|
|
|
- if (from.contains("@google.com")) {
|
|
|
- log.info("来自谷歌邮箱,跳过");
|
|
|
- continue;
|
|
|
- }
|
|
|
- Date receivedDate = message.getReceivedDate();
|
|
|
- // 再次过滤,因为上面的邮件搜索只能按天,这次过滤准确值
|
|
|
- if (receivedDate == null || receivedDate.before(extractDateTime)) {
|
|
|
- log.info("邮件时间早于上次提取时间,跳过;time:{}", receivedDate);
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (receivedDate.after(dateFormat.parse(endTime))) {
|
|
|
- log.info("邮件时间晚于本次提取时间,跳过;time:{}", receivedDate);
|
|
|
- continue;
|
|
|
- }
|
|
|
- /*log.info("过滤邮件主题: {}", subject);
|
|
|
- // 过滤邮件主题 和 反馈类型
|
|
|
- EmailSubjectEnum emailSubjectEnum = EmailSubjectEnum.getByLikeValue(subject);
|
|
|
- ClientFeedbackEnum clientFeedbackEnum = ClientFeedbackEnum.getByLikeValue(subject);
|
|
|
- if (emailSubjectEnum == null || clientFeedbackEnum == null) {
|
|
|
- continue;
|
|
|
- }*/
|
|
|
- // @人员相关,根据产品类型判断
|
|
|
- /*switch(emailSubjectEnum) {
|
|
|
- case MAC_APP_STORE_FREE:
|
|
|
- case UWP_FREE:
|
|
|
- case IOS:
|
|
|
- case MAC_APP_STORE_PAID:
|
|
|
- case UWP_PAID:
|
|
|
- atPeople = "@dinglingui @suvi @wanjun @zhoujiesheng @chuge ";
|
|
|
- break;
|
|
|
- case MAC_DMG:
|
|
|
- case ANDROID:
|
|
|
- atPeople = "@dinglingui @suvi @wanjun @chuge ";
|
|
|
- break;
|
|
|
- case WINDOWS:
|
|
|
- atPeople = "@dinglingui @suvi @zhoujiesheng @liyijie @chuge ";
|
|
|
- break;
|
|
|
- }*/
|
|
|
-
|
|
|
- // 创建 Mattermost 请求参数对象
|
|
|
- MattermostCreatePost mattermostCreatePost = new MattermostCreatePost();
|
|
|
- mattermostCreatePost.setChannel_id(channelId);
|
|
|
- String content = "### 收到一封新邮件"
|
|
|
- + "\n**From: **" + from
|
|
|
- + "\n**邮件主题: **" + subject
|
|
|
- + "\n**收件时间: **" + receivedDate
|
|
|
- + "\n**收件箱: **" + folderStr
|
|
|
- + "\n**内容: **" + EmailUtil.getTextFromMessage(message)
|
|
|
- + "\n" + remindPersonnel;
|
|
|
- mattermostCreatePost.setMessage(content);
|
|
|
-
|
|
|
- // 图片 + 附件 查询
|
|
|
- // 构建 MultiValueMap 以包含文件数据
|
|
|
- MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
|
- boolean fileFlag = false;
|
|
|
- // 判断邮件中的附件和图片
|
|
|
- List<File> attachmentFiles = new ArrayList<>();
|
|
|
- EmailUtil.saveAttachmentFiles(message, attachmentFiles);
|
|
|
- if (!CollectionUtils.isEmpty(attachmentFiles)) {
|
|
|
- log.info("邮件正文包含图片");
|
|
|
- fileFlag = true;
|
|
|
- for (File imageFile : attachmentFiles) {
|
|
|
- body.add("files", new FileSystemResource(imageFile));
|
|
|
- }
|
|
|
- }
|
|
|
- // 如果有附件就处理
|
|
|
-// if (message.getContent() instanceof Multipart) {
|
|
|
-// log.info("邮件包含附件");
|
|
|
-// fileFlag = true;
|
|
|
-// Multipart multipart = (Multipart) message.getContent();
|
|
|
-// for (int i = 0; i < multipart.getCount(); i++) {
|
|
|
-// BodyPart bodyPart = multipart.getBodyPart(i);
|
|
|
-// if (Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {
|
|
|
-// File file = EmailUtil.saveAttachment(bodyPart);
|
|
|
-// body.add("files", new FileSystemResource(file));
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
- if (fileFlag) {
|
|
|
- List<String> fileIds = new ArrayList<>();
|
|
|
- // 创建 HttpEntity 包含 headers 和 body
|
|
|
- body.add("channel_id", channelId);
|
|
|
- HttpHeaders headers = new HttpHeaders();
|
|
|
- headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
|
|
- headers.set(HttpHeaders.AUTHORIZATION, authorization);
|
|
|
- HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
|
|
- // 发送 POST 请求并获取响应
|
|
|
- ResponseEntity<String> response = restTemplate.exchange(fileUrl, HttpMethod.POST, requestEntity, String.class);
|
|
|
- log.info("上传文件Response Code: " + response.getStatusCodeValue());
|
|
|
- if (response.getStatusCode().is2xxSuccessful()) {
|
|
|
- String responseBody = response.getBody();
|
|
|
- // 解析响应体,获取 file_ids
|
|
|
- JSONObject jsonObject = JSONUtil.parseObj(responseBody);
|
|
|
- JSONArray jsonArray = jsonObject.getJSONArray("file_infos");
|
|
|
- for (Object obj : jsonArray) {
|
|
|
- JSONObject fileInfo = (JSONObject) obj;
|
|
|
- String fileId = fileInfo.getStr("id");
|
|
|
- fileIds.add(fileId);
|
|
|
- }
|
|
|
- }
|
|
|
- mattermostCreatePost.setFile_ids(fileIds);
|
|
|
- }
|
|
|
-
|
|
|
- // 发送Mattermost信息
|
|
|
- HttpHeaders headers = new HttpHeaders();
|
|
|
- headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
- headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
|
|
|
- headers.set(HttpHeaders.AUTHORIZATION, authorization);
|
|
|
- HttpEntity<String> request = new HttpEntity<>(JSONUtil.toJsonStr(mattermostCreatePost), headers);
|
|
|
- try {
|
|
|
- ResponseEntity<JSONObject> response = restTemplate
|
|
|
- .exchange(postUrl, HttpMethod.POST, request, JSONObject.class);
|
|
|
- log.info("发送Mattermost信息Response Code: " + response.getStatusCodeValue());
|
|
|
- log.info("发送Mattermost信息Response Body: " + response.getBody());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("发送Mattermost信息失败", e);
|
|
|
- }
|
|
|
- }
|
|
|
- // 关闭文件夹和存储
|
|
|
- folder.close(false);
|
|
|
- }
|
|
|
-
|
|
|
-}
|