feat: Controladores con operaciones CRUD completas

This commit is contained in:
2025-10-02 15:32:23 -03:00
parent 10f2f2ba67
commit 80210e5d4c
7 changed files with 719 additions and 15 deletions

View File

@@ -0,0 +1,114 @@
using Dapper;
using Inventario.API.Data;
using Inventario.API.Models;
using Microsoft.AspNetCore.Mvc;
namespace Inventario.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class DiscosController : ControllerBase
{
private readonly DapperContext _context;
public DiscosController(DapperContext context)
{
_context = context;
}
// --- GET /api/discos ---
[HttpGet]
public async Task<IActionResult> Consultar()
{
var query = "SELECT Id, Mediatype, Size FROM dbo.discos;";
using (var connection = _context.CreateConnection())
{
var discos = await connection.QueryAsync<Disco>(query);
return Ok(discos);
}
}
// --- GET /api/discos/{id} ---
[HttpGet("{id}")]
public async Task<IActionResult> ConsultarDetalle(int id)
{
var query = "SELECT Id, Mediatype, Size FROM dbo.discos WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var disco = await connection.QuerySingleOrDefaultAsync<Disco>(query, new { Id = id });
if (disco == null)
{
return NotFound("Disco no encontrado.");
}
return Ok(disco);
}
}
// --- POST /api/discos ---
// Replica la lógica de recibir uno o varios discos y crear solo los que no existen.
[HttpPost]
public async Task<IActionResult> Ingresar([FromBody] List<Disco> discos)
{
var queryCheck = "SELECT * FROM dbo.discos WHERE Mediatype = @Mediatype AND Size = @Size;";
var queryInsert = "INSERT INTO dbo.discos (Mediatype, Size) VALUES (@Mediatype, @Size); SELECT CAST(SCOPE_IDENTITY() as int);";
var resultados = new List<object>();
using (var connection = _context.CreateConnection())
{
foreach (var disco in discos)
{
var existente = await connection.QuerySingleOrDefaultAsync<Disco>(queryCheck, new { disco.Mediatype, disco.Size });
if (existente == null)
{
var nuevoId = await connection.ExecuteScalarAsync<int>(queryInsert, new { disco.Mediatype, disco.Size });
var nuevoDisco = new Disco { Id = nuevoId, Mediatype = disco.Mediatype, Size = disco.Size };
resultados.Add(new { action = "created", registro = nuevoDisco });
}
else
{
// Opcional: podrías añadirlo si quieres saber cuáles ya existían
// resultados.Add(new { action = "exists", registro = existente });
}
}
}
// Devolvemos HTTP 200 OK con la lista de los discos que se crearon.
return Ok(resultados);
}
// --- PUT /api/discos/{id} ---
[HttpPut("{id}")]
public async Task<IActionResult> Actualizar(int id, [FromBody] Disco disco)
{
var query = "UPDATE dbo.discos SET Mediatype = @Mediatype, Size = @Size WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var filasAfectadas = await connection.ExecuteAsync(query, new { disco.Mediatype, disco.Size, Id = id });
if (filasAfectadas == 0)
{
return NotFound("Disco no encontrado.");
}
var discoActualizado = new Disco { Id = id, Mediatype = disco.Mediatype, Size = disco.Size };
return Ok(discoActualizado);
}
}
// --- DELETE /api/discos/{id} ---
[HttpDelete("{id}")]
public async Task<IActionResult> Borrar(int id)
{
var query = "DELETE FROM dbo.discos WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var filasAfectadas = await connection.ExecuteAsync(query, new { Id = id });
if (filasAfectadas == 0)
{
return NotFound("Disco no encontrado.");
}
return NoContent();
}
}
}
}

View File

@@ -0,0 +1,205 @@
using Dapper;
using Inventario.API.Data;
using Inventario.API.Helpers;
using Inventario.API.Models;
using Microsoft.AspNetCore.Mvc;
namespace Inventario.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class EquiposController : ControllerBase
{
private readonly DapperContext _context;
public EquiposController(DapperContext context)
{
_context = context;
}
// --- GET /api/equipos ---
// Consulta todos los equipos con sus relaciones
[HttpGet]
public async Task<IActionResult> Consultar()
{
// Query para traer todo en una sola llamada a la BD
var query = @"
SELECT
e.*,
s.Id as SectorId, s.Nombre as SectorNombre,
u.Id as UsuarioId, u.Username, u.Password,
d.Id as DiscoId, d.Mediatype, d.Size,
mr.Id as MemoriaRamId, mr.part_number as PartNumber, mr.Fabricante, mr.Tamano, mr.Velocidad, emr.Slot
FROM dbo.equipos e
LEFT JOIN dbo.sectores s ON e.sector_id = s.id
LEFT JOIN dbo.usuarios_equipos ue ON e.id = ue.equipo_id
LEFT JOIN dbo.usuarios u ON ue.usuario_id = u.id
LEFT JOIN dbo.equipos_discos ed ON e.id = ed.equipo_id
LEFT JOIN dbo.discos d ON ed.disco_id = d.id
LEFT JOIN dbo.equipos_memorias_ram emr ON e.id = emr.equipo_id
LEFT JOIN dbo.memorias_ram mr ON emr.memoria_ram_id = mr.id;";
using (var connection = _context.CreateConnection())
{
var equipoDict = new Dictionary<int, Equipo>();
await connection.QueryAsync<Equipo, Sector, Usuario, Disco, MemoriaRamDetalle, Equipo>(
query, (equipo, sector, usuario, disco, memoria) =>
{
if (!equipoDict.TryGetValue(equipo.Id, out var equipoActual))
{
equipoActual = equipo;
equipoActual.Sector = sector;
equipoDict.Add(equipoActual.Id, equipoActual);
}
if (usuario != null && !equipoActual.Usuarios.Any(u => u.Id == usuario.Id))
equipoActual.Usuarios.Add(usuario);
if (disco != null && !equipoActual.Discos.Any(d => d.Id == disco.Id))
equipoActual.Discos.Add(disco);
if (memoria != null && !equipoActual.MemoriasRam.Any(m => m.Id == memoria.Id && m.Slot == memoria.Slot))
equipoActual.MemoriasRam.Add(memoria);
return equipoActual;
},
splitOn: "SectorId,UsuarioId,DiscoId,MemoriaRamId"
);
return Ok(equipoDict.Values);
}
}
// --- GET /api/equipos/{hostname} ---
[HttpGet("{hostname}")]
public async Task<IActionResult> ConsultarDetalle(string hostname)
{
var query = @"SELECT
e.*,
s.Id as SectorId, s.Nombre as SectorNombre,
u.Id as UsuarioId, u.Username, u.Password
FROM dbo.equipos e
LEFT JOIN dbo.sectores s ON e.sector_id = s.id
LEFT JOIN dbo.usuarios_equipos ue ON e.id = ue.equipo_id
LEFT JOIN dbo.usuarios u ON ue.usuario_id = u.id
WHERE e.Hostname = @Hostname;";
using (var connection = _context.CreateConnection())
{
var equipoDict = new Dictionary<int, Equipo>();
var equipo = (await connection.QueryAsync<Equipo, Sector, Usuario, Equipo>(
query, (e, sector, usuario) =>
{
if (!equipoDict.TryGetValue(e.Id, out var equipoActual))
{
equipoActual = e;
equipoActual.Sector = sector;
equipoDict.Add(equipoActual.Id, equipoActual);
}
if (usuario != null && !equipoActual.Usuarios.Any(u => u.Id == usuario.Id))
equipoActual.Usuarios.Add(usuario);
return equipoActual;
},
new { Hostname = hostname },
splitOn: "SectorId,UsuarioId"
)).FirstOrDefault();
if (equipo == null) return NotFound("Equipo no encontrado.");
return Ok(equipo);
}
}
// --- POST /api/equipos/{hostname} ---
[HttpPost("{hostname}")]
public async Task<IActionResult> Ingresar(string hostname, [FromBody] Equipo equipoData)
{
var findQuery = "SELECT * FROM dbo.equipos WHERE Hostname = @Hostname;";
using (var connection = _context.CreateConnection())
{
var equipoExistente = await connection.QuerySingleOrDefaultAsync<Equipo>(findQuery, new { Hostname = hostname });
if (equipoExistente == null)
{
// Crear
var insertQuery = @"INSERT INTO dbo.equipos (Hostname, Ip, Mac, Motherboard, Cpu, Ram_installed, Ram_slots, Os, Architecture)
VALUES (@Hostname, @Ip, @Mac, @Motherboard, @Cpu, @Ram_installed, @Ram_slots, @Os, @Architecture);
SELECT CAST(SCOPE_IDENTITY() as int);";
var nuevoId = await connection.ExecuteScalarAsync<int>(insertQuery, equipoData);
equipoData.Id = nuevoId;
return CreatedAtAction(nameof(ConsultarDetalle), new { hostname = equipoData.Hostname }, equipoData);
}
else
{
// Actualizar y registrar historial
var cambios = new Dictionary<string, (string anterior, string nuevo)>();
// Comparamos campos para registrar en historial
if (equipoData.Ip != equipoExistente.Ip) cambios["ip"] = (equipoExistente.Ip, equipoData.Ip);
if (equipoData.Mac != equipoExistente.Mac) cambios["mac"] = (equipoExistente.Mac ?? "", equipoData.Mac ?? "");
var updateQuery = @"UPDATE dbo.equipos SET Ip = @Ip, Mac = @Mac, Motherboard = @Motherboard,
Cpu = @Cpu, Ram_installed = @Ram_installed, Ram_slots = @Ram_slots, Os = @Os, Architecture = @Architecture
WHERE Hostname = @Hostname;";
await connection.ExecuteAsync(updateQuery, equipoData);
if (cambios.Count > 0)
{
await HistorialHelper.RegistrarCambios(_context, equipoExistente.Id, cambios);
}
return Ok(equipoData);
}
}
}
// --- PUT /api/equipos/{id} ---
[HttpPut("{id}")]
public async Task<IActionResult> Actualizar(int id, [FromBody] Equipo equipoData)
{
var updateQuery = @"UPDATE dbo.equipos SET Hostname = @Hostname, Ip = @Ip, Mac = @Mac, Motherboard = @Motherboard,
Cpu = @Cpu, Ram_installed = @Ram_installed, Ram_slots = @Ram_slots, Os = @Os, Architecture = @Architecture
WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
// Asignamos el ID del parámetro de la ruta al objeto que recibimos.
equipoData.Id = id;
// Ahora pasamos el objeto completo a Dapper.
var filasAfectadas = await connection.ExecuteAsync(updateQuery, equipoData);
if (filasAfectadas == 0) return NotFound("Equipo no encontrado.");
return NoContent();
}
}
// --- DELETE /api/equipos/{id} ---
[HttpDelete("{id}")]
public async Task<IActionResult> Borrar(int id)
{
// La base de datos está configurada con ON DELETE CASCADE, por lo que las relaciones se borrarán automáticamente.
var query = "DELETE FROM dbo.equipos WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var filasAfectadas = await connection.ExecuteAsync(query, new { Id = id });
if (filasAfectadas == 0) return NotFound("Equipo no encontrado.");
return NoContent();
}
}
// --- GET /api/equipos/{hostname}/historial ---
[HttpGet("{hostname}/historial")]
public async Task<IActionResult> ConsultarHistorial(string hostname)
{
var query = @"SELECT h.* FROM dbo.historial_equipos h
JOIN dbo.equipos e ON h.equipo_id = e.id
WHERE e.Hostname = @Hostname
ORDER BY h.fecha_cambio DESC;";
using (var connection = _context.CreateConnection())
{
var equipo = await connection.QueryFirstOrDefaultAsync<Equipo>("SELECT Id FROM dbo.equipos WHERE Hostname = @Hostname", new { Hostname = hostname });
if (equipo == null) return NotFound("Equipo no encontrado.");
var historial = await connection.QueryAsync<HistorialEquipo>(query, new { Hostname = hostname });
return Ok(new { equipo = hostname, historial });
}
}
}
}

View File

@@ -0,0 +1,155 @@
using Dapper;
using Inventario.API.Data;
using Inventario.API.Models;
using Microsoft.AspNetCore.Mvc;
namespace Inventario.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class MemoriasRamController : ControllerBase
{
private readonly DapperContext _context;
public MemoriasRamController(DapperContext context)
{
_context = context;
}
// --- GET /api/memoriasram ---
[HttpGet]
public async Task<IActionResult> Consultar()
{
var query = "SELECT Id, part_number as PartNumber, Fabricante, Tamano, Velocidad FROM dbo.memorias_ram;";
using (var connection = _context.CreateConnection())
{
var memorias = await connection.QueryAsync<MemoriaRam>(query);
return Ok(memorias);
}
}
// --- GET /api/memoriasram/{id} ---
[HttpGet("{id}")]
public async Task<IActionResult> ConsultarDetalle(int id)
{
var query = "SELECT Id, part_number as PartNumber, Fabricante, Tamano, Velocidad FROM dbo.memorias_ram WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var memoria = await connection.QuerySingleOrDefaultAsync<MemoriaRam>(query, new { Id = id });
if (memoria == null)
{
return NotFound("Módulo de memoria RAM no encontrado.");
}
return Ok(memoria);
}
}
// --- POST /api/memoriasram ---
[HttpPost]
public async Task<IActionResult> Ingresar([FromBody] List<MemoriaRam> memorias)
{
// Consulta para verificar la existencia. Maneja correctamente los valores nulos.
var queryCheck = @"SELECT * FROM dbo.memorias_ram WHERE
(part_number = @Part_number OR (part_number IS NULL AND @Part_number IS NULL)) AND
(fabricante = @Fabricante OR (fabricante IS NULL AND @Fabricante IS NULL)) AND
tamano = @Tamano AND
(velocidad = @Velocidad OR (velocidad IS NULL AND @Velocidad IS NULL));";
var queryInsert = @"INSERT INTO dbo.memorias_ram (part_number, fabricante, tamano, velocidad)
VALUES (@Part_number, @Fabricante, @Tamano, @Velocidad);
SELECT CAST(SCOPE_IDENTITY() as int);";
var resultados = new List<object>();
using (var connection = _context.CreateConnection())
{
foreach (var memoria in memorias)
{
var existente = await connection.QuerySingleOrDefaultAsync<MemoriaRam>(queryCheck, memoria);
if (existente == null)
{
var nuevoId = await connection.ExecuteScalarAsync<int>(queryInsert, memoria);
var nuevaMemoria = new MemoriaRam
{
Id = nuevoId,
Part_number = memoria.Part_number,
Fabricante = memoria.Fabricante,
Tamano = memoria.Tamano,
Velocidad = memoria.Velocidad
};
resultados.Add(new { action = "created", registro = nuevaMemoria });
}
else
{
resultados.Add(new { action = "exists", registro = existente });
}
}
}
return Ok(resultados);
}
// --- PUT /api/memoriasram/{id} ---
[HttpPut("{id}")]
public async Task<IActionResult> Actualizar(int id, [FromBody] MemoriaRam memoria)
{
var query = @"UPDATE dbo.memorias_ram SET
part_number = @Part_number,
fabricante = @Fabricante,
tamano = @Tamano,
velocidad = @Velocidad
WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var filasAfectadas = await connection.ExecuteAsync(query, new { memoria.Part_number, memoria.Fabricante, memoria.Tamano, memoria.Velocidad, Id = id });
if (filasAfectadas == 0)
{
return NotFound("Módulo de memoria RAM no encontrado.");
}
memoria.Id = id;
return Ok(memoria);
}
}
// --- DELETE /api/memoriasram/{id} ---
[HttpDelete("{id}")]
public async Task<IActionResult> Borrar(int id)
{
var deleteAssociationsQuery = "DELETE FROM dbo.equipos_memorias_ram WHERE memoria_ram_id = @Id;";
var deleteRamQuery = "DELETE FROM dbo.memorias_ram WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
// Primero eliminamos las asociaciones en la tabla intermedia
await connection.ExecuteAsync(deleteAssociationsQuery, new { Id = id }, transaction: transaction);
// Luego eliminamos el módulo de RAM
var filasAfectadas = await connection.ExecuteAsync(deleteRamQuery, new { Id = id }, transaction: transaction);
if (filasAfectadas == 0)
{
// Si no se borró nada, hacemos rollback y devolvemos NotFound.
transaction.Rollback();
return NotFound("Módulo de memoria RAM no encontrado.");
}
// Si todo salió bien, confirmamos la transacción.
transaction.Commit();
return NoContent();
}
catch
{
transaction.Rollback();
throw; // Relanza la excepción para que sea manejada por el middleware de errores de ASP.NET Core
}
}
}
}
}
}

View File

@@ -5,40 +5,118 @@ using Microsoft.AspNetCore.Mvc;
namespace Inventario.API.Controllers namespace Inventario.API.Controllers
{ {
// [ApiController] habilita comportamientos estándar de API como la validación automática.
[ApiController] [ApiController]
// [Route("api/[controller]")] define la URL base para este controlador.
// "[controller]" se reemplaza por el nombre de la clase sin "Controller", es decir, "api/sectores".
[Route("api/[controller]")] [Route("api/[controller]")]
public class SectoresController : ControllerBase public class SectoresController : ControllerBase
{ {
// Campo privado para guardar la referencia a nuestro contexto de Dapper.
private readonly DapperContext _context; private readonly DapperContext _context;
// El constructor recibe el DapperContext a través de la inyección de dependencias que configuramos en Program.cs.
public SectoresController(DapperContext context) public SectoresController(DapperContext context)
{ {
_context = context; _context = context;
} }
// [HttpGet] marca este método para que responda a peticiones GET a la ruta base ("api/sectores"). // --- GET /api/sectores ---
// Método para obtener todos los sectores.
[HttpGet] [HttpGet]
public async Task<IActionResult> ConsultarSectores() public async Task<IActionResult> ConsultarSectores()
{ {
// Definimos nuestra consulta SQL. Es buena práctica listar las columnas explícitamente.
var query = "SELECT Id, Nombre FROM dbo.sectores ORDER BY Nombre;"; var query = "SELECT Id, Nombre FROM dbo.sectores ORDER BY Nombre;";
// Creamos una conexión a la base de datos. El 'using' asegura que la conexión se cierre y se libere correctamente, incluso si hay errores.
using (var connection = _context.CreateConnection()) using (var connection = _context.CreateConnection())
{ {
// Usamos el método QueryAsync de Dapper.
// <Sector> le dice a Dapper que mapee cada fila del resultado a un objeto de nuestra clase Sector.
// 'await' espera a que la base de datos responda sin bloquear el servidor.
var sectores = await connection.QueryAsync<Sector>(query); var sectores = await connection.QueryAsync<Sector>(query);
// Ok() crea una respuesta HTTP 200 OK y serializa la lista de sectores a formato JSON.
return Ok(sectores); return Ok(sectores);
} }
} }
// --- GET /api/sectores/{id} ---
// Método para obtener un único sector por su ID.
[HttpGet("{id}")]
public async Task<IActionResult> ConsultarSectorDetalle(int id)
{
var query = "SELECT Id, Nombre FROM dbo.sectores WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
// QuerySingleOrDefaultAsync es perfecto para obtener un solo registro.
// Devuelve el objeto si lo encuentra, o null si no existe.
var sector = await connection.QuerySingleOrDefaultAsync<Sector>(query, new { Id = id });
if (sector == null)
{
// Si no se encuentra, devolvemos un error HTTP 404 Not Found.
return NotFound();
}
return Ok(sector);
}
}
// --- POST /api/sectores ---
// Método para crear un nuevo sector.
// Nota: Cambiado de /:nombre a un POST estándar que recibe el objeto en el body. Es más RESTful.
[HttpPost]
public async Task<IActionResult> IngresarSector([FromBody] Sector sector)
{
var checkQuery = "SELECT COUNT(1) FROM dbo.sectores WHERE Nombre = @Nombre;";
var insertQuery = "INSERT INTO dbo.sectores (Nombre) VALUES (@Nombre); SELECT CAST(SCOPE_IDENTITY() as int);";
using (var connection = _context.CreateConnection())
{
// Primero, verificamos si ya existe un sector con ese nombre.
var existe = await connection.ExecuteScalarAsync<bool>(checkQuery, new { sector.Nombre });
if (existe)
{
// Si ya existe, devolvemos un error HTTP 409 Conflict.
return Conflict($"Ya existe un sector con el nombre '{sector.Nombre}'");
}
// ExecuteScalarAsync ejecuta la consulta y devuelve el primer valor de la primera fila (el nuevo ID).
var nuevoId = await connection.ExecuteScalarAsync<int>(insertQuery, new { sector.Nombre });
var nuevoSector = new Sector { Id = nuevoId, Nombre = sector.Nombre };
// Devolvemos una respuesta HTTP 201 Created, con la ubicación del nuevo recurso y el objeto creado.
return CreatedAtAction(nameof(ConsultarSectorDetalle), new { id = nuevoId }, nuevoSector);
}
}
// --- PUT /api/sectores/{id} ---
// Método para actualizar un sector existente.
[HttpPut("{id}")]
public async Task<IActionResult> ActualizarSector(int id, [FromBody] Sector sector)
{
var query = "UPDATE dbo.sectores SET Nombre = @Nombre WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
// ExecuteAsync devuelve el número de filas afectadas.
var filasAfectadas = await connection.ExecuteAsync(query, new { Nombre = sector.Nombre, Id = id });
if (filasAfectadas == 0)
{
// Si no se afectó ninguna fila, significa que el ID no existía.
return NotFound();
}
// Devolvemos HTTP 204 No Content para indicar que la actualización fue exitosa pero no hay nada que devolver.
return NoContent();
}
}
// --- DELETE /api/sectores/{id} ---
// Método para eliminar un sector.
[HttpDelete("{id}")]
public async Task<IActionResult> BorrarSector(int id)
{
var query = "DELETE FROM dbo.sectores WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var filasAfectadas = await connection.ExecuteAsync(query, new { Id = id });
if (filasAfectadas == 0)
{
return NotFound();
}
return NoContent();
}
}
} }
} }

View File

@@ -0,0 +1,123 @@
using Dapper;
using Inventario.API.Data;
using Inventario.API.Models;
using Microsoft.AspNetCore.Mvc;
namespace Inventario.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UsuariosController : ControllerBase
{
private readonly DapperContext _context;
public UsuariosController(DapperContext context)
{
_context = context;
}
// --- GET /api/usuarios ---
[HttpGet]
public async Task<IActionResult> Consultar()
{
var query = "SELECT Id, Username, Password, Created_at, Updated_at FROM dbo.usuarios ORDER BY Username;";
using (var connection = _context.CreateConnection())
{
var usuarios = await connection.QueryAsync<Usuario>(query);
return Ok(usuarios);
}
}
// --- GET /api/usuarios/{id} ---
[HttpGet("{id}")]
public async Task<IActionResult> ConsultarDetalle(int id)
{
var query = "SELECT Id, Username, Password, Created_at, Updated_at FROM dbo.usuarios WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var usuario = await connection.QuerySingleOrDefaultAsync<Usuario>(query, new { Id = id });
if (usuario == null)
{
return NotFound("Usuario no encontrado.");
}
return Ok(usuario);
}
}
// --- POST /api/usuarios ---
// Este endpoint replica la lógica "upsert" del original: si el usuario existe, lo actualiza; si no, lo crea.
[HttpPost]
public async Task<IActionResult> Ingresar([FromBody] Usuario usuario)
{
var findQuery = "SELECT * FROM dbo.usuarios WHERE Username = @Username;";
using (var connection = _context.CreateConnection())
{
var usuarioExistente = await connection.QuerySingleOrDefaultAsync<Usuario>(findQuery, new { usuario.Username });
if (usuarioExistente != null)
{
// El usuario ya existe, lo actualizamos (solo la contraseña si viene)
var updateQuery = "UPDATE dbo.usuarios SET Password = @Password WHERE Id = @Id;";
await connection.ExecuteAsync(updateQuery, new { usuario.Password, Id = usuarioExistente.Id });
// Devolvemos el usuario actualizado
var usuarioActualizado = await connection.QuerySingleOrDefaultAsync<Usuario>(findQuery, new { usuario.Username });
return Ok(usuarioActualizado);
}
else
{
// El usuario no existe, lo creamos
var insertQuery = "INSERT INTO dbo.usuarios (Username, Password) VALUES (@Username, @Password); SELECT CAST(SCOPE_IDENTITY() as int);";
var nuevoId = await connection.ExecuteScalarAsync<int>(insertQuery, new { usuario.Username, usuario.Password });
var nuevoUsuario = new Usuario
{
Id = nuevoId,
Username = usuario.Username,
Password = usuario.Password
};
return CreatedAtAction(nameof(ConsultarDetalle), new { id = nuevoId }, nuevoUsuario);
}
}
}
// --- PUT /api/usuarios/{id} ---
// Endpoint específico para actualizar la contraseña, como en el original.
[HttpPut("{id}")]
public async Task<IActionResult> Actualizar(int id, [FromBody] Usuario data)
{
var updateQuery = "UPDATE dbo.usuarios SET Password = @Password WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var filasAfectadas = await connection.ExecuteAsync(updateQuery, new { data.Password, Id = id });
if (filasAfectadas == 0)
{
return NotFound("Usuario no encontrado.");
}
// Para replicar la respuesta del backend original, volvemos a consultar el usuario (sin la contraseña).
var selectQuery = "SELECT Id, Username FROM dbo.usuarios WHERE Id = @Id;";
var usuarioActualizado = await connection.QuerySingleOrDefaultAsync(selectQuery, new { Id = id });
return Ok(usuarioActualizado);
}
}
// --- DELETE /api/usuarios/{id} ---
[HttpDelete("{id}")]
public async Task<IActionResult> Borrar(int id)
{
var query = "DELETE FROM dbo.usuarios WHERE Id = @Id;";
using (var connection = _context.CreateConnection())
{
var filasAfectadas = await connection.ExecuteAsync(query, new { Id = id });
if (filasAfectadas == 0)
{
return NotFound("Usuario no encontrado.");
}
return NoContent(); // Respuesta HTTP 204 No Content
}
}
}
}

View File

@@ -0,0 +1,29 @@
using Dapper;
using Inventario.API.Data;
using Inventario.API.Models;
namespace Inventario.API.Helpers
{
public static class HistorialHelper
{
public static async Task RegistrarCambios(DapperContext context, int equipoId, Dictionary<string, (string anterior, string nuevo)> cambios)
{
var query = @"INSERT INTO dbo.historial_equipos (equipo_id, campo_modificado, valor_anterior, valor_nuevo)
VALUES (@EquipoId, @CampoModificado, @ValorAnterior, @ValorNuevo);";
using (var connection = context.CreateConnection())
{
foreach (var cambio in cambios)
{
await connection.ExecuteAsync(query, new
{
EquipoId = equipoId,
CampoModificado = cambio.Key,
ValorAnterior = cambio.Value.anterior,
ValorNuevo = cambio.Value.nuevo
});
}
}
}
}
}

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Inventario.API")] [assembly: System.Reflection.AssemblyCompanyAttribute("Inventario.API")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+80eeac45d8b90ed69a6479e9d3dcadde5e317a90")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+10f2f2ba67eac900e8a7b63ced2f3689c309fa6f")]
[assembly: System.Reflection.AssemblyProductAttribute("Inventario.API")] [assembly: System.Reflection.AssemblyProductAttribute("Inventario.API")]
[assembly: System.Reflection.AssemblyTitleAttribute("Inventario.API")] [assembly: System.Reflection.AssemblyTitleAttribute("Inventario.API")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]