first commit
This commit is contained in:
169
RS_system/Services/AuthService.cs
Normal file
169
RS_system/Services/AuthService.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Rs_system.Data;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Models.ViewModels;
|
||||
using BC = BCrypt.Net.BCrypt;
|
||||
|
||||
namespace Rs_system.Services;
|
||||
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly ILogger<AuthService> _logger;
|
||||
|
||||
public AuthService(ApplicationDbContext context, ILogger<AuthService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<Usuario?> ValidateUserAsync(string username, string password)
|
||||
{
|
||||
var usuario = await _context.Usuarios
|
||||
.AsNoTracking()
|
||||
.Include(u => u.Persona)
|
||||
.Include(u => u.RolesUsuario)
|
||||
.ThenInclude(ru => ru.Rol)
|
||||
.FirstOrDefaultAsync(u => u.NombreUsuario == username && u.Activo);
|
||||
|
||||
if (usuario == null)
|
||||
{
|
||||
_logger.LogWarning("Login attempt for non-existent user: {Username}", username);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Verify password using BCrypt
|
||||
try
|
||||
{
|
||||
if (!BC.Verify(password, usuario.HashContrasena))
|
||||
{
|
||||
_logger.LogWarning("Invalid password for user: {Username}", username);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error verifying password for user: {Username}", username);
|
||||
return null;
|
||||
}
|
||||
|
||||
_logger.LogInformation("User {Username} logged in successfully", username);
|
||||
return usuario;
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message, Usuario? User)> RegisterUserAsync(RegisterViewModel model)
|
||||
{
|
||||
// Check if username already exists
|
||||
if (await _context.Usuarios.AnyAsync(u => u.NombreUsuario == model.NombreUsuario))
|
||||
{
|
||||
return (false, "El nombre de usuario ya existe", null);
|
||||
}
|
||||
|
||||
// Check if email already exists
|
||||
if (await _context.Usuarios.AnyAsync(u => u.Email == model.Email))
|
||||
{
|
||||
return (false, "El correo electrónico ya está registrado", null);
|
||||
}
|
||||
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Create persona first
|
||||
var persona = new Persona
|
||||
{
|
||||
Nombres = model.Nombres,
|
||||
Apellidos = model.Apellidos,
|
||||
Email = model.Email,
|
||||
Activo = true,
|
||||
CreadoEn = DateTime.UtcNow,
|
||||
ActualizadoEn = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.Personas.Add(persona);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Create user with hashed password
|
||||
var usuario = new Usuario
|
||||
{
|
||||
PersonaId = persona.Id,
|
||||
NombreUsuario = model.NombreUsuario,
|
||||
Email = model.Email,
|
||||
HashContrasena = BC.HashPassword(model.Contrasena),
|
||||
Activo = true,
|
||||
CreadoEn = DateTime.UtcNow,
|
||||
ActualizadoEn = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.Usuarios.Add(usuario);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Assign default role (LECTOR - reader)
|
||||
var defaultRole = await _context.RolesSistema
|
||||
.FirstOrDefaultAsync(r => r.Codigo == "LECTOR");
|
||||
|
||||
if (defaultRole != null)
|
||||
{
|
||||
var rolUsuario = new RolUsuario
|
||||
{
|
||||
UsuarioId = usuario.Id,
|
||||
RolId = defaultRole.Id,
|
||||
AsignadoEn = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.RolesUsuario.Add(rolUsuario);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await transaction.CommitAsync();
|
||||
|
||||
_logger.LogInformation("New user registered: {Username}", model.NombreUsuario);
|
||||
return (true, "Usuario registrado exitosamente", usuario);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(ex, "Error registering user: {Username}", model.NombreUsuario);
|
||||
return (false, "Error al registrar el usuario", null);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetUserRolesAsync(long userId)
|
||||
{
|
||||
return await _context.RolesUsuario
|
||||
.AsNoTracking()
|
||||
.Where(ru => ru.UsuarioId == userId)
|
||||
.Select(ru => ru.Rol.Codigo)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateLastLoginAsync(long userId)
|
||||
{
|
||||
var usuario = await _context.Usuarios.FindAsync(userId);
|
||||
if (usuario != null)
|
||||
{
|
||||
usuario.UltimoLogin = DateTime.UtcNow;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> HasPermissionAsync(long userId, string permissionCode)
|
||||
{
|
||||
// ROOT has all permissions
|
||||
var roles = await GetUserRolesAsync(userId);
|
||||
if (roles.Contains("ROOT")) return true;
|
||||
|
||||
return await _context.RolesUsuario
|
||||
.AsNoTracking()
|
||||
.Where(ru => ru.UsuarioId == userId)
|
||||
.Join(_context.RolesPermisos.AsNoTracking(),
|
||||
ru => ru.RolId,
|
||||
rp => rp.RolId,
|
||||
(ru, rp) => rp.PermisoId)
|
||||
.Join(_context.Permisos.AsNoTracking(),
|
||||
permisoId => permisoId,
|
||||
p => p.Id,
|
||||
(permisoId, p) => p.Codigo)
|
||||
.AnyAsync(codigo => codigo == permissionCode);
|
||||
}
|
||||
}
|
||||
27
RS_system/Services/ConfiguracionService.cs
Normal file
27
RS_system/Services/ConfiguracionService.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Rs_system.Data;
|
||||
|
||||
namespace Rs_system.Services;
|
||||
|
||||
public class ConfiguracionService : IConfiguracionService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public ConfiguracionService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<string?> GetValorAsync(string clave)
|
||||
{
|
||||
var config = await _context.Configuraciones
|
||||
.FirstOrDefaultAsync(c => c.Clave == clave);
|
||||
return config?.Valor;
|
||||
}
|
||||
|
||||
public async Task<string> GetValorOrDefaultAsync(string clave, string defaultValue)
|
||||
{
|
||||
var valor = await GetValorAsync(clave);
|
||||
return valor ?? defaultValue;
|
||||
}
|
||||
}
|
||||
32
RS_system/Services/IAuthService.cs
Normal file
32
RS_system/Services/IAuthService.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Models.ViewModels;
|
||||
|
||||
namespace Rs_system.Services;
|
||||
|
||||
public interface IAuthService
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates user credentials and returns the user if valid
|
||||
/// </summary>
|
||||
Task<Usuario?> ValidateUserAsync(string username, string password);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new user
|
||||
/// </summary>
|
||||
Task<(bool Success, string Message, Usuario? User)> RegisterUserAsync(RegisterViewModel model);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the roles for a user
|
||||
/// </summary>
|
||||
Task<List<string>> GetUserRolesAsync(long userId);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the last login timestamp
|
||||
/// </summary>
|
||||
Task UpdateLastLoginAsync(long userId);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user has a specific permission
|
||||
/// </summary>
|
||||
Task<bool> HasPermissionAsync(long userId, string permissionCode);
|
||||
}
|
||||
7
RS_system/Services/IConfiguracionService.cs
Normal file
7
RS_system/Services/IConfiguracionService.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Rs_system.Services;
|
||||
|
||||
public interface IConfiguracionService
|
||||
{
|
||||
Task<string?> GetValorAsync(string clave);
|
||||
Task<string> GetValorOrDefaultAsync(string clave, string defaultValue);
|
||||
}
|
||||
9
RS_system/Services/IReporteService.cs
Normal file
9
RS_system/Services/IReporteService.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Rs_system.Services
|
||||
{
|
||||
public interface IReporteService
|
||||
{
|
||||
}
|
||||
}
|
||||
139
RS_system/Services/MenuService.cs
Normal file
139
RS_system/Services/MenuService.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Rs_system.Data;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Models.ViewModels;
|
||||
|
||||
namespace Rs_system.Services;
|
||||
|
||||
public interface IMenuService
|
||||
{
|
||||
Task<MenuViewModel> GetUserMenuAsync(long userId, bool isRoot);
|
||||
}
|
||||
|
||||
public class MenuService : IMenuService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IQueryCacheService _cache;
|
||||
private readonly ILogger<MenuService> _logger;
|
||||
|
||||
public MenuService(ApplicationDbContext context, IQueryCacheService cache, ILogger<MenuService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_cache = cache;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<MenuViewModel> GetUserMenuAsync(long userId, bool isRoot)
|
||||
{
|
||||
var cacheKey = $"menu_{userId}_{isRoot}";
|
||||
|
||||
return await _cache.GetOrCreateAsync(cacheKey, async () =>
|
||||
{
|
||||
var userPermisoIds = await GetUserPermissionIdsAsync(userId, isRoot);
|
||||
var allModules = await GetAllActiveModulesAsync();
|
||||
|
||||
return BuildMenuViewModel(allModules, userPermisoIds);
|
||||
}, TimeSpan.FromMinutes(15));
|
||||
}
|
||||
|
||||
private async Task<List<int>> GetUserPermissionIdsAsync(long userId, bool isRoot)
|
||||
{
|
||||
if (isRoot)
|
||||
{
|
||||
return await _context.Permisos
|
||||
.AsNoTracking()
|
||||
.Where(p => p.EsMenu)
|
||||
.Select(p => p.Id)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
return await _context.RolesUsuario
|
||||
.AsNoTracking()
|
||||
.Where(ru => ru.UsuarioId == userId)
|
||||
.Select(ru => ru.RolId)
|
||||
.Distinct()
|
||||
.Join(_context.RolesPermisos.AsNoTracking(),
|
||||
rolId => rolId,
|
||||
rp => rp.RolId,
|
||||
(rolId, rp) => rp.PermisoId)
|
||||
.Join(_context.Permisos.AsNoTracking(),
|
||||
permisoId => permisoId,
|
||||
p => p.Id,
|
||||
(permisoId, p) => new { permisoId, p.EsMenu })
|
||||
.Where(x => x.EsMenu)
|
||||
.Select(x => x.permisoId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private async Task<List<Modulo>> GetAllActiveModulesAsync()
|
||||
{
|
||||
return await _context.Modulos
|
||||
.AsNoTracking()
|
||||
.Include(m => m.Permisos.Where(p => p.EsMenu))
|
||||
.Where(m => m.Activo)
|
||||
.OrderBy(m => m.Orden)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private MenuViewModel BuildMenuViewModel(List<Modulo> allModules, List<int> userPermisoIds)
|
||||
{
|
||||
var menuViewModel = new MenuViewModel();
|
||||
|
||||
// Build the tree starting from root modules (ParentId == null)
|
||||
var rootModules = allModules.Where(m => m.ParentId == null).OrderBy(m => m.Orden);
|
||||
|
||||
foreach (var module in rootModules)
|
||||
{
|
||||
var menuItem = BuildModuleMenuItem(module, allModules, userPermisoIds);
|
||||
if (menuItem != null)
|
||||
{
|
||||
menuViewModel.Items.Add(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
return menuViewModel;
|
||||
}
|
||||
|
||||
private MenuItem? BuildModuleMenuItem(Modulo module, List<Modulo> allModules, List<int> userPermisoIds)
|
||||
{
|
||||
var item = new MenuItem
|
||||
{
|
||||
Title = module.Nombre,
|
||||
Icon = module.Icono,
|
||||
IsGroup = true,
|
||||
Order = module.Orden
|
||||
};
|
||||
|
||||
// 1. Add Submodules
|
||||
var subModules = allModules.Where(m => m.ParentId == module.Id).OrderBy(m => m.Orden);
|
||||
foreach (var sub in subModules)
|
||||
{
|
||||
var subItem = BuildModuleMenuItem(sub, allModules, userPermisoIds);
|
||||
if (subItem != null)
|
||||
{
|
||||
item.Children.Add(subItem);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Add Direct Permissions (Menu Items)
|
||||
var permissions = module.Permisos
|
||||
.Where(p => userPermisoIds.Contains(p.Id))
|
||||
.OrderBy(p => p.Orden);
|
||||
|
||||
foreach (var p in permissions)
|
||||
{
|
||||
item.Children.Add(new MenuItem
|
||||
{
|
||||
Title = p.Nombre,
|
||||
Icon = p.Icono,
|
||||
Url = p.Url,
|
||||
IsGroup = false,
|
||||
Order = p.Orden
|
||||
});
|
||||
}
|
||||
|
||||
// Only return the item if it has children (permissions or submodules with permissions)
|
||||
return item.Children.Any() ? item : null;
|
||||
}
|
||||
}
|
||||
64
RS_system/Services/QueryCacheService.cs
Normal file
64
RS_system/Services/QueryCacheService.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Rs_system.Services;
|
||||
|
||||
public interface IQueryCacheService
|
||||
{
|
||||
Task<T?> GetOrCreateAsync<T>(string cacheKey, Func<Task<T>> factory, TimeSpan? expiration = null);
|
||||
void Remove(string cacheKey);
|
||||
void Clear();
|
||||
}
|
||||
|
||||
public class QueryCacheService : IQueryCacheService
|
||||
{
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly ILogger<QueryCacheService> _logger;
|
||||
private static readonly TimeSpan DefaultExpiration = TimeSpan.FromMinutes(5);
|
||||
|
||||
public QueryCacheService(IMemoryCache cache, ILogger<QueryCacheService> logger)
|
||||
{
|
||||
_cache = cache;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<T?> GetOrCreateAsync<T>(string cacheKey, Func<Task<T>> factory, TimeSpan? expiration = null)
|
||||
{
|
||||
if (_cache.TryGetValue(cacheKey, out T? cachedValue))
|
||||
{
|
||||
_logger.LogDebug("Cache hit for key: {CacheKey}", cacheKey);
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Cache miss for key: {CacheKey}", cacheKey);
|
||||
var value = await factory();
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
var cacheOptions = new MemoryCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = expiration ?? DefaultExpiration,
|
||||
Size = 1 // Each cache entry has size 1 for memory management
|
||||
};
|
||||
|
||||
_cache.Set(cacheKey, value, cacheOptions);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Remove(string cacheKey)
|
||||
{
|
||||
_cache.Remove(cacheKey);
|
||||
_logger.LogDebug("Cache removed for key: {CacheKey}", cacheKey);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (_cache is MemoryCache memoryCache)
|
||||
{
|
||||
memoryCache.Compact(1.0); // Clear all cache entries
|
||||
_logger.LogInformation("Cache cleared");
|
||||
}
|
||||
}
|
||||
}
|
||||
33
RS_system/Services/ReporteService.cs
Normal file
33
RS_system/Services/ReporteService.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Threading.Tasks;
|
||||
using Rs_system.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Rs_system.Services
|
||||
{
|
||||
public class ReporteService : IReporteService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public ReporteService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private void AddDateParams(System.Data.Common.DbCommand command, DateOnly inicio, DateOnly fin)
|
||||
{
|
||||
var p1 = command.CreateParameter();
|
||||
p1.ParameterName = "@p_inicio";
|
||||
p1.Value = inicio.ToDateTime(TimeOnly.MinValue);
|
||||
p1.DbType = DbType.Date;
|
||||
command.Parameters.Add(p1);
|
||||
|
||||
var p2 = command.CreateParameter();
|
||||
p2.ParameterName = "@p_fin";
|
||||
p2.Value = fin.ToDateTime(TimeOnly.MaxValue);
|
||||
p2.DbType = DbType.Date;
|
||||
command.Parameters.Add(p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user