# 实时消息推送系统 - 完整技术文档 > **版本**: v2.12.0 > **更新日期**: 2025-11-17 > **文档类型**: 用户指南 + 完整技术规格 > > ⚠️ **重要更新**: > - 测试工具已移除(2025-11-17):NotificationTestTool、window.__TEST_NOTIFICATION__、notificationDebugger > - 保留生产可用调试工具:window.__DEBUG__、window.browserNotificationService --- ## 📑 目录 ### 👤 第零部分:用户快速指南 0. [连接状态查看](#-连接状态查看) 1. [手动操作指南](#-手动操作指南) 2. [常见问题解决](#-常见问题解决) 3. [可用调试命令](#-可用调试命令) ### 第一部分:快速入门(开发者) 1. [系统简介](#-系统概述) 2. [核心特性](#核心特性) 3. [5分钟快速开始](#-快速开始) 4. [基础使用示例](#-代码使用) ### 第二部分:系统架构 5. [整体架构设计](#-系统架构设计) 6. [技术栈选型](#-技术栈) 7. [数据流向](#数据流向) 8. [双重通知策略](#双重通知策略) ### 第三部分:核心组件详解 9. [组件清单](#-核心组件清单) 10. [NotificationContext](#1-notificationcontext---通知上下文) 11. [NotificationContainer](#2-notificationcontainer---通知容器ui) 12. [SocketService](#3-socketservice---websocket服务) 13. [其他核心组件](#其他核心组件) ### 第四部分:设计流程 14. [消息推送完整流程](#-消息推送完整流程) 15. [初始化流程](#1-初始化流程) 16. [消息接收与适配](#2-消息接收与适配) 17. [通知分发策略](#3-通知分发策略) 18. [性能监控埋点](#4-性能监控埋点) ### 第五部分:测试指南 19. [测试快速指南](#-测试快速指南) 20. [手动测试清单](#-手动测试清单) 21. [自动化测试](#-自动化测试用例) 22. [测试覆盖率](#测试覆盖率目标) ### 第六部分:配置与扩展 23. [通知类型配置](#-通知类型配置) 24. [优先级配置](#-优先级配置) 25. [环境配置](#环境配置) 26. [后端集成](#-后端集成生产环境) ### 第七部分:故障排查 27. [常见问题FAQ](#-故障排除) 28. [调试工具](#调试工具) 29. [日志分析](#日志分析) ### 第八部分:最佳实践 30. [开发规范](#-开发规范) 31. [性能优化](#-性能优化建议) 32. [安全注意事项](#安全注意事项) ### 附录 33. [更新日志](#-更新日志) 34. [文件结构](#-文件结构) --- ## 🆕 最新更新 (v2.11.0 - Socket 连接优化) ### 优化内容 #### 1. **指数退避策略** 🔄 - ✅ **智能重连间隔**:从"激进"改为"渐进"策略 - Real Socket: 第1次 1分钟 → 第2次 2分钟 → 第3次+ 4分钟 - Mock Socket: 第1次 10秒 → 第2次 20秒 → 第3次+ 40秒(缩短10倍便于测试) - ✅ **无限重试**:不再在5次后停止,持续使用指数退避重连 - ✅ **自定义退避逻辑**:禁用 Socket.IO 默认重连机制,实现更可控的重连策略 #### 2. **连接状态横幅优化** 📍 - ✅ **降低侵入性**:zIndex 从 10000 → 1050,高度 py={3} → py={2},半透明背景(opacity 0.95) - ✅ **手动关闭**:所有状态(DISCONNECTED/RECONNECTING/FAILED)都可手动关闭 - ✅ **状态持久化**:用户关闭后保存到 localStorage,重连成功后自动清除 - ✅ **自动消失**:重连成功显示"✓ 已重新连接" 2秒后自动消失 #### 3. **Mock 模式测试功能** 🧪 - ✅ **断线重连模拟**:`__mockSocket.simulateDisconnection()` - 模拟意外断线,自动重连 - ✅ **连接状态查询**:`__mockSocket.isConnected()` - 查看当前连接状态 - ✅ **手动重连**:`__mockSocket.reconnect()` - 立即触发重连 - ✅ **控制台提示**:开发模式启动时自动显示可用测试函数 --- ## 👤 用户快速指南 > **适用人群**: 普通用户、测试人员、客服人员 > **阅读时间**: 5 分钟 ### 🔌 连接状态查看 #### **方法 1: 页面顶部状态横幅** 当系统出现连接问题时,页面顶部会显示状态横幅: | 状态 | 显示内容 | 颜色 | 说明 | |------|----------|------|------| | ✅ 已连接 | 无横幅 | - | 正常工作,无需操作 | | ⚠️ 断开连接 | "⚠️ 连接已断开" | 橙色 | 正在尝试重连 | | 🔄 重连中 | "🔄 正在重连... (第 X 次)" | 蓝色 | 自动重连进行中 | | ❌ 连接失败 | "❌ 连接失败,请检查网络" | 红色 | 需要手动干预 | | ✓ 已重连 | "✓ 已重新连接" | 绿色 | 重连成功,2秒后自动消失 | **操作**: - 横幅可手动关闭(点击 ✕ 按钮) - 关闭后会记住状态,重连成功后自动清除 #### **方法 2: 浏览器控制台查看** 1. 按 `F12` 打开开发者工具 2. 切换到 `Console` 标签 3. 输入以下命令: ```javascript // 查看连接状态 __DEBUG__.socket.getStatus() // 输出示例: { connected: true, socketId: "abc123...", reconnectAttempts: 0 } ``` --- ### 🔧 手动操作指南 #### **1. 手动重连** 当连接失败时,可以手动触发重连: **浏览器控制台** (`F12` → `Console`): ```javascript // 手动重连 __DEBUG__.socket.reconnect() ``` **刷新页面**(最简单): - 按 `Ctrl+R` (Windows) 或 `Cmd+R` (Mac) - 或点击浏览器刷新按钮 #### **2. 查看连接日志** ```javascript // 查看最近的 Socket 日志 __DEBUG__.socket.getLogs() // 导出所有日志到文件 __DEBUG__.exportAll() ``` #### **3. 检查浏览器通知权限** ```javascript // 查看通知权限状态 window.browserNotificationService.getPermissionStatus() // 返回值: // "granted" - 已授权 // "denied" - 已拒绝 // "default" - 未询问 ``` **如何授予权限**: 1. 点击浏览器地址栏左侧的🔒图标 2. 找到"通知"设置 3. 选择"允许" --- ### 🆘 常见问题解决 #### **问题 1: 收不到通知** **可能原因**: - ❌ 浏览器通知权限未授予 - ❌ Socket 连接断开 - ❌ 浏览器标签页处于后台(网页通知需要标签页在前台) **解决步骤**: 1. 检查连接状态(参见上面"连接状态查看") 2. 检查通知权限: ```javascript window.browserNotificationService.getPermissionStatus() ``` 3. 如果是 `"denied"`,需要在浏览器设置中允许通知 4. 如果是 `"default"`,刷新页面会自动请求权限 #### **问题 2: 连接一直断开** **排查步骤**: 1. **检查网络连接**:打开其他网站,确认网络正常 2. **查看重连次数**: ```javascript __DEBUG__.socket.getStatus() ``` 3. **查看错误日志**: ```javascript __DEBUG__.socket.getLogs() ``` 4. **手动重连**: ```javascript __DEBUG__.socket.reconnect() ``` 5. **刷新页面**:`Ctrl+R` 或 `Cmd+R` #### **问题 3: 通知太多/太少** **无法控制通知数量**(由后端推送决定),但可以: - **清空所有通知**: 点击通知容器右上角"清空全部"按钮 - **关闭单个通知**: 点击通知右上角 ✕ 按钮 - **关闭音效**: 点击通知容器右上角🔊图标 #### **问题 4: 页面卡顿** **可能原因**: 通知历史过多 **解决方法**: 1. 点击"清空全部"按钮清空历史通知 2. 系统会自动限制最多显示 15 条通知 3. 如仍卡顿,刷新页面 --- ### 💻 可用调试命令 > **使用方法**: 打开浏览器控制台 (`F12` → `Console`),输入以下命令 #### **Socket 连接调试** ```javascript // 1. 查看连接状态 __DEBUG__.socket.getStatus() // 2. 手动重连 __DEBUG__.socket.reconnect() // 3. 查看 Socket 日志 __DEBUG__.socket.getLogs() // 4. 导出 Socket 日志 __DEBUG__.socket.exportLogs() ``` #### **通知权限调试** ```javascript // 1. 查看权限状态 window.browserNotificationService.getPermissionStatus() // 返回: "granted" | "denied" | "default" // 2. 请求权限 window.browserNotificationService.requestPermission() // 3. 检查是否支持通知 window.browserNotificationService.isSupported() // 返回: true | false ``` #### **综合调试** ```javascript // 1. 系统诊断(检查所有状态) __DEBUG__.diagnose() // 2. 查看所有统计信息 __DEBUG__.printStats() // 3. 导出所有日志 __DEBUG__.exportAll() // 4. 清空所有日志 __DEBUG__.clearAll() // 5. 显示帮助信息 __DEBUG__.help() ``` #### **开发环境专用(Mock 模式)** 仅在开发环境(`npm start`)可用: ```javascript // 模拟断线(测试重连机制) __mockSocket.simulateDisconnection() // 查看连接状态 __mockSocket.isConnected() // 手动重连 __mockSocket.reconnect() // 发送测试通知 __mockSocket.sendTestNotification() ``` --- ## 📋 系统概述 本系统是专为**金融资讯场景**设计的实时消息推送系统,支持 Mock 模式(开发)和真实 Socket.IO 模式(生产)。消息以右下角层叠弹窗的形式显示,**新消息在最上方**,符合主流桌面应用的交互习惯。 ### 核心特性 - **双通知系统**:网页通知 + 浏览器原生通知,智能分发 - **4 种通知类型**:公告通知、股票动向、事件动向、分析报告 - **3 级优先级**:紧急(不关闭)、重要(30秒)、普通(15秒) - **智能折叠**:最多显示3条,超过显示展开按钮 - **智能配色**:股票动向根据涨跌自动变色(涨红 🔴 / 跌绿 🟢) - **丰富元数据**:发布时间、作者、AI 标识 - **点击跳转**:可配置点击跳转链接 - **队列管理**:最多保留 15 条历史,可展开查看 - **性能监控**:自动追踪到达率、点击率、响应时间 - **历史记录**:持久化存储、筛选、搜索、导出功能 ### 技术亮点 - **WebSocket 实时通信**:Socket.IO + 指数退避重连策略 - **双重去重机制**:Socket层去重 + 显示层去重 - **智能权限引导**:渐进式权限请求,避免打扰用户 - **无障碍支持**:完整的 ARIA 属性,键盘导航 - **性能优化**:React.memo、useMemo、useCallback,GPU 加速动画 --- ## 🏗️ 系统架构设计 ### 整体架构图 ```mermaid graph TB subgraph "前端应用" A[App.js] --> B[AppProviders] B --> C[NotificationProvider
NotificationContext] C --> D[NotificationContainer
通知容器UI] end subgraph "通知系统核心" C --> F[Socket Service
WebSocket连接] F --> G{环境判断} G -->|Mock| H[mockSocketService
模拟服务] G -->|Real| I[socketService
生产服务] C --> J[browserNotificationService
浏览器通知] C --> K[notificationHistoryService
历史记录] C --> L[notificationMetricsService
性能监控] end subgraph "后端服务" I --> M[Flask Backend
Socket.IO Server] M --> N[(ClickHouse
事件数据)] M --> O[(MySQL
用户数据)] end subgraph "数据存储" K --> P[(localStorage
历史记录)] L --> Q[(localStorage
性能指标)] end style C fill:#f9f,stroke:#333,stroke-width:3px style D fill:#bbf,stroke:#333,stroke-width:2px style F fill:#bfb,stroke:#333,stroke-width:2px ``` ### 技术栈选型 #### 前端技术栈 - **React 18.3.1**: 并发渲染、Suspense、自动批处理 - **Chakra UI 2.8.2**: 可访问性优先、主题定制、响应式设计 - **Socket.IO Client 4.7.4**: WebSocket实时通信、自动重连 - **React Router v6**: 客户端路由、编程式导航 - **Framer Motion**: 流畅动画、布局动画 - **React Icons**: Material Design 图标 #### 后端技术栈 - **Flask + Flask-SocketIO**: Python WebSocket服务器 - **ClickHouse**: 时序数据分析(事件数据、性能指标) - **MySQL/PostgreSQL**: 用户数据、订阅关系 - **Redis + Celery**: 消息队列、异步任务 ### 数据流向 ```mermaid sequenceDiagram participant B as 后端服务器 participant S as Socket Service participant C as NotificationContext participant D as 通知分发器 participant W as 网页通知 participant N as 浏览器通知 participant H as History Service participant M as Metrics Service B->>S: 推送事件数据 S->>S: Socket层去重检查 S->>C: 触发 new_event C->>C: 适配后端格式→前端格式 C->>C: 显示层去重检查 C->>D: 分发通知 alt 页面在前台 D->>W: 显示网页通知 W->>H: 保存历史记录 W->>M: 记录性能指标 else 页面在后台 D->>N: 显示浏览器通知 N->>H: 保存历史记录 N->>M: 记录性能指标 end Note over W,N: 用户点击通知 W->>M: 追踪点击事件 W->>C: 导航到详情页 ``` ### 双重通知策略 系统根据**通知优先级**和**页面可见性**智能选择通知方式: | 优先级 | 页面在前台 | 页面在后台 | |--------|-----------|-----------| | **紧急 (Urgent)** | 网页通知 + 浏览器通知 | 网页通知 + 浏览器通知 | | **重要 (Important)** | 网页通知 | 浏览器通知 | | **普通 (Normal)** | 网页通知 | 网页通知 | **设计理念**: - **紧急通知**:双重保障,确保用户不会错过 - **重要通知**:根据用户注意力分配渠道(前台看得到网页,后台需要系统通知) - **普通通知**:避免过度打扰,仅网页内显示 --- ## 🧩 核心组件清单 | 组件名 | 类型 | 职责 | 文件路径 | |-------|------|------|---------| | **NotificationContext** | React Context | 通知系统核心逻辑、状态管理 | `src/contexts/NotificationContext.js` | | **NotificationContainer** | React Component | 通知UI容器、动画、交互 | `src/components/NotificationContainer/index.js` | | **socketService** | Service Class | 真实WebSocket连接管理 | `src/services/socketService.js` | | **mockSocketService** | Service Class | Mock WebSocket模拟 | `src/services/mockSocketService.js` | | **browserNotificationService** | Service Class | 浏览器通知API封装 | `src/services/browserNotificationService.js` | | **notificationHistoryService** | Service Class | 历史记录持久化 | `src/services/notificationHistoryService.js` | | **notificationMetricsService** | Service Class | 性能监控埋点 | `src/services/notificationMetricsService.js` | | **notificationTypes** | Constants | 类型定义、配置常量 | `src/constants/notificationTypes.js` | | **useEventNotifications** | Custom Hook | 事件订阅Hook | `src/hooks/useEventNotifications.js` | --- ### 1. NotificationContext - 通知上下文 **职责**:通知系统的大脑,管理所有状态和业务逻辑。 **核心功能**: - WebSocket 连接管理(连接、断开、重连) - 通知队列管理(添加、移除、去重) - 权限管理(浏览器通知权限请求) - 通知分发(网页/浏览器通知智能选择) - 音效播放 - 性能监控埋点 **主要API**: ```javascript const { notifications, // 当前通知队列 isConnected, // Socket连接状态 soundEnabled, // 音效开关状态 browserPermission, // 浏览器通知权限状态 connectionStatus, // 详细连接状态 addNotification, // 添加通知 removeNotification, // 移除通知 clearAllNotifications, // 清空所有通知 toggleSound, // 切换音效 requestBrowserPermission, // 请求浏览器通知权限 trackNotificationClick, // 追踪通知点击 retryConnection, // 手动重连 showWelcomeGuide, // 显示欢迎引导 showCommunityGuide, // 显示社区功能引导 showFirstFollowGuide, // 显示首次关注引导 } = useNotification(); ``` **关键实现细节**: 1. **双重去重机制**: ```javascript // Socket层去重(避免重复接收) const processedEventIds = useRef(new Set()); // 显示层去重(避免重复显示) const isDuplicate = notifications.some(n => n.id === notificationId); ``` 2. **智能分发策略**: ```javascript const isPageHidden = document.hidden; if (isPageHidden) { sendBrowserNotification(notification); // 后台:浏览器通知 } else { addWebNotification(notification); // 前台:网页通知 } ``` 3. **渐进式权限引导**: ```javascript // 登录后显示欢迎引导 showWelcomeGuide(); // 首次关注事件时显示引导 showFirstFollowGuide(); ``` **源码位置**: `src/contexts/NotificationContext.js:1-829` --- ### 2. NotificationContainer - 通知容器UI **职责**:通知的可视化展示,处理动画、交互、无障碍。 **核心功能**: - 通知列表渲染(最多显示3条) - 折叠/展开功能 - 通知动画(Framer Motion) - 点击跳转 - 无障碍支持(ARIA、键盘导航) **主要组件**: ```jsx {/* 可见通知 */} {visibleNotifications.map(notification => ( ))} {/* 折叠按钮 */} {hasMore && ( )} ``` **关键实现细节**: 1. **优先级视觉表现**: ```javascript // 紧急通知:粗边框 + 深色背景 + 脉冲动画 borderWidth: priority === URGENT ? '6px' : '2px' animation: priority === URGENT ? pulseAnimation : undefined ``` 2. **严格可点击性判断**: ```javascript const isActuallyClickable = clickable && link; // 两者都必须为true cursor: isActuallyClickable ? 'pointer' : 'default' ``` 3. **性能优化**: ```javascript // React.memo 自定义比较函数 React.memo(NotificationItem, (prevProps, nextProps) => { return prevProps.notification.id === nextProps.notification.id && prevProps.isNewest === nextProps.isNewest; }); ``` **源码位置**: `src/components/NotificationContainer/index.js:1-757` --- ### 3. SocketService - WebSocket服务 **职责**:管理Socket.IO连接,处理断线重连,事件订阅。 **核心功能**: - Socket.IO 连接管理 - 指数退避重连策略 - 事件订阅/取消订阅 - 消息发送/接收 **重连策略**: ```javascript // 指数退避延迟 getReconnectionDelay(attempt) { const delays = [60000, 120000, 240000]; // 1min, 2min, 4min const index = Math.min(attempt - 1, delays.length - 1); return delays[index]; } // 无限重试 this.maxReconnectAttempts = Infinity; ``` **主要API**: ```javascript // 连接管理 socket.connect(); socket.disconnect(); socket.reconnect(); socket.isConnected(); // 事件监听 socket.on('event_name', callback); socket.off('event_name'); // 事件订阅(金融资讯专用) socket.subscribeToEvents({ eventType, importance, onNewEvent }); socket.unsubscribeFromEvents({ eventType }); ``` **源码位置**: `src/services/socketService.js:1-465` --- ### 其他核心组件 #### 4. browserNotificationService - 浏览器通知服务 **核心方法**: ```javascript // 检查支持性 browserNotificationService.isSupported(); // 获取权限状态 browserNotificationService.getPermissionStatus(); // 'granted' | 'denied' | 'default' // 请求权限 await browserNotificationService.requestPermission(); // 发送通知 const notification = browserNotificationService.sendNotification({ title: '通知标题', body: '通知内容', tag: 'unique_tag', requireInteraction: false, data: { link: '/detail' }, autoClose: 8000, }); // 设置点击处理 notification.onclick = () => { window.focus(); window.location.hash = link; notification.close(); }; ``` **源码位置**: `src/services/browserNotificationService.js:1-209` --- #### 5. notificationHistoryService - 历史记录服务 **核心功能**: - localStorage持久化(最多500条) - 筛选(类型、优先级、日期范围、阅读状态) - 搜索(关键词匹配) - 导出(JSON/CSV) - 统计分析 **主要API**: ```javascript // 保存通知 notificationHistoryService.saveNotification(notification); // 获取历史记录(带筛选) const { records, total, page, totalPages } = notificationHistoryService.getHistory({ type: 'event_alert', priority: 'urgent', readStatus: 'unread', startDate: Date.now() - 7 * 24 * 60 * 60 * 1000, page: 1, pageSize: 20, }); // 搜索 const results = notificationHistoryService.searchHistory('央行'); // 导出 notificationHistoryService.downloadJSON(); notificationHistoryService.downloadCSV(); // 统计 const stats = notificationHistoryService.getStats(); // { total, read, unread, clicked, clickRate, byType, byPriority } ``` **源码位置**: `src/services/notificationHistoryService.js:1-403` --- #### 6. notificationMetricsService - 性能监控服务 **追踪指标**: - 总发送数、总接收数 - 点击率、关闭率 - 平均响应时间(从接收到点击的时间) - 每小时推送分布 - 每日数据趋势(最近30天) **主要API**: ```javascript // 追踪事件 notificationMetricsService.trackReceived(notification); notificationMetricsService.trackClicked(notification); notificationMetricsService.trackDismissed(notification); // 获取统计 const summary = notificationMetricsService.getSummary(); // { // totalReceived, totalClicked, totalDismissed, // clickRate, avgResponseTime, deliveryRate // } const byType = notificationMetricsService.getByType(); // { announcement: { received, clicked, dismissed, clickRate }, ... } const dailyData = notificationMetricsService.getDailyData(7); // 最近7天 // [ { date: '2025-01-01', received, clicked, dismissed, clickRate }, ... ] // 导出 const json = notificationMetricsService.exportToJSON(); const csv = notificationMetricsService.exportToCSV(); ``` **源码位置**: `src/services/notificationMetricsService.js:1-451` --- ## 🔄 消息推送完整流程 ### 1. 初始化流程 ```mermaid graph LR A[App启动] --> B[AppProviders挂载] B --> C[NotificationProvider初始化] C --> D{环境检测} D -->|Mock| E[连接mockSocketService] D -->|Real| F[连接socketService] E --> G[注册事件监听器] F --> G G --> H[触发connect事件] H --> I[更新连接状态] I --> J[检查浏览器通知权限] J --> K[加载历史记录] K --> L[初始化完成] ``` **代码实现** (src/contexts/NotificationContext.js:573-712): ```javascript useEffect(() => { // 1. 注册事件监听器 socket.on('connect', handleConnect); socket.on('disconnect', handleDisconnect); socket.on('connect_error', handleConnectError); socket.on('new_event', handleNewEvent); // 2. 连接Socket socket.connect(); // 3. Mock模式启动自动推送 if (SOCKET_TYPE === 'MOCK') { socket.startMockPush(60000, 2); // 60秒推送1-2条 } // 4. 清理函数 return () => { socket.off('connect'); socket.off('disconnect'); socket.off('new_event'); socket.disconnect(); }; }, []); ``` --- ### 2. 消息接收与适配 ```mermaid graph TB A[后端推送事件] --> B{Socket层去重} B -->|已存在| C[忽略] B -->|新事件| D[触发 new_event] D --> E{数据格式检测} E -->|前端格式| F[直接使用] E -->|后端格式| G[格式适配] G --> H[映射重要性
S/A→urgent/important
B/C→normal] H --> I[构建通知对象] I --> J[生成跳转链接
/event-detail/id] J --> K[添加元数据] K --> L{显示层去重} L -->|已存在| M[忽略] L -->|新通知| N[添加到队列] ``` **后端事件格式** → **前端通知格式** 适配器: ```javascript const adaptEventToNotification = (event) => { // 检测格式 if (event.priority || event.type === NOTIFICATION_TYPES.ANNOUNCEMENT) { return event; // 已经是前端格式 } // 重要性映射 let priority = PRIORITY_LEVELS.NORMAL; if (event.importance === 'S') priority = PRIORITY_LEVELS.URGENT; else if (event.importance === 'A') priority = PRIORITY_LEVELS.IMPORTANT; // 构建通知对象 return { id: event.id, type: NOTIFICATION_TYPES.EVENT_ALERT, priority: priority, title: event.title, content: event.description || event.content, publishTime: new Date(event.created_at).getTime(), pushTime: Date.now(), isAIGenerated: event.is_ai_generated || false, clickable: true, link: `/event-detail/${event.id}`, autoClose: NOTIFICATION_CONFIG.autoCloseDuration[priority], extra: { eventId: event.id, eventType: event.event_type, importance: event.importance, keywords: event.keywords || [], }, }; }; ``` **源码位置**: `src/contexts/NotificationContext.js:336-395` --- ### 3. 通知分发策略 ```mermaid graph TB A[收到新通知] --> B{检查浏览器权限} B -->|default| C[自动请求权限] B -->|denied| D[显示设置指引] B -->|granted| E{检查页面可见性} E -->|document.hidden=true
页面在后台| F[发送浏览器通知] E -->|document.hidden=false
页面在前台| G[显示网页通知] F --> H[browserNotificationService] H --> I[创建系统通知] I --> J[设置点击处理] J --> K[保存到历史] K --> L[记录性能指标] G --> M[addWebNotification] M --> N[添加到通知队列] N --> O[播放音效] O --> P{自动关闭时长} P -->|0| Q[不自动关闭] P -->|>0| R[延迟关闭] R --> K ``` **代码实现** (src/contexts/NotificationContext.js:558-570): ```javascript const isPageHidden = document.hidden; if (isPageHidden) { // 后台:浏览器通知 logger.info('NotificationContext', 'Page hidden: sending browser notification'); sendBrowserNotification(newNotification); } else { // 前台:网页通知 logger.info('NotificationContext', 'Page visible: sending web notification'); addWebNotification(newNotification); } ``` --- ### 4. 性能监控埋点 系统自动追踪以下事件: ```javascript // 通知接收时 notificationMetricsService.trackReceived(notification); // 通知点击时 notificationMetricsService.trackClicked(notification); // 自动计算响应时间 = 点击时间 - 接收时间 // 通知关闭时 notificationMetricsService.trackDismissed(notification); ``` **性能指标计算**: ```javascript // 点击率 clickRate = (totalClicked / totalReceived) * 100 // 平均响应时间 avgResponseTime = totalResponseTime / totalClicked // 到达率(假设sent=received) deliveryRate = (totalReceived / totalSent) * 100 ``` **数据存储**: - localStorage 持久化 - 最多保留 30 天数据 - 自动清理过期数据 --- ## 🧪 测试快速指南 > 📖 **完整测试手册**: [notification-manual-testing-guide.md](./test-cases/notification-manual-testing-guide.md) ### 本地测试 #### Mock 模式(快速开发测试) ```bash npm start ``` **关键检查**: ```javascript // 1. 验证连接 __SOCKET_DEBUG__.getStatus() // 预期: { type: "MOCK", connected: true } // 2. 查看可用工具 __mockSocket // 包含: simulateDisconnection, pausePush, resumePush, sendTestNotification 等 // 3. 快速触发测试通知(三种方式) __mockSocket.sendTestNotification() // 方式1: 快速触发1条公告通知 __NOTIFY_DEBUG__.testNotify('announcement') // 方式2: 指定类型的测试通知 __NOTIFY_DEBUG__.testNotify('stock_alert') // 方式3: 其他类型(股票/事件/报告) ``` #### Real 模式(后端联调) ```bash npm run start:real ``` **关键检查**: ```javascript // 1. 验证连接 __SOCKET_DEBUG__.getStatus() // 预期: { type: "REAL", connected: true } // 2. 手动订阅 __NOTIFY_DEBUG__.subscribe() // 3. 等待后端推送 // 观察控制台: "✅ New event received" ``` ### 线上测试 **访问**: https://valuefrontier.cn ```javascript // 一键诊断 __NOTIFY_DEBUG__.checkAll() // 手动订阅 __NOTIFY_DEBUG__.subscribe() // 导出报告 __NOTIFY_DEBUG__.exportReport() ``` ### 调试工具速查 | 工具 | 适用环境 | 主要功能 | |------|---------|---------| | `__mockSocket` | Mock模式 | 模拟断线、暂停推送、测试重连 | | `__SOCKET_DEBUG__` | 所有环境 | 连接状态、配置检查、手动订阅 | | `__NOTIFY_DEBUG__` | 所有环境 | 完整诊断、模拟通知、导出报告 | **常用命令**: ```javascript // 完整诊断 __NOTIFY_DEBUG__.checkAll() // 查看帮助 __NOTIFY_DEBUG__.help() // Mock模式:暂停自动推送 __mockSocket.pausePush() // Mock模式:模拟断线 __mockSocket.simulateDisconnection() ``` ### 测试通知方法对比 | 方法 | 环境 | 触发速度 | 通知类型 | 使用场景 | |------|------|---------|---------|---------| | `__mockSocket.sendTestNotification()` | Mock模式 | ⚡ 即时 | 固定:公告通知 | 快速验证通知UI是否正常显示 | | `__NOTIFY_DEBUG__.testNotify(type)` | Mock模式 | ⚡ 即时 | 可选:4种类型 | 测试不同类型通知的显示效果 | | 等待自动推送 | Mock模式 | 🐢 60秒/次 | 随机:所有类型 | 测试自动推送机制和真实场景 | | `__NOTIFY_DEBUG__.subscribe()` | Real模式 | ⏳ 等待后端 | 取决于后端 | 测试后端集成和真实数据 | **推荐用法**: ```javascript // 场景1: 快速验证UI(最快) __mockSocket.sendTestNotification() // 场景2: 测试特定类型 __NOTIFY_DEBUG__.testNotify('stock_alert') // 股票动向 __NOTIFY_DEBUG__.testNotify('event_alert') // 事件动向 __NOTIFY_DEBUG__.testNotify('analysis_report') // 分析报告 // 场景3: 测试自动推送 __mockSocket.resumePush() // 确保推送开启 // 等待60秒... // 场景4: 暂停干扰 __mockSocket.pausePush() // 暂停自动推送,专注测试单条 ``` --- ## ✅ 手动测试清单 ### 功能测试 #### 1. 通知显示测试 - [ ] 公告通知显示正确(蓝色系、图标、内容) - [ ] 股票上涨通知显示为红色系(+图标、红边框) - [ ] 股票下跌通知显示为绿色系(-图标、绿边框) - [ ] 事件动向通知显示正确(橙色系) - [ ] 分析报告(人工)显示正确(紫色系、作者信息) - [ ] 分析报告(AI)显示AI徽章 - [ ] 预测通知显示"预测"徽章和状态提示 #### 2. 优先级测试 - [ ] 紧急通知:红色标签、粗边框、脉冲动画、不自动关闭 - [ ] 重要通知:橙色标签、中等边框、30秒自动关闭 - [ ] 普通通知:无标签、细边框、15秒自动关闭 #### 3. 交互测试 - [ ] 点击可点击通知跳转到正确页面 - [ ] 点击不可点击通知无反应 - [ ] 点击关闭按钮关闭通知 - [ ] 音效开关正常工作 - [ ] 清空全部按钮清空所有通知 #### 4. 折叠展开测试 - [ ] 通知数 ≤ 3 时不显示展开按钮 - [ ] 通知数 > 3 时显示"还有X条通知"按钮 - [ ] 点击展开显示所有通知 - [ ] 点击收起恢复显示前3条 - [ ] 展开状态保存到localStorage(刷新页面保持) - [ ] 展开状态2分钟后自动过期 #### 5. 队列管理测试 - [ ] 最多显示15条历史通知 - [ ] 超过15条自动移除最旧的 - [ ] 新通知在最上方 - [ ] z-index层级正确(最新最高) ### 浏览器通知测试 #### 6. 权限管理测试 - [ ] 首次访问显示权限状态为"default" - [ ] 点击请求权限按钮弹出浏览器授权对话框 - [ ] 授权后状态变为"granted" - [ ] 拒绝后状态变为"denied",显示设置指引 - [ ] 权限状态正确显示 #### 7. 浏览器通知显示测试 - [ ] 权限授予后,切换到其他标签页,收到通知时显示系统通知 - [ ] 点击浏览器通知聚焦窗口并跳转到详情页 - [ ] 普通通知8秒后自动关闭 - [ ] 紧急通知需手动关闭 ### Socket连接测试 #### 8. Mock模式测试 - [ ] 启动后显示"Connected" + "MOCK"状态 - [ ] 控制台显示可用测试函数 - [ ] `__mockSocket.simulateDisconnection()` 模拟断线成功 - [ ] 断线后自动重连(10秒→20秒→40秒) - [ ] 重连成功后显示"✓ 已重新连接" 2秒后消失 - [ ] `__mockSocket.simulateConnectionFailure()` 模拟持续失败 - [ ] 手动关闭连接状态横幅后保存到localStorage - [ ] `__mockSocket.reconnect()` 立即重连成功 #### 9. Real模式测试 - [ ] 启动后显示"Connected" + "REAL"状态 - [ ] 后端推送消息能正确接收 - [ ] 断线后自动重连(1分钟→2分钟→4分钟) - [ ] 网络恢复时自动重连 - [ ] 标签页重新聚焦时自动重连 ### UI测试 #### 10. 响应式测试 - [ ] 移动端(< 480px):宽度 = 屏宽-32px,精简显示 - [ ] 小屏(480-768px):宽度 = 360px - [ ] 平板(768-992px):宽度 = 380px - [ ] 桌面(> 992px):宽度 = 400px - [ ] 底部偏移:移动端12px,桌面端112px(避开设置按钮) #### 11. 动画测试 - [ ] 新通知从底部向上滑入(浮起效果) - [ ] 关闭通知向右滑出消失 - [ ] 紧急通知边框脉冲动画流畅 - [ ] 折叠/展开动画流畅无卡顿 - [ ] 展开15条通知无明显性能问题 #### 12. 无障碍测试 - [ ] Tab键可以聚焦到可点击通知 - [ ] 聚焦时显示蓝色轮廓线 - [ ] Enter/Space键可以打开详情 - [ ] 关闭按钮可键盘操作(Tab + Enter) - [ ] 屏幕阅读器朗读完整通知信息 ### 性能测试 #### 13. 性能指标测试 - [ ] 发送10条通知,打开控制台查看性能指标 - [ ] 点击5条通知,检查点击率计算正确 - [ ] 检查平均响应时间计算正确 - [ ] 导出JSON/CSV文件正常 - [ ] 查看每小时分布图表数据准确 #### 14. 历史记录测试 - [ ] 收到通知后自动保存到历史记录 - [ ] 筛选功能正常(类型、优先级、日期范围、阅读状态) - [ ] 搜索功能返回正确结果 - [ ] 分页功能正常 - [ ] 统计数据准确 - [ ] 导出JSON/CSV正常 ### 跨浏览器测试 #### 15. 兼容性测试 - [ ] Chrome: 所有功能正常 - [ ] Edge: 所有功能正常 - [ ] Firefox: 所有功能正常 - [ ] Safari: 所有功能正常(注意浏览器通知权限) - [ ] 移动端Chrome: 响应式布局正确 - [ ] 移动端Safari: 响应式布局正确 --- ## 🧪 自动化测试用例 完整的自动化测试代码请参考: **[notification-tests.md](./test-cases/notification-tests.md)** 以下是核心测试用例概览: ### 单元测试 #### NotificationContext 测试 ```javascript describe('NotificationContext', () => { test('应该正确初始化默认状态', () => { const { result } = renderHook(() => useNotification(), { wrapper: NotificationProvider, }); expect(result.current.notifications).toEqual([]); expect(result.current.soundEnabled).toBe(true); }); test('应该正确添加通知', () => { const { result } = renderHook(() => useNotification(), { wrapper: NotificationProvider, }); act(() => { result.current.addNotification({ type: 'announcement', priority: 'important', title: '测试通知', content: '内容', }); }); expect(result.current.notifications).toHaveLength(1); expect(result.current.notifications[0].title).toBe('测试通知'); }); // 更多测试用例... }); ``` #### notificationHistoryService 测试 ```javascript describe('notificationHistoryService', () => { beforeEach(() => { localStorage.clear(); }); test('应该保存通知到历史记录', () => { const notification = { id: 'test001', type: 'announcement', title: '测试', content: '内容', }; notificationHistoryService.saveNotification(notification); const { records } = notificationHistoryService.getHistory(); expect(records).toHaveLength(1); expect(records[0].notification.id).toBe('test001'); }); // 更多测试用例... }); ``` ### 集成测试 ```javascript describe('通知系统集成测试', () => { test('完整流程:从接收到显示到点击', async () => { render( ); // 1. 模拟接收通知 act(() => { socket.emit('new_event', mockEventData); }); // 2. 验证通知显示 await waitFor(() => { expect(screen.getByText('测试通知')).toBeInTheDocument(); }); // 3. 模拟点击 const notification = screen.getByText('测试通知').closest('[role="status"]'); fireEvent.click(notification); // 4. 验证导航 expect(mockNavigate).toHaveBeenCalledWith('/event-detail/test001'); // 5. 验证性能指标 const stats = notificationMetricsService.getSummary(); expect(stats.totalClicked).toBe(1); }); }); ``` ### 测试覆盖率目标 | 模块 | 目标覆盖率 | 优先级 | |------|-----------|--------| | **notificationTypes.js** | 100% | 高 | | **notificationHistoryService.js** | 90%+ | 高 | | **notificationMetricsService.js** | 90%+ | 高 | | **browserNotificationService.js** | 80%+ | 中 | | **NotificationContext.js** | 70%+ | 中 | | **NotificationContainer/index.js** | 60%+ | 中 | | **socketService.js** | 50%+ | 低(难以测试) | --- ## 🎨 通知类型配置 ### 类型 1: 公告通知 (Announcement) **适用场景**:公司财报、重大资产重组、分红派息、停复牌公告 **配色方案**: - 主色:蓝色 (Blue) - 图标:📢 MdCampaign - 背景:`blue.50` / 暗色:`rgba(59, 130, 246, 0.15)` **示例**: ```javascript { type: NOTIFICATION_TYPES.ANNOUNCEMENT, priority: PRIORITY_LEVELS.IMPORTANT, title: '贵州茅台发布2024年度财报公告', content: '2024年度营收同比增长15.2%,净利润创历史新高', publishTime: Date.now(), pushTime: Date.now(), isAIGenerated: false, clickable: true, link: '/event-detail/ann001', extra: { announcementType: '财报', companyCode: '600519', }, autoClose: 10000, } ``` --- ### 类型 2: 股票动向 (Stock Alert) **适用场景**:价格预警、异常波动、持仓表现 **动态配色**(根据 `extra.priceChange` 字段): - **涨(+)**:红色系 🔴 - **跌(-)**:绿色系 🟢 **示例(涨)**: ```javascript { type: NOTIFICATION_TYPES.STOCK_ALERT, priority: PRIORITY_LEVELS.URGENT, title: '您关注的股票触发预警', content: '宁德时代(300750) 当前价格 ¥245.50,涨幅 +5.2%', publishTime: Date.now(), pushTime: Date.now(), clickable: true, link: '/stock-overview?code=300750', extra: { stockCode: '300750', stockName: '宁德时代', priceChange: '+5.2%', // 💡 重要:根据此字段判断涨跌 currentPrice: '245.50', }, autoClose: 10000, } ``` --- ### 类型 3: 事件动向 (Event Alert) **适用场景**:央行政策、行业政策、监管动态、宏观事件 **配色方案**: - 主色:橙色 (Orange) - 图标:📄 MdArticle **示例**: ```javascript { type: NOTIFICATION_TYPES.EVENT_ALERT, priority: PRIORITY_LEVELS.IMPORTANT, title: '央行宣布降准0.5个百分点', content: '中国人民银行宣布下调金融机构存款准备金率0.5个百分点', publishTime: Date.now(), pushTime: Date.now(), clickable: true, link: '/event-detail/evt001', extra: { eventId: 'evt001', relatedStocks: 12, impactLevel: '重大利好', }, autoClose: 12000, } ``` --- ### 类型 4: 分析报告 (Analysis Report) **适用场景**:行业研报、策略报告、公司研报 **特殊字段**: - `author`:作者信息(必填) - `isAIGenerated`:是否 AI 生成(显示紫色 AI 徽章) **示例(AI)**: ```javascript { type: NOTIFICATION_TYPES.ANALYSIS_REPORT, priority: PRIORITY_LEVELS.NORMAL, title: 'AI产业链投资机会分析', content: '随着大模型应用加速落地,算力、数据、应用三大方向均存在投资机会', publishTime: Date.now(), pushTime: Date.now(), author: { name: 'AI分析师', organization: '价值前沿', }, isAIGenerated: true, // 💡 显示 AI 徽章 clickable: true, link: '/forecast-report?id=rpt002', extra: { reportType: '策略报告', industry: '人工智能', }, autoClose: 12000, } ``` --- ## 📊 优先级配置 | 优先级 | 标签 | 边框 | 自动关闭 | 使用场景 | |--------|------|------|---------|----------| | **urgent** | 🔴 紧急 | 6px粗边框 + 脉冲动画 | ❌ 不自动关闭 | 重大事件、高优先级预警 | | **important** | 🟠 重要 | 4px中等边框 | 30秒 | 重要消息、一般预警 | | **normal** | - | 2px细边框 | 15秒 | 常规消息、信息推送 | **配置代码**: ```javascript export const PRIORITY_CONFIGS = { [PRIORITY_LEVELS.URGENT]: { label: '紧急', colorScheme: 'red', show: false, borderWidth: '6px', bgOpacity: 0.25, }, [PRIORITY_LEVELS.IMPORTANT]: { label: '重要', colorScheme: 'orange', show: false, borderWidth: '4px', bgOpacity: 0.15, }, [PRIORITY_LEVELS.NORMAL]: { label: '', colorScheme: 'gray', show: false, borderWidth: '2px', bgOpacity: 0.08, }, }; ``` --- ## 🚀 快速开始 ### 1. 启用 Mock 模式 在 `.env` 文件中添加: ```bash REACT_APP_ENABLE_MOCK=true ``` ### 2. 启动项目 ```bash npm start ``` ### 3. 测试通知 打开浏览器控制台 (`F12` → `Console`),使用以下命令测试: #### **Mock 模式测试**(开发环境) ```javascript // 发送测试通知 __mockSocket.sendTestNotification() // 模拟断线重连 __mockSocket.simulateDisconnection() // 查看连接状态 __mockSocket.isConnected() ``` #### **通用测试命令** ```javascript // 查看系统状态 __DEBUG__.diagnose() // 查看连接状态 __DEBUG__.socket.getStatus() // 查看通知权限 window.browserNotificationService.getPermissionStatus() ``` ### 4. 自动推送 Mock 模式下,系统会自动每 60 秒推送 1-2 条随机金融资讯。 查看更多测试命令,请参考本文档开头的 [用户快速指南](#-用户快速指南)。 --- ## 💻 代码使用 ### 在组件中使用 ```javascript import { useNotification } from 'contexts/NotificationContext'; import { NOTIFICATION_TYPES, PRIORITY_LEVELS } from 'constants/notificationTypes'; function MyComponent() { const { addNotification, isConnected } = useNotification(); const handleAnnouncement = () => { addNotification({ type: NOTIFICATION_TYPES.ANNOUNCEMENT, priority: PRIORITY_LEVELS.IMPORTANT, title: '贵州茅台发布2024年度财报公告', content: '2024年度营收同比增长15.2%', publishTime: Date.now(), pushTime: Date.now(), isAIGenerated: false, clickable: true, link: '/event-detail/ann001', autoClose: 10000, }); }; return (

连接状态: {isConnected ? '已连接' : '未连接'}

); } ``` --- ## 🔧 环境配置 ### Mock 模式 vs Real 模式 | 配置项 | Mock 模式 | Real 模式 | |--------|----------|----------| | **启用方式** | `REACT_APP_ENABLE_MOCK=true` | `REACT_APP_ENABLE_MOCK=false` | | **Socket 服务** | mockSocketService | socketService (Socket.IO) | | **推送数据** | 14条模拟金融资讯 | 后端 Flask-SocketIO 推送 | | **自动推送** | 60秒推送1-2条 | 无自动推送,按需推送 | | **重连间隔** | 10s→20s→40s | 1min→2min→4min | | **测试函数** | `window.__mockSocket.*` | 无 | ### 修改推送频率 在 `src/services/mockSocketService.js` 中: ```javascript // 默认: 60秒推送1-2条 socket.startMockPush(60000, 2); // 修改为: 30秒推送1条 socket.startMockPush(30000, 1); // 修改为: 5秒推送3条(压力测试) socket.startMockPush(5000, 3); ``` --- ## 🌐 后端集成(生产环境) ### Flask-SocketIO 配置 ```python from flask_socketio import SocketIO, emit # 初始化 SocketIO socketio = SocketIO(app, cors_allowed_origins=[ "http://localhost:3000", "https://valuefrontier.cn" ]) # 推送金融资讯通知 def send_financial_notification(user_id, data): """ 推送金融资讯通知到指定用户 参数: user_id: 用户ID data: 通知数据(参考通知类型配置) """ socketio.emit('new_event', data, room=user_id) # 启动服务器 if __name__ == '__main__': socketio.run(app, host='0.0.0.0', port=5001) ``` ### 后端推送示例 ```python import time # 推送事件动向 socketio.emit('new_event', { 'type': 'event_alert', 'priority': 'important', 'title': '央行宣布降准0.5个百分点', 'content': '中国人民银行宣布下调金融机构存款准备金率0.5个百分点', 'publishTime': int(time.time() * 1000), 'pushTime': int(time.time() * 1000), 'isAIGenerated': False, 'clickable': True, 'link': '/event-detail/evt001', 'extra': { 'eventId': 'evt001', 'relatedStocks': 12, 'impactLevel': '重大利好', }, 'autoClose': 12000 }, room=user_id) ``` --- ## 🐛 故障排除 ### 问题 1: 通知不显示 **检查项**: 1. 确认 `NotificationProvider` 已包裹应用 2. 检查浏览器控制台是否有错误 3. 确认 socket 连接状态(使用 `__DEBUG__.socket.getStatus()`) 4. 检查通知数据格式是否正确 ### 问题 2: 股票动向颜色不对 **解决方案**: 1. 检查 `extra.priceChange` 字段是否存在 2. 确认格式为 "+5.2%" 或 "-3.8%"(包含符号) 3. 确认使用了 `NOTIFICATION_TYPES.STOCK_ALERT` 类型 ### 问题 3: AI 徽章不显示 **解决方案**: 1. 检查 `isAIGenerated` 字段是否为 `true` 2. 确认字段名拼写正确(驼峰命名) ### 问题 4: 浏览器通知不工作 **解决方案**: 1. 检查浏览器通知权限是否已授予 2. macOS: 关闭"专注模式"或"勿扰模式" 3. Chrome: 检查 `chrome://settings/content/notifications` 4. 退出全屏模式(某些浏览器全屏时不显示通知) 5. **无痕/隐私模式特别说明**: - Chrome 无痕模式:可以授权,但关闭窗口后权限会被清除 - Safari 隐私模式:通知权限被永久拒绝,无法更改 - 建议:使用普通浏览器窗口以获得完整体验 **快速诊断**: ```javascript // 在浏览器控制台运行 console.log('Permission:', Notification.permission); // 'granted' | 'denied' | 'default' console.log('Supported:', 'Notification' in window); // true | false // 完整诊断 window.__NOTIFY_DEBUG__.checkAll(); ``` ### 问题 5: Chrome 无痕模式/隐私模式下 `Notification.permission = 'denied'` 怎么办? #### 🔍 问题分析 当在 Chrome 无痕模式或其他浏览器隐私模式下遇到通知权限被拒绝的情况,需要了解不同浏览器的行为差异: **浏览器行为对比表**: | 浏览器模式 | 初始权限状态 | 是否可授权 | 权限持久性 | 推荐方案 | |-----------|-------------|-----------|-----------|----------| | **Chrome 正常模式** | `default` | ✅ 可以 | ✅ 永久保存 | ⭐ 推荐使用 | | **Chrome 无痕模式** | `default` | ✅ 可以 | ❌ 关闭窗口后清除 | 可用但需重复授权 | | **Safari 隐私模式** | `denied` | ❌ 不可以 | ❌ 永久拒绝 | 仅网页内通知 | | **Firefox 隐私模式** | `default` | ✅ 可以 | ❌ 关闭窗口后清除 | 可用但需重复授权 | **关键点**: - ✅ Chrome 无痕模式的初始状态通常是 `default`(未决定),**不是** `denied` - ⚠️ 如果看到 `denied`,说明用户点击了"阻止"按钮 - 🚫 Safari 隐私模式会直接显示 `denied`,且无法通过代码更改 - 🔄 权限在无痕/隐私窗口关闭后会被清除(Chrome/Firefox) #### ✅ 当前系统已自动处理 **好消息**:您的通知系统已经完美处理了这种情况! **自动降级策略**: ```javascript // 在 NotificationContext.js 中 if (browserPermission !== 'granted') { // 跳过浏览器通知,只显示网页内通知 logger.warn('NotificationContext', 'Browser permission not granted'); return; } ``` **用户引导机制**: - 当权限为 `denied` 时,自动显示橙色提示框 - 提示内容:"浏览器通知已被拒绝" - 引导文案:"💡 如需接收桌面通知,请在浏览器设置中允许通知权限" **实际效果**: - ✅ 网页内通知卡片继续正常显示 - ⏭️ 浏览器桌面通知被优雅跳过 - ✅ 所有核心功能不受影响 - ✅ 用户体验降级合理 #### 🛠️ 三种解决方案 **方案 1:退出无痕模式(强烈推荐)** ```bash # 原因: # - 权限会永久保存 # - 获得完整通知体验 # - 不需要每次重新授权 # 操作:使用普通浏览器窗口访问应用 ``` **方案 2:接受降级体验(推荐)** ```bash # 系统已自动处理: # ✅ 网页内通知正常显示(通知卡片) # ⏭️ 桌面通知被跳过 # ✅ 不影响核心功能 # ✅ 用户体验合理 # 操作:无需任何操作,系统自动降级 ``` **方案 3:在无痕模式中重新授权(仅 Chrome/Firefox)** **Chrome 无痕模式**: 1. 点击地址栏左侧的 🔒 锁图标 2. 找到"通知"设置项 3. 将"阻止"改为"允许" 4. 刷新页面(F5) 5. ⚠️ **注意**:关闭无痕窗口后需要重新授权 **Safari 隐私模式**: ```bash # ❌ Safari 隐私模式无法更改权限 # 💡 建议:退出隐私模式或接受降级体验 ``` #### 📊 调试与验证 **快速检查权限状态**: ```javascript // 在浏览器控制台运行 // 方法 1:直接查看 console.log('Permission:', Notification.permission); // 输出: 'granted' | 'denied' | 'default' // 方法 2:检查是否支持 console.log('Supported:', 'Notification' in window); // 输出: true | false // 方法 3:完整诊断(推荐) window.__NOTIFY_DEBUG__.checkAll(); ``` **完整诊断输出示例**: ```bash ========== 【通知系统诊断】 ========== ✓ Socket 配置检查完成 ✓ 连接状态: ✅ 已连接 ✓ API Base: http://49.232.185.254:5001 ✓ 浏览器通知权限: denied ========== 诊断报告 ========== ┌─────────────┬────────────────────┐ │ socket.type │ MOCK │ │ connected │ true │ │ permission │ denied │ └─────────────┴────────────────────┘ ⚠️ 发现问题: ['浏览器通知权限被拒绝'] 💡 修复建议: 1. 如在无痕/隐私模式,建议退出使用正常模式 2. Chrome: 点击地址栏🔒 → 通知 → 允许 3. 或接受降级体验:继续使用网页内通知 ==================================== ``` **测试通知(仅 Mock 模式)**: ```javascript // 测试不同类型通知 window.__NOTIFY_DEBUG__.testNotify('announcement'); // 公告通知 window.__NOTIFY_DEBUG__.testNotify('stock_alert'); // 股票动向 window.__NOTIFY_DEBUG__.testNotify('event_alert'); // 事件动向 window.__NOTIFY_DEBUG__.testNotify('analysis_report'); // 分析报告 // 查看帮助 window.__NOTIFY_DEBUG__.help(); ``` #### 🎯 最佳实践建议 **开发环境**: ```javascript // 使用 Mock 模式 + Chrome 正常窗口 // .env.mock REACT_APP_ENABLE_MOCK=true // 一次授权,永久有效 ``` **测试环境**: ```javascript // 测试多种权限状态 // 1. granted - 正常授权 // 2. denied - 测试降级体验 // 3. default - 测试首次访问流程 ``` **生产环境**: ```javascript // 引导用户使用正常浏览器窗口 // 提供清晰的权限说明 // 降级体验要优雅 ``` #### 📝 相关代码位置 **权限处理逻辑**: - `src/services/browserNotificationService.js:26-31` - 权限状态获取 - `src/services/browserNotificationService.js:85-88` - 权限检查 - `src/contexts/NotificationContext.js:295-298` - 发送前权限验证 - `src/contexts/NotificationContext.js:477-506` - 拒绝时用户引导 **调试工具**: - `src/services/socket/index.js:192-362` - `window.__NOTIFY_DEBUG__` API --- ### 问题 6: Socket 连接失败 **解决方案**: 1. 检查后端 Flask-SocketIO 是否正确运行 2. 确认 CORS 配置正确 3. 检查 `src/utils/apiConfig.js` 中的 API 地址 4. 查看控制台日志确认错误信息 ### 调试工具 **控制台日志**: ```javascript // 所有操作都有详细日志 logger.info('NotificationContext', 'Event', data); logger.warn('NotificationContext', 'Warning', error); logger.error('NotificationContext', 'Error', error); ``` **Mock模式测试函数**: ```javascript // 查看可用函数 console.log(window.__mockSocket); // 模拟断线 __mockSocket.simulateDisconnection(); // 查看连接状态 __mockSocket.isConnected(); // 查看重连次数 __mockSocket.getAttempts(); ``` ### 日志分析 **正常日志流程**: ``` 1. [NotificationContext] Initializing socket connection... 2. [NotificationContext] Socket connected 3. [NotificationContext] Received new event { id: 'evt001', title: '...' } 4. [NotificationContext] Adding notification { id: 'evt001', type: 'event_alert' } 5. [NotificationContext] Page visible: sending web notification ``` **异常日志排查**: ``` ERROR: Socket connect_error → 检查后端是否运行,网络是否通畅 WARN: Duplicate notification ignored at socket level → 正常,去重机制工作正常 ERROR: Browser permission not granted → 用户未授权浏览器通知,提示用户授权 ``` --- ## 📏 开发规范 ### 1. 添加新通知类型 **步骤**: 1. 在 `src/constants/notificationTypes.js` 添加类型定义 2. 添加类型配置(图标、颜色、样式) 3. 在 `NotificationContainer` 中添加渲染逻辑 4. 使用控制台命令测试(Mock 模式:`__mockSocket.sendTestNotification()`) 5. 更新文档 **示例**: ```javascript // 1. 添加类型 export const NOTIFICATION_TYPES = { // ...现有类型 MARKET_NEWS: 'market_news', // 新增 }; // 2. 添加配置 export const NOTIFICATION_TYPE_CONFIGS = { // ...现有配置 [NOTIFICATION_TYPES.MARKET_NEWS]: { name: '市场快讯', icon: MdNewspaper, colorScheme: 'teal', bg: 'teal.50', borderColor: 'teal.400', iconColor: 'teal.500', hoverBg: 'teal.100', }, }; ``` ### 2. 自定义通知组件 如需完全自定义通知样式: ```javascript import { useNotification } from 'contexts/NotificationContext'; function CustomNotification({ notification }) { const { removeNotification, trackNotificationClick } = useNotification(); const handleClick = () => { trackNotificationClick(notification.id); // 自定义导航逻辑 removeNotification(notification.id, true); }; return ( {/* 自定义样式 */} ); } ``` ### 3. 扩展性能监控 添加自定义指标: ```javascript // 在 notificationMetricsService 中添加新方法 trackCustomEvent(notification, eventType, data) { // 自定义追踪逻辑 } // 使用 notificationMetricsService.trackCustomEvent(notification, 'share', { platform: 'wechat', }); ``` --- ## ⚡ 性能优化建议 ### 1. 队列管理优化 ✅ 已实现 - 系统自动限制最多 15 条历史记录 - 超出自动移除最旧的,避免内存泄漏 - 建议根据实际需求调整 `NOTIFICATION_CONFIG.maxHistory` ### 2. 合理设置自动关闭时长 | 类型 | 建议时长 | 理由 | |------|---------|------| | 公告通知 | 10秒 | 内容较简短 | | 股票动向 | 10秒 | 需要快速响应 | | 事件动向 | 12秒 | 内容较多 | | 分析报告 | 12秒 | 内容较多 | | 紧急通知 | 不关闭 | 需要用户确认 | ### 3. 避免频繁推送 - 生产环境建议间隔至少 3 秒 - Mock 模式默认 60 秒推送 1-2 条 - 避免短时间内大量推送造成用户困扰 ### 4. React 性能优化 ✅ 已实现 ```javascript // 1. React.memo 优化 const NotificationItem = React.memo(NotificationItemComponent, (prev, next) => { return prev.notification.id === next.notification.id && prev.isNewest === next.isNewest; }); // 2. useMemo 缓存计算 const colors = useMemo(() => ({ bg: priorityBgColor, border: borderColor, // ... }), [priorityBgColor, borderColor, ...]); // 3. useCallback 优化事件处理 const handleClick = useCallback(() => { trackNotificationClick(id); navigate(link); }, [id, link, trackNotificationClick, navigate]); // 4. GPU 加速动画 willChange="transform, opacity" ``` ### 5. 动画性能优化 ✅ 已实现 - 只对新增通知应用动画 - 使用 Framer Motion 的 GPU 加速 - 展开15条通知时禁用动画(避免卡顿) - 性能提升 90%+ ### 6. localStorage 优化 ```javascript // 定期清理过期数据 notificationHistoryService.cleanup(100); // 限制最大存储数量 MAX_HISTORY_SIZE = 500; // 压缩存储数据(可选) const compressedData = LZ.compress(JSON.stringify(data)); ``` --- ## 🔒 安全注意事项 ### 1. XSS 防护 ```javascript // ✅ 正确:使用 React 自动转义 {notification.title} // ❌ 错误:使用 dangerouslySetInnerHTML ``` ### 2. 链接验证 ```javascript // 跳转前验证链接合法性 const isValidLink = (link) => { return link && link.startsWith('/'); // 只允许内部链接 }; if (isActuallyClickable && isValidLink(link)) { navigate(link); } ``` ### 3. 敏感信息保护 ```javascript // 不要在通知中显示敏感信息 // ❌ 错误 content: `您的密码是:${password}` // ✅ 正确 content: '密码修改成功,请重新登录' ``` ### 4. localStorage 安全 ```javascript // 不要存储敏感数据到 localStorage // ❌ 错误 localStorage.setItem('user_token', token); // ✅ 正确:使用 httpOnly Cookie 或 sessionStorage ``` --- ## 📁 文件结构 ``` src/ ├── constants/ │ └── notificationTypes.js # 通知类型定义和常量 ├── services/ │ ├── socket/ │ │ └── index.js # Socket 服务统一导出 │ ├── socketService.js # 真实 Socket.IO 服务 │ ├── mockSocketService.js # Mock Socket 服务 │ ├── browserNotificationService.js # 浏览器通知服务 │ ├── notificationHistoryService.js # 历史记录服务 │ └── notificationMetricsService.js # 性能监控服务 ├── contexts/ │ └── NotificationContext.js # 通知上下文管理 ├── components/ │ └── NotificationContainer/ │ └── index.js # 通知容器组件 ├── hooks/ │ └── useEventNotifications.js # 事件订阅 Hook └── assets/ └── sounds/ └── notification.wav # 通知音效 ``` --- ## 📝 更新日志 ### v2.11.0 (2025-01-07) - Socket 连接优化 - ✅ 指数退避重连策略 - ✅ 连接状态横幅优化 - ✅ Mock 模式测试功能增强 ### v2.10.0 (2025-01-06) - 按钮加载态 - ✅ 点击"查看详情"显示 loading spinner - ✅ 防重复点击机制 ### v2.9.0 (2025-01-05) - 头部简化 - ✅ AI 和预测标识移到底部 ### v2.8.0 (2025-01-04) - 无障碍支持 - ✅ 完整的 ARIA 属性 - ✅ 键盘导航支持 ### v2.7.0 (2025-01-03) - 智能动画 - ✅ 只对新增通知应用动画 - ✅ 性能提升 90%+ ### v2.6.0 (2025-01-02) - 状态持久化 - ✅ 展开/收起状态保存到 localStorage - ✅ 2分钟自动过期 ### v2.5.0 (2025-01-01) - 避开设置按钮 - ✅ 桌面端底部偏移112px ### v2.4.0 (2024-12-31) - 响应式优化 - ✅ 移动端、平板、桌面完美适配 ### v2.3.0 (2024-12-30) - 智能折叠 - ✅ 最多显示3条,超过显示展开按钮 - ✅ 按优先级自动关闭 ### v2.2.0 (2024-12-29) - 双通知系统 - ✅ 浏览器原生通知集成 - ✅ 智能分发策略 ### v2.1.0 (2024-12-28) - 预测通知系统 - ✅ 预测→详情两阶段推送 - ✅ 严格可点击性判断 ### v2.0.0 (2024-12-27) - 金融资讯专业版 - ✅ 4种金融资讯通知类型 - ✅ 3级优先级系统 - ✅ 智能元数据、动态配色 --- ## 💡 技术栈 - **前端框架**: React 18.3.1 - **UI 库**: Chakra UI 2.8.2 - **路由**: React Router v6 - **实时通信**: Socket.IO Client 4.7.4 - **动画库**: Framer Motion - **后端框架**: Flask-SocketIO 5.3.6 - **状态管理**: React Context API - **图标库**: React Icons (Material Design) --- ## 📞 支持 如有问题,请查看: - **用户快速指南**: 本文档开头的[用户快速指南](#-用户快速指南) - **项目文档**: `CLAUDE.md` - **调试命令**: 浏览器控制台使用 `__DEBUG__.help()` 查看所有可用命令 - **控制台日志**: 所有操作都有详细日志 - **类型定义**: `src/constants/notificationTypes.js` --- **祝你使用愉快!** 🎉