141 lines
6.1 KiB
C#
141 lines
6.1 KiB
C#
using Microsoft.Extensions.Localization;
|
||
using Microsoft.Extensions.Logging;
|
||
using CandyHouseContracts.BusinessLogicsContracts;
|
||
using CandyHouseContracts.DataModels;
|
||
using CandyHouseContracts.Exceptions;
|
||
using CandyHouseContracts.Extensions;
|
||
using CandyHouseContracts.Infrastructure;
|
||
using CandyHouseContracts.Infrastructure.ClientConfigurations;
|
||
using CandyHouseContracts.Resources;
|
||
using CandyHouseContracts.StoragesContracts;
|
||
|
||
namespace CandyHouseBusinessLogic.Implementations;
|
||
|
||
internal class ClientDiscountBusinessLogicContract(IClientDiscountStorageContract clientDiscountStorageContract,
|
||
ISaleStorageContract saleStorageContract, IClientStorageContract clientStorageContract, IStringLocalizer<Messages> localizer, ILogger logger, IConfigurationClientDiscount сonfiguration) : IClientDiscountBusinessLogicContract
|
||
{
|
||
private readonly ILogger _logger = logger;
|
||
private readonly IClientDiscountStorageContract _clientDiscountStorageContract = clientDiscountStorageContract;
|
||
private readonly ISaleStorageContract _saleStorageContract = saleStorageContract;
|
||
private readonly IClientStorageContract _clientStorageContract = clientStorageContract;
|
||
private readonly IConfigurationClientDiscount _clientDiscountConfiguration = сonfiguration;
|
||
private readonly IStringLocalizer<Messages> _localizer = localizer;
|
||
private readonly Lock _lockObject = new();
|
||
|
||
public List<ClientDiscountDataModel> GetAllClientDiscountsByPeriod(DateTime fromDate, DateTime toDate)
|
||
{
|
||
_logger.LogInformation("GetAllClientDiscounts params: {fromDate}, {toDate}", fromDate, toDate);
|
||
if (fromDate.IsDateNotOlder(toDate))
|
||
{
|
||
throw new IncorrectDatesException(fromDate, toDate, _localizer);
|
||
}
|
||
return _clientDiscountStorageContract.GetList(fromDate, toDate);
|
||
}
|
||
|
||
public List<ClientDiscountDataModel> GetAllClientDiscountsByPeriodByClient(DateTime fromDate, DateTime toDate, string clientId)
|
||
{
|
||
if (fromDate.IsDateNotOlder(toDate))
|
||
{
|
||
throw new IncorrectDatesException(fromDate, toDate, _localizer);
|
||
}
|
||
if (clientId.IsEmpty())
|
||
{
|
||
throw new ArgumentNullException(nameof(clientId));
|
||
}
|
||
if (!clientId.IsGuid())
|
||
{
|
||
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ClientId"));
|
||
}
|
||
_logger.LogInformation("GetAllClientDiscounts params: {fromDate}, {toDate}, {clientId}", fromDate, toDate, clientId);
|
||
return _clientDiscountStorageContract.GetList(fromDate, toDate, clientId);
|
||
}
|
||
|
||
public void CalculateClientDiscountByMonth(DateTime date)
|
||
{
|
||
_logger.LogInformation("CalculateClientDiscountByMounth: {date}", date);
|
||
var startDate = new DateTime(date.Year, date.Month, 1).ToUniversalTime();
|
||
var finishDate = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month)).ToUniversalTime();
|
||
var clients = _clientStorageContract.GetList();
|
||
foreach (var client in clients)
|
||
{
|
||
var sales = _saleStorageContract.GetList(startDate, finishDate, clientId: client.Id);
|
||
double discountAmount = client.ConfigurationModel switch
|
||
{
|
||
null => 0,
|
||
SilverClientConfiguration sbc => CalculateClientDiscountForSilverClient(sales, startDate, finishDate, sbc),
|
||
GoldenClientConfiguration gbc => CalculateClientDiscountForGoldenClient(sales, startDate, finishDate, gbc),
|
||
ClientConfiguration bc => bc.BasicLevel,
|
||
};
|
||
|
||
_logger.LogDebug("The client {clientId} received a discount in the amount {discountAmount}", client.Id, discountAmount);
|
||
_clientDiscountStorageContract.AddElement(new ClientDiscountDataModel(client.Id, finishDate, discountAmount));
|
||
}
|
||
}
|
||
|
||
private double CalculateClientDiscountForSilverClient(List<SaleDataModel> sales, DateTime startDate, DateTime finishDate, SilverClientConfiguration config)
|
||
{
|
||
var tasks = new List<Task>();
|
||
var totalDiscount = 0.0;
|
||
var semaphore = new SemaphoreSlim(_clientDiscountConfiguration.MaxParallelThreads);
|
||
for (var date = startDate; date < finishDate; date = date.AddDays(1))
|
||
{
|
||
semaphore.Wait();
|
||
|
||
tasks.Add(Task.Factory.StartNew((object? obj) =>
|
||
{
|
||
try
|
||
{
|
||
var dateInTask = (DateTime)obj!;
|
||
var salesInDay = sales.Where(x => x.SaleDate >= dateInTask && x.SaleDate <= dateInTask.AddDays(1)).ToArray();
|
||
if (salesInDay.Length > 0)
|
||
{
|
||
lock (_lockObject)
|
||
{
|
||
totalDiscount += salesInDay.Sum(x => x.Sum) * config.SilverLevel;
|
||
}
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
semaphore.Release();
|
||
}
|
||
}, date));
|
||
}
|
||
|
||
var calcBonusTask = Task.Run(() =>
|
||
{
|
||
return sales.Where(x => x.Sum > _clientDiscountConfiguration.ExtraSaleSum).Sum(x => x.Sum) * config.BonusForPurchases;
|
||
});
|
||
|
||
try
|
||
{
|
||
Task.WaitAll([Task.WhenAll(tasks), calcBonusTask]);
|
||
}
|
||
catch (AggregateException agEx)
|
||
{
|
||
foreach (var ex in agEx.InnerExceptions)
|
||
{
|
||
_logger.LogError(ex, "Error in the SilverClient discount calculation process");
|
||
}
|
||
return 0;
|
||
}
|
||
finally
|
||
{
|
||
semaphore.Dispose();
|
||
}
|
||
return config.BasicLevel + totalDiscount + calcBonusTask.Result;
|
||
}
|
||
|
||
private double CalculateClientDiscountForGoldenClient(List<SaleDataModel> sales, DateTime startDate, DateTime finishDate, GoldenClientConfiguration config)
|
||
{
|
||
try
|
||
{
|
||
return config.BasicLevel + config.GoldenLevel * sales.Count;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "Error in GoldenClient discount calculation process");
|
||
return 0;
|
||
}
|
||
}
|
||
} |