diff --git a/docs/DARK_MODE_TEST.md b/docs/DARK_MODE_TEST.md index 965b0a2c..97a73720 100644 --- a/docs/DARK_MODE_TEST.md +++ b/docs/DARK_MODE_TEST.md @@ -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', }); ``` diff --git a/docs/MESSAGE_PUSH_INTEGRATION_TEST.md b/docs/MESSAGE_PUSH_INTEGRATION_TEST.md index a368b484..6d1e7510 100644 --- a/docs/MESSAGE_PUSH_INTEGRATION_TEST.md +++ b/docs/MESSAGE_PUSH_INTEGRATION_TEST.md @@ -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 连接。 + ### 数据流 ``` diff --git a/docs/NOTIFICATION_SYSTEM.md b/docs/NOTIFICATION_SYSTEM.md index b042e458..0fc20c6a 100644 --- a/docs/NOTIFICATION_SYSTEM.md +++ b/docs/NOTIFICATION_SYSTEM.md @@ -1,8 +1,10 @@ # 实时消息推送系统 - 完整技术文档 > **版本**: v2.11.0 -> **更新日期**: 2025-01-07 +> **更新日期**: 2025-01-10 > **文档类型**: 快速入门 + 完整技术规格 +> +> ⚠️ **重要更新**: Mock Socket 已移除(2025-01-10),文档中关于 `mockSocketService` 的内容仅供历史参考。 --- diff --git a/src/services/mockSocketService.js b/src/services/mockSocketService.js deleted file mode 100644 index 9e69bc94..00000000 --- a/src/services/mockSocketService.js +++ /dev/null @@ -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; diff --git a/src/services/socket/index.js b/src/services/socket/index.js index 8e7d49d3..cee61978 100644 --- a/src/services/socket/index.js +++ b/src/services/socket/index.js @@ -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;