diff --git a/PARR.API/Contracts/V1/ApiRoutes.cs b/PARR.API/Contracts/V1/ApiRoutes.cs
index 594ad678..3108d749 100644
--- a/PARR.API/Contracts/V1/ApiRoutes.cs
+++ b/PARR.API/Contracts/V1/ApiRoutes.cs
@@ -299,6 +299,10 @@
public static class Workload
{
public const string Build = BaseStat + "/workload/build";
+
+ public const string WorkGroup = BaseStat + "/workload/work-groups";
+
+ public const string ResponseArea = BaseStat + "/workload/response-areas";
}
#endregion
diff --git a/PARR.API/Contracts/V1/Requests/BaseRequests/TimeZoneOffsetClient.cs b/PARR.API/Contracts/V1/Requests/BaseRequests/TimeZoneOffsetClient.cs
index cead2206..c3cfc1ee 100644
--- a/PARR.API/Contracts/V1/Requests/BaseRequests/TimeZoneOffsetClient.cs
+++ b/PARR.API/Contracts/V1/Requests/BaseRequests/TimeZoneOffsetClient.cs
@@ -7,10 +7,10 @@ namespace PARR.API.Contracts.V1.Requests.BaseRequests
///
public class TimeZoneOffsetClient
{
- ///
- /// Удалить TimeZoneOffsetHours и не использовать!!!!!! Использовать только в минутах TimeZoneOffsetMinutes
- ///
- public int TimeZoneOffsetHours { get; set; } = 0;
+ /////
+ ///// Удалить TimeZoneOffsetHours и не использовать!!!!!! Использовать только в минутах TimeZoneOffsetMinutes
+ /////
+ //public int TimeZoneOffsetHours { get; set; } = 0;
///
/// Часовая зона клиента, в минутах
diff --git a/PARR.API/Contracts/V1/Requests/Queries/WorkloadBaseQuery.cs b/PARR.API/Contracts/V1/Requests/Queries/WorkloadBaseQuery.cs
index 30f7a355..33e01e8e 100644
--- a/PARR.API/Contracts/V1/Requests/Queries/WorkloadBaseQuery.cs
+++ b/PARR.API/Contracts/V1/Requests/Queries/WorkloadBaseQuery.cs
@@ -1,18 +1,13 @@
-using PARR.API.Contracts.V1.Requests.BaseRequests;
-
-namespace PARR.API.Contracts.V1.Requests.Queries
+namespace PARR.API.Contracts.V1.Requests.Queries
{
///
/// Базовый запрос по загрузке по ЗО, РГ...
///
- public class WorkloadBaseQuery: TimeZoneOffsetClient
+ public class WorkloadBaseQuery
{
- public bool? IsActiveTemplate { get; set; }
-
- public bool? IsActiveSchedule { get; set; }
-
- public bool? IsMonth { get; set; }
-
- //public int TimeZoneOffsetHours { get; set; } = 0;
+ ///
+ /// Период отчетности (по умолчанию 7 дней)
+ ///
+ public int DurationDays { get; set; } = 7;
}
}
diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatResponseAreaWorkLoadResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatResponseAreaWorkLoadResponse.cs
deleted file mode 100644
index 5df79513..00000000
--- a/PARR.API/Contracts/V1/Responses/Statistics/StatResponseAreaWorkLoadResponse.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace PARR.API.Contracts.V1.Responses.Statistics
-{
- public class StatResponseAreaWorkLoadResponse : StatWorkLoadDataBaseResponse
- {
- //public required ResponseAreaResponse ResponseArea { get; set; }
- }
-}
diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkGroupWorkLoadResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatWorkGroupWorkLoadResponse.cs
deleted file mode 100644
index 9cf20874..00000000
--- a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkGroupWorkLoadResponse.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace PARR.API.Contracts.V1.Responses.Statistics
-{
- public class StatWorkGroupWorkLoadResponse : StatWorkLoadDataBaseResponse
- {
- //public required WorkGroupResponse WorkGroup { get; set; }
- }
-}
diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkLoadDataBaseResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatWorkLoadDataBaseResponse.cs
deleted file mode 100644
index 7644388f..00000000
--- a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkLoadDataBaseResponse.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace PARR.API.Contracts.V1.Responses.Statistics
-{
- public class StatWorkLoadDataBaseResponse
- {
- public List WorkLoad { get; set; }
-
- public int WorksCount => WorkLoad?.Sum(t => t.TemplateCount) ?? 0;
-
- public double AverageWorksCount
- {
- get
- {
- int divider = WorkLoad.Where(t => t.TemplateCount > 0).Count();
- if (divider > 0)
- return Math.Round((double)WorksCount / divider, 2);
-
- return 0;
- }
- }//=> Math.Round((double)WorksCount / WorkLoad.Where(t => t.TemplateCount > 0).Count(), 2);
- }
-
- public class StatWorkLoadDataResponse
- {
- //public DateTimeOffset Date { get; set; }
- public DateOnly Date { get; set; }
- public int TemplateCount { get; set; }
- }
-}
diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkWorkLoadResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatWorkWorkLoadResponse.cs
deleted file mode 100644
index 9764fc66..00000000
--- a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkWorkLoadResponse.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace PARR.API.Contracts.V1.Responses.Statistics
-{
- public class StatWorkWorkLoadResponse : StatWorkLoadDataBaseResponse
- {
- //public required ApplicationInWorkBaseResponse Job { get; set; }
-
- //public required WorkGroupResponse WorkGroup { get; set; }
- }
-}
diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatWorkloadReportResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatWorkloadReportResponse.cs
new file mode 100644
index 00000000..c1dc7b4b
--- /dev/null
+++ b/PARR.API/Contracts/V1/Responses/Statistics/StatWorkloadReportResponse.cs
@@ -0,0 +1,90 @@
+using PARR.Domain.Enums;
+
+namespace PARR.API.Contracts.V1.Responses.Statistics
+{
+ ///
+ /// Response отчета о загруженности
+ ///
+ public record StatWorkloadReportResponse
+ {
+ ///
+ /// Тип отчета (все ЗО, все РГ, одна ЗО, одна РГ)
+ ///
+ public WorkloadReportType Type { get; init; }
+
+ ///
+ /// Название отчета (По всем ЗО, по одной ЗО)
+ ///
+ public required string DisplayName { get; init; }
+
+ ///
+ /// Подзаголовок, наименование конкретной ЗО/РГ
+ ///
+ public string? SubfilterName { get; init; }
+
+ ///
+ /// Дата формирования отчета
+ ///
+ public DateTimeOffset ReportCacheCreatedAt { get; init; }
+
+ ///
+ /// Дата формирования кэша ЗО и РГ
+ ///
+ public DateTimeOffset TemplateCacheCreatedAt { get; init; }
+
+ ///
+ /// Список дней
+ ///
+ public required List Days { get; init; }
+
+ ///
+ /// Нагруженность
+ ///
+ public required List Statistics { get; init; }
+ }
+
+ public record WorkloadSummaryResponse
+ {
+ public required WorkloadSummaryItemResponse Total { get; init; }
+
+ public required WorkloadSummaryItemResponse Activated { get; init; }
+
+ public required WorkloadSummaryItemResponse Deactivated { get; init; }
+ }
+
+ public record WorkloadSummaryItemResponse
+ {
+ public int Count { get; init; }
+
+ public double Average { get; init; }
+ }
+
+ public record WorkloadDayItemResponse
+ {
+ public DateOnly Date { get; init; }
+
+ public bool IsWeekend { get; init; }
+ }
+
+ public record WorkloadStatisticItemResponse
+ {
+ public required string Title { get; init; }
+
+ public required WorkloadSummaryResponse Summary { get; init; }
+
+ public required List DailyMetrics { get; init; }
+ }
+
+ public record WorkloadDailyItemResponse
+ {
+ public DateOnly Date { get; init; }
+
+ public bool IsWeekend { get; init; }
+
+ public int Total { get; init; }
+
+ public int Activated { get; init; }
+
+ public int Deactivated { get; init; }
+ }
+}
diff --git a/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs b/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs
index d75a9cd1..0da2dea9 100644
--- a/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs
+++ b/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs
@@ -1,12 +1,17 @@
-using Microsoft.AspNetCore.Authorization;
+using AutoMapper;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using NodaTime;
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.API.Services.Interfaces;
-using PARR.API.Settings;
using PARR.Core.Services.TaskServices.Interfaces;
using PARR.Core.Services.TaskServices.Providers;
+using PARR.Core.Services.Workload.Interfaces;
using PARR.Domain.Common.Roles;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Enums;
@@ -21,26 +26,53 @@ namespace PARR.API.Controllers.V1.Statistics
{
private readonly ITaskManagementService taskManagementService;
private readonly IClientService clientService;
- //private readonly MqSettings mqSettings;
private readonly ILogger logger;
private readonly ITaskMqSettingsProvider taskMqSettingsProvider;
+ private readonly IWorkloadService workloadService;
+ private readonly IMapper mapper;
public StatWorkloadController(
ITaskManagementService taskManagementService,
IClientService clientService,
- //MqSettings mqSettings,
ILogger logger,
- ITaskMqSettingsProvider taskMqSettingsProvider
+ ITaskMqSettingsProvider taskMqSettingsProvider,
+ IWorkloadService workloadService,
+ IMapper mapper
)
{
this.taskManagementService = taskManagementService;
this.clientService = clientService;
- //this.mqSettings = mqSettings;
this.logger = logger;
this.taskMqSettingsProvider = taskMqSettingsProvider;
+ this.workloadService = workloadService;
+ this.mapper = mapper;
}
+ ///
+ /// Отчет о загруженности по всем РГ
+ ///
+ ///
+ [HttpGet(ApiRoutes.Workload.WorkGroup)]
+ public async Task GetWorkGroupReport([FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request)
+ {
+ return await GetReportAsync(WorkloadReportType.WorkGroupAll, request.DurationDays, offset.TimeZoneOffset, null);
+ }
+
+
+ ///
+ /// Отчет о загруженности по всем ЗО
+ ///
+ ///
+ [HttpGet(ApiRoutes.Workload.ResponseArea)]
+ public async Task GetResponseAreaReport([FromQuery] TimeZoneOffsetClient offset, [FromQuery] WorkloadBaseQuery request)
+ {
+ return await GetReportAsync(WorkloadReportType.ResponseAreaAll, request.DurationDays, offset.TimeZoneOffset, null);
+ }
+
+
+
+
///
/// Сформировать отчетность
///
@@ -79,5 +111,26 @@ namespace PARR.API.Controllers.V1.Statistics
}
+ ///
+ /// Универсальный метод получения отчетности
+ ///
+ ///
+ private async Task GetReportAsync(WorkloadReportType reportType, int durationDays, TimeSpan offset, string? filter)
+ {
+ 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);
+
+ if (report == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = "Необходимо сформировать отчетность." } }));
+
+ var resonse = mapper.Map(report);
+
+ return Ok(new Response(resonse, true));
+ }
+
+
}
}
diff --git a/PARR.API/MappingProfiles/DomainToResponseProfile.cs b/PARR.API/MappingProfiles/DomainToResponseProfile.cs
index 42e4c730..e6847037 100644
--- a/PARR.API/MappingProfiles/DomainToResponseProfile.cs
+++ b/PARR.API/MappingProfiles/DomainToResponseProfile.cs
@@ -1,10 +1,12 @@
using AutoMapper;
using PARR.API.Authentication.Models;
using PARR.API.Contracts.V1.Responses;
+using PARR.API.Contracts.V1.Responses.Statistics;
using PARR.API.MappingProfiles.Resolvers;
using PARR.Core.Repositories.Interfaces.Schedule;
using PARR.Domain.DTOs.Matching;
using PARR.Domain.DTOs.Shortcode;
+using PARR.Domain.DTOs.Workload;
using PARR.Domain.Entities;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Entities.Job;
@@ -425,6 +427,24 @@ namespace PARR.API.MappingProfiles
.ForMember(d => d.DetailsJobs, o => o.MapFrom(s => s.DetailsJobs != null ? s.DetailsJobs.Select(t => t.Data) : null));
#endregion
+
+
+ #region StatWorkloadReportResponse
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap();
+
+ #endregion
+
}
}
diff --git a/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs b/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs
index 44d8f582..e327e39f 100644
--- a/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs
+++ b/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs
@@ -110,7 +110,7 @@ namespace PARR.Core.Services.Workload.Implementations
var cacheItem = new TemplateCache
{
- Data = new TemplateCacheDto { TemplateId = template.Id, ResponseArea = responseArea, WorkGroup = workGroup },
+ Data = new TemplateCacheDto { TemplateId = template.Id, ResponseArea = responseArea, WorkGroup = workGroup, JobGroupId = template.Job!.GroupId, JobId = template.JobId },
Source = nameof(WorkloadCacheService),
Timestamp = DateTimeOffset.UtcNow
};
@@ -193,11 +193,15 @@ namespace PARR.Core.Services.Workload.Implementations
var dtStart = new DateTimeOffset(dateStart.Year, dateStart.Month, dateStart.Day, 0, 0, 0, offset);
var dtEnd = dtStart.AddDays(durationDays);
+ // Конвертируем в UTC для совместимости с PostgreSQL timestamptz
+ var dtStartUtc = dtStart.ToUniversalTime();
+ var dtEndUtc = dtEnd.ToUniversalTime();
+
logger.LogDebug("Фильтруем шаблоны в БД по интервалу: Start={DtStart:yyyy-MM-dd HH:mm:ss zzz}, End={DtEnd:yyyy-MM-dd HH:mm:ss zzz}", dtStart, dtEnd);
var templates = await templateRepository.Get()
.AsNoTracking()
- .Where(t => dtStart <= t.NextRun && t.NextRun < dtEnd)
+ .Where(t => dtStartUtc <= t.NextRun && t.NextRun < dtEndUtc)
.Select(t => new { Id = t.Id, IsActiveTemplate = t.IsActiveTemplate, IsActiveSchedule = t.IsActiveSchedule, Name = t.Name, NextRun = t.NextRun })
.ToListAsync();
@@ -231,7 +235,7 @@ namespace PARR.Core.Services.Workload.Implementations
template.NextRun,
template.IsActiveTemplate,
template.IsActiveSchedule,
- DateTimeOffset.UtcNow);
+ cacheDate);
reportData.Add(data);
}
@@ -266,7 +270,7 @@ namespace PARR.Core.Services.Workload.Implementations
// Запишем результат в кэш
var cacheItem = new TemplateCache
{
- Data = new TemplateCacheDto { TemplateId = template.Id, ResponseArea = responseArea, WorkGroup = workGroup },
+ Data = new TemplateCacheDto { TemplateId = template.Id, ResponseArea = responseArea, WorkGroup = workGroup, JobId = template.JobId, JobGroupId = template.Job!.GroupId },
Source = nameof(WorkloadCacheService),
Timestamp = DateTimeOffset.UtcNow
};
diff --git a/PARR.Core/Services/Workload/Implementations/WorkloadService.cs b/PARR.Core/Services/Workload/Implementations/WorkloadService.cs
index 61068db1..0471e332 100644
--- a/PARR.Core/Services/Workload/Implementations/WorkloadService.cs
+++ b/PARR.Core/Services/Workload/Implementations/WorkloadService.cs
@@ -72,6 +72,7 @@ namespace PARR.Core.Services.Workload.Implementations
break;
case WorkloadReportType.ResponseAreaItem:
displayName = "По зоне ответственности";
+ //todo: тут нужно вернуть другой отчет, либо группы работ, либо список работ
var filteredRA = templates.Where(t => t.ResponseArea.Equals(filterParam, StringComparison.OrdinalIgnoreCase)).ToList();
statistics.Add(BuildStatisticItem(filterParam!, filteredRA, reportDays, offset));
break;
@@ -82,6 +83,7 @@ namespace PARR.Core.Services.Workload.Implementations
break;
case WorkloadReportType.WorkGroupItem:
displayName = "По рабочей группе";
+ //todo: тут нужно вернуть другой отчет, либо группы работ, либо список работ
var filteredWG = templates.Where(t => t.WorkGroup.Equals(filterParam, StringComparison.OrdinalIgnoreCase)).ToList();
statistics.Add(BuildStatisticItem(filterParam!, filteredWG, reportDays, offset));
break;
@@ -95,7 +97,7 @@ namespace PARR.Core.Services.Workload.Implementations
ReportCacheCreatedAt = DateTimeOffset.UtcNow,
TemplateCacheCreatedAt = templates.FirstOrDefault()?.CacheDate ?? DateTimeOffset.MinValue,
Days = reportDays,
- Statistics = statistics
+ Statistics = statistics.OrderBy(t => t.Title).ToList()
};
// Сохраним в кэш
diff --git a/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs b/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs
index f94c2fa9..aeb775dd 100644
--- a/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs
+++ b/PARR.Core/Services/Workload/Interfaces/IWorkloadService.cs
@@ -1,9 +1,22 @@
-namespace PARR.Core.Services.Workload.Interfaces
+using PARR.Domain.DTOs.Workload;
+using PARR.Domain.Enums;
+
+namespace PARR.Core.Services.Workload.Interfaces
{
///
/// Сервис формирования отчетности о загруженности
///
public interface IWorkloadService
{
+ ///
+ /// Получить отчет о загруженности
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task GetWorkloadReport(WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? filterParam);
}
}
diff --git a/PARR.Domain/Cache/Models/TemplateCache.cs b/PARR.Domain/Cache/Models/TemplateCache.cs
index c6b916fd..a0e4ec00 100644
--- a/PARR.Domain/Cache/Models/TemplateCache.cs
+++ b/PARR.Domain/Cache/Models/TemplateCache.cs
@@ -18,5 +18,7 @@ namespace PARR.Domain.Cache.Models
public Guid TemplateId { get; init; }
public required string WorkGroup { get; init; }
public required string ResponseArea { get; init; }
+ public Guid JobId { get; init; }
+ public Guid JobGroupId { get; init; }
}
}