diff --git a/PARR.API/Contracts/V1/ApiRoutes.cs b/PARR.API/Contracts/V1/ApiRoutes.cs index 4c2a5934..fa684666 100644 --- a/PARR.API/Contracts/V1/ApiRoutes.cs +++ b/PARR.API/Contracts/V1/ApiRoutes.cs @@ -258,6 +258,7 @@ public static class StatRobotSnapshot { + public const string GetAll = BaseStat + "/robot-snapshots/"; public const string Create = BaseStat + "/robot-snapshots/"; } diff --git a/PARR.API/Contracts/V1/Requests/Queries/StatRobotSnapshotQuery.cs b/PARR.API/Contracts/V1/Requests/Queries/StatRobotSnapshotQuery.cs new file mode 100644 index 00000000..df565d1a --- /dev/null +++ b/PARR.API/Contracts/V1/Requests/Queries/StatRobotSnapshotQuery.cs @@ -0,0 +1,15 @@ +namespace PARR.API.Contracts.V1.Requests.Queries +{ + public record StatRobotSnapshotQuery + { + /// + /// IP робота + /// + public string? Ip { get; init; } + + /// + /// Кол-во минут от текущего времени, по умолчнию 12 часов. + /// + public int Minutes { get; init; } = 12 * 60; + } +} diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatRobotSnapshotItemResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatRobotSnapshotItemResponse.cs index 33731241..4893d44b 100644 --- a/PARR.API/Contracts/V1/Responses/Statistics/StatRobotSnapshotItemResponse.cs +++ b/PARR.API/Contracts/V1/Responses/Statistics/StatRobotSnapshotItemResponse.cs @@ -1,6 +1,13 @@ namespace PARR.API.Contracts.V1.Responses.Statistics { public record StatRobotSnapshotItemResponse + { + public required UserBaseResponse Robot { get; init; } + + public List Snapshots { get; init; } = new(); + } + + public record StatRobotSnapshotResponse { public Guid Id { get; init; } @@ -11,7 +18,5 @@ public int ScheduleRobotsCount { get; init; } public int MaxRobots { get; init; } - - public required string Ip { get; init; } } } diff --git a/PARR.API/Controllers/V1/Statistics/StatRobotSnapshotController.cs b/PARR.API/Controllers/V1/Statistics/StatRobotSnapshotController.cs index 30baad32..7ef8d895 100644 --- a/PARR.API/Controllers/V1/Statistics/StatRobotSnapshotController.cs +++ b/PARR.API/Controllers/V1/Statistics/StatRobotSnapshotController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PARR.API.Contracts.V1; using PARR.API.Contracts.V1.Requests; +using PARR.API.Contracts.V1.Requests.Queries; using PARR.API.Contracts.V1.Responses.Base; using PARR.API.Contracts.V1.Responses.Statistics; using PARR.API.Controllers.V1.Base; @@ -13,7 +14,7 @@ using PARR.Domain.DTOs.RobotSnapshotDTO; namespace PARR.API.Controllers.V1.Statistics { - [Authorize(Roles = ParrRoles.Administrator.Role)] + [Authorize] public class StatRobotSnapshotController : BaseApiController { private readonly IRobotSnapshotService robotSnapshotService; @@ -31,6 +32,28 @@ namespace PARR.API.Controllers.V1.Statistics this.clientService = clientService; } + + /// + /// Получить снапшоты роботов. + /// + /// + [Authorize(Roles = ParrRoles.Administrator.Role)] + [HttpGet(ApiRoutes.StatRobotSnapshot.GetAll)] + public async Task GetAll([FromQuery] StatRobotSnapshotQuery query) + { + var queryDto = mapper.Map(query); + + var snapshots = await robotSnapshotService.GetAsync(queryDto); + + if (snapshots.Count == 0) + return NoContent(); + + var response = mapper.Map>(snapshots); + + return Ok(new Response>(response, true)); + } + + /// /// Отправить снапшот роботов. /// Доступ только с ролью "Робот ЕСПП" diff --git a/PARR.API/MappingProfiles/DomainToResponseProfile.cs b/PARR.API/MappingProfiles/DomainToResponseProfile.cs index 46672b21..73287145 100644 --- a/PARR.API/MappingProfiles/DomainToResponseProfile.cs +++ b/PARR.API/MappingProfiles/DomainToResponseProfile.cs @@ -9,6 +9,7 @@ using PARR.Domain.DTOs.RobotSnapshotDTO; using PARR.Domain.DTOs.RobotTask; using PARR.Domain.DTOs.Shortcode; using PARR.Domain.DTOs.TaskDTO; +using PARR.Domain.DTOs.User; using PARR.Domain.DTOs.Workload; using PARR.Domain.Entities; using PARR.Domain.Entities.Base.History; @@ -312,6 +313,8 @@ namespace PARR.API.MappingProfiles CreateMap(); + CreateMap(); + #endregion #region Order @@ -486,8 +489,13 @@ namespace PARR.API.MappingProfiles #endregion + #region RobotSnapshot - CreateMap(); + CreateMap(); + + CreateMap(); + + #endregion } } diff --git a/PARR.API/MappingProfiles/RequestToDomainProfile.cs b/PARR.API/MappingProfiles/RequestToDomainProfile.cs index 6a2c5b9b..1278a3ed 100644 --- a/PARR.API/MappingProfiles/RequestToDomainProfile.cs +++ b/PARR.API/MappingProfiles/RequestToDomainProfile.cs @@ -2,6 +2,7 @@ using PARR.API.Contracts.V1.Requests; using PARR.API.Contracts.V1.Requests.Queries; using PARR.Domain.Common.Pagination; +using PARR.Domain.DTOs.RobotSnapshotDTO; using PARR.Domain.Entities.Job; namespace PARR.API.MappingProfiles @@ -45,6 +46,8 @@ namespace PARR.API.MappingProfiles CreateMap(); #endregion + + CreateMap(); } } } diff --git a/PARR.Core/Infrastructure/Mapping/RobotSnapshot/RobotSnapshotMappingProfile.cs b/PARR.Core/Infrastructure/Mapping/RobotSnapshot/RobotSnapshotMappingProfile.cs index 56ebf523..81e547c6 100644 --- a/PARR.Core/Infrastructure/Mapping/RobotSnapshot/RobotSnapshotMappingProfile.cs +++ b/PARR.Core/Infrastructure/Mapping/RobotSnapshot/RobotSnapshotMappingProfile.cs @@ -7,7 +7,7 @@ namespace PARR.Core.Infrastructure.Mapping.RobotSnapshot { public RobotSnapshotMappingProfile() { - CreateMap(); + CreateMap(); } } } diff --git a/PARR.Core/Services/RobotSnapshotServices/IRobotSnapshotService.cs b/PARR.Core/Services/RobotSnapshotServices/IRobotSnapshotService.cs index 6582ed6a..6ac083f1 100644 --- a/PARR.Core/Services/RobotSnapshotServices/IRobotSnapshotService.cs +++ b/PARR.Core/Services/RobotSnapshotServices/IRobotSnapshotService.cs @@ -4,6 +4,8 @@ namespace PARR.Core.Services.RobotSnapshotServices { public interface IRobotSnapshotService { - Task CreateAsync(CreateRobotSnapshot robotSnapshot); + Task> GetAsync(RobotSnapshotQuery queryDto); + + Task CreateAsync(CreateRobotSnapshot robotSnapshot); } } diff --git a/PARR.Core/Services/RobotSnapshotServices/RobotSnapshotService.cs b/PARR.Core/Services/RobotSnapshotServices/RobotSnapshotService.cs index c1ff0b9b..67738bc7 100644 --- a/PARR.Core/Services/RobotSnapshotServices/RobotSnapshotService.cs +++ b/PARR.Core/Services/RobotSnapshotServices/RobotSnapshotService.cs @@ -1,6 +1,9 @@ using AutoMapper; +using Microsoft.EntityFrameworkCore; +using PARR.Core.Repositories.Interfaces; using PARR.Core.Repositories.Interfaces.RobotRepositories; using PARR.Domain.DTOs.RobotSnapshotDTO; +using PARR.Domain.DTOs.User; using PARR.Domain.Entities.RobotEntities; using PARR.Domain.Exceptions; @@ -9,19 +12,63 @@ namespace PARR.Core.Services.RobotSnapshotServices internal class RobotSnapshotService : IRobotSnapshotService { private readonly IRobotSnapshotRepository robotSnapshotRepository; + private readonly IUserRepository userRepository; private readonly IMapper mapper; public RobotSnapshotService( IRobotSnapshotRepository robotSnapshotRepository, + IUserRepository userRepository, IMapper mapper ) { this.robotSnapshotRepository = robotSnapshotRepository; + this.userRepository = userRepository; this.mapper = mapper; } - public async Task CreateAsync(CreateRobotSnapshot robotSnapshot) + public async Task> GetAsync(RobotSnapshotQuery queryDto) + { + // Просто возвращаем данные из БД, не проверяем что есть промежутки между которыми не было данных + // Т.е это просто чтоб посмотреть что есть в БД + + var minutes = queryDto.Minutes > 0 ? queryDto.Minutes : 60 * 12; + + var dateEnd = DateTimeOffset.UtcNow; + var dateStart = dateEnd.AddMinutes(-minutes); + + var query = robotSnapshotRepository.Get().AsNoTracking() + .Where(t => dateStart <= t.DateCreated && t.DateCreated <= dateEnd); + + if (!string.IsNullOrEmpty(queryDto.Ip)) + query = query.Where(t => t.Ip == queryDto.Ip); + + var groupingSnapshots = (await query.ToListAsync()).GroupBy(t => t.Ip); + + + var robotIps = groupingSnapshots.Select(t => t.Key).ToHashSet(); + var robotList = await userRepository.Get() + .AsNoTracking() + .Where(t => robotIps.Contains(t.Ip)) + .ToDictionaryAsync(t => t.Ip); + + var result = new List(); + + foreach (var item in groupingSnapshots) + { + robotList.TryGetValue(item.Key, out var robot); + + var userDto = new UserBaseDto { Ip = item.Key, Description = robot?.Description ?? string.Empty, Name = robot?.Name ?? string.Empty }; + var snapshots = mapper.Map>(item).OrderBy(t => t.DateCreated).ToList(); + + result.Add(new RobotSnapshotItemDto { Robot = userDto, Snapshots = snapshots }); + } + + return result.OrderBy(t => t.Robot.Ip).ThenBy(t => t.Robot.Name).ToList(); + } + + + public async Task CreateAsync(CreateRobotSnapshot robotSnapshot) { var snapshot = new RobotSnapshot { @@ -38,7 +85,16 @@ namespace PARR.Core.Services.RobotSnapshotServices if (!createdResult || !commitResult) throw new DbErrorException("Ошибка сохранения в БД"); - return mapper.Map(snapshot); + var user = await userRepository.Get().AsNoTracking().FirstOrDefaultAsync(t => t.Ip == robotSnapshot.Ip); + var userDto = new UserBaseDto { Ip = robotSnapshot.Ip, Description = user?.Description ?? string.Empty, Name = user?.Name ?? string.Empty }; + + var result = new RobotSnapshotItemDto + { + Robot = userDto, + Snapshots = mapper.Map>(new List { snapshot }) + }; + + return result; } diff --git a/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotItem.cs b/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotItem.cs deleted file mode 100644 index 521d14c7..00000000 --- a/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotItem.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace PARR.Domain.DTOs.RobotSnapshotDTO -{ - public record RobotSnapshotItem - { - public Guid Id { get; init; } - - public DateTimeOffset DateCreated { get; init; } - - public int TemplateRobotsCount { get; init; } - - public int ScheduleRobotsCount { get; init; } - - public int MaxRobots { get; init; } - - public required string Ip { get; init; } - } -} diff --git a/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotItemDto.cs b/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotItemDto.cs new file mode 100644 index 00000000..8fc0c7f0 --- /dev/null +++ b/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotItemDto.cs @@ -0,0 +1,26 @@ + + +using PARR.Domain.DTOs.User; + +namespace PARR.Domain.DTOs.RobotSnapshotDTO +{ + public record RobotSnapshotItemDto + { + public required UserBaseDto Robot { get; init; } + + public List Snapshots { get; init; } = new(); + } + + public record RobotSnapshotDto + { + public Guid Id { get; init; } + + public DateTimeOffset DateCreated { get; init; } + + public int TemplateRobotsCount { get; init; } + + public int ScheduleRobotsCount { get; init; } + + public int MaxRobots { get; init; } + } +} diff --git a/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotQuery.cs b/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotQuery.cs new file mode 100644 index 00000000..b45f0c0c --- /dev/null +++ b/PARR.Domain/DTOs/RobotSnapshotDTO/RobotSnapshotQuery.cs @@ -0,0 +1,15 @@ +namespace PARR.Domain.DTOs.RobotSnapshotDTO +{ + public record RobotSnapshotQuery + { + /// + /// IP робота + /// + public string? Ip { get; init; } + + /// + /// Кол-во минут от текущего времени + /// + public int Minutes { get; init; } + } +} diff --git a/PARR.Domain/DTOs/User/UserDto.cs b/PARR.Domain/DTOs/User/UserDto.cs new file mode 100644 index 00000000..b8e03875 --- /dev/null +++ b/PARR.Domain/DTOs/User/UserDto.cs @@ -0,0 +1,25 @@ +namespace PARR.Domain.DTOs.User +{ + //todo: Не делал маппингов + + public record UserDto : UserBaseDto + { + public Guid Id { get; set; } + + public DateTimeOffset DateCreated { get; set; } + + public DateTimeOffset? LastLogon { get; set; } + + //todo: + //public List? Roles { get; set; } + } + + public record UserBaseDto + { + public required string Ip { get; set; } + + public string Name { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + } +}