From 525668cb62bdd100e9d59810ed1c3d8b61d4b583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B0=D1=82=D1=8C=D1=8F=D0=BD=D0=B0=20=D0=90=D1=80?= =?UTF-8?q?=D1=82=D0=B0=D0=BC=D0=BE=D0=BD=D0=BE=D0=B2=D0=B0?= Date: Tue, 19 Nov 2024 16:45:59 +0400 Subject: [PATCH] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8C=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=B0=D0=BD=D0=B8=D0=B8=20=D1=81=D0=BE?= =?UTF-8?q?=20=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=BE=D0=BC=20=D0=B2=D0=B0?= =?UTF-8?q?=D0=BA=D0=B0=D0=BD=D1=81=D0=B8=D0=B9,=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=8C=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=B8=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BusinessLogic/CompanyLogic.cs | 53 +++++++++- .../BusinessLogic/UserLogic.cs | 1 - CandidateReviewClientApp/APIClient.cs | 1 + .../Controllers/CompanyController.cs | 16 ++- .../Controllers/HomeController.cs | 4 + .../Controllers/UserController.cs | 44 ++++++-- .../Controllers/VacancyController.cs | 92 ++++++++++++++++ .../Views/Company/CompanyProfile.cshtml | 98 ++++++++++++------ .../Views/Home/Privacy.cshtml | 6 -- .../Views/Shared/_Layout.cshtml | 14 ++- .../Views/User/UserProfile.cshtml | 54 +++++----- .../Views/Vacancy/EditVacancy.cshtml | 90 ++++++++++++++++ .../Views/Vacancy/SearchVacancies.cshtml | 40 +++++++ .../Views/Vacancy/VacancyDetails.cshtml | 54 ++++++++++ .../images/default-company-logo.png | Bin 0 -> 14369 bytes .../BindingModels/CompanyBindingModel.cs | 5 +- .../ViewModels/CompanyViewModel.cs | 2 + .../Models/Vacancy.cs | 2 +- .../Controllers/CompanyController.cs | 18 +++- .../Controllers/UserController.cs | 14 +++ .../Controllers/VacancyController.cs | 80 ++++++++++++++ 21 files changed, 605 insertions(+), 83 deletions(-) create mode 100644 CandidateReviewClientApp/Controllers/VacancyController.cs delete mode 100644 CandidateReviewClientApp/Views/Home/Privacy.cshtml create mode 100644 CandidateReviewClientApp/Views/Vacancy/EditVacancy.cshtml create mode 100644 CandidateReviewClientApp/Views/Vacancy/SearchVacancies.cshtml create mode 100644 CandidateReviewClientApp/Views/Vacancy/VacancyDetails.cshtml create mode 100644 CandidateReviewClientApp/images/default-company-logo.png create mode 100644 CandidateReviewRestApi/Controllers/VacancyController.cs diff --git a/CandidateReviewBusinessLogic/BusinessLogic/CompanyLogic.cs b/CandidateReviewBusinessLogic/BusinessLogic/CompanyLogic.cs index 335ae50..ba782e5 100644 --- a/CandidateReviewBusinessLogic/BusinessLogic/CompanyLogic.cs +++ b/CandidateReviewBusinessLogic/BusinessLogic/CompanyLogic.cs @@ -12,10 +12,12 @@ namespace CandidateReviewBusinessLogic.BusinessLogic { private readonly ILogger _logger; private readonly ICompanyStorage _сompanyStorage; - public CompanyLogic(ILogger logger, ICompanyStorage сompanyStorage) + private readonly IVacancyStorage _vacancyStorage; + public CompanyLogic(ILogger logger, ICompanyStorage сompanyStorage, IVacancyStorage vacancyStorage) { _logger = logger; _сompanyStorage = сompanyStorage; + _vacancyStorage = vacancyStorage; } public int Create(CompanyBindingModel model) { @@ -40,7 +42,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic return true; } - public CompanyViewModel? ReadElement(CompanySearchModel model) + /*public CompanyViewModel? ReadElement(CompanySearchModel model) { if (model == null) { @@ -54,8 +56,55 @@ namespace CandidateReviewBusinessLogic.BusinessLogic } _logger.LogInformation("ReadElement find. Id: {Id}", element.Id); return element; + }*/ + + public CompanyViewModel? ReadElement(CompanySearchModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + var company = _сompanyStorage.GetElement(model); + if (company == null) + { + _logger.LogWarning("ReadElement: Company not found for Id: {Id}", model.Id); + return null; + } + var vacancies = _vacancyStorage.GetFilteredList(new VacancySearchModel { CompanyId = company.Id}); + + var vacancyViewModels = vacancies?.Select(v => new VacancyViewModel + { + Id = v.Id, + CompanyId = v.CompanyId, + JobTitle = v.JobTitle, + Requirements = v.Requirements, + Responsibilities = v.Responsibilities, + JobType = v.JobType, + Salary = v.Salary, + Description = v.Description, + Status = v.Status, + CreatedAt = v.CreatedAt, + Tags = v.Tags + }).ToList() ?? new List(); + + var companyViewModel = new CompanyViewModel + { + Id = company.Id, + Name = company.Name, + Description = company.Description, + Contacts = company.Contacts, + Address = company.Address, + Website = company.Website, + LogoFilePath = company.LogoFilePath, + Vacancies = vacancyViewModels + }; + + _logger.LogInformation("ReadElement: Company found. Id: {Id}", company.Id); + return companyViewModel; } + + public List? ReadList(CompanySearchModel? model) { var list = model == null ? _сompanyStorage.GetFullList() : _сompanyStorage.GetFilteredList(model); diff --git a/CandidateReviewBusinessLogic/BusinessLogic/UserLogic.cs b/CandidateReviewBusinessLogic/BusinessLogic/UserLogic.cs index 6e0c09f..b515749 100644 --- a/CandidateReviewBusinessLogic/BusinessLogic/UserLogic.cs +++ b/CandidateReviewBusinessLogic/BusinessLogic/UserLogic.cs @@ -41,7 +41,6 @@ namespace CandidateReviewBusinessLogic.BusinessLogic public bool Delete(UserBindingModel model) { CheckModel(model, false); - CheckPassword(model); _logger.LogInformation("Delete. Id: {Id}", model.Id); if (_userStorage.Delete(model) == null) { diff --git a/CandidateReviewClientApp/APIClient.cs b/CandidateReviewClientApp/APIClient.cs index 43edc7f..5ecd33c 100644 --- a/CandidateReviewClientApp/APIClient.cs +++ b/CandidateReviewClientApp/APIClient.cs @@ -10,6 +10,7 @@ namespace CandidateReviewClientApp private static readonly HttpClient _user = new(); public static UserViewModel? User { get; set; } = null; public static CompanyViewModel? Company { get; set; } = null; + public static VacancyViewModel? Vacancy { get; set; } = null; public static void Connect(IConfiguration configuration) { _user.BaseAddress = new Uri(configuration["IPAddress"]); diff --git a/CandidateReviewClientApp/Controllers/CompanyController.cs b/CandidateReviewClientApp/Controllers/CompanyController.cs index 19e8a4d..ea28f6a 100644 --- a/CandidateReviewClientApp/Controllers/CompanyController.cs +++ b/CandidateReviewClientApp/Controllers/CompanyController.cs @@ -41,7 +41,19 @@ namespace CandidateReviewClientApp.Controllers var model = APIClient.GetRequest($"api/company/profile?id={id}"); if (model != null) { - APIClient.PostRequest($"api/user/update", new UserBindingModel { Id = APIClient.User.Id, CompanyId = model.Id }); + APIClient.PostRequest($"api/user/update", new UserBindingModel { + Id = APIClient.User.Id, + CompanyId = model.Id, + Surname = APIClient.User.Surname, + Name = APIClient.User.Name, + LastName = APIClient.User.LastName, + Email = APIClient.User.Email, + Password = APIClient.User.Password, + EmailConfirmed = APIClient.User.EmailConfirmed, + Role = APIClient.User.Role, + AvatarFilePath = APIClient.User.AvatarFilePath, + PhoneNumber = APIClient.User.PhoneNumber + }); } return View(model); } @@ -61,7 +73,7 @@ namespace CandidateReviewClientApp.Controllers { APIClient.PostRequest("api/company/create", model); } - Response.Redirect("/Home/Index"); + Response.Redirect($"/Company/CompanyProfile/{model.Id}"); } [HttpPost] diff --git a/CandidateReviewClientApp/Controllers/HomeController.cs b/CandidateReviewClientApp/Controllers/HomeController.cs index bcdabe1..cd43eb1 100644 --- a/CandidateReviewClientApp/Controllers/HomeController.cs +++ b/CandidateReviewClientApp/Controllers/HomeController.cs @@ -41,6 +41,10 @@ namespace CandidateReviewUserApp.Controllers throw new Exception(" "); } APIClient.User = APIClient.GetRequest($"api/user/login?login={login}&password={password}"); + if (APIClient.User.CompanyId != null) + { + APIClient.Company = APIClient.GetRequest($"api/company/profile?id={APIClient.User?.CompanyId}"); + } if (APIClient.User == null) { throw new Exception(" /"); diff --git a/CandidateReviewClientApp/Controllers/UserController.cs b/CandidateReviewClientApp/Controllers/UserController.cs index 1cbaf14..37bde7e 100644 --- a/CandidateReviewClientApp/Controllers/UserController.cs +++ b/CandidateReviewClientApp/Controllers/UserController.cs @@ -24,7 +24,7 @@ namespace CandidateReviewClientApp.Controllers if (model == null) { - return RedirectToAction("Index"); + return RedirectToAction("/Home/Index"); } return View(model); @@ -37,7 +37,12 @@ namespace CandidateReviewClientApp.Controllers { return Redirect("/Home/Enter"); } - return View(APIClient.User); + var model = APIClient.GetRequest($"api/user/profile?id={APIClient.User.Id}"); + if (model == null) + { + return RedirectToAction("/Home/Index"); + } + return View(model); } [HttpPost] @@ -48,17 +53,17 @@ namespace CandidateReviewClientApp.Controllers Id = model.Id, Surname = model.Surname, Name = model.Name, - LastName = model.LastName ?? null, - CompanyId = model.CompanyId ?? null, + LastName = model.LastName, + CompanyId = model.CompanyId, Email = model.Email, Password = model.Password, EmailConfirmed = model.EmailConfirmed, Role = model.Role, - AvatarFilePath = model.AvatarFilePath ?? null, - PhoneNumber = model.PhoneNumber ?? null + AvatarFilePath = model.AvatarFilePath, + PhoneNumber = model.PhoneNumber }); - Response.Redirect("/User/UserProfile"); + Response.Redirect($"/User/UserProfile/{model.Id}"); } [HttpGet] @@ -68,6 +73,31 @@ namespace CandidateReviewClientApp.Controllers Response.Redirect("/Home/Enter"); } + [HttpPost] + public void Delete(UserBindingModel model) + { + if (APIClient.User == null) + { + throw new Exception("Доступно только авторизованным пользователям"); + } + + APIClient.PostRequest($"api/user/delete", new UserBindingModel { + Id = model.Id, + Surname = model.Surname, + Name = model.Name, + LastName = model.LastName, + CompanyId = model.CompanyId, + Email = model.Email, + Password = model.Password, + EmailConfirmed = model.EmailConfirmed, + Role = model.Role, + AvatarFilePath = model.AvatarFilePath, + PhoneNumber = model.PhoneNumber + }); + + Response.Redirect("/Home/Enter"); + } + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { diff --git a/CandidateReviewClientApp/Controllers/VacancyController.cs b/CandidateReviewClientApp/Controllers/VacancyController.cs new file mode 100644 index 0000000..3f566eb --- /dev/null +++ b/CandidateReviewClientApp/Controllers/VacancyController.cs @@ -0,0 +1,92 @@ +using CandidateReviewContracts.BindingModels; +using CandidateReviewContracts.ViewModels; +using Microsoft.AspNetCore.Mvc; + +namespace CandidateReviewClientApp.Controllers +{ + public class VacancyController : Controller + { + private readonly ILogger _logger; + + public VacancyController(ILogger logger) + { + _logger = logger; + } + [HttpGet] + public IActionResult Vacancy(int? id) + { + if (APIClient.User == null) + { + return Redirect("~/Home/Enter"); + } + if (id.HasValue) + { + APIClient.Vacancy = APIClient.GetRequest($"api/vacancy/details?id={id}"); + } + var model = APIClient.Vacancy; + return View(model); + } + + [HttpGet] + public IActionResult EditVacancy(int? id) + { + if (APIClient.User == null) + { + return Redirect("~/Home/Enter"); + } + if (!id.HasValue) + { + return View(new VacancyViewModel()); + } + var model = APIClient.GetRequest($"api/vacancy/details?id={id}"); + return View(model); + } + + [HttpPost] + public void EditVacancy(VacancyBindingModel model) + { + if (APIClient.User == null) + { + throw new Exception("Доступно только авторизованным пользователям"); + } + if (model.Id != 0) + { + APIClient.PostRequest("api/vacancy/update", model); + } + else + { + APIClient.PostRequest("api/vacancy/create", model); + if (APIClient.Company != null) + { + APIClient.Company?.Vacancies.Add(new VacancyViewModel + { + Id = model.Id, + CompanyId = model.CompanyId, + CreatedAt = model.CreatedAt, + Description = model.Description, + JobTitle = model.JobTitle, + JobType = model.JobType, + Requirements = model.Requirements, + Responsibilities = model.Responsibilities, + Salary = model.Salary, + Status = model.Status, + Tags = model.Tags + }); + } + } + Response.Redirect($"/Company/CompanyProfile/{model.CompanyId}"); + } + + [HttpPost] + public void Delete(int id) + { + if (APIClient.User == null) + { + throw new Exception("Доступно только авторизованным пользователям"); + } + + APIClient.PostRequest($"api/vacancy/delete", new VacancyBindingModel { Id = id }); + Response.Redirect("/Home/Index"); + } + } +} diff --git a/CandidateReviewClientApp/Views/Company/CompanyProfile.cshtml b/CandidateReviewClientApp/Views/Company/CompanyProfile.cshtml index 63f898f..5c07672 100644 --- a/CandidateReviewClientApp/Views/Company/CompanyProfile.cshtml +++ b/CandidateReviewClientApp/Views/Company/CompanyProfile.cshtml @@ -3,54 +3,86 @@ @{ ViewData["Title"] = "Профиль компании"; + var userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Сотрудник ? true : false; }
- @if (!string.IsNullOrEmpty(Model.LogoFilePath)) - { - Логотип компании - } + Логотип компании
@Model.Name
-

@Model.Description

- @Model.Website +

@(Model.Description == null ? "Описание отсутствует" : Model.Description)

+ @(Model.Website ?? "Веб-сайт отсутствует")
-
-
+
+

Информация о компании

- Редактировать + @if (userRole) + { + Редактировать + }
- - - - - - - - - - - - - - - - - - - - - - - -
Название@Model.Name
Описание@Model.Description
Официальный сайт@Model.Website
Адрес@Model.Address
Контакты@Model.Contacts
+
+
Адрес:
+
@(Model.Address?.ToString() ?? "Адрес не указан")
+ +
Контакты:
+
@(Model.Contacts?.ToString() ?? "Контакты не указаны")
+
+
+
+ +
+
+

Вакансии компании

+ @if (userRole) + { + Добавить вакансию + } +
+
+ @if (@Model.Vacancies != null && @Model.Vacancies.Any()) + { + + + + + + + + + + + @foreach (var vacancy in @Model.Vacancies) + { + + + + + + + } + +
НазваниеТип занятостиЗарплатаДействия
@vacancy.JobTitle@vacancy.JobType@vacancy.Salary + Просмотр + @if (userRole) + { + Редактировать + Удалить + } +
+ } + else + { +

Вакансий нет.

+ }
diff --git a/CandidateReviewClientApp/Views/Home/Privacy.cshtml b/CandidateReviewClientApp/Views/Home/Privacy.cshtml deleted file mode 100644 index af4fb19..0000000 --- a/CandidateReviewClientApp/Views/Home/Privacy.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -@{ - ViewData["Title"] = "Privacy Policy"; -} -

@ViewData["Title"]

- -

Use this page to detail your site's privacy policy.

diff --git a/CandidateReviewClientApp/Views/Shared/_Layout.cshtml b/CandidateReviewClientApp/Views/Shared/_Layout.cshtml index 0357106..2d39dc1 100644 --- a/CandidateReviewClientApp/Views/Shared/_Layout.cshtml +++ b/CandidateReviewClientApp/Views/Shared/_Layout.cshtml @@ -8,7 +8,6 @@ - @@ -17,7 +16,7 @@
@await RenderSectionAsync("Scripts", required: false) + - + \ No newline at end of file diff --git a/CandidateReviewClientApp/Views/User/UserProfile.cshtml b/CandidateReviewClientApp/Views/User/UserProfile.cshtml index a69cdec..f4f3117 100644 --- a/CandidateReviewClientApp/Views/User/UserProfile.cshtml +++ b/CandidateReviewClientApp/Views/User/UserProfile.cshtml @@ -9,41 +9,43 @@
- @if (!string.IsNullOrEmpty(@Model.AvatarFilePath)) + @if (!string.IsNullOrEmpty(@Model?.AvatarFilePath)) { - Аватар пользователя + Аватар пользователя }
- @Model.Name @(string.IsNullOrEmpty(@Model.Surname) ? "" : @Model.Surname) @(string.IsNullOrEmpty(@Model.LastName) ? "" : @Model.LastName) + @Model?.Name @(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname) @(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)
-
-
-
-
-
-
-

Контактная информация

-
- Редактировать - Удалить +
+
Email:
+
@Model?.Email
+ @if (!string.IsNullOrEmpty(@Model?.PhoneNumber)) + { +
Телефон:
+
@Model?.PhoneNumber
+ } +
Роль:
+
@Model?.Role
+
+
+ Редактировать профиль +
+ + +
Выйти
+
+
+ +
+
+
+

Мои резюме

+
-
-
Email
-
@Model.Email
- - @if (!string.IsNullOrEmpty(@Model.PhoneNumber)) - { -
Телефон
-
@Model.PhoneNumber
- } - -
Роль
-
@Model.Role
-
diff --git a/CandidateReviewClientApp/Views/Vacancy/EditVacancy.cshtml b/CandidateReviewClientApp/Views/Vacancy/EditVacancy.cshtml new file mode 100644 index 0000000..e7f7872 --- /dev/null +++ b/CandidateReviewClientApp/Views/Vacancy/EditVacancy.cshtml @@ -0,0 +1,90 @@ +@using CandidateReviewContracts.ViewModels +@using CandidateReviewDataModels.Enums +@model VacancyViewModel + +@{ + var title = @Model.Id <= 0 ? "Создать вакансию" : "Редактировать вакансию"; +} + +
+

@title

+
+ + + +
+ + +
Пожалуйста, введите название должности.
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + @{ + var tagsString = @Model?.Tags; + var displayedTags = string.IsNullOrEmpty(tagsString) ? "" : string.Join(", ", tagsString.Split(',').Select(t => t.Trim())); + } + +
+ + +
+ +@* + *@ +
+ +
+
+
+ + + diff --git a/CandidateReviewClientApp/Views/Vacancy/SearchVacancies.cshtml b/CandidateReviewClientApp/Views/Vacancy/SearchVacancies.cshtml new file mode 100644 index 0000000..9c4a045 --- /dev/null +++ b/CandidateReviewClientApp/Views/Vacancy/SearchVacancies.cshtml @@ -0,0 +1,40 @@ +@* @using CandidateReviewContracts.ViewModels +@model VacancyViewModel + +@{ + ViewData["Title"] = "Поиск вакансий"; +} + +
+
+
+

Поиск вакансий

+
+
+ + +
+
+ + @if (@Model != null && @Model.Vacancies != null) + { +

Результаты поиска:

+ @if (Model.Vacancies.Count > 0) + { +
    + @foreach (var vacancy in Model.Vacancies) + { +
  • @vacancy.JobTitle
  • + } +
+ } + else + { +

Вакансий не найдено.

+ } + } +
+
+
+ + *@ \ No newline at end of file diff --git a/CandidateReviewClientApp/Views/Vacancy/VacancyDetails.cshtml b/CandidateReviewClientApp/Views/Vacancy/VacancyDetails.cshtml new file mode 100644 index 0000000..9c1243d --- /dev/null +++ b/CandidateReviewClientApp/Views/Vacancy/VacancyDetails.cshtml @@ -0,0 +1,54 @@ +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using CandidateReviewContracts.ViewModels +@model VacancyViewModel + +@{ + ViewData["Title"] = "Информация о вакансии"; +} + +
+
+
+

@Model.JobTitle

+ +
+
+
+
+
+
Компания:
+
@Model.CompanyId
+ +
Тип занятости:
+
@Model.JobType
+ +
Зарплата:
+
@Model.Salary
+ +
Дата публикации:
+
@Model.CreatedAt.ToShortDateString()
+ +
Статус:
+
@Model.Status
+ +
Теги:
+
@Model.Tags
+
+
+
+
+
Требования:
+
@Html.Raw(Model.Requirements)
+ +
Обязанности:
+
@Html.Raw(Model.Responsibilities)
+ +
Описание:
+
@Html.Raw(Model.Description)
+
+
+
+
+
+
+ diff --git a/CandidateReviewClientApp/images/default-company-logo.png b/CandidateReviewClientApp/images/default-company-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1f71f2508e6fdf62bd151d66a3780e8e8b0beafd GIT binary patch literal 14369 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 CSAi7( literal 0 HcmV?d00001 diff --git a/CandidateReviewContracts/BindingModels/CompanyBindingModel.cs b/CandidateReviewContracts/BindingModels/CompanyBindingModel.cs index 7d8164b..9487fcd 100644 --- a/CandidateReviewContracts/BindingModels/CompanyBindingModel.cs +++ b/CandidateReviewContracts/BindingModels/CompanyBindingModel.cs @@ -1,4 +1,5 @@ -using CandidateReviewDataModels.Models; +using CandidateReviewContracts.ViewModels; +using CandidateReviewDataModels.Models; namespace CandidateReviewContracts.BindingModels { @@ -16,5 +17,7 @@ namespace CandidateReviewContracts.BindingModels public string? Contacts { get; set; } public int Id { get; set; } + + public List Vacancies { get; set; } = new(); } } diff --git a/CandidateReviewContracts/ViewModels/CompanyViewModel.cs b/CandidateReviewContracts/ViewModels/CompanyViewModel.cs index c44dfb9..a93f31e 100644 --- a/CandidateReviewContracts/ViewModels/CompanyViewModel.cs +++ b/CandidateReviewContracts/ViewModels/CompanyViewModel.cs @@ -16,5 +16,7 @@ namespace CandidateReviewContracts.ViewModels public string? Contacts { get; set; } public int Id { get; set; } + + public List Vacancies { get; set; } = new(); } } diff --git a/CandidateReviewDatabaseImplement/Models/Vacancy.cs b/CandidateReviewDatabaseImplement/Models/Vacancy.cs index 6796d1c..5e4c913 100644 --- a/CandidateReviewDatabaseImplement/Models/Vacancy.cs +++ b/CandidateReviewDatabaseImplement/Models/Vacancy.cs @@ -53,7 +53,7 @@ namespace CandidateReviewDatabaseImplement.Models Salary = model.Salary, Description = model.Description, Status = model.Status, - CreatedAt = model.CreatedAt, + CreatedAt = DateTime.Now.ToUniversalTime(), Tags = model.Tags }; } diff --git a/CandidateReviewRestApi/Controllers/CompanyController.cs b/CandidateReviewRestApi/Controllers/CompanyController.cs index e6fed6c..6ce23d5 100644 --- a/CandidateReviewRestApi/Controllers/CompanyController.cs +++ b/CandidateReviewRestApi/Controllers/CompanyController.cs @@ -2,7 +2,9 @@ using CandidateReviewContracts.BusinessLogicsContracts; using CandidateReviewContracts.SearchModels; using CandidateReviewContracts.ViewModels; +using CandidateReviewDataModels.Models; using Microsoft.AspNetCore.Mvc; +using System.Linq; namespace CandidateReviewRestApi.Controllers { @@ -12,10 +14,12 @@ namespace CandidateReviewRestApi.Controllers { private readonly ILogger _logger; private readonly ICompanyLogic _logic; - public CompanyController(ICompanyLogic logic, ILogger logger) + private readonly IVacancyLogic _vacancyLogic; + public CompanyController(ICompanyLogic logic, IVacancyLogic vacancyLogic, ILogger logger) { _logger = logger; _logic = logic; + _vacancyLogic = vacancyLogic; } [HttpGet] @@ -77,5 +81,17 @@ namespace CandidateReviewRestApi.Controllers throw; } } + + /*private CompanyBindingModel GetCompanyWithVacancies(CompanyBindingModel model) + { + var vacancies = _vacancyLogic.ReadList(new VacancySearchModel { CompanyId = model.Id }); + if (vacancies != null) + { + model.Vacancies = vacancies.Where(v => v.CompanyId == model.Id) + .Select(v => (IVacancyModel)v) + .ToList(); + } + return model; + }*/ } } diff --git a/CandidateReviewRestApi/Controllers/UserController.cs b/CandidateReviewRestApi/Controllers/UserController.cs index 29f0ae5..5a9b28d 100644 --- a/CandidateReviewRestApi/Controllers/UserController.cs +++ b/CandidateReviewRestApi/Controllers/UserController.cs @@ -80,5 +80,19 @@ namespace CandidateReviewRestApi.Controllers throw; } } + + [HttpPost] + public void Delete(UserBindingModel model) + { + try + { + _logic.Delete(model); + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка удаления профиля пользователя"); + throw; + } + } } } diff --git a/CandidateReviewRestApi/Controllers/VacancyController.cs b/CandidateReviewRestApi/Controllers/VacancyController.cs new file mode 100644 index 0000000..17deeff --- /dev/null +++ b/CandidateReviewRestApi/Controllers/VacancyController.cs @@ -0,0 +1,80 @@ +using CandidateReviewContracts.BindingModels; +using CandidateReviewContracts.BusinessLogicsContracts; +using CandidateReviewContracts.SearchModels; +using CandidateReviewContracts.ViewModels; +using Microsoft.AspNetCore.Mvc; + +namespace CandidateReviewRestApi.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + public class VacancyController : Controller + { + private readonly ILogger _logger; + private readonly IVacancyLogic _logic; + public VacancyController(IVacancyLogic logic, ILogger logger) + { + _logger = logger; + _logic = logic; + } + + [HttpGet] + public VacancyViewModel? Details(int id) + { + try + { + return _logic.ReadElement(new VacancySearchModel + { + Id = id + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка получения вакансии"); + throw; + } + } + + [HttpPost] + public void Create(VacancyBindingModel model) + { + try + { + _logic.Create(model); + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка создания вакансии"); + throw; + } + } + + [HttpPost] + public void Update(VacancyBindingModel model) + { + try + { + _logic.Update(model); + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка обновления вакансии"); + throw; + } + } + + [HttpDelete] + public void Delete(VacancyBindingModel model) + { + try + { + _logic.Delete(model); + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка удаления вакансии"); + throw; + } + } + } +}