提交 34fa2ab7 作者: duanxincheng

纪要重新生成;邮件二次重试逻辑调整;周期会议生成纪要失败逻辑调整;

父级 5cb7fb90
...@@ -220,8 +220,8 @@ public class UserServiceImpl implements UserService { ...@@ -220,8 +220,8 @@ public class UserServiceImpl implements UserService {
private R loginByAD(ApplicationUserVO.Login login) { private R loginByAD(ApplicationUserVO.Login login) {
// AD验证 // AD验证
// boolean auth = iLdapService.authenticate(login.getUsername().trim(), login.getPassword().trim()); boolean auth = iLdapService.authenticate(login.getUsername().trim(), login.getPassword().trim());
if (true) { if (auth) {
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, login.getUsername().trim())); .eq(SysUserSync::getUserId, login.getUsername().trim()));
......
...@@ -5,7 +5,9 @@ import cn.hutool.core.io.IoUtil; ...@@ -5,7 +5,9 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cmeeting.email.EmailSender; import com.cmeeting.email.EmailSender;
import com.cmeeting.mapper.primary.MeetingRecordTemplateMapper;
import com.cmeeting.pojo.MeetingInfo; import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.service.MeetingInfoService; import com.cmeeting.service.MeetingInfoService;
import com.cmeeting.util.MinioUtils; import com.cmeeting.util.MinioUtils;
import com.cmeeting.util.R; import com.cmeeting.util.R;
...@@ -16,6 +18,7 @@ import com.fasterxml.jackson.databind.JsonNode; ...@@ -16,6 +18,7 @@ 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;
...@@ -50,6 +53,8 @@ public class MeetingInfoController { ...@@ -50,6 +53,8 @@ public class MeetingInfoController {
private MinioUtils minioUtils; private MinioUtils minioUtils;
@Resource @Resource
private EmailSender emailSender; private EmailSender emailSender;
@Resource
private MeetingRecordTemplateMapper meetingRecordTemplateMapper;
@PostMapping("/updateRecordXml") @PostMapping("/updateRecordXml")
public R updateRecordXml(@RequestBody MeetingInfoVO vo) { public R updateRecordXml(@RequestBody MeetingInfoVO vo) {
...@@ -57,6 +62,17 @@ public class MeetingInfoController { ...@@ -57,6 +62,17 @@ public class MeetingInfoController {
return R.ok(save); return R.ok(save);
} }
/**
* 重新生成
* @param vo
* @return
*/
@PostMapping("/regenerateXml")
public R regenerateXml(@RequestBody MeetingInfoVO vo) {
boolean isSuccess = meetingInfoService.regenerateXml(vo);
return R.ok(isSuccess);
}
@PostMapping("/list") @PostMapping("/list")
public R list(@RequestBody MeetingInfoVO vo) { public R list(@RequestBody MeetingInfoVO vo) {
IPage<MeetingInfo> page = meetingInfoService.getPage(vo); IPage<MeetingInfo> page = meetingInfoService.getPage(vo);
...@@ -76,10 +92,21 @@ public class MeetingInfoController { ...@@ -76,10 +92,21 @@ public class MeetingInfoController {
String json = convertXmlToJSON(xml); String json = convertXmlToJSON(xml);
vo.setRecordJson(json); vo.setRecordJson(json);
} }
} catch (Exception e) {
e.printStackTrace();
// try(InputStream is = minioUtils.getFile(recordXml)){
// byte[] bytes = IOUtils.toByteArray(is);
// String json = convertXmlToJSON(new String(bytes));
// vo.setRecordJson(json);
// }catch (Exception ex){
// e.printStackTrace();
// }
}
try {
if(StringUtils.isNotEmpty(recordContent)){ if(StringUtils.isNotEmpty(recordContent)){
vo.setRecordContent(minioUtils.getFileText(recordContent)); vo.setRecordContent(minioUtils.getFileText(recordContent));
} }
} catch (IOException e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
return R.ok(vo); return R.ok(vo);
...@@ -98,7 +125,6 @@ public class MeetingInfoController { ...@@ -98,7 +125,6 @@ public class MeetingInfoController {
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf8")); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf8"));
XWPFTemplate template;
try (XWPFDocument document = new XWPFDocument(); OutputStream os = response.getOutputStream()) { try (XWPFDocument document = new XWPFDocument(); OutputStream os = response.getOutputStream()) {
// 将文本按行分割并逐行添加到 Word 文档 // 将文本按行分割并逐行添加到 Word 文档
String[] lines = content.split("\\r?\\n"); String[] lines = content.split("\\r?\\n");
...@@ -140,13 +166,13 @@ public class MeetingInfoController { ...@@ -140,13 +166,13 @@ public class MeetingInfoController {
participantsMap.put("meeting_host",meetingInfo.getHost()); participantsMap.put("meeting_host",meetingInfo.getHost());
dataModel.putAll(participantsMap); dataModel.putAll(participantsMap);
ClassPathResource resource = new ClassPathResource("template/data_network.docx"); MeetingRecordTemplate meetingRecordTemplate = meetingRecordTemplateMapper.selectById(2);
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");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf8")); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf8"));
XWPFTemplate template; XWPFTemplate template;
try (InputStream is = resource.getInputStream();OutputStream os = response.getOutputStream()) { try (InputStream is = minioUtils.getFile(meetingRecordTemplate.getTemplate());OutputStream os = response.getOutputStream()) {
template = XWPFTemplate.compile(is).render(dataModel); template = XWPFTemplate.compile(is).render(dataModel);
template.write(os); template.write(os);
} catch (IOException e) { } catch (IOException e) {
...@@ -179,10 +205,10 @@ public class MeetingInfoController { ...@@ -179,10 +205,10 @@ public class MeetingInfoController {
participantsMap.put("meeting_host",meetingInfo.getHost()); participantsMap.put("meeting_host",meetingInfo.getHost());
dataModel.putAll(participantsMap); dataModel.putAll(participantsMap);
ClassPathResource resource = new ClassPathResource("template/data_network.docx"); MeetingRecordTemplate meetingRecordTemplate = meetingRecordTemplateMapper.selectById(2);
XWPFTemplate template; XWPFTemplate template;
byte[] meetingMinutesBytes; byte[] meetingMinutesBytes;
try (InputStream is = resource.getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream()) { try (InputStream is = minioUtils.getFile(meetingRecordTemplate.getTemplate());ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
template = XWPFTemplate.compile(is).render(dataModel); template = XWPFTemplate.compile(is).render(dataModel);
template.write(baos); template.write(baos);
meetingMinutesBytes = baos.toByteArray(); meetingMinutesBytes = baos.toByteArray();
......
...@@ -174,8 +174,8 @@ public class EmailSender { ...@@ -174,8 +174,8 @@ public class EmailSender {
AtomicInteger retryCount = new AtomicInteger(0); AtomicInteger retryCount = new AtomicInteger(0);
boolean isSent = false; boolean isSent = false;
// String toEmail = emailPushBuilder.getToEmail(); String toEmail = emailPushBuilder.getToEmail();
String toEmail = "duanxincheng@chatbot.cn"; // String toEmail = "duanxincheng@chatbot.cn";
String subject = emailPushBuilder.getSubject(); String subject = emailPushBuilder.getSubject();
String meetingId = emailPushBuilder.getMeetingId(); String meetingId = emailPushBuilder.getMeetingId();
if(StringUtils.isEmpty(toEmail)){ if(StringUtils.isEmpty(toEmail)){
......
...@@ -92,7 +92,8 @@ public class CmeetingJob { ...@@ -92,7 +92,8 @@ public class CmeetingJob {
log.info("-------关联企微腾会人员定时任务结束--------"); log.info("-------关联企微腾会人员定时任务结束--------");
} }
// @Scheduled(fixedRate = 10 * 60 * 1000,initialDelay = 2 * 60 * 1000) // @Scheduled(fixedRate = 20 * 60 * 1000,initialDelay = 2 * 60 * 1000)
@Scheduled(fixedRate = 20 * 60 * 1000)
public void execute() { public void execute() {
// 定义时间格式化器 // 定义时间格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
...@@ -133,7 +134,7 @@ public class CmeetingJob { ...@@ -133,7 +134,7 @@ public class CmeetingJob {
* 定时扫描早于一小时之前的,所有未重试过的会议,重新生成纪要 * 定时扫描早于一小时之前的,所有未重试过的会议,重新生成纪要
*/ */
// @Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 10 * 60 * 1000) // @Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 10 * 60 * 1000)
@Scheduled(fixedRate = 30 * 60 * 1000) // @Scheduled(fixedRate = 30 * 60 * 1000)
public void meetingMinutesRetry() { public void meetingMinutesRetry() {
try { try {
log.info("-------生成纪要重试定时任务开始-------"); log.info("-------生成纪要重试定时任务开始-------");
...@@ -173,7 +174,8 @@ public class CmeetingJob { ...@@ -173,7 +174,8 @@ public class CmeetingJob {
/** /**
* 定时扫描早于一小时之前的,所有邮件推送未重试过的会议,重新推送邮件 * 定时扫描早于一小时之前的,所有邮件推送未重试过的会议,重新推送邮件
*/ */
@Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 15 * 60 * 1000) // @Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 15 * 60 * 1000)
// @Scheduled(fixedRate = 30 * 60 * 1000)
public void emailPushRetry() { public void emailPushRetry() {
try { try {
log.info("-------邮件推送重试定时任务开始-------"); log.info("-------邮件推送重试定时任务开始-------");
...@@ -184,6 +186,8 @@ public class CmeetingJob { ...@@ -184,6 +186,8 @@ public class CmeetingJob {
meetingInfoService.list(new LambdaQueryWrapper<MeetingInfo>() meetingInfoService.list(new LambdaQueryWrapper<MeetingInfo>()
.eq(MeetingInfo::getIsGenerated,Boolean.TRUE) .eq(MeetingInfo::getIsGenerated,Boolean.TRUE)
.eq(MeetingInfo::getEmailPushAccess,Boolean.TRUE) .eq(MeetingInfo::getEmailPushAccess,Boolean.TRUE)
.eq(MeetingInfo::getIsPushed,Boolean.FALSE)
.eq(MeetingInfo::getPushRetry,Boolean.FALSE)
.le(MeetingInfo::getSyncTime,LocalDateTime.now().minusHours(1)) .le(MeetingInfo::getSyncTime,LocalDateTime.now().minusHours(1))
); );
......
...@@ -51,10 +51,7 @@ import java.nio.file.Files; ...@@ -51,10 +51,7 @@ import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.Instant; import java.time.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
...@@ -101,46 +98,72 @@ public class FileProcessTask { ...@@ -101,46 +98,72 @@ public class FileProcessTask {
MeetingInfo meetingInfo = meetingInfoMapper.selectOne(new LambdaQueryWrapper<MeetingInfo>() MeetingInfo meetingInfo = meetingInfoMapper.selectOne(new LambdaQueryWrapper<MeetingInfo>()
.eq(MeetingInfo::getMeetingId,meetingId) .eq(MeetingInfo::getMeetingId,meetingId)
.eq(subMeetingId != null, MeetingInfo::getSubMeetingId, subMeetingId)); .eq(subMeetingId != null, MeetingInfo::getSubMeetingId, subMeetingId));
String meetingDate = meetingInfo.getStartTime().toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE);
//下面再查一遍会议信息的意义是,为了获取会议中的子会议Id,如果是周期会议,需要保存下来(重要) //下面再查一遍会议信息的意义是,为了获取会议中的子会议Id,如果是周期会议,需要保存下来(重要)
MeetingsApi.ApiV1MeetingsMeetingIdGetRequest meetingRequest = // MeetingsApi.ApiV1MeetingsMeetingIdGetRequest meetingRequest =
new MeetingsApi.ApiV1MeetingsMeetingIdGetRequest.Builder(meetingId) // new MeetingsApi.ApiV1MeetingsMeetingIdGetRequest.Builder(meetingId)
.operatorId(tencentAdminUserId) // .operatorId(tencentAdminUserId)
.operatorIdType("1") // .operatorIdType("1")
.instanceid("0") // .instanceid("0")
.build(); // .build();
MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse = // MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse =
client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder() // client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder()
.nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt()))) // .nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
.timestamp(String.valueOf(System.currentTimeMillis() / 1000L))); // .timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
V1MeetingsMeetingIdGet200Response meetingResponseData = meetingResponse.getData(); // V1MeetingsMeetingIdGet200Response meetingResponseData = meetingResponse.getData();
List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner> meetingInfoList = meetingResponseData.getMeetingInfoList(); // List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner> meetingInfoList = meetingResponseData.getMeetingInfoList();
V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner meetingItem = meetingInfoList.get(0); // V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner meetingItem = meetingInfoList.get(0);
//会议类型 // //会议类型
//0:一次性会议 // //0:一次性会议
//1:周期性会议 // //1:周期性会议
Long meetingType = meetingItem.getMeetingType(); // Long meetingType = meetingItem.getMeetingType();
if(meetingType.intValue() == 1){ // if(meetingType.intValue() == 1){
//如果是周期会议,获取子会议的ID,用于查询参会人员 // //如果是周期会议,获取子会议的ID,用于查询参会人员
List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeetings = meetingItem.getSubMeetings(); // List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeetings = meetingItem.getSubMeetings();
//如果主持人突然取消了后续的所有周期会议,subMeetings列表会返回null // //如果主持人突然取消了后续的所有周期会议,subMeetings列表会返回null
if(!CollectionUtils.isEmpty(subMeetings)){ // if(!CollectionUtils.isEmpty(subMeetings)){
LocalDate meetingStartDate = meetingInfo.getStartTime().toLocalDate(); // LocalDate meetingStartDate = meetingInfo.getStartTime().toLocalDate();
Optional<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeeting = subMeetings.stream().filter(item -> //// long meetingDayStartTimeStamp = meetingStartDate.atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond();
Instant.ofEpochSecond(Long.valueOf(item.getStartTime())).atZone(ZoneId.systemDefault()).toLocalDate().equals(meetingStartDate)) //// long meetingDayEndTimeStamp = meetingStartDate.plusDays(1).atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond();
.findFirst(); // Optional<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeeting = subMeetings.stream().filter(item ->
if(!subMeeting.isPresent()){ // Instant.ofEpochSecond(Long.valueOf(item.getStartTime())).atZone(ZoneId.systemDefault()).toLocalDate().equals(meetingStartDate))
log.error("周期会议"+meetingId+"未知子会议ID"); // .findFirst();
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"未知子会议ID"); // if(!subMeeting.isPresent()){
retryCount++; // log.error("账户级周期会议"+meetingId+"未知子会议ID");
continue; // retryCount++;
} // continue;
subMeetingId = subMeeting.get().getSubMeetingId(); //// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"未知子会议ID");
}else{ // //会议详情查不到历史的子会议id了,需要到用户已结束会议列表查一下
subMeetingId = meetingItem.getCurrentSubMeetingId(); //// MeetingsApi.ApiV1HistoryMeetingsUseridGetRequest completedMeetingRequest =
log.info("周期会议"+meetingId+"的子会议列表为空,获取当前子会议号"); //// new MeetingsApi.ApiV1HistoryMeetingsUseridGetRequest.Builder(tencentAdminUserId)
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"的子会议列表为空,获取当前子会议号"); //// .pageSize("20")
} //// .page("1")
} //// .meetingCode(meetingInfo.getMeetingCode())
//// .startTime(String.valueOf(meetingDayStartTimeStamp))
//// .endTime(String.valueOf(meetingDayEndTimeStamp))
//// .build();
//// MeetingsApi.ApiV1HistoryMeetingsUseridGetResponse completedMeetingResponse =
//// client.meetings().v1HistoryMeetingsUseridGet(completedMeetingRequest, new JWTAuthenticator.Builder()
//// .nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
//// .timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
////
//// V1HistoryMeetingsUseridGet200Response completedMeetingData = completedMeetingResponse.getData();
//// List<V1HistoryMeetingsUseridGet200ResponseMeetingInfoListInner> completedMeetingList = completedMeetingData.getMeetingInfoList();
//// if(CollectionUtils.isEmpty(completedMeetingList)){
//// log.error("账户级周期会议"+meetingId+"未知子会议ID");
//// retryCount++;
//// continue;
//// }
//// subMeetingId = completedMeetingList.get(0).getSubMeetingId();
// }else{
// subMeetingId = subMeeting.get().getSubMeetingId();
// }
// }else{
// subMeetingId = meetingItem.getCurrentSubMeetingId();
// log.info("周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
//// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
// }
// }
// 获取参会成员明细 // 获取参会成员明细
MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest = MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest =
...@@ -219,7 +242,7 @@ public class FileProcessTask { ...@@ -219,7 +242,7 @@ public class FileProcessTask {
log.info("choiceTemplateType->{}",choiceTemplateType); log.info("choiceTemplateType->{}",choiceTemplateType);
//获取系统模板 //获取系统模板
MeetingRecordTemplate meetingRecordTemplate = meetingRecordTemplateMapper.selectById(1); MeetingRecordTemplate meetingRecordTemplate = meetingRecordTemplateMapper.selectById(1);
String processedResult = processWithClaude(recordTextBuffer.toString(),meetingRecordTemplate.getPrompt()); String processedResult = processWithClaude(recordTextBuffer.toString(),meetingDate,meetingRecordTemplate.getPrompt());
// 4. 保存结果 // 4. 保存结果
MeetingRecordTemplate meetingRecordTemplate2 = meetingRecordTemplateMapper.selectById(2); MeetingRecordTemplate meetingRecordTemplate2 = meetingRecordTemplateMapper.selectById(2);
...@@ -236,7 +259,7 @@ public class FileProcessTask { ...@@ -236,7 +259,7 @@ public class FileProcessTask {
FileUtil.del(dataNetworkMinutesPath); FileUtil.del(dataNetworkMinutesPath);
} }
String processedResult2 = processWithClaude(recordTextBuffer.toString(),meetingRecordTemplate2.getPrompt()); String processedResult2 = processWithClaude(recordTextBuffer.toString(),meetingDate,meetingRecordTemplate2.getPrompt());
String groupOfficeMinutesPath = saveResult(processedResult2, recordTextBuffer.toString().getBytes(StandardCharsets.UTF_8), meetingInfo, meetingRecordTemplate2); String groupOfficeMinutesPath = saveResult(processedResult2, recordTextBuffer.toString().getBytes(StandardCharsets.UTF_8), meetingInfo, meetingRecordTemplate2);
try(InputStream is = new FileInputStream(groupOfficeMinutesPath)){ try(InputStream is = new FileInputStream(groupOfficeMinutesPath)){
byte[] meetingMinutesBytes = IOUtils.toByteArray(is); byte[] meetingMinutesBytes = IOUtils.toByteArray(is);
...@@ -351,18 +374,19 @@ public class FileProcessTask { ...@@ -351,18 +374,19 @@ public class FileProcessTask {
* @param prompt 提示词 * @param prompt 提示词
* @return * @return
*/ */
private String processWithClaude(String textContent, String prompt) { private String processWithClaude(String textContent, String meetingDate, String prompt) {
//将文件传送给大模型处理 //将文件传送给大模型处理
String token = "AKIAXFAXF62IWJXGLVEE.LnKInaahcMZG9zLsGMH3nTLOw3S3lK5Vcu0+ifnO"; String token = "AKIAXFAXF62IWJXGLVEE.LnKInaahcMZG9zLsGMH3nTLOw3S3lK5Vcu0+ifnO";
String apiAddr = llmApiAddr + "/llm/sse-invoke"; String apiAddr = llmApiAddr + "/llm/sse-invoke";
String model = "anthropic.claude-3-5-sonnet-20240620-v1:0"; String model = "anthropic.claude-3-5-sonnet-20240620-v1:0";
int maxTokens = 5000; int maxTokens = 5000;
List<Message> messages = new ArrayList<>(); List<Message> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), prompt);
messages.add(chatMessage); ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), prompt.replaceAll("\\{transcript\\}",textContent).replaceAll("\\{meetingDate\\}",meetingDate));
chatMessage = new ChatMessage(ChatMessageRole.ASSISTANT.value(), "好的请提供会议记录");
messages.add(chatMessage); messages.add(chatMessage);
chatMessage = new ChatMessage(ChatMessageRole.USER.value(), textContent); // chatMessage = new ChatMessage(ChatMessageRole.ASSISTANT.value(), "好的请提供会议记录");
// messages.add(chatMessage);
// chatMessage = new ChatMessage(ChatMessageRole.USER.value(), textContent);
messages.add(chatMessage); messages.add(chatMessage);
......
...@@ -2,6 +2,7 @@ package com.cmeeting.mapper.primary; ...@@ -2,6 +2,7 @@ package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.pojo.MeetingInfo; import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.vo.TencentMeetingVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
...@@ -12,6 +13,6 @@ import java.util.List; ...@@ -12,6 +13,6 @@ import java.util.List;
public interface MeetingInfoMapper extends BaseMapper<MeetingInfo> { public interface MeetingInfoMapper extends BaseMapper<MeetingInfo> {
void batchInsert(@Param("meetingSaveList")List<MeetingInfo> meetingSaveList); void batchInsert(@Param("meetingSaveList")List<MeetingInfo> meetingSaveList);
List<String> getAllMeetingIds(); List<TencentMeetingVO.SimpleMeetingInfo> getAllMeetingIds();
} }
\ No newline at end of file
...@@ -9,4 +9,6 @@ public interface MeetingInfoService extends IService<MeetingInfo> { ...@@ -9,4 +9,6 @@ public interface MeetingInfoService extends IService<MeetingInfo> {
IPage<MeetingInfo> getPage(MeetingInfoVO vo); IPage<MeetingInfo> getPage(MeetingInfoVO vo);
boolean updateRecordXml(MeetingInfoVO vo); boolean updateRecordXml(MeetingInfoVO vo);
boolean regenerateXml(MeetingInfoVO vo);
} }
package com.cmeeting.service.impl; package com.cmeeting.service.impl;
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.io.FileUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
...@@ -9,30 +15,76 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; ...@@ -9,30 +15,76 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmeeting.log.service.ProcessLogService;
import com.cmeeting.mapper.primary.MeetingInfoMapper; import com.cmeeting.mapper.primary.MeetingInfoMapper;
import com.cmeeting.mapper.primary.MeetingRecordTemplateMapper;
import com.cmeeting.pojo.MeetingInfo; import com.cmeeting.pojo.MeetingInfo;
import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.service.MeetingInfoService; import com.cmeeting.service.MeetingInfoService;
import com.cmeeting.util.MinioUtils; import com.cmeeting.util.MinioUtils;
import com.cmeeting.vo.EmailPush;
import com.cmeeting.vo.MeetingInfoVO; import com.cmeeting.vo.MeetingInfoVO;
import com.deepoove.poi.XWPFTemplate;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.tencentcloudapi.wemeet.Client;
import com.tencentcloudapi.wemeet.core.authenticator.AuthenticatorBuilder;
import com.tencentcloudapi.wemeet.core.authenticator.JWTAuthenticator;
import com.tencentcloudapi.wemeet.service.meetings.api.MeetingsApi;
import com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200Response;
import com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner;
import com.tencentcloudapi.wemeet.service.records.api.RecordsApi;
import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200Response;
import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200ResponseAiMeetingTranscriptsInner;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.*;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashMap; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.*;
import java.util.Map; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Service @Service
@Slf4j
public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, MeetingInfo> implements MeetingInfoService { public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, MeetingInfo> implements MeetingInfoService {
@Resource @Resource
private MeetingInfoMapper mapper; private MeetingInfoMapper mapper;
@Resource @Resource
private MinioUtils minioUtils; private MinioUtils minioUtils;
@Value(value = "${tencent.appId}")
private String tencentAppId;
@Value(value = "${tencent.sdkId}")
private String tencentSdkId;
@Value(value = "${tencent.secretId}")
private String tencentSecretId;
@Value(value = "${tencent.secretKey}")
private String tencentSecretKey;
@Value(value = "${tencent.admin.userId}")
private String tencentAdminUserId;
@Value(value = "${llm.api-addr}")
private String llmApiAddr;
@Value(value = "${tencent.base-save-path}")
private String baseSavePath;
@Resource
private ProcessLogService processLogService;
@Resource
private MeetingRecordTemplateMapper meetingRecordTemplateMapper;
@Resource
private MeetingInfoMapper meetingInfoMapper;
@Override @Override
public IPage<MeetingInfo> getPage(MeetingInfoVO vo) { public IPage<MeetingInfo> getPage(MeetingInfoVO vo) {
...@@ -57,6 +109,254 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti ...@@ -57,6 +109,254 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti
return update(null,updateWrapper); return update(null,updateWrapper);
} }
@Override
public boolean regenerateXml(MeetingInfoVO vo) {
Client client = new Client.Builder()
.withAppId(tencentAppId).withSdkId(tencentSdkId)
.withSecret(tencentSecretId,tencentSecretKey)
.build();
MeetingInfo meetingInfo = mapper.selectById(vo.getId());
//已保存的会议信息
String meetingId = meetingInfo.getMeetingId();
String subMeetingId = meetingInfo.getSubMeetingId();
String recordFileIdArray = meetingInfo.getRecordFileId();
try{
if(StringUtils.isEmpty(recordFileIdArray)){
return false;
}
String meetingDate = meetingInfo.getStartTime().toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE);
// 获取参会成员明细
MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest =
new MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest
.Builder(meetingId).subMeetingId(subMeetingId).operatorId(tencentAdminUserId).operatorIdType("1").build();
AuthenticatorBuilder<JWTAuthenticator> participantsAuthenticatorBuilder =
new JWTAuthenticator.Builder()
.nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
.timestamp(String.valueOf(System.currentTimeMillis() / 1000L));
MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetResponse participantsResponse =
client.meetings().v1MeetingsMeetingIdParticipantsGet(participantsRequest, participantsAuthenticatorBuilder);
V1MeetingsMeetingIdParticipantsGet200Response participantsData = participantsResponse.getData();
List<V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner> participants = participantsData.getParticipants();
String participantNames = participants.stream().map(item -> new String(Base64.getDecoder().decode(item.getUserName()))).distinct().collect(Collectors.joining("、"));
meetingInfo.setParticipantUsers(participantNames);
//每场会议可能会分段录制,查出每个文件的转录记录后拼接
StringBuffer recordTextBuffer = new StringBuffer();
List<String> recordFileIdList = Arrays.asList(meetingInfo.getRecordFileId().split(","));
for (String recordFileId : recordFileIdList) {
//查询录制转写详情
RecordsApi.ApiV1AddressesRecordFileIdGetRequest addressRequest =
new RecordsApi.ApiV1AddressesRecordFileIdGetRequest.Builder(recordFileId)
.operatorId(tencentAdminUserId)
.operatorIdType("1")
.build();
RecordsApi.ApiV1AddressesRecordFileIdGetResponse addressResponse =
client.records().v1AddressesRecordFileIdGet(addressRequest,
new JWTAuthenticator.Builder().nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
.timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
// 处理响应
if (addressResponse != null && addressResponse.getData() != null) {
log.info("Successfully got address for record file {}", recordFileId);
V1AddressesRecordFileIdGet200Response addressData = addressResponse.getData();
// 获取AI会议转录文件
List<V1AddressesRecordFileIdGet200ResponseAiMeetingTranscriptsInner> transcripts =
addressData.getAiMeetingTranscripts();
if (transcripts != null && !transcripts.isEmpty()) {
log.info("Found {} AI meeting transcripts for record file {}",
transcripts.size(), recordFileId);
// 处理每个转录文件
for (V1AddressesRecordFileIdGet200ResponseAiMeetingTranscriptsInner transcript : transcripts) {
String fileType = transcript.getFileType();
String downloadUrl = transcript.getDownloadAddress();
if ("txt".equalsIgnoreCase(fileType)) {
log.info("AI Transcript - Type: {}, URL: {}", fileType, downloadUrl);
// 1. 下载文件
byte[] fileData = downloadFile(downloadUrl);
// 2. 将二进制文件转换为文本
String recordTextContent = new String(fileData);
if(StringUtils.isNotEmpty(recordTextContent.replaceAll("\\n","").trim())){
recordTextBuffer.append("\n\n");
recordTextBuffer.append(recordTextContent);
}
}
}
} else {
log.info("No AI meeting transcripts found for record file {}", recordFileId);
}
} else {
log.warn("Empty response for record file: {}", recordFileId);
}
}
if(StringUtils.isEmpty(recordTextBuffer.toString().replaceAll("\\n","").trim())){
log.info("获取的转录文本为空,跳过纪要生成,meetingId:{},fileRecordId:{}",meetingId,recordFileIdList.toString());
processLogService.log(meetingId,subMeetingId,"获取的转录文本为空,跳过纪要生成");
throw new RuntimeException("获取的转录文本为空,跳过纪要生成");
}
//获取系统模板
MeetingRecordTemplate meetingRecordTemplate = meetingRecordTemplateMapper.selectById(2);
String processedResult = processWithClaude(recordTextBuffer.toString(),meetingDate,meetingRecordTemplate.getPrompt(),meetingId,subMeetingId);
String groupOfficeMinutesPath = saveResult(processedResult, recordTextBuffer.toString().getBytes(StandardCharsets.UTF_8), meetingInfo, meetingRecordTemplate);
try(InputStream is = new FileInputStream(groupOfficeMinutesPath)){
byte[] meetingMinutesBytes = IOUtils.toByteArray(is);
minioUtils.upload(groupOfficeMinutesPath,meetingMinutesBytes);
meetingInfoMapper.update(meetingInfo,
new LambdaUpdateWrapper<MeetingInfo>()
.eq(MeetingInfo::getMeetingId,meetingId)
.eq(subMeetingId != null,MeetingInfo::getSubMeetingId,subMeetingId)
.set(MeetingInfo::getRecordXml,groupOfficeMinutesPath));
}catch (Exception e){
throw new RuntimeException(e);
}finally {
FileUtil.del(groupOfficeMinutesPath);
}
return true;
}catch (Exception e){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
processLogService.log(meetingId,subMeetingId,sw.toString());
return false;
}
}
/**
* 保存会议纪要相关的文件
* @param content 大模型返回的不规则xml
* @param recordData 转录文本
* @param meetingInfo 会议对象
* @param meetingRecordTemplate 模板信息
*/
private String saveResult(String content, byte[] recordData, MeetingInfo meetingInfo,MeetingRecordTemplate meetingRecordTemplate) {
String meetingName;
String meetingId = meetingInfo.getMeetingId();
String subMeetingId = meetingInfo.getSubMeetingId();
//转录文件临时存储路径
String recordContentPath = meetingId + "-recordContent-" + IdUtil.fastSimpleUUID() + ".txt";
//生成的xml临时存储路径
String recordXmlPath = meetingId + "-recordXmlPath-" + IdUtil.fastSimpleUUID() + ".xml";
//填充后的会议纪要名称
String meetingMinutesFileName;
//填充后的会议纪要word文件临时路径
String meetingMinutesPath;
try {
//去除内容中除了xml内容以外其他的信息,格式化xml
String xml = extractXmlFromMarkdown(content, meetingId, subMeetingId);
minioUtils.upload(recordContentPath,recordData);
minioUtils.upload(recordXmlPath,xml.getBytes(StandardCharsets.UTF_8));
//将xml格式的内容转换为map,用于填充模板
Map<String, Object> dataModel = convertXmlToMap(xml);
meetingName = dataModel.get("meeting_name") != null ? String.valueOf(dataModel.get("meeting_name")) : "腾讯会议纪要";
meetingMinutesFileName = meetingName + "_" + meetingRecordTemplate.getName();
//追加参会人员信息
Map<String,Object> participantsMap = new ConcurrentHashMap<>();
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String meetingDate = meetingInfo.getStartTime().toLocalDate().format(df);
participantsMap.put("meeting_date",meetingDate);
participantsMap.put("meeting_location","线上腾讯会议");
participantsMap.put("meeting_participants", meetingInfo.getParticipantUsers());
participantsMap.put("meeting_host",meetingInfo.getHost());
dataModel.putAll(participantsMap);
XWPFTemplate template;
try (InputStream inputStream = minioUtils.getFile(meetingRecordTemplate.getTemplate())) {
template = XWPFTemplate.compile(inputStream).render(dataModel);
} catch (IOException e) {
throw new RuntimeException(e);
}
meetingMinutesPath = baseSavePath + meetingMinutesFileName + ".docx";
template.writeAndClose(new FileOutputStream(meetingMinutesPath));
processLogService.log(meetingId,subMeetingId,"填充会议纪要成功");
} catch (Exception e) {
log.error("填充会议纪要失败: {}", e.getMessage(), e);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
processLogService.log(meetingId,subMeetingId,"填充会议纪要失败"+sw.toString());
throw new RuntimeException("填充会议纪要失败");
}
meetingInfoMapper.update(meetingInfo,
new LambdaUpdateWrapper<MeetingInfo>()
.eq(MeetingInfo::getMeetingId,meetingId)
.eq(subMeetingId != null,MeetingInfo::getSubMeetingId,subMeetingId)
.set(MeetingInfo::getRecordContent,recordContentPath)
.set(MeetingInfo::getRecordXml,recordXmlPath)
.set(MeetingInfo::getParticipantUsers,meetingInfo.getParticipantUsers())
.set(MeetingInfo::getIsGenerated,Boolean.TRUE)
);
meetingInfo.setRecordContent(recordContentPath);
meetingInfo.setRecordXml(recordXmlPath);
return baseSavePath + meetingMinutesFileName + ".docx";
}
/**
* 大模型生成纪要xml
* @param textContent 转录文件
* @param prompt 提示词
* @return
*/
private String processWithClaude(String textContent, String meetingDate, String prompt,String meetingId, String subMeetingId) {
//将文件传送给大模型处理
String token = "AKIAXFAXF62IWJXGLVEE.LnKInaahcMZG9zLsGMH3nTLOw3S3lK5Vcu0+ifnO";
String apiAddr = llmApiAddr + "/llm/sse-invoke";
String model = "anthropic.claude-3-5-sonnet-20240620-v1:0";
int maxTokens = 5000;
List<Message> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), prompt.replaceAll("\\{transcript\\}",textContent).replaceAll("\\{meetingDate\\}",meetingDate));
messages.add(chatMessage);
// chatMessage = new ChatMessage(ChatMessageRole.ASSISTANT.value(), "好的请提供会议记录");
// messages.add(chatMessage);
// chatMessage = new ChatMessage(ChatMessageRole.USER.value(), textContent);
messages.add(chatMessage);
// 调用Claude API处理文件
String ret = call_llm(apiAddr, model, token, messages, maxTokens,meetingId,subMeetingId);
return ret;
}
private String call_llm(String apiAddr, String model, String token, List<Message> messages, int maxTokens,String meetingId, String subMeetingId) {
LLMService service = new LLMService(token, apiAddr);
StringBuilder stringBuilder = new StringBuilder();
try {
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(model)
.messages(messages)
.stream(true)
.n(1)
.maxTokens(maxTokens)
.logitBias(new HashMap<>())
.build();
service.streamChatCompletion(chatCompletionRequest).doOnError(Throwable::printStackTrace).blockingForEach(chunk -> {
chunk.getChoices().stream().map(choice -> choice.getMessage().getContent())
.filter(Objects::nonNull).findFirst().ifPresent(o -> {
try {
stringBuilder.append(new String(o.getBytes(Charset.defaultCharset())));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
});
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
processLogService.log(meetingId,subMeetingId,"【大模型处理异常】:"+sw.toString());
throw new RuntimeException(e);
}
service.shutdownExecutor();
return stringBuilder.toString();
}
private String convertJSONToXml(String json) { private String convertJSONToXml(String json) {
JSONArray jsonArray = JSON.parseArray(json);; JSONArray jsonArray = JSON.parseArray(json);;
StringBuilder xmlBuilder = new StringBuilder(); StringBuilder xmlBuilder = new StringBuilder();
...@@ -76,4 +376,63 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti ...@@ -76,4 +376,63 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti
return xmlBuilder.toString(); return xmlBuilder.toString();
} }
private 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();
//取出正确的value并设置
String realValue = String.valueOf(value.get("")).replaceAll("^\\n+","");
//内容段首移除换行,段末追加换行(会议名称结尾不换行
entry.setValue(realValue.endsWith("\n") || "meeting_name".equals(entry.getKey()) || "meeting_purpose".equals(entry.getKey()) ? realValue : realValue + "\n");
}
return map;
}
/**
* markdown转xml
* @param markdown
* @return
*/
private String extractXmlFromMarkdown(String markdown,String meetingId, String subMeetingId) {
StringBuffer sb;
try {
int start = markdown.indexOf("<");
if(start == -1){
processLogService.log(meetingId,subMeetingId,"markdown转xml失败,未输出正确的xml格式,markdown内容:"+markdown);
}
int end = markdown.lastIndexOf(">") + 1;
sb = new StringBuffer();
sb.append("<root>");
String xml = markdown.substring(start, end).trim().replaceAll("\n\n","\n");
sb.append(xml);
sb.append("</root>");
} catch (Exception e) {
log.info("markdown转xml,markdown->{}",markdown);
throw new RuntimeException(e.getMessage());
}
return sb.toString();
}
private byte[] downloadFile(String url) {
// 实现文件下载逻辑
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
return response.body().bytes();
}catch (Exception e){
throw new RuntimeException("下载文件失败", e);
}
}
} }
...@@ -329,6 +329,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -329,6 +329,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
.withSecret(tencentSecretId,tencentSecretKey) .withSecret(tencentSecretId,tencentSecretKey)
.build(); .build();
List<TencentMeetingVO.RecordFile> meetingFiles = new ArrayList<>();
List<TencentMeetingVO.RecordFile> recordFileUrlList = new ArrayList<>(); List<TencentMeetingVO.RecordFile> recordFileUrlList = new ArrayList<>();
List<MeetingInfo> meetingSaveList = new ArrayList<>(); List<MeetingInfo> meetingSaveList = new ArrayList<>();
// 查询近两天的会议录制列表 // 查询近两天的会议录制列表
...@@ -343,7 +344,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -343,7 +344,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
Integer totalPage = firstData.getTotalPage(); Integer totalPage = firstData.getTotalPage();
//目前已存储的会议id //目前已存储的会议id
List<String> 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){ while (currentPage.intValue() <= totalPage){
...@@ -356,11 +357,8 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -356,11 +357,8 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
//1:录制中 //1:录制中
//2:转码中 //2:转码中
//3:转码完成 //3:转码完成
if(meetings.stream().allMatch(item->item.getState() != 3)){
return null;
}
for (CorpRecordsVO.RecordMeeting meeting : meetings) { for (CorpRecordsVO.RecordMeeting meeting : meetings) {
//会议没结束,跳过 //录制文件未转码完成,跳过
if(meeting.getState() != 3){ if(meeting.getState() != 3){
// processLogService.log(meeting.getMeetingId(),null,"会议未结束,跳过生成"); // processLogService.log(meeting.getMeetingId(),null,"会议未结束,跳过生成");
continue; continue;
...@@ -372,15 +370,36 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -372,15 +370,36 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
//查询会议详情 //查询会议详情
String meetingId = meeting.getMeetingId(); String meetingId = meeting.getMeetingId();
String subMeetingId = null; String subMeetingId = null;
MeetingsApi.ApiV1MeetingsMeetingIdGetRequest meetingRequest = LocalDateTime mediaStartTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.valueOf(meeting.getMediaStartTime())), ZoneId.systemDefault());
new MeetingsApi.ApiV1MeetingsMeetingIdGetRequest.Builder(meetingId)
.operatorId(tencentAdminUserId)
.operatorIdType("1")
.instanceid("0")
.build();
try { try {
log.info("【周期会议扫描】:查询用户的已结束会议列表...userId->{}",meeting.getUserid());
//获取子会议id
MeetingsApi.ApiV1HistoryMeetingsUseridGetRequest historyMeetingRequest =
new MeetingsApi.ApiV1HistoryMeetingsUseridGetRequest.Builder(meeting.getUserid())
.pageSize("20")
.page("1")
.meetingCode(meeting.getMeetingCode())
.startTime(String.valueOf(mediaStartTime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond()))
.endTime(String.valueOf(mediaStartTime.toLocalDate().atStartOfDay().plusDays(1).atZone(ZoneId.systemDefault()).toEpochSecond()))
.build();
MeetingsApi.ApiV1HistoryMeetingsUseridGetResponse historyMeetingResponse =
client.meetings().v1HistoryMeetingsUseridGet(historyMeetingRequest, new JWTAuthenticator.Builder()
.nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt()))).timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
V1HistoryMeetingsUseridGet200Response historyMeetingResponseData = historyMeetingResponse.getData();
List<V1HistoryMeetingsUseridGet200ResponseMeetingInfoListInner> historyMeetingInfoList = historyMeetingResponseData.getMeetingInfoList();
if(CollectionUtils.isEmpty(historyMeetingInfoList)){
log.error("会议未结束,获取子会议id信息失败");
continue;
}
V1HistoryMeetingsUseridGet200ResponseMeetingInfoListInner historyMeeting = historyMeetingInfoList.get(0);
//如果是周期会议
if(historyMeeting.getMeetingType() == 1){
subMeetingId = historyMeeting.getSubMeetingId();
}
//如果数据库中已有相同会议id的记录,跳过同步 //如果数据库中已有相同会议id的记录,跳过同步
if(!meetingIds.contains(meetingId)){ String finalSubMeetingId = subMeetingId;
if(!meetingIds.stream().anyMatch(item->item.getMeetingId().equals(meetingId) && Objects.equals(item.getSubMeetingId(), finalSubMeetingId))){
log.info("【会议检索】新的会议meetingId->{}",meeting.getMeetingId()); log.info("【会议检索】新的会议meetingId->{}",meeting.getMeetingId());
List<CorpRecordsVO.RecordFile> recordFiles = meeting.getRecordFiles(); List<CorpRecordsVO.RecordFile> recordFiles = meeting.getRecordFiles();
//按转录文件时间升序,便于后续的内容拼接 //按转录文件时间升序,便于后续的内容拼接
...@@ -399,6 +418,12 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -399,6 +418,12 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
}else{ }else{
//判断主持人是否存在,如果主持人未参会,是查不到主持人的 //判断主持人是否存在,如果主持人未参会,是查不到主持人的
//如果主持人未参会,使用会议详情中的创建人作为主持人 //如果主持人未参会,使用会议详情中的创建人作为主持人
MeetingsApi.ApiV1MeetingsMeetingIdGetRequest meetingRequest =
new MeetingsApi.ApiV1MeetingsMeetingIdGetRequest.Builder(meetingId)
.operatorId(tencentAdminUserId)
.operatorIdType("1")
.instanceid("0")
.build();
MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse = MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse =
client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder() client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder()
.nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt()))) .nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
...@@ -469,7 +494,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -469,7 +494,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
//会议基本信息保存 //会议基本信息保存
MeetingInfo meetingItem = MeetingInfo.builder().meetingId(meetingId).meetingCode(meeting.getMeetingCode()) MeetingInfo meetingItem = MeetingInfo.builder().meetingId(meetingId).meetingCode(meeting.getMeetingCode())
.subject(meeting.getSubject()) .subject(meeting.getSubject())
.startTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.valueOf(meeting.getMediaStartTime())), ZoneId.systemDefault())) .startTime(mediaStartTime)
// .endTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.valueOf(meeting.getEndTime())), ZoneId.systemDefault())) // .endTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.valueOf(meeting.getEndTime())), ZoneId.systemDefault()))
.isGenerated(Boolean.FALSE).emailPushAccess(Boolean.TRUE).isPushed(Boolean.FALSE).syncTime(LocalDateTime.now()) .isGenerated(Boolean.FALSE).emailPushAccess(Boolean.TRUE).isPushed(Boolean.FALSE).syncTime(LocalDateTime.now())
.subMeetingId(subMeetingId).generateRetry(Boolean.FALSE).pushRetry(Boolean.FALSE) .subMeetingId(subMeetingId).generateRetry(Boolean.FALSE).pushRetry(Boolean.FALSE)
...@@ -497,8 +522,13 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -497,8 +522,13 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
List<MeetingInfo> finalSaveList = new ArrayList<>(); List<MeetingInfo> finalSaveList = new ArrayList<>();
for (Map.Entry<String, List<MeetingInfo>> entry : meetingSaveMap.entrySet()) { for (Map.Entry<String, List<MeetingInfo>> entry : meetingSaveMap.entrySet()) {
MeetingInfo meetingInfo = entry.getValue().get(0); MeetingInfo meetingInfo = entry.getValue().get(0);
meetingInfo.setRecordFileId(entry.getValue().stream().map(MeetingInfo::getRecordFileId).collect(Collectors.joining(","))); List<String> recordFileIdList = entry.getValue().stream().flatMap(s -> Arrays.stream(s.getRecordFileId().split(","))).collect(Collectors.toList());
meetingInfo.setRecordFileId(recordFileIdList.stream().collect(Collectors.joining(",")));
finalSaveList.add(meetingInfo); finalSaveList.add(meetingInfo);
meetingFiles.add(TencentMeetingVO.RecordFile.builder()
.meetingId(meetingInfo.getMeetingId())
.subMeetingId(meetingInfo.getSubMeetingId())
.recordFileIdList(recordFileIdList).build());
} }
meetingInfoMapper.batchInsert(finalSaveList); meetingInfoMapper.batchInsert(finalSaveList);
} }
...@@ -510,7 +540,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -510,7 +540,7 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
processLogService.log(null,null,sw.toString()); processLogService.log(null,null,sw.toString());
throw new RuntimeException(e.getMessage()); throw new RuntimeException(e.getMessage());
} }
return recordFileUrlList; return meetingFiles;
} }
/** /**
......
...@@ -34,4 +34,13 @@ public class TencentMeetingVO { ...@@ -34,4 +34,13 @@ public class TencentMeetingVO {
private String subMeetingId;//如果是周期会议,这个id表示子会议 private String subMeetingId;//如果是周期会议,这个id表示子会议
private List<String> recordFileIdList; private List<String> recordFileIdList;
} }
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class SimpleMeetingInfo{
private String meetingId;//如果是周期会议,这个id表示主会议
private String subMeetingId;//如果是周期会议,这个id表示子会议
}
} }
\ No newline at end of file
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
) )
</foreach> </foreach>
</insert> </insert>
<select id="getAllMeetingIds" resultType="java.lang.String"> <select id="getAllMeetingIds" resultType="com.cmeeting.vo.TencentMeetingVO$SimpleMeetingInfo">
select meeting_id from cmt_meeting_info select meeting_id,sub_meeting_id from cmt_meeting_info
</select> </select>
</mapper> </mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论