forked from erp-dev/erp
187 lines
5.3 KiB
JavaScript
187 lines
5.3 KiB
JavaScript
/**
|
||
* SSE客户端示例
|
||
*
|
||
* 由于浏览器原生的EventSource不支持自定义请求头,
|
||
* 这里提供了两种实现方式:
|
||
* 1. 使用fetch实现(推荐)
|
||
* 2. 使用EventSource polyfill(备选)
|
||
*/
|
||
|
||
/**
|
||
* 方式1:使用fetch实现SSE客户端(推荐)
|
||
*
|
||
* @param {string} url - SSE端点URL
|
||
* @param {string} token - JWT认证token
|
||
* @param {function} onMessage - 接收到消息时的回调函数
|
||
* @param {function} onError - 连接错误时的回调函数
|
||
* @param {function} onClose - 连接关闭时的回调函数
|
||
*/
|
||
function createSSEConnectionWithFetch(url, token, onMessage, onError, onClose) {
|
||
const controller = new AbortController();
|
||
const signal = controller.signal;
|
||
|
||
// 启动一个长时间运行的fetch请求
|
||
fetch(url, {
|
||
method: 'GET',
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Accept': 'text/event-stream',
|
||
'Cache-Control': 'no-cache',
|
||
},
|
||
signal: signal
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const reader = response.body.getReader();
|
||
const decoder = new TextDecoder();
|
||
let buffer = '';
|
||
|
||
function processBuffer() {
|
||
const lines = buffer.split('\n');
|
||
buffer = lines.pop(); // 保留最后一个不完整的行
|
||
|
||
for (const line of lines) {
|
||
if (line.trim() === '') continue; // 空行表示事件结束
|
||
|
||
if (line.startsWith('data: ')) {
|
||
const data = line.substring(6); // 去掉 'data: ' 前缀
|
||
try {
|
||
const event = JSON.parse(data);
|
||
if (onMessage) onMessage(event);
|
||
} catch (e) {
|
||
console.error('Error parsing SSE data:', e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function read() {
|
||
return reader.read().then(({ done, value }) => {
|
||
if (done) {
|
||
if (onClose) onClose();
|
||
return;
|
||
}
|
||
|
||
buffer += decoder.decode(value, { stream: true });
|
||
processBuffer();
|
||
|
||
// 继续读取
|
||
return read();
|
||
});
|
||
}
|
||
|
||
return read();
|
||
})
|
||
.catch(error => {
|
||
if (onError) onError(error);
|
||
});
|
||
|
||
// 返回一个对象,包含关闭连接的方法
|
||
return {
|
||
close: () => controller.abort()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 方式2:使用EventSource Polyfill实现
|
||
*
|
||
* 需要先安装EventSource polyfill:
|
||
* npm install event-source-polyfill
|
||
*
|
||
* 在应用入口处导入:
|
||
* import 'event-source-polyfill';
|
||
*/
|
||
function createSSEConnectionWithPolyfill(url, token, onMessage, onError, onClose) {
|
||
// 创建带有认证的URL
|
||
const urlWithAuth = `${url}?token=${encodeURIComponent(token)}`;
|
||
|
||
const eventSource = new EventSource(urlWithAuth);
|
||
|
||
eventSource.onmessage = function(event) {
|
||
try {
|
||
const data = JSON.parse(event.data);
|
||
if (onMessage) onMessage(data);
|
||
} catch (e) {
|
||
console.error('Error parsing SSE data:', e);
|
||
}
|
||
};
|
||
|
||
eventSource.onerror = function(error) {
|
||
if (onError) onError(error);
|
||
};
|
||
|
||
eventSource.onclose = function() {
|
||
if (onClose) onClose();
|
||
};
|
||
|
||
return {
|
||
close: () => eventSource.close()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 使用示例
|
||
*/
|
||
const JWT_TOKEN = 'your_jwt_token_here';
|
||
const SSE_URL = '/sse/';
|
||
|
||
// 使用fetch方式(推荐)
|
||
const sseConnection = createSSEConnectionWithFetch(
|
||
SSE_URL,
|
||
JWT_TOKEN,
|
||
(event) => {
|
||
console.log('收到SSE事件:', event);
|
||
|
||
// 根据事件类型处理不同业务逻辑
|
||
switch (event.type) {
|
||
case 'connected':
|
||
console.log(`SSE连接成功,商户ID: ${event.merchant_id}`);
|
||
break;
|
||
|
||
case 'order_paid':
|
||
console.log(`订单已支付: ${event.object_id}`);
|
||
// 刷新订单列表或显示通知
|
||
break;
|
||
|
||
case 'stock_change_record':
|
||
console.log(`库存变动记录: ${event.object_id}`);
|
||
// 刷新库存数据
|
||
break;
|
||
|
||
case 'server_shutdown':
|
||
console.log('服务器即将关闭连接');
|
||
// 可以提示用户重新连接
|
||
break;
|
||
}
|
||
},
|
||
(error) => {
|
||
console.error('SSE连接错误:', error);
|
||
// 可以在这里实现重连逻辑
|
||
setTimeout(() => {
|
||
console.log('尝试重新连接...');
|
||
// 重新创建连接
|
||
}, 5000);
|
||
},
|
||
() => {
|
||
console.log('SSE连接已关闭');
|
||
}
|
||
);
|
||
|
||
// 当需要关闭连接时(例如用户退出登录)
|
||
// sseConnection.close();
|
||
|
||
/**
|
||
* 如果需要使用polyfill方式,确保在应用入口处导入polyfill
|
||
*/
|
||
// import 'event-source-polyfill';
|
||
//
|
||
// const sseConnection = createSSEConnectionWithPolyfill(
|
||
// SSE_URL,
|
||
// JWT_TOKEN,
|
||
// (event) => { /* 处理事件 */ },
|
||
// (error) => { /* 处理错误 */ },
|
||
// () => { /* 处理关闭 */ }
|
||
// );
|