Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
cmeeting
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
翟斌
cmeeting
Commits
ee9a4269
提交
ee9a4269
authored
6月 04, 2025
作者:
duanxincheng
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
GraphApi接入推送邮件
父级
1a40d520
隐藏空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
212 行增加
和
72 行删除
+212
-72
pom.xml
+0
-3
src/main/java/com/cmeeting/email/EmailSender.java
+187
-54
src/main/java/com/cmeeting/job/CmeetingJob.java
+9
-9
src/main/java/com/cmeeting/job/FileProcessTask.java
+12
-3
src/main/java/com/cmeeting/service/impl/TecentMeetingServiceImpl.java
+2
-2
src/main/resources/mapper/primary/TecentMeetingMapper.xml
+2
-1
没有找到文件。
pom.xml
浏览文件 @
ee9a4269
...
...
@@ -73,9 +73,6 @@
<version>
3.1
</version>
</dependency>
<dependency>
<groupId>
com.microsoft.graph
</groupId>
<artifactId>
microsoft-graph-auth
</artifactId>
<version>
0.3.0
</version>
...
...
src/main/java/com/cmeeting/email/EmailSender.java
浏览文件 @
ee9a4269
package
com
.
cmeeting
.
email
;
import
com.azure.core.credential.AccessToken
;
import
com.azure.core.credential.TokenRequestContext
;
import
com.azure.identity.ClientSecretCredential
;
import
com.azure.identity.ClientSecretCredentialBuilder
;
import
com.cmeeting.log.service.ProcessLogService
;
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
;
...
...
@@ -15,6 +26,11 @@ import javax.mail.internet.*;
import
java.io.File
;
import
java.io.PrintWriter
;
import
java.io.StringWriter
;
import
java.nio.file.Files
;
import
java.nio.file.Paths
;
import
java.util.Arrays
;
import
java.util.Base64
;
import
java.util.LinkedList
;
import
java.util.Properties
;
import
java.util.concurrent.atomic.AtomicInteger
;
...
...
@@ -25,8 +41,10 @@ public class EmailSender {
private
String
SENDER
;
@Value
(
"${email.sender-pwd}"
)
private
String
EMAIL_PWD
;
@Value
(
"${email.smtp-host}"
)
private
String
SMTP_HOST
;
private
String
CLIENT_ID
=
"c06fe7cf-2a89-4099-9805-ce03031938f8"
;
private
String
CLIENT_SECRET
=
"wsu8Q~GxYxPLf2akioQZDRG8NR1EzCAHIAQRVc6u"
;
private
String
TENANT_ID
=
"18653b3e-03c7-499e-8baf-42ef06a814ef"
;
@Resource
private
ProcessLogService
processLogService
;
...
...
@@ -39,33 +57,121 @@ public class EmailSender {
* @param meetingId 会议id
* @return
*/
public
boolean
sendEmailWithAttachment
(
String
toEmail
,
String
subject
,
String
attachmentPath
,
String
meetingId
)
{
// 邮件服务器配置
Properties
props
=
new
Properties
();
props
.
put
(
"mail.smtp.host"
,
SMTP_HOST
);
props
.
put
(
"mail.smtp.auth"
,
"true"
);
// public boolean sendEmailWithAttachment(String toEmail, String subject, String attachmentPath, String meetingId) {
// log.info("sendEmailWithAttachment start...");
// // 邮件服务器配置
// Properties props = new Properties();
// props.put("mail.smtp.host", SMTP_HOST);
// props.put("mail.smtp.auth", "true");
//
// props.put("mail.smtp.port", "587");
// props.put("mail.smtp.starttls.enable", "true"); // 使用TLS
// props.put("mail.smtp.starttls.required", "true");
// props.put("mail.debug", "true");
// props.put("mail.smtp.connectiontimeout", "5000"); // 连接超时时间(毫秒)
// props.put("mail.smtp.timeout", "5000"); // 读取超时时间(毫秒)
// props.remove("mail.smtp.user");
// props.remove("mail.user");
// props.remove("mail.smtp.ssl.enable");
//
// // 创建会话
// log.info("sender->{},email_pwd->{}",SENDER, EMAIL_PWD);
//
// System.getProperties().stringPropertyNames().stream()
// .filter(name -> name.startsWith("mail."))
// .forEach(name -> log.info(name + " = " + System.getProperty(name)));
//
// Session session = Session.getInstance(props); // 不传递 Authenticator
// String body = "您好:\n" +
// "\n" +
// " 附件为您本次会议的会议纪要,烦请下载查看";
//
// AtomicInteger retryCount = new AtomicInteger(0);
// boolean isSent = false;
// if(StringUtils.isEmpty(toEmail)){
// log.error("收件邮箱为空,推送失败");
// processLogService.log(meetingId,null,"收件邮箱为空,推送失败");
// return false;
// }
// log.info("准备开始邮件推送...");
// while (retryCount.intValue() < MAX_RETRY && !isSent){
// try {
// Transport transport = session.getTransport("smtp");
// // 创建邮件消息
// Message message = new MimeMessage(session);
// message.setFrom(new InternetAddress("cmeeting_assistant@cimc.com"));
// message.setRecipients(Message.RecipientType.TO,
// InternetAddress.parse(toEmail));
// message.setSubject(subject);
//
// // 创建消息体部分(正文)
// MimeBodyPart messageBodyPart = new MimeBodyPart();
// messageBodyPart.setText(body);
//
// // 创建多部分消息
// Multipart multipart = new MimeMultipart();
//
// // 添加文本部分
// multipart.addBodyPart(messageBodyPart);
//
// // 添加附件部分
// if (attachmentPath != null && !attachmentPath.isEmpty()) {
// MimeBodyPart attachmentPart = new MimeBodyPart();
// DataSource source = new FileDataSource(attachmentPath);
// attachmentPart.setDataHandler(new DataHandler(source));
// attachmentPart.setFileName(new File(attachmentPath).getName());
// multipart.addBodyPart(attachmentPart);
// }
//
// // 设置完整消息内容
// message.setContent(multipart);
//
// // 发送邮件
// log.info("Transport.send...");
//// Transport.send(message);
// transport.connect("smtp.office365.com", 587, "cmeeting_assistant@cimc.com", "scyou@xih45g6@xih4");
// transport.sendMessage(message, message.getAllRecipients());
// transport.close();
// log.error("邮件已成功发送: meetingId->{}", meetingId);
// isSent = true;
// }catch (Exception e) {
// retryCount.getAndIncrement();
// // 异常处理
// StringWriter sw = new StringWriter();
// PrintWriter pw = new PrintWriter(sw);
// e.printStackTrace(pw);
// processLogService.log(meetingId,null,"【邮件推送异常】:"+sw.toString());
// if (retryCount.intValue() > MAX_RETRY) {
// log.error("邮件发送达到最大重试次数: meetingId->{}", meetingId);
// throw new RuntimeException(e);
// }
// // 指数退避
// try {
// Thread.sleep((long) Math.pow(2, retryCount.intValue()) * 1000);
// } catch (InterruptedException ie) {
// Thread.currentThread().interrupt();
// throw new RuntimeException("邮件发送重试失败", ie);
// }
// }
// }
// return isSent;
// }
// props.put("mail.smtp.port", "465");
// props.put("mail.smtp.ssl.enable", "true"); // 使用SSL
/**
* 发送邮件,带附件
* @param toEmail
* @param subject
* @param attachmentPath
* @param meetingId
* @return
*/
public
boolean
sendEmailWithAttachment
(
String
toEmail
,
String
subject
,
String
attachmentPath
,
String
meetingId
)
{
log
.
info
(
"sendEmailWithAttachment start..."
);
props
.
put
(
"mail.smtp.port"
,
"587"
);
props
.
put
(
"mail.smtp.starttls.enable"
,
"true"
);
// 使用TLS
// 创建会话
log
.
info
(
"sender->{},email_pwd->{}"
,
SENDER
,
EMAIL_PWD
);
// 或者使用SSL
// props.put("mail.smtp.port", "465");
// props.put("mail.smtp.ssl.enable", "true");
// 创建会话
Session
session
=
Session
.
getInstance
(
props
,
new
javax
.
mail
.
Authenticator
()
{
protected
PasswordAuthentication
getPasswordAuthentication
()
{
// return new PasswordAuthentication("cmeeting_assistant@cimc.com", "scyou@xih45g6@xih4");
return
new
PasswordAuthentication
(
SENDER
,
EMAIL_PWD
);
}
});
String
body
=
"您好:\n"
+
"\n"
+
" 附件为您本次会议的会议纪要,烦请下载查看"
;
AtomicInteger
retryCount
=
new
AtomicInteger
(
0
);
boolean
isSent
=
false
;
...
...
@@ -74,42 +180,67 @@ public class EmailSender {
processLogService
.
log
(
meetingId
,
null
,
"收件邮箱为空,推送失败"
);
return
false
;
}
log
.
info
(
"准备开始邮件推送..."
);
while
(
retryCount
.
intValue
()
<
MAX_RETRY
&&
!
isSent
){
try
{
// 创建邮件消息
Message
message
=
new
MimeMessage
(
session
);
message
.
setFrom
(
new
InternetAddress
(
SENDER
));
message
.
setRecipients
(
Message
.
RecipientType
.
TO
,
InternetAddress
.
parse
(
toEmail
));
message
.
setSubject
(
subject
);
// 创建消息体部分(正文)
MimeBodyPart
messageBodyPart
=
new
MimeBodyPart
();
messageBodyPart
.
setText
(
body
);
// 创建多部分消息
Multipart
multipart
=
new
MimeMultipart
();
// 添加文本部分
multipart
.
addBodyPart
(
messageBodyPart
);
// 添加附件部分
if
(
attachmentPath
!=
null
&&
!
attachmentPath
.
isEmpty
())
{
MimeBodyPart
attachmentPart
=
new
MimeBodyPart
();
DataSource
source
=
new
FileDataSource
(
attachmentPath
);
attachmentPart
.
setDataHandler
(
new
DataHandler
(
source
));
attachmentPart
.
setFileName
(
new
File
(
attachmentPath
).
getName
());
multipart
.
addBodyPart
(
attachmentPart
);
ClientSecretCredential
clientSecretCredential
=
new
ClientSecretCredentialBuilder
()
.
clientId
(
CLIENT_ID
)
.
clientSecret
(
CLIENT_SECRET
)
.
tenantId
(
TENANT_ID
)
.
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
());
}
// 设置完整消息内容
message
.
setContent
(
multipart
);
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
.
TEXT
;
body
.
content
=
"您好:\n\n 附件为您本次会议的会议纪要,烦请下载查看"
;
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
<>();
byte
[]
recordXmlData
=
Files
.
readAllBytes
(
Paths
.
get
(
attachmentPath
));
FileAttachment
attachments
=
new
FileAttachment
();
attachments
.
name
=
attachmentPath
.
substring
(
attachmentPath
.
lastIndexOf
(
"/"
)
+
1
);
attachments
.
oDataType
=
"#microsoft.graph.fileAttachment"
;
attachments
.
contentType
=
"application/msword"
;
attachments
.
contentBytes
=
recordXmlData
;
attachmentsList
.
add
(
attachments
);
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
();
// 发送邮件
Transport
.
send
(
message
);
log
.
error
(
"邮件已成功发送: meetingId->{}"
,
meetingId
);
isSent
=
true
;
}
catch
(
Messaging
Exception
e
)
{
}
catch
(
Exception
e
)
{
retryCount
.
getAndIncrement
();
// 异常处理
StringWriter
sw
=
new
StringWriter
();
...
...
@@ -131,4 +262,5 @@ public class EmailSender {
}
return
isSent
;
}
}
\ No newline at end of file
src/main/java/com/cmeeting/job/CmeetingJob.java
浏览文件 @
ee9a4269
...
...
@@ -38,15 +38,15 @@ public class CmeetingJob {
@Autowired
private
FileProcessProducer
producer
;
//
@PostConstruct
//
public void weComUserInit(){
//
weComUserSync();
//
}
//
//
@PostConstruct
//
public void tencentUserInit(){
//
TencentUserSync();
//
}
@PostConstruct
public
void
weComUserInit
(){
weComUserSync
();
}
@PostConstruct
public
void
tencentUserInit
(){
TencentUserSync
();
}
/**
* 企微人员定时同步
...
...
src/main/java/com/cmeeting/job/FileProcessTask.java
浏览文件 @
ee9a4269
...
...
@@ -267,9 +267,9 @@ public class FileProcessTask {
// 将xml格式的内容转换为特殊json
String
json
=
convertXmlToJSON
(
xml
);
String
recordContentPath
=
meetingId
+
"-recordContent-"
+
IdUtil
.
fastSimpleUUID
()
+
"txt"
;
minioUtils
.
upload
(
recordContentPath
,
recordData
);
String
recordXmlPath
=
meetingId
+
"-recordXmlPath-"
+
IdUtil
.
fastSimpleUUID
()
+
"xml"
;
try
{
minioUtils
.
upload
(
recordContentPath
,
recordData
);
//写入json文件,这里的json文件用于用户可编辑
Files
.
write
(
Paths
.
get
(
targetPath
),
json
.
getBytes
());
log
.
info
(
"json文件已保存到: {}"
,
targetPath
);
...
...
@@ -312,8 +312,17 @@ public class FileProcessTask {
if
(
meetingInfo
.
getEmailPushAccess
()){
log
.
info
(
"用户允许邮件推送,准备推送邮件至{}------"
,
meetingInfo
.
getEmail
());
//邮件推送
// isPushed = emailSender.sendEmailWithAttachment("duanxincheng@chatbot.cn",meetingName,path + targetFileName + ".docx",meetingId);
isPushed
=
emailSender
.
sendEmailWithAttachment
(
meetingInfo
.
getEmail
(),
meetingName
,
path
+
targetFileName
+
".docx"
,
meetingId
);
isPushed
=
emailSender
.
sendEmailWithAttachment
(
"duanxincheng@chatbot.cn"
,
meetingName
,
path
+
targetFileName
+
".docx"
,
meetingId
);
try
{
// isPushed = emailSender.sendEmailWithAttachment(meetingInfo.getEmail(),meetingName,path + targetFileName + ".docx",meetingId);
}
catch
(
Exception
e
)
{
log
.
error
(
"邮件推送失败: {}"
,
e
.
getMessage
(),
e
);
StringWriter
sw
=
new
StringWriter
();
PrintWriter
pw
=
new
PrintWriter
(
sw
);
e
.
printStackTrace
(
pw
);
processLogService
.
log
(
meetingId
,
subMeetingId
,
"【邮件推送】:"
+
sw
.
toString
());
throw
new
RuntimeException
(
e
);
}
if
(
isPushed
)
processLogService
.
log
(
meetingId
,
subMeetingId
,
"用户允许邮件推送,推送邮件至"
+
meetingInfo
.
getEmail
());
// emailSender.sendEmailWithAttachment("xuwentao@chatbot.cn",meetingName,path + targetFileName + ".docx",meetingId);
...
...
src/main/java/com/cmeeting/service/impl/TecentMeetingServiceImpl.java
浏览文件 @
ee9a4269
...
...
@@ -103,7 +103,7 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
// 检查重名并设置标志
markDuplicateNamesTecent
(
users
);
// 批量插入数据库(分批次)
int
batchSize
=
10
;
int
batchSize
=
10
00
;
for
(
int
i
=
0
;
i
<
users
.
size
();
i
+=
batchSize
)
{
List
<
TencentMeetingUser
>
batch
=
users
.
subList
(
i
,
Math
.
min
(
i
+
batchSize
,
users
.
size
()));
batchInsert
(
batch
);
...
...
@@ -123,7 +123,7 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
// 查询近两天的会议录制列表
try
{
ZonedDateTime
now
=
ZonedDateTime
.
now
();
String
startTime
=
String
.
valueOf
(
now
.
minusDays
(
5
).
toEpochSecond
());
String
startTime
=
String
.
valueOf
(
now
.
minusDays
(
2
).
toEpochSecond
());
String
endTime
=
String
.
valueOf
(
now
.
toEpochSecond
());
AtomicInteger
currentPage
=
new
AtomicInteger
(
1
);
Long
totalPage
=
1L
;
...
...
src/main/resources/mapper/primary/TecentMeetingMapper.xml
浏览文件 @
ee9a4269
...
...
@@ -22,6 +22,6 @@
SELECT * FROM user_tencentmeeting
</select>
<select
id=
"getUsernameByUserId"
resultType=
"java.lang.String"
>
SELECT user_name FROM user_tencentmeeting where user_id = #{userId}
SELECT user_name FROM user_tencentmeeting where user_id = #{userId}
limit 1
</select>
</mapper>
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论