refactor: 优化错误消息为中文

- 将多个服务和控制器中的英文错误消息替换为中文
- 更新测试用例以匹配新的错误消息
dev_na
chenhao 2026-04-24 14:27:28 +08:00
parent a295a3b15b
commit 2f80c6c55e
36 changed files with 203 additions and 203 deletions

View File

@ -72,6 +72,6 @@ public class GrpcExceptionLoggingInterceptor implements ServerInterceptor {
if (!closed.compareAndSet(false, true)) { if (!closed.compareAndSet(false, true)) {
return; return;
} }
call.close(Status.UNKNOWN.withDescription("Application error processing RPC").withCause(ex), new Metadata()); call.close(Status.UNKNOWN.withDescription("应用处理 RPC 时发生异常").withCause(ex), new Metadata());
} }
} }

View File

@ -107,7 +107,7 @@ public class AndroidMeetingController {
}) })
@PostMapping("/upload-audio") @PostMapping("/upload-audio")
public ApiResponse<LegacyUploadAudioResponse> uploadAudio(HttpServletRequest request, public ApiResponse<LegacyUploadAudioResponse> uploadAudio(HttpServletRequest request,
@RequestParam("meeting_id") Long meetingId, @RequestParam("id") Long meetingId,
@RequestParam(value = "prompt_id", required = false) Long promptId, @RequestParam(value = "prompt_id", required = false) Long promptId,
@RequestParam(value = "model_code", required = false) String modelCode, @RequestParam(value = "model_code", required = false) String modelCode,
@RequestParam(value = "force_replace", defaultValue = "false") boolean forceReplace, @RequestParam(value = "force_replace", defaultValue = "false") boolean forceReplace,

View File

@ -44,7 +44,7 @@ public class AndroidPromptController {
@GetMapping("/active/{scene}") @GetMapping("/active/{scene}")
public ApiResponse<List<PromptTemplateVO>> activePrompts(HttpServletRequest request, @PathVariable String scene) { public ApiResponse<List<PromptTemplateVO>> activePrompts(HttpServletRequest request, @PathVariable String scene) {
if (!LEGACY_MEETING_SCENE.equals(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); AndroidAuthContext authContext = androidAuthService.authenticateHttp(request);

View File

@ -35,7 +35,7 @@ public class LegacyPromptController {
@PreAuthorize("isAuthenticated()") @PreAuthorize("isAuthenticated()")
public LegacyApiResponse<LegacyPromptListResponse> activePrompts(@PathVariable String scene) { public LegacyApiResponse<LegacyPromptListResponse> activePrompts(@PathVariable String scene) {
if (!LEGACY_MEETING_SCENE.equals(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(); LoginUser loginUser = currentLoginUser();

View File

@ -105,10 +105,10 @@ public class AiModelController {
@PreAuthorize("isAuthenticated()") @PreAuthorize("isAuthenticated()")
public ApiResponse<AiLocalProfileVO> testLocalConnectivity(@RequestBody AiModelDTO dto) { public ApiResponse<AiLocalProfileVO> testLocalConnectivity(@RequestBody AiModelDTO dto) {
if (dto.getBaseUrl() == null || dto.getBaseUrl().isBlank()) { if (dto.getBaseUrl() == null || dto.getBaseUrl().isBlank()) {
return ApiResponse.error("Base URL不能为空"); return ApiResponse.error("基础地址不能为空");
} }
if (dto.getApiKey() == null || dto.getApiKey().isBlank()) { 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())); return ApiResponse.ok(aiModelService.testLocalConnectivity(dto.getBaseUrl(), dto.getApiKey()));
} }
@ -118,7 +118,7 @@ public class AiModelController {
@PreAuthorize("isAuthenticated()") @PreAuthorize("isAuthenticated()")
public ApiResponse<Boolean> testLlmConnectivity(@RequestBody AiModelDTO dto) { public ApiResponse<Boolean> testLlmConnectivity(@RequestBody AiModelDTO dto) {
if ("custom".equalsIgnoreCase(dto.getProvider()) && (dto.getBaseUrl() == null || dto.getBaseUrl().isBlank())) { 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()) { if (dto.getModelCode() == null || dto.getModelCode().isBlank()) {
return ApiResponse.error("模型名称不能为空"); return ApiResponse.error("模型名称不能为空");

View File

@ -107,7 +107,7 @@ public class MeetingController {
try { try {
return ApiResponse.ok(new com.fasterxml.jackson.databind.ObjectMapper().readValue(json, Map.class)); return ApiResponse.ok(new com.fasterxml.jackson.databind.ObjectMapper().readValue(json, Map.class));
} catch (Exception ex) { } catch (Exception ex) {
return ApiResponse.error("Progress parse failed"); return ApiResponse.error("进度解析失败");
} }
} }
@ -206,7 +206,7 @@ public class MeetingController {
MeetingVO meetingDetail = meetingQueryService.getDetail(id); MeetingVO meetingDetail = meetingQueryService.getDetail(id);
if (meetingDetail == null) { if (meetingDetail == null) {
throw new RuntimeException("Meeting not found"); throw new RuntimeException("会议不存在");
} }
MeetingSummaryExportResult exportResult = meetingExportService.exportSummary(meeting, meetingDetail, format); MeetingSummaryExportResult exportResult = meetingExportService.exportSummary(meeting, meetingDetail, format);
@ -437,7 +437,7 @@ public class MeetingController {
loginUser.getIsTenantAdmin() loginUser.getIsTenantAdmin()
); );
if (!enabled) { if (!enabled) {
throw new RuntimeException("Summary template unavailable"); throw new RuntimeException("总结模板不可用");
} }
} }

View File

@ -35,7 +35,7 @@ public class PromptTemplateController {
if (Integer.valueOf(1).equals(dto.getIsSystem())) { if (Integer.valueOf(1).equals(dto.getIsSystem())) {
if (!Boolean.TRUE.equals(loginUser.getIsPlatformAdmin()) && !Boolean.TRUE.equals(loginUser.getIsTenantAdmin())) { 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())) { if (!Boolean.TRUE.equals(loginUser.getIsPlatformAdmin())) {
dto.setTenantId(loginUser.getTenantId()); dto.setTenantId(loginUser.getTenantId());
@ -56,7 +56,7 @@ public class PromptTemplateController {
LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
PromptTemplate existing = promptTemplateService.getById(dto.getId()); PromptTemplate existing = promptTemplateService.getById(dto.getId());
if (existing == null) { if (existing == null) {
return ApiResponse.error("Template not found"); return ApiResponse.error("模板不存在");
} }
boolean canModify = false; boolean canModify = false;
@ -71,7 +71,7 @@ public class PromptTemplateController {
} }
if (!canModify) { if (!canModify) {
return ApiResponse.error("No permission to modify this template"); return ApiResponse.error("无权修改该模板");
} }
return ApiResponse.ok(promptTemplateService.updateTemplate(dto, loginUser.getUserId(), loginUser.getTenantId())); return ApiResponse.ok(promptTemplateService.updateTemplate(dto, loginUser.getUserId(), loginUser.getTenantId()));
@ -84,7 +84,7 @@ public class PromptTemplateController {
LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
PromptTemplate existing = promptTemplateService.getById(id); PromptTemplate existing = promptTemplateService.getById(id);
if (existing == null) { if (existing == null) {
return ApiResponse.error("Template not found"); return ApiResponse.error("模板不存在");
} }
boolean canGlobalModify = false; boolean canGlobalModify = false;
@ -110,7 +110,7 @@ public class PromptTemplateController {
loginUser.getIsTenantAdmin() loginUser.getIsTenantAdmin()
); );
if (!success) { if (!success) {
return ApiResponse.error("Template not found or no permission"); return ApiResponse.error("模板不存在或无权限访问");
} }
return ApiResponse.ok(true); return ApiResponse.ok(true);
} }
@ -137,7 +137,7 @@ public class PromptTemplateController {
} }
if (!canModify) { if (!canModify) {
return ApiResponse.error("No permission to delete this template"); return ApiResponse.error("无权删除该模板");
} }
return ApiResponse.ok(promptTemplateService.removeById(id)); return ApiResponse.ok(promptTemplateService.removeById(id));

View File

@ -80,7 +80,7 @@ public class ScreenSaverController {
public ApiResponse<Boolean> updateStatus(@PathVariable Long id, @RequestParam Integer status) { public ApiResponse<Boolean> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
boolean success = screenSaverService.updateStatus(id, status, currentLoginUser()); boolean success = screenSaverService.updateStatus(id, status, currentLoginUser());
if (!success) { if (!success) {
return ApiResponse.error("Screen saver not found or no permission"); return ApiResponse.error("屏保不存在或无权限访问");
} }
return ApiResponse.ok(true); return ApiResponse.ok(true);
} }

View File

@ -11,7 +11,7 @@ import java.util.List;
@Data @Data
public class CreateMeetingCommand { public class CreateMeetingCommand {
@NotBlank(message = "title must not be blank") @NotBlank(message = "标题不能为空")
private String title; private String title;
@NotNull(message = "meetingTime must not be null") @NotNull(message = "meetingTime must not be null")
@ -23,7 +23,7 @@ public class CreateMeetingCommand {
private Long hostUserId; private Long hostUserId;
private String hostName; private String hostName;
@NotBlank(message = "audioUrl must not be blank") @NotBlank(message = "音频地址不能为空")
private String audioUrl; private String audioUrl;
@NotNull(message = "asrModelId must not be null") @NotNull(message = "asrModelId must not be null")

View File

@ -11,7 +11,7 @@ import java.util.List;
@Data @Data
public class CreateRealtimeMeetingCommand { public class CreateRealtimeMeetingCommand {
@NotBlank(message = "title must not be blank") @NotBlank(message = "标题不能为空")
private String title; private String title;
@NotNull(message = "meetingTime must not be null") @NotNull(message = "meetingTime must not be null")

View File

@ -77,7 +77,7 @@ public class AndroidGatewayGrpcService extends AndroidGatewayServiceGrpc.Android
} }
AndroidDeviceSessionState state = androidDeviceSessionService.refreshHeartbeat(connectionId, heartbeat.getClientTime()); AndroidDeviceSessionState state = androidDeviceSessionService.refreshHeartbeat(connectionId, heartbeat.getClientTime());
if (state == null) { if (state == null) {
sendError(responseObserver, "Android device session not found"); sendError(responseObserver, "未找到安卓设备会话");
return; return;
} }
responseObserver.onNext(GatewayServerPacket.newBuilder() responseObserver.onNext(GatewayServerPacket.newBuilder()

View File

@ -72,7 +72,7 @@ public class RealtimeMeetingGrpcService extends RealtimeMeetingServiceGrpc.Realt
private void handleAudio(AudioChunk audioChunk) { private void handleAudio(AudioChunk audioChunk) {
if (connectionId == null) { 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()); realtimeMeetingGrpcSessionService.onAudio(connectionId, audioChunk.getPcm16().toByteArray(), audioChunk.getSeq(), audioChunk.getLastChunk());
} }
@ -107,7 +107,7 @@ public class RealtimeMeetingGrpcService extends RealtimeMeetingServiceGrpc.Realt
.setRequestId(requestId == null ? "" : requestId) .setRequestId(requestId == null ? "" : requestId)
.setError(ErrorEvent.newBuilder() .setError(ErrorEvent.newBuilder()
.setCode("REALTIME_GRPC_ERROR") .setCode("REALTIME_GRPC_ERROR")
.setMessage(message == null || message.isBlank() ? "Realtime meeting gRPC processing failed" : message) .setMessage(message == null || message.isBlank() ? "实时会议 gRPC 处理失败" : message)
.setRetryable(false) .setRetryable(false)
.build()) .build())
.build()); .build());
@ -136,4 +136,4 @@ public class RealtimeMeetingGrpcService extends RealtimeMeetingServiceGrpc.Realt
} }
}; };
} }
} }

View File

@ -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); auth == null ? null : auth.getAppVersion(), auth == null ? null : auth.getPlatform(), auth == null ? null : auth.getAccessToken(), fallbackDeviceId, null, null);
} }
if (properties.isEnabled() && !properties.isAllowAnonymous()) { if (properties.isEnabled() && !properties.isAllowAnonymous()) {
throw new RuntimeException("Android gRPC auth is required"); throw new RuntimeException("缺少 Android gRPC 认证信息");
} }
return buildContext("NONE", true, return buildContext("NONE", true,
auth == null ? null : auth.getDeviceId(), auth == null ? null : auth.getDeviceId(),
@ -100,7 +100,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService {
null, null,
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, 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 fallbackDeviceId, InternalAuthCheckResponse authResult, LoginUser loginUser) {
String resolvedDeviceId = StringUtils.hasText(deviceId) ? deviceId : fallbackDeviceId; String resolvedDeviceId = StringUtils.hasText(deviceId) ? deviceId : fallbackDeviceId;
if (!StringUtils.hasText(resolvedDeviceId)) { if (!StringUtils.hasText(resolvedDeviceId)) {
throw new RuntimeException("Android deviceId is required"); throw new RuntimeException("缺少 Android deviceId");
} }
AndroidAuthContext context = new AndroidAuthContext(); AndroidAuthContext context = new AndroidAuthContext();
context.setAuthMode(authMode); context.setAuthMode(authMode);
@ -148,14 +148,14 @@ public class AndroidAuthServiceImpl implements AndroidAuthService {
private InternalAuthCheckResponse validateToken(String token) { private InternalAuthCheckResponse validateToken(String token) {
String resolvedToken = normalizeToken(token); String resolvedToken = normalizeToken(token);
if (!StringUtils.hasText(resolvedToken)) { if (!StringUtils.hasText(resolvedToken)) {
throw new RuntimeException("Android access token is required"); throw new RuntimeException("缺少 Android 访问令牌");
} }
InternalAuthCheckResponse authResult = tokenValidationService.validateAccessToken(resolvedToken); InternalAuthCheckResponse authResult = tokenValidationService.validateAccessToken(resolvedToken);
if (authResult == null || !authResult.isValid()) { 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) { if (authResult.getUserId() == null || authResult.getTenantId() == null) {
throw new RuntimeException("Android access token missing user or tenant context"); throw new RuntimeException("Android 访问令牌缺少用户或租户上下文");
} }
return authResult; return authResult;
} }
@ -166,7 +166,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService {
return null; return null;
} }
if (!authorization.startsWith(BEARER_PREFIX)) { 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(); 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) { private void requireAndroidHttpHeaders(String deviceId, String appVersion, String platform) {
if (!StringUtils.hasText(deviceId)) { if (!StringUtils.hasText(deviceId)) {
throw new RuntimeException("Android device_id is required"); throw new RuntimeException("缺少 Android device_id");
} }
if (!StringUtils.hasText(appVersion)) { if (!StringUtils.hasText(appVersion)) {
throw new RuntimeException("Android-App-Version is required"); throw new RuntimeException("缺少 Android-App-Version 请求头");
} }
if (!StringUtils.hasText(platform)) { if (!StringUtils.hasText(platform)) {
throw new RuntimeException("X-Android-Platform is required"); throw new RuntimeException("缺少 X-Android-Platform 请求头");
} }
} }

View File

@ -73,7 +73,7 @@ public class AndroidDeviceSessionServiceImpl implements AndroidDeviceSessionServ
objectMapper.writeValueAsString(topics == null ? List.of() : topics) objectMapper.writeValueAsString(topics == null ? List.of() : topics)
); );
} catch (Exception ex) { } 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.androidDeviceActiveConnectionKey(state.getDeviceId()), state.getConnectionId(), ttl);
redisTemplate.opsForValue().set(RedisKeys.androidDeviceConnectionKey(state.getConnectionId()), json, ttl); redisTemplate.opsForValue().set(RedisKeys.androidDeviceConnectionKey(state.getConnectionId()), json, ttl);
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Failed to write android device session state", ex); throw new RuntimeException("写入安卓设备会话状态失败", ex);
} }
} }

View File

@ -61,11 +61,11 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public MeetingVO createMeeting(LegacyMeetingCreateRequest request, LoginUser loginUser) { public MeetingVO createMeeting(LegacyMeetingCreateRequest request, LoginUser loginUser) {
if (request == null || request.getTitle() == null || request.getTitle().isBlank()) { 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()); LocalDateTime meetingTime = parseMeetingTime(request.getMeetingTime());
if (meetingTime == null) { if (meetingTime == null) {
throw new RuntimeException("Meeting time cannot be empty"); throw new RuntimeException("会议时间不能为空");
} }
Meeting meeting = meetingDomainSupport.initMeeting( Meeting meeting = meetingDomainSupport.initMeeting(
@ -99,22 +99,22 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
MultipartFile audioFile, MultipartFile audioFile,
LoginUser loginUser) throws IOException { LoginUser loginUser) throws IOException {
if (meetingId == null) { if (meetingId == null) {
throw new RuntimeException("meeting_id cannot be empty"); throw new RuntimeException("meeting_id 不能为空");
} }
if (audioFile == null || audioFile.isEmpty()) { if (audioFile == null || audioFile.isEmpty()) {
throw new RuntimeException("audio_file cannot be empty"); throw new RuntimeException("audio_file 不能为空");
} }
Meeting meeting = meetingAccessService.requireMeeting(meetingId); Meeting meeting = meetingAccessService.requireMeeting(meetingId);
meetingAccessService.assertCanEditMeeting(meeting, loginUser); meetingAccessService.assertCanEditMeeting(meeting, loginUser);
if (!forceReplace && meeting.getAudioUrl() != null && !meeting.getAudioUrl().isBlank()) { 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<MeetingTranscript>() long transcriptCount = transcriptMapper.selectCount(new LambdaQueryWrapper<MeetingTranscript>()
.eq(MeetingTranscript::getMeetingId, meetingId)); .eq(MeetingTranscript::getMeetingId, meetingId));
if (transcriptCount > 0) { 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( if (promptId != null && !promptTemplateService.isTemplateEnabledForUser(
promptId, promptId,
@ -122,7 +122,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
loginUser.getUserId(), loginUser.getUserId(),
loginUser.getIsPlatformAdmin(), loginUser.getIsPlatformAdmin(),
loginUser.getIsTenantAdmin())) { loginUser.getIsTenantAdmin())) {
throw new RuntimeException("Summary template unavailable"); throw new RuntimeException("总结模板不可用");
} }
RealtimeMeetingRuntimeProfile profile = runtimeProfileResolver.resolve( RealtimeMeetingRuntimeProfile profile = runtimeProfileResolver.resolve(
@ -183,7 +183,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
try { try {
return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} catch (DateTimeParseException ex) { } 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) .orderByDesc(LlmModel::getTenantId)
.last("LIMIT 1")); .last("LIMIT 1"));
if (model == null) { if (model == null) {
throw new RuntimeException("LLM model not found or disabled: " + modelCode); throw new RuntimeException("LLM 模型不存在或已禁用: " + modelCode);
} }
return model.getId(); return model.getId();
} }

View File

@ -99,7 +99,7 @@ public class AiModelServiceImpl implements AiModelService {
if (TYPE_ASR.equals(type)) { if (TYPE_ASR.equals(type)) {
AsrModel entity = asrModelMapper.selectById(dto.getId()); AsrModel entity = asrModelMapper.selectById(dto.getId());
if (entity == null) { if (entity == null) {
throw new RuntimeException("Model not found"); throw new RuntimeException("模型不存在");
} }
copyAsrProperties(dto, entity); copyAsrProperties(dto, entity);
pushAsrConfig(entity); pushAsrConfig(entity);
@ -111,7 +111,7 @@ public class AiModelServiceImpl implements AiModelService {
LlmModel entity = llmModelMapper.selectById(dto.getId()); LlmModel entity = llmModelMapper.selectById(dto.getId());
if (entity == null) { if (entity == null) {
throw new RuntimeException("Model not found"); throw new RuntimeException("模型不存在");
} }
copyLlmProperties(dto, entity); copyLlmProperties(dto, entity);
handleLlmDefaultLogic(entity); handleLlmDefaultLogic(entity);
@ -357,14 +357,14 @@ public class AiModelServiceImpl implements AiModelService {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() < 200 || response.statusCode() >= 300) { 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()); JsonNode root = objectMapper.readTree(response.body());
String responseContent = readConnectivityResponseContent(root); String responseContent = readConnectivityResponseContent(root);
String resultMessage = extractConnectivityResultMessage(responseContent); String resultMessage = extractConnectivityResultMessage(responseContent);
if (resultMessage == null || resultMessage.isBlank()) { if (resultMessage == null || resultMessage.isBlank()) {
throw new RuntimeException("Unexpected empty connectivity response, body=" + response.body()); throw new RuntimeException("连通性测试返回空响应,body=" + response.body());
} }
} catch (Exception e) { } catch (Exception e) {
String detail = describeException(e); String detail = describeException(e);
@ -412,13 +412,13 @@ public class AiModelServiceImpl implements AiModelService {
private void updateLocalProfile(AsrModel entity) { private void updateLocalProfile(AsrModel entity) {
if (entity.getBaseUrl() == null || entity.getBaseUrl().isBlank()) { 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()) { 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()) { if (entity.getModelCode() == null || entity.getModelCode().isBlank()) {
throw new RuntimeException("modelCode is required for ASR model"); throw new RuntimeException("ASR 模型必须配置 modelCode");
} }
Map<String, Object> mediaConfig = entity.getMediaConfig() == null ? Collections.emptyMap() : entity.getMediaConfig(); Map<String, Object> mediaConfig = entity.getMediaConfig() == null ? Collections.emptyMap() : entity.getMediaConfig();
@ -482,7 +482,7 @@ public class AiModelServiceImpl implements AiModelService {
private String appendPath(String baseUrl, String path) { private String appendPath(String baseUrl, String path) {
if (baseUrl == null || baseUrl.isBlank()) { if (baseUrl == null || baseUrl.isBlank()) {
throw new RuntimeException("baseUrl is required"); throw new RuntimeException("baseUrl 不能为空");
} }
String trimmedBaseUrl = baseUrl.trim(); String trimmedBaseUrl = baseUrl.trim();
if (path == null || path.isBlank()) { if (path == null || path.isBlank()) {
@ -747,10 +747,10 @@ public class AiModelServiceImpl implements AiModelService {
if (TYPE_ASR.equals(normalizeType(dto.getModelType()))) { if (TYPE_ASR.equals(normalizeType(dto.getModelType()))) {
Map<String, Object> mediaConfig = dto.getMediaConfig() == null ? Collections.emptyMap() : dto.getMediaConfig(); Map<String, Object> mediaConfig = dto.getMediaConfig() == null ? Collections.emptyMap() : dto.getMediaConfig();
if (readConfigString(mediaConfig.get("speakerModel")) == null) { 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) { 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; return;
} }
if (entity.getBaseUrl() == null || entity.getBaseUrl().isBlank()) { 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()) { 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("/") String targetUrl = entity.getBaseUrl().endsWith("/")
@ -867,10 +867,10 @@ public class AiModelServiceImpl implements AiModelService {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() < 200 || response.statusCode() >= 300) { 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) { } 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(); String normalized = type.trim().toUpperCase();
if (!TYPE_ASR.equals(normalized) && !TYPE_LLM.equals(normalized)) { if (!TYPE_ASR.equals(normalized) && !TYPE_LLM.equals(normalized)) {
throw new RuntimeException("Unsupported model type: " + type); throw new RuntimeException("不支持的模型类型:" + type);
} }
return normalized; return normalized;
} }

View File

@ -122,7 +122,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
.orderByDesc(AiTask::getId) .orderByDesc(AiTask::getId)
.last("limit 1")); .last("limit 1"));
if (asrText == null || asrText.isBlank()) { if (asrText == null || asrText.isBlank()) {
failPendingSummaryTask(sumTask, "No transcript content available for summary"); failPendingSummaryTask(sumTask, "没有可用于总结的转录内容");
updateMeetingStatus(meetingId, 4); updateMeetingStatus(meetingId, 4);
updateProgress(meetingId, -1, "未识别到可用于总结的转录内容", 0); updateProgress(meetingId, -1, "未识别到可用于总结的转录内容", 0);
return; return;
@ -136,7 +136,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
redisTemplate.delete(RedisKeys.meetingProgressKey(meetingId)); redisTemplate.delete(RedisKeys.meetingProgressKey(meetingId));
} catch (Exception e) { } catch (Exception e) {
log.error("Meeting {} AI Task Flow failed", meetingId, 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); updateMeetingStatus(meetingId, 4);
updateProgress(meetingId, -1, "分析失败: " + e.getMessage(), 0); updateProgress(meetingId, -1, "分析失败: " + e.getMessage(), 0);
} finally { } finally {
@ -164,14 +164,14 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
.orderByAsc(MeetingTranscript::getStartTime)); .orderByAsc(MeetingTranscript::getStartTime));
if (transcripts.isEmpty()) { if (transcripts.isEmpty()) {
failPendingSummaryTask(sumTask, "No transcript content available for summary"); failPendingSummaryTask(sumTask, "没有可用于总结的转录内容");
throw new RuntimeException("没有找到可用的转录文本,无法生成总结"); throw new RuntimeException("没有找到可用的转录文本,无法生成总结");
} }
String asrText = buildTranscriptText(transcripts); String asrText = buildTranscriptText(transcripts);
if (asrText == null || asrText.isBlank()) { if (asrText == null || asrText.isBlank()) {
failPendingSummaryTask(sumTask, "No transcript content available for summary"); failPendingSummaryTask(sumTask, "没有可用于总结的转录内容");
throw new RuntimeException("No transcript content available for summary"); throw new RuntimeException("没有可用于总结的转录内容");
} }
if (sumTask != null && sumTask.getStatus() == 0) { if (sumTask != null && sumTask.getStatus() == 0) {
processSummaryTask(meeting, asrText, sumTask); processSummaryTask(meeting, asrText, sumTask);
@ -205,7 +205,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
String respBody = postJson(submitUrl, req, asrModel.getApiKey()); String respBody = postJson(submitUrl, req, asrModel.getApiKey());
JsonNode submitNode = objectMapper.readTree(respBody); JsonNode submitNode = objectMapper.readTree(respBody);
if (submitNode.path("code").asInt() != 0) { if (submitNode.path("code").asInt() != 0) {
updateAiTaskFail(taskRecord, "Submission Failed: " + respBody); updateAiTaskFail(taskRecord, "提交失败:" + respBody);
throw new RuntimeException("ASR引擎拒绝请求: " + submitNode.path("msg").asText()); throw new RuntimeException("ASR引擎拒绝请求: " + submitNode.path("msg").asText());
} }
taskId = submitNode.path("data").path("task_id").asText(); taskId = submitNode.path("data").path("task_id").asText();
@ -231,7 +231,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
updateAiTaskSuccess(taskRecord, statusNode); updateAiTaskSuccess(taskRecord, statusNode);
break; break;
} else if ("failed".equalsIgnoreCase(status)) { } else if ("failed".equalsIgnoreCase(status)) {
updateAiTaskFail(taskRecord, "ASR engine reported failure: " + queryResp); updateAiTaskFail(taskRecord, "ASR 引擎返回失败:" + queryResp);
throw new RuntimeException("ASR引擎处理失败: " + data.path("message").asText()); throw new RuntimeException("ASR引擎处理失败: " + data.path("message").asText());
} else { } else {
int currentPercent = data.path("percentage").asInt(); int currentPercent = data.path("percentage").asInt();
@ -477,11 +477,11 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
Long summaryModelId = Long.valueOf(taskRecord.getTaskConfig().get("summaryModelId").toString()); Long summaryModelId = Long.valueOf(taskRecord.getTaskConfig().get("summaryModelId").toString());
AiModelVO llmModel = aiModelService.getModelById(summaryModelId, "LLM"); AiModelVO llmModel = aiModelService.getModelById(summaryModelId, "LLM");
if (llmModel == null) { if (llmModel == null) {
updateAiTaskFail(taskRecord, "LLM model not found: " + summaryModelId); updateAiTaskFail(taskRecord, "LLM 模型不存在:" + summaryModelId);
throw new RuntimeException("LLM模型配置不存在"); throw new RuntimeException("LLM模型配置不存在");
} }
if (!Integer.valueOf(1).equals(llmModel.getStatus())) { if (!Integer.valueOf(1).equals(llmModel.getStatus())) {
updateAiTaskFail(taskRecord, "LLM model disabled: " + summaryModelId); updateAiTaskFail(taskRecord, "LLM 模型已禁用:" + summaryModelId);
throw new RuntimeException("LLM模型未启用"); throw new RuntimeException("LLM模型未启用");
} }
@ -533,7 +533,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
markdownContent = meetingSummaryFileService.buildSummaryMarkdown(normalizedAnalysis); markdownContent = meetingSummaryFileService.buildSummaryMarkdown(normalizedAnalysis);
} }
if (markdownContent == null || markdownContent.isBlank()) { if (markdownContent == null || markdownContent.isBlank()) {
updateAiTaskFail(taskRecord, "LLM summary content parse failed: " + content); updateAiTaskFail(taskRecord, "LLM 总结内容解析失败:" + content);
throw new RuntimeException("AI总结结果解析失败未生成可保存的会议纪要"); throw new RuntimeException("AI总结结果解析失败未生成可保存的会议纪要");
} }
@ -565,7 +565,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
updateProgress(meeting.getId(), 100, "全流程分析完成", 0); updateProgress(meeting.getId(), 100, "全流程分析完成", 0);
} else { } else {
updateAiTaskFail(taskRecord, "LLM Summary failed: " + response.body()); updateAiTaskFail(taskRecord, "LLM 总结失败:" + response.body());
throw new RuntimeException("AI总结生成异常"); throw new RuntimeException("AI总结生成异常");
} }
} }
@ -630,7 +630,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
private String normalizeUrlComponent(String value, String fieldName) { private String normalizeUrlComponent(String value, String fieldName) {
if (value == null || value.isBlank()) { if (value == null || value.isBlank()) {
throw new IllegalArgumentException(fieldName + " cannot be blank"); throw new IllegalArgumentException(fieldName + "不能为空");
} }
return value.trim(); return value.trim();
} }

View File

@ -92,10 +92,10 @@ public class ClientDownloadServiceImpl extends ServiceImpl<ClientDownloadMapper,
@Override @Override
public Map<String, Object> uploadPackage(String platformCode, MultipartFile file) throws IOException { public Map<String, Object> uploadPackage(String platformCode, MultipartFile file) throws IOException {
if (platformCode == null || platformCode.isBlank()) { if (platformCode == null || platformCode.isBlank()) {
throw new RuntimeException("platformCode is required"); throw new RuntimeException("platformCode 不能为空");
} }
if (file == null || file.isEmpty()) { if (file == null || file.isEmpty()) {
throw new RuntimeException("file is required"); throw new RuntimeException("file 不能为空");
} }
String cleanCode = platformCode.trim().toLowerCase(); String cleanCode = platformCode.trim().toLowerCase();
String originalName = sanitizeFileName(file.getOriginalFilename()); String originalName = sanitizeFileName(file.getOriginalFilename());
@ -124,17 +124,17 @@ public class ClientDownloadServiceImpl extends ServiceImpl<ClientDownloadMapper,
private void validate(ClientDownloadDTO dto, boolean partial) { private void validate(ClientDownloadDTO dto, boolean partial) {
if (dto == null) { if (dto == null) {
throw new RuntimeException("payload is required"); throw new RuntimeException("payload 不能为空");
} }
if (!partial) { if (!partial) {
if (isBlank(dto.getPlatformCode())) { if (isBlank(dto.getPlatformCode())) {
throw new RuntimeException("platformCode is required"); throw new RuntimeException("platformCode 不能为空");
} }
if (isBlank(dto.getVersion())) { if (isBlank(dto.getVersion())) {
throw new RuntimeException("version is required"); throw new RuntimeException("version 不能为空");
} }
if (isBlank(dto.getDownloadUrl())) { if (isBlank(dto.getDownloadUrl())) {
throw new RuntimeException("downloadUrl is required"); throw new RuntimeException("downloadUrl 不能为空");
} }
} }
} }
@ -192,7 +192,7 @@ public class ClientDownloadServiceImpl extends ServiceImpl<ClientDownloadMapper,
private ClientDownload requireExisting(Long id) { private ClientDownload requireExisting(Long id) {
ClientDownload entity = this.getById(id); ClientDownload entity = this.getById(id);
if (entity == null) { if (entity == null) {
throw new RuntimeException("Client version not found"); throw new RuntimeException("客户端版本不存在");
} }
return entity; return entity;
} }

View File

@ -145,14 +145,14 @@ public class ExternalAppServiceImpl extends ServiceImpl<ExternalAppMapper, Exter
private void validate(ExternalAppDTO dto, boolean partial) { private void validate(ExternalAppDTO dto, boolean partial) {
if (dto == null) { if (dto == null) {
throw new RuntimeException("payload is required"); throw new RuntimeException("payload 不能为空");
} }
if (!partial) { if (!partial) {
if (isBlank(dto.getAppName())) { if (isBlank(dto.getAppName())) {
throw new RuntimeException("appName is required"); throw new RuntimeException("appName 不能为空");
} }
if (isBlank(dto.getAppType())) { if (isBlank(dto.getAppType())) {
throw new RuntimeException("appType is required"); throw new RuntimeException("appType 不能为空");
} }
} }
} }
@ -184,14 +184,14 @@ public class ExternalAppServiceImpl extends ServiceImpl<ExternalAppMapper, Exter
private ExternalApp requireExisting(Long id) { private ExternalApp requireExisting(Long id) {
ExternalApp entity = this.getById(id); ExternalApp entity = this.getById(id);
if (entity == null) { if (entity == null) {
throw new RuntimeException("External app not found"); throw new RuntimeException("外部应用不存在");
} }
return entity; return entity;
} }
private StoredFile storeFile(String folder, MultipartFile file) throws IOException { private StoredFile storeFile(String folder, MultipartFile file) throws IOException {
if (file == null || file.isEmpty()) { if (file == null || file.isEmpty()) {
throw new RuntimeException("file is required"); throw new RuntimeException("file 不能为空");
} }
String originalName = sanitizeFileName(file.getOriginalFilename()); String originalName = sanitizeFileName(file.getOriginalFilename());
String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/"; String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";

View File

@ -18,7 +18,7 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
public Meeting requireMeeting(Long meetingId) { public Meeting requireMeeting(Long meetingId) {
Meeting meeting = meetingMapper.selectById(meetingId); Meeting meeting = meetingMapper.selectById(meetingId);
if (meeting == null) { if (meeting == null) {
throw new RuntimeException("Meeting not found"); throw new RuntimeException("会议不存在");
} }
return meeting; return meeting;
} }
@ -27,7 +27,7 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
public Meeting requireMeetingIgnoreTenant(Long meetingId) { public Meeting requireMeetingIgnoreTenant(Long meetingId) {
Meeting meeting = meetingMapper.selectByIdIgnoreTenant(meetingId); Meeting meeting = meetingMapper.selectByIdIgnoreTenant(meetingId);
if (meeting == null) { if (meeting == null) {
throw new RuntimeException("Meeting not found"); throw new RuntimeException("会议不存在");
} }
return meeting; return meeting;
} }
@ -45,10 +45,10 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
} }
String providedPassword = normalizePreviewPassword(accessPassword); String providedPassword = normalizePreviewPassword(accessPassword);
if (providedPassword == null) { if (providedPassword == null) {
throw new RuntimeException("Access password is required"); throw new RuntimeException("访问密码不能为空");
} }
if (!expectedPassword.equals(providedPassword)) { if (!expectedPassword.equals(providedPassword)) {
throw new RuntimeException("Access password is incorrect"); throw new RuntimeException("访问密码错误");
} }
} }
@ -58,7 +58,7 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
return; return;
} }
if (!isSameTenant(meeting, loginUser)) { if (!isSameTenant(meeting, loginUser)) {
throw new RuntimeException("No permission to view this meeting"); throw new RuntimeException("无权查看该会议");
} }
if (isTenantAdmin(loginUser)) { if (isTenantAdmin(loginUser)) {
return; return;
@ -66,7 +66,7 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
if (isCreator(meeting, loginUser) || isParticipant(meeting, loginUser)) { if (isCreator(meeting, loginUser) || isParticipant(meeting, loginUser)) {
return; return;
} }
throw new RuntimeException("No permission to view this meeting"); throw new RuntimeException("无权查看该会议");
} }
@Override @Override
@ -75,12 +75,12 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
return; return;
} }
if (!isSameTenant(meeting, loginUser)) { if (!isSameTenant(meeting, loginUser)) {
throw new RuntimeException("No permission to edit this meeting"); throw new RuntimeException("无权编辑该会议");
} }
if (isTenantAdmin(loginUser) || isCreator(meeting, loginUser)) { if (isTenantAdmin(loginUser) || isCreator(meeting, loginUser)) {
return; return;
} }
throw new RuntimeException("No permission to edit this meeting"); throw new RuntimeException("无权编辑该会议");
} }
@Override @Override
@ -89,25 +89,25 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
return; return;
} }
if (!isSameTenant(meeting, loginUser)) { if (!isSameTenant(meeting, loginUser)) {
throw new RuntimeException("No permission to manage this realtime meeting"); throw new RuntimeException("无权管理该实时会议");
} }
if (isTenantAdmin(loginUser) || isCreator(meeting, loginUser)) { if (isTenantAdmin(loginUser) || isCreator(meeting, loginUser)) {
return; return;
} }
throw new RuntimeException("No permission to manage this realtime meeting"); throw new RuntimeException("无权管理该实时会议");
} }
@Override @Override
public void assertCanControlRealtimeMeeting(Meeting meeting, LoginUser loginUser, String currentPlatform) { public void assertCanControlRealtimeMeeting(Meeting meeting, LoginUser loginUser, String currentPlatform) {
assertCanManageRealtimeMeeting(meeting, loginUser); assertCanManageRealtimeMeeting(meeting, loginUser);
if (!MeetingConstants.TYPE_REALTIME.equalsIgnoreCase(meeting.getMeetingType())) { if (!MeetingConstants.TYPE_REALTIME.equalsIgnoreCase(meeting.getMeetingType())) {
throw new RuntimeException("Current meeting is not a realtime meeting"); throw new RuntimeException("当前会议不是实时会议");
} }
if (meeting.getMeetingSource() == null || meeting.getMeetingSource().isBlank()) { if (meeting.getMeetingSource() == null || meeting.getMeetingSource().isBlank()) {
return; return;
} }
if (!meeting.getMeetingSource().equalsIgnoreCase(currentPlatform)) { if (!meeting.getMeetingSource().equalsIgnoreCase(currentPlatform)) {
throw new RuntimeException("Cross-platform realtime takeover is not allowed"); throw new RuntimeException("不允许跨平台接管实时会议");
} }
} }
@ -117,12 +117,12 @@ public class MeetingAccessServiceImpl implements MeetingAccessService {
return; return;
} }
if (!isSameTenant(meeting, loginUser)) { if (!isSameTenant(meeting, loginUser)) {
throw new RuntimeException("No permission to export this meeting"); throw new RuntimeException("无权导出该会议");
} }
if (isTenantAdmin(loginUser) || isCreator(meeting, loginUser) || isParticipant(meeting, loginUser)) { if (isTenantAdmin(loginUser) || isCreator(meeting, loginUser) || isParticipant(meeting, loginUser)) {
return; return;
} }
throw new RuntimeException("No permission to export this meeting"); throw new RuntimeException("无权导出该会议");
} }
private boolean isPlatformAdmin(LoginUser loginUser) { private boolean isPlatformAdmin(LoginUser loginUser) {

View File

@ -81,7 +81,7 @@ public class MeetingAudioUploadSupport {
private static String normalizeUploadPath(String uploadPath) { private static String normalizeUploadPath(String uploadPath) {
if (!StringUtils.hasText(uploadPath)) { if (!StringUtils.hasText(uploadPath)) {
throw new IllegalArgumentException("uploadPath must not be blank"); throw new IllegalArgumentException("uploadPath 不能为空");
} }
return uploadPath.endsWith("/") || uploadPath.endsWith("\\") ? uploadPath : uploadPath + "/"; return uploadPath.endsWith("/") || uploadPath.endsWith("\\") ? uploadPath : uploadPath + "/";
} }

View File

@ -43,12 +43,12 @@ public class MeetingAuthorizationServiceImpl implements MeetingAuthorizationServ
public void assertCanControlRealtimeMeeting(Meeting meeting, AndroidAuthContext authContext, String currentPlatform) { public void assertCanControlRealtimeMeeting(Meeting meeting, AndroidAuthContext authContext, String currentPlatform) {
if (allowAnonymous(authContext)) { if (allowAnonymous(authContext)) {
if (!MeetingConstants.TYPE_REALTIME.equalsIgnoreCase(meeting.getMeetingType())) { if (!MeetingConstants.TYPE_REALTIME.equalsIgnoreCase(meeting.getMeetingType())) {
throw new RuntimeException("Current meeting is not a realtime meeting"); throw new RuntimeException("当前会议不是实时会议");
} }
if (meeting.getMeetingSource() != null if (meeting.getMeetingSource() != null
&& !meeting.getMeetingSource().isBlank() && !meeting.getMeetingSource().isBlank()
&& !meeting.getMeetingSource().equalsIgnoreCase(currentPlatform)) { && !meeting.getMeetingSource().equalsIgnoreCase(currentPlatform)) {
throw new RuntimeException("Cross-platform realtime takeover is not allowed"); throw new RuntimeException("不允许跨平台接管实时会议");
} }
return; return;
} }

View File

@ -251,13 +251,13 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void completeRealtimeMeeting(Long meetingId, String audioUrl, boolean overwriteAudio) { public void completeRealtimeMeeting(Long meetingId, String audioUrl, boolean overwriteAudio) {
Meeting meeting = meetingService.getById(meetingId); Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) { if (meeting == null) {
throw new RuntimeException("Meeting not found"); throw new RuntimeException("会议不存在");
} }
RealtimeMeetingSessionStatusVO currentStatus = realtimeMeetingSessionStateService.getStatus(meetingId); RealtimeMeetingSessionStatusVO currentStatus = realtimeMeetingSessionStateService.getStatus(meetingId);
if (overwriteAudio) { if (overwriteAudio) {
if (audioUrl == null || audioUrl.isBlank()) { if (audioUrl == null || audioUrl.isBlank()) {
throw new RuntimeException("Audio URL is required when overwriteAudio is true"); throw new RuntimeException("overwriteAudio 为 true 时必须提供音频地址");
} }
meeting.setAudioUrl(meetingDomainSupport.relocateAudioUrl(meetingId, audioUrl)); meeting.setAudioUrl(meetingDomainSupport.relocateAudioUrl(meetingId, audioUrl));
markAudioSaveSuccess(meeting); markAudioSaveSuccess(meeting);
@ -325,7 +325,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
private void prepareOfflineReprocessTasks(Long meetingId, RealtimeMeetingSessionStatusVO currentStatus) { private void prepareOfflineReprocessTasks(Long meetingId, RealtimeMeetingSessionStatusVO currentStatus) {
RealtimeMeetingResumeConfig resumeConfig = currentStatus == null ? null : currentStatus.getResumeConfig(); RealtimeMeetingResumeConfig resumeConfig = currentStatus == null ? null : currentStatus.getResumeConfig();
if (resumeConfig == null || resumeConfig.getAsrModelId() == null) { if (resumeConfig == null || resumeConfig.getAsrModelId() == null) {
throw new RuntimeException("Realtime resume config missing, cannot overwrite audio"); throw new RuntimeException("缺少实时恢复配置,无法覆盖音频");
} }
AiTask asrTask = aiTaskService.getOne(new LambdaQueryWrapper<AiTask>() AiTask asrTask = aiTaskService.getOne(new LambdaQueryWrapper<AiTask>()
@ -358,7 +358,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
.orderByDesc(AiTask::getId) .orderByDesc(AiTask::getId)
.last("LIMIT 1")); .last("LIMIT 1"));
if (summaryTask == null) { if (summaryTask == null) {
throw new RuntimeException("Summary task missing, cannot continue offline process"); throw new RuntimeException("缺少总结任务,无法继续离线流程");
} }
resetAiTask(summaryTask, summaryTask.getTaskConfig()); resetAiTask(summaryTask, summaryTask.getTaskConfig());
@ -425,7 +425,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void updateMeetingTranscript(UpdateMeetingTranscriptCommand command) { public void updateMeetingTranscript(UpdateMeetingTranscriptCommand command) {
String content = command.getContent() == null ? "" : command.getContent().trim(); String content = command.getContent() == null ? "" : command.getContent().trim();
if (content.isEmpty()) { if (content.isEmpty()) {
throw new RuntimeException("Transcript content cannot be empty"); throw new RuntimeException("转录内容不能为空");
} }
int updated = transcriptMapper.update(null, new LambdaUpdateWrapper<MeetingTranscript>() int updated = transcriptMapper.update(null, new LambdaUpdateWrapper<MeetingTranscript>()
@ -433,7 +433,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
.eq(MeetingTranscript::getId, command.getTranscriptId()) .eq(MeetingTranscript::getId, command.getTranscriptId())
.set(MeetingTranscript::getContent, content)); .set(MeetingTranscript::getContent, content));
if (updated <= 0) { 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) { public void updateSummaryContent(Long meetingId, String summaryContent) {
Meeting meeting = meetingService.getById(meetingId); Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) { if (meeting == null) {
throw new RuntimeException("Meeting not found"); throw new RuntimeException("会议不存在");
} }
meetingSummaryFileService.updateSummaryContent(meeting, summaryContent); meetingSummaryFileService.updateSummaryContent(meeting, summaryContent);
} }
@ -479,7 +479,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void reSummary(Long meetingId, Long summaryModelId, Long promptId, String userPrompt) { public void reSummary(Long meetingId, Long summaryModelId, Long promptId, String userPrompt) {
Meeting meeting = meetingService.getById(meetingId); Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) { if (meeting == null) {
throw new RuntimeException("Meeting not found"); throw new RuntimeException("会议不存在");
} }
meetingDomainSupport.createSummaryTask(meetingId, summaryModelId, promptId, userPrompt); meetingDomainSupport.createSummaryTask(meetingId, summaryModelId, promptId, userPrompt);
@ -493,7 +493,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void retryTranscription(Long meetingId) { public void retryTranscription(Long meetingId) {
Meeting meeting = meetingService.getById(meetingId); Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) { if (meeting == null) {
throw new RuntimeException("Meeting not found"); throw new RuntimeException("会议不存在");
} }
long transcriptCount = transcriptMapper.selectCount(new LambdaQueryWrapper<MeetingTranscript>() long transcriptCount = transcriptMapper.selectCount(new LambdaQueryWrapper<MeetingTranscript>()

View File

@ -107,7 +107,7 @@ public class MeetingDomainSupport {
return plan.relocatedUrl(); return plan.relocatedUrl();
} catch (Exception ex) { } catch (Exception ex) {
log.error("Failed to move audio file for meeting {}", meetingId, 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 { try {
Files.deleteIfExists(path); Files.deleteIfExists(path);
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Delete meeting artifact failed: " + path, ex); throw new RuntimeException("删除会议产物失败:" + path, ex);
} }
}); });
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw ex; throw ex;
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Delete meeting artifacts failed", ex); throw new RuntimeException("删除会议产物失败", ex);
} }
} }

View File

@ -55,7 +55,7 @@ public class MeetingExportServiceImpl implements MeetingExportService {
ext = "pdf"; ext = "pdf";
contentType = MediaType.APPLICATION_PDF_VALUE; contentType = MediaType.APPLICATION_PDF_VALUE;
} else { } else {
throw new RuntimeException("Unsupported export format"); throw new RuntimeException("不支持的导出格式");
} }
try { try {
@ -79,7 +79,7 @@ public class MeetingExportServiceImpl implements MeetingExportService {
return new MeetingSummaryExportResult(bytes, contentType, safeTitle + "-AI-Summary." + ext); return new MeetingSummaryExportResult(bytes, contentType, safeTitle + "-AI-Summary." + ext);
} catch (IOException ex) { } 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(); builder.run();
return out.toByteArray(); return out.toByteArray();
} catch (Exception ex) { } catch (Exception ex) {
throw new IOException("PDF generation failed", ex); throw new IOException("PDF 生成失败", ex);
} }
} }

View File

@ -36,13 +36,13 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService
public Path requireSummarySourcePath(Meeting meeting) { public Path requireSummarySourcePath(Meeting meeting) {
AiTask summaryTask = findLatestSummaryTask(meeting); AiTask summaryTask = findLatestSummaryTask(meeting);
if (summaryTask == null || summaryTask.getResultFilePath() == null || summaryTask.getResultFilePath().isBlank()) { 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 + "/"; String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
Path summaryPath = Paths.get(basePath, summaryTask.getResultFilePath().replace("\\", "/")); Path summaryPath = Paths.get(basePath, summaryTask.getResultFilePath().replace("\\", "/"));
if (!Files.exists(summaryPath)) { if (!Files.exists(summaryPath)) {
throw new RuntimeException("Summary source file is missing"); throw new RuntimeException("总结源文件缺失");
} }
return summaryPath; return summaryPath;
} }
@ -56,7 +56,7 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
return null; return null;
} catch (Exception ex) { } 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; return null;
} catch (Exception ex) { } 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) { public void updateSummaryContent(Meeting meeting, String summaryContent) {
AiTask summaryTask = findLatestSummaryTask(meeting); AiTask summaryTask = findLatestSummaryTask(meeting);
if (summaryTask == null || summaryTask.getResultFilePath() == null || summaryTask.getResultFilePath().isBlank()) { 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 + "/"; String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
@ -236,7 +236,7 @@ public class MeetingSummaryFileServiceImpl implements MeetingSummaryFileService
String frontMatter = extractFrontMatter(existingContent, meeting, summaryTask); String frontMatter = extractFrontMatter(existingContent, meeting, summaryTask);
Files.writeString(summaryPath, frontMatter + normalizeSummaryMarkdown(summaryContent), StandardCharsets.UTF_8); Files.writeString(summaryPath, frontMatter + normalizeSummaryMarkdown(summaryContent), StandardCharsets.UTF_8);
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Update summary file failed", ex); throw new RuntimeException("更新总结文件失败", ex);
} }
} }

View File

@ -72,16 +72,16 @@ public class RealtimeMeetingSessionStateServiceImpl implements RealtimeMeetingSe
String currentStatus = status.getStatus(); String currentStatus = status.getStatus();
if ("COMPLETING".equals(currentStatus)) { if ("COMPLETING".equals(currentStatus)) {
throw new RuntimeException("Realtime meeting is completing"); throw new RuntimeException("实时会议正在结束处理中");
} }
if ("COMPLETED".equals(currentStatus)) { if ("COMPLETED".equals(currentStatus)) {
throw new RuntimeException("Realtime meeting has completed"); throw new RuntimeException("实时会议已结束");
} }
if ("ACTIVE".equals(currentStatus) || Boolean.TRUE.equals(status.getActiveConnection())) { 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())) { 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) objectMapper.writeValueAsString(state)
); );
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Failed to write realtime meeting session state", ex); throw new RuntimeException("写入实时会议会话状态失败", ex);
} }
} }

View File

@ -42,10 +42,10 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
Boolean enableTextRefine, Boolean saveAudio, Boolean enableTextRefine, Boolean saveAudio,
List<Map<String, Object>> hotwords, LoginUser loginUser) { List<Map<String, Object>> hotwords, LoginUser loginUser) {
if (meetingId == null) { if (meetingId == null) {
throw new RuntimeException("Meeting ID is required"); throw new RuntimeException("会议 ID 不能为空");
} }
if (asrModelId == null) { if (asrModelId == null) {
throw new RuntimeException("ASR model ID is required"); throw new RuntimeException("ASR 模型 ID 不能为空");
} }
Meeting meeting = meetingAccessService.requireMeeting(meetingId); Meeting meeting = meetingAccessService.requireMeeting(meetingId);
@ -56,12 +56,12 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
AiModelVO asrModel = aiModelService.getModelById(asrModelId, "ASR"); AiModelVO asrModel = aiModelService.getModelById(asrModelId, "ASR");
if (asrModel == null) { if (asrModel == null) {
throw new RuntimeException("ASR model not found"); throw new RuntimeException("ASR 模型不存在");
} }
String targetWsUrl = resolveWsUrl(asrModel); String targetWsUrl = resolveWsUrl(asrModel);
if (targetWsUrl == null || targetWsUrl.isBlank()) { if (targetWsUrl == null || targetWsUrl.isBlank()) {
throw new RuntimeException("ASR model WebSocket is not configured"); throw new RuntimeException("ASR 模型未配置 WebSocket 地址");
} }
RealtimeMeetingResumeConfig resumeConfig = new RealtimeMeetingResumeConfig(); RealtimeMeetingResumeConfig resumeConfig = new RealtimeMeetingResumeConfig();
@ -97,7 +97,7 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
SESSION_TTL SESSION_TTL
); );
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Failed to create realtime socket session", ex); throw new RuntimeException("创建实时 Socket 会话失败", ex);
} }
RealtimeSocketSessionVO vo = new RealtimeSocketSessionVO(); RealtimeSocketSessionVO vo = new RealtimeSocketSessionVO();
@ -131,7 +131,7 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
try { try {
return objectMapper.readValue(raw, RealtimeSocketSessionData.class); return objectMapper.readValue(raw, RealtimeSocketSessionData.class);
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Failed to read realtime socket session", ex); throw new RuntimeException("读取实时 Socket 会话失败", ex);
} }
} }

View File

@ -170,7 +170,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
@Override @Override
public ScreenSaverImageUploadVO uploadImage(MultipartFile file) throws IOException { public ScreenSaverImageUploadVO uploadImage(MultipartFile file) throws IOException {
if (file == null || file.isEmpty()) { if (file == null || file.isEmpty()) {
throw new RuntimeException("image file is required"); throw new RuntimeException("图片文件不能为空");
} }
String originalName = sanitizeFileName(file.getOriginalFilename()); String originalName = sanitizeFileName(file.getOriginalFilename());
String format = resolveAndValidateFormat(originalName, file.getContentType()); String format = resolveAndValidateFormat(originalName, file.getContentType());
@ -198,7 +198,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
@Override @Override
public ScreenSaverUserSettingsVO getMySettings(LoginUser loginUser) { public ScreenSaverUserSettingsVO getMySettings(LoginUser loginUser) {
if (loginUser == null || loginUser.getUserId() == null) { if (loginUser == null || loginUser.getUserId() == null) {
throw new RuntimeException("login user is required"); throw new RuntimeException("登录用户不能为空");
} }
return buildUserSettingsVO(loginUser.getUserId(), resolveDisplayDurationSec(loginUser.getTenantId(), loginUser.getUserId())); return buildUserSettingsVO(loginUser.getUserId(), resolveDisplayDurationSec(loginUser.getTenantId(), loginUser.getUserId()));
} }
@ -207,7 +207,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ScreenSaverUserSettingsVO updateMySettings(ScreenSaverUserSettingsDTO dto, LoginUser loginUser) { public ScreenSaverUserSettingsVO updateMySettings(ScreenSaverUserSettingsDTO dto, LoginUser loginUser) {
if (loginUser == null || loginUser.getUserId() == null || loginUser.getTenantId() == null) { if (loginUser == null || loginUser.getUserId() == null || loginUser.getTenantId() == null) {
throw new RuntimeException("login user is required"); throw new RuntimeException("登录用户不能为空");
} }
Integer displayDurationSec = dto == null ? null : dto.getDisplayDurationSec(); Integer displayDurationSec = dto == null ? null : dto.getDisplayDurationSec();
validateDisplayDurationSec(displayDurationSec); validateDisplayDurationSec(displayDurationSec);
@ -263,12 +263,12 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
} }
String scopeType = normalizeScopeType(dto.getScopeType()); String scopeType = normalizeScopeType(dto.getScopeType());
if (!SCOPE_PLATFORM.equals(scopeType) && !SCOPE_USER.equals(scopeType)) { if (!SCOPE_PLATFORM.equals(scopeType) && !SCOPE_USER.equals(scopeType)) {
throw new RuntimeException("scopeType only supports PLATFORM or USER"); throw new RuntimeException("scopeType 仅支持 PLATFORM 或 USER");
} }
if (SCOPE_USER.equals(scopeType)) { if (SCOPE_USER.equals(scopeType)) {
dto.setOwnerUserId(loginUser.getUserId()); dto.setOwnerUserId(loginUser.getUserId());
} else if (!isAdmin(loginUser)) { } else if (!isAdmin(loginUser)) {
throw new RuntimeException("no permission to create platform screen saver"); throw new RuntimeException("无权创建平台级屏保");
} }
return dto; return dto;
} }
@ -419,14 +419,14 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
private void validate(ScreenSaverDTO dto, boolean partial, ScreenSaver existing) { private void validate(ScreenSaverDTO dto, boolean partial, ScreenSaver existing) {
if (dto == null) { if (dto == null) {
throw new RuntimeException("payload is required"); throw new RuntimeException("payload 不能为空");
} }
if (!partial) { if (!partial) {
if (!StringUtils.hasText(dto.getName())) { if (!StringUtils.hasText(dto.getName())) {
throw new RuntimeException("name is required"); throw new RuntimeException("name 不能为空");
} }
if (!StringUtils.hasText(dto.getImageUrl())) { if (!StringUtils.hasText(dto.getImageUrl())) {
throw new RuntimeException("imageUrl is required"); throw new RuntimeException("imageUrl 不能为空");
} }
requireImageMetadata(dto); requireImageMetadata(dto);
} }
@ -437,25 +437,25 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
String resolvedScopeType = resolveScopeTypeForValidation(dto, existing); String resolvedScopeType = resolveScopeTypeForValidation(dto, existing);
Long resolvedOwnerUserId = dto.getOwnerUserId() != null ? dto.getOwnerUserId() : existing == null ? null : existing.getOwnerUserId(); Long resolvedOwnerUserId = dto.getOwnerUserId() != null ? dto.getOwnerUserId() : existing == null ? null : existing.getOwnerUserId();
if (SCOPE_USER.equals(resolvedScopeType) && resolvedOwnerUserId == null) { if (SCOPE_USER.equals(resolvedScopeType) && resolvedOwnerUserId == null) {
throw new RuntimeException("ownerUserId is required when scopeType is USER"); throw new RuntimeException("scopeType 为 USER 时 ownerUserId 不能为空");
} }
if (!SCOPE_PLATFORM.equals(resolvedScopeType) && !SCOPE_USER.equals(resolvedScopeType)) { if (!SCOPE_PLATFORM.equals(resolvedScopeType) && !SCOPE_USER.equals(resolvedScopeType)) {
throw new RuntimeException("scopeType only supports PLATFORM or USER"); throw new RuntimeException("scopeType 仅支持 PLATFORM 或 USER");
} }
} }
private void requireImageMetadata(ScreenSaverDTO dto) { private void requireImageMetadata(ScreenSaverDTO dto) {
if (!StringUtils.hasText(dto.getImageUrl())) { if (!StringUtils.hasText(dto.getImageUrl())) {
throw new RuntimeException("imageUrl is required"); throw new RuntimeException("imageUrl 不能为空");
} }
if (dto.getImageWidth() == null || dto.getImageHeight() == null || !StringUtils.hasText(dto.getImageFormat())) { if (dto.getImageWidth() == null || dto.getImageHeight() == null || !StringUtils.hasText(dto.getImageFormat())) {
throw new RuntimeException("image metadata is required"); throw new RuntimeException("图片元数据不能为空");
} }
// if (dto.getImageWidth() != REQUIRED_WIDTH || dto.getImageHeight() != REQUIRED_HEIGHT) { // if (dto.getImageWidth() != REQUIRED_WIDTH || dto.getImageHeight() != REQUIRED_HEIGHT) {
// throw new RuntimeException("image must be 1280x800"); // throw new RuntimeException("image must be 1280x800");
// } // }
if (!ALLOWED_FORMATS.contains(dto.getImageFormat().trim().toLowerCase())) { if (!ALLOWED_FORMATS.contains(dto.getImageFormat().trim().toLowerCase())) {
throw new RuntimeException("imageFormat only supports jpg/jpeg/png"); throw new RuntimeException("imageFormat 仅支持 jpg/jpeg/png");
} }
} }
@ -510,7 +510,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
private ScreenSaver requireExisting(Long id) { private ScreenSaver requireExisting(Long id) {
ScreenSaver entity = this.getById(id); ScreenSaver entity = this.getById(id);
if (entity == null) { if (entity == null) {
throw new RuntimeException("screen saver not found"); throw new RuntimeException("屏保不存在");
} }
return entity; return entity;
} }
@ -523,7 +523,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
private void assertCanManageEntity(ScreenSaver entity, LoginUser loginUser) { private void assertCanManageEntity(ScreenSaver entity, LoginUser loginUser) {
if (!canManageEntity(entity, loginUser)) { if (!canManageEntity(entity, loginUser)) {
throw new RuntimeException("no permission to modify this screen saver"); throw new RuntimeException("无权修改该屏保");
} }
} }
@ -542,10 +542,10 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
return; return;
} }
if (dto.getScopeType() != null && !Objects.equals(normalizeScopeType(dto.getScopeType()), entity.getScopeType())) { if (dto.getScopeType() != null && !Objects.equals(normalizeScopeType(dto.getScopeType()), entity.getScopeType())) {
throw new RuntimeException("no permission to change scopeType"); throw new RuntimeException("无权修改 scopeType");
} }
if (dto.getOwnerUserId() != null && !Objects.equals(dto.getOwnerUserId(), entity.getOwnerUserId())) { if (dto.getOwnerUserId() != null && !Objects.equals(dto.getOwnerUserId(), entity.getOwnerUserId())) {
throw new RuntimeException("no permission to change ownerUserId"); throw new RuntimeException("无权修改 ownerUserId");
} }
} }
@ -595,25 +595,25 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
private void validateStatus(Integer status) { private void validateStatus(Integer status) {
if (status == null || (status != 0 && status != 1)) { if (status == null || (status != 0 && status != 1)) {
throw new RuntimeException("status only supports 0 or 1"); throw new RuntimeException("status 仅支持 0 或 1");
} }
} }
private void validateDisplayDurationSec(Integer displayDurationSec) { private void validateDisplayDurationSec(Integer displayDurationSec) {
if (displayDurationSec == null || displayDurationSec < MIN_DISPLAY_DURATION_SEC || displayDurationSec > MAX_DISPLAY_DURATION_SEC) { if (displayDurationSec == null || displayDurationSec < MIN_DISPLAY_DURATION_SEC || displayDurationSec > 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) { private String resolveAndValidateFormat(String fileName, String contentType) {
String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
if (!ALLOWED_FORMATS.contains(extension)) { if (!ALLOWED_FORMATS.contains(extension)) {
throw new RuntimeException("only jpg/jpeg/png are supported"); throw new RuntimeException("仅支持 jpg/jpeg/png 格式");
} }
if (StringUtils.hasText(contentType)) { if (StringUtils.hasText(contentType)) {
String normalized = contentType.trim().toLowerCase(); String normalized = contentType.trim().toLowerCase();
if (!normalized.equals("image/jpeg") && !normalized.equals("image/png") && !normalized.equals("image/jpg")) { 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; return extension;
@ -623,7 +623,7 @@ public class ScreenSaverServiceImpl extends ServiceImpl<ScreenSaverMapper, Scree
try (InputStream inputStream = file.getInputStream()) { try (InputStream inputStream = file.getInputStream()) {
BufferedImage image = ImageIO.read(inputStream); BufferedImage image = ImageIO.read(inputStream);
if (image == null) { if (image == null) {
throw new RuntimeException("invalid image file"); throw new RuntimeException("图片文件无效");
} }
return new ImageMetadata(image.getWidth(), image.getHeight()); return new ImageMetadata(image.getWidth(), image.getHeight());
} }

View File

@ -209,7 +209,7 @@ public class SpeakerServiceImpl extends ServiceImpl<SpeakerMapper, Speaker> impl
} }
} catch (IOException e) { } catch (IOException e) {
log.error("Create voiceprints directory error", e); log.error("Create voiceprints directory error", e);
throw new RuntimeException("Failed to initialize storage"); throw new RuntimeException("初始化存储失败");
} }
String fileName = UUID.randomUUID().toString() + extension; String fileName = UUID.randomUUID().toString() + extension;
@ -218,7 +218,7 @@ public class SpeakerServiceImpl extends ServiceImpl<SpeakerMapper, Speaker> impl
Files.copy(file.getInputStream(), filePath); Files.copy(file.getInputStream(), filePath);
} catch (IOException e) { } catch (IOException e) {
log.error("Save voice file error", e); log.error("Save voice file error", e);
throw new RuntimeException("Failed to save voice file"); throw new RuntimeException("保存声纹文件失败");
} }
speaker.setVoicePath("voiceprints/" + fileName); speaker.setVoicePath("voiceprints/" + fileName);

View File

@ -52,7 +52,7 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS
ttl ttl
); );
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Failed to create realtime gRPC session", ex); throw new RuntimeException("创建实时会议 gRPC 会话失败", ex);
} }
AndroidRealtimeGrpcSessionVO vo = new AndroidRealtimeGrpcSessionVO(); AndroidRealtimeGrpcSessionVO vo = new AndroidRealtimeGrpcSessionVO();
@ -84,13 +84,13 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS
try { try {
return objectMapper.readValue(raw, AndroidRealtimeGrpcSessionData.class); return objectMapper.readValue(raw, AndroidRealtimeGrpcSessionData.class);
} catch (Exception ex) { } 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) { private PreparedRealtimeSession prepareSession(Long meetingId, AndroidOpenRealtimeGrpcSessionCommand command, AndroidAuthContext authContext) {
if (meetingId == null) { if (meetingId == null) {
throw new RuntimeException("Meeting ID is required"); throw new RuntimeException("会议 ID 不能为空");
} }
Meeting meeting = meetingAccessService.requireMeeting(meetingId); Meeting meeting = meetingAccessService.requireMeeting(meetingId);
meetingAuthorizationService.assertCanControlRealtimeMeeting(meeting, authContext, MeetingConstants.SOURCE_ANDROID); meetingAuthorizationService.assertCanControlRealtimeMeeting(meeting, authContext, MeetingConstants.SOURCE_ANDROID);
@ -104,17 +104,17 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS
resolveDefaultAsrModelId(meeting.getTenantId()) resolveDefaultAsrModelId(meeting.getTenantId())
); );
if (asrModelId == null) { if (asrModelId == null) {
throw new RuntimeException("ASR model ID is required"); throw new RuntimeException("ASR 模型 ID 不能为空");
} }
realtimeMeetingSessionStateService.assertCanOpenSession(meetingId); realtimeMeetingSessionStateService.assertCanOpenSession(meetingId);
AiModelVO asrModel = aiModelService.getModelById(asrModelId, "ASR"); AiModelVO asrModel = aiModelService.getModelById(asrModelId, "ASR");
if (asrModel == null) { if (asrModel == null) {
throw new RuntimeException("ASR model not found"); throw new RuntimeException("ASR 模型不存在");
} }
String targetWsUrl = resolveWsUrl(asrModel); String targetWsUrl = resolveWsUrl(asrModel);
if (targetWsUrl == null || targetWsUrl.isBlank()) { 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); RealtimeMeetingResumeConfig resumeConfig = buildResumeConfig(command, currentResumeConfig, asrModelId);
@ -133,7 +133,7 @@ public class AndroidRealtimeSessionTicketServiceImpl implements AndroidRealtimeS
try { try {
sessionData.setStartMessageJson(objectMapper.writeValueAsString(startMessage)); sessionData.setStartMessageJson(objectMapper.writeValueAsString(startMessage));
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Failed to serialize realtime start message", ex); throw new RuntimeException("序列化实时启动消息失败", ex);
} }
return new PreparedRealtimeSession(sessionData, resumeConfig, latestStatus); return new PreparedRealtimeSession(sessionData, resumeConfig, latestStatus);
} }

View File

@ -54,22 +54,22 @@ public class RealtimeMeetingGrpcSessionServiceImpl implements RealtimeMeetingGrp
} else if (streamToken != null && !streamToken.isBlank()) { } else if (streamToken != null && !streamToken.isBlank()) {
sessionData = ticketService.getSessionData(streamToken); sessionData = ticketService.getSessionData(streamToken);
if (sessionData == null) { if (sessionData == null) {
throw new RuntimeException("Invalid realtime gRPC session token"); throw new RuntimeException("实时会议 gRPC 会话令牌无效");
} }
if (sessionData.getDeviceId() != null && !sessionData.getDeviceId().isBlank() if (sessionData.getDeviceId() != null && !sessionData.getDeviceId().isBlank()
&& authContext.getDeviceId() != null && authContext.getDeviceId() != null
&& !sessionData.getDeviceId().equals(authContext.getDeviceId())) { && !sessionData.getDeviceId().equals(authContext.getDeviceId())) {
throw new RuntimeException("Realtime gRPC session token does not match deviceId"); throw new RuntimeException("实时会议 gRPC 会话令牌与 deviceId 不匹配");
} }
} else { } else {
throw new RuntimeException("Meeting ID is required"); throw new RuntimeException("会议 ID 不能为空");
} }
String connectionId = "grpc_" + java.util.UUID.randomUUID().toString().replace("-", ""); String connectionId = "grpc_" + java.util.UUID.randomUUID().toString().replace("-", "");
SessionRuntime runtime = new SessionRuntime(connectionId, streamToken, sessionData, responseObserver); SessionRuntime runtime = new SessionRuntime(connectionId, streamToken, sessionData, responseObserver);
SessionRuntime previous = sessions.putIfAbsent(connectionId, runtime); SessionRuntime previous = sessions.putIfAbsent(connectionId, runtime);
if (previous != null) { if (previous != null) {
throw new RuntimeException("Duplicate realtime gRPC connectionId"); throw new RuntimeException("实时会议 gRPC connectionId 重复");
} }
try { try {

View File

@ -59,7 +59,7 @@ public class RealtimeMeetingProxyWebSocketHandler extends AbstractWebSocketHandl
RealtimeSocketSessionData sessionData = realtimeMeetingSocketSessionService.getSessionData(sessionToken); RealtimeSocketSessionData sessionData = realtimeMeetingSocketSessionService.getSessionData(sessionToken);
if (sessionData == null) { if (sessionData == null) {
log.warn("Realtime websocket rejected: invalid session token, sessionId={}", session.getId()); 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; return;
} }
@ -96,13 +96,13 @@ public class RealtimeMeetingProxyWebSocketHandler extends AbstractWebSocketHandl
sessionData.getMeetingId(), session.getId(), ex); sessionData.getMeetingId(), session.getId(), ex);
sendFrontendError(frontendSession, "REALTIME_UPSTREAM_CONNECT_INTERRUPTED", "连接第三方识别服务时被中断"); sendFrontendError(frontendSession, "REALTIME_UPSTREAM_CONNECT_INTERRUPTED", "连接第三方识别服务时被中断");
realtimeMeetingAudioStorageService.closeSession(session.getId()); realtimeMeetingAudioStorageService.closeSession(session.getId());
frontendSession.close(CloseStatus.SERVER_ERROR.withReason("Interrupted while connecting upstream")); frontendSession.close(CloseStatus.SERVER_ERROR.withReason("连接上游服务时被中断"));
return; return;
} catch (ExecutionException | CompletionException ex) { } catch (ExecutionException | CompletionException ex) {
log.warn("Failed to connect upstream websocket, meetingId={}, target={}", sessionData.getMeetingId(), sessionData.getTargetWsUrl(), ex); log.warn("Failed to connect upstream websocket, meetingId={}, target={}", sessionData.getMeetingId(), sessionData.getTargetWsUrl(), ex);
sendFrontendError(frontendSession, "REALTIME_UPSTREAM_CONNECT_FAILED", "连接第三方识别服务失败,请检查模型 WebSocket 配置或服务状态"); sendFrontendError(frontendSession, "REALTIME_UPSTREAM_CONNECT_FAILED", "连接第三方识别服务失败,请检查模型 WebSocket 配置或服务状态");
realtimeMeetingAudioStorageService.closeSession(session.getId()); realtimeMeetingAudioStorageService.closeSession(session.getId());
frontendSession.close(CloseStatus.SERVER_ERROR.withReason("Failed to connect ASR websocket")); frontendSession.close(CloseStatus.SERVER_ERROR.withReason("连接 ASR WebSocket 失败"));
return; return;
} }
@ -348,7 +348,7 @@ public class RealtimeMeetingProxyWebSocketHandler extends AbstractWebSocketHandl
if (!realtimeMeetingSessionStateService.activate(meetingId, rawSession.getId())) { if (!realtimeMeetingSessionStateService.activate(meetingId, rawSession.getId())) {
sendFrontendError("REALTIME_ACTIVE_CONNECTION_EXISTS", "当前会议已有活跃实时连接,请先关闭旧连接后再继续"); sendFrontendError("REALTIME_ACTIVE_CONNECTION_EXISTS", "当前会议已有活跃实时连接,请先关闭旧连接后再继续");
webSocket.sendClose(CloseStatus.POLICY_VIOLATION.getCode(), "Active realtime connection already 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; return;
} }
try { try {

View File

@ -33,7 +33,7 @@ class RealtimeMeetingGrpcServiceTest {
CapturingObserver responseObserver = new CapturingObserver(); CapturingObserver responseObserver = new CapturingObserver();
when(authService.authenticateGrpc(any(ClientAuth.class), isNull())) when(authService.authenticateGrpc(any(ClientAuth.class), isNull()))
.thenThrow(new RuntimeException("Android deviceId is required")); .thenThrow(new RuntimeException("缺少 Android deviceId"));
StreamObserver<RealtimeClientPacket> requestObserver = service.streamMeetingAudio(responseObserver); StreamObserver<RealtimeClientPacket> requestObserver = service.streamMeetingAudio(responseObserver);
RealtimeClientPacket openPacket = RealtimeClientPacket.newBuilder() RealtimeClientPacket openPacket = RealtimeClientPacket.newBuilder()
@ -48,7 +48,7 @@ class RealtimeMeetingGrpcServiceTest {
assertEquals("rt-open-001", errorPacket.getRequestId()); assertEquals("rt-open-001", errorPacket.getRequestId());
assertTrue(errorPacket.hasError()); assertTrue(errorPacket.hasError());
assertEquals("REALTIME_GRPC_ERROR", errorPacket.getError().getCode()); assertEquals("REALTIME_GRPC_ERROR", errorPacket.getError().getCode());
assertEquals("Android deviceId is required", errorPacket.getError().getMessage()); assertEquals("缺少 Android deviceId", errorPacket.getError().getMessage());
assertTrue(responseObserver.completed); assertTrue(responseObserver.completed);
assertNull(responseObserver.error); assertNull(responseObserver.error);
verify(sessionService, never()).closeStream(anyString(), anyString(), anyBoolean()); verify(sessionService, never()).closeStream(anyString(), anyString(), anyBoolean());
@ -65,7 +65,7 @@ class RealtimeMeetingGrpcServiceTest {
when(authService.authenticateGrpc(any(ClientAuth.class), isNull())).thenReturn(authContext); when(authService.authenticateGrpc(any(ClientAuth.class), isNull())).thenReturn(authContext);
when(sessionService.openStream(1001L, "", authContext, responseObserver)) when(sessionService.openStream(1001L, "", authContext, responseObserver))
.thenThrow(new RuntimeException("ASR model WebSocket is not configured")); .thenThrow(new RuntimeException("ASR 模型未配置 WebSocket 地址"));
StreamObserver<RealtimeClientPacket> requestObserver = service.streamMeetingAudio(responseObserver); StreamObserver<RealtimeClientPacket> requestObserver = service.streamMeetingAudio(responseObserver);
RealtimeClientPacket openPacket = RealtimeClientPacket.newBuilder() RealtimeClientPacket openPacket = RealtimeClientPacket.newBuilder()
@ -81,7 +81,7 @@ class RealtimeMeetingGrpcServiceTest {
assertEquals("rt-open-002", errorPacket.getRequestId()); assertEquals("rt-open-002", errorPacket.getRequestId());
assertTrue(errorPacket.hasError()); assertTrue(errorPacket.hasError());
assertEquals("REALTIME_GRPC_ERROR", errorPacket.getError().getCode()); 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); assertTrue(responseObserver.completed);
assertNull(responseObserver.error); assertNull(responseObserver.error);
verify(sessionService, never()).closeStream(anyString(), anyString(), anyBoolean()); verify(sessionService, never()).closeStream(anyString(), anyString(), anyBoolean());

View File

@ -114,7 +114,7 @@ class AiTaskServiceImplTest {
service.dispatchTasks(66L, 1L, 2L); service.dispatchTasks(66L, 1L, 2L);
assertEquals(3, summaryTask.getStatus()); assertEquals(3, summaryTask.getStatus());
assertTrue(summaryTask.getErrorMsg().contains("No transcript content available for summary")); assertTrue(summaryTask.getErrorMsg().contains("没有可用于总结的转录内容"));
verify(aiModelService, never()).getModelById(anyLong(), anyString()); verify(aiModelService, never()).getModelById(anyLong(), anyString());
} }
@ -156,7 +156,7 @@ class AiTaskServiceImplTest {
service.dispatchSummaryTask(77L, 1L, 2L); service.dispatchSummaryTask(77L, 1L, 2L);
assertEquals(3, summaryTask.getStatus()); assertEquals(3, summaryTask.getStatus());
assertTrue(summaryTask.getErrorMsg().contains("No transcript content available for summary")); assertTrue(summaryTask.getErrorMsg().contains("没有可用于总结的转录内容"));
verify(aiModelService, never()).getModelById(anyLong(), anyString()); verify(aiModelService, never()).getModelById(anyLong(), anyString());
} }

View File

@ -45,32 +45,32 @@ function LazyPage({ children }: { children: JSX.Element }) {
} }
export const menuRoutes: MenuRoute[] = [ export const menuRoutes: MenuRoute[] = [
{ path: "/", label: "首页", element: <HomePage />, perm: "menu:dashboard" }, { path: "/", label: "首页", element: <LazyPage><HomePage /></LazyPage>, perm: "menu:dashboard" },
{ path: "/profile", label: "个人中心", element: <Profile /> }, { path: "/profile", label: "个人中心", element: <LazyPage><Profile /></LazyPage> },
{ path: "/speaker-reg", label: "声纹注册", element: <SpeakerReg />, perm: "menu:speaker" }, { path: "/speaker-reg", label: "声纹注册", element: <LazyPage><SpeakerReg /></LazyPage>, perm: "menu:speaker" },
{ path: "/tenants", label: "租户管理", element: <Tenants />, perm: "menu:tenants" }, { path: "/tenants", label: "租户管理", element: <LazyPage><Tenants /></LazyPage>, perm: "menu:tenants" },
{ path: "/orgs", label: "组织管理", element: <Orgs />, perm: "menu:orgs" }, { path: "/orgs", label: "组织管理", element: <LazyPage><Orgs /></LazyPage>, perm: "menu:orgs" },
{ path: "/users", label: "用户管理", element: <Users />, perm: "menu:users" }, { path: "/users", label: "用户管理", element: <LazyPage><Users /></LazyPage>, perm: "menu:users" },
{ path: "/roles", label: "角色管理", element: <Roles />, perm: "menu:roles" }, { path: "/roles", label: "角色管理", element: <LazyPage><Roles /></LazyPage>, perm: "menu:roles" },
{ path: "/permissions", label: "权限管理", element: <Permissions />, perm: "menu:permissions" }, { path: "/permissions", label: "权限管理", element: <LazyPage><Permissions /></LazyPage>, perm: "menu:permissions" },
{ path: "/params", label: "系统参数", element: <SysParams />, perm: "menu:params" }, { path: "/params", label: "系统参数", element: <LazyPage><SysParams /></LazyPage>, perm: "menu:params" },
{ path: "/platform-settings", label: "平台设置", element: <PlatformSettings />, perm: "menu:platform" }, { path: "/platform-settings", label: "平台设置", element: <LazyPage><PlatformSettings /></LazyPage>, perm: "menu:platform" },
{ path: "/dictionaries", label: "字典管理", element: <Dictionaries />, perm: "menu:dict" }, { path: "/dictionaries", label: "字典管理", element: <LazyPage><Dictionaries /></LazyPage>, perm: "menu:dict" },
{ path: "/logs", label: "日志管理", element: <Logs />, perm: "menu:logs" }, { path: "/logs", label: "日志管理", element: <LazyPage><Logs /></LazyPage>, perm: "menu:logs" },
{ path: "/devices", label: "设备管理", element: <Devices />, perm: "menu:devices" }, { path: "/devices", label: "设备管理", element: <LazyPage><Devices /></LazyPage>, perm: "menu:devices" },
{ path: "/user-roles", label: "用户角色绑定", element: <UserRoleBinding />, perm: "menu:user-roles" }, { path: "/user-roles", label: "用户角色绑定", element: <LazyPage><UserRoleBinding /></LazyPage>, perm: "menu:user-roles" },
{ path: "/role-permissions", label: "角色权限绑定", element: <RolePermissionBinding />, perm: "menu:role-permissions" }, { path: "/role-permissions", label: "角色权限绑定", element: <LazyPage><RolePermissionBinding /></LazyPage>, perm: "menu:role-permissions" },
{ path: "/hotwords", label: "热词管理", element: <HotWords />, perm: "menu:hotword" }, { path: "/hotwords", label: "热词管理", element: <LazyPage><HotWords /></LazyPage>, perm: "menu:hotword" },
{ path: "/prompts", label: "总结模板", element: <PromptTemplates />, perm: "menu:prompt" }, { path: "/prompts", label: "总结模板", element: <LazyPage><PromptTemplates /></LazyPage>, perm: "menu:prompt" },
{ path: "/aimodels", label: "模型配置", element: <AiModels />, perm: "menu:aimodel" }, { path: "/aimodels", label: "模型配置", element: <LazyPage><AiModels /></LazyPage>, perm: "menu:aimodel" },
{ path: "/clients", label: "客户端管理", element: <ClientManagement />, perm: "menu:clients" }, { path: "/clients", label: "客户端管理", element: <LazyPage><ClientManagement /></LazyPage>, perm: "menu:clients" },
{ path: "/external-apps", label: "外部应用管理", element: <ExternalAppManagement />, perm: "menu:external-apps" }, { path: "/external-apps", label: "外部应用管理", element: <LazyPage><ExternalAppManagement /></LazyPage>, perm: "menu:external-apps" },
{ path: "/screen-savers", label: "屏保管理", element: <ScreenSaverManagement />, perm: "menu:screen-savers" }, { path: "/screen-savers", label: "屏保管理", element: <LazyPage><ScreenSaverManagement /></LazyPage>, perm: "menu:screen-savers" },
{ path: "/meetings", label: "会议中心", element: <Meetings />, perm: "menu:meeting" }, { path: "/meetings", label: "会议中心", element: <LazyPage><Meetings /></LazyPage>, perm: "menu:meeting" },
]; ];
export const extraRoutes = [ export const extraRoutes = [
{ path: "/dashboard-monitor", element: <Dashboard />, perm: "menu:dashboard" }, { path: "/dashboard-monitor", element: <LazyPage><Dashboard /></LazyPage>, perm: "menu:dashboard" },
{ path: "/meetings/:id", element: <MeetingDetail />, perm: "menu:meeting" }, { path: "/meetings/:id", element: <LazyPage><MeetingDetail /></LazyPage>, perm: "menu:meeting" },
{ path: "/meeting-live-session/:id", element: <RealtimeAsrSession />, perm: "menu:meeting" }, { path: "/meeting-live-session/:id", element: <LazyPage><RealtimeAsrSession /></LazyPage>, perm: "menu:meeting" },
]; ];