Этап 3. Получение отчетов pdf на почту

This commit is contained in:
prodigygirl 2023-05-19 08:30:36 +04:00
parent c529bcf1b9
commit 675971d32f
16 changed files with 273 additions and 88 deletions

View File

@ -4,6 +4,7 @@ using HospitalContracts.SearchModels;
using HospitalContracts.StorageContracts;
using HospitalContracts.ViewModels;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
namespace HospitalBusinessLogic
{
@ -103,6 +104,10 @@ namespace HospitalBusinessLogic
throw new ArgumentNullException("Нет пароля пользователя",
nameof(model.Password));
}
if (!Regex.IsMatch(model.Login, @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,})+)$"))
{
throw new ArgumentException("Некорректно введена почта клиента", nameof(model.Login));
}
if (model.Password.Length < 5 || model.Password.Length > 15)
{
throw new ArgumentNullException("Пароль должен иметь длину от 5 до 15 символов",

View File

@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
<PackageReference Include="MailKit" Version="4.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />

View File

@ -0,0 +1,54 @@
using DocumentFormat.OpenXml.EMMA;
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
namespace HospitalBusinessLogic.MailWorker
{
public abstract class AbstractMailWorker
{
protected string _mailLogin = string.Empty;
protected string _mailPassword = string.Empty;
protected string _smtpClientHost = string.Empty;
protected int _smtpClientPort;
private readonly ILogger _logger;
public AbstractMailWorker(ILogger<AbstractMailWorker> logger)
{
_logger = logger;
}
public void MailConfig(MailConfigBindingModel config)
{
_mailLogin = config.MailLogin;
_mailPassword = config.MailPassword;
_smtpClientHost = config.SmtpClientHost;
_smtpClientPort = config.SmtpClientPort;
_logger.LogDebug("Config: {login}, {password}, {clientHost},{ clientPOrt}", _mailLogin, _mailPassword, _smtpClientHost,
_smtpClientPort);
}
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;
}
if (!Regex.IsMatch(info.MailAddress, @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,})+)$"))
{
return;
}
_logger.LogDebug("Send Mail: {To}, {Subject}", info.MailAddress, info.Subject);
await SendMailAsync(info);
}
protected abstract Task SendMailAsync(MailSendInfoBindingModel info);
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.Extensions.Logging;
using System.Net.Mail;
using System.Net;
using System.Text;
using MailKit.Net.Pop3;
using MailKit.Security;
using HospitalContracts.BindingModels;
namespace HospitalBusinessLogic.MailWorker
{
public class MailKitWorker : AbstractMailWorker
{
public MailKitWorker(ILogger<MailKitWorker> logger) : base(logger) { }
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;
info.Attachments.ForEach(attachment => objMailMessage.Attachments.Add(attachment));
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;
}
}
}
}

View File

@ -1,4 +1,5 @@
using HospitalBusinessLogic.OfficePackage;
using HospitalBusinessLogic.MailWorker;
using HospitalBusinessLogic.OfficePackage;
using HospitalBusinessLogic.OfficePackage.HelperModels;
using HospitalBusinessLogic.OfficePackage.Implements;
using HospitalContracts.BindingModels;
@ -7,6 +8,7 @@ using HospitalContracts.SearchModels;
using HospitalContracts.StorageContracts;
using HospitalContracts.ViewModels;
using System.Linq;
using System.Net.Mail;
namespace HospitalBusinessLogic
{
@ -23,10 +25,12 @@ namespace HospitalBusinessLogic
private readonly AbstractSaveToWord _saveToWord;
private readonly AbstractSaveToPdf _saveToPdf;
private readonly AbstractMailWorker _mailWorker;
public ReportLogic(IMedicineStorage medicineStorage, IPatientStorage patientStorage,
IPrescriptionStorage prescriptionStorage, ITreatmentStorage treatmentStorage,
IProcedureStorage procedureStorage, IRecipeStorage recipeStorage,
AbstractSaveToExcel abstractSaveToExcel, AbstractSaveToWord abstractSaveToWord, AbstractSaveToPdf abstractSaveToPdf)
AbstractSaveToExcel abstractSaveToExcel, AbstractSaveToWord abstractSaveToWord, AbstractSaveToPdf abstractSaveToPdf, AbstractMailWorker abstractMailWorker)
{
_medicineStorage = medicineStorage;
_patientStorage = patientStorage;
@ -38,6 +42,8 @@ namespace HospitalBusinessLogic
_saveToExcel = abstractSaveToExcel;
_saveToWord = abstractSaveToWord;
_saveToPdf = abstractSaveToPdf;
_mailWorker = abstractMailWorker;
}
public List<ReportPatientsMedicinesViewModel> GetMedicinePatients(ReportBindingModel model)
@ -138,7 +144,19 @@ namespace HospitalBusinessLogic
public void SendMailWithReportAttachments(ReportBindingModel model)
{
var stream = SavePrescriptionsToPdfFile(model);
if (stream == null)
{
throw new Exception("Pdf-document is not created");
}
var attachments = new List<Attachment>();
attachments.Add(new Attachment(stream, $"report-{DateTime.Now.ToShortDateString()}.pdf"));
_mailWorker.MailSendAsync(new MailSendInfoBindingModel()
{
Subject = "Отчет \"Поступления\"",
Text = $"Отчет по поступлениям лекарств, требующихся для выполнения процедур, за период с {model.DateFrom} по {model.DateTo}",
MailAddress = model.Email,
Attachments = attachments
});
}
}
}

View File

@ -0,0 +1,10 @@
namespace HospitalContracts.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; }
}
}

View File

@ -0,0 +1,13 @@
using System.Net.Mail;
namespace HospitalContracts.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;
public List<Attachment> Attachments { get; set; } = new();
}
}

View File

@ -14,5 +14,6 @@ namespace HospitalContracts.BindingModels
public DateTime? DateTo { get; set; }
// выбранные лекарства
public List<int> Medicines { get; set; } = new();
public string Email { get; set; } = string.Empty;
}
}

View File

@ -27,9 +27,9 @@ namespace HospitalContracts.BusinessLogicContracts
/// <param name="model"></param>
MemoryStream SavePatientsToExcelFile(ReportBindingModel model);
/// <summary>
/// Сохранение пациентов с указанием лекарств в файл-Pdf
/// Отправка отчета на почту по поступлением с указанием лекарств и процедур в файл-Pdf
/// </summary>
/// <param name="model"></param>
MemoryStream SavePrescriptionsToPdfFile(ReportBindingModel model);
void SendMailWithReportAttachments(ReportBindingModel model);
}
}

View File

@ -13,13 +13,11 @@ namespace HospitalRestApi.Controllers
{
private readonly ILogger _logger;
private readonly IApothecaryLogic _logic;
//private readonly IMessageInfoLogic _mailLogic;
public ApothecaryController(IApothecaryLogic logic, ILogger<ApothecaryController> logger)
{
_logger = logger;
_logic = logic;
//_mailLogic = mailLogic;
}
[HttpGet]
@ -64,22 +62,6 @@ namespace HospitalRestApi.Controllers
_logger.LogError(ex, "Ошибка обновления данных");
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

@ -67,18 +67,17 @@ namespace HospitalRestApi.Controllers
throw;
}
}
[HttpPost]
public ActionResult GetReportPrescriptionsPdf(DateTime startDate, DateTime endDate, ReportBindingModel model)
public void SendReportPrescriptionsPdf(ReportBindingModel model)
{
try
{
var stream = _logic.SavePrescriptionsToPdfFile(model);
return File(stream, "application/pdf", "pdf.pdf");
}
_logic.SendMailWithReportAttachments(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка пациентов");
_logger.LogError(ex, "Ошибка при отправке отчета на почту");
throw;
}
}

View File

@ -1,11 +1,14 @@
using HospitalBusinessLogic;
using HospitalBusinessLogic.MailWorker;
using HospitalBusinessLogic.OfficePackage;
using HospitalBusinessLogic.OfficePackage.Implements;
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.StorageContracts;
using HospitalDatabaseImplement.Implements;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
@ -31,7 +34,7 @@ builder.Services.AddTransient<IReportLogic, ReportLogic>();
builder.Services.AddTransient<AbstractSaveToWord, SaveToWord>();
builder.Services.AddTransient<AbstractSaveToExcel, SaveToExcel>();
builder.Services.AddTransient<AbstractSaveToPdf, SaveToPdf>();
//builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>();
builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>();
builder.Services.AddControllers().AddJsonOptions((option) =>
{
@ -50,8 +53,18 @@ 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()),
});
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{

View File

@ -5,5 +5,10 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"SmtpClientHost": "smtp.gmail.com",
"SmtpClientPort": "587",
"MailLogin": "MailNoNameLab@gmail.com",
"MailPassword": "yfbo xzzw mxee jaqt"
}

View File

@ -14,32 +14,52 @@ namespace HospitalWeb.Controllers
[HttpGet("/reports/patients")]
public IActionResult ReportPatients()
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Medicines = APIClient.GetRequest<List<MedicineViewModel>>($"api/medicine/getmedicines");
return View();
}
[HttpGet("/reports/precriptions")]
public IActionResult ReportPrescriptions(List<ReportPrescriptionProcedureViewModel>? report)
{
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
return View(report);
}
[HttpGet("/report/getprescriptions")]
public IActionResult GetPrescriptions(DateTime startDate, DateTime endDate, string type)
{
/* // если type - получение отчета на форме
// Преобразуем даты к нужному формату
string startDateString = startDate.ToString("yyyy-MM-dd HH:mm:ss.fffffff");
string endDateString = endDate.ToString("yyyy-MM-dd HH:mm:ss.fffffff");
var report = APIClient.GetRequest<List<ReportPrescriptionProcedureViewModel>>($"api/report/getreportprescriptions?startDate={startDateString}&endDate={endDateString}");
return View("ReportPrescriptions", report);*/
// иначе отправка на почту
string startDateString = startDate.ToString("yyyy-MM-dd HH:mm:ss.fffffff");
string endDateString = endDate.ToString("yyyy-MM-dd HH:mm:ss.fffffff");
var fileData = APIClient.GetFileRequest($"api/report/getreportprescriptionspdf?", new ReportBindingModel { DateFrom = startDate, DateTo = endDate });
if (fileData != null)
return File(fileData.Value.stream, fileData.Value.contentType!, "prescriptions.pdf");
throw new Exception($"Failed to retrieve file from.");
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
if (type == "email")
{
APIClient.PostRequest($"api/report/sendreportprescriptionspdf", new ReportBindingModel { DateFrom = startDate, DateTo = endDate, Email = APIClient.Apothecary.Login });
ViewBag.EmailSent = true;
ViewBag.EmailAddress = APIClient.Apothecary.Login;
return View("ReportPrescriptions");
}
else if (type == "form")
{
// Преобразуем даты к нужному формату
string startDateString = startDate.ToString("yyyy-MM-dd HH:mm:ss.fffffff");
string endDateString = endDate.ToString("yyyy-MM-dd HH:mm:ss.fffffff");
var report = APIClient.GetRequest<List<ReportPrescriptionProcedureViewModel>>($"api/report/getreportprescriptions?startDate={startDateString}&endDate={endDateString}");
return View("ReportPrescriptions", report);
}
else
{
return BadRequest("Invalid report type");
}
}
[HttpGet("/reports/download")]

View File

@ -9,57 +9,57 @@
if (Model.Id > 0)
{
<div class="text-center">
<h2 class="display-4">Редактирование рецепта</h2>
<h2 class="display-4">Редактирование рецепта</h2>
</div>
}
else {
<div class="text-center">
<h2 class="display-4">Создание рецепта</h2>
<h2 class="display-4">Создание рецепта</h2>
</div>
}
<form id="recipe-form" method="post">
<input type="hidden" name="id" value="@Model?.Id" />
<div class="row">
<div class="col-4">Название:</div>
<div class="col-8"><input type="text" id="name" value="@Model?.Name" name="name" required/></div>
</div>
<div class="row">
<div class="col-4">Добавление лекарств</div>
<div class="col-8">
<select id="medicines" name="medicines" class="form-control" asp-items="@(new SelectList(@ViewBag.Medicines,"Id", "Name"))"></select>
<button type="button" onclick="addMedicine()">Добавить лекарство</button>
<input type="hidden" name="id" value="@Model?.Id" />
<div class="row">
<div class="col-4">Название:</div>
<div class="col-8"><input type="text" id="name" value="@Model?.Name" name="name" required/></div>
</div>
<div class="row">
<div class="col-4">Добавление лекарств</div>
<div class="col-8">
<select id="medicines" name="medicines" class="form-control" asp-items="@(new SelectList(@ViewBag.Medicines,"Id", "Name"))"></select>
<button type="button" onclick="addMedicine()">Добавить лекарство</button>
</div>
</div>
</div>
<div class="row">
<table id="medicinesTable">
<thead>
<tr>
<th>Название</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var medicine in Model.RecipeMedicines)
<div class="row">
<table id="medicinesTable">
<thead>
<tr>
<th>Название</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var medicine in Model.RecipeMedicines)
{
<tr>
<td>@medicine.Value.Name</td>
<td>
<button type="button" data-id="@medicine.Key" onclick="removeMedicine('@medicine.Key')">Удалить</button>
</td>
</tr>
<tr>
<td>@medicine.Value.Name</td>
<td>
<button type="button" class="btn btn-danger" data-id="@medicine.Key" onclick="removeMedicine('@medicine.Key')">Удалить</button>
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="row">
<div class="col-8"></div>
<div class="col-4">
<input type="submit" value="Отправить" class="btn btn-primary" />
</tbody>
</table>
</div>
</div>
@foreach (var medicine in Model.RecipeMedicines.Keys)
<div class="row">
<div class="col-8"></div>
<div class="col-4">
<input type="submit" value="Отправить" class="btn btn-primary" />
</div>
</div>
@foreach (var medicine in Model.RecipeMedicines.Keys)
{
<input type="hidden" name="RecipeMedicines[@medicine]" value="@medicine"/>
<input type="hidden" name="RecipeMedicines[@medicine]" value="@medicine"/>
}
</form>
}
@ -75,7 +75,7 @@
}
recipeMedicines[medicineId] = { Id: medicineId, Name: medicineName };
var row = $('<tr>').append($('<td>').text(medicineName));
var removeButton = $('<button>').text('Remove').attr('data-id', medicineId).click((function(id) {
var removeButton = $('<button>').text('Удалить').attr('data-id', medicineId).attr('class', 'btn btn-danger').click((function(id) {
return function() {
removeMedicine(id);
};

View File

@ -19,10 +19,10 @@
<input type="date" class="form-control" id="endDate" name="endDate" required />
</div>
<div class="form-group mx-sm-3 mb-2">
<button type="submit" class="btn btn-primary" name="type" value="getReport">Получить отчет</button>
<button type="submit" class="btn btn-primary" name="type" value="form">Получить отчет</button>
</div>
<div class="form-group mx-sm-3 mb-2">
<button type="submit" class="btn btn-secondary" name="type" value="sendPdf">Отправить pdf на почту</button>
<button type="submit" class="btn btn-secondary" name="type" value="email">Отправить pdf на почту</button>
</div>
</form>
@if (Model != null && Model.Any())
@ -48,4 +48,28 @@
}
</tbody>
</table>
}
<div class="modal" id="emailSentModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Успешно отправлено</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Письмо успешно отправлено на почту: <span id="emailAddress"></span>
</div>
</div>
</div>
</div>
@section scripts {
@if (ViewBag.EmailSent != null && ViewBag.EmailSent)
{
<script>
$(document).ready(function () {
$('#emailSentModal').modal('show');
$('#emailAddress').text('@ViewBag.EmailAddress');
});
</script>
}
}