Этап 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.StorageContracts;
using HospitalContracts.ViewModels; using HospitalContracts.ViewModels;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
namespace HospitalBusinessLogic namespace HospitalBusinessLogic
{ {
@ -103,6 +104,10 @@ namespace HospitalBusinessLogic
throw new ArgumentNullException("Нет пароля пользователя", throw new ArgumentNullException("Нет пароля пользователя",
nameof(model.Password)); 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) if (model.Password.Length < 5 || model.Password.Length > 15)
{ {
throw new ArgumentNullException("Пароль должен иметь длину от 5 до 15 символов", throw new ArgumentNullException("Пароль должен иметь длину от 5 до 15 символов",

View File

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" /> <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.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" 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.HelperModels;
using HospitalBusinessLogic.OfficePackage.Implements; using HospitalBusinessLogic.OfficePackage.Implements;
using HospitalContracts.BindingModels; using HospitalContracts.BindingModels;
@ -7,6 +8,7 @@ using HospitalContracts.SearchModels;
using HospitalContracts.StorageContracts; using HospitalContracts.StorageContracts;
using HospitalContracts.ViewModels; using HospitalContracts.ViewModels;
using System.Linq; using System.Linq;
using System.Net.Mail;
namespace HospitalBusinessLogic namespace HospitalBusinessLogic
{ {
@ -23,10 +25,12 @@ namespace HospitalBusinessLogic
private readonly AbstractSaveToWord _saveToWord; private readonly AbstractSaveToWord _saveToWord;
private readonly AbstractSaveToPdf _saveToPdf; private readonly AbstractSaveToPdf _saveToPdf;
private readonly AbstractMailWorker _mailWorker;
public ReportLogic(IMedicineStorage medicineStorage, IPatientStorage patientStorage, public ReportLogic(IMedicineStorage medicineStorage, IPatientStorage patientStorage,
IPrescriptionStorage prescriptionStorage, ITreatmentStorage treatmentStorage, IPrescriptionStorage prescriptionStorage, ITreatmentStorage treatmentStorage,
IProcedureStorage procedureStorage, IRecipeStorage recipeStorage, IProcedureStorage procedureStorage, IRecipeStorage recipeStorage,
AbstractSaveToExcel abstractSaveToExcel, AbstractSaveToWord abstractSaveToWord, AbstractSaveToPdf abstractSaveToPdf) AbstractSaveToExcel abstractSaveToExcel, AbstractSaveToWord abstractSaveToWord, AbstractSaveToPdf abstractSaveToPdf, AbstractMailWorker abstractMailWorker)
{ {
_medicineStorage = medicineStorage; _medicineStorage = medicineStorage;
_patientStorage = patientStorage; _patientStorage = patientStorage;
@ -38,6 +42,8 @@ namespace HospitalBusinessLogic
_saveToExcel = abstractSaveToExcel; _saveToExcel = abstractSaveToExcel;
_saveToWord = abstractSaveToWord; _saveToWord = abstractSaveToWord;
_saveToPdf = abstractSaveToPdf; _saveToPdf = abstractSaveToPdf;
_mailWorker = abstractMailWorker;
} }
public List<ReportPatientsMedicinesViewModel> GetMedicinePatients(ReportBindingModel model) public List<ReportPatientsMedicinesViewModel> GetMedicinePatients(ReportBindingModel model)
@ -138,7 +144,19 @@ namespace HospitalBusinessLogic
public void SendMailWithReportAttachments(ReportBindingModel model) public void SendMailWithReportAttachments(ReportBindingModel model)
{ {
var stream = SavePrescriptionsToPdfFile(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 DateTime? DateTo { get; set; }
// выбранные лекарства // выбранные лекарства
public List<int> Medicines { get; set; } = new(); 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> /// <param name="model"></param>
MemoryStream SavePatientsToExcelFile(ReportBindingModel model); MemoryStream SavePatientsToExcelFile(ReportBindingModel model);
/// <summary> /// <summary>
/// Сохранение пациентов с указанием лекарств в файл-Pdf /// Отправка отчета на почту по поступлением с указанием лекарств и процедур в файл-Pdf
/// </summary> /// </summary>
/// <param name="model"></param> /// <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 ILogger _logger;
private readonly IApothecaryLogic _logic; private readonly IApothecaryLogic _logic;
//private readonly IMessageInfoLogic _mailLogic;
public ApothecaryController(IApothecaryLogic logic, ILogger<ApothecaryController> logger) public ApothecaryController(IApothecaryLogic logic, ILogger<ApothecaryController> logger)
{ {
_logger = logger; _logger = logger;
_logic = logic; _logic = logic;
//_mailLogic = mailLogic;
} }
[HttpGet] [HttpGet]
@ -65,21 +63,5 @@ namespace HospitalRestApi.Controllers
throw; throw;
} }
} }
/* [HttpGet]
public List<MessageInfoViewModel>? GetMessages(int clientId)
{
try
{
return _mailLogic.ReadList(new MessageInfoSearchModel
{
ClientId = clientId
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения писем клиента");
throw;
}
}*/
} }
} }

View File

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

View File

@ -1,11 +1,14 @@
using HospitalBusinessLogic; using HospitalBusinessLogic;
using HospitalBusinessLogic.MailWorker;
using HospitalBusinessLogic.OfficePackage; using HospitalBusinessLogic.OfficePackage;
using HospitalBusinessLogic.OfficePackage.Implements; using HospitalBusinessLogic.OfficePackage.Implements;
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts; using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.StorageContracts; using HospitalContracts.StorageContracts;
using HospitalDatabaseImplement.Implements; using HospitalDatabaseImplement.Implements;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
@ -31,7 +34,7 @@ builder.Services.AddTransient<IReportLogic, ReportLogic>();
builder.Services.AddTransient<AbstractSaveToWord, SaveToWord>(); builder.Services.AddTransient<AbstractSaveToWord, SaveToWord>();
builder.Services.AddTransient<AbstractSaveToExcel, SaveToExcel>(); builder.Services.AddTransient<AbstractSaveToExcel, SaveToExcel>();
builder.Services.AddTransient<AbstractSaveToPdf, SaveToPdf>(); builder.Services.AddTransient<AbstractSaveToPdf, SaveToPdf>();
//builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>(); builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>();
builder.Services.AddControllers().AddJsonOptions((option) => builder.Services.AddControllers().AddJsonOptions((option) =>
{ {
@ -50,8 +53,18 @@ builder.Services.AddSwaggerGen(c =>
}); });
}); });
var app = builder.Build(); var app = builder.Build();
var mailSender = app.Services.GetService<AbstractMailWorker>();
mailSender?.MailConfig(new MailConfigBindingModel
{
MailLogin = builder.Configuration?.GetSection("MailLogin")?.Value?.ToString() ?? string.Empty,
MailPassword = builder.Configuration?.GetSection("MailPassword")?.Value?.ToString() ?? string.Empty,
SmtpClientHost = builder.Configuration?.GetSection("SmtpClientHost")?.Value?.ToString() ?? string.Empty,
SmtpClientPort = Convert.ToInt32(builder.Configuration?.GetSection("SmtpClientPort")?.Value?.ToString()),
});
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {

View File

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

View File

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

View File

@ -44,7 +44,7 @@
<tr> <tr>
<td>@medicine.Value.Name</td> <td>@medicine.Value.Name</td>
<td> <td>
<button type="button" data-id="@medicine.Key" onclick="removeMedicine('@medicine.Key')">Удалить</button> <button type="button" class="btn btn-danger" data-id="@medicine.Key" onclick="removeMedicine('@medicine.Key')">Удалить</button>
</td> </td>
</tr> </tr>
} }
@ -75,7 +75,7 @@
} }
recipeMedicines[medicineId] = { Id: medicineId, Name: medicineName }; recipeMedicines[medicineId] = { Id: medicineId, Name: medicineName };
var row = $('<tr>').append($('<td>').text(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() { return function() {
removeMedicine(id); removeMedicine(id);
}; };

View File

@ -19,10 +19,10 @@
<input type="date" class="form-control" id="endDate" name="endDate" required /> <input type="date" class="form-control" id="endDate" name="endDate" required />
</div> </div>
<div class="form-group mx-sm-3 mb-2"> <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>
<div class="form-group mx-sm-3 mb-2"> <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> </div>
</form> </form>
@if (Model != null && Model.Any()) @if (Model != null && Model.Any())
@ -49,3 +49,27 @@
</tbody> </tbody>
</table> </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>
}
}