Skip to content

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

  1. Cache inteligent - Cache mai lung pentru date istorice
  2. Limite de timp - Limitați intervalele pentru performanță
  3. Agregare eficientă - Pre-calculați statistici comune
  4. Securitate pe rol - Filtrați date bazat pe permisiuni
  5. Monitorizare - Urmăriți performanța query-urilor
  6. 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.