не работает сохранение сообщений

This commit is contained in:
ityurner02@mail.ru 2023-04-29 22:03:56 +04:00
parent 9f8ec53755
commit 139326a4b8
41 changed files with 1525 additions and 14 deletions

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="SmtpClientHost" value="smtp.gmail.com" />
<add key="SmtpClientPort" value="587" />
<add key="PopHost" value="pop.gmail.com" />
<add key="PopPort" value="995" />
<add key="MailLogin" value="labwork7turner@gmail.com" />
<add key="MailPassword" value="daxm gvdc qdza sfza" />
</appSettings>
</configuration>

View File

@ -0,0 +1,62 @@
namespace PlumbingRepair
{
partial class FormMails
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.DataGridView = new System.Windows.Forms.DataGridView();
((System.ComponentModel.ISupportInitialize)(this.DataGridView)).BeginInit();
this.SuspendLayout();
//
// DataGridView
//
this.DataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.DataGridView.Location = new System.Drawing.Point(3, 3);
this.DataGridView.Name = "DataGridView";
this.DataGridView.RowTemplate.Height = 25;
this.DataGridView.Size = new System.Drawing.Size(794, 445);
this.DataGridView.TabIndex = 0;
//
// FormMails
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.DataGridView);
this.Name = "FormMails";
this.Text = "Эл. письма";
this.Load += new System.EventHandler(this.FormMails_Load);
((System.ComponentModel.ISupportInitialize)(this.DataGridView)).EndInit();
this.ResumeLayout(false);
}
#endregion
private DataGridView DataGridView;
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.Extensions.Logging;
using PlumbingRepairContracts.BusinessLogicsContracts;
namespace PlumbingRepair
{
public partial class FormMails : Form
{
private readonly ILogger _logger;
private readonly IMessageInfoLogic _logic;
public FormMails(ILogger<FormMails> logger, IMessageInfoLogic logic)
{
InitializeComponent();
_logger = logger;
_logic = logic;
LoadData();
}
private void LoadData()
{
try
{
var list = _logic.ReadList(null);
if (list != null)
{
DataGridView.DataSource = list;
DataGridView.Columns["ClientId"].Visible = false;
DataGridView.Columns["MessageId"].Visible = false;
DataGridView.Columns["Body"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
_logger.LogInformation("Загрузка писем");
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка загрузки писем");
MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
private void FormMails_Load(object sender, EventArgs e)
{
LoadData();
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -43,6 +43,7 @@
IssuedOrderButton = new Button(); IssuedOrderButton = new Button();
UpdateListButton = new Button(); UpdateListButton = new Button();
DataGridView = new DataGridView(); DataGridView = new DataGridView();
письмаToolStripMenuItem = new ToolStripMenuItem();
MenuStrip.SuspendLayout(); MenuStrip.SuspendLayout();
((System.ComponentModel.ISupportInitialize)DataGridView).BeginInit(); ((System.ComponentModel.ISupportInitialize)DataGridView).BeginInit();
SuspendLayout(); SuspendLayout();
@ -50,7 +51,7 @@
// MenuStrip // MenuStrip
// //
MenuStrip.ImageScalingSize = new Size(20, 20); MenuStrip.ImageScalingSize = new Size(20, 20);
MenuStrip.Items.AddRange(new ToolStripItem[] { СправочникиToolStripMenuItem, отчетыToolStripMenuItem, запускРаботToolStripMenuItem }); MenuStrip.Items.AddRange(new ToolStripItem[] { СправочникиToolStripMenuItem, отчетыToolStripMenuItem, запускРаботToolStripMenuItem, письмаToolStripMenuItem });
MenuStrip.Location = new Point(0, 0); MenuStrip.Location = new Point(0, 0);
MenuStrip.Name = "MenuStrip"; MenuStrip.Name = "MenuStrip";
MenuStrip.Padding = new Padding(7, 3, 0, 3); MenuStrip.Padding = new Padding(7, 3, 0, 3);
@ -172,6 +173,13 @@
DataGridView.Size = new Size(825, 561); DataGridView.Size = new Size(825, 561);
DataGridView.TabIndex = 1; DataGridView.TabIndex = 1;
// //
// письмаToolStripMenuItem
//
письмаToolStripMenuItem.Name = "письмаToolStripMenuItem";
письмаToolStripMenuItem.Size = new Size(77, 24);
письмаToolStripMenuItem.Text = "Письма";
письмаToolStripMenuItem.Click += письмаToolStripMenuItem_Click;
//
// FormMain // FormMain
// //
AutoScaleDimensions = new SizeF(8F, 20F); AutoScaleDimensions = new SizeF(8F, 20F);
@ -211,5 +219,6 @@
private ToolStripMenuItem исполнителиToolStripMenuItem; private ToolStripMenuItem исполнителиToolStripMenuItem;
private ToolStripMenuItem запускРаботToolStripMenuItem; private ToolStripMenuItem запускРаботToolStripMenuItem;
private DataGridView DataGridView; private DataGridView DataGridView;
private ToolStripMenuItem письмаToolStripMenuItem;
} }
} }

View File

@ -173,5 +173,14 @@ namespace PlumbingRepair
form.ShowDialog(); form.ShowDialog();
} }
} }
private void письмаToolStripMenuItem_Click(object sender, EventArgs e)
{
var service = Program.ServiceProvider?.GetService(typeof(FormMails));
if (service is FormMails form)
{
form.ShowDialog();
}
}
} }
} }

View File

@ -28,6 +28,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="App.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="nlog.config"> <None Update="nlog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>

View File

@ -7,6 +7,8 @@ using NLog.Extensions.Logging;
using PlumbingRepairBusinessLogic.BusinessLogic; using PlumbingRepairBusinessLogic.BusinessLogic;
using PlumbingRepairBusinessLogic.OfficePackage; using PlumbingRepairBusinessLogic.OfficePackage;
using PlumbingRepairBusinessLogic.OfficePackage.Implements; using PlumbingRepairBusinessLogic.OfficePackage.Implements;
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairBusinessLogic.MailWorker;
namespace PlumbingRepair namespace PlumbingRepair
{ {
@ -26,6 +28,26 @@ namespace PlumbingRepair
var services = new ServiceCollection(); var services = new ServiceCollection();
ConfigureServices(services); ConfigureServices(services);
_serviceProvider = services.BuildServiceProvider(); _serviceProvider = services.BuildServiceProvider();
try
{
var mailSender = _serviceProvider.GetService<AbstractMailWorker>();
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<ILogger>();
logger?.LogError(ex, "Error");
}
Application.Run(_serviceProvider.GetRequiredService<FormMain>()); Application.Run(_serviceProvider.GetRequiredService<FormMain>());
} }
private static void ConfigureServices(ServiceCollection services) private static void ConfigureServices(ServiceCollection services)
@ -40,13 +62,16 @@ namespace PlumbingRepair
services.AddTransient<IWorkStorage, WorkStorage>(); services.AddTransient<IWorkStorage, WorkStorage>();
services.AddTransient<IClientStorage, ClientStorage>(); services.AddTransient<IClientStorage, ClientStorage>();
services.AddTransient<IImplementerStorage, ImplementerStorage>(); services.AddTransient<IImplementerStorage, ImplementerStorage>();
services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
services.AddTransient<IComponentLogic, ComponentLogic>(); services.AddTransient<IComponentLogic, ComponentLogic>();
services.AddTransient<IOrderLogic, OrderLogic>(); services.AddTransient<IOrderLogic, OrderLogic>();
services.AddTransient<IWorkLogic, WorkLogic>(); services.AddTransient<IWorkLogic, WorkLogic>();
services.AddTransient<IClientLogic, ClientLogic>(); services.AddTransient<IClientLogic, ClientLogic>();
services.AddTransient<IReportLogic, ReportLogic>(); services.AddTransient<IReportLogic, ReportLogic>();
services.AddTransient<IImplementerLogic, ImplementerLogic>(); services.AddTransient<IImplementerLogic, ImplementerLogic>();
services.AddTransient<IMessageInfoLogic, MessageInfoLogic>();
services.AddTransient<IWorkProcess, WorkModeling>(); services.AddTransient<IWorkProcess, WorkModeling>();
services.AddSingleton<AbstractMailWorker, MailKitWorker>();
services.AddTransient<AbstractSaveToExcel, SaveToExcel>(); services.AddTransient<AbstractSaveToExcel, SaveToExcel>();
services.AddTransient<AbstractSaveToWord, SaveToWord>(); services.AddTransient<AbstractSaveToWord, SaveToWord>();
services.AddTransient<AbstractSaveToPdf, SaveToPdf>(); services.AddTransient<AbstractSaveToPdf, SaveToPdf>();
@ -62,6 +87,9 @@ namespace PlumbingRepair
services.AddTransient<FormClients>(); services.AddTransient<FormClients>();
services.AddTransient<FormImplementer>(); services.AddTransient<FormImplementer>();
services.AddTransient<FormImplementers>(); services.AddTransient<FormImplementers>();
} services.AddTransient<FormMails>();
}
private static void MailCheck(object obj) => ServiceProvider?.GetService<AbstractMailWorker>()?.MailCheck();
} }
} }

View File

@ -4,6 +4,7 @@ using PlumbingRepairContracts.BusinessLogicsContracts;
using PlumbingRepairContracts.SearchModels; using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts; using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels; using PlumbingRepairContracts.ViewModels;
using System.Text.RegularExpressions;
namespace PlumbingRepairBusinessLogic.BusinessLogic namespace PlumbingRepairBusinessLogic.BusinessLogic
{ {
@ -96,6 +97,15 @@ namespace PlumbingRepairBusinessLogic.BusinessLogic
if (string.IsNullOrEmpty(model.Password)) if (string.IsNullOrEmpty(model.Password))
{ {
throw new ArgumentNullException("Нет пароля учетной записи клиента", nameof(model.ClientFIO)); throw new ArgumentNullException("Нет пароля учетной записи клиента", nameof(model.ClientFIO));
}
if (!Regex.IsMatch(model.Email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase))
{
throw new ArgumentException("Неправильно введенный email", nameof(model.Email));
}
if (!Regex.IsMatch(model.Password, @"^^((\w+\d+\W+)|(\w+\W+\d+)|(\d+\w+\W+)|(\d+\W+\w+)|(\W+\w+\d+)|(\W+\d+\w+))[\w\d\W]*$", RegexOptions.IgnoreCase))
{
throw new ArgumentException("Неправильно введенный пароль", nameof(model.Password));
} }
_logger.LogInformation("Client. ClientFIO:{ClientFIO}. Email:{Email}. Password:{Password}. Id:{Id}", model.ClientFIO, model.Email, model.Password, model.Id); _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 var element = _clientStorage.GetElement(new ClientSearchModel

View File

@ -0,0 +1,50 @@
using Microsoft.Extensions.Logging;
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.BusinessLogicsContracts;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
namespace PlumbingRepairBusinessLogic.BusinessLogic
{
public class MessageInfoLogic : IMessageInfoLogic
{
private readonly ILogger _logger;
private readonly IMessageInfoStorage _messageStorage;
public MessageInfoLogic(ILogger<MessageInfoLogic> logger, IMessageInfoStorage messageStorage)
{
_logger = logger;
_messageStorage = messageStorage;
}
public bool Create(MessageInfoBindingModel model)
{
if (_messageStorage.Insert(model) == null)
{
_logger.LogWarning("Insert operation failed");
return false;
}
return true;
}
public List<MessageInfoViewModel>? ReadList(MessageInfoSearchModel? model)
{
_logger.LogInformation("ReadList. MessageId:{MessageId}.ClientId:{ClientId} ", model?.MessageId, model?.ClientId);
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;
}
}
}

View File

@ -5,6 +5,7 @@ using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels; using PlumbingRepairContracts.ViewModels;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PlumbingRepairDataModels.Enums; using PlumbingRepairDataModels.Enums;
using PlumbingRepairBusinessLogic.MailWorker;
namespace PlumbingRepairBusinessLogic.BusinessLogic namespace PlumbingRepairBusinessLogic.BusinessLogic
{ {
@ -12,11 +13,15 @@ namespace PlumbingRepairBusinessLogic.BusinessLogic
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IOrderStorage _orderStorage; private readonly IOrderStorage _orderStorage;
private readonly AbstractMailWorker _mailWorker;
private readonly IClientLogic _clientLogic;
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage) public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage, AbstractMailWorker mailWorker, IClientLogic clientLogic)
{ {
_logger = logger; _logger = logger;
_orderStorage = orderStorage; _orderStorage = orderStorage;
_mailWorker = mailWorker;
_clientLogic = clientLogic;
} }
public bool CreateOrder(OrderBindingModel model) public bool CreateOrder(OrderBindingModel model)
@ -30,14 +35,17 @@ namespace PlumbingRepairBusinessLogic.BusinessLogic
} }
model.Status = OrderStatus.Принят; model.Status = OrderStatus.Принят;
var result = _orderStorage.Insert(model);
if (_orderStorage.Insert(model) == null) if (result == null)
{ {
model.Status = OrderStatus.Неизвестен; model.Status = OrderStatus.Неизвестен;
_logger.LogWarning("Insert operation failed"); _logger.LogWarning("Insert operation failed");
return false; return false;
} }
SendOrderMessage(result.ClientId, $"Ремонт сантехники, Заказ №{result.Id}", $"Заказ №{result.Id} от {result.DateCreate} на сумму {result.Sum:0.00} принят");
return true; return true;
} }
public bool StatusUpdate(OrderBindingModel model, OrderStatus newStatus) public bool StatusUpdate(OrderBindingModel model, OrderStatus newStatus)
@ -68,12 +76,34 @@ namespace PlumbingRepairBusinessLogic.BusinessLogic
model.DateImplement = viewModel.DateImplement; model.DateImplement = viewModel.DateImplement;
} }
CheckModel(model); CheckModel(model);
if (_orderStorage.Update(model) == null)
var result = _orderStorage.Update(model);
if (result == null)
{ {
model.Status--; model.Status--;
_logger.LogWarning("Update operation failed"); _logger.LogWarning("Update operation failed");
return false; return false;
} }
SendOrderMessage(result.ClientId, $"Ремонт сантехники, Заказ №{result.Id}", $"Заказ №{model.Id} изменен статус на {result.Status}");
return true;
}
private bool SendOrderMessage(int clientId, string subject, string text)
{
var client = _clientLogic.ReadElement(new() { Id = clientId });
if (client == null)
{
return false;
}
_mailWorker.MailSendAsync(new()
{
MailAddress = client.Email,
Subject = subject,
Text = text
});
return true; return true;
} }

View File

@ -0,0 +1,89 @@
using Microsoft.Extensions.Logging;
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.BusinessLogicsContracts;
namespace PlumbingRepairBusinessLogic.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 IClientLogic _clientLogic;
private readonly ILogger _logger;
public AbstractMailWorker(ILogger<AbstractMailWorker> logger, IMessageInfoLogic messageInfoLogic, IClientLogic clientLogic)
{
_logger = logger;
_messageInfoLogic = messageInfoLogic;
_clientLogic = clientLogic;
}
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)
{
mail.ClientId = _clientLogic.ReadElement(new() { Email = mail.SenderName })?.Id;
_messageInfoLogic.Create(mail);
}
}
protected abstract Task SendMailAsync(MailSendInfoBindingModel info);
protected abstract Task<List<MessageInfoBindingModel>> ReceiveMailAsync();
}
}

View File

@ -0,0 +1,78 @@
using Microsoft.Extensions.Logging;
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.BusinessLogicsContracts;
using System.Net.Mail;
using System.Net;
using System.Text;
using MailKit.Net.Pop3;
using MailKit.Security;
namespace PlumbingRepairBusinessLogic.MailWorker
{
public class MailKitWorker : AbstractMailWorker
{
public MailKitWorker(ILogger<MailKitWorker> logger, IMessageInfoLogic messageInfoLogic, IClientLogic clientLogic) : base(logger, messageInfoLogic, clientLogic) { }
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)
{
throw;
}
}
protected override async Task<List<MessageInfoBindingModel>> ReceiveMailAsync()
{
var list = new List<MessageInfoBindingModel>();
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;
}
}
}

View File

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.19.0" /> <PackageReference Include="DocumentFormat.OpenXml" Version="2.19.0" />
<PackageReference Include="MailKit" Version="4.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" /> <PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
</ItemGroup> </ItemGroup>

View File

@ -132,5 +132,15 @@ namespace PlumbingRepairClientApp.Controllers
var wor = APIClient.GetRequest<WorkViewModel>($"api/main/getwork?workId={work}"); var wor = APIClient.GetRequest<WorkViewModel>($"api/main/getwork?workId={work}");
return count * (wor?.Price ?? 1); return count * (wor?.Price ?? 1);
} }
[HttpGet]
public IActionResult Mails()
{
if (APIClient.Client == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<MessageInfoViewModel>>($"api/client/getmessages?clientId={APIClient.Client.Id}"));
}
} }
} }

View File

@ -0,0 +1,54 @@
@using PlumbingRepairContracts.ViewModels
@model List<MessageInfoViewModel>
@{
ViewData["Title"] = "Mails";
}
<div class="text-center">
<h1 class="display-4">Письма</h1>
</div>
<div class="text-center">
@{
if (Model == null)
{
<h3 class="display-4">Авторизируйтесь</h3>
return;
}
<table class="table">
<thead>
<tr>
<th>
Дата письма
</th>
<th>
Тема
</th>
<th>
Содержание
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.DateDelivery)
</td>
<td>
@Html.DisplayFor(modelItem => item.Subject)
</td>
<td>
@Html.DisplayFor(modelItem => item.Body)
</td>
</tr>
}
</tbody>
</table>
}
</div>

View File

@ -29,6 +29,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Личные данные</a> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Личные данные</a>
</li> </li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Mails">Письма</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Enter">Вход</a> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Enter">Вход</a>
</li> </li>

View File

@ -0,0 +1,12 @@
namespace PlumbingRepairContracts.BindingModels
{
public class MailConfigBindingModel
{
public string MailLogin { get; set; } = string.Empty;
public string MailPassword { get; set; } = string.Empty;
public string SmtpClientHost { get; set; } = string.Empty;
public int SmtpClientPort { get; set; }
public string PopHost { get; set; } = string.Empty;
public int PopPort { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace PlumbingRepairContracts.BindingModels
{
public class MailSendInfoBindingModel
{
public string MailAddress { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,14 @@
using PlumbingRepairDataModels.Models;
namespace PlumbingRepairContracts.BindingModels
{
public class MessageInfoBindingModel : IMessageInfoModel
{
public string MessageId { get; set; } = string.Empty;
public int? ClientId { get; set; }
public string SenderName { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
public string Body { get; set; } = string.Empty;
public DateTime DateDelivery { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.ViewModels;
namespace PlumbingRepairContracts.BusinessLogicsContracts
{
public interface IMessageInfoLogic
{
List<MessageInfoViewModel>? ReadList(MessageInfoSearchModel? model);
bool Create(MessageInfoBindingModel model);
}
}

View File

@ -0,0 +1,8 @@
namespace PlumbingRepairContracts.SearchModels
{
public class MessageInfoSearchModel
{
public int? ClientId { get; set; }
public string? MessageId { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.ViewModels;
namespace PlumbingRepairContracts.StoragesContracts
{
public interface IMessageInfoStorage
{
List<MessageInfoViewModel> GetFullList();
List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model);
MessageInfoViewModel? GetElement(MessageInfoSearchModel model);
MessageInfoViewModel? Insert(MessageInfoBindingModel model);
}
}

View File

@ -0,0 +1,24 @@
using PlumbingRepairDataModels.Models;
using System.ComponentModel;
namespace PlumbingRepairContracts.ViewModels
{
public class MessageInfoViewModel : IMessageInfoModel
{
public string MessageId { get; set; } = string.Empty;
public int? ClientId { get; set; }
[DisplayName("Имя отправителя")]
public string SenderName { get; set; } = string.Empty;
[DisplayName("Дата отправления")]
public DateTime DateDelivery { get; set; }
[DisplayName("Тема")]
public string Subject { get; set; } = string.Empty;
[DisplayName("Содержание")]
public string Body { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,12 @@
namespace PlumbingRepairDataModels.Models
{
public interface IMessageInfoModel
{
string MessageId { get; }
int? ClientId { get; }
string SenderName { get; }
DateTime DateDelivery { get; }
string Subject { get; }
string Body { get; }
}
}

View File

@ -0,0 +1,51 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDatabaseImplement.Models;
namespace PlumbingRepairDatabaseImplement.Implements
{
public class MessageInfoStorage : IMessageInfoStorage
{
public MessageInfoViewModel? GetElement(MessageInfoSearchModel model)
{
if (model.MessageId == null)
return null;
using var context = new PlumbingRepairDataBase();
return context.Messages.FirstOrDefault(x => x.MessageId == model.MessageId)?.GetViewModel;
}
public List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model)
{
if (!model.ClientId.HasValue)
return new();
using var context = new PlumbingRepairDataBase();
return context.Messages
.Where(x => x.ClientId == model.ClientId)
.Select(x => x.GetViewModel)
.ToList();
}
public List<MessageInfoViewModel> GetFullList()
{
using var context = new PlumbingRepairDataBase();
return context.Messages
.Select(x => x.GetViewModel)
.ToList();
}
public MessageInfoViewModel? Insert(MessageInfoBindingModel model)
{
var newMessage = Message.Create(model);
if (newMessage == null)
{
return null;
}
using var context = new PlumbingRepairDataBase();
context.Messages.Add(newMessage);
context.SaveChanges();
return newMessage.GetViewModel;
}
}
}

View File

@ -0,0 +1,302 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using PlumbingRepairDatabaseImplement;
#nullable disable
namespace PlumbingRepairDatabaseImplement.Migrations
{
[DbContext(typeof(PlumbingRepairDataBase))]
[Migration("20230429174158_WithMessage")]
partial class WithMessage
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClientFIO")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Clients");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Component", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ComponentName")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Cost")
.HasColumnType("double precision");
b.HasKey("Id");
b.ToTable("Components");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Implementer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ImplementerFIO")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Qualification")
.HasColumnType("integer");
b.Property<int>("WorkExperience")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("Implementers");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Message", b =>
{
b.Property<string>("MessageId")
.HasColumnType("text");
b.Property<string>("Body")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("ClientId")
.HasColumnType("integer");
b.Property<DateTime>("DateDelivery")
.HasColumnType("timestamp with time zone");
b.Property<string>("SenderName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("text");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.ToTable("Messages");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ClientId")
.HasColumnType("integer");
b.Property<int>("Count")
.HasColumnType("integer");
b.Property<DateTime>("DateCreate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DateImplement")
.HasColumnType("timestamp with time zone");
b.Property<int?>("ImplementerId")
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.Property<double>("Sum")
.HasColumnType("double precision");
b.Property<int>("WorkId")
.HasColumnType("integer");
b.Property<string>("WorkName")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ClientId");
b.HasIndex("ImplementerId");
b.HasIndex("WorkId");
b.ToTable("Orders");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Work", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double>("Price")
.HasColumnType("double precision");
b.Property<string>("WorkName")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Works");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.WorkComponent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ComponentId")
.HasColumnType("integer");
b.Property<int>("Count")
.HasColumnType("integer");
b.Property<int>("WorkId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ComponentId");
b.HasIndex("WorkId");
b.ToTable("WorkComponents");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Message", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Client", "Client")
.WithMany("Messages")
.HasForeignKey("ClientId");
b.Navigation("Client");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Order", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Client", "Client")
.WithMany("Orders")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PlumbingRepairDatabaseImplement.Models.Implementer", "Implementer")
.WithMany("Orders")
.HasForeignKey("ImplementerId");
b.HasOne("PlumbingRepairDatabaseImplement.Models.Work", "Work")
.WithMany("Orders")
.HasForeignKey("WorkId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("Implementer");
b.Navigation("Work");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.WorkComponent", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Component", "Component")
.WithMany("WorkComponents")
.HasForeignKey("ComponentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PlumbingRepairDatabaseImplement.Models.Work", "Work")
.WithMany("Components")
.HasForeignKey("WorkId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Component");
b.Navigation("Work");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Client", b =>
{
b.Navigation("Messages");
b.Navigation("Orders");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Component", b =>
{
b.Navigation("WorkComponents");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Implementer", b =>
{
b.Navigation("Orders");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Work", b =>
{
b.Navigation("Components");
b.Navigation("Orders");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PlumbingRepairDatabaseImplement.Migrations
{
/// <inheritdoc />
public partial class WithMessage : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Messages",
columns: table => new
{
MessageId = table.Column<string>(type: "text", nullable: false),
ClientId = table.Column<int>(type: "integer", nullable: true),
SenderName = table.Column<string>(type: "text", nullable: false),
DateDelivery = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
Subject = table.Column<string>(type: "text", nullable: false),
Body = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Messages", x => x.MessageId);
table.ForeignKey(
name: "FK_Messages_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_Messages_ClientId",
table: "Messages",
column: "ClientId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Messages");
}
}
}

View File

@ -94,6 +94,36 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.ToTable("Implementers"); b.ToTable("Implementers");
}); });
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Message", b =>
{
b.Property<string>("MessageId")
.HasColumnType("text");
b.Property<string>("Body")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("ClientId")
.HasColumnType("integer");
b.Property<DateTime>("DateDelivery")
.HasColumnType("timestamp with time zone");
b.Property<string>("SenderName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("text");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.ToTable("Messages");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Order", b => modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Order", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -187,6 +217,15 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.ToTable("WorkComponents"); b.ToTable("WorkComponents");
}); });
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Message", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Client", "Client")
.WithMany("Messages")
.HasForeignKey("ClientId");
b.Navigation("Client");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Order", b => modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Order", b =>
{ {
b.HasOne("PlumbingRepairDatabaseImplement.Models.Client", "Client") b.HasOne("PlumbingRepairDatabaseImplement.Models.Client", "Client")
@ -233,6 +272,8 @@ namespace PlumbingRepairDatabaseImplement.Migrations
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Client", b => modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Client", b =>
{ {
b.Navigation("Messages");
b.Navigation("Orders"); b.Navigation("Orders");
}); });

View File

@ -17,6 +17,8 @@ namespace PlumbingRepairDatabaseImplement.Models
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
[ForeignKey("ClientId")] [ForeignKey("ClientId")]
public virtual List<Order> Orders { get; set; } = new(); public virtual List<Order> Orders { get; set; } = new();
[ForeignKey("ClientId")]
public virtual List<Message> Messages { get; set; } = new();
public static Client? Create(ClientBindingModel? model) public static Client? Create(ClientBindingModel? model)
{ {
if (model == null) if (model == null)

View File

@ -0,0 +1,51 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using System.ComponentModel.DataAnnotations;
namespace PlumbingRepairDatabaseImplement.Models
{
public class Message : IMessageInfoModel
{
[Key]
public string MessageId { get; private set; } = string.Empty;
public int? ClientId { get; private set; }
[Required]
public string SenderName { get; private set; } = string.Empty;
[Required]
public DateTime DateDelivery { get; private set; }
[Required]
public string Subject { get; private set; } = string.Empty;
[Required]
public string Body { get; private set; } = string.Empty;
public virtual Client? Client { get; set; }
public static Message? Create(MessageInfoBindingModel model)
{
if (model == null)
{
return null;
}
return new()
{
Body = model.Body,
Subject = model.Subject,
ClientId = model.ClientId,
MessageId = model.MessageId,
SenderName = model.SenderName,
DateDelivery = DateTime.SpecifyKind(model.DateDelivery, DateTimeKind.Utc)
};
}
public MessageInfoViewModel GetViewModel => new()
{
Body = Body,
Subject = Subject,
ClientId = ClientId,
MessageId = MessageId,
SenderName = SenderName,
DateDelivery = DateDelivery
};
}
}

View File

@ -19,5 +19,6 @@ namespace PlumbingRepairDatabaseImplement
public virtual DbSet<Order> Orders { set; get; } public virtual DbSet<Order> Orders { set; get; }
public virtual DbSet<Client> Clients { set; get; } public virtual DbSet<Client> Clients { set; get; }
public virtual DbSet<Implementer> Implementers { set; get; } public virtual DbSet<Implementer> Implementers { set; get; }
public virtual DbSet<Message> Messages { set; get; }
} }
} }

View File

@ -10,6 +10,7 @@ namespace PlumbingRepairListImplement
public List<Work> Works { get; set; } public List<Work> Works { get; set; }
public List<Client> Clients { get; set; } public List<Client> Clients { get; set; }
public List<Implementer> Implementers { get; set; } public List<Implementer> Implementers { get; set; }
public List<Message> Messages { get; set; }
private DataListSingleton() private DataListSingleton()
{ {
@ -18,6 +19,7 @@ namespace PlumbingRepairListImplement
Works = new List<Work>(); Works = new List<Work>();
Clients = new List<Client>(); Clients = new List<Client>();
Implementers = new List<Implementer>(); Implementers = new List<Implementer>();
Messages = new List<Message>();
} }
public static DataListSingleton GetInstance() public static DataListSingleton GetInstance()

View File

@ -0,0 +1,68 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairListImplement.Models;
namespace PlumbingRepairListImplement.Implements
{
public class MessageInfoStorage : IMessageInfoStorage
{
private readonly DataListSingleton _source;
public MessageInfoStorage()
{
_source = DataListSingleton.GetInstance();
}
public MessageInfoViewModel? GetElement(MessageInfoSearchModel model)
{
if (model.MessageId == null)
{
return null;
}
foreach (var message in _source.Messages)
{
if (model.MessageId.Equals(message.MessageId))
return message.GetViewModel;
}
return null;
}
public List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model)
{
if (!model.ClientId.HasValue)
{
return new();
}
var result = new List<MessageInfoViewModel>();
foreach (var item in _source.Messages)
{
if (item.ClientId == model.ClientId)
{
result.Add(item.GetViewModel);
}
}
return result;
}
public List<MessageInfoViewModel> GetFullList()
{
var result = new List<MessageInfoViewModel>();
foreach (var item in _source.Messages)
{
result.Add(item.GetViewModel);
}
return result;
}
public MessageInfoViewModel? Insert(MessageInfoBindingModel model)
{
var newMessage = Message.Create(model);
if (newMessage == null)
{
return null;
}
_source.Messages.Add(newMessage);
return newMessage.GetViewModel;
}
}
}

View File

@ -0,0 +1,48 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
namespace PlumbingRepairListImplement.Models
{
public class Message : IMessageInfoModel
{
public string MessageId { get; private set; } = string.Empty;
public int? ClientId { get; private set; }
public string SenderName { get; private set; } = string.Empty;
public DateTime DateDelivery { get; private set; } = DateTime.Now;
public string Subject { get; private set; } = string.Empty;
public string Body { get; private set; } = string.Empty;
public static Message? Create(MessageInfoBindingModel model)
{
if (model == null)
{
return null;
}
return new()
{
Body = model.Body,
Subject = model.Subject,
DateDelivery = model.DateDelivery,
SenderName = model.SenderName,
ClientId = model.ClientId,
MessageId = model.MessageId
};
}
public MessageInfoViewModel GetViewModel => new()
{
Body = Body,
Subject = Subject,
DateDelivery = DateDelivery,
SenderName = SenderName,
ClientId = ClientId,
MessageId = MessageId
};
}
}

View File

@ -12,10 +12,12 @@ namespace PlumbingRepairRestApi.Controllers
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IClientLogic _logic; private readonly IClientLogic _logic;
public ClientController(IClientLogic logic, ILogger<ClientController> logger) private readonly IMessageInfoLogic _mailLogic;
public ClientController(IClientLogic logic, ILogger<ClientController> logger, IMessageInfoLogic mailLogic)
{ {
_logger = logger; _logger = logger;
_logic = logic; _logic = logic;
_mailLogic = mailLogic;
} }
[HttpGet] [HttpGet]
public ClientViewModel? Login(string login, string password) public ClientViewModel? Login(string login, string password)
@ -60,5 +62,22 @@ namespace PlumbingRepairRestApi.Controllers
throw; throw;
} }
} }
[HttpGet]
public List<MessageInfoViewModel>? GetMessages(int clientId)
{
try
{
return _mailLogic.ReadList(new MessageInfoSearchModel
{
ClientId = clientId
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения писем клиента");
throw;
}
}
} }
} }

View File

@ -1,5 +1,7 @@
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using PlumbingRepairBusinessLogic.BusinessLogic; using PlumbingRepairBusinessLogic.BusinessLogic;
using PlumbingRepairBusinessLogic.MailWorker;
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.BusinessLogicsContracts; using PlumbingRepairContracts.BusinessLogicsContracts;
using PlumbingRepairContracts.StoragesContracts; using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairDatabaseImplement.Implements; using PlumbingRepairDatabaseImplement.Implements;
@ -14,11 +16,15 @@ builder.Services.AddTransient<IClientStorage, ClientStorage>();
builder.Services.AddTransient<IOrderStorage, OrderStorage>(); builder.Services.AddTransient<IOrderStorage, OrderStorage>();
builder.Services.AddTransient<IWorkStorage, WorkStorage>(); builder.Services.AddTransient<IWorkStorage, WorkStorage>();
builder.Services.AddTransient<IImplementerStorage, ImplementerStorage>(); builder.Services.AddTransient<IImplementerStorage, ImplementerStorage>();
builder.Services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
builder.Services.AddTransient<IOrderLogic, OrderLogic>(); builder.Services.AddTransient<IOrderLogic, OrderLogic>();
builder.Services.AddTransient<IImplementerLogic, ImplementerLogic>(); builder.Services.AddTransient<IImplementerLogic, ImplementerLogic>();
builder.Services.AddTransient<IClientLogic, ClientLogic>(); builder.Services.AddTransient<IClientLogic, ClientLogic>();
builder.Services.AddTransient<IWorkLogic, WorkLogic>(); builder.Services.AddTransient<IWorkLogic, WorkLogic>();
builder.Services.AddTransient<IMessageInfoLogic, MessageInfoLogic>();
builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>();
builder.Services.AddControllers(); builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
@ -30,6 +36,18 @@ builder.Services.AddSwaggerGen(c =>
var app = builder.Build(); var app = builder.Build();
var mailSender = app.Services.GetService<AbstractMailWorker>();
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. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {

View File

@ -5,5 +5,12 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"SmtpClientHost": "smtp.gmail.com",
"SmtpClientPort": "587",
"PopHost": "pop.gmail.com",
"PopPort": "995",
"MailLogin": "labwork7turner@gmail.com",
"MailPassword": "daxm gvdc qdza sfza"
} }

View File

@ -12,12 +12,14 @@ namespace PlumbingRepairFileImplement
private readonly string WorkFileName = "Work.xml"; private readonly string WorkFileName = "Work.xml";
private readonly string ClientFileName = "Client.xml"; private readonly string ClientFileName = "Client.xml";
private readonly string ImplementerFileName = "Implementer.xml"; private readonly string ImplementerFileName = "Implementer.xml";
private readonly string MessageFileName = "Message.xml";
public List<Component> Components { get; private set; } public List<Component> Components { get; private set; }
public List<Order> Orders { get; private set; } public List<Order> Orders { get; private set; }
public List<Work> Works { get; private set; } public List<Work> Works { get; private set; }
public List<Client> Clients { get; private set; } public List<Client> Clients { get; private set; }
public List<Implementer> Implementers { get; private set; } public List<Implementer> Implementers { get; private set; }
public List<Message> Messages { get; private set; }
public static DataFileSingleton GetInstance() public static DataFileSingleton GetInstance()
{ {
@ -33,6 +35,7 @@ namespace PlumbingRepairFileImplement
public void SaveOrders() => SaveData(Orders, OrderFileName, "Orders", x => x.GetXElement); public void SaveOrders() => SaveData(Orders, OrderFileName, "Orders", x => x.GetXElement);
public void SaveClients() => SaveData(Clients, ClientFileName, "Clients", x => x.GetXElement); public void SaveClients() => SaveData(Clients, ClientFileName, "Clients", x => x.GetXElement);
public void SaveImplementers() => SaveData(Implementers, ImplementerFileName, "Implementers", x => x.GetXElement); public void SaveImplementers() => SaveData(Implementers, ImplementerFileName, "Implementers", x => x.GetXElement);
public void SaveMessages() => SaveData(Messages, MessageFileName, "Messages", x => x.GetXElement);
private DataFileSingleton() private DataFileSingleton()
{ {
@ -41,6 +44,7 @@ namespace PlumbingRepairFileImplement
Orders = LoadData(OrderFileName, "Order", x => Order.Create(x)!)!; Orders = LoadData(OrderFileName, "Order", x => Order.Create(x)!)!;
Clients = LoadData(ClientFileName, "Client", x => Client.Create(x)!)!; Clients = LoadData(ClientFileName, "Client", x => Client.Create(x)!)!;
Implementers = LoadData(ImplementerFileName, "Implementer", x => Implementer.Create(x)!)!; Implementers = LoadData(ImplementerFileName, "Implementer", x => Implementer.Create(x)!)!;
Messages = LoadData(MessageFileName, "Message", x => Message.Create(x)!)!;
} }
private static List<T>? LoadData<T>(string filename, string xmlNodeName, Func<XElement, T> selectFunction) private static List<T>? LoadData<T>(string filename, string xmlNodeName, Func<XElement, T> selectFunction)

View File

@ -0,0 +1,52 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairFileImplement.Models;
namespace PlumbingRepairFileImplement.Implements
{
public class MessageInfoStorage : IMessageInfoStorage
{
private readonly DataFileSingleton source;
public MessageInfoStorage()
{
source = DataFileSingleton.GetInstance();
}
public MessageInfoViewModel? GetElement(MessageInfoSearchModel model)
{
if (model.MessageId == null)
return null;
return source.Messages.FirstOrDefault(x => x.MessageId == model.MessageId)?.GetViewModel;
}
public List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model)
{
if (!model.ClientId.HasValue)
return new();
return source.Messages
.Where(x => x.ClientId == model.ClientId)
.Select(x => x.GetViewModel)
.ToList();
}
public List<MessageInfoViewModel> GetFullList()
{
return source.Messages
.Select(x => x.GetViewModel)
.ToList();
}
public MessageInfoViewModel? Insert(MessageInfoBindingModel model)
{
var newMessage = Message.Create(model);
if (newMessage == null)
{
return null;
}
source.Messages.Add(newMessage);
source.SaveMessages();
return newMessage.GetViewModel;
}
}
}

View File

@ -0,0 +1,75 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using System.Xml.Linq;
namespace PlumbingRepairFileImplement.Models
{
public class Message : IMessageInfoModel
{
public string MessageId { get; private set; } = string.Empty;
public int? ClientId { get; private set; }
public string SenderName { get; private set; } = string.Empty;
public DateTime DateDelivery { get; private set; } = DateTime.Now;
public string Subject { get; private set; } = string.Empty;
public string Body { get; private set; } = string.Empty;
public static Message? Create(MessageInfoBindingModel model)
{
if (model == null)
{
return null;
}
return new()
{
Body = model.Body,
Subject = model.Subject,
DateDelivery = model.DateDelivery,
SenderName = model.SenderName,
ClientId = model.ClientId,
MessageId = model.MessageId
};
}
public static Message? Create(XElement element)
{
if (element == null)
{
return null;
}
return new()
{
Body = element.Attribute("Body")!.Value,
Subject = element.Attribute("Subject")!.Value,
DateDelivery = Convert.ToDateTime(element.Attribute("DateDelivery")!.Value),
SenderName = element.Attribute("SenderName")!.Value,
ClientId = Convert.ToInt32(element.Attribute("ClientId")!.Value),
MessageId = element.Attribute("MessageId")!.Value,
};
}
public MessageInfoViewModel GetViewModel => new()
{
Body = Body,
Subject = Subject,
DateDelivery = DateDelivery,
SenderName = SenderName,
ClientId = ClientId,
MessageId = MessageId
};
public XElement GetXElement => new("MessageInfo",
new XAttribute("Subject", Subject),
new XAttribute("Body", Body),
new XAttribute("ClientId", ClientId.ToString()),
new XAttribute("MessageId", MessageId),
new XAttribute("SenderName", SenderName),
new XAttribute("DateDelivery", DateDelivery.ToString())
);
}
}