Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
cmeeting
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
翟斌
cmeeting
Commits
8a23a94c
提交
8a23a94c
authored
5月 21, 2025
作者:
duanxincheng
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
纪要生成服务优化
父级
0a34c27f
全部展开
显示空白字符变更
内嵌
并排
正在显示
24 个修改的文件
包含
541 行增加
和
179 行删除
+541
-179
pom.xml
+16
-8
src/main/java/com/cmeeting/TencentMeetingCallbackController.java
+11
-11
src/main/java/com/cmeeting/config/ThreadPoolConfig.java
+43
-0
src/main/java/com/cmeeting/controller/TencentMeetingController.java
+7
-20
src/main/java/com/cmeeting/controller/UnifiedController.java
+11
-8
src/main/java/com/cmeeting/controller/WeComcontroller.java
+1
-61
src/main/java/com/cmeeting/email/EmailSender.java
+85
-58
src/main/java/com/cmeeting/job/CmeetingJob.java
+0
-0
src/main/java/com/cmeeting/job/CmeetingJob2.java
+0
-0
src/main/java/com/cmeeting/job/FileProcessTask.java
+0
-0
src/main/java/com/cmeeting/mapper/primary/TecentMeetingMapper.java
+2
-2
src/main/java/com/cmeeting/mapper/primary/WeComUserMapper.java
+4
-4
src/main/java/com/cmeeting/pojo/TencentMeetingRecord.java
+26
-0
src/main/java/com/cmeeting/pojo/WeComUser.java
+7
-1
src/main/java/com/cmeeting/service/FileProcessCallbackHandler.java
+26
-0
src/main/java/com/cmeeting/service/FileProcessProducer.java
+82
-0
src/main/java/com/cmeeting/service/TecentMeetingService.java
+8
-1
src/main/java/com/cmeeting/service/WeComService.java
+6
-1
src/main/java/com/cmeeting/service/impl/TecentMeetingServiceImpl.java
+0
-0
src/main/java/com/cmeeting/service/impl/WeComServiceImpl.java
+144
-2
src/main/java/com/cmeeting/vo/TencentMeetingVO.java
+36
-0
src/main/resources/application.properties
+19
-0
src/main/resources/mapper/primary/TecentMeetingMapper.xml
+2
-0
src/main/resources/mapper/primary/WeComUserMapper.xml
+5
-2
没有找到文件。
pom.xml
浏览文件 @
8a23a94c
...
...
@@ -31,6 +31,7 @@
<maven.compiler.source>
1.8
</maven.compiler.source>
<maven.compiler.target>
1.8
</maven.compiler.target>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<mybatis-plus.version>
3.3.0
</mybatis-plus.version>
</properties>
<dependencyManagement>
<dependencies>
...
...
@@ -246,11 +247,11 @@
</dependency>
<dependency
>
<groupId>
org.mybatis.spring.boot
</groupId
>
<artifactId>
mybatis-spring-boot-starter
</artifactId
>
<version>
2.3.0
</version>
<!-- 请使用最新版本
-->
</dependency
>
<!-- <dependency>--
>
<!-- <groupId>org.mybatis.spring.boot</groupId>--
>
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>--
>
<!-- <version>2.3.0</version> <!– 请使用最新版本 –>
-->
<!-- </dependency>--
>
<dependency>
<groupId>
mysql
</groupId>
<artifactId>
mysql-connector-java
</artifactId>
...
...
@@ -293,10 +294,11 @@
</dependency>
<!-- Word 文档操作 -->
<dependency>
<groupId>
org.apache.poi
</groupId>
<artifactId>
poi-
ooxm
l
</artifactId>
<version>
5.2.3
</version>
<groupId>
com.deepoove
</groupId>
<artifactId>
poi-
t
l
</artifactId>
<version>
1.12.2
</version>
</dependency>
...
...
@@ -305,6 +307,12 @@
<artifactId>
commonmark
</artifactId>
<version>
0.17.1
</version>
<!-- 可升级至最新稳定版 -->
</dependency>
<dependency>
<groupId>
com.baomidou
</groupId>
<artifactId>
mybatis-plus-boot-starter
</artifactId>
<version>
${mybatis-plus.version}
</version>
</dependency>
</dependencies>
...
...
src/main/java/com/cmeeting/TencentMeetingCallbackController.java
浏览文件 @
8a23a94c
...
...
@@ -431,17 +431,17 @@ public class TencentMeetingCallbackController {
Thread
.
sleep
(
10000
);
EmailSender
emailSender
=
new
EmailSender
();
//response.getRawBody()
boolean
mailFlag
=
emailSender
.
sendEmailWithAttachment
(
emailAddress
,
targetPath
,
"重要文件"
,
"您好:\n"
+
"\n"
+
" 附件为您本次会议的会议纪要,烦请下载查看,如需对会议纪要结果进行修改或查看历史会议,可点击下方链接。"
);
if
(
mailFlag
)
{
logger
.
info
(
"邮件发送成功"
);
}
else
{
logger
.
error
(
"邮件发送失败"
);
}
//
boolean mailFlag = emailSender.sendEmailWithAttachment(emailAddress,
//
targetPath,
//
"重要文件",
//
"您好:\n" +
//
"\n" +
//
" 附件为您本次会议的会议纪要,烦请下载查看,如需对会议纪要结果进行修改或查看历史会议,可点击下方链接。");
//
if (mailFlag) {
//
logger.info("邮件发送成功");
//
} else {
//
logger.error("邮件发送失败");
//
}
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"下载文件时出错: {}"
,
e
.
getMessage
());
...
...
src/main/java/com/cmeeting/config/ThreadPoolConfig.java
0 → 100644
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
config
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
;
import
java.util.concurrent.ThreadPoolExecutor
;
@Configuration
@Slf4j
public
class
ThreadPoolConfig
{
@Bean
(
"fileProcessExecutor"
)
public
ThreadPoolTaskExecutor
fileProcessExecutor
()
{
ThreadPoolTaskExecutor
executor
=
new
ThreadPoolTaskExecutor
();
// 核心线程数 (CPU密集型任务建议核心数+1)
executor
.
setCorePoolSize
(
4
);
// 固定核心线程数,避免动态获取CPU核心数
// 最大线程数
executor
.
setMaxPoolSize
(
4
);
// 队列容量
executor
.
setQueueCapacity
(
1000
);
// 线程名前缀
executor
.
setThreadNamePrefix
(
"file-process-"
);
// 明确设置所有必要属性
executor
.
setAllowCoreThreadTimeOut
(
false
);
// 核心线程不允许超时
executor
.
setWaitForTasksToCompleteOnShutdown
(
true
);
// 优雅关闭
executor
.
setAwaitTerminationSeconds
(
60
);
// 等待任务完成的最大时间
// 拒绝策略
executor
.
setRejectedExecutionHandler
(
new
ThreadPoolExecutor
.
CallerRunsPolicy
());
// 初始化前打印配置检查
log
.
info
(
"Initializing ThreadPool: core={}, max={}"
,
executor
.
getCorePoolSize
(),
executor
.
getMaxPoolSize
());
executor
.
initialize
();
return
executor
;
}
}
\ No newline at end of file
src/main/java/com/cmeeting/controller/TencentMeetingController.java
浏览文件 @
8a23a94c
...
...
@@ -2,6 +2,7 @@ package com.cmeeting.controller;
import
com.cmeeting.pojo.TencentMeetingUser
;
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.web.bind.annotation.GetMapping
;
...
...
@@ -13,32 +14,18 @@ import java.util.List;
import
static
com
.
cmeeting
.
WeComAndTencentMeeting
.*;
@RestController
@RequestMapping
(
"/tecent"
)
@RequestMapping
(
"/te
n
cent"
)
public
class
TencentMeetingController
{
@Autowired
private
TecentMeetingService
tecentMeetingService
;
@GetMapping
(
"/add"
)
public
void
addUsers
()
throws
Exception
{
dou
sers
();
public
void
addUsers
()
{
tecentMeetingService
.
doU
sers
();
}
private
void
dousers
()
{
/**
* 腾讯会议通过通讯录获取员工信息
*/
// 1. 构造client客户端
Client
client
=
new
Client
.
Builder
()
.
withAppId
(
"211153201"
).
withSdkId
(
"28370276340"
)
.
withSecret
(
"BKOMDZVbvh0iT7k6UHsSizAWBCOVDtT6"
,
"3Y1j0mzNp7KChKFJGyaEnZHLobFoAQ8eLwfaMx8nLbtXAerO"
)
.
build
();
// 2.获取到全部用户
List
<
TencentMeetingUser
>
users
=
fetchUsersInBatches
(
client
,
3
);;
// 3. 检查重名并设置标志
markDuplicateNamesTecent
(
users
);
// 4. 批量插入数据库
tecentMeetingService
.
batchInsert
(
users
);
@GetMapping
(
"/getMeetingFiles"
)
public
void
getMeetingFiles
(
TencentMeetingVO
.
TencentMeetingRequest
requestVO
){
tecentMeetingService
.
getMeetingFiles
(
requestVO
);
}
}
src/main/java/com/cmeeting/controller/UnifiedController.java
浏览文件 @
8a23a94c
...
...
@@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RestController
;
import
org.json.JSONObject
;
import
javax.annotation.Resource
;
import
java.io.IOException
;
import
java.math.BigInteger
;
import
java.security.SecureRandom
;
...
...
@@ -33,9 +34,9 @@ import java.util.stream.Collectors;
@RestController
@RequestMapping
public
class
UnifiedController
{
@
Autowired
@
Resource
private
WeComUserMapper
weComUserMapper
;
@
Autowired
@
Resource
private
TecentMeetingMapper
tecentMeetingMapper
;
@Autowired
private
UserIdMapper
userIdMapper
;
...
...
@@ -78,7 +79,7 @@ public class UnifiedController {
}
@GetMapping
(
"/insertTid"
)
public
void
insertTid
()
{
public
void
insertTid
(
String
corpid
,
String
corpsecret
)
{
List
<
UserId
>
users
=
userIdMapper
.
getUsers
();
List
<
UserId
>
usersWithNullTid
=
new
ArrayList
<>();
for
(
UserId
user
:
users
)
{
...
...
@@ -88,7 +89,7 @@ public class UnifiedController {
}
for
(
UserId
user
:
usersWithNullTid
)
{
//获取企业微信token
String
weComToken
=
getWeComToken
();
String
weComToken
=
getWeComToken
(
corpid
,
corpsecret
);
try
{
// 3.1 创建会议(传入用户的 wid)
Map
<
String
,
String
>
meetingCodeAndMeetingid
=
createMeeting
(
user
.
getWid
(),
weComToken
);
...
...
@@ -128,8 +129,8 @@ public class UnifiedController {
//todo 待测试
@GetMapping
(
"/sameNameInsertTid"
)
public
void
sameNameInsertTid
()
throws
IOException
{
String
weComToken
=
getWeComToken
();
public
void
sameNameInsertTid
(
String
corpid
,
String
corpsecret
)
throws
IOException
{
String
weComToken
=
getWeComToken
(
corpid
,
corpsecret
);
List
<
WeComUser
>
sameNameUsers
=
weComUserMapper
.
getSameName
();
List
<
UserId
>
userIds
=
new
ArrayList
<>();
for
(
WeComUser
user
:
sameNameUsers
)
{
...
...
@@ -269,9 +270,11 @@ public class UnifiedController {
*
* @return
*/
public
String
getWeComToken
()
{
@GetMapping
(
"/getWeComToken"
)
public
String
getWeComToken
(
String
corpid
,
String
corpsecret
)
{
//获取token
String
url
=
"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww1fd8778458e9f1e8&corpsecret=uFRq9Xi8-dVY90LydXYBhjc91JnnfkPUR6lHDdeJ_fo"
;
// String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww1fd8778458e9f1e8&corpsecret=uFRq9Xi8-dVY90LydXYBhjc91JnnfkPUR6lHDdeJ_fo";
String
url
=
"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="
+
corpid
+
"&corpsecret="
+
corpsecret
;
String
accessToken
=
""
;
OkHttpClient
client
=
new
OkHttpClient
();
Request
request
=
new
Request
.
Builder
()
...
...
src/main/java/com/cmeeting/controller/WeComcontroller.java
浏览文件 @
8a23a94c
...
...
@@ -23,73 +23,13 @@ import static com.cmeeting.WeComAndTencentMeeting.*;
@RestController
@RequestMapping
(
"/wecom"
)
public
class
WeComcontroller
{
private
static
final
String
BASE_URL
=
"https://qyapi.weixin.qq.com/cgi-bin/user/simplelist"
;
private
static
final
String
ACCESS_TOKEN
=
"wyYAmCbeVIPvj_Gpdd8ixp_td_hPv2OtTU8gpTGSpB7TaftbpNps5fc_JdCRzpgy_tUv-aGueJpRV6XJeYAG-rqkzCfDV-rcpJbqB0LypjvJUtxPeTmPqEtQuCMwH6euGGVaJZXoc33ATrS70U0T4_4WIrATqpsN2Ed2XsNGyN5N6aG8mq6AyLMG9TokVWx1qd2qF1zVTwve6aqRMWHwXg"
;
//动态获取token
@Autowired
private
WeComService
weComService
;
@GetMapping
(
"/add"
)
public
void
addUsers
()
throws
Exception
{
dousers
();
}
public
void
dousers
()
throws
Exception
{
// 示例:获取部门ID为6的用户列表
int
departmentId
=
6
;
JsonObject
result
=
getUserListByDepartment
(
departmentId
);
// 处理返回结果
if
(
result
.
get
(
"errcode"
).
getAsInt
()
==
0
)
{
System
.
out
.
println
(
"企微获取用户列表成功:"
);
JsonArray
userList
=
result
.
getAsJsonArray
(
"userlist"
);
for
(
int
i
=
0
;
i
<
userList
.
size
();
i
++)
{
JsonObject
user
=
userList
.
get
(
i
).
getAsJsonObject
();
System
.
out
.
println
(
"姓名: "
+
user
.
get
(
"name"
).
getAsString
()
+
", UserID: "
+
user
.
get
(
"userid"
).
getAsString
());
}
// 2. 转换为WeComUser集合
List
<
WeComUser
>
users
=
convertJsonToWeComUsers
(
result
);
// 3. 检查重名并设置标志
markDuplicateNames
(
users
);
// 4. 批量插入数据库
weComService
.
batchInsert
(
users
);
}
}
/**
* 根据企业微信部门ID获取用户列表
*
* @param departmentId 部门ID
* @return 包含用户列表的JsonObject
* @throws Exception
*/
public
static
JsonObject
getUserListByDepartment
(
int
departmentId
)
throws
Exception
{
String
urlStr
=
BASE_URL
+
"?access_token="
+
ACCESS_TOKEN
+
"&department_id="
+
departmentId
;
URL
url
=
new
URL
(
urlStr
);
HttpURLConnection
conn
=
(
HttpURLConnection
)
url
.
openConnection
();
conn
.
setRequestMethod
(
"GET"
);
int
responseCode
=
conn
.
getResponseCode
();
if
(
responseCode
==
HttpURLConnection
.
HTTP_OK
)
{
BufferedReader
in
=
new
BufferedReader
(
new
InputStreamReader
(
conn
.
getInputStream
()));
String
inputLine
;
StringBuilder
response
=
new
StringBuilder
();
while
((
inputLine
=
in
.
readLine
())
!=
null
)
{
response
.
append
(
inputLine
);
}
in
.
close
();
// 使用Gson解析JSON响应
Gson
gson
=
new
Gson
();
return
gson
.
fromJson
(
response
.
toString
(),
JsonObject
.
class
);
}
else
{
throw
new
RuntimeException
(
"HTTP GET请求失败,错误码: "
+
responseCode
);
}
weComService
.
doUsers
();
}
...
...
src/main/java/com/cmeeting/email/EmailSender.java
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
email
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Value
;
import
javax.activation.DataHandler
;
import
javax.activation.DataSource
;
import
javax.activation.FileDataSource
;
import
javax.mail.*
;
import
javax.mail.internet.*
;
import
java.io.File
;
import
java.util.Properties
;
import
java.util.concurrent.atomic.AtomicInteger
;
@Slf4j
public
class
EmailSender
{
// 发件人邮箱配置(建议从配置文件读取,此处写死示例)
private
static
final
String
FROM_EMAIL
=
"binzhai321@163.com"
;
private
static
final
String
AUTH_CODE
=
"RXbRu3AdrCxX57ib"
;
// 授权码
private
static
final
String
SMTP_HOST
=
"smtp.163.com"
;
private
static
final
int
SMTP_PORT
=
465
;
@Value
(
"${email.sender}"
)
private
String
SENDER
;
@Value
(
"${email.sender.pwd}"
)
private
String
EMAIL_PWD
;
@Value
(
"${email.smtp.host}"
)
private
String
SMTP_HOST
;
private
static
final
Integer
MAX_RETRY
=
3
;
/**
* 发送带附件的邮件
* @param toEmail 收件人邮箱
* @param filePath 附件本地路径(如 "C:/test.pdf")
* @param subject 邮件主题(可选,默认值)
* @param text 邮件正文(可选,默认值)
* @return true发送成功,false发送失败
* @param toEmail 收件人
* @param subject 邮件主题
* @param attachmentPath 附件路径
* @param recordFileId 转录文件ID
* @return
*/
public
static
boolean
sendEmailWithAttachment
(
String
toEmail
,
String
filePath
,
String
subject
,
String
text
)
{
// 1. 参数校验
if
(
toEmail
==
null
||
toEmail
.
isEmpty
()
||
filePath
==
null
)
{
System
.
err
.
println
(
"参数错误:收件人或附件路径为空"
);
return
false
;
}
// 2. 设置默认邮件主题和正文
String
mailSubject
=
(
subject
!=
null
)
?
subject
:
""
;
String
mailText
=
(
text
!=
null
)
?
text
:
"请查收附件。"
;
// 3. 配置SMTP
public
boolean
sendEmailWithAttachment
(
String
toEmail
,
String
subject
,
String
attachmentPath
,
String
recordFileId
)
{
// 邮件服务器配置
Properties
props
=
new
Properties
();
props
.
put
(
"mail.smtp.host"
,
SMTP_HOST
);
props
.
put
(
"mail.smtp.port"
,
SMTP_PORT
);
props
.
put
(
"mail.smtp.host"
,
"smtp.office365.com"
);
props
.
put
(
"mail.smtp.auth"
,
"true"
);
props
.
put
(
"mail.smtp.ssl.enable"
,
"true"
);
try
{
// 4. 创建会话
Session
session
=
Session
.
getInstance
(
props
,
new
Authenticator
()
{
@Override
// props.put("mail.smtp.port", "465");
// props.put("mail.smtp.ssl.enable", "true"); // 使用SSL
props
.
put
(
"mail.smtp.port"
,
"587"
);
props
.
put
(
"mail.smtp.starttls.enable"
,
"true"
);
// 使用TLS
// 或者使用SSL
// props.put("mail.smtp.port", "465");
// props.put("mail.smtp.ssl.enable", "true");
// 创建会话
Session
session
=
Session
.
getInstance
(
props
,
new
javax
.
mail
.
Authenticator
()
{
protected
PasswordAuthentication
getPasswordAuthentication
()
{
return
new
PasswordAuthentication
(
FROM_EMAIL
,
AUTH_CODE
);
return
new
PasswordAuthentication
(
"cmeeting_assistant@cimc.com"
,
"scyou@xih45g6@xih4"
);
}
});
String
body
=
"您好:\n"
+
"\n"
+
" 附件为您本次会议的会议纪要,烦请下载查看"
;
AtomicInteger
retryCount
=
new
AtomicInteger
(
0
);
try
{
boolean
isSent
=
false
;
while
(
retryCount
.
intValue
()
<
MAX_RETRY
&&
!
isSent
){
// 创建邮件消息
Message
message
=
new
MimeMessage
(
session
);
message
.
setFrom
(
new
InternetAddress
(
"cmeeting_assistant@cimc.com"
));
message
.
setRecipients
(
Message
.
RecipientType
.
TO
,
InternetAddress
.
parse
(
toEmail
));
message
.
setSubject
(
subject
);
// 5. 构建邮件内容
MimeMessage
message
=
new
MimeMessage
(
session
);
message
.
setFrom
(
new
InternetAddress
(
FROM_EMAIL
));
message
.
setRecipient
(
Message
.
RecipientType
.
TO
,
new
InternetAddress
(
toEmail
));
message
.
setSubject
(
mailSubject
);
// 创建消息体部分(正文)
MimeBodyPart
messageBodyPart
=
new
MimeBodyPart
();
messageBodyPart
.
setText
(
body
);
// 6. 添加正文和附件
// 创建多部分消息
Multipart
multipart
=
new
MimeMultipart
();
// 文本正文
MimeBodyPart
textPart
=
new
MimeBodyPart
();
textPart
.
setText
(
mailText
);
multipart
.
addBodyPart
(
textPart
);
// 添加文本部分
multipart
.
addBodyPart
(
messageBodyPart
);
// 附件
// 添加附件部分
if
(
attachmentPath
!=
null
&&
!
attachmentPath
.
isEmpty
())
{
MimeBodyPart
attachmentPart
=
new
MimeBodyPart
();
attachmentPart
.
attachFile
(
new
File
(
filePath
));
DataSource
source
=
new
FileDataSource
(
attachmentPath
);
attachmentPart
.
setDataHandler
(
new
DataHandler
(
source
));
attachmentPart
.
setFileName
(
new
File
(
attachmentPath
).
getName
());
multipart
.
addBodyPart
(
attachmentPart
);
}
// 设置完整消息内容
message
.
setContent
(
multipart
);
// 7.
发送邮件
//
发送邮件
Transport
.
send
(
message
);
System
.
out
.
println
(
"邮件发送成功至: "
+
toEmail
);
return
true
;
}
catch
(
Exception
e
)
{
System
.
err
.
println
(
"邮件发送失败: "
+
e
.
getMessage
());
return
false
;
log
.
error
(
"邮件已成功发送: recordFileId->{}"
,
recordFileId
);
isSent
=
true
;
}
}
catch
(
MessagingException
e
)
{
//todo 邮件失败记录
// 异常处理
retryCount
.
getAndIncrement
();
if
(
retryCount
.
intValue
()
>
MAX_RETRY
)
{
log
.
error
(
"邮件发送达到最大重试次数: recordFileId->{}"
,
recordFileId
);
throw
new
RuntimeException
(
e
);
}
/**
* 简化版调用(仅需收件人和附件路径)
*/
public
static
boolean
sendEmailWithAttachment
(
String
toEmail
,
String
filePath
)
{
return
sendEmailWithAttachment
(
toEmail
,
filePath
,
null
,
null
);
// 指数退避
try
{
Thread
.
sleep
((
long
)
Math
.
pow
(
2
,
retryCount
.
intValue
())
*
1000
);
}
catch
(
InterruptedException
ie
)
{
Thread
.
currentThread
().
interrupt
();
throw
new
RuntimeException
(
"邮件发送重试失败"
,
ie
);
}
return
false
;
}
return
true
;
}
}
\ No newline at end of file
src/main/java/com/cmeeting/job/CmeetingJob.java
浏览文件 @
8a23a94c
差异被折叠。
点击展开。
src/main/java/com/cmeeting/job/CmeetingJob2.java
deleted
100644 → 0
浏览文件 @
0a34c27f
差异被折叠。
点击展开。
src/main/java/com/cmeeting/job/FileProcessTask.java
0 → 100644
浏览文件 @
8a23a94c
差异被折叠。
点击展开。
src/main/java/com/cmeeting/mapper/primary/TecentMeetingMapper.java
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
mapper
.
primary
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
import
com.cmeeting.pojo.TencentMeetingUser
;
import
org.apache.ibatis.annotations.Mapper
;
import
org.apache.ibatis.annotations.Param
;
import
java.util.List
;
@Mapper
public
interface
TecentMeetingMapper
{
public
interface
TecentMeetingMapper
extends
BaseMapper
<
TencentMeetingUser
>
{
void
batchInsertUsers
(
@Param
(
"userList"
)
List
<
TencentMeetingUser
>
userList
);
List
<
TencentMeetingUser
>
getAlluser
();
}
src/main/java/com/cmeeting/mapper/primary/WeComUserMapper.java
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
mapper
.
primary
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
import
com.cmeeting.pojo.WeComUser
;
import
org.apache.ibatis.annotations.Mapper
;
import
java.util.List
;
@Mapper
public
interface
WeComUserMapper
{
public
interface
WeComUserMapper
extends
BaseMapper
<
WeComUser
>
{
List
<
WeComUser
>
getSameName
()
;
WeComUser
selectById
(
Integer
id
);
void
insert
(
WeComUser
user
);
int
insert
(
WeComUser
user
);
List
<
WeComUser
>
getAlluser
();
/**
* 批量插入用户
...
...
src/main/java/com/cmeeting/pojo/TencentMeetingRecord.java
0 → 100644
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
pojo
;
import
lombok.Data
;
@Data
public
class
TencentMeetingRecord
{
/**
* 主键ID
*/
private
Integer
id
;
/**
* 会议id
*/
private
String
meetingId
;
/**
* 用户ID
*/
private
String
userId
;
/**
* 是否是重名用户(1:重名, 0:不重名)
*/
private
String
isrepeatName
;
}
src/main/java/com/cmeeting/pojo/WeComUser.java
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
pojo
;
import
com.baomidou.mybatisplus.extension.activerecord.Model
;
import
lombok.Data
;
import
java.io.Serializable
;
@Data
public
class
WeComUser
{
public
class
WeComUser
extends
Model
implements
Serializable
{
private
Integer
id
;
private
String
userName
;
private
String
userId
;
private
String
isRepeatName
;
private
String
email
;
public
WeComUser
()
{
}
public
WeComUser
(
Integer
id
,
String
userName
,
String
userId
,
String
isRepeatName
)
{
...
...
@@ -24,6 +29,7 @@ public class WeComUser {
", userName='"
+
userName
+
'\''
+
", userId='"
+
userId
+
'\''
+
", isRepeatName='"
+
isRepeatName
+
'\''
+
", email='"
+
email
+
'\''
+
'}'
;
}
}
src/main/java/com/cmeeting/service/FileProcessCallbackHandler.java
0 → 100644
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
service
;
import
com.cmeeting.job.FileProcessTask
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.stereotype.Service
;
@Service
@Slf4j
public
class
FileProcessCallbackHandler
{
// 单个任务完成回调
public
void
onComplete
(
FileProcessTask
task
)
{
// 可以记录任务状态、发送通知等
log
.
info
(
"任务处理完成: {}"
,
task
.
getRecordFileId
());
// 更新数据库状态等
// taskRepository.updateStatus(task.getId(), "COMPLETED");
}
// 所有任务完成回调
public
void
onAllComplete
()
{
log
.
info
(
"所有文件处理任务已完成"
);
// 可以发送全局通知等
}
}
\ No newline at end of file
src/main/java/com/cmeeting/service/FileProcessProducer.java
0 → 100644
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
service
;
import
com.cmeeting.job.FileProcessTask
;
import
com.cmeeting.vo.TencentMeetingVO
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
;
import
org.springframework.stereotype.Service
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.concurrent.ExecutionException
;
import
java.util.concurrent.Future
;
@Service
@Slf4j
public
class
FileProcessProducer
{
@Autowired
private
ThreadPoolTaskExecutor
fileProcessExecutor
;
@Autowired
private
FileProcessCallbackHandler
callbackHandler
;
@Value
(
value
=
"${tencent.appId}"
)
private
String
tencentAppId
;
@Value
(
value
=
"${tencent.sdkId}"
)
private
String
tencentSdkId
;
@Value
(
value
=
"${tencent.secretId}"
)
private
String
tencentSecretId
;
@Value
(
value
=
"${tencent.secretKey}"
)
private
String
tencentSecretKey
;
@Value
(
value
=
"${tencent.admin.userId}"
)
private
String
tencentAdminUserId
;
// 批量提交任务
public
void
submitBatchTasks
(
List
<
TencentMeetingVO
.
RecordFile
>
recordFiles
,
String
baseSavePath
)
{
List
<
Future
<?>>
futures
=
new
ArrayList
<>();
for
(
TencentMeetingVO
.
RecordFile
recordFile
:
recordFiles
)
{
// 为每个URL创建任务
FileProcessTask
task
=
new
FileProcessTask
(
recordFile
.
getRecordFileId
(),
recordFile
.
getMeetingId
(),
recordFile
.
getSubMeetingId
(),
"/save/"
,
Collections
.
emptyMap
(),
tencentAppId
,
tencentSdkId
,
tencentSecretId
,
tencentSecretKey
,
tencentAdminUserId
);
// 提交任务到线程池
Future
<?>
future
=
fileProcessExecutor
.
submit
(()
->
{
task
.
process
();
callbackHandler
.
onComplete
(
task
);
// 回调处理
});
futures
.
add
(
future
);
}
// 可以添加一个监控线程来检查所有任务完成情况
monitorTaskCompletion
(
futures
);
}
private
void
monitorTaskCompletion
(
List
<
Future
<?>>
futures
)
{
new
Thread
(()
->
{
for
(
Future
<?>
future
:
futures
)
{
try
{
future
.
get
();
// 阻塞直到任务完成
}
catch
(
InterruptedException
|
ExecutionException
e
)
{
log
.
error
(
"任务执行异常"
,
e
);
}
}
callbackHandler
.
onAllComplete
();
// 所有任务完成回调
}).
start
();
}
}
\ No newline at end of file
src/main/java/com/cmeeting/service/TecentMeetingService.java
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
service
;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
com.cmeeting.pojo.TencentMeetingUser
;
import
com.cmeeting.vo.TencentMeetingVO
;
import
java.util.List
;
public
interface
TecentMeetingService
{
public
interface
TecentMeetingService
extends
IService
<
TencentMeetingUser
>
{
void
batchInsert
(
List
<
TencentMeetingUser
>
users
);
void
doUsers
();
List
<
TencentMeetingVO
.
RecordFile
>
getMeetingFiles
(
TencentMeetingVO
.
TencentMeetingRequest
requestVO
);
}
src/main/java/com/cmeeting/service/WeComService.java
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
service
;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
com.cmeeting.pojo.WeComUser
;
import
java.util.List
;
public
interface
WeComService
{
public
interface
WeComService
extends
IService
<
WeComUser
>
{
void
batchInsert
(
List
<
WeComUser
>
users
);
void
doUsers
()
throws
Exception
;
String
getToken
();
}
src/main/java/com/cmeeting/service/impl/TecentMeetingServiceImpl.java
浏览文件 @
8a23a94c
差异被折叠。
点击展开。
src/main/java/com/cmeeting/service/impl/WeComServiceImpl.java
浏览文件 @
8a23a94c
package
com
.
cmeeting
.
service
.
impl
;
import
com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
;
import
com.cmeeting.mapper.primary.WeComUserMapper
;
import
com.cmeeting.pojo.WeComUser
;
import
com.cmeeting.service.WeComService
;
import
com.google.gson.Gson
;
import
com.google.gson.JsonArray
;
import
com.google.gson.JsonObject
;
import
okhttp3.OkHttpClient
;
import
okhttp3.Request
;
import
okhttp3.Response
;
import
org.json.JSONObject
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.Resource
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.InputStreamReader
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.util.Collection
;
import
java.util.List
;
import
static
com
.
cmeeting
.
WeComAndTencentMeeting
.
convertJsonToWeComUsers
;
import
static
com
.
cmeeting
.
WeComAndTencentMeeting
.
markDuplicateNames
;
@Service
public
class
WeComServiceImpl
implements
WeComService
{
@Autowired
public
class
WeComServiceImpl
extends
ServiceImpl
<
WeComUserMapper
,
WeComUser
>
implements
WeComService
{
private
static
final
String
BASE_URL
=
"https://qyapi.weixin.qq.com/cgi-bin/user/simplelist"
;
private
static
final
String
ACCESS_TOKEN
=
"z8g0fkzlVBMKrwi67CRwE7gcnGqjOMrTK6LDih06vs1ixpV9BkrVWQJkoF3HFf5a2UrMvTKEBewOo7wPGvVxNjV_ZGOp_dUxTfnCG6kRUK9LKXPlhskGOI3mcI3ZZpGqpPcgjCsE877hh1BYTB_i3t5X_q1tZfv0K636o3a0HyAMf4GlzviCd1vQHfkGf3p15FjnATwDV8ddhMZo8U-_uA"
;
private
static
final
String
CORP_ID
=
"wx34544d057db97ffd"
;
private
static
final
String
CORP_SECRET
=
"7YLePWG7rJqkQFnAB4FeylqAXpmu7q5qv_NOeSGNbm0"
;
@Resource
private
WeComUserMapper
weComUserMapper
;
@Override
public
void
batchInsert
(
List
<
WeComUser
>
users
)
{
weComUserMapper
.
batchInsert
(
users
);
}
@Override
public
void
doUsers
()
throws
Exception
{
// 示例:获取部门ID为6的用户列表
int
departmentId
=
6
;
JsonObject
departmentList
=
getDepartmentList
();
JsonObject
result
=
getUserListByDepartment
(
departmentId
);
// 处理返回结果
if
(
result
.
get
(
"errcode"
).
getAsInt
()
==
0
)
{
System
.
out
.
println
(
"企微获取用户列表成功:"
);
JsonArray
userList
=
result
.
getAsJsonArray
(
"userlist"
);
for
(
int
i
=
0
;
i
<
userList
.
size
();
i
++)
{
JsonObject
user
=
userList
.
get
(
i
).
getAsJsonObject
();
System
.
out
.
println
(
"姓名: "
+
user
.
get
(
"name"
).
getAsString
()
+
", UserID: "
+
user
.
get
(
"userid"
).
getAsString
());
}
// 2. 转换为WeComUser集合
List
<
WeComUser
>
users
=
convertJsonToWeComUsers
(
result
);
// 3. 检查重名并设置标志
markDuplicateNames
(
users
);
// 4. 批量插入数据库
batchInsert
(
users
);
}
}
@Override
public
String
getToken
()
{
//获取token
// String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww1fd8778458e9f1e8&corpsecret=uFRq9Xi8-dVY90LydXYBhjc91JnnfkPUR6lHDdeJ_fo";
String
url
=
"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="
+
CORP_ID
+
"&corpsecret="
+
CORP_SECRET
;
String
accessToken
=
""
;
OkHttpClient
client
=
new
OkHttpClient
();
Request
request
=
new
Request
.
Builder
()
.
url
(
url
)
.
get
()
.
build
();
try
(
Response
response
=
client
.
newCall
(
request
).
execute
())
{
if
(
response
.
isSuccessful
())
{
String
responseBody
=
response
.
body
().
string
();
JSONObject
jsonResponse
=
new
JSONObject
(
responseBody
);
accessToken
=
jsonResponse
.
getString
(
"access_token"
);
}
else
{
throw
new
RuntimeException
(
"Failed to fetch token. HTTP Code: "
+
response
.
code
());
}
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
return
accessToken
;
}
/**
* 获取企微部门列表
*
* @return 包含用户列表的JsonObject
* @throws Exception
*/
public
JsonObject
getDepartmentList
()
throws
Exception
{
String
urlStr
=
"https://qyapi.weixin.qq.com/cgi-bin/department/simplelist?access_token="
+
getToken
();
URL
url
=
new
URL
(
urlStr
);
HttpURLConnection
conn
=
(
HttpURLConnection
)
url
.
openConnection
();
conn
.
setRequestMethod
(
"GET"
);
int
responseCode
=
conn
.
getResponseCode
();
if
(
responseCode
==
HttpURLConnection
.
HTTP_OK
)
{
BufferedReader
in
=
new
BufferedReader
(
new
InputStreamReader
(
conn
.
getInputStream
()));
String
inputLine
;
StringBuilder
response
=
new
StringBuilder
();
while
((
inputLine
=
in
.
readLine
())
!=
null
)
{
response
.
append
(
inputLine
);
}
in
.
close
();
// 使用Gson解析JSON响应
Gson
gson
=
new
Gson
();
return
gson
.
fromJson
(
response
.
toString
(),
JsonObject
.
class
);
}
else
{
throw
new
RuntimeException
(
"HTTP GET请求失败,错误码: "
+
responseCode
);
}
}
/**
* 根据企业微信部门ID获取用户列表
*
* @param departmentId 部门ID
* @return 包含用户列表的JsonObject
* @throws Exception
*/
public
JsonObject
getUserListByDepartment
(
int
departmentId
)
throws
Exception
{
String
urlStr
=
BASE_URL
+
"?access_token="
+
ACCESS_TOKEN
+
"&department_id="
+
departmentId
;
URL
url
=
new
URL
(
urlStr
);
HttpURLConnection
conn
=
(
HttpURLConnection
)
url
.
openConnection
();
conn
.
setRequestMethod
(
"GET"
);
int
responseCode
=
conn
.
getResponseCode
();
if
(
responseCode
==
HttpURLConnection
.
HTTP_OK
)
{
BufferedReader
in
=
new
BufferedReader
(
new
InputStreamReader
(
conn
.
getInputStream
()));
String
inputLine
;
StringBuilder
response
=
new
StringBuilder
();
while
((
inputLine
=
in
.
readLine
())
!=
null
)
{
response
.
append
(
inputLine
);
}
in
.
close
();
// 使用Gson解析JSON响应
Gson
gson
=
new
Gson
();
return
gson
.
fromJson
(
response
.
toString
(),
JsonObject
.
class
);
}
else
{
throw
new
RuntimeException
(
"HTTP GET请求失败,错误码: "
+
responseCode
);
}
}
}
src/main/java/com/cmeeting/vo/TencentMeetingVO.java
0 → 100644
浏览文件 @
8a23a94c
// vo/LoginVO.java
package
com
.
cmeeting
.
vo
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
@Data
@Builder
public
class
TencentMeetingVO
{
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public
static
class
TencentMeetingRequest
{
private
Long
startTime
;
//开始结束时间戳
private
Long
endTime
;
private
Integer
pageSize
;
private
Integer
page
;
private
String
operatorId
;
private
Integer
operatorIdType
;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public
static
class
RecordFile
{
private
String
meetingId
;
//如果是周期会议,这个id表示主会议
private
String
subMeetingId
;
//如果是周期会议,这个id表示子会议
private
String
recordFileId
;
}
}
\ No newline at end of file
src/main/resources/application.properties
浏览文件 @
8a23a94c
...
...
@@ -29,3 +29,22 @@ mybatis.type-aliases-package=com.cmeeting.pojo\
mybatis.configuration.map-underscore-to-camel-case: true
logging.level.com.zaxxer.hikari
=
INFO
# tencent meeting
# local
tencent.appId
=
211153201
tencent.sdkId
=
28370276340
tencent.secretId
=
BKOMDZVbvh0iT7k6UHsSizAWBCOVDtT6
tencent.secretKey
=
3Y1j0mzNp7KChKFJGyaEnZHLobFoAQ8eLwfaMx8nLbtXAerO
#tencent.admin.userId=woaJARCQAAJU1EsO73Ww5rn8YHMW6iYA
tencent.admin.userId
=
woaJARCQAAhkyWGuf8n9InhZsxQstjjA
# prod
#tencent.appId=210468336
#tencent.sdkId=28790143843
#tencent.secretId=0ks7u8cgQ8DGVtlYZeRA9TxZCjvUT3oL
#tencent.secretKey=gQU09rkJjiQfiGcUYdhiKq5Ol6LebXg4w7F7Ol0rwvvdv3Xy
#tencent.admin.userId=woaJARCQAAftcvU6GGoOn66rdSZ4IrOA
email.sender
=
cmeeting_assistant@cimc.com
email.sender.pwd
=
scyou@xih45g6@xih4
email.smtp.host
=
smtp.office365.com
src/main/resources/mapper/primary/TecentMeetingMapper.xml
浏览文件 @
8a23a94c
...
...
@@ -14,6 +14,8 @@
#{user.isrepeatName}
)
</foreach>
ON DUPLICATE KEY UPDATE
user_name = VALUES(user_name)
</insert>
<select
id=
"getAlluser"
resultType=
"com.cmeeting.pojo.TencentMeetingUser"
>
...
...
src/main/resources/mapper/primary/WeComUserMapper.xml
浏览文件 @
8a23a94c
...
...
@@ -13,15 +13,18 @@
</insert>
<insert
id=
"batchInsert"
parameterType=
"list"
>
INSERT INTO user_wecom ( user_name, user_id, is
epeat_name
)
INSERT INTO user_wecom ( user_name, user_id, is
repeat_name,email
)
VALUES
<foreach
collection=
"list"
item=
"user"
separator=
","
>
(
#{user.userName},
#{user.userId},
#{user.isRepeatName}
#{user.isRepeatName},
#{user.email}
)
</foreach>
ON DUPLICATE KEY UPDATE
user_name = VALUES(user_name)
</insert>
<select
id=
"getAlluser"
resultType=
"com.cmeeting.pojo.WeComUser"
>
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论