多租户字典管理系统完整设计
多租户字典管理系统
一、完整数据库设计
1. 核心表结构
租户表
CREATE TABLE `sys_tenant` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`tenant_code` varchar(64) NOT NULL COMMENT '租户编码',`tenant_name` varchar(128) NOT NULL COMMENT '租户名称',`status` tinyint(1) DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_tenant_code` (`tenant_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户信息表';
字典类型表
CREATE TABLE `sys_dict_type` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',`type_code` varchar(64) NOT NULL COMMENT '字典类型编码',`type_name` varchar(128) NOT NULL COMMENT '字典类型名称',`is_system` tinyint(1) DEFAULT '0' COMMENT '是否系统字典(0:否,1:是)',`is_shared` tinyint(1) DEFAULT '0' COMMENT '是否共享(0:否,1:是)',`status` tinyint(1) DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_tenant_type_code` (`tenant_id`,`type_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表';
字典数据表
CREATE TABLE `sys_dict_data` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',`type_id` bigint(20) NOT NULL COMMENT '字典类型ID',`parent_id` bigint(20) DEFAULT '0' COMMENT '父级ID',`data_code` varchar(64) NOT NULL COMMENT '字典项编码',`data_name` varchar(128) NOT NULL COMMENT '字典项名称',`data_value` varchar(512) DEFAULT NULL COMMENT '字典项值',`path` varchar(255) DEFAULT NULL COMMENT '树路径(如:1.2.3)',`level` int(11) DEFAULT '1' COMMENT '层级深度',`sort` int(11) DEFAULT '0' COMMENT '排序',`status` tinyint(1) DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_type_data_code` (`type_id`,`data_code`),KEY `idx_parent_id` (`parent_id`),KEY `idx_path` (`path`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典数据表';
2. 扩展表结构
字典扩展字段定义表
CREATE TABLE `sys_dict_extend_define` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',`type_id` bigint(20) NOT NULL COMMENT '字典类型ID',`field_code` varchar(64) NOT NULL COMMENT '字段编码',`field_name` varchar(128) NOT NULL COMMENT '字段名称',`field_type` varchar(20) NOT NULL COMMENT '字段类型(string/number/boolean/date/json)',`default_value` varchar(512) DEFAULT NULL COMMENT '默认值',`is_required` tinyint(1) DEFAULT '0' COMMENT '是否必填(0:否,1:是)',`status` tinyint(1) DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_type_field_code` (`type_id`,`field_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典扩展字段定义表';
字典扩展字段值表
CREATE TABLE `sys_dict_extend_value` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',`data_id` bigint(20) NOT NULL COMMENT '字典数据ID',`field_id` bigint(20) NOT NULL COMMENT '字段定义ID',`string_value` varchar(512) DEFAULT NULL COMMENT '字符串值',`number_value` decimal(20,6) DEFAULT NULL COMMENT '数值',`boolean_value` tinyint(1) DEFAULT NULL COMMENT '布尔值',`date_value` datetime DEFAULT NULL COMMENT '日期值',`json_value` json DEFAULT NULL COMMENT 'JSON值',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_data_field` (`data_id`,`field_id`),KEY `idx_field_id` (`field_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典扩展字段值表';
字典扩展字段历史表
CREATE TABLE `sys_dict_extend_history` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`value_id` bigint(20) NOT NULL COMMENT '字段值ID',`before_value` json DEFAULT NULL COMMENT '修改前值',`after_value` json DEFAULT NULL COMMENT '修改后值',`operation_type` varchar(20) NOT NULL COMMENT '操作类型(CREATE/UPDATE/DELETE)',`operator` varchar(64) NOT NULL COMMENT '操作人',`operation_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',PRIMARY KEY (`id`),KEY `idx_value_id` (`value_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典扩展字段历史表';
二、领域模型设计
// 字典类型实体
public class DictType {private Long id;private Long tenantId;private String typeCode;private String typeName;private Boolean isSystem;private Boolean isShared;private List<DictExtendDefine> extendDefines;// 其他字段...
}// 字典数据实体
public class DictData {private Long id;private Long tenantId;private Long typeId;private Long parentId;private String dataCode;private String dataName;private String dataValue;private String path;private Integer level;private Map<String, DictExtendValue> extendValues;// 其他字段...
}// 字典扩展字段定义
public class DictExtendDefine {private Long id;private Long tenantId;private Long typeId;private String fieldCode;private String fieldName;private String fieldType;private Object defaultValue;// 其他字段...
}// 字典扩展字段值
public class DictExtendValue {private Long id;private Long dataId;private Long fieldId;private Object value;// 其他字段...
}
三、核心流程图
1. 字典项创建流程
2. 字典树查询流程
四、扩展字段管理API设计
1. 扩展字段定义接口
POST /api/dict/extend/define # 创建扩展字段定义
PUT /api/dict/extend/define/{id} # 更新扩展字段定义
DELETE /api/dict/extend/define/{id} # 删除扩展字段定义
GET /api/dict/extend/define # 获取字典类型的扩展字段定义
2. 扩展字段值接口
POST /api/dict/data/{dataId}/extend # 批量设置扩展字段值
GET /api/dict/data/{dataId}/extend # 获取字典项的所有扩展字段值
PATCH /api/dict/extend/value/{id} # 更新单个扩展字段值
五、设计优势说明
- 灵活扩展:
- 支持按字典类型动态定义字段
- 多种数据类型支持(字符串、数字、布尔值、日期、JSON)
- 不影响主表结构的情况下实现扩展
- 数据完整:
- 扩展字段定义与值分离
- 通过外键约束保证数据一致性
- 完整的历史变更记录
- 查询高效:
- 扩展字段值表建立合适索引
- 批量查询避免N+1问题
- 支持缓存扩展字段定义
- 多租户支持:
- 所有扩展表包含tenant_id
- 共享字典的扩展字段可继承
- 租户间扩展字段定义隔离
六、典型使用场景示例
场景1:地区字典扩展经纬度
// 1. 定义扩展字段
POST /api/dict/extend/define
{"typeId": 5,"fieldCode": "longitude","fieldName": "经度","fieldType": "number"
}// 2. 设置扩展值
POST /api/dict/data/123/extend
[{"fieldCode": "longitude","numberValue": 116.404
}]
场景2:产品分类扩展图片和描述
// 批量定义扩展字段
POST /api/dict/extend/define/batch
[{"typeId": 8,"fieldCode": "image_url","fieldName": "分类图片","fieldType": "string"
},{"typeId": 8,"fieldCode": "description","fieldName": "分类描述","fieldType": "string"
}]
此设计完整实现了多租户环境下字典系统的扩展字段需求,既保持了核心字典功能的简洁性,又提供了强大的扩展能力,适合需要高度定制化字典项的业务场景。
多租户字典表设计理念与流程图
一、表设计理念
1. 核心设计原则
-
多租户隔离性:
- 所有表都包含
tenant_id
字段,确保数据天然隔离 - 通过数据库中间件自动注入租户条件
- 共享数据通过显式的关联表控制
- 所有表都包含
-
树形结构优化:
- 采用
邻接表+路径枚举
混合模型
parent_id BIGINT COMMENT '直接父节点', path VARCHAR(255) COMMENT '完整路径(如1.3.5)', level INT COMMENT '层级深度'
- 平衡查询效率与更新复杂度
- 采用
-
扩展性设计:
- 垂直拆分:核心字段与扩展字段分离
- 动态字段:通过扩展定义表+扩展值表实现
- 多语言支持:独立的多语言表结构
-
数据一致性:
- 重要业务操作使用事务
@Transactional public void saveDictWithExtend(DictData data, List<ExtendValue> values) {// 保存主表dictMapper.insert(data);// 批量保存扩展值extendMapper.batchInsert(values); }
- 通过外键约束保证引用完整性
2. 表关系设计
二、核心流程图解
1. 字典项创建流程
2. 字典树查询流程
3. 扩展字段更新流程
三、表操作流程图
1. 字典数据移动(含子树处理)
2. 扩展字段值批量更新
四、设计亮点说明
-
高效树查询:
/* 查询子树(利用path字段) */ SELECT * FROM dict_data WHERE path LIKE '1.3.%' AND tenant_id = 123 ORDER BY path;/* 查询路径(利用path解析) */ SELECT * FROM dict_data WHERE id IN (SELECT SUBSTRING_INDEX(path, '.', 1) FROM dict_data WHERE id = 5) ORDER BY level;
-
扩展字段动态处理:
// 扩展字段值转换逻辑 public Object convertValue(ExtendDefine define, String input) {switch (define.getFieldType()) {case "number":return NumberUtils.parseDouble(input);case "boolean":return BooleanUtils.toBoolean(input);case "json":return JSON.parse(input);default:return input;} }
-
缓存策略:
// 多级缓存Key生成策略 public String buildCacheKey(Long tenantId, String typeCode) {return String.format("dict:%d:%s", tenantId, typeCode); }// 缓存数据结构示例 {"dict:123:region": {"lastUpdated": "2023-07-20T08:00:00Z","data": [{"id": 1,"name": "华东","extend": {"area_code": "0086","timezone": "UTC+8"}}]} }
这套设计通过清晰的表结构划分和流程图解,完整展现了:
- 多租户数据隔离的实现方式
- 树形字典的高效存储与查询
- 动态扩展字段的管理机制
- 保证数据一致性的技术方案
- 高性能访问的缓存策略