Napisałem aplikację w .NET 6. Posiadam w niej klasy PrinterrProxy i PosnetPrinterProxy. Obie te klasy korzystają z PrinterProxyClient. PrinterProxyClent wysyła request na serwis podany w _settings.PrintersProxyUrl. Chcę aby w przypadku PosnetPrinterProxy request był wysyłany na adres określony w _settings.PrintersPosnetProxyUrl. Zaproponuj implementację.

/// PrinterProxyClient.cs

#nullable enable
using System;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Threading;
using System.Threading.Tasks;
using Abp.Timing;
using Medica.Commerce.Printers.ApiModel;
using Medica.Configuration.AppConfiguration;
using Medica.Extensions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using RestSharp;
using RestSharp.Serializers.NewtonsoftJson;

namespace Medica.Commerce.Printers;

public class PrinterProxyClient : IPrinterProxyClient
{
    private readonly RestClient? _client;
    private readonly SecurityAppConfigurationSection _settings;

    public PrinterProxyClient(
        IOptions<SecurityAppConfigurationSection> settings)
    {
        _settings = settings.Value;

        if (!string.IsNullOrWhiteSpace(_settings.PrintersProxyUrl))
        {
            _client = new RestClient(_settings.PrintersProxyUrl)
            {
                Timeout = 12 * 1000,
                ReadWriteTimeout = 12 * 1000,
                CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache),
                CookieContainer = new CookieContainer(),
                AutomaticDecompression = true,
            };
            _client.UseNewtonsoftJson(new JsonSerializerSettings()
            {
                ContractResolver = new DefaultContractResolver { NamingStrategy = new LowerCaseNamingStrategy() },
                NullValueHandling = NullValueHandling.Ignore
            });
        }
    }

    private class LowerCaseNamingStrategy : NamingStrategy
    {
        protected override string ResolvePropertyName(string name)
        {
            return name.ToLower();
        }
    }

    public async Task<Either<T>> ExecuteAsync<T>(IRestRequest request) where T : GenericPrinterResponse
    {
        if (_client == null)
            return Either<T>.InternalError("PrinterProxyClient is disabled");

        using var cts = new CancellationTokenSource();

        var startTime = Clock.Now;
        try
        {
            if (_settings.PrintersApiKey is { } apiKey)
                request.AddHeader("X-API-Key", apiKey);

            var response = await _client.ExecuteAsync<T>(request, cts.Token);

            if (response.ErrorException is { } ex)
                return Either<T>.InternalError(ex);

            if (response.Data is not { } responseData)
                return Either<T>.InternalError($"Response data for request is null. Url: {request.Resource}. Body: {request.GetRequestBodyAsString()}");

            if (!responseData.Ok)
            {
                var errors = new[] { response.Data.Message, PrinterErrorResolver.ResolveError(response.Data.Error) };
                var errorMessage = string.Join(". ", errors.Where(v => !string.IsNullOrWhiteSpace(v)));

                return Either<T>.InternalError($"Printer has rejected the request: {errorMessage}. Url: {request.Resource}. Body: {request.GetRequestBodyAsString()}");
            }

            var duration = Clock.Now.Subtract(startTime).TotalMilliseconds;
            response.Data.ExecutionDuration = (long)duration;
            return response.Data.AsSuccess();
        }
        catch (Exception e)
        {
            return Either<T>.InternalError(e);
        }
    }
}

/// PrinterProxy.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Medica.Commerce.Printers.ApiModel;
using Medica.Extensions;
using Medica.Helpers;
using Newtonsoft.Json;
using RestSharp;

namespace Medica.Commerce.Printers;

public class PrinterProxy : IPrinterProxy
{
    private readonly IPrinterProxyClient _printerProxyClient;

    public PrinterProxy(IPrinterProxyClient printerProxyClient)
    {
        _printerProxyClient = printerProxyClient;
    }

    public Task<Either<IList<Printer>>> GetPrintersAsync()
    {
        const string url = "/printers";
        var request = new RestRequest(url, Method.GET, DataFormat.Json);
        return _printerProxyClient.ExecuteAsync<PrintersListModel>(request)
            .FlatMapAsync(response => response.Items is not { } apiPrinters
                ? Either<IList<Printer>>.InternalError("Null list of printers")
                : apiPrinters.AsSuccess()
            );
    }


    public Task<Either<ReceiptStatusResponse>> GetReceiptStatus(Guid id, string printerId)
    {
        var url = $"/printers/{printerId}/paragon/{id}";
        var request = new RestRequest(url, Method.GET, DataFormat.Json);
        return _printerProxyClient.ExecuteAsync<ReceiptStatusResponse>(request);
    }

    public Task<Either<PrinterStatus>> GetPrinterStatus(string printerId)
    {
        var url = $"/printers/{printerId}/status";
        var request = new RestRequest(url, Method.GET, DataFormat.Json);
        return _printerProxyClient.ExecuteAsync<PrinterStatus>(request);
    }
    public Task<Either> RestartProxy(string printerId)
    {
        var url = $"/restart/{printerId}";
        var request = new RestRequest(url, Method.POST, DataFormat.Json);
        return _printerProxyClient.ExecuteAsync<GenericPrinterResponse>(request)
            .TransparentExecuteAsync(_ => Task.Delay(2000)) //To restart take affect
            .IgnoreValue();
    }

    public Task<Either<Guid>> PrintReceipt(PrinterRequestModel data, string printerId)
    {
        var url = $"/printers/{printerId}/paragon?async=true";
        var request = new RestRequest(url, Method.POST, DataFormat.Json);
        request.AddJsonBody(data);
        return _printerProxyClient.ExecuteAsync<PrinterResponse>(request)
            .FlatMapAsync(response => response.Transaction == Guid.Empty
                ? Either<Guid>.InternalError($"Response data does not contain valid transaction id")
                : response.Transaction.AsSuccess()
            );
    }

    public Task<Either<T>> CustomCommandAsync<T>(IEnumerable<CmdParams> cmdParams, string printerId) where T : GenericPrinterResponse
    {
        var url = $"/printers/{printerId}/command?fulldebug={EnvironmentHelper.IsDebug}";
        var request = new RestRequest(url, Method.POST, DataFormat.Json);
        request.AddJsonBody(cmdParams);

        return _printerProxyClient.ExecuteAsync<T>(request).FlatMapAsync(v =>
        {
            return !string.IsNullOrEmpty(v.Error)
                ? Either<T>.InternalError($"{PrinterErrorResolver.ResolveError(v.Error).Trim('.')}. Url: {request.Resource}. Body: {request.GetRequestBodyAsString()}") : v.AsSuccess();
        });
    }

    public Task<Either<T>> CustomCommandAsync<T>(string command, string printerId) where T : GenericPrinterResponse
    {
        var url = $"/printers/{printerId}/command?fulldebug={EnvironmentHelper.IsDebug}";
        var request = new RestRequest(url, Method.POST, DataFormat.Json);
        var cmdParams = new List<CmdWithoutParams>() { new CmdWithoutParams(command) };
        request.AddJsonBody(cmdParams);

        if (EnvironmentHelper.IsDebug)
            Console.WriteLine(request.GetRequestBodyAsString());

        return _printerProxyClient.ExecuteAsync<T>(request).FlatMapAsync(v =>
        {
            return !string.IsNullOrEmpty(v.Error)
                ? Either<T>.InternalError($"{PrinterErrorResolver.ResolveError(v.Error).Trim('.')}. Url: {request.Resource}. Body: {request.GetRequestBodyAsString()}") : v.AsSuccess();
        });
    }

    public async Task<Either<GenericPrinterResponse>> PrintDailyReportAsync(DateTime date, string printerId)
    {
        var cmdParams1 = new List<CmdParams>()
        {
            new CmdParams("dailyrep", $"da{date:yyyy-MM-dd}"),
        };

        var result1 = await CustomCommandAsync<GenericPrinterResponse>(cmdParams1, printerId);

        if (result1.IsFailure)
            return result1;

        return await CustomCommandAsync<GenericPrinterResponse>("fmrecrd", printerId);
    }

    public Task<Either<GenericPrinterResponse>> PrintMonthlyReportAsync(DateTime date, string printerId)
    {
        var cmdParams = new List<CmdParams>()
        {
            new CmdParams("monthlyrep", $"da{date:yyyy-MM-dd}"),
        };

        return CustomCommandAsync<GenericPrinterResponse>(cmdParams, printerId);
    }
}

/// PrinterPosnetProxy.cs

using Medica.Commerce.Printers;
using Medica.Commerce.Printers.ApiModel;
using Microsoft.Extensions.Configuration;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Medica.Commerce.PrintersPosnet;

public class PrinterPosnetProxy : IPrinterPosnetProxy
{
    private readonly IPrinterPosnetService _printerPosnetServive;
    private readonly IPrinterProxyClient _printerProxyClient;

    private readonly string _port;

    public PrinterPosnetProxy(
        IConfiguration configuration, 
        IPrinterPosnetService printerPosnetServive,
        IPrinterProxyClient printerProxyClient)
    {
        _port = configuration["FiscalPrinterApi:Port"];
        _printerPosnetServive = printerPosnetServive;
        _printerProxyClient = printerProxyClient;
    }

    public async Task<Either<IList<Printer>>> GetPrintersAsync()
    {
        var posnetPrinters = await _printerPosnetServive.GetAllPrintersAsync();
        if (posnetPrinters == null || !posnetPrinters.Any())
        {
            return Either<IList<Printer>>.InternalError("No Posnet printers found.");
        }

        var printers = posnetPrinters.Select(p => new Printer
        {
            Id = p.Id.ToString(),
            Name = p.Name,
            OrgUnitId = p.OrganizationUnitId,
            Target = PrinterPosnetTarget.GetByEnumVal(p.Target)
        }).ToList();

        return Either<IList<Printer>>.Success(printers);
    }

    public async Task<Either<PrinterStatus>> GetPrinterStatus(string id)
    {
        var ipAddress = await GetIpAddressAsync(new Guid(id));
        if (string.IsNullOrWhiteSpace(ipAddress))
        {
            return Either<PrinterStatus>.InternalError($"Ip address of printer with ID {id} not found.");
        }

        var request = new RestRequest("/status", Method.GET, DataFormat.Json);
        request.AddQueryParameter("ip", ipAddress);
        request.AddQueryParameter("port", _port);
        return await _printerProxyClient.ExecuteAsync<PrinterStatus>(request);
    }

    public async Task<Either<Guid>> PrintReceipt(PrinterRequestModel data, string id)
    {
        var ipAddress = await GetIpAddressAsync(new Guid(id));
        if (string.IsNullOrWhiteSpace(ipAddress))
        {
            return Either<Guid>.InternalError($"Ip address of printer with ID {id} not found.");
        }

        var request = new RestRequest("/paragon", Method.POST, DataFormat.Json);
        request.AddQueryParameter("ip", ipAddress);
        request.AddQueryParameter("port", _port);
        request.AddJsonBody(data);
        return await _printerProxyClient.ExecuteAsync<PrinterResponse>(request)
            .FlatMapAsync(response => response.Transaction == Guid.Empty
                ? Either<Guid>.InternalError($"Response data does not contain valid transaction id")
                : response.Transaction.AsSuccess()
            );
    }

    public async Task<Either<ReceiptStatusResponse>> GetReceiptStatus(Guid id, string printerId)
    {
        var request = new RestRequest($"/paragon/{id}", Method.GET, DataFormat.Json);
        return await _printerProxyClient.ExecuteAsync<ReceiptStatusResponse>(request);
    }

    public async Task<Either> RestartProxy(string printerId)
    {
        return Either.Success();
    }

    private async Task<Either<string>> GetIpAddressAsync(Guid id)
    {
        var result = await _printerPosnetServive.GetAsync(id);
        return result.Match(
            success => Either<string>.Success(success.IpAddress),
            error => throw new Exception(error.Message)
        );
    }
}