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)) {
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")
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 = "model_code", required = false) String modelCode,
@RequestParam(value = "force_replace", defaultValue = "false") boolean forceReplace,

View File

@ -44,7 +44,7 @@ public class AndroidPromptController {
@GetMapping("/active/{scene}")
public ApiResponse<List<PromptTemplateVO>> activePrompts(HttpServletRequest request, @PathVariable String scene) {
if (!LEGACY_MEETING_SCENE.equals(scene)) {
return ApiResponse.error("scene only supports MEETING_TASK");
return ApiResponse.error("scene 仅支持 MEETING_TASK");
}
AndroidAuthContext authContext = androidAuthService.authenticateHttp(request);

View File

@ -35,7 +35,7 @@ public class LegacyPromptController {
@PreAuthorize("isAuthenticated()")
public LegacyApiResponse<LegacyPromptListResponse> activePrompts(@PathVariable String scene) {
if (!LEGACY_MEETING_SCENE.equals(scene)) {
return LegacyApiResponse.error("400", "scene only supports MEETING_TASK");
return LegacyApiResponse.error("400", "scene 仅支持 MEETING_TASK");
}
LoginUser loginUser = currentLoginUser();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -61,11 +61,11 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
@Transactional(rollbackFor = Exception.class)
public MeetingVO createMeeting(LegacyMeetingCreateRequest request, LoginUser loginUser) {
if (request == null || request.getTitle() == null || request.getTitle().isBlank()) {
throw new RuntimeException("Meeting title cannot be empty");
throw new RuntimeException("会议标题不能为空");
}
LocalDateTime meetingTime = parseMeetingTime(request.getMeetingTime());
if (meetingTime == null) {
throw new RuntimeException("Meeting time cannot be empty");
throw new RuntimeException("会议时间不能为空");
}
Meeting meeting = meetingDomainSupport.initMeeting(
@ -99,22 +99,22 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
MultipartFile audioFile,
LoginUser loginUser) throws IOException {
if (meetingId == null) {
throw new RuntimeException("meeting_id cannot be empty");
throw new RuntimeException("meeting_id 不能为空");
}
if (audioFile == null || audioFile.isEmpty()) {
throw new RuntimeException("audio_file cannot be empty");
throw new RuntimeException("audio_file 不能为空");
}
Meeting meeting = meetingAccessService.requireMeeting(meetingId);
meetingAccessService.assertCanEditMeeting(meeting, loginUser);
if (!forceReplace && meeting.getAudioUrl() != null && !meeting.getAudioUrl().isBlank()) {
throw new RuntimeException("Meeting already has audio, set force_replace=true to replace it");
throw new RuntimeException("当前会议已存在音频,如需替换请设置 force_replace=true");
}
long transcriptCount = transcriptMapper.selectCount(new LambdaQueryWrapper<MeetingTranscript>()
.eq(MeetingTranscript::getMeetingId, meetingId));
if (transcriptCount > 0) {
throw new RuntimeException("Current meeting already has transcripts; legacy Android upload does not support replacing generated transcripts");
throw new RuntimeException("当前会议已存在转录内容,旧版 Android 上传不支持替换已生成的转录");
}
if (promptId != null && !promptTemplateService.isTemplateEnabledForUser(
promptId,
@ -122,7 +122,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
loginUser.getUserId(),
loginUser.getIsPlatformAdmin(),
loginUser.getIsTenantAdmin())) {
throw new RuntimeException("Summary template unavailable");
throw new RuntimeException("总结模板不可用");
}
RealtimeMeetingRuntimeProfile profile = runtimeProfileResolver.resolve(
@ -183,7 +183,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
try {
return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} catch (DateTimeParseException ex) {
throw new RuntimeException("Meeting time format is invalid");
throw new RuntimeException("会议时间格式无效");
}
}
@ -226,7 +226,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
.orderByDesc(LlmModel::getTenantId)
.last("LIMIT 1"));
if (model == null) {
throw new RuntimeException("LLM model not found or disabled: " + modelCode);
throw new RuntimeException("LLM 模型不存在或已禁用: " + modelCode);
}
return model.getId();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -81,7 +81,7 @@ public class MeetingAudioUploadSupport {
private static String normalizeUploadPath(String uploadPath) {
if (!StringUtils.hasText(uploadPath)) {
throw new IllegalArgumentException("uploadPath must not be blank");
throw new IllegalArgumentException("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) {
if (allowAnonymous(authContext)) {
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()
&& !meeting.getMeetingSource().equalsIgnoreCase(currentPlatform)) {
throw new RuntimeException("Cross-platform realtime takeover is not allowed");
throw new RuntimeException("不允许跨平台接管实时会议");
}
return;
}

View File

@ -251,13 +251,13 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void completeRealtimeMeeting(Long meetingId, String audioUrl, boolean overwriteAudio) {
Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) {
throw new RuntimeException("Meeting not found");
throw new RuntimeException("会议不存在");
}
RealtimeMeetingSessionStatusVO currentStatus = realtimeMeetingSessionStateService.getStatus(meetingId);
if (overwriteAudio) {
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));
markAudioSaveSuccess(meeting);
@ -325,7 +325,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
private void prepareOfflineReprocessTasks(Long meetingId, RealtimeMeetingSessionStatusVO currentStatus) {
RealtimeMeetingResumeConfig resumeConfig = currentStatus == null ? null : currentStatus.getResumeConfig();
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>()
@ -358,7 +358,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
.orderByDesc(AiTask::getId)
.last("LIMIT 1"));
if (summaryTask == null) {
throw new RuntimeException("Summary task missing, cannot continue offline process");
throw new RuntimeException("缺少总结任务,无法继续离线流程");
}
resetAiTask(summaryTask, summaryTask.getTaskConfig());
@ -425,7 +425,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void updateMeetingTranscript(UpdateMeetingTranscriptCommand command) {
String content = command.getContent() == null ? "" : command.getContent().trim();
if (content.isEmpty()) {
throw new RuntimeException("Transcript content cannot be empty");
throw new RuntimeException("转录内容不能为空");
}
int updated = transcriptMapper.update(null, new LambdaUpdateWrapper<MeetingTranscript>()
@ -433,7 +433,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
.eq(MeetingTranscript::getId, command.getTranscriptId())
.set(MeetingTranscript::getContent, content));
if (updated <= 0) {
throw new RuntimeException("Transcript not found");
throw new RuntimeException("转录记录不存在");
}
}
@ -469,7 +469,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void updateSummaryContent(Long meetingId, String summaryContent) {
Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) {
throw new RuntimeException("Meeting not found");
throw new RuntimeException("会议不存在");
}
meetingSummaryFileService.updateSummaryContent(meeting, summaryContent);
}
@ -479,7 +479,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void reSummary(Long meetingId, Long summaryModelId, Long promptId, String userPrompt) {
Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) {
throw new RuntimeException("Meeting not found");
throw new RuntimeException("会议不存在");
}
meetingDomainSupport.createSummaryTask(meetingId, summaryModelId, promptId, userPrompt);
@ -493,7 +493,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
public void retryTranscription(Long meetingId) {
Meeting meeting = meetingService.getById(meetingId);
if (meeting == null) {
throw new RuntimeException("Meeting not found");
throw new RuntimeException("会议不存在");
}
long transcriptCount = transcriptMapper.selectCount(new LambdaQueryWrapper<MeetingTranscript>()

View File

@ -107,7 +107,7 @@ public class MeetingDomainSupport {
return plan.relocatedUrl();
} catch (Exception ex) {
log.error("Failed to move audio file for meeting {}", meetingId, ex);
throw new RuntimeException("File relocation failed: " + ex.getMessage());
throw new RuntimeException("文件迁移失败:" + ex.getMessage());
}
}
@ -125,13 +125,13 @@ public class MeetingDomainSupport {
try {
Files.deleteIfExists(path);
} catch (Exception ex) {
throw new RuntimeException("Delete meeting artifact failed: " + path, ex);
throw new RuntimeException("删除会议产物失败:" + path, ex);
}
});
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException("Delete meeting artifacts failed", ex);
throw new RuntimeException("删除会议产物失败", ex);
}
}

View File

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

View File

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

View File

@ -72,16 +72,16 @@ public class RealtimeMeetingSessionStateServiceImpl implements RealtimeMeetingSe
String currentStatus = status.getStatus();
if ("COMPLETING".equals(currentStatus)) {
throw new RuntimeException("Realtime meeting is completing");
throw new RuntimeException("实时会议正在结束处理中");
}
if ("COMPLETED".equals(currentStatus)) {
throw new RuntimeException("Realtime meeting has completed");
throw new RuntimeException("实时会议已结束");
}
if ("ACTIVE".equals(currentStatus) || Boolean.TRUE.equals(status.getActiveConnection())) {
throw new RuntimeException("Realtime meeting already has an active connection");
throw new RuntimeException("实时会议已存在活动连接");
}
if ("PAUSED_RESUMABLE".equals(currentStatus) && !Boolean.TRUE.equals(status.getCanResume())) {
throw new RuntimeException("Realtime meeting resume window has expired");
throw new RuntimeException("实时会议恢复窗口已过期");
}
}
@ -391,7 +391,7 @@ public class RealtimeMeetingSessionStateServiceImpl implements RealtimeMeetingSe
objectMapper.writeValueAsString(state)
);
} catch (Exception ex) {
throw new RuntimeException("Failed to write realtime meeting session state", ex);
throw new RuntimeException("写入实时会议会话状态失败", ex);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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