背景
结果数据转换:
1. 场景:数据库表存储关联表的ID,如创建人ID、机构ID、部门ID等,而前端展示需要的是名称。为了展示,正常是通过关联表查询名称解决,但这样可能导致查询执行效率低下、SQL复杂化,而且还要定义名称字段来存储名称
2. 策略:数据库表还是存储关联表的ID,但字段类型定义为字符串类型(为了代码生成器生成String类型的字段)。service层对外提供的统一为ID查询结果,若有展示需要,则在controller通过DataConverter统一转换为名称返回前端展示
3. 查询结果对象为POJO(DTO\VO\ENTITY),若定义了名称字段(名称字段需以Name结尾,如userId-》userName,org-》orgName),则通过反射将名称存储在Name字段,若未定义名称字段,则直接覆盖ID字段。
4. 查询结果对象为Map,则会新增一个名称字段(名称字段需以Name结尾,如userId-》userName,org-》orgName),名称存储在名称字段,不会覆盖ID字段
5. DataConverter中调用关联表的服务查询名称并做缓存提供转换效率
调用方法
// 转换分页对象数据
DataConverter.toName(DataType.USER_NICK_NAME, page);
// 转换单个对象
DataConverter.toName(DataType.USER_NICK_NAME, obj);
// 转换集合对象
DataConverter.toName(DataType.USER_NICK_NAME, list);
其中,DataType为支持的数据类型(具体见源码)。若默认的转换字段不满足,临时要用的请调用如下代码,比较公用的字段请联系管理员添加到数据类型配置中。

数据字典转换
sql查询语句,需在待转换字典值前加字典编码,例子如下(其中yes_no,user_type为字典编码):
select concat('yes_no','-',t.status),concat('user_type','-',t.type) from user t
转换调用,例子如下(其中status、type为待转换的字段):
DataConverter.toName(DataType.YAOXIE_DICT_ITEM,"status","type");
具体实现
DataConverter:结果数据转换器
package com.shusi.convertor;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shusi.domain.dto.OptionDTO;
import com.shusi.redis.RedisUtil;
import com.shusi.util.DataUtil;
import com.shusi.util.SpringContextHolder;
import com.shusi.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 结果数据转换器
*
* @Author: lincl
* @Date: 2019/2/28 16:11
*/
@Slf4j
public class DataConverter {
/**
* 最多缓存多少
*/
private final static int MAX_SIZE = 10000;
/**
* redis
*/
private static RedisUtil redisUtil;
/**
* 初始化
*/
public static void init() {
// 初始化获取bean
if (redisUtil == null) {
redisUtil = SpringContextHolder.getBean(RedisUtil.class);
}
}
/**
* 获取名称
*
* @param dataType
* @param id
* @return
*/
public static String getName(DataType dataType, String id) {
// 初始化
init();
List<String> idList = StrUtil.split(id, ',');
// 查询结果
List<String> result = redisUtil.getItemListOfMap(dataType.getCachePrefix(), idList);
// 如果请求服务后,依旧没有数据,则返回id
if (result == null) {
return id;
}
return StrUtil.join(",", result);
}
/**
* 更新缓存
*
* @param dataType 数据类型
* @param list 数据集合
*/
public static void setName(DataType dataType, List<OptionDTO> list) {
// 初始化
init();
Map<String, String> map = new HashMap<>(list.size());
for (OptionDTO item : list) {
map.put(item.getValue(), item.getLabel());
}
// 更新缓存
redisUtil.setMap(dataType.getCachePrefix(), map);
log.info("“ {} ” 缓存设置成功!共 {} 个数据!", dataType.getType(), list.size());
}
/**
* 更新名称
*
* @param dataType 数据类型
* @param id id
* @param name 名称
*/
public static void updName(DataType dataType, String id, String name) {
// 初始化
init();
// 更新缓存
redisUtil.setItemOfMap(dataType.getCachePrefix(), id, name);
}
/**
* 删除名称
*
* @param dataType 数据类型
* @param ids ids
*/
public static void delName(DataType dataType, String... ids) {
// 初始化
init();
redisUtil.delItemOfMap(dataType.getCachePrefix(), Arrays.asList(ids));
}
/**
* 单个对象转换
*
* @param dataType 数据类型
* @param obj 数据对象
* @param <T>
* @return
*/
public static <T> T toName(DataType dataType, T obj) {
return DataConverter.toName(dataType, obj, null);
}
/**
* 集合转换
*
* @param list 数据集合
* @param <T>
* @return
*/
public static <T> List<T> toName(DataType dataType, List<T> list) {
return DataConverter.toName(dataType, list, null);
}
/**
* 分页对象转换
*
* @param page
* @return
*/
public static Page toName(DataType dataType, Page page) {
return DataConverter.toName(dataType, page, null);
}
/**
* 单个对象转换
*
* @param dataType 数据类型
* @param obj 数据对象
* @param fieldNames 转换字段名称
* @param <T>
* @return
*/
public static <T> T toName(DataType dataType, T obj, String[] fieldNames) {
if (obj == null) {
return null;
}
// 默认转换字段
if (DataUtil.isEmpty(fieldNames)) {
fieldNames = dataType.getFieldNames();
}
if (DataUtil.isNotEmpty(fieldNames)) {
for (int i = 0; i < fieldNames.length; i++) {
String fieldName = fieldNames[i];
// 存储名称的字段名
String labelField = fieldName.replace("Id", "") + "Name";
try {
//Map对象
if (obj instanceof Map) {
if (((Map) obj).get(fieldName) != null) {
String userId = ((Map) obj).get(fieldName) + "";
String nickName = DataConverter.getName(dataType, userId);
((Map) obj).put(labelField, nickName);
}
} else {
Class clz = obj.getClass();
if (ReflectUtil.hasField(clz, fieldName)) {
String id = (String) ReflectUtil.getFieldValue(obj, fieldName);
if (StrUtil.isNotBlank(id)) {
String nickName = DataConverter.getName(dataType, id);
if (ReflectUtil.hasField(clz, labelField)) {
// 定义了额外的字段来存储名称
ReflectUtil.setFieldValue(obj, labelField, nickName);
} else {
ReflectUtil.setFieldValue(obj, fieldName, nickName);
}
}
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
return obj;
}
/**
* 集合转换
*
* @param list 数据集合
* @param fieldNames 字段名称
* @param <T>
* @return
*/
public static <T> List<T> toName(DataType dataType, List<T> list, String[] fieldNames) {
if (list != null && list.size() > 0) {
for (int i = 0; i < list.size(); i++) {
DataConverter.toName(dataType, list.get(i), fieldNames);
}
}
return list;
}
/**
* 分页对象转换
*
* @param page
* @param fieldNames 字段名称
* @return
*/
public static Page toName(DataType dataType, Page page, String[] fieldNames) {
if (page.getRecords() != null && page.getRecords().size() > 0) {
DataConverter.toName(dataType, page.getRecords(), fieldNames);
}
return page;
}
}
DataType:数据类型
package com.shusi.convertor;
/**
* 数据类型枚举类
*
* @author lincl
*/
public enum DataType {
/**
* 用户账号昵称,默认转换字段:"createBy", "updateBy", "operator", "orderUserId", "pushUserId"
*/
USER_NICK_NAME("用户账号昵称", "userId", "createBy", "updateBy", "operator", "orderUserId", "pushUserId", "returnUserId", "optionUserId", "sendUserId", "receiveUserId", "receiveUserIds"),
/**
* 机构名称,默认转换字段:"orgId", "hospitalOrgId"
*/
ORG_NAME("机构名称", "orgId","reqOrgId", "userBuyerId", "userSupplierId"),
/**
* 厂家名称,默认转换字段:"factoryId"
*/
FACTORY_NAME("厂家名称", "factoryId", "factory", "userFactoryId"),
/**
* 部门名称,默认转换字段:"deptId"
*/
DEPT_NAME("部门名称", "deptId", "departmentId"),
/**
* 部门合并名称,默认转换字段:"deptMergeId"
*/
DEPT_MERGE_NAME("部门合并名称", "deptMergeId", "deptId", "departmentId"),
/**
* 行政区域名称
*/
REGION_NAME("行政区域名称", "province", "city", "district", "region", "provinceId", "cityId", "districtId"),
/**
* 药械网:数据字典
*/
YAOXIE_DICT_ITEM("数据字典", null),
/**
* 统一认证:数据字典
*/
AUTH_DICT_ITEM("数据字典", null),
/**
* 权限名称
*/
PERMISSION("权限名称", "permissionId");
/**
* 类型
*/
private String type;
/**
* 默认转换字段
*/
private String[] fieldNames;
DataType(String type, String... fieldNames) {
this.type = type;
this.fieldNames = fieldNames;
}
/**
* 获取数据类型
*
* @return
*/
public String getType() {
return type;
}
/**
* 获取默认转换字段
*
* @return
*/
public String[] getFieldNames() {
return fieldNames;
}
/**
* 获取缓存前缀
*
* @return
*/
public String getCachePrefix() {
return "ID_NAME:" + this.name();
}
}