Compare commits

..

2 Commits

Author SHA1 Message Date
059eede984 не получается 2024-06-02 14:24:38 +04:00
bb87bb3fb8 в процессе 2024-06-01 22:55:07 +04:00
19 changed files with 272 additions and 170 deletions

View File

@ -4,6 +4,7 @@ using ConfectioneryContracts.SearchModels;
using ConfectioneryContracts.StoragesContracts;
using ConfectioneryContracts.ViewModels;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
using System;
using System.Collections.Generic;
using System.Linq;
@ -101,16 +102,16 @@ namespace ConfectioneryBusinessLogic
{
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);
_logger.LogInformation("Client. ClientFIO:{ClientFIO}.Email:{Email}.Id:{Id}",
model.ClientFIO, model.Email, model.Id);
var element = _clientStorage.GetElement(new ClientSearchModel
{
ClientFIO = model.ClientFIO

View File

@ -61,7 +61,7 @@ namespace ConfectioneryBusinessLogic
}
if (string.IsNullOrEmpty(model.SenderName))
{
throw new ArgumentNullException("Не указао почта", nameof(model.SenderName));
throw new ArgumentNullException("Не указана почта", nameof(model.SenderName));
}
if (string.IsNullOrEmpty(model.Subject))
{
@ -79,7 +79,7 @@ namespace ConfectioneryBusinessLogic
});
if (element == null)
{
_logger.LogWarning("Не удалоссь найти клиента, отправившего письмо с адреса Email:{Email}", model.SenderName);
_logger.LogWarning("Не удалось найти клиента, отправившего письмо с адреса Email:{Email}", model.SenderName);
}
else
{

View File

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ConfectioneryBusinessLogic
{
@ -17,10 +18,13 @@ namespace ConfectioneryBusinessLogic
{
private readonly ILogger _logger;
private readonly IOrderStorage _orderStorage;
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage)
private readonly AbstractMailWorker _mailWorker;
static readonly object _locker = new object();
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage, AbstractMailWorker mailWorker)
{
_logger = logger;
_orderStorage = orderStorage;
_mailWorker = mailWorker;
}
public OrderViewModel? ReadElement(OrderSearchModel model)
{
@ -58,19 +62,24 @@ namespace ConfectioneryBusinessLogic
if (model.Status != OrderStatus.Неизвестен)
return false;
model.Status = OrderStatus.Принят;
if (_orderStorage.Insert(model) == null)
var element = _orderStorage.Insert(model);
if (element == null)
{
_logger.LogWarning("Insert operation failed");
return false;
}
Task.Run(() => _mailWorker.MailSendAsync(new MailSendInfoBindingModel
{
MailAddress = element.ClientEmail,
Subject = $"Изменение статуса заказа №{element.Id}",
Text = $"Заказ №{element.Id} на кондитерское изделие {element.PastryName} от {element.DateCreate} на сумму {element.Sum} принят."
}));
return true;
}
public bool TakeOrderInWork(OrderBindingModel model)
{
return ChangeStatus(model, OrderStatus.Выполняется);
}
public bool FinishOrder(OrderBindingModel model)
{
return ChangeStatus(model, OrderStatus.Готов);
@ -141,6 +150,13 @@ namespace ConfectioneryBusinessLogic
_logger.LogWarning("Update operation failed");
return false;
}
string DateInfo = model.DateImplement.HasValue ? $"Дата выполнения {model.DateImplement}" : "";
Task.Run(() => _mailWorker.MailSendAsync(new MailSendInfoBindingModel
{
MailAddress = element.ClientEmail,
Subject = $"Изменение статуса заказа №{element.Id}",
Text = $"Заказ №{element.Id} на кондитерское изделие {element.PastryName} от {element.DateCreate} на сумму {element.Sum} {model.Status}. {DateInfo}"
}));
return true;
}
_logger.LogWarning("Changing status operation faled: Current-{Status}:required-{requiredStatus}.", model.Status, requiredStatus);

View File

@ -4,6 +4,7 @@ using ConfectioneryClientApp.Models;
using ConfectioneryClientApp;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
namespace ConfectioneryClientApp.Controllers
{
@ -140,5 +141,16 @@ namespace ConfectioneryClientApp.Controllers
var prod = APIClient.GetRequest<PastryViewModel>($"api/main/getpastry?pastryId={pastry}");
return count * (prod?.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,51 @@
@using ConfectioneryContracts.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

@ -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">Письма</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Enter">Вход</a>
</li>

View File

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

View File

@ -23,7 +23,10 @@ namespace ConfectioneryDatabaseImplement.Models
public string Password { get; set; } = string.Empty;
[ForeignKey("ClientId")]
public virtual List<Order> Orders { get; set; } = new();
public virtual List<Order> ClientOrders { get; set; } = new();
[ForeignKey("ClientId")]
public virtual List<MessageInfo> ClientMessages { get; set; } = new();
public static Client? Create(ClientBindingModel model)
{

View File

@ -36,10 +36,9 @@ namespace ConfectioneryDatabaseImplement.Implements
return null;
}
using var context = new ConfectioneryDatabase();
return context.Clients.FirstOrDefault(x =>
(!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && !string.IsNullOrEmpty(model.Password) && x.Password == model.Password) ||
(model.Id.HasValue && x.Id == model.Id))
?.GetViewModel;
return context.Clients.FirstOrDefault(x => (model.Id.HasValue && x.Id == model.Id) ||
(!string.IsNullOrEmpty(model.ClientFIO) && x.ClientFIO == model.ClientFIO) ||
(!string.IsNullOrEmpty(model.Email) && x.Email == model.Email && (string.IsNullOrEmpty(model.Password) || x.Password == model.Password)))?.GetViewModel;
}
public ClientViewModel? Insert(ClientBindingModel model)

View File

@ -15,7 +15,7 @@ namespace ConfectioneryDatabaseImplement
{
if (optionsBuilder.IsConfigured == false)
{
optionsBuilder.UseSqlServer(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=ConfectioneryDatabaseNewMail;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True");
optionsBuilder.UseSqlServer(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=ConfectioneryDatabaseNewMailDB;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True");
}
base.OnConfiguring(optionsBuilder);
}

View File

@ -1,48 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ConfectioneryDatabaseImplement.Migrations
{
/// <inheritdoc />
public partial class InitialMail : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
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.CreateIndex(
name: "IX_MessageInfos_ClientId",
table: "MessageInfos",
column: "ClientId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MessageInfos");
}
}
}

View File

@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace ConfectioneryDatabaseImplement.Migrations
{
[DbContext(typeof(ConfectioneryDatabase))]
[Migration("20240523044722_InitialMail")]
[Migration("20240602085942_InitialMail")]
partial class InitialMail
{
/// <inheritdoc />
@ -219,7 +219,7 @@ namespace ConfectioneryDatabaseImplement.Migrations
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.MessageInfo", b =>
{
b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client")
.WithMany()
.WithMany("ClientMessages")
.HasForeignKey("ClientId");
b.Navigation("Client");
@ -228,7 +228,7 @@ namespace ConfectioneryDatabaseImplement.Migrations
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Order", b =>
{
b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client")
.WithMany("Orders")
.WithMany("ClientOrders")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@ -271,7 +271,9 @@ namespace ConfectioneryDatabaseImplement.Migrations
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Client", b =>
{
b.Navigation("Orders");
b.Navigation("ClientMessages");
b.Navigation("ClientOrders");
});
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Component", b =>

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ConfectioneryDatabaseImplement.Migrations
{
/// <inheritdoc />
public partial class InitialMail : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -216,7 +216,7 @@ namespace ConfectioneryDatabaseImplement.Migrations
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.MessageInfo", b =>
{
b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client")
.WithMany()
.WithMany("ClientMessages")
.HasForeignKey("ClientId");
b.Navigation("Client");
@ -225,7 +225,7 @@ namespace ConfectioneryDatabaseImplement.Migrations
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Order", b =>
{
b.HasOne("ConfectioneryDatabaseImplement.Models.Client", "Client")
.WithMany("Orders")
.WithMany("ClientOrders")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@ -268,7 +268,9 @@ namespace ConfectioneryDatabaseImplement.Migrations
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Client", b =>
{
b.Navigation("Orders");
b.Navigation("ClientMessages");
b.Navigation("ClientOrders");
});
modelBuilder.Entity("ConfectioneryDatabaseImplement.Models.Component", b =>

View File

@ -76,6 +76,7 @@ namespace ConfectioneryDatabaseImplement.Models
Id = Id,
ClientId = ClientId,
ClientFIO = Client.ClientFIO,
ClientEmail = Client.Email,
PastryId = PastryId,
PastryName = Pastry.PastryName,
ImplementerId = ImplementerId,

View File

@ -11,15 +11,15 @@ namespace ConfectioneryRestApi.Controllers
public class ClientController : Controller
{
private readonly ILogger _logger;
private readonly IClientLogic _logic;
private readonly IMessageInfoLogic _mailLogic;
public ClientController(IClientLogic logic, ILogger<ClientController> logger)
public ClientController(IClientLogic logic, IMessageInfoLogic mailLogic, ILogger<ClientController> logger)
{
_logger = logger;
_logic = logic;
_mailLogic = mailLogic;
}
[HttpGet]
public ClientViewModel? Login(string login, string password)
{
@ -37,7 +37,6 @@ namespace ConfectioneryRestApi.Controllers
throw;
}
}
[HttpPost]
public void Register(ClientBindingModel model)
{
@ -51,7 +50,6 @@ namespace ConfectioneryRestApi.Controllers
throw;
}
}
[HttpPost]
public void UpdateData(ClientBindingModel model)
{
@ -65,5 +63,23 @@ namespace ConfectioneryRestApi.Controllers
throw;
}
}
[HttpGet]
public List<MessageInfoViewModel>? GetMessages(int clientId)
{
try
{
return _mailLogic.ReadList(new MessageInfoSearchModel
{
ClientId = clientId
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения писем клиента");
throw;
}
}
}
}

View File

@ -1,4 +1,5 @@
using ConfectioneryBusinessLogic;
using ConfectioneryContracts.BindingModels;
using ConfectioneryContracts.BusinessLogicsContracts;
using ConfectioneryContracts.StoragesContracts;
using ConfectioneryDatabaseImplement.Implements;
@ -17,10 +18,14 @@ builder.Services.AddTransient<IClientLogic, ClientLogic>();
builder.Services.AddTransient<IPastryLogic, PastryLogic>();
builder.Services.AddTransient<IImplementerStorage, ImplementerStorage>();
builder.Services.AddTransient<IImplementerLogic, ImplementerLogic>();
builder.Services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
builder.Services.AddTransient<IMessageInfoLogic, MessageInfoLogic>();
builder.Services.AddTransient<AbstractMailWorker, MailKitWorker>();
builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at
https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
@ -31,6 +36,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

@ -43,6 +43,7 @@ namespace ConfectioneryView
dataGridView.DataSource = list;
dataGridView.Columns["PastryId"].Visible = false;
dataGridView.Columns["ClientId"].Visible = false;
dataGridView.Columns["ClientEmail"].Visible = false;
dataGridView.Columns["ImplementerId"].Visible = false;
dataGridView.Columns["PastryName"].AutoSizeMode =
DataGridViewAutoSizeColumnMode.Fill;

View File

@ -63,6 +63,7 @@ namespace ConfectioneryView
services.AddTransient<IMessageInfoStorage, MessageInfoStorage>();
services.AddTransient<IClientStorage, ClientStorage>();
services.AddTransient<IImplementerStorage, ImplementerStorage>();
services.AddTransient<IImplementerLogic, ImplementerLogic>();
services.AddTransient<IClientLogic, ClientLogic>();
services.AddTransient<IComponentLogic, ComponentLogic>();
@ -71,10 +72,12 @@ namespace ConfectioneryView
services.AddTransient<IReportLogic, ReportLogic>();
services.AddTransient<IMessageInfoLogic, MessageInfoLogic>();
services.AddTransient<IWorkProcess, WorkModeling>();
services.AddTransient<AbstractSaveToWord, SaveToWord>();
services.AddTransient<AbstractSaveToExcel, SaveToExcel>();
services.AddTransient<AbstractSaveToPdf, SaveToPdf>();
services.AddSingleton<AbstractMailWorker, MailKitWorker>();
services.AddTransient<FormMain>();
services.AddTransient<FormComponent>();
services.AddTransient<FormComponents>();
@ -88,6 +91,7 @@ namespace ConfectioneryView
services.AddTransient<FormImplementers>();
services.AddTransient<FormImplementer>();
services.AddTransient<FormMail>();
services.AddTransient<EntityFrameworkDesignServicesBuilder>();
}
private static void MailCheck(object obj) => ServiceProvider?.GetService<AbstractMailWorker>()?.MailCheck();