This commit is contained in:
Mikhail Kuznetsov
2026-06-09 11:42:30 +10:00
18 changed files with 514 additions and 199 deletions

View File

@@ -6,9 +6,10 @@ using PARR.Core.Services.Shortcodes;
using PARR.Core.Services.Workload.Models;
using PARR.Domain.Cache;
using PARR.Domain.Cache.Models;
using PARR.Domain.DTOs.UnitDto;
using PARR.Domain.DTOs.Workload;
using PARR.Domain.Entities;
using PARR.Domain.Enums;
using PARR.Domain.Enums.Workload;
namespace PARR.Core.Services.Workload.Implementations
{
@@ -175,6 +176,47 @@ namespace PARR.Core.Services.Workload.Implementations
await redisCacheService.DeleteKeysByPatternAsync(redisCacheService.GetKey(CacheKeys.Workload.WorkloadWorkGroupItem()) + "*");
}
/// <summary>
/// Получить список шаблонов по ИД из кэш/бд.
/// Если каких то данных не хватает в кэше, дополнить кэш.
/// </summary>
/// <param name="templateIds"></param>
/// <returns></returns>
public async Task<List<TemplateCache>?> GetTemplateReportDataByIdsAsync(IReadOnlySet<Guid> templateIds)
{
if (templateIds == null || templateIds.Count == 0)
return null;
// Формируем ключи
var keys = templateIds.Select(t =>
{
var (hashKey, fieldKey) = (redisCacheService.GetKey(CacheKeys.Workload.TemplateHash()), redisCacheService.GetKey(CacheKeys.Workload.Template(t)));
return (HashKey: hashKey, FieldKey: fieldKey);
}).ToList();
// Получаем из кэш
var cacheData = await redisCacheService.GetHashFieldsAsync<TemplateCache>(keys);
var resultDictionary = cacheData.ToDictionary(t => t.Data.TemplateId, t => t);
if (resultDictionary.Count < templateIds.Count)
{
// Догружаем недостающие данные
// Находим id которых нет в кэше
var missingIds = templateIds.Where(id => !resultDictionary.ContainsKey(id)).ToHashSet();
foreach (var templateId in missingIds)
{
var newCacheItem = await GetTemplatePropsAsync(templateId);
resultDictionary[templateId] = newCacheItem;
}
}
return resultDictionary.Select(t => t.Value).ToList();
}
/// <summary>
/// Получить кэш РГ и ЗО всех шаблонов, получить недостающие поля из БД.
/// Сравнит кол-во записей в БД и в КЭШ, если не хватает до 1000 шаблонов, доформирует кэш,
@@ -196,14 +238,9 @@ namespace PARR.Core.Services.Workload.Implementations
return null;
}
// Сравним кол-во записей в БД и в КЭШ, вдруг там разница больше допустимой
var templatesCount = await templateRepository.Get().AsNoTracking().CountAsync();
if (templatesCount - cacheData.Count > maxCacheDiff)
{
logger.LogInformation("Разница в количестве шаблонов в БД ({TemplateCount}) и в КЭШ ({CacheCount}) больше допустимой {MaxCacheDiff}. Считаем КЭШ неактуальным. Требуется повторное формирование КЭШа.",
templatesCount, cacheData.Count, maxCacheDiff);
// Кэш актуальный?
if (!await IsTemplateReportCacheActualAsync(cacheData.Count))
return null;
}
// Получаем из БД только нужные данные для отчета, попадающие в интервал
var dtStart = new DateTimeOffset(dateStart.Year, dateStart.Month, dateStart.Day, 0, 0, 0, offset);
@@ -251,7 +288,11 @@ namespace PARR.Core.Services.Workload.Implementations
}
else
{
(workGroup, responseArea, jobId) = await GetTemplatePropsAsync(template.Id);
//(workGroup, responseArea, jobId) = await GetTemplatePropsAsync(template.Id);
var newCacheItem = await GetTemplatePropsAsync(template.Id);
workGroup = newCacheItem.Data.WorkGroup;
responseArea = newCacheItem.Data.ResponseArea;
jobId = newCacheItem.Data.JobId;
}
var data = new TemplateReportData(
@@ -272,12 +313,35 @@ namespace PARR.Core.Services.Workload.Implementations
return reportData;
}
/// <summary>
/// Проверка кэша на акутальность.
/// </summary>
/// <returns></returns>
public async Task<bool> IsTemplateReportCacheActualAsync(long? cacheCount = null)
{
long finalCacheCount = cacheCount ?? await redisCacheService.GetHashLengthAsync(redisCacheService.GetKey(CacheKeys.Workload.TemplateHash()));
// Сравним кол-во записей в БД и в КЭШ, вдруг там разница больше допустимой
var templatesCount = await templateRepository.Get().AsNoTracking().CountAsync();
if (Math.Abs(templatesCount - finalCacheCount) > maxCacheDiff)
{
logger.LogInformation("Разница в количестве шаблонов в БД ({TemplateCount}) и в КЭШ ({CacheCount}) больше допустимой {MaxCacheDiff}. Считаем КЭШ неактуальным. Требуется повторное формирование КЭШа.",
templatesCount, cacheCount, maxCacheDiff);
return false;
}
return true;
}
/// <summary>
/// Получить РГ и ЗО из шорткода. Сохранить в КЭШ.
/// </summary>
/// <param name="templateId"></param>
/// <returns></returns>
private async Task<(string WorkGroup, string ResponseArea, Guid jobId)> GetTemplatePropsAsync(Guid templateId)
//private async Task<(string WorkGroup, string ResponseArea, Guid jobId)> GetTemplatePropsAsync(Guid templateId)
private async Task<TemplateCache> GetTemplatePropsAsync(Guid templateId)
{
logger.LogDebug("Дозагружаю недостающие данные из БД для шаблона {TemplateId}", templateId);
@@ -290,7 +354,20 @@ namespace PARR.Core.Services.Workload.Implementations
if (template == null)
{
logger.LogWarning("Шаблон {TemplateId} не найден в БД", templateId);
return ($"РГ {templateId}", $"ЗО {templateId}", templateId);
//return ($"РГ {templateId}", $"ЗО {templateId}", templateId);
return new TemplateCache
{
Timestamp = DateTimeOffset.UtcNow,
Source = "",
Data = new TemplateCacheDto
{
JobGroupId = Guid.Empty,
JobId = Guid.Empty,
TemplateId = templateId,
ResponseArea = $"ЗО {templateId}",
WorkGroup = $"РГ {templateId}"
}
};
}
var (workGroup, responseArea) = await ApplyShortcodesAsync(template);
@@ -311,7 +388,8 @@ namespace PARR.Core.Services.Workload.Implementations
);
logger.LogDebug("Добавлена недостающая запись в кэш. {TemplateId}, '{WorkGroup}', '{ResponseArea}'", templateId, workGroup, responseArea);
return (workGroup, responseArea, template.JobId);
//return (workGroup, responseArea, template.JobId);
return cacheItem;
}
/// <summary>
@@ -337,9 +415,9 @@ namespace PARR.Core.Services.Workload.Implementations
/// <param name="offset"></param>
/// <param name="filterParam">Наименование ЗО или РГ или null</param>
/// <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);
}
@@ -355,9 +433,9 @@ namespace PARR.Core.Services.Workload.Implementations
/// <param name="offset"></param>
/// <param name="filterParam">Наименование ЗО или РГ или null</param>
/// <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);
}
@@ -384,10 +462,10 @@ namespace PARR.Core.Services.Workload.Implementations
/// <param name="offset"></param>
/// <param name="filterParam"></param>
/// <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>();
@@ -415,10 +493,10 @@ namespace PARR.Core.Services.Workload.Implementations
var keyStr = string.Empty;
if (filterParam == null)
if (responseArea == null && workGroup == null)
keyStr = redisCacheService.GetKey(keyList.ToArray());
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);

View File

@@ -1,14 +1,18 @@
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.Job;
using PARR.Core.Repositories.Interfaces.TaskRepositories;
using PARR.Core.Services.NextRunServices;
using PARR.Core.Services.TaskServices.Helpers;
using PARR.Core.Services.Workload.Interfaces;
using PARR.Core.Services.Workload.Models;
using PARR.Domain.Cache.Models;
using PARR.Domain.DTOs.Workload;
using PARR.Domain.Entities;
using PARR.Domain.Enums;
using PARR.Domain.Enums.Workload;
namespace PARR.Core.Services.Workload.Implementations
{
@@ -20,8 +24,7 @@ namespace PARR.Core.Services.Workload.Implementations
private readonly ITaskRepository taskRepository;
private readonly IMapper mapper;
private readonly IJobRepository jobRepository;
//private readonly ITaskManagementService taskManagementService;
private readonly ITemplateRepository templateRepository;
public WorkloadService(
ILogger<WorkloadService> logger,
@@ -29,8 +32,8 @@ namespace PARR.Core.Services.Workload.Implementations
INextRunService nextRunService,
ITaskRepository taskRepository,
IMapper mapper,
IJobRepository jobRepository
//ITaskManagementService taskManagementService
IJobRepository jobRepository,
ITemplateRepository templateRepository
)
{
this.logger = logger;
@@ -39,24 +42,29 @@ namespace PARR.Core.Services.Workload.Implementations
this.taskRepository = taskRepository;
this.mapper = mapper;
this.jobRepository = jobRepository;
//this.taskManagementService = taskManagementService;
this.templateRepository = templateRepository;
}
public async Task<WorkloadReport?> GetWorkloadReport(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}",
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());
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)
{
logger.LogDebug("Вернул отчет из кэша.");
@@ -86,26 +94,30 @@ 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 = "По зоне ответственности";
// Фильтруем по ЗО
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))
statistics.Add(BuildStatisticItem(group.Key, group, reportDays, offset));
//statistics.Add(BuildStatisticItem(filterParam!, filteredRA, reportDays, offset));
statistics.Add(BuildStatisticItem(group.Key, null, group, 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 = "По рабочей группе";
// Фильтруем по РГ
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 из бд
var jobIds = filteredWG.Select(t => t.JobId).ToHashSet();
@@ -117,16 +129,17 @@ 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;
}
var report = new WorkloadReport
{
Type = reportType,
DisplayName = displayName,
SubfilterName = filterParam,
SubfilterName = string.Join(", ", new[] { responseArea, workGroup }),
ReportCacheCreatedAt = DateTimeOffset.UtcNow,
TemplateCacheCreatedAt = templates.FirstOrDefault()?.CacheDate ?? DateTimeOffset.MinValue,
Days = reportDays,
@@ -134,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("Отчет построен и сохранен в кэш");
return report;
@@ -174,6 +187,153 @@ namespace PARR.Core.Services.Workload.Implementations
}
public async Task<List<WorkloadTemplateReport>> GetTemplateReportAsync(
WorkloadTemplateReportType reportType,
string filter,
WorkloadTemplateReportState state,
DateOnly date,
TimeSpan offset,
string? responseArea,
string? workGroup
)
{
if (string.IsNullOrWhiteSpace(filter))
{
logger.LogWarning("Не передано значение фильтра");
throw new ArgumentException(nameof(filter), "Не передано значение фильтра.");
}
// Template cache вообще есть, он актуальный?
if (!await workloadCacheService.IsTemplateReportCacheActualAsync())
{
logger.LogDebug("Не могу рассчитать отчет Workload, так как нет основного кэш или он не актуальный.");
return new List<WorkloadTemplateReport>();
}
var trimmedFilter = filter.Trim();
// Формируем дату и время начала и конца
var dtStart = new DateTimeOffset(date.Year, date.Month, date.Day, 0, 0, 0, offset);
var dtEnd = dtStart.AddDays(1);
// Конвертируем в UTC для совместимости с PostgreSQL timestamptz
var dtStartUtc = dtStart.ToUniversalTime();
var dtEndUtc = dtEnd.ToUniversalTime();
// Запрос к БД
var query = templateRepository.Get().AsNoTracking()
.Where(t => dtStartUtc <= t.NextRun && t.NextRun < dtEndUtc);
// Фильтруем по состоянию
switch (state)
{
case WorkloadTemplateReportState.All:
break;
case WorkloadTemplateReportState.Activated:
query = query.Where(t => t.IsActiveTemplate && t.IsActiveSchedule);
break;
case WorkloadTemplateReportState.Deactivated:
query = query.Where(t => !t.IsActiveTemplate || !t.IsActiveSchedule);
break;
}
// Если тип - работы, получим из бд сразу нужные работы
if (reportType == WorkloadTemplateReportType.Job)
{
if (!Guid.TryParse(trimmedFilter, out Guid jobId))
throw new ArgumentException("Значение фильтра не является Guid");
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();
if (dbTemplates.Count == 0)
return new List<WorkloadTemplateReport>();
// Если тип - работы, и нет дополнительных фильтров по ЗО и РГ, не полезем в кэш, вернем сразу
if (reportType == WorkloadTemplateReportType.Job && string.IsNullOrWhiteSpace(responseArea) && string.IsNullOrWhiteSpace(workGroup))
{
// Тут кэш не нужен, вернем сразу из бд (нет фильтров по РГ и ЗО, нужен список всех работ на дату)
return FilterWorkloadJobTemplateReport(dbTemplates, null);
}
// Получаем из кэша шаблоны (если их в кэше нет, догружаем в кэш)
var cacheData = await workloadCacheService.GetTemplateReportDataByIdsAsync(dbTemplates.Select(t => t.TemplateId).ToHashSet());
if (cacheData == null)
throw new Exception("Из кэша не получили значения.");
if (dbTemplates.Count != cacheData.Count)
logger.LogWarning("Количество шаблонов в БД {DbCount} не совпадает с количеством шаблонов полученных из кэша {CacheCount}", dbTemplates.Count, cacheData.Count);
// Дополнительные фильтры
var trimmedResponseArea = responseArea?.Trim();
var trimmedWorkGroup = workGroup?.Trim();
// Фильтр по типу
switch (reportType)
{
case WorkloadTemplateReportType.Job:
// Смотрим есть ли дополнительные фильтры
if (!string.IsNullOrEmpty(trimmedResponseArea) || !string.IsNullOrEmpty(trimmedWorkGroup))
{
// Фильтруем из кэша
IEnumerable<TemplateCache> cacheTemplatesJobs = cacheData;
// Фильтруем по ЗО если есть
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);
case WorkloadTemplateReportType.WorkGroup:
var cacheTemplatesWg = cacheData.Where(t => string.Equals(t.Data.WorkGroup, trimmedFilter, StringComparison.OrdinalIgnoreCase));
// Дополнительный фильтр по ЗО, если есть
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:
var cacheTemplatesRa = cacheData.Where(t => string.Equals(t.Data.ResponseArea, trimmedFilter, StringComparison.OrdinalIgnoreCase));
// Дополнительный фильтр по РГ если есть
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);
return new List<WorkloadTemplateReport>();
}
private List<WorkloadTemplateReport> FilterWorkloadJobTemplateReport(List<WorkloadTemplateReport> dbTemplates, HashSet<Guid>? filteredCacheTemplates)
{
if (filteredCacheTemplates == null)
return dbTemplates;
// Отсекаем шаблоны которых нет в кэше
return dbTemplates.Where(t => filteredCacheTemplates.Contains(t.TemplateId)).ToList();
}
/// <summary>
/// Получить список дней для отчета, с отметкой выходной/рабочий
/// </summary>
@@ -255,13 +415,12 @@ namespace PARR.Core.Services.Workload.Implementations
/// <param name="reportDays"></param>
/// <param name="offset"></param>
/// <returns></returns>
private WorkloadStatisticItem BuildStatisticItem(string title, IEnumerable<TemplateReportData> groupTemplates, List<WorkloadDayItem> reportDays, TimeSpan offset)
private WorkloadStatisticItem BuildStatisticItem(string title, Guid? objId, IEnumerable<TemplateReportData> groupTemplates, List<WorkloadDayItem> 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 };
}
}
}

View File

@@ -1,5 +1,6 @@
using PARR.Domain.DTOs.Workload;
using PARR.Domain.Enums;
using Microsoft.Extensions.Primitives;
using PARR.Domain.DTOs.Workload;
using PARR.Domain.Enums.Workload;
namespace PARR.Core.Services.Workload.Interfaces
{
@@ -21,8 +22,31 @@ namespace PARR.Core.Services.Workload.Interfaces
/// <param name="dateStart"></param>
/// <param name="durationDays"></param>
/// <param name="offset"></param>
/// <param name="filterParam"></param>
/// <param name="responseArea"></param>
/// <param name="workGroup"></param>
/// <returns></returns>
Task<WorkloadReport?> GetWorkloadReport(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>
/// <param name="reportType"></param>
/// <param name="filter">Фильтр, значение в зависимости от reportType</param>
/// <param name="state"></param>
/// <param name="date"></param>
/// <param name="offset"></param>
/// <param name="responseArea">Дополнительный фильтр при отчете по шаблонам, ЗО</param>
/// <param name="workGorup">Дополнительный фильтр при отчете по шаблонам, РГ</param>
/// <returns></returns>
Task<List<WorkloadTemplateReport>> GetTemplateReportAsync(
WorkloadTemplateReportType reportType,
string filter,
WorkloadTemplateReportState state,
DateOnly date,
TimeSpan offset,
string? responseArea,
string? workGroup
);
}
}