# 5.11 字典配置与使用 :::tip 本章概要 数据字典的完整使用流程,从新增字典类型到在代码中使用字典数据的实操指南。字典是系统中管理枚举值的标准方式,替代硬编码,实现"数据与代码分离"。 ::: ## 数据字典结构 数据字典由两层组成: ```text 字典类型(laravel_dict) 字典项(laravel_dict_item) ├── gender(性别) ├── 1 → 男 │ ├── 2 → 女 │ └── 3 → 保密 ├── user_status(用户状态) ├── 1 → 正常 │ └── 2 → 禁用 ├── example_status(案例状态) ├── 1 → 在用 │ └── 2 → 停用 └── ... ``` | 层级 | 表 | 说明 | |------|-----|------| | 字典类型 | `laravel_dict` | 字典分类(如"性别""用户状态") | | 字典项 | `laravel_dict_item` | 字典的具体选项(如"1-男""2-女") | ### 表结构 **laravel_dict(字典主表):** | 字段 | 类型 | 说明 | 示例 | |------|------|------|------| | `id` | INT | 主键 | 1 | | `name` | VARCHAR(50) | 字典名称 | 性别 | | `code` | VARCHAR(50) | 字典编码(唯一) | gender | | `remark` | VARCHAR(500) | 备注 | 性别字典 | | `is_delete` | TINYINT(1) | 软删除 | 0 | | `create_user` | VARCHAR(50) | 创建人 | admin | | `create_time` | DATETIME | 创建时间 | | | `update_user` | VARCHAR(50) | 更新人 | | | `update_time` | DATETIME | 更新时间 | | **laravel_dict_item(字典项表):** | 字段 | 类型 | 说明 | 示例 | |------|------|------|------| | `id` | INT | 主键 | 1 | | `dict_id` | INT | 字典 ID(外键) | 1 | | `name` | VARCHAR(50) | 项名称(显示标签) | 男 | | `value` | VARCHAR(50) | 项值(存储值) | 1 | | `sort` | INT | 排序 | 1 | | `note` | VARCHAR(200) | 备注 | | | `is_delete` + 四个审计字段 | | 公共字段 | | ## 系统内置字典 | 字典编码 | 字典名称 | 字典项 | |----------|---------|--------| | `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=停用 | (以数据库初始化脚本中的实际字典项为准,可在「数据管理 → 字典管理」中查看。) ## 实操:新增一个字典类型 ### 场景 需要为"案例类型"模块添加一个下拉选项:`1-基础 2-进阶 3-高级`。 ### 第 1 步:在后台添加字典类型 登录管理后台,进入「数据管理 → 字典管理」: 1. 点击「新增」按钮 2. 填写信息: | 字段 | 值 | |------|-----| | 字典名称 | 案例类型 | | 字典编码 | example_level | | 状态 | 启用 | 3. 保存 ### 第 2 步:添加字典项 在字典类型列表中点击「案例类型」,进入字典项管理: 1. 添加字典项: | 字典值 | 字典标签 | 排序 | |--------|---------|------| | 1 | 基础 | 1 | | 2 | 进阶 | 2 | | 3 | 高级 | 3 | 2. 保存(保存即触发 `DictItemObserver`,相关缓存自动失效) ### 第 3 步:在 Service 中关联字典 在模块的 Service 中配置 `serializeMaps`(需 `use HasSerializeMaps`): ```php class ExampleService extends BaseService { use HasCrudOperations, HasQueryScopes, HasSerializeMaps, HasUniqueCheck; // ... 其他配置 ... /** * 字典翻译配置:[字段 => 字典编码] * * @var array */ protected array $serializeMaps = [ 'type' => 'example_level', 'status' => 'example_status', ]; } ``` 配置后,列表和详情接口会自动将 `type=1` 转换为 `typeText='基础'`。 ### 第 4 步:在 FormRequest 中校验枚举值(可选) ```php class ExampleRequest extends BaseRequest { public function rules(): array { return [ 'name' => 'required|string|max:255', 'type' => 'nullable|integer|in:1,2,3', // 限制为合法枚举值 'status' => 'nullable|integer|in:1,2', ]; } } ``` ## 在后端代码中使用 ### 方式 1:通过 serializeMaps 自动转换(推荐) ```php // Service 中配置后,查询数据自动补全 protected array $serializeMaps = [ 'status' => 'example_status', ]; // 查询结果自动包含 statusText 字段 // { "id": 1, "name": "测试", "status": 1, "statusText": "在用" } ``` ### 方式 2:手动调用 DictCacheService ```php use App\Services\DictCacheService; // 根据值获取名称 $text = DictCacheService::getText('example_status', 1); // '在用' // 根据名称反向获取值(导入时用) $value = DictCacheService::getValue('example_status', '在用'); // '1' // 获取下拉框选项 $options = DictCacheService::getOptions('example_status'); // [['label'=>'在用','value'=>'1'], ['label'=>'停用','value'=>'2']] // 批量获取多个字典映射(列表翻译一次取词) $maps = DictCacheService::getBatchTextMaps(['example_type', 'example_status']); ``` ### 方式 3:在导入时反向映射 ```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); ``` ## 在前端使用 ### 获取字典项 ```typescript // ui/src/api/common/index.ts(实际代码) /** * 根据字典编码查询字典项数据 * @param code 字典编码 * @returns 返回结果 */ export function getDictItemByCode(code) { return http.request({ url: '/dict/item/getDictItemList/' + code, method: 'GET', }); } ``` ### 下拉框使用 ```vue ``` ### 多个字典批量获取 ```typescript // 同时获取多个字典选项 const [genderOptions, statusOptions] = await Promise.all([ getDictItemByCode('gender'), getDictItemByCode('user_status'), ]); ``` ### 字典值显示为 Tag ```vue ``` ## 缓存管理 DictCacheService 采用两级缓存(请求级 + 持久缓存)。修改字典数据后的缓存失效: ```bash # 手动刷新接口(认证组内) GET /api/dict/refreshCache # 命令行兜底 php artisan dict:clear-cache ``` ### 缓存失效时机 | 时机 | 操作 | 说明 | |------|------|------| | 字典项新增/修改/删除 | **自动清除** | `DictItemObserver` 监听模型事件 | | 手动刷新 | `GET /api/dict/refreshCache` | 清除所有字典缓存(兜底) | | 命令行清理 | `php artisan dict:clear-cache` | 直接改库等绕过模型事件时使用 | | 缓存过期 | 自动失效 | TTL 见 `DictCacheService::CACHE_TTL` | :::tip 正常走后台接口修改字典会自动失效缓存;只有**绕过模型直接操作数据库**(如手工 SQL 改字典项)才需要手动刷新。 ::: ## 字典 vs 硬编码 | 方式 | 优点 | 缺点 | |------|------|------| | **字典(推荐)** | 可在后台动态修改,无需改代码重启 | 需要额外的数据库查询(有缓存) | | 硬编码 | 简单直接,无额外查询 | 修改需改代码重新部署 | **何时用字典:** 值可能变化(状态、类型、分类)、需要前端下拉框、需要多处复用 **何时硬编码:** 值几乎不变、单次使用、性能极致敏感 ## 命名规范 | 类型 | 命名规范 | 示例 | |------|---------|------| | 字典编码 | 小写下划线 | `example_type`、`user_status` | | 字典值 | 数字字符串 | `1`、`2`、`3` | | 字典标签 | 中文名称 | `基础`、`启用` | | 字段名 | 小写下划线 | `type`、`status` | | 显示名字段 | 字段名 + Text | `typeText`、`statusText` | ## 最佳实践 | 实践 | 说明 | |------|------| | 编码统一 | 同一字典编码全局唯一,如 `user_status` | | 值用数字 | 字典值使用数字字符串(`1`、`2`),便于排序和比较 | | 标签用中文 | 字典标签使用中文(`启用`、`禁用`),便于前端直接显示 | | 排序有序 | 通过 `sort` 字段控制下拉框选项顺序 | | 备注说明 | 在 `note` 字段记录特殊值的含义,便于维护 | | FormRequest 校验 | 在 rules 中用 `in:1,2` 限制合法值 | | 导入导出 | 导入时用 `getValue` 反向映射,导出时用 `getText`/`serializeMaps` 正向翻译 |