zoukankan      html  css  js  c++  java
  • AOP+Redis+SpringCache翻译字典状态

    1,字典表Or枚举类?

    项目里有很多标识状态的字段,比如订单状态:0-未支付,1-已支付,2-已取消。或者性别sex: 0-未知,1-男,2-女 。等等。一般这种我们都会建相应的枚举类,比如性别枚举:

    public enum SexEnum {
        UNKNOWN(0,"未知"),
        MAN(1,"男"),
        WOMAN(2,"女");
        private final int code;
        private final String text;
    
        SexEnum(int code, String text) {
            this.code = code;
            this.text = text;
        }
        //省略getter方法
    }
    

    项目里一般都有字典表,我们写了枚举类,还需要把他们存在字典表么?

    以前我也没搞清楚,现在清楚了。存字典表里,可以用AOP进行统一字典翻译。比如你返回给前端一个分页列表,列表有sex字段,你就需要在接口文档写上:sex(0-未知,1-男,2-女)。像性别这种还好,一般不会再新增状态。但是其他类型字段,以后再增加一种类型,接口文档就要改,前端代码也要改。但是如果后端返回列表时,把这些字段都给前端翻译好,即使以后再增加类型,前端直接展示,就不用改代码了。比如返回的列表里,遇到这种类型值的,比如以前返回sex=1,现在返回俩字段:sex=1, sex_text=男,前端直接拿到sex_text做展示即可。

    有了字典,还需要枚举类或者常量类么?需要的。因为你的代码里,常常是需要关于sex类型的判断的,比如:

    if(SexEnum.MAN.getCode() == user.getSex()){
    
    	//do something
    
    }
    

    这就需要在代码里再维护一份和字典一样的常量或枚举。至于用枚举还是常量类,无所谓。

    2.AOP翻译字典表

    一个注解:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 字典
     * create by lihaoyang on 2020/10/28
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Dict {
    
        /**
         * 数据字典code
         * @return
         */
        String dictCode();
    }
    
    

    注解标在实体类属性上:

     /**
         * 性别(0-默认未知,1-男,2-女)
         */
        @Dict(dictCode = "sex")
        private Integer sex;
    

    一个切面类:

    import com.alibaba.fastjson.JSONObject;
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.nb.nbbase2.beans.Result;
    import com.nb.nbbase2.constant.SysConstant;
    import com.nb.nbbase2.service.SysDictService;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.ArrayUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.AnnotationConfigUtils;
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.stereotype.Component;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 字典aop类
     * create by lihaoyang on 2020/10/28
     */
    @Slf4j
    @Aspect  //标识该类是一个切面
    @Component
    public class DictAspect {
    
        @Autowired
        private SysDictService sysDictService;
    
        @Autowired
        private ObjectMapper objectMapper;
    
        //切入点
        @Pointcut("execution(public * com.nb.nbbase2.*.*Controller.*(..))")
        public void dictPointCut() {
    
        }
    
    
        @Around("dictPointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
    
            Object result = point.proceed();
            long start=System.currentTimeMillis();
            //解析字典
            this.parseDictText(result);
            long end=System.currentTimeMillis();
            log.debug("AOP解析字典耗时{}ms",(end-start));
    
            return result;
        }
    
        /**
         * 解析字典
         * @param result
         */
        private void parseDictText(Object result) {
    
            //如果control返回的是Result对象
            if(result instanceof Result){
                //如果是分页, TODO:是不是也可以处理不分页的list??或者单个对象?道理一样,这里就不写了
                if(((Result) result).getResult() instanceof IPage){
                    //存放字典转换加工处理后的结果列表
                    List<JSONObject> items = new ArrayList<>();
                    IPage iPage = (IPage) ((Result) result).getResult();
                    //遍历分页列表,
                    for(Object record : iPage.getRecords()){
                        //解决@JsonFormat注解解析不了的问题,这个我没实验,先这样吧!
                        //一行数据的json:如{"sex":1,"id":9,"userType":3,"email":"ttrrr@qq.com","username":"admin后台","status":0}
                        String json = null;
                        try {
                            json = objectMapper.writeValueAsString(record);
                        } catch (JsonProcessingException e) {
                            log.error("翻译字典json解析失败:"+e.getMessage(),e);
                        }
                        //存放加工处理后的一行数据,准备往里头加字段text用
                        JSONObject itemObj = JSONObject.parseObject(json);
                        System.err.println("对象==========> "+itemObj.toJSONString());
                        //反射获取到所有属性
                        for(Field field : getAllFields(record)){
                            Dict dict = field.getAnnotation(Dict.class);
                            if(dict != null){
                                //获取实体类成员变量上的@Dict注解的dictCode值 比如@Dict(dictCode = "sex")
                                String dictCode = dict.dictCode();
                                //获取属性的值,比如sex=1
                                String filedValue =  String.valueOf(itemObj.get(field.getName()));
                                //翻译字典,加_text
                                String itemText = convertDictValue2Text(dictCode,filedValue);
                                itemObj.put(field.getName()+ SysConstant.DICT_LABEL_SUFFIX,itemText);
    
                                //date类型默认转换string格式化日期
                                //if (field.getType().getName().equals("java.util.Date")&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){
                                //    SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                //    item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
                                //}
                            }
                        }
                        items.add(itemObj);
                    }
                    //////set给分页////
                    iPage.setRecords(items);
                }
            }
        }
    
    
        /**
         * 根据字典code和字典值,转换为字典text文本
         * @param dictCode 字典表code 比如sex
         * @param dictValue 字典项value 比如男=1
         * @return
         */
        private String convertDictValue2Text(String dictCode,String dictValue){
            if(StringUtils.isEmpty(dictValue)){
                return null;
            }
            StringBuilder dictText = new StringBuilder();
            //可能一个字段存多个字典值,比如:允许性别字段(1,2)男女都允许,逗号分隔的,这里需要分隔一下,循环处理。
            //如果没有一个字典存多个字典的需求,直接用一次查询翻译就好了??
            String[] dictValueArr = dictValue.split(",");
            if(ArrayUtils.isNotEmpty(dictValueArr)){
                for(String dictVal : dictValueArr){
                    String text = null;
    
                    if(dictVal.trim().length() != 0){
                        text = sysDictService.findTextByCodeAndDictItemValue(dictCode,dictVal);
                    }
                    if(text != null){
                        if(!"".equals(dictText.toString())){
                            dictText.append(",");
                        }
                        dictText.append(text);
                    }
                }
            }
            return dictText.toString();
        }
    
    
        /**
         * 获取类的所有属性,包括父类
         *
         * @param object
         * @return
         */
        public static Field[] getAllFields(Object object) {
            Class<?> clazz = object.getClass();
            List<Field> fieldList = new ArrayList<>();
            while (clazz != null) {
                fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
                //对于继承的对象,父类的属性也得获取一下
                clazz = clazz.getSuperclass();
            }
            Field[] fields = new Field[fieldList.size()];
            fieldList.toArray(fields);
            return fields;
        }
    
    
    }
    

    查询用户列表,返回json效果:

    {
        "message":"成功",
        "code":200,
        "result":{
            "records":[
                {
                    "userType_text":"学生",
                    "sex_text":"未知",
                    "sex":0,
                    "allowSex_text":"男,女",
                    "id":6,
                    "userType":2,
                    "status_text":"启用",
                    "allowSex":"1,2",
                    "email":"asd@qq.com",
                    "username":"张三",
                    "status":1
                },
                {
                    "userType_text":"学生",
                    "sex_text":"男",
                    "sex":1,
                    "allowSex_text":"男",
                    "id":7,
                    "userType":2,
                    "status_text":"启用",
                    "allowSex":"1,",
                    "email":"dsasd@qq.com",
                    "username":"李四",
                    "status":1
                },
                {
                    "userType_text":"老师",
                    "sex_text":"未知",
                    "sex":0,
                    "allowSex_text":"男",
                    "id":8,
                    "userType":1,
                    "status_text":"启用",
                    "allowSex":"1",
                    "email":"gfdsa@qq.com",
                    "username":"张老师",
                    "status":1
                },
                {
                    "userType_text":"管理员",
                    "sex_text":"男",
                    "sex":1,
                    "allowSex_text":"男,女",
                    "id":9,
                    "userType":3,
                    "status_text":"禁用",
                    "allowSex":"1,2",
                    "email":"ttrrr@qq.com",
                    "username":"admin后台",
                    "status":0
                }
            ],
            "total":4,
            "size":10,
            "current":1,
            "orders":[
    
            ],
            "optimizeCountSql":true,
            "hitCount":false,
            "searchCount":true,
            "pages":1
        },
        "timestamp":1605668907169
    }
    

    可以看到,性别sex、用户状态status、用户类型userType 都对应多了一个_text结尾的翻译字段。

    对于字典表,可以存到redis缓存起来:

    mybatis-plus的通用枚举:听同事说他们以前用mybatis-plus的枚举,多么多么好用,看了一下,这么搞得:

    实体类属性类型直接是你的枚举类:教师职称

    /**
         * 职称(引用字典表)
         * 原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
         */
        private TeacherTitleEnum title;
    

    教师职称枚举类:

    import com.baomidou.mybatisplus.annotation.EnumValue;
    import com.fasterxml.jackson.annotation.JsonValue;
    
    /**
     * 教师职称
     * @author lihaoyang
     * @date 2020/11/12
     */
    public enum TeacherTitleEnum {
    
        ZHUJIAO(1, "助教"),
        JIANGSHI(2, "讲师"),
        FUJIAOSHOU(3, "副教授"),
        JIAOSHOU(4,"教授");
    
        @EnumValue//标记数据库存的值是code
        private final int code;
        private final String desc;
    
    
    
        TeacherTitleEnum(int code, String desc) {
            this.code = code;
            this.desc = desc;
        }
    
        public int getCode() {
            return code;
        }
        @JsonValue    //标记响应json值
        public String getDesc() {
            return desc;
        }
    }
    

    测试:

       @Test
        public void insert(){
    //        SysTeacher teacher = new SysTeacher();
    //        teacher.setTeacherName("小羊");
    //        teacher.setTitle(TeacherTitleEnum.FUJIAOSHOU);
    //        teacherMapper.insert(teacher);
            List<SysTeacher> list = teacherMapper.selectList(null);
            for (SysTeacher tea : list) {
                System.err.println(JSONObject.toJSONString(tea));
            }
        }
        ////打印:
    //
    //        {"id":1,"teacherName":"牛贝","title":"JIANGSHI"}
    //        {"id":2,"teacherName":"西决","title":"ZHUJIAO"}
    //        {"id":3,"teacherName":"石竹","title":"FUJIAOSHOU"}
    //        {"id":4,"teacherName":"樟树鱼","title":"JIAOSHOU"}
    //        {"id":5,"teacherName":"小羊","title":"FUJIAOSHOU"}
    //        {"id":6,"teacherName":"小羊","title":"FUJIAOSHOU"}
    //        {"id":7,"teacherName":"小羊","title":"FUJIAOSHOU"}
    //        {"id":8,"teacherName":"小羊","title":"FUJIAOSHOU"}
    //        {"id":9,"teacherName":"小羊","title":"FUJIAOSHOU"}
    

    他并没有把枚举的汉字描述翻译出来,而是把枚举的变量值翻译出来了。前端拿到怎么处理??

    完整代码:https://github.com/lhy1234/Project-Practice/tree/master/aop_dict

  • 相关阅读:
    记录一下 一个复杂SQL的执行效率分析
    1582 Incorrect parameter count in the call to native function 'FIND_IN_SET'
    教我兄弟学Android逆向10 静态分析反调试apk
    教我兄弟学Android逆向09 IDA动态破解登陆验证
    教我兄弟学Android逆向08 IDA爆破签名验证
    教我兄弟学Android逆向07 IDA破解第一个so
    教我兄弟学Android逆向06 用AndroidStudio编写第一个so
    教我兄弟学Android逆向05 在smali代码中插入Log
    教我兄弟学Android逆向04 动态调试smali代码
    教我兄弟学Android逆向03 破解第一个Android游戏
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/13999165.html
Copyright © 2011-2022 走看看