< Summary

Information
Class: FAU.API.Controllers.UsuariosController
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/UsuariosController.cs
Line coverage
87%
Covered lines: 131
Uncovered lines: 18
Coverable lines: 149
Total lines: 345
Line coverage: 87.9%
Branch coverage
63%
Covered branches: 58
Total branches: 92
Branch coverage: 63%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetUsuarios()75%1212100%
GetUsuario()60%1010100%
CreateUsuario()77.27%252282.14%
UpdateUsuario()42.85%692862.5%
CambiarPassword()60%101093.75%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/UsuariosController.cs

#LineLine coverage
 1using FAU.Logica.DTOs;
 2using FAU.Logica.Services;
 3using Microsoft.AspNetCore.Authorization;
 4using Microsoft.AspNetCore.Http;
 5using Microsoft.AspNetCore.Mvc;
 6
 7namespace FAU.API.Controllers;
 8
 9/// <summary>
 10/// Gestión de usuarios del sistema
 11/// </summary>
 12[ApiController]
 13[Route("api/[controller]")]
 14[Authorize(Roles = "Administrador")]
 15[Produces("application/json")]
 16public class UsuariosController : ControllerBase
 17{
 18    private readonly IUsuarioService _usuarioService;
 19    private readonly ILogger<UsuariosController> _logger;
 20
 2021    public UsuariosController(
 2022        IUsuarioService usuarioService,
 2023        ILogger<UsuariosController> logger)
 24    {
 2025        _usuarioService = usuarioService;
 2026        _logger = logger;
 2027    }
 28
 29    /// <summary>
 30    /// Listar usuarios con filtros y paginación
 31    /// GET /api/usuarios?rolId=&amp;estado=&amp;q=&amp;page=1&amp;pageSize=25
 32    /// </summary>
 33    [HttpGet]
 34    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 35    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 36    public async Task<IActionResult> GetUsuarios(
 37        [FromQuery] long? rolId,
 38        [FromQuery] string? estado,
 39        [FromQuery] string? q,
 40        [FromQuery] int page = 1,
 41        [FromQuery] int pageSize = 25)
 42    {
 43        try
 44        {
 45            // Validación de parámetros
 646            if (page < 1) page = 1;
 647            if (pageSize < 1) pageSize = 25;
 648            if (pageSize > 100) pageSize = 100; // Límite máximo
 49
 650            var usuarios = await _usuarioService.GetAllAsync();
 51
 52            // Aplicar filtros
 553            var query = usuarios.AsQueryable();
 54
 555            if (rolId.HasValue)
 56            {
 157                query = query.Where(u => u.Roles.Any(r => r.Id == rolId.Value));
 58            }
 59
 560            if (!string.IsNullOrWhiteSpace(estado))
 61            {
 162                query = query.Where(u => u.Estado.Equals(estado, StringComparison.OrdinalIgnoreCase));
 63            }
 64
 565            if (!string.IsNullOrWhiteSpace(q))
 66            {
 167                var searchTerm = q.ToLower();
 168                query = query.Where(u =>
 169                    u.Username.ToLower().Contains(searchTerm) ||
 170                    (u.Persona != null && (
 171                        u.Persona.PrimerNombre.ToLower().Contains(searchTerm) ||
 172                        u.Persona.PrimerApellido.ToLower().Contains(searchTerm) ||
 173                        (u.Persona.SegundoNombre != null && u.Persona.SegundoNombre.ToLower().Contains(searchTerm)) ||
 174                        (u.Persona.SegundoApellido != null && u.Persona.SegundoApellido.ToLower().Contains(searchTerm)) 
 175                        (u.Persona.Email != null && u.Persona.Email.ToLower().Contains(searchTerm))
 176                    ))
 177                );
 78            }
 79
 580            var total = query.Count();
 81
 82            // Paginación
 583            var usuariosPaginados = query
 584                .Skip((page - 1) * pageSize)
 585                .Take(pageSize)
 586                .ToList()
 1487                .Select(u => new
 1488                {
 1489                    id = u.Id,
 1490                    usuario = u.Username,
 1491                    nombre = u.Persona != null ? u.Persona.NombreCompleto : "",
 1492                    email = u.Persona?.Email ?? "",
 1493                    rol = u.Roles.FirstOrDefault()?.Nombre ?? "",
 1494                    estado = u.Estado,
 1495                    ultimoAcceso = u.FechaActualizacion
 1496                })
 597                .ToList();
 98
 599            var response = new
 5100            {
 5101                items = usuariosPaginados,
 5102                total = total,
 5103                page = page,
 5104                pageSize = pageSize,
 5105                totalPages = (int)Math.Ceiling((double)total / pageSize)
 5106            };
 107
 5108            return Ok(ApiResponse<object>.SuccessResult(response));
 109        }
 1110        catch (Exception ex)
 111        {
 1112            _logger.LogError(ex, "Error al obtener usuarios");
 1113            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 114        }
 6115    }
 116
 117    /// <summary>
 118    /// Obtener detalle de usuario por ID
 119    /// GET /api/usuarios/{id}
 120    /// </summary>
 121    [HttpGet("{id}")]
 122    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 123    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 124    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 125    public async Task<IActionResult> GetUsuario(long id)
 126    {
 127        try
 128        {
 3129            var usuario = await _usuarioService.GetByIdAsync(id);
 130
 2131            if (usuario == null)
 132            {
 1133                return NotFound(ApiResponse<string>.ErrorResult("Usuario no encontrado"));
 134            }
 135
 1136            var response = new
 1137            {
 1138                id = usuario.Id,
 1139                usuario = usuario.Username,
 1140                nombre = usuario.Persona != null ? usuario.Persona.NombreCompleto : "",
 1141                email = usuario.Persona?.Email ?? "",
 1142                rolId = usuario.Roles.FirstOrDefault()?.Id,
 1143                estado = usuario.Estado
 1144            };
 145
 1146            return Ok(ApiResponse<object>.SuccessResult(response));
 147        }
 1148        catch (Exception ex)
 149        {
 1150            _logger.LogError(ex, "Error al obtener usuario {Id}", id);
 1151            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 152        }
 3153    }
 154
 155    /// <summary>
 156    /// Crear usuario con contraseña temporal
 157    /// POST /api/usuarios
 158    /// </summary>
 159    [HttpPost]
 160    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status201Created)]
 161    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 162    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 163    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 164    public async Task<IActionResult> CreateUsuario([FromBody] CreateUsuarioRequest request)
 165    {
 166        try
 167        {
 168            // Validaciones
 3169            if (string.IsNullOrWhiteSpace(request.Username))
 170            {
 1171                return BadRequest(ApiResponse<string>.ErrorResult("El nombre de usuario es requerido"));
 172            }
 173
 2174            if (string.IsNullOrWhiteSpace(request.Password))
 175            {
 0176                return BadRequest(ApiResponse<string>.ErrorResult("La contraseña es requerida"));
 177            }
 178
 2179            var (success, usuario, error) = await _usuarioService.CreateAsync(
 2180                request.Username,
 2181                request.Password,
 2182                request.PersonaId);
 183
 2184            if (!success)
 185            {
 1186                if (error?.Contains("ya existe") == true || error?.Contains("ya está en uso") == true)
 187                {
 1188                    return Conflict(ApiResponse<string>.ErrorResult(error));
 189                }
 0190                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al crear usuario"));
 191            }
 192
 193            // Asignar roles si se especificaron
 1194            if (request.RolesIds != null && request.RolesIds.Any())
 195            {
 4196                foreach (var rolId in request.RolesIds)
 197                {
 1198                    await _usuarioService.AsignarRolAsync(usuario!.Id, rolId);
 199                }
 200            }
 201
 1202            var response = new
 1203            {
 1204                id = usuario!.Id,
 1205                creado = true
 1206            };
 207
 1208            return CreatedAtAction(
 1209                nameof(GetUsuario),
 1210                new { id = usuario.Id },
 1211                ApiResponse<object>.SuccessResult(response, "Usuario creado exitosamente"));
 212        }
 0213        catch (Exception ex)
 214        {
 0215            _logger.LogError(ex, "Error al crear usuario");
 0216            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 217        }
 3218    }
 219
 220    /// <summary>
 221    /// Editar usuario (incluye activar/bloquear mediante campo estado)
 222    /// PUT /api/usuarios/{id}
 223    /// </summary>
 224    [HttpPut("{id}")]
 225    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 226    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 227    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 228    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 229    public async Task<IActionResult> UpdateUsuario(long id, [FromBody] UpdateUsuarioRequest request)
 230    {
 231        try
 232        {
 4233            var usuario = await _usuarioService.GetByIdAsync(id);
 3234            if (usuario == null)
 235            {
 1236                return NotFound(ApiResponse<string>.ErrorResult("Usuario no encontrado"));
 237            }
 238
 239            // Cambiar persona vinculada si se especifica
 2240            if (request.PersonaId.HasValue)
 241            {
 1242                var (successPersona, errorPersona) = await _usuarioService.CambiarPersonaAsync(id, request.PersonaId.Val
 1243                if (!successPersona)
 244                {
 0245                    return BadRequest(ApiResponse<string>.ErrorResult(errorPersona ?? "Error al cambiar persona"));
 246                }
 247            }
 248
 249            // Cambiar rol si se especifica
 2250            if (request.RolId.HasValue)
 251            {
 252                // Remover roles actuales
 0253                var rolesActuales = usuario.Roles.ToList();
 0254                foreach (var rol in rolesActuales)
 255                {
 0256                    var (successRemove, errorRemove) = await _usuarioService.RemoverRolAsync(id, rol.Id);
 0257                    if (!successRemove)
 258                    {
 0259                        return BadRequest(ApiResponse<string>.ErrorResult(errorRemove ?? "Error al remover rol"));
 260                    }
 261                }
 262
 263                // Asignar nuevo rol
 0264                var (successRol, errorRol) = await _usuarioService.AsignarRolAsync(id, request.RolId.Value);
 0265                if (!successRol)
 266                {
 0267                    return BadRequest(ApiResponse<string>.ErrorResult(errorRol ?? "Error al asignar rol"));
 268                }
 269
 270                // Recargar usuario para reflejar cambios de roles
 0271                usuario = await _usuarioService.GetByIdAsync(id);
 0272                if (usuario == null)
 273                {
 0274                    return NotFound(ApiResponse<string>.ErrorResult("Usuario no encontrado"));
 275                }
 276            }
 277
 278            // Cambiar estado si se especifica
 2279            if (!string.IsNullOrWhiteSpace(request.Estado))
 280            {
 2281                var (successEstado, errorEstado) = await _usuarioService.CambiarEstadoAsync(id, request.Estado);
 2282                if (!successEstado)
 283                {
 1284                    return BadRequest(ApiResponse<string>.ErrorResult(errorEstado ?? "Error al cambiar estado"));
 285                }
 286            }
 287
 1288            var response = new
 1289            {
 1290                actualizado = true
 1291            };
 292
 1293            return Ok(ApiResponse<object>.SuccessResult(response, "Usuario actualizado exitosamente"));
 294        }
 1295        catch (Exception ex)
 296        {
 1297            _logger.LogError(ex, "Error al actualizar usuario {Id}", id);
 1298            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 299        }
 4300    }
 301
 302    /// <summary>
 303    /// Cambiar contraseña de usuario (también para reset)
 304    /// PATCH /api/usuarios/{id}/password
 305    /// </summary>
 306    [HttpPatch("{id}/password")]
 307    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 308    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 309    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 310    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 311    public async Task<IActionResult> CambiarPassword(long id, [FromBody] CambiarPasswordRequest request)
 312    {
 313        try
 314        {
 4315            if (string.IsNullOrWhiteSpace(request.NuevaPassword))
 316            {
 1317                return BadRequest(ApiResponse<string>.ErrorResult("La nueva contraseña es requerida"));
 318            }
 319
 3320            var (success, error) = await _usuarioService.CambiarPasswordAsync(id, request.NuevaPassword);
 321
 2322            if (!success)
 323            {
 1324                if (error?.Contains("no encontrado") == true)
 325                {
 1326                    return NotFound(ApiResponse<string>.ErrorResult(error));
 327                }
 0328                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al cambiar contraseña"));
 329            }
 330
 1331            var response = new
 1332            {
 1333                actualizado = true
 1334            };
 335
 1336            return Ok(ApiResponse<object>.SuccessResult(response, "Contraseña actualizada exitosamente"));
 337        }
 1338        catch (Exception ex)
 339        {
 1340            _logger.LogError(ex, "Error al cambiar contraseña del usuario {Id}", id);
 1341            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 342        }
 4343    }
 344}
 345