JAVA 实战 字典自动翻译

JAVA字典 自动翻译

(1) MySQL8 表结构

CREATE TABLE `sys_dict` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `type_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型编码',
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典项名称',
  `value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典项值'
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=417 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='字典数据表';

(2) Mypper.xml

<select id="translateDictByKeys" resultType="com.wangyao.common.database.model.DictModel">
        select
        ${name} as "value",
        ${value} as "code"
        from sys_dict
        where ${value}
        IN (
        <foreach item="key" collection="keys" separator=",">
            #{key}
        </foreach>
        )
    </select>

    <select id="queryManyDictByKeys" resultType="com.wangyao.common.database.model.DictModelMany">
        SELECT
        item.type_code AS dictCode,
        item.name AS "value",
        item.value AS "code"
        FROM
        sys_dict item
        WHERE item.type_code IN (
        <foreach item="dictCode" collection="dictCodeList" separator=",">
            #{dictCode}
        </foreach>
        )
        AND item.value IN (
        <foreach item="key" collection="keys" separator=",">
            #{key}
        </foreach>
        )
    </select>

    <select id="translateAllDict" resultType="com.wangyao.common.database.model.DictModelMany">
        select
        type_code as "dictCode",
        name as "value",
        `value` as "code"
        from sys_dict
    </select>

(3) mapper 接口

/***
     * Desc:
     * @param name 字段名
     * @param value 值
     * @param keys keys
     * @return {@link List<DictModel>}
     * @author 01传说
     */
    List<DictModel> translateDictByKeys(@Param("name") String name, @Param("value") String value, @Param("keys") List<String> keys);

    /***
     * Desc:
     * @param dictCodeList 字典集合
     * @param keys keys
     * @return {@link List<DictModelMany>}
     * @author 01传说
     */
    List<DictModelMany> queryManyDictByKeys(@Param("dictCodeList") List<String> dictCodeList, @Param("keys") List<String> keys);

    /***
     * Desc:
     * @return {@link List<DictModelMany>}
     * @author 01传说
     */
    List<DictModelMany> translateAllDict();

(4)service实现接口

public interface BaseCommonService {
    /***
     * Desc:
     * @param text text
     * @param code code
     * @param values values
     * @return {@link List<DictModel>}
     * @author 01传说
     */
    List<DictModel> translateDictByKeys(String text, String code, String values);

    /***
     * Desc:
     * @param dictCodes dictCodes
     * @param values values
     * @return {@link Map<String,List<DictModel>>}
     * @author 01传说
     */
    Map<String, List<DictModel>> translateManyDict(List<String> dictCodes, List<String> values);

    /***
     * Desc:
     * @return {@link List<DictModelMany>}
     * @author 01传说
     */
    List<DictModelMany> queryManyDictByKeys();

(5)serviceImpl

@Service
@Slf4j
public class BaseCommonServiceImpl implements BaseCommonService {

    @Resource
    private BaseCommonMapper baseCommonMapper;

    @Override
    public List<DictModel> translateDictByKeys(String text, String code, String values) {
        return baseCommonMapper.translateDictByKeys(text,code, Collections.singletonList(values));
    }

    @Override
    public Map<String, List<DictModel>> translateManyDict(List<String> dictCodes, List<String> values) {
        List<DictModelMany> list = baseCommonMapper.queryManyDictByKeys(dictCodes, values);
        Map<String, List<DictModel>> dictMap = new HashMap<>(16);
        for (DictModelMany dict : list) {
            List<DictModel> dictItemList = dictMap.computeIfAbsent(dict.getDictCode(), i -> new ArrayList<>());
            dictItemList.add(new DictModel(dict.getCode(), dict.getValue()));
        }
        return dictMap;
    }
    
    @Override
    public List<DictModelMany> queryManyDictByKeys() {
        return baseCommonMapper.translateAllDict();
    }
}

(6)封装工具类

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Lists;
import com.wangyao.common.annotation.Dict;
import com.wangyao.common.cache.util.RedisUtil;
import com.wangyao.common.constant.ConstantNumeral;
import com.wangyao.common.database.model.DictModel;
import com.wangyao.common.database.service.BaseCommonService;
import com.wangyao.common.database.vo.PageVO;
import com.wangyao.common.response.ServerResponseEntity;
import com.wangyao.common.util.ConvertUtils;
import com.wangyao.common.vo.EsPageVO;
import jakarta.annotation.Resource;
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.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.alibaba.fastjson.JSON.toJavaObject;
@Aspect
@Component
@Slf4j
public class DictAspect {

    @Resource
    private BaseCommonService baseCommonService;

    /**
     * 定义切点Pointcut
     */
    @Pointcut("execution(public * com.wangyao..*.*Controller.*(..))")
    public void executionController() {
    }

    @Around("executionController()")
    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();
        this.translateDictFromTable(result);
        long end=System.currentTimeMillis();
        log.debug("解析注入JSON数据  耗时"+(end-start)+"ms");
        return result;
    }

    /**
     * 读取数据库翻译字典
     * @param result 返回结果
     */
    private void translateDictFromTable(Object result) throws IllegalAccessException {
        if (result instanceof ServerResponseEntity) {
            // 分页处理
            handle(result);
        }
    }

    @SuppressWarnings("all")
    private void handle(Object result) throws IllegalAccessException {
        if (((ServerResponseEntity<?>) result).getData() instanceof PageVO) {
            List<JSONObject> items = handleList(((PageVO<?>) ((ServerResponseEntity<?>) result).getData()).getList());
            if (CollUtil.isNotEmpty(items)) {
                ((PageVO) ((ServerResponseEntity<?>) result).getData()).setList(items);
            }
        } else if (((ServerResponseEntity<?>) result).getData() instanceof List) {
            List<JSONObject> items = handleList(((List<?>) ((ServerResponseEntity<?>) result).getData()));
            if (CollUtil.isNotEmpty(items)) {
                ((ServerResponseEntity<List>) result).setData(items);
            }
        }else if (((ServerResponseEntity<?>) result).getData() instanceof EsPageVO
                && ObjectUtil.isNotEmpty(((EsPageVO) ((ServerResponseEntity<?>) result).getData()).getList())) {
            List<JSONObject> items = handleList(((EsPageVO<?>) ((ServerResponseEntity<?>) result).getData()).getList());
            if (CollUtil.isNotEmpty(items)) {
                ((EsPageVO)  ((ServerResponseEntity<List>) result).getData()).setList(items);
            }
        }else if(((ServerResponseEntity<?>) result).getData() instanceof String
        || ((ServerResponseEntity<?>) result).getData() instanceof Integer
                ||((ServerResponseEntity<?>) result).getData() instanceof Long){

        } else if (((ServerResponseEntity<?>) result).getData() != null) {
            List<JSONObject> items = handleList(List.of((Object)((ServerResponseEntity<?>) result).getData()));
            if (CollUtil.isNotEmpty(items)) {
                ((ServerResponseEntity) result).setData(items.get(ConstantNumeral.ZERO.value()));
            }
        }
    }

    /**
     * 判断object是否为基本类型
     * @param object 对象
     */
    public static boolean isBaseType(Object object) {
        Class<?> className = object.getClass();
        return className.equals(Integer.class) ||
                className.equals(Byte.class) ||
                className.equals(Long.class) ||
                className.equals(Double.class) ||
                className.equals(Float.class) ||
                className.equals(Character.class) ||
                className.equals(Short.class) ||
                className.equals(Boolean.class);
    }

    @NotNull
    private List<JSONObject> handleList(List<?> result){
        List<JSONObject> items = Lists.newArrayList();
        //step.1 筛选出加了 Dict 注解的字段列表
        List<Field> dictFieldList = new ArrayList<>();
        // 字典数据列表, key = 字典code,value=数据列表
        Map<String, List<String>> dataListMap = new HashMap<>(16);

        for (Object record : result) {
            try {
                if(!isBaseType(record)){
                    items.add(handleObject(record, dictFieldList, dataListMap));
                }
            } catch (JSONException | IllegalAccessException e) {
                log.error("can not cast to JSONObject:{}", record.getClass(), e);
            }
        }

        //step.2 调用翻译方法,一次性翻译
        Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);

        //step.3 将翻译结果填充到返回结果里
        for (JSONObject record : items) {
            translationField(record, dictFieldList, translText);
        }
        return items;
    }


    private JSONObject handleObject(Object record, List<Field> dictFieldList, Map<String, List<String>> dataListMap) throws IllegalAccessException {
        JSONObject item = JSONObject.parseObject(JSON.toJSONString(record, SerializerFeature.WriteMapNullValue));
        // 遍历所有字段,把字典Code取出来,放到 map 里
        Field[] fields = ConvertUtils.getAllFields(record);
        for (Field field : fields) {
            String value = item.getString(field.getName());
            if (ConvertUtils.isEmpty(value)) {
                continue;
            }
            if (field.getType().getName().contains("wangyao")) {
                List<JSONObject> items = handleList(List.of(toJavaObject(JSONObject.parseObject(value), field.getType())));
                item.put(field.getName(), items.get(0));
            }
            if(field.getType().equals(List.class)){
                // 获取List泛型中的对象的Class
                Type genericType = field.getGenericType();
                // 如果是泛型参数的类型
                if(genericType instanceof ParameterizedType pt){
                    //得到泛型里的class类型对象
                    Type actualTypeArgument = pt.getActualTypeArguments()[0];
                    //List中的泛型类型不为T
                    if(!"T".equals(actualTypeArgument.getTypeName())){
                        Class<?> genericClazz = (Class<?>)actualTypeArgument;
                        Field[] declaredFields = genericClazz.getDeclaredFields();
                        handleListField(dictFieldList, dataListMap, declaredFields, value);
                    }else{
                        log.info("List泛型类型为T");
                        Class<? extends Type> aClass = actualTypeArgument.getClass();
                        System.out.println(Arrays.toString(aClass.getDeclaredFields()));
                    }
                }
            }
            if (field.getAnnotation(Dict.class) != null) {
                if (!dictFieldList.contains(field)) {
                    dictFieldList.add(field);
                }
                String code = field.getAnnotation(Dict.class).dicCode();
                List<String> dataList;
                dataList = dataListMap.computeIfAbsent(code, k -> new ArrayList<>());
                this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
            }
        }
        return item;
    }

    /***
     * Desc:
     * @param dictFieldList 字典字段列表
     * @param dataListMap 字典数据列表
     * @param declaredFields 声明的字段
     * @param value 值
     * @author 01传说
     */
    private void handleListField(List<Field> dictFieldList, Map<String, List<String>> dataListMap, Field[] declaredFields, String value) {
        for (Field declaredField : declaredFields) {
            if (declaredField.getAnnotation(Dict.class) != null) {
                if (!dictFieldList.contains(declaredField)) {
                    dictFieldList.add(declaredField);
                }
                JSONArray jsonArray = JSONArray.parseArray(value);
                if(CollectionUtil.isNotEmpty(jsonArray)){
                    for(Object object:jsonArray){
                        JSONObject listItem = (JSONObject) object;
                        String listValue = listItem.getString(declaredField.getName());
                        String declaredFieldDictCode = declaredField.getAnnotation(Dict.class).dicCode();
                        if (ConvertUtils.isNotEmpty(listValue)) {
                            List<String> dataList;
                            dataList = dataListMap.computeIfAbsent(declaredFieldDictCode, k -> new ArrayList<>());
                            this.listAddAllDeduplicate(dataList, Arrays.asList(listValue.split(",")));
                        }
                    }
                }
            }
        }
    }

    private void translationField(JSONObject record, List<Field> dictFieldList, Map<String, List<DictModel>> translText) {
        for (Map.Entry<String, Object> next : record.entrySet()) {
            Object value = next.getValue();
            if (value instanceof List) {
                JSONArray array = JSONArray.parseArray(JSON.toJSONString(value));
                for (Object o : array) {
                    if(!o.getClass().getName().contains("wangyao")){
                        continue;
                    }
                    JSONObject listItem = (JSONObject) o;
                    handleTranslate(listItem, dictFieldList, translText);
                }
                next.setValue(array);
            }
        }
        handleTranslate(record, dictFieldList, translText);
    }

    /***
     * Desc:
     * @param record 记录
     * @param dictFieldList 字典字段列表
     * @param translText 翻译字段
     * @author 01传说
     */
    private void handleTranslate(JSONObject record, List<Field> dictFieldList, Map<String, List<DictModel>> translText) {
        for (Field field : dictFieldList) {
            String fieldDictCode = field.getAnnotation(Dict.class).dicCode();
            String value = record.getString(field.getName());

            if (ConvertUtils.isNotEmpty(value)) {
                List<DictModel> dictModels = translText.get(fieldDictCode);
                if(CollUtil.isEmpty(dictModels)){
                    continue;
                }

                String textValue = this.translDictText(dictModels, value);
                log.debug(" 字典Val : " + textValue);
                log.debug(" __翻译字典字段__ " + field.getName() + "DictText: " + textValue);
                log.debug(" ---- dictCode: " + fieldDictCode);
                log.debug(" ---- value: " + value);
                log.debug(" ----- text: " + textValue);
                log.debug(" ---- dictModels: " + JSON.toJSONString(dictModels));
                record.put(field.getName() + "DictText", textValue);
            }
        }
    }

    /**
     * list 去重添加
     */
    private void listAddAllDeduplicate(List<String> dataList, List<String> addList) {
        // 筛选出dataList中没有的数据
        List<String> filterList = addList.stream().filter(i -> !dataList.contains(i)).toList();
        dataList.addAll(filterList);
    }

    /**
     * 一次性把所有的字典都翻译了
     * 1.  所有的普通数据字典的所有数据只执行一次SQL
     * 2.  表字典相同的所有数据只执行一次SQL
     * @param dataListMap 字典信息
     * @return Map
     */
    private Map<String, List<DictModel>> translateAllDict(Map<String, List<String>> dataListMap) {
        // 翻译后的字典文本,key=dictCode
        Map<String, List<DictModel>> translText = new HashMap<>();
        // 需要翻译的数据(有些可以从redis缓存中获取,就不走数据库查询)
        List<String> needTranslData = new ArrayList<>();
        //step.1 先通过redis中获取缓存字典数据
        for (String dictCode : dataListMap.keySet()) {
            List<String> dataList = dataListMap.get(dictCode);
            if (dataList.isEmpty()) {
                continue;
            }
            // 表字典需要翻译的数据
            List<String> needTranslDataTable = new ArrayList<>();
            for (String s : dataList) {
                String data = s.trim();
                if (data.isEmpty()) {
                    continue; //跳过循环
                }
                if (dictCode.contains(",")) {
                    String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, data);
                    existKey(translText, dictCode, needTranslDataTable, data, keyString);
                } else {
                    String keyString = String.format("sys:cache:dict::%s:%s", dictCode, data);
                    existKey(translText, dictCode, needTranslData, data, keyString);
                }
            }
            //step.2 调用数据库翻译表字典
            if (!needTranslDataTable.isEmpty()) {
                String[] arr = dictCode.split(",");
                String text = arr[0], code = arr[1];
                String values = String.join(",", needTranslDataTable);
                log.info("translateDictFromTableByKeys.dictCode:" + dictCode);
                log.info("translateDictFromTableByKeys.values:" + values);
                List<DictModel> texts = baseCommonService.translateDictByKeys(text, code, values);
                log.info("translateDictFromTableByKeys.result:" + texts);
                List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
                list.addAll(texts);

                // 做 redis 缓存
                for (DictModel dict : texts) {
                    String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue());
                    try {
                        RedisUtil.set(redisKey, dict.getCode(),60);
                    } catch (Exception e) {
                        log.warn(e.getMessage(), e);
                    }
                }
            }
        }

        //step.3 调用数据库进行翻译普通字典
        if (!needTranslData.isEmpty()) {
            List<String> dictCodeList = Arrays.asList(dataListMap.keySet().toArray(new String[]{}));
            // 将不包含逗号的字典code筛选出来,因为带逗号的是表字典,而不是普通的数据字典
            List<String> filterDictCodes = dictCodeList.stream().filter(key -> !key.contains(",")).collect(Collectors.toList());
            String dictCodes = String.join(",", filterDictCodes);
            String values = String.join(",", needTranslData);
            log.info("translateManyDict.dictCodes:" + dictCodes);
            log.info("translateManyDict.values:" + values);
            Map<String, List<DictModel>> manyDict = baseCommonService.translateManyDict(filterDictCodes, needTranslData);
            log.info("translateManyDict.result:" + manyDict);
            for (String dictCode : manyDict.keySet()) {
                List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
                List<DictModel> newList = manyDict.get(dictCode);
                list.addAll(newList);

                // 做 redis 缓存
                for (DictModel dict : newList) {
                    String redisKey = String.format("sys:cache:dict::%s:%s", dictCode, dict.getCode());
                    try {
                        RedisUtil.set(redisKey, dict.getValue(),60);
                    } catch (Exception e) {
                        log.warn(e.getMessage(), e);
                    }
                }
            }
        }
        return translText;
    }

    private void existKey(Map<String, List<DictModel>> translText, String dictCode, List<String> needTranslDataTable, String data, String keyString) {
        if (RedisUtil.hasKey(keyString)) {
            try {
                String text = ConvertUtils.getString(RedisUtil.get(keyString));
                List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
                list.add(new DictModel(data, text));
            } catch (Exception e) {
                log.warn(e.getMessage());
            }
        } else if (!needTranslDataTable.contains(data)) {
            // 去重添加
            needTranslDataTable.add(data);
        }
    }

    /**
     * 字典值替换文本
     * @param dictModels 字典值
     * @param values 值
     * @return String
     */
    private String translDictText(List<DictModel> dictModels, String values) {
        List<String> result = new ArrayList<>();
        // 允许多个逗号分隔,允许传数组对象
        String[] splitVal = values.split(",");
        for (String val : splitVal) {
            String dictText = val;
            for (DictModel dict : dictModels) {
                if (val.equals(dict.getCode())) {
                    dictText = dict.getValue();
                    break;
                }
            }
            result.add(dictText);
        }
        return String.join(",", result);
    }
}

(7)Dict 实体类

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
    /**
     * 方法描述:  数据code
     * @return 返回类型: String
     */
    String dicCode();
    /**
     * 方法描述:  数据Text
     * @return 返回类型: String
     */
    String dicText() default "";
}

(8)DictModel实体类

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DictModel implements Serializable {
	@Serial
	private static final long serialVersionUID = 1L;
	public DictModel() {
	}
	public DictModel(String code, String value) {
		this.code = code;
		this.value = value;
	}
	/**
	 * 字典value
	 */
	private String code;
	/**
	 * 字典文本
	 */
	private String value;
}

(9)实际应用

    @Dict(dicCode = "up_price_flag")
    private Integer upPriceFlag;

up_price_flag对应数据库中 type_code值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

01传说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值