diff --git a/ShipyardBusinessLogic/ImplementerLogic.cs b/ShipyardBusinessLogic/ImplementerLogic.cs new file mode 100644 index 0000000..7055a1a --- /dev/null +++ b/ShipyardBusinessLogic/ImplementerLogic.cs @@ -0,0 +1,126 @@ +using Microsoft.Extensions.Logging; +using ShipyardContracts.BindingModels; +using ShipyardContracts.BusinessLogicContracts; +using ShipyardContracts.SearchModels; +using ShipyardContracts.StorageContracts; +using ShipyardContracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShipyardBusinessLogic +{ + public class ImplementerLogic : IImplementerLogic + { + private readonly ILogger _logger; + private readonly IImplementerStorage _implementerStorage; + public ImplementerLogic(ILogger logger, IImplementerStorage implementerStorage) + { + _logger = logger; + _implementerStorage = implementerStorage; + } + public List? ReadList(ImplementerSearchModel? model) + { + _logger.LogInformation("ReadList. ImplementerFIO:{ClientFIO}. Id:{ Id}", model?.ImplementerFIO, model?.Id); + var list = model == null ? _implementerStorage.GetFullList() : _implementerStorage.GetFilteredList(model); + if (list == null) + { + _logger.LogWarning("ReadList return null list"); + return null; + } + _logger.LogInformation("ReadList. Count:{Count}", list.Count); + return list; + } + public ImplementerViewModel? ReadElement(ImplementerSearchModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + _logger.LogInformation("ReadElement. ImplementerFIO:{ImplementerFIO}.Id:{ Id}", model.ImplementerFIO, model.Id); + var element = _implementerStorage.GetElement(model); + if (element == null) + { + _logger.LogWarning("ReadElement element not found"); + return null; + } + _logger.LogInformation("ReadElement find. Id:{Id}", element.Id); + return element; + } + public bool Create(ImplementerBindingModel model) + { + CheckModel(model); + if (_implementerStorage.Insert(model) == null) + { + _logger.LogWarning("Insert operation failed"); + return false; + } + return true; + } + public bool Update(ImplementerBindingModel model) + { + CheckModel(model); + if (_implementerStorage.Update(model) == null) + { + _logger.LogWarning("Update operation failed"); + return false; + } + return true; + } + public bool Delete(ImplementerBindingModel model) + { + CheckModel(model, false); + _logger.LogInformation("Delete. Id:{Id}", model.Id); + if (_implementerStorage.Delete(model) == null) + { + _logger.LogWarning("Delete operation failed"); + return false; + } + return true; + } + private void CheckModel(ImplementerBindingModel model, bool withParams = + true) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + if (!withParams) + { + return; + } + if (string.IsNullOrEmpty(model.ImplementerFIO)) + { + throw new ArgumentNullException("Нет ФИО исполнителя", + nameof(model.ImplementerFIO)); + } + if (string.IsNullOrEmpty(model.Password)) + { + throw new ArgumentNullException("Нет пароля клиента", + nameof(model.Password)); + } + if (model.WorkExperience < 0) + { + throw new ArgumentNullException("Стаж меньше 0", + nameof(model.WorkExperience)); + } + if (model.Qualification < 0) + { + throw new ArgumentException("Квалификация меньше 0", nameof(model.Qualification)); + } + _logger.LogInformation("Implementer. ImplementerFIO:{ImplementerFIO}." + + "Password:{ Password}. WorkExperience:{ WorkExperience}. Qualification:{ Qualification}. Id: { Id} ", + model.ImplementerFIO, model.Password, model.WorkExperience, model.Qualification, model.Id); + var element = _implementerStorage.GetElement(new ImplementerSearchModel + { + ImplementerFIO = model.ImplementerFIO, + }); + if (element != null && element.Id != model.Id) + { + throw new InvalidOperationException("Исполнитель с таким ФИО уже есть"); + } + } + } +} diff --git a/ShipyardBusinessLogic/OrderLogic.cs b/ShipyardBusinessLogic/OrderLogic.cs index 0e1cf2b..17309fa 100644 --- a/ShipyardBusinessLogic/OrderLogic.cs +++ b/ShipyardBusinessLogic/OrderLogic.cs @@ -17,14 +17,30 @@ namespace ShipyardBusinessLogic { private readonly ILogger _logger; private readonly IOrderStorage _orderStorage; + static readonly object locker = new object(); - public OrderLogic(ILogger logger, IOrderStorage orderStorage) + public OrderLogic(ILogger logger, IOrderStorage orderStorage) { _logger = logger; _orderStorage = orderStorage; } - - public List? ReadList(OrderSearchModel? model) + public OrderViewModel? ReadElement(OrderSearchModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + _logger.LogInformation("ReadElement. Id:{ Id}", model.Id); + var element = _orderStorage.GetElement(model); + if (element == null) + { + _logger.LogWarning("ReadElement element not found"); + return null; + } + _logger.LogInformation("ReadElement find. Id:{Id}", element.Id); + return element; + } + public List? ReadList(OrderSearchModel? model) { _logger.LogInformation("ReadList. Id:{ Id}", model?.Id); var list = model == null ? _orderStorage.GetFullList() : @@ -53,8 +69,8 @@ namespace ShipyardBusinessLogic public bool ChangeStatus(OrderBindingModel model, OrderStatus status) { - CheckModel(model); - var element = _orderStorage.GetElement(new OrderSearchModel { Id = model.Id }); + CheckModel(model, false); + var element = _orderStorage.GetElement(new OrderSearchModel { Id = model.Id }); if (element == null) { _logger.LogWarning("Read operation failed"); @@ -66,15 +82,21 @@ namespace ShipyardBusinessLogic throw new InvalidOperationException("Текущий статус заказа не может быть переведен в выбранный"); } model.Status = status; - if (model.Status == OrderStatus.Выдан) + model.DateImplement = element.DateImplement; + if (model.Status == OrderStatus.Готов) model.DateImplement = DateTime.Now; - _orderStorage.Update(model); + if (element.ImplementerId.HasValue) + model.ImplementerId = element.ImplementerId; + _orderStorage.Update(model); return true; } public bool TakeOrderInWork(OrderBindingModel model) { - return ChangeStatus(model, OrderStatus.Выполняется); + lock (locker) + { + return ChangeStatus(model, OrderStatus.Выполняется); + } } public bool FinishOrder(OrderBindingModel model) @@ -87,8 +109,7 @@ namespace ShipyardBusinessLogic return ChangeStatus(model, OrderStatus.Выдан); } - private void CheckModel(OrderBindingModel model, bool withParams = - true) + private void CheckModel(OrderBindingModel model, bool withParams = true) { if (model == null) { diff --git a/ShipyardBusinessLogic/WorkModeling.cs b/ShipyardBusinessLogic/WorkModeling.cs new file mode 100644 index 0000000..ee8be59 --- /dev/null +++ b/ShipyardBusinessLogic/WorkModeling.cs @@ -0,0 +1,137 @@ +using Microsoft.Extensions.Logging; +using ShipyardContracts.BindingModels; +using ShipyardContracts.BusinessLogicContracts; +using ShipyardContracts.BusinessLogicsContracts; +using ShipyardContracts.SearchModels; +using ShipyardContracts.ViewModels; +using ShipyardDataModels.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShipyardBusinessLogic +{ + public class WorkModeling : IWorkProcess + { + private readonly ILogger _logger; + private readonly Random _rnd; + private IOrderLogic? _orderLogic; + public WorkModeling(ILogger logger) + { + _logger = logger; + _rnd = new Random(1000); + } + public void DoWork(IImplementerLogic implementerLogic, IOrderLogic orderLogic) + { + _orderLogic = orderLogic; + var implementers = implementerLogic.ReadList(null); + if (implementers == null) + { + _logger.LogWarning("DoWork. Implementers is null"); + return; + } + var orders = _orderLogic.ReadList(new OrderSearchModel + { + Status = OrderStatus.Принят + }); + if (orders == null || orders.Count == 0) + { + _logger.LogWarning("DoWork. Orders is null or empty"); + return; + } + _logger.LogDebug("DoWork for {Count} orders", orders.Count); + foreach (var implementer in implementers) + { + Task.Run(() => WorkerWorkAsync(implementer, orders)); + } + } + /// Иммитация работы исполнителя + private async Task WorkerWorkAsync(ImplementerViewModel implementer, List orders) + { + if (_orderLogic == null || implementer == null) + { + return; + } + await RunOrderInWork(implementer); + await Task.Run(() => + { + foreach (var order in orders) + { + try + { + _logger.LogDebug("DoWork. Worker {Id} try get order { Order}", implementer.Id, order.Id); + // пытаемся назначить заказ на исполнителя + _orderLogic.TakeOrderInWork(new OrderBindingModel + { + Id = order.Id, + ImplementerId = implementer.Id + }); + // делаем работу + Thread.Sleep(implementer.WorkExperience * _rnd.Next(100, 1000) * order.Count); + _logger.LogDebug("DoWork. Worker {Id} finish order { Order}", implementer.Id, order.Id); + _orderLogic.FinishOrder(new OrderBindingModel + { + Id = order.Id + }); + // отдыхаем + Thread.Sleep(implementer.Qualification * _rnd.Next(10, 100)); + } + // кто-то мог уже перехватить заказ, игнорируем ошибку + catch (InvalidOperationException ex) + { + _logger.LogWarning(ex, "Error try get work"); + } + // заканчиваем выполнение имитации в случае иной ошибки + catch (Exception ex) + { + _logger.LogError(ex, "Error while do work"); + throw; + } + } + }); + } + /// Ищем заказ, которые уже в работе (вдруг исполнителя прервали) + private async Task RunOrderInWork(ImplementerViewModel implementer) + { + if (_orderLogic == null || implementer == null) + { + return; + } + try + { + var runOrder = await Task.Run(() => _orderLogic.ReadElement(new OrderSearchModel + { + ImplementerId = implementer.Id, + Status = OrderStatus.Выполняется + })); + if (runOrder == null) + { + return; + } + _logger.LogDebug("DoWork. Worker {Id} back to order {Order}", implementer.Id, runOrder.Id); + // доделываем работу + Thread.Sleep(implementer.WorkExperience * _rnd.Next(100, 300) * runOrder.Count); + _logger.LogDebug("DoWork. Worker {Id} finish order {Order}", implementer.Id, runOrder.Id); + _orderLogic.FinishOrder(new OrderBindingModel + { + Id = runOrder.Id + }); + // отдыхаем + Thread.Sleep(implementer.Qualification * _rnd.Next(10, 100)); + } + // заказа может не быть, просто игнорируем ошибку + catch (InvalidOperationException ex) + { + _logger.LogWarning(ex, "Error try get work"); + } + // а может возникнуть иная ошибка, тогда просто заканчиваем выполнение имитации + catch (Exception ex) + { + _logger.LogError(ex, "Error while do work"); + throw; + } + } + } +}