package com.yf.exam.aspect; // 导入FastJSON库,用于将Java对象转换为JSON字符串以及从JSON字符串解析为Java对象等操作, // 在本类的多个方法中用于对象与JSON字符串之间的转换,以便于处理数据字典相关的值。 import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; // 导入MyBatis Plus的接口,用于处理分页相关的数据结构,在本类中用于判断和处理分页数据中的数据字典值。 import com.baomidou.mybatisplus.core.metadata.IPage; // 导入Jackson库的注解,用于指定日期格式的序列化和反序列化方式, // 在本类的parseObject方法中用于根据注解设置日期字段的格式化输出。 import com.fasterxml.jackson.annotation.JsonFormat; // 导入自定义的注解,可能用于标记与数据字典相关的字段,以便在本类中识别并处理这些字段的数据字典值。 import com.yf.exam.core.annon.Dict; // 导入自定义的API响应类,用于封装API调用的返回结果,包括数据、状态码等信息, // 在本类的多个方法中用于获取和设置返回结果中的数据部分,以便处理其中的数据字典值。 import com.yf.exam.core.api.ApiRest; // 导入自定义的反射工具类,可能用于获取对象的所有字段等反射相关操作, // 在本类的parseObject方法中用于获取对象的所有字段以便遍历处理数据字典值。 import com.yf.exam.core.utils.Reflections; // 导入系统数据字典服务类,用于查询数据字典表以获取数据字典值的翻译文本, // 在本类的translateDictValue方法中用于根据字典代码、文本、表名和键值查询对应的字典文本。 import com.yf.exam.modules.sys.system.service.SysDictService; // 导入Lombok的Log4j2注解,用于简化日志记录的配置,通过该注解可以方便地在类中使用Log4j2进行日志输出。 import lombok.extern.log4j.Log4j2; // 导入AspectJ的相关类,用于定义切面、切点和环绕通知等AOP相关的操作, // 在本类中用于实现对特定方法的拦截和处理,以实现数据字典值的翻译等功能。 import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; // 导入Spring框架的注解,用于自动注入依赖对象和标记类为Spring组件, // 在本类中通过@Autowired注入SysDictService对象,并通过@Component标记本类为Spring组件,使其可被Spring容器管理。 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; // 导入Spring框架的工具类,用于判断字符串是否为空等操作, // 在本类的多个方法中用于判断字符串是否为空,以便进行相应的处理逻辑。 import org.springframework.util.StringUtils; // 导入Java标准库中的反射相关类,用于通过反射操作对象的字段、获取类型信息等, // 在本类的多个方法中广泛用于获取对象的字段、判断字段类型、获取字段注解等操作。 import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; // 导入Java标准库中的日期格式化类和日期类,用于处理日期格式的转换和操作, // 在本类的parseObject方法中用于根据注解或默认格式对日期字段进行格式化输出。 import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 数据字典AOP类,主要功能是处理数据字典值。通过拦截特定的方法调用, * 对返回结果中的数据字典相关字段进行翻译、格式化等处理,以提供更友好的展示效果。 * * @author bool */ @Aspect // 标记该类为一个AspectJ切面类,用于定义切面相关的逻辑。 @Component // 标记该类为Spring组件,使其能够被Spring容器管理和实例化,以便在应用中使用。 @Log4j2 // 使用Log4j2注解启用日志记录功能,方便在类中记录相关操作的日志信息。 public class DictAspect { @Autowired private SysDictService sysDictService; // 通过自动注入获取系统数据字典服务对象,用于查询数据字典值。 /** * 定义一个环绕通知,切入到指定的Controller方法执行前后。 * 这里的切点表达式指定了拦截所有在com.yf.exam包及其子包下的所有Controller类中的所有公有方法。 * * @param pjp 切入点对象,包含了被拦截方法的相关信息,如方法参数、目标对象等。 * @return 返回结果,经过处理后的方法执行结果,可能经过了数据字典值的处理等操作。 * @throws Throwable 如果在环绕通知的执行过程中出现异常,则抛出。 */ @Around("execution(public * com.yf.exam..*.*Controller.*(..))") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { return this.translate(pjp); // 调用translate方法对被拦截方法的执行结果进行处理,主要是处理数据字典值。 } /** * 对被拦截方法的执行结果进行翻译处理,并返回处理后的结果。 * 在调用此方法之前,通常需要确保相关的BaseDictService(可能是数据字典相关的基础服务)已经实现。 * * @param pjp 切入点对象,包含了被拦截方法的相关信息,如方法参数、目标对象等。 * @return 返回结果,经过数据字典值处理后的方法执行结果。 * @throws Throwable 如果在处理过程中出现异常,则抛出。 */ public Object translate(ProceedingJoinPoint pjp) throws Throwable { // 调用被拦截方法获取原始结果,然后调用parseAllDictText方法对结果进行数据字典值的处理。 return this.parseAllDictText(pjp.proceed()); } /** * 根据结果对象的类型,判断是否需要对其进行数据字典值的处理。 * 如果结果对象是ApiRest类型,则调用parseFullDictText方法进行完整的数据字典值处理。 * * @param result 结果对象,即被拦截方法执行后的返回结果。 */ private Object parseAllDictText(Object result) { // 判断结果对象是否是ApiRest类型,如果是则进行数据字典值的处理。 if (result instanceof ApiRest) { parseFullDictText(result); } return result; } /** * 对ApiRest类型的结果对象进行完整的数据字典值处理,包括处理分页数据、列表数据以及单对象数据等情况。 * * @param result 结果对象,即ApiRest类型的返回结果,其中包含了要处理的数据部分。 */ private void parseFullDictText(Object result) { try { Object rest = ((ApiRest) result).getData(); // 获取ApiRest对象中的数据部分,这部分数据可能包含数据字典相关字段。 // 如果数据部分为空或者是基本数据类型,则不需要进行数据字典值的处理,直接返回。 if (rest == null || this.isBaseType(rest.getClass())) { return; } // 如果数据部分是分页数据类型(IPage),则对分页数据中的每条记录进行数据字典值处理。 if (rest instanceof IPage) { List items = new ArrayList<>(16); for (Object record : ((IPage) rest).getRecords()) { Object item = this.parseObject(record); // 调用parseObject方法对每条记录进行数据字典值处理。 items.add(item); } ((IPage) rest).setRecords(items); // 将处理后的记录列表重新设置回分页对象中。 return; } // 如果数据部分是列表数据类型(List),则对列表中的每条记录进行数据字典值处理。 if (rest instanceof List) { List items = new ArrayList<>(); for (Object record : ((List) rest)) { Object item = this.parseObject(record); // 调用parseObject方法对每条记录进行数据字典值处理。 items.add(item); } // 将处理后的记录列表重新设置回ApiRest对象的数据部分。 ((ApiRest) result).setData(items); return; } // 如果数据部分是单对象数据,则对该单对象进行数据字典值处理。 Object item = this.parseObject(((ApiRest) result).getData()); ((ApiRest) result).setData(item); } catch (Exception e) { e.printStackTrace(); // 如果在处理过程中出现异常,则打印异常堆栈信息。 } } /** * 对单个记录对象进行数据字典值处理,包括处理列表字段、带有数据字典注解的普通字段以及日期字段等情况。 * * @param record 记录对象,即要进行数据字典值处理的对象。 * @return 处理后的对象,经过数据字典值处理后的记录对象。 */ public Object parseObject(Object record) { if (record == null) { return null; } // 如果记录对象是基本数据类型,则不需要进行数据字典值处理,直接返回原对象。 if (this.isBaseType(record.getClass())) { return record; } // 将记录对象转换为JSON字符串,再解析为JSONObject对象,以便于通过字段名获取和设置值。 String json = JSON.toJSONString(record); JSONObject item = JSONObject.parseObject(json); for (Field field : Reflections.getAllFields(record)) { // 遍历记录对象的所有字段。 // 如果字段类型是List类型,则对列表字段进行特殊处理。 if (List.class.isAssignableFrom(field.getType())) { try { List list = this.processList(field, item.getObject(field.getName(), List.class)); // 调用processList方法处理列表字段。 item.put(field.getName(), list); continue; } catch (Exception e) { e.printStackTrace(); } continue; } // 如果字段带有数据字典注解(Dict),则对该字段进行数据字典值的翻译处理。 if (field.getAnnotation(Dict.class)!= null) { String code = field.getAnnotation(Dict.class).dicCode(); String text = field.getAnnotation(Dict.class).dicText(); String table = field.getAnnotation(Dict.class).dictTable(); String key = String.valueOf(item.get(field.getName())); // 调用translateDictValue方法翻译字典值对应的文本,根据字典代码、文本、表名和键值查询对应的字典文本。 String textValue = this.translateDictValue(code, text, table, key); if (StringUtils.isEmpty(textValue)) { textValue = ""; } item.put(field.getName() + "_dictText", textValue); continue; } // 如果字段类型是日期类型(java.util.Date)且字段值不为空,则对日期字段进行格式转换处理。 if ("java.util.Date".equals(field.getType().getName()) && item.get(field.getName())!= null) { // 获取字段上的JsonFormat注解。 JsonFormat ann = field.getAnnotation(JsonFormat.class); // 定义日期格式化对象。 SimpleDateFormat fmt; // 如果注解不为空且指定了日期格式模式,则使用注解指定的格式创建日期格式化对象。 if (ann!= null &&!StringUtils.isEmpty(ann.pattern())) { fmt = new SimpleDateFormat(ann.pattern()); } else { // 如果注解为空或未指定格式,则使用默认的日期格式创建日期格式化对象。 fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } item.put(field.getName(), fmt.format(new Date((Long) item.get(field.getName())))); continue; } } return item; } /** * 处理类型为List的字段,对列表中的每个元素进行数据字典值处理等操作。 * * @param field 字段对象,即要处理的List类型字段。 , * @param list 列表对象,即字段对应的列表值。 * @return 处理后的列表,经过数据字典值处理后的列表对象。 */ private List processList(Field field, List list) { // 如果列表为空,则返回一个空的ArrayList对象。 if (list == null || list.size() == 0) { return new ArrayList<>(); } // 获取List属性的真实类型,通过反射获取字段的泛型类型,再尝试获取其实际的类型参数。 Type genericType = field.getGenericType(); Class actualType = null; if (genericType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) genericType; try { actualType = (Class) pt.getActualTypeArguments()[0]; } catch (Exception e) { return list; } } // 如果列表元素的类型是基本数据类型,则不需要进行数据字典值处理,直接返回原列表。 if (isBaseType(actualType)) { return list; } // 创建一个新的ArrayList对象,用于存储处理后的列表元素。 List result = new ArrayList<>(16); for (int i = 0; i < list.size(); i++) { // 获取列表中的每个元素。 Object data = list.get(i); try { // 将列表元素转换为JSON字符串,再解析为其真实类型的对象,以便进行数据字典值处理。 data = JSON.parseObject(JSON.toJSONString(data), actualType); } catch (Exception e) { // 如果转换过程中出现错误,则不进行处理,直接使用原元素。 // 这里可以根据实际需求进一步处理错误情况,比如记录日志等。 // 目前只是简单地忽略错误,继续处理下一个元素。 ; } // 对处理后的元素进行数据字典值处理,调用parseObject方法。 Object pds = this.parseObject(data); result.add(pds); } return result; } /** , * 根据字典代码、文本、表名和键值翻译数据字典值对应的文本。 * * @param code 字典代码,用于在数据字典表中定位特定的字典项。 * @param text 字典文本,可能是与字典代码相关的描述信息等。 * @param table 字典表名,指定要查询的数据字典表。 * @param key 字典键值,用于在字典表中查找对应的字典项。 * @return 翻译后的值,即根据字典代码、文本、表名和键值查询到的字典文本值,如果未找到则返回空字符串。 */ private String translateDictValue(String code, String text, String table, String键值) { if (StringUtils.isEmpty(key)) { return null; } try { // 定义变量用于存储翻译后的字典文本值。 String dictText = null; if (!StringUtils.isEmpty(table)) { // 如果字典表名不为空,则调用sysDictService的findDict方法查询数据字典表,获取对应的字典文本值。 dictText = sysDictService.findDict(table, text, code, key.trim()); } if (!StringUtils.isEmpty(dictText)) { return dictText; } } catch (Exception e) { e.printStackTrace(); } return ""; } /** * 判断给定的类是否是基本数据类型,包括常见的整数、字节、长整数、双精度浮点数、单精度浮点数、字符、短整数、布尔值以及字符串和数字类型等。 * * @param clazz 要判断的类对象。 * @return 是否基本类型,如果是基本数据类型则返回true,否则返回false。 */ private boolean isBaseType(Class clazz) { // 判断是否是常见的基本数据类型,如整数、字节、长整数等。 if (clazz.equals(java.lang.Integer.class) || clazz.equals(java.lang.Byte.class) || clazz.equals(java.lang.Long.class) || clazz.equals(java.lang.Double.class) || clazz.equals(java.lang.Float.class) || clazz.equals(java.lang.Character.class) || clazz.equals(java.lang.Short.class) || clazz.equals(java.lang.Boolean.class)) { return true; } // 判断是否是字符串类型。 if (clazz.equals(java.lang.String.class)) { return true; } // 判断是否是数字类型(这里的数字类型可能是指抽象的数字类型,比如Number的子类等)。 if (clazz.equals(java.lang.Number.class)) { return true; } return false; } }