diff --git a/Confectionery/ConfectioneryBusinessLogic/ClientLogic.cs b/Confectionery/ConfectioneryBusinessLogic/ClientLogic.cs index 0ebb028..807e77c 100644 --- a/Confectionery/ConfectioneryBusinessLogic/ClientLogic.cs +++ b/Confectionery/ConfectioneryBusinessLogic/ClientLogic.cs @@ -4,6 +4,7 @@ using ConfectioneryContracts.SearchModels; using ConfectioneryContracts.StoragesContracts; using ConfectioneryContracts.ViewModels; using Microsoft.Extensions.Logging; +using System.Text.RegularExpressions; using System; using System.Collections.Generic; using System.Linq; @@ -89,36 +90,36 @@ namespace ConfectioneryBusinessLogic private void CheckModel(ClientBindingModel model, bool withParams = true) { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - if (!withParams) - { - return; - } - if (string.IsNullOrEmpty(model.ClientFIO)) - { - throw new ArgumentNullException("Нет ФИО пользователя", nameof(model.ClientFIO)); - } - if (string.IsNullOrEmpty(model.Email)) - { - throw new ArgumentNullException("Нет почты пользователя", nameof(model.Email)); - } - if (string.IsNullOrEmpty(model.Password)) - { - throw new ArgumentNullException("Нет пароля пользователя", nameof(model.Password)); - } - _logger.LogInformation("Client. ClientFIO:{ClientFIO}.Email:{Email}.Password:{Password}.Id:{Id}", - model.ClientFIO, model.Email, model.Password, model.Id); - var element = _clientStorage.GetElement(new ClientSearchModel - { - ClientFIO = model.ClientFIO - }); - if (element != null && element.Id != model.Id) - { - throw new InvalidOperationException("Клиент с таким именем уже есть"); - } - } + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + if (!withParams) + { + return; + } + if (string.IsNullOrEmpty(model.ClientFIO)) + { + throw new ArgumentNullException("Нет ФИО пользователя", nameof(model.ClientFIO)); + } + if (string.IsNullOrEmpty(model.Email) || !Regex.IsMatch(model.Email, @"^[a-z0-9._%+-]+\@([a-z0-9-]+\.)+[a-z]{2,4}$")) + { + throw new ArgumentNullException("Неверно введена почта", nameof(model.Email)); + } + if (string.IsNullOrEmpty(model.Password) || !Regex.IsMatch(model.Password, @"^(?=.*[A-Za-z])(?=.*\d)(?=.*[^A-Za-z0-9\n]).{10,50}$")) + { + throw new ArgumentNullException("Не указан правильный пароль", nameof(model.Password)); + } + _logger.LogInformation("Client. ClientFIO:{ClientFIO}.Email:{Email}.Id:{Id}", + model.ClientFIO, model.Email, model.Id); + var element = _clientStorage.GetElement(new ClientSearchModel + { + ClientFIO = model.ClientFIO + }); + if (element != null && element.Id != model.Id) + { + throw new InvalidOperationException("Клиент с таким именем уже есть"); + } + } } } diff --git a/Confectionery/ConfectioneryBusinessLogic/MessageInfoLogic.cs b/Confectionery/ConfectioneryBusinessLogic/MessageInfoLogic.cs index bdae3aa..68eaf1d 100644 --- a/Confectionery/ConfectioneryBusinessLogic/MessageInfoLogic.cs +++ b/Confectionery/ConfectioneryBusinessLogic/MessageInfoLogic.cs @@ -61,7 +61,7 @@ namespace ConfectioneryBusinessLogic } if (string.IsNullOrEmpty(model.SenderName)) { - throw new ArgumentNullException("Не указао почта", nameof(model.SenderName)); + throw new ArgumentNullException("Не указана почта", nameof(model.SenderName)); } if (string.IsNullOrEmpty(model.Subject)) { @@ -79,7 +79,7 @@ namespace ConfectioneryBusinessLogic }); if (element == null) { - _logger.LogWarning("Не удалоссь найти клиента, отправившего письмо с адреса Email:{Email}", model.SenderName); + _logger.LogWarning("Не удалось найти клиента, отправившего письмо с адреса Email:{Email}", model.SenderName); } else { diff --git a/Confectionery/ConfectioneryBusinessLogic/OrderLogic.cs b/Confectionery/ConfectioneryBusinessLogic/OrderLogic.cs index c6e9e30..fcb1a9b 100644 --- a/Confectionery/ConfectioneryBusinessLogic/OrderLogic.cs +++ b/Confectionery/ConfectioneryBusinessLogic/OrderLogic.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml.Linq; namespace ConfectioneryBusinessLogic { @@ -17,11 +18,14 @@ namespace ConfectioneryBusinessLogic { private readonly ILogger _logger; private readonly IOrderStorage _orderStorage; - public OrderLogic(ILogger logger, IOrderStorage orderStorage) + private readonly AbstractMailWorker _mailWorker; + static readonly object _locker = new object(); + public OrderLogic(ILogger logger, IOrderStorage orderStorage, AbstractMailWorker mailWorker) { _logger = logger; _orderStorage = orderStorage; - } + _mailWorker = mailWorker; + } public OrderViewModel? ReadElement(OrderSearchModel model) { if (model == null) @@ -58,23 +62,28 @@ namespace ConfectioneryBusinessLogic if (model.Status != OrderStatus.Неизвестен) return false; model.Status = OrderStatus.Принят; - if (_orderStorage.Insert(model) == null) - { - _logger.LogWarning("Insert operation failed"); - return false; - } - return true; - } - - public bool TakeOrderInWork(OrderBindingModel model) - { - return ChangeStatus(model, OrderStatus.Выполняется); - } - - public bool FinishOrder(OrderBindingModel model) - { - return ChangeStatus(model, OrderStatus.Готов); - } + var element = _orderStorage.Insert(model); + if (element == null) + { + _logger.LogWarning("Insert operation failed"); + return false; + } + Task.Run(() => _mailWorker.MailSendAsync(new MailSendInfoBindingModel + { + MailAddress = element.ClientEmail, + Subject = $"Изменение статуса заказа №{element.Id}", + Text = $"Заказ №{element.Id} на кондитерское изделие {element.PastryName} от {element.DateCreate} на сумму {element.Sum} принят." + })); + return true; + } + public bool TakeOrderInWork(OrderBindingModel model) + { + return ChangeStatus(model, OrderStatus.Выполняется); + } + public bool FinishOrder(OrderBindingModel model) + { + return ChangeStatus(model, OrderStatus.Готов); + } public bool DeliveryOrder(OrderBindingModel model) { @@ -141,6 +150,13 @@ namespace ConfectioneryBusinessLogic _logger.LogWarning("Update operation failed"); return false; } + string DateInfo = model.DateImplement.HasValue ? $"Дата выполнения {model.DateImplement}" : ""; + Task.Run(() => _mailWorker.MailSendAsync(new MailSendInfoBindingModel + { + MailAddress = element.ClientEmail, + Subject = $"Изменение статуса заказа №{element.Id}", + Text = $"Заказ №{element.Id} на кондитерское изделие {element.PastryName} от {element.DateCreate} на сумму {element.Sum} {model.Status}. {DateInfo}" + })); return true; } _logger.LogWarning("Changing status operation faled: Current-{Status}:required-{requiredStatus}.", model.Status, requiredStatus); diff --git a/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs b/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs index 1eaa2a5..968a73c 100644 --- a/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs +++ b/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs @@ -17,6 +17,7 @@ namespace ConfectioneryContracts.ViewModels [DisplayName("ФИО клиента")] public string ClientFIO { get; set; } = string.Empty; + public string ClientEmail { get; set; } = string.Empty; public int? ImplementerId { get; set; } [DisplayName("Исполнитель")] public string? ImplementerFIO { get; set; } = null; diff --git a/Confectionery/ConfectioneryDatabaseImplement/Client.cs b/Confectionery/ConfectioneryDatabaseImplement/Client.cs index 5d63231..4362564 100644 --- a/Confectionery/ConfectioneryDatabaseImplement/Client.cs +++ b/Confectionery/ConfectioneryDatabaseImplement/Client.cs @@ -22,10 +22,13 @@ namespace ConfectioneryDatabaseImplement.Models [Required] public string Password { get; set; } = string.Empty; - [ForeignKey("ClientId")] - public virtual List Orders { get; set; } = new(); + [ForeignKey("ClientId")] + public virtual List ClientOrders { get; set; } = new(); - public static Client? Create(ClientBindingModel model) + [ForeignKey("ClientId")] + public virtual List ClientMessages { get; set; } = new(); + + public static Client? Create(ClientBindingModel model) { if (model == null) { diff --git a/Confectionery/ConfectioneryDatabaseImplement/ClientStorage.cs b/Confectionery/ConfectioneryDatabaseImplement/ClientStorage.cs index 6a4be8b..436e80f 100644 --- a/Confectionery/ConfectioneryDatabaseImplement/ClientStorage.cs +++ b/Confectionery/ConfectioneryDatabaseImplement/ClientStorage.cs @@ -36,11 +36,10 @@ namespace ConfectioneryDatabaseImplement.Implements return null; } using var context = new ConfectioneryDatabase(); - return context.Clients.FirstOrDefault(x => - (!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && !string.IsNullOrEmpty(model.Password) && x.Password == model.Password) || - (model.Id.HasValue && x.Id == model.Id)) - ?.GetViewModel; - } + return context.Clients.FirstOrDefault(x => (model.Id.HasValue && x.Id == model.Id) || + (!string.IsNullOrEmpty(model.ClientFIO) && x.ClientFIO == model.ClientFIO) || + (!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && (string.IsNullOrEmpty(model.Password) || x.Password == model.Password)))?.GetViewModel; + } public ClientViewModel? Insert(ClientBindingModel model) { diff --git a/Confectionery/ConfectioneryDatabaseImplement/ConfectioneryDatabase.cs b/Confectionery/ConfectioneryDatabaseImplement/ConfectioneryDatabase.cs index 4a14f7d..2007c1b 100644 --- a/Confectionery/ConfectioneryDatabaseImplement/ConfectioneryDatabase.cs +++ b/Confectionery/ConfectioneryDatabaseImplement/ConfectioneryDatabase.cs @@ -15,7 +15,7 @@ namespace ConfectioneryDatabaseImplement { if (optionsBuilder.IsConfigured == false) { - optionsBuilder.UseSqlServer(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=ConfectioneryDatabaseNewMail;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True"); + optionsBuilder.UseSqlServer(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=ConfectioneryDatabaseNewMailDB;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True"); } base.OnConfiguring(optionsBuilder); } diff --git a/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240523044722_InitialMail.cs b/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240523044722_InitialMail.cs deleted file mode 100644 index 2b76485..0000000 --- a/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240523044722_InitialMail.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace ConfectioneryDatabaseImplement.Migrations -{ - /// - public partial class InitialMail : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "MessageInfos", - columns: table => new - { - MessageId = table.Column(type: "nvarchar(450)", nullable: false), - ClientId = table.Column(type: "int", nullable: true), - SenderName = table.Column(type: "nvarchar(max)", nullable: false), - DateDelivery = table.Column(type: "datetime2", nullable: false), - Subject = table.Column(type: "nvarchar(max)", nullable: false), - Body = table.Column(type: "nvarchar(max)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_MessageInfos", x => x.MessageId); - table.ForeignKey( - name: "FK_MessageInfos_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_MessageInfos_ClientId", - table: "MessageInfos", - column: "ClientId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "MessageInfos"); - } - } -} diff --git a/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240523044722_InitialMail.Designer.cs b/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240602085942_InitialMail.Designer.cs similarity index 97% rename from Confectionery/ConfectioneryDatabaseImplement/Migrations/20240523044722_InitialMail.Designer.cs rename to Confectionery/ConfectioneryDatabaseImplement/Migrations/20240602085942_InitialMail.Designer.cs index 805ac6d..4a1f863 100644 --- a/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240523044722_InitialMail.Designer.cs +++ b/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240602085942_InitialMail.Designer.cs @@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace ConfectioneryDatabaseImplement.Migrations { [DbContext(typeof(ConfectioneryDatabase))] - [Migration("20240523044722_InitialMail")] + [Migration("20240602085942_InitialMail")] partial class InitialMail { /// @@ -219,7 +219,7 @@ namespace ConfectioneryDatabaseImplement.Migrations modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.MessageInfo", b => { b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client") - .WithMany() + .WithMany("ClientMessages") .HasForeignKey("ClientId"); b.Navigation("Client"); @@ -228,7 +228,7 @@ namespace ConfectioneryDatabaseImplement.Migrations modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Order", b => { b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client") - .WithMany("Orders") + .WithMany("ClientOrders") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -271,7 +271,9 @@ namespace ConfectioneryDatabaseImplement.Migrations modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Client", b => { - b.Navigation("Orders"); + b.Navigation("ClientMessages"); + + b.Navigation("ClientOrders"); }); modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Component", b => diff --git a/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240602085942_InitialMail.cs b/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240602085942_InitialMail.cs new file mode 100644 index 0000000..4598767 --- /dev/null +++ b/Confectionery/ConfectioneryDatabaseImplement/Migrations/20240602085942_InitialMail.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ConfectioneryDatabaseImplement.Migrations +{ + /// + public partial class InitialMail : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Confectionery/ConfectioneryDatabaseImplement/Migrations/ConfectioneryDatabaseModelSnapshot.cs b/Confectionery/ConfectioneryDatabaseImplement/Migrations/ConfectioneryDatabaseModelSnapshot.cs index 84b0adb..bbf5663 100644 --- a/Confectionery/ConfectioneryDatabaseImplement/Migrations/ConfectioneryDatabaseModelSnapshot.cs +++ b/Confectionery/ConfectioneryDatabaseImplement/Migrations/ConfectioneryDatabaseModelSnapshot.cs @@ -216,7 +216,7 @@ namespace ConfectioneryDatabaseImplement.Migrations modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.MessageInfo", b => { b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client") - .WithMany() + .WithMany("ClientMessages") .HasForeignKey("ClientId"); b.Navigation("Client"); @@ -225,7 +225,7 @@ namespace ConfectioneryDatabaseImplement.Migrations modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Order", b => { b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client") - .WithMany("Orders") + .WithMany("ClientOrders") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -268,7 +268,9 @@ namespace ConfectioneryDatabaseImplement.Migrations modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Client", b => { - b.Navigation("Orders"); + b.Navigation("ClientMessages"); + + b.Navigation("ClientOrders"); }); modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Component", b => diff --git a/Confectionery/ConfectioneryDatabaseImplement/Order.cs b/Confectionery/ConfectioneryDatabaseImplement/Order.cs index 2d4dae4..ab625d6 100644 --- a/Confectionery/ConfectioneryDatabaseImplement/Order.cs +++ b/Confectionery/ConfectioneryDatabaseImplement/Order.cs @@ -76,7 +76,8 @@ namespace ConfectioneryDatabaseImplement.Models Id = Id, ClientId = ClientId, ClientFIO = Client.ClientFIO, - PastryId = PastryId, + ClientEmail = Client.Email, + PastryId = PastryId, PastryName = Pastry.PastryName, ImplementerId = ImplementerId, ImplementerFIO = Implementer != null ? Implementer.ImplementerFIO : null, diff --git a/Confectionery/ConfectioneryRestApi/Program.cs b/Confectionery/ConfectioneryRestApi/Program.cs index 6c027dc..a833af4 100644 --- a/Confectionery/ConfectioneryRestApi/Program.cs +++ b/Confectionery/ConfectioneryRestApi/Program.cs @@ -22,10 +22,10 @@ builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddSingleton(); + builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at -https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { diff --git a/Confectionery/ConfectioneryView/FormMain.cs b/Confectionery/ConfectioneryView/FormMain.cs index 250c8c3..03aa49d 100644 --- a/Confectionery/ConfectioneryView/FormMain.cs +++ b/Confectionery/ConfectioneryView/FormMain.cs @@ -43,6 +43,7 @@ namespace ConfectioneryView dataGridView.DataSource = list; dataGridView.Columns["PastryId"].Visible = false; dataGridView.Columns["ClientId"].Visible = false; + dataGridView.Columns["ClientEmail"].Visible = false; dataGridView.Columns["ImplementerId"].Visible = false; dataGridView.Columns["PastryName"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; diff --git a/Confectionery/ConfectioneryView/Program.cs b/Confectionery/ConfectioneryView/Program.cs index 24bde84..aabcde4 100644 --- a/Confectionery/ConfectioneryView/Program.cs +++ b/Confectionery/ConfectioneryView/Program.cs @@ -63,6 +63,7 @@ namespace ConfectioneryView services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -71,10 +72,12 @@ namespace ConfectioneryView services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddSingleton(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -88,6 +91,7 @@ namespace ConfectioneryView services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } private static void MailCheck(object obj) => ServiceProvider?.GetService()?.MailCheck();