后端注释添加日期人员划分

This commit is contained in:
chenhaodong
2026-06-18 22:44:37 +08:00
parent 1c0b377474
commit 5f99b138cb
104 changed files with 1371 additions and 430 deletions
@@ -25,9 +25,26 @@ import java.util.List;
/**
* 数据字典翻译AOP切面
* <p>
* 拦截所有Controller方法的返回结果,自动将带有@Dict注解的字段从字典编码翻译为可读文本。
* 翻译后的文本以"字段名_dictText"的形式追加到返回的JSON对象中。
* 同时处理Date类型字段的格式化输出。
* </p>
* <p>
* 支持的数据结构:
* - 单个对象:直接遍历字段进行翻译
* - 列表(List):遍历每个元素进行翻译
* - 分页结果(IPage):遍历分页记录进行翻译
* - 嵌套List字段:递归处理子列表中的字典翻译
* </p>
* <p>
* 注意:当前@Component注解被注释,如需启用字典翻译功能需取消注释或通过其他方式注册为Spring Bean。
* </p>
*
* @Description 描述:数据字典AOP类,处理数据字典值
* @Author A贾宇婷034244310
* @Date 20260615
* @Author C薛涵艺034244315
* @Date 20260616
*/
@Aspect
//@Component
@@ -35,14 +52,19 @@ import java.util.List;
public class DictAspect {
/** 字典服务,用于根据字典编码查询对应的可读文本 */
@Autowired
private SysDictService sysDictService;
/**
* 切入Controller执行
* @param pjp
* @return
* @throws Throwable
* 环绕通知:切入所有Controller方法执行前后
* <p>
* 切点表达式匹配com.bc.exam包下所有Controller类的public方法,
* 在方法执行完成后对返回结果进行字典翻译处理。
* </p>
* @param pjp 连接点对象,封装了目标方法的执行信息
* @return 经过字典翻译处理后的返回结果
* @throws Throwable 目标方法执行过程中抛出的异常
*/
@Around("execution(public * com.bc.exam..*.*Controller.*(..))")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
@@ -50,25 +72,30 @@ public class DictAspect {
}
/**
* 进行翻译并返回,调用前必须实现:BaseDictService
*
* @param pjp
* @return
* @throws Throwable
* 执行目标方法并对返回结果进行字典翻译
* <p>
* 调用前必须确保SysDictService已正确配置并注入。
* </p>
* @param pjp 连接点对象
* @return 翻译处理后的返回结果
* @throws Throwable 目标方法执行异常
*/
public Object translate(ProceedingJoinPoint pjp) throws Throwable {
// 处理字典
// 先执行目标方法获取返回结果,再对结果进行字典翻译处理
return this.parseAllDictText(pjp.proceed());
}
/**
* 转换全部数据字典
*
* @param result
* <p>
* 仅对ApiRest类型的返回值进行字典翻译处理,其他类型直接透传。
* </p>
* @param result Controller方法的返回结果
* @return 处理后的返回结果(ApiRest类型会被翻译,其他类型原样返回)
*/
private Object parseAllDictText(Object result) {
// 非ApiRest类型处理
// 仅ApiRest统一响应格式才进行字典翻译,非ApiRest类型直接返回不做处理
if (result instanceof ApiRest) {
parseFullDictText(result);
}
@@ -78,45 +105,53 @@ public class DictAspect {
/**
* 转换所有类型的数据字典、包含子列表
*
* @param result
* 解析并翻译ApiRest响应体中的所有数据字典字段
* <p>
* 根据data字段的实际类型分三种情况处理:
* 1. 分页对象(IPage):遍历分页记录逐条翻译
* 2. 列表对象(List):遍历列表元素逐条翻译
* 3. 单个对象:直接进行字段翻译
* 基本数据类型和null值不做处理。
* </p>
* @param result ApiRest统一响应对象
*/
private void parseFullDictText(Object result) {
try {
// 从ApiRest中提取实际业务数据
Object rest = ((ApiRest) result).getData();
// 不处理普通数据类型
// null值或基本数据类型(String、Integer等)无需字典翻译,直接跳过
if (rest == null || this.isBaseType(rest.getClass())) {
return;
}
// 分页的
// 处理分页查询结果:遍历分页记录(records)逐条翻译字典字段
if (rest instanceof IPage) {
List<Object> items = new ArrayList<>(16);
for (Object record : ((IPage) rest).getRecords()) {
Object item = this.parseObject(record);
items.add(item);
}
// 用翻译后的记录替换原始分页记录
((IPage) rest).setRecords(items);
return;
}
// 数据列表的
// 处理列表查询结果:遍历列表中每个元素进行字典翻译
if (rest instanceof List) {
List<Object> items = new ArrayList<>();
for (Object record : ((List) rest)) {
Object item = this.parseObject(record);
items.add(item);
}
// 重新回写值
// 将翻译后的列表重新写回ApiRest的data字段
((ApiRest) result).setData(items);
return;
}
// 处理单对象
// 处理单对象:直接对对象的字段进行字典翻译
Object item = this.parseObject(((ApiRest) result).getData());
((ApiRest) result).setData(item);
@@ -126,10 +161,17 @@ public class DictAspect {
}
/**
* 处理数据字典值
*
* @param record
* @return
* 处理单个对象的字典翻译和日期格式化
* <p>
* 处理流程:
* 1. 将对象序列化为JSON再解析为JSONObject,便于动态添加翻译字段
* 2. 遍历对象所有字段,按类型分别处理:
* - List类型字段:递归调用processList处理嵌套列表
* - 带@Dict注解的字段:查询字典服务翻译编码为可读文本,结果以"字段名_dictText"存储
* - Date类型字段:按@JsonFormat注解指定的格式或默认格式(yyyy-MM-dd HH:mm:ss)格式化
* </p>
* @param record 待处理的业务对象
* @return 处理后的JSONObject(包含字典翻译字段和格式化日期),null输入则返回null
*/
public Object parseObject(Object record) {
@@ -137,18 +179,19 @@ public class DictAspect {
return null;
}
// 不处理普通数据类型
// 基本数据类型(String、Number等)没有复杂字段,无需翻译处理
if (this.isBaseType(record.getClass())) {
return record;
}
// 转换JSON字符
// 将对象转为JSON格式处理,便于动态添加字典翻译字段(如xxx_dictText
String json = JSON.toJSONString(record);
JSONObject item = JSONObject.parseObject(json);
// 遍历对象的所有字段(包括父类字段),逐个进行翻译或格式化处理
for (Field field : Reflections.getAllFields(record)) {
// 如果是List类型
// 分支1List类型字段 - 递归处理嵌套列表中的字典翻译
if (List.class.isAssignableFrom(field.getType())) {
try {
List list = this.processList(field, item.getObject(field.getName(), List.class));
@@ -160,37 +203,39 @@ public class DictAspect {
continue;
}
// 处理普通字段
// 分支2:带@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()));
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())); // 当前字段的字典编码值
//翻译字典值对应的txt
// 调用字典服务将编码值翻译为可读文本
String textValue = this.translateDictValue(code, text, table, key);
if (StringUtils.isEmpty(textValue)) {
textValue = "";
}
// 翻译结果以"字段名_dictText"的命名规则追加到JSON对象中
item.put(field.getName() + "_dictText", textValue);
continue;
}
//日期格式转换
// 分支3:Date类型字段 - 按注解格式或默认格式进行日期格式
if (field.getType().getName().equals("java.util.Date") && item.get(field.getName()) != null) {
// 获取注解
// 获取字段上的@JsonFormat注解以确定日期格式
JsonFormat ann = field.getAnnotation(JsonFormat.class);
// 格式化方式
// 日期格式化
SimpleDateFormat fmt;
// 使用注解指定的
// 优先使用@JsonFormat注解指定的日期格式
if (ann != null && !StringUtils.isEmpty(ann.pattern())) {
fmt = new SimpleDateFormat(ann.pattern());
} else {
// 默认时间样式
// 未指定格式时使用默认时间样式
fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
// 将时间戳(Long)格式化为日期字符串
item.put(field.getName(), fmt.format(new Date((Long) item.get(field.getName()))));
continue;
@@ -201,49 +246,55 @@ public class DictAspect {
}
/**
* 获得类型为List的值
*
* @param field
* @return
* 处理List类型字段的字典翻译
* <p>
* 通过反射获取List的泛型参数确定元素实际类型,
* 然后将每个列表元素反序列化为实际类型后递归调用parseObject进行字典翻译。
* 基本数据类型的列表(如List&lt;String&gt;)无需处理直接返回。
* </p>
* @param field List类型的字段对象,用于获取泛型类型信息
* @param list 字段对应的列表数据
* @return 经过字典翻译处理的列表,若输入为空则返回空列表
*/
private List<Object> processList(Field field, List list) {
// 空判断
// 空列表或null直接返回空集合,避免后续空指针
if (list == null || list.size() == 0) {
return new ArrayList<>();
}
// 获得List属性的真实类
// 通过泛型反射获取List元素的实际数据类型(如List<User>中的User类)
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;
}
}
// 常规列表无需处理
// 元素为基本数据类型(如String、Integer等)的列表无需字典翻译
if (isBaseType(actualType)) {
return list;
}
// 返回列表
// 对复杂对象列表逐条进行字典翻译处理
List<Object> result = new ArrayList<>(16);
for (int i = 0; i < list.size(); i++) {
// 创建实例-->赋值-->字典处理
// 将每个元素先转为JSON再反序列化为实际类型,确保类型准确
Object data = list.get(i);
try {
data = JSON.parseObject(JSON.toJSONString(data), actualType);
}catch (Exception e){
// 转换出错不处理
// 反序列化失败时使用原始数据继续处理
}
// 处理后的数据
// 递归调用parseObject对列表中每个对象进行字典翻译
Object pds = this.parseObject(data);
result.add(pds);
}
@@ -252,22 +303,27 @@ public class DictAspect {
}
/**
* 翻译实现
*
* @param code
* @param text
* @param table
* @param key
* @return
* 字典翻译实现:根据字典表、编码字段、文本字段和key值查询对应的可读文本
* <p>
* 通过SysDictService查询指定字典表中,匹配code字段值为key的记录,
* 返回其text字段的值作为翻译结果。
* </p>
* @param code 字典编码字段名(如"code"
* @param text 字典文本字段名(如"name"),即需要返回的翻译结果字段
* @param table 字典表名(如"sys_dict"
* @param key 当前字段的字典编码值,用于匹配查询
* @return 字典翻译后的可读文本,查询失败或未找到时返回空字符串
*/
private String translateDictValue(String code, String text, String table, String key) {
// key为空时无需查询,直接返回null
if (StringUtils.isEmpty(key)) {
return null;
}
try {
// 翻译值
// 调用字典服务查询翻译文本
String dictText = null;
if (!StringUtils.isEmpty(table)) {
// 从指定字典表中,根据code字段匹配key值,返回text字段的翻译结果
dictText = sysDictService.findDict(table, text, code, key.trim());
}
@@ -275,16 +331,20 @@ public class DictAspect {
return dictText;
}
} catch (Exception e) {
// 字典查询异常时不影响主流程,打印堆栈后返回空字符串
e.printStackTrace();
}
return "";
}
/**
* 判断是否基本类型
*
* @param clazz
* @return
* 判断给定类型是否基本类型(无需字典翻译的简单类型)
* <p>
* 基本类型包括:8种包装类型(Integer/Byte/Long/Double/Float/Character/Short/Boolean)、
* String类型以及Number类型。这些类型不包含@Dict注解字段,无需进行字典翻译处理。
* </p>
* @param clazz 待判断的类
* @return true表示是基本类型无需翻译,false表示是复杂对象需要翻译
*/
private boolean isBaseType(Class clazz) {