using Tnsu.ApprovalRoute.Infrastructure.TnsuPortal; namespace Tnsu.ApprovalRoute.Application.Reminders; internal enum EmploymentStatus { /// Сотрудника с таким ФИО в справочнике нет вовсе — трудоустройство не подтверждено. NotFound, /// Работает хотя бы в одной компании. Employed, /// Есть в справочнике, но уволен из всех компаний, где числился. Dismissed, } /// /// Оба справочника api.tnsu.kz разом, разобранные по ФИО: дата увольнения есть /// только в /Employee/FullBase, а email — только в /Employee/All, поэтому нужны /// оба. Ищем ПО ФИО, потому что точечного поиска по нему у api.tnsu.kz нет /// (эндпоинта /Employee/ByFio не существует), а в заявке на приём кандидат /// записан именно ФИО (колонка CandidateName) — временное решение, см. /// . /// /// ФИО не уникально: один человек может числиться в нескольких компаниях (тогда /// это несколько записей одного и того же человека), но с тем же успехом могут /// совпасть ФИО двух разных людей — различить эти два случая по одному ФИО нельзя, /// поэтому все совпадения трактуются как записи одного человека. /// internal sealed class EmployeeDirectorySnapshot { private readonly ILookup _fullBaseByFio; private readonly ILookup _contactsByFio; public EmployeeDirectorySnapshot( IReadOnlyList fullBase, IReadOnlyList contacts) { _fullBaseByFio = fullBase.ToLookup(e => Normalize(e.Fio)); _contactsByFio = contacts.ToLookup(e => Normalize(e.Fio)); } /// /// Уволенным считаем только того, у кого дата увольнения стоит во ВСЕХ его /// записях: сотрудник, уволенный из одной компании холдинга, но оставшийся в /// другой, продолжает работать — и напоминание ему по-прежнему нужно. /// public EmploymentStatus EmploymentOf(string fio) { var entries = _fullBaseByFio[Normalize(fio)].ToList(); if (entries.Count == 0) return EmploymentStatus.NotFound; return entries.All(e => e.DismissalDate is not null) ? EmploymentStatus.Dismissed : EmploymentStatus.Employed; } /// /// Первый непустой email среди записей этого ФИО. У одного человека записей /// несколько (по компании на каждую), а email у него один и тот же — но /// заполнен он может быть не в каждой записи. /// public string? EmailOf(string fio) => _contactsByFio[Normalize(fio)] .Select(e => e.Email) .FirstOrDefault(email => !string.IsNullOrWhiteSpace(email)); /// /// В справочнике и в заявке ФИО набирают руками, поэтому сравниваем без учёта /// регистра и лишних пробелов между словами (в живых данных встречаются и /// двойные пробелы, и неразрывный пробел — см. поле department в /Employee/All). /// private static string Normalize(string fio) => string.Join(' ', fio.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)).ToLowerInvariant(); }