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,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 | | [MySQL](https://www.mysql.com/cn/) | 鏁版嵁搴撴湇鍔″櫒 | 5.7 / 8.0+ | | | 260 | | [MySQL](https://www.mysql.com/cn/) | 鏁版嵁搴撴湇鍔″櫒 | 5.7 / 8.0+ | | |
| 261 | | [Druid](https://github.com/alibaba/druid) | JDBC 杩炴帴姹犮佺洃鎺х粍浠 | 1.2.19 | [鏂囨。](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | | 261 | | [Druid](https://github.com/alibaba/druid) | JDBC 杩炴帴姹犮佺洃鎺х粍浠 | 1.2.19 | [鏂囨。](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | |
| 262 | | [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 澧炲己宸ュ叿鍖 | 3.5.3.2 | [鏂囨。](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | | 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,16 +13,16 @@ | ||
| 13 | <!-- Server 主项目 --> | 13 | <!-- Server 主项目 --> |
| 14 | <module>yudao-server</module> | 14 | <module>yudao-server</module> |
| 15 | <!-- 各种 module 拓展 --> | 15 | <!-- 各种 module 拓展 --> |
| 16 | - <module>yudao-module-member</module> | ||
| 17 | <module>yudao-module-system</module> | 16 | <module>yudao-module-system</module> |
| 18 | <module>yudao-module-infra</module> | 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 | <!-- <module>yudao-module-report</module>--> | 20 | <!-- <module>yudao-module-report</module>--> |
| 21 | <!-- <module>yudao-module-mp</module>--> | 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 | </modules> | 26 | </modules> |
| 27 | 27 | ||
| 28 | <name>${project.artifactId}</name> | 28 | <name>${project.artifactId}</name> |
| @@ -30,7 +30,7 @@ | @@ -30,7 +30,7 @@ | ||
| 30 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 30 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 31 | 31 | ||
| 32 | <properties> | 32 | <properties> |
| 33 | - <revision>1.8.2-snapshot</revision> | 33 | + <revision>1.8.3-snapshot</revision> |
| 34 | <!-- Maven 相关 --> | 34 | <!-- Maven 相关 --> |
| 35 | <java.version>1.8</java.version> | 35 | <java.version>1.8</java.version> |
| 36 | <maven.compiler.source>${java.version}</maven.compiler.source> | 36 | <maven.compiler.source>${java.version}</maven.compiler.source> |
| @@ -40,7 +40,7 @@ | @@ -40,7 +40,7 @@ | ||
| 40 | <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version> | 40 | <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version> |
| 41 | <!-- 看看咋放到 bom 里 --> | 41 | <!-- 看看咋放到 bom 里 --> |
| 42 | <lombok.version>1.18.30</lombok.version> | 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 | <mapstruct.version>1.5.5.Final</mapstruct.version> | 44 | <mapstruct.version>1.5.5.Final</mapstruct.version> |
| 45 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | 45 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 46 | </properties> | 46 | </properties> |
yudao-dependencies/pom.xml
| @@ -14,28 +14,30 @@ | @@ -14,28 +14,30 @@ | ||
| 14 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 14 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 15 | 15 | ||
| 16 | <properties> | 16 | <properties> |
| 17 | - <revision>1.8.2-snapshot</revision> | 17 | + <revision>1.8.3-snapshot</revision> |
| 18 | <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version> | 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 | <!-- Web 相关 --> | 21 | <!-- Web 相关 --> |
| 22 | <springdoc.version>1.6.15</springdoc.version> | 22 | <springdoc.version>1.6.15</springdoc.version> |
| 23 | <knife4j.version>4.3.0</knife4j.version> | 23 | <knife4j.version>4.3.0</knife4j.version> |
| 24 | <servlet.versoin>2.5</servlet.versoin> | 24 | <servlet.versoin>2.5</servlet.versoin> |
| 25 | <!-- DB 相关 --> | 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 | <dynamic-datasource.version>3.6.1</dynamic-datasource.version> | 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 | <redisson.version>3.18.0</redisson.version> | 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 | <resilience4j.version>1.7.1</resilience4j.version> | 37 | <resilience4j.version>1.7.1</resilience4j.version> |
| 36 | <!-- 监控相关 --> | 38 | <!-- 监控相关 --> |
| 37 | <skywalking.version>8.12.0</skywalking.version> | 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 | <opentracing.version>0.33.0</opentracing.version> | 41 | <opentracing.version>0.33.0</opentracing.version> |
| 40 | <!-- Test 测试相关 --> | 42 | <!-- Test 测试相关 --> |
| 41 | <podam.version>7.2.11.RELEASE</podam.version> | 43 | <podam.version>7.2.11.RELEASE</podam.version> |
| @@ -44,8 +46,8 @@ | @@ -44,8 +46,8 @@ | ||
| 44 | <!-- Bpm 工作流相关 --> | 46 | <!-- Bpm 工作流相关 --> |
| 45 | <flowable.version>6.8.0</flowable.version> | 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 | <lombok.version>1.18.30</lombok.version> | 51 | <lombok.version>1.18.30</lombok.version> |
| 50 | <mapstruct.version>1.5.5.Final</mapstruct.version> | 52 | <mapstruct.version>1.5.5.Final</mapstruct.version> |
| 51 | <hutool.version>5.8.22</hutool.version> | 53 | <hutool.version>5.8.22</hutool.version> |
| @@ -53,10 +55,10 @@ | @@ -53,10 +55,10 @@ | ||
| 53 | <velocity.version>2.3</velocity.version> | 55 | <velocity.version>2.3</velocity.version> |
| 54 | <screw.version>1.0.5</screw.version> | 56 | <screw.version>1.0.5</screw.version> |
| 55 | <fastjson.version>1.2.83</fastjson.version> | 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 | <guice.version>5.1.0</guice.version> | 59 | <guice.version>5.1.0</guice.version> |
| 58 | <transmittable-thread-local.version>2.14.2</transmittable-thread-local.version> | 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 | <jsch.version>0.1.55</jsch.version> | 62 | <jsch.version>0.1.55</jsch.version> |
| 61 | <tika-core.version>2.7.0</tika-core.version> | 63 | <tika-core.version>2.7.0</tika-core.version> |
| 62 | <ip2region.version>2.7.0</ip2region.version> | 64 | <ip2region.version>2.7.0</ip2region.version> |
| @@ -67,8 +69,8 @@ | @@ -67,8 +69,8 @@ | ||
| 67 | <minio.version>8.5.6</minio.version> | 69 | <minio.version>8.5.6</minio.version> |
| 68 | <aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version> | 70 | <aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version> |
| 69 | <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version> | 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 | <jimureport.version>1.6.1</jimureport.version> | 74 | <jimureport.version>1.6.1</jimureport.version> |
| 73 | <xercesImpl.version>2.12.2</xercesImpl.version> | 75 | <xercesImpl.version>2.12.2</xercesImpl.version> |
| 74 | <weixin-java.version>4.5.0</weixin-java.version> | 76 | <weixin-java.version>4.5.0</weixin-java.version> |
| @@ -98,11 +100,6 @@ | @@ -98,11 +100,6 @@ | ||
| 98 | </dependency> | 100 | </dependency> |
| 99 | <dependency> | 101 | <dependency> |
| 100 | <groupId>cn.iocoder.boot</groupId> | 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 | <artifactId>yudao-spring-boot-starter-biz-dict</artifactId> | 103 | <artifactId>yudao-spring-boot-starter-biz-dict</artifactId> |
| 107 | <version>${revision}</version> | 104 | <version>${revision}</version> |
| 108 | </dependency> | 105 | </dependency> |
| @@ -260,6 +257,12 @@ | @@ -260,6 +257,12 @@ | ||
| 260 | <version>${revision}</version> | 257 | <version>${revision}</version> |
| 261 | </dependency> | 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 | <dependency> | 267 | <dependency> |
| 265 | <groupId>cn.iocoder.boot</groupId> | 268 | <groupId>cn.iocoder.boot</groupId> |
yudao-example/yudao-sso-demo-by-code/pom.xml
| @@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
| 21 | <maven.compiler.target>8</maven.compiler.target> | 21 | <maven.compiler.target>8</maven.compiler.target> |
| 22 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | 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 | </properties> | 25 | </properties> |
| 26 | 26 | ||
| 27 | <dependencyManagement> | 27 | <dependencyManagement> |
yudao-example/yudao-sso-demo-by-password/pom.xml
| @@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
| 21 | <maven.compiler.target>8</maven.compiler.target> | 21 | <maven.compiler.target>8</maven.compiler.target> |
| 22 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | 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 | </properties> | 25 | </properties> |
| 26 | 26 | ||
| 27 | <dependencyManagement> | 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,6 +4,8 @@ import lombok.AllArgsConstructor; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | import lombok.NoArgsConstructor; | 5 | import lombok.NoArgsConstructor; |
| 6 | 6 | ||
| 7 | +import java.io.Serializable; | ||
| 8 | + | ||
| 7 | /** | 9 | /** |
| 8 | * Key Value 的键值对 | 10 | * Key Value 的键值对 |
| 9 | * | 11 | * |
| @@ -12,7 +14,7 @@ import lombok.NoArgsConstructor; | @@ -12,7 +14,7 @@ import lombok.NoArgsConstructor; | ||
| 12 | @Data | 14 | @Data |
| 13 | @NoArgsConstructor | 15 | @NoArgsConstructor |
| 14 | @AllArgsConstructor | 16 | @AllArgsConstructor |
| 15 | -public class KeyValue<K, V> { | 17 | +public class KeyValue<K, V> implements Serializable { |
| 16 | 18 | ||
| 17 | private K key; | 19 | private K key; |
| 18 | private V value; | 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,6 +8,7 @@ import com.google.common.collect.ImmutableMap; | ||
| 8 | import java.util.*; | 8 | import java.util.*; |
| 9 | import java.util.function.*; | 9 | import java.util.function.*; |
| 10 | import java.util.stream.Collectors; | 10 | import java.util.stream.Collectors; |
| 11 | +import java.util.stream.Stream; | ||
| 11 | 12 | ||
| 12 | import static java.util.Arrays.asList; | 13 | import static java.util.Arrays.asList; |
| 13 | 14 | ||
| @@ -271,4 +272,20 @@ public class CollectionUtils { | @@ -271,4 +272,20 @@ public class CollectionUtils { | ||
| 271 | return deptId == null ? Collections.emptyList() : Collections.singleton(deptId); | 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,7 +21,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; | ||
| 21 | @ConditionalOnProperty(prefix = "yudao.error-code", value = "enable", matchIfMissing = true) // 允许使用 yudao.error-code.enable=false 禁用访问日志 | 21 | @ConditionalOnProperty(prefix = "yudao.error-code", value = "enable", matchIfMissing = true) // 允许使用 yudao.error-code.enable=false 禁用访问日志 |
| 22 | @EnableConfigurationProperties(ErrorCodeProperties.class) | 22 | @EnableConfigurationProperties(ErrorCodeProperties.class) |
| 23 | @EnableScheduling // 开启调度任务的功能,因为 ErrorCodeRemoteLoader 通过定时刷新错误码 | 23 | @EnableScheduling // 开启调度任务的功能,因为 ErrorCodeRemoteLoader 通过定时刷新错误码 |
| 24 | -public class YudaoErrorCodeConfiguration { | 24 | +public class YudaoErrorCodeAutoConfiguration { |
| 25 | 25 | ||
| 26 | @Bean | 26 | @Bean |
| 27 | public ErrorCodeAutoGenerator errorCodeAutoGenerator(@Value("${spring.application.name}") String applicationName, | 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,6 +48,22 @@ | ||
| 48 | <dependency> | 48 | <dependency> |
| 49 | <groupId>cn.iocoder.boot</groupId> | 49 | <groupId>cn.iocoder.boot</groupId> |
| 50 | <artifactId>yudao-spring-boot-starter-mq</artifactId> | 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 | </dependency> | 67 | </dependency> |
| 52 | 68 | ||
| 53 | <!-- Test 测试相关 --> | 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,7 +6,9 @@ import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; | ||
| 6 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; | 6 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; |
| 7 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; | 7 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; |
| 8 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect; | 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 | import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; | 12 | import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; |
| 11 | import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; | 13 | import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; |
| 12 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; | 14 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; |
| @@ -18,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.tenant.TenantApi; | @@ -18,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.tenant.TenantApi; | ||
| 18 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; | 20 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| 19 | import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; | 21 | import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; |
| 20 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 22 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 23 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
| 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 22 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 25 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 23 | import org.springframework.boot.web.servlet.FilterRegistrationBean; | 26 | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| @@ -92,6 +95,18 @@ public class YudaoTenantAutoConfiguration { | @@ -92,6 +95,18 @@ public class YudaoTenantAutoConfiguration { | ||
| 92 | return new TenantRedisMessageInterceptor(); | 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 | // ========== Job ========== | 110 | // ========== Job ========== |
| 96 | 111 | ||
| 97 | @Bean | 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 | \ No newline at end of file | 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 | import cn.hutool.core.util.StrUtil; | 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 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | 6 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 7 | 7 | ||
| 8 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | 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 | \ No newline at end of file | 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,7 +12,7 @@ | ||
| 12 | <packaging>jar</packaging> | 12 | <packaging>jar</packaging> |
| 13 | 13 | ||
| 14 | <name>${project.artifactId}</name> | 14 | <name>${project.artifactId}</name> |
| 15 | - <description>消息队列,基于 Redis Pub/Sub 实现广播消费,基于 Stream 实现集群消费</description> | 15 | + <description>消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种</description> |
| 16 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 16 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 17 | 17 | ||
| 18 | <dependencies> | 18 | <dependencies> |
| @@ -21,6 +21,23 @@ | @@ -21,6 +21,23 @@ | ||
| 21 | <groupId>cn.iocoder.boot</groupId> | 21 | <groupId>cn.iocoder.boot</groupId> |
| 22 | <artifactId>yudao-spring-boot-starter-redis</artifactId> | 22 | <artifactId>yudao-spring-boot-starter-redis</artifactId> |
| 23 | </dependency> | 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 | </dependencies> | 41 | </dependencies> |
| 25 | 42 | ||
| 26 | -</project> | 43 | -</project> |
| 44 | +</project> | ||
| 27 | \ No newline at end of file | 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 | import cn.hutool.core.map.MapUtil; | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | import cn.hutool.system.SystemUtil; | 5 | import cn.hutool.system.SystemUtil; |
| 6 | import cn.iocoder.yudao.framework.common.enums.DocumentEnum; | 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 | import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; | 12 | import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
| 13 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | import org.redisson.api.RedissonClient; | 14 | import org.redisson.api.RedissonClient; |
| 15 | import org.springframework.beans.factory.annotation.Value; | 15 | import org.springframework.beans.factory.annotation.Value; |
| 16 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 16 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
| 18 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| 19 | import org.springframework.context.annotation.Bean; | 18 | import org.springframework.context.annotation.Bean; |
| 20 | import org.springframework.data.redis.connection.RedisServerCommands; | 19 | import org.springframework.data.redis.connection.RedisServerCommands; |
| 21 | import org.springframework.data.redis.connection.stream.Consumer; | 20 | import org.springframework.data.redis.connection.stream.Consumer; |
| @@ -27,7 +26,6 @@ import org.springframework.data.redis.core.RedisTemplate; | @@ -27,7 +26,6 @@ import org.springframework.data.redis.core.RedisTemplate; | ||
| 27 | import org.springframework.data.redis.core.StringRedisTemplate; | 26 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 28 | import org.springframework.data.redis.listener.ChannelTopic; | 27 | import org.springframework.data.redis.listener.ChannelTopic; |
| 29 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; | 28 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
| 30 | -import org.springframework.data.redis.stream.DefaultStreamMessageListenerContainerX; | ||
| 31 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; | 29 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; |
| 32 | import org.springframework.scheduling.annotation.EnableScheduling; | 30 | import org.springframework.scheduling.annotation.EnableScheduling; |
| 33 | 31 | ||
| @@ -42,7 +40,7 @@ import java.util.Properties; | @@ -42,7 +40,7 @@ import java.util.Properties; | ||
| 42 | @Slf4j | 40 | @Slf4j |
| 43 | @EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息 | 41 | @EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息 |
| 44 | @AutoConfiguration(after = YudaoRedisAutoConfiguration.class) | 42 | @AutoConfiguration(after = YudaoRedisAutoConfiguration.class) |
| 45 | -public class YudaoMQAutoConfiguration { | 43 | +public class YudaoRedisMQAutoConfiguration { |
| 46 | 44 | ||
| 47 | @Bean | 45 | @Bean |
| 48 | public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, | 46 | public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, |
| @@ -59,10 +57,9 @@ public class YudaoMQAutoConfiguration { | @@ -59,10 +57,9 @@ public class YudaoMQAutoConfiguration { | ||
| 59 | * 创建 Redis Pub/Sub 广播消费的容器 | 57 | * 创建 Redis Pub/Sub 广播消费的容器 |
| 60 | */ | 58 | */ |
| 61 | @Bean(initMethod = "start", destroyMethod = "stop") | 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 | public RedisMessageListenerContainer redisMessageListenerContainer( | 61 | public RedisMessageListenerContainer redisMessageListenerContainer( |
| 65 | - RedisMQTemplate redisMQTemplate, List<AbstractChannelMessageListener<?>> listeners) { | 62 | + RedisMQTemplate redisMQTemplate, List<AbstractRedisChannelMessageListener<?>> listeners) { |
| 66 | // 创建 RedisMessageListenerContainer 对象 | 63 | // 创建 RedisMessageListenerContainer 对象 |
| 67 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | 64 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
| 68 | // 设置 RedisConnection 工厂。 | 65 | // 设置 RedisConnection 工厂。 |
| @@ -81,9 +78,8 @@ public class YudaoMQAutoConfiguration { | @@ -81,9 +78,8 @@ public class YudaoMQAutoConfiguration { | ||
| 81 | * 创建 Redis Stream 重新消费的任务 | 78 | * 创建 Redis Stream 重新消费的任务 |
| 82 | */ | 79 | */ |
| 83 | @Bean | 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 | RedisMQTemplate redisTemplate, | 83 | RedisMQTemplate redisTemplate, |
| 88 | @Value("${spring.application.name}") String groupName, | 84 | @Value("${spring.application.name}") String groupName, |
| 89 | RedissonClient redissonClient) { | 85 | RedissonClient redissonClient) { |
| @@ -92,14 +88,13 @@ public class YudaoMQAutoConfiguration { | @@ -92,14 +88,13 @@ public class YudaoMQAutoConfiguration { | ||
| 92 | 88 | ||
| 93 | /** | 89 | /** |
| 94 | * 创建 Redis Stream 集群消费的容器 | 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 | @Bean(initMethod = "start", destroyMethod = "stop") | 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 | public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( | 96 | public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( |
| 102 | - RedisMQTemplate redisMQTemplate, List<AbstractStreamMessageListener<?>> listeners) { | 97 | + RedisMQTemplate redisMQTemplate, List<AbstractRedisStreamMessageListener<?>> listeners) { |
| 103 | RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate(); | 98 | RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate(); |
| 104 | checkRedisVersion(redisTemplate); | 99 | checkRedisVersion(redisTemplate); |
| 105 | // 第一步,创建 StreamMessageListenerContainer 容器 | 100 | // 第一步,创建 StreamMessageListenerContainer 容器 |
| @@ -111,8 +106,7 @@ public class YudaoMQAutoConfiguration { | @@ -111,8 +106,7 @@ public class YudaoMQAutoConfiguration { | ||
| 111 | .build(); | 106 | .build(); |
| 112 | // 创建 container 对象 | 107 | // 创建 container 对象 |
| 113 | StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = | 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 | // 第二步,注册监听器,消费对应的 Stream 主题 | 111 | // 第二步,注册监听器,消费对应的 Stream 主题 |
| 118 | String consumerName = buildConsumerName(); | 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 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 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 | import lombok.AllArgsConstructor; | 8 | import lombok.AllArgsConstructor; |
| 9 | import lombok.Getter; | 9 | import lombok.Getter; |
| 10 | import org.springframework.data.redis.connection.stream.RecordId; | 10 | import org.springframework.data.redis.connection.stream.RecordId; |
| @@ -35,7 +35,7 @@ public class RedisMQTemplate { | @@ -35,7 +35,7 @@ public class RedisMQTemplate { | ||
| 35 | * | 35 | * |
| 36 | * @param message 消息 | 36 | * @param message 消息 |
| 37 | */ | 37 | */ |
| 38 | - public <T extends AbstractChannelMessage> void send(T message) { | 38 | + public <T extends AbstractRedisChannelMessage> void send(T message) { |
| 39 | try { | 39 | try { |
| 40 | sendMessageBefore(message); | 40 | sendMessageBefore(message); |
| 41 | // 发送消息 | 41 | // 发送消息 |
| @@ -51,7 +51,7 @@ public class RedisMQTemplate { | @@ -51,7 +51,7 @@ public class RedisMQTemplate { | ||
| 51 | * @param message 消息 | 51 | * @param message 消息 |
| 52 | * @return 消息记录的编号对象 | 52 | * @return 消息记录的编号对象 |
| 53 | */ | 53 | */ |
| 54 | - public <T extends AbstractStreamMessage> RecordId send(T message) { | 54 | + public <T extends AbstractRedisStreamMessage> RecordId send(T message) { |
| 55 | try { | 55 | try { |
| 56 | sendMessageBefore(message); | 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 | * {@link AbstractRedisMessage} 消息拦截器 | 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 | import cn.hutool.core.collection.CollUtil; | 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 | import lombok.AllArgsConstructor; | 6 | import lombok.AllArgsConstructor; |
| 7 | import lombok.extern.slf4j.Slf4j; | 7 | import lombok.extern.slf4j.Slf4j; |
| 8 | import org.redisson.api.RLock; | 8 | import org.redisson.api.RLock; |
| @@ -33,7 +33,7 @@ public class RedisPendingMessageResendJob { | @@ -33,7 +33,7 @@ public class RedisPendingMessageResendJob { | ||
| 33 | */ | 33 | */ |
| 34 | private static final int EXPIRE_TIME = 5 * 60; | 34 | private static final int EXPIRE_TIME = 5 * 60; |
| 35 | 35 | ||
| 36 | - private final List<AbstractStreamMessageListener<?>> listeners; | 36 | + private final List<AbstractRedisStreamMessageListener<?>> listeners; |
| 37 | private final RedisMQTemplate redisTemplate; | 37 | private final RedisMQTemplate redisTemplate; |
| 38 | private final String groupName; | 38 | private final String groupName; |
| 39 | private final RedissonClient redissonClient; | 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 | import com.fasterxml.jackson.annotation.JsonIgnore; | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | ||
| 8 | * | 8 | * |
| 9 | * @author 芋道源码 | 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 | * @return Channel | 16 | * @return Channel |
| 17 | */ | 17 | */ |
| 18 | @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。 | 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 | import cn.hutool.core.util.TypeUtil; | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 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 | import lombok.Setter; | 8 | import lombok.Setter; |
| 9 | import lombok.SneakyThrows; | 9 | import lombok.SneakyThrows; |
| 10 | import org.springframework.data.redis.connection.Message; | 10 | import org.springframework.data.redis.connection.Message; |
| @@ -20,7 +20,7 @@ import java.util.List; | @@ -20,7 +20,7 @@ import java.util.List; | ||
| 20 | * | 20 | * |
| 21 | * @author 芋道源码 | 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,7 +37,7 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | ||
| 37 | private RedisMQTemplate redisMQTemplate; | 37 | private RedisMQTemplate redisMQTemplate; |
| 38 | 38 | ||
| 39 | @SneakyThrows | 39 | @SneakyThrows |
| 40 | - protected AbstractChannelMessageListener() { | 40 | + protected AbstractRedisChannelMessageListener() { |
| 41 | this.messageType = getMessageClass(); | 41 | this.messageType = getMessageClass(); |
| 42 | this.channel = messageType.getDeclaredConstructor().newInstance().getChannel(); | 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 | import com.fasterxml.jackson.annotation.JsonIgnore; | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | ||
| 8 | * | 8 | * |
| 9 | * @author 芋道源码 | 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 | * @return Channel | 16 | * @return Channel |
| 17 | */ | 17 | */ |
| 18 | @JsonIgnore // 避免序列化 | 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 | import cn.hutool.core.util.TypeUtil; | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 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 | import lombok.Getter; | 8 | import lombok.Getter; |
| 9 | import lombok.Setter; | 9 | import lombok.Setter; |
| 10 | import lombok.SneakyThrows; | 10 | import lombok.SneakyThrows; |
| @@ -22,7 +22,7 @@ import java.util.List; | @@ -22,7 +22,7 @@ import java.util.List; | ||
| 22 | * | 22 | * |
| 23 | * @author 芋道源码 | 23 | * @author 芋道源码 |
| 24 | */ | 24 | */ |
| 25 | -public abstract class AbstractStreamMessageListener<T extends AbstractStreamMessage> | 25 | +public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedisStreamMessage> |
| 26 | implements StreamListener<String, ObjectRecord<String, String>> { | 26 | implements StreamListener<String, ObjectRecord<String, String>> { |
| 27 | 27 | ||
| 28 | /** | 28 | /** |
| @@ -48,7 +48,7 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | @@ -48,7 +48,7 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | ||
| 48 | private RedisMQTemplate redisMQTemplate; | 48 | private RedisMQTemplate redisMQTemplate; |
| 49 | 49 | ||
| 50 | @SneakyThrows | 50 | @SneakyThrows |
| 51 | - protected AbstractStreamMessageListener() { | 51 | + protected AbstractRedisStreamMessageListener() { |
| 52 | this.messageType = getMessageClass(); | 52 | this.messageType = getMessageClass(); |
| 53 | this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); | 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
| 1 | -cn.iocoder.yudao.framework.mq.config.YudaoMQAutoConfiguration | ||
| 2 | \ No newline at end of file | 1 | \ No newline at end of file |
| 2 | +cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQAutoConfiguration | ||
| 3 | +cn.iocoder.yudao.framework.mq.rabbitmq.config.YudaoRabbitMQAutoConfiguration | ||
| 3 | \ No newline at end of file | 4 | \ No newline at end of file |
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
| @@ -5,7 +5,6 @@ import lombok.Data; | @@ -5,7 +5,6 @@ import lombok.Data; | ||
| 5 | import javax.validation.constraints.NotNull; | 5 | import javax.validation.constraints.NotNull; |
| 6 | import java.time.LocalDateTime; | 6 | import java.time.LocalDateTime; |
| 7 | 7 | ||
| 8 | -// TODO @小吉祥:搞个 job,清理 14 天外的访问日志; | ||
| 9 | /** | 8 | /** |
| 10 | * API 访问日志 | 9 | * API 访问日志 |
| 11 | * | 10 | * |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java
| @@ -5,7 +5,6 @@ import lombok.Data; | @@ -5,7 +5,6 @@ import lombok.Data; | ||
| 5 | import javax.validation.constraints.NotNull; | 5 | import javax.validation.constraints.NotNull; |
| 6 | import java.time.LocalDateTime; | 6 | import java.time.LocalDateTime; |
| 7 | 7 | ||
| 8 | -// TODO @小吉祥:搞个 job,清理 14 天外的异常日志; | ||
| 9 | /** | 8 | /** |
| 10 | * API 错误日志 | 9 | * API 错误日志 |
| 11 | * | 10 | * |
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,16 +20,16 @@ public enum BpmCommentTypeEnum { | ||
| 20 | ; | 20 | ; |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | - * 结果 | 23 | + * 操作类型 |
| 24 | */ | 24 | */ |
| 25 | private final Integer type; | 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,13 +30,13 @@ public enum BpmProcessInstanceResultEnum { | ||
| 30 | * 相当于是 通过 APPROVE 的特殊状态 | 30 | * 相当于是 通过 APPROVE 的特殊状态 |
| 31 | * 例如:A审批, A 后加签了 B,并且审批通过了任务,但是 B 还未审批,则当前任务状态为“待后加签任务完成” | 31 | * 例如:A审批, A 后加签了 B,并且审批通过了任务,但是 B 还未审批,则当前任务状态为“待后加签任务完成” |
| 32 | */ | 32 | */ |
| 33 | - ADD_SIGN_AFTER(7, "待后加签任务完成"), | 33 | + SIGN_AFTER(7, "待后加签任务完成"), |
| 34 | /** | 34 | /** |
| 35 | * 【加签】源任务未审批,但是向前加签了,所以源任务状态变为“待前加签任务完成” | 35 | * 【加签】源任务未审批,但是向前加签了,所以源任务状态变为“待前加签任务完成” |
| 36 | * 相当于是 处理中 PROCESS 的特殊状态 | 36 | * 相当于是 处理中 PROCESS 的特殊状态 |
| 37 | * 例如:A 审批, A 前加签了 B,B 还未审核 | 37 | * 例如:A 审批, A 前加签了 B,B 还未审核 |
| 38 | */ | 38 | */ |
| 39 | - ADD_SIGN_BEFORE(8, "待前加签任务完成"), | 39 | + SIGN_BEFORE(8, "待前加签任务完成"), |
| 40 | /** | 40 | /** |
| 41 | * 【加签】后加签任务被创建时的初始状态 | 41 | * 【加签】后加签任务被创建时的初始状态 |
| 42 | * 相当于是 处理中 PROCESS 的特殊状态 | 42 | * 相当于是 处理中 PROCESS 的特殊状态 |
| @@ -71,7 +71,7 @@ public enum BpmProcessInstanceResultEnum { | @@ -71,7 +71,7 @@ public enum BpmProcessInstanceResultEnum { | ||
| 71 | public static boolean isEndResult(Integer result) { | 71 | public static boolean isEndResult(Integer result) { |
| 72 | return ObjectUtils.equalsAny(result, APPROVE.getResult(), REJECT.getResult(), | 72 | return ObjectUtils.equalsAny(result, APPROVE.getResult(), REJECT.getResult(), |
| 73 | CANCEL.getResult(), BACK.getResult(), | 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
| @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.enums.task; | @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.enums.task; | ||
| 3 | import lombok.AllArgsConstructor; | 3 | import lombok.AllArgsConstructor; |
| 4 | import lombok.Getter; | 4 | import lombok.Getter; |
| 5 | 5 | ||
| 6 | - | ||
| 7 | /** | 6 | /** |
| 8 | * 流程任务 -- 加签类型枚举类型 | 7 | * 流程任务 -- 加签类型枚举类型 |
| 9 | */ | 8 | */ |
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,7 +74,7 @@ public class BpmTaskController { | ||
| 74 | return success(true); | 74 | return success(true); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | - @GetMapping("/get-return-list") | 77 | + @GetMapping("/return-list") |
| 78 | @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") | 78 | @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") |
| 79 | @Parameter(name = "taskId", description = "当前任务ID", required = true) | 79 | @Parameter(name = "taskId", description = "当前任务ID", required = true) |
| 80 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") | 80 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") |
| @@ -98,27 +98,28 @@ public class BpmTaskController { | @@ -98,27 +98,28 @@ public class BpmTaskController { | ||
| 98 | return success(true); | 98 | return success(true); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | - @PutMapping("/add-sign") | 101 | + @PutMapping("/create-sign") |
| 102 | @Operation(summary = "加签", description = "before 前加签,after 后加签") | 102 | @Operation(summary = "加签", description = "before 前加签,after 后加签") |
| 103 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") | 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 | return success(true); | 106 | return success(true); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | - @PutMapping("/sub-sign") | 109 | + @DeleteMapping("/delete-sign") |
| 110 | @Operation(summary = "减签") | 110 | @Operation(summary = "减签") |
| 111 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") | 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 | return success(true); | 114 | return success(true); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | - @GetMapping("/get-children-task-list") | 117 | + @GetMapping("children-list") |
| 118 | @Operation(summary = "获取能被减签的任务") | 118 | @Operation(summary = "获取能被减签的任务") |
| 119 | + @Parameter(name = "parentId", description = "父级任务 ID", required = true) | ||
| 119 | @PreAuthorize("@ss.hasPermission('bpm:task:update')") | 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 +6,7 @@ import lombok.Data; | ||
| 6 | import javax.validation.constraints.NotEmpty; | 6 | import javax.validation.constraints.NotEmpty; |
| 7 | import java.util.Set; | 7 | import java.util.Set; |
| 8 | 8 | ||
| 9 | +// TODO @海洋:类名,应该是 create 哈 | ||
| 9 | @Schema(description = "管理后台 - 加签流程任务的 Request VO") | 10 | @Schema(description = "管理后台 - 加签流程任务的 Request VO") |
| 10 | @Data | 11 | @Data |
| 11 | public class BpmTaskAddSignReqVO { | 12 | public class BpmTaskAddSignReqVO { |
| @@ -26,4 +27,4 @@ public class BpmTaskAddSignReqVO { | @@ -26,4 +27,4 @@ public class BpmTaskAddSignReqVO { | ||
| 26 | @NotEmpty(message = "加签原因不能为空") | 27 | @NotEmpty(message = "加签原因不能为空") |
| 27 | private String reason; | 28 | private String reason; |
| 28 | 29 | ||
| 29 | -} | ||
| 30 | \ No newline at end of file | 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
| @@ -5,6 +5,7 @@ import lombok.Data; | @@ -5,6 +5,7 @@ import lombok.Data; | ||
| 5 | 5 | ||
| 6 | import javax.validation.constraints.NotEmpty; | 6 | import javax.validation.constraints.NotEmpty; |
| 7 | 7 | ||
| 8 | +// TODO @海洋:类名,应该是 delete 哈 | ||
| 8 | @Schema(description = "管理后台 - 减签流程任务的 Request VO") | 9 | @Schema(description = "管理后台 - 减签流程任务的 Request VO") |
| 9 | @Data | 10 | @Data |
| 10 | public class BpmTaskSubSignReqVO { | 11 | public class BpmTaskSubSignReqVO { |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
| 1 | package cn.iocoder.yudao.module.bpm.convert.task; | 1 | package cn.iocoder.yudao.module.bpm.convert.task; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.hutool.core.date.LocalDateTimeUtil; | 3 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 4 | +import cn.hutool.core.util.ObjectUtil; | ||
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | ||
| 7 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; | 8 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 8 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 9 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 9 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| @@ -25,6 +26,9 @@ import java.util.Date; | @@ -25,6 +26,9 @@ import java.util.Date; | ||
| 25 | import java.util.List; | 26 | import java.util.List; |
| 26 | import java.util.Map; | 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 | * Bpm 任务 Convert | 33 | * Bpm 任务 Convert |
| 30 | * | 34 | * |
| @@ -167,32 +171,31 @@ public interface BpmTaskConvert { | @@ -167,32 +171,31 @@ public interface BpmTaskConvert { | ||
| 167 | default List<BpmTaskSubSignRespVO> convertList(List<BpmTaskExtDO> bpmTaskExtDOList, | 171 | default List<BpmTaskSubSignRespVO> convertList(List<BpmTaskExtDO> bpmTaskExtDOList, |
| 168 | Map<Long, AdminUserRespDTO> userMap, | 172 | Map<Long, AdminUserRespDTO> userMap, |
| 169 | Map<String, Task> idTaskMap){ | 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 | // 后加签任务不会直接设置 assignee ,所以不存在 assignee 的情况,则去取 owner | 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 | return bpmTaskSubSignRespVO; | 182 | return bpmTaskSubSignRespVO; |
| 182 | }); | 183 | }); |
| 183 | } | 184 | } |
| 184 | 185 | ||
| 185 | /** | 186 | /** |
| 186 | * 转换任务为父子级 | 187 | * 转换任务为父子级 |
| 188 | + * | ||
| 187 | * @param sourceList 原始数据 | 189 | * @param sourceList 原始数据 |
| 188 | * @return 转换后的父子级数组 | 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 | for (BpmTaskRespVO bpmTaskRespVO : sourceList) { | 195 | for (BpmTaskRespVO bpmTaskRespVO : sourceList) { |
| 194 | bpmTaskRespVO.setChildren(parentChildrenTaskListMap.get(bpmTaskRespVO.getId())); | 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,13 +21,13 @@ public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> { | ||
| 21 | return selectList(BpmTaskExtDO::getTaskId, taskIds); | 21 | return selectList(BpmTaskExtDO::getTaskId, taskIds); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | + // TODO @海:BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST) 应该作为条件,mapper 不要有业务 | ||
| 24 | default List<BpmTaskExtDO> selectProcessListByTaskIds(Collection<String> taskIds) { | 25 | default List<BpmTaskExtDO> selectProcessListByTaskIds(Collection<String> taskIds) { |
| 25 | return selectList(new LambdaQueryWrapperX<BpmTaskExtDO>() | 26 | return selectList(new LambdaQueryWrapperX<BpmTaskExtDO>() |
| 26 | .in(BpmTaskExtDO::getTaskId, taskIds) | 27 | .in(BpmTaskExtDO::getTaskId, taskIds) |
| 27 | .in(BpmTaskExtDO::getResult, BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST)); | 28 | .in(BpmTaskExtDO::getResult, BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST)); |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | - | ||
| 31 | default BpmTaskExtDO selectByTaskId(String taskId) { | 31 | default BpmTaskExtDO selectByTaskId(String taskId) { |
| 32 | return selectOne(BpmTaskExtDO::getTaskId, taskId); | 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
| @@ -288,5 +288,4 @@ public class BpmModelServiceImpl implements BpmModelService { | @@ -288,5 +288,4 @@ public class BpmModelServiceImpl implements BpmModelService { | ||
| 288 | processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode()); | 288 | processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode()); |
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | - | ||
| 292 | } | 291 | } |
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,7 +163,7 @@ public interface BpmTaskService { | ||
| 163 | * @param userId 被加签的用户和任务 ID,加签类型 | 163 | * @param userId 被加签的用户和任务 ID,加签类型 |
| 164 | * @param reqVO 当前用户 ID | 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,14 +171,14 @@ public interface BpmTaskService { | ||
| 171 | * @param userId 当前用户ID | 171 | * @param userId 当前用户ID |
| 172 | * @param reqVO 被减签的任务 ID,理由 | 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 | * @return 子任务列表 | 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,7 +53,6 @@ import javax.annotation.Resource; | ||
| 53 | import javax.validation.Valid; | 53 | import javax.validation.Valid; |
| 54 | import java.time.LocalDateTime; | 54 | import java.time.LocalDateTime; |
| 55 | import java.util.*; | 55 | import java.util.*; |
| 56 | -import java.util.stream.Collectors; | ||
| 57 | import java.util.stream.Stream; | 56 | import java.util.stream.Stream; |
| 58 | 57 | ||
| 59 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 58 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| @@ -240,10 +239,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -240,10 +239,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 240 | handleParentTask(task); | 239 | handleParentTask(task); |
| 241 | } | 240 | } |
| 242 | 241 | ||
| 242 | + | ||
| 243 | /** | 243 | /** |
| 244 | * 审批通过存在“后加签”的任务。 | 244 | * 审批通过存在“后加签”的任务。 |
| 245 | * <p> | 245 | * <p> |
| 246 | - * 注意:该任务不能马上完成,需要一个中间状态(ADD_SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理 | 246 | + * 注意:该任务不能马上完成,需要一个中间状态(SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理 |
| 247 | * | 247 | * |
| 248 | * @param task 当前任务 | 248 | * @param task 当前任务 |
| 249 | * @param reqVO 前端请求参数 | 249 | * @param reqVO 前端请求参数 |
| @@ -251,7 +251,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -251,7 +251,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 251 | private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { | 251 | private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { |
| 252 | // 1. 有向后加签,则该任务状态临时设置为 ADD_SIGN_AFTER 状态 | 252 | // 1. 有向后加签,则该任务状态临时设置为 ADD_SIGN_AFTER 状态 |
| 253 | taskExtMapper.updateByTaskId( | 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 | .setReason(reqVO.getReason()).setEndTime(LocalDateTime.now())); | 255 | .setReason(reqVO.getReason()).setEndTime(LocalDateTime.now())); |
| 256 | 256 | ||
| 257 | // 2. 激活子任务 | 257 | // 2. 激活子任务 |
| @@ -265,7 +265,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -265,7 +265,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | /** | 267 | /** |
| 268 | - * 处理当前任务的父任务 | 268 | + * 处理当前任务的父任务,主要处理“加签”的情况 |
| 269 | * | 269 | * |
| 270 | * @param task 当前任务 | 270 | * @param task 当前任务 |
| 271 | */ | 271 | */ |
| @@ -274,76 +274,79 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -274,76 +274,79 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 274 | if (StrUtil.isBlank(parentTaskId)) { | 274 | if (StrUtil.isBlank(parentTaskId)) { |
| 275 | return; | 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 | * @param parentTask 当前审批任务的父任务 | 317 | * @param parentTask 当前审批任务的父任务 |
| 315 | */ | 318 | */ |
| 316 | - private void handleAfterSign(Task parentTask) { | 319 | + // TODO @海:这个逻辑,怎么感觉可以是 parentTask 的 parent,再去调用 handleParentTask 方法;可以微信聊下; |
| 320 | + private void handleParentTaskForAfterSign(Task parentTask) { | ||
| 317 | String parentTaskId = parentTask.getId(); | 321 | String parentTaskId = parentTask.getId(); |
| 318 | - //1. 更新 parentTask 的任务拓展表为通过,并调用 complete 完成自己 | 322 | + // 1. 更新 parentTask 的任务拓展表为通过,并调用 complete 完成自己 |
| 319 | BpmTaskExtDO currentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId()); | 323 | BpmTaskExtDO currentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId()); |
| 320 | - BpmTaskExtDO currentTaskUpdateEntity = new BpmTaskExtDO().setTaskId(parentTask.getId()) | 324 | + BpmTaskExtDO currentTaskExtUpdateObj = new BpmTaskExtDO().setTaskId(parentTask.getId()) |
| 321 | .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); | 325 | .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); |
| 322 | if (currentTaskExt.getEndTime() == null) { | 326 | if (currentTaskExt.getEndTime() == null) { |
| 323 | // 1.1 有这个判断是因为,以前没设置过结束时间,才去设置 | 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 | taskService.complete(parentTaskId); | 332 | taskService.complete(parentTaskId); |
| 329 | 333 | ||
| 330 | // 2. 如果有父级,递归查询上级任务是否都已经完成 | 334 | // 2. 如果有父级,递归查询上级任务是否都已经完成 |
| 331 | if (StrUtil.isEmpty(parentTask.getParentTaskId())) { | 335 | if (StrUtil.isEmpty(parentTask.getParentTaskId())) { |
| 332 | return; | 336 | return; |
| 333 | } | 337 | } |
| 334 | - // TODO @海:这块待讨论,脑子略乱;感觉 handleAfterSign 的后半段,和 handleParentTask 有点重叠??? | ||
| 335 | // 2.1 判断整条链路的任务是否完成 | 338 | // 2.1 判断整条链路的任务是否完成 |
| 336 | // 例如从 A 任务加签了一个 B 任务,B 任务又加签了一个 C 任务,C 任务加签了 D 任务 | 339 | // 例如从 A 任务加签了一个 B 任务,B 任务又加签了一个 C 任务,C 任务加签了 D 任务 |
| 337 | // 此时,D 任务完成,要一直往上找到祖先任务 A调用 complete 方法完成 A 任务 | 340 | // 此时,D 任务完成,要一直往上找到祖先任务 A调用 complete 方法完成 A 任务 |
| 338 | boolean allChildrenTaskFinish = true; | 341 | boolean allChildrenTaskFinish = true; |
| 339 | while (StrUtil.isNotBlank(parentTask.getParentTaskId())) { | 342 | while (StrUtil.isNotBlank(parentTask.getParentTaskId())) { |
| 340 | parentTask = validateTaskExist(parentTask.getParentTaskId()); | 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 | break; | 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 | if (allChildrenTaskFinish) { | 350 | if (allChildrenTaskFinish) { |
| 348 | allChildrenTaskFinish = currentTaskFinish; | 351 | allChildrenTaskFinish = currentTaskFinish; |
| 349 | } | 352 | } |
| @@ -354,19 +357,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -354,19 +357,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 354 | 357 | ||
| 355 | // 3 处理非完成状态的任务 | 358 | // 3 处理非完成状态的任务 |
| 356 | // 3.1 判断当前任务的父任务是否还有子任务 | 359 | // 3.1 判断当前任务的父任务是否还有子任务 |
| 357 | - Long childrenTaskCount = getChildrenTaskCount(bpmTaskExtDO.getTaskId()); | 360 | + Long childrenTaskCount = getChildrenTaskCount(parentTaskExt.getTaskId()); |
| 358 | if (childrenTaskCount > 0) { | 361 | if (childrenTaskCount > 0) { |
| 359 | continue; | 362 | continue; |
| 360 | } | 363 | } |
| 361 | // 3.2 没有子任务,判断当前任务状态是否为 ADD_SIGN_BEFORE 待前加签任务完成 | 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 | // 3.3 需要修改该任务状态为处理中 | 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 | // 3.4 清空 scopeType 字段,用于任务没有子任务时使用该方法,方便任务可以再次被不同的方式加签 | 371 | // 3.4 清空 scopeType 字段,用于任务没有子任务时使用该方法,方便任务可以再次被不同的方式加签 |
| 369 | - parentTask = validateTaskExist(bpmTaskExtDO.getTaskId()); | 372 | + parentTask = validateTaskExist(parentTaskExt.getTaskId()); |
| 370 | clearTaskScopeTypeAndSave(parentTask); | 373 | clearTaskScopeTypeAndSave(parentTask); |
| 371 | } | 374 | } |
| 372 | 375 | ||
| @@ -387,7 +390,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -387,7 +390,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 387 | taskService.saveTask(task); | 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,7 +711,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 709 | 711 | ||
| 710 | @Override | 712 | @Override |
| 711 | @Transactional(rollbackFor = Exception.class) | 713 | @Transactional(rollbackFor = Exception.class) |
| 712 | - public void addSignTask(Long userId, BpmTaskAddSignReqVO reqVO) { | 714 | + public void createSignTask(Long userId, BpmTaskAddSignReqVO reqVO) { |
| 713 | // 1. 获取和校验任务 | 715 | // 1. 获取和校验任务 |
| 714 | TaskEntityImpl taskEntity = validateAddSign(userId, reqVO); | 716 | TaskEntityImpl taskEntity = validateAddSign(userId, reqVO); |
| 715 | List<AdminUserRespDTO> userList = adminUserApi.getUserList(reqVO.getUserIdList()); | 717 | List<AdminUserRespDTO> userList = adminUserApi.getUserList(reqVO.getUserIdList()); |
| @@ -728,7 +730,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -728,7 +730,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 728 | // 2.3 更新扩展表状态 | 730 | // 2.3 更新扩展表状态 |
| 729 | taskExtMapper.updateByTaskId( | 731 | taskExtMapper.updateByTaskId( |
| 730 | new BpmTaskExtDO().setTaskId(taskEntity.getId()) | 732 | new BpmTaskExtDO().setTaskId(taskEntity.getId()) |
| 731 | - .setResult(BpmProcessInstanceResultEnum.ADD_SIGN_BEFORE.getResult()) | 733 | + .setResult(BpmProcessInstanceResultEnum.SIGN_BEFORE.getResult()) |
| 732 | .setReason(reqVO.getReason())); | 734 | .setReason(reqVO.getReason())); |
| 733 | } | 735 | } |
| 734 | // 2.4 记录加签方式,完成任务时需要用到判断 | 736 | // 2.4 记录加签方式,完成任务时需要用到判断 |
| @@ -737,12 +739,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -737,12 +739,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 737 | taskService.saveTask(taskEntity); | 739 | taskService.saveTask(taskEntity); |
| 738 | 740 | ||
| 739 | // 3. 创建加签任务 | 741 | // 3. 创建加签任务 |
| 740 | - createAddSignChildrenTasks(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity); | 742 | + createSignTask(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity); |
| 741 | 743 | ||
| 742 | // 4. 记录加签 comment,拼接结果为: [当前用户]向前加签/向后加签给了[多个用户],理由为:reason | 744 | // 4. 记录加签 comment,拼接结果为: [当前用户]向前加签/向后加签给了[多个用户],理由为:reason |
| 743 | AdminUserRespDTO currentUser = adminUserApi.getUser(userId); | 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 | taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), | 748 | taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), |
| 747 | BpmCommentTypeEnum.ADD_SIGN.getType().toString(), comment); | 749 | BpmCommentTypeEnum.ADD_SIGN.getType().toString(), comment); |
| 748 | } | 750 | } |
| @@ -786,7 +788,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -786,7 +788,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 786 | * @param addSingUserIdList 被加签的用户 ID | 788 | * @param addSingUserIdList 被加签的用户 ID |
| 787 | * @param taskEntity 被加签的任务 | 789 | * @param taskEntity 被加签的任务 |
| 788 | */ | 790 | */ |
| 789 | - private void createAddSignChildrenTasks(List<String> addSingUserIdList, TaskEntityImpl taskEntity) { | 791 | + private void createSignTask(List<String> addSingUserIdList, TaskEntityImpl taskEntity) { |
| 790 | if (CollUtil.isEmpty(addSingUserIdList)) { | 792 | if (CollUtil.isEmpty(addSingUserIdList)) { |
| 791 | return; | 793 | return; |
| 792 | } | 794 | } |
| @@ -795,23 +797,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -795,23 +797,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 795 | if (StrUtil.isBlank(addSignId)) { | 797 | if (StrUtil.isBlank(addSignId)) { |
| 796 | continue; | 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 | * @param parentTask 父任务 | 807 | * @param parentTask 父任务 |
| 806 | * @param assignee 子任务的执行人 | 808 | * @param assignee 子任务的执行人 |
| 807 | - * @return | ||
| 808 | */ | 809 | */ |
| 809 | - private void createChildrenTask(TaskEntityImpl parentTask, String assignee) { | 810 | + private void createSignTask(TaskEntityImpl parentTask, String assignee) { |
| 810 | // 1. 生成子任务 | 811 | // 1. 生成子任务 |
| 811 | TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); | 812 | TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); |
| 812 | task = BpmTaskConvert.INSTANCE.convert(task, parentTask); | 813 | task = BpmTaskConvert.INSTANCE.convert(task, parentTask); |
| 813 | if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { | 814 | if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { |
| 814 | - // 2.1 前加签,才设置审批人,否则设置 owner | 815 | + // 2.1 前加签,设置审批人 |
| 815 | task.setAssignee(assignee); | 816 | task.setAssignee(assignee); |
| 816 | } else { | 817 | } else { |
| 817 | // 2.2.1 设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 | 818 | // 2.2.1 设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 |
| @@ -825,9 +826,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -825,9 +826,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 825 | 826 | ||
| 826 | @Override | 827 | @Override |
| 827 | @Transactional(rollbackFor = Exception.class) | 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 | Task task = validateSubSign(reqVO.getId()); | 831 | Task task = validateSubSign(reqVO.getId()); |
| 830 | - AdminUserRespDTO user = adminUserApi.getUser(userId); | 832 | + // 1.2 校验取消人存在 |
| 831 | AdminUserRespDTO cancelUser = null; | 833 | AdminUserRespDTO cancelUser = null; |
| 832 | if (StrUtil.isNotBlank(task.getAssignee())) { | 834 | if (StrUtil.isNotBlank(task.getAssignee())) { |
| 833 | cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())); | 835 | cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())); |
| @@ -836,20 +838,24 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -836,20 +838,24 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 836 | cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); | 838 | cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); |
| 837 | } | 839 | } |
| 838 | Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); | 840 | Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); |
| 839 | - //1. 获取所有需要删除的任务 ID ,包含当前任务和所有子任务 | 841 | + |
| 842 | + // 2. 删除任务和对应子任务 | ||
| 843 | + // 2.1 获取所有需要删除的任务 ID ,包含当前任务和所有子任务 | ||
| 840 | List<String> allTaskIdList = getAllChildTaskIds(task.getId()); | 844 | List<String> allTaskIdList = getAllChildTaskIds(task.getId()); |
| 841 | - //2. 删除任务和所有子任务 | 845 | + // 2.2 删除任务和所有子任务 |
| 842 | taskService.deleteTasks(allTaskIdList); | 846 | taskService.deleteTasks(allTaskIdList); |
| 843 | - //3. 修改扩展表状态为取消 | 847 | + // 2.3 修改扩展表状态为取消 |
| 848 | + AdminUserRespDTO user = adminUserApi.getUser(userId); | ||
| 844 | taskExtMapper.updateBatchByTaskIdList(allTaskIdList, new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()) | 849 | taskExtMapper.updateBatchByTaskIdList(allTaskIdList, new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()) |
| 845 | .setReason(StrUtil.format("由于{}操作[减签],任务被取消", user.getNickname()))); | 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 | taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), | 854 | taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), |
| 849 | BpmCommentTypeEnum.SUB_SIGN.getType().toString(), comment); | 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,14 +866,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 860 | */ | 866 | */ |
| 861 | private Task validateSubSign(String id) { | 867 | private Task validateSubSign(String id) { |
| 862 | Task task = validateTaskExist(id); | 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 | throw exception(TASK_SUB_SIGN_NO_PARENT); | 877 | throw exception(TASK_SUB_SIGN_NO_PARENT); |
| 866 | } | 878 | } |
| 867 | return task; | 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 | * 获取所有要被取消的删除的任务 ID 集合 | 892 | * 获取所有要被取消的删除的任务 ID 集合 |
| 872 | * | 893 | * |
| 873 | * @param parentTaskId 父级任务ID | 894 | * @param parentTaskId 父级任务ID |
| @@ -875,25 +896,40 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -875,25 +896,40 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 875 | */ | 896 | */ |
| 876 | public List<String> getAllChildTaskIds(String parentTaskId) { | 897 | public List<String> getAllChildTaskIds(String parentTaskId) { |
| 877 | List<String> allChildTaskIds = new ArrayList<>(); | 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 | return allChildTaskIds; | 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,33 +938,32 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 902 | * @param parentTaskId 父任务 ID | 938 | * @param parentTaskId 父任务 ID |
| 903 | * @return 所有子任务的 ID 集合 | 939 | * @return 所有子任务的 ID 集合 |
| 904 | */ | 940 | */ |
| 905 | - private List<String> getChildrenTaskIdList(String parentTaskId) { | 941 | + private List<Task> getChildrenTaskList0(String parentTaskId) { |
| 906 | String tableName = managementService.getTableName(TaskEntity.class); | 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 | @Override | 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 | return Collections.emptyList(); | 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 | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); | 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,7 +147,7 @@ public class DatabaseDocController { | ||
| 147 | */ | 147 | */ |
| 148 | private static ProcessConfig buildProcessConfig() { | 148 | private static ProcessConfig buildProcessConfig() { |
| 149 | return ProcessConfig.builder() | 149 | return ProcessConfig.builder() |
| 150 | - .ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_")) // 忽略表前缀 | 150 | + .ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_", "FLW_")) // 忽略表前缀 |
| 151 | .build(); | 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,8 +29,6 @@ public class SecurityConfiguration { | ||
| 29 | .antMatchers("/swagger-resources/**").anonymous() | 29 | .antMatchers("/swagger-resources/**").anonymous() |
| 30 | .antMatchers("/webjars/**").anonymous() | 30 | .antMatchers("/webjars/**").anonymous() |
| 31 | .antMatchers("/*/api-docs").anonymous(); | 31 | .antMatchers("/*/api-docs").anonymous(); |
| 32 | - // 积木报表 | ||
| 33 | - registry.antMatchers("/jmreport/**").permitAll(); | ||
| 34 | // Spring Boot Actuator 的安全配置 | 32 | // Spring Boot Actuator 的安全配置 |
| 35 | registry.antMatchers("/actuator").anonymous() | 33 | registry.antMatchers("/actuator").anonymous() |
| 36 | .antMatchers("/actuator/**").anonymous(); | 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 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | 3 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| @@ -33,7 +33,7 @@ public class JobLogCleanJob implements JobHandler { | @@ -33,7 +33,7 @@ public class JobLogCleanJob implements JobHandler { | ||
| 33 | @TenantIgnore | 33 | @TenantIgnore |
| 34 | public String execute(String param) { | 34 | public String execute(String param) { |
| 35 | Integer count = jobLogService.cleanJobLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); | 35 | Integer count = jobLogService.cleanJobLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); |
| 36 | - log.info("[count][定时执行清理定时任务日志数量 ({}) 个]", count); | 36 | + log.info("[execute][定时执行清理定时任务日志数量 ({}) 个]", count); |
| 37 | return String.format("定时执行清理定时任务日志数量 %s 个", count); | 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 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | 3 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| @@ -34,7 +34,7 @@ public class AccessLogCleanJob implements JobHandler { | @@ -34,7 +34,7 @@ public class AccessLogCleanJob implements JobHandler { | ||
| 34 | @TenantIgnore | 34 | @TenantIgnore |
| 35 | public String execute(String param) { | 35 | public String execute(String param) { |
| 36 | Integer count = apiAccessLogService.cleanAccessLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); | 36 | Integer count = apiAccessLogService.cleanAccessLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); |
| 37 | - log.info("[count][定时执行清理访问日志数量 ({}) 个]", count); | 37 | + log.info("[execute][定时执行清理访问日志数量 ({}) 个]", count); |
| 38 | return String.format("定时执行清理错误日志数量 %s 个", count); | 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 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | 3 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | 4 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| @@ -34,7 +34,7 @@ public class ErrorLogCleanJob implements JobHandler { | @@ -34,7 +34,7 @@ public class ErrorLogCleanJob implements JobHandler { | ||
| 34 | @TenantIgnore | 34 | @TenantIgnore |
| 35 | public String execute(String param) { | 35 | public String execute(String param) { |
| 36 | Integer count = apiErrorLogService.cleanErrorLog(JOB_CLEAN_RETAIN_DAY,DELETE_LIMIT); | 36 | Integer count = apiErrorLogService.cleanErrorLog(JOB_CLEAN_RETAIN_DAY,DELETE_LIMIT); |
| 37 | - log.info("[count][定时执行清理错误日志数量 ({}) 个]", count); | 37 | + log.info("[execute][定时执行清理错误日志数量 ({}) 个]", count); |
| 38 | return String.format("定时执行清理错误日志数量 %s 个", count); | 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,9 +58,9 @@ public class FileConfigServiceImpl implements FileConfigService { | ||
| 58 | FileConfigDO config = Objects.equals(CACHE_MASTER_ID, id) ? | 58 | FileConfigDO config = Objects.equals(CACHE_MASTER_ID, id) ? |
| 59 | fileConfigMapper.selectByMaster() : fileConfigMapper.selectById(id); | 59 | fileConfigMapper.selectByMaster() : fileConfigMapper.selectById(id); |
| 60 | if (config != null) { | 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,5 +28,5 @@ export function delete${simpleClassName}(id: number) { | ||
| 28 | 28 | ||
| 29 | // 导出${table.classComment} Excel | 29 | // 导出${table.classComment} Excel |
| 30 | export function export${simpleClassName}(params) { | 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 | export const columns: BasicColumn[] = [ | 5 | export const columns: BasicColumn[] = [ |
| 6 | #foreach($column in $columns) | 6 | #foreach($column in $columns) |
| @@ -50,7 +50,7 @@ export const searchFormSchema: FormSchema[] = [ | @@ -50,7 +50,7 @@ export const searchFormSchema: FormSchema[] = [ | ||
| 50 | field: '${javaField}', | 50 | field: '${javaField}', |
| 51 | #if ($column.htmlType == "input") | 51 | #if ($column.htmlType == "input") |
| 52 | component: 'Input', | 52 | component: 'Input', |
| 53 | - #elseif ($column.htmlType == "select" || $column.htmlType == "radio") | 53 | + #elseif ($column.htmlType == "select") |
| 54 | component: 'Select', | 54 | component: 'Select', |
| 55 | componentProps: { | 55 | componentProps: { |
| 56 | #if ("" != $dictType)## 设置了 dictType 数据字典的情况 | 56 | #if ("" != $dictType)## 设置了 dictType 数据字典的情况 |
| @@ -59,6 +59,15 @@ export const searchFormSchema: FormSchema[] = [ | @@ -59,6 +59,15 @@ export const searchFormSchema: FormSchema[] = [ | ||
| 59 | options: [], | 59 | options: [], |
| 60 | #end | 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 | #elseif($column.htmlType == "datetime") | 71 | #elseif($column.htmlType == "datetime") |
| 63 | component: 'RangePicker', | 72 | component: 'RangePicker', |
| 64 | #end | 73 | #end |
| @@ -181,7 +190,8 @@ export const updateFormSchema: FormSchema[] = [ | @@ -181,7 +190,8 @@ export const updateFormSchema: FormSchema[] = [ | ||
| 181 | fileType: 'file', | 190 | fileType: 'file', |
| 182 | maxCount: 1, | 191 | maxCount: 1, |
| 183 | }, | 192 | }, |
| 184 | - #elseif($column.htmlType == "editor")## 文本编辑器component: 'Editor', | 193 | + #elseif($column.htmlType == "editor")## 文本编辑器 |
| 194 | + component: 'Editor', | ||
| 185 | #elseif($column.htmlType == "select")## 下拉框 | 195 | #elseif($column.htmlType == "select")## 下拉框 |
| 186 | component: 'Select', | 196 | component: 'Select', |
| 187 | componentProps: { | 197 | componentProps: { |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | -import ${ simpleClassName }Modal from './${simpleClassName}Modal.vue' | 2 | +import ${simpleClassName}Modal from './${simpleClassName}Modal.vue' |
| 3 | import { columns, searchFormSchema } from './${classNameVar}.data' | 3 | import { columns, searchFormSchema } from './${classNameVar}.data' |
| 4 | import { useI18n } from '@/hooks/web/useI18n' | 4 | import { useI18n } from '@/hooks/web/useI18n' |
| 5 | import { useMessage } from '@/hooks/web/useMessage' | 5 | import { useMessage } from '@/hooks/web/useMessage' |
| 6 | import { useModal } from '@/components/Modal' | 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 | defineOptions({ name: '${table.className}' }) | 10 | defineOptions({ name: '${table.className}' }) |
| 12 | 11 | ||
| @@ -16,7 +15,7 @@ const [registerModal, { openModal }] = useModal() | @@ -16,7 +15,7 @@ const [registerModal, { openModal }] = useModal() | ||
| 16 | 15 | ||
| 17 | const [registerTable, { getForm, reload }] = useTable({ | 16 | const [registerTable, { getForm, reload }] = useTable({ |
| 18 | title: '${table.classComment}列表', | 17 | title: '${table.classComment}列表', |
| 19 | - api: get${ simpleClassName }Page, | 18 | + api: get${simpleClassName}Page, |
| 20 | columns, | 19 | columns, |
| 21 | formConfig: { labelWidth: 120, schemas: searchFormSchema }, | 20 | formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
| 22 | useSearchForm: true, | 21 | useSearchForm: true, |
| @@ -43,14 +42,14 @@ async function handleExport() { | @@ -43,14 +42,14 @@ async function handleExport() { | ||
| 43 | iconType: 'warning', | 42 | iconType: 'warning', |
| 44 | content: t('common.exportMessage'), | 43 | content: t('common.exportMessage'), |
| 45 | async onOk() { | 44 | async onOk() { |
| 46 | - await export${ simpleClassName } (getForm().getFieldsValue()) | 45 | + await export${simpleClassName}(getForm().getFieldsValue()) |
| 47 | createMessage.success(t('common.exportSuccessText')) | 46 | createMessage.success(t('common.exportSuccessText')) |
| 48 | }, | 47 | }, |
| 49 | }) | 48 | }) |
| 50 | } | 49 | } |
| 51 | 50 | ||
| 52 | async function handleDelete(record: Recordable) { | 51 | async function handleDelete(record: Recordable) { |
| 53 | - await delete${ simpleClassName } (record.id) | 52 | + await delete${simpleClassName}(record.id) |
| 54 | createMessage.success(t('common.delSuccessText')) | 53 | createMessage.success(t('common.delSuccessText')) |
| 55 | reload() | 54 | reload() |
| 56 | } | 55 | } |
| @@ -62,7 +61,7 @@ async function handleDelete(record: Recordable) { | @@ -62,7 +61,7 @@ async function handleDelete(record: Recordable) { | ||
| 62 | <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate"> | 61 | <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate"> |
| 63 | {{ t('action.create') }} | 62 | {{ t('action.create') }} |
| 64 | </a-button> | 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 | {{ t('action.export') }} | 65 | {{ t('action.export') }} |
| 67 | </a-button> | 66 | </a-button> |
| 68 | </template> | 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,12 +258,12 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { | ||
| 258 | Long id = fileConfig.getId(); | 258 | Long id = fileConfig.getId(); |
| 259 | // mock 获得 Client | 259 | // mock 获得 Client |
| 260 | FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig()); | 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 | assertSame(fileClient, fileConfigService.getMasterFileClient()); | 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 | eq(fileConfig.getConfig())); | 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,6 +2,7 @@ package cn.iocoder.yudao.module.product.api.comment.dto; | ||
| 2 | 2 | ||
| 3 | import lombok.Data; | 3 | import lombok.Data; |
| 4 | 4 | ||
| 5 | +import javax.validation.constraints.NotNull; | ||
| 5 | import java.util.List; | 6 | import java.util.List; |
| 6 | 7 | ||
| 7 | /** | 8 | /** |
| @@ -15,6 +16,7 @@ public class ProductCommentCreateReqDTO { | @@ -15,6 +16,7 @@ public class ProductCommentCreateReqDTO { | ||
| 15 | /** | 16 | /** |
| 16 | * 商品 SKU 编号 | 17 | * 商品 SKU 编号 |
| 17 | */ | 18 | */ |
| 19 | + @NotNull(message = "商品 SKU 编号不能为空") | ||
| 18 | private Long skuId; | 20 | private Long skuId; |
| 19 | /** | 21 | /** |
| 20 | * 订单编号 | 22 | * 订单编号 |
| @@ -26,20 +28,19 @@ public class ProductCommentCreateReqDTO { | @@ -26,20 +28,19 @@ public class ProductCommentCreateReqDTO { | ||
| 26 | private Long orderItemId; | 28 | private Long orderItemId; |
| 27 | 29 | ||
| 28 | /** | 30 | /** |
| 29 | - * 评分星级 1-5 分 | ||
| 30 | - */ | ||
| 31 | - private Integer scores; | ||
| 32 | - /** | ||
| 33 | * 描述星级 1-5 分 | 31 | * 描述星级 1-5 分 |
| 34 | */ | 32 | */ |
| 33 | + @NotNull(message = "描述星级不能为空") | ||
| 35 | private Integer descriptionScores; | 34 | private Integer descriptionScores; |
| 36 | /** | 35 | /** |
| 37 | * 服务星级 1-5 分 | 36 | * 服务星级 1-5 分 |
| 38 | */ | 37 | */ |
| 38 | + @NotNull(message = "服务星级不能为空") | ||
| 39 | private Integer benefitScores; | 39 | private Integer benefitScores; |
| 40 | /** | 40 | /** |
| 41 | * 评论内容 | 41 | * 评论内容 |
| 42 | */ | 42 | */ |
| 43 | + @NotNull(message = "评论内容不能为空") | ||
| 43 | private String content; | 44 | private String content; |
| 44 | /** | 45 | /** |
| 45 | * 评论图片地址数组,以逗号分隔最多上传 9 张 | 46 | * 评论图片地址数组,以逗号分隔最多上传 9 张 |
| @@ -49,11 +50,12 @@ public class ProductCommentCreateReqDTO { | @@ -49,11 +50,12 @@ public class ProductCommentCreateReqDTO { | ||
| 49 | /** | 50 | /** |
| 50 | * 是否匿名 | 51 | * 是否匿名 |
| 51 | */ | 52 | */ |
| 53 | + @NotNull(message = "是否匿名不能为空") | ||
| 52 | private Boolean anonymous; | 54 | private Boolean anonymous; |
| 53 | /** | 55 | /** |
| 54 | * 评价人 | 56 | * 评价人 |
| 55 | */ | 57 | */ |
| 58 | + @NotNull(message = "评价人不能为空") | ||
| 56 | private Long userId; | 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,8 +3,6 @@ package cn.iocoder.yudao.module.product.api.spu.dto; | ||
| 3 | import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | 3 | import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | -import java.util.List; | ||
| 7 | - | ||
| 8 | // TODO @LeeYan9: ProductSpuRespDTO | 6 | // TODO @LeeYan9: ProductSpuRespDTO |
| 9 | /** | 7 | /** |
| 10 | * 商品 SPU 信息 Response DTO | 8 | * 商品 SPU 信息 Response DTO |
| @@ -27,55 +25,22 @@ public class ProductSpuRespDTO { | @@ -27,55 +25,22 @@ public class ProductSpuRespDTO { | ||
| 27 | */ | 25 | */ |
| 28 | private String name; | 26 | private String name; |
| 29 | /** | 27 | /** |
| 30 | - * 关键字 | ||
| 31 | - */ | ||
| 32 | - private String keyword; | ||
| 33 | - /** | ||
| 34 | * 单位 | 28 | * 单位 |
| 35 | * | 29 | * |
| 36 | * 对应 product_unit 数据字典 | 30 | * 对应 product_unit 数据字典 |
| 37 | */ | 31 | */ |
| 38 | private Integer unit; | 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 | private Long categoryId; | 37 | private Long categoryId; |
| 57 | /** | 38 | /** |
| 58 | - * 商品品牌编号 | ||
| 59 | - */ | ||
| 60 | - private Long brandId; | ||
| 61 | - /** | ||
| 62 | * 商品封面图 | 39 | * 商品封面图 |
| 63 | */ | 40 | */ |
| 64 | private String picUrl; | 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 | * <p> | 45 | * <p> |
| 81 | * 枚举 {@link ProductSpuStatusEnum} | 46 | * 枚举 {@link ProductSpuStatusEnum} |
| @@ -124,22 +89,6 @@ public class ProductSpuRespDTO { | @@ -124,22 +89,6 @@ public class ProductSpuRespDTO { | ||
| 124 | */ | 89 | */ |
| 125 | private Integer giveIntegral; | 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 | package cn.iocoder.yudao.module.product.api.sku; | 1 | package cn.iocoder.yudao.module.product.api.sku; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; | 3 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 5 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; | 4 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; |
| 6 | import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; | 5 | import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; |
| @@ -11,7 +10,6 @@ import org.springframework.validation.annotation.Validated; | @@ -11,7 +10,6 @@ import org.springframework.validation.annotation.Validated; | ||
| 11 | 10 | ||
| 12 | import javax.annotation.Resource; | 11 | import javax.annotation.Resource; |
| 13 | import java.util.Collection; | 12 | import java.util.Collection; |
| 14 | -import java.util.Collections; | ||
| 15 | import java.util.List; | 13 | import java.util.List; |
| 16 | 14 | ||
| 17 | /** | 15 | /** |
| @@ -35,18 +33,12 @@ public class ProductSkuApiImpl implements ProductSkuApi { | @@ -35,18 +33,12 @@ public class ProductSkuApiImpl implements ProductSkuApi { | ||
| 35 | 33 | ||
| 36 | @Override | 34 | @Override |
| 37 | public List<ProductSkuRespDTO> getSkuList(Collection<Long> ids) { | 35 | public List<ProductSkuRespDTO> getSkuList(Collection<Long> ids) { |
| 38 | - if (CollUtil.isEmpty(ids)) { | ||
| 39 | - return Collections.emptyList(); | ||
| 40 | - } | ||
| 41 | List<ProductSkuDO> skus = productSkuService.getSkuList(ids); | 36 | List<ProductSkuDO> skus = productSkuService.getSkuList(ids); |
| 42 | return ProductSkuConvert.INSTANCE.convertList04(skus); | 37 | return ProductSkuConvert.INSTANCE.convertList04(skus); |
| 43 | } | 38 | } |
| 44 | 39 | ||
| 45 | @Override | 40 | @Override |
| 46 | public List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds) { | 41 | public List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds) { |
| 47 | - if (CollUtil.isEmpty(spuIds)) { | ||
| 48 | - return Collections.emptyList(); | ||
| 49 | - } | ||
| 50 | List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spuIds); | 42 | List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spuIds); |
| 51 | return ProductSkuConvert.INSTANCE.convertList04(skus); | 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,9 +27,6 @@ public class ProductSpuApiImpl implements ProductSpuApi { | ||
| 27 | 27 | ||
| 28 | @Override | 28 | @Override |
| 29 | public List<ProductSpuRespDTO> getSpuList(Collection<Long> ids) { | 29 | public List<ProductSpuRespDTO> getSpuList(Collection<Long> ids) { |
| 30 | - if (CollectionUtil.isEmpty(ids)) { | ||
| 31 | - return Collections.emptyList(); | ||
| 32 | - } | ||
| 33 | return ProductSpuConvert.INSTANCE.convertList2(spuService.getSpuList(ids)); | 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 | package cn.iocoder.yudao.module.product.convert.property; | 1 | package cn.iocoder.yudao.module.product.convert.property; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 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 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; | 4 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; |
| 8 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; |
| 9 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; | 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 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; | 7 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; |
| 12 | -import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; | ||
| 13 | import org.mapstruct.Mapper; | 8 | import org.mapstruct.Mapper; |
| 14 | import org.mapstruct.factory.Mappers; | 9 | import org.mapstruct.factory.Mappers; |
| 15 | 10 | ||
| 16 | import java.util.List; | 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 | * 属性值 Convert | 14 | * 属性值 Convert |
| @@ -38,18 +30,4 @@ public interface ProductPropertyValueConvert { | @@ -38,18 +30,4 @@ public interface ProductPropertyValueConvert { | ||
| 38 | 30 | ||
| 39 | PageResult<ProductPropertyValueRespVO> convertPage(PageResult<ProductPropertyValueDO> page); | 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,7 +5,6 @@ import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.Produc | ||
| 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; |
| 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; | 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; |
| 7 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; | 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 | import java.util.Collection; | 9 | import java.util.Collection; |
| 11 | import java.util.List; | 10 | import java.util.List; |
| @@ -57,14 +56,6 @@ public interface ProductPropertyValueService { | @@ -57,14 +56,6 @@ public interface ProductPropertyValueService { | ||
| 57 | List<ProductPropertyValueDO> getPropertyValueListByPropertyId(Collection<Long> propertyIds); | 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 | * @param propertyId 属性项编号数 | 61 | * @param propertyId 属性项编号数 |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java
| 1 | package cn.iocoder.yudao.module.product.service.property; | 1 | package cn.iocoder.yudao.module.product.service.property; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; | 4 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; |
| 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; |
| 7 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; | 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; |
| 8 | import cn.iocoder.yudao.module.product.convert.property.ProductPropertyValueConvert; | 7 | import cn.iocoder.yudao.module.product.convert.property.ProductPropertyValueConvert; |
| 9 | -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; | ||
| 10 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; | 8 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; |
| 11 | import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper; | 9 | import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper; |
| 12 | -import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; | ||
| 13 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; | 10 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; |
| 14 | import org.springframework.context.annotation.Lazy; | 11 | import org.springframework.context.annotation.Lazy; |
| 15 | import org.springframework.stereotype.Service; | 12 | import org.springframework.stereotype.Service; |
| @@ -17,11 +14,9 @@ import org.springframework.validation.annotation.Validated; | @@ -17,11 +14,9 @@ import org.springframework.validation.annotation.Validated; | ||
| 17 | 14 | ||
| 18 | import javax.annotation.Resource; | 15 | import javax.annotation.Resource; |
| 19 | import java.util.Collection; | 16 | import java.util.Collection; |
| 20 | -import java.util.Collections; | ||
| 21 | import java.util.List; | 17 | import java.util.List; |
| 22 | 18 | ||
| 23 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 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 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_EXISTS; | 20 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_EXISTS; |
| 26 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS; | 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,23 +95,6 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ | ||
| 100 | } | 95 | } |
| 101 | 96 | ||
| 102 | @Override | 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 | public Integer getPropertyValueCountByPropertyId(Long propertyId) { | 98 | public Integer getPropertyValueCountByPropertyId(Long propertyId) { |
| 121 | return productPropertyValueMapper.selectCountByPropertyId(propertyId); | 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,6 +153,9 @@ public class ProductSkuServiceImpl implements ProductSkuService { | ||
| 153 | 153 | ||
| 154 | @Override | 154 | @Override |
| 155 | public List<ProductSkuDO> getSkuListBySpuId(Collection<Long> spuIds) { | 155 | public List<ProductSkuDO> getSkuListBySpuId(Collection<Long> spuIds) { |
| 156 | + if (CollUtil.isEmpty(spuIds)) { | ||
| 157 | + return Collections.emptyList(); | ||
| 158 | + } | ||
| 156 | return productSkuMapper.selectListBySpuId(spuIds); | 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,6 +188,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { | ||
| 188 | 188 | ||
| 189 | @Override | 189 | @Override |
| 190 | public List<ProductSpuDO> getSpuList(Collection<Long> ids) { | 190 | public List<ProductSpuDO> getSpuList(Collection<Long> ids) { |
| 191 | + if (CollUtil.isEmpty(ids)) { | ||
| 192 | + return Collections.emptyList(); | ||
| 193 | + } | ||
| 191 | return productSpuMapper.selectBatchIds(ids); | 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,14 +42,6 @@ public interface CombinationRecordApi { | ||
| 42 | boolean isCombinationRecordSuccess(Long userId, Long orderId); | 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,8 +14,8 @@ | ||
| 14 | <name>${project.artifactId}</name> | 14 | <name>${project.artifactId}</name> |
| 15 | 15 | ||
| 16 | <description> | 16 | <description> |
| 17 | - market模块,主要实现营销相关功能 | ||
| 18 | - 例如:营销活动、banner广告、优惠券、优惠码等功能。 | 17 | + promotion 模块,主要实现营销相关功能 |
| 18 | + 例如:营销活动、banner 广告、优惠券、优惠码等功能。 | ||
| 19 | </description> | 19 | </description> |
| 20 | 20 | ||
| 21 | <dependencies> | 21 | <dependencies> |
| @@ -49,10 +49,6 @@ | @@ -49,10 +49,6 @@ | ||
| 49 | <groupId>cn.iocoder.boot</groupId> | 49 | <groupId>cn.iocoder.boot</groupId> |
| 50 | <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> | 50 | <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> |
| 51 | </dependency> | 51 | </dependency> |
| 52 | - <dependency> | ||
| 53 | - <groupId>cn.iocoder.boot</groupId> | ||
| 54 | - <artifactId>yudao-spring-boot-starter-biz-weixin</artifactId> | ||
| 55 | - </dependency> | ||
| 56 | 52 | ||
| 57 | <!-- Web 相关 --> | 53 | <!-- Web 相关 --> |
| 58 | <dependency> | 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,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.api.bargain; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService; | 3 | import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService; |
| 4 | import org.springframework.stereotype.Service; | 4 | import org.springframework.stereotype.Service; |
| 5 | +import org.springframework.validation.annotation.Validated; | ||
| 5 | 6 | ||
| 6 | import javax.annotation.Resource; | 7 | import javax.annotation.Resource; |
| 7 | 8 | ||
| @@ -11,6 +12,7 @@ import javax.annotation.Resource; | @@ -11,6 +12,7 @@ import javax.annotation.Resource; | ||
| 11 | * @author HUIHUI | 12 | * @author HUIHUI |
| 12 | */ | 13 | */ |
| 13 | @Service | 14 | @Service |
| 15 | +@Validated | ||
| 14 | public class BargainActivityApiImpl implements BargainActivityApi { | 16 | public class BargainActivityApiImpl implements BargainActivityApi { |
| 15 | 17 | ||
| 16 | @Resource | 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,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.bargain; | ||
| 3 | import cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO; | 3 | import cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO; |
| 4 | import cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService; | 4 | import cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService; |
| 5 | import org.springframework.stereotype.Service; | 5 | import org.springframework.stereotype.Service; |
| 6 | +import org.springframework.validation.annotation.Validated; | ||
| 6 | 7 | ||
| 7 | import javax.annotation.Resource; | 8 | import javax.annotation.Resource; |
| 8 | 9 | ||
| @@ -12,6 +13,7 @@ import javax.annotation.Resource; | @@ -12,6 +13,7 @@ import javax.annotation.Resource; | ||
| 12 | * @author HUIHUI | 13 | * @author HUIHUI |
| 13 | */ | 14 | */ |
| 14 | @Service | 15 | @Service |
| 16 | +@Validated | ||
| 15 | public class BargainRecordApiImpl implements BargainRecordApi { | 17 | public class BargainRecordApiImpl implements BargainRecordApi { |
| 16 | 18 | ||
| 17 | @Resource | 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,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR | ||
| 8 | import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; | 8 | import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; |
| 9 | import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; | 9 | import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; |
| 10 | import org.springframework.stereotype.Service; | 10 | import org.springframework.stereotype.Service; |
| 11 | +import org.springframework.validation.annotation.Validated; | ||
| 11 | 12 | ||
| 12 | import javax.annotation.Resource; | 13 | import javax.annotation.Resource; |
| 13 | 14 | ||
| @@ -20,6 +21,7 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINA | @@ -20,6 +21,7 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINA | ||
| 20 | * @author HUIHUI | 21 | * @author HUIHUI |
| 21 | */ | 22 | */ |
| 22 | @Service | 23 | @Service |
| 24 | +@Validated | ||
| 23 | public class CombinationRecordApiImpl implements CombinationRecordApi { | 25 | public class CombinationRecordApiImpl implements CombinationRecordApi { |
| 24 | 26 | ||
| 25 | @Resource | 27 | @Resource |
| @@ -45,11 +47,6 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { | @@ -45,11 +47,6 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { | ||
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | @Override | 49 | @Override |
| 48 | - public void updateRecordStatusToFailed(Long userId, Long orderId) { | ||
| 49 | - recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.FAILED.getStatus(), userId, orderId); | ||
| 50 | - } | ||
| 51 | - | ||
| 52 | - @Override | ||
| 53 | public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) { | 50 | public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) { |
| 54 | return recordService.validateJoinCombination(userId, activityId, headId, skuId, count); | 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,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; | ||
| 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
| 9 | import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; | 9 | import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; |
| 10 | import org.springframework.stereotype.Service; | 10 | import org.springframework.stereotype.Service; |
| 11 | +import org.springframework.validation.annotation.Validated; | ||
| 11 | 12 | ||
| 12 | import javax.annotation.Resource; | 13 | import javax.annotation.Resource; |
| 13 | 14 | ||
| @@ -17,6 +18,7 @@ import javax.annotation.Resource; | @@ -17,6 +18,7 @@ import javax.annotation.Resource; | ||
| 17 | * @author 芋道源码 | 18 | * @author 芋道源码 |
| 18 | */ | 19 | */ |
| 19 | @Service | 20 | @Service |
| 21 | +@Validated | ||
| 20 | public class CouponApiImpl implements CouponApi { | 22 | public class CouponApiImpl implements CouponApi { |
| 21 | 23 | ||
| 22 | @Resource | 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,6 +4,7 @@ import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO | ||
| 4 | import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert; | 4 | import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert; |
| 5 | import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService; | 5 | import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService; |
| 6 | import org.springframework.stereotype.Service; | 6 | import org.springframework.stereotype.Service; |
| 7 | +import org.springframework.validation.annotation.Validated; | ||
| 7 | 8 | ||
| 8 | import javax.annotation.Resource; | 9 | import javax.annotation.Resource; |
| 9 | import java.util.Collection; | 10 | import java.util.Collection; |
| @@ -15,6 +16,7 @@ import java.util.List; | @@ -15,6 +16,7 @@ import java.util.List; | ||
| 15 | * @author 芋道源码 | 16 | * @author 芋道源码 |
| 16 | */ | 17 | */ |
| 17 | @Service | 18 | @Service |
| 19 | +@Validated | ||
| 18 | public class DiscountActivityApiImpl implements DiscountActivityApi { | 20 | public class DiscountActivityApiImpl implements DiscountActivityApi { |
| 19 | 21 | ||
| 20 | @Resource | 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,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.reward; | ||
| 3 | import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; | 3 | import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; |
| 4 | import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; | 4 | import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; |
| 5 | import org.springframework.stereotype.Service; | 5 | import org.springframework.stereotype.Service; |
| 6 | +import org.springframework.validation.annotation.Validated; | ||
| 6 | 7 | ||
| 7 | import javax.annotation.Resource; | 8 | import javax.annotation.Resource; |
| 8 | import java.util.Collection; | 9 | import java.util.Collection; |
| @@ -14,6 +15,7 @@ import java.util.List; | @@ -14,6 +15,7 @@ import java.util.List; | ||
| 14 | * @author 芋道源码 | 15 | * @author 芋道源码 |
| 15 | */ | 16 | */ |
| 16 | @Service | 17 | @Service |
| 18 | +@Validated | ||
| 17 | public class RewardActivityApiImpl implements RewardActivityApi { | 19 | public class RewardActivityApiImpl implements RewardActivityApi { |
| 18 | 20 | ||
| 19 | @Resource | 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,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.seckill; | ||
| 3 | import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO; | 3 | import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO; |
| 4 | import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService; | 4 | import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService; |
| 5 | import org.springframework.stereotype.Service; | 5 | import org.springframework.stereotype.Service; |
| 6 | +import org.springframework.validation.annotation.Validated; | ||
| 6 | 7 | ||
| 7 | import javax.annotation.Resource; | 8 | import javax.annotation.Resource; |
| 8 | 9 | ||
| @@ -12,6 +13,7 @@ import javax.annotation.Resource; | @@ -12,6 +13,7 @@ import javax.annotation.Resource; | ||
| 12 | * @author HUIHUI | 13 | * @author HUIHUI |
| 13 | */ | 14 | */ |
| 14 | @Service | 15 | @Service |
| 16 | +@Validated | ||
| 15 | public class SeckillActivityApiImpl implements SeckillActivityApi { | 17 | public class SeckillActivityApiImpl implements SeckillActivityApi { |
| 16 | 18 | ||
| 17 | @Resource | 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,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | import lombok.ToString; | 5 | import lombok.ToString; |
| 6 | 6 | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | + | ||
| 7 | @Schema(description = "管理后台 - Banner Response VO") | 9 | @Schema(description = "管理后台 - Banner Response VO") |
| 8 | @Data | 10 | @Data |
| 9 | @ToString(callSuper = true) | 11 | @ToString(callSuper = true) |
| @@ -12,4 +14,7 @@ public class BannerRespVO extends BannerBaseVO { | @@ -12,4 +14,7 @@ public class BannerRespVO extends BannerBaseVO { | ||
| 12 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED) | 14 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED) |
| 13 | private Long id; | 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,13 +96,13 @@ public class AppCouponTemplateController { | ||
| 96 | */ | 96 | */ |
| 97 | private Long getProductScopeValue(Integer productScope, Long spuId) { | 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 | return null; | 100 | return null; |
| 101 | } | 101 | } |
| 102 | // 品类券:查询商品的品类编号 | 102 | // 品类券:查询商品的品类编号 |
| 103 | if (Objects.equals(productScope, PromotionProductScopeEnum.CATEGORY.getScope()) && spuId != null) { | 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 | return spuId; | 108 | return spuId; |