提交 e0b7d23d 作者: duanxincheng

二次重试机制;模板创建与维护;会议纪要生成权限控制

父级 b87c127c
......@@ -330,11 +330,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.0</version> <!-- 与你的Spring Boot版本一致 -->
</dependency>
</dependencies>
......
......@@ -146,7 +146,7 @@ public class TencentMeetingCallbackController {
//通过智能体id查询该id下的部门和用户
String targetId = "1815393211829587968";//职能体id
String tenantId = "1806976109082972160";//租户id
List<CoreModulePermissions> auths = authMapper.getAuthByTargrtId(targetId, tenantId);
List<CoreModulePermissions> auths = authMapper.getAuthByTargetId(targetId, tenantId);
// 创建两个集合分别存储type=0(部门)和type=1(员工)的数据
List<CoreModulePermissions> type0List = new ArrayList<>();
List<CoreModulePermissions> type1List = new ArrayList<>();
......
......@@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.cmeeting.WeComAndTencentMeeting.*;
@RestController
@RequestMapping("/tencent")
......@@ -24,8 +23,8 @@ public class TencentMeetingController {
tecentMeetingService.doUsers();
}
@GetMapping("/getMeetingFiles")
public void getMeetingFiles(){
tecentMeetingService.getMeetingFiles();
}
// @GetMapping("/getMeetingFiles")
// public void getMeetingFiles(){
// tecentMeetingService.getMeetingFiles();
// }
}
......@@ -127,32 +127,32 @@ public class UnifiedController {
//取消预约会议
}
//todo 待测试
@GetMapping("/sameNameInsertTid")
public void sameNameInsertTid(String corpid, String corpsecret) throws IOException {
String weComToken = getWeComToken(corpid,corpsecret);
List<WeComUser> sameNameUsers = weComUserMapper.getSameName();
List<UserId> userIds = new ArrayList<>();
for (WeComUser user : sameNameUsers) {
Map<String, String> meetingCodeAndMeetingid = createMeeting(user.getUserId(), weComToken);
String meetingId = meetingCodeAndMeetingid.get("meetingid");
String meetingCode = meetingCodeAndMeetingid.get("meeting_code");
if (meetingCode == null || meetingCode.isEmpty()) {
System.err.println("会议创建失败,跳过用户: " + user.getUserId());
continue;
}
// 3.2 查询会议详情,获取主持人腾讯会议 userid(假设是 host_userid)
String hostUserId = getMeetingHost(meetingCode);
if (hostUserId == null || hostUserId.isEmpty()) {
System.err.println("获取主持人失败,跳过会议: " + meetingCode);
cancelMeeting(meetingId, weComToken); // 尝试取消无效会议
continue;
}
UserId userId = new UserId(user.getUserName(), user.getUserId(), hostUserId);
userIds.add(userId);
}
userIdMapper.insertUsers(userIds);
}
// //todo 待测试
// @GetMapping("/sameNameInsertTid")
// public void sameNameInsertTid(String corpid, String corpsecret) throws IOException {
// String weComToken = getWeComToken(corpid,corpsecret);
// List<WeComUser> sameNameUsers = weComUserMapper.getSameName();
// List<UserId> userIds = new ArrayList<>();
// for (WeComUser user : sameNameUsers) {
// Map<String, String> meetingCodeAndMeetingid = createMeeting(user.getUserId(), weComToken);
// String meetingId = meetingCodeAndMeetingid.get("meetingid");
// String meetingCode = meetingCodeAndMeetingid.get("meeting_code");
// if (meetingCode == null || meetingCode.isEmpty()) {
// System.err.println("会议创建失败,跳过用户: " + user.getUserId());
// continue;
// }
// // 3.2 查询会议详情,获取主持人腾讯会议 userid(假设是 host_userid)
// String hostUserId = getMeetingHost(meetingCode);
// if (hostUserId == null || hostUserId.isEmpty()) {
// System.err.println("获取主持人失败,跳过会议: " + meetingCode);
// cancelMeeting(meetingId, weComToken); // 尝试取消无效会议
// continue;
// }
// UserId userId = new UserId(user.getUserName(), user.getUserId(), hostUserId);
// userIds.add(userId);
// }
// userIdMapper.insertUsers(userIds);
// }
/**
* 通过企业微信接口取消预约会议
......
......@@ -18,7 +18,6 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static com.cmeeting.WeComAndTencentMeeting.*;
@RestController
@RequestMapping("/wecom")
......
// vo/LoginVO.java
package com.cmeeting.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
/**
* 员工姓名
*/
private String userName;
/**
* 企业微信userid
*/
private String wid;
/**
* 腾讯会议userid
*/
private String tid;
/**
* 邮箱
*/
private String email;
}
\ No newline at end of file
package com.cmeeting.email;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
......@@ -29,10 +30,10 @@ public class EmailSender {
* @param toEmail 收件人
* @param subject 邮件主题
* @param attachmentPath 附件路径
* @param recordFileId 转录文件ID
* @param meetingId 会议id
* @return
*/
public boolean sendEmailWithAttachment(String toEmail, String subject, String attachmentPath, String recordFileId) throws Exception {
public boolean sendEmailWithAttachment(String toEmail, String subject, String attachmentPath, String meetingId) {
// 邮件服务器配置
Properties props = new Properties();
props.put("mail.smtp.host", SMTP_HOST);
......@@ -62,6 +63,10 @@ public class EmailSender {
AtomicInteger retryCount = new AtomicInteger(0);
boolean isSent = false;
if(StringUtils.isEmpty(toEmail)){
log.error("收件邮箱为空,推送失败");
return false;
}
while (retryCount.intValue() < MAX_RETRY && !isSent){
try {
// 创建邮件消息
......@@ -95,14 +100,14 @@ public class EmailSender {
// 发送邮件
Transport.send(message);
log.error("邮件已成功发送: recordFileId->{}", recordFileId);
log.error("邮件已成功发送: meetingId->{}", meetingId);
isSent = true;
} catch (MessagingException e) {
//todo 邮件失败记录
// 异常处理
retryCount.getAndIncrement();
if (retryCount.intValue() > MAX_RETRY) {
log.error("邮件发送达到最大重试次数: recordFileId->{}", recordFileId);
log.error("邮件发送达到最大重试次数: meetingId->{}", meetingId);
throw new RuntimeException(e);
}
// 指数退避
......@@ -112,9 +117,8 @@ public class EmailSender {
Thread.currentThread().interrupt();
throw new RuntimeException("邮件发送重试失败", ie);
}
return false;
}
}
return true;
return isSent;
}
}
\ No newline at end of file
package com.cmeeting.job;
import cn.chatbot.openai.completion.chat.ChatCompletionRequest;
import cn.chatbot.openai.completion.chat.ChatMessage;
import cn.chatbot.openai.completion.chat.ChatMessageRole;
import cn.chatbot.openai.completion.chat.Message;
import cn.chatbot.openai.service.LLMService;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cmeeting.email.EmailSender;
import com.cmeeting.mapper.primary.MeetingInfoMapper;
import com.cmeeting.mapper.primary.MeetingRecordTemplateMapper;
import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.util.MinioUtils;
import com.deepoove.poi.XWPFTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.tencentcloudapi.wemeet.Client;
import com.tencentcloudapi.wemeet.core.authenticator.AuthenticatorBuilder;
import com.tencentcloudapi.wemeet.core.authenticator.JWTAuthenticator;
import com.tencentcloudapi.wemeet.service.meetings.api.MeetingsApi;
import com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200Response;
import com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner;
import com.tencentcloudapi.wemeet.service.records.api.RecordsApi;
import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200Response;
import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200ResponseAiMeetingTranscriptsInner;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Service;
import java.io.*;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Data
@NoArgsConstructor
@Slf4j
@Service
public class EmailPushTask {
private String meetingId;
private String subMeetingId;
private String savePath;
private Map<String, Object> metadata;
private static final int MAX_RETRY = 3;
private MeetingInfoMapper meetingInfoMapper;
private MinioUtils minioUtils;
private EmailSender emailSender;
private MeetingRecordTemplateMapper meetingRecordTemplateMapper;
// 实际处理逻辑
public void process() {
Boolean isSuccess = Boolean.FALSE;
AtomicInteger retryCount = new AtomicInteger(0);
MeetingInfo meetingInfo = meetingInfoMapper.selectOne(new LambdaQueryWrapper<MeetingInfo>()
.eq(MeetingInfo::getMeetingId,meetingId)
.eq(subMeetingId != null, MeetingInfo::getSubMeetingId, subMeetingId));
while (retryCount.intValue() <= MAX_RETRY && !isSuccess) {
try {
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HHmmss"));
String targetFileName;
String meetingName;
String recordXmlPath = meetingId + "-recordXmlPath-" + IdUtil.fastSimpleUUID() + "xml";
String xml;
try(InputStream is = minioUtils.getFile(recordXmlPath)){
xml = convertInputStreamToString(is);
}catch (Exception e){
log.error(e.getMessage());
continue;
}
//将xml格式的内容转换为map,用于填充模板
Map<String, Object> dataModel = convertXmlToMap(xml);
//追加参会人员信息
Map<String,Object> participantsMap = new ConcurrentHashMap<>();
participantsMap.put("meeting_date",meetingInfo.getStartTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
participantsMap.put("meeting_location","线上腾讯会议");
participantsMap.put("meeting_participants", meetingInfo.getParticipantUsers());
participantsMap.put("meeting_host",meetingInfo.getHost());
dataModel.putAll(participantsMap);
String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
XWPFTemplate template = XWPFTemplate.compile(path + "template/data_net_template.docx").render(dataModel);
meetingName = dataModel.get("meeting_name") != null ? String.valueOf(dataModel.get("meeting_name")) : "腾讯会议纪要";
targetFileName = meetingName + "_" + nowTime;
template.writeAndClose(new FileOutputStream(savePath + targetFileName + ".docx"));
byte[] recordXmlData = Files.readAllBytes(Paths.get(savePath + targetFileName + ".docx"));
minioUtils.upload(recordXmlPath,recordXmlData);
//邮件推送
isSuccess = emailSender.sendEmailWithAttachment("duanxincheng@chatbot.cn",meetingName,savePath + targetFileName + ".docx",meetingId);
} catch (Exception e) {
// 异常处理
int currentRetryCount = retryCount.getAndIncrement();
if (currentRetryCount > MAX_RETRY) {
log.error("达到最大重试次数:meetingId {}", meetingId);
}else{
// 指数退避
try {
Thread.sleep((long) Math.pow(2, currentRetryCount) * 1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
log.error("重试失败: {}", ie.getMessage());
}
}
}
}
meetingInfoMapper.update(meetingInfo,
new LambdaUpdateWrapper<MeetingInfo>()
.eq(MeetingInfo::getMeetingId,meetingId)
.eq(subMeetingId != null,MeetingInfo::getSubMeetingId,subMeetingId)
.set(MeetingInfo::getIsPushed,isSuccess)
.set(MeetingInfo::getPushRetry,Boolean.TRUE)
);
}
public static Map<String, Object> convertXmlToMap(String xml) throws Exception {
XmlMapper xmlMapper = new XmlMapper();
ObjectMapper objectMapper = new ObjectMapper();
// 先将 XML 读取为 Map
Map<?, ?> xmlMap = xmlMapper.readValue(xml, Map.class);
// 转换为更标准的 Map<String, Object>
Map<String,Object> map = objectMapper.convertValue(xmlMap, Map.class);
//特殊处理map格式
for (Map.Entry<String, Object> entry : map.entrySet()) {
Map<String,Object> value = (Map<String, Object>) entry.getValue();
Object realValue = value.get("");
entry.setValue(realValue);
}
return map;
}
public static String convertInputStreamToString(InputStream inputStream) throws IOException {
StringBuilder result = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
// 如果需要保留换行符,可添加:result.append("\n");
}
}
return result.toString();
}
public EmailPushTask(String meetingId, String subMeetingId, String savePath, Map<String, Object> metadata,
MeetingInfoMapper meetingInfoMapper, MinioUtils minioUtils, EmailSender emailSender, MeetingRecordTemplateMapper meetingRecordTemplateMapper) {
this.savePath = savePath;
this.metadata = metadata;
this.meetingId = meetingId;
this.subMeetingId = subMeetingId;
this.meetingInfoMapper = meetingInfoMapper;
this.minioUtils = minioUtils;
this.emailSender = emailSender;
this.meetingRecordTemplateMapper = meetingRecordTemplateMapper;
}
}
\ No newline at end of file
......@@ -8,5 +8,6 @@ import java.util.List;
@Mapper
public interface AuthMapper {
List<CoreModulePermissions> getAuthByTargrtId(@Param("targetId") String targetId, @Param("tenantId") String tenantId);
List<CoreModulePermissions> getAuthByTargetId(@Param("targetId") String targetId, @Param("tenantId") String tenantId);
}
package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.pojo.MeetingRecordTemplate;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MeetingRecordTemplateMapper extends BaseMapper<MeetingRecordTemplate> {
}
\ No newline at end of file
package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.pojo.UserId;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserIdMapper {
public interface UserIdMapper extends BaseMapper<UserId> {
void insertUsers (List<UserId> userIds);
List<UserId> getUsers();
......
......@@ -7,7 +7,7 @@ import java.util.List;
public interface WeComUserMapper extends BaseMapper<WeComUser> {
List<WeComUser> getSameName() ;
List<WeComUser> noBindUsers() ;
WeComUser selectById(Integer id);
int insert(WeComUser user);
......
package com.cmeeting.mapper.secondary;
import com.cmeeting.dto.UserDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysUserSysMapper {
String getCompanyEmail(String wid, String tenantId);
String getParentDeptId(String deptId, String tenantId);
List<String> getSubDeptId(@Param(value = "deptId")String deptId,@Param(value = "tenantId") String tenantId);
List<String> getUsersByDept(@Param(value = "deptIds") List<String> deptIds,@Param(value = "tenantId") String tenantId);
List<UserDTO> getUserEmail(@Param(value = "tenantId")String tenantId);
}
......@@ -20,6 +20,8 @@ import java.time.LocalDateTime;
@Accessors(chain = true)
@TableName("cmt_meeting_info")
public class MeetingInfo implements Serializable {
private static final long serialVersionUID = -26238487532381000L;
/**
* 主键id
*/
......@@ -48,12 +50,17 @@ public class MeetingInfo implements Serializable {
/**
* 主持人
*/
private String hostId;
private String host;
/**
* 主持人uid
*/
private String hostUid;
/**
* 参会人员名单
*/
private String participantUserIds;
private String participantUsers;
/**
* 会议开始时间(时间戳)
......@@ -78,6 +85,14 @@ public class MeetingInfo implements Serializable {
*/
private Boolean isPushed;
/**
* 会议纪要重新生成标识
*/
private Boolean generateRetry;
/**
* 邮件推送重试标识
*/
private Boolean pushRetry;
/**
* 同步时间
*/
private LocalDateTime syncTime;
......@@ -91,4 +106,12 @@ public class MeetingInfo implements Serializable {
* 纪要xml
*/
private String recordXml;
/**
* 每个会议对应的转录文件id,多个用逗号分隔,按参会前后升序
*/
private String recordFileId;
/**
* 邮箱
*/
private String email;
}
\ No newline at end of file
package com.cmeeting.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 会议纪要模板实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("cmt_meeting_record_template")
public class MeetingRecordTemplate implements Serializable {
private static final long serialVersionUID = -26238487532381000L;
/**
* 主键id
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 模板名称
*/
private String name;
/**
* 模板类型(系统模板/自定义模板)
*/
private String type;
/**
* 会议类型
*/
private String meetingType;
/**
* 会议类型描述
*/
private String typeDetail;
/**
* 提示词内容
*/
private String prompt;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 创建时间
*/
private LocalDateTime updateTime;
/**
* 创建人
*/
private String createUser;
/**
* 更新人
*/
private String updateUser;
}
\ No newline at end of file
package com.cmeeting.pojo;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
@Data
import java.io.Serializable;
public class TencentMeetingUser {
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user_tencentmeeting")
public class TencentMeetingUser implements Serializable {
private static final long serialVersionUID = -26238487532381000L;
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
......@@ -25,26 +37,4 @@ public class TencentMeetingUser {
* 是否是重名用户(1:重名, 0:不重名)
*/
private String isrepeatName;
// 无参构造方法
public TencentMeetingUser() {
}
// 全参构造方法
public TencentMeetingUser(Integer id, String userName, String userId, String isrepeatName) {
this.id = id;
this.userName = userName;
this.userId = userId;
this.isrepeatName = isrepeatName;
}
@Override
public String toString() {
return "TencentMeetingUser{" +
"id=" + id +
", userName='" + userName + '\'' +
", userId='" + userId + '\'' +
", isrepeatName='" + isrepeatName + '\'' +
'}';
}
}
package com.cmeeting.pojo;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("userid")
public class UserId {
/**
* 主键ID
......@@ -24,23 +32,4 @@ public class UserId {
*/
private String tid;
// 无参构造方法
public UserId() {
}
// 全参构造方法
public UserId(Integer id, String userName, String wid, String tid) {
this.id = id;
this.userName = userName;
this.wid = wid;
this.tid = tid;
}
// 全参构造方法
public UserId( String userName, String wid, String tid) {
this.userName = userName;
this.wid = wid;
this.tid = tid;
}
}
package com.cmeeting.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
public class WeComUser extends Model implements Serializable {
@AllArgsConstructor
@NoArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user_wecom")
public class WeComUser implements Serializable {
private static final long serialVersionUID = -26238487532381000L;
@TableId(type = IdType.AUTO)
private Integer id;
private String userName;
private String userId;
private String isRepeatName;
private String email;
public WeComUser() {
}
public WeComUser(Integer id, String userName, String userId, String isRepeatName) {
this.id = id;
this.userName = userName;
this.userId = userId;
this.isRepeatName = isRepeatName;
}
@Override
public String toString() {
return "WeComUser{" +
"id=" + id +
", userName='" + userName + '\'' +
", userId='" + userId + '\'' +
", isRepeatName='" + isRepeatName + '\'' +
", email='" + email + '\'' +
'}';
}
}
......@@ -11,7 +11,7 @@ public class FileProcessCallbackHandler {
// 单个任务完成回调
public void onComplete(FileProcessTask task) {
// 可以记录任务状态、发送通知等
log.info("任务处理完成: {}", task.getRecordFileId());
log.info("任务处理完成: meetingId {}", task.getMeetingId());
// 更新数据库状态等
// taskRepository.updateStatus(task.getId(), "COMPLETED");
......
package com.cmeeting.service;
import com.cmeeting.dto.UserDTO;
import com.cmeeting.email.EmailSender;
import com.cmeeting.job.EmailPushTask;
import com.cmeeting.job.FileProcessTask;
import com.cmeeting.mapper.primary.MeetingInfoMapper;
import com.cmeeting.mapper.primary.MeetingRecordTemplateMapper;
import com.cmeeting.pojo.UserId;
import com.cmeeting.util.MinioUtils;
import com.cmeeting.vo.TencentMeetingVO;
import lombok.extern.slf4j.Slf4j;
......@@ -37,24 +41,33 @@ public class FileProcessProducer {
private String tencentSecretKey;
@Value(value = "${tencent.admin.userId}")
private String tencentAdminUserId;
@Value(value = "${llm.api-addr}")
private String llmApiAddr;
@Resource
private MeetingInfoMapper meetingInfoMapper;
@Resource
private MeetingRecordTemplateMapper meetingRecordTemplateMapper;
@Resource
private MinioUtils minioUtils;
@Resource
private EmailSender emailSender;
// 批量提交任务
public void submitBatchTasks(List<TencentMeetingVO.RecordFile> recordFiles, String baseSavePath) {
/**
* 批量提交生成纪要任务
* @param recordFiles 转录文件信息
* @param baseSavePath 存储临时路径
* @param finalRetry
*/
public void submitBatchTasks(List<TencentMeetingVO.RecordFile> recordFiles, String baseSavePath, Boolean finalRetry) {
List<Future<?>> futures = new ArrayList<>();
for (TencentMeetingVO.RecordFile recordFile : recordFiles) {
// 为每个URL创建任务
FileProcessTask task = new FileProcessTask(
recordFile.getRecordFileId(),
recordFile.getRecordFileIdList(),
recordFile.getMeetingId(),
recordFile.getSubMeetingId(),
"/save/",
baseSavePath,
Collections.emptyMap(),
tencentAppId,
tencentSdkId,
......@@ -63,7 +76,10 @@ public class FileProcessProducer {
tencentAdminUserId,
meetingInfoMapper,
minioUtils,
emailSender
emailSender,
meetingRecordTemplateMapper,
llmApiAddr,
finalRetry
);
// 提交任务到线程池
......@@ -79,6 +95,35 @@ public class FileProcessProducer {
monitorTaskCompletion(futures);
}
// 批量提交邮箱推送重试任务
public void submitEmailPushTasks(List<TencentMeetingVO.RecordFile> recordFiles, String baseSavePath) {
List<Future<?>> futures = new ArrayList<>();
for (TencentMeetingVO.RecordFile recordFile : recordFiles) {
// 为每个URL创建任务
EmailPushTask task = new EmailPushTask(
recordFile.getMeetingId(),
recordFile.getSubMeetingId(),
baseSavePath,
Collections.emptyMap(),
meetingInfoMapper,
minioUtils,
emailSender,
meetingRecordTemplateMapper
);
// 提交任务到线程池
Future<?> future = fileProcessExecutor.submit(() -> {
task.process();
});
futures.add(future);
}
// 可以添加一个监控线程来检查所有任务完成情况
monitorTaskCompletion(futures);
}
private void monitorTaskCompletion(List<Future<?>> futures) {
new Thread(() -> {
for (Future<?> future : futures) {
......
package com.cmeeting.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmeeting.pojo.MeetingRecordTemplate;
public interface MeetingRecordTemplateService extends IService<MeetingRecordTemplate> {
}
package com.cmeeting.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmeeting.dto.UserDTO;
import com.cmeeting.pojo.TencentMeetingUser;
import com.cmeeting.pojo.UserId;
import com.cmeeting.vo.TencentMeetingVO;
import java.util.List;
......@@ -11,6 +13,10 @@ public interface TecentMeetingService extends IService<TencentMeetingUser> {
void doUsers();
List<TencentMeetingVO.RecordFile> getMeetingFiles();
List<TencentMeetingVO.RecordFile> getMeetingFiles(List<UserDTO> accessUserIds);
List<UserDTO> getAccessUserIds();
String getMeetingHost(String meetingCode);
}
......@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.cmeeting.pojo.WeComUser;
import java.util.List;
import java.util.Map;
public interface WeComService extends IService<WeComUser> {
......@@ -12,4 +13,10 @@ public interface WeComService extends IService<WeComUser> {
void doUsers() throws Exception;
String getToken();
List<WeComUser> noBindUsers();
Map<String, String> createTempMeeting(String wid, String token);
void cancelMeeting(String meetingId, String weComToken);
}
package com.cmeeting.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmeeting.mapper.primary.MeetingRecordTemplateMapper;
import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.service.MeetingRecordTemplateService;
import org.springframework.stereotype.Service;
@Service
public class MeetingRecordTemplateServiceImpl extends ServiceImpl<MeetingRecordTemplateMapper, MeetingRecordTemplate> implements MeetingRecordTemplateService {
}
\ No newline at end of file
......@@ -33,6 +33,16 @@ public class MinioUtils {
}
}
public InputStream getFile(String fileName){
try{
getMinioClient();
InputStream inputStream = minioClient.getObject(bucketName, fileName);
return inputStream;
}catch (Exception e){
throw new RuntimeException(e);
}
}
public InputStream getFile(MinioClient minioClient, String fileName){
try{
InputStream inputStream = minioClient.getObject(bucketName, fileName);
......
......@@ -9,8 +9,6 @@ import com.google.gson.JsonElement;
import java.util.*;
import java.util.stream.Collectors;
import static com.cmeeting.WeComAndTencentMeeting.getUserListByDepartment;
public class WeComUserService {
// 假设这是你的MyBatis Mapper
......@@ -21,23 +19,6 @@ public class WeComUserService {
}
/**
* 处理部门用户数据并存入数据库
*/
public void processAndSaveDepartmentUsers(int departmentId) throws Exception {
// 1. 获取企业微信API数据
JsonObject result = getUserListByDepartment(departmentId);
// 2. 转换为WeComUser集合
List<WeComUser> users = convertJsonToWeComUsers(result);
// 3. 检查重名并设置标志
markDuplicateNames(users);
// 4. 批量插入数据库
weComUserMapper.batchInsert(users);
}
/**
* 将企业微信返回的JSON转换为WeComUser列表
*/
private List<WeComUser> convertJsonToWeComUsers(JsonObject json) {
......
......@@ -6,6 +6,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
public class TencentMeetingVO {
......@@ -30,6 +32,6 @@ public class TencentMeetingVO {
public static class RecordFile{
private String meetingId;//如果是周期会议,这个id表示主会议
private String subMeetingId;//如果是周期会议,这个id表示子会议
private String recordFileId;
private List<String> recordFileIdList;
}
}
\ No newline at end of file
server:
port: 8080
# ????????
# application.yml
# spring.datasource.url=jdbc:mysql://192.168.10.155:3306/cmeeting?useSSL=false&characterEncoding=utf8
# spring.datasource.username=root
# spring.datasource.password=qizhi123
# spring.datasource.driver-class-name=com.mysql.jdbc.Driver
############################################################## minio
MINIO_ADDRESS: http://192.168.10.154:9000
MINIO_BUCKET: zhongji
MINIO_USERNAME: minio
MINIO_PASSWORD: minio123
############################################################## redis
REDIS_ADDRESS: 192.168.10.154
REDIS_PORT: 6380
REDIS_PASS: standard123
REDIS_DATABASE: 8
############################################################## llm
# local
LLM_API_ADDR: https://bedrock.chatbot.cn
# prod
#LLM_API_ADDR: http://10.56.1.150:8000
############################################################## tencent meeting
# local
#TENCENT_APPID: 211153201
#TENCENT_SDKID: 28370276340
#TENCENT_SECRETID: BKOMDZVbvh0iT7k6UHsSizAWBCOVDtT6
#TENCENT_SECRETKEY: 3Y1j0mzNp7KChKFJGyaEnZHLobFoAQ8eLwfaMx8nLbtXAerO
#TENCENT_ADMIN_USERID: woaJARCQAAhkyWGuf8n9InhZsxQstjjA
#TENCENT_ADMIN_USERID: woaJARCQAAJU1EsO73Ww5rn8YHMW6iYA
# prod
TENCENT_APPID: 210468336
TENCENT_SDKID: 28790143843
TENCENT_SECRETID: 0ks7u8cgQ8DGVtlYZeRA9TxZCjvUT3oL
TENCENT_SECRETKEY: gQU09rkJjiQfiGcUYdhiKq5Ol6LebXg4w7F7Ol0rwvvdv3Xy
TENCENT_ADMIN_USERID: woaJARCQAAftcvU6GGoOn66rdSZ4IrOA
############################################################## email
EMAIL_SENDER: cmeeting_assistant@cimc.com
EMAIL_SENDER_PWD: scyou@xih45g6@xih4
EMAIL_SMTP_HOST: smtp.office365.com
############################################################## permission
PERMISSION_APPLiCATION_ID: 1928343847335759872
PERMISSION_TENANT_ID: 1928343652791357440
# ?????primary?
#spring.datasource.primary.jdbc-url=jdbc:mysql://192.168.10.154:3307/aigc-zhongji-test?useSSL=false&characterEncoding=utf8&serverTimezone=UTC
##spring.datasource.primary.username=root
##spring.datasource.primary.password=123456
##spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
##
### ?????secondary?
##spring.datasource.secondary.jdbc-url=jdbc:mysql://192.168.10.154:3307/useradmin-zhongji-test?useSSL=false&characterEncoding=utf8&serverTimezone=UTC
##spring.datasource.secondary.username=root
##spring.datasource.secondary.password=123456
##spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
spring:
datasource:
......@@ -34,12 +56,23 @@ spring:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
redis:
database: ${REDIS_DATABASE}
host: ${REDIS_ADDRESS}
jedis:
pool:
max-active: 8
max-idle: 8
max-wait: 30000ms
min-idle: 1
port: ${REDIS_PORT}
timeout: 6000ms
password: ${REDIS_PASS}
# MyBatis ??
# mybatis.mapper-locations=classpath:mapper/primary/*.xml
mybatis.type-aliases-package: com.cmeeting.pojo\
# ??????
mybatis:
configuration:
map-underscore-to-camel-case: true
......@@ -52,19 +85,6 @@ global-config:
db-config:
column-underline: true
#logging:
# level:
# com:
# zaxxer:
# hikari: INFO
############################################################## minIO
MINIO_ADDRESS: http://192.168.10.154:9000
MINIO_BUCKET: zhongji
MINIO_USERNAME: minio
MINIO_PASSWORD: minio123
#Minio服务所在地址
minio.endpoint: ${MINIO_ADDRESS}
#存储桶名称
......@@ -73,33 +93,28 @@ minio.bucketName: ${MINIO_BUCKET}
minio.accessKey: ${MINIO_USERNAME}
#访问的秘钥
minio.secretKey: ${MINIO_PASSWORD}
############################################################## minIO
############################################################## 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
sdkId: 28790143843
secretId: 0ks7u8cgQ8DGVtlYZeRA9TxZCjvUT3oL
secretKey: gQU09rkJjiQfiGcUYdhiKq5Ol6LebXg4w7F7Ol0rwvvdv3Xy
admin.userId: woaJARCQAAftcvU6GGoOn66rdSZ4IrOA
appId: ${TENCENT_APPID}
sdkId: ${TENCENT_SDKID}
secretId: ${TENCENT_SECRETID}
secretKey: ${TENCENT_SECRETKEY}
admin.userId: ${TENCENT_ADMIN_USERID}
meeting:
token: QQZNb7xWQB47MpZF4C2DFAkv8
aesKey: agy6ALUePp34lljWz1uIQWa7yQq3dgxxQNmfaN9GROm
email:
sender: cmeeting_assistant@cimc.com
sender-pwd: scyou@xih45g6@xih4
smtp-host: smtp.office365.com
############################################################## tencent meeting
sender: ${EMAIL_SENDER}
sender-pwd: ${EMAIL_SENDER_PWD}
smtp-host: ${EMAIL_SMTP_HOST}
llm:
api-addr: ${LLM_API_ADDR}
permission:
applicationId: ${PERMISSION_APPLiCATION_ID}
tenantId: ${PERMISSION_TENANT_ID}
logging:
level:
......
......@@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmeeting.mapper.primary.AuthMapper">
<select id="getAuthByTargrtId" resultType="com.cmeeting.pojo.CoreModulePermissions">
<select id="getAuthByTargetId" resultType="com.cmeeting.pojo.CoreModulePermissions">
SELECT
id,
type,
......@@ -16,7 +16,8 @@
FROM
core_module_permissions
WHERE
target_id = #{targetId}
user_type = 2
and target_id = #{targetId}
<if test="tenantId != null and tenantId != ''">
AND tenant_id = #{tenantId}
</if>
......
......@@ -2,16 +2,17 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmeeting.mapper.primary.MeetingInfoMapper">
<insert id="batchInsert" parameterType="list">
INSERT IGNORE INTO cmt_meeting_info (subject, meeting_id, meeting_code, host_id, participant_user_ids, start_time,
end_time, is_generated, email_push_access, is_pushed, sync_time, sub_meeting_id, record_content, record_xml)
INSERT IGNORE INTO cmt_meeting_info (subject, meeting_id, meeting_code, host, participant_users, start_time,
end_time, is_generated, email_push_access, is_pushed, sync_time, sub_meeting_id, record_content, record_xml, generate_retry,
push_retry, record_file_id,email)
VALUES
<foreach collection="meetingSaveList" item="meeting" separator=",">
(
#{meeting.subject},
#{meeting.meetingId},
#{meeting.meetingCode},
#{meeting.hostId},
#{meeting.participantUserIds},
#{meeting.host},
#{meeting.participantUsers},
#{meeting.startTime},
#{meeting.endTime},
#{meeting.isGenerated},
......@@ -20,7 +21,11 @@ end_time, is_generated, email_push_access, is_pushed, sync_time, sub_meeting_id,
#{meeting.syncTime},
#{meeting.subMeetingId},
#{meeting.recordContent},
#{meeting.recordXml}
#{meeting.recordXml},
#{meeting.generateRetry},
#{meeting.pushRetry},
#{meeting.recordFileId},
#{meeting.email}
)
</foreach>
</insert>
......
......@@ -4,9 +4,9 @@
<!-- 批量插入用户ID信息 -->
<insert id="insertUsers" parameterType="List">
INSERT INTO userid (
userName,
Wid,
Tid
user_name,
wid,
tid
) VALUES
<foreach collection="list" item="item" separator=",">
(
......@@ -19,14 +19,14 @@
<!-- 更新 Tid 字段 -->
<update id="updateUser" parameterType="com.cmeeting.pojo.UserId">
UPDATE userid
SET Tid = #{tid}
SET tid = #{tid}
WHERE wid = #{wid}
</update>
<select id="getUsers" resultType="com.cmeeting.pojo.UserId">
select * from userid
</select>
<select id="getWidByTid" resultType="java.lang.String">
select Wid from userid WHERE Tid = #{operatorUserId}
select wid from userid WHERE Tid = #{operatorUserId}
</select>
</mapper>
\ No newline at end of file
......@@ -30,8 +30,10 @@
<select id="getAlluser" resultType="com.cmeeting.pojo.WeComUser">
SELECT * FROM user_wecom
</select>
<select id="getSameName" resultType="com.cmeeting.pojo.WeComUser">
SELECT * FROM user_wecom
WHERE isrepeat_name = "1"
<select id="noBindUsers" resultType="com.cmeeting.pojo.WeComUser">
SELECT t1.*
FROM user_wecom t1
left join userid t2 on t1.user_id = t2.Wid
WHERE t2.id is null
</select>
</mapper>
\ No newline at end of file
......@@ -9,4 +9,31 @@
AND tenant_id = #{tenantId}
LIMIT 1
</select>
<select id="getParentDeptId" resultType="java.lang.String">
SELECT parent_id
FROM sys_user_sync_category
WHERE dept_id = #{deptId}
AND tenant_id = #{tenantId}
LIMIT 1
</select>
<select id="getSubDeptId" resultType="java.lang.String">
SELECT dept_id
FROM sys_user_sync_category
WHERE parent_id = #{deptId}
AND tenant_id = #{tenantId}
</select>
<select id="getUsersByDept" resultType="java.lang.String">
select t1.user_id as wId
from sys_user_sync t1
where t1.dept_id in
<foreach collection="deptIds" item="deptId" separator="," open="(" close=")">
#{deptId}
</foreach>
</select>
<select id="getUserEmail" resultType="com.cmeeting.dto.UserDTO">
select t1.user_id as wId,IFNULL(t1.email,t1.company_email) as email
from sys_user_sync t1
where t1.email is not null
or t1.company_email is not null
</select>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论