using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using DebtRequests = Hcs.Service.Async.DebtRequests.v15_7_0_1; namespace Hcs.ClientApi.DebtRequestsApi { /// /// Метод получения данных о направленных нам (под)запросах о наличии задолженности /// public class HcsDebtSubrequestExporter : HcsDebtRequestsMethod { public HcsDebtSubrequestExporter(HcsClientConfig config) : base(config) { EnableMinimalResponseWaitDelay = true; } public class DSRsBatch { public List DebtSubrequests = new List(); public Guid NextSubrequestGuid; public bool LastPage; } public async Task ExportDSRByRequestNumber(string requestNumber, CancellationToken token) { var conditionTypes = new List(); var conditionValues = new List(); conditionTypes.Add(DebtRequests.ItemsChoiceType5.requestNumber); conditionValues.Add(requestNumber); var result = await ExportSubrequestBatchByCondition( conditionTypes.ToArray(), conditionValues.ToArray(), token); int n = result.DebtSubrequests.Count; if (n == 0) return null; if (n == 1) return result.DebtSubrequests[0]; throw new HcsException( $"По номеру запроса о наличии задолженности №{requestNumber}" + $" получено несколько ({n}) ответов, ожидался только один"); } public async Task ExportDSRsByPeriodOfSending( DateTime startDate, DateTime endDate, Guid? firstSubrequestGuid, Action resultHandler, CancellationToken token = default) { int numResults = 0; Guid? nextSubrequestGuid = firstSubrequestGuid; bool firstGuidIsReliable = false; while (true) { if (numResults == 0) Log("Запрашиваем первую партию записей..."); else Log($"Запрашиваем следующую партию записей, уже получено {numResults}..."); var batch = await ExportDSRsBatchByPeriodOfSending( startDate, endDate, nextSubrequestGuid, token, firstGuidIsReliable); foreach (var s in batch.DebtSubrequests) { if (resultHandler != null) resultHandler(s); numResults += 1; } if (batch.LastPage) break; nextSubrequestGuid = batch.NextSubrequestGuid; firstGuidIsReliable = true; } return numResults; } public async Task ExportDSRsBatchByPeriodOfSending( DateTime startDate, DateTime endDate, Guid? firstSubrequestGuid, CancellationToken token, bool firstGuidIsReliable) { var conditionTypes = new List(); var conditionValues = new List(); conditionTypes.Add(DebtRequests.ItemsChoiceType5.periodOfSendingRequest); conditionValues.Add(new DebtRequests.Period() { startDate = startDate, endDate = endDate }); if (firstSubrequestGuid != null) { conditionTypes.Add(DebtRequests.ItemsChoiceType5.exportSubrequestGUID); conditionValues.Add(firstSubrequestGuid.ToString()); } Func> taskFunc = async () => await ExportSubrequestBatchByCondition( conditionTypes.ToArray(), conditionValues.ToArray(), token); Func canIgnoreFunc = delegate (Exception e) { return CanIgnoreSuchException(e, firstGuidIsReliable); }; return await RunRepeatableTaskAsync(taskFunc, canIgnoreFunc, int.MaxValue); } private async Task ExportSubrequestBatchByCondition( DebtRequests.ItemsChoiceType5[] conditionTypes, object[] conditionValues, CancellationToken token) { var requestHeader = CreateRequestHeader(); var requestBody = new DebtRequests.exportDSRsRequest { Id = HcsConstants.SignedXmlElementId, // TODO: Тут напрямую указывается версия version = "14.0.0.0", ItemsElementName = conditionTypes, Items = conditionValues }; var request = new DebtRequests.exportDebtSubrequestsRequest { RequestHeader = requestHeader, exportDSRsRequest = requestBody }; var ack = await SendAsync(request, token); try { var result = await WaitForResultAsync(ack, true, token); return ParseExportResultBatch(result); } catch (HcsNoResultsRemoteException) { return new DSRsBatch() { LastPage = true }; } } private DSRsBatch ParseExportResultBatch(RemoteCaller.IHcsGetStateResult result) { var batch = new DSRsBatch(); result.Items.OfType().ToList().ForEach(r => { Log($"Принято запросов о наличии задолженности: {r.subrequestData?.Count()}"); // на последней странице вывода может не быть ни одной записи if (r.subrequestData != null) { r.subrequestData.ToList().ForEach(s => { batch.DebtSubrequests.Add(Adapt(s)); }); } if (r.pagedOutput == null || r.pagedOutput.Item == null) batch.LastPage = true; else { var item = r.pagedOutput.Item; if (item is bool && (bool)item == true) batch.LastPage = true; else if (!Guid.TryParse(item.ToString(), out batch.NextSubrequestGuid)) throw new HcsException($"Неожиданное значение pagedOutput [{item}]"); } }); return batch; } private HcsDebtSubrequest Adapt(DebtRequests.DSRType s) { var dsr = new HcsDebtSubrequest(); dsr.SubrequestGuid = ParseGuid(s.subrequestGUID); dsr.RequestGuid = ParseGuid(s.requestInfo.requestGUID); dsr.RequestNumber = s.requestInfo.requestNumber; dsr.SentDate = s.requestInfo.sentDate; dsr.Address = s.requestInfo.housingFundObject.address; var hfo = s.requestInfo.housingFundObject; if (hfo.Items != null && hfo.ItemsElementName != null && hfo.Items.Length == hfo.ItemsElementName.Length) { for (int i = 0; i < hfo.Items.Length; i++) { string itemValue = hfo.Items[i]; switch (hfo.ItemsElementName[i]) { case DebtRequests.ItemsChoiceType7.HMobjectGUID: dsr.HМObjectGuid = ParseGuid(itemValue); break; case DebtRequests.ItemsChoiceType7.houseGUID: dsr.GisHouseGuid = ParseGuid(itemValue); break; case DebtRequests.ItemsChoiceType7.adressType: dsr.HMObjectType = itemValue; break; case DebtRequests.ItemsChoiceType7.addressDetails: dsr.AddressDetails = itemValue; break; } } } if (!string.IsNullOrEmpty(hfo.fiasHouseGUID)) { dsr.FiasHouseGuid = ParseGuid(hfo.fiasHouseGUID); } // TODO: Проверить комментарий // Из hcs-v13 //dsr.GisHouseGuid = ParseGuid(s.requestInfo.housingFundObject.houseGUID); //dsr.AddressDetails = s.requestInfo.housingFundObject.addressDetails; dsr.DebtStartDate = s.requestInfo.period.startDate; dsr.DebtEndDate = s.requestInfo.period.endDate; dsr.ResponseStatus = ConvertStatusType(s.responseStatus); dsr.ResponseDate = s.requestInfo.responseDate; return dsr; } private HcsDebtSubrequest.ResponseStatusType ConvertStatusType(DebtRequests.ResponseStatusType type) { switch (type) { case DebtRequests.ResponseStatusType.Sent: return HcsDebtSubrequest.ResponseStatusType.Sent; case DebtRequests.ResponseStatusType.NotSent: return HcsDebtSubrequest.ResponseStatusType.NotSent; case DebtRequests.ResponseStatusType.AutoGenerated: return HcsDebtSubrequest.ResponseStatusType.AutoGenerated; default: throw new HcsException("Неизвестный статус отправки ответа: " + type); } } // TODO: Проверить игнорирование ошибок private bool CanIgnoreSuchException(Exception e, bool firstGuidIsReliable) { // "Произошла ошибка при передаче данных. Попробуйте осуществить передачу данных повторно." if (HcsUtil.EnumerateInnerExceptions(e).Any( x => x is HcsRemoteException && (x as HcsRemoteException).ErrorCode == "EXP001000")) { return true; } // Возникающий на больших списках отказ возобновляемый, учитывем факт что GUID был // получен из ГИСЖКХ и явно является надежным if (firstGuidIsReliable && HcsUtil.EnumerateInnerExceptions(e).Any( x => x.Message != null && x.Message.Contains("Error loading content: Content not found for guid:"))) { return true; } return false; } } }