You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:24:53