基于Springboot一个注解搞定数据字典的实践方案

目录
  • 问题引出:
  • 要求:
  • 方案
  • 实现

问题引出:

最近开了新项目,项目中用到了数据字典,列表查询数据返回的时候需要手动将code转换为name,到前台展示。项目经理表示可以封装一个统一的功能,避免程序员各自写各自的,代码混乱,风格不统一。

要求:

  • 基于微服务架构,数据字典通过服务获取;
  • 简化代码,使用简单;
  • 使用Redis;

方案

大致的方向是自定义注解,在序列化的时候进行数据处理; 考虑到微服务,需要将主要逻辑放到common中,然后对外提供接口,各业务服务实现接口以获取字典数据; 考虑Redis,序列化处理数据时,首先通过Redis获取,获取不到在通过接口获取,拿到数据后存到Redis中,然后再返回处理; 也可以多做一步,在新增、修改数据字典时,同步更新Redis内容,以保证数据有效性。

实现

  • 定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DictSerializer.class)
public @interface Dict {

    /** 字典类型 */
    String type();
}
  • 指定注解添加位置
  • 指定注解生效时间
  • 指定序列化处理类
  • 序列化处理类
public class DictSerializer extends StdSerializer<Object> implements ContextualSerializer {
    /** 字典注解 */
    private Dict dict;
    public DictSerializer() {
        super(Object.class);
    }
    public DictSerializer(Dict dict) {
        super(Object.class);
        this.dict = dict;
    }
    private String type;
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (Objects.isNull(value)) {
            gen.writeObject(value);
            return;
        }
        if (Objects.nonNull(dict)){
            type = dict.type();
        }
        // 通过数据字典类型和value获取name

        gen.writeObject(value);
        gen.writeFieldName(gen.getOutputContext().getCurrentName()+"Name");
        gen.writeObject(label);
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty beanProperty) throws JsonMappingException {
        if (Objects.isNull(beanProperty)){
            return prov.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        Dict dict = beanProperty.getAnnotation(Dict.class);
        if (Objects.nonNull(dict)){
            type = dict.type();
            return this;
        }
        return prov.findNullValueSerializer(null);
    }
}

这里处理的逻辑是原先的字段内容不变,添加一个新的字段用来存储转化后的值;

  • 数据字典获取
private static String changeLabel(String type,String code) {
    if(code.indexOf(",") > -1) {
        String[] strs = code.split(",");
        if (strs.length > 1) {
            StringBuilder sb = new StringBuilder();
            for (String str : strs) {
                // 从缓存中获取字典。如果不行,通过SpringUtil.getBean(); 获取服务处理
                sb.append(DictDataCache.getLabel(type, str)).append(separator);
            }
            return sb.substring(0, sb.length() - 1);
        }
    }
    // 从缓存中获取字典。如果不行,通过SpringUtil.getBean(); 获取服务处理
    return DictDataCache.getLabel(type, code);
}

考虑存在多选的情况,先判断下是否是多选的,默认逗号拼接,后期添加入参控制;

@Override
public String getDictDataOptions(String typeCode,String value) {
    if (redisTemplate.hasKey("dict:"+typeCode+":"+value)){
        return (String) redisTemplate.opsForValue().get("dict:"+typeCode+":"+value);
    }
    List<DictDataOptions> dictDataList = getDictDataHandler().getDictDataOptions(typeCode);
    if(CollUtil.isNotEmpty(dictDataList)) {
        put(typeCode, dictDataList);
    }
    if (redisTemplate.hasKey("dict:"+typeCode+":"+value)){
        return (String) redisTemplate.opsForValue().get("dict:"+typeCode+":"+value);
    }
    return null;
}

根据key判断Redis中是否存在,存在则直接获取,不存在则通过接口获取,获取到直接放到Redis中,然后再次从Redis获取。

protected void put(String typeCode, List<DictDataOptions> dataList) {
    if (CollUtil.isNotEmpty(dataList)){
        for (DictDataOptions dictDataOptions : dataList) {
            AbstractDictHandler.redisTemplate.opsForValue().set("dict:"+typeCode+":"+dictDataOptions.getDataLabel(),dictDataOptions.getDataValue());
        }
    }
}

循环放置数据字典值

@Override
public List<DictDataOptions> getDictDataOptions(String typeCode) {
    return iSysDictService.queryDictItemsByCode(typeCode).stream()
            .map(e -> DictDataOptions.builder().typeCode(typeCode).dataLabel(e.getValue()).dataValue(e.getText()).build())
            .collect(Collectors.toList());
}

根据数据字典类型,通过接口获取数据;注意该实现类需要每个微服务实现一个;然后为了避免基础数据服务挂掉,调用报错,common中提供一个默认实现。

4.使用

@Dict(type = "inspectType")
private String checkType;

在返回前端的实体中,对应字段添加注解,并指定数据字典type值

      {
        "id": "1522492702905954306",
        "professionName": "专业名称888",
        "checkCode": "检测项编码8",
        "checkProject": "rrrr检测项目88",
        "checkDevice": "52",
        "checkStandard": "检测项编码88",
        "referenceStandard": "wq参考标准8",
        "checkType": "1",
        "checkTypeName": "尺寸",
        "remarks": "ef备注备注8"
      },

前端获取的json会多一个字段:checkTypeName,内容为checkType 的中文值。

到此这篇关于基于Springboot一个注解搞定数据字典问题的文章就介绍到这了,更多相关Springboot数据字典内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于Springboot一个注解搞定数据字典的实践方案

    目录 问题引出: 要求: 方案 实现 问题引出: 最近开了新项目,项目中用到了数据字典,列表查询数据返回的时候需要手动将code转换为name,到前台展示.项目经理表示可以封装一个统一的功能,避免程序员各自写各自的,代码混乱,风格不统一. 要求: 基于微服务架构,数据字典通过服务获取: 简化代码,使用简单: 使用Redis: 方案 大致的方向是自定义注解,在序列化的时候进行数据处理: 考虑到微服务,需要将主要逻辑放到common中,然后对外提供接口,各业务服务实现接口以获取字典数据: 考虑Red

  • Springboot一个注解搞定返回参数key转换功能

    目录 前言 正文 前言 平时在搬砖的时候,大家有没有遇到过这样的一个场景,由于各种不可描述因素导致, 一个接口返回的数据 里面的 key 是 A , 但是客户端(前端) 要求返回的key 不叫 A 叫 Aa . 也就是返回的值不变,就是key 换了. 例如 : 正文 那么需要怎么做的 ? ① 新写一个类,用于值的返回,拿到值,把属性 get set 一下. ② 也就是本篇文章想提到的 ,使用注解, @JsonProperty 这个很多人都知道, 绕半天原来是 炒冷饭 ? 且慢. ② 这种方式,其

  • 一个注解搞定Spring Security基于Oauth2的SSO单点登录功能

    目录 一.说明 二.原理说明 2.1. 同域单点登录 2.2. 跨域单点登录 2.3. 基于Oauth2的跨域单点登录流程 三.Spring Security实现 四.demo下载地址 一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼.本文主要介绍 同域 和 跨域 两种不同场景单点登录的实现原理,并使用 Spring Security 来实现一个最简单的跨域 SSO客户端 . 二.原理说明 单点登录主流都是基于共享 cookie

  • 一个依赖搞定 Spring Boot 接口防盗刷的流程分析

    目录 系统要求​ 工作流程​ 命中规则后 接入使用 注意 配置一览表 kk-anti-reptile 是适用于基于 spring-boot 开发的分布式系统的反爬虫组件. 系统要求​ 基于 spring-boot 开发(spring-boot1.x, spring-boot2.x均可) 需要使用 redis 工作流程​ kk-anti-reptile 使用基于 Servlet 规范的的 Filter 对请求进行过滤,在其内部通过 spring-boot 的扩展点机制,实例化一个 Filter,并

  • php一个文件搞定微信jssdk配置

    php一个文件搞定微信jssdk配置: 包括缓存,包括https通讯,获取微信access_token,签名什么的都有.但是防范性编程做得比较少,商业用的话,需要完善下代码. 使用姿势 ^ajax(Common.ServerUrl + "GetWX.php", { data: { Type: "config", url: location.href.split('#')[0] }, dataType: 'json', type: 'get', timeout: 50

  • 一个方法搞定iOS下拉放大及上推缩小

    下面这种效果在ios应用中很常见: 实现思路: 1、创建头部的视图和tableview,需要注意的是tableview要设置contentInset,contentInsent 的顶部要和头部视图的背景图的高度一样,否则会有空隙(或是有遮挡). myTableView.contentInset = UIEdgeInsetsMake(headRect.size.height-navHeight-navHeight, 0, 0, 0); 2、对头部视图的背景图片的尺寸进行处理,当然,你也可以直接找一

  • 推荐一个文件搞定操作系统的所有常见问题第1/3页

    使用电脑的过程中我们会遇到很多的问题,烦人的广告窗口不停的弹出:不停的在多套网络配置中切换:时常忘掉备份网络中的关键数据:加密的文件夹由于误操作无法打开.你想过没有以上这些问题都可以通过一个小文件解决?你甚至可以借助它解决几乎所有在使用电脑时遇到的问题.它就是功能强大的bat文件. 一.查漏补缺--给系统功能添把火 我们的操作系统虽然功能强大,但是在某方面的应用上依旧存在欠缺,如:没有定时关机软件.而用bat文件可以解决很多这类问题. 1.关机与重启 我们先做个让电脑在每天指定时间关机的bat,

  • 一个文件搞定系统所有问题 推荐

    在使用电脑的过程中我们会遇到很多的问题,烦人的广告窗口不停的弹出:不停的在多套网络配置中切换:时常忘掉备份网络中的关键数据:加密的文件夹由于误操作无法打开.你想过没有以上这些问题都可以通过一个小文件解决?你甚至可以借助它解决几乎所有在使用电脑时遇到的问题.它就是功能强大的bat文件. 一.查漏补缺--给系统功能添把火 我们的操作系统虽然功能强大,但是在某方面的应用上依旧存在欠缺,如:没有定时关机软件.而用bat文件可以解决很多这类问题. 1.关机与重启 我们先做个让电脑在每天指定时间关机的bat

  • 基于JQuery的一句话搞定手风琴菜单

    一.Html代码 复制代码 代码如下: <div class="MenuSubTitle AccordionCollapse">子菜单1</div> <div> <a href="Fld_BaseData/aa.aspx">aa管理</a><br/> <a href="Fld_BaseData/bb.aspx">bb管理</a><br/>

  • SpringBoot利用注解来实现Redis分布式锁

    目录 一.业务背景 二.分析流程 加锁 超时问题 解决方案:增加一个「续时」 三.设计方案 四.实操 相关属性类配置 核心切面拦截的操作 五.开始测试 六.总结 一.业务背景 有些业务请求,属于耗时操作,需要加锁,防止后续的并发操作,同时对数据库的数据进行操作,需要避免对之前的业务造成影响. 二.分析流程 使用 Redis 作为分布式锁,将锁的状态放到 Redis 统一维护,解决集群中单机 JVM 信息不互通的问题,规定操作顺序,保护用户的数据正确. 梳理设计流程 新建注解 @interface

随机推荐