first commit
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user