Aop+自定義註解實現資料字典翻譯

Dawn.x發表於2020-11-30

前言

一般來說,專案開發會用自定義註解去實現日誌監控等操作
在實際專案中,前後臺資料互動時,每次都需要根據一個code值去進行查詢資料庫進行中間操作進行獲取text值
本博文用自定義註解結合aop實現資料字典的自動翻譯

原始碼

首先附上資料庫表結構:

資料字典表(dict)
在這裡插入圖片描述

資料字典型別中間表(dict_type)

在這裡插入圖片描述

使用者表(user)

在這裡插入圖片描述

註解類(Dict)

package com.xiaoyang.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author xiaoyang
 * @create  2020-11-27 10:36
 * 資料字典的數字轉漢字的自定義註解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {

    /**
     * 資料dataSource
     *
     * @return
     */
    String dictDataSource();

    /**
     * 返回put到json中的文字key
     * @return
     */
    String dictText() default "";
}

Controller:

package com.xiaoyang.controller;

import com.xiaoyang.model.User;
import com.xiaoyang.service.UserService;
import com.xiaoyang.util.PageBean;
import com.xiaoyang.util.PageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author xiaoyang
 * @create  2020-11-27 16:47
 */
@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @ResponseBody
    @RequestMapping("/QueryPager")
    public PageUtils QueryPager(HttpServletRequest req) {
        User user=new User();
        user.setName("a");
        PageBean pageBean = new PageBean();
        pageBean.setRequest(req);
        List<User> users = this.userService.QueryPager(user, pageBean);
        PageUtils pageUtils = new PageUtils(users, pageBean.getTotal());
        return pageUtils;
    }
}

aop切面類:

package com.xiaoyang;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoyang.annotation.Dict;
import com.xiaoyang.service.DictService;
import com.xiaoyang.util.ObjConvertUtils;
import com.xiaoyang.util.PageUtils;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 @author xiaoyang
 @create  2020-11-27 16:52
 */
@Aspect
@Component
@Slf4j
public class DictAspect {
    //這是運算元據字典那張表的 service
    @Autowired
    private DictService dictService;
    //翻譯後拼接的內容
    private static String DICT_TEXT_SUFFIX = "_dictText";

    // 定義切點Pointcut 攔截所有對伺服器的請求
    @Pointcut("execution( * com.xiaoyang..controller.*.*(..))")
    public void excudeService() {
    }

    /**
     * 這是觸發 excudeService 的時候會執行的
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("excudeService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        //這是定義開始事件
        long time1 = System.currentTimeMillis();
        //這是方法並獲取返回結果
        Object result = pjp.proceed();
        //這是獲取到 結束時間
        long time2 = System.currentTimeMillis();
        log.debug("獲取JSON資料 耗時:" + (time2 - time1) + "ms");
        //解析開始時間
        long start = System.currentTimeMillis();
        //開始解析(翻譯欄位內部的值凡是打了 @Dict 這玩意的都會被翻譯)
        this.parseDictText(result);
        //解析結束時間
        long end = System.currentTimeMillis();
        log.debug("解析注入JSON資料  耗時" + (end - start) + "ms");
        return result;
    }

    /**
     * 本方法針對返回物件為Result 的IPage的分頁列表資料進行動態字典注入
     * 字典注入實現 通過對實體類新增註解@dict 來標識需要的字典內容,字典分為單字典code即可 ,table字典 code table text配合使用與原來jeecg的用法相同
     * 示例為SysUser   欄位為sex 新增了註解@Dict(dicCode = "sex") 會在字典服務立馬查出來對應的text 然後在請求list的時候將這個字典text,已欄位名稱加_dictText形式返回到前端
     * 例輸入當前返回值的就會多出一個sex_dictText欄位
     * {
     * sex:1,
     * sex_dictText:"男"
     * }
     * 前端直接取值sext_dictText在table裡面無需再進行前端的字典轉換了
     * customRender:function (text) {
     * if(text==1){
     * return "男";
     * }else if(text==2){
     * return "女";
     * }else{
     * return text;
     * }
     * }
     * 目前vue是這麼進行字典渲染到table上的多了就很麻煩了 這個直接在服務端渲染完成前端可以直接用
     *
     * @param result
     */
    private void parseDictText(Object result) {
        if (result instanceof PageUtils) {
            List<JSONObject> items = new ArrayList<>();
            PageUtils pageUtils = (PageUtils) result;
            //迴圈查詢出來的資料
            for (Object record : pageUtils.getData()) {
                ObjectMapper mapper = new ObjectMapper();
                String json = "{}";
                try {
                    //解決@JsonFormat註解解析不了的問題詳見SysAnnouncement類的@JsonFormat
                    json = mapper.writeValueAsString(record);
                } catch (JsonProcessingException e) {
                    log.error("json解析失敗" + e.getMessage(), e);
                }
                JSONObject item = JSONObject.parseObject(json);

                //update-begin--Author:scott -- Date:20190603 ----for:解決繼承實體欄位無法翻譯問題------
                //for (Field field : record.getClass().getDeclaredFields()) {
                for (Field field : ObjConvertUtils.getAllFields(record)) {
                    //update-end--Author:scott  -- Date:20190603 ----for:解決繼承實體欄位無法翻譯問題------
                    if (field.getAnnotation(Dict.class) != null) {
                        String code = field.getAnnotation(Dict.class).dictDataSource();
                        String text = field.getAnnotation(Dict.class).dictText();
                        //獲取當前帶翻譯的值
                        String key = String.valueOf(item.get(field.getName()));
                        //翻譯字典值對應的txt
                        String textValue = translateDictValue(code, key);
                        //  CommonConstant.DICT_TEXT_SUFFIX的值為,是預設值:
                        // public static final String DICT_TEXT_SUFFIX = "_dictText";
                        log.debug(" 字典Val : " + textValue);
                        log.debug(" __翻譯字典欄位__ " + field.getName() + DICT_TEXT_SUFFIX + ": " + textValue);
                        //如果給了文字名
                        if (!StringUtils.isEmpty(text)) {
                            item.put(text, textValue);
                        } else {
                            //走預設策略
                            item.put(field.getName() + DICT_TEXT_SUFFIX, textValue);
                        }

                    }
                    //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(item);
            }
            pageUtils.setData(items);

        }
    }


    /**
     * 翻譯字典文字
     *
     * @param code
     * @param key
     * @return
     */
    private String translateDictValue(String code, String key) {
        //如果key為空直接返回就好了
        if (ObjConvertUtils.isEmpty(key)) {
            return null;
        }
        StringBuffer textValue = new StringBuffer();
        //分割 key 值
        System.out.println(code+":::::"+key);
        String[] keys = key.split(",");
        //迴圈 keys 中的所有值
        for (String k : keys) {
            String tmpValue = null;
            log.debug(" 字典 key : " + k);
            if (k.trim().length() == 0) {
                continue; //跳過迴圈
            }
            tmpValue = dictService.selectByDatasourceCode(code,k.trim());

            if (tmpValue != null) {
                if (!"".equals(textValue.toString())) {
                    textValue.append(",");
                }
                textValue.append(tmpValue);
            }
        }
        //返回翻譯的值
        return textValue.toString();
    }

}

工具類(反射類):

package com.xiaoyang.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author xiaoyang
 * @create  2020-11-29 8:32
 */
public class ObjConvertUtils {
    /**
     * 獲取類的所有屬性,包括父類
     *
     * @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;
    }

    public static boolean isEmpty(Object object) {
        if (object == null) {
            return (true);
        }
        if ("".equals(object)) {
            return (true);
        }
        if ("null".equals(object)) {
            return (true);
        }
        return (false);
    }


}



User的實體類中使用註解(可傳兩個引數,這裡使用註解後會在切面類進行獲取註解的內容進行資料庫查詢,例如這裡userlevel屬性註解傳入的是user_level,那麼controller層查詢出結果集合後,在切面類進行字典表對應欄位的查詢):
在這裡插入圖片描述
結果:
在這裡插入圖片描述

思維導圖

自定義註解運用aop的思想去進行資料字典的轉譯從根本上來說就是為了簡化我們的程式碼,實現後不需要每次都呼叫方法去查詢(需結合快取)
最後附上流程圖供理解:
在這裡插入圖片描述
over…

相關文章