| | | 1 | | using System.Text.Json; |
| | | 2 | | using FAU.DataAccess.Repositories; |
| | | 3 | | using FAU.Entidades; |
| | | 4 | | using Serilog; |
| | | 5 | | |
| | | 6 | | namespace FAU.Logica.Services; |
| | | 7 | | |
| | | 8 | | public class AuditoriaService : IAuditoriaService |
| | | 9 | | { |
| | | 10 | | private readonly IAuditoriaRepository _auditoriaRepository; |
| | | 11 | | private readonly IAuditoriaCatalogoResolver _catalogoResolver; |
| | | 12 | | |
| | 2 | 13 | | public AuditoriaService(IAuditoriaRepository auditoriaRepository, IAuditoriaCatalogoResolver catalogoResolver) |
| | | 14 | | { |
| | 2 | 15 | | _auditoriaRepository = auditoriaRepository ?? throw new ArgumentNullException(nameof(auditoriaRepository)); |
| | 2 | 16 | | _catalogoResolver = catalogoResolver ?? throw new ArgumentNullException(nameof(catalogoResolver)); |
| | 2 | 17 | | } |
| | | 18 | | |
| | | 19 | | public Task<(IEnumerable<BitacoraAuditoria> Items, int TotalCount)> GetPagedAsync( |
| | | 20 | | int page, |
| | | 21 | | int pageSize, |
| | | 22 | | string? contexto = null, |
| | | 23 | | string? accion = null, |
| | | 24 | | string? usuario = null, |
| | | 25 | | DateTime? desde = null, |
| | | 26 | | DateTime? hasta = null) |
| | | 27 | | { |
| | 1 | 28 | | return _auditoriaRepository.GetPagedAsync(page, pageSize, contexto, accion, usuario, desde, hasta); |
| | | 29 | | } |
| | | 30 | | |
| | | 31 | | /// <summary> |
| | | 32 | | /// Overload recomendado: recibe enums (centralizado) pero resuelve IDs reales por nombre en BD. |
| | | 33 | | /// Esto evita romper auditor�a cuando los IDs de cat�logo est�n corridos entre ambientes. |
| | | 34 | | /// </summary> |
| | | 35 | | public async Task LogAuditoriaAsync( |
| | | 36 | | long usuarioId, |
| | | 37 | | AccionEnum accion, |
| | | 38 | | ContextoEnum contexto, |
| | | 39 | | string? host = null, |
| | | 40 | | string? entidad = null, |
| | | 41 | | long? entidadId = null, |
| | | 42 | | object? detalle = null) |
| | | 43 | | { |
| | 0 | 44 | | var accionId = await _catalogoResolver.GetAccionIdAsync(accion.ToString()); |
| | 0 | 45 | | var contextoId = await _catalogoResolver.GetContextoIdAsync(contexto.ToString()); |
| | | 46 | | |
| | 0 | 47 | | await LogAuditoriaAsync(usuarioId, (int)accionId, (int)contextoId, host, entidad, entidadId, detalle); |
| | 0 | 48 | | } |
| | | 49 | | |
| | | 50 | | public async Task LogAuditoriaAsync( |
| | | 51 | | long usuarioId, |
| | | 52 | | int accionId, |
| | | 53 | | int contextoId, |
| | | 54 | | string? host = null, |
| | | 55 | | string? entidad = null, |
| | | 56 | | long? entidadId = null, |
| | | 57 | | object? detalle = null) |
| | | 58 | | { |
| | 1 | 59 | | var hostFinal = string.IsNullOrWhiteSpace(host) ? "Desconocida" : host; |
| | | 60 | | |
| | 1 | 61 | | string? detalleJson = null; |
| | 1 | 62 | | if (detalle != null) |
| | | 63 | | { |
| | 0 | 64 | | detalleJson = detalle is string s |
| | 0 | 65 | | ? s |
| | 0 | 66 | | : JsonSerializer.Serialize(detalle, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy. |
| | | 67 | | } |
| | | 68 | | |
| | | 69 | | // 1) Persistir en DB (fuente de verdad) |
| | 1 | 70 | | await _auditoriaRepository.CreateAsync(new BitacoraAuditoria |
| | 1 | 71 | | { |
| | 1 | 72 | | UsuarioId = usuarioId, |
| | 1 | 73 | | AccionId = accionId, |
| | 1 | 74 | | ContextoId = contextoId, |
| | 1 | 75 | | Host = hostFinal, |
| | 1 | 76 | | Fecha = DateTime.UtcNow, |
| | 1 | 77 | | Entidad = entidad, |
| | 1 | 78 | | EntidadId = entidadId, |
| | 1 | 79 | | Detalle = detalleJson |
| | 1 | 80 | | }); |
| | | 81 | | |
| | | 82 | | // 2) Log estructurado (�til para troubleshooting) |
| | 1 | 83 | | Log.Logger |
| | 1 | 84 | | .ForContext("usuario_id", usuarioId) |
| | 1 | 85 | | .ForContext("accion_id", accionId) |
| | 1 | 86 | | .ForContext("contexto_id", contextoId) |
| | 1 | 87 | | .ForContext("host", hostFinal) |
| | 1 | 88 | | .ForContext("entidad", entidad) |
| | 1 | 89 | | .ForContext("entidad_id", entidadId) |
| | 1 | 90 | | .ForContext("detalle", detalleJson) |
| | 1 | 91 | | .Information("Auditoria {usuario_id} {accion_id} {contexto_id} {host}", usuarioId, accionId, contextoId, hos |
| | 1 | 92 | | } |
| | | 93 | | |
| | | 94 | | public void LogAuditoria(long usuarioId, int accionId, int contextoId, string host) |
| | | 95 | | { |
| | | 96 | | // Mantener firma anterior: log + persistencia best-effort. |
| | 1 | 97 | | _ = LogAuditoriaAsync(usuarioId, accionId, contextoId, host); |
| | 1 | 98 | | } |
| | | 99 | | } |