From 874bce1113d18d56ac8398e0c0f925e1c9f0fe3d Mon Sep 17 00:00:00 2001 From: MahanCh Date: Sun, 7 Sep 2025 09:22:03 +0330 Subject: [PATCH] feat: add TicketController with CRUD operations for ticket management --- .../Client/Controllers/TicketController.cs | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 ServiceHost/Areas/Client/Controllers/TicketController.cs diff --git a/ServiceHost/Areas/Client/Controllers/TicketController.cs b/ServiceHost/Areas/Client/Controllers/TicketController.cs new file mode 100644 index 00000000..0e760916 --- /dev/null +++ b/ServiceHost/Areas/Client/Controllers/TicketController.cs @@ -0,0 +1,261 @@ +using System.Security.Claims; +using _0_Framework.Application; +using AccountManagement.Application.Contracts.Account; +using AccountManagement.Application.Contracts.Ticket; +using AccountManagement.Domain.ClientResponseAgg; +using AccountManagement.Domain.TicketAgg; +using CompanyManagment.App.Contracts.Workshop; +using Microsoft.AspNetCore.Mvc; +using ServiceHost.BaseControllers; + +namespace ServiceHost.Areas.Client.Controllers; + +public class TicketController : ClientBaseController +{ + private readonly IAuthHelper _authHelper; + private readonly ITicketApplication _ticketApplication; + private readonly IAccountApplication _accountApplication; + private readonly IPasswordHasher _passwordHasher; + private readonly IWorkshopApplication _workshopApplication; + + public TicketController( + ITicketApplication ticketApplication, + IAuthHelper authHelper, + IAccountApplication accountApplication, + IPasswordHasher passwordHasher, + IWorkshopApplication workshopApplication) + { + _ticketApplication = ticketApplication; + _authHelper = authHelper; + _accountApplication = accountApplication; + _passwordHasher = passwordHasher; + _workshopApplication = workshopApplication; + } + + [HttpGet] + public ActionResult GetTickets([FromQuery] TicketSearchModel searchModel) + { + var workshopHash = User.FindFirstValue("WorkshopSlug"); + var workshopId = _passwordHasher.SlugDecrypt(workshopHash); + + if (workshopId <= 0) + { + return NotFound(new { message = "Workshop not found" }); + } + + var hasListTickets = _ticketApplication.GetTicketsForClients(searchModel).Count > 0; + var workshopFullName = _authHelper.GetWorkshopName(); + var typesCountOfTicket = _ticketApplication.GetTypesCountOfTicketForClient(workshopId); + + return Ok(new + { + hasListTickets, + workshopFullName, + typesCountOfTicket + }); + } + + [HttpGet("data")] + public ActionResult GetTicketData( + [FromQuery] int pageIndex = 0, + [FromQuery] string status = "", + [FromQuery] string ticketNumber = "", + [FromQuery] string generalSearch = "") + { + var searchModel = new TicketSearchModel() + { + PageIndex = pageIndex, + Status = status, + TicketNumber = ticketNumber, + GeneralSearch = generalSearch, + }; + + var search = _ticketApplication.GetTicketsForClients(searchModel); + + return Ok(new + { + data = search, + pageIndex = search.Count + }); + } + + [HttpGet("{ticketId}/details")] + public ActionResult GetTicketDetails(long ticketId) + { + var result = _ticketApplication.GetDetailsForClient(ticketId); + + if (result == null) + { + return NotFound(new { message = "Ticket not found" }); + } + + result.WorkshopName = _workshopApplication.GetDetails(result.WorkshopId).WorkshopFullName; + + return Ok(result); + } + + [HttpGet("{ticketId}/messages")] + public ActionResult GetTicketMessages(long ticketId) + { + var result = _ticketApplication.GetDetailsForClient(ticketId); + + if (result == null) + { + return NotFound(new { message = "Ticket not found" }); + } + + return Ok(new + { + ticketDetail = result + }); + } + + [HttpPost] + public ActionResult CreateTicket([FromBody] CreateTicket command) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var accountInfo = _authHelper.CurrentAccountInfo(); + command.SenderId = _authHelper.CurrentAccountId(); + command.ContractingPartyName = accountInfo.Fullname; + command.WorkshopId = _passwordHasher.SlugDecrypt(User.FindFirstValue("WorkshopSlug")); + command.Description = command.Description?.Replace("\n", "
"); + command.SubAccountId = accountInfo.SubAccountId; + + var result = _ticketApplication.CreateTicket(command); + + return Ok(new + { + isSuccedded = result.IsSuccedded, + message = result.Message + }); + } + + [HttpPost("upload")] + public ActionResult UploadFile(IFormFile media) + { + if (media == null) + { + return BadRequest(new { message = "No file provided" }); + } + + var accountId = _authHelper.CurrentAccountId(); + var operation = _ticketApplication.UploadMedia(media, accountId); + + return Ok(new + { + isSuccedded = operation.IsSuccedded, + message = operation.Message, + id = operation.SendId, + }); + } + + [HttpDelete("media/{mediaId}")] + public ActionResult DeleteFile(long mediaId) + { + var operation = _ticketApplication.RemoveMedia(mediaId); + + return Ok(new + { + isSuccedded = operation.IsSuccedded, + message = operation.Message, + }); + } + + [HttpDelete("temp-files")] + public ActionResult RemoveAllTempFiles() + { + var accId = _authHelper.CurrentAccountId(); + var operation = new OperationResult(); + _ticketApplication.RemoveTempUploadedFiles(accId); + operation.Succcedded(); + + return Ok(new + { + isSuccedded = operation.IsSuccedded, + message = operation.Message, + }); + } + + [HttpPost("{ticketId}/response")] + public ActionResult SaveClientResponse(long ticketId, [FromBody] ResponseTicket command) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + command.Response = command.Response?.Replace("\n", "
"); + + var result = _ticketApplication.ClientResponseTicket(command); + + return Ok(new + { + isSuccedded = result.IsSuccedded, + message = result.Message + }); + } + + [HttpGet("file")] + public IActionResult GetFile([FromQuery] string filePath, [FromQuery] long id) + { + if (string.IsNullOrEmpty(filePath)) + { + return NotFound(new { message = "File path is required" }); + } + + if (!System.IO.File.Exists(filePath)) + { + return NotFound(new { message = "File not found" }); + } + + var contentType = Tools.GetContentTypeImage(Path.GetExtension(filePath)); + if (string.IsNullOrWhiteSpace(contentType)) + { + byte[] fileContent = System.IO.File.ReadAllBytes(filePath); + var extension = Tools.GetContentType(Path.GetExtension(filePath)); + return File(fileContent, extension, $"Task_{id}{Path.GetExtension(filePath)}"); + } + else + { + return PhysicalFile(filePath, contentType); + } + } + + [HttpGet("voice")] + public IActionResult GetVoice([FromQuery] string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + return NotFound(new { message = "File path is required" }); + } + + if (!System.IO.File.Exists(filePath)) + { + return NotFound(new { message = "Voice file not found" }); + } + + byte[] voiceContent = System.IO.File.ReadAllBytes(filePath); + return File(voiceContent, "audio/ogg", "Task_voice.ogg"); + } + + [HttpGet("picture")] + public IActionResult GetPicture([FromQuery] string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + return NotFound(new { message = "File path is required" }); + } + + if (!System.IO.File.Exists(filePath)) + { + return NotFound(new { message = "Picture not found" }); + } + + var contentType = Tools.GetContentTypeImage(Path.GetExtension(filePath)); + return PhysicalFile(filePath, contentType); + } +}