using System.Net; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using Tnsu.ApprovalRoute.Domain.Enums; namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal; public sealed class HttpTnsuPortalClient( HttpClient httpClient, TnsuPortalAuthorizer authorizer) : ITnsuPortalClient { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true }; public async Task> GetEmployeeByEmailAsync(string email, CancellationToken cancellationToken) { var employees = await GetAsync>( $"/Employee/ByEmail?email={Uri.EscapeDataString(email)}", cancellationToken); if (employees is null) return []; return employees .Select(e => new EmployeeDirectoryInfo(e.CompanyId ?? Guid.Empty, e.DepartmentId, e.Company ?? string.Empty, e.Position, e.Id)) .ToList(); } public async Task GetEmployeeByPositionAsync(string position, string company, CancellationToken cancellationToken) { var employee = await GetAsync( $"/Employee/ByPosition?position={Uri.EscapeDataString(position)}&company={Uri.EscapeDataString(company)}", cancellationToken); return new EmployeeLookupResult(employee is not null, employee?.Email, employee?.Fio); } public async Task> GetAllEmployeesAsync(CancellationToken cancellationToken) { var employees = await GetAsync>("/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, e.Position)) .ToList(); } // /Dictionary/* отдаёт «голый» массив с ключами в PascalCase (Id/Name) — // PropertyNameCaseInsensitive в JsonOptions это покрывает. public async Task> GetCompaniesAsync(CancellationToken cancellationToken) { var companies = await GetAsync>("/Dictionary/Companies", cancellationToken); return (companies ?? []) .Where(c => c.Id is not null && !string.IsNullOrWhiteSpace(c.Name)) .Select(c => new CompanyInfo(c.Id!.Value, c.Name!)) .ToList(); } public async Task> GetDepartmentsAsync(CancellationToken cancellationToken) { var departments = await GetAsync>("/Dictionary/Departments", cancellationToken); return (departments ?? []) .Where(d => d.Id is not null && !string.IsNullOrWhiteSpace(d.Name)) .Select(d => new DepartmentInfo(d.Id!.Value, d.Name!)) .ToList(); } public async Task GetManagerAsync(string email, string managerType, CancellationToken cancellationToken) { var manager = await GetAsync( $"/Employee/Manager?email={Uri.EscapeDataString(email)}&managerType={Uri.EscapeDataString(managerType)}", cancellationToken); return manager?.ManagerEmail; } public async Task> GetFullBaseAsync(CancellationToken cancellationToken) { var employees = await GetAsync>("/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> GetProjectExecutorsAsync(Guid projectId, CancellationToken cancellationToken) { var project = await GetAsync($"/Project/{projectId}", cancellationToken); if (project is null) return []; var executors = new List(); AddIfPresent(executors, ProjectRole.ProjectManager, project.ProjectManager); AddIfPresent(executors, ProjectRole.Planner, project.Planner); AddIfPresent(executors, ProjectRole.ManagerHSE, project.ManagerHSE); AddIfPresent(executors, ProjectRole.Security, project.Security); AddIfPresent(executors, ProjectRole.Keeper, project.Keeper); return executors; } // Ответ — {"managerEmail": "..."} (та же форма, что у /Employee/Manager), // а НЕ форма сотрудника (EmployeeResponse) — проверено живым вызовом // (GET /Employee/DepartmentHead?departmentId=... на отделе "АУП"/"ТОО // Монтажник-Т" вернул именно managerEmail). 404 с телом {"error": "..."} // на отделах без руководителя уже отфильтровывается общим GetAsync. public async Task GetDepartmentHeadEmailAsync(Guid departmentId, CancellationToken cancellationToken) { var manager = await GetAsync( $"/Employee/DepartmentHead?departmentId={Uri.EscapeDataString(departmentId.ToString())}", cancellationToken); return manager?.ManagerEmail; } private async Task GetAsync(string requestUri, CancellationToken cancellationToken) where T : class { using var request = new HttpRequestMessage(HttpMethod.Get, requestUri); await authorizer.AuthorizeAsync(request, cancellationToken); using var response = await httpClient.SendAsync(request, cancellationToken); if (response.StatusCode == HttpStatusCode.NotFound) return null; response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync(JsonOptions, cancellationToken); } private static void AddIfPresent(List executors, ProjectRole role, string? email) { if (!string.IsNullOrWhiteSpace(email)) executors.Add(new ProjectExecutorInfo(role, email)); } private sealed record EmployeeResponse( [property: JsonPropertyName("email")] string? Email, [property: JsonPropertyName("company")] string? Company, [property: JsonPropertyName("companyId")] Guid? CompanyId, [property: JsonPropertyName("departmentId")] Guid? DepartmentId, [property: JsonPropertyName("position")] string? Position, [property: JsonPropertyName("fio")] string? Fio, [property: JsonPropertyName("id")] Guid? Id = null); private sealed record DictionaryItemResponse( [property: JsonPropertyName("id")] Guid? Id, [property: JsonPropertyName("name")] string? Name); 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, [property: JsonPropertyName("managerHSE")] string? ManagerHSE, [property: JsonPropertyName("security")] string? Security, [property: JsonPropertyName("keeper")] string? Keeper); }