160 lines
4.4 KiB
PHP
160 lines
4.4 KiB
PHP
<?php
|
||
/**
|
||
* 🍽️ 点餐助手 SSE 实时推送端点
|
||
*
|
||
* Server-Sent Events 实现实时更新
|
||
* 网页端连接: new EventSource('kitchen_sse.php?order_id=xxx')
|
||
* App端更新订单后,kitchen.php 会更新 last_update.json 时间戳
|
||
* SSE 端检测到时间戳变化后推送更新给网页端
|
||
*
|
||
* @file kitchen_sse.php
|
||
* @date 2026-04-17
|
||
* @version 1.0.0
|
||
* @desc SSE实时推送,监听订单变化并推送
|
||
* @lastUpdate 2026-04-17 初始创建
|
||
*/
|
||
|
||
// ─── SSE响应头 ───
|
||
header('Content-Type: text/event-stream; charset=utf-8');
|
||
header('Cache-Control: no-cache');
|
||
header('Connection: keep-alive');
|
||
header('Access-Control-Allow-Origin: *');
|
||
header('X-Accel-Buffering: no');
|
||
|
||
// 禁用PHP输出缓冲
|
||
if (ob_get_level()) ob_end_clean();
|
||
|
||
$order_id = trim($_GET['order_id'] ?? '');
|
||
$last_timestamp = (float)($_GET['last_timestamp'] ?? 0);
|
||
|
||
$dataDir = dirname(__FILE__) . '/cache/kitchen/';
|
||
if (!is_dir($dataDir)) {
|
||
if (!@mkdir($dataDir, 0755, true)) {
|
||
$tmpDir = sys_get_temp_dir() . '/kitchen/';
|
||
if (!is_dir($tmpDir)) {
|
||
@mkdir($tmpDir, 0755, true);
|
||
}
|
||
$dataDir = $tmpDir;
|
||
}
|
||
}
|
||
if (!is_writable($dataDir)) {
|
||
$tmpDir = sys_get_temp_dir() . '/kitchen/';
|
||
if (!is_dir($tmpDir)) {
|
||
@mkdir($tmpDir, 0755, true);
|
||
}
|
||
$dataDir = $tmpDir;
|
||
}
|
||
$updateFlagFile = $dataDir . 'last_update.json';
|
||
$ordersFile = $dataDir . 'orders.json';
|
||
|
||
$maxIterations = 120; // 最多运行2分钟(每次1秒)
|
||
$iteration = 0;
|
||
|
||
// ─── 发送SSE事件 ───
|
||
function send_sse($event, $data) {
|
||
echo "event: {$event}\n";
|
||
echo "data: " . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n";
|
||
if (function_exists('fastcgi_finish_request')) {
|
||
// 不调用fastcgi_finish_request,保持连接
|
||
}
|
||
ob_flush();
|
||
flush();
|
||
}
|
||
|
||
// ─── 读取更新标记 ───
|
||
function read_update_flag() {
|
||
global $updateFlagFile;
|
||
if (!file_exists($updateFlagFile)) {
|
||
return array('timestamp' => 0, 'time' => '');
|
||
}
|
||
$content = file_get_contents($updateFlagFile);
|
||
$data = json_decode($content, true);
|
||
return is_array($data) ? $data : array('timestamp' => 0, 'time' => '');
|
||
}
|
||
|
||
// ─── 读取订单数据 ───
|
||
function read_order_by_id($id) {
|
||
global $ordersFile;
|
||
if (!file_exists($ordersFile)) {
|
||
return null;
|
||
}
|
||
$content = file_get_contents($ordersFile);
|
||
$orders = json_decode($content, true);
|
||
if (!is_array($orders)) return null;
|
||
|
||
foreach ($orders as $order) {
|
||
if (isset($order['id']) && $order['id'] === $id) {
|
||
return $order;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// ─── 发送初始连接事件 ───
|
||
send_sse('connected', array(
|
||
'message' => 'SSE连接已建立',
|
||
'order_id' => $order_id,
|
||
'timestamp' => microtime(true),
|
||
));
|
||
|
||
// ─── 如果有order_id,立即发送当前订单数据 ───
|
||
if (!empty($order_id)) {
|
||
$order = read_order_by_id($order_id);
|
||
if ($order !== null) {
|
||
send_sse('order_update', $order);
|
||
}
|
||
}
|
||
|
||
// ─── 主循环:监听变化 ───
|
||
$lastKnownTimestamp = read_update_flag()['timestamp'];
|
||
|
||
while ($iteration < $maxIterations) {
|
||
$iteration++;
|
||
|
||
// 检查客户端是否断开
|
||
if (connection_aborted()) {
|
||
break;
|
||
}
|
||
|
||
// 检查更新标记
|
||
$flag = read_update_flag();
|
||
$currentTimestamp = (float)($flag['timestamp'] ?? 0);
|
||
|
||
if ($currentTimestamp > $lastKnownTimestamp) {
|
||
$lastKnownTimestamp = $currentTimestamp;
|
||
|
||
// 有更新,推送数据
|
||
if (!empty($order_id)) {
|
||
// 推送特定订单
|
||
$order = read_order_by_id($order_id);
|
||
if ($order !== null) {
|
||
send_sse('order_update', $order);
|
||
} else {
|
||
send_sse('order_deleted', array(
|
||
'order_id' => $order_id,
|
||
'timestamp' => $currentTimestamp,
|
||
));
|
||
}
|
||
} else {
|
||
// 推送全局更新通知
|
||
send_sse('global_update', array(
|
||
'timestamp' => $currentTimestamp,
|
||
'time' => $flag['time'] ?? '',
|
||
));
|
||
}
|
||
}
|
||
|
||
// 心跳包(每10秒)
|
||
if ($iteration % 10 === 0) {
|
||
send_sse('heartbeat', array(
|
||
'timestamp' => microtime(true),
|
||
'iteration' => $iteration,
|
||
));
|
||
}
|
||
|
||
sleep(1);
|
||
}
|
||
|
||
// ─── 连接关闭 ───
|
||
send_sse('close', array('message' => '连接超时,请重新连接'));
|