From 2f80c6c55e60be689b38924e05c02c1c0e9af27d Mon Sep 17 00:00:00 2001 From: chenhao Date: Fri, 24 Apr 2026 14:27:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=B6=88=E6=81=AF=E4=B8=BA=E4=B8=AD=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将多个服务和控制器中的英文错误消息替换为中文 - 更新测试用例以匹配新的错误消息 --- .../grpc/GrpcExceptionLoggingInterceptor.java | 2 +- .../android/AndroidMeetingController.java | 2 +- .../android/AndroidPromptController.java | 2 +- .../legacy/LegacyPromptController.java | 2 +- .../controller/biz/AiModelController.java | 6 +-- .../controller/biz/MeetingController.java | 6 +-- .../biz/PromptTemplateController.java | 12 ++--- .../controller/biz/ScreenSaverController.java | 2 +- .../dto/biz/CreateMeetingCommand.java | 4 +- .../dto/biz/CreateRealtimeMeetingCommand.java | 2 +- .../gateway/AndroidGatewayGrpcService.java | 2 +- .../realtime/RealtimeMeetingGrpcService.java | 6 +-- .../android/impl/AndroidAuthServiceImpl.java | 20 ++++---- .../impl/AndroidDeviceSessionServiceImpl.java | 4 +- .../impl/LegacyMeetingAdapterServiceImpl.java | 18 +++---- .../service/biz/impl/AiModelServiceImpl.java | 30 +++++------ .../service/biz/impl/AiTaskServiceImpl.java | 24 ++++----- .../biz/impl/ClientDownloadServiceImpl.java | 14 +++--- .../biz/impl/ExternalAppServiceImpl.java | 10 ++-- .../biz/impl/MeetingAccessServiceImpl.java | 28 +++++------ .../biz/impl/MeetingAudioUploadSupport.java | 2 +- .../impl/MeetingAuthorizationServiceImpl.java | 4 +- .../biz/impl/MeetingCommandServiceImpl.java | 18 +++---- .../biz/impl/MeetingDomainSupport.java | 6 +-- .../biz/impl/MeetingExportServiceImpl.java | 6 +-- .../impl/MeetingSummaryFileServiceImpl.java | 12 ++--- ...ealtimeMeetingSessionStateServiceImpl.java | 10 ++-- ...altimeMeetingSocketSessionServiceImpl.java | 12 ++--- .../biz/impl/ScreenSaverServiceImpl.java | 44 ++++++++-------- .../service/biz/impl/SpeakerServiceImpl.java | 4 +- ...droidRealtimeSessionTicketServiceImpl.java | 14 +++--- ...RealtimeMeetingGrpcSessionServiceImpl.java | 8 +-- .../RealtimeMeetingProxyWebSocketHandler.java | 8 +-- .../RealtimeMeetingGrpcServiceTest.java | 8 +-- .../biz/impl/AiTaskServiceImplTest.java | 4 +- frontend/src/routes/routes.tsx | 50 +++++++++---------- 36 files changed, 203 insertions(+), 203 deletions(-) diff --git a/backend/src/main/java/com/imeeting/config/grpc/GrpcExceptionLoggingInterceptor.java b/backend/src/main/java/com/imeeting/config/grpc/GrpcExceptionLoggingInterceptor.java index f0d68be..d003a88 100644 --- a/backend/src/main/java/com/imeeting/config/grpc/GrpcExceptionLoggingInterceptor.java +++ b/backend/src/main/java/com/imeeting/config/grpc/GrpcExceptionLoggingInterceptor.java @@ -72,6 +72,6 @@ public class GrpcExceptionLoggingInterceptor implements ServerInterceptor { if (!closed.compareAndSet(false, true)) { return; } - call.close(Status.UNKNOWN.withDescription("Application error processing RPC").withCause(ex), new Metadata()); + call.close(Status.UNKNOWN.withDescription("应用处理 RPC 时发生异常").withCause(ex), new Metadata()); } } 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 7b3351f..ec7538a 100644 --- a/backend/src/main/java/com/imeeting/controller/android/AndroidMeetingController.java +++ b/backend/src/main/java/com/imeeting/controller/android/AndroidMeetingController.java @@ -107,7 +107,7 @@ public class AndroidMeetingController { }) @PostMapping("/upload-audio") public ApiResponse uploadAudio(HttpServletRequest request, - @RequestParam("meeting_id") Long meetingId, + @RequestParam("id") Long meetingId, @RequestParam(value = "prompt_id", required = false) Long promptId, @RequestParam(value = "model_code", required = false) String modelCode, @RequestParam(value = "force_replace", defaultValue = "false") boolean forceReplace, diff --git a/backend/src/main/java/com/imeeting/controller/android/AndroidPromptController.java b/backend/src/main/java/com/imeeting/controller/android/AndroidPromptController.java index 79e1586..0131b97 100644 --- a/backend/src/main/java/com/imeeting/controller/android/AndroidPromptController.java +++ b/backend/src/main/java/com/imeeting/controller/android/AndroidPromptController.java @@ -44,7 +44,7 @@ public class AndroidPromptController { @GetMapping("/active/{scene}") public ApiResponse> activePrompts(HttpServletRequest request, @PathVariable String scene) { if (!LEGACY_MEETING_SCENE.equals(scene)) { - return ApiResponse.error("scene only supports MEETING_TASK"); + return ApiResponse.error("scene 仅支持 MEETING_TASK"); } AndroidAuthContext authContext = androidAuthService.authenticateHttp(request); diff --git a/backend/src/main/java/com/imeeting/controller/android/legacy/LegacyPromptController.java b/backend/src/main/java/com/imeeting/controller/android/legacy/LegacyPromptController.java index 9bcfa4e..f677820 100644 --- a/backend/src/main/java/com/imeeting/controller/android/legacy/LegacyPromptController.java +++ b/backend/src/main/java/com/imeeting/controller/android/legacy/LegacyPromptController.java @@ -35,7 +35,7 @@ public class LegacyPromptController { @PreAuthorize("isAuthenticated()") public LegacyApiResponse activePrompts(@PathVariable String scene) { if (!LEGACY_MEETING_SCENE.equals(scene)) { - return LegacyApiResponse.error("400", "scene only supports MEETING_TASK"); + return LegacyApiResponse.error("400", "scene 仅支持 MEETING_TASK"); } LoginUser loginUser = currentLoginUser(); diff --git a/backend/src/main/java/com/imeeting/controller/biz/AiModelController.java b/backend/src/main/java/com/imeeting/controller/biz/AiModelController.java index 1f5889f..9330f35 100644 --- a/backend/src/main/java/com/imeeting/controller/biz/AiModelController.java +++ b/backend/src/main/java/com/imeeting/controller/biz/AiModelController.java @@ -105,10 +105,10 @@ public class AiModelController { @PreAuthorize("isAuthenticated()") public ApiResponse testLocalConnectivity(@RequestBody AiModelDTO dto) { if (dto.getBaseUrl() == null || dto.getBaseUrl().isBlank()) { - return ApiResponse.error("Base URL不能为空"); + return ApiResponse.error("基础地址不能为空"); } if (dto.getApiKey() == null || dto.getApiKey().isBlank()) { - return ApiResponse.error("API Key不能为空"); + return ApiResponse.error("API 密钥不能为空"); } return ApiResponse.ok(aiModelService.testLocalConnectivity(dto.getBaseUrl(), dto.getApiKey())); } @@ -118,7 +118,7 @@ public class AiModelController { @PreAuthorize("isAuthenticated()") public ApiResponse testLlmConnectivity(@RequestBody AiModelDTO dto) { if ("custom".equalsIgnoreCase(dto.getProvider()) && (dto.getBaseUrl() == null || dto.getBaseUrl().isBlank())) { - return ApiResponse.error("Base URL不能为空"); + return ApiResponse.error("基础地址不能为空"); } if (dto.getModelCode() == null || dto.getModelCode().isBlank()) { return ApiResponse.error("模型名称不能为空"); diff --git a/backend/src/main/java/com/imeeting/controller/biz/MeetingController.java b/backend/src/main/java/com/imeeting/controller/biz/MeetingController.java index 8613803..826dda0 100644 --- a/backend/src/main/java/com/imeeting/controller/biz/MeetingController.java +++ b/backend/src/main/java/com/imeeting/controller/biz/MeetingController.java @@ -107,7 +107,7 @@ public class MeetingController { try { return ApiResponse.ok(new com.fasterxml.jackson.databind.ObjectMapper().readValue(json, Map.class)); } catch (Exception ex) { - return ApiResponse.error("Progress parse failed"); + return ApiResponse.error("进度解析失败"); } } @@ -206,7 +206,7 @@ public class MeetingController { MeetingVO meetingDetail = meetingQueryService.getDetail(id); if (meetingDetail == null) { - throw new RuntimeException("Meeting not found"); + throw new RuntimeException("会议不存在"); } MeetingSummaryExportResult exportResult = meetingExportService.exportSummary(meeting, meetingDetail, format); @@ -437,7 +437,7 @@ public class MeetingController { loginUser.getIsTenantAdmin() ); if (!enabled) { - throw new RuntimeException("Summary template unavailable"); + throw new RuntimeException("总结模板不可用"); } } diff --git a/backend/src/main/java/com/imeeting/controller/biz/PromptTemplateController.java b/backend/src/main/java/com/imeeting/controller/biz/PromptTemplateController.java index b988e87..98a1bc4 100644 --- a/backend/src/main/java/com/imeeting/controller/biz/PromptTemplateController.java +++ b/backend/src/main/java/com/imeeting/controller/biz/PromptTemplateController.java @@ -35,7 +35,7 @@ public class PromptTemplateController { if (Integer.valueOf(1).equals(dto.getIsSystem())) { if (!Boolean.TRUE.equals(loginUser.getIsPlatformAdmin()) && !Boolean.TRUE.equals(loginUser.getIsTenantAdmin())) { - return ApiResponse.error("No permission to create public template"); + return ApiResponse.error("无权创建公共模板"); } if (!Boolean.TRUE.equals(loginUser.getIsPlatformAdmin())) { dto.setTenantId(loginUser.getTenantId()); @@ -56,7 +56,7 @@ public class PromptTemplateController { LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); PromptTemplate existing = promptTemplateService.getById(dto.getId()); if (existing == null) { - return ApiResponse.error("Template not found"); + return ApiResponse.error("模板不存在"); } boolean canModify = false; @@ -71,7 +71,7 @@ public class PromptTemplateController { } if (!canModify) { - return ApiResponse.error("No permission to modify this template"); + return ApiResponse.error("无权修改该模板"); } return ApiResponse.ok(promptTemplateService.updateTemplate(dto, loginUser.getUserId(), loginUser.getTenantId())); @@ -84,7 +84,7 @@ public class PromptTemplateController { LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); PromptTemplate existing = promptTemplateService.getById(id); if (existing == null) { - return ApiResponse.error("Template not found"); + return ApiResponse.error("模板不存在"); } boolean canGlobalModify = false; @@ -110,7 +110,7 @@ public class PromptTemplateController { loginUser.getIsTenantAdmin() ); if (!success) { - return ApiResponse.error("Template not found or no permission"); + return ApiResponse.error("模板不存在或无权限访问"); } return ApiResponse.ok(true); } @@ -137,7 +137,7 @@ public class PromptTemplateController { } if (!canModify) { - return ApiResponse.error("No permission to delete this template"); + return ApiResponse.error("无权删除该模板"); } return ApiResponse.ok(promptTemplateService.removeById(id)); diff --git a/backend/src/main/java/com/imeeting/controller/biz/ScreenSaverController.java b/backend/src/main/java/com/imeeting/controller/biz/ScreenSaverController.java index b57e9b2..6ce21f9 100644 --- a/backend/src/main/java/com/imeeting/controller/biz/ScreenSaverController.java +++ b/backend/src/main/java/com/imeeting/controller/biz/ScreenSaverController.java @@ -80,7 +80,7 @@ public class ScreenSaverController { public ApiResponse updateStatus(@PathVariable Long id, @RequestParam Integer status) { boolean success = screenSaverService.updateStatus(id, status, currentLoginUser()); if (!success) { - return ApiResponse.error("Screen saver not found or no permission"); + return ApiResponse.error("屏保不存在或无权限访问"); } return ApiResponse.ok(true); } diff --git a/backend/src/main/java/com/imeeting/dto/biz/CreateMeetingCommand.java b/backend/src/main/java/com/imeeting/dto/biz/CreateMeetingCommand.java index b8b4f5a..c253a84 100644 --- a/backend/src/main/java/com/imeeting/dto/biz/CreateMeetingCommand.java +++ b/backend/src/main/java/com/imeeting/dto/biz/CreateMeetingCommand.java @@ -11,7 +11,7 @@ import java.util.List; @Data public class CreateMeetingCommand { - @NotBlank(message = "title must not be blank") + @NotBlank(message = "标题不能为空") private String title; @NotNull(message = "meetingTime must not be null") @@ -23,7 +23,7 @@ public class CreateMeetingCommand { private Long hostUserId; private String hostName; - @NotBlank(message = "audioUrl must not be blank") + @NotBlank(message = "音频地址不能为空") private String audioUrl; @NotNull(message = "asrModelId must not be null") diff --git a/backend/src/main/java/com/imeeting/dto/biz/CreateRealtimeMeetingCommand.java b/backend/src/main/java/com/imeeting/dto/biz/CreateRealtimeMeetingCommand.java index 844d17f..2283a76 100644 --- a/backend/src/main/java/com/imeeting/dto/biz/CreateRealtimeMeetingCommand.java +++ b/backend/src/main/java/com/imeeting/dto/biz/CreateRealtimeMeetingCommand.java @@ -11,7 +11,7 @@ import java.util.List; @Data public class CreateRealtimeMeetingCommand { - @NotBlank(message = "title must not be blank") + @NotBlank(message = "标题不能为空") private String title; @NotNull(message = "meetingTime must not be null") diff --git a/backend/src/main/java/com/imeeting/grpc/gateway/AndroidGatewayGrpcService.java b/backend/src/main/java/com/imeeting/grpc/gateway/AndroidGatewayGrpcService.java index 1a09b02..3ec68ed 100644 --- a/backend/src/main/java/com/imeeting/grpc/gateway/AndroidGatewayGrpcService.java +++ b/backend/src/main/java/com/imeeting/grpc/gateway/AndroidGatewayGrpcService.java @@ -77,7 +77,7 @@ public class AndroidGatewayGrpcService extends AndroidGatewayServiceGrpc.Android } AndroidDeviceSessionState state = androidDeviceSessionService.refreshHeartbeat(connectionId, heartbeat.getClientTime()); if (state == null) { - sendError(responseObserver, "Android device session not found"); + sendError(responseObserver, "未找到安卓设备会话"); return; } responseObserver.onNext(GatewayServerPacket.newBuilder() diff --git a/backend/src/main/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcService.java b/backend/src/main/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcService.java index 1ffaaed..ed3bd43 100644 --- a/backend/src/main/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcService.java +++ b/backend/src/main/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcService.java @@ -72,7 +72,7 @@ public class RealtimeMeetingGrpcService extends RealtimeMeetingServiceGrpc.Realt private void handleAudio(AudioChunk audioChunk) { if (connectionId == null) { - throw new RuntimeException("Realtime gRPC stream is not opened"); + throw new RuntimeException("实时 gRPC 流未打开"); } realtimeMeetingGrpcSessionService.onAudio(connectionId, audioChunk.getPcm16().toByteArray(), audioChunk.getSeq(), audioChunk.getLastChunk()); } @@ -107,7 +107,7 @@ public class RealtimeMeetingGrpcService extends RealtimeMeetingServiceGrpc.Realt .setRequestId(requestId == null ? "" : requestId) .setError(ErrorEvent.newBuilder() .setCode("REALTIME_GRPC_ERROR") - .setMessage(message == null || message.isBlank() ? "Realtime meeting gRPC processing failed" : message) + .setMessage(message == null || message.isBlank() ? "实时会议 gRPC 处理失败" : message) .setRetryable(false) .build()) .build()); @@ -136,4 +136,4 @@ public class RealtimeMeetingGrpcService extends RealtimeMeetingServiceGrpc.Realt } }; } -} \ No newline at end of file +} 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 97e0a4f..f1a55f8 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 @@ -41,7 +41,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { auth == null ? null : auth.getAppVersion(), auth == null ? null : auth.getPlatform(), auth == null ? null : auth.getAccessToken(), fallbackDeviceId, null, null); } if (properties.isEnabled() && !properties.isAllowAnonymous()) { - throw new RuntimeException("Android gRPC auth is required"); + throw new RuntimeException("缺少 Android gRPC 认证信息"); } return buildContext("NONE", true, auth == null ? null : auth.getDeviceId(), @@ -100,7 +100,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { null, null); } - throw new RuntimeException("Android HTTP access token is required"); + throw new RuntimeException("缺少 Android HTTP 访问令牌"); } private AndroidAuthContext buildContext(String authMode, boolean anonymous, String deviceId, @@ -108,7 +108,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { String fallbackDeviceId, InternalAuthCheckResponse authResult, LoginUser loginUser) { String resolvedDeviceId = StringUtils.hasText(deviceId) ? deviceId : fallbackDeviceId; if (!StringUtils.hasText(resolvedDeviceId)) { - throw new RuntimeException("Android deviceId is required"); + throw new RuntimeException("缺少 Android deviceId"); } AndroidAuthContext context = new AndroidAuthContext(); context.setAuthMode(authMode); @@ -148,14 +148,14 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { private InternalAuthCheckResponse validateToken(String token) { String resolvedToken = normalizeToken(token); if (!StringUtils.hasText(resolvedToken)) { - throw new RuntimeException("Android access token is required"); + throw new RuntimeException("缺少 Android 访问令牌"); } InternalAuthCheckResponse authResult = tokenValidationService.validateAccessToken(resolvedToken); if (authResult == null || !authResult.isValid()) { - throw new RuntimeException(authResult == null || !StringUtils.hasText(authResult.getMessage()) ? "Android access token is invalid" : authResult.getMessage()); + throw new RuntimeException(authResult == null || !StringUtils.hasText(authResult.getMessage()) ? "Android 访问令牌无效" : authResult.getMessage()); } if (authResult.getUserId() == null || authResult.getTenantId() == null) { - throw new RuntimeException("Android access token missing user or tenant context"); + throw new RuntimeException("Android 访问令牌缺少用户或租户上下文"); } return authResult; } @@ -166,7 +166,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { return null; } if (!authorization.startsWith(BEARER_PREFIX)) { - throw new RuntimeException("Android HTTP access token is invalid"); + throw new RuntimeException("Android HTTP 访问令牌无效"); } return authorization.substring(BEARER_PREFIX.length()).trim(); } @@ -183,13 +183,13 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { private void requireAndroidHttpHeaders(String deviceId, String appVersion, String platform) { if (!StringUtils.hasText(deviceId)) { - throw new RuntimeException("Android device_id is required"); + throw new RuntimeException("缺少 Android device_id"); } if (!StringUtils.hasText(appVersion)) { - throw new RuntimeException("Android-App-Version is required"); + throw new RuntimeException("缺少 Android-App-Version 请求头"); } if (!StringUtils.hasText(platform)) { - throw new RuntimeException("X-Android-Platform is required"); + throw new RuntimeException("缺少 X-Android-Platform 请求头"); } } diff --git a/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceSessionServiceImpl.java b/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceSessionServiceImpl.java index ffc7f06..cf4b15c 100644 --- a/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceSessionServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceSessionServiceImpl.java @@ -73,7 +73,7 @@ public class AndroidDeviceSessionServiceImpl implements AndroidDeviceSessionServ objectMapper.writeValueAsString(topics == null ? List.of() : topics) ); } catch (Exception ex) { - throw new RuntimeException("Failed to update android device topics", ex); + throw new RuntimeException("更新安卓设备主题失败", ex); } } @@ -100,7 +100,7 @@ public class AndroidDeviceSessionServiceImpl implements AndroidDeviceSessionServ redisTemplate.opsForValue().set(RedisKeys.androidDeviceActiveConnectionKey(state.getDeviceId()), state.getConnectionId(), ttl); redisTemplate.opsForValue().set(RedisKeys.androidDeviceConnectionKey(state.getConnectionId()), json, ttl); } catch (Exception ex) { - throw new RuntimeException("Failed to write android device session state", ex); + throw new RuntimeException("写入安卓设备会话状态失败", ex); } } 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 13cad9f..3253f51 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 @@ -61,11 +61,11 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ @Transactional(rollbackFor = Exception.class) public MeetingVO createMeeting(LegacyMeetingCreateRequest request, LoginUser loginUser) { if (request == null || request.getTitle() == null || request.getTitle().isBlank()) { - throw new RuntimeException("Meeting title cannot be empty"); + throw new RuntimeException("会议标题不能为空"); } LocalDateTime meetingTime = parseMeetingTime(request.getMeetingTime()); if (meetingTime == null) { - throw new RuntimeException("Meeting time cannot be empty"); + throw new RuntimeException("会议时间不能为空"); } Meeting meeting = meetingDomainSupport.initMeeting( @@ -99,22 +99,22 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ MultipartFile audioFile, LoginUser loginUser) throws IOException { if (meetingId == null) { - throw new RuntimeException("meeting_id cannot be empty"); + throw new RuntimeException("meeting_id 不能为空"); } if (audioFile == null || audioFile.isEmpty()) { - throw new RuntimeException("audio_file cannot be empty"); + throw new RuntimeException("audio_file 不能为空"); } Meeting meeting = meetingAccessService.requireMeeting(meetingId); meetingAccessService.assertCanEditMeeting(meeting, loginUser); if (!forceReplace && meeting.getAudioUrl() != null && !meeting.getAudioUrl().isBlank()) { - throw new RuntimeException("Meeting already has audio, set force_replace=true to replace it"); + throw new RuntimeException("当前会议已存在音频,如需替换请设置 force_replace=true"); } long transcriptCount = transcriptMapper.selectCount(new LambdaQueryWrapper() .eq(MeetingTranscript::getMeetingId, meetingId)); if (transcriptCount > 0) { - throw new RuntimeException("Current meeting already has transcripts; legacy Android upload does not support replacing generated transcripts"); + throw new RuntimeException("当前会议已存在转录内容,旧版 Android 上传不支持替换已生成的转录"); } if (promptId != null && !promptTemplateService.isTemplateEnabledForUser( promptId, @@ -122,7 +122,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ loginUser.getUserId(), loginUser.getIsPlatformAdmin(), loginUser.getIsTenantAdmin())) { - throw new RuntimeException("Summary template unavailable"); + throw new RuntimeException("总结模板不可用"); } RealtimeMeetingRuntimeProfile profile = runtimeProfileResolver.resolve( @@ -183,7 +183,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ try { return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } catch (DateTimeParseException ex) { - throw new RuntimeException("Meeting time format is invalid"); + throw new RuntimeException("会议时间格式无效"); } } @@ -226,7 +226,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ .orderByDesc(LlmModel::getTenantId) .last("LIMIT 1")); if (model == null) { - throw new RuntimeException("LLM model not found or disabled: " + modelCode); + throw new RuntimeException("LLM 模型不存在或已禁用: " + modelCode); } return model.getId(); } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/AiModelServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/AiModelServiceImpl.java index 6073382..e80426a 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/AiModelServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/AiModelServiceImpl.java @@ -99,7 +99,7 @@ public class AiModelServiceImpl implements AiModelService { if (TYPE_ASR.equals(type)) { AsrModel entity = asrModelMapper.selectById(dto.getId()); if (entity == null) { - throw new RuntimeException("Model not found"); + throw new RuntimeException("模型不存在"); } copyAsrProperties(dto, entity); pushAsrConfig(entity); @@ -111,7 +111,7 @@ public class AiModelServiceImpl implements AiModelService { LlmModel entity = llmModelMapper.selectById(dto.getId()); if (entity == null) { - throw new RuntimeException("Model not found"); + throw new RuntimeException("模型不存在"); } copyLlmProperties(dto, entity); handleLlmDefaultLogic(entity); @@ -357,14 +357,14 @@ public class AiModelServiceImpl implements AiModelService { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() < 200 || response.statusCode() >= 300) { - throw new RuntimeException("HTTP " + response.statusCode() + ", body=" + response.body()); + throw new RuntimeException("HTTP 状态异常:" + response.statusCode() + ", body=" + response.body()); } JsonNode root = objectMapper.readTree(response.body()); String responseContent = readConnectivityResponseContent(root); String resultMessage = extractConnectivityResultMessage(responseContent); if (resultMessage == null || resultMessage.isBlank()) { - throw new RuntimeException("Unexpected empty connectivity response, body=" + response.body()); + throw new RuntimeException("连通性测试返回空响应,body=" + response.body()); } } catch (Exception e) { String detail = describeException(e); @@ -412,13 +412,13 @@ public class AiModelServiceImpl implements AiModelService { private void updateLocalProfile(AsrModel entity) { if (entity.getBaseUrl() == null || entity.getBaseUrl().isBlank()) { - throw new RuntimeException("baseUrl is required for ASR model"); + throw new RuntimeException("ASR 模型必须配置 baseUrl"); } if (entity.getApiKey() == null || entity.getApiKey().isBlank()) { - throw new RuntimeException("apiKey is required for local ASR model"); + throw new RuntimeException("本地 ASR 模型必须配置 apiKey"); } if (entity.getModelCode() == null || entity.getModelCode().isBlank()) { - throw new RuntimeException("modelCode is required for ASR model"); + throw new RuntimeException("ASR 模型必须配置 modelCode"); } Map mediaConfig = entity.getMediaConfig() == null ? Collections.emptyMap() : entity.getMediaConfig(); @@ -482,7 +482,7 @@ public class AiModelServiceImpl implements AiModelService { private String appendPath(String baseUrl, String path) { if (baseUrl == null || baseUrl.isBlank()) { - throw new RuntimeException("baseUrl is required"); + throw new RuntimeException("baseUrl 不能为空"); } String trimmedBaseUrl = baseUrl.trim(); if (path == null || path.isBlank()) { @@ -747,10 +747,10 @@ public class AiModelServiceImpl implements AiModelService { if (TYPE_ASR.equals(normalizeType(dto.getModelType()))) { Map mediaConfig = dto.getMediaConfig() == null ? Collections.emptyMap() : dto.getMediaConfig(); if (readConfigString(mediaConfig.get("speakerModel")) == null) { - throw new RuntimeException("Local ASR model requires a speaker model"); + throw new RuntimeException("本地 ASR 模型必须配置声纹模型"); } if (mediaConfig.get("svThreshold") == null) { - throw new RuntimeException("Local ASR model requires svThreshold"); + throw new RuntimeException("本地 ASR 模型必须配置 svThreshold"); } } } @@ -844,10 +844,10 @@ public class AiModelServiceImpl implements AiModelService { return; } if (entity.getBaseUrl() == null || entity.getBaseUrl().isBlank()) { - throw new RuntimeException("baseUrl is required for ASR model"); + throw new RuntimeException("ASR 模型必须配置 baseUrl"); } if (entity.getModelCode() == null || entity.getModelCode().isBlank()) { - throw new RuntimeException("modelCode is required for ASR model"); + throw new RuntimeException("ASR 模型必须配置 modelCode"); } String targetUrl = entity.getBaseUrl().endsWith("/") @@ -867,10 +867,10 @@ public class AiModelServiceImpl implements AiModelService { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() < 200 || response.statusCode() >= 300) { - throw new RuntimeException("Third-party ASR config save failed: HTTP " + response.statusCode()); + throw new RuntimeException("第三方 ASR 配置保存失败:HTTP " + response.statusCode()); } } catch (Exception e) { - throw new RuntimeException("Third-party ASR config save failed: " + e.getMessage(), e); + throw new RuntimeException("第三方 ASR 配置保存失败:" + e.getMessage(), e); } } @@ -999,7 +999,7 @@ public class AiModelServiceImpl implements AiModelService { } String normalized = type.trim().toUpperCase(); if (!TYPE_ASR.equals(normalized) && !TYPE_LLM.equals(normalized)) { - throw new RuntimeException("Unsupported model type: " + type); + throw new RuntimeException("不支持的模型类型:" + type); } return normalized; } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/AiTaskServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/AiTaskServiceImpl.java index 3189c28..886388b 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/AiTaskServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/AiTaskServiceImpl.java @@ -122,7 +122,7 @@ public class AiTaskServiceImpl extends ServiceImpl impleme .orderByDesc(AiTask::getId) .last("limit 1")); if (asrText == null || asrText.isBlank()) { - failPendingSummaryTask(sumTask, "No transcript content available for summary"); + failPendingSummaryTask(sumTask, "没有可用于总结的转录内容"); updateMeetingStatus(meetingId, 4); updateProgress(meetingId, -1, "未识别到可用于总结的转录内容", 0); return; @@ -136,7 +136,7 @@ public class AiTaskServiceImpl extends ServiceImpl impleme redisTemplate.delete(RedisKeys.meetingProgressKey(meetingId)); } catch (Exception e) { log.error("Meeting {} AI Task Flow failed", meetingId, e); - failPendingSummaryTask(findLatestSummaryTask(meetingId), "Summary skipped because transcription failed: " + e.getMessage()); + failPendingSummaryTask(findLatestSummaryTask(meetingId), "转录失败,已跳过总结任务:" + e.getMessage()); updateMeetingStatus(meetingId, 4); updateProgress(meetingId, -1, "分析失败: " + e.getMessage(), 0); } finally { @@ -164,14 +164,14 @@ public class AiTaskServiceImpl extends ServiceImpl impleme .orderByAsc(MeetingTranscript::getStartTime)); if (transcripts.isEmpty()) { - failPendingSummaryTask(sumTask, "No transcript content available for summary"); + failPendingSummaryTask(sumTask, "没有可用于总结的转录内容"); throw new RuntimeException("没有找到可用的转录文本,无法生成总结"); } String asrText = buildTranscriptText(transcripts); if (asrText == null || asrText.isBlank()) { - failPendingSummaryTask(sumTask, "No transcript content available for summary"); - throw new RuntimeException("No transcript content available for summary"); + failPendingSummaryTask(sumTask, "没有可用于总结的转录内容"); + throw new RuntimeException("没有可用于总结的转录内容"); } if (sumTask != null && sumTask.getStatus() == 0) { processSummaryTask(meeting, asrText, sumTask); @@ -205,7 +205,7 @@ public class AiTaskServiceImpl extends ServiceImpl impleme String respBody = postJson(submitUrl, req, asrModel.getApiKey()); JsonNode submitNode = objectMapper.readTree(respBody); if (submitNode.path("code").asInt() != 0) { - updateAiTaskFail(taskRecord, "Submission Failed: " + respBody); + updateAiTaskFail(taskRecord, "提交失败:" + respBody); throw new RuntimeException("ASR引擎拒绝请求: " + submitNode.path("msg").asText()); } taskId = submitNode.path("data").path("task_id").asText(); @@ -231,7 +231,7 @@ public class AiTaskServiceImpl extends ServiceImpl impleme updateAiTaskSuccess(taskRecord, statusNode); break; } else if ("failed".equalsIgnoreCase(status)) { - updateAiTaskFail(taskRecord, "ASR engine reported failure: " + queryResp); + updateAiTaskFail(taskRecord, "ASR 引擎返回失败:" + queryResp); throw new RuntimeException("ASR引擎处理失败: " + data.path("message").asText()); } else { int currentPercent = data.path("percentage").asInt(); @@ -477,11 +477,11 @@ public class AiTaskServiceImpl extends ServiceImpl impleme Long summaryModelId = Long.valueOf(taskRecord.getTaskConfig().get("summaryModelId").toString()); AiModelVO llmModel = aiModelService.getModelById(summaryModelId, "LLM"); if (llmModel == null) { - updateAiTaskFail(taskRecord, "LLM model not found: " + summaryModelId); + updateAiTaskFail(taskRecord, "LLM 模型不存在:" + summaryModelId); throw new RuntimeException("LLM模型配置不存在"); } if (!Integer.valueOf(1).equals(llmModel.getStatus())) { - updateAiTaskFail(taskRecord, "LLM model disabled: " + summaryModelId); + updateAiTaskFail(taskRecord, "LLM 模型已禁用:" + summaryModelId); throw new RuntimeException("LLM模型未启用"); } @@ -533,7 +533,7 @@ public class AiTaskServiceImpl extends ServiceImpl impleme markdownContent = meetingSummaryFileService.buildSummaryMarkdown(normalizedAnalysis); } if (markdownContent == null || markdownContent.isBlank()) { - updateAiTaskFail(taskRecord, "LLM summary content parse failed: " + content); + updateAiTaskFail(taskRecord, "LLM 总结内容解析失败:" + content); throw new RuntimeException("AI总结结果解析失败,未生成可保存的会议纪要"); } @@ -565,7 +565,7 @@ public class AiTaskServiceImpl extends ServiceImpl impleme updateProgress(meeting.getId(), 100, "全流程分析完成", 0); } else { - updateAiTaskFail(taskRecord, "LLM Summary failed: " + response.body()); + updateAiTaskFail(taskRecord, "LLM 总结失败:" + response.body()); throw new RuntimeException("AI总结生成异常"); } } @@ -630,7 +630,7 @@ public class AiTaskServiceImpl extends ServiceImpl impleme private String normalizeUrlComponent(String value, String fieldName) { if (value == null || value.isBlank()) { - throw new IllegalArgumentException(fieldName + " cannot be blank"); + throw new IllegalArgumentException(fieldName + "不能为空"); } return value.trim(); } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/ClientDownloadServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/ClientDownloadServiceImpl.java index 880ed95..0539f59 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/ClientDownloadServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/ClientDownloadServiceImpl.java @@ -92,10 +92,10 @@ public class ClientDownloadServiceImpl extends ServiceImpl uploadPackage(String platformCode, MultipartFile file) throws IOException { if (platformCode == null || platformCode.isBlank()) { - throw new RuntimeException("platformCode is required"); + throw new RuntimeException("platformCode 不能为空"); } if (file == null || file.isEmpty()) { - throw new RuntimeException("file is required"); + throw new RuntimeException("file 不能为空"); } String cleanCode = platformCode.trim().toLowerCase(); String originalName = sanitizeFileName(file.getOriginalFilename()); @@ -124,17 +124,17 @@ public class ClientDownloadServiceImpl extends ServiceImpl() @@ -358,7 +358,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService { .orderByDesc(AiTask::getId) .last("LIMIT 1")); if (summaryTask == null) { - throw new RuntimeException("Summary task missing, cannot continue offline process"); + throw new RuntimeException("缺少总结任务,无法继续离线流程"); } resetAiTask(summaryTask, summaryTask.getTaskConfig()); @@ -425,7 +425,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService { public void updateMeetingTranscript(UpdateMeetingTranscriptCommand command) { String content = command.getContent() == null ? "" : command.getContent().trim(); if (content.isEmpty()) { - throw new RuntimeException("Transcript content cannot be empty"); + throw new RuntimeException("转录内容不能为空"); } int updated = transcriptMapper.update(null, new LambdaUpdateWrapper() @@ -433,7 +433,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService { .eq(MeetingTranscript::getId, command.getTranscriptId()) .set(MeetingTranscript::getContent, content)); if (updated <= 0) { - throw new RuntimeException("Transcript not found"); + throw new RuntimeException("转录记录不存在"); } } @@ -469,7 +469,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService { public void updateSummaryContent(Long meetingId, String summaryContent) { Meeting meeting = meetingService.getById(meetingId); if (meeting == null) { - throw new RuntimeException("Meeting not found"); + throw new RuntimeException("会议不存在"); } meetingSummaryFileService.updateSummaryContent(meeting, summaryContent); } @@ -479,7 +479,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService { public void reSummary(Long meetingId, Long summaryModelId, Long promptId, String userPrompt) { Meeting meeting = meetingService.getById(meetingId); if (meeting == null) { - throw new RuntimeException("Meeting not found"); + throw new RuntimeException("会议不存在"); } meetingDomainSupport.createSummaryTask(meetingId, summaryModelId, promptId, userPrompt); @@ -493,7 +493,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService { public void retryTranscription(Long meetingId) { Meeting meeting = meetingService.getById(meetingId); if (meeting == null) { - throw new RuntimeException("Meeting not found"); + throw new RuntimeException("会议不存在"); } long transcriptCount = transcriptMapper.selectCount(new LambdaQueryWrapper() diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java index 74daadc..bb2fd41 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java @@ -107,7 +107,7 @@ public class MeetingDomainSupport { return plan.relocatedUrl(); } catch (Exception ex) { log.error("Failed to move audio file for meeting {}", meetingId, ex); - throw new RuntimeException("File relocation failed: " + ex.getMessage()); + throw new RuntimeException("文件迁移失败:" + ex.getMessage()); } } @@ -125,13 +125,13 @@ public class MeetingDomainSupport { try { Files.deleteIfExists(path); } catch (Exception ex) { - throw new RuntimeException("Delete meeting artifact failed: " + path, ex); + throw new RuntimeException("删除会议产物失败:" + path, ex); } }); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { - throw new RuntimeException("Delete meeting artifacts failed", ex); + throw new RuntimeException("删除会议产物失败", ex); } } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingExportServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingExportServiceImpl.java index 60ba0dd..b1ad1b3 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingExportServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingExportServiceImpl.java @@ -55,7 +55,7 @@ public class MeetingExportServiceImpl implements MeetingExportService { ext = "pdf"; contentType = MediaType.APPLICATION_PDF_VALUE; } else { - throw new RuntimeException("Unsupported export format"); + throw new RuntimeException("不支持的导出格式"); } try { @@ -79,7 +79,7 @@ public class MeetingExportServiceImpl implements MeetingExportService { return new MeetingSummaryExportResult(bytes, contentType, safeTitle + "-AI-Summary." + ext); } catch (IOException ex) { - throw new RuntimeException("Export failed: " + ex.getMessage(), ex); + throw new RuntimeException("导出失败:" + ex.getMessage(), ex); } } @@ -194,7 +194,7 @@ public class MeetingExportServiceImpl implements MeetingExportService { builder.run(); return out.toByteArray(); } catch (Exception ex) { - throw new IOException("PDF generation failed", ex); + throw new IOException("PDF 生成失败", ex); } } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingSummaryFileServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingSummaryFileServiceImpl.java index 7c8f3f5..d03f0eb 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingSummaryFileServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingSummaryFileServiceImpl.java @@ -36,13 +36,13 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService public Path requireSummarySourcePath(Meeting meeting) { AiTask summaryTask = findLatestSummaryTask(meeting); if (summaryTask == null || summaryTask.getResultFilePath() == null || summaryTask.getResultFilePath().isBlank()) { - throw new RuntimeException("Summary file not found"); + throw new RuntimeException("总结文件不存在"); } String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/"; Path summaryPath = Paths.get(basePath, summaryTask.getResultFilePath().replace("\\", "/")); if (!Files.exists(summaryPath)) { - throw new RuntimeException("Summary source file is missing"); + throw new RuntimeException("总结源文件缺失"); } return summaryPath; } @@ -56,7 +56,7 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService } catch (RuntimeException ex) { return null; } catch (Exception ex) { - throw new RuntimeException("Load summary content failed", ex); + throw new RuntimeException("加载总结内容失败", ex); } } @@ -91,7 +91,7 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService } return null; } catch (Exception ex) { - throw new RuntimeException("Load summary analysis failed", ex); + throw new RuntimeException("加载总结分析失败", ex); } } @@ -221,7 +221,7 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService public void updateSummaryContent(Meeting meeting, String summaryContent) { AiTask summaryTask = findLatestSummaryTask(meeting); if (summaryTask == null || summaryTask.getResultFilePath() == null || summaryTask.getResultFilePath().isBlank()) { - throw new RuntimeException("Summary file not found"); + throw new RuntimeException("总结文件不存在"); } String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/"; @@ -236,7 +236,7 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService String frontMatter = extractFrontMatter(existingContent, meeting, summaryTask); Files.writeString(summaryPath, frontMatter + normalizeSummaryMarkdown(summaryContent), StandardCharsets.UTF_8); } catch (Exception ex) { - throw new RuntimeException("Update summary file failed", ex); + throw new RuntimeException("更新总结文件失败", ex); } } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSessionStateServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSessionStateServiceImpl.java index 8267d61..11f80f3 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSessionStateServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSessionStateServiceImpl.java @@ -72,16 +72,16 @@ public class RealtimeMeetingSessionStateServiceImpl implements RealtimeMeetingSe String currentStatus = status.getStatus(); if ("COMPLETING".equals(currentStatus)) { - throw new RuntimeException("Realtime meeting is completing"); + throw new RuntimeException("实时会议正在结束处理中"); } if ("COMPLETED".equals(currentStatus)) { - throw new RuntimeException("Realtime meeting has completed"); + throw new RuntimeException("实时会议已结束"); } if ("ACTIVE".equals(currentStatus) || Boolean.TRUE.equals(status.getActiveConnection())) { - throw new RuntimeException("Realtime meeting already has an active connection"); + throw new RuntimeException("实时会议已存在活动连接"); } if ("PAUSED_RESUMABLE".equals(currentStatus) && !Boolean.TRUE.equals(status.getCanResume())) { - throw new RuntimeException("Realtime meeting resume window has expired"); + throw new RuntimeException("实时会议恢复窗口已过期"); } } @@ -391,7 +391,7 @@ public class RealtimeMeetingSessionStateServiceImpl implements RealtimeMeetingSe objectMapper.writeValueAsString(state) ); } catch (Exception ex) { - throw new RuntimeException("Failed to write realtime meeting session state", ex); + throw new RuntimeException("写入实时会议会话状态失败", ex); } } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSocketSessionServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSocketSessionServiceImpl.java index 5cbde42..d18fed4 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSocketSessionServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/RealtimeMeetingSocketSessionServiceImpl.java @@ -42,10 +42,10 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS Boolean enableTextRefine, Boolean saveAudio, List> hotwords, LoginUser loginUser) { if (meetingId == null) { - throw new RuntimeException("Meeting ID is required"); + throw new RuntimeException("会议 ID 不能为空"); } if (asrModelId == null) { - throw new RuntimeException("ASR model ID is required"); + throw new RuntimeException("ASR 模型 ID 不能为空"); } Meeting meeting = meetingAccessService.requireMeeting(meetingId); @@ -56,12 +56,12 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS AiModelVO asrModel = aiModelService.getModelById(asrModelId, "ASR"); if (asrModel == null) { - throw new RuntimeException("ASR model not found"); + throw new RuntimeException("ASR 模型不存在"); } String targetWsUrl = resolveWsUrl(asrModel); if (targetWsUrl == null || targetWsUrl.isBlank()) { - throw new RuntimeException("ASR model WebSocket is not configured"); + throw new RuntimeException("ASR 模型未配置 WebSocket 地址"); } RealtimeMeetingResumeConfig resumeConfig = new RealtimeMeetingResumeConfig(); @@ -97,7 +97,7 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS SESSION_TTL ); } catch (Exception ex) { - throw new RuntimeException("Failed to create realtime socket session", ex); + throw new RuntimeException("创建实时 Socket 会话失败", ex); } RealtimeSocketSessionVO vo = new RealtimeSocketSessionVO(); @@ -131,7 +131,7 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS try { return objectMapper.readValue(raw, RealtimeSocketSessionData.class); } catch (Exception ex) { - throw new RuntimeException("Failed to read realtime socket session", ex); + throw new RuntimeException("读取实时 Socket 会话失败", ex); } } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/ScreenSaverServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/ScreenSaverServiceImpl.java index 30f7ce0..e766fa9 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/ScreenSaverServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/ScreenSaverServiceImpl.java @@ -170,7 +170,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl MAX_DISPLAY_DURATION_SEC) { - throw new RuntimeException("displayDurationSec must be between 3 and 3600"); + throw new RuntimeException("displayDurationSec 必须在 3 到 3600 之间"); } } private String resolveAndValidateFormat(String fileName, String contentType) { String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); if (!ALLOWED_FORMATS.contains(extension)) { - throw new RuntimeException("only jpg/jpeg/png are supported"); + throw new RuntimeException("仅支持 jpg/jpeg/png 格式"); } if (StringUtils.hasText(contentType)) { String normalized = contentType.trim().toLowerCase(); if (!normalized.equals("image/jpeg") && !normalized.equals("image/png") && !normalized.equals("image/jpg")) { - throw new RuntimeException("invalid image content type"); + throw new RuntimeException("图片内容类型无效"); } } return extension; @@ -623,7 +623,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl impl } } catch (IOException e) { log.error("Create voiceprints directory error", e); - throw new RuntimeException("Failed to initialize storage"); + throw new RuntimeException("初始化存储失败"); } String fileName = UUID.randomUUID().toString() + extension; @@ -218,7 +218,7 @@ public class SpeakerServiceImpl extends ServiceImpl impl Files.copy(file.getInputStream(), filePath); } catch (IOException e) { log.error("Save voice file error", e); - throw new RuntimeException("Failed to save voice file"); + throw new RuntimeException("保存声纹文件失败"); } speaker.setVoicePath("voiceprints/" + fileName); diff --git a/backend/src/main/java/com/imeeting/service/realtime/impl/AndroidRealtimeSessionTicketServiceImpl.java b/backend/src/main/java/com/imeeting/service/realtime/impl/AndroidRealtimeSessionTicketServiceImpl.java index adfcd6c..747a3d2 100644 --- a/backend/src/main/java/com/imeeting/service/realtime/impl/AndroidRealtimeSessionTicketServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/realtime/impl/AndroidRealtimeSessionTicketServiceImpl.java @@ -52,7 +52,7 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS ttl ); } catch (Exception ex) { - throw new RuntimeException("Failed to create realtime gRPC session", ex); + throw new RuntimeException("创建实时会议 gRPC 会话失败", ex); } AndroidRealtimeGrpcSessionVO vo = new AndroidRealtimeGrpcSessionVO(); @@ -84,13 +84,13 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS try { return objectMapper.readValue(raw, AndroidRealtimeGrpcSessionData.class); } catch (Exception ex) { - throw new RuntimeException("Failed to read realtime gRPC session", ex); + throw new RuntimeException("读取实时会议 gRPC 会话失败", ex); } } private PreparedRealtimeSession prepareSession(Long meetingId, AndroidOpenRealtimeGrpcSessionCommand command, AndroidAuthContext authContext) { if (meetingId == null) { - throw new RuntimeException("Meeting ID is required"); + throw new RuntimeException("会议 ID 不能为空"); } Meeting meeting = meetingAccessService.requireMeeting(meetingId); meetingAuthorizationService.assertCanControlRealtimeMeeting(meeting, authContext, MeetingConstants.SOURCE_ANDROID); @@ -104,17 +104,17 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS resolveDefaultAsrModelId(meeting.getTenantId()) ); if (asrModelId == null) { - throw new RuntimeException("ASR model ID is required"); + throw new RuntimeException("ASR 模型 ID 不能为空"); } realtimeMeetingSessionStateService.assertCanOpenSession(meetingId); AiModelVO asrModel = aiModelService.getModelById(asrModelId, "ASR"); if (asrModel == null) { - throw new RuntimeException("ASR model not found"); + throw new RuntimeException("ASR 模型不存在"); } String targetWsUrl = resolveWsUrl(asrModel); if (targetWsUrl == null || targetWsUrl.isBlank()) { - throw new RuntimeException("ASR model WebSocket is not configured"); + throw new RuntimeException("ASR 模型未配置 WebSocket 地址"); } RealtimeMeetingResumeConfig resumeConfig = buildResumeConfig(command, currentResumeConfig, asrModelId); @@ -133,7 +133,7 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS try { sessionData.setStartMessageJson(objectMapper.writeValueAsString(startMessage)); } catch (Exception ex) { - throw new RuntimeException("Failed to serialize realtime start message", ex); + throw new RuntimeException("序列化实时启动消息失败", ex); } return new PreparedRealtimeSession(sessionData, resumeConfig, latestStatus); } diff --git a/backend/src/main/java/com/imeeting/service/realtime/impl/RealtimeMeetingGrpcSessionServiceImpl.java b/backend/src/main/java/com/imeeting/service/realtime/impl/RealtimeMeetingGrpcSessionServiceImpl.java index c3f2eba..d55ccb9 100644 --- a/backend/src/main/java/com/imeeting/service/realtime/impl/RealtimeMeetingGrpcSessionServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/realtime/impl/RealtimeMeetingGrpcSessionServiceImpl.java @@ -54,22 +54,22 @@ public class RealtimeMeetingGrpcSessionServiceImpl implements RealtimeMeetingGrp } else if (streamToken != null && !streamToken.isBlank()) { sessionData = ticketService.getSessionData(streamToken); if (sessionData == null) { - throw new RuntimeException("Invalid realtime gRPC session token"); + throw new RuntimeException("实时会议 gRPC 会话令牌无效"); } if (sessionData.getDeviceId() != null && !sessionData.getDeviceId().isBlank() && authContext.getDeviceId() != null && !sessionData.getDeviceId().equals(authContext.getDeviceId())) { - throw new RuntimeException("Realtime gRPC session token does not match deviceId"); + throw new RuntimeException("实时会议 gRPC 会话令牌与 deviceId 不匹配"); } } else { - throw new RuntimeException("Meeting ID is required"); + throw new RuntimeException("会议 ID 不能为空"); } String connectionId = "grpc_" + java.util.UUID.randomUUID().toString().replace("-", ""); SessionRuntime runtime = new SessionRuntime(connectionId, streamToken, sessionData, responseObserver); SessionRuntime previous = sessions.putIfAbsent(connectionId, runtime); if (previous != null) { - throw new RuntimeException("Duplicate realtime gRPC connectionId"); + throw new RuntimeException("实时会议 gRPC connectionId 重复"); } try { diff --git a/backend/src/main/java/com/imeeting/websocket/RealtimeMeetingProxyWebSocketHandler.java b/backend/src/main/java/com/imeeting/websocket/RealtimeMeetingProxyWebSocketHandler.java index 7d44edb..0ffee12 100644 --- a/backend/src/main/java/com/imeeting/websocket/RealtimeMeetingProxyWebSocketHandler.java +++ b/backend/src/main/java/com/imeeting/websocket/RealtimeMeetingProxyWebSocketHandler.java @@ -59,7 +59,7 @@ public class RealtimeMeetingProxyWebSocketHandler extends AbstractWebSocketHandl RealtimeSocketSessionData sessionData = realtimeMeetingSocketSessionService.getSessionData(sessionToken); if (sessionData == null) { log.warn("Realtime websocket rejected: invalid session token, sessionId={}", session.getId()); - session.close(CloseStatus.POLICY_VIOLATION.withReason("Invalid realtime socket session")); + session.close(CloseStatus.POLICY_VIOLATION.withReason("实时 Socket 会话无效")); return; } @@ -96,13 +96,13 @@ public class RealtimeMeetingProxyWebSocketHandler extends AbstractWebSocketHandl sessionData.getMeetingId(), session.getId(), ex); sendFrontendError(frontendSession, "REALTIME_UPSTREAM_CONNECT_INTERRUPTED", "连接第三方识别服务时被中断"); realtimeMeetingAudioStorageService.closeSession(session.getId()); - frontendSession.close(CloseStatus.SERVER_ERROR.withReason("Interrupted while connecting upstream")); + frontendSession.close(CloseStatus.SERVER_ERROR.withReason("连接上游服务时被中断")); return; } catch (ExecutionException | CompletionException ex) { log.warn("Failed to connect upstream websocket, meetingId={}, target={}", sessionData.getMeetingId(), sessionData.getTargetWsUrl(), ex); sendFrontendError(frontendSession, "REALTIME_UPSTREAM_CONNECT_FAILED", "连接第三方识别服务失败,请检查模型 WebSocket 配置或服务状态"); realtimeMeetingAudioStorageService.closeSession(session.getId()); - frontendSession.close(CloseStatus.SERVER_ERROR.withReason("Failed to connect ASR websocket")); + frontendSession.close(CloseStatus.SERVER_ERROR.withReason("连接 ASR WebSocket 失败")); return; } @@ -348,7 +348,7 @@ public class RealtimeMeetingProxyWebSocketHandler extends AbstractWebSocketHandl if (!realtimeMeetingSessionStateService.activate(meetingId, rawSession.getId())) { sendFrontendError("REALTIME_ACTIVE_CONNECTION_EXISTS", "当前会议已有活跃实时连接,请先关闭旧连接后再继续"); webSocket.sendClose(CloseStatus.POLICY_VIOLATION.getCode(), "Active realtime connection already exists"); - closeFrontend(CloseStatus.POLICY_VIOLATION.withReason("Active realtime connection already exists")); + closeFrontend(CloseStatus.POLICY_VIOLATION.withReason("已存在活动的实时连接")); return; } try { diff --git a/backend/src/test/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcServiceTest.java b/backend/src/test/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcServiceTest.java index 08088a6..040d3d3 100644 --- a/backend/src/test/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcServiceTest.java +++ b/backend/src/test/java/com/imeeting/grpc/realtime/RealtimeMeetingGrpcServiceTest.java @@ -33,7 +33,7 @@ class RealtimeMeetingGrpcServiceTest { CapturingObserver responseObserver = new CapturingObserver(); when(authService.authenticateGrpc(any(ClientAuth.class), isNull())) - .thenThrow(new RuntimeException("Android deviceId is required")); + .thenThrow(new RuntimeException("缺少 Android deviceId")); StreamObserver requestObserver = service.streamMeetingAudio(responseObserver); RealtimeClientPacket openPacket = RealtimeClientPacket.newBuilder() @@ -48,7 +48,7 @@ class RealtimeMeetingGrpcServiceTest { assertEquals("rt-open-001", errorPacket.getRequestId()); assertTrue(errorPacket.hasError()); assertEquals("REALTIME_GRPC_ERROR", errorPacket.getError().getCode()); - assertEquals("Android deviceId is required", errorPacket.getError().getMessage()); + assertEquals("缺少 Android deviceId", errorPacket.getError().getMessage()); assertTrue(responseObserver.completed); assertNull(responseObserver.error); verify(sessionService, never()).closeStream(anyString(), anyString(), anyBoolean()); @@ -65,7 +65,7 @@ class RealtimeMeetingGrpcServiceTest { when(authService.authenticateGrpc(any(ClientAuth.class), isNull())).thenReturn(authContext); when(sessionService.openStream(1001L, "", authContext, responseObserver)) - .thenThrow(new RuntimeException("ASR model WebSocket is not configured")); + .thenThrow(new RuntimeException("ASR 模型未配置 WebSocket 地址")); StreamObserver requestObserver = service.streamMeetingAudio(responseObserver); RealtimeClientPacket openPacket = RealtimeClientPacket.newBuilder() @@ -81,7 +81,7 @@ class RealtimeMeetingGrpcServiceTest { assertEquals("rt-open-002", errorPacket.getRequestId()); assertTrue(errorPacket.hasError()); assertEquals("REALTIME_GRPC_ERROR", errorPacket.getError().getCode()); - assertEquals("ASR model WebSocket is not configured", errorPacket.getError().getMessage()); + assertEquals("ASR 模型未配置 WebSocket 地址", errorPacket.getError().getMessage()); assertTrue(responseObserver.completed); assertNull(responseObserver.error); verify(sessionService, never()).closeStream(anyString(), anyString(), anyBoolean()); diff --git a/backend/src/test/java/com/imeeting/service/biz/impl/AiTaskServiceImplTest.java b/backend/src/test/java/com/imeeting/service/biz/impl/AiTaskServiceImplTest.java index 47527cc..c34cf10 100644 --- a/backend/src/test/java/com/imeeting/service/biz/impl/AiTaskServiceImplTest.java +++ b/backend/src/test/java/com/imeeting/service/biz/impl/AiTaskServiceImplTest.java @@ -114,7 +114,7 @@ class AiTaskServiceImplTest { service.dispatchTasks(66L, 1L, 2L); assertEquals(3, summaryTask.getStatus()); - assertTrue(summaryTask.getErrorMsg().contains("No transcript content available for summary")); + assertTrue(summaryTask.getErrorMsg().contains("没有可用于总结的转录内容")); verify(aiModelService, never()).getModelById(anyLong(), anyString()); } @@ -156,7 +156,7 @@ class AiTaskServiceImplTest { service.dispatchSummaryTask(77L, 1L, 2L); assertEquals(3, summaryTask.getStatus()); - assertTrue(summaryTask.getErrorMsg().contains("No transcript content available for summary")); + assertTrue(summaryTask.getErrorMsg().contains("没有可用于总结的转录内容")); verify(aiModelService, never()).getModelById(anyLong(), anyString()); } diff --git a/frontend/src/routes/routes.tsx b/frontend/src/routes/routes.tsx index cade5ec..a6ddbc5 100644 --- a/frontend/src/routes/routes.tsx +++ b/frontend/src/routes/routes.tsx @@ -45,32 +45,32 @@ function LazyPage({ children }: { children: JSX.Element }) { } export const menuRoutes: MenuRoute[] = [ - { path: "/", label: "首页", element: , perm: "menu:dashboard" }, - { path: "/profile", label: "个人中心", element: }, - { path: "/speaker-reg", label: "声纹注册", element: , perm: "menu:speaker" }, - { path: "/tenants", label: "租户管理", element: , perm: "menu:tenants" }, - { path: "/orgs", label: "组织管理", element: , perm: "menu:orgs" }, - { path: "/users", label: "用户管理", element: , perm: "menu:users" }, - { path: "/roles", label: "角色管理", element: , perm: "menu:roles" }, - { path: "/permissions", label: "权限管理", element: , perm: "menu:permissions" }, - { path: "/params", label: "系统参数", element: , perm: "menu:params" }, - { path: "/platform-settings", label: "平台设置", element: , perm: "menu:platform" }, - { path: "/dictionaries", label: "字典管理", element: , perm: "menu:dict" }, - { path: "/logs", label: "日志管理", element: , perm: "menu:logs" }, - { path: "/devices", label: "设备管理", element: , perm: "menu:devices" }, - { path: "/user-roles", label: "用户角色绑定", element: , perm: "menu:user-roles" }, - { path: "/role-permissions", label: "角色权限绑定", element: , perm: "menu:role-permissions" }, - { path: "/hotwords", label: "热词管理", element: , perm: "menu:hotword" }, - { path: "/prompts", label: "总结模板", element: , perm: "menu:prompt" }, - { path: "/aimodels", label: "模型配置", element: , perm: "menu:aimodel" }, - { path: "/clients", label: "客户端管理", element: , perm: "menu:clients" }, - { path: "/external-apps", label: "外部应用管理", element: , perm: "menu:external-apps" }, - { path: "/screen-savers", label: "屏保管理", element: , perm: "menu:screen-savers" }, - { path: "/meetings", label: "会议中心", element: , perm: "menu:meeting" }, + { path: "/", label: "首页", element: , perm: "menu:dashboard" }, + { path: "/profile", label: "个人中心", element: }, + { path: "/speaker-reg", label: "声纹注册", element: , perm: "menu:speaker" }, + { path: "/tenants", label: "租户管理", element: , perm: "menu:tenants" }, + { path: "/orgs", label: "组织管理", element: , perm: "menu:orgs" }, + { path: "/users", label: "用户管理", element: , perm: "menu:users" }, + { path: "/roles", label: "角色管理", element: , perm: "menu:roles" }, + { path: "/permissions", label: "权限管理", element: , perm: "menu:permissions" }, + { path: "/params", label: "系统参数", element: , perm: "menu:params" }, + { path: "/platform-settings", label: "平台设置", element: , perm: "menu:platform" }, + { path: "/dictionaries", label: "字典管理", element: , perm: "menu:dict" }, + { path: "/logs", label: "日志管理", element: , perm: "menu:logs" }, + { path: "/devices", label: "设备管理", element: , perm: "menu:devices" }, + { path: "/user-roles", label: "用户角色绑定", element: , perm: "menu:user-roles" }, + { path: "/role-permissions", label: "角色权限绑定", element: , perm: "menu:role-permissions" }, + { path: "/hotwords", label: "热词管理", element: , perm: "menu:hotword" }, + { path: "/prompts", label: "总结模板", element: , perm: "menu:prompt" }, + { path: "/aimodels", label: "模型配置", element: , perm: "menu:aimodel" }, + { path: "/clients", label: "客户端管理", element: , perm: "menu:clients" }, + { path: "/external-apps", label: "外部应用管理", element: , perm: "menu:external-apps" }, + { path: "/screen-savers", label: "屏保管理", element: , perm: "menu:screen-savers" }, + { path: "/meetings", label: "会议中心", element: , perm: "menu:meeting" }, ]; export const extraRoutes = [ - { path: "/dashboard-monitor", element: , perm: "menu:dashboard" }, - { path: "/meetings/:id", element: , perm: "menu:meeting" }, - { path: "/meeting-live-session/:id", element: , perm: "menu:meeting" }, + { path: "/dashboard-monitor", element: , perm: "menu:dashboard" }, + { path: "/meetings/:id", element: , perm: "menu:meeting" }, + { path: "/meeting-live-session/:id", element: , perm: "menu:meeting" }, ];