|
|
|
@ -0,0 +1,350 @@
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|