refactor(socket): 移除 Mock Socket 服务并简化导出逻辑
- 删除 mockSocketService.js(916 行) - 简化 socket/index.js(365 行 → 19 行) - 移除 Mock/Real 服务选择逻辑 - 移除 SOCKET_TYPE 和 useMock 标识 - 移除全局调试 API(迁移到 src/devtools/) - 更新相关文档说明 Mock Socket 已移除 现在仅使用真实 Socket.IO 服务连接后端,代码更简洁清晰。 🔧 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -48,16 +48,18 @@ npm start
|
||||
|
||||
### 3. 触发通知
|
||||
|
||||
**Mock 模式**(默认):
|
||||
- 等待 60 秒,会自动推送 1-2 条通知
|
||||
- 或在控制台执行:
|
||||
**测试通知**:
|
||||
- 使用调试 API 发送测试通知:
|
||||
```javascript
|
||||
import { mockSocketService } from './services/mockSocketService.js';
|
||||
mockSocketService.sendTestNotification();
|
||||
```
|
||||
// 方式1: 使用调试工具(推荐)
|
||||
window.__DEBUG__.notification.forceNotification({
|
||||
title: '测试通知',
|
||||
body: '验证暗色模式下的通知样式'
|
||||
});
|
||||
|
||||
**Real 模式**:
|
||||
- 创建测试事件(运行后端测试脚本)
|
||||
// 方式2: 等待后端真实推送
|
||||
// 确保已连接后端,等待真实事件推送
|
||||
```
|
||||
|
||||
### 4. 验证效果
|
||||
|
||||
@@ -139,61 +141,46 @@ npm start
|
||||
|
||||
### 手动触发各类型通知
|
||||
|
||||
```javascript
|
||||
// 引入服务
|
||||
import { mockSocketService } from './services/mockSocketService.js';
|
||||
import { NOTIFICATION_TYPES, PRIORITY_LEVELS } from './constants/notificationTypes.js';
|
||||
> **注意**: Mock Socket 已移除,请使用调试工具或真实后端测试。
|
||||
|
||||
// 测试公告通知(蓝色)
|
||||
mockSocketService.sendTestNotification({
|
||||
type: NOTIFICATION_TYPES.ANNOUNCEMENT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
```javascript
|
||||
// 使用调试工具测试不同类型的通知
|
||||
// 确保已开启调试模式:REACT_APP_ENABLE_DEBUG=true
|
||||
|
||||
// 测试公告通知
|
||||
window.__DEBUG__.notification.forceNotification({
|
||||
title: '测试公告通知',
|
||||
content: '这是暗色模式下的蓝色通知',
|
||||
timestamp: Date.now(),
|
||||
body: '这是暗色模式下的蓝色通知',
|
||||
tag: 'test_announcement',
|
||||
autoClose: 0,
|
||||
});
|
||||
|
||||
// 测试股票上涨(红色)
|
||||
mockSocketService.sendTestNotification({
|
||||
type: NOTIFICATION_TYPES.STOCK_ALERT,
|
||||
priority: PRIORITY_LEVELS.URGENT,
|
||||
title: '测试股票上涨',
|
||||
content: '宁德时代 +5.2%',
|
||||
extra: { priceChange: '+5.2%' },
|
||||
timestamp: Date.now(),
|
||||
autoClose: 0,
|
||||
window.__DEBUG__.notification.forceNotification({
|
||||
title: '🔴 测试股票上涨',
|
||||
body: '宁德时代 +5.2%',
|
||||
tag: 'test_stock_up',
|
||||
});
|
||||
|
||||
// 测试股票下跌(绿色)
|
||||
mockSocketService.sendTestNotification({
|
||||
type: NOTIFICATION_TYPES.STOCK_ALERT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: '测试股票下跌',
|
||||
content: '比亚迪 -3.8%',
|
||||
extra: { priceChange: '-3.8%' },
|
||||
timestamp: Date.now(),
|
||||
autoClose: 0,
|
||||
window.__DEBUG__.notification.forceNotification({
|
||||
title: '🟢 测试股票下跌',
|
||||
body: '比亚迪 -3.8%',
|
||||
tag: 'test_stock_down',
|
||||
});
|
||||
|
||||
// 测试事件动向(橙色)
|
||||
mockSocketService.sendTestNotification({
|
||||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: '测试事件动向',
|
||||
content: '央行宣布降准',
|
||||
timestamp: Date.now(),
|
||||
autoClose: 0,
|
||||
window.__DEBUG__.notification.forceNotification({
|
||||
title: '🟠 测试事件动向',
|
||||
body: '央行宣布降准',
|
||||
tag: 'test_event',
|
||||
});
|
||||
|
||||
// 测试分析报告(紫色)
|
||||
mockSocketService.sendTestNotification({
|
||||
type: NOTIFICATION_TYPES.ANALYSIS_REPORT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '测试分析报告',
|
||||
content: '医药行业深度报告',
|
||||
timestamp: Date.now(),
|
||||
autoClose: 0,
|
||||
window.__DEBUG__.notification.forceNotification({
|
||||
title: '🟣 测试分析报告',
|
||||
body: '医药行业深度报告',
|
||||
tag: 'test_report',
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -330,13 +330,14 @@ if (Notification.permission === 'granted') {
|
||||
|
||||
### 关键文件
|
||||
|
||||
- `src/services/mockSocketService.js` - Mock Socket 服务
|
||||
- `src/services/socketService.js` - 真实 Socket.IO 服务
|
||||
- `src/services/socket/index.js` - 统一导出
|
||||
- `src/contexts/NotificationContext.js` - 通知上下文(含适配器)
|
||||
- `src/services/socketService.js` - Socket.IO 服务
|
||||
- `src/services/socket/index.js` - Socket 服务导出
|
||||
- `src/contexts/NotificationContext.js` - 通知上下文
|
||||
- `src/hooks/useEventNotifications.js` - React Hook
|
||||
- `src/views/Community/components/EventList.js` - 事件列表集成
|
||||
|
||||
> **注意**: `mockSocketService.js` 已移除(2025-01-10),现仅使用真实 Socket 连接。
|
||||
|
||||
### 数据流
|
||||
|
||||
```
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# 实时消息推送系统 - 完整技术文档
|
||||
|
||||
> **版本**: v2.11.0
|
||||
> **更新日期**: 2025-01-07
|
||||
> **更新日期**: 2025-01-10
|
||||
> **文档类型**: 快速入门 + 完整技术规格
|
||||
>
|
||||
> ⚠️ **重要更新**: Mock Socket 已移除(2025-01-10),文档中关于 `mockSocketService` 的内容仅供历史参考。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,916 +0,0 @@
|
||||
// src/services/mockSocketService.js
|
||||
/**
|
||||
* Mock Socket 服务 - 用于开发环境模拟实时推送
|
||||
* 模拟金融资讯、事件动向、分析报告等实时消息推送
|
||||
*/
|
||||
|
||||
import { logger } from '../utils/logger';
|
||||
import { NOTIFICATION_TYPES, PRIORITY_LEVELS } from '../constants/notificationTypes';
|
||||
|
||||
// 模拟金融资讯数据
|
||||
const mockFinancialNews = [
|
||||
// ========== 公告通知 ==========
|
||||
{
|
||||
type: NOTIFICATION_TYPES.ANNOUNCEMENT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: '贵州茅台发布2024年度财报公告',
|
||||
content: '2024年度营收同比增长15.2%,净利润创历史新高,董事会建议每10股派息180元',
|
||||
publishTime: new Date('2024-03-28T15:30:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/event-detail/ann001',
|
||||
extra: {
|
||||
announcementType: '财报',
|
||||
companyCode: '600519',
|
||||
companyName: '贵州茅台',
|
||||
},
|
||||
autoClose: 10000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.ANNOUNCEMENT,
|
||||
priority: PRIORITY_LEVELS.URGENT,
|
||||
title: '宁德时代发布重大资产重组公告',
|
||||
content: '公司拟收购某新能源材料公司100%股权,交易金额约120亿元,预计增厚业绩20%',
|
||||
publishTime: new Date('2024-03-28T09:00:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/event-detail/ann002',
|
||||
extra: {
|
||||
announcementType: '重组',
|
||||
companyCode: '300750',
|
||||
companyName: '宁德时代',
|
||||
},
|
||||
autoClose: 12000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.ANNOUNCEMENT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '中国平安发布分红派息公告',
|
||||
content: '2023年度利润分配方案:每10股派发现金红利23.0元(含税),分红率达30.5%',
|
||||
publishTime: new Date('2024-03-27T16:00:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/event-detail/ann003',
|
||||
extra: {
|
||||
announcementType: '分红',
|
||||
companyCode: '601318',
|
||||
companyName: '中国平安',
|
||||
},
|
||||
autoClose: 10000,
|
||||
},
|
||||
|
||||
// ========== 股票动向 ==========
|
||||
{
|
||||
type: NOTIFICATION_TYPES.STOCK_ALERT,
|
||||
priority: PRIORITY_LEVELS.URGENT,
|
||||
title: '您关注的股票触发预警',
|
||||
content: '宁德时代(300750) 当前价格 ¥245.50,盘中涨幅达 +5.2%,已触达您设置的目标价位',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/stock-overview?code=300750',
|
||||
extra: {
|
||||
stockCode: '300750',
|
||||
stockName: '宁德时代',
|
||||
priceChange: '+5.2%',
|
||||
currentPrice: '245.50',
|
||||
triggerType: '目标价',
|
||||
},
|
||||
autoClose: 10000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.STOCK_ALERT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: '您关注的股票异常波动',
|
||||
content: '比亚迪(002594) 5分钟内跌幅达 -3.8%,当前价格 ¥198.20,建议关注',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/stock-overview?code=002594',
|
||||
extra: {
|
||||
stockCode: '002594',
|
||||
stockName: '比亚迪',
|
||||
priceChange: '-3.8%',
|
||||
currentPrice: '198.20',
|
||||
triggerType: '异常波动',
|
||||
},
|
||||
autoClose: 10000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.STOCK_ALERT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '持仓股票表现',
|
||||
content: '隆基绿能(601012) 今日表现优异,涨幅 +4.5%,您当前持仓浮盈 +¥8,200',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/trading-simulation',
|
||||
extra: {
|
||||
stockCode: '601012',
|
||||
stockName: '隆基绿能',
|
||||
priceChange: '+4.5%',
|
||||
profit: '+8200',
|
||||
},
|
||||
autoClose: 8000,
|
||||
},
|
||||
|
||||
// ========== 事件动向 ==========
|
||||
{
|
||||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: '央行宣布降准0.5个百分点',
|
||||
content: '中国人民银行宣布下调金融机构存款准备金率0.5个百分点,释放长期资金约1万亿元,利好股市',
|
||||
publishTime: new Date('2024-03-28T09:00:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/event-detail/evt001',
|
||||
extra: {
|
||||
eventId: 'evt001',
|
||||
relatedStocks: 12,
|
||||
impactLevel: '重大利好',
|
||||
sectors: ['银行', '地产', '基建'],
|
||||
},
|
||||
autoClose: 12000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: '新能源汽车补贴政策延期',
|
||||
content: '财政部宣布新能源汽车购置补贴政策延长至2024年底,涉及比亚迪、理想汽车等5家龙头企业',
|
||||
publishTime: new Date('2024-03-28T10:30:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/event-detail/evt002',
|
||||
extra: {
|
||||
eventId: 'evt002',
|
||||
relatedStocks: 5,
|
||||
impactLevel: '重大利好',
|
||||
sectors: ['新能源汽车'],
|
||||
},
|
||||
autoClose: 12000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '芯片产业扶持政策出台',
|
||||
content: '工信部发布《半导体产业发展指导意见》,未来三年投入500亿专项资金支持芯片研发',
|
||||
publishTime: new Date('2024-03-27T14:00:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/event-detail/evt003',
|
||||
extra: {
|
||||
eventId: 'evt003',
|
||||
relatedStocks: 8,
|
||||
impactLevel: '中长期利好',
|
||||
sectors: ['半导体', '芯片设计'],
|
||||
},
|
||||
autoClose: 10000,
|
||||
},
|
||||
|
||||
// ========== 预测通知 ==========
|
||||
{
|
||||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '【预测】央行可能宣布降准政策',
|
||||
content: '基于最新宏观数据分析,预计央行将在本周宣布降准0.5个百分点,释放长期资金',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: true,
|
||||
clickable: false, // ❌ 不可点击
|
||||
link: null,
|
||||
extra: {
|
||||
isPrediction: true,
|
||||
statusHint: '详细报告生成中...',
|
||||
relatedPredictionId: 'pred_001',
|
||||
},
|
||||
autoClose: 15000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '【预测】新能源补贴政策或将延期',
|
||||
content: '根据政策趋势分析,财政部可能宣布新能源汽车购置补贴政策延长至2025年底',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
isAIGenerated: true,
|
||||
clickable: false, // ❌ 不可点击
|
||||
link: null,
|
||||
extra: {
|
||||
isPrediction: true,
|
||||
statusHint: '详细报告生成中...',
|
||||
relatedPredictionId: 'pred_002',
|
||||
},
|
||||
autoClose: 15000,
|
||||
},
|
||||
|
||||
// ========== 分析报告 ==========
|
||||
{
|
||||
type: NOTIFICATION_TYPES.ANALYSIS_REPORT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: '医药行业深度报告:创新药迎来政策拐点',
|
||||
content: 'CXO板块持续受益于全球创新药研发外包需求,建议关注药明康德、凯莱英等龙头企业',
|
||||
publishTime: new Date('2024-03-28T08:00:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
author: {
|
||||
name: '李明',
|
||||
organization: '中信证券',
|
||||
},
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/forecast-report?id=rpt001',
|
||||
extra: {
|
||||
reportType: '行业研报',
|
||||
industry: '医药',
|
||||
rating: '强烈推荐',
|
||||
},
|
||||
autoClose: 12000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.ANALYSIS_REPORT,
|
||||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||||
title: 'AI产业链投资机会分析',
|
||||
content: '随着大模型应用加速落地,算力、数据、应用三大方向均存在投资机会,重点关注海光信息、寒武纪',
|
||||
publishTime: new Date('2024-03-28T07:30:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
author: {
|
||||
name: '王芳',
|
||||
organization: '招商证券',
|
||||
},
|
||||
isAIGenerated: true,
|
||||
clickable: true,
|
||||
link: '/forecast-report?id=rpt002',
|
||||
extra: {
|
||||
reportType: '策略报告',
|
||||
industry: '人工智能',
|
||||
rating: '推荐',
|
||||
},
|
||||
autoClose: 12000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.ANALYSIS_REPORT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '比亚迪:新能源汽车龙头业绩持续超预期',
|
||||
content: '2024年销量目标400万辆,海外市场拓展顺利,维持"买入"评级,目标价280元',
|
||||
publishTime: new Date('2024-03-27T09:00:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
author: {
|
||||
name: '张伟',
|
||||
organization: '国泰君安',
|
||||
},
|
||||
isAIGenerated: false,
|
||||
clickable: true,
|
||||
link: '/forecast-report?id=rpt003',
|
||||
extra: {
|
||||
reportType: '公司研报',
|
||||
industry: '新能源汽车',
|
||||
rating: '买入',
|
||||
targetPrice: '280',
|
||||
},
|
||||
autoClose: 10000,
|
||||
},
|
||||
{
|
||||
type: NOTIFICATION_TYPES.ANALYSIS_REPORT,
|
||||
priority: PRIORITY_LEVELS.NORMAL,
|
||||
title: '2024年A股市场展望:结构性行情延续',
|
||||
content: 'AI应用、高端制造、自主可控三大主线贯穿全年,建议关注科技成长板块配置机会',
|
||||
publishTime: new Date('2024-03-26T16:00:00').getTime(),
|
||||
pushTime: Date.now(),
|
||||
author: {
|
||||
name: 'AI分析师',
|
||||
organization: '价值前沿',
|
||||
},
|
||||
isAIGenerated: true,
|
||||
clickable: true,
|
||||
link: '/forecast-report?id=rpt004',
|
||||
extra: {
|
||||
reportType: '策略报告',
|
||||
industry: '市场策略',
|
||||
rating: '谨慎乐观',
|
||||
},
|
||||
autoClose: 10000,
|
||||
},
|
||||
];
|
||||
|
||||
class MockSocketService {
|
||||
constructor() {
|
||||
this.connected = false;
|
||||
this.connecting = false; // 新增:正在连接标志,防止重复连接
|
||||
this.listeners = new Map();
|
||||
this.intervals = [];
|
||||
this.messageQueue = [];
|
||||
this.reconnectAttempts = 0;
|
||||
this.customReconnectTimer = null;
|
||||
this.failConnection = false; // 是否模拟连接失败
|
||||
this.pushPaused = false; // 新增:暂停推送标志(保持连接)
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算指数退避延迟(Mock 模式使用更短的时间便于测试)
|
||||
* 第1次: 10秒, 第2次: 20秒, 第3次: 40秒, 第4次及以后: 40秒
|
||||
*/
|
||||
getReconnectionDelay(attempt) {
|
||||
const delays = [10000, 20000, 40000]; // 10s, 20s, 40s (缩短10倍便于测试)
|
||||
const index = Math.min(attempt - 1, delays.length - 1);
|
||||
return delays[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接到 mock socket
|
||||
*/
|
||||
connect() {
|
||||
// ✅ 防止重复连接
|
||||
if (this.connected) {
|
||||
logger.warn('mockSocketService', 'Already connected');
|
||||
console.log('%c[Mock Socket] Already connected, skipping', 'color: #FF9800; font-weight: bold;');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.connecting) {
|
||||
logger.warn('mockSocketService', 'Connection in progress');
|
||||
console.log('%c[Mock Socket] Connection already in progress, skipping', 'color: #FF9800; font-weight: bold;');
|
||||
return;
|
||||
}
|
||||
|
||||
this.connecting = true; // 标记为连接中
|
||||
logger.info('mockSocketService', 'Connecting to mock socket service...');
|
||||
console.log('%c[Mock Socket] 🔌 Connecting...', 'color: #2196F3; font-weight: bold;');
|
||||
|
||||
// 模拟连接延迟
|
||||
setTimeout(() => {
|
||||
// 检查是否应该模拟连接失败
|
||||
if (this.failConnection) {
|
||||
this.connecting = false; // 清除连接中标志
|
||||
logger.warn('mockSocketService', 'Simulated connection failure');
|
||||
console.log('%c[Mock Socket] ❌ Connection failed (simulated)', 'color: #F44336; font-weight: bold;');
|
||||
|
||||
// 触发连接错误事件
|
||||
this.emit('connect_error', {
|
||||
message: 'Mock connection error for testing',
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// 安排下次重连(会继续失败,直到 failConnection 被清除)
|
||||
this.scheduleReconnection();
|
||||
return;
|
||||
}
|
||||
|
||||
// 正常连接成功
|
||||
this.connected = true;
|
||||
this.connecting = false; // 清除连接中标志
|
||||
this.reconnectAttempts = 0;
|
||||
|
||||
// 清除自定义重连定时器
|
||||
if (this.customReconnectTimer) {
|
||||
clearTimeout(this.customReconnectTimer);
|
||||
this.customReconnectTimer = null;
|
||||
}
|
||||
|
||||
logger.info('mockSocketService', 'Mock socket connected successfully');
|
||||
console.log('%c[Mock Socket] ✅ Connected successfully!', 'color: #4CAF50; font-weight: bold; font-size: 14px;');
|
||||
console.log(`%c[Mock Socket] Status: connected=${this.connected}, connecting=${this.connecting}`, 'color: #4CAF50;');
|
||||
|
||||
// ✅ 使用 setTimeout(0) 确保监听器已注册后再触发事件
|
||||
setTimeout(() => {
|
||||
console.log('%c[Mock Socket] Emitting connect event...', 'color: #9C27B0;');
|
||||
this.emit('connect', { timestamp: Date.now() });
|
||||
console.log('%c[Mock Socket] Connect event emitted', 'color: #9C27B0;');
|
||||
}, 0);
|
||||
|
||||
// 在连接后3秒发送欢迎消息
|
||||
setTimeout(() => {
|
||||
if (this.connected) {
|
||||
this.emit('new_event', {
|
||||
type: 'system_notification',
|
||||
severity: 'info',
|
||||
title: '连接成功',
|
||||
message: '实时消息推送服务已启动 (Mock 模式)',
|
||||
timestamp: Date.now(),
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
}, 3000);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
* @param {boolean} triggerReconnect - 是否触发自动重连(模拟意外断开)
|
||||
*/
|
||||
disconnect(triggerReconnect = false) {
|
||||
if (!this.connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('mockSocketService', 'Disconnecting from mock socket service...');
|
||||
|
||||
// 清除所有定时器
|
||||
this.intervals.forEach(interval => clearInterval(interval));
|
||||
this.intervals = [];
|
||||
this.pushPaused = false; // 重置暂停状态
|
||||
|
||||
const wasConnected = this.connected;
|
||||
this.connected = false;
|
||||
this.emit('disconnect', {
|
||||
timestamp: Date.now(),
|
||||
reason: triggerReconnect ? 'transport close' : 'io client disconnect'
|
||||
});
|
||||
|
||||
// 如果需要触发重连(模拟意外断开)
|
||||
if (triggerReconnect && wasConnected) {
|
||||
this.scheduleReconnection();
|
||||
} else {
|
||||
// 清除重连定时器
|
||||
if (this.customReconnectTimer) {
|
||||
clearTimeout(this.customReconnectTimer);
|
||||
this.customReconnectTimer = null;
|
||||
}
|
||||
this.reconnectAttempts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指数退避策略安排重连
|
||||
*/
|
||||
scheduleReconnection() {
|
||||
// 清除之前的定时器
|
||||
if (this.customReconnectTimer) {
|
||||
clearTimeout(this.customReconnectTimer);
|
||||
}
|
||||
|
||||
this.reconnectAttempts++;
|
||||
const delay = this.getReconnectionDelay(this.reconnectAttempts);
|
||||
logger.info('mockSocketService', `Scheduling reconnection in ${delay / 1000}s (attempt ${this.reconnectAttempts})`);
|
||||
|
||||
// 触发 connect_error 事件通知UI
|
||||
this.emit('connect_error', {
|
||||
message: 'Mock connection error for testing',
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
this.customReconnectTimer = setTimeout(() => {
|
||||
if (!this.connected) {
|
||||
logger.info('mockSocketService', 'Attempting reconnection...', {
|
||||
attempt: this.reconnectAttempts,
|
||||
});
|
||||
this.connect();
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动重连
|
||||
* @returns {boolean} 是否触发重连
|
||||
*/
|
||||
reconnect() {
|
||||
if (this.connected) {
|
||||
logger.info('mockSocketService', 'Already connected, no need to reconnect');
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info('mockSocketService', 'Manually triggering reconnection...');
|
||||
|
||||
// 清除自动重连定时器
|
||||
if (this.customReconnectTimer) {
|
||||
clearTimeout(this.customReconnectTimer);
|
||||
this.customReconnectTimer = null;
|
||||
}
|
||||
|
||||
// 重置重连计数
|
||||
this.reconnectAttempts = 0;
|
||||
|
||||
// 立即触发重连
|
||||
this.connect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟意外断线(测试用)
|
||||
* @param {number} duration - 断线持续时间(毫秒),0表示需要手动重连
|
||||
*/
|
||||
simulateDisconnection(duration = 0) {
|
||||
logger.info('mockSocketService', `Simulating disconnection${duration > 0 ? ` for ${duration}ms` : ' (manual reconnect required)'}...`);
|
||||
|
||||
if (duration > 0) {
|
||||
// 短暂断线,自动重连
|
||||
this.disconnect(true);
|
||||
} else {
|
||||
// 需要手动重连
|
||||
this.disconnect(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟持续连接失败(测试用)
|
||||
* 连接会一直失败,直到调用 allowReconnection()
|
||||
*/
|
||||
simulateConnectionFailure() {
|
||||
logger.info('mockSocketService', '🚫 Simulating persistent connection failure...');
|
||||
logger.info('mockSocketService', 'Connection will keep failing until allowReconnection() is called');
|
||||
|
||||
// 设置失败标志
|
||||
this.failConnection = true;
|
||||
|
||||
// 如果当前已连接,先断开并触发重连(会失败)
|
||||
if (this.connected) {
|
||||
this.disconnect(true);
|
||||
} else {
|
||||
// 如果未连接,直接触发一次连接尝试(会失败)
|
||||
this.connect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 允许重连成功(测试用)
|
||||
* 清除连接失败标志,下次重连将会成功
|
||||
*/
|
||||
allowReconnection() {
|
||||
logger.info('mockSocketService', '✅ Allowing reconnection to succeed...');
|
||||
logger.info('mockSocketService', 'Next reconnection attempt will succeed');
|
||||
|
||||
// 清除失败标志
|
||||
this.failConnection = false;
|
||||
|
||||
// 不立即重连,等待自动重连或手动重连
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param {string} event - 事件名称
|
||||
* @param {Function} callback - 回调函数
|
||||
*/
|
||||
on(event, callback) {
|
||||
if (!this.listeners.has(event)) {
|
||||
this.listeners.set(event, []);
|
||||
}
|
||||
this.listeners.get(event).push(callback);
|
||||
|
||||
logger.info('mockSocketService', `Event listener added: ${event}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听
|
||||
* @param {string} event - 事件名称
|
||||
* @param {Function} callback - 回调函数
|
||||
*/
|
||||
off(event, callback) {
|
||||
if (!this.listeners.has(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callbacks = this.listeners.get(event);
|
||||
const index = callbacks.indexOf(callback);
|
||||
|
||||
if (index !== -1) {
|
||||
callbacks.splice(index, 1);
|
||||
logger.info('mockSocketService', `Event listener removed: ${event}`);
|
||||
}
|
||||
|
||||
// 如果没有监听器了,删除该事件
|
||||
if (callbacks.length === 0) {
|
||||
this.listeners.delete(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
* @param {string} event - 事件名称
|
||||
* @param {*} data - 事件数据
|
||||
*/
|
||||
emit(event, data) {
|
||||
if (!this.listeners.has(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callbacks = this.listeners.get(event);
|
||||
callbacks.forEach(callback => {
|
||||
try {
|
||||
callback(data);
|
||||
} catch (error) {
|
||||
logger.error('mockSocketService', 'emit', error, { event, data });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动模拟消息推送
|
||||
* @param {number} interval - 推送间隔(毫秒)
|
||||
* @param {number} burstCount - 每次推送的消息数量(1-3条)
|
||||
*/
|
||||
startMockPush(interval = 15000, burstCount = 1) {
|
||||
if (!this.connected) {
|
||||
logger.warn('mockSocketService', 'Cannot start mock push: not connected');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('mockSocketService', `Starting mock push: interval=${interval}ms, burst=${burstCount}`);
|
||||
|
||||
const pushInterval = setInterval(() => {
|
||||
// 检查是否暂停推送
|
||||
if (this.pushPaused) {
|
||||
logger.info('mockSocketService', '⏸️ Mock push is paused, skipping this cycle...');
|
||||
return;
|
||||
}
|
||||
|
||||
// 随机选择 1-burstCount 条消息
|
||||
const count = Math.floor(Math.random() * burstCount) + 1;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// 从模拟数据中随机选择一条
|
||||
const randomIndex = Math.floor(Math.random() * mockFinancialNews.length);
|
||||
const alert = {
|
||||
...mockFinancialNews[randomIndex],
|
||||
timestamp: Date.now(),
|
||||
id: `mock_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||
};
|
||||
|
||||
// 延迟发送(模拟层叠效果)
|
||||
setTimeout(() => {
|
||||
this.emit('new_event', alert);
|
||||
logger.info('mockSocketService', 'Mock notification sent', alert);
|
||||
}, i * 500); // 每条消息间隔500ms
|
||||
}
|
||||
}, interval);
|
||||
|
||||
this.intervals.push(pushInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止模拟推送
|
||||
*/
|
||||
stopMockPush() {
|
||||
this.intervals.forEach(interval => clearInterval(interval));
|
||||
this.intervals = [];
|
||||
this.pushPaused = false; // 重置暂停状态
|
||||
logger.info('mockSocketService', 'Mock push stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停自动推送(保持连接和定时器运行)
|
||||
*/
|
||||
pausePush() {
|
||||
this.pushPaused = true;
|
||||
logger.info('mockSocketService', '⏸️ Mock push paused (connection and intervals maintained)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复自动推送
|
||||
*/
|
||||
resumePush() {
|
||||
this.pushPaused = false;
|
||||
logger.info('mockSocketService', '▶️ Mock push resumed');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询推送暂停状态
|
||||
* @returns {boolean} 是否已暂停
|
||||
*/
|
||||
isPushPaused() {
|
||||
return this.pushPaused;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发一条测试消息
|
||||
* @param {object} customData - 自定义消息数据(可选)
|
||||
*/
|
||||
sendTestNotification(customData = null) {
|
||||
// 如果传入自定义数据,直接使用(向后兼容)
|
||||
if (customData) {
|
||||
this.emit('new_event', customData);
|
||||
logger.info('mockSocketService', 'Custom test notification sent', customData);
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认发送新格式的测试通知(符合当前通知系统规范)
|
||||
const notification = {
|
||||
type: 'announcement', // 公告通知类型
|
||||
priority: 'important', // 重要优先级(30秒自动关闭)
|
||||
title: '🧪 测试通知',
|
||||
content: '这是一条手动触发的测试消息,用于验证通知系统是否正常工作',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
id: `test_${Date.now()}`,
|
||||
clickable: false,
|
||||
};
|
||||
|
||||
this.emit('new_event', notification);
|
||||
logger.info('mockSocketService', 'Test notification sent', notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接状态
|
||||
*/
|
||||
isConnected() {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前重连尝试次数
|
||||
*/
|
||||
getReconnectAttempts() {
|
||||
return this.reconnectAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最大重连次数(Mock 模式无限重试)
|
||||
*/
|
||||
getMaxReconnectAttempts() {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅事件推送(Mock 实现)
|
||||
* @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;
|
||||
|
||||
logger.info('mockSocketService', 'Subscribing to events', { eventType, importance });
|
||||
|
||||
// Mock: 立即触发订阅成功回调
|
||||
if (onSubscribed) {
|
||||
setTimeout(() => {
|
||||
onSubscribed({
|
||||
success: true,
|
||||
event_type: eventType,
|
||||
importance: importance,
|
||||
message: 'Mock subscription confirmed'
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Mock: 如果提供了 onNewEvent 回调,监听 'new_event' 事件
|
||||
if (onNewEvent) {
|
||||
// 先移除之前的监听器(避免重复)
|
||||
this.off('new_event', onNewEvent);
|
||||
// 添加新的监听器
|
||||
this.on('new_event', onNewEvent);
|
||||
logger.info('mockSocketService', 'Event listener registered for new_event');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅事件推送(Mock 实现)
|
||||
* @param {object} options - 取消订阅选项
|
||||
* @param {string} options.eventType - 事件类型
|
||||
* @param {Function} options.onUnsubscribed - 取消订阅成功的回调函数(可选)
|
||||
*/
|
||||
unsubscribeFromEvents(options = {}) {
|
||||
const {
|
||||
eventType = 'all',
|
||||
onUnsubscribed,
|
||||
} = options;
|
||||
|
||||
logger.info('mockSocketService', 'Unsubscribing from events', { eventType });
|
||||
|
||||
// Mock: 移除 new_event 监听器
|
||||
this.off('new_event');
|
||||
|
||||
// Mock: 立即触发取消订阅成功回调
|
||||
if (onUnsubscribed) {
|
||||
setTimeout(() => {
|
||||
onUnsubscribed({
|
||||
success: true,
|
||||
event_type: eventType,
|
||||
message: 'Mock unsubscription confirmed'
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷方法:订阅所有类型的事件(Mock 实现)
|
||||
* @param {Function} onNewEvent - 收到新事件时的回调函数
|
||||
*/
|
||||
subscribeToAllEvents(onNewEvent) {
|
||||
this.subscribeToEvents({
|
||||
eventType: 'all',
|
||||
importance: 'all',
|
||||
onNewEvent,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷方法:订阅指定重要性的事件(Mock 实现)
|
||||
* @param {string} importance - 重要性级别 ('S' | 'A' | 'B' | 'C')
|
||||
* @param {Function} onNewEvent - 收到新事件时的回调函数
|
||||
*/
|
||||
subscribeToImportantEvents(importance, onNewEvent) {
|
||||
this.subscribeToEvents({
|
||||
eventType: 'all',
|
||||
importance,
|
||||
onNewEvent,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷方法:订阅指定类型的事件(Mock 实现)
|
||||
* @param {string} eventType - 事件类型
|
||||
* @param {Function} onNewEvent - 收到新事件时的回调函数
|
||||
*/
|
||||
subscribeToEventType(eventType, onNewEvent) {
|
||||
this.subscribeToEvents({
|
||||
eventType,
|
||||
importance: 'all',
|
||||
onNewEvent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
export const mockSocketService = new MockSocketService();
|
||||
|
||||
// 开发模式下添加全局测试函数
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
window.__mockSocket = {
|
||||
// 模拟意外断线(自动重连成功)
|
||||
simulateDisconnection: () => {
|
||||
logger.info('mockSocketService', '🔌 Simulating disconnection (will auto-reconnect)...');
|
||||
mockSocketService.simulateDisconnection(1); // 触发自动重连
|
||||
},
|
||||
|
||||
// 模拟持续连接失败
|
||||
simulateConnectionFailure: () => {
|
||||
logger.info('mockSocketService', '🚫 Simulating connection failure (will keep retrying)...');
|
||||
mockSocketService.simulateConnectionFailure();
|
||||
},
|
||||
|
||||
// 允许重连成功
|
||||
allowReconnection: () => {
|
||||
logger.info('mockSocketService', '✅ Allowing next reconnection to succeed...');
|
||||
mockSocketService.allowReconnection();
|
||||
},
|
||||
|
||||
// 获取连接状态
|
||||
isConnected: () => {
|
||||
const connected = mockSocketService.isConnected();
|
||||
logger.info('mockSocketService', `Connection status: ${connected ? '✅ Connected' : '❌ Disconnected'}`);
|
||||
return connected;
|
||||
},
|
||||
|
||||
// 手动重连
|
||||
reconnect: () => {
|
||||
logger.info('mockSocketService', '🔄 Manually triggering reconnection...');
|
||||
return mockSocketService.reconnect();
|
||||
},
|
||||
|
||||
// 获取重连尝试次数
|
||||
getAttempts: () => {
|
||||
const attempts = mockSocketService.getReconnectAttempts();
|
||||
logger.info('mockSocketService', `Current reconnection attempts: ${attempts}`);
|
||||
return attempts;
|
||||
},
|
||||
|
||||
// 暂停自动推送(保持连接)
|
||||
pausePush: () => {
|
||||
mockSocketService.pausePush();
|
||||
logger.info('mockSocketService', '⏸️ Auto push paused');
|
||||
return true;
|
||||
},
|
||||
|
||||
// 恢复自动推送
|
||||
resumePush: () => {
|
||||
mockSocketService.resumePush();
|
||||
logger.info('mockSocketService', '▶️ Auto push resumed');
|
||||
return true;
|
||||
},
|
||||
|
||||
// 查看推送暂停状态
|
||||
isPushPaused: () => {
|
||||
const paused = mockSocketService.isPushPaused();
|
||||
logger.info('mockSocketService', `Push status: ${paused ? '⏸️ Paused' : '▶️ Active'}`);
|
||||
return paused;
|
||||
},
|
||||
};
|
||||
|
||||
logger.info('mockSocketService', '💡 Mock Socket test functions available:');
|
||||
logger.info('mockSocketService', ' __mockSocket.simulateDisconnection() - 模拟断线(自动重连成功)');
|
||||
logger.info('mockSocketService', ' __mockSocket.simulateConnectionFailure() - 模拟连接失败(持续失败)');
|
||||
logger.info('mockSocketService', ' __mockSocket.allowReconnection() - 允许重连成功');
|
||||
logger.info('mockSocketService', ' __mockSocket.isConnected() - 查看连接状态');
|
||||
logger.info('mockSocketService', ' __mockSocket.reconnect() - 手动重连');
|
||||
logger.info('mockSocketService', ' __mockSocket.getAttempts() - 查看重连次数');
|
||||
logger.info('mockSocketService', ' __mockSocket.pausePush() - ⏸️ 暂停自动推送(保持连接)');
|
||||
logger.info('mockSocketService', ' __mockSocket.resumePush() - ▶️ 恢复自动推送');
|
||||
logger.info('mockSocketService', ' __mockSocket.isPushPaused() - 查看推送状态');
|
||||
}
|
||||
|
||||
export default mockSocketService;
|
||||
@@ -1,364 +1,19 @@
|
||||
// src/services/socket/index.js
|
||||
/**
|
||||
* Socket 服务统一导出
|
||||
* 根据环境变量自动选择使用 Mock 或真实 Socket.IO 服务
|
||||
* 使用真实 Socket.IO 服务连接后端
|
||||
*/
|
||||
|
||||
import { mockSocketService } from '../mockSocketService';
|
||||
import { socketService } from '../socketService';
|
||||
|
||||
// 判断是否使用 Mock
|
||||
const useMock = process.env.REACT_APP_ENABLE_MOCK === 'true' || process.env.REACT_APP_USE_MOCK_SOCKET === 'true';
|
||||
|
||||
// 根据环境选择服务
|
||||
export const socket = useMock ? mockSocketService : socketService;
|
||||
|
||||
// 同时导出两个服务,方便测试和调试
|
||||
export { mockSocketService, socketService };
|
||||
|
||||
// 导出服务类型标识
|
||||
export const SOCKET_TYPE = useMock ? 'MOCK' : 'REAL';
|
||||
// 导出 socket 服务
|
||||
export const socket = socketService;
|
||||
export { socketService };
|
||||
|
||||
// 打印当前使用的服务类型
|
||||
console.log(
|
||||
`%c[Socket Service] Using ${SOCKET_TYPE} Socket Service`,
|
||||
`color: ${useMock ? '#FF9800' : '#4CAF50'}; font-weight: bold; font-size: 12px;`
|
||||
'%c[Socket Service] Using REAL Socket Service',
|
||||
'color: #4CAF50; font-weight: bold; font-size: 12px;'
|
||||
);
|
||||
|
||||
// ========== 暴露调试 API 到全局 ==========
|
||||
if (typeof window !== 'undefined') {
|
||||
// 暴露 Socket 类型到全局
|
||||
window.SOCKET_TYPE = SOCKET_TYPE;
|
||||
|
||||
// 暴露调试 API
|
||||
window.__SOCKET_DEBUG__ = {
|
||||
// 获取当前连接状态
|
||||
getStatus: () => {
|
||||
const isConnected = socket.connected || false;
|
||||
return {
|
||||
type: SOCKET_TYPE,
|
||||
connected: isConnected,
|
||||
reconnectAttempts: socket.getReconnectAttempts?.() || 0,
|
||||
maxReconnectAttempts: socket.getMaxReconnectAttempts?.() || Infinity,
|
||||
service: useMock ? 'mockSocketService' : 'socketService',
|
||||
};
|
||||
},
|
||||
|
||||
// 手动重连
|
||||
reconnect: () => {
|
||||
console.log('[Socket Debug] Manual reconnect triggered');
|
||||
if (socket.reconnect) {
|
||||
socket.reconnect();
|
||||
} else {
|
||||
socket.disconnect();
|
||||
socket.connect();
|
||||
}
|
||||
},
|
||||
|
||||
// 断开连接
|
||||
disconnect: () => {
|
||||
console.log('[Socket Debug] Manual disconnect triggered');
|
||||
socket.disconnect();
|
||||
},
|
||||
|
||||
// 连接
|
||||
connect: () => {
|
||||
console.log('[Socket Debug] Manual connect triggered');
|
||||
socket.connect();
|
||||
},
|
||||
|
||||
// 获取服务实例 (仅用于调试)
|
||||
getService: () => socket,
|
||||
|
||||
// 导出诊断信息
|
||||
exportDiagnostics: () => {
|
||||
const status = window.__SOCKET_DEBUG__.getStatus();
|
||||
const diagnostics = {
|
||||
...status,
|
||||
timestamp: new Date().toISOString(),
|
||||
userAgent: navigator.userAgent,
|
||||
url: window.location.href,
|
||||
env: {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
REACT_APP_ENABLE_MOCK: process.env.REACT_APP_ENABLE_MOCK,
|
||||
REACT_APP_USE_MOCK_SOCKET: process.env.REACT_APP_USE_MOCK_SOCKET,
|
||||
REACT_APP_API_URL: process.env.REACT_APP_API_URL,
|
||||
REACT_APP_ENV: process.env.REACT_APP_ENV,
|
||||
},
|
||||
};
|
||||
console.log('[Socket Diagnostics]', diagnostics);
|
||||
return diagnostics;
|
||||
},
|
||||
|
||||
// 手动订阅事件
|
||||
subscribe: (options = {}) => {
|
||||
const { eventType = 'all', importance = 'all' } = options;
|
||||
console.log(`[Socket Debug] Subscribing to events: type=${eventType}, importance=${importance}`);
|
||||
|
||||
if (socket.subscribeToEvents) {
|
||||
socket.subscribeToEvents({
|
||||
eventType,
|
||||
importance,
|
||||
onNewEvent: (event) => {
|
||||
console.log('[Socket Debug] ✅ New event received:', event);
|
||||
},
|
||||
onSubscribed: (data) => {
|
||||
console.log('[Socket Debug] ✅ Subscription confirmed:', data);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
console.error('[Socket Debug] ❌ subscribeToEvents method not available');
|
||||
}
|
||||
},
|
||||
|
||||
// 测试连接质量
|
||||
testConnection: () => {
|
||||
console.log('[Socket Debug] Testing connection...');
|
||||
const start = Date.now();
|
||||
|
||||
if (socket.emit) {
|
||||
socket.emit('ping', { timestamp: start }, (response) => {
|
||||
const latency = Date.now() - start;
|
||||
console.log(`[Socket Debug] ✅ Connection OK - Latency: ${latency}ms`, response);
|
||||
});
|
||||
} else {
|
||||
console.error('[Socket Debug] ❌ Cannot test connection - socket.emit not available');
|
||||
}
|
||||
},
|
||||
|
||||
// 检查配置是否正确
|
||||
checkConfig: () => {
|
||||
const config = {
|
||||
socketType: SOCKET_TYPE,
|
||||
useMock,
|
||||
envVars: {
|
||||
REACT_APP_ENABLE_MOCK: process.env.REACT_APP_ENABLE_MOCK,
|
||||
REACT_APP_USE_MOCK_SOCKET: process.env.REACT_APP_USE_MOCK_SOCKET,
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
REACT_APP_API_URL: process.env.REACT_APP_API_URL,
|
||||
},
|
||||
socketMethods: {
|
||||
connect: typeof socket.connect,
|
||||
disconnect: typeof socket.disconnect,
|
||||
on: typeof socket.on,
|
||||
emit: typeof socket.emit,
|
||||
subscribeToEvents: typeof socket.subscribeToEvents,
|
||||
},
|
||||
};
|
||||
|
||||
console.log('[Socket Debug] Configuration Check:', config);
|
||||
|
||||
// 检查潜在问题
|
||||
const issues = [];
|
||||
if (SOCKET_TYPE === 'MOCK' && process.env.NODE_ENV === 'production') {
|
||||
issues.push('⚠️ WARNING: Using MOCK socket in production!');
|
||||
}
|
||||
if (!socket.subscribeToEvents) {
|
||||
issues.push('❌ ERROR: subscribeToEvents method missing');
|
||||
}
|
||||
|
||||
if (issues.length > 0) {
|
||||
console.warn('[Socket Debug] Issues found:', issues);
|
||||
} else {
|
||||
console.log('[Socket Debug] ✅ No issues found');
|
||||
}
|
||||
|
||||
return { config, issues };
|
||||
},
|
||||
};
|
||||
|
||||
console.log(
|
||||
'%c[Socket Debug] Debug API available at window.__SOCKET_DEBUG__',
|
||||
'color: #2196F3; font-weight: bold;'
|
||||
);
|
||||
console.log(
|
||||
'%cTry: window.__SOCKET_DEBUG__.getStatus()',
|
||||
'color: #2196F3;'
|
||||
);
|
||||
console.log(
|
||||
'%c window.__SOCKET_DEBUG__.checkConfig() - 检查配置',
|
||||
'color: #2196F3;'
|
||||
);
|
||||
console.log(
|
||||
'%c window.__SOCKET_DEBUG__.subscribe() - 手动订阅事件',
|
||||
'color: #2196F3;'
|
||||
);
|
||||
console.log(
|
||||
'%c window.__SOCKET_DEBUG__.testConnection() - 测试连接',
|
||||
'color: #2196F3;'
|
||||
);
|
||||
|
||||
// ========== 通知系统专用调试 API ==========
|
||||
window.__NOTIFY_DEBUG__ = {
|
||||
// 完整检查(配置+连接+订阅状态)
|
||||
checkAll: () => {
|
||||
console.log('\n==========【通知系统诊断】==========');
|
||||
|
||||
// 1. 检查 Socket 配置
|
||||
const socketCheck = window.__SOCKET_DEBUG__.checkConfig();
|
||||
console.log('\n✓ Socket 配置检查完成');
|
||||
|
||||
// 2. 检查连接状态
|
||||
const status = window.__SOCKET_DEBUG__.getStatus();
|
||||
console.log('\n✓ 连接状态:', status.connected ? '✅ 已连接' : '❌ 未连接');
|
||||
|
||||
// 3. 检查环境变量
|
||||
console.log('\n✓ API Base:', process.env.REACT_APP_API_URL || '(使用相对路径)');
|
||||
|
||||
// 4. 检查浏览器通知权限
|
||||
const browserPermission = Notification?.permission || 'unsupported';
|
||||
console.log('\n✓ 浏览器通知权限:', browserPermission);
|
||||
|
||||
// 5. 汇总报告
|
||||
const report = {
|
||||
timestamp: new Date().toISOString(),
|
||||
socket: {
|
||||
type: SOCKET_TYPE,
|
||||
connected: status.connected,
|
||||
reconnectAttempts: status.reconnectAttempts,
|
||||
},
|
||||
env: socketCheck.config.envVars,
|
||||
browserNotification: browserPermission,
|
||||
issues: socketCheck.issues,
|
||||
};
|
||||
|
||||
console.log('\n========== 诊断报告 ==========');
|
||||
console.table(report);
|
||||
|
||||
if (report.issues.length > 0) {
|
||||
console.warn('\n⚠️ 发现问题:', report.issues);
|
||||
} else {
|
||||
console.log('\n✅ 系统正常,未发现问题');
|
||||
}
|
||||
|
||||
// 提供修复建议
|
||||
if (!status.connected) {
|
||||
console.log('\n💡 修复建议:');
|
||||
console.log(' 1. 检查网络连接');
|
||||
console.log(' 2. 尝试手动重连: __SOCKET_DEBUG__.reconnect()');
|
||||
console.log(' 3. 检查后端服务是否运行');
|
||||
}
|
||||
|
||||
if (browserPermission === 'denied') {
|
||||
console.log('\n💡 浏览器通知已被拒绝,请在浏览器设置中允许通知权限');
|
||||
}
|
||||
|
||||
console.log('\n====================================\n');
|
||||
|
||||
return report;
|
||||
},
|
||||
|
||||
// 手动订阅事件(简化版)
|
||||
subscribe: (eventType = 'all', importance = 'all') => {
|
||||
console.log(`\n[通知调试] 手动订阅事件: type=${eventType}, importance=${importance}`);
|
||||
window.__SOCKET_DEBUG__.subscribe({ eventType, importance });
|
||||
},
|
||||
|
||||
// 模拟接收通知(用于测试UI)
|
||||
testNotify: (type = 'announcement') => {
|
||||
console.log('\n[通知调试] 模拟通知:', type);
|
||||
|
||||
const mockNotifications = {
|
||||
announcement: {
|
||||
id: `test_${Date.now()}`,
|
||||
type: 'announcement',
|
||||
priority: 'important',
|
||||
title: '🧪 测试公告通知',
|
||||
content: '这是一条测试消息,用于验证通知系统是否正常工作',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
},
|
||||
stock_alert: {
|
||||
id: `test_${Date.now()}`,
|
||||
type: 'stock_alert',
|
||||
priority: 'urgent',
|
||||
title: '🧪 测试股票预警',
|
||||
content: '贵州茅台触发价格预警: 1850.00元 (+5.2%)',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
},
|
||||
event_alert: {
|
||||
id: `test_${Date.now()}`,
|
||||
type: 'event_alert',
|
||||
priority: 'important',
|
||||
title: '🧪 测试事件动向',
|
||||
content: 'AI大模型新政策发布,影响科技板块',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
},
|
||||
analysis_report: {
|
||||
id: `test_${Date.now()}`,
|
||||
type: 'analysis_report',
|
||||
priority: 'normal',
|
||||
title: '🧪 测试分析报告',
|
||||
content: '2024年Q1市场策略报告已发布',
|
||||
publishTime: Date.now(),
|
||||
pushTime: Date.now(),
|
||||
},
|
||||
};
|
||||
|
||||
const notification = mockNotifications[type] || mockNotifications.announcement;
|
||||
|
||||
// 触发 new_event 事件
|
||||
if (socket.emit) {
|
||||
// 对于真实 Socket,模拟服务端推送(实际上客户端无法这样做,仅用于Mock模式)
|
||||
console.warn('⚠️ 真实 Socket 无法模拟服务端推送,请使用 Mock 模式或等待真实推送');
|
||||
}
|
||||
|
||||
// 直接触发事件监听器(如果是 Mock 模式)
|
||||
if (SOCKET_TYPE === 'MOCK' && socket.emit) {
|
||||
socket.emit('new_event', notification);
|
||||
console.log('✅ 已触发 Mock 通知事件');
|
||||
}
|
||||
|
||||
console.log('通知数据:', notification);
|
||||
return notification;
|
||||
},
|
||||
|
||||
// 导出完整诊断报告
|
||||
exportReport: () => {
|
||||
const report = window.__NOTIFY_DEBUG__.checkAll();
|
||||
|
||||
// 生成可下载的 JSON
|
||||
const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `notification-debug-${Date.now()}.json`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
console.log('✅ 诊断报告已导出');
|
||||
return report;
|
||||
},
|
||||
|
||||
// 快捷帮助
|
||||
help: () => {
|
||||
console.log('\n========== 通知系统调试 API ==========');
|
||||
console.log('window.__NOTIFY_DEBUG__.checkAll() - 完整诊断检查');
|
||||
console.log('window.__NOTIFY_DEBUG__.subscribe() - 手动订阅事件');
|
||||
console.log('window.__NOTIFY_DEBUG__.testNotify(type) - 模拟通知 (announcement/stock_alert/event_alert/analysis_report)');
|
||||
console.log('window.__NOTIFY_DEBUG__.exportReport() - 导出诊断报告');
|
||||
console.log('\n========== Socket 调试 API ==========');
|
||||
console.log('window.__SOCKET_DEBUG__.getStatus() - 获取连接状态');
|
||||
console.log('window.__SOCKET_DEBUG__.checkConfig() - 检查配置');
|
||||
console.log('window.__SOCKET_DEBUG__.reconnect() - 手动重连');
|
||||
console.log('====================================\n');
|
||||
},
|
||||
};
|
||||
|
||||
console.log(
|
||||
'%c[Notify Debug] Notification Debug API available at window.__NOTIFY_DEBUG__',
|
||||
'color: #FF9800; font-weight: bold;'
|
||||
);
|
||||
console.log(
|
||||
'%cTry: window.__NOTIFY_DEBUG__.checkAll() - 完整诊断',
|
||||
'color: #FF9800;'
|
||||
);
|
||||
console.log(
|
||||
'%c window.__NOTIFY_DEBUG__.help() - 查看所有命令',
|
||||
'color: #FF9800;'
|
||||
);
|
||||
}
|
||||
|
||||
export default socket;
|
||||
|
||||
Reference in New Issue
Block a user