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}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|