This commit is contained in:
2026-02-18 19:41:03 +08:00
parent a0c2350662
commit 6543e2ccdd
18 changed files with 4885 additions and 1196 deletions

View File

@@ -1,684 +0,0 @@
# WebSocket 功能文档
## 概述
本项目基于 Laravel-S 和 Swoole 实现了完整的 WebSocket 功能,支持实时通信、消息推送、广播等功能。
## 功能特性
- ✅ 实时双向通信
- ✅ 用户连接管理
- ✅ 点对点消息发送
- ✅ 群发消息/广播
- ✅ 频道订阅/取消订阅
- ✅ 心跳机制
- ✅ 自动重连
- ✅ 在线状态管理
- ✅ 系统通知推送
- ✅ 数据更新推送
## 架构设计
### 后端组件
#### 1. WebSocketHandler (`app/Services/WebSocket/WebSocketHandler.php`)
WebSocket 处理器,实现了 Swoole 的 `WebSocketHandlerInterface` 接口。
**主要方法:**
- `onOpen()`: 处理连接建立事件
- `onMessage()`: 处理消息接收事件
- `onClose()`: 处理连接关闭事件
**支持的消息类型:**
- `ping/pong`: 心跳检测
- `heartbeat`: 心跳确认
- `chat`: 私聊消息
- `broadcast`: 广播消息
- `subscribe/unsubscribe`: 频道订阅/取消订阅
#### 2. WebSocketService (`app/Services/WebSocket/WebSocketService.php`)
WebSocket 服务类,提供便捷的 WebSocket 操作方法。
**主要方法:**
- `sendToUser($userId, $data)`: 发送消息给指定用户
- `sendToUsers($userIds, $data)`: 发送消息给多个用户
- `broadcast($data, $excludeUserId)`: 广播消息给所有用户
- `sendToChannel($channel, $data)`: 发送消息给指定频道
- `getOnlineUserCount()`: 获取在线用户数
- `isUserOnline($userId)`: 检查用户是否在线
- `sendSystemNotification()`: 发送系统通知
- `pushDataUpdate()`: 推送数据更新
#### 3. WebSocketController (`app/Http/Controllers/System/WebSocket.php`)
WebSocket API 控制器,提供 HTTP 接口用于管理 WebSocket 连接。
### 前端组件
#### WebSocketClient (`resources/admin/src/utils/websocket.js`)
WebSocket 客户端封装类。
**功能:**
- 自动连接和重连
- 心跳机制
- 消息类型路由
- 事件监听
- 连接状态管理
## 配置说明
### Laravel-S 配置 (`config/laravels.php`)
```php
'websocket' => [
'enable' => env('LARAVELS_WEBSOCKET', true),
'handler' => \App\Services\WebSocket\WebSocketHandler::class,
],
'swoole_tables' => [
'wsTable' => [
'size' => 102400,
'column' => [
['name' => 'value', 'type' => \Swoole\Table::TYPE_STRING, 'size' => 1024],
['name' => 'expiry', 'type' => \Swoole\Table::TYPE_INT, 'size' => 4],
],
],
],
```
### 环境变量
`.env` 文件中添加:
```env
LARAVELS_WEBSOCKET=true
```
## API 接口
### 1. 获取在线用户数
```
GET /admin/websocket/online-count
```
**响应:**
```json
{
"code": 200,
"message": "success",
"data": {
"online_count": 10
}
}
```
### 2. 获取在线用户列表
```
GET /admin/websocket/online-users
```
**响应:**
```json
{
"code": 200,
"message": "success",
"data": {
"user_ids": [1, 2, 3, 4, 5],
"count": 5
}
}
```
### 3. 检查用户在线状态
```
POST /admin/websocket/check-online
```
**请求参数:**
```json
{
"user_id": 1
}
```
**响应:**
```json
{
"code": 200,
"message": "success",
"data": {
"user_id": 1,
"is_online": true
}
}
```
### 4. 发送消息给指定用户
```
POST /admin/websocket/send-to-user
```
**请求参数:**
```json
{
"user_id": 1,
"type": "notification",
"data": {
"title": "新消息",
"message": "您有一条新消息"
}
}
```
### 5. 发送消息给多个用户
```
POST /admin/websocket/send-to-users
```
**请求参数:**
```json
{
"user_ids": [1, 2, 3],
"type": "notification",
"data": {
"title": "系统通知",
"message": "系统将在今晚进行维护"
}
}
```
### 6. 广播消息
```
POST /admin/websocket/broadcast
```
**请求参数:**
```json
{
"type": "notification",
"data": {
"title": "公告",
"message": "欢迎使用新版本"
},
"exclude_user_id": 1 // 可选:排除某个用户
}
```
### 7. 发送消息到频道
```
POST /admin/websocket/send-to-channel
```
**请求参数:**
```json
{
"channel": "orders",
"type": "data_update",
"data": {
"order_id": 123,
"status": "paid"
}
}
```
### 8. 发送系统通知
```
POST /admin/websocket/send-notification
```
**请求参数:**
```json
{
"title": "系统维护",
"message": "系统将于今晚 23:00-24:00 进行维护",
"type": "warning",
"extra_data": {
"start_time": "23:00",
"end_time": "24:00"
}
}
```
### 9. 发送通知给指定用户
```
POST /admin/websocket/send-notification-to-users
```
**请求参数:**
```json
{
"user_ids": [1, 2, 3],
"title": "订单更新",
"message": "您的订单已发货",
"type": "success"
}
```
### 10. 推送数据更新
```
POST /admin/websocket/push-data-update
```
**请求参数:**
```json
{
"user_ids": [1, 2, 3],
"resource_type": "order",
"action": "update",
"data": {
"id": 123,
"status": "shipped"
}
}
```
### 11. 推送数据更新到频道
```
POST /admin/websocket/push-data-update-channel
```
**请求参数:**
```json
{
"channel": "orders",
"resource_type": "order",
"action": "create",
"data": {
"id": 124,
"customer": "张三",
"amount": 100.00
}
}
```
### 12. 断开用户连接
```
POST /admin/websocket/disconnect-user
```
**请求参数:**
```json
{
"user_id": 1
}
```
## 前端使用示例
### 1. 基本连接
```javascript
import { getWebSocket, closeWebSocket } from '@/utils/websocket'
import { useUserStore } from '@/stores/modules/user'
const userStore = useUserStore()
// 连接 WebSocket
const ws = getWebSocket(userStore.userInfo.id, userStore.token, {
onOpen: (event) => {
console.log('WebSocket 已连接')
},
onMessage: (message) => {
console.log('收到消息:', message)
},
onError: (error) => {
console.error('WebSocket 错误:', error)
},
onClose: (event) => {
console.log('WebSocket 已关闭')
}
})
// 连接
ws.connect()
```
### 2. 监听特定消息类型
```javascript
// 监听通知消息
ws.on('notification', (data) => {
message.success(data.title, data.message)
})
// 监听数据更新
ws.on('data_update', (data) => {
console.log('数据更新:', data.resource_type, data.action)
// 刷新数据
loadData()
})
```
### 3. 发送消息
```javascript
// 发送心跳
ws.send('heartbeat', { timestamp: Date.now() })
// 发送私聊消息
ws.send('chat', {
to_user_id: 2,
content: '你好,这是一条私聊消息'
})
// 订阅频道
ws.send('subscribe', { channel: 'orders' })
// 取消订阅
ws.send('unsubscribe', { channel: 'orders' })
```
### 4. 发送广播消息
```javascript
ws.send('broadcast', {
message: '这是一条广播消息'
})
```
### 5. 断开连接
```javascript
// 断开连接
ws.disconnect()
// 或使用全局方法
closeWebSocket()
```
### 6. 在 Vue 组件中使用
```vue
<template>
<div>
<a-button @click="connectWebSocket">连接 WebSocket</a-button>
<a-button @click="disconnectWebSocket">断开连接</a-button>
<a-button @click="sendMessage">发送消息</a-button>
<div>连接状态: {{ connectionStatus }}</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { getWebSocket } from '@/utils/websocket'
import { useUserStore } from '@/stores/modules/user'
const userStore = useUserStore()
const ws = ref(null)
const connectionStatus = ref('未连接')
const connectWebSocket = () => {
ws.value = getWebSocket(userStore.userInfo.id, userStore.token, {
onOpen: () => {
connectionStatus.value = '已连接'
},
onMessage: (message) => {
handleMessage(message)
},
onClose: () => {
connectionStatus.value = '已断开'
}
})
ws.value.connect()
}
const disconnectWebSocket = () => {
if (ws.value) {
ws.value.disconnect()
connectionStatus.value = '已断开'
}
}
const sendMessage = () => {
if (ws.value && ws.value.isConnected) {
ws.value.send('chat', {
to_user_id: 2,
content: '测试消息'
})
}
}
const handleMessage = (message) => {
switch (message.type) {
case 'notification':
message.success(message.data.title, message.data.message)
break
case 'data_update':
// 处理数据更新
break
case 'chat':
// 处理聊天消息
break
}
}
onMounted(() => {
connectWebSocket()
})
onUnmounted(() => {
disconnectWebSocket()
})
</script>
```
## 消息格式
### 服务端发送的消息格式
```json
{
"type": "notification",
"data": {
"title": "标题",
"message": "内容",
"type": "info",
"timestamp": 1641234567
}
}
```
### 客户端发送的消息格式
```json
{
"type": "chat",
"data": {
"to_user_id": 2,
"content": "消息内容"
}
}
```
## 启动和停止
### 启动 Laravel-S 服务
```bash
php bin/laravels start
```
### 停止 Laravel-S 服务
```bash
php bin/laravels stop
```
### 重启 Laravel-S 服务
```bash
php bin/laravels restart
```
### 重载 Laravel-S 服务(平滑重启)
```bash
php bin/laravels reload
```
### 查看服务状态
```bash
php bin/laravels status
```
## WebSocket 连接地址
### 开发环境
```
ws://localhost:5200/ws?user_id={user_id}&token={token}
```
### 生产环境
```
wss://yourdomain.com/ws?user_id={user_id}&token={token}
```
## Nginx 配置示例
```nginx
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/project/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# WebSocket 代理配置
location /ws {
proxy_pass http://127.0.0.1:5200;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
```
## 使用场景
### 1. 实时通知
```php
// 发送系统通知
$webSocketService->sendSystemNotification(
'系统维护',
'系统将于今晚进行维护',
'warning'
);
```
### 2. 订单状态更新
```php
// 推送订单状态更新给相关人员
$webSocketService->pushDataUpdate(
[$order->user_id],
'order',
'update',
[
'id' => $order->id,
'status' => $order->status,
'updated_at' => $order->updated_at
]
);
```
### 3. 实时聊天
```javascript
// 发送私聊消息
ws.send('chat', {
to_user_id: 2,
content: '你好'
})
```
### 4. 数据监控
```php
// 推送系统监控数据到特定频道
$webSocketService->sendToChannel('system_monitor', 'monitor', [
'cpu_usage' => 75,
'memory_usage' => 80,
'disk_usage' => 60
]);
```
## 注意事项
1. **连接认证**: WebSocket 连接时需要提供 `user_id``token` 参数
2. **心跳机制**: 客户端默认每 30 秒发送一次心跳
3. **自动重连**: 连接断开后会自动尝试重连,最多重试 5 次
4. **并发限制**: Swoole Table 最多支持 102,400 个连接
5. **内存管理**: 注意内存泄漏问题,定期重启服务
6. **安全性**: 生产环境建议使用 WSS (WebSocket Secure)
7. **日志监控**: 查看日志文件 `storage/logs/swoole-YYYY-MM.log`
## 故障排查
### 1. 无法连接 WebSocket
- 检查 Laravel-S 服务是否启动
- 检查端口 5200 是否被占用
- 检查防火墙设置
- 查看日志文件
### 2. 连接频繁断开
- 检查网络稳定性
- 调整心跳间隔
- 检查服务器资源使用情况
### 3. 消息发送失败
- 检查用户是否在线
- 检查消息格式是否正确
- 查看错误日志
## 参考资料
- [Laravel-S 文档](https://github.com/hhxsv5/laravel-s)
- [Swoole 文档](https://www.swoole.com/)
- [WebSocket API](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket)
## 更新日志
### 2024-02-08
- ✅ 初始版本发布
- ✅ 实现基础 WebSocket 功能
- ✅ 实现消息推送功能
- ✅ 实现频道订阅功能
- ✅ 实现前端客户端封装
- ✅ 实现管理 API 接口

File diff suppressed because it is too large Load Diff

View File

@@ -1,444 +0,0 @@
# WebSocket 功能文档
## 概述
本系统使用 WebSocket 实现实时通信功能,支持消息推送、数据更新通知等实时功能。
## 技术栈
- **前端**: 原生 WebSocket API + Vue 3 Composable
- **后端**: Laravel-S (Swoole) WebSocket Server
- **协议**: WS/WSS
## 功能特性
### 1. 自动连接管理
- 登录后自动建立 WebSocket 连接
- 用户信息加载完成后自动重试连接
- 支持断线自动重连(最多 5 次)
- 退出登录时自动关闭连接
### 2. 消息推送
- 系统通知推送
- 数据更新通知
- 字典数据更新通知
- 任务提醒
### 3. 心跳机制
- 客户端每 30 秒发送心跳
- 保持连接活跃状态
- 检测连接状态
### 4. 消息管理
- 消息持久化存储localStorage
- 未读消息计数
- 消息分类筛选
- 消息分页显示
- 标记已读/删除消息
## 前端使用
### 1. 在组件中使用 WebSocket
```javascript
import { useWebSocket } from '@/composables/useWebSocket'
const { initWebSocket, closeWebSocket, isConnected, send } = useWebSocket()
// 初始化连接
onMounted(() => {
initWebSocket()
})
// 检查连接状态
if (isConnected()) {
console.log('WebSocket 已连接')
}
// 发送消息
send('message_type', { data: 'your data' })
// 关闭连接
onUnmounted(() => {
closeWebSocket()
})
```
### 2. 在 App.vue 中自动初始化
WebSocket 已在 App.vue 中自动集成,无需手动调用:
- 监听用户信息变化,自动初始化连接
- 组件卸载时自动关闭连接
- 消息数据自动恢复
### 3. 使用消息 Store
```javascript
import { useMessageStore } from '@/stores/modules/message'
const messageStore = useMessageStore()
// 添加消息
messageStore.addMessage({
type: 'notification',
title: '系统通知',
content: '这是一条通知'
})
// 标记已读
messageStore.markAsRead(messageId)
// 标记所有已读
messageStore.markAllAsRead()
// 删除消息
messageStore.removeMessage(messageId)
// 清空所有消息
messageStore.clearAll()
// 获取消息列表(分页)
const { list, total, page } = messageStore.getMessages({
page: 1,
pageSize: 10,
type: 'notification' // 可选:按类型过滤
})
// 格式化消息时间
const timeStr = messageStore.formatMessageTime(timestamp)
```
## 消息类型
### 支持的消息类型
| 类型 | 说明 | 枚举值 |
|------|------|--------|
| 系统通知 | 系统级别的通知消息 | `notification` |
| 任务提醒 | 任务相关提醒 | `task` |
| 警告消息 | 警告类消息 | `warning` |
| 错误消息 | 错误类消息 | `error` |
| 成功消息 | 成功类消息 | `success` |
| 信息消息 | 一般信息 | `info` |
### 消息优先级
| 优先级 | 说明 | 枚举值 |
|--------|------|--------|
| 低 | 低优先级消息 | `low` |
| 中 | 中等优先级消息 | `medium` |
| 高 | 高优先级消息 | `high` |
| 紧急 | 紧急消息 | `urgent` |
## WebSocket 消息格式
### 客户端发送格式
```json
{
"type": "message_type",
"data": {
"key": "value"
}
}
```
### 服务端推送格式
```json
{
"type": "notification",
"data": {
"title": "消息标题",
"content": "消息内容",
"type": "success",
"timestamp": 1234567890
}
}
```
### 数据更新消息格式
```json
{
"type": "data_update",
"data": {
"resource_type": "dictionary",
"action": "update",
"data": {},
"timestamp": 1234567890
}
}
```
## 后端使用
### 1. 发送消息给特定用户
```php
use App\Services\WebSocket\WebSocketService;
$wsService = app(WebSocketService::class);
// 发送给单个用户
$wsService->sendToUser($userId, [
'type' => 'notification',
'data' => [
'title' => '系统通知',
'content' => '这是一条通知',
'type' => 'info',
'timestamp' => time()
]
]);
// 发送给多个用户
$userIds = [1, 2, 3];
$sentTo = $wsService->sendToUsers($userIds, $data);
```
### 2. 广播消息
```php
// 广播给所有在线用户
$count = $wsService->broadcast([
'type' => 'notification',
'data' => [
'title' => '系统维护通知',
'content' => '系统将在 10 分钟后进行维护',
'type' => 'warning',
'timestamp' => time()
]
]);
// 广播给所有在线用户(排除指定用户)
$count = $wsService->broadcast($data, $excludeUserId);
```
### 3. 发送系统通知
```php
// 发送系统通知
$count = $wsService->sendSystemNotification(
'新版本发布',
'系统已更新到 v2.0',
'success'
);
// 发送通知给特定用户
$count = $wsService->sendNotificationToUsers(
[1, 2, 3],
'任务分配',
'您有新的待处理任务',
'task'
);
```
### 4. 发送数据更新通知
```php
// 发送数据更新给特定用户
$userIds = [1, 2, 3];
$sentTo = $wsService->pushDataUpdate(
$userIds,
'dictionary',
'update',
['id' => 1, 'name' => 'test']
);
// 发送数据更新到频道
$count = $wsService->pushDataUpdateToChannel(
'system_admin',
'user',
'create',
['id' => 10, 'username' => 'newuser']
);
```
### 5. 检查用户在线状态
```php
// 检查用户是否在线
$isOnline = $wsService->isUserOnline($userId);
// 获取在线用户数量
$count = $wsService->getOnlineUserCount();
// 获取所有在线用户 ID
$userIds = $wsService->getOnlineUserIds();
// 强制断开用户连接
$wsService->disconnectUser($userId);
```
## 顶部消息组件
### 功能说明
顶部消息组件位于 `layouts/components/userbar.vue`,提供以下功能:
1. **消息通知铃铛**
- 显示未读消息数量
- 点击打开消息列表
2. **消息列表**
- 按类型筛选消息(全部/通知/任务/警告)
- 显示消息标题、内容、时间
- 点击消息标记为已读
- 悬浮显示删除按钮
- 分页浏览
3. **操作按钮**
- 全部标为已读
- 清空全部消息
### 消息样式
- **未读消息**: 蓝色背景 + 左侧蓝条 + 红点标记
- **已读消息**: 普通样式
- **删除按钮**: 悬浮时显示
## 配置说明
### WebSocket URL 配置
`resources/admin/src/config/index.js` 中配置:
```javascript
export default {
// WebSocket URL如果不配置则使用当前域名
WS_URL: 'ws://localhost:8080',
// 其他配置...
}
```
### 后端 WebSocket 配置
`config/laravels.php` 中配置:
```php
'swoole' => [
'enable_coroutine' => true,
'worker_num' => 4,
'max_request' => 5000,
'max_request_grace' => 500,
// ... 其他配置
]
```
## 故障排查
### 1. WebSocket 连接失败
**可能原因**:
- WebSocket 服务未启动
- 端口被占用
- 防火墙阻止连接
- URL 配置错误
**解决方法**:
```bash
# 检查 Laravel-S 是否运行
php bin/laravels status
# 启动 Laravel-S
php bin/laravels start
# 检查端口是否被占用
netstat -ano | findstr :8080
```
### 2. 登录后 WebSocket 未连接
**可能原因**:
- 用户信息未加载完成
- token 无效
**解决方法**:
- 检查控制台日志
- 确认 `userStore.isUserInfoComplete()` 返回 true
- 查看 `getWebSocket` 调用参数
### 3. 消息未收到
**可能原因**:
- 消息处理器未注册
- 消息类型不匹配
- 网络问题
**解决方法**:
- 检查 `useWebSocket.js` 中的消息处理器注册
- 确认消息类型格式正确
- 查看网络面板 WebSocket 帧
## 开发建议
### 1. 测试 WebSocket 功能
```javascript
// 在浏览器控制台测试
import { useWebSocket } from '@/composables/useWebSocket'
import { useMessageStore } from '@/stores/modules/message'
const { send } = useWebSocket()
const messageStore = useMessageStore()
// 手动添加测试消息
messageStore.addMessage({
type: 'notification',
title: '测试消息',
content: '这是一条测试消息',
timestamp: Date.now()
})
// 发送测试消息到服务器
send('test', { message: 'hello' })
```
### 2. 添加新的消息类型
1. 在 `message.js` store 中添加类型枚举
2. 在 `userbar.vue` 中添加对应的 Tab
3. 在 `i18n` 中添加翻译
4. 在 `useWebSocket.js` 中添加处理逻辑
### 3. 自定义消息处理
```javascript
// 在 useWebSocket.js 中注册自定义处理器
ws.value.on('custom_event', handleCustomEvent)
function handleCustomEvent(data) {
console.log('收到自定义消息:', data)
// 处理逻辑
}
```
## 性能优化
1. **消息限制**: 最多存储 100 条消息,超出后自动删除旧消息
2. **分页加载**: 消息列表使用分页,避免一次性加载过多数据
3. **心跳机制**: 保持连接活跃,减少不必要的重连
4. **延迟加载**: 用户信息加载完成后才初始化连接
## 安全考虑
1. **Token 验证**: WebSocket 连接时发送 token 进行验证
2. **用户隔离**: 每个用户只能接收自己的消息
3. **消息过滤**: 根据权限过滤敏感消息
4. **连接限制**: 限制单个用户的连接数量
## 更新日志
### v1.0.0 (2024-01-18)
- 初始版本
- 实现基础 WebSocket 连接功能
- 实现消息推送和接收
- 实现消息管理 Store
- 实现顶部消息组件
- 支持中英文国际化