目的

统一数据字典转换的写法,代码风格统一。
转换方式为手动调用工具类,且提供peek方法,较为灵活。
目前数据字典转换还有一种注解转换的方式,其逻辑多为aop拦截注解方法,对其返回值以反射的方式设置字段值,也是很好的处理方式,可根据场景自行选择。

使用展示

package cn.wangjiahang.record.util.dev.test;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.RandomUtil;
import cn.wangjiahang.record.util.dev.BizCommonUtil;
import cn.wangjiahang.record.util.dev.base.BizPageVo;
import cn.wangjiahang.record.util.dev.base.DeptService;
import cn.wangjiahang.record.util.dev.base.DictService;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Test;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author jh.wang
 * @since 2022/12/10
 */
public class BizUtilTest {
    private final BizCommonUtil bizUtil = new BizCommonUtil(new DictService(), new DeptService());

    @Test
    public void aTest(){
        final List<BizPageVo> pageContent = this.mock(5);
        bizUtil.buildShowValue(pageContent,
                Triple.of("status", BizPageVo::getStatus, BizPageVo::setStatusName),
                Triple.of("type", BizPageVo::getType, BizPageVo::setTypeName)
        );

        pageContent.forEach(System.out::println);
    }
    /*
		BizPageVo(deptNo=3, status=1, type=1, deptName=null, statusName=状态1, typeName=类型1)
		BizPageVo(deptNo=1, status=2, type=1, 2, deptName=null, statusName=状态2, typeName=类型1,类型2)
		BizPageVo(deptNo=1, status=2, type=1, deptName=null, statusName=状态2, typeName=类型1)
		BizPageVo(deptNo=1, status=1, type=1, 2, deptName=null, statusName=状态1, typeName=类型1,类型2)
		BizPageVo(deptNo=1, status=1, type=1, deptName=null, statusName=状态1, typeName=类型1)
    */

    @Test
    public void bTest(){
        final List<BizPageVo> pageContent = this.mock(5);
        bizUtil.buildShowValue(pageContent,
                Pair.of(BizPageVo::getDeptNo, BizPageVo::setDeptName),
                Triple.of("status", BizPageVo::getStatus, BizPageVo::setStatusName),
                Triple.of("type", BizPageVo::getType, BizPageVo::setTypeName)
        );

        pageContent.forEach(System.out::println);
    }
    /*
		BizPageVo(deptNo=3, status=1, type=2, deptName=部门1-部门2-部门3, statusName=状态1, typeName=类型2)
		BizPageVo(deptNo=3, status=1, type=2, deptName=部门1-部门2-部门3, statusName=状态1, typeName=类型2)
		BizPageVo(deptNo=1, status=1, type=1, 2, deptName=部门1, statusName=状态1, typeName=类型1,类型2)
		BizPageVo(deptNo=3, status=2, type=2,1, deptName=部门1-部门2-部门3, statusName=状态2, typeName=类型2,类型1)
		BizPageVo(deptNo=3, status=1, type=1, 2, deptName=部门1-部门2-部门3, statusName=状态1, typeName=类型1,类型2)
    */

    @Test
    public void cTest(){
        final List<BizPageVo> pageContent = this.mock(5);
        bizUtil.buildShowValue(pageContent, "status", BizPageVo::getStatus, (item, val) -> item.setStatusName(val));

        pageContent.forEach(System.out::println);
    }

    /*
		BizPageVo(deptNo=2, status=2, type=1, deptName=null, statusName=状态2, typeName=null)
		BizPageVo(deptNo=1, status=1, type=2, deptName=null, statusName=状态1, typeName=null)
		BizPageVo(deptNo=3, status=1, type=1, deptName=null, statusName=状态1, typeName=null)
		BizPageVo(deptNo=2, status=1, type=2, deptName=null, statusName=状态1, typeName=null)
		BizPageVo(deptNo=3, status=2, type=1, deptName=null, statusName=状态2, typeName=null)
    */

    @Test
    public void dTest(){
        final List<BizPageVo> pageContent = this.mock(5);
        bizUtil.buildShowValue(
                pageContent,
                item -> {
                    item.setTypeName("dTest");
                    System.out.print(item);
                    System.out.print("  是最新的数据哦 => example: ");
                    System.out.println(item.getStatusName());
                },
                Triple.of("status", BizPageVo::getStatus, BizPageVo::setStatusName)
        );

        System.out.println("end");
        pageContent.forEach(System.out::println);
    }
    /*
		BizPageVo(deptNo=2, status=2, type=1, deptName=null, statusName=状态2, typeName=dTest)  是最新的数据哦 => example: 状态2
		BizPageVo(deptNo=2, status=2, type=2, deptName=null, statusName=状态2, typeName=dTest)  是最新的数据哦 => example: 状态2
		BizPageVo(deptNo=3, status=1, type=1, deptName=null, statusName=状态1, typeName=dTest)  是最新的数据哦 => example: 状态1
		BizPageVo(deptNo=3, status=1, type=2, deptName=null, statusName=状态1, typeName=dTest)  是最新的数据哦 => example: 状态1
		BizPageVo(deptNo=3, status=2, type=1, deptName=null, statusName=状态2, typeName=dTest)  是最新的数据哦 => example: 状态2
		end
		BizPageVo(deptNo=2, status=2, type=1, deptName=null, statusName=状态2, typeName=dTest)
		BizPageVo(deptNo=2, status=2, type=2, deptName=null, statusName=状态2, typeName=dTest)
		BizPageVo(deptNo=3, status=1, type=1, deptName=null, statusName=状态1, typeName=dTest)
		BizPageVo(deptNo=3, status=1, type=2, deptName=null, statusName=状态1, typeName=dTest)
		BizPageVo(deptNo=3, status=2, type=1, deptName=null, statusName=状态2, typeName=dTest)
    */
    public List<BizPageVo> mock(int count){
        return Stream.generate(()  -> BizPageVo.builder()
                        .deptNo(RandomUtil.randomEle(ListUtil.of(1L, 2L, 3L)))
                        .status(RandomUtil.randomEle(ListUtil.of(1, 2)))
                        .type(RandomUtil.randomEle(ListUtil.of("1", "2", "2,1", "1, 2")))
                        .build())
                .limit(count)
                .collect(Collectors.toList());
    }

}

工具类

package cn.wangjiahang.record.util.dev;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import cn.wangjiahang.record.util.dev.base.DeptService;
import cn.wangjiahang.record.util.dev.base.DeptVo;
import cn.wangjiahang.record.util.dev.base.DictService;
import cn.wangjiahang.record.util.dev.base.DictionaryDataVO;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author jh.wang
 * @since 2022/12/10
 */
@RequiredArgsConstructor
// TODO
//  @Component
public class BizCommonUtil {
    private static final String CHINESE_COMMA = ",";
    private final DictService dictService;
    private final DeptService deptService;

    /**
     * 构建前台需要展示的数据
     *
     * @param content     列表数据
     * @param dictCode    字典code
     * @param getDataCode 获取字典值函数
     * @param setDictName 消费字典值对应名称
     * @param <E>         列表数据类型
     */
    public final <E> void buildShowValue(List<E> content, String dictCode, Function<E, Object> getDataCode, BiConsumer<E, String> setDictName) {
        final Map<String, String> dictMap = this.dictToMap(dictCode);

        // 遍历列表数据
        content.forEach(item -> {
            final String dataCode = Convert.toStr(getDataCode.apply(item));
            // 循环处理字典项
            setDictName.accept(item, dictMap.getOrDefault(dataCode, dataCode));
        });
    }

    /**
     * 构建前台需要展示的数据
     *
     * @param content     列表数据
     * @param peekItem    处理完成后的每项数据回调
     * @param dictCode    字典code
     * @param getDataCode 获取item字典数据
     * @param setDictName 设置字典值
     * @param <E>         列表数据类型
     */
    public final <E> void buildShowValue(List<E> content,
                                         Consumer<E> peekItem,
                                         String dictCode,
                                         Function<E, Object> getDataCode,
                                         BiConsumer<E, String> setDictName) {
        final Map<String, String> dictMap = this.dictToMap(dictCode);

        // 遍历列表数据
        content.forEach(item -> {
            final String dataCode = Convert.toStr(getDataCode.apply(item));
            // 循环处理字典项
            setDictName.accept(item, dictMap.getOrDefault(dataCode, dataCode));
            // 消费已处理的item
            peekItem.accept(item);
        });
    }

    /**
     * 构建前台需要展示的数据
     *
     * @param content  列表数据
     * @param manyDict 需要处理的字典信息
     * @param <E>      列表数据类型
     */
    @SafeVarargs
    public final <E> void buildShowValue(List<E> content, Triple<String, Function<E, Object>, BiConsumer<E, String>>... manyDict) {

        // 查询多个字典, 并以<字典code, <字典数据code, 字典数据value>>的形式组装数据
        final Map<String, Map<String, String>> manyDictMap = this.manyDictToMap(Arrays.stream(manyDict).map(Triple::getLeft));

        // 遍历列表数据
        content.forEach(item -> {
            // 循环处理多个 字典项
            consumerDictName(manyDictMap, item, manyDict);
        });
    }

    /**
     * 构建前台需要展示的数据
     *
     * @param content  列表数据
     * @param peekItem 处理完成后的每项数据回调
     * @param manyDict 需要处理的字典信息
     * @param <E>      列表数据类型
     */
    @SafeVarargs
    public final <E> void buildShowValue(List<E> content, Consumer<E> peekItem,
                                         Triple<String, Function<E, Object>, BiConsumer<E, String>>... manyDict) {
        // 查询多个字典, 并以<字典code, <字典数据code, 字典数据value>>的形式组装数据
        final Map<String, Map<String, String>> manyDictMap = this.manyDictToMap(Arrays.stream(manyDict).map(Triple::getLeft));

        // 遍历列表数据
        content.forEach(item -> {
            // 循环处理多个 字典项
            consumerDictName(manyDictMap, item, manyDict);
            // 额外业务操作, 在每次循环时会调用
            peekItem.accept(item);
        });
    }


    /**
     * 构建前台需要展示的数据
     *
     * @param content       列表数据
     * @param buildDeptName 处理部门名称
     * @param manyDict      需要处理的字典信息
     * @param <E>           列表数据类型
     */
    @SafeVarargs
    public final <E> void buildShowValue(List<E> content,
                                         Pair<Function<E, Long>, BiConsumer<E, String>> buildDeptName,
                                         Triple<String, Function<E, Object>, BiConsumer<E, String>>... manyDict) {

        final Map<String, Map<String, String>> manyDictMap = this.manyDictToMap(Arrays.stream(manyDict).map(Triple::getLeft));
        final Map<Long, DeptVo> deptMap = this.allIdDeptMap();

        // 遍历列表数据
        content.forEach(item -> {
            // 循环处理多个 字典项
            this.consumerDictName(manyDictMap, item, manyDict);
            // 构建该部门id的父部门的名称, 以'-'分割
            buildDeptName.getRight().accept(item, this.buildParentName(buildDeptName.getLeft().apply(item), deptMap));
        });
    }

    /**
     * 构建前台需要展示的数据
     *
     * @param content       列表数据
     * @param peekItem      处理完成后的每项数据回调
     * @param buildDeptName 处理部门名称
     * @param manyDict      需要处理的字典信息
     * @param <E>           列表数据类型
     */
    @SafeVarargs
    public final <E> void buildShowValue(List<E> content,
                                         Consumer<E> peekItem,
                                         Pair<Function<E, Long>, BiConsumer<E, String>> buildDeptName,
                                         Triple<String, Function<E, Object>, BiConsumer<E, String>>... manyDict) {

        final Map<String, Map<String, String>> manyDictMap = this.manyDictToMap(Arrays.stream(manyDict).map(Triple::getLeft));
        final Map<Long, DeptVo> deptMap = this.allIdDeptMap();

        // 遍历列表数据
        content.forEach(item -> {
            // 循环处理多个 字典项
            consumerDictName(manyDictMap, item, manyDict);
            // 构建该部门id的父部门的名称, 以'-'分割
            buildDeptName.getRight().accept(item, this.buildParentName(buildDeptName.getLeft().apply(item), deptMap));
            // 额外业务操作, 在每次循环时会调用
            peekItem.accept(item);
        });
    }


    /**
     * 消费字典信息
     *
     * @param manyDictMap 字典与字典数据映射
     * @param item        需要处理的每项数据
     * @param manyDict    需要处理的字典信息
     * @param <E>         列表数据类型
     */
    private <E> void consumerDictName(Map<String, Map<String, String>> manyDictMap,
                                      E item,
                                      Triple<String, Function<E, Object>, BiConsumer<E, String>>[] manyDict) {
        // 填充字典数据
        for (Triple<String, Function<E, Object>, BiConsumer<E, String>> dict : manyDict) {
            Optional.ofNullable(manyDictMap.get(dict.getLeft())).ifPresent(dictMap -> {
                dict.getRight().accept(item, dictDataName(dict.getMiddle().apply(item), dictMap));
            });
        }
    }

    /**
     * 根据字典值获取字典名称
     *
     * @param dictDataVal 字典值
     * @param dictMap     字典值与名称map
     * @return 字典名称
     */
    public String dictDataName(Object dictDataVal, Map<String, String> dictMap) {
        if (dictDataVal instanceof String) {
            final String val = (String) dictDataVal;
            // 包含中英文逗号
            if (StrUtil.containsAny(val, StrPool.COMMA, CHINESE_COMMA)) {
                // 认为是多个类型的字典, 切割获取字典名称并以中文逗号拼接
                return StrUtil.splitTrim(StrUtil.replace(val, CHINESE_COMMA, StrPool.COMMA), StrPool.COMMA)
                        .stream().map(item -> dictMap.getOrDefault(item, item)).collect(Collectors.joining(CHINESE_COMMA));
            }
        }
        final String dictVal = Convert.toStr(dictDataVal);
        return dictMap.getOrDefault(dictVal, dictVal);
    }

    /**
     * 部门id与部门信息映射
     *
     * @return 部门id与部门信息映射
     */
    public Map<Long, DeptVo> allIdDeptMap() {
        return deptService.list().stream().collect(Collectors.toMap(DeptVo::getId, Function.identity()));
    }

    /**
     * 构建该部门id的父部门的名称, 请勿在循环中调用
     *
     * @param currentDeptId 当前部门id
     * @return 父部门名称
     */
    public String buildParentName(Long currentDeptId) {
        return buildParentName(new LinkedList<>(), currentDeptId, null);
    }

    /**
     * 构建该部门id的父部门的名称
     *
     * @param currentDeptId 当前部门id
     * @param allDeptInfo   全部部门信息Map
     * @return 父部门名称
     */
    public String buildParentName(Long currentDeptId, Map<Long, DeptVo> allDeptInfo) {
        return buildParentName(new LinkedList<>(), currentDeptId, allDeptInfo);
    }

    /**
     * 构建该部门id的父部门的名称
     *
     * @param deptNames     收集部门名称的容器
     * @param currentDeptId 当前部门id
     * @param allDeptInfo   全部部门信息Map
     * @return 父部门名称
     */
    public String buildParentName(LinkedList<String> deptNames, Long currentDeptId, Map<Long, DeptVo> allDeptInfo) {
        DeptVo dept = allDeptInfo == null ? deptService.getById(currentDeptId) : allDeptInfo.get(currentDeptId);
        if (dept != null) {
            deptNames.addFirst(dept.getDeptName());
            buildParentName(deptNames, dept.getParentId(), allDeptInfo);
        }
        return String.join("-", deptNames);
    }

    /**
     * 查询多个字典, 并以<字典code, <字典数据code, 字典数据value>>的形式组装数据
     *
     * @param manyDict 全部字典code列表stream
     * @return 字典信息map
     */
    public Map<String, Map<String, String>> manyDictToMap(Stream<String> manyDict) {
        return manyDict.map(code -> Pair.of(code, this.dictToMap(code))).collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
    }

    /**
     * 查询字典, 并以<字典数据code, 字典数据value>的形式组装数据
     *
     * @param code 字典code
     * @return 字典信息map
     */
    private Map<String, String> dictToMap(String code) {
        return dictService.listDictionaryData(code).stream()
                .collect(Collectors.toMap(DictionaryDataVO::getDataCode, DictionaryDataVO::getDataValue));
    }

    /**
     * 查询多个字典, 并以<字典code, <字典数据code, 字典数据value>>的形式组装数据
     *
     * @param manyDict 全部字典code列表
     * @return 字典信息map
     */
    public Map<String, Map<String, String>> manyDictToMap(List<String> manyDict) {
        return manyDictToMap(manyDict.stream());
    }

}

mock 业务对象

java 以及数据转换 数据字典使用 java数据字典的使用_后端

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class BizPageVo {
	private Long deptNo;
	private Integer status;
	private String type;
	private String deptName;
	private String statusName;
	private String typeName;
}
// -----------------------------------------------------
import cn.hutool.core.collection.ListUtil;
import java.util.List;

public class DeptService {
    public List<DeptVo> list() {
        final DeptVo value1 = new DeptVo().setId(1L).setParentId(null).setDeptName("部门1");
        final DeptVo value2 = new DeptVo().setId(2L).setParentId(1L).setDeptName("部门2");
        final DeptVo value3 = new DeptVo().setId(3L).setParentId(2L).setDeptName("部门3");

        return ListUtil.toList(value1, value2, value3);
    }

    public DeptVo getById(Long id) {
        return this.list().stream().filter(item -> item.getId().compareTo(id) == 0).findFirst().orElse(null);
    }
}
// -----------------------------------------------------
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class DeptVo {
    private Long id;
    private Long parentId;
    private String deptName;
    private String deptCode;
}
// -----------------------------------------------------
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class DictionaryDataVO{
    private String dataName;
    private String dataCode;
    private String dataValue;
}
// -----------------------------------------------------
import cn.hutool.core.collection.ListUtil;
import java.util.List;

public class DictService {
    public List<DictionaryDataVO> listDictionaryData(String code) {
        String pre = "type".equals(code) ? "类型" : "状态";

        final DictionaryDataVO value1 = new DictionaryDataVO().setDataCode("1").setDataValue(String.format("%s1", pre));
        final DictionaryDataVO value2 = new DictionaryDataVO().setDataCode("2").setDataValue(String.format("%s2", pre));
        return ListUtil.toList(value1, value2);
    }
}