328 lines
14 KiB
C#
328 lines
14 KiB
C#
using Fitness_core_BE.Helpers;
|
|
using Fitness_core_BE.Infra.UoW;
|
|
using Fitness_core_BE.Interfaces;
|
|
using Fitness_core_BE.Interfaces.Services;
|
|
using Fitness_core_BE.Models;
|
|
using System.Net.Sockets;
|
|
|
|
namespace Fitness_core_BE.Services.Ticket
|
|
{
|
|
public class TicketService : ITicketService
|
|
{
|
|
private readonly ITicketRepository _repository;
|
|
private readonly UserRoleHelper _userRoleHelper;
|
|
private readonly IUnitOfWork _uow;
|
|
private readonly IActivityLogService _activityLogService;
|
|
|
|
public TicketService(ITicketRepository repository, UserRoleHelper userRoleHelper, IUnitOfWork uow, IActivityLogService activityLogService)
|
|
{
|
|
_repository = repository;
|
|
_userRoleHelper = userRoleHelper;
|
|
_uow = uow;
|
|
_activityLogService = activityLogService;
|
|
}
|
|
|
|
public async Task<object> GetTicketList(object payload, HttpContext httpContext)
|
|
{
|
|
try
|
|
{
|
|
var tickets = await _repository.GetTicketListAsync();
|
|
return tickets;
|
|
}
|
|
catch
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> GetTicketByUserId(object payload, HttpContext httpContext)
|
|
{
|
|
try
|
|
{
|
|
// Guid userId = Guid.TryParse(httpContext.Items["UserId"]?.ToString(), out var parsedUserId)
|
|
// ? parsedUserId
|
|
// : throw new UnauthorizedAccessException("User ID not found in context");
|
|
// Guid userId = Guid.Parse("a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d"); // Testing purposes
|
|
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
|
|
|
|
var tickets = await _repository.GetTicketByUserIdAsync(userId);
|
|
return tickets;
|
|
}
|
|
catch
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> GetTicketById(object payload, HttpContext httpContext)
|
|
{
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
Guid ticketId = Guid.Parse(data["ticketId"].GetString()!);
|
|
|
|
var ticket = await _repository.GetByTicketIdAsync(ticketId);
|
|
|
|
if (ticket == null)
|
|
throw new KeyNotFoundException($"Ticket with ID {ticketId} not found");
|
|
if (ticket.UserId != DataExtractor.GetUserIdFromContext(httpContext))
|
|
throw new UnauthorizedAccessException("You do not have permission to view this ticket.");
|
|
|
|
return ticket;
|
|
}
|
|
catch
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> GetTicketCategoryList(object payload, HttpContext httpContext)
|
|
{
|
|
try
|
|
{
|
|
var categories = await _repository.GetTicketCategoryListAsync();
|
|
return categories;
|
|
}
|
|
catch
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> CreateTicket(object payload, HttpContext httpContext)
|
|
{
|
|
await _uow.BeginAsync();
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
|
|
// Parse userId from context
|
|
// Guid userId = Guid.TryParse(httpContext.Items["UserId"]?.ToString(), out var parsedUserId)
|
|
// ? parsedUserId
|
|
// : throw new UnauthorizedAccessException("User ID not found in context");
|
|
// Guid userId = Guid.Parse("a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d"); // Testing purposes
|
|
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
|
|
|
|
var ticket = new Fitness_core_BE.Models.Ticket
|
|
{
|
|
MainReason = data["mainReason"].GetString()!,
|
|
FKTicketCategoryId = data.ContainsKey("ticketCategoryId") && !string.IsNullOrEmpty(data["ticketCategoryId"].GetString())
|
|
? Guid.Parse(data["ticketCategoryId"].GetString()!)
|
|
: (Guid?)null,
|
|
TicketStatus = data.ContainsKey("ticketStatus") && !string.IsNullOrEmpty(data["ticketStatus"].GetString())
|
|
? Enum.Parse<Fitness_core_BE.Models.Enums.TicketStatusEnum>(data["ticketStatus"].GetString()!)
|
|
: Fitness_core_BE.Models.Enums.TicketStatusEnum.Pending,
|
|
FKUserId = userId,
|
|
CreateDate = DateTime.UtcNow
|
|
};
|
|
|
|
await _repository.CreateTicketAsync(ticket);
|
|
|
|
await _uow.CommitAsync();
|
|
|
|
// Log activity - non-blocking, failures won't crash the operation
|
|
try
|
|
{
|
|
await _activityLogService.LogActivity(new { description = $"Ticket created with ID {ticket.TicketId}" }, httpContext);
|
|
}
|
|
catch (Exception logEx)
|
|
{
|
|
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
|
|
}
|
|
|
|
return ticket;
|
|
}
|
|
catch
|
|
{
|
|
await _uow.RollbackAsync();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> UpdateTicket(object payload, HttpContext httpContext)
|
|
{
|
|
await _uow.BeginAsync();
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
Guid ticketId = Guid.Parse(data["ticketId"].GetString()!);
|
|
var ticket = await _repository.GetByTicketIdAsync(ticketId) ?? throw new KeyNotFoundException($"Ticket with ID {ticketId} not found.");
|
|
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
|
|
if (ticket.UserId != userId)
|
|
throw new UnauthorizedAccessException("You do not have permission to update this ticket.");
|
|
|
|
ticket.MainReason = data.ContainsKey("mainReason") && !string.IsNullOrEmpty(data["mainReason"].GetString())
|
|
? data["mainReason"].GetString()!
|
|
: ticket.MainReason;
|
|
ticket.TicketCategoryId = data.ContainsKey("ticketCategoryId") && !string.IsNullOrEmpty(data["ticketCategoryId"].GetString())
|
|
? Guid.Parse(data["ticketCategoryId"].GetString()!)
|
|
: ticket.TicketCategoryId;
|
|
|
|
await _repository.UpdateTicketAsync(ticket);
|
|
await _uow.CommitAsync();
|
|
return ticket;
|
|
}
|
|
catch
|
|
{
|
|
await _uow.RollbackAsync();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> SendMessage(object payload, HttpContext httpContext)
|
|
{
|
|
await _uow.BeginAsync();
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
|
|
// Parse userId from context
|
|
// Guid userId = Guid.TryParse(httpContext.Items["UserId"]?.ToString(), out var parsedUserId)
|
|
// ? parsedUserId
|
|
// : throw new UnauthorizedAccessException("User ID not found in context");
|
|
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
|
|
|
|
Guid? adminId = await _userRoleHelper.GetAdminId(userId);
|
|
bool isAdmin = adminId.HasValue;
|
|
Guid ticketId = Guid.Parse(data["ticketId"].GetString()!);
|
|
var ticket = await _repository.GetByTicketIdAsync(ticketId)
|
|
?? throw new KeyNotFoundException($"Ticket with ID {ticketId} not found.");
|
|
if (!isAdmin && ticket.UserId != userId)
|
|
throw new UnauthorizedAccessException("You do not have permission to message this ticket.");
|
|
|
|
var ticketMessage = new TicketMessage
|
|
{
|
|
Message = data["message"].GetString()!,
|
|
FKTicketId = ticketId,
|
|
IsSentByAdmin = isAdmin,
|
|
AttachmentURL = data.ContainsKey("attachmentURL") ? data["attachmentURL"].GetString() : null,
|
|
FKDocumentId = DataExtractor.GetNullableInt(data, "documentId"),
|
|
CreateDate = DateTime.UtcNow
|
|
};
|
|
|
|
// First admin reply moves a ticket from Pending to Open — nothing previously
|
|
// set this transition, so tickets sat in Pending forever once created.
|
|
if (isAdmin)
|
|
{
|
|
if (ticket.TicketStatus == Models.Enums.TicketStatusEnum.Pending)
|
|
{
|
|
await _repository.UpdateTicketStatusAsync(ticketId, Models.Enums.TicketStatusEnum.Open);
|
|
}
|
|
}
|
|
|
|
//Guid ticCatId = DataExtractor.GetRequiredGuid(data, "ticketCategoryId");
|
|
|
|
//Guid? TcatId = await _repository.GetTicketCatId("Chat");
|
|
|
|
//if (ticCatId == TcatId)
|
|
//{
|
|
|
|
//}
|
|
|
|
await _repository.SendMessageAsync(ticketMessage);
|
|
await _uow.CommitAsync();
|
|
return ticketMessage;
|
|
}
|
|
catch
|
|
{
|
|
await _uow.RollbackAsync();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> SubmitTicketFeedback(object payload, HttpContext httpContext)
|
|
{
|
|
await _uow.BeginAsync();
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
Guid ticketId = Guid.Parse(data["ticketId"].GetString()!);
|
|
int rating = DataExtractor.GetRequiredInt(data, "rating");
|
|
string? feedback = data.TryGetValue("feedback", out var feedbackContent) && !string.IsNullOrEmpty(feedbackContent.GetString())
|
|
? DataExtractor.GetOptionalString(data, "feedback")
|
|
: null;
|
|
|
|
if (rating < 1 || rating > 5)
|
|
throw new ArgumentException("Rating must be between 1 and 5.");
|
|
|
|
var ticket = await _repository.GetByTicketIdAsync(ticketId) ?? throw new KeyNotFoundException($"Ticket with ID {ticketId} not found.");
|
|
if (ticket.UserId != DataExtractor.GetUserIdFromContext(httpContext))
|
|
throw new UnauthorizedAccessException("You do not have permission to submit feedback for this ticket.");
|
|
if (ticket.TicketStatus != Models.Enums.TicketStatusEnum.Closed)
|
|
throw new InvalidOperationException("Feedback can only be submitted after the ticket is closed.");
|
|
|
|
await _repository.SubmitTicketFeedbackAsync(ticketId, rating, feedback);
|
|
await _uow.CommitAsync();
|
|
return new { message = $"The feedback for ticket with ID {ticketId} submitted successfully." };
|
|
|
|
}
|
|
catch
|
|
{
|
|
await _uow.RollbackAsync();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> CloseTicket(object payload, HttpContext httpContext)
|
|
{
|
|
await _uow.BeginAsync();
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
|
|
Guid ticketId = Guid.Parse(data["ticketId"].GetString()!);
|
|
var ticket = await _repository.GetByTicketIdAsync(ticketId)
|
|
?? throw new KeyNotFoundException($"Ticket with ID {ticketId} not found.");
|
|
if (ticket.UserId != DataExtractor.GetUserIdFromContext(httpContext))
|
|
throw new UnauthorizedAccessException("You do not have permission to close this ticket.");
|
|
|
|
await _repository.UpdateTicketStatusAsync(ticketId, Fitness_core_BE.Models.Enums.TicketStatusEnum.Closed);
|
|
await _uow.CommitAsync();
|
|
|
|
var updatedTicket = await _repository.GetByTicketIdAsync(ticketId);
|
|
|
|
if (updatedTicket == null)
|
|
throw new KeyNotFoundException($"Ticket with ID {ticketId} not found after update");
|
|
|
|
return updatedTicket;
|
|
}
|
|
catch
|
|
{
|
|
await _uow.RollbackAsync();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<object> DeleteTicket(object payload, HttpContext httpContext)
|
|
{
|
|
await _uow.BeginAsync();
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
|
|
Guid ticketId = Guid.Parse(data["ticketId"].GetString()!);
|
|
var ticket = await _repository.GetByTicketIdAsync(ticketId) ?? throw new KeyNotFoundException($"Ticket with ID {ticketId} not found.");
|
|
|
|
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
|
|
if (ticket.UserId != userId)
|
|
throw new UnauthorizedAccessException("You do not have permission to delete this ticket.");
|
|
|
|
await _repository.DeleteAsync(ticketId);
|
|
await _uow.CommitAsync();
|
|
|
|
var deletedTicket = await _repository.GetByTicketIdAsync(ticketId);
|
|
|
|
if (deletedTicket == null)
|
|
return true;
|
|
|
|
throw new ArgumentException($"Ticket with ID {ticketId} has not been deleted");
|
|
}
|
|
catch
|
|
{
|
|
await _uow.RollbackAsync();
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|