using System.Net.WebSockets; using System.Text.Json; using Health.Application.Speech; namespace Health.WebApi.Endpoints; public static class SpeechEndpoints { public static void MapSpeechEndpoints(this WebApplication app) { app.Map("/api/speech/realtime", async ( HttpContext http, IRealtimeSpeechRecognitionProxy proxy, ILoggerFactory loggerFactory) => { if (!http.WebSockets.IsWebSocketRequest) { http.Response.StatusCode = StatusCodes.Status400BadRequest; await http.Response.WriteAsJsonAsync(new { code = 40001, data = (object?)null, message = "该接口仅支持 WebSocket 连接", }); return; } if (!proxy.IsConfigured) { http.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; await http.Response.WriteAsJsonAsync(new { code = 50301, data = (object?)null, message = "实时语音识别服务尚未配置", }); return; } using var socket = await http.WebSockets.AcceptWebSocketAsync(); try { await proxy.ProxyAsync(socket, http.RequestAborted); } catch (OperationCanceledException) when (http.RequestAborted.IsCancellationRequested) { // 客户端离开页面或断开网络。 } catch (Exception ex) { loggerFactory.CreateLogger("SpeechEndpoints") .LogError(ex, "Realtime speech proxy failed"); if (socket.State == WebSocketState.Open) { var payload = JsonSerializer.SerializeToUtf8Bytes(new { type = "error", message = "语音服务暂时不可用,请稍后重试", }); await socket.SendAsync( payload, WebSocketMessageType.Text, true, CancellationToken.None); } } }).RequireAuthorization(policy => policy.RequireRole("User")); } }