Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
cmeeting
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
翟斌
cmeeting
Commits
2089d7b6
提交
2089d7b6
authored
6月 09, 2025
作者:
duanxincheng
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
纪要模板上传,邮件推送服务迭代,历史会议纪要接口(包括导出原文,导出会议纪要)
父级
4ce8c62e
隐藏空白字符变更
内嵌
并排
正在显示
21 个修改的文件
包含
1207 行增加
和
270 行删除
+1207
-270
src/main/java/com/cmeeting/config/ThreadPoolConfig.java
+2
-2
src/main/java/com/cmeeting/controller/MeetingInfoController.java
+259
-0
src/main/java/com/cmeeting/controller/RecordTemplateController.java
+29
-2
src/main/java/com/cmeeting/controller/TencentMeetingController.java
+2
-14
src/main/java/com/cmeeting/email/EmailSender.java
+18
-13
src/main/java/com/cmeeting/job/CmeetingJob.java
+1
-1
src/main/java/com/cmeeting/job/EmailPushTask.java
+14
-32
src/main/java/com/cmeeting/job/FileProcessTask.java
+102
-51
src/main/java/com/cmeeting/pojo/MeetingInfo.java
+10
-0
src/main/java/com/cmeeting/pojo/MeetingRecordTemplate.java
+6
-0
src/main/java/com/cmeeting/service/MeetingInfoService.java
+5
-0
src/main/java/com/cmeeting/service/MeetingRecordTemplateService.java
+10
-0
src/main/java/com/cmeeting/service/impl/MeetingInfoServiceImpl.java
+76
-0
src/main/java/com/cmeeting/service/impl/MeetingRecordTemplateServiceImpl.java
+55
-0
src/main/java/com/cmeeting/service/impl/TecentMeetingServiceImpl.java
+344
-146
src/main/java/com/cmeeting/util/MinioUtils.java
+16
-9
src/main/java/com/cmeeting/util/SignatureUtil.java
+41
-0
src/main/java/com/cmeeting/vo/CorpRecordsVO.java
+52
-0
src/main/java/com/cmeeting/vo/EmailPush.java
+38
-0
src/main/java/com/cmeeting/vo/MeetingInfoVO.java
+127
-0
src/main/resources/template/data_net_template.docx
+0
-0
没有找到文件。
src/main/java/com/cmeeting/config/ThreadPoolConfig.java
浏览文件 @
2089d7b6
...
@@ -15,9 +15,9 @@ public class ThreadPoolConfig {
...
@@ -15,9 +15,9 @@ public class ThreadPoolConfig {
public
ThreadPoolTaskExecutor
fileProcessExecutor
()
{
public
ThreadPoolTaskExecutor
fileProcessExecutor
()
{
ThreadPoolTaskExecutor
executor
=
new
ThreadPoolTaskExecutor
();
ThreadPoolTaskExecutor
executor
=
new
ThreadPoolTaskExecutor
();
// 核心线程数 (CPU密集型任务建议核心数+1)
// 核心线程数 (CPU密集型任务建议核心数+1)
executor
.
setCorePoolSize
(
4
);
// 固定核心线程数,避免动态获取CPU核心数
executor
.
setCorePoolSize
(
2
);
// 固定核心线程数,避免动态获取CPU核心数
// 最大线程数
// 最大线程数
executor
.
setMaxPoolSize
(
4
);
executor
.
setMaxPoolSize
(
2
);
// 队列容量
// 队列容量
executor
.
setQueueCapacity
(
1000
);
executor
.
setQueueCapacity
(
1000
);
// 线程名前缀
// 线程名前缀
...
...
src/main/java/com/cmeeting/controller/MeetingInfoController.java
0 → 100644
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
controller
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.io.IoUtil
;
import
cn.hutool.core.util.IdUtil
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.cmeeting.email.EmailSender
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.service.MeetingInfoService
;
import
com.cmeeting.util.MinioUtils
;
import
com.cmeeting.util.R
;
import
com.cmeeting.vo.EmailPush
;
import
com.cmeeting.vo.MeetingInfoVO
;
import
com.deepoove.poi.XWPFTemplate
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.dataformat.xml.XmlMapper
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.poi.xwpf.usermodel.XWPFDocument
;
import
org.apache.poi.xwpf.usermodel.XWPFParagraph
;
import
org.apache.poi.xwpf.usermodel.XWPFRun
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.core.io.ClassPathResource
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.ByteArrayOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.net.URLEncoder
;
import
java.nio.charset.StandardCharsets
;
import
java.time.format.DateTimeFormatter
;
import
java.util.*
;
import
java.util.concurrent.ConcurrentHashMap
;
@Slf4j
@RestController
@RequestMapping
(
"/meetingInfo"
)
public
class
MeetingInfoController
{
@Resource
private
MeetingInfoService
meetingInfoService
;
@Resource
private
MinioUtils
minioUtils
;
@Resource
private
EmailSender
emailSender
;
@PostMapping
(
"/updateRecordXml"
)
public
R
updateRecordXml
(
@RequestBody
MeetingInfoVO
vo
)
{
boolean
save
=
meetingInfoService
.
updateRecordXml
(
vo
);
return
R
.
ok
(
save
);
}
@PostMapping
(
"/list"
)
public
R
list
(
@RequestBody
MeetingInfoVO
vo
)
{
IPage
<
MeetingInfo
>
page
=
meetingInfoService
.
getPage
(
vo
);
return
R
.
ok
(
page
);
}
@PostMapping
(
"/detail"
)
public
R
detail
(
@RequestBody
MeetingInfoVO
vo
)
{
MeetingInfo
meetingInfo
=
meetingInfoService
.
getById
(
vo
.
getId
());
BeanUtils
.
copyProperties
(
meetingInfo
,
vo
);
String
recordXml
=
vo
.
getRecordXml
();
String
recordContent
=
vo
.
getRecordContent
();
try
{
if
(
StringUtils
.
isNotEmpty
(
recordXml
)){
//xml转json,用于前端的表单回显
String
xml
=
minioUtils
.
getFileText
(
recordXml
);
String
json
=
convertXmlToJSON
(
xml
);
vo
.
setRecordJson
(
json
);
}
if
(
StringUtils
.
isNotEmpty
(
recordContent
)){
vo
.
setRecordContent
(
minioUtils
.
getFileText
(
recordContent
));
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
return
R
.
ok
(
vo
);
}
/**
* 导出转写原文
*/
@PostMapping
(
"/exportMeetingRecord"
)
public
void
exportMeetingRecord
(
@RequestBody
MeetingInfoVO
vo
,
HttpServletResponse
response
){
try
{
MeetingInfo
meetingInfo
=
meetingInfoService
.
getById
(
vo
.
getId
());
String
content
=
minioUtils
.
getFileText
(
meetingInfo
.
getRecordContent
());
String
fileName
=
String
.
format
(
meetingInfo
.
getSubject
()
+
"_转写原文_%s.docx"
,
DateUtil
.
today
());
response
.
setContentType
(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
response
.
setCharacterEncoding
(
"utf-8"
);
response
.
setHeader
(
"Content-Disposition"
,
"attachment;filename="
+
URLEncoder
.
encode
(
fileName
,
"utf8"
));
XWPFTemplate
template
;
try
(
XWPFDocument
document
=
new
XWPFDocument
();
OutputStream
os
=
response
.
getOutputStream
())
{
// 将文本按行分割并逐行添加到 Word 文档
String
[]
lines
=
content
.
split
(
"\\r?\\n"
);
for
(
String
line
:
lines
)
{
XWPFParagraph
paragraph
=
document
.
createParagraph
();
XWPFRun
run
=
paragraph
.
createRun
();
run
.
setText
(
line
);
}
// 输出文档
document
.
write
(
os
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
catch
(
Exception
e
)
{
response
.
reset
();
response
.
setContentType
(
"text/plain"
);
response
.
setCharacterEncoding
(
"utf-8"
);
e
.
printStackTrace
();
}
}
/**
* 导出会议纪要(本地下载)
*/
@PostMapping
(
"/downloadMeetingMinutes"
)
public
void
downloadMeetingMinutes
(
@RequestBody
MeetingInfoVO
vo
,
HttpServletResponse
response
){
try
{
MeetingInfo
meetingInfo
=
meetingInfoService
.
getById
(
vo
.
getId
());
String
xml
=
minioUtils
.
getFileText
(
meetingInfo
.
getRecordXml
());
Map
<
String
,
Object
>
dataModel
=
convertXmlToMap
(
xml
);
//追加参会人员信息
Map
<
String
,
Object
>
participantsMap
=
new
ConcurrentHashMap
<>();
DateTimeFormatter
df
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd"
);
String
meetingDate
=
meetingInfo
.
getStartTime
().
toLocalDate
().
format
(
df
);
participantsMap
.
put
(
"meeting_date"
,
meetingDate
);
participantsMap
.
put
(
"meeting_location"
,
"线上腾讯会议"
);
participantsMap
.
put
(
"meeting_participants"
,
meetingInfo
.
getParticipantUsers
());
participantsMap
.
put
(
"meeting_host"
,
meetingInfo
.
getHost
());
dataModel
.
putAll
(
participantsMap
);
ClassPathResource
resource
=
new
ClassPathResource
(
"template/data_network.docx"
);
String
fileName
=
String
.
format
(
meetingInfo
.
getSubject
()
+
"_会议纪要_%s.docx"
,
DateUtil
.
today
());
response
.
setContentType
(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
response
.
setCharacterEncoding
(
"utf-8"
);
response
.
setHeader
(
"Content-Disposition"
,
"attachment;filename="
+
URLEncoder
.
encode
(
fileName
,
"utf8"
));
XWPFTemplate
template
;
try
(
InputStream
is
=
resource
.
getInputStream
();
OutputStream
os
=
response
.
getOutputStream
())
{
template
=
XWPFTemplate
.
compile
(
is
).
render
(
dataModel
);
template
.
write
(
os
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
catch
(
Exception
e
)
{
response
.
reset
();
response
.
setContentType
(
"text/plain"
);
response
.
setCharacterEncoding
(
"utf-8"
);
e
.
printStackTrace
();
}
}
/**
* 导出会议纪要(推送邮件)
*/
@PostMapping
(
"/pushMeetingMinutes"
)
public
R
exportMeetingMinutes
(
@RequestBody
MeetingInfoVO
vo
,
HttpServletResponse
response
){
try
{
MeetingInfo
meetingInfo
=
meetingInfoService
.
getById
(
vo
.
getId
());
String
xml
=
minioUtils
.
getFileText
(
meetingInfo
.
getRecordXml
());
Map
<
String
,
Object
>
dataModel
=
convertXmlToMap
(
xml
);
//追加参会人员信息
Map
<
String
,
Object
>
participantsMap
=
new
ConcurrentHashMap
<>();
DateTimeFormatter
df
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd"
);
String
meetingDate
=
meetingInfo
.
getStartTime
().
toLocalDate
().
format
(
df
);
participantsMap
.
put
(
"meeting_date"
,
meetingDate
);
participantsMap
.
put
(
"meeting_location"
,
"线上腾讯会议"
);
participantsMap
.
put
(
"meeting_participants"
,
meetingInfo
.
getParticipantUsers
());
participantsMap
.
put
(
"meeting_host"
,
meetingInfo
.
getHost
());
dataModel
.
putAll
(
participantsMap
);
ClassPathResource
resource
=
new
ClassPathResource
(
"template/data_network.docx"
);
XWPFTemplate
template
;
byte
[]
meetingMinutesBytes
;
try
(
InputStream
is
=
resource
.
getInputStream
();
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
())
{
template
=
XWPFTemplate
.
compile
(
is
).
render
(
dataModel
);
template
.
write
(
baos
);
meetingMinutesBytes
=
baos
.
toByteArray
();
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
List
<
EmailPush
.
Attachment
>
attachments
=
new
ArrayList
<>();
EmailPush
.
Attachment
attachment
=
EmailPush
.
Attachment
.
builder
().
name
(
meetingInfo
.
getSubject
()+
"_数据网络中心"
).
bytes
(
meetingMinutesBytes
).
build
();
attachments
.
add
(
attachment
);
EmailPush
emailPushBuilder
=
EmailPush
.
builder
()
.
toEmail
(
meetingInfo
.
getEmail
())
.
meetingId
(
meetingInfo
.
getMeetingId
())
.
attachments
(
attachments
)
.
subject
(
meetingInfo
.
getSubject
()).
build
();
emailSender
.
sendEmailWithAttachment
(
emailPushBuilder
);
return
R
.
ok
(
"发送纪要邮件成功"
);
}
catch
(
Exception
e
)
{
response
.
reset
();
response
.
setContentType
(
"text/plain"
);
response
.
setCharacterEncoding
(
"utf-8"
);
e
.
printStackTrace
();
return
R
.
error
(
"发送纪要邮件失败"
);
}
}
public
static
Map
<
String
,
Object
>
convertXmlToMap
(
String
xml
)
throws
Exception
{
XmlMapper
xmlMapper
=
new
XmlMapper
();
ObjectMapper
objectMapper
=
new
ObjectMapper
();
// 先将 XML 读取为 Map
Map
<?,
?>
xmlMap
=
xmlMapper
.
readValue
(
xml
,
Map
.
class
);
// 转换为更标准的 Map<String, Object>
Map
<
String
,
Object
>
map
=
objectMapper
.
convertValue
(
xmlMap
,
Map
.
class
);
//特殊处理map格式
for
(
Map
.
Entry
<
String
,
Object
>
entry
:
map
.
entrySet
())
{
Map
<
String
,
Object
>
value
=
(
Map
<
String
,
Object
>)
entry
.
getValue
();
Object
realValue
=
value
.
get
(
""
);
entry
.
setValue
(
realValue
);
}
return
map
;
}
private
String
convertXmlToJSON
(
String
xml
)
{
String
json
;
try
{
XmlMapper
xmlMapper
=
new
XmlMapper
();
JsonNode
rootNode
=
xmlMapper
.
readTree
(
xml
.
getBytes
());
// 正确获取节点和属性的方式
List
<
Map
>
list
=
new
ArrayList
<>();
Iterator
<
String
>
iterator
=
rootNode
.
fieldNames
();
while
(
iterator
.
hasNext
()){
String
tagName
=
iterator
.
next
();
JsonNode
subNode
=
rootNode
.
path
(
tagName
);
String
displayName
=
subNode
.
path
(
"label"
).
asText
();
String
content
=
subNode
.
path
(
""
).
asText
();
list
.
add
(
new
HashMap
(){{
put
(
"key"
,
tagName
);
put
(
"keyName"
,
displayName
);
put
(
"value"
,
content
);
}});
}
// 构建JSON对象
ObjectMapper
jsonMapper
=
new
ObjectMapper
();
json
=
jsonMapper
.
writeValueAsString
(
list
);
}
catch
(
IOException
e
)
{
log
.
error
(
e
.
getMessage
());
throw
new
RuntimeException
(
e
.
getMessage
());
}
return
json
;
}
}
src/main/java/com/cmeeting/controller/RecordTemplateController.java
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
controller
;
package
com
.
cmeeting
.
controller
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.service.MeetingRecordTemplateService
;
import
com.cmeeting.service.MeetingRecordTemplateService
;
import
com.cmeeting.util.R
;
import
com.cmeeting.util.R
;
import
com.cmeeting.vo.RecordTemplateVO
;
import
com.cmeeting.vo.RecordTemplateVO
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
...
@@ -45,9 +44,37 @@ public class RecordTemplateController {
...
@@ -45,9 +44,37 @@ public class RecordTemplateController {
return
R
.
ok
(
save
);
return
R
.
ok
(
save
);
}
}
/**
* 纪要模板列表,通过type区分为系统模板还是自定义模板
* @param vo
* @return
*/
@PostMapping
(
"/list"
)
@PostMapping
(
"/list"
)
public
R
list
(
@RequestBody
RecordTemplateVO
vo
)
{
public
R
list
(
@RequestBody
RecordTemplateVO
vo
)
{
IPage
<
MeetingRecordTemplate
>
page
=
recordTemplateService
.
getPage
(
vo
);
IPage
<
MeetingRecordTemplate
>
page
=
recordTemplateService
.
getPage
(
vo
);
return
R
.
ok
(
page
);
return
R
.
ok
(
page
);
}
}
/**
* 普通用户纪要模板,查出有授权的系统模板和个人创建的自定义模板
* @param vo
* @return
*/
@PostMapping
(
"/personalList"
)
public
R
personalList
(
@RequestBody
RecordTemplateVO
vo
)
{
IPage
<
MeetingRecordTemplate
>
page
=
recordTemplateService
.
getPersonalPage
(
vo
);
return
R
.
ok
(
page
);
}
@PostMapping
(
"/detail"
)
public
R
detail
(
@RequestBody
RecordTemplateVO
vo
)
{
MeetingRecordTemplate
detail
=
recordTemplateService
.
getById
(
vo
.
getId
());
return
R
.
ok
(
detail
);
}
@PostMapping
(
"/templateUpload"
)
public
R
templateUpload
(
@RequestParam
(
"file"
)
MultipartFile
file
,
@RequestParam
(
"id"
)
Integer
id
)
{
String
template
=
recordTemplateService
.
templateUpload
(
file
,
id
);
return
R
.
ok
(
template
);
}
}
}
src/main/java/com/cmeeting/controller/TencentMeetingController.java
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
controller
;
package
com
.
cmeeting
.
controller
;
import
com.cmeeting.pojo.TencentMeetingUser
;
import
com.cmeeting.service.TecentMeetingService
;
import
com.cmeeting.service.TecentMeetingService
;
import
com.cmeeting.vo.TencentMeetingVO
;
import
com.tencentcloudapi.wemeet.Client
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.util.List
;
@RestController
@RestController
@RequestMapping
(
"/tencent"
)
@RequestMapping
(
"/tencent
Meeting
"
)
public
class
TencentMeetingController
{
public
class
TencentMeetingController
{
@Autowired
@Autowired
private
TecentMeetingService
tecentMeetingService
;
private
TecentMeetingService
tecentMeetingService
;
...
@@ -22,9 +15,4 @@ public class TencentMeetingController {
...
@@ -22,9 +15,4 @@ public class TencentMeetingController {
public
void
addUsers
()
{
public
void
addUsers
()
{
tecentMeetingService
.
doUsers
();
tecentMeetingService
.
doUsers
();
}
}
// @GetMapping("/getMeetingFiles")
// public void getMeetingFiles(){
// tecentMeetingService.getMeetingFiles();
// }
}
}
src/main/java/com/cmeeting/email/EmailSender.java
浏览文件 @
2089d7b6
...
@@ -5,6 +5,7 @@ import com.azure.core.credential.TokenRequestContext;
...
@@ -5,6 +5,7 @@ import com.azure.core.credential.TokenRequestContext;
import
com.azure.identity.ClientSecretCredential
;
import
com.azure.identity.ClientSecretCredential
;
import
com.azure.identity.ClientSecretCredentialBuilder
;
import
com.azure.identity.ClientSecretCredentialBuilder
;
import
com.cmeeting.log.service.ProcessLogService
;
import
com.cmeeting.log.service.ProcessLogService
;
import
com.cmeeting.vo.EmailPush
;
import
com.microsoft.graph.authentication.TokenCredentialAuthProvider
;
import
com.microsoft.graph.authentication.TokenCredentialAuthProvider
;
import
com.microsoft.graph.models.*
;
import
com.microsoft.graph.models.*
;
import
com.microsoft.graph.models.Message
;
import
com.microsoft.graph.models.Message
;
...
@@ -31,6 +32,7 @@ import java.nio.file.Paths;
...
@@ -31,6 +32,7 @@ import java.nio.file.Paths;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Base64
;
import
java.util.Base64
;
import
java.util.LinkedList
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Properties
;
import
java.util.Properties
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.concurrent.atomic.AtomicInteger
;
...
@@ -159,13 +161,10 @@ public class EmailSender {
...
@@ -159,13 +161,10 @@ public class EmailSender {
/**
/**
* 发送邮件,带附件
* 发送邮件,带附件
* @param toEmail
* @param emailPushBuilder
* @param subject
* @param attachmentPath
* @param meetingId
* @return
* @return
*/
*/
public
boolean
sendEmailWithAttachment
(
String
toEmail
,
String
subject
,
String
attachmentPath
,
String
meetingId
)
{
public
boolean
sendEmailWithAttachment
(
EmailPush
emailPushBuilder
)
{
log
.
info
(
"sendEmailWithAttachment start..."
);
log
.
info
(
"sendEmailWithAttachment start..."
);
// 创建会话
// 创建会话
...
@@ -175,6 +174,10 @@ public class EmailSender {
...
@@ -175,6 +174,10 @@ public class EmailSender {
AtomicInteger
retryCount
=
new
AtomicInteger
(
0
);
AtomicInteger
retryCount
=
new
AtomicInteger
(
0
);
boolean
isSent
=
false
;
boolean
isSent
=
false
;
// String toEmail = emailPushBuilder.getToEmail();
String
toEmail
=
"duanxincheng@chatbot.cn"
;
String
subject
=
emailPushBuilder
.
getSubject
();
String
meetingId
=
emailPushBuilder
.
getMeetingId
();
if
(
StringUtils
.
isEmpty
(
toEmail
)){
if
(
StringUtils
.
isEmpty
(
toEmail
)){
log
.
error
(
"收件邮箱为空,推送失败"
);
log
.
error
(
"收件邮箱为空,推送失败"
);
processLogService
.
log
(
meetingId
,
null
,
"收件邮箱为空,推送失败"
);
processLogService
.
log
(
meetingId
,
null
,
"收件邮箱为空,推送失败"
);
...
@@ -204,7 +207,7 @@ public class EmailSender {
...
@@ -204,7 +207,7 @@ public class EmailSender {
GraphServiceClient
<
Request
>
graphClient
=
GraphServiceClient
.
builder
().
authenticationProvider
(
tokenCredAuthProvider
).
buildClient
();
GraphServiceClient
<
Request
>
graphClient
=
GraphServiceClient
.
builder
().
authenticationProvider
(
tokenCredAuthProvider
).
buildClient
();
com
.
microsoft
.
graph
.
models
.
Message
message
=
new
Message
();
com
.
microsoft
.
graph
.
models
.
Message
message
=
new
Message
();
message
.
subject
=
subject
;
message
.
subject
=
subject
+
"会议纪要"
;
ItemBody
body
=
new
ItemBody
();
ItemBody
body
=
new
ItemBody
();
body
.
contentType
=
BodyType
.
TEXT
;
body
.
contentType
=
BodyType
.
TEXT
;
body
.
content
=
"您好:\n\n 附件为您本次会议的会议纪要,烦请下载查看"
;
body
.
content
=
"您好:\n\n 附件为您本次会议的会议纪要,烦请下载查看"
;
...
@@ -218,13 +221,15 @@ public class EmailSender {
...
@@ -218,13 +221,15 @@ public class EmailSender {
message
.
toRecipients
=
toRecipientsList
;
message
.
toRecipients
=
toRecipientsList
;
//构建附件
//构建附件
LinkedList
<
Attachment
>
attachmentsList
=
new
LinkedList
<>();
LinkedList
<
Attachment
>
attachmentsList
=
new
LinkedList
<>();
byte
[]
recordXmlData
=
Files
.
readAllBytes
(
Paths
.
get
(
attachmentPath
));
for
(
EmailPush
.
Attachment
attachment
:
emailPushBuilder
.
getAttachments
())
{
FileAttachment
attachments
=
new
FileAttachment
();
FileAttachment
attachments
=
new
FileAttachment
();
attachments
.
name
=
attachmentPath
.
substring
(
attachmentPath
.
lastIndexOf
(
"/"
)
+
1
);
attachments
.
name
=
attachment
.
getName
()
+
".docx"
;
attachments
.
oDataType
=
"#microsoft.graph.fileAttachment"
;
attachments
.
oDataType
=
"#microsoft.graph.fileAttachment"
;
attachments
.
contentType
=
"application/msword"
;
attachments
.
contentType
=
"application/msword"
;
attachments
.
contentBytes
=
recordXmlData
;
attachments
.
contentBytes
=
attachment
.
getBytes
();
attachmentsList
.
add
(
attachments
);
attachmentsList
.
add
(
attachments
);
}
AttachmentCollectionResponse
attachmentCollectionResponse
=
new
AttachmentCollectionResponse
();
AttachmentCollectionResponse
attachmentCollectionResponse
=
new
AttachmentCollectionResponse
();
attachmentCollectionResponse
.
value
=
attachmentsList
;
attachmentCollectionResponse
.
value
=
attachmentsList
;
...
...
src/main/java/com/cmeeting/job/CmeetingJob.java
浏览文件 @
2089d7b6
...
@@ -134,7 +134,7 @@ public class CmeetingJob {
...
@@ -134,7 +134,7 @@ public class CmeetingJob {
}
}
// @Scheduled(fixedRate = 5 * 60 * 1000,initialDelay = 2 * 60 * 1000)
// @Scheduled(fixedRate = 5 * 60 * 1000,initialDelay = 2 * 60 * 1000)
@Scheduled
(
fixedRate
=
5
*
60
*
1000
)
@Scheduled
(
fixedRate
=
10
*
60
*
1000
)
public
void
execute
()
{
public
void
execute
()
{
// 定义时间格式化器
// 定义时间格式化器
DateTimeFormatter
formatter
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
);
DateTimeFormatter
formatter
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
);
...
...
src/main/java/com/cmeeting/job/EmailPushTask.java
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
job
;
package
com
.
cmeeting
.
job
;
import
cn.chatbot.openai.completion.chat.ChatCompletionRequest
;
import
cn.chatbot.openai.completion.chat.ChatMessage
;
import
cn.chatbot.openai.completion.chat.ChatMessageRole
;
import
cn.chatbot.openai.completion.chat.Message
;
import
cn.chatbot.openai.service.LLMService
;
import
cn.hutool.core.util.IdUtil
;
import
cn.hutool.core.util.IdUtil
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
...
@@ -12,48 +7,27 @@ import com.cmeeting.email.EmailSender;
...
@@ -12,48 +7,27 @@ import com.cmeeting.email.EmailSender;
import
com.cmeeting.mapper.primary.MeetingInfoMapper
;
import
com.cmeeting.mapper.primary.MeetingInfoMapper
;
import
com.cmeeting.mapper.primary.MeetingRecordTemplateMapper
;
import
com.cmeeting.mapper.primary.MeetingRecordTemplateMapper
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.util.MinioUtils
;
import
com.cmeeting.util.MinioUtils
;
import
com.cmeeting.vo.EmailPush
;
import
com.deepoove.poi.XWPFTemplate
;
import
com.deepoove.poi.XWPFTemplate
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.dataformat.xml.XmlMapper
;
import
com.fasterxml.jackson.dataformat.xml.XmlMapper
;
import
com.tencentcloudapi.wemeet.Client
;
import
com.tencentcloudapi.wemeet.core.authenticator.AuthenticatorBuilder
;
import
com.tencentcloudapi.wemeet.core.authenticator.JWTAuthenticator
;
import
com.tencentcloudapi.wemeet.service.meetings.api.MeetingsApi
;
import
com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200Response
;
import
com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner
;
import
com.tencentcloudapi.wemeet.service.records.api.RecordsApi
;
import
com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200Response
;
import
com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200ResponseAiMeetingTranscriptsInner
;
import
lombok.Data
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
lombok.NoArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
okhttp3.OkHttpClient
;
import
org.apache.commons.io.IOUtils
;
import
okhttp3.Request
;
import
okhttp3.Response
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.poi.xwpf.extractor.XWPFWordExtractor
;
import
org.apache.poi.xwpf.usermodel.XWPFDocument
;
import
org.springframework.core.io.ClassPathResource
;
import
org.springframework.core.io.ClassPathResource
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
import
java.io.*
;
import
java.io.*
;
import
java.math.BigInteger
;
import
java.nio.charset.Charset
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.file.Files
;
import
java.nio.file.Files
;
import
java.nio.file.Paths
;
import
java.nio.file.Paths
;
import
java.security.SecureRandom
;
import
java.time.Instant
;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
import
java.time.ZoneId
;
import
java.time.format.DateTimeFormatter
;
import
java.time.format.DateTimeFormatter
;
import
java.util.*
;
import
java.util.*
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.stream.Collectors
;
@Data
@Data
@NoArgsConstructor
@NoArgsConstructor
...
@@ -105,9 +79,9 @@ public class EmailPushTask {
...
@@ -105,9 +79,9 @@ public class EmailPushTask {
dataModel
.
putAll
(
participantsMap
);
dataModel
.
putAll
(
participantsMap
);
String
path
=
Thread
.
currentThread
().
getContextClassLoader
().
getResource
(
""
).
getPath
();
String
path
=
Thread
.
currentThread
().
getContextClassLoader
().
getResource
(
""
).
getPath
();
ClassPathResource
resource
=
new
ClassPathResource
(
"template/data_net
_template
.docx"
);
ClassPathResource
resource
=
new
ClassPathResource
(
"template/data_net
work
.docx"
);
meetingName
=
dataModel
.
get
(
"meeting_name"
)
!=
null
?
String
.
valueOf
(
dataModel
.
get
(
"meeting_name"
))
:
"腾讯会议纪要"
;
meetingName
=
dataModel
.
get
(
"meeting_name"
)
!=
null
?
String
.
valueOf
(
dataModel
.
get
(
"meeting_name"
))
:
"腾讯会议纪要"
;
targetFileName
=
meetingName
+
"_
"
+
nowTime
;
targetFileName
=
meetingName
+
"_
data_network"
;
try
(
InputStream
inputStream
=
resource
.
getInputStream
())
{
try
(
InputStream
inputStream
=
resource
.
getInputStream
())
{
XWPFTemplate
template
=
XWPFTemplate
.
compile
(
inputStream
).
render
(
dataModel
);
XWPFTemplate
template
=
XWPFTemplate
.
compile
(
inputStream
).
render
(
dataModel
);
template
.
writeAndClose
(
new
FileOutputStream
(
path
+
targetFileName
+
".docx"
));
template
.
writeAndClose
(
new
FileOutputStream
(
path
+
targetFileName
+
".docx"
));
...
@@ -118,8 +92,16 @@ public class EmailPushTask {
...
@@ -118,8 +92,16 @@ public class EmailPushTask {
}
}
//邮件推送
//邮件推送
// isSuccess = emailSender.sendEmailWithAttachment("duanxincheng@chatbot.cn",meetingName,savePath + targetFileName + ".docx",meetingId);
List
<
EmailPush
.
Attachment
>
attachments
=
new
ArrayList
<>();
isSuccess
=
emailSender
.
sendEmailWithAttachment
(
meetingInfo
.
getEmail
(),
meetingName
,
savePath
+
targetFileName
+
".docx"
,
meetingId
);
try
(
InputStream
is
=
new
FileInputStream
(
path
+
targetFileName
+
".docx"
)){
byte
[]
meetingMinutesBytes
=
IOUtils
.
toByteArray
(
is
);
EmailPush
.
Attachment
attachment
=
EmailPush
.
Attachment
.
builder
().
name
(
meetingInfo
.
getSubject
()+
"_数据网络中心"
).
bytes
(
meetingMinutesBytes
).
build
();
attachments
.
add
(
attachment
);
}
catch
(
Exception
e
){
throw
new
RuntimeException
(
e
);
}
EmailPush
emailPushBuilder
=
EmailPush
.
builder
().
toEmail
(
meetingInfo
.
getEmail
()).
meetingId
(
meetingId
).
attachments
(
attachments
).
subject
(
meetingInfo
.
getSubject
()).
build
();
isSuccess
=
emailSender
.
sendEmailWithAttachment
(
emailPushBuilder
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
// 异常处理
// 异常处理
int
currentRetryCount
=
retryCount
.
getAndIncrement
();
int
currentRetryCount
=
retryCount
.
getAndIncrement
();
...
...
src/main/java/com/cmeeting/job/FileProcessTask.java
浏览文件 @
2089d7b6
...
@@ -5,21 +5,17 @@ import cn.chatbot.openai.completion.chat.ChatMessage;
...
@@ -5,21 +5,17 @@ import cn.chatbot.openai.completion.chat.ChatMessage;
import
cn.chatbot.openai.completion.chat.ChatMessageRole
;
import
cn.chatbot.openai.completion.chat.ChatMessageRole
;
import
cn.chatbot.openai.completion.chat.Message
;
import
cn.chatbot.openai.completion.chat.Message
;
import
cn.chatbot.openai.service.LLMService
;
import
cn.chatbot.openai.service.LLMService
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.util.IdUtil
;
import
cn.hutool.core.util.IdUtil
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
;
import
com.cmeeting.dto.UserDTO
;
import
com.cmeeting.email.EmailSender
;
import
com.cmeeting.email.EmailSender
;
import
com.cmeeting.log.service.ProcessLogService
;
import
com.cmeeting.log.service.ProcessLogService
;
import
com.cmeeting.mapper.primary.MeetingInfoMapper
;
import
com.cmeeting.mapper.primary.MeetingInfoMapper
;
import
com.cmeeting.mapper.primary.MeetingRecordTemplateMapper
;
import
com.cmeeting.mapper.primary.MeetingRecordTemplateMapper
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.pojo.UserId
;
import
com.cmeeting.service.MeetingInfoService
;
import
com.cmeeting.util.MinioUtils
;
import
com.cmeeting.util.MinioUtils
;
import
com.cmeeting.vo.EmailPush
;
import
com.deepoove.poi.XWPFTemplate
;
import
com.deepoove.poi.XWPFTemplate
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
...
@@ -39,6 +35,7 @@ import lombok.extern.slf4j.Slf4j;
...
@@ -39,6 +35,7 @@ import lombok.extern.slf4j.Slf4j;
import
okhttp3.OkHttpClient
;
import
okhttp3.OkHttpClient
;
import
okhttp3.Request
;
import
okhttp3.Request
;
import
okhttp3.Response
;
import
okhttp3.Response
;
import
org.apache.commons.io.IOUtils
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.poi.xwpf.extractor.XWPFWordExtractor
;
import
org.apache.poi.xwpf.extractor.XWPFWordExtractor
;
import
org.apache.poi.xwpf.usermodel.XWPFDocument
;
import
org.apache.poi.xwpf.usermodel.XWPFDocument
;
...
@@ -53,10 +50,7 @@ import java.nio.file.Files;
...
@@ -53,10 +50,7 @@ import java.nio.file.Files;
import
java.nio.file.Paths
;
import
java.nio.file.Paths
;
import
java.security.SecureRandom
;
import
java.security.SecureRandom
;
import
java.text.MessageFormat
;
import
java.text.MessageFormat
;
import
java.time.Instant
;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
import
java.time.ZoneId
;
import
java.time.ZonedDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.time.format.DateTimeFormatter
;
import
java.util.*
;
import
java.util.*
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentHashMap
;
...
@@ -103,6 +97,21 @@ public class FileProcessTask {
...
@@ -103,6 +97,21 @@ public class FileProcessTask {
.
eq
(
MeetingInfo:
:
getMeetingId
,
meetingId
)
.
eq
(
MeetingInfo:
:
getMeetingId
,
meetingId
)
.
eq
(
subMeetingId
!=
null
,
MeetingInfo:
:
getSubMeetingId
,
subMeetingId
));
.
eq
(
subMeetingId
!=
null
,
MeetingInfo:
:
getSubMeetingId
,
subMeetingId
));
// 获取参会成员明细
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetRequest
participantsRequest
=
new
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetRequest
.
Builder
(
meetingId
).
subMeetingId
(
subMeetingId
).
operatorId
(
tencentAdminUserId
).
operatorIdType
(
"1"
).
build
();
AuthenticatorBuilder
<
JWTAuthenticator
>
participantsAuthenticatorBuilder
=
new
JWTAuthenticator
.
Builder
()
.
nonce
(
BigInteger
.
valueOf
(
Math
.
abs
((
new
SecureRandom
()).
nextInt
())))
.
timestamp
(
String
.
valueOf
(
System
.
currentTimeMillis
()
/
1000L
));
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetResponse
participantsResponse
=
client
.
meetings
().
v1MeetingsMeetingIdParticipantsGet
(
participantsRequest
,
participantsAuthenticatorBuilder
);
V1MeetingsMeetingIdParticipantsGet200Response
participantsData
=
participantsResponse
.
getData
();
List
<
V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner
>
participants
=
participantsData
.
getParticipants
();
String
participantNames
=
participants
.
stream
().
map
(
item
->
new
String
(
Base64
.
getDecoder
().
decode
(
item
.
getUserName
()))).
distinct
().
collect
(
Collectors
.
joining
(
"、"
));
meetingInfo
.
setParticipantUsers
(
participantNames
);
//每场会议可能会分段录制,查出每个文件的转录记录后拼接
//每场会议可能会分段录制,查出每个文件的转录记录后拼接
StringBuffer
recordTextBuffer
=
new
StringBuffer
();
StringBuffer
recordTextBuffer
=
new
StringBuffer
();
for
(
String
recordFileId
:
recordFileIdList
)
{
for
(
String
recordFileId
:
recordFileIdList
)
{
...
@@ -142,8 +151,6 @@ public class FileProcessTask {
...
@@ -142,8 +151,6 @@ public class FileProcessTask {
// 2. 将二进制文件转换为文本
// 2. 将二进制文件转换为文本
String
recordTextContent
=
new
String
(
fileData
);
String
recordTextContent
=
new
String
(
fileData
);
// byte[] fileData = Files.readAllBytes(Paths.get("/20250514132555-中集AIGC项目例会-转写智能优化版.txt"));
// String recordTextContent = new String(fileData);
if
(
StringUtils
.
isNotEmpty
(
recordTextContent
.
replaceAll
(
"\\n"
,
""
).
trim
())){
if
(
StringUtils
.
isNotEmpty
(
recordTextContent
.
replaceAll
(
"\\n"
,
""
).
trim
())){
recordTextBuffer
.
append
(
"\n\n"
);
recordTextBuffer
.
append
(
"\n\n"
);
recordTextBuffer
.
append
(
recordTextContent
);
recordTextBuffer
.
append
(
recordTextContent
);
...
@@ -165,11 +172,35 @@ public class FileProcessTask {
...
@@ -165,11 +172,35 @@ public class FileProcessTask {
// 3. 处理文件 (调用Claude API等)
// 3. 处理文件 (调用Claude API等)
String
choiceTemplateType
=
choiceTemplateType
(
meetingInfo
.
getSubject
(),
recordTextBuffer
.
toString
());
String
choiceTemplateType
=
choiceTemplateType
(
meetingInfo
.
getSubject
(),
recordTextBuffer
.
toString
());
log
.
info
(
"choiceTemplateType->{}"
,
choiceTemplateType
);
log
.
info
(
"choiceTemplateType->{}"
,
choiceTemplateType
);
String
processedResult
=
processWithClaude
(
recordTextBuffer
.
toString
());
//获取系统模板
MeetingRecordTemplate
meetingRecordTemplate
=
meetingRecordTemplateMapper
.
selectById
(
1
);
String
processedResult
=
processWithClaude
(
recordTextBuffer
.
toString
(),
meetingRecordTemplate
.
getPrompt
());
// 4. 保存结果
// 4. 保存结果
saveResult
(
savePath
,
processedResult
,
recordTextBuffer
.
toString
().
getBytes
(
StandardCharsets
.
UTF_8
),
meetingInfo
);
MeetingRecordTemplate
meetingRecordTemplate2
=
meetingRecordTemplateMapper
.
selectById
(
2
);
String
dataNetworkMinutesPath
=
saveResult
(
processedResult
,
recordTextBuffer
.
toString
().
getBytes
(
StandardCharsets
.
UTF_8
),
meetingInfo
,
meetingRecordTemplate
);
List
<
EmailPush
.
Attachment
>
attachments
=
new
ArrayList
<>();
try
(
InputStream
is
=
new
FileInputStream
(
dataNetworkMinutesPath
)){
byte
[]
meetingMinutesBytes
=
IOUtils
.
toByteArray
(
is
);
EmailPush
.
Attachment
attachment
=
EmailPush
.
Attachment
.
builder
().
name
(
meetingInfo
.
getSubject
()+
"会议纪要_数据网络中心"
).
bytes
(
meetingMinutesBytes
).
build
();
attachments
.
add
(
attachment
);
// emailPush(meetingMinutesBytes, meetingInfo.getSubject(), meetingInfo.getEmail(), meetingInfo.getEmailPushAccess());
}
catch
(
Exception
e
){
throw
new
RuntimeException
(
e
);
}
String
processedResult2
=
processWithClaude
(
recordTextBuffer
.
toString
(),
meetingRecordTemplate2
.
getPrompt
());
String
groupOfficeMinutesPath
=
saveResult
(
processedResult2
,
recordTextBuffer
.
toString
().
getBytes
(
StandardCharsets
.
UTF_8
),
meetingInfo
,
meetingRecordTemplate2
);
try
(
InputStream
is
=
new
FileInputStream
(
groupOfficeMinutesPath
)){
byte
[]
meetingMinutesBytes
=
IOUtils
.
toByteArray
(
is
);
EmailPush
.
Attachment
attachment
=
EmailPush
.
Attachment
.
builder
().
name
(
meetingInfo
.
getSubject
()+
"会议纪要_集团办"
).
bytes
(
meetingMinutesBytes
).
build
();
attachments
.
add
(
attachment
);
// emailPush(meetingMinutesBytes, meetingInfo.getSubject(), meetingInfo.getEmail(), meetingInfo.getEmailPushAccess());
}
catch
(
Exception
e
){
throw
new
RuntimeException
(
e
);
}
EmailPush
emailPushBuilder
=
EmailPush
.
builder
().
toEmail
(
meetingInfo
.
getEmail
()).
meetingId
(
meetingId
).
attachments
(
attachments
).
subject
(
meetingInfo
.
getSubject
()).
build
();
emailPush
(
emailPushBuilder
);
isSuccess
=
true
;
isSuccess
=
true
;
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
StringWriter
sw
=
new
StringWriter
();
StringWriter
sw
=
new
StringWriter
();
...
@@ -264,17 +295,21 @@ public class FileProcessTask {
...
@@ -264,17 +295,21 @@ public class FileProcessTask {
private
String
formatMessage
(
String
pattern
,
Object
...
args
)
{
private
String
formatMessage
(
String
pattern
,
Object
...
args
)
{
return
MessageFormat
.
format
(
pattern
,
args
);
return
MessageFormat
.
format
(
pattern
,
args
);
}
}
private
String
processWithClaude
(
String
textContent
)
{
/**
* 大模型生成纪要xml
* @param textContent 转录文件
* @param prompt 提示词
* @return
*/
private
String
processWithClaude
(
String
textContent
,
String
prompt
)
{
//将文件传送给大模型处理
//将文件传送给大模型处理
String
token
=
"AKIAXFAXF62IWJXGLVEE.LnKInaahcMZG9zLsGMH3nTLOw3S3lK5Vcu0+ifnO"
;
String
token
=
"AKIAXFAXF62IWJXGLVEE.LnKInaahcMZG9zLsGMH3nTLOw3S3lK5Vcu0+ifnO"
;
String
apiAddr
=
llmApiAddr
+
"/llm/sse-invoke"
;
String
apiAddr
=
llmApiAddr
+
"/llm/sse-invoke"
;
String
model
=
"anthropic.claude-3-5-sonnet-20240620-v1:0"
;
String
model
=
"anthropic.claude-3-5-sonnet-20240620-v1:0"
;
int
maxTokens
=
5000
;
int
maxTokens
=
5000
;
//获取系统模板
MeetingRecordTemplate
meetingRecordTemplate
=
meetingRecordTemplateMapper
.
selectById
(
1
);
List
<
Message
>
messages
=
new
ArrayList
<>();
List
<
Message
>
messages
=
new
ArrayList
<>();
ChatMessage
chatMessage
=
new
ChatMessage
(
ChatMessageRole
.
USER
.
value
(),
meetingRecordTemplate
.
getPrompt
()
);
ChatMessage
chatMessage
=
new
ChatMessage
(
ChatMessageRole
.
USER
.
value
(),
prompt
);
messages
.
add
(
chatMessage
);
messages
.
add
(
chatMessage
);
chatMessage
=
new
ChatMessage
(
ChatMessageRole
.
ASSISTANT
.
value
(),
"好的请提供会议记录"
);
chatMessage
=
new
ChatMessage
(
ChatMessageRole
.
ASSISTANT
.
value
(),
"好的请提供会议记录"
);
messages
.
add
(
chatMessage
);
messages
.
add
(
chatMessage
);
...
@@ -287,24 +322,32 @@ public class FileProcessTask {
...
@@ -287,24 +322,32 @@ public class FileProcessTask {
return
ret
;
return
ret
;
}
}
private
void
saveResult
(
String
path
,
String
content
,
byte
[]
recordData
,
MeetingInfo
meetingInfo
)
{
/**
String
nowTime
=
LocalDateTime
.
now
().
format
(
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd-HHmmss"
));
* 保存会议纪要相关的文件
String
targetPath
=
path
+
nowTime
+
".json"
;
// 改为.json扩展名;
* @param content 大模型返回的不规则xml
String
targetFileName
;
* @param recordData 转录文本
* @param meetingInfo 会议对象
* @param meetingRecordTemplate 模板信息
*/
private
String
saveResult
(
String
content
,
byte
[]
recordData
,
MeetingInfo
meetingInfo
,
MeetingRecordTemplate
meetingRecordTemplate
)
{
String
meetingName
;
String
meetingName
;
String
xml
=
extractXmlFromMarkdown
(
content
);
//转录文件临时存储路径
String
recordContentPath
=
meetingId
+
"-recordContent-"
+
IdUtil
.
fastSimpleUUID
()
+
".txt"
;
// 将xml格式的内容转换为特殊json
//生成的xml临时存储路径
String
json
=
convertXmlToJSON
(
xml
);
String
recordXmlPath
=
meetingId
+
"-recordXmlPath-"
+
IdUtil
.
fastSimpleUUID
()
+
".xml"
;
String
recordContentPath
=
meetingId
+
"-recordContent-"
+
IdUtil
.
fastSimpleUUID
()
+
"txt"
;
//填充后的会议纪要名称
String
recordXmlPath
=
meetingId
+
"-recordXmlPath-"
+
IdUtil
.
fastSimpleUUID
()
+
"xml"
;
String
meetingMinutesFileName
;
//填充后的会议纪要word文件临时路径
String
meetingMinutesPath
;
try
{
try
{
//去除内容中除了xml内容以外其他的信息,格式化xml
String
xml
=
extractXmlFromMarkdown
(
content
);
minioUtils
.
upload
(
recordContentPath
,
recordData
);
minioUtils
.
upload
(
recordContentPath
,
recordData
);
//写入json文件,这里的json文件用于用户可编辑
minioUtils
.
upload
(
recordXmlPath
,
xml
.
getBytes
(
StandardCharsets
.
UTF_8
));
Files
.
write
(
Paths
.
get
(
targetPath
),
json
.
getBytes
());
log
.
info
(
"json文件已保存到: {}"
,
targetPath
);
//将xml格式的内容转换为map,用于填充模板
//将xml格式的内容转换为map,用于填充模板
Map
<
String
,
Object
>
dataModel
=
convertXmlToMap
(
xml
);
Map
<
String
,
Object
>
dataModel
=
convertXmlToMap
(
xml
);
meetingName
=
dataModel
.
get
(
"meeting_name"
)
!=
null
?
String
.
valueOf
(
dataModel
.
get
(
"meeting_name"
))
:
"腾讯会议纪要"
;
meetingMinutesFileName
=
meetingName
+
"_"
+
meetingRecordTemplate
.
getName
();
//追加参会人员信息
//追加参会人员信息
Map
<
String
,
Object
>
participantsMap
=
new
ConcurrentHashMap
<>();
Map
<
String
,
Object
>
participantsMap
=
new
ConcurrentHashMap
<>();
DateTimeFormatter
df
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd"
);
DateTimeFormatter
df
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd"
);
...
@@ -313,20 +356,16 @@ public class FileProcessTask {
...
@@ -313,20 +356,16 @@ public class FileProcessTask {
participantsMap
.
put
(
"meeting_location"
,
"线上腾讯会议"
);
participantsMap
.
put
(
"meeting_location"
,
"线上腾讯会议"
);
participantsMap
.
put
(
"meeting_participants"
,
meetingInfo
.
getParticipantUsers
());
participantsMap
.
put
(
"meeting_participants"
,
meetingInfo
.
getParticipantUsers
());
participantsMap
.
put
(
"meeting_host"
,
meetingInfo
.
getHost
());
participantsMap
.
put
(
"meeting_host"
,
meetingInfo
.
getHost
());
dataModel
.
putAll
(
participantsMap
);
dataModel
.
putAll
(
participantsMap
);
ClassPathResource
resource
=
new
ClassPathResource
(
"template/data_net_template.docx"
);
meetingName
=
dataModel
.
get
(
"meeting_name"
)
!=
null
?
String
.
valueOf
(
dataModel
.
get
(
"meeting_name"
))
:
"腾讯会议纪要"
;
targetFileName
=
meetingName
+
"_"
+
nowTime
;
XWPFTemplate
template
;
XWPFTemplate
template
;
try
(
InputStream
inputStream
=
resource
.
getInputStream
(
))
{
try
(
InputStream
inputStream
=
minioUtils
.
getFile
(
meetingRecordTemplate
.
getTemplate
()
))
{
template
=
XWPFTemplate
.
compile
(
inputStream
).
render
(
dataModel
);
template
=
XWPFTemplate
.
compile
(
inputStream
).
render
(
dataModel
);
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
throw
new
RuntimeException
(
e
);
}
}
template
.
writeAndClose
(
new
FileOutputStream
(
path
+
targetFileName
+
".docx"
));
meetingMinutesPath
=
savePath
+
meetingMinutesFileName
+
".docx"
;
byte
[]
recordXmlData
=
Files
.
readAllBytes
(
Paths
.
get
(
path
+
targetFileName
+
".docx"
));
template
.
writeAndClose
(
new
FileOutputStream
(
meetingMinutesPath
));
minioUtils
.
upload
(
recordXmlPath
,
recordXmlData
);
processLogService
.
log
(
meetingId
,
subMeetingId
,
"填充会议纪要成功"
);
processLogService
.
log
(
meetingId
,
subMeetingId
,
"填充会议纪要成功"
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
log
.
error
(
"填充会议纪要失败: {}"
,
e
.
getMessage
(),
e
);
log
.
error
(
"填充会议纪要失败: {}"
,
e
.
getMessage
(),
e
);
...
@@ -337,14 +376,29 @@ public class FileProcessTask {
...
@@ -337,14 +376,29 @@ public class FileProcessTask {
throw
new
RuntimeException
(
"填充会议纪要失败"
);
throw
new
RuntimeException
(
"填充会议纪要失败"
);
}
}
meetingInfoMapper
.
update
(
meetingInfo
,
new
LambdaUpdateWrapper
<
MeetingInfo
>()
.
eq
(
MeetingInfo:
:
getMeetingId
,
meetingId
)
.
eq
(
subMeetingId
!=
null
,
MeetingInfo:
:
getSubMeetingId
,
subMeetingId
)
.
set
(
MeetingInfo:
:
getRecordContent
,
recordContentPath
)
.
set
(
MeetingInfo:
:
getRecordXml
,
recordXmlPath
)
.
set
(
MeetingInfo:
:
getParticipantUsers
,
meetingInfo
.
getParticipantUsers
())
.
set
(
MeetingInfo:
:
getIsGenerated
,
Boolean
.
TRUE
)
);
meetingInfo
.
setRecordContent
(
recordContentPath
);
meetingInfo
.
setRecordXml
(
recordXmlPath
);
return
savePath
+
meetingMinutesFileName
+
".docx"
;
}
private
void
emailPush
(
EmailPush
emailPushBuilder
)
{
Boolean
isPushed
;
Boolean
isPushed
;
log
.
info
(
"开始邮件推送------"
);
log
.
info
(
"开始邮件推送------"
);
if
(
meetingInfo
.
getEmailPushAccess
()
){
if
(
true
){
log
.
info
(
"用户允许邮件推送,准备推送邮件至{}------"
,
meetingInfo
.
get
Email
());
log
.
info
(
"用户允许邮件推送,准备推送邮件至{}------"
,
emailPushBuilder
.
getTo
Email
());
//邮件推送
//邮件推送
isPushed
=
emailSender
.
sendEmailWithAttachment
(
"duanxincheng@chatbot.cn"
,
meetingName
,
path
+
targetFileName
+
".docx"
,
meetingId
);
isPushed
=
emailSender
.
sendEmailWithAttachment
(
emailPushBuilder
);
try
{
try
{
// isPushed = emailSender.sendEmailWithAttachment(
meetingInfo.getEmail(),meetingName,path + targetFileName + ".docx",meetingId
);
// isPushed = emailSender.sendEmailWithAttachment(
emailPushBuilder
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
log
.
error
(
"邮件推送失败: {}"
,
e
.
getMessage
(),
e
);
log
.
error
(
"邮件推送失败: {}"
,
e
.
getMessage
(),
e
);
StringWriter
sw
=
new
StringWriter
();
StringWriter
sw
=
new
StringWriter
();
...
@@ -354,22 +408,16 @@ public class FileProcessTask {
...
@@ -354,22 +408,16 @@ public class FileProcessTask {
throw
new
RuntimeException
(
e
);
throw
new
RuntimeException
(
e
);
}
}
if
(
isPushed
)
if
(
isPushed
)
processLogService
.
log
(
meetingId
,
subMeetingId
,
"用户允许邮件推送,推送邮件至"
+
meetingInfo
.
getEmail
());
processLogService
.
log
(
meetingId
,
subMeetingId
,
"用户允许邮件推送,推送邮件至"
+
emailPushBuilder
.
getToEmail
());
// emailSender.sendEmailWithAttachment("xuwentao@chatbot.cn",meetingName,path + targetFileName + ".docx",meetingId);
// emailSender.sendEmailWithAttachment("jiaqi.cai@cimc.com",meetingName,path + targetFileName + ".docx",meetingId);
}
else
{
}
else
{
log
.
info
(
"用户关闭了邮件推送,推送终止------"
);
log
.
info
(
"用户关闭了邮件推送,推送终止------"
);
processLogService
.
log
(
meetingId
,
subMeetingId
,
"用户关闭了邮件推送,推送终止"
);
processLogService
.
log
(
meetingId
,
subMeetingId
,
"用户关闭了邮件推送,推送终止"
);
isPushed
=
Boolean
.
FALSE
;
isPushed
=
Boolean
.
FALSE
;
}
}
meetingInfoMapper
.
update
(
null
,
meetingInfoMapper
.
update
(
meetingInfo
,
new
LambdaUpdateWrapper
<
MeetingInfo
>()
new
LambdaUpdateWrapper
<
MeetingInfo
>()
.
eq
(
MeetingInfo:
:
getMeetingId
,
meetingId
)
.
eq
(
MeetingInfo:
:
getMeetingId
,
meetingId
)
.
eq
(
subMeetingId
!=
null
,
MeetingInfo:
:
getSubMeetingId
,
subMeetingId
)
.
eq
(
subMeetingId
!=
null
,
MeetingInfo:
:
getSubMeetingId
,
subMeetingId
)
.
set
(
MeetingInfo:
:
getRecordContent
,
recordContentPath
)
.
set
(
MeetingInfo:
:
getRecordXml
,
recordXmlPath
)
.
set
(
MeetingInfo:
:
getIsGenerated
,
Boolean
.
TRUE
)
.
set
(
MeetingInfo:
:
getIsPushed
,
isPushed
)
.
set
(
MeetingInfo:
:
getIsPushed
,
isPushed
)
);
);
}
}
...
@@ -460,10 +508,13 @@ public class FileProcessTask {
...
@@ -460,10 +508,13 @@ public class FileProcessTask {
* @param markdown
* @param markdown
* @return
* @return
*/
*/
private
static
String
extractXmlFromMarkdown
(
String
markdown
)
{
private
String
extractXmlFromMarkdown
(
String
markdown
)
{
StringBuffer
sb
=
null
;
StringBuffer
sb
=
null
;
try
{
try
{
int
start
=
markdown
.
indexOf
(
"<"
);
int
start
=
markdown
.
indexOf
(
"<"
);
if
(
start
==
-
1
){
processLogService
.
log
(
meetingId
,
subMeetingId
,
"markdown转xml失败,未输出正确的xml格式,markdown内容:"
+
markdown
);
}
int
end
=
markdown
.
lastIndexOf
(
">"
)
+
1
;
int
end
=
markdown
.
lastIndexOf
(
">"
)
+
1
;
sb
=
new
StringBuffer
();
sb
=
new
StringBuffer
();
sb
.
append
(
"<root>"
);
sb
.
append
(
"<root>"
);
...
...
src/main/java/com/cmeeting/pojo/MeetingInfo.java
浏览文件 @
2089d7b6
...
@@ -114,4 +114,13 @@ public class MeetingInfo implements Serializable {
...
@@ -114,4 +114,13 @@ public class MeetingInfo implements Serializable {
* 邮箱
* 邮箱
*/
*/
private
String
email
;
private
String
email
;
/**
* 同步时间
*/
private
LocalDateTime
updateTime
;
/**
* 模板ID
*/
private
Integer
templateId
;
}
}
\ No newline at end of file
src/main/java/com/cmeeting/pojo/MeetingRecordTemplate.java
浏览文件 @
2089d7b6
...
@@ -76,4 +76,9 @@ public class MeetingRecordTemplate implements Serializable {
...
@@ -76,4 +76,9 @@ public class MeetingRecordTemplate implements Serializable {
* 删除标识
* 删除标识
*/
*/
private
Boolean
isDel
;
private
Boolean
isDel
;
/**
* 模板文件
*/
private
String
template
;
}
}
\ No newline at end of file
src/main/java/com/cmeeting/service/MeetingInfoService.java
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
service
;
package
com
.
cmeeting
.
service
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.vo.MeetingInfoVO
;
public
interface
MeetingInfoService
extends
IService
<
MeetingInfo
>
{
public
interface
MeetingInfoService
extends
IService
<
MeetingInfo
>
{
IPage
<
MeetingInfo
>
getPage
(
MeetingInfoVO
vo
);
boolean
updateRecordXml
(
MeetingInfoVO
vo
);
}
}
src/main/java/com/cmeeting/service/MeetingRecordTemplateService.java
浏览文件 @
2089d7b6
...
@@ -4,7 +4,17 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
...
@@ -4,7 +4,17 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.vo.RecordTemplateVO
;
import
com.cmeeting.vo.RecordTemplateVO
;
import
org.springframework.web.multipart.MultipartFile
;
public
interface
MeetingRecordTemplateService
extends
IService
<
MeetingRecordTemplate
>
{
public
interface
MeetingRecordTemplateService
extends
IService
<
MeetingRecordTemplate
>
{
IPage
<
MeetingRecordTemplate
>
getPage
(
RecordTemplateVO
vo
);
IPage
<
MeetingRecordTemplate
>
getPage
(
RecordTemplateVO
vo
);
IPage
<
MeetingRecordTemplate
>
getPersonalPage
(
RecordTemplateVO
vo
);
/**
* 上传附件模板
* @param file
* @param id
* @return
*/
String
templateUpload
(
MultipartFile
file
,
Integer
id
);
}
}
src/main/java/com/cmeeting/service/impl/MeetingInfoServiceImpl.java
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
service
.
impl
;
package
com
.
cmeeting
.
service
.
impl
;
import
cn.hutool.core.util.IdUtil
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
;
import
com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
;
import
com.cmeeting.mapper.primary.MeetingInfoMapper
;
import
com.cmeeting.mapper.primary.MeetingInfoMapper
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.pojo.MeetingInfo
;
import
com.cmeeting.service.MeetingInfoService
;
import
com.cmeeting.service.MeetingInfoService
;
import
com.cmeeting.util.MinioUtils
;
import
com.cmeeting.vo.MeetingInfoVO
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.dataformat.xml.XmlMapper
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.Resource
;
import
java.io.IOException
;
import
java.nio.charset.StandardCharsets
;
import
java.time.LocalDateTime
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
@Service
@Service
public
class
MeetingInfoServiceImpl
extends
ServiceImpl
<
MeetingInfoMapper
,
MeetingInfo
>
implements
MeetingInfoService
{
public
class
MeetingInfoServiceImpl
extends
ServiceImpl
<
MeetingInfoMapper
,
MeetingInfo
>
implements
MeetingInfoService
{
@Resource
private
MeetingInfoMapper
mapper
;
@Resource
private
MinioUtils
minioUtils
;
@Override
public
IPage
<
MeetingInfo
>
getPage
(
MeetingInfoVO
vo
)
{
LambdaQueryWrapper
<
MeetingInfo
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
IPage
<
MeetingInfo
>
resultPage
=
mapper
.
selectPage
(
new
Page
<>(
vo
.
getCurrent
(),
vo
.
getSize
()),
queryWrapper
);
return
resultPage
;
}
@Override
public
boolean
updateRecordXml
(
MeetingInfoVO
vo
)
{
//前端的表单json转xml,xml格式的纪要内容便于生成会议纪要文件
String
recordJson
=
vo
.
getRecordJson
();
String
recordXml
=
convertJSONToXml
(
recordJson
);
String
key
=
vo
.
getMeetingId
()
+
"-recordXmlPath-"
+
IdUtil
.
fastSimpleUUID
()
+
".xml"
;
minioUtils
.
upload
(
key
,
recordXml
.
getBytes
(
StandardCharsets
.
UTF_8
));
vo
.
setRecordXml
(
key
);
LambdaUpdateWrapper
<
MeetingInfo
>
updateWrapper
=
new
LambdaUpdateWrapper
<>();
updateWrapper
.
eq
(
MeetingInfo:
:
getId
,
vo
.
getId
());
updateWrapper
.
set
(
MeetingInfo:
:
getUpdateTime
,
LocalDateTime
.
now
());
updateWrapper
.
set
(
MeetingInfo:
:
getRecordXml
,
key
);
return
update
(
null
,
updateWrapper
);
}
private
String
convertJSONToXml
(
String
json
)
{
try
{
// 解析 JSON 字符串
ObjectMapper
jsonMapper
=
new
ObjectMapper
();
List
<
Map
<
String
,
String
>>
list
=
jsonMapper
.
readValue
(
json
,
List
.
class
);
// 创建 XML 根节点
Map
<
String
,
Object
>
root
=
new
HashMap
<>();
// 处理每个 JSON 项
for
(
Map
<
String
,
String
>
item
:
list
)
{
String
keyValue
=
item
.
get
(
"key"
);
String
keyName
=
item
.
get
(
"keyName"
);
String
value
=
item
.
get
(
"value"
);
// 创建子节点
Map
<
String
,
Object
>
subNode
=
new
HashMap
<>();
subNode
.
put
(
"label"
,
keyName
);
subNode
.
put
(
""
,
value
);
// 添加到根节点
root
.
put
(
keyValue
,
subNode
);
}
// 转换为 XML
XmlMapper
xmlMapper
=
new
XmlMapper
();
return
xmlMapper
.
writeValueAsString
(
root
);
}
catch
(
IOException
e
)
{
log
.
error
(
"JSON 转 XML 失败: "
+
e
.
getMessage
());
throw
new
RuntimeException
(
"JSON 转 XML 失败"
,
e
);
}
}
}
}
src/main/java/com/cmeeting/service/impl/MeetingRecordTemplateServiceImpl.java
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
service
.
impl
;
package
com
.
cmeeting
.
service
.
impl
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.core.util.IdUtil
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
...
@@ -7,10 +10,15 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
...
@@ -7,10 +10,15 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import
com.cmeeting.mapper.primary.MeetingRecordTemplateMapper
;
import
com.cmeeting.mapper.primary.MeetingRecordTemplateMapper
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.pojo.MeetingRecordTemplate
;
import
com.cmeeting.service.MeetingRecordTemplateService
;
import
com.cmeeting.service.MeetingRecordTemplateService
;
import
com.cmeeting.util.MinioUtils
;
import
com.cmeeting.vo.RecordTemplateVO
;
import
com.cmeeting.vo.RecordTemplateVO
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.annotation.Resource
;
import
javax.annotation.Resource
;
import
java.io.InputStream
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.List
;
@Service
@Service
...
@@ -18,12 +26,58 @@ public class MeetingRecordTemplateServiceImpl extends ServiceImpl<MeetingRecordT
...
@@ -18,12 +26,58 @@ public class MeetingRecordTemplateServiceImpl extends ServiceImpl<MeetingRecordT
@Resource
@Resource
private
MeetingRecordTemplateMapper
mapper
;
private
MeetingRecordTemplateMapper
mapper
;
@Autowired
private
MinioUtils
minioUtils
;
@Override
@Override
public
IPage
<
MeetingRecordTemplate
>
getPage
(
RecordTemplateVO
vo
)
{
public
IPage
<
MeetingRecordTemplate
>
getPage
(
RecordTemplateVO
vo
)
{
LambdaQueryWrapper
<
MeetingRecordTemplate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
LambdaQueryWrapper
<
MeetingRecordTemplate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
MeetingRecordTemplate:
:
getIsDel
,
Boolean
.
FALSE
);
queryWrapper
.
eq
(
MeetingRecordTemplate:
:
getIsDel
,
Boolean
.
FALSE
);
queryWrapper
.
eq
(
vo
.
getType
()
!=
null
,
MeetingRecordTemplate:
:
getType
,
vo
.
getType
());
queryWrapper
.
like
(
vo
.
getName
()
!=
null
,
MeetingRecordTemplate:
:
getName
,
vo
.
getName
());
queryWrapper
.
like
(
vo
.
getMeetingType
()
!=
null
,
MeetingRecordTemplate:
:
getMeetingType
,
vo
.
getMeetingType
());
IPage
<
MeetingRecordTemplate
>
resultPage
=
mapper
.
selectPage
(
new
Page
<>(
vo
.
getCurrent
(),
vo
.
getSize
()),
queryWrapper
);
IPage
<
MeetingRecordTemplate
>
resultPage
=
mapper
.
selectPage
(
new
Page
<>(
vo
.
getCurrent
(),
vo
.
getSize
()),
queryWrapper
);
return
resultPage
;
return
resultPage
;
}
}
@Override
public
IPage
<
MeetingRecordTemplate
>
getPersonalPage
(
RecordTemplateVO
vo
)
{
LambdaQueryWrapper
<
MeetingRecordTemplate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
MeetingRecordTemplate:
:
getIsDel
,
Boolean
.
FALSE
);
queryWrapper
.
eq
(
vo
.
getType
()
!=
null
,
MeetingRecordTemplate:
:
getType
,
vo
.
getType
());
queryWrapper
.
like
(
vo
.
getName
()
!=
null
,
MeetingRecordTemplate:
:
getName
,
vo
.
getName
());
queryWrapper
.
like
(
vo
.
getMeetingType
()
!=
null
,
MeetingRecordTemplate:
:
getMeetingType
,
vo
.
getMeetingType
());
IPage
<
MeetingRecordTemplate
>
resultPage
=
mapper
.
selectPage
(
new
Page
<>(
vo
.
getCurrent
(),
vo
.
getSize
()),
queryWrapper
);
return
resultPage
;
}
/**
* 上传附件模板到minio
* @param file
* @param id
* @return
*/
@Override
public
String
templateUpload
(
MultipartFile
file
,
Integer
id
)
{
MeetingRecordTemplate
detail
=
getById
(
id
);
String
separator
=
"/"
;
String
point
=
"."
;
// 获取文件名
String
originalFilename
=
file
.
getOriginalFilename
();
// 获取文件后缀
String
originalSuffix
=
FileUtil
.
getSuffix
(
originalFilename
);
// 生成文件名
String
nowDate
=
DateUtil
.
format
(
new
Date
(),
"yyyy-MM-dd"
);
String
uuid
=
IdUtil
.
fastSimpleUUID
();
String
originalFilePath
=
separator
+
nowDate
+
separator
+
uuid
+
point
+
originalSuffix
;
// 获取文件流
try
(
InputStream
is
=
file
.
getInputStream
()){
minioUtils
.
upload
(
originalFilePath
,
is
);
}
catch
(
Exception
e
){
throw
new
RuntimeException
(
e
.
getMessage
());
}
detail
.
setTemplate
(
originalFilePath
);
updateById
(
detail
);
return
originalFilePath
;
}
}
}
\ No newline at end of file
src/main/java/com/cmeeting/service/impl/TecentMeetingServiceImpl.java
浏览文件 @
2089d7b6
...
@@ -14,7 +14,10 @@ import com.cmeeting.pojo.TencentMeetingUser;
...
@@ -14,7 +14,10 @@ import com.cmeeting.pojo.TencentMeetingUser;
import
com.cmeeting.pojo.UserId
;
import
com.cmeeting.pojo.UserId
;
import
com.cmeeting.service.TecentMeetingService
;
import
com.cmeeting.service.TecentMeetingService
;
import
com.cmeeting.util.RedisUtils
;
import
com.cmeeting.util.RedisUtils
;
import
com.cmeeting.util.SignatureUtil
;
import
com.cmeeting.vo.CorpRecordsVO
;
import
com.cmeeting.vo.TencentMeetingVO
;
import
com.cmeeting.vo.TencentMeetingVO
;
import
com.google.gson.*
;
import
com.tencentcloudapi.wemeet.Client
;
import
com.tencentcloudapi.wemeet.Client
;
import
com.tencentcloudapi.wemeet.core.authenticator.AuthenticatorBuilder
;
import
com.tencentcloudapi.wemeet.core.authenticator.AuthenticatorBuilder
;
import
com.tencentcloudapi.wemeet.core.authenticator.JWTAuthenticator
;
import
com.tencentcloudapi.wemeet.core.authenticator.JWTAuthenticator
;
...
@@ -29,6 +32,11 @@ import com.tencentcloudapi.wemeet.service.user_manager.model.V1UsersListGet200Re
...
@@ -29,6 +32,11 @@ import com.tencentcloudapi.wemeet.service.user_manager.model.V1UsersListGet200Re
import
com.tencentcloudapi.wemeet.service.user_manager.model.V1UsersListGet200ResponseUsersInner
;
import
com.tencentcloudapi.wemeet.service.user_manager.model.V1UsersListGet200ResponseUsersInner
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
okhttp3.Headers
;
import
okhttp3.Headers
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.http.client.methods.HttpGet
;
import
org.apache.http.impl.client.CloseableHttpClient
;
import
org.apache.http.impl.client.HttpClients
;
import
org.apache.http.util.EntityUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
...
@@ -73,7 +81,6 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -73,7 +81,6 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
private
static
final
String
HMAC_ALGORITHM
=
"HmacSHA256"
;
private
static
final
String
HMAC_ALGORITHM
=
"HmacSHA256"
;
private
static
final
char
[]
HEX_CHAR
=
{
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
};
private
static
final
char
[]
HEX_CHAR
=
{
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
};
private
final
Object
lock
=
new
Object
();
@Value
(
value
=
"${tencent.appId}"
)
@Value
(
value
=
"${tencent.appId}"
)
private
String
tencentAppId
;
private
String
tencentAppId
;
...
@@ -111,6 +118,223 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -111,6 +118,223 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
}
}
// @Override
// public List<TencentMeetingVO.RecordFile> getMeetingFiles(List<UserDTO> accessUserIds) {
// Client client = new Client.Builder()
// .withAppId(tencentAppId).withSdkId(tencentSdkId)
// .withSecret(tencentSecretId,tencentSecretKey)
// .build();
//
// List<TencentMeetingVO.RecordFile> recordFileUrlList = new ArrayList<>();
// List<MeetingInfo> meetingSaveList = new ArrayList<>();
// // 查询近两天的会议录制列表
// try {
// ZonedDateTime now = ZonedDateTime.now();
// String startTime = String.valueOf(now.minusDays(2).toEpochSecond());
// String endTime = String.valueOf(now.toEpochSecond());
// AtomicInteger currentPage = new AtomicInteger(1);
// Long totalPage = 1L;
//
// //目前已存储的会议id
// List<String> meetingIds = meetingInfoMapper.getAllMeetingIds();
// while (currentPage.intValue() <= totalPage.intValue()){
// RecordsApi.ApiV1RecordsGetRequest request =
// new RecordsApi.ApiV1RecordsGetRequest.Builder()
// .operatorId(tencentAdminUserId)
// .operatorIdType("1")
// .startTime(startTime)
// .endTime(endTime)
// .pageSize("20")
// .page(String.valueOf(currentPage.getAndIncrement()))
// .mediaSetType("0")
// .build();
// RecordsApi.ApiV1RecordsGetResponse response =
// client.records().v1RecordsGet(request, new JWTAuthenticator.Builder()
// .nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
// .timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
// V1RecordsGet200Response data = response.getData();
// //设置总页数
// totalPage = data.getTotalPage();
// if (data != null && data.getRecordMeetings() != null && !data.getRecordMeetings().isEmpty()) {
// List<V1RecordsGet200ResponseRecordMeetingsInner> meetings = data.getRecordMeetings();
//
// //录制状态:
// //1:录制中
// //2:转码中
// //3:转码完成
// if(meetings.stream().allMatch(item->item.getState() != 3)){
// return null;
// }
// for (V1RecordsGet200ResponseRecordMeetingsInner meeting : meetings) {
// //会议没结束,跳过
// if(meeting.getState() != 3){
// processLogService.log(meeting.getMeetingId(),null,"会议未结束,跳过生成");
// continue;
// }
//
//
// log.info("【会议检索】转录文件的meetingId->{},recordFileId->{}",meeting.getMeetingId(),meeting.getMeetingRecordId());
//
// //查询会议详情
// String meetingId = meeting.getMeetingId();
// String subMeetingId = null;
// MeetingsApi.ApiV1MeetingsMeetingIdGetRequest meetingRequest =
// new MeetingsApi.ApiV1MeetingsMeetingIdGetRequest.Builder(meetingId)
// .operatorId(tencentAdminUserId)
// .operatorIdType("1")
// .instanceid("0")
// .build();
// try {
// MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse =
// client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder()
// .nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
// .timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
// V1MeetingsMeetingIdGet200Response meetingResponseData = meetingResponse.getData();
// List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner> meetingInfoList = meetingResponseData.getMeetingInfoList();
// //尝试获取会议详情
// if(meetingInfoList != null && meetingInfoList.size() > 0){
// V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner meetingInfo = meetingInfoList.get(0);
// //会议类型
// //0:一次性会议
// //1:周期性会议
// Long meetingType = meetingInfo.getMeetingType();
// if(meetingType.intValue() == 1){
// //如果是周期会议,获取子会议的ID,用于查询参会人员
// List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeetings = meetingInfo.getSubMeetings();
// //如果主持人突然取消了后续的所有周期会议,subMeetings列表会返回null
// if(!CollectionUtils.isEmpty(subMeetings)){
// LocalDate meetingStartDate = Instant.ofEpochMilli(meeting.getMediaStartTime()).atZone(ZoneId.systemDefault()).toLocalDate();
// Optional<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeeting = subMeetings.stream().filter(item ->
// Instant.ofEpochSecond(Long.valueOf(item.getStartTime())).atZone(ZoneId.systemDefault()).toLocalDate().equals(meetingStartDate))
// .findFirst();
// if(!subMeeting.isPresent()){
// log.error("周期会议"+meetingId+"未知子会议ID");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"未知子会议ID");
// continue;
// }
// subMeetingId = subMeeting.get().getSubMeetingId();
// }else{
// subMeetingId = meetingInfo.getCurrentSubMeetingId();
// log.info("周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
// }
// }
// //如果数据库中已有相同会议id的记录,跳过同步
// if(!meetingIds.contains(meetingId)){
// log.info("【会议检索】新的会议meetingId->{},开始持久化",meeting.getMeetingId());
// List<V1RecordsGet200ResponseRecordMeetingsInnerRecordFilesInner> recordFiles = meeting.getRecordFiles();
// //按转录文件时间升序,便于后续的内容拼接
// List<String> recordFileIdList = recordFiles.stream().sorted(Comparator.comparingLong(V1RecordsGet200ResponseRecordMeetingsInnerRecordFilesInner::getRecordStartTime))
// .map(V1RecordsGet200ResponseRecordMeetingsInnerRecordFilesInner::getRecordFileId).collect(Collectors.toList());
// TencentMeetingVO.RecordFile recordFileItem = TencentMeetingVO.RecordFile.builder()
// .recordFileIdList(recordFileIdList).meetingId(meetingId).subMeetingId(subMeetingId).build();
//
// // 获取参会成员明细
// MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest =
// new MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest
// .Builder(meetingId).subMeetingId(subMeetingId).operatorId(tencentAdminUserId).operatorIdType("1").build();
// AuthenticatorBuilder<JWTAuthenticator> participantsAuthenticatorBuilder =
// new JWTAuthenticator.Builder()
// .nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
// .timestamp(String.valueOf(System.currentTimeMillis() / 1000L));
// //主持人角色,以下角色都可以表示主持人
// //用户角色:
// //0:普通成员角色
// //1:创建者角色
// //2:主持人
// //3:创建者+主持人
// //4:游客
// //5:游客+主持人
// //6:联席主持人
// //7:创建者+联席主持人
// List<Long> hostRoleList = Arrays.asList(2L,3L,5L,6L,7L);
// MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetResponse participantsResponse =
// client.meetings().v1MeetingsMeetingIdParticipantsGet(participantsRequest, participantsAuthenticatorBuilder);
// V1MeetingsMeetingIdParticipantsGet200Response participantsData = participantsResponse.getData();
// List<V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner> participants = participantsData.getParticipants();
// Optional<V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner> host = participants.stream().filter(item -> hostRoleList.contains(item.getUserRole())).findFirst();
// String email;
// String hostId;
// String hostName;
// //判断主持人是否存在,如果主持人未参会,是查不到主持人的
// //如果主持人未参会,使用会议详情中的创建人作为主持人
// if(host.isPresent()) {
// hostId = host.get().getUserid();
// hostName = new String(Base64.getDecoder().decode(host.get().getUserName()));
// }else{
// //未找到主持人,读取会议详情中的创建人作为主持人
// List<V1MeetingsGet200ResponseMeetingInfoListInnerCurrentCoHostsInner> currentHosts = meetingInfo.getCurrentHosts();
// if(CollectionUtils.isEmpty(currentHosts)){
// log.error("未找到主持人,默认没有生成纪要权限");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"未找到主持人,默认没有生成纪要权限");
// continue;
// }
// hostId = currentHosts.get(0).getUserid();
// hostName = tecentMeetingMapper.getUsernameByUserId(hostId);
// log.info("主持人会中缺席,默认会议创建人为主持人");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"未找到主持人,默认会议创建人为主持人");
// }
// //判断是否有权限生成纪要
// boolean generateAccess = accessUserIds.stream().anyMatch(item -> item.getTid().equals(hostId));
// if(!generateAccess){
// log.error("【权限校验】主持人{}没有生成纪要权限,跳过生成",hostId);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"没有生成纪要权限,跳过生成");
// continue;
// }
// log.info("【权限校验】主持人{}允许生成纪要",hostId);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"允许生成纪要");
// UserDTO userDTO = accessUserIds.stream().filter(item -> item.getTid().equals(hostId)).findFirst().get();
// email = userDTO.getEmail();
//
// //会议基本信息保存
// MeetingInfo meetingItem = MeetingInfo.builder().meetingId(meetingId).meetingCode(meetingInfo.getMeetingCode())
// .subject(meetingInfo.getSubject())
// .startTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.valueOf(meetingInfo.getStartTime())), ZoneId.systemDefault()))
// .endTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.valueOf(meetingInfo.getEndTime())), ZoneId.systemDefault()))
// .isGenerated(Boolean.FALSE).emailPushAccess(Boolean.TRUE).isPushed(Boolean.FALSE).syncTime(LocalDateTime.now())
// .subMeetingId(subMeetingId).generateRetry(Boolean.FALSE).pushRetry(Boolean.FALSE)
// .host(hostName)
// .hostUid(hostId)
// .participantUsers(participants.stream()
// .map(item->new String(Base64.getDecoder().decode(item.getUserName()))).distinct().collect(Collectors.joining("、")))
// .recordFileId(recordFileIdList.stream().collect(Collectors.joining(",")))
// .email(email)
// .build();
// recordFileUrlList.add(recordFileItem);
// meetingSaveList.add(meetingItem);
// }
// }
// } catch (ClientException e) {
// throw new RuntimeException(e);
// } catch (ServiceException e) {
// throw new RuntimeException(e);
// }
// }
// }
// }
// if(meetingSaveList.size() > 0){
// Map<String, List<MeetingInfo>> meetingSaveMap = meetingSaveList.stream().collect(Collectors.groupingBy(
// item -> item.getMeetingId() + "_" +
// (item.getSubMeetingId() != null ? item.getSubMeetingId() : "null")));
// List<MeetingInfo> finalSaveList = new ArrayList<>();
// for (Map.Entry<String, List<MeetingInfo>> entry : meetingSaveMap.entrySet()) {
// MeetingInfo meetingInfo = entry.getValue().get(0);
// meetingInfo.setRecordFileId(entry.getValue().stream().map(MeetingInfo::getRecordFileId).collect(Collectors.joining(",")));
// finalSaveList.add(meetingInfo);
// }
// meetingInfoMapper.batchInsert(finalSaveList);
// }
// } catch (Exception e) {
// log.error(e.getMessage());
// StringWriter sw = new StringWriter();
// PrintWriter pw = new PrintWriter(sw);
// e.printStackTrace(pw);
// processLogService.log(null,null,sw.toString());
// throw new RuntimeException(e.getMessage());
// }
// return recordFileUrlList;
// }
@Override
@Override
public
List
<
TencentMeetingVO
.
RecordFile
>
getMeetingFiles
(
List
<
UserDTO
>
accessUserIds
)
{
public
List
<
TencentMeetingVO
.
RecordFile
>
getMeetingFiles
(
List
<
UserDTO
>
accessUserIds
)
{
Client
client
=
new
Client
.
Builder
()
Client
client
=
new
Client
.
Builder
()
...
@@ -123,33 +347,21 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -123,33 +347,21 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
// 查询近两天的会议录制列表
// 查询近两天的会议录制列表
try
{
try
{
ZonedDateTime
now
=
ZonedDateTime
.
now
();
ZonedDateTime
now
=
ZonedDateTime
.
now
();
String
startTime
=
String
.
valueOf
(
now
.
minusDays
(
2
).
toEpochSecond
()
);
long
startTime
=
now
.
minusDays
(
2
).
toEpochSecond
(
);
String
endTime
=
String
.
valueOf
(
now
.
toEpochSecond
()
);
long
endTime
=
now
.
toEpochSecond
(
);
AtomicInteger
currentPage
=
new
AtomicInteger
(
1
);
AtomicInteger
currentPage
=
new
AtomicInteger
(
1
);
Long
totalPage
=
1L
;
Integer
totalPage
=
1
;
//目前已存储的会议id
//目前已存储的会议id
List
<
String
>
meetingIds
=
meetingInfoMapper
.
getAllMeetingIds
();
List
<
String
>
meetingIds
=
meetingInfoMapper
.
getAllMeetingIds
();
while
(
currentPage
.
intValue
()
<=
totalPage
.
intValue
()){
List
<
TencentMeetingUser
>
meetingUsers
=
tecentMeetingMapper
.
getAlluser
();
RecordsApi
.
ApiV1RecordsGetRequest
request
=
Map
<
String
,
String
>
meetingMap
=
meetingUsers
.
stream
().
collect
(
Collectors
.
toMap
(
TencentMeetingUser:
:
getUserId
,
TencentMeetingUser:
:
getUserName
));
new
RecordsApi
.
ApiV1RecordsGetRequest
.
Builder
()
while
(
currentPage
.
intValue
()
<=
totalPage
){
.
operatorId
(
tencentAdminUserId
)
CorpRecordsVO
data
=
fetchMeetingRecords
(
tencentAdminUserId
,
1
,
startTime
,
endTime
,
currentPage
.
getAndIncrement
(),
20
);
.
operatorIdType
(
"1"
)
.
startTime
(
startTime
)
.
endTime
(
endTime
)
.
pageSize
(
"20"
)
.
page
(
String
.
valueOf
(
currentPage
.
getAndIncrement
()))
.
mediaSetType
(
"0"
)
.
build
();
RecordsApi
.
ApiV1RecordsGetResponse
response
=
client
.
records
().
v1RecordsGet
(
request
,
new
JWTAuthenticator
.
Builder
()
.
nonce
(
BigInteger
.
valueOf
(
Math
.
abs
((
new
SecureRandom
()).
nextInt
())))
.
timestamp
(
String
.
valueOf
(
System
.
currentTimeMillis
()
/
1000L
)));
V1RecordsGet200Response
data
=
response
.
getData
();
//设置总页数
//设置总页数
totalPage
=
data
.
getTotalPage
();
totalPage
=
data
.
getTotalPage
();
if
(
data
!=
null
&&
data
.
getRecordMeetings
()
!=
null
&&
!
data
.
getRecordMeetings
().
isEmpty
())
{
if
(
data
!=
null
&&
data
.
getRecordMeetings
()
!=
null
&&
!
data
.
getRecordMeetings
().
isEmpty
())
{
List
<
V1RecordsGet200ResponseRecordMeetingsInner
>
meetings
=
data
.
getRecordMeetings
();
List
<
CorpRecordsVO
.
RecordMeeting
>
meetings
=
data
.
getRecordMeetings
();
//录制状态:
//录制状态:
//1:录制中
//1:录制中
...
@@ -158,10 +370,10 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -158,10 +370,10 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
if
(
meetings
.
stream
().
allMatch
(
item
->
item
.
getState
()
!=
3
)){
if
(
meetings
.
stream
().
allMatch
(
item
->
item
.
getState
()
!=
3
)){
return
null
;
return
null
;
}
}
for
(
V1RecordsGet200ResponseRecordMeetingsInner
meeting
:
meetings
)
{
for
(
CorpRecordsVO
.
RecordMeeting
meeting
:
meetings
)
{
//会议没结束,跳过
//会议没结束,跳过
if
(
meeting
.
getState
()
!=
3
){
if
(
meeting
.
getState
()
!=
3
){
processLogService
.
log
(
meeting
.
getMeetingId
(),
null
,
"会议未结束,跳过生成"
);
//
processLogService.log(meeting.getMeetingId(),null,"会议未结束,跳过生成");
continue
;
continue
;
}
}
...
@@ -194,81 +406,91 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -194,81 +406,91 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
if
(
meetingType
.
intValue
()
==
1
){
if
(
meetingType
.
intValue
()
==
1
){
//如果是周期会议,获取子会议的ID,用于查询参会人员
//如果是周期会议,获取子会议的ID,用于查询参会人员
List
<
V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner
>
subMeetings
=
meetingInfo
.
getSubMeetings
();
List
<
V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner
>
subMeetings
=
meetingInfo
.
getSubMeetings
();
LocalDate
meetingStartDate
=
Instant
.
ofEpochMilli
(
meeting
.
getMediaStartTime
()).
atZone
(
ZoneId
.
systemDefault
()).
toLocalDate
();
//如果主持人突然取消了后续的所有周期会议,subMeetings列表会返回null
Optional
<
V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner
>
subMeeting
=
subMeetings
.
stream
().
filter
(
item
->
if
(!
CollectionUtils
.
isEmpty
(
subMeetings
)){
Instant
.
ofEpochSecond
(
Long
.
valueOf
(
item
.
getStartTime
())).
atZone
(
ZoneId
.
systemDefault
()).
toLocalDate
().
equals
(
meetingStartDate
))
LocalDate
meetingStartDate
=
Instant
.
ofEpochMilli
(
meeting
.
getMediaStartTime
()).
atZone
(
ZoneId
.
systemDefault
()).
toLocalDate
();
.
findFirst
();
Optional
<
V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner
>
subMeeting
=
subMeetings
.
stream
().
filter
(
item
->
if
(!
subMeeting
.
isPresent
()){
Instant
.
ofEpochSecond
(
Long
.
valueOf
(
item
.
getStartTime
())).
atZone
(
ZoneId
.
systemDefault
()).
toLocalDate
().
equals
(
meetingStartDate
))
System
.
out
.
println
(
"周期会议"
+
meetingId
+
"未知子会议ID"
);
.
findFirst
();
processLogService
.
log
(
meeting
.
getMeetingId
(),
subMeetingId
,
"周期会议"
+
meetingId
+
"未知子会议ID"
);
if
(!
subMeeting
.
isPresent
()){
continue
;
log
.
error
(
"周期会议"
+
meetingId
+
"未知子会议ID"
);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"未知子会议ID");
continue
;
}
subMeetingId
=
subMeeting
.
get
().
getSubMeetingId
();
}
else
{
subMeetingId
=
meetingInfo
.
getCurrentSubMeetingId
();
log
.
info
(
"周期会议"
+
meetingId
+
"的子会议列表为空,获取当前子会议号"
);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
}
}
subMeetingId
=
subMeeting
.
get
().
getSubMeetingId
();
}
}
//如果数据库中已有相同会议id的记录,跳过同步
//如果数据库中已有相同会议id的记录,跳过同步
if
(!
meetingIds
.
contains
(
meetingId
)){
if
(!
meetingIds
.
contains
(
meetingId
)){
log
.
info
(
"【会议检索】新的会议meetingId->{},开始持久化"
,
meeting
.
getMeetingId
());
log
.
info
(
"【会议检索】新的会议meetingId->{},开始持久化"
,
meeting
.
getMeetingId
());
List
<
V1RecordsGet200ResponseRecordMeetingsInnerRecordFilesInner
>
recordFiles
=
meeting
.
getRecordFiles
();
List
<
CorpRecordsVO
.
RecordFile
>
recordFiles
=
meeting
.
getRecordFiles
();
//按转录文件时间升序,便于后续的内容拼接
//按转录文件时间升序,便于后续的内容拼接
List
<
String
>
recordFileIdList
=
recordFiles
.
stream
().
sorted
(
Comparator
.
comparingLong
(
V1RecordsGet200ResponseRecordMeetingsInnerRecordFilesInner
:
:
getRecordStartTime
))
List
<
String
>
recordFileIdList
=
recordFiles
.
stream
().
sorted
(
Comparator
.
comparingLong
(
CorpRecordsVO
.
RecordFile
::
getRecordStartTime
))
.
map
(
V1RecordsGet200ResponseRecordMeetingsInnerRecordFilesInner
:
:
getRecordFileId
).
collect
(
Collectors
.
toList
());
.
map
(
CorpRecordsVO
.
RecordFile
::
getRecordFileId
).
collect
(
Collectors
.
toList
());
TencentMeetingVO
.
RecordFile
recordFileItem
=
TencentMeetingVO
.
RecordFile
.
builder
()
TencentMeetingVO
.
RecordFile
recordFileItem
=
TencentMeetingVO
.
RecordFile
.
builder
()
.
recordFileIdList
(
recordFileIdList
).
meetingId
(
meetingId
).
subMeetingId
(
subMeetingId
).
build
();
.
recordFileIdList
(
recordFileIdList
).
meetingId
(
meetingId
).
subMeetingId
(
subMeetingId
).
build
();
// 获取参会成员明细
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetRequest
participantsRequest
=
new
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetRequest
.
Builder
(
meetingId
).
subMeetingId
(
subMeetingId
).
operatorId
(
tencentAdminUserId
).
operatorIdType
(
"1"
).
build
();
AuthenticatorBuilder
<
JWTAuthenticator
>
participantsAuthenticatorBuilder
=
new
JWTAuthenticator
.
Builder
()
.
nonce
(
BigInteger
.
valueOf
(
Math
.
abs
((
new
SecureRandom
()).
nextInt
())))
.
timestamp
(
String
.
valueOf
(
System
.
currentTimeMillis
()
/
1000L
));
//主持人角色,以下角色都可以表示主持人
//用户角色:
//0:普通成员角色
//1:创建者角色
//2:主持人
//3:创建者+主持人
//4:游客
//5:游客+主持人
//6:联席主持人
//7:创建者+联席主持人
List
<
Long
>
hostRoleList
=
Arrays
.
asList
(
2L
,
3L
,
5L
,
6L
,
7L
);
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetResponse
participantsResponse
=
client
.
meetings
().
v1MeetingsMeetingIdParticipantsGet
(
participantsRequest
,
participantsAuthenticatorBuilder
);
V1MeetingsMeetingIdParticipantsGet200Response
participantsData
=
participantsResponse
.
getData
();
List
<
V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner
>
participants
=
participantsData
.
getParticipants
();
Optional
<
V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner
>
host
=
participants
.
stream
().
filter
(
item
->
hostRoleList
.
contains
(
item
.
getUserRole
())).
findFirst
();
String
email
;
String
hostId
;
String
hostId
;
String
hostName
;
String
hostName
;
//判断主持人是否存在,如果主持人未参会,是查不到主持人的
//优先使用会议列表中已有的主持人字段
//如果主持人未参会,使用会议详情中的创建人作为主持人
if
(
StringUtils
.
isNotEmpty
(
meeting
.
getHostUserId
())){
if
(
host
.
isPresent
())
{
hostId
=
meeting
.
getHostUserId
();
hostId
=
host
.
get
().
getUserid
();
hostName
=
meetingMap
.
containsKey
(
hostId
)
?
meetingMap
.
get
(
hostId
)
:
null
;
hostName
=
new
String
(
Base64
.
getDecoder
().
decode
(
host
.
get
().
getUserName
()));
}
else
if
(!
CollectionUtils
.
isEmpty
(
meetingInfo
.
getCurrentHosts
())){
//判断主持人是否存在,如果主持人未参会,是查不到主持人的
//如果主持人未参会,使用会议详情中的创建人作为主持人
hostId
=
meetingInfo
.
getCurrentHosts
().
get
(
0
).
getUserid
();
hostName
=
meetingMap
.
containsKey
(
hostId
)
?
meetingMap
.
get
(
hostId
)
:
null
;
log
.
info
(
"主持人会中缺席,默认会议创建人为主持人"
);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"未找到主持人,默认会议创建人为主持人");
}
else
{
}
else
{
//未找到主持人,读取会议详情中的创建人作为主持人
// 获取参会成员明细
List
<
V1MeetingsGet200ResponseMeetingInfoListInnerCurrentCoHostsInner
>
currentHosts
=
meetingInfo
.
getCurrentHosts
();
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetRequest
participantsRequest
=
if
(
CollectionUtils
.
isEmpty
(
currentHosts
)){
new
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetRequest
.
Builder
(
meetingId
).
subMeetingId
(
subMeetingId
).
operatorId
(
tencentAdminUserId
).
operatorIdType
(
"1"
).
build
();
AuthenticatorBuilder
<
JWTAuthenticator
>
participantsAuthenticatorBuilder
=
new
JWTAuthenticator
.
Builder
()
.
nonce
(
BigInteger
.
valueOf
(
Math
.
abs
((
new
SecureRandom
()).
nextInt
())))
.
timestamp
(
String
.
valueOf
(
System
.
currentTimeMillis
()
/
1000L
));
//主持人角色,以下角色都可以表示主持人
//用户角色:
//0:普通成员角色
//1:创建者角色
//2:主持人
//3:创建者+主持人
//4:游客
//5:游客+主持人
//6:联席主持人
//7:创建者+联席主持人
List
<
Long
>
hostRoleList
=
Arrays
.
asList
(
2L
,
3L
,
5L
,
6L
,
7L
);
MeetingsApi
.
ApiV1MeetingsMeetingIdParticipantsGetResponse
participantsResponse
=
client
.
meetings
().
v1MeetingsMeetingIdParticipantsGet
(
participantsRequest
,
participantsAuthenticatorBuilder
);
V1MeetingsMeetingIdParticipantsGet200Response
participantsData
=
participantsResponse
.
getData
();
List
<
V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner
>
participants
=
participantsData
.
getParticipants
();
Optional
<
V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner
>
host
=
participants
.
stream
().
filter
(
item
->
hostRoleList
.
contains
(
item
.
getUserRole
())).
findFirst
();
if
(
host
.
isPresent
())
{
hostId
=
host
.
get
().
getUserid
();
hostName
=
new
String
(
Base64
.
getDecoder
().
decode
(
host
.
get
().
getUserName
()));
}
else
{
log
.
error
(
"未找到主持人,默认没有生成纪要权限"
);
log
.
error
(
"未找到主持人,默认没有生成纪要权限"
);
processLogService
.
log
(
meeting
.
getMeetingId
(),
subMeetingId
,
"未找到主持人,默认没有生成纪要权限"
);
//
processLogService.log(meeting.getMeetingId(),subMeetingId,"未找到主持人,默认没有生成纪要权限");
continue
;
continue
;
}
}
hostId
=
currentHosts
.
get
(
0
).
getUserid
();
hostName
=
tecentMeetingMapper
.
getUsernameByUserId
(
hostId
);
log
.
info
(
"主持人会中缺席,默认会议创建人为主持人"
);
processLogService
.
log
(
meeting
.
getMeetingId
(),
subMeetingId
,
"未找到主持人,默认会议创建人为主持人"
);
}
}
String
email
;
//判断是否有权限生成纪要
//判断是否有权限生成纪要
boolean
generateAccess
=
accessUserIds
.
stream
().
anyMatch
(
item
->
item
.
getTid
().
equals
(
hostId
));
boolean
generateAccess
=
accessUserIds
.
stream
().
anyMatch
(
item
->
item
.
getTid
().
equals
(
hostId
));
if
(!
generateAccess
){
if
(!
generateAccess
){
log
.
error
(
"【权限校验】主持人{}没有生成纪要权限,跳过生成"
,
hostId
);
log
.
error
(
"【权限校验】主持人{}没有生成纪要权限,跳过生成"
,
hostId
);
processLogService
.
log
(
meeting
.
getMeetingId
(),
subMeetingId
,
"【权限校验】主持人"
+
hostId
+
"没有生成纪要权限,跳过生成"
);
//
processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"没有生成纪要权限,跳过生成");
continue
;
continue
;
}
}
log
.
info
(
"【权限校验】主持人{}允许生成纪要"
,
hostId
);
log
.
info
(
"【权限校验】主持人{}允许生成纪要"
,
hostId
);
processLogService
.
log
(
meeting
.
getMeetingId
(),
subMeetingId
,
"【权限校验】主持人"
+
hostId
+
"允许生成纪要"
);
//
processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"允许生成纪要");
UserDTO
userDTO
=
accessUserIds
.
stream
().
filter
(
item
->
item
.
getTid
().
equals
(
hostId
)).
findFirst
().
get
();
UserDTO
userDTO
=
accessUserIds
.
stream
().
filter
(
item
->
item
.
getTid
().
equals
(
hostId
)).
findFirst
().
get
();
email
=
userDTO
.
getEmail
();
email
=
userDTO
.
getEmail
();
...
@@ -281,8 +503,8 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -281,8 +503,8 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
.
subMeetingId
(
subMeetingId
).
generateRetry
(
Boolean
.
FALSE
).
pushRetry
(
Boolean
.
FALSE
)
.
subMeetingId
(
subMeetingId
).
generateRetry
(
Boolean
.
FALSE
).
pushRetry
(
Boolean
.
FALSE
)
.
host
(
hostName
)
.
host
(
hostName
)
.
hostUid
(
hostId
)
.
hostUid
(
hostId
)
.
participantUsers
(
participants
.
stream
()
//
.participantUsers(participants.stream()
.
map
(
item
->
new
String
(
Base64
.
getDecoder
().
decode
(
item
.
getUserName
()))).
distinct
().
collect
(
Collectors
.
joining
(
"、"
)))
//
.map(item->new String(Base64.getDecoder().decode(item.getUserName()))).distinct().collect(Collectors.joining("、")))
.
recordFileId
(
recordFileIdList
.
stream
().
collect
(
Collectors
.
joining
(
","
)))
.
recordFileId
(
recordFileIdList
.
stream
().
collect
(
Collectors
.
joining
(
","
)))
.
email
(
email
)
.
email
(
email
)
.
build
();
.
build
();
...
@@ -290,10 +512,9 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -290,10 +512,9 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
meetingSaveList
.
add
(
meetingItem
);
meetingSaveList
.
add
(
meetingItem
);
}
}
}
}
}
catch
(
ClientException
e
)
{
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
e
.
printStackTrace
();
}
catch
(
ServiceException
e
)
{
continue
;
throw
new
RuntimeException
(
e
);
}
}
}
}
}
}
...
@@ -395,73 +616,50 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
...
@@ -395,73 +616,50 @@ public class TecentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,Te
}
}
/**
/**
* 生成请求腾会接口的请求头
* 拉取账户级的会议记录列表
* @param httpMethod http请求方法 GET/POST/PUT等
* @param operatorId
* @param requestUri 请求uri,eg:/v1/meetings
* @param operatorIdType
* @return
* @param startTime
*/
* @param endTime
private
Headers
generateHeaders
(
String
httpMethod
,
String
requestUri
){
* @param page
//生成签名的时间戳
* @param pageSize
String
headerTimestamp
=
String
.
valueOf
(
LocalDateTime
.
now
().
atZone
(
ZoneId
.
systemDefault
()).
toEpochSecond
());
* @throws Exception
//生成随机正整数(暂定八位
String
randomNumber
=
String
.
valueOf
((
int
)(
Math
.
random
()
*
99999999
)
+
1
);
//生成签名
String
signature
=
sign
(
tencentSecretId
,
tencentSecretKey
,
httpMethod
,
randomNumber
,
headerTimestamp
,
requestUri
,
"{}"
);
Headers
headers
=
new
Headers
.
Builder
()
.
add
(
"Content-Type"
,
"application/json"
)
.
add
(
"X-TC-Key"
,
tencentSecretId
)
.
add
(
"X-TC-Timestamp"
,
headerTimestamp
)
.
add
(
"X-TC-Nonce"
,
randomNumber
)
.
add
(
"X-TC-Signature"
,
signature
)
.
add
(
"AppId"
,
tencentAppId
)
.
add
(
"SdkId"
,
tencentSdkId
)
.
add
(
"X-TC-Registered"
,
"1"
)
.
build
();
return
headers
;
}
/**
* 生成签名,开发版本oracle jdk 1.8.0_221
*
* @param secretId 邮件下发的secret_id
* @param secretKey 邮件下发的secret_key
* @param httpMethod http请求方法 GET/POST/PUT等
* @param headerNonce X-TC-Nonce请求头,随机数
* @param headerTimestamp X-TC-Timestamp请求头,当前时间的秒级时间戳
* @param requestUri 请求uri,eg:/v1/meetings
* @param requestBody 请求体,没有的设为空串
* @return 签名,需要设置在请求头X-TC-Signature中
* @throws NoSuchAlgorithmException e
* @throws InvalidKeyException e
*/
*/
private
static
String
sign
(
String
secretId
,
String
secretKey
,
String
httpMethod
,
String
headerNonce
,
String
headerTimestamp
,
String
requestUri
,
String
requestBody
)
{
public
CorpRecordsVO
fetchMeetingRecords
(
String
operatorId
,
int
operatorIdType
,
long
startTime
,
long
endTime
,
int
page
,
int
pageSize
)
String
tobeSig
=
throws
Exception
{
httpMethod
+
"\nX-TC-Key="
+
secretId
+
"&X-TC-Nonce="
+
headerNonce
+
"&X-TC-Timestamp="
+
headerTimestamp
+
"\n"
+
requestUri
+
"\n"
+
requestBody
;
String
uri
=
String
.
format
(
Mac
mac
;
"/v1/corp/records?start_time=%d&end_time=%d&page=%d&page_size=%d&operator_id=%s&operator_id_type=%d"
,
try
{
startTime
,
endTime
,
page
,
pageSize
,
operatorId
,
operatorIdType
mac
=
Mac
.
getInstance
(
HMAC_ALGORITHM
);
);
SecretKeySpec
secretKeySpec
=
new
SecretKeySpec
(
secretKey
.
getBytes
(
StandardCharsets
.
UTF_8
),
mac
.
getAlgorithm
());
mac
.
init
(
secretKeySpec
);
String
httpMethod
=
"GET"
;
}
catch
(
Exception
e
)
{
String
nonce
=
String
.
valueOf
(
new
Random
().
nextInt
(
100000
));
log
.
error
(
"获取腾会签名错误:"
+
e
.
getMessage
());
String
timestamp
=
String
.
valueOf
(
Instant
.
now
().
getEpochSecond
());
throw
new
RuntimeException
(
"请求失败"
);
}
// 3. 生成签名
byte
[]
hash
=
mac
.
doFinal
(
tobeSig
.
getBytes
(
StandardCharsets
.
UTF_8
));
String
signature
=
SignatureUtil
.
generateSignature
(
tencentSecretId
,
tencentSecretKey
,
httpMethod
,
nonce
,
timestamp
,
uri
,
""
);
String
hexHash
=
bytesToHex
(
hash
);
String
signature
=
new
String
(
Base64
.
getEncoder
().
encode
(
hexHash
.
getBytes
(
StandardCharsets
.
UTF_8
)));
// 4. 发送请求
return
signature
;
try
(
CloseableHttpClient
httpClient
=
HttpClients
.
createDefault
())
{
}
HttpGet
request
=
new
HttpGet
(
"https://api.meeting.qq.com"
+
uri
);
request
.
setHeader
(
"X-TC-Key"
,
tencentSecretId
);
private
static
String
bytesToHex
(
byte
[]
bytes
)
{
request
.
setHeader
(
"X-TC-Timestamp"
,
timestamp
);
request
.
setHeader
(
"X-TC-Nonce"
,
nonce
);
char
[]
buf
=
new
char
[
bytes
.
length
*
2
];
request
.
setHeader
(
"X-TC-Signature"
,
signature
);
int
index
=
0
;
request
.
setHeader
(
"AppId"
,
tencentAppId
);
for
(
byte
b
:
bytes
)
{
request
.
setHeader
(
"SdkId"
,
tencentSdkId
);
buf
[
index
++]
=
HEX_CHAR
[
b
>>>
4
&
0xf
];
buf
[
index
++]
=
HEX_CHAR
[
b
&
0xf
];
// 5. 解析响应
String
response
=
EntityUtils
.
toString
(
httpClient
.
execute
(
request
).
getEntity
());
JsonObject
jsonResponse
=
JsonParser
.
parseString
(
response
).
getAsJsonObject
();
Gson
gson
=
new
GsonBuilder
()
.
setFieldNamingPolicy
(
FieldNamingPolicy
.
LOWER_CASE_WITH_UNDERSCORES
)
.
create
();
// 将 JsonObject 转换为实体类
CorpRecordsVO
corpRecords
=
gson
.
fromJson
(
jsonResponse
,
CorpRecordsVO
.
class
);
return
corpRecords
;
}
}
return
new
String
(
buf
);
}
}
/**
/**
...
...
src/main/java/com/cmeeting/util/MinioUtils.java
浏览文件 @
2089d7b6
...
@@ -4,10 +4,8 @@ import io.minio.MinioClient;
...
@@ -4,10 +4,8 @@ import io.minio.MinioClient;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Component
;
import
java.io.ByteArrayInputStream
;
import
java.io.*
;
import
java.io.FileInputStream
;
import
java.nio.charset.StandardCharsets
;
import
java.io.IOException
;
import
java.io.InputStream
;
@Component
@Component
public
class
MinioUtils
{
public
class
MinioUtils
{
...
@@ -33,6 +31,20 @@ public class MinioUtils {
...
@@ -33,6 +31,20 @@ public class MinioUtils {
}
}
}
}
public
String
getFileText
(
String
fileName
)
throws
IOException
{
InputStream
is
=
getFile
(
fileName
);
StringBuilder
textBuilder
=
new
StringBuilder
();
try
(
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
is
,
StandardCharsets
.
UTF_8
)))
{
String
line
;
while
((
line
=
reader
.
readLine
())
!=
null
)
{
textBuilder
.
append
(
line
);
textBuilder
.
append
(
System
.
lineSeparator
());
}
}
return
textBuilder
.
toString
();
}
public
InputStream
getFile
(
String
fileName
){
public
InputStream
getFile
(
String
fileName
){
try
{
try
{
getMinioClient
();
getMinioClient
();
...
@@ -57,11 +69,6 @@ public class MinioUtils {
...
@@ -57,11 +69,6 @@ public class MinioUtils {
public
void
upload
(
String
path
,
byte
[]
bytes
)
{
public
void
upload
(
String
path
,
byte
[]
bytes
)
{
try
(
ByteArrayInputStream
inputStream
=
new
ByteArrayInputStream
(
bytes
))
{
try
(
ByteArrayInputStream
inputStream
=
new
ByteArrayInputStream
(
bytes
))
{
// 自动管理流的生命周期,无需手动close
int
data
;
while
((
data
=
inputStream
.
read
())
!=
-
1
)
{
System
.
out
.
print
((
char
)
data
);
}
upload
(
path
,
inputStream
);
upload
(
path
,
inputStream
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
throw
new
RuntimeException
(
e
);
...
...
src/main/java/com/cmeeting/util/SignatureUtil.java
0 → 100644
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
util
;
import
javax.crypto.Mac
;
import
javax.crypto.spec.SecretKeySpec
;
import
java.nio.charset.StandardCharsets
;
import
java.security.InvalidKeyException
;
import
java.security.NoSuchAlgorithmException
;
import
java.util.Base64
;
public
class
SignatureUtil
{
static
String
HMAC_ALGORITHM
=
"HmacSHA256"
;
static
char
[]
HEX_CHAR
=
{
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
};
static
String
bytesToHex
(
byte
[]
bytes
)
{
char
[]
buf
=
new
char
[
bytes
.
length
*
2
];
int
index
=
0
;
for
(
byte
b
:
bytes
)
{
buf
[
index
++]
=
HEX_CHAR
[
b
>>>
4
&
0xf
];
buf
[
index
++]
=
HEX_CHAR
[
b
&
0xf
];
}
return
new
String
(
buf
);
}
public
static
String
generateSignature
(
String
secretId
,
String
secretKey
,
String
httpMethod
,
String
headerNonce
,
String
headerTimestamp
,
String
requestUri
,
String
requestBody
)
throws
NoSuchAlgorithmException
,
InvalidKeyException
{
String
tobeSig
=
httpMethod
+
"\nX-TC-Key="
+
secretId
+
"&X-TC-Nonce="
+
headerNonce
+
"&X-TC-Timestamp="
+
headerTimestamp
+
"\n"
+
requestUri
+
"\n"
+
requestBody
;
Mac
mac
=
Mac
.
getInstance
(
HMAC_ALGORITHM
);
SecretKeySpec
secretKeySpec
=
new
SecretKeySpec
(
secretKey
.
getBytes
(
StandardCharsets
.
UTF_8
),
mac
.
getAlgorithm
());
mac
.
init
(
secretKeySpec
);
byte
[]
hash
=
mac
.
doFinal
(
tobeSig
.
getBytes
(
StandardCharsets
.
UTF_8
));
String
hexHash
=
bytesToHex
(
hash
);
return
new
String
(
Base64
.
getEncoder
().
encode
(
hexHash
.
getBytes
(
StandardCharsets
.
UTF_8
)));
}
}
src/main/java/com/cmeeting/vo/CorpRecordsVO.java
0 → 100644
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
vo
;
import
lombok.Data
;
import
java.util.List
;
/**
* 会议纪要模板展示层
*/
/**
* 会议纪要模板展示层
*/
@Data
public
class
CorpRecordsVO
{
private
Integer
totalCount
;
private
Integer
totalPage
;
private
Integer
currentPage
;
private
Integer
currentSize
;
private
List
<
RecordMeeting
>
recordMeetings
;
@Data
public
class
RecordMeeting
{
private
String
meetingRecordId
;
private
String
meetingId
;
private
String
meetingCode
;
private
String
userid
;
private
String
hostUserId
;
private
Integer
mediaStartTime
;
private
String
subject
;
private
Integer
state
;
private
Integer
recordType
;
private
List
<
RecordFile
>
recordFiles
;
}
@Data
public
class
RecordFile
{
private
String
recordFileId
;
private
Integer
recordStartTime
;
private
Integer
recordEndTime
;
private
Integer
recordSize
;
private
Integer
sharingState
;
private
String
sharingUrl
;
private
Boolean
requiredSameCorp
;
private
Boolean
requiredParticipant
;
private
Integer
sharingExpire
;
private
Boolean
allowDownload
;
private
String
downloadAddress
;
private
String
viewAddress
;
}
}
\ No newline at end of file
src/main/java/com/cmeeting/vo/EmailPush.java
0 → 100644
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
vo
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
;
import
java.util.List
;
/**
* 会议纪要模板展示层
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public
class
EmailPush
{
private
String
toEmail
;
private
String
subject
;
private
String
meetingId
;
/**
* 附件
*/
private
List
<
Attachment
>
attachments
;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public
static
class
Attachment
{
private
String
name
;
private
byte
[]
bytes
;
}
}
\ No newline at end of file
src/main/java/com/cmeeting/vo/MeetingInfoVO.java
0 → 100644
浏览文件 @
2089d7b6
package
com
.
cmeeting
.
vo
;
import
lombok.Data
;
import
java.time.LocalDateTime
;
/**
* 会议纪要模板展示层
*/
@Data
public
class
MeetingInfoVO
{
/**
* 当前页
*/
private
Integer
current
;
/**
* 每页最大数据行
*/
private
Integer
size
;
/**
* 主键id
*/
private
Integer
id
;
/**
* 会议主题
*/
private
String
subject
;
/**
* 会议ID(字符串类型)
*/
private
String
meetingId
;
/**
* 子会议ID
*/
private
String
subMeetingId
;
/**
* 会议号码
*/
private
String
meetingCode
;
/**
* 主持人
*/
private
String
host
;
/**
* 主持人uid
*/
private
String
hostUid
;
/**
* 参会人员名单
*/
private
String
participantUsers
;
/**
* 会议开始时间(时间戳)
*/
private
LocalDateTime
startTime
;
/**
* 会议结束时间(时间戳)
*/
private
LocalDateTime
endTime
;
/**
* 是否生成会议纪要完成
*/
private
Boolean
isGenerated
;
/**
* 推送邮件许可 为false不推送
*/
private
Boolean
emailPushAccess
;
/**
* 是否推送邮件完成
*/
private
Boolean
isPushed
;
/**
* 会议纪要重新生成标识
*/
private
Boolean
generateRetry
;
/**
* 邮件推送重试标识
*/
private
Boolean
pushRetry
;
/**
* 同步时间
*/
private
LocalDateTime
syncTime
;
/**
* 转录文本
*/
private
String
recordContent
;
/**
* 纪要xml
*/
private
String
recordXml
;
/**
* 每个会议对应的转录文件id,多个用逗号分隔,按参会前后升序
*/
private
String
recordFileId
;
/**
* 邮箱
*/
private
String
email
;
/**
* 同步时间
*/
private
LocalDateTime
updateTime
;
/**
* 模板ID
*/
private
Integer
templateId
;
/**
* 会议纪要json
*/
private
String
recordJson
;
}
\ No newline at end of file
src/main/resources/template/data_net_template.docx
deleted
100644 → 0
浏览文件 @
4ce8c62e
File deleted
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论