From afd5bd91cebdb8c0d01992f153d1d525c7818a1a Mon Sep 17 00:00:00 2001 From: Katanaa Die Date: Fri, 17 May 2024 23:22:03 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B4=D0=B0=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bar/BarBusinessLogic/BarBusinessLogic.csproj | 1 + .../BusinessLogics/ClientLogic.cs | 5 +- .../BusinessLogics/MessageInfoLogic.cs | 99 +++++++++++++++ .../BusinessLogics/OrderLogic.cs | 62 +++++++-- .../BusinessLogics/WorkModeling.cs | 3 +- .../MailWorker/AbstractMailWorker.cs | 82 ++++++++++++ .../MailWorker/MailKitWorker.cs | 79 ++++++++++++ .../Controllers/HomeController.cs | 10 ++ Bar/BarClientApp/Views/Home/Create.cshtml | 2 +- Bar/BarClientApp/Views/Home/Mails.cshtml | 52 ++++++++ Bar/BarClientApp/Views/Shared/_Layout.cshtml | 3 + .../BindingModels/MailConfigBindingModel.cs | 18 +++ .../BindingModels/MailSendInfoBindingModel.cs | 15 +++ .../BindingModels/MessageInfoBindingModel.cs | 24 ++++ .../IMessageInfoLogic.cs | 17 +++ .../SearchModels/MessageInfoSearchModel.cs | 14 ++ .../StoragesContracts/IMessageInfoStorage.cs | 19 +++ .../ViewModels/MessageInfoViewModel.cs | 25 ++++ Bar/BarDataModels/Models/IMessageInfoModel.cs | 18 +++ Bar/BarDatabaseImplement/BarDatabase.cs | 1 + .../BarDatabaseImplement.csproj | 2 + .../Implements/ClientStorage.cs | 2 +- .../Implements/MessageInfoStorage.cs | 55 ++++++++ ...ner.cs => 20240517095734_init.Designer.cs} | 43 ++++++- ...7092627_init.cs => 20240517095734_init.cs} | 29 +++++ .../Migrations/BarDatabaseModelSnapshot.cs | 41 ++++++ Bar/BarDatabaseImplement/Models/Client.cs | 4 +- .../Models/MessageInfo.cs | 54 ++++++++ Bar/BarFileImplement/DataFileSingleton.cs | 4 + .../Implements/MessageInfoStorage.cs | 58 +++++++++ Bar/BarFileImplement/Models/MessageInfo.cs | 80 ++++++++++++ Bar/BarListImplement/DataListSingleton.cs | 3 + .../Implements/MessageInfoStorage.cs | 73 +++++++++++ Bar/BarListImplement/Models/MessageInfo.cs | 51 ++++++++ .../Controllers/ClientController.cs | 24 +++- Bar/BarRestApi/Program.cs | 17 +++ Bar/BarRestApi/appsettings.json | 9 +- Bar/BarView/App.config | 11 ++ Bar/BarView/FormMail.Designer.cs | 62 +++++++++ Bar/BarView/FormMail.cs | 53 ++++++++ Bar/BarView/FormMail.resx | 120 ++++++++++++++++++ Bar/BarView/FormMain.Designer.cs | 11 +- Bar/BarView/FormMain.cs | 10 +- Bar/BarView/Program.cs | 31 ++++- 44 files changed, 1370 insertions(+), 26 deletions(-) create mode 100644 Bar/BarBusinessLogic/BusinessLogics/MessageInfoLogic.cs create mode 100644 Bar/BarBusinessLogic/MailWorker/AbstractMailWorker.cs create mode 100644 Bar/BarBusinessLogic/MailWorker/MailKitWorker.cs create mode 100644 Bar/BarClientApp/Views/Home/Mails.cshtml create mode 100644 Bar/BarContracts/BindingModels/MailConfigBindingModel.cs create mode 100644 Bar/BarContracts/BindingModels/MailSendInfoBindingModel.cs create mode 100644 Bar/BarContracts/BindingModels/MessageInfoBindingModel.cs create mode 100644 Bar/BarContracts/BusinessLogicsContracts/IMessageInfoLogic.cs create mode 100644 Bar/BarContracts/SearchModels/MessageInfoSearchModel.cs create mode 100644 Bar/BarContracts/StoragesContracts/IMessageInfoStorage.cs create mode 100644 Bar/BarContracts/ViewModels/MessageInfoViewModel.cs create mode 100644 Bar/BarDataModels/Models/IMessageInfoModel.cs create mode 100644 Bar/BarDatabaseImplement/Implements/MessageInfoStorage.cs rename Bar/BarDatabaseImplement/Migrations/{20240517092627_init.Designer.cs => 20240517095734_init.Designer.cs} (85%) rename Bar/BarDatabaseImplement/Migrations/{20240517092627_init.cs => 20240517095734_init.cs} (85%) create mode 100644 Bar/BarDatabaseImplement/Models/MessageInfo.cs create mode 100644 Bar/BarFileImplement/Implements/MessageInfoStorage.cs create mode 100644 Bar/BarFileImplement/Models/MessageInfo.cs create mode 100644 Bar/BarListImplement/Implements/MessageInfoStorage.cs create mode 100644 Bar/BarListImplement/Models/MessageInfo.cs create mode 100644 Bar/BarView/App.config create mode 100644 Bar/BarView/FormMail.Designer.cs create mode 100644 Bar/BarView/FormMail.cs create mode 100644 Bar/BarView/FormMail.resx diff --git a/Bar/BarBusinessLogic/BarBusinessLogic.csproj b/Bar/BarBusinessLogic/BarBusinessLogic.csproj index 978cc54..2a1ed08 100644 --- a/Bar/BarBusinessLogic/BarBusinessLogic.csproj +++ b/Bar/BarBusinessLogic/BarBusinessLogic.csproj @@ -8,6 +8,7 @@ + diff --git a/Bar/BarBusinessLogic/BusinessLogics/ClientLogic.cs b/Bar/BarBusinessLogic/BusinessLogics/ClientLogic.cs index 97dad26..6a50321 100644 --- a/Bar/BarBusinessLogic/BusinessLogics/ClientLogic.cs +++ b/Bar/BarBusinessLogic/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 BarBusinessLogic.BusinessLogics @@ -101,11 +102,11 @@ namespace BarBusinessLogic.BusinessLogics { throw new ArgumentNullException("Нет ФИО пользователя", nameof(model.ClientFIO)); } - if (string.IsNullOrEmpty(model.Email)) + if (string.IsNullOrEmpty(model.Email) || !Regex.IsMatch(model.Email, @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$", RegexOptions.IgnoreCase)) { throw new ArgumentNullException("Нет почты пользователя", nameof(model.Email)); } - if (string.IsNullOrEmpty(model.Password)) + if (string.IsNullOrEmpty(model.Password) || !Regex.IsMatch(model.Password, @"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$")) { throw new ArgumentNullException("Нет пароля пользователя", nameof(model.Password)); } diff --git a/Bar/BarBusinessLogic/BusinessLogics/MessageInfoLogic.cs b/Bar/BarBusinessLogic/BusinessLogics/MessageInfoLogic.cs new file mode 100644 index 0000000..46f5269 --- /dev/null +++ b/Bar/BarBusinessLogic/BusinessLogics/MessageInfoLogic.cs @@ -0,0 +1,99 @@ +using BarContracts.BindingModels; +using BarContracts.BusinessLogicsContracts; +using BarContracts.SearchModels; +using BarContracts.StoragesContracts; +using BarContracts.ViewModels; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BarBusinessLogic.BusinessLogics +{ + public class MessageInfoLogic : IMessageInfoLogic + { + private readonly ILogger _logger; + + private readonly IMessageInfoStorage _messageStorage; + + private readonly IClientStorage _clientStorage; + + public MessageInfoLogic(IMessageInfoStorage messageStorage, ILogger logger, IClientStorage clientStorage) + { + _messageStorage = messageStorage; + _logger = logger; + _clientStorage = clientStorage; + } + public bool Create(MessageInfoBindingModel model) + { + CheckModel(model); + + if (_messageStorage.Insert(model) == null) + { + _logger.LogWarning("Insert operation failed"); + return false; + } + return true; + } + + public List? ReadList(MessageInfoSearchModel? model) + { + _logger.LogInformation("ReadList. ClientID:{ClientID}. ID:{ ID}", model?.ClientId, model?.MessageId); + + var list = model == null ? _messageStorage.GetFullList() : _messageStorage.GetFilteredList(model); + + if (list == null) + { + _logger.LogWarning("ReadList return null list"); + return null; + } + _logger.LogInformation("ReadList. Count:{Count}", list.Count); + return list; + } + + private void CheckModel(MessageInfoBindingModel model, bool withParams = true) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + if (!withParams) + { + return; + } + if (string.IsNullOrEmpty(model.MessageId)) + { + throw new ArgumentNullException("Не указан id сообщения", nameof(model.MessageId)); + } + if (string.IsNullOrEmpty(model.SenderName)) + { + throw new ArgumentNullException("Не указана почта", nameof(model.SenderName)); + } + if (string.IsNullOrEmpty(model.Subject)) + { + throw new ArgumentNullException("Не указана тема", nameof(model.Subject)); + } + if (string.IsNullOrEmpty(model.Body)) + { + throw new ArgumentNullException("Не указан текст сообщения", nameof(model.Subject)); + } + + _logger.LogInformation("MessageInfo. MessageId:{MessageId}.SenderName:{SenderName}.Subject:{Subject}.Body:{Body}", model.MessageId, model.SenderName, model.Subject, model.Body); + var element = _clientStorage.GetElement(new ClientSearchModel + { + Email = model.SenderName + }); + if (element == null) + { + _logger.LogWarning("Не удалось найти клиента, отправившего письмо с адреса Email:{Email}", model.SenderName); + } + else + { + model.ClientId = element.Id; + } + } + + } +} diff --git a/Bar/BarBusinessLogic/BusinessLogics/OrderLogic.cs b/Bar/BarBusinessLogic/BusinessLogics/OrderLogic.cs index bcfc7b2..14e2937 100644 --- a/Bar/BarBusinessLogic/BusinessLogics/OrderLogic.cs +++ b/Bar/BarBusinessLogic/BusinessLogics/OrderLogic.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using BarBusinessLogic.MailWorker; namespace BarBusinessLogic.BusinessLogics { @@ -17,13 +18,17 @@ namespace BarBusinessLogic.BusinessLogics { private readonly ILogger _logger; private readonly IOrderStorage _orderStorage; + private readonly IClientStorage _clientStorage; + private readonly AbstractMailWorker _mailLogic; static readonly object _locker = new object(); - public OrderLogic(ILogger logger, IOrderStorage orderStorage) + public OrderLogic(ILogger logger, IOrderStorage orderStorage, IClientStorage clientStorage, AbstractMailWorker worker) { _logger = logger; _orderStorage = orderStorage; - } + _clientStorage = clientStorage; + _mailLogic = worker; + } public OrderViewModel? ReadElement(OrderSearchModel model) { @@ -61,12 +66,16 @@ namespace BarBusinessLogic.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; } - return true; + var clientView = _clientStorage.GetElement(new() { Id = order!.ClientId }); + + SendEmail(clientView, order); + return true; } public bool ChangeStatus(OrderBindingModel model, OrderStatus status) { @@ -85,9 +94,12 @@ namespace BarBusinessLogic.BusinessLogics if (element.ImplementerId != null) element.ImplementerId = model.ImplementerId; model.Status = status; - if (model.Status == OrderStatus.Готов || model.Status == OrderStatus.Выдан) model.DateImplement = DateTime.Now; - _orderStorage.Update(model); - return true; + if (model.Status == OrderStatus.Выдан || model.Status == OrderStatus.Готов) model.DateImplement = DateTime.Now; + _orderStorage.Update(model); + var clientView = _clientStorage.GetElement(new() { Id = element.ClientId }); + + SendEmail(clientView, element); + return true; } public bool TakeOrderInWork(OrderBindingModel model) @@ -128,5 +140,37 @@ namespace BarBusinessLogic.BusinessLogics } _logger.LogInformation("Order. Sum:{ Cost}. Id: { Id}", model.Sum, model.Id); } - } + public void SendEmail(ClientViewModel clientModel, OrderViewModel orderModel) + { + if (clientModel == null && orderModel == null) + { + return; + } + + MailSendInfoBindingModel mailModel; + + if (orderModel.Status == OrderStatus.Принят) + { + mailModel = new MailSendInfoBindingModel + { + MailAddress = clientModel.Email, + Subject = $"Order №{orderModel.Id}", + Text = $"Your order №{orderModel.Id} by {orderModel.DateCreate} on {orderModel.Sum} was accepted!" + }; + } + else + { + mailModel = new MailSendInfoBindingModel + { + MailAddress = clientModel.Email, + Subject = $"Order №{orderModel.Id}", + Text = $"Order №{orderModel.Id} status was changed to {orderModel.Status}" + }; + } + + + _mailLogic.MailSendAsync(mailModel); + } + } } + diff --git a/Bar/BarBusinessLogic/BusinessLogics/WorkModeling.cs b/Bar/BarBusinessLogic/BusinessLogics/WorkModeling.cs index 7dba40b..ae899a2 100644 --- a/Bar/BarBusinessLogic/BusinessLogics/WorkModeling.cs +++ b/Bar/BarBusinessLogic/BusinessLogics/WorkModeling.cs @@ -120,8 +120,7 @@ namespace BarBusinessLogic.BusinessLogics _logger.LogDebug("DoWork. Worker {Id} finish order {Order}", implementer.Id, runOrder.Id); _orderLogic.FinishOrder(new OrderBindingModel { - Id = runOrder.Id, - ImplementerId = implementer.Id, + Id = runOrder.Id }); // отдыхаем Thread.Sleep(implementer.Qualification * _rnd.Next(10, 100)); diff --git a/Bar/BarBusinessLogic/MailWorker/AbstractMailWorker.cs b/Bar/BarBusinessLogic/MailWorker/AbstractMailWorker.cs new file mode 100644 index 0000000..bd758de --- /dev/null +++ b/Bar/BarBusinessLogic/MailWorker/AbstractMailWorker.cs @@ -0,0 +1,82 @@ +using BarContracts.BindingModels; +using BarContracts.BusinessLogicsContracts; +using DocumentFormat.OpenXml.Spreadsheet; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BarBusinessLogic.MailWorker +{ + public abstract class AbstractMailWorker + { + protected string _mailLogin = string.Empty; + protected string _mailPassword = string.Empty; + protected string _smtpClientHost = string.Empty; + protected int _smtpClientPort; + protected string _popHost = string.Empty; + protected int _popPort; + private readonly IMessageInfoLogic _messageInfoLogic; + private readonly ILogger _logger; + public AbstractMailWorker(ILogger logger, IMessageInfoLogic messageInfoLogic) + { + _logger = logger; + _messageInfoLogic = messageInfoLogic; + _logger.LogWarning("INIT ABSTRACT MAIL WORKER"); + } + public void MailConfig(MailConfigBindingModel config) + { + _mailLogin = config.MailLogin; + _mailPassword = config.MailPassword; + _smtpClientHost = config.SmtpClientHost; + _smtpClientPort = config.SmtpClientPort; + _popHost = config.PopHost; + _popPort = config.PopPort; + _logger.LogDebug("Config: {login}, {password}, {clientHost}, {clientPort}, {popHost}, {popPort} ", _mailLogin, _mailPassword, _smtpClientHost, _smtpClientPort, _popHost, _popPort); + } + public async void MailSendAsync(MailSendInfoBindingModel info) + { + if (string.IsNullOrEmpty(_mailLogin) || string.IsNullOrEmpty(_mailPassword)) + { + return; + } + if (string.IsNullOrEmpty(_smtpClientHost) || _smtpClientPort == 0) + { + return; + } + if (string.IsNullOrEmpty(info.MailAddress) || string.IsNullOrEmpty(info.Subject) || string.IsNullOrEmpty(info.Text)) + { + return; + } + _logger.LogDebug("Send Mail: {To}, {Subject}", info.MailAddress, info.Subject); + await SendMailAsync(info); + } + public async void MailCheck() + { + if (string.IsNullOrEmpty(_mailLogin) || + string.IsNullOrEmpty(_mailPassword)) + { + return; + } + if (string.IsNullOrEmpty(_popHost) || _popPort == 0) + { + return; + } + if (_messageInfoLogic == null) + { + return; + } + var list = await ReceiveMailAsync(); + _logger.LogDebug("Check Mail: {Count} new mails", list.Count); + foreach (var mail in list) + { + _messageInfoLogic.Create(mail); + + } + } + protected abstract Task SendMailAsync(MailSendInfoBindingModel info); + protected abstract Task> ReceiveMailAsync(); + } +} diff --git a/Bar/BarBusinessLogic/MailWorker/MailKitWorker.cs b/Bar/BarBusinessLogic/MailWorker/MailKitWorker.cs new file mode 100644 index 0000000..db8437e --- /dev/null +++ b/Bar/BarBusinessLogic/MailWorker/MailKitWorker.cs @@ -0,0 +1,79 @@ +using BarContracts.BindingModels; +using BarContracts.BusinessLogicsContracts; +using MailKit.Net.Pop3; +using MailKit.Security; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Mail; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace BarBusinessLogic.MailWorker +{ + public class MailKitWorker : AbstractMailWorker + { + public MailKitWorker(ILogger logger, IMessageInfoLogic messageInfoLogic) : base(logger, messageInfoLogic) { } + protected override async Task SendMailAsync(MailSendInfoBindingModel info) + { + using var objMailMessage = new MailMessage(); + using var objSmtpClient = new SmtpClient(_smtpClientHost, _smtpClientPort); + try + { + objMailMessage.From = new MailAddress(_mailLogin); + objMailMessage.To.Add(new MailAddress(info.MailAddress)); + objMailMessage.Subject = info.Subject; + objMailMessage.Body = info.Text; + objMailMessage.SubjectEncoding = Encoding.UTF8; + objMailMessage.BodyEncoding = Encoding.UTF8; + objSmtpClient.UseDefaultCredentials = false; + objSmtpClient.EnableSsl = true; + objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network; + objSmtpClient.Credentials = new NetworkCredential(_mailLogin, _mailPassword); + await Task.Run(() => objSmtpClient.Send(objMailMessage)); + } + catch (Exception ex) + { + throw; + } + } + + protected override async Task> ReceiveMailAsync() + { + var list = new List(); + using var client = new Pop3Client(); + await Task.Run(() => + { + try + { + client.Connect(_popHost, _popPort, SecureSocketOptions.SslOnConnect); + client.Authenticate(_mailLogin, _mailPassword); + for (int i = 0; i < client.Count; i++) + { + var message = client.GetMessage(i); + foreach (var mail in message.From.Mailboxes) + { + list.Add(new MessageInfoBindingModel + { + DateDelivery = message.Date.DateTime, + MessageId = message.MessageId, + SenderName = mail.Address, + Subject = message.Subject, + Body = message.TextBody + }); + } + } + } + catch (AuthenticationException) + { } + finally + { + client.Disconnect(true); + } + }); + return list; + } + } +} diff --git a/Bar/BarClientApp/Controllers/HomeController.cs b/Bar/BarClientApp/Controllers/HomeController.cs index 28524a8..31ed0de 100644 --- a/Bar/BarClientApp/Controllers/HomeController.cs +++ b/Bar/BarClientApp/Controllers/HomeController.cs @@ -144,5 +144,15 @@ namespace BarClientApp.Controllers var piz = APIClient.GetRequest($"api/main/getcocktail?cocktailId={cocktail}"); return count * (piz?.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}")); + } } } diff --git a/Bar/BarClientApp/Views/Home/Create.cshtml b/Bar/BarClientApp/Views/Home/Create.cshtml index 5ad10b0..544df89 100644 --- a/Bar/BarClientApp/Views/Home/Create.cshtml +++ b/Bar/BarClientApp/Views/Home/Create.cshtml @@ -24,7 +24,7 @@
- +