Расширение reminders (#19)
Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/19
This commit was merged in pull request #19.
This commit is contained in:
@@ -27,11 +27,21 @@ public sealed class CachingTnsuPortalClient(HttpTnsuPortalClient inner, IMemoryC
|
||||
$"emp:position:{position.ToLowerInvariant()}|{company.ToLowerInvariant()}",
|
||||
() => inner.GetEmployeeByPositionAsync(position, company, cancellationToken));
|
||||
|
||||
public Task<IReadOnlyList<EmployeeContactInfo>> GetAllEmployeesAsync(CancellationToken cancellationToken) =>
|
||||
GetOrAddAsync("emp:all", () => inner.GetAllEmployeesAsync(cancellationToken));
|
||||
|
||||
public Task<string?> GetManagerAsync(string email, string managerType, CancellationToken cancellationToken) =>
|
||||
GetOrAddAsync(
|
||||
$"mgr:{email.ToLowerInvariant()}|{managerType.ToLowerInvariant()}",
|
||||
() => inner.GetManagerAsync(email, managerType, cancellationToken));
|
||||
|
||||
// Базы целиком (/Employee/All выше и /Employee/FullBase ниже) кешируем по
|
||||
// фиксированному ключу — это не запросы «по чему-то», а единственные на весь
|
||||
// процесс снимки справочника; TTL тот же (5 минут), что и у остальных
|
||||
// обращений к api.tnsu.kz.
|
||||
public Task<IReadOnlyList<EmployeeFullBaseInfo>> GetFullBaseAsync(CancellationToken cancellationToken) =>
|
||||
GetOrAddAsync("emp:fullbase", () => inner.GetFullBaseAsync(cancellationToken));
|
||||
|
||||
public Task<IReadOnlyList<ProjectExecutorInfo>> GetProjectExecutorsAsync(Guid projectId, CancellationToken cancellationToken) =>
|
||||
GetOrAddAsync(
|
||||
$"projexec:{projectId}",
|
||||
|
||||
@@ -32,6 +32,18 @@ public sealed class HttpTnsuPortalClient(
|
||||
return new EmployeeLookupResult(employee is not null, employee?.Email, employee?.Fio);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<EmployeeContactInfo>> GetAllEmployeesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var employees = await GetAsync<List<EmployeeResponse>>("/Employee/All", cancellationToken);
|
||||
if (employees is null)
|
||||
return [];
|
||||
|
||||
return employees
|
||||
.Where(e => !string.IsNullOrWhiteSpace(e.Fio))
|
||||
.Select(e => new EmployeeContactInfo(e.Id, e.Fio!, e.Email, e.Company ?? string.Empty))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<string?> GetManagerAsync(string email, string managerType, CancellationToken cancellationToken)
|
||||
{
|
||||
var manager = await GetAsync<ManagerResponse>(
|
||||
@@ -40,6 +52,18 @@ public sealed class HttpTnsuPortalClient(
|
||||
return manager?.ManagerEmail;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<EmployeeFullBaseInfo>> GetFullBaseAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var employees = await GetAsync<List<FullBaseResponse>>("/Employee/FullBase", cancellationToken);
|
||||
if (employees is null)
|
||||
return [];
|
||||
|
||||
return employees
|
||||
.Where(e => !string.IsNullOrWhiteSpace(e.Fio))
|
||||
.Select(e => new EmployeeFullBaseInfo(e.Id, e.Fio!, e.Company ?? string.Empty, e.DismissalDate))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<ProjectExecutorInfo>> GetProjectExecutorsAsync(Guid projectId, CancellationToken cancellationToken)
|
||||
{
|
||||
var project = await GetAsync<ProjectResponse>($"/Project/{projectId}", cancellationToken);
|
||||
@@ -100,6 +124,12 @@ public sealed class HttpTnsuPortalClient(
|
||||
private sealed record ManagerResponse(
|
||||
[property: JsonPropertyName("managerEmail")] string? ManagerEmail);
|
||||
|
||||
private sealed record FullBaseResponse(
|
||||
[property: JsonPropertyName("id")] Guid? Id,
|
||||
[property: JsonPropertyName("fio")] string? Fio,
|
||||
[property: JsonPropertyName("company")] string? Company,
|
||||
[property: JsonPropertyName("dismissalDate")] DateTimeOffset? DismissalDate);
|
||||
|
||||
private sealed record ProjectResponse(
|
||||
[property: JsonPropertyName("projectManager")] string? ProjectManager,
|
||||
[property: JsonPropertyName("planner")] string? Planner,
|
||||
|
||||
@@ -11,8 +11,24 @@ public interface ITnsuPortalClient
|
||||
|
||||
Task<EmployeeLookupResult> GetEmployeeByPositionAsync(string position, string company, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Весь справочник сотрудников с email — /Employee/All. Нужен там, где известно
|
||||
/// только ФИО, а email ещё нет: точечного поиска по ФИО в api.tnsu.kz нет вовсе
|
||||
/// (эндпоинта /Employee/ByFio не существует), поэтому приходится тянуть базу
|
||||
/// целиком и искать в ней локально. ВРЕМЕННОЕ решение — как только на стороне
|
||||
/// api.tnsu.kz появится поиск по ФИО, этот вызов заменится точечным.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<EmployeeContactInfo>> GetAllEmployeesAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task<string?> GetManagerAsync(string email, string managerType, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Весь справочник сотрудников — /Employee/FullBase. Единственный источник, где
|
||||
/// есть дата увольнения (DismissalDate): нужен, чтобы не напоминать про
|
||||
/// испытательный срок сотрудника, который уже уволен.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<EmployeeFullBaseInfo>> GetFullBaseAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task<IReadOnlyList<ProjectExecutorInfo>> GetProjectExecutorsAsync(Guid projectId, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>Email руководителя указанного отдела (для HeadType.DepartmentHead) — null, если не найден.</summary>
|
||||
|
||||
@@ -17,3 +17,17 @@ public sealed record ProjectExecutorInfo(ProjectRole ProjectRole, string Email);
|
||||
|
||||
/// <summary>Найден ли сотрудник вообще — отдельно от того, заполнен ли у него email в портале (это разные проблемы).</summary>
|
||||
public sealed record EmployeeLookupResult(bool Found, string? Email, string? Fio);
|
||||
|
||||
/// <summary>
|
||||
/// Запись из /Employee/FullBase — весь справочник сотрудников, но БЕЗ email (в
|
||||
/// отличие от /Employee/All). Нужна только ради DismissalDate: единственное место,
|
||||
/// где отдаётся дата увольнения.
|
||||
/// </summary>
|
||||
public sealed record EmployeeFullBaseInfo(Guid? Id, string Fio, string Company, DateTimeOffset? DismissalDate);
|
||||
|
||||
/// <summary>
|
||||
/// Запись из /Employee/All — весь справочник сотрудников с email, но БЕЗ даты
|
||||
/// увольнения (в отличие от /Employee/FullBase). Связать одну базу с другой можно
|
||||
/// по <paramref name="Id"/> — он один и тот же в обоих ответах.
|
||||
/// </summary>
|
||||
public sealed record EmployeeContactInfo(Guid? Id, string Fio, string? Email, string Company);
|
||||
|
||||
Reference in New Issue
Block a user