在设计Restful接口时,一些字典数据为了更符合用户使用习惯,需要转换成对应的字典值。比如创建用户,数据库存的都是用户id,而页面需要展示用户的真实姓名。再比如一些状态,数据库存的可能是一些数字,需要页面上展示对应的状态描述。原则上应该由前端来调用字典接口进行转换,但有时为了降低前端的开发工作量,需要后端转换完成后返回给前端(仅针对前端开发资源不足的情况下,不推荐)
故基于springboot的返参解析机制,在处理返回结果时,检查返回对象中是否有@Dict注解,如有则基于返回对象类创建一个新的的类,添加被@Dict注解的字段,字段名为在原字段名后面拼了一个Label,然后创建对象并赋值。下面是具体代码实现。
新建一个@Dict注解,用来配置字典相关信息
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
String dictName() default "";
Class<? extends BaseMapping> mappingClass() default StaticDictMapping.class;
}
public interface BaseMapping {
Object mapping(Object o, String dictName, String propertyName);
}
这里给一个静态字典的实现
@Component
public class StaticDictMapping implements BaseMapping {
@Autowired
private IDictDataService dictDataService;
@Override
public Object mapping(Object o, String dictName, String propertyName) {
if (Objects.isNull(o) || StringUtils.isEmpty(dictName)) {
return o;
}
return dictDataService.getDictDataPojo(dictName, o.toString()).getDictLabel();
}
}
这里再给一个动态字典的实现
@Service
public class UserServiceImpl implements IUserService, BaseMapping {
@Autowired
private UserDao userDao;
@Override
public UserPojo queryUserByUserId(Long userId) {
return userDao.queryUserByUserId(userId);
}
@Override
public Object mapping(Object o, String dictName, String propertyName) {
if (Objects.equals(dictName, "batchQuery")) {
List<Long> userIds = Arrays.stream(((String) o).split(",")).filter(StringUtil::isNotEmpty).map(Long::valueOf).collect(Collectors.toList());
return listUsersByIds(userIds);
} else {
UserPojo userPojo = this.queryUserByUserId(o instanceof Long ? (Long) o : Long.valueOf(o.toString()));
return userPojo.getNickName();
}
}
}
新建一个Convert继承MappingJackson2HttpMessageConvert
@Configuration
public class DictMessageConvert extends MappingJackson2HttpMessageConverter {
public DictMessageConvert(ObjectMapper objectMapper) {
super(objectMapper);
}
@Override
protected void writeInternal(Object o, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
if (o instanceof TablePojo && !CollectionUtils.isEmpty(((TablePojo) o).getRows())) {
((TablePojo) o).setRows(parseObjToMap(((TablePojo) o).getRows()));
}
if (o instanceof AjaxResult && Objects.nonNull(((AjaxResult) o).get("data"))) {
Object data = ((AjaxResult) o).get("data");
Object parsed = parseObjToMap(data instanceof List ? (List<?>) data : Collections.singletonList(data));
if (Objects.isNull(parsed)) {
((AjaxResult) o).put("data", null);
}
((AjaxResult) o).put("data", data instanceof List ? parsed : ((List<?>) parsed).get(0));
}
super.writeInternal(o, type, outputMessage);
}
@Override
protected boolean canWrite(MediaType mediaType) {
return Objects.nonNull(mediaType) && Objects.equals(mediaType.toString(), "application/json");
}
public List<?> parseObjToMap(List<?> rows) {
return rows.stream().map(o -> {
Map<String, Object> labelMap = getLabelStr(o);
if (!CollectionUtils.isEmpty(labelMap)) {
return createDynamicBean(o, labelMap);
}
return o;
}).collect(Collectors.toList());
}
private Object createDynamicBean(Object source, Map<String, Object> labelMap) {
BeanGenerator generator = new BeanGenerator();
generator.setSuperclass(source.getClass());
labelMap.forEach((k, v) -> generator.addProperty(k + "Label", Object.class));
Object rsltObj = generator.create();
BeanUtils.copyProperties(source, rsltObj);
final Class<?> dynamicObjClass = rsltObj.getClass();
labelMap.forEach((k, v) -> {
try {
Field field = dynamicObjClass.getDeclaredField("$cglib_prop_" + k + "Label");
field.setAccessible(true);
field.set(rsltObj, v);
} catch (Exception e) {
logger.error(e);
}
});
return rsltObj;
}
private Map<String, Object> getLabelStr(Object o) {
return Arrays.stream(o.getClass().getDeclaredFields()).filter(field -> !Modifier.isFinal(field.getModifiers()))
.filter(field -> field.isAnnotationPresent(Dict.class)).collect(Collectors.toMap(Field::getName, field -> {
try {
field.setAccessible(true);
Object val = field.get(o);
if (Objects.nonNull(val)) {
Dict dict = field.getAnnotation(Dict.class);
BaseMapping mapping = SpringUtil.getBeanWithNoThrow(dict.mappingClass());
if (Objects.isNull(mapping)) {
mapping = dict.mappingClass().newInstance();
}
return Objects.requireNonNull(mapping).mapping(val, dict.dictName(), field.getName());
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return Collections.emptyMap();
}));
}
}
返回对象用法
@Data
public class PermApplyPojo {
/**
* 申请id
*/
private String applyId;
/**
* 申请人id
*/
@Dict(mappingClass = UserServiceImpl.class)
private Long applyUsrId;
/**
* 未审批-1,同意-3,拒绝-4,审批中-2
*/
@Dict(dictName = "approval_status")
private String auditStatus;
/**
* 被授权人
*/
@NotEmpty(message = "被授权人不能为空")
@Dict(mappingClass = UserServiceImpl.class, dictName = "batchQuery")
private String authenticUsrIds;
/**
* 申请时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
}