/
kochkatech
/
Queue
Обзор
Документация
Войти
/
kochkatech
/
Queue
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
dev
src/Queue.OperatorApp/Services/QueueApiClient.cs
113 строк
5 KB
kochkareal
feat: фирменный стиль, крупнее окна и переработка UX АРМ оператора
27 июл 2026, 19:35
27 июл 2026, 19:35
4a9973e
Код
Авторство
О чём код?
using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; using Queue.OperatorApp.Models; namespace Queue.OperatorApp.Services; // Тонкий клиент Queue.Server: HTTP-запросы + подписка на события очереди по SignalR. // JsonSerializerDefaults.Web — те же соглашения именования (camelCase, регистронезависимо), // что ASP.NET Core применяет по умолчанию на стороне сервера; без этого поля не свяжутся. public class QueueApiClient { private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web); private readonly HttpClient _http = new(); private HubConnection? _hubConnection; public string BaseAddress { get; private set; } = "http://localhost:5080"; public event Action? QueueChanged; public void Configure(string baseAddress) { var trimmed = baseAddress.TrimEnd('/'); // HttpClient запрещает менять BaseAddress после первого отправленного запроса — // а Configure вызывается на каждую попытку входа (в т.ч. повторную после неверного // пароля, когда первый запрос уже ушёл). Если адрес не поменялся, просто ничего не // делаем; если поменялся после первого запроса — HttpClient всё равно не даст его // сменить, это ограничение самого экземпляра, а не повод падать необработанным. if (trimmed == BaseAddress && _http.BaseAddress is not null) { return; } BaseAddress = trimmed; _http.BaseAddress = new Uri(BaseAddress); } public async Task ConnectHubAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl($"{BaseAddress}/hubs/queue") .WithAutomaticReconnect() .Build(); _hubConnection.On("QueueChanged", () => QueueChanged?.Invoke()); await _hubConnection.StartAsync(); } public async Task<LoginResult?> LoginAsync(string login, string password) { var response = await _http.PostAsJsonAsync("/api/auth/login", new LoginRequest(login, password), JsonOptions); if (!response.IsSuccessStatusCode) { return null; } return await response.Content.ReadFromJsonAsync<LoginResult>(JsonOptions); } public async Task<List<ServiceDto>> GetServicesAsync() => await _http.GetFromJsonAsync<List<ServiceDto>>("/api/services", JsonOptions) ?? new(); public async Task<List<WindowDto>> GetWindowsAsync() => await _http.GetFromJsonAsync<List<WindowDto>>("/api/windows", JsonOptions) ?? new(); public async Task<List<TicketDto>> GetQueueAsync(int windowId) => await _http.GetFromJsonAsync<List<TicketDto>>($"/api/tickets/queue?windowId={windowId}", JsonOptions) ?? new(); public async Task<List<TicketDto>> GetServedTodayAsync(int employeeId) => await _http.GetFromJsonAsync<List<TicketDto>>($"/api/tickets/served-today?employeeId={employeeId}", JsonOptions) ?? new(); public async Task<TicketDto?> CallNextAsync(int windowId, int employeeId) { var response = await _http.PostAsJsonAsync("/api/tickets/call", new CallRequest(windowId, employeeId), JsonOptions); response.EnsureSuccessStatusCode(); return await ReadTicketOrNullAsync(response); } public Task<TicketDto?> StartAsync(int ticketId) => PostActionAsync($"/api/tickets/{ticketId}/start"); public Task<TicketDto?> FinishAsync(int ticketId) => PostActionAsync($"/api/tickets/{ticketId}/finish"); public Task<TicketDto?> NoShowAsync(int ticketId) => PostActionAsync($"/api/tickets/{ticketId}/noshow"); public Task<TicketDto?> CancelAsync(int ticketId) => PostActionAsync($"/api/tickets/{ticketId}/cancel"); public async Task<TicketDto?> TransferAsync(int ticketId, int newServiceId) { var response = await _http.PostAsJsonAsync($"/api/tickets/{ticketId}/transfer", new TransferRequest(newServiceId), JsonOptions); response.EnsureSuccessStatusCode(); return await ReadTicketOrNullAsync(response); } private async Task<TicketDto?> PostActionAsync(string url) { var response = await _http.PostAsync(url, content: null); response.EnsureSuccessStatusCode(); return await ReadTicketOrNullAsync(response); } // Results.Ok((TicketDto?)null) (см. /api/tickets/call, когда очередь пуста) отдаёт пустое // тело ответа, а не JSON-литерал "null" — ReadFromJsonAsync на пустом потоке падает с // JsonException. Читаем тело как строку и парсим только если оно не пустое. private static async Task<TicketDto?> ReadTicketOrNullAsync(HttpResponseMessage response) { var body = await response.Content.ReadAsStringAsync(); return string.IsNullOrWhiteSpace(body) ? null : JsonSerializer.Deserialize<TicketDto>(body, JsonOptions); } }