目录
前言
实现
新增注解
新增切面
前言
字典管理是大部分系统都有的一个模块,用来管理业务上的字典数据,通常是树状结构,用键值对进行存储。然后具体业务场景使用字典数据时,业务数据往往存的是字典编码,因此查看业务数据时就需要同时返回字典对应的值,这便是字典回写。
实现字典回写其实也很容易,无非是哪种方式更优雅,比如最low的做法就是在类中增加字典值字段,数据查出来后再根据字典编码统一去字典表查出对应的字典值并赋到对应的字段上;稍微好点的方式是通过mybatis拦截器在查出数据通过反射获取字典编码并直接去查字典表,省去了在service层面去查的通用逻辑。但这两种方式都有个弊端,就是都得新增字典值冗余字段,业务场景一多就特别麻烦。
这里推荐第三种方式,通过切面的方式在controller层返回数据的时候动态增加字典值字段,更优雅的完成字典回写。
实现
新增注解
这里需要新增两个注解,一个用来作为切点,一个用来标识字典字段
/*** 字典方法标识*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DictionaryMethod {
}/*** 字典字段标识*/
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DictionaryField {/*** 回写目标字段,不填则是原字段名 + Name*/String writeBackTargetField() default "";
}
新增切面
主要思路就是拿到controller层返回的数据,通过反射获取对应的字典字段并新增字段进行赋值,结果集使用map接收
/*** 字典回写切面*/
@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class DictionaryFieldAspect {private SystemDictionaryItemMapper systemDictionaryItemMapper;@Around("@within(org.springframework.web.bind.annotation.RestController) && @annotation(com.hthjsj.icp.entity.common.annotation.DictionaryMethod)")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object object = pjp.proceed();if (object instanceof HttpResult && ((HttpResult) object).getCode() == 0) {// 只尝试处理正常返回的数据HttpResult result = (HttpResult) object;Object data = result.getData();if (data instanceof Page) {Page page = (Page) data;List list = page.getList();if (!list.isEmpty()) {List<Map<String, Object>> listResult = fillField(list);page.setList(listResult);return HttpResult.success(page);}} else if (data instanceof Collection) {Collection collection = (Collection) object;if (!collection.isEmpty()) {List<Map<String, Object>> listResult = fillField(collection);return HttpResult.success(listResult);}} else {return HttpResult.success(fillField(object));}}return object;}private List<Map<String, Object>> fillField(Collection list) throws Exception {List<Map<String, Object>> result = new ArrayList<>();for (Object o : list) {result.add(fillField(o));}return result;}private Map<String, Object> fillField(Object o) throws Exception {Map<String, Object> map = PropertyUtils.describe(o);Class<?> objectClass = o.getClass();Arrays.stream(objectClass.getDeclaredFields()).forEach(field -> {DictionaryField dictionaryField = field.getAnnotation(DictionaryField.class);if (dictionaryField != null) {field.setAccessible(true);try {String appCode = SecurityUtil.getSourceAppCode();Object fieldInstance = field.get(o);String code = (String) fieldInstance;String value = systemDictionaryItemMapper.getValueByCode(code, appCode);String writeBackTargetField = StringUtils.isNotEmpty(dictionaryField.writeBackTargetField()) ? dictionaryField.writeBackTargetField() : field.getName() + "Name";map.put(writeBackTargetField, value);} catch (Exception e) {log.error("字典回写异常:{}", e.getMessage());}}});return map;}
}
具体使用时只需要在controller层的方法上加上 @DictionaryMethod,同时在返回的字典字段加上 @DictionaryField 即可。