- 删除 mockSocketService.js(916 行) - 简化 socket/index.js(365 行 → 19 行) - 移除 Mock/Real 服务选择逻辑 - 移除 SOCKET_TYPE 和 useMock 标识 - 移除全局调试 API(迁移到 src/devtools/) - 更新相关文档说明 Mock Socket 已移除 现在仅使用真实 Socket.IO 服务连接后端,代码更简洁清晰。 🔧 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1974 lines
55 KiB
Markdown
1974 lines
55 KiB
Markdown
# 实时消息推送系统 - 完整技术文档
|
||
|
||
> **版本**: v2.11.0
|
||
> **更新日期**: 2025-01-10
|
||
> **文档类型**: 快速入门 + 完整技术规格
|
||
>
|
||
> ⚠️ **重要更新**: Mock Socket 已移除(2025-01-10),文档中关于 `mockSocketService` 的内容仅供历史参考。
|
||
|
||
---
|
||
|
||
## 📑 目录
|
||
|
||
### 第一部分:快速入门
|
||
1. [系统简介](#-系统概述)
|
||
2. [核心特性](#核心特性)
|
||
3. [5分钟快速开始](#-快速开始)
|
||
4. [基础使用示例](#-代码使用)
|
||
|
||
### 第二部分:系统架构
|
||
5. [整体架构设计](#-系统架构设计)
|
||
6. [技术栈选型](#-技术栈)
|
||
7. [数据流向](#数据流向)
|
||
8. [双重通知策略](#双重通知策略)
|
||
|
||
### 第三部分:核心组件详解
|
||
9. [组件清单](#-核心组件清单)
|
||
10. [NotificationContext](#1-notificationcontext---通知上下文)
|
||
11. [NotificationContainer](#2-notificationcontainer---通知容器ui)
|
||
12. [SocketService](#3-socketservice---websocket服务)
|
||
13. [其他核心组件](#其他核心组件)
|
||
|
||
### 第四部分:设计流程
|
||
14. [消息推送完整流程](#-消息推送完整流程)
|
||
15. [初始化流程](#1-初始化流程)
|
||
16. [消息接收与适配](#2-消息接收与适配)
|
||
17. [通知分发策略](#3-通知分发策略)
|
||
18. [性能监控埋点](#4-性能监控埋点)
|
||
|
||
### 第五部分:测试指南
|
||
19. [测试快速指南](#-测试快速指南)
|
||
20. [手动测试清单](#-手动测试清单)
|
||
21. [自动化测试](#-自动化测试用例)
|
||
22. [测试覆盖率](#测试覆盖率目标)
|
||
|
||
### 第六部分:配置与扩展
|
||
23. [通知类型配置](#-通知类型配置)
|
||
24. [优先级配置](#-优先级配置)
|
||
25. [环境配置](#环境配置)
|
||
26. [后端集成](#-后端集成生产环境)
|
||
|
||
### 第七部分:故障排查
|
||
27. [常见问题FAQ](#-故障排除)
|
||
28. [调试工具](#调试工具)
|
||
29. [日志分析](#日志分析)
|
||
|
||
### 第八部分:最佳实践
|
||
30. [开发规范](#-开发规范)
|
||
31. [性能优化](#-性能优化建议)
|
||
32. [安全注意事项](#安全注意事项)
|
||
|
||
### 附录
|
||
33. [更新日志](#-更新日志)
|
||
34. [文件结构](#-文件结构)
|
||
|
||
---
|
||
|
||
## 🆕 最新更新 (v2.11.0 - Socket 连接优化)
|
||
|
||
### 优化内容
|
||
|
||
#### 1. **指数退避策略** 🔄
|
||
- ✅ **智能重连间隔**:从"激进"改为"渐进"策略
|
||
- Real Socket: 第1次 1分钟 → 第2次 2分钟 → 第3次+ 4分钟
|
||
- Mock Socket: 第1次 10秒 → 第2次 20秒 → 第3次+ 40秒(缩短10倍便于测试)
|
||
- ✅ **无限重试**:不再在5次后停止,持续使用指数退避重连
|
||
- ✅ **自定义退避逻辑**:禁用 Socket.IO 默认重连机制,实现更可控的重连策略
|
||
|
||
#### 2. **连接状态横幅优化** 📍
|
||
- ✅ **降低侵入性**:zIndex 从 10000 → 1050,高度 py={3} → py={2},半透明背景(opacity 0.95)
|
||
- ✅ **手动关闭**:所有状态(DISCONNECTED/RECONNECTING/FAILED)都可手动关闭
|
||
- ✅ **状态持久化**:用户关闭后保存到 localStorage,重连成功后自动清除
|
||
- ✅ **自动消失**:重连成功显示"✓ 已重新连接" 2秒后自动消失
|
||
|
||
#### 3. **Mock 模式测试功能** 🧪
|
||
- ✅ **断线重连模拟**:`__mockSocket.simulateDisconnection()` - 模拟意外断线,自动重连
|
||
- ✅ **连接状态查询**:`__mockSocket.isConnected()` - 查看当前连接状态
|
||
- ✅ **手动重连**:`__mockSocket.reconnect()` - 立即触发重连
|
||
- ✅ **控制台提示**:开发模式启动时自动显示可用测试函数
|
||
|
||
---
|
||
|
||
## 📋 系统概述
|
||
|
||
本系统是专为**金融资讯场景**设计的实时消息推送系统,支持 Mock 模式(开发)和真实 Socket.IO 模式(生产)。消息以右下角层叠弹窗的形式显示,**新消息在最上方**,符合主流桌面应用的交互习惯。
|
||
|
||
### 核心特性
|
||
|
||
- **双通知系统**:网页通知 + 浏览器原生通知,智能分发
|
||
- **4 种通知类型**:公告通知、股票动向、事件动向、分析报告
|
||
- **3 级优先级**:紧急(不关闭)、重要(30秒)、普通(15秒)
|
||
- **智能折叠**:最多显示3条,超过显示展开按钮
|
||
- **智能配色**:股票动向根据涨跌自动变色(涨红 🔴 / 跌绿 🟢)
|
||
- **丰富元数据**:发布时间、作者、AI 标识
|
||
- **点击跳转**:可配置点击跳转链接
|
||
- **队列管理**:最多保留 15 条历史,可展开查看
|
||
- **性能监控**:自动追踪到达率、点击率、响应时间
|
||
- **历史记录**:持久化存储、筛选、搜索、导出功能
|
||
|
||
### 技术亮点
|
||
|
||
- **WebSocket 实时通信**:Socket.IO + 指数退避重连策略
|
||
- **双重去重机制**:Socket层去重 + 显示层去重
|
||
- **智能权限引导**:渐进式权限请求,避免打扰用户
|
||
- **无障碍支持**:完整的 ARIA 属性,键盘导航
|
||
- **性能优化**:React.memo、useMemo、useCallback,GPU 加速动画
|
||
|
||
---
|
||
|
||
## 🏗️ 系统架构设计
|
||
|
||
### 整体架构图
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "前端应用"
|
||
A[App.js] --> B[AppProviders]
|
||
B --> C[NotificationProvider<br/>NotificationContext]
|
||
C --> D[NotificationContainer<br/>通知容器UI]
|
||
C --> E[NotificationTestTool<br/>开发测试工具]
|
||
end
|
||
|
||
subgraph "通知系统核心"
|
||
C --> F[Socket Service<br/>WebSocket连接]
|
||
F --> G{环境判断}
|
||
G -->|Mock| H[mockSocketService<br/>模拟服务]
|
||
G -->|Real| I[socketService<br/>生产服务]
|
||
|
||
C --> J[browserNotificationService<br/>浏览器通知]
|
||
C --> K[notificationHistoryService<br/>历史记录]
|
||
C --> L[notificationMetricsService<br/>性能监控]
|
||
end
|
||
|
||
subgraph "后端服务"
|
||
I --> 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` |
|
||
| **NotificationTestTool** | React Component | 开发测试工具 | `src/components/NotificationTestTool/index.js` |
|
||
| **socketService** | Service Class | 真实WebSocket连接管理 | `src/services/socketService.js` |
|
||
| **mockSocketService** | Service Class | Mock WebSocket模拟 | `src/services/mockSocketService.js` |
|
||
| **browserNotificationService** | Service Class | 浏览器通知API封装 | `src/services/browserNotificationService.js` |
|
||
| **notificationHistoryService** | Service Class | 历史记录持久化 | `src/services/notificationHistoryService.js` |
|
||
| **notificationMetricsService** | Service Class | 性能监控埋点 | `src/services/notificationMetricsService.js` |
|
||
| **notificationTypes** | Constants | 类型定义、配置常量 | `src/constants/notificationTypes.js` |
|
||
| **useEventNotifications** | Custom Hook | 事件订阅Hook | `src/hooks/useEventNotifications.js` |
|
||
|
||
---
|
||
|
||
### 1. NotificationContext - 通知上下文
|
||
|
||
**职责**:通知系统的大脑,管理所有状态和业务逻辑。
|
||
|
||
**核心功能**:
|
||
- WebSocket 连接管理(连接、断开、重连)
|
||
- 通知队列管理(添加、移除、去重)
|
||
- 权限管理(浏览器通知权限请求)
|
||
- 通知分发(网页/浏览器通知智能选择)
|
||
- 音效播放
|
||
- 性能监控埋点
|
||
|
||
**主要API**:
|
||
```javascript
|
||
const {
|
||
notifications, // 当前通知队列
|
||
isConnected, // Socket连接状态
|
||
soundEnabled, // 音效开关状态
|
||
browserPermission, // 浏览器通知权限状态
|
||
connectionStatus, // 详细连接状态
|
||
addNotification, // 添加通知
|
||
removeNotification, // 移除通知
|
||
clearAllNotifications, // 清空所有通知
|
||
toggleSound, // 切换音效
|
||
requestBrowserPermission, // 请求浏览器通知权限
|
||
trackNotificationClick, // 追踪通知点击
|
||
retryConnection, // 手动重连
|
||
showWelcomeGuide, // 显示欢迎引导
|
||
showCommunityGuide, // 显示社区功能引导
|
||
showFirstFollowGuide, // 显示首次关注引导
|
||
} = useNotification();
|
||
```
|
||
|
||
**关键实现细节**:
|
||
|
||
1. **双重去重机制**:
|
||
```javascript
|
||
// Socket层去重(避免重复接收)
|
||
const processedEventIds = useRef(new Set());
|
||
|
||
// 显示层去重(避免重复显示)
|
||
const isDuplicate = notifications.some(n => n.id === notificationId);
|
||
```
|
||
|
||
2. **智能分发策略**:
|
||
```javascript
|
||
const isPageHidden = document.hidden;
|
||
|
||
if (isPageHidden) {
|
||
sendBrowserNotification(notification); // 后台:浏览器通知
|
||
} else {
|
||
addWebNotification(notification); // 前台:网页通知
|
||
}
|
||
```
|
||
|
||
3. **渐进式权限引导**:
|
||
```javascript
|
||
// 登录后显示欢迎引导
|
||
showWelcomeGuide();
|
||
|
||
// 首次关注事件时显示引导
|
||
showFirstFollowGuide();
|
||
```
|
||
|
||
**源码位置**: `src/contexts/NotificationContext.js:1-829`
|
||
|
||
---
|
||
|
||
### 2. NotificationContainer - 通知容器UI
|
||
|
||
**职责**:通知的可视化展示,处理动画、交互、无障碍。
|
||
|
||
**核心功能**:
|
||
- 通知列表渲染(最多显示3条)
|
||
- 折叠/展开功能
|
||
- 通知动画(Framer Motion)
|
||
- 点击跳转
|
||
- 无障碍支持(ARIA、键盘导航)
|
||
|
||
**主要组件**:
|
||
```jsx
|
||
<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:1-757`
|
||
|
||
---
|
||
|
||
### 3. SocketService - WebSocket服务
|
||
|
||
**职责**:管理Socket.IO连接,处理断线重连,事件订阅。
|
||
|
||
**核心功能**:
|
||
- Socket.IO 连接管理
|
||
- 指数退避重连策略
|
||
- 事件订阅/取消订阅
|
||
- 消息发送/接收
|
||
|
||
**重连策略**:
|
||
```javascript
|
||
// 指数退避延迟
|
||
getReconnectionDelay(attempt) {
|
||
const delays = [60000, 120000, 240000]; // 1min, 2min, 4min
|
||
const index = Math.min(attempt - 1, delays.length - 1);
|
||
return delays[index];
|
||
}
|
||
|
||
// 无限重试
|
||
this.maxReconnectAttempts = Infinity;
|
||
```
|
||
|
||
**主要API**:
|
||
```javascript
|
||
// 连接管理
|
||
socket.connect();
|
||
socket.disconnect();
|
||
socket.reconnect();
|
||
socket.isConnected();
|
||
|
||
// 事件监听
|
||
socket.on('event_name', callback);
|
||
socket.off('event_name');
|
||
|
||
// 事件订阅(金融资讯专用)
|
||
socket.subscribeToEvents({ eventType, importance, onNewEvent });
|
||
socket.unsubscribeFromEvents({ eventType });
|
||
```
|
||
|
||
**源码位置**: `src/services/socketService.js:1-465`
|
||
|
||
---
|
||
|
||
### 其他核心组件
|
||
|
||
#### 4. browserNotificationService - 浏览器通知服务
|
||
|
||
**核心方法**:
|
||
```javascript
|
||
// 检查支持性
|
||
browserNotificationService.isSupported();
|
||
|
||
// 获取权限状态
|
||
browserNotificationService.getPermissionStatus(); // 'granted' | 'denied' | 'default'
|
||
|
||
// 请求权限
|
||
await browserNotificationService.requestPermission();
|
||
|
||
// 发送通知
|
||
const notification = browserNotificationService.sendNotification({
|
||
title: '通知标题',
|
||
body: '通知内容',
|
||
tag: 'unique_tag',
|
||
requireInteraction: false,
|
||
data: { link: '/detail' },
|
||
autoClose: 8000,
|
||
});
|
||
|
||
// 设置点击处理
|
||
notification.onclick = () => {
|
||
window.focus();
|
||
window.location.hash = link;
|
||
notification.close();
|
||
};
|
||
```
|
||
|
||
**源码位置**: `src/services/browserNotificationService.js:1-209`
|
||
|
||
---
|
||
|
||
#### 5. notificationHistoryService - 历史记录服务
|
||
|
||
**核心功能**:
|
||
- localStorage持久化(最多500条)
|
||
- 筛选(类型、优先级、日期范围、阅读状态)
|
||
- 搜索(关键词匹配)
|
||
- 导出(JSON/CSV)
|
||
- 统计分析
|
||
|
||
**主要API**:
|
||
```javascript
|
||
// 保存通知
|
||
notificationHistoryService.saveNotification(notification);
|
||
|
||
// 获取历史记录(带筛选)
|
||
const { records, total, page, totalPages } = notificationHistoryService.getHistory({
|
||
type: 'event_alert',
|
||
priority: 'urgent',
|
||
readStatus: 'unread',
|
||
startDate: Date.now() - 7 * 24 * 60 * 60 * 1000,
|
||
page: 1,
|
||
pageSize: 20,
|
||
});
|
||
|
||
// 搜索
|
||
const results = notificationHistoryService.searchHistory('央行');
|
||
|
||
// 导出
|
||
notificationHistoryService.downloadJSON();
|
||
notificationHistoryService.downloadCSV();
|
||
|
||
// 统计
|
||
const stats = notificationHistoryService.getStats();
|
||
// { total, read, unread, clicked, clickRate, byType, byPriority }
|
||
```
|
||
|
||
**源码位置**: `src/services/notificationHistoryService.js:1-403`
|
||
|
||
---
|
||
|
||
#### 6. notificationMetricsService - 性能监控服务
|
||
|
||
**追踪指标**:
|
||
- 总发送数、总接收数
|
||
- 点击率、关闭率
|
||
- 平均响应时间(从接收到点击的时间)
|
||
- 每小时推送分布
|
||
- 每日数据趋势(最近30天)
|
||
|
||
**主要API**:
|
||
```javascript
|
||
// 追踪事件
|
||
notificationMetricsService.trackReceived(notification);
|
||
notificationMetricsService.trackClicked(notification);
|
||
notificationMetricsService.trackDismissed(notification);
|
||
|
||
// 获取统计
|
||
const summary = notificationMetricsService.getSummary();
|
||
// {
|
||
// totalReceived, totalClicked, totalDismissed,
|
||
// clickRate, avgResponseTime, deliveryRate
|
||
// }
|
||
|
||
const byType = notificationMetricsService.getByType();
|
||
// { announcement: { received, clicked, dismissed, clickRate }, ... }
|
||
|
||
const dailyData = notificationMetricsService.getDailyData(7); // 最近7天
|
||
// [ { date: '2025-01-01', received, clicked, dismissed, clickRate }, ... ]
|
||
|
||
// 导出
|
||
const json = notificationMetricsService.exportToJSON();
|
||
const csv = notificationMetricsService.exportToCSV();
|
||
```
|
||
|
||
**源码位置**: `src/services/notificationMetricsService.js:1-451`
|
||
|
||
---
|
||
|
||
## 🔄 消息推送完整流程
|
||
|
||
### 1. 初始化流程
|
||
|
||
```mermaid
|
||
graph LR
|
||
A[App启动] --> B[AppProviders挂载]
|
||
B --> C[NotificationProvider初始化]
|
||
C --> D{环境检测}
|
||
D -->|Mock| E[连接mockSocketService]
|
||
D -->|Real| F[连接socketService]
|
||
E --> G[注册事件监听器]
|
||
F --> G
|
||
G --> H[触发connect事件]
|
||
H --> I[更新连接状态]
|
||
I --> J[检查浏览器通知权限]
|
||
J --> K[加载历史记录]
|
||
K --> L[初始化完成]
|
||
```
|
||
|
||
**代码实现** (src/contexts/NotificationContext.js:573-712):
|
||
```javascript
|
||
useEffect(() => {
|
||
// 1. 注册事件监听器
|
||
socket.on('connect', handleConnect);
|
||
socket.on('disconnect', handleDisconnect);
|
||
socket.on('connect_error', handleConnectError);
|
||
socket.on('new_event', handleNewEvent);
|
||
|
||
// 2. 连接Socket
|
||
socket.connect();
|
||
|
||
// 3. Mock模式启动自动推送
|
||
if (SOCKET_TYPE === 'MOCK') {
|
||
socket.startMockPush(60000, 2); // 60秒推送1-2条
|
||
}
|
||
|
||
// 4. 清理函数
|
||
return () => {
|
||
socket.off('connect');
|
||
socket.off('disconnect');
|
||
socket.off('new_event');
|
||
socket.disconnect();
|
||
};
|
||
}, []);
|
||
```
|
||
|
||
---
|
||
|
||
### 2. 消息接收与适配
|
||
|
||
```mermaid
|
||
graph TB
|
||
A[后端推送事件] --> B{Socket层去重}
|
||
B -->|已存在| C[忽略]
|
||
B -->|新事件| D[触发 new_event]
|
||
D --> E{数据格式检测}
|
||
E -->|前端格式| F[直接使用]
|
||
E -->|后端格式| G[格式适配]
|
||
|
||
G --> H[映射重要性<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:336-395`
|
||
|
||
---
|
||
|
||
### 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:558-570):
|
||
```javascript
|
||
const isPageHidden = document.hidden;
|
||
|
||
if (isPageHidden) {
|
||
// 后台:浏览器通知
|
||
logger.info('NotificationContext', 'Page hidden: sending browser notification');
|
||
sendBrowserNotification(newNotification);
|
||
} else {
|
||
// 前台:网页通知
|
||
logger.info('NotificationContext', 'Page visible: sending web notification');
|
||
addWebNotification(newNotification);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 性能监控埋点
|
||
|
||
系统自动追踪以下事件:
|
||
|
||
```javascript
|
||
// 通知接收时
|
||
notificationMetricsService.trackReceived(notification);
|
||
|
||
// 通知点击时
|
||
notificationMetricsService.trackClicked(notification);
|
||
// 自动计算响应时间 = 点击时间 - 接收时间
|
||
|
||
// 通知关闭时
|
||
notificationMetricsService.trackDismissed(notification);
|
||
```
|
||
|
||
**性能指标计算**:
|
||
```javascript
|
||
// 点击率
|
||
clickRate = (totalClicked / totalReceived) * 100
|
||
|
||
// 平均响应时间
|
||
avgResponseTime = totalResponseTime / totalClicked
|
||
|
||
// 到达率(假设sent=received)
|
||
deliveryRate = (totalReceived / totalSent) * 100
|
||
```
|
||
|
||
**数据存储**:
|
||
- localStorage 持久化
|
||
- 最多保留 30 天数据
|
||
- 自动清理过期数据
|
||
|
||
---
|
||
|
||
## 🧪 测试快速指南
|
||
|
||
> 📖 **完整测试手册**: [notification-manual-testing-guide.md](./test-cases/notification-manual-testing-guide.md)
|
||
|
||
### 本地测试
|
||
|
||
#### Mock 模式(快速开发测试)
|
||
|
||
```bash
|
||
npm start
|
||
```
|
||
|
||
**关键检查**:
|
||
```javascript
|
||
// 1. 验证连接
|
||
__SOCKET_DEBUG__.getStatus()
|
||
// 预期: { type: "MOCK", connected: true }
|
||
|
||
// 2. 查看可用工具
|
||
__mockSocket
|
||
// 包含: simulateDisconnection, pausePush, resumePush, sendTestNotification 等
|
||
|
||
// 3. 快速触发测试通知(三种方式)
|
||
__mockSocket.sendTestNotification() // 方式1: 快速触发1条公告通知
|
||
__NOTIFY_DEBUG__.testNotify('announcement') // 方式2: 指定类型的测试通知
|
||
__NOTIFY_DEBUG__.testNotify('stock_alert') // 方式3: 其他类型(股票/事件/报告)
|
||
```
|
||
|
||
#### Real 模式(后端联调)
|
||
|
||
```bash
|
||
npm run start:real
|
||
```
|
||
|
||
**关键检查**:
|
||
```javascript
|
||
// 1. 验证连接
|
||
__SOCKET_DEBUG__.getStatus()
|
||
// 预期: { type: "REAL", connected: true }
|
||
|
||
// 2. 手动订阅
|
||
__NOTIFY_DEBUG__.subscribe()
|
||
|
||
// 3. 等待后端推送
|
||
// 观察控制台: "✅ New event received"
|
||
```
|
||
|
||
### 线上测试
|
||
|
||
**访问**: https://valuefrontier.cn
|
||
|
||
```javascript
|
||
// 一键诊断
|
||
__NOTIFY_DEBUG__.checkAll()
|
||
|
||
// 手动订阅
|
||
__NOTIFY_DEBUG__.subscribe()
|
||
|
||
// 导出报告
|
||
__NOTIFY_DEBUG__.exportReport()
|
||
```
|
||
|
||
### 调试工具速查
|
||
|
||
| 工具 | 适用环境 | 主要功能 |
|
||
|------|---------|---------|
|
||
| `__mockSocket` | Mock模式 | 模拟断线、暂停推送、测试重连 |
|
||
| `__SOCKET_DEBUG__` | 所有环境 | 连接状态、配置检查、手动订阅 |
|
||
| `__NOTIFY_DEBUG__` | 所有环境 | 完整诊断、模拟通知、导出报告 |
|
||
|
||
**常用命令**:
|
||
```javascript
|
||
// 完整诊断
|
||
__NOTIFY_DEBUG__.checkAll()
|
||
|
||
// 查看帮助
|
||
__NOTIFY_DEBUG__.help()
|
||
|
||
// Mock模式:暂停自动推送
|
||
__mockSocket.pausePush()
|
||
|
||
// Mock模式:模拟断线
|
||
__mockSocket.simulateDisconnection()
|
||
```
|
||
|
||
### 测试通知方法对比
|
||
|
||
| 方法 | 环境 | 触发速度 | 通知类型 | 使用场景 |
|
||
|------|------|---------|---------|---------|
|
||
| `__mockSocket.sendTestNotification()` | Mock模式 | ⚡ 即时 | 固定:公告通知 | 快速验证通知UI是否正常显示 |
|
||
| `__NOTIFY_DEBUG__.testNotify(type)` | Mock模式 | ⚡ 即时 | 可选:4种类型 | 测试不同类型通知的显示效果 |
|
||
| 等待自动推送 | Mock模式 | 🐢 60秒/次 | 随机:所有类型 | 测试自动推送机制和真实场景 |
|
||
| `__NOTIFY_DEBUG__.subscribe()` | Real模式 | ⏳ 等待后端 | 取决于后端 | 测试后端集成和真实数据 |
|
||
|
||
**推荐用法**:
|
||
```javascript
|
||
// 场景1: 快速验证UI(最快)
|
||
__mockSocket.sendTestNotification()
|
||
|
||
// 场景2: 测试特定类型
|
||
__NOTIFY_DEBUG__.testNotify('stock_alert') // 股票动向
|
||
__NOTIFY_DEBUG__.testNotify('event_alert') // 事件动向
|
||
__NOTIFY_DEBUG__.testNotify('analysis_report') // 分析报告
|
||
|
||
// 场景3: 测试自动推送
|
||
__mockSocket.resumePush() // 确保推送开启
|
||
// 等待60秒...
|
||
|
||
// 场景4: 暂停干扰
|
||
__mockSocket.pausePush() // 暂停自动推送,专注测试单条
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ 手动测试清单
|
||
|
||
### 功能测试
|
||
|
||
#### 1. 通知显示测试
|
||
- [ ] 公告通知显示正确(蓝色系、图标、内容)
|
||
- [ ] 股票上涨通知显示为红色系(+图标、红边框)
|
||
- [ ] 股票下跌通知显示为绿色系(-图标、绿边框)
|
||
- [ ] 事件动向通知显示正确(橙色系)
|
||
- [ ] 分析报告(人工)显示正确(紫色系、作者信息)
|
||
- [ ] 分析报告(AI)显示AI徽章
|
||
- [ ] 预测通知显示"预测"徽章和状态提示
|
||
|
||
#### 2. 优先级测试
|
||
- [ ] 紧急通知:红色标签、粗边框、脉冲动画、不自动关闭
|
||
- [ ] 重要通知:橙色标签、中等边框、30秒自动关闭
|
||
- [ ] 普通通知:无标签、细边框、15秒自动关闭
|
||
|
||
#### 3. 交互测试
|
||
- [ ] 点击可点击通知跳转到正确页面
|
||
- [ ] 点击不可点击通知无反应
|
||
- [ ] 点击关闭按钮关闭通知
|
||
- [ ] 音效开关正常工作
|
||
- [ ] 清空全部按钮清空所有通知
|
||
|
||
#### 4. 折叠展开测试
|
||
- [ ] 通知数 ≤ 3 时不显示展开按钮
|
||
- [ ] 通知数 > 3 时显示"还有X条通知"按钮
|
||
- [ ] 点击展开显示所有通知
|
||
- [ ] 点击收起恢复显示前3条
|
||
- [ ] 展开状态保存到localStorage(刷新页面保持)
|
||
- [ ] 展开状态2分钟后自动过期
|
||
|
||
#### 5. 队列管理测试
|
||
- [ ] 最多显示15条历史通知
|
||
- [ ] 超过15条自动移除最旧的
|
||
- [ ] 新通知在最上方
|
||
- [ ] z-index层级正确(最新最高)
|
||
|
||
### 浏览器通知测试
|
||
|
||
#### 6. 权限管理测试
|
||
- [ ] 首次访问显示权限状态为"default"
|
||
- [ ] 点击请求权限按钮弹出浏览器授权对话框
|
||
- [ ] 授权后状态变为"granted"
|
||
- [ ] 拒绝后状态变为"denied",显示设置指引
|
||
- [ ] 权限状态在测试工具中正确显示
|
||
|
||
#### 7. 浏览器通知显示测试
|
||
- [ ] 权限授予后,切换到其他标签页,收到通知时显示系统通知
|
||
- [ ] 点击浏览器通知聚焦窗口并跳转到详情页
|
||
- [ ] 普通通知8秒后自动关闭
|
||
- [ ] 紧急通知需手动关闭
|
||
|
||
### Socket连接测试
|
||
|
||
#### 8. Mock模式测试
|
||
- [ ] 启动后显示"Connected" + "MOCK"状态
|
||
- [ ] 控制台显示可用测试函数
|
||
- [ ] `__mockSocket.simulateDisconnection()` 模拟断线成功
|
||
- [ ] 断线后自动重连(10秒→20秒→40秒)
|
||
- [ ] 重连成功后显示"✓ 已重新连接" 2秒后消失
|
||
- [ ] `__mockSocket.simulateConnectionFailure()` 模拟持续失败
|
||
- [ ] 手动关闭连接状态横幅后保存到localStorage
|
||
- [ ] `__mockSocket.reconnect()` 立即重连成功
|
||
|
||
#### 9. Real模式测试
|
||
- [ ] 启动后显示"Connected" + "REAL"状态
|
||
- [ ] 后端推送消息能正确接收
|
||
- [ ] 断线后自动重连(1分钟→2分钟→4分钟)
|
||
- [ ] 网络恢复时自动重连
|
||
- [ ] 标签页重新聚焦时自动重连
|
||
|
||
### UI测试
|
||
|
||
#### 10. 响应式测试
|
||
- [ ] 移动端(< 480px):宽度 = 屏宽-32px,精简显示
|
||
- [ ] 小屏(480-768px):宽度 = 360px
|
||
- [ ] 平板(768-992px):宽度 = 380px
|
||
- [ ] 桌面(> 992px):宽度 = 400px
|
||
- [ ] 底部偏移:移动端12px,桌面端112px(避开设置按钮)
|
||
|
||
#### 11. 动画测试
|
||
- [ ] 新通知从底部向上滑入(浮起效果)
|
||
- [ ] 关闭通知向右滑出消失
|
||
- [ ] 紧急通知边框脉冲动画流畅
|
||
- [ ] 折叠/展开动画流畅无卡顿
|
||
- [ ] 展开15条通知无明显性能问题
|
||
|
||
#### 12. 无障碍测试
|
||
- [ ] Tab键可以聚焦到可点击通知
|
||
- [ ] 聚焦时显示蓝色轮廓线
|
||
- [ ] Enter/Space键可以打开详情
|
||
- [ ] 关闭按钮可键盘操作(Tab + Enter)
|
||
- [ ] 屏幕阅读器朗读完整通知信息
|
||
|
||
### 性能测试
|
||
|
||
#### 13. 性能指标测试
|
||
- [ ] 发送10条通知,打开控制台查看性能指标
|
||
- [ ] 点击5条通知,检查点击率计算正确
|
||
- [ ] 检查平均响应时间计算正确
|
||
- [ ] 导出JSON/CSV文件正常
|
||
- [ ] 查看每小时分布图表数据准确
|
||
|
||
#### 14. 历史记录测试
|
||
- [ ] 收到通知后自动保存到历史记录
|
||
- [ ] 筛选功能正常(类型、优先级、日期范围、阅读状态)
|
||
- [ ] 搜索功能返回正确结果
|
||
- [ ] 分页功能正常
|
||
- [ ] 统计数据准确
|
||
- [ ] 导出JSON/CSV正常
|
||
|
||
### 跨浏览器测试
|
||
|
||
#### 15. 兼容性测试
|
||
- [ ] Chrome: 所有功能正常
|
||
- [ ] Edge: 所有功能正常
|
||
- [ ] Firefox: 所有功能正常
|
||
- [ ] Safari: 所有功能正常(注意浏览器通知权限)
|
||
- [ ] 移动端Chrome: 响应式布局正确
|
||
- [ ] 移动端Safari: 响应式布局正确
|
||
|
||
---
|
||
|
||
## 🧪 自动化测试用例
|
||
|
||
完整的自动化测试代码请参考: **[notification-tests.md](./test-cases/notification-tests.md)**
|
||
|
||
以下是核心测试用例概览:
|
||
|
||
### 单元测试
|
||
|
||
#### NotificationContext 测试
|
||
```javascript
|
||
describe('NotificationContext', () => {
|
||
test('应该正确初始化默认状态', () => {
|
||
const { result } = renderHook(() => useNotification(), {
|
||
wrapper: NotificationProvider,
|
||
});
|
||
|
||
expect(result.current.notifications).toEqual([]);
|
||
expect(result.current.soundEnabled).toBe(true);
|
||
});
|
||
|
||
test('应该正确添加通知', () => {
|
||
const { result } = renderHook(() => useNotification(), {
|
||
wrapper: NotificationProvider,
|
||
});
|
||
|
||
act(() => {
|
||
result.current.addNotification({
|
||
type: 'announcement',
|
||
priority: 'important',
|
||
title: '测试通知',
|
||
content: '内容',
|
||
});
|
||
});
|
||
|
||
expect(result.current.notifications).toHaveLength(1);
|
||
expect(result.current.notifications[0].title).toBe('测试通知');
|
||
});
|
||
|
||
// 更多测试用例...
|
||
});
|
||
```
|
||
|
||
#### notificationHistoryService 测试
|
||
```javascript
|
||
describe('notificationHistoryService', () => {
|
||
beforeEach(() => {
|
||
localStorage.clear();
|
||
});
|
||
|
||
test('应该保存通知到历史记录', () => {
|
||
const notification = {
|
||
id: 'test001',
|
||
type: 'announcement',
|
||
title: '测试',
|
||
content: '内容',
|
||
};
|
||
|
||
notificationHistoryService.saveNotification(notification);
|
||
const { records } = notificationHistoryService.getHistory();
|
||
|
||
expect(records).toHaveLength(1);
|
||
expect(records[0].notification.id).toBe('test001');
|
||
});
|
||
|
||
// 更多测试用例...
|
||
});
|
||
```
|
||
|
||
### 集成测试
|
||
|
||
```javascript
|
||
describe('通知系统集成测试', () => {
|
||
test('完整流程:从接收到显示到点击', async () => {
|
||
render(
|
||
<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);
|
||
});
|
||
});
|
||
```
|
||
|
||
### 测试覆盖率目标
|
||
|
||
| 模块 | 目标覆盖率 | 优先级 |
|
||
|------|-----------|--------|
|
||
| **notificationTypes.js** | 100% | 高 |
|
||
| **notificationHistoryService.js** | 90%+ | 高 |
|
||
| **notificationMetricsService.js** | 90%+ | 高 |
|
||
| **browserNotificationService.js** | 80%+ | 中 |
|
||
| **NotificationContext.js** | 70%+ | 中 |
|
||
| **NotificationContainer/index.js** | 60%+ | 中 |
|
||
| **socketService.js** | 50%+ | 低(难以测试) |
|
||
|
||
---
|
||
|
||
## 🎨 通知类型配置
|
||
|
||
### 类型 1: 公告通知 (Announcement)
|
||
|
||
**适用场景**:公司财报、重大资产重组、分红派息、停复牌公告
|
||
|
||
**配色方案**:
|
||
- 主色:蓝色 (Blue)
|
||
- 图标:📢 MdCampaign
|
||
- 背景:`blue.50` / 暗色:`rgba(59, 130, 246, 0.15)`
|
||
|
||
**示例**:
|
||
```javascript
|
||
{
|
||
type: NOTIFICATION_TYPES.ANNOUNCEMENT,
|
||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||
title: '贵州茅台发布2024年度财报公告',
|
||
content: '2024年度营收同比增长15.2%,净利润创历史新高',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
isAIGenerated: false,
|
||
clickable: true,
|
||
link: '/event-detail/ann001',
|
||
extra: {
|
||
announcementType: '财报',
|
||
companyCode: '600519',
|
||
},
|
||
autoClose: 10000,
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 类型 2: 股票动向 (Stock Alert)
|
||
|
||
**适用场景**:价格预警、异常波动、持仓表现
|
||
|
||
**动态配色**(根据 `extra.priceChange` 字段):
|
||
- **涨(+)**:红色系 🔴
|
||
- **跌(-)**:绿色系 🟢
|
||
|
||
**示例(涨)**:
|
||
```javascript
|
||
{
|
||
type: NOTIFICATION_TYPES.STOCK_ALERT,
|
||
priority: PRIORITY_LEVELS.URGENT,
|
||
title: '您关注的股票触发预警',
|
||
content: '宁德时代(300750) 当前价格 ¥245.50,涨幅 +5.2%',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
clickable: true,
|
||
link: '/stock-overview?code=300750',
|
||
extra: {
|
||
stockCode: '300750',
|
||
stockName: '宁德时代',
|
||
priceChange: '+5.2%', // 💡 重要:根据此字段判断涨跌
|
||
currentPrice: '245.50',
|
||
},
|
||
autoClose: 10000,
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 类型 3: 事件动向 (Event Alert)
|
||
|
||
**适用场景**:央行政策、行业政策、监管动态、宏观事件
|
||
|
||
**配色方案**:
|
||
- 主色:橙色 (Orange)
|
||
- 图标:📄 MdArticle
|
||
|
||
**示例**:
|
||
```javascript
|
||
{
|
||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||
title: '央行宣布降准0.5个百分点',
|
||
content: '中国人民银行宣布下调金融机构存款准备金率0.5个百分点',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
clickable: true,
|
||
link: '/event-detail/evt001',
|
||
extra: {
|
||
eventId: 'evt001',
|
||
relatedStocks: 12,
|
||
impactLevel: '重大利好',
|
||
},
|
||
autoClose: 12000,
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 类型 4: 分析报告 (Analysis Report)
|
||
|
||
**适用场景**:行业研报、策略报告、公司研报
|
||
|
||
**特殊字段**:
|
||
- `author`:作者信息(必填)
|
||
- `isAIGenerated`:是否 AI 生成(显示紫色 AI 徽章)
|
||
|
||
**示例(AI)**:
|
||
```javascript
|
||
{
|
||
type: NOTIFICATION_TYPES.ANALYSIS_REPORT,
|
||
priority: PRIORITY_LEVELS.NORMAL,
|
||
title: 'AI产业链投资机会分析',
|
||
content: '随着大模型应用加速落地,算力、数据、应用三大方向均存在投资机会',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
author: {
|
||
name: 'AI分析师',
|
||
organization: '价值前沿',
|
||
},
|
||
isAIGenerated: true, // 💡 显示 AI 徽章
|
||
clickable: true,
|
||
link: '/forecast-report?id=rpt002',
|
||
extra: {
|
||
reportType: '策略报告',
|
||
industry: '人工智能',
|
||
},
|
||
autoClose: 12000,
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 优先级配置
|
||
|
||
| 优先级 | 标签 | 边框 | 自动关闭 | 使用场景 |
|
||
|--------|------|------|---------|----------|
|
||
| **urgent** | 🔴 紧急 | 6px粗边框 + 脉冲动画 | ❌ 不自动关闭 | 重大事件、高优先级预警 |
|
||
| **important** | 🟠 重要 | 4px中等边框 | 30秒 | 重要消息、一般预警 |
|
||
| **normal** | - | 2px细边框 | 15秒 | 常规消息、信息推送 |
|
||
|
||
**配置代码**:
|
||
```javascript
|
||
export const PRIORITY_CONFIGS = {
|
||
[PRIORITY_LEVELS.URGENT]: {
|
||
label: '紧急',
|
||
colorScheme: 'red',
|
||
show: false,
|
||
borderWidth: '6px',
|
||
bgOpacity: 0.25,
|
||
},
|
||
[PRIORITY_LEVELS.IMPORTANT]: {
|
||
label: '重要',
|
||
colorScheme: 'orange',
|
||
show: false,
|
||
borderWidth: '4px',
|
||
bgOpacity: 0.15,
|
||
},
|
||
[PRIORITY_LEVELS.NORMAL]: {
|
||
label: '',
|
||
colorScheme: 'gray',
|
||
show: false,
|
||
borderWidth: '2px',
|
||
bgOpacity: 0.08,
|
||
},
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 1. 启用 Mock 模式
|
||
|
||
在 `.env` 文件中添加:
|
||
```bash
|
||
REACT_APP_ENABLE_MOCK=true
|
||
```
|
||
|
||
### 2. 启动项目
|
||
|
||
```bash
|
||
npm start
|
||
```
|
||
|
||
### 3. 测试通知
|
||
|
||
打开浏览器,右上角会显示 **"金融资讯测试工具"**,包含:
|
||
|
||
#### 通知类型测试
|
||
- 公告通知、股票动向、事件动向、分析报告
|
||
- 预测通知(不可跳转)
|
||
|
||
#### 组合测试
|
||
- 预测→详情流程(5秒延迟)
|
||
|
||
#### 功能按钮
|
||
- 清空全部、音效开关、队列状态
|
||
|
||
### 4. 自动推送
|
||
|
||
Mock 模式下,系统会自动每 60 秒推送 1-2 条随机金融资讯。
|
||
|
||
---
|
||
|
||
## 💻 代码使用
|
||
|
||
### 在组件中使用
|
||
|
||
```javascript
|
||
import { useNotification } from 'contexts/NotificationContext';
|
||
import { NOTIFICATION_TYPES, PRIORITY_LEVELS } from 'constants/notificationTypes';
|
||
|
||
function MyComponent() {
|
||
const { addNotification, isConnected } = useNotification();
|
||
|
||
const handleAnnouncement = () => {
|
||
addNotification({
|
||
type: NOTIFICATION_TYPES.ANNOUNCEMENT,
|
||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||
title: '贵州茅台发布2024年度财报公告',
|
||
content: '2024年度营收同比增长15.2%',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
isAIGenerated: false,
|
||
clickable: true,
|
||
link: '/event-detail/ann001',
|
||
autoClose: 10000,
|
||
});
|
||
};
|
||
|
||
return (
|
||
<div>
|
||
<p>连接状态: {isConnected ? '已连接' : '未连接'}</p>
|
||
<button onClick={handleAnnouncement}>发送公告</button>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 环境配置
|
||
|
||
### Mock 模式 vs Real 模式
|
||
|
||
| 配置项 | Mock 模式 | Real 模式 |
|
||
|--------|----------|----------|
|
||
| **启用方式** | `REACT_APP_ENABLE_MOCK=true` | `REACT_APP_ENABLE_MOCK=false` |
|
||
| **Socket 服务** | mockSocketService | socketService (Socket.IO) |
|
||
| **推送数据** | 14条模拟金融资讯 | 后端 Flask-SocketIO 推送 |
|
||
| **自动推送** | 60秒推送1-2条 | 无自动推送,按需推送 |
|
||
| **重连间隔** | 10s→20s→40s | 1min→2min→4min |
|
||
| **测试函数** | `window.__mockSocket.*` | 无 |
|
||
|
||
### 修改推送频率
|
||
|
||
在 `src/services/mockSocketService.js` 中:
|
||
```javascript
|
||
// 默认: 60秒推送1-2条
|
||
socket.startMockPush(60000, 2);
|
||
|
||
// 修改为: 30秒推送1条
|
||
socket.startMockPush(30000, 1);
|
||
|
||
// 修改为: 5秒推送3条(压力测试)
|
||
socket.startMockPush(5000, 3);
|
||
```
|
||
|
||
---
|
||
|
||
## 🌐 后端集成(生产环境)
|
||
|
||
### Flask-SocketIO 配置
|
||
|
||
```python
|
||
from flask_socketio import SocketIO, emit
|
||
|
||
# 初始化 SocketIO
|
||
socketio = SocketIO(app, cors_allowed_origins=[
|
||
"http://localhost:3000",
|
||
"https://valuefrontier.cn"
|
||
])
|
||
|
||
# 推送金融资讯通知
|
||
def send_financial_notification(user_id, data):
|
||
"""
|
||
推送金融资讯通知到指定用户
|
||
|
||
参数:
|
||
user_id: 用户ID
|
||
data: 通知数据(参考通知类型配置)
|
||
"""
|
||
socketio.emit('new_event', data, room=user_id)
|
||
|
||
# 启动服务器
|
||
if __name__ == '__main__':
|
||
socketio.run(app, host='0.0.0.0', port=5001)
|
||
```
|
||
|
||
### 后端推送示例
|
||
|
||
```python
|
||
import time
|
||
|
||
# 推送事件动向
|
||
socketio.emit('new_event', {
|
||
'type': 'event_alert',
|
||
'priority': 'important',
|
||
'title': '央行宣布降准0.5个百分点',
|
||
'content': '中国人民银行宣布下调金融机构存款准备金率0.5个百分点',
|
||
'publishTime': int(time.time() * 1000),
|
||
'pushTime': int(time.time() * 1000),
|
||
'isAIGenerated': False,
|
||
'clickable': True,
|
||
'link': '/event-detail/evt001',
|
||
'extra': {
|
||
'eventId': 'evt001',
|
||
'relatedStocks': 12,
|
||
'impactLevel': '重大利好',
|
||
},
|
||
'autoClose': 12000
|
||
}, room=user_id)
|
||
```
|
||
|
||
---
|
||
|
||
## 🐛 故障排除
|
||
|
||
### 问题 1: 通知不显示
|
||
|
||
**检查项**:
|
||
1. 确认 `NotificationProvider` 已包裹应用
|
||
2. 检查浏览器控制台是否有错误
|
||
3. 确认 socket 连接状态(查看测试工具)
|
||
4. 检查通知数据格式是否正确
|
||
|
||
### 问题 2: 股票动向颜色不对
|
||
|
||
**解决方案**:
|
||
1. 检查 `extra.priceChange` 字段是否存在
|
||
2. 确认格式为 "+5.2%" 或 "-3.8%"(包含符号)
|
||
3. 确认使用了 `NOTIFICATION_TYPES.STOCK_ALERT` 类型
|
||
|
||
### 问题 3: AI 徽章不显示
|
||
|
||
**解决方案**:
|
||
1. 检查 `isAIGenerated` 字段是否为 `true`
|
||
2. 确认字段名拼写正确(驼峰命名)
|
||
|
||
### 问题 4: 浏览器通知不工作
|
||
|
||
**解决方案**:
|
||
1. 检查浏览器通知权限是否已授予
|
||
2. macOS: 关闭"专注模式"或"勿扰模式"
|
||
3. Chrome: 检查 `chrome://settings/content/notifications`
|
||
4. 退出全屏模式(某些浏览器全屏时不显示通知)
|
||
5. **无痕/隐私模式特别说明**:
|
||
- Chrome 无痕模式:可以授权,但关闭窗口后权限会被清除
|
||
- Safari 隐私模式:通知权限被永久拒绝,无法更改
|
||
- 建议:使用普通浏览器窗口以获得完整体验
|
||
|
||
**快速诊断**:
|
||
```javascript
|
||
// 在浏览器控制台运行
|
||
console.log('Permission:', Notification.permission); // 'granted' | 'denied' | 'default'
|
||
console.log('Supported:', 'Notification' in window); // true | false
|
||
|
||
// 完整诊断
|
||
window.__NOTIFY_DEBUG__.checkAll();
|
||
```
|
||
|
||
### 问题 5: Chrome 无痕模式/隐私模式下 `Notification.permission = 'denied'` 怎么办?
|
||
|
||
#### 🔍 问题分析
|
||
|
||
当在 Chrome 无痕模式或其他浏览器隐私模式下遇到通知权限被拒绝的情况,需要了解不同浏览器的行为差异:
|
||
|
||
**浏览器行为对比表**:
|
||
|
||
| 浏览器模式 | 初始权限状态 | 是否可授权 | 权限持久性 | 推荐方案 |
|
||
|-----------|-------------|-----------|-----------|----------|
|
||
| **Chrome 正常模式** | `default` | ✅ 可以 | ✅ 永久保存 | ⭐ 推荐使用 |
|
||
| **Chrome 无痕模式** | `default` | ✅ 可以 | ❌ 关闭窗口后清除 | 可用但需重复授权 |
|
||
| **Safari 隐私模式** | `denied` | ❌ 不可以 | ❌ 永久拒绝 | 仅网页内通知 |
|
||
| **Firefox 隐私模式** | `default` | ✅ 可以 | ❌ 关闭窗口后清除 | 可用但需重复授权 |
|
||
|
||
**关键点**:
|
||
- ✅ Chrome 无痕模式的初始状态通常是 `default`(未决定),**不是** `denied`
|
||
- ⚠️ 如果看到 `denied`,说明用户点击了"阻止"按钮
|
||
- 🚫 Safari 隐私模式会直接显示 `denied`,且无法通过代码更改
|
||
- 🔄 权限在无痕/隐私窗口关闭后会被清除(Chrome/Firefox)
|
||
|
||
#### ✅ 当前系统已自动处理
|
||
|
||
**好消息**:您的通知系统已经完美处理了这种情况!
|
||
|
||
**自动降级策略**:
|
||
```javascript
|
||
// 在 NotificationContext.js 中
|
||
if (browserPermission !== 'granted') {
|
||
// 跳过浏览器通知,只显示网页内通知
|
||
logger.warn('NotificationContext', 'Browser permission not granted');
|
||
return;
|
||
}
|
||
```
|
||
|
||
**用户引导机制**:
|
||
- 当权限为 `denied` 时,自动显示橙色提示框
|
||
- 提示内容:"浏览器通知已被拒绝"
|
||
- 引导文案:"💡 如需接收桌面通知,请在浏览器设置中允许通知权限"
|
||
|
||
**实际效果**:
|
||
- ✅ 网页内通知卡片继续正常显示
|
||
- ⏭️ 浏览器桌面通知被优雅跳过
|
||
- ✅ 所有核心功能不受影响
|
||
- ✅ 用户体验降级合理
|
||
|
||
#### 🛠️ 三种解决方案
|
||
|
||
**方案 1:退出无痕模式(强烈推荐)**
|
||
```bash
|
||
# 原因:
|
||
# - 权限会永久保存
|
||
# - 获得完整通知体验
|
||
# - 不需要每次重新授权
|
||
|
||
# 操作:使用普通浏览器窗口访问应用
|
||
```
|
||
|
||
**方案 2:接受降级体验(推荐)**
|
||
```bash
|
||
# 系统已自动处理:
|
||
# ✅ 网页内通知正常显示(通知卡片)
|
||
# ⏭️ 桌面通知被跳过
|
||
# ✅ 不影响核心功能
|
||
# ✅ 用户体验合理
|
||
|
||
# 操作:无需任何操作,系统自动降级
|
||
```
|
||
|
||
**方案 3:在无痕模式中重新授权(仅 Chrome/Firefox)**
|
||
|
||
**Chrome 无痕模式**:
|
||
1. 点击地址栏左侧的 🔒 锁图标
|
||
2. 找到"通知"设置项
|
||
3. 将"阻止"改为"允许"
|
||
4. 刷新页面(F5)
|
||
5. ⚠️ **注意**:关闭无痕窗口后需要重新授权
|
||
|
||
**Safari 隐私模式**:
|
||
```bash
|
||
# ❌ Safari 隐私模式无法更改权限
|
||
# 💡 建议:退出隐私模式或接受降级体验
|
||
```
|
||
|
||
#### 📊 调试与验证
|
||
|
||
**快速检查权限状态**:
|
||
```javascript
|
||
// 在浏览器控制台运行
|
||
|
||
// 方法 1:直接查看
|
||
console.log('Permission:', Notification.permission);
|
||
// 输出: 'granted' | 'denied' | 'default'
|
||
|
||
// 方法 2:检查是否支持
|
||
console.log('Supported:', 'Notification' in window);
|
||
// 输出: true | false
|
||
|
||
// 方法 3:完整诊断(推荐)
|
||
window.__NOTIFY_DEBUG__.checkAll();
|
||
```
|
||
|
||
**完整诊断输出示例**:
|
||
```bash
|
||
========== 【通知系统诊断】 ==========
|
||
|
||
✓ Socket 配置检查完成
|
||
✓ 连接状态: ✅ 已连接
|
||
✓ API Base: http://49.232.185.254:5001
|
||
✓ 浏览器通知权限: denied
|
||
|
||
========== 诊断报告 ==========
|
||
┌─────────────┬────────────────────┐
|
||
│ socket.type │ MOCK │
|
||
│ connected │ true │
|
||
│ permission │ denied │
|
||
└─────────────┴────────────────────┘
|
||
|
||
⚠️ 发现问题: ['浏览器通知权限被拒绝']
|
||
|
||
💡 修复建议:
|
||
1. 如在无痕/隐私模式,建议退出使用正常模式
|
||
2. Chrome: 点击地址栏🔒 → 通知 → 允许
|
||
3. 或接受降级体验:继续使用网页内通知
|
||
|
||
====================================
|
||
```
|
||
|
||
**测试通知(仅 Mock 模式)**:
|
||
```javascript
|
||
// 测试不同类型通知
|
||
window.__NOTIFY_DEBUG__.testNotify('announcement'); // 公告通知
|
||
window.__NOTIFY_DEBUG__.testNotify('stock_alert'); // 股票动向
|
||
window.__NOTIFY_DEBUG__.testNotify('event_alert'); // 事件动向
|
||
window.__NOTIFY_DEBUG__.testNotify('analysis_report'); // 分析报告
|
||
|
||
// 查看帮助
|
||
window.__NOTIFY_DEBUG__.help();
|
||
```
|
||
|
||
#### 🎯 最佳实践建议
|
||
|
||
**开发环境**:
|
||
```javascript
|
||
// 使用 Mock 模式 + Chrome 正常窗口
|
||
// .env.mock
|
||
REACT_APP_ENABLE_MOCK=true
|
||
|
||
// 一次授权,永久有效
|
||
```
|
||
|
||
**测试环境**:
|
||
```javascript
|
||
// 测试多种权限状态
|
||
// 1. granted - 正常授权
|
||
// 2. denied - 测试降级体验
|
||
// 3. default - 测试首次访问流程
|
||
```
|
||
|
||
**生产环境**:
|
||
```javascript
|
||
// 引导用户使用正常浏览器窗口
|
||
// 提供清晰的权限说明
|
||
// 降级体验要优雅
|
||
```
|
||
|
||
#### 📝 相关代码位置
|
||
|
||
**权限处理逻辑**:
|
||
- `src/services/browserNotificationService.js:26-31` - 权限状态获取
|
||
- `src/services/browserNotificationService.js:85-88` - 权限检查
|
||
- `src/contexts/NotificationContext.js:295-298` - 发送前权限验证
|
||
- `src/contexts/NotificationContext.js:477-506` - 拒绝时用户引导
|
||
|
||
**调试工具**:
|
||
- `src/services/socket/index.js:192-362` - `window.__NOTIFY_DEBUG__` API
|
||
|
||
---
|
||
|
||
### 问题 6: Socket 连接失败
|
||
|
||
**解决方案**:
|
||
1. 检查后端 Flask-SocketIO 是否正确运行
|
||
2. 确认 CORS 配置正确
|
||
3. 检查 `src/utils/apiConfig.js` 中的 API 地址
|
||
4. 查看控制台日志确认错误信息
|
||
|
||
### 调试工具
|
||
|
||
**控制台日志**:
|
||
```javascript
|
||
// 所有操作都有详细日志
|
||
logger.info('NotificationContext', 'Event', data);
|
||
logger.warn('NotificationContext', 'Warning', error);
|
||
logger.error('NotificationContext', 'Error', error);
|
||
```
|
||
|
||
**Mock模式测试函数**:
|
||
```javascript
|
||
// 查看可用函数
|
||
console.log(window.__mockSocket);
|
||
|
||
// 模拟断线
|
||
__mockSocket.simulateDisconnection();
|
||
|
||
// 查看连接状态
|
||
__mockSocket.isConnected();
|
||
|
||
// 查看重连次数
|
||
__mockSocket.getAttempts();
|
||
```
|
||
|
||
### 日志分析
|
||
|
||
**正常日志流程**:
|
||
```
|
||
1. [NotificationContext] Initializing socket connection...
|
||
2. [NotificationContext] Socket connected
|
||
3. [NotificationContext] Received new event { id: 'evt001', title: '...' }
|
||
4. [NotificationContext] Adding notification { id: 'evt001', type: 'event_alert' }
|
||
5. [NotificationContext] Page visible: sending web notification
|
||
```
|
||
|
||
**异常日志排查**:
|
||
```
|
||
ERROR: Socket connect_error
|
||
→ 检查后端是否运行,网络是否通畅
|
||
|
||
WARN: Duplicate notification ignored at socket level
|
||
→ 正常,去重机制工作正常
|
||
|
||
ERROR: Browser permission not granted
|
||
→ 用户未授权浏览器通知,提示用户授权
|
||
```
|
||
|
||
---
|
||
|
||
## 📏 开发规范
|
||
|
||
### 1. 添加新通知类型
|
||
|
||
**步骤**:
|
||
1. 在 `src/constants/notificationTypes.js` 添加类型定义
|
||
2. 添加类型配置(图标、颜色、样式)
|
||
3. 在 `NotificationContainer` 中添加渲染逻辑
|
||
4. 在测试工具中添加测试按钮
|
||
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 秒
|
||
- 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 自动转义
|
||
<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
|
||
```
|
||
|
||
---
|
||
|
||
## 📁 文件结构
|
||
|
||
```
|
||
src/
|
||
├── constants/
|
||
│ └── notificationTypes.js # 通知类型定义和常量
|
||
├── services/
|
||
│ ├── socket/
|
||
│ │ └── index.js # Socket 服务统一导出
|
||
│ ├── socketService.js # 真实 Socket.IO 服务
|
||
│ ├── mockSocketService.js # Mock Socket 服务
|
||
│ ├── browserNotificationService.js # 浏览器通知服务
|
||
│ ├── notificationHistoryService.js # 历史记录服务
|
||
│ └── notificationMetricsService.js # 性能监控服务
|
||
├── contexts/
|
||
│ └── NotificationContext.js # 通知上下文管理
|
||
├── components/
|
||
│ ├── NotificationContainer/
|
||
│ │ └── index.js # 通知容器组件
|
||
│ └── NotificationTestTool/
|
||
│ └── index.js # 测试工具组件
|
||
├── hooks/
|
||
│ └── useEventNotifications.js # 事件订阅 Hook
|
||
└── assets/
|
||
└── sounds/
|
||
└── notification.wav # 通知音效
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 更新日志
|
||
|
||
### v2.11.0 (2025-01-07) - Socket 连接优化
|
||
- ✅ 指数退避重连策略
|
||
- ✅ 连接状态横幅优化
|
||
- ✅ Mock 模式测试功能增强
|
||
|
||
### v2.10.0 (2025-01-06) - 按钮加载态
|
||
- ✅ 点击"查看详情"显示 loading spinner
|
||
- ✅ 防重复点击机制
|
||
|
||
### v2.9.0 (2025-01-05) - 头部简化
|
||
- ✅ AI 和预测标识移到底部
|
||
|
||
### v2.8.0 (2025-01-04) - 无障碍支持
|
||
- ✅ 完整的 ARIA 属性
|
||
- ✅ 键盘导航支持
|
||
|
||
### v2.7.0 (2025-01-03) - 智能动画
|
||
- ✅ 只对新增通知应用动画
|
||
- ✅ 性能提升 90%+
|
||
|
||
### v2.6.0 (2025-01-02) - 状态持久化
|
||
- ✅ 展开/收起状态保存到 localStorage
|
||
- ✅ 2分钟自动过期
|
||
|
||
### v2.5.0 (2025-01-01) - 避开设置按钮
|
||
- ✅ 桌面端底部偏移112px
|
||
|
||
### v2.4.0 (2024-12-31) - 响应式优化
|
||
- ✅ 移动端、平板、桌面完美适配
|
||
|
||
### v2.3.0 (2024-12-30) - 智能折叠
|
||
- ✅ 最多显示3条,超过显示展开按钮
|
||
- ✅ 按优先级自动关闭
|
||
|
||
### v2.2.0 (2024-12-29) - 双通知系统
|
||
- ✅ 浏览器原生通知集成
|
||
- ✅ 智能分发策略
|
||
|
||
### v2.1.0 (2024-12-28) - 预测通知系统
|
||
- ✅ 预测→详情两阶段推送
|
||
- ✅ 严格可点击性判断
|
||
|
||
### v2.0.0 (2024-12-27) - 金融资讯专业版
|
||
- ✅ 4种金融资讯通知类型
|
||
- ✅ 3级优先级系统
|
||
- ✅ 智能元数据、动态配色
|
||
|
||
---
|
||
|
||
## 💡 技术栈
|
||
|
||
- **前端框架**: React 18.3.1
|
||
- **UI 库**: Chakra UI 2.8.2
|
||
- **路由**: React Router v6
|
||
- **实时通信**: Socket.IO Client 4.7.4
|
||
- **动画库**: Framer Motion
|
||
- **后端框架**: Flask-SocketIO 5.3.6
|
||
- **状态管理**: React Context API
|
||
- **图标库**: React Icons (Material Design)
|
||
|
||
---
|
||
|
||
## 📞 支持
|
||
|
||
如有问题,请查看:
|
||
- **项目文档**: `CLAUDE.md`
|
||
- **测试工具**: 开发环境右上角"金融资讯测试工具"
|
||
- **控制台日志**: 所有操作都有详细日志
|
||
- **类型定义**: `src/constants/notificationTypes.js`
|
||
- **测试用例**: `docs/test-cases/notification-tests.md`
|
||
|
||
---
|
||
|
||
**祝你使用愉快!** 🎉
|