Commit ba390c67f8502d7826f50dcb4201d5d31f9e13f8

Authored by YunaiV
2 parents 07d09992 a64df8e2

Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/ai

Showing 23 changed files with 149 additions and 126 deletions
README.md
... ... @@ -308,26 +308,26 @@
308 308  
309 309 | 框架 | 说明 | 版本 | 学习指南 |
310 310 |---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
311   -| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.4.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
  311 +| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.5.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
312 312 | [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
313   -| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.23 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
314   -| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.7 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
  313 +| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.27 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
  314 +| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.12 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
315 315 | [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 4.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
316 316 | [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | |
317   -| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.32.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
318   -| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.1.10 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
319   -| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
320   -| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.1 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
  317 +| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.35.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
  318 +| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.2.9 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
  319 +| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
  320 +| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.2 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
321 321 | [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) |
322   -| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
323   -| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.3.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
324   -| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
325   -| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
326   -| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.17.1 | |
  322 +| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
  323 +| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.8.9 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
  324 +| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
  325 +| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
  326 +| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.30.14 | |
327 327 | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.6.3 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
328   -| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.34 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
329   -| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.10.1 | - |
330   -| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.7.0 | - |
  328 +| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.38 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
  329 +| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.12.2 | - |
  330 +| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.17.0 | - |
331 331  
332 332 ## 🐷 演示图
333 333  
... ...
... ... @@ -37,12 +37,12 @@
37 37 <java.version>17</java.version>
38 38 <maven.compiler.source>${java.version}</maven.compiler.source>
39 39 <maven.compiler.target>${java.version}</maven.compiler.target>
40   - <maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version>
  40 + <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
41 41 <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
42   - <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
  42 + <flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
43 43 <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) -->
44 44 <lombok.version>1.18.38</lombok.version>
45   - <spring.boot.version>3.5.4</spring.boot.version>
  45 + <spring.boot.version>3.5.5</spring.boot.version>
46 46 <mapstruct.version>1.6.3</mapstruct.version>
47 47 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
48 48 </properties>
... ...
yudao-dependencies/pom.xml
... ... @@ -15,53 +15,54 @@
15 15  
16 16 <properties>
17 17 <revision>2025.08-SNAPSHOT</revision>
18   - <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
  18 + <flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
19 19 <!-- 统一依赖管理 -->
20   - <spring.boot.version>3.5.4</spring.boot.version>
  20 + <spring.boot.version>3.5.5</spring.boot.version>
21 21 <!-- Web 相关 -->
22   - <springdoc.version>2.8.9</springdoc.version>
  22 + <springdoc.version>2.8.11</springdoc.version>
23 23 <knife4j.version>4.5.0</knife4j.version>
24 24 <!-- DB 相关 -->
25   - <druid.version>1.2.24</druid.version>
  25 + <druid.version>1.2.27</druid.version>
26 26 <mybatis.version>3.5.19</mybatis.version>
27 27 <mybatis-plus.version>3.5.12</mybatis-plus.version>
28 28 <mybatis-plus-join.version>1.5.4</mybatis-plus-join.version>
29 29 <dynamic-datasource.version>4.3.1</dynamic-datasource.version>
30 30 <easy-trans.version>3.0.6</easy-trans.version>
31   - <redisson.version>3.41.0</redisson.version>
  31 + <redisson.version>3.51.0</redisson.version>
32 32 <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
33 33 <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
34 34 <opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
35   - <taos.version>3.3.3</taos.version>
  35 + <taos.version>3.7.3</taos.version>
36 36 <!-- 消息队列 -->
37   - <rocketmq-spring.version>2.3.2</rocketmq-spring.version>
  37 + <rocketmq-spring.version>2.3.4</rocketmq-spring.version>
38 38 <!-- 服务保障相关 -->
39 39 <lock4j.version>2.2.7</lock4j.version>
40 40 <!-- 监控相关 -->
41   - <skywalking.version>9.0.0</skywalking.version>
  41 + <skywalking.version>9.5.0</skywalking.version>
42 42 <spring-boot-admin.version>3.5.2</spring-boot-admin.version>
43 43 <opentracing.version>0.33.0</opentracing.version>
44 44 <!-- Test 测试相关 -->
45 45 <podam.version>8.0.2.RELEASE</podam.version>
46   - <jedis-mock.version>1.1.8</jedis-mock.version>
  46 + <jedis-mock.version>1.1.11</jedis-mock.version>
47 47 <mockito-inline.version>5.2.0</mockito-inline.version>
48 48 <!-- Bpm 工作流相关 -->
49 49 <flowable.version>7.0.1</flowable.version>
50 50 <!-- 工具类相关 -->
51 51 <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
52   - <jsoup.version>1.18.3</jsoup.version>
  52 + <jsoup.version>1.21.2</jsoup.version>
53 53 <lombok.version>1.18.38</lombok.version>
54 54 <mapstruct.version>1.6.3</mapstruct.version>
55   - <hutool-5.version>5.8.35</hutool-5.version>
56   - <hutool-6.version>6.0.0-M19</hutool-6.version>
57   - <fastexcel.version>1.2.0</fastexcel.version>
  55 + <hutool-5.version>5.8.40</hutool-5.version>
  56 + <hutool-6.version>6.0.0-M22</hutool-6.version>
  57 + <fastexcel.version>1.3.0</fastexcel.version>
58 58 <velocity.version>2.4.1</velocity.version>
59 59 <fastjson.version>1.2.83</fastjson.version>
60 60 <guava.version>33.4.8-jre</guava.version>
61 61 <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
62 62 <commons-net.version>3.11.1</commons-net.version>
  63 + <commons-lang3.version>3.18.0</commons-lang3.version>
63 64 <jsch.version>0.1.55</jsch.version>
64   - <tika-core.version>3.1.0</tika-core.version>
  65 + <tika-core.version>3.2.2</tika-core.version>
65 66 <ip2region.version>2.7.0</ip2region.version>
66 67 <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
67 68 <netty.version>4.2.4.Final</netty.version>
... ... @@ -74,7 +75,7 @@
74 75 <justauth-starter.version>1.4.0</justauth-starter.version>
75 76 <jimureport.version>2.0.0</jimureport.version>
76 77 <jimubi.version>1.9.5</jimubi.version>
77   - <weixin-java.version>4.7.5.B</weixin-java.version>
  78 + <weixin-java.version>4.7.7-20250808.182223</weixin-java.version>
78 79 </properties>
79 80  
80 81 <dependencyManagement>
... ... @@ -524,7 +525,6 @@
524 525 <artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->
525 526 <version>${commons-net.version}</version>
526 527 </dependency>
527   -
528 528 <dependency>
529 529 <groupId>com.jcraft</groupId>
530 530 <artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->
... ... @@ -532,6 +532,12 @@
532 532 </dependency>
533 533  
534 534 <dependency>
  535 + <groupId>org.apache.commons</groupId>
  536 + <artifactId>commons-lang3</artifactId>
  537 + <version>${commons-lang3.version}</version> <!-- 解决 CVE-2025-48924 漏洞 -->
  538 + </dependency>
  539 +
  540 + <dependency>
535 541 <groupId>com.anji-plus</groupId>
536 542 <artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
537 543 <version>${anji-plus-captcha.version}</version>
... ...
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 {
75 75 if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) {
76 76 return false;
77 77 }
78   - // 如果添加了 @TenantIgnore 注解,显然也不忽略租户
  78 + // 如果添加了 @TenantIgnore 注解,则忽略租户
79 79 TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class);
80 80 return tenantIgnore != null;
81 81 }
... ...
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 {
13 13  
14 14 @Override
15 15 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
16   - if (bean instanceof RabbitTemplate) {
17   - RabbitTemplate rabbitTemplate = (RabbitTemplate) bean;
  16 + if (bean instanceof RabbitTemplate rabbitTemplate) {
18 17 rabbitTemplate.addBeforePublishPostProcessors(new TenantRabbitMQMessagePostProcessor());
19 18 }
20 19 return bean;
21 20 }
22 21  
23   -}
24 22 \ No newline at end of file
  23 +}
... ...
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 {
18 18  
19 19 @Override
20 20 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
21   - if (bean instanceof DefaultRocketMQListenerContainer) {
22   - DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean;
  21 + if (bean instanceof DefaultRocketMQListenerContainer container) {
23 22 initTenantConsumer(container.getConsumer());
24   - } else if (bean instanceof RocketMQTemplate) {
25   - RocketMQTemplate template = (RocketMQTemplate) bean;
  23 + } else if (bean instanceof RocketMQTemplate template) {
26 24 initTenantProducer(template.getProducer());
27 25 }
28 26 return bean;
... ... @@ -50,4 +48,4 @@ public class TenantRocketMQInitializer implements BeanPostProcessor {
50 48 consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook());
51 49 }
52 50  
53   -}
54 51 \ No newline at end of file
  52 +}
... ...
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 {
23 23 @Override
24 24 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
25 25 // 处理 ThreadPoolTaskExecutor
26   - if (bean instanceof ThreadPoolTaskExecutor) {
27   - ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean;
  26 + if (bean instanceof ThreadPoolTaskExecutor executor) {
28 27 executor.setTaskDecorator(TtlRunnable::get);
29 28 return executor;
30 29 }
31 30 // 处理 SimpleAsyncTaskExecutor
32 31 // 参考 https://t.zsxq.com/CBoks 增加
33   - if (bean instanceof SimpleAsyncTaskExecutor) {
34   - SimpleAsyncTaskExecutor executor = (SimpleAsyncTaskExecutor) bean;
  32 + if (bean instanceof SimpleAsyncTaskExecutor executor) {
35 33 executor.setTaskDecorator(TtlRunnable::get);
36 34 return executor;
37 35 }
... ...
yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java
1 1 package cn.iocoder.yudao.framework.tracer.config;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
4   -import cn.iocoder.yudao.framework.tracer.core.aop.BizTraceAspect;
5 4 import cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter;
6   -import io.opentracing.Tracer;
7   -import io.opentracing.util.GlobalTracer;
8   -import org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer;
9 5 import org.springframework.boot.autoconfigure.AutoConfiguration;
10 6 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
11 7 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
... ... @@ -20,32 +16,28 @@ import org.springframework.context.annotation.Bean;
20 16 */
21 17 @AutoConfiguration
22 18 @ConditionalOnClass(name = {
23   - "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer",
24   - "io.opentracing.Tracer",
  19 + "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer", // 来自 apm-toolkit-opentracing.jar
  20 +// "io.opentracing.Tracer", // 来自 opentracing-api.jar
25 21 "jakarta.servlet.Filter"
26 22 })
27 23 @EnableConfigurationProperties(TracerProperties.class)
28 24 @ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true)
29 25 public class YudaoTracerAutoConfiguration {
30 26  
31   - @Bean
32   - public TracerProperties bizTracerProperties() {
33   - return new TracerProperties();
34   - }
35   -
36   - @Bean
37   - public BizTraceAspect bizTracingAop() {
38   - return new BizTraceAspect(tracer());
39   - }
40   -
41   - @Bean
42   - public Tracer tracer() {
43   - // 创建 SkywalkingTracer 对象
44   - SkywalkingTracer tracer = new SkywalkingTracer();
45   - // 设置为 GlobalTracer 的追踪器
46   - GlobalTracer.registerIfAbsent(tracer);
47   - return tracer;
48   - }
  27 + // TODO @芋艿:skywalking 不兼容最新的 opentracing 版本。同时,opentracing 也停止了维护,尬住了!后续换 opentelemetry 即可!
  28 +// @Bean
  29 +// public BizTraceAspect bizTracingAop() {
  30 +// return new BizTraceAspect(tracer());
  31 +// }
  32 +//
  33 +// @Bean
  34 +// public Tracer tracer() {
  35 +// // 创建 SkywalkingTracer 对象
  36 +// SkywalkingTracer tracer = new SkywalkingTracer();
  37 +// // 设置为 GlobalTracer 的追踪器
  38 +// GlobalTracer.registerIfAbsent(tracer);
  39 +// return tracer;
  40 +// }
49 41  
50 42 /**
51 43 * 创建 TraceFilter 过滤器,响应 header 设置 traceId
... ...
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.*;
10 10 import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
11 11 import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache;
12 12 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
  13 +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
13 14 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
14 15 import org.apache.ibatis.annotations.Mapper;
15 16 import org.mybatis.spring.annotation.MapperScan;
... ... @@ -42,6 +43,7 @@ public class YudaoMybatisAutoConfiguration {
42 43 public MybatisPlusInterceptor mybatisPlusInterceptor() {
43 44 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
44 45 mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件
  46 + mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 拦截没有指定条件的 update 和 delete 语句
45 47 return mybatisPlusInterceptor;
46 48 }
47 49  
... ...
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 {
19 19  
20 20 @Override
21 21 public void insertFill(MetaObject metaObject) {
22   - if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
23   - BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
  22 + if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO baseDO) {
24 23  
25 24 LocalDateTime current = LocalDateTime.now();
26 25 // 创建时间为空,则以当前时间为插入时间
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java
1 1 package cn.iocoder.yudao.framework.jackson.config;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4 3 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 4 import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer;
6 5 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
7 6 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
  7 +import com.fasterxml.jackson.databind.Module;
8 8 import com.fasterxml.jackson.databind.ObjectMapper;
9 9 import com.fasterxml.jackson.databind.module.SimpleModule;
10 10 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
... ... @@ -13,39 +13,65 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
13 13 import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
14 14 import lombok.extern.slf4j.Slf4j;
15 15 import org.springframework.boot.autoconfigure.AutoConfiguration;
  16 +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
  17 +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
16 18 import org.springframework.context.annotation.Bean;
17 19  
18 20 import java.time.LocalDate;
19 21 import java.time.LocalDateTime;
20 22 import java.time.LocalTime;
21   -import java.util.List;
22 23  
23   -@AutoConfiguration
  24 +@AutoConfiguration(after = JacksonAutoConfiguration.class)
24 25 @Slf4j
25 26 public class YudaoJacksonAutoConfiguration {
26 27  
  28 + /**
  29 + * 从 Builder 源头定制(关键:使用 *ByType,避免 handledType 要求)
  30 + */
27 31 @Bean
28   - @SuppressWarnings("InstantiationOfUtilityClass")
29   - public JsonUtils jsonUtils(List<ObjectMapper> objectMappers) {
30   - // 1.1 创建 SimpleModule 对象
31   - SimpleModule simpleModule = new SimpleModule();
32   - simpleModule
33   - // 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型
34   - .addSerializer(Long.class, NumberSerializer.INSTANCE)
35   - .addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
36   - .addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
37   - .addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
38   - .addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
39   - .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
40   - // 新增 LocalDateTime 序列化、反序列化规则,使用 Long 时间戳
41   - .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)
42   - .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
43   - // 1.2 注册到 objectMapper
44   - objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule));
  32 + public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() {
  33 + return builder -> builder
  34 + // Long -> Number
  35 + .serializerByType(Long.class, NumberSerializer.INSTANCE)
  36 + .serializerByType(Long.TYPE, NumberSerializer.INSTANCE)
  37 + // LocalDate / LocalTime
  38 + .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE)
  39 + .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE)
  40 + .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE)
  41 + .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE)
  42 + // LocalDateTime < - > EpochMillis
  43 + .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)
  44 + .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
  45 + }
45 46  
46   - // 2. 设置 objectMapper 到 JsonUtils
47   - JsonUtils.init(CollUtil.getFirst(objectMappers));
48   - log.info("[init][初始化 JsonUtils 成功]");
  47 + /**
  48 + * 以 Bean 形式暴露 Module(Boot 会自动注册到所有 ObjectMapper)
  49 + */
  50 + @Bean
  51 + public Module timestampSupportModuleBean() {
  52 + SimpleModule m = new SimpleModule("TimestampSupportModule");
  53 + // Long -> Number,避免前端精度丢失
  54 + m.addSerializer(Long.class, NumberSerializer.INSTANCE);
  55 + m.addSerializer(Long.TYPE, NumberSerializer.INSTANCE);
  56 + // LocalDate / LocalTime
  57 + m.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE);
  58 + m.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
  59 + m.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);
  60 + m.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);
  61 + // LocalDateTime < - > EpochMillis
  62 + m.addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE);
  63 + m.addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
  64 + return m;
  65 + }
  66 +
  67 + /**
  68 + * 初始化全局 JsonUtils,直接使用主 ObjectMapper
  69 + */
  70 + @Bean
  71 + @SuppressWarnings("InstantiationOfUtilityClass")
  72 + public JsonUtils jsonUtils(ObjectMapper objectMapper) {
  73 + JsonUtils.init(objectMapper);
  74 + log.debug("[init][初始化 JsonUtils 成功]");
49 75 return new JsonUtils();
50 76 }
51 77  
... ...
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 {
184 184 @ExceptionHandler(HttpMessageNotReadableException.class)
185 185 public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) {
186 186 log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex);
187   - if (ex.getCause() instanceof InvalidFormatException) {
188   - InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause();
  187 + if (ex.getCause() instanceof InvalidFormatException invalidFormatException) {
189 188 return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", invalidFormatException.getValue()));
190 189 }
191 190 if (StrUtil.startWith(ex.getMessage(), "Required request body is missing")) {
... ...
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 {
146 146  
147 147 public static HttpServletRequest getRequest() {
148 148 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
149   - if (!(requestAttributes instanceof ServletRequestAttributes)) {
  149 + if (!(requestAttributes instanceof ServletRequestAttributes servletRequestAttributes)) {
150 150 return null;
151 151 }
152   - ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
153 152 return servletRequestAttributes.getRequest();
154 153 }
155 154  
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java
... ... @@ -29,6 +29,11 @@ public class BpmProcessInstanceStatusEvent extends ApplicationEvent {
29 29 @NotNull(message = "流程实例的状态不能为空")
30 30 private Integer status;
31 31 /**
  32 + * 流程实例结束的原因
  33 + */
  34 + private String reason;
  35 +
  36 + /**
32 37 * 流程实例对应的业务标识
33 38 * 例如说,请假
34 39 */
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
... ... @@ -119,8 +119,9 @@ public interface BpmProcessInstanceConvert {
119 119 @Mapping(source = "from.id", target = "to.id", ignore = true)
120 120 void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
121 121  
122   - default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {
123   - return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
  122 + default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance,
  123 + Integer status, String reason) {
  124 + return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status).setReason(reason)
124 125 .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
125 126 }
126 127  
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
... ... @@ -960,7 +960,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
960 960  
961 961 // 3. 发送流程实例的状态事件
962 962 processInstanceEventPublisher.sendProcessInstanceResultEvent(
963   - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status));
  963 + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status, reason));
964 964  
965 965 // 4. 流程后置通知
966 966 if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
... ...
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 {
166 166 public void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId) {
167 167 // 循环收回
168 168 for (Long couponId : giveCouponIds) {
  169 + // couponId 为空或 0 则跳过
  170 + if (null == couponId || couponId <= 0) {
  171 + continue;
  172 + }
169 173 try {
170 174 getSelf().invalidateCoupon(couponId, userId);
171 175 } catch (Exception e) {
... ...
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;
8 8 import com.anji.captcha.util.AESUtil;
9 9 import com.anji.captcha.util.ImageUtils;
10 10 import com.anji.captcha.util.RandomUtils;
11   -import org.apache.commons.lang3.StringUtils;
12 11 import cn.hutool.core.util.RandomUtil;
  12 +import org.apache.commons.lang3.Strings;
13 13  
14 14 import java.awt.*;
15 15 import java.awt.geom.AffineTransform;
... ... @@ -82,7 +82,7 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
82 82  
83 83 // 用户输入的验证码(CaptchaVO 中 没有预留字段,暂时用 pointJson 无需加解密)
84 84 String userCode = captchaVO.getPointJson();
85   - if (!StringUtils.equalsIgnoreCase(code, userCode)) {
  85 + if (!Strings.CI.equals(code, userCode)) {
86 86 afterValidateFail(captchaVO);
87 87 return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_COORDINATE_ERROR);
88 88 }
... ... @@ -209,4 +209,4 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
209 209 return RandomUtil.randomString(CHARACTERS, length);
210 210 }
211 211  
212   -}
213 212 \ No newline at end of file
  213 +}
... ...
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java
... ... @@ -77,8 +77,8 @@ public class AuthRequestFactory {
77 77 extendList = extend.getConfig()
78 78 .keySet()
79 79 .stream()
80   - .filter(x -> names.contains(x.toUpperCase()))
81 80 .map(String::toUpperCase)
  81 + .filter(names::contains)
82 82 .collect(Collectors.toList());
83 83 }
84 84  
... ... @@ -318,4 +318,4 @@ public class AuthRequestFactory {
318 318 .proxy(new Proxy(Proxy.Type.valueOf(proxyConfig.getType()), new InetSocketAddress(proxyConfig.getHostname(), proxyConfig.getPort())))
319 319 .build());
320 320 }
321   -}
322 321 \ No newline at end of file
  322 +}
... ...
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 {
114 114  
115 115 @VisibleForTesting
116 116 Integer convertSmsTemplateAuditStatus(Integer templateStatus) {
117   - switch (templateStatus) {
118   - case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
119   - case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
120   - case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus();
121   - default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
122   - }
  117 + return switch (templateStatus) {
  118 + case 0 -> SmsTemplateAuditStatusEnum.CHECKING.getStatus();
  119 + case 1 -> SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
  120 + case 2 -> SmsTemplateAuditStatusEnum.FAIL.getStatus();
  121 + default -> throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
  122 + };
123 123 }
124 124  
125 125 /**
... ... @@ -153,8 +153,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
153 153 StringBuilder canonicalHeaders = new StringBuilder(); // 构造请求头,多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起
154 154 StringBuilder signedHeadersBuilder = new StringBuilder(); // 已签名消息头列表,多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔
155 155 headers.entrySet().stream().filter(entry -> entry.getKey().toLowerCase().startsWith("x-acs-")
156   - || entry.getKey().equalsIgnoreCase("host")
157   - || entry.getKey().equalsIgnoreCase("content-type"))
  156 + || "host".equalsIgnoreCase(entry.getKey())
  157 + || "content-type".equalsIgnoreCase(entry.getKey()))
158 158 .sorted(Map.Entry.comparingByKey()).forEach(entry -> {
159 159 String lowerKey = entry.getKey().toLowerCase();
160 160 canonicalHeaders.append(lowerKey).append(":").append(String.valueOf(entry.getValue()).trim()).append("\n");
... ... @@ -189,10 +189,10 @@ public class AliyunSmsClient extends AbstractSmsClient {
189 189 @SneakyThrows
190 190 private static String percentCode(String str) {
191 191 Assert.notNull(str, "str 不能为空");
192   - return URLEncoder.encode(str, StandardCharsets.UTF_8.name())
  192 + return URLEncoder.encode(str, StandardCharsets.UTF_8)
193 193 .replace("+", "%20") // 加号 "+" 被替换为 "%20"
194 194 .replace("*", "%2A") // 星号 "*" 被替换为 "%2A"
195 195 .replace("%7E", "~"); // 波浪号 "%7E" 被替换为 "~"
196 196 }
197 197  
198   -}
199 198 \ No newline at end of file
  199 +}
... ...
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;
6 6 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
7 7 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
8 8 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
  9 +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
9 10 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
10 11 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
11 12 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
... ... @@ -97,6 +98,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
97 98 }
98 99  
99 100 @Override
  101 + @DataPermission(enable = false)
100 102 public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
101 103 // 校验验证码
102 104 validateCaptcha(reqVO);
... ...
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java
... ... @@ -16,7 +16,6 @@ import java.util.List;
16 16 import static cn.hutool.core.util.RandomUtil.randomEle;
17 17 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
18 18 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
19   -import static java.util.Collections.emptyList;
20 19 import static org.junit.jupiter.api.Assertions.*;
21 20 import static org.mockito.ArgumentMatchers.eq;
22 21 import static org.mockito.Mockito.when;
... ... @@ -136,13 +135,6 @@ public class OAuth2GrantServiceImplTest extends BaseMockitoUnitTest {
136 135 }
137 136  
138 137 @Test
139   - public void testGrantClientCredentials() {
140   - assertThrows(UnsupportedOperationException.class,
141   - () -> oauth2GrantService.grantClientCredentials(randomString(), emptyList()),
142   - "暂时不支持 client_credentials 授权模式");
143   - }
144   -
145   - @Test
146 138 public void testRevokeToken_clientIdError() {
147 139 // 准备参数
148 140 String clientId = randomString();
... ...
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java
... ... @@ -236,6 +236,7 @@ public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest {
236 236 public void testCheckAccessToken_refreshToken() {
237 237 // mock 数据(访问令牌)
238 238 OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class)
  239 + .setUserId(0L)
239 240 .setExpiresTime(LocalDateTime.now().plusDays(1));
240 241 oauth2RefreshTokenMapper.insert(refreshTokenDO);
241 242 // 准备参数
... ...