Compare commits

...

14 Commits

76 changed files with 3388 additions and 65 deletions

View File

@ -11,9 +11,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HospitalContracts", "Hospit
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HospitalDatabaseImplement", "HospitalDatabaseImplement\HospitalDatabaseImplement.csproj", "{B99AB6C1-2F4F-4E40-B933-45CE5E6CC37B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HospitalDatabaseImplement", "HospitalDatabaseImplement\HospitalDatabaseImplement.csproj", "{B99AB6C1-2F4F-4E40-B933-45CE5E6CC37B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HospitalBusinessLogic", "HospitalBusinessLogic\HospitalBusinessLogic.csproj", "{C8819EE4-365C-4960-9578-3B8A45B5EBF4}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HospitalBusinessLogic", "HospitalBusinessLogic\HospitalBusinessLogic.csproj", "{C8819EE4-365C-4960-9578-3B8A45B5EBF4}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest", "UnitTest\UnitTest.csproj", "{A8EB3C34-5C9B-4C40-8B0F-21D62ACBFFFF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HospitalRestApi", "HospitalRestApi\HospitalRestApi.csproj", "{71DD78F3-FA96-4027-97D7-45E6E34654B8}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -41,10 +41,10 @@ Global
{C8819EE4-365C-4960-9578-3B8A45B5EBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {C8819EE4-365C-4960-9578-3B8A45B5EBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8819EE4-365C-4960-9578-3B8A45B5EBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8819EE4-365C-4960-9578-3B8A45B5EBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8819EE4-365C-4960-9578-3B8A45B5EBF4}.Release|Any CPU.Build.0 = Release|Any CPU {C8819EE4-365C-4960-9578-3B8A45B5EBF4}.Release|Any CPU.Build.0 = Release|Any CPU
{A8EB3C34-5C9B-4C40-8B0F-21D62ACBFFFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {71DD78F3-FA96-4027-97D7-45E6E34654B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8EB3C34-5C9B-4C40-8B0F-21D62ACBFFFF}.Debug|Any CPU.Build.0 = Debug|Any CPU {71DD78F3-FA96-4027-97D7-45E6E34654B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8EB3C34-5C9B-4C40-8B0F-21D62ACBFFFF}.Release|Any CPU.ActiveCfg = Release|Any CPU {71DD78F3-FA96-4027-97D7-45E6E34654B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8EB3C34-5C9B-4C40-8B0F-21D62ACBFFFF}.Release|Any CPU.Build.0 = Release|Any CPU {71DD78F3-FA96-4027-97D7-45E6E34654B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

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

@ -7,10 +7,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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.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" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.3" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.2.3" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

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

@ -0,0 +1,123 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
using HospitalBusinessLogic.OfficePackage.HelperModels;
namespace HospitalBusinessLogic.OfficePackage
{
public abstract class AbstractSaveToExcel
{
/// <summary>
/// Создание отчета
/// </summary>
/// <param name="info"></param>
public MemoryStream CreateReport(ExcelInfo info)
{
CreateExcel(info);
// название таблицы
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = 1,
Text = info.Title,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
MergeCells(new ExcelMergeParameters
{
CellFromName = "A1",
CellToName = "E1"
});
// названия столбцов
char columnName = 'A';
foreach (var header in info.Headers)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = columnName.ToString(),
RowIndex = 2,
Text = header,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
columnName++;
}
uint rowIndex = 3;
foreach (var element in info.PatientsMedicines)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = element.MedicineName,
StyleInfo = ExcelStyleInfoType.Text
});
rowIndex++;
int patientNum = 1;
foreach (var patient in element.Patients)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = patientNum.ToString(),
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "B",
RowIndex = rowIndex,
Text = patient.Item1,
StyleInfo =
ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "C",
RowIndex = rowIndex,
Text = patient.Item2,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "D",
RowIndex = rowIndex,
Text = patient.Item3 != null ? patient.Item3 : "",
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "E",
RowIndex = rowIndex,
Text = patient.Item4.ToShortDateString(),
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
patientNum++;
}
rowIndex++;
}
return SaveExcel(info);
}
/// <summary>
/// Создание excel-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreateExcel(ExcelInfo info);
/// <summary>
/// Добавляем новую ячейку в лист
/// </summary>
/// <param name="cellParameters"></param>
protected abstract void InsertCellInWorksheet(ExcelCellParameters excelParams);
/// <summary>
/// Объединение ячеек
/// </summary>
/// <param name="mergeParameters"></param>
protected abstract void MergeCells(ExcelMergeParameters excelParams);
/// <summary>
/// Сохранение файла
/// </summary>
/// <param name="info"></param>
protected abstract MemoryStream SaveExcel(ExcelInfo info);
}
}

View File

@ -0,0 +1,68 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
using HospitalBusinessLogic.OfficePackage.HelperModels;
namespace HospitalBusinessLogic.OfficePackage
{
public abstract class AbstractSaveToPdf
{
public MemoryStream CreateDoc(PdfInfo info)
{
CreatePdf(info);
CreateParagraph(new PdfParagraph
{
Text = info.Title,
Style = "NormalTitle",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
CreateParagraph(new PdfParagraph
{
Text = $"с { info.DateFrom.ToShortDateString() } по { info.DateTo.ToShortDateString() }", Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
CreateTable(new List<string> { "3cm", "3cm", "3cm", "3cm" });
CreateRow(new PdfRowParameters
{
Texts = new List<string> { "Дата поступления", "Лекарство", "Количество", "Название процедуры" },
Style = "NormalTitle",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
foreach (var element in info.Prescriptions)
{
CreateRow(new PdfRowParameters
{
Texts = new List<string> { element.DateCreate.ToShortDateString(), element.MedicineName, element.Number.ToString(), element.ProcedureName.ToString() },
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left
});
}
return SavePdf(info);
}
/// <summary>
/// Создание pdf-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreatePdf(PdfInfo info);
/// <summary>
/// Создание параграфа с текстом
/// </summary>
/// <param name="title"></param>
/// <param name="style"></param>
protected abstract void CreateParagraph(PdfParagraph paragraph);
/// <summary>
/// Создание таблицы
/// </summary>
/// <param name="title"></param>
/// <param name="style"></param>
protected abstract void CreateTable(List<string> columns);
/// <summary>
/// Создание и заполнение строки
/// </summary>
/// <param name="rowParameters"></param>
protected abstract void CreateRow(PdfRowParameters rowParameters);
/// <summary>
/// Сохранение файла
/// </summary>
/// <param name="info"></param>
protected abstract MemoryStream SavePdf(PdfInfo info);
}
}

View File

@ -0,0 +1,68 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
using HospitalBusinessLogic.OfficePackage.HelperModels;
namespace HospitalBusinessLogic.OfficePackage
{
public abstract class AbstractSaveToWord
{
public MemoryStream CreateDoc(WordInfo info)
{
CreateWord(info);
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)> { (info.Title, new WordTextProperties { Bold = true, Size = "24", }) },
TextProperties = new WordTextProperties
{
Size = "24",
JustificationType = WordJustificationType.Center
}
});
foreach (var element in info.PatientsMedicines)
{
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)> { (element.MedicineName, new WordTextProperties { Size = "24", Bold = true }) },
TextProperties = new WordTextProperties
{
Size = "24",
JustificationType = WordJustificationType.Both
}
});
foreach (var patient in element.Patients)
{
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)> {
(patient.Item1, new WordTextProperties { Size = "24"}),
(" " + patient.Item2, new WordTextProperties { Size = "24", }),
(" " + patient.Item3, new WordTextProperties { Size = "24", }), (", " + patient.Item4.ToShortDateString(), new WordTextProperties { Size = "24", }) },
TextProperties = new WordTextProperties
{
Size = "24",
JustificationType = WordJustificationType.Both
}
});
}
}
return SaveWord(info);
}
/// <summary>
/// Создание doc-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreateWord(WordInfo info);
/// <summary>
/// Создание абзаца с текстом
/// </summary>
/// <param name="paragraph"></param>
/// <returns></returns>
protected abstract void CreateParagraph(WordParagraph paragraph);
/// <summary>
/// Сохранение файла
/// </summary>
/// <param name="info"></param>
protected abstract MemoryStream SaveWord(WordInfo info);
}
}

View File

@ -0,0 +1,9 @@
namespace HospitalBusinessLogic.OfficePackage.HelperEnums
{
public enum ExcelStyleInfoType
{
Title,
Text,
TextWithBroder
}
}

View File

@ -0,0 +1,9 @@
namespace HospitalBusinessLogic.OfficePackage.HelperEnums
{
public enum PdfParagraphAlignmentType
{
Center,
Left,
Right
}
}

View File

@ -0,0 +1,8 @@
namespace HospitalBusinessLogic.OfficePackage.HelperEnums
{
public enum WordJustificationType
{
Center,
Both
}
}

View File

@ -0,0 +1,13 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class ExcelCellParameters
{
public string ColumnName { get; set; } = string.Empty;
public uint RowIndex { get; set; }
public string Text { get; set; } = string.Empty;
public string CellReference => $"{ColumnName}{RowIndex}";
public ExcelStyleInfoType StyleInfo { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using HospitalContracts.ViewModels;
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class ExcelInfo
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public List<string> Headers { get; set; } = new();
public List<ReportPatientsMedicinesViewModel> PatientsMedicines{ get; set; } = new();
}
}

View File

@ -0,0 +1,9 @@
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class ExcelMergeParameters
{
public string CellFromName { get; set; } = string.Empty;
public string CellToName { get; set; } = string.Empty;
public string Merge => $"{CellFromName}:{CellToName}";
}
}

View File

@ -0,0 +1,14 @@
using HospitalContracts.ViewModels;
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class PdfInfo
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public List<ReportPrescriptionProcedureViewModel> Prescriptions { get; set; } = new();
}
}

View File

@ -0,0 +1,11 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class PdfParagraph
{
public string Text { get; set; } = string.Empty;
public string Style { get; set; } = string.Empty;
public PdfParagraphAlignmentType ParagraphAlignment { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class PdfRowParameters
{
public List<string> Texts { get; set; } = new();
public string Style { get; set; } = string.Empty;
public PdfParagraphAlignmentType ParagraphAlignment { get; set; }
}
}

View File

@ -0,0 +1,11 @@
using HospitalContracts.ViewModels;
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class WordInfo
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public List<ReportPatientsMedicinesViewModel> PatientsMedicines { get; set; } = new();
}
}

View File

@ -0,0 +1,8 @@
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class WordParagraph
{
public List<(string, WordTextProperties)> Texts { get; set; } = new();
public WordTextProperties? TextProperties { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
namespace HospitalBusinessLogic.OfficePackage.HelperModels
{
public class WordTextProperties
{
public string Size { get; set; } = string.Empty;
public bool Bold { get; set; }
public WordJustificationType JustificationType { get; set; }
}
}

View File

@ -0,0 +1,359 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Office2013.Excel;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using HospitalBusinessLogic.OfficePackage.HelperEnums;
using HospitalBusinessLogic.OfficePackage.HelperModels;
namespace HospitalBusinessLogic.OfficePackage.Implements
{
public class SaveToExcel : AbstractSaveToExcel
{
private SpreadsheetDocument? _spreadsheetDocument;
private SharedStringTablePart? _shareStringPart;
private Worksheet? _worksheet;
private MemoryStream? stream;
/// <summary>
/// Настройка стилей для файла
/// </summary>
/// <param name="workbookpart"></param>
private static void CreateStyles(WorkbookPart workbookpart)
{
var sp = workbookpart.AddNewPart<WorkbookStylesPart>();
sp.Stylesheet = new Stylesheet();
var fonts = new Fonts() { Count = 2U, KnownFonts = true };
var fontUsual = new Font();
fontUsual.Append(new FontSize() { Val = 12D });
fontUsual.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color()
{ Theme = 1U });
fontUsual.Append(new FontName() { Val = "Times New Roman" });
fontUsual.Append(new FontFamilyNumbering() { Val = 2 });
fontUsual.Append(new FontScheme() { Val = FontSchemeValues.Minor });
var fontTitle = new Font();
fontTitle.Append(new Bold());
fontTitle.Append(new FontSize() { Val = 14D });
fontTitle.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color()
{ Theme = 1U });
fontTitle.Append(new FontName() { Val = "Times New Roman" });
fontTitle.Append(new FontFamilyNumbering() { Val = 2 });
fontTitle.Append(new FontScheme() { Val = FontSchemeValues.Minor });
fonts.Append(fontUsual);
fonts.Append(fontTitle);
var fills = new Fills() { Count = 2U };
var fill1 = new Fill();
fill1.Append(new PatternFill() { PatternType = PatternValues.None });
var fill2 = new Fill();
fill2.Append(new PatternFill()
{
PatternType = PatternValues.Gray125
});
fills.Append(fill1);
fills.Append(fill2);
var borders = new Borders() { Count = 2U };
var borderNoBorder = new Border();
borderNoBorder.Append(new LeftBorder());
borderNoBorder.Append(new RightBorder());
borderNoBorder.Append(new TopBorder());
borderNoBorder.Append(new BottomBorder());
borderNoBorder.Append(new DiagonalBorder());
var borderThin = new Border();
var leftBorder = new LeftBorder() { Style = BorderStyleValues.Thin };
leftBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color()
{ Indexed = 64U });
var rightBorder = new RightBorder()
{
Style = BorderStyleValues.Thin
};
rightBorder.Append(new
DocumentFormat.OpenXml.Office2010.Excel.Color()
{ Indexed = 64U });
var topBorder = new TopBorder() { Style = BorderStyleValues.Thin };
topBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color()
{ Indexed = 64U });
var bottomBorder = new BottomBorder()
{
Style =
BorderStyleValues.Thin
};
bottomBorder.Append(new
DocumentFormat.OpenXml.Office2010.Excel.Color()
{ Indexed = 64U });
borderThin.Append(leftBorder);
borderThin.Append(rightBorder);
borderThin.Append(topBorder);
borderThin.Append(bottomBorder);
borderThin.Append(new DiagonalBorder());
borders.Append(borderNoBorder);
borders.Append(borderThin);
var cellStyleFormats = new CellStyleFormats() { Count = 1U };
var cellFormatStyle = new CellFormat()
{
NumberFormatId = 0U,
FontId
= 0U,
FillId = 0U,
BorderId = 0U
};
cellStyleFormats.Append(cellFormatStyle);
var cellFormats = new CellFormats() { Count = 3U };
var cellFormatFont = new CellFormat()
{
NumberFormatId = 0U,
FontId =
0U,
FillId = 0U,
BorderId = 0U,
FormatId = 0U,
ApplyFont = true
};
var cellFormatFontAndBorder = new CellFormat()
{
NumberFormatId = 0U,
FontId = 0U,
FillId = 0U,
BorderId = 1U,
FormatId = 0U,
ApplyFont = true,
ApplyBorder = true
};
var cellFormatTitle = new CellFormat()
{
NumberFormatId = 0U,
FontId
= 1U,
FillId = 0U,
BorderId = 0U,
FormatId = 0U,
Alignment = new Alignment()
{
Vertical = VerticalAlignmentValues.Center,
WrapText = true,
Horizontal =
HorizontalAlignmentValues.Center
},
ApplyFont = true
};
cellFormats.Append(cellFormatFont);
cellFormats.Append(cellFormatFontAndBorder);
cellFormats.Append(cellFormatTitle);
var cellStyles = new CellStyles() { Count = 1U };
cellStyles.Append(new CellStyle()
{
Name = "Normal",
FormatId = 0U,
BuiltinId = 0U
});
var differentialFormats = new
DocumentFormat.OpenXml.Office2013.Excel.DifferentialFormats()
{ Count = 0U };
var tableStyles = new TableStyles()
{
Count = 0U,
DefaultTableStyle =
"TableStyleMedium2",
DefaultPivotStyle = "PivotStyleLight16"
};
var stylesheetExtensionList = new StylesheetExtensionList();
var stylesheetExtension1 = new StylesheetExtension()
{
Uri =
"{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}"
};
stylesheetExtension1.AddNamespaceDeclaration("x14",
"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
stylesheetExtension1.Append(new SlicerStyles()
{
DefaultSlicerStyle =
"SlicerStyleLight1"
});
var stylesheetExtension2 = new StylesheetExtension()
{
Uri =
"{9260A510-F301-46a8-8635-F512D64BE5F5}"
};
stylesheetExtension2.AddNamespaceDeclaration("x15",
"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main");
stylesheetExtension2.Append(new TimelineStyles()
{
DefaultTimelineStyle = "TimeSlicerStyleLight1"
});
stylesheetExtensionList.Append(stylesheetExtension1);
stylesheetExtensionList.Append(stylesheetExtension2);
sp.Stylesheet.Append(fonts);
sp.Stylesheet.Append(fills);
sp.Stylesheet.Append(borders);
sp.Stylesheet.Append(cellStyleFormats);
sp.Stylesheet.Append(cellFormats);
sp.Stylesheet.Append(cellStyles);
sp.Stylesheet.Append(differentialFormats);
sp.Stylesheet.Append(tableStyles);
sp.Stylesheet.Append(stylesheetExtensionList);
}
/// <summary>
/// Получение номера стиля из типа
/// </summary>
/// <param name="styleInfo"></param>
/// <returns></returns>
private static uint GetStyleValue(ExcelStyleInfoType styleInfo)
{
return styleInfo switch
{
ExcelStyleInfoType.Title => 2U,
ExcelStyleInfoType.TextWithBroder => 1U,
ExcelStyleInfoType.Text => 0U,
_ => 0U,
};
}
protected override void CreateExcel(ExcelInfo info)
{
stream = new MemoryStream();
_spreadsheetDocument = SpreadsheetDocument.Create(stream,
SpreadsheetDocumentType.Workbook);
// Создаем книгу (в ней хранятся листы)
var workbookpart = _spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
CreateStyles(workbookpart);
// Получаем/создаем хранилище текстов для книги
_shareStringPart =
_spreadsheetDocument.WorkbookPart!.GetPartsOfType<SharedStringTablePart>().Any()
?
_spreadsheetDocument.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First()
:
_spreadsheetDocument.WorkbookPart.AddNewPart<SharedStringTablePart>();
// Создаем SharedStringTable, если его нет
if (_shareStringPart.SharedStringTable == null)
{
_shareStringPart.SharedStringTable = new SharedStringTable();
}
// Создаем лист в книгу
var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Добавляем лист в книгу
var sheets =
_spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
var sheet = new Sheet()
{
Id =
_spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Лист"
};
sheets.Append(sheet);
_worksheet = worksheetPart.Worksheet;
}
protected override void InsertCellInWorksheet(ExcelCellParameters excelParams)
{
if (_worksheet == null || _shareStringPart == null)
{
return;
}
var sheetData = _worksheet.GetFirstChild<SheetData>();
if (sheetData == null)
{
return;
}
// Ищем строку, либо добавляем ее
Row row;
if (sheetData.Elements<Row>().Where(r => r.RowIndex! ==
excelParams.RowIndex).Any())
{
row = sheetData.Elements<Row>().Where(r => r.RowIndex! ==
excelParams.RowIndex).First();
}
else
{
row = new Row() { RowIndex = excelParams.RowIndex };
sheetData.Append(row);
}
// Ищем нужную ячейку
Cell cell;
if (row.Elements<Cell>().Where(c => c.CellReference!.Value ==
excelParams.CellReference).Any())
{
cell = row.Elements<Cell>().Where(c => c.CellReference!.Value ==
excelParams.CellReference).First();
}
else
{
// Все ячейки должны быть последовательно друг за другом расположены
// нужно определить, после какой вставлять
Cell? refCell = null;
foreach (Cell rowCell in row.Elements<Cell>())
{
if (string.Compare(rowCell.CellReference!.Value,
excelParams.CellReference, true) > 0)
{
refCell = rowCell;
break;
}
}
var newCell = new Cell()
{
CellReference =
excelParams.CellReference
};
row.InsertBefore(newCell, refCell);
cell = newCell;
}
// вставляем новый текст
_shareStringPart.SharedStringTable.AppendChild(new
SharedStringItem(new Text(excelParams.Text)));
_shareStringPart.SharedStringTable.Save();
cell.CellValue = new
CellValue((_shareStringPart.SharedStringTable.Elements<SharedStringItem>().Count(
) - 1).ToString());
cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
cell.StyleIndex = GetStyleValue(excelParams.StyleInfo);
}
protected override void MergeCells(ExcelMergeParameters excelParams)
{
if (_worksheet == null)
{
return;
}
MergeCells mergeCells;
if (_worksheet.Elements<MergeCells>().Any())
{
mergeCells = _worksheet.Elements<MergeCells>().First();
}
else
{
mergeCells = new MergeCells();
if (_worksheet.Elements<CustomSheetView>().Any())
{
_worksheet.InsertAfter(mergeCells,
_worksheet.Elements<CustomSheetView>().First());
}
else
{
_worksheet.InsertAfter(mergeCells,
_worksheet.Elements<SheetData>().First());
}
}
var mergeCell = new MergeCell()
{
Reference = new StringValue(excelParams.Merge)
};
mergeCells.Append(mergeCell);
}
protected override MemoryStream SaveExcel(ExcelInfo info)
{
if (_spreadsheetDocument == null)
{
throw new InvalidOperationException("Document is not created");
}
_spreadsheetDocument.WorkbookPart!.Workbook.Save();
_spreadsheetDocument.Dispose();
if (stream == null)
throw new InvalidOperationException("Stream is empty");
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
}
}

View File

@ -0,0 +1,103 @@
using HospitalBusinessLogic.OfficePackage.HelperEnums;
using HospitalBusinessLogic.OfficePackage.HelperModels;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;
namespace HospitalBusinessLogic.OfficePackage.Implements
{
public class SaveToPdf : AbstractSaveToPdf
{
private Document? _document;
private Section? _section;
private Table? _table;
private MemoryStream _stream;
private static ParagraphAlignment GetParagraphAlignment(PdfParagraphAlignmentType type)
{
return type switch
{
PdfParagraphAlignmentType.Center => ParagraphAlignment.Center,
PdfParagraphAlignmentType.Left => ParagraphAlignment.Left,
PdfParagraphAlignmentType.Right => ParagraphAlignment.Right,
_ => ParagraphAlignment.Justify,
};
}
/// <summary>
/// Создание стилей для документа
/// </summary>
/// <param name="document"></param>
private static void DefineStyles(Document document)
{
var style = document.Styles["Normal"];
style.Font.Name = "Times New Roman";
style.Font.Size = 14;
style = document.Styles.AddStyle("NormalTitle", "Normal");
style.Font.Bold = true;
}
protected override void CreatePdf(PdfInfo info)
{
_stream = new MemoryStream();
_document = new Document();
DefineStyles(_document);
_section = _document.AddSection();
}
protected override void CreateParagraph(PdfParagraph pdfParagraph)
{
if (_section == null)
{
return;
}
var paragraph = _section.AddParagraph(pdfParagraph.Text);
paragraph.Format.SpaceAfter = "1cm";
paragraph.Format.Alignment =
GetParagraphAlignment(pdfParagraph.ParagraphAlignment);
paragraph.Style = pdfParagraph.Style;
}
protected override void CreateTable(List<string> columns)
{
if (_document == null)
{
return;
}
_table = _document.LastSection.AddTable();
foreach (var elem in columns)
{
_table.AddColumn(elem);
}
}
protected override void CreateRow(PdfRowParameters rowParameters)
{
if (_table == null)
{
return;
}
var row = _table.AddRow();
for (int i = 0; i < rowParameters.Texts.Count; ++i)
{
row.Cells[i].AddParagraph(rowParameters.Texts[i]);
if (!string.IsNullOrEmpty(rowParameters.Style))
{
row.Cells[i].Style = rowParameters.Style;
}
Unit borderWidth = 0.5;
row.Cells[i].Borders.Left.Width = borderWidth;
row.Cells[i].Borders.Right.Width = borderWidth;
row.Cells[i].Borders.Top.Width = borderWidth;
row.Cells[i].Borders.Bottom.Width = borderWidth;
row.Cells[i].Format.Alignment =
GetParagraphAlignment(rowParameters.ParagraphAlignment);
row.Cells[i].VerticalAlignment = VerticalAlignment.Center;
}
}
protected override MemoryStream SavePdf(PdfInfo info)
{
var renderer = new PdfDocumentRenderer(true)
{
Document = _document
};
renderer.RenderDocument();
renderer.PdfDocument.Save(_stream);
return _stream;
}
}
}

View File

@ -0,0 +1,132 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using HospitalBusinessLogic.OfficePackage.HelperEnums;
using HospitalBusinessLogic.OfficePackage.HelperModels;
namespace HospitalBusinessLogic.OfficePackage.Implements
{
public class SaveToWord : AbstractSaveToWord
{
private WordprocessingDocument? _wordDocument;
private Body? _docBody;
private MemoryStream? stream;
/// <summary>
/// Получение типа выравнивания
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static JustificationValues GetJustificationValues(WordJustificationType type)
{
return type switch
{
WordJustificationType.Both => JustificationValues.Both,
WordJustificationType.Center => JustificationValues.Center,
_ => JustificationValues.Left,
};
}
/// <summary>
/// Настройки страницы
/// </summary>
/// <returns></returns>
private static SectionProperties CreateSectionProperties()
{
var properties = new SectionProperties();
var pageSize = new PageSize
{
Orient = PageOrientationValues.Portrait
};
properties.AppendChild(pageSize);
return properties;
}
/// <summary>
/// Задание форматирования для абзаца
/// </summary>
/// <param name="paragraphProperties"></param>
/// <returns></returns>
private static ParagraphProperties?
CreateParagraphProperties(WordTextProperties? paragraphProperties)
{
if (paragraphProperties == null)
{
return null;
}
var properties = new ParagraphProperties();
properties.AppendChild(new Justification()
{
Val =
GetJustificationValues(paragraphProperties.JustificationType)
});
properties.AppendChild(new SpacingBetweenLines
{
LineRule = LineSpacingRuleValues.Auto
});
properties.AppendChild(new Indentation());
var paragraphMarkRunProperties = new ParagraphMarkRunProperties();
if (!string.IsNullOrEmpty(paragraphProperties.Size))
{
paragraphMarkRunProperties.AppendChild(new FontSize
{
Val =
paragraphProperties.Size
});
}
properties.AppendChild(paragraphMarkRunProperties);
return properties;
}
protected override void CreateWord(WordInfo info)
{
stream = new MemoryStream();
_wordDocument = WordprocessingDocument.Create(stream,
WordprocessingDocumentType.Document);
MainDocumentPart mainPart = _wordDocument.AddMainDocumentPart();
mainPart.Document = new Document();
_docBody = mainPart.Document.AppendChild(new Body());
}
protected override void CreateParagraph(WordParagraph paragraph)
{
if (_docBody == null || paragraph == null)
{
return;
}
var docParagraph = new Paragraph();
docParagraph.AppendChild(CreateParagraphProperties(paragraph.TextProperties));
foreach (var run in paragraph.Texts)
{
var docRun = new Run();
var properties = new RunProperties();
properties.AppendChild(new FontSize { Val = run.Item2.Size });
if (run.Item2.Bold)
{
properties.AppendChild(new Bold());
}
docRun.AppendChild(properties);
docRun.AppendChild(new Text
{
Text = run.Item1,
Space =
SpaceProcessingModeValues.Preserve
});
docParagraph.AppendChild(docRun);
}
_docBody.AppendChild(docParagraph);
}
protected override MemoryStream SaveWord(WordInfo info)
{
if (_docBody == null || _wordDocument == null)
{
throw new InvalidOperationException("Document is not created");
}
_docBody.AppendChild(CreateSectionProperties());
_wordDocument.MainDocumentPart!.Document.Save();
_wordDocument.Dispose();
if (stream == null)
throw new InvalidOperationException("Stream is empty");
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
}
}

View File

@ -1,9 +1,14 @@
using HospitalContracts.BindingModels; using HospitalBusinessLogic.MailWorker;
using HospitalBusinessLogic.OfficePackage;
using HospitalBusinessLogic.OfficePackage.HelperModels;
using HospitalBusinessLogic.OfficePackage.Implements;
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts; using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.SearchModels; 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
{ {
@ -16,8 +21,16 @@ namespace HospitalBusinessLogic
private readonly IProcedureStorage _procedureStorage; private readonly IProcedureStorage _procedureStorage;
private readonly IRecipeStorage _recipeStorage; private readonly IRecipeStorage _recipeStorage;
private readonly AbstractSaveToExcel _saveToExcel;
private readonly AbstractSaveToWord _saveToWord;
private readonly AbstractSaveToPdf _saveToPdf;
private readonly AbstractMailWorker _mailWorker;
public ReportLogic(IMedicineStorage medicineStorage, IPatientStorage patientStorage, public ReportLogic(IMedicineStorage medicineStorage, IPatientStorage patientStorage,
IPrescriptionStorage prescriptionStorage, ITreatmentStorage treatmentStorage, IProcedureStorage procedureStorage, IRecipeStorage recipeStorage) IPrescriptionStorage prescriptionStorage, ITreatmentStorage treatmentStorage,
IProcedureStorage procedureStorage, IRecipeStorage recipeStorage,
AbstractSaveToExcel abstractSaveToExcel, AbstractSaveToWord abstractSaveToWord, AbstractSaveToPdf abstractSaveToPdf, AbstractMailWorker abstractMailWorker)
{ {
_medicineStorage = medicineStorage; _medicineStorage = medicineStorage;
_patientStorage = patientStorage; _patientStorage = patientStorage;
@ -25,6 +38,12 @@ namespace HospitalBusinessLogic
_treatmentStorage = treatmentStorage; _treatmentStorage = treatmentStorage;
_procedureStorage = procedureStorage; _procedureStorage = procedureStorage;
_recipeStorage = recipeStorage; _recipeStorage = recipeStorage;
_saveToExcel = abstractSaveToExcel;
_saveToWord = abstractSaveToWord;
_saveToPdf = abstractSaveToPdf;
_mailWorker = abstractMailWorker;
} }
public List<ReportPatientsMedicinesViewModel> GetMedicinePatients(ReportBindingModel model) public List<ReportPatientsMedicinesViewModel> GetMedicinePatients(ReportBindingModel model)
@ -89,14 +108,55 @@ namespace HospitalBusinessLogic
return list; return list;
} }
public void SavePatientsToExcelFile(ReportBindingModel model) public MemoryStream SavePatientsToExcelFile(ReportBindingModel model)
{ {
throw new NotImplementedException(); return _saveToExcel.CreateReport(new ExcelInfo
{
FileName = model.FileName,
Title = "Список пациентов",
PatientsMedicines = GetMedicinePatients(model),
Headers = {"№","Фамилия", "Имя", "Отчество", "Дата рождения"}
});
} }
public void SavePatientsToWordFile(ReportBindingModel model) public MemoryStream SavePatientsToWordFile(ReportBindingModel model)
{ {
throw new NotImplementedException(); return _saveToWord.CreateDoc(new WordInfo
{
FileName = model.FileName,
Title = "Список пациентов",
PatientsMedicines = GetMedicinePatients(model)
});
}
public MemoryStream SavePrescriptionsToPdfFile(ReportBindingModel model)
{
return _saveToPdf.CreateDoc(new PdfInfo
{
FileName = model.FileName,
Title = "Список поступлений и процедур",
Prescriptions = GetPrescriptionProcedures(model),
DateFrom = model.DateFrom!.Value,
DateTo = model.DateTo!.Value
});
}
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 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

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

View File

@ -6,6 +6,10 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\HospitalDataModels\HospitalDataModels.csproj" /> <ProjectReference Include="..\HospitalDataModels\HospitalDataModels.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -5,5 +5,7 @@
public int? Id { get; set; } public int? Id { get; set; }
public string? Name { get; set; } public string? Name { get; set; }
public int? ApothecaryId { get; set; } public int? ApothecaryId { get; set; }
public int[]? Ids { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using HospitalDataModels.Models; using HospitalDataModels.Models;
using Newtonsoft.Json;
using System.ComponentModel; using System.ComponentModel;
namespace HospitalContracts.ViewModels namespace HospitalContracts.ViewModels
@ -10,5 +11,13 @@ namespace HospitalContracts.ViewModels
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public Dictionary<int, IMedicineModel> ProcedureMedicines { get; set; } = new(); public Dictionary<int, IMedicineModel> ProcedureMedicines { get; set; } = new();
public ProcedureViewModel() { }
[JsonConstructor]
public ProcedureViewModel(Dictionary<int, MedicineViewModel> ProcedureMedicines)
{
this.ProcedureMedicines = ProcedureMedicines.ToDictionary(x => x.Key, x => x.Value as IMedicineModel);
}
} }
} }

View File

@ -1,4 +1,5 @@
using HospitalDataModels.Models; using HospitalDataModels.Models;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@ -21,5 +22,13 @@ namespace HospitalContracts.ViewModels
public Dictionary<int, IMedicineModel> RecipeMedicines { get; set; } = new(); public Dictionary<int, IMedicineModel> RecipeMedicines { get; set; } = new();
public Dictionary<int, ITreatmentModel> RecipeTreatments { get; set; } = new(); public Dictionary<int, ITreatmentModel> RecipeTreatments { get; set; } = new();
public RecipeViewModel() { }
[JsonConstructor]
public RecipeViewModel(Dictionary<int, MedicineViewModel> RecipeMedicines, Dictionary<int, TreatmentViewModel> RecipeTreatments)
{
this.RecipeMedicines = RecipeMedicines.ToDictionary(x => x.Key, x => x.Value as IMedicineModel);
this.RecipeTreatments = RecipeTreatments.ToDictionary(x => x.Key, x => x.Value as ITreatmentModel);
}
} }
} }

View File

@ -1,4 +1,5 @@
using HospitalDataModels.Models; using HospitalDataModels.Models;
using Newtonsoft.Json;
using System.ComponentModel; using System.ComponentModel;
namespace HospitalContracts.ViewModels namespace HospitalContracts.ViewModels
@ -10,5 +11,13 @@ namespace HospitalContracts.ViewModels
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public Dictionary<int, IProcedureModel> TreatmentProcedures { get; set; } = new(); public Dictionary<int, IProcedureModel> TreatmentProcedures { get; set; } = new();
public TreatmentViewModel() { }
[JsonConstructor]
public TreatmentViewModel(Dictionary<int, ProcedureViewModel> TreatmentProcedures)
{
this.TreatmentProcedures = TreatmentProcedures.ToDictionary(x => x.Key, x => x.Value as IProcedureModel);
}
} }
} }

View File

@ -25,13 +25,13 @@ namespace HospitalDatabaseImplement.Implements
public List<MedicineViewModel> GetFilteredList(MedicineSearchModel model) public List<MedicineViewModel> GetFilteredList(MedicineSearchModel model)
{ {
if (!model.ApothecaryId.HasValue) if (!model.ApothecaryId.HasValue && model.Ids == null)
{ {
return new(); return new();
} }
using var context = new HospitalDatabase(); using var context = new HospitalDatabase();
return context.Medicines.Include(x => x.Apothecary) return context.Medicines.Include(x => x.Apothecary)
.Where(x => x.ApothecaryId == model.ApothecaryId) .Where(x => x.ApothecaryId == model.ApothecaryId || (model.Ids != null && model.Ids.Contains(x.Id)))
.Select(x => x.GetViewModel) .Select(x => x.GetViewModel)
.ToList(); .ToList();
} }

View File

@ -5,9 +5,9 @@ namespace HospitalDatabaseImplement
{ {
public class LoaderFromXML public class LoaderFromXML
{ {
private static readonly string PatientFileName = "XMLData\\Patient.xml"; private static readonly string PatientFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XMLData\\Patient.xml");
private static readonly string TreatmentFileName = "XMLData\\Treatment.xml"; private static readonly string TreatmentFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XMLData\\Treatment.xml");
private static readonly string ProcedureFileName = "XMLData\\Procedure.xml"; private static readonly string ProcedureFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XMLData\\Procedure.xml");
private static List<T>? LoadData<T>(string filename, string xmlNodeName, Func<XElement, T> selectFunction) private static List<T>? LoadData<T>(string filename, string xmlNodeName, Func<XElement, T> selectFunction)
{ {

View File

@ -40,7 +40,7 @@ namespace HospitalDatabaseImplement.Models
public void Update(PrescriptionBindingModel model) public void Update(PrescriptionBindingModel model)
{ {
Date = model.Date; Number = model.Number;
} }
public PrescriptionViewModel GetViewModel => new() public PrescriptionViewModel GetViewModel => new()
{ {

View File

@ -91,15 +91,19 @@ namespace HospitalDatabaseImplement.Models
context.SaveChanges(); context.SaveChanges();
} }
var procedure = context.Procedures.First(x => x.Id == Id); var procedure = context.Procedures.First(x => x.Id == Id);
var existingMedicineIds = procedureMedicines?.Select(x => x.MedicineId).ToList();
foreach (var pm in model.ProcedureMedicines) foreach (var pm in model.ProcedureMedicines)
{
if (existingMedicineIds != null && !existingMedicineIds.Contains(pm.Key))
{ {
context.ProcedureMedicines.Add(new ProcedureMedicine context.ProcedureMedicines.Add(new ProcedureMedicine
{ {
Procedure = procedure, Procedure = procedure,
Medicine = context.Medicines.First(x => x.Id == pm.Key), Medicine = context.Medicines.First(x => x.Id == pm.Key),
}); });
context.SaveChanges();
} }
}
context.SaveChanges();
_procedureMedicines = null; _procedureMedicines = null;
} }
} }

View File

@ -18,7 +18,7 @@ namespace HospitalDatabaseImplement.Models
[Required] [Required]
public int ApothecaryId { get; private set; } public int ApothecaryId { get; private set; }
public virtual Apothecary Apothecary { get; set; } public virtual Apothecary? Apothecary { get; set; }
private Dictionary<int, IMedicineModel>? _recipeMedicines = null; private Dictionary<int, IMedicineModel>? _recipeMedicines = null;
@ -82,7 +82,6 @@ namespace HospitalDatabaseImplement.Models
public void Update(RecipeBindingModel model) public void Update(RecipeBindingModel model)
{ {
Name = model.Name; Name = model.Name;
Date = model.Date;
} }
public RecipeViewModel GetViewModel => new() public RecipeViewModel GetViewModel => new()
{ {
@ -90,7 +89,9 @@ namespace HospitalDatabaseImplement.Models
Name = Name, Name = Name,
Date = Date, Date = Date,
ApothecaryId = ApothecaryId, ApothecaryId = ApothecaryId,
ApothecaryLogin = Apothecary.Login ApothecaryLogin = Apothecary?.Login,
RecipeMedicines = RecipeMedicines,
RecipeTreatments = RecipeTreatments
}; };
public void UpdateMedicines(HospitalDatabase context, RecipeBindingModel model) public void UpdateMedicines(HospitalDatabase context, RecipeBindingModel model)
@ -103,15 +104,20 @@ namespace HospitalDatabaseImplement.Models
context.SaveChanges(); context.SaveChanges();
} }
var recipe = context.Recipes.First(x => x.Id == Id); var recipe = context.Recipes.First(x => x.Id == Id);
var existingMedicineIds = recipeMedicines?.Select(x => x.MedicineId).ToList();
foreach (var rec in model.RecipeMedicines) foreach (var rec in model.RecipeMedicines)
{
if (existingMedicineIds != null && !existingMedicineIds.Contains(rec.Key))
{ {
context.RecipeMedicines.Add(new RecipeMedicine context.RecipeMedicines.Add(new RecipeMedicine
{ {
Recipe = recipe, Recipe = recipe,
Medicine = context.Medicines.First(x => x.Id == rec.Key), Medicine = context.Medicines.First(x => x.Id == rec.Key),
}); });
context.SaveChanges();
} }
}
context.SaveChanges();
_recipeMedicines = null; _recipeMedicines = null;
} }
@ -125,13 +131,18 @@ namespace HospitalDatabaseImplement.Models
context.SaveChanges(); context.SaveChanges();
} }
var recipe = context.Recipes.First(x => x.Id == Id); var recipe = context.Recipes.First(x => x.Id == Id);
var existingTreatmentsIds = recipeTreatments?.Select(x => x.TreatmentId).ToList();
foreach (var rec in model.RecipeTreatments) foreach (var rec in model.RecipeTreatments)
{
if (existingTreatmentsIds != null && !existingTreatmentsIds.Contains(rec.Key))
{ {
context.RecipeTreatments.Add(new RecipeTreatment context.RecipeTreatments.Add(new RecipeTreatment
{ {
Recipe = recipe, Recipe = recipe,
Treatment = context.Treatments.First(x => x.Id == rec.Key), Treatment = context.Treatments.First(x => x.Id == rec.Key),
}); });
}
context.SaveChanges(); context.SaveChanges();
} }
_recipeTreatments = null; _recipeTreatments = null;

View File

@ -1,17 +1,52 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Patients> <Patients>
<Patient Id="1"> <Patient Id="1">
<Surname>Фамилия 1</Surname> <Surname>Гейтс</Surname>
<Name>Имя 1</Name> <Name>Билл</Name>
<Patronymic>Отчество 1</Patronymic> <Patronymic>Иванович</Patronymic>
<BirthDate>10.01.2021</BirthDate> <BirthDate>28.10.1955</BirthDate>
<TreatmentId>1</TreatmentId> <TreatmentId>1</TreatmentId>
</Patient> </Patient>
<Patient Id="2"> <Patient Id="2">
<Surname>Фамилия 2</Surname> <Surname>Джобс</Surname>
<Name>Имя 2</Name> <Name>Стив</Name>
<Patronymic>Отчество 1</Patronymic> <Patronymic>Петрович</Patronymic>
<BirthDate>15.04.2001</BirthDate> <BirthDate>24.02.1955</BirthDate>
<TreatmentId>1</TreatmentId> <TreatmentId>1</TreatmentId>
</Patient> </Patient>
<Patient Id="3">
<Surname>Безос</Surname>
<Name>Джефф</Name>
<Patronymic>Степанович</Patronymic>
<BirthDate>12.01.1964</BirthDate>
<TreatmentId>2</TreatmentId>
</Patient>
<Patient Id="4">
<Surname>Монро</Surname>
<Name>Мэрилин</Name>
<Patronymic></Patronymic>
<BirthDate>01.06.1926</BirthDate>
<TreatmentId>2</TreatmentId>
</Patient>
<Patient Id="5">
<Surname>Шанель</Surname>
<Name>Коко</Name>
<Patronymic></Patronymic>
<BirthDate>20.01.1999</BirthDate>
<TreatmentId>3</TreatmentId>
</Patient>
<Patient Id="6">
<Surname>Кук</Surname>
<Name>Тим</Name>
<Patronymic></Patronymic>
<BirthDate>12.09.1945</BirthDate>
<TreatmentId>4</TreatmentId>
</Patient>
<Patient Id="7">
<Surname>Маск</Surname>
<Name>Илон</Name>
<Patronymic></Patronymic>
<BirthDate>23.06.1975</BirthDate>
<TreatmentId>5</TreatmentId>
</Patient>
</Patients> </Patients>

View File

@ -0,0 +1,67 @@
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.SearchModels;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace HospitalRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class ApothecaryController : ControllerBase
{
private readonly ILogger _logger;
private readonly IApothecaryLogic _logic;
public ApothecaryController(IApothecaryLogic logic, ILogger<ApothecaryController> logger)
{
_logger = logger;
_logic = logic;
}
[HttpGet]
public ApothecaryViewModel? Login(string login, string password)
{
try
{
return _logic.ReadElement(new ApothecarySearchModel
{
Login = login,
Password = password
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка входа в систему");
throw;
}
}
[HttpPost]
public void Register(ApothecaryBindingModel model)
{
try
{
_logic.Create(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка регистрации");
throw;
}
}
[HttpPost]
public void UpdateData(ApothecaryBindingModel model)
{
try
{
_logic.Update(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка обновления данных");
throw;
}
}
}
}

View File

@ -0,0 +1,124 @@
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.SearchModels;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace HospitalRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class MedicineController : ControllerBase
{
private readonly ILogger _logger;
private readonly IMedicineLogic _logic;
private readonly IProcedureLogic _logicP;
public MedicineController(IMedicineLogic logic, ILogger<MedicineController> logger, IProcedureLogic logicP)
{
_logger = logger;
_logic = logic;
_logicP = logicP;
}
[HttpGet]
public List<MedicineViewModel>? GetMedicines(int? apothecaryid)
{
try
{
if (apothecaryid == null)
{
return _logic.ReadList(null);
}
return _logic.ReadList(new MedicineSearchModel
{
ApothecaryId = apothecaryid
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка лекарств");
throw;
}
}
[HttpGet]
public MedicineViewModel? GetMedicine(int id)
{
try
{
return _logic.ReadElement(new MedicineSearchModel
{
Id = id
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения поступления ");
throw;
}
}
[HttpPost]
public void Create(MedicineBindingModel model)
{
try
{
_logic.Create(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка создания лекарства");
throw;
}
}
[HttpPost]
public void Update(MedicineBindingModel model)
{
try
{
_logic.Update(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка обновления лекарства");
throw;
}
}
[HttpPost]
public void Delete(MedicineBindingModel model)
{
try
{
_logic.Delete(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка удаления лекарства");
throw;
}
}
[HttpPost]
public void AddProcedureMedicine(Tuple<MedicineBindingModel, ProcedureBindingModel> addInfo)
{
var medicineId = addInfo.Item1.Id;
var procedureId = addInfo.Item2.Id;
var procedure = _logicP.ReadElement(new ProcedureSearchModel { Id = procedureId });
var medicine = _logic.ReadElement(new MedicineSearchModel { Id = medicineId });
if (procedure != null && medicine != null)
{
var procedureMedicines = procedure.ProcedureMedicines;
procedureMedicines[medicineId] = medicine;
_logicP.Update(new ProcedureBindingModel {
Id = procedureId,
Name = procedure.Name,
ProcedureMedicines = procedureMedicines
});
}
}
}
}

View File

@ -0,0 +1,99 @@
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.SearchModels;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace HospitalRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class PrescriptionController : ControllerBase
{
private readonly ILogger _logger;
private readonly IPrescriptionLogic _logic;
public PrescriptionController(IPrescriptionLogic logic, ILogger<PrescriptionController> logger)
{
_logger = logger;
_logic = logic;
}
[HttpGet]
public List<PrescriptionViewModel>? GetPrescriptions(int apothecaryid)
{
try
{
return _logic.ReadList(new PrescriptionSearchModel
{
ApothecaryId = apothecaryid
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка поступлений аптекаря id ={ Id}", apothecaryid);
throw;
}
}
[HttpGet]
public PrescriptionViewModel? GetPrescription(int id)
{
try
{
return _logic.ReadElement(new PrescriptionSearchModel
{
Id = id
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения поступления ");
throw;
}
}
[HttpPost]
public void Create(PrescriptionBindingModel model)
{
try
{
_logic.Create(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка создания поступления");
throw;
}
}
[HttpPost]
public void Update(PrescriptionBindingModel model)
{
try
{
_logic.Update(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка обновления поступления");
throw;
}
}
[HttpPost]
public void Delete(PrescriptionBindingModel model)
{
try
{
_logic.Delete(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка удаления поступления");
throw;
}
}
}
}

View File

@ -0,0 +1,38 @@
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.SearchModels;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace HospitalRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class ProcedureController : ControllerBase
{
private readonly ILogger _logger;
private readonly IProcedureLogic _logic;
public ProcedureController(ILogger<ProcedureController> logger, IProcedureLogic logic)
{
_logger = logger;
_logic = logic;
}
[HttpGet]
public List<ProcedureViewModel>? GetProcedures()
{
try
{
return _logic.ReadList(null);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка процедур");
throw;
}
}
}
}

View File

@ -0,0 +1,113 @@
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.SearchModels;
using HospitalContracts.ViewModels;
using HospitalDataModels.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace HospitalRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class RecipeController : ControllerBase
{
private readonly ILogger _logger;
private readonly IRecipeLogic _logic;
private readonly IMedicineLogic _logicM;
public RecipeController(IRecipeLogic logic, ILogger<RecipeController> logger, IMedicineLogic logicM)
{
_logger = logger;
_logic = logic;
_logicM = logicM;
}
[HttpGet]
public List<RecipeViewModel>? GetRecipes(int apothecaryid)
{
try
{
return _logic.ReadList(new RecipeSearchModel
{
ApothecaryId = apothecaryid
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка рецептов аптекаря id ={ Id}", apothecaryid);
throw;
}
}
[HttpGet]
public RecipeViewModel? GetRecipe(int id)
{
try
{
return _logic.ReadElement(new RecipeSearchModel
{
Id = id
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения рецепта ");
throw;
}
}
[HttpPost]
public void Create(RecipeBindingModel model)
{
try
{
_logic.Create(GetModelWithMedicines(model));
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка создания рецепта");
throw;
}
}
[HttpPost]
public void Update(RecipeBindingModel model)
{
try
{
_logic.Update(GetModelWithMedicines(model));
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка обновления рецепта");
throw;
}
}
[HttpPost]
public void Delete(RecipeBindingModel model)
{
try
{
_logic.Delete(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка удаления рецепта");
throw;
}
}
private RecipeBindingModel GetModelWithMedicines(RecipeBindingModel model)
{
var medicines = _logicM.ReadList(new MedicineSearchModel { Ids = model.RecipeMedicines.Keys.ToArray() });
if (medicines != null)
{
model.RecipeMedicines = medicines.Where(m => model.RecipeMedicines.Keys.Contains(m.Id))
.ToDictionary(m => m.Id, m => m as IMedicineModel);
}
return model;
}
}
}

View File

@ -0,0 +1,86 @@
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.IO;
namespace HospitalRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
[ApiExplorerSettings(IgnoreApi = true)]
public class ReportController : ControllerBase
{
private readonly ILogger _logger;
private readonly IReportLogic _logic;
public ReportController(ILogger<ReportController> logger, IReportLogic logic)
{
_logger = logger;
_logic = logic;
}
[HttpPost]
public ActionResult GetReportPatientsToExcelFile(ReportBindingModel model)
{
try
{
var stream = _logic.SavePatientsToExcelFile(model);
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", model.FileName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка пациентов");
throw;
}
}
[HttpPost]
public ActionResult GetReportPatientsToWordFile(ReportBindingModel model)
{
try
{
var stream = _logic.SavePatientsToWordFile(model);
return File(stream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", model.FileName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка пациентов");
throw;
}
}
[HttpGet]
public List<ReportPrescriptionProcedureViewModel> GetReportPrescriptions(DateTime startDate, DateTime endDate)
{
try
{
return _logic.GetPrescriptionProcedures(new ReportBindingModel { DateFrom = startDate, DateTo = endDate});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка пациентов");
throw;
}
}
[HttpPost]
public void SendReportPrescriptionsPdf(ReportBindingModel model)
{
try
{
_logic.SendMailWithReportAttachments(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка при отправке отчета на почту");
throw;
}
}
}
}

View File

@ -0,0 +1,34 @@
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.SearchModels;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace HospitalRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class TreatmentController : ControllerBase
{
private readonly ITreatmentLogic _logic;
public TreatmentController(ITreatmentLogic logic)
{
_logic = logic;
}
[HttpGet]
public List<TreatmentViewModel>? GetTreatments()
{
try
{
return _logic.ReadList(null);
}
catch (Exception ex)
{
throw;
}
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.16" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HospitalBusinessLogic\HospitalBusinessLogic.csproj" />
<ProjectReference Include="..\HospitalDatabaseImplement\HospitalDatabaseImplement.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,85 @@
using HospitalBusinessLogic;
using HospitalBusinessLogic.MailWorker;
using HospitalBusinessLogic.OfficePackage;
using HospitalBusinessLogic.OfficePackage.Implements;
using HospitalContracts.BindingModels;
using HospitalContracts.BusinessLogicContracts;
using HospitalContracts.StorageContracts;
using HospitalDatabaseImplement;
using HospitalDatabaseImplement.Implements;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddTransient<IApothecaryStorage, ApothecaryStorage>();
builder.Services.AddTransient<IMedicineStorage, MedicineStorage>();
builder.Services.AddTransient<IPrescriptionStorage, PrescriptionStorage>();
builder.Services.AddTransient<IRecipeStorage, RecipeStorage>();
builder.Services.AddTransient<IPatientStorage, PatientStorage>();
builder.Services.AddTransient<IProcedureStorage, ProcedureStorage>();
builder.Services.AddTransient<ITreatmentStorage, TreatmentStorage>();
builder.Services.AddTransient<IApothecaryLogic, ApothecaryLogic>();
builder.Services.AddTransient<IMedicineLogic, MedicineLogic>();
builder.Services.AddTransient<IPrescriptionLogic, PrescriptionLogic>();
builder.Services.AddTransient<IRecipeLogic, RecipeLogic>();
builder.Services.AddTransient<IProcedureLogic, ProcedureLogic>();
builder.Services.AddTransient<ITreatmentLogic, TreatmentLogic>();
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.AddControllers().AddJsonOptions((option) =>
{
option.JsonSerializerOptions.IncludeFields = true;
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "HospitalRestApi",
Version = "v1"
});
});
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()),
});
// çàãðóçêà äàííûõ "íàïàðíèêà"
LoaderFromXML.LoadProcedures();
LoaderFromXML.LoadTreatments();
LoaderFromXML.LoadPatients();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
"HospitalRestApi v1"));
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,31 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:43890",
"sslPort": 44365
}
},
"profiles": {
"HospitalRestApi": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7199;http://localhost:5199",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"SmtpClientHost": "smtp.gmail.com",
"SmtpClientPort": "587",
"MailLogin": "MailNoNameLab@gmail.com",
"MailPassword": "yfbo xzzw mxee jaqt"
}

View File

@ -0,0 +1,64 @@
using System.Net.Http.Headers;
using System.Text;
using HospitalContracts.ViewModels;
using Newtonsoft.Json;
namespace HospitalWeb
{
public static class APIClient
{
private static readonly HttpClient _client = new();
public static ApothecaryViewModel? Apothecary { get; set; } = null;
public static void Connect(IConfiguration configuration)
{
_client.BaseAddress = new Uri(configuration["IPAddress"]);
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
}
public static T? GetRequest<T>(string requestUrl)
{
var response = _client.GetAsync(requestUrl);
var result = response.Result.Content.ReadAsStringAsync().Result;
if (response.Result.IsSuccessStatusCode)
{
return JsonConvert.DeserializeObject<T>(result);
}
else
{
throw new Exception(result);
}
}
public static void PostRequest<T>(string requestUrl, T model)
{
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = _client.PostAsync(requestUrl, data);
var result = response.Result.Content.ReadAsStringAsync().Result;
if (!response.Result.IsSuccessStatusCode)
{
throw new Exception(result);
}
}
public static (Stream stream, string? contentType)? GetFileRequest<T>(string requestUrl, T model)
{
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = _client.PostAsync(requestUrl, data);
var result = response.Result.Content.ReadAsStreamAsync().Result;
response.Result.Content.Headers.TryGetValues("content-type", out var type);
if (response.Result.IsSuccessStatusCode)
{
return (stream: result, contentType: type?.First());
}
else
{
throw new Exception($"Failed to retrieve file from {requestUrl}. StatusCode: {response.Result.StatusCode}");
}
}
}
}

View File

@ -1,4 +1,7 @@
using HospitalWeb.Models; using HospitalContracts.BindingModels;
using HospitalContracts.ViewModels;
using HospitalDatabaseImplement;
using HospitalWeb.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Diagnostics; using System.Diagnostics;
@ -18,11 +21,85 @@ namespace HospitalWeb.Controllers
return View(); return View();
} }
[HttpGet]
public IActionResult Privacy() public IActionResult Privacy()
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.Apothecary);
}
[HttpPost]
public void Privacy(string login, string password)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
if (string.IsNullOrEmpty(login) ||
string.IsNullOrEmpty(password))
{
throw new Exception("Введите логин и пароль");
}
APIClient.PostRequest("api/apothecary/updatedata", new
ApothecaryBindingModel
{
Id = APIClient.Apothecary.Id,
Login = login,
Password = password
});
APIClient.Apothecary.Login = login;
APIClient.Apothecary.Password = password;
Response.Redirect("Index");
}
[HttpGet]
public IActionResult Enter()
{ {
return View(); return View();
} }
[HttpPost]
public void Enter(string login, string password)
{
if (string.IsNullOrEmpty(login) ||
string.IsNullOrEmpty(password))
{
throw new Exception("Введите логин и пароль");
}
APIClient.Apothecary =
APIClient.GetRequest<ApothecaryViewModel>($"api/apothecary/login?login={login}&password={password}");
if (APIClient.Apothecary == null)
{
throw new Exception("Неверный логин/пароль");
}
Response.Redirect("Index");
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
public void Register(string login, string password)
{
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password))
{
throw new Exception("Введите логин и пароль");
}
APIClient.PostRequest("api/apothecary/register", new
ApothecaryBindingModel
{
Login = login,
Password = password
});
Response.Redirect("Enter");
return;
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() public IActionResult Error()
{ {

View File

@ -0,0 +1,122 @@
using HospitalContracts.BindingModels;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Globalization;
namespace HospitalWeb.Controllers
{
public class MedicineController : Controller
{
private readonly ILogger<MedicineController> _logger;
public MedicineController(ILogger<MedicineController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
[HttpGet("/medicine")]
public IActionResult Medicines()
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<MedicineViewModel>>($"api/medicine/getmedicines?apothecaryId={APIClient.Apothecary.Id}"));
}
[HttpGet("/medicine/save/{id?}")]
public IActionResult Create(int? id)
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
if (!id.HasValue)
{
return View();
}
var model = APIClient.GetRequest<MedicineViewModel?>($"api/medicine/getmedicine?id={id}");
return View(model);
}
[HttpPost("/medicine/save/{id?}")]
public void Create(int? id, string name, string costString, string dose)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
if (!double.TryParse(costString, NumberStyles.Any, CultureInfo.InvariantCulture, out double cost) || cost <= 0)
{
throw new Exception("Цена лекарства должна быть больше 0");
}
if (id.HasValue)
{
APIClient.PostRequest("api/medicine/update", new MedicineBindingModel
{
Id = id.Value,
Name = name,
Cost = cost,
Dose = dose
});
}
else
{
APIClient.PostRequest("api/medicine/create", new MedicineBindingModel
{
ApothecaryId = APIClient.Apothecary.Id,
Name = name,
Cost = cost,
Dose = dose
});
}
Response.Redirect("/medicine");
}
[HttpPost("/medicine/delete")]
public void Delete(int id)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
APIClient.PostRequest($"api/medicine/delete", new MedicineBindingModel { Id = id });
Response.Redirect("/medicine");
}
[HttpGet("/medicine/addProcedure")]
public IActionResult AddProcedureMedicine()
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Procedures = APIClient.GetRequest<List<ProcedureViewModel>>($"api/procedure/getprocedures");
ViewBag.Medicines = APIClient.GetRequest<List<MedicineViewModel>>($"api/medicine/getmedicines?apothecaryId={APIClient.Apothecary.Id}");
return View();
}
[HttpPost("/medicine/addProcedure")]
public void AddProcedureMedicine(int medicine, int procedure)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
APIClient.PostRequest($"api/medicine/addproceduremedicine", (new MedicineBindingModel { Id = medicine}, new ProcedureBindingModel { Id = procedure}));
Response.Redirect("/medicine");
}
}
}

View File

@ -0,0 +1,85 @@
using HospitalContracts.BindingModels;
using HospitalContracts.ViewModels;
using Microsoft.AspNetCore.Mvc;
namespace HospitalWeb.Controllers
{
public class PrescriptionController : Controller
{
private readonly ILogger<PrescriptionController> _logger;
public PrescriptionController(ILogger<PrescriptionController> logger)
{
_logger = logger;
}
[HttpGet("/prescription")]
public IActionResult Prescriptions()
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<PrescriptionViewModel>>($"api/prescription/getprescriptions?apothecaryId={APIClient.Apothecary.Id}"));
}
[HttpGet("/prescription/save/{id?}")]
public IActionResult Create(int? id)
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Medicines = APIClient.GetRequest<List<MedicineViewModel>>($"api/medicine/getmedicines");
if (!id.HasValue) {
return View();
}
var model = APIClient.GetRequest<PrescriptionViewModel?>($"api/prescription/getprescription?id={id}");
return View(model);
}
[HttpPost("/prescription/save/{id?}")]
public void Create(int? id, int medicine, int number)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
if (number <= 0)
{
throw new Exception("Количество лекарств должно быть больше 0");
}
if (id.HasValue)
{
APIClient.PostRequest("api/prescription/update", new PrescriptionBindingModel
{
Id = id.Value,
Number = number
});
}
else {
APIClient.PostRequest("api/prescription/create", new PrescriptionBindingModel
{
ApothecaryId = APIClient.Apothecary.Id,
MedicineId = medicine,
Number = number
});
}
Response.Redirect("/prescription");
}
[HttpPost("/prescription/delete")]
public void Delete(int id)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
APIClient.PostRequest($"api/prescription/delete", new PrescriptionBindingModel { Id = id });
Response.Redirect("/prescription");
}
}
}

View File

@ -0,0 +1,77 @@
using HospitalContracts.BindingModels;
using HospitalContracts.ViewModels;
using HospitalDatabaseImplement.Models;
using Microsoft.AspNetCore.Mvc;
namespace HospitalWeb.Controllers
{
public class RecipeController : Controller
{
private readonly ILogger<RecipeController> _logger;
public RecipeController(ILogger<RecipeController> logger)
{
_logger = logger;
}
[HttpGet("/recipe")]
public IActionResult Recipes()
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<RecipeViewModel>>($"api/recipe/getrecipes?apothecaryId={APIClient.Apothecary.Id}"));
}
[HttpGet("/recipe/save/{id?}")]
public IActionResult Create(int? id)
{
if (APIClient.Apothecary == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Medicines = APIClient.GetRequest<List<MedicineViewModel>>($"api/medicine/getmedicines");
ViewBag.Treatments = APIClient.GetRequest<List<TreatmentViewModel>>($"api/treatment/gettreatments");
if (!id.HasValue)
{
return View(new RecipeViewModel());
}
var model = APIClient.GetRequest<RecipeViewModel?>($"api/recipe/getrecipe?id={id}");
return View(model);
}
[HttpPost("/recipe/save/{id?}")]
public void Create(RecipeBindingModel model)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
model.ApothecaryId = APIClient.Apothecary.Id;
if (model.Id != 0)
{
APIClient.PostRequest("api/recipe/update", model);
}
else
{
APIClient.PostRequest("api/recipe/create", model);
}
Response.Redirect("/recipe");
}
[HttpPost("/recipe/delete")]
public void Delete(int id)
{
if (APIClient.Apothecary == null)
{
throw new Exception("Доступно только авторизованным пользователям");
}
APIClient.PostRequest($"api/recipe/delete", new RecipeBindingModel { Id = id });
Response.Redirect("/recipe");
}
}
}

View File

@ -0,0 +1,82 @@
using HospitalContracts.BindingModels;
using HospitalContracts.ViewModels;
using HospitalWeb.HelpModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Net;
namespace HospitalWeb.Controllers
{
public class ReportController : Controller
{
[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)
{
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")]
public IActionResult DownloadReport(ReportBindingModel model, string Format)
{
var format = Enum.Parse<FileFormat>(Format);
var (endpoint, extension) = format switch
{
FileFormat.Word => ("api/report/getreportpatientstowordfile/", ".docx"),
FileFormat.Excel => ("api/report/getreportpatientstoexcelfile/", ".xlsx"),
_ => throw new ArgumentException($"Invalid file format: {format}", nameof(Format))
};
model.FileName += extension;
var fileData = APIClient.GetFileRequest(endpoint, model);
if (fileData != null)
return File(fileData.Value.stream, fileData.Value.contentType!, model.FileName);
throw new Exception($"Failed to retrieve file from {endpoint}.");
}
}
}

View File

@ -0,0 +1,8 @@
namespace HospitalWeb.HelpModels
{
public enum FileFormat
{
Word = 0,
Excel = 1
}
}

View File

@ -11,12 +11,13 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\HospitalBusinessLogic\HospitalBusinessLogic.csproj" />
<ProjectReference Include="..\HospitalContracts\HospitalContracts.csproj" /> <ProjectReference Include="..\HospitalContracts\HospitalContracts.csproj" />
<ProjectReference Include="..\HospitalDatabaseImplement\HospitalDatabaseImplement.csproj" /> <ProjectReference Include="..\HospitalDatabaseImplement\HospitalDatabaseImplement.csproj" />
<ProjectReference Include="..\HospitalDataModels\HospitalDataModels.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,10 +1,13 @@
using HospitalDatabaseImplement;
using HospitalWeb;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddControllersWithViews(); builder.Services.AddControllersWithViews();
var app = builder.Build(); var app = builder.Build();
APIClient.Connect(builder.Configuration);
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment()) if (!app.Environment.IsDevelopment())
{ {

View File

@ -0,0 +1,26 @@
@{
ViewData["Title"] = "Enter";
}
<div class="text-center">
<h2 class="display-4">Вход в приложение</h2>
</div>
<form method="post" class="form mx-auto d-block shadow p-3 mb-5 bg-white rounded" style="width: 30%">
<div class="form-group row">
<label for="login" class="col-sm-4 col-form-label">Логин:</label>
<div class="col-sm-8">
<input type="text" class="form-control" id="login" name="login">
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-4 col-form-label">Пароль:</label>
<div class="col-sm-8">
<input type="password" class="form-control" id="password" name="password">
</div>
</div>
<div class="form-group row">
<div class="col-sm-8"></div>
<div class="col-sm-4">
<button type="submit" class="btn btn-primary btn-block">Вход</button>
</div>
</div>
</form>

View File

@ -1,8 +1,39 @@
@{ @{
ViewData["Title"] = "Home Page"; ViewData["Title"] = "Аптека";
} }
<div class="text-center"> <div class="container">
<h1 class="display-4">Welcome</h1> <div class="jumbotron text-center">
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> <h1 class="display-4">Аптека</h1>
<p class="lead">Приложение для управления лекарствами, поступлениями и рецептами</p>
</div>
<div class="row">
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Лекарства</h5>
<p class="card-text">Добавляйте, редактируйте и удаляйте лекарства в базе данных</p>
<a href="@Url.Action("Medicines", "Medicine")" class="btn btn-outline-success">Перейти</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Поступления</h5>
<p class="card-text">Управляйте поступлениями лекарств в аптеку</p>
<a href="@Url.Action("Prescriptions", "Prescription")" class="btn btn-outline-success">Перейти</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Рецепты</h5>
<p class="card-text">Добавляйте, редактируйте и удаляйте рецепты в базе данных</p>
<a href="@Url.Action("Recipes", "Recipe")" class="btn btn-outline-success">Перейти</a>
</div>
</div>
</div>
</div>
</div> </div>

View File

@ -1,6 +1,25 @@
@{ @using HospitalContracts.ViewModels
@model ApothecaryViewModel
@{
ViewData["Title"] = "Privacy Policy"; ViewData["Title"] = "Privacy Policy";
} }
<h1>@ViewData["Title"]</h1> <div class="text-center">
<h2 class="display-4">Личные данные</h2>
<p>Use this page to detail your site's privacy policy.</p> </div>
<form method="post" class="form">
<div class="row mb-3">
<div class="col-4">Логин:</div>
<div class="col-8"><input class="form-control" type="text" name="login"
value="@Model.Login"/></div>
</div>
<div class="row mb-3">
<div class="col-4">Пароль:</div>
<div class="col-8"><input class="form-control" type="password" name="password"
value="@Model.Password"/></div>
</div>
<div class="row mb-3">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Сохранить" class="btn
btn-primary" /></div>
</div>
</form>

View File

@ -0,0 +1,21 @@
@{
ViewData["Title"] = "Register";
}
<div class="text-center">
<h2 class="display-4">Регистрация</h2>
</div>
<form method="post" class="form">
<div class="row mb-3">
<div class="col-4">Логин:</div>
<div class="col-8"><input class="form-control" type="text" name="login" /></div>
</div>
<div class="row mb-3">
<div class="col-4">Пароль:</div>
<div class="col-8"><input class="form-control" type="password" name="password" /></div>
</div>
<div class="row mb-3">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Регистрация"
class="btn btn-primary" /></div>
</div>
</form>

View File

@ -0,0 +1,35 @@
@using HospitalContracts.ViewModels;
@using System.Globalization
@model MedicineViewModel
@{
ViewData["Title"] = "Create";
}
@{
<div class="text-center">
<h2 class="display-4">Привязка процедуры</h2>
</div>
<form method="post" class="form">
<div class="row mb-3">
<div class="col-4">Процедура:</div>
<div class="col-8">
<select id="procedure" name="procedure" class="form-control" asp-items="@(new SelectList(@ViewBag.Procedures,"Id", "Name"))"></select>
</div>
</div>
<div class="row mb-3">
<div class="col-4">Лекарство:</div>
<div class="col-8">
<select id="medicine" name="medicine" class="form-control" asp-items="@(new SelectList(@ViewBag.Medicines,"Id", "Name"))"></select>
</div>
</div>
<div class="row mb-3">
<div class="col-8"></div>
<div class="col-4">
<input type="submit" value="Отправить" class="btn btn-primary" />
</div>
</div>
</form>
}

View File

@ -0,0 +1,43 @@
@using HospitalContracts.ViewModels;
@using System.Globalization
@model MedicineViewModel
@{
ViewData["Title"] = "Create";
}
@{
if (Model != null)
{
<div class="text-center">
<h2 class="display-4">Редактирование лекарства</h2>
</div>
}
else {
<div class="text-center">
<h2 class="display-4">Создание лекарства</h2>
</div>
}
<form method="post" class="form">
<input type="hidden" name="id" value="@Model?.Id" />
<div class="row mb-3">
<div class="col-4">Название:</div>
<div class="col-8"><input class="form-control" type="text" id="name" value="@Model?.Name" name="name" required/></div>
</div>
<div class="row mb-3">
<div class="col-4">Цена:</div>
<div class="col-8"><input class="form-control" type="number" step="0.1" id="costString" value="@Model?.Cost.ToString(CultureInfo.InvariantCulture)" name="costString" required/></div>
</div>
<div class="row mb-3">
<div class="col-4">Дозировка:</div>
<div class="col-8"><input class="form-control" type="text" id="dose" value="@Model?.Dose" name="dose" required/></div>
</div>
<div class="row mb-3">
<div class="col-8"></div>
<div class="col-4">
<input type="submit" value="Отправить" class="btn btn-primary" />
</div>
</div>
</form>
}

View File

@ -0,0 +1,70 @@
@using HospitalContracts.ViewModels
@model List<MedicineViewModel>
@{
ViewData["Title"] = "Лекарства";
}
<div class="text-center">
<h1 class="display-4">Лекарства</h1>
</div>
<div class="text-center">
@{
if (Model == null)
{
<h3 class="display-4">Авторизируйтесь</h3>
return;
}
<p class="button-links">
<a class="button-link" asp-controller="Home" asp-action="Index">На главную</a>
<a class="button-link" asp-controller="Medicine" asp-action="Create">Создать лекарство</a>
<a class="button-link" asp-controller="Medicine" asp-action="AddProcedureMedicine">Привязать процедуру</a>
</p>
<table class="table">
<thead>
<tr>
<th>Номер</th>
<th>Название</th>
<th>Цена</th>
<th>Дозировка</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Cost)
</td>
<td>
@Html.DisplayFor(modelItem => item.Dose)
</td>
<td>
<a class="btn btn-warning" asp-controller="Medicine" asp-action="Create" asp-route-id="@item.Id">Редактировать</a>
<button class="btn btn-danger delete-btn" data-id="@item.Id">Удалить</button>
</td>
</tr>
}
</tbody>
</table>
}
</div>
@section scripts {
<script>
$(function () {
$(".delete-btn").click(function () {
var id = $(this).data("id");
if (confirm("Вы действительно хотите удалить эту запись?")) {
$.post("@Url.Action("Delete", "Medicine")", { id: id }, function () {
window.location.reload();
});
}
});
});
</script>
}

View File

@ -0,0 +1,48 @@
@using HospitalContracts.ViewModels;
@model PrescriptionViewModel
@{
ViewData["Title"] = "Поступление";
}
@{
if (Model != null)
{
<div class="text-center">
<h2 class="display-4">Редактирование поступления</h2>
</div>
}
else {
<div class="text-center">
<h2 class="display-4">Создание поступления</h2>
</div>
}
<form method="post" class="form" >
<input type="hidden" name="id" value="@Model?.Id" />
<div class="row">
<div class="row mb-3">
<div class="col-4">Лекарство:</div>
<div class="col-8">
@{
if (Model != null)
{
<select id="medicine" name="medicine" class="form-control" asp-items="@(new SelectList(@ViewBag.Medicines,"Id", "Name"))" value="@Model?.MedicineId" disabled="disabled" required></select>
} else {
<select id="medicine" name="medicine" class="form-control" asp-items="@(new SelectList(@ViewBag.Medicines,"Id", "Name"))"></select>
}
}
</div>
</div>
<div class="row mb-3">
<div class="col-4">Количество:</div>
<div class="col-8"><input type="number" id="number" value="@Model?.Number" name="number" required/></div>
</div>
<div class="row mb-3">
<div class="col-8"></div>
<div class="col-4">
<input type="submit" value="Отправить" class="btn btn-primary" />
</div>
</div>
</form>
}

View File

@ -0,0 +1,69 @@
@using HospitalContracts.ViewModels
@model List<PrescriptionViewModel>
@{
ViewData["Title"] = "Поступления";
}
<div class="text-center">
<h1 class="display-4">Поступления</h1>
</div>
<div class="text-center">
@{
if (Model == null)
{
<h3 class="display-4">Авторизируйтесь</h3>
return;
}
<p>
<a asp-controller="Home" asp-action="Index">На главную</a>
<a asp-controller="Prescription" asp-action="Create">Создать поступление</a>
</p>
<table class="table">
<thead>
<tr>
<th>Номер</th>
<th>Дата поступления</th>
<th>Лекарство</th>
<th>Количество</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Date)
</td>
<td>
@Html.DisplayFor(modelItem => item.MedicineName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Number)
</td>
<td>
<a class="btn btn-warning" asp-controller="Prescription" asp-action="Create" asp-route-id="@item.Id">Редактировать</a>
<button class="btn btn-danger delete-btn" data-id="@item.Id">Удалить</button>
</td>
</tr>
}
</tbody>
</table>
}
</div>
@section scripts {
<script>
$(function () {
$(".delete-btn").click(function () {
var id = $(this).data("id");
if (confirm("Вы действительно хотите удалить эту запись?")) {
$.post("@Url.Action("Delete", "Prescription")", { id: id }, function () {
window.location.reload();
});
}
});
});
</script>
}

View File

@ -0,0 +1,170 @@
@using HospitalContracts.ViewModels;
@using System.Globalization
@model RecipeViewModel
@{
ViewData["Title"] = "Рецепт";
}
@{
if (Model.Id > 0)
{
<div class="text-center">
<h2 class="display-4">Редактирование рецепта</h2>
</div>
}
else {
<div class="text-center">
<h2 class="display-4">Создание рецепта</h2>
</div>
}
<form id="recipe-form" class="form" method="post">
<input type="hidden" name="id" value="@Model?.Id" />
<div class="row mb-3">
<div class="col-4">Название:</div>
<div class="col-8"><input class="form-control" type="text" id="name" value="@Model?.Name" name="name" required/></div>
</div>
<div class="row mb-3">
<div class="col-4">Добавление лекарств</div>
<div class="col-8">
<div class="row">
<div class="col-6">
<select id="medicines" name="medicines" class="form-control" asp-items="@(new SelectList(@ViewBag.Medicines,"Id", "Name"))"></select>
</div>
<div class="col-6">
<button type="button" class="btn btn-success" onclick="addMedicine()">Добавить</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-4">Добавление лечений</div>
<div class="col-8">
<div class="row">
<div class="col-6">
<select id="treatments" name="treatments" class="form-control" asp-items="@(new SelectList(@ViewBag.Treatments,"Id", "Name"))"></select>
</div>
<div class="col-6">
<button type="button" class="btn btn-success" onclick="addTreatment()">Добавить</button>
</div>
</div>
</div>
</div>
<div class="row">
<p class="text-center"><strong>Лекарства</strong></p>
<table id="medicinesTable" class="table table-bordered table-striped">
<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" class="btn btn-danger" data-id="@medicine.Key" onclick="removeMedicine('@medicine.Key')">Удалить</button>
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="row">
<p class="text-center"><strong>Лечения</strong></p>
<table id="treatmentsTable" class="table table-bordered table-striped">
<thead>
<tr>
<th>Название</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var treatment in Model.RecipeTreatments)
{
<tr>
<td>@treatment.Value.Name</td>
<td>
<button type="button" class="btn btn-danger" data-id="@treatment.Key" onclick="removeTreatment('@treatment.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" />
</div>
</div>
@foreach (var medicine in Model.RecipeMedicines.Keys)
{
<input type="hidden" name="RecipeMedicines[@medicine]" value="@medicine"/>
}
@foreach (var treatment in Model.RecipeTreatments.Keys)
{
<input type="hidden" name="RecipeTreatments[@treatment]" value="@treatment"/>
}
</form>
}
@section scripts {
<script>
var recipeMedicines = @Json.Serialize(Model.RecipeMedicines);
function addMedicine() {
var medicineId = $('#medicines').val();
var medicineName = $('#medicines option:selected').text();
if (recipeMedicines.hasOwnProperty(medicineId)) {
alert('This medicine is already added.');
return;
}
recipeMedicines[medicineId] = { Id: medicineId, Name: medicineName };
var row = $('<tr>').append($('<td>').text(medicineName));
var removeButton = $('<button>').text('Удалить').attr('data-id', medicineId).attr('class', 'btn btn-danger').click((function(id) {
return function() {
removeMedicine(id);
};
})(medicineId));
row.append($('<td>').append(removeButton));
$('#medicinesTable tbody').append(row);
var input = $('<input>').attr('type', 'hidden').attr('name', 'RecipeMedicines[' + medicineId + ']').val(medicineId);
$('#recipe-form').append(input);
}
function removeMedicine(medicineId) {
delete recipeMedicines[medicineId];
$('#medicinesTable button[data-id="' + medicineId + '"]').closest('tr').remove();
$('#recipe-form input[name="RecipeMedicines[' + medicineId + ']"]').remove();
}
var recipeTreatments = @Json.Serialize(Model.RecipeTreatments);
function addTreatment() {
var treatmentId = $('#treatments').val();
var treatmentName = $('#treatments option:selected').text();
if (recipeTreatments.hasOwnProperty(treatmentId)) {
alert('This treatment is already added.');
return;
}
recipeTreatments[treatmentId] = { Id: treatmentId, Name: treatmentName };
var row = $('<tr>').append($('<td>').text(treatmentName));
var removeButton = $('<button>').text('Удалить').attr('data-id', treatmentId).attr('class', 'btn btn-danger').click((function(id) {
return function() {
removeTreatment(id);
};
})(treatmentId));
row.append($('<td>').append(removeButton));
$('#treatmentsTable tbody').append(row);
var input = $('<input>').attr('type', 'hidden').attr('name', 'RecipeTreatments[' + treatmentId + ']').val(treatmentId);
$('#recipe-form').append(input);
}
function removeTreatment(treatmentId) {
delete recipeTreatments[treatmentId];
$('#treatmentsTable button[data-id="' + treatmentId + '"]').closest('tr').remove();
$('#recipe-form input[name="RecipeTreatments[' + treatmentId + ']"]').remove();
}
</script>
}

View File

@ -0,0 +1,65 @@
@using HospitalContracts.ViewModels
@model List<RecipeViewModel>
@{
ViewData["Title"] = "Рецепты";
}
<div class="text-center">
<h1 class="display-4">Рецепты</h1>
</div>
<div class="text-center">
@{
if (Model == null)
{
<h3 class="display-4">Авторизируйтесь</h3>
return;
}
<p>
<a asp-controller="Home" asp-action="Index">На главную</a>
<a asp-controller="Recipe" asp-action="Create">Создать рецепт</a>
</p>
<table class="table">
<thead>
<tr>
<th>Номер</th>
<th>Дата создания рецепта</th>
<th>Название рецепта</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Date)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<a class="btn btn-warning" asp-controller="Recipe" asp-action="Create" asp-route-id="@item.Id">Редактировать</a>
<button class="btn btn-danger delete-btn" data-id="@item.Id">Удалить</button>
</td>
</tr>
}
</tbody>
</table>
}
</div>
@section scripts {
<script>
$(function () {
$(".delete-btn").click(function () {
var id = $(this).data("id");
if (confirm("Вы действительно хотите удалить эту запись?")) {
$.post("@Url.Action("Delete", "Recipe")", { id: id }, function () {
window.location.reload();
});
}
});
});
</script>
}

View File

@ -0,0 +1,34 @@
@using HospitalContracts.ViewModels;
@using System.Globalization
@model MedicineViewModel
@{
ViewData["Title"] = "Отчет Пациенты";
}
<div class="text-center">
<h2 class="display-4">@ViewData["Title"]</h2>
</div>
<form class="form" id="reportForm" method="get" action="/reports/download">
<div class="row">
<div class="col-4">Выбор лекарств</div>
<div class="col-8">
<select id="medicines" name="Medicines" class="form-control" multiple asp-items="@(new SelectList(@ViewBag.Medicines,"Id", "Name"))" required></select>
<small>Для выбора нескольких элементов удерживайте клавишу Ctrl.</small>
</div>
</div>
<div class="row">
<div class="col-4">Название файла (без формата):</div>
<div class="col-8"><input type="text" name="FileName" class="form-control" required /></div>
</div>
<div class="row">
<div class="col-4">Формат файла:</div>
<div class="col-8">
<select class="form-control" name="Format" required>
<option value="Word">.docx</option>
<option value="Excel">.xlsx</option>
</select>
</div>
</div>
<button class="btn btn-primary" id="submitReport">Скачать отчет</button>
</form>

View File

@ -0,0 +1,73 @@
@using HospitalContracts.ViewModels;
@model List<ReportPrescriptionProcedureViewModel>
@{
ViewData["Title"] = "Отчет Поступления";
}
<div class="text-center">
<h2 class="display-4">@ViewData["Title"]</h2>
</div>
<form method="get" class="form form-inline" action="/report/getprescriptions">
<div class="form-group mx-sm-3 mb-2">
<label for="startDate" class="sr-only">Начальная дата:</label>
<input type="date" class="form-control" id="startDate" name="startDate" required />
</div>
<div class="form-group mx-sm-3 mb-2">
<label for="endDate" class="sr-only">Конечная дата:</label>
<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="form">Получить отчет</button>
<button type="submit" class="btn btn-secondary" name="type" value="email">Отправить pdf на почту</button>
</div>
</form>
@if (Model != null && Model.Any())
{
<table class="table" style="width: 70%; margin: 0 auto;">
<thead>
<tr>
<th>Дата поступления</th>
<th>Лекарство</th>
<th>Количество</th>
<th>Название процедуры</th>
</tr>
</thead>
<tbody>
@foreach (var report in Model)
{
<tr>
<td>@report.DateCreate.ToShortDateString()</td>
<td>@report.MedicineName</td>
<td>@report.Number</td>
<td>@report.ProcedureName</td>
</tr>
}
</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>
}
}

View File

@ -7,6 +7,7 @@
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/HospitalWeb.styles.css" asp-append-version="true" /> <link rel="stylesheet" href="~/HospitalWeb.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/styles.css" asp-append-version="true" />
</head> </head>
<body> <body>
<header> <header>
@ -19,11 +20,36 @@
</button> </button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"> <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1"> <ul class="navbar-nav flex-grow-1">
@if (APIClient.Apothecary != null) {
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a> <a class="nav-link text-dark" asp-area="" asp-controller="Prescription" asp-action="Prescriptions">Поступления</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a> <a class="nav-link text-dark" asp-area="" asp-controller="Medicine" asp-action="Medicines">Лекарства</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Recipe" asp-action="Recipes">Рецепты</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="reportDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Отчеты
</a>
<div class="dropdown-menu" aria-labelledby="reportDropdown">
<a class="dropdown-item" asp-area="" asp-controller="Report" asp-action="ReportPatients">Отчет Пациенты</a>
<a class="dropdown-item" asp-area="" asp-controller="Report" asp-action="ReportPrescriptions">Отчет Поступления</a>
</div>
</li>
<li class="nav-item">
<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" 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> </li>
</ul> </ul>
</div> </div>
@ -38,7 +64,7 @@
<footer class="border-top footer text-muted"> <footer class="border-top footer text-muted">
<div class="container"> <div class="container">
&copy; 2023 - HospitalWeb - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a> &copy; 2023 - HospitalWeb
</div> </div>
</footer> </footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script> <script src="~/lib/jquery/dist/jquery.min.js"></script>

View File

@ -5,5 +5,6 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"IPAddress": "http://localhost:5199/"
} }

View File

@ -0,0 +1,100 @@
.form {
width: 70%;
margin: 0 auto;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
.form input,
.form select {
padding: 10px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #ccc;
}
.form button {
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.jumbotron {
background-color: #fff;
padding: 4rem 2rem;
margin-bottom: 2rem;
border-radius: 0.3rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.lead {
color: #9ac86a;
font-weight: bold;
}
.card {
margin-bottom: 2rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
transition: transform 0.3s ease-in-out;
}
.card:hover {
transform: scale(1.05);
}
.card-title {
font-size: 1.25rem;
font-weight: bold;
margin-bottom: 0.5rem;
color: #4c4c4c;
}
.card-text {
margin-bottom: 1rem;
color: #4c4c4c;
}
.btn-outline-success {
color: #7cb342;
border-color: #7cb342;
}
.btn-outline-success:hover {
background-color: #7cb342;
color: #fff;
}
.navbar-brand {
font-weight: bold;
color: #7cb342 !important;
}
a {
color: #7cb342; /* нежно-зеленый цвет для ссылок */
}
a:hover {
color: #5f8e29; /* темнее зеленый цвет для наведенных ссылок */
}
.table {
background-color: #fff; /* белый цвет фона таблицы */
}
.table th {
background-color: #7cb342; /* нежно-зеленый цвет фона заголовков таблицы */
color: #fff; /* белый цвет текста заголовков таблицы */
}
.table td {
background-color: #fff;
}
.navbar-nav .nav-link:hover {
background-color: #f2f2f2; /* нежно-зеленый цвет фона при наведении */
}