Files
laravel_swoole/docs/DICTIONARY_CACHE_UPDATE.md
2026-02-18 17:54:07 +08:00

416 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 字典缓存更新机制
## 概述
本文档说明前后端字典缓存的更新逻辑,确保在字典分类和字典项的增删改等操作后,前端字典缓存能够自动更新。
## 技术实现
### 1. 后端实现
#### 1.1 DictionaryService 更新
`app/Services/System/DictionaryService.php` 中添加了 WebSocket 通知功能:
**依赖注入:**
```php
protected $webSocketService;
public function __construct(WebSocketService $webSocketService)
{
$this->webSocketService = $webSocketService;
}
```
**通知方法:**
1. **字典分类更新通知** (`notifyDictionaryUpdate`)
- 触发时机:创建、更新、删除、批量删除、批量更新状态
- 消息类型:`dictionary_update`
2. **字典项更新通知** (`notifyDictionaryItemUpdate`)
- 触发时机:创建、更新、删除、批量删除、批量更新状态
- 消息类型:`dictionary_item_update`
**修改的方法列表:**
- `create()` - 创建字典分类后发送通知
- `update()` - 更新字典分类后发送通知
- `delete()` - 删除字典分类后发送通知
- `batchDelete()` - 批量删除字典分类后发送通知
- `batchUpdateStatus()` - 批量更新状态后发送通知
- `createItem()` - 创建字典项后发送通知
- `updateItem()` - 更新字典项后发送通知
- `deleteItem()` - 删除字典项后发送通知
- `batchDeleteItems()` - 批量删除字典项后发送通知
- `batchUpdateItemsStatus()` - 批量更新字典项状态后发送通知
#### 1.2 WebSocket 消息格式
**字典分类更新消息:**
```json
{
"type": "dictionary_update",
"data": {
"action": "create|update|delete|batch_delete|batch_update_status",
"resource_type": "dictionary",
"data": {
// 字典分类数据
},
"timestamp": 1234567890
}
}
```
**字典项更新消息:**
```json
{
"type": "dictionary_item_update",
"data": {
"action": "create|update|delete|batch_delete|batch_update_status",
"resource_type": "dictionary_item",
"data": {
// 字典项数据
},
"timestamp": 1234567890
}
}
```
### 2. 前端实现
#### 2.1 WebSocket Composable
创建了 `resources/admin/src/composables/useWebSocket.js` 来处理 WebSocket 连接和消息监听:
**主要功能:**
1. **初始化 WebSocket 连接**
- 检查用户登录状态
- 验证用户信息完整性
- 建立连接并注册消息处理器
2. **消息处理器**
- `handleDictionaryUpdate` - 处理字典分类更新
- `handleDictionaryItemUpdate` - 处理字典项更新
3. **缓存刷新**
- 接收到更新通知后,自动刷新字典缓存
- 显示成功提示消息
#### 2.2 App.vue 集成
`resources/admin/src/App.vue` 中集成了 WebSocket
**生命周期钩子:**
```javascript
onMounted(async () => {
// ... 其他初始化代码
// 初始化 WebSocket 连接
if (userStore.isLoggedIn()) {
initWebSocket()
}
})
onUnmounted(() => {
// 关闭 WebSocket 连接
closeWebSocket()
})
```
## 工作流程
### 完整流程图
```
用户操作(增删改字典)
后端 Controller 调用 Service
Service 执行数据库操作
Service 清理后端缓存Redis
Service 发送 WebSocket 广播通知
WebSocket 推送消息到所有在线客户端
前端接收 WebSocket 消息
触发相应的消息处理器
刷新前端字典缓存
显示成功提示
```
### 详细步骤
1. **用户操作**
- 管理员在后台管理界面进行字典分类或字典项的增删改操作
- 例如:创建新字典分类、修改字典项、批量删除等
2. **后端处理**
- 接收请求并验证数据
- 执行数据库操作INSERT/UPDATE/DELETE
- 清理 Redis 缓存(`DictionaryService::clearCache()`
- 通过 WebSocket 广播更新通知
3. **WebSocket 通知**
- 服务端向所有连接的 WebSocket 客户端广播消息
- 消息包含操作类型、资源类型和更新的数据
4. **前端接收**
- App.vue 在 onMounted 时初始化 WebSocket 连接
- 注册消息处理器监听 `dictionary_update``dictionary_item_update` 事件
- 接收到消息后调用对应的处理器
5. **缓存刷新**
- 处理器调用 `dictionaryStore.refresh(true)` 强制刷新缓存
- 从后端 API 重新加载所有字典数据
- 更新 Pinia store 中的字典数据
- 持久化到本地存储
6. **用户反馈**
- 显示 "字典数据已更新" 的成功提示
- 页面上的字典数据自动更新,无需手动刷新
## 使用示例
### 示例 1创建新字典分类
```php
// 后端代码
$dictionary = $dictionaryService->create([
'name' => '订单状态',
'code' => 'order_status',
'description' => '订单状态字典',
'value_type' => 'string',
'sort' => 1,
'status' => true
]);
// 自动触发:
// 1. 清理 Redis 缓存
// 2. 广播 WebSocket 消息
```
前端自动刷新缓存并显示提示。
### 示例 2更新字典项
```php
// 后端代码
$item = $dictionaryService->updateItem(1, [
'label' => '已付款',
'value' => 'paid',
'sort' => 2
]);
// 自动触发:
// 1. 清理对应字典的 Redis 缓存
// 2. 广播 WebSocket 消息
```
前端自动刷新缓存并显示提示。
### 示例 3批量操作
```php
// 后端代码
$dictionaryService->batchUpdateStatus([1, 2, 3], false);
// 自动触发:
// 1. 清理所有相关字典的 Redis 缓存
// 2. 广播 WebSocket 消息(包含批量更新的 ID
```
前端自动刷新缓存并显示提示。
## 注意事项
### 1. WebSocket 连接
- WebSocket 仅在用户登录后建立连接
- 连接失败会自动重试(最多 5 次)
- 页面卸载时会自动关闭连接
### 2. 缓存一致性
- 后端缓存使用 RedisTTL 为 3600 秒1 小时)
- 前端缓存使用 Pinia + 本地存储持久化
- WebSocket 通知确保前后端缓存同步更新
### 3. 错误处理
- WebSocket 连接失败不影响页面正常使用
- 缓存刷新失败会在控制台输出错误日志
- 不会阻塞用户操作
### 4. 性能考虑
- 批量操作会一次性清理相关缓存
- WebSocket 广播向所有在线用户推送
- 前端刷新时会重新加载所有字典数据
## 扩展建议
### 1. 细粒度缓存更新
当前实现是全量刷新,未来可以优化为增量更新:
```javascript
// 只更新受影响的字典
async function handleDictionaryUpdate(data) {
const { action, data: dictData } = data
if (action === 'update' && dictData.code) {
// 只更新特定的字典
await dictionaryStore.getDictionary(dictData.code, true)
} else {
// 全量刷新
await dictionaryStore.refresh(true)
}
}
```
### 2. 权限控制
可以只向有权限的用户发送通知:
```php
// 后端只向有字典管理权限的用户发送
$adminUserIds = User::whereHas('roles', function($query) {
$query->where('name', 'admin');
})->pluck('id')->toArray();
$this->webSocketService->sendToUsers($adminUserIds, $message);
```
### 3. 消息队列
对于高并发场景,可以使用消息队列异步发送 WebSocket 通知:
```php
// 使用 Laravel 队列
UpdateDictionaryCacheJob::dispatch($action, $data);
```
## 测试建议
### 1. 单元测试
测试后端 WebSocket 通知是否正确发送:
```php
public function testDictionaryUpdateSendsWebSocketNotification()
{
$this->mockWebSocketService();
$dictionary = DictionaryService::create([
'name' => 'Test',
'code' => 'test'
]);
// 验证 WebSocket 广播被调用
}
```
### 2. 集成测试
1. 启动后端服务Laravel-S
2. 启动前端开发服务器
3. 在浏览器中登录系统
4. 打开开发者工具的 Network -> WS 标签查看 WebSocket 消息
5. 执行字典增删改操作
6. 验证:
- WebSocket 消息是否正确接收
- 缓存是否自动刷新
- 页面数据是否更新
- 提示消息是否显示
### 3. 并发测试
1. 打开多个浏览器窗口并登录
2. 在一个窗口中进行字典操作
3. 验证所有窗口的缓存是否同步更新
## 故障排查
### 问题 1前端未收到 WebSocket 消息
**可能原因:**
- WebSocket 服务未启动
- 网络连接问题
- 用户未登录
- Laravel-S 未运行(使用普通 PHP 运行时)
**解决方法:**
1. 检查 Laravel-S 服务是否启动:`php bin/laravels status`
2. 检查浏览器控制台是否有 WebSocket 错误
3. 确认用户已登录且有 token
4. 确认是否在 Laravel-S 环境下运行WebSocket 通知仅在 Laravel-S 环境下有效)
**注意:**
- WebSocket 通知功能依赖于 Laravel-S (Swoole) 环境
- 在普通 PHP 环境下运行时WebSocket 通知会优雅降级(不发送通知,但不影响功能)
- 仍需手动刷新页面或使用 API 轮询来获取最新数据
### 问题 2后端 WebSocket 通知发送失败
**可能原因:**
- Laravel-S 未运行
- Swoole 服务器未启动
- WebSocket 服务实例获取失败
**解决方法:**
1. 确认在 Laravel-S 环境下运行:`php bin/laravels start`
2. 检查 Laravel-S 配置文件 `config/laravels.php`
3. 查看后端日志:`tail -f storage/logs/laravel.log`
**注意:**
- 如果未在 Laravel-S 环境下运行,后端会记录警告日志,但不会报错
- 字典数据仍会正常更新到数据库和 Redis 缓存
- 只是前端不会收到自动更新通知
### 问题 3缓存未更新
**可能原因:**
- WebSocket 消息处理失败
- API 请求失败
- 前端未连接 WebSocket
**解决方法:**
1. 查看浏览器控制台错误日志
2. 检查网络请求是否成功
3. 手动刷新页面验证 API 是否正常
4. 确认 WebSocket 连接状态(浏览器开发者工具 Network -> WS
### 问题 3通知频繁弹出
**可能原因:**
- 批量操作触发了多次通知
**解决方法:**
1. 优化后端批量操作,只发送一次通知
2. 前端添加防抖/节流逻辑
## 总结
通过 WebSocket 实现的字典缓存自动更新机制,确保了前后端数据的一致性,提升了用户体验。用户无需手动刷新页面即可获取最新的字典数据。
### 优势
- ✅ 实时更新,无需手动刷新
- ✅ 多端同步,所有在线用户自动更新
- ✅ 操作透明,用户有明确的反馈
- ✅ 易于扩展,可应用于其他数据类型
### 限制
- 需要稳定的 WebSocket 连接
- 当前实现为全量刷新,可以优化为增量更新
- 依赖后端服务Laravel-S正常运行