主要更新: - 合并 ENHANCED_FEATURES_GUIDE.md 到 NOTIFICATION_SYSTEM.md - 移除过时的 Mock 模式和测试工具引用 - 更新所有调试工具为 window.__DEBUG__ - 完善增强功能文档(智能桌面通知、性能监控、历史记录) - 重新组织文档结构为 10 个清晰的部分 - 更新所有代码示例与最新代码保持一致 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2200 lines
56 KiB
Markdown
2200 lines
56 KiB
Markdown
# 实时消息推送系统 - 完整技术文档
|
||
|
||
> **版本**: v3.0.0
|
||
> **更新日期**: 2025-11-17
|
||
> **文档类型**: 用户指南 + 完整技术规格
|
||
>
|
||
> 🎯 **重要说明**:
|
||
> - 本文档已合并原 `ENHANCED_FEATURES_GUIDE.md` 的内容
|
||
> - 所有 API 引用和代码示例已更新至最新版本
|
||
> - 调试工具统一使用 `window.__DEBUG__`
|
||
|
||
---
|
||
|
||
## 📑 目录
|
||
|
||
### 👤 第一部分:用户快速指南
|
||
1. [连接状态查看](#-连接状态查看)
|
||
2. [手动操作指南](#-手动操作指南)
|
||
3. [常见问题解决](#-常见问题解决)
|
||
4. [可用调试命令](#-可用调试命令)
|
||
|
||
### 🚀 第二部分:快速入门(开发者)
|
||
5. [系统简介](#-系统概述)
|
||
6. [核心特性](#核心特性)
|
||
7. [5分钟快速开始](#-快速开始)
|
||
8. [基础使用示例](#-代码使用)
|
||
|
||
### 🏗️ 第三部分:系统架构
|
||
9. [整体架构设计](#-系统架构设计)
|
||
10. [技术栈选型](#-技术栈)
|
||
11. [数据流向](#数据流向)
|
||
12. [双重通知策略](#双重通知策略)
|
||
|
||
### 🧩 第四部分:核心组件详解
|
||
13. [组件清单](#-核心组件清单)
|
||
14. [NotificationContext](#1-notificationcontext---通知上下文)
|
||
15. [NotificationContainer](#2-notificationcontainer---通知容器ui)
|
||
16. [SocketService](#3-socketservice---websocket服务)
|
||
17. [其他核心组件](#其他核心组件)
|
||
|
||
### 🔄 第五部分:设计流程
|
||
18. [消息推送完整流程](#-消息推送完整流程)
|
||
19. [初始化流程](#1-初始化流程)
|
||
20. [消息接收与适配](#2-消息接收与适配)
|
||
21. [通知分发策略](#3-通知分发策略)
|
||
22. [性能监控埋点](#4-性能监控埋点)
|
||
|
||
### 🎨 第六部分:通知类型与配置
|
||
23. [通知类型配置](#-通知类型配置)
|
||
24. [优先级配置](#-优先级配置)
|
||
25. [环境配置](#环境配置)
|
||
|
||
### ✨ 第七部分:增强功能详解
|
||
26. [智能桌面通知](#-功能-1智能桌面通知)
|
||
27. [性能监控](#-功能-2性能监控)
|
||
28. [历史记录](#-功能-3历史记录)
|
||
|
||
### 🧪 第八部分:测试指南
|
||
29. [测试快速指南](#-测试快速指南)
|
||
30. [手动测试清单](#-手动测试清单)
|
||
31. [自动化测试](#-自动化测试用例)
|
||
|
||
### 🐛 第九部分:故障排查
|
||
32. [常见问题FAQ](#-故障排除)
|
||
33. [调试工具](#调试工具)
|
||
34. [日志分析](#日志分析)
|
||
|
||
### 📏 第十部分:最佳实践
|
||
35. [开发规范](#-开发规范)
|
||
36. [性能优化](#-性能优化建议)
|
||
37. [安全注意事项](#安全注意事项)
|
||
|
||
### 附录
|
||
38. [后端集成](#-后端集成生产环境)
|
||
39. [更新日志](#-更新日志)
|
||
40. [文件结构](#-文件结构)
|
||
|
||
---
|
||
|
||
## 👤 第一部分:用户快速指南
|
||
|
||
> **适用人群**: 普通用户、测试人员、客服人员
|
||
> **阅读时间**: 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()
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 第二部分:快速入门(开发者)
|
||
|
||
### 📋 系统概述
|
||
|
||
本系统是专为**金融资讯场景**设计的实时消息推送系统,基于 Socket.IO 实现 WebSocket 实时通信。消息以右下角层叠弹窗的形式显示,**新消息在最上方**,符合主流桌面应用的交互习惯。
|
||
|
||
### 核心特性
|
||
|
||
- **双通知系统**:网页通知 + 浏览器原生通知,智能分发
|
||
- **4 种通知类型**:公告通知、股票动向、事件动向、分析报告
|
||
- **3 级优先级**:紧急(不关闭)、重要(30秒)、普通(15秒)
|
||
- **智能折叠**:最多显示3条,超过显示展开按钮
|
||
- **智能配色**:股票动向根据涨跌自动变色(涨红 🔴 / 跌绿 🟢)
|
||
- **丰富元数据**:发布时间、作者、AI 标识
|
||
- **点击跳转**:可配置点击跳转链接
|
||
- **队列管理**:最多保留 15 条历史,可展开查看
|
||
- **性能监控**:自动追踪到达率、点击率、响应时间
|
||
- **历史记录**:持久化存储、筛选、搜索、导出功能
|
||
|
||
### 技术亮点
|
||
|
||
- **WebSocket 实时通信**:Socket.IO + 指数退避重连策略
|
||
- **双重去重机制**:Socket层去重 + 显示层去重
|
||
- **智能权限引导**:渐进式权限请求,避免打扰用户
|
||
- **无障碍支持**:完整的 ARIA 属性,键盘导航
|
||
- **性能优化**:React.memo、useMemo、useCallback,GPU 加速动画
|
||
|
||
---
|
||
|
||
### 🏃 快速开始
|
||
|
||
#### 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 (
|
||
<div>
|
||
<p>连接状态: {isConnected ? '已连接' : '未连接'}</p>
|
||
<button onClick={handleAnnouncement}>发送公告</button>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🏗️ 第三部分:系统架构
|
||
|
||
### 整体架构图
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "前端应用"
|
||
A[App.js] --> B[AppProviders]
|
||
B --> C[NotificationProvider<br/>NotificationContext]
|
||
C --> D[NotificationContainer<br/>通知容器UI]
|
||
end
|
||
|
||
subgraph "通知系统核心"
|
||
C --> F[socketService<br/>Socket.IO连接]
|
||
C --> J[browserNotificationService<br/>浏览器通知]
|
||
C --> K[notificationHistoryService<br/>历史记录]
|
||
C --> L[notificationMetricsService<br/>性能监控]
|
||
end
|
||
|
||
subgraph "后端服务"
|
||
F --> M[Flask Backend<br/>Socket.IO Server]
|
||
M --> N[(ClickHouse<br/>事件数据)]
|
||
M --> O[(MySQL<br/>用户数据)]
|
||
end
|
||
|
||
subgraph "数据存储"
|
||
K --> P[(localStorage<br/>历史记录)]
|
||
L --> Q[(localStorage<br/>性能指标)]
|
||
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` |
|
||
| **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`
|
||
|
||
---
|
||
|
||
### 2. NotificationContainer - 通知容器UI
|
||
|
||
**职责**:通知的可视化展示,处理动画、交互、无障碍。
|
||
|
||
**核心功能**:
|
||
- 通知列表渲染(最多显示3条)
|
||
- 折叠/展开功能
|
||
- 通知动画(Framer Motion)
|
||
- 点击跳转
|
||
- 无障碍支持(ARIA、键盘导航)
|
||
|
||
**主要组件**:
|
||
```jsx
|
||
<NotificationContainer>
|
||
<VStack spacing={3}>
|
||
{/* 可见通知 */}
|
||
{visibleNotifications.map(notification => (
|
||
<NotificationItem
|
||
notification={notification}
|
||
onClose={removeNotification}
|
||
isNewest={index === 0} // 最新通知特殊样式
|
||
/>
|
||
))}
|
||
|
||
{/* 折叠按钮 */}
|
||
{hasMore && (
|
||
<Button onClick={() => setIsExpanded(!isExpanded)}>
|
||
{isExpanded ? '收起' : `还有 ${hiddenCount} 条通知`}
|
||
</Button>
|
||
)}
|
||
</VStack>
|
||
</NotificationContainer>
|
||
```
|
||
|
||
**关键实现细节**:
|
||
|
||
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`
|
||
|
||
---
|
||
|
||
### 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`
|
||
|
||
---
|
||
|
||
### 其他核心组件
|
||
|
||
#### 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`
|
||
|
||
---
|
||
|
||
#### 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`
|
||
|
||
---
|
||
|
||
#### 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. 初始化流程
|
||
|
||
```mermaid
|
||
graph LR
|
||
A[App启动] --> B[AppProviders挂载]
|
||
B --> C[NotificationProvider初始化]
|
||
C --> D[连接socketService]
|
||
D --> E[注册事件监听器]
|
||
E --> F[触发connect事件]
|
||
F --> G[更新连接状态]
|
||
G --> H[检查浏览器通知权限]
|
||
H --> I[加载历史记录]
|
||
I --> J[初始化完成]
|
||
```
|
||
|
||
**代码实现** (src/contexts/NotificationContext.js):
|
||
```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. 清理函数
|
||
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[映射重要性<br/>S/A→urgent/important<br/>B/C→normal]
|
||
H --> I[构建通知对象]
|
||
I --> J[生成跳转链接<br/>/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`
|
||
|
||
---
|
||
|
||
### 3. 通知分发策略
|
||
|
||
```mermaid
|
||
graph TB
|
||
A[收到新通知] --> B{检查浏览器权限}
|
||
B -->|default| C[自动请求权限]
|
||
B -->|denied| D[显示设置指引]
|
||
B -->|granted| E{检查页面可见性}
|
||
|
||
E -->|document.hidden=true<br/>页面在后台| F[发送浏览器通知]
|
||
E -->|document.hidden=false<br/>页面在前台| 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):
|
||
```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 天数据
|
||
- 自动清理过期数据
|
||
|
||
---
|
||
|
||
## 🎨 第六部分:通知类型与配置
|
||
|
||
### 🎯 通知类型配置
|
||
|
||
### 类型 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,
|
||
},
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 环境配置
|
||
|
||
### API 配置
|
||
|
||
在 `.env` 文件中配置后端地址:
|
||
|
||
```bash
|
||
# 开发环境
|
||
REACT_APP_API_URL=http://49.232.185.254:5001
|
||
|
||
# 生产环境
|
||
REACT_APP_API_URL=
|
||
# 空字符串表示使用相对路径
|
||
```
|
||
|
||
### Socket 连接配置
|
||
|
||
Socket 服务会自动读取 `REACT_APP_API_URL` 并连接到后端:
|
||
|
||
```javascript
|
||
// src/services/socketService.js
|
||
const API_BASE_URL = getApiBase();
|
||
|
||
this.socket = io(API_BASE_URL, {
|
||
transports: ['websocket', 'polling'],
|
||
reconnection: false, // 使用自定义重连策略
|
||
timeout: 20000,
|
||
withCredentials: true,
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## ✨ 第七部分:增强功能详解
|
||
|
||
### 🔔 功能 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 (
|
||
<div>
|
||
<p>当前状态: {browserPermission}</p>
|
||
<button onClick={requestBrowserPermission}>
|
||
开启桌面通知
|
||
</button>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
#### 通知分发策略
|
||
|
||
| 优先级 | 页面在前台 | 页面在后台 |
|
||
|-------|----------|----------|
|
||
| 紧急 | 桌面通知 + 网页通知 | 桌面通知 + 网页通知 |
|
||
| 重要 | 网页通知 | 桌面通知 |
|
||
| 普通 | 网页通知 | 网页通知 |
|
||
|
||
---
|
||
|
||
### 📊 功能 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 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:历史记录
|
||
|
||
#### 功能说明
|
||
|
||
持久化存储所有接收到的通知,支持:
|
||
- 查询和筛选
|
||
- 搜索关键词
|
||
- 标记已读/已点击
|
||
- 批量删除
|
||
- 导出(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(
|
||
<NotificationProvider>
|
||
<NotificationContainer />
|
||
</NotificationProvider>
|
||
);
|
||
|
||
// 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 (
|
||
<Box onClick={handleClick}>
|
||
{/* 自定义样式 */}
|
||
</Box>
|
||
);
|
||
}
|
||
```
|
||
|
||
#### 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 自动转义
|
||
<Text>{notification.title}</Text>
|
||
|
||
// ❌ 错误:使用 dangerouslySetInnerHTML
|
||
<Text dangerouslySetInnerHTML={{ __html: notification.title }} />
|
||
```
|
||
|
||
#### 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
|
||
|
||
# 初始化 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)
|
||
```
|
||
|
||
---
|
||
|
||
### 📝 更新日志
|
||
|
||
#### v3.0.0 (2025-11-17) - 文档合并与更新
|
||
- ✅ 合并 ENHANCED_FEATURES_GUIDE.md 内容
|
||
- ✅ 移除过时的 Mock 模式引用
|
||
- ✅ 更新所有调试工具引用(统一使用 `window.__DEBUG__`)
|
||
- ✅ 更新代码示例与最新代码一致
|
||
- ✅ 完善增强功能文档(智能桌面通知、性能监控、历史记录)
|
||
- ✅ 更新测试指南
|
||
|
||
#### v2.11.0 (2025-01-07) - Socket 连接优化
|
||
- ✅ 指数退避重连策略
|
||
- ✅ 连接状态横幅优化
|
||
- ✅ 手动关闭状态持久化
|
||
|
||
#### 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.0.0 (2024-12-27) - 金融资讯专业版
|
||
- ✅ 4种金融资讯通知类型
|
||
- ✅ 3级优先级系统
|
||
- ✅ 智能元数据、动态配色
|
||
|
||
---
|
||
|
||
### 📁 文件结构
|
||
|
||
```
|
||
src/
|
||
├── constants/
|
||
│ └── notificationTypes.js # 通知类型定义和常量
|
||
├── services/
|
||
│ ├── socket/
|
||
│ │ └── index.js # Socket 服务统一导出
|
||
│ ├── socketService.js # Socket.IO 服务
|
||
│ ├── browserNotificationService.js # 浏览器通知服务
|
||
│ ├── notificationHistoryService.js # 历史记录服务
|
||
│ └── notificationMetricsService.js # 性能监控服务
|
||
├── contexts/
|
||
│ └── NotificationContext.js # 通知上下文管理
|
||
├── components/
|
||
│ └── NotificationContainer/
|
||
│ └── index.js # 通知容器组件
|
||
├── hooks/
|
||
│ └── useEventNotifications.js # 事件订阅 Hook
|
||
├── devtools/
|
||
│ ├── index.js # 调试工具统一入口
|
||
│ ├── socketDebugger.js # Socket 调试工具
|
||
│ └── apiDebugger.js # API 调试工具
|
||
└── assets/
|
||
└── sounds/
|
||
└── notification.wav # 通知音效
|
||
```
|
||
|
||
---
|
||
|
||
## 📞 支持
|
||
|
||
如有问题,请查看:
|
||
- **用户快速指南**: 本文档开头的[用户快速指南](#-第一部分用户快速指南)
|
||
- **项目文档**: `CLAUDE.md`
|
||
- **调试命令**: 浏览器控制台使用 `__DEBUG__.help()` 查看所有可用命令
|
||
- **控制台日志**: 所有操作都有详细日志
|
||
- **类型定义**: `src/constants/notificationTypes.js`
|
||
|
||
---
|
||
|
||
**祝你使用愉快!** 🎉
|