Merge branch 'feature' of https://git.valuefrontier.cn/vf/vf_react into feature
This commit is contained in:
161
src/hooks/useEventNotifications.js
Normal file
161
src/hooks/useEventNotifications.js
Normal file
@@ -0,0 +1,161 @@
|
||||
// src/hooks/useEventNotifications.js
|
||||
/**
|
||||
* React Hook:用于在组件中订阅事件推送通知
|
||||
*
|
||||
* 使用示例:
|
||||
* ```jsx
|
||||
* import { useEventNotifications } from 'hooks/useEventNotifications';
|
||||
*
|
||||
* function MyComponent() {
|
||||
* const { newEvent, isConnected } = useEventNotifications({
|
||||
* eventType: 'all',
|
||||
* importance: 'all',
|
||||
* onNewEvent: (event) => {
|
||||
* console.log('收到新事件:', event);
|
||||
* // 显示通知...
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* return <div>...</div>;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { socketService } from '../services/socketService';
|
||||
|
||||
export const useEventNotifications = (options = {}) => {
|
||||
const {
|
||||
eventType = 'all',
|
||||
importance = 'all',
|
||||
enabled = true,
|
||||
onNewEvent,
|
||||
} = options;
|
||||
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [newEvent, setNewEvent] = useState(null);
|
||||
const [error, setError] = useState(null);
|
||||
const unsubscribeRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
// 如果禁用,则不订阅
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 连接状态监听
|
||||
const handleConnect = () => {
|
||||
setIsConnected(true);
|
||||
setError(null);
|
||||
};
|
||||
|
||||
const handleDisconnect = () => {
|
||||
setIsConnected(false);
|
||||
};
|
||||
|
||||
const handleConnectError = (err) => {
|
||||
setError(err);
|
||||
setIsConnected(false);
|
||||
};
|
||||
|
||||
// 连接 WebSocket
|
||||
socketService.connect();
|
||||
|
||||
// 监听连接事件
|
||||
socketService.on('connect', handleConnect);
|
||||
socketService.on('disconnect', handleDisconnect);
|
||||
socketService.on('connect_error', handleConnectError);
|
||||
|
||||
// 新事件处理函数
|
||||
const handleNewEvent = (eventData) => {
|
||||
setNewEvent(eventData);
|
||||
|
||||
// 调用外部回调
|
||||
if (onNewEvent) {
|
||||
onNewEvent(eventData);
|
||||
}
|
||||
};
|
||||
|
||||
// 订阅事件推送
|
||||
socketService.subscribeToEvents({
|
||||
eventType,
|
||||
importance,
|
||||
onNewEvent: handleNewEvent,
|
||||
onSubscribed: (data) => {
|
||||
console.log('订阅成功:', data);
|
||||
},
|
||||
});
|
||||
|
||||
// 保存取消订阅函数
|
||||
unsubscribeRef.current = () => {
|
||||
socketService.unsubscribeFromEvents({ eventType });
|
||||
};
|
||||
|
||||
// 组件卸载时清理
|
||||
return () => {
|
||||
console.log('清理 WebSocket 订阅');
|
||||
|
||||
// 取消订阅
|
||||
if (unsubscribeRef.current) {
|
||||
unsubscribeRef.current();
|
||||
}
|
||||
|
||||
// 移除监听器
|
||||
socketService.off('connect', handleConnect);
|
||||
socketService.off('disconnect', handleDisconnect);
|
||||
socketService.off('connect_error', handleConnectError);
|
||||
|
||||
// 断开连接
|
||||
socketService.disconnect();
|
||||
};
|
||||
}, [eventType, importance, enabled, onNewEvent]);
|
||||
|
||||
return {
|
||||
newEvent, // 最新收到的事件
|
||||
isConnected, // WebSocket 连接状态
|
||||
error, // 错误信息
|
||||
clearNewEvent: () => setNewEvent(null), // 清除新事件状态
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 简化版 Hook:只订阅所有事件
|
||||
*/
|
||||
export const useAllEventNotifications = (onNewEvent) => {
|
||||
return useEventNotifications({
|
||||
eventType: 'all',
|
||||
importance: 'all',
|
||||
onNewEvent,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook:订阅重要事件(S 和 A 级)
|
||||
*/
|
||||
export const useImportantEventNotifications = (onNewEvent) => {
|
||||
const [importantEvents, setImportantEvents] = useState([]);
|
||||
|
||||
const handleEvent = (event) => {
|
||||
// 只处理 S 和 A 级事件
|
||||
if (event.importance === 'S' || event.importance === 'A') {
|
||||
setImportantEvents(prev => [event, ...prev].slice(0, 10)); // 最多保留 10 个
|
||||
if (onNewEvent) {
|
||||
onNewEvent(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const result = useEventNotifications({
|
||||
eventType: 'all',
|
||||
importance: 'all',
|
||||
onNewEvent: handleEvent,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
importantEvents,
|
||||
clearImportantEvents: () => setImportantEvents([]),
|
||||
};
|
||||
};
|
||||
|
||||
export default useEventNotifications;
|
||||
@@ -186,6 +186,169 @@ class SocketService {
|
||||
getSocketId() {
|
||||
return this.socket?.id || null;
|
||||
}
|
||||
|
||||
// ==================== 事件推送专用方法 ====================
|
||||
|
||||
/**
|
||||
* 订阅事件推送
|
||||
* @param {object} options - 订阅选项
|
||||
* @param {string} options.eventType - 事件类型 ('all' | 'policy' | 'market' | 'tech' | ...)
|
||||
* @param {string} options.importance - 重要性 ('all' | 'S' | 'A' | 'B' | 'C')
|
||||
* @param {Function} options.onNewEvent - 收到新事件时的回调函数
|
||||
* @param {Function} options.onSubscribed - 订阅成功的回调函数(可选)
|
||||
*/
|
||||
subscribeToEvents(options = {}) {
|
||||
const {
|
||||
eventType = 'all',
|
||||
importance = 'all',
|
||||
onNewEvent,
|
||||
onSubscribed,
|
||||
} = options;
|
||||
|
||||
if (!this.socket || !this.connected) {
|
||||
logger.warn('socketService', 'Cannot subscribe: socket not connected');
|
||||
// 自动连接
|
||||
this.connect();
|
||||
// 等待连接成功后再订阅
|
||||
this.socket.once('connect', () => {
|
||||
this._doSubscribe(eventType, importance, onNewEvent, onSubscribed);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._doSubscribe(eventType, importance, onNewEvent, onSubscribed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行订阅操作(内部方法)
|
||||
*/
|
||||
_doSubscribe(eventType, importance, onNewEvent, onSubscribed) {
|
||||
// 发送订阅请求
|
||||
this.emit('subscribe_events', {
|
||||
event_type: eventType,
|
||||
importance: importance,
|
||||
});
|
||||
|
||||
// 监听订阅确认
|
||||
this.socket.once('subscription_confirmed', (data) => {
|
||||
logger.info('socketService', 'Subscription confirmed', data);
|
||||
if (onSubscribed) {
|
||||
onSubscribed(data);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听订阅错误
|
||||
this.socket.once('subscription_error', (error) => {
|
||||
logger.error('socketService', 'Subscription error', error);
|
||||
});
|
||||
|
||||
// 监听新事件推送
|
||||
if (onNewEvent) {
|
||||
// 先移除之前的监听器(避免重复)
|
||||
this.socket.off('new_event');
|
||||
// 添加新的监听器
|
||||
this.socket.on('new_event', (eventData) => {
|
||||
logger.info('socketService', 'New event received', eventData);
|
||||
onNewEvent(eventData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅事件推送
|
||||
* @param {object} options - 取消订阅选项
|
||||
* @param {string} options.eventType - 事件类型
|
||||
* @param {Function} options.onUnsubscribed - 取消订阅成功的回调函数(可选)
|
||||
*/
|
||||
unsubscribeFromEvents(options = {}) {
|
||||
const {
|
||||
eventType = 'all',
|
||||
onUnsubscribed,
|
||||
} = options;
|
||||
|
||||
if (!this.socket || !this.connected) {
|
||||
logger.warn('socketService', 'Cannot unsubscribe: socket not connected');
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送取消订阅请求
|
||||
this.emit('unsubscribe_events', {
|
||||
event_type: eventType,
|
||||
});
|
||||
|
||||
// 监听取消订阅确认
|
||||
this.socket.once('unsubscription_confirmed', (data) => {
|
||||
logger.info('socketService', 'Unsubscription confirmed', data);
|
||||
|
||||
// 移除新事件监听器
|
||||
this.socket.off('new_event');
|
||||
|
||||
if (onUnsubscribed) {
|
||||
onUnsubscribed(data);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听取消订阅错误
|
||||
this.socket.once('unsubscription_error', (error) => {
|
||||
logger.error('socketService', 'Unsubscription error', error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷方法:订阅所有类型的事件
|
||||
* @param {Function} onNewEvent - 收到新事件时的回调函数
|
||||
* @returns {Function} 取消订阅的函数
|
||||
*/
|
||||
subscribeToAllEvents(onNewEvent) {
|
||||
this.subscribeToEvents({
|
||||
eventType: 'all',
|
||||
importance: 'all',
|
||||
onNewEvent,
|
||||
});
|
||||
|
||||
// 返回取消订阅的清理函数
|
||||
return () => {
|
||||
this.unsubscribeFromEvents({ eventType: 'all' });
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷方法:订阅特定重要性的事件
|
||||
* @param {string} importance - 重要性级别 ('S' | 'A' | 'B' | 'C')
|
||||
* @param {Function} onNewEvent - 收到新事件时的回调函数
|
||||
* @returns {Function} 取消订阅的函数
|
||||
*/
|
||||
subscribeToImportantEvents(importance, onNewEvent) {
|
||||
this.subscribeToEvents({
|
||||
eventType: 'all',
|
||||
importance,
|
||||
onNewEvent,
|
||||
});
|
||||
|
||||
// 返回取消订阅的清理函数
|
||||
return () => {
|
||||
this.unsubscribeFromEvents({ eventType: 'all' });
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷方法:订阅特定类型的事件
|
||||
* @param {string} eventType - 事件类型
|
||||
* @param {Function} onNewEvent - 收到新事件时的回调函数
|
||||
* @returns {Function} 取消订阅的函数
|
||||
*/
|
||||
subscribeToEventType(eventType, onNewEvent) {
|
||||
this.subscribeToEvents({
|
||||
eventType,
|
||||
importance: 'all',
|
||||
onNewEvent,
|
||||
});
|
||||
|
||||
// 返回取消订阅的清理函数
|
||||
return () => {
|
||||
this.unsubscribeFromEvents({ eventType });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
|
||||
Reference in New Issue
Block a user