提交 8a23a94c 作者: duanxincheng

纪要生成服务优化

父级 0a34c27f
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
<maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mybatis-plus.version>3.3.0</mybatis-plus.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
...@@ -246,11 +247,11 @@ ...@@ -246,11 +247,11 @@
</dependency> </dependency>
<dependency> <!-- <dependency>-->
<groupId>org.mybatis.spring.boot</groupId> <!-- <groupId>org.mybatis.spring.boot</groupId>-->
<artifactId>mybatis-spring-boot-starter</artifactId> <!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<version>2.3.0</version> <!-- 请使用最新版本 --> <!-- <version>2.3.0</version> &lt;!&ndash; 请使用最新版本 &ndash;&gt;-->
</dependency> <!-- </dependency>-->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
...@@ -293,10 +294,11 @@ ...@@ -293,10 +294,11 @@
</dependency> </dependency>
<!-- Word 文档操作 --> <!-- Word 文档操作 -->
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>com.deepoove</groupId>
<artifactId>poi-ooxml</artifactId> <artifactId>poi-tl</artifactId>
<version>5.2.3</version> <version>1.12.2</version>
</dependency> </dependency>
...@@ -305,6 +307,12 @@ ...@@ -305,6 +307,12 @@
<artifactId>commonmark</artifactId> <artifactId>commonmark</artifactId>
<version>0.17.1</version> <!-- 可升级至最新稳定版 --> <version>0.17.1</version> <!-- 可升级至最新稳定版 -->
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
</dependencies> </dependencies>
......
...@@ -431,17 +431,17 @@ public class TencentMeetingCallbackController { ...@@ -431,17 +431,17 @@ public class TencentMeetingCallbackController {
Thread.sleep(10000); Thread.sleep(10000);
EmailSender emailSender = new EmailSender(); EmailSender emailSender = new EmailSender();
//response.getRawBody() //response.getRawBody()
boolean mailFlag = emailSender.sendEmailWithAttachment(emailAddress, // boolean mailFlag = emailSender.sendEmailWithAttachment(emailAddress,
targetPath, // targetPath,
"重要文件", // "重要文件",
"您好:\n" + // "您好:\n" +
"\n" + // "\n" +
" 附件为您本次会议的会议纪要,烦请下载查看,如需对会议纪要结果进行修改或查看历史会议,可点击下方链接。"); // " 附件为您本次会议的会议纪要,烦请下载查看,如需对会议纪要结果进行修改或查看历史会议,可点击下方链接。");
if (mailFlag) { // if (mailFlag) {
logger.info("邮件发送成功"); // logger.info("邮件发送成功");
} else { // } else {
logger.error("邮件发送失败"); // logger.error("邮件发送失败");
} // }
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("下载文件时出错: {}", e.getMessage()); logger.error("下载文件时出错: {}", e.getMessage());
......
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
...@@ -2,6 +2,7 @@ package com.cmeeting.controller; ...@@ -2,6 +2,7 @@ package com.cmeeting.controller;
import com.cmeeting.pojo.TencentMeetingUser; 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 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.GetMapping;
...@@ -13,32 +14,18 @@ import java.util.List; ...@@ -13,32 +14,18 @@ import java.util.List;
import static com.cmeeting.WeComAndTencentMeeting.*; import static com.cmeeting.WeComAndTencentMeeting.*;
@RestController @RestController
@RequestMapping("/tecent") @RequestMapping("/tencent")
public class TencentMeetingController { public class TencentMeetingController {
@Autowired @Autowired
private TecentMeetingService tecentMeetingService; private TecentMeetingService tecentMeetingService;
@GetMapping("/add") @GetMapping("/add")
public void addUsers() throws Exception { public void addUsers() {
dousers(); tecentMeetingService.doUsers();
} }
private void dousers() { @GetMapping("/getMeetingFiles")
/** public void getMeetingFiles(TencentMeetingVO.TencentMeetingRequest requestVO){
* 腾讯会议通过通讯录获取员工信息 tecentMeetingService.getMeetingFiles(requestVO);
*/
// 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);
} }
} }
...@@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping; ...@@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.json.JSONObject; import org.json.JSONObject;
import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.SecureRandom; import java.security.SecureRandom;
...@@ -33,9 +34,9 @@ import java.util.stream.Collectors; ...@@ -33,9 +34,9 @@ import java.util.stream.Collectors;
@RestController @RestController
@RequestMapping @RequestMapping
public class UnifiedController { public class UnifiedController {
@Autowired @Resource
private WeComUserMapper weComUserMapper; private WeComUserMapper weComUserMapper;
@Autowired @Resource
private TecentMeetingMapper tecentMeetingMapper; private TecentMeetingMapper tecentMeetingMapper;
@Autowired @Autowired
private UserIdMapper userIdMapper; private UserIdMapper userIdMapper;
...@@ -78,7 +79,7 @@ public class UnifiedController { ...@@ -78,7 +79,7 @@ public class UnifiedController {
} }
@GetMapping("/insertTid") @GetMapping("/insertTid")
public void insertTid() { public void insertTid(String corpid, String corpsecret) {
List<UserId> users = userIdMapper.getUsers(); List<UserId> users = userIdMapper.getUsers();
List<UserId> usersWithNullTid = new ArrayList<>(); List<UserId> usersWithNullTid = new ArrayList<>();
for (UserId user : users) { for (UserId user : users) {
...@@ -88,7 +89,7 @@ public class UnifiedController { ...@@ -88,7 +89,7 @@ public class UnifiedController {
} }
for (UserId user : usersWithNullTid) { for (UserId user : usersWithNullTid) {
//获取企业微信token //获取企业微信token
String weComToken = getWeComToken(); String weComToken = getWeComToken(corpid,corpsecret);
try { try {
// 3.1 创建会议(传入用户的 wid) // 3.1 创建会议(传入用户的 wid)
Map<String, String> meetingCodeAndMeetingid = createMeeting(user.getWid(), weComToken); Map<String, String> meetingCodeAndMeetingid = createMeeting(user.getWid(), weComToken);
...@@ -128,8 +129,8 @@ public class UnifiedController { ...@@ -128,8 +129,8 @@ public class UnifiedController {
//todo 待测试 //todo 待测试
@GetMapping("/sameNameInsertTid") @GetMapping("/sameNameInsertTid")
public void sameNameInsertTid() throws IOException { public void sameNameInsertTid(String corpid, String corpsecret) throws IOException {
String weComToken = getWeComToken(); String weComToken = getWeComToken(corpid,corpsecret);
List<WeComUser> sameNameUsers = weComUserMapper.getSameName(); List<WeComUser> sameNameUsers = weComUserMapper.getSameName();
List<UserId> userIds = new ArrayList<>(); List<UserId> userIds = new ArrayList<>();
for (WeComUser user : sameNameUsers) { for (WeComUser user : sameNameUsers) {
...@@ -269,9 +270,11 @@ public class UnifiedController { ...@@ -269,9 +270,11 @@ public class UnifiedController {
* *
* @return * @return
*/ */
public String getWeComToken() { @GetMapping("/getWeComToken")
public String getWeComToken(String corpid, String corpsecret) {
//获取token //获取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 = ""; String accessToken = "";
OkHttpClient client = new OkHttpClient(); OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() Request request = new Request.Builder()
......
...@@ -23,73 +23,13 @@ import static com.cmeeting.WeComAndTencentMeeting.*; ...@@ -23,73 +23,13 @@ import static com.cmeeting.WeComAndTencentMeeting.*;
@RestController @RestController
@RequestMapping("/wecom") @RequestMapping("/wecom")
public class WeComcontroller { 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 @Autowired
private WeComService weComService; private WeComService weComService;
@GetMapping("/add") @GetMapping("/add")
public void addUsers() throws Exception { public void addUsers() throws Exception {
dousers(); weComService.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);
}
} }
......
package com.cmeeting.email; 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.*;
import javax.mail.internet.*; import javax.mail.internet.*;
import java.io.File; import java.io.File;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class EmailSender { public class EmailSender {
// 发件人邮箱配置(建议从配置文件读取,此处写死示例) @Value("${email.sender}")
private static final String FROM_EMAIL = "binzhai321@163.com"; private String SENDER;
private static final String AUTH_CODE = "RXbRu3AdrCxX57ib"; // 授权码 @Value("${email.sender.pwd}")
private static final String SMTP_HOST = "smtp.163.com"; private String EMAIL_PWD;
private static final int SMTP_PORT = 465; @Value("${email.smtp.host}")
private String SMTP_HOST;
private static final Integer MAX_RETRY = 3;
/** /**
* 发送带附件的邮件 * @param toEmail 收件人
* @param toEmail 收件人邮箱 * @param subject 邮件主题
* @param filePath 附件本地路径(如 "C:/test.pdf") * @param attachmentPath 附件路径
* @param subject 邮件主题(可选,默认值) * @param recordFileId 转录文件ID
* @param text 邮件正文(可选,默认值) * @return
* @return true发送成功,false发送失败
*/ */
public static boolean sendEmailWithAttachment(String toEmail, String filePath, public boolean sendEmailWithAttachment(String toEmail, String subject, String attachmentPath, String recordFileId) {
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
Properties props = new Properties(); Properties props = new Properties();
props.put("mail.smtp.host", SMTP_HOST); props.put("mail.smtp.host", "smtp.office365.com");
props.put("mail.smtp.port", SMTP_PORT);
props.put("mail.smtp.auth", "true"); props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true");
try { // props.put("mail.smtp.port", "465");
// 4. 创建会话 // props.put("mail.smtp.ssl.enable", "true"); // 使用SSL
Session session = Session.getInstance(props, new Authenticator() {
@Override 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() { 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); MimeBodyPart messageBodyPart = new MimeBodyPart();
message.setFrom(new InternetAddress(FROM_EMAIL)); messageBodyPart.setText(body);
message.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
message.setSubject(mailSubject);
// 6. 添加正文和附件 // 创建多部分消息
Multipart multipart = new MimeMultipart(); Multipart multipart = new MimeMultipart();
// 文本正文 // 添加文本部分
MimeBodyPart textPart = new MimeBodyPart(); multipart.addBodyPart(messageBodyPart);
textPart.setText(mailText);
multipart.addBodyPart(textPart);
// 附件 // 添加附件部分
if (attachmentPath != null && !attachmentPath.isEmpty()) {
MimeBodyPart attachmentPart = new MimeBodyPart(); 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); multipart.addBodyPart(attachmentPart);
}
// 设置完整消息内容
message.setContent(multipart); message.setContent(multipart);
// 7. 发送邮件 // 发送邮件
Transport.send(message); Transport.send(message);
System.out.println("邮件发送成功至: " + toEmail); log.error("邮件已成功发送: recordFileId->{}", recordFileId);
return true; isSent = true;
} catch (Exception e) {
System.err.println("邮件发送失败: " + e.getMessage());
return false;
} }
} catch (MessagingException e) {
//todo 邮件失败记录
// 异常处理
retryCount.getAndIncrement();
if (retryCount.intValue() > MAX_RETRY) {
log.error("邮件发送达到最大重试次数: recordFileId->{}", recordFileId);
throw new RuntimeException(e);
} }
// 指数退避
/** try {
* 简化版调用(仅需收件人和附件路径) Thread.sleep((long) Math.pow(2, retryCount.intValue()) * 1000);
*/ } catch (InterruptedException ie) {
public static boolean sendEmailWithAttachment(String toEmail, String filePath) { Thread.currentThread().interrupt();
return sendEmailWithAttachment(toEmail, filePath, null, null); throw new RuntimeException("邮件发送重试失败", ie);
}
return false;
}
return true;
} }
} }
\ No newline at end of file
package com.cmeeting.mapper.primary; package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.pojo.TencentMeetingUser; import com.cmeeting.pojo.TencentMeetingUser;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@Mapper public interface TecentMeetingMapper extends BaseMapper<TencentMeetingUser> {
public interface TecentMeetingMapper {
void batchInsertUsers(@Param("userList") List<TencentMeetingUser> userList); void batchInsertUsers(@Param("userList") List<TencentMeetingUser> userList);
List<TencentMeetingUser> getAlluser(); List<TencentMeetingUser> getAlluser();
} }
package com.cmeeting.mapper.primary; package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.pojo.WeComUser; import com.cmeeting.pojo.WeComUser;
import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;
@Mapper
public interface WeComUserMapper { public interface WeComUserMapper extends BaseMapper<WeComUser> {
List<WeComUser> getSameName() ; List<WeComUser> getSameName() ;
WeComUser selectById(Integer id); WeComUser selectById(Integer id);
void insert(WeComUser user); int insert(WeComUser user);
List<WeComUser> getAlluser(); List<WeComUser> getAlluser();
/** /**
* 批量插入用户 * 批量插入用户
......
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;
}
package com.cmeeting.pojo; package com.cmeeting.pojo;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class WeComUser { public class WeComUser extends Model implements Serializable {
private Integer id; private Integer id;
private String userName; private String userName;
private String userId; private String userId;
private String isRepeatName; private String isRepeatName;
private String email;
public WeComUser() { public WeComUser() {
} }
public WeComUser(Integer id, String userName, String userId, String isRepeatName) { public WeComUser(Integer id, String userName, String userId, String isRepeatName) {
...@@ -24,6 +29,7 @@ public class WeComUser { ...@@ -24,6 +29,7 @@ public class WeComUser {
", userName='" + userName + '\'' + ", userName='" + userName + '\'' +
", userId='" + userId + '\'' + ", userId='" + userId + '\'' +
", isRepeatName='" + isRepeatName + '\'' + ", isRepeatName='" + isRepeatName + '\'' +
", email='" + email + '\'' +
'}'; '}';
} }
} }
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
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
package com.cmeeting.service; package com.cmeeting.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmeeting.pojo.TencentMeetingUser; import com.cmeeting.pojo.TencentMeetingUser;
import com.cmeeting.vo.TencentMeetingVO;
import java.util.List; import java.util.List;
public interface TecentMeetingService { public interface TecentMeetingService extends IService<TencentMeetingUser> {
void batchInsert(List<TencentMeetingUser> users); void batchInsert(List<TencentMeetingUser> users);
void doUsers();
List<TencentMeetingVO.RecordFile> getMeetingFiles(TencentMeetingVO.TencentMeetingRequest requestVO);
} }
package com.cmeeting.service; package com.cmeeting.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmeeting.pojo.WeComUser; import com.cmeeting.pojo.WeComUser;
import java.util.List; import java.util.List;
public interface WeComService { public interface WeComService extends IService<WeComUser> {
void batchInsert(List<WeComUser> users); void batchInsert(List<WeComUser> users);
void doUsers() throws Exception;
String getToken();
} }
package com.cmeeting.service.impl; package com.cmeeting.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmeeting.mapper.primary.WeComUserMapper; import com.cmeeting.mapper.primary.WeComUserMapper;
import com.cmeeting.pojo.WeComUser; import com.cmeeting.pojo.WeComUser;
import com.cmeeting.service.WeComService; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; 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 java.util.List;
import static com.cmeeting.WeComAndTencentMeeting.convertJsonToWeComUsers;
import static com.cmeeting.WeComAndTencentMeeting.markDuplicateNames;
@Service @Service
public class WeComServiceImpl implements WeComService { public class WeComServiceImpl extends ServiceImpl<WeComUserMapper, WeComUser> implements WeComService {
@Autowired
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; private WeComUserMapper weComUserMapper;
@Override @Override
public void batchInsert(List<WeComUser> users) { public void batchInsert(List<WeComUser> users) {
weComUserMapper.batchInsert(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);
}
}
} }
// 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
...@@ -29,3 +29,22 @@ mybatis.type-aliases-package=com.cmeeting.pojo\ ...@@ -29,3 +29,22 @@ mybatis.type-aliases-package=com.cmeeting.pojo\
mybatis.configuration.map-underscore-to-camel-case: true mybatis.configuration.map-underscore-to-camel-case: true
logging.level.com.zaxxer.hikari=INFO 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
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#{user.isrepeatName} #{user.isrepeatName}
) )
</foreach> </foreach>
ON DUPLICATE KEY UPDATE
user_name = VALUES(user_name)
</insert> </insert>
<select id="getAlluser" resultType="com.cmeeting.pojo.TencentMeetingUser"> <select id="getAlluser" resultType="com.cmeeting.pojo.TencentMeetingUser">
......
...@@ -13,15 +13,18 @@ ...@@ -13,15 +13,18 @@
</insert> </insert>
<insert id="batchInsert" parameterType="list"> <insert id="batchInsert" parameterType="list">
INSERT INTO user_wecom ( user_name, user_id, isepeat_name) INSERT INTO user_wecom ( user_name, user_id, isrepeat_name,email)
VALUES VALUES
<foreach collection="list" item="user" separator=","> <foreach collection="list" item="user" separator=",">
( (
#{user.userName}, #{user.userName},
#{user.userId}, #{user.userId},
#{user.isRepeatName} #{user.isRepeatName},
#{user.email}
) )
</foreach> </foreach>
ON DUPLICATE KEY UPDATE
user_name = VALUES(user_name)
</insert> </insert>
<select id="getAlluser" resultType="com.cmeeting.pojo.WeComUser"> <select id="getAlluser" resultType="com.cmeeting.pojo.WeComUser">
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论