diff --git a/PARR.API/Contracts/V1/Requests/Queries/StatWorkloadTemplateReqportQuery.cs b/PARR.API/Contracts/V1/Requests/Queries/StatWorkloadTemplateReqportQuery.cs new file mode 100644 index 00000000..53273ac1 --- /dev/null +++ b/PARR.API/Contracts/V1/Requests/Queries/StatWorkloadTemplateReqportQuery.cs @@ -0,0 +1,17 @@ +using PARR.Domain.Enums.Workload; + +namespace PARR.API.Contracts.V1.Requests.Queries +{ + public record StatWorkloadTemplateReqportQuery + { + /// + /// Тип дополнительно фильтра для работы (ЗО, РГ) + /// + public WorkloadJobSubfliterType? JobSubfilterType { get; init; } + + /// + /// Значение дополнительного фильтра для работы + /// + public string? JobSubfilterValue { get; init; } + } +} diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkloadReportResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatWorkloadReportResponse.cs index 6d266c49..c7dac959 100644 --- a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkloadReportResponse.cs +++ b/PARR.API/Contracts/V1/Responses/Statistics/StatWorkloadReportResponse.cs @@ -72,6 +72,8 @@ namespace PARR.API.Contracts.V1.Responses.Statistics { public required string Title { get; init; } + public Guid? ObjId { get; init; } + public required WorkloadSummaryResponse Summary { get; init; } public required List DailyMetrics { get; init; } diff --git a/PARR.API/Controllers/V1/Statistics/StatWorkloadTemplateController.cs b/PARR.API/Controllers/V1/Statistics/StatWorkloadTemplateController.cs index b5b33e9a..b443fa5e 100644 --- a/PARR.API/Controllers/V1/Statistics/StatWorkloadTemplateController.cs +++ b/PARR.API/Controllers/V1/Statistics/StatWorkloadTemplateController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PARR.API.Contracts.V1; using PARR.API.Contracts.V1.Requests.BaseRequests; +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; @@ -45,7 +46,14 @@ namespace PARR.API.Controllers.V1.Statistics /// /// [HttpGet(ApiRoutes.StatWorkloadTemplate.GetWorkloadTemplateReport)] - public async Task GetWorkloadTemplateReport([FromRoute] WorkloadTemplateReportType reportType, [FromRoute] string filter, [FromRoute] WorkloadTemplateReportState state, [FromRoute] DateOnly date, [FromQuery] TimeZoneOffsetClient offset) + public async Task GetWorkloadTemplateReport( + [FromRoute] WorkloadTemplateReportType reportType, + [FromRoute] string filter, + [FromRoute] WorkloadTemplateReportState state, + [FromRoute] DateOnly date, + [FromQuery] TimeZoneOffsetClient offset, + [FromQuery] StatWorkloadTemplateReqportQuery query + ) { if (string.IsNullOrWhiteSpace(filter)) { @@ -53,7 +61,13 @@ namespace PARR.API.Controllers.V1.Statistics throw new ArgumentException(nameof(filter), "Не передано значение фильтра."); } - var report = await workloadService.GetTemplateReportAsync(reportType, filter, state, date, offset.TimeZoneOffset); + if (query.JobSubfilterType.HasValue && string.IsNullOrWhiteSpace(query.JobSubfilterValue)) + { + logger.LogWarning("Не передано значение дополнительного фильтра для работы"); + throw new ArgumentException(nameof(filter), "Не передано значение дополнительного фильтра для работы."); + } + + var report = await workloadService.GetTemplateReportAsync(reportType, filter, state, date, offset.TimeZoneOffset, query.JobSubfilterType, query.JobSubfilterValue); var response = mapper.Map>(report); diff --git a/PARR.Core/Services/Workload/Implementations/WorkloadService.cs b/PARR.Core/Services/Workload/Implementations/WorkloadService.cs index 8f533012..07f98fe3 100644 --- a/PARR.Core/Services/Workload/Implementations/WorkloadService.cs +++ b/PARR.Core/Services/Workload/Implementations/WorkloadService.cs @@ -9,6 +9,7 @@ using PARR.Core.Services.TaskServices.Helpers; using PARR.Core.Services.Workload.Interfaces; using PARR.Core.Services.Workload.Models; using PARR.Domain.DTOs.Workload; +using PARR.Domain.Entities; using PARR.Domain.Enums; using PARR.Domain.Enums.Workload; @@ -55,7 +56,6 @@ namespace PARR.Core.Services.Workload.Implementations throw new ArgumentNullException(nameof(filterParam), $"Для отчета типа {reportType.ToString()} параметр является обязательным."); } - // Если есть отчет в КЭШ, вернуть var cacheReport = await workloadCacheService.GetWorkloadReportAsync(reportType, dateStart, durationDays, offset, filterParam); if (cacheReport != null) @@ -87,7 +87,7 @@ namespace PARR.Core.Services.Workload.Implementations case WorkloadReportType.ResponseAreaAll: displayName = "По зонам ответственности"; foreach (var group in templates.GroupBy(t => t.ResponseArea, StringComparer.OrdinalIgnoreCase)) - statistics.Add(BuildStatisticItem(group.Key, group, reportDays, offset)); + statistics.Add(BuildStatisticItem(group.Key, null, group, reportDays, offset)); break; case WorkloadReportType.ResponseAreaWorkGroups: displayName = "По зоне ответственности"; @@ -95,13 +95,13 @@ namespace PARR.Core.Services.Workload.Implementations var filteredRA = templates.Where(t => string.Equals(t.ResponseArea, filterParam, StringComparison.OrdinalIgnoreCase)).ToList(); // Группируем работы по РГ foreach (var group in filteredRA.GroupBy(t => t.WorkGroup, StringComparer.OrdinalIgnoreCase)) - statistics.Add(BuildStatisticItem(group.Key, group, reportDays, offset)); + statistics.Add(BuildStatisticItem(group.Key, null, group, reportDays, offset)); //statistics.Add(BuildStatisticItem(filterParam!, filteredRA, reportDays, offset)); break; case WorkloadReportType.WorkGroupAll: displayName = "По рабочим группам"; foreach (var group in templates.GroupBy(t => t.WorkGroup)) - statistics.Add(BuildStatisticItem(group.Key, group, reportDays, offset)); + statistics.Add(BuildStatisticItem(group.Key, null, group, reportDays, offset)); break; case WorkloadReportType.WorkGroupJob: displayName = "По рабочей группе"; @@ -118,7 +118,7 @@ namespace PARR.Core.Services.Workload.Implementations foreach (var group in filteredWG.GroupBy(t => t.JobId)) { var jobName = jobs.GetValueOrDefault(group.Key) ?? "Работа"; - statistics.Add(BuildStatisticItem(jobName, group, reportDays, offset)); + statistics.Add(BuildStatisticItem(jobName, group.Key, group, reportDays, offset)); } break; } @@ -175,7 +175,15 @@ namespace PARR.Core.Services.Workload.Implementations } - public async Task> GetTemplateReportAsync(WorkloadTemplateReportType reportType, string filter, WorkloadTemplateReportState state, DateOnly date, TimeSpan offset) + public async Task> GetTemplateReportAsync( + WorkloadTemplateReportType reportType, + string filter, + WorkloadTemplateReportState state, + DateOnly date, + TimeSpan offset, + WorkloadJobSubfliterType? jobSubfilterType, + string? jobSubfilterValue + ) { if (string.IsNullOrWhiteSpace(filter)) { @@ -218,19 +226,16 @@ namespace PARR.Core.Services.Workload.Implementations break; } - // Если тип - работы + // Если тип - работы, получим из бд сразу нужные работы if (reportType == WorkloadTemplateReportType.Job) { if (!Guid.TryParse(trimmedFilter, out Guid jobId)) throw new ArgumentException("Значение фильтра не является Guid"); - return await query.Where(t => t.JobId == jobId) - .OrderBy(t => t.Name) - .Select(t => new WorkloadTemplateReport(t.Id, t.Name, t.NextRun, t.IsActiveTemplate, t.IsActiveSchedule)) - .ToListAsync(); + query = query.Where(t => t.JobId == jobId); } - // Тип "не работы", загружаем шаблоны + // Загружаем шаблоны var dbTemplates = await query.OrderBy(t => t.Name) .Select(t => new WorkloadTemplateReport(t.Id, t.Name, t.NextRun, t.IsActiveTemplate, t.IsActiveSchedule)) .ToListAsync(); @@ -238,6 +243,13 @@ namespace PARR.Core.Services.Workload.Implementations if (dbTemplates.Count == 0) return new List(); + // Если тип - работы, и нет дополнительных фильтров по ЗО или РГ, не полезем в кэш, вернем сразу + if (reportType == WorkloadTemplateReportType.Job && (!jobSubfilterType.HasValue || string.IsNullOrWhiteSpace(jobSubfilterValue))) + { + // Тут кэш не нужен, вернем сразу из бд (нет фильтров по РГ и ЗО, нужен список всех работ на дату) + return FilterWorkloadJobTemplateReport(dbTemplates, null); + } + // Получаем из кэша шаблоны (если их в кэше нет, догружаем в кэш) var cacheData = await workloadCacheService.GetTemplateReportDataByIdsAsync(dbTemplates.Select(t => t.TemplateId).ToHashSet()); @@ -250,6 +262,30 @@ namespace PARR.Core.Services.Workload.Implementations // Фильтр по типу switch (reportType) { + case WorkloadTemplateReportType.Job: + // Смотрим есть ли дополнительные фильтры + if (jobSubfilterType.HasValue && !string.IsNullOrWhiteSpace(jobSubfilterValue)) + { + var cacheTemplatesJobs = new HashSet(); + // Фильтруем из кэша + var trimmedJobSubfilterValue = jobSubfilterValue.Trim(); + switch (jobSubfilterType) + { + case WorkloadJobSubfliterType.ResponseArea: + cacheTemplatesJobs = cacheData.Where(t => string.Equals(t.Data.ResponseArea, trimmedJobSubfilterValue, StringComparison.OrdinalIgnoreCase)) + .Select(t => t.Data.TemplateId).ToHashSet(); + break; + case WorkloadJobSubfliterType.WorkGroup: + cacheTemplatesJobs = cacheData.Where(t => string.Equals(t.Data.WorkGroup, trimmedJobSubfilterValue, StringComparison.OrdinalIgnoreCase)) + .Select(t => t.Data.TemplateId).ToHashSet(); + break; + default: + throw new ArgumentException("Неверный тип фильтра", nameof(jobSubfilterType)); + } + + return FilterWorkloadJobTemplateReport(dbTemplates, cacheTemplatesJobs); + } + return FilterWorkloadJobTemplateReport(dbTemplates, null); case WorkloadTemplateReportType.WorkGroup: var cacheTemplatesWg = cacheData.Where(t => string.Equals(t.Data.WorkGroup, trimmedFilter, StringComparison.OrdinalIgnoreCase)) .Select(t => t.Data.TemplateId).ToHashSet(); @@ -266,6 +302,16 @@ namespace PARR.Core.Services.Workload.Implementations } + private List FilterWorkloadJobTemplateReport(List dbTemplates, HashSet? filteredCacheTemplates) + { + if (filteredCacheTemplates == null) + return dbTemplates; + + // Отсекаем шаблоны которых нет в кэше + return dbTemplates.Where(t => filteredCacheTemplates.Contains(t.TemplateId)).ToList(); + } + + /// /// Получить список дней для отчета, с отметкой выходной/рабочий /// @@ -347,12 +393,12 @@ namespace PARR.Core.Services.Workload.Implementations /// /// /// - private WorkloadStatisticItem BuildStatisticItem(string title, IEnumerable groupTemplates, List reportDays, TimeSpan offset) + private WorkloadStatisticItem BuildStatisticItem(string title, Guid? objId, IEnumerable groupTemplates, List reportDays, TimeSpan offset) { var dailyMetrics = GetDailyMetrics(groupTemplates.ToList(), reportDays, offset); var summary = GetSummary(dailyMetrics, reportDays.Count); - return new WorkloadStatisticItem { DailyMetrics = dailyMetrics, Summary = summary, Title = title }; + return new WorkloadStatisticItem { DailyMetrics = dailyMetrics, Summary = summary, Title = title, ObjId = objId }; } } } diff --git a/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs b/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs index f44595c4..12c905f3 100644 --- a/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs +++ b/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs @@ -33,8 +33,10 @@ namespace PARR.Core.Services.Workload.Interfaces /// /// /// + /// Дополнительный фильтр при отчете по шаблонам, тип + /// Дополнительный фильтр при отчете по шаблонам, значение /// - Task> GetTemplateReportAsync(WorkloadTemplateReportType reportType, string filter, WorkloadTemplateReportState state, DateOnly date, TimeSpan offset); + Task> GetTemplateReportAsync(WorkloadTemplateReportType reportType, string filter, WorkloadTemplateReportState state, DateOnly date, TimeSpan offset, WorkloadJobSubfliterType? jobSubfilterType, string? jobSubfilterValue); } } diff --git a/PARR.Domain/DTOs/Workload/WorkloadReport.cs b/PARR.Domain/DTOs/Workload/WorkloadReport.cs index e3956864..862577ab 100644 --- a/PARR.Domain/DTOs/Workload/WorkloadReport.cs +++ b/PARR.Domain/DTOs/Workload/WorkloadReport.cs @@ -73,6 +73,12 @@ namespace PARR.Domain.DTOs.Workload { public required string Title { get; init; } + /// + /// ИД строки, если есть. + /// Например JobId + /// + public Guid? ObjId { get; init; } + public required WorkloadSummary Summary { get; init; } public required List DailyMetrics { get; init; } diff --git a/PARR.Domain/Enums/Workload/WorkloadJobSubfliterType.cs b/PARR.Domain/Enums/Workload/WorkloadJobSubfliterType.cs new file mode 100644 index 00000000..0d0954f0 --- /dev/null +++ b/PARR.Domain/Enums/Workload/WorkloadJobSubfliterType.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace PARR.Domain.Enums.Workload +{ + /// + /// Дополнительный фильтр для работ. + /// Отчет Workload, шаблоны + /// + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum WorkloadJobSubfliterType + { + /// + /// ЗО + /// + ResponseArea = 0, + + /// + /// РГ + /// + WorkGroup=1 + } +}