7лаба

This commit is contained in:
a.puchkina 2024-05-08 08:58:45 +04:00
parent 8baee9f902
commit ee5236b324
45 changed files with 1331 additions and 69 deletions

View File

@ -11,12 +11,14 @@ namespace TravelCompanyFileImplement
private readonly string TravelFileName = "Travel.xml";
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<Travel> Travels { 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)
@ -30,6 +32,7 @@ namespace TravelCompanyFileImplement
public void SaveOrders() => SaveData(Orders, OrderFileName, "Orders", 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 SaveMessages() => SaveData(Orders, ImplementerFileName, "Messages", x => x.GetXElement);
private DataFileSingleton()
{
@ -38,6 +41,7 @@ namespace TravelCompanyFileImplement
Orders = LoadData(OrderFileName, "Order", x => Order.Create(x)!)!;
Clients = LoadData(ClientFileName, "Client", x => 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

@ -42,7 +42,8 @@ namespace TravelCompanyFileImplement.Implements
}
return source.Clients
.FirstOrDefault(x =>
(!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && !string.IsNullOrEmpty(model.Password) && x.Password == model.Password)
(!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && !string.IsNullOrEmpty(model.Password)
| (!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && string.IsNullOrEmpty(model.Password)) && x.Password == model.Password)
|| (model.Id.HasValue && x.Id == model.Id))?.GetViewModel;
}

View File

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

View File

@ -0,0 +1,75 @@
using System.Xml.Linq;
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.ViewModels;
using TravelCompanyDataModels.Models;
namespace TravelCompanyFileImplement.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 static MessageInfo? Create(MessageInfoBindingModel model)
{
if (model == null)
{
return null;
}
return new()
{
MessageId = model.MessageId,
ClientId = model.ClientId,
SenderName = model.SenderName,
DateDelivery = model.DateDelivery,
Subject = model.Subject,
Body = model.Body
};
}
public static MessageInfo? Create(XElement element)
{
if (element == null)
{
return null;
}
return new()
{
MessageId = element.Attribute("MessageId")!.Value,
ClientId = Convert.ToInt32(element.Element("ClientId")!.Value),
SenderName = element.Element("SenderName")!.Value,
DateDelivery = Convert.ToDateTime(element.Element("DateDelivery")!.Value),
Subject = element.Element("Subject")!.Value,
Body = element.Element("Body")!.Value
};
}
public MessageInfoViewModel GetViewModel => new()
{
MessageId = MessageId,
ClientId = ClientId,
SenderName = SenderName,
DateDelivery = DateDelivery,
Subject = Subject,
Body = Body
};
public XElement GetXElement => new("MessageInfo",
new XAttribute("MessageId", MessageId),
new XElement("ClientId", ClientId),
new XElement("SenderName", SenderName),
new XElement("DateDelivery", DateDelivery),
new XElement("Subject", Subject),
new XElement("Body", Body)
);
}
}

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="lab7mail@gmail.com" />
<add key="MailPassword" value="lab7" />
</appSettings>
</configuration>

View File

@ -0,0 +1,66 @@
namespace TravelCompanyView
{
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()
{
dataGridView = new DataGridView();
((System.ComponentModel.ISupportInitialize)dataGridView).BeginInit();
SuspendLayout();
//
// dataGridView
//
dataGridView.AllowUserToAddRows = false;
dataGridView.AllowUserToDeleteRows = false;
dataGridView.BackgroundColor = SystemColors.ButtonHighlight;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView.Dock = DockStyle.Fill;
dataGridView.Location = new Point(0, 0);
dataGridView.Name = "dataGridView";
dataGridView.ReadOnly = true;
dataGridView.RowTemplate.Height = 29;
dataGridView.Size = new Size(718, 402);
dataGridView.TabIndex = 0;
//
// FormMails
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(718, 402);
Controls.Add(dataGridView);
Name = "FormMails";
Text = "Письма";
Load += FormMails_Load;
((System.ComponentModel.ISupportInitialize)dataGridView).EndInit();
ResumeLayout(false);
}
#endregion
private DataGridView dataGridView;
}
}

View File

@ -0,0 +1,43 @@
using Microsoft.Extensions.Logging;
using TravelCompanyContracts.BusinessLogicContracts;
namespace TravelCompanyView
{
public partial class FormMails : Form
{
private readonly ILogger _logger;
private readonly IMessageInfoLogic _logic;
public FormMails(ILogger<FormMails> logger, IMessageInfoLogic logic)
{
_logger = logger;
_logic = logic;
InitializeComponent();
}
private void FormMails_Load(object sender, EventArgs e)
{
LoadData();
}
private void LoadData()
{
try
{
var list = _logic.ReadList(null);
if (list != null)
{
dataGridView.DataSource = list;
dataGridView.Columns["MessageId"].Visible = false;
dataGridView.Columns["ClientId"].Visible = false;
dataGridView.Columns["Body"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
_logger.LogInformation("Messages loading");
}
catch (Exception ex)
{
_logger.LogError(ex, "Messages loading error");
MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}

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

@ -41,6 +41,7 @@
toolStripMenuItem2 = new ToolStripMenuItem();
списокЗаказовToolStripMenuItem = new ToolStripMenuItem();
toolStripLabel1 = new ToolStripLabel();
почтаtoolStripLabel = new ToolStripLabel();
buttonCreateeOrder = new Button();
buttonIssuedOrder = new Button();
buttonRefresh = new Button();
@ -52,23 +53,22 @@
//
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView.Location = new Point(1, 31);
dataGridView.Margin = new Padding(3, 2, 3, 2);
dataGridView.Location = new Point(1, 41);
dataGridView.Name = "dataGridView";
dataGridView.ReadOnly = true;
dataGridView.RowHeadersWidth = 51;
dataGridView.RowTemplate.Height = 29;
dataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView.Size = new Size(1129, 427);
dataGridView.Size = new Size(1290, 569);
dataGridView.TabIndex = 0;
//
// toolStrip1
//
toolStrip1.ImageScalingSize = new Size(20, 20);
toolStrip1.Items.AddRange(new ToolStripItem[] { toolStripDropDownButton1, toolStripDropDownButton2, toolStripLabel1 });
toolStrip1.Items.AddRange(new ToolStripItem[] { toolStripDropDownButton1, toolStripDropDownButton2, toolStripLabel1, почтаtoolStripLabel });
toolStrip1.Location = new Point(0, 0);
toolStrip1.Name = "toolStrip1";
toolStrip1.Size = new Size(1297, 25);
toolStrip1.Size = new Size(1482, 27);
toolStrip1.TabIndex = 1;
toolStrip1.Text = "toolStrip1";
//
@ -79,34 +79,34 @@
toolStripDropDownButton1.Image = (Image)resources.GetObject("toolStripDropDownButton1.Image");
toolStripDropDownButton1.ImageTransparentColor = Color.Magenta;
toolStripDropDownButton1.Name = "toolStripDropDownButton1";
toolStripDropDownButton1.Size = new Size(88, 22);
toolStripDropDownButton1.Size = new Size(107, 24);
toolStripDropDownButton1.Text = "Справочник";
//
// компонентыToolStripMenuItem
//
компонентыToolStripMenuItem.Name = омпонентыToolStripMenuItem";
компонентыToolStripMenuItem.Size = new Size(202, 22);
компонентыToolStripMenuItem.Size = new Size(238, 24);
компонентыToolStripMenuItem.Text = "Компоненты";
компонентыToolStripMenuItem.Click += компонентыToolStripMenuItem_Click;
//
// туристическиеПутевкиToolStripMenuItem
//
туристическиеПутевкиToolStripMenuItem.Name = уристическиеПутевкиToolStripMenuItem";
туристическиеПутевкиToolStripMenuItem.Size = new Size(202, 22);
туристическиеПутевкиToolStripMenuItem.Size = new Size(238, 24);
туристическиеПутевкиToolStripMenuItem.Text = "Туристические путевки";
туристическиеПутевкиToolStripMenuItem.Click += путевкиToolStripMenuItem_Click;
//
// клиентыToolStripMenuItem
//
клиентыToolStripMenuItem.Name = "клиентыToolStripMenuItem";
клиентыToolStripMenuItem.Size = new Size(202, 22);
клиентыToolStripMenuItem.Size = new Size(238, 24);
клиентыToolStripMenuItem.Text = "Клиенты";
клиентыToolStripMenuItem.Click += клиентыToolStripMenuItem_Click;
//
// исполнителиToolStripMenuItem
//
исполнителиToolStripMenuItem.Name = сполнителиToolStripMenuItem";
исполнителиToolStripMenuItem.Size = new Size(202, 22);
исполнителиToolStripMenuItem.Size = new Size(238, 24);
исполнителиToolStripMenuItem.Text = "Исполнители";
исполнителиToolStripMenuItem.Click += исполнителиToolStripMenuItem_Click;
//
@ -117,43 +117,49 @@
toolStripDropDownButton2.Image = (Image)resources.GetObject("toolStripDropDownButton2.Image");
toolStripDropDownButton2.ImageTransparentColor = Color.Magenta;
toolStripDropDownButton2.Name = "toolStripDropDownButton2";
toolStripDropDownButton2.Size = new Size(61, 22);
toolStripDropDownButton2.Size = new Size(72, 24);
toolStripDropDownButton2.Text = "Отчеты";
//
// toolStripMenuItem1
//
toolStripMenuItem1.Name = "toolStripMenuItem1";
toolStripMenuItem1.Size = new Size(216, 22);
toolStripMenuItem1.Size = new Size(258, 24);
toolStripMenuItem1.Text = "Список компонентов";
toolStripMenuItem1.Click += списокКомпонентовToolStripMenuItem_Click;
//
// toolStripMenuItem2
//
toolStripMenuItem2.Name = "toolStripMenuItem2";
toolStripMenuItem2.Size = new Size(216, 22);
toolStripMenuItem2.Size = new Size(258, 24);
toolStripMenuItem2.Text = "Компоненты по путевкам";
toolStripMenuItem2.Click += компонентыПоПутeвкамToolStripMenuItem_Click;
//
// списокЗаказовToolStripMenuItem
//
списокЗаказовToolStripMenuItem.Name = "списокЗаказовToolStripMenuItem";
списокЗаказовToolStripMenuItem.Size = new Size(216, 22);
списокЗаказовToolStripMenuItem.Size = new Size(258, 24);
списокЗаказовToolStripMenuItem.Text = "Список заказов";
списокЗаказовToolStripMenuItem.Click += списокЗаказовToolStripMenuItem_Click;
//
// toolStripLabel1
//
toolStripLabel1.Name = "toolStripLabel1";
toolStripLabel1.Size = new Size(80, 22);
toolStripLabel1.Size = new Size(100, 24);
toolStripLabel1.Text = "Запуск работ";
toolStripLabel1.Click += toolStripLabel1_Click;
//
// почтаtoolStripLabel
//
почтаtoolStripLabel.Name = "почтаtoolStripLabel";
почтаtoolStripLabel.Size = new Size(51, 24);
почтаtoolStripLabel.Text = "Почта";
почтаtoolStripLabel.Click += почтаtoolStripLabel_Click;
//
// buttonCreateeOrder
//
buttonCreateeOrder.Location = new Point(1149, 44);
buttonCreateeOrder.Margin = new Padding(3, 2, 3, 2);
buttonCreateeOrder.Location = new Point(1313, 59);
buttonCreateeOrder.Name = "buttonCreateeOrder";
buttonCreateeOrder.Size = new Size(136, 29);
buttonCreateeOrder.Size = new Size(155, 39);
buttonCreateeOrder.TabIndex = 2;
buttonCreateeOrder.Text = "Создать заказ";
buttonCreateeOrder.UseVisualStyleBackColor = true;
@ -161,10 +167,9 @@
//
// buttonIssuedOrder
//
buttonIssuedOrder.Location = new Point(1149, 93);
buttonIssuedOrder.Margin = new Padding(3, 2, 3, 2);
buttonIssuedOrder.Location = new Point(1313, 124);
buttonIssuedOrder.Name = "buttonIssuedOrder";
buttonIssuedOrder.Size = new Size(136, 29);
buttonIssuedOrder.Size = new Size(155, 39);
buttonIssuedOrder.TabIndex = 5;
buttonIssuedOrder.Text = "Заказ выдан";
buttonIssuedOrder.UseVisualStyleBackColor = true;
@ -172,10 +177,9 @@
//
// buttonRefresh
//
buttonRefresh.Location = new Point(1149, 141);
buttonRefresh.Margin = new Padding(3, 2, 3, 2);
buttonRefresh.Location = new Point(1313, 188);
buttonRefresh.Name = "buttonRefresh";
buttonRefresh.Size = new Size(136, 29);
buttonRefresh.Size = new Size(155, 39);
buttonRefresh.TabIndex = 6;
buttonRefresh.Text = "Обновить список";
buttonRefresh.UseVisualStyleBackColor = true;
@ -183,15 +187,14 @@
//
// FormMain
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1297, 458);
ClientSize = new Size(1482, 611);
Controls.Add(buttonRefresh);
Controls.Add(buttonIssuedOrder);
Controls.Add(buttonCreateeOrder);
Controls.Add(toolStrip1);
Controls.Add(dataGridView);
Margin = new Padding(3, 2, 3, 2);
Name = "FormMain";
Text = "Туристическая фирма";
((System.ComponentModel.ISupportInitialize)dataGridView).EndInit();
@ -218,5 +221,6 @@
private ToolStripMenuItem клиентыToolStripMenuItem;
private ToolStripMenuItem исполнителиToolStripMenuItem;
private ToolStripLabel toolStripLabel1;
private ToolStripLabel почтаtoolStripLabel;
}
}

View File

@ -39,6 +39,7 @@ namespace TravelCompanyView
dataGridView.Columns["TravelName"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView.Columns["ClientId"].Visible = false;
dataGridView.Columns["ClientFIO"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView.Columns["ClientEmail"].Visible = false;
dataGridView.Columns["ImplementerId"].Visible = false;
dataGridView.Columns["ImplementerFIO"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
@ -161,5 +162,14 @@ namespace TravelCompanyView
_workProcess.DoWork((Program.ServiceProvider?.GetService(typeof(IImplementerLogic)) as IImplementerLogic)!, _orderLogic);
MessageBox.Show("Процесс обработки запущен", "Сообщение", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void почтаtoolStripLabel_Click(object sender, EventArgs e)
{
var service = Program.ServiceProvider?.GetService(typeof(FormMails));
if (service is FormMails form)
{
form.ShowDialog();
}
}
}
}

View File

@ -10,6 +10,8 @@ using TravelCompanyBusinessLogic.OfficePackage.Implements;
using TravelCompanyBusinessLogic.OfficePackage;
using TravelCompanyContracts.BusinessLogicContracts;
using TravelCompanyDataBaseImplement.Implements;
using TravelCompanyBusinessLogic.MailWorker;
using TravelCompanyContracts.BindingModels;
namespace TravelCompany
{
@ -25,11 +27,33 @@ namespace TravelCompany
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
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, "Mails Problem");
}
Application.Run(_serviceProvider.GetRequiredService<FormMain>());
}
private static void ConfigureServices(ServiceCollection services)
@ -44,6 +68,7 @@ namespace TravelCompany
services.AddTransient<ITravelStorage, TravelStorage>();
services.AddTransient<IClientStorage, ClientStorage>();
services.AddTransient<IImplementerStorage, ImplementerStorage>();
services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
services.AddTransient<IComponentLogic, ComponentLogic>();
services.AddTransient<IOrderLogic, OrderLogic>();
@ -51,6 +76,10 @@ namespace TravelCompany
services.AddTransient<IReportLogic, ReportLogic>();
services.AddTransient<IClientLogic, ClientLogic>();
services.AddTransient<IImplementerLogic, ImplementerLogic>();
services.AddTransient<IMessageInfoLogic, MessageInfoLogic>();
services.AddSingleton<AbstractMailWorker, MailKitWorker>();
services.AddTransient<IWorkProcess, WorkModeling>();
services.AddTransient<FormMain>();
@ -65,11 +94,13 @@ namespace TravelCompany
services.AddTransient<FormClients>();
services.AddTransient<FormImplementer>();
services.AddTransient<FormImplementers>();
services.AddTransient<FormMails>();
services.AddTransient<AbstractSaveToExcel, SaveToExcel>();
services.AddTransient<AbstractSaveToWord, SaveToWord>();
services.AddTransient<AbstractSaveToPdf, SaveToPdf>();
}
private static void MailCheck(object obj) => ServiceProvider?.GetService<AbstractMailWorker>()?.MailCheck();
}
}

View File

@ -4,6 +4,7 @@ using TravelCompanyContracts.ViewModels;
using TravelCompanyContracts.StoragesContracts;
using TravelCompanyContracts.BusinessLogicContracts;
using TravelCompanyContracts.SearchModels;
using System.Text.RegularExpressions;
namespace TravelCompanyBusinessLogic.BusinessLogic
@ -97,13 +98,13 @@ namespace TravelCompanyBusinessLogic.BusinessLogic
{
throw new ArgumentNullException("Нет ФИО пользователя", nameof(model.ClientFIO));
}
if (string.IsNullOrEmpty(model.Email))
if (string.IsNullOrEmpty(model.Email) || !Regex.IsMatch(model.Email, @"^[a-z0-9._%+-]+\@([a-z0-9-]+\.)+[a-z]{2,4}$"))
{
throw new ArgumentNullException("Нет почты пользователя", nameof(model.Email));
throw new ArgumentNullException("Неправильно введена почта пользователя", nameof(model.Email));
}
if (string.IsNullOrEmpty(model.Password))
if (string.IsNullOrEmpty(model.Password) || !Regex.IsMatch(model.Password, @"^(?=.*[A-Za-z])(?=.*\d)(?=.*[^A-Za-z0-9\n]).{10,50}$"))
{
throw new ArgumentNullException("Нет пароля пользователя", nameof(model.Password));
throw new ArgumentNullException("Неправильно введен пароль пользователя", nameof(model.Password));
}
_logger.LogInformation("Client. ClientFIO:{ClientFIO}.Email:{Email}.Password:{Password}.Id:{Id}",
model.ClientFIO, model.Email, model.Password, model.Id);

View File

@ -0,0 +1,90 @@
using Microsoft.Extensions.Logging;
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.BusinessLogicContracts;
using TravelCompanyContracts.SearchModels;
using TravelCompanyContracts.StoragesContracts;
using TravelCompanyContracts.ViewModels;
namespace TravelCompanyBusinessLogic.BusinessLogic
{
public class MessageInfoLogic : IMessageInfoLogic
{
private readonly ILogger _logger;
private readonly IMessageInfoStorage _messageInfoStorage;
private readonly IClientStorage _clientStorage;
public MessageInfoLogic(ILogger<MessageInfoLogic> logger, IMessageInfoStorage messageInfoStorage, IClientStorage clientStorage)
{
_logger = logger;
_messageInfoStorage = messageInfoStorage;
_clientStorage = clientStorage;
}
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 bool Create(MessageInfoBindingModel model)
{
CheckModel(model);
if (_messageInfoStorage.Insert(model) == null)
{
_logger.LogWarning("Insert operation failed");
return false;
}
return true;
}
private void CheckModel(MessageInfoBindingModel model, bool withParams = true)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
if (!withParams)
{
return;
}
if (string.IsNullOrEmpty(model.MessageId))
{
throw new ArgumentNullException("Не указан id сообщения", nameof(model.MessageId));
}
if (string.IsNullOrEmpty(model.SenderName))
{
throw new ArgumentNullException("Не указана почта отправителя", nameof(model.SenderName));
}
if (string.IsNullOrEmpty(model.Subject))
{
throw new ArgumentNullException("Не указан заголовок", nameof(model.Subject));
}
if (string.IsNullOrEmpty(model.Body))
{
throw new ArgumentNullException("Не указан текст сообщения", nameof(model.Subject));
}
_logger.LogInformation("MessageInfo. MessageId: {MessageId}. SenderName: {SenderName}. DateDelivery: {DateDelivery} Subject: {Subject}. Body: {Body}", model.MessageId, model.SenderName, model.DateDelivery, model.Subject, model.Body);
var element = _clientStorage.GetElement(new ClientSearchModel
{
Email = model.SenderName
});
if (element == null)
{
_logger.LogWarning("Не удалось найти клиента, отправившего письмо с адреса Email: {Email}", model.SenderName);
}
else
{
model.ClientId = element.Id;
}
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using TravelCompanyBusinessLogic.MailWorker;
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.BusinessLogicsContracts;
using TravelCompanyContracts.SearchModels;
@ -16,12 +17,15 @@ namespace TravelCompanyBusinessLogic.BusinessLogic
private readonly ILogger _logger;
private readonly IOrderStorage _orderStorage;
private readonly AbstractMailWorker _mailWorker;
static readonly object locker = new();
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage)
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage, AbstractMailWorker mailWorker)
{
_logger = logger;
_orderStorage = orderStorage;
_mailWorker = mailWorker;
}
public List<OrderViewModel>? ReadList(OrderSearchModel? model)
@ -61,13 +65,19 @@ namespace TravelCompanyBusinessLogic.BusinessLogic
if (model.Status != OrderStatus.Неизвестен)
return false;
model.Status = OrderStatus.Принят;
if (_orderStorage.Insert(model) == null)
var element = _orderStorage.Insert(model);
if (element == null)
{
model.Status = OrderStatus.Неизвестен;
_logger.LogWarning("Insert operation failed");
return false;
}
Task.Run(() => _mailWorker.MailSendAsync(new MailSendInfoBindingModel
{
MailAddress = element.ClientEmail,
Subject = $"Новый заказ создан. Номер заказа - {element.Id}",
Text = $"Заказ №{element.Id} от {element.DateCreate} на сумму {element.Sum} принят."
}));
return true;
}
@ -128,6 +138,13 @@ namespace TravelCompanyBusinessLogic.BusinessLogic
_logger.LogWarning("Changing status operation faled");
return false;
}
string DateInfo = model.DateImplement.HasValue ? $"Дата выполнения {model.DateImplement}" : "";
Task.Run(() => _mailWorker.MailSendAsync(new MailSendInfoBindingModel
{
MailAddress = element.ClientEmail,
Subject = $"Заказ №{element.Id}",
Text = $"Заказ №{element.Id} изменен статус на {model.Status}. {DateInfo}"
}));
return true;
}

View File

@ -0,0 +1,92 @@
using Microsoft.Extensions.Logging;
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.BusinessLogicContracts;
namespace TravelCompanyBusinessLogic.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, _smtpClientHost, _smtpClientPort, _popHost, _popPort);
}
public async void MailSendAsync(MailSendInfoBindingModel info)
{
if (string.IsNullOrEmpty(_mailLogin) || string.IsNullOrEmpty(_mailPassword))
{
return;
}
if (string.IsNullOrEmpty(_smtpClientHost) || _smtpClientPort == 0)
{
return;
}
if (string.IsNullOrEmpty(info.MailAddress) || string.IsNullOrEmpty(info.Subject) || string.IsNullOrEmpty(info.Text))
{
return;
}
_logger.LogDebug("Send Mail: {To}, {Subject}", info.MailAddress, info.Subject);
await SendMailAsync(info);
}
public async void MailCheck()
{
if (string.IsNullOrEmpty(_mailLogin) || string.IsNullOrEmpty(_mailPassword))
{
return;
}
if (string.IsNullOrEmpty(_popHost) || _popPort == 0)
{
return;
}
if (_messageInfoLogic == null)
{
return;
}
var list = await ReceiveMailAsync();
_logger.LogDebug("Check Mail: {Count} new mails", list.Count);
foreach (var mail in list)
{
_messageInfoLogic.Create(mail);
}
}
protected abstract Task SendMailAsync(MailSendInfoBindingModel info);
protected abstract Task<List<MessageInfoBindingModel>> ReceiveMailAsync();
}
}

View File

@ -0,0 +1,78 @@
using MailKit.Net.Pop3;
using MailKit.Security;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Net.Mail;
using System.Text;
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.BusinessLogicContracts;
namespace TravelCompanyBusinessLogic.MailWorker
{
public class MailKitWorker : AbstractMailWorker
{
public MailKitWorker(ILogger<MailKitWorker> logger, IMessageInfoLogic messageInfoLogic) : base(logger, messageInfoLogic) { }
protected override async Task SendMailAsync(MailSendInfoBindingModel info)
{
using var objMailMessage = new MailMessage();
using var objSmtpClient = new SmtpClient(_smtpClientHost, _smtpClientPort);
try
{
objMailMessage.From = new MailAddress(_mailLogin);
objMailMessage.To.Add(new MailAddress(info.MailAddress));
objMailMessage.Subject = info.Subject;
objMailMessage.Body = info.Text;
objMailMessage.SubjectEncoding = Encoding.UTF8;
objMailMessage.BodyEncoding = Encoding.UTF8;
objSmtpClient.UseDefaultCredentials = false;
objSmtpClient.EnableSsl = true;
objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
objSmtpClient.Credentials = new NetworkCredential(_mailLogin, _mailPassword);
await Task.Run(() => objSmtpClient.Send(objMailMessage));
}
catch (Exception)
{
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>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.0.2" />
<PackageReference Include="MailKit" Version="4.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />

View File

@ -144,5 +144,15 @@ namespace TravelCompanyClientApp.Controllers
var tr = APIClient.GetRequest<TravelViewModel>($"api/main/gettravel?travelId={travel}");
return count * (tr?.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 TravelCompanyContracts.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

@ -22,16 +22,19 @@
<div class="navbar-collapse collapse d-sm-inline-flex flex-smrow-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Index">Заказы</a>
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Заказы</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Privacy">Личные данные</a>
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Личные данные</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Enter">Вход</a>
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Mails">Письма</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Register">Регистрация</a>
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Enter">Вход</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Register">Регистрация</a>
</li>
</ul>
</div>

View File

@ -0,0 +1,12 @@
namespace TravelCompanyContracts.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 TravelCompanyContracts.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 TravelCompanyDataModels.Models;
namespace TravelCompanyContracts.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,12 @@
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.SearchModels;
using TravelCompanyContracts.ViewModels;
namespace TravelCompanyContracts.BusinessLogicContracts
{
public interface IMessageInfoLogic
{
List<MessageInfoViewModel>? ReadList(MessageInfoSearchModel? model);
bool Create(MessageInfoBindingModel model);
}
}

View File

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

View File

@ -0,0 +1,14 @@
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.SearchModels;
using TravelCompanyContracts.ViewModels;
namespace TravelCompanyContracts.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,23 @@
using System.ComponentModel;
using TravelCompanyDataModels.Models;
namespace TravelCompanyContracts.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

@ -19,6 +19,7 @@ namespace TravelCompanyContracts.ViewModels
[DisplayName("ФИО клиента")]
public string ClientFIO { get; set; } = string.Empty;
public string ClientEmail { get; set; } = string.Empty;
public int? ImplementerId { get; set; }
[DisplayName("ФИО исполнителя")]

View File

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

View File

@ -38,6 +38,7 @@ namespace TravelCompanyDatabaseImplement.Implements
using var context = new TravelCompanyDataBase();
return context.Clients.FirstOrDefault(x =>
(!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && !string.IsNullOrEmpty(model.Password) && x.Password == model.Password)
|| (!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && string.IsNullOrEmpty(model.Password))
|| (model.Id.HasValue && x.Id == model.Id))?.GetViewModel;
}

View File

@ -0,0 +1,58 @@
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.SearchModels;
using TravelCompanyContracts.StoragesContracts;
using TravelCompanyContracts.ViewModels;
using TravelCompanyDatabaseImplement.Models;
namespace TravelCompanyDatabaseImplement.Implements
{
public class MessageInfoStorage : IMessageInfoStorage
{
public List<MessageInfoViewModel> GetFullList()
{
using var context = new TravelCompanyDataBase();
return context.MessageInfos
.Select(x => x.GetViewModel)
.ToList();
}
public List<MessageInfoViewModel> GetFilteredList(MessageInfoSearchModel model)
{
if (!model.ClientId.HasValue)
{
return new();
}
using var context = new TravelCompanyDataBase();
return context.MessageInfos
.Where(x => x.ClientId.HasValue && x.ClientId == model.ClientId)
.Select(x => x.GetViewModel)
.ToList();
}
public MessageInfoViewModel? GetElement(MessageInfoSearchModel model)
{
if (string.IsNullOrEmpty(model.MessageId))
{
return new();
}
using var context = new TravelCompanyDataBase();
return context.MessageInfos
.FirstOrDefault(x => model.MessageId.Equals(x.MessageId))
?.GetViewModel;
}
public MessageInfoViewModel? Insert(MessageInfoBindingModel model)
{
var newMessage = MessageInfo.Create(model);
if (newMessage == null)
{
return null;
}
using var context = new TravelCompanyDataBase();
context.MessageInfos.Add(newMessage);
context.SaveChanges();
return newMessage.GetViewModel;
}
}
}

View File

@ -12,7 +12,7 @@ using TravelCompanyDatabaseImplement;
namespace TravelCompanyDatabaseImplement.Migrations
{
[DbContext(typeof(TravelCompanyDataBase))]
[Migration("20240505221825_InitialCreate")]
[Migration("20240507172413_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
@ -97,6 +97,36 @@ namespace TravelCompanyDatabaseImplement.Migrations
b.ToTable("Implementers");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.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<string>("SenderName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.ToTable("MessageInfos");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.Order", b =>
{
b.Property<int>("Id")
@ -186,6 +216,15 @@ namespace TravelCompanyDatabaseImplement.Migrations
b.ToTable("TravelComponents");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.MessageInfo", b =>
{
b.HasOne("TravelCompanyDatabaseImplement.Models.Client", "Client")
.WithMany("ClientMessages")
.HasForeignKey("ClientId");
b.Navigation("Client");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.Order", b =>
{
b.HasOne("TravelCompanyDatabaseImplement.Models.Client", "Client")
@ -232,6 +271,8 @@ namespace TravelCompanyDatabaseImplement.Migrations
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.Client", b =>
{
b.Navigation("ClientMessages");
b.Navigation("Orders");
});

View File

@ -70,6 +70,27 @@ namespace TravelCompanyDatabaseImplement.Migrations
table.PrimaryKey("PK_Travels", x => x.Id);
});
migrationBuilder.CreateTable(
name: "MessageInfos",
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)
},
constraints: table =>
{
table.PrimaryKey("PK_MessageInfos", x => x.MessageId);
table.ForeignKey(
name: "FK_MessageInfos_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
@ -134,6 +155,11 @@ namespace TravelCompanyDatabaseImplement.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_MessageInfos_ClientId",
table: "MessageInfos",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_Orders_ClientId",
table: "Orders",
@ -163,6 +189,9 @@ namespace TravelCompanyDatabaseImplement.Migrations
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MessageInfos");
migrationBuilder.DropTable(
name: "Orders");

View File

@ -94,6 +94,36 @@ namespace TravelCompanyDatabaseImplement.Migrations
b.ToTable("Implementers");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.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<string>("SenderName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.ToTable("MessageInfos");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.Order", b =>
{
b.Property<int>("Id")
@ -183,6 +213,15 @@ namespace TravelCompanyDatabaseImplement.Migrations
b.ToTable("TravelComponents");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.MessageInfo", b =>
{
b.HasOne("TravelCompanyDatabaseImplement.Models.Client", "Client")
.WithMany("ClientMessages")
.HasForeignKey("ClientId");
b.Navigation("Client");
});
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.Order", b =>
{
b.HasOne("TravelCompanyDatabaseImplement.Models.Client", "Client")
@ -229,6 +268,8 @@ namespace TravelCompanyDatabaseImplement.Migrations
modelBuilder.Entity("TravelCompanyDatabaseImplement.Models.Client", b =>
{
b.Navigation("ClientMessages");
b.Navigation("Orders");
});

View File

@ -21,6 +21,8 @@ namespace TravelCompanyDatabaseImplement.Models
[ForeignKey("ClientId")]
public virtual List<Order> Orders { get; set; } = new();
[ForeignKey("ClientId")]
public virtual List<MessageInfo> ClientMessages { get; set; } = new();
public static Client? Create(ClientBindingModel model)
{

View File

@ -0,0 +1,56 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.ViewModels;
using TravelCompanyDataModels.Models;
namespace TravelCompanyDatabaseImplement.Models
{
public class MessageInfo : IMessageInfoModel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string MessageId { get; set; } = string.Empty;
public int? ClientId { get; set; }
[Required]
public string SenderName { get; set; } = string.Empty;
[Required]
public DateTime DateDelivery { get; set; }
[Required]
public string Subject { get; set; } = string.Empty;
[Required]
public string Body { get; set; } = string.Empty;
public virtual Client? Client { get; set; }
public static MessageInfo? Create(MessageInfoBindingModel? model)
{
if (model == null)
{
return null;
}
return new()
{
MessageId = model.MessageId,
ClientId = model.ClientId,
SenderName = model.SenderName,
DateDelivery = model.DateDelivery,
Subject = model.Subject,
Body = model.Body
};
}
public MessageInfoViewModel GetViewModel => new()
{
MessageId = MessageId,
ClientId = ClientId,
SenderName = SenderName,
DateDelivery = DateDelivery,
Subject = Subject,
Body = Body
};
}
}

View File

@ -75,6 +75,7 @@ namespace TravelCompanyDatabaseImplement.Models
Id = Id,
TravelName = Travel.TravelName ?? string.Empty,
ClientFIO = Client.ClientFIO ?? string.Empty,
ClientEmail = Client.Email ?? string.Empty,
ImplementerFIO = Implementer?.ImplementerFIO ?? string.Empty,
};

View File

@ -9,7 +9,7 @@ namespace TravelCompanyDatabaseImplement
{
if (optionsBuilder.IsConfigured == false)
{
optionsBuilder.UseSqlServer(@"Data Source=PC-Anna\SQLEXPRESS;Initial Catalog=TravelCompanyDatabase6laba;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True");
optionsBuilder.UseSqlServer(@"Data Source=PC-Anna\SQLEXPRESS;Initial Catalog=TravelCompanyDatabase7laba;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True");
}
base.OnConfiguring(optionsBuilder);
}
@ -19,5 +19,6 @@ namespace TravelCompanyDatabaseImplement
public virtual DbSet<Order> Orders { set; get; }
public virtual DbSet<Client> Clients { set; get; }
public virtual DbSet<Implementer> Implementers { set; get; }
public virtual DbSet<MessageInfo> MessageInfos { set; get; }
}
}

View File

@ -1,9 +1,4 @@
using TravelCompanyListImplement.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TravelCompanyListImplement
{
@ -14,8 +9,8 @@ namespace TravelCompanyListImplement
public List<Order> Orders { get; set; }
public List<Travel> Travels { 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>();
@ -23,6 +18,7 @@ namespace TravelCompanyListImplement
Travels = new List<Travel>();
Clients = new List<Client>();
Implementers = new List<Implementer>();
Messages = new List<MessageInfo>();
}
public static DataListSingleton GetInstance()
{

View File

@ -52,6 +52,7 @@ namespace TravelCompanyListImplement.Implements
{
if ((!string.IsNullOrEmpty(model.Email) && client.Email == model.Email
&& !string.IsNullOrEmpty(model.Password) && client.Password == model.Password)
|| (!string.IsNullOrEmpty(model.Email) && client.Email == model.Email && string.IsNullOrEmpty(model.Password))
|| (model.Id.HasValue && client.Id == model.Id))
{
return client.GetViewModel;

View File

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

View File

@ -0,0 +1,48 @@
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.ViewModels;
using TravelCompanyDataModels.Models;
namespace TravelCompanyListImplement.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 static MessageInfo? Create(MessageInfoBindingModel model)
{
if (model == null)
{
return null;
}
return new()
{
MessageId = model.MessageId,
ClientId = model.ClientId,
SenderName = model.SenderName,
DateDelivery = model.DateDelivery,
Subject = model.Subject,
Body = model.Body
};
}
public MessageInfoViewModel GetViewModel => new()
{
MessageId = MessageId,
ClientId = ClientId,
SenderName = SenderName,
DateDelivery = DateDelivery,
Subject = Subject,
Body = Body
};
}
}

View File

@ -1,8 +1,6 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using TravelCompanyContracts.BindingModels;
using TravelCompanyContracts.BusinessLogicContracts;
using TravelCompanyContracts.BusinessLogicsContracts;
using TravelCompanyContracts.SearchModels;
using TravelCompanyContracts.ViewModels;
@ -14,10 +12,12 @@ namespace TravelCompanyRestApi.Controllers
{
private readonly ILogger _logger;
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;
_logic = logic;
_mailLogic = mailLogic;
}
[HttpGet]
public ClientViewModel? Login(string login, string password)
@ -62,5 +62,21 @@ namespace TravelCompanyRestApi.Controllers
throw;
}
}
[HttpGet]
public List<MessageInfoViewModel>? GetMessages(int clientId)
{
try
{
return _mailLogic.ReadList(new MessageInfoSearchModel
{
ClientId = clientId
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error receiving client's emails");
throw;
}
}
}
}

View File

@ -5,6 +5,8 @@ using TravelCompanyDatabaseImplement.Implements;
using Microsoft.OpenApi.Models;
using TravelCompanyContracts.BusinessLogicContracts;
using TravelCompanyDataBaseImplement.Implements;
using TravelCompanyContracts.BindingModels;
using TravelCompanyBusinessLogic.MailWorker;
var builder = WebApplication.CreateBuilder(args);
@ -16,11 +18,16 @@ builder.Services.AddTransient<IClientStorage, ClientStorage>();
builder.Services.AddTransient<IImplementerStorage, ImplementerStorage>();
builder.Services.AddTransient<IOrderStorage, OrderStorage>();
builder.Services.AddTransient<ITravelStorage, TravelStorage>();
builder.Services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
builder.Services.AddTransient<IOrderLogic, OrderLogic>();
builder.Services.AddTransient<IImplementerLogic, ImplementerLogic>();
builder.Services.AddTransient<IClientLogic, ClientLogic>();
builder.Services.AddTransient<ITravelLogic, TravelLogic>();
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
@ -35,6 +42,17 @@ builder.Services.AddSwaggerGen(c =>
});
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,12 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"SmtpClientHost": "smtp.gmail.com",
"SmtpClientPort": "587",
"PopHost": "pop.gmail.com",
"PopPort": "995",
"MailLogin": "lab7mailgmail.com",
"MailPassword": "lab7"
}