Files
kitchen/docs/api/kitchen_sse.php
2026-04-17 07:00:26 +08:00

160 lines
4.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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' => '连接超时,请重新连接'));