提交 a8de9001 作者: 洪东保

Merge branch 'old'

# Conflicts:
#	pom.xml
#	src/main/java/com/cmeeting/TencentMeetingCallbackApplication.java
#	src/main/java/com/cmeeting/ad/controller/UserController.java
#	src/main/java/com/cmeeting/ad/service/UserService.java
#	src/main/java/com/cmeeting/ad/service/impl/UserServiceImpl.java
#	src/main/java/com/cmeeting/constant/RecordTemplateConstant.java
#	src/main/java/com/cmeeting/controller/MeetingInfoController.java
#	src/main/java/com/cmeeting/email/EmailSender.java
#	src/main/java/com/cmeeting/job/CmeetingJob.java
#	src/main/java/com/cmeeting/job/EmailPushTask.java
#	src/main/java/com/cmeeting/job/FileProcessTask.java
#	src/main/java/com/cmeeting/service/FileProcessProducer.java
#	src/main/java/com/cmeeting/service/MeetingInfoService.java
#	src/main/java/com/cmeeting/service/impl/MeetingInfoServiceImpl.java
#	src/main/java/com/cmeeting/service/impl/TencentMeetingServiceImpl.java
#	src/main/resources/application.yml
#	src/main/resources/mapper/primary/MeetingInfoMapper.xml
...@@ -44,6 +44,13 @@ ...@@ -44,6 +44,13 @@
<artifactId>jackson-dataformat-xml</artifactId> <artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.0</version> <version>2.13.0</version>
</dependency> </dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-dependencies</artifactId>-->
<!-- <version>Finchley.M8</version>-->
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
<!-- </dependency>-->
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<dependencies> <dependencies>
...@@ -352,6 +359,12 @@ ...@@ -352,6 +359,12 @@
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<!-- yml参数加密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
package com.cmeeting; package com.cmeeting;
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
...@@ -9,6 +10,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; ...@@ -9,6 +10,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication @SpringBootApplication
@EnableEncryptableProperties
@MapperScan("com.cmeeting.mapper.primary") @MapperScan("com.cmeeting.mapper.primary")
@EnableScheduling @EnableScheduling
public class TencentMeetingCallbackApplication { public class TencentMeetingCallbackApplication {
......
...@@ -33,15 +33,6 @@ public class UserController { ...@@ -33,15 +33,6 @@ public class UserController {
return userService.login(vo.getAgentId(), vo.getData(), ipAddr); return userService.login(vo.getAgentId(), vo.getData(), ipAddr);
} }
/**
* 超管登录
* @param vo
* @return
*/
@PostMapping(value = "/auth")
public R auth(@Validated @RequestBody UserVo.Auth vo) {
return R.ok(userService.auth(vo, RecordTemplateConstant.TEMPLATE_TYPE_SYSTEM));
}
/** /**
* 角色扮演进入系统接口 * 角色扮演进入系统接口
......
...@@ -21,7 +21,7 @@ public class SysDept implements Serializable { ...@@ -21,7 +21,7 @@ public class SysDept implements Serializable {
/** /**
* 部门id * 部门id
*/ */
private String id; private String deptId;
/** /**
* 部门名称 * 部门名称
*/ */
......
...@@ -13,7 +13,7 @@ import java.util.List; ...@@ -13,7 +13,7 @@ import java.util.List;
public interface UserService { public interface UserService {
R login(String agentId, String data, String ip); R login(String agentId, String data, String ip);
String auth(UserVo.Auth vo, String role); String auth(String userId, String nick, String role);
List<String> getRoleIdByUserId(String tenantId, String userId, String path); List<String> getRoleIdByUserId(String tenantId, String userId, String path);
......
...@@ -89,9 +89,7 @@ public class UserServiceImpl implements UserService { ...@@ -89,9 +89,7 @@ public class UserServiceImpl implements UserService {
} }
@Override @Override
public String auth(UserVo.Auth vo, String role) { public String auth(String userId, String nick, String role) {
String userId = vo.getId();
String nick = vo.getNick();
HashMap<String, String> stringStringHashMap = new HashMap<>(); HashMap<String, String> stringStringHashMap = new HashMap<>();
SysTenant sysTenant = iTenantService.getById(permissionTenantId); SysTenant sysTenant = iTenantService.getById(permissionTenantId);
stringStringHashMap.put("userId", userId); stringStringHashMap.put("userId", userId);
...@@ -226,9 +224,7 @@ public class UserServiceImpl implements UserService { ...@@ -226,9 +224,7 @@ public class UserServiceImpl implements UserService {
} }
} }
UserVo.Auth authParams = UserVo.Auth.builder().id(userId).nick(robotSecurityUser.getNickName()).build(); return auth(userId, robotSecurityUser.getNickName(), RecordTemplateConstant.TEMPLATE_TYPE_CUSTOM);
String token = auth(authParams, RecordTemplateConstant.TEMPLATE_TYPE_CUSTOM);
return token;
} }
@Override @Override
...@@ -263,19 +259,19 @@ public class UserServiceImpl implements UserService { ...@@ -263,19 +259,19 @@ public class UserServiceImpl implements UserService {
private R loginByAD(ApplicationUserVO.Login login) { private R loginByAD(ApplicationUserVO.Login login) {
// AD验证 // AD验证
String username = login.getUsername().trim(); String username = login.getUsername().trim();
if (!isDev) {
boolean auth = iLdapService.authenticate(username, login.getPassword().trim()); boolean auth = iLdapService.authenticate(username, login.getPassword().trim());
if (!auth) { if (auth) {
return R.error("账号/密码错误!"); // if(true){
}
}
SysUserSync sysUserSync = sysUserSysMapper.selectOne(new LambdaQueryWrapper<SysUserSync>() SysUserSync sysUserSync = sysUserSysMapper.selectOne(new LambdaQueryWrapper<SysUserSync>()
.eq(SysUserSync::getTenantId, permissionTenantId) .eq(SysUserSync::getTenantId, permissionTenantId)
.eq(SysUserSync::getUserId, username)); .eq(SysUserSync::getUserId, username));
if (StringUtils.isEmpty(adminWhiteUsers) || !Arrays.asList(adminWhiteUsers.split(",")).contains(username)) { if(StringUtils.isEmpty(adminWhiteUsers) || !Arrays.asList(adminWhiteUsers.split(",")).contains(username)){
throw new RobotBaseException("尊敬的用户 [" + sysUserSync.getName() + "],您的账户" + username + "未被授权访问此系统,请联系系统管理员。"); throw new RobotBaseException("尊敬的用户 ["+sysUserSync.getName()+"],您的账户"+username+"未被授权访问此系统,请联系系统管理员。");
}
return R.ok(auth(sysUserSync.getUserId(), sysUserSync.getName(), RecordTemplateConstant.TEMPLATE_TYPE_SYSTEM));
} else {
return R.error("账号/密码错误!");
} }
return R.ok(sysUserSync);
} }
/** /**
......
...@@ -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(2); // 固定核心线程数,避免动态获取CPU核心数 executor.setCorePoolSize(4); // 固定核心线程数,避免动态获取CPU核心数
// 最大线程数 // 最大线程数
executor.setMaxPoolSize(2); executor.setMaxPoolSize(4);
// 队列容量 // 队列容量
executor.setQueueCapacity(1000); executor.setQueueCapacity(1000);
// 线程名前缀 // 线程名前缀
...@@ -39,4 +39,34 @@ public class ThreadPoolConfig { ...@@ -39,4 +39,34 @@ public class ThreadPoolConfig {
executor.initialize(); executor.initialize();
return executor; return executor;
} }
@Bean("emailProcessExecutor")
public ThreadPoolTaskExecutor emailProcessExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数 (CPU密集型任务建议核心数+1)
executor.setCorePoolSize(1); // 固定核心线程数,避免动态获取CPU核心数
// 最大线程数
executor.setMaxPoolSize(5);
// 队列容量
executor.setQueueCapacity(1000);
// 线程名前缀
executor.setThreadNamePrefix("email-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
package com.cmeeting.constant;
/**
* 会议处理状态枚举类
*/
public enum MeetingState {
/**
* 会议状态枚举类型
*/
// 新会议待处理
NEW(0, "新会议待处理"),
// 处理中
GENERATE_ERROR(1, "生成纪要失败"),
// 生成纪要成功
NOTE_GENERATED(2, "生成纪要成功"),
// 推送纪要成功
PUSH_SUCCESS(3, "推送纪要邮件成功"),
PUSH_ERROR(4, "推送纪要邮件失败"),
EMPTY(5, "转录文件为空")
;
// 状态码
private final int code;
// 状态描述
private final String description;
/**
* 枚举构造方法
* @param code 状态码
* @param description 状态描述
*/
private MeetingState(int code, String description) {
this.code = code;
this.description = description;
}
// 获取状态码
public int getCode() {
return code;
}
// 获取状态描述
public String getDescription() {
return description;
}
/**
* 根据状态码获取状态枚举值
* @param code 状态码
* @return 对应的枚举值,若不存在则返回null
*/
public static MeetingState fromCode(int code) {
for (MeetingState state : values()) {
if (state.code == code) {
return state;
}
}
return null;
}
/**
* 扩展方法示例:获取状态类型描述
* @return 返回状态类型描述
*/
public String getTypeDescription() {
return "会议处理状态";
}
}
\ No newline at end of file
...@@ -2,7 +2,6 @@ package com.cmeeting.controller; ...@@ -2,7 +2,6 @@ package com.cmeeting.controller;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cmeeting.ad.entity.RobotSecurityUser; import com.cmeeting.ad.entity.RobotSecurityUser;
import com.cmeeting.ad.util.SecurityUtil; import com.cmeeting.ad.util.SecurityUtil;
...@@ -14,34 +13,28 @@ import com.cmeeting.mapper.primary.UserIdMapper; ...@@ -14,34 +13,28 @@ import com.cmeeting.mapper.primary.UserIdMapper;
import com.cmeeting.pojo.MeetingInfo; import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.pojo.MeetingRecordTemplate; import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.service.MeetingInfoService; import com.cmeeting.service.MeetingInfoService;
import com.cmeeting.util.AESUtils;
import com.cmeeting.util.MinioUtils; import com.cmeeting.util.MinioUtils;
import com.cmeeting.util.R; import com.cmeeting.util.R;
import com.cmeeting.vo.EmailPush; import com.cmeeting.vo.EmailPush;
import com.cmeeting.vo.EmailStatisticsVo;
import com.cmeeting.vo.MeetingInfoVO; import com.cmeeting.vo.MeetingInfoVO;
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;
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource; import org.springframework.web.bind.annotation.*;
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.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream; import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
...@@ -67,6 +60,8 @@ public class MeetingInfoController { ...@@ -67,6 +60,8 @@ public class MeetingInfoController {
private String permissionApplicationId; private String permissionApplicationId;
@Value(value = "${userAdmin.file-download-path}") @Value(value = "${userAdmin.file-download-path}")
private String fileDownloadPath; private String fileDownloadPath;
@Value("${aec.key}")
public String aesKey;
@OperLog(location = "历史会议纪要详情页面", operation = "修改会议纪要") @OperLog(location = "历史会议纪要详情页面", operation = "修改会议纪要")
@PostMapping("/updateRecordXml") @PostMapping("/updateRecordXml")
...@@ -106,7 +101,10 @@ public class MeetingInfoController { ...@@ -106,7 +101,10 @@ public class MeetingInfoController {
try { try {
if (StringUtils.isNotEmpty(recordXml)) { if (StringUtils.isNotEmpty(recordXml)) {
//xml转json,用于前端的表单回显 //xml转json,用于前端的表单回显
String xml = minioUtils.getFileText(recordXml); InputStream inputStream = minioUtils.getFile(meetingInfo.getRecordXml());
// 解密
String xml = AESUtils.decrypt(IoUtil.read(inputStream, StandardCharsets.UTF_8), aesKey);
String json = convertXmlToJSON(xml); String json = convertXmlToJSON(xml);
vo.setRecordJson(json); vo.setRecordJson(json);
} }
...@@ -114,8 +112,11 @@ public class MeetingInfoController { ...@@ -114,8 +112,11 @@ public class MeetingInfoController {
e.printStackTrace(); e.printStackTrace();
} }
try { try {
if (StringUtils.isNotEmpty(recordContent)) { if(StringUtils.isNotEmpty(recordContent)){
vo.setRecordContent(minioUtils.getFileText(recordContent.replaceAll(fileDownloadPath, ""))); InputStream inputStream = minioUtils.getFile(meetingInfo.getRecordContent());
// 解密
String content = AESUtils.decrypt(IoUtil.read(inputStream, StandardCharsets.UTF_8), aesKey);
vo.setRecordContent(content);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
...@@ -131,7 +132,9 @@ public class MeetingInfoController { ...@@ -131,7 +132,9 @@ public class MeetingInfoController {
public void exportMeetingRecord(@RequestBody MeetingInfoVO vo, HttpServletResponse response) { public void exportMeetingRecord(@RequestBody MeetingInfoVO vo, HttpServletResponse response) {
try { try {
MeetingInfo meetingInfo = meetingInfoService.getById(vo.getId()); MeetingInfo meetingInfo = meetingInfoService.getById(vo.getId());
String content = minioUtils.getFileText(meetingInfo.getRecordContent()); InputStream inputStream = minioUtils.getFile(meetingInfo.getRecordContent());
// 解密
String content = AESUtils.decrypt(IoUtil.read(inputStream, StandardCharsets.UTF_8), aesKey);
String fileName = String.format(meetingInfo.getSubject() + "_转写原文_%s.docx", DateUtil.today()); String fileName = String.format(meetingInfo.getSubject() + "_转写原文_%s.docx", DateUtil.today());
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
...@@ -167,7 +170,10 @@ public class MeetingInfoController { ...@@ -167,7 +170,10 @@ public class MeetingInfoController {
public void downloadMeetingMinutes(@RequestBody MeetingInfoVO vo, HttpServletResponse response) { public void downloadMeetingMinutes(@RequestBody MeetingInfoVO vo, HttpServletResponse response) {
try { try {
MeetingInfo meetingInfo = meetingInfoService.getById(vo.getId()); MeetingInfo meetingInfo = meetingInfoService.getById(vo.getId());
String xml = minioUtils.getFileText(meetingInfo.getRecordXml()); InputStream inputStream = minioUtils.getFile(meetingInfo.getRecordXml());
// 解密
String xml = AESUtils.decrypt(IoUtil.read(inputStream, StandardCharsets.UTF_8), aesKey);
Map<String, Object> dataModel = convertXmlToMap(xml); Map<String, Object> dataModel = convertXmlToMap(xml);
//追加参会人员信息 //追加参会人员信息
Map<String, Object> participantsMap = new ConcurrentHashMap<>(); Map<String, Object> participantsMap = new ConcurrentHashMap<>();
...@@ -212,7 +218,9 @@ public class MeetingInfoController { ...@@ -212,7 +218,9 @@ public class MeetingInfoController {
public R exportMeetingMinutes(@RequestBody MeetingInfoVO vo, HttpServletResponse response) { public R exportMeetingMinutes(@RequestBody MeetingInfoVO vo, HttpServletResponse response) {
try { try {
MeetingInfo meetingInfo = meetingInfoService.getById(vo.getId()); MeetingInfo meetingInfo = meetingInfoService.getById(vo.getId());
String xml = minioUtils.getFileText(meetingInfo.getRecordXml()); InputStream inputStream = minioUtils.getFile(meetingInfo.getRecordXml());
// 解密
String xml = AESUtils.decrypt(IoUtil.read(inputStream, StandardCharsets.UTF_8), aesKey);
Map<String, Object> dataModel = convertXmlToMap(xml); Map<String, Object> dataModel = convertXmlToMap(xml);
//追加参会人员信息 //追加参会人员信息
Map<String, Object> participantsMap = new ConcurrentHashMap<>(); Map<String, Object> participantsMap = new ConcurrentHashMap<>();
...@@ -315,4 +323,11 @@ public class MeetingInfoController { ...@@ -315,4 +323,11 @@ public class MeetingInfoController {
} }
return json; return json;
} }
@PostMapping("/statistics")
public void statisticsEmail(@RequestBody EmailStatisticsVo vo, HttpServletResponse response) {
meetingInfoService.statisticsEmail(vo.getType(), vo.getStartTime(), vo.getEndTime(), response);
}
} }
...@@ -7,6 +7,7 @@ import com.azure.identity.ClientSecretCredentialBuilder; ...@@ -7,6 +7,7 @@ import com.azure.identity.ClientSecretCredentialBuilder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmeeting.ad.service.UserService; import com.cmeeting.ad.service.UserService;
import com.cmeeting.ad.vo.UserVo; import com.cmeeting.ad.vo.UserVo;
import com.cmeeting.exception.RobotBaseException;
import com.cmeeting.log.service.ProcessLogService; import com.cmeeting.log.service.ProcessLogService;
import com.cmeeting.pojo.MeetEmailTemplate; import com.cmeeting.pojo.MeetEmailTemplate;
import com.cmeeting.service.MeetEmailTemplateService; import com.cmeeting.service.MeetEmailTemplateService;
...@@ -49,8 +50,8 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -49,8 +50,8 @@ import java.util.concurrent.atomic.AtomicInteger;
public class EmailSender { public class EmailSender {
@Value("${email.sender}") @Value("${email.sender}")
private String SENDER; private String SENDER;
@Value("${email.sender-pwd}") // @Value("${email.sender-pwd}")
private String EMAIL_PWD; // private String EMAIL_PWD;
@Value("${email.push-switch}") @Value("${email.push-switch}")
private Boolean pushSwitch; private Boolean pushSwitch;
@Value("${email.environment}") @Value("${email.environment}")
...@@ -70,112 +71,6 @@ public class EmailSender { ...@@ -70,112 +71,6 @@ public class EmailSender {
@Resource @Resource
private RedisUtils redisUtils; private RedisUtils redisUtils;
/**
* @param toEmail 收件人
* @param subject 邮件主题
* @param attachmentPath 附件路径
* @param meetingId 会议id
* @return
*/
// public boolean sendEmailWithAttachment(String toEmail, String subject, String attachmentPath, String meetingId) {
// log.info("sendEmailWithAttachment start...");
// // 邮件服务器配置
// Properties props = new Properties();
// props.put("mail.smtp.host", SMTP_HOST);
// props.put("mail.smtp.auth", "true");
//
// props.put("mail.smtp.port", "587");
// props.put("mail.smtp.starttls.enable", "true"); // 使用TLS
// props.put("mail.smtp.starttls.required", "true");
// props.put("mail.debug", "true");
// props.put("mail.smtp.connectiontimeout", "5000"); // 连接超时时间(毫秒)
// props.put("mail.smtp.timeout", "5000"); // 读取超时时间(毫秒)
// props.remove("mail.smtp.user");
// props.remove("mail.user");
// props.remove("mail.smtp.ssl.enable");
//
// // 创建会话
// log.info("sender->{},email_pwd->{}",SENDER, EMAIL_PWD);
//
// System.getProperties().stringPropertyNames().stream()
// .filter(name -> name.startsWith("mail."))
// .forEach(name -> log.info(name + " = " + System.getProperty(name)));
//
// Session session = Session.getInstance(props); // 不传递 Authenticator
// String body = "您好:\n" +
// "\n" +
// " 附件为您本次会议的会议纪要,烦请下载查看";
//
// AtomicInteger retryCount = new AtomicInteger(0);
// boolean isSent = false;
// if(StringUtils.isEmpty(toEmail)){
// log.error("收件邮箱为空,推送失败");
// processLogService.log(meetingId,null,"收件邮箱为空,推送失败");
// return false;
// }
// log.info("准备开始邮件推送...");
// while (retryCount.intValue() < MAX_RETRY && !isSent){
// try {
// Transport transport = session.getTransport("smtp");
// // 创建邮件消息
// Message message = new MimeMessage(session);
// message.setFrom(new InternetAddress("cmeeting_assistant@cimc.com"));
// message.setRecipients(Message.RecipientType.TO,
// InternetAddress.parse(toEmail));
// message.setSubject(subject);
//
// // 创建消息体部分(正文)
// MimeBodyPart messageBodyPart = new MimeBodyPart();
// messageBodyPart.setText(body);
//
// // 创建多部分消息
// Multipart multipart = new MimeMultipart();
//
// // 添加文本部分
// multipart.addBodyPart(messageBodyPart);
//
// // 添加附件部分
// if (attachmentPath != null && !attachmentPath.isEmpty()) {
// MimeBodyPart attachmentPart = new MimeBodyPart();
// DataSource source = new FileDataSource(attachmentPath);
// attachmentPart.setDataHandler(new DataHandler(source));
// attachmentPart.setFileName(new File(attachmentPath).getName());
// multipart.addBodyPart(attachmentPart);
// }
//
// // 设置完整消息内容
// message.setContent(multipart);
//
// // 发送邮件
// log.info("Transport.send...");
//// Transport.send(message);
// transport.connect("smtp.office365.com", 587, "cmeeting_assistant@cimc.com", "scyou@xih45g6@xih4");
// transport.sendMessage(message, message.getAllRecipients());
// transport.close();
// log.error("邮件已成功发送: meetingId->{}", meetingId);
// isSent = true;
// }catch (Exception e) {
// retryCount.getAndIncrement();
// // 异常处理
// StringWriter sw = new StringWriter();
// PrintWriter pw = new PrintWriter(sw);
// e.printStackTrace(pw);
// processLogService.log(meetingId,null,"【邮件推送异常】:"+sw.toString());
// if (retryCount.intValue() > MAX_RETRY) {
// log.error("邮件发送达到最大重试次数: meetingId->{}", meetingId);
// throw new RuntimeException(e);
// }
// // 指数退避
// try {
// Thread.sleep((long) Math.pow(2, retryCount.intValue()) * 1000);
// } catch (InterruptedException ie) {
// Thread.currentThread().interrupt();
// throw new RuntimeException("邮件发送重试失败", ie);
// }
// }
// }
// return isSent;
// }
/** /**
* 发送邮件,带附件 * 发送邮件,带附件
...@@ -183,10 +78,7 @@ public class EmailSender { ...@@ -183,10 +78,7 @@ public class EmailSender {
* @return * @return
*/ */
public boolean sendEmailWithAttachment(EmailPush emailPushBuilder) { public boolean sendEmailWithAttachment(EmailPush emailPushBuilder) {
log.info("sendEmailWithAttachment start..."); log.info("sendEmailWithAttachment start, sender -> {}", SENDER);
// 创建会话
log.info("sender->{},email_pwd->{}",SENDER, EMAIL_PWD);
if(!pushSwitch){ if(!pushSwitch){
log.info("【邮箱推送】:应用未开启邮件推送功能"); log.info("【邮箱推送】:应用未开启邮件推送功能");
...@@ -255,8 +147,7 @@ public class EmailSender { ...@@ -255,8 +147,7 @@ public class EmailSender {
}else{ }else{
isSent = false; isSent = false;
processLogService.log(meetingId,subMeetingId,"【邮件推送异常】:邮件模板未设置"); processLogService.log(meetingId,subMeetingId,"【邮件推送异常】:邮件模板未设置");
retryCount.getAndIncrement(); throw new RobotBaseException("邮件模板未设置");
continue;
} }
message.body = body; message.body = body;
...@@ -291,7 +182,7 @@ public class EmailSender { ...@@ -291,7 +182,7 @@ public class EmailSender {
.buildRequest() .buildRequest()
.post(); .post();
log.error("邮件已成功发送: meetingId->{}", meetingId); log.info("邮件已成功发送: meetingId->{}, subMeetingId->{}", meetingId, subMeetingId);
isSent = true; isSent = true;
}catch (Exception e) { }catch (Exception e) {
retryCount.getAndIncrement(); retryCount.getAndIncrement();
......
package com.cmeeting.job; package com.cmeeting.job;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmeeting.constant.MeetingState;
import com.cmeeting.dto.UserDTO; import com.cmeeting.dto.UserDTO;
import com.cmeeting.mapper.primary.UserIdMapper; import com.cmeeting.mapper.primary.UserIdMapper;
import com.cmeeting.pojo.MeetingInfo; import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.pojo.UserId; import com.cmeeting.pojo.UserId;
import com.cmeeting.pojo.WeComUser; import com.cmeeting.pojo.WeComUser;
import com.cmeeting.service.*; import com.cmeeting.service.*;
import com.cmeeting.util.RedisUtils;
import com.cmeeting.vo.TencentMeetingVO; import com.cmeeting.vo.TencentMeetingVO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -42,6 +44,8 @@ public class CmeetingJob { ...@@ -42,6 +44,8 @@ public class CmeetingJob {
private UserIdMapper userIdMapper; private UserIdMapper userIdMapper;
@Value("${isDev}") @Value("${isDev}")
private Boolean isDev; private Boolean isDev;
@Resource
private RedisUtils redisUtils;
// @PostConstruct // @PostConstruct
public void weComUserInit() { public void weComUserInit() {
...@@ -118,7 +122,10 @@ public class CmeetingJob { ...@@ -118,7 +122,10 @@ public class CmeetingJob {
if (isDev) { if (isDev) {
return; return;
} }
log.info("-------生成纪要定时任务开始-------"); if (redisUtils.setnx("Scheduled-All", "Scheduled-All", 18 * 60)){
return;
}
try {
//查出企微id和腾会id的关联关系 //查出企微id和腾会id的关联关系
List<UserId> userIdRelations = userIdMapper.selectList(null); List<UserId> userIdRelations = userIdMapper.selectList(null);
Map<String, String> widTidRelations = userIdRelations.stream().collect(Collectors.toMap(UserId::getWid, UserId::getTid)); Map<String, String> widTidRelations = userIdRelations.stream().collect(Collectors.toMap(UserId::getWid, UserId::getTid));
...@@ -133,7 +140,7 @@ public class CmeetingJob { ...@@ -133,7 +140,7 @@ public class CmeetingJob {
log.info("无生成纪要权限的人员"); log.info("无生成纪要权限的人员");
return; return;
} else { } else {
log.info("生成纪要权限人员:->{}", accessUserIds.stream().map(UserDTO::getWid).collect(Collectors.joining(","))); log.info("生成纪要权限人员:->{}", accessUserIds.toString());
} }
List<TencentMeetingVO.RecordFile> meetingFiles = tencentMeetingService.getMeetingFiles(accessUserIds, weComUserMap); List<TencentMeetingVO.RecordFile> meetingFiles = tencentMeetingService.getMeetingFiles(accessUserIds, weComUserMap);
...@@ -147,28 +154,38 @@ public class CmeetingJob { ...@@ -147,28 +154,38 @@ public class CmeetingJob {
// 提交处理任务 // 提交处理任务
producer.submitBatchTasks(meetingFiles, authorizedUsers, tidWidRelations, Boolean.FALSE); producer.submitBatchTasks(meetingFiles, authorizedUsers, tidWidRelations, Boolean.FALSE);
} catch (Exception e){
e.printStackTrace();
} finally {
redisUtils.del("Scheduled-All");
}
} }
/** /**
* 定时扫描早于一小时之前的,所有未重试过的会议,重新生成纪要 * 定时扫描早于一小时之前的,所有未重试过的会议,重新生成纪要
*/ */
@Scheduled(fixedRate = 30 * 60 * 1000, initialDelay = 10 * 60 * 1000) @Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 10 * 60 * 1000)
public void meetingMinutesRetry() { public void meetingMinutesRetry() {
if (isDev) { if (isDev) {
return; return;
} }
if (redisUtils.setnx("Scheduled-retry", "Scheduled-retry", 28 * 60)){
return;
}
try { try {
log.info("-------生成纪要重试定时任务开始-------"); log.info("-------生成纪要重试定时任务开始-------");
log.info("当前时间: " + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)); log.info("当前时间: " + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
//查出所有早于一小时前的,生成失败且未重试过的会议 //查出所有早于一小时前的,生成失败且未重试过的会议
// 不能用status筛选,因为可能线程执行一般服务终止,status状态没变
List<MeetingInfo> meetingInfoList = List<MeetingInfo> meetingInfoList =
meetingInfoService.list(new LambdaQueryWrapper<MeetingInfo>() meetingInfoService.list(new LambdaQueryWrapper<MeetingInfo>()
.eq(MeetingInfo::getIsGenerated, Boolean.FALSE) .eq(MeetingInfo::getEmailPushAccess,Boolean.TRUE)
.eq(MeetingInfo::getEmailGenerateAccess, true) .eq(MeetingInfo::getEmailGenerateAccess,Boolean.TRUE)
.eq(MeetingInfo::getGenerateRetry, Boolean.FALSE) .eq(MeetingInfo::getIsGenerated,Boolean.FALSE)
.le(MeetingInfo::getSyncTime, LocalDateTime.now().minusHours(1)) .eq(MeetingInfo::getGenerateRetry,Boolean.FALSE)
.le(MeetingInfo::getSyncTime,LocalDateTime.now().minusHours(1))
); );
if (meetingInfoList == null || meetingInfoList.isEmpty()) { if (meetingInfoList == null || meetingInfoList.isEmpty()) {
...@@ -196,17 +213,22 @@ public class CmeetingJob { ...@@ -196,17 +213,22 @@ public class CmeetingJob {
log.info("-------生成纪要重试定时任务结束--------"); log.info("-------生成纪要重试定时任务结束--------");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} finally {
redisUtils.del("Scheduled-retry");
} }
} }
/** /**
* 定时扫描早于一小时之前的,所有邮件推送未重试过的会议,重新推送邮件 * 定时扫描早于一小时之前的,所有邮件推送未重试过的会议,重新推送邮件
*/ */
@Scheduled(fixedRate = 30 * 60 * 1000, initialDelay = 15 * 60 * 1000) @Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 15 * 60 * 1000)
public void emailPushRetry() { public void emailPushRetry() {
if (isDev) { if (isDev) {
return; return;
} }
if (redisUtils.setnx("Scheduled-email-retry", "Scheduled-email-retry", 28 * 60)){
return;
}
try { try {
log.info("-------邮件推送重试定时任务开始-------"); log.info("-------邮件推送重试定时任务开始-------");
log.info("当前时间: " + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)); log.info("当前时间: " + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
...@@ -214,11 +236,11 @@ public class CmeetingJob { ...@@ -214,11 +236,11 @@ public class CmeetingJob {
//查出所有早于一小时前的,邮件推送失败且未重试过的会议 //查出所有早于一小时前的,邮件推送失败且未重试过的会议
List<MeetingInfo> meetingInfoList = List<MeetingInfo> meetingInfoList =
meetingInfoService.list(new LambdaQueryWrapper<MeetingInfo>() meetingInfoService.list(new LambdaQueryWrapper<MeetingInfo>()
.eq(MeetingInfo::getIsGenerated, Boolean.TRUE) .eq(MeetingInfo::getEmailPushAccess,Boolean.TRUE)
.eq(MeetingInfo::getEmailPushAccess, Boolean.TRUE) .eq(MeetingInfo::getIsGenerated,Boolean.TRUE)
.eq(MeetingInfo::getIsPushed, Boolean.FALSE) .eq(MeetingInfo::getIsPushed,Boolean.FALSE)
.eq(MeetingInfo::getPushRetry, Boolean.FALSE) .eq(MeetingInfo::getPushRetry,Boolean.FALSE)
.le(MeetingInfo::getSyncTime, LocalDateTime.now().minusHours(1)) .le(MeetingInfo::getSyncTime,LocalDateTime.now().minusHours(1))
); );
if (meetingInfoList == null || meetingInfoList.isEmpty()) { if (meetingInfoList == null || meetingInfoList.isEmpty()) {
...@@ -241,6 +263,8 @@ public class CmeetingJob { ...@@ -241,6 +263,8 @@ public class CmeetingJob {
log.info("-------邮件推送重试定时任务结束--------"); log.info("-------邮件推送重试定时任务结束--------");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} finally {
redisUtils.del("Scheduled-email-retry");
} }
} }
} }
...@@ -4,6 +4,7 @@ import cn.hutool.core.io.FileUtil; ...@@ -4,6 +4,7 @@ import cn.hutool.core.io.FileUtil;
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.cmeeting.constant.MeetingState;
import com.cmeeting.email.EmailSender; 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;
...@@ -54,8 +55,9 @@ public class EmailPushTask { ...@@ -54,8 +55,9 @@ public class EmailPushTask {
// 实际处理逻辑 // 实际处理逻辑
public void process() { public void process() {
boolean getKey = redisUtils.setnx("meeting_" + meetingId + subMeetingId, meetingId, 19*60); String key = "meet_process" + meetingId + "_" + (subMeetingId == null ? "" : "subMeetingId");
if (!getKey) { if (!redisUtils.setnx(key, 1, 120)) {
log.warn("key already exists in redis!, key: {}", key);
return; return;
} }
Boolean isSuccess = Boolean.FALSE; Boolean isSuccess = Boolean.FALSE;
...@@ -97,17 +99,16 @@ public class EmailPushTask { ...@@ -97,17 +99,16 @@ public class EmailPushTask {
XWPFTemplate template = XWPFTemplate.compile(inputStream).render(dataModel); XWPFTemplate template = XWPFTemplate.compile(inputStream).render(dataModel);
template.writeAndClose(new FileOutputStream(dataNetworkMinutesPath)); template.writeAndClose(new FileOutputStream(dataNetworkMinutesPath));
byte[] recordXmlData = Files.readAllBytes(Paths.get(dataNetworkMinutesPath)); byte[] recordXmlData = Files.readAllBytes(Paths.get(dataNetworkMinutesPath));
minioUtils.upload(meetingInfo.getRecordXml(),recordXmlData);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
//邮件推送 //邮件推送
List<EmailPush.Attachment> attachments = new ArrayList<>(); List<EmailPush.Attachment> attachments = new ArrayList<>();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd");
try(InputStream is = new FileInputStream(dataNetworkMinutesPath)){ try(InputStream is = new FileInputStream(dataNetworkMinutesPath)){
byte[] meetingMinutesBytes = IOUtils.toByteArray(is); byte[] meetingMinutesBytes = IOUtils.toByteArray(is);
EmailPush.Attachment attachment = EmailPush.Attachment.builder().name(meetingInfo.getSubject() + "会议纪要_" + fmt.format(meetingInfo.getStartTime())).bytes(meetingMinutesBytes).build(); DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd");
EmailPush.Attachment attachment = EmailPush.Attachment.builder().name(meetingInfo.getSubject()+"会议纪要_" + fmt.format(meetingInfo.getStartTime())).bytes(meetingMinutesBytes).build();
attachments.add(attachment); attachments.add(attachment);
}catch (Exception e){ }catch (Exception e){
throw new RuntimeException(e); throw new RuntimeException(e);
...@@ -149,9 +150,10 @@ public class EmailPushTask { ...@@ -149,9 +150,10 @@ public class EmailPushTask {
.eq(MeetingInfo::getMeetingId,meetingId) .eq(MeetingInfo::getMeetingId,meetingId)
.eq(subMeetingId != null,MeetingInfo::getSubMeetingId,subMeetingId) .eq(subMeetingId != null,MeetingInfo::getSubMeetingId,subMeetingId)
.set(MeetingInfo::getIsPushed,isSuccess) .set(MeetingInfo::getIsPushed,isSuccess)
.set(MeetingInfo::getStatus, isSuccess ? MeetingState.PUSH_SUCCESS.getCode() : MeetingState.PUSH_ERROR.getCode())
.set(MeetingInfo::getPushRetry,Boolean.TRUE) .set(MeetingInfo::getPushRetry,Boolean.TRUE)
); );
redisUtils.del("meeting_" + meetingId + subMeetingId); redisUtils.del(key);
} }
...@@ -192,14 +194,15 @@ public class EmailPushTask { ...@@ -192,14 +194,15 @@ public class EmailPushTask {
public EmailPushTask(String meetingId, String subMeetingId, String savePath, Map<String, Object> metadata, public EmailPushTask(String meetingId, String subMeetingId, String savePath, Map<String, Object> metadata,
MeetingInfoMapper meetingInfoMapper, MinioUtils minioUtils, EmailSender emailSender, MeetingInfoMapper meetingInfoMapper, MinioUtils minioUtils, RedisUtils redisUtils, EmailSender emailSender,
MeetingRecordTemplateMapper meetingRecordTemplateMapper,Map<String,String> tidWidRelations) { MeetingRecordTemplateMapper meetingRecordTemplateMapper, Map<String,String> tidWidRelations) {
this.savePath = savePath; this.savePath = savePath;
this.metadata = metadata; this.metadata = metadata;
this.meetingId = meetingId; this.meetingId = meetingId;
this.subMeetingId = subMeetingId; this.subMeetingId = subMeetingId;
this.meetingInfoMapper = meetingInfoMapper; this.meetingInfoMapper = meetingInfoMapper;
this.minioUtils = minioUtils; this.minioUtils = minioUtils;
this.redisUtils = redisUtils;
this.emailSender = emailSender; this.emailSender = emailSender;
this.meetingRecordTemplateMapper = meetingRecordTemplateMapper; this.meetingRecordTemplateMapper = meetingRecordTemplateMapper;
this.tidWidRelations = tidWidRelations; this.tidWidRelations = tidWidRelations;
......
...@@ -99,6 +99,10 @@ public class MeetingInfo implements Serializable { ...@@ -99,6 +99,10 @@ public class MeetingInfo implements Serializable {
*/ */
private Boolean isPushed; private Boolean isPushed;
/** /**
* 会议状态: 0新会议待处理 1生成纪要失败 2生成纪要成功 3推送邮件成功 4推送邮件失败 5转录文件为空
*/
private Integer status;
/**
* 会议纪要重新生成标识 * 会议纪要重新生成标识
*/ */
private Boolean generateRetry; private Boolean generateRetry;
......
...@@ -32,9 +32,12 @@ import java.util.concurrent.Future; ...@@ -32,9 +32,12 @@ import java.util.concurrent.Future;
@Slf4j @Slf4j
public class FileProcessProducer { public class FileProcessProducer {
@Autowired @Resource(name = "fileProcessExecutor")
private ThreadPoolTaskExecutor fileProcessExecutor; private ThreadPoolTaskExecutor fileProcessExecutor;
@Resource(name = "emailProcessExecutor")
private ThreadPoolTaskExecutor emailProcessExecutor;
@Value("${aec.key}")
public String aesKey;
@Autowired @Autowired
private FileProcessCallbackHandler callbackHandler; private FileProcessCallbackHandler callbackHandler;
@Value(value = "${llm.api-addr}") @Value(value = "${llm.api-addr}")
...@@ -81,6 +84,7 @@ public class FileProcessProducer { ...@@ -81,6 +84,7 @@ public class FileProcessProducer {
public void submitBatchTasks(List<TencentMeetingVO.RecordFile> recordFiles, List<UserDTO.TemplateAuthorizedUserDTO> authorizedUsers, Map<String, String> tidWidRelations, Boolean finalRetry) { public void submitBatchTasks(List<TencentMeetingVO.RecordFile> recordFiles, List<UserDTO.TemplateAuthorizedUserDTO> authorizedUsers, Map<String, String> tidWidRelations, Boolean finalRetry) {
List<Future<?>> futures = new ArrayList<>(); List<Future<?>> futures = new ArrayList<>();
String adminToken = UserAdminTokenUtil.getUserAdminToken(); String adminToken = UserAdminTokenUtil.getUserAdminToken();
log.info("待处理会议数量: {}", recordFiles.size());
for (TencentMeetingVO.RecordFile recordFile : recordFiles) { for (TencentMeetingVO.RecordFile recordFile : recordFiles) {
// 为每个URL创建任务 // 为每个URL创建任务
FileProcessTask task = new FileProcessTask( FileProcessTask task = new FileProcessTask(
...@@ -106,7 +110,8 @@ public class FileProcessProducer { ...@@ -106,7 +110,8 @@ public class FileProcessProducer {
adminToken, adminToken,
applicationId, applicationId,
fileDownloadPath, fileDownloadPath,
permTenantId permTenantId,
aesKey
); );
// 提交任务到线程池 // 提交任务到线程池
...@@ -135,13 +140,14 @@ public class FileProcessProducer { ...@@ -135,13 +140,14 @@ public class FileProcessProducer {
Collections.emptyMap(), Collections.emptyMap(),
meetingInfoMapper, meetingInfoMapper,
minioUtils, minioUtils,
redisUtils,
emailSender, emailSender,
meetingRecordTemplateMapper, meetingRecordTemplateMapper,
tidWidRelations tidWidRelations
); );
// 提交任务到线程池 // 提交任务到线程池
Future<?> future = fileProcessExecutor.submit(() -> { Future<?> future = emailProcessExecutor.submit(() -> {
task.process(); task.process();
}); });
......
...@@ -18,6 +18,12 @@ public interface MeetingInfoService extends IService<MeetingInfo> { ...@@ -18,6 +18,12 @@ public interface MeetingInfoService extends IService<MeetingInfo> {
boolean regenerateXml(MeetingInfoVO vo); boolean regenerateXml(MeetingInfoVO vo);
/** /**
* 统计邮件推送情况
*/
void statisticsEmail(Integer type, Date startTime, Date endTime, HttpServletResponse response);
/**
* 统计 * 统计
* *
* @param searchValue 查询值 * @param searchValue 查询值
......
...@@ -25,6 +25,7 @@ import com.cmeeting.ad.entity.RobotSecurityUser; ...@@ -25,6 +25,7 @@ import com.cmeeting.ad.entity.RobotSecurityUser;
import com.cmeeting.ad.util.SecurityUtil; import com.cmeeting.ad.util.SecurityUtil;
import com.cmeeting.constant.RecordTemplateConstant; import com.cmeeting.constant.RecordTemplateConstant;
import com.cmeeting.exception.RobotBaseException; import com.cmeeting.exception.RobotBaseException;
import com.cmeeting.constant.MeetingState;
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;
...@@ -34,6 +35,9 @@ import com.cmeeting.pojo.MeetingInfo; ...@@ -34,6 +35,9 @@ import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.pojo.MeetingRecordTemplate; import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.pojo.UserId; import com.cmeeting.pojo.UserId;
import com.cmeeting.service.MeetingInfoService; import com.cmeeting.service.MeetingInfoService;
import com.cmeeting.service.MeetingRecordTemplateService;
import com.cmeeting.service.TencentMeetingService;
import com.cmeeting.service.WeComService;
import com.cmeeting.util.MinioUtils; import com.cmeeting.util.MinioUtils;
import com.cmeeting.util.page.PageUtil; import com.cmeeting.util.page.PageUtil;
import com.cmeeting.vo.MeetingInfoVO; import com.cmeeting.vo.MeetingInfoVO;
...@@ -56,6 +60,7 @@ import org.apache.commons.lang3.StringUtils; ...@@ -56,6 +60,7 @@ import org.apache.commons.lang3.StringUtils;
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;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
...@@ -103,6 +108,17 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti ...@@ -103,6 +108,17 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti
@Autowired @Autowired
private SysUserSysMapper sysUserSysMapper; private SysUserSysMapper sysUserSysMapper;
@Autowired
private WeComService weComService;
@Autowired
private TencentMeetingService tencentMeetingService;
@Autowired
private MeetingRecordTemplateService meetingRecordTemplateService;
private String CLIENT_ID = "c06fe7cf-2a89-4099-9805-ce03031938f8";
private String CLIENT_SECRET = "wsu8Q~GxYxPLf2akioQZDRG8NR1EzCAHIAQRVc6u";
private String TENANT_ID = "18653b3e-03c7-499e-8baf-42ef06a814ef";
@Override @Override
public IPage<MeetingInfo> getPage(MeetingInfoVO vo) { public IPage<MeetingInfo> getPage(MeetingInfoVO vo) {
...@@ -575,4 +591,128 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti ...@@ -575,4 +591,128 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti
throw new RuntimeException("下载文件失败", e); throw new RuntimeException("下载文件失败", e);
} }
} }
/**
* 统计邮件推送情况
*/
@Override
public void statisticsEmail(Integer type, Date startTime, Date endTime, HttpServletResponse response) {
DateTime now = DateUtil.date();
if (ObjectUtils.isEmpty(startTime) || ObjectUtils.isEmpty(endTime)) {
if (1== type) {
startTime = DateUtil.beginOfDay(DateUtil.offset(now, DateField.DAY_OF_MONTH, -2));
endTime = DateUtil.offset(now, DateField.HOUR_OF_DAY, -2);
} else {
startTime = DateUtil.beginOfDay(now);
endTime = now;
}
}
List<MeetingInfo> meetingInfoList = meetingInfoMapper.selectList(new LambdaQueryWrapper<MeetingInfo>()
// .eq(MeetingInfo::getEmailPushAccess, true)
.between(MeetingInfo::getStartTime, startTime, endTime));
Map<String, List<MeetingInfo>> hostUidMeetingInfoMap = meetingInfoList.stream().collect(Collectors.groupingBy(MeetingInfo::getHostUid));
Map<String, String> hostMap = new HashMap<>();
meetingInfoList.forEach(meetingInfo -> {
if (!hostMap.containsKey(meetingInfo.getHostUid())) {
hostMap.put(meetingInfo.getHostUid(), meetingInfo.getHost());
}
});
// 总计
Map<String, Object> totalMap = new LinkedHashMap<>();
totalMap.put("host", "总计");
List<Map<String, Object>> mapList = new ArrayList<>();
for (Map.Entry<String, List<MeetingInfo>> entry : hostUidMeetingInfoMap.entrySet()) {
// 使用linkedHashMap,保证输出到excel中的顺序
Map<String, Object> map = new LinkedHashMap<>();
String host = hostMap.get(entry.getKey());
List<MeetingInfo> meetingList = entry.getValue();
Integer totalNum = meetingList.size();
// 没有转录文件会议,需要生成会议纪要的会议, 待处理, 处理中, 纪要生成成功, 推送成功, 推送失败
Integer emptyNum=0, emailPushAccessNum=0, newNum=0, generatErrorNum=0, noteGeneratedNum=0, pushSuccessNum=0, pushErrorNum=0;
for (MeetingInfo meetingInfo : meetingList) {
if (meetingInfo.getStatus() == MeetingState.NEW.getCode()) {
newNum++;
} else if (meetingInfo.getStatus() == MeetingState.GENERATE_ERROR.getCode()) {
generatErrorNum++;
} else if (meetingInfo.getStatus() == MeetingState.PUSH_SUCCESS.getCode()) {
pushSuccessNum++;
} else if (meetingInfo.getStatus() == MeetingState.PUSH_ERROR.getCode()) {
pushErrorNum++;
} else if (meetingInfo.getStatus() == MeetingState.EMPTY.getCode()) {
emptyNum++;
}
if (meetingInfo.getIsGenerated() || meetingInfo.getStatus() == MeetingState.NOTE_GENERATED.getCode()) {
noteGeneratedNum++;
}
// 有转录文件 且 email_push_access为true,
if (meetingInfo.getStatus() != MeetingState.EMPTY.getCode() && meetingInfo.getEmailPushAccess()) {
emailPushAccessNum++;
}
}
Integer avaliableNum = totalNum - emptyNum;
map.put("host", host);
map.put("totalNum", totalNum);
map.put("avaliableNum", avaliableNum);
map.put("emptyNum", emptyNum);
map.put("emailPushAccessNum", emailPushAccessNum);
map.put("newNum", newNum);
map.put("generatErrorNum", generatErrorNum);
map.put("noteGeneratedNum", noteGeneratedNum);
map.put("pushErrorNum", pushErrorNum);
map.put("pushSuccessNum", pushSuccessNum);
mapList.add(map);
// 计算总计
totalMap.put("totalNum", Integer.valueOf(totalMap.getOrDefault("totalNum", 0).toString()) + totalNum);
totalMap.put("avaliableNum", Integer.valueOf(totalMap.getOrDefault("avaliableNum", 0).toString()) + avaliableNum);
totalMap.put("emptyNum", Integer.valueOf(totalMap.getOrDefault("emptyNum", 0).toString()) + emptyNum);
totalMap.put("emailPushAccessNum", Integer.valueOf(totalMap.getOrDefault("emailPushAccessNum", 0).toString()) + emailPushAccessNum);
totalMap.put("newNum", Integer.valueOf(totalMap.getOrDefault("newNum", 0).toString()) + newNum);
totalMap.put("generatErrorNum", Integer.valueOf(totalMap.getOrDefault("generatErrorNum", 0).toString()) + generatErrorNum);
totalMap.put("noteGeneratedNum", Integer.valueOf(totalMap.getOrDefault("noteGeneratedNum", 0).toString()) + noteGeneratedNum);
totalMap.put("pushErrorNum", Integer.valueOf(totalMap.getOrDefault("pushErrorNum", 0).toString()) + pushErrorNum);
totalMap.put("pushSuccessNum", Integer.valueOf(totalMap.getOrDefault("pushSuccessNum", 0).toString()) + pushSuccessNum);
}
mapList.add(totalMap);
// 导出
OutputStream outputStream = null;
ExcelWriter writer = ExcelUtil.getWriter(true);
try {
String title = String.format("会议纪要推送统计表_%s-%s", DateUtil.format(startTime, "yyyyMMdd"), DateUtil.format(endTime, "yyyyMMdd"));
outputStream = response.getOutputStream();
String fileName = String.format("%s.xlsx", title);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf8"));
writer.addHeaderAlias("host", "主持人");
writer.addHeaderAlias("totalNum", "总会议");
writer.addHeaderAlias("avaliableNum", "有效会议");
writer.addHeaderAlias("emptyNum", "没有转录文件");
writer.addHeaderAlias("emailPushAccessNum", "需生成纪要的会议总数");
writer.addHeaderAlias("newNum", "待处理");
writer.addHeaderAlias("generatErrorNum", "处理中");
writer.addHeaderAlias("noteGeneratedNum", "纪要生成成功");
writer.addHeaderAlias("pushSuccessNum", "纪要推送成功");
writer.addHeaderAlias("pushErrorNum", "纪要推送失败");
// 合并单元格后的标题行,使用默认标题样式
writer.merge(writer.getHeaderAlias().size() - 1, title);
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(mapList, true);
writer.flush(outputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
writer.close();
IoUtil.close(outputStream);
}
}
} }
...@@ -126,7 +126,7 @@ public class RecordTemplatePermissionServiceImpl extends ServiceImpl<RecordTempl ...@@ -126,7 +126,7 @@ public class RecordTemplatePermissionServiceImpl extends ServiceImpl<RecordTempl
? new HashMap<>() : simpleUserList.stream().collect(Collectors.toMap(SysUserSync::getUserId, SysUserSync::getName)); ? new HashMap<>() : simpleUserList.stream().collect(Collectors.toMap(SysUserSync::getUserId, SysUserSync::getName));
List<SysDept> simpleDeptList = sysUserSysMapper.getSimpleDeptList(user.getTenantId()); List<SysDept> simpleDeptList = sysUserSysMapper.getSimpleDeptList(user.getTenantId());
Map<String, String> simpleDeptMap = CollectionUtils.isEmpty(simpleDeptList) Map<String, String> simpleDeptMap = CollectionUtils.isEmpty(simpleDeptList)
? new HashMap<>() : simpleDeptList.stream().collect(Collectors.toMap(SysDept::getId, SysDept::getName)); ? new HashMap<>() : simpleDeptList.stream().collect(Collectors.toMap(SysDept::getDeptId, SysDept::getName));
for (RecordTemplatePermission permission : permissions) { for (RecordTemplatePermission permission : permissions) {
String relName = "1".equals(permission.getRelType()) && simpleDeptMap.containsKey(permission.getRelId()) String relName = "1".equals(permission.getRelType()) && simpleDeptMap.containsKey(permission.getRelId())
? simpleDeptMap.get(permission.getRelId()) ? simpleDeptMap.get(permission.getRelId())
......
...@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; ...@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmeeting.constant.PermissionPruposeType; import com.cmeeting.constant.PermissionPruposeType;
import com.cmeeting.constant.MeetingState;
import com.cmeeting.dto.UserDTO; import com.cmeeting.dto.UserDTO;
import com.cmeeting.log.service.ProcessLogService; import com.cmeeting.log.service.ProcessLogService;
import com.cmeeting.mapper.primary.AuthMapper; import com.cmeeting.mapper.primary.AuthMapper;
...@@ -45,7 +46,10 @@ import java.io.PrintWriter; ...@@ -45,7 +46,10 @@ import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*; import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
...@@ -76,6 +80,11 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper, ...@@ -76,6 +80,11 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,
@Value(value = "${tencent.search-scope}") @Value(value = "${tencent.search-scope}")
private Integer searchDays; private Integer searchDays;
/**
* 查询会议不得早于这个时间
*/
@Value(value = "${tencent.search-end-time}")
private String searchEndTime;
@Value(value = "${tencent.appId}") @Value(value = "${tencent.appId}")
private String tencentAppId; private String tencentAppId;
@Value(value = "${tencent.sdkId}") @Value(value = "${tencent.sdkId}")
...@@ -124,24 +133,39 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper, ...@@ -124,24 +133,39 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,
public List<TencentMeetingVO.RecordFile> getMeetingFiles(List<UserDTO> accessUserIds, Map<String, WeComUser> weComUserMap) { public List<TencentMeetingVO.RecordFile> getMeetingFiles(List<UserDTO> accessUserIds, Map<String, WeComUser> weComUserMap) {
List<TencentMeetingVO.RecordFile> meetingFiles = new ArrayList<>(); List<TencentMeetingVO.RecordFile> meetingFiles = new ArrayList<>();
List<MeetingInfo> meetingSaveList = new ArrayList<>(); List<MeetingInfo> meetingSaveList = new ArrayList<>();
int i = 0;
// 查询近searchDays天的会议录制列表 // 查询近searchDays天的会议录制列表
try { try {
ZonedDateTime now = ZonedDateTime.now(); ZonedDateTime now = ZonedDateTime.now();
long startTime = now.minusDays(searchDays).toEpochSecond(); long startTime = now.minusDays(searchDays).toEpochSecond();
long endTime = now.toEpochSecond(); long endTime = now.toEpochSecond();
try {
long end = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(searchEndTime).getTime() / 1000;
//仅查询2025-07-30 23:59:59之后的会议
if (end > endTime) {
return new ArrayList<>();
}
if (end > startTime) {
startTime = end;
}
} catch (Exception e) {
log.error("参数searchEndTime格式错误!");
}
AtomicInteger currentPage = new AtomicInteger(1); AtomicInteger currentPage = new AtomicInteger(1);
//获取总页数 //获取总页数
CorpRecordsVO firstData = fetchMeetingRecords(tencentAdminUserId, 1, startTime, endTime, 1, 20); CorpRecordsVO firstData = fetchMeetingRecords(tencentAdminUserId, 1, startTime, endTime, 1, 20);
Integer totalPage = firstData.getTotalPage(); Integer totalPage = firstData.getTotalPage();
log.info("总页数: {}", totalPage);
//目前已存储的会议id //目前已存储的会议id
List<TencentMeetingVO.SimpleMeetingInfo> meetingIds = meetingInfoMapper.getAllMeetingIds(); List<TencentMeetingVO.SimpleMeetingInfo> meetingIds = meetingInfoMapper.getAllMeetingIds();
List<TencentMeetingUser> meetingUsers = tecentMeetingMapper.getAlluser(); List<TencentMeetingUser> meetingUsers = tecentMeetingMapper.getAlluser();
Map<String, String> meetingMap = meetingUsers.stream().collect(Collectors.toMap(TencentMeetingUser::getUserId, TencentMeetingUser::getUserName)); Map<String, String> meetingMap = meetingUsers.stream().collect(Collectors.toMap(TencentMeetingUser::getUserId, TencentMeetingUser::getUserName));
while (currentPage.intValue() <= totalPage){
if (i >= 150) {
while (currentPage.intValue() <= totalPage) { break;
}
CorpRecordsVO data = fetchMeetingRecords(tencentAdminUserId, 1, startTime, endTime, currentPage.getAndIncrement(), 20); CorpRecordsVO data = fetchMeetingRecords(tencentAdminUserId, 1, startTime, endTime, currentPage.getAndIncrement(), 20);
//设置总页数 //设置总页数
if (data != null && data.getRecordMeetings() != null && !data.getRecordMeetings().isEmpty()) { if (data != null && data.getRecordMeetings() != null && !data.getRecordMeetings().isEmpty()) {
...@@ -218,8 +242,9 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper, ...@@ -218,8 +242,9 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,
if (host.isPresent()) { if (host.isPresent()) {
hostId = host.get().getUserid(); hostId = host.get().getUserid();
hostName = new String(Base64.getDecoder().decode(host.get().getUserName())); hostName = new String(Base64.getDecoder().decode(host.get().getUserName()));
} else { }else{
log.error("未找到主持人,默认没有生成纪要权限"); log.error("未找到主持人,默认没有生成纪要权限, meetId: {}, subId: {}", meetingId, subMeetingId);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"未找到主持人,默认没有生成纪要权限");
continue; continue;
} }
} }
...@@ -269,12 +294,14 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper, ...@@ -269,12 +294,14 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,
.subMeetingId(subMeetingId).generateRetry(Boolean.FALSE).pushRetry(Boolean.FALSE) .subMeetingId(subMeetingId).generateRetry(Boolean.FALSE).pushRetry(Boolean.FALSE)
.host(hostName) .host(hostName)
.hostUid(hostId) .hostUid(hostId)
.status(MeetingState.NEW.getCode())
// .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(String.join(",", recordFileIdList)) .recordFileId(String.join(",", recordFileIdList))
.email(email) .email(email)
.build(); .build();
meetingSaveList.add(meetingItem); meetingSaveList.add(meetingItem);
i++;
} }
} catch (Exception e) { } catch (Exception e) {
if (e instanceof NoSuchElementException) { if (e instanceof NoSuchElementException) {
...@@ -296,11 +323,14 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper, ...@@ -296,11 +323,14 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,
List<String> recordFileIdList = entry.getValue().stream().flatMap(s -> Arrays.stream(s.getRecordFileId().split(","))).collect(Collectors.toList()); List<String> recordFileIdList = entry.getValue().stream().flatMap(s -> Arrays.stream(s.getRecordFileId().split(","))).collect(Collectors.toList());
meetingInfo.setRecordFileId(String.join(",", recordFileIdList)); meetingInfo.setRecordFileId(String.join(",", recordFileIdList));
finalSaveList.add(meetingInfo); finalSaveList.add(meetingInfo);
if (meetingInfo.getEmailPushAccess()) {
meetingFiles.add(TencentMeetingVO.RecordFile.builder() meetingFiles.add(TencentMeetingVO.RecordFile.builder()
.meetingId(meetingInfo.getMeetingId()) .meetingId(meetingInfo.getMeetingId())
.subMeetingId(meetingInfo.getSubMeetingId()) .subMeetingId(meetingInfo.getSubMeetingId())
.recordFileIdList(recordFileIdList).build()); .recordFileIdList(recordFileIdList).build());
} }
}
meetingInfoMapper.batchInsert(finalSaveList); meetingInfoMapper.batchInsert(finalSaveList);
} }
} catch (Exception e) { } catch (Exception e) {
...@@ -415,7 +445,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper, ...@@ -415,7 +445,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,
// 3. 生成签名 // 3. 生成签名
String signature = SignatureUtil.generateSignature(tencentSecretId, tencentSecretKey, httpMethod, nonce, timestamp, uri, ""); String signature = SignatureUtil.generateSignature(tencentSecretId, tencentSecretKey, httpMethod, nonce, timestamp, uri, "");
log.info("fetchMeetingRecords params: {}, {}", page, pageSize);
// 4. 发送请求 // 4. 发送请求
try (CloseableHttpClient httpClient = HttpClients.createDefault()) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet request = new HttpGet("https://api.meeting.qq.com" + uri); HttpGet request = new HttpGet("https://api.meeting.qq.com" + uri);
...@@ -597,4 +627,5 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper, ...@@ -597,4 +627,5 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,
return null; return null;
} }
} }
} }
package com.cmeeting.util;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
public class JasyptUtilTest {
public static String password = "EbfYkitulv23I2p1mXI50JMXoaxZTKJ7";
public static String algorithm = "PBEWITHMD5ANDDES";
/**
* 加密方法
*
* @param value 需要加密的值
* @return
*/
public static String encyptPwd(String value) {
//1.创建加密工具实例
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
//2.加解密配置
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm(algorithm);
//3.为了减少配置文件的书写,以下都是jasyp 3.x版本,配置文件默认配种
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor.encrypt(value);
}
public static String decryptPwd(String value) {
//1.创建加密工具实例
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
//2.加解密配置
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm(algorithm);
//3.为了减少配置文件的书写,以下都是jasyp 3.x版本,配置文件默认配种
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor.decrypt(value);
}
public static void main(String[] args) {
String en = encyptPwd("scyou@xih45g6@xih4");
System.out.println(en);
}
}
...@@ -140,6 +140,14 @@ public class RedisUtils { ...@@ -140,6 +140,14 @@ public class RedisUtils {
return false; return false;
} }
} }
public boolean setnx(String key, Object value, long time) {
try {
return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean setnx(String key, Object value, long time) { public boolean setnx(String key, Object value, long time) {
try { try {
......
package com.cmeeting.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* @Description
* @Author zhang kaishi
* @Date 2025/7/28 18:22
**/
@Data
public class EmailStatisticsVo {
/**
* 统计类型
*/
private Integer type;
@DateTimeFormat(pattern = "yyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone = "GMT+8")
private Date startTime;
@DateTimeFormat(pattern = "yyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone = "GMT+8")
private Date endTime;
}
...@@ -30,7 +30,7 @@ CORP_SECRET: 7YLePWG7rJqkQFnAB4FeylqAXpmu7q5qv_NOeSGNbm0 ...@@ -30,7 +30,7 @@ CORP_SECRET: 7YLePWG7rJqkQFnAB4FeylqAXpmu7q5qv_NOeSGNbm0
############################################################## email ############################################################## email
EMAIL_SENDER: cmeeting_assistant@cimc.com EMAIL_SENDER: cmeeting_assistant@cimc.com
EMAIL_SENDER_PWD: scyou@xih45g6@xih4 #EMAIL_SENDER_PWD: ENC(bH585hgTtUY5KpMgFqFpNRyL4kI/G7czZUk/7+U+LZY=)
EMAIL_SMTP_HOST: smtp.office365.com EMAIL_SMTP_HOST: smtp.office365.com
############################################################## permission ############################################################## permission
...@@ -105,10 +105,11 @@ tencent: ...@@ -105,10 +105,11 @@ tencent:
aesKey: agy6ALUePp34lljWz1uIQWa7yQq3dgxxQNmfaN9GROm aesKey: agy6ALUePp34lljWz1uIQWa7yQq3dgxxQNmfaN9GROm
base-save-path: E:/save/ #会议纪要临时文件存储路径 base-save-path: E:/save/ #会议纪要临时文件存储路径
search-scope: 2 #腾会转录文件拉取时间范围,查询过去n天的记录 search-scope: 2 #腾会转录文件拉取时间范围,查询过去n天的记录
search-end-time: '2025-07-30 23:59:59' #此时间之前的数据不拉取
email: email:
sender: ${EMAIL_SENDER} sender: ${EMAIL_SENDER}
sender-pwd: ${EMAIL_SENDER_PWD} # sender-pwd: ${EMAIL_SENDER_PWD}
smtp-host: ${EMAIL_SMTP_HOST} smtp-host: ${EMAIL_SMTP_HOST}
push-switch: true #邮件推送总开关,高优先级 push-switch: true #邮件推送总开关,高优先级
environment: test #test推给本公司人员,prod推给用户 environment: test #test推给本公司人员,prod推给用户
...@@ -152,6 +153,15 @@ permission: ...@@ -152,6 +153,15 @@ permission:
tenantId: ${PERMISSION_TENANT_ID} tenantId: ${PERMISSION_TENANT_ID}
admin-white_users: ${PERMISSION_ADMIN_WHITE_USERS} admin-white_users: ${PERMISSION_ADMIN_WHITE_USERS}
jasypt:
encryptor:
password: EbfYkitulv23I2p1mXI50JMXoaxZTKJ7
algorithm: PBEWITHMD5ANDDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
property:
prefix: ENC(
suffix: )
# 默认模板 # 默认模板
record-template-path-default: /2025-07-21/a12465sdfsa2gas.docx record-template-path-default: /2025-07-21/a12465sdfsa2gas.docx
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<insert id="batchInsert" parameterType="list"> <insert id="batchInsert" parameterType="list">
INSERT IGNORE INTO cmt_meeting_info (subject, meeting_id, meeting_code, host, host_uid, participant_users, start_time, INSERT IGNORE INTO cmt_meeting_info (subject, meeting_id, meeting_code, host, host_uid, participant_users, start_time,
end_time, is_generated, email_generate_access, email_push_access, is_pushed, sync_time, sub_meeting_id, record_content, record_xml, generate_retry, end_time, is_generated, email_generate_access, email_push_access, is_pushed, sync_time, sub_meeting_id, record_content, record_xml, generate_retry,
push_retry, record_file_id,email) push_retry, record_file_id, status, email)
VALUES VALUES
<foreach collection="meetingSaveList" item="meeting" separator=","> <foreach collection="meetingSaveList" item="meeting" separator=",">
( (
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#{meeting.generateRetry}, #{meeting.generateRetry},
#{meeting.pushRetry}, #{meeting.pushRetry},
#{meeting.recordFileId}, #{meeting.recordFileId},
#{meeting.status},
#{meeting.email} #{meeting.email}
) )
</foreach> </foreach>
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
</select> </select>
<select id="getSimpleDeptList" resultType="com.cmeeting.ad.entity.SysDept"> <select id="getSimpleDeptList" resultType="com.cmeeting.ad.entity.SysDept">
SELECT SELECT
susc.id, susc.dept_id,
susc.`name` susc.`name`
FROM FROM
sys_user_sync_category AS susc sys_user_sync_category AS susc
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论