feat: 新增heritage-plugins基础插件(第1部分)
- 新增heritage-resultex:统一结果封装和全局异常处理插件 - 新增heritage-desensitize:数据脱敏插件,支持手机号、身份证等 - 新增heritage-dict:字典翻译插件,自动翻译字典值 - 新增plugins父POM配置
This commit is contained in:
parent
6e36d9ab83
commit
09457ecebd
31
heritage-plugins/heritage-desensitize/pom.xml
Normal file
31
heritage-plugins/heritage-desensitize/pom.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-plugins</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
|
||||
<name>heritage-desensitize</name>
|
||||
<artifactId>heritage-desensitize</artifactId>
|
||||
<description>数据脱敏插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 全局公共模块 -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- SpringBoot Aop依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,39 @@
|
||||
package org.leocoder.heritage.desensitize.anno;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.leocoder.heritage.desensitize.config.DesensitizeJsonSerializer;
|
||||
import org.leocoder.heritage.desensitize.enums.DesensitizeRuleEnum;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [CoderDesensitize]
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = DesensitizeJsonSerializer.class)
|
||||
public @interface CoderDesensitize {
|
||||
|
||||
/**
|
||||
* 脱敏数据类型,在MY_RULE的时候,startInclude和endExclude生效
|
||||
*/
|
||||
DesensitizeRuleEnum rule() default DesensitizeRuleEnum.CODER_RULE;
|
||||
|
||||
/**
|
||||
* 脱敏开始位置[不包含]
|
||||
*/
|
||||
int beginExclude() default 0;
|
||||
|
||||
/**
|
||||
* 脱敏结束位置[包含]
|
||||
*/
|
||||
int endInclude() default 0;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package org.leocoder.heritage.desensitize.anno;
|
||||
|
||||
import org.leocoder.heritage.desensitize.config.DesensitizeJsonSerializer;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
// 可用在字段上。
|
||||
@Target({ElementType.TYPE})
|
||||
// 运行时生效。
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({ DesensitizeJsonSerializer.class })
|
||||
public @interface EnableCoderDesensitize {
|
||||
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
package org.leocoder.heritage.desensitize.config;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.DesensitizedUtil;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.leocoder.heritage.common.satoken.CoderLoginUtil;
|
||||
import org.leocoder.heritage.desensitize.anno.CoderDesensitize;
|
||||
import org.leocoder.heritage.desensitize.enums.DesensitizeRuleEnum;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [DesensitizeSerialize自定义序列化类]
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DesensitizeJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
|
||||
|
||||
private DesensitizeRuleEnum ruleEnum;
|
||||
|
||||
private Integer beginExclude;
|
||||
|
||||
private Integer endInclude;
|
||||
|
||||
|
||||
public DesensitizeJsonSerializer(DesensitizeRuleEnum rule, int beginExclude, int endInclude, String[] strings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
// 绑定的需要脱敏的部门ID
|
||||
try {
|
||||
boolean isCoderAdmin = CoderLoginUtil.getIsCoderAdmin();
|
||||
if(isCoderAdmin) {
|
||||
// 无脱敏部门数据,直接写入原始值
|
||||
jsonGenerator.writeString(str);
|
||||
return;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
switch (ruleEnum) {
|
||||
// 自定义类型脱敏
|
||||
case CODER_RULE:
|
||||
jsonGenerator.writeString(CharSequenceUtil.hide(str, beginExclude, endInclude));
|
||||
break;
|
||||
// userId脱敏
|
||||
case USER_ID:
|
||||
jsonGenerator.writeString(String.valueOf(DesensitizedUtil.userId()));
|
||||
break;
|
||||
// 中文姓名脱敏
|
||||
case CHINESE_NAME:
|
||||
jsonGenerator.writeString(DesensitizedUtil.chineseName(String.valueOf(str)));
|
||||
break;
|
||||
// 身份证脱敏
|
||||
case ID_CARD:
|
||||
jsonGenerator.writeString(DesensitizedUtil.idCardNum(String.valueOf(str), 3, 4));
|
||||
break;
|
||||
// 固定电话脱敏
|
||||
case FIXED_PHONE:
|
||||
jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str)));
|
||||
break;
|
||||
// 手机号脱敏
|
||||
case MOBILE_PHONE:
|
||||
jsonGenerator.writeString(DesensitizedUtil.mobilePhone(String.valueOf(str)));
|
||||
break;
|
||||
// 地址脱敏
|
||||
case ADDRESS:
|
||||
jsonGenerator.writeString(DesensitizedUtil.address(String.valueOf(str), 8));
|
||||
break;
|
||||
// 邮箱脱敏
|
||||
case EMAIL:
|
||||
jsonGenerator.writeString(DesensitizedUtil.email(String.valueOf(str)));
|
||||
break;
|
||||
// 密码脱敏
|
||||
case PASSWORD:
|
||||
jsonGenerator.writeString(DesensitizedUtil.password(String.valueOf(str)));
|
||||
break;
|
||||
// 中国车牌脱敏
|
||||
case CAR_LICENSE:
|
||||
jsonGenerator.writeString(DesensitizedUtil.carLicense(String.valueOf(str)));
|
||||
break;
|
||||
// 银行卡脱敏
|
||||
case BANK_CARD:
|
||||
jsonGenerator.writeString(DesensitizedUtil.bankCard(String.valueOf(str)));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
|
||||
if (beanProperty != null) {
|
||||
// 判断数据类型是否为String类型
|
||||
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
|
||||
// 获取定义的注解
|
||||
CoderDesensitize desensitize = beanProperty.getAnnotation(CoderDesensitize.class);
|
||||
// 为null
|
||||
if (desensitize == null) {
|
||||
desensitize = beanProperty.getContextAnnotation(CoderDesensitize.class);
|
||||
}
|
||||
// 不为null
|
||||
if (desensitize != null) {
|
||||
// 创建定义的序列化类的实例并且返回,入参为注解定义的type,开始位置,结束位置。
|
||||
return new DesensitizeJsonSerializer(desensitize.rule(), desensitize.beginExclude(),
|
||||
desensitize.endInclude());
|
||||
}
|
||||
}
|
||||
|
||||
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
|
||||
}
|
||||
return serializerProvider.findNullValueSerializer(null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package org.leocoder.heritage.desensitize.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [DesensitizeRuleEnum脱敏策略]
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum DesensitizeRuleEnum {
|
||||
|
||||
/**
|
||||
* 自定义规则
|
||||
*/
|
||||
CODER_RULE,
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
USER_ID,
|
||||
|
||||
/**
|
||||
* 中文名
|
||||
*/
|
||||
CHINESE_NAME,
|
||||
|
||||
/**
|
||||
* 身份证号
|
||||
*/
|
||||
ID_CARD,
|
||||
|
||||
/**
|
||||
* 固定电话(座机号)
|
||||
*/
|
||||
FIXED_PHONE,
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
MOBILE_PHONE,
|
||||
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
ADDRESS,
|
||||
|
||||
/**
|
||||
* 电子邮件
|
||||
*/
|
||||
EMAIL,
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
PASSWORD,
|
||||
|
||||
/**
|
||||
* 中国大陆车牌,包含普通车辆、新能源车辆
|
||||
*/
|
||||
CAR_LICENSE,
|
||||
|
||||
/**
|
||||
* 银行卡
|
||||
*/
|
||||
BANK_CARD
|
||||
|
||||
}
|
||||
35
heritage-plugins/heritage-dict/pom.xml
Normal file
35
heritage-plugins/heritage-dict/pom.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-plugins</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<name>heritage-dict</name>
|
||||
<artifactId>heritage-dict</artifactId>
|
||||
<description>字典翻译插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 通用公共模块 -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 数据模型模块 -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-model</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- Aop依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,16 @@
|
||||
package org.leocoder.heritage.dict.anno;
|
||||
|
||||
import org.leocoder.heritage.common.utils.cache.RedisUtil;
|
||||
import org.leocoder.heritage.dict.aspect.CoderDictAspect;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({CoderDictAspect.class, RedisUtil.class})
|
||||
public @interface EnableCoderDict {
|
||||
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
package org.leocoder.heritage.dict.aspect;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.leocoder.heritage.common.anno.CoderDict;
|
||||
import org.leocoder.heritage.common.anno.CoderDictClass;
|
||||
import org.leocoder.heritage.common.constants.CoderCacheConstants;
|
||||
import org.leocoder.heritage.common.utils.cache.RedisUtil;
|
||||
import org.leocoder.heritage.domain.pojo.system.SysDictData;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [CoderDictAspect]
|
||||
*/
|
||||
@Aspect
|
||||
@Order(4)
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CoderDictAspect {
|
||||
|
||||
private Map<String, String> dictMap = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Pointcut("@annotation(org.leocoder.heritage.common.anno.CoderDictClass)")
|
||||
public void logPointCut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [翻译数据]
|
||||
* 需要处理的数据类型:
|
||||
* 1、分页数据R<IPage<T>>
|
||||
* 2、普通列表R<List<T>>
|
||||
* 3、普通数据R<T>
|
||||
* @author Leocoder
|
||||
*/
|
||||
@Around("@annotation(CoderDictClass)")
|
||||
public Object CoderTypeDictTranslation(final ProceedingJoinPoint proceedingJoinPoint, CoderDictClass CoderDictClass) throws Throwable {
|
||||
Object proceed = proceedingJoinPoint.proceed();
|
||||
if (ObjectUtils.isEmpty(proceed)) {
|
||||
return proceed;
|
||||
}
|
||||
if (proceed instanceof List) {
|
||||
// 数据字典翻译
|
||||
List<Object> list = (List<Object>) proceed;
|
||||
CoderDictTranslate(list);
|
||||
return list;
|
||||
} else if (proceed instanceof Page) {
|
||||
// Page 类型处理逻辑
|
||||
IPage page = (IPage) proceed;
|
||||
List records = page.getRecords();
|
||||
// 数据字典翻译
|
||||
CoderDictTranslate(records);
|
||||
page.setRecords(records);
|
||||
// 返回修改后的分页对象
|
||||
return page;
|
||||
} else {
|
||||
// 其他类型处理逻辑或错误处理
|
||||
return proceed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [获取不同类型返回结果数据进行翻译]
|
||||
* @author Leocoder
|
||||
*/
|
||||
private void CoderDictTranslate(Object resultData) {
|
||||
Object objectData;
|
||||
// 检查输入的result是否是List或者ArrayList的实例
|
||||
if (resultData instanceof List) {
|
||||
// 如果是列表,则获取第一个元素
|
||||
List CoderList = ((List) resultData);
|
||||
// 如果列表为空,则直接返回原始结果
|
||||
if (CoderList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 获取集合的第一条数据
|
||||
objectData = CoderList.get(0);
|
||||
} else {
|
||||
// 否则直接使用result对象
|
||||
objectData = resultData;
|
||||
}
|
||||
// 获取数据字典key 和 实体类字段名称
|
||||
List<CoderDictModel> dictModelList = getCoderDict(objectData.getClass());
|
||||
// dictModelList.forEach(System.out::println);
|
||||
// CoderDictModel(dictKey=sys_user_sex, dictValue=sex)
|
||||
// 如果没有字典映射,则直接返回原始结果
|
||||
if (dictModelList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取所有的字典数据
|
||||
List<SysDictData> dictDataList = listDictCache(dictModelList);
|
||||
|
||||
// 如果字典数据是空则直接返回
|
||||
if (CollectionUtils.isEmpty(dictDataList)) {
|
||||
return;
|
||||
}
|
||||
// 将字典值转换成map形式
|
||||
for (SysDictData dictData : dictDataList) {
|
||||
dictMap.put(dictData.getDictType() + "_" + dictData.getDictValue(), dictData.getDictLabel());
|
||||
}
|
||||
// 根据对象类型,为返回数据的每一个对象赋予字典值
|
||||
if (resultData instanceof List) {
|
||||
for (Object entity : (List) resultData) {
|
||||
assignDictValue(entity, dictModelList, dictMap);
|
||||
}
|
||||
} else {
|
||||
assignDictValue(resultData, dictModelList, dictMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description [获取字典数据缓存]
|
||||
* @author Leocoder
|
||||
*/
|
||||
private List<SysDictData> listDictCache(List<CoderDictModel> dictModelList) {
|
||||
List<SysDictData> dictDataList = new ArrayList<>();
|
||||
List<String> dictKeyList = getDictKey(dictModelList);
|
||||
for (String dictKey : dictKeyList) {
|
||||
Boolean hasKey = redisUtil.hasKey(CoderCacheConstants.DICT_REDIS_KEY + dictKey);
|
||||
if(hasKey) {
|
||||
List<SysDictData> dictDataKeyList = redisUtil.getKey(CoderCacheConstants.DICT_REDIS_KEY + dictKey);
|
||||
dictDataList.addAll(dictDataKeyList);
|
||||
}
|
||||
}
|
||||
return dictDataList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param entity 返回List集合数据循环出来的实体类对象,转换字典的核心代码
|
||||
* @param dictModelList CoderDictModel(dictKey=sys_user_sex, dictValue=sex) 获取实体类的@CoderDict注解值
|
||||
* @param dictMap 所有字典数据缓存
|
||||
*/
|
||||
public void assignDictValue(Object entity, List<CoderDictModel> dictModelList, Map<String, String> dictMap) {
|
||||
try {
|
||||
// 遍历每个CoderDictModel对象
|
||||
for (CoderDictModel dictModel : dictModelList) {
|
||||
String dictKey = dictModel.getDictKey(); // 获取字典类型
|
||||
String fieldName = dictModel.getDictValue(); // 获取需要赋值的字段名
|
||||
Class<?> classData = entity.getClass(); // 获取实体类的类
|
||||
Field fieldData = classData.getDeclaredField(fieldName); // 获取需要赋值的字段
|
||||
fieldData.setAccessible(true); // 设置字段可访问性
|
||||
Object originalValue = fieldData.get(entity); // 获取字段的原始值
|
||||
if (ObjectUtils.isNotEmpty(originalValue)) {
|
||||
// 如果原始值不为空,则将字典值赋给字段
|
||||
String dictValue = dictMap.getOrDefault(dictKey + "_" + originalValue, originalValue.toString());
|
||||
fieldData.set(entity, dictValue);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取实体中所有需要翻译的dictKey,返回需要翻译的dictKey列表
|
||||
*/
|
||||
private List<String> getDictKey(List<CoderDictModel> CoderDictBos) {
|
||||
// 创建一个字符串列表用于存储dictKey
|
||||
List<String> dictKeyList = new ArrayList<>();
|
||||
// 如果传入的CoderDictBo为空,则直接返回空列表
|
||||
if (CollectionUtils.isEmpty(CoderDictBos)) {
|
||||
return dictKeyList;
|
||||
}
|
||||
for (CoderDictModel dictBo : CoderDictBos) {
|
||||
// 将每个CoderDictBo对象的key添加到列表中
|
||||
dictKeyList.add(dictBo.getDictKey());
|
||||
}
|
||||
return dictKeyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体类中配置的dictKey 和 对应的字段名称
|
||||
*/
|
||||
private List<CoderDictModel> getCoderDict(Class classData) {
|
||||
Field[] fields = classData.getDeclaredFields(); // 获取类classData中声明的所有字段
|
||||
List<CoderDictModel> list = new ArrayList<>(); // 创建一个CoderDictBo列表
|
||||
CoderDictModel CoderDictBo;
|
||||
CoderDict CoderDict;
|
||||
for (Field field : fields) {
|
||||
if (field.isAnnotationPresent(CoderDict.class)) { // 检查字段是否带有CoderDict注解
|
||||
CoderDict = field.getAnnotation(CoderDict.class); // 获取CoderDict注解
|
||||
CoderDictBo = new CoderDictModel(CoderDict.dictKey(), field.getName()); // 创建CoderDictBo对象,存储dictKey和字段名
|
||||
list.add(CoderDictBo);
|
||||
}
|
||||
}
|
||||
// 返回包含配置的dictKey和对应字段内容的列表
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package org.leocoder.heritage.dict.aspect;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [CoderDictModel-@CoderDict注解字段]
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class CoderDictModel {
|
||||
|
||||
/** 字典Key值 */
|
||||
private String dictKey;
|
||||
|
||||
/** 实体类字段名称 */
|
||||
private String dictValue;
|
||||
}
|
||||
61
heritage-plugins/heritage-resultex/pom.xml
Normal file
61
heritage-plugins/heritage-resultex/pom.xml
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-plugins</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
|
||||
<name>heritage-resultex</name>
|
||||
<artifactId>heritage-resultex</artifactId>
|
||||
<description>全局统一返回 和 全局异常类</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Model模块 -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-model</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- MyBatisPlus模块[可删除,coder-dict插件已经依赖] -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-mybatisplus</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 数据字典翻译 -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.leocoder.heritage</groupId> -->
|
||||
<!-- <artifactId>heritage-dict</artifactId> -->
|
||||
<!-- <version>${revision}</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- 限流(工具类模块已包含里面,其他插件都使用) -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-limit</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 重复提交插件-->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-repect</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- EasyExcel插件 -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-easyexcel</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 权限认证,用来处理全局拦截sa-token异常使用,在线文档:https://sa-token.cc -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,17 @@
|
||||
package org.leocoder.heritage.resultex.anno;
|
||||
|
||||
import org.leocoder.heritage.resultex.handler.GlobalExceptionHandler;
|
||||
import org.leocoder.heritage.resultex.handler.ResultResponseHandler;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({ GlobalExceptionHandler.class, ResultResponseHandler.class })
|
||||
public @interface EnableResultEx {
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,362 @@
|
||||
package org.leocoder.heritage.resultex.handler;
|
||||
|
||||
import cn.dev33.satoken.exception.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.leocoder.heritage.common.enmus.common.IResultEnum;
|
||||
import org.leocoder.heritage.common.exception.BusinessException;
|
||||
import org.leocoder.heritage.common.exception.RateLimiterException;
|
||||
import org.leocoder.heritage.common.exception.RepeatSubmitException;
|
||||
import org.leocoder.heritage.common.exception.coder.ParamsException;
|
||||
import org.leocoder.heritage.common.resultex.ErrorHandler;
|
||||
import org.leocoder.heritage.common.utils.json.JsonUtil;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
import org.yaml.snakeyaml.constructor.DuplicateKeyException;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [全局异常拦截-所有异常必须放置这个类中]
|
||||
*/
|
||||
@Slf4j
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* @description [拦截所有程序异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
public ErrorHandler errorHandler(HttpServletRequest request, Exception ex) {
|
||||
log.error("Coder-ADMIN未知异常:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, "系统异常,请联系管理员!", request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [自定义异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(value = ParamsException.class)
|
||||
public ErrorHandler errorHandlerParamsException(HttpServletRequest request, ParamsException ex) {
|
||||
log.error("Coder-ADMIN自定义异常ParamsException:{},请求地址:{}", ex.getMessage(), request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(ex.getStatus(), ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [自定义业务异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(value = BusinessException.class)
|
||||
public ErrorHandler errorHandlerBusinessEx(HttpServletRequest request, BusinessException ex) {
|
||||
log.error("业务异常:{},请求地址:{}", ex.getMessage(), request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(ex.getStatus(), ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [Sa-Token 登录失败异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
public ErrorHandler handlerNotLoginException(HttpServletRequest request, NotLoginException ex) {
|
||||
// 不同异常返回不同状态码
|
||||
String message = "";
|
||||
if (ex.getType().equals(NotLoginException.NOT_TOKEN)) {
|
||||
message = "未提供Token";
|
||||
} else if (ex.getType().equals(NotLoginException.INVALID_TOKEN)) {
|
||||
message = "未提供有效的Token";
|
||||
} else if (ex.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
|
||||
message = "登录信息已过期,请重新登录";
|
||||
} else if (ex.getType().equals(NotLoginException.BE_REPLACED)) {
|
||||
message = "您的账户已在另一台设备上登录,如非本人操作,请立即修改密码";
|
||||
} else if (ex.getType().equals(NotLoginException.KICK_OUT)) {
|
||||
message = "已被系统强制下线";
|
||||
} else {
|
||||
message = "当前会话未登录";
|
||||
}
|
||||
log.error("Sa-Token异常提示:{}", ex.getMessage());
|
||||
// 返回给前端
|
||||
return ErrorHandler.error(401, message, request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
public ErrorHandler handlerNotRoleException(HttpServletRequest request, NotRoleException ex) {
|
||||
log.error("Sa-Token提示无此角色:{}", ex.getRole());
|
||||
return ErrorHandler.error(401, "无此角色:" + ex.getRole(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
public ErrorHandler handlerNotPermissionException(HttpServletRequest request, NotPermissionException ex) {
|
||||
log.error("Sa-Token提示无此权限:{}", ex.getPermission());
|
||||
return ErrorHandler.error(401, "无此权限:" + ex.getPermission(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
public ErrorHandler handlerDisableLoginException(HttpServletRequest request, DisableServiceException ex) {
|
||||
log.error("Sa-Token提示账户被封禁:{}秒后解封", ex.getDisableTime());
|
||||
return ErrorHandler.error(401, "账户被封禁:" + ex.getDisableTime() + "秒后解封", request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
public ErrorHandler handlerNotSafeException(HttpServletRequest request, NotSafeException ex) {
|
||||
log.error("Sa-Token提示二级认证异常:{}", ex.getMessage());
|
||||
return ErrorHandler.error(401, "二级认证异常:" + ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
public ErrorHandler handlerStopMatchException(HttpServletRequest request, StopMatchException ex) {
|
||||
log.error("Sa-Token提示路由匹配异常:{}", ex.getMessage());
|
||||
return ErrorHandler.error(500, "路由匹配异常", request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
public ErrorHandler handlerBackResultException(HttpServletRequest request, BackResultException ex) {
|
||||
log.error("Sa-Token提示停止匹配:{}", ex.getMessage());
|
||||
return ErrorHandler.error(500, "停止匹配", request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [处理Get请求中,使用@Valid 验证路径中请求实体校验失败后抛出的异常]
|
||||
* @Validated @Valid[仅对于表单提交有效,对于以json格式提交将会失效]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
public ErrorHandler handleBindException(BindException e) {
|
||||
log.error("自定义验证异常BindException:{}", e.getMessage());
|
||||
String message = e.getAllErrors().stream()
|
||||
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
return ErrorHandler.error(IResultEnum.SERVER_ERROR, JsonUtil.toJsonString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [@Validated @Valid 前端提交的方式为json格式有效]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ErrorHandler handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
log.error("自定义验证异常MethodArgumentNotValidException:{}", e.getMessage());
|
||||
String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
|
||||
return ErrorHandler.error(IResultEnum.SERVER_ERROR, JsonUtil.toJsonString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException]
|
||||
* 针对:@NotBlank @NotNull @NotEmpty
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public ErrorHandler handlerConstraintViolationException(ConstraintViolationException e) {
|
||||
log.error("自定义验证异常ConstraintViolationException:{}", e.getMessage());
|
||||
String errorMessages = e.getConstraintViolations().stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.joining(";"));
|
||||
return ErrorHandler.error(IResultEnum.SERVER_ERROR, errorMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对验证异常进行统一处理,List集合方式
|
||||
*/
|
||||
// private List<Map<String, String>> toValidatorMsg(List<FieldError> fieldErrorList) {
|
||||
// List<Map<String, String>> mapList = new ArrayList<>();
|
||||
// for (FieldError fieldError : fieldErrorList) {
|
||||
// Map<String, String> map = new HashMap<>();
|
||||
// map.put("field", fieldError.getField());
|
||||
// map.put("msg", fieldError.getDefaultMessage());
|
||||
// mapList.add(map);
|
||||
// }
|
||||
// return mapList;
|
||||
// }
|
||||
|
||||
/**
|
||||
* @description [IllegalArgumentException]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ErrorHandler handlerIllegalArgumentException(HttpServletRequest request, IllegalArgumentException ex) {
|
||||
log.error("IllegalArgumentException异常:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [lua限流异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(RateLimiterException.class)
|
||||
public ErrorHandler RateLimitException(HttpServletRequest request, RateLimiterException ex) {
|
||||
log.error("lua限流异常:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [重复提交异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(RepeatSubmitException.class)
|
||||
public ErrorHandler RepeatSubmitException(HttpServletRequest request, RepeatSubmitException ex) {
|
||||
log.error("重复提交异常:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [处理空指针异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(NullPointerException.class)
|
||||
public ErrorHandler exceptionHandler(HttpServletRequest request, NullPointerException ex) {
|
||||
log.error("空指针异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, "空指针异常", request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [DB主键冲突异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(DuplicateKeyException.class)
|
||||
public ErrorHandler handleDuplicateKeyException(HttpServletRequest request, DuplicateKeyException ex) {
|
||||
log.error("数据库主键冲突异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [运算异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(ArithmeticException.class)
|
||||
public ErrorHandler arithmeticExceptionHandler(HttpServletRequest request, ArithmeticException ex) {
|
||||
log.error("运算异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [类型转换异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(ClassCastException.class)
|
||||
public ErrorHandler classCastExceptionHandler(HttpServletRequest request, ClassCastException ex) {
|
||||
log.error("类型转换异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [数据下标越界异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(IndexOutOfBoundsException.class)
|
||||
public ErrorHandler indexOutOfBoundsExceptionHandler(HttpServletRequest request, IndexOutOfBoundsException ex) {
|
||||
log.error("数据下标越界异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [文件未找到异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(FileNotFoundException.class)
|
||||
public ErrorHandler fileNotFoundExceptionHandler(HttpServletRequest request, FileNotFoundException ex) {
|
||||
log.error("文件未找到异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [IO异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(IOException.class)
|
||||
public ErrorHandler IOExceptionHandler(HttpServletRequest request, IOException ex) {
|
||||
log.error("IO异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [参数类型不匹配]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler({MethodArgumentTypeMismatchException.class})
|
||||
public ErrorHandler requestTypeMismatch(HttpServletRequest request, MethodArgumentTypeMismatchException ex) {
|
||||
log.error("参数类型不匹配异常,请联系管理员核实:{},请求地址:{}", ex, request.getRequestURL().toString());
|
||||
ex.printStackTrace();
|
||||
return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [文件上传大小超限异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||
public ErrorHandler handleMaxUploadSizeExceededException(HttpServletRequest request, MaxUploadSizeExceededException ex) {
|
||||
log.error("文件上传大小超限异常:{},请求地址:{}", ex.getMessage(), request.getRequestURL().toString());
|
||||
|
||||
// 提取文件大小限制信息,提供更友好的错误提示
|
||||
String message = "文件大小超出限制,请上传小于2MB的文件";
|
||||
|
||||
// 尝试从异常信息中提取具体的大小限制
|
||||
String exceptionMsg = ex.getMessage();
|
||||
if (exceptionMsg != null && exceptionMsg.contains("maximum permitted size")) {
|
||||
if (exceptionMsg.contains("1048576")) {
|
||||
message = "文件大小超出限制,最大允许上传1MB的文件";
|
||||
} else if (exceptionMsg.contains("2097152")) {
|
||||
message = "文件大小超出限制,最大允许上传2MB的文件";
|
||||
} else if (exceptionMsg.contains("5242880")) {
|
||||
message = "文件大小超出限制,最大允许上传5MB的文件";
|
||||
} else if (exceptionMsg.contains("10485760")) {
|
||||
message = "文件大小超出限制,最大允许上传10MB的文件";
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorHandler.error(413, message, request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [文件上传异常]
|
||||
* @author Leocoder
|
||||
*/
|
||||
@ExceptionHandler(MultipartException.class)
|
||||
public ErrorHandler handleMultipartException(HttpServletRequest request, MultipartException ex) {
|
||||
log.error("文件上传异常:{},请求地址:{}", ex.getMessage(), request.getRequestURL().toString());
|
||||
|
||||
String message = "文件上传失败";
|
||||
String exceptionMsg = ex.getMessage();
|
||||
|
||||
if (exceptionMsg != null) {
|
||||
if (exceptionMsg.contains("size")) {
|
||||
message = "文件大小超出限制,请选择较小的文件";
|
||||
} else if (exceptionMsg.contains("format") || exceptionMsg.contains("type")) {
|
||||
message = "文件格式不支持,请选择正确的文件格式";
|
||||
} else if (exceptionMsg.contains("empty")) {
|
||||
message = "请选择要上传的文件";
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorHandler.error(400, message, request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package org.leocoder.heritage.resultex.handler;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Nullable;
|
||||
import net.dreamlu.mica.core.result.R;
|
||||
import org.leocoder.heritage.common.anno.CoderIgnoreR;
|
||||
import org.leocoder.heritage.common.resultex.ErrorHandler;
|
||||
import org.leocoder.heritage.common.resultex.ResultUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author leocoder
|
||||
* @description 全局统一返回类
|
||||
* bug: (basePackages = "org.leocoder")建议扫包
|
||||
* 为什么?
|
||||
* 如果你项目中没有使用Swagger,你可以扫包也可以不扫。都是正常的。
|
||||
* 但是如果你项目使用了Swagger,因为Swagger本身也是一个springmvc的项目,他里面也是一个个http请求
|
||||
* 这个请求的时候如果你项目中配置了拦截器,或者一些通知类xxxAdvice,那么就会把Swagger都会进行拦截。
|
||||
* 就会造成Swagger失效。
|
||||
* 解决knife4j失效问题:(basePackages = { "org.leocoder" }, annotations = { RestController.class })
|
||||
*/
|
||||
@RestControllerAdvice(basePackages = { "org.leocoder" }, annotations = { RestController.class })
|
||||
public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
|
||||
|
||||
/**
|
||||
* 是否支持advice功能,true是支持 false是不支持
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(@Nullable MethodParameter methodParameter, @Nullable Class<? extends HttpMessageConverter<?>> CoderClass) {
|
||||
// 排除Swagger相关路径,避免干扰OpenAPI文档生成
|
||||
if (methodParameter != null && methodParameter.getMethod() != null) {
|
||||
String className = methodParameter.getMethod().getDeclaringClass().getName();
|
||||
// 排除SpringDoc相关的Controller
|
||||
if (className.contains("springdoc") || className.contains("swagger")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object body, @Nullable MethodParameter methodParameter, @Nullable MediaType mediaType, Class<? extends HttpMessageConverter<?>> coderClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
|
||||
// 参数body 代表其实就是SpringMvc的请求的方法的结果
|
||||
// 对请求的结果在这里统一返回和处理
|
||||
|
||||
// 排除Swagger相关路径,避免包装OpenAPI文档
|
||||
String requestPath = serverHttpRequest.getURI().getPath();
|
||||
if (requestPath.startsWith("/v3/api-docs") ||
|
||||
requestPath.startsWith("/swagger-ui") ||
|
||||
requestPath.contains("/swagger") ||
|
||||
requestPath.contains("/api-docs")) {
|
||||
return body;
|
||||
}
|
||||
|
||||
if (body instanceof ErrorHandler errorHandler) {
|
||||
// 如果返回的结果是一个异常的结果,就把异常返回的结构数据倒腾到R.error里面即可
|
||||
return ResultUtils.error(errorHandler.getStatus(), errorHandler.getMsg());
|
||||
}
|
||||
|
||||
// 检查是否有 CoderIgnoreR 注解,如果有则不进行封装,直接返回原始数据body
|
||||
if (methodParameter != null) {
|
||||
Method method = methodParameter.getMethod();
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
|
||||
// 检查方法级别的注解
|
||||
if (method.isAnnotationPresent(CoderIgnoreR.class)) {
|
||||
return body;
|
||||
}
|
||||
|
||||
// 检查类级别的注解
|
||||
if (declaringClass.isAnnotationPresent(CoderIgnoreR.class)) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
if (body instanceof R) {
|
||||
return body;
|
||||
}
|
||||
|
||||
if (body instanceof String) {
|
||||
try {
|
||||
// 因为SpringMVC数据转换器对String是有特殊处理 StringHttpMessageConverter,解决String类型的返回
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
R r = R.success(body);
|
||||
return objectMapper.writeValueAsString(r);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 对于其他类型的对象,直接封装到 R 中
|
||||
return R.success(body);
|
||||
}
|
||||
|
||||
}
|
||||
30
heritage-plugins/pom.xml
Normal file
30
heritage-plugins/pom.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.leocoder.heritage</groupId>
|
||||
<artifactId>heritage-backend</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
|
||||
<artifactId>heritage-plugins</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<description>插件模块</description>
|
||||
|
||||
<modules>
|
||||
<module>heritage-resultex</module>
|
||||
<module>heritage-sa-token</module>
|
||||
<module>heritage-desensitize</module>
|
||||
<module>heritage-easyexcel</module>
|
||||
<module>heritage-repect</module>
|
||||
<module>heritage-limit</module>
|
||||
<module>heritage-oper-logs</module>
|
||||
<module>heritage-oss</module>
|
||||
<module>heritage-dict</module>
|
||||
<module>heritage-job</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
Loading…
Reference in New Issue
Block a user