diff --git a/backend/go-service/app/meeting/service/meeting_recording_service.go b/backend/go-service/app/meeting/service/meeting_recording_service.go index 5a1b12c..32c36d6 100644 --- a/backend/go-service/app/meeting/service/meeting_recording_service.go +++ b/backend/go-service/app/meeting/service/meeting_recording_service.go @@ -370,7 +370,29 @@ func (s *MeetingRecordingService) ForceStopByAdmin(ctx context.Context, recordin logs.Info(ctx, funcName, "管理员强制结束录制", zap.Int64("recording_id", rec.ID), zap.String("room_code", rec.RoomCode)) - return s.StopRecording(ctx, room.HostID, rec.RoomCode, rec.ID) + stopped, stopErr := s.StopRecording(ctx, room.HostID, rec.RoomCode, rec.ID) + if stopErr == nil { + return stopped, nil + } + + // admin 强制结束的语义是"让录制脱离 recording 状态",而 StopRecording 内部 + // 在 media-server stop / MinIO 上传失败时已经调过 markFailedAndBroadcast: + // DB 此刻多半已经是 failed(或 ready,极端竞态)。这种情况下应吞掉错误返回最新记录, + // 否则 admin 会看到 500 但 UI 刷新发现录制已成终态,体验割裂。 + latest, lookupErr := s.recordingDAO.GetByID(ctx, recordingID) + if lookupErr != nil { + logs.Warn(ctx, funcName, "StopRecording 失败后回查录制记录失败,原始错误透传", + zap.Int64("recording_id", recordingID), zap.Error(lookupErr)) + return nil, stopErr + } + if latest != nil && latest.Status != model.MeetingRecordingStatusRecording { + logs.Warn(ctx, funcName, "StopRecording 报错但录制已进入终态,按成功返回", + zap.Int64("recording_id", recordingID), + zap.String("status", string(latest.Status)), + zap.Error(stopErr)) + return latest, nil + } + return nil, stopErr } // StopActiveForEndRoom 在 EndRoom / EmptyTTL 路径上由系统强制停止当前活跃录制