# 5.10 字典模块 :::tip 概述 字典体系由三个服务组成:`DictService`(字典 CRUD,`DictController` 注入)、`DictItemService`(字典项 CRUD)、`DictCacheService`(字典项读取、正反向转换与缓存管理,本页主体)。读取侧采用两级缓存(请求级 + 持久缓存),同一请求内不重复查库,跨请求复用持久缓存;字典项变更时 `DictItemObserver` 自动失效对应缓存。 ::: ## 字典体系架构 ```text ┌─────────────────────────────────────────────────────────────────────┐ │ 字典体系 │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ laravel_dict │ │ laravel_dict_item│ │ │ │ 字典主表 │ │ 字典项表 │ │ │ │ │ │ │ │ │ │ id │◄───│ dict_id │ │ │ │ name (字典名称) │ │ name (项名称) │ │ │ │ code (字典编码) │ │ value (项值) │ │ │ │ remark │ │ sort (排序) │ │ │ └──────────────────┘ │ note (备注) │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ DictCacheService │ │ Service 层 │ │ │ │ 字典服务 │ │ serializeMaps │ │ │ │ │ │ 自动翻译 │ │ │ │ getText() │ │ │ │ │ │ getValue() │◄───│ 'status' => │ │ │ │ getOptions() │ │ 'user_status' │ │ │ │ clearCache() │ │ │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ DictItemObserver(字典项变更 → 自动清除所属字典缓存) │ │ │ └──────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` ## 缓存架构 ```text 请求级缓存(静态变量) ← 同一请求内不重复查库 │ 未命中 ▼ 持久缓存(cache() 门面,1小时) ← 跨请求复用(Redis) │ 未命中 ▼ 数据库查询(dict → dict_item) ← 查询后写入两级缓存 ``` ### 缓存 Key 格式 | 缓存类型 | Key 格式 | 存储内容 | |----------|---------|---------| | 请求级 | `$localCache['gender']` | `['1'=>'男', '2'=>'女']` | | 持久缓存 | `dict_gender`(前缀 `CACHE_PREFIX='dict_'`) | `['1'=>'男', '2'=>'女']` | ### 缓存生命周期 ```text 1. 首次查询 → 数据库 → 写入持久缓存 + 请求级缓存 2. 同一请求再次命中 → 直接返回请求级缓存(最快) 3. 不同请求命中 → 读取持久缓存(不查库) 4. 缓存过期(TTL 见 DictCacheService::CACHE_TTL)→ 重新查库 → 更新两级缓存 5. 字典项变更 → DictItemObserver 自动调用 clearCache() → 下次查询重新加载 ``` ## 核心方法 ### getText — 根据值获取名称(最常用) ```php DictCacheService::getText('gender', 1); // '男' DictCacheService::getText('user_status', 1); // '正常' DictCacheService::getText('gender', ''); // ''(空值返回空字符串) DictCacheService::getText('gender', null); // ''(空值返回空字符串) ``` **用途:** 列表展示时将数据库值转换为可读名称。 ### getValue — 根据名称反向获取值 ```php DictCacheService::getValue('gender', '男'); // '1' DictCacheService::getValue('user_status', '正常'); // '1' DictCacheService::getValue('gender', ''); // ''(空值返回空字符串) ``` **用途:** Excel 导入时将中文文字转换为数据库存储的数值(`ExampleService::import()` 即用此方法反查状态文本)。 ### getOptions — 获取下拉框选项 ```php DictCacheService::getOptions('gender'); // [ // ['label' => '男', 'value' => '1'], // ['label' => '女', 'value' => '2'], // ] ``` **用途:** 前端下拉框、单选框、复选框组件的数据源。 ### getItems — 获取值→名称映射 ```php DictCacheService::getItems('gender'); // ['1' => '男', '2' => '女'] ``` ### getBatchTextMaps — 批量获取多个字典的映射 ```php DictCacheService::getBatchTextMaps(['gender', 'user_status']); // [ // 'gender' => ['1' => '男', '2' => '女'], // 'user_status' => ['1' => '正常', '2' => '禁用'], // ] ``` **用途:** 列表翻译时一次取多个字典,避免逐个查询。 ### clearCache / clearAllCache — 缓存管理 ```php DictCacheService::clearCache('gender'); // 清除指定字典的缓存 DictCacheService::clearCache(); // 仅清空请求级缓存 DictCacheService::clearAllCache(); // 清除所有字典持久缓存 ``` ## 方法清单 | 方法 | 说明 | 返回值 | |------|------|--------| | `getText($dictCode, $value)` | 值 → 名称 | `string` | | `getValue($dictCode, $text)` | 名称 → 值 | `string` | | `getItems($dictCode)` | 获取值→名称映射 | `array` `['1'=>'男', '2'=>'女']` | | `getOptions($dictCode)` | 获取下拉框选项 | `array` `[['label'=>'男','value'=>'1'], ...]` | | `getBatchTextMaps($codes)` | 批量获取多个字典映射 | `array` `[code => map, ...]` | | `clearCache($dictCode?)` | 清除指定/请求级缓存 | `void` | | `clearAllCache()` | 清除所有字典缓存 | `void` | ## 在 Service 中使用 ### 自动翻译(serializeMaps) 配置 `serializeMaps` 后(需 `use HasSerializeMaps`),查询数据时自动补全 `{字段名}Text` 字段,无需手动调用 DictCacheService: ```php class UserService extends BaseTenantService { /** * 字典翻译配置:[字段 => 字典编码] * * @var array */ protected array $serializeMaps = [ 'gender' => 'gender', 'status' => 'user_status', ]; } ``` **效果:** ```text 数据库:{ gender: 1, status: 1 } │ ▼ processSerializeMapsList() │ DictCacheService 批量取词(getBatchTextMaps,同一请求只查一次) │ getText('gender', 1) → '男' │ getText('user_status', 1) → '正常' ▼ 前端接收:{ gender: 1, genderText: '男', status: 1, statusText: '正常' } ``` ### 手动调用 ```php // 在 Service 中手动调用 $genderText = DictCacheService::getText('gender', $user['gender']); $options = DictCacheService::getOptions('gender'); ``` ## 在 Controller 中使用 ### 获取字典项(前端下拉数据源) ```bash # routes/api.php(认证组内) GET /api/dict/item/getDictItemList/{code} ``` ### 刷新字典缓存 ```php /** * 刷新字典缓存 * * GET /api/dict/refreshCache */ public function refreshCache(): JsonResponse { DictCacheService::clearAllCache(); return $this->success(null, '缓存刷新成功'); } ``` ## 在导入导出中使用 ### 导入时:文字转值 ```php // ExampleService::import()(来自项目实际代码) foreach ($data as &$row) { // Excel 中"在用" → 数据库中 1 if (! empty($row['status']) && ! is_numeric($row['status'])) { $row['status'] = DictCacheService::getValue('example_status', $row['status']); } } unset($row); ``` ### 导出时:值转文字 ```php // 导出表头使用 Text 后缀字段(由 serializeMaps 自动补全) $headers = [ 'name' => '名称', 'statusText' => '状态', // 自动翻译后的字段 ]; ``` ## 前端集成 ### 获取字典选项 ```typescript // API export function getDictItemByCode(code: string) { return http.request({ url: `/dict/item/getDictItemList/${code}`, method: 'GET' }); } // 使用 const genderOptions = ref([]); onMounted(async () => { genderOptions.value = await getDictItemByCode('gender'); }); ``` ### 下拉框组件 ```vue ``` ## 数据库表结构 ### laravel_dict(字典主表) | 字段 | 类型 | 说明 | 示例 | |------|------|------|------| | `id` | INT | 主键 | 1 | | `name` | VARCHAR(50) | 字典名称 | 性别 | | `code` | VARCHAR(50) | 字典编码(唯一) | gender | | `remark` | VARCHAR(500) | 备注 | 性别字典 | | `...` | | 公共字段 | | ### laravel_dict_item(字典项表) | 字段 | 类型 | 说明 | 示例 | |------|------|------|------| | `id` | INT | 主键 | 1 | | `dict_id` | INT | 字典 ID(外键) | 1 | | `name` | VARCHAR(50) | 项名称(显示标签) | 男 | | `value` | VARCHAR(50) | 项值 | 1 | | `sort` | INT | 排序 | 1 | | `note` | VARCHAR(200) | 备注 | | | `...` | | 公共字段 | | ## 字典编码规范 | 字典编码 | 名称 | 项值 | 用途 | |----------|------|------|------| | `gender` | 性别 | 1=男, 2=女, 3=保密 | 用户性别 | | `user_status` | 用户状态 | 1=正常, 2=禁用 | 用户账号状态 | | `tenant_status` | 租户状态 | 1=启用, 0=禁用 | 租户状态 | | `example_type` | 案例类型 | 1=类型1, 2=类型2, 3=类型3, 4=类型4 | 案例分类 | | `example_status` | 案例状态 | 1=在用, 2=停用 | 案例状态 | (项值以数据库初始化脚本中的实际字典项为准。) ## 缓存管理 ### 缓存失效时机 | 时机 | 操作 | 说明 | |------|------|------| | 字典项新增/修改/删除 | **`DictItemObserver` 自动清除** | 模型事件触发,无需手动 | | 手动刷新 | `GET /api/dict/refreshCache` | 清除所有字典缓存 | | 命令行清理 | `php artisan dict:clear-cache` | 运维兜底 | | 缓存过期 | 自动失效 | TTL 见 `CACHE_TTL` | :::tip 缓存一致性已自动保障 字典项(`DictItem`)保存/删除时 `DictItemObserver` 自动调用 `clearCache($code)`——业务代码无需手动刷新;上面的手动接口与命令仅作兜底(如直接改库绕过模型事件)。 ::: ## 安全特性 | 特性 | 说明 | |------|------| | 两级缓存 | 请求级 + 持久缓存,减少数据库压力 | | 空值安全 | `getText`/`getValue` 对 null/空字符串返回空字符串 | | 类型安全 | 值统一转为字符串比较,避免类型松散比较问题 | | 缓存隔离 | 每个字典编码独立缓存,互不影响 | | 自动失效 | Observer 保证数据变更后缓存不脏 |