Merge remote-tracking branch 'origin/master-jdk17' into master-jdk17-dev

This commit is contained in:
1351515658@qq.com
2024-07-29 16:53:36 +08:00
285 changed files with 2863 additions and 2193 deletions

View File

@@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.jackson.core.databind;
package cn.iocoder.yudao.framework.common.util.json.databind;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;

View File

@@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.jackson.core.databind;
package cn.iocoder.yudao.framework.common.util.json.databind;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;

View File

@@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.jackson.core.databind;
package cn.iocoder.yudao.framework.common.util.json.databind;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;

View File

@@ -34,6 +34,13 @@
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
<optional>true</optional>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>

View File

@@ -0,0 +1,34 @@
package cn.iocoder.yudao.framework.datapermission.config;
import cn.iocoder.yudao.framework.datapermission.core.rpc.DataPermissionRequestInterceptor;
import cn.iocoder.yudao.framework.datapermission.core.rpc.DataPermissionRpcWebFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import static cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum.TENANT_CONTEXT_FILTER;
/**
* 数据权限针对 RPC 的自动配置类
*
* @author 芋道源码
*/
@AutoConfiguration
@ConditionalOnClass(name = "feign.RequestInterceptor")
public class YudaoDataPermissionRpcAutoConfiguration {
@Bean
public DataPermissionRequestInterceptor dataPermissionRequestInterceptor() {
return new DataPermissionRequestInterceptor();
}
@Bean
public FilterRegistrationBean<DataPermissionRpcWebFilter> dataPermissionRpcFilter() {
FilterRegistrationBean<DataPermissionRpcWebFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new DataPermissionRpcWebFilter());
registrationBean.setOrder(TENANT_CONTEXT_FILTER - 1); // 顺序没有绝对的要求,在租户 Filter 前面稳妥点
return registrationBean;
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.framework.datapermission.core.rpc;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
import feign.RequestInterceptor;
import feign.RequestTemplate;
/**
* DataPermission 的 RequestInterceptor 实现类Feign 请求时,将 {@link DataPermission} 设置到 header 中,继续透传给被调用的服务
*
* 注意:由于 {@link DataPermission} 不支持序列化和反序列化,所以暂时只能传递它的 enable 属性
*
* @author 芋道源码
*/
public class DataPermissionRequestInterceptor implements RequestInterceptor {
public static final String ENABLE_HEADER_NAME = "data-permission-enable";
@Override
public void apply(RequestTemplate requestTemplate) {
DataPermission dataPermission = DataPermissionContextHolder.get();
if (dataPermission != null && Boolean.FALSE.equals(dataPermission.enable())) {
requestTemplate.header(ENABLE_HEADER_NAME, "false");
}
}
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.yudao.framework.datapermission.core.rpc;
import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Objects;
/**
* 针对 {@link DataPermissionRequestInterceptor} 的 RPC 调用,设置 {@link DataPermissionContextHolder} 的上下文
*
* @author 芋道源码
*/
public class DataPermissionRpcWebFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String enable = request.getHeader(DataPermissionRequestInterceptor.ENABLE_HEADER_NAME);
if (Objects.equals(enable, Boolean.FALSE.toString())) {
DataPermissionUtils.executeIgnore(() -> {
try {
chain.doFilter(request, response);
} catch (IOException | ServletException e) {
throw new RuntimeException(e);
}
});
} else {
chain.doFilter(request, response);
}
}
}

View File

@@ -157,7 +157,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
// 拼接条件
return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName),
// Parenthesis 的目的,是提供 (1,2,3) 的 () 左右括号
new Parenthesis(new ExpressionList<>(CollectionUtils.convertList(deptIds, LongValue::new))));
new Parenthesis(new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new))));
}
private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {

View File

@@ -1,2 +1,3 @@
cn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionAutoConfiguration
cn.iocoder.yudao.framework.datapermission.config.YudaoDeptDataPermissionAutoConfiguration
cn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionRpcAutoConfiguration

View File

@@ -1,9 +1,11 @@
package cn.iocoder.yudao.framework.ip.core;
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.List;
@@ -17,6 +19,7 @@ import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = {"parent"}) // 参见 https://gitee.com/yudaocode/yudao-cloud-mini/pulls/2 原因
public class Area {
/**
@@ -46,10 +49,12 @@ public class Area {
/**
* 父节点
*/
@JsonManagedReference
private Area parent;
/**
* 子节点
*/
@JsonManagedReference
private List<Area> children;
}

View File

@@ -39,4 +39,11 @@ public class TenantProperties {
*/
private Set<String> ignoreTables = Collections.emptySet();
/**
* 需要忽略多租户的 Spring Cache 缓存
*
* 即默认所有缓存都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟
*/
private Set<String> ignoreCaches = Collections.emptySet();
}

View File

@@ -133,12 +133,13 @@ public class YudaoTenantAutoConfiguration {
@Primary // 引入租户时tenantRedisCacheManager 为主 Bean
public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate,
RedisCacheConfiguration redisCacheConfiguration,
YudaoCacheProperties yudaoCacheProperties) {
YudaoCacheProperties yudaoCacheProperties,
TenantProperties tenantProperties) {
// 创建 RedisCacheWriter 对象
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
// 创建 TenantRedisCacheManager 对象
return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration);
return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties.getIgnoreCaches());
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.tenant.core.redis;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
@@ -8,6 +9,8 @@ import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import java.util.Set;
/**
* 多租户的 {@link RedisCacheManager} 实现类
*
@@ -18,16 +21,21 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
@Slf4j
public class TenantRedisCacheManager extends TimeoutRedisCacheManager {
private final Set<String> ignoreCaches;
public TenantRedisCacheManager(RedisCacheWriter cacheWriter,
RedisCacheConfiguration defaultCacheConfiguration) {
RedisCacheConfiguration defaultCacheConfiguration,
Set<String> ignoreCaches) {
super(cacheWriter, defaultCacheConfiguration);
this.ignoreCaches = ignoreCaches;
}
@Override
public Cache getCache(String name) {
// 如果开启多租户,则 name 拼接租户后缀
if (!TenantContextHolder.isIgnore()
&& TenantContextHolder.getTenantId() != null) {
&& TenantContextHolder.getTenantId() != null
&& !CollUtil.contains(ignoreCaches, name)) {
name = name + ":" + TenantContextHolder.getTenantId();
}

View File

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.excel.core.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
@@ -55,12 +54,6 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
private final Map<Integer, List<String>> selectMap = new HashMap<>();
public SelectSheetWriteHandler(Class<?> head) {
// 加载下拉数据获取接口
Map<String, ExcelColumnSelectFunction> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class);
if (MapUtil.isEmpty(beansMap)) {
return;
}
// 解析下拉数据
int colIndex = 0;
for (Field field : head.getDeclaredFields()) {

View File

@@ -56,7 +56,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
default <D> PageResult<D> selectJoinPage(PageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {
// 特殊:不分页,直接查询全部
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageNo())) {
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
List<D> list = selectJoinList(clazz, lambdaWrapper);
return new PageResult<>(list, (long) list.size());
}

View File

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -42,6 +43,10 @@ public class LoginUser {
* 授权范围
*/
private List<String> scopes;
/**
* 过期时间
*/
private LocalDateTime expiresTime;
// ========== 上下文 ==========
/**

View File

@@ -96,7 +96,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
// 构建登录用户
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
.setInfo(accessToken.getUserInfo()) // 额外的用户信息
.setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes());
.setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
.setExpiresTime(accessToken.getExpiresTime());
} catch (ServiceException serviceException) {
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
return null;

View File

@@ -1,13 +1,7 @@
package cn.iocoder.yudao.framework.security.core.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
@@ -20,7 +14,6 @@ import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildCache;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -70,7 +63,11 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override
@SneakyThrows
public boolean hasAnyPermissions(String... permissions) {
return hasAnyPermissionsCache.get(new KeyValue<>(getLoginUserId(), Arrays.asList(permissions)));
Long userId = getLoginUserId();
if (userId == null) {
return false;
}
return hasAnyPermissionsCache.get(new KeyValue<>(userId, Arrays.asList(permissions)));
}
@Override
@@ -81,7 +78,11 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override
@SneakyThrows
public boolean hasAnyRoles(String... roles) {
return hasAnyRolesCache.get(new KeyValue<>(getLoginUserId(), Arrays.asList(roles)));
Long userId = getLoginUserId();
if (userId == null) {
return false;
}
return hasAnyRolesCache.get(new KeyValue<>(userId, Arrays.asList(roles)));
}
@Override

View File

@@ -181,7 +181,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
// ========== 请求和响应的脱敏逻辑,移除类似 password、token 等敏感字段 ==========
private static String sanitizeMap(Map<String, ?> map, String[] sanitizeKeys) {
if (CollUtil.isNotEmpty(map)) {
if (CollUtil.isEmpty(map)) {
return null;
}
if (sanitizeKeys != null) {

View File

@@ -1,6 +1,8 @@
package cn.iocoder.yudao.framework.apilog.core.interceptor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
@@ -11,7 +13,11 @@ import org.springframework.util.StopWatch;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.IntStream;
/**
* API 访问日志 Interceptor
@@ -49,6 +55,8 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
// 打印 Controller 路径
printHandlerMethodPosition(handlerMethod);
}
return true;
}
@@ -64,4 +72,32 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
}
}
/**
* 打印 Controller 方法路径
*/
private void printHandlerMethodPosition(HandlerMethod handlerMethod) {
if (handlerMethod == null) {
return;
}
Method method = handlerMethod.getMethod();
Class<?> clazz = method.getDeclaringClass();
try {
// 获取 method 的 lineNumber
List<String> clazzContents = FileUtil.readUtf8Lines(
ResourceUtil.getResource(null, clazz).getPath().replace("/target/classes/", "/src/main/java/")
+ clazz.getSimpleName() + ".java");
Optional<Integer> lineNumber = IntStream.range(0, clazzContents.size())
.filter(i -> clazzContents.get(i).contains(" " + method.getName() + "(")) // 简单匹配,不考虑方法重名
.mapToObj(i -> i + 1) // 行号从 1 开始
.findFirst();
if (!lineNumber.isPresent()) {
return;
}
// 打印结果
System.out.printf("\tController 方法路径:%s(%s.java:%d)\n", clazz.getName(), clazz.getSimpleName(), lineNumber.get());
} catch (Exception ignore) {
// 忽略异常。原因:仅仅打印,非重要逻辑
}
}
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service;
import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
/**
@@ -13,6 +14,7 @@ import org.springframework.scheduling.annotation.Async;
* @author 芋道源码
*/
@RequiredArgsConstructor
@Slf4j
public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService {
private final ApiAccessLogApi apiAccessLogApi;
@@ -20,7 +22,12 @@ public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkSe
@Override
@Async
public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) {
apiAccessLogApi.createApiAccessLog(reqDTO);
try {
apiAccessLogApi.createApiAccessLog(reqDTO);
} catch (Throwable ex) {
// 由于 @Async 异步调用,这里打印下日志,更容易跟进
log.error("[createApiAccessLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex);
}
}
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service;
import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
/**
@@ -13,6 +14,7 @@ import org.springframework.scheduling.annotation.Async;
* @author 芋道源码
*/
@RequiredArgsConstructor
@Slf4j
public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService {
private final ApiErrorLogApi apiErrorLogApi;
@@ -20,7 +22,12 @@ public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkServ
@Override
@Async
public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) {
apiErrorLogApi.createApiErrorLog(reqDTO);
try {
apiErrorLogApi.createApiErrorLog(reqDTO);
} catch (Throwable ex) {
// 由于 @Async 异步调用,这里打印下日志,更容易跟进
log.error("[createApiErrorLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex);
}
}
}

View File

@@ -2,9 +2,9 @@ package cn.iocoder.yudao.framework.jackson.config;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.jackson.core.databind.NumberSerializer;
import cn.iocoder.yudao.framework.jackson.core.databind.TimestampLocalDateTimeDeserializer;
import cn.iocoder.yudao.framework.jackson.core.databind.TimestampLocalDateTimeSerializer;
import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer;
import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;

View File

@@ -2,10 +2,12 @@ package cn.iocoder.yudao.framework.web.core.handler;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -30,6 +32,7 @@ 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.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import java.time.LocalDateTime;
import java.util.Map;
@@ -87,6 +90,9 @@ public class GlobalExceptionHandler {
if (ex instanceof NoHandlerFoundException) {
return noHandlerFoundExceptionHandler((NoHandlerFoundException) ex);
}
if (ex instanceof NoResourceFoundException) {
return noResourceFoundExceptionHandler(request, (NoResourceFoundException) ex);
}
if (ex instanceof HttpRequestMethodNotSupportedException) {
return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex);
}
@@ -176,6 +182,15 @@ public class GlobalExceptionHandler {
return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getRequestURL()));
}
/**
* 处理 SpringMVC 请求地址不存在
*/
@ExceptionHandler(NoResourceFoundException.class)
private CommonResult<?> noResourceFoundExceptionHandler(HttpServletRequest req, NoResourceFoundException ex) {
log.warn("[noResourceFoundExceptionHandler]", ex);
return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getResourcePath()));
}
/**
* 处理 SpringMVC 请求方法不正确
*
@@ -206,9 +221,20 @@ public class GlobalExceptionHandler {
*/
@ExceptionHandler(value = ServiceException.class)
public CommonResult<?> serviceExceptionHandler(ServiceException ex) {
// 不包含的时候,才进行打印,避免 ex 堆栈过多
if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) {
// 不包含的时候,才进行打印,避免 ex 堆栈过多
log.info("[serviceExceptionHandler]", ex);
// 即使打印,也只打印第一层 StackTraceElement并且使用 warn 在控制台输出,更容易看到
try {
StackTraceElement[] stackTraces = ex.getStackTrace();
for (StackTraceElement stackTrace : stackTraces) {
if (ObjUtil.notEqual(stackTrace.getClassName(), ServiceExceptionUtil.class.getName())) {
log.warn("[serviceExceptionHandler]\n\t{}", stackTrace);
break;
}
}
} catch (Exception ignored) {
// 忽略日志,避免影响主流程
}
}
return CommonResult.error(ex.getCode(), ex.getMessage());
}
@@ -288,45 +314,51 @@ public class GlobalExceptionHandler {
}
// 1. 数据报表
if (message.contains("report_")) {
log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://doc.iocoder.cn/report/ 开启]");
log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[报表模块 yudao-module-report - 表结构未导入][参考 https://doc.iocoder.cn/report/ 开启]");
"[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
}
// 2. 工作流
if (message.contains("bpm_")) {
log.error("[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://doc.iocoder.cn/bpm/ 开启]");
log.error("[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://doc.iocoder.cn/bpm/ 开启]");
"[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
}
// 3. 微信公众号
if (message.contains("mp_")) {
log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://doc.iocoder.cn/mp/build/ 开启]");
log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[微信公众号 yudao-module-mp - 表结构未导入][参考 https://doc.iocoder.cn/mp/build/ 开启]");
"[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
}
// 4. 商城系统
if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) {
log.error("[商城系统 yudao-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]");
log.error("[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[商城系统 yudao-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]");
"[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
}
// 5. ERP 系统
if (message.contains("erp_")) {
log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]");
log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]");
"[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
}
// 6. CRM 系统
if (message.contains("crm_")) {
log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://doc.iocoder.cn/crm/build/ 开启]");
log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://doc.iocoder.cn/crm/build/ 开启]");
"[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
}
// 7. 支付平台
if (message.contains("pay_")) {
log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]");
log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]");
"[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
}
// 8. AI 大模型
if (message.contains("ai_")) {
log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
}
return null;
}