diff --git a/README.md b/README.md index efdb44c7fc..c0ecdf6f49 100644 --- a/README.md +++ b/README.md @@ -308,26 +308,26 @@ | 框架 | 说明 | 版本 | 学习指南 | |---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------| -| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.4.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | +| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.5.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | | [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | | -| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.23 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | -| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.7 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | +| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.27 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | +| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.12 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | | [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 4.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | | [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | | -| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.32.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) | -| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.1.10 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) | -| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) | -| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.1 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) | +| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.35.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) | +| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.2.9 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) | +| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) | +| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.2 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) | | [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) | -| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) | -| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.3.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) | -| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) | -| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | -| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.17.1 | | +| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) | +| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.8.9 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) | +| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) | +| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | +| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.30.14 | | | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.6.3 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) | -| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.34 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | -| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.10.1 | - | -| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.7.0 | - | +| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.38 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | +| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.12.2 | - | +| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.17.0 | - | ## 🐷 演示图 diff --git a/pom.xml b/pom.xml index 621b67a4af..e1317e1240 100644 --- a/pom.xml +++ b/pom.xml @@ -37,12 +37,12 @@ 17 ${java.version} ${java.version} - 3.2.2 + 3.5.3 3.14.0 - 1.6.0 + 1.7.2 1.18.38 - 3.5.4 + 3.5.5 1.6.3 UTF-8 diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 317e8b331f..c2cf9eda99 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -15,53 +15,54 @@ 2025.08-SNAPSHOT - 1.6.0 + 1.7.2 - 3.5.4 + 3.5.5 - 2.8.9 + 2.8.11 4.5.0 - 1.2.24 + 1.2.27 3.5.19 3.5.12 1.5.4 4.3.1 3.0.6 - 3.41.0 + 3.51.0 8.1.3.140 8.6.0 5.1.0 - 3.3.3 + 3.7.3 - 2.3.2 + 2.3.4 2.2.7 - 9.0.0 + 9.5.0 3.5.2 0.33.0 8.0.2.RELEASE - 1.1.8 + 1.1.11 5.2.0 7.0.1 1.4.0 - 1.18.3 + 1.21.2 1.18.38 1.6.3 - 5.8.35 - 6.0.0-M19 - 1.2.0 + 5.8.40 + 6.0.0-M22 + 1.3.0 2.4.1 1.2.83 33.4.8-jre 2.14.5 3.11.1 + 3.18.0 0.1.55 - 3.1.0 + 3.2.2 2.7.0 3.0.6 4.2.4.Final @@ -74,7 +75,7 @@ 1.4.0 2.0.0 1.9.5 - 4.7.5.B + 4.7.7-20250808.182223 @@ -524,13 +525,18 @@ commons-net ${commons-net.version} - com.jcraft jsch ${jsch.version} + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + com.anji-plus captcha-spring-boot-starter diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java index 47e5df004c..11f0a4b4c3 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java @@ -75,7 +75,7 @@ public class TenantDatabaseInterceptor implements TenantLineHandler { if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) { return false; } - // 如果添加了 @TenantIgnore 注解,显然也不忽略租户 + // 如果添加了 @TenantIgnore 注解,则忽略租户 TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class); return tenantIgnore != null; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java index b856ce9542..f18ec1acf6 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java @@ -13,11 +13,10 @@ public class TenantRabbitMQInitializer implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof RabbitTemplate) { - RabbitTemplate rabbitTemplate = (RabbitTemplate) bean; + if (bean instanceof RabbitTemplate rabbitTemplate) { rabbitTemplate.addBeforePublishPostProcessors(new TenantRabbitMQMessagePostProcessor()); } return bean; } -} \ No newline at end of file +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java index 7f12ac5205..6c3247df14 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java @@ -18,11 +18,9 @@ public class TenantRocketMQInitializer implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof DefaultRocketMQListenerContainer) { - DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean; + if (bean instanceof DefaultRocketMQListenerContainer container) { initTenantConsumer(container.getConsumer()); - } else if (bean instanceof RocketMQTemplate) { - RocketMQTemplate template = (RocketMQTemplate) bean; + } else if (bean instanceof RocketMQTemplate template) { initTenantProducer(template.getProducer()); } return bean; @@ -50,4 +48,4 @@ public class TenantRocketMQInitializer implements BeanPostProcessor { consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook()); } -} \ No newline at end of file +} diff --git a/yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java index 4b08210971..5f8a4944d3 100644 --- a/yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java @@ -23,15 +23,13 @@ public class YudaoAsyncAutoConfiguration { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 处理 ThreadPoolTaskExecutor - if (bean instanceof ThreadPoolTaskExecutor) { - ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean; + if (bean instanceof ThreadPoolTaskExecutor executor) { executor.setTaskDecorator(TtlRunnable::get); return executor; } // 处理 SimpleAsyncTaskExecutor // 参考 https://t.zsxq.com/CBoks 增加 - if (bean instanceof SimpleAsyncTaskExecutor) { - SimpleAsyncTaskExecutor executor = (SimpleAsyncTaskExecutor) bean; + if (bean instanceof SimpleAsyncTaskExecutor executor) { executor.setTaskDecorator(TtlRunnable::get); return executor; } diff --git a/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java index 14a8a974f2..596b516757 100644 --- a/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java @@ -1,11 +1,7 @@ package cn.iocoder.yudao.framework.tracer.config; import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; -import cn.iocoder.yudao.framework.tracer.core.aop.BizTraceAspect; import cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter; -import io.opentracing.Tracer; -import io.opentracing.util.GlobalTracer; -import org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -20,32 +16,28 @@ import org.springframework.context.annotation.Bean; */ @AutoConfiguration @ConditionalOnClass(name = { - "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer", - "io.opentracing.Tracer", + "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer", // 来自 apm-toolkit-opentracing.jar +// "io.opentracing.Tracer", // 来自 opentracing-api.jar "jakarta.servlet.Filter" }) @EnableConfigurationProperties(TracerProperties.class) @ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true) public class YudaoTracerAutoConfiguration { - @Bean - public TracerProperties bizTracerProperties() { - return new TracerProperties(); - } - - @Bean - public BizTraceAspect bizTracingAop() { - return new BizTraceAspect(tracer()); - } - - @Bean - public Tracer tracer() { - // 创建 SkywalkingTracer 对象 - SkywalkingTracer tracer = new SkywalkingTracer(); - // 设置为 GlobalTracer 的追踪器 - GlobalTracer.registerIfAbsent(tracer); - return tracer; - } + // TODO @芋艿:skywalking 不兼容最新的 opentracing 版本。同时,opentracing 也停止了维护,尬住了!后续换 opentelemetry 即可! +// @Bean +// public BizTraceAspect bizTracingAop() { +// return new BizTraceAspect(tracer()); +// } +// +// @Bean +// public Tracer tracer() { +// // 创建 SkywalkingTracer 对象 +// SkywalkingTracer tracer = new SkywalkingTracer(); +// // 设置为 GlobalTracer 的追踪器 +// GlobalTracer.registerIfAbsent(tracer); +// return tracer; +// } /** * 创建 TraceFilter 过滤器,响应 header 设置 traceId diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java index ab2992184f..96872b6438 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java @@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.incrementer.*; import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal; import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.apache.ibatis.annotations.Mapper; import org.mybatis.spring.annotation.MapperScan; @@ -42,6 +43,7 @@ public class YudaoMybatisAutoConfiguration { public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件 + mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 拦截没有指定条件的 update 和 delete 语句 return mybatisPlusInterceptor; } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java index 3c35999109..6596bb0eef 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java @@ -19,8 +19,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { - if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) { - BaseDO baseDO = (BaseDO) metaObject.getOriginalObject(); + if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO baseDO) { LocalDateTime current = LocalDateTime.now(); // 创建时间为空,则以当前时间为插入时间 diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java index c62f0a0300..280f8da349 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java @@ -1,10 +1,10 @@ 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.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.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; @@ -13,39 +13,65 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.context.annotation.Bean; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.util.List; -@AutoConfiguration +@AutoConfiguration(after = JacksonAutoConfiguration.class) @Slf4j public class YudaoJacksonAutoConfiguration { + /** + * 从 Builder 源头定制(关键:使用 *ByType,避免 handledType 要求) + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() { + return builder -> builder + // Long -> Number + .serializerByType(Long.class, NumberSerializer.INSTANCE) + .serializerByType(Long.TYPE, NumberSerializer.INSTANCE) + // LocalDate / LocalTime + .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE) + .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE) + .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE) + .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE) + // LocalDateTime < - > EpochMillis + .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) + .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); + } + + /** + * 以 Bean 形式暴露 Module(Boot 会自动注册到所有 ObjectMapper) + */ + @Bean + public Module timestampSupportModuleBean() { + SimpleModule m = new SimpleModule("TimestampSupportModule"); + // Long -> Number,避免前端精度丢失 + m.addSerializer(Long.class, NumberSerializer.INSTANCE); + m.addSerializer(Long.TYPE, NumberSerializer.INSTANCE); + // LocalDate / LocalTime + m.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE); + m.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE); + m.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE); + m.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE); + // LocalDateTime < - > EpochMillis + m.addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE); + m.addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); + return m; + } + + /** + * 初始化全局 JsonUtils,直接使用主 ObjectMapper + */ @Bean @SuppressWarnings("InstantiationOfUtilityClass") - public JsonUtils jsonUtils(List objectMappers) { - // 1.1 创建 SimpleModule 对象 - SimpleModule simpleModule = new SimpleModule(); - simpleModule - // 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型 - .addSerializer(Long.class, NumberSerializer.INSTANCE) - .addSerializer(Long.TYPE, NumberSerializer.INSTANCE) - .addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE) - .addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE) - .addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE) - .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE) - // 新增 LocalDateTime 序列化、反序列化规则,使用 Long 时间戳 - .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) - .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); - // 1.2 注册到 objectMapper - objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule)); - - // 2. 设置 objectMapper 到 JsonUtils - JsonUtils.init(CollUtil.getFirst(objectMappers)); - log.info("[init][初始化 JsonUtils 成功]"); + public JsonUtils jsonUtils(ObjectMapper objectMapper) { + JsonUtils.init(objectMapper); + log.debug("[init][初始化 JsonUtils 成功]"); return new JsonUtils(); } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index 3f3a871a13..5147999ad6 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -184,8 +184,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(HttpMessageNotReadableException.class) public CommonResult methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) { log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex); - if (ex.getCause() instanceof InvalidFormatException) { - InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause(); + if (ex.getCause() instanceof InvalidFormatException invalidFormatException) { return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", invalidFormatException.getValue())); } if (StrUtil.startWith(ex.getMessage(), "Required request body is missing")) { diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java index b4aa301676..d45cfaa8b1 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java @@ -146,10 +146,9 @@ public class WebFrameworkUtils { public static HttpServletRequest getRequest() { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - if (!(requestAttributes instanceof ServletRequestAttributes)) { + if (!(requestAttributes instanceof ServletRequestAttributes servletRequestAttributes)) { return null; } - ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes; return servletRequestAttributes.getRequest(); } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java index 5522d01b38..1340e89f80 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java @@ -28,6 +28,11 @@ public class BpmProcessInstanceStatusEvent extends ApplicationEvent { */ @NotNull(message = "流程实例的状态不能为空") private Integer status; + /** + * 流程实例结束的原因 + */ + private String reason; + /** * 流程实例对应的业务标识 * 例如说,请假 diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java index c82414b532..460115286b 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -119,8 +119,9 @@ public interface BpmProcessInstanceConvert { @Mapping(source = "from.id", target = "to.id", ignore = true) void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); - default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) { - return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) + default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, + Integer status, String reason) { + return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status).setReason(reason) .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index b4a871e145..877d47e88d 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -960,7 +960,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status, reason)); // 4. 流程后置通知 if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { diff --git a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java index e175807503..fee8a4851f 100644 --- a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java @@ -166,6 +166,10 @@ public class CouponServiceImpl implements CouponService { public void invalidateCouponsByAdmin(List giveCouponIds, Long userId) { // 循环收回 for (Long couponId : giveCouponIds) { + // couponId 为空或 0 则跳过 + if (null == couponId || couponId <= 0) { + continue; + } try { getSelf().invalidateCoupon(couponId, userId); } catch (Exception e) { diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/PictureWordCaptchaServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/PictureWordCaptchaServiceImpl.java index ed15a0277b..e5f7741907 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/PictureWordCaptchaServiceImpl.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/PictureWordCaptchaServiceImpl.java @@ -8,8 +8,8 @@ import com.anji.captcha.service.impl.CaptchaServiceFactory; import com.anji.captcha.util.AESUtil; import com.anji.captcha.util.ImageUtils; import com.anji.captcha.util.RandomUtils; -import org.apache.commons.lang3.StringUtils; import cn.hutool.core.util.RandomUtil; +import org.apache.commons.lang3.Strings; import java.awt.*; import java.awt.geom.AffineTransform; @@ -82,7 +82,7 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService { // 用户输入的验证码(CaptchaVO 中 没有预留字段,暂时用 pointJson 无需加解密) String userCode = captchaVO.getPointJson(); - if (!StringUtils.equalsIgnoreCase(code, userCode)) { + if (!Strings.CI.equals(code, userCode)) { afterValidateFail(captchaVO); return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_COORDINATE_ERROR); } @@ -209,4 +209,4 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService { return RandomUtil.randomString(CHARACTERS, length); } -} \ No newline at end of file +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java index 4ae8b78c6c..53ceabcec6 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java @@ -77,8 +77,8 @@ public class AuthRequestFactory { extendList = extend.getConfig() .keySet() .stream() - .filter(x -> names.contains(x.toUpperCase())) .map(String::toUpperCase) + .filter(names::contains) .collect(Collectors.toList()); } @@ -318,4 +318,4 @@ public class AuthRequestFactory { .proxy(new Proxy(Proxy.Type.valueOf(proxyConfig.getType()), new InetSocketAddress(proxyConfig.getHostname(), proxyConfig.getPort()))) .build()); } -} \ No newline at end of file +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java index d4ed95d9b2..538efb966b 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java @@ -114,12 +114,12 @@ public class AliyunSmsClient extends AbstractSmsClient { @VisibleForTesting Integer convertSmsTemplateAuditStatus(Integer templateStatus) { - switch (templateStatus) { - case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); - case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); - case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); - default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); - } + return switch (templateStatus) { + case 0 -> SmsTemplateAuditStatusEnum.CHECKING.getStatus(); + case 1 -> SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); + case 2 -> SmsTemplateAuditStatusEnum.FAIL.getStatus(); + default -> throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); + }; } /** @@ -153,8 +153,8 @@ public class AliyunSmsClient extends AbstractSmsClient { StringBuilder canonicalHeaders = new StringBuilder(); // 构造请求头,多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起 StringBuilder signedHeadersBuilder = new StringBuilder(); // 已签名消息头列表,多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔 headers.entrySet().stream().filter(entry -> entry.getKey().toLowerCase().startsWith("x-acs-") - || entry.getKey().equalsIgnoreCase("host") - || entry.getKey().equalsIgnoreCase("content-type")) + || "host".equalsIgnoreCase(entry.getKey()) + || "content-type".equalsIgnoreCase(entry.getKey())) .sorted(Map.Entry.comparingByKey()).forEach(entry -> { String lowerKey = entry.getKey().toLowerCase(); canonicalHeaders.append(lowerKey).append(":").append(String.valueOf(entry.getValue()).trim()).append("\n"); @@ -189,10 +189,10 @@ public class AliyunSmsClient extends AbstractSmsClient { @SneakyThrows private static String percentCode(String str) { Assert.notNull(str, "str 不能为空"); - return URLEncoder.encode(str, StandardCharsets.UTF_8.name()) + return URLEncoder.encode(str, StandardCharsets.UTF_8) .replace("+", "%20") // 加号 "+" 被替换为 "%20" .replace("*", "%2A") // 星号 "*" 被替换为 "%2A" .replace("%7E", "~"); // 波浪号 "%7E" 被替换为 "~" } -} \ No newline at end of file +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index 94e589de5e..22d159a8ce 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; @@ -97,6 +98,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { } @Override + @DataPermission(enable = false) public AuthLoginRespVO login(AuthLoginReqVO reqVO) { // 校验验证码 validateCaptcha(reqVO); diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java index 52c722831f..944086671b 100644 --- a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java @@ -16,7 +16,6 @@ import java.util.List; import static cn.hutool.core.util.RandomUtil.randomEle; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @@ -135,13 +134,6 @@ public class OAuth2GrantServiceImplTest extends BaseMockitoUnitTest { refreshToken, clientId)); } - @Test - public void testGrantClientCredentials() { - assertThrows(UnsupportedOperationException.class, - () -> oauth2GrantService.grantClientCredentials(randomString(), emptyList()), - "暂时不支持 client_credentials 授权模式"); - } - @Test public void testRevokeToken_clientIdError() { // 准备参数 diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java index 0aad4446a1..36b69c75dd 100644 --- a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java @@ -236,6 +236,7 @@ public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest { public void testCheckAccessToken_refreshToken() { // mock 数据(访问令牌) OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) + .setUserId(0L) .setExpiresTime(LocalDateTime.now().plusDays(1)); oauth2RefreshTokenMapper.insert(refreshTokenDO); // 准备参数