Reminders: убрать Category из общей части DTO, добавить ReceiverEmail/ReceiverName, ограничить чтение испытательного срока последними партициями
Tests / frontend-test (pull_request) Successful in 45s
Tests / backend-test (pull_request) Successful in 1m54s

- ReminderDto: базовый класс сузился до ReceiverEmail/ReceiverName/Url;
  RequestTypeId/RequestTypeName/RequestId/Status/RequestUniqueCode остались
  только в ApprovalOverdueReminderDto — ProbationEndingReminderDto они не
  нужны. Дискриминатор полиморфной сериализации ($type) больше не дублируется
  отдельным enum-полем Category.
- ApprovalOverdueReminderDto: старый Email сохранён рядом с унаследованным
  ReceiverEmail (то же значение) — контракт можно переключать постепенно.
- ApprovalRow/RowSelect: добавлен Title согласующего (TansuSiteApprover/Title)
  — заполняет ReceiverName для просроченных шагов, когда он есть в SharePoint.
- EmployeeDirectorySnapshot.NameByEmail: обратный поиск ФИО руководителя по
  email — заполняет ReceiverName для напоминаний об испытательном сроке без
  лишнего похода в api.tnsu.kz (данные уже загружены для EmailOf).
- GetProbationEndingAsync: испытательный срок не длиннее 6 месяцев, поэтому
  читаем только текущую и последнюю архивную партицию (не весь архив) и
  фильтруем строки по HireDate не старше 6 месяцев — экономим лишние запросы
  к SharePoint без потери актуальных заявок.
This commit is contained in:
gorbenko.pavel
2026-08-27 14:12:39 +05:00
parent 5d7503209a
commit cfc2b393bd
9 changed files with 184 additions and 83 deletions
@@ -31,12 +31,22 @@ internal sealed class EmployeeDirectorySnapshot
{
private readonly ILookup<string, EmployeeFullBaseInfo> _fullBaseByFio;
private readonly ILookup<string, EmployeeContactInfo> _contactsByFio;
private readonly IReadOnlyDictionary<string, string> _fioByEmail;
public EmployeeDirectorySnapshot(
IReadOnlyList<EmployeeFullBaseInfo> fullBase, IReadOnlyList<EmployeeContactInfo> contacts)
{
_fullBaseByFio = fullBase.ToLookup(e => Normalize(e.Fio));
_contactsByFio = contacts.ToLookup(e => Normalize(e.Fio));
// Обратный индекс email → ФИО — нужен, чтобы подписать напоминание именем
// получателя, когда известен только его email (руководитель кандидата,
// см. GetManagerAsync). При дублях email в справочнике побеждает первая
// запись — этого достаточно для отображения имени, не для идентификации.
_fioByEmail = contacts
.Where(e => !string.IsNullOrWhiteSpace(e.Email))
.GroupBy(e => e.Email!.Trim().ToLowerInvariant())
.ToDictionary(g => g.Key, g => g.First().Fio);
}
/// <summary>
@@ -64,6 +74,10 @@ internal sealed class EmployeeDirectorySnapshot
.Select(e => e.Email)
.FirstOrDefault(email => !string.IsNullOrWhiteSpace(email));
/// <summary>ФИО по email — для ReceiverName, когда известен только email получателя (см. EmailOf).</summary>
public string? NameByEmail(string email) =>
_fioByEmail.GetValueOrDefault(email.Trim().ToLowerInvariant());
/// <summary>
/// В справочнике и в заявке ФИО набирают руками, поэтому сравниваем без учёта
/// регистра и лишних пробелов между словами (в живых данных встречаются и