feat: 添加会议完成推送功能并优化相关服务

- 引入 `AndroidMeetingPushService` 用于推送会议完成通知
- 在 `MeetingCommandServiceImpl` 和 `AiTaskServiceImpl` 中添加 `pushMeetingCompletedAfterCommitIfNeeded` 方法,确保事务提交后触发推送
- 更新 `MeetingInternalWorkflowController` 以支持手动触发会议完成推送和查询 gRPC 连接详情
- 新增 `MeetingPushTypeEnum` 枚举类,定义推送类型
- 优化 `AndroidGatewayPushService` 接口,添加用户级别的推送方法和连接快照功能
- 更新 `AndroidPushGrpcService` 和 `AndroidGatewayPushServiceImpl` 以支持新的注册参数和推送逻辑
dev_na
chenhao 2026-05-28 16:18:41 +08:00
parent 7f9c080bf7
commit 92a12c4c81
10 changed files with 308 additions and 14 deletions

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
};
}

View File

@ -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,

View File

@ -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();
}

View File

@ -0,0 +1,10 @@
package com.imeeting.service.android;
public interface AndroidMeetingPushService {
void pushMeetingCompleted(Long meetingId);
}

View File

@ -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) {
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;