Центральным объектом в системе helpdesk является заявка на обслуживание, сообщение о проблеме, что-то поломалось, что-то работает не так. При построении системы надо разграничить права и круг действий всех возможных ролей в системе. Итак, все группы пользователей у нас смогут создавать новые заявки. Далее все группы пользователей могут смотреть список своих заявок, а администратор может смотреть все заявки.
Кроме того, модератору добавляется функция распределять все новые заявки по исполнителям. А исполнители могут просматривать список назначенных заявок и изменять их статус - вплоть до самого завершения.
Итак, добавим контроллер RequestController, который у нас будет управлять системой заявок:
using System; using System.Collections.Generic; using System.Linq; using System.Data.Entity; using System.Web; using System.Web.Mvc; using HelpDeskTrain.Models; namespace HelpDeskTrain.Controllers { [Authorize] public class RequestController : Controller { HelpdeskContext db = new HelpdeskContext(); public ActionResult Index() { return View(); } } }
Также используем атрибут [Authorize]
, чтобы запретить неавторизированный доступ. Метод Index мы потом изменим - он у нас буде выводить
все заявки для текущего пользователя. А пока настроим маршрутизацию, чтобы при обращении к приложению по умолчанию шло обращение к этому методу.
Перейдем в проекте в папку App_Start и откроем в ней файл RouteConfig.cs, который содержит определение маршрутов. Сейчас в нем есть
класс RouteConfig
, который выглядит следующим образом:
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
Параметр defaults
в методе routes.MapRoute
указывает на маршрут по умолчанию. В данном случае по умолчанию
приложение обращается к методу Index контроллера Home. Теперь изменим название контроллера на Request:
defaults: new { controller = "Request", action = "Index", id = UrlParameter.Optional }
Также необходимо, чтобы при удачном логине в методе Login контроллера Account также был предусмотрен редирект на метод Index контроллера RequestController, а не контроллера HomeController.
Теперь по умолчанию при логине пользователь сразу будет попадать на страницу со своим заявками.
Теперь добавим первую общую для всех групп функциональность - создание новой заявки. Итак, добавим в контроллер следующий метод Create:
[HttpGet] public ActionResult Create() { // получаем текущего пользователя User user = db.Users.Where(m => m.Login == HttpContext.User.Identity.Name).FirstOrDefault(); if (user != null) { // получаем набор кабинетов для департамента, в котором работает пользователь var cabs = from cab in db.Activs where cab.DepartmentId == user.DepartmentId select cab; ViewBag.Cabs = new SelectList(cabs, "Id", "CabNumber"); ViewBag.Categories = new SelectList(db.Categories, "Id", "Name"); return View(); } return RedirectToAction("LogOff", "Account"); } // Создание новой заявки [HttpPost] public ActionResult Create(Request request, HttpPostedFileBase error) { // получаем текущего пользователя User user = db.Users.Where(m => m.Login == HttpContext.User.Identity.Name).FirstOrDefault(); if(user==null) { return RedirectToAction("LogOff", "Account"); } if (ModelState.IsValid) { // указываем статус Открыта у заявки request.Status = (int)RequestStatus.Open; //получаем время открытия DateTime current = DateTime.Now; //Создаем запись о жизненном цикле заявки Lifecycle newLifecycle = new Lifecycle() { Opened = current }; request.Lifecycle = newLifecycle; //Добавляем жизненный цикл заявки db.Lifecycles.Add(newLifecycle); // указываем пользователя заявки request.UserId = user.Id; // если получен файл if (error != null) { // Получаем расширение string ext = error.FileName.Substring(error.FileName.LastIndexOf('.')); // сохраняем файл по определенному пути на сервере string path = current.ToString("dd/MM/yyyy H:mm:ss").Replace(":", "_").Replace("/", ".") + ext; error.SaveAs(Server.MapPath("~/Files/" + path)); request.File = path; } //Добавляем заявку db.Requests.Add(request); db.SaveChanges(); return RedirectToAction("Index"); } return View(request); }
В get-версии метода Create сначала получаем текущего пользователя через объект HttpContext.User.Identity. Если пользователь по какой-то причине не определен, делаем логаут. Если же определен, получаем кабинеты департамента пользователя, а также категории проблем и передаем из в представление через ViewBag.
В post-версии мы получаем два параметра: Request (сам объект заявки) и HttpPostedFileBase (данный объект у нас представляет переданный вместе с заявкой файл с ошибкой, например, какое-нибудь изображение).
В самом методе мы также получаем опять же текущего пользователя и его id устанавливаем у заявки. Устанавливаем ее статус, объект жизненного цикла и сохраняем переданный файл, если он, конечно же, был передан. При этом сохранение идет в папку Files, которая должна быть в проекте в приложения. Для этого мы можем добавить папку Files в наш проект, а при развертывании приложения также не надо забывать, что финальное приложение также должно содержать данную папку. В этой папке будут храниться переданные файлы, и для каждого файла имя будет образовано от времени передачи. И после всех этих этапов заявка попадает в базу данных.
И создадим представление Create, которое будет выглядеть следующим образом:
@model HelpDeskTrain.Models.Request @{ ViewBag.Title = "Создать заявку"; } <h2>Cоздать заявку</h2> @using (Html.BeginForm("Create", "Request", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.ValidationSummary(true) <fieldset> <legend>Новая заявка</legend> <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label"> @Html.LabelFor(model => model.Description) </div> <div class="editor-field"> @Html.EditorFor(model => model.Description) @Html.ValidationMessageFor(model => model.Description) </div> <div class="editor-label"> @Html.LabelFor(model => model.Priority) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.Priority, new[] { new SelectListItem() { Text = "Низкий", Value = "1" }, new SelectListItem() { Text = "Средний", Value = "2" }, new SelectListItem() { Text = "Высокий", Value = "3" }, new SelectListItem() { Text = "Критичный", Value = "4" }}) @Html.ValidationMessageFor(model => model.Priority) </div> <div class="editor-label"> @Html.LabelFor(model => model.ActivId) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.ActivId, ViewBag.Cabs as SelectList) @Html.ValidationMessageFor(model => model.ActivId) </div> <div class="editor-label"> @Html.LabelFor(model => model.CategoryId) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.CategoryId, ViewBag.Categories as SelectList) @Html.ValidationMessageFor(model => model.CategoryId) </div> <div class="editor-label"> @Html.LabelFor(model => model.File) </div> <div class="editor-field"> <input type="file" id="error" name="error" /> </div> <div class="editor-label"> @Html.LabelFor(model => model.Comment) </div> <div class="editor-field"> @Html.EditorFor(model => model.Comment) @Html.ValidationMessageFor(model => model.Comment) </div> <p> <input type="submit" value="Сохранить заявку" /> </p> </fieldset> } @section Scripts { @Scripts.Render("~/bundles/jqueryval") }