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

10 KiB
Raw Blame History

字典缓存更新机制

概述

本文档说明前后端字典缓存的更新逻辑,确保在字典分类和字典项的增删改等操作后,前端字典缓存能够自动更新。

技术实现

1. 后端实现

1.1 DictionaryService 更新

app/Services/System/DictionaryService.php 中添加了 WebSocket 通知功能:

依赖注入:

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 消息格式

字典分类更新消息:

{
  "type": "dictionary_update",
  "data": {
    "action": "create|update|delete|batch_delete|batch_update_status",
    "resource_type": "dictionary",
    "data": {
      // 字典分类数据
    },
    "timestamp": 1234567890
  }
}

字典项更新消息:

{
  "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

生命周期钩子:

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_updatedictionary_item_update 事件
    • 接收到消息后调用对应的处理器
  5. 缓存刷新

    • 处理器调用 dictionaryStore.refresh(true) 强制刷新缓存
    • 从后端 API 重新加载所有字典数据
    • 更新 Pinia store 中的字典数据
    • 持久化到本地存储
  6. 用户反馈

    • 显示 "字典数据已更新" 的成功提示
    • 页面上的字典数据自动更新,无需手动刷新

使用示例

示例 1创建新字典分类

// 后端代码
$dictionary = $dictionaryService->create([
    'name' => '订单状态',
    'code' => 'order_status',
    'description' => '订单状态字典',
    'value_type' => 'string',
    'sort' => 1,
    'status' => true
]);

// 自动触发:
// 1. 清理 Redis 缓存
// 2. 广播 WebSocket 消息

前端自动刷新缓存并显示提示。

示例 2更新字典项

// 后端代码
$item = $dictionaryService->updateItem(1, [
    'label' => '已付款',
    'value' => 'paid',
    'sort' => 2
]);

// 自动触发:
// 1. 清理对应字典的 Redis 缓存
// 2. 广播 WebSocket 消息

前端自动刷新缓存并显示提示。

示例 3批量操作

// 后端代码
$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. 细粒度缓存更新

当前实现是全量刷新,未来可以优化为增量更新:

// 只更新受影响的字典
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. 权限控制

可以只向有权限的用户发送通知:

// 后端只向有字典管理权限的用户发送
$adminUserIds = User::whereHas('roles', function($query) {
    $query->where('name', 'admin');
})->pluck('id')->toArray();

$this->webSocketService->sendToUsers($adminUserIds, $message);

3. 消息队列

对于高并发场景,可以使用消息队列异步发送 WebSocket 通知:

// 使用 Laravel 队列
UpdateDictionaryCacheJob::dispatch($action, $data);

测试建议

1. 单元测试

测试后端 WebSocket 通知是否正确发送:

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正常运行