diff --git a/README.md b/README.md index c28bae463..768f6120b 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ _前端基于 crmeb uniapp 经过授权重构,优化代码实现,接入芋 | 框架 | 说明 | 版本 | 学习指南 | |---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------| -| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.16 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | +| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.17 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | | [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | | | [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.19 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | | [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | diff --git a/pom.xml b/pom.xml index 88a3a34eb..dae40f3dc 100644 --- a/pom.xml +++ b/pom.xml @@ -13,16 +13,16 @@ yudao-server - yudao-module-member yudao-module-system yudao-module-infra - + + - yudao-module-pay - yudao-module-mall + + - yudao-example + ${project.artifactId} @@ -30,7 +30,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 1.8.2-snapshot + 1.8.3-snapshot 1.8 ${java.version} @@ -40,7 +40,7 @@ 1.5.0 1.18.30 - 2.7.16 + 2.7.17 1.5.5.Final UTF-8 diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 2e589fbac..dbd6e2f3c 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -14,28 +14,30 @@ https://github.com/YunaiV/ruoyi-vue-pro - 1.8.2-snapshot + 1.8.3-snapshot 1.5.0 - 2.7.16 + 2.7.17 1.6.15 4.3.0 2.5 - 1.2.19 - 3.5.3.2 - 3.5.3.2 + 1.2.20 + 3.5.4 + 3.5.4 3.6.1 - 1.4.6 + 1.4.7 3.18.0 - 8.1.2.141 + 8.1.3.62 + + 2.2.3 - 2.2.3 + 2.2.5 1.7.1 8.12.0 - 2.7.10 + 2.7.11 0.33.0 7.2.11.RELEASE @@ -44,8 +46,8 @@ 6.8.0 - 1.0.8 - 1.16.1 + 1.0.10 + 1.16.2 1.18.30 1.5.5.Final 5.8.22 @@ -53,10 +55,10 @@ 2.3 1.0.5 1.2.83 - 32.1.2-jre + 32.1.3-jre 5.1.0 2.14.2 - 3.9.0 + 3.10.0 0.1.55 2.7.0 2.7.0 @@ -67,8 +69,8 @@ 8.5.6 4.6.4 2.2.1 - 3.1.853 - 1.0.5 + 3.1.880 + 1.0.7 1.6.1 2.12.2 4.5.0 @@ -96,11 +98,6 @@ yudao-spring-boot-starter-biz-operatelog ${revision} - - cn.iocoder.boot - yudao-spring-boot-starter-biz-trade - ${revision} - cn.iocoder.boot yudao-spring-boot-starter-biz-dict @@ -260,6 +257,12 @@ ${revision} + + org.apache.rocketmq + rocketmq-spring-boot-starter + ${rocketmq-spring.version} + + cn.iocoder.boot diff --git a/yudao-example/yudao-sso-demo-by-code/pom.xml b/yudao-example/yudao-sso-demo-by-code/pom.xml index dd617588d..dffca80ea 100644 --- a/yudao-example/yudao-sso-demo-by-code/pom.xml +++ b/yudao-example/yudao-sso-demo-by-code/pom.xml @@ -21,7 +21,7 @@ 8 UTF-8 - 2.7.16 + 2.7.17 diff --git a/yudao-example/yudao-sso-demo-by-password/pom.xml b/yudao-example/yudao-sso-demo-by-password/pom.xml index a5fc6129c..63287913f 100644 --- a/yudao-example/yudao-sso-demo-by-password/pom.xml +++ b/yudao-example/yudao-sso-demo-by-password/pom.xml @@ -21,7 +21,7 @@ 8 UTF-8 - 2.7.16 + 2.7.17 diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java index 48cf8e7ef..07a8f39b5 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + /** * Key Value 的键值对 * @@ -12,7 +14,7 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor -public class KeyValue { +public class KeyValue implements Serializable { private K key; private V value; diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index 689ce0dd0..366e78e23 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/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; import java.util.*; import java.util.function.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.util.Arrays.asList; @@ -271,4 +272,20 @@ public class CollectionUtils { return deptId == null ? Collections.emptyList() : Collections.singleton(deptId); } + public static List convertListByFlatMap(Collection from, + Function> func) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public static Set convertSetByFlatMap(Collection from, + Function> func) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeAutoConfiguration.java similarity index 97% rename from yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeConfiguration.java rename to yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeAutoConfiguration.java index 74c21f189..ed2c92fc2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeConfiguration.java +++ b/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; @ConditionalOnProperty(prefix = "yudao.error-code", value = "enable", matchIfMissing = true) // 允许使用 yudao.error-code.enable=false 禁用访问日志 @EnableConfigurationProperties(ErrorCodeProperties.class) @EnableScheduling // 开启调度任务的功能,因为 ErrorCodeRemoteLoader 通过定时刷新错误码 -public class YudaoErrorCodeConfiguration { +public class YudaoErrorCodeAutoConfiguration { @Bean public ErrorCodeAutoGenerator errorCodeAutoGenerator(@Value("${spring.application.name}") String applicationName, diff --git a/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 69aa25696..ecd3f7183 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1 @@ -cn.iocoder.yudao.framework.errorcode.config.YudaoErrorCodeConfiguration \ No newline at end of file +cn.iocoder.yudao.framework.errorcode.config.YudaoErrorCodeAutoConfiguration diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml index 0fb0a4f71..c1a537613 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml @@ -48,6 +48,22 @@ cn.iocoder.boot yudao-spring-boot-starter-mq + true + + + org.springframework.kafka + spring-kafka + true + + + org.springframework.amqp + spring-rabbit + true + + + org.apache.rocketmq + rocketmq-spring-boot-starter + true diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index a632aab7a..c3dd35c91 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/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; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect; -import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor; +import cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer; +import cn.iocoder.yudao.framework.tenant.core.mq.redis.TenantRedisMessageInterceptor; +import cn.iocoder.yudao.framework.tenant.core.mq.rocketmq.TenantRocketMQInitializer; import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; @@ -18,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.tenant.TenantApi; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; @@ -92,6 +95,18 @@ public class YudaoTenantAutoConfiguration { return new TenantRedisMessageInterceptor(); } + @Bean + @ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") + public TenantRabbitMQInitializer tenantRabbitMQInitializer() { + return new TenantRabbitMQInitializer(); + } + + @Bean + @ConditionalOnClass(name = "org.apache.rocketmq.spring.core.RocketMQTemplate") + public TenantRocketMQInitializer tenantRocketMQInitializer() { + return new TenantRocketMQInitializer(); + } + // ========== Job ========== @Bean diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java new file mode 100644 index 000000000..8bf7cc1a8 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.framework.tenant.core.mq.kafka; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * 多租户的 Kafka 的 {@link EnvironmentPostProcessor} 实现类 + * + * Kafka Producer 发送消息时,增加 {@link TenantKafkaProducerInterceptor} 拦截器 + * + * @author 芋道源码 + */ +@Slf4j +public class TenantKafkaEnvironmentPostProcessor implements EnvironmentPostProcessor { + + private static final String PROPERTY_KEY_INTERCEPTOR_CLASSES = "spring.kafka.producer.properties.interceptor.classes"; + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + // 添加 TenantKafkaProducerInterceptor 拦截器 + try { + String value = environment.getProperty(PROPERTY_KEY_INTERCEPTOR_CLASSES); + if (StrUtil.isEmpty(value)) { + value = TenantKafkaProducerInterceptor.class.getName(); + } else { + value += "," + TenantKafkaProducerInterceptor.class.getName(); + } + environment.getSystemProperties().put(PROPERTY_KEY_INTERCEPTOR_CLASSES, value); + } catch (NoClassDefFoundError ignore) { + // 如果触发 NoClassDefFoundError 异常,说明 TenantKafkaProducerInterceptor 类不存在,即没引入 kafka-spring 依赖 + } + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java new file mode 100644 index 000000000..8ded8019a --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.framework.tenant.core.mq.kafka; + +import cn.hutool.core.util.ReflectUtil; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import org.apache.kafka.clients.producer.ProducerInterceptor; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.header.Headers; +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; + +/** + * Kafka 消息队列的多租户 {@link ProducerInterceptor} 实现类 + * + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 + * + * @author 芋道源码 + */ +public class TenantKafkaProducerInterceptor implements ProducerInterceptor { + + @Override + public ProducerRecord onSend(ProducerRecord record) { + Long tenantId = TenantContextHolder.getTenantId(); + if (tenantId != null) { + Headers headers = (Headers) ReflectUtil.getFieldValue(record, "headers"); // private 属性,没有 get 方法,智能反射 + headers.add(HEADER_TENANT_ID, tenantId.toString().getBytes()); + } + return record; + } + + @Override + public void onAcknowledgement(RecordMetadata metadata, Exception exception) { + } + + @Override + public void close() { + } + + @Override + public void configure(Map configs) { + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java new file mode 100644 index 000000000..b856ce954 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +/** + * 多租户的 RabbitMQ 初始化器 + * + * @author 芋道源码 + */ +public class TenantRabbitMQInitializer implements BeanPostProcessor { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof RabbitTemplate) { + RabbitTemplate rabbitTemplate = (RabbitTemplate) bean; + rabbitTemplate.addBeforePublishPostProcessors(new TenantRabbitMQMessagePostProcessor()); + } + return bean; + } + +} \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java new file mode 100644 index 000000000..3e6969cd2 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq; + +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import org.apache.kafka.clients.producer.ProducerInterceptor; +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessagePostProcessor; +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; + +/** + * RabbitMQ 消息队列的多租户 {@link ProducerInterceptor} 实现类 + * + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 + * + * @author 芋道源码 + */ +public class TenantRabbitMQMessagePostProcessor implements MessagePostProcessor { + + @Override + public Message postProcessMessage(Message message) throws AmqpException { + Long tenantId = TenantContextHolder.getTenantId(); + if (tenantId != null) { + message.getMessageProperties().getHeaders().put(HEADER_TENANT_ID, tenantId); + } + return message; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java similarity index 86% rename from yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java rename to yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java index c493b41d1..f6b7747ff 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.framework.tenant.core.mq; +package cn.iocoder.yudao.framework.tenant.core.mq.redis; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java new file mode 100644 index 000000000..d9d7334e0 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import org.apache.rocketmq.client.hook.ConsumeMessageContext; +import org.apache.rocketmq.client.hook.ConsumeMessageHook; +import org.apache.rocketmq.common.message.MessageExt; +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; + +import java.util.List; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; + +/** + * RocketMQ 消息队列的多租户 {@link ConsumeMessageHook} 实现类 + * + * Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 + * + * @author 芋道源码 + */ +public class TenantRocketMQConsumeMessageHook implements ConsumeMessageHook { + + @Override + public String hookName() { + return getClass().getSimpleName(); + } + + @Override + public void consumeMessageBefore(ConsumeMessageContext context) { + // 校验,消息必须是单条,不然设置租户可能不正确 + List messages = context.getMsgList(); + Assert.isTrue(messages.size() == 1, "消息条数({})不正确", messages.size()); + // 设置租户编号 + String tenantId = messages.get(0).getUserProperty(HEADER_TENANT_ID); + if (StrUtil.isNotEmpty(tenantId)) { + TenantContextHolder.setTenantId(Long.parseLong(tenantId)); + } + } + + @Override + public void consumeMessageAfter(ConsumeMessageContext context) { + TenantContextHolder.clear(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java new file mode 100644 index 000000000..7f12ac520 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; + +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; +import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +/** + * 多租户的 RocketMQ 初始化器 + * + * @author 芋道源码 + */ +public class TenantRocketMQInitializer implements BeanPostProcessor { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof DefaultRocketMQListenerContainer) { + DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean; + initTenantConsumer(container.getConsumer()); + } else if (bean instanceof RocketMQTemplate) { + RocketMQTemplate template = (RocketMQTemplate) bean; + initTenantProducer(template.getProducer()); + } + return bean; + } + + private void initTenantProducer(DefaultMQProducer producer) { + if (producer == null) { + return; + } + DefaultMQProducerImpl producerImpl = producer.getDefaultMQProducerImpl(); + if (producerImpl == null) { + return; + } + producerImpl.registerSendMessageHook(new TenantRocketMQSendMessageHook()); + } + + private void initTenantConsumer(DefaultMQPushConsumer consumer) { + if (consumer == null) { + return; + } + DefaultMQPushConsumerImpl consumerImpl = consumer.getDefaultMQPushConsumerImpl(); + if (consumerImpl == null) { + return; + } + consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook()); + } + +} \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java new file mode 100644 index 000000000..4f0307465 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; + +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.hook.SendMessageHook; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; + +/** + * RocketMQ 消息队列的多租户 {@link SendMessageHook} 实现类 + * + * Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 + * + * @author 芋道源码 + */ +public class TenantRocketMQSendMessageHook implements SendMessageHook { + + @Override + public String hookName() { + return getClass().getSimpleName(); + } + + @Override + public void sendMessageBefore(SendMessageContext sendMessageContext) { + Long tenantId = TenantContextHolder.getTenantId(); + if (tenantId == null) { + return; + } + sendMessageContext.getMessage().putUserProperty(HEADER_TENANT_ID, tenantId.toString()); + } + + @Override + public void sendMessageAfter(SendMessageContext sendMessageContext) { + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java new file mode 100644 index 000000000..059d8f97f --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java @@ -0,0 +1,269 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.messaging.handler.invocation; + +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.MethodParameter; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.ResolvableType; +import org.springframework.lang.Nullable; +import org.springframework.messaging.Message; +import org.springframework.messaging.handler.HandlerMethod; +import org.springframework.util.ObjectUtils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Arrays; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; + +/** + * Extension of {@link HandlerMethod} that invokes the underlying method with + * argument values resolved from the current HTTP request through a list of + * {@link HandlerMethodArgumentResolver}. + * + * 针对 rabbitmq-spring 和 kafka-spring,不存在合适的拓展点,可以实现 Consumer 消费前,读取 Header 中的 tenant-id 设置到 {@link TenantContextHolder} 中 + * TODO 芋艿:持续跟进,看看有没新的拓展点 + * + * @author Rossen Stoyanchev + * @author Juergen Hoeller + * @since 4.0 + */ +public class InvocableHandlerMethod extends HandlerMethod { + + private static final Object[] EMPTY_ARGS = new Object[0]; + + private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); + + private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); + + /** + * Create an instance from a {@code HandlerMethod}. + */ + public InvocableHandlerMethod(HandlerMethod handlerMethod) { + super(handlerMethod); + } + + /** + * Create an instance from a bean instance and a method. + */ + public InvocableHandlerMethod(Object bean, Method method) { + super(bean, method); + } + + /** + * Construct a new handler method with the given bean instance, method name and parameters. + * @param bean the object bean + * @param methodName the method name + * @param parameterTypes the method parameter types + * @throws NoSuchMethodException when the method cannot be found + */ + public InvocableHandlerMethod(Object bean, String methodName, Class... parameterTypes) + throws NoSuchMethodException { + + super(bean, methodName, parameterTypes); + } + + /** + * Set {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} to use for resolving method argument values. + */ + public void setMessageMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) { + this.resolvers = argumentResolvers; + } + + /** + * Set the ParameterNameDiscoverer for resolving parameter names when needed + * (e.g. default request attribute name). + *

Default is a {@link DefaultParameterNameDiscoverer}. + */ + public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { + this.parameterNameDiscoverer = parameterNameDiscoverer; + } + + /** + * Invoke the method after resolving its argument values in the context of the given message. + *

Argument values are commonly resolved through + * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. + * The {@code providedArgs} parameter however may supply argument values to be used directly, + * i.e. without argument resolution. + *

Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the + * resolved arguments. + * @param message the current message being processed + * @param providedArgs "given" arguments matched by type, not resolved + * @return the raw value returned by the invoked method + * @throws Exception raised if no suitable argument resolver can be found, + * or if the method raised an exception + * @see #getMethodArgumentValues + * @see #doInvoke + */ + @Nullable + public Object invoke(Message message, Object... providedArgs) throws Exception { + Object[] args = getMethodArgumentValues(message, providedArgs); + if (logger.isTraceEnabled()) { + logger.trace("Arguments: " + Arrays.toString(args)); + } + // 注意:如下是本类的改动点!!! + // 情况一:无租户编号的情况 + Long tenantId= parseTenantId(message); + if (tenantId == null) { + return doInvoke(args); + } + // 情况二:有租户的情况下 + return TenantUtils.execute(tenantId, () -> doInvoke(args)); + } + + private Long parseTenantId(Message message) { + Object tenantId = message.getHeaders().get(HEADER_TENANT_ID); + if (tenantId == null) { + return null; + } + if (tenantId instanceof Long) { + return (Long) tenantId; + } + if (tenantId instanceof Number) { + return ((Number) tenantId).longValue(); + } + if (tenantId instanceof String) { + return Long.parseLong((String) tenantId); + } + if (tenantId instanceof byte[]) { + return Long.parseLong(new String((byte[]) tenantId)); + } + throw new IllegalArgumentException("未知的数据类型:" + tenantId); + } + + /** + * Get the method argument values for the current message, checking the provided + * argument values and falling back to the configured argument resolvers. + *

The resulting array will be passed into {@link #doInvoke}. + * @since 5.1.2 + */ + protected Object[] getMethodArgumentValues(Message message, Object... providedArgs) throws Exception { + MethodParameter[] parameters = getMethodParameters(); + if (ObjectUtils.isEmpty(parameters)) { + return EMPTY_ARGS; + } + + Object[] args = new Object[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + MethodParameter parameter = parameters[i]; + parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); + args[i] = findProvidedArgument(parameter, providedArgs); + if (args[i] != null) { + continue; + } + if (!this.resolvers.supportsParameter(parameter)) { + throw new MethodArgumentResolutionException( + message, parameter, formatArgumentError(parameter, "No suitable resolver")); + } + try { + args[i] = this.resolvers.resolveArgument(parameter, message); + } + catch (Exception ex) { + // Leave stack trace for later, exception may actually be resolved and handled... + if (logger.isDebugEnabled()) { + String exMsg = ex.getMessage(); + if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { + logger.debug(formatArgumentError(parameter, exMsg)); + } + } + throw ex; + } + } + return args; + } + + /** + * Invoke the handler method with the given argument values. + */ + @Nullable + protected Object doInvoke(Object... args) throws Exception { + try { + return getBridgedMethod().invoke(getBean(), args); + } + catch (IllegalArgumentException ex) { + assertTargetBean(getBridgedMethod(), getBean(), args); + String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); + throw new IllegalStateException(formatInvokeError(text, args), ex); + } + catch (InvocationTargetException ex) { + // Unwrap for HandlerExceptionResolvers ... + Throwable targetException = ex.getTargetException(); + if (targetException instanceof RuntimeException) { + throw (RuntimeException) targetException; + } + else if (targetException instanceof Error) { + throw (Error) targetException; + } + else if (targetException instanceof Exception) { + throw (Exception) targetException; + } + else { + throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException); + } + } + } + + MethodParameter getAsyncReturnValueType(@Nullable Object returnValue) { + return new AsyncResultMethodParameter(returnValue); + } + + private class AsyncResultMethodParameter extends HandlerMethodParameter { + + @Nullable + private final Object returnValue; + + private final ResolvableType returnType; + + public AsyncResultMethodParameter(@Nullable Object returnValue) { + super(-1); + this.returnValue = returnValue; + this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(); + } + + protected AsyncResultMethodParameter(AsyncResultMethodParameter original) { + super(original); + this.returnValue = original.returnValue; + this.returnType = original.returnType; + } + + @Override + public Class getParameterType() { + if (this.returnValue != null) { + return this.returnValue.getClass(); + } + if (!ResolvableType.NONE.equals(this.returnType)) { + return this.returnType.toClass(); + } + return super.getParameterType(); + } + + @Override + public Type getGenericParameterType() { + return this.returnType.getType(); + } + + @Override + public AsyncResultMethodParameter clone() { + return new AsyncResultMethodParameter(this); + } + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..a495842a0 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.env.EnvironmentPostProcessor=\ + cn.iocoder.yudao.framework.tenant.core.mq.kafka.TenantKafkaEnvironmentPostProcessor diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/job/TestJob.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/job/TestJob.java deleted file mode 100644 index 2a6d200c4..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/job/TestJob.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.framework.tenant.core.job; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -@Component -public class TestJob implements JobHandler { - - private final List tenantIds = new CopyOnWriteArrayList<>(); - - @Override - @TenantJob // 标记多租户 - public String execute(String param) throws Exception { - tenantIds.add(TenantContextHolder.getTenantId()); - return "success"; - } - - public List getTenantIds() { - CollUtil.sort(tenantIds, Long::compareTo); - return tenantIds; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-mq/pom.xml b/yudao-framework/yudao-spring-boot-starter-mq/pom.xml index 75303d4e3..c8972f16b 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mq/pom.xml @@ -12,7 +12,7 @@ jar ${project.artifactId} - 消息队列,基于 Redis Pub/Sub 实现广播消费,基于 Stream 实现集群消费 + 消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种 https://github.com/YunaiV/ruoyi-vue-pro @@ -21,6 +21,23 @@ cn.iocoder.boot yudao-spring-boot-starter-redis + + + + org.springframework.kafka + spring-kafka + true + + + org.springframework.amqp + spring-rabbit + true + + + org.apache.rocketmq + rocketmq-spring-boot-starter + true + - + \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java deleted file mode 100644 index fbc2a2826..000000000 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.framework.mq.core.pubsub; - -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Redis Channel Message 抽象类 - * - * @author 芋道源码 - */ -public abstract class AbstractChannelMessage extends AbstractRedisMessage { - - /** - * 获得 Redis Channel - * - * @return Channel - */ - @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。 - public abstract String getChannel(); - -} diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessage.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessage.java deleted file mode 100644 index 29ea833f3..000000000 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.framework.mq.core.stream; - -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Redis Stream Message 抽象类 - * - * @author 芋道源码 - */ -public abstract class AbstractStreamMessage extends AbstractRedisMessage { - - /** - * 获得 Redis Stream Key - * - * @return Channel - */ - @JsonIgnore // 避免序列化 - public abstract String getStreamKey(); - -} diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/package-info.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/package-info.java index 48eaf2386..3b716cb77 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/package-info.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/package-info.java @@ -1,6 +1,4 @@ /** - * 消息队列,基于 Redis 提供: - * 1. 基于 Pub/Sub 实现广播消费 - * 2. 基于 Stream 实现集群消费 + * 消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种 */ package cn.iocoder.yudao.framework.mq; diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java new file mode 100644 index 000000000..770c50ff7 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.framework.mq.rabbitmq.config; + +import cn.hutool.core.util.ReflectUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.utils.SerializationUtils; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; + +import java.lang.reflect.Field; + +/** + * RabbitMQ 消息队列配置类 + * + * @author 芋道源码 + */ +@AutoConfiguration +@Slf4j +@ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") +public class YudaoRabbitMQAutoConfiguration { + + static { + // 强制设置 SerializationUtils 的 TRUST_ALL 为 true,避免 RabbitMQ Consumer 反序列化消息报错 + // 为什么不通过设置 spring.amqp.deserialization.trust.all 呢?因为可能在 SerializationUtils static 初始化后 + Field trustAllField = ReflectUtil.getField(SerializationUtils.class, "TRUST_ALL"); + ReflectUtil.removeFinalModify(trustAllField); + ReflectUtil.setFieldValue(SerializationUtils.class, trustAllField, true); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/core/package-info.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/core/package-info.java new file mode 100644 index 000000000..2773b5828 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位符,无特殊逻辑 + */ +package cn.iocoder.yudao.framework.mq.rabbitmq.core; \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/package-info.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/package-info.java new file mode 100644 index 000000000..9f6032c92 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/package-info.java @@ -0,0 +1,4 @@ +/** + * 消息队列,基于 RabbitMQ 提供 + */ +package cn.iocoder.yudao.framework.mq.rabbitmq; diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQAutoConfiguration.java similarity index 77% rename from yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java rename to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQAutoConfiguration.java index e300b1ad5..bbc63b719 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQAutoConfiguration.java @@ -1,21 +1,20 @@ -package cn.iocoder.yudao.framework.mq.config; +package cn.iocoder.yudao.framework.mq.redis.config; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.system.SystemUtil; import cn.iocoder.yudao.framework.common.enums.DocumentEnum; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; -import cn.iocoder.yudao.framework.mq.job.RedisPendingMessageResendJob; +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; +import cn.iocoder.yudao.framework.mq.redis.core.job.RedisPendingMessageResendJob; +import cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener; +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisServerCommands; import org.springframework.data.redis.connection.stream.Consumer; @@ -27,7 +26,6 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.data.redis.stream.DefaultStreamMessageListenerContainerX; import org.springframework.data.redis.stream.StreamMessageListenerContainer; import org.springframework.scheduling.annotation.EnableScheduling; @@ -42,7 +40,7 @@ import java.util.Properties; @Slf4j @EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息 @AutoConfiguration(after = YudaoRedisAutoConfiguration.class) -public class YudaoMQAutoConfiguration { +public class YudaoRedisMQAutoConfiguration { @Bean public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, @@ -59,10 +57,9 @@ public class YudaoMQAutoConfiguration { * 创建 Redis Pub/Sub 广播消费的容器 */ @Bean(initMethod = "start", destroyMethod = "stop") - @ConditionalOnBean(AbstractChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候,才需要注册 Redis pubsub 监听 - @ConditionalOnProperty(prefix = "yudao.mq.redis.pubsub", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.pubsub.enable=false 禁用多租户 + @ConditionalOnBean(AbstractRedisChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候,才需要注册 Redis pubsub 监听 public RedisMessageListenerContainer redisMessageListenerContainer( - RedisMQTemplate redisMQTemplate, List> listeners) { + RedisMQTemplate redisMQTemplate, List> listeners) { // 创建 RedisMessageListenerContainer 对象 RedisMessageListenerContainer container = new RedisMessageListenerContainer(); // 设置 RedisConnection 工厂。 @@ -81,9 +78,8 @@ public class YudaoMQAutoConfiguration { * 创建 Redis Stream 重新消费的任务 */ @Bean - @ConditionalOnBean(AbstractStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 - @ConditionalOnProperty(prefix = "yudao.mq.redis.stream", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.stream.enable=false 禁用多租户 - public RedisPendingMessageResendJob redisPendingMessageResendJob(List> listeners, + @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 + public RedisPendingMessageResendJob redisPendingMessageResendJob(List> listeners, RedisMQTemplate redisTemplate, @Value("${spring.application.name}") String groupName, RedissonClient redissonClient) { @@ -92,14 +88,13 @@ public class YudaoMQAutoConfiguration { /** * 创建 Redis Stream 集群消费的容器 - *

- * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html + * + * 基础知识:Redis Stream 的 xreadgroup 命令 */ @Bean(initMethod = "start", destroyMethod = "stop") - @ConditionalOnBean(AbstractStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 - @ConditionalOnProperty(prefix = "yudao.mq.redis.stream", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.stream.enable=false 禁用多租户 + @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 public StreamMessageListenerContainer> redisStreamMessageListenerContainer( - RedisMQTemplate redisMQTemplate, List> listeners) { + RedisMQTemplate redisMQTemplate, List> listeners) { RedisTemplate redisTemplate = redisMQTemplate.getRedisTemplate(); checkRedisVersion(redisTemplate); // 第一步,创建 StreamMessageListenerContainer 容器 @@ -111,8 +106,7 @@ public class YudaoMQAutoConfiguration { .build(); // 创建 container 对象 StreamMessageListenerContainer> container = -// StreamMessageListenerContainer.create(redisTemplate.getRequiredConnectionFactory(), containerOptions); - DefaultStreamMessageListenerContainerX.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); + StreamMessageListenerContainer.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); // 第二步,注册监听器,消费对应的 Stream 主题 String consumerName = buildConsumerName(); diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/RedisMQTemplate.java similarity index 80% rename from yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java rename to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/RedisMQTemplate.java index 8a31feda7..5755ffa51 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/RedisMQTemplate.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.framework.mq.core; +package cn.iocoder.yudao.framework.mq.redis.core; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; +import cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessage; +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessage; import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.data.redis.connection.stream.RecordId; @@ -35,7 +35,7 @@ public class RedisMQTemplate { * * @param message 消息 */ - public void send(T message) { + public void send(T message) { try { sendMessageBefore(message); // 发送消息 @@ -51,7 +51,7 @@ public class RedisMQTemplate { * @param message 消息 * @return 消息记录的编号对象 */ - public RecordId send(T message) { + public RecordId send(T message) { try { sendMessageBefore(message); // 发送消息 diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/interceptor/RedisMessageInterceptor.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java similarity index 79% rename from yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/interceptor/RedisMessageInterceptor.java rename to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java index 11d8e1337..dbcee7fe2 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/interceptor/RedisMessageInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.framework.mq.core.interceptor; +package cn.iocoder.yudao.framework.mq.redis.core.interceptor; -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; /** * {@link AbstractRedisMessage} 消息拦截器 diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java similarity index 93% rename from yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java rename to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java index ea0f53d19..b84f17c15 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.framework.mq.job; +package cn.iocoder.yudao.framework.mq.redis.core.job; import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; @@ -33,7 +33,7 @@ public class RedisPendingMessageResendJob { */ private static final int EXPIRE_TIME = 5 * 60; - private final List> listeners; + private final List> listeners; private final RedisMQTemplate redisTemplate; private final String groupName; private final RedissonClient redissonClient; diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/message/AbstractRedisMessage.java similarity index 88% rename from yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java rename to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/message/AbstractRedisMessage.java index f02e89d6f..ee40814dd 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/message/AbstractRedisMessage.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.framework.mq.core.message; +package cn.iocoder.yudao.framework.mq.redis.core.message; import lombok.Data; diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java new file mode 100644 index 000000000..d5ea5b9d5 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.framework.mq.redis.core.pubsub; + +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Redis Channel Message 抽象类 + * + * @author 芋道源码 + */ +public abstract class AbstractRedisChannelMessage extends AbstractRedisMessage { + + /** + * 获得 Redis Channel,默认使用类名 + * + * @return Channel + */ + @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。 + public String getChannel() { + return getClass().getSimpleName(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessageListener.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java similarity index 85% rename from yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessageListener.java rename to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java index e7d737d1b..fd7c910c9 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessageListener.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.framework.mq.core.pubsub; +package cn.iocoder.yudao.framework.mq.redis.core.pubsub; import cn.hutool.core.util.TypeUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; import lombok.Setter; import lombok.SneakyThrows; import org.springframework.data.redis.connection.Message; @@ -20,7 +20,7 @@ import java.util.List; * * @author 芋道源码 */ -public abstract class AbstractChannelMessageListener implements MessageListener { +public abstract class AbstractRedisChannelMessageListener implements MessageListener { /** * 消息类型 @@ -37,7 +37,7 @@ public abstract class AbstractChannelMessageListener +public abstract class AbstractRedisStreamMessageListener implements StreamListener> { /** @@ -48,7 +48,7 @@ public abstract class AbstractStreamMessageListener> extends DefaultStreamMessageListenerContainer { - - /** - * 参考 {@link StreamMessageListenerContainer#create(RedisConnectionFactory, StreamMessageListenerContainerOptions)} 的实现 - */ - public static > StreamMessageListenerContainer create(RedisConnectionFactory connectionFactory, StreamMessageListenerContainer.StreamMessageListenerContainerOptions options) { - Assert.notNull(connectionFactory, "RedisConnectionFactory must not be null!"); - Assert.notNull(options, "StreamMessageListenerContainerOptions must not be null!"); - return new DefaultStreamMessageListenerContainerX<>(connectionFactory, options); - } - - public DefaultStreamMessageListenerContainerX(RedisConnectionFactory connectionFactory, StreamMessageListenerContainerOptions containerOptions) { - super(connectionFactory, containerOptions); - } - - /** - * 参考 {@link DefaultStreamMessageListenerContainer#register(StreamReadRequest, StreamListener)} 的实现 - */ - @Override - public Subscription register(StreamReadRequest streamRequest, StreamListener listener) { - return this.doRegisterX(getReadTaskX(streamRequest, listener)); - } - - @SuppressWarnings("unchecked") - private StreamPollTask getReadTaskX(StreamReadRequest streamRequest, StreamListener listener) { - StreamPollTask task = ReflectUtil.invoke(this, "getReadTask", streamRequest, listener); - // 修改 readFunction 方法 - Function> readFunction = (Function>) ReflectUtil.getFieldValue(task, "readFunction"); - ReflectUtil.setFieldValue(task, "readFunction", (Function>) readOffset -> { - List records = readFunction.apply(readOffset); - //【重点】保证 records 不是空,避免 NPE 的问题!!! - return records != null ? records : Collections.emptyList(); - }); - return task; - } - - private Subscription doRegisterX(Task task) { - return ReflectUtil.invoke(this, "doRegister", task); - } - -} - diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yudao-framework/yudao-spring-boot-starter-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index c47aa4d7b..660865453 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1,2 @@ -cn.iocoder.yudao.framework.mq.config.YudaoMQAutoConfiguration \ No newline at end of file +cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQAutoConfiguration +cn.iocoder.yudao.framework.mq.rabbitmq.config.YudaoRabbitMQAutoConfiguration \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 事件机制 Event 入门》.md b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 事件机制 Event 入门》.md new file mode 100644 index 000000000..08586b379 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 事件机制 Event 入门》.md @@ -0,0 +1 @@ + diff --git a/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 Kafka 入门》.md b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 Kafka 入门》.md new file mode 100644 index 000000000..b66d6334c --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 Kafka 入门》.md @@ -0,0 +1 @@ + diff --git a/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md new file mode 100644 index 000000000..eff46e2f7 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md @@ -0,0 +1 @@ + diff --git a/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md new file mode 100644 index 000000000..08586b379 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md @@ -0,0 +1 @@ + diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java index ffbeb39e1..7799c42f6 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java +++ b/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; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; -// TODO @小吉祥:搞个 job,清理 14 天外的访问日志; /** * API 访问日志 * diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java index cae595a7c..087dd5d08 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java +++ b/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; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; -// TODO @小吉祥:搞个 job,清理 14 天外的异常日志; /** * API 错误日志 * diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java index 67a4a222e..89d56b3c0 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java +++ b/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 { ; /** - * 结果 + * 操作类型 */ private final Integer type; /** - * 描述 + * 操作名字 */ - private final String desc; + private final String name; /** - * 模板信息 + * 操作描述 */ - private final String templateComment; + private final String comment; } diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java index 519c7c68c..615416c73 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java +++ b/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 { * 相当于是 通过 APPROVE 的特殊状态 * 例如:A审批, A 后加签了 B,并且审批通过了任务,但是 B 还未审批,则当前任务状态为“待后加签任务完成” */ - ADD_SIGN_AFTER(7, "待后加签任务完成"), + SIGN_AFTER(7, "待后加签任务完成"), /** * 【加签】源任务未审批,但是向前加签了,所以源任务状态变为“待前加签任务完成” * 相当于是 处理中 PROCESS 的特殊状态 * 例如:A 审批, A 前加签了 B,B 还未审核 */ - ADD_SIGN_BEFORE(8, "待前加签任务完成"), + SIGN_BEFORE(8, "待前加签任务完成"), /** * 【加签】后加签任务被创建时的初始状态 * 相当于是 处理中 PROCESS 的特殊状态 @@ -71,7 +71,7 @@ public enum BpmProcessInstanceResultEnum { public static boolean isEndResult(Integer result) { return ObjectUtils.equalsAny(result, APPROVE.getResult(), REJECT.getResult(), CANCEL.getResult(), BACK.getResult(), - ADD_SIGN_AFTER.getResult()); + SIGN_AFTER.getResult()); } } diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java index 3bf41a153..42c212e28 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java +++ b/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; import lombok.AllArgsConstructor; import lombok.Getter; - /** * 流程任务 -- 加签类型枚举类型 */ diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java index a67af2eeb..dcfab78ef 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java +++ b/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 { return success(true); } - @GetMapping("/get-return-list") + @GetMapping("/return-list") @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") @Parameter(name = "taskId", description = "当前任务ID", required = true) @PreAuthorize("@ss.hasPermission('bpm:task:update')") @@ -98,27 +98,28 @@ public class BpmTaskController { return success(true); } - @PutMapping("/add-sign") + @PutMapping("/create-sign") @Operation(summary = "加签", description = "before 前加签,after 后加签") @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult addSignTask(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) { - taskService.addSignTask(getLoginUserId(), reqVO); + public CommonResult createSignTask(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) { + taskService.createSignTask(getLoginUserId(), reqVO); return success(true); } - @PutMapping("/sub-sign") + @DeleteMapping("/delete-sign") @Operation(summary = "减签") @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult subSignTask(@Valid @RequestBody BpmTaskSubSignReqVO reqVO) { - taskService.subSignTask(getLoginUserId(), reqVO); + public CommonResult deleteSignTask(@Valid @RequestBody BpmTaskSubSignReqVO reqVO) { + taskService.deleteSignTask(getLoginUserId(), reqVO); return success(true); } - @GetMapping("/get-children-task-list") + @GetMapping("children-list") @Operation(summary = "获取能被减签的任务") + @Parameter(name = "parentId", description = "父级任务 ID", required = true) @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult> getChildrenTaskList(@RequestParam("taskId") String taskId) { - return success(taskService.getChildrenTaskList(taskId)); + public CommonResult> getChildrenTaskList(@RequestParam("parentId") String parentId) { + return success(taskService.getChildrenTaskList(parentId)); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java index 93b95d3fd..cabb91be1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java +++ b/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; import javax.validation.constraints.NotEmpty; import java.util.Set; +// TODO @海洋:类名,应该是 create 哈 @Schema(description = "管理后台 - 加签流程任务的 Request VO") @Data public class BpmTaskAddSignReqVO { @@ -26,4 +27,4 @@ public class BpmTaskAddSignReqVO { @NotEmpty(message = "加签原因不能为空") private String reason; -} \ No newline at end of file +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java index 665a9aec8..731e4804a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java +++ b/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; import javax.validation.constraints.NotEmpty; +// TODO @海洋:类名,应该是 delete 哈 @Schema(description = "管理后台 - 减签流程任务的 Request VO") @Data public class BpmTaskSubSignReqVO { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java index 0d55a82eb..60ce84021 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java @@ -1,9 +1,10 @@ package cn.iocoder.yudao.module.bpm.convert.task; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; @@ -25,6 +26,9 @@ import java.util.Date; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; + /** * Bpm 任务 Convert * @@ -167,32 +171,31 @@ public interface BpmTaskConvert { default List convertList(List bpmTaskExtDOList, Map userMap, Map idTaskMap){ - return CollectionUtils.convertList(bpmTaskExtDOList, task->{ - BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO(); - bpmTaskSubSignRespVO.setName(task.getName()); - bpmTaskSubSignRespVO.setId(task.getTaskId()); - Task sourceTask = idTaskMap.get(task.getTaskId()); + return CollectionUtils.convertList(bpmTaskExtDOList, task -> { + BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO() + .setId(task.getTaskId()).setName(task.getName()); // 后加签任务不会直接设置 assignee ,所以不存在 assignee 的情况,则去取 owner - String assignee = StrUtil.isNotEmpty(sourceTask.getAssignee()) ? sourceTask.getAssignee() : sourceTask.getOwner(); - AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(assignee)); - if (assignUser != null) { - bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser)); - } + Task sourceTask = idTaskMap.get(task.getTaskId()); + String assignee = ObjectUtil.defaultIfBlank(sourceTask.getOwner(),sourceTask.getAssignee()); + MapUtils.findAndThen(userMap,NumberUtils.parseLong(assignee), + assignUser-> bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser))); return bpmTaskSubSignRespVO; }); } /** * 转换任务为父子级 + * * @param sourceList 原始数据 * @return 转换后的父子级数组 */ - default List convertChildrenList(List sourceList){ - List childrenTaskList = CollectionUtils.filterList(sourceList, r -> StrUtil.isNotEmpty(r.getParentTaskId())); - Map> parentChildrenTaskListMap = CollectionUtils.convertMultiMap(childrenTaskList, BpmTaskRespVO::getParentTaskId); + default List convertChildrenList(List sourceList) { + List childrenTaskList = filterList(sourceList, r -> StrUtil.isNotEmpty(r.getParentTaskId())); + Map> parentChildrenTaskListMap = convertMultiMap(childrenTaskList, BpmTaskRespVO::getParentTaskId); for (BpmTaskRespVO bpmTaskRespVO : sourceList) { bpmTaskRespVO.setChildren(parentChildrenTaskListMap.get(bpmTaskRespVO.getId())); } - return CollectionUtils.filterList(sourceList, r -> StrUtil.isEmpty(r.getParentTaskId())); + return filterList(sourceList, r -> StrUtil.isEmpty(r.getParentTaskId())); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java index 852ccb2e0..8108e613d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java +++ b/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 { return selectList(BpmTaskExtDO::getTaskId, taskIds); } + // TODO @海:BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST) 应该作为条件,mapper 不要有业务 default List selectProcessListByTaskIds(Collection taskIds) { return selectList(new LambdaQueryWrapperX() .in(BpmTaskExtDO::getTaskId, taskIds) .in(BpmTaskExtDO::getResult, BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST)); } - default BpmTaskExtDO selectByTaskId(String taskId) { return selectOne(BpmTaskExtDO::getTaskId, taskId); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmSecurityConfiguration.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmSecurityConfiguration.java deleted file mode 100644 index 2069f7d11..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmSecurityConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.bpm.config; - -import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; - -/** - * @author kemengkai - * @create 2022-05-07 08:15 - */ -@Configuration("bpmSecurityConfiguration") -public class BpmSecurityConfiguration { - - @Bean("bpmAuthorizeRequestsCustomizer") - public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { - return new AuthorizeRequestsCustomizer() { - - @Override - public void customize(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry) { - // 任务回退接口 - registry.antMatchers(buildAdminApi("/bpm/task/back")).permitAll(); - } - - }; - } -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java index 0addde074..03fc99e03 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java @@ -12,6 +12,7 @@ import javax.validation.Valid; * @author yunlongn */ public interface BpmModelService { + /** * 获得流程模型分页 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 81c437710..8f7e3996b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/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 { processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode()); } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index 772425b66..42be9260a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/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 { * @param userId 被加签的用户和任务 ID,加签类型 * @param reqVO 当前用户 ID */ - void addSignTask(Long userId, BpmTaskAddSignReqVO reqVO); + void createSignTask(Long userId, BpmTaskAddSignReqVO reqVO); /** * 任务减签名 @@ -171,14 +171,14 @@ public interface BpmTaskService { * @param userId 当前用户ID * @param reqVO 被减签的任务 ID,理由 */ - void subSignTask(Long userId, BpmTaskSubSignReqVO reqVO); + void deleteSignTask(Long userId, BpmTaskSubSignReqVO reqVO); /** * 获取指定任务的子任务和审批人信息 * - * @param taskId 指定任务ID + * @param parentId 指定任务ID * @return 子任务列表 */ - List getChildrenTaskList(String taskId); + List getChildrenTaskList(String parentId); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 6bf2fb5b8..2b0e18e70 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/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; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -240,10 +239,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { handleParentTask(task); } + /** * 审批通过存在“后加签”的任务。 *

- * 注意:该任务不能马上完成,需要一个中间状态(ADD_SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理 + * 注意:该任务不能马上完成,需要一个中间状态(SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理 * * @param task 当前任务 * @param reqVO 前端请求参数 @@ -251,7 +251,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { // 1. 有向后加签,则该任务状态临时设置为 ADD_SIGN_AFTER 状态 taskExtMapper.updateByTaskId( - new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.ADD_SIGN_AFTER.getResult()) + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.SIGN_AFTER.getResult()) .setReason(reqVO.getReason()).setEndTime(LocalDateTime.now())); // 2. 激活子任务 @@ -265,7 +265,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } /** - * 处理当前任务的父任务 + * 处理当前任务的父任务,主要处理“加签”的情况 * * @param task 当前任务 */ @@ -274,76 +274,79 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (StrUtil.isBlank(parentTaskId)) { return; } - if (StrUtil.isNotBlank(parentTaskId)) { - // 1. 判断当前任务的父任务是否还有子任务 - Long childrenTaskCount = getChildrenTaskCount(parentTaskId); - if (childrenTaskCount > 0) { - return; - } - // 2. 获取父任务 - Task parentTask = validateTaskExist(parentTaskId); - - // 3. 情况一:处理向前加签 - String scopeType = parentTask.getScopeType(); - if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) { - // 3.1 如果是向前加签的任务,则调用 resolveTask 指派父任务,将 owner 重新赋值给父任务的 assignee - taskService.resolveTask(parentTaskId); - // 3.2 更新任务拓展表为处理中 - taskExtMapper.updateByTaskId( - new BpmTaskExtDO().setTaskId(parentTask.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult())); - } else if (BpmTaskAddSignTypeEnum.AFTER.getType().equals(scopeType)) { - // 3. 情况二:处理向后加签 - handleAfterSign(parentTask); - } - - // 4. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 - // 再查询一次的原因是避免报错:Task was updated by another transaction concurrently - // 因为前面处理后可能会导致 parentTask rev 字段被修改,需要重新获取最新的 - parentTask = getTask(parentTaskId); - if (parentTask == null) { - // 为空的情况是:已经通过 handleAfterSign 方法将任务完成了,所以 ru_task 表会查不到数据 - return; - } - clearTaskScopeTypeAndSave(parentTask); + // 1. 判断当前任务的父任务是否还有子任务 + Long childrenTaskCount = getChildrenTaskCount(parentTaskId); + if (childrenTaskCount > 0) { + return; } + // 2. 获取父任务 + Task parentTask = validateTaskExist(parentTaskId); + + // 3. 处理加签情况 + String scopeType = parentTask.getScopeType(); + if(!validateSignType(scopeType)){ + return; + } + // 3.1 情况一:处理向前加签 + if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) { + // 3.1.1 如果是向前加签的任务,则调用 resolveTask 指派父任务,将 owner 重新赋值给父任务的 assignee,这样它就可以被审批 + taskService.resolveTask(parentTaskId); + // 3.1.2 更新任务拓展表为处理中 + taskExtMapper.updateByTaskId( + new BpmTaskExtDO().setTaskId(parentTask.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult())); + } else if (BpmTaskAddSignTypeEnum.AFTER.getType().equals(scopeType)) { + // 3.2 情况二:处理向后加签 + handleParentTaskForAfterSign(parentTask); + } + + // 4. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 + // 再查询一次的原因是避免报错:Task was updated by another transaction concurrently + // 因为前面处理后可能会导致 parentTask rev 字段被修改,需要重新获取最新的 + parentTask = getTask(parentTaskId); + if (parentTask == null) { + // 为空的情况是:已经通过 handleAfterSign 方法将任务完成了,所以 ru_task 表会查不到数据 + return; + } + clearTaskScopeTypeAndSave(parentTask); } + /** * 处理后加签任务 * * @param parentTask 当前审批任务的父任务 */ - private void handleAfterSign(Task parentTask) { + // TODO @海:这个逻辑,怎么感觉可以是 parentTask 的 parent,再去调用 handleParentTask 方法;可以微信聊下; + private void handleParentTaskForAfterSign(Task parentTask) { String parentTaskId = parentTask.getId(); - //1. 更新 parentTask 的任务拓展表为通过,并调用 complete 完成自己 + // 1. 更新 parentTask 的任务拓展表为通过,并调用 complete 完成自己 BpmTaskExtDO currentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId()); - BpmTaskExtDO currentTaskUpdateEntity = new BpmTaskExtDO().setTaskId(parentTask.getId()) + BpmTaskExtDO currentTaskExtUpdateObj = new BpmTaskExtDO().setTaskId(parentTask.getId()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); if (currentTaskExt.getEndTime() == null) { // 1.1 有这个判断是因为,以前没设置过结束时间,才去设置 - currentTaskUpdateEntity.setEndTime(LocalDateTime.now()); + currentTaskExtUpdateObj.setEndTime(LocalDateTime.now()); } - // 1.2 完成自己 - taskExtMapper.updateByTaskId(currentTaskUpdateEntity); + taskExtMapper.updateByTaskId(currentTaskExtUpdateObj); + // 1.2 完成自己(因为它已经没有子任务,所以也可以完成) taskService.complete(parentTaskId); // 2. 如果有父级,递归查询上级任务是否都已经完成 if (StrUtil.isEmpty(parentTask.getParentTaskId())) { return; } - // TODO @海:这块待讨论,脑子略乱;感觉 handleAfterSign 的后半段,和 handleParentTask 有点重叠??? // 2.1 判断整条链路的任务是否完成 // 例如从 A 任务加签了一个 B 任务,B 任务又加签了一个 C 任务,C 任务加签了 D 任务 // 此时,D 任务完成,要一直往上找到祖先任务 A调用 complete 方法完成 A 任务 boolean allChildrenTaskFinish = true; while (StrUtil.isNotBlank(parentTask.getParentTaskId())) { parentTask = validateTaskExist(parentTask.getParentTaskId()); - BpmTaskExtDO bpmTaskExtDO = taskExtMapper.selectByTaskId(parentTask.getId()); - if (bpmTaskExtDO == null) { + BpmTaskExtDO parentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId()); + if (parentTaskExt == null) { break; } - boolean currentTaskFinish = BpmProcessInstanceResultEnum.isEndResult(bpmTaskExtDO.getResult()); - // 2.2 如果 allChildrenTaskFinish 已经被赋值为 false ,则不会再赋值为 true,因为整个链路没有完成 + boolean currentTaskFinish = BpmProcessInstanceResultEnum.isEndResult(parentTaskExt.getResult()); + // 2.2 如果 allChildrenTaskFinish 已经被赋值为 false,则不会再赋值为 true,因为整个链路没有完成 if (allChildrenTaskFinish) { allChildrenTaskFinish = currentTaskFinish; } @@ -354,19 +357,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 3 处理非完成状态的任务 // 3.1 判断当前任务的父任务是否还有子任务 - Long childrenTaskCount = getChildrenTaskCount(bpmTaskExtDO.getTaskId()); + Long childrenTaskCount = getChildrenTaskCount(parentTaskExt.getTaskId()); if (childrenTaskCount > 0) { continue; } // 3.2 没有子任务,判断当前任务状态是否为 ADD_SIGN_BEFORE 待前加签任务完成 - if (BpmProcessInstanceResultEnum.ADD_SIGN_BEFORE.getResult().equals(bpmTaskExtDO.getResult())) { + if (BpmProcessInstanceResultEnum.SIGN_BEFORE.getResult().equals(parentTaskExt.getResult())) { // 3.3 需要修改该任务状态为处理中 - taskService.resolveTask(bpmTaskExtDO.getTaskId()); - bpmTaskExtDO.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); - taskExtMapper.updateByTaskId(bpmTaskExtDO); + taskService.resolveTask(parentTaskExt.getTaskId()); + parentTaskExt.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); + taskExtMapper.updateByTaskId(parentTaskExt); } // 3.4 清空 scopeType 字段,用于任务没有子任务时使用该方法,方便任务可以再次被不同的方式加签 - parentTask = validateTaskExist(bpmTaskExtDO.getTaskId()); + parentTask = validateTaskExist(parentTaskExt.getTaskId()); clearTaskScopeTypeAndSave(parentTask); } @@ -387,7 +390,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.saveTask(task); } - /** * 获取子任务个数 * @@ -709,7 +711,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) - public void addSignTask(Long userId, BpmTaskAddSignReqVO reqVO) { + public void createSignTask(Long userId, BpmTaskAddSignReqVO reqVO) { // 1. 获取和校验任务 TaskEntityImpl taskEntity = validateAddSign(userId, reqVO); List userList = adminUserApi.getUserList(reqVO.getUserIdList()); @@ -728,7 +730,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.3 更新扩展表状态 taskExtMapper.updateByTaskId( new BpmTaskExtDO().setTaskId(taskEntity.getId()) - .setResult(BpmProcessInstanceResultEnum.ADD_SIGN_BEFORE.getResult()) + .setResult(BpmProcessInstanceResultEnum.SIGN_BEFORE.getResult()) .setReason(reqVO.getReason())); } // 2.4 记录加签方式,完成任务时需要用到判断 @@ -737,12 +739,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.saveTask(taskEntity); // 3. 创建加签任务 - createAddSignChildrenTasks(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity); + createSignTask(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity); // 4. 记录加签 comment,拼接结果为: [当前用户]向前加签/向后加签给了[多个用户],理由为:reason AdminUserRespDTO currentUser = adminUserApi.getUser(userId); - String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getTemplateComment(), currentUser.getNickname(), BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()), - String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); + String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), currentUser.getNickname(), + BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()), String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType().toString(), comment); } @@ -786,7 +788,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param addSingUserIdList 被加签的用户 ID * @param taskEntity 被加签的任务 */ - private void createAddSignChildrenTasks(List addSingUserIdList, TaskEntityImpl taskEntity) { + private void createSignTask(List addSingUserIdList, TaskEntityImpl taskEntity) { if (CollUtil.isEmpty(addSingUserIdList)) { return; } @@ -795,23 +797,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (StrUtil.isBlank(addSignId)) { continue; } - createChildrenTask(taskEntity, addSignId); + createSignTask(taskEntity, addSignId); } } /** - * 创建子任务 + * 创建加签子任务 * * @param parentTask 父任务 * @param assignee 子任务的执行人 - * @return */ - private void createChildrenTask(TaskEntityImpl parentTask, String assignee) { + private void createSignTask(TaskEntityImpl parentTask, String assignee) { // 1. 生成子任务 TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); task = BpmTaskConvert.INSTANCE.convert(task, parentTask); if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { - // 2.1 前加签,才设置审批人,否则设置 owner + // 2.1 前加签,设置审批人 task.setAssignee(assignee); } else { // 2.2.1 设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 @@ -825,9 +826,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) - public void subSignTask(Long userId, BpmTaskSubSignReqVO reqVO) { + public void deleteSignTask(Long userId, BpmTaskSubSignReqVO reqVO) { + // 1.1 校验 task 可以被减签 Task task = validateSubSign(reqVO.getId()); - AdminUserRespDTO user = adminUserApi.getUser(userId); + // 1.2 校验取消人存在 AdminUserRespDTO cancelUser = null; if (StrUtil.isNotBlank(task.getAssignee())) { cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())); @@ -836,20 +838,24 @@ public class BpmTaskServiceImpl implements BpmTaskService { cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); } Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); - //1. 获取所有需要删除的任务 ID ,包含当前任务和所有子任务 + + // 2. 删除任务和对应子任务 + // 2.1 获取所有需要删除的任务 ID ,包含当前任务和所有子任务 List allTaskIdList = getAllChildTaskIds(task.getId()); - //2. 删除任务和所有子任务 + // 2.2 删除任务和所有子任务 taskService.deleteTasks(allTaskIdList); - //3. 修改扩展表状态为取消 + // 2.3 修改扩展表状态为取消 + AdminUserRespDTO user = adminUserApi.getUser(userId); taskExtMapper.updateBatchByTaskIdList(allTaskIdList, new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()) .setReason(StrUtil.format("由于{}操作[减签],任务被取消", user.getNickname()))); - //4.记录日志到父任务中 先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 - String comment = StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getTemplateComment(), user.getNickname(), cancelUser.getNickname()); + + // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 + String comment = StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname()); taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN.getType().toString(), comment); - //5. 处理当前任务的父任务 - this.handleParentTask(task); + // 4. 处理当前任务的父任务 + handleParentTask(task); } /** @@ -860,13 +866,28 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private Task validateSubSign(String id) { Task task = validateTaskExist(id); - //必须有parentId - if (StrUtil.isEmpty(task.getParentTaskId())) { + + // 必须有 scopeType + String scopeType = task.getScopeType(); + if (StrUtil.isEmpty(scopeType)) { + throw exception(TASK_SUB_SIGN_NO_PARENT); + } + // 并且值为 向前和向后加签 + if (!validateSignType(scopeType)) { throw exception(TASK_SUB_SIGN_NO_PARENT); } return task; } + /** + * 判断当前类型是否为加签 + * @param scopeType 任务的 scopeType + * @return 当前 scopeType 为加签则返回 true + */ + private boolean validateSignType(String scopeType){ + return StrUtil.equalsAny(scopeType,BpmTaskAddSignTypeEnum.BEFORE.getType(),scopeType, BpmTaskAddSignTypeEnum.AFTER.getType()); + } + /** * 获取所有要被取消的删除的任务 ID 集合 * @@ -875,25 +896,30 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ public List getAllChildTaskIds(String parentTaskId) { List allChildTaskIds = new ArrayList<>(); - //1. 先将自己放入 - allChildTaskIds.add(parentTaskId); - //2. 递归获取子级 - recursiveGetChildTaskIds(parentTaskId, allChildTaskIds); - return allChildTaskIds; - } - - /** - * 递归处理子级任务 - * - * @param taskId 当前任务ID - * @param taskIds 结果 - */ - private void recursiveGetChildTaskIds(String taskId, List taskIds) { - List childrenTaskIdList = getChildrenTaskIdList(taskId); - for (String childTaskId : childrenTaskIdList) { - taskIds.add(childTaskId); // 将子任务的ID添加到集合中 - recursiveGetChildTaskIds(childTaskId, taskIds); // 递归获取子任务的子任务 + // 1. 递归获取子级 + Stack stack = new Stack<>(); + // 1.1 将根任务ID入栈 + stack.push(parentTaskId); + //控制遍历的次数不超过 Byte.MAX_VALUE,避免脏数据造成死循环 + int count = 0; + // TODO @海:< 的前后空格,要注意哈; + while (!stack.isEmpty() && count childrenTaskIdList = getChildrenTaskIdList(taskId); + if (CollUtil.isNotEmpty(childrenTaskIdList)) { + for (String childTaskId : childrenTaskIdList) { + // 1.5 将子任务ID入栈,以便后续处理 + stack.push(childTaskId); + } + } + count++; } + return allChildTaskIds; } /** @@ -903,32 +929,41 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @return 所有子任务的 ID 集合 */ private List getChildrenTaskIdList(String parentTaskId) { - String tableName = managementService.getTableName(TaskEntity.class); - String sql = "select ID_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; - List childrenTaskList = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); - return convertList(childrenTaskList, Task::getId); + return convertList(getChildrenTaskList0(parentTaskId), Task::getId); } + /** + * 获取指定父级任务的所有子任务 ID 集合 + * + * @param parentTaskId 父任务 ID + * @return 所有子任务的 ID 集合 + */ + private List getChildrenTaskList0(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 + String sql = "select ID_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); + } + + @Override - public List getChildrenTaskList(String taskId) { - List childrenTaskIdList = getChildrenTaskIdList(taskId); - if (CollUtil.isEmpty(childrenTaskIdList)) { + public List getChildrenTaskList(String parentId) { + // 1. 只查询进行中的任务 后加签的任务,可能不存在 assignee,所以还需要查询 owner + List taskList = getChildrenTaskList0(parentId); + if (CollUtil.isEmpty(taskList)) { return Collections.emptyList(); } - //1. 只查询进行中的任务 - List bpmTaskExtDOList = taskExtMapper.selectProcessListByTaskIds(childrenTaskIdList); - //2. 后加签的任务,可能不存在 assignee,所以还需要查询 owner - List taskList = taskService.createTaskQuery().taskIds(childrenTaskIdList).list(); - Map idTaskMap = convertMap(taskList, TaskInfo::getId); - //3. 将 owner 和 assignee 统一到一个集合中 - List userIds = taskList.stream() - .flatMap(control -> - Stream.of(control.getAssignee(), control.getOwner()) - .filter(Objects::nonNull)) - .distinct() - .map(NumberUtils::parseLong) - .collect(Collectors.toList()); + List childrenTaskIdList = convertList(taskList, Task::getId); + + // 2.1 将 owner 和 assignee 统一到一个集合中 + List userIds = convertListByFlatMap(taskList, control -> + Stream.of(NumberUtils.parseLong(control.getAssignee()), NumberUtils.parseLong(control.getOwner())) + .filter(Objects::nonNull)); + // 2.2 组装数据 Map userMap = adminUserApi.getUserMap(userIds); - return BpmTaskConvert.INSTANCE.convertList(bpmTaskExtDOList, userMap, idTaskMap); + List taskExtList = taskExtMapper.selectProcessListByTaskIds(childrenTaskIdList); + Map idTaskMap = convertMap(taskList, TaskInfo::getId); + return BpmTaskConvert.INSTANCE.convertList(taskExtList, userMap, idTaskMap); } + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java index 6e05844de..beef57f99 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java +++ b/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 { */ private static ProcessConfig buildProcessConfig() { return ProcessConfig.builder() - .ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_")) // 忽略表前缀 + .ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_", "FLW_")) // 忽略表前缀 .build(); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java index 01b5714ba..2f22f4fb9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java +++ b/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 { .antMatchers("/swagger-resources/**").anonymous() .antMatchers("/webjars/**").anonymous() .antMatchers("/*/api-docs").anonymous(); - // 积木报表 - registry.antMatchers("/jmreport/**").permitAll(); // Spring Boot Actuator 的安全配置 registry.antMatchers("/actuator").anonymous() .antMatchers("/actuator/**").anonymous(); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/JobLogCleanJob.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java similarity index 88% rename from yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/JobLogCleanJob.java rename to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java index 4d8c955a1..3f9d29333 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/JobLogCleanJob.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.infra.job.logger; +package cn.iocoder.yudao.module.infra.job.job; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; @@ -33,7 +33,7 @@ public class JobLogCleanJob implements JobHandler { @TenantIgnore public String execute(String param) { Integer count = jobLogService.cleanJobLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); - log.info("[count][定时执行清理定时任务日志数量 ({}) 个]", count); + log.info("[execute][定时执行清理定时任务日志数量 ({}) 个]", count); return String.format("定时执行清理定时任务日志数量 %s 个", count); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/AccessLogCleanJob.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java similarity index 88% rename from yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/AccessLogCleanJob.java rename to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java index 2e7198de0..9ddab4163 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/AccessLogCleanJob.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.infra.job.job; +package cn.iocoder.yudao.module.infra.job.logger; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; @@ -34,7 +34,7 @@ public class AccessLogCleanJob implements JobHandler { @TenantIgnore public String execute(String param) { Integer count = apiAccessLogService.cleanAccessLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT); - log.info("[count][定时执行清理访问日志数量 ({}) 个]", count); + log.info("[execute][定时执行清理访问日志数量 ({}) 个]", count); return String.format("定时执行清理错误日志数量 %s 个", count); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/ErrorLogCleanJob.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java similarity index 88% rename from yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/ErrorLogCleanJob.java rename to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java index 7e69fd272..9b50aaf5c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/ErrorLogCleanJob.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.infra.job.job; +package cn.iocoder.yudao.module.infra.job.logger; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; @@ -34,7 +34,7 @@ public class ErrorLogCleanJob implements JobHandler { @TenantIgnore public String execute(String param) { Integer count = apiErrorLogService.cleanErrorLog(JOB_CLEAN_RETAIN_DAY,DELETE_LIMIT); - log.info("[count][定时执行清理错误日志数量 ({}) 个]", count); + log.info("[execute][定时执行清理错误日志数量 ({}) 个]", count); return String.format("定时执行清理错误日志数量 %s 个", count); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index 5c94c99e1..4b54efc2f 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/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 { FileConfigDO config = Objects.equals(CACHE_MASTER_ID, id) ? fileConfigMapper.selectByMaster() : fileConfigMapper.selectById(id); if (config != null) { - fileClientFactory.createOrUpdateFileClient(id, config.getStorage(), config.getConfig()); + fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig()); } - return fileClientFactory.getFileClient(id); + return fileClientFactory.getFileClient(null == config ? id : config.getId()); } }); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm index c7283a121..b7f26510e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm +++ b/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) { // 导出${table.classComment} Excel export function export${simpleClassName}(params) { - return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') + return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm index 5557b38c9..92d3b2d75 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm @@ -1,6 +1,6 @@ -import type { BasicColumn, FormSchema } from '@/components/Table' -import { useRender } from '@/components/Table' -import { DICT_TYPE, getDictOptions } from '@/utils/dict' +import type {BasicColumn, FormSchema} from '@/components/Table' +import {useRender} from '@/components/Table' +import {DICT_TYPE, getDictOptions} from '@/utils/dict' export const columns: BasicColumn[] = [ #foreach($column in $columns) @@ -50,7 +50,7 @@ export const searchFormSchema: FormSchema[] = [ field: '${javaField}', #if ($column.htmlType == "input") component: 'Input', - #elseif ($column.htmlType == "select" || $column.htmlType == "radio") + #elseif ($column.htmlType == "select") component: 'Select', componentProps: { #if ("" != $dictType)## 设置了 dictType 数据字典的情况 @@ -59,6 +59,15 @@ export const searchFormSchema: FormSchema[] = [ options: [], #end }, + #elseif ($column.htmlType == "radio") + component: 'Radio', + componentProps: { + #if ("" != $dictType)## 设置了 dictType 数据字典的情况 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase()), + #else## 未设置 dictType 数据字典的情况 + options: [], + #end + }, #elseif($column.htmlType == "datetime") component: 'RangePicker', #end @@ -181,7 +190,8 @@ export const updateFormSchema: FormSchema[] = [ fileType: 'file', maxCount: 1, }, - #elseif($column.htmlType == "editor")## 文本编辑器component: 'Editor', + #elseif($column.htmlType == "editor")## 文本编辑器 + component: 'Editor', #elseif($column.htmlType == "select")## 下拉框 component: 'Select', componentProps: { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm index 07f3285c1..7f794585c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm @@ -1,12 +1,11 @@ - - - -

点击如下按钮,发起支付的测试
-
- -
- - - diff --git a/yudao-ui-admin/package.json b/yudao-ui-admin/package.json index 301e894a9..06019b1e2 100644 --- a/yudao-ui-admin/package.json +++ b/yudao-ui-admin/package.json @@ -1,6 +1,6 @@ { "name": "yudao-ui-admin", - "version": "1.8.0-snapshot", + "version": "1.8.3-snapshot", "description": "芋道管理系统", "author": "芋道", "license": "MIT",