Document all client HTTP calls in docs/API.md, link from README, and delete dead GetPriceItem code. Co-authored-by: Cursor <cursoragent@cursor.com>
174 lines
6.7 KiB
C#
174 lines
6.7 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Net.Http.Json;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
|
||
namespace Электронная_Фармация.Classes
|
||
{
|
||
public class ApiClient
|
||
{
|
||
private readonly HttpClient _httpClient;
|
||
private readonly string _baseUrl;
|
||
private string _token;
|
||
|
||
public ApiClient(string baseUrl = null)
|
||
{
|
||
_baseUrl = (baseUrl ?? AppConfig.ApiBaseUrl).TrimEnd('/');
|
||
_httpClient = new HttpClient
|
||
{
|
||
BaseAddress = new Uri(_baseUrl),
|
||
Timeout = TimeSpan.FromSeconds(30)
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// Выполняет авторизацию и сохраняет токен
|
||
/// </summary>
|
||
/// <param name="username">Имя пользователя</param>
|
||
/// <param name="password">Пароль</param>
|
||
/// <returns>JWT токен</returns>
|
||
/// <exception cref="HttpRequestException">При ошибке авторизации</exception>
|
||
public async Task<string> LoginAsync(string username, string password)
|
||
{
|
||
var request = new LoginRequest
|
||
{
|
||
Username = username,
|
||
Password = password
|
||
};
|
||
|
||
try
|
||
{
|
||
var response = await _httpClient.PostAsJsonAsync("/auth/login", request);
|
||
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
var loginResponse = await response.Content.ReadFromJsonAsync<LoginResponse>();
|
||
_token = loginResponse.Token;
|
||
return _token;
|
||
}
|
||
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||
{
|
||
var error = await response.Content.ReadFromJsonAsync<ErrorResponse>();
|
||
throw new UnauthorizedAccessException($"Ошибка авторизации: {error?.Error ?? "Неверные учетные данные"}");
|
||
}
|
||
else
|
||
{
|
||
var error = await response.Content.ReadFromJsonAsync<ErrorResponse>();
|
||
throw new HttpRequestException($"Ошибка HTTP {response.StatusCode}: {error?.Error ?? "Неизвестная ошибка"}");
|
||
}
|
||
}
|
||
catch (TaskCanceledException)
|
||
{
|
||
throw new HttpRequestException("Превышено время ожидания ответа сервера");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Устанавливает токен для последующих запросов
|
||
/// </summary>
|
||
public void SetToken(string token)
|
||
{
|
||
_token = token;
|
||
}
|
||
|
||
public string Token => _token;
|
||
|
||
/// <summary>
|
||
/// Проверяет, авторизован ли клиент
|
||
/// </summary>
|
||
public bool IsAuthenticated => !string.IsNullOrEmpty(_token);
|
||
|
||
public async Task<PriceSummaryResponse> GetPriceSummaryAsync(string supplierId = null, string regionId = null)
|
||
{
|
||
if (!IsAuthenticated)
|
||
{
|
||
throw new InvalidOperationException("Необходимо выполнить авторизацию перед запросом данных");
|
||
}
|
||
|
||
var queryParams = new List<string>();
|
||
if (!string.IsNullOrEmpty(supplierId))
|
||
{
|
||
queryParams.Add($"supplier_id={Uri.EscapeDataString(supplierId)}");
|
||
}
|
||
if (!string.IsNullOrEmpty(regionId))
|
||
{
|
||
queryParams.Add($"region_id={Uri.EscapeDataString(regionId)}");
|
||
}
|
||
|
||
var url = "/api/supplier-prices/summary";
|
||
if (queryParams.Count > 0)
|
||
{
|
||
url += "?" + string.Join("&", queryParams);
|
||
}
|
||
|
||
_httpClient.DefaultRequestHeaders.Authorization =
|
||
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);
|
||
|
||
try
|
||
{
|
||
var response = await _httpClient.GetAsync(url);
|
||
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
var summary = await response.Content.ReadFromJsonAsync<PriceSummaryResponse>();
|
||
return summary;
|
||
}
|
||
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||
{
|
||
_token = null; // Токен истек или невалиден
|
||
var error = await response.Content.ReadFromJsonAsync<ErrorResponse>();
|
||
throw new UnauthorizedAccessException($"Сессия истекла: {error?.Error ?? "Необходимо войти заново"}");
|
||
}
|
||
else
|
||
{
|
||
var error = await response.Content.ReadFromJsonAsync<ErrorResponse>();
|
||
throw new HttpRequestException($"Ошибка HTTP {response.StatusCode}: {error?.Error ?? "Неизвестная ошибка"}");
|
||
}
|
||
|
||
|
||
|
||
}
|
||
catch (TaskCanceledException)
|
||
{
|
||
throw new HttpRequestException("Превышено время ожидания ответа сервера (таймаут 30 секунд)");
|
||
}
|
||
}
|
||
|
||
public async Task<List<InvoiceApiItem>> GetInvoicesAsync()
|
||
{
|
||
if (!IsAuthenticated)
|
||
{
|
||
throw new InvalidOperationException("Необходимо выполнить авторизацию перед запросом накладных");
|
||
}
|
||
|
||
_httpClient.DefaultRequestHeaders.Authorization =
|
||
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);
|
||
|
||
var response = await _httpClient.GetAsync("/api/buyer/invoices");
|
||
var body = await response.Content.ReadAsStringAsync();
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
throw new HttpRequestException(
|
||
$"Ошибка загрузки накладных: {(int)response.StatusCode} {response.ReasonPhrase}\n{body}");
|
||
}
|
||
|
||
return JsonSerializer.Deserialize<List<InvoiceApiItem>>(body) ?? new List<InvoiceApiItem>();
|
||
}
|
||
}
|
||
|
||
public sealed class InvoiceApiItem
|
||
{
|
||
public string InvoiceNumber { get; set; }
|
||
public string InvoiceDate { get; set; }
|
||
public string SupplierName { get; set; }
|
||
public string ConsigneeName { get; set; }
|
||
public string InvoiceSum { get; set; }
|
||
public string RefuseSum { get; set; }
|
||
}
|
||
}
|