213 lines
7.1 KiB
PHP
213 lines
7.1 KiB
PHP
<?php
|
|
/**
|
|
* ActividadAdmin — Trazabilidad del módulo administrativo de laboratorio
|
|
* Registra toda acción realizada sobre las entidades del módulo.
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../classes/Database.php';
|
|
|
|
class ActividadAdmin {
|
|
|
|
private Database $db;
|
|
|
|
public function __construct() {
|
|
$this->db = Database::getInstance();
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Registro de actividad
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Registra una acción en el log.
|
|
*
|
|
* @param int|null $adminId admin_users.id
|
|
* @param string $modulo pacientes|ordenes|domicilios|enfermeras|asignaciones
|
|
* @param string $accion crear|editar|eliminar|autorizar|rechazar|asignar...
|
|
* @param int|null $entidadId ID del registro afectado
|
|
* @param mixed $detalle Texto o array (se serializa como JSON)
|
|
*/
|
|
public function registrar(
|
|
?int $adminId,
|
|
string $modulo,
|
|
string $accion,
|
|
?int $entidadId = null,
|
|
$detalle = null
|
|
): int {
|
|
$adminNombre = null;
|
|
|
|
if ($adminId) {
|
|
$row = $this->db->fetch(
|
|
'SELECT full_name FROM admin_users WHERE id = ?',
|
|
[$adminId]
|
|
);
|
|
$adminNombre = $row['full_name'] ?? null;
|
|
}
|
|
|
|
$detalleStr = is_array($detalle) || is_object($detalle)
|
|
? json_encode($detalle, JSON_UNESCAPED_UNICODE)
|
|
: (string)($detalle ?? '');
|
|
|
|
// Resolver IP real del cliente (detrás de proxy/Coolify/Nginx)
|
|
$ip = null;
|
|
foreach (['HTTP_X_FORWARDED_FOR','HTTP_X_REAL_IP','HTTP_CF_CONNECTING_IP','REMOTE_ADDR'] as $h) {
|
|
if (!empty($_SERVER[$h])) {
|
|
$ip = trim(explode(',', $_SERVER[$h])[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $this->db->insert('lab_actividad_admin', [
|
|
'admin_id' => $adminId,
|
|
'admin_nombre'=> $adminNombre,
|
|
'modulo' => $modulo,
|
|
'accion' => $accion,
|
|
'entidad_id' => $entidadId,
|
|
'detalle' => $detalleStr,
|
|
'ip_address' => $ip,
|
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
|
|
]);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Consultas
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Actividad reciente del módulo.
|
|
*
|
|
* @param int $limit
|
|
* @param string $modulo Filtro opcional por módulo
|
|
*/
|
|
public function reciente(int $limit = 50, string $modulo = ''): array {
|
|
$where = $modulo ? 'WHERE a.modulo = ?' : '';
|
|
$params = $modulo ? [$modulo] : [];
|
|
$params[] = $limit;
|
|
|
|
return $this->db->fetchAll("
|
|
SELECT
|
|
a.*,
|
|
u.username AS admin_username
|
|
FROM lab_actividad_admin a
|
|
LEFT JOIN admin_users u ON u.id = a.admin_id
|
|
$where
|
|
ORDER BY a.created_at DESC
|
|
LIMIT ?
|
|
", $params);
|
|
}
|
|
|
|
/**
|
|
* Actividad de un administrador específico.
|
|
*/
|
|
public function porAdmin(int $adminId, int $limit = 100): array {
|
|
return $this->db->fetchAll("
|
|
SELECT * FROM lab_actividad_admin
|
|
WHERE admin_id = ?
|
|
ORDER BY created_at DESC
|
|
LIMIT ?
|
|
", [$adminId, $limit]);
|
|
}
|
|
|
|
/**
|
|
* Actividad sobre una entidad concreta.
|
|
*/
|
|
public function porEntidad(string $modulo, int $entidadId): array {
|
|
return $this->db->fetchAll("
|
|
SELECT
|
|
a.*,
|
|
u.username AS admin_username
|
|
FROM lab_actividad_admin a
|
|
LEFT JOIN admin_users u ON u.id = a.admin_id
|
|
WHERE a.modulo = ? AND a.entidad_id = ?
|
|
ORDER BY a.created_at ASC
|
|
", [$modulo, $entidadId]);
|
|
}
|
|
|
|
/**
|
|
* Estadísticas de actividad por módulo en un rango de fechas.
|
|
*/
|
|
public function estadisticas(string $desde, string $hasta): array {
|
|
return $this->db->fetchAll("
|
|
SELECT
|
|
modulo,
|
|
accion,
|
|
COUNT(*) AS total
|
|
FROM lab_actividad_admin
|
|
WHERE DATE(created_at) BETWEEN ? AND ?
|
|
GROUP BY modulo, accion
|
|
ORDER BY modulo, total DESC
|
|
", [$desde, $hasta]);
|
|
}
|
|
|
|
/**
|
|
* Filtrado avanzado con paginación.
|
|
*
|
|
* @param array $opts {desde, hasta, modulo, accion, admin_id, buscar, page, per_page}
|
|
* @return array {data, total, paginas, pagina}
|
|
*/
|
|
public function filtrar(array $opts = []): array {
|
|
$where = [];
|
|
$params = [];
|
|
|
|
if (!empty($opts['desde'])) {
|
|
$where[] = 'DATE(a.created_at) >= ?';
|
|
$params[] = $opts['desde'];
|
|
}
|
|
if (!empty($opts['hasta'])) {
|
|
$where[] = 'DATE(a.created_at) <= ?';
|
|
$params[] = $opts['hasta'];
|
|
}
|
|
if (!empty($opts['modulo'])) {
|
|
$where[] = 'a.modulo = ?';
|
|
$params[] = $opts['modulo'];
|
|
}
|
|
if (!empty($opts['accion'])) {
|
|
$where[] = 'a.accion = ?';
|
|
$params[] = $opts['accion'];
|
|
}
|
|
if (!empty($opts['admin_id'])) {
|
|
$where[] = 'a.admin_id = ?';
|
|
$params[] = (int)$opts['admin_id'];
|
|
}
|
|
if (!empty($opts['buscar'])) {
|
|
$where[] = '(a.admin_nombre LIKE ? OR a.detalle LIKE ?)';
|
|
$like = '%' . $opts['buscar'] . '%';
|
|
$params[] = $like;
|
|
$params[] = $like;
|
|
}
|
|
|
|
$whereStr = $where ? 'WHERE ' . implode(' AND ', $where) : '';
|
|
|
|
$total = (int)($this->db->fetch(
|
|
"SELECT COUNT(*) AS n FROM lab_actividad_admin a $whereStr",
|
|
$params
|
|
)['n'] ?? 0);
|
|
|
|
$pp = max(1, (int)($opts['per_page'] ?? 50));
|
|
$pag = max(1, (int)($opts['page'] ?? 1));
|
|
$off = ($pag - 1) * $pp;
|
|
|
|
$pParams = $params;
|
|
$pParams[] = $pp;
|
|
$pParams[] = $off;
|
|
|
|
$data = $this->db->fetchAll("
|
|
SELECT
|
|
a.*,
|
|
u.username AS admin_username
|
|
FROM lab_actividad_admin a
|
|
LEFT JOIN admin_users u ON u.id = a.admin_id
|
|
$whereStr
|
|
ORDER BY a.created_at DESC
|
|
LIMIT ? OFFSET ?
|
|
", $pParams);
|
|
|
|
return [
|
|
'data' => $data,
|
|
'total' => $total,
|
|
'paginas' => (int)ceil($total / $pp),
|
|
'pagina' => $pag,
|
|
];
|
|
}
|
|
}
|