Springboot数据字典万金油

334 阅读3分钟

背景

页面查询的时候,我们一般是从库里直接拿数据,但是库里的数据有些字段是字典的,我们库里保存的也是一下代码值,这时候就需要我们对返回的代码值联合字典表进行翻译

涉及知识点:

  • 注解类
  • ResponseBodyAdvice类
  • 反射
  • 思路

    做一个拦截器,拦截返回前端的数据,然后统一对返回数据里的代码值进行分类。但是我们只需要拦截那些需要进行翻译的请求,并不需要拦截全部的响应,这就需要我们声明一个标志,当拦截器捕捉到这个标志的时候,就拦截这个响应,对里面的数据进行翻译。我们使用注解类来作为标志。

    代码

    声明DictTypeSensible注解类

    import java.lang.annotation.*;
    
    /**
     *
     * 针对单个字典
     * @Author: huangjun
     * @Date: 2021/8/18 13:10
     * @Version 1.0
     */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface DictTypeSensible  {
    
        /**
         * 实体类字段
         */
        String value();
    
        /**
         * 字典code,默认使用 value 转下划线形式
         */
        String code() default "dm";
    
        /**
         * dmb表的类型(dmbyw)
         */
        String type() default "";
    }
    

    声明DictTypeSensibles

    import java.lang.annotation.*;
    
    /**
     * @Author: huangjun
     * @Date: 2021/8/18 13:12
     * @Version 1.0
     *
     * 多个字典值
     */
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface DictTypeSensibles {
    
        DictTypeSensible[] value();
    }
    

    定义拦截器DictTypeSensibleAspect

    
    import cn.hutool.core.util.ReflectUtil;
    import cn.hutool.core.util.StrUtil;
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import com.baomidou.mybatisplus.extension.api.R;
    import com.ufgov.gdyb.expansion.config.constant.RedisKeyConstant;
    import com.ufgov.test.abc.entity.main.Dmb;
    import com.ufgov.test.entity.service.main.IDmbService;
    import com.ufgov.test.entity.util.RedisService;
    import lombok.AllArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    
    import java.lang.reflect.Field;
    
    /**
     * @Author: huangjun
     * @Date: 2021/8/18 13:13
     * @Version 1.0
     */
    @Slf4j
    @ControllerAdvice
    @AllArgsConstructor
    public class DictTypeSensibleAspect implements ResponseBodyAdvice<Object> {
    
        @Autowired
        private RedisService redisService;
        @Autowired
        private IDmbService dmbMainService;
    
        @Override
        public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
            return methodParameter.hasMethodAnnotation(DictTypeSensible.class)
                    || methodParameter.hasMethodAnnotation(DictTypeSensibles.class);
        }
    
        @Override
        public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
            if (o == null) {
                return null;
            }
            // 获取方法 注解参数
            DictTypeSensible[] sensibles;
            if (methodParameter.hasMethodAnnotation(DictTypeSensible.class)) {
                DictTypeSensible sensible = methodParameter.getMethodAnnotation(DictTypeSensible.class);
                sensibles = new DictTypeSensible[]{sensible};
            } else {
                DictTypeSensibles typeSensibles = methodParameter.getMethodAnnotation(DictTypeSensibles.class);
                sensibles = typeSensibles == null ? new DictTypeSensible[]{} : typeSensibles.value();
            }
            // 处理返回值
            this.dis(o, sensibles);
            return o;
        }
    
        private void dis(Object obj, DictTypeSensible[] sensibles) {
            // 处理返回类型
            if (obj instanceof R) {
                this.dis(((R) obj).getData(), sensibles);
                return;
            }
            if (obj instanceof IPage) {
                ((IPage) obj).getRecords().forEach(e -> this.dis(e, sensibles));
                return;
            }
            //TODO 可以自定义其他的返回值,其本质目的是为了获取返回的具体数据
            Class<?> clazz = obj.getClass();
            for (DictTypeSensible sensible : sensibles) {
                try {
                    Field field = ReflectUtil.getField(clazz, sensible.value());
                    if (field == null || !field.getType().getSimpleName().contains("String")) {
                        log.warn("字典 {} 进行忽略,不存在或者是非String类型 ------", sensible.value());
                        continue;
                    }
                    Object val = ReflectUtil.getFieldValue(obj, field);
                    if (val == null) {
                        continue;
                    }
                    String type = StrUtil.isEmpty(sensible.type())
                            ? StrUtil.toUnderlineCase(sensible.value()) : sensible.type();
    
                    String label = redisService.get(RedisKeyConstant.REDIS_DICT_NAME_SPACE + type + "_" + val);
                    if (StringUtils.isBlank(label)){
                        Dmb dmbMain = dmbMainService.getBaseMapper().selectOne(Wrappers.<Dmb>lambdaQuery().eq(Dmb::getDmbyw, type).eq(Dmb::getDm, val).eq(Dmb::getAgencyCode,"440199").groupBy(Dmb::getDmbyw));
                        label = dmbMain == null ? null : dmbMain.getDmfy();
                        if (StringUtils.isBlank(label)){
                            continue;
                        }
                        redisService.set(RedisKeyConstant.REDIS_DICT_NAME_SPACE + type + "_" + val,label);
                    }
                    ReflectUtil.setFieldValue(obj, field, label);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    使用方式,直接在Controller类上面使用,使用方式类似于@ApiImplicitParams注解

    
    @Api(tags = "服务")
    @RestController
    @RequestMapping("/my")
    public class TestController {
    
        @Autowired
        TestService testService;
    
    
        @ApiResponses({})
        @ApiImplicitParams({
    //            @ApiImplicitParam(value = "", name = "params", required = true, paramType = "body", dataType = "Map", dataTypeClass = Map.class)
        })
        @ApiOperation(value = "根据条件查询明细表内容", notes = "", httpMethod = "POST")
        @PostMapping("/getPage")
        @ResponseBody
        @DictTypeSensibles({
                @DictTypeSensible(value = "payType",type ="PAYTYPE"),
                @DictTypeSensible(value = "personType",type = "PERSONTYPE"),
                @DictTypeSensible(value = "chechType",type = "CHECKTYPE"),
        })
        public R getPage(@RequestBody Map params)  {
            R r= R.ok( testService.getListPageByMap(params));
            r.setCode(200);
            return r;
        }
    }
    

    补充说明ResponseBodyAdvice

    ResponseBodyAdvice类也是个拦截器,是针对响应体进行拦截,里面有两个方法supports和beforeBodyWrite,当supports方法返回true时,执行beforeBodyWrite方法,否则不执行beforeBodyWrite。ResponseBodyAdvice还有个@ControllerAdvice注解可以声明需要拦截的包或类,具体自行学习吧