ServiceRequestStatisticsService
Documentație pentru serviciul ServiceRequestStatisticsService
1. Descriere Generală
ServiceRequestStatisticsService
este un serviciu pentru obținerea statisticilor detaliate despre cererile de servicii din sistemul FOD. Oferă date agregate pentru analiză, raportare și monitorizare a activității sistemului.
Caracteristici principale: - Obținere statistici filtrate pe perioadă - Date agregate pe multiple dimensiuni - Statistici pentru toate stările cererilor - Suport pentru diferite tipuri de livrare (MDelivery) - Statistici separate pentru frontoffice/backoffice - Integrare cu componenta FodServiceRequestsStatistics
2. Configurare și Înregistrare
// Program.cs sau Startup.cs
builder.Services.AddHttpClient<IServiceRequestStatisticsService, ServiceRequestStatisticsService>(client =>
{
client.BaseAddress = new Uri(configuration["ApiSettings:BaseUrl"]);
});
// Pentru implementare server-side cu acces direct
builder.Services.AddScoped<IServiceRequestStatisticsService, ServerServiceRequestStatisticsService>();
3. Interfața IServiceRequestStatisticsService
public interface IServiceRequestStatisticsService
{
Task<ServiceRequestStatisticsModel> Get(ServiceRequestStatisticsFilter filter);
}
4. Metode Disponibile
Metodă | Parametri | Return | Descriere |
---|---|---|---|
Get |
ServiceRequestStatisticsFilter filter |
Task<ServiceRequestStatisticsModel> |
Obține statistici filtrate |
5. Modele de Date
ServiceRequestStatisticsFilter
public class ServiceRequestStatisticsFilter
{
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public string ServiceCode { get; set; }
public string OfficeCode { get; set; }
public string RequestorType { get; set; }
public bool IncludeDeleted { get; set; }
}
ServiceRequestStatisticsModel
public class ServiceRequestStatisticsModel
{
// Statistici generale
public int TotalServiceRequests { get; set; }
public int DraftServiceRequests { get; set; }
public int NewServiceRequests { get; set; }
public int PaidServiceRequests { get; set; }
public int FreeServiceRequests { get; set; }
public int InProgressServiceRequests { get; set; }
public int ProcessedServiceRequests { get; set; }
public int IssuedServiceRequests { get; set; }
public int RejectedServiceRequests { get; set; }
public int SuspendedServiceRequests { get; set; }
// Statistici confirmare
public int ConfirmedServiceRequests { get; set; }
public int ConfirmedServiceRequestsWithResponseOnPaper { get; set; }
public int ConfirmedServiceRequestsWithResponseOnElectronicDocument { get; set; }
// Statistici MDelivery
public int NewMDeliveryServiceRequets { get; set; }
public int ConfirmedMDeliveryServiceRequests { get; set; }
public int ProcessedMdeliveryServiceRequests { get; set; }
public int IssuedMDeliveryServiceRequests { get; set; }
// Statistici canale
public int FrontofficeConfirmedServiceRequests { get; set; }
public int BackofficeConfirmedServiceRequests { get; set; }
// Statistici apostilare
public int ConfirmedApostillationRequests { get; set; }
// Statistici financiare
public decimal TotalRevenue { get; set; }
public decimal AverageServiceCost { get; set; }
// Statistici temporale
public double AverageProcessingTimeDays { get; set; }
public DateTime? LastRequestDate { get; set; }
}
6. Exemple de Utilizare
Dashboard statistici generale
@page "/admin/statistici"
@inject IServiceRequestStatisticsService StatisticsService
<FodContainer>
<FodText Typo="Typo.h4" GutterBottom="true">
Statistici cereri servicii
</FodText>
<!-- Filtre -->
<FodCard Class="mb-4">
<FodCardContent>
<FodGrid Container="true" Spacing="2" AlignItems="Align.End">
<FodGrid Item="true" xs="12" sm="6" md="3">
<FodDatePicker @bind-Date="filter.FromDate"
Label="De la data"
MaxDate="DateTime.Now" />
</FodGrid>
<FodGrid Item="true" xs="12" sm="6" md="3">
<FodDatePicker @bind-Date="filter.ToDate"
Label="Până la data"
MaxDate="DateTime.Now" />
</FodGrid>
<FodGrid Item="true" xs="12" sm="6" md="3">
<FodSelect @bind-Value="filter.ServiceCode"
Label="Serviciu"
Items="availableServices" />
</FodGrid>
<FodGrid Item="true" xs="12" sm="6" md="3">
<FodButton OnClick="LoadStatistics"
Color="FodColor.Primary"
FullWidth="true">
Actualizează
</FodButton>
</FodGrid>
</FodGrid>
</FodCardContent>
</FodCard>
@if (isLoading)
{
<FodLoadingLinear Indeterminate="true" />
}
else if (statistics != null)
{
<!-- Cards cu statistici principale -->
<FodGrid Container="true" Spacing="3" Class="mb-4">
<FodGrid Item="true" xs="12" sm="6" md="3">
<StatCard Title="Total cereri"
Value="@statistics.TotalServiceRequests"
Icon="@FodIcons.Material.Filled.Assignment"
Color="FodColor.Primary" />
</FodGrid>
<FodGrid Item="true" xs="12" sm="6" md="3">
<StatCard Title="În procesare"
Value="@statistics.InProgressServiceRequests"
Icon="@FodIcons.Material.Filled.Pending"
Color="FodColor.Warning" />
</FodGrid>
<FodGrid Item="true" xs="12" sm="6" md="3">
<StatCard Title="Finalizate"
Value="@statistics.IssuedServiceRequests"
Icon="@FodIcons.Material.Filled.CheckCircle"
Color="FodColor.Success" />
</FodGrid>
<FodGrid Item="true" xs="12" sm="6" md="3">
<StatCard Title="Respinse"
Value="@statistics.RejectedServiceRequests"
Icon="@FodIcons.Material.Filled.Cancel"
Color="FodColor.Error" />
</FodGrid>
</FodGrid>
<!-- Detalii statistici -->
<FodGrid Container="true" Spacing="3">
<FodGrid Item="true" xs="12" md="6">
<StatusDistributionChart Statistics="@statistics" />
</FodGrid>
<FodGrid Item="true" xs="12" md="6">
<DeliveryMethodChart Statistics="@statistics" />
</FodGrid>
</FodGrid>
<!-- Tabel detaliat -->
<FodCard Class="mt-4">
<FodCardContent>
<FodServiceRequestsStatistics />
</FodCardContent>
</FodCard>
}
</FodContainer>
@code {
private ServiceRequestStatisticsFilter filter = new()
{
FromDate = DateTime.Now.AddMonths(-1),
ToDate = DateTime.Now
};
private ServiceRequestStatisticsModel statistics;
private bool isLoading;
private List<SelectableItem> availableServices = new();
protected override async Task OnInitializedAsync()
{
await LoadStatistics();
}
private async Task LoadStatistics()
{
isLoading = true;
try
{
statistics = await StatisticsService.Get(filter);
}
finally
{
isLoading = false;
}
}
}
Widget pentru dashboard principal
<FodCard>
<FodCardContent>
<FodText Typo="Typo.h6" GutterBottom="true">
Statistici rapide - Ultima săptămână
</FodText>
@if (weeklyStats == null)
{
<FodSkeleton Type="SkeletonType.Rectangle" Height="200" />
}
else
{
<FodGrid Container="true" Spacing="2">
<FodGrid Item="true" xs="6">
<div class="text-center">
<FodText Typo="Typo.h3" Color="FodColor.Primary">
@weeklyStats.NewServiceRequests
</FodText>
<FodText Typo="Typo.caption">Cereri noi</FodText>
</div>
</FodGrid>
<FodGrid Item="true" xs="6">
<div class="text-center">
<FodText Typo="Typo.h3" Color="FodColor.Success">
@weeklyStats.IssuedServiceRequests
</FodText>
<FodText Typo="Typo.caption">Finalizate</FodText>
</div>
</FodGrid>
</FodGrid>
<FodDivider Class="my-3" />
<FodText Typo="Typo.body2">
Timp mediu procesare: @weeklyStats.AverageProcessingTimeDays.ToString("F1") zile
</FodText>
<FodButton Variant="FodVariant.Text"
OnClick="ViewFullStatistics"
Class="mt-2">
Vezi toate statisticile
</FodButton>
}
</FodCardContent>
</FodCard>
@code {
[Inject] private IServiceRequestStatisticsService StatisticsService { get; set; }
private ServiceRequestStatisticsModel weeklyStats;
protected override async Task OnInitializedAsync()
{
var filter = new ServiceRequestStatisticsFilter
{
FromDate = DateTime.Now.AddDays(-7),
ToDate = DateTime.Now
};
weeklyStats = await StatisticsService.Get(filter);
}
}
Raport lunar automatizat
@inject IServiceRequestStatisticsService StatisticsService
@inject IPrintingService PrintingService
<FodCard>
<FodCardContent>
<FodText Typo="Typo.h5" GutterBottom="true">
Raport lunar - @currentMonth.ToString("MMMM yyyy")
</FodText>
@if (monthlyReport != null)
{
<div id="monthly-report">
<!-- Header raport -->
<div class="report-header mb-4">
<FodText Typo="Typo.subtitle1">
Perioada: @filter.FromDate?.ToString("dd.MM.yyyy") -
@filter.ToDate?.ToString("dd.MM.yyyy")
</FodText>
</div>
<!-- Sumar executiv -->
<FodPaper Elevation="1" Class="p-3 mb-4">
<FodText Typo="Typo.h6" GutterBottom="true">
Sumar executiv
</FodText>
<FodList Dense="true">
<FodListItem>
<FodText>Total cereri procesate:</FodText>
<FodText Class="ms-auto font-weight-bold">
@monthlyReport.TotalServiceRequests
</FodText>
</FodListItem>
<FodListItem>
<FodText>Rata de finalizare:</FodText>
<FodText Class="ms-auto font-weight-bold">
@CalculateCompletionRate()%
</FodText>
</FodListItem>
<FodListItem>
<FodText>Venituri totale:</FodText>
<FodText Class="ms-auto font-weight-bold">
@monthlyReport.TotalRevenue.ToString("N2") MDL
</FodText>
</FodListItem>
</FodList>
</FodPaper>
<!-- Statistici pe servicii -->
<FodText Typo="Typo.h6" GutterBottom="true">
Distribuție pe tipuri de servicii
</FodText>
<FodDataTable Items="serviceBreakdown" Dense="true">
<HeaderContent>
<FodTHeadRow>
<FodTh>Serviciu</FodTh>
<FodTh>Cereri</FodTh>
<FodTh>Finalizate</FodTh>
<FodTh>În procesare</FodTh>
<FodTh>Respinse</FodTh>
</FodTHeadRow>
</HeaderContent>
<RowTemplate>
<FodTr>
<FodTd>@context.ServiceName</FodTd>
<FodTd>@context.Total</FodTd>
<FodTd>@context.Completed</FodTd>
<FodTd>@context.InProgress</FodTd>
<FodTd>@context.Rejected</FodTd>
</FodTr>
</RowTemplate>
</FodDataTable>
</div>
}
<div class="mt-4">
<FodButton OnClick="PrintReport"
StartIcon="@FodIcons.Material.Filled.Print">
Printează raport
</FodButton>
<FodButton OnClick="ExportExcel"
StartIcon="@FodIcons.Material.Filled.GetApp"
Class="ms-2">
Export Excel
</FodButton>
<FodButton OnClick="EmailReport"
StartIcon="@FodIcons.Material.Filled.Email"
Class="ms-2">
Trimite pe email
</FodButton>
</div>
</FodCardContent>
</FodCard>
@code {
private DateTime currentMonth = DateTime.Now;
private ServiceRequestStatisticsFilter filter;
private ServiceRequestStatisticsModel monthlyReport;
private List<ServiceBreakdown> serviceBreakdown = new();
protected override async Task OnInitializedAsync()
{
filter = new ServiceRequestStatisticsFilter
{
FromDate = new DateTime(currentMonth.Year, currentMonth.Month, 1),
ToDate = currentMonth.AddMonths(1).AddDays(-1)
};
monthlyReport = await StatisticsService.Get(filter);
// Load service breakdown data
}
private double CalculateCompletionRate()
{
if (monthlyReport.TotalServiceRequests == 0) return 0;
return Math.Round((double)monthlyReport.IssuedServiceRequests /
monthlyReport.TotalServiceRequests * 100, 2);
}
private async Task PrintReport()
{
await PrintingService.PrintAsync(new PrintOptions
{
ElementId = "monthly-report",
PageTitle = $"Raport lunar - {currentMonth:MMMM yyyy}"
});
}
}
Comparație perioade
<FodCard>
<FodCardContent>
<FodText Typo="Typo.h6" GutterBottom="true">
Comparație perioade
</FodText>
<FodGrid Container="true" Spacing="2" Class="mb-3">
<FodGrid Item="true" xs="12" md="6">
<FodDateRangePicker @bind-DateRange="period1"
Label="Perioada 1" />
</FodGrid>
<FodGrid Item="true" xs="12" md="6">
<FodDateRangePicker @bind-DateRange="period2"
Label="Perioada 2" />
</FodGrid>
</FodGrid>
<FodButton OnClick="ComparePerioads"
Color="FodColor.Primary"
Disabled="@isComparing">
Compară
</FodButton>
@if (comparison != null)
{
<div class="mt-4">
<ComparisonTable Period1="@comparison.Period1"
Period2="@comparison.Period2" />
<!-- Grafic comparativ -->
<div class="mt-3">
<ComparisonChart Data="@comparison" />
</div>
</div>
}
</FodCardContent>
</FodCard>
@code {
private DateRange period1 = new()
{
Start = DateTime.Now.AddMonths(-2),
End = DateTime.Now.AddMonths(-1)
};
private DateRange period2 = new()
{
Start = DateTime.Now.AddMonths(-1),
End = DateTime.Now
};
private PeriodComparison comparison;
private bool isComparing;
private async Task ComparePerioads()
{
isComparing = true;
var stats1 = await StatisticsService.Get(new ServiceRequestStatisticsFilter
{
FromDate = period1.Start,
ToDate = period1.End
});
var stats2 = await StatisticsService.Get(new ServiceRequestStatisticsFilter
{
FromDate = period2.Start,
ToDate = period2.End
});
comparison = new PeriodComparison
{
Period1 = stats1,
Period2 = stats2
};
isComparing = false;
}
}
7. Cache și Performanță
public class CachedServiceRequestStatisticsService : IServiceRequestStatisticsService
{
private readonly IServiceRequestStatisticsService _innerService;
private readonly IMemoryCache _cache;
private readonly ILogger<CachedServiceRequestStatisticsService> _logger;
public async Task<ServiceRequestStatisticsModel> Get(ServiceRequestStatisticsFilter filter)
{
var cacheKey = GenerateCacheKey(filter);
// Cache mai lung pentru date istorice
var cacheExpiration = filter.ToDate < DateTime.Today
? TimeSpan.FromHours(24)
: TimeSpan.FromMinutes(15);
if (_cache.TryGetValue<ServiceRequestStatisticsModel>(cacheKey, out var cached))
{
_logger.LogDebug("Returnare statistici din cache");
return cached;
}
var result = await _innerService.Get(filter);
_cache.Set(cacheKey, result, cacheExpiration);
return result;
}
private string GenerateCacheKey(ServiceRequestStatisticsFilter filter)
{
return $"Stats_{filter.FromDate:yyyyMMdd}_{filter.ToDate:yyyyMMdd}_" +
$"{filter.ServiceCode}_{filter.OfficeCode}_{filter.RequestorType}";
}
}
8. Agregare și Analiză
public class EnhancedServiceRequestStatisticsService : IServiceRequestStatisticsService
{
private readonly IServiceRequestStatisticsService _baseService;
public async Task<ServiceRequestStatisticsModel> Get(ServiceRequestStatisticsFilter filter)
{
var stats = await _baseService.Get(filter);
// Calculează metrici adiționale
EnhanceStatistics(stats, filter);
return stats;
}
private void EnhanceStatistics(ServiceRequestStatisticsModel stats,
ServiceRequestStatisticsFilter filter)
{
// Rata de succes
if (stats.TotalServiceRequests > 0)
{
stats.SuccessRate = (double)stats.IssuedServiceRequests /
stats.TotalServiceRequests * 100;
}
// Distribuție canale
var totalConfirmed = stats.FrontofficeConfirmedServiceRequests +
stats.BackofficeConfirmedServiceRequests;
if (totalConfirmed > 0)
{
stats.FrontofficePercentage = (double)stats.FrontofficeConfirmedServiceRequests /
totalConfirmed * 100;
}
// Tendințe
if (filter.FromDate.HasValue && filter.ToDate.HasValue)
{
var days = (filter.ToDate.Value - filter.FromDate.Value).TotalDays;
stats.DailyAverage = stats.TotalServiceRequests / Math.Max(1, days);
}
}
}
9. Export și Raportare
public class ReportingServiceRequestStatisticsService : IServiceRequestStatisticsService
{
private readonly IServiceRequestStatisticsService _innerService;
private readonly IReportGenerator _reportGenerator;
public async Task<ServiceRequestStatisticsModel> Get(ServiceRequestStatisticsFilter filter)
{
var stats = await _innerService.Get(filter);
// Generare automată rapoarte pentru manageri
if (ShouldGenerateReport(filter))
{
await GenerateAndSendReport(stats, filter);
}
return stats;
}
private async Task GenerateAndSendReport(ServiceRequestStatisticsModel stats,
ServiceRequestStatisticsFilter filter)
{
var report = await _reportGenerator.GenerateStatisticsReport(stats, filter);
// Trimite către manageri
await EmailService.SendAsync(new EmailMessage
{
To = GetManagerEmails(),
Subject = $"Raport statistici {filter.FromDate:dd.MM.yyyy} - {filter.ToDate:dd.MM.yyyy}",
Body = "Găsiți atașat raportul de statistici pentru perioada selectată.",
Attachments = new[] { report }
});
}
}
10. Monitorizare Real-time
public class RealTimeStatisticsService : IServiceRequestStatisticsService
{
private readonly IServiceRequestStatisticsService _innerService;
private readonly IHubContext<StatisticsHub> _hubContext;
private readonly Timer _refreshTimer;
public RealTimeStatisticsService(/* dependencies */)
{
// Refresh automat la fiecare 5 minute
_refreshTimer = new Timer(async _ => await BroadcastStatistics(),
null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
}
private async Task BroadcastStatistics()
{
var todayStats = await Get(new ServiceRequestStatisticsFilter
{
FromDate = DateTime.Today,
ToDate = DateTime.Now
});
await _hubContext.Clients.All.SendAsync("StatisticsUpdate", todayStats);
}
}
11. Validare și Securitate
public class SecureServiceRequestStatisticsService : IServiceRequestStatisticsService
{
private readonly IServiceRequestStatisticsService _innerService;
private readonly IAuthorizationService _authorizationService;
private readonly IHttpContextAccessor _httpContextAccessor;
public async Task<ServiceRequestStatisticsModel> Get(ServiceRequestStatisticsFilter filter)
{
var user = _httpContextAccessor.HttpContext?.User;
// Verificare permisiuni
if (!await _authorizationService.AuthorizeAsync(user, "ViewStatistics"))
{
throw new UnauthorizedException("Nu aveți permisiunea de a vizualiza statistici");
}
// Filtrare pe baza rolului
if (user.IsInRole("OfficeManager"))
{
// Manager poate vedea doar statistici pentru biroul său
filter.OfficeCode = user.FindFirst("OfficeCode")?.Value;
}
// Validare interval
if (filter.FromDate > filter.ToDate)
{
throw new ValidationException("Data de început nu poate fi după data de sfârșit");
}
// Limitare interval pentru performanță
if (filter.FromDate.HasValue && filter.ToDate.HasValue)
{
var days = (filter.ToDate.Value - filter.FromDate.Value).TotalDays;
if (days > 365)
{
throw new ValidationException("Intervalul maxim permis este de 1 an");
}
}
return await _innerService.Get(filter);
}
}
12. Testare
[TestClass]
public class ServiceRequestStatisticsServiceTests
{
private Mock<HttpMessageHandler> _httpHandler;
private IServiceRequestStatisticsService _service;
[TestMethod]
public async Task Get_ValidFilter_ReturnsStatistics()
{
// Arrange
var filter = new ServiceRequestStatisticsFilter
{
FromDate = DateTime.Today.AddDays(-30),
ToDate = DateTime.Today
};
var expectedStats = new ServiceRequestStatisticsModel
{
TotalServiceRequests = 100,
NewServiceRequests = 20,
InProgressServiceRequests = 30,
IssuedServiceRequests = 45,
RejectedServiceRequests = 5
};
SetupHttpResponse(expectedStats);
// Act
var result = await _service.Get(filter);
// Assert
Assert.AreEqual(100, result.TotalServiceRequests);
Assert.AreEqual(45, result.IssuedServiceRequests);
}
[TestMethod]
public async Task Get_EmptyFilter_ReturnsAllTimeStatistics()
{
// Arrange
var filter = new ServiceRequestStatisticsFilter();
// Act
var result = await _service.Get(filter);
// Assert
Assert.IsNotNull(result);
Assert.IsTrue(result.TotalServiceRequests >= 0);
}
}
13. Best Practices
- Cache inteligent - Cache mai lung pentru date istorice
- Limite de timp - Limitați intervalele pentru performanță
- Agregare eficientă - Pre-calculați statistici comune
- Securitate pe rol - Filtrați date bazat pe permisiuni
- Monitorizare - Urmăriți performanța query-urilor
- Export optimizat - Generați rapoarte asincron pentru date mari
14. Integrare cu Dashboard
@inject IServiceRequestStatisticsService StatisticsService
<AdminDashboard>
<StatisticsWidgets>
<TodayStatsWidget />
<WeeklyTrendWidget />
<ServiceDistributionWidget />
<PerformanceMetricsWidget />
</StatisticsWidgets>
<QuickActions>
<FodButton OnClick="GenerateMonthlyReport">
Generează raport lunar
</FodButton>
<FodButton OnClick="ExportStatistics">
Export statistici
</FodButton>
</QuickActions>
</AdminDashboard>
@code {
protected override async Task OnInitializedAsync()
{
// Pre-load statistici pentru widgets
var todayFilter = new ServiceRequestStatisticsFilter
{
FromDate = DateTime.Today,
ToDate = DateTime.Now
};
TodayStats = await StatisticsService.Get(todayFilter);
}
}
15. Concluzie
ServiceRequestStatisticsService
oferă o interfață completă pentru analiza și monitorizarea activității sistemului FOD. Cu suport pentru filtrare flexibilă, cache optimizat și posibilități extinse de raportare, serviciul este esențial pentru managementul eficient al serviciilor guvernamentale digitale.