You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
test/src-源文件/main/java/com/yf/exam/aspect/DictAspect.java

350 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<Object> 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<Object> 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<Object> 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<Object> 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;
}
}