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
@@ -2,40 +2,31 @@ using System.Text.Json.Serialization;
namespace Tnsu.ApprovalRoute.Application.Reminders;
/// <param name="Category">Вид напоминания — один на конкретный (sealed) наследник, см. их конструкторы.</param>
/// <param name="Url">Ссылка на заявку.</param>
/// <param name="RequestTypeId">Id типа заявки (RequestType).</param>
/// <param name="RequestTypeName">Название типа заявки — для читаемости в письме/таблице, без похода в БД.</param>
/// <param name="RequestId">Id заявки (TicketId строки списка согласования / Id строки списка данных).</param>
/// <param name="Status">Статус заявки/шага — из того списка, который читает конкретный вид напоминания.</param>
/// <param name="RequestUniqueCode">
/// Уникальный код заявки (колонка RequestUniqueCode списка данных) — null, если у
/// заявки его нет или строка данных не найдена.
/// <param name="ReceiverEmail">Email того, кому адресовано напоминание.</param>
/// <param name="ReceiverName">
/// ФИО получателя, если оно известно. Не всегда доступно (например, у согласующего
/// в старой строке списка без заполненного Title персонального поля) — тогда null.
/// </param>
/// <param name="Url">
/// Ссылка на заявку. Собирается здесь, в сервисе (а не на стороне потребителя),
/// потому что не константна: requestId и партиция списка (server-relative url,
/// параметр rplist) у каждой заявки свои, и сервис их всё равно уже знает —
/// дублировать эту логику снаружи незачем.
/// </param>
/// <remarks>
/// Общая часть двух видов напоминаний — <see cref="ApprovalOverdueReminderDto"/> и
/// <see cref="ProbationEndingReminderDto"/>. У них разные поля-триггеры и разные
/// адресаты, общее — только то, что описано здесь (к какой заявке относится строка
/// и как её открыть).
/// <see cref="ProbationEndingReminderDto"/>. Категории заявки (RequestTypeId,
/// RequestId и т.п.) здесь нет: она нужна только просроченным шагам согласования
/// (Power Automate ссылается на неё, работая со списком согласования конкретного
/// типа заявки) и объявлена прямо в ApprovalOverdueReminderDto.
///
/// [JsonPolymorphic]/[JsonDerivedType] — вместо того чтобы полагаться на
/// compile-time тип элемента (ReminderService всегда работает со списком именно
/// этого базового типа, см. IReadOnlyList&lt;ReminderDto&gt;), System.Text.Json без
/// этих атрибутов сериализовал бы только поля базового класса, теряя специфичные
/// для наследника (Email/HoursOverdue или ManagerEmail/CandidateEmail и т.д.).
/// Дискриминатор — отдельное системное поле "$type", а не Category: значение
/// Category читаемое (совпадает с именем enum-константы через уже подключенный
/// глобально JsonStringEnumConverter, см. Program.cs) и не привязано к деталям
/// сериализации.
/// [JsonPolymorphic]/[JsonDerivedType] нужны, потому что ReminderService отдаёт
/// список именно базового типа (IReadOnlyList&lt;ReminderDto&gt;) — без этих
/// атрибутов System.Text.Json сериализовал бы только то, что объявлено здесь,
/// теряя специфичные для наследника поля. Дискриминатор — системное поле "$type"
/// со значением "ApprovalOverdue"/"ProbationEnding".
/// </remarks>
[JsonPolymorphic]
[JsonDerivedType(typeof(ApprovalOverdueReminderDto), nameof(ReminderCategory.ApprovalOverdue))]
[JsonDerivedType(typeof(ProbationEndingReminderDto), nameof(ReminderCategory.ProbationEnding))]
public abstract record ReminderDto(
ReminderCategory Category,
string Url,
int RequestTypeId,
string RequestTypeName,
int RequestId,
string Status,
string? RequestUniqueCode);
[JsonDerivedType(typeof(ApprovalOverdueReminderDto), "ApprovalOverdue")]
[JsonDerivedType(typeof(ProbationEndingReminderDto), "ProbationEnding")]
public abstract record ReminderDto(string ReceiverEmail, string? ReceiverName, string Url);