Parcourir la source

bump: 发布2.3.1

details see: https://helio.uncarbon.cc/#/i18n/zh-CN/appendix/change-log
Uncarbon il y a 1 an
Parent
commit
f3955e6433
25 fichiers modifiés avec 404 ajouts et 71 suppressions
  1. 7 1
      .gitignore
  2. 1 1
      helio-core/pom.xml
  3. 1 1
      helio-starter-aop/pom.xml
  4. 11 1
      helio-starter-cloud/pom.xml
  5. 1 1
      helio-starter-crud/pom.xml
  6. 61 7
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/mapper/HelioBaseMapper.java
  7. 1 1
      helio-starter-dubbo/pom.xml
  8. 1 1
      helio-starter-i18n/pom.xml
  9. 6 1
      helio-starter-knife4j/pom.xml
  10. 5 4
      helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/config/HelioKnife4jAutoConfiguration.java
  11. 170 0
      helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/customizer/Knife4jOpenApiCustomizerOverride.java
  12. 1 1
      helio-starter-rate-limit-redis/pom.xml
  13. 1 5
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByClientIPStrategy.java
  14. 2 5
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByUserIdStrategy.java
  15. 2 4
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitGlobalStrategy.java
  16. 15 11
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/SimpleRedisBasedRateLimitStrategy.java
  17. 1 1
      helio-starter-redis/pom.xml
  18. 9 1
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/model/FormattedRedisKey.java
  19. 43 1
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/model/RedisKeyDefinition.java
  20. 1 1
      helio-starter-rocketmq-aliyun/pom.xml
  21. 1 1
      helio-starter-satoken/pom.xml
  22. 1 1
      helio-starter-tenant/pom.xml
  23. 1 1
      helio-starter-test/pom.xml
  24. 1 1
      helio-starter-web/pom.xml
  25. 60 18
      pom.xml

+ 7 - 1
.gitignore

@@ -29,4 +29,10 @@ hs_err_pid*
 .idea
 *.iws
 *.iml
-*.ipr
+*.ipr
+
+# VSCode
+.vscode
+
+# VSCode 不会自动忽略 **/target 目录
+target/

+ 1 - 1
helio-core/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 1 - 1
helio-starter-aop/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 11 - 1
helio-starter-cloud/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 
@@ -46,5 +46,15 @@
             <artifactId>nacos-client</artifactId>
             <version>${nacos-client.vesion}</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-commons</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-context</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 1 - 1
helio-starter-crud/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 61 - 7
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/mapper/HelioBaseMapper.java

@@ -1,7 +1,6 @@
 package cc.uncarbon.framework.crud.mapper;
 
 import cc.uncarbon.framework.core.function.StreamFunction;
-import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ArrayUtil;
@@ -10,10 +9,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
 
 import java.io.Serializable;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -23,7 +19,7 @@ import java.util.stream.Collectors;
  * @param <E> 实体类
  * @author Uncarbon
  */
-public interface HelioBaseMapper<E extends HelioBaseEntity<? extends Serializable>> extends BaseMapper<E> {
+public interface HelioBaseMapper<E> extends BaseMapper<E> {
 
     /**
      * 根据IDs查询
@@ -42,7 +38,7 @@ public interface HelioBaseMapper<E extends HelioBaseEntity<? extends Serializabl
      */
     @Deprecated(since = "2.2.0", forRemoval = true)
     default <T extends Serializable> Map<T, String> getNameMap(Collection<T> ids,
-                                                           Function<E, T> idMapper, Function<E, String> nameMapper) {
+                                                               Function<E, T> idMapper, Function<E, String> nameMapper) {
         List<E> entityList = selectBatchIds(ids);
         if (CollUtil.isEmpty(entityList)) {
             return Collections.emptyMap();
@@ -51,6 +47,24 @@ public interface HelioBaseMapper<E extends HelioBaseEntity<? extends Serializabl
                 .collect(Collectors.toMap(idMapper, nameMapper, StreamFunction.ignoredThrowingMerger()));
     }
 
+    /**
+     * 快速构建业务中常用的查库并转map,常用于根据ID补全名称等业务场景
+     * 如:selectToMap(ids, Entity::getId)
+     */
+    default <K> Map<K, E> selectToMap(Collection<K> keys, SFunction<E, K> keyFieldGetter) {
+        if (CollUtil.isEmpty(keys)) {
+            return Map.of();
+        }
+        List<E> entityList = selectList(
+                new LambdaQueryWrapper<E>()
+                        .in(keyFieldGetter, keys)
+        );
+        if (CollUtil.isEmpty(entityList)) {
+            return Map.of();
+        }
+        return entityList.stream().collect(Collectors.toMap(keyFieldGetter, Function.identity(), StreamFunction.ignoredThrowingMerger()));
+    }
+
     /**
      * 快速构建业务中常用的查库并转map,常用于根据ID补全名称等业务场景
      * 如:selectToMap(ids, Entity::getId, Entity::getName)
@@ -92,4 +106,44 @@ public interface HelioBaseMapper<E extends HelioBaseEntity<? extends Serializabl
         return entityList.stream().collect(Collectors.toMap(keyFieldGetter, entity -> BeanUtil.copyProperties(entity, javaBeanClass),
                 StreamFunction.ignoredThrowingMerger()));
     }
+
+    /**
+     * 快速构建业务中常用的查库并转map,常用于一对多的情况下,根据ID补全多个关联值(使用List保存,有序、不去重)等业务场景
+     * 如:selectToGroupingMap(ids, Entity::getId, Entity::getRoleId)
+     */
+    default <K, V> Map<K, List<V>> selectToGroupingMap(Collection<K> keys,
+                                                       SFunction<E, K> keyFieldGetter, SFunction<E, V> valueFieldGetter) {
+        if (CollUtil.isEmpty(keys)) {
+            return Map.of();
+        }
+        List<E> entityList = selectList(
+                new LambdaQueryWrapper<E>()
+                        .select(keyFieldGetter, valueFieldGetter)
+                        .in(keyFieldGetter, keys)
+        );
+        if (CollUtil.isEmpty(entityList)) {
+            return Map.of();
+        }
+        return entityList.stream().collect(Collectors.groupingBy(keyFieldGetter, Collectors.mapping(valueFieldGetter, Collectors.toList())));
+    }
+
+    /**
+     * 快速构建业务中常用的查库并转map,常用于一对多的情况下,根据ID补全多个关联值(使用Set保存,无序、去重)等业务场景
+     * 如:selectToGroupingMap(ids, Entity::getId, Entity::getRoleId)
+     */
+    default <K, V> Map<K, Set<V>> selectToGroupingSetMap(Collection<K> keys,
+                                                         SFunction<E, K> keyFieldGetter, SFunction<E, V> valueFieldGetter) {
+        if (CollUtil.isEmpty(keys)) {
+            return Map.of();
+        }
+        List<E> entityList = selectList(
+                new LambdaQueryWrapper<E>()
+                        .select(keyFieldGetter, valueFieldGetter)
+                        .in(keyFieldGetter, keys)
+        );
+        if (CollUtil.isEmpty(entityList)) {
+            return Map.of();
+        }
+        return entityList.stream().collect(Collectors.groupingBy(keyFieldGetter, Collectors.mapping(valueFieldGetter, Collectors.toSet())));
+    }
 }

+ 1 - 1
helio-starter-dubbo/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 1 - 1
helio-starter-i18n/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 6 - 1
helio-starter-knife4j/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 
@@ -37,5 +37,10 @@
                 </exclusion>
             </exclusions>
         </dependency>
+
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 5 - 4
helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/config/HelioKnife4jAutoConfiguration.java

@@ -2,8 +2,9 @@ package cc.uncarbon.framework.knife4j.config;
 
 import cc.uncarbon.framework.core.props.HelioProperties;
 import cc.uncarbon.framework.knife4j.customizer.HelioBaseEnumCustomizer;
+import cc.uncarbon.framework.knife4j.customizer.Knife4jOpenApiCustomizerOverride;
 import cn.hutool.core.text.CharSequenceUtil;
-import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
+import com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration;
 import io.swagger.v3.oas.models.Components;
 import io.swagger.v3.oas.models.OpenAPI;
 import io.swagger.v3.oas.models.info.Info;
@@ -24,10 +25,10 @@ import org.springframework.core.env.ConfigurableEnvironment;
  * @author Uncarbon
  * @author xiaoymin
  */
-@Import(value = {HelioBaseEnumCustomizer.class})
-@EnableKnife4j
+// 需要控制自动配置类加载顺序,去除显式 @EnableKnife4j
+@Import(value = {Knife4jOpenApiCustomizerOverride.class, HelioBaseEnumCustomizer.class, Knife4jAutoConfiguration.class, SpringDocConfiguration.class})
 @ConditionalOnExpression(value = "!${knife4j.production:false}")
-@AutoConfiguration(before = SpringDocConfiguration.class)
+@AutoConfiguration
 public class HelioKnife4jAutoConfiguration {
 
     @Bean

+ 170 - 0
helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/customizer/Knife4jOpenApiCustomizerOverride.java

@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2017-2023 Knife4j(xiaoymin@foxmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package cc.uncarbon.framework.knife4j.customizer;
+
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;
+import com.github.xiaoymin.knife4j.core.conf.GlobalConstants;
+import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;
+import com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting;
+import com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer;
+import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.models.OpenAPI;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springdoc.core.properties.SpringDocConfigProperties;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.annotation.Annotation;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 增强扩展属性支持
+ * @since 4.1.0
+ * @author <a href="xiaoymin@foxmail.com">xiaoymin@foxmail.com</a>
+ * 2022/12/11 22:40
+ * @author Uncarbon
+ * 自 Spring Boot 3.4 开始,ControllerAdvice 类的构造函数改变,需要升级 SpringDoc 版本来适配,导致
+ * SpringDocConfigProperties.getGroupConfigs() 返回值从 List 变为 Set,老的 knife4j 不兼容
+ * 只能手动覆盖原有的 Knife4jOpenApiCustomizer
+ */
+@Slf4j
+@Component
+public class Knife4jOpenApiCustomizerOverride extends Knife4jOpenApiCustomizer {
+
+    final Knife4jProperties knife4jProperties;
+    final SpringDocConfigProperties properties;
+
+    public Knife4jOpenApiCustomizerOverride(Knife4jProperties knife4jProperties, SpringDocConfigProperties properties) {
+        super(knife4jProperties, properties);
+        this.knife4jProperties = knife4jProperties;
+        this.properties = properties;
+    }
+
+    @Override
+    public void customise(OpenAPI openApi) {
+        if (log.isDebugEnabled()) {
+            log.debug("Knife4j OpenApiCustomizer Override");
+        }
+        if (knife4jProperties.isEnable()) {
+            Knife4jSetting setting = knife4jProperties.getSetting();
+            OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());
+            // 解析初始化
+            openApiExtensionResolver.start();
+            Map<String, Object> objectMap = new HashMap<>(2, 1);
+            objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting);
+            objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles());
+            openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap);
+            addOrderExtension(openApi);
+        }
+    }
+    /**
+     * 往OpenAPI内tags字段添加x-order属性
+     *
+     * @param openApi openApi
+     */
+    private void addOrderExtension(OpenAPI openApi) {
+        if (CollectionUtils.isEmpty(properties.getGroupConfigs())) {
+            return;
+        }
+        // 获取包扫描路径
+        Set<String> packagesToScan =
+                properties.getGroupConfigs().stream()
+                        .map(SpringDocConfigProperties.GroupConfig::getPackagesToScan)
+                        .filter(toScan -> !CollectionUtils.isEmpty(toScan))
+                        .flatMap(List::stream)
+                        .collect(Collectors.toSet());
+        if (CollectionUtils.isEmpty(packagesToScan)) {
+            return;
+        }
+        // 扫描包下被ApiSupport注解的RestController Class
+        Set<Class<?>> classes =
+                packagesToScan.stream()
+                        .map(packageToScan -> scanPackageByAnnotation(packageToScan, RestController.class))
+                        .flatMap(Set::stream)
+                        .filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class))
+                        .collect(Collectors.toSet());
+        if (!CollectionUtils.isEmpty(classes)) {
+            // ApiSupport oder值存入tagSortMap<Tag.name,ApiSupport.order>
+            Map<String, Integer> tagOrderMap = new HashMap<>();
+            classes.forEach(
+                    clazz -> {
+                        Tag tag = getTag(clazz);
+                        if (Objects.nonNull(tag)) {
+                            ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class);
+                            tagOrderMap.putIfAbsent(tag.name(), apiSupport.order());
+                        }
+                    });
+            // 往openApi tags字段添加x-order增强属性
+            if (openApi.getTags() != null) {
+                openApi
+                        .getTags()
+                        .forEach(
+                                tag -> {
+                                    if (tagOrderMap.containsKey(tag.getName())) {
+                                        tag.addExtension(
+                                                ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName()));
+                                    }
+                                });
+            }
+        }
+    }
+
+    private Tag getTag(Class<?> clazz) {
+        // 从类上获取
+        Tag tag = clazz.getAnnotation(Tag.class);
+        if (Objects.isNull(tag)) {
+            // 从接口上获取
+            Class<?>[] interfaces = clazz.getInterfaces();
+            if (ArrayUtils.isNotEmpty(interfaces)) {
+                for (Class<?> interfaceClazz : interfaces) {
+                    Tag anno = interfaceClazz.getAnnotation(Tag.class);
+                    if (Objects.nonNull(anno)) {
+                        tag = anno;
+                        break;
+                    }
+                }
+            }
+        }
+        return tag;
+    }
+
+    private Set<Class<?>> scanPackageByAnnotation(
+            String packageName, final Class<? extends Annotation> annotationClass) {
+        ClassPathScanningCandidateComponentProvider scanner =
+                new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationClass));
+        Set<Class<?>> classes = new HashSet<>();
+        for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) {
+            try {
+                Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
+                classes.add(clazz);
+            } catch (ClassNotFoundException ignore) {
+
+            }
+        }
+        return classes;
+    }
+}

+ 1 - 1
helio-starter-rate-limit-redis/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 1 - 5
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByClientIPStrategy.java

@@ -6,7 +6,6 @@ import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant;
 import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy;
 import cc.uncarbon.framework.web.util.IPUtil;
 import cn.hutool.core.text.CharSequenceUtil;
-import cn.hutool.core.text.StrPool;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -31,10 +30,7 @@ public class RateLimitByClientIPStrategy extends SimpleRedisBasedRateLimitStrate
 
     @Override
     protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
-        final String splitter = StrPool.COLON;
-        // 编译器将自动优化
-        return RateLimitConstant.REDIS_KEY_PREFIX
-                + "ip" + splitter + currentClientIP() + splitter + determineMark(annotation, point);
+        return CharSequenceUtil.format("{}ip:{}:{}", RateLimitConstant.REDIS_KEY_PREFIX, currentClientIP(), determineMark(annotation, point));
     }
 
     /**

+ 2 - 5
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByUserIdStrategy.java

@@ -4,7 +4,7 @@ import cc.uncarbon.framework.core.context.UserContextHolder;
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
 import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant;
 import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy;
-import cn.hutool.core.text.StrPool;
+import cn.hutool.core.text.CharSequenceUtil;
 import cn.hutool.core.util.ObjectUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
@@ -30,10 +30,7 @@ public class RateLimitByUserIdStrategy extends SimpleRedisBasedRateLimitStrategy
 
     @Override
     protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
-        final String splitter = StrPool.COLON;
-        // 编译器将自动优化
-        return RateLimitConstant.REDIS_KEY_PREFIX
-                + "userId" + splitter + currentUserId() + splitter + determineMark(annotation, point);
+        return CharSequenceUtil.format("{}userId:{}:{}", RateLimitConstant.REDIS_KEY_PREFIX, currentUserId(), determineMark(annotation, point));
     }
 
     /**

+ 2 - 4
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitGlobalStrategy.java

@@ -3,7 +3,7 @@ package cc.uncarbon.framework.ratelimit.stratrgy.impl;
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
 import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant;
 import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy;
-import cn.hutool.core.text.StrPool;
+import cn.hutool.core.text.CharSequenceUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -28,8 +28,6 @@ public class RateLimitGlobalStrategy extends SimpleRedisBasedRateLimitStrategy i
      */
     @Override
     protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
-        final String splitter = StrPool.COLON;
-        // 由编译器自动使用StringBuilder拼接,下同
-        return RateLimitConstant.REDIS_KEY_PREFIX + "global" + splitter + determineMark(annotation, point);
+        return CharSequenceUtil.format("{}global:{}", RateLimitConstant.REDIS_KEY_PREFIX, determineMark(annotation, point));
     }
 }

+ 15 - 11
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/SimpleRedisBasedRateLimitStrategy.java

@@ -28,21 +28,25 @@ public abstract class SimpleRedisBasedRateLimitStrategy {
 
     /**
      * 预置限流Lua脚本
-     * 编译器将自动优化
      */
     private static final String LUA_SCRIPT = """
         local key = KEYS[1]
         local count = tonumber(ARGV[1])
         local time = tonumber(ARGV[2])
-        local current = redis.call('get', key);
-        if current and tonumber(current) > count then
-            return tonumber(current);
-        end
-        current = redis.call('incr', key)
-        if tonumber(current) == 1 then
-            redis.call('expire', key, time)
+        local current = redis.call('get', key)
+
+        if current then
+            current = tonumber(current)
+            if current > count then
+                return current
+            end
+            current = redis.call('incr', key)
+        else
+            current = 1
+            redis.call('set', key, current, 'EX', time)
         end
-        return tonumber(current);
+
+        return current
         """;
 
     /**
@@ -54,8 +58,8 @@ public abstract class SimpleRedisBasedRateLimitStrategy {
     protected void performRateLimitCheck(UseRateLimit annotation, JoinPoint point,
                                          Supplier<RateLimitedException> rateLimitedExceptionSupplier)
             throws RateLimitStrategyException {
-        long duration = annotation.duration();
-        long max = annotation.max();
+        int duration = annotation.duration();
+        int max = annotation.max();
 
         String redisKey = determineRedisKey(annotation, point);
         Long current = objectRedisTemplate.execute(REDIS_SCRIPT, Collections.singletonList(redisKey), max, duration);

+ 1 - 1
helio-starter-redis/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 9 - 1
helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/model/FormattedRedisKey.java

@@ -7,7 +7,7 @@ import cn.hutool.core.util.RandomUtil;
  * 已固定好键名的 Redis Key
  * @param key 已固定好的键名
  * @param keyType 键类型
- * @param valueClass 值类型
+ * @param valueClass 值类型【@deprecated 并没有实际用途,未来移除】
  * @param durationSeconds 有效时长,永久为-1,可通过 hasExpiration() 方法判断
  * @param <V>
  */
@@ -37,4 +37,12 @@ public record FormattedRedisKey<V>(
         }
         return (long)(RandomUtil.randomDouble(this.durationSeconds * minRatio, this.durationSeconds * maxRatio));
     }
+
+    /**
+     * @return durationSeconds的 int 形式
+     * @throws ArithmeticException 需要注意数值溢出风险
+     */
+    public int durationSecondsAsInt() throws ArithmeticException {
+        return Math.toIntExact(this.durationSeconds);
+    }
 }

+ 43 - 1
helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/model/RedisKeyDefinition.java

@@ -25,7 +25,9 @@ public class RedisKeyDefinition<V> {
 
     /**
      * 值类型对应 Class
+     * @deprecated 并没有实际用途,未来移除
      */
+    @Deprecated(since = "2.3.0", forRemoval = true)
     private final Class<V> valueClass;
 
     /**
@@ -34,12 +36,50 @@ public class RedisKeyDefinition<V> {
     private final long durationSeconds;
 
 
+    /**
+     * 不设置有效时长
+     * @param keyTemplate 键名模板,占位符需要用 {}
+     * @param keyType 键类型
+     */
+    public RedisKeyDefinition(String keyTemplate, KeyTypeEnum keyType) {
+        this.keyTemplate = keyTemplate;
+        this.keyType = keyType;
+        this.valueClass = null;
+        this.durationSeconds = -1;
+    }
+
+    /**
+     * 有设置有效时长
+     * @param keyTemplate 键名模板,占位符需要用 {}
+     * @param keyType 键类型
+     * @param duration 有效时长
+     * @param timeUnit 有效时长的单位,最低为秒
+     */
+    public RedisKeyDefinition(String keyTemplate, KeyTypeEnum keyType, long duration, TimeUnit timeUnit) {
+        if (timeUnit == TimeUnit.NANOSECONDS || timeUnit == TimeUnit.MICROSECONDS || timeUnit == TimeUnit.MILLISECONDS) {
+            throw new IllegalArgumentException("timeUnit CANNOT be NANOSECONDS or MICROSECONDS or MILLISECONDS");
+        }
+
+        this.keyTemplate = keyTemplate;
+        this.keyType = keyType;
+        this.valueClass = null;
+
+        // 兜底:最短缓存1s
+        long asSeconds = timeUnit.toSeconds(duration);
+        if (asSeconds < 1) {
+            asSeconds = 1;
+        }
+        this.durationSeconds = asSeconds;
+    }
+
     /**
      * 不设置有效时长
      * @param keyTemplate 键名模板,占位符需要用 {}
      * @param keyType 键类型
      * @param valueClass 值类型对应 Class
+     * @deprecated valueClass 并没有实际用途,未来移除
      */
+    @Deprecated(since = "2.3.0", forRemoval = true)
     public RedisKeyDefinition(String keyTemplate, KeyTypeEnum keyType, Class<V> valueClass) {
         this.keyTemplate = keyTemplate;
         this.keyType = keyType;
@@ -54,7 +94,9 @@ public class RedisKeyDefinition<V> {
      * @param valueClass 值类型对应 Class
      * @param duration 有效时长
      * @param timeUnit 有效时长的单位,最低为秒
+     * @deprecated valueClass 并没有实际用途,未来移除
      */
+    @Deprecated(since = "2.3.0", forRemoval = true)
     public RedisKeyDefinition(String keyTemplate, KeyTypeEnum keyType, Class<V> valueClass, long duration, TimeUnit timeUnit) {
         if (timeUnit == TimeUnit.NANOSECONDS || timeUnit == TimeUnit.MICROSECONDS || timeUnit == TimeUnit.MILLISECONDS) {
             throw new IllegalArgumentException("timeUnit CANNOT be NANOSECONDS or MICROSECONDS or MILLISECONDS");
@@ -78,6 +120,6 @@ public class RedisKeyDefinition<V> {
      */
     public FormattedRedisKey<V> format(Object... params) {
         return new FormattedRedisKey<>(CharSequenceUtil.format(this.keyTemplate, params),
-                this.keyType, this.valueClass, this.durationSeconds);
+                this.keyType, null, this.durationSeconds);
     }
 }

+ 1 - 1
helio-starter-rocketmq-aliyun/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 1 - 1
helio-starter-satoken/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 1 - 1
helio-starter-tenant/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
helio-starter-test/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 1 - 1
helio-starter-web/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 60 - 18
pom.xml

@@ -24,44 +24,51 @@
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>3.3.3</version>
-        <relativePath/>
+        <version>3.4.1</version>
     </parent>
 
     <groupId>cc.uncarbon.framework</groupId>
     <artifactId>helio-starters</artifactId>
-    <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+    <version>2.3.1</version><!-- BATCH REPLACEMENT MARK -->
 
     <properties>
         <!-- core versions -->
-        <helio-starters.version>2.2.0</helio-starters.version>
+        <helio-starters.version>2.3.1</helio-starters.version>
         <!-- 可根据实际JDK版本修改;JDK17则改为'17'  JDK21则改为'21' -->
         <java.version>17</java.version>
         <!-- 若变更Spring Boot版本, 上面parent依赖版本也要变 -->
-        <spring-boot.version>3.3.3</spring-boot.version>
+        <spring-boot.version>3.4.1</spring-boot.version>
 
 
         <!-- Spring Cloud versions -->
         <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
-        <spring-cloud.version>2023.0.3</spring-cloud.version>
-        <spring-cloud-starter-bootstrap.version>4.1.1</spring-cloud-starter-bootstrap.version>
+        <spring-cloud.version>2023.0.4</spring-cloud.version>
+        <spring-cloud-starter.version>4.2.0</spring-cloud-starter.version>
+        <spring-cloud-starter-bootstrap.version>${spring-cloud-starter.version}</spring-cloud-starter-bootstrap.version>
+        <spring-cloud-commons.version>${spring-cloud-starter.version}</spring-cloud-commons.version>
+        <spring-cloud-context.version>${spring-cloud-starter.version}</spring-cloud-context.version>
+
 
         <!-- Spring Cloud Alibaba versions -->
-        <spring-cloud-alibaba.version>2023.0.1.2</spring-cloud-alibaba.version>
-        <dubbo.version>3.2.11</dubbo.version>
-        <nacos-client.vesion>2.3.0</nacos-client.vesion>
+        <spring-cloud-alibaba.version>2023.0.3.2</spring-cloud-alibaba.version>
+        <dubbo.version>3.3.2</dubbo.version>
+        <nacos-client.vesion>2.4.3</nacos-client.vesion>
 
 
         <!-- 3rd-party versions -->
         <lombok.version>1.18.30</lombok.version>
         <slf4j.version>2.0.12</slf4j.version>
-        <hutool.version>5.8.31</hutool.version>
+        <hutool.version>5.8.34</hutool.version>
         <sa-token.version>1.39.0</sa-token.version>
         <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
         <!-- 如果 mp 较新版存在问题,可以回退至 3.5.5 试试  -->
         <mybatis-plus.version>3.5.7</mybatis-plus.version>
         <knife4j.version>4.5.0</knife4j.version>
-        <springdoc-annotations.version>2.2.20</springdoc-annotations.version>
+        <!-- 以下两个 SpringDoc 依赖不完全配套,但能适配 Spring Boot 3.4.x -->
+        <!-- https://central.sonatype.com/artifact/org.springdoc/springdoc-openapi-starter-common/版本号/dependencies -->
+        <springdoc-openapi-starter.version>2.7.0</springdoc-openapi-starter.version>
+        <springdoc-annotations.version>2.2.27</springdoc-annotations.version>
+
         <!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
         <redisson.version>3.33.0</redisson.version>
         <aspectj.version>1.9.22.1</aspectj.version>
@@ -245,6 +252,24 @@
 
 
             <!-- CLOUD dependencies -->
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-starter</artifactId>
+                <version>${spring-cloud-starter.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-commons</artifactId>
+                <version>${spring-cloud-commons.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-context</artifactId>
+                <version>${spring-cloud-context.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>org.springframework.cloud</groupId>
                 <artifactId>spring-cloud-starter-bootstrap</artifactId>
@@ -260,6 +285,14 @@
                         <groupId>com.alibaba.nacos</groupId>
                         <artifactId>nacos-client</artifactId>
                     </exclusion>
+                    <exclusion>
+                        <groupId>org.springframework.cloud</groupId>
+                        <artifactId>spring-cloud-commons</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.springframework.cloud</groupId>
+                        <artifactId>spring-cloud-context</artifactId>
+                    </exclusion>
                 </exclusions>
             </dependency>
 
@@ -275,12 +308,6 @@
                 </exclusions>
             </dependency>
 
-            <dependency>
-                <groupId>com.alibaba.cloud</groupId>
-                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
-                <version>${spring-cloud-alibaba.version}</version>
-            </dependency>
-
             <dependency>
                 <groupId>com.alibaba.nacos</groupId>
                 <artifactId>nacos-client</artifactId>
@@ -367,6 +394,13 @@
                 <version>${springdoc-annotations.version}</version>
             </dependency>
 
+            <!-- Knife4j 默认引入的 SpringDoc 版本还没有适配 Spring Boot 3.4.x,需要手动升级底层依赖 -->
+            <dependency>
+                <groupId>org.springdoc</groupId>
+                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+                <version>${springdoc-openapi-starter.version}</version>
+            </dependency>
+
 
             <!-- REDIS dependencies -->
             <dependency>
@@ -469,4 +503,12 @@
             </dependency>
         </dependencies>
     </dependencyManagement>
+
+    <!-- 仓库信息 -->
+    <scm>
+        <tag>2.x</tag>
+        <url>https://github.com/uncarbon97/helio-starters.git</url>
+        <connection>scm:git:https://github.com/uncarbon97/helio-starters.git</connection>
+        <developerConnection>scm:git:https://github.com/uncarbon97/helio-starters.git</developerConnection>
+    </scm>
 </project>