< 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
88%
Covered lines: 136
Uncovered lines: 17
Coverable lines: 153
Total lines: 355
Line coverage: 88.8%
Branch coverage
67%
Covered branches: 63
Total branches: 94
Branch coverage: 67%
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()56.66%563069.44%
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=&estado=&q=&page=1&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            // Actualizar datos de persona si se proporcionan
 2240            if (usuario.Persona != null)
 241            {
 2242                if (!string.IsNullOrWhiteSpace(request.Nombre))
 243                {
 244                    // Asumiendo que nombre viene como "Nombre Apellido"
 1245                    var nombres = request.Nombre.Split(' ', 2);
 1246                    usuario.Persona.PrimerNombre = nombres[0];
 1247                    if (nombres.Length > 1)
 248                    {
 1249                        usuario.Persona.PrimerApellido = nombres[1];
 250                    }
 251                }
 252
 2253                if (!string.IsNullOrWhiteSpace(request.Email))
 254                {
 1255                    usuario.Persona.Email = request.Email;
 256                }
 257            }
 258
 259            // Cambiar rol si se especifica
 2260            if (request.RolId.HasValue)
 261            {
 262                // Remover roles actuales
 0263                var rolesActuales = usuario.Roles.ToList();
 0264                foreach (var rol in rolesActuales)
 265                {
 0266                    var (successRemove, errorRemove) = await _usuarioService.RemoverRolAsync(id, rol.Id);
 0267                    if (!successRemove)
 268                    {
 0269                        return BadRequest(ApiResponse<string>.ErrorResult(errorRemove ?? "Error al remover rol"));
 270                    }
 271                }
 272
 273                // Asignar nuevo rol
 0274                var (successRol, errorRol) = await _usuarioService.AsignarRolAsync(id, request.RolId.Value);
 0275                if (!successRol)
 276                {
 0277                    return BadRequest(ApiResponse<string>.ErrorResult(errorRol ?? "Error al asignar rol"));
 278                }
 279
 280                // Recargar usuario para reflejar cambios de roles
 0281                usuario = await _usuarioService.GetByIdAsync(id);
 0282                if (usuario == null)
 283                {
 0284                    return NotFound(ApiResponse<string>.ErrorResult("Usuario no encontrado"));
 285                }
 286            }
 287
 288            // Cambiar estado si se especifica
 2289            if (!string.IsNullOrWhiteSpace(request.Estado))
 290            {
 2291                var (successEstado, errorEstado) = await _usuarioService.CambiarEstadoAsync(id, request.Estado);
 2292                if (!successEstado)
 293                {
 1294                    return BadRequest(ApiResponse<string>.ErrorResult(errorEstado ?? "Error al cambiar estado"));
 295                }
 296            }
 297
 1298            var response = new
 1299            {
 1300                actualizado = true
 1301            };
 302
 1303            return Ok(ApiResponse<object>.SuccessResult(response, "Usuario actualizado exitosamente"));
 304        }
 1305        catch (Exception ex)
 306        {
 1307            _logger.LogError(ex, "Error al actualizar usuario {Id}", id);
 1308            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 309        }
 4310    }
 311
 312    /// <summary>
 313    /// Cambiar contraseña de usuario (también para reset)
 314    /// PATCH /api/usuarios/{id}/password
 315    /// </summary>
 316    [HttpPatch("{id}/password")]
 317    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 318    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 319    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 320    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 321    public async Task<IActionResult> CambiarPassword(long id, [FromBody] CambiarPasswordRequest request)
 322    {
 323        try
 324        {
 4325            if (string.IsNullOrWhiteSpace(request.NuevaPassword))
 326            {
 1327                return BadRequest(ApiResponse<string>.ErrorResult("La nueva contraseña es requerida"));
 328            }
 329
 3330            var (success, error) = await _usuarioService.CambiarPasswordAsync(id, request.NuevaPassword);
 331
 2332            if (!success)
 333            {
 1334                if (error?.Contains("no encontrado") == true)
 335                {
 1336                    return NotFound(ApiResponse<string>.ErrorResult(error));
 337                }
 0338                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al cambiar contraseña"));
 339            }
 340
 1341            var response = new
 1342            {
 1343                actualizado = true
 1344            };
 345
 1346            return Ok(ApiResponse<object>.SuccessResult(response, "Contraseña actualizada exitosamente"));
 347        }
 1348        catch (Exception ex)
 349        {
 1350            _logger.LogError(ex, "Error al cambiar contraseña del usuario {Id}", id);
 1351            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 352        }
 4353    }
 354}
 355