Add resource supply contract export by its guid
This commit is contained in:
24
Hcs.Client/Client/Api/HouseManagementApi.cs
Normal file
24
Hcs.Client/Client/Api/HouseManagementApi.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Hcs.Client.Api.Request.HouseManagement;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Client.Api
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagementServiceAsync/
|
||||
public class HouseManagementApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// Возвращает договор ресурсоснабжения по его идентификатору в ГИС ЖКХ
|
||||
/// </summary>
|
||||
/// <param name="contractRootGuid">Идентификатор договора ресурсоснабжения в ГИС ЖКХ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Договор ресурсоснабжения</returns>
|
||||
public async Task<exportSupplyResourceContractResultType> ExportSupplyResourceContractDataAsync(Guid contractRootGuid, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportSupplyResourceContractDataRequest(client);
|
||||
return await request.ExecuteAsync(contractRootGuid, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@ namespace Hcs.Client.Api
|
||||
/// <param name="registryNumber">Реестровый номер справочника</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Данные справочника</returns>
|
||||
public async Task<IEnumerable<NsiItemType>> ExportDataProviderNsiItem(exportDataProviderNsiItemRequestRegistryNumber registryNumber, CancellationToken token = default)
|
||||
public async Task<IEnumerable<NsiItemType>> ExportDataProviderNsiItemAsync(exportDataProviderNsiItemRequestRegistryNumber registryNumber, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@ -16,7 +16,7 @@ namespace Hcs.Client.Api
|
||||
/// <param name="listGroup">Группа справочников, где NSI - общесистемный, а NSIRAO - ОЖФ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Данные общесистемного справочника</returns>
|
||||
public async Task<NsiItemType> ExportNsiItem(int registryNumber, ListGroup listGroup, CancellationToken token = default)
|
||||
public async Task<NsiItemType> ExportNsiItemAsync(int registryNumber, ListGroup listGroup, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
using Hcs.Client.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Client.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ExportSupplyResourceContractDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool EnableMinimalResponseWaitDelay => false;
|
||||
|
||||
internal async Task<exportSupplyResourceContractResultType> ExecuteAsync(Guid contractRootGuid, CancellationToken token)
|
||||
{
|
||||
exportSupplyResourceContractResultType result = null;
|
||||
|
||||
void OnResultReceived(exportSupplyResourceContractResultType[] contracts)
|
||||
{
|
||||
result = contracts.Length > 0 ? contracts[0] : null;
|
||||
}
|
||||
|
||||
await QueryBatchAsync(contractRootGuid, null, null, OnResultReceived, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<PaginationData> QueryBatchAsync(
|
||||
Guid? contractRootGuid, string contractNumber, Guid? exportContractRootGuid,
|
||||
Action<exportSupplyResourceContractResultType[]> onResultReceived,
|
||||
CancellationToken token)
|
||||
{
|
||||
var itemsElementName = new List<ItemsChoiceType32>();
|
||||
var items = new List<object>();
|
||||
|
||||
if (contractRootGuid.HasValue)
|
||||
{
|
||||
itemsElementName.Add(ItemsChoiceType32.ContractRootGUID);
|
||||
items.Add(contractRootGuid.ToString());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(contractNumber))
|
||||
{
|
||||
itemsElementName.Add(ItemsChoiceType32.ContractNumber);
|
||||
items.Add(contractNumber);
|
||||
}
|
||||
|
||||
if (exportContractRootGuid.HasValue)
|
||||
{
|
||||
itemsElementName.Add(ItemsChoiceType32.ExportContractRootGUID);
|
||||
items.Add(exportContractRootGuid.ToString());
|
||||
}
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/exportSupplyResourceContractRequest.html
|
||||
var request = new exportSupplyResourceContractRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.1.1.1",
|
||||
ItemsElementName = [.. itemsElementName],
|
||||
Items = [.. items]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var ackResponse = await asyncClient.exportSupplyResourceContractDataAsync(
|
||||
CreateRequestHeader(), request);
|
||||
return ackResponse.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
var contractResult = result.Items.OfType<getStateResultExportSupplyResourceContractResult>().First();
|
||||
onResultReceived?.Invoke(contractResult.Contract);
|
||||
|
||||
return new PaginationData(contractResult.Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
using Hcs.Client.Api.Request;
|
||||
using Hcs.Client.Api.Request.Adapter;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.HouseManagement
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultMany { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class HouseManagementPortsTypeAsyncClient : IAsyncClient<RequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> GetStateAsync(RequestHeader header, IGetStateRequest request)
|
||||
{
|
||||
return await getStateAsync(header, (getStateRequest)request);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResponse : IGetStateResponse
|
||||
#pragma warning restore IDE1006
|
||||
{
|
||||
public IGetStateResult GetStateResult => getStateResult;
|
||||
}
|
||||
|
||||
public partial class AckRequestAck : IAck { }
|
||||
|
||||
public partial class ErrorMessageType : IErrorMessage { }
|
||||
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateRequest : IGetStateRequest { }
|
||||
#pragma warning restore IDE1006
|
||||
}
|
||||
|
||||
namespace Hcs.Client.Api.Request.HouseManagement
|
||||
{
|
||||
internal class HouseManagementRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
HouseManagementPortsTypeAsyncClient,
|
||||
HouseManagementPortsTypeAsync,
|
||||
RequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.HomeManagementAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => true;
|
||||
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ namespace Hcs.Client.Api.Request.Nsi
|
||||
RegistryNumber = registryNumber
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async(asyncClient) =>
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportDataProviderNsiItemAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
|
||||
@ -5,7 +5,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.Nsi
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultMany { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class NsiPortsTypeAsyncClient : IAsyncClient<RequestHeader>
|
||||
{
|
||||
@ -15,7 +17,9 @@ namespace Hcs.Service.Async.Nsi
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResponse : IGetStateResponse
|
||||
#pragma warning restore IDE1006
|
||||
{
|
||||
public IGetStateResult GetStateResult => getStateResult;
|
||||
}
|
||||
@ -24,7 +28,9 @@ namespace Hcs.Service.Async.Nsi
|
||||
|
||||
public partial class ErrorMessageType : IErrorMessage { }
|
||||
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateRequest : IGetStateRequest { }
|
||||
#pragma warning restore IDE1006
|
||||
}
|
||||
|
||||
namespace Hcs.Client.Api.Request.Nsi
|
||||
|
||||
@ -18,7 +18,7 @@ namespace Hcs.Client.Api.Request.NsiCommon
|
||||
ListGroup = listGroup
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async (asyncClient) =>
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportNsiItemAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
|
||||
54
Hcs.Client/Client/Api/Request/PaginationData.cs
Normal file
54
Hcs.Client/Client/Api/Request/PaginationData.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
|
||||
namespace Hcs.Client.Api.Request
|
||||
{
|
||||
internal class PaginationData
|
||||
{
|
||||
/// <summary>
|
||||
/// Состояние, указывающее на то, что это последняя страница
|
||||
/// </summary>
|
||||
internal bool IsLastPage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор следующей страницы
|
||||
/// </summary>
|
||||
internal Guid NextGuid { get; private set; }
|
||||
|
||||
public PaginationData(object item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new System.Exception($"[{nameof(PaginationData)}] item is null");
|
||||
}
|
||||
else if (item is bool boolItem)
|
||||
{
|
||||
if (boolItem == false)
|
||||
{
|
||||
throw new System.Exception($"[{nameof(PaginationData)}] item is false");
|
||||
}
|
||||
|
||||
IsLastPage = true;
|
||||
}
|
||||
else if (item is string stringItem)
|
||||
{
|
||||
IsLastPage = false;
|
||||
NextGuid = Guid.Parse(stringItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception($"[{nameof(PaginationData)}] failed to handle item of {item.GetType().FullName} type");
|
||||
}
|
||||
}
|
||||
|
||||
internal static PaginationData CreateLastPageData()
|
||||
{
|
||||
return new PaginationData(true);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{nameof(PaginationData)}] {nameof(IsLastPage)} = {IsLastPage}" +
|
||||
(IsLastPage ? "" : $", {nameof(NextGuid)} = {NextGuid}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,38 +130,15 @@ namespace Hcs.Client.Api.Request
|
||||
{
|
||||
try
|
||||
{
|
||||
if (request == null)
|
||||
if (CanBeRestarted)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
return await RunRepeatableTaskInsistentlyAsync(
|
||||
async () => await ExecuteSendAndWaitResultAsync(request, sender, token), token);
|
||||
}
|
||||
|
||||
var version = RequestHelper.GetRequestVersionString(request);
|
||||
client.TryLog($"Executing request {RemoteAddress.Uri}/{request.GetType().Name} of version {version}...");
|
||||
|
||||
TAck ack;
|
||||
|
||||
var stopWatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
using (var asyncClient = CreateAsyncClient())
|
||||
else
|
||||
{
|
||||
ack = await sender(asyncClient);
|
||||
return await ExecuteSendAndWaitResultAsync(request, sender, token);
|
||||
}
|
||||
stopWatch.Stop();
|
||||
|
||||
client.TryLog($"Request executed in {stopWatch.ElapsedMilliseconds} ms, result GUID is {ack.MessageGUID}");
|
||||
|
||||
var result = await WaitForResultAsync(ack, true, token);
|
||||
if (result is IQueryable queryableResult)
|
||||
{
|
||||
queryableResult.OfType<TErrorMessage>().ToList().ForEach(x =>
|
||||
{
|
||||
throw RemoteException.CreateNew(x.ErrorCode, x.Description);
|
||||
});
|
||||
}
|
||||
else if (result is TErrorMessage x)
|
||||
{
|
||||
throw RemoteException.CreateNew(x.ErrorCode, x.Description);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (RestartTimeoutException e)
|
||||
{
|
||||
@ -175,6 +152,108 @@ namespace Hcs.Client.Api.Request
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Для запросов к серверу которые можно направлять несколько раз, разрешаем
|
||||
/// серверу аномально отказаться. Предполагается, что здесь мы игнорируем
|
||||
/// только жесткие отказы серверной инфраструктуры, которые указывают
|
||||
/// что запрос даже не был принят в обработку. Также все запросы на
|
||||
/// чтение можно повторять в случае их серверных системных ошибок.
|
||||
/// </summary>
|
||||
protected async Task<TRepeatableResult> RunRepeatableTaskInsistentlyAsync<TRepeatableResult>(
|
||||
Func<Task<TRepeatableResult>> func, CancellationToken token)
|
||||
{
|
||||
var afterErrorDelaySec = 120;
|
||||
for (var attempt = 1; ; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
if (CanIgnoreSuchException(e, out string marker))
|
||||
{
|
||||
client.TryLog($"Ignoring error of attempt #{attempt} with type [{marker}]");
|
||||
client.TryLog($"Waiting {afterErrorDelaySec} sec until next attempt...");
|
||||
|
||||
await Task.Delay(afterErrorDelaySec * 1000, token);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e is RestartTimeoutException)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (e is RemoteException)
|
||||
{
|
||||
throw RemoteException.CreateNew(e as RemoteException);
|
||||
}
|
||||
|
||||
throw new System.Exception("Cannot ignore this exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanIgnoreSuchException(System.Exception e, out string resultMarker)
|
||||
{
|
||||
foreach (var marker in ignorableSystemErrorMarkers)
|
||||
{
|
||||
var found = Util.EnumerateInnerExceptions(e).Find(
|
||||
x => x.Message != null && x.Message.Contains(marker));
|
||||
if (found != null)
|
||||
{
|
||||
resultMarker = marker;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
resultMarker = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<TResult> ExecuteSendAndWaitResultAsync(
|
||||
object request,
|
||||
Func<TAsyncClient, Task<TAck>> sender,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
var version = RequestHelper.GetRequestVersionString(request);
|
||||
client.TryLog($"Executing request {RemoteAddress.Uri}/{request.GetType().Name} of version {version}...");
|
||||
|
||||
TAck ack;
|
||||
|
||||
var stopWatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
using (var asyncClient = CreateAsyncClient())
|
||||
{
|
||||
ack = await sender(asyncClient);
|
||||
}
|
||||
stopWatch.Stop();
|
||||
|
||||
client.TryLog($"Request executed in {stopWatch.ElapsedMilliseconds} ms, result GUID is {ack.MessageGUID}");
|
||||
|
||||
var result = await WaitForResultAsync(ack, true, token);
|
||||
if (result is IQueryable queryableResult)
|
||||
{
|
||||
queryableResult.OfType<TErrorMessage>().ToList().ForEach(x =>
|
||||
{
|
||||
throw RemoteException.CreateNew(x.ErrorCode, x.Description);
|
||||
});
|
||||
}
|
||||
else if (result is TErrorMessage x)
|
||||
{
|
||||
throw RemoteException.CreateNew(x.ErrorCode, x.Description);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private TAsyncClient CreateAsyncClient()
|
||||
{
|
||||
var asyncClient = (TAsyncClient)Activator.CreateInstance(typeof(TAsyncClient), binding, RemoteAddress);
|
||||
@ -340,68 +419,5 @@ namespace Hcs.Client.Api.Request
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Для запросов к серверу которые можно направлять несколько раз, разрешаем
|
||||
/// серверу аномально отказаться. Предполагается, что здесь мы игнорируем
|
||||
/// только жесткие отказы серверной инфраструктуры, которые указывают
|
||||
/// что запрос даже не был принят в обработку. Также все запросы на
|
||||
/// чтение можно повторять в случае их серверных системных ошибок.
|
||||
/// </summary>
|
||||
protected async Task<TRepeatableResult> RunRepeatableTaskInsistentlyAsync<TRepeatableResult>(
|
||||
Func<Task<TRepeatableResult>> func, CancellationToken token)
|
||||
{
|
||||
var afterErrorDelaySec = 120;
|
||||
for (var attempt = 1; ; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
if (CanIgnoreSuchException(e, out string marker))
|
||||
{
|
||||
client.TryLog($"Ignoring error of attempt #{attempt} with type [{marker}]");
|
||||
client.TryLog($"Waiting {afterErrorDelaySec} sec until next attempt...");
|
||||
|
||||
await Task.Delay(afterErrorDelaySec * 1000, token);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e is RestartTimeoutException)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (e is RemoteException)
|
||||
{
|
||||
throw RemoteException.CreateNew(e as RemoteException);
|
||||
}
|
||||
|
||||
throw new System.Exception("Cannot ignore this exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanIgnoreSuchException(System.Exception e, out string resultMarker)
|
||||
{
|
||||
foreach (var marker in ignorableSystemErrorMarkers)
|
||||
{
|
||||
var found = Util.EnumerateInnerExceptions(e).Find(
|
||||
x => x.Message != null && x.Message.Contains(marker));
|
||||
if (found != null)
|
||||
{
|
||||
resultMarker = marker;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
resultMarker = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ namespace Hcs.Client
|
||||
/// </summary>
|
||||
public class UniClient : ClientBase
|
||||
{
|
||||
public HouseManagementApi HouseManagement => new(this);
|
||||
|
||||
public NsiApi Nsi => new(this);
|
||||
|
||||
public NsiCommonApi NsiCommon => new(this);
|
||||
|
||||
Reference in New Issue
Block a user