9.0 KiB
9.0 KiB
5.11 字典配置与使用
:::tip 本章概要 数据字典的完整使用流程,从新增字典类型到在代码中使用字典数据的实操指南。字典是系统中管理枚举值的标准方式,替代硬编码,实现"数据与代码分离"。 :::
数据字典结构
数据字典由两层组成:
字典类型(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 步:在后台添加字典类型
登录管理后台,进入「数据管理 → 字典管理」:
- 点击「新增」按钮
- 填写信息:
| 字段 | 值 |
|---|---|
| 字典名称 | 案例类型 |
| 字典编码 | example_level |
| 状态 | 启用 |
- 保存
第 2 步:添加字典项
在字典类型列表中点击「案例类型」,进入字典项管理:
- 添加字典项:
| 字典值 | 字典标签 | 排序 |
|---|---|---|
| 1 | 基础 | 1 |
| 2 | 进阶 | 2 |
| 3 | 高级 | 3 |
- 保存(保存即触发
DictItemObserver,相关缓存自动失效)
第 3 步:在 Service 中关联字典
在模块的 Service 中配置 serializeMaps(需 use HasSerializeMaps):
class ExampleService extends BaseService
{
use HasCrudOperations, HasQueryScopes, HasSerializeMaps, HasUniqueCheck;
// ... 其他配置 ...
/**
* 字典翻译配置:[字段 => 字典编码]
*
* @var array<string, string>
*/
protected array $serializeMaps = [
'type' => 'example_level',
'status' => 'example_status',
];
}
配置后,列表和详情接口会自动将 type=1 转换为 typeText='基础'。
第 4 步:在 FormRequest 中校验枚举值(可选)
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 自动转换(推荐)
// Service 中配置后,查询数据自动补全
protected array $serializeMaps = [
'status' => 'example_status',
];
// 查询结果自动包含 statusText 字段
// { "id": 1, "name": "测试", "status": 1, "statusText": "在用" }
方式 2:手动调用 DictCacheService
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:在导入时反向映射
// 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);
在前端使用
获取字典项
// ui/src/api/common/index.ts(实际代码)
/**
* 根据字典编码查询字典项数据
* @param code 字典编码
* @returns 返回结果
*/
export function getDictItemByCode(code) {
return http.request({
url: '/dict/item/getDictItemList/' + code,
method: 'GET',
});
}
下拉框使用
<template>
<el-select v-model="formData.status" placeholder="请选择状态">
<el-option
v-for="item in statusOptions"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { getDictItemByCode } from '@/api/common/index';
const statusOptions = ref([]);
onMounted(async () => {
statusOptions.value = await getDictItemByCode('example_status');
});
</script>
多个字典批量获取
// 同时获取多个字典选项
const [genderOptions, statusOptions] = await Promise.all([
getDictItemByCode('gender'),
getDictItemByCode('user_status'),
]);
字典值显示为 Tag
<template>
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
{{ row.statusText }}
</el-tag>
</template>
缓存管理
DictCacheService 采用两级缓存(请求级 + 持久缓存)。修改字典数据后的缓存失效:
# 手动刷新接口(认证组内)
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 正向翻译 |