diff --git a/admin/src/api/meeting.js b/admin/src/api/meeting.js index 409c10a..e2e065b 100644 --- a/admin/src/api/meeting.js +++ b/admin/src/api/meeting.js @@ -54,3 +54,16 @@ export function getMeetingStats() { method: 'get' }) } + +/** + * 强制结束指定录制(admin 兜底操作) + * 适用:host 失联未点停止 / ffmpeg 异常但 webhook 失效 / 长时间停留 recording 状态 + * 终态记录调用此接口幂等返回成功 + * @param {number} recordingID + */ +export function forceStopRecording(recordingID) { + return request({ + url: `/api/v1/admin/meetings/recordings/${recordingID}/force-stop`, + method: 'post' + }) +} diff --git a/admin/src/views/meeting/list.vue b/admin/src/views/meeting/list.vue index ca07198..7c704bf 100644 --- a/admin/src/views/meeting/list.vue +++ b/admin/src/views/meeting/list.vue @@ -208,6 +208,7 @@ width="780px" destroy-on-close class="detail-dialog" + @close="onDetailDialogClose" >
+ + + +
+
+ + {{ currentTranscript.status_label }} + + + 提供商:{{ currentTranscript.provider_code }} + + + 模型:{{ currentTranscript.model_code }} + + + 语言:{{ currentTranscript.language }} + + + 音频时长:{{ formatDuration(currentTranscript.duration_sec) }} + +
+ +
+ + {{ currentTranscript.error_msg || '转写失败' }} +
+ +
+
+ {{ formatSegTime(seg.start) }} + {{ seg.text }} +
+
+ +
+ {{ currentTranscript.text }} +
+ + +
+
diff --git a/backend/go-service/app/admin/router.go b/backend/go-service/app/admin/router.go index 30cdde3..1ef284e 100644 --- a/backend/go-service/app/admin/router.go +++ b/backend/go-service/app/admin/router.go @@ -59,5 +59,7 @@ func RegisterRoutes( adminGroup.GET("/meetings/stats", meetingManageCtrl.GetMeetingStats) adminGroup.GET("/meetings", meetingManageCtrl.GetMeetingList) adminGroup.GET("/meetings/:id", meetingManageCtrl.GetMeetingDetail) + // 录制兜底操作(admin 强制结束):用于 host 失联 / ffmpeg 崩 webhook 失效等场景 + adminGroup.POST("/meetings/recordings/:id/force-stop", meetingManageCtrl.ForceStopRecording) } } diff --git a/backend/go-service/app/provider/wire_gen.go b/backend/go-service/app/provider/wire_gen.go index 92add2f..899a7d7 100644 --- a/backend/go-service/app/provider/wire_gen.go +++ b/backend/go-service/app/provider/wire_gen.go @@ -84,8 +84,7 @@ func InitializeApp(cfg *config.Config) (*App, error) { messageManageService := service2.NewMessageManageService(messageManageDAO, userDAO, conversationDAO, pubSub) messageManageController := controller2.NewMessageManageController(messageManageService) meetingManageDAO := dao2.NewMeetingManageDAO(gormDB) - meetingManageService := service2.NewMeetingManageService(meetingManageDAO) - meetingManageController := controller2.NewMeetingManageController(meetingManageService) + // admin meetingManageService 依赖 meetingRecordingService(强制结束录制兜底),延后到 meetingRecordingService 构造后再实例化 serverConfig := provideServerConfig(cfg) handler := ws.ProvideWSHandler(hub, pubSub, jwtConfig, serverConfig, onlineService, authService) friendGroupDAO := dao3.NewFriendGroupDAO(gormDB) @@ -116,6 +115,9 @@ func InitializeApp(cfg *config.Config) (*App, error) { meetingService := service7.NewMeetingService(meetingRoomDAO, meetingParticipantDAO, meetingChatDAO, gormDB, client, meetingBroadcaster, notifyService, friendshipDAO, onlineService, httpMediaOrchestrator, meetingLifecycleService) meetingSignalService := service7.NewMeetingSignalService(meetingRoomDAO, meetingParticipantDAO, client, meetingBroadcaster, httpMediaOrchestrator, meetingLifecycleService) meetingRecordingService := service7.NewMeetingRecordingService(meetingRoomDAO, meetingParticipantDAO, meetingRecordingDAO, client, meetingBroadcaster, httpMediaOrchestrator, minioClient, minioConfig) + // admin 会议管理服务在 recording service 构造后再创建,从而支持"强制结束录制"兜底 + meetingManageService := service2.NewMeetingManageService(meetingManageDAO, meetingRecordingService) + meetingManageController := controller2.NewMeetingManageController(meetingManageService) meetingController := controller7.NewMeetingController(meetingService) meetingRecordingController := controller7.NewMeetingRecordingController(meetingRecordingService) meetingWSHandler := controller7.NewMeetingWSHandler(meetingSignalService, hub)