Become a sponsor

概述
DictService 提供字典项的读取、正反向转换与缓存管理,是字典体系的核心服务。采用两级缓存(请求级 + 持久缓存),同一请求内不重复查库,跨请求复用持久缓存。
┌─────────────────────────────────────────────────────────────────────┐
│ 字典体系 │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ think_dict │ │ think_dict_item │ │
│ │ 字典主表 │ │ 字典项表 │ │
│ │ │ │ │ │
│ │ id │◄───│ dict_id │ │
│ │ name (字典名称) │ │ name (项名称) │ │
│ │ code (字典编码) │ │ value (项值) │ │
│ │ remark │ │ sort (排序) │ │
│ └──────────────────┘ │ note (备注) │ │
│ └──────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ DictService │ │ Logic 层 │ │
│ │ 字典服务 │ │ serializeMaps │ │
│ │ │ │ 自动翻译 │ │
│ │ getText() │ │ │ │
│ │ getValue() │◄───│ 'status' => │ │
│ │ getOptions() │ │ 'user_status' │ │
│ │ clearCache() │ │ │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘请求级缓存(static::$localCache) ← 同一请求内不重复查库
│ 未命中
▼
持久缓存(cache() 门面,1小时) ← 跨请求复用(file/redis)
│ 未命中
▼
数据库查询(dict → dict_item) ← 查询后写入两级缓存| 缓存类型 | Key 格式 | 存储内容 |
|---|---|---|
| 请求级 | $localCache['gender'] | ['1'=>'男', '2'=>'女'] |
| 请求级(完整) | $localCache['full_gender'] | [{id, name, value, sort, note}, ...] |
| 持久缓存 | dict_gender | ['1'=>'男', '2'=>'女'] |
| 持久缓存(完整) | dict_full_gender | [{id, name, value, sort, note}, ...] |
1. 首次查询 → 数据库 → 写入持久缓存 + 请求级缓存
2. 同一请求再次命中 → 直接返回请求级缓存(最快)
3. 不同请求命中 → 读取持久缓存(不查库)
4. 缓存过期(1小时)→ 重新查库 → 更新两级缓存
5. 字典数据变更 → 调用 clearCache() 清除 → 下次查询重新加载DictService::getText('gender', 1); // '男'
DictService::getText('user_status', 1); // '启用'
DictService::getText('gender', ''); // ''(空值返回空字符串)
DictService::getText('gender', null); // ''(空值返回空字符串)用途: 列表展示时将数据库值转换为可读名称。
DictService::getValue('gender', '男'); // '1'
DictService::getValue('user_status', '启用'); // '1'
DictService::getValue('gender', ''); // ''(空值返回空字符串)用途: Excel 导入时将中文文字转换为数据库存储的数值。
DictService::getOptions('gender');
// [
// ['label' => '男', 'value' => '1'],
// ['label' => '女', 'value' => '2'],
// ]用途: 前端下拉框、单选框、复选框组件的数据源。
DictService::getFullItems('gender');
// [
// ['id' => 1, 'name' => '男', 'value' => '1', 'sort' => 1, 'note' => ''],
// ['id' => 2, 'name' => '女', 'value' => '2', 'sort' => 2, 'note' => ''],
// ]用途: 需要 id、排序、备注等完整信息的场景。
DictService::clearCache('gender'); // 清除指定字典的缓存
DictService::clearCache(); // 仅清空请求级缓存
DictService::clearAllCache(); // 清除所有字典持久缓存| 方法 | 说明 | 返回值 |
|---|---|---|
getText($code, $value) | 值 → 名称 | string |
getValue($code, $text) | 名称 → 值 | string |
getItems($code) | 获取值→名称映射 | array ['1'=>'男', '2'=>'女'] |
getOptions($code) | 获取下拉框选项 | array [['label'=>'男','value'=>'1'], ...] |
getFullItems($code) | 获取完整字典项 | array [{id, name, value, sort, note}, ...] |
clearCache($code?) | 清除指定/请求级缓存 | void |
clearAllCache() | 清除所有字典缓存 | void |
配置 serializeMaps 后,查询数据时自动补全 {字段名}Text 字段,无需手动调用 DictService:
class UserLogic extends BaseTenantLogic
{
/**
* 枚举显示名映射
*
* @var array
*/
protected array $serializeMaps = [
'gender' => 'gender',
'status' => 'user_status',
];
}效果:
数据库:{ gender: 1, status: 1 }
│
▼ processSerializeMaps()
│ DictService::getText('gender', 1) → '男'
│ DictService::getText('user_status', 1) → '启用'
▼
前端接收:{ gender: 1, genderText: '男', status: 1, statusText: '启用' }// 在 Logic 或 Service 中手动调用
$genderText = DictService::getText('gender', $user['gender']);
$options = DictService::getOptions('gender');// app/controller/DictController.php
/**
* 获取字典项下拉列表
*
* GET /dict/getOptions/{code}
*/
public function getOptions(string $code): Json
{
$options = DictService::getOptions($code);
return $this->success($options);
}/**
* 刷新字典缓存
*
* GET /dict/refreshCache
*/
public function refreshCache(): Json
{
DictService::clearAllCache();
return $this->success(null, '缓存刷新成功');
}// UserLogic::import()
foreach ($data as &$row) {
// Excel 中"男" → 数据库中 1
if (!empty($row['gender']) && !is_numeric($row['gender'])) {
$row['gender'] = DictService::getValue('gender', $row['gender']);
}
// Excel 中"启用" → 数据库中 1
if (!empty($row['status']) && !is_numeric($row['status'])) {
$row['status'] = DictService::getValue('user_status', $row['status']);
}
}// 导出表头使用 Text 后缀字段
$headers = [
'name' => '姓名',
'genderText' => '性别', // 自动翻译后的字段
'statusText' => '状态', // 自动翻译后的字段
];// API
export function getDictOptions(code: string) {
return http.request({ url: `/dict/getOptions/${code}`, method: 'GET' });
}
// 使用
const genderOptions = ref([]);
onMounted(async () => {
genderOptions.value = await getDictOptions('gender');
});<template>
<el-select v-model="form.gender" placeholder="请选择性别">
<el-option
v-for="item in genderOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>| 字段 | 类型 | 说明 | 示例 |
|---|---|---|---|
id | INT | 主键 | 1 |
name | VARCHAR(50) | 字典名称 | 性别 |
code | VARCHAR(50) | 字典编码(唯一) | gender |
remark | VARCHAR(500) | 备注 | 性别字典 |
... | 公共字段 |
| 字段 | 类型 | 说明 | 示例 |
|---|---|---|---|
id | INT | 主键 | 1 |
dict_id | INT | 字典 ID(外键) | 1 |
name | VARCHAR(50) | 项名称 | 男 |
value | VARCHAR(50) | 项值 | 1 |
sort | INT | 排序 | 1 |
note | VARCHAR(200) | 备注 | |
... | 公共字段 |
| 字典编码 | 名称 | 项值 | 用途 |
|---|---|---|---|
gender | 性别 | 0=女, 1=男 | 用户性别 |
user_status | 用户状态 | 0=禁用, 1=启用 | 用户账号状态 |
article_status | 文章状态 | 0=草稿, 1=已发布 | 文章状态 |
tenant_status | 租户状态 | 0=禁用, 1=启用 | 租户状态 |
example_type | 案例类型 | 0=类型一, 1=类型二 | 案例分类 |
example_status | 案例状态 | 0=禁用, 1=启用 | 案例状态 |
| 时机 | 操作 | 说明 |
|---|---|---|
| 字典项新增/修改/删除 | DictService::clearCache($code) | 清除该字典的缓存 |
| 手动刷新 | GET /dict/refreshCache | 清除所有字典缓存 |
| 缓存过期 | 自动失效 | 1 小时 TTL |
字典数据变更后必须刷新缓存
修改字典项后,如果不刷新缓存,前端显示的可能仍是旧数据。建议在字典管理的增删改接口中自动调用 clearCache()。
| 特性 | 说明 |
|---|---|
| 两级缓存 | 请求级 + 持久缓存,减少数据库压力 |
| 空值安全 | getText/getValue 对 null/空字符串返回空字符串 |
| 类型安全 | 值统一转为字符串比较,避免类型松散比较问题 |
| 缓存隔离 | 每个字典编码独立缓存,互不影响 |