diff --git a/Confectionery/Confectionery/App.config b/Confectionery/Confectionery/App.config index ee7d169..1e559f8 100644 --- a/Confectionery/Confectionery/App.config +++ b/Confectionery/Confectionery/App.config @@ -6,6 +6,6 @@ - + \ No newline at end of file diff --git a/Confectionery/Confectionery/Confectionery.csproj b/Confectionery/Confectionery/Confectionery.csproj index 8ced976..a8e24e5 100644 --- a/Confectionery/Confectionery/Confectionery.csproj +++ b/Confectionery/Confectionery/Confectionery.csproj @@ -57,6 +57,9 @@ + + Always + Always diff --git a/Confectionery/Confectionery/FormMain.Designer.cs b/Confectionery/Confectionery/FormMain.Designer.cs index f11208c..43fef5e 100644 --- a/Confectionery/Confectionery/FormMain.Designer.cs +++ b/Confectionery/Confectionery/FormMain.Designer.cs @@ -43,6 +43,7 @@ this.buttonCreateOrder = new System.Windows.Forms.Button(); this.buttonIssuedOrder = new System.Windows.Forms.Button(); this.buttonRef = new System.Windows.Forms.Button(); + this.письмаToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit(); this.SuspendLayout(); @@ -53,7 +54,8 @@ this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.справочникиToolStripMenuItem, this.отчётыToolStripMenuItem, - this.запускРаботToolStripMenuItem}); + this.запускРаботToolStripMenuItem, + this.письмаToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; this.menuStrip1.Size = new System.Drawing.Size(1703, 28); @@ -180,6 +182,13 @@ this.buttonRef.UseVisualStyleBackColor = true; this.buttonRef.Click += new System.EventHandler(this.buttonRef_Click); // + // письмаToolStripMenuItem + // + this.письмаToolStripMenuItem.Name = "письмаToolStripMenuItem"; + this.письмаToolStripMenuItem.Size = new System.Drawing.Size(77, 24); + this.письмаToolStripMenuItem.Text = "Письма"; + this.письмаToolStripMenuItem.Click += new System.EventHandler(this.письмаToolStripMenuItem_Click); + // // FormMain // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); @@ -219,5 +228,6 @@ private ToolStripMenuItem клиентыToolStripMenuItem; private ToolStripMenuItem исполнителиToolStripMenuItem; private ToolStripMenuItem запускРаботToolStripMenuItem; + private ToolStripMenuItem письмаToolStripMenuItem; } } \ No newline at end of file diff --git a/Confectionery/Confectionery/FormMain.cs b/Confectionery/Confectionery/FormMain.cs index 8c083db..5b8cc52 100644 --- a/Confectionery/Confectionery/FormMain.cs +++ b/Confectionery/Confectionery/FormMain.cs @@ -49,6 +49,7 @@ namespace Confectionery dataGridView.Columns["ClientFIO"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView.Columns["ImplementerFIO"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView.Columns["ClientId"].Visible = false; + dataGridView.Columns["ClientEmail"].Visible = false; dataGridView.Columns["ImplementerId"].Visible = false; } } @@ -213,5 +214,14 @@ namespace Confectionery _workProcess.DoWork((Program.ServiceProvider?.GetService(typeof(IImplementerLogic)) as IImplementerLogic)!, _orderLogic); MessageBox.Show("Процесс обработки запущен", "Сообщение", MessageBoxButtons.OK, MessageBoxIcon.Information); } + + private void письмаToolStripMenuItem_Click(object sender, EventArgs e) + { + var service = Program.ServiceProvider?.GetService(typeof(FormMails)); + if (service is FormMails form) + { + form.ShowDialog(); + } + } } } diff --git a/Confectionery/Confectionery/Program.cs b/Confectionery/Confectionery/Program.cs index f231258..47ca145 100644 --- a/Confectionery/Confectionery/Program.cs +++ b/Confectionery/Confectionery/Program.cs @@ -7,6 +7,8 @@ using ConfectioneryDataBaseImplement.Implements; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog.Extensions.Logging; +using ConfectioneryBusinessLogic.MailWorker; +using ConfectioneryContracts.BindingModels; namespace Confectionery { @@ -27,7 +29,30 @@ namespace Confectionery var services = new ServiceCollection(); ConfigureServices(services); _serviceProvider = services.BuildServiceProvider(); + try + { + var mailSender = _serviceProvider.GetService(); + mailSender?.MailConfig(new MailConfigBindingModel + { + MailLogin = System.Configuration.ConfigurationManager.AppSettings["MailLogin"] ?? string.Empty, + MailPassword = System.Configuration.ConfigurationManager.AppSettings["MailPassword"] ?? string.Empty, + SmtpClientHost = System.Configuration.ConfigurationManager.AppSettings["SmtpClientHost"] ?? string.Empty, + SmtpClientPort = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["SmtpClientPort"]), + PopHost = System.Configuration.ConfigurationManager.AppSettings["PopHost"] ?? string.Empty, + PopPort = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["PopPort"]) + }); + + // + var timer = new System.Threading.Timer(new TimerCallback(MailCheck!), null, 0, 100000); + } + catch (Exception ex) + { + var logger = _serviceProvider.GetService(); + logger?.LogError(ex, " "); + } Application.Run(_serviceProvider.GetRequiredService()); + + } private static void ConfigureServices(ServiceCollection services) @@ -42,16 +67,19 @@ namespace Confectionery services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -64,6 +92,9 @@ namespace Confectionery services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } + + private static void MailCheck(object obj) => ServiceProvider?.GetService()?.MailCheck(); } } \ No newline at end of file diff --git a/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/ClientLogic.cs b/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/ClientLogic.cs index dddc043..648e97e 100644 --- a/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/ClientLogic.cs +++ b/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/ClientLogic.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace ConfectioneryBusinessLogic.BusinessLogics @@ -16,6 +17,8 @@ namespace ConfectioneryBusinessLogic.BusinessLogics { private readonly ILogger _logger; private readonly IClientStorage _clientStorage; + private Regex validateEmailRegex = new Regex("^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"); + private Regex validatePasswordRegex = new Regex("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).(?=.*?[#?!@$%^&*-]).{10,50}$"); public ClientLogic(ILogger logger, IClientStorage clientStorage) { @@ -108,6 +111,14 @@ namespace ConfectioneryBusinessLogic.BusinessLogics { throw new ArgumentNullException("Нет электронной почты клиента", nameof(model.Email)); } + if (!validateEmailRegex.IsMatch(model.Email)) + { + throw new InvalidOperationException("Почта введена некорректно!"); + } + if (!validatePasswordRegex.IsMatch(model.Password)) + { + throw new InvalidOperationException("Пароль не удовлетворяет требованиям"); + } _logger.LogInformation("Client. ClientFIO:{ClientFIO}. Password:{Password}. Email:{Email}. Id:{Id}", model.ClientFIO, model.Password, model.Email, model.Id); var element = _clientStorage.GetElement(new ClientSearchModel { diff --git a/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/MessageInfoLogic.cs b/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/MessageInfoLogic.cs index 04d6f46..0304ec2 100644 --- a/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/MessageInfoLogic.cs +++ b/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/MessageInfoLogic.cs @@ -16,11 +16,13 @@ namespace ConfectioneryBusinessLogic.BusinessLogics { private readonly ILogger _logger; private readonly IMessageInfoStorage _messageStorage; + private readonly IClientLogic _clientLogic; - public MessageInfoLogic(ILogger logger, IMessageInfoStorage logic) + public MessageInfoLogic(ILogger logger, IMessageInfoStorage logic, IClientLogic clientLogic) { _logger = logger; _messageStorage = logic; + _clientLogic = clientLogic; } public List? ReadList(MessageInfoSearchModel? model) @@ -39,6 +41,7 @@ namespace ConfectioneryBusinessLogic.BusinessLogics public bool Create(MessageInfoBindingModel model) { CheckModel(model); + model.ClientId = _clientLogic.ReadElement(new ClientSearchModel() { Email = model.SenderName })?.Id; if (_messageStorage.Insert(model) == null) { _logger.LogWarning("Insert operation failed"); diff --git a/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/OrderLogic.cs b/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/OrderLogic.cs index 42c6155..6303230 100644 --- a/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/OrderLogic.cs +++ b/Confectionery/ConfectioneryBusinessLogic/BusinessLogics/OrderLogic.cs @@ -1,4 +1,5 @@ -using ConfectioneryContracts.BindingModels; +using ConfectioneryBusinessLogic.MailWorker; +using ConfectioneryContracts.BindingModels; using ConfectioneryContracts.BusinessLogicsContracts; using ConfectioneryContracts.SearchModels; using ConfectioneryContracts.StoragesContracts; @@ -17,12 +18,14 @@ namespace ConfectioneryBusinessLogic.BusinessLogics { private readonly ILogger _logger; private readonly IOrderStorage _orderStorage; + private readonly AbstractMailWorker _mailWorker; static readonly object _lock = new object(); - public OrderLogic(ILogger logger, IOrderStorage orderStorage) + public OrderLogic(ILogger logger, IOrderStorage orderStorage, AbstractMailWorker mailWorker) { _logger = logger; _orderStorage = orderStorage; + _mailWorker = mailWorker; } public bool CreateOrder(OrderBindingModel model) @@ -30,11 +33,19 @@ namespace ConfectioneryBusinessLogic.BusinessLogics CheckModel(model); if (model.Status != OrderStatus.Неизвестен) return false; model.Status = OrderStatus.Принят; - if (_orderStorage.Insert(model) == null) + var order = _orderStorage.Insert(model); + if (order == null) { _logger.LogWarning("Insert operation failed"); return false; } + _mailWorker.MailSendAsync(new MailSendInfoBindingModel() + { + MailAddress = order.ClientEmail, + Subject = "Создан заказ №" + order.Id, + Text = $"Создан заказ №{order.Id} от {order.DateCreate} на кондитерское изделие {order.PastryName} в количестве {order.Count} шт. Сумма заказа: {order.Sum}." + + }); return true; } @@ -56,7 +67,14 @@ namespace ConfectioneryBusinessLogic.BusinessLogics throw new InvalidOperationException("Заказ должен быть переведен в статус готовности перед выдачей!"); } model.Status = OrderStatus.Выдан; - _orderStorage.Update(model); + var order = _orderStorage.Update(model); + _mailWorker.MailSendAsync(new MailSendInfoBindingModel() + { + MailAddress = order.ClientEmail, + Subject = $"Заказ №{order.Id}. Статус изменен на Выдан", + Text = $"Выдан заказ №{order.Id} от {order.DateCreate} на кондитерское изделие {order.PastryName} в количестве {order.Count} шт. Сумма заказа: {order.Sum}." + + }); return true; } @@ -79,7 +97,14 @@ namespace ConfectioneryBusinessLogic.BusinessLogics } model.Status = OrderStatus.Готов; model.DateImplement = DateTime.Now; - _orderStorage.Update(model); + var order = _orderStorage.Update(model); + _mailWorker.MailSendAsync(new MailSendInfoBindingModel() + { + MailAddress = order.ClientEmail, + Subject = $"Заказ №{order.Id}. Статус изменен на Готов", + Text = $"Готов заказ №{order.Id} от {order.DateCreate} на кондитерское изделие {order.PastryName} в количестве {order.Count} шт. Сумма заказа: {order.Sum}." + + }); return true; } @@ -133,7 +158,14 @@ namespace ConfectioneryBusinessLogic.BusinessLogics throw new InvalidOperationException("Заказ должен быть переведен в статус принятого перед его выполнением!"); } model.Status = OrderStatus.Выполняется; - _orderStorage.Update(model); + var order = _orderStorage.Update(model); + _mailWorker.MailSendAsync(new MailSendInfoBindingModel() + { + MailAddress = order.ClientEmail, + Subject = $"Заказ №{order.Id}. Статус изменен на Выполняется", + Text = $"Выполняется заказ №{order.Id} от {order.DateCreate} на кондитерское изделие {order.PastryName} в количестве {order.Count} шт. Сумма заказа: {order.Sum}." + + }); return true; } } diff --git a/Confectionery/ConfectioneryClientApp/Controllers/HomeController.cs b/Confectionery/ConfectioneryClientApp/Controllers/HomeController.cs index 5567964..d7001f7 100644 --- a/Confectionery/ConfectioneryClientApp/Controllers/HomeController.cs +++ b/Confectionery/ConfectioneryClientApp/Controllers/HomeController.cs @@ -143,5 +143,15 @@ namespace ConfectioneryClientApp.Controllers var prod = APIClient.GetRequest($"api/main/getpastry?pastryId={product}"); return count * (prod?.Price ?? 1); } + + [HttpGet] + public IActionResult Mails() + { + if (APIClient.Client == null) + { + return Redirect("~/Home/Enter"); + } + return View(APIClient.GetRequest>($"api/client/getmessages?clientId={APIClient.Client.Id}")); + } } } \ No newline at end of file diff --git a/Confectionery/ConfectioneryClientApp/Views/Home/Mails.cshtml b/Confectionery/ConfectioneryClientApp/Views/Home/Mails.cshtml new file mode 100644 index 0000000..45afc00 --- /dev/null +++ b/Confectionery/ConfectioneryClientApp/Views/Home/Mails.cshtml @@ -0,0 +1,54 @@ +@using ConfectioneryContracts.ViewModels + +@model List + +@{ + ViewData["Title"] = "Mails"; +} + +
+

Заказы

+
+ + +
+ @{ + if (Model == null) + { +

Авторизируйтесь

+ return; + } + + + + + + + + + + + @foreach (var item in Model) + { + + + + + + } + +
+ Дата письма + + Заголовок + + Текст +
+ @Html.DisplayFor(modelItem => item.DateDelivery) + + @Html.DisplayFor(modelItem => item.Subject) + + @Html.DisplayFor(modelItem => item.Body) +
+ } +
\ No newline at end of file diff --git a/Confectionery/ConfectioneryClientApp/Views/Shared/_Layout.cshtml b/Confectionery/ConfectioneryClientApp/Views/Shared/_Layout.cshtml index eed0c70..7617a2e 100644 --- a/Confectionery/ConfectioneryClientApp/Views/Shared/_Layout.cshtml +++ b/Confectionery/ConfectioneryClientApp/Views/Shared/_Layout.cshtml @@ -26,6 +26,9 @@ + diff --git a/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs b/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs index 5695edf..2603110 100644 --- a/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs +++ b/Confectionery/ConfectioneryContracts/ViewModels/OrderViewModel.cs @@ -22,6 +22,7 @@ namespace ConfectioneryContracts.ViewModels public int ClientId { get; set; } [DisplayName("ФИО клиента")] public string ClientFIO { get; set; } = string.Empty; + public string ClientEmail { get; set; } = string.Empty; [DisplayName("Количество")] public int Count { get; set; } [DisplayName("Сумма")] diff --git a/Confectionery/ConfectioneryDataBaseImplement/Implements/OrderStorage.cs b/Confectionery/ConfectioneryDataBaseImplement/Implements/OrderStorage.cs index c87b380..d9de6cc 100644 --- a/Confectionery/ConfectioneryDataBaseImplement/Implements/OrderStorage.cs +++ b/Confectionery/ConfectioneryDataBaseImplement/Implements/OrderStorage.cs @@ -96,7 +96,7 @@ namespace ConfectioneryDataBaseImplement.Implements public OrderViewModel? Update(OrderBindingModel model) { using var context = new ConfectioneryDatabase(); - var order = context.Orders.FirstOrDefault(x => x.Id == model.Id); + var order = context.Orders.Include(x => x.Client).FirstOrDefault(x => x.Id == model.Id); if (order == null) { return null; diff --git a/Confectionery/ConfectioneryDataBaseImplement/Models/Order.cs b/Confectionery/ConfectioneryDataBaseImplement/Models/Order.cs index 2940516..1dfa5ec 100644 --- a/Confectionery/ConfectioneryDataBaseImplement/Models/Order.cs +++ b/Confectionery/ConfectioneryDataBaseImplement/Models/Order.cs @@ -68,6 +68,7 @@ namespace ConfectioneryDataBaseImplement.Models PastryId = PastryId, ClientId = ClientId, ClientFIO = Client.ClientFIO, + ClientEmail = Client.Email, Count = Count, Sum = Sum, Status = Status, diff --git a/Confectionery/ConfectioneryRestApi/ConfectioneryRestApi.csproj b/Confectionery/ConfectioneryRestApi/ConfectioneryRestApi.csproj index 8e50618..934a6ef 100644 --- a/Confectionery/ConfectioneryRestApi/ConfectioneryRestApi.csproj +++ b/Confectionery/ConfectioneryRestApi/ConfectioneryRestApi.csproj @@ -16,4 +16,10 @@
+ + + Always + + + diff --git a/Confectionery/ConfectioneryRestApi/Controllers/ClientController.cs b/Confectionery/ConfectioneryRestApi/Controllers/ClientController.cs index 46a4c9f..65a9e3e 100644 --- a/Confectionery/ConfectioneryRestApi/Controllers/ClientController.cs +++ b/Confectionery/ConfectioneryRestApi/Controllers/ClientController.cs @@ -13,11 +13,13 @@ namespace ConfectioneryRestApi.Controllers private readonly ILogger _logger; private readonly IClientLogic _logic; + private readonly IMessageInfoLogic _mailLogic; - public ClientController(IClientLogic logic, ILogger logger) + public ClientController(IClientLogic logic, ILogger logger, IMessageInfoLogic mailLogic) { _logger = logger; _logic = logic; + _mailLogic = mailLogic; } [HttpGet] @@ -65,5 +67,21 @@ namespace ConfectioneryRestApi.Controllers throw; } } + [HttpGet] + public List? GetMessages(int clientId) + { + try + { + return _mailLogic.ReadList(new MessageInfoSearchModel + { + ClientId = clientId + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка получения писем клиента"); + throw; + } + } } } diff --git a/Confectionery/ConfectioneryRestApi/Program.cs b/Confectionery/ConfectioneryRestApi/Program.cs index 5f2e382..afb481f 100644 --- a/Confectionery/ConfectioneryRestApi/Program.cs +++ b/Confectionery/ConfectioneryRestApi/Program.cs @@ -1,4 +1,6 @@ using ConfectioneryBusinessLogic.BusinessLogics; +using ConfectioneryBusinessLogic.MailWorker; +using ConfectioneryContracts.BindingModels; using ConfectioneryContracts.BusinessLogicsContracts; using ConfectioneryContracts.StoragesContracts; using ConfectioneryDataBaseImplement.Implements; @@ -12,11 +14,16 @@ builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); 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(); @@ -26,7 +33,16 @@ builder.Services.AddSwaggerGen(c => }); var app = builder.Build(); - +var mailSender = app.Services.GetService(); +mailSender?.MailConfig(new MailConfigBindingModel +{ + MailLogin = builder.Configuration?.GetSection("MailLogin")?.Value?.ToString() ?? string.Empty, + MailPassword = builder.Configuration?.GetSection("MailPassword")?.Value?.ToString() ?? string.Empty, + SmtpClientHost = builder.Configuration?.GetSection("SmtpClientHost")?.Value?.ToString() ?? string.Empty, + SmtpClientPort = Convert.ToInt32(builder.Configuration?.GetSection("SmtpClientPort")?.Value?.ToString()), + PopHost = builder.Configuration?.GetSection("PopHost")?.Value?.ToString() ?? string.Empty, + PopPort = Convert.ToInt32(builder.Configuration?.GetSection("PopPort")?.Value?.ToString()) +}); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { diff --git a/Confectionery/ConfectioneryRestApi/appsettings.json b/Confectionery/ConfectioneryRestApi/appsettings.json index 10f68b8..c6ac744 100644 --- a/Confectionery/ConfectioneryRestApi/appsettings.json +++ b/Confectionery/ConfectioneryRestApi/appsettings.json @@ -5,5 +5,11 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "SmtpClientHost": "smtp.gmail.com", + "SmtpClientPort": "587", + "PopHost": "pop.gmail.com", + "PopPort": "995", + "MailLogin": "rpplab7@gmail.com", + "MailPassword": "edjc dmsf pqne gxwy" }