bps-rw/Services/StatisticsService.cs

189 lines
7.1 KiB
C#

using BankSampahApp.Models;
namespace BankSampahApp.Services;
/// <summary>
/// Service implementation untuk menangani statistik aplikasi
/// Menggunakan base service untuk common functionality
/// </summary>
public class StatisticsService : BaseService, IStatisticsService
{
private readonly ICacheService _cacheService;
private readonly IAppConfigurationService _appConfig;
private readonly IValidationService _validationService;
/// <summary>
/// Cache key untuk statistik
/// </summary>
private const string StatisticsCacheKey = "current_statistics";
/// <summary>
/// Constructor dengan dependency injection
/// </summary>
/// <param name="logger">Logger untuk logging</param>
/// <param name="cache">Memory cache untuk caching</param>
/// <param name="configuration">Configuration untuk settings</param>
/// <param name="cacheService">Cache service</param>
/// <param name="appConfig">Application configuration service</param>
/// <param name="validationService">Validation service</param>
public StatisticsService(
ILogger<StatisticsService> logger,
Microsoft.Extensions.Caching.Memory.IMemoryCache cache,
IConfiguration configuration,
ICacheService cacheService,
IAppConfigurationService appConfig,
IValidationService validationService) : base(logger, cache, configuration)
{
_cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService));
_appConfig = appConfig ?? throw new ArgumentNullException(nameof(appConfig));
_validationService = validationService ?? throw new ArgumentNullException(nameof(validationService));
}
/// <inheritdoc/>
public async Task<StatisticsModel> GetCurrentStatisticsAsync()
{
return await ExecuteWithErrorHandlingAsync(
async () => await _cacheService.GetOrSetWithSlidingAsync(
StatisticsCacheKey,
LoadStatisticsFromSourceAsync,
_appConfig.CacheSettings.StatisticsSlidingExpiration,
_appConfig.CacheSettings.StatisticsExpiration,
Microsoft.Extensions.Caching.Memory.CacheItemPriority.High),
"Fetching current statistics",
GetDefaultStatistics());
}
/// <inheritdoc/>
public async Task<StatisticsModel> GetStatisticsByPeriodAsync(DateTime startDate, DateTime endDate)
{
return await ExecuteWithErrorHandlingAsync(
async () =>
{
var validation = _validationService.ValidateDateRange(startDate, endDate);
if (!validation.IsValid)
{
throw new ArgumentException(string.Join("; ", validation.Errors));
}
return await LoadStatisticsByPeriodFromSourceAsync(startDate, endDate);
},
$"Fetching statistics for period {startDate:yyyy-MM-dd} to {endDate:yyyy-MM-dd}");
}
/// <inheritdoc/>
public async Task<bool> UpdateStatisticsAsync(StatisticsModel statistics)
{
return await ExecuteWithErrorHandlingAsync(
async () =>
{
ArgumentNullException.ThrowIfNull(statistics);
var validation = _validationService.ValidateStatistics(statistics);
if (!validation.IsValid)
{
_logger.LogWarning("Invalid statistics data: {Errors}", string.Join("; ", validation.Errors));
return false;
}
// Simulate async update operation
if (_appConfig.DevelopmentSettings.SimulatedDelayMs > 0)
{
await Task.Delay(_appConfig.DevelopmentSettings.SimulatedDelayMs);
}
else
{
await Task.Delay(10);
}
// Clear cache after update
_cacheService.Remove(StatisticsCacheKey);
return true;
},
"Updating statistics",
false);
}
/// <summary>
/// Load statistics from data source (placeholder for actual implementation)
/// </summary>
/// <returns>Current statistics</returns>
private async Task<StatisticsModel> LoadStatisticsFromSourceAsync()
{
// In a real application, this would load from database, API, etc.
await Task.CompletedTask;
// Get base values from configuration
var baseUsers = _appConfig.StatisticsSettings.BaseUsers;
var baseWaste = _appConfig.StatisticsSettings.BaseWaste;
// Simulate some growth over time
var daysSinceStart = (DateTime.UtcNow - new DateTime(2024, 1, 1)).Days;
var growthFactor = Math.Min(_appConfig.StatisticsSettings.MaxGrowthFactor,
1.0 + (daysSinceStart * _appConfig.StatisticsSettings.GrowthFactorMultiplier));
return new StatisticsModel
{
TotalUsers = (int)(baseUsers * growthFactor),
WasteCollected = baseWaste * (decimal)growthFactor,
RewardsDistributed = (int)(_appConfig.StatisticsSettings.BaseRewards * growthFactor),
EnvironmentImpact = Math.Min(99.9m, _appConfig.StatisticsSettings.BaseEnvironmentImpact + (decimal)(growthFactor * 2))
};
}
/// <summary>
/// Load statistics for specific period (placeholder)
/// </summary>
/// <param name="startDate">Start date</param>
/// <param name="endDate">End date</param>
/// <returns>Period statistics</returns>
private async Task<StatisticsModel> LoadStatisticsByPeriodFromSourceAsync(DateTime startDate, DateTime endDate)
{
await Task.CompletedTask;
// Calculate period-specific statistics
var days = (endDate - startDate).Days + 1;
var dailyAverage = await GetDailyAverageAsync();
return new StatisticsModel
{
TotalUsers = dailyAverage.TotalUsers * days / 365,
WasteCollected = dailyAverage.WasteCollected * days,
RewardsDistributed = dailyAverage.RewardsDistributed * days,
EnvironmentImpact = dailyAverage.EnvironmentImpact
};
}
/// <summary>
/// Get daily average statistics
/// </summary>
/// <returns>Daily average statistics</returns>
private async Task<StatisticsModel> GetDailyAverageAsync()
{
await Task.CompletedTask;
return new StatisticsModel
{
TotalUsers = 3, // New users per day
WasteCollected = 0.1m, // Tons per day
RewardsDistributed = 5, // Rewards per day
EnvironmentImpact = 98.5m // Overall impact
};
}
/// <summary>
/// Get default statistics when error occurs
/// </summary>
/// <returns>Default statistics</returns>
private StatisticsModel GetDefaultStatistics()
{
return new StatisticsModel
{
TotalUsers = _appConfig.StatisticsSettings.BaseUsers,
WasteCollected = _appConfig.StatisticsSettings.BaseWaste,
RewardsDistributed = _appConfig.StatisticsSettings.BaseRewards,
EnvironmentImpact = _appConfig.StatisticsSettings.BaseEnvironmentImpact
};
}
}