From 052d6a8ce2b9bbc36f55e161c17854d0e69f610b Mon Sep 17 00:00:00 2001 From: Inohara Date: Sat, 8 Apr 2023 18:30:16 +0400 Subject: [PATCH] =?UTF-8?q?=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BusinessLogics/OrderLogic.cs | 17 ++ .../BusinessLogics/WorkModeling.cs | 143 ++++++++++ IceCreamShop/IceCreamShop/Program.cs | 2 + .../BusinessLogicsContracts/IWorkProcess.cs | 10 + .../20230408135215_implementer.Designer.cs | 257 ++++++++++++++++++ .../Migrations/20230408135215_implementer.cs | 67 +++++ .../IceCreamShopDatabaseModelSnapshot.cs | 43 +++ .../Controllers/ImplementerController.cs | 7 +- 8 files changed, 543 insertions(+), 3 deletions(-) create mode 100644 IceCreamShop/IceCreamBusinessLogic/BusinessLogics/WorkModeling.cs create mode 100644 IceCreamShop/IceCreamShopContracts/BusinessLogicsContracts/IWorkProcess.cs create mode 100644 IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.Designer.cs create mode 100644 IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.cs diff --git a/IceCreamShop/IceCreamBusinessLogic/BusinessLogics/OrderLogic.cs b/IceCreamShop/IceCreamBusinessLogic/BusinessLogics/OrderLogic.cs index 9d31ba3..f4f83be 100644 --- a/IceCreamShop/IceCreamBusinessLogic/BusinessLogics/OrderLogic.cs +++ b/IceCreamShop/IceCreamBusinessLogic/BusinessLogics/OrderLogic.cs @@ -134,5 +134,22 @@ namespace IceCreamBusinessLogic.BusinessLogics } return true; } + + 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; + } } } diff --git a/IceCreamShop/IceCreamBusinessLogic/BusinessLogics/WorkModeling.cs b/IceCreamShop/IceCreamBusinessLogic/BusinessLogics/WorkModeling.cs new file mode 100644 index 0000000..20f9fd0 --- /dev/null +++ b/IceCreamShop/IceCreamBusinessLogic/BusinessLogics/WorkModeling.cs @@ -0,0 +1,143 @@ +using IceCreamShopContracts.BindingModels; +using IceCreamShopContracts.BusinessLogicsContracts; +using IceCreamShopContracts.SearchModels; +using IceCreamShopContracts.ViewModels; +using AbstractIceCreamShopDataModels.Enums; +using Microsoft.Extensions.Logging; + +namespace IceCreamBusinessLogic.BusinessLogic +{ + 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 { Statusses = new() { OrderStatus.Принят, 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, orders); + + 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, List allOrders) + { + if (_orderLogic == null || implementer == null || allOrders == null || allOrders.Count == 0) + { + return; + } + try + { + // Выбираем из всех заказов тот, который выполняется данным исполнителем + var runOrder = await Task.Run(() => allOrders.FirstOrDefault(x => x.ImplementerId == implementer.Id && x.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; + } + } + } +} diff --git a/IceCreamShop/IceCreamShop/Program.cs b/IceCreamShop/IceCreamShop/Program.cs index a6b4ea8..f78aa62 100644 --- a/IceCreamShop/IceCreamShop/Program.cs +++ b/IceCreamShop/IceCreamShop/Program.cs @@ -8,6 +8,7 @@ using NLog.Extensions.Logging; using IceCreamBusinessLogic.OfficePackage.Implements; using IceCreamBusinessLogic.OfficePackage; using IceCreamShopDatabaseImplement.Implements; +using IceCreamBusinessLogic.BusinessLogic; namespace IceCreamShop { @@ -50,6 +51,7 @@ namespace IceCreamShop services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/IceCreamShop/IceCreamShopContracts/BusinessLogicsContracts/IWorkProcess.cs b/IceCreamShop/IceCreamShopContracts/BusinessLogicsContracts/IWorkProcess.cs new file mode 100644 index 0000000..8986976 --- /dev/null +++ b/IceCreamShop/IceCreamShopContracts/BusinessLogicsContracts/IWorkProcess.cs @@ -0,0 +1,10 @@ +namespace IceCreamShopContracts.BusinessLogicsContracts +{ + public interface IWorkProcess + { + /// + /// Запуск работ + /// + void DoWork(IImplementerLogic implementerLogic, IOrderLogic orderLogic); + } +} diff --git a/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.Designer.cs b/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.Designer.cs new file mode 100644 index 0000000..8f06b20 --- /dev/null +++ b/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.Designer.cs @@ -0,0 +1,257 @@ +// +using System; +using IceCreamShopDatabaseImplement; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace IceCreamShopDatabaseImplement.Migrations +{ + [DbContext(typeof(IceCreamShopDatabase))] + [Migration("20230408135215_implementer")] + partial class implementer + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClientFIO") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Clients"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Component", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ComponentName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Cost") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.ToTable("Components"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.IceCream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IceCreamName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.ToTable("IceCreams"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.IceCreamComponent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ComponentId") + .HasColumnType("int"); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("IceCreamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ComponentId"); + + b.HasIndex("IceCreamId"); + + b.ToTable("IceCreamComponents"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Implementer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ImplementerFIO") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Qualification") + .HasColumnType("int"); + + b.Property("WorkExperience") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Implementers"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("DateCreate") + .HasColumnType("datetime2"); + + b.Property("DateImplement") + .HasColumnType("datetime2"); + + b.Property("IceCreamId") + .HasColumnType("int"); + + b.Property("ImplementerId") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Sum") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("IceCreamId"); + + b.HasIndex("ImplementerId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.IceCreamComponent", b => + { + b.HasOne("IceCreamShopDatabaseImplement.Models.Component", "Component") + .WithMany("IceCreamComponents") + .HasForeignKey("ComponentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("IceCreamShopDatabaseImplement.Models.IceCream", "IceCream") + .WithMany("Components") + .HasForeignKey("IceCreamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Component"); + + b.Navigation("IceCream"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Order", b => + { + b.HasOne("IceCreamShopDatabaseImplement.Models.Client", "Client") + .WithMany("Orders") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("IceCreamShopDatabaseImplement.Models.IceCream", "IceCream") + .WithMany("Orders") + .HasForeignKey("IceCreamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("IceCreamShopDatabaseImplement.Models.Implementer", "Implementer") + .WithMany("Orders") + .HasForeignKey("ImplementerId"); + + b.Navigation("Client"); + + b.Navigation("IceCream"); + + b.Navigation("Implementer"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Client", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Component", b => + { + b.Navigation("IceCreamComponents"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.IceCream", b => + { + b.Navigation("Components"); + + b.Navigation("Orders"); + }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Implementer", b => + { + b.Navigation("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.cs b/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.cs new file mode 100644 index 0000000..34ba28a --- /dev/null +++ b/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/20230408135215_implementer.cs @@ -0,0 +1,67 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace IceCreamShopDatabaseImplement.Migrations +{ + /// + public partial class implementer : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ImplementerId", + table: "Orders", + type: "int", + nullable: true); + + migrationBuilder.CreateTable( + name: "Implementers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ImplementerFIO = table.Column(type: "nvarchar(max)", nullable: false), + Password = table.Column(type: "nvarchar(max)", nullable: false), + WorkExperience = table.Column(type: "int", nullable: false), + Qualification = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Implementers", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Orders_ImplementerId", + table: "Orders", + column: "ImplementerId"); + + migrationBuilder.AddForeignKey( + name: "FK_Orders_Implementers_ImplementerId", + table: "Orders", + column: "ImplementerId", + principalTable: "Implementers", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Orders_Implementers_ImplementerId", + table: "Orders"); + + migrationBuilder.DropTable( + name: "Implementers"); + + migrationBuilder.DropIndex( + name: "IX_Orders_ImplementerId", + table: "Orders"); + + migrationBuilder.DropColumn( + name: "ImplementerId", + table: "Orders"); + } + } +} diff --git a/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/IceCreamShopDatabaseModelSnapshot.cs b/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/IceCreamShopDatabaseModelSnapshot.cs index 5535d99..e6fe34e 100644 --- a/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/IceCreamShopDatabaseModelSnapshot.cs +++ b/IceCreamShop/IceCreamShopDatabaseImplement/Migrations/IceCreamShopDatabaseModelSnapshot.cs @@ -113,6 +113,33 @@ namespace IceCreamShopDatabaseImplement.Migrations b.ToTable("IceCreamComponents"); }); + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Implementer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ImplementerFIO") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Qualification") + .HasColumnType("int"); + + b.Property("WorkExperience") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Implementers"); + }); + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Order", b => { b.Property("Id") @@ -136,6 +163,9 @@ namespace IceCreamShopDatabaseImplement.Migrations b.Property("IceCreamId") .HasColumnType("int"); + b.Property("ImplementerId") + .HasColumnType("int"); + b.Property("Status") .HasColumnType("int"); @@ -148,6 +178,8 @@ namespace IceCreamShopDatabaseImplement.Migrations b.HasIndex("IceCreamId"); + b.HasIndex("ImplementerId"); + b.ToTable("Orders"); }); @@ -184,9 +216,15 @@ namespace IceCreamShopDatabaseImplement.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("IceCreamShopDatabaseImplement.Models.Implementer", "Implementer") + .WithMany("Orders") + .HasForeignKey("ImplementerId"); + b.Navigation("Client"); b.Navigation("IceCream"); + + b.Navigation("Implementer"); }); modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Client", b => @@ -205,6 +243,11 @@ namespace IceCreamShopDatabaseImplement.Migrations b.Navigation("Orders"); }); + + modelBuilder.Entity("IceCreamShopDatabaseImplement.Models.Implementer", b => + { + b.Navigation("Orders"); + }); #pragma warning restore 612, 618 } } diff --git a/IceCreamShop/IceCreamShopRestApi/Controllers/ImplementerController.cs b/IceCreamShop/IceCreamShopRestApi/Controllers/ImplementerController.cs index adc91d8..82f8444 100644 --- a/IceCreamShop/IceCreamShopRestApi/Controllers/ImplementerController.cs +++ b/IceCreamShop/IceCreamShopRestApi/Controllers/ImplementerController.cs @@ -1,4 +1,5 @@ -using IceCreamShopContracts.BindingModels; +using AbstractIceCreamShopDataModels.Enums; +using IceCreamShopContracts.BindingModels; using IceCreamShopContracts.BusinessLogicsContracts; using IceCreamShopContracts.SearchModels; using IceCreamShopContracts.ViewModels; @@ -48,7 +49,7 @@ namespace IceCreamShopRestApi.Controllers { return _order.ReadList(new OrderSearchModel { - //Status = OrderStatus.Принят + Statusses = new() { OrderStatus.Принят } }); } catch (Exception ex) @@ -65,7 +66,7 @@ namespace IceCreamShopRestApi.Controllers { return _order.ReadElement(new OrderSearchModel { - //ImplementerId = implementerId + ImplementerId = implementerId }); } catch (Exception ex)