75 lines
4.2 KiB
C#
75 lines
4.2 KiB
C#
using Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
||
|
||
namespace Tnsu.ApprovalRoute.Application.Reminders;
|
||
|
||
internal enum EmploymentStatus
|
||
{
|
||
/// <summary>Сотрудника с таким ФИО в справочнике нет вовсе — трудоустройство не подтверждено.</summary>
|
||
NotFound,
|
||
|
||
/// <summary>Работает хотя бы в одной компании.</summary>
|
||
Employed,
|
||
|
||
/// <summary>Есть в справочнике, но уволен из всех компаний, где числился.</summary>
|
||
Dismissed,
|
||
}
|
||
|
||
/// <summary>
|
||
/// Оба справочника api.tnsu.kz разом, разобранные по ФИО: дата увольнения есть
|
||
/// только в /Employee/FullBase, а email — только в /Employee/All, поэтому нужны
|
||
/// оба. Ищем ПО ФИО, потому что точечного поиска по нему у api.tnsu.kz нет
|
||
/// (эндпоинта /Employee/ByFio не существует), а в заявке на приём кандидат
|
||
/// записан именно ФИО (колонка CandidateName) — временное решение, см.
|
||
/// <see cref="ITnsuPortalClient.GetAllEmployeesAsync"/>.
|
||
///
|
||
/// ФИО не уникально: один человек может числиться в нескольких компаниях (тогда
|
||
/// это несколько записей одного и того же человека), но с тем же успехом могут
|
||
/// совпасть ФИО двух разных людей — различить эти два случая по одному ФИО нельзя,
|
||
/// поэтому все совпадения трактуются как записи одного человека.
|
||
/// </summary>
|
||
internal sealed class EmployeeDirectorySnapshot
|
||
{
|
||
private readonly ILookup<string, EmployeeFullBaseInfo> _fullBaseByFio;
|
||
private readonly ILookup<string, EmployeeContactInfo> _contactsByFio;
|
||
|
||
public EmployeeDirectorySnapshot(
|
||
IReadOnlyList<EmployeeFullBaseInfo> fullBase, IReadOnlyList<EmployeeContactInfo> contacts)
|
||
{
|
||
_fullBaseByFio = fullBase.ToLookup(e => Normalize(e.Fio));
|
||
_contactsByFio = contacts.ToLookup(e => Normalize(e.Fio));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Уволенным считаем только того, у кого дата увольнения стоит во ВСЕХ его
|
||
/// записях: сотрудник, уволенный из одной компании холдинга, но оставшийся в
|
||
/// другой, продолжает работать — и напоминание ему по-прежнему нужно.
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Первый непустой email среди записей этого ФИО. У одного человека записей
|
||
/// несколько (по компании на каждую), а email у него один и тот же — но
|
||
/// заполнен он может быть не в каждой записи.
|
||
/// </summary>
|
||
public string? EmailOf(string fio) =>
|
||
_contactsByFio[Normalize(fio)]
|
||
.Select(e => e.Email)
|
||
.FirstOrDefault(email => !string.IsNullOrWhiteSpace(email));
|
||
|
||
/// <summary>
|
||
/// В справочнике и в заявке ФИО набирают руками, поэтому сравниваем без учёта
|
||
/// регистра и лишних пробелов между словами (в живых данных встречаются и
|
||
/// двойные пробелы, и неразрывный пробел — см. поле department в /Employee/All).
|
||
/// </summary>
|
||
private static string Normalize(string fio) =>
|
||
string.Join(' ', fio.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)).ToLowerInvariant();
|
||
}
|