feat(api,core,domain): StatWorkloadTemplate - статистика загруженности, шаблоны

This commit is contained in:
Mikhail Trubnikov
2026-06-04 17:20:16 +10:00
parent 1f4d338422
commit 1baa8f2296
15 changed files with 349 additions and 168 deletions

View File

@@ -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 Наряды

View File

@@ -1,4 +1,4 @@
using PARR.Domain.Enums;
using PARR.Domain.Enums.Workload;
namespace PARR.API.Contracts.V1.Responses.Statistics
{

View File

@@ -0,0 +1,4 @@
namespace PARR.API.Contracts.V1.Responses.Statistics
{
public record StatWorkloadTemplateReportResponse(Guid TemplateId, string Name, DateTimeOffset NextRun, bool IsActiveTemplate, bool IsActiveSchedule);
}

View File

@@ -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;
}
*/
}
}

View File

@@ -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
{
@@ -153,7 +153,7 @@ namespace PARR.API.Controllers.V1.Statistics
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, filter?.Trim());
if (report == null)
return BadRequest(new Response<string?>(null, false, new List<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }, "empty"));

View File

@@ -0,0 +1,65 @@
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.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)
{
if (string.IsNullOrWhiteSpace(filter))
{
logger.LogWarning("Не передано значение фильтра");
throw new ArgumentException(nameof(filter), "Не передано значение фильтра.");
}
var report = await workloadService.GetTemplateReportAsync(reportType, filter, state, date, offset.TimeZoneOffset);
var response = mapper.Map<List<StatWorkloadTemplateReportResponse>>(report);
return Ok(new Response<List<StatWorkloadTemplateReportResponse>>(response, true));
}
}
}

View File

@@ -460,6 +460,12 @@ namespace PARR.API.MappingProfiles
#endregion
#region StatWorkloadTemplate
CreateMap<WorkloadTemplateReport, StatWorkloadTemplateReportResponse>();
#endregion
#region Task

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>

View File

@@ -1,6 +1,7 @@
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;
@@ -9,6 +10,7 @@ using PARR.Core.Services.Workload.Interfaces;
using PARR.Core.Services.Workload.Models;
using PARR.Domain.DTOs.Workload;
using PARR.Domain.Enums;
using PARR.Domain.Enums.Workload;
namespace PARR.Core.Services.Workload.Implementations
{
@@ -20,8 +22,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 +30,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,11 +40,11 @@ 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? filterParam)
{
logger.LogDebug("Начинаю формировать отчет о загруженности по параметрам: reportType '{ReportType}', dateStart {DateStart}, durationDays {DurationDays}, offset {Offset}",
reportType.ToString(), dateStart, durationDays, offset);
@@ -174,6 +175,97 @@ namespace PARR.Core.Services.Workload.Implementations
}
public async Task<List<WorkloadTemplateReport>> GetTemplateReportAsync(WorkloadTemplateReportType reportType, string filter, WorkloadTemplateReportState state, DateOnly date, TimeSpan offset)
{
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");
return await query.Where(t => t.JobId == jobId)
.OrderBy(t => t.Name)
.Select(t => new WorkloadTemplateReport(t.Id, t.Name, t.NextRun, t.IsActiveTemplate, t.IsActiveSchedule))
.ToListAsync();
}
// Тип "не работы", загружаем шаблоны
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>();
// Получаем из кэша шаблоны (если их в кэше нет, догружаем в кэш)
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);
// Фильтр по типу
switch (reportType)
{
case WorkloadTemplateReportType.WorkGroup:
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();
case WorkloadTemplateReportType.ResponseArea:
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();
}
logger.LogWarning("Не обработанный тип отчета {ReportType}", reportType);
return new List<WorkloadTemplateReport>();
}
/// <summary>
/// Получить список дней для отчета, с отметкой выходной/рабочий
/// </summary>
@@ -262,6 +354,5 @@ namespace PARR.Core.Services.Workload.Implementations
return new WorkloadStatisticItem { DailyMetrics = dailyMetrics, Summary = summary, Title = title };
}
}
}

View File

@@ -1,5 +1,5 @@
using PARR.Domain.DTOs.Workload;
using PARR.Domain.Enums;
using PARR.Domain.Enums.Workload;
namespace PARR.Core.Services.Workload.Interfaces
{
@@ -23,6 +23,18 @@ namespace PARR.Core.Services.Workload.Interfaces
/// <param name="offset"></param>
/// <param name="filterParam"></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? filterParam);
/// <summary>
/// Получить отчет - список шаблонов на дату согласно фильтрам
/// </summary>
/// <param name="reportType"></param>
/// <param name="filter">Фильтр, значение в зависимости от reportType</param>
/// <param name="state"></param>
/// <param name="date"></param>
/// <param name="offset"></param>
/// <returns></returns>
Task<List<WorkloadTemplateReport>> GetTemplateReportAsync(WorkloadTemplateReportType reportType, string filter, WorkloadTemplateReportState state, DateOnly date, TimeSpan offset);
}
}

View File

@@ -1,4 +1,4 @@
using PARR.Domain.Enums;
using PARR.Domain.Enums.Workload;
namespace PARR.Domain.DTOs.Workload
{

View 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);
}

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace PARR.Domain.Enums
namespace PARR.Domain.Enums.Workload
{
/// <summary>
/// Тип отчета о загруженности

View 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
}
}

View 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
}
}