parent
a295a3b15b
commit
2f80c6c55e
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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("模型名称不能为空");
|
||||
|
|
|
|||
|
|
@ -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("总结模板不可用");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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 请求头");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 + "/";
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 + "/";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue