feat(api,core,infrastructure): Workload - дополнительная фильтрация по свяанным ЗО и РГ. RedisCacheService - при формирование hash key, дополнительная фильтрация на null, empty.

This commit is contained in:
Mikhail Trubnikov
2026-06-09 10:16:32 +10:00
parent 8cb7a2d599
commit 6fa9c3d445
7 changed files with 102 additions and 77 deletions

View File

@@ -1,17 +1,15 @@
using PARR.Domain.Enums.Workload; namespace PARR.API.Contracts.V1.Requests.Queries
namespace PARR.API.Contracts.V1.Requests.Queries
{ {
public record StatWorkloadTemplateReqportQuery public record StatWorkloadTemplateReqportQuery
{ {
/// <summary> /// <summary>
/// Тип дополнительно фильтра для работы (ЗО, РГ) /// Дополнительный фильтр для работ - ЗО
/// </summary> /// </summary>
public WorkloadJobSubfliterType? JobSubfilterType { get; init; } public string? ResponseArea { get; init; }
/// <summary> /// <summary>
/// Значение дополнительного фильтра для работы /// Дополнительный фильтр для работ - РГ
/// </summary> /// </summary>
public string? JobSubfilterValue { get; init; } public string? WorkGroup { get; init; }
} }
} }

View File

@@ -57,7 +57,7 @@ namespace PARR.API.Controllers.V1.Statistics
[HttpGet(ApiRoutes.Workload.WorkGroup)] [HttpGet(ApiRoutes.Workload.WorkGroup)]
public async Task<IActionResult> GetWorkGroupReport([FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request) public async Task<IActionResult> GetWorkGroupReport([FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request)
{ {
return await GetReportAsync(WorkloadReportType.WorkGroupAll, request.DurationDays, offset.TimeZoneOffset, null); return await GetReportAsync(WorkloadReportType.WorkGroupAll, request.DurationDays, offset.TimeZoneOffset, null, null);
} }
@@ -69,12 +69,12 @@ namespace PARR.API.Controllers.V1.Statistics
/// <param name="request"></param> /// <param name="request"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet(ApiRoutes.Workload.WorkGroupJob)] [HttpGet(ApiRoutes.Workload.WorkGroupJob)]
public async Task<IActionResult> GetWorkGroupJobReport([FromRoute] string workGroup, [FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request) public async Task<IActionResult> GetWorkGroupJobReport([FromRoute] string workGroup, [FromQuery] string? responseArea, [FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request)
{ {
if (string.IsNullOrWhiteSpace(workGroup)) if (string.IsNullOrWhiteSpace(workGroup))
throw new AppValidationException("Не указано значение рабочей группы"); throw new AppValidationException("Не указано значение рабочей группы");
return await GetReportAsync(WorkloadReportType.WorkGroupJob, request.DurationDays, offset.TimeZoneOffset, workGroup); return await GetReportAsync(WorkloadReportType.WorkGroupJob, request.DurationDays, offset.TimeZoneOffset, responseArea, workGroup);
} }
@@ -85,7 +85,7 @@ namespace PARR.API.Controllers.V1.Statistics
[HttpGet(ApiRoutes.Workload.ResponseArea)] [HttpGet(ApiRoutes.Workload.ResponseArea)]
public async Task<IActionResult> GetResponseAreaReport([FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request) public async Task<IActionResult> GetResponseAreaReport([FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request)
{ {
return await GetReportAsync(WorkloadReportType.ResponseAreaAll, request.DurationDays, offset.TimeZoneOffset, null); return await GetReportAsync(WorkloadReportType.ResponseAreaAll, request.DurationDays, offset.TimeZoneOffset, null, null);
} }
@@ -102,7 +102,7 @@ namespace PARR.API.Controllers.V1.Statistics
if (string.IsNullOrWhiteSpace(responseArea)) if (string.IsNullOrWhiteSpace(responseArea))
throw new AppValidationException("Не указано значение зоны ответственности"); throw new AppValidationException("Не указано значение зоны ответственности");
return await GetReportAsync(WorkloadReportType.ResponseAreaWorkGroups, request.DurationDays, offset.TimeZoneOffset, responseArea); return await GetReportAsync(WorkloadReportType.ResponseAreaWorkGroups, request.DurationDays, offset.TimeZoneOffset, responseArea, null);
} }
@@ -147,13 +147,14 @@ namespace PARR.API.Controllers.V1.Statistics
/// Универсальный метод получения отчетности /// Универсальный метод получения отчетности
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private async Task<IActionResult> GetReportAsync(WorkloadReportType reportType, int durationDays, TimeSpan offset, string? filter) //private async Task<IActionResult> GetReportAsync(WorkloadReportType reportType, int durationDays, TimeSpan offset, string? filter)
private async Task<IActionResult> GetReportAsync(WorkloadReportType reportType, int durationDays, TimeSpan offset, string? responseArea, string? workGroup)
{ {
var utcNow = DateTimeOffset.UtcNow; var utcNow = DateTimeOffset.UtcNow;
var localTime = utcNow.ToOffset(offset); var localTime = utcNow.ToOffset(offset);
var dateStart = DateOnly.FromDateTime(localTime.Date); var dateStart = DateOnly.FromDateTime(localTime.Date);
var report = await workloadService.GetWorkloadReportAsync(reportType, dateStart, durationDays, offset, filter?.Trim()); var report = await workloadService.GetWorkloadReportAsync(reportType, dateStart, durationDays, offset, responseArea?.Trim(), workGroup?.Trim());
if (report == null) if (report == null)
return BadRequest(new Response<string?>(null, false, new List<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }, "empty")); return BadRequest(new Response<string?>(null, false, new List<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }, "empty"));

View File

@@ -38,11 +38,11 @@ namespace PARR.API.Controllers.V1.Statistics
/// <summary> /// <summary>
/// Отчет о загруженности. Шаблоны /// Отчет о загруженности. Шаблоны
/// </summary> /// </summary>
/// <param name="reportType"></param> /// <param name="reportType">Тип отчета (ЗО, РГ, Работа)</param>
/// <param name="filter"></param> /// <param name="filter">Фильтр, значиение в зависимости от типа отчета</param>
/// <param name="state"></param> /// <param name="state">Все, активированные, деактивированные</param>
/// <param name="date"></param> /// <param name="date">Дата</param>
/// <param name="offset"></param> /// <param name="offset">Часовой пояс</param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
[HttpGet(ApiRoutes.StatWorkloadTemplate.GetWorkloadTemplateReport)] [HttpGet(ApiRoutes.StatWorkloadTemplate.GetWorkloadTemplateReport)]
@@ -61,13 +61,7 @@ namespace PARR.API.Controllers.V1.Statistics
throw new ArgumentException(nameof(filter), "Не передано значение фильтра."); throw new ArgumentException(nameof(filter), "Не передано значение фильтра.");
} }
if (query.JobSubfilterType.HasValue && string.IsNullOrWhiteSpace(query.JobSubfilterValue)) var report = await workloadService.GetTemplateReportAsync(reportType, filter.Trim(), state, date, offset.TimeZoneOffset, query.ResponseArea?.Trim(), query.WorkGroup?.Trim());
{
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<List<StatWorkloadTemplateReportResponse>>(report); var response = mapper.Map<List<StatWorkloadTemplateReportResponse>>(report);

View File

@@ -415,9 +415,9 @@ namespace PARR.Core.Services.Workload.Implementations
/// <param name="offset"></param> /// <param name="offset"></param>
/// <param name="filterParam">Наименование ЗО или РГ или null</param> /// <param name="filterParam">Наименование ЗО или РГ или null</param>
/// <returns></returns> /// <returns></returns>
public async Task<WorkloadReport?> GetWorkloadReportAsync(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? filterParam) public async Task<WorkloadReport?> GetWorkloadReportAsync(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? responseArea, string? workGroup)
{ {
var key = GetReportCacheKey(reportType, dateStart, durationDays, offset, filterParam); var key = GetReportCacheKey(reportType, dateStart, durationDays, offset, responseArea, workGroup);
return await redisCacheService.GetCachedDataAsync<WorkloadReport>(key, true); return await redisCacheService.GetCachedDataAsync<WorkloadReport>(key, true);
} }
@@ -433,9 +433,9 @@ namespace PARR.Core.Services.Workload.Implementations
/// <param name="offset"></param> /// <param name="offset"></param>
/// <param name="filterParam">Наименование ЗО или РГ или null</param> /// <param name="filterParam">Наименование ЗО или РГ или null</param>
/// <returns></returns> /// <returns></returns>
public async Task SetWorkloadReport(WorkloadReport report, WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? filterParam) public async Task SetWorkloadReport(WorkloadReport report, WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? responseArea, string? workGroup)
{ {
var key = GetReportCacheKey(reportType, dateStart, durationDays, offset, filterParam); var key = GetReportCacheKey(reportType, dateStart, durationDays, offset, responseArea, workGroup);
await redisCacheService.SetCachedDataAsync(key, report, cacheWorkloadTtl, true); await redisCacheService.SetCachedDataAsync(key, report, cacheWorkloadTtl, true);
} }
@@ -462,10 +462,10 @@ namespace PARR.Core.Services.Workload.Implementations
/// <param name="offset"></param> /// <param name="offset"></param>
/// <param name="filterParam"></param> /// <param name="filterParam"></param>
/// <returns></returns> /// <returns></returns>
private string GetReportCacheKey(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? filterParam = null) private string GetReportCacheKey(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? responseArea, string? workGroup)
{ {
// формируе ключ, вида: основа + дата начала + продолжительность + смещение // формируе ключ, вида: основа + дата начала + продолжительность + смещение
// Если есть параметр (filterParam), добавляется параметр в виде Hash // Если есть параметр (responseArea, workGroup), добавляется параметр в виде Hash
var keyList = new List<string>(); var keyList = new List<string>();
@@ -493,10 +493,10 @@ namespace PARR.Core.Services.Workload.Implementations
var keyStr = string.Empty; var keyStr = string.Empty;
if (filterParam == null) if (responseArea == null && workGroup == null)
keyStr = redisCacheService.GetKey(keyList.ToArray()); keyStr = redisCacheService.GetKey(keyList.ToArray());
else else
keyStr = redisCacheService.GetKey(keyList.ToArray(), new string[] { filterParam }); keyStr = redisCacheService.GetKey(keyList.ToArray(), new string[] { responseArea ?? string.Empty, workGroup ?? string.Empty });
logger.LogDebug("Сформирован ключ для кэш '{Key}'", keyStr); logger.LogDebug("Сформирован ключ для кэш '{Key}'", keyStr);

View File

@@ -8,6 +8,7 @@ using PARR.Core.Services.NextRunServices;
using PARR.Core.Services.TaskServices.Helpers; using PARR.Core.Services.TaskServices.Helpers;
using PARR.Core.Services.Workload.Interfaces; using PARR.Core.Services.Workload.Interfaces;
using PARR.Core.Services.Workload.Models; using PARR.Core.Services.Workload.Models;
using PARR.Domain.Cache.Models;
using PARR.Domain.DTOs.Workload; using PARR.Domain.DTOs.Workload;
using PARR.Domain.Entities; using PARR.Domain.Entities;
using PARR.Domain.Enums; using PARR.Domain.Enums;
@@ -45,19 +46,25 @@ namespace PARR.Core.Services.Workload.Implementations
} }
public async Task<WorkloadReport?> GetWorkloadReportAsync(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? filterParam) public async Task<WorkloadReport?> GetWorkloadReportAsync(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? responseArea, string? workGroup)
{ {
logger.LogDebug("Начинаю формировать отчет о загруженности по параметрам: reportType '{ReportType}', dateStart {DateStart}, durationDays {DurationDays}, offset {Offset}", logger.LogDebug("Начинаю формировать отчет о загруженности по параметрам: reportType '{ReportType}', dateStart {DateStart}, durationDays {DurationDays}, offset {Offset}",
reportType.ToString(), dateStart, durationDays, offset); reportType.ToString(), dateStart, durationDays, offset);
if ((reportType == WorkloadReportType.WorkGroupJob || reportType == WorkloadReportType.ResponseAreaWorkGroups) && string.IsNullOrEmpty(filterParam)) if (reportType == WorkloadReportType.WorkGroupJob && string.IsNullOrEmpty(workGroup))
{ {
logger.LogError("Для типа отчета {ReportType} не передан параметр.", reportType.ToString()); logger.LogError("Для типа отчета {ReportType} не передан параметр.", reportType.ToString());
throw new ArgumentNullException(nameof(filterParam), $"Для отчета типа {reportType.ToString()} параметр является обязательным."); throw new ArgumentNullException(nameof(workGroup), $"Для отчета типа {reportType.ToString()} параметр является обязательным.");
}
if (reportType == WorkloadReportType.ResponseAreaWorkGroups && string.IsNullOrEmpty(responseArea))
{
logger.LogError("Для типа отчета {ReportType} не передан параметр.", reportType.ToString());
throw new ArgumentNullException(nameof(responseArea), $"Для отчета типа {reportType.ToString()} параметр является обязательным.");
} }
// Если есть отчет в КЭШ, вернуть // Если есть отчет в КЭШ, вернуть
var cacheReport = await workloadCacheService.GetWorkloadReportAsync(reportType, dateStart, durationDays, offset, filterParam); var cacheReport = await workloadCacheService.GetWorkloadReportAsync(reportType, dateStart, durationDays, offset, responseArea, workGroup);
if (cacheReport != null) if (cacheReport != null)
{ {
logger.LogDebug("Вернул отчет из кэша."); logger.LogDebug("Вернул отчет из кэша.");
@@ -92,11 +99,10 @@ namespace PARR.Core.Services.Workload.Implementations
case WorkloadReportType.ResponseAreaWorkGroups: case WorkloadReportType.ResponseAreaWorkGroups:
displayName = "По зоне ответственности"; displayName = "По зоне ответственности";
// Фильтруем по ЗО // Фильтруем по ЗО
var filteredRA = templates.Where(t => string.Equals(t.ResponseArea, filterParam, StringComparison.OrdinalIgnoreCase)).ToList(); var filteredRA = templates.Where(t => string.Equals(t.ResponseArea, responseArea, StringComparison.OrdinalIgnoreCase)).ToList();
// Группируем работы по РГ // Группируем работы по РГ
foreach (var group in filteredRA.GroupBy(t => t.WorkGroup, StringComparer.OrdinalIgnoreCase)) foreach (var group in filteredRA.GroupBy(t => t.WorkGroup, StringComparer.OrdinalIgnoreCase))
statistics.Add(BuildStatisticItem(group.Key, null, group, reportDays, offset)); statistics.Add(BuildStatisticItem(group.Key, null, group, reportDays, offset));
//statistics.Add(BuildStatisticItem(filterParam!, filteredRA, reportDays, offset));
break; break;
case WorkloadReportType.WorkGroupAll: case WorkloadReportType.WorkGroupAll:
displayName = "По рабочим группам"; displayName = "По рабочим группам";
@@ -106,7 +112,12 @@ namespace PARR.Core.Services.Workload.Implementations
case WorkloadReportType.WorkGroupJob: case WorkloadReportType.WorkGroupJob:
displayName = "По рабочей группе"; displayName = "По рабочей группе";
// Фильтруем по РГ // Фильтруем по РГ
var filteredWG = templates.Where(t => string.Equals(t.WorkGroup, filterParam, StringComparison.OrdinalIgnoreCase)).ToList(); var filteredWG = templates.Where(t => string.Equals(t.WorkGroup, workGroup, StringComparison.OrdinalIgnoreCase)).ToList();
if (!string.IsNullOrWhiteSpace(responseArea))
{
// Дополнительно фильтруем по ЗО
filteredWG = filteredWG.Where(t => string.Equals(t.ResponseArea, responseArea, StringComparison.OrdinalIgnoreCase)).ToList();
}
// Получим все JobName из бд // Получим все JobName из бд
var jobIds = filteredWG.Select(t => t.JobId).ToHashSet(); var jobIds = filteredWG.Select(t => t.JobId).ToHashSet();
@@ -123,11 +134,12 @@ namespace PARR.Core.Services.Workload.Implementations
break; break;
} }
var report = new WorkloadReport var report = new WorkloadReport
{ {
Type = reportType, Type = reportType,
DisplayName = displayName, DisplayName = displayName,
SubfilterName = filterParam, SubfilterName = string.Join(", ", new[] { responseArea, workGroup }),
ReportCacheCreatedAt = DateTimeOffset.UtcNow, ReportCacheCreatedAt = DateTimeOffset.UtcNow,
TemplateCacheCreatedAt = templates.FirstOrDefault()?.CacheDate ?? DateTimeOffset.MinValue, TemplateCacheCreatedAt = templates.FirstOrDefault()?.CacheDate ?? DateTimeOffset.MinValue,
Days = reportDays, Days = reportDays,
@@ -135,7 +147,7 @@ namespace PARR.Core.Services.Workload.Implementations
}; };
// Сохраним в кэш // Сохраним в кэш
await workloadCacheService.SetWorkloadReport(report, reportType, dateStart, durationDays, offset, filterParam); await workloadCacheService.SetWorkloadReport(report, reportType, dateStart, durationDays, offset, responseArea, workGroup);
logger.LogDebug("Отчет построен и сохранен в кэш"); logger.LogDebug("Отчет построен и сохранен в кэш");
return report; return report;
@@ -181,8 +193,8 @@ namespace PARR.Core.Services.Workload.Implementations
WorkloadTemplateReportState state, WorkloadTemplateReportState state,
DateOnly date, DateOnly date,
TimeSpan offset, TimeSpan offset,
WorkloadJobSubfliterType? jobSubfilterType, string? responseArea,
string? jobSubfilterValue string? workGroup
) )
{ {
if (string.IsNullOrWhiteSpace(filter)) if (string.IsNullOrWhiteSpace(filter))
@@ -243,8 +255,8 @@ namespace PARR.Core.Services.Workload.Implementations
if (dbTemplates.Count == 0) if (dbTemplates.Count == 0)
return new List<WorkloadTemplateReport>(); return new List<WorkloadTemplateReport>();
// Если тип - работы, и нет дополнительных фильтров по ЗО или РГ, не полезем в кэш, вернем сразу // Если тип - работы, и нет дополнительных фильтров по ЗО и РГ, не полезем в кэш, вернем сразу
if (reportType == WorkloadTemplateReportType.Job && (!jobSubfilterType.HasValue || string.IsNullOrWhiteSpace(jobSubfilterValue))) if (reportType == WorkloadTemplateReportType.Job && string.IsNullOrWhiteSpace(responseArea) && string.IsNullOrWhiteSpace(workGroup))
{ {
// Тут кэш не нужен, вернем сразу из бд (нет фильтров по РГ и ЗО, нужен список всех работ на дату) // Тут кэш не нужен, вернем сразу из бд (нет фильтров по РГ и ЗО, нужен список всех работ на дату)
return FilterWorkloadJobTemplateReport(dbTemplates, null); return FilterWorkloadJobTemplateReport(dbTemplates, null);
@@ -259,41 +271,51 @@ namespace PARR.Core.Services.Workload.Implementations
if (dbTemplates.Count != cacheData.Count) if (dbTemplates.Count != cacheData.Count)
logger.LogWarning("Количество шаблонов в БД {DbCount} не совпадает с количеством шаблонов полученных из кэша {CacheCount}", dbTemplates.Count, cacheData.Count); logger.LogWarning("Количество шаблонов в БД {DbCount} не совпадает с количеством шаблонов полученных из кэша {CacheCount}", dbTemplates.Count, cacheData.Count);
// Дополнительные фильтры
var trimmedResponseArea = responseArea?.Trim();
var trimmedWorkGroup = workGroup?.Trim();
// Фильтр по типу // Фильтр по типу
switch (reportType) switch (reportType)
{ {
case WorkloadTemplateReportType.Job: case WorkloadTemplateReportType.Job:
// Смотрим есть ли дополнительные фильтры // Смотрим есть ли дополнительные фильтры
if (jobSubfilterType.HasValue && !string.IsNullOrWhiteSpace(jobSubfilterValue)) if (!string.IsNullOrEmpty(trimmedResponseArea) || !string.IsNullOrEmpty(trimmedWorkGroup))
{ {
var cacheTemplatesJobs = new HashSet<Guid>();
// Фильтруем из кэша // Фильтруем из кэша
var trimmedJobSubfilterValue = jobSubfilterValue.Trim(); IEnumerable<TemplateCache> cacheTemplatesJobs = cacheData;
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); // Фильтруем по ЗО если есть
if (!string.IsNullOrEmpty(trimmedResponseArea))
cacheTemplatesJobs = cacheTemplatesJobs.Where(t => string.Equals(t.Data.ResponseArea, trimmedResponseArea, StringComparison.OrdinalIgnoreCase));
// Дополнительный фильтр по РГ, если есть
if (!string.IsNullOrEmpty(trimmedWorkGroup))
cacheTemplatesJobs = cacheTemplatesJobs.Where(t => string.Equals(t.Data.WorkGroup, trimmedWorkGroup, StringComparison.OrdinalIgnoreCase));
var filteredTemplateJobIds = cacheTemplatesJobs.Select(t => t.Data.TemplateId).ToHashSet();
return FilterWorkloadJobTemplateReport(dbTemplates, filteredTemplateJobIds);
} }
return FilterWorkloadJobTemplateReport(dbTemplates, null); return FilterWorkloadJobTemplateReport(dbTemplates, null);
case WorkloadTemplateReportType.WorkGroup: case WorkloadTemplateReportType.WorkGroup:
var cacheTemplatesWg = cacheData.Where(t => string.Equals(t.Data.WorkGroup, trimmedFilter, StringComparison.OrdinalIgnoreCase)) var cacheTemplatesWg = cacheData.Where(t => string.Equals(t.Data.WorkGroup, trimmedFilter, StringComparison.OrdinalIgnoreCase));
.Select(t => t.Data.TemplateId).ToHashSet(); // Дополнительный фильтр по ЗО, если есть
return dbTemplates.Where(t => cacheTemplatesWg.Contains(t.TemplateId)).ToList(); if (!string.IsNullOrEmpty(trimmedResponseArea))
cacheTemplatesWg = cacheTemplatesWg.Where(t => string.Equals(t.Data.ResponseArea, trimmedResponseArea, StringComparison.OrdinalIgnoreCase));
var filteredTemplateWgIds = cacheTemplatesWg.Select(t => t.Data.TemplateId).ToHashSet();
return dbTemplates.Where(t => filteredTemplateWgIds.Contains(t.TemplateId)).ToList();
case WorkloadTemplateReportType.ResponseArea: case WorkloadTemplateReportType.ResponseArea:
var cacheTemplatesRa = cacheData.Where(t => string.Equals(t.Data.ResponseArea, trimmedFilter, StringComparison.OrdinalIgnoreCase)) var cacheTemplatesRa = cacheData.Where(t => string.Equals(t.Data.ResponseArea, trimmedFilter, StringComparison.OrdinalIgnoreCase));
.Select(t => t.Data.TemplateId).ToHashSet(); // Дополнительный фильтр по РГ если есть
return dbTemplates.Where(t => cacheTemplatesRa.Contains(t.TemplateId)).ToList(); if (!string.IsNullOrEmpty(trimmedWorkGroup))
cacheTemplatesRa = cacheTemplatesRa.Where(t => string.Equals(t.Data.WorkGroup, trimmedWorkGroup, StringComparison.OrdinalIgnoreCase));
var filteredTemplateRaIds = cacheTemplatesRa.Select(t => t.Data.TemplateId).ToHashSet();
return dbTemplates.Where(t => filteredTemplateRaIds.Contains(t.TemplateId)).ToList();
} }
logger.LogWarning("Не обработанный тип отчета {ReportType}", reportType); logger.LogWarning("Не обработанный тип отчета {ReportType}", reportType);

View File

@@ -1,4 +1,5 @@
using PARR.Domain.DTOs.Workload; using Microsoft.Extensions.Primitives;
using PARR.Domain.DTOs.Workload;
using PARR.Domain.Enums.Workload; using PARR.Domain.Enums.Workload;
namespace PARR.Core.Services.Workload.Interfaces namespace PARR.Core.Services.Workload.Interfaces
@@ -21,9 +22,10 @@ namespace PARR.Core.Services.Workload.Interfaces
/// <param name="dateStart"></param> /// <param name="dateStart"></param>
/// <param name="durationDays"></param> /// <param name="durationDays"></param>
/// <param name="offset"></param> /// <param name="offset"></param>
/// <param name="filterParam"></param> /// <param name="responseArea"></param>
/// <param name="workGroup"></param>
/// <returns></returns> /// <returns></returns>
Task<WorkloadReport?> GetWorkloadReportAsync(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? filterParam); Task<WorkloadReport?> GetWorkloadReportAsync(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? responseArea, string? workGroup);
/// <summary> /// <summary>
/// Получить отчет - список шаблонов на дату согласно фильтрам /// Получить отчет - список шаблонов на дату согласно фильтрам
@@ -33,10 +35,18 @@ namespace PARR.Core.Services.Workload.Interfaces
/// <param name="state"></param> /// <param name="state"></param>
/// <param name="date"></param> /// <param name="date"></param>
/// <param name="offset"></param> /// <param name="offset"></param>
/// <param name="jobSubfilterType">Дополнительный фильтр при отчете по шаблонам, тип</param> /// <param name="responseArea">Дополнительный фильтр при отчете по шаблонам, ЗО</param>
/// <param name="jobSubfilterValue">Дополнительный фильтр при отчете по шаблонам, значение</param> /// <param name="workGorup">Дополнительный фильтр при отчете по шаблонам, РГ</param>
/// <returns></returns> /// <returns></returns>
Task<List<WorkloadTemplateReport>> GetTemplateReportAsync(WorkloadTemplateReportType reportType, string filter, WorkloadTemplateReportState state, DateOnly date, TimeSpan offset, WorkloadJobSubfliterType? jobSubfilterType, string? jobSubfilterValue); Task<List<WorkloadTemplateReport>> GetTemplateReportAsync(
WorkloadTemplateReportType reportType,
string filter,
WorkloadTemplateReportState state,
DateOnly date,
TimeSpan offset,
string? responseArea,
string? workGroup
);
} }
} }

View File

@@ -504,7 +504,7 @@ namespace PARR.Infrastructure.Redis
if (keyPartsToHash != null && keyPartsToHash.Length > 0) if (keyPartsToHash != null && keyPartsToHash.Length > 0)
{ {
var partsToHashStr = string.Join(mainSeparator, keyPartsToHash) var partsToHashStr = string.Join(mainSeparator, keyPartsToHash.Where(t => !string.IsNullOrWhiteSpace(t)))
.Replace(" ", wordSeparator) .Replace(" ", wordSeparator)
.ToLower(); .ToLower();