目的
统一数据字典转换的写法,代码风格统一。
转换方式为手动调用工具类,且提供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 业务对象
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);
}
}