方式一:使用字典表存取
说明:适合于前端页面使用的下拉框数据值、或者字典数据不固定有变化调整的字典,建议放在数据表中维护。
直接借鉴Ruoyi框架提供的2张字典表,sys_dict_type(字典类型定义表)、sys_dict_data(字典数据表)
- CREATE TABLE `sys_dict_type` (
- `dict_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '字典主键',
- `dict_name` varchar(100) DEFAULT '' COMMENT '字典名称',
- `dict_type` varchar(100) DEFAULT '' COMMENT '字典类型',
- `status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
- `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `remark` varchar(500) DEFAULT NULL COMMENT '备注',
- PRIMARY KEY (`dict_id`),
- UNIQUE KEY `dict_type` (`dict_type`)
- ) COMMENT='字典类型表';
-
- CREATE TABLE `sys_dict_data` (
- `dict_code` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '字典编码',
- `dict_sort` int(4) DEFAULT '0' COMMENT '字典排序',
- `dict_label` varchar(100) DEFAULT '' COMMENT '字典标签',
- `dict_value` varchar(100) DEFAULT '' COMMENT '字典键值',
- `dict_type` varchar(100) DEFAULT '' COMMENT '字典类型',
- `css_class` varchar(100) DEFAULT NULL COMMENT '样式属性(其他样式扩展)',
- `list_class` varchar(100) DEFAULT NULL COMMENT '表格回显样式',
- `is_default` char(1) DEFAULT 'N' COMMENT '是否默认(Y是 N否)',
- `status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
- `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `remark` varchar(500) DEFAULT NULL COMMENT '备注',
- PRIMARY KEY (`dict_code`)
- ) COMMENT='字典数据表';
方式二:自定义枚举变量
说明:适用于系统级别、固定不变的字典;可以考虑直接写在代码中,方便其它人在代码中使用。
- @DictType("sys_disable")
- public enum SysDisableEnum {
- ENABLE("0", "启用"),
- DISABLE("1", "停用");
-
- private String code;
- private String desc;
-
- public static List
toList() { - return (List)Stream.of(values()).map((row) -> {
- DictOptions options = new DictOptions();
- options.setDictLabel(row.getDesc());
- options.setDictValue(row.getCode());
- return options;
- }).collect(Collectors.toList());
- }
-
- private SysDisableEnum(final String code, final String desc) {
- this.code = code;
- this.desc = desc;
- }
-
- public String getCode() {
- return this.code;
- }
-
- public String getDesc() {
- return this.desc;
- }
- }
对于字典表,在使用时很简单,无非是提供好添加、更新、删除及查询的API即可;但对于枚举字典,怎么能不重复配置到字典表中,同时又能融入到 查询字典的API中,这里就需要好好封装一下代码了!
具体实现代码参考如下:
- /**
- * 字典类型枚举:@DictType(value="sys_sex")
- */
- @Documented
- @Inherited
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.TYPE)
- public @interface DictType {
-
- String value();//字典类型别名
- }
-
-
- /**
- * 字典翻译注解:@DictEnum(value="sex", dictType="sys_sex")
- */
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface DictEnum {
-
- String value();//实体类字段
-
- String dictType() default "";//字典code;指的是dict_type
-
- String target() default "";//返回目标属性,如 sexDesc
- }
-
- /**
- * 字典类型注解:用于多个字典翻译
- */
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface DictEnums {
-
- DictEnum[] value();//多个字典值
- }
- package com.geline.cloud.core.dict;
-
- import com.geline.cloud.core.domain.DictOptions;
-
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- /**
- * 字典枚举缓存
- * @author: mengxin
- * @date: 2022/9/25 10:14
- */
- public class DictTypeCache {
-
- private static Map
> dictMapCache = new HashMap<>(); -
- public static void put(String dictType, List
optionsList) { - dictMapCache.put(dictType, optionsList);
- }
-
- public static Map
> getAll(){ - return dictMapCache;
- }
-
- /**
- * 获取字典值对应的标签名
- * @param dictType
- * @param dictValue
- * @return
- */
- public static String getDictLabel(String dictType, String dictValue){
- List
options = dictMapCache.get(dictType); - if(options != null){
- for (DictOptions row : options){
- if(row.getDictValue().equals(dictValue)){
- return row.getDictLabel();
- }
- }
- }
- return null;
- }
- }
- package com.geline.cloud.core.dict;
-
- import com.geline.cloud.core.dict.annotation.DictType;
- import com.geline.cloud.core.domain.DictOptions;
- import org.springframework.boot.CommandLineRunner;
- import org.springframework.context.ResourceLoaderAware;
- import org.springframework.core.io.Resource;
- import org.springframework.core.io.ResourceLoader;
- import org.springframework.core.io.support.ResourcePatternResolver;
- import org.springframework.core.io.support.ResourcePatternUtils;
- import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
- import org.springframework.core.type.classreading.MetadataReader;
- import org.springframework.core.type.classreading.MetadataReaderFactory;
- import org.springframework.stereotype.Component;
- import org.springframework.util.ClassUtils;
-
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.List;
- import java.util.Map;
-
- /**
- * 扫描自定义字典枚举类 @DictType
- * @author: mengxin
- * @date: 2022/9/25 10:16
- */
- @Component
- public class DictTypeHandler implements CommandLineRunner, ResourceLoaderAware {
- private ResourceLoader resourceLoader;
-
- private ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
- private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
- private static final String FULLTEXT_SACN_PACKAGE_PATH = "com.geline.cloud";
-
- @Override
- public void setResourceLoader(ResourceLoader resourceLoader) {
- this.resourceLoader = resourceLoader;
- }
-
- @Override
- public void run(String... args) throws Exception {
- String concat = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
- .concat(ClassUtils.convertClassNameToResourcePath(FULLTEXT_SACN_PACKAGE_PATH)
- .concat("/**/*Enum*.class")); //只扫描文件名中包含Enum的文件,会快点
- Resource[] resources = resolver.getResources(concat);
- MetadataReader metadataReader = null;
- for (Resource resource : resources) {
- if (resource.isReadable()) {
- metadataReader = metadataReaderFactory.getMetadataReader(resource);
- boolean flag = metadataReader.getAnnotationMetadata().hasAnnotation(DictType.class.getName());
- String className = metadataReader.getClassMetadata().getClassName();
-
- if(flag) {
- Map
annotationAttributes = metadataReader.getAnnotationMetadata() - .getAnnotationAttributes(DictType.class.getName());
- String dictType = annotationAttributes.get("value").toString();
-
- try {
- Class> aClass = Class.forName(className);
- Method listDict = aClass.getDeclaredMethod("toList");
- Object[] oo = aClass.getEnumConstants();
- List
list = (List) listDict.invoke(oo[0]); - if(list != null){
- DictTypeCache.put(dictType, list);
- }
- } catch (ClassNotFoundException | NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
- }
- package com.geline.cloud.core.domain;
-
- import lombok.Getter;
- import lombok.Setter;
-
- /**
- * 查询字典列表
- * @author: mengx
- * @date: 2021/9/14 16:18
- */
- @Getter
- @Setter
- public class DictOptions {
-
- private String dictType;
- private String dictLabel;
- private String dictValue;
- private String isDefault;
- private String listClass;
- }
- package com.geline.cloud.core.dict;
-
- import cn.hutool.core.util.ObjectUtil;
- import cn.hutool.core.util.ReflectUtil;
- import cn.hutool.core.util.StrUtil;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.geline.cloud.core.dict.annotation.DictEnum;
- import com.geline.cloud.core.dict.annotation.DictEnums;
- import com.geline.cloud.core.domain.Result;
- import com.geline.cloud.core.domain.TableDataInfo;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.core.MethodParameter;
- import org.springframework.http.MediaType;
- import org.springframework.http.server.ServerHttpRequest;
- import org.springframework.http.server.ServerHttpResponse;
- import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
-
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.util.Map;
-
- /**
- * 使用方式:子类继承后加 @ControllerAdvice
- * @author: mengxin
- * @date: 2022/9/17 10:05
- */
- @Slf4j
- public abstract class AbstractDictEnumHandler implements ResponseBodyAdvice
-
- /**
- * 从DB中查询字典标签名
- * @param dictType 字典类型code
- * @param dictValue 字典值code
- * @return
- */
- public abstract String selectDictLabelByDatabase(String dictType, String dictValue);
-
- /**
- * 从枚举字典中取字典标签名
- * @param dictType
- * @param dictValue
- * @return
- */
- public String selectDictLabelByEnum(String dictType, String dictValue){
- return DictTypeCache.getDictLabel(dictType, dictValue);
- }
-
- @Override
- public boolean supports(MethodParameter methodParameter, Class aClass) {
- return methodParameter.hasMethodAnnotation(DictEnum.class) || methodParameter.hasMethodAnnotation(DictEnums.class);
- }
-
- @Override
- public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class clazz,
- ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
- if (ObjectUtil.isEmpty(o)) {
- return null;
- }
- // 获取方法 注解参数
- Method method = methodParameter.getMethod();
- final String methodName = method == null ? "" : (method.getDeclaringClass().getSimpleName() + "." + method.getName());
- DictEnum[] sensibles;
- if (methodParameter.hasMethodAnnotation(DictEnum.class)) {
- DictEnum sensible = methodParameter.getMethodAnnotation(DictEnum.class);
- sensibles = new DictEnum[]{sensible};
- } else {
- DictEnums typeSensibles = methodParameter.getMethodAnnotation(DictEnums.class);
- sensibles = typeSensibles == null ? new DictEnum[]{} : typeSensibles.value();
- }
- // 处理返回值
- this.dis(o, sensibles, methodName);
- return o;
- }
-
- /**
- * 处理返回数据
- *
- * @param obj 对象
- * @param sensibles 字典类型
- */
- private void dis(Object obj, DictEnum[] sensibles, String methodName) {
- // 处理返回类型
- if (obj instanceof TableDataInfo) {
- this.dis(((TableDataInfo) obj).getRows(), sensibles, methodName);
- return;
- }
- if (obj instanceof Result) {
- this.dis(((Result) obj).getData(), sensibles, methodName);
- return;
- }
- if (obj instanceof IPage) {
- ((IPage) obj).getRecords().forEach(e -> this.dis(e, sensibles, methodName));
- return;
- }
- if (obj instanceof Iterable) {
- ((Iterable) obj).forEach(e -> this.dis(e, sensibles, methodName));
- return;
- }
- if (obj instanceof Map) {
- Map
map = (Map) obj; - for (DictEnum sensible : sensibles) {
- if (!map.containsKey(sensible.value())) {
- log.warn("--- 方法 {},字典 {} 进行忽略,不存在 ------", methodName, sensible.value());
- continue;
- }
- Object value = map.get(sensible.value());
- if (value instanceof Iterable) {
- this.dis(value, sensibles, methodName);
- continue;
- }
- String dictType = StrUtil.isEmpty(sensible.dictType()) ? StrUtil.toUnderlineCase(sensible.value()) : sensible.dictType();
- String label = selectDictLabelByEnum(dictType, String.valueOf(value));
- if(label == null){
- label = selectDictLabelByDatabase(dictType, String.valueOf(value));
- }
- String target = sensible.target();
- map.put(StrUtil.isNotBlank(target) ? target : sensible.value(), label);
- }
- return;
- }
- Class> clazz = obj.getClass();
- for (DictEnum sensible : sensibles) {
- try {
- Field field = ReflectUtil.getField(clazz, sensible.value());
- if (field == null || !field.getType().getSimpleName().contains("String")) {
- log.warn("--- 方法 {},字典 {} 进行忽略,不存在 || 非String类型 ------", methodName, sensible.value());
- continue;
- }
- Object val = ReflectUtil.getFieldValue(obj, field);
- if (ObjectUtil.isEmpty(val)) {
- continue;
- }
- String dictType = StrUtil.isEmpty(sensible.dictType()) ? StrUtil.toUnderlineCase(sensible.value()) : sensible.dictType();
- String label = selectDictLabelByEnum(dictType, String.valueOf(val));
- if(label == null){
- label = selectDictLabelByDatabase(dictType, String.valueOf(val));
- }
- ReflectUtil.setFieldValue(obj, field, label);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- package com.geline.cloud.util.dict;
-
- import com.geline.cloud.core.dict.AbstractDictEnumHandler;
- import com.geline.cloud.modules.system.entity.SysDictData;
- import com.geline.cloud.modules.system.service.SysDictDataService;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.ControllerAdvice;
-
- /**
- * 自定义字典处理
- * @author: mengxin
- * @date: 2022/9/17 10:43
- */
- @ControllerAdvice
- @Slf4j
- public class MyDictEnumHandler extends AbstractDictEnumHandler {
-
- @Autowired
- private SysDictDataService sysDictDataService;
-
- @Override
- public String selectDictLabelByDatabase(String dictType, String dictValue) {
- SysDictData dictData = sysDictDataService.getDictData(dictType, dictValue);
- if(dictData != null){
- return dictData.getDictLabel();
- }
- return null;
- }
- }
具体使用方法:
在controller方法上添加注解,扩展返回字典字段,如查询返回 sex, 增加一个sexDesc;
举例:分页查询返回 Page
- /**
- * 分页查询
- * localhost:9501/reportData/pageMaps?pageNum=1&pageSize=10&orderByColumn=create_time&isAsc=desc&reportTitle=标题
- * @param pageNum
- * @param pageSize
- * @param orderByColumn
- * @param isAsc
- * @return
- */
- @DictEnums({
- @DictEnum(value = "reportType", dictType = "sys_report_type"),
- @DictEnum(value = "status", dictType = "sys_disable", target = "statusDesc")
- })
- @GetMapping({"/pageMaps"})
- public TableDataInfo pageMaps(String reportTitle,
- int pageNum, int pageSize, String orderByColumn, String isAsc) {
- ReportDataQueryVO queryVO = new ReportDataQueryVO();
- queryVO.setReportTitle(reportTitle);
- QueryWrapper query = QueryWrapperUtil.build(queryVO, orderByColumn, isAsc);
- Page page = this.getBaseService().pageMaps(new Page(pageNum, pageSize), query);
- return this.getTableDataInfo(page);
- }
-
-
- /**
- * 地图数据 - 分页查询
- * localhost:9501/visualMap/pageMaps?pageNum=1&pageSize=10&orderByColumn=create_time&isAsc=desc&name={名称}
- * @return
- */
- @DictEnum(value = "disable", dictType = "sys_disable")
- @GetMapping({"/pageMaps"})
- public TableDataInfo pageMaps(int pageNum, int pageSize, String orderByColumn, String isAsc, String name) {
- VisualMapQueryVO queryVO = new VisualMapQueryVO();
- queryVO.setName(name);
- QueryWrapper query = QueryWrapperUtil.build(queryVO, orderByColumn, isAsc);
- Page page = this.visualMapService.pageMaps(new Page(pageNum, pageSize), query);
- return this.getTableDataInfo(page);
- }
至于 添加、更新相关代码,直接用mybatis生成的 Enitity.java,不用在实体类中额外添加 @Dict注解,前端添加、更新时传入字典code直接保存及更新,字典问题-只考虑查询返回的字典翻译问题。
最后,再提供2个查询返回字典列表方法:
- package com.geline.cloud.system;
-
- import com.geline.cloud.core.dict.DictTypeCache;
- import com.geline.cloud.core.domain.DictOptions;
- import com.geline.cloud.core.domain.Result;
- import com.geline.cloud.modules.system.service.SysDictDataService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.util.List;
- import java.util.Map;
-
- /**
- * 字典管理 Controller
- * @author mengx
- * @date 2020/7/26 21:45
- */
- @RestController
- @RequestMapping("/system/dict")
- public class SysDictController {
-
- @Autowired
- private SysDictDataService sysDictDataService;
-
- /**
- * 查询所有枚举字典Map
- * /system/dict/listEnums
- * @return
- */
- @GetMapping("/listEnums")
- public Result listEnums(){
- return Result.ok(DictTypeCache.getAll());
- }
-
- /**
- * 根据字典类型查询字典列表
- * /system/dict/list/{dictType}
- * @param dictType
- * @return
- */
- @GetMapping("/list/{dictType}")
- public Result list(@PathVariable String dictType){
- Map
> all = DictTypeCache.getAll(); - List
options = all.get(dictType); - if(options != null){
- return Result.ok(options);
- }
- return Result.ok(sysDictDataService.getListByDictType(dictType));
- }
-
- }