1
0
forked from erp-dev/erp

feat: big version, added tasks for backup_database and stock change, added health check api, approve sse (support channel via merchant)

This commit is contained in:
2025-11-26 21:49:42 +08:00
parent 6bf0465d05
commit a9c75a13fa
26 changed files with 7158 additions and 216 deletions

187
sse/client_example.js Normal file
View File

@@ -0,0 +1,187 @@
/**
* 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) => { /* 处理错误 */ },
// () => { /* 处理关闭 */ }
// );