From 678eb6838e64165f913e8018eb9ff25d4be5565b Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 17 Nov 2025 18:40:05 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=90=88=E5=B9=B6=E5=B9=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=80=9A=E7=9F=A5=E7=B3=BB=E7=BB=9F=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E8=87=B3=20v3.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要更新: - 合并 ENHANCED_FEATURES_GUIDE.md 到 NOTIFICATION_SYSTEM.md - 移除过时的 Mock 模式和测试工具引用 - 更新所有调试工具为 window.__DEBUG__ - 完善增强功能文档(智能桌面通知、性能监控、历史记录) - 重新组织文档结构为 10 个清晰的部分 - 更新所有代码示例与最新代码保持一致 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/ENHANCED_FEATURES_GUIDE.md | 626 --------- docs/NOTIFICATION_SYSTEM.md | 2178 +++++++++++++++---------------- 2 files changed, 1084 insertions(+), 1720 deletions(-) delete mode 100644 docs/ENHANCED_FEATURES_GUIDE.md diff --git a/docs/ENHANCED_FEATURES_GUIDE.md b/docs/ENHANCED_FEATURES_GUIDE.md deleted file mode 100644 index 9fc726a5..00000000 --- a/docs/ENHANCED_FEATURES_GUIDE.md +++ /dev/null @@ -1,626 +0,0 @@ -# 通知系统增强功能 - 使用指南 - -## 📋 概述 - -本指南介绍通知系统的三大增强功能: -1. **智能桌面通知** - 自动请求权限,系统级通知 -2. **性能监控** - 追踪推送效果,数据驱动优化 -3. **历史记录** - 持久化存储,随时查询 - ---- - -## 🎯 功能 1:智能桌面通知 - -### 功能说明 - -首次收到重要/紧急通知时,自动请求浏览器通知权限,确保用户不错过关键信息。 - -### 工作原理 - -```javascript -// 在 NotificationContext 中的逻辑 -if (priority === URGENT || priority === IMPORTANT) { - if (browserPermission === 'default' && !hasRequestedPermission) { - // 首次遇到重要通知,自动请求权限 - await requestBrowserPermission(); - setHasRequestedPermission(true); // 避免重复请求 - } -} -``` - -### 权限状态 - -- **granted**: 已授权,可以发送桌面通知 -- **denied**: 已拒绝,无法发送桌面通知 -- **default**: 未请求,首次重要通知时会自动请求 - -### 使用示例 - -**自动触发**(推荐) -```javascript -// 无需任何代码,系统自动处理 -// 首次收到重要/紧急通知时会自动弹出权限请求 -``` - -**手动请求** -```javascript -import { useNotification } from 'contexts/NotificationContext'; - -function SettingsPage() { - const { requestBrowserPermission, browserPermission } = useNotification(); - - return ( -
-

当前状态: {browserPermission}

- -
- ); -} -``` - -### 通知分发策略 - -| 优先级 | 页面在前台 | 页面在后台 | -|-------|----------|----------| -| 紧急 | 桌面通知 + 网页通知 | 桌面通知 + 网页通知 | -| 重要 | 网页通知 | 桌面通知 | -| 普通 | 网页通知 | 网页通知 | - -### 测试步骤 - -1. **清除已保存的权限状态** - ```javascript - localStorage.removeItem('browser_notification_requested'); - ``` - -2. **刷新页面** - -3. **触发一个重要/紧急通知** - - Mock 模式:等待自动推送 - - Real 模式:创建测试事件 - -4. **观察权限请求弹窗** - - 浏览器会弹出通知权限请求 - - 点击"允许"授权 - -5. **验证桌面通知** - - 切换到其他标签页 - - 收到重要通知时应该看到桌面通知 - ---- - -## 📊 功能 2:性能监控 - -### 功能说明 - -追踪通知推送的各项指标,包括: -- **到达率**: 发送 vs 接收 -- **点击率**: 点击 vs 接收 -- **响应时间**: 收到通知到点击的平均时间 -- **类型分布**: 各类型通知的数量和效果 -- **时段分布**: 每小时推送量 - -### API 参考 - -#### 获取汇总统计 - -```javascript -import { notificationMetricsService } from 'services/notificationMetricsService'; - -const summary = notificationMetricsService.getSummary(); -console.log(summary); -/* 输出: -{ - totalSent: 100, - totalReceived: 98, - totalClicked: 45, - totalDismissed: 53, - avgResponseTime: 5200, // 毫秒 - clickRate: '45.92', // 百分比 - deliveryRate: '98.00' // 百分比 -} -*/ -``` - -#### 获取按类型统计 - -```javascript -const byType = notificationMetricsService.getByType(); -console.log(byType); -/* 输出: -{ - announcement: { sent: 20, received: 20, clicked: 15, dismissed: 5, clickRate: '75.00' }, - stock_alert: { sent: 30, received: 30, clicked: 20, dismissed: 10, clickRate: '66.67' }, - event_alert: { sent: 40, received: 38, clicked: 10, dismissed: 28, clickRate: '26.32' }, - analysis_report: { sent: 10, received: 10, clicked: 0, dismissed: 10, clickRate: '0.00' } -} -*/ -``` - -#### 获取按优先级统计 - -```javascript -const byPriority = notificationMetricsService.getByPriority(); -console.log(byPriority); -/* 输出: -{ - urgent: { sent: 10, received: 10, clicked: 9, dismissed: 1, clickRate: '90.00' }, - important: { sent: 40, received: 39, clicked: 25, dismissed: 14, clickRate: '64.10' }, - normal: { sent: 50, received: 49, clicked: 11, dismissed: 38, clickRate: '22.45' } -} -*/ -``` - -#### 获取每日数据 - -```javascript -const dailyData = notificationMetricsService.getDailyData(7); // 最近 7 天 -console.log(dailyData); -/* 输出: -[ - { date: '2025-01-15', sent: 15, received: 14, clicked: 6, dismissed: 8, clickRate: '42.86' }, - { date: '2025-01-16', sent: 20, received: 20, clicked: 10, dismissed: 10, clickRate: '50.00' }, - ... -] -*/ -``` - -#### 获取完整指标 - -```javascript -const allMetrics = notificationMetricsService.getAllMetrics(); -console.log(allMetrics); -``` - -#### 导出数据 - -```javascript -// 导出为 JSON -const json = notificationMetricsService.exportToJSON(); -console.log(json); - -// 导出为 CSV -const csv = notificationMetricsService.exportToCSV(); -console.log(csv); -``` - -#### 重置指标 - -```javascript -notificationMetricsService.reset(); -``` - -### 在控制台查看实时指标 - -打开浏览器控制台,执行: - -```javascript -// 引入服务 -import { notificationMetricsService } from './services/notificationMetricsService.js'; - -// 查看汇总 -console.table(notificationMetricsService.getSummary()); - -// 查看按类型分布 -console.table(notificationMetricsService.getByType()); - -// 查看最近 7 天数据 -console.table(notificationMetricsService.getDailyData(7)); -``` - -### 监控埋点(自动) - -监控服务已自动集成到 `NotificationContext`,无需手动调用: - -- **trackReceived**: 收到通知时自动调用 -- **trackClicked**: 点击通知时自动调用 -- **trackDismissed**: 关闭通知时自动调用 - -### 可视化展示(可选) - -你可以基于监控数据创建仪表板: - -```javascript -import { notificationMetricsService } from 'services/notificationMetricsService'; -import { PieChart, LineChart } from 'recharts'; - -function MetricsDashboard() { - const summary = notificationMetricsService.getSummary(); - const dailyData = notificationMetricsService.getDailyData(7); - const byType = notificationMetricsService.getByType(); - - return ( -
- {/* 汇总卡片 */} - - - - - {/* 类型分布饼图 */} - ({ - name: type, - value: data.received - }))} /> - - {/* 每日趋势折线图 */} - -
- ); -} -``` - ---- - -## 📜 功能 3:历史记录 - -### 功能说明 - -持久化存储所有接收到的通知,支持: -- 查询和筛选 -- 搜索关键词 -- 标记已读/已点击 -- 批量删除 -- 导出(JSON/CSV) - -### API 参考 - -#### 获取历史记录(支持筛选和分页) - -```javascript -import { notificationHistoryService } from 'services/notificationHistoryService'; - -const result = notificationHistoryService.getHistory({ - type: 'event_alert', // 可选:筛选类型 - priority: 'urgent', // 可选:筛选优先级 - readStatus: 'unread', // 可选:'read' | 'unread' | 'all' - startDate: Date.now() - 7 * 24 * 60 * 60 * 1000, // 可选:开始日期 - endDate: Date.now(), // 可选:结束日期 - page: 1, // 页码 - pageSize: 20, // 每页数量 -}); - -console.log(result); -/* 输出: -{ - records: [...], // 当前页的记录 - total: 150, // 总记录数 - page: 1, // 当前页 - pageSize: 20, // 每页数量 - totalPages: 8 // 总页数 -} -*/ -``` - -#### 搜索历史记录 - -```javascript -const results = notificationHistoryService.searchHistory('降准'); -console.log(results); // 返回标题/内容中包含"降准"的所有记录 -``` - -#### 标记已读/已点击 - -```javascript -// 标记已读 -notificationHistoryService.markAsRead('notification_id'); - -// 标记已点击 -notificationHistoryService.markAsClicked('notification_id'); -``` - -#### 删除记录 - -```javascript -// 删除单条 -notificationHistoryService.deleteRecord('notification_id'); - -// 批量删除 -notificationHistoryService.deleteRecords(['id1', 'id2', 'id3']); - -// 清空所有 -notificationHistoryService.clearHistory(); -``` - -#### 获取统计数据 - -```javascript -const stats = notificationHistoryService.getStats(); -console.log(stats); -/* 输出: -{ - total: 500, // 总记录数 - read: 320, // 已读数 - unread: 180, // 未读数 - clicked: 150, // 已点击数 - clickRate: '30.00', // 点击率 - byType: { // 按类型统计 - announcement: 100, - stock_alert: 150, - event_alert: 200, - analysis_report: 50 - }, - byPriority: { // 按优先级统计 - urgent: 50, - important: 200, - normal: 250 - } -} -*/ -``` - -#### 导出历史记录 - -```javascript -// 导出为 JSON 字符串 -const json = notificationHistoryService.exportToJSON({ - type: 'event_alert' // 可选:只导出特定类型 -}); - -// 导出为 CSV 字符串 -const csv = notificationHistoryService.exportToCSV(); - -// 直接下载 JSON 文件 -notificationHistoryService.downloadJSON(); - -// 直接下载 CSV 文件 -notificationHistoryService.downloadCSV(); -``` - -### 在控制台使用 - -打开浏览器控制台,执行: - -```javascript -// 引入服务 -import { notificationHistoryService } from './services/notificationHistoryService.js'; - -// 查看所有历史 -console.table(notificationHistoryService.getHistory().records); - -// 搜索 -const results = notificationHistoryService.searchHistory('央行'); -console.table(results); - -// 查看统计 -console.table(notificationHistoryService.getStats()); - -// 导出并下载 -notificationHistoryService.downloadJSON(); -``` - -### 数据结构 - -每条历史记录包含: - -```javascript -{ - id: 'notif_123', // 通知 ID - notification: { // 完整通知对象 - type: 'event_alert', - priority: 'urgent', - title: '...', - content: '...', - ... - }, - receivedAt: 1737459600000, // 接收时间戳 - readAt: 1737459650000, // 已读时间戳(null 表示未读) - clickedAt: null, // 已点击时间戳(null 表示未点击) -} -``` - -### 存储限制 - -- **最大数量**: 500 条(超过后自动删除最旧的) -- **存储位置**: localStorage -- **容量估算**: 约 2-5MB(取决于通知内容长度) - ---- - -## 🔧 技术细节 - -### 文件结构 - -``` -src/ -├── services/ -│ ├── browserNotificationService.js [已存在] 浏览器通知服务 -│ ├── notificationMetricsService.js [新建] 性能监控服务 -│ └── notificationHistoryService.js [新建] 历史记录服务 -├── contexts/ -│ └── NotificationContext.js [修改] 集成所有功能 -└── components/ - └── NotificationContainer/ - └── index.js [修改] 添加点击追踪 -``` - -### 修改清单 - -| 文件 | 修改内容 | 状态 | -|------|---------|------| -| `NotificationContext.js` | 添加智能权限请求、监控埋点、历史保存 | ✅ 已完成 | -| `NotificationContainer/index.js` | 添加点击追踪 | ✅ 已完成 | -| `notificationMetricsService.js` | 性能监控服务 | ✅ 已创建 | -| `notificationHistoryService.js` | 历史记录服务 | ✅ 已创建 | - -### 数据流 - -``` -用户收到通知 - ↓ -NotificationContext.addWebNotification() - ├─ notificationMetricsService.trackReceived() [监控埋点] - ├─ notificationHistoryService.saveNotification() [历史保存] - ├─ 首次重要通知 → requestBrowserPermission() [智能权限] - └─ 显示网页通知或桌面通知 - -用户点击通知 - ↓ -NotificationContainer.handleClick() - ├─ notificationMetricsService.trackClicked() [监控埋点] - ├─ notificationHistoryService.markAsClicked() [历史标记] - └─ 跳转到目标页面 - -用户关闭通知 - ↓ -NotificationContext.removeNotification() - └─ notificationMetricsService.trackDismissed() [监控埋点] -``` - ---- - -## 🧪 测试步骤 - -### 1. 测试智能桌面通知 - -```bash -# 1. 清除已保存的权限状态 -localStorage.removeItem('browser_notification_requested'); - -# 2. 刷新页面 - -# 3. 等待或触发一个重要/紧急通知 - -# 4. 观察浏览器弹出权限请求 - -# 5. 授权后验证桌面通知功能 -``` - -### 2. 测试性能监控 - -```javascript -// 在控制台执行 -import { notificationMetricsService } from './services/notificationMetricsService.js'; - -// 查看实时统计 -console.table(notificationMetricsService.getSummary()); - -// 模拟推送几条通知,再次查看 -console.table(notificationMetricsService.getAllMetrics()); - -// 导出数据 -console.log(notificationMetricsService.exportToJSON()); -``` - -### 3. 测试历史记录 - -```javascript -// 在控制台执行 -import { notificationHistoryService } from './services/notificationHistoryService.js'; - -// 查看历史 -console.table(notificationHistoryService.getHistory().records); - -// 搜索 -console.table(notificationHistoryService.searchHistory('降准')); - -// 查看统计 -console.table(notificationHistoryService.getStats()); - -// 导出 -notificationHistoryService.downloadJSON(); -``` - ---- - -## 📈 数据导出示例 - -### 导出性能监控数据 - -```javascript -import { notificationMetricsService } from 'services/notificationMetricsService'; - -// 导出 JSON -const json = notificationMetricsService.exportToJSON(); -// 复制到剪贴板或保存 - -// 导出 CSV -const csv = notificationMetricsService.exportToCSV(); -// 可以在 Excel 中打开 -``` - -### 导出历史记录 - -```javascript -import { notificationHistoryService } from 'services/notificationHistoryService'; - -// 导出最近 7 天的事件动向通知 -const json = notificationHistoryService.exportToJSON({ - type: 'event_alert', - startDate: Date.now() - 7 * 24 * 60 * 60 * 1000 -}); - -// 直接下载为文件 -notificationHistoryService.downloadJSON({ - type: 'event_alert' -}); -``` - ---- - -## ⚠️ 注意事项 - -### 1. localStorage 容量限制 - -- 大多数浏览器限制为 5-10MB -- 建议定期清理历史记录和监控数据 -- 使用导出功能备份数据 - -### 2. 浏览器兼容性 - -- **桌面通知**: 需要 HTTPS 或 localhost -- **localStorage**: 所有现代浏览器支持 -- **权限请求**: 需要用户交互(不能自动授权) - -### 3. 隐私和数据安全 - -- 所有数据存储在本地(localStorage) -- 不会上传到服务器 -- 用户可以随时清空数据 - -### 4. 性能影响 - -- 监控埋点非常轻量,几乎无性能影响 -- 历史记录保存异步进行,不阻塞 UI -- 数据查询在客户端完成,不增加服务器负担 - ---- - -## 🎉 总结 - -### 已实现的功能 - -✅ **智能桌面通知** -- 首次重要通知时自动请求权限 -- 智能分发策略(前台/后台) -- localStorage 持久化权限状态 - -✅ **性能监控** -- 到达率、点击率、响应时间追踪 -- 按类型、优先级、时段统计 -- 数据导出(JSON/CSV) - -✅ **历史记录** -- 持久化存储(最多 500 条) -- 筛选、搜索、分页 -- 已读/已点击标记 -- 数据导出(JSON/CSV) - -### 未实现的功能(备份,待上线) - -⏸️ 历史记录页面 UI(代码已备份,随时可上线) -⏸️ 监控仪表板 UI(可选,暂未实现) - -### 下一步建议 - -1. **用户设置页面**: 允许用户自定义通知偏好 -2. **声音提示**: 为紧急通知添加音效 -3. **数据同步**: 将历史和监控数据同步到服务器 -4. **高级筛选**: 添加更多筛选维度(如关键词、股票代码等) - ---- - -**文档版本**: v1.0 -**最后更新**: 2025-01-21 -**维护者**: Claude Code diff --git a/docs/NOTIFICATION_SYSTEM.md b/docs/NOTIFICATION_SYSTEM.md index 224a7a4b..51f16a40 100644 --- a/docs/NOTIFICATION_SYSTEM.md +++ b/docs/NOTIFICATION_SYSTEM.md @@ -1,103 +1,83 @@ # 实时消息推送系统 - 完整技术文档 -> **版本**: v2.12.0 +> **版本**: v3.0.0 > **更新日期**: 2025-11-17 > **文档类型**: 用户指南 + 完整技术规格 > -> ⚠️ **重要更新**: -> - 测试工具已移除(2025-11-17):NotificationTestTool、window.__TEST_NOTIFICATION__、notificationDebugger -> - 保留生产可用调试工具:window.__DEBUG__、window.browserNotificationService +> 🎯 **重要说明**: +> - 本文档已合并原 `ENHANCED_FEATURES_GUIDE.md` 的内容 +> - 所有 API 引用和代码示例已更新至最新版本 +> - 调试工具统一使用 `window.__DEBUG__` --- ## 📑 目录 -### 👤 第零部分:用户快速指南 -0. [连接状态查看](#-连接状态查看) -1. [手动操作指南](#-手动操作指南) -2. [常见问题解决](#-常见问题解决) -3. [可用调试命令](#-可用调试命令) +### 👤 第一部分:用户快速指南 +1. [连接状态查看](#-连接状态查看) +2. [手动操作指南](#-手动操作指南) +3. [常见问题解决](#-常见问题解决) +4. [可用调试命令](#-可用调试命令) -### 第一部分:快速入门(开发者) -1. [系统简介](#-系统概述) -2. [核心特性](#核心特性) -3. [5分钟快速开始](#-快速开始) -4. [基础使用示例](#-代码使用) +### 🚀 第二部分:快速入门(开发者) +5. [系统简介](#-系统概述) +6. [核心特性](#核心特性) +7. [5分钟快速开始](#-快速开始) +8. [基础使用示例](#-代码使用) -### 第二部分:系统架构 -5. [整体架构设计](#-系统架构设计) -6. [技术栈选型](#-技术栈) -7. [数据流向](#数据流向) -8. [双重通知策略](#双重通知策略) +### 🏗️ 第三部分:系统架构 +9. [整体架构设计](#-系统架构设计) +10. [技术栈选型](#-技术栈) +11. [数据流向](#数据流向) +12. [双重通知策略](#双重通知策略) -### 第三部分:核心组件详解 -9. [组件清单](#-核心组件清单) -10. [NotificationContext](#1-notificationcontext---通知上下文) -11. [NotificationContainer](#2-notificationcontainer---通知容器ui) -12. [SocketService](#3-socketservice---websocket服务) -13. [其他核心组件](#其他核心组件) +### 🧩 第四部分:核心组件详解 +13. [组件清单](#-核心组件清单) +14. [NotificationContext](#1-notificationcontext---通知上下文) +15. [NotificationContainer](#2-notificationcontainer---通知容器ui) +16. [SocketService](#3-socketservice---websocket服务) +17. [其他核心组件](#其他核心组件) -### 第四部分:设计流程 -14. [消息推送完整流程](#-消息推送完整流程) -15. [初始化流程](#1-初始化流程) -16. [消息接收与适配](#2-消息接收与适配) -17. [通知分发策略](#3-通知分发策略) -18. [性能监控埋点](#4-性能监控埋点) +### 🔄 第五部分:设计流程 +18. [消息推送完整流程](#-消息推送完整流程) +19. [初始化流程](#1-初始化流程) +20. [消息接收与适配](#2-消息接收与适配) +21. [通知分发策略](#3-通知分发策略) +22. [性能监控埋点](#4-性能监控埋点) -### 第五部分:测试指南 -19. [测试快速指南](#-测试快速指南) -20. [手动测试清单](#-手动测试清单) -21. [自动化测试](#-自动化测试用例) -22. [测试覆盖率](#测试覆盖率目标) - -### 第六部分:配置与扩展 +### 🎨 第六部分:通知类型与配置 23. [通知类型配置](#-通知类型配置) 24. [优先级配置](#-优先级配置) 25. [环境配置](#环境配置) -26. [后端集成](#-后端集成生产环境) -### 第七部分:故障排查 -27. [常见问题FAQ](#-故障排除) -28. [调试工具](#调试工具) -29. [日志分析](#日志分析) +### ✨ 第七部分:增强功能详解 +26. [智能桌面通知](#-功能-1智能桌面通知) +27. [性能监控](#-功能-2性能监控) +28. [历史记录](#-功能-3历史记录) -### 第八部分:最佳实践 -30. [开发规范](#-开发规范) -31. [性能优化](#-性能优化建议) -32. [安全注意事项](#安全注意事项) +### 🧪 第八部分:测试指南 +29. [测试快速指南](#-测试快速指南) +30. [手动测试清单](#-手动测试清单) +31. [自动化测试](#-自动化测试用例) + +### 🐛 第九部分:故障排查 +32. [常见问题FAQ](#-故障排除) +33. [调试工具](#调试工具) +34. [日志分析](#日志分析) + +### 📏 第十部分:最佳实践 +35. [开发规范](#-开发规范) +36. [性能优化](#-性能优化建议) +37. [安全注意事项](#安全注意事项) ### 附录 -33. [更新日志](#-更新日志) -34. [文件结构](#-文件结构) +38. [后端集成](#-后端集成生产环境) +39. [更新日志](#-更新日志) +40. [文件结构](#-文件结构) --- -## 🆕 最新更新 (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 分钟 @@ -293,29 +273,13 @@ __DEBUG__.clearAll() __DEBUG__.help() ``` -#### **开发环境专用(Mock 模式)** - -仅在开发环境(`npm start`)可用: - -```javascript -// 模拟断线(测试重连机制) -__mockSocket.simulateDisconnection() - -// 查看连接状态 -__mockSocket.isConnected() - -// 手动重连 -__mockSocket.reconnect() - -// 发送测试通知 -__mockSocket.sendTestNotification() -``` - --- -## 📋 系统概述 +## 🚀 第二部分:快速入门(开发者) -本系统是专为**金融资讯场景**设计的实时消息推送系统,支持 Mock 模式(开发)和真实 Socket.IO 模式(生产)。消息以右下角层叠弹窗的形式显示,**新消息在最上方**,符合主流桌面应用的交互习惯。 +### 📋 系统概述 + +本系统是专为**金融资讯场景**设计的实时消息推送系统,基于 Socket.IO 实现 WebSocket 实时通信。消息以右下角层叠弹窗的形式显示,**新消息在最上方**,符合主流桌面应用的交互习惯。 ### 核心特性 @@ -340,7 +304,74 @@ __mockSocket.sendTestNotification() --- -## 🏗️ 系统架构设计 +### 🏃 快速开始 + +#### 1. 启动项目 + +```bash +# 开发环境 +npm start + +# 或使用真实后端 +npm run start:real +``` + +#### 2. 验证连接 + +打开浏览器控制台 (`F12` → `Console`),查看连接状态: + +```javascript +// 查看 Socket 连接状态 +__DEBUG__.socket.getStatus() + +// 预期输出: +// { connected: true, socketId: "...", reconnectAttempts: 0 } +``` + +#### 3. 测试通知 + +等待后端推送通知,或使用后端 API 手动触发(参见[后端集成](#-后端集成生产环境))。 + +--- + +### 💻 代码使用 + +#### 在组件中使用 + +```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 ? '已连接' : '未连接'}

+ +
+ ); +} +``` + +--- + +## 🏗️ 第三部分:系统架构 ### 整体架构图 @@ -353,18 +384,14 @@ graph TB end subgraph "通知系统核心" - C --> F[Socket Service
WebSocket连接] - F --> G{环境判断} - G -->|Mock| H[mockSocketService
模拟服务] - G -->|Real| I[socketService
生产服务] - + C --> F[socketService
Socket.IO连接] C --> J[browserNotificationService
浏览器通知] C --> K[notificationHistoryService
历史记录] C --> L[notificationMetricsService
性能监控] end subgraph "后端服务" - I --> M[Flask Backend
Socket.IO Server] + F --> M[Flask Backend
Socket.IO Server] M --> N[(ClickHouse
事件数据)] M --> O[(MySQL
用户数据)] end @@ -379,7 +406,7 @@ graph TB style F fill:#bfb,stroke:#333,stroke-width:2px ``` -### 技术栈选型 +### 📚 技术栈 #### 前端技术栈 - **React 18.3.1**: 并发渲染、Suspense、自动批处理 @@ -448,14 +475,15 @@ sequenceDiagram --- -## 🧩 核心组件清单 +## 🧩 第四部分:核心组件详解 + +### 📦 核心组件清单 | 组件名 | 类型 | 职责 | 文件路径 | |-------|------|------|---------| | **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` | +| **socketService** | Service Class | WebSocket连接管理 | `src/services/socketService.js` | | **browserNotificationService** | Service Class | 浏览器通知API封装 | `src/services/browserNotificationService.js` | | **notificationHistoryService** | Service Class | 历史记录持久化 | `src/services/notificationHistoryService.js` | | **notificationMetricsService** | Service Class | 性能监控埋点 | `src/services/notificationMetricsService.js` | @@ -528,7 +556,7 @@ const { showFirstFollowGuide(); ``` -**源码位置**: `src/contexts/NotificationContext.js:1-829` +**源码位置**: `src/contexts/NotificationContext.js` --- @@ -590,7 +618,7 @@ const { }); ``` -**源码位置**: `src/components/NotificationContainer/index.js:1-757` +**源码位置**: `src/components/NotificationContainer/index.js` --- @@ -634,7 +662,7 @@ socket.subscribeToEvents({ eventType, importance, onNewEvent }); socket.unsubscribeFromEvents({ eventType }); ``` -**源码位置**: `src/services/socketService.js:1-465` +**源码位置**: `src/services/socketService.js` --- @@ -671,7 +699,7 @@ notification.onclick = () => { }; ``` -**源码位置**: `src/services/browserNotificationService.js:1-209` +**源码位置**: `src/services/browserNotificationService.js` --- @@ -711,7 +739,7 @@ const stats = notificationHistoryService.getStats(); // { total, read, unread, clicked, clickRate, byType, byPriority } ``` -**源码位置**: `src/services/notificationHistoryService.js:1-403` +**源码位置**: `src/services/notificationHistoryService.js` --- @@ -749,11 +777,13 @@ const json = notificationMetricsService.exportToJSON(); const csv = notificationMetricsService.exportToCSV(); ``` -**源码位置**: `src/services/notificationMetricsService.js:1-451` +**源码位置**: `src/services/notificationMetricsService.js` --- -## 🔄 消息推送完整流程 +## 🔄 第五部分:设计流程 + +### 📡 消息推送完整流程 ### 1. 初始化流程 @@ -761,19 +791,16 @@ const csv = notificationMetricsService.exportToCSV(); 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[初始化完成] + C --> D[连接socketService] + D --> E[注册事件监听器] + E --> F[触发connect事件] + F --> G[更新连接状态] + G --> H[检查浏览器通知权限] + H --> I[加载历史记录] + I --> J[初始化完成] ``` -**代码实现** (src/contexts/NotificationContext.js:573-712): +**代码实现** (src/contexts/NotificationContext.js): ```javascript useEffect(() => { // 1. 注册事件监听器 @@ -785,12 +812,7 @@ useEffect(() => { // 2. 连接Socket socket.connect(); - // 3. Mock模式启动自动推送 - if (SOCKET_TYPE === 'MOCK') { - socket.startMockPush(60000, 2); // 60秒推送1-2条 - } - - // 4. 清理函数 + // 3. 清理函数 return () => { socket.off('connect'); socket.off('disconnect'); @@ -859,7 +881,7 @@ const adaptEventToNotification = (event) => { }; ``` -**源码位置**: `src/contexts/NotificationContext.js:336-395` +**源码位置**: `src/contexts/NotificationContext.js` --- @@ -890,7 +912,7 @@ graph TB R --> K ``` -**代码实现** (src/contexts/NotificationContext.js:558-570): +**代码实现** (src/contexts/NotificationContext.js): ```javascript const isPageHidden = document.hidden; @@ -942,363 +964,9 @@ deliveryRate = (totalReceived / totalSent) * 100 --- -## 🧪 测试快速指南 +## 🎨 第六部分:通知类型与配置 -> 📖 **完整测试手册**: [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) @@ -1426,7 +1094,7 @@ describe('通知系统集成测试', () => { --- -## 📊 优先级配置 +### 📊 优先级配置 | 优先级 | 标签 | 边框 | 自动关闭 | 使用场景 | |--------|------|------|---------|----------| @@ -1463,126 +1131,939 @@ export const PRIORITY_CONFIGS = { --- -## 🚀 快速开始 +### 环境配置 -### 1. 启用 Mock 模式 +### API 配置 -在 `.env` 文件中添加: -```bash -REACT_APP_ENABLE_MOCK=true -``` - -### 2. 启动项目 +在 `.env` 文件中配置后端地址: ```bash -npm start +# 开发环境 +REACT_APP_API_URL=http://49.232.185.254:5001 + +# 生产环境 +REACT_APP_API_URL= +# 空字符串表示使用相对路径 ``` -### 3. 测试通知 +### Socket 连接配置 -打开浏览器控制台 (`F12` → `Console`),使用以下命令测试: +Socket 服务会自动读取 `REACT_APP_API_URL` 并连接到后端: -#### **Mock 模式测试**(开发环境) ```javascript -// 发送测试通知 -__mockSocket.sendTestNotification() +// src/services/socketService.js +const API_BASE_URL = getApiBase(); -// 模拟断线重连 -__mockSocket.simulateDisconnection() - -// 查看连接状态 -__mockSocket.isConnected() +this.socket = io(API_BASE_URL, { + transports: ['websocket', 'polling'], + reconnection: false, // 使用自定义重连策略 + timeout: 20000, + withCredentials: true, +}); ``` -#### **通用测试命令** -```javascript -// 查看系统状态 -__DEBUG__.diagnose() - -// 查看连接状态 -__DEBUG__.socket.getStatus() - -// 查看通知权限 -window.browserNotificationService.getPermissionStatus() -``` - -### 4. 自动推送 - -Mock 模式下,系统会自动每 60 秒推送 1-2 条随机金融资讯。 - -查看更多测试命令,请参考本文档开头的 [用户快速指南](#-用户快速指南)。 - --- -## 💻 代码使用 +## ✨ 第七部分:增强功能详解 -### 在组件中使用 +### 🔔 功能 1:智能桌面通知 + +#### 功能说明 + +首次收到重要/紧急通知时,自动请求浏览器通知权限,确保用户不错过关键信息。 + +#### 工作原理 +```javascript +// 在 NotificationContext 中的逻辑 +if (priority === URGENT || priority === IMPORTANT) { + if (browserPermission === 'default' && !hasRequestedPermission) { + // 首次遇到重要通知,自动请求权限 + await requestBrowserPermission(); + setHasRequestedPermission(true); // 避免重复请求 + } +} +``` + +#### 权限状态 + +- **granted**: 已授权,可以发送桌面通知 +- **denied**: 已拒绝,无法发送桌面通知 +- **default**: 未请求,首次重要通知时会自动请求 + +#### 使用示例 + +**自动触发**(推荐) +```javascript +// 无需任何代码,系统自动处理 +// 首次收到重要/紧急通知时会自动弹出权限请求 +``` + +**手动请求** ```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, - }); - }; +function SettingsPage() { + const { requestBrowserPermission, browserPermission } = useNotification(); return (
-

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

- +

当前状态: {browserPermission}

+
); } ``` +#### 通知分发策略 + +| 优先级 | 页面在前台 | 页面在后台 | +|-------|----------|----------| +| 紧急 | 桌面通知 + 网页通知 | 桌面通知 + 网页通知 | +| 重要 | 网页通知 | 桌面通知 | +| 普通 | 网页通知 | 网页通知 | + --- -## 🔧 环境配置 +### 📊 功能 2:性能监控 -### 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.*` | 无 | +追踪通知推送的各项指标,包括: +- **到达率**: 发送 vs 接收 +- **点击率**: 点击 vs 接收 +- **响应时间**: 收到通知到点击的平均时间 +- **类型分布**: 各类型通知的数量和效果 +- **时段分布**: 每小时推送量 -### 修改推送频率 +#### API 参考 + +##### 获取汇总统计 -在 `src/services/mockSocketService.js` 中: ```javascript -// 默认: 60秒推送1-2条 -socket.startMockPush(60000, 2); +import { notificationMetricsService } from 'services/notificationMetricsService'; -// 修改为: 30秒推送1条 -socket.startMockPush(30000, 1); +const summary = notificationMetricsService.getSummary(); +console.log(summary); +/* 输出: +{ + totalSent: 100, + totalReceived: 98, + totalClicked: 45, + totalDismissed: 53, + avgResponseTime: 5200, // 毫秒 + clickRate: '45.92', // 百分比 + deliveryRate: '98.00' // 百分比 +} +*/ +``` -// 修改为: 5秒推送3条(压力测试) -socket.startMockPush(5000, 3); +##### 获取按类型统计 + +```javascript +const byType = notificationMetricsService.getByType(); +console.log(byType); +/* 输出: +{ + announcement: { sent: 20, received: 20, clicked: 15, dismissed: 5, clickRate: '75.00' }, + stock_alert: { sent: 30, received: 30, clicked: 20, dismissed: 10, clickRate: '66.67' }, + event_alert: { sent: 40, received: 38, clicked: 10, dismissed: 28, clickRate: '26.32' }, + analysis_report: { sent: 10, received: 10, clicked: 0, dismissed: 10, clickRate: '0.00' } +} +*/ +``` + +##### 获取每日数据 + +```javascript +const dailyData = notificationMetricsService.getDailyData(7); // 最近 7 天 +console.log(dailyData); +/* 输出: +[ + { date: '2025-01-15', sent: 15, received: 14, clicked: 6, dismissed: 8, clickRate: '42.86' }, + { date: '2025-01-16', sent: 20, received: 20, clicked: 10, dismissed: 10, clickRate: '50.00' }, + ... +] +*/ +``` + +##### 导出数据 + +```javascript +// 导出为 JSON +const json = notificationMetricsService.exportToJSON(); +console.log(json); + +// 导出为 CSV +const csv = notificationMetricsService.exportToCSV(); +console.log(csv); +``` + +#### 在控制台查看实时指标 + +打开浏览器控制台,执行: + +```javascript +// 查看汇总 +import { notificationMetricsService } from './services/notificationMetricsService.js'; +console.table(notificationMetricsService.getSummary()); + +// 查看按类型分布 +console.table(notificationMetricsService.getByType()); + +// 查看最近 7 天数据 +console.table(notificationMetricsService.getDailyData(7)); ``` --- -## 🌐 后端集成(生产环境) +### 📜 功能 3:历史记录 -### Flask-SocketIO 配置 +#### 功能说明 + +持久化存储所有接收到的通知,支持: +- 查询和筛选 +- 搜索关键词 +- 标记已读/已点击 +- 批量删除 +- 导出(JSON/CSV) + +#### API 参考 + +##### 获取历史记录(支持筛选和分页) + +```javascript +import { notificationHistoryService } from 'services/notificationHistoryService'; + +const result = notificationHistoryService.getHistory({ + type: 'event_alert', // 可选:筛选类型 + priority: 'urgent', // 可选:筛选优先级 + readStatus: 'unread', // 可选:'read' | 'unread' | 'all' + startDate: Date.now() - 7 * 24 * 60 * 60 * 1000, // 可选:开始日期 + endDate: Date.now(), // 可选:结束日期 + page: 1, // 页码 + pageSize: 20, // 每页数量 +}); + +console.log(result); +/* 输出: +{ + records: [...], // 当前页的记录 + total: 150, // 总记录数 + page: 1, // 当前页 + pageSize: 20, // 每页数量 + totalPages: 8 // 总页数 +} +*/ +``` + +##### 搜索历史记录 + +```javascript +const results = notificationHistoryService.searchHistory('降准'); +console.log(results); // 返回标题/内容中包含"降准"的所有记录 +``` + +##### 标记已读/已点击 + +```javascript +// 标记已读 +notificationHistoryService.markAsRead('notification_id'); + +// 标记已点击 +notificationHistoryService.markAsClicked('notification_id'); +``` + +##### 删除记录 + +```javascript +// 删除单条 +notificationHistoryService.deleteRecord('notification_id'); + +// 批量删除 +notificationHistoryService.deleteRecords(['id1', 'id2', 'id3']); + +// 清空所有 +notificationHistoryService.clearHistory(); +``` + +##### 获取统计数据 + +```javascript +const stats = notificationHistoryService.getStats(); +console.log(stats); +/* 输出: +{ + total: 500, // 总记录数 + read: 320, // 已读数 + unread: 180, // 未读数 + clicked: 150, // 已点击数 + clickRate: '30.00', // 点击率 + byType: { // 按类型统计 + announcement: 100, + stock_alert: 150, + event_alert: 200, + analysis_report: 50 + }, + byPriority: { // 按优先级统计 + urgent: 50, + important: 200, + normal: 250 + } +} +*/ +``` + +##### 导出历史记录 + +```javascript +// 导出为 JSON 字符串 +const json = notificationHistoryService.exportToJSON({ + type: 'event_alert' // 可选:只导出特定类型 +}); + +// 导出为 CSV 字符串 +const csv = notificationHistoryService.exportToCSV(); + +// 直接下载 JSON 文件 +notificationHistoryService.downloadJSON(); + +// 直接下载 CSV 文件 +notificationHistoryService.downloadCSV(); +``` + +#### 数据结构 + +每条历史记录包含: + +```javascript +{ + id: 'notif_123', // 通知 ID + notification: { // 完整通知对象 + type: 'event_alert', + priority: 'urgent', + title: '...', + content: '...', + ... + }, + receivedAt: 1737459600000, // 接收时间戳 + readAt: 1737459650000, // 已读时间戳(null 表示未读) + clickedAt: null, // 已点击时间戳(null 表示未点击) +} +``` + +#### 存储限制 + +- **最大数量**: 500 条(超过后自动删除最旧的) +- **存储位置**: localStorage +- **容量估算**: 约 2-5MB(取决于通知内容长度) + +--- + +## 🧪 第八部分:测试指南 + +### 🧪 测试快速指南 + +#### 开发环境测试 + +```bash +# 启动开发服务器 +npm start +``` + +**关键检查**: +```javascript +// 1. 验证连接 +__DEBUG__.socket.getStatus() +// 预期: { connected: true, socketId: "..." } + +// 2. 系统诊断 +__DEBUG__.diagnose() +``` + +#### 生产环境测试 + +**访问**: https://valuefrontier.cn + +```javascript +// 一键诊断 +__DEBUG__.diagnose() + +// 导出报告 +__DEBUG__.exportAll() +``` + +#### 调试工具速查 + +| 工具 | 适用环境 | 主要功能 | +|------|---------|---------| +| `__DEBUG__.socket` | 所有环境 | 连接状态、配置检查、手动重连 | +| `__DEBUG__.api` | 所有环境 | API 日志、错误追踪 | +| `__DEBUG__` | 所有环境 | 完整诊断、导出报告、性能监控 | + +**常用命令**: +```javascript +// 完整诊断 +__DEBUG__.diagnose() + +// 查看帮助 +__DEBUG__.help() + +// 查看连接状态 +__DEBUG__.socket.getStatus() + +// 手动重连 +__DEBUG__.socket.reconnect() +``` + +--- + +### ✅ 手动测试清单 + +#### 功能测试 + +##### 1. 通知显示测试 +- [ ] 公告通知显示正确(蓝色系、图标、内容) +- [ ] 股票上涨通知显示为红色系(+图标、红边框) +- [ ] 股票下跌通知显示为绿色系(-图标、绿边框) +- [ ] 事件动向通知显示正确(橙色系) +- [ ] 分析报告(人工)显示正确(紫色系、作者信息) +- [ ] 分析报告(AI)显示AI徽章 + +##### 2. 优先级测试 +- [ ] 紧急通知:红色标签、粗边框、脉冲动画、不自动关闭 +- [ ] 重要通知:橙色标签、中等边框、30秒自动关闭 +- [ ] 普通通知:无标签、细边框、15秒自动关闭 + +##### 3. 交互测试 +- [ ] 点击可点击通知跳转到正确页面 +- [ ] 点击不可点击通知无反应 +- [ ] 点击关闭按钮关闭通知 +- [ ] 音效开关正常工作 +- [ ] 清空全部按钮清空所有通知 + +##### 4. 折叠展开测试 +- [ ] 通知数 ≤ 3 时不显示展开按钮 +- [ ] 通知数 > 3 时显示"还有X条通知"按钮 +- [ ] 点击展开显示所有通知 +- [ ] 点击收起恢复显示前3条 + +##### 5. 队列管理测试 +- [ ] 最多显示15条历史通知 +- [ ] 超过15条自动移除最旧的 +- [ ] 新通知在最上方 +- [ ] z-index层级正确(最新最高) + +#### 浏览器通知测试 + +##### 6. 权限管理测试 +- [ ] 首次访问显示权限状态为"default" +- [ ] 点击请求权限按钮弹出浏览器授权对话框 +- [ ] 授权后状态变为"granted" +- [ ] 拒绝后状态变为"denied",显示设置指引 + +##### 7. 浏览器通知显示测试 +- [ ] 权限授予后,切换到其他标签页,收到通知时显示系统通知 +- [ ] 点击浏览器通知聚焦窗口并跳转到详情页 +- [ ] 普通通知8秒后自动关闭 +- [ ] 紧急通知需手动关闭 + +#### Socket连接测试 + +##### 8. 连接测试 +- [ ] 启动后显示"Connected"状态 +- [ ] 后端推送消息能正确接收 +- [ ] 断线后自动重连(1分钟→2分钟→4分钟) +- [ ] 网络恢复时自动重连 +- [ ] 标签页重新聚焦时自动重连 +- [ ] 重连成功后显示"✓ 已重新连接" 2秒后消失 + +#### UI测试 + +##### 9. 响应式测试 +- [ ] 移动端(< 480px):宽度 = 屏宽-32px,精简显示 +- [ ] 小屏(480-768px):宽度 = 360px +- [ ] 平板(768-992px):宽度 = 380px +- [ ] 桌面(> 992px):宽度 = 400px + +##### 10. 动画测试 +- [ ] 新通知从底部向上滑入(浮起效果) +- [ ] 关闭通知向右滑出消失 +- [ ] 紧急通知边框脉冲动画流畅 +- [ ] 折叠/展开动画流畅无卡顿 + +##### 11. 无障碍测试 +- [ ] Tab键可以聚焦到可点击通知 +- [ ] 聚焦时显示蓝色轮廓线 +- [ ] Enter/Space键可以打开详情 +- [ ] 关闭按钮可键盘操作(Tab + Enter) + +#### 性能测试 + +##### 12. 性能指标测试 +- [ ] 发送10条通知,打开控制台查看性能指标 +- [ ] 点击5条通知,检查点击率计算正确 +- [ ] 检查平均响应时间计算正确 +- [ ] 导出JSON/CSV文件正常 + +##### 13. 历史记录测试 +- [ ] 收到通知后自动保存到历史记录 +- [ ] 筛选功能正常(类型、优先级、日期范围、阅读状态) +- [ ] 搜索功能返回正确结果 +- [ ] 分页功能正常 +- [ ] 统计数据准确 +- [ ] 导出JSON/CSV正常 + +#### 跨浏览器测试 + +##### 14. 兼容性测试 +- [ ] Chrome: 所有功能正常 +- [ ] Edge: 所有功能正常 +- [ ] Firefox: 所有功能正常 +- [ ] Safari: 所有功能正常(注意浏览器通知权限) +- [ ] 移动端Chrome: 响应式布局正确 +- [ ] 移动端Safari: 响应式布局正确 + +--- + +### 🧪 自动化测试用例 + +#### 单元测试 + +##### 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); + }); +}); +``` + +--- + +## 🐛 第九部分:故障排查 + +### 🔧 故障排除 + +#### 问题 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 + +// 完整诊断 +__DEBUG__.diagnose(); +``` + +#### 问题 5: Socket 连接失败 + +**解决方案**: +1. 检查后端 Flask-SocketIO 是否正确运行 +2. 确认 CORS 配置正确 +3. 检查 `src/utils/apiConfig.js` 中的 API 地址 +4. 查看控制台日志确认错误信息 + +**使用调试工具**: +```javascript +// 查看连接状态 +__DEBUG__.socket.getStatus() + +// 查看错误日志 +__DEBUG__.socket.getLogs() + +// 手动重连 +__DEBUG__.socket.reconnect() +``` + +--- + +### 调试工具 + +#### window.__DEBUG__ - 主调试工具 + +**可用命令**: +```javascript +// 系统诊断 +__DEBUG__.diagnose() // 检查所有系统状态 +__DEBUG__.printStats() // 打印所有统计信息 +__DEBUG__.performance() // 性能监控 + +// 日志管理 +__DEBUG__.exportAll() // 导出所有日志 +__DEBUG__.clearAll() // 清空所有日志 + +// 帮助信息 +__DEBUG__.help() // 显示帮助信息 +``` + +#### window.__DEBUG__.socket - Socket 调试 + +**可用命令**: +```javascript +// 连接管理 +__DEBUG__.socket.getStatus() // 获取连接状态 +__DEBUG__.socket.reconnect() // 手动重连 + +// 日志管理 +__DEBUG__.socket.getLogs() // 获取所有 Socket 日志 +__DEBUG__.socket.clearLogs() // 清空日志 +__DEBUG__.socket.exportLogs() // 导出日志 +``` + +#### window.__DEBUG__.api - API 调试 + +**可用命令**: +```javascript +// 日志查询 +__DEBUG__.api.getLogs() // 获取所有 API 日志 +__DEBUG__.api.getRecentErrors() // 获取最近的错误 + +// 测试请求 +__DEBUG__.api.testRequest('GET', '/api/endpoint') + +// 日志管理 +__DEBUG__.api.exportLogs() // 导出日志 +__DEBUG__.api.clearLogs() // 清空日志 +``` + +--- + +### 日志分析 + +#### 正常日志流程 + +``` +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. 使用控制台命令测试 +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 秒 +- 避免短时间内大量推送造成用户困扰 + +#### 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; +``` + +--- + +### 🔒 安全注意事项 + +#### 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 +``` + +--- + +## 附录 + +### 🌐 后端集成(生产环境) + +#### Flask-SocketIO 配置 ```python from flask_socketio import SocketIO, emit @@ -1609,7 +2090,7 @@ if __name__ == '__main__': socketio.run(app, host='0.0.0.0', port=5001) ``` -### 后端推送示例 +#### 后端推送示例 ```python import time @@ -1636,474 +2117,44 @@ socketio.emit('new_event', { --- -## 🐛 故障排除 +### 📝 更新日志 -### 问题 1: 通知不显示 +#### v3.0.0 (2025-11-17) - 文档合并与更新 +- ✅ 合并 ENHANCED_FEATURES_GUIDE.md 内容 +- ✅ 移除过时的 Mock 模式引用 +- ✅ 更新所有调试工具引用(统一使用 `window.__DEBUG__`) +- ✅ 更新代码示例与最新代码一致 +- ✅ 完善增强功能文档(智能桌面通知、性能监控、历史记录) +- ✅ 更新测试指南 -**检查项**: -1. 确认 `NotificationProvider` 已包裹应用 -2. 检查浏览器控制台是否有错误 -3. 确认 socket 连接状态(使用 `__DEBUG__.socket.getStatus()`) -4. 检查通知数据格式是否正确 +#### v2.11.0 (2025-01-07) - Socket 连接优化 +- ✅ 指数退避重连策略 +- ✅ 连接状态横幅优化 +- ✅ 手动关闭状态持久化 -### 问题 2: 股票动向颜色不对 +#### v2.10.0 (2025-01-06) - 按钮加载态 +- ✅ 点击"查看详情"显示 loading spinner +- ✅ 防重复点击机制 -**解决方案**: -1. 检查 `extra.priceChange` 字段是否存在 -2. 确认格式为 "+5.2%" 或 "-3.8%"(包含符号) -3. 确认使用了 `NOTIFICATION_TYPES.STOCK_ALERT` 类型 +#### v2.9.0 (2025-01-05) - 头部简化 +- ✅ AI 和预测标识移到底部 -### 问题 3: AI 徽章不显示 +#### v2.8.0 (2025-01-04) - 无障碍支持 +- ✅ 完整的 ARIA 属性 +- ✅ 键盘导航支持 -**解决方案**: -1. 检查 `isAIGenerated` 字段是否为 `true` -2. 确认字段名拼写正确(驼峰命名) +#### v2.7.0 (2025-01-03) - 智能动画 +- ✅ 只对新增通知应用动画 +- ✅ 性能提升 90%+ -### 问题 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 +#### v2.0.0 (2024-12-27) - 金融资讯专业版 +- ✅ 4种金融资讯通知类型 +- ✅ 3级优先级系统 +- ✅ 智能元数据、动态配色 --- -### 问题 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/ @@ -2112,8 +2163,7 @@ src/ ├── services/ │ ├── socket/ │ │ └── index.js # Socket 服务统一导出 -│ ├── socketService.js # 真实 Socket.IO 服务 -│ ├── mockSocketService.js # Mock Socket 服务 +│ ├── socketService.js # Socket.IO 服务 │ ├── browserNotificationService.js # 浏览器通知服务 │ ├── notificationHistoryService.js # 历史记录服务 │ └── notificationMetricsService.js # 性能监控服务 @@ -2124,6 +2174,10 @@ src/ │ └── index.js # 通知容器组件 ├── hooks/ │ └── useEventNotifications.js # 事件订阅 Hook +├── devtools/ +│ ├── index.js # 调试工具统一入口 +│ ├── socketDebugger.js # Socket 调试工具 +│ └── apiDebugger.js # API 调试工具 └── assets/ └── sounds/ └── notification.wav # 通知音效 @@ -2131,74 +2185,10 @@ src/ --- -## 📝 更新日志 - -### 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()` 查看所有可用命令 - **控制台日志**: 所有操作都有详细日志