├── .gitignore ├── LICENSE ├── README.md ├── checkstyle-v1.xml ├── helio-core ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── core │ │ ├── cache │ │ └── TimedCacheEx.java │ │ ├── config │ │ ├── AdaptHistoryVersionAutoConfiguration.java │ │ ├── HelioPropertiesAutoConfiguration.java │ │ └── StaticMethodAutoConfiguration.java │ │ ├── constant │ │ └── HelioConstant.java │ │ ├── context │ │ ├── TenantContext.java │ │ ├── TenantContextHolder.java │ │ ├── UserContext.java │ │ └── UserContextHolder.java │ │ ├── enums │ │ ├── EnabledStatusEnum.java │ │ ├── GenderEnum.java │ │ ├── HelioBaseEnum.java │ │ ├── IdGeneratorStrategyEnum.java │ │ ├── TenantIsolateLevelEnum.java │ │ └── YesOrNoEnum.java │ │ ├── exception │ │ ├── BusinessException.java │ │ ├── HelioFrameworkException.java │ │ ├── RateLimitStrategyException.java │ │ ├── RateLimitedException.java │ │ └── UnreachableCodeException.java │ │ ├── function │ │ └── StreamFunction.java │ │ ├── page │ │ ├── PageParam.java │ │ └── PageResult.java │ │ └── props │ │ └── HelioProperties.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-aop ├── pom.xml └── src │ └── main │ └── java │ └── cc │ └── uncarbon │ └── aop │ └── stub │ └── HelioStarterAopStub.java ├── helio-starter-cloud ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── cloud │ │ └── config │ │ ├── NacosConfigAutoConfiguration.java │ │ └── NacosDiscoveryAutoConfiguration.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-crud ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── crud │ │ ├── annotation │ │ └── EnableInitHikariPoolAtStartup.java │ │ ├── config │ │ ├── DynamicDataSourceAutoConfiguration.java │ │ ├── HelioMybatisPlusAutoConfiguration.java │ │ └── InitHikariPoolAtStartupConfiguration.java │ │ ├── dynamicdatasource │ │ ├── DataSourceDefinition.java │ │ ├── DataSourceDefinitionProvider.java │ │ └── HelioDynamicDataSourceRegistry.java │ │ ├── entity │ │ ├── HelioBaseEntity.java │ │ └── HelioNoTenantBaseEntity.java │ │ ├── handler │ │ ├── HelioSequenceIdGenerateHandler.java │ │ ├── HelioSnowflakeIdGenerateHandler.java │ │ └── MybatisPlusAutoFillColumnHandler.java │ │ ├── mapper │ │ └── HelioBaseMapper.java │ │ ├── service │ │ ├── HelioBaseService.java │ │ └── impl │ │ │ └── HelioBaseServiceImpl.java │ │ └── support │ │ ├── TenantSupport.java │ │ └── impl │ │ └── DefaultTenantSupport.java │ └── resources │ ├── META-INF │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── application.yml ├── helio-starter-dubbo ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── dubbo │ │ └── filter │ │ ├── ConsumerContextFilter.java │ │ ├── HelioDubboExceptionFilter.java │ │ └── ProviderContextFilter.java │ └── resources │ └── META-INF │ └── dubbo │ ├── org.apache.dubbo.rpc.Filter │ └── org.apache.dubbo.validation.Validation ├── helio-starter-i18n ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── i18n │ │ └── util │ │ └── I18nUtil.java │ └── resources │ └── PLACEHOLDER ├── helio-starter-knife4j ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── knife4j │ │ ├── config │ │ └── HelioKnife4jAutoConfiguration.java │ │ └── customizer │ │ ├── HelioBaseEnumCustomizer.java │ │ └── Knife4jOpenApiCustomizerOverride.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-rate-limit-redis ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── ratelimit │ │ ├── annotation │ │ └── UseRateLimit.java │ │ ├── aspect │ │ └── UseRateLimitAspect.java │ │ ├── constant │ │ └── RateLimitConstant.java │ │ └── stratrgy │ │ ├── RateLimitStrategy.java │ │ └── impl │ │ ├── RateLimitByClientIPStrategy.java │ │ ├── RateLimitByUserIdStrategy.java │ │ ├── RateLimitGlobalStrategy.java │ │ └── SimpleRedisBasedRateLimitStrategy.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-redis ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── redis │ │ ├── config │ │ └── HelioRedisAutoConfiguration.java │ │ ├── enums │ │ └── KeyTypeEnum.java │ │ ├── lock │ │ ├── RedisDistributedLock.java │ │ └── impl │ │ │ └── RedisDistributedLockImpl.java │ │ ├── model │ │ ├── FormattedRedisKey.java │ │ └── RedisKeyDefinition.java │ │ └── template │ │ ├── RedisDistributedLockTemplate.java │ │ └── impl │ │ └── RedisDistributedLockTemplateImpl.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-rocketmq-aliyun ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── rocketmq │ │ ├── annotation │ │ ├── CommonMessage.java │ │ ├── MessageListener.java │ │ ├── OrderMessage.java │ │ ├── RocketListener.java │ │ ├── RocketMessage.java │ │ ├── ShardingKey.java │ │ ├── StartDeliverTime.java │ │ └── TransactionMessage.java │ │ ├── aspect │ │ └── RocketAspect.java │ │ ├── config │ │ └── AliyunRocketAutoConfiguration.java │ │ ├── container │ │ ├── RocketConsumerContainer.java │ │ └── RocketProducerContainer.java │ │ ├── core │ │ ├── consumer │ │ │ ├── AbstractRocketListener.java │ │ │ ├── DefaultBatchMessageListener.java │ │ │ ├── DefaultMessageListener.java │ │ │ └── DefaultMessageOrderListener.java │ │ ├── factory │ │ │ ├── ConsumerFactory.java │ │ │ ├── ConsumerPropertiesFactory.java │ │ │ ├── MessageFactory.java │ │ │ ├── MethodFactory.java │ │ │ ├── ProducerConsumerFactory.java │ │ │ ├── ProducerFactory.java │ │ │ ├── ProducerPropertiesFactory.java │ │ │ ├── PropertiesFactory.java │ │ │ ├── SendMessageFactory.java │ │ │ ├── ShardingKeyFactory.java │ │ │ ├── StartDeliverTimeFactory.java │ │ │ ├── ThreadPoolFactory.java │ │ │ └── execution │ │ │ │ ├── ConsumerFactoryExecution.java │ │ │ │ ├── MethodFactoryExecution.java │ │ │ │ ├── ProducerFactoryExecution.java │ │ │ │ ├── SendMessageFactoryExecution.java │ │ │ │ └── ThreadPoolExecutorExecution.java │ │ ├── producer │ │ │ ├── DefaultLocalTransactionChecker.java │ │ │ ├── DefaultLocalTransactionExecutor.java │ │ │ ├── DefaultSendCallback.java │ │ │ └── MessageSendType.java │ │ ├── serializer │ │ │ ├── Base64Serializer.java │ │ │ ├── HutoolJsonSerializer.java │ │ │ ├── JacksonJsonSerializer.java │ │ │ └── RocketSerializer.java │ │ ├── strategy │ │ │ ├── ProducerStrategy.java │ │ │ ├── PutProducerStrategy.java │ │ │ ├── RocketConsumerStrategy.java │ │ │ └── SendMessageStrategy.java │ │ └── utils │ │ │ ├── AnnotatedMethodsUtils.java │ │ │ ├── ApplicationContextUtils.java │ │ │ ├── AspectUtils.java │ │ │ └── InterceptRocket.java │ │ ├── exception │ │ └── RocketException.java │ │ ├── props │ │ └── AliyunRocketProperties.java │ │ └── thread │ │ ├── AbstractConsumerThread.java │ │ ├── AbstractProducerThread.java │ │ └── AbstractSendMessageThread.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-satoken ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── satoken │ │ ├── config │ │ └── SaTokenLocalCacheAutoConfiguration.java │ │ └── dao │ │ └── SaTokenLocalCacheDao.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-tenant ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── tenant │ │ ├── annotation │ │ ├── EnableGlobalTenantDataSource.java │ │ └── IgnoreTenantDataSource.java │ │ ├── config │ │ ├── GlobalTenantDataSourceConfiguration.java │ │ └── HelioTenantAutoConfiguration.java │ │ ├── support │ │ ├── TenantDataSourceSupport.java │ │ ├── TenantLineSupport.java │ │ └── TenantTableSupport.java │ │ └── tenantdatasource │ │ ├── GlobalTenantDataSourceAdvisor.java │ │ └── GlobalTenantDataSourceInterceptor.java │ └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── helio-starter-test ├── pom.xml └── src │ └── main │ └── java │ └── cc │ └── uncarbon │ └── framework │ └── test │ └── stub │ └── HelioStarterTestStub.java ├── helio-starter-web ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── uncarbon │ │ └── framework │ │ └── web │ │ ├── aspect │ │ └── WebLoggingAspect.java │ │ ├── config │ │ ├── DefaultWebMvcAutoConfiguration.java │ │ ├── GlobalWebExceptionHandlerAutoConfiguration.java │ │ ├── JacksonExtendAutoConfiguration.java │ │ ├── MessageConverterAutoConfiguration.java │ │ └── XssAutoConfiguration.java │ │ ├── enums │ │ └── GlobalWebExceptionI18nMessageEnum.java │ │ ├── jackson │ │ ├── EnumConverterFactory.java │ │ ├── EnumModule.java │ │ └── HelioJacksonModule.java │ │ ├── listener │ │ └── LaunchEventListener.java │ │ ├── model │ │ ├── request │ │ │ └── IdsDTO.java │ │ └── response │ │ │ └── ApiResult.java │ │ ├── util │ │ ├── IPUtil.java │ │ ├── InvalidFieldUtil.java │ │ └── MockMultipartFile.java │ │ └── xss │ │ ├── HTMLFilter.java │ │ ├── SQLFilter.java │ │ ├── XssFilter.java │ │ └── XssHttpServletRequestWrapper.java │ └── resources │ ├── META-INF │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── application.yml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # MacOS 26 | .DS_Store 27 | 28 | # IntelliJ IDEA 29 | .idea 30 | *.iws 31 | *.iml 32 | *.ipr 33 | 34 | # VSCode 35 | .vscode 36 | 37 | # VSCode 不会自动忽略 **/target 目录 38 | target/ 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # helio-starters 2 | 3 | ## 项目介绍 4 | 5 | `helio-starters`是Helio脚手架及其业务微服务模块的基础支撑构件,可以快速引入其他中间件和基础配置 6 | 7 | JDK compatibility: 17 - 21 8 | 9 | [官方文档](https://helio.uncarbon.cc/) 10 | [主要依赖](https://helio.uncarbon.cc/#/i18n/zh-CN/helio-starters/dependencies) 11 | 12 | ## Helio系列脚手架 13 | | 项目名 | 简介 | Gitee | GitHub | 14 | |--------------------|-------------------------------------------------------------------|----------------------------------------------------------|------------------------------------------------------------| 15 | | helio-boot | 单Maven模块,定位为快速开发脚手架,适合初学者学习 JavaWeb 开发的良好实践 | [Gitee](https://gitee.com/uncarbon97/helio-boot) | [GitHub](https://github.com/uncarbon97/helio-boot) | 16 | | helio-boot-modular | 按职责拆分为多Maven模块,依然是大单体,但命名及用途对标`helio-cloud`;适合多人协作、但不打算使用微服务架构的团队 | [Gitee](https://gitee.com/uncarbon97/helio-boot-modular) | [GitHub](https://github.com/uncarbon97/helio-boot-modular) | 17 | | helio-cloud | 微服务项目脚手架 | [Gitee](https://gitee.com/uncarbon97/helio-cloud) | [GitHub](https://github.com/uncarbon97/helio-cloud) | 18 | -------------------------------------------------------------------------------- /helio-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-core 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter 26 | 27 | 28 | 29 | org.apache.logging.log4j 30 | log4j-to-slf4j 31 | 32 | 33 | 34 | 35 | org.slf4j 36 | jul-to-slf4j 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-configuration-processor 45 | 46 | 47 | 48 | 49 | com.fasterxml.jackson.core 50 | jackson-annotations 51 | 52 | 53 | 54 | 55 | jakarta.validation 56 | jakarta.validation-api 57 | 58 | 59 | 60 | 61 | com.alibaba 62 | transmittable-thread-local 63 | 64 | 65 | 66 | 67 | cn.hutool 68 | hutool-all 69 | 70 | 71 | 72 | 73 | io.swagger.core.v3 74 | swagger-annotations 75 | 76 | 77 | 78 | 79 | com.baomidou 80 | mybatis-plus-annotation 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/cache/TimedCacheEx.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.cache; 2 | 3 | import cn.hutool.cache.impl.TimedCache; 4 | import cn.hutool.core.lang.mutable.MutableObj; 5 | 6 | /** 7 | * 继承自 hutool TimedCache 8 | * 增加了容量限制,重写了 containsKey 方法判断逻辑提升效率并支持空值 9 | * @param key类型 10 | * @param value类型 11 | * 12 | * @since 2.1.0 13 | * @author Uncarbon 14 | */ 15 | public class TimedCacheEx extends TimedCache { 16 | 17 | public TimedCacheEx(long timeout, int capacity) { 18 | super(timeout); 19 | this.capacity = capacity; 20 | } 21 | 22 | @Override 23 | public boolean containsKey(K key) { 24 | return this.cacheMap.containsKey(MutableObj.of(key)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/config/HelioPropertiesAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.config; 2 | 3 | import cc.uncarbon.framework.core.props.HelioProperties; 4 | import org.springframework.boot.autoconfigure.AutoConfiguration; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | 7 | /** 8 | * properties 解析自动配置类 9 | * 10 | * @author Uncarbon 11 | */ 12 | @EnableConfigurationProperties(value = {HelioProperties.class}) 13 | @AutoConfiguration 14 | public class HelioPropertiesAutoConfiguration { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/config/StaticMethodAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.config; 2 | 3 | import cn.hutool.http.HttpGlobalConfig; 4 | import jakarta.annotation.PostConstruct; 5 | import org.springframework.boot.autoconfigure.AutoConfiguration; 6 | 7 | /** 8 | * 静态工具方法自动配置类 9 | */ 10 | @AutoConfiguration 11 | public class StaticMethodAutoConfiguration { 12 | 13 | /** 14 | * hutool HttpUtil 默认超时,单位毫秒 15 | * 同时应用于 connectionTimeout 和 readTimeout 16 | */ 17 | private static final int HUTOOL_HTTP_UTIL_DEFAULT_TIMEOUT = 30000; 18 | 19 | 20 | @PostConstruct 21 | public void postConstruct() { 22 | HttpGlobalConfig.setTimeout(HUTOOL_HTTP_UTIL_DEFAULT_TIMEOUT); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/context/TenantContext.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.context; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.experimental.Accessors; 8 | import lombok.experimental.SuperBuilder; 9 | 10 | import java.io.Serial; 11 | import java.io.Serializable; 12 | 13 | /** 14 | * 当前租户上下文对象 15 | * 16 | * @author Uncarbon 17 | */ 18 | @Accessors(chain = true) 19 | @SuperBuilder 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | @Data 23 | public class TenantContext implements Serializable { 24 | 25 | @Serial 26 | private static final long serialVersionUID = 1L; 27 | 28 | public static final String CAMEL_NAME = "tenantContext"; 29 | 30 | @Schema(description = "租户ID") 31 | private Long tenantId; 32 | 33 | @Schema(description = "租户名") 34 | private String tenantName; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/context/TenantContextHolder.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.context; 2 | 3 | import com.alibaba.ttl.TransmittableThreadLocal; 4 | import lombok.experimental.UtilityClass; 5 | 6 | import java.util.Optional; 7 | 8 | /** 9 | * 租户上下文持有者类 10 | * 11 | * @author Uncarbon 12 | */ 13 | @UtilityClass 14 | public class TenantContextHolder { 15 | 16 | private static final TransmittableThreadLocal THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>(); 17 | 18 | 19 | /** 20 | * 获取当前租户上下文 21 | * 22 | * @return null or 当前租户上下文 23 | */ 24 | public TenantContext getTenantContext() { 25 | return THREAD_LOCAL_CONTEXT.get(); 26 | } 27 | 28 | /** 29 | * 获取当前租户上下文 30 | */ 31 | public Optional getTenantContextOptional() { 32 | return Optional.ofNullable(THREAD_LOCAL_CONTEXT.get()); 33 | } 34 | 35 | /** 36 | * 设置当前租户上下文 37 | * 38 | * @param newContext 新上下文,传 null 则为清除 39 | */ 40 | public void setTenantContext(TenantContext newContext) { 41 | if (newContext == null) { 42 | THREAD_LOCAL_CONTEXT.remove(); 43 | return; 44 | } 45 | 46 | THREAD_LOCAL_CONTEXT.set(newContext); 47 | } 48 | 49 | /** 50 | * 强制清空本线程的租户上下文,防止影响被线程池复用的其他线程,以及内存泄露 51 | */ 52 | public void clear() { 53 | setTenantContext(null); 54 | } 55 | 56 | /** 57 | * 捷径API-取当前租户ID 58 | * 59 | * @return null or 当前租户ID 60 | */ 61 | public Long getTenantId() { 62 | TenantContext context = getTenantContext(); 63 | return context == null ? null : context.getTenantId(); 64 | } 65 | 66 | /** 67 | * 捷径API-取当前租户名 68 | * 69 | * @return null or 当前租户名 70 | */ 71 | public String getTenantName() { 72 | TenantContext context = getTenantContext(); 73 | return context == null ? null : context.getTenantName(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/context/UserContext.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.context; 2 | 3 | import cn.hutool.core.lang.Dict; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.experimental.Accessors; 9 | import lombok.experimental.SuperBuilder; 10 | 11 | import java.io.Serial; 12 | import java.io.Serializable; 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | /** 17 | * 当前用户上下文对象 18 | * 19 | * @author Uncarbon 20 | */ 21 | @Accessors(chain = true) 22 | @SuperBuilder 23 | @AllArgsConstructor 24 | @NoArgsConstructor 25 | @Data 26 | public class UserContext implements Serializable { 27 | 28 | @Serial 29 | private static final long serialVersionUID = 1L; 30 | 31 | public static final String CAMEL_NAME = "userContext"; 32 | 33 | @Schema(description = "用户ID") 34 | private Long userId; 35 | 36 | @Schema(description = "用户账号") 37 | private String userName; 38 | 39 | @Schema(description = "用户手机号") 40 | private String userPhoneNo; 41 | 42 | @Schema(description = "用户类型") 43 | private String userTypeStr; 44 | 45 | @Schema(description = "用户拥有角色ID", title = "后台管理使用", example = "[1, 2, 3]") 46 | private Set rolesIds; 47 | 48 | @Schema(description = "用户拥有角色名称", title = "后台管理使用", example = "[SuperAdmin, Admin, CEO]") 49 | private List roles; 50 | 51 | @Schema(description = "附加数据") 52 | private Dict extraData; 53 | 54 | @Schema(description = "客户端IP地址") 55 | private String clientIP; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/context/UserContextHolder.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.context; 2 | 3 | import com.alibaba.ttl.TransmittableThreadLocal; 4 | import lombok.experimental.UtilityClass; 5 | 6 | import java.util.Optional; 7 | 8 | /** 9 | * 用户上下文持有者类 10 | * 11 | * @author Uncarbon 12 | */ 13 | @UtilityClass 14 | public class UserContextHolder { 15 | 16 | private static final TransmittableThreadLocal THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>(); 17 | 18 | 19 | /** 20 | * 获取当前用户上下文 21 | * 22 | * @return null or 当前用户上下文 23 | */ 24 | public UserContext getUserContext() { 25 | return THREAD_LOCAL_CONTEXT.get(); 26 | } 27 | 28 | /** 29 | * 获取当前用户上下文 30 | */ 31 | public Optional getUserContextOptional() { 32 | return Optional.ofNullable(THREAD_LOCAL_CONTEXT.get()); 33 | } 34 | 35 | /** 36 | * 设置当前用户上下文 37 | * 38 | * @param newContext 新上下文,传 null 则为清除 39 | */ 40 | public void setUserContext(UserContext newContext) { 41 | if (newContext == null) { 42 | THREAD_LOCAL_CONTEXT.remove(); 43 | return; 44 | } 45 | 46 | THREAD_LOCAL_CONTEXT.set(newContext); 47 | } 48 | 49 | /** 50 | * 强制清空本线程的用户上下文,防止影响被线程池复用的其他线程,以及内存泄露 51 | */ 52 | public void clear() { 53 | setUserContext(null); 54 | } 55 | 56 | /** 57 | * 捷径API-取当前用户ID 58 | * 59 | * @return null or 当前用户ID 60 | */ 61 | public Long getUserId() { 62 | UserContext context = getUserContext(); 63 | return context == null ? null : context.getUserId(); 64 | } 65 | 66 | /** 67 | * 捷径API-取当前用户名 68 | * 69 | * @return null or 当前用户名 70 | */ 71 | public String getUserName() { 72 | UserContext context = getUserContext(); 73 | return context == null ? null : context.getUserName(); 74 | } 75 | 76 | /** 77 | * 捷径API-取当前用户手机号 78 | * 79 | * @return null or 当前用户手机号 80 | */ 81 | public String getUserPhoneNo() { 82 | UserContext context = getUserContext(); 83 | return context == null ? null : context.getUserPhoneNo(); 84 | } 85 | 86 | /** 87 | * 捷径API-取当前用户 IP 地址 88 | * 89 | * @return null or 当前用户 IP 地址 90 | */ 91 | public String getClientIP() { 92 | UserContext context = getUserContext(); 93 | return context == null ? null : context.getClientIP(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/enums/EnabledStatusEnum.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.enums; 2 | 3 | import cc.uncarbon.framework.core.exception.UnreachableCodeException; 4 | import com.baomidou.mybatisplus.annotation.EnumValue; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | 8 | 9 | /** 10 | * 启用状态枚举 11 | */ 12 | @AllArgsConstructor 13 | @Getter 14 | public enum EnabledStatusEnum implements HelioBaseEnum { 15 | 16 | DISABLED(0, "禁用"), 17 | ENABLED(1, "启用"),; 18 | 19 | @EnumValue 20 | private final Integer value; 21 | private final String label; 22 | 23 | /** 24 | * 根据值得到枚举对象 25 | * @param value 外部值 26 | * @return null or 枚举对象 27 | */ 28 | public static EnabledStatusEnum of(Integer value) { 29 | if (value == null) { 30 | return null; 31 | } 32 | 33 | // 区分度小,直接用if判断了 34 | if (DISABLED.value.equals(value)) { 35 | return DISABLED; 36 | } 37 | if (ENABLED.value.equals(value)) { 38 | return ENABLED; 39 | } 40 | return null; 41 | } 42 | 43 | /** 44 | * 根据值得到枚举对象 45 | * @param value 外部值 46 | * @return null or 枚举对象 47 | */ 48 | public static EnabledStatusEnum of(boolean value) { 49 | return value ? ENABLED : DISABLED; 50 | } 51 | 52 | /** 53 | * 取反值 54 | * @param old 原值 55 | * @return 新值 56 | */ 57 | public static EnabledStatusEnum reverse(EnabledStatusEnum old) { 58 | if (old == null) { 59 | return null; 60 | } 61 | 62 | if (old == EnabledStatusEnum.DISABLED) { 63 | return ENABLED; 64 | } else if (old == EnabledStatusEnum.ENABLED) { 65 | return DISABLED; 66 | } 67 | 68 | throw new UnreachableCodeException(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/enums/GenderEnum.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | 8 | /** 9 | * 生理性别枚举类 10 | */ 11 | @AllArgsConstructor 12 | @Getter 13 | public enum GenderEnum implements HelioBaseEnum { 14 | 15 | UNKNOWN(0, "未知"), 16 | MALE(1, "男"), 17 | FEMALE(2, "女"),; 18 | 19 | @EnumValue 20 | private final Integer value; 21 | private final String label; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/enums/IdGeneratorStrategyEnum.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.enums; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | /** 8 | * 主键ID生成器策略枚举类 9 | * @author Uncarbon 10 | */ 11 | @AllArgsConstructor 12 | @Getter 13 | public enum IdGeneratorStrategyEnum implements HelioBaseEnum { 14 | 15 | /** 16 | * Twitter雪花算法 17 | */ 18 | SNOWFLAKE(1, "Twitter Snowflake"), 19 | 20 | /** 21 | * Mybatis-Plus 提供的 Sequence 算法(类雪花) 22 | */ 23 | SEQUENCE(2, "Mybatis-Plus Sequence"),; 24 | 25 | private final Integer value; 26 | private final String label; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/enums/TenantIsolateLevelEnum.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.enums; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | /** 8 | * 多租户隔离级别枚举类 9 | * 10 | * @author Uncarbon 11 | */ 12 | @AllArgsConstructor 13 | @Getter 14 | public enum TenantIsolateLevelEnum implements HelioBaseEnum { 15 | 16 | /** 17 | * 行级,即每张表增加一个'租户ID'字段 18 | */ 19 | LINE(1, "行级"), 20 | 21 | /** 22 | * 数据源级,即每个租户使用独立的数据源 23 | */ 24 | DATASOURCE(2, "数据源级"),; 25 | 26 | private final Integer value; 27 | private final String label; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/enums/YesOrNoEnum.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.enums; 2 | 3 | import cc.uncarbon.framework.core.exception.UnreachableCodeException; 4 | import com.baomidou.mybatisplus.annotation.EnumValue; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | 8 | /** 9 | * 是或否枚举 10 | */ 11 | @AllArgsConstructor 12 | @Getter 13 | public enum YesOrNoEnum implements HelioBaseEnum { 14 | 15 | NO(0, "否"), 16 | YES(1, "是"),; 17 | 18 | @EnumValue 19 | private final Integer value; 20 | private final String label; 21 | 22 | /** 23 | * 根据值得到枚举对象 24 | * @param value 外部值 25 | * @return null or 枚举对象 26 | */ 27 | public static YesOrNoEnum of(Integer value) { 28 | if (value == null) { 29 | return null; 30 | } 31 | 32 | // 区分度小,直接用if判断了 33 | if (YES.value.equals(value)) { 34 | return YES; 35 | } 36 | if (NO.value.equals(value)) { 37 | return NO; 38 | } 39 | return null; 40 | } 41 | 42 | /** 43 | * 根据值得到枚举对象 44 | * @param value 外部值 45 | * @return null or 枚举对象 46 | */ 47 | public static YesOrNoEnum of(boolean value) { 48 | return value ? YES : NO; 49 | } 50 | 51 | /** 52 | * 取反值 53 | * @param old 原值 54 | * @return 新值 55 | */ 56 | public static YesOrNoEnum reverse(YesOrNoEnum old) { 57 | if (old == null) { 58 | return null; 59 | } 60 | 61 | if (old == YesOrNoEnum.YES) { 62 | return NO; 63 | } else if (old == YesOrNoEnum.NO) { 64 | return YES; 65 | } 66 | 67 | throw new UnreachableCodeException(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/exception/HelioFrameworkException.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.exception; 2 | 3 | /** 4 | * Helio脚手架内部出错异常类 5 | * 6 | * @author Uncarbon 7 | */ 8 | public class HelioFrameworkException extends RuntimeException { 9 | 10 | public HelioFrameworkException(String msg) { 11 | super(msg); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitStrategyException.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.exception; 2 | 3 | /** 4 | * 限流策略异常 5 | * 用于helio-starter-rate-limit-redis 6 | */ 7 | public class RateLimitStrategyException extends HelioFrameworkException { 8 | 9 | public RateLimitStrategyException(String msg) { 10 | super(msg); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitedException.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.exception; 2 | 3 | import cc.uncarbon.framework.core.enums.HelioBaseEnum; 4 | import cn.hutool.http.HttpStatus; 5 | import lombok.NonNull; 6 | 7 | /** 8 | * 已被限流异常 9 | * 用于helio-starter-rate-limit-redis 10 | */ 11 | public class RateLimitedException extends BusinessException { 12 | 13 | public RateLimitedException() { 14 | super(HttpStatus.HTTP_TOO_MANY_REQUESTS, "操作频率不要太快"); 15 | } 16 | 17 | public RateLimitedException(String msg) { 18 | super(msg); 19 | } 20 | 21 | public RateLimitedException(int code, String msg) { 22 | super(code, msg); 23 | } 24 | 25 | public RateLimitedException(int code, String msg, Object... templateParams) { 26 | super(code, msg, templateParams); 27 | } 28 | 29 | public RateLimitedException(@NonNull HelioBaseEnum customEnum) { 30 | super(customEnum); 31 | } 32 | 33 | public RateLimitedException(@NonNull HelioBaseEnum customEnum, Object... templateParams) { 34 | super(customEnum, templateParams); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/exception/UnreachableCodeException.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.exception; 2 | 3 | /** 4 | * 不可达代码异常类 5 | * 6 | * @author Uncarbon 7 | */ 8 | public class UnreachableCodeException extends RuntimeException { 9 | 10 | public UnreachableCodeException() { 11 | super("Execution path unreachable"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/function/StreamFunction.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.function; 2 | 3 | import java.util.function.BinaryOperator; 4 | 5 | /** 6 | * Stream 流函数 7 | * 其实更像是一个另类的工具方法集合 8 | * 9 | * @author Uncarbon 10 | */ 11 | public final class StreamFunction { 12 | 13 | private StreamFunction() { 14 | } 15 | 16 | /** 17 | * 用于 stream-collect-toMap 时,直接覆盖已存在的 key;不忽略的话,遇到重复项时默认会抛出异常 18 | *

19 | * 用法:stream().collect(Collectors.toMap(XXX, YYY, StreamFunction.ignoredThrowingMerger())) 20 | * 21 | * @param 键类型 22 | * @return 键名 23 | */ 24 | public static BinaryOperator ignoredThrowingMerger() { 25 | return (u, v) -> u; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/page/PageParam.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.page; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.experimental.Accessors; 8 | 9 | import java.io.Serializable; 10 | import java.util.function.UnaryOperator; 11 | 12 | /** 13 | * 分页查询参数 14 | * 15 | * @author Uncarbon 16 | */ 17 | @Accessors(chain = true) 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @Data 21 | public class PageParam implements Serializable { 22 | 23 | @Schema(description = "当前页码") 24 | private Integer pageNum; 25 | public Integer getPageNum() { 26 | if (pageNum == null) { 27 | pageNum = 1; 28 | } 29 | 30 | if (globalPageNumLimiter != null) { 31 | pageNum = globalPageNumLimiter.apply(pageNum); 32 | } 33 | return pageNum; 34 | } 35 | 36 | @Schema(description = "当前页大小") 37 | private Integer pageSize; 38 | public Integer getPageSize() { 39 | if (pageSize == null) { 40 | pageSize = 10; 41 | } 42 | 43 | if (globalPageSizeLimiter != null) { 44 | return globalPageSizeLimiter.apply(pageSize); 45 | } 46 | return pageSize; 47 | } 48 | 49 | /** 50 | * 全局分页页码限制器 51 | */ 52 | private static UnaryOperator globalPageNumLimiter = null; 53 | 54 | /** 55 | * 指定全局分页页码限制器 56 | */ 57 | public static synchronized void setGlobalPageNumLimiter(UnaryOperator globalPageNumLimiter) { 58 | PageParam.globalPageNumLimiter = globalPageNumLimiter; 59 | } 60 | 61 | /** 62 | * 全局分页大小限制器(如:限制页大小上限为1000) 63 | */ 64 | private static UnaryOperator globalPageSizeLimiter = null; 65 | 66 | /** 67 | * 指定全局分页大小限制器 68 | */ 69 | public static synchronized void setGlobalPageSizeLimiter(UnaryOperator globalPageSizeLimiter) { 70 | PageParam.globalPageSizeLimiter = globalPageSizeLimiter; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /helio-core/src/main/java/cc/uncarbon/framework/core/page/PageResult.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.core.page; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.experimental.Accessors; 8 | 9 | import java.io.Serializable; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | /** 14 | * 分页查询结果 15 | * @param records字段的泛型类型 16 | * 17 | * @author Uncarbon 18 | */ 19 | @Accessors(chain = true) 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | @Data 23 | public class PageResult implements Serializable { 24 | 25 | @Schema(description = "当前页") 26 | private long current; 27 | 28 | @Schema(description = "当前页数量") 29 | private long size; 30 | 31 | @Schema(description = "总量") 32 | private long total; 33 | 34 | @Schema(description = "记录") 35 | private List records; 36 | 37 | 38 | /** 39 | * 构造方法 - 用于空集合结果 40 | * @param pageParam 分页查询参数 41 | */ 42 | public PageResult(PageParam pageParam) { 43 | this.current = pageParam.getPageNum(); 44 | this.size = pageParam.getPageSize(); 45 | this.total = 0L; 46 | this.records = Collections.emptyList(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /helio-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.core.config.HelioPropertiesAutoConfiguration 2 | cc.uncarbon.framework.core.config.AdaptHistoryVersionAutoConfiguration 3 | cc.uncarbon.framework.core.config.StaticMethodAutoConfiguration 4 | -------------------------------------------------------------------------------- /helio-starter-aop/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-aop 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.aspectj 24 | aspectjrt 25 | 26 | 27 | 28 | org.aspectj 29 | aspectjweaver 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /helio-starter-aop/src/main/java/cc/uncarbon/aop/stub/HelioStarterAopStub.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.aop.stub; 2 | 3 | /** 4 | * 需要一个空类以解决:Missing: no javadoc jar found in folder 5 | */ 6 | public final class HelioStarterAopStub { 7 | private HelioStarterAopStub() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /helio-starter-cloud/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-cloud 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-bootstrap 32 | 33 | 34 | 35 | com.alibaba.cloud 36 | spring-cloud-starter-alibaba-nacos-discovery 37 | 38 | 39 | 40 | com.alibaba.cloud 41 | spring-cloud-starter-alibaba-nacos-config 42 | 43 | 44 | 45 | com.alibaba.nacos 46 | nacos-client 47 | ${nacos-client.vesion} 48 | 49 | 50 | 51 | org.springframework.cloud 52 | spring-cloud-commons 53 | 54 | 55 | 56 | org.springframework.cloud 57 | spring-cloud-context 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /helio-starter-cloud/src/main/java/cc/uncarbon/framework/cloud/config/NacosConfigAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.cloud.config; 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfiguration; 4 | import org.springframework.cloud.context.config.annotation.RefreshScope; 5 | 6 | /** 7 | * 启用配置自动刷新 自动配置类 8 | * 9 | * @author Uncarbon 10 | */ 11 | @RefreshScope 12 | @AutoConfiguration 13 | public class NacosConfigAutoConfiguration { 14 | } 15 | -------------------------------------------------------------------------------- /helio-starter-cloud/src/main/java/cc/uncarbon/framework/cloud/config/NacosDiscoveryAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.cloud.config; 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfiguration; 4 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 5 | 6 | /** 7 | * 启用服务发现 自动配置类 8 | * 9 | * @author Uncarbon 10 | */ 11 | @EnableDiscoveryClient 12 | @AutoConfiguration 13 | public class NacosDiscoveryAutoConfiguration { 14 | } 15 | -------------------------------------------------------------------------------- /helio-starter-cloud/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.cloud.config.NacosDiscoveryAutoConfiguration 2 | cc.uncarbon.framework.cloud.config.NacosConfigAutoConfiguration 3 | -------------------------------------------------------------------------------- /helio-starter-crud/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-crud 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | cc.uncarbon.framework 24 | helio-core 25 | 26 | 27 | 28 | 29 | com.baomidou 30 | mybatis-plus-spring-boot3-starter 31 | 32 | 33 | 34 | com.mysql 35 | mysql-connector-j 36 | 37 | 38 | 39 | org.postgresql 40 | postgresql 41 | 42 | 43 | 44 | com.oracle.database.jdbc 45 | ojdbc8 46 | true 47 | 48 | 49 | 50 | com.microsoft.sqlserver 51 | mssql-jdbc 52 | true 53 | 54 | 55 | 56 | com.baomidou 57 | dynamic-datasource-spring-boot-starter 58 | true 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/annotation/EnableInitHikariPoolAtStartup.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.annotation; 2 | 3 | import cc.uncarbon.framework.crud.config.InitHikariPoolAtStartupConfiguration; 4 | import org.springframework.context.annotation.Import; 5 | 6 | import java.lang.annotation.*; 7 | 8 | /** 9 | * 在项目启动时直接初始化Hikari连接池, 关闭按需连接 10 | * 参考https://github.com/spring-projects/spring-boot/issues/19596 11 | * @author Uncarbon 12 | */ 13 | @Import({InitHikariPoolAtStartupConfiguration.class}) 14 | @Target(ElementType.TYPE) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Documented 17 | @Inherited 18 | public @interface EnableInitHikariPoolAtStartup { 19 | } 20 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/config/DynamicDataSourceAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.config; 2 | 3 | import cc.uncarbon.framework.crud.dynamicdatasource.DataSourceDefinitionProvider; 4 | import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegistry; 5 | import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; 6 | import com.baomidou.dynamic.datasource.creator.HikariDataSourceCreator; 7 | import org.springframework.beans.factory.ObjectProvider; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 10 | import org.springframework.context.annotation.Bean; 11 | 12 | /** 13 | * 动态数据源自动配置类 14 | * 15 | * @author Uncarbon 16 | */ 17 | @ConditionalOnClass(name = "com.baomidou.dynamic.datasource.DynamicRoutingDataSource") 18 | public class DynamicDataSourceAutoConfiguration { 19 | 20 | @ConditionalOnBean(value = DynamicRoutingDataSource.class) 21 | @Bean 22 | public HelioDynamicDataSourceRegistry helioDynamicDataSourceRegistry( 23 | DynamicRoutingDataSource dynamicRoutingDataSource, 24 | HikariDataSourceCreator dataSourceCreator, 25 | ObjectProvider dataSourceDefinitionProviders 26 | ) { 27 | return new HelioDynamicDataSourceRegistry( 28 | dynamicRoutingDataSource, dataSourceCreator, dataSourceDefinitionProviders); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/config/InitHikariPoolAtStartupConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.config; 2 | 3 | import javax.sql.DataSource; 4 | import org.springframework.boot.ApplicationRunner; 5 | import org.springframework.context.annotation.Bean; 6 | 7 | 8 | /** 9 | * 在项目启动时直接初始化 Hikari 连接池(否则默认为懒加载) 10 | * 11 | * @author Uncarbon 12 | */ 13 | public class InitHikariPoolAtStartupConfiguration { 14 | 15 | @Bean 16 | public ApplicationRunner runner(DataSource dataSource) { 17 | return args -> dataSource.getConnection(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/DataSourceDefinition.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.dynamicdatasource; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.experimental.Accessors; 8 | import lombok.experimental.SuperBuilder; 9 | 10 | import java.io.Serial; 11 | import java.io.Serializable; 12 | 13 | /** 14 | * 动态数据源定义 15 | */ 16 | @Accessors(chain = true) 17 | @SuperBuilder 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @Data 21 | public class DataSourceDefinition implements Serializable { 22 | 23 | @Serial 24 | private static final long serialVersionUID = 1L; 25 | 26 | /** 27 | * 数据源名称,切换数据源时需要这个,最好不用数字开头 28 | */ 29 | @Schema(description = "数据源名称", title = "切换数据源时需要这个,最好不用数字开头") 30 | private String name; 31 | 32 | /** 33 | * 数据库驱动类名称 34 | */ 35 | @Schema(description = "数据库驱动类名称") 36 | private String driverClassName; 37 | 38 | /** 39 | * 数据库连接地址 40 | */ 41 | @Schema(description = "数据库连接地址") 42 | private String url; 43 | 44 | /** 45 | * 数据库账号 46 | */ 47 | @Schema(description = "数据库账号") 48 | private String username; 49 | 50 | /** 51 | * 数据库密码 52 | */ 53 | @Schema(description = "数据库密码") 54 | private String password; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/DataSourceDefinitionProvider.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.dynamicdatasource; 2 | 3 | /** 4 | * 动态数据源定义提供者 5 | */ 6 | public interface DataSourceDefinitionProvider { 7 | 8 | /** 9 | * 根据数据源名称,得到数据源定义 10 | * @param dataSourceName 需要的数据源名称 11 | * @return DataSourceDefinition 12 | */ 13 | DataSourceDefinition getDataSourceDefinition(String dataSourceName); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/entity/HelioNoTenantBaseEntity.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.entity; 2 | 3 | import cc.uncarbon.framework.core.constant.HelioConstant; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | import lombok.experimental.Accessors; 10 | import lombok.experimental.SuperBuilder; 11 | 12 | import java.io.Serializable; 13 | 14 | /** 15 | * 基础实体类,去除[行级租户ID] 16 | * @param 主键类型,一般用 Long 17 | * 18 | * @author Uncarbon 19 | */ 20 | @EqualsAndHashCode(callSuper = true) 21 | @Accessors(chain = true) 22 | @SuperBuilder 23 | @AllArgsConstructor 24 | @NoArgsConstructor 25 | @Data 26 | public abstract class HelioNoTenantBaseEntity extends HelioBaseEntity { 27 | 28 | /** 29 | * 覆盖掉行级租户ID 30 | */ 31 | @TableField(value = HelioConstant.CRUD.COLUMN_TENANT_ID, exist = false) 32 | private Long tenantId; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/handler/HelioSequenceIdGenerateHandler.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.handler; 2 | 3 | import cc.uncarbon.framework.core.enums.IdGeneratorStrategyEnum; 4 | import cn.hutool.core.net.NetUtil; 5 | import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | /** 9 | * 自定义ID生成器 - Mybatis-Plus Sequence 10 | * 11 | * @author Uncarbon 12 | */ 13 | @Slf4j 14 | public class HelioSequenceIdGenerateHandler extends DefaultIdentifierGenerator { 15 | 16 | public HelioSequenceIdGenerateHandler() { 17 | super(NetUtil.getLocalhost()); 18 | log.info("[主键ID生成器] >> strategy=[{}]", 19 | IdGeneratorStrategyEnum.SEQUENCE 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/handler/HelioSnowflakeIdGenerateHandler.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.handler; 2 | 3 | import cc.uncarbon.framework.core.enums.IdGeneratorStrategyEnum; 4 | import cc.uncarbon.framework.core.props.HelioProperties; 5 | import cn.hutool.core.date.DateUtil; 6 | import cn.hutool.core.lang.Singleton; 7 | import cn.hutool.core.lang.Snowflake; 8 | import cn.hutool.core.net.NetUtil; 9 | import cn.hutool.core.util.IdUtil; 10 | import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | import java.util.Date; 14 | 15 | /** 16 | * 自定义ID生成器 - 雪花ID 17 | * 18 | * @author Uncarbon 19 | */ 20 | @Slf4j 21 | public class HelioSnowflakeIdGenerateHandler implements IdentifierGenerator { 22 | 23 | private final Snowflake snowflakeInstance; 24 | 25 | 26 | public HelioSnowflakeIdGenerateHandler(HelioProperties helioProperties) { 27 | long workerId; 28 | 29 | try { 30 | // 当前机器的局域网IP 31 | workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr()); 32 | } catch (Exception e) { 33 | workerId = NetUtil.getLocalhost().hashCode(); 34 | } 35 | 36 | // Hutool的雪花生成器仅支持0-31 37 | workerId = workerId % 32; 38 | Long datacenterId = helioProperties.getCrud().getIdGenerator().getDatacenterId(); 39 | String epochDateStr = helioProperties.getCrud().getIdGenerator().getEpochDate(); 40 | Date epochDate = DateUtil.parseDate(epochDateStr); 41 | 42 | log.info("[主键ID生成器] >> strategy=[{}], workerId=[{}], datacenterId=[{}], epochDate=[{}]", 43 | IdGeneratorStrategyEnum.SNOWFLAKE, 44 | workerId, 45 | datacenterId, 46 | epochDate 47 | ); 48 | 49 | snowflakeInstance = getSnowflake(workerId, datacenterId, epochDate); 50 | } 51 | 52 | @Override 53 | public Number nextId(Object entity) { 54 | return snowflakeInstance.nextId(); 55 | } 56 | 57 | @Override 58 | public String nextUUID(Object entity) { 59 | return IdUtil.randomUUID(); 60 | } 61 | 62 | private static Snowflake getSnowflake(long workerId, long datacenterId, Date epochDate) { 63 | return Singleton.get(Snowflake.class, epochDate, workerId, datacenterId, false); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/handler/MybatisPlusAutoFillColumnHandler.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.handler; 2 | 3 | import cc.uncarbon.framework.core.context.TenantContextHolder; 4 | import cc.uncarbon.framework.core.context.UserContextHolder; 5 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; 6 | import org.apache.ibatis.reflection.MetaObject; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | import static cc.uncarbon.framework.core.constant.HelioConstant.CRUD.*; 11 | 12 | /** 13 | * 字段自动填充, 摘自Mybatis-Plus官方例程 14 | * @author nieqiurong 15 | * @author Uncarbon 16 | */ 17 | public class MybatisPlusAutoFillColumnHandler implements MetaObjectHandler { 18 | 19 | @Override 20 | public void insertFill(MetaObject metaObject) { 21 | this.strictInsertFill(metaObject, ENTITY_FIELD_TENANT_ID, Long.class, TenantContextHolder.getTenantId()); 22 | this.strictInsertFill(metaObject, ENTITY_FIELD_CREATED_AT, LocalDateTime.class, LocalDateTime.now()); 23 | this.strictInsertFill(metaObject, ENTITY_FIELD_CREATED_BY, String.class, UserContextHolder.getUserName()); 24 | 25 | // 同时加入更新字段 26 | this.updateFill(metaObject); 27 | } 28 | 29 | @Override 30 | public void updateFill(MetaObject metaObject) { 31 | this.strictUpdateFill(metaObject, ENTITY_FIELD_UPDATED_AT, LocalDateTime.class, LocalDateTime.now()); 32 | this.strictUpdateFill(metaObject, ENTITY_FIELD_UPDATED_BY, String.class, UserContextHolder.getUserName()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/service/HelioBaseService.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.service; 2 | 3 | import cc.uncarbon.framework.crud.entity.HelioBaseEntity; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * 服务基础模板 8 | * @param 实体类 9 | * 10 | * @author Uncarbon 11 | */ 12 | public interface HelioBaseService> 13 | extends IService { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/service/impl/HelioBaseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.service.impl; 2 | 3 | import cc.uncarbon.framework.crud.entity.HelioBaseEntity; 4 | import cc.uncarbon.framework.crud.service.HelioBaseService; 5 | import cn.hutool.core.util.ReflectUtil; 6 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 7 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 8 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.util.Objects; 12 | 13 | /** 14 | * 服务实现类基础模板 15 | * @param 持久层接口 16 | * @param 实体类 17 | * 18 | * @author Uncarbon 19 | */ 20 | @NoArgsConstructor 21 | public class HelioBaseServiceImpl, E extends HelioBaseEntity> 22 | extends ServiceImpl 23 | implements HelioBaseService { 24 | 25 | /** 26 | * 解决MP自带的本方法,自动填充字段不生效问题 27 | * 参考文章 28 | */ 29 | @Override 30 | public boolean update(Wrapper updateWrapper) { 31 | E entity = updateWrapper.getEntity(); 32 | if (Objects.isNull(entity)) { 33 | entity = ReflectUtil.newInstance(this.getEntityClass()); 34 | } 35 | return update(entity, updateWrapper); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/support/TenantSupport.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.support; 2 | 3 | import cc.uncarbon.framework.core.props.HelioProperties; 4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 | 6 | /** 7 | * Mybatis-Plus 多租户支持接口 8 | * 9 | * @author Uncarbon 10 | */ 11 | public interface TenantSupport { 12 | 13 | /** 14 | * 不同的多租户隔离级别分别实现本方法,按需添加 SQL 拦截器 15 | * @param helioProperties Helio 配置属性 16 | * @param interceptor Mybatis-Plus 拦截器 17 | */ 18 | void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/support/impl/DefaultTenantSupport.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.crud.support.impl; 2 | 3 | import cc.uncarbon.framework.core.props.HelioProperties; 4 | import cc.uncarbon.framework.crud.support.TenantSupport; 5 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 6 | 7 | /** 8 | * 多租户支持-默认处理 9 | */ 10 | public class DefaultTenantSupport implements TenantSupport { 11 | 12 | @Override 13 | public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) { 14 | System.err.println("\n[多租户支持] >> 您启用了多租户,但未引入 helio-starter-tenant,无法对 SQL 进行拦截处理\n"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.crud.config.HelioMybatisPlusAutoConfiguration 2 | cc.uncarbon.framework.crud.config.DynamicDataSourceAutoConfiguration 3 | -------------------------------------------------------------------------------- /helio-starter-crud/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | mybatis-plus: 2 | global-config: 3 | banner: false -------------------------------------------------------------------------------- /helio-starter-dubbo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-dubbo 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | 30 | org.apache.dubbo 31 | dubbo-spring-boot-starter 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /helio-starter-dubbo/src/main/java/cc/uncarbon/framework/dubbo/filter/ConsumerContextFilter.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.dubbo.filter; 2 | 3 | import cc.uncarbon.framework.core.context.TenantContext; 4 | import cc.uncarbon.framework.core.context.TenantContextHolder; 5 | import cc.uncarbon.framework.core.context.UserContext; 6 | import cc.uncarbon.framework.core.context.UserContextHolder; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.dubbo.common.constants.CommonConstants; 9 | import org.apache.dubbo.common.extension.Activate; 10 | import org.apache.dubbo.rpc.*; 11 | 12 | /** 13 | * 自定义Dubbo消费者上下文 14 | * 15 | * @author Zhu JW 16 | * @author Uncarbon 17 | **/ 18 | @Activate(group = CommonConstants.CONSUMER) 19 | @Slf4j 20 | public class ConsumerContextFilter implements Filter { 21 | 22 | /** 23 | * 将用户上下文、租户上下文等业务字段,放进 Dubbo 附件中 24 | * @since 1.6.0 不再转换成 JSON 字符串后放入附件中,直接使用可序列化的对象 25 | * @since 1.8.0 判断isConsumerSide,非消费者不设置附件 26 | */ 27 | @Override 28 | public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { 29 | if (isConsumerSide(invoker)) { 30 | RpcContextAttachment clientAttachment = RpcContext.getClientAttachment(); 31 | 32 | UserContext userContext = UserContextHolder.getUserContext(); 33 | if (userContext != null) { 34 | log.debug("[Dubbo RPC] 设置当前用户上下文 >> {}", userContext); 35 | clientAttachment.setAttachment(UserContext.CAMEL_NAME, userContext); 36 | } 37 | 38 | TenantContext tenantContext = TenantContextHolder.getTenantContext(); 39 | if (tenantContext != null && tenantContext.getTenantId() != null) { 40 | // 实际启用了租户 41 | log.debug("[Dubbo RPC] 设置当前租户上下文 >> {}", tenantContext); 42 | clientAttachment.setAttachment(TenantContext.CAMEL_NAME, tenantContext); 43 | } 44 | } 45 | return invoker.invoke(invocation); 46 | } 47 | 48 | /** 49 | * 原来的 clientAttachment.isConsumerSide() 抽风了,只能手撸一个了 50 | * @return 调用源是否为【消费者】 51 | */ 52 | private boolean isConsumerSide(Invoker invoker) { 53 | return CommonConstants.CONSUMER_SIDE.equals(invoker.getUrl().getSide()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /helio-starter-dubbo/src/main/java/cc/uncarbon/framework/dubbo/filter/ProviderContextFilter.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.dubbo.filter; 2 | 3 | import cc.uncarbon.framework.core.context.TenantContext; 4 | import cc.uncarbon.framework.core.context.TenantContextHolder; 5 | import cc.uncarbon.framework.core.context.UserContext; 6 | import cc.uncarbon.framework.core.context.UserContextHolder; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.dubbo.common.constants.CommonConstants; 9 | import org.apache.dubbo.common.extension.Activate; 10 | import org.apache.dubbo.rpc.*; 11 | 12 | /** 13 | * 自定义Dubbo提供者上下文 14 | * 15 | * @author Zhu JW 16 | * @author Uncarbon 17 | **/ 18 | @Activate(group = CommonConstants.PROVIDER) 19 | @Slf4j 20 | public class ProviderContextFilter implements Filter { 21 | 22 | /** 23 | * 从 Dubbo 附件中,取出用户上下文、租户上下文等业务字段 24 | * @since 1.6.0 附件内容不再是 JSON 字符串,直接使用可序列化的对象 25 | * @since 1.8.0 判断isProviderSide,非提供者不从附件取值 26 | */ 27 | @Override 28 | public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { 29 | if (isProviderSide(invoker)) { 30 | RpcContextAttachment serverAttachment = RpcContext.getServerAttachment(); 31 | 32 | // 清理数据,避免线程池化复用残留 33 | UserContextHolder.clear(); 34 | TenantContextHolder.clear(); 35 | 36 | Object attachment = serverAttachment.getObjectAttachment(UserContext.CAMEL_NAME); 37 | if (attachment instanceof UserContext userContext) { 38 | UserContextHolder.setUserContext(userContext); 39 | log.debug("[Dubbo RPC] 取出当前用户上下文 >> {}", userContext); 40 | } 41 | 42 | attachment = serverAttachment.getObjectAttachment(TenantContext.CAMEL_NAME); 43 | if (attachment instanceof TenantContext tenantContext) { 44 | TenantContextHolder.setTenantContext(tenantContext); 45 | log.debug("[Dubbo RPC] 取出当前租户上下文 >> {}", tenantContext); 46 | } 47 | } 48 | 49 | return invoker.invoke(invocation); 50 | } 51 | 52 | /** 53 | * 原来的 serverAttachment.isProviderSide() 抽风了,只能手撸一个了 54 | * @return 调用源是否为【提供者】 55 | */ 56 | private boolean isProviderSide(Invoker invoker) { 57 | return CommonConstants.PROVIDER.equals(invoker.getUrl().getSide()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /helio-starter-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter: -------------------------------------------------------------------------------- 1 | helioDubboExceptionFilter=cc.uncarbon.framework.dubbo.filter.HelioDubboExceptionFilter 2 | consumerContext=cc.uncarbon.framework.dubbo.filter.ConsumerContextFilter 3 | providerContext=cc.uncarbon.framework.dubbo.filter.ProviderContextFilter 4 | -------------------------------------------------------------------------------- /helio-starter-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.validation.Validation: -------------------------------------------------------------------------------- 1 | jvalidation=org.apache.dubbo.validation.support.jvalidation.JValidationNew 2 | -------------------------------------------------------------------------------- /helio-starter-i18n/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-i18n 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /helio-starter-i18n/src/main/resources/PLACEHOLDER: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uncarbon97/helio-starters/f3955e6433691a8cf70bcd52fb66a77ea1da5a85/helio-starter-i18n/src/main/resources/PLACEHOLDER -------------------------------------------------------------------------------- /helio-starter-knife4j/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-knife4j 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | 30 | com.github.xiaoymin 31 | knife4j-openapi3-jakarta-spring-boot-starter 32 | 33 | 34 | 35 | org.webjars 36 | swagger-ui 37 | 38 | 39 | 40 | 41 | 42 | org.springdoc 43 | springdoc-openapi-starter-webmvc-ui 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /helio-starter-knife4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.knife4j.config.HelioKnife4jAutoConfiguration 2 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-rate-limit-redis 16 | 基于Redis实现的限流组件,内置简易的3种限流策略(全局限流、根据当前用户ID限流、根据客户端IP限流) 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | cc.uncarbon.framework 26 | helio-core 27 | 28 | 29 | 30 | cc.uncarbon.framework 31 | helio-starter-aop 32 | 33 | 34 | 35 | cc.uncarbon.framework 36 | helio-starter-redis 37 | 38 | 39 | 40 | 41 | cc.uncarbon.framework 42 | helio-starter-web 43 | true 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/annotation/UseRateLimit.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.ratelimit.annotation; 2 | 3 | import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy; 4 | import cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitGlobalStrategy; 5 | 6 | import java.lang.annotation.*; 7 | 8 | /** 9 | * 标记使用服务端限流,单接口在{duration}秒内只能被请求{max}次 10 | * 底层实现:Redis + 计数器 11 | * 备选名:FrequencyControl 12 | * 13 | * @author ruoyi 14 | * @author Uncarbon 15 | */ 16 | @Target(ElementType.METHOD) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Documented 19 | public @interface UseRateLimit { 20 | 21 | /** 22 | * 限流标识文本,会拼接至Redis Key中 23 | * 空文本则会使用Java方法的全限定名 24 | */ 25 | String mark() default ""; 26 | 27 | /** 28 | * 限流时长,单位=秒 29 | */ 30 | int duration() default 60; 31 | 32 | /** 33 | * 在限流时长内,最多可请求多少次 34 | */ 35 | int max() default 100; 36 | 37 | /** 38 | * 限流策略;默认为全局限流 39 | */ 40 | Class strategy() default RateLimitGlobalStrategy.class; 41 | } 42 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/aspect/UseRateLimitAspect.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.ratelimit.aspect; 2 | 3 | import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit; 4 | import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy; 5 | import cn.hutool.core.util.ReflectUtil; 6 | import cn.hutool.extra.spring.SpringUtil; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.aspectj.lang.JoinPoint; 10 | import org.aspectj.lang.annotation.Aspect; 11 | import org.aspectj.lang.annotation.Before; 12 | import org.springframework.beans.BeansException; 13 | 14 | /** 15 | * 限流注解关联切面 16 | * 17 | * @author ruoyi 18 | * @author Uncarbon 19 | */ 20 | @Aspect 21 | @RequiredArgsConstructor 22 | @Slf4j 23 | public class UseRateLimitAspect { 24 | 25 | @Before("@annotation(annotation)") 26 | public void before(JoinPoint point, UseRateLimit annotation) { 27 | // 确定限流策略实例 28 | Class strategyClass = annotation.strategy(); 29 | RateLimitStrategy strategyInstance = null; 30 | try { 31 | strategyInstance = SpringUtil.getBean(strategyClass); 32 | } catch (BeansException be) { 33 | // 无法从Spring容器中得到策略类实例 34 | } 35 | if (strategyInstance == null) { 36 | strategyInstance = ReflectUtil.newInstance(strategyClass); 37 | } 38 | 39 | strategyInstance.performRateLimitCheck(annotation, point); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/constant/RateLimitConstant.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.ratelimit.constant; 2 | 3 | /** 4 | * 限流组件常量 5 | */ 6 | public final class RateLimitConstant { 7 | 8 | private RateLimitConstant() { 9 | } 10 | 11 | /** 12 | * Redis Key前缀 13 | */ 14 | public static final String REDIS_KEY_PREFIX = "rateLimit:"; 15 | 16 | /** 17 | * 未知客户端IP地址 18 | */ 19 | public static final String CLIENT_IP_UNKNOWN = "unknown"; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/RateLimitStrategy.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.ratelimit.stratrgy; 2 | 3 | import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit; 4 | import cc.uncarbon.framework.core.exception.RateLimitedException; 5 | import org.aspectj.lang.JoinPoint; 6 | 7 | /** 8 | * 限流策略接口 9 | */ 10 | public interface RateLimitStrategy { 11 | 12 | /** 13 | * 限流异常生产者 14 | * 通过覆写本方法,可替换提示消息、实现国际化文案等 15 | */ 16 | default RateLimitedException rateLimitedExceptionSupplier() { 17 | return new RateLimitedException(); 18 | } 19 | 20 | /** 21 | * 执行限流检查,根据不同维度判断是否已触达限流上限 22 | * @param annotation 注解实例 23 | * @param point 切点 24 | */ 25 | void performRateLimitCheck(UseRateLimit annotation, JoinPoint point); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByClientIPStrategy.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.ratelimit.stratrgy.impl; 2 | 3 | import cc.uncarbon.framework.core.context.UserContextHolder; 4 | import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit; 5 | import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant; 6 | import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy; 7 | import cc.uncarbon.framework.web.util.IPUtil; 8 | import cn.hutool.core.text.CharSequenceUtil; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.aspectj.lang.JoinPoint; 11 | import org.springframework.data.redis.core.RedisTemplate; 12 | import org.springframework.web.context.request.RequestContextHolder; 13 | import org.springframework.web.context.request.ServletRequestAttributes; 14 | 15 | /** 16 | * 以客户端IP为维度限流策略 17 | * 基于{@link SimpleRedisBasedRateLimitStrategy}使用相同Lua脚本,仅在RedisKey有差异 18 | */ 19 | @Slf4j 20 | public class RateLimitByClientIPStrategy extends SimpleRedisBasedRateLimitStrategy implements RateLimitStrategy { 21 | 22 | public RateLimitByClientIPStrategy(RedisTemplate objectRedisTemplate) { 23 | super(objectRedisTemplate, "RateLimitByClientIPStrategy"); 24 | } 25 | 26 | @Override 27 | public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) { 28 | super.performRateLimitCheck(annotation, point, this::rateLimitedExceptionSupplier); 29 | } 30 | 31 | @Override 32 | protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) { 33 | return CharSequenceUtil.format("{}ip:{}:{}", RateLimitConstant.REDIS_KEY_PREFIX, currentClientIP(), determineMark(annotation, point)); 34 | } 35 | 36 | /** 37 | * 取当前客户端IP 38 | */ 39 | protected String currentClientIP() { 40 | String clientIP = UserContextHolder.getClientIP(); 41 | if (CharSequenceUtil.isNotEmpty(clientIP)) { 42 | return clientIP; 43 | } 44 | 45 | // 尝试从request中得到未登录用户的IP地址 46 | try { 47 | ServletRequestAttributes servletRequestAttributes = 48 | (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 49 | if (servletRequestAttributes != null) { 50 | return IPUtil.getClientIPAddress(servletRequestAttributes.getRequest(), 0); 51 | } 52 | } catch (Exception | NoClassDefFoundError e) { 53 | // 可能没有引入 helio-starter-web 依赖,忽略 54 | } 55 | 56 | // 兜底 57 | return RateLimitConstant.CLIENT_IP_UNKNOWN; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByUserIdStrategy.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.ratelimit.stratrgy.impl; 2 | 3 | import cc.uncarbon.framework.core.context.UserContextHolder; 4 | import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit; 5 | import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant; 6 | import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy; 7 | import cn.hutool.core.text.CharSequenceUtil; 8 | import cn.hutool.core.util.ObjectUtil; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.aspectj.lang.JoinPoint; 11 | import org.springframework.data.redis.core.RedisTemplate; 12 | 13 | import java.math.BigInteger; 14 | 15 | /** 16 | * 以当前用户ID为维度限流策略 17 | * 基于{@link SimpleRedisBasedRateLimitStrategy}使用相同Lua脚本,仅在RedisKey有差异 18 | */ 19 | @Slf4j 20 | public class RateLimitByUserIdStrategy extends SimpleRedisBasedRateLimitStrategy implements RateLimitStrategy { 21 | 22 | public RateLimitByUserIdStrategy(RedisTemplate objectRedisTemplate) { 23 | super(objectRedisTemplate, "RateLimitByUserIdStrategy"); 24 | } 25 | 26 | @Override 27 | public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) { 28 | super.performRateLimitCheck(annotation, point, this::rateLimitedExceptionSupplier); 29 | } 30 | 31 | @Override 32 | protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) { 33 | return CharSequenceUtil.format("{}userId:{}:{}", RateLimitConstant.REDIS_KEY_PREFIX, currentUserId(), determineMark(annotation, point)); 34 | } 35 | 36 | /** 37 | * 取当前用户ID 38 | */ 39 | protected Long currentUserId() { 40 | // 默认值=0 41 | return ObjectUtil.defaultIfNull(UserContextHolder.getUserId(), BigInteger.ZERO::longValue); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitGlobalStrategy.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.ratelimit.stratrgy.impl; 2 | 3 | import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit; 4 | import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant; 5 | import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy; 6 | import cn.hutool.core.text.CharSequenceUtil; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.aspectj.lang.JoinPoint; 9 | import org.springframework.data.redis.core.RedisTemplate; 10 | 11 | /** 12 | * 无维度区分,全局限流策略 13 | */ 14 | @Slf4j 15 | public class RateLimitGlobalStrategy extends SimpleRedisBasedRateLimitStrategy implements RateLimitStrategy { 16 | 17 | public RateLimitGlobalStrategy(RedisTemplate objectRedisTemplate) { 18 | super(objectRedisTemplate, "RateLimitGlobalStrategy"); 19 | } 20 | 21 | @Override 22 | public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) { 23 | super.performRateLimitCheck(annotation, point, this::rateLimitedExceptionSupplier); 24 | } 25 | 26 | /** 27 | * 确定RedisKey 28 | */ 29 | @Override 30 | protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) { 31 | return CharSequenceUtil.format("{}global:{}", RateLimitConstant.REDIS_KEY_PREFIX, determineMark(annotation, point)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /helio-starter-rate-limit-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.ratelimit.aspect.UseRateLimitAspect 2 | cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitGlobalStrategy 3 | cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitByUserIdStrategy 4 | cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitByClientIPStrategy 5 | -------------------------------------------------------------------------------- /helio-starter-redis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-redis 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | 30 | org.redisson 31 | redisson-spring-boot-starter 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/enums/KeyTypeEnum.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.redis.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor 7 | @Getter 8 | public enum KeyTypeEnum { 9 | 10 | // 基本类型 11 | STRING, 12 | HASH, 13 | LIST, 14 | SET, 15 | ZSET, 16 | STREAM, 17 | RE_JSON, 18 | 19 | // 封装类型 20 | LOCK, 21 | PUB_SUB, 22 | HYPER_LOG_LOG, 23 | GEO, 24 | 25 | // 尚未收录的 26 | OTHER 27 | 28 | } 29 | -------------------------------------------------------------------------------- /helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/lock/RedisDistributedLock.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.redis.lock; 2 | 3 | import org.redisson.api.RLock; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * Redis分布式可重入锁 9 | * 10 | * @author dcy 11 | * @author Uncarbon 12 | */ 13 | public interface RedisDistributedLock { 14 | 15 | /** 16 | * 获取分布式锁,等待时长无限 17 | * 18 | * @param lockName 锁名称 19 | * @param holdDuration 锁持有时长,单位:秒;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 20 | * @return 锁对象 21 | */ 22 | RLock lock(String lockName, int holdDuration); 23 | 24 | /** 25 | * 获取分布式锁,等待时长无限 26 | * 27 | * @param lockName 锁名称 28 | * @param unit 时间单位 29 | * @param holdDuration 锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 30 | * @return 锁对象 31 | */ 32 | RLock lock(String lockName, TimeUnit unit, int holdDuration); 33 | 34 | /** 35 | * 尝试获取锁,在等待时长内获取到锁则返回true,否则返回false 36 | * 37 | * @param lockName 锁名称 38 | * @param waitDuration 最大等待时长 39 | * @param holdDuration 锁持有时长,单位:秒;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 40 | * @return 锁对象 41 | */ 42 | boolean tryLock(String lockName, int waitDuration, int holdDuration); 43 | 44 | /** 45 | * 尝试获取锁,在等待时长内获取到锁则返回true,否则返回false 46 | * 47 | * @param lockName 锁名称 48 | * @param unit 时间单位 49 | * @param waitDuration 最大等待时长 50 | * @param holdDuration 锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 51 | * @return 锁对象 52 | */ 53 | boolean tryLock(String lockName, TimeUnit unit, int waitDuration, int holdDuration); 54 | 55 | /** 56 | * 主动释放锁 57 | * 注意:若锁并非当前线程持有,会抛出IllegalMonitorStateException异常 58 | * 建议使用unlockSafely 59 | * 60 | * @param lockName 锁名称 61 | */ 62 | void unlock(String lockName); 63 | 64 | /** 65 | * 主动释放锁 66 | * 注意:若锁并非当前线程持有,会抛出IllegalMonitorStateException异常 67 | * 建议使用unlockSafely 68 | * 69 | * @param lock 锁对象 70 | */ 71 | void unlock(RLock lock); 72 | 73 | /** 74 | * 安全地主动释放锁 75 | * 76 | * @param lockName 锁名称 77 | */ 78 | void unlockSafely(String lockName); 79 | 80 | /** 81 | * 安全地主动释放锁 82 | * 83 | * @param lock 锁对象 84 | */ 85 | void unlockSafely(RLock lock); 86 | 87 | /** 88 | * 指定本类最终拼接的键名前缀 89 | * 默认值为【distributedLock:】 90 | */ 91 | void setLockKeyPrefix(String newPrefix); 92 | 93 | } 94 | -------------------------------------------------------------------------------- /helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/lock/impl/RedisDistributedLockImpl.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.redis.lock.impl; 2 | 3 | import cc.uncarbon.framework.redis.lock.RedisDistributedLock; 4 | import lombok.RequiredArgsConstructor; 5 | import org.redisson.api.RLock; 6 | import org.redisson.api.RedissonClient; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicReference; 10 | 11 | /** 12 | * Redis分布式可重入锁,基于Redisson实现 13 | * 14 | * @author dcy 15 | * @author Uncarbon 16 | */ 17 | @RequiredArgsConstructor 18 | public class RedisDistributedLockImpl implements RedisDistributedLock { 19 | 20 | /** 21 | * 锁名称前缀 22 | */ 23 | private static final AtomicReference LOCK_KEY_PREFIX = new AtomicReference<>("distributedLock:"); 24 | 25 | private final RedissonClient redissonClient; 26 | 27 | 28 | @Override 29 | public RLock lock(String lockName, int holdDuration) { 30 | return this.lock(lockName, TimeUnit.SECONDS, holdDuration); 31 | } 32 | 33 | @Override 34 | public RLock lock(String lockName, TimeUnit unit, int holdDuration) { 35 | RLock lock = this.getRedissonLockByName(lockName); 36 | lock.lock(holdDuration, unit); 37 | 38 | return lock; 39 | } 40 | 41 | @Override 42 | public boolean tryLock(String lockName, int waitDuration, int holdDuration) { 43 | return this.tryLock(lockName, TimeUnit.SECONDS, waitDuration, holdDuration); 44 | } 45 | 46 | @Override 47 | public boolean tryLock(String lockName, TimeUnit unit, int waitDuration, int holdDuration) { 48 | RLock lock = this.getRedissonLockByName(lockName); 49 | try { 50 | return lock.tryLock(waitDuration, holdDuration, unit); 51 | } catch (InterruptedException ex) { 52 | return false; 53 | } 54 | } 55 | 56 | @Override 57 | public void unlock(String lockName) { 58 | RLock lock = this.getRedissonLockByName(lockName); 59 | this.unlock(lock); 60 | } 61 | 62 | @Override 63 | public void unlock(RLock lock) { 64 | lock.unlock(); 65 | } 66 | 67 | @Override 68 | public void unlockSafely(String lockName) { 69 | RLock lock = this.getRedissonLockByName(lockName); 70 | this.unlockSafely(lock); 71 | } 72 | 73 | @Override 74 | public void unlockSafely(RLock lock) { 75 | if (lock == null) { 76 | return; 77 | } 78 | 79 | if (lock.isLocked() && lock.isHeldByCurrentThread()) { 80 | this.unlock(lock); 81 | } 82 | } 83 | 84 | @Override 85 | public void setLockKeyPrefix(String newPrefix) { 86 | LOCK_KEY_PREFIX.set(newPrefix); 87 | } 88 | 89 | private RLock getRedissonLockByName(String lockName) { 90 | return redissonClient.getLock(LOCK_KEY_PREFIX.get() + lockName); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/model/FormattedRedisKey.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.redis.model; 2 | 3 | import cc.uncarbon.framework.redis.enums.KeyTypeEnum; 4 | import cn.hutool.core.util.RandomUtil; 5 | 6 | /** 7 | * 已固定好键名的 Redis Key 8 | * @param key 已固定好的键名 9 | * @param keyType 键类型 10 | * @param valueClass 值类型【@deprecated 并没有实际用途,未来移除】 11 | * @param durationSeconds 有效时长,永久为-1,可通过 hasExpiration() 方法判断 12 | * @param 13 | */ 14 | public record FormattedRedisKey( 15 | String key, 16 | KeyTypeEnum keyType, 17 | Class valueClass, 18 | long durationSeconds 19 | ) { 20 | /** 21 | * 是否已设置有效时长 22 | */ 23 | public boolean hasExpiration() { 24 | return this.durationSeconds > 0; 25 | } 26 | 27 | /** 28 | * 计算随机有效时长,有助于缓解缓存击穿、缓存雪崩 29 | * 30 | * @param minRatio 最小比例因子,如0.9表示90% 31 | * @param maxRatio 最大比例因子,如1.1表示110% 32 | * @return 随机有效时长,单位=秒 33 | */ 34 | public long randomDurationSeconds(double minRatio, double maxRatio) { 35 | if (!hasExpiration()) { 36 | throw new IllegalArgumentException("no-expiration key CANNOT calculate randomDurationSeconds"); 37 | } 38 | return (long)(RandomUtil.randomDouble(this.durationSeconds * minRatio, this.durationSeconds * maxRatio)); 39 | } 40 | 41 | /** 42 | * @return durationSeconds的 int 形式 43 | * @throws ArithmeticException 需要注意数值溢出风险 44 | */ 45 | public int durationSecondsAsInt() throws ArithmeticException { 46 | return Math.toIntExact(this.durationSeconds); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/template/RedisDistributedLockTemplate.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.redis.template; 2 | 3 | import jakarta.annotation.Nonnull; 4 | import jakarta.annotation.Nullable; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.function.Consumer; 8 | 9 | /** 10 | * Redis分布式可重入锁模板类 11 | * 编程式获取分布式锁,模板化分布式锁的使用方式 12 | * 13 | * @author Uncarbon 14 | */ 15 | public interface RedisDistributedLockTemplate { 16 | 17 | /** 18 | * 获取分布式锁并执行锁内代码,等待时长无限 19 | * 20 | * @param lockName 锁名称 21 | * @param unit 时间单位 22 | * @param holdDuration 锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 23 | * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用 24 | */ 25 | void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration, 26 | @Nonnull Runnable lockedRunnable); 27 | 28 | /** 29 | * 获取分布式锁并执行锁内代码,等待时长无限 30 | * 31 | * @param lockName 锁名称 32 | * @param unit 时间单位 33 | * @param holdDuration 锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 34 | * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用 35 | * @param exceptionHandler (可选)锁内代码异常处理过程 36 | */ 37 | void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration, 38 | @Nonnull Runnable lockedRunnable, @Nullable Consumer exceptionHandler); 39 | 40 | /** 41 | * 获取分布式锁并执行锁内代码 42 | * 【注意】务必考虑到锁内代码有抛出异常的可能 43 | * 44 | * @param lockName 锁名称 45 | * @param unit 时间单位 46 | * @param waitDuration 最大等待时长 47 | * @param holdDuration 锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 48 | * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用 49 | * @return 是否实际拿到锁 50 | */ 51 | boolean executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int waitDuration, int holdDuration, 52 | @Nonnull Runnable lockedRunnable); 53 | 54 | /** 55 | * 获取分布式锁并执行锁内代码 56 | * 可手动指定锁内代码异常处理过程 57 | * 58 | * @param lockName 锁名称 59 | * @param unit 时间单位 60 | * @param waitDuration 最大等待时长 61 | * @param holdDuration 锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期 62 | * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用 63 | * @param exceptionHandler (可选)锁内代码异常处理过程 64 | * @return 是否实际拿到锁 65 | */ 66 | boolean executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int waitDuration, int holdDuration, 67 | @Nonnull Runnable lockedRunnable, @Nullable Consumer exceptionHandler); 68 | } 69 | -------------------------------------------------------------------------------- /helio-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.redis.config.HelioRedisAutoConfiguration 2 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-rocketmq-aliyun 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | cc.uncarbon.framework 30 | helio-starter-aop 31 | 32 | 33 | 34 | 35 | com.aliyun.openservices 36 | ons-client 37 | 38 | 39 | 40 | com.fasterxml.jackson.core 41 | jackson-databind 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/CommonMessage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.annotation; 18 | 19 | import cc.uncarbon.framework.rocketmq.core.producer.DefaultSendCallback; 20 | import cc.uncarbon.framework.rocketmq.core.producer.MessageSendType; 21 | import com.aliyun.openservices.ons.api.SendCallback; 22 | 23 | import java.lang.annotation.*; 24 | 25 | /** 26 | * ClassName: CommonMessage 27 | * Description: 28 | * date: 2019/5/3 11:16 29 | * 30 | * @author ThierrySquirrel 31 | * @since JDK 1.8 32 | */ 33 | @Target(ElementType.METHOD) 34 | @Retention(RetentionPolicy.RUNTIME) 35 | @Inherited 36 | @Documented 37 | public @interface CommonMessage { 38 | /** 39 | * Message 所属的 Topic 40 | * 41 | * @return String 42 | */ 43 | String topic() default ""; 44 | 45 | /** 46 | * 订阅指定 Topic 下的 Tags: 47 | * 1. * 表示订阅所有消息 48 | * 2. TagA || TagB || TagC 表示订阅 TagA 或 TagB 或 TagC 的消息 49 | * 50 | * @return String 51 | */ 52 | String tag() default "*"; 53 | 54 | /** 55 | * 消息发送类型 默认异步 56 | * 57 | * @return MessageSendType 58 | */ 59 | MessageSendType messageSendType() default MessageSendType.SEND_ASYNC; 60 | 61 | /** 62 | * 自定义SendCallback类 63 | * 64 | * @return callback 65 | */ 66 | Class callback() default DefaultSendCallback.class; 67 | } 68 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/MessageListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.annotation; 18 | 19 | import org.springframework.stereotype.Component; 20 | 21 | import java.lang.annotation.*; 22 | 23 | /** 24 | * ClassName: MessageListener 25 | * Description: 26 | * date: 2019/4/26 22:37 27 | * 28 | * @author ThierrySquirrel 29 | * @since JDK 1.8 30 | */ 31 | @Target(ElementType.METHOD) 32 | @Retention(RetentionPolicy.RUNTIME) 33 | @Inherited 34 | @Documented 35 | @Component 36 | public @interface MessageListener { 37 | /** 38 | * Message 所属的 Topic 39 | * 40 | * @return String 41 | */ 42 | String topic() default ""; 43 | 44 | /** 45 | * 订阅指定 Topic 下的 Tags: 46 | * 1. * 表示订阅所有消息 47 | * 2. TagA || TagB || TagC 表示订阅 TagA 或 TagB 或 TagC 的消息 48 | * 49 | * @return String 50 | */ 51 | String tag() default "*"; 52 | 53 | /** 54 | * 是否为顺序消息 55 | * 56 | * @return Boolean 57 | */ 58 | 59 | boolean orderConsumer() default false; 60 | 61 | /** 62 | * 是否为批量消息 63 | * 64 | * @return Boolean 65 | */ 66 | boolean batchConsumer() default false; 67 | 68 | /** 69 | * 设置批量消费最大消息数量,当指定Topic的消息数量已经攒够128条,SDK立即执行回调进行消费.默认值:32,取值范围:1~1024. 70 | */ 71 | int consumeMessageBatchMaxSize() default 32; 72 | 73 | /** 74 | * 设置批量消费最大等待时长,当等待时间达到10秒,SDK立即执行回调进行消费.默认值:0,取值范围:0~450,单位:秒. 75 | */ 76 | int batchConsumeMaxAwaitDurationInSeconds() default 0; 77 | } 78 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/OrderMessage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.annotation; 18 | 19 | 20 | import java.lang.annotation.*; 21 | 22 | /** 23 | * ClassName: OrderMessage 24 | * Description: 25 | * date: 2019/4/27 21:36 26 | * 27 | * @author ThierrySquirrel 28 | * @since JDK 1.8 29 | */ 30 | @Target(ElementType.METHOD) 31 | @Retention(RetentionPolicy.RUNTIME) 32 | @Inherited 33 | @Documented 34 | public @interface OrderMessage { 35 | /** 36 | * Message 所属的 Topic 37 | * 38 | * @return String 39 | */ 40 | String topic() default ""; 41 | 42 | /** 43 | * 订阅指定 Topic 下的 Tags: 44 | * 1. * 表示订阅所有消息 45 | * 2. TagA || TagB || TagC 表示订阅 TagA 或 TagB 或 TagC 的消息 46 | * 47 | * @return String 48 | */ 49 | String tag() default "*"; 50 | } 51 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/RocketListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.annotation; 18 | 19 | import com.aliyun.openservices.ons.api.PropertyValueConst; 20 | import java.lang.annotation.Documented; 21 | import java.lang.annotation.ElementType; 22 | import java.lang.annotation.Inherited; 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.RetentionPolicy; 25 | import java.lang.annotation.Target; 26 | import org.springframework.stereotype.Component; 27 | 28 | /** 29 | * ClassName: RocketListener 30 | * Description: 31 | * date: 2019/4/26 21:35 32 | * 33 | * @author ThierrySquirrel 34 | * @since JDK 1.8 35 | */ 36 | @Target(ElementType.TYPE) 37 | @Retention(RetentionPolicy.RUNTIME) 38 | @Inherited 39 | @Documented 40 | @Component 41 | public @interface RocketListener { 42 | /** 43 | * 您在控制台创建的 Group ID 44 | * 45 | * @return String 46 | */ 47 | String groupID() default ""; 48 | 49 | /** 50 | * 消费模式,默认集群消费 51 | * 52 | * @return String 53 | */ 54 | String messageModel() default PropertyValueConst.CLUSTERING; 55 | 56 | /** 57 | * 消费线程数量,特定需求可以单独指定,默认同配置文件 58 | * 59 | * @return String 60 | */ 61 | String consumeThreadNums(); 62 | 63 | /** 64 | * 一次最大拉取消费数量,特定需求可以单独指定,默认同配置文件 65 | * 66 | * @return String 67 | */ 68 | String consumeBatchSize(); 69 | 70 | } 71 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/RocketMessage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.annotation; 18 | 19 | import org.springframework.stereotype.Component; 20 | 21 | import java.lang.annotation.*; 22 | 23 | /** 24 | * ClassName: RocketMessage 25 | * Description: 26 | * date: 2019/4/26 21:36 27 | * 28 | * @author ThierrySquirrel 29 | * @since JDK 1.8 30 | */ 31 | @Target(ElementType.TYPE) 32 | @Retention(RetentionPolicy.RUNTIME) 33 | @Inherited 34 | @Component 35 | @Documented 36 | public @interface RocketMessage { 37 | /** 38 | * 您在控制台创建的 Group ID 39 | * 40 | * @return String 41 | */ 42 | String groupID() default ""; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/ShardingKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cc.uncarbon.framework.rocketmq.annotation; 17 | 18 | import java.lang.annotation.*; 19 | 20 | /** 21 | * Classname: ShardingKey 22 | * Description: 23 | * Date: 2021/11/3 19:57 24 | * 25 | * @author ThierrySquirrel 26 | * @since JDK 11 27 | */ 28 | @Target({ElementType.PARAMETER}) 29 | @Retention(RetentionPolicy.RUNTIME) 30 | @Documented 31 | @Inherited 32 | public @interface ShardingKey { 33 | } 34 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/StartDeliverTime.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.annotation; 18 | 19 | import java.lang.annotation.*; 20 | 21 | /** 22 | * ClassName: StartDeliverTime 23 | * Description: 24 | * date: 2020/1/29 19:05 25 | * 26 | * @author ThierrySquirrel 27 | * @since JDK 1.8 28 | */ 29 | @Target({ElementType.PARAMETER}) 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Documented 32 | @Inherited 33 | public @interface StartDeliverTime { 34 | } 35 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/annotation/TransactionMessage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.annotation; 18 | 19 | import cc.uncarbon.framework.rocketmq.core.producer.DefaultLocalTransactionChecker; 20 | import cc.uncarbon.framework.rocketmq.core.producer.DefaultLocalTransactionExecutor; 21 | import com.aliyun.openservices.ons.api.transaction.LocalTransactionChecker; 22 | import com.aliyun.openservices.ons.api.transaction.LocalTransactionExecuter; 23 | import com.aliyun.openservices.ons.api.transaction.TransactionStatus; 24 | 25 | import java.lang.annotation.*; 26 | 27 | /** 28 | * ClassName: TransactionMessage 29 | * Description: 30 | * date: 2019/4/27 21:39 31 | * 32 | * @author ThierrySquirrel 33 | * @since JDK 1.8 34 | */ 35 | @Target(ElementType.METHOD) 36 | @Retention(RetentionPolicy.RUNTIME) 37 | @Inherited 38 | @Documented 39 | public @interface TransactionMessage { 40 | /** 41 | * Message 所属的 Topic 42 | * 43 | * @return String 44 | */ 45 | String topic() default ""; 46 | 47 | /** 48 | * 订阅指定 Topic 下的 Tags: 49 | * 1. * 表示订阅所有消息 50 | * 2. TagA || TagB || TagC 表示订阅 TagA 或 TagB 或 TagC 的消息 51 | * 52 | * @return String 53 | */ 54 | String tag() default "*"; 55 | 56 | /** 57 | * 触发消息回查后的操作:默认提交(最终一致性) 58 | * 未收到消息确认时,会触发消息回查 59 | * 例如消费长时间未发送 60 | * 注意,正常情况下不会触发消息回查 61 | * 62 | * @return TransactionStatus 63 | */ 64 | TransactionStatus transactionStatus() default TransactionStatus.CommitTransaction; 65 | 66 | /** 67 | * 自定义LocalTransactionChecker类 68 | * 69 | * @return checker类对象 70 | */ 71 | Class checker() default DefaultLocalTransactionChecker.class; 72 | 73 | /** 74 | * 自定义LocalTransactionExecutor类 75 | * 76 | * @return executor类对象 77 | */ 78 | Class executor() default DefaultLocalTransactionExecutor.class; 79 | } 80 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/container/RocketProducerContainer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.container; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 20 | import cc.uncarbon.framework.rocketmq.core.factory.ThreadPoolFactory; 21 | import cc.uncarbon.framework.rocketmq.core.strategy.RocketConsumerStrategy; 22 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 23 | import jakarta.annotation.PostConstruct; 24 | import lombok.NonNull; 25 | import org.springframework.context.ApplicationContext; 26 | import org.springframework.context.ApplicationContextAware; 27 | 28 | import java.util.Map; 29 | import java.util.concurrent.ThreadPoolExecutor; 30 | 31 | /** 32 | * ClassName: RocketProducerContainer 33 | * Description: 34 | * date: 2019/5/3 11:29 35 | * 36 | * @author ThierrySquirrel 37 | * @since JDK 1.8 38 | */ 39 | public class RocketProducerContainer implements ApplicationContextAware { 40 | 41 | private ApplicationContext applicationContext; 42 | 43 | private final AliyunRocketProperties rocketProperties; 44 | 45 | private final Map consumerContainer; 46 | 47 | 48 | public RocketProducerContainer(Map consumerContainer, AliyunRocketProperties rocketProperties) { 49 | this.consumerContainer = consumerContainer; 50 | this.rocketProperties = rocketProperties; 51 | } 52 | 53 | @PostConstruct 54 | public void initialize() { 55 | // 创建临时线程池 56 | ThreadPoolExecutor threadPoolExecutor = ThreadPoolFactory.createProducerThreadPoolExecutor(rocketProperties); 57 | // 扫描标记 @RocketMessage 注解的类,再将其中标记了 @CommonMessage/@OrderMessage/@TransactionMessage 的方法包装一下 58 | // 依次注册到阿里云SDK 59 | applicationContext.getBeansWithAnnotation(RocketMessage.class) 60 | .forEach((beanName, bean) -> RocketConsumerStrategy.putProducer(threadPoolExecutor, consumerContainer, bean, rocketProperties, applicationContext)); 61 | // 销毁临时线程池 62 | threadPoolExecutor.shutdown(); 63 | } 64 | 65 | @Override 66 | public void setApplicationContext(@NonNull ApplicationContext applicationContext) { 67 | this.applicationContext = applicationContext; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/AbstractRocketListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.consumer; 18 | 19 | import cc.uncarbon.framework.rocketmq.core.factory.execution.MethodFactoryExecution; 20 | import lombok.Data; 21 | import lombok.extern.slf4j.Slf4j; 22 | 23 | /** 24 | * ClassName: AbstractRocketListener 25 | * Description: 26 | * date: 2019/4/27 17:07 27 | * 28 | * @author ThierrySquirrel 29 | * @since JDK 1.8 30 | */ 31 | @Data 32 | @Slf4j 33 | public abstract class AbstractRocketListener { 34 | private MethodFactoryExecution methodFactoryExecution; 35 | 36 | protected AbstractRocketListener(MethodFactoryExecution methodFactoryExecution) { 37 | this.methodFactoryExecution = methodFactoryExecution; 38 | } 39 | 40 | public void printErrorLog() { 41 | log.error("Consumer agent failed!bean:{},method:{}", 42 | methodFactoryExecution.getBean(), 43 | methodFactoryExecution.getMethod()); 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/DefaultBatchMessageListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cc.uncarbon.framework.rocketmq.core.consumer; 17 | 18 | import cc.uncarbon.framework.rocketmq.core.factory.execution.MethodFactoryExecution; 19 | import cc.uncarbon.framework.rocketmq.exception.RocketException; 20 | import com.aliyun.openservices.ons.api.Action; 21 | import com.aliyun.openservices.ons.api.ConsumeContext; 22 | import com.aliyun.openservices.ons.api.Message; 23 | import com.aliyun.openservices.ons.api.batch.BatchMessageListener; 24 | import lombok.extern.slf4j.Slf4j; 25 | 26 | import java.util.List; 27 | 28 | /** 29 | * ClassName: DefaultBatchMessageListener 30 | * Description: 31 | * date: 2020/12/8 9:08 32 | * 33 | * @author ThierrySquirrel 34 | * @since JDK 1.8 35 | */ 36 | @Slf4j 37 | public class DefaultBatchMessageListener extends AbstractRocketListener implements BatchMessageListener { 38 | public DefaultBatchMessageListener(MethodFactoryExecution methodFactoryExecution) { 39 | super(methodFactoryExecution); 40 | } 41 | 42 | /** 43 | * 批量消费消息接口,由应用来实现
44 | * 需要注意网络抖动等不稳定的情形可能会带来消息重复,对重复消息敏感的业务可对消息做幂等处理 45 | * 46 | * @param messages 一批消息 47 | * @param context 消费上下文 48 | * @return {@link Action} 消费结果,如果应用抛出异常或者返回Null等价于返回Action.ReconsumeLater 49 | * @see 如何做到消费幂等 50 | */ 51 | @Override 52 | public Action consume(List messages, ConsumeContext context) { 53 | log.info(">>>>messageList:{}>>>>", messages); 54 | try { 55 | for (Message message : messages) { 56 | super.getMethodFactoryExecution().methodExecution(message.getBody()); 57 | } 58 | } catch (RocketException e) { 59 | super.printErrorLog(); 60 | return Action.ReconsumeLater; 61 | } 62 | return Action.CommitMessage; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/DefaultMessageListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.consumer; 18 | 19 | import cc.uncarbon.framework.rocketmq.core.factory.execution.MethodFactoryExecution; 20 | import cc.uncarbon.framework.rocketmq.exception.RocketException; 21 | import com.aliyun.openservices.ons.api.Action; 22 | import com.aliyun.openservices.ons.api.ConsumeContext; 23 | import com.aliyun.openservices.ons.api.Message; 24 | import com.aliyun.openservices.ons.api.MessageListener; 25 | import lombok.extern.slf4j.Slf4j; 26 | 27 | /** 28 | * ClassName: DefaultMessageListener 29 | * Description: 30 | * date: 2019/4/27 17:05 31 | * 32 | * @author ThierrySquirrel 33 | * @since JDK 1.8 34 | */ 35 | @Slf4j 36 | public class DefaultMessageListener extends AbstractRocketListener implements MessageListener { 37 | 38 | public DefaultMessageListener(MethodFactoryExecution methodFactoryExecution) { 39 | super(methodFactoryExecution); 40 | } 41 | 42 | /** 43 | * 消费消息接口,由应用来实现
44 | * 网络抖动等不稳定的情形可能会带来消息重复,对重复消息敏感的业务可对消息做幂等处理 45 | * 46 | * @param message 消息 47 | * @param context 消费上下文 48 | * @return 消费结果,如果应用抛出异常或者返回Null等价于返回Action.ReconsumeLater 49 | * @see 如何做到消费幂等 50 | */ 51 | @Override 52 | public Action consume(Message message, ConsumeContext context) { 53 | log.info(">>>>message:{}>>>>", message); 54 | try { 55 | super.getMethodFactoryExecution().methodExecution(message.getBody()); 56 | } catch (RocketException e) { 57 | super.printErrorLog(); 58 | return Action.ReconsumeLater; 59 | } 60 | return Action.CommitMessage; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/DefaultMessageOrderListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.consumer; 18 | 19 | import cc.uncarbon.framework.rocketmq.core.factory.execution.MethodFactoryExecution; 20 | import cc.uncarbon.framework.rocketmq.exception.RocketException; 21 | import com.aliyun.openservices.ons.api.Message; 22 | import com.aliyun.openservices.ons.api.order.ConsumeOrderContext; 23 | import com.aliyun.openservices.ons.api.order.MessageOrderListener; 24 | import com.aliyun.openservices.ons.api.order.OrderAction; 25 | import lombok.extern.slf4j.Slf4j; 26 | 27 | /** 28 | * ClassName: DefaultMessageOrderListener 29 | * Description: 30 | * date: 2019/4/26 23:16 31 | * 32 | * @author ThierrySquirrel 33 | * @since JDK 1.8 34 | */ 35 | 36 | @Slf4j 37 | public class DefaultMessageOrderListener extends AbstractRocketListener implements MessageOrderListener { 38 | 39 | 40 | public DefaultMessageOrderListener(MethodFactoryExecution methodFactoryExecution) { 41 | super(methodFactoryExecution); 42 | } 43 | 44 | /** 45 | * 消费消息接口,由应用来实现
46 | * 需要注意网络抖动等不稳定的情形可能会带来消息重复,对重复消息敏感的业务可对消息做幂等处理 47 | * 48 | * @param message 消息 49 | * @param context 消费上下文 50 | * @return {@link OrderAction} 消费结果,如果应用抛出异常或者返回Null等价于返回Action.ReconsumeLater 51 | * @see 如何做到消费幂等 52 | */ 53 | @Override 54 | public OrderAction consume(Message message, ConsumeOrderContext context) { 55 | log.info(">>>> Order message:{}>>>>", message); 56 | try { 57 | super.getMethodFactoryExecution().methodExecution(message.getBody()); 58 | } catch (RocketException e) { 59 | super.printErrorLog(); 60 | return OrderAction.Suspend; 61 | } 62 | return OrderAction.Success; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ConsumerFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import com.aliyun.openservices.ons.api.Consumer; 20 | import com.aliyun.openservices.ons.api.ONSFactory; 21 | import com.aliyun.openservices.ons.api.batch.BatchConsumer; 22 | import com.aliyun.openservices.ons.api.order.OrderConsumer; 23 | 24 | import java.util.Properties; 25 | 26 | 27 | /** 28 | * ClassName: ConsumerFactory 29 | * Description: 30 | * date: 2019/4/27 15:55 31 | * 32 | * @author ThierrySquirrel 33 | * @since JDK 1.8 34 | */ 35 | public final class ConsumerFactory { 36 | private ConsumerFactory() { 37 | } 38 | 39 | public static Consumer createConsumer(Properties properties) { 40 | return ONSFactory.createConsumer(properties); 41 | } 42 | 43 | 44 | public static OrderConsumer createOrderConsumer(Properties properties) { 45 | return ONSFactory.createOrderedConsumer(properties); 46 | } 47 | 48 | public static BatchConsumer createBatchConsumer(Properties properties) { 49 | return ONSFactory.createBatchConsumer(properties); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ConsumerPropertiesFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.RocketListener; 20 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 21 | import com.aliyun.openservices.ons.api.PropertyKeyConst; 22 | import java.util.Objects; 23 | import java.util.Properties; 24 | 25 | /** 26 | * ClassName: ConsumerPropertiesFactory 27 | * Description: 28 | * date: 2019/4/27 15:37 29 | * 30 | * @author ThierrySquirrel 31 | * @since JDK 1.8 32 | */ 33 | public final class ConsumerPropertiesFactory { 34 | private ConsumerPropertiesFactory() { 35 | } 36 | 37 | public static Properties createConsumerProperties(AliyunRocketProperties rocketProperties, 38 | RocketListener rocketListener) { 39 | 40 | Properties properties = PropertiesFactory.createProperties(rocketProperties); 41 | 42 | properties.put(PropertyKeyConst.GROUP_ID, rocketListener.groupID()); 43 | properties.put(PropertyKeyConst.MessageModel, rocketListener.messageModel()); 44 | properties.put(PropertyKeyConst.ConsumeThreadNums, 45 | // 消费线程数量,特定需求可以单独指定 46 | Objects.nonNull(rocketListener.consumeThreadNums()) ? rocketListener.consumeThreadNums() 47 | : rocketProperties.getConsumeThreadNums()); 48 | properties.put(PropertyKeyConst.MaxReconsumeTimes, rocketProperties.getMaxReconsumeTimes()); 49 | properties.put(PropertyKeyConst.ConsumeTimeout, rocketProperties.getConsumeTimeout()); 50 | 51 | if (Objects.nonNull(rocketListener.consumeBatchSize())) { 52 | // 一次最大拉取消费数量,特定需求可以单独指定 53 | properties.put(PropertyKeyConst.MAX_BATCH_MESSAGE_COUNT, rocketListener.consumeBatchSize()); 54 | } 55 | 56 | return properties; 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/MessageFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.CommonMessage; 20 | import cc.uncarbon.framework.rocketmq.annotation.OrderMessage; 21 | import cc.uncarbon.framework.rocketmq.annotation.TransactionMessage; 22 | import com.aliyun.openservices.ons.api.Message; 23 | 24 | /** 25 | * ClassName: MessageFactory 26 | * Description: 27 | * date: 2019/4/28 21:55 28 | * 29 | * @author ThierrySquirrel 30 | * @since JDK 1.8 31 | */ 32 | public final class MessageFactory { 33 | private MessageFactory() { 34 | } 35 | 36 | public static Message createMessage(CommonMessage commonMessage, byte[] body) { 37 | return new Message(commonMessage.topic(), 38 | commonMessage.tag(), 39 | body); 40 | } 41 | 42 | public static Message createMessage(OrderMessage orderMessage, byte[] body) { 43 | return new Message(orderMessage.topic(), 44 | orderMessage.tag(), 45 | body); 46 | } 47 | 48 | public static Message createMessage(TransactionMessage transactionMessage, byte[] body) { 49 | return new Message(transactionMessage.topic(), 50 | transactionMessage.tag(), 51 | body); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/MethodFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import java.lang.reflect.Method; 20 | 21 | /** 22 | * ClassName: MethodFactory 23 | * Description: 24 | * date: 2019/4/27 16:13 25 | * 26 | * @author ThierrySquirrel 27 | * @since JDK 1.8 28 | */ 29 | 30 | public final class MethodFactory { 31 | private MethodFactory() { 32 | } 33 | 34 | public static Class getMethodParameter(Method method) { 35 | return method.getParameterTypes()[0]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 20 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 21 | import com.aliyun.openservices.ons.api.ONSFactory; 22 | import com.aliyun.openservices.ons.api.Producer; 23 | import com.aliyun.openservices.ons.api.PropertyKeyConst; 24 | import com.aliyun.openservices.ons.api.order.OrderProducer; 25 | import com.aliyun.openservices.ons.api.transaction.LocalTransactionChecker; 26 | import com.aliyun.openservices.ons.api.transaction.TransactionProducer; 27 | 28 | import java.util.Properties; 29 | 30 | /** 31 | * ClassName: ProducerFactory 32 | * Description: 33 | * date: 2019/4/28 21:35 34 | * 35 | * @author ThierrySquirrel 36 | * @since JDK 1.8 37 | */ 38 | public final class ProducerFactory { 39 | private ProducerFactory() { 40 | } 41 | 42 | public static Producer createProducer(RocketMessage rocketMessage, AliyunRocketProperties rocketProperties) { 43 | Properties properties = ProducerPropertiesFactory.createProducerProperties(rocketMessage, rocketProperties); 44 | return ONSFactory.createProducer(properties); 45 | } 46 | 47 | public static OrderProducer createOrderProducer(RocketMessage rocketMessage, AliyunRocketProperties rocketProperties) { 48 | Properties properties = ProducerPropertiesFactory.createProducerProperties(rocketMessage, rocketProperties); 49 | return ONSFactory.createOrderProducer(properties); 50 | } 51 | 52 | public static TransactionProducer createTransactionProducer(RocketMessage rocketMessage, AliyunRocketProperties rocketProperties, LocalTransactionChecker localTransactionChecker) { 53 | Properties properties = ProducerPropertiesFactory.createProducerProperties(rocketMessage, rocketProperties); 54 | properties.put(PropertyKeyConst.CheckImmunityTimeInSeconds, rocketProperties.getCheckImmunityTimeInSeconds()); 55 | return ONSFactory.createTransactionProducer(properties, localTransactionChecker); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerPropertiesFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 20 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 21 | import com.aliyun.openservices.ons.api.PropertyKeyConst; 22 | 23 | import java.util.Properties; 24 | 25 | /** 26 | * ClassName: ProducerPropertiesFactory 27 | * Description: 28 | * date: 2019/4/28 21:21 29 | * 30 | * @author ThierrySquirrel 31 | * @since JDK 1.8 32 | */ 33 | public final class ProducerPropertiesFactory { 34 | private ProducerPropertiesFactory() { 35 | } 36 | 37 | public static Properties createProducerProperties(RocketMessage rockerMessage, AliyunRocketProperties rocketProperties) { 38 | Properties properties = PropertiesFactory.createProperties(rocketProperties); 39 | properties.put(PropertyKeyConst.SendMsgTimeoutMillis, rocketProperties.getSendMsgTimeoutMillis()); 40 | properties.put(PropertyKeyConst.GROUP_ID, rockerMessage.groupID()); 41 | return properties; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/PropertiesFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 20 | import com.aliyun.openservices.ons.api.PropertyKeyConst; 21 | 22 | import java.util.Properties; 23 | 24 | /** 25 | * ClassName: PropertiesFactory 26 | * Description: 27 | * date: 2019/4/27 20:26 28 | * 29 | * @author ThierrySquirrel 30 | * @since JDK 1.8 31 | */ 32 | public final class PropertiesFactory { 33 | private PropertiesFactory() { 34 | } 35 | 36 | public static Properties createProperties(AliyunRocketProperties rocketProperties) { 37 | Properties properties = new Properties(); 38 | properties.put(PropertyKeyConst.NAMESRV_ADDR, rocketProperties.getNameSrvAddr()); 39 | properties.put(PropertyKeyConst.AccessKey, rocketProperties.getAccessKey()); 40 | properties.put(PropertyKeyConst.SecretKey, rocketProperties.getSecretKey()); 41 | properties.put(PropertyKeyConst.OnsChannel, rocketProperties.getOnsChannel()); 42 | 43 | return properties; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/SendMessageFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.CommonMessage; 20 | import cc.uncarbon.framework.rocketmq.annotation.OrderMessage; 21 | import cc.uncarbon.framework.rocketmq.annotation.TransactionMessage; 22 | import cc.uncarbon.framework.rocketmq.core.strategy.SendMessageStrategy; 23 | import cc.uncarbon.framework.rocketmq.core.utils.ApplicationContextUtils; 24 | import com.aliyun.openservices.ons.api.Message; 25 | import com.aliyun.openservices.ons.api.Producer; 26 | import com.aliyun.openservices.ons.api.order.OrderProducer; 27 | import com.aliyun.openservices.ons.api.transaction.TransactionProducer; 28 | import org.springframework.context.ApplicationContext; 29 | 30 | 31 | /** 32 | * ClassName: SendMessageFactory 33 | * Description: 34 | * date: 2019/4/29 21:52 35 | * 36 | * @author ThierrySquirrel 37 | * @since JDK 1.8 38 | */ 39 | public final class SendMessageFactory { 40 | private SendMessageFactory() { 41 | } 42 | 43 | public static void sendMessage(Long startDeliverTime, Producer producer, CommonMessage commonMessage, byte[] bytes, ApplicationContext applicationContext) { 44 | Message message = MessageFactory.createMessage(commonMessage, bytes); 45 | if (null != startDeliverTime) { 46 | message.setStartDeliverTime(startDeliverTime); 47 | } 48 | SendMessageStrategy.send(commonMessage, producer, message, applicationContext); 49 | } 50 | 51 | public static void sendMessage(OrderProducer orderProducer, OrderMessage orderMessage, byte[] bytes, String shardingKeyFactory) { 52 | Message message = MessageFactory.createMessage(orderMessage, bytes); 53 | orderProducer.send(message, shardingKeyFactory); 54 | } 55 | 56 | public static void sendMessage(TransactionProducer transactionProducer, TransactionMessage transactionMessage, byte[] bytes, ApplicationContext applicationContext) { 57 | Message message = MessageFactory.createMessage(transactionMessage, bytes); 58 | transactionProducer.send(message, ApplicationContextUtils.getLocalTransactionExecuter(applicationContext, transactionMessage.executor()), null); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ShardingKeyFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cc.uncarbon.framework.rocketmq.core.factory; 17 | 18 | import cc.uncarbon.framework.rocketmq.annotation.ShardingKey; 19 | import cn.hutool.core.util.ObjectUtil; 20 | 21 | import java.lang.reflect.Parameter; 22 | 23 | /** 24 | * Classname: ShardingKeyFactory 25 | * Description: 26 | * Date: 2021/11/3 20:00 27 | * 28 | * @author ThierrySquirrel 29 | * @since JDK 11 30 | */ 31 | public final class ShardingKeyFactory { 32 | private ShardingKeyFactory() { 33 | } 34 | 35 | public static String getShardingKeyFactory(Object[] args, Parameter[] params) { 36 | for (int i = 0; i < args.length; i++) { 37 | ShardingKey annotation = params[i].getAnnotation(ShardingKey.class); 38 | if (ObjectUtil.isNotEmpty(annotation)) { 39 | return (String) args[i]; 40 | } 41 | } 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/StartDeliverTimeFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.StartDeliverTime; 20 | import cn.hutool.core.util.ObjectUtil; 21 | 22 | import java.lang.reflect.Parameter; 23 | 24 | /** 25 | * ClassName: StartDeliverTimeFactory 26 | * Description: 27 | * date: 2020/1/29 19:07 28 | * 29 | * @author ThierrySquirrel 30 | * @since JDK 1.8 31 | */ 32 | public final class StartDeliverTimeFactory { 33 | 34 | private StartDeliverTimeFactory() { 35 | } 36 | 37 | public static Long getStartDeliverTime(Object[] args, Parameter[] params) { 38 | for (int i = 0; i < args.length; i++) { 39 | StartDeliverTime annotation = params[i].getAnnotation(StartDeliverTime.class); 40 | if (ObjectUtil.isNotEmpty(annotation)) { 41 | return (Long) args[i]; 42 | } 43 | } 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/MethodFactoryExecution.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory.execution; 18 | 19 | import cc.uncarbon.framework.rocketmq.core.factory.MethodFactory; 20 | import cc.uncarbon.framework.rocketmq.core.serializer.RocketSerializer; 21 | import cc.uncarbon.framework.rocketmq.exception.RocketException; 22 | import lombok.AllArgsConstructor; 23 | import lombok.Data; 24 | import lombok.extern.slf4j.Slf4j; 25 | 26 | import java.lang.reflect.Method; 27 | 28 | /** 29 | * ClassName: MethodFactoryExecution 30 | * Description: 31 | * date: 2019/4/27 16:26 32 | * 33 | * @author ThierrySquirrel 34 | * @since JDK 1.8 35 | */ 36 | @Slf4j 37 | @AllArgsConstructor 38 | @Data 39 | public class MethodFactoryExecution { 40 | private Object bean; 41 | private Method method; 42 | private RocketSerializer rocketSerializer; 43 | 44 | public void methodExecution(byte[] message) throws RocketException { 45 | try { 46 | Class methodParameter = MethodFactory.getMethodParameter(method); 47 | Object methodParameterBean = rocketSerializer.deSerialize(message, methodParameter); 48 | method.invoke(bean, methodParameterBean); 49 | } catch (Exception e) { 50 | throw new RocketException(e); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/ProducerFactoryExecution.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory.execution; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 20 | import cc.uncarbon.framework.rocketmq.core.strategy.PutProducerStrategy; 21 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 22 | import cc.uncarbon.framework.rocketmq.thread.AbstractProducerThread; 23 | import org.springframework.context.ApplicationContext; 24 | 25 | import java.util.Map; 26 | 27 | /** 28 | * ClassName: ProducerFactoryExecution 29 | * Description: 30 | * date: 2019/5/3 13:25 31 | * 32 | * @author ThierrySquirrel 33 | * @since JDK 1.8 34 | */ 35 | public class ProducerFactoryExecution extends AbstractProducerThread { 36 | 37 | public ProducerFactoryExecution(Map producerConsumer, RocketMessage rocketMessage, Object bean, 38 | AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) { 39 | super(producerConsumer, rocketMessage, bean, rocketProperties, applicationContext); 40 | } 41 | 42 | /** 43 | * 开始向容器装填 44 | * 45 | * @param producerConsumer producerConsumer 46 | * @param rocketMessage rocketMessage 47 | * @param bean bean 48 | * @param rocketProperties rocketProperties 49 | * @param applicationContext applicationContext 50 | */ 51 | @Override 52 | protected void statsPutProducer(Map producerConsumer, RocketMessage rocketMessage, Object bean, 53 | AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) { 54 | PutProducerStrategy.putProducer(producerConsumer, rocketMessage, bean, rocketProperties, applicationContext); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/SendMessageFactoryExecution.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory.execution; 18 | 19 | 20 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 21 | import cc.uncarbon.framework.rocketmq.core.strategy.ProducerStrategy; 22 | import cc.uncarbon.framework.rocketmq.thread.AbstractSendMessageThread; 23 | import lombok.extern.slf4j.Slf4j; 24 | import org.springframework.context.ApplicationContext; 25 | 26 | import java.util.Map; 27 | 28 | 29 | /** 30 | * ClassName: SendMessageFactoryExecution 31 | * Description: 32 | * date: 2019/4/28 21:31 33 | * 34 | * @author ThierrySquirrel 35 | * @since JDK 1.8 36 | */ 37 | @Slf4j 38 | public class SendMessageFactoryExecution extends AbstractSendMessageThread { 39 | 40 | public SendMessageFactoryExecution(Long startDeliverTime, String shardingKeyFactory, 41 | Map consumerContainer, RocketMessage rocketMessage, 42 | Object message, byte[] bytes, ApplicationContext applicationContext) { 43 | super(startDeliverTime, shardingKeyFactory, consumerContainer, rocketMessage, message, bytes, applicationContext); 44 | } 45 | 46 | /** 47 | * 开始发送消息 48 | * 49 | * @param startDeliverTime startDeliverTime 50 | * @param shardingKeyFactory shardingKeyFactory 51 | * @param consumerContainer consumerContainer 52 | * @param rocketMessage rocketMessage 53 | * @param message message 54 | * @param bytes bytes 55 | * @param applicationContext applicationContext 56 | */ 57 | @Override 58 | protected void statsSendMessage(Long startDeliverTime, String shardingKeyFactory, 59 | Map consumerContainer, RocketMessage rocketMessage, 60 | Object message, byte[] bytes, ApplicationContext applicationContext) { 61 | ProducerStrategy.statsSendMessage(startDeliverTime, shardingKeyFactory, consumerContainer, rocketMessage, message, bytes, applicationContext); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/ThreadPoolExecutorExecution.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.factory.execution; 18 | 19 | import lombok.experimental.UtilityClass; 20 | 21 | import java.util.concurrent.ThreadPoolExecutor; 22 | 23 | /** 24 | * ClassName: ThreadPoolExecutorExecution 25 | * Description: 26 | * date: 2019/4/27 19:55 27 | * 28 | * @author ThierrySquirrel 29 | * @since JDK 1.8 30 | */ 31 | @UtilityClass 32 | public class ThreadPoolExecutorExecution { 33 | 34 | public static void statsThread(ThreadPoolExecutor threadPoolExecutor, Runnable runnable) { 35 | threadPoolExecutor.execute(runnable); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/producer/DefaultLocalTransactionChecker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.producer; 18 | 19 | import com.aliyun.openservices.ons.api.Message; 20 | import com.aliyun.openservices.ons.api.transaction.LocalTransactionChecker; 21 | import com.aliyun.openservices.ons.api.transaction.TransactionStatus; 22 | import lombok.AllArgsConstructor; 23 | import lombok.Data; 24 | import lombok.extern.slf4j.Slf4j; 25 | 26 | /** 27 | * ClassName: DefaultLocalTransactionChecker 28 | * Description: 29 | * date: 2019/4/28 21:42 30 | * 31 | * @author ThierrySquirrel 32 | * @since JDK 1.8 33 | */ 34 | @Slf4j 35 | @AllArgsConstructor 36 | @Data 37 | public class DefaultLocalTransactionChecker implements LocalTransactionChecker { 38 | private TransactionStatus transactionStatus; 39 | 40 | /** 41 | * 回查本地事务,Broker回调Producer,将未结束的事务发给Producer,由Producer来再次决定事务是提交还是回滚 42 | * 43 | * @param msg 消息 44 | * @return {@link TransactionStatus} 事务状态, 包含提交事务、回滚事务、未知状态 45 | */ 46 | @Override 47 | public TransactionStatus check(Message msg) { 48 | log.info(">>>> Review local transactions message:{}>>>>", msg); 49 | return transactionStatus; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/producer/DefaultLocalTransactionExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.producer; 18 | 19 | import com.aliyun.openservices.ons.api.Message; 20 | import com.aliyun.openservices.ons.api.transaction.LocalTransactionExecuter; 21 | import com.aliyun.openservices.ons.api.transaction.TransactionStatus; 22 | import lombok.extern.slf4j.Slf4j; 23 | 24 | /** 25 | * ClassName: DefaultLocalTransactionExecuter 26 | * Description: 27 | * date: 2019/4/28 22:28 28 | * 29 | * @author ThierrySquirrel 30 | * @since JDK 1.8 31 | */ 32 | @Slf4j 33 | public class DefaultLocalTransactionExecutor implements LocalTransactionExecuter { 34 | /** 35 | * 执行本地事务,由应用来重写 36 | * 37 | * @param msg 消息 38 | * @param arg 应用自定义参数,由send方法传入并回调 39 | * @return {@link TransactionStatus} 返回事务执行结果,包括提交事务、回滚事务、未知状态 40 | */ 41 | @Override 42 | public TransactionStatus execute(Message msg, Object arg) { 43 | log.info(">>>> Execute local transaction message:{}>>>>", msg); 44 | return TransactionStatus.CommitTransaction; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/producer/DefaultSendCallback.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.producer; 18 | 19 | import com.aliyun.openservices.ons.api.OnExceptionContext; 20 | import com.aliyun.openservices.ons.api.SendCallback; 21 | import com.aliyun.openservices.ons.api.SendResult; 22 | import lombok.extern.slf4j.Slf4j; 23 | 24 | /** 25 | * ClassName: DefaultSendCallback 26 | * Description: 27 | * date: 2019/4/29 23:32 28 | * 29 | * @author ThierrySquirrel 30 | * @since JDK 1.8 31 | */ 32 | @Slf4j 33 | public class DefaultSendCallback implements SendCallback { 34 | /** 35 | * 发送成功回调的方法. 36 | * 37 | * @param sendResult 发送结果 38 | */ 39 | @Override 40 | public void onSuccess(SendResult sendResult) { 41 | log.info("Message sent successfully. >> sendResult = {}", sendResult); 42 | } 43 | 44 | /** 45 | * 发送失败回调方法. 46 | * 47 | * @param context 失败上下文. 48 | */ 49 | @Override 50 | public void onException(OnExceptionContext context) { 51 | log.error("Failed to send message. >> topic = {}, messageId = {}, exception ", context.getTopic(), context.getMessageId(), context.getException()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/producer/MessageSendType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.producer; 18 | 19 | /** 20 | * ClassName: MessageSendType 21 | * Description: 22 | * date: 2019/4/28 21:02 23 | * 24 | * @author ThierrySquirrel 25 | * @since JDK 1.8 26 | */ 27 | public enum MessageSendType { 28 | /** 29 | * 同步发送 30 | */ 31 | SEND, 32 | /** 33 | * 异步发送 34 | */ 35 | SEND_ASYNC, 36 | /** 37 | * 单向发送 38 | */ 39 | SEND_ONE_WAY 40 | 41 | } 42 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/serializer/Base64Serializer.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.rocketmq.core.serializer; 2 | 3 | import cn.hutool.core.codec.Base64; 4 | import cn.hutool.json.JSONUtil; 5 | 6 | import java.nio.charset.StandardCharsets; 7 | 8 | /** 9 | * 基于 Hutool JSON 的 MQ Message 序列化器 10 | * 11 | * @author Uncarbon 12 | */ 13 | public class Base64Serializer implements RocketSerializer { 14 | 15 | @Override 16 | public byte[] serialize(T object) { 17 | if (object == null) { 18 | return new byte[0]; 19 | } 20 | 21 | return Base64.encode(JSONUtil.toJsonStr(object)).getBytes(StandardCharsets.UTF_8); 22 | } 23 | 24 | @Override 25 | public T deSerialize(byte[] bytes, Class clazz) { 26 | if (bytes.length == 0 || clazz == null) { 27 | return null; 28 | } 29 | 30 | return JSONUtil.toBean(new String(Base64.decode(bytes)), clazz); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/serializer/HutoolJsonSerializer.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.rocketmq.core.serializer; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import cn.hutool.json.JSONUtil; 5 | 6 | import java.nio.charset.StandardCharsets; 7 | 8 | /** 9 | * 基于 Hutool JSON 的 MQ Message 序列化器 10 | * 11 | * @author Uncarbon 12 | */ 13 | public class HutoolJsonSerializer implements RocketSerializer { 14 | 15 | @Override 16 | public byte[] serialize(T object) { 17 | if (object == null) { 18 | return new byte[0]; 19 | } 20 | 21 | return JSONUtil.toJsonStr(object).getBytes(StandardCharsets.UTF_8); 22 | } 23 | 24 | @Override 25 | public T deSerialize(byte[] bytes, Class clazz) { 26 | if (bytes.length == 0 || clazz == null) { 27 | return null; 28 | } 29 | 30 | return JSONUtil.toBean(StrUtil.toString(bytes), clazz); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/serializer/JacksonJsonSerializer.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.rocketmq.core.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * 基于 Jackson JSON 的 MQ Message 序列化器 12 | * 13 | * @author Uncarbon 14 | */ 15 | @RequiredArgsConstructor 16 | @Slf4j 17 | public class JacksonJsonSerializer implements RocketSerializer { 18 | 19 | private final ObjectMapper objectMapper; 20 | 21 | @Override 22 | public byte[] serialize(T object) { 23 | if (object != null) { 24 | try { 25 | return objectMapper.writeValueAsBytes(object); 26 | } catch (JsonProcessingException jpe) { 27 | log.error("[Rocket MQ-Aliyun][JacksonJsonSerializer] 序列化时发生异常 >> 堆栈", jpe); 28 | } 29 | } 30 | 31 | return new byte[0]; 32 | } 33 | 34 | @Override 35 | public T deSerialize(byte[] bytes, Class clazz) { 36 | if (bytes.length > 0 && clazz != null) { 37 | try { 38 | return objectMapper.readValue(bytes, clazz); 39 | } catch (IOException e) { 40 | log.error("[Rocket MQ-Aliyun][JacksonJsonSerializer] 反序列化时发生异常 >> 堆栈", e); 41 | } 42 | } 43 | 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/serializer/RocketSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.serializer; 18 | 19 | /** 20 | * ClassName: RocketSerializer 21 | * Description: 22 | * date: 2019/10/17 18:24 23 | * 24 | * @author ThierrySquirrel 25 | * @since JDK 1.8 26 | */ 27 | public interface RocketSerializer { 28 | /** 29 | * 序列化为byte[] 30 | * @param object 对象 31 | * @param 泛型 32 | * @return 二进制数据 33 | */ 34 | byte[] serialize(T object); 35 | 36 | /** 37 | * byte[]反序列化为对象 38 | * 39 | * @param bytes 序列化的二进制数据 40 | * @param clazz 反序列化后的对象 41 | * @param T 42 | * @return 对象 43 | */ 44 | T deSerialize(byte[] bytes, Class clazz); 45 | } 46 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/strategy/ProducerStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.strategy; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.CommonMessage; 20 | import cc.uncarbon.framework.rocketmq.annotation.OrderMessage; 21 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 22 | import cc.uncarbon.framework.rocketmq.annotation.TransactionMessage; 23 | import cc.uncarbon.framework.rocketmq.core.factory.ProducerConsumerFactory; 24 | import cc.uncarbon.framework.rocketmq.core.factory.SendMessageFactory; 25 | import com.aliyun.openservices.ons.api.Producer; 26 | import com.aliyun.openservices.ons.api.order.OrderProducer; 27 | import com.aliyun.openservices.ons.api.transaction.TransactionProducer; 28 | import org.springframework.context.ApplicationContext; 29 | 30 | import java.util.Map; 31 | 32 | /** 33 | * ClassName: ProducerStrategy 34 | * Description: 35 | * date: 2019/4/29 21:34 36 | * 37 | * @author ThierrySquirrel 38 | * @since JDK 1.8 39 | */ 40 | public class ProducerStrategy { 41 | private ProducerStrategy() { 42 | } 43 | 44 | public static void statsSendMessage(Long startDeliverTime, String shardingKeyFactory, Map consumerContainer, RocketMessage rocketMessage, Object message, byte[] bytes, ApplicationContext applicationContext) { 45 | if (message instanceof CommonMessage commonMessage) { 46 | Producer producer = ProducerConsumerFactory.getProducer(consumerContainer, rocketMessage, commonMessage); 47 | SendMessageFactory.sendMessage(startDeliverTime, producer, commonMessage, bytes, applicationContext); 48 | return; 49 | } 50 | if (message instanceof OrderMessage orderMessage) { 51 | OrderProducer orderProducer = ProducerConsumerFactory.getProducer(consumerContainer, rocketMessage, orderMessage); 52 | SendMessageFactory.sendMessage(orderProducer, orderMessage, bytes, shardingKeyFactory); 53 | return; 54 | } 55 | if (message instanceof TransactionMessage transactionMessage) { 56 | TransactionProducer transactionProducer = ProducerConsumerFactory.getProducer(consumerContainer, rocketMessage, transactionMessage); 57 | SendMessageFactory.sendMessage(transactionProducer, transactionMessage, bytes, applicationContext); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/strategy/SendMessageStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.strategy; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.CommonMessage; 20 | import cc.uncarbon.framework.rocketmq.core.producer.MessageSendType; 21 | import cc.uncarbon.framework.rocketmq.core.utils.ApplicationContextUtils; 22 | import com.aliyun.openservices.ons.api.Message; 23 | import com.aliyun.openservices.ons.api.Producer; 24 | import org.springframework.context.ApplicationContext; 25 | 26 | 27 | /** 28 | * ClassName: SendMessageStrategy 29 | * Description: 30 | * date: 2019/4/29 23:37 31 | * 32 | * @author ThierrySquirrel 33 | * @since JDK 1.8 34 | */ 35 | public final class SendMessageStrategy { 36 | private SendMessageStrategy() { 37 | } 38 | 39 | public static void send(CommonMessage commonMessage, Producer producer, Message message, ApplicationContext applicationContext) { 40 | if (commonMessage.messageSendType().equals(MessageSendType.SEND)) { 41 | producer.send(message); 42 | } else if (commonMessage.messageSendType().equals(MessageSendType.SEND_ASYNC)) { 43 | producer.sendAsync(message, ApplicationContextUtils.getSendCallback(applicationContext, commonMessage.callback())); 44 | } else if (commonMessage.messageSendType().equals(MessageSendType.SEND_ONE_WAY)) { 45 | producer.sendOneway(message); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/AnnotatedMethodsUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.utils; 18 | 19 | import org.springframework.core.MethodIntrospector; 20 | import org.springframework.core.annotation.AnnotatedElementUtils; 21 | 22 | import java.lang.annotation.Annotation; 23 | import java.lang.reflect.Method; 24 | import java.util.Map; 25 | 26 | /** 27 | * ClassName: AnnotatedMethodsUtils 28 | * Description: 29 | * date: 2019/5/3 11:44 30 | * 31 | * @author ThierrySquirrel 32 | * @since JDK 1.8 33 | */ 34 | public final class AnnotatedMethodsUtils { 35 | private AnnotatedMethodsUtils() { 36 | } 37 | 38 | public static Map getMethodAndAnnotation(Object bean, Class annotation) { 39 | return MethodIntrospector.selectMethods(bean.getClass(), 40 | (MethodIntrospector.MetadataLookup) method -> AnnotatedElementUtils 41 | .findMergedAnnotation(method, annotation)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/AspectUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.utils; 18 | 19 | import org.aspectj.lang.ProceedingJoinPoint; 20 | import org.aspectj.lang.reflect.MethodSignature; 21 | 22 | import java.lang.annotation.Annotation; 23 | import java.lang.reflect.Method; 24 | import java.lang.reflect.Parameter; 25 | 26 | /** 27 | * ClassName: AspectUtils 28 | * Description: 29 | * date: 2020/1/29 18:49 30 | * 31 | * @author ThierrySquirrel 32 | * @since JDK 1.8 33 | */ 34 | public final class AspectUtils { 35 | private AspectUtils() { 36 | } 37 | 38 | private static Method getMethod(ProceedingJoinPoint proceedingJoinPoint) { 39 | MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature(); 40 | return signature.getMethod(); 41 | } 42 | 43 | public static T getAnnotation(ProceedingJoinPoint proceedingJoinPoint, Class annotationClass) { 44 | return getMethod(proceedingJoinPoint).getAnnotation(annotationClass); 45 | } 46 | 47 | public static T getDeclaringClassAnnotation(ProceedingJoinPoint proceedingJoinPoint, Class annotationClass) { 48 | return getMethod(proceedingJoinPoint).getDeclaringClass().getAnnotation(annotationClass); 49 | } 50 | 51 | public static Parameter[] getParams(ProceedingJoinPoint proceedingJoinPoint) { 52 | Method method = getMethod(proceedingJoinPoint); 53 | return method.getParameters(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/InterceptRocket.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.core.utils; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 20 | import cc.uncarbon.framework.rocketmq.core.factory.execution.SendMessageFactoryExecution; 21 | import cc.uncarbon.framework.rocketmq.core.factory.execution.ThreadPoolExecutorExecution; 22 | import cc.uncarbon.framework.rocketmq.core.serializer.RocketSerializer; 23 | import org.springframework.context.ApplicationContext; 24 | 25 | import java.lang.annotation.Annotation; 26 | import java.util.Map; 27 | import java.util.concurrent.ThreadPoolExecutor; 28 | 29 | /** 30 | * ClassName: InterceptRocket 31 | * Description: 32 | * date: 2019/4/29 22:03 33 | * 34 | * @author ThierrySquirrel 35 | * @since JDK 1.8 36 | */ 37 | public final class InterceptRocket { 38 | private InterceptRocket() { 39 | } 40 | 41 | public static Object intercept(Long startDeliverTime, String shardingKeyFactory, 42 | RocketMessage rocketMessage, T annotation, Object proceed, 43 | Map consumerContainer, ThreadPoolExecutor threadPoolExecutor, 44 | ApplicationContext applicationContext) { 45 | RocketSerializer mqSerializer = applicationContext.getBean(RocketSerializer.class); 46 | byte[] body = mqSerializer.serialize(proceed); 47 | 48 | ThreadPoolExecutorExecution.statsThread(threadPoolExecutor, 49 | new SendMessageFactoryExecution(startDeliverTime, shardingKeyFactory, consumerContainer, rocketMessage, annotation, body, applicationContext)); 50 | return proceed; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/exception/RocketException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.exception; 18 | 19 | /** 20 | * ClassName: RocketException 21 | * Description: 22 | * date: 2019/4/27 16:44 23 | * 24 | * @author ThierrySquirrel 25 | * @since JDK 1.8 26 | */ 27 | public class RocketException extends Exception { 28 | 29 | public RocketException(String message) { 30 | super(message); 31 | } 32 | 33 | public RocketException(Throwable cause) { 34 | super(cause); 35 | } 36 | 37 | public RocketException(String message, Throwable cause) { 38 | super(message, cause); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/thread/AbstractConsumerThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.thread; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.MessageListener; 20 | import cc.uncarbon.framework.rocketmq.annotation.RocketListener; 21 | import cc.uncarbon.framework.rocketmq.core.factory.execution.MethodFactoryExecution; 22 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 23 | import lombok.Data; 24 | 25 | /** 26 | * ClassName: AbstractConsumerThread 27 | * Description: 28 | * date: 2019/4/27 20:03 29 | * 30 | * @author ThierrySquirrel 31 | * @since JDK 1.8 32 | */ 33 | @Data 34 | public abstract class AbstractConsumerThread implements Runnable { 35 | private AliyunRocketProperties rocketProperties; 36 | private RocketListener rocketListener; 37 | private MessageListener consumerListener; 38 | private MethodFactoryExecution methodFactoryExecution; 39 | 40 | protected AbstractConsumerThread(AliyunRocketProperties rocketProperties, RocketListener rocketListener, MessageListener consumerListener, MethodFactoryExecution methodFactoryExecution) { 41 | this.rocketProperties = rocketProperties; 42 | this.rocketListener = rocketListener; 43 | this.consumerListener = consumerListener; 44 | this.methodFactoryExecution = methodFactoryExecution; 45 | } 46 | 47 | /** 48 | * 消费者开始监听 49 | * 50 | * @param rocketProperties rocketProperties 51 | * @param rocketListener rocketListener 52 | * @param consumerListener consumerListener 53 | * @param methodFactoryExecution methodFactoryExecution 54 | */ 55 | protected abstract void statsConsumer(AliyunRocketProperties rocketProperties, 56 | RocketListener rocketListener, 57 | MessageListener consumerListener, 58 | MethodFactoryExecution methodFactoryExecution); 59 | 60 | @Override 61 | public void run() { 62 | statsConsumer(this.getRocketProperties(), 63 | this.getRocketListener(), 64 | this.getConsumerListener(), 65 | this.getMethodFactoryExecution()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/thread/AbstractProducerThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cc.uncarbon.framework.rocketmq.thread; 18 | 19 | import cc.uncarbon.framework.rocketmq.annotation.RocketMessage; 20 | import cc.uncarbon.framework.rocketmq.props.AliyunRocketProperties; 21 | import lombok.Data; 22 | import org.springframework.context.ApplicationContext; 23 | 24 | import java.util.Map; 25 | 26 | /** 27 | * ClassName: AbstractProducerThread 28 | * Description: 29 | * date: 2019/5/3 14:02 30 | * 31 | * @author ThierrySquirrel 32 | * @since JDK 1.8 33 | */ 34 | @Data 35 | public abstract class AbstractProducerThread implements Runnable { 36 | private Map producerConsumer; 37 | private RocketMessage rocketMessage; 38 | private Object bean; 39 | private AliyunRocketProperties rocketProperties; 40 | private ApplicationContext applicationContext; 41 | 42 | protected AbstractProducerThread(Map producerConsumer, RocketMessage rocketMessage, Object bean, 43 | AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) { 44 | this.producerConsumer = producerConsumer; 45 | this.rocketMessage = rocketMessage; 46 | this.bean = bean; 47 | this.rocketProperties = rocketProperties; 48 | this.applicationContext = applicationContext; 49 | } 50 | 51 | /** 52 | * 开始向容器装填 53 | * 54 | * @param producerConsumer producerConsumer 55 | * @param rocketMessage rocketMessage 56 | * @param bean bean 57 | * @param rocketProperties rocketProperties 58 | * @param applicationContext applicationContext 59 | */ 60 | protected abstract void statsPutProducer(Map producerConsumer, RocketMessage rocketMessage, 61 | Object bean, AliyunRocketProperties rocketProperties, 62 | ApplicationContext applicationContext); 63 | 64 | @Override 65 | public void run() { 66 | statsPutProducer(producerConsumer, rocketMessage, bean, rocketProperties, applicationContext); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /helio-starter-rocketmq-aliyun/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.rocketmq.config.AliyunRocketAutoConfiguration 2 | -------------------------------------------------------------------------------- /helio-starter-satoken/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-satoken 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | 30 | jakarta.servlet 31 | jakarta.servlet-api 32 | 33 | 34 | 35 | cn.dev33 36 | sa-token-spring-boot3-starter 37 | 38 | 39 | 40 | cn.dev33 41 | sa-token-redis-jackson 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /helio-starter-satoken/src/main/java/cc/uncarbon/framework/satoken/config/SaTokenLocalCacheAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.satoken.config; 2 | 3 | import cc.uncarbon.framework.core.props.HelioProperties; 4 | import cc.uncarbon.framework.satoken.dao.SaTokenLocalCacheDao; 5 | import org.springframework.boot.autoconfigure.AutoConfiguration; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Primary; 10 | 11 | /** 12 | * SA-Token 本地缓存自动配置类 13 | * 14 | * @author Uncarbon 15 | */ 16 | @ConditionalOnExpression(value = "${helio.security.saTokenLocalCache.enabled:false} || ${helio.security.sa-token-local-cache.enabled:false}") 17 | @AutoConfiguration 18 | public class SaTokenLocalCacheAutoConfiguration { 19 | 20 | @ConditionalOnMissingBean(value = SaTokenLocalCacheDao.class) 21 | @Primary 22 | @Bean 23 | public SaTokenLocalCacheDao saTokenLocalCacheDao(HelioProperties helioProperties) { 24 | HelioProperties.Security.SaTokenLocalCache props = helioProperties.getSecurity().getSaTokenLocalCache(); 25 | return new SaTokenLocalCacheDao(props.getDuration(), props.getCapacity()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /helio-starter-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.satoken.config.SaTokenLocalCacheAutoConfiguration 2 | -------------------------------------------------------------------------------- /helio-starter-tenant/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | helio-starters 7 | cc.uncarbon.framework 8 | 2.3.1 9 | 10 | 4.0.0 11 | 12 | helio-starter-tenant 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | cc.uncarbon.framework 21 | helio-core 22 | 23 | 24 | 25 | cc.uncarbon.framework 26 | helio-starter-crud 27 | 28 | 29 | 30 | 31 | com.baomidou 32 | dynamic-datasource-spring-boot-starter 33 | true 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/annotation/EnableGlobalTenantDataSource.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.annotation; 2 | 3 | import cc.uncarbon.framework.tenant.config.GlobalTenantDataSourceConfiguration; 4 | import java.lang.annotation.Documented; 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | import org.springframework.context.annotation.Import; 10 | 11 | /** 12 | * 启用基于全局 AOP 的数据源级多租户 13 | * 14 | * @author Uncarbon 15 | */ 16 | @Import(value = GlobalTenantDataSourceConfiguration.class) 17 | @Target({ElementType.TYPE}) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Documented 20 | public @interface EnableGlobalTenantDataSource { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/annotation/IgnoreTenantDataSource.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 数据源级多租户专用,忽略 AOP 拦截 11 | * 12 | * @author Uncarbon 13 | */ 14 | @Target({ElementType.TYPE, ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Documented 17 | public @interface IgnoreTenantDataSource { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/config/GlobalTenantDataSourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.config; 2 | 3 | import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegistry; 4 | import cc.uncarbon.framework.tenant.tenantdatasource.GlobalTenantDataSourceAdvisor; 5 | import cc.uncarbon.framework.tenant.tenantdatasource.GlobalTenantDataSourceInterceptor; 6 | import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; 7 | import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; 8 | import org.springframework.beans.factory.config.BeanDefinition; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Role; 12 | 13 | /** 14 | * 基于全局 AOP 的数据源级多租户配置类 15 | * 参考文章 16 | * 17 | * @author Uncarbon 18 | */ 19 | public class GlobalTenantDataSourceConfiguration { 20 | 21 | @Bean 22 | @ConditionalOnMissingBean 23 | public DynamicRoutingDataSource dynamicRoutingDataSource() { 24 | return new DynamicRoutingDataSource(); 25 | } 26 | 27 | @Bean 28 | @ConditionalOnMissingBean 29 | @Role(value = BeanDefinition.ROLE_INFRASTRUCTURE) 30 | public GlobalTenantDataSourceInterceptor interceptor( 31 | HelioDynamicDataSourceRegistry dataSourceRegistry 32 | ) { 33 | return new GlobalTenantDataSourceInterceptor(dataSourceRegistry); 34 | } 35 | 36 | 37 | @Bean 38 | @ConditionalOnMissingBean 39 | @Role(value = BeanDefinition.ROLE_INFRASTRUCTURE) 40 | public GlobalTenantDataSourceAdvisor tenantDataSourceGlobalAdvisor( 41 | GlobalTenantDataSourceInterceptor interceptor, 42 | DynamicDataSourceProperties properties 43 | ) { 44 | GlobalTenantDataSourceAdvisor advisor = new GlobalTenantDataSourceAdvisor(interceptor); 45 | // 数值越高,优先度越低 46 | advisor.setOrder(properties.getAop().getOrder() + 1); 47 | return advisor; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/config/HelioTenantAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.config; 2 | 3 | import cc.uncarbon.framework.core.enums.TenantIsolateLevelEnum; 4 | import cc.uncarbon.framework.core.props.HelioProperties; 5 | import cc.uncarbon.framework.crud.support.TenantSupport; 6 | import cc.uncarbon.framework.crud.support.impl.DefaultTenantSupport; 7 | import cc.uncarbon.framework.tenant.support.TenantDataSourceSupport; 8 | import cc.uncarbon.framework.tenant.support.TenantLineSupport; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.boot.autoconfigure.AutoConfiguration; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Primary; 13 | 14 | /** 15 | * Helio 多租户自动配置类 16 | * 当启用多租户功能,且多租户隔离级别配置正确,则向 IoC 容器注入对应处理 Bean 17 | * 18 | * @author Uncarbon 19 | */ 20 | @RequiredArgsConstructor 21 | @AutoConfiguration 22 | public class HelioTenantAutoConfiguration { 23 | 24 | private final HelioProperties helioProperties; 25 | 26 | 27 | @Bean 28 | @Primary 29 | public TenantSupport tenantSupport() { 30 | if (!Boolean.TRUE.equals(helioProperties.getTenant().getEnabled())) { 31 | // 引入了 starter,但未启用多租户 32 | return new DefaultTenantSupport(); 33 | } 34 | 35 | TenantIsolateLevelEnum isolateLevel = helioProperties.getTenant().getIsolateLevel(); 36 | if (isolateLevel == TenantIsolateLevelEnum.LINE) { 37 | // 行级 38 | return new TenantLineSupport(); 39 | } else if (isolateLevel == TenantIsolateLevelEnum.DATASOURCE) { 40 | // 数据源级 41 | return new TenantDataSourceSupport(); 42 | } 43 | throw new IllegalArgumentException("启用多租户功能后,请正确配置对应的多租户隔离级别(helio.tenant.isolate-level)"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantDataSourceSupport.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.support; 2 | 3 | import cc.uncarbon.framework.core.props.HelioProperties; 4 | import cc.uncarbon.framework.crud.support.TenantSupport; 5 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.util.ClassUtils; 8 | 9 | /** 10 | * 多租户支持-数据源级 11 | * 12 | * @author Uncarbon 13 | */ 14 | @Slf4j 15 | public class TenantDataSourceSupport implements TenantSupport { 16 | 17 | @Override 18 | public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) { 19 | log.info("\n\n[多租户支持] >> 隔离级别: 数据源级"); 20 | 21 | System.err.println("数据源级多租户支持还处于试验阶段,请经过测试后再投入生产使用!"); 22 | 23 | try { 24 | Class.forName("com.baomidou.dynamic.datasource.DynamicRoutingDataSource", 25 | false, ClassUtils.getDefaultClassLoader()); 26 | } catch (ClassNotFoundException cnfe) { 27 | System.err.println("\n\n ERROR: 没有找到 dynamic-datasource-spring-boot-starter 依赖,请检查是否引入"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantLineSupport.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.support; 2 | 3 | import cc.uncarbon.framework.core.constant.HelioConstant; 4 | import cc.uncarbon.framework.core.context.TenantContextHolder; 5 | import cc.uncarbon.framework.core.props.HelioProperties; 6 | import cc.uncarbon.framework.crud.support.TenantSupport; 7 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 8 | import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; 9 | import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; 10 | import lombok.AllArgsConstructor; 11 | import lombok.extern.slf4j.Slf4j; 12 | import net.sf.jsqlparser.expression.Expression; 13 | import net.sf.jsqlparser.expression.LongValue; 14 | 15 | import java.util.Collection; 16 | import java.util.Objects; 17 | 18 | /** 19 | * 多租户支持-行级 20 | * 21 | * @author Uncarbon 22 | */ 23 | @Slf4j 24 | public class TenantLineSupport implements TenantSupport { 25 | 26 | @Override 27 | public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) { 28 | Collection ignoredTables = helioProperties.getTenant().getIgnoredTables(); 29 | 30 | // 添加行级租户内联拦截器 31 | interceptor.addInnerInterceptor( 32 | new TenantLineInnerInterceptor(new HelioLineTenantHandler( 33 | helioProperties.getTenant().getPrivilegedTenantId(), ignoredTables) 34 | ) 35 | ); 36 | 37 | log.info("\n\n[多租户支持] >> 隔离级别: 行级"); 38 | 39 | System.err.println("以下数据表不参与租户隔离: " + ignoredTables); 40 | } 41 | 42 | 43 | /** 44 | * 行级租户mybatis-plus拦截器实现类 45 | */ 46 | @AllArgsConstructor 47 | public static class HelioLineTenantHandler implements TenantLineHandler { 48 | 49 | /** 50 | * 特权租户ID 51 | */ 52 | private final Long privilegedTenantId; 53 | 54 | /** 55 | * 忽略租户隔离的表 56 | */ 57 | private final Collection ignoredTables; 58 | 59 | 60 | @Override 61 | public Expression getTenantId() { 62 | Long currentTenantId = TenantContextHolder.getTenantId(); 63 | if (currentTenantId == null) { 64 | return null; 65 | } 66 | 67 | return new LongValue(currentTenantId); 68 | } 69 | 70 | @Override 71 | public String getTenantIdColumn() { 72 | return HelioConstant.CRUD.COLUMN_TENANT_ID; 73 | } 74 | 75 | @Override 76 | public boolean ignoreTable(String tableName) { 77 | Long currentTenantId = TenantContextHolder.getTenantId(); 78 | 79 | if (Objects.nonNull(privilegedTenantId) 80 | && Objects.nonNull(currentTenantId) 81 | && privilegedTenantId.equals(currentTenantId)) { 82 | return true; 83 | } 84 | 85 | return ignoredTables.contains(tableName); 86 | } 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantTableSupport.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.support; 2 | 3 | import cc.uncarbon.framework.core.context.TenantContextHolder; 4 | import cc.uncarbon.framework.core.props.HelioProperties; 5 | import cc.uncarbon.framework.crud.support.TenantSupport; 6 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 7 | import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler; 8 | import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor; 9 | import lombok.AllArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.util.Collection; 13 | 14 | /** 15 | * 多租户支持-表级 16 | * @deprecated 后来发现并不是太适合实践使用,仅保留本类 17 | * 18 | * @author Uncarbon 19 | */ 20 | @Slf4j 21 | @Deprecated 22 | public class TenantTableSupport implements TenantSupport { 23 | 24 | @Override 25 | public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) { 26 | Collection ignoredTables = helioProperties.getTenant().getIgnoredTables(); 27 | 28 | DynamicTableNameInnerInterceptor innerInterceptor = new DynamicTableNameInnerInterceptor(); 29 | innerInterceptor.setTableNameHandler(new HelioTableTenantHandler(ignoredTables)); 30 | 31 | // 添加表级租户内联拦截器 32 | interceptor.addInnerInterceptor(innerInterceptor); 33 | 34 | log.info("\n\n[多租户支持] >> 隔离级别: 表级,以下数据表不参与租户隔离: {}\n", 35 | ignoredTables); 36 | } 37 | 38 | /** 39 | * 表级租户mybatis-plus拦截器实现类 40 | */ 41 | @AllArgsConstructor 42 | public static class HelioTableTenantHandler implements TableNameHandler { 43 | 44 | /** 45 | * 忽略租户隔离的表 46 | */ 47 | private Collection ignoredTables; 48 | 49 | @Override 50 | public String dynamicTableName(String sql, String tableName) { 51 | if (ignoredTables.contains(tableName)) { 52 | return tableName; 53 | } 54 | 55 | // 拼接新表名 56 | return String.format("%s_%s", tableName, TenantContextHolder.getTenantId()); 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/tenantdatasource/GlobalTenantDataSourceAdvisor.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.tenantdatasource; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.NonNull; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.aopalliance.aop.Advice; 8 | import org.springframework.aop.Pointcut; 9 | import org.springframework.aop.aspectj.AspectJExpressionPointcut; 10 | import org.springframework.aop.support.AbstractPointcutAdvisor; 11 | import org.springframework.aop.support.ComposablePointcut; 12 | import org.springframework.beans.BeansException; 13 | import org.springframework.beans.factory.BeanFactory; 14 | import org.springframework.beans.factory.BeanFactoryAware; 15 | 16 | /** 17 | * 数据源级多租户全局 AOP 织入点 18 | * 19 | * @author chill 20 | * @author Uncarbon 21 | */ 22 | @EqualsAndHashCode(callSuper = true) 23 | @Slf4j 24 | @Getter 25 | public class GlobalTenantDataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware { 26 | 27 | private final Advice advice; 28 | private final Pointcut pointcut; 29 | 30 | public GlobalTenantDataSourceAdvisor(@NonNull GlobalTenantDataSourceInterceptor globalTenantDataSourceInterceptor) { 31 | this.advice = globalTenantDataSourceInterceptor; 32 | this.pointcut = buildPointcut(); 33 | } 34 | 35 | @Override 36 | public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException { 37 | if (this.advice instanceof BeanFactoryAware beanFactoryAware) { 38 | beanFactoryAware.setBeanFactory(beanFactory); 39 | } 40 | } 41 | 42 | /** 43 | * 配置需要拦截的切面 44 | */ 45 | private Pointcut buildPointcut() { 46 | AspectJExpressionPointcut cut = new AspectJExpressionPointcut(); 47 | cut.setExpression( 48 | "@within(org.springframework.stereotype.Service)" 49 | + " && !@annotation(cc.uncarbon.framework.tenant.annotation.IgnoreTenantDataSource)" 50 | ); 51 | return new ComposablePointcut((Pointcut) cut); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/tenantdatasource/GlobalTenantDataSourceInterceptor.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.tenant.tenantdatasource; 2 | 3 | import cc.uncarbon.framework.core.constant.HelioConstant.CRUD; 4 | import cc.uncarbon.framework.core.context.TenantContextHolder; 5 | import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegistry; 6 | import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; 7 | import java.util.Objects; 8 | import lombok.NonNull; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.aopalliance.intercept.MethodInterceptor; 12 | import org.aopalliance.intercept.MethodInvocation; 13 | 14 | /** 15 | * 数据源级多租户全局 AOP 处理过程 16 | * 17 | * @author chill 18 | * @author Uncarbon 19 | */ 20 | @RequiredArgsConstructor 21 | @Slf4j 22 | public class GlobalTenantDataSourceInterceptor implements MethodInterceptor { 23 | 24 | private final HelioDynamicDataSourceRegistry dataSourceRegistry; 25 | 26 | 27 | @Override 28 | public Object invoke(@NonNull MethodInvocation invocation) throws Throwable { 29 | Long currentTenantId = TenantContextHolder.getTenantId(); 30 | if (Objects.isNull(currentTenantId)) { 31 | // 没有租户信息,直接跳过 32 | return invocation.proceed(); 33 | } 34 | 35 | // 是否有切换过数据源(入栈) 36 | boolean pushedFlag = false; 37 | try { 38 | // 不适合纯数字作为数据源名称,给他拼个前缀 39 | String tenantDataSourceName = CRUD.COLUMN_TENANT_ID + currentTenantId; 40 | if (dataSourceRegistry.containsDataSource(tenantDataSourceName, true)) { 41 | log.debug("[多租户][数据源级] 使用租户数据源 >> {}", tenantDataSourceName); 42 | DynamicDataSourceContextHolder.push(tenantDataSourceName); 43 | pushedFlag = true; 44 | } 45 | return invocation.proceed(); 46 | } finally { 47 | if (pushedFlag) { 48 | // 数据源出栈 49 | DynamicDataSourceContextHolder.poll(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /helio-starter-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.tenant.config.HelioTenantAutoConfiguration 2 | -------------------------------------------------------------------------------- /helio-starter-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-test 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-test 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /helio-starter-test/src/main/java/cc/uncarbon/framework/test/stub/HelioStarterTestStub.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.test.stub; 2 | 3 | /** 4 | * 需要一个空类以解决:Missing: no javadoc jar found in folder 5 | */ 6 | public final class HelioStarterTestStub { 7 | private HelioStarterTestStub() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /helio-starter-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | helio-starters 10 | cc.uncarbon.framework 11 | 2.3.1 12 | ../pom.xml 13 | 14 | 15 | helio-starter-web 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cc.uncarbon.framework 25 | helio-core 26 | 27 | 28 | 29 | cc.uncarbon.framework 30 | helio-starter-aop 31 | 32 | 33 | 34 | cc.uncarbon.framework 35 | helio-starter-i18n 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-web 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-undertow 47 | 48 | 49 | 50 | cn.dev33 51 | sa-token-core 52 | ${sa-token.version} 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-validation 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/DefaultWebMvcAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.config; 2 | 3 | import cc.uncarbon.framework.web.jackson.EnumConverterFactory; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.propertyeditors.StringTrimmerEditor; 6 | import org.springframework.boot.autoconfigure.AutoConfiguration; 7 | import org.springframework.format.FormatterRegistry; 8 | import org.springframework.web.bind.WebDataBinder; 9 | import org.springframework.web.bind.support.WebBindingInitializer; 10 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 12 | 13 | /** 14 | * 默认 WebMVC 行为自动配置类 15 | * 16 | * @author Uncarbon 17 | **/ 18 | @AutoConfiguration 19 | @Slf4j 20 | public class DefaultWebMvcAutoConfiguration implements WebMvcConfigurer, WebBindingInitializer { 21 | /** 22 | * 自定义静态资源 23 | */ 24 | @Override 25 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 26 | registry 27 | .addResourceHandler("/static/**") 28 | .addResourceLocations("classpath:/static/"); 29 | registry 30 | .addResourceHandler("/doc.html") 31 | .addResourceLocations("classpath:/META-INF/resources/"); 32 | registry 33 | .addResourceHandler("/swagger-resources") 34 | .addResourceLocations("classpath:/META-INF/resources/"); 35 | registry 36 | .addResourceHandler("/webjars/**") 37 | .addResourceLocations("classpath:/META-INF/resources/webjars/"); 38 | } 39 | 40 | /** 41 | * [GET]请求, 将所有参数的空格trim 42 | */ 43 | @Override 44 | public void initBinder(WebDataBinder webDataBinder) { 45 | webDataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(false)); 46 | } 47 | 48 | /** 49 | * [GET]请求, 将int值转换成枚举类 50 | */ 51 | @Override 52 | public void addFormatters(FormatterRegistry registry) { 53 | registry.addConverterFactory(new EnumConverterFactory()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/MessageConverterAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.boot.autoconfigure.AutoConfiguration; 6 | import org.springframework.core.Ordered; 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.http.converter.*; 9 | import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; 10 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 12 | 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.List; 15 | 16 | /** 17 | * Jackson 类型转换器自动配置类 18 | * 19 | * @author Zhu JW 20 | * @author Uncarbon 21 | **/ 22 | @RequiredArgsConstructor 23 | @Order(Ordered.HIGHEST_PRECEDENCE) 24 | @AutoConfiguration 25 | public class MessageConverterAutoConfiguration implements WebMvcConfigurer { 26 | 27 | private final ObjectMapper objectMapper; 28 | 29 | 30 | /** 31 | * 使用 Jackson 作为JSON MessageConverter 32 | * 消息转换,内置断点续传,下载和字符串 33 | */ 34 | @Override 35 | public void configureMessageConverters(List> converters) { 36 | converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof AbstractJackson2HttpMessageConverter); 37 | converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); 38 | converters.add(new ByteArrayHttpMessageConverter()); 39 | converters.add(new ResourceHttpMessageConverter()); 40 | converters.add(new ResourceRegionHttpMessageConverter()); 41 | converters.add(new MappingJackson2HttpMessageConverter(objectMapper)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/XssAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.config; 2 | 3 | import cc.uncarbon.framework.core.props.HelioProperties; 4 | import cc.uncarbon.framework.web.xss.XssFilter; 5 | import jakarta.servlet.DispatcherType; 6 | import org.springframework.boot.autoconfigure.AutoConfiguration; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.core.Ordered; 12 | 13 | /** 14 | * 反 XSS 注入自动配置类 15 | * 16 | * @author Uncarbon 17 | */ 18 | @AutoConfiguration 19 | public class XssAutoConfiguration { 20 | 21 | @Bean 22 | @ConditionalOnMissingBean 23 | @ConditionalOnExpression(value = "${helio.security.xss.enabled:true}") 24 | public FilterRegistrationBean xssFilterRegistration(HelioProperties helioProperties) { 25 | FilterRegistrationBean registration = new FilterRegistrationBean<>(); 26 | registration.setDispatcherTypes(DispatcherType.REQUEST); 27 | registration.setFilter(new XssFilter(helioProperties.getSecurity().getXss().getExcludedRoutes())); 28 | registration.addUrlPatterns("/*"); 29 | registration.setName("xssFilter"); 30 | registration.setOrder(Ordered.LOWEST_PRECEDENCE); 31 | return registration; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/enums/GlobalWebExceptionI18nMessageEnum.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * Web 全局异常处理国际化消息枚举 8 | * 枚举值name 同时视为 i18nCode 9 | * 10 | * @author Uncarbon 11 | */ 12 | @AllArgsConstructor 13 | @Getter 14 | public enum GlobalWebExceptionI18nMessageEnum { 15 | 16 | GLOBAL__NO_LOGIN("请您先登录"), 17 | GLOBAL__PERMISSION_NOT_MATCH("您权限不足"), 18 | GLOBAL__ROLE_NOT_MATCH("您与要求角色不符"), 19 | GLOBAL__NOT_FOUND("你迷路啦"), 20 | GLOBAL__UNACCEPTABLE_PARAMETERS("错误参数格式或值"), 21 | GLOBAL__METHOD_NOT_ALLOWED("错误的请求方式"), 22 | 23 | /** 24 | * 一般为最后兜底使用 25 | */ 26 | GLOBAL__INTERNAL_ERROR("请稍后再试"),; 27 | 28 | private final String defaultValue; 29 | 30 | public String i18nCode() { 31 | return name(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/EnumConverterFactory.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.jackson; 2 | 3 | import cc.uncarbon.framework.core.enums.HelioBaseEnum; 4 | import cc.uncarbon.framework.core.exception.BusinessException; 5 | import lombok.NonNull; 6 | import org.springframework.core.convert.converter.Converter; 7 | import org.springframework.core.convert.converter.ConverterFactory; 8 | 9 | import java.util.Map; 10 | import java.util.WeakHashMap; 11 | 12 | /** 13 | * 枚举转换 14 | * 15 | * @author Zhu JW 16 | **/ 17 | public class EnumConverterFactory implements ConverterFactory { 18 | private final Map converterCache = new WeakHashMap<>(); 19 | 20 | @Override 21 | public Converter getConverter(@NonNull Class targetType) { 22 | return converterCache.computeIfAbsent(targetType, 23 | k -> converterCache.put(k, new EnumConverter(k)) 24 | ); 25 | } 26 | 27 | protected static class EnumConverter> implements Converter { 28 | 29 | private final Class enumType; 30 | 31 | public EnumConverter(@NonNull Class enumType) { 32 | this.enumType = enumType; 33 | } 34 | 35 | @Override 36 | public T convert(@NonNull Object value) { 37 | return HelioBaseEnum.of(this.enumType, value) 38 | .orElseThrow(() -> new BusinessException("Contains illegal enumeration value")); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/HelioJacksonModule.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.jackson; 2 | 3 | import cc.uncarbon.framework.core.constant.HelioConstant; 4 | import com.fasterxml.jackson.databind.module.SimpleModule; 5 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 6 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 7 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 8 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; 9 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 10 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 11 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; 12 | 13 | import java.math.BigDecimal; 14 | import java.math.BigInteger; 15 | import java.time.LocalDate; 16 | import java.time.LocalDateTime; 17 | import java.time.LocalTime; 18 | import java.time.format.DateTimeFormatter; 19 | 20 | /** 21 | * 自定义序列化规则 22 | **/ 23 | public class HelioJacksonModule extends SimpleModule { 24 | 25 | public HelioJacksonModule() { 26 | super(); 27 | /* 28 | 大整数转字符串, 避免精度丢失问题 29 | */ 30 | this.addSerializer(Long.class, ToStringSerializer.instance); 31 | this.addSerializer(Long.TYPE, ToStringSerializer.instance); 32 | this.addSerializer(BigInteger.class, ToStringSerializer.instance); 33 | this.addSerializer(BigDecimal.class, ToStringSerializer.instance); 34 | 35 | /* 36 | 时间相关;时区跟随JVM设置 37 | */ 38 | // knife4j-aggression用的hutool 5.4.1版本还没有DatePattern.*FORMATTER常量,为了兼容只能先手动构造了 39 | DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(HelioConstant.Jackson.DATE_TIME_FORMAT); 40 | DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(HelioConstant.Jackson.DATE_FORMAT); 41 | DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(HelioConstant.Jackson.TIME_FORMAT); 42 | this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter)); 43 | this.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter)); 44 | this.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter)); 45 | this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter)); 46 | this.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter)); 47 | this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/listener/LaunchEventListener.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.listener; 2 | 3 | import cn.hutool.core.text.CharSequenceUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.boot.web.context.WebServerInitializedEvent; 6 | import org.springframework.context.event.EventListener; 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.scheduling.annotation.Async; 10 | 11 | import java.net.InetAddress; 12 | import java.net.UnknownHostException; 13 | 14 | /** 15 | * 项目启动事件通知 16 | * 17 | * @author Zhu JW 18 | * @author Uncarbon 19 | **/ 20 | @Slf4j 21 | public class LaunchEventListener { 22 | 23 | @Async 24 | @Order 25 | @EventListener(WebServerInitializedEvent.class) 26 | public void afterStart(WebServerInitializedEvent event) { 27 | Environment env = event.getApplicationContext().getEnvironment(); 28 | int port = event.getWebServer().getPort(); 29 | 30 | String protocol = "http"; 31 | if (env.getProperty("server.ssl.key-store") != null) { 32 | protocol = "https"; 33 | } 34 | 35 | String contextPath = env.getProperty("server.servlet.context-path"); 36 | if (CharSequenceUtil.isBlank(contextPath)) { 37 | contextPath = "/"; 38 | } 39 | 40 | String externalHost = "localhost"; 41 | try { 42 | externalHost = InetAddress.getLocalHost().getHostAddress(); 43 | } catch (UnknownHostException e) { 44 | log.warn("获取外部(局域网/公网)IP失败"); 45 | } 46 | 47 | System.out.println( 48 | CharSequenceUtil.format( 49 | """ 50 | 51 | ---------------------------------------------------------- 52 | \tApplication '{}' is running! Access URLs: 53 | \tLocal: \t\t{}://127.0.0.1:{}{} 54 | \tExternal: \t{}://{}:{}{} 55 | ---------------------------------------------------------- 56 | 57 | """, 58 | env.getProperty("spring.application.name"), 59 | protocol, 60 | port, 61 | contextPath, 62 | protocol, 63 | externalHost, 64 | port, 65 | contextPath 66 | ) 67 | ); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/model/request/IdsDTO.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.model.request; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotEmpty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.experimental.Accessors; 9 | import lombok.experimental.SuperBuilder; 10 | 11 | import java.io.Serializable; 12 | import java.util.List; 13 | 14 | /** 15 | * 用于API接口接收参数 16 | * 兼容Integer Long String等多种类型的主键 17 | * 注: 记得加 @RequestBody @Valid 注解 18 | * @param 主键数据类型 19 | * 20 | * @author Uncarbon 21 | */ 22 | @Accessors(chain = true) 23 | @SuperBuilder 24 | @AllArgsConstructor 25 | @NoArgsConstructor 26 | @Data 27 | public class IdsDTO implements Serializable { 28 | 29 | @Schema(description = "主键ID数组", requiredMode = Schema.RequiredMode.REQUIRED) 30 | @NotEmpty(message = "ids不能为空") 31 | private List ids; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/model/response/ApiResult.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.model.response; 2 | 3 | import cc.uncarbon.framework.core.constant.HelioConstant; 4 | import cc.uncarbon.framework.core.enums.HelioBaseEnum; 5 | import cn.hutool.core.util.ObjectUtil; 6 | import cn.hutool.http.HttpStatus; 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | import lombok.experimental.Accessors; 12 | 13 | import java.io.Serializable; 14 | 15 | /** 16 | * HTTP接口通用返回对象 17 | * @param 承载数据类型 18 | * 19 | * @author Uncarbon 20 | */ 21 | @Accessors(chain = true) 22 | @AllArgsConstructor 23 | @NoArgsConstructor 24 | @Data 25 | public class ApiResult implements Serializable { 26 | 27 | @Schema(description = "状态码") 28 | private int code; 29 | 30 | @Schema(description = "返回消息") 31 | private String msg; 32 | 33 | @Schema(description = "承载数据") 34 | private T data; 35 | 36 | public static ApiResult success() { 37 | return build(HttpStatus.HTTP_OK, HelioConstant.Message.SUCCESS, null); 38 | } 39 | 40 | public static ApiResult success(String msg) { 41 | return build(HttpStatus.HTTP_OK, msg, null); 42 | } 43 | 44 | public static ApiResult fail(Integer code, String msg) { 45 | return build(code, msg, null); 46 | } 47 | 48 | public static ApiResult fail(Integer code, String msg, T data) { 49 | return build(code, msg, data); 50 | } 51 | 52 | public static ApiResult data(T data) { 53 | return build(HttpStatus.HTTP_OK, 54 | ObjectUtil.isEmpty(data) ? HelioConstant.Message.NULL : HelioConstant.Message.SUCCESS, 55 | data); 56 | } 57 | 58 | public static ApiResult data(String msg, T data) { 59 | return build(HttpStatus.HTTP_OK, msg, data); 60 | } 61 | 62 | public static ApiResult build(HelioBaseEnum enumItem) { 63 | return build(enumItem.getValue(), enumItem.getLabel(), null); 64 | } 65 | 66 | public static ApiResult build(HelioBaseEnum enumItem, T data) { 67 | return build(enumItem.getValue(), enumItem.getLabel(), data); 68 | } 69 | 70 | private static ApiResult build(Integer code, String msg, T data) { 71 | ApiResult ret = new ApiResult<>(); 72 | ret 73 | .setCode(code) 74 | .setMsg(msg) 75 | .setData(data); 76 | 77 | return ret; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/util/IPUtil.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.util; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.core.text.CharSequenceUtil; 5 | import cn.hutool.core.text.StrPool; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import lombok.experimental.UtilityClass; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * IP地址工具类 13 | * 方法来自于互联网 14 | * @author Uncarbon 15 | */ 16 | @UtilityClass 17 | public class IPUtil { 18 | private static final String[] HEADERS_TO_TRY = { 19 | "X-Forwarded-For", 20 | "Proxy-Client-IP", 21 | "WL-Proxy-Client-IP", 22 | "HTTP_X_FORWARDED_FOR", 23 | "HTTP_X_FORWARDED", 24 | "HTTP_X_CLUSTER_CLIENT_IP", 25 | "HTTP_CLIENT_IP", 26 | "HTTP_FORWARDED_FOR", 27 | "HTTP_FORWARDED", 28 | "HTTP_VIA", 29 | "REMOTE_ADDR", 30 | "X-Real-IP"}; 31 | 32 | /*** 33 | * 获取客户端IP地址(可以穿透代理) 34 | * @param request 请求对象 35 | * @return 客户端IP地址 36 | */ 37 | public String getClientIPAddress(HttpServletRequest request) { 38 | for (String header : HEADERS_TO_TRY) { 39 | String ip = request.getHeader(header); 40 | if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) { 41 | return ip; 42 | } 43 | } 44 | return request.getRemoteAddr(); 45 | } 46 | 47 | /*** 48 | * 获取客户端IP地址(可以穿透代理) 49 | * @param request 请求对象 50 | * @param indexOfCommaSplit 先按逗号分隔后,再取第index个IP地址(从0开始);兼容启用了云防护盾CDN的服务器(可能获取到的IP会带上中间代理节点的IP地址) 51 | * @return 客户端IP地址 52 | */ 53 | public String getClientIPAddress(HttpServletRequest request, int indexOfCommaSplit) { 54 | String source = getClientIPAddress(request); 55 | if (!CharSequenceUtil.contains(source, StrPool.COMMA)) { 56 | // 不含逗号 57 | return source; 58 | } 59 | List split = CharSequenceUtil.split(source, StrPool.COMMA); 60 | return CharSequenceUtil.cleanBlank(CollUtil.get(split, indexOfCommaSplit)); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/SQLFilter.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.xss; 2 | 3 | import cc.uncarbon.framework.core.exception.BusinessException; 4 | import cn.hutool.core.text.CharSequenceUtil; 5 | import org.springframework.util.StringUtils; 6 | 7 | /** 8 | * SQL过滤 9 | * 10 | * @author Mark sunlightcs@gmail.com 11 | */ 12 | public final class SQLFilter { 13 | 14 | private SQLFilter() { 15 | } 16 | 17 | /** 18 | * SQL注入过滤 19 | * 20 | * @param str 待验证的字符串 21 | */ 22 | public static String sqlInject(String str) { 23 | if (CharSequenceUtil.isBlank(str)) { 24 | return null; 25 | } 26 | // 去掉'|"|;|\字符 27 | str = StringUtils.replace(str, "'", ""); 28 | str = StringUtils.replace(str, "\"", ""); 29 | str = StringUtils.replace(str, ";", ""); 30 | str = StringUtils.replace(str, "\\", ""); 31 | 32 | // 转换成小写 33 | str = str.toLowerCase(); 34 | 35 | // 非法字符 36 | String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"}; 37 | 38 | // 判断是否包含非法字符 39 | for (String keyword : keywords) { 40 | if (str.contains(keyword)) { 41 | throw new BusinessException("Contains illegal character"); 42 | } 43 | } 44 | 45 | return str; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/XssFilter.java: -------------------------------------------------------------------------------- 1 | package cc.uncarbon.framework.web.xss; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import jakarta.servlet.*; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.util.AntPathMatcher; 8 | 9 | import java.io.IOException; 10 | import java.util.List; 11 | 12 | /** 13 | * XSS过滤器 14 | * 15 | * @author Mark sunlightcs@gmail.com 16 | */ 17 | @RequiredArgsConstructor 18 | public class XssFilter implements Filter { 19 | 20 | /** 21 | * 不进行过滤的路由(直接放行) 22 | */ 23 | private final List excludedRoutes; 24 | 25 | private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher(); 26 | 27 | 28 | @Override 29 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 30 | throws IOException, ServletException { 31 | HttpServletRequest httpServletRequest = (HttpServletRequest) request; 32 | if (canExclude(httpServletRequest)) { 33 | // 不经过XSS过滤,交给下一个过滤器 34 | chain.doFilter(httpServletRequest, response); 35 | return; 36 | } 37 | 38 | XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(httpServletRequest); 39 | chain.doFilter(xssRequest, response); 40 | } 41 | 42 | /** 43 | * 该路由是否可以排除XSS过滤 44 | */ 45 | private boolean canExclude(HttpServletRequest request) { 46 | if (CollUtil.isEmpty(excludedRoutes)) { 47 | return false; 48 | } 49 | 50 | String requestUri = request.getServletPath(); 51 | return excludedRoutes.stream().anyMatch( 52 | excludedRoute -> ANT_PATH_MATCHER.match(excludedRoute, requestUri) 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | cc.uncarbon.framework.web.listener.LaunchEventListener 2 | cc.uncarbon.framework.web.config.DefaultWebMvcAutoConfiguration 3 | cc.uncarbon.framework.web.config.GlobalWebExceptionHandlerAutoConfiguration 4 | cc.uncarbon.framework.web.config.JacksonExtendAutoConfiguration 5 | cc.uncarbon.framework.web.config.MessageConverterAutoConfiguration 6 | cc.uncarbon.framework.web.config.XssAutoConfiguration 7 | cc.uncarbon.framework.web.aspect.WebLoggingAspect 8 | -------------------------------------------------------------------------------- /helio-starter-web/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | # 不为工程中的资源文件建立映射 3 | web: 4 | resources: 5 | add-mappings: false 6 | --------------------------------------------------------------------------------