Files
PIBD24_BoikoM.S._Candyhouse/CandyHouseSolution/CandyHouseBusinessLogic/Implementations/ClientDiscountBusinessLogicContract.cs
2025-04-25 10:36:24 +04:00

141 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}