using Microsoft.EntityFrameworkCore; using PnvPanel.Application.Auth; using PnvPanel.Application.Common.Concurrency; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; using PnvPanel.Application.Support; using PnvPanel.Domain.Audit; using PnvPanel.Domain.Support; namespace PnvPanel.Application.Admin.Support; /// Проверка статуса + Close() — под AdvisoryLock (по Id тикета), см. /// ApproveRoleRequestCommandHandler для полного обоснования. public sealed class RejectRoleRequestCommandHandler( IAppDbContext dbContext, IRealtimeNotifier notifier, ITelegramNotifier telegramNotifier, ICurrentUser currentUser ) : ICommandHandler { public async Task Handle( RejectRoleRequestCommand command, CancellationToken cancellationToken ) { if (currentUser.UserId is not { } adminId) return Result.Failure(AuthErrors.Unauthorized); var claimed = await AdvisoryLock.RunAsync( dbContext, command.TicketId, async lockedCancellationToken => { var fresh = await dbContext.SupportTickets.FirstOrDefaultAsync( t => t.Id == command.TicketId, lockedCancellationToken ); if (fresh is null) return Result.Failure(SupportErrors.NotFound); if (fresh.Type != TicketType.RoleRequest) return Result.Failure(SupportErrors.NotRoleRequest); if (fresh.Status == TicketStatus.Closed) return Result.Failure(SupportErrors.AlreadyClosed); if (!string.IsNullOrWhiteSpace(command.Reason)) dbContext.TicketComments.Add(TicketComment.Create(fresh.Id, adminId, command.Reason)); fresh.Close(); return Result.Success(fresh); }, cancellationToken ); if (!claimed.IsSuccess) return Result.Failure(claimed.Error); var ticket = claimed.Value; dbContext.AuditLogs.Add( AuditLog.Create( adminId, "RoleRequestRejected", "SupportTicket", ticket.Id.ToString(), metadata: null, AuditSource.Web ) ); await notifier.NotifyTicketUpdatedAsync(ticket.Id, ticket.UserId, cancellationToken); await telegramNotifier.NotifyUserAsync( ticket.UserId, "❌ Ваша заявка на роль отклонена.", $"/support?ticket={ticket.Id}", cancellationToken ); return Result.Success(); } }