方式一:使用字典表存取
说明:适合于前端页面使用的下拉框数据值、或者字典数据不固定有变化调整的字典,建议放在数据表中维护。
直接借鉴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
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));
}
}