feat(core, api): Workload - Controller для формирования отчетов

This commit is contained in:
Mikhail Trubnikov
2026-05-07 10:33:53 +10:00
parent eb6036f2c9
commit 277bf0caa3
14 changed files with 210 additions and 78 deletions

View File

@@ -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

View File

@@ -7,10 +7,10 @@ namespace PARR.API.Contracts.V1.Requests.BaseRequests
/// </summary>
public class TimeZoneOffsetClient
{
/// <summary>
/// Удалить TimeZoneOffsetHours и не использовать!!!!!! Использовать только в минутах TimeZoneOffsetMinutes
/// </summary>
public int TimeZoneOffsetHours { get; set; } = 0;
///// <summary>
///// Удалить TimeZoneOffsetHours и не использовать!!!!!! Использовать только в минутах TimeZoneOffsetMinutes
///// </summary>
//public int TimeZoneOffsetHours { get; set; } = 0;
/// <summary>
/// Часовая зона клиента, в минутах

View File

@@ -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
{
/// <summary>
/// Базовый запрос по загрузке по ЗО, РГ...
/// </summary>
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;
/// <summary>
/// Период отчетности (по умолчанию 7 дней)
/// </summary>
public int DurationDays { get; set; } = 7;
}
}

View File

@@ -1,7 +0,0 @@
namespace PARR.API.Contracts.V1.Responses.Statistics
{
public class StatResponseAreaWorkLoadResponse : StatWorkLoadDataBaseResponse
{
//public required ResponseAreaResponse ResponseArea { get; set; }
}
}

View File

@@ -1,7 +0,0 @@
namespace PARR.API.Contracts.V1.Responses.Statistics
{
public class StatWorkGroupWorkLoadResponse : StatWorkLoadDataBaseResponse
{
//public required WorkGroupResponse WorkGroup { get; set; }
}
}

View File

@@ -1,28 +0,0 @@
namespace PARR.API.Contracts.V1.Responses.Statistics
{
public class StatWorkLoadDataBaseResponse
{
public List<StatWorkLoadDataResponse> 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; }
}
}

View File

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

View File

@@ -0,0 +1,90 @@
using PARR.Domain.Enums;
namespace PARR.API.Contracts.V1.Responses.Statistics
{
/// <summary>
/// Response отчета о загруженности
/// </summary>
public record StatWorkloadReportResponse
{
/// <summary>
/// Тип отчета (все ЗО, все РГ, одна ЗО, одна РГ)
/// </summary>
public WorkloadReportType Type { get; init; }
/// <summary>
/// Название отчета (По всем ЗО, по одной ЗО)
/// </summary>
public required string DisplayName { get; init; }
/// <summary>
/// Подзаголовок, наименование конкретной ЗО/РГ
/// </summary>
public string? SubfilterName { get; init; }
/// <summary>
/// Дата формирования отчета
/// </summary>
public DateTimeOffset ReportCacheCreatedAt { get; init; }
/// <summary>
/// Дата формирования кэша ЗО и РГ
/// </summary>
public DateTimeOffset TemplateCacheCreatedAt { get; init; }
/// <summary>
/// Список дней
/// </summary>
public required List<WorkloadDayItemResponse> Days { get; init; }
/// <summary>
/// Нагруженность
/// </summary>
public required List<WorkloadStatisticItemResponse> 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<WorkloadDailyItemResponse> 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; }
}
}

View File

@@ -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<StatWorkloadController> logger;
private readonly ITaskMqSettingsProvider taskMqSettingsProvider;
private readonly IWorkloadService workloadService;
private readonly IMapper mapper;
public StatWorkloadController(
ITaskManagementService taskManagementService,
IClientService clientService,
//MqSettings mqSettings,
ILogger<StatWorkloadController> 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;
}
/// <summary>
/// Отчет о загруженности по всем РГ
/// </summary>
/// <returns></returns>
[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);
}
/// <summary>
/// Отчет о загруженности по всем ЗО
/// </summary>
/// <returns></returns>
[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);
}
/// <summary>
/// Сформировать отчетность
/// </summary>
@@ -79,5 +111,26 @@ namespace PARR.API.Controllers.V1.Statistics
}
/// <summary>
/// Универсальный метод получения отчетности
/// </summary>
/// <returns></returns>
private async Task<IActionResult> 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<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }));
var resonse = mapper.Map<StatWorkloadReportResponse>(report);
return Ok(new Response<StatWorkloadReportResponse>(resonse, true));
}
}
}

View File

@@ -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<WorkloadReport, StatWorkloadReportResponse>();
CreateMap<WorkloadSummary, WorkloadSummaryResponse>();
CreateMap<WorkloadSummaryItem, WorkloadSummaryItemResponse>();
CreateMap<WorkloadDayItem, WorkloadDayItemResponse>();
CreateMap<WorkloadStatisticItem, WorkloadStatisticItemResponse>();
CreateMap<WorkloadDailyItem, WorkloadDailyItemResponse>();
#endregion
}
}