250 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			250 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using Dapper; | ||
|  | using GestionIntegral.Api.Models.Usuarios; | ||
|  | using Microsoft.Extensions.Logging; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Data; | ||
|  | using System.Linq; | ||
|  | using System.Text; | ||
|  | using System.Threading.Tasks; | ||
|  | 
 | ||
|  | namespace GestionIntegral.Api.Data.Repositories.Usuarios | ||
|  | { | ||
|  |     public class PerfilRepository : IPerfilRepository | ||
|  |     { | ||
|  |         private readonly DbConnectionFactory _connectionFactory; | ||
|  |         private readonly ILogger<PerfilRepository> _logger; | ||
|  | 
 | ||
|  |         public PerfilRepository(DbConnectionFactory connectionFactory, ILogger<PerfilRepository> logger) | ||
|  |         { | ||
|  |             _connectionFactory = connectionFactory; | ||
|  |             _logger = logger; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<IEnumerable<Perfil>> GetAllAsync(string? nombreFilter) | ||
|  |         { | ||
|  |             var sqlBuilder = new StringBuilder("SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles WHERE 1=1"); | ||
|  |             var parameters = new DynamicParameters(); | ||
|  | 
 | ||
|  |             if (!string.IsNullOrWhiteSpace(nombreFilter)) | ||
|  |             { | ||
|  |                 sqlBuilder.Append(" AND perfil LIKE @NombreFilter"); | ||
|  |                 parameters.Add("NombreFilter", $"%{nombreFilter}%"); | ||
|  |             } | ||
|  |             sqlBuilder.Append(" ORDER BY perfil;"); | ||
|  | 
 | ||
|  |             try | ||
|  |             { | ||
|  |                 using var connection = _connectionFactory.CreateConnection(); | ||
|  |                 return await connection.QueryAsync<Perfil>(sqlBuilder.ToString(), parameters); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _logger.LogError(ex, "Error al obtener todos los Perfiles. Filtro: {NombreFilter}", nombreFilter); | ||
|  |                 return Enumerable.Empty<Perfil>(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<Perfil?> GetByIdAsync(int id) | ||
|  |         { | ||
|  |             const string sql = "SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles WHERE id = @Id"; | ||
|  |             try | ||
|  |             { | ||
|  |                 using var connection = _connectionFactory.CreateConnection(); | ||
|  |                 return await connection.QuerySingleOrDefaultAsync<Perfil>(sql, new { Id = id }); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _logger.LogError(ex, "Error al obtener Perfil por ID: {IdPerfil}", id); | ||
|  |                 return null; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<bool> ExistsByNameAsync(string nombrePerfil, int? excludeId = null) | ||
|  |         { | ||
|  |             var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.gral_Perfiles WHERE perfil = @NombrePerfil"); | ||
|  |             var parameters = new DynamicParameters(); | ||
|  |             parameters.Add("NombrePerfil", nombrePerfil); | ||
|  | 
 | ||
|  |             if (excludeId.HasValue) | ||
|  |             { | ||
|  |                 sqlBuilder.Append(" AND id != @ExcludeId"); | ||
|  |                 parameters.Add("ExcludeId", excludeId.Value); | ||
|  |             } | ||
|  | 
 | ||
|  |             try | ||
|  |             { | ||
|  |                 using var connection = _connectionFactory.CreateConnection(); | ||
|  |                 var count = await connection.ExecuteScalarAsync<int>(sqlBuilder.ToString(), parameters); | ||
|  |                 return count > 0; | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _logger.LogError(ex, "Error en ExistsByNameAsync para Perfil con nombre: {NombrePerfil}", nombrePerfil); | ||
|  |                 return true; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<bool> IsInUseAsync(int id) | ||
|  |         { | ||
|  |             const string sqlCheckUsuarios = "SELECT TOP 1 1 FROM dbo.gral_Usuarios WHERE IdPerfil = @IdPerfil"; | ||
|  |             const string sqlCheckPermisos = "SELECT TOP 1 1 FROM dbo.gral_PermisosPerfiles WHERE idPerfil = @IdPerfil"; | ||
|  |             try | ||
|  |             { | ||
|  |                 using var connection = _connectionFactory.CreateConnection(); | ||
|  |                 var inUsuarios = await connection.ExecuteScalarAsync<int?>(sqlCheckUsuarios, new { IdPerfil = id }); | ||
|  |                 if (inUsuarios.HasValue && inUsuarios.Value == 1) return true; | ||
|  | 
 | ||
|  |                 var inPermisos = await connection.ExecuteScalarAsync<int?>(sqlCheckPermisos, new { IdPerfil = id }); | ||
|  |                 return inPermisos.HasValue && inPermisos.Value == 1; | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _logger.LogError(ex, "Error en IsInUseAsync para Perfil ID: {IdPerfil}", id); | ||
|  |                 return true; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<Perfil?> CreateAsync(Perfil nuevoPerfil, int idUsuario, IDbTransaction transaction) | ||
|  |         { | ||
|  |             const string sqlInsert = @"
 | ||
|  |                 INSERT INTO dbo.gral_Perfiles (perfil, descPerfil) | ||
|  |                 OUTPUT INSERTED.id AS Id, INSERTED.perfil AS NombrePerfil, INSERTED.descPerfil AS Descripcion | ||
|  |                 VALUES (@NombrePerfil, @Descripcion);";
 | ||
|  |             const string sqlInsertHistorico = @"
 | ||
|  |                 INSERT INTO dbo.gral_Perfiles_H (idPerfil, perfil, descPerfil, Id_Usuario, FechaMod, TipoMod) | ||
|  |                 VALUES (@IdPerfilHist, @NombrePerfilHist, @DescripcionHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
 | ||
|  | 
 | ||
|  |             var connection = transaction.Connection!; // La conexión debe venir de la transacción | ||
|  | 
 | ||
|  |             var insertedPerfil = await connection.QuerySingleAsync<Perfil>( | ||
|  |                 sqlInsert, | ||
|  |                 new { nuevoPerfil.NombrePerfil, nuevoPerfil.Descripcion }, | ||
|  |                 transaction: transaction); | ||
|  | 
 | ||
|  |             if (insertedPerfil == null || insertedPerfil.Id <= 0) | ||
|  |             { | ||
|  |                 throw new DataException("No se pudo obtener el ID del perfil insertado."); | ||
|  |             } | ||
|  | 
 | ||
|  |             await connection.ExecuteAsync(sqlInsertHistorico, new | ||
|  |             { | ||
|  |                 IdPerfilHist = insertedPerfil.Id, // Correcto | ||
|  |                 NombrePerfilHist = insertedPerfil.NombrePerfil, | ||
|  |                 DescripcionHist = insertedPerfil.Descripcion, | ||
|  |                 IdUsuarioHist = idUsuario, | ||
|  |                 FechaModHist = DateTime.Now, | ||
|  |                 TipoModHist = "Insertada" | ||
|  |             }, transaction: transaction); | ||
|  | 
 | ||
|  |             return insertedPerfil; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<bool> UpdateAsync(Perfil perfilAActualizar, int idUsuario, IDbTransaction transaction) | ||
|  |         { | ||
|  |             var connection = transaction.Connection!; | ||
|  |             // Obtener el estado actual PARA EL HISTORIAL DENTRO DE LA MISMA TRANSACCIÓN | ||
|  |             var perfilActual = await connection.QuerySingleOrDefaultAsync<Perfil>( | ||
|  |                 "SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles WHERE id = @Id", | ||
|  |                 new { Id = perfilAActualizar.Id }, | ||
|  |                 transaction); | ||
|  | 
 | ||
|  |             if (perfilActual == null) | ||
|  |             { | ||
|  |                 // Esto no debería pasar si el servicio verifica la existencia antes, pero es una salvaguarda. | ||
|  |                 throw new KeyNotFoundException($"Perfil con ID {perfilAActualizar.Id} no encontrado para actualizar."); | ||
|  |             } | ||
|  | 
 | ||
|  |             const string sqlUpdate = "UPDATE dbo.gral_Perfiles SET perfil = @NombrePerfil, descPerfil = @Descripcion WHERE id = @Id;"; | ||
|  |             const string sqlInsertHistorico = @"
 | ||
|  |                 INSERT INTO dbo.gral_Perfiles_H (idPerfil, perfil, descPerfil, Id_Usuario, FechaMod, TipoMod) | ||
|  |                 VALUES (@IdPerfilHist, @NombrePerfilHist, @DescripcionHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
 | ||
|  | 
 | ||
|  |             // Insertar en historial con los valores ANTES de la modificación | ||
|  |             await connection.ExecuteAsync(sqlInsertHistorico, new | ||
|  |             { | ||
|  |                 IdPerfilHist = perfilActual.Id, | ||
|  |                 NombrePerfilHist = perfilActual.NombrePerfil, | ||
|  |                 DescripcionHist = perfilActual.Descripcion, | ||
|  |                 IdUsuarioHist = idUsuario, | ||
|  |                 FechaModHist = DateTime.Now, | ||
|  |                 TipoModHist = "Modificada" | ||
|  |             }, transaction: transaction); | ||
|  | 
 | ||
|  |             // Actualizar la tabla principal | ||
|  |             var rowsAffected = await connection.ExecuteAsync(sqlUpdate, perfilAActualizar, transaction: transaction); | ||
|  |             return rowsAffected == 1; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<bool> DeleteAsync(int id, int idUsuario, IDbTransaction transaction) | ||
|  |         { | ||
|  |             var connection = transaction.Connection!; | ||
|  |             // Obtener el estado actual PARA EL HISTORIAL DENTRO DE LA MISMA TRANSACCIÓN | ||
|  |             var perfilActual = await connection.QuerySingleOrDefaultAsync<Perfil>( | ||
|  |                  "SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles WHERE id = @Id", | ||
|  |                  new { Id = id }, | ||
|  |                  transaction); | ||
|  | 
 | ||
|  |             if (perfilActual == null) | ||
|  |             { | ||
|  |                 throw new KeyNotFoundException($"Perfil con ID {id} no encontrado para eliminar."); | ||
|  |             } | ||
|  | 
 | ||
|  |             const string sqlDelete = "DELETE FROM dbo.gral_Perfiles WHERE id = @Id"; | ||
|  |             const string sqlInsertHistorico = @"
 | ||
|  |                 INSERT INTO dbo.gral_Perfiles_H (idPerfil, perfil, descPerfil, Id_Usuario, FechaMod, TipoMod) | ||
|  |                 VALUES (@IdPerfilHist, @NombrePerfilHist, @DescripcionHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
 | ||
|  | 
 | ||
|  |             // Insertar en historial con los valores ANTES de la eliminación | ||
|  |             await connection.ExecuteAsync(sqlInsertHistorico, new | ||
|  |             { | ||
|  |                 IdPerfilHist = perfilActual.Id, | ||
|  |                 NombrePerfilHist = perfilActual.NombrePerfil, | ||
|  |                 DescripcionHist = perfilActual.Descripcion, | ||
|  |                 IdUsuarioHist = idUsuario, | ||
|  |                 FechaModHist = DateTime.Now, | ||
|  |                 TipoModHist = "Eliminada" | ||
|  |             }, transaction: transaction); | ||
|  | 
 | ||
|  |             // Eliminar de la tabla principal | ||
|  |             var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { Id = id }, transaction: transaction); | ||
|  |             return rowsAffected == 1; | ||
|  |         } | ||
|  |         public async Task<IEnumerable<int>> GetPermisoIdsByPerfilIdAsync(int idPerfil) | ||
|  |         { | ||
|  |             const string sql = "SELECT idPermiso FROM dbo.gral_PermisosPerfiles WHERE idPerfil = @IdPerfil"; | ||
|  |             try | ||
|  |             { | ||
|  |                 using var connection = _connectionFactory.CreateConnection(); | ||
|  |                 return await connection.QueryAsync<int>(sql, new { IdPerfil = idPerfil }); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _logger.LogError(ex, "Error al obtener IDs de permisos para el Perfil ID: {IdPerfil}", idPerfil); | ||
|  |                 return Enumerable.Empty<int>(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task UpdatePermisosByPerfilIdAsync(int idPerfil, IEnumerable<int> nuevosPermisosIds, IDbTransaction transaction) | ||
|  |         { | ||
|  |             var connection = transaction.Connection!; | ||
|  | 
 | ||
|  |             // 1. Eliminar todos los permisos existentes para este perfil (dentro de la transacción) | ||
|  |             const string sqlDelete = "DELETE FROM dbo.gral_PermisosPerfiles WHERE idPerfil = @IdPerfil"; | ||
|  |             await connection.ExecuteAsync(sqlDelete, new { IdPerfil = idPerfil }, transaction: transaction); | ||
|  | 
 | ||
|  |             // 2. Insertar los nuevos permisos (si hay alguno) | ||
|  |             if (nuevosPermisosIds != null && nuevosPermisosIds.Any()) | ||
|  |             { | ||
|  |                 const string sqlInsert = "INSERT INTO dbo.gral_PermisosPerfiles (idPerfil, idPermiso) VALUES (@IdPerfil, @IdPermiso)"; | ||
|  |                 // Dapper puede manejar una lista de objetos para inserciones múltiples | ||
|  |                 var permisosParaInsertar = nuevosPermisosIds.Select(idPermiso => new { IdPerfil = idPerfil, IdPermiso = idPermiso }); | ||
|  |                 await connection.ExecuteAsync(sqlInsert, permisosParaInsertar, transaction: transaction); | ||
|  |             } | ||
|  |             // No hay tabla _H para gral_PermisosPerfiles directamente en este diseño. | ||
|  |             // La auditoría de qué usuario cambió los permisos de un perfil se podría registrar | ||
|  |             // en una tabla de log de acciones más general si fuera necesario, o deducir | ||
|  |             // indirectamente si la interfaz de usuario solo permite a SuperAdmin hacer esto. | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  | } |