提交 6b22ba70 作者: 刘振萌

邮箱变更

父级 620577dc
......@@ -17,7 +17,6 @@
</properties>
<dependencyManagement>
<dependencies>
<!-- Microsoft Graph SDK -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
......@@ -61,29 +60,6 @@
<version>3.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph-auth</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<!-- Include the sdk as a dependency -->
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph-core</artifactId>
<version>2.0.14</version>
</dependency>
<dependency>
<!-- Include the sdk as a dependency -->
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph</artifactId>
<version>5.42.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.15</version>
......@@ -154,19 +130,6 @@
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<!-- Microsoft Graph SDK -->
<dependency>
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph-auth</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<!-- Include the sdk as a dependency -->
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph-core</artifactId>
<version>2.0.14</version>
</dependency>
<!-- MSAL认证库 -->
......@@ -218,18 +181,6 @@
<version>4.9.3</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.3.3</version>
</dependency>
<!-- 确保所有Azure库版本一致 -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
<version>1.34.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version> <!-- 请使用适合您MySQL版本的驱动 -->
......@@ -253,6 +204,11 @@
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.13.0</version>
......
package com.cmeeting.email;
import cn.hutool.json.JSONUtil;
import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenRequestContext;
import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmeeting.ad.service.UserService;
import com.cmeeting.ad.vo.UserVo;
import com.cmeeting.exception.RobotBaseException;
import com.cmeeting.log.service.ProcessLogService;
import com.cmeeting.pojo.MeetEmailTemplate;
import com.cmeeting.service.MeetEmailTemplateService;
import com.cmeeting.util.AESUtils;
import com.cmeeting.util.RSAUtils;
import com.cmeeting.util.RedisUtils;
import com.cmeeting.vo.EmailPush;
import com.cmeeting.vo.StatisticsEmailPush;
import com.microsoft.graph.authentication.TokenCredentialAuthProvider;
import com.microsoft.graph.models.*;
import com.microsoft.graph.models.Message;
import com.microsoft.graph.requests.AttachmentCollectionPage;
import com.microsoft.graph.requests.AttachmentCollectionResponse;
import com.microsoft.graph.requests.GraphServiceClient;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.annotation.Resource;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.File;
import javax.mail.util.ByteArrayDataSource;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.List;
import javax.annotation.Resource;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
......@@ -53,16 +32,16 @@ import java.util.concurrent.atomic.AtomicInteger;
public class EmailSender {
@Value("${email.sender}")
private String SENDER;
@Value("${email.sender-pwd}")
private String SENDER_PWD;
@Value("${email.smtp-host}")
private String SMTP_HOST;
@Value("${email.smtp-port}")
private int SMTP_PORT;
@Value("${email.push-switch}")
private Boolean pushSwitch;
@Value("${email.environment}")
private String environment;
@Value("${email.microsoft.clientId}")
private String clientId;
@Value("${email.microsoft.clientSecret}")
private String clientSecret;
@Value("${email.microsoft.tenantId}")
private String tenantId;
@Value("${aec.key}")
private String aseKey;
@Resource
......@@ -72,12 +51,25 @@ public class EmailSender {
private static final Integer MAX_RETRY = 3;
/**
* 发送邮件,带附件
*
* @param emailPushBuilder
* @return
*/
private Session createMailSession() {
Properties props = new Properties();
props.put("mail.smtp.host", SMTP_HOST);
props.put("mail.smtp.port", String.valueOf(SMTP_PORT));
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.ssl.protocols", "TLSv1.2");
props.put("mail.smtp.connectiontimeout", "30000");
props.put("mail.smtp.timeout", "60000");
props.put("mail.smtp.writetimeout", "60000");
return Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(SENDER, SENDER_PWD);
}
});
}
public Boolean sendEmailWithAttachment(EmailPush emailPushBuilder) {
log.info("sendEmailWithAttachment start, sender -> {}", SENDER);
......@@ -86,7 +78,7 @@ public class EmailSender {
throw new RuntimeException("【邮箱推送】:应用未开启邮件推送功能");
}
AtomicInteger retryCount = new AtomicInteger(0);
Boolean isSent = false;
boolean isSent = false;
String toEmail = emailPushBuilder.getToEmail();
String subject = emailPushBuilder.getSubject();
String meetingRecordId = emailPushBuilder.getMeetingRecordId();
......@@ -96,110 +88,80 @@ public class EmailSender {
String toUserCode = emailPushBuilder.getToUserCode();
String toUser = emailPushBuilder.getToUser();
Boolean emailPushAccess = emailPushBuilder.getEmailPushAccess();
//用户自定义邮件推送许可
if (emailPushAccess) {
log.info("用户允许邮件推送,准备推送邮件至{}------", emailPushBuilder.getToEmail());
} else {
log.info("用户关闭了邮件推送,推送终止------");
processLogService.log(meetingId,subMeetingId,"用户关闭了邮件推送,推送终止");
processLogService.log(meetingId, subMeetingId, "用户关闭了邮件推送,推送终止");
throw new RuntimeException("用户关闭了邮件推送,推送终止");
}
if (StringUtils.isEmpty(toEmail)) {
log.error("收件邮箱为空,推送失败");
processLogService.log(meetingId,subMeetingId,"收件邮箱为空,推送失败。environment->"+environment+",realEmail->"+emailPushBuilder.getToEmail());
processLogService.log(meetingId, subMeetingId, "收件邮箱为空,推送失败。environment->" + environment + ",realEmail->" + emailPushBuilder.getToEmail());
throw new RuntimeException("收件邮箱为空,推送失败");
}
log.info("准备开始邮件推送...");
while (retryCount.intValue() < MAX_RETRY && isSent != null && !isSent) {
try {
ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId(RSAUtils.decrypt(clientId))
.clientSecret(RSAUtils.decrypt(clientSecret))
.tenantId(RSAUtils.decrypt(tenantId))
.build();
while (retryCount.intValue() < MAX_RETRY && !isSent) {
try {
TokenRequestContext context = new TokenRequestContext()
.addScopes("https://graph.microsoft.com/.default");
AccessToken token = clientSecretCredential.getToken(context).block();
System.out.println(token != null ? token.getToken() : null);
} catch (Exception e) {
log.error("获取访问令牌失败: {}", e.getMessage());
}
final TokenCredentialAuthProvider tokenCredAuthProvider = new TokenCredentialAuthProvider(Arrays.asList("https://graph.microsoft.com/.default"), clientSecretCredential);
System.out.println("First Step Reached. ");
GraphServiceClient<Request> graphClient = GraphServiceClient.builder().authenticationProvider(tokenCredAuthProvider).buildClient();
com.microsoft.graph.models.Message message = new Message();
message.subject = subject + "会议纪要";
ItemBody body = new ItemBody();
body.contentType = BodyType.HTML;
Session session = createMailSession();
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(SENDER));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
message.setSubject(subject + "会议纪要", "UTF-8");
if (StringUtils.isEmpty(toUserCode)) {
isSent = false;
processLogService.log(meetingId, subMeetingId, "【邮件推送异常】:收件人工号不能为空");
continue;
}
// HTML body content from template
MimeBodyPart bodyPart = new MimeBodyPart();
MeetEmailTemplate one = meetEmailTemplateService.getOne(new LambdaQueryWrapper<MeetEmailTemplate>()
.orderByDesc(MeetEmailTemplate::getCreateTime)
.select(MeetEmailTemplate::getContent).last("limit 1"));
String emailContentTemplate = one.getContent();
long expireTimestamp = ZonedDateTime.now().plusDays(1).toInstant().toEpochMilli();
UserVo.Auth auth = new UserVo.Auth();
com.cmeeting.ad.vo.UserVo.Auth auth = new com.cmeeting.ad.vo.UserVo.Auth();
auth.setId(toUserCode);
auth.setNick(toUser);
auth.setExpireDate(expireTimestamp);
String s = JSONUtil.toJsonStr(auth);
String encrypt = AESUtils.encrypt(s, aseKey);
body.content = MessageFormat.format(emailContentTemplate, URLEncoder.encode(encrypt, StandardCharsets.UTF_8.name()), String.valueOf(meetingInstanceId));
String htmlContent = MessageFormat.format(emailContentTemplate,
URLEncoder.encode(encrypt, StandardCharsets.UTF_8.name()),
String.valueOf(meetingInstanceId));
bodyPart.setContent(htmlContent, "text/html; charset=utf-8");
// Build attachments
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(bodyPart);
message.body = body;
LinkedList<Recipient> toRecipientsList = new LinkedList<>();
Recipient toRecipients = new Recipient();
EmailAddress emailAddress = new EmailAddress();
emailAddress.address = toEmail;
toRecipients.emailAddress = emailAddress;
toRecipientsList.add(toRecipients);
message.toRecipients = toRecipientsList;
//构建附件
LinkedList<Attachment> attachmentsList = new LinkedList<>();
for (EmailPush.Attachment attachment : emailPushBuilder.getAttachments()) {
FileAttachment attachments = new FileAttachment();
if (attachment.getName().endsWith(".pdf")) {
attachments.name = attachment.getName();
attachments.contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
MimeBodyPart attachPart = new MimeBodyPart();
String fileName = attachment.getName();
String contentType;
if (fileName.endsWith(".pdf")) {
contentType = "application/pdf";
} else {
attachments.name = attachment.getName() + ".docx";
attachments.contentType = "application/pdf";
fileName += ".docx";
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
}
attachments.oDataType = "#microsoft.graph.fileAttachment";
attachments.contentBytes = attachment.getBytes();
attachmentsList.add(attachments);
attachPart.setDataHandler(new DataHandler(new ByteArrayDataSource(attachment.getBytes(), contentType)));
attachPart.setFileName(MimeUtility.encodeText(fileName, "UTF-8", "B"));
multipart.addBodyPart(attachPart);
}
AttachmentCollectionResponse attachmentCollectionResponse = new AttachmentCollectionResponse();
attachmentCollectionResponse.value = attachmentsList;
AttachmentCollectionPage attachmentCollectionPage = new AttachmentCollectionPage(attachmentCollectionResponse, null);
message.attachments = attachmentCollectionPage;
//以指定用户邮箱发送邮件
graphClient.users(SENDER)
.sendMail(UserSendMailParameterSet.newBuilder().
withMessage(message).
withSaveToSentItems(true).build())
.buildRequest()
.post();
message.setContent(multipart);
Transport.send(message);
log.info("邮件已成功发送: meetingRecordId->{}", meetingRecordId);
isSent = true;
} catch (Exception e) {
retryCount.getAndIncrement();
// 异常处理
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
......@@ -208,7 +170,6 @@ public class EmailSender {
log.error("邮件发送达到最大重试次数: meetingId->{}", meetingId);
throw new RuntimeException(e);
}
// 指数退避
try {
Thread.sleep((long) Math.pow(2, retryCount.intValue()) * 1000);
} catch (InterruptedException ie) {
......@@ -227,65 +188,41 @@ public class EmailSender {
boolean isSent = false;
while (retryCount.intValue() < MAX_RETRY && !isSent) {
try {
ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId(RSAUtils.decrypt(clientId))
.clientSecret(RSAUtils.decrypt(clientSecret))
.tenantId(RSAUtils.decrypt(tenantId))
.build();
try {
TokenRequestContext context = new TokenRequestContext().addScopes("https://graph.microsoft.com/.default");
AccessToken token = clientSecretCredential.getToken(context).block();
System.out.println(token != null ? token.getToken() : null);
} catch (Exception e) {
log.error("统计邮件获取访问令牌失败: {}", e.getMessage());
}
final TokenCredentialAuthProvider tokenCredAuthProvider = new TokenCredentialAuthProvider(Collections.singletonList("https://graph.microsoft.com/.default"), clientSecretCredential);
System.out.println("First Step Reached. ");
GraphServiceClient<Request> graphClient = GraphServiceClient.builder().authenticationProvider(tokenCredAuthProvider).buildClient();
Session session = createMailSession();
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(SENDER));
message.setSubject(emailPushBuilder.getSubject(), "UTF-8");
com.microsoft.graph.models.Message message = new Message();
message.subject = emailPushBuilder.getSubject();
ItemBody body = new ItemBody();
body.contentType = BodyType.HTML;
body.content = emailPushBuilder.getContent();
message.body = body;
LinkedList<Recipient> toRecipientsList = new LinkedList<>();
// HTML body
MimeBodyPart bodyPart = new MimeBodyPart();
bodyPart.setContent(emailPushBuilder.getContent(), "text/html; charset=utf-8");
// Recipients
for (StatisticsEmailPush.ToEmail toEmail : emailPushBuilder.getToEmails()) {
Recipient toRecipients = new Recipient();
EmailAddress emailAddress = new EmailAddress();
emailAddress.address = toEmail.getEmail();
emailAddress.name = toEmail.getUser();
toRecipients.emailAddress = emailAddress;
toRecipientsList.add(toRecipients);
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(toEmail.getEmail(), toEmail.getUser()));
}
message.toRecipients = toRecipientsList;
//构建附件
LinkedList<Attachment> attachmentsList = new LinkedList<>();
// Attachments
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(bodyPart);
for (StatisticsEmailPush.Attachment attachment : emailPushBuilder.getAttachments()) {
FileAttachment attachments = new FileAttachment();
attachments.name = attachment.getName() + ".xlsx";
attachments.oDataType = "#microsoft.graph.fileAttachment";
attachments.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
attachments.contentBytes = attachment.getBytes();
attachmentsList.add(attachments);
MimeBodyPart attachPart = new MimeBodyPart();
String fileName = attachment.getName() + ".xlsx";
String contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
attachPart.setDataHandler(new DataHandler(new ByteArrayDataSource(attachment.getBytes(), contentType)));
attachPart.setFileName(MimeUtility.encodeText(fileName, "UTF-8", "B"));
multipart.addBodyPart(attachPart);
}
AttachmentCollectionResponse attachmentCollectionResponse = new AttachmentCollectionResponse();
attachmentCollectionResponse.value = attachmentsList;
message.setContent(multipart);
Transport.send(message);
message.attachments = new AttachmentCollectionPage(attachmentCollectionResponse, null);
//以指定用户邮箱发送邮件
graphClient.users(SENDER)
.sendMail(UserSendMailParameterSet.newBuilder().
withMessage(message).
withSaveToSentItems(true).build())
.buildRequest()
.post();
log.info("统计邮件已成功发送");
isSent = true;
} catch (Exception e) {
retryCount.getAndIncrement();
// 异常处理
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
......@@ -293,7 +230,6 @@ public class EmailSender {
log.error("统计邮件发送达到最大重试次数");
throw new RuntimeException(e);
}
// 指数退避
try {
Thread.sleep((long) Math.pow(2, retryCount.intValue()) * 1000);
} catch (InterruptedException ie) {
......
......@@ -3,10 +3,6 @@ package com.cmeeting.job;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenRequestContext;
import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmeeting.config.StatisticsEmailPushProperties;
import com.cmeeting.constant.DeptCollect;
......
......@@ -367,25 +367,25 @@ public class FileProcessTask {
"text/plain; charset=utf-8", // MIME类型
recordData.getBytes(StandardCharsets.UTF_8) // 字节内容
);
//将转录文件存到知识向量库
Map<String, String> otherParams = new HashMap<>();
otherParams.put("userId", toUserCode);
otherParams.put("tenantId", permTenantId);
otherParams.put("layout", "V1");
String responseData = HttpClientKnowledgePlatformUtil.sendPostByFormDataFiles(userAdminConfig.getDocDomain() + KnowledgePlatformRouteConstant.DOC.SIMPLE_DOC_UPLOAD_URL, Arrays.asList(multipartFile), otherParams, null);
if (StringUtils.isNotBlank(responseData)) {
R result = JSON.parseObject(responseData, R.class);
List<DocResultDto> docResultDtoList = JSON.parseObject(JSONObject.toJSONString(result.getData()), new TypeReference<List<DocResultDto>>() {
});
DocResultDto docResultDto = docResultDtoList.get(0);
// String previewPath = docResultDto.getPreviewPath();
// recordContentPath = previewPath.replaceAll(fileDownloadPath,"");
meetingInfo.setTransDocId(docResultDto.getId());
} else {
processLogService.log(meetingId, subMeetingId, "填充会议纪要失败,上传转录文件到向量知识库失败");
// throw new RuntimeException("填充会议纪要失败");
meetingInfo.setTransDocId("");
}
// //将转录文件存到知识向量库
// Map<String, String> otherParams = new HashMap<>();
// otherParams.put("userId", toUserCode);
// otherParams.put("tenantId", permTenantId);
// otherParams.put("layout", "V1");
// String responseData = HttpClientKnowledgePlatformUtil.sendPostByFormDataFiles(userAdminConfig.getDocDomain() + KnowledgePlatformRouteConstant.DOC.SIMPLE_DOC_UPLOAD_URL, Arrays.asList(multipartFile), otherParams, null);
// if (StringUtils.isNotBlank(responseData)) {
// R result = JSON.parseObject(responseData, R.class);
// List<DocResultDto> docResultDtoList = JSON.parseObject(JSONObject.toJSONString(result.getData()), new TypeReference<List<DocResultDto>>() {
// });
// DocResultDto docResultDto = docResultDtoList.get(0);
//// String previewPath = docResultDto.getPreviewPath();
//// recordContentPath = previewPath.replaceAll(fileDownloadPath,"");
// meetingInfo.setTransDocId(docResultDto.getId());
// } else {
// processLogService.log(meetingId, subMeetingId, "填充会议纪要失败,上传转录文件到向量知识库失败");
//// throw new RuntimeException("填充会议纪要失败");
// meetingInfo.setTransDocId("");
// }
// 将转录文件保存到MinIO
String encryptedRecordData = AESUtils.encrypt(recordData, aesKey);
......
......@@ -34,7 +34,9 @@ CORP_SECRET: 7YLePWG7rJqkQFnAB4FeylqAXpmu7q5qv_NOeSGNbm0
############################################################## email
EMAIL_SENDER: cmeeting_assistant@cimc.com
EMAIL_SMTP_HOST: smtp.office365.com
EMAIL_SENDER_PWD: 4Zno69350R7Hwrj8
EMAIL_SMTP_HOST: mail.ksomail.com
EMAIL_SMTP_PORT: 465
############################################################## permission
PERMISSION_APPLiCATION_ID: 1945681039926104064
......@@ -106,14 +108,11 @@ tencent:
email:
sender: ${EMAIL_SENDER}
# sender-pwd: ${EMAIL_SENDER_PWD}
sender-pwd: ${EMAIL_SENDER_PWD}
smtp-host: ${EMAIL_SMTP_HOST}
smtp-port: ${EMAIL_SMTP_PORT}
push-switch: true #邮件推送总开关,高优先级
environment: prod #test推给本公司人员,prod推给用户
microsoft:
clientId: 'rNvsNtIQTUAD7aYik9/1LDpfO+XbM4nuPkpf131H8vHfJg7Uj7HxJJcHFbtmO4uWagelhTsOTSx2PDqjeuqiYw=='
clientSecret: 'DERmPlHbjiN4rjIFHvpe+OypNRCuSwLe/MBy56EFtI7FkzHfOnVjMdCTRG/CmybBBfha6geFbijC6ymeMVSSeg=='
tenantId: 'dFSC/Jg4ibKnmKWCofo1KfR5IdsvHEHYiAE0iehpiyj0rzes8BRBzvQAu2rjG8SAm8VQetE+sLNvxObI8vq5iA=='
llm:
api-addr: ${LLM_API_ADDR}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论