Commit 787de1be94f21d945d55c2de28ad4c0db6692fdb
Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/mall_product
# Conflicts: # sql/mysql/mall.sql # sql/mysql/ruoyi-vue-pro.sql # yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateReqVO.java # yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java # yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
Showing
189 changed files
with
1533 additions
and
2012 deletions
Too many changes.
To preserve performance only 100 of 189 files are displayed.
README.md
| ... | ... | @@ -256,7 +256,7 @@ _鍓嶇鍩轰簬 crmeb uniapp 缁忚繃鎺堟潈閲嶆瀯锛屼紭鍖栦唬鐮佸疄鐜帮紝鎺ュ叆鑺嬮 |
| 256 | 256 | |
| 257 | 257 | | 妗嗘灦 | 璇存槑 | 鐗堟湰 | 瀛︿範鎸囧崡 | |
| 258 | 258 | |---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------| |
| 259 | -| [Spring Boot](https://spring.io/projects/spring-boot) | 搴旂敤寮鍙戞鏋 | 2.7.16 | [鏂囨。](https://github.com/YunaiV/SpringBoot-Labs) | | |
| 259 | +| [Spring Boot](https://spring.io/projects/spring-boot) | 搴旂敤寮鍙戞鏋 | 2.7.17 | [鏂囨。](https://github.com/YunaiV/SpringBoot-Labs) | | |
| 260 | 260 | | [MySQL](https://www.mysql.com/cn/) | 鏁版嵁搴撴湇鍔″櫒 | 5.7 / 8.0+ | | |
| 261 | 261 | | [Druid](https://github.com/alibaba/druid) | JDBC 杩炴帴姹犮佺洃鎺х粍浠 | 1.2.19 | [鏂囨。](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | |
| 262 | 262 | | [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 澧炲己宸ュ叿鍖 | 3.5.3.2 | [鏂囨。](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | | ... | ... |
pom.xml
| ... | ... | @@ -13,16 +13,16 @@ |
| 13 | 13 | <!-- Server 主项目 --> |
| 14 | 14 | <module>yudao-server</module> |
| 15 | 15 | <!-- 各种 module 拓展 --> |
| 16 | - <module>yudao-module-member</module> | |
| 17 | 16 | <module>yudao-module-system</module> |
| 18 | 17 | <module>yudao-module-infra</module> |
| 19 | - <!-- <module>yudao-module-bpm</module>--> | |
| 18 | +<!-- <module>yudao-module-member</module>--> | |
| 19 | +<!-- <module>yudao-module-bpm</module>--> | |
| 20 | 20 | <!-- <module>yudao-module-report</module>--> |
| 21 | 21 | <!-- <module>yudao-module-mp</module>--> |
| 22 | - <module>yudao-module-pay</module> | |
| 23 | - <module>yudao-module-mall</module> | |
| 22 | +<!-- <module>yudao-module-pay</module>--> | |
| 23 | +<!-- <module>yudao-module-mall</module>--> | |
| 24 | 24 | <!-- 示例项目 --> |
| 25 | - <module>yudao-example</module> | |
| 25 | +<!-- <module>yudao-example</module>--> | |
| 26 | 26 | </modules> |
| 27 | 27 | |
| 28 | 28 | <name>${project.artifactId}</name> |
| ... | ... | @@ -30,7 +30,7 @@ |
| 30 | 30 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 31 | 31 | |
| 32 | 32 | <properties> |
| 33 | - <revision>1.8.2-snapshot</revision> | |
| 33 | + <revision>1.8.3-snapshot</revision> | |
| 34 | 34 | <!-- Maven 相关 --> |
| 35 | 35 | <java.version>1.8</java.version> |
| 36 | 36 | <maven.compiler.source>${java.version}</maven.compiler.source> |
| ... | ... | @@ -40,7 +40,7 @@ |
| 40 | 40 | <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version> |
| 41 | 41 | <!-- 看看咋放到 bom 里 --> |
| 42 | 42 | <lombok.version>1.18.30</lombok.version> |
| 43 | - <spring.boot.version>2.7.16</spring.boot.version> | |
| 43 | + <spring.boot.version>2.7.17</spring.boot.version> | |
| 44 | 44 | <mapstruct.version>1.5.5.Final</mapstruct.version> |
| 45 | 45 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 46 | 46 | </properties> | ... | ... |
yudao-dependencies/pom.xml
| ... | ... | @@ -14,28 +14,30 @@ |
| 14 | 14 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 15 | 15 | |
| 16 | 16 | <properties> |
| 17 | - <revision>1.8.2-snapshot</revision> | |
| 17 | + <revision>1.8.3-snapshot</revision> | |
| 18 | 18 | <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version> |
| 19 | 19 | <!-- 统一依赖管理 --> |
| 20 | - <spring.boot.version>2.7.16</spring.boot.version> | |
| 20 | + <spring.boot.version>2.7.17</spring.boot.version> | |
| 21 | 21 | <!-- Web 相关 --> |
| 22 | 22 | <springdoc.version>1.6.15</springdoc.version> |
| 23 | 23 | <knife4j.version>4.3.0</knife4j.version> |
| 24 | 24 | <servlet.versoin>2.5</servlet.versoin> |
| 25 | 25 | <!-- DB 相关 --> |
| 26 | - <druid.version>1.2.19</druid.version> | |
| 27 | - <mybatis-plus.version>3.5.3.2</mybatis-plus.version> | |
| 28 | - <mybatis-plus-generator.version>3.5.3.2</mybatis-plus-generator.version> | |
| 26 | + <druid.version>1.2.20</druid.version> | |
| 27 | + <mybatis-plus.version>3.5.4</mybatis-plus.version> | |
| 28 | + <mybatis-plus-generator.version>3.5.4</mybatis-plus-generator.version> | |
| 29 | 29 | <dynamic-datasource.version>3.6.1</dynamic-datasource.version> |
| 30 | - <mybatis-plus-join.version>1.4.6</mybatis-plus-join.version> | |
| 30 | + <mybatis-plus-join.version>1.4.7</mybatis-plus-join.version> | |
| 31 | 31 | <redisson.version>3.18.0</redisson.version> |
| 32 | - <dm8.jdbc.version>8.1.2.141</dm8.jdbc.version> | |
| 32 | + <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version> | |
| 33 | + <!-- 消息队列 --> | |
| 34 | + <rocketmq-spring.version>2.2.3</rocketmq-spring.version> | |
| 33 | 35 | <!-- 服务保障相关 --> |
| 34 | - <lock4j.version>2.2.3</lock4j.version> | |
| 36 | + <lock4j.version>2.2.5</lock4j.version> | |
| 35 | 37 | <resilience4j.version>1.7.1</resilience4j.version> |
| 36 | 38 | <!-- 监控相关 --> |
| 37 | 39 | <skywalking.version>8.12.0</skywalking.version> |
| 38 | - <spring-boot-admin.version>2.7.10</spring-boot-admin.version> | |
| 40 | + <spring-boot-admin.version>2.7.11</spring-boot-admin.version> | |
| 39 | 41 | <opentracing.version>0.33.0</opentracing.version> |
| 40 | 42 | <!-- Test 测试相关 --> |
| 41 | 43 | <podam.version>7.2.11.RELEASE</podam.version> |
| ... | ... | @@ -44,8 +46,8 @@ |
| 44 | 46 | <!-- Bpm 工作流相关 --> |
| 45 | 47 | <flowable.version>6.8.0</flowable.version> |
| 46 | 48 | <!-- 工具类相关 --> |
| 47 | - <captcha-plus.version>1.0.8</captcha-plus.version> | |
| 48 | - <jsoup.version>1.16.1</jsoup.version> | |
| 49 | + <captcha-plus.version>1.0.10</captcha-plus.version> | |
| 50 | + <jsoup.version>1.16.2</jsoup.version> | |
| 49 | 51 | <lombok.version>1.18.30</lombok.version> |
| 50 | 52 | <mapstruct.version>1.5.5.Final</mapstruct.version> |
| 51 | 53 | <hutool.version>5.8.22</hutool.version> |
| ... | ... | @@ -53,10 +55,10 @@ |
| 53 | 55 | <velocity.version>2.3</velocity.version> |
| 54 | 56 | <screw.version>1.0.5</screw.version> |
| 55 | 57 | <fastjson.version>1.2.83</fastjson.version> |
| 56 | - <guava.version>32.1.2-jre</guava.version> | |
| 58 | + <guava.version>32.1.3-jre</guava.version> | |
| 57 | 59 | <guice.version>5.1.0</guice.version> |
| 58 | 60 | <transmittable-thread-local.version>2.14.2</transmittable-thread-local.version> |
| 59 | - <commons-net.version>3.9.0</commons-net.version> | |
| 61 | + <commons-net.version>3.10.0</commons-net.version> | |
| 60 | 62 | <jsch.version>0.1.55</jsch.version> |
| 61 | 63 | <tika-core.version>2.7.0</tika-core.version> |
| 62 | 64 | <ip2region.version>2.7.0</ip2region.version> |
| ... | ... | @@ -67,8 +69,8 @@ |
| 67 | 69 | <minio.version>8.5.6</minio.version> |
| 68 | 70 | <aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version> |
| 69 | 71 | <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version> |
| 70 | - <tencentcloud-sdk-java.version>3.1.853</tencentcloud-sdk-java.version> | |
| 71 | - <justauth.version>1.0.5</justauth.version> | |
| 72 | + <tencentcloud-sdk-java.version>3.1.880</tencentcloud-sdk-java.version> | |
| 73 | + <justauth.version>1.0.7</justauth.version> | |
| 72 | 74 | <jimureport.version>1.6.1</jimureport.version> |
| 73 | 75 | <xercesImpl.version>2.12.2</xercesImpl.version> |
| 74 | 76 | <weixin-java.version>4.5.0</weixin-java.version> |
| ... | ... | @@ -98,11 +100,6 @@ |
| 98 | 100 | </dependency> |
| 99 | 101 | <dependency> |
| 100 | 102 | <groupId>cn.iocoder.boot</groupId> |
| 101 | - <artifactId>yudao-spring-boot-starter-biz-trade</artifactId> | |
| 102 | - <version>${revision}</version> | |
| 103 | - </dependency> | |
| 104 | - <dependency> | |
| 105 | - <groupId>cn.iocoder.boot</groupId> | |
| 106 | 103 | <artifactId>yudao-spring-boot-starter-biz-dict</artifactId> |
| 107 | 104 | <version>${revision}</version> |
| 108 | 105 | </dependency> |
| ... | ... | @@ -260,6 +257,12 @@ |
| 260 | 257 | <version>${revision}</version> |
| 261 | 258 | </dependency> |
| 262 | 259 | |
| 260 | + <dependency> | |
| 261 | + <groupId>org.apache.rocketmq</groupId> | |
| 262 | + <artifactId>rocketmq-spring-boot-starter</artifactId> | |
| 263 | + <version>${rocketmq-spring.version}</version> | |
| 264 | + </dependency> | |
| 265 | + | |
| 263 | 266 | <!-- 服务保障相关 --> |
| 264 | 267 | <dependency> |
| 265 | 268 | <groupId>cn.iocoder.boot</groupId> | ... | ... |
yudao-example/yudao-sso-demo-by-code/pom.xml
| ... | ... | @@ -21,7 +21,7 @@ |
| 21 | 21 | <maven.compiler.target>8</maven.compiler.target> |
| 22 | 22 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 23 | 23 | <!-- 统一依赖管理 --> |
| 24 | - <spring.boot.version>2.7.16</spring.boot.version> | |
| 24 | + <spring.boot.version>2.7.17</spring.boot.version> | |
| 25 | 25 | </properties> |
| 26 | 26 | |
| 27 | 27 | <dependencyManagement> | ... | ... |
yudao-example/yudao-sso-demo-by-password/pom.xml
| ... | ... | @@ -21,7 +21,7 @@ |
| 21 | 21 | <maven.compiler.target>8</maven.compiler.target> |
| 22 | 22 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 23 | 23 | <!-- 统一依赖管理 --> |
| 24 | - <spring.boot.version>2.7.16</spring.boot.version> | |
| 24 | + <spring.boot.version>2.7.17</spring.boot.version> | |
| 25 | 25 | </properties> |
| 26 | 26 | |
| 27 | 27 | <dependencyManagement> | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java
| ... | ... | @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | import lombok.NoArgsConstructor; |
| 6 | 6 | |
| 7 | +import java.io.Serializable; | |
| 8 | + | |
| 7 | 9 | /** |
| 8 | 10 | * Key Value 的键值对 |
| 9 | 11 | * |
| ... | ... | @@ -12,7 +14,7 @@ import lombok.NoArgsConstructor; |
| 12 | 14 | @Data |
| 13 | 15 | @NoArgsConstructor |
| 14 | 16 | @AllArgsConstructor |
| 15 | -public class KeyValue<K, V> { | |
| 17 | +public class KeyValue<K, V> implements Serializable { | |
| 16 | 18 | |
| 17 | 19 | private K key; |
| 18 | 20 | private V value; | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
| ... | ... | @@ -8,6 +8,7 @@ import com.google.common.collect.ImmutableMap; |
| 8 | 8 | import java.util.*; |
| 9 | 9 | import java.util.function.*; |
| 10 | 10 | import java.util.stream.Collectors; |
| 11 | +import java.util.stream.Stream; | |
| 11 | 12 | |
| 12 | 13 | import static java.util.Arrays.asList; |
| 13 | 14 | |
| ... | ... | @@ -271,4 +272,20 @@ public class CollectionUtils { |
| 271 | 272 | return deptId == null ? Collections.emptyList() : Collections.singleton(deptId); |
| 272 | 273 | } |
| 273 | 274 | |
| 275 | + public static <T, U> List<U> convertListByFlatMap(Collection<T> from, | |
| 276 | + Function<T, ? extends Stream<? extends U>> func) { | |
| 277 | + if (CollUtil.isEmpty(from)) { | |
| 278 | + return new ArrayList<>(); | |
| 279 | + } | |
| 280 | + return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); | |
| 281 | + } | |
| 282 | + | |
| 283 | + public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from, | |
| 284 | + Function<T, ? extends Stream<? extends U>> func) { | |
| 285 | + if (CollUtil.isEmpty(from)) { | |
| 286 | + return new HashSet<>(); | |
| 287 | + } | |
| 288 | + return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); | |
| 289 | + } | |
| 290 | + | |
| 274 | 291 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeConfiguration.java renamed to yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeAutoConfiguration.java
| ... | ... | @@ -21,7 +21,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; |
| 21 | 21 | @ConditionalOnProperty(prefix = "yudao.error-code", value = "enable", matchIfMissing = true) // 允许使用 yudao.error-code.enable=false 禁用访问日志 |
| 22 | 22 | @EnableConfigurationProperties(ErrorCodeProperties.class) |
| 23 | 23 | @EnableScheduling // 开启调度任务的功能,因为 ErrorCodeRemoteLoader 通过定时刷新错误码 |
| 24 | -public class YudaoErrorCodeConfiguration { | |
| 24 | +public class YudaoErrorCodeAutoConfiguration { | |
| 25 | 25 | |
| 26 | 26 | @Bean |
| 27 | 27 | public ErrorCodeAutoGenerator errorCodeAutoGenerator(@Value("${spring.application.name}") String applicationName, | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml
| ... | ... | @@ -48,6 +48,22 @@ |
| 48 | 48 | <dependency> |
| 49 | 49 | <groupId>cn.iocoder.boot</groupId> |
| 50 | 50 | <artifactId>yudao-spring-boot-starter-mq</artifactId> |
| 51 | + <optional>true</optional> | |
| 52 | + </dependency> | |
| 53 | + <dependency> | |
| 54 | + <groupId>org.springframework.kafka</groupId> | |
| 55 | + <artifactId>spring-kafka</artifactId> | |
| 56 | + <optional>true</optional> | |
| 57 | + </dependency> | |
| 58 | + <dependency> | |
| 59 | + <groupId>org.springframework.amqp</groupId> | |
| 60 | + <artifactId>spring-rabbit</artifactId> | |
| 61 | + <optional>true</optional> | |
| 62 | + </dependency> | |
| 63 | + <dependency> | |
| 64 | + <groupId>org.apache.rocketmq</groupId> | |
| 65 | + <artifactId>rocketmq-spring-boot-starter</artifactId> | |
| 66 | + <optional>true</optional> | |
| 51 | 67 | </dependency> |
| 52 | 68 | |
| 53 | 69 | <!-- Test 测试相关 --> | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
| ... | ... | @@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; |
| 6 | 6 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; |
| 7 | 7 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; |
| 8 | 8 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect; |
| 9 | -import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor; | |
| 9 | +import cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer; | |
| 10 | +import cn.iocoder.yudao.framework.tenant.core.mq.redis.TenantRedisMessageInterceptor; | |
| 11 | +import cn.iocoder.yudao.framework.tenant.core.mq.rocketmq.TenantRocketMQInitializer; | |
| 10 | 12 | import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; |
| 11 | 13 | import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; |
| 12 | 14 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; |
| ... | ... | @@ -18,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.tenant.TenantApi; |
| 18 | 20 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| 19 | 21 | import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; |
| 20 | 22 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 23 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | |
| 21 | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 22 | 25 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 23 | 26 | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| ... | ... | @@ -92,6 +95,18 @@ public class YudaoTenantAutoConfiguration { |
| 92 | 95 | return new TenantRedisMessageInterceptor(); |
| 93 | 96 | } |
| 94 | 97 | |
| 98 | + @Bean | |
| 99 | + @ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") | |
| 100 | + public TenantRabbitMQInitializer tenantRabbitMQInitializer() { | |
| 101 | + return new TenantRabbitMQInitializer(); | |
| 102 | + } | |
| 103 | + | |
| 104 | + @Bean | |
| 105 | + @ConditionalOnClass(name = "org.apache.rocketmq.spring.core.RocketMQTemplate") | |
| 106 | + public TenantRocketMQInitializer tenantRocketMQInitializer() { | |
| 107 | + return new TenantRocketMQInitializer(); | |
| 108 | + } | |
| 109 | + | |
| 95 | 110 | // ========== Job ========== |
| 96 | 111 | |
| 97 | 112 | @Bean | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.kafka; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | +import lombok.extern.slf4j.Slf4j; | |
| 5 | +import org.springframework.boot.SpringApplication; | |
| 6 | +import org.springframework.boot.env.EnvironmentPostProcessor; | |
| 7 | +import org.springframework.core.env.ConfigurableEnvironment; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 多租户的 Kafka 的 {@link EnvironmentPostProcessor} 实现类 | |
| 11 | + * | |
| 12 | + * Kafka Producer 发送消息时,增加 {@link TenantKafkaProducerInterceptor} 拦截器 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +@Slf4j | |
| 17 | +public class TenantKafkaEnvironmentPostProcessor implements EnvironmentPostProcessor { | |
| 18 | + | |
| 19 | + private static final String PROPERTY_KEY_INTERCEPTOR_CLASSES = "spring.kafka.producer.properties.interceptor.classes"; | |
| 20 | + | |
| 21 | + @Override | |
| 22 | + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { | |
| 23 | + // 添加 TenantKafkaProducerInterceptor 拦截器 | |
| 24 | + try { | |
| 25 | + String value = environment.getProperty(PROPERTY_KEY_INTERCEPTOR_CLASSES); | |
| 26 | + if (StrUtil.isEmpty(value)) { | |
| 27 | + value = TenantKafkaProducerInterceptor.class.getName(); | |
| 28 | + } else { | |
| 29 | + value += "," + TenantKafkaProducerInterceptor.class.getName(); | |
| 30 | + } | |
| 31 | + environment.getSystemProperties().put(PROPERTY_KEY_INTERCEPTOR_CLASSES, value); | |
| 32 | + } catch (NoClassDefFoundError ignore) { | |
| 33 | + // 如果触发 NoClassDefFoundError 异常,说明 TenantKafkaProducerInterceptor 类不存在,即没引入 kafka-spring 依赖 | |
| 34 | + } | |
| 35 | + } | |
| 36 | + | |
| 37 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.kafka; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ReflectUtil; | |
| 4 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 5 | +import org.apache.kafka.clients.producer.ProducerInterceptor; | |
| 6 | +import org.apache.kafka.clients.producer.ProducerRecord; | |
| 7 | +import org.apache.kafka.clients.producer.RecordMetadata; | |
| 8 | +import org.apache.kafka.common.header.Headers; | |
| 9 | +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; | |
| 10 | + | |
| 11 | +import java.util.Map; | |
| 12 | + | |
| 13 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * Kafka 消息队列的多租户 {@link ProducerInterceptor} 实现类 | |
| 17 | + * | |
| 18 | + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 | |
| 19 | + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 | |
| 20 | + * | |
| 21 | + * @author 芋道源码 | |
| 22 | + */ | |
| 23 | +public class TenantKafkaProducerInterceptor implements ProducerInterceptor<Object, Object> { | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + public ProducerRecord<Object, Object> onSend(ProducerRecord<Object, Object> record) { | |
| 27 | + Long tenantId = TenantContextHolder.getTenantId(); | |
| 28 | + if (tenantId != null) { | |
| 29 | + Headers headers = (Headers) ReflectUtil.getFieldValue(record, "headers"); // private 属性,没有 get 方法,智能反射 | |
| 30 | + headers.add(HEADER_TENANT_ID, tenantId.toString().getBytes()); | |
| 31 | + } | |
| 32 | + return record; | |
| 33 | + } | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + public void onAcknowledgement(RecordMetadata metadata, Exception exception) { | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public void close() { | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Override | |
| 44 | + public void configure(Map<String, ?> configs) { | |
| 45 | + } | |
| 46 | + | |
| 47 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq; | |
| 2 | + | |
| 3 | +import org.springframework.amqp.rabbit.core.RabbitTemplate; | |
| 4 | +import org.springframework.beans.BeansException; | |
| 5 | +import org.springframework.beans.factory.config.BeanPostProcessor; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 多租户的 RabbitMQ 初始化器 | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +public class TenantRabbitMQInitializer implements BeanPostProcessor { | |
| 13 | + | |
| 14 | + @Override | |
| 15 | + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { | |
| 16 | + if (bean instanceof RabbitTemplate) { | |
| 17 | + RabbitTemplate rabbitTemplate = (RabbitTemplate) bean; | |
| 18 | + rabbitTemplate.addBeforePublishPostProcessors(new TenantRabbitMQMessagePostProcessor()); | |
| 19 | + } | |
| 20 | + return bean; | |
| 21 | + } | |
| 22 | + | |
| 23 | +} | |
| 0 | 24 | \ No newline at end of file | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 4 | +import org.apache.kafka.clients.producer.ProducerInterceptor; | |
| 5 | +import org.springframework.amqp.AmqpException; | |
| 6 | +import org.springframework.amqp.core.Message; | |
| 7 | +import org.springframework.amqp.core.MessagePostProcessor; | |
| 8 | +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * RabbitMQ 消息队列的多租户 {@link ProducerInterceptor} 实现类 | |
| 14 | + * | |
| 15 | + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 | |
| 16 | + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 | |
| 17 | + * | |
| 18 | + * @author 芋道源码 | |
| 19 | + */ | |
| 20 | +public class TenantRabbitMQMessagePostProcessor implements MessagePostProcessor { | |
| 21 | + | |
| 22 | + @Override | |
| 23 | + public Message postProcessMessage(Message message) throws AmqpException { | |
| 24 | + Long tenantId = TenantContextHolder.getTenantId(); | |
| 25 | + if (tenantId != null) { | |
| 26 | + message.getMessageProperties().getHeaders().put(HEADER_TENANT_ID, tenantId); | |
| 27 | + } | |
| 28 | + return message; | |
| 29 | + } | |
| 30 | + | |
| 31 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java renamed to yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java
| 1 | -package cn.iocoder.yudao.framework.tenant.core.mq; | |
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.redis; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | |
| 5 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | |
| 4 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | |
| 6 | 6 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 7 | 7 | |
| 8 | 8 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; | |
| 2 | + | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 5 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 6 | +import org.apache.rocketmq.client.hook.ConsumeMessageContext; | |
| 7 | +import org.apache.rocketmq.client.hook.ConsumeMessageHook; | |
| 8 | +import org.apache.rocketmq.common.message.MessageExt; | |
| 9 | +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; | |
| 10 | + | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * RocketMQ 消息队列的多租户 {@link ConsumeMessageHook} 实现类 | |
| 17 | + * | |
| 18 | + * Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 | |
| 19 | + * | |
| 20 | + * @author 芋道源码 | |
| 21 | + */ | |
| 22 | +public class TenantRocketMQConsumeMessageHook implements ConsumeMessageHook { | |
| 23 | + | |
| 24 | + @Override | |
| 25 | + public String hookName() { | |
| 26 | + return getClass().getSimpleName(); | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + public void consumeMessageBefore(ConsumeMessageContext context) { | |
| 31 | + // 校验,消息必须是单条,不然设置租户可能不正确 | |
| 32 | + List<MessageExt> messages = context.getMsgList(); | |
| 33 | + Assert.isTrue(messages.size() == 1, "消息条数({})不正确", messages.size()); | |
| 34 | + // 设置租户编号 | |
| 35 | + String tenantId = messages.get(0).getUserProperty(HEADER_TENANT_ID); | |
| 36 | + if (StrUtil.isNotEmpty(tenantId)) { | |
| 37 | + TenantContextHolder.setTenantId(Long.parseLong(tenantId)); | |
| 38 | + } | |
| 39 | + } | |
| 40 | + | |
| 41 | + @Override | |
| 42 | + public void consumeMessageAfter(ConsumeMessageContext context) { | |
| 43 | + TenantContextHolder.clear(); | |
| 44 | + } | |
| 45 | + | |
| 46 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; | |
| 2 | + | |
| 3 | +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; | |
| 4 | +import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; | |
| 5 | +import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; | |
| 6 | +import org.apache.rocketmq.client.producer.DefaultMQProducer; | |
| 7 | +import org.apache.rocketmq.spring.core.RocketMQTemplate; | |
| 8 | +import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; | |
| 9 | +import org.springframework.beans.BeansException; | |
| 10 | +import org.springframework.beans.factory.config.BeanPostProcessor; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 多租户的 RocketMQ 初始化器 | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +public class TenantRocketMQInitializer implements BeanPostProcessor { | |
| 18 | + | |
| 19 | + @Override | |
| 20 | + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { | |
| 21 | + if (bean instanceof DefaultRocketMQListenerContainer) { | |
| 22 | + DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean; | |
| 23 | + initTenantConsumer(container.getConsumer()); | |
| 24 | + } else if (bean instanceof RocketMQTemplate) { | |
| 25 | + RocketMQTemplate template = (RocketMQTemplate) bean; | |
| 26 | + initTenantProducer(template.getProducer()); | |
| 27 | + } | |
| 28 | + return bean; | |
| 29 | + } | |
| 30 | + | |
| 31 | + private void initTenantProducer(DefaultMQProducer producer) { | |
| 32 | + if (producer == null) { | |
| 33 | + return; | |
| 34 | + } | |
| 35 | + DefaultMQProducerImpl producerImpl = producer.getDefaultMQProducerImpl(); | |
| 36 | + if (producerImpl == null) { | |
| 37 | + return; | |
| 38 | + } | |
| 39 | + producerImpl.registerSendMessageHook(new TenantRocketMQSendMessageHook()); | |
| 40 | + } | |
| 41 | + | |
| 42 | + private void initTenantConsumer(DefaultMQPushConsumer consumer) { | |
| 43 | + if (consumer == null) { | |
| 44 | + return; | |
| 45 | + } | |
| 46 | + DefaultMQPushConsumerImpl consumerImpl = consumer.getDefaultMQPushConsumerImpl(); | |
| 47 | + if (consumerImpl == null) { | |
| 48 | + return; | |
| 49 | + } | |
| 50 | + consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook()); | |
| 51 | + } | |
| 52 | + | |
| 53 | +} | |
| 0 | 54 | \ No newline at end of file | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 4 | +import org.apache.rocketmq.client.hook.SendMessageContext; | |
| 5 | +import org.apache.rocketmq.client.hook.SendMessageHook; | |
| 6 | + | |
| 7 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * RocketMQ 消息队列的多租户 {@link SendMessageHook} 实现类 | |
| 11 | + * | |
| 12 | + * Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +public class TenantRocketMQSendMessageHook implements SendMessageHook { | |
| 17 | + | |
| 18 | + @Override | |
| 19 | + public String hookName() { | |
| 20 | + return getClass().getSimpleName(); | |
| 21 | + } | |
| 22 | + | |
| 23 | + @Override | |
| 24 | + public void sendMessageBefore(SendMessageContext sendMessageContext) { | |
| 25 | + Long tenantId = TenantContextHolder.getTenantId(); | |
| 26 | + if (tenantId == null) { | |
| 27 | + return; | |
| 28 | + } | |
| 29 | + sendMessageContext.getMessage().putUserProperty(HEADER_TENANT_ID, tenantId.toString()); | |
| 30 | + } | |
| 31 | + | |
| 32 | + @Override | |
| 33 | + public void sendMessageAfter(SendMessageContext sendMessageContext) { | |
| 34 | + } | |
| 35 | + | |
| 36 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
0 → 100644
| 1 | +/* | |
| 2 | + * Copyright 2002-2021 the original author or authors. | |
| 3 | + * | |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 | + * you may not use this file except in compliance with the License. | |
| 6 | + * You may obtain a copy of the License at | |
| 7 | + * | |
| 8 | + * https://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | + * | |
| 10 | + * Unless required by applicable law or agreed to in writing, software | |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 | + * See the License for the specific language governing permissions and | |
| 14 | + * limitations under the License. | |
| 15 | + */ | |
| 16 | + | |
| 17 | +package org.springframework.messaging.handler.invocation; | |
| 18 | + | |
| 19 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 20 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | |
| 21 | +import org.springframework.core.DefaultParameterNameDiscoverer; | |
| 22 | +import org.springframework.core.MethodParameter; | |
| 23 | +import org.springframework.core.ParameterNameDiscoverer; | |
| 24 | +import org.springframework.core.ResolvableType; | |
| 25 | +import org.springframework.lang.Nullable; | |
| 26 | +import org.springframework.messaging.Message; | |
| 27 | +import org.springframework.messaging.handler.HandlerMethod; | |
| 28 | +import org.springframework.util.ObjectUtils; | |
| 29 | + | |
| 30 | +import java.lang.reflect.InvocationTargetException; | |
| 31 | +import java.lang.reflect.Method; | |
| 32 | +import java.lang.reflect.Type; | |
| 33 | +import java.util.Arrays; | |
| 34 | + | |
| 35 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * Extension of {@link HandlerMethod} that invokes the underlying method with | |
| 39 | + * argument values resolved from the current HTTP request through a list of | |
| 40 | + * {@link HandlerMethodArgumentResolver}. | |
| 41 | + * | |
| 42 | + * 针对 rabbitmq-spring 和 kafka-spring,不存在合适的拓展点,可以实现 Consumer 消费前,读取 Header 中的 tenant-id 设置到 {@link TenantContextHolder} 中 | |
| 43 | + * TODO 芋艿:持续跟进,看看有没新的拓展点 | |
| 44 | + * | |
| 45 | + * @author Rossen Stoyanchev | |
| 46 | + * @author Juergen Hoeller | |
| 47 | + * @since 4.0 | |
| 48 | + */ | |
| 49 | +public class InvocableHandlerMethod extends HandlerMethod { | |
| 50 | + | |
| 51 | + private static final Object[] EMPTY_ARGS = new Object[0]; | |
| 52 | + | |
| 53 | + private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); | |
| 54 | + | |
| 55 | + private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * Create an instance from a {@code HandlerMethod}. | |
| 59 | + */ | |
| 60 | + public InvocableHandlerMethod(HandlerMethod handlerMethod) { | |
| 61 | + super(handlerMethod); | |
| 62 | + } | |
| 63 | + | |
| 64 | + /** | |
| 65 | + * Create an instance from a bean instance and a method. | |
| 66 | + */ | |
| 67 | + public InvocableHandlerMethod(Object bean, Method method) { | |
| 68 | + super(bean, method); | |
| 69 | + } | |
| 70 | + | |
| 71 | + /** | |
| 72 | + * Construct a new handler method with the given bean instance, method name and parameters. | |
| 73 | + * @param bean the object bean | |
| 74 | + * @param methodName the method name | |
| 75 | + * @param parameterTypes the method parameter types | |
| 76 | + * @throws NoSuchMethodException when the method cannot be found | |
| 77 | + */ | |
| 78 | + public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) | |
| 79 | + throws NoSuchMethodException { | |
| 80 | + | |
| 81 | + super(bean, methodName, parameterTypes); | |
| 82 | + } | |
| 83 | + | |
| 84 | + /** | |
| 85 | + * Set {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} to use for resolving method argument values. | |
| 86 | + */ | |
| 87 | + public void setMessageMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) { | |
| 88 | + this.resolvers = argumentResolvers; | |
| 89 | + } | |
| 90 | + | |
| 91 | + /** | |
| 92 | + * Set the ParameterNameDiscoverer for resolving parameter names when needed | |
| 93 | + * (e.g. default request attribute name). | |
| 94 | + * <p>Default is a {@link DefaultParameterNameDiscoverer}. | |
| 95 | + */ | |
| 96 | + public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { | |
| 97 | + this.parameterNameDiscoverer = parameterNameDiscoverer; | |
| 98 | + } | |
| 99 | + | |
| 100 | + /** | |
| 101 | + * Invoke the method after resolving its argument values in the context of the given message. | |
| 102 | + * <p>Argument values are commonly resolved through | |
| 103 | + * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. | |
| 104 | + * The {@code providedArgs} parameter however may supply argument values to be used directly, | |
| 105 | + * i.e. without argument resolution. | |
| 106 | + * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the | |
| 107 | + * resolved arguments. | |
| 108 | + * @param message the current message being processed | |
| 109 | + * @param providedArgs "given" arguments matched by type, not resolved | |
| 110 | + * @return the raw value returned by the invoked method | |
| 111 | + * @throws Exception raised if no suitable argument resolver can be found, | |
| 112 | + * or if the method raised an exception | |
| 113 | + * @see #getMethodArgumentValues | |
| 114 | + * @see #doInvoke | |
| 115 | + */ | |
| 116 | + @Nullable | |
| 117 | + public Object invoke(Message<?> message, Object... providedArgs) throws Exception { | |
| 118 | + Object[] args = getMethodArgumentValues(message, providedArgs); | |
| 119 | + if (logger.isTraceEnabled()) { | |
| 120 | + logger.trace("Arguments: " + Arrays.toString(args)); | |
| 121 | + } | |
| 122 | + // 注意:如下是本类的改动点!!! | |
| 123 | + // 情况一:无租户编号的情况 | |
| 124 | + Long tenantId= parseTenantId(message); | |
| 125 | + if (tenantId == null) { | |
| 126 | + return doInvoke(args); | |
| 127 | + } | |
| 128 | + // 情况二:有租户的情况下 | |
| 129 | + return TenantUtils.execute(tenantId, () -> doInvoke(args)); | |
| 130 | + } | |
| 131 | + | |
| 132 | + private Long parseTenantId(Message<?> message) { | |
| 133 | + Object tenantId = message.getHeaders().get(HEADER_TENANT_ID); | |
| 134 | + if (tenantId == null) { | |
| 135 | + return null; | |
| 136 | + } | |
| 137 | + if (tenantId instanceof Long) { | |
| 138 | + return (Long) tenantId; | |
| 139 | + } | |
| 140 | + if (tenantId instanceof Number) { | |
| 141 | + return ((Number) tenantId).longValue(); | |
| 142 | + } | |
| 143 | + if (tenantId instanceof String) { | |
| 144 | + return Long.parseLong((String) tenantId); | |
| 145 | + } | |
| 146 | + if (tenantId instanceof byte[]) { | |
| 147 | + return Long.parseLong(new String((byte[]) tenantId)); | |
| 148 | + } | |
| 149 | + throw new IllegalArgumentException("未知的数据类型:" + tenantId); | |
| 150 | + } | |
| 151 | + | |
| 152 | + /** | |
| 153 | + * Get the method argument values for the current message, checking the provided | |
| 154 | + * argument values and falling back to the configured argument resolvers. | |
| 155 | + * <p>The resulting array will be passed into {@link #doInvoke}. | |
| 156 | + * @since 5.1.2 | |
| 157 | + */ | |
| 158 | + protected Object[] getMethodArgumentValues(Message<?> message, Object... providedArgs) throws Exception { | |
| 159 | + MethodParameter[] parameters = getMethodParameters(); | |
| 160 | + if (ObjectUtils.isEmpty(parameters)) { | |
| 161 | + return EMPTY_ARGS; | |
| 162 | + } | |
| 163 | + | |
| 164 | + Object[] args = new Object[parameters.length]; | |
| 165 | + for (int i = 0; i < parameters.length; i++) { | |
| 166 | + MethodParameter parameter = parameters[i]; | |
| 167 | + parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); | |
| 168 | + args[i] = findProvidedArgument(parameter, providedArgs); | |
| 169 | + if (args[i] != null) { | |
| 170 | + continue; | |
| 171 | + } | |
| 172 | + if (!this.resolvers.supportsParameter(parameter)) { | |
| 173 | + throw new MethodArgumentResolutionException( | |
| 174 | + message, parameter, formatArgumentError(parameter, "No suitable resolver")); | |
| 175 | + } | |
| 176 | + try { | |
| 177 | + args[i] = this.resolvers.resolveArgument(parameter, message); | |
| 178 | + } | |
| 179 | + catch (Exception ex) { | |
| 180 | + // Leave stack trace for later, exception may actually be resolved and handled... | |
| 181 | + if (logger.isDebugEnabled()) { | |
| 182 | + String exMsg = ex.getMessage(); | |
| 183 | + if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { | |
| 184 | + logger.debug(formatArgumentError(parameter, exMsg)); | |
| 185 | + } | |
| 186 | + } | |
| 187 | + throw ex; | |
| 188 | + } | |
| 189 | + } | |
| 190 | + return args; | |
| 191 | + } | |
| 192 | + | |
| 193 | + /** | |
| 194 | + * Invoke the handler method with the given argument values. | |
| 195 | + */ | |
| 196 | + @Nullable | |
| 197 | + protected Object doInvoke(Object... args) throws Exception { | |
| 198 | + try { | |
| 199 | + return getBridgedMethod().invoke(getBean(), args); | |
| 200 | + } | |
| 201 | + catch (IllegalArgumentException ex) { | |
| 202 | + assertTargetBean(getBridgedMethod(), getBean(), args); | |
| 203 | + String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); | |
| 204 | + throw new IllegalStateException(formatInvokeError(text, args), ex); | |
| 205 | + } | |
| 206 | + catch (InvocationTargetException ex) { | |
| 207 | + // Unwrap for HandlerExceptionResolvers ... | |
| 208 | + Throwable targetException = ex.getTargetException(); | |
| 209 | + if (targetException instanceof RuntimeException) { | |
| 210 | + throw (RuntimeException) targetException; | |
| 211 | + } | |
| 212 | + else if (targetException instanceof Error) { | |
| 213 | + throw (Error) targetException; | |
| 214 | + } | |
| 215 | + else if (targetException instanceof Exception) { | |
| 216 | + throw (Exception) targetException; | |
| 217 | + } | |
| 218 | + else { | |
| 219 | + throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException); | |
| 220 | + } | |
| 221 | + } | |
| 222 | + } | |
| 223 | + | |
| 224 | + MethodParameter getAsyncReturnValueType(@Nullable Object returnValue) { | |
| 225 | + return new AsyncResultMethodParameter(returnValue); | |
| 226 | + } | |
| 227 | + | |
| 228 | + private class AsyncResultMethodParameter extends HandlerMethodParameter { | |
| 229 | + | |
| 230 | + @Nullable | |
| 231 | + private final Object returnValue; | |
| 232 | + | |
| 233 | + private final ResolvableType returnType; | |
| 234 | + | |
| 235 | + public AsyncResultMethodParameter(@Nullable Object returnValue) { | |
| 236 | + super(-1); | |
| 237 | + this.returnValue = returnValue; | |
| 238 | + this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(); | |
| 239 | + } | |
| 240 | + | |
| 241 | + protected AsyncResultMethodParameter(AsyncResultMethodParameter original) { | |
| 242 | + super(original); | |
| 243 | + this.returnValue = original.returnValue; | |
| 244 | + this.returnType = original.returnType; | |
| 245 | + } | |
| 246 | + | |
| 247 | + @Override | |
| 248 | + public Class<?> getParameterType() { | |
| 249 | + if (this.returnValue != null) { | |
| 250 | + return this.returnValue.getClass(); | |
| 251 | + } | |
| 252 | + if (!ResolvableType.NONE.equals(this.returnType)) { | |
| 253 | + return this.returnType.toClass(); | |
| 254 | + } | |
| 255 | + return super.getParameterType(); | |
| 256 | + } | |
| 257 | + | |
| 258 | + @Override | |
| 259 | + public Type getGenericParameterType() { | |
| 260 | + return this.returnType.getType(); | |
| 261 | + } | |
| 262 | + | |
| 263 | + @Override | |
| 264 | + public AsyncResultMethodParameter clone() { | |
| 265 | + return new AsyncResultMethodParameter(this); | |
| 266 | + } | |
| 267 | + } | |
| 268 | + | |
| 269 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories
0 → 100644
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/job/TestJob.java deleted
| 1 | -package cn.iocoder.yudao.framework.tenant.core.job; | |
| 2 | - | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | |
| 5 | -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 6 | -import org.springframework.stereotype.Component; | |
| 7 | - | |
| 8 | -import java.util.List; | |
| 9 | -import java.util.concurrent.CopyOnWriteArrayList; | |
| 10 | - | |
| 11 | -@Component | |
| 12 | -public class TestJob implements JobHandler { | |
| 13 | - | |
| 14 | - private final List<Long> tenantIds = new CopyOnWriteArrayList<>(); | |
| 15 | - | |
| 16 | - @Override | |
| 17 | - @TenantJob // 标记多租户 | |
| 18 | - public String execute(String param) throws Exception { | |
| 19 | - tenantIds.add(TenantContextHolder.getTenantId()); | |
| 20 | - return "success"; | |
| 21 | - } | |
| 22 | - | |
| 23 | - public List<Long> getTenantIds() { | |
| 24 | - CollUtil.sort(tenantIds, Long::compareTo); | |
| 25 | - return tenantIds; | |
| 26 | - } | |
| 27 | - | |
| 28 | -} |
yudao-framework/yudao-spring-boot-starter-mq/pom.xml
| ... | ... | @@ -12,7 +12,7 @@ |
| 12 | 12 | <packaging>jar</packaging> |
| 13 | 13 | |
| 14 | 14 | <name>${project.artifactId}</name> |
| 15 | - <description>消息队列,基于 Redis Pub/Sub 实现广播消费,基于 Stream 实现集群消费</description> | |
| 15 | + <description>消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种</description> | |
| 16 | 16 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 17 | 17 | |
| 18 | 18 | <dependencies> |
| ... | ... | @@ -21,6 +21,23 @@ |
| 21 | 21 | <groupId>cn.iocoder.boot</groupId> |
| 22 | 22 | <artifactId>yudao-spring-boot-starter-redis</artifactId> |
| 23 | 23 | </dependency> |
| 24 | + | |
| 25 | + <!-- 消息队列相关 --> | |
| 26 | + <dependency> | |
| 27 | + <groupId>org.springframework.kafka</groupId> | |
| 28 | + <artifactId>spring-kafka</artifactId> | |
| 29 | + <optional>true</optional> | |
| 30 | + </dependency> | |
| 31 | + <dependency> | |
| 32 | + <groupId>org.springframework.amqp</groupId> | |
| 33 | + <artifactId>spring-rabbit</artifactId> | |
| 34 | + <optional>true</optional> | |
| 35 | + </dependency> | |
| 36 | + <dependency> | |
| 37 | + <groupId>org.apache.rocketmq</groupId> | |
| 38 | + <artifactId>rocketmq-spring-boot-starter</artifactId> | |
| 39 | + <optional>true</optional> | |
| 40 | + </dependency> | |
| 24 | 41 | </dependencies> |
| 25 | 42 | |
| 26 | 43 | -</project> |
| 44 | +</project> | |
| 27 | 45 | \ No newline at end of file | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/package-info.java
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.mq.rabbitmq.config; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ReflectUtil; | |
| 4 | +import lombok.extern.slf4j.Slf4j; | |
| 5 | +import org.springframework.amqp.utils.SerializationUtils; | |
| 6 | +import org.springframework.boot.autoconfigure.AutoConfiguration; | |
| 7 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | |
| 8 | + | |
| 9 | +import java.lang.reflect.Field; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * RabbitMQ 消息队列配置类 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +@AutoConfiguration | |
| 17 | +@Slf4j | |
| 18 | +@ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") | |
| 19 | +public class YudaoRabbitMQAutoConfiguration { | |
| 20 | + | |
| 21 | + static { | |
| 22 | + // 强制设置 SerializationUtils 的 TRUST_ALL 为 true,避免 RabbitMQ Consumer 反序列化消息报错 | |
| 23 | + // 为什么不通过设置 spring.amqp.deserialization.trust.all 呢?因为可能在 SerializationUtils static 初始化后 | |
| 24 | + Field trustAllField = ReflectUtil.getField(SerializationUtils.class, "TRUST_ALL"); | |
| 25 | + ReflectUtil.removeFinalModify(trustAllField); | |
| 26 | + ReflectUtil.setFieldValue(SerializationUtils.class, trustAllField, true); | |
| 27 | + } | |
| 28 | + | |
| 29 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/core/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQAutoConfiguration.java
| 1 | -package cn.iocoder.yudao.framework.mq.config; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.config; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.hutool.system.SystemUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.enums.DocumentEnum; |
| 7 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | |
| 8 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | |
| 9 | -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; | |
| 10 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | |
| 11 | -import cn.iocoder.yudao.framework.mq.job.RedisPendingMessageResendJob; | |
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; | |
| 8 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | |
| 9 | +import cn.iocoder.yudao.framework.mq.redis.core.job.RedisPendingMessageResendJob; | |
| 10 | +import cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener; | |
| 11 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; | |
| 12 | 12 | import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
| 13 | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | 14 | import org.redisson.api.RedissonClient; |
| 15 | 15 | import org.springframework.beans.factory.annotation.Value; |
| 16 | 16 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 17 | 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
| 18 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 19 | 18 | import org.springframework.context.annotation.Bean; |
| 20 | 19 | import org.springframework.data.redis.connection.RedisServerCommands; |
| 21 | 20 | import org.springframework.data.redis.connection.stream.Consumer; |
| ... | ... | @@ -27,7 +26,6 @@ import org.springframework.data.redis.core.RedisTemplate; |
| 27 | 26 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 28 | 27 | import org.springframework.data.redis.listener.ChannelTopic; |
| 29 | 28 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
| 30 | -import org.springframework.data.redis.stream.DefaultStreamMessageListenerContainerX; | |
| 31 | 29 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; |
| 32 | 30 | import org.springframework.scheduling.annotation.EnableScheduling; |
| 33 | 31 | |
| ... | ... | @@ -42,7 +40,7 @@ import java.util.Properties; |
| 42 | 40 | @Slf4j |
| 43 | 41 | @EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息 |
| 44 | 42 | @AutoConfiguration(after = YudaoRedisAutoConfiguration.class) |
| 45 | -public class YudaoMQAutoConfiguration { | |
| 43 | +public class YudaoRedisMQAutoConfiguration { | |
| 46 | 44 | |
| 47 | 45 | @Bean |
| 48 | 46 | public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, |
| ... | ... | @@ -59,10 +57,9 @@ public class YudaoMQAutoConfiguration { |
| 59 | 57 | * 创建 Redis Pub/Sub 广播消费的容器 |
| 60 | 58 | */ |
| 61 | 59 | @Bean(initMethod = "start", destroyMethod = "stop") |
| 62 | - @ConditionalOnBean(AbstractChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | |
| 63 | - @ConditionalOnProperty(prefix = "yudao.mq.redis.pubsub", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.pubsub.enable=false 禁用多租户 | |
| 60 | + @ConditionalOnBean(AbstractRedisChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | |
| 64 | 61 | public RedisMessageListenerContainer redisMessageListenerContainer( |
| 65 | - RedisMQTemplate redisMQTemplate, List<AbstractChannelMessageListener<?>> listeners) { | |
| 62 | + RedisMQTemplate redisMQTemplate, List<AbstractRedisChannelMessageListener<?>> listeners) { | |
| 66 | 63 | // 创建 RedisMessageListenerContainer 对象 |
| 67 | 64 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
| 68 | 65 | // 设置 RedisConnection 工厂。 |
| ... | ... | @@ -81,9 +78,8 @@ public class YudaoMQAutoConfiguration { |
| 81 | 78 | * 创建 Redis Stream 重新消费的任务 |
| 82 | 79 | */ |
| 83 | 80 | @Bean |
| 84 | - @ConditionalOnBean(AbstractStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | |
| 85 | - @ConditionalOnProperty(prefix = "yudao.mq.redis.stream", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.stream.enable=false 禁用多租户 | |
| 86 | - public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractStreamMessageListener<?>> listeners, | |
| 81 | + @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | |
| 82 | + public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners, | |
| 87 | 83 | RedisMQTemplate redisTemplate, |
| 88 | 84 | @Value("${spring.application.name}") String groupName, |
| 89 | 85 | RedissonClient redissonClient) { |
| ... | ... | @@ -92,14 +88,13 @@ public class YudaoMQAutoConfiguration { |
| 92 | 88 | |
| 93 | 89 | /** |
| 94 | 90 | * 创建 Redis Stream 集群消费的容器 |
| 95 | - * <p> | |
| 96 | - * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html | |
| 91 | + * | |
| 92 | + * 基础知识:<a href="https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html">Redis Stream 的 xreadgroup 命令</a> | |
| 97 | 93 | */ |
| 98 | 94 | @Bean(initMethod = "start", destroyMethod = "stop") |
| 99 | - @ConditionalOnBean(AbstractStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | |
| 100 | - @ConditionalOnProperty(prefix = "yudao.mq.redis.stream", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.stream.enable=false 禁用多租户 | |
| 95 | + @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | |
| 101 | 96 | public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( |
| 102 | - RedisMQTemplate redisMQTemplate, List<AbstractStreamMessageListener<?>> listeners) { | |
| 97 | + RedisMQTemplate redisMQTemplate, List<AbstractRedisStreamMessageListener<?>> listeners) { | |
| 103 | 98 | RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate(); |
| 104 | 99 | checkRedisVersion(redisTemplate); |
| 105 | 100 | // 第一步,创建 StreamMessageListenerContainer 容器 |
| ... | ... | @@ -111,8 +106,7 @@ public class YudaoMQAutoConfiguration { |
| 111 | 106 | .build(); |
| 112 | 107 | // 创建 container 对象 |
| 113 | 108 | StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = |
| 114 | -// StreamMessageListenerContainer.create(redisTemplate.getRequiredConnectionFactory(), containerOptions); | |
| 115 | - DefaultStreamMessageListenerContainerX.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); | |
| 109 | + StreamMessageListenerContainer.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); | |
| 116 | 110 | |
| 117 | 111 | // 第二步,注册监听器,消费对应的 Stream 主题 |
| 118 | 112 | String consumerName = buildConsumerName(); | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/RedisMQTemplate.java
| 1 | -package cn.iocoder.yudao.framework.mq.core; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.core; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | |
| 5 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | |
| 6 | -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; | |
| 7 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; | |
| 4 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | |
| 6 | +import cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessage; | |
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessage; | |
| 8 | 8 | import lombok.AllArgsConstructor; |
| 9 | 9 | import lombok.Getter; |
| 10 | 10 | import org.springframework.data.redis.connection.stream.RecordId; |
| ... | ... | @@ -35,7 +35,7 @@ public class RedisMQTemplate { |
| 35 | 35 | * |
| 36 | 36 | * @param message 消息 |
| 37 | 37 | */ |
| 38 | - public <T extends AbstractChannelMessage> void send(T message) { | |
| 38 | + public <T extends AbstractRedisChannelMessage> void send(T message) { | |
| 39 | 39 | try { |
| 40 | 40 | sendMessageBefore(message); |
| 41 | 41 | // 发送消息 |
| ... | ... | @@ -51,7 +51,7 @@ public class RedisMQTemplate { |
| 51 | 51 | * @param message 消息 |
| 52 | 52 | * @return 消息记录的编号对象 |
| 53 | 53 | */ |
| 54 | - public <T extends AbstractStreamMessage> RecordId send(T message) { | |
| 54 | + public <T extends AbstractRedisStreamMessage> RecordId send(T message) { | |
| 55 | 55 | try { |
| 56 | 56 | sendMessageBefore(message); |
| 57 | 57 | // 发送消息 | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/interceptor/RedisMessageInterceptor.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.interceptor; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.core.interceptor; | |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | |
| 3 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * {@link AbstractRedisMessage} 消息拦截器 | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java
| 1 | -package cn.iocoder.yudao.framework.mq.job; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.core.job; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | |
| 5 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | |
| 4 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; | |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; | |
| 6 | 6 | import lombok.AllArgsConstructor; |
| 7 | 7 | import lombok.extern.slf4j.Slf4j; |
| 8 | 8 | import org.redisson.api.RLock; |
| ... | ... | @@ -33,7 +33,7 @@ public class RedisPendingMessageResendJob { |
| 33 | 33 | */ |
| 34 | 34 | private static final int EXPIRE_TIME = 5 * 60; |
| 35 | 35 | |
| 36 | - private final List<AbstractStreamMessageListener<?>> listeners; | |
| 36 | + private final List<AbstractRedisStreamMessageListener<?>> listeners; | |
| 37 | 37 | private final RedisMQTemplate redisTemplate; |
| 38 | 38 | private final String groupName; |
| 39 | 39 | private final RedissonClient redissonClient; | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/message/AbstractRedisMessage.java
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.pubsub; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.core.pubsub; | |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | |
| 3 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | |
| 4 | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 5 | 5 | |
| 6 | 6 | /** |
| ... | ... | @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; |
| 8 | 8 | * |
| 9 | 9 | * @author 芋道源码 |
| 10 | 10 | */ |
| 11 | -public abstract class AbstractChannelMessage extends AbstractRedisMessage { | |
| 11 | +public abstract class AbstractRedisChannelMessage extends AbstractRedisMessage { | |
| 12 | 12 | |
| 13 | 13 | /** |
| 14 | - * 获得 Redis Channel | |
| 14 | + * 获得 Redis Channel,默认使用类名 | |
| 15 | 15 | * |
| 16 | 16 | * @return Channel |
| 17 | 17 | */ |
| 18 | 18 | @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。 |
| 19 | - public abstract String getChannel(); | |
| 19 | + public String getChannel() { | |
| 20 | + return getClass().getSimpleName(); | |
| 21 | + } | |
| 20 | 22 | |
| 21 | 23 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessageListener.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.pubsub; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.core.pubsub; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | |
| 6 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | |
| 7 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; | |
| 6 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | |
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | |
| 8 | 8 | import lombok.Setter; |
| 9 | 9 | import lombok.SneakyThrows; |
| 10 | 10 | import org.springframework.data.redis.connection.Message; |
| ... | ... | @@ -20,7 +20,7 @@ import java.util.List; |
| 20 | 20 | * |
| 21 | 21 | * @author 芋道源码 |
| 22 | 22 | */ |
| 23 | -public abstract class AbstractChannelMessageListener<T extends AbstractChannelMessage> implements MessageListener { | |
| 23 | +public abstract class AbstractRedisChannelMessageListener<T extends AbstractRedisChannelMessage> implements MessageListener { | |
| 24 | 24 | |
| 25 | 25 | /** |
| 26 | 26 | * 消息类型 |
| ... | ... | @@ -37,7 +37,7 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe |
| 37 | 37 | private RedisMQTemplate redisMQTemplate; |
| 38 | 38 | |
| 39 | 39 | @SneakyThrows |
| 40 | - protected AbstractChannelMessageListener() { | |
| 40 | + protected AbstractRedisChannelMessageListener() { | |
| 41 | 41 | this.messageType = getMessageClass(); |
| 42 | 42 | this.channel = messageType.getDeclaredConstructor().newInstance().getChannel(); |
| 43 | 43 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessage.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessage.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.stream; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.core.stream; | |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | |
| 3 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | |
| 4 | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 5 | 5 | |
| 6 | 6 | /** |
| ... | ... | @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; |
| 8 | 8 | * |
| 9 | 9 | * @author 芋道源码 |
| 10 | 10 | */ |
| 11 | -public abstract class AbstractStreamMessage extends AbstractRedisMessage { | |
| 11 | +public abstract class AbstractRedisStreamMessage extends AbstractRedisMessage { | |
| 12 | 12 | |
| 13 | 13 | /** |
| 14 | - * 获得 Redis Stream Key | |
| 14 | + * 获得 Redis Stream Key,默认使用类名 | |
| 15 | 15 | * |
| 16 | 16 | * @return Channel |
| 17 | 17 | */ |
| 18 | 18 | @JsonIgnore // 避免序列化 |
| 19 | - public abstract String getStreamKey(); | |
| 19 | + public String getStreamKey() { | |
| 20 | + return getClass().getSimpleName(); | |
| 21 | + } | |
| 20 | 22 | |
| 21 | 23 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessageListener.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.stream; | |
| 1 | +package cn.iocoder.yudao.framework.mq.redis.core.stream; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | |
| 6 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | |
| 7 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; | |
| 6 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | |
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | |
| 8 | 8 | import lombok.Getter; |
| 9 | 9 | import lombok.Setter; |
| 10 | 10 | import lombok.SneakyThrows; |
| ... | ... | @@ -22,7 +22,7 @@ import java.util.List; |
| 22 | 22 | * |
| 23 | 23 | * @author 芋道源码 |
| 24 | 24 | */ |
| 25 | -public abstract class AbstractStreamMessageListener<T extends AbstractStreamMessage> | |
| 25 | +public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedisStreamMessage> | |
| 26 | 26 | implements StreamListener<String, ObjectRecord<String, String>> { |
| 27 | 27 | |
| 28 | 28 | /** |
| ... | ... | @@ -48,7 +48,7 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess |
| 48 | 48 | private RedisMQTemplate redisMQTemplate; |
| 49 | 49 | |
| 50 | 50 | @SneakyThrows |
| 51 | - protected AbstractStreamMessageListener() { | |
| 51 | + protected AbstractRedisStreamMessageListener() { | |
| 52 | 52 | this.messageType = getMessageClass(); |
| 53 | 53 | this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); |
| 54 | 54 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainerX.java deleted
| 1 | -package org.springframework.data.redis.stream; | |
| 2 | - | |
| 3 | -import cn.hutool.core.util.ReflectUtil; | |
| 4 | -import org.springframework.data.redis.connection.RedisConnectionFactory; | |
| 5 | -import org.springframework.data.redis.connection.stream.ByteRecord; | |
| 6 | -import org.springframework.data.redis.connection.stream.ReadOffset; | |
| 7 | -import org.springframework.data.redis.connection.stream.Record; | |
| 8 | -import org.springframework.util.Assert; | |
| 9 | - | |
| 10 | -import java.util.Collections; | |
| 11 | -import java.util.List; | |
| 12 | -import java.util.function.Function; | |
| 13 | - | |
| 14 | -/** | |
| 15 | - * 拓展 DefaultStreamMessageListenerContainer 实现,解决 Spring Data Redis + Redisson 结合使用时,Redisson 在 Stream 获得不到数据时,返回 null 而不是空 List,导致 NPE 异常。 | |
| 16 | - * 对应 issue:https://github.com/spring-projects/spring-data-redis/issues/2147 和 https://github.com/redisson/redisson/issues/4006 | |
| 17 | - * 目前看下来 Spring Data Redis 不肯加 null 判断,Redisson 暂时也没改返回 null 到空 List 的打算,所以暂时只能自己改,哽咽! | |
| 18 | - * | |
| 19 | - * @author 芋道源码 | |
| 20 | - */ | |
| 21 | -public class DefaultStreamMessageListenerContainerX<K, V extends Record<K, ?>> extends DefaultStreamMessageListenerContainer<K, V> { | |
| 22 | - | |
| 23 | - /** | |
| 24 | - * 参考 {@link StreamMessageListenerContainer#create(RedisConnectionFactory, StreamMessageListenerContainerOptions)} 的实现 | |
| 25 | - */ | |
| 26 | - public static <K, V extends Record<K, ?>> StreamMessageListenerContainer<K, V> create(RedisConnectionFactory connectionFactory, StreamMessageListenerContainer.StreamMessageListenerContainerOptions<K, V> options) { | |
| 27 | - Assert.notNull(connectionFactory, "RedisConnectionFactory must not be null!"); | |
| 28 | - Assert.notNull(options, "StreamMessageListenerContainerOptions must not be null!"); | |
| 29 | - return new DefaultStreamMessageListenerContainerX<>(connectionFactory, options); | |
| 30 | - } | |
| 31 | - | |
| 32 | - public DefaultStreamMessageListenerContainerX(RedisConnectionFactory connectionFactory, StreamMessageListenerContainerOptions<K, V> containerOptions) { | |
| 33 | - super(connectionFactory, containerOptions); | |
| 34 | - } | |
| 35 | - | |
| 36 | - /** | |
| 37 | - * 参考 {@link DefaultStreamMessageListenerContainer#register(StreamReadRequest, StreamListener)} 的实现 | |
| 38 | - */ | |
| 39 | - @Override | |
| 40 | - public Subscription register(StreamReadRequest<K> streamRequest, StreamListener<K, V> listener) { | |
| 41 | - return this.doRegisterX(getReadTaskX(streamRequest, listener)); | |
| 42 | - } | |
| 43 | - | |
| 44 | - @SuppressWarnings("unchecked") | |
| 45 | - private StreamPollTask<K, V> getReadTaskX(StreamReadRequest<K> streamRequest, StreamListener<K, V> listener) { | |
| 46 | - StreamPollTask<K, V> task = ReflectUtil.invoke(this, "getReadTask", streamRequest, listener); | |
| 47 | - // 修改 readFunction 方法 | |
| 48 | - Function<ReadOffset, List<ByteRecord>> readFunction = (Function<ReadOffset, List<ByteRecord>>) ReflectUtil.getFieldValue(task, "readFunction"); | |
| 49 | - ReflectUtil.setFieldValue(task, "readFunction", (Function<ReadOffset, List<ByteRecord>>) readOffset -> { | |
| 50 | - List<ByteRecord> records = readFunction.apply(readOffset); | |
| 51 | - //【重点】保证 records 不是空,避免 NPE 的问题!!! | |
| 52 | - return records != null ? records : Collections.emptyList(); | |
| 53 | - }); | |
| 54 | - return task; | |
| 55 | - } | |
| 56 | - | |
| 57 | - private Subscription doRegisterX(Task task) { | |
| 58 | - return ReflectUtil.invoke(this, "doRegister", task); | |
| 59 | - } | |
| 60 | - | |
| 61 | -} | |
| 62 | - |
yudao-framework/yudao-spring-boot-starter-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 事件机制 Event 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/RocketMQ/?yudao> | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 Kafka 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/Kafka/?yudao> | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/RabbitMQ/?yudao> | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/RocketMQ/?yudao> | ... | ... |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java
| ... | ... | @@ -20,16 +20,16 @@ public enum BpmCommentTypeEnum { |
| 20 | 20 | ; |
| 21 | 21 | |
| 22 | 22 | /** |
| 23 | - * 结果 | |
| 23 | + * 操作类型 | |
| 24 | 24 | */ |
| 25 | 25 | private final Integer type; |
| 26 | 26 | /** |
| 27 | - * 描述 | |
| 27 | + * 操作名字 | |
| 28 | 28 | */ |
| 29 | - private final String desc; | |
| 29 | + private final String name; | |
| 30 | 30 | /** |
| 31 | - * 模板信息 | |
| 31 | + * 操作描述 | |
| 32 | 32 | */ |
| 33 | - private final String templateComment; | |
| 33 | + private final String comment; | |
| 34 | 34 | |
| 35 | 35 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java
| ... | ... | @@ -30,13 +30,13 @@ public enum BpmProcessInstanceResultEnum { |
| 30 | 30 | * 相当于是 通过 APPROVE 的特殊状态 |
| 31 | 31 | * 例如:A审批, A 后加签了 B,并且审批通过了任务,但是 B 还未审批,则当前任务状态为“待后加签任务完成” |
| 32 | 32 | */ |
| 33 | - ADD_SIGN_AFTER(7, "待后加签任务完成"), | |
| 33 | + SIGN_AFTER(7, "待后加签任务完成"), | |
| 34 | 34 | /** |
| 35 | 35 | * 【加签】源任务未审批,但是向前加签了,所以源任务状态变为“待前加签任务完成” |
| 36 | 36 | * 相当于是 处理中 PROCESS 的特殊状态 |
| 37 | 37 | * 例如:A 审批, A 前加签了 B,B 还未审核 |
| 38 | 38 | */ |
| 39 | - ADD_SIGN_BEFORE(8, "待前加签任务完成"), | |
| 39 | + SIGN_BEFORE(8, "待前加签任务完成"), | |
| 40 | 40 | /** |
| 41 | 41 | * 【加签】后加签任务被创建时的初始状态 |
| 42 | 42 | * 相当于是 处理中 PROCESS 的特殊状态 |
| ... | ... | @@ -71,7 +71,7 @@ public enum BpmProcessInstanceResultEnum { |
| 71 | 71 | public static boolean isEndResult(Integer result) { |
| 72 | 72 | return ObjectUtils.equalsAny(result, APPROVE.getResult(), REJECT.getResult(), |
| 73 | 73 | CANCEL.getResult(), BACK.getResult(), |
| 74 | - ADD_SIGN_AFTER.getResult()); | |
| 74 | + SIGN_AFTER.getResult()); | |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
| ... | ... | @@ -74,7 +74,7 @@ public class BpmTaskController { |
| 74 | 74 | return success(true); |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | - @GetMapping("/get-return-list") | |
| 77 | + @GetMapping("/return-list") | |
| 78 | 78 | @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") |
| 79 | 79 | @Parameter(name = "taskId", description = "当前任务ID", required = true) |
| 80 | 80 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") |
| ... | ... | @@ -98,27 +98,28 @@ public class BpmTaskController { |
| 98 | 98 | return success(true); |
| 99 | 99 | } |
| 100 | 100 | |
| 101 | - @PutMapping("/add-sign") | |
| 101 | + @PutMapping("/create-sign") | |
| 102 | 102 | @Operation(summary = "加签", description = "before 前加签,after 后加签") |
| 103 | 103 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") |
| 104 | - public CommonResult<Boolean> addSignTask(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) { | |
| 105 | - taskService.addSignTask(getLoginUserId(), reqVO); | |
| 104 | + public CommonResult<Boolean> createSignTask(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) { | |
| 105 | + taskService.createSignTask(getLoginUserId(), reqVO); | |
| 106 | 106 | return success(true); |
| 107 | 107 | } |
| 108 | 108 | |
| 109 | - @PutMapping("/sub-sign") | |
| 109 | + @DeleteMapping("/delete-sign") | |
| 110 | 110 | @Operation(summary = "减签") |
| 111 | 111 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") |
| 112 | - public CommonResult<Boolean> subSignTask(@Valid @RequestBody BpmTaskSubSignReqVO reqVO) { | |
| 113 | - taskService.subSignTask(getLoginUserId(), reqVO); | |
| 112 | + public CommonResult<Boolean> deleteSignTask(@Valid @RequestBody BpmTaskSubSignReqVO reqVO) { | |
| 113 | + taskService.deleteSignTask(getLoginUserId(), reqVO); | |
| 114 | 114 | return success(true); |
| 115 | 115 | } |
| 116 | 116 | |
| 117 | - @GetMapping("/get-children-task-list") | |
| 117 | + @GetMapping("children-list") | |
| 118 | 118 | @Operation(summary = "获取能被减签的任务") |
| 119 | + @Parameter(name = "parentId", description = "父级任务 ID", required = true) | |
| 119 | 120 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") |
| 120 | - public CommonResult<List<BpmTaskSubSignRespVO>> getChildrenTaskList(@RequestParam("taskId") String taskId) { | |
| 121 | - return success(taskService.getChildrenTaskList(taskId)); | |
| 121 | + public CommonResult<List<BpmTaskSubSignRespVO>> getChildrenTaskList(@RequestParam("parentId") String parentId) { | |
| 122 | + return success(taskService.getChildrenTaskList(parentId)); | |
| 122 | 123 | } |
| 123 | 124 | |
| 124 | 125 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java
| ... | ... | @@ -6,6 +6,7 @@ import lombok.Data; |
| 6 | 6 | import javax.validation.constraints.NotEmpty; |
| 7 | 7 | import java.util.Set; |
| 8 | 8 | |
| 9 | +// TODO @海洋:类名,应该是 create 哈 | |
| 9 | 10 | @Schema(description = "管理后台 - 加签流程任务的 Request VO") |
| 10 | 11 | @Data |
| 11 | 12 | public class BpmTaskAddSignReqVO { |
| ... | ... | @@ -26,4 +27,4 @@ public class BpmTaskAddSignReqVO { |
| 26 | 27 | @NotEmpty(message = "加签原因不能为空") |
| 27 | 28 | private String reason; |
| 28 | 29 | |
| 29 | -} | |
| 30 | 30 | \ No newline at end of file |
| 31 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.convert.task; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | 3 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 4 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 8 | 9 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 9 | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| ... | ... | @@ -25,6 +26,9 @@ import java.util.Date; |
| 25 | 26 | import java.util.List; |
| 26 | 27 | import java.util.Map; |
| 27 | 28 | |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; | |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | |
| 31 | + | |
| 28 | 32 | /** |
| 29 | 33 | * Bpm 任务 Convert |
| 30 | 34 | * |
| ... | ... | @@ -167,32 +171,31 @@ public interface BpmTaskConvert { |
| 167 | 171 | default List<BpmTaskSubSignRespVO> convertList(List<BpmTaskExtDO> bpmTaskExtDOList, |
| 168 | 172 | Map<Long, AdminUserRespDTO> userMap, |
| 169 | 173 | Map<String, Task> idTaskMap){ |
| 170 | - return CollectionUtils.convertList(bpmTaskExtDOList, task->{ | |
| 171 | - BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO(); | |
| 172 | - bpmTaskSubSignRespVO.setName(task.getName()); | |
| 173 | - bpmTaskSubSignRespVO.setId(task.getTaskId()); | |
| 174 | - Task sourceTask = idTaskMap.get(task.getTaskId()); | |
| 174 | + return CollectionUtils.convertList(bpmTaskExtDOList, task -> { | |
| 175 | + BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO() | |
| 176 | + .setId(task.getTaskId()).setName(task.getName()); | |
| 175 | 177 | // 后加签任务不会直接设置 assignee ,所以不存在 assignee 的情况,则去取 owner |
| 176 | - String assignee = StrUtil.isNotEmpty(sourceTask.getAssignee()) ? sourceTask.getAssignee() : sourceTask.getOwner(); | |
| 177 | - AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(assignee)); | |
| 178 | - if (assignUser != null) { | |
| 179 | - bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser)); | |
| 180 | - } | |
| 178 | + Task sourceTask = idTaskMap.get(task.getTaskId()); | |
| 179 | + String assignee = ObjectUtil.defaultIfBlank(sourceTask.getOwner(),sourceTask.getAssignee()); | |
| 180 | + MapUtils.findAndThen(userMap,NumberUtils.parseLong(assignee), | |
| 181 | + assignUser-> bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser))); | |
| 181 | 182 | return bpmTaskSubSignRespVO; |
| 182 | 183 | }); |
| 183 | 184 | } |
| 184 | 185 | |
| 185 | 186 | /** |
| 186 | 187 | * 转换任务为父子级 |
| 188 | + * | |
| 187 | 189 | * @param sourceList 原始数据 |
| 188 | 190 | * @return 转换后的父子级数组 |
| 189 | 191 | */ |
| 190 | - default List<BpmTaskRespVO> convertChildrenList(List<BpmTaskRespVO> sourceList){ | |
| 191 | - List<BpmTaskRespVO> childrenTaskList = CollectionUtils.filterList(sourceList, r -> StrUtil.isNotEmpty(r.getParentTaskId())); | |
| 192 | - Map<String, List<BpmTaskRespVO>> parentChildrenTaskListMap = CollectionUtils.convertMultiMap(childrenTaskList, BpmTaskRespVO::getParentTaskId); | |
| 192 | + default List<BpmTaskRespVO> convertChildrenList(List<BpmTaskRespVO> sourceList) { | |
| 193 | + List<BpmTaskRespVO> childrenTaskList = filterList(sourceList, r -> StrUtil.isNotEmpty(r.getParentTaskId())); | |
| 194 | + Map<String, List<BpmTaskRespVO>> parentChildrenTaskListMap = convertMultiMap(childrenTaskList, BpmTaskRespVO::getParentTaskId); | |
| 193 | 195 | for (BpmTaskRespVO bpmTaskRespVO : sourceList) { |
| 194 | 196 | bpmTaskRespVO.setChildren(parentChildrenTaskListMap.get(bpmTaskRespVO.getId())); |
| 195 | 197 | } |
| 196 | - return CollectionUtils.filterList(sourceList, r -> StrUtil.isEmpty(r.getParentTaskId())); | |
| 198 | + return filterList(sourceList, r -> StrUtil.isEmpty(r.getParentTaskId())); | |
| 197 | 199 | } |
| 200 | + | |
| 198 | 201 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java
| ... | ... | @@ -21,13 +21,13 @@ public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> { |
| 21 | 21 | return selectList(BpmTaskExtDO::getTaskId, taskIds); |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | + // TODO @海:BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST) 应该作为条件,mapper 不要有业务 | |
| 24 | 25 | default List<BpmTaskExtDO> selectProcessListByTaskIds(Collection<String> taskIds) { |
| 25 | 26 | return selectList(new LambdaQueryWrapperX<BpmTaskExtDO>() |
| 26 | 27 | .in(BpmTaskExtDO::getTaskId, taskIds) |
| 27 | 28 | .in(BpmTaskExtDO::getResult, BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST)); |
| 28 | 29 | } |
| 29 | 30 | |
| 30 | - | |
| 31 | 31 | default BpmTaskExtDO selectByTaskId(String taskId) { |
| 32 | 32 | return selectOne(BpmTaskExtDO::getTaskId, taskId); |
| 33 | 33 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmSecurityConfiguration.java deleted
| 1 | -package cn.iocoder.yudao.module.bpm.framework.bpm.config; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; | |
| 4 | -import org.springframework.context.annotation.Bean; | |
| 5 | -import org.springframework.context.annotation.Configuration; | |
| 6 | -import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |
| 7 | -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * @author kemengkai | |
| 11 | - * @create 2022-05-07 08:15 | |
| 12 | - */ | |
| 13 | -@Configuration("bpmSecurityConfiguration") | |
| 14 | -public class BpmSecurityConfiguration { | |
| 15 | - | |
| 16 | - @Bean("bpmAuthorizeRequestsCustomizer") | |
| 17 | - public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { | |
| 18 | - return new AuthorizeRequestsCustomizer() { | |
| 19 | - | |
| 20 | - @Override | |
| 21 | - public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) { | |
| 22 | - // 任务回退接口 | |
| 23 | - registry.antMatchers(buildAdminApi("/bpm/task/back")).permitAll(); | |
| 24 | - } | |
| 25 | - | |
| 26 | - }; | |
| 27 | - } | |
| 28 | -} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| ... | ... | @@ -163,7 +163,7 @@ public interface BpmTaskService { |
| 163 | 163 | * @param userId 被加签的用户和任务 ID,加签类型 |
| 164 | 164 | * @param reqVO 当前用户 ID |
| 165 | 165 | */ |
| 166 | - void addSignTask(Long userId, BpmTaskAddSignReqVO reqVO); | |
| 166 | + void createSignTask(Long userId, BpmTaskAddSignReqVO reqVO); | |
| 167 | 167 | |
| 168 | 168 | /** |
| 169 | 169 | * 任务减签名 |
| ... | ... | @@ -171,14 +171,14 @@ public interface BpmTaskService { |
| 171 | 171 | * @param userId 当前用户ID |
| 172 | 172 | * @param reqVO 被减签的任务 ID,理由 |
| 173 | 173 | */ |
| 174 | - void subSignTask(Long userId, BpmTaskSubSignReqVO reqVO); | |
| 174 | + void deleteSignTask(Long userId, BpmTaskSubSignReqVO reqVO); | |
| 175 | 175 | |
| 176 | 176 | /** |
| 177 | 177 | * 获取指定任务的子任务和审批人信息 |
| 178 | 178 | * |
| 179 | - * @param taskId 指定任务ID | |
| 179 | + * @param parentId 指定任务ID | |
| 180 | 180 | * @return 子任务列表 |
| 181 | 181 | */ |
| 182 | - List<BpmTaskSubSignRespVO> getChildrenTaskList(String taskId); | |
| 182 | + List<BpmTaskSubSignRespVO> getChildrenTaskList(String parentId); | |
| 183 | 183 | |
| 184 | 184 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -53,7 +53,6 @@ import javax.annotation.Resource; |
| 53 | 53 | import javax.validation.Valid; |
| 54 | 54 | import java.time.LocalDateTime; |
| 55 | 55 | import java.util.*; |
| 56 | -import java.util.stream.Collectors; | |
| 57 | 56 | import java.util.stream.Stream; |
| 58 | 57 | |
| 59 | 58 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| ... | ... | @@ -240,10 +239,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 240 | 239 | handleParentTask(task); |
| 241 | 240 | } |
| 242 | 241 | |
| 242 | + | |
| 243 | 243 | /** |
| 244 | 244 | * 审批通过存在“后加签”的任务。 |
| 245 | 245 | * <p> |
| 246 | - * 注意:该任务不能马上完成,需要一个中间状态(ADD_SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理 | |
| 246 | + * 注意:该任务不能马上完成,需要一个中间状态(SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理 | |
| 247 | 247 | * |
| 248 | 248 | * @param task 当前任务 |
| 249 | 249 | * @param reqVO 前端请求参数 |
| ... | ... | @@ -251,7 +251,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 251 | 251 | private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { |
| 252 | 252 | // 1. 有向后加签,则该任务状态临时设置为 ADD_SIGN_AFTER 状态 |
| 253 | 253 | taskExtMapper.updateByTaskId( |
| 254 | - new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.ADD_SIGN_AFTER.getResult()) | |
| 254 | + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.SIGN_AFTER.getResult()) | |
| 255 | 255 | .setReason(reqVO.getReason()).setEndTime(LocalDateTime.now())); |
| 256 | 256 | |
| 257 | 257 | // 2. 激活子任务 |
| ... | ... | @@ -265,7 +265,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | 267 | /** |
| 268 | - * 处理当前任务的父任务 | |
| 268 | + * 处理当前任务的父任务,主要处理“加签”的情况 | |
| 269 | 269 | * |
| 270 | 270 | * @param task 当前任务 |
| 271 | 271 | */ |
| ... | ... | @@ -274,76 +274,79 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 274 | 274 | if (StrUtil.isBlank(parentTaskId)) { |
| 275 | 275 | return; |
| 276 | 276 | } |
| 277 | - if (StrUtil.isNotBlank(parentTaskId)) { | |
| 278 | - // 1. 判断当前任务的父任务是否还有子任务 | |
| 279 | - Long childrenTaskCount = getChildrenTaskCount(parentTaskId); | |
| 280 | - if (childrenTaskCount > 0) { | |
| 281 | - return; | |
| 282 | - } | |
| 283 | - // 2. 获取父任务 | |
| 284 | - Task parentTask = validateTaskExist(parentTaskId); | |
| 285 | - | |
| 286 | - // 3. 情况一:处理向前加签 | |
| 287 | - String scopeType = parentTask.getScopeType(); | |
| 288 | - if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) { | |
| 289 | - // 3.1 如果是向前加签的任务,则调用 resolveTask 指派父任务,将 owner 重新赋值给父任务的 assignee | |
| 290 | - taskService.resolveTask(parentTaskId); | |
| 291 | - // 3.2 更新任务拓展表为处理中 | |
| 292 | - taskExtMapper.updateByTaskId( | |
| 293 | - new BpmTaskExtDO().setTaskId(parentTask.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult())); | |
| 294 | - } else if (BpmTaskAddSignTypeEnum.AFTER.getType().equals(scopeType)) { | |
| 295 | - // 3. 情况二:处理向后加签 | |
| 296 | - handleAfterSign(parentTask); | |
| 297 | - } | |
| 277 | + // 1. 判断当前任务的父任务是否还有子任务 | |
| 278 | + Long childrenTaskCount = getChildrenTaskCount(parentTaskId); | |
| 279 | + if (childrenTaskCount > 0) { | |
| 280 | + return; | |
| 281 | + } | |
| 282 | + // 2. 获取父任务 | |
| 283 | + Task parentTask = validateTaskExist(parentTaskId); | |
| 298 | 284 | |
| 299 | - // 4. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 | |
| 300 | - // 再查询一次的原因是避免报错:Task was updated by another transaction concurrently | |
| 301 | - // 因为前面处理后可能会导致 parentTask rev 字段被修改,需要重新获取最新的 | |
| 302 | - parentTask = getTask(parentTaskId); | |
| 303 | - if (parentTask == null) { | |
| 304 | - // 为空的情况是:已经通过 handleAfterSign 方法将任务完成了,所以 ru_task 表会查不到数据 | |
| 305 | - return; | |
| 306 | - } | |
| 307 | - clearTaskScopeTypeAndSave(parentTask); | |
| 285 | + // 3. 处理加签情况 | |
| 286 | + String scopeType = parentTask.getScopeType(); | |
| 287 | + if(!validateSignType(scopeType)){ | |
| 288 | + return; | |
| 308 | 289 | } |
| 290 | + // 3.1 情况一:处理向前加签 | |
| 291 | + if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) { | |
| 292 | + // 3.1.1 如果是向前加签的任务,则调用 resolveTask 指派父任务,将 owner 重新赋值给父任务的 assignee,这样它就可以被审批 | |
| 293 | + taskService.resolveTask(parentTaskId); | |
| 294 | + // 3.1.2 更新任务拓展表为处理中 | |
| 295 | + taskExtMapper.updateByTaskId( | |
| 296 | + new BpmTaskExtDO().setTaskId(parentTask.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult())); | |
| 297 | + } else if (BpmTaskAddSignTypeEnum.AFTER.getType().equals(scopeType)) { | |
| 298 | + // 3.2 情况二:处理向后加签 | |
| 299 | + handleParentTaskForAfterSign(parentTask); | |
| 300 | + } | |
| 301 | + | |
| 302 | + // 4. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 | |
| 303 | + // 再查询一次的原因是避免报错:Task was updated by another transaction concurrently | |
| 304 | + // 因为前面处理后可能会导致 parentTask rev 字段被修改,需要重新获取最新的 | |
| 305 | + parentTask = getTask(parentTaskId); | |
| 306 | + if (parentTask == null) { | |
| 307 | + // 为空的情况是:已经通过 handleAfterSign 方法将任务完成了,所以 ru_task 表会查不到数据 | |
| 308 | + return; | |
| 309 | + } | |
| 310 | + clearTaskScopeTypeAndSave(parentTask); | |
| 309 | 311 | } |
| 310 | 312 | |
| 313 | + | |
| 311 | 314 | /** |
| 312 | 315 | * 处理后加签任务 |
| 313 | 316 | * |
| 314 | 317 | * @param parentTask 当前审批任务的父任务 |
| 315 | 318 | */ |
| 316 | - private void handleAfterSign(Task parentTask) { | |
| 319 | + // TODO @海:这个逻辑,怎么感觉可以是 parentTask 的 parent,再去调用 handleParentTask 方法;可以微信聊下; | |
| 320 | + private void handleParentTaskForAfterSign(Task parentTask) { | |
| 317 | 321 | String parentTaskId = parentTask.getId(); |
| 318 | - //1. 更新 parentTask 的任务拓展表为通过,并调用 complete 完成自己 | |
| 322 | + // 1. 更新 parentTask 的任务拓展表为通过,并调用 complete 完成自己 | |
| 319 | 323 | BpmTaskExtDO currentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId()); |
| 320 | - BpmTaskExtDO currentTaskUpdateEntity = new BpmTaskExtDO().setTaskId(parentTask.getId()) | |
| 324 | + BpmTaskExtDO currentTaskExtUpdateObj = new BpmTaskExtDO().setTaskId(parentTask.getId()) | |
| 321 | 325 | .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); |
| 322 | 326 | if (currentTaskExt.getEndTime() == null) { |
| 323 | 327 | // 1.1 有这个判断是因为,以前没设置过结束时间,才去设置 |
| 324 | - currentTaskUpdateEntity.setEndTime(LocalDateTime.now()); | |
| 328 | + currentTaskExtUpdateObj.setEndTime(LocalDateTime.now()); | |
| 325 | 329 | } |
| 326 | - // 1.2 完成自己 | |
| 327 | - taskExtMapper.updateByTaskId(currentTaskUpdateEntity); | |
| 330 | + taskExtMapper.updateByTaskId(currentTaskExtUpdateObj); | |
| 331 | + // 1.2 完成自己(因为它已经没有子任务,所以也可以完成) | |
| 328 | 332 | taskService.complete(parentTaskId); |
| 329 | 333 | |
| 330 | 334 | // 2. 如果有父级,递归查询上级任务是否都已经完成 |
| 331 | 335 | if (StrUtil.isEmpty(parentTask.getParentTaskId())) { |
| 332 | 336 | return; |
| 333 | 337 | } |
| 334 | - // TODO @海:这块待讨论,脑子略乱;感觉 handleAfterSign 的后半段,和 handleParentTask 有点重叠??? | |
| 335 | 338 | // 2.1 判断整条链路的任务是否完成 |
| 336 | 339 | // 例如从 A 任务加签了一个 B 任务,B 任务又加签了一个 C 任务,C 任务加签了 D 任务 |
| 337 | 340 | // 此时,D 任务完成,要一直往上找到祖先任务 A调用 complete 方法完成 A 任务 |
| 338 | 341 | boolean allChildrenTaskFinish = true; |
| 339 | 342 | while (StrUtil.isNotBlank(parentTask.getParentTaskId())) { |
| 340 | 343 | parentTask = validateTaskExist(parentTask.getParentTaskId()); |
| 341 | - BpmTaskExtDO bpmTaskExtDO = taskExtMapper.selectByTaskId(parentTask.getId()); | |
| 342 | - if (bpmTaskExtDO == null) { | |
| 344 | + BpmTaskExtDO parentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId()); | |
| 345 | + if (parentTaskExt == null) { | |
| 343 | 346 | break; |
| 344 | 347 | } |
| 345 | - boolean currentTaskFinish = BpmProcessInstanceResultEnum.isEndResult(bpmTaskExtDO.getResult()); | |
| 346 | - // 2.2 如果 allChildrenTaskFinish 已经被赋值为 false ,则不会再赋值为 true,因为整个链路没有完成 | |
| 348 | + boolean currentTaskFinish = BpmProcessInstanceResultEnum.isEndResult(parentTaskExt.getResult()); | |
| 349 | + // 2.2 如果 allChildrenTaskFinish 已经被赋值为 false,则不会再赋值为 true,因为整个链路没有完成 | |
| 347 | 350 | if (allChildrenTaskFinish) { |
| 348 | 351 | allChildrenTaskFinish = currentTaskFinish; |
| 349 | 352 | } |
| ... | ... | @@ -354,19 +357,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 354 | 357 | |
| 355 | 358 | // 3 处理非完成状态的任务 |
| 356 | 359 | // 3.1 判断当前任务的父任务是否还有子任务 |
| 357 | - Long childrenTaskCount = getChildrenTaskCount(bpmTaskExtDO.getTaskId()); | |
| 360 | + Long childrenTaskCount = getChildrenTaskCount(parentTaskExt.getTaskId()); | |
| 358 | 361 | if (childrenTaskCount > 0) { |
| 359 | 362 | continue; |
| 360 | 363 | } |
| 361 | 364 | // 3.2 没有子任务,判断当前任务状态是否为 ADD_SIGN_BEFORE 待前加签任务完成 |
| 362 | - if (BpmProcessInstanceResultEnum.ADD_SIGN_BEFORE.getResult().equals(bpmTaskExtDO.getResult())) { | |
| 365 | + if (BpmProcessInstanceResultEnum.SIGN_BEFORE.getResult().equals(parentTaskExt.getResult())) { | |
| 363 | 366 | // 3.3 需要修改该任务状态为处理中 |
| 364 | - taskService.resolveTask(bpmTaskExtDO.getTaskId()); | |
| 365 | - bpmTaskExtDO.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); | |
| 366 | - taskExtMapper.updateByTaskId(bpmTaskExtDO); | |
| 367 | + taskService.resolveTask(parentTaskExt.getTaskId()); | |
| 368 | + parentTaskExt.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); | |
| 369 | + taskExtMapper.updateByTaskId(parentTaskExt); | |
| 367 | 370 | } |
| 368 | 371 | // 3.4 清空 scopeType 字段,用于任务没有子任务时使用该方法,方便任务可以再次被不同的方式加签 |
| 369 | - parentTask = validateTaskExist(bpmTaskExtDO.getTaskId()); | |
| 372 | + parentTask = validateTaskExist(parentTaskExt.getTaskId()); | |
| 370 | 373 | clearTaskScopeTypeAndSave(parentTask); |
| 371 | 374 | } |
| 372 | 375 | |
| ... | ... | @@ -387,7 +390,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 387 | 390 | taskService.saveTask(task); |
| 388 | 391 | } |
| 389 | 392 | |
| 390 | - | |
| 391 | 393 | /** |
| 392 | 394 | * 获取子任务个数 |
| 393 | 395 | * |
| ... | ... | @@ -709,7 +711,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 709 | 711 | |
| 710 | 712 | @Override |
| 711 | 713 | @Transactional(rollbackFor = Exception.class) |
| 712 | - public void addSignTask(Long userId, BpmTaskAddSignReqVO reqVO) { | |
| 714 | + public void createSignTask(Long userId, BpmTaskAddSignReqVO reqVO) { | |
| 713 | 715 | // 1. 获取和校验任务 |
| 714 | 716 | TaskEntityImpl taskEntity = validateAddSign(userId, reqVO); |
| 715 | 717 | List<AdminUserRespDTO> userList = adminUserApi.getUserList(reqVO.getUserIdList()); |
| ... | ... | @@ -728,7 +730,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 728 | 730 | // 2.3 更新扩展表状态 |
| 729 | 731 | taskExtMapper.updateByTaskId( |
| 730 | 732 | new BpmTaskExtDO().setTaskId(taskEntity.getId()) |
| 731 | - .setResult(BpmProcessInstanceResultEnum.ADD_SIGN_BEFORE.getResult()) | |
| 733 | + .setResult(BpmProcessInstanceResultEnum.SIGN_BEFORE.getResult()) | |
| 732 | 734 | .setReason(reqVO.getReason())); |
| 733 | 735 | } |
| 734 | 736 | // 2.4 记录加签方式,完成任务时需要用到判断 |
| ... | ... | @@ -737,12 +739,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 737 | 739 | taskService.saveTask(taskEntity); |
| 738 | 740 | |
| 739 | 741 | // 3. 创建加签任务 |
| 740 | - createAddSignChildrenTasks(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity); | |
| 742 | + createSignTask(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity); | |
| 741 | 743 | |
| 742 | 744 | // 4. 记录加签 comment,拼接结果为: [当前用户]向前加签/向后加签给了[多个用户],理由为:reason |
| 743 | 745 | AdminUserRespDTO currentUser = adminUserApi.getUser(userId); |
| 744 | - String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getTemplateComment(), currentUser.getNickname(), BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()), | |
| 745 | - String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); | |
| 746 | + String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), currentUser.getNickname(), | |
| 747 | + BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()), String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); | |
| 746 | 748 | taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), |
| 747 | 749 | BpmCommentTypeEnum.ADD_SIGN.getType().toString(), comment); |
| 748 | 750 | } |
| ... | ... | @@ -786,7 +788,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 786 | 788 | * @param addSingUserIdList 被加签的用户 ID |
| 787 | 789 | * @param taskEntity 被加签的任务 |
| 788 | 790 | */ |
| 789 | - private void createAddSignChildrenTasks(List<String> addSingUserIdList, TaskEntityImpl taskEntity) { | |
| 791 | + private void createSignTask(List<String> addSingUserIdList, TaskEntityImpl taskEntity) { | |
| 790 | 792 | if (CollUtil.isEmpty(addSingUserIdList)) { |
| 791 | 793 | return; |
| 792 | 794 | } |
| ... | ... | @@ -795,23 +797,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 795 | 797 | if (StrUtil.isBlank(addSignId)) { |
| 796 | 798 | continue; |
| 797 | 799 | } |
| 798 | - createChildrenTask(taskEntity, addSignId); | |
| 800 | + createSignTask(taskEntity, addSignId); | |
| 799 | 801 | } |
| 800 | 802 | } |
| 801 | 803 | |
| 802 | 804 | /** |
| 803 | - * 创建子任务 | |
| 805 | + * 创建加签子任务 | |
| 804 | 806 | * |
| 805 | 807 | * @param parentTask 父任务 |
| 806 | 808 | * @param assignee 子任务的执行人 |
| 807 | - * @return | |
| 808 | 809 | */ |
| 809 | - private void createChildrenTask(TaskEntityImpl parentTask, String assignee) { | |
| 810 | + private void createSignTask(TaskEntityImpl parentTask, String assignee) { | |
| 810 | 811 | // 1. 生成子任务 |
| 811 | 812 | TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); |
| 812 | 813 | task = BpmTaskConvert.INSTANCE.convert(task, parentTask); |
| 813 | 814 | if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { |
| 814 | - // 2.1 前加签,才设置审批人,否则设置 owner | |
| 815 | + // 2.1 前加签,设置审批人 | |
| 815 | 816 | task.setAssignee(assignee); |
| 816 | 817 | } else { |
| 817 | 818 | // 2.2.1 设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 |
| ... | ... | @@ -825,9 +826,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 825 | 826 | |
| 826 | 827 | @Override |
| 827 | 828 | @Transactional(rollbackFor = Exception.class) |
| 828 | - public void subSignTask(Long userId, BpmTaskSubSignReqVO reqVO) { | |
| 829 | + public void deleteSignTask(Long userId, BpmTaskSubSignReqVO reqVO) { | |
| 830 | + // 1.1 校验 task 可以被减签 | |
| 829 | 831 | Task task = validateSubSign(reqVO.getId()); |
| 830 | - AdminUserRespDTO user = adminUserApi.getUser(userId); | |
| 832 | + // 1.2 校验取消人存在 | |
| 831 | 833 | AdminUserRespDTO cancelUser = null; |
| 832 | 834 | if (StrUtil.isNotBlank(task.getAssignee())) { |
| 833 | 835 | cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())); |
| ... | ... | @@ -836,20 +838,24 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 836 | 838 | cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); |
| 837 | 839 | } |
| 838 | 840 | Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); |
| 839 | - //1. 获取所有需要删除的任务 ID ,包含当前任务和所有子任务 | |
| 841 | + | |
| 842 | + // 2. 删除任务和对应子任务 | |
| 843 | + // 2.1 获取所有需要删除的任务 ID ,包含当前任务和所有子任务 | |
| 840 | 844 | List<String> allTaskIdList = getAllChildTaskIds(task.getId()); |
| 841 | - //2. 删除任务和所有子任务 | |
| 845 | + // 2.2 删除任务和所有子任务 | |
| 842 | 846 | taskService.deleteTasks(allTaskIdList); |
| 843 | - //3. 修改扩展表状态为取消 | |
| 847 | + // 2.3 修改扩展表状态为取消 | |
| 848 | + AdminUserRespDTO user = adminUserApi.getUser(userId); | |
| 844 | 849 | taskExtMapper.updateBatchByTaskIdList(allTaskIdList, new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()) |
| 845 | 850 | .setReason(StrUtil.format("由于{}操作[减签],任务被取消", user.getNickname()))); |
| 846 | - //4.记录日志到父任务中 先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 | |
| 847 | - String comment = StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getTemplateComment(), user.getNickname(), cancelUser.getNickname()); | |
| 851 | + | |
| 852 | + // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 | |
| 853 | + String comment = StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname()); | |
| 848 | 854 | taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), |
| 849 | 855 | BpmCommentTypeEnum.SUB_SIGN.getType().toString(), comment); |
| 850 | - //5. 处理当前任务的父任务 | |
| 851 | - this.handleParentTask(task); | |
| 852 | 856 | |
| 857 | + // 4. 处理当前任务的父任务 | |
| 858 | + handleParentTask(task); | |
| 853 | 859 | } |
| 854 | 860 | |
| 855 | 861 | /** |
| ... | ... | @@ -860,14 +866,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 860 | 866 | */ |
| 861 | 867 | private Task validateSubSign(String id) { |
| 862 | 868 | Task task = validateTaskExist(id); |
| 863 | - //必须有parentId | |
| 864 | - if (StrUtil.isEmpty(task.getParentTaskId())) { | |
| 869 | + | |
| 870 | + // 必须有 scopeType | |
| 871 | + String scopeType = task.getScopeType(); | |
| 872 | + if (StrUtil.isEmpty(scopeType)) { | |
| 873 | + throw exception(TASK_SUB_SIGN_NO_PARENT); | |
| 874 | + } | |
| 875 | + // 并且值为 向前和向后加签 | |
| 876 | + if (!validateSignType(scopeType)) { | |
| 865 | 877 | throw exception(TASK_SUB_SIGN_NO_PARENT); |
| 866 | 878 | } |
| 867 | 879 | return task; |
| 868 | 880 | } |
| 869 | 881 | |
| 870 | 882 | /** |
| 883 | + * 判断当前类型是否为加签 | |
| 884 | + * @param scopeType 任务的 scopeType | |
| 885 | + * @return 当前 scopeType 为加签则返回 true | |
| 886 | + */ | |
| 887 | + private boolean validateSignType(String scopeType){ | |
| 888 | + return StrUtil.equalsAny(scopeType,BpmTaskAddSignTypeEnum.BEFORE.getType(),scopeType, BpmTaskAddSignTypeEnum.AFTER.getType()); | |
| 889 | + } | |
| 890 | + | |
| 891 | + /** | |
| 871 | 892 | * 获取所有要被取消的删除的任务 ID 集合 |
| 872 | 893 | * |
| 873 | 894 | * @param parentTaskId 父级任务ID |
| ... | ... | @@ -875,25 +896,40 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 875 | 896 | */ |
| 876 | 897 | public List<String> getAllChildTaskIds(String parentTaskId) { |
| 877 | 898 | List<String> allChildTaskIds = new ArrayList<>(); |
| 878 | - //1. 先将自己放入 | |
| 879 | - allChildTaskIds.add(parentTaskId); | |
| 880 | - //2. 递归获取子级 | |
| 881 | - recursiveGetChildTaskIds(parentTaskId, allChildTaskIds); | |
| 899 | + // 1. 递归获取子级 | |
| 900 | + Stack<String> stack = new Stack<>(); | |
| 901 | + // 1.1 将根任务ID入栈 | |
| 902 | + stack.push(parentTaskId); | |
| 903 | + //控制遍历的次数不超过 Byte.MAX_VALUE,避免脏数据造成死循环 | |
| 904 | + int count = 0; | |
| 905 | + // TODO @海:< 的前后空格,要注意哈; | |
| 906 | + while (!stack.isEmpty() && count<Byte.MAX_VALUE) { | |
| 907 | + // 1.2 弹出栈顶任务ID | |
| 908 | + String taskId = stack.pop(); | |
| 909 | + // 1.3 将任务ID添加到结果集合中 | |
| 910 | + allChildTaskIds.add(taskId); | |
| 911 | + // 1.4 获取该任务的子任务列表 | |
| 912 | + // TODO @海:有个更高效的写法;一次性去 in 一层;不然每个节点,都去查询一次 db, 太浪费了;每次 in,最终就是 O(h) 查询,而不是 O(n) 查询; | |
| 913 | + List<String> childrenTaskIdList = getChildrenTaskIdList(taskId); | |
| 914 | + if (CollUtil.isNotEmpty(childrenTaskIdList)) { | |
| 915 | + for (String childTaskId : childrenTaskIdList) { | |
| 916 | + // 1.5 将子任务ID入栈,以便后续处理 | |
| 917 | + stack.push(childTaskId); | |
| 918 | + } | |
| 919 | + } | |
| 920 | + count++; | |
| 921 | + } | |
| 882 | 922 | return allChildTaskIds; |
| 883 | 923 | } |
| 884 | 924 | |
| 885 | 925 | /** |
| 886 | - * 递归处理子级任务 | |
| 926 | + * 获取指定父级任务的所有子任务 ID 集合 | |
| 887 | 927 | * |
| 888 | - * @param taskId 当前任务ID | |
| 889 | - * @param taskIds 结果 | |
| 928 | + * @param parentTaskId 父任务 ID | |
| 929 | + * @return 所有子任务的 ID 集合 | |
| 890 | 930 | */ |
| 891 | - private void recursiveGetChildTaskIds(String taskId, List<String> taskIds) { | |
| 892 | - List<String> childrenTaskIdList = getChildrenTaskIdList(taskId); | |
| 893 | - for (String childTaskId : childrenTaskIdList) { | |
| 894 | - taskIds.add(childTaskId); // 将子任务的ID添加到集合中 | |
| 895 | - recursiveGetChildTaskIds(childTaskId, taskIds); // 递归获取子任务的子任务 | |
| 896 | - } | |
| 931 | + private List<String> getChildrenTaskIdList(String parentTaskId) { | |
| 932 | + return convertList(getChildrenTaskList0(parentTaskId), Task::getId); | |
| 897 | 933 | } |
| 898 | 934 | |
| 899 | 935 | /** |
| ... | ... | @@ -902,33 +938,32 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 902 | 938 | * @param parentTaskId 父任务 ID |
| 903 | 939 | * @return 所有子任务的 ID 集合 |
| 904 | 940 | */ |
| 905 | - private List<String> getChildrenTaskIdList(String parentTaskId) { | |
| 941 | + private List<Task> getChildrenTaskList0(String parentTaskId) { | |
| 906 | 942 | String tableName = managementService.getTableName(TaskEntity.class); |
| 907 | - String sql = "select ID_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; | |
| 908 | - List<Task> childrenTaskList = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); | |
| 909 | - return convertList(childrenTaskList, Task::getId); | |
| 943 | + // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 | |
| 944 | + String sql = "select ID_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; | |
| 945 | + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); | |
| 910 | 946 | } |
| 911 | 947 | |
| 948 | + | |
| 912 | 949 | @Override |
| 913 | - public List<BpmTaskSubSignRespVO> getChildrenTaskList(String taskId) { | |
| 914 | - List<String> childrenTaskIdList = getChildrenTaskIdList(taskId); | |
| 915 | - if (CollUtil.isEmpty(childrenTaskIdList)) { | |
| 950 | + public List<BpmTaskSubSignRespVO> getChildrenTaskList(String parentId) { | |
| 951 | + // 1. 只查询进行中的任务 后加签的任务,可能不存在 assignee,所以还需要查询 owner | |
| 952 | + List<Task> taskList = getChildrenTaskList0(parentId); | |
| 953 | + if (CollUtil.isEmpty(taskList)) { | |
| 916 | 954 | return Collections.emptyList(); |
| 917 | 955 | } |
| 918 | - //1. 只查询进行中的任务 | |
| 919 | - List<BpmTaskExtDO> bpmTaskExtDOList = taskExtMapper.selectProcessListByTaskIds(childrenTaskIdList); | |
| 920 | - //2. 后加签的任务,可能不存在 assignee,所以还需要查询 owner | |
| 921 | - List<Task> taskList = taskService.createTaskQuery().taskIds(childrenTaskIdList).list(); | |
| 922 | - Map<String, Task> idTaskMap = convertMap(taskList, TaskInfo::getId); | |
| 923 | - //3. 将 owner 和 assignee 统一到一个集合中 | |
| 924 | - List<Long> userIds = taskList.stream() | |
| 925 | - .flatMap(control -> | |
| 926 | - Stream.of(control.getAssignee(), control.getOwner()) | |
| 927 | - .filter(Objects::nonNull)) | |
| 928 | - .distinct() | |
| 929 | - .map(NumberUtils::parseLong) | |
| 930 | - .collect(Collectors.toList()); | |
| 956 | + List<String> childrenTaskIdList = convertList(taskList, Task::getId); | |
| 957 | + | |
| 958 | + // 2.1 将 owner 和 assignee 统一到一个集合中 | |
| 959 | + List<Long> userIds = convertListByFlatMap(taskList, control -> | |
| 960 | + Stream.of(NumberUtils.parseLong(control.getAssignee()), NumberUtils.parseLong(control.getOwner())) | |
| 961 | + .filter(Objects::nonNull)); | |
| 962 | + // 2.2 组装数据 | |
| 931 | 963 | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); |
| 932 | - return BpmTaskConvert.INSTANCE.convertList(bpmTaskExtDOList, userMap, idTaskMap); | |
| 964 | + List<BpmTaskExtDO> taskExtList = taskExtMapper.selectProcessListByTaskIds(childrenTaskIdList); | |
| 965 | + Map<String, Task> idTaskMap = convertMap(taskList, TaskInfo::getId); | |
| 966 | + return BpmTaskConvert.INSTANCE.convertList(taskExtList, userMap, idTaskMap); | |
| 933 | 967 | } |
| 968 | + | |
| 934 | 969 | } | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java
| ... | ... | @@ -147,7 +147,7 @@ public class DatabaseDocController { |
| 147 | 147 | */ |
| 148 | 148 | private static ProcessConfig buildProcessConfig() { |
| 149 | 149 | return ProcessConfig.builder() |
| 150 | - .ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_")) // 忽略表前缀 | |
| 150 | + .ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_", "FLW_")) // 忽略表前缀 | |
| 151 | 151 | .build(); |
| 152 | 152 | } |
| 153 | 153 | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java
| ... | ... | @@ -29,8 +29,6 @@ public class SecurityConfiguration { |
| 29 | 29 | .antMatchers("/swagger-resources/**").anonymous() |
| 30 | 30 | .antMatchers("/webjars/**").anonymous() |
| 31 | 31 | .antMatchers("/*/api-docs").anonymous(); |
| 32 | - // 积木报表 | |
| 33 | - registry.antMatchers("/jmreport/**").permitAll(); | |
| 34 | 32 | // Spring Boot Actuator 的安全配置 |
| 35 | 33 | registry.antMatchers("/actuator").anonymous() |
| 36 | 34 | .antMatchers("/actuator/**").anonymous(); | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/JobLogCleanJob.java renamed to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java
| 1 | -package cn.iocoder.yudao.module.infra.job.logger; | |
| 1 | +package cn.iocoder.yudao.module.infra.job.job; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 4 | 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| ... | ... | @@ -33,7 +33,7 @@ public class JobLogCleanJob implements JobHandler { |
| 33 | 33 | @TenantIgnore |
| 34 | 34 | public String execute(String param) { |
| 35 | 35 | Integer count = jobLogService.cleanJobLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); |
| 36 | - log.info("[count][定时执行清理定时任务日志数量 ({}) 个]", count); | |
| 36 | + log.info("[execute][定时执行清理定时任务日志数量 ({}) 个]", count); | |
| 37 | 37 | return String.format("定时执行清理定时任务日志数量 %s 个", count); |
| 38 | 38 | } |
| 39 | 39 | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/AccessLogCleanJob.java renamed to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java
| 1 | -package cn.iocoder.yudao.module.infra.job.job; | |
| 1 | +package cn.iocoder.yudao.module.infra.job.logger; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 4 | 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| ... | ... | @@ -34,7 +34,7 @@ public class AccessLogCleanJob implements JobHandler { |
| 34 | 34 | @TenantIgnore |
| 35 | 35 | public String execute(String param) { |
| 36 | 36 | Integer count = apiAccessLogService.cleanAccessLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); |
| 37 | - log.info("[count][定时执行清理访问日志数量 ({}) 个]", count); | |
| 37 | + log.info("[execute][定时执行清理访问日志数量 ({}) 个]", count); | |
| 38 | 38 | return String.format("定时执行清理错误日志数量 %s 个", count); |
| 39 | 39 | } |
| 40 | 40 | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/ErrorLogCleanJob.java renamed to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java
| 1 | -package cn.iocoder.yudao.module.infra.job.job; | |
| 1 | +package cn.iocoder.yudao.module.infra.job.logger; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 4 | 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| ... | ... | @@ -34,7 +34,7 @@ public class ErrorLogCleanJob implements JobHandler { |
| 34 | 34 | @TenantIgnore |
| 35 | 35 | public String execute(String param) { |
| 36 | 36 | Integer count = apiErrorLogService.cleanErrorLog(JOB_CLEAN_RETAIN_DAY,DELETE_LIMIT); |
| 37 | - log.info("[count][定时执行清理错误日志数量 ({}) 个]", count); | |
| 37 | + log.info("[execute][定时执行清理错误日志数量 ({}) 个]", count); | |
| 38 | 38 | return String.format("定时执行清理错误日志数量 %s 个", count); |
| 39 | 39 | } |
| 40 | 40 | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
| ... | ... | @@ -58,9 +58,9 @@ public class FileConfigServiceImpl implements FileConfigService { |
| 58 | 58 | FileConfigDO config = Objects.equals(CACHE_MASTER_ID, id) ? |
| 59 | 59 | fileConfigMapper.selectByMaster() : fileConfigMapper.selectById(id); |
| 60 | 60 | if (config != null) { |
| 61 | - fileClientFactory.createOrUpdateFileClient(id, config.getStorage(), config.getConfig()); | |
| 61 | + fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig()); | |
| 62 | 62 | } |
| 63 | - return fileClientFactory.getFileClient(id); | |
| 63 | + return fileClientFactory.getFileClient(null == config ? id : config.getId()); | |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | }); | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm
| ... | ... | @@ -28,5 +28,5 @@ export function delete${simpleClassName}(id: number) { |
| 28 | 28 | |
| 29 | 29 | // 导出${table.classComment} Excel |
| 30 | 30 | export function export${simpleClassName}(params) { |
| 31 | - return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') | |
| 31 | + return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') | |
| 32 | 32 | } | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm
| 1 | -import type { BasicColumn, FormSchema } from '@/components/Table' | |
| 2 | -import { useRender } from '@/components/Table' | |
| 3 | -import { DICT_TYPE, getDictOptions } from '@/utils/dict' | |
| 1 | +import type {BasicColumn, FormSchema} from '@/components/Table' | |
| 2 | +import {useRender} from '@/components/Table' | |
| 3 | +import {DICT_TYPE, getDictOptions} from '@/utils/dict' | |
| 4 | 4 | |
| 5 | 5 | export const columns: BasicColumn[] = [ |
| 6 | 6 | #foreach($column in $columns) |
| ... | ... | @@ -50,7 +50,7 @@ export const searchFormSchema: FormSchema[] = [ |
| 50 | 50 | field: '${javaField}', |
| 51 | 51 | #if ($column.htmlType == "input") |
| 52 | 52 | component: 'Input', |
| 53 | - #elseif ($column.htmlType == "select" || $column.htmlType == "radio") | |
| 53 | + #elseif ($column.htmlType == "select") | |
| 54 | 54 | component: 'Select', |
| 55 | 55 | componentProps: { |
| 56 | 56 | #if ("" != $dictType)## 设置了 dictType 数据字典的情况 |
| ... | ... | @@ -59,6 +59,15 @@ export const searchFormSchema: FormSchema[] = [ |
| 59 | 59 | options: [], |
| 60 | 60 | #end |
| 61 | 61 | }, |
| 62 | + #elseif ($column.htmlType == "radio") | |
| 63 | + component: 'Radio', | |
| 64 | + componentProps: { | |
| 65 | + #if ("" != $dictType)## 设置了 dictType 数据字典的情况 | |
| 66 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase()), | |
| 67 | + #else## 未设置 dictType 数据字典的情况 | |
| 68 | + options: [], | |
| 69 | + #end | |
| 70 | + }, | |
| 62 | 71 | #elseif($column.htmlType == "datetime") |
| 63 | 72 | component: 'RangePicker', |
| 64 | 73 | #end |
| ... | ... | @@ -181,7 +190,8 @@ export const updateFormSchema: FormSchema[] = [ |
| 181 | 190 | fileType: 'file', |
| 182 | 191 | maxCount: 1, |
| 183 | 192 | }, |
| 184 | - #elseif($column.htmlType == "editor")## 文本编辑器component: 'Editor', | |
| 193 | + #elseif($column.htmlType == "editor")## 文本编辑器 | |
| 194 | + component: 'Editor', | |
| 185 | 195 | #elseif($column.htmlType == "select")## 下拉框 |
| 186 | 196 | component: 'Select', |
| 187 | 197 | componentProps: { | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
| 1 | 1 | <script lang="ts" setup> |
| 2 | -import ${ simpleClassName }Modal from './${simpleClassName}Modal.vue' | |
| 2 | +import ${simpleClassName}Modal from './${simpleClassName}Modal.vue' | |
| 3 | 3 | import { columns, searchFormSchema } from './${classNameVar}.data' |
| 4 | 4 | import { useI18n } from '@/hooks/web/useI18n' |
| 5 | 5 | import { useMessage } from '@/hooks/web/useMessage' |
| 6 | 6 | import { useModal } from '@/components/Modal' |
| 7 | -import { IconEnum } from '@/enums/appEnum' | |
| 8 | -import { BasicTable, useTable, TableAction } from '@/components/Table' | |
| 9 | -import { delete${ simpleClassName }, export${ simpleClassName }, get${ simpleClassName } Page } from '@/api/${table.moduleName}/${classNameVar}' | |
| 7 | +import { useTable } from '@/components/Table' | |
| 8 | +import { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${classNameVar}' | |
| 10 | 9 | |
| 11 | 10 | defineOptions({ name: '${table.className}' }) |
| 12 | 11 | |
| ... | ... | @@ -16,7 +15,7 @@ const [registerModal, { openModal }] = useModal() |
| 16 | 15 | |
| 17 | 16 | const [registerTable, { getForm, reload }] = useTable({ |
| 18 | 17 | title: '${table.classComment}列表', |
| 19 | - api: get${ simpleClassName }Page, | |
| 18 | + api: get${simpleClassName}Page, | |
| 20 | 19 | columns, |
| 21 | 20 | formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
| 22 | 21 | useSearchForm: true, |
| ... | ... | @@ -43,14 +42,14 @@ async function handleExport() { |
| 43 | 42 | iconType: 'warning', |
| 44 | 43 | content: t('common.exportMessage'), |
| 45 | 44 | async onOk() { |
| 46 | - await export${ simpleClassName } (getForm().getFieldsValue()) | |
| 45 | + await export${simpleClassName}(getForm().getFieldsValue()) | |
| 47 | 46 | createMessage.success(t('common.exportSuccessText')) |
| 48 | 47 | }, |
| 49 | 48 | }) |
| 50 | 49 | } |
| 51 | 50 | |
| 52 | 51 | async function handleDelete(record: Recordable) { |
| 53 | - await delete${ simpleClassName } (record.id) | |
| 52 | + await delete${simpleClassName}(record.id) | |
| 54 | 53 | createMessage.success(t('common.delSuccessText')) |
| 55 | 54 | reload() |
| 56 | 55 | } |
| ... | ... | @@ -62,7 +61,7 @@ async function handleDelete(record: Recordable) { |
| 62 | 61 | <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate"> |
| 63 | 62 | {{ t('action.create') }} |
| 64 | 63 | </a-button> |
| 65 | - <a-button type="warning" v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport"> | |
| 64 | + <a-button v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport"> | |
| 66 | 65 | {{ t('action.export') }} |
| 67 | 66 | </a-button> |
| 68 | 67 | </template> | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java
| ... | ... | @@ -258,12 +258,12 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { |
| 258 | 258 | Long id = fileConfig.getId(); |
| 259 | 259 | // mock 获得 Client |
| 260 | 260 | FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig()); |
| 261 | - when(fileClientFactory.getFileClient(eq(0L))).thenReturn(fileClient); | |
| 261 | + when(fileClientFactory.getFileClient(eq(fileConfig.getId()))).thenReturn(fileClient); | |
| 262 | 262 | |
| 263 | 263 | // 调用,并断言 |
| 264 | 264 | assertSame(fileClient, fileConfigService.getMasterFileClient()); |
| 265 | 265 | // 断言缓存 |
| 266 | - verify(fileClientFactory).createOrUpdateFileClient(eq(0L), eq(fileConfig.getStorage()), | |
| 266 | + verify(fileClientFactory).createOrUpdateFileClient(eq(fileConfig.getId()), eq(fileConfig.getStorage()), | |
| 267 | 267 | eq(fileConfig.getConfig())); |
| 268 | 268 | } |
| 269 | 269 | ... | ... |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/comment/dto/ProductCommentCreateReqDTO.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.api.comment.dto; |
| 2 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | 4 | |
| 5 | +import javax.validation.constraints.NotNull; | |
| 5 | 6 | import java.util.List; |
| 6 | 7 | |
| 7 | 8 | /** |
| ... | ... | @@ -15,6 +16,7 @@ public class ProductCommentCreateReqDTO { |
| 15 | 16 | /** |
| 16 | 17 | * 商品 SKU 编号 |
| 17 | 18 | */ |
| 19 | + @NotNull(message = "商品 SKU 编号不能为空") | |
| 18 | 20 | private Long skuId; |
| 19 | 21 | /** |
| 20 | 22 | * 订单编号 |
| ... | ... | @@ -26,20 +28,19 @@ public class ProductCommentCreateReqDTO { |
| 26 | 28 | private Long orderItemId; |
| 27 | 29 | |
| 28 | 30 | /** |
| 29 | - * 评分星级 1-5 分 | |
| 30 | - */ | |
| 31 | - private Integer scores; | |
| 32 | - /** | |
| 33 | 31 | * 描述星级 1-5 分 |
| 34 | 32 | */ |
| 33 | + @NotNull(message = "描述星级不能为空") | |
| 35 | 34 | private Integer descriptionScores; |
| 36 | 35 | /** |
| 37 | 36 | * 服务星级 1-5 分 |
| 38 | 37 | */ |
| 38 | + @NotNull(message = "服务星级不能为空") | |
| 39 | 39 | private Integer benefitScores; |
| 40 | 40 | /** |
| 41 | 41 | * 评论内容 |
| 42 | 42 | */ |
| 43 | + @NotNull(message = "评论内容不能为空") | |
| 43 | 44 | private String content; |
| 44 | 45 | /** |
| 45 | 46 | * 评论图片地址数组,以逗号分隔最多上传 9 张 |
| ... | ... | @@ -49,11 +50,12 @@ public class ProductCommentCreateReqDTO { |
| 49 | 50 | /** |
| 50 | 51 | * 是否匿名 |
| 51 | 52 | */ |
| 53 | + @NotNull(message = "是否匿名不能为空") | |
| 52 | 54 | private Boolean anonymous; |
| 53 | 55 | /** |
| 54 | 56 | * 评价人 |
| 55 | 57 | */ |
| 58 | + @NotNull(message = "评价人不能为空") | |
| 56 | 59 | private Long userId; |
| 57 | 60 | |
| 58 | - | |
| 59 | 61 | } | ... | ... |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApi.java deleted
| 1 | -package cn.iocoder.yudao.module.product.api.property; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; | |
| 4 | - | |
| 5 | -import java.util.Collection; | |
| 6 | -import java.util.List; | |
| 7 | - | |
| 8 | -/** | |
| 9 | - * 商品属性值 API 接口 | |
| 10 | - * | |
| 11 | - * @author 芋道源码 | |
| 12 | - */ | |
| 13 | -public interface ProductPropertyValueApi { | |
| 14 | - | |
| 15 | - /** | |
| 16 | - * 根据编号数组,获得属性值列表 | |
| 17 | - * | |
| 18 | - * @param ids 编号数组 | |
| 19 | - * @return 属性值明细列表 | |
| 20 | - */ | |
| 21 | - List<ProductPropertyValueDetailRespDTO> getPropertyValueDetailList(Collection<Long> ids); | |
| 22 | - | |
| 23 | -} |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java
| ... | ... | @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.product.api.spu.dto; |
| 3 | 3 | import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | |
| 6 | -import java.util.List; | |
| 7 | - | |
| 8 | 6 | // TODO @LeeYan9: ProductSpuRespDTO |
| 9 | 7 | /** |
| 10 | 8 | * 商品 SPU 信息 Response DTO |
| ... | ... | @@ -27,55 +25,22 @@ public class ProductSpuRespDTO { |
| 27 | 25 | */ |
| 28 | 26 | private String name; |
| 29 | 27 | /** |
| 30 | - * 关键字 | |
| 31 | - */ | |
| 32 | - private String keyword; | |
| 33 | - /** | |
| 34 | 28 | * 单位 |
| 35 | 29 | * |
| 36 | 30 | * 对应 product_unit 数据字典 |
| 37 | 31 | */ |
| 38 | 32 | private Integer unit; |
| 39 | - /** | |
| 40 | - * 商品简介 | |
| 41 | - */ | |
| 42 | - private String introduction; | |
| 43 | - /** | |
| 44 | - * 商品详情 | |
| 45 | - */ | |
| 46 | - private String description; | |
| 47 | - // TODO @芋艿:是不是要删除 | |
| 48 | - /** | |
| 49 | - * 商品条码(一维码) | |
| 50 | - */ | |
| 51 | - private String barCode; | |
| 52 | 33 | |
| 53 | 34 | /** |
| 54 | 35 | * 商品分类编号 |
| 55 | 36 | */ |
| 56 | 37 | private Long categoryId; |
| 57 | 38 | /** |
| 58 | - * 商品品牌编号 | |
| 59 | - */ | |
| 60 | - private Long brandId; | |
| 61 | - /** | |
| 62 | 39 | * 商品封面图 |
| 63 | 40 | */ |
| 64 | 41 | private String picUrl; |
| 65 | - /** | |
| 66 | - * 商品轮播图 | |
| 67 | - */ | |
| 68 | - private List<String> sliderPicUrls; | |
| 69 | - /** | |
| 70 | - * 商品视频 | |
| 71 | - */ | |
| 72 | - private String videoUrl; | |
| 73 | 42 | |
| 74 | 43 | /** |
| 75 | - * 排序字段 | |
| 76 | - */ | |
| 77 | - private Integer sort; | |
| 78 | - /** | |
| 79 | 44 | * 商品状态 |
| 80 | 45 | * <p> |
| 81 | 46 | * 枚举 {@link ProductSpuStatusEnum} |
| ... | ... | @@ -124,22 +89,6 @@ public class ProductSpuRespDTO { |
| 124 | 89 | */ |
| 125 | 90 | private Integer giveIntegral; |
| 126 | 91 | |
| 127 | - // ========== 统计相关字段 ========= | |
| 128 | - | |
| 129 | - /** | |
| 130 | - * 商品销量 | |
| 131 | - */ | |
| 132 | - private Integer salesCount; | |
| 133 | - /** | |
| 134 | - * 虚拟销量 | |
| 135 | - */ | |
| 136 | - private Integer virtualSalesCount; | |
| 137 | - /** | |
| 138 | - * 商品点击量 | |
| 139 | - */ | |
| 140 | - private Integer clickCount; | |
| 141 | - | |
| 142 | - | |
| 143 | 92 | // ========== 分销相关字段 ========= |
| 144 | 93 | |
| 145 | 94 | /** | ... | ... |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/group/ProductGroupStyleEnum.java deleted
| 1 | -package cn.iocoder.yudao.module.product.enums.group; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | |
| 4 | -import lombok.AllArgsConstructor; | |
| 5 | -import lombok.Getter; | |
| 6 | - | |
| 7 | -import java.util.Arrays; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * 商品分组的样式枚举 | |
| 11 | - * | |
| 12 | - * @author 芋道源码 | |
| 13 | - */ | |
| 14 | -@Getter | |
| 15 | -@AllArgsConstructor | |
| 16 | -public enum ProductGroupStyleEnum implements IntArrayValuable { | |
| 17 | - | |
| 18 | - ONE(1, "每列一个"), | |
| 19 | - TWO(2, "每列两个"), | |
| 20 | - THREE(2, "每列三个"),; | |
| 21 | - | |
| 22 | - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductGroupStyleEnum::getStyle).toArray(); | |
| 23 | - | |
| 24 | - /** | |
| 25 | - * 列表样式 | |
| 26 | - */ | |
| 27 | - private final Integer style; | |
| 28 | - /** | |
| 29 | - * 状态名 | |
| 30 | - */ | |
| 31 | - private final String name; | |
| 32 | - | |
| 33 | - @Override | |
| 34 | - public int[] array() { | |
| 35 | - return ARRAYS; | |
| 36 | - } | |
| 37 | - | |
| 38 | -} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApiImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.product.api.property; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; | |
| 4 | -import cn.iocoder.yudao.module.product.convert.property.ProductPropertyValueConvert; | |
| 5 | -import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; | |
| 6 | -import org.springframework.stereotype.Service; | |
| 7 | -import org.springframework.validation.annotation.Validated; | |
| 8 | - | |
| 9 | -import javax.annotation.Resource; | |
| 10 | -import java.util.Collection; | |
| 11 | -import java.util.List; | |
| 12 | - | |
| 13 | -/** | |
| 14 | - * 商品属性值 API 实现类 | |
| 15 | - * | |
| 16 | - * @author 芋道源码 | |
| 17 | - */ | |
| 18 | -@Service | |
| 19 | -@Validated | |
| 20 | -public class ProductPropertyValueApiImpl implements ProductPropertyValueApi { | |
| 21 | - | |
| 22 | - @Resource | |
| 23 | - private ProductPropertyValueService productPropertyValueService; | |
| 24 | - | |
| 25 | - @Override | |
| 26 | - public List<ProductPropertyValueDetailRespDTO> getPropertyValueDetailList(Collection<Long> ids) { | |
| 27 | - return ProductPropertyValueConvert.INSTANCE.convertList02( | |
| 28 | - productPropertyValueService.getPropertyValueDetailList(ids)); | |
| 29 | - } | |
| 30 | - | |
| 31 | -} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.product.api.sku; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | 3 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 5 | 4 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; |
| 6 | 5 | import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; |
| ... | ... | @@ -11,7 +10,6 @@ import org.springframework.validation.annotation.Validated; |
| 11 | 10 | |
| 12 | 11 | import javax.annotation.Resource; |
| 13 | 12 | import java.util.Collection; |
| 14 | -import java.util.Collections; | |
| 15 | 13 | import java.util.List; |
| 16 | 14 | |
| 17 | 15 | /** |
| ... | ... | @@ -35,18 +33,12 @@ public class ProductSkuApiImpl implements ProductSkuApi { |
| 35 | 33 | |
| 36 | 34 | @Override |
| 37 | 35 | public List<ProductSkuRespDTO> getSkuList(Collection<Long> ids) { |
| 38 | - if (CollUtil.isEmpty(ids)) { | |
| 39 | - return Collections.emptyList(); | |
| 40 | - } | |
| 41 | 36 | List<ProductSkuDO> skus = productSkuService.getSkuList(ids); |
| 42 | 37 | return ProductSkuConvert.INSTANCE.convertList04(skus); |
| 43 | 38 | } |
| 44 | 39 | |
| 45 | 40 | @Override |
| 46 | 41 | public List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds) { |
| 47 | - if (CollUtil.isEmpty(spuIds)) { | |
| 48 | - return Collections.emptyList(); | |
| 49 | - } | |
| 50 | 42 | List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spuIds); |
| 51 | 43 | return ProductSkuConvert.INSTANCE.convertList04(skus); |
| 52 | 44 | } | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java
| ... | ... | @@ -27,9 +27,6 @@ public class ProductSpuApiImpl implements ProductSpuApi { |
| 27 | 27 | |
| 28 | 28 | @Override |
| 29 | 29 | public List<ProductSpuRespDTO> getSpuList(Collection<Long> ids) { |
| 30 | - if (CollectionUtil.isEmpty(ids)) { | |
| 31 | - return Collections.emptyList(); | |
| 32 | - } | |
| 33 | 30 | return ProductSpuConvert.INSTANCE.convertList2(spuService.getSpuList(ids)); |
| 34 | 31 | } |
| 35 | 32 | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyValueConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.product.convert.property; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | |
| 5 | -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 6 | -import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; | |
| 7 | 4 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; |
| 8 | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; |
| 9 | 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; |
| 10 | -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; | |
| 11 | 7 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; |
| 12 | -import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; | |
| 13 | 8 | import org.mapstruct.Mapper; |
| 14 | 9 | import org.mapstruct.factory.Mappers; |
| 15 | 10 | |
| 16 | 11 | import java.util.List; |
| 17 | -import java.util.Map; | |
| 18 | - | |
| 19 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | |
| 20 | 12 | |
| 21 | 13 | /** |
| 22 | 14 | * 属性值 Convert |
| ... | ... | @@ -38,18 +30,4 @@ public interface ProductPropertyValueConvert { |
| 38 | 30 | |
| 39 | 31 | PageResult<ProductPropertyValueRespVO> convertPage(PageResult<ProductPropertyValueDO> page); |
| 40 | 32 | |
| 41 | - default List<ProductPropertyValueDetailRespBO> convertList(List<ProductPropertyValueDO> values, List<ProductPropertyDO> keys) { | |
| 42 | - Map<Long, ProductPropertyDO> keyMap = convertMap(keys, ProductPropertyDO::getId); | |
| 43 | - return CollectionUtils.convertList(values, value -> { | |
| 44 | - ProductPropertyValueDetailRespBO valueDetail = new ProductPropertyValueDetailRespBO() | |
| 45 | - .setValueId(value.getId()).setValueName(value.getName()); | |
| 46 | - // 设置属性项 | |
| 47 | - MapUtils.findAndThen(keyMap, value.getPropertyId(), | |
| 48 | - key -> valueDetail.setPropertyId(key.getId()).setPropertyName(key.getName())); | |
| 49 | - return valueDetail; | |
| 50 | - }); | |
| 51 | - } | |
| 52 | - | |
| 53 | - List<ProductPropertyValueDetailRespDTO> convertList02(List<ProductPropertyValueDetailRespBO> list); | |
| 54 | - | |
| 55 | 33 | } | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueService.java
| ... | ... | @@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.Produc |
| 5 | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; |
| 6 | 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; |
| 8 | -import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; | |
| 9 | 8 | |
| 10 | 9 | import java.util.Collection; |
| 11 | 10 | import java.util.List; |
| ... | ... | @@ -57,14 +56,6 @@ public interface ProductPropertyValueService { |
| 57 | 56 | List<ProductPropertyValueDO> getPropertyValueListByPropertyId(Collection<Long> propertyIds); |
| 58 | 57 | |
| 59 | 58 | /** |
| 60 | - * 根据编号数组,获得属性值列表 | |
| 61 | - * | |
| 62 | - * @param ids 编号数组 | |
| 63 | - * @return 属性值明细列表 | |
| 64 | - */ | |
| 65 | - List<ProductPropertyValueDetailRespBO> getPropertyValueDetailList(Collection<Long> ids); | |
| 66 | - | |
| 67 | - /** | |
| 68 | 59 | * 根据属性项编号,活的属性值数量 |
| 69 | 60 | * |
| 70 | 61 | * @param propertyId 属性项编号数 | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.product.service.property; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 4 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; |
| 6 | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; |
| 7 | 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; |
| 8 | 7 | import cn.iocoder.yudao.module.product.convert.property.ProductPropertyValueConvert; |
| 9 | -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; | |
| 10 | 8 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; |
| 11 | 9 | import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper; |
| 12 | -import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; | |
| 13 | 10 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; |
| 14 | 11 | import org.springframework.context.annotation.Lazy; |
| 15 | 12 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -17,11 +14,9 @@ import org.springframework.validation.annotation.Validated; |
| 17 | 14 | |
| 18 | 15 | import javax.annotation.Resource; |
| 19 | 16 | import java.util.Collection; |
| 20 | -import java.util.Collections; | |
| 21 | 17 | import java.util.List; |
| 22 | 18 | |
| 23 | 19 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 24 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 25 | 20 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_EXISTS; |
| 26 | 21 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS; |
| 27 | 22 | |
| ... | ... | @@ -100,23 +95,6 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ |
| 100 | 95 | } |
| 101 | 96 | |
| 102 | 97 | @Override |
| 103 | - public List<ProductPropertyValueDetailRespBO> getPropertyValueDetailList(Collection<Long> ids) { | |
| 104 | - // 获得属性值列表 | |
| 105 | - if (CollUtil.isEmpty(ids)) { | |
| 106 | - return Collections.emptyList(); | |
| 107 | - } | |
| 108 | - List<ProductPropertyValueDO> values = productPropertyValueMapper.selectBatchIds(ids); | |
| 109 | - if (CollUtil.isEmpty(values)) { | |
| 110 | - return Collections.emptyList(); | |
| 111 | - } | |
| 112 | - // 获得属性项列表 | |
| 113 | - List<ProductPropertyDO> keys = productPropertyService.getPropertyList( | |
| 114 | - convertSet(values, ProductPropertyValueDO::getPropertyId)); | |
| 115 | - // 组装明细 | |
| 116 | - return ProductPropertyValueConvert.INSTANCE.convertList(values, keys); | |
| 117 | - } | |
| 118 | - | |
| 119 | - @Override | |
| 120 | 98 | public Integer getPropertyValueCountByPropertyId(Long propertyId) { |
| 121 | 99 | return productPropertyValueMapper.selectCountByPropertyId(propertyId); |
| 122 | 100 | } | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/bo/ProductPropertyValueDetailRespBO.java deleted
| 1 | -package cn.iocoder.yudao.module.product.service.property.bo; | |
| 2 | - | |
| 3 | -import lombok.Data; | |
| 4 | - | |
| 5 | -/** | |
| 6 | - * 商品属性项的明细 Response BO | |
| 7 | - * | |
| 8 | - * @author 芋道源码 | |
| 9 | - */ | |
| 10 | -@Data | |
| 11 | -public class ProductPropertyValueDetailRespBO { | |
| 12 | - | |
| 13 | - /** | |
| 14 | - * 属性的编号 | |
| 15 | - */ | |
| 16 | - private Long propertyId; | |
| 17 | - | |
| 18 | - /** | |
| 19 | - * 属性的名称 | |
| 20 | - */ | |
| 21 | - private String propertyName; | |
| 22 | - | |
| 23 | - /** | |
| 24 | - * 属性值的编号 | |
| 25 | - */ | |
| 26 | - private Long valueId; | |
| 27 | - | |
| 28 | - /** | |
| 29 | - * 属性值的名称 | |
| 30 | - */ | |
| 31 | - private String valueName; | |
| 32 | - | |
| 33 | -} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
| ... | ... | @@ -153,6 +153,9 @@ public class ProductSkuServiceImpl implements ProductSkuService { |
| 153 | 153 | |
| 154 | 154 | @Override |
| 155 | 155 | public List<ProductSkuDO> getSkuListBySpuId(Collection<Long> spuIds) { |
| 156 | + if (CollUtil.isEmpty(spuIds)) { | |
| 157 | + return Collections.emptyList(); | |
| 158 | + } | |
| 156 | 159 | return productSkuMapper.selectListBySpuId(spuIds); |
| 157 | 160 | } |
| 158 | 161 | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
| ... | ... | @@ -188,6 +188,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 188 | 188 | |
| 189 | 189 | @Override |
| 190 | 190 | public List<ProductSpuDO> getSpuList(Collection<Long> ids) { |
| 191 | + if (CollUtil.isEmpty(ids)) { | |
| 192 | + return Collections.emptyList(); | |
| 193 | + } | |
| 191 | 194 | return productSpuMapper.selectBatchIds(ids); |
| 192 | 195 | } |
| 193 | 196 | ... | ... |
yudao-module-mall/yudao-module-promotion-api/pom.xml
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationActivityApi.java deleted
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
| ... | ... | @@ -42,14 +42,6 @@ public interface CombinationRecordApi { |
| 42 | 42 | boolean isCombinationRecordSuccess(Long userId, Long orderId); |
| 43 | 43 | |
| 44 | 44 | /** |
| 45 | - * 更新拼团状态为【失败】 | |
| 46 | - * | |
| 47 | - * @param userId 用户编号 | |
| 48 | - * @param orderId 订单编号 | |
| 49 | - */ | |
| 50 | - void updateRecordStatusToFailed(Long userId, Long orderId); | |
| 51 | - | |
| 52 | - /** | |
| 53 | 45 | * 【下单前】校验是否满足拼团活动条件 |
| 54 | 46 | * |
| 55 | 47 | * 如果校验失败,则抛出业务异常 | ... | ... |
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApi.java deleted
| 1 | -package cn.iocoder.yudao.module.promotion.api.price; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; | |
| 4 | -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; | |
| 5 | - | |
| 6 | -/** | |
| 7 | - * 价格 API 接口 | |
| 8 | - * | |
| 9 | - * @author 芋道源码 | |
| 10 | - */ | |
| 11 | -public interface PriceApi { | |
| 12 | - | |
| 13 | - /** | |
| 14 | - * 计算商品的价格 | |
| 15 | - * | |
| 16 | - * @param calculateReqDTO 价格请求 | |
| 17 | - * @return 价格相应 | |
| 18 | - */ | |
| 19 | - PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO); | |
| 20 | - | |
| 21 | -} |
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/CouponMeetRespDTO.java deleted
| 1 | -package cn.iocoder.yudao.module.promotion.api.price.dto; | |
| 2 | - | |
| 3 | -import lombok.Data; | |
| 4 | - | |
| 5 | -/** | |
| 6 | - * 优惠劵的匹配信息 Response DTO | |
| 7 | - * | |
| 8 | - * why 放在 price 包下?主要获取的时候,需要涉及到较多的价格计算逻辑,放在 price 可以更好的复用逻辑 | |
| 9 | - * | |
| 10 | - * @author 芋道源码 | |
| 11 | - */ | |
| 12 | -@Data | |
| 13 | -public class CouponMeetRespDTO { | |
| 14 | - | |
| 15 | - /** | |
| 16 | - * 优惠劵编号 | |
| 17 | - */ | |
| 18 | - private Long id; | |
| 19 | - | |
| 20 | - // ========== 非优惠劵的基本信息字段 ========== | |
| 21 | - /** | |
| 22 | - * 是否匹配 | |
| 23 | - */ | |
| 24 | - private Boolean meet; | |
| 25 | - /** | |
| 26 | - * 不匹配的提示,即 {@link #meet} = true 才有值 | |
| 27 | - * | |
| 28 | - * 例如说: | |
| 29 | - * 1. 所结算商品没有符合条件的商品 | |
| 30 | - * 2. 差 XXX 元可用优惠劵 | |
| 31 | - * 3. 优惠劵未到使用时间 | |
| 32 | - */ | |
| 33 | - private String meetTip; | |
| 34 | - | |
| 35 | -} |
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java deleted
| 1 | -package cn.iocoder.yudao.module.promotion.api.price.dto; | |
| 2 | - | |
| 3 | -import lombok.Data; | |
| 4 | - | |
| 5 | -import javax.validation.constraints.Min; | |
| 6 | -import javax.validation.constraints.NotNull; | |
| 7 | -import java.util.List; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * 价格计算 Request DTO | |
| 11 | - * | |
| 12 | - * @author 芋道源码 | |
| 13 | - */ | |
| 14 | -@Data | |
| 15 | -@Deprecated | |
| 16 | -public class PriceCalculateReqDTO { | |
| 17 | - | |
| 18 | - /** | |
| 19 | - * 用户编号 | |
| 20 | - * | |
| 21 | - * 对应 MemberUserDO 的 id 编号 | |
| 22 | - */ | |
| 23 | - private Long userId; | |
| 24 | - | |
| 25 | - /** | |
| 26 | - * 优惠劵编号 | |
| 27 | - */ | |
| 28 | - private Long couponId; | |
| 29 | - | |
| 30 | - /** | |
| 31 | - * 收货地址编号 | |
| 32 | - */ | |
| 33 | - private Long addressId; | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 商品 SKU 数组 | |
| 37 | - */ | |
| 38 | - @NotNull(message = "商品数组不能为空") | |
| 39 | - private List<Item> items; | |
| 40 | - | |
| 41 | - /** | |
| 42 | - * 商品 SKU | |
| 43 | - */ | |
| 44 | - @Data | |
| 45 | - public static class Item { | |
| 46 | - | |
| 47 | - /** | |
| 48 | - * SKU 编号 | |
| 49 | - */ | |
| 50 | - @NotNull(message = "商品 SKU 编号不能为空") | |
| 51 | - private Long skuId; | |
| 52 | - | |
| 53 | - /** | |
| 54 | - * SKU 数量 | |
| 55 | - */ | |
| 56 | - @NotNull(message = "商品 SKU 数量不能为空") | |
| 57 | - @Min(value = 0L, message = "商品 SKU 数量必须大于等于 0") // 可传递 0 数量,用于购物车未选中的情况 | |
| 58 | - private Integer count; | |
| 59 | - | |
| 60 | - } | |
| 61 | - | |
| 62 | -} |
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateRespDTO.java deleted
| 1 | -package cn.iocoder.yudao.module.promotion.api.price.dto; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; | |
| 4 | -import lombok.Data; | |
| 5 | - | |
| 6 | -import java.util.List; | |
| 7 | - | |
| 8 | -/** | |
| 9 | - * 价格计算 Response DTO | |
| 10 | - * | |
| 11 | - * 整体设计,参考 taobao 的技术文档: | |
| 12 | - * 1. <a href="https://developer.alibaba.com/docs/doc.htm?treeId=1&articleId=1029&docType=1">订单管理</a> | |
| 13 | - * 2. <a href="https://open.taobao.com/docV3.htm?docId=108471&docType=1">常用订单金额说明</a> | |
| 14 | - * | |
| 15 | - * 举个例子:<a href="https://img.alicdn.com/top/i1/LB1mALAi4HI8KJjy1zbXXaxdpXa">订单图</a> | |
| 16 | - * 输入: | |
| 17 | - * 1. 订单实付: trade.payment = 198.00;订单邮费:5 元; | |
| 18 | - * 2. 商品级优惠 圣诞价: 省 29.00 元 和 圣诞价:省 150.00 元; 订单级优惠,圣诞 2:省 5.00 元; | |
| 19 | - * 分摊: | |
| 20 | - * 1. 商品 1:原价 108 元,优惠 29 元,子订单实付 79 元,分摊主订单优惠 1.99 元; | |
| 21 | - * 2. 商品 2:原价 269 元,优惠 150 元,子订单实付 119 元,分摊主订单优惠 3.01 元; | |
| 22 | - * | |
| 23 | - * @author 芋道源码 | |
| 24 | - */ | |
| 25 | -@Data | |
| 26 | -@Deprecated | |
| 27 | -public class PriceCalculateRespDTO { | |
| 28 | - | |
| 29 | - /** | |
| 30 | - * 订单 | |
| 31 | - */ | |
| 32 | - private Order order; | |
| 33 | - | |
| 34 | - /** | |
| 35 | - * 营销活动数组 | |
| 36 | - * | |
| 37 | - * 只对应 {@link Order#items} 商品匹配的活动 | |
| 38 | - */ | |
| 39 | - private List<Promotion> promotions; | |
| 40 | - | |
| 41 | - // TODO @芋艿:需要改造下,主要是价格字段 | |
| 42 | - /** | |
| 43 | - * 订单 | |
| 44 | - */ | |
| 45 | - @Data | |
| 46 | - public static class Order { | |
| 47 | - | |
| 48 | - /** | |
| 49 | - * 商品原价(总),单位:分 | |
| 50 | - * | |
| 51 | - * 基于 {@link OrderItem#getOriginalPrice()} 求和 | |
| 52 | - * | |
| 53 | - * 对应 taobao 的 trade.total_fee 字段 | |
| 54 | - */ | |
| 55 | - private Integer totalPrice; | |
| 56 | - /** | |
| 57 | - * 订单优惠(总),单位:分 | |
| 58 | - * | |
| 59 | - * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。 | |
| 60 | - * | |
| 61 | - * 对应 taobao 的 order.discount_fee 字段 | |
| 62 | - */ | |
| 63 | - private Integer discountPrice; | |
| 64 | - /** | |
| 65 | - * 优惠劵减免金额(总),单位:分 | |
| 66 | - * | |
| 67 | - * 对应 taobao 的 trade.coupon_fee 字段 | |
| 68 | - */ | |
| 69 | - private Integer couponPrice; | |
| 70 | - /** | |
| 71 | - * 积分减免金额(总),单位:分 | |
| 72 | - * | |
| 73 | - * 对应 taobao 的 trade.point_fee 字段 | |
| 74 | - */ | |
| 75 | - private Integer pointPrice; | |
| 76 | - /** | |
| 77 | - * 运费金额,单位:分 | |
| 78 | - */ | |
| 79 | - private Integer deliveryPrice; | |
| 80 | - /** | |
| 81 | - * 最终购买金额(总),单位:分 | |
| 82 | - * | |
| 83 | - * = {@link OrderItem#getPayPrice()} 求和 | |
| 84 | - * - {@link #couponPrice} | |
| 85 | - * - {@link #pointPrice} | |
| 86 | - * + {@link #deliveryPrice} | |
| 87 | - * - {@link #discountPrice} | |
| 88 | - */ | |
| 89 | - private Integer payPrice; | |
| 90 | - /** | |
| 91 | - * 商品 SKU 数组 | |
| 92 | - */ | |
| 93 | - private List<OrderItem> items; | |
| 94 | - | |
| 95 | - // ========== 营销基本信息 ========== | |
| 96 | - /** | |
| 97 | - * 优惠劵编号 | |
| 98 | - */ | |
| 99 | - private Long couponId; | |
| 100 | - | |
| 101 | - } | |
| 102 | - | |
| 103 | - /** | |
| 104 | - * 订单商品 SKU | |
| 105 | - */ | |
| 106 | - @Data | |
| 107 | - public static class OrderItem { | |
| 108 | - | |
| 109 | - /** | |
| 110 | - * SPU 编号 | |
| 111 | - */ | |
| 112 | - private Long spuId; | |
| 113 | - /** | |
| 114 | - * SKU 编号 | |
| 115 | - */ | |
| 116 | - private Long skuId; | |
| 117 | - /** | |
| 118 | - * 购买数量 | |
| 119 | - */ | |
| 120 | - private Integer count; | |
| 121 | - | |
| 122 | - /** | |
| 123 | - * 商品原价(总),单位:分 | |
| 124 | - * | |
| 125 | - * = {@link #originalUnitPrice} * {@link #getCount()} | |
| 126 | - */ | |
| 127 | - private Integer originalPrice; | |
| 128 | - /** | |
| 129 | - * 商品原价(单),单位:分 | |
| 130 | - * | |
| 131 | - * 对应 ProductSkuDO 的 price 字段 | |
| 132 | - * 对应 taobao 的 order.price 字段 | |
| 133 | - */ | |
| 134 | - private Integer originalUnitPrice; | |
| 135 | - /** | |
| 136 | - * 商品优惠(总),单位:分 | |
| 137 | - * | |
| 138 | - * 商品级优惠:对单个商品的,常见如:商品原价的 8 折;商品原价的减 50 元 | |
| 139 | - * | |
| 140 | - * 对应 taobao 的 order.discount_fee 字段 | |
| 141 | - */ | |
| 142 | - private Integer discountPrice; | |
| 143 | - /** | |
| 144 | - * 子订单实付金额,不算主订单分摊金额,单位:分 | |
| 145 | - * | |
| 146 | - * = {@link #originalPrice} | |
| 147 | - * - {@link #discountPrice} | |
| 148 | - * | |
| 149 | - * 对应 taobao 的 order.payment 字段 | |
| 150 | - */ | |
| 151 | - private Integer payPrice; | |
| 152 | - | |
| 153 | - /** | |
| 154 | - * 子订单分摊金额(总),单位:分 | |
| 155 | - * 需要分摊 {@link Order#discountPrice}、{@link Order#couponPrice}、{@link Order#pointPrice} | |
| 156 | - * | |
| 157 | - * 对应 taobao 的 order.part_mjz_discount 字段 | |
| 158 | - * 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。 | |
| 159 | - */ | |
| 160 | - private Integer orderPartPrice; | |
| 161 | - /** | |
| 162 | - * 分摊后子订单实付金额(总),单位:分 | |
| 163 | - * | |
| 164 | - * = {@link #payPrice} | |
| 165 | - * - {@link #orderPartPrice} | |
| 166 | - * | |
| 167 | - * 对应 taobao 的 divide_order_fee 字段 | |
| 168 | - */ | |
| 169 | - private Integer orderDividePrice; | |
| 170 | - | |
| 171 | - } | |
| 172 | - | |
| 173 | - /** | |
| 174 | - * 营销明细 | |
| 175 | - */ | |
| 176 | - @Data | |
| 177 | - @Deprecated | |
| 178 | - public static class Promotion { | |
| 179 | - | |
| 180 | - /** | |
| 181 | - * 营销编号 | |
| 182 | - * | |
| 183 | - * 例如说:营销活动的编号、优惠劵的编号 | |
| 184 | - */ | |
| 185 | - private Long id; | |
| 186 | - /** | |
| 187 | - * 营销名字 | |
| 188 | - */ | |
| 189 | - private String name; | |
| 190 | - /** | |
| 191 | - * 营销类型 | |
| 192 | - * | |
| 193 | - * 枚举 {@link PromotionTypeEnum} | |
| 194 | - */ | |
| 195 | - private Integer type; | |
| 196 | - /** | |
| 197 | - * 营销级别 | |
| 198 | - * | |
| 199 | - * 枚举 @link PromotionLevelEnum} TODO PromotionLevelEnum 没有这个枚举类 | |
| 200 | - */ | |
| 201 | - private Integer level; | |
| 202 | - /** | |
| 203 | - * 计算时的原价(总),单位:分 | |
| 204 | - */ | |
| 205 | - private Integer totalPrice; | |
| 206 | - /** | |
| 207 | - * 计算时的优惠(总),单位:分 | |
| 208 | - */ | |
| 209 | - private Integer discountPrice; | |
| 210 | - /** | |
| 211 | - * 匹配的商品 SKU 数组 | |
| 212 | - */ | |
| 213 | - private List<PromotionItem> items; | |
| 214 | - | |
| 215 | - // ========== 匹配情况 ========== | |
| 216 | - | |
| 217 | - /** | |
| 218 | - * 是否满足优惠条件 | |
| 219 | - */ | |
| 220 | - private Boolean match; | |
| 221 | - /** | |
| 222 | - * 满足条件的提示 | |
| 223 | - * | |
| 224 | - * 如果 {@link #match} = true 满足,则提示“圣诞价:省 150.00 元” | |
| 225 | - * 如果 {@link #match} = false 不满足,则提示“购满 85 元,可减 40 元” | |
| 226 | - */ | |
| 227 | - private String description; | |
| 228 | - | |
| 229 | - } | |
| 230 | - | |
| 231 | - /** | |
| 232 | - * 营销匹配的商品 SKU | |
| 233 | - */ | |
| 234 | - @Data | |
| 235 | - public static class PromotionItem { | |
| 236 | - | |
| 237 | - /** | |
| 238 | - * 商品 SKU 编号 | |
| 239 | - */ | |
| 240 | - private Long skuId; | |
| 241 | - /** | |
| 242 | - * 计算时的原价(总),单位:分 | |
| 243 | - */ | |
| 244 | - private Integer originalPrice; | |
| 245 | - /** | |
| 246 | - * 计算时的优惠(总),单位:分 | |
| 247 | - */ | |
| 248 | - private Integer discountPrice; | |
| 249 | - | |
| 250 | - } | |
| 251 | - | |
| 252 | -} |
yudao-module-mall/yudao-module-promotion-biz/pom.xml
| ... | ... | @@ -14,8 +14,8 @@ |
| 14 | 14 | <name>${project.artifactId}</name> |
| 15 | 15 | |
| 16 | 16 | <description> |
| 17 | - market模块,主要实现营销相关功能 | |
| 18 | - 例如:营销活动、banner广告、优惠券、优惠码等功能。 | |
| 17 | + promotion 模块,主要实现营销相关功能 | |
| 18 | + 例如:营销活动、banner 广告、优惠券、优惠码等功能。 | |
| 19 | 19 | </description> |
| 20 | 20 | |
| 21 | 21 | <dependencies> |
| ... | ... | @@ -49,10 +49,6 @@ |
| 49 | 49 | <groupId>cn.iocoder.boot</groupId> |
| 50 | 50 | <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> |
| 51 | 51 | </dependency> |
| 52 | - <dependency> | |
| 53 | - <groupId>cn.iocoder.boot</groupId> | |
| 54 | - <artifactId>yudao-spring-boot-starter-biz-weixin</artifactId> | |
| 55 | - </dependency> | |
| 56 | 52 | |
| 57 | 53 | <!-- Web 相关 --> |
| 58 | 54 | <dependency> | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApiImpl.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.api.bargain; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService; |
| 4 | 4 | import org.springframework.stereotype.Service; |
| 5 | +import org.springframework.validation.annotation.Validated; | |
| 5 | 6 | |
| 6 | 7 | import javax.annotation.Resource; |
| 7 | 8 | |
| ... | ... | @@ -11,6 +12,7 @@ import javax.annotation.Resource; |
| 11 | 12 | * @author HUIHUI |
| 12 | 13 | */ |
| 13 | 14 | @Service |
| 15 | +@Validated | |
| 14 | 16 | public class BargainActivityApiImpl implements BargainActivityApi { |
| 15 | 17 | |
| 16 | 18 | @Resource | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainRecordApiImpl.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.bargain; |
| 3 | 3 | import cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO; |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService; |
| 5 | 5 | import org.springframework.stereotype.Service; |
| 6 | +import org.springframework.validation.annotation.Validated; | |
| 6 | 7 | |
| 7 | 8 | import javax.annotation.Resource; |
| 8 | 9 | |
| ... | ... | @@ -12,6 +13,7 @@ import javax.annotation.Resource; |
| 12 | 13 | * @author HUIHUI |
| 13 | 14 | */ |
| 14 | 15 | @Service |
| 16 | +@Validated | |
| 15 | 17 | public class BargainRecordApiImpl implements BargainRecordApi { |
| 16 | 18 | |
| 17 | 19 | @Resource | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationActivityApiImpl.java deleted
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
| ... | ... | @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR |
| 8 | 8 | import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; |
| 9 | 9 | import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; |
| 10 | 10 | import org.springframework.stereotype.Service; |
| 11 | +import org.springframework.validation.annotation.Validated; | |
| 11 | 12 | |
| 12 | 13 | import javax.annotation.Resource; |
| 13 | 14 | |
| ... | ... | @@ -20,6 +21,7 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINA |
| 20 | 21 | * @author HUIHUI |
| 21 | 22 | */ |
| 22 | 23 | @Service |
| 24 | +@Validated | |
| 23 | 25 | public class CombinationRecordApiImpl implements CombinationRecordApi { |
| 24 | 26 | |
| 25 | 27 | @Resource |
| ... | ... | @@ -45,11 +47,6 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { |
| 45 | 47 | } |
| 46 | 48 | |
| 47 | 49 | @Override |
| 48 | - public void updateRecordStatusToFailed(Long userId, Long orderId) { | |
| 49 | - recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.FAILED.getStatus(), userId, orderId); | |
| 50 | - } | |
| 51 | - | |
| 52 | - @Override | |
| 53 | 50 | public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) { |
| 54 | 51 | return recordService.validateJoinCombination(userId, activityId, headId, skuId, count); |
| 55 | 52 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
| ... | ... | @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; |
| 8 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
| 9 | 9 | import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; |
| 10 | 10 | import org.springframework.stereotype.Service; |
| 11 | +import org.springframework.validation.annotation.Validated; | |
| 11 | 12 | |
| 12 | 13 | import javax.annotation.Resource; |
| 13 | 14 | |
| ... | ... | @@ -17,6 +18,7 @@ import javax.annotation.Resource; |
| 17 | 18 | * @author 芋道源码 |
| 18 | 19 | */ |
| 19 | 20 | @Service |
| 21 | +@Validated | |
| 20 | 22 | public class CouponApiImpl implements CouponApi { |
| 21 | 23 | |
| 22 | 24 | @Resource | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert; |
| 5 | 5 | import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService; |
| 6 | 6 | import org.springframework.stereotype.Service; |
| 7 | +import org.springframework.validation.annotation.Validated; | |
| 7 | 8 | |
| 8 | 9 | import javax.annotation.Resource; |
| 9 | 10 | import java.util.Collection; |
| ... | ... | @@ -15,6 +16,7 @@ import java.util.List; |
| 15 | 16 | * @author 芋道源码 |
| 16 | 17 | */ |
| 17 | 18 | @Service |
| 19 | +@Validated | |
| 18 | 20 | public class DiscountActivityApiImpl implements DiscountActivityApi { |
| 19 | 21 | |
| 20 | 22 | @Resource | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApiImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.promotion.api.price; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; | |
| 4 | -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; | |
| 5 | -import cn.iocoder.yudao.module.promotion.service.price.PriceService; | |
| 6 | -import org.springframework.stereotype.Service; | |
| 7 | - | |
| 8 | -import javax.annotation.Resource; | |
| 9 | - | |
| 10 | -/** | |
| 11 | - * 价格 API 实现类 | |
| 12 | - * | |
| 13 | - * @author 芋道源码 | |
| 14 | - */ | |
| 15 | -@Service | |
| 16 | -public class PriceApiImpl implements PriceApi { | |
| 17 | - | |
| 18 | - @Resource | |
| 19 | - private PriceService priceService; | |
| 20 | - | |
| 21 | - @Override | |
| 22 | - public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) { | |
| 23 | - //return priceService.calculatePrice(calculateReqDTO); TODO 没有 calculatePrice 这个方法 | |
| 24 | - | |
| 25 | - return null; | |
| 26 | - } | |
| 27 | - | |
| 28 | -} |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApiImpl.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.reward; |
| 3 | 3 | import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; |
| 5 | 5 | import org.springframework.stereotype.Service; |
| 6 | +import org.springframework.validation.annotation.Validated; | |
| 6 | 7 | |
| 7 | 8 | import javax.annotation.Resource; |
| 8 | 9 | import java.util.Collection; |
| ... | ... | @@ -14,6 +15,7 @@ import java.util.List; |
| 14 | 15 | * @author 芋道源码 |
| 15 | 16 | */ |
| 16 | 17 | @Service |
| 18 | +@Validated | |
| 17 | 19 | public class RewardActivityApiImpl implements RewardActivityApi { |
| 18 | 20 | |
| 19 | 21 | @Resource | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.seckill; |
| 3 | 3 | import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO; |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService; |
| 5 | 5 | import org.springframework.stereotype.Service; |
| 6 | +import org.springframework.validation.annotation.Validated; | |
| 6 | 7 | |
| 7 | 8 | import javax.annotation.Resource; |
| 8 | 9 | |
| ... | ... | @@ -12,6 +13,7 @@ import javax.annotation.Resource; |
| 12 | 13 | * @author HUIHUI |
| 13 | 14 | */ |
| 14 | 15 | @Service |
| 16 | +@Validated | |
| 15 | 17 | public class SeckillActivityApiImpl implements SeckillActivityApi { |
| 16 | 18 | |
| 17 | 19 | @Resource | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerRespVO.java
| ... | ... | @@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | import lombok.ToString; |
| 6 | 6 | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 7 | 9 | @Schema(description = "管理后台 - Banner Response VO") |
| 8 | 10 | @Data |
| 9 | 11 | @ToString(callSuper = true) |
| ... | ... | @@ -12,4 +14,7 @@ public class BannerRespVO extends BannerBaseVO { |
| 12 | 14 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED) |
| 13 | 15 | private Long id; |
| 14 | 16 | |
| 17 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-01 23:59:59") | |
| 18 | + private LocalDateTime createTime; | |
| 19 | + | |
| 15 | 20 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponTemplateController.java
| ... | ... | @@ -96,13 +96,13 @@ public class AppCouponTemplateController { |
| 96 | 96 | */ |
| 97 | 97 | private Long getProductScopeValue(Integer productScope, Long spuId) { |
| 98 | 98 | // 通用券:没有商品范围 |
| 99 | - if (productScope == null || ObjectUtils.equalsAny(productScope, PromotionProductScopeEnum.ALL.getScope(), null)) { | |
| 99 | + if (ObjectUtils.equalsAny(productScope, PromotionProductScopeEnum.ALL.getScope(), null)) { | |
| 100 | 100 | return null; |
| 101 | 101 | } |
| 102 | 102 | // 品类券:查询商品的品类编号 |
| 103 | 103 | if (Objects.equals(productScope, PromotionProductScopeEnum.CATEGORY.getScope()) && spuId != null) { |
| 104 | - return Optional.ofNullable(productSpuApi.getSpu(spuId)) | |
| 105 | - .map(ProductSpuRespDTO::getCategoryId).orElse(null); | |
| 104 | + ProductSpuRespDTO spu = productSpuApi.getSpu(spuId); | |
| 105 | + return spu != null ? spu.getCategoryId() : null; | |
| 106 | 106 | } |
| 107 | 107 | // 商品卷:直接返回 |
| 108 | 108 | return spuId; | ... | ... |