Merge branch 'dev' of http://gitlab.dvgd.oao.rzd/devptk/parr/parr_api into dev
This commit is contained in:
@@ -311,6 +311,11 @@
|
||||
public const string ReportInfo = BaseStat + "/workload/report-info";
|
||||
}
|
||||
|
||||
public static class StatWorkloadTemplate
|
||||
{
|
||||
public const string GetWorkloadTemplateReport = BaseStat + "/workload/templates/{reportType}/{filter}/{state}/{date}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Наряды
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace PARR.API.Contracts.V1.Requests.Queries
|
||||
{
|
||||
public record StatWorkloadTemplateReqportQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Дополнительный фильтр для работ - ЗО
|
||||
/// </summary>
|
||||
public string? ResponseArea { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Дополнительный фильтр для работ - РГ
|
||||
/// </summary>
|
||||
public string? WorkGroup { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using PARR.Domain.Enums;
|
||||
using PARR.Domain.Enums.Workload;
|
||||
|
||||
namespace PARR.API.Contracts.V1.Responses.Statistics
|
||||
{
|
||||
@@ -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<WorkloadDailyItemResponse> DailyMetrics { get; init; }
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace PARR.API.Contracts.V1.Responses.Statistics
|
||||
{
|
||||
public record StatWorkloadTemplateReportResponse(Guid TemplateId, string Name, DateTimeOffset NextRun, bool IsActiveTemplate, bool IsActiveSchedule);
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PARR.API.Contracts.V1;
|
||||
using PARR.API.Contracts.V1.Requests;
|
||||
using PARR.API.Contracts.V1.Requests.Queries;
|
||||
using PARR.API.Contracts.V1.Responses;
|
||||
using PARR.API.Contracts.V1.Responses.Base;
|
||||
using PARR.API.Contracts.V1.Responses.Statistics;
|
||||
using PARR.API.Controllers.V1.Base;
|
||||
using PARR.API.Services.Interfaces;
|
||||
using PARR.Core.Repositories.Interfaces;
|
||||
|
||||
namespace PARR.API.Controllers.V1.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Статистика загрузки регламентными работами по рабочим группам
|
||||
/// </summary>
|
||||
public class StatWorkGroupWorkLoadController : BaseApiController
|
||||
{
|
||||
private readonly ITemplateRepository templateService;
|
||||
//private readonly IWorkLoadService workLoadService;
|
||||
//private readonly IWorkGroupService workGroupService;
|
||||
private readonly IMapper mapper;
|
||||
private readonly ILogger<StatWorkGroupWorkLoadController> logger;
|
||||
|
||||
public StatWorkGroupWorkLoadController(
|
||||
ITemplateRepository templateService,
|
||||
//IWorkLoadService workLoadService,
|
||||
//IWorkGroupService workGroupService,
|
||||
IMapper mapper,
|
||||
ILogger<StatWorkGroupWorkLoadController> logger
|
||||
)
|
||||
{
|
||||
this.templateService = templateService;
|
||||
//this.workLoadService = workLoadService;
|
||||
//this.workGroupService = workGroupService;
|
||||
this.mapper = mapper;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Статистика загрузки регламентными работами рабочих групп по ЗО
|
||||
/// </summary>
|
||||
/// <param name="responseAreaCode"></param>
|
||||
/// <param name="requestQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet(ApiRoutes.StatWorkGroupWorkLoad.Get)]
|
||||
public async Task<IActionResult> Get([FromRoute] int responseAreaCode, [FromQuery] WorkloadBaseQuery requestQuery)
|
||||
{
|
||||
var response = await GetStatisticsAsync(responseAreaCode, requestQuery);
|
||||
|
||||
return Ok(new Response<List<StatWorkGroupWorkLoadResponse>>(response, true));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Статистика загрузки РГ по списку РГ
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost(ApiRoutes.StatWorkGroupWorkLoad.GetByWorkGroups)]
|
||||
public async Task<IActionResult> GetByWorkGroups([FromBody] StatWorkGroupsWorkLoadRequest request)
|
||||
{
|
||||
var response = await GetStatisticsAsync(null, request.Filter, request.WorkGroups);
|
||||
|
||||
return Ok(new Response<List<StatWorkGroupWorkLoadResponse>>(response, true));
|
||||
}
|
||||
|
||||
|
||||
private async Task<List<StatWorkGroupWorkLoadResponse>> GetStatisticsAsync(int? responseAreaCode, WorkloadBaseQuery requestQuery, List<Guid>? workGroups = null)
|
||||
{
|
||||
//защита от дурака, чтоб не выгрузить всю БД
|
||||
if (responseAreaCode == null && (workGroups == null || workGroups.Count() == 0))
|
||||
{
|
||||
logger.LogWarning($"Пытались выполнить запрос получения статситики с нулевыми параметрами {nameof(responseAreaCode)}, {nameof(workGroups)}. Controller {nameof(StatWorkGroupWorkLoadController)}");
|
||||
return new List<StatWorkGroupWorkLoadResponse>();
|
||||
}
|
||||
|
||||
|
||||
var daysList = workLoadService.GetDatesForPeriod(requestQuery);
|
||||
|
||||
IQueryable<DAL.Models.Template> query = templateService.Get()
|
||||
.Include(t => t.Host);
|
||||
|
||||
query = workLoadService.FilterTemplatesQuery(query, requestQuery);
|
||||
|
||||
if (responseAreaCode.HasValue)
|
||||
query = query.Where(t => t.Host!.WorkGroup!.ResponseAreaCode == responseAreaCode.Value);
|
||||
|
||||
if (workGroups != null && workGroups.Count() > 0)
|
||||
query = query.Where(t => workGroups.Any(x => x == t.Host!.WorkGroupId.Value));
|
||||
|
||||
|
||||
// var groupedQuery = query.GroupBy(t => new { t.NextRun.DateTime.Date, t.Host!.WorkGroupId })
|
||||
var groupedQuery = query.GroupBy(t => new { t.NextRun.AddHours(requestQuery.TimeZoneOffsetHours).DateTime.Date, t.Host!.WorkGroupId })
|
||||
.Select(t => new
|
||||
{
|
||||
WorkGroupId = t.Key.WorkGroupId,
|
||||
Date = t.Key.Date,
|
||||
TemplateCount = t.Count()
|
||||
});
|
||||
|
||||
var queryResult = await groupedQuery.ToListAsync();
|
||||
|
||||
|
||||
IQueryable<DAL.Models.WorkGroup> workGroupsQuery = workGroupService.Get()
|
||||
.Include(t => t.ResponseArea);
|
||||
|
||||
if (responseAreaCode.HasValue)
|
||||
workGroupsQuery = workGroupsQuery.Where(t => t.ResponseAreaCode == responseAreaCode);
|
||||
|
||||
if (workGroups != null && workGroups.Count() > 0)
|
||||
workGroupsQuery = workGroupsQuery.Where(t => workGroups.Any(x => x == t.Id));
|
||||
|
||||
|
||||
var allWorkGroups = await workGroupsQuery.ToListAsync();
|
||||
|
||||
var response = new List<StatWorkGroupWorkLoadResponse>();
|
||||
|
||||
allWorkGroups.ForEach(wg =>
|
||||
{
|
||||
var workLoads = daysList.Select(t => new StatWorkLoadDataResponse
|
||||
{
|
||||
Date = t,
|
||||
TemplateCount = queryResult.FirstOrDefault(x => x.WorkGroupId == wg.Id && DateOnly.FromDateTime(x.Date) == t)?.TemplateCount ?? 0
|
||||
}).OrderBy(t => t.Date).ToList();
|
||||
|
||||
response.Add(new StatWorkGroupWorkLoadResponse
|
||||
{
|
||||
WorkGroup = mapper.Map<WorkGroupResponse>(wg),
|
||||
WorkLoad = workLoads
|
||||
});
|
||||
});
|
||||
|
||||
response = response.OrderBy(t => t.WorkGroup.Name).ToList();
|
||||
|
||||
return response;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ using PARR.Core.Services.Workload.Interfaces;
|
||||
using PARR.Domain.Common.Roles;
|
||||
using PARR.Domain.Entities.Base.History;
|
||||
using PARR.Domain.Enums;
|
||||
using PARR.Domain.Enums.Workload;
|
||||
using PARR.Domain.Exceptions;
|
||||
using static PARR.API.Contracts.V1.ApiRoutes;
|
||||
|
||||
namespace PARR.API.Controllers.V1.Statistics
|
||||
{
|
||||
@@ -57,7 +57,7 @@ namespace PARR.API.Controllers.V1.Statistics
|
||||
[HttpGet(ApiRoutes.Workload.WorkGroup)]
|
||||
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>
|
||||
/// <returns></returns>
|
||||
[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))
|
||||
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)]
|
||||
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))
|
||||
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>
|
||||
/// <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 localTime = utcNow.ToOffset(offset);
|
||||
var dateStart = DateOnly.FromDateTime(localTime.Date);
|
||||
|
||||
var report = await workloadService.GetWorkloadReport(reportType, dateStart, durationDays, offset, filter?.Trim());
|
||||
var report = await workloadService.GetWorkloadReportAsync(reportType, dateStart, durationDays, offset, responseArea?.Trim(), workGroup?.Trim());
|
||||
|
||||
if (report == null)
|
||||
return BadRequest(new Response<string?>(null, false, new List<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }, "empty"));
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using AutoMapper;
|
||||
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;
|
||||
using PARR.Core.Services.Workload.Interfaces;
|
||||
using PARR.Domain.Common.Roles;
|
||||
using PARR.Domain.Enums.Workload;
|
||||
|
||||
namespace PARR.API.Controllers.V1.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Статситика загруженности - шаблоны
|
||||
/// </summary>
|
||||
[Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)]
|
||||
public class StatWorkloadTemplateController : BaseApiController
|
||||
{
|
||||
private readonly IMapper mapper;
|
||||
private readonly IWorkloadService workloadService;
|
||||
private readonly ILogger<StatWorkloadTemplateController> logger;
|
||||
|
||||
public StatWorkloadTemplateController(
|
||||
IMapper mapper,
|
||||
IWorkloadService workloadService,
|
||||
ILogger<StatWorkloadTemplateController> logger
|
||||
)
|
||||
{
|
||||
this.mapper = mapper;
|
||||
this.workloadService = workloadService;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Отчет о загруженности. Шаблоны
|
||||
/// </summary>
|
||||
/// <param name="reportType">Тип отчета (ЗО, РГ, Работа)</param>
|
||||
/// <param name="filter">Фильтр, значиение в зависимости от типа отчета</param>
|
||||
/// <param name="state">Все, активированные, деактивированные</param>
|
||||
/// <param name="date">Дата</param>
|
||||
/// <param name="offset">Часовой пояс</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[HttpGet(ApiRoutes.StatWorkloadTemplate.GetWorkloadTemplateReport)]
|
||||
public async Task<IActionResult> GetWorkloadTemplateReport(
|
||||
[FromRoute] WorkloadTemplateReportType reportType,
|
||||
[FromRoute] string filter,
|
||||
[FromRoute] WorkloadTemplateReportState state,
|
||||
[FromRoute] DateOnly date,
|
||||
[FromQuery] TimeZoneOffsetClient offset,
|
||||
[FromQuery] StatWorkloadTemplateReqportQuery query
|
||||
)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
logger.LogWarning("Не передано значение фильтра");
|
||||
throw new ArgumentException(nameof(filter), "Не передано значение фильтра.");
|
||||
}
|
||||
|
||||
var report = await workloadService.GetTemplateReportAsync(reportType, filter.Trim(), state, date, offset.TimeZoneOffset, query.ResponseArea?.Trim(), query.WorkGroup?.Trim());
|
||||
|
||||
var response = mapper.Map<List<StatWorkloadTemplateReportResponse>>(report);
|
||||
|
||||
return Ok(new Response<List<StatWorkloadTemplateReportResponse>>(response, true));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -460,6 +460,12 @@ namespace PARR.API.MappingProfiles
|
||||
|
||||
#endregion
|
||||
|
||||
#region StatWorkloadTemplate
|
||||
|
||||
CreateMap<WorkloadTemplateReport, StatWorkloadTemplateReportResponse>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Task
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using PARR.Domain.Enums;
|
||||
using PARR.Domain.Enums.Workload;
|
||||
|
||||
namespace PARR.Domain.DTOs.Workload
|
||||
{
|
||||
@@ -73,6 +73,12 @@ namespace PARR.Domain.DTOs.Workload
|
||||
{
|
||||
public required string Title { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// ИД строки, если есть.
|
||||
/// Например JobId
|
||||
/// </summary>
|
||||
public Guid? ObjId { get; init; }
|
||||
|
||||
public required WorkloadSummary Summary { get; init; }
|
||||
|
||||
public required List<WorkloadDailyItem> DailyMetrics { get; init; }
|
||||
|
||||
10
PARR.Domain/DTOs/Workload/WorkloadTemplateReport.cs
Normal file
10
PARR.Domain/DTOs/Workload/WorkloadTemplateReport.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace PARR.Domain.DTOs.Workload
|
||||
{
|
||||
/// <summary>
|
||||
/// Отчет о шаблонах, для отчета о нагруженности
|
||||
/// </summary>
|
||||
/// <param name="TemplateId"></param>
|
||||
/// <param name="Name"></param>
|
||||
/// <param name="NextRun"></param>
|
||||
public record WorkloadTemplateReport(Guid TemplateId, string Name, DateTimeOffset NextRun, bool IsActiveTemplate, bool IsActiveSchedule);
|
||||
}
|
||||
22
PARR.Domain/Enums/Workload/WorkloadJobSubfliterType.cs
Normal file
22
PARR.Domain/Enums/Workload/WorkloadJobSubfliterType.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PARR.Domain.Enums.Workload
|
||||
{
|
||||
/// <summary>
|
||||
/// Дополнительный фильтр для работ.
|
||||
/// Отчет Workload, шаблоны
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum WorkloadJobSubfliterType
|
||||
{
|
||||
/// <summary>
|
||||
/// ЗО
|
||||
/// </summary>
|
||||
ResponseArea = 0,
|
||||
|
||||
/// <summary>
|
||||
/// РГ
|
||||
/// </summary>
|
||||
WorkGroup=1
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PARR.Domain.Enums
|
||||
namespace PARR.Domain.Enums.Workload
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип отчета о загруженности
|
||||
26
PARR.Domain/Enums/Workload/WorkloadTemplateReportState.cs
Normal file
26
PARR.Domain/Enums/Workload/WorkloadTemplateReportState.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PARR.Domain.Enums.Workload
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип отчета о загруженности, состояние
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum WorkloadTemplateReportState
|
||||
{
|
||||
/// <summary>
|
||||
/// Все
|
||||
/// </summary>
|
||||
All = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Активированные
|
||||
/// </summary>
|
||||
Activated = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Деактивированные
|
||||
/// </summary>
|
||||
Deactivated = 2
|
||||
}
|
||||
}
|
||||
26
PARR.Domain/Enums/Workload/WorkloadTemplateReportType.cs
Normal file
26
PARR.Domain/Enums/Workload/WorkloadTemplateReportType.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PARR.Domain.Enums.Workload
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип отчета о загруженности, шаблоны
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum WorkloadTemplateReportType
|
||||
{
|
||||
/// <summary>
|
||||
/// Зона ответственности
|
||||
/// </summary>
|
||||
ResponseArea = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Рабочая группа
|
||||
/// </summary>
|
||||
WorkGroup = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Работа
|
||||
/// </summary>
|
||||
Job = 2
|
||||
}
|
||||
}
|
||||
@@ -504,7 +504,7 @@ namespace PARR.Infrastructure.Redis
|
||||
|
||||
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)
|
||||
.ToLower();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user