feat: 添加会议完成推送功能并优化相关服务
- 引入 `AndroidMeetingPushService` 用于推送会议完成通知 - 在 `MeetingCommandServiceImpl` 和 `AiTaskServiceImpl` 中添加 `pushMeetingCompletedAfterCommitIfNeeded` 方法,确保事务提交后触发推送 - 更新 `MeetingInternalWorkflowController` 以支持手动触发会议完成推送和查询 gRPC 连接详情 - 新增 `MeetingPushTypeEnum` 枚举类,定义推送类型 - 优化 `AndroidGatewayPushService` 接口,添加用户级别的推送方法和连接快照功能 - 更新 `AndroidPushGrpcService` 和 `AndroidGatewayPushServiceImpl` 以支持新的注册参数和推送逻辑dev_na
parent
7f9c080bf7
commit
92a12c4c81
|
|
@ -87,6 +87,13 @@
|
|||
<artifactId>easy-captcha</artifactId>
|
||||
<version>${easycaptcha.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.38</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.imeeting.controller.biz;
|
||||
|
||||
import com.imeeting.dto.android.AndroidGrpcConnectionSnapshotVO;
|
||||
import com.imeeting.dto.biz.MeetingSummaryFinalizeDTO;
|
||||
import com.imeeting.dto.biz.MeetingExternalWorkflowFailureDTO;
|
||||
import com.imeeting.dto.biz.MeetingSummaryPromptContextRequestDTO;
|
||||
|
|
@ -7,6 +8,8 @@ import com.imeeting.dto.biz.MeetingSummaryPromptContextVO;
|
|||
import com.imeeting.dto.biz.MeetingTranscriptChapterImportDTO;
|
||||
import com.imeeting.dto.biz.MeetingTranscriptChapterImportResultVO;
|
||||
import com.imeeting.dto.biz.MeetingTranscriptSourceVO;
|
||||
import com.imeeting.service.android.AndroidGatewayPushService;
|
||||
import com.imeeting.service.android.AndroidMeetingPushService;
|
||||
import com.imeeting.service.biz.MeetingCommandService;
|
||||
import com.imeeting.service.biz.MeetingQueryService;
|
||||
import com.unisbase.common.ApiResponse;
|
||||
|
|
@ -30,6 +33,8 @@ public class MeetingInternalWorkflowController {
|
|||
|
||||
private final MeetingCommandService meetingCommandService;
|
||||
private final MeetingQueryService meetingQueryService;
|
||||
private final AndroidGatewayPushService androidGatewayPushService;
|
||||
private final AndroidMeetingPushService androidMeetingPushService;
|
||||
private final UnisBaseProperties unisBaseProperties;
|
||||
|
||||
@Value("${imeeting.summary-orchestration.mode:INTERNAL_BUILTIN}")
|
||||
|
|
@ -37,9 +42,13 @@ public class MeetingInternalWorkflowController {
|
|||
|
||||
public MeetingInternalWorkflowController(MeetingCommandService meetingCommandService,
|
||||
MeetingQueryService meetingQueryService,
|
||||
AndroidGatewayPushService androidGatewayPushService,
|
||||
AndroidMeetingPushService androidMeetingPushService,
|
||||
UnisBaseProperties unisBaseProperties) {
|
||||
this.meetingCommandService = meetingCommandService;
|
||||
this.meetingQueryService = meetingQueryService;
|
||||
this.androidGatewayPushService = androidGatewayPushService;
|
||||
this.androidMeetingPushService = androidMeetingPushService;
|
||||
this.unisBaseProperties = unisBaseProperties;
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +128,27 @@ public class MeetingInternalWorkflowController {
|
|||
return ApiResponse.ok(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "手工触发会议完成推送")
|
||||
@PostMapping("/{meetingId}/push/meeting-completed")
|
||||
public ApiResponse<Boolean> pushMeetingCompleted(HttpServletRequest request,
|
||||
@PathVariable Long meetingId) {
|
||||
if (!isInternalSecretValid(request)) {
|
||||
return ApiResponse.error("Invalid internal secret");
|
||||
}
|
||||
|
||||
androidMeetingPushService.pushMeetingCompleted(meetingId);
|
||||
return ApiResponse.ok(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询 Android gRPC 连接详情")
|
||||
@GetMapping("/grpc/connections")
|
||||
public ApiResponse<AndroidGrpcConnectionSnapshotVO> listGrpcConnections(HttpServletRequest request) {
|
||||
if (!isInternalSecretValid(request)) {
|
||||
return ApiResponse.error("Invalid internal secret");
|
||||
}
|
||||
return ApiResponse.ok(androidGatewayPushService.snapshotConnections());
|
||||
}
|
||||
|
||||
private boolean isExternalModeEnabled() {
|
||||
return "EXTERNAL_N8N".equalsIgnoreCase(summaryOrchestrationMode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package com.imeeting.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum MeetingPushTypeEnum {
|
||||
MEETING_COMPLETED("MEETING_COMPLETED","会议完成通知"),;
|
||||
private final String code;
|
||||
private final String desc;
|
||||
MeetingPushTypeEnum(String code,String desc){
|
||||
this.code=code;
|
||||
this.desc=desc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -123,7 +123,13 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
platform = authContext.getPlatform();
|
||||
deviceOnlineManagementService.recordConnected(authContext);
|
||||
connected = true;
|
||||
String replacedConnectionId = androidGatewayPushService.register(connectionId, deviceId, responseObserver);
|
||||
String replacedConnectionId = androidGatewayPushService.register(
|
||||
connectionId,
|
||||
deviceId,
|
||||
authContext.getTenantId(),
|
||||
authContext.getUserId(),
|
||||
responseObserver
|
||||
);
|
||||
if (replacedConnectionId != null && !replacedConnectionId.equals(connectionId)) {
|
||||
log.info(buildLog("gRPC连接替换",
|
||||
"同设备旧连接被新连接替换,旧连接ID=" + replacedConnectionId + ",新连接ID=" + connectionId,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
package com.imeeting.service.android;
|
||||
|
||||
import com.imeeting.dto.android.AndroidGrpcConnectionSnapshotVO;
|
||||
import com.imeeting.grpc.push.PushMessage;
|
||||
import com.imeeting.grpc.push.ServerMessage;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
public interface AndroidGatewayPushService {
|
||||
String register(String connectionId, String deviceId, StreamObserver<ServerMessage> observer);
|
||||
String register(String connectionId,
|
||||
String deviceId,
|
||||
Long tenantId,
|
||||
Long userId,
|
||||
StreamObserver<ServerMessage> observer);
|
||||
|
||||
void unregister(String connectionId);
|
||||
|
||||
|
|
@ -13,5 +18,9 @@ public interface AndroidGatewayPushService {
|
|||
|
||||
int pushToDevice(String deviceId, PushMessage message);
|
||||
|
||||
int pushToUser(Long tenantId, Long userId, PushMessage message);
|
||||
|
||||
String disconnectDevice(String deviceId);
|
||||
|
||||
AndroidGrpcConnectionSnapshotVO snapshotConnections();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.imeeting.service.android;
|
||||
|
||||
public interface AndroidMeetingPushService {
|
||||
|
||||
void pushMeetingCompleted(Long meetingId);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.imeeting.service.android.impl;
|
||||
|
||||
import com.imeeting.dto.android.AndroidGrpcConnectionDetailVO;
|
||||
import com.imeeting.dto.android.AndroidGrpcConnectionSnapshotVO;
|
||||
import com.imeeting.grpc.push.PushMessage;
|
||||
import com.imeeting.grpc.push.ServerMessage;
|
||||
import com.imeeting.service.android.AndroidGatewayPushService;
|
||||
|
|
@ -7,6 +9,8 @@ import io.grpc.stub.StreamObserver;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
|
@ -16,19 +20,27 @@ public class AndroidGatewayPushServiceImpl implements AndroidGatewayPushService
|
|||
|
||||
private final Map<String, Binding> byConnectionId = new ConcurrentHashMap<>();
|
||||
private final Map<String, String> connectionByDeviceId = new ConcurrentHashMap<>();
|
||||
private final Map<String, Map<String, Binding>> connectionsByUserKey = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public String register(String connectionId, String deviceId, StreamObserver<ServerMessage> observer) {
|
||||
Binding newBinding = new Binding(deviceId, observer);
|
||||
public String register(String connectionId,
|
||||
String deviceId,
|
||||
Long tenantId,
|
||||
Long userId,
|
||||
StreamObserver<ServerMessage> observer) {
|
||||
Binding newBinding = new Binding(deviceId, tenantId, userId, observer);
|
||||
Binding previousBinding = byConnectionId.put(connectionId, newBinding);
|
||||
if (previousBinding != null && !previousBinding.deviceId().equals(deviceId)) {
|
||||
connectionByDeviceId.remove(previousBinding.deviceId(), connectionId);
|
||||
if (previousBinding != null) {
|
||||
removeDeviceIndex(connectionId, previousBinding);
|
||||
removeUserIndex(connectionId, previousBinding);
|
||||
}
|
||||
|
||||
String previousConnectionId = connectionByDeviceId.put(deviceId, connectionId);
|
||||
addUserIndex(connectionId, newBinding);
|
||||
if (previousConnectionId != null && !previousConnectionId.equals(connectionId)) {
|
||||
Binding replacedBinding = byConnectionId.remove(previousConnectionId);
|
||||
if (replacedBinding != null) {
|
||||
removeUserIndex(previousConnectionId, replacedBinding);
|
||||
safeComplete(previousConnectionId, replacedBinding);
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +53,8 @@ public class AndroidGatewayPushServiceImpl implements AndroidGatewayPushService
|
|||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
connectionByDeviceId.remove(binding.deviceId(), connectionId);
|
||||
removeDeviceIndex(connectionId, binding);
|
||||
removeUserIndex(connectionId, binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -71,6 +84,25 @@ public class AndroidGatewayPushServiceImpl implements AndroidGatewayPushService
|
|||
return pushToConnection(connectionId, message) ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int pushToUser(Long tenantId, Long userId, PushMessage message) {
|
||||
String userKey = buildUserKey(tenantId, userId);
|
||||
if (userKey == null) {
|
||||
return 0;
|
||||
}
|
||||
Map<String, Binding> bindings = connectionsByUserKey.get(userKey);
|
||||
if (bindings == null || bindings.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
int successCount = 0;
|
||||
for (String connectionId : bindings.keySet()) {
|
||||
if (pushToConnection(connectionId, message)) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
return successCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String disconnectDevice(String deviceId) {
|
||||
String connectionId = connectionByDeviceId.get(deviceId);
|
||||
|
|
@ -87,6 +119,18 @@ public class AndroidGatewayPushServiceImpl implements AndroidGatewayPushService
|
|||
return connectionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidGrpcConnectionSnapshotVO snapshotConnections() {
|
||||
List<AndroidGrpcConnectionDetailVO> connections = byConnectionId.entrySet().stream()
|
||||
.map(entry -> toDetail(entry.getKey(), entry.getValue()))
|
||||
.sorted(Comparator.comparing(AndroidGrpcConnectionDetailVO::getConnectionId, Comparator.nullsLast(String::compareTo)))
|
||||
.toList();
|
||||
AndroidGrpcConnectionSnapshotVO snapshot = new AndroidGrpcConnectionSnapshotVO();
|
||||
snapshot.setConnectionCount(connections.size());
|
||||
snapshot.setConnections(connections);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private void safeComplete(String connectionId, Binding binding) {
|
||||
synchronized (binding) {
|
||||
try {
|
||||
|
|
@ -97,6 +141,47 @@ public class AndroidGatewayPushServiceImpl implements AndroidGatewayPushService
|
|||
}
|
||||
}
|
||||
|
||||
private record Binding(String deviceId, StreamObserver<ServerMessage> observer) {
|
||||
private void addUserIndex(String connectionId, Binding binding) {
|
||||
String userKey = buildUserKey(binding.tenantId(), binding.userId());
|
||||
if (userKey == null) {
|
||||
return;
|
||||
}
|
||||
connectionsByUserKey
|
||||
.computeIfAbsent(userKey, ignored -> new ConcurrentHashMap<>())
|
||||
.put(connectionId, binding);
|
||||
}
|
||||
|
||||
private void removeDeviceIndex(String connectionId, Binding binding) {
|
||||
connectionByDeviceId.remove(binding.deviceId(), connectionId);
|
||||
}
|
||||
|
||||
private void removeUserIndex(String connectionId, Binding binding) {
|
||||
String userKey = buildUserKey(binding.tenantId(), binding.userId());
|
||||
if (userKey == null) {
|
||||
return;
|
||||
}
|
||||
connectionsByUserKey.computeIfPresent(userKey, (ignored, bindings) -> {
|
||||
bindings.remove(connectionId);
|
||||
return bindings.isEmpty() ? null : bindings;
|
||||
});
|
||||
}
|
||||
|
||||
private String buildUserKey(Long tenantId, Long userId) {
|
||||
if (tenantId == null || userId == null) {
|
||||
return null;
|
||||
}
|
||||
return tenantId + ":" + userId;
|
||||
}
|
||||
|
||||
private AndroidGrpcConnectionDetailVO toDetail(String connectionId, Binding binding) {
|
||||
AndroidGrpcConnectionDetailVO detail = new AndroidGrpcConnectionDetailVO();
|
||||
detail.setConnectionId(connectionId);
|
||||
detail.setDeviceId(binding.deviceId());
|
||||
detail.setTenantId(binding.tenantId());
|
||||
detail.setUserId(binding.userId());
|
||||
return detail;
|
||||
}
|
||||
|
||||
private record Binding(String deviceId, Long tenantId, Long userId, StreamObserver<ServerMessage> observer) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
package com.imeeting.service.android.impl;
|
||||
|
||||
import cn.hutool.json.JSON;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.imeeting.dto.biz.MeetingVO;
|
||||
import com.imeeting.entity.biz.Meeting;
|
||||
import com.imeeting.enums.MeetingPushTypeEnum;
|
||||
import com.imeeting.grpc.push.PushMessage;
|
||||
|
||||
import com.imeeting.service.android.AndroidGatewayPushService;
|
||||
import com.imeeting.service.android.AndroidMeetingPushService;
|
||||
import com.imeeting.service.biz.MeetingQueryService;
|
||||
import com.imeeting.service.biz.MeetingService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
|
||||
public class AndroidMeetingPushServiceImpl implements AndroidMeetingPushService {
|
||||
|
||||
private static final DateTimeFormatter TITLE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private MeetingQueryService meetingService;
|
||||
@Autowired
|
||||
private AndroidGatewayPushService androidGatewayPushService;
|
||||
|
||||
@Override
|
||||
public void pushMeetingCompleted(Long meetingId) {
|
||||
if (meetingId == null) {
|
||||
return;
|
||||
}
|
||||
MeetingVO meeting = meetingService.getDetailIgnoreTenant(meetingId);
|
||||
if (meeting == null || meeting.getTenantId() == null || meeting.getCreatorId() == null) {
|
||||
return;
|
||||
}
|
||||
PushMessage message = PushMessage.newBuilder()
|
||||
.setMessageId("meeting_completed:" + meetingId + ":" + UUID.randomUUID())
|
||||
.setTimestamp(System.currentTimeMillis())
|
||||
.setType(MeetingPushTypeEnum.MEETING_COMPLETED.getCode())
|
||||
.setTitle(resolveTitle(meeting))
|
||||
.setContent(resolveContent(meeting))
|
||||
.setNeedAck(false)
|
||||
.build();
|
||||
int pushed = androidGatewayPushService.pushToUser(meeting.getTenantId(), meeting.getCreatorId(), message);
|
||||
log.info("Android meeting completion push finished, meetingId={}, tenantId={}, creatorId={}, pushedConnections={}",
|
||||
meetingId, meeting.getTenantId(), meeting.getCreatorId(), pushed);
|
||||
}
|
||||
|
||||
|
||||
private String resolveTitle(MeetingVO meeting) {
|
||||
String title = meeting.getTitle();
|
||||
if (title != null && !title.isBlank()) {
|
||||
return "会议已完成: " + title.trim();
|
||||
}
|
||||
LocalDateTime meetingTime = meeting.getMeetingTime();
|
||||
return meetingTime == null
|
||||
? "会议已完成"
|
||||
: "会议已完成: " + TITLE_TIME_FORMATTER.format(meetingTime);
|
||||
}
|
||||
|
||||
private String resolveContent(MeetingVO meeting) {
|
||||
Map<String,Object> result=new HashMap<>();
|
||||
result.put("meetingId",meeting.getId());
|
||||
return JSONUtil.toJsonStr(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.imeeting.entity.biz.MeetingTranscript;
|
|||
import com.imeeting.mapper.biz.AiTaskMapper;
|
||||
import com.imeeting.mapper.biz.MeetingMapper;
|
||||
import com.imeeting.mapper.biz.MeetingTranscriptMapper;
|
||||
import com.imeeting.service.android.AndroidMeetingPushService;
|
||||
import com.imeeting.support.TaskSecurityContextRunner;
|
||||
import com.imeeting.service.biz.AiModelService;
|
||||
import com.imeeting.service.biz.AiTaskService;
|
||||
|
|
@ -41,6 +42,8 @@ import org.springframework.data.redis.core.StringRedisTemplate;
|
|||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
|
|
@ -78,6 +81,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
private final TaskSecurityContextRunner taskSecurityContextRunner;
|
||||
private final MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger;
|
||||
private final SysParamService sysParamService;
|
||||
private final AndroidMeetingPushService androidMeetingPushService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("asrTaskExecutor")
|
||||
|
|
@ -120,7 +124,8 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
MeetingSummaryPromptAssembler meetingSummaryPromptAssembler,
|
||||
TaskSecurityContextRunner taskSecurityContextRunner,
|
||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger,
|
||||
SysParamService sysParamService) {
|
||||
SysParamService sysParamService,
|
||||
AndroidMeetingPushService androidMeetingPushService) {
|
||||
this.meetingMapper = meetingMapper;
|
||||
this.transcriptMapper = transcriptMapper;
|
||||
this.aiModelService = aiModelService;
|
||||
|
|
@ -136,6 +141,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
this.taskSecurityContextRunner = taskSecurityContextRunner;
|
||||
this.meetingExternalSummaryWebhookTrigger = meetingExternalSummaryWebhookTrigger;
|
||||
this.sysParamService = sysParamService;
|
||||
this.androidMeetingPushService = androidMeetingPushService;
|
||||
}
|
||||
|
||||
public AiTaskServiceImpl(MeetingMapper meetingMapper,
|
||||
|
|
@ -151,7 +157,8 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
MeetingTranscriptChapterService meetingTranscriptChapterService,
|
||||
MeetingSummaryPromptAssembler meetingSummaryPromptAssembler,
|
||||
TaskSecurityContextRunner taskSecurityContextRunner,
|
||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger) {
|
||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger,
|
||||
AndroidMeetingPushService androidMeetingPushService) {
|
||||
this(
|
||||
meetingMapper,
|
||||
transcriptMapper,
|
||||
|
|
@ -167,7 +174,8 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
meetingSummaryPromptAssembler,
|
||||
taskSecurityContextRunner,
|
||||
meetingExternalSummaryWebhookTrigger,
|
||||
null
|
||||
null,
|
||||
androidMeetingPushService
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -972,6 +980,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
|
||||
Files.writeString(filePath, markdownContent, StandardCharsets.UTF_8);
|
||||
|
||||
boolean alreadyCompleted = Integer.valueOf(3).equals(meeting.getStatus());
|
||||
taskRecord.setResultFilePath("meetings/" + meeting.getId() + "/summaries/" + fileName);
|
||||
Map<String, Object> responseData = objectMapper.convertValue(respNode, Map.class);
|
||||
responseData.put("summarySource", summarySource.toSnapshot());
|
||||
|
|
@ -992,6 +1001,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
AiTask latestChapterTask = findLatestTask(meeting.getId(), "CHAPTER");
|
||||
if (latestChapterTask != null && Integer.valueOf(2).equals(latestChapterTask.getStatus())) {
|
||||
updateProgress(meeting.getId(), 100, "全流程分析完成", 0);
|
||||
pushMeetingCompletedAfterCommitIfNeeded(meeting.getId(), alreadyCompleted);
|
||||
} else {
|
||||
updateProgress(meeting.getId(), 95, "总结生成完成,等待 AI 目录完成...", 0);
|
||||
}
|
||||
|
|
@ -1335,6 +1345,22 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
|||
meetingMapper.updateById(m);
|
||||
}
|
||||
|
||||
private void pushMeetingCompletedAfterCommitIfNeeded(Long meetingId, boolean alreadyCompleted) {
|
||||
if (alreadyCompleted) {
|
||||
return;
|
||||
}
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
androidMeetingPushService.pushMeetingCompleted(meetingId);
|
||||
return;
|
||||
}
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
androidMeetingPushService.pushMeetingCompleted(meetingId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private AiTask createAiTask(Long meetingId, String type, Map<String, Object> req) {
|
||||
AiTask task = new AiTask();
|
||||
task.setMeetingId(meetingId);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.imeeting.entity.biz.HotWord;
|
|||
import com.imeeting.entity.biz.Meeting;
|
||||
import com.imeeting.entity.biz.MeetingTranscript;
|
||||
import com.imeeting.entity.biz.MeetingTranscriptChapterVersion;
|
||||
import com.imeeting.service.android.AndroidMeetingPushService;
|
||||
import com.imeeting.service.biz.AiTaskService;
|
||||
import com.imeeting.service.biz.HotWordService;
|
||||
import com.imeeting.service.biz.MeetingCommandService;
|
||||
|
|
@ -75,6 +76,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
private final MeetingProgressService meetingProgressService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger;
|
||||
private final AndroidMeetingPushService androidMeetingPushService;
|
||||
private StringRedisTemplate compatibilityRedisTemplate;
|
||||
|
||||
@Value("${imeeting.summary-orchestration.mode:INTERNAL_BUILTIN}")
|
||||
|
|
@ -95,7 +97,8 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
RealtimeMeetingAudioStorageService realtimeMeetingAudioStorageService,
|
||||
MeetingProgressService meetingProgressService,
|
||||
ObjectMapper objectMapper,
|
||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger) {
|
||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger,
|
||||
AndroidMeetingPushService androidMeetingPushService) {
|
||||
this.meetingService = meetingService;
|
||||
this.aiTaskService = aiTaskService;
|
||||
this.hotWordService = hotWordService;
|
||||
|
|
@ -111,6 +114,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
this.meetingProgressService = meetingProgressService;
|
||||
this.objectMapper = objectMapper;
|
||||
this.meetingExternalSummaryWebhookTrigger = meetingExternalSummaryWebhookTrigger;
|
||||
this.androidMeetingPushService = androidMeetingPushService;
|
||||
}
|
||||
|
||||
public MeetingCommandServiceImpl(MeetingService meetingService,
|
||||
|
|
@ -127,7 +131,8 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
RealtimeMeetingAudioStorageService realtimeMeetingAudioStorageService,
|
||||
StringRedisTemplate redisTemplate,
|
||||
ObjectMapper objectMapper,
|
||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger) {
|
||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger,
|
||||
AndroidMeetingPushService androidMeetingPushService) {
|
||||
this(
|
||||
meetingService,
|
||||
aiTaskService,
|
||||
|
|
@ -143,7 +148,8 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
realtimeMeetingAudioStorageService,
|
||||
new RedisOnlyMeetingProgressServiceAdapter(redisTemplate, objectMapper),
|
||||
objectMapper,
|
||||
meetingExternalSummaryWebhookTrigger
|
||||
meetingExternalSummaryWebhookTrigger,
|
||||
androidMeetingPushService
|
||||
);
|
||||
this.compatibilityRedisTemplate = redisTemplate;
|
||||
}
|
||||
|
|
@ -790,6 +796,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
summaryTask.setCompletedAt(java.time.LocalDateTime.now());
|
||||
aiTaskService.updateById(summaryTask);
|
||||
|
||||
boolean alreadyCompleted = Integer.valueOf(3).equals(meeting.getStatus());
|
||||
meeting.setLatestSummaryTaskId(summaryTask.getId());
|
||||
meetingService.updateById(meeting);
|
||||
aiTaskService.reconcileMeetingStatus(meeting.getId());
|
||||
|
|
@ -801,6 +808,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
.last("LIMIT 1"));
|
||||
if (latestChapterTask != null && Integer.valueOf(2).equals(latestChapterTask.getStatus())) {
|
||||
updateMeetingProgress(meeting.getId(), 100, "外部总结回填完成", 0);
|
||||
pushMeetingCompletedAfterCommitIfNeeded(meeting.getId(), alreadyCompleted);
|
||||
} else {
|
||||
updateMeetingProgress(meeting.getId(), 95, "外部总结回填完成,等待 AI 目录完成...", 0);
|
||||
}
|
||||
|
|
@ -1219,6 +1227,22 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
});
|
||||
}
|
||||
|
||||
private void pushMeetingCompletedAfterCommitIfNeeded(Long meetingId, boolean alreadyCompleted) {
|
||||
if (alreadyCompleted) {
|
||||
return;
|
||||
}
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
androidMeetingPushService.pushMeetingCompleted(meetingId);
|
||||
return;
|
||||
}
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
androidMeetingPushService.pushMeetingCompleted(meetingId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateMeetingProgress(Long meetingId, int percent, String message, int eta) {
|
||||
com.imeeting.common.MeetingProgressStage stage;
|
||||
int meetingStatus;
|
||||
|
|
|
|||
Loading…
Reference in New Issue