= :since'; $params[':since'] = $since; } if ($until !== null) { $where[] = 'ae.createdAt <= :until'; $params[':until'] = $until; } if ($action !== '') { $where[] = 'ae.action = :action'; $params[':action'] = $action; } $whereSql = implode(' AND ', $where); $countStmt = $pdo->prepare("SELECT COUNT(*) FROM activity_event ae WHERE $whereSql"); $countStmt->execute($params); $total = (int)$countStmt->fetchColumn(); $sql = " SELECT ae.eventID, ae.createdAt, ae.actorUserID, ae.actorUsername, ae.actorRole, ae.actorEntityID, ae.action, ae.targetType, ae.targetID, ae.summary, ae.detailsJson FROM activity_event ae WHERE $whereSql ORDER BY ae.createdAt DESC LIMIT :lim OFFSET :off "; $stmt = $pdo->prepare($sql); foreach ($params as $k => $v) { $stmt->bindValue($k, $v); } $stmt->bindValue(':lim', $limit, PDO::PARAM_INT); $stmt->bindValue(':off', $offset, PDO::PARAM_INT); $stmt->execute(); $events = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($events as &$e) { $e['createdAt'] = (int)$e['createdAt']; $e['details'] = json_decode($e['detailsJson'] ?? '{}', true) ?: []; unset($e['detailsJson']); } unset($e); qdb_discard($tmpDb, $lockFp); json_success([ 'events' => $events, 'total' => $total, 'limit' => $limit, 'offset' => $offset, ]);