如何在MySQL数据插入后触发PHP仪表盘刷新(避免定时刷新)
插入打卡数据后触发仪表盘实时刷新的方案
下面是几种无需固定间隔刷新的实现方式,根据你的场景选择最合适的:
1. Server-Sent Events (SSE) 推荐
SSE是HTML5原生的单向推送方案,后端主动向前端发送更新,实现简单,无需额外依赖,适合这种仅需后端通知前端刷新的场景。
实现步骤:
第一步:创建更新日志表
用来记录打卡数据的最新更新时间,让SSE接口可以检测到变化:CREATE TABLE update_log ( id INT PRIMARY KEY DEFAULT 1, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT single_row CHECK (id = 1) ); INSERT INTO update_log VALUES (1, NOW());第二步:修改打卡插入逻辑
在执行INSERT插入打卡记录后,更新日志表的时间戳:// 假设$pdo是你的MySQL连接实例 $stmt = $pdo->prepare("INSERT INTO punch_records (user_id, punch_time, type) VALUES (?, ?, ?)"); $stmt->execute([$userId, $punchTime, $type]); // 更新最新时间戳 $pdo->query("UPDATE update_log SET last_update_time = NOW() WHERE id = 1");第三步:编写SSE推送接口
创建stream_updates.php,保持与前端的长连接,检测到更新时推送最新数据:header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); $lastUpdate = $_GET['last_update'] ?? 0; // 将时间戳转为Unix时间戳方便比较 $lastUpdate = strtotime($lastUpdate); while (true) { // 查询最新更新时间 $stmt = $pdo->query("SELECT UNIX_TIMESTAMP(last_update_time) AS ts FROM update_log LIMIT 1"); $currentTs = $stmt->fetchColumn(); if ($currentTs > $lastUpdate) { // 获取最新10条打卡记录 $stmt = $pdo->query("SELECT * FROM punch_records ORDER BY created_at DESC LIMIT 10"); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); // 推送数据(SSE格式要求以data:开头,末尾加两个换行) echo "data: " . json_encode([ 'records' => $records, 'last_update' => date('Y-m-d H:i:s', $currentTs) ]) . "\n\n"; ob_flush(); flush(); $lastUpdate = $currentTs; } // 每5秒检测一次,避免过度消耗数据库资源 sleep(5); }第四步:前端接收更新并刷新
在仪表盘页面用EventSource连接SSE接口,收到数据后更新DOM:let lastUpdate = '0'; let eventSource = new EventSource(`stream_updates.php?last_update=${lastUpdate}`); eventSource.onmessage = function(e) { const res = JSON.parse(e.data); const recordsList = document.getElementById('records-table-body'); // 清空现有内容并插入新记录 recordsList.innerHTML = ''; res.records.forEach(record => { const row = document.createElement('tr'); row.innerHTML = ` <td>${record.user_id}</td> <td>${record.punch_time}</td> <td>${record.type === 1 ? '上班打卡' : '下班打卡'}</td> `; recordsList.appendChild(row); }); // 更新最后更新时间,重置连接避免超时 lastUpdate = res.last_update; eventSource.close(); eventSource = new EventSource(`stream_updates.php?last_update=${lastUpdate}`); }; // 连接出错时自动重试 eventSource.onerror = function() { setTimeout(() => { eventSource = new EventSource(`stream_updates.php?last_update=${lastUpdate}`); }, 5000); };
2. WebSocket 双向交互场景适用
如果未来需要前端给后端发送指令(比如手动刷新、筛选数据),可以用WebSocket实现双向通信。这里用PHP的Ratchet库来实现:
实现步骤:
安装Ratchet
composer require cboden/ratchet创建WebSocket服务器
新建punch_server.php:require __DIR__ . '/vendor/autoload.php'; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; class PunchUpdateServer implements MessageComponentInterface { protected $clients; public function __construct() { $this->clients = new \SplObjectStorage; } public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); } public function onMessage(ConnectionInterface $from, $msg) { // 收到后端的更新通知,转发给所有连接的前端客户端 foreach ($this->clients as $client) { if ($from !== $client) { $client->send($msg); } } } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); } public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } } $server = IoServer::factory( new HttpServer(new WsServer(new PunchUpdateServer())), 8080 ); $server->run();修改打卡插入逻辑
插入记录后给WebSocket服务器发送通知:// 执行插入操作后 $client = new \WebSocket\Client("ws://localhost:8080"); $client->send('punch_updated'); $client->close();前端监听WebSocket消息
const ws = new WebSocket('ws://localhost:8080'); ws.onmessage = function(e) { if (e.data === 'punch_updated') { // 请求最新打卡数据并更新DOM fetch('get_latest_punches.php') .then(res => res.json()) .then(records => { const recordsList = document.getElementById('records-table-body'); recordsList.innerHTML = ''; records.forEach(record => { const row = document.createElement('tr'); row.innerHTML = ` <td>${record.user_id}</td> <td>${record.punch_time}</td> <td>${record.type === 1 ? '上班打卡' : '下班打卡'}</td> `; recordsList.appendChild(row); }); }); } }; // 连接失败自动重试 ws.onerror = function() { setTimeout(() => { window.ws = new WebSocket('ws://localhost:8080'); }, 5000); };
3. 长轮询 兼容旧浏览器
如果需要兼容不支持SSE/WebSocket的旧浏览器,可以用长轮询:后端在没有新数据时hold住请求,直到有更新或超时,再返回结果。
后端接口get_latest_punches.php:
header('Content-Type: application/json'); header('Cache-Control: no-cache'); $lastId = $_GET['last_id'] ?? 0; $timeout = 30; // 超时30秒 $startTime = time(); while (true) { // 查询是否有新记录 $stmt = $pdo->query("SELECT * FROM punch_records WHERE id > $lastId ORDER BY created_at DESC LIMIT 10"); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); if (!empty($records)) { echo json_encode([ 'records' => $records, 'last_id' => $records[0]['id'] ]); exit; } // 超时则返回空数据 if (time() - $startTime >= $timeout) { echo json_encode(['records' => [], 'last_id' => $lastId]); exit; } sleep(1); }
前端JS:
let lastId = 0; function fetchNewRecords() { fetch(`get_latest_punches.php?last_id=${lastId}`) .then(res => res.json()) .then(data => { if (data.records.length > 0) { const recordsList = document.getElementById('records-table-body'); recordsList.innerHTML = ''; data.records.forEach(record => { const row = document.createElement('tr'); row.innerHTML = ` <td>${record.user_id}</td> <td>${record.punch_time}</td> <td>${record.type === 1 ? '上班打卡' : '下班打卡'}</td> `; recordsList.appendChild(row); }); lastId = data.last_id; } // 立即发起下一次请求 fetchNewRecords(); }) .catch(() => { // 请求失败5秒后重试 setTimeout(fetchNewRecords, 5000); }); } // 初始化请求 fetchNewRecords();
内容的提问来源于stack exchange,提问作者cornacum
相关产品推荐
相关产品推荐

