数据字典是系统中基本的必不可少的功能,在多种多样的系统中,数据字典表的设计都大同小异。但是使用方式确是多种多样,设计好一套易用的数据字典功能模块,可以使开发事半功倍。
成都创新互联专注于城西企业网站建设,自适应网站建设,成都商城网站开发。城西网站建设公司,为城西等地区提供建站服务。全流程按需求定制制作,专业设计,全程项目跟踪,成都创新互联专业和态度为您提供的服务
我们为了更好的满足多样的业务需求,那么我们肯定是需要支持多种多样的方式,来实现数据字典转换的功能,接下来,我们以1注解+2工具+3前端转换的方式来支持数据字典转换。三种方式相辅相成、可以单独使用,也可以结合起来使用。
package com.gitegg.platform.base.annotation.dict;
import java.lang.annotation.*;
/**
* 数据字典注解,注解在方法上,自动设置返回参数的字典数据
* 1、可以注解在 service和 controller上,只注解返回值,支持引用类型和常用的集合类型
* 2、具体的实体类中,如果是引用类型,那么递归赋值
* 3、支持的集合类型:List Set Queue ,引用类型:Array一维数组,普通对象类型(自定义实体bean)。
* @author GitEgg
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictAuto {
}
package com.gitegg.platform.base.annotation.dict;
import java.lang.annotation.*;
/**
* 数据字典注解,注解在字段上
* 如果dictCode为空,且此字段是对象类型,那么表示此字段对象中拥有字典类型,
* 也就是只有注解了此字段的数据才会去递归设置字典值,不去随便做无所谓的遍历
*
* @author GitEgg
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictField {
/**
* 数据字典类型 :系统字典 system 业务字典 business 地区字典 areas 其他字典:直接表名,例: t_sys_role
* 1、确定选择哪种类型的数据字典
*/
String dictType() default "business";
/**
* 数据字典编码,就是取哪些数据字典的值
* 2、确定需要匹配数据字典的集合
*/
String dictCode() default "";
/**
* 要最终转换最终数据字典的键,是实体类中的一个字段,通常配置为此字段的定义名称,通过此字段作为key来转换数据字典的值
* 3、确定需要把实体中哪个字段转换为字典值
*/
String dictKey() default "";
/**
* 如果是自定义表数据时,此字段作为字典code,对应数据表的字段
* 4、表中作为数据字典的键
*/
String dictFiled() default "";
/**
* 如果是自定义表数据时,此字段作为字典value,对应数据表的字段
* 5、表中作为数据字典的值
*/
String dictValue() default "";
}
package com.gitegg.platform.boot.aspect;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gitegg.platform.base.annotation.dict.DictAuto;
import com.gitegg.platform.base.annotation.dict.DictField;
import com.gitegg.platform.base.constant.DictConstant;
import com.gitegg.platform.base.constant.GitEggConstant;
import com.gitegg.platform.base.result.Result;
import com.gitegg.platform.boot.util.GitEggAuthUtils;
import com.gitegg.platform.redis.lock.IDistributedLockService;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import jodd.util.StringPool;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
/**
* 数据字典切面
* @author GitEgg
* @date 2022-4-10
*/
@Log4j2
@Component
@Aspect
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@ConditionalOnProperty(name = "enabled", prefix = "dict", havingValue = "true", matchIfMissing = true)
public class DictAspect {
/**
* 是否开启租户模式
*/
@Value("${tenant.enable}")
private Boolean enable;
private final RedisTemplate redisTemplate;
/**
* 后置通知 解析返回参数,进行字典设置
* @AfterReturning 只有存在返回值时才会执行 @After 无论有没有返回值都会执行 所以这里使用 @AfterReturning 只有存在返回值时才执行字典值注入操作
* @param dictAuto 注解配置
*/
@AfterReturning(pointcut = "@annotation(dictAuto)", returning = "returnObj")
public void doAfterReturning( DictAuto dictAuto, Object returnObj){
// 返回不为null时,进行数据字典处理
if (null != returnObj) {
doDictAuto(dictAuto, returnObj);
}
}
/**
* key的组成为: dict:userId:sessionId:uri:method:(根据spring EL表达式对参数进行拼接)
* 此处要考虑多种返回类型,集合类型、引用类型、对象类型和基本数据类型,这里只处理 集合类型:List Set Queue ,引用类型:Array数组,Array只支持一维数组。
* 对于对象中的子对象,为了提升性能,同样需要加@DictField注解才去填充,否则每个子对象都去递归判断,影响性能
* 我们要考虑此处的逻辑:
* 1、判断返回数据类型,如果是集合类型,那么取出包含实体对象的集合类,然后进行对象解析
* 2、如果是对象类型,那么直接进行对象解析
* 3、如果是IPage类型,那么取出其中的list数据,判断是否为空,不为空,执行 1 步骤
* 4、如果是Result类型,判断Result的data是IPage还是集合类型,分别执行对应的 1 步骤 或 3 步骤,如果不是IPage也不是集合类型,直接执行第 2 步骤
* @param dictAuto 注解
* @param objectReturn 方法返回值
*/
private void doDictAuto(@NonNull DictAuto dictAuto, Object objectReturn) {
// 临时存储数据字典map
Map> dictMap = new HashMap<>();
this.translationObjectDict(objectReturn, dictMap);
}
/**
* 找到实际的对象或对象列表
* 此处要考虑多种返回类型,集合类型、引用类型、对象类型和基本数据类型,这里只处理 集合类型:List Set Queue ,引用类型:Array一维数组。
* @param objectReturn
* @param dictMap
* @return
*/
private void translationObjectDict(Object objectReturn, Map> dictMap) {
if (Objects.isNull(objectReturn))
{
return;
}
// 判断返回值类型是Result、IPage、List、Object
if (objectReturn instanceof Result) {
Object objectTarget = ((Result) objectReturn).getData();
translationObjectDict(objectTarget, dictMap);
} else if (objectReturn instanceof IPage) {
List
比较灵活,选择需要转换的数据即可。
定义通用接口,首先从缓存查,缓存没有再查询数据库,在登录后重置数据字典。
1、前端新增dictUtils,用于dictCode的查询、缓存等操作。
import { getAuthCache, setAuthCache } from '/@/utils/auth';
import { DICT_SYSTEM_CACHE_KEY, DICT_BUSSINESS_CACHE_KEY } from '/@/enums/cacheEnum';
import { listDict, batchListDict } from '/@/api/system/base/dict';
import { listDictBusiness, batchListDictBusiness } from '/@/api/system/base/dictBusiness';
// System default cache time
export const DICT_CACHE_TIME = 60 * 60 * 2 * 1000;
// Dict
export interface Dict {
// dictCode
dictCode: string;
// dictList
dictList?: [];
// filterMap
filterMap?: {};
}
// DictMap
export interface DictMap {
// dictList
dictMap: {};
}
export function getDictCacheOnly(dict: Dict) {
let dictMap = getAuthCache(DICT_SYSTEM_CACHE_KEY) as any;
if (!dictMap) {
dictMap = {};
}
if (dictMap[dict.dictCode]) {
return dictMap[dict.dictCode] as Dict;
} else {
getDict(dict).then(function (dictReturn) {
dictMap[dict.dictCode] = dictReturn;
// 数据字典默认缓存2小时,重新登陆后失效
setAuthCache(DICT_SYSTEM_CACHE_KEY, dictMap);
});
return dict;
}
}
export function getDictBusinessCacheOnly(dict: Dict) {
let dictBusinessMap = getAuthCache(DICT_BUSSINESS_CACHE_KEY) as any;
if (!dictBusinessMap) {
dictBusinessMap = {};
}
if (dictBusinessMap[dict.dictCode]) {
return dictBusinessMap[dict.dictCode] as Dict;
} else {
getDictBusiness(dict).then(function (dictReturn) {
dictBusinessMap[dict.dictCode] = dictReturn;
// 数据字典默认缓存2小时,重新登陆后失效
setAuthCache(DICT_BUSSINESS_CACHE_KEY, dictBusinessMap);
});
return dict;
}
}
export async function getDictCache(dict: Dict) {
let dictMap = getAuthCache(DICT_SYSTEM_CACHE_KEY) as any;
if (!dictMap) {
dictMap = {};
}
if (dictMap[dict.dictCode]) {
return dictMap[dict.dictCode] as Dict;
} else {
const dictReturn = await getDict(dict);
dictMap[dict.dictCode] = dictReturn;
// 数据字典默认缓存2小时,重新登陆后失效
setAuthCache(DICT_SYSTEM_CACHE_KEY, dictMap);
return dictReturn;
}
}
export async function getDictBusinessCache(dict: Dict) {
let dictBusinessMap = getAuthCache(DICT_BUSSINESS_CACHE_KEY) as any;
if (!dictBusinessMap) {
dictBusinessMap = {};
}
if (dictBusinessMap[dict.dictCode]) {
return dictBusinessMap[dict.dictCode] as Dict;
} else {
const dictReturn = await getDictBusiness(dict);
dictBusinessMap[dict.dictCode] = dictReturn;
// 数据字典默认缓存2小时,重新登陆后失效
setAuthCache(DICT_BUSSINESS_CACHE_KEY, dictBusinessMap);
return dictReturn;
}
}
// 批量初始化系统字典
export async function initDictCache(dictCodeList: string[]) {
let dictMap = getAuthCache(DICT_SYSTEM_CACHE_KEY) as any;
if (!dictMap) {
dictMap = {};
}
const dictResultMap = await batchListDict(dictCodeList);
if (dictResultMap) {
dictCodeList.forEach(function (dictCode) {
if (dictResultMap[dictCode]) {
const dict = {} as Dict;
dict.dictList = dictResultMap[dictCode];
dict.filterMap = {};
dict.dictList.forEach((item) => {
const itemDict = item as any;
dict.filterMap[itemDict.dictCode] = itemDict.dictName;
});
dictMap[dictCode] = dict;
}
});
// 数据字典默认缓存2小时,重新登陆后失效
setAuthCache(DICT_SYSTEM_CACHE_KEY, dictMap);
}
}
// 批量初始化业务字典
export async function initDictBusinessCache(dictCodeList: string[]) {
let dictMap = getAuthCache(DICT_BUSSINESS_CACHE_KEY) as any;
if (!dictMap) {
dictMap = {};
}
const dictResultMap = await batchListDictBusiness(dictCodeList);
if (dictResultMap) {
dictCodeList.forEach(function (dictCode) {
if (dictResultMap[dictCode]) {
const dict = {} as Dict;
dict.dictList = dictResultMap[dictCode];
dict.filterMap = {};
dict.dictList.forEach((item) => {
const itemDict = item as any;
dict.filterMap[itemDict.dictCode] = itemDict.dictName;
});
dictMap[dictCode] = dict;
}
});
// 数据字典默认缓存2小时,重新登陆后失效
setAuthCache(DICT_BUSSINESS_CACHE_KEY, dictMap);
}
}
export async function getDict(dict: Dict) {
const dictList = await listDict(dict.dictCode);
if (dictList && dictList.length > 0) {
dict.dictList = dictList;
dict.filterMap = {};
dictList.forEach((item) => {
dict.filterMap[item.dictCode] = item.dictName;
});
}
return dict;
}
export async function getDictBusiness(dict: Dict) {
const dictBusinessList = await listDictBusiness(dict.dictCode);
if (dictBusinessList && dictBusinessList.length > 0) {
dict.dictList = dictBusinessList;
dict.filterMap = {};
dictBusinessList.forEach((item) => {
dict.filterMap[item.dictCode] = item.dictName;
});
}
return dict;
}
2、登录成功后重新数据字典缓存,也就是每次在后台数据字典修改之后,前端需要重新登录才能刷新数据字典缓存。
// 重新初始化系统数据字典
setAuthCache(DICT_SYSTEM_CACHE_KEY, {});
// 重新初始化业务数据字典
setAuthCache(DICT_BUSSINESS_CACHE_KEY, {});
3、在需要用到数据字典时,直接调用即可,根据utils的实现,首先会从缓存查询,如果缓存中没有,才会从后台查询。
import { getDictBusinessCache } from '/@/utils/gitegg/dictUtils';
......
{
label: '状态',
field: 'status',
component: 'ApiRadioGroup',
required: true,
defaultValue: '2',
componentProps: {
api: getDictBusinessCache,
params: { dictCode: 'USER_STATUS' },
resultField: 'dictList',
// use name as label
labelField: 'dictName',
// use id as value
valueField: 'dictCode',
},
},
......
数据字典在系统中的使用非常频繁,所以在设计和使用时,既要保证实时更新获取最新配置,又要保证不能引发系统性能问题。在系统设计的时候,既要考虑到后台数据字典转换,还需要考虑到前端数据字典转换,这两种转换方式在使用过程中,我们根据具体业务需求和使用条件具体选择其中一种即可。
新闻名称:SpringCloud—数据字典注解的设计与实现
URL标题:http://www.shufengxianlan.com/qtweb/news14/506664.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联