ПИбд-23 Захаров Ростислав Андреевич Лабораторная работа №7 усложненная #21

Closed
Zakharov_Rostislav wants to merge 16 commits from lab-7-hard into lab-6-hard
45 changed files with 2157 additions and 16 deletions

View File

@ -13,11 +13,13 @@ namespace BlackcmithWorkshopFileImplement
public List<Shop> Shops { get; private set; }
private readonly string ClientFileName = "Client.xml";
private readonly string ImplementerFileName = "Implementer.xml";
private readonly string MessageInfoFileName = "MessageInfo.xml";
public List<Component> Components { get; private set; }
public List<Order> Orders { get; private set; }
public List<Manufacture> Manufactures { get; private set; }
public List<Client> Clients { get; private set; }
public List<Implementer> Implementers { get; private set; }
public List<MessageInfo> Messages { get; private set; }
public static DataFileSingleton GetInstance()
{
if (instance == null)
@ -38,6 +40,8 @@ namespace BlackcmithWorkshopFileImplement
"Clients", x => x.GetXElement);
public void SaveImplementers() => SaveData(Implementers, ImplementerFileName,
"Implementers", x => x.GetXElement);
public void SaveMessages() => SaveData(Orders, ImplementerFileName,
"Messages", x => x.GetXElement);
private DataFileSingleton()
{
Components = LoadData(ComponentFileName, "Component", x =>
@ -52,6 +56,8 @@ namespace BlackcmithWorkshopFileImplement
Client.Create(x)!)!;
Implementers = LoadData(ImplementerFileName, "Implementer", x =>
Implementer.Create(x)!)!;
Messages = LoadData(MessageInfoFileName, "MessageInfo", x =>
MessageInfo.Create(x)!)!;
}
private static List<T>? LoadData<T>(string filename, string xmlNodeName,
Func<XElement, T> selectFunction)

View File

@ -0,0 +1,72 @@
using BlackcmithWorkshopFileImplement;
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.StoragesContracts;
using BlacksmithWorkshopContracts.ViewModels;
using BlacksmithWorkshopFileImplement.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopFileImplement.Implements
{
public class MessageInfoStorage : IMessageInfoStorage
{
private readonly DataFileSingleton _source;
public MessageInfoStorage()
{
_source = DataFileSingleton.GetInstance();
}
public MessageInfoViewModel? GetElement(MessageInfoSearchModel model)
{
if (model.MessageId != null)
{
return _source.Messages.FirstOrDefault(x => x.MessageId == model.MessageId)?.GetViewModel;
}
return null;
}
public List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model)
{
var res = _source.Messages
.Where(x => !model.ClientId.HasValue || x.ClientId == model.ClientId)
.Select(x => x.GetViewModel);
if (!(model.PageIndex.HasValue && model.PageLength.HasValue))
{
return res.ToList();
}
return res
.Skip((model.PageIndex.Value - 1) * model.PageLength.Value)
.Take(model.PageLength.Value)
.ToList();
}
public List<MessageInfoViewModel> GetFullList()
{
return _source.Messages
.Select(x => x.GetViewModel)
.ToList();
}
public MessageInfoViewModel? Insert(MessageInfoBindingModel model)
{
var newMessage = MessageInfo.Create(model);
if (newMessage == null)
{
return null;
}
_source.Messages.Add(newMessage);
_source.SaveMessages();
return newMessage.GetViewModel;
}
public MessageInfoViewModel? Update(MessageInfoBindingModel model)
{
var res = _source.Messages.FirstOrDefault(x => x.MessageId.Equals(model.MessageId));
if (res != null)
{
res.Update(model);
_source.SaveMessages();
}
return res?.GetViewModel;
}
}
}

View File

@ -0,0 +1,95 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.ViewModels;
using BlacksmithWorkshopDataModels.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace BlacksmithWorkshopFileImplement.Models
{
public class MessageInfo : 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 bool IsReaded { get; private set; }
public bool IsReply { get; private set; }
public string? ReplyMessageId { get; private set; } = string.Empty;
public static MessageInfo? 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 = model.DateDelivery,
IsReply = model.IsReply,
IsReaded = model.IsReaded,
ReplyMessageId = model.ReplyMessageId,
};
}
public static MessageInfo? Create(XElement element)
{
if (element == null)
{
return null;
}
return new()
{
Body = element.Attribute("Body")!.Value,
Subject = element.Attribute("Subject")!.Value,
ClientId = Convert.ToInt32(element.Attribute("ClientId")!.Value),
MessageId = element.Attribute("MessageId")!.Value,
SenderName = element.Attribute("SenderName")!.Value,
DateDelivery = Convert.ToDateTime(element.Attribute("DateDelivery")!.Value),
IsReply = Convert.ToBoolean(element.Attribute("IsReply")!.Value),
IsReaded = Convert.ToBoolean(element.Attribute("HasRead")!.Value),
ReplyMessageId = element.Attribute("ReplyMessageId")!.Value,
};
}
public void Update(MessageInfoBindingModel model)
{
if (model == null)
{
return;
}
IsReply = model.IsReply;
IsReaded = model.IsReaded;
}
public MessageInfoViewModel GetViewModel => new()
{
Body = Body,
Subject = Subject,
ClientId = ClientId,
MessageId = MessageId,
SenderName = SenderName,
DateDelivery = DateDelivery,
IsReply = IsReply,
IsReaded = IsReaded,
ReplyMessageId = ReplyMessageId,
};
public XElement GetXElement => new("MessageInfo",
new XAttribute("Body", Body),
new XAttribute("Subject", Subject),
new XAttribute("ClientId", ClientId ?? 0),
new XAttribute("MessageId", MessageId),
new XAttribute("SenderName", SenderName),
new XAttribute("DateDelivery", DateDelivery),
new XAttribute("IsReply", IsReply),
new XAttribute("IsReaded", IsReaded),
new XAttribute("ReplyMessageId", ReplyMessageId ?? "")
);
}
}

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="forlabspibd23@gmail.com" />
<add key="MailPassword" value="euby honk jvrl rrjj" />
</appSettings>
</configuration>

View File

@ -0,0 +1,189 @@
namespace BlacksmithWorkshop
{
partial class FormLetter
{
/// <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()
{
textBoxEmail = new TextBox();
labelAdress = new Label();
labelSubject = new Label();
textBoxSubject = new TextBox();
labelBody = new Label();
textBoxBody = new TextBox();
buttonClose = new Button();
buttonReply = new Button();
labelDate = new Label();
textBoxDate = new TextBox();
buttonSend = new Button();
SuspendLayout();
//
// textBoxEmail
//
textBoxEmail.Location = new Point(63, 4);
textBoxEmail.Margin = new Padding(3, 2, 3, 2);
textBoxEmail.Name = "textBoxEmail";
textBoxEmail.ReadOnly = true;
textBoxEmail.Size = new Size(163, 23);
textBoxEmail.TabIndex = 0;
//
// labelAdress
//
labelAdress.AutoSize = true;
labelAdress.Location = new Point(10, 7);
labelAdress.Name = "labelAdress";
labelAdress.Size = new Size(43, 15);
labelAdress.TabIndex = 1;
labelAdress.Text = "Адрес:";
//
// labelSubject
//
labelSubject.AutoSize = true;
labelSubject.Location = new Point(10, 41);
labelSubject.Name = "labelSubject";
labelSubject.Size = new Size(37, 15);
labelSubject.TabIndex = 2;
labelSubject.Text = "Тема:";
//
// textBoxSubject
//
textBoxSubject.Location = new Point(63, 39);
textBoxSubject.Margin = new Padding(3, 2, 3, 2);
textBoxSubject.Name = "textBoxSubject";
textBoxSubject.ReadOnly = true;
textBoxSubject.Size = new Size(484, 23);
textBoxSubject.TabIndex = 3;
//
// labelBody
//
labelBody.AutoSize = true;
labelBody.Location = new Point(10, 72);
labelBody.Name = "labelBody";
labelBody.Size = new Size(83, 15);
labelBody.TabIndex = 4;
labelBody.Text = "Текст письма:";
//
// textBoxBody
//
textBoxBody.Location = new Point(10, 89);
textBoxBody.Margin = new Padding(3, 2, 3, 2);
textBoxBody.Multiline = true;
textBoxBody.Name = "textBoxBody";
textBoxBody.ReadOnly = true;
textBoxBody.Size = new Size(536, 140);
textBoxBody.TabIndex = 5;
//
// buttonClose
//
buttonClose.Location = new Point(430, 244);
buttonClose.Margin = new Padding(3, 2, 3, 2);
buttonClose.Name = "buttonClose";
buttonClose.Size = new Size(97, 29);
buttonClose.TabIndex = 6;
buttonClose.Text = "Закрыть";
buttonClose.UseVisualStyleBackColor = true;
buttonClose.Click += buttonClose_Click;
//
// buttonReply
//
buttonReply.Location = new Point(327, 244);
buttonReply.Margin = new Padding(3, 2, 3, 2);
buttonReply.Name = "buttonReply";
buttonReply.Size = new Size(97, 29);
buttonReply.TabIndex = 7;
buttonReply.Text = "Ответить";
buttonReply.UseVisualStyleBackColor = true;
buttonReply.Click += buttonReply_Click;
//
// labelDate
//
labelDate.AutoSize = true;
labelDate.Location = new Point(243, 7);
labelDate.Name = "labelDate";
labelDate.Size = new Size(101, 15);
labelDate.TabIndex = 8;
labelDate.Text = "Дата получения: ";
//
// textBoxDate
//
textBoxDate.Location = new Point(360, 4);
textBoxDate.Margin = new Padding(3, 2, 3, 2);
textBoxDate.Name = "textBoxDate";
textBoxDate.ReadOnly = true;
textBoxDate.Size = new Size(187, 23);
textBoxDate.TabIndex = 9;
//
// buttonSend
//
buttonSend.Location = new Point(224, 244);
buttonSend.Margin = new Padding(3, 2, 3, 2);
buttonSend.Name = "buttonSend";
buttonSend.Size = new Size(97, 29);
buttonSend.TabIndex = 10;
buttonSend.Text = "Отправить";
buttonSend.UseVisualStyleBackColor = true;
buttonSend.Visible = false;
buttonSend.Click += buttonSend_Click;
//
// FormLetter
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(556, 283);
Controls.Add(buttonSend);
Controls.Add(textBoxDate);
Controls.Add(labelDate);
Controls.Add(buttonReply);
Controls.Add(buttonClose);
Controls.Add(textBoxBody);
Controls.Add(labelBody);
Controls.Add(textBoxSubject);
Controls.Add(labelSubject);
Controls.Add(labelAdress);
Controls.Add(textBoxEmail);
Margin = new Padding(3, 2, 3, 2);
Name = "FormLetter";
Text = "Письмо";
Load += FormLetter_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
private TextBox textBoxEmail;
private Label labelAdress;
private Label labelSubject;
private TextBox textBoxSubject;
private Label labelBody;
private TextBox textBoxBody;
private Button buttonClose;
private Button buttonReply;
private Label labelDate;
private TextBox textBoxDate;
private Button buttonSend;
}
}

View File

@ -0,0 +1,141 @@
using BlacksmithWorkshopBusinessLogic.MailWorker;
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.BusinessLogicsContracts;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.ViewModels;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace BlacksmithWorkshop
{
public partial class FormLetter : Form
{
private readonly ILogger _logger;
private readonly IMessageInfoLogic _logic;
private readonly AbstractMailWorker _worker;
public MessageInfoViewModel? model;
public string? messageId;
public FormLetter(ILogger<FormLetter> logger, IMessageInfoLogic logic, AbstractMailWorker worker)
{
InitializeComponent();
_logger = logger;
_logic = logic;
_worker = worker;
}
private void FormLetter_Load(object sender, EventArgs e)
Review

При открытии письмо не помечается прочитанным

При открытии письмо не помечается прочитанным
{
if (!string.IsNullOrEmpty(messageId))
{
ReloadLetter();
return;
}
else if (model != null)
{
ConfigurateToCreateAnsver();
return;
}
_logger.LogError("Для формы не переданно сведений о письме, на которое отвечаем!");
DialogResult = DialogResult.Abort;
Close();
return;
}
private void ReloadLetter()
{
_logger.LogInformation("Загрузка существующего письма с id:{}", messageId);
model = _logic.ReadElement(new MessageInfoSearchModel
{
MessageId = messageId
});
if (model != null)
{
_logger.LogInformation("Письмо найдено");
textBoxEmail.Text = model.SenderName;
textBoxDate.Text = model.DateDelivery.ToString();
textBoxSubject.Text = model.Subject;
textBoxBody.Text = model.Body;
if (model.IsReply)
{
_logger.LogInformation("Письмо само и есть ответ");
buttonReply.Visible = false;
}
else
{
if (!string.IsNullOrEmpty(model.ReplyMessageId))
{
_logger.LogInformation("У письма есть ответ.");
buttonReply.Text = "Прочитать ответ";
}
}
return;
}
_logger.LogWarning("Письмо с таким id не удалось найти");
DialogResult = DialogResult.Abort;
Close();
return;
}
private void ConfigurateToCreateAnsver()
{
textBoxEmail.Text = model.SenderName;
labelDate.Visible = false;
textBoxDate.Visible = false;
textBoxSubject.Text = $"re: {model.Subject}";
textBoxBody.ReadOnly = false;
buttonReply.Visible = false;
buttonSend.Visible = true;
_logger.LogInformation("Запущена форма создания нового письма - ответа");
}
private void buttonClose_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void buttonReply_Click(object sender, EventArgs e)
{
var service = Program.ServiceProvider?.GetService(typeof(FormLetter));
if (service is FormLetter form)
{
if (!string.IsNullOrEmpty(model.ReplyMessageId))
{
form.messageId = model.ReplyMessageId;
}
else
{
form.model = model;
}
if (form.ShowDialog() != DialogResult.Cancel)
{
buttonReply.Visible = false;
}
}
}
private void buttonSend_Click(object sender, EventArgs e)
{
if (model == null)
{
return;
}
string subject = textBoxSubject.Text;
string text = textBoxBody.Text;
Task.Run(() => _worker.MailSendReplyAsync(new MailReplySendInfoBindingModel
{
MailAddress = model.SenderName,
Subject = subject,
Text = text,
ParentMessageId = model.MessageId,
}));
DialogResult = DialogResult.OK;
Close();
}
}
}

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

@ -52,6 +52,7 @@
ShopsListToolStripMenuItem = new ToolStripMenuItem();
ShopsManufacturesListToolStripMenuItem = new ToolStripMenuItem();
ClientsToolStripMenuItem = new ToolStripMenuItem();
MailToolStripMenuItem = new ToolStripMenuItem();
menuStrip.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dataGridView).BeginInit();
SuspendLayout();
@ -70,6 +71,7 @@
GuidesToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { ComponentsToolStripMenuItem, ManufacturesToolStripMenuItem, ClientsToolStripMenuItem, ImplementersToolStripMenuItem, StartWorkingToolStripMenuItem });
GuidesToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { ComponentsToolStripMenuItem, ManufacturesToolStripMenuItem, ShopsToolStripMenuItem, SupplyToolStripMenuItem, SalesToolStripMenuItem });
GuidesToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { ComponentsToolStripMenuItem, ManufacturesToolStripMenuItem, ClientsToolStripMenuItem});
GuidesToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { ComponentsToolStripMenuItem, ManufacturesToolStripMenuItem, ClientsToolStripMenuItem, ImplementersToolStripMenuItem, StartWorkingToolStripMenuItem, MailToolStripMenuItem });
GuidesToolStripMenuItem.Name = "GuidesToolStripMenuItem";
GuidesToolStripMenuItem.Size = new Size(94, 20);
GuidesToolStripMenuItem.Text = "Справочники";
@ -247,6 +249,13 @@
ClientsToolStripMenuItem.Text = "Клиенты";
ClientsToolStripMenuItem.Click += ClientsToolStripMenuItem_Click;
//
// MailToolStripMenuItem
//
MailToolStripMenuItem.Name = "MailToolStripMenuItem";
MailToolStripMenuItem.Size = new Size(181, 22);
MailToolStripMenuItem.Text = "Почта";
MailToolStripMenuItem.Click += MailToolStripMenuItem_Click;
//
// FormMain
//
AutoScaleDimensions = new SizeF(7F, 15F);
@ -296,5 +305,6 @@
private ToolStripMenuItem ClientsToolStripMenuItem;
private ToolStripMenuItem ImplementersToolStripMenuItem;
private ToolStripMenuItem StartWorkingToolStripMenuItem;
private ToolStripMenuItem MailToolStripMenuItem;
}
}

View File

@ -275,5 +275,13 @@ namespace BlacksmithWorkshop
form.ShowDialog();
}
}
private void MailToolStripMenuItem_Click(object sender, EventArgs e)
{
var service = Program.ServiceProvider?.GetService(typeof(ViewMailForm));
if (service is ViewMailForm form)
{
form.ShowDialog();
}
}
}
}

View File

@ -1,4 +1,4 @@
using BlacksmithWorkshopBusinessLogic.BusinessLogics;
using BlacksmithWorkshopBusinessLogic.BusinessLogics;
using BlacksmithWorkshopBusinessLogic.OfficePackage.Implements;
using BlacksmithWorkshopBusinessLogic.OfficePackage;
using BlacksmithWorkshopContracts.BusinessLogicsContracts;
@ -8,6 +8,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using System;
using BlacksmithWorkshopBusinessLogic.MailWorker;
using BlacksmithWorkshopContracts.BindingModels;
namespace BlacksmithWorkshop
{
@ -27,6 +29,29 @@ namespace BlacksmithWorkshop
var services = new ServiceCollection();
ConfigureServices(services);
_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, "Ошибка работы с почтой");
}
Application.Run(_serviceProvider.GetRequiredService<FormMain>());
}
private static void ConfigureServices(ServiceCollection services)
@ -52,6 +77,9 @@ namespace BlacksmithWorkshop
services.AddTransient<IClientStorage, ClientStorage>();
services.AddTransient<IImplementerLogic, ImplementerLogic>();
services.AddTransient<IImplementerStorage, ImplementerStorage>();
services.AddTransient<IMessageInfoLogic, MessageInfoLogic>();
services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
services.AddSingleton<AbstractMailWorker, MailKitWorker>();
services.AddTransient<IWorkProcess, WorkModeling>();
services.AddTransient<FormMain>();
services.AddTransient<FormComponent>();
@ -71,6 +99,10 @@ namespace BlacksmithWorkshop
services.AddTransient<ClientsForm>();
services.AddTransient<ImplementersForm>();
services.AddTransient<ImplementerForm>();
}
services.AddTransient<ViewMailForm>();
services.AddTransient<FormLetter>();
}
private static void MailCheck(object obj) => ServiceProvider?
.GetService<AbstractMailWorker>()?.MailCheck();
}
}

View File

@ -0,0 +1,149 @@
namespace BlacksmithWorkshop
{
partial class ViewMailForm
{
/// <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()
{
panel1 = new Panel();
dataGridView = new DataGridView();
buttonOpen = new Button();
numericUpDownPage = new NumericUpDown();
buttonPreveous = new Button();
buttonNext = new Button();
pageTextBox = new TextBox();
panel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dataGridView).BeginInit();
((System.ComponentModel.ISupportInitialize)numericUpDownPage).BeginInit();
SuspendLayout();
//
// panel1
//
panel1.Controls.Add(dataGridView);
panel1.Location = new Point(3, 1);
panel1.Name = "panel1";
panel1.Size = new Size(696, 323);
panel1.TabIndex = 0;
//
// dataGridView
//
dataGridView.AllowUserToAddRows = false;
dataGridView.AllowUserToDeleteRows = false;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView.Dock = DockStyle.Fill;
dataGridView.Location = new Point(0, 0);
dataGridView.Margin = new Padding(3, 2, 3, 2);
dataGridView.Name = "dataGridView";
dataGridView.ReadOnly = true;
dataGridView.RowHeadersWidth = 51;
dataGridView.RowTemplate.Height = 29;
dataGridView.Size = new Size(696, 323);
dataGridView.TabIndex = 2;
//
// buttonOpen
//
buttonOpen.Location = new Point(705, 235);
buttonOpen.Name = "buttonOpen";
buttonOpen.Size = new Size(106, 23);
buttonOpen.TabIndex = 1;
buttonOpen.Text = "Прочитать";
buttonOpen.UseVisualStyleBackColor = true;
buttonOpen.Click += buttonOpen_Click;
//
// numericUpDownPage
//
numericUpDownPage.Location = new Point(705, 263);
numericUpDownPage.Margin = new Padding(3, 2, 3, 2);
numericUpDownPage.Name = "numericUpDownPage";
numericUpDownPage.Size = new Size(106, 23);
numericUpDownPage.TabIndex = 4;
numericUpDownPage.ValueChanged += numericUpDownPage_ValueChanged;
//
// buttonPreveous
//
buttonPreveous.Location = new Point(705, 289);
buttonPreveous.Margin = new Padding(3, 2, 3, 2);
buttonPreveous.Name = "buttonPreveous";
buttonPreveous.Size = new Size(35, 25);
buttonPreveous.TabIndex = 5;
buttonPreveous.Text = "<-";
buttonPreveous.UseVisualStyleBackColor = true;
buttonPreveous.Click += buttonPreveous_Click;
//
// buttonNext
//
buttonNext.Location = new Point(776, 289);
buttonNext.Margin = new Padding(3, 2, 3, 2);
buttonNext.Name = "buttonNext";
buttonNext.Size = new Size(35, 25);
buttonNext.TabIndex = 6;
buttonNext.Text = "->";
buttonNext.UseVisualStyleBackColor = true;
buttonNext.Click += buttonNext_Click;
//
// pageTextBox
//
pageTextBox.Enabled = false;
pageTextBox.Location = new Point(746, 289);
pageTextBox.Name = "pageTextBox";
pageTextBox.ReadOnly = true;
pageTextBox.Size = new Size(24, 23);
pageTextBox.TabIndex = 7;
pageTextBox.Text = "0";
//
// ViewMailForm
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(817, 321);
Controls.Add(pageTextBox);
Controls.Add(buttonNext);
Controls.Add(buttonPreveous);
Controls.Add(numericUpDownPage);
Controls.Add(buttonOpen);
Controls.Add(panel1);
Margin = new Padding(3, 2, 3, 2);
Name = "ViewMailForm";
Text = "Письма";
Load += ViewMailForm_Load;
panel1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)dataGridView).EndInit();
((System.ComponentModel.ISupportInitialize)numericUpDownPage).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private Panel panel1;
private DataGridView dataGridView;
private Button buttonOpen;
private NumericUpDown numericUpDownPage;
private Button buttonPreveous;
private Button buttonNext;
private TextBox pageTextBox;
}
}

View File

@ -0,0 +1,104 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.BusinessLogicsContracts;
using BlacksmithWorkshopContracts.SearchModels;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BlacksmithWorkshop
{
public partial class ViewMailForm : Form
{
private readonly ILogger _logger;
private readonly IMessageInfoLogic _logic;
private int currentPage = 1;
private int pageLength = 3;
public ViewMailForm(ILogger<ViewMailForm> logger, IMessageInfoLogic logic)
{
InitializeComponent();
_logger = logger;
_logic = logic;
}
private void ViewMailForm_Load(object sender, EventArgs e)
{
LoadData();
numericUpDownPage.Value = pageLength;
pageTextBox.Text = currentPage.ToString();
}
private void LoadData()
{
try
{
var list = _logic.ReadList(new MessageInfoSearchModel()
{
PageLength = pageLength,
PageIndex = currentPage
});
if (list != null)
{
dataGridView.DataSource = list;
dataGridView.Columns["ClientId"].Visible = false;
dataGridView.Columns["MessageId"].Visible = false;
dataGridView.Columns["ReplyMessageId"].Visible = false;
dataGridView.Columns["Reply"].Visible = false;
dataGridView.Columns["IsReply"].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 buttonOpen_Click(object sender, EventArgs e)
{
if (dataGridView.SelectedRows.Count <= 0)
return;
var service = Program.ServiceProvider?.GetService(typeof(FormLetter));
if (service is FormLetter form)
{
string? messageId = dataGridView.SelectedRows[0].Cells["MessageId"].Value.ToString();
if (messageId == null) return;
form.messageId = messageId;
if (!Convert.ToBoolean(dataGridView.SelectedRows[0].Cells["IsReaded"].Value))
{
_logic.Update(new MessageInfoBindingModel
{
MessageId = messageId,
IsReaded = true,
ReplyMessageId = dataGridView.SelectedRows[0].Cells["ReplyMessageId"].Value?.ToString()
});
}
form.ShowDialog();
LoadData();
}
}
private void buttonPreveous_Click(object sender, EventArgs e)
{
currentPage = Math.Max(1, currentPage - 1);
pageTextBox.Text = currentPage.ToString();
LoadData();
}
private void buttonNext_Click(object sender, EventArgs e)
{
currentPage++;
pageTextBox.Text = currentPage.ToString();
LoadData();
}
private void numericUpDownPage_ValueChanged(object sender, EventArgs e)
{
pageLength = Math.Max(1, (int)numericUpDownPage.Value);
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

@ -10,6 +10,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="DocumentFormat.OpenXml" Version="3.0.2" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
<PackageReference Include="MailKit" Version="4.5.0" />
</ItemGroup>
<ItemGroup>

View File

@ -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 BlacksmithWorkshopBusinessLogic.BusinessLogics
@ -106,6 +107,14 @@ namespace BlacksmithWorkshopBusinessLogic.BusinessLogics
throw new ArgumentNullException("Нет пароля клиента",
nameof(model.ClientFIO));
}
if (!Regex.IsMatch(model.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
{
throw new ArgumentException("Некорретно введен email клиента", nameof(model.Email));
}
if (!Regex.IsMatch(model.Password, @"^(?=.*\d)(?=.*\W)(?=.*[^\d\s]).+$"))
{
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);
var element = _clientStorage.GetElement(new ClientSearchModel

View File

@ -0,0 +1,69 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.BusinessLogicsContracts;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.StoragesContracts;
using BlacksmithWorkshopContracts.ViewModels;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopBusinessLogic.BusinessLogics
{
public class MessageInfoLogic : IMessageInfoLogic
{
private readonly ILogger _logger;
private readonly IMessageInfoStorage _messageInfoStorage;
public MessageInfoLogic(ILogger<MessageInfoLogic> logger, IMessageInfoStorage MessageInfoStorage)
{
_logger = logger;
_messageInfoStorage = MessageInfoStorage;
}
public bool Create(MessageInfoBindingModel model)
{
if (_messageInfoStorage.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) ? _messageInfoStorage.GetFullList() : _messageInfoStorage.GetFilteredList(model);
if (list == null)
{
_logger.LogWarning("ReadList return null list");
return null;
}
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
return list;
}
public MessageInfoViewModel? ReadElement(MessageInfoSearchModel model)
{
_logger.LogInformation("ReadElement. MessageId:{MessageId}", model?.MessageId);
if (model == null)
throw new ArgumentNullException(nameof(model));
var element = _messageInfoStorage.GetElement(model);
if (element == null)
{
_logger.LogWarning("ReadElement element not found");
return null;
}
_logger.LogInformation("ReadElement find. Id:{Id}", element.MessageId);
return element;
}
public bool Update(MessageInfoBindingModel model)
{
if (_messageInfoStorage.Update(model) == null)
{
_logger.LogWarning("Update operation failed");
return false;
}
return true;
}
}
}

View File

@ -1,4 +1,5 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopBusinessLogic.MailWorker;
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.BusinessLogicsContracts;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.StoragesContracts;
@ -21,15 +22,21 @@ namespace BlacksmithWorkshopBusinessLogic.BusinessLogics
private readonly IShopLogic _shopLogic;
private readonly IManufactureStorage _manufactureStorage;
private readonly IShopStorage _shopStorage;
private readonly AbstractMailWorker _mailWorker;
private readonly IClientLogic _clientLogic;
static readonly object locker = new object();
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage,
IManufactureStorage manufactureStorage, IShopLogic shopLogic, IShopStorage shopStorage)
AbstractMailWorker mailWorker, IClientLogic clientLogic, IShopLogic shopLogic,
IManufactureStorage manufactureStorage, IShopStorage shopStorage)
{
_orderStorage = orderStorage;
_logger = logger;
_orderStorage = orderStorage;
_manufactureStorage = manufactureStorage;
_shopLogic = shopLogic;
_shopStorage = shopStorage;
_mailWorker = mailWorker;
_clientLogic = clientLogic;
}
public OrderViewModel? ReadElement(OrderSearchModel model)
{
@ -64,12 +71,29 @@ namespace BlacksmithWorkshopBusinessLogic.BusinessLogics
{
CheckModel(model);
if (model.Status != OrderStatus.Неизвестен) return false;
model.Status = OrderStatus.Принят;
if (_orderStorage.Insert(model) == null)
model.Status = OrderStatus.Принят;
var res = _orderStorage.Insert(model);
if (res == null)
{
_logger.LogWarning("Insert operation failed");
return false;
}
SendOrderStatusMail(res.ClientId, $"Изменен статус заказа #{res.Id}", $"Заказ #{res.Id} изменен статус на {model.Status}");
return true;
}
private bool SendOrderStatusMail(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;
}
@ -106,7 +130,13 @@ namespace BlacksmithWorkshopBusinessLogic.BusinessLogics
model.DateImplement = DateTime.Now;
if (element.ImplementerId.HasValue)
model.ImplementerId = element.ImplementerId;
_orderStorage.Update(model);
var result = _orderStorage.Update(model);
if (result == null)
{
_logger.LogWarning("Update operation failed");
return false;
}
SendOrderStatusMail(result.ClientId, $"Изменен статус заказа #{result.Id}", $"Заказ #{result.Id} изменен статус на {result.Status}");
return true;
}
public bool TakeOrderInWork(OrderBindingModel model)

View File

@ -0,0 +1,96 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.BusinessLogicsContracts;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopBusinessLogic.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<AbstractMailWorker> logger, IMessageInfoLogic messageInfoLogic)
{
_logger = logger;
_messageInfoLogic = messageInfoLogic;
}
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.Length, _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);
}
public async void MailSendReplyAsync(MailReplySendInfoBindingModel 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) || string.IsNullOrEmpty(info.ParentMessageId))
return;
_logger.LogDebug("Send Mail as reply: {To}, {Subject}, {parentId}", info.MailAddress, info.Subject, info.ParentMessageId);
string? messageId = await SendMailAsync(info);
if (string.IsNullOrEmpty(messageId))
throw new InvalidOperationException("Непредвиденная ошибка при отправке сообщения в ответ");
if (_messageInfoLogic.Create(new MessageInfoBindingModel
{
MessageId = messageId,
DateDelivery = DateTime.Now,
SenderName = _mailLogin,
IsReply = true,
Subject = info.Subject,
Body = info.Text,
}))
{
_messageInfoLogic.Update(new MessageInfoBindingModel()
{
MessageId = info.ParentMessageId,
ReplyMessageId = messageId,
IsReaded = true
});
}
}
protected abstract Task<string?> SendMailAsync(MailSendInfoBindingModel info);
protected abstract Task<List<MessageInfoBindingModel>> ReceiveMailAsync();
}
}

View File

@ -0,0 +1,96 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.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 BlacksmithWorkshopBusinessLogic.MailWorker
{
public class MailKitWorker : AbstractMailWorker
{
public MailKitWorker(ILogger<MailKitWorker> logger, IMessageInfoLogic messageInfoLogic) : base(logger, messageInfoLogic) { }
protected override async Task<string?> SendMailAsync(MailSendInfoBindingModel info)
{
string? resount = null;
using var objMailMessage = new MailMessage();
using var objSmtpClient = new SmtpClient(_smtpClientHost, _smtpClientPort);
try
{
ConfigurateSmtpClient(objSmtpClient);
CreateMessage(objMailMessage, info);
if (info is MailReplySendInfoBindingModel replyInfo)
{
objMailMessage.Headers.Add("In-Reply-To", replyInfo.ParentMessageId);
objMailMessage.Headers.Add("References", replyInfo.ParentMessageId);
string messageGuid = Guid.NewGuid().ToString();
objMailMessage.Headers.Add("Message-Id", messageGuid);
resount = messageGuid;
}
await Task.Run(() => objSmtpClient.Send(objMailMessage));
}
catch (Exception)
{
throw;
}
return resount;
}
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;
}
private void CreateMessage(MailMessage objMailMessage, MailSendInfoBindingModel info)
{
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;
}
private void ConfigurateSmtpClient(SmtpClient objSmtpClient)
{
objSmtpClient.UseDefaultCredentials = false;
objSmtpClient.EnableSsl = true;
objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
objSmtpClient.Credentials = new NetworkCredential(_mailLogin, _mailPassword);
}
}
}

View File

@ -9,7 +9,8 @@ namespace BlacksmithWorkshopClientApp
{
private static readonly HttpClient _client = new();
public static ClientViewModel? Client { get; set; } = null;
public static void Connect(IConfiguration configuration)
public static int MailPage { get; set; } = 1;
public static void Connect(IConfiguration configuration)
{
_client.BaseAddress = new Uri(configuration["IPAddress"]);
_client.DefaultRequestHeaders.Accept.Clear();

View File

@ -143,5 +143,15 @@ namespace BlacksmithWorkshopClientApp.Controllers
APIClient.GetRequest<ManufactureViewModel>($"api/main/getmanufacture?manufactureid={manufacture}");
return Math.Round(count * (_manufacture?.Price ?? 1), 2);
}
[HttpGet]
public IActionResult Mails(int page = 1)
{
if (APIClient.Client == null)
{
return Redirect("~/Home/Enter");
}
page = Math.Max(page, 1);
return View(APIClient.GetRequest<List<MessageInfoViewModel>>($"api/client/getmessages?clientId={APIClient.Client.Id}&page={page}"));
}
}
}

View File

@ -0,0 +1,67 @@
@using BlacksmithWorkshopContracts.ViewModels
@model List<MessageInfoViewModel>
@Url.ActionContext.RouteData.Values["page"]
@{
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 class="d-flex justify-content-center align-items-center">
@{
int page = int.Parse(Context.Request.Query["page"]);
<div class="m-1">
<input type="number" class="form-control" min="1" step="1" asp-action="Mails" name="page" value="@(page)" readonly>
</div>
if (page > 1)
{
<a name="page" class="btn btn-primary" type="button" asp-action="Mails" asp-route-page="@(page-1)">&lt;-</a>
}
else
{
<p class="btn btn-primary my-auto">&lt;-</p>
}
<a name="" id="" class="btn btn-primary" type="button" asp-action="Mails" asp-route-page="@(page+1)">-&gt;</a>
}
</div>
}
</div>

View File

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

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.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,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.BindingModels
{
public class MailReplySendInfoBindingModel : MailSendInfoBindingModel
{
public string ParentMessageId { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.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,22 @@
using BlacksmithWorkshopDataModels.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.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; }
public bool IsReaded { get; set; }
public string? ReplyMessageId { get; set; }
public bool IsReply { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.BusinessLogicsContracts
{
public interface IMessageInfoLogic
{
List<MessageInfoViewModel>? ReadList(MessageInfoSearchModel? model);
MessageInfoViewModel? ReadElement(MessageInfoSearchModel model);
bool Create(MessageInfoBindingModel model);
bool Update(MessageInfoBindingModel model);
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.SearchModels
{
public class MessageInfoSearchModel
{
public int? ClientId { get; set; }
public string? MessageId { get; set; }
public int? PageLength { get; set; }
public int? PageIndex { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.StoragesContracts
{
public interface IMessageInfoStorage
{
List<MessageInfoViewModel> GetFullList();
List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model);
MessageInfoViewModel? GetElement(MessageInfoSearchModel model);
MessageInfoViewModel? Insert(MessageInfoBindingModel model);
MessageInfoViewModel? Update(MessageInfoBindingModel model);
}
}

View File

@ -0,0 +1,29 @@
using BlacksmithWorkshopDataModels.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopContracts.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;
[DisplayName("Прочитанно")]
public bool IsReaded { get; set; }
public string? ReplyMessageId { get; set; }
public IMessageInfoModel? Reply { get; set; }
public bool IsReply { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopDataModels.Models
{
public interface IMessageInfoModel
{
string MessageId { get; }
int? ClientId { get; }
string SenderName { get; }
DateTime DateDelivery { get; }
string Subject { get; }
string Body { get; }
bool IsReaded { get; }
string? ReplyMessageId { get; }
bool IsReply { get; }
}
}

View File

@ -15,7 +15,7 @@ namespace BlacksmithWorkshopDatabaseImplement
{
if (optionsBuilder.IsConfigured == false)
{
optionsBuilder.UseSqlServer(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=BlacksmithWorkshopDataBaseHard;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True");
optionsBuilder.UseSqlServer(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=BlacksmithWorkshopDataBaseHardv7;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True");
}
base.OnConfiguring(optionsBuilder);
}
@ -27,6 +27,7 @@ namespace BlacksmithWorkshopDatabaseImplement
public virtual DbSet<ShopManufacture> ShopManufactures { set; get; }
public virtual DbSet<Client> Clients { set; get; }
public virtual DbSet<Implementer> Implementers { set; get; }
public virtual DbSet<MessageInfo> Messages { set; get; }
}
}

View File

@ -0,0 +1,84 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.StoragesContracts;
using BlacksmithWorkshopContracts.ViewModels;
using BlacksmithWorkshopDatabaseImplement.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopDatabaseImplement.Implements
{
public class MessageInfoStorage : IMessageInfoStorage
{
public MessageInfoViewModel? GetElement(MessageInfoSearchModel model)
{
using var context = new BlacksmithWorkshopDataBase();
if (!string.IsNullOrEmpty(model.MessageId))
{
return context.Messages.FirstOrDefault(x => x.MessageId == model.MessageId)?.GetViewModel;
}
return null;
}
public List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model)
{
if (model == null || !model.ClientId.HasValue && !model.PageLength.HasValue && !model.PageIndex.HasValue)
{
return new();
}
using var context = new BlacksmithWorkshopDataBase();
if (model.PageLength.HasValue)
{
int skipRows = model.PageIndex.HasValue ? (model.PageIndex.Value - 1) * model.PageLength.Value : 0;
return context.Messages
.Where(x => !x.IsReply)
.Where(x => !model.ClientId.HasValue || x.ClientId.HasValue && x.ClientId == model.ClientId)
.OrderBy(x => x.DateDelivery)
.Skip(skipRows)
.Take(model.PageLength.Value)
.Select(x => x.GetViewModel)
.ToList();
}
return context.Messages
.Where(x => !x.IsReply)
.Where(x => !model.ClientId.HasValue || x.ClientId.HasValue && x.ClientId == model.ClientId)
.OrderBy(x => x.DateDelivery)
.Select(x => x.GetViewModel)
.ToList();
}
public List<MessageInfoViewModel> GetFullList()
{
using var context = new BlacksmithWorkshopDataBase();
return context.Messages
.OrderBy(x => x.DateDelivery)
.Select(x => x.GetViewModel)
.ToList();
}
public MessageInfoViewModel? Insert(MessageInfoBindingModel model)
{
using var context = new BlacksmithWorkshopDataBase();
var newMessage = MessageInfo.Create(context, model);
if (newMessage == null || context.Messages.Any(x => x.MessageId.Equals(model.MessageId)))
{
return null;
}
context.Messages.Add(newMessage);
context.SaveChanges();
return newMessage.GetViewModel;
}
public MessageInfoViewModel? Update(MessageInfoBindingModel model)
{
using var context = new BlacksmithWorkshopDataBase();
var message = context.Messages.FirstOrDefault(x => x.MessageId == model.MessageId);
if (message == null)
{
return null;
}
message.Update(context, model);
context.SaveChanges();
return message.GetViewModel;
}
}
}

View File

@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace BlacksmithWorkshopDatabaseImplement.Migrations
{
[DbContext(typeof(BlacksmithWorkshopDataBase))]
[Migration("20240507165329_InitialCreate")]
[Migration("20240521123604_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
@ -143,6 +143,47 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
b.ToTable("ManufactureComponents");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.MessageInfo", b =>
{
b.Property<string>("MessageId")
.HasColumnType("nvarchar(450)");
b.Property<string>("Body")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int?>("ClientId")
.HasColumnType("int");
b.Property<DateTime>("DateDelivery")
.HasColumnType("datetime2");
b.Property<bool>("IsReaded")
.HasColumnType("bit");
b.Property<bool>("IsReply")
.HasColumnType("bit");
b.Property<string>("ReplyMessageId")
.HasColumnType("nvarchar(450)");
b.Property<string>("SenderName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.HasIndex("ReplyMessageId");
b.ToTable("Messages");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.Order", b =>
{
b.Property<int>("Id")
@ -258,6 +299,21 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
b.Navigation("Manufacture");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.MessageInfo", b =>
{
b.HasOne("BlacksmithWorkshopDatabaseImplement.Models.Client", "Client")
.WithMany("MessageInfos")
.HasForeignKey("ClientId");
b.HasOne("BlacksmithWorkshopDatabaseImplement.Models.MessageInfo", "Reply")
.WithMany()
.HasForeignKey("ReplyMessageId");
b.Navigation("Client");
b.Navigation("Reply");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.Order", b =>
{
b.HasOne("BlacksmithWorkshopDatabaseImplement.Models.Client", "Client")
@ -309,6 +365,8 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.Client", b =>
{
b.Navigation("MessageInfos");
b.Navigation("Orders");
});

View File

@ -86,6 +86,35 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
table.PrimaryKey("PK_Shops", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Messages",
columns: table => new
{
MessageId = table.Column<string>(type: "nvarchar(450)", nullable: false),
ClientId = table.Column<int>(type: "int", nullable: true),
SenderName = table.Column<string>(type: "nvarchar(max)", nullable: false),
DateDelivery = table.Column<DateTime>(type: "datetime2", nullable: false),
Subject = table.Column<string>(type: "nvarchar(max)", nullable: false),
Body = table.Column<string>(type: "nvarchar(max)", nullable: false),
IsReaded = table.Column<bool>(type: "bit", nullable: false),
ReplyMessageId = table.Column<string>(type: "nvarchar(450)", nullable: true),
IsReply = table.Column<bool>(type: "bit", 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");
table.ForeignKey(
name: "FK_Messages_Messages_ReplyMessageId",
column: x => x.ReplyMessageId,
principalTable: "Messages",
principalColumn: "MessageId");
});
migrationBuilder.CreateTable(
name: "ManufactureComponents",
columns: table => new
@ -187,6 +216,16 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
table: "ManufactureComponents",
column: "ManufactureId");
migrationBuilder.CreateIndex(
name: "IX_Messages_ClientId",
table: "Messages",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_Messages_ReplyMessageId",
table: "Messages",
column: "ReplyMessageId");
migrationBuilder.CreateIndex(
name: "IX_Orders_ClientId",
table: "Orders",
@ -219,6 +258,9 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
migrationBuilder.DropTable(
name: "ManufactureComponents");
migrationBuilder.DropTable(
name: "Messages");
migrationBuilder.DropTable(
name: "Orders");

View File

@ -140,6 +140,47 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
b.ToTable("ManufactureComponents");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.MessageInfo", b =>
{
b.Property<string>("MessageId")
.HasColumnType("nvarchar(450)");
b.Property<string>("Body")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int?>("ClientId")
.HasColumnType("int");
b.Property<DateTime>("DateDelivery")
.HasColumnType("datetime2");
b.Property<bool>("IsReaded")
.HasColumnType("bit");
b.Property<bool>("IsReply")
.HasColumnType("bit");
b.Property<string>("ReplyMessageId")
.HasColumnType("nvarchar(450)");
b.Property<string>("SenderName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.HasIndex("ReplyMessageId");
b.ToTable("Messages");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.Order", b =>
{
b.Property<int>("Id")
@ -255,6 +296,21 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
b.Navigation("Manufacture");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.MessageInfo", b =>
{
b.HasOne("BlacksmithWorkshopDatabaseImplement.Models.Client", "Client")
.WithMany("MessageInfos")
.HasForeignKey("ClientId");
b.HasOne("BlacksmithWorkshopDatabaseImplement.Models.MessageInfo", "Reply")
.WithMany()
.HasForeignKey("ReplyMessageId");
b.Navigation("Client");
b.Navigation("Reply");
});
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.Order", b =>
{
b.HasOne("BlacksmithWorkshopDatabaseImplement.Models.Client", "Client")
@ -306,6 +362,8 @@ namespace BlacksmithWorkshopDatabaseImplement.Migrations
modelBuilder.Entity("BlacksmithWorkshopDatabaseImplement.Models.Client", b =>
{
b.Navigation("MessageInfos");
b.Navigation("Orders");
});

View File

@ -22,7 +22,9 @@ namespace BlacksmithWorkshopDatabaseImplement.Models
public string Password { get; set; } = string.Empty;
[ForeignKey("ClientId")]
public virtual List<Order> Orders { get; set; } = new();
public static Client? Create(ClientBindingModel model)
[ForeignKey("ClientId")]
public virtual List<MessageInfo> MessageInfos { get; set; } = new();
public static Client? Create(ClientBindingModel model)
{
if (model == null)
{

View File

@ -0,0 +1,82 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.ViewModels;
using BlacksmithWorkshopDataModels.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopDatabaseImplement.Models
{
public class MessageInfo : 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; } = DateTime.Now;
[Required]
public string Subject { get; private set; } = string.Empty;
[Required]
public string Body { get; private set; } = string.Empty;
public Client? Client { get; private set; }
[Required]
public bool IsReaded { get; set; }
public string? ReplyMessageId { get; set; }
[ForeignKey("ReplyMessageId")]
public virtual MessageInfo? Reply { get; set; }
[Required]
public bool IsReply { get; set; }
public static MessageInfo? Create(BlacksmithWorkshopDataBase context, MessageInfoBindingModel model)
{
if (model == null)
{
return null;
}
return new()
{
Body = model.Body,
Subject = model.Subject,
ClientId = context.Clients.FirstOrDefault(x => x.Email == model.SenderName)?.Id,
Client = context.Clients.FirstOrDefault(x => x.Email == model.SenderName),
MessageId = model.MessageId,
SenderName = model.SenderName,
DateDelivery = model.DateDelivery,
IsReaded = model.IsReaded,
ReplyMessageId = model.ReplyMessageId,
IsReply = model.IsReply
};
}
public void Update(BlacksmithWorkshopDataBase context, MessageInfoBindingModel model)
{
if (model == null)
{
return;
}
IsReaded = model.IsReaded;
ReplyMessageId = model.ReplyMessageId;
if (!string.IsNullOrEmpty(ReplyMessageId))
{
Reply = context.Messages.First(x => x.MessageId == ReplyMessageId);
}
}
public MessageInfoViewModel GetViewModel => new()
{
Body = Body,
Subject = Subject,
ClientId = ClientId,
MessageId = MessageId,
SenderName = SenderName,
DateDelivery = DateDelivery,
IsReaded = IsReaded,
ReplyMessageId = ReplyMessageId,
Reply = Reply,
IsReply = IsReply
};
}
}

View File

@ -16,6 +16,7 @@ namespace BlacksmithWorkshopListImplement
public List<Shop> Shops { get; set; }
public List<Client> Clients { get; set; }
public List<Implementer> Implementers { get; set; }
public List<MessageInfo> Messages { get; set; }
private DataListSingleton()
{
Components = new List<Component>();
@ -24,6 +25,7 @@ namespace BlacksmithWorkshopListImplement
Shops = new List<Shop>();
Clients = new List<Client>();
Implementers = new List<Implementer>();
Messages = new List<MessageInfo>();
}
public static DataListSingleton GetInstance()
{

View File

@ -0,0 +1,87 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.SearchModels;
using BlacksmithWorkshopContracts.StoragesContracts;
using BlacksmithWorkshopContracts.ViewModels;
using BlacksmithWorkshopListImplement.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopListImplement.Implements
{
public class MessageInfoStorage : IMessageInfoStorage
{
private readonly DataListSingleton _source;
public MessageInfoStorage()
{
_source = DataListSingleton.GetInstance();
}
public MessageInfoViewModel? GetElement(MessageInfoSearchModel model)
{
foreach (var message in _source.Messages)
{
if (model.MessageId != null && model.MessageId.Equals(message.MessageId))
return message.GetViewModel;
}
return null;
}
public List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model)
{
List<MessageInfoViewModel> result = new();
foreach (var item in _source.Messages)
{
if (item.ClientId.HasValue && item.ClientId == model.ClientId)
{
result.Add(item.GetViewModel);
}
}
if (!(model.PageIndex.HasValue && model.PageLength.HasValue))
{
return result;
}
if (model.PageIndex * model.PageLength >= result.Count)
{
return new();
}
List<MessageInfoViewModel> filteredResult = new();
for (var i = (model.PageIndex.Value - 1) * model.PageLength.Value; i < model.PageIndex.Value * model.PageLength.Value; i++)
{
filteredResult.Add(result[i]);
}
return filteredResult;
}
public List<MessageInfoViewModel> GetFullList()
{
List<MessageInfoViewModel> result = new();
foreach (var item in _source.Messages)
{
result.Add(item.GetViewModel);
}
return result;
}
public MessageInfoViewModel? Insert(MessageInfoBindingModel model)
{
var newMessage = MessageInfo.Create(model);
if (newMessage == null)
{
return null;
}
_source.Messages.Add(newMessage);
return newMessage.GetViewModel;
}
public MessageInfoViewModel? Update(MessageInfoBindingModel model)
{
foreach (var message in _source.Messages)
{
if (message.MessageId.Equals(model.MessageId))
{
message.Update(model);
return message.GetViewModel;
}
}
return null;
}
}
}

View File

@ -0,0 +1,65 @@
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.ViewModels;
using BlacksmithWorkshopDataModels.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace BlacksmithWorkshopListImplement.Models
{
public class MessageInfo : 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 bool IsReaded { get; private set; }
public bool IsReply { get; private set; }
public string? ReplyMessageId { get; private set; }
public static MessageInfo? 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 = model.DateDelivery,
IsReply = model.IsReply,
IsReaded = model.IsReaded,
ReplyMessageId = model.ReplyMessageId,
};
}
public void Update(MessageInfoBindingModel model)
{
if (model == null)
{
return;
}
IsReply = model.IsReply;
IsReaded = model.IsReaded;
}
public MessageInfoViewModel GetViewModel => new()
{
Body = Body,
Subject = Subject,
ClientId = ClientId,
MessageId = MessageId,
SenderName = SenderName,
DateDelivery = DateDelivery,
IsReply = IsReply,
IsReaded = IsReaded,
ReplyMessageId = ReplyMessageId,
};
}
}

View File

@ -17,11 +17,14 @@ namespace BlacksmithWorkshopRestApi.Controllers
public class ClientController : Controller
{
private readonly ILogger _logger;
private readonly IClientLogic _logic;
public ClientController(IClientLogic logic, ILogger<ClientController> logger)
private readonly IClientLogic _logic;
private readonly IMessageInfoLogic _mailLogic;
public ClientController(IClientLogic logic, IMessageInfoLogic mailLogic,
ILogger<ClientController> logger)
{
_logger = logger;
_logic = logic;
_mailLogic = mailLogic;
}
[HttpGet]
public ClientViewModel? Login(string login, string password)
@ -53,7 +56,25 @@ namespace BlacksmithWorkshopRestApi.Controllers
throw;
}
}
[HttpPost]
[HttpGet]
public List<MessageInfoViewModel>? GetMessages(int clientId, int page, int pagesize = 3)
{
try
{
return _mailLogic.ReadList(new MessageInfoSearchModel
{
ClientId = clientId,
PageLength = pagesize,
PageIndex = page
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения писем клиента");
throw;
}
}
[HttpPost]
public void UpdateData(ClientBindingModel model)
{
try

View File

@ -1,4 +1,6 @@
using BlacksmithWorkshopBusinessLogic.BusinessLogics;
using BlacksmithWorkshopBusinessLogic.MailWorker;
using BlacksmithWorkshopContracts.BindingModels;
using BlacksmithWorkshopContracts.BusinessLogicsContracts;
using BlacksmithWorkshopContracts.StoragesContracts;
using BlacksmithWorkshopDatabaseImplement.Implements;
@ -10,11 +12,16 @@ builder.Logging.AddLog4Net("log4net.config");
builder.Services.AddTransient<IClientStorage, ClientStorage>();
builder.Services.AddTransient<IOrderStorage, OrderStorage>();
builder.Services.AddTransient<IManufactureStorage, ManufactureStorage>();
builder.Services.AddTransient<IImplementerStorage, ImplementerStorage>();
builder.Services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
builder.Services.AddTransient<IShopStorage, ShopStorage>();
builder.Services.AddTransient<IOrderLogic, OrderLogic>();
builder.Services.AddTransient<IClientLogic, ClientLogic>();
builder.Services.AddTransient<IManufactureLogic, ManufactureLogic>();
builder.Services.AddTransient<IShopLogic, ShopLogic>();
builder.Services.AddTransient<IImplementerLogic, ImplementerLogic>();
builder.Services.AddTransient<IMessageInfoLogic, MessageInfoLogic>();
builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at
//https://aka.ms/aspnetcore/swashbuckle
@ -28,7 +35,21 @@ builder.Services.AddSwaggerGen(c =>
= "v1"
});
});
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.
if (app.Environment.IsDevelopment())
{

View File

@ -5,5 +5,11 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"SmtpClientHost": "smtp.gmail.com",
"SmtpClientPort": "587",
"PopHost": "pop.gmail.com",
"PopPort": "995",
"MailLogin": "forlabspibd23@gmail.com",
"MailPassword": "euby honk jvrl rrjj"
}