feat: добавил получение person id сотрудников
All checks were successful
Create and publish a Docker image / Publish image (push) Successful in 1m45s
All checks were successful
Create and publish a Docker image / Publish image (push) Successful in 1m45s
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace ModeusSchedule.Abstractions.DTO;
|
namespace ModeusSchedule.Abstractions.DTO.Requests;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DTO для запроса расписания в Modeus.
|
/// DTO для запроса расписания в Modeus.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SfeduSchedule.DTO.Requests;
|
||||||
|
|
||||||
|
public record ModeusSearchPersonRequest(
|
||||||
|
[property: JsonPropertyName("fullName")]
|
||||||
|
string FullName = "",
|
||||||
|
|
||||||
|
[property: JsonPropertyName("sort")]
|
||||||
|
string Sort = "+fullName",
|
||||||
|
|
||||||
|
[property: JsonPropertyName("size")]
|
||||||
|
int Size = 10,
|
||||||
|
|
||||||
|
[property: JsonPropertyName("page")]
|
||||||
|
int Page = 0
|
||||||
|
);
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// Auto-generated by https://json2csharp.com/
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SfeduSchedule.DTO.Responses;
|
||||||
|
|
||||||
|
// SearchEmployeesResponse myDeserializedClass = JsonSerializer.Deserialize<SearchEmployeesResponse>(myJsonResponse);
|
||||||
|
|
||||||
|
public record Embedded(
|
||||||
|
[property: JsonPropertyName("persons")]
|
||||||
|
IReadOnlyList<Person> Persons,
|
||||||
|
[property: JsonPropertyName("employees")]
|
||||||
|
IReadOnlyList<Employee> Employees,
|
||||||
|
[property: JsonPropertyName("students")]
|
||||||
|
IReadOnlyList<Student> Students
|
||||||
|
);
|
||||||
|
|
||||||
|
public record Employee(
|
||||||
|
[property: JsonPropertyName("id")] string Id,
|
||||||
|
[property: JsonPropertyName("personId")]
|
||||||
|
string PersonId,
|
||||||
|
[property: JsonPropertyName("groupId")]
|
||||||
|
string GroupId,
|
||||||
|
[property: JsonPropertyName("groupName")]
|
||||||
|
string GroupName,
|
||||||
|
[property: JsonPropertyName("dateIn")] string DateIn,
|
||||||
|
[property: JsonPropertyName("dateOut")]
|
||||||
|
string DateOut
|
||||||
|
);
|
||||||
|
|
||||||
|
public record Links(
|
||||||
|
[property: JsonPropertyName("self")] Self Self
|
||||||
|
);
|
||||||
|
|
||||||
|
public record Page(
|
||||||
|
[property: JsonPropertyName("size")] int? Size,
|
||||||
|
[property: JsonPropertyName("totalElements")]
|
||||||
|
int? TotalElements,
|
||||||
|
[property: JsonPropertyName("totalPages")]
|
||||||
|
int? TotalPages,
|
||||||
|
[property: JsonPropertyName("number")] int? Number
|
||||||
|
);
|
||||||
|
|
||||||
|
public record Person(
|
||||||
|
[property: JsonPropertyName("lastName")]
|
||||||
|
string LastName,
|
||||||
|
[property: JsonPropertyName("firstName")]
|
||||||
|
string FirstName,
|
||||||
|
[property: JsonPropertyName("middleName")]
|
||||||
|
string MiddleName,
|
||||||
|
[property: JsonPropertyName("fullName")]
|
||||||
|
string FullName,
|
||||||
|
[property: JsonPropertyName("_links")] Links Links,
|
||||||
|
[property: JsonPropertyName("id")] string Id
|
||||||
|
);
|
||||||
|
|
||||||
|
public record ModeusSearchPersonResponse(
|
||||||
|
[property: JsonPropertyName("_embedded")]
|
||||||
|
Embedded Embedded,
|
||||||
|
[property: JsonPropertyName("page")] Page Page
|
||||||
|
);
|
||||||
|
|
||||||
|
public record Self(
|
||||||
|
[property: JsonPropertyName("href")] string Href
|
||||||
|
);
|
||||||
|
|
||||||
|
public record Student(
|
||||||
|
[property: JsonPropertyName("id")] string Id,
|
||||||
|
[property: JsonPropertyName("personId")]
|
||||||
|
string PersonId,
|
||||||
|
[property: JsonPropertyName("flowId")] string FlowId,
|
||||||
|
[property: JsonPropertyName("flowCode")]
|
||||||
|
string FlowCode,
|
||||||
|
[property: JsonPropertyName("specialtyCode")]
|
||||||
|
string SpecialtyCode,
|
||||||
|
[property: JsonPropertyName("specialtyName")]
|
||||||
|
string SpecialtyName,
|
||||||
|
[property: JsonPropertyName("specialtyProfile")]
|
||||||
|
string SpecialtyProfile,
|
||||||
|
[property: JsonPropertyName("learningStartDate")]
|
||||||
|
DateTime? LearningStartDate,
|
||||||
|
[property: JsonPropertyName("learningEndDate")]
|
||||||
|
DateTime? LearningEndDate
|
||||||
|
);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SfeduSchedule.DTO.Responses;
|
||||||
|
|
||||||
|
public record SearchPersonResponse(
|
||||||
|
IReadOnlyList<SearchPerson> Persons
|
||||||
|
);
|
||||||
|
|
||||||
|
public record SearchPerson(
|
||||||
|
[property: JsonPropertyName("name")]
|
||||||
|
string Name,
|
||||||
|
[property: JsonPropertyName("person_id")]
|
||||||
|
string PersonId
|
||||||
|
);
|
||||||
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using ModeusSchedule.Abstractions;
|
using ModeusSchedule.Abstractions;
|
||||||
using ModeusSchedule.Abstractions.DTO;
|
using ModeusSchedule.Abstractions.DTO;
|
||||||
|
using ModeusSchedule.Abstractions.DTO.Requests;
|
||||||
using SfeduSchedule.Services;
|
using SfeduSchedule.Services;
|
||||||
|
|
||||||
namespace SfeduSchedule.Controllers;
|
namespace SfeduSchedule.Controllers;
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using ModeusSchedule.Abstractions.DTO;
|
using ModeusSchedule.Abstractions.DTO;
|
||||||
|
using ModeusSchedule.Abstractions.DTO.Requests;
|
||||||
|
using SfeduSchedule.DTO.Responses;
|
||||||
using SfeduSchedule.Services;
|
using SfeduSchedule.Services;
|
||||||
|
|
||||||
namespace SfeduSchedule.Controllers;
|
namespace SfeduSchedule.Controllers;
|
||||||
@@ -10,13 +13,13 @@ namespace SfeduSchedule.Controllers;
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/schedule")]
|
[Route("api/schedule")]
|
||||||
[EnableRateLimiting("throttle")]
|
[EnableRateLimiting("throttle")]
|
||||||
public class ScheduleController(ModeusService modeusService, ILogger<ScheduleController> logger) : ControllerBase
|
public class ScheduleController(ModeusService modeusService, ModeusEmployeeService modeusEmployeeService, ILogger<ScheduleController> logger) : ControllerBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить GUID пользователя по полному имени. (требуется авторизация)
|
/// Получить GUID пользователя по полному имени. (включая студентов, требуется авторизация)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fullname">Полное имя пользователя.</param>
|
/// <param name="fullname">Полное имя пользователя.</param>
|
||||||
/// <returns>GUID пользователя.</returns>
|
/// <returns>GUID пользователя</returns>
|
||||||
/// <response code="200">Возвращает GUID пользователя</response>
|
/// <response code="200">Возвращает GUID пользователя</response>
|
||||||
/// <response code="404">Пользователь не найден</response>
|
/// <response code="404">Пользователь не найден</response>
|
||||||
/// <response code="401">Неавторизованный</response>
|
/// <response code="401">Неавторизованный</response>
|
||||||
@@ -33,6 +36,29 @@ public class ScheduleController(ModeusService modeusService, ILogger<ScheduleCon
|
|||||||
return Ok(guid);
|
return Ok(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Поиск сотрудников по имени. (преподавателей)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullname">ФИО Сотрудника</param>
|
||||||
|
/// <returns>Список сотрудников (до 10 записей)</returns>
|
||||||
|
/// <response code="200">Возвращает список сотрудников с их GUID</response>
|
||||||
|
/// <response code="404">Сотрудник не найден</response>
|
||||||
|
/// <response code="401">Неавторизованный</response>
|
||||||
|
[HttpGet]
|
||||||
|
[Route("searchemployee")]
|
||||||
|
public async Task<IActionResult> SearchEmployees([Required][MinLength(1)] string fullname)
|
||||||
|
{
|
||||||
|
var employees = await modeusEmployeeService.GetEmployees(fullname, 10);
|
||||||
|
if (employees.Count == 0)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var persons = new List<SearchPerson>(employees.Count);
|
||||||
|
foreach (var employee in employees)
|
||||||
|
persons.Add(new SearchPerson(Name: employee.Key, PersonId: employee.Value.Item1));
|
||||||
|
|
||||||
|
return Ok(new SearchPersonResponse(Persons: persons).Persons);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить расписание в формате ICS по пользовательскому запросу.
|
/// Получить расписание в формате ICS по пользовательскому запросу.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ builder.Services.AddHttpClient("modeus", client =>
|
|||||||
client.BaseAddress = new Uri(configuration["MODEUS_URL"]!);
|
client.BaseAddress = new Uri(configuration["MODEUS_URL"]!);
|
||||||
});
|
});
|
||||||
builder.Services.AddSingleton<ModeusHttpClient>();
|
builder.Services.AddSingleton<ModeusHttpClient>();
|
||||||
|
builder.Services.AddHostedService<ModeusEmployeeService>();
|
||||||
builder.Services.AddSingleton<ModeusService>();
|
builder.Services.AddSingleton<ModeusService>();
|
||||||
builder.Services.AddHttpClient("authClient");
|
builder.Services.AddHttpClient("authClient");
|
||||||
|
|
||||||
|
|||||||
75
SfeduSchedule/Services/ModeusEmployeeService.cs
Normal file
75
SfeduSchedule/Services/ModeusEmployeeService.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using SfeduSchedule.Logging;
|
||||||
|
|
||||||
|
namespace SfeduSchedule.Services;
|
||||||
|
|
||||||
|
public class ModeusEmployeeService(ILogger<ModeusEmployeeService> logger, ModeusService modeusService)
|
||||||
|
: IHostedService
|
||||||
|
{
|
||||||
|
private Dictionary<string, (string, List<string>)> _employees = [];
|
||||||
|
private Task? _backgroundTask;
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, (string, List<string>)>> GetEmployees(string fullname, int size = 10)
|
||||||
|
{
|
||||||
|
return _employees
|
||||||
|
.Where(e => e.Key.Contains(fullname, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Take(size)
|
||||||
|
.ToDictionary(e => e.Key, e => e.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||||
|
_backgroundTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(15), _cts.Token);
|
||||||
|
|
||||||
|
var employees = await modeusService.GetEmployeesAsync();
|
||||||
|
if (employees.Count == 0)
|
||||||
|
{
|
||||||
|
logger.LogWarningHere("Не удалось получить список сотрудников из Modeus.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_employees = employees;
|
||||||
|
logger.LogInformationHere($"Получено {employees.Count} сотрудников из Modeus.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5), _cts.Token);
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogErrorHere(ex, "Ошибка при загрузке сотрудников из Modeus.");
|
||||||
|
}
|
||||||
|
}, _cts.Token);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (_cts is null || _backgroundTask is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cts.Cancel();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.WhenAny(_backgroundTask, Task.Delay(TimeSpan.FromSeconds(5), cancellationToken));
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using ModeusSchedule.Abstractions;
|
using ModeusSchedule.Abstractions;
|
||||||
using ModeusSchedule.Abstractions.DTO;
|
using ModeusSchedule.Abstractions.DTO;
|
||||||
|
using ModeusSchedule.Abstractions.DTO.Requests;
|
||||||
|
using SfeduSchedule.DTO.Requests;
|
||||||
|
using SfeduSchedule.DTO.Responses;
|
||||||
using SfeduSchedule.Logging;
|
using SfeduSchedule.Logging;
|
||||||
|
|
||||||
namespace SfeduSchedule.Services;
|
namespace SfeduSchedule.Services;
|
||||||
@@ -67,7 +71,44 @@ public class ModeusHttpClient
|
|||||||
_logger.LogErrorHere(ex, "Deserialization failed.");
|
_logger.LogErrorHere(ex, "Deserialization failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new List<Attendees>();
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ModeusSearchPersonResponse?> SearchPersonAsync(ModeusSearchPersonRequest modeusSearchPersonRequest)
|
||||||
|
{
|
||||||
|
using var request = new HttpRequestMessage(HttpMethod.Post,
|
||||||
|
$"schedule-calendar-v2/api/people/persons/search");
|
||||||
|
request.Content = new StringContent(
|
||||||
|
JsonSerializer.Serialize(modeusSearchPersonRequest, GlobalConsts.JsonSerializerOptions),
|
||||||
|
Encoding.UTF8, "application/json");
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
var requestMs = stopwatch.ElapsedMilliseconds;
|
||||||
|
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
_logger.LogErrorHere($"Неуспешный статус при получении расписания: {response.StatusCode}, Поле ФИО: \"{modeusSearchPersonRequest.FullName}\"");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var deserializeStartMs = stopwatch.ElapsedMilliseconds;
|
||||||
|
await using var contentStream = await response.Content.ReadAsStreamAsync();
|
||||||
|
var content = await JsonSerializer.DeserializeAsync<ModeusSearchPersonResponse>(
|
||||||
|
contentStream,
|
||||||
|
GlobalConsts.JsonSerializerOptions);
|
||||||
|
var groupMs = stopwatch.ElapsedMilliseconds - deserializeStartMs;
|
||||||
|
|
||||||
|
_logger.LogInformationHere($"SearchPersonAsync: Request time: {requestMs} ms, Deserialization time: {groupMs} ms, Total time: {stopwatch.ElapsedMilliseconds} ms.");
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogErrorHere(ex, "SearchPersonAsync: Deserialization failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> SearchRoomsAsync(RoomSearchRequest requestDto)
|
public async Task<string?> SearchRoomsAsync(RoomSearchRequest requestDto)
|
||||||
@@ -85,43 +126,4 @@ public class ModeusHttpClient
|
|||||||
return await response.Content.ReadAsStringAsync();
|
return await response.Content.ReadAsStringAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> GetGuidAsync(string fullName)
|
|
||||||
{
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "schedule-calendar-v2/api/people/persons/search");
|
|
||||||
request.Content = new StringContent(JsonSerializer.Serialize(new
|
|
||||||
{
|
|
||||||
fullName,
|
|
||||||
sort = "+fullName",
|
|
||||||
size = 10,
|
|
||||||
page = 0
|
|
||||||
}), Encoding.UTF8, "application/json");
|
|
||||||
var response = await _httpClient.SendAsync(request);
|
|
||||||
|
|
||||||
_logger.LogInformationHere($"Ответ получен: {response.StatusCode}");
|
|
||||||
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
|
||||||
{
|
|
||||||
_logger.LogErrorHere($"Неуспешный статус при получении расписания: {response.StatusCode}, Name: {fullName}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
string? personId;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
personId = JsonDocument.Parse(json).RootElement
|
|
||||||
.GetProperty("_embedded")
|
|
||||||
.GetProperty("persons")[0]
|
|
||||||
.GetProperty("id")
|
|
||||||
.GetString();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_logger.LogWarningHere($"Не удалось получить идентификатор пользователя. FullName={fullName}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return personId;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Ical.Net;
|
using Ical.Net;
|
||||||
using Ical.Net.CalendarComponents;
|
using Ical.Net.CalendarComponents;
|
||||||
@@ -5,33 +6,25 @@ using Ical.Net.DataTypes;
|
|||||||
using Ical.Net.Serialization;
|
using Ical.Net.Serialization;
|
||||||
using ModeusSchedule.Abstractions;
|
using ModeusSchedule.Abstractions;
|
||||||
using ModeusSchedule.Abstractions.DTO;
|
using ModeusSchedule.Abstractions.DTO;
|
||||||
|
using ModeusSchedule.Abstractions.DTO.Requests;
|
||||||
|
using SfeduSchedule.DTO.Requests;
|
||||||
|
using SfeduSchedule.DTO.Responses;
|
||||||
using SfeduSchedule.Logging;
|
using SfeduSchedule.Logging;
|
||||||
|
|
||||||
namespace SfeduSchedule.Services;
|
namespace SfeduSchedule.Services;
|
||||||
|
|
||||||
public class ModeusService
|
public class ModeusService(
|
||||||
{
|
|
||||||
private readonly IConfiguration _configuration;
|
|
||||||
|
|
||||||
private readonly ILogger<ModeusService> _logger;
|
|
||||||
private readonly ModeusHttpClient _modeusHttpClient;
|
|
||||||
|
|
||||||
public ModeusService(
|
|
||||||
ILogger<ModeusService> logger,
|
ILogger<ModeusService> logger,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
ModeusHttpClient modeusHttpClient)
|
ModeusHttpClient modeusHttpClient)
|
||||||
{
|
{
|
||||||
_modeusHttpClient = modeusHttpClient;
|
|
||||||
_logger = logger;
|
|
||||||
_configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Schedule?> GetScheduleJsonAsync(ModeusScheduleRequest msr)
|
public async Task<Schedule?> GetScheduleJsonAsync(ModeusScheduleRequest msr)
|
||||||
{
|
{
|
||||||
var schedule = await GetScheduleAsync(msr);
|
var schedule = await GetScheduleAsync(msr);
|
||||||
if (schedule == null)
|
if (schedule == null)
|
||||||
{
|
{
|
||||||
_logger.LogErrorHere($"schedule is null. {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
logger.LogErrorHere(
|
||||||
|
$"schedule is null. {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,16 +35,20 @@ public class ModeusService
|
|||||||
switch (scheduleJson)
|
switch (scheduleJson)
|
||||||
{
|
{
|
||||||
case null:
|
case null:
|
||||||
_logger.LogErrorHere($"scheduleJson is null. Schedule: {schedule}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
logger.LogErrorHere(
|
||||||
|
$"scheduleJson is null. Schedule: {schedule}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||||
break;
|
break;
|
||||||
case { Embedded: null }:
|
case { Embedded: null }:
|
||||||
_logger.LogErrorHere($"scheduleJson.Embedded is null. Response: {schedule}\nscheduleJson: {scheduleJson}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
logger.LogErrorHere(
|
||||||
|
$"scheduleJson.Embedded is null. Response: {schedule}\nscheduleJson: {scheduleJson}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||||
break;
|
break;
|
||||||
case { Embedded.Events: null }:
|
case { Embedded.Events: null }:
|
||||||
_logger.LogErrorHere($"scheduleJson.Embedded.Events is null. Response: {schedule}\nscheduleJson: {scheduleJson}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
logger.LogErrorHere(
|
||||||
|
$"scheduleJson.Embedded.Events is null. Response: {schedule}\nscheduleJson: {scheduleJson}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||||
break;
|
break;
|
||||||
case { Embedded.Events.Length: 0 }:
|
case { Embedded.Events.Length: 0 }:
|
||||||
_logger.LogWarningHere($"scheduleJson.Embedded.Events is empty. Embedded: {scheduleJson.Embedded}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
logger.LogWarningHere(
|
||||||
|
$"scheduleJson.Embedded.Events is empty. Embedded: {scheduleJson.Embedded}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return scheduleJson;
|
return scheduleJson;
|
||||||
@@ -59,7 +56,8 @@ public class ModeusService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogErrorHere($"Deserialization failed. Schedule: {schedule}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}\n Exception: {ex}");
|
logger.LogErrorHere(
|
||||||
|
$"Deserialization failed. Schedule: {schedule}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}\n Exception: {ex}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -70,12 +68,13 @@ public class ModeusService
|
|||||||
var scheduleJson = await GetScheduleJsonAsync(msr);
|
var scheduleJson = await GetScheduleJsonAsync(msr);
|
||||||
if (scheduleJson == null)
|
if (scheduleJson == null)
|
||||||
{
|
{
|
||||||
_logger.LogErrorHere($"scheduleJson is null after deserialization. Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
logger.LogErrorHere(
|
||||||
|
$"scheduleJson is null after deserialization. Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var calendar = new Calendar();
|
var calendar = new Calendar();
|
||||||
calendar.AddTimeZone(new VTimeZone(_configuration["TZ"]!));
|
calendar.AddTimeZone(new VTimeZone(configuration["TZ"]!));
|
||||||
|
|
||||||
foreach (var e in scheduleJson.Embedded.Events)
|
foreach (var e in scheduleJson.Embedded.Events)
|
||||||
{
|
{
|
||||||
@@ -176,37 +175,150 @@ public class ModeusService
|
|||||||
Summary = (string.IsNullOrEmpty(shortNameCourse) ? "" : shortNameCourse + " / ") + e.Name,
|
Summary = (string.IsNullOrEmpty(shortNameCourse) ? "" : shortNameCourse + " / ") + e.Name,
|
||||||
Description = e.NameShort + (string.IsNullOrEmpty(roomName) ? "" : $"\nАудитория: {roomName}") +
|
Description = e.NameShort + (string.IsNullOrEmpty(roomName) ? "" : $"\nАудитория: {roomName}") +
|
||||||
(string.IsNullOrEmpty(teachersNames) ? "" : $"\nПреподаватели: {teachersNames}"),
|
(string.IsNullOrEmpty(teachersNames) ? "" : $"\nПреподаватели: {teachersNames}"),
|
||||||
Start = new CalDateTime(e.StartsAtLocal, _configuration["TZ"]!),
|
Start = new CalDateTime(e.StartsAtLocal, configuration["TZ"]!),
|
||||||
End = new CalDateTime(e.EndsAtLocal, _configuration["TZ"]!)
|
End = new CalDateTime(e.EndsAtLocal, configuration["TZ"]!)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var serializer = new CalendarSerializer();
|
var serializer = new CalendarSerializer();
|
||||||
var serializedCalendar = serializer.SerializeToString(calendar);
|
var serializedCalendar = serializer.SerializeToString(calendar);
|
||||||
_logger.LogInformationHere($"serialized calendar created. Length: {serializedCalendar?.Length ?? 0}");
|
logger.LogInformationHere($"serialized calendar created. Length: {serializedCalendar?.Length ?? 0}");
|
||||||
return serializedCalendar;
|
return serializedCalendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, (string, List<string>)>> GetEmployeesAsync()
|
||||||
|
{
|
||||||
|
var searchPersonResponse =
|
||||||
|
await modeusHttpClient.SearchPersonAsync(new ModeusSearchPersonRequest() { Size = 38000 });
|
||||||
|
if (searchPersonResponse == null)
|
||||||
|
{
|
||||||
|
logger.LogErrorHere("persons is null");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var persons = searchPersonResponse.Embedded.Persons;
|
||||||
|
var employees = searchPersonResponse.Embedded.Employees;
|
||||||
|
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
var personsById = new Dictionary<string, SfeduSchedule.DTO.Responses.Person>();
|
||||||
|
foreach (var p in persons)
|
||||||
|
{
|
||||||
|
var id = p?.Id;
|
||||||
|
if (!string.IsNullOrEmpty(id))
|
||||||
|
{
|
||||||
|
personsById[id] = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapMs = stopwatch.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
var groupStartMs = stopwatch.ElapsedMilliseconds;
|
||||||
|
var grouped =
|
||||||
|
new Dictionary<string, (string PersonId, List<string> Positions)>(employees.Count,
|
||||||
|
StringComparer.Ordinal);
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var e in employees)
|
||||||
|
{
|
||||||
|
if (e == null) continue;
|
||||||
|
var personId = e.PersonId ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(personId)) continue;
|
||||||
|
|
||||||
|
var fullName = personId;
|
||||||
|
if (personsById.TryGetValue(personId, out var person))
|
||||||
|
{
|
||||||
|
var name = (person.FullName ?? string.Empty).Trim();
|
||||||
|
if (!string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
fullName = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = (e.GroupName ?? string.Empty).Trim();
|
||||||
|
|
||||||
|
static string FormatDateRange(string? dateIn, string? dateOut)
|
||||||
|
{
|
||||||
|
var start = (dateIn ?? string.Empty).Trim();
|
||||||
|
if (string.IsNullOrEmpty(start)) start = "?";
|
||||||
|
|
||||||
|
var end = (dateOut ?? string.Empty).Trim();
|
||||||
|
if (string.IsNullOrEmpty(end)) end = "по наст.вр.";
|
||||||
|
|
||||||
|
return $"{start}–{end}";
|
||||||
|
}
|
||||||
|
|
||||||
|
var dateRange = FormatDateRange(e.DateIn, e.DateOut);
|
||||||
|
var positionWithDates = string.IsNullOrEmpty(position) ? $"({dateRange})" : $"{position} ({dateRange})";
|
||||||
|
|
||||||
|
if (!grouped.TryGetValue(fullName, out var entry))
|
||||||
|
{
|
||||||
|
entry = (personId, new List<string>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(entry.PersonId) || entry.PersonId == personId)
|
||||||
|
{
|
||||||
|
entry.PersonId = personId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry.Positions.Contains(positionWithDates))
|
||||||
|
{
|
||||||
|
entry.Positions.Add(positionWithDates);
|
||||||
|
}
|
||||||
|
|
||||||
|
grouped[fullName] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupMs = stopwatch.ElapsedMilliseconds - groupStartMs;
|
||||||
|
var totalMs = stopwatch.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
logger.LogInformationHere(
|
||||||
|
$"GetEmployeesAsync timing: mapPersons={mapMs}ms, groupEmployees={groupMs}ms, total={totalMs}ms");
|
||||||
|
|
||||||
|
return grouped;
|
||||||
|
}
|
||||||
|
|
||||||
#region Проксирование методов из ModeusHttpClient
|
#region Проксирование методов из ModeusHttpClient
|
||||||
|
|
||||||
public async Task<string?> SearchRoomsAsync(RoomSearchRequest request)
|
public async Task<string?> SearchRoomsAsync(RoomSearchRequest request)
|
||||||
{
|
{
|
||||||
return await _modeusHttpClient.SearchRoomsAsync(request);
|
return await modeusHttpClient.SearchRoomsAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> GetScheduleAsync(ModeusScheduleRequest msr)
|
public async Task<string?> GetScheduleAsync(ModeusScheduleRequest msr)
|
||||||
{
|
{
|
||||||
return await _modeusHttpClient.GetScheduleAsync(msr);
|
return await modeusHttpClient.GetScheduleAsync(msr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> GetGuidAsync(string fullname)
|
public async Task<string?> GetGuidAsync(string fullname)
|
||||||
{
|
{
|
||||||
return await _modeusHttpClient.GetGuidAsync(fullname);
|
var searchPersonResponse = await modeusHttpClient.SearchPersonAsync(new ModeusSearchPersonRequest
|
||||||
|
{ FullName = fullname, Page = 0, Size = 10, Sort = "+fullName" });
|
||||||
|
if (searchPersonResponse == null)
|
||||||
|
{
|
||||||
|
logger.LogErrorHere($"Не удалось получить ответ от Modeus при поиске пользователя. FullName={fullname}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? personId;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
personId = searchPersonResponse.Embedded.Persons[0].Id;
|
||||||
|
return personId;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
logger.LogWarningHere(
|
||||||
|
$"Не удалось получить идентификатор пользователя. FullName={fullname}. Ответ Modeus: {JsonSerializer.Serialize(searchPersonResponse, GlobalConsts.JsonSerializerOptions)}. Exception: {exception}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Attendees>> GetAttendeesAsync(Guid eventId)
|
public async Task<List<Attendees>> GetAttendeesAsync(Guid eventId)
|
||||||
{
|
{
|
||||||
return await _modeusHttpClient.GetAttendeesAsync(eventId);
|
return await modeusHttpClient.GetAttendeesAsync(eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
Reference in New Issue
Block a user