From 3c3c7caa271d762097d26158559c01994bac49d4 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 2 Oct 2026 14:53:54 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=87?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BB=D0=BB=D0=B5?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=BC=20=D0=B8=20=D0=BB=D0=BE=D0=B3=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D0=BA=D0=B8=20=D0=B2=20by-approver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Экран «Заявки по участнику» показывал пустой список: чтение 18 типов заявок шло разом, ошибки SharePoint глотались молча. Теперь не более 3 типов одновременно, а пропущенные типы/списки пишутся в лог. Co-Authored-By: Claude Sonnet 5.5 --- .../RequestManage/RequestManageService.cs | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs b/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs index 609b1ad..50e18aa 100644 --- a/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs +++ b/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs @@ -2,6 +2,7 @@ using System.Text.Json; using System.Text.RegularExpressions; using FluentResults; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Tnsu.ApprovalRoute.Application.Common.Errors; using Tnsu.ApprovalRoute.Application.ListPartitioning; using Tnsu.ApprovalRoute.Domain.Entities; @@ -25,10 +26,16 @@ namespace Tnsu.ApprovalRoute.Application.RequestManage; public sealed class RequestManageService( ApprovalRouteDbContext db, ISharePointClient sharePoint, - ListPartitionRegistry partitionRegistry) + ListPartitionRegistry partitionRegistry, + ILogger logger) { private const int DefaultPageSize = 20; + // Сколько типов заявок читаем из SharePoint одновременно. Без ограничения + // 18 типов × партиции разом упирались в троттлинг (429), а ошибки глотались + // и экран показывал пустой список. + private const int MaxParallelTypes = 3; + // Внутренние имена колонок списка согласования — 1:1 с FIELDS в // requestManageService.ts (тот же самый список SharePoint, тот же провижининг). private const string ApproverField = "TansuSiteApprover"; @@ -145,8 +152,19 @@ public sealed class RequestManageService( var statusFilter = string.Join(" or ", activeStatuses.Select(s => $"{StatusField} eq '{Escape(s)}'")); var email = approverEmail.Trim(); - var results = await Task.WhenAll( - requestTypes.Select(rt => FindForTypeSafeAsync(rt, statusFilter, email, cancellationToken))); + using var gate = new SemaphoreSlim(MaxParallelTypes); + var results = await Task.WhenAll(requestTypes.Select(async rt => + { + await gate.WaitAsync(cancellationToken); + try + { + return await FindForTypeSafeAsync(rt, statusFilter, email, cancellationToken); + } + finally + { + gate.Release(); + } + })); return Result.Ok>(results.SelectMany(r => r).ToList()); } @@ -158,10 +176,11 @@ public sealed class RequestManageService( { return await FindForTypeAsync(requestType, statusFilter, approverEmail, cancellationToken); } - catch + catch (Exception ex) when (ex is not OperationCanceledException) { // Один недоступный/переименованный список не должен обнулять результат по // остальным типам заявок — та же терпимость, что и в ReminderService. + logger.LogWarning(ex, "by-approver: тип заявок {RequestTypeId} «{RequestTypeName}» пропущен", requestType.Id, requestType.Name); return []; } } @@ -190,9 +209,11 @@ public sealed class RequestManageService( hits = await sharePoint.GetItemsByListTitleAsync( siteUrl, approvalListTitle, $"{ApprovalRowSelect}&$filter=({statusFilter})&$top=2000", cancellationToken); } - catch + catch (Exception ex) when (ex is not OperationCanceledException) { - continue; // партиция сломана/переименована вручную — остальные не теряем. + // партиция сломана/переименована вручную — остальные не теряем. + logger.LogWarning(ex, "by-approver: список «{ApprovalList}» пропущен", approvalListTitle); + continue; } var matched = hits