225 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			225 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using GestionIntegral.Api.Data; // Para DbConnectionFactory | ||
|  | using GestionIntegral.Api.Data.Repositories.Usuarios; | ||
|  | using GestionIntegral.Api.Dtos.Usuarios; | ||
|  | using GestionIntegral.Api.Models.Usuarios; | ||
|  | using Microsoft.Extensions.Logging; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Data; // Para IsolationLevel | ||
|  | using System.Linq; | ||
|  | using System.Threading.Tasks; | ||
|  | 
 | ||
|  | namespace GestionIntegral.Api.Services.Usuarios | ||
|  | { | ||
|  |     public class PerfilService : IPerfilService | ||
|  |     { | ||
|  |         private readonly IPerfilRepository _perfilRepository; | ||
|  |         private readonly DbConnectionFactory _connectionFactory; // Necesario para transacciones | ||
|  |         private readonly ILogger<PerfilService> _logger; | ||
|  |         private readonly IPermisoRepository _permisoRepository; | ||
|  | 
 | ||
|  |         public PerfilService(IPerfilRepository perfilRepository, IPermisoRepository permisoRepository, DbConnectionFactory connectionFactory, ILogger<PerfilService> logger) | ||
|  |         { | ||
|  |             _perfilRepository = perfilRepository; | ||
|  |             _permisoRepository = permisoRepository; | ||
|  |             _connectionFactory = connectionFactory; // Inyectar DbConnectionFactory | ||
|  |             _logger = logger; | ||
|  |         } | ||
|  | 
 | ||
|  |         private PerfilDto MapToDto(Perfil perfil) => new PerfilDto | ||
|  |         { | ||
|  |             Id = perfil.Id, | ||
|  |             NombrePerfil = perfil.NombrePerfil, | ||
|  |             Descripcion = perfil.Descripcion | ||
|  |         }; | ||
|  | 
 | ||
|  |         public async Task<IEnumerable<PerfilDto>> ObtenerTodosAsync(string? nombreFilter) | ||
|  |         { | ||
|  |             var perfiles = await _perfilRepository.GetAllAsync(nombreFilter); | ||
|  |             return perfiles.Select(MapToDto); | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<PerfilDto?> ObtenerPorIdAsync(int id) | ||
|  |         { | ||
|  |             var perfil = await _perfilRepository.GetByIdAsync(id); | ||
|  |             return perfil == null ? null : MapToDto(perfil); | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(PerfilDto? Perfil, string? Error)> CrearAsync(CreatePerfilDto createDto, int idUsuario) | ||
|  |         { | ||
|  |             if (await _perfilRepository.ExistsByNameAsync(createDto.NombrePerfil)) | ||
|  |             { | ||
|  |                 return (null, "El nombre del perfil ya existe."); | ||
|  |             } | ||
|  | 
 | ||
|  |             var nuevoPerfil = new Perfil { NombrePerfil = createDto.NombrePerfil, Descripcion = createDto.Descripcion }; | ||
|  | 
 | ||
|  |             using var connection = _connectionFactory.CreateConnection(); | ||
|  |             if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); | ||
|  |             using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); | ||
|  | 
 | ||
|  |             try | ||
|  |             { | ||
|  |                 var perfilCreado = await _perfilRepository.CreateAsync(nuevoPerfil, idUsuario, transaction); | ||
|  |                 if (perfilCreado == null) throw new DataException("La creación en el repositorio devolvió null."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("Perfil ID {IdPerfil} creado por Usuario ID {IdUsuario}.", perfilCreado.Id, idUsuario); | ||
|  |                 return (MapToDto(perfilCreado), null); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback CrearAsync Perfil."); } | ||
|  |                 _logger.LogError(ex, "Error CrearAsync Perfil. Nombre: {NombrePerfil}", createDto.NombrePerfil); | ||
|  |                 return (null, $"Error interno al crear el perfil: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdatePerfilDto updateDto, int idUsuario) | ||
|  |         { | ||
|  |             // Verificar existencia ANTES de iniciar la transacción para evitar trabajo innecesario | ||
|  |             var perfilExistente = await _perfilRepository.GetByIdAsync(id); | ||
|  |             if (perfilExistente == null) return (false, "Perfil no encontrado."); | ||
|  | 
 | ||
|  |             if (await _perfilRepository.ExistsByNameAsync(updateDto.NombrePerfil, id)) | ||
|  |             { | ||
|  |                 return (false, "El nombre del perfil ya existe para otro registro."); | ||
|  |             } | ||
|  | 
 | ||
|  |             var perfilAActualizar = new Perfil { Id = id, NombrePerfil = updateDto.NombrePerfil, Descripcion = updateDto.Descripcion }; | ||
|  | 
 | ||
|  |             using var connection = _connectionFactory.CreateConnection(); | ||
|  |             if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); | ||
|  |             using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); | ||
|  | 
 | ||
|  |             try | ||
|  |             { | ||
|  |                 var actualizado = await _perfilRepository.UpdateAsync(perfilAActualizar, idUsuario, transaction); | ||
|  |                 if (!actualizado) | ||
|  |                 { | ||
|  |                     // El repositorio ahora lanza KeyNotFoundException si no lo encuentra DENTRO de la tx. | ||
|  |                     // Si devuelve false sin excepción, podría ser otro error. | ||
|  |                     throw new DataException("La operación de actualización no afectó ninguna fila o falló."); | ||
|  |                 } | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("Perfil ID {IdPerfil} actualizado por Usuario ID {IdUsuario}.", id, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException knfex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback ActualizarAsync Perfil (KeyNotFound)."); } | ||
|  |                 _logger.LogWarning(knfex, "Intento de actualizar Perfil ID: {Id} no encontrado dentro de la transacción.", id); | ||
|  |                 return (false, "Perfil no encontrado."); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback ActualizarAsync Perfil."); } | ||
|  |                 _logger.LogError(ex, "Error ActualizarAsync Perfil ID: {Id}", id); | ||
|  |                 return (false, $"Error interno al actualizar el perfil: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario) | ||
|  |         { | ||
|  |             // Verificar existencia y si está en uso ANTES de la transacción | ||
|  |             var perfilExistente = await _perfilRepository.GetByIdAsync(id); | ||
|  |             if (perfilExistente == null) return (false, "Perfil no encontrado."); | ||
|  | 
 | ||
|  |             if (await _perfilRepository.IsInUseAsync(id)) | ||
|  |             { | ||
|  |                 return (false, "No se puede eliminar. El perfil está asignado a usuarios o permisos."); | ||
|  |             } | ||
|  | 
 | ||
|  |             using var connection = _connectionFactory.CreateConnection(); | ||
|  |             if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); | ||
|  |             using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); | ||
|  | 
 | ||
|  |             try | ||
|  |             { | ||
|  |                 var eliminado = await _perfilRepository.DeleteAsync(id, idUsuario, transaction); | ||
|  |                 if (!eliminado) | ||
|  |                 { | ||
|  |                     throw new DataException("La operación de eliminación no afectó ninguna fila o falló."); | ||
|  |                 } | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("Perfil ID {IdPerfil} eliminado por Usuario ID {IdUsuario}.", id, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException knfex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback EliminarAsync Perfil (KeyNotFound)."); } | ||
|  |                 _logger.LogWarning(knfex, "Intento de eliminar Perfil ID: {Id} no encontrado dentro de la transacción.", id); | ||
|  |                 return (false, "Perfil no encontrado."); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback EliminarAsync Perfil."); } | ||
|  |                 _logger.LogError(ex, "Error EliminarAsync Perfil ID: {Id}", id); | ||
|  |                 return (false, $"Error interno al eliminar el perfil: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  |         public async Task<IEnumerable<PermisoAsignadoDto>> ObtenerPermisosAsignadosAsync(int idPerfil) | ||
|  |         { | ||
|  |             // 1. Obtener todos los permisos definidos en el sistema | ||
|  |             var todosLosPermisos = await _permisoRepository.GetAllAsync(null, null); // Sin filtros | ||
|  | 
 | ||
|  |             // 2. Obtener los IDs de los permisos actualmente asignados a este perfil | ||
|  |             var idsPermisosAsignados = (await _perfilRepository.GetPermisoIdsByPerfilIdAsync(idPerfil)).ToHashSet(); | ||
|  | 
 | ||
|  |             // 3. Mapear a DTO, marcando 'Asignado' | ||
|  |             var resultado = todosLosPermisos.Select(p => new PermisoAsignadoDto | ||
|  |             { | ||
|  |                 Id = p.Id, | ||
|  |                 Modulo = p.Modulo, | ||
|  |                 DescPermiso = p.DescPermiso, | ||
|  |                 CodAcc = p.CodAcc, | ||
|  |                 Asignado = idsPermisosAsignados.Contains(p.Id) | ||
|  |             }).OrderBy(p => p.Modulo).ThenBy(p => p.DescPermiso); // Ordenar para la UI | ||
|  | 
 | ||
|  |             return resultado; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> ActualizarPermisosAsignadosAsync( | ||
|  |             int idPerfil, | ||
|  |             ActualizarPermisosPerfilRequestDto request, | ||
|  |             int idUsuarioModificador) | ||
|  |         { | ||
|  |             // Validación: Verificar que el perfil exista | ||
|  |             var perfil = await _perfilRepository.GetByIdAsync(idPerfil); | ||
|  |             if (perfil == null) | ||
|  |             { | ||
|  |                 return (false, "Perfil no encontrado."); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Validación opcional: Verificar que todos los IDs de permisos en la solicitud sean válidos | ||
|  |             if (request.PermisosIds != null && request.PermisosIds.Any()) | ||
|  |             { | ||
|  |                 var permisosValidos = await _permisoRepository.GetPermisosByIdsAsync(request.PermisosIds); | ||
|  |                 if (permisosValidos.Count() != request.PermisosIds.Distinct().Count()) // Compara counts para detectar IDs inválidos | ||
|  |                 { | ||
|  |                     return (false, "Uno o más IDs de permisos proporcionados son inválidos."); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             using var connection = _connectionFactory.CreateConnection(); | ||
|  |             if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); | ||
|  |             using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); | ||
|  | 
 | ||
|  |             try | ||
|  |             { | ||
|  |                 // El idUsuarioModificador no se usa directamente en UpdatePermisosByPerfilIdAsync | ||
|  |                 // porque no estamos grabando en una tabla _H para gral_PermisosPerfiles. | ||
|  |                 // Si se necesitara auditoría de esta acción específica, se debería añadir. | ||
|  |                 await _perfilRepository.UpdatePermisosByPerfilIdAsync(idPerfil, request.PermisosIds ?? new List<int>(), transaction); | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("Permisos actualizados para Perfil ID {IdPerfil} por Usuario ID {IdUsuarioModificador}.", idPerfil, idUsuarioModificador); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback ActualizarPermisosAsignadosAsync."); } | ||
|  |                 _logger.LogError(ex, "Error al actualizar permisos para Perfil ID: {IdPerfil}", idPerfil); | ||
|  |                 return (false, $"Error interno al actualizar los permisos del perfil: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |