using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Unicode; using JetBrains.Annotations; using Livia.Utility; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Livia.Models.Data; public readonly record struct LoginResponseJson { [JsonPropertyName("access_token")] public string AccessToken { get; init; } [JsonPropertyName("token_type")] public string TokenType { get; init; } } public readonly record struct LoginErrorResponseJson { [JsonPropertyName("errno")] public int ErrorNumber { get; init; } [JsonPropertyName("count")] public int? Count { get; init; } [JsonPropertyName("seconds")] public int? Seconds { get; init; } } public readonly record struct ProcessDataResponseJson { [JsonPropertyName("code")] public int Code { get; init; } [JsonPropertyName("msg")] public string Msg { get; init; } [JsonPropertyName("key")] public string Key { get; init; } [JsonPropertyName("callback")] public string CallBack { get; init; } [JsonPropertyName("errno")] public int ErrorNumber { get; init; } } public readonly record struct ServerResponseJson { [JsonPropertyName("code")] public int Code { get; init; } [JsonPropertyName("msg")] public string Msg { get; init; } [JsonPropertyName("errno")] public int ErrorNumber { get; init; } } public readonly record struct SimpleKeyJson { [JsonPropertyName("key")] [UsedImplicitly] public string Key { get; init; } } public readonly record struct ReportKeyIdJson { [JsonPropertyName("key")] [UsedImplicitly] public string Key { get; init; } [JsonPropertyName("report_id")] [UsedImplicitly] public int ReportId { get; init; } } public readonly record struct ReportIdJson { [JsonPropertyName("report_id")] [UsedImplicitly] public int ReportId { get; init; } [JsonPropertyName("errno")] public int? ErrorNumber { get; init; } } public readonly record struct EditPasswordJson { [JsonPropertyName("old_password")] [UsedImplicitly] public string OldPassword { get; init; } [JsonPropertyName("new_password")] [UsedImplicitly] public string NewPassword { get; init; } } public readonly record struct SelectSeriesJson { [JsonPropertyName("key")] [UsedImplicitly] public string Key { get; init; } [JsonPropertyName("choose_series")] [UsedImplicitly] public List SelectedSeries { get; init; } } public readonly record struct DongleInfoResponseJson { [JsonPropertyName("code")] public int Code { get; init; } [JsonPropertyName("quota")] public uint Quota { get; init; } [JsonPropertyName("msg")] public string? Msg { get; init; } } public readonly record struct ServerJobListJsonResponse { [JsonPropertyName("code")] public int Code { get; init; } [JsonPropertyName("data")] [UsedImplicitly] public List DataList { get; init; } [JsonPropertyName("count")] [UsedImplicitly] public int Count { get; init; } } public readonly record struct ServerNoticeJsonResponse { [JsonPropertyName("code")] public int Code { get; init; } [JsonPropertyName("notice")] [UsedImplicitly] public ServerNotice? Notice { get; init; } } public readonly record struct ServerNotice { [JsonPropertyName("notice_type")] [UsedImplicitly] public int NoticeType { get; init; } [JsonPropertyName("server_status")] [UsedImplicitly] public int ServerStatus { get; init; } [JsonPropertyName("create_at")] [UsedImplicitly] public DateTime CreateAt { get; init; } [JsonPropertyName("id")] [UsedImplicitly] public int Id { get; init; } [JsonPropertyName("is_show")] [UsedImplicitly] public bool ShowMessage { get; init; } [JsonPropertyName("content")] [UsedImplicitly] public string Content { get; init; } } public readonly record struct DicomNodeInfoJsonResponse { [JsonPropertyName("code")] public int Code { get; init; } [JsonPropertyName("dicom"), UsedImplicitly] public DicomNodeInfo Dicom { get; init; } } public readonly record struct DicomNodeInfo { [JsonPropertyName("host"), UsedImplicitly] public string Host { get; init; } [JsonPropertyName("port"), UsedImplicitly] public int Port { get; init; } [JsonPropertyName("aet"), UsedImplicitly] public string Aet { get; init; } } public class ReportItem { [JsonPropertyName("id"), UsedImplicitly] public int Id { get; init; } [JsonPropertyName("title"), UsedImplicitly] public string? Title { get; init; } } public interface ILiviaHttpClient { Task Login(string username, string password); Task EditPassword(string oldPassword, string newPassword); Task Cancel(string key); Task DeleteReport(string key, int id); Task UpdateDongleInfo(); Task CheckServerJobList(int limit, int offset); Task> GetReportList(string key); Task SearchData(SearchQueryJson json); Task DownloadReport(string key, int id, string savePath, CancellationToken token); Task ProcessData(string zipPath, CancellationToken token); Task CheckResult(string key, string savePath, CancellationToken token); Task DownloadArchive(string key, string savePath, CancellationToken token); Task DownloadSorted(string key, string savePath, CancellationToken token); Task DeleteData(string? id, CancellationToken token); Task DeleteHistory(string key, CancellationToken token); Task GetNotice(); Task GetSeriesGroupList(string key); Task SelectSeries(string key, List series); Task Start(string key); Task SelectReportModule(SelectReportModuleJson json); Task PushToPacs(string key, int id); Task GetDicomNodeInfo(); } public class LiviaHttpClientFactory(IServiceProvider serviceProvider) { public ILiviaHttpClient Create() { return serviceProvider.GetRequiredService(); } } internal class LiviaHttpClient(HttpClient httpClient, ILogger logger) : ILiviaHttpClient { private readonly HttpClient _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); private static string _token = string.Empty; private static readonly JsonSerializerOptions EncodeAll = new() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }; private static readonly JsonSerializerOptions IgnoreNull = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; private void UpdateToken() { if (!string.IsNullOrEmpty(_token)) { _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token); } } public static void ClearToken() { _token = string.Empty; } public async Task Login(string username, string password) { KeyValuePair[] data = [ new KeyValuePair("username", username), new KeyValuePair("password", password) ]; logger.LogInformation("Sending post request"); HttpResponseMessage result = await _httpClient.PostAsync(ServiceConfigurations.PostLoginPath, new FormUrlEncodedContent(data)).ConfigureAwait(false); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); if (result.StatusCode == HttpStatusCode.OK) { LoginResponseJson responseJson = await JsonSerializer.DeserializeAsync(contentStream); _token = responseJson.AccessToken; } else { LoginErrorResponseJson responseJson = await JsonSerializer.DeserializeAsync(contentStream); throw new LoginErrorException(responseJson); } } public async Task EditPassword(string oldPassword, string newPassword) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Put, ServiceConfigurations.PutEditPasswordPath); EditPasswordJson processJson = new() { OldPassword = oldPassword, NewPassword = newPassword }; string jsonString = JsonSerializer.Serialize(processJson); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending edit password request"); //_logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task Cancel(string key) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostCancelPath); SimpleKeyJson processJson = new() { Key = key }; string jsonString = JsonSerializer.Serialize(processJson); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending cancel request"); logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task DeleteReport(string key, int id) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Delete, ServiceConfigurations.DeleteReportPath); ReportKeyIdJson processKeyIdJson = new() { Key = key, ReportId = id }; string jsonString = JsonSerializer.Serialize(processKeyIdJson); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending DeleteReport request"); logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task UpdateDongleInfo() { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Get, ServiceConfigurations.GetDongleInfoPath); logger.LogInformation("Sending UpdateDongleInfo request"); using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task CheckServerJobList(int limit, int offset) { UpdateToken(); string queryUri = $"{ServiceConfigurations.GetServerJobListPath}?limit={limit}&offset={offset}"; HttpRequestMessage request = new(HttpMethod.Get, queryUri); logger.LogInformation("Sending CheckServerJobList request"); using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); //_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult()); //return new ServerJobListJsonResponse(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task> GetReportList(string key) { UpdateToken(); string getUrl = string.Format(ServiceConfigurations.GetReportLIstPath, key); HttpRequestMessage request = new(HttpMethod.Get, getUrl); logger.LogInformation("Sending GetReportList request"); HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); //_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult()); //return new List(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync>(contentStream) ?? []; } public async Task SearchData(SearchQueryJson json) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostSearchPath); string jsonString = JsonSerializer.Serialize(json, IgnoreNull); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending SearchData request"); logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); //_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult()); //return new ServerJobListJsonResponse(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task DownloadReport(string key, int id, string savePath, CancellationToken token) { UpdateToken(); string extension = Path.GetExtension(savePath)[1..]; string getUrl = string.Format(ServiceConfigurations.GetReportPath, key, id, extension); HttpRequestMessage request = new(HttpMethod.Get, getUrl); logger.LogInformation("Sending DownloadReport request"); HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(token); if (result.Content.Headers.ContentType?.MediaType == "application/json") { ServerResponseJson serverResponseJson = await JsonSerializer.DeserializeAsync(contentStream, cancellationToken: token); throw new ServerErrorException(serverResponseJson); } await using FileStream outputFileStream = new(savePath, FileMode.Create); await contentStream.CopyToAsync(outputFileStream, token); } public async Task ProcessData(string zipPath, CancellationToken token) { UpdateToken(); logger.LogInformation("Sending post request, fileName is {fileName}", zipPath); string queryUri = $"{ServiceConfigurations.PostProcessDataPath}?language={LiviaUtility.GetLanguageOnlyString()}"; await using FileStream fileStream = new(zipPath, FileMode.Open); MultipartFormDataContent form = new() { { new StreamContent(fileStream), "file", "data.zip" } }; HttpRequestMessage request = new(HttpMethod.Post, queryUri); logger.LogInformation("Sending ProcessData request"); request.Content = form; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(token); return await JsonSerializer.DeserializeAsync(contentStream, cancellationToken: token); } public async Task CheckResult(string key, string savePath, CancellationToken token) { UpdateToken(); string getUrl = string.Format(ServiceConfigurations.GetCheckResultPath, key); HttpRequestMessage request = new(HttpMethod.Get, getUrl); logger.LogInformation("Sending CheckResult request"); using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(token); if (result.Content.Headers.ContentType?.MediaType == "application/json") { ServerResponseJson serverResponseJson = await JsonSerializer.DeserializeAsync(contentStream, cancellationToken: token); throw new ResultNotReadyException(serverResponseJson); } await using FileStream outputFileStream = new(savePath, FileMode.Create); await contentStream.CopyToAsync(outputFileStream, token); } public async Task DownloadArchive(string key, string savePath, CancellationToken token) { UpdateToken(); string getUrl = string.Format(ServiceConfigurations.GetDownloadArchivePath, key); HttpRequestMessage request = new(HttpMethod.Get, getUrl); logger.LogInformation("Sending DownloadArchive request"); HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(token); await using FileStream outputFileStream = new(savePath, FileMode.Create); await contentStream.CopyToAsync(outputFileStream, token); } public async Task DownloadSorted(string key, string savePath, CancellationToken token) { UpdateToken(); string getUrl = string.Format(ServiceConfigurations.GetDownloadSortedPath, key); HttpRequestMessage request = new(HttpMethod.Get, getUrl); logger.LogInformation("Sending DownloadSorted request"); HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(token); await using FileStream outputFileStream = new(savePath, FileMode.Create); await contentStream.CopyToAsync(outputFileStream, token); } public async Task DeleteData(string? id, CancellationToken token) { UpdateToken(); string queryUri = $"{ServiceConfigurations.DeleteDataPath}?id={id}"; HttpRequestMessage request = new(HttpMethod.Delete, queryUri); logger.LogInformation("Sending DeleteData request"); HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(token); return await JsonSerializer.DeserializeAsync(contentStream, cancellationToken: token); } public async Task DeleteHistory(string key, CancellationToken token) { UpdateToken(); string queryUri = $"{ServiceConfigurations.DeleteHistoryPath}?key={key}"; HttpRequestMessage request = new(HttpMethod.Delete, queryUri); logger.LogInformation("Sending DeleteHistory request"); HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(token); return await JsonSerializer.DeserializeAsync(contentStream, cancellationToken: token); } public async Task GetNotice() { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Get, ServiceConfigurations.GetNoticePath); logger.LogInformation("Sending GetNotice request"); using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task GetSeriesGroupList(string key) { UpdateToken(); string getUrl = string.Format(ServiceConfigurations.GetSeriesGroupListPath, key); HttpRequestMessage request = new(HttpMethod.Get, getUrl); logger.LogInformation("Sending GetSeriesGroupList request"); using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task SelectSeries(string key, List series) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostSelectSeriesPath); SelectSeriesJson processJson = new() { Key = key, SelectedSeries = series }; string jsonString = JsonSerializer.Serialize(processJson); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending SelectSeries request"); logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task Start(string key) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostStartPath); SimpleKeyJson processJson = new() { Key = key }; string jsonString = JsonSerializer.Serialize(processJson); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending Start request"); logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task SelectReportModule(SelectReportModuleJson json) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostSelectReportModulePath); string jsonString = JsonSerializer.Serialize(json, EncodeAll); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending SelectReportModule request"); logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); //_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult()); //return new ReportIdJson(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); ReportIdJson resultJson = await JsonSerializer.DeserializeAsync(contentStream); if (resultJson.ErrorNumber != null) throw new SelectReportModuleFailedException(resultJson.ErrorNumber.Value); return resultJson; } public async Task PushToPacs(string key, int id) { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostPushReportPath); ReportKeyIdJson processKeyIdJson = new() { Key = key, ReportId = id }; string jsonString = JsonSerializer.Serialize(processKeyIdJson); using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json"); logger.LogInformation("Sending PushToPacs request"); logger.LogInformation("JSON is {json}", jsonString); request.Content = stringContent; using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } public async Task GetDicomNodeInfo() { UpdateToken(); HttpRequestMessage request = new(HttpMethod.Get, ServiceConfigurations.GetDicomNodeInfo); logger.LogInformation("Sending GetDicomNodeInfo request"); using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); result.EnsureSuccessStatusCode(); await using Stream contentStream = await result.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync(contentStream); } }