using Microsoft.EntityFrameworkCore; using PnvPanel.Application.Auth; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; using PnvPanel.Domain.Support; namespace PnvPanel.Application.Support.CreateRoleRequest; public sealed class CreateRoleRequestTicketCommandHandler( IAppDbContext dbContext, IRoleService roleService, IRealtimeNotifier notifier, ITelegramNotifier telegramNotifier, ICurrentUser currentUser ) : ICommandHandler> { // Совпадает со значением Infrastructure.Identity.RoleNames.Admin — Application не может ссылаться // на Infrastructure (направление зависимостей), поэтому системное имя роли продублировано здесь. private const string AdminRoleName = "admin"; public async Task> Handle( CreateRoleRequestTicketCommand command, CancellationToken cancellationToken ) { if (currentUser.UserId is not { } userId) return Result.Failure(AuthErrors.Unauthorized); var hasPending = await dbContext.SupportTickets.AnyAsync( t => t.UserId == userId && t.Type == TicketType.RoleRequest && t.Status == TicketStatus.Open, cancellationToken ); if (hasPending) return Result.Failure(SupportErrors.RoleRequestAlreadyPending); SupportTicket ticket; string? requestedRoleName = null; if (command.ExistingRoleId is { } roleId) { var roles = await roleService.ListRolesAsync(cancellationToken); var role = roles.FirstOrDefault(r => r.Id == roleId); if (role is null) return Result.Failure(SupportErrors.RoleNotFound); if (role.Name.Equals(AdminRoleName, StringComparison.OrdinalIgnoreCase)) return Result.Failure(SupportErrors.CannotRequestAdminRole); ticket = SupportTicket.CreateRoleRequestForExistingRole(userId, roleId); requestedRoleName = role.Name; } else { ticket = SupportTicket.CreateRoleRequestForNewRole( userId, command.NewRoleName!, command.NewRoleMaxConfigs!.Value, command.NewRoleMaxIpLimit!.Value ); } dbContext.SupportTickets.Add(ticket); var comment = TicketComment.Create(ticket.Id, userId, command.Justification); dbContext.TicketComments.Add(comment); var userName = currentUser.UserName ?? userId.ToString(); var roleDescription = requestedRoleName ?? $"новая роль «{command.NewRoleName}» (конфигов: {command.NewRoleMaxConfigs}, IP: {command.NewRoleMaxIpLimit})"; await notifier.NotifyTicketCreatedAsync( ticket.Id, userId, userName, ticket.Type, cancellationToken ); await telegramNotifier.NotifyAdminsRoleRequestCreatedAsync( ticket.Id, userName, roleDescription, command.Justification, cancellationToken ); var commentDto = new TicketCommentDto( comment.Id, userId, userName, comment.Body, comment.CreatedAt, [] ); var dto = new TicketDetailDto( ticket.Id, ticket.UserId, userName, ticket.Type, ticket.Status, ticket.RequestedRoleId, requestedRoleName, ticket.ProposedRoleName, ticket.ProposedMaxConfigs, ticket.ProposedMaxIpLimit, ticket.RequestedDays, ticket.CreatedAt, [commentDto] ); return Result.Success(dto); } }