diff --git a/backend/src/main/java/com/imeeting/common/exception/ExistingOfflineMeetingException.java b/backend/src/main/java/com/imeeting/common/exception/ExistingOfflineMeetingException.java deleted file mode 100644 index 07865b8..0000000 --- a/backend/src/main/java/com/imeeting/common/exception/ExistingOfflineMeetingException.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.imeeting.common.exception; - -import lombok.Getter; - -@Getter -public class ExistingOfflineMeetingException extends RuntimeException { - private final Long meetingId; - - public ExistingOfflineMeetingException(Long meetingId) { - super("有未结束会议"); - this.meetingId = meetingId; - } -} diff --git a/backend/src/main/java/com/imeeting/controller/android/AndroidMeetingController.java b/backend/src/main/java/com/imeeting/controller/android/AndroidMeetingController.java index 32e4960..509ea14 100644 --- a/backend/src/main/java/com/imeeting/controller/android/AndroidMeetingController.java +++ b/backend/src/main/java/com/imeeting/controller/android/AndroidMeetingController.java @@ -4,7 +4,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.imeeting.common.MeetingConstants; import com.imeeting.common.SysParamKeys; -import com.imeeting.common.exception.ExistingOfflineMeetingException; import com.imeeting.dto.android.AndroidAuthContext; import com.imeeting.dto.android.AndroidOfflineMeetingCreateCommand; import com.imeeting.dto.android.AndroidMeetingCreateResponse; @@ -28,6 +27,7 @@ import com.imeeting.dto.biz.UnifiedMeetingStatusVO; import com.imeeting.entity.biz.AiTask; import com.imeeting.entity.biz.Meeting; import com.imeeting.entity.biz.PromptTemplate; +import com.imeeting.enums.BusinessErrorCodeEnum; import com.imeeting.enums.MeetingStatusEnum; import com.imeeting.service.android.AndroidAuthService; import com.imeeting.service.android.AndroidChunkUploadService; @@ -37,6 +37,7 @@ import com.imeeting.service.biz.*; import com.unisbase.annotation.Anonymous; import com.unisbase.common.ApiResponse; import com.unisbase.common.annotation.Log; +import com.unisbase.common.exception.BusinessException; import com.unisbase.dto.PageResult; import com.unisbase.entity.SysTenant; import com.unisbase.entity.SysUser; @@ -166,16 +167,13 @@ public class AndroidMeetingController { AndroidAuthContext authContext = androidAuthService.authenticateHttp(request); resolvePublicDeviceTenantId(request, command, authContext); LoginUser loginUser = authContext.isAnonymous() ? null : AndroidLoginUserSupport.requireLoginUser(authContext); - try { + // Meeting existingMeeting = findLatestUnfinishedMeetingByDevice(authContext.getDeviceId()); // if (existingMeeting != null) { // return new ApiResponse<>("409", "设备端已有会议", meetingQueryService.getDetailIgnoreTenant(existingMeeting.getId())); // } MeetingVO meeting = legacyMeetingAdapterService.createMeeting(command, authContext, loginUser); return ApiResponse.ok(buildAndroidMeetingCreateResponse(meeting)); - } catch (ExistingOfflineMeetingException ex) { - return new ApiResponse<>("409", "有未结束会议", new AndroidOfflineMeetingConflictVO(ex.getMeetingId())); - } } @Operation(summary = "上传Android会议音频") @@ -292,23 +290,8 @@ public class AndroidMeetingController { return ApiResponse.ok(buildAndroidMeetingListPage(result)); } - @Operation(summary = "查询Android会议预览数据") - @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse( - responseCode = "200", - description = "返回会议预览结果,包含已完成摘要或处理中状态信息", - content = @Content(schema = @Schema(implementation = LegacyMeetingPreviewDataResponse.class)) - ) - }) - @GetMapping("/{meetingId}/preview-data") - public ApiResponse previewData(HttpServletRequest request, @PathVariable Long meetingId) { - AndroidRequestLogHelper.logRequest(log, "Android会议", "查询会议预览数据接口", "meetingId", meetingId); - androidAuthService.authenticateHttp(request); - LegacyMeetingPreviewResult result = buildPreviewResult(meetingId); - return new ApiResponse<>(result.getCode(), result.getMessage(), result.getData()); - } - @Operation(summary = "查询Android会议统一状态") + @Operation(summary = "查询Android会议统一状态") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse( responseCode = "200", @@ -560,7 +543,7 @@ public class AndroidMeetingController { private MeetingVO requireOperableOfflineMeeting(Long meetingId, AndroidAuthContext authContext, LoginUser loginUser) { MeetingVO meeting = meetingQueryService.getDetailIgnoreTenant(meetingId); if (meeting == null) { - throw new RuntimeException("会议不存在"); + throw new BusinessException(BusinessErrorCodeEnum.MEETING_NOT_FOUND.getCode(), "会议不存在"); } if (!MeetingConstants.TYPE_OFFLINE.equals(meeting.getMeetingType())) { throw new RuntimeException("当前会议不是离线会议"); @@ -588,107 +571,6 @@ public class AndroidMeetingController { && MeetingConstants.OFFLINE_RECORDING_UPLOAD_FINISHED.equalsIgnoreCase(command.getFinishStage()); } - private LegacyMeetingPreviewResult buildPreviewResult(Long meetingId) { - Meeting meeting = meetingService.getById(meetingId); - if (meeting == null) { - return new LegacyMeetingPreviewResult("404", "会议不存在", null); - } - - AiTask asrTask = findLatestTask(meetingId, "ASR"); - AiTask summaryTask = findLatestTask(meetingId, "SUMMARY"); - boolean summaryCompleted = summaryTask != null && Integer.valueOf(2).equals(summaryTask.getStatus()); - MeetingVO detail = (MeetingStatusEnum.isCode(meeting.getStatus(), MeetingStatusEnum.COMPLETED) || summaryCompleted) - ? meetingQueryService.getDetail(meetingId) - : null; - boolean hasSummary = detail != null && detail.getSummaryContent() != null && !detail.getSummaryContent().isBlank(); - - if (hasSummary) { - return new LegacyMeetingPreviewResult("200", "success", buildCompletedPreview(meeting, detail, summaryTask)); - } - if (summaryCompleted) { - return new LegacyMeetingPreviewResult( - "504", - "处理已完成,但摘要尚未同步,请稍后重试", - buildProcessingPreview(meeting, summaryTask, processingStatus("摘要已生成,可查看详情", 100, STAGE_COMPLETED)) - ); - } - if (isFailed(asrTask)) { - return new LegacyMeetingPreviewResult( - "503", - buildFailureMessage(asrTask, "转写"), - buildProcessingPreview(meeting, summaryTask, processingStatus("转写或总结失败", 50, STAGE_AUDIO_TRANSCRIPTION)) - ); - } - if (isFailed(summaryTask)) { - return new LegacyMeetingPreviewResult( - "503", - buildFailureMessage(summaryTask, "总结"), - buildProcessingPreview(meeting, summaryTask, processingStatus("转写或总结失败", 75, STAGE_SUMMARY_GENERATION)) - ); - } - - Integer realtimeProgress = resolveRealtimeProgress(meetingId); - if (asrTask != null && Integer.valueOf(0).equals(asrTask.getStatus()) && realtimeProgress != null && realtimeProgress <= 0) { - return new LegacyMeetingPreviewResult( - "400", - "会议正在处理中", - buildProcessingPreview(meeting, summaryTask, processingStatus("会议数据准备中", 25, STAGE_DATA_INITIALIZATION)) - ); - } - if (realtimeProgress != null) { - if (realtimeProgress >= 100) { - MeetingVO completedDetail = detail != null ? detail : meetingQueryService.getDetail(meetingId); - boolean completedHasSummary = completedDetail != null - && completedDetail.getSummaryContent() != null - && !completedDetail.getSummaryContent().isBlank(); - if (completedHasSummary) { - return new LegacyMeetingPreviewResult("200", "success", buildCompletedPreview(meeting, completedDetail, summaryTask)); - } - return new LegacyMeetingPreviewResult( - "504", - "处理已完成,但摘要尚未同步,请稍后重试", - buildProcessingPreview(meeting, summaryTask, processingStatus("摘要已生成,可查看详情", 100, STAGE_COMPLETED)) - ); - } - if (realtimeProgress < 90) { - return new LegacyMeetingPreviewResult( - "400", - "会议正在处理中", - buildProcessingPreview(meeting, summaryTask, processingStatus("正在转写音频", 50, STAGE_AUDIO_TRANSCRIPTION)) - ); - } - if (realtimeProgress >= 90) { - return new LegacyMeetingPreviewResult( - "400", - "会议正在处理中", - buildProcessingPreview(meeting, summaryTask, processingStatus("正在生成总结", 75, STAGE_SUMMARY_GENERATION)) - ); - } - } - - boolean isSummaryStage = isSummaryStage(meeting.getStatus(), summaryTask); - boolean isAsrStage = isAsrStage(meeting.getStatus(), asrTask, hasAudio(meeting), isSummaryStage); - - if (!isAsrStage && !isSummaryStage) { - return new LegacyMeetingPreviewResult( - "400", - "会议正在处理中", - buildProcessingPreview(meeting, summaryTask, processingStatus("会议数据准备中", 25, STAGE_DATA_INITIALIZATION)) - ); - } - if (!isSummaryStage) { - return new LegacyMeetingPreviewResult( - "400", - "会议正在处理中", - buildProcessingPreview(meeting, summaryTask, processingStatus("正在转写音频", 50, STAGE_AUDIO_TRANSCRIPTION)) - ); - } - return new LegacyMeetingPreviewResult( - "400", - "会议正在处理中", - buildProcessingPreview(meeting, summaryTask, processingStatus("正在生成总结", 75, STAGE_SUMMARY_GENERATION)) - ); - } private LegacyMeetingPreviewDataResponse buildCompletedPreview(Meeting meeting, MeetingVO detail, AiTask summaryTask) { LegacyMeetingPreviewDataResponse data = new LegacyMeetingPreviewDataResponse(); diff --git a/backend/src/main/java/com/imeeting/dto/android/AndroidDeviceHomeStatsVO.java b/backend/src/main/java/com/imeeting/dto/android/AndroidDeviceHomeStatsVO.java index 33842e1..d2235a0 100644 --- a/backend/src/main/java/com/imeeting/dto/android/AndroidDeviceHomeStatsVO.java +++ b/backend/src/main/java/com/imeeting/dto/android/AndroidDeviceHomeStatsVO.java @@ -33,4 +33,6 @@ public class AndroidDeviceHomeStatsVO { @Schema(description = "是否已登录") private Boolean loggedIn; + @Schema(description = "是否开启余额校验") + private Boolean balanceCheckEnabled; } diff --git a/backend/src/main/java/com/imeeting/enums/BusinessErrorCodeEnum.java b/backend/src/main/java/com/imeeting/enums/BusinessErrorCodeEnum.java new file mode 100644 index 0000000..44e454e --- /dev/null +++ b/backend/src/main/java/com/imeeting/enums/BusinessErrorCodeEnum.java @@ -0,0 +1,18 @@ +package com.imeeting.enums; + +import lombok.Getter; + +@Getter +public enum BusinessErrorCodeEnum { + MEETING_NOT_FOUND("40001", "会议不存在"), + ; + + + private final String code; + private final String desc; + + BusinessErrorCodeEnum(String code, String desc) { + this.code = code; + this.desc = desc; + } +} diff --git a/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java b/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java index b958c10..a0ea0a1 100644 --- a/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java @@ -28,6 +28,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { private static final String HEADER_DEVICE_ID = "X-Android-Device-Id"; private static final String HEADER_APP_ID = "X-Android-App-Id"; + private static final String HEADER_TENANT_CODE = "X-Tenant-Code"; private static final String HEADER_APP_VERSION = "X-Android-App-Version"; private static final String HEADER_PLATFORM = "X-Android-Platform"; private static final String HEADER_AUTHORIZATION = "Authorization"; diff --git a/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceHomeServiceImpl.java b/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceHomeServiceImpl.java index 485dee5..9a85e1b 100644 --- a/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceHomeServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceHomeServiceImpl.java @@ -11,12 +11,14 @@ import com.imeeting.dto.android.AndroidDeviceWeatherCacheValue; import com.imeeting.dto.biz.MeetingPointsBalanceVO; import com.imeeting.entity.biz.DeviceInfoEntity; import com.imeeting.entity.biz.LicenseEntity; +import com.imeeting.entity.biz.TenantMeetingPointsSetting; import com.imeeting.mapper.DeviceInfoMapper; import com.imeeting.mapper.DeviceLoginLogMapper; import com.imeeting.mapper.LicenseMapper; import com.imeeting.mapper.biz.MeetingMapper; import com.imeeting.service.android.AndroidDeviceHomeService; import com.imeeting.service.biz.MeetingPointsService; +import com.imeeting.service.biz.TenantMeetingPointsSettingService; import com.imeeting.support.RedisSupport; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -55,8 +57,9 @@ public class AndroidDeviceHomeServiceImpl implements AndroidDeviceHomeService { private final RedisSupport redisSupport; private final com.unisbase.service.SysParamService sysParamService; private final ObjectMapper objectMapper; + private final TenantMeetingPointsSettingService tenantMeetingPointsSettingService; - @Value("${imeeting.h5.base-url:}") + @Value("${imeeting.h5.base-url:}") private String h5BaseUrl; private final HttpClient httpClient = HttpClient.newBuilder() @@ -95,7 +98,11 @@ public class AndroidDeviceHomeServiceImpl implements AndroidDeviceHomeService { vo.setRemainingMinutes(calculateRemainingMinutes(tenantId, authContext.getUserId(), authContext.isAnonymous())); vo.setWeather(resolveWeather(device.getWeatherCityName())); vo.setLoggedIn(!authContext.isAnonymous() && authContext.getUserId() != null); - return vo; + TenantMeetingPointsSetting byTenantId = tenantMeetingPointsSettingService.getByTenantId(authContext.getTenantId()); + vo.setBalanceCheckEnabled(byTenantId == null || byTenantId.getBalanceCheckEnabled() == 1); + + + return vo; } private Long calculateRemainingMinutes(Long tenantId, Long userId, boolean anonymous) { diff --git a/backend/src/main/java/com/imeeting/service/android/legacy/impl/LegacyMeetingAdapterServiceImpl.java b/backend/src/main/java/com/imeeting/service/android/legacy/impl/LegacyMeetingAdapterServiceImpl.java index a4fd43b..901004c 100644 --- a/backend/src/main/java/com/imeeting/service/android/legacy/impl/LegacyMeetingAdapterServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/android/legacy/impl/LegacyMeetingAdapterServiceImpl.java @@ -97,7 +97,8 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ Meeting existingMeeting = findLatestBlockingOfflineMeeting(authContext == null ? null : authContext.getDeviceId(), creatorUserId); if (existingMeeting != null) { - throw new ExistingOfflineMeetingException(existingMeeting.getId()); + existingMeeting.setOfflineRecordingStatus(MeetingConstants.OFFLINE_RECORDING_PRE_END); + meetingService.updateById(existingMeeting); } Long requestedSummaryModelId = request instanceof AndroidOfflineMeetingCreateCommand androidCommand diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingUnifiedStatusServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingUnifiedStatusServiceImpl.java index 06f9e72..22bd4db 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingUnifiedStatusServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingUnifiedStatusServiceImpl.java @@ -26,260 +26,260 @@ import java.util.Objects; @RequiredArgsConstructor public class MeetingUnifiedStatusServiceImpl implements MeetingUnifiedStatusService { - private final MeetingMapper meetingMapper; - private final AiTaskMapper aiTaskMapper; - private final MeetingTranscriptMapper meetingTranscriptMapper; - private final MeetingTranscriptChapterVersionMapper chapterVersionMapper; - private final MeetingProgressCache meetingProgressCache; + private final MeetingMapper meetingMapper; + private final AiTaskMapper aiTaskMapper; + private final MeetingTranscriptMapper meetingTranscriptMapper; + private final MeetingTranscriptChapterVersionMapper chapterVersionMapper; + private final MeetingProgressCache meetingProgressCache; - @Override - public UnifiedMeetingStatusVO resolve(MeetingVO meeting, MeetingProgressSnapshot snapshot) { - if (meeting == null || meeting.getId() == null) { - return null; - } - - UnifiedMeetingStatusStage stage = resolveStage(meeting, snapshot); - UnifiedMeetingStatusStage failedStage = resolveFailedStage(meeting); - boolean failed = failedStage != null; - UnifiedMeetingStatusStage effectiveStage = failed ? failedStage : stage; - - return UnifiedMeetingStatusVO.builder() - .meetingId(meeting.getId()) - .statusCode(effectiveStage.getCode()) - .statusText(effectiveStage.getText()) - .percent(resolvePercent(snapshot, effectiveStage)) - .message(resolveMessage(meeting, snapshot, effectiveStage)) - .eta(snapshot == null ? null : snapshot.getEta()) - .failedStageCode(failedStage == null ? null : failedStage.getCode()) - .failedStageText(failedStage == null ? null : failedStage.getText()) - .canViewTranscript(canViewTranscript(meeting.getId())) - .canViewAiChapters(canViewAiChapters(meeting.getId())) - .canViewSummary(canViewSummary(meeting)) - .build(); + @Override + public UnifiedMeetingStatusVO resolve(MeetingVO meeting, MeetingProgressSnapshot snapshot) { + if (meeting == null || meeting.getId() == null) { + return null; } - @Override - public UnifiedMeetingStatusVO resolve(Long meetingId) { - if (meetingId == null) { - return null; - } - Meeting meeting = meetingMapper.selectByIdIgnoreTenant(meetingId); - if (meeting == null) { - return null; - } - return resolve(toMeetingVO(meeting), meetingProgressCache.getSnapshot(meetingId)); + UnifiedMeetingStatusStage stage = resolveStage(meeting, snapshot); + UnifiedMeetingStatusStage failedStage = resolveFailedStage(meeting); + boolean failed = failedStage != null; + UnifiedMeetingStatusStage effectiveStage = failed ? failedStage : stage; + + return UnifiedMeetingStatusVO.builder() + .meetingId(meeting.getId()) + .statusCode(effectiveStage.getCode()) + .statusText(effectiveStage.getText()) + .percent(resolvePercent(snapshot, effectiveStage)) + .message(resolveMessage(meeting, snapshot, effectiveStage)) + .eta(snapshot == null ? null : snapshot.getEta()) + .failedStageCode(failedStage == null ? null : failedStage.getCode()) + .failedStageText(failedStage == null ? null : failedStage.getText()) + .canViewTranscript(canViewTranscript(meeting.getId())) + .canViewAiChapters(canViewAiChapters(meeting.getId())) + .canViewSummary(canViewSummary(meeting)) + .build(); + } + + @Override + public UnifiedMeetingStatusVO resolve(Long meetingId) { + if (meetingId == null) { + return null; + } + Meeting meeting = meetingMapper.selectByIdIgnoreTenant(meetingId); + if (meeting == null) { + return null; + } + return resolve(toMeetingVO(meeting), meetingProgressCache.getSnapshot(meetingId)); + } + + private MeetingUnifiedStageContext buildStageContext(Long meetingId, MeetingProgressSnapshot snapshot) { + AiTask latestAsr = findLatestTask(meetingId, "ASR"); + AiTask latestChapter = findLatestTask(meetingId, "CHAPTER"); + AiTask latestSummary = findLatestTask(meetingId, "SUMMARY"); + return new MeetingUnifiedStageContext(latestAsr, latestChapter, latestSummary, snapshot); + } + + private UnifiedMeetingStatusStage resolveStage(MeetingVO meeting, MeetingProgressSnapshot snapshot) { + if (meeting == null) { + return UnifiedMeetingStatusStage.INITIALIZING; + } + if (MeetingStatusEnum.isCode(meeting.getStatus(), MeetingStatusEnum.COMPLETED)) { + return UnifiedMeetingStatusStage.COMPLETED; + } + if (isAndroidOfflineMeetingWaitingUpload(meeting)) { + return UnifiedMeetingStatusStage.WAITING_UPLOAD; + } + UnifiedMeetingStatusStage stageFromSnapshot = resolveStageFromSnapshot(snapshot); + if (stageFromSnapshot != null) { + return stageFromSnapshot; } - private MeetingUnifiedStageContext buildStageContext(Long meetingId, MeetingProgressSnapshot snapshot) { - AiTask latestAsr = findLatestTask(meetingId, "ASR"); - AiTask latestChapter = findLatestTask(meetingId, "CHAPTER"); - AiTask latestSummary = findLatestTask(meetingId, "SUMMARY"); - return new MeetingUnifiedStageContext(latestAsr, latestChapter, latestSummary, snapshot); + MeetingUnifiedStageContext context = buildStageContext(meeting.getId(), snapshot); + if (isTranscribing(context)) { + return UnifiedMeetingStatusStage.TRANSCRIBING; + } + if (isSummarizing(context)) { + return UnifiedMeetingStatusStage.SUMMARIZING; } - private UnifiedMeetingStatusStage resolveStage(MeetingVO meeting, MeetingProgressSnapshot snapshot) { - if (meeting == null) { - return UnifiedMeetingStatusStage.INITIALIZING; - } - if (MeetingStatusEnum.isCode(meeting.getStatus(), MeetingStatusEnum.COMPLETED)) { - return UnifiedMeetingStatusStage.COMPLETED; - } - if (isAndroidOfflineMeetingWaitingUpload(meeting)) { - return UnifiedMeetingStatusStage.WAITING_UPLOAD; - } - UnifiedMeetingStatusStage stageFromSnapshot = resolveStageFromSnapshot(snapshot); - if (stageFromSnapshot != null) { - return stageFromSnapshot; - } + return UnifiedMeetingStatusStage.INITIALIZING; + } - MeetingUnifiedStageContext context = buildStageContext(meeting.getId(), snapshot); - if (isTranscribing(context)) { - return UnifiedMeetingStatusStage.TRANSCRIBING; - } - if (isSummarizing(context)) { - return UnifiedMeetingStatusStage.SUMMARIZING; - } + private UnifiedMeetingStatusStage resolveStageFromSnapshot(MeetingProgressSnapshot snapshot) { + if (snapshot == null || snapshot.getStage() == null || snapshot.getStage().isBlank()) { + return null; + } + return switch (snapshot.getStage()) { + case "failed" -> null; + case "completed" -> UnifiedMeetingStatusStage.COMPLETED; + case "summary_running", "chapter_running" -> UnifiedMeetingStatusStage.SUMMARIZING; + case "asr_running", "asr_completed", "asr_submitted" -> UnifiedMeetingStatusStage.TRANSCRIBING; + case "queued" -> UnifiedMeetingStatusStage.INITIALIZING; + default -> null; + }; + } - return UnifiedMeetingStatusStage.INITIALIZING; + private UnifiedMeetingStatusStage resolveFailedStage(MeetingVO meeting) { + if (meeting == null || !MeetingStatusEnum.isCode(meeting.getStatus(), MeetingStatusEnum.FAILED)) { + return null; + } + AiTask asrTask = findLatestTask(meeting.getId(), "ASR"); + if (isTaskFailed(asrTask)) { + return UnifiedMeetingStatusStage.FAILED_TRANSCRIBING; + } + AiTask summaryTask = findLatestTask(meeting.getId(), "SUMMARY"); + if (isTaskFailed(summaryTask)) { + return UnifiedMeetingStatusStage.FAILED_SUMMARIZING; + } + AiTask chapterTask = findLatestTask(meeting.getId(), "CHAPTER"); + if (isTaskFailed(chapterTask)) { + return UnifiedMeetingStatusStage.FAILED_SUMMARIZING; } - private UnifiedMeetingStatusStage resolveStageFromSnapshot(MeetingProgressSnapshot snapshot) { - if (snapshot == null || snapshot.getStage() == null || snapshot.getStage().isBlank()) { - return null; - } - return switch (snapshot.getStage()) { - case "failed" -> null; - case "completed" -> UnifiedMeetingStatusStage.COMPLETED; - case "summary_running", "chapter_running" -> UnifiedMeetingStatusStage.SUMMARIZING; - case "asr_running", "asr_completed", "asr_submitted" -> UnifiedMeetingStatusStage.TRANSCRIBING; - case "queued" -> UnifiedMeetingStatusStage.INITIALIZING; - default -> null; - }; - } + return UnifiedMeetingStatusStage.FAILED_INITIALIZING; + } - private UnifiedMeetingStatusStage resolveFailedStage(MeetingVO meeting) { - if (meeting == null || !MeetingStatusEnum.isCode(meeting.getStatus(), MeetingStatusEnum.FAILED)) { - return null; - } + private boolean isAndroidOfflineMeetingWaitingUpload(MeetingVO meeting) { + return meeting != null + && MeetingConstants.TYPE_OFFLINE.equalsIgnoreCase(meeting.getMeetingType()) + && MeetingConstants.SOURCE_ANDROID.equalsIgnoreCase(meeting.getMeetingSource()) + && !MeetingConstants.OFFLINE_RECORDING_UPLOAD_FINISHED.equalsIgnoreCase(meeting.getOfflineRecordingStatus()); + } - AiTask summaryTask = findLatestTask(meeting.getId(), "SUMMARY"); - if (isTaskFailed(summaryTask)) { - return UnifiedMeetingStatusStage.FAILED_SUMMARIZING; - } - AiTask chapterTask = findLatestTask(meeting.getId(), "CHAPTER"); - if (isTaskFailed(chapterTask)) { - return UnifiedMeetingStatusStage.FAILED_SUMMARIZING; - } - AiTask asrTask = findLatestTask(meeting.getId(), "ASR"); - if (isTaskFailed(asrTask)) { - return UnifiedMeetingStatusStage.FAILED_TRANSCRIBING; - } - return UnifiedMeetingStatusStage.FAILED_INITIALIZING; - } + private boolean isSummarizing(MeetingUnifiedStageContext context) { + return isTaskRunning(context.summaryTask()) + || isTaskRunning(context.chapterTask()) + || isTaskCompleted(context.chapterTask()) + || isTaskCompleted(context.summaryTask()); + } - private boolean isAndroidOfflineMeetingWaitingUpload(MeetingVO meeting) { - return meeting != null - && MeetingConstants.TYPE_OFFLINE.equalsIgnoreCase(meeting.getMeetingType()) - && MeetingConstants.SOURCE_ANDROID.equalsIgnoreCase(meeting.getMeetingSource()) - && !MeetingConstants.OFFLINE_RECORDING_UPLOAD_FINISHED.equalsIgnoreCase(meeting.getOfflineRecordingStatus()); + private boolean isTranscribing(MeetingUnifiedStageContext context) { + if (isTaskRunningOrQueued(context.summaryTask()) || isTaskRunningOrQueued(context.chapterTask())) { + return false; } + return isTaskRunningOrQueued(context.asrTask()) || isTaskCompleted(context.asrTask()); + } - private boolean isSummarizing(MeetingUnifiedStageContext context) { - return isTaskRunning(context.summaryTask()) - || isTaskRunning(context.chapterTask()) - || isTaskCompleted(context.chapterTask()) - || isTaskCompleted(context.summaryTask()); + private Integer resolvePercent(MeetingProgressSnapshot snapshot, UnifiedMeetingStatusStage stage) { + if (snapshot != null && snapshot.getPercent() != null) { + return snapshot.getPercent(); } + return switch (stage) { + case WAITING_UPLOAD -> 0; + case INITIALIZING -> 5; + case TRANSCRIBING -> 50; + case SUMMARIZING -> 90; + case COMPLETED -> 100; + case FAILED_INITIALIZING, FAILED_TRANSCRIBING, FAILED_SUMMARIZING -> -1; + }; + } - private boolean isTranscribing(MeetingUnifiedStageContext context) { - if (isTaskRunningOrQueued(context.summaryTask()) || isTaskRunningOrQueued(context.chapterTask())) { - return false; - } - return isTaskRunningOrQueued(context.asrTask()) || isTaskCompleted(context.asrTask()); + private String resolveMessage(MeetingVO meeting, MeetingProgressSnapshot snapshot, UnifiedMeetingStatusStage stage) { + if (stage == UnifiedMeetingStatusStage.WAITING_UPLOAD) { + return "待上传录音文件"; } + if (snapshot != null && snapshot.getMessage() != null && !snapshot.getMessage().isBlank() && !Objects.equals(snapshot.getMessage(), "Waiting...")) { + return snapshot.getMessage(); + } + if (stage == UnifiedMeetingStatusStage.FAILED_INITIALIZING + || stage == UnifiedMeetingStatusStage.FAILED_TRANSCRIBING + || stage == UnifiedMeetingStatusStage.FAILED_SUMMARIZING) { + return resolveFailureMessage(meeting); + } + return switch (stage) { + case WAITING_UPLOAD -> "待上传录音文件"; + case INITIALIZING -> "数据初始化"; + case TRANSCRIBING -> "转译音频"; + case SUMMARIZING -> "生成总结"; + case COMPLETED -> "处理完成"; + case FAILED_INITIALIZING -> "数据初始化失败"; + case FAILED_TRANSCRIBING -> "转译音频失败"; + case FAILED_SUMMARIZING -> "生成总结失败"; + }; + } - private Integer resolvePercent(MeetingProgressSnapshot snapshot, UnifiedMeetingStatusStage stage) { - if (snapshot != null && snapshot.getPercent() != null) { - return snapshot.getPercent(); - } - return switch (stage) { - case WAITING_UPLOAD -> 0; - case INITIALIZING -> 5; - case TRANSCRIBING -> 50; - case SUMMARIZING -> 90; - case COMPLETED -> 100; - case FAILED_INITIALIZING, FAILED_TRANSCRIBING, FAILED_SUMMARIZING -> -1; - }; + private String resolveFailureMessage(MeetingVO meeting) { + if (meeting == null) { + return "处理失败"; } + if (meeting.getLatestSummaryAttemptErrorMsg() != null && !meeting.getLatestSummaryAttemptErrorMsg().isBlank()) { + return meeting.getLatestSummaryAttemptErrorMsg(); + } + if (meeting.getLatestChapterAttemptErrorMsg() != null && !meeting.getLatestChapterAttemptErrorMsg().isBlank()) { + return meeting.getLatestChapterAttemptErrorMsg(); + } + return "处理失败"; + } - private String resolveMessage(MeetingVO meeting, MeetingProgressSnapshot snapshot, UnifiedMeetingStatusStage stage) { - if (stage == UnifiedMeetingStatusStage.WAITING_UPLOAD) { - return "待上传录音文件"; - } - if (snapshot != null && snapshot.getMessage() != null && !snapshot.getMessage().isBlank() && !Objects.equals(snapshot.getMessage(), "Waiting...")) { - return snapshot.getMessage(); - } - if (stage == UnifiedMeetingStatusStage.FAILED_INITIALIZING - || stage == UnifiedMeetingStatusStage.FAILED_TRANSCRIBING - || stage == UnifiedMeetingStatusStage.FAILED_SUMMARIZING) { - return resolveFailureMessage(meeting); - } - return switch (stage) { - case WAITING_UPLOAD -> "待上传录音文件"; - case INITIALIZING -> "数据初始化"; - case TRANSCRIBING -> "转译音频"; - case SUMMARIZING -> "生成总结"; - case COMPLETED -> "处理完成"; - case FAILED_INITIALIZING -> "数据初始化失败"; - case FAILED_TRANSCRIBING -> "转译音频失败"; - case FAILED_SUMMARIZING -> "生成总结失败"; - }; - } + private boolean canViewTranscript(Long meetingId) { + return meetingId != null && meetingTranscriptMapper.selectCount(new LambdaQueryWrapper() + .eq(MeetingTranscript::getMeetingId, meetingId)) > 0; + } - private String resolveFailureMessage(MeetingVO meeting) { - if (meeting == null) { - return "处理失败"; - } - if (meeting.getLatestSummaryAttemptErrorMsg() != null && !meeting.getLatestSummaryAttemptErrorMsg().isBlank()) { - return meeting.getLatestSummaryAttemptErrorMsg(); - } - if (meeting.getLatestChapterAttemptErrorMsg() != null && !meeting.getLatestChapterAttemptErrorMsg().isBlank()) { - return meeting.getLatestChapterAttemptErrorMsg(); - } - return "处理失败"; - } + private boolean canViewAiChapters(Long meetingId) { + return meetingId != null && chapterVersionMapper.selectCount(new LambdaQueryWrapper() + .eq(MeetingTranscriptChapterVersion::getMeetingId, meetingId) + .eq(MeetingTranscriptChapterVersion::getIsCurrent, 1) + .eq(MeetingTranscriptChapterVersion::getStatus, 2)) > 0; + } - private boolean canViewTranscript(Long meetingId) { - return meetingId != null && meetingTranscriptMapper.selectCount(new LambdaQueryWrapper() - .eq(MeetingTranscript::getMeetingId, meetingId)) > 0; - } + private boolean canViewSummary(MeetingVO meeting) { + return meeting != null && meeting.getSummaryContent() != null && !meeting.getSummaryContent().isBlank(); + } - private boolean canViewAiChapters(Long meetingId) { - return meetingId != null && chapterVersionMapper.selectCount(new LambdaQueryWrapper() - .eq(MeetingTranscriptChapterVersion::getMeetingId, meetingId) - .eq(MeetingTranscriptChapterVersion::getIsCurrent, 1) - .eq(MeetingTranscriptChapterVersion::getStatus, 2)) > 0; - } + private AiTask findLatestTask(Long meetingId, String taskType) { + return aiTaskMapper.selectOne(new LambdaQueryWrapper() + .eq(AiTask::getMeetingId, meetingId) + .eq(AiTask::getTaskType, taskType) + .orderByDesc(AiTask::getId) + .last("LIMIT 1")); + } - private boolean canViewSummary(MeetingVO meeting) { - return meeting != null && meeting.getSummaryContent() != null && !meeting.getSummaryContent().isBlank(); - } + private boolean isTaskRunningOrQueued(AiTask task) { + return task != null && (Integer.valueOf(0).equals(task.getStatus()) || Integer.valueOf(1).equals(task.getStatus())); + } - private AiTask findLatestTask(Long meetingId, String taskType) { - return aiTaskMapper.selectOne(new LambdaQueryWrapper() - .eq(AiTask::getMeetingId, meetingId) - .eq(AiTask::getTaskType, taskType) - .orderByDesc(AiTask::getId) - .last("LIMIT 1")); - } + private boolean isTaskRunning(AiTask task) { + return task != null && Integer.valueOf(1).equals(task.getStatus()); + } - private boolean isTaskRunningOrQueued(AiTask task) { - return task != null && (Integer.valueOf(0).equals(task.getStatus()) || Integer.valueOf(1).equals(task.getStatus())); - } + private boolean isTaskCompleted(AiTask task) { + return task != null && Integer.valueOf(2).equals(task.getStatus()); + } - private boolean isTaskRunning(AiTask task) { - return task != null && Integer.valueOf(1).equals(task.getStatus()); - } + private boolean isTaskFailed(AiTask task) { + return task != null && Integer.valueOf(3).equals(task.getStatus()); + } - private boolean isTaskCompleted(AiTask task) { - return task != null && Integer.valueOf(2).equals(task.getStatus()); - } + private MeetingVO toMeetingVO(Meeting meeting) { + MeetingVO vo = new MeetingVO(); + vo.setId(meeting.getId()); + vo.setTenantId(meeting.getTenantId()); + vo.setCreatorId(meeting.getCreatorId()); + vo.setCreatorName(meeting.getCreatorName()); + vo.setHostUserId(meeting.getHostUserId()); + vo.setHostName(meeting.getHostName()); + vo.setTitle(meeting.getTitle()); + vo.setMeetingTime(meeting.getMeetingTime()); + vo.setParticipants(meeting.getParticipants()); + vo.setTags(meeting.getTags()); + vo.setAudioUrl(meeting.getAudioUrl()); + vo.setMeetingType(meeting.getMeetingType()); + vo.setMeetingSource(meeting.getMeetingSource()); + vo.setSourceDeviceCode(meeting.getSourceDeviceCode()); + vo.setSourceDeviceMode(meeting.getSourceDeviceMode()); + vo.setOfflineRecordingStatus(meeting.getOfflineRecordingStatus()); + vo.setSummaryDetailLevel(meeting.getSummaryDetailLevel()); + vo.setAudioSaveStatus(meeting.getAudioSaveStatus()); + vo.setAudioSaveMessage(meeting.getAudioSaveMessage()); + vo.setAccessPassword(meeting.getAccessPassword()); + vo.setEffectiveAudioDurationSeconds(meeting.getEffectiveAudioDurationSeconds()); + vo.setStatus(meeting.getStatus()); + vo.setCreatedAt(meeting.getCreatedAt()); + return vo; + } - private boolean isTaskFailed(AiTask task) { - return task != null && Integer.valueOf(3).equals(task.getStatus()); - } - - private MeetingVO toMeetingVO(Meeting meeting) { - MeetingVO vo = new MeetingVO(); - vo.setId(meeting.getId()); - vo.setTenantId(meeting.getTenantId()); - vo.setCreatorId(meeting.getCreatorId()); - vo.setCreatorName(meeting.getCreatorName()); - vo.setHostUserId(meeting.getHostUserId()); - vo.setHostName(meeting.getHostName()); - vo.setTitle(meeting.getTitle()); - vo.setMeetingTime(meeting.getMeetingTime()); - vo.setParticipants(meeting.getParticipants()); - vo.setTags(meeting.getTags()); - vo.setAudioUrl(meeting.getAudioUrl()); - vo.setMeetingType(meeting.getMeetingType()); - vo.setMeetingSource(meeting.getMeetingSource()); - vo.setSourceDeviceCode(meeting.getSourceDeviceCode()); - vo.setSourceDeviceMode(meeting.getSourceDeviceMode()); - vo.setOfflineRecordingStatus(meeting.getOfflineRecordingStatus()); - vo.setSummaryDetailLevel(meeting.getSummaryDetailLevel()); - vo.setAudioSaveStatus(meeting.getAudioSaveStatus()); - vo.setAudioSaveMessage(meeting.getAudioSaveMessage()); - vo.setAccessPassword(meeting.getAccessPassword()); - vo.setEffectiveAudioDurationSeconds(meeting.getEffectiveAudioDurationSeconds()); - vo.setStatus(meeting.getStatus()); - vo.setCreatedAt(meeting.getCreatedAt()); - return vo; - } - - private record MeetingUnifiedStageContext(AiTask asrTask, - AiTask chapterTask, - AiTask summaryTask, - MeetingProgressSnapshot snapshot) { - } + private record MeetingUnifiedStageContext(AiTask asrTask, + AiTask chapterTask, + AiTask summaryTask, + MeetingProgressSnapshot snapshot) { + } } diff --git a/imeeting-h5/src/App.tsx b/imeeting-h5/src/App.tsx index 842760d..acb259a 100644 --- a/imeeting-h5/src/App.tsx +++ b/imeeting-h5/src/App.tsx @@ -12,7 +12,6 @@ const MeetingDetailPage = lazy(() => import("@/pages/meeting-detail")); const MeetingPreviewPage = lazy(() => import("@/pages/meeting-preview")); const ProfilePage = lazy(() => import("@/pages/profile")); const PasswordPage = lazy(() => import("@/pages/password")); -const AboutPage = lazy(() => import("@/pages/about")); const ScanConfirmPage = lazy(() => import("@/pages/scan-confirm")); function HomeRedirect() { @@ -67,16 +66,6 @@ export default function App() { } /> - - - - - - } - /> - - - {platformConfig?.projectName || "iMeeting H5"} - - iMeeting H5 是面向移动端的会议查看入口,提供登录、我的会议、会议详情、分享预览、安卓扫码确认与个人中心等能力。 - - - 第一版页面以快速访问和移动端阅读体验为优先,聚焦“查看”和“确认”两类核心动作,不混入后台管理功能。 - - - 联系邮箱:support@imeeting.example.com - - {platformConfig?.copyrightInfo || "Copyright © iMeeting"} - - - ); -} diff --git a/imeeting-h5/src/pages/profile/index.tsx b/imeeting-h5/src/pages/profile/index.tsx index 0a85dec..9bd6f91 100644 --- a/imeeting-h5/src/pages/profile/index.tsx +++ b/imeeting-h5/src/pages/profile/index.tsx @@ -1,4 +1,4 @@ -import { App, Avatar, Button, Card, Space, Typography } from "antd"; +import {App, Avatar, Button, Card, Modal, Space, Typography} from "antd"; import { InfoCircleOutlined, LockOutlined, LogoutOutlined, RightOutlined } from "@ant-design/icons"; import { useEffect, useState, type ReactNode } from "react"; import { useNavigate } from "react-router-dom"; @@ -6,6 +6,7 @@ import { useNavigate } from "react-router-dom"; import { getCurrentUser } from "@/api/user"; import LoadingScreen from "@/components/LoadingScreen"; import PageHeader from "@/components/PageHeader"; +import {usePlatformConfig} from "@/components/PlatformConfigProvider"; import usePageTitle from "@/hooks/usePageTitle"; import type { UserProfile } from "@/types"; import { clearAuth, saveProfile } from "@/utils/auth"; @@ -38,9 +39,11 @@ function ProfileAction({ export default function ProfilePage() { const { message } = App.useApp(); const navigate = useNavigate(); + const {platformConfig} = usePlatformConfig(); usePageTitle("个人中心"); const [loading, setLoading] = useState(true); const [profile, setProfile] = useState(null); + const [aboutVisible, setAboutVisible] = useState(false); useEffect(() => { const loadProfile = async () => { @@ -95,7 +98,7 @@ export default function ProfilePage() { } label="个人设置" onClick={() => navigate("/profile/password")} /> - } label="关于我们" onClick={() => navigate("/about")} /> + } label="关于我们" onClick={() => setAboutVisible(true)}/> } label="退出当前账号" onClick={handleLogout} danger /> @@ -103,6 +106,40 @@ export default function ProfilePage() { 登录用户可直接查看自己的会议详情。分享给外部访问者时,可在会议详情中单独设置访问密码。 + + setAboutVisible(false)} + footer={null} + centered + width="90%" + styles={{body: {textAlign: "center", padding: "24px 16px"}}} + > + {/*logo*/} + + {platformConfig?.projectName || "iMeeting H5"} + + {/**/} + {/* 版本号:v0.1.0*/} + {/**/} + + 智听云由紫光汇紫信息技术有限公司倾力打 + 造,是一款专注企业级智能会议体验的AI助 + 手。我们支持实时语音转写、内容智能摘要 + 与会议纪要自动生成,显著提升会议效率与 + 信息沉淀能力。 + + {/**/} + {/* 第一版页面以快速访问和移动端阅读体验为优先,聚焦“查看”和“确认”两类核心动作,不混入后台管理功能。*/} + {/**/} + {/**/} + {/* 联系邮箱:support@imeeting.example.com*/} + {/**/} + + {platformConfig?.copyrightInfo || "Copyright © iMeeting"} + + ); }