跳至主要內容

SpringCloud微服务实战——搭建企业级开发框架(五十):系统通用数据字典注解的设计与实现

Mr.Dabaospringcloud实战约 3998 字大约 13 分钟

SpringCloud微服务实战——搭建企业级开发框架(五十):系统通用数据字典注解的设计与实现

数据字典是系统中基本的必不可少的功能,在多种多样的系统中,数据字典表的设计都大同小异。但是使用方式确是多种多样,设计好一套易用的数据字典功能模块,可以使开发事半功倍。

常用的数据字典使用方式:

  • 直接在SQL语句中LEFT JOIN (当然是不推荐这样用的)
  • 查询出原始数据,然后再根据原始数据需要的字典编码,批量查询字典表,并赋值到原始数据的字典值字段。
  • 后台提供通用数据字典接口,前端根据需求统一查询数据字典数据并缓存在前台。当业务返回后台原始数据时,前台通过类似于Filter(VUE)功能,进行字典值对应。
  • 自定义数据字典注解,当接口返回原始数据时,通过切面分析返回对象中的数据字典字段,并将数据字典赋值到数据字典值字段。
  • 提供数据字典通用工具,手动处理单个或批量需要进行数据字典转换的数据。

我们为了更好的满足多样的业务需求,那么我们肯定是需要支持多种多样的方式,来实现数据字典转换的功能,接下来,我们以1注解+2工具+3前端转换的方式来支持数据字典转换。三种方式相辅相成、可以单独使用,也可以结合起来使用。

  • 可注解在Controller
  • 可注解在Service
  • 支持的集合类型:List、Set、Queue ,引用类型:Array一维数组
  • 单独的bean支持递归赋值,不支持复杂数据递归
  • 后台提供通用数据字典接口,前端页面提供通用转换方法。
  • 只注解在普通字段上,不要注解到复杂对象上

数据字典转换流程:

1、在Service或者Controller添加@DictAuto注解,用于切面判断此方法是需要进行数据字典转换的方法。

2、切面发现此方法是需要数据字典转换的方法之后,那么解析方法的返回参数,返回参数有多种数据类型,这里只处理集合类型:List、Set、Queue ,引用类型:Array一维数组还有普通对象类型(自定义实体bean)。

3、无论是集合类型还是普通对象类型都需要进行遍历、递归等操作,因为List里面是普通对象,对象中也有可能是集合类型。(此处需要注意,请不要在对象中的字段嵌套自己,这要会造成死循环。当然,对象中可以嵌套自己的对象类型,可以引用非自己的对象实例,因为递归操作中,我们会判断如果是null,那么终止递归)

4、对返回类型进行递归时,通过注解获取到数据字典类型(system、business等)、数据字典CODE(一级数据字典CODE,作为数据字典的分类),通过此条件去Redis数据库查询数据字典列表。将查询的数据字典列表存储在Map中。在循环遍历过程中,增加判断,如果Map中有了,那么不再查询Redis数据库,而是直接从Map中取。

5、在遍历递归对象的同时,根据数据字典注解,获取本对象中用于映射数据字典的字段值作为数据字典的CODE值(二级数据字典CODE,对应具体的数据字典),然后赋值到数据字典值上。

一、后端通过注解实现数据字典转换功能

1、新增数据字典注解定义
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 "";
}


2、新增切面,处理数据字典注解
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.