Forráskód Böngészése

bump: 发布2.2.0

details see: https://helio.uncarbon.cc/#/i18n/zh-CN/appendix/change-log
Uncarbon 1 éve
szülő
commit
8e2a3d4812
100 módosított fájl, 2760 hozzáadás és 2661 törlés
  1. 185 185
      checkstyle-v1.xml
  2. 83 83
      helio-core/pom.xml
  3. 26 26
      helio-core/src/main/java/cc/uncarbon/framework/core/cache/TimedCacheEx.java
  4. 24 22
      helio-core/src/main/java/cc/uncarbon/framework/core/config/StaticMethodAutoConfiguration.java
  5. 1 2
      helio-core/src/main/java/cc/uncarbon/framework/core/enums/EnabledStatusEnum.java
  6. 1 2
      helio-core/src/main/java/cc/uncarbon/framework/core/enums/GenderEnum.java
  7. 1 3
      helio-core/src/main/java/cc/uncarbon/framework/core/enums/IdGeneratorStrategyEnum.java
  8. 1 3
      helio-core/src/main/java/cc/uncarbon/framework/core/enums/TenantIsolateLevelEnum.java
  9. 1 2
      helio-core/src/main/java/cc/uncarbon/framework/core/enums/YesOrNoEnum.java
  10. 13 13
      helio-core/src/main/java/cc/uncarbon/framework/core/exception/HelioFrameworkException.java
  11. 12 12
      helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitStrategyException.java
  12. 36 36
      helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitedException.java
  13. 13 13
      helio-core/src/main/java/cc/uncarbon/framework/core/exception/UnreachableCodeException.java
  14. 28 28
      helio-core/src/main/java/cc/uncarbon/framework/core/function/StreamFunction.java
  15. 3 3
      helio-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  16. 1 1
      helio-starter-aop/pom.xml
  17. 9 9
      helio-starter-aop/src/main/java/cc/uncarbon/aop/stub/HelioStarterAopStub.java
  18. 50 50
      helio-starter-cloud/pom.xml
  19. 2 2
      helio-starter-cloud/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  20. 61 61
      helio-starter-crud/pom.xml
  21. 30 30
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/config/DynamicDataSourceAutoConfiguration.java
  22. 22 22
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/handler/HelioSequenceIdGenerateHandler.java
  23. 95 44
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/mapper/HelioBaseMapper.java
  24. 6 7
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/service/impl/HelioBaseServiceImpl.java
  25. 20 20
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/support/TenantSupport.java
  26. 16 16
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/support/impl/DefaultTenantSupport.java
  27. 2 2
      helio-starter-crud/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  28. 34 34
      helio-starter-dubbo/pom.xml
  29. 1 1
      helio-starter-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.validation.Validation
  30. 29 29
      helio-starter-i18n/pom.xml
  31. 192 192
      helio-starter-i18n/src/main/java/cc/uncarbon/framework/i18n/util/I18nUtil.java
  32. 41 41
      helio-starter-knife4j/pom.xml
  33. 168 86
      helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/customizer/HelioBaseEnumCustomizer.java
  34. 1 1
      helio-starter-knife4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  35. 47 47
      helio-starter-rate-limit-redis/pom.xml
  36. 21 21
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/constant/RateLimitConstant.java
  37. 27 27
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/RateLimitStrategy.java
  38. 64 64
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByClientIPStrategy.java
  39. 35 35
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitGlobalStrategy.java
  40. 91 89
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/SimpleRedisBasedRateLimitStrategy.java
  41. 4 4
      helio-starter-rate-limit-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  42. 35 35
      helio-starter-redis/pom.xml
  43. 28 28
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/enums/KeyTypeEnum.java
  44. 40 40
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/model/FormattedRedisKey.java
  45. 83 83
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/model/RedisKeyDefinition.java
  46. 68 68
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/template/RedisDistributedLockTemplate.java
  47. 76 76
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/template/impl/RedisDistributedLockTemplateImpl.java
  48. 1 1
      helio-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  49. 1 1
      helio-starter-rocketmq-aliyun/pom.xml
  50. 2 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/container/RocketProducerContainer.java
  51. 4 4
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/DefaultMessageListener.java
  52. 4 4
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/DefaultMessageOrderListener.java
  53. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ConsumerFactory.java
  54. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ConsumerPropertiesFactory.java
  55. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/MessageFactory.java
  56. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/MethodFactory.java
  57. 24 30
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerConsumerFactory.java
  58. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerFactory.java
  59. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerPropertiesFactory.java
  60. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/PropertiesFactory.java
  61. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/SendMessageFactory.java
  62. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ShardingKeyFactory.java
  63. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/StartDeliverTimeFactory.java
  64. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ThreadPoolFactory.java
  65. 5 8
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/ConsumerFactoryExecution.java
  66. 4 2
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/ProducerFactoryExecution.java
  67. 6 3
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/SendMessageFactoryExecution.java
  68. 3 2
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/strategy/RocketConsumerStrategy.java
  69. 3 7
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/strategy/SendMessageStrategy.java
  70. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/AnnotatedMethodsUtils.java
  71. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/ApplicationContextUtils.java
  72. 1 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/AspectUtils.java
  73. 7 3
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/InterceptRocket.java
  74. 5 2
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/thread/AbstractProducerThread.java
  75. 7 3
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/thread/AbstractSendMessageThread.java
  76. 1 1
      helio-starter-rocketmq-aliyun/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  77. 45 45
      helio-starter-satoken/pom.xml
  78. 27 27
      helio-starter-satoken/src/main/java/cc/uncarbon/framework/satoken/config/SaTokenLocalCacheAutoConfiguration.java
  79. 180 180
      helio-starter-satoken/src/main/java/cc/uncarbon/framework/satoken/dao/SaTokenLocalCacheDao.java
  80. 1 1
      helio-starter-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  81. 0 28
      helio-starter-seata/pom.xml
  82. 0 9
      helio-starter-seata/src/main/java/cc/uncarbon/framework/seata/stub/HelioStarterSeataStub.java
  83. 1 1
      helio-starter-tenant/pom.xml
  84. 22 22
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/annotation/EnableGlobalTenantDataSource.java
  85. 19 19
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/annotation/IgnoreTenantDataSource.java
  86. 1 2
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/config/GlobalTenantDataSourceConfiguration.java
  87. 45 46
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/config/HelioTenantAutoConfiguration.java
  88. 30 30
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantDataSourceSupport.java
  89. 90 90
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantLineSupport.java
  90. 60 60
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantTableSupport.java
  91. 53 53
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/tenantdatasource/GlobalTenantDataSourceInterceptor.java
  92. 1 1
      helio-starter-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  93. 28 28
      helio-starter-test/pom.xml
  94. 9 9
      helio-starter-test/src/main/java/cc/uncarbon/framework/test/stub/HelioStarterTestStub.java
  95. 61 61
      helio-starter-web/pom.xml
  96. 121 123
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/aspect/WebLoggingAspect.java
  97. 7 3
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/GlobalWebExceptionHandlerAutoConfiguration.java
  98. 33 35
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/enums/GlobalWebExceptionI18nMessageEnum.java
  99. 7 3
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/EnumModule.java
  100. 1 1
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/HelioJacksonModule.java

+ 185 - 185
checkstyle-v1.xml

@@ -1,185 +1,185 @@
-<?xml version="1.0"?>
-<!DOCTYPE module PUBLIC
-        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
-        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
-
-<module name="Checker">
-
-    <!-- 文件长度不超过2000行 -->
-    <module name="FileLength">
-        <property name="max" value="2000"/>
-    </module>
-
-    <!-- 每个java文件一个语法树 -->
-    <module name="TreeWalker">
-        <!-- import检查-->
-        <!-- 检查是否从非法的包中导入了类 -->
-        <module name="IllegalImport"/>
-        <!-- 检查是否导入了多余的包 -->
-        <module name="RedundantImport"/>
-        <!-- 没用的import检查,比如:1.没有被用到 2.重复的 3.import java.lang的 4.import 与该类在同一个package的 -->
-        <module name="UnusedImports" />
-
-        <!-- 注释检查 -->
-        <!-- 检查构造函数的javadoc -->
-        <!--
-        <module name="JavadocType">
-            <property name="allowUnknownTags" value="true"/>
-            <message key="javadoc.missing" value="类注释:缺少Javadoc注释。"/>
-        </module>
-        -->
-
-        <!-- 命名检查 -->
-        <!-- 局部的final变量,包括catch中的参数的检查 -->
-        <module name="LocalFinalVariableName" />
-        <!-- 局部的非final型的变量,包括catch中的参数的检查 -->
-        <module name="LocalVariableName" />
-        <!-- 包名的检查(只允许小写字母),默认^[a-z]+(\.[a-zA-Z_][a-zA-Z_0-9_]*)*$ -->
-        <module name="PackageName">
-            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
-            <message key="name.invalidPattern" value="包名 ''{0}'' 要符合 ''{1}''格式."/>
-        </module>
-        <!-- 仅仅是static型的变量(不包括static final型)的检查 -->
-        <module name="StaticVariableName" />
-        <!-- Class或Interface名检查,默认^[A-Z][a-zA-Z0-9]*$-->
-        <module name="TypeName">
-            <property name="severity" value="warning"/>
-            <message key="name.invalidPattern" value="名称 ''{0}'' 要符合 ''{1}''格式."/>
-        </module>
-        <!-- 非static型变量的检查 -->
-        <module name="MemberName" />
-        <!-- 方法名的检查 -->
-        <module name="MethodName" />
-        <!-- 方法的参数名 -->
-        <module name="ParameterName " />
-        <!-- 常量名的检查(只允许大写),默认^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$ -->
-        <module name="ConstantName" />
-
-        <!-- 定义检查 -->
-        <!-- 检查数组类型定义的样式 -->
-        <module name="ArrayTypeStyle"/>
-        <!-- 检查long型定义是否有大写的“L” -->
-        <module name="UpperEll"/>
-
-        <!-- 长度检查 -->
-        <!-- 每行不超过200个字符 -->
-        <module name="LineLength">
-            <property name="max" value="200" />
-        </module>
-        <!-- 方法不超过120行 -->
-        <module name="MethodLength">
-            <property name="tokens" value="METHOD_DEF" />
-            <property name="max" value="120" />
-        </module>
-        <!-- 方法的参数个数不超过5个。 并且不对构造方法进行检查-->
-        <module name="ParameterNumber">
-            <property name="max" value="5" />
-            <property name="ignoreOverriddenMethods" value="true"/>
-            <property name="tokens" value="METHOD_DEF" />
-        </module>
-
-        <!-- 空格检查-->
-        <!-- 方法名后跟左圆括号"(" -->
-        <module name="MethodParamPad" />
-        <!-- 在类型转换时,不允许左圆括号右边有空格,也不允许与右圆括号左边有空格 -->
-        <module name="TypecastParenPad" />
-        <!-- 检查在某个特定关键字之后应保留空格 -->
-        <module name="NoWhitespaceAfter"/>
-        <!-- 检查在某个特定关键字之前应保留空格 -->
-        <module name="NoWhitespaceBefore"/>
-        <!-- 操作符换行策略检查 -->
-        <module name="OperatorWrap"/>
-        <!-- 圆括号空白 -->
-        <module name="ParenPad"/>
-        <!-- 检查分隔符是否在空白之后 -->
-        <module name="WhitespaceAfter"/>
-        <!-- 检查分隔符周围是否有空白 -->
-        <module name="WhitespaceAround"/>
-
-        <!-- 修饰符检查 -->
-        <!-- 检查修饰符的顺序是否遵照java语言规范,默认public、protected、private、abstract、static、final、transient、volatile、synchronized、native、strictfp -->
-        <module name="ModifierOrder"/>
-        <!-- 检查接口和annotation中是否有多余修饰符,如接口方法不必使用public -->
-        <module name="RedundantModifier"/>
-
-        <!-- 代码块检查 -->
-        <!-- 检查是否有嵌套代码块 -->
-        <module name="AvoidNestedBlocks"/>
-        <!-- 检查是否有空代码块 -->
-        <module name="EmptyBlock"/>
-        <!-- 检查左大括号位置 -->
-        <module name="LeftCurly"/>
-        <!-- 检查代码块是否缺失{} -->
-        <module name="NeedBraces"/>
-        <!-- 检查右大括号位置 -->
-        <module name="RightCurly"/>
-
-        <!-- 代码检查 -->
-        <!-- 检查空的代码段 -->
-        <module name="EmptyStatement"/>
-        <!-- 检查在重写了equals方法后是否重写了hashCode方法 -->
-        <module name="EqualsHashCode"/>
-        <!-- 检查局部变量或参数是否隐藏了类中的变量 -->
-        <module name="HiddenField">
-            <property name="tokens" value="VARIABLE_DEF"/>
-        </module>
-        <!-- 检查子表达式中是否有赋值操作 -->
-        <module name="InnerAssignment"/>
-        <!-- 检查switch语句是否有default -->
-        <module name="MissingSwitchDefault"/>
-        <!-- 检查是否有过度复杂的布尔表达式 -->
-        <module name="SimplifyBooleanExpression"/>
-        <!-- 检查是否有过于复杂的布尔返回代码段 -->
-        <module name="SimplifyBooleanReturn"/>
-
-        <!-- 类设计检查 -->
-        <!-- 检查类是否为扩展设计l -->
-        <!-- 检查只有private构造函数的类是否声明为final -->
-        <module name="FinalClass"/>
-        <!-- 检查接口是否仅定义类型 -->
-        <module name="InterfaceIsType"/>
-        <!-- 检查类成员的可见度 检查类成员的可见性。只有static final 成员是public的
-        除非在本检查的protectedAllowed和packagedAllowed属性中进行了设置-->
-        <module name="VisibilityModifier">
-            <property name="packageAllowed" value="true"/>
-            <property name="protectedAllowed" value="true"/>
-        </module>
-
-        <!-- 语法 -->
-        <!-- String的比较不能用!= 和 == -->
-        <module name="StringLiteralEquality"/>
-        <!-- 限制for循环最多嵌套2层 -->
-        <module name="NestedForDepth">
-            <property name="max" value="2"/>
-        </module>
-        <!-- if最多嵌套3层 -->
-        <module name="NestedIfDepth">
-            <property name="max" value="3"/>
-        </module>
-        <!-- 检查未被注释的main方法,排除以Application、Test结尾命名的类 -->
-        <module name="UncommentedMain">
-            <property name="excludedClasses" value=".*[Application,Test]$"/>
-        </module>
-        <!-- 禁止使用System.out.println -->
-        <module name="Regexp">
-            <property name="format" value="System\.out\.println"/>
-            <property name="illegalPattern" value="true"/>
-        </module>
-        <!-- return个数 5个-->
-        <module name="ReturnCount">
-            <property name="max" value="5"/>
-        </module>
-        <!--try catch 异常处理数量 3-->
-        <module name="NestedTryDepth ">
-            <property name="max" value="3"/>
-        </module>
-        <!-- clone方法必须调用了super.clone() -->
-        <module name="SuperClone" />
-        <!-- finalize 必须调用了super.finalize() -->
-        <module name="SuperFinalize" />
-
-
-    </module>
-</module>
-<!-- https://blog.csdn.net/liuwei0376/article/details/123180920 -->
-<!-- 依赖checkstyle 8.14版本-->
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<module name="Checker">
+
+    <!-- 文件长度不超过2000行 -->
+    <module name="FileLength">
+        <property name="max" value="2000"/>
+    </module>
+
+    <!-- 每个java文件一个语法树 -->
+    <module name="TreeWalker">
+        <!-- import检查-->
+        <!-- 检查是否从非法的包中导入了类 -->
+        <module name="IllegalImport"/>
+        <!-- 检查是否导入了多余的包 -->
+        <module name="RedundantImport"/>
+        <!-- 没用的import检查,比如:1.没有被用到 2.重复的 3.import java.lang的 4.import 与该类在同一个package的 -->
+        <module name="UnusedImports" />
+
+        <!-- 注释检查 -->
+        <!-- 检查构造函数的javadoc -->
+        <!--
+        <module name="JavadocType">
+            <property name="allowUnknownTags" value="true"/>
+            <message key="javadoc.missing" value="类注释:缺少Javadoc注释。"/>
+        </module>
+        -->
+
+        <!-- 命名检查 -->
+        <!-- 局部的final变量,包括catch中的参数的检查 -->
+        <module name="LocalFinalVariableName" />
+        <!-- 局部的非final型的变量,包括catch中的参数的检查 -->
+        <module name="LocalVariableName" />
+        <!-- 包名的检查(只允许小写字母),默认^[a-z]+(\.[a-zA-Z_][a-zA-Z_0-9_]*)*$ -->
+        <module name="PackageName">
+            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
+            <message key="name.invalidPattern" value="包名 ''{0}'' 要符合 ''{1}''格式."/>
+        </module>
+        <!-- 仅仅是static型的变量(不包括static final型)的检查 -->
+        <module name="StaticVariableName" />
+        <!-- Class或Interface名检查,默认^[A-Z][a-zA-Z0-9]*$-->
+        <module name="TypeName">
+            <property name="severity" value="warning"/>
+            <message key="name.invalidPattern" value="名称 ''{0}'' 要符合 ''{1}''格式."/>
+        </module>
+        <!-- 非static型变量的检查 -->
+        <module name="MemberName" />
+        <!-- 方法名的检查 -->
+        <module name="MethodName" />
+        <!-- 方法的参数名 -->
+        <module name="ParameterName " />
+        <!-- 常量名的检查(只允许大写),默认^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$ -->
+        <module name="ConstantName" />
+
+        <!-- 定义检查 -->
+        <!-- 检查数组类型定义的样式 -->
+        <module name="ArrayTypeStyle"/>
+        <!-- 检查long型定义是否有大写的“L” -->
+        <module name="UpperEll"/>
+
+        <!-- 长度检查 -->
+        <!-- 每行不超过200个字符 -->
+        <module name="LineLength">
+            <property name="max" value="200" />
+        </module>
+        <!-- 方法不超过120行 -->
+        <module name="MethodLength">
+            <property name="tokens" value="METHOD_DEF" />
+            <property name="max" value="120" />
+        </module>
+        <!-- 方法的参数个数不超过5个。 并且不对构造方法进行检查-->
+        <module name="ParameterNumber">
+            <property name="max" value="5" />
+            <property name="ignoreOverriddenMethods" value="true"/>
+            <property name="tokens" value="METHOD_DEF" />
+        </module>
+
+        <!-- 空格检查-->
+        <!-- 方法名后跟左圆括号"(" -->
+        <module name="MethodParamPad" />
+        <!-- 在类型转换时,不允许左圆括号右边有空格,也不允许与右圆括号左边有空格 -->
+        <module name="TypecastParenPad" />
+        <!-- 检查在某个特定关键字之后应保留空格 -->
+        <module name="NoWhitespaceAfter"/>
+        <!-- 检查在某个特定关键字之前应保留空格 -->
+        <module name="NoWhitespaceBefore"/>
+        <!-- 操作符换行策略检查 -->
+        <module name="OperatorWrap"/>
+        <!-- 圆括号空白 -->
+        <module name="ParenPad"/>
+        <!-- 检查分隔符是否在空白之后 -->
+        <module name="WhitespaceAfter"/>
+        <!-- 检查分隔符周围是否有空白 -->
+        <module name="WhitespaceAround"/>
+
+        <!-- 修饰符检查 -->
+        <!-- 检查修饰符的顺序是否遵照java语言规范,默认public、protected、private、abstract、static、final、transient、volatile、synchronized、native、strictfp -->
+        <module name="ModifierOrder"/>
+        <!-- 检查接口和annotation中是否有多余修饰符,如接口方法不必使用public -->
+        <module name="RedundantModifier"/>
+
+        <!-- 代码块检查 -->
+        <!-- 检查是否有嵌套代码块 -->
+        <module name="AvoidNestedBlocks"/>
+        <!-- 检查是否有空代码块 -->
+        <module name="EmptyBlock"/>
+        <!-- 检查左大括号位置 -->
+        <module name="LeftCurly"/>
+        <!-- 检查代码块是否缺失{} -->
+        <module name="NeedBraces"/>
+        <!-- 检查右大括号位置 -->
+        <module name="RightCurly"/>
+
+        <!-- 代码检查 -->
+        <!-- 检查空的代码段 -->
+        <module name="EmptyStatement"/>
+        <!-- 检查在重写了equals方法后是否重写了hashCode方法 -->
+        <module name="EqualsHashCode"/>
+        <!-- 检查局部变量或参数是否隐藏了类中的变量 -->
+        <module name="HiddenField">
+            <property name="tokens" value="VARIABLE_DEF"/>
+        </module>
+        <!-- 检查子表达式中是否有赋值操作 -->
+        <module name="InnerAssignment"/>
+        <!-- 检查switch语句是否有default -->
+        <module name="MissingSwitchDefault"/>
+        <!-- 检查是否有过度复杂的布尔表达式 -->
+        <module name="SimplifyBooleanExpression"/>
+        <!-- 检查是否有过于复杂的布尔返回代码段 -->
+        <module name="SimplifyBooleanReturn"/>
+
+        <!-- 类设计检查 -->
+        <!-- 检查类是否为扩展设计l -->
+        <!-- 检查只有private构造函数的类是否声明为final -->
+        <module name="FinalClass"/>
+        <!-- 检查接口是否仅定义类型 -->
+        <module name="InterfaceIsType"/>
+        <!-- 检查类成员的可见度 检查类成员的可见性。只有static final 成员是public的
+        除非在本检查的protectedAllowed和packagedAllowed属性中进行了设置-->
+        <module name="VisibilityModifier">
+            <property name="packageAllowed" value="true"/>
+            <property name="protectedAllowed" value="true"/>
+        </module>
+
+        <!-- 语法 -->
+        <!-- String的比较不能用!= 和 == -->
+        <module name="StringLiteralEquality"/>
+        <!-- 限制for循环最多嵌套2层 -->
+        <module name="NestedForDepth">
+            <property name="max" value="2"/>
+        </module>
+        <!-- if最多嵌套3层 -->
+        <module name="NestedIfDepth">
+            <property name="max" value="3"/>
+        </module>
+        <!-- 检查未被注释的main方法,排除以Application、Test结尾命名的类 -->
+        <module name="UncommentedMain">
+            <property name="excludedClasses" value=".*[Application,Test]$"/>
+        </module>
+        <!-- 禁止使用System.out.println -->
+        <module name="Regexp">
+            <property name="format" value="System\.out\.println"/>
+            <property name="illegalPattern" value="true"/>
+        </module>
+        <!-- return个数 5个-->
+        <module name="ReturnCount">
+            <property name="max" value="5"/>
+        </module>
+        <!--try catch 异常处理数量 3-->
+        <module name="NestedTryDepth ">
+            <property name="max" value="3"/>
+        </module>
+        <!-- clone方法必须调用了super.clone() -->
+        <module name="SuperClone" />
+        <!-- finalize 必须调用了super.finalize() -->
+        <module name="SuperFinalize" />
+
+
+    </module>
+</module>
+<!-- https://blog.csdn.net/liuwei0376/article/details/123180920 -->
+<!-- 依赖checkstyle 8.14版本-->

+ 83 - 83
helio-core/pom.xml

@@ -1,83 +1,83 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-core</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- Spring Boot starter -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter</artifactId>
-            <exclusions>
-                <!-- 排除 Log4j 门面层 -->
-                <exclusion>
-                    <groupId>org.apache.logging.log4j</groupId>
-                    <artifactId>log4j-to-slf4j</artifactId>
-                </exclusion>
-
-                <!-- 排除 jul 门面层 -->
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>jul-to-slf4j</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <!-- Spring 自动配置处理器 -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-configuration-processor</artifactId>
-        </dependency>
-
-        <!-- Jackson 注解包 -->
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-annotations</artifactId>
-        </dependency>
-
-        <!-- javax validation 注解包 -->
-        <dependency>
-            <groupId>jakarta.validation</groupId>
-            <artifactId>jakarta.validation-api</artifactId>
-        </dependency>
-
-        <!-- TTL -->
-        <dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>transmittable-thread-local</artifactId>
-        </dependency>
-
-        <!-- hutool -->
-        <dependency>
-            <groupId>cn.hutool</groupId>
-            <artifactId>hutool-all</artifactId>
-        </dependency>
-
-        <!-- SpringDoc 注解包 -->
-        <dependency>
-            <groupId>io.swagger.core.v3</groupId>
-            <artifactId>swagger-annotations</artifactId>
-        </dependency>
-
-        <!-- mybatis-plus 注解包 -->
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>mybatis-plus-annotation</artifactId>
-        </dependency>
-    </dependencies>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-core</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- Spring Boot starter -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <exclusions>
+                <!-- 排除 Log4j 门面层 -->
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-to-slf4j</artifactId>
+                </exclusion>
+
+                <!-- 排除 jul 门面层 -->
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>jul-to-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- Spring 自动配置处理器 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+
+        <!-- Jackson 注解包 -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <!-- javax validation 注解包 -->
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+
+        <!-- TTL -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>transmittable-thread-local</artifactId>
+        </dependency>
+
+        <!-- hutool -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+
+        <!-- SpringDoc 注解包 -->
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+
+        <!-- mybatis-plus 注解包 -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-annotation</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 26 - 26
helio-core/src/main/java/cc/uncarbon/framework/core/cache/TimedCacheEx.java

@@ -1,26 +1,26 @@
-package cc.uncarbon.framework.core.cache;
-
-import cn.hutool.cache.impl.TimedCache;
-import cn.hutool.core.lang.mutable.MutableObj;
-
-/**
- * 继承自 hutool TimedCache
- * 增加了容量限制,重写了 containsKey 方法判断逻辑提升效率并支持空值
- * @param <K> key类型
- * @param <V> value类型
- *
- * @since 2.1.0
- * @author Uncarbon
- */
-public class TimedCacheEx<K, V> extends TimedCache<K, V> {
-
-    public TimedCacheEx(long timeout, int capacity) {
-        super(timeout);
-        this.capacity = capacity;
-    }
-
-    @Override
-    public boolean containsKey(K key) {
-        return this.cacheMap.containsKey(MutableObj.of(key));
-    }
-}
+package cc.uncarbon.framework.core.cache;
+
+import cn.hutool.cache.impl.TimedCache;
+import cn.hutool.core.lang.mutable.MutableObj;
+
+/**
+ * 继承自 hutool TimedCache
+ * 增加了容量限制,重写了 containsKey 方法判断逻辑提升效率并支持空值
+ * @param <K> key类型
+ * @param <V> value类型
+ *
+ * @since 2.1.0
+ * @author Uncarbon
+ */
+public class TimedCacheEx<K, V> extends TimedCache<K, V> {
+
+    public TimedCacheEx(long timeout, int capacity) {
+        super(timeout);
+        this.capacity = capacity;
+    }
+
+    @Override
+    public boolean containsKey(K key) {
+        return this.cacheMap.containsKey(MutableObj.of(key));
+    }
+}

+ 24 - 22
helio-core/src/main/java/cc/uncarbon/framework/core/config/StaticMethodAutoConfiguration.java

@@ -1,22 +1,24 @@
-package cc.uncarbon.framework.core.config;
-
-import cn.hutool.http.HttpGlobalConfig;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-
-/**
- * 静态工具方法自动配置类
- */
-@AutoConfiguration
-public class StaticMethodAutoConfiguration {
-
-    /**
-     * hutool HttpUtil 默认超时,单位毫秒
-     * 同时应用于 connectionTimeout 和 readTimeout
-     */
-    private static final int HUTOOL_HTTP_UTIL_DEFAULT_TIMEOUT = 30000;
-
-
-    public StaticMethodAutoConfiguration() {
-        HttpGlobalConfig.setTimeout(HUTOOL_HTTP_UTIL_DEFAULT_TIMEOUT);
-    }
-}
+package cc.uncarbon.framework.core.config;
+
+import cn.hutool.http.HttpGlobalConfig;
+import jakarta.annotation.PostConstruct;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+
+/**
+ * 静态工具方法自动配置类
+ */
+@AutoConfiguration
+public class StaticMethodAutoConfiguration {
+
+    /**
+     * hutool HttpUtil 默认超时,单位毫秒
+     * 同时应用于 connectionTimeout 和 readTimeout
+     */
+    private static final int HUTOOL_HTTP_UTIL_DEFAULT_TIMEOUT = 30000;
+
+
+    @PostConstruct
+    public void postConstruct() {
+        HttpGlobalConfig.setTimeout(HUTOOL_HTTP_UTIL_DEFAULT_TIMEOUT);
+    }
+}

+ 1 - 2
helio-core/src/main/java/cc/uncarbon/framework/core/enums/EnabledStatusEnum.java

@@ -14,9 +14,8 @@ import lombok.Getter;
 public enum EnabledStatusEnum implements HelioBaseEnum<Integer> {
 
     DISABLED(0, "禁用"),
-    ENABLED(1, "启用"),
+    ENABLED(1, "启用"),;
 
-    ;
     @EnumValue
     private final Integer value;
     private final String label;

+ 1 - 2
helio-core/src/main/java/cc/uncarbon/framework/core/enums/GenderEnum.java

@@ -14,9 +14,8 @@ public enum GenderEnum implements HelioBaseEnum<Integer> {
 
     UNKNOWN(0, "未知"),
     MALE(1, "男"),
-    FEMALE(2, "女"),
+    FEMALE(2, "女"),;
 
-    ;
     @EnumValue
     private final Integer value;
     private final String label;

+ 1 - 3
helio-core/src/main/java/cc/uncarbon/framework/core/enums/IdGeneratorStrategyEnum.java

@@ -20,9 +20,7 @@ public enum IdGeneratorStrategyEnum implements HelioBaseEnum<Integer> {
     /**
      * Mybatis-Plus 提供的 Sequence 算法(类雪花)
      */
-    SEQUENCE(2, "Mybatis-Plus Sequence"),
-
-    ;
+    SEQUENCE(2, "Mybatis-Plus Sequence"),;
 
     private final Integer value;
     private final String label;

+ 1 - 3
helio-core/src/main/java/cc/uncarbon/framework/core/enums/TenantIsolateLevelEnum.java

@@ -21,9 +21,7 @@ public enum TenantIsolateLevelEnum implements HelioBaseEnum<Integer> {
     /**
      * 数据源级,即每个租户使用独立的数据源
      */
-    DATASOURCE(2, "数据源级"),
-
-    ;
+    DATASOURCE(2, "数据源级"),;
 
     private final Integer value;
     private final String label;

+ 1 - 2
helio-core/src/main/java/cc/uncarbon/framework/core/enums/YesOrNoEnum.java

@@ -13,9 +13,8 @@ import lombok.Getter;
 public enum YesOrNoEnum implements HelioBaseEnum<Integer> {
 
     NO(0, "否"),
-    YES(1, "是"),
+    YES(1, "是"),;
 
-    ;
     @EnumValue
     private final Integer value;
     private final String label;

+ 13 - 13
helio-core/src/main/java/cc/uncarbon/framework/core/exception/HelioFrameworkException.java

@@ -1,13 +1,13 @@
-package cc.uncarbon.framework.core.exception;
-
-/**
- * Helio脚手架内部出错异常类
- *
- * @author Uncarbon
- */
-public class HelioFrameworkException extends RuntimeException {
-
-    public HelioFrameworkException(String msg) {
-        super(msg);
-    }
-}
+package cc.uncarbon.framework.core.exception;
+
+/**
+ * Helio脚手架内部出错异常类
+ *
+ * @author Uncarbon
+ */
+public class HelioFrameworkException extends RuntimeException {
+
+    public HelioFrameworkException(String msg) {
+        super(msg);
+    }
+}

+ 12 - 12
helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitStrategyException.java

@@ -1,12 +1,12 @@
-package cc.uncarbon.framework.core.exception;
-
-/**
- * 限流策略异常
- * 用于helio-starter-rate-limit-redis
- */
-public class RateLimitStrategyException extends HelioFrameworkException {
-
-    public RateLimitStrategyException(String msg) {
-        super(msg);
-    }
-}
+package cc.uncarbon.framework.core.exception;
+
+/**
+ * 限流策略异常
+ * 用于helio-starter-rate-limit-redis
+ */
+public class RateLimitStrategyException extends HelioFrameworkException {
+
+    public RateLimitStrategyException(String msg) {
+        super(msg);
+    }
+}

+ 36 - 36
helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitedException.java

@@ -1,36 +1,36 @@
-package cc.uncarbon.framework.core.exception;
-
-import cc.uncarbon.framework.core.enums.HelioBaseEnum;
-import cn.hutool.http.HttpStatus;
-import lombok.NonNull;
-
-/**
- * 已被限流异常
- * 用于helio-starter-rate-limit-redis
- */
-public class RateLimitedException extends BusinessException {
-
-    public RateLimitedException() {
-        super(HttpStatus.HTTP_TOO_MANY_REQUESTS, "操作频率不要太快");
-    }
-
-    public RateLimitedException(String msg) {
-        super(msg);
-    }
-
-    public RateLimitedException(int code, String msg) {
-        super(code, msg);
-    }
-
-    public RateLimitedException(int code, String msg, Object... templateParams) {
-        super(code, msg, templateParams);
-    }
-
-    public RateLimitedException(@NonNull HelioBaseEnum<Integer> customEnum) {
-        super(customEnum);
-    }
-
-    public RateLimitedException(@NonNull HelioBaseEnum<Integer> customEnum, Object... templateParams) {
-        super(customEnum, templateParams);
-    }
-}
+package cc.uncarbon.framework.core.exception;
+
+import cc.uncarbon.framework.core.enums.HelioBaseEnum;
+import cn.hutool.http.HttpStatus;
+import lombok.NonNull;
+
+/**
+ * 已被限流异常
+ * 用于helio-starter-rate-limit-redis
+ */
+public class RateLimitedException extends BusinessException {
+
+    public RateLimitedException() {
+        super(HttpStatus.HTTP_TOO_MANY_REQUESTS, "操作频率不要太快");
+    }
+
+    public RateLimitedException(String msg) {
+        super(msg);
+    }
+
+    public RateLimitedException(int code, String msg) {
+        super(code, msg);
+    }
+
+    public RateLimitedException(int code, String msg, Object... templateParams) {
+        super(code, msg, templateParams);
+    }
+
+    public RateLimitedException(@NonNull HelioBaseEnum<Integer> customEnum) {
+        super(customEnum);
+    }
+
+    public RateLimitedException(@NonNull HelioBaseEnum<Integer> customEnum, Object... templateParams) {
+        super(customEnum, templateParams);
+    }
+}

+ 13 - 13
helio-core/src/main/java/cc/uncarbon/framework/core/exception/UnreachableCodeException.java

@@ -1,13 +1,13 @@
-package cc.uncarbon.framework.core.exception;
-
-/**
- * 不可达代码异常类
- *
- * @author Uncarbon
- */
-public class UnreachableCodeException extends RuntimeException {
-
-    public UnreachableCodeException() {
-        super("Execution path unreachable");
-    }
-}
+package cc.uncarbon.framework.core.exception;
+
+/**
+ * 不可达代码异常类
+ *
+ * @author Uncarbon
+ */
+public class UnreachableCodeException extends RuntimeException {
+
+    public UnreachableCodeException() {
+        super("Execution path unreachable");
+    }
+}

+ 28 - 28
helio-core/src/main/java/cc/uncarbon/framework/core/function/StreamFunction.java

@@ -1,28 +1,28 @@
-package cc.uncarbon.framework.core.function;
-
-import java.util.function.BinaryOperator;
-
-/**
- * Stream 流函数
- * 其实更像是一个另类的工具方法集合
- *
- * @author Uncarbon
- */
-public final class StreamFunction {
-
-    private StreamFunction() {
-    }
-
-    /**
-     * 用于 stream-collect-toMap 时,直接覆盖已存在的 key;不忽略的话,遇到重复项时默认会抛出异常
-     * <p>
-     * 用法:stream().collect(Collectors.toMap(XXX, YYY, StreamFunction.ignoredThrowingMerger()))
-     *
-     * @param <T> 键类型
-     * @return 键名
-     */
-    public static <T> BinaryOperator<T> ignoredThrowingMerger() {
-        return (u, v) -> u;
-    }
-
-}
+package cc.uncarbon.framework.core.function;
+
+import java.util.function.BinaryOperator;
+
+/**
+ * Stream 流函数
+ * 其实更像是一个另类的工具方法集合
+ *
+ * @author Uncarbon
+ */
+public final class StreamFunction {
+
+    private StreamFunction() {
+    }
+
+    /**
+     * 用于 stream-collect-toMap 时,直接覆盖已存在的 key;不忽略的话,遇到重复项时默认会抛出异常
+     * <p>
+     * 用法:stream().collect(Collectors.toMap(XXX, YYY, StreamFunction.ignoredThrowingMerger()))
+     *
+     * @param <T> 键类型
+     * @return 键名
+     */
+    public static <T> BinaryOperator<T> ignoredThrowingMerger() {
+        return (u, v) -> u;
+    }
+
+}

+ 3 - 3
helio-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1,3 +1,3 @@
-cc.uncarbon.framework.core.config.HelioPropertiesAutoConfiguration
-cc.uncarbon.framework.core.config.AdaptHistoryVersionAutoConfiguration
-cc.uncarbon.framework.core.config.StaticMethodAutoConfiguration
+cc.uncarbon.framework.core.config.HelioPropertiesAutoConfiguration
+cc.uncarbon.framework.core.config.AdaptHistoryVersionAutoConfiguration
+cc.uncarbon.framework.core.config.StaticMethodAutoConfiguration

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

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

+ 9 - 9
helio-starter-aop/src/main/java/cc/uncarbon/aop/stub/HelioStarterAopStub.java

@@ -1,9 +1,9 @@
-package cc.uncarbon.aop.stub;
-
-/**
- * 需要一个空类以解决:Missing: no javadoc jar found in folder
- */
-public final class HelioStarterAopStub {
-    private HelioStarterAopStub() {
-    }
-}
+package cc.uncarbon.aop.stub;
+
+/**
+ * 需要一个空类以解决:Missing: no javadoc jar found in folder
+ */
+public final class HelioStarterAopStub {
+    private HelioStarterAopStub() {
+    }
+}

+ 50 - 50
helio-starter-cloud/pom.xml

@@ -1,50 +1,50 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-cloud</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-bootstrap</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.alibaba.cloud</groupId>
-            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.alibaba.cloud</groupId>
-            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.alibaba.nacos</groupId>
-            <artifactId>nacos-client</artifactId>
-            <version>${nacos-client.vesion}</version>
-        </dependency>
-    </dependencies>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-cloud</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+            <version>${nacos-client.vesion}</version>
+        </dependency>
+    </dependencies>
+</project>

+ 2 - 2
helio-starter-cloud/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1,2 +1,2 @@
-cc.uncarbon.framework.cloud.config.NacosDiscoveryAutoConfiguration
-cc.uncarbon.framework.cloud.config.NacosConfigAutoConfiguration
+cc.uncarbon.framework.cloud.config.NacosDiscoveryAutoConfiguration
+cc.uncarbon.framework.cloud.config.NacosConfigAutoConfiguration

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

@@ -1,61 +1,61 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-crud</artifactId>
-
-    <properties>
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.mysql</groupId>
-            <artifactId>mysql-connector-j</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.postgresql</groupId>
-            <artifactId>postgresql</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.oracle.database.jdbc</groupId>
-            <artifactId>ojdbc8</artifactId>
-            <optional>true</optional>
-        </dependency>
-
-        <dependency>
-            <groupId>com.microsoft.sqlserver</groupId>
-            <artifactId>mssql-jdbc</artifactId>
-            <optional>true</optional>
-        </dependency>
-
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
-            <optional>true</optional>
-        </dependency>
-    </dependencies>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-crud</artifactId>
+
+    <properties>
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.oracle.database.jdbc</groupId>
+            <artifactId>ojdbc8</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+</project>

+ 30 - 30
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/config/DynamicDataSourceAutoConfiguration.java

@@ -1,30 +1,30 @@
-package cc.uncarbon.framework.crud.config;
-
-import cc.uncarbon.framework.crud.dynamicdatasource.DataSourceDefinitionProvider;
-import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegistry;
-import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
-import com.baomidou.dynamic.datasource.creator.HikariDataSourceCreator;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.context.annotation.Bean;
-
-/**
- * 动态数据源自动配置类
- *
- * @author Uncarbon
- */
-@ConditionalOnClass(name = "com.baomidou.dynamic.datasource.DynamicRoutingDataSource")
-public class DynamicDataSourceAutoConfiguration {
-
-    @ConditionalOnBean(value = DynamicRoutingDataSource.class)
-    @Bean
-    public HelioDynamicDataSourceRegistry helioDynamicDataSourceRegistry(
-            DynamicRoutingDataSource dynamicRoutingDataSource,
-            HikariDataSourceCreator dataSourceCreator,
-            ObjectProvider<DataSourceDefinitionProvider> dataSourceDefinitionProviders
-    ) {
-        return new HelioDynamicDataSourceRegistry(
-                dynamicRoutingDataSource, dataSourceCreator, dataSourceDefinitionProviders);
-    }
-}
+package cc.uncarbon.framework.crud.config;
+
+import cc.uncarbon.framework.crud.dynamicdatasource.DataSourceDefinitionProvider;
+import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegistry;
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.creator.HikariDataSourceCreator;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * 动态数据源自动配置类
+ *
+ * @author Uncarbon
+ */
+@ConditionalOnClass(name = "com.baomidou.dynamic.datasource.DynamicRoutingDataSource")
+public class DynamicDataSourceAutoConfiguration {
+
+    @ConditionalOnBean(value = DynamicRoutingDataSource.class)
+    @Bean
+    public HelioDynamicDataSourceRegistry helioDynamicDataSourceRegistry(
+            DynamicRoutingDataSource dynamicRoutingDataSource,
+            HikariDataSourceCreator dataSourceCreator,
+            ObjectProvider<DataSourceDefinitionProvider> dataSourceDefinitionProviders
+    ) {
+        return new HelioDynamicDataSourceRegistry(
+                dynamicRoutingDataSource, dataSourceCreator, dataSourceDefinitionProviders);
+    }
+}

+ 22 - 22
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/handler/HelioSequenceIdGenerateHandler.java

@@ -1,22 +1,22 @@
-package cc.uncarbon.framework.crud.handler;
-
-import cc.uncarbon.framework.core.enums.IdGeneratorStrategyEnum;
-import cn.hutool.core.net.NetUtil;
-import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 自定义ID生成器 - Mybatis-Plus Sequence
- *
- * @author Uncarbon
- */
-@Slf4j
-public class HelioSequenceIdGenerateHandler extends DefaultIdentifierGenerator {
-
-    public HelioSequenceIdGenerateHandler() {
-        super(NetUtil.getLocalhost());
-        log.info("[主键ID生成器] >> strategy=[{}]",
-                IdGeneratorStrategyEnum.SEQUENCE
-        );
-    }
-}
+package cc.uncarbon.framework.crud.handler;
+
+import cc.uncarbon.framework.core.enums.IdGeneratorStrategyEnum;
+import cn.hutool.core.net.NetUtil;
+import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 自定义ID生成器 - Mybatis-Plus Sequence
+ *
+ * @author Uncarbon
+ */
+@Slf4j
+public class HelioSequenceIdGenerateHandler extends DefaultIdentifierGenerator {
+
+    public HelioSequenceIdGenerateHandler() {
+        super(NetUtil.getLocalhost());
+        log.info("[主键ID生成器] >> strategy=[{}]",
+                IdGeneratorStrategyEnum.SEQUENCE
+        );
+    }
+}

+ 95 - 44
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/mapper/HelioBaseMapper.java

@@ -1,44 +1,95 @@
-package cc.uncarbon.framework.crud.mapper;
-
-import cc.uncarbon.framework.core.function.StreamFunction;
-import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
-import cn.hutool.core.collection.CollUtil;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * 持久层基础模板,在Mybatis-Plus BaseMapper上进一步增加功能
- * @param <E> 实体类
- * @param <T> 实体类主键ID类型
- *
- * @author Uncarbon
- */
-public interface HelioBaseMapper<E extends HelioBaseEntity<T>, T extends Serializable> extends BaseMapper<E> {
-
-    /**
-     * 根据IDs查询
-     */
-    default List<E> selectByIds(Collection<T> ids) {
-        return selectBatchIds(ids);
-    }
-
-    /**
-     * 取ID👉名 映射map
-     */
-    default Map<T, String> getNameMap(Collection<T> ids,
-                                      Function<E, T> idMapper, Function<E, String> nameMapper) {
-        List<E> entityList = selectBatchIds(ids);
-        if (CollUtil.isEmpty(entityList)) {
-            return Collections.emptyMap();
-        }
-        return entityList.stream()
-                .collect(Collectors.toMap(idMapper, nameMapper, StreamFunction.ignoredThrowingMerger()));
-    }
-}
+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;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+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.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 持久层基础模板,在Mybatis-Plus BaseMapper上进一步增加功能
+ *
+ * @param <E> 实体类
+ * @author Uncarbon
+ */
+public interface HelioBaseMapper<E extends HelioBaseEntity<? extends Serializable>> extends BaseMapper<E> {
+
+    /**
+     * 根据IDs查询
+     *
+     * @deprecated use selectBatchIds()
+     */
+    @Deprecated(since = "2.2.0", forRemoval = true)
+    default List<E> selectByIds(Collection<? extends Serializable> ids) {
+        return selectBatchIds(ids);
+    }
+
+    /**
+     * 取ID👉名 映射map
+     *
+     * @deprecated use selectToMap()
+     */
+    @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) {
+        List<E> entityList = selectBatchIds(ids);
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyMap();
+        }
+        return entityList.stream()
+                .collect(Collectors.toMap(idMapper, nameMapper, StreamFunction.ignoredThrowingMerger()));
+    }
+
+    /**
+     * 快速构建业务中常用的查库并转map,常用于根据ID补全名称等业务场景
+     * 如:selectToMap(ids, Entity::getId, Entity::getName)
+     */
+    default <K, V> Map<K, V> selectToMap(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.toMap(keyFieldGetter, valueFieldGetter, StreamFunction.ignoredThrowingMerger()));
+    }
+
+    /**
+     * 快速构建业务中常用的查库并转map,常用于根据ID补全名称等业务场景
+     * 如:selectToMap(ids, GoodsExtBO.class, Entity::getId, Entity::getName, Entity::getType)
+     */
+    default <K, V, B> Map<K, B> selectToMap(Collection<K> keys, Class<B> javaBeanClass,
+                                            SFunction<E, K> keyFieldGetter, SFunction<E, V>... valueFieldGetters) {
+        if (CollUtil.isEmpty(keys)) {
+            return Map.of();
+        }
+        SFunction<E, ?>[] getters = ArrayUtil.append(valueFieldGetters, keyFieldGetter);
+        List<E> entityList = selectList(
+                new LambdaQueryWrapper<E>()
+                        .select(getters)
+                        .in(keyFieldGetter, keys)
+        );
+        if (CollUtil.isEmpty(entityList)) {
+            return Map.of();
+        }
+        return entityList.stream().collect(Collectors.toMap(keyFieldGetter, entity -> BeanUtil.copyProperties(entity, javaBeanClass),
+                StreamFunction.ignoredThrowingMerger()));
+    }
+}

+ 6 - 7
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/service/impl/HelioBaseServiceImpl.java

@@ -2,11 +2,14 @@ package cc.uncarbon.framework.crud.service.impl;
 
 import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
 import cc.uncarbon.framework.crud.service.HelioBaseService;
+import cn.hutool.core.util.ReflectUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.NoArgsConstructor;
 
+import java.util.Objects;
+
 /**
  * 服务实现类基础模板
  * @param <M> 持久层接口
@@ -21,17 +24,13 @@ public class HelioBaseServiceImpl<M extends BaseMapper<E>, E extends HelioBaseEn
 
     /**
      * 解决MP自带的本方法,自动填充字段不生效问题
-     * 参考: https://cloud.tencent.com/developer/article/1930108
+     * <a href="https://cloud.tencent.com/developer/article/1930108">参考文章</a>
      */
     @Override
     public boolean update(Wrapper<E> updateWrapper) {
         E entity = updateWrapper.getEntity();
-        if (null == entity) {
-            try {
-                entity = this.currentModelClass().newInstance();
-            } catch (InstantiationException | IllegalAccessException e) {
-                // ignored
-            }
+        if (Objects.isNull(entity)) {
+            entity = ReflectUtil.newInstance(this.getEntityClass());
         }
         return update(entity, updateWrapper);
     }

+ 20 - 20
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/support/TenantSupport.java

@@ -1,20 +1,20 @@
-package cc.uncarbon.framework.crud.support;
-
-import cc.uncarbon.framework.core.props.HelioProperties;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-
-/**
- * Mybatis-Plus 多租户支持接口
- *
- * @author Uncarbon
- */
-public interface TenantSupport {
-
-    /**
-     * 不同的多租户隔离级别分别实现本方法,按需添加 SQL 拦截器
-     * @param helioProperties Helio 配置属性
-     * @param interceptor Mybatis-Plus 拦截器
-     */
-    void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor);
-
-}
+package cc.uncarbon.framework.crud.support;
+
+import cc.uncarbon.framework.core.props.HelioProperties;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+
+/**
+ * Mybatis-Plus 多租户支持接口
+ *
+ * @author Uncarbon
+ */
+public interface TenantSupport {
+
+    /**
+     * 不同的多租户隔离级别分别实现本方法,按需添加 SQL 拦截器
+     * @param helioProperties Helio 配置属性
+     * @param interceptor Mybatis-Plus 拦截器
+     */
+    void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor);
+
+}

+ 16 - 16
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/support/impl/DefaultTenantSupport.java

@@ -1,16 +1,16 @@
-package cc.uncarbon.framework.crud.support.impl;
-
-import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.crud.support.TenantSupport;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-
-/**
- * 多租户支持-默认处理
- */
-public class DefaultTenantSupport implements TenantSupport {
-
-    @Override
-    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
-        System.err.println("\n[多租户支持] >> 您启用了多租户,但未引入 helio-starter-tenant,无法对 SQL 进行拦截处理\n");
-    }
-}
+package cc.uncarbon.framework.crud.support.impl;
+
+import cc.uncarbon.framework.core.props.HelioProperties;
+import cc.uncarbon.framework.crud.support.TenantSupport;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+
+/**
+ * 多租户支持-默认处理
+ */
+public class DefaultTenantSupport implements TenantSupport {
+
+    @Override
+    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
+        System.err.println("\n[多租户支持] >> 您启用了多租户,但未引入 helio-starter-tenant,无法对 SQL 进行拦截处理\n");
+    }
+}

+ 2 - 2
helio-starter-crud/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1,2 +1,2 @@
-cc.uncarbon.framework.crud.config.HelioMybatisPlusAutoConfiguration
-cc.uncarbon.framework.crud.config.DynamicDataSourceAutoConfiguration
+cc.uncarbon.framework.crud.config.HelioMybatisPlusAutoConfiguration
+cc.uncarbon.framework.crud.config.DynamicDataSourceAutoConfiguration

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

@@ -1,34 +1,34 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-dubbo</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-spring-boot-starter</artifactId>
-        </dependency>
-    </dependencies>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-dubbo</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-spring-boot-starter</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 1 - 1
helio-starter-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.validation.Validation

@@ -1 +1 @@
-jvalidation=org.apache.dubbo.validation.support.jvalidation.JValidationNew
+jvalidation=org.apache.dubbo.validation.support.jvalidation.JValidationNew

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

@@ -1,29 +1,29 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-i18n</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-    </dependencies>
-
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-i18n</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 192 - 192
helio-starter-i18n/src/main/java/cc/uncarbon/framework/i18n/util/I18nUtil.java

@@ -1,192 +1,192 @@
-package cc.uncarbon.framework.i18n.util;
-
-import cc.uncarbon.framework.core.enums.HelioBaseEnum;
-import cn.hutool.core.text.CharSequenceUtil;
-import cn.hutool.core.text.StrPool;
-import cn.hutool.extra.spring.SpringUtil;
-import lombok.experimental.UtilityClass;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.MessageSource;
-import org.springframework.context.NoSuchMessageException;
-
-import java.util.Locale;
-import java.util.function.Supplier;
-
-/**
- * 获取 i18n 资源
- *
- * @author Lion Li
- * @author Uncarbon
- */
-@UtilityClass
-@Slf4j
-public class I18nUtil {
-
-    /**
-     * Spring提供的MessageSource
-     */
-    private static final MessageSource MESSAGE_SOURCE = SpringUtil.getBean(MessageSource.class);
-
-    /**
-     * Slf4j风格的占位符
-     */
-    private static final String SLF4J_STYLE_PLACEHOLDER = StrPool.DELIM_START + StrPool.DELIM_END;
-
-    /**
-     * Locale实例对象提供者
-     */
-    private Supplier<Locale> localeSupplier = I18nUtil::getDefaultLocale;
-
-
-    /**
-     * 取Spring提供的MessageSource
-     */
-    public MessageSource getMessageSource() {
-        return MESSAGE_SOURCE;
-    }
-
-    /**
-     * 取默认Locale
-     */
-    public Locale getDefaultLocale() {
-        return Locale.getDefault();
-    }
-
-    /**
-     * 取Locale实例对象
-     * 由localeSupplier产生返回值
-     */
-    public Locale determineLocale() {
-        return localeSupplier.get();
-    }
-
-    /**
-     * 置Locale实例对象提供者
-     */
-    public synchronized void setLocaleSupplier(Supplier<Locale> localeSupplier) {
-        I18nUtil.localeSupplier = localeSupplier;
-    }
-
-    /**
-     * 根据消息键和参数,获取国际化翻译值;默认使用当前JVM的默认locale
-     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
-     *
-     * @param code 消息代码
-     * @param templateParams 模板填充参数
-     * @return null or 国际化翻译值
-     */
-    public String messageOf(String code, Object... templateParams) {
-        return messageOf(determineLocale(), code, templateParams);
-    }
-
-    /**
-     * 根据消息键和参数,获取国际化翻译值
-     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
-     *
-     * @param locale locale
-     * @param code 消息代码
-     * @param templateParams 模板填充参数
-     * @return null or 国际化翻译值
-     */
-    public String messageOf(Locale locale, String code, Object... templateParams) {
-        if (code == null) {
-            return null;
-        }
-
-        try {
-            String msg = getMessageSource().getMessage(code, null, locale != null ? locale : determineLocale());
-            if (CharSequenceUtil.isEmpty(msg)) {
-                return msg;
-            }
-
-            if (CharSequenceUtil.contains(msg, SLF4J_STYLE_PLACEHOLDER)) {
-                // 使用 hutool 的模板填充
-                return CharSequenceUtil.format(msg, templateParams);
-            }
-
-            return msg;
-        } catch (NoSuchMessageException nsme) {
-            // 未找到对应国际化翻译值
-        }
-        return null;
-    }
-
-    /**
-     * 根据消息键和参数,获取国际化翻译值;默认使用当前JVM的默认locale
-     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
-     *
-     * @param code 消息代码
-     * @param defaultValue 未找到对应国际化翻译值的情况下,默认返回值
-     * @param templateParams 模板填充参数
-     * @return null or 国际化翻译值
-     */
-    public String messageOf(String code, String defaultValue, Object... templateParams) {
-        return messageOf(determineLocale(), code, defaultValue, templateParams);
-    }
-
-    /**
-     * 根据消息键和参数,获取国际化翻译值
-     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
-     *
-     * @param locale locale
-     * @param code 消息代码
-     * @param defaultValue 未找到对应国际化翻译值的情况下,默认返回值
-     * @param templateParams 模板填充参数
-     * @return null or 国际化翻译值
-     */
-    public String messageOf(Locale locale, String code, String defaultValue, Object... templateParams) {
-        String msg = messageOf(locale, code, templateParams);
-        return msg == null ? defaultValue : msg;
-    }
-
-    /**
-     * 根据枚举值和参数,获取国际化翻译值;默认使用当前JVM的默认locale
-     * 支持模板填充,如: Nickname '{}' has been existing, do you like '{}'?
-     *
-     * @param enumField 枚举值
-     * @param templateParams 模板填充参数
-     * @return null or 国际化翻译值
-     */
-    public String messageOf(Enum<?> enumField, Object... templateParams) {
-        return messageOf(determineLocale(), enumField, templateParams);
-    }
-
-    /**
-     * 根据枚举值和参数,获取国际化翻译值
-     * 支持模板填充,如: Nickname '{}' has been existing, do you like '{}'?
-     *
-     * @param locale locale
-     * @param enumField 枚举值
-     * @param templateParams 模板填充参数
-     * @return null or 国际化翻译值
-     */
-    public String messageOf(Locale locale, Enum<?> enumField, Object... templateParams) {
-        if (enumField == null) {
-            return null;
-        }
-
-        String i18nCode;
-
-        // 尝试以较长的 枚举类短名.枚举值name 为 code 尝试获取翻译值
-        i18nCode = String.format("%s.%s", enumField.getDeclaringClass().getSimpleName(), enumField.name());
-        String i18nMessage = messageOf(locale, i18nCode, templateParams);
-        if (CharSequenceUtil.isNotEmpty(i18nMessage)) {
-            return i18nMessage;
-        }
-
-        // 尝试以 枚举值name 为 code 尝试获取翻译值
-        i18nCode = enumField.name();
-        i18nMessage = messageOf(locale, i18nCode, templateParams);
-        if (CharSequenceUtil.isNotEmpty(i18nMessage)) {
-            return i18nMessage;
-        }
-
-        // 以上都没找到,兜底:如果是 HelioBaseEnum 的实现,直接返回 label 值
-        if (HelioBaseEnum.class.isAssignableFrom(enumField.getDeclaringClass())) {
-            return ((HelioBaseEnum<?>) enumField).getLabel();
-        }
-
-        // 最终兜底:返回枚举值 name
-        return enumField.name();
-    }
-}
+package cc.uncarbon.framework.i18n.util;
+
+import cc.uncarbon.framework.core.enums.HelioBaseEnum;
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.text.StrPool;
+import cn.hutool.extra.spring.SpringUtil;
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.MessageSource;
+import org.springframework.context.NoSuchMessageException;
+
+import java.util.Locale;
+import java.util.function.Supplier;
+
+/**
+ * 获取 i18n 资源
+ *
+ * @author Lion Li
+ * @author Uncarbon
+ */
+@UtilityClass
+@Slf4j
+public class I18nUtil {
+
+    /**
+     * Spring提供的MessageSource
+     */
+    private static final MessageSource MESSAGE_SOURCE = SpringUtil.getBean(MessageSource.class);
+
+    /**
+     * Slf4j风格的占位符
+     */
+    private static final String SLF4J_STYLE_PLACEHOLDER = StrPool.DELIM_START + StrPool.DELIM_END;
+
+    /**
+     * Locale实例对象提供者
+     */
+    private Supplier<Locale> localeSupplier = I18nUtil::getDefaultLocale;
+
+
+    /**
+     * 取Spring提供的MessageSource
+     */
+    public MessageSource getMessageSource() {
+        return MESSAGE_SOURCE;
+    }
+
+    /**
+     * 取默认Locale
+     */
+    public Locale getDefaultLocale() {
+        return Locale.getDefault();
+    }
+
+    /**
+     * 取Locale实例对象
+     * 由localeSupplier产生返回值
+     */
+    public Locale determineLocale() {
+        return localeSupplier.get();
+    }
+
+    /**
+     * 置Locale实例对象提供者
+     */
+    public synchronized void setLocaleSupplier(Supplier<Locale> localeSupplier) {
+        I18nUtil.localeSupplier = localeSupplier;
+    }
+
+    /**
+     * 根据消息键和参数,获取国际化翻译值;默认使用当前JVM的默认locale
+     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
+     *
+     * @param code 消息代码
+     * @param templateParams 模板填充参数
+     * @return null or 国际化翻译值
+     */
+    public String messageOf(String code, Object... templateParams) {
+        return messageOf(determineLocale(), code, templateParams);
+    }
+
+    /**
+     * 根据消息键和参数,获取国际化翻译值
+     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
+     *
+     * @param locale locale
+     * @param code 消息代码
+     * @param templateParams 模板填充参数
+     * @return null or 国际化翻译值
+     */
+    public String messageOf(Locale locale, String code, Object... templateParams) {
+        if (code == null) {
+            return null;
+        }
+
+        try {
+            String msg = getMessageSource().getMessage(code, null, locale != null ? locale : determineLocale());
+            if (CharSequenceUtil.isEmpty(msg)) {
+                return msg;
+            }
+
+            if (CharSequenceUtil.contains(msg, SLF4J_STYLE_PLACEHOLDER)) {
+                // 使用 hutool 的模板填充
+                return CharSequenceUtil.format(msg, templateParams);
+            }
+
+            return msg;
+        } catch (NoSuchMessageException nsme) {
+            // 未找到对应国际化翻译值
+        }
+        return null;
+    }
+
+    /**
+     * 根据消息键和参数,获取国际化翻译值;默认使用当前JVM的默认locale
+     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
+     *
+     * @param code 消息代码
+     * @param defaultValue 未找到对应国际化翻译值的情况下,默认返回值
+     * @param templateParams 模板填充参数
+     * @return null or 国际化翻译值
+     */
+    public String messageOf(String code, String defaultValue, Object... templateParams) {
+        return messageOf(determineLocale(), code, defaultValue, templateParams);
+    }
+
+    /**
+     * 根据消息键和参数,获取国际化翻译值
+     * 支持模板填充,如: Nickname '{}' is already exist, do you like '{}'?
+     *
+     * @param locale locale
+     * @param code 消息代码
+     * @param defaultValue 未找到对应国际化翻译值的情况下,默认返回值
+     * @param templateParams 模板填充参数
+     * @return null or 国际化翻译值
+     */
+    public String messageOf(Locale locale, String code, String defaultValue, Object... templateParams) {
+        String msg = messageOf(locale, code, templateParams);
+        return msg == null ? defaultValue : msg;
+    }
+
+    /**
+     * 根据枚举值和参数,获取国际化翻译值;默认使用当前JVM的默认locale
+     * 支持模板填充,如: Nickname '{}' has been existing, do you like '{}'?
+     *
+     * @param enumField 枚举值
+     * @param templateParams 模板填充参数
+     * @return null or 国际化翻译值
+     */
+    public String messageOf(Enum<?> enumField, Object... templateParams) {
+        return messageOf(determineLocale(), enumField, templateParams);
+    }
+
+    /**
+     * 根据枚举值和参数,获取国际化翻译值
+     * 支持模板填充,如: Nickname '{}' has been existing, do you like '{}'?
+     *
+     * @param locale locale
+     * @param enumField 枚举值
+     * @param templateParams 模板填充参数
+     * @return null or 国际化翻译值
+     */
+    public String messageOf(Locale locale, Enum<?> enumField, Object... templateParams) {
+        if (enumField == null) {
+            return null;
+        }
+
+        String i18nCode;
+
+        // 尝试以较长的 枚举类短名.枚举值name 为 code 尝试获取翻译值
+        i18nCode = String.format("%s.%s", enumField.getDeclaringClass().getSimpleName(), enumField.name());
+        String i18nMessage = messageOf(locale, i18nCode, templateParams);
+        if (CharSequenceUtil.isNotEmpty(i18nMessage)) {
+            return i18nMessage;
+        }
+
+        // 尝试以 枚举值name 为 code 尝试获取翻译值
+        i18nCode = enumField.name();
+        i18nMessage = messageOf(locale, i18nCode, templateParams);
+        if (CharSequenceUtil.isNotEmpty(i18nMessage)) {
+            return i18nMessage;
+        }
+
+        // 以上都没找到,兜底:如果是 HelioBaseEnum 的实现,直接返回 label 值
+        if (HelioBaseEnum.class.isAssignableFrom(enumField.getDeclaringClass())) {
+            return ((HelioBaseEnum<?>) enumField).getLabel();
+        }
+
+        // 最终兜底:返回枚举值 name
+        return enumField.name();
+    }
+}

+ 41 - 41
helio-starter-knife4j/pom.xml

@@ -1,41 +1,41 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-knife4j</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>com.github.xiaoymin</groupId>
-            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
-            <exclusions>
-                <!-- 用不到 -->
-                <exclusion>
-                    <groupId>org.webjars</groupId>
-                    <artifactId>swagger-ui</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-    </dependencies>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-knife4j</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
+            <exclusions>
+                <!-- 用不到 -->
+                <exclusion>
+                    <groupId>org.webjars</groupId>
+                    <artifactId>swagger-ui</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+</project>

+ 168 - 86
helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/customizer/HelioBaseEnumCustomizer.java

@@ -1,86 +1,168 @@
-package cc.uncarbon.framework.knife4j.customizer;
-
-import cc.uncarbon.framework.core.enums.HelioBaseEnum;
-import com.fasterxml.jackson.databind.type.SimpleType;
-import io.swagger.v3.core.converter.AnnotatedType;
-import io.swagger.v3.oas.models.media.Schema;
-import io.swagger.v3.oas.models.parameters.Parameter;
-import org.springdoc.core.customizers.ParameterCustomizer;
-import org.springdoc.core.customizers.PropertyCustomizer;
-import org.springframework.core.MethodParameter;
-
-import java.util.Objects;
-
-/**
- * HelioBaseEnum子枚举增强
- * <p>
- * 示例代码:<br />
- * {@code @Schema(description} = "性别")<br />
- * private GenderEnum gender;
- * <p>
- * 输出文档描述:
- * 性别(0=未知 1=男 2=女)
- *
- * @author Uncarbon
- */
-public class HelioBaseEnumCustomizer implements PropertyCustomizer, ParameterCustomizer {
-
-    /**
-     * 主要用于 @Schema 注解标记的字段
-     */
-    @Override
-    public Schema<?> customize(Schema property, AnnotatedType propertyType) {
-        if (Objects.nonNull(property) && Objects.nonNull(propertyType)) {
-            try {
-                SimpleType simpleType = (SimpleType) propertyType.getType();
-                if (HelioBaseEnum.class.isAssignableFrom(simpleType.getRawClass())) {
-                    property.setDescription(determineDescription(simpleType.getRawClass(), property.getDescription()));
-                }
-            } catch (Exception e) {
-                // fail to customize, ignored
-            }
-        }
-        return property;
-    }
-
-    /**
-     *
-     * 主要用于 @Parameter 注解标记的字段,以及路径参数、Query
-     */
-    @Override
-    public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) {
-        if (Objects.nonNull(parameterModel) && Objects.nonNull(methodParameter)
-                && HelioBaseEnum.class.isAssignableFrom(methodParameter.getParameterType())) {
-            parameterModel.setDescription(determineDescription(methodParameter.getParameterType(), parameterModel.getDescription()));
-        }
-        return parameterModel;
-    }
-
-    /**
-     * 确定输出的描述文本
-     */
-    protected String determineDescription(Class<?> helioBaseEnum, String originalDescription) {
-        // 获取枚举类中的所有枚举项
-        HelioBaseEnum<?>[] enumItems = (HelioBaseEnum<?>[]) helioBaseEnum.getEnumConstants();
-
-        // 拼接描述字符串
-        StringBuilder newDescription = new StringBuilder(128)
-                .append(originalDescription)
-                .append('(');
-
-        for (int i = 0; i < enumItems.length; i++) {
-            newDescription
-                    .append(enumItems[i].getValue())
-                    .append('=')
-                    .append(enumItems[i].getLabel());
-
-            if (i < enumItems.length - 1) {
-                // 不是最后一项,增加分割符
-                newDescription.append(' ');
-            }
-        }
-        newDescription.append(")<br />");
-
-        return newDescription.toString();
-    }
-}
+package cc.uncarbon.framework.knife4j.customizer;
+
+import cc.uncarbon.framework.core.enums.HelioBaseEnum;
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.text.StrPool;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.type.SimpleType;
+import io.swagger.v3.core.converter.AnnotatedType;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import org.springdoc.core.customizers.ParameterCustomizer;
+import org.springdoc.core.customizers.PropertyCustomizer;
+import org.springframework.core.MethodParameter;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * HelioBaseEnum子枚举增强
+ * <p>
+ * 示例代码:<br />
+ * {@code @Schema(description} = "性别")<br />
+ * private GenderEnum gender;
+ * <p>
+ * 输出文档描述:
+ * 性别(0=未知 1=男 2=女)
+ *
+ * @author Uncarbon
+ */
+@Component
+public class HelioBaseEnumCustomizer implements PropertyCustomizer, ParameterCustomizer {
+
+    protected static final String TYPE_NAME_KEYWORD_COLLECTION_TYPE = "collection type;";
+    protected static final String TYPE_NAME_KEYWORD_SIMPLE_TYPE = "simple type, class ";
+    protected static final String SCHEMA_TYPE_INTEGER = "integer";
+    protected static final String SCHEMA_FORMAT_INT32 = "int32";
+    protected static final String SCHEMA_FORMAT_INT64 = "int64";
+
+    /**
+     * 主要用于 @Schema 注解标记的字段
+     */
+    @Override
+    public Schema<?> customize(Schema schema, AnnotatedType propertyType) {
+        if (Objects.nonNull(schema) && Objects.nonNull(propertyType)) {
+            try {
+                HelioBaseEnum<?>[] enumItems = findHelioBaseEnumInGenerics(propertyType.getType().getTypeName());
+                if (ArrayUtil.isNotEmpty(enumItems)) {
+                    updateSchema(schema, enumItems, true);
+                    return schema;
+                }
+
+                SimpleType simpleType = (SimpleType) propertyType.getType();
+                if (HelioBaseEnum.class.isAssignableFrom(simpleType.getRawClass())) {
+                    enumItems = (HelioBaseEnum<?>[]) simpleType.getRawClass().getEnumConstants();
+                    updateSchema(schema, enumItems, false);
+                    return schema;
+                }
+            } catch (Exception e) {
+                // fail to customize, ignored
+            }
+        }
+        return schema;
+    }
+
+    /**
+     * 主要用于 @Parameter 注解标记的字段,以及路径参数、Query
+     */
+    @Override
+    public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) {
+        if (Objects.nonNull(parameterModel) && Objects.nonNull(methodParameter)
+                && HelioBaseEnum.class.isAssignableFrom(methodParameter.getParameterType())) {
+            HelioBaseEnum<?>[] enumItems = (HelioBaseEnum<?>[]) methodParameter.getParameterType().getEnumConstants();
+            // 只变更描述
+            parameterModel.setDescription(determineDescription(enumItems, parameterModel.getDescription()));
+        }
+        return parameterModel;
+    }
+
+    /**
+     * 尝试在文本描述中,寻找泛型中实现了 HelioBaseEnum 的枚举
+     */
+    protected HelioBaseEnum<?>[] findHelioBaseEnumInGenerics(String typeName) {
+        // 也许是枚举类的全限定名数组
+        String[] maybeEnumClassNames = {};
+        if (CharSequenceUtil.contains(typeName, TYPE_NAME_KEYWORD_COLLECTION_TYPE)) {
+            // 硬编码,针对类似 List<YesOrNoEnum> 的集合类型字段
+            maybeEnumClassNames = CharSequenceUtil.subBetweenAll(typeName, TYPE_NAME_KEYWORD_SIMPLE_TYPE, StrPool.BRACKET_END);
+        }
+        if (ArrayUtil.isNotEmpty(maybeEnumClassNames)) {
+            ClassLoader classLoader = getClass().getClassLoader();
+            // 从后往前匹配,一般最内层的泛型在最后
+            for (int i = maybeEnumClassNames.length - 1; i >= 0; i--) {
+                String maybeEnumClassName = maybeEnumClassNames[i];
+                try {
+                    Class<?> maybeEnumClass = Class.forName(maybeEnumClassName, false, classLoader);
+                    if (HelioBaseEnum.class.isAssignableFrom(maybeEnumClass)) {
+                        return (HelioBaseEnum<?>[]) maybeEnumClass.getEnumConstants();
+                    }
+                } catch (ClassNotFoundException e) {
+                    // ignored
+                }
+            }
+        }
+        return new HelioBaseEnum[]{};
+    }
+
+    /**
+     * @param isArray 枚举项是否按数组形式展现
+     */
+    @SuppressWarnings(value = "unchecked")
+    protected void updateSchema(Schema schema, HelioBaseEnum<?>[] enumItems, boolean isArray) {
+        if (ArrayUtil.isEmpty(enumItems)) {
+            return;
+        }
+        Object firstItemValue = enumItems[0].getValue();
+        if (!isArray) {
+            // 非数组形式
+            markSchemaTypeAsInteger(firstItemValue, schema);
+        } else {
+            // 数组形式
+            markSchemaTypeAsInteger(firstItemValue, schema.getItems());
+        }
+        // 外显时统一转换为字符串
+        schema.setEnum(Arrays.stream(enumItems).map(HelioBaseEnum::getValue).map(StrUtil::toStringOrNull).toList());
+        schema.setDescription(determineDescription(enumItems, schema.getDescription()));
+    }
+
+    /**
+     * 为整数类型特别标注,免得都外显成string类型,导致文档显示有误
+     */
+    protected void markSchemaTypeAsInteger(Object firstItemValue, Schema schema) {
+        if (firstItemValue instanceof Integer) {
+            schema.setType(SCHEMA_TYPE_INTEGER);
+            schema.setFormat(SCHEMA_FORMAT_INT32);
+            schema.setExample(firstItemValue);
+        } else if (firstItemValue instanceof Long) {
+            schema.setType(SCHEMA_TYPE_INTEGER);
+            schema.setFormat(SCHEMA_FORMAT_INT64);
+            schema.setExample(firstItemValue);
+        }
+    }
+
+    /**
+     * 确定输出的描述文本
+     */
+    protected String determineDescription(HelioBaseEnum<?>[] enumItems, String originalDescription) {
+        // 拼接描述字符串
+        StringBuilder newDescription = new StringBuilder(128)
+                .append(originalDescription)
+                .append('(');
+
+        for (int i = 0; i < enumItems.length; i++) {
+            newDescription
+                    .append(enumItems[i].getValue())
+                    .append('=')
+                    .append(enumItems[i].getLabel());
+
+            if (i < enumItems.length - 1) {
+                // 不是最后一项,增加分割符
+                newDescription.append(' ');
+            }
+        }
+        newDescription.append(")<br />");
+        return newDescription.toString();
+    }
+}

+ 1 - 1
helio-starter-knife4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +1 @@
-cc.uncarbon.framework.knife4j.config.HelioKnife4jAutoConfiguration
+cc.uncarbon.framework.knife4j.config.HelioKnife4jAutoConfiguration

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

@@ -1,47 +1,47 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-rate-limit-redis</artifactId>
-    <description>基于Redis实现的限流组件,内置简易的3种限流策略(全局限流、根据当前用户ID限流、根据客户端IP限流)</description>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-starter-aop</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-starter-redis</artifactId>
-        </dependency>
-
-        <!-- 仅 RateLimitByClientIPStrategy 需要该依赖,若不使用可排除 -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-starter-web</artifactId>
-            <optional>true</optional>
-        </dependency>
-    </dependencies>
-
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-rate-limit-redis</artifactId>
+    <description>基于Redis实现的限流组件,内置简易的3种限流策略(全局限流、根据当前用户ID限流、根据客户端IP限流)</description>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-starter-aop</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-starter-redis</artifactId>
+        </dependency>
+
+        <!-- 仅 RateLimitByClientIPStrategy 需要该依赖,若不使用可排除 -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-starter-web</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 21 - 21
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/constant/RateLimitConstant.java

@@ -1,21 +1,21 @@
-package cc.uncarbon.framework.ratelimit.constant;
-
-/**
- * 限流组件常量
- */
-public final class RateLimitConstant {
-
-    private RateLimitConstant() {
-    }
-
-    /**
-     * Redis Key前缀
-     */
-    public static final String REDIS_KEY_PREFIX = "rateLimit:";
-
-    /**
-     * 未知客户端IP地址
-     */
-    public static final String CLIENT_IP_UNKNOWN = "unknown";
-
-}
+package cc.uncarbon.framework.ratelimit.constant;
+
+/**
+ * 限流组件常量
+ */
+public final class RateLimitConstant {
+
+    private RateLimitConstant() {
+    }
+
+    /**
+     * Redis Key前缀
+     */
+    public static final String REDIS_KEY_PREFIX = "rateLimit:";
+
+    /**
+     * 未知客户端IP地址
+     */
+    public static final String CLIENT_IP_UNKNOWN = "unknown";
+
+}

+ 27 - 27
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/RateLimitStrategy.java

@@ -1,27 +1,27 @@
-package cc.uncarbon.framework.ratelimit.stratrgy;
-
-import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
-import cc.uncarbon.framework.core.exception.RateLimitedException;
-import org.aspectj.lang.JoinPoint;
-
-/**
- * 限流策略接口
- */
-public interface RateLimitStrategy {
-
-    /**
-     * 限流异常生产者
-     * 通过覆写本方法,可替换提示消息、实现国际化文案等
-     */
-    default RateLimitedException rateLimitedExceptionSupplier() {
-        return new RateLimitedException();
-    }
-
-    /**
-     * 执行限流检查,根据不同维度判断是否已触达限流上限
-     * @param annotation 注解实例
-     * @param point 切点
-     */
-    void performRateLimitCheck(UseRateLimit annotation, JoinPoint point);
-
-}
+package cc.uncarbon.framework.ratelimit.stratrgy;
+
+import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
+import cc.uncarbon.framework.core.exception.RateLimitedException;
+import org.aspectj.lang.JoinPoint;
+
+/**
+ * 限流策略接口
+ */
+public interface RateLimitStrategy {
+
+    /**
+     * 限流异常生产者
+     * 通过覆写本方法,可替换提示消息、实现国际化文案等
+     */
+    default RateLimitedException rateLimitedExceptionSupplier() {
+        return new RateLimitedException();
+    }
+
+    /**
+     * 执行限流检查,根据不同维度判断是否已触达限流上限
+     * @param annotation 注解实例
+     * @param point 切点
+     */
+    void performRateLimitCheck(UseRateLimit annotation, JoinPoint point);
+
+}

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

@@ -1,64 +1,64 @@
-package cc.uncarbon.framework.ratelimit.stratrgy.impl;
-
-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 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;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-
-/**
- * 以客户端IP为维度限流策略
- * 基于{@link SimpleRedisBasedRateLimitStrategy}使用相同Lua脚本,仅在RedisKey有差异
- */
-@Slf4j
-public class RateLimitByClientIPStrategy extends SimpleRedisBasedRateLimitStrategy implements RateLimitStrategy {
-
-    public RateLimitByClientIPStrategy(RedisTemplate<String, Object> objectRedisTemplate) {
-        super(objectRedisTemplate, "RateLimitByClientIPStrategy");
-    }
-
-    @Override
-    public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) {
-        super.performRateLimitCheck(annotation, point, this::rateLimitedExceptionSupplier);
-    }
-
-    @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);
-    }
-
-    /**
-     * 取当前客户端IP
-     */
-    protected String currentClientIP() {
-        String clientIP = UserContextHolder.getClientIP();
-        if (CharSequenceUtil.isNotEmpty(clientIP)) {
-            return clientIP;
-        }
-
-        // 尝试从request中得到未登录用户的IP地址
-        try {
-            ServletRequestAttributes servletRequestAttributes =
-                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-            if (servletRequestAttributes != null) {
-                return IPUtil.getClientIPAddress(servletRequestAttributes.getRequest(), 0);
-            }
-        } catch (Exception | NoClassDefFoundError e) {
-            // 可能没有引入 helio-starter-web 依赖,忽略
-        }
-
-        // 兜底
-        return RateLimitConstant.CLIENT_IP_UNKNOWN;
-    }
-
-}
+package cc.uncarbon.framework.ratelimit.stratrgy.impl;
+
+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 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;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+/**
+ * 以客户端IP为维度限流策略
+ * 基于{@link SimpleRedisBasedRateLimitStrategy}使用相同Lua脚本,仅在RedisKey有差异
+ */
+@Slf4j
+public class RateLimitByClientIPStrategy extends SimpleRedisBasedRateLimitStrategy implements RateLimitStrategy {
+
+    public RateLimitByClientIPStrategy(RedisTemplate<String, Object> objectRedisTemplate) {
+        super(objectRedisTemplate, "RateLimitByClientIPStrategy");
+    }
+
+    @Override
+    public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) {
+        super.performRateLimitCheck(annotation, point, this::rateLimitedExceptionSupplier);
+    }
+
+    @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);
+    }
+
+    /**
+     * 取当前客户端IP
+     */
+    protected String currentClientIP() {
+        String clientIP = UserContextHolder.getClientIP();
+        if (CharSequenceUtil.isNotEmpty(clientIP)) {
+            return clientIP;
+        }
+
+        // 尝试从request中得到未登录用户的IP地址
+        try {
+            ServletRequestAttributes servletRequestAttributes =
+                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            if (servletRequestAttributes != null) {
+                return IPUtil.getClientIPAddress(servletRequestAttributes.getRequest(), 0);
+            }
+        } catch (Exception | NoClassDefFoundError e) {
+            // 可能没有引入 helio-starter-web 依赖,忽略
+        }
+
+        // 兜底
+        return RateLimitConstant.CLIENT_IP_UNKNOWN;
+    }
+
+}

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

@@ -1,35 +1,35 @@
-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 lombok.extern.slf4j.Slf4j;
-import org.aspectj.lang.JoinPoint;
-import org.springframework.data.redis.core.RedisTemplate;
-
-/**
- * 无维度区分,全局限流策略
- */
-@Slf4j
-public class RateLimitGlobalStrategy extends SimpleRedisBasedRateLimitStrategy implements RateLimitStrategy {
-
-    public RateLimitGlobalStrategy(RedisTemplate<String, Object> objectRedisTemplate) {
-        super(objectRedisTemplate, "RateLimitGlobalStrategy");
-    }
-
-    @Override
-    public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) {
-        super.performRateLimitCheck(annotation, point, this::rateLimitedExceptionSupplier);
-    }
-
-    /**
-     * 确定RedisKey
-     */
-    @Override
-    protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
-        final String splitter = StrPool.COLON;
-        // 由编译器自动使用StringBuilder拼接,下同
-        return RateLimitConstant.REDIS_KEY_PREFIX + "global" + splitter + determineMark(annotation, point);
-    }
-}
+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 lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.springframework.data.redis.core.RedisTemplate;
+
+/**
+ * 无维度区分,全局限流策略
+ */
+@Slf4j
+public class RateLimitGlobalStrategy extends SimpleRedisBasedRateLimitStrategy implements RateLimitStrategy {
+
+    public RateLimitGlobalStrategy(RedisTemplate<String, Object> objectRedisTemplate) {
+        super(objectRedisTemplate, "RateLimitGlobalStrategy");
+    }
+
+    @Override
+    public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) {
+        super.performRateLimitCheck(annotation, point, this::rateLimitedExceptionSupplier);
+    }
+
+    /**
+     * 确定RedisKey
+     */
+    @Override
+    protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
+        final String splitter = StrPool.COLON;
+        // 由编译器自动使用StringBuilder拼接,下同
+        return RateLimitConstant.REDIS_KEY_PREFIX + "global" + splitter + determineMark(annotation, point);
+    }
+}

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

@@ -1,89 +1,91 @@
-package cc.uncarbon.framework.ratelimit.stratrgy.impl;
-
-import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
-import cc.uncarbon.framework.core.exception.RateLimitStrategyException;
-import cc.uncarbon.framework.core.exception.RateLimitedException;
-import cn.hutool.core.text.CharSequenceUtil;
-import cn.hutool.core.text.StrPool;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.core.script.RedisScript;
-
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.function.Supplier;
-
-/**
- * 脚手架预置的简易限流策略基类
- */
-@RequiredArgsConstructor
-@Slf4j
-public abstract class SimpleRedisBasedRateLimitStrategy {
-
-    private final RedisTemplate<String, Object> objectRedisTemplate;
-    private final String realClassName;
-
-    /**
-     * 预置限流Lua脚本
-     * 编译器将自动优化
-     */
-    private static final String LUA_SCRIPT = "local key = KEYS[1]\n"
-            + "local count = tonumber(ARGV[1])\n"
-            + "local time = tonumber(ARGV[2])\n"
-            + "local current = redis.call('get', key);\n"
-            + "if current and tonumber(current) > count then\n"
-            + "    return tonumber(current);\n"
-            + "end\n"
-            + "current = redis.call('incr', key)\n"
-            + "if tonumber(current) == 1 then\n"
-            + "    redis.call('expire', key, time)\n"
-            + "end\n"
-            + "return tonumber(current);";
-
-    /**
-     * 预置限流Lua脚本的 RedisScript 类实例
-     */
-    private static final RedisScript<Long> REDIS_SCRIPT = RedisScript.of(LUA_SCRIPT, Long.class);
-
-
-    protected void performRateLimitCheck(UseRateLimit annotation, JoinPoint point,
-                                         Supplier<RateLimitedException> rateLimitedExceptionSupplier)
-            throws RateLimitStrategyException {
-        long duration = annotation.duration();
-        long max = annotation.max();
-
-        String redisKey = determineRedisKey(annotation, point);
-        Long current = objectRedisTemplate.execute(REDIS_SCRIPT, Collections.singletonList(redisKey), max, duration);
-        if (current == null) {
-            log.error("SimpleRedisBasedRateLimitStrategy.REDIS_SCRIPT executed but no result.");
-            throw new RateLimitStrategyException("SCRIPT executed but no result");
-        }
-        if (current > max) {
-            log.info("[UseRateLimit][{}] 键名 {} 已触达限流阈值 {}", realClassName, redisKey, max);
-            throw rateLimitedExceptionSupplier.get();
-        }
-        log.info("[UseRateLimit][{}] 键名 {} 限流进度 {}/{}", realClassName, redisKey, current, max);
-    }
-
-    /**
-     * 确定RedisKey
-     */
-    protected abstract String determineRedisKey(UseRateLimit annotation, JoinPoint point);
-
-    /**
-     * 确定被限流方法的标识
-     */
-    protected String determineMark(UseRateLimit annotation, JoinPoint point) {
-        if (CharSequenceUtil.isNotEmpty(annotation.mark())) {
-            return annotation.mark();
-        }
-        // 使用Java方法的全限定名
-        MethodSignature signature = (MethodSignature) point.getSignature();
-        Method method = signature.getMethod();
-        Class<?> targetClass = method.getDeclaringClass();
-        return targetClass.getName() + StrPool.DASHED + method.getName();
-    }
-}
+package cc.uncarbon.framework.ratelimit.stratrgy.impl;
+
+import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
+import cc.uncarbon.framework.core.exception.RateLimitStrategyException;
+import cc.uncarbon.framework.core.exception.RateLimitedException;
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.text.StrPool;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.RedisScript;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.function.Supplier;
+
+/**
+ * 脚手架预置的简易限流策略基类
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class SimpleRedisBasedRateLimitStrategy {
+
+    private final RedisTemplate<String, Object> objectRedisTemplate;
+    private final String realClassName;
+
+    /**
+     * 预置限流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)
+        end
+        return tonumber(current);
+        """;
+
+    /**
+     * 预置限流Lua脚本的 RedisScript 类实例
+     */
+    private static final RedisScript<Long> REDIS_SCRIPT = RedisScript.of(LUA_SCRIPT, Long.class);
+
+
+    protected void performRateLimitCheck(UseRateLimit annotation, JoinPoint point,
+                                         Supplier<RateLimitedException> rateLimitedExceptionSupplier)
+            throws RateLimitStrategyException {
+        long duration = annotation.duration();
+        long max = annotation.max();
+
+        String redisKey = determineRedisKey(annotation, point);
+        Long current = objectRedisTemplate.execute(REDIS_SCRIPT, Collections.singletonList(redisKey), max, duration);
+        if (current == null) {
+            log.error("SimpleRedisBasedRateLimitStrategy.REDIS_SCRIPT executed but no result.");
+            throw new RateLimitStrategyException("SCRIPT executed but no result");
+        }
+        if (current > max) {
+            log.info("[UseRateLimit][{}] 键名 {} 已触达限流阈值 {}", realClassName, redisKey, max);
+            throw rateLimitedExceptionSupplier.get();
+        }
+        log.info("[UseRateLimit][{}] 键名 {} 限流进度 {}/{}", realClassName, redisKey, current, max);
+    }
+
+    /**
+     * 确定RedisKey
+     */
+    protected abstract String determineRedisKey(UseRateLimit annotation, JoinPoint point);
+
+    /**
+     * 确定被限流方法的标识
+     */
+    protected String determineMark(UseRateLimit annotation, JoinPoint point) {
+        if (CharSequenceUtil.isNotEmpty(annotation.mark())) {
+            return annotation.mark();
+        }
+        // 使用Java方法的全限定名
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        Class<?> targetClass = method.getDeclaringClass();
+        return targetClass.getName() + StrPool.DASHED + method.getName();
+    }
+}

+ 4 - 4
helio-starter-rate-limit-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1,4 +1,4 @@
-cc.uncarbon.framework.ratelimit.aspect.UseRateLimitAspect
-cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitGlobalStrategy
-cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitByUserIdStrategy
-cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitByClientIPStrategy
+cc.uncarbon.framework.ratelimit.aspect.UseRateLimitAspect
+cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitGlobalStrategy
+cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitByUserIdStrategy
+cc.uncarbon.framework.ratelimit.stratrgy.impl.RateLimitByClientIPStrategy

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

@@ -1,35 +1,35 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-redis</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>org.redisson</groupId>
-            <artifactId>redisson-spring-boot-starter</artifactId>
-        </dependency>
-    </dependencies>
-
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-redis</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 28 - 28
helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/enums/KeyTypeEnum.java

@@ -1,28 +1,28 @@
-package cc.uncarbon.framework.redis.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-@AllArgsConstructor
-@Getter
-public enum KeyTypeEnum {
-
-    // 基本类型
-    STRING,
-    HASH,
-    LIST,
-    SET,
-    ZSET,
-    STREAM,
-    RE_JSON,
-
-    // 封装类型
-    LOCK,
-    PUB_SUB,
-    HYPER_LOG_LOG,
-    GEO,
-
-    // 尚未收录的
-    OTHER
-
-}
+package cc.uncarbon.framework.redis.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum KeyTypeEnum {
+
+    // 基本类型
+    STRING,
+    HASH,
+    LIST,
+    SET,
+    ZSET,
+    STREAM,
+    RE_JSON,
+
+    // 封装类型
+    LOCK,
+    PUB_SUB,
+    HYPER_LOG_LOG,
+    GEO,
+
+    // 尚未收录的
+    OTHER
+
+}

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

@@ -1,40 +1,40 @@
-package cc.uncarbon.framework.redis.model;
-
-import cc.uncarbon.framework.redis.enums.KeyTypeEnum;
-import cn.hutool.core.util.RandomUtil;
-
-/**
- * 已固定好键名的 Redis Key
- * @param key 已固定好的键名
- * @param keyType 键类型
- * @param valueClass 值类型
- * @param durationSeconds 有效时长,永久为-1,可通过 hasExpiration() 方法判断
- * @param <V>
- */
-public record FormattedRedisKey<V>(
-        String key,
-        KeyTypeEnum keyType,
-        Class<V> valueClass,
-        long durationSeconds
-) {
-    /**
-     * 是否已设置有效时长
-     */
-    public boolean hasExpiration() {
-        return this.durationSeconds > 0;
-    }
-
-    /**
-     * 计算随机有效时长,有助于缓解缓存击穿、缓存雪崩
-     *
-     * @param minRatio 最小比例因子,如0.9表示90%
-     * @param maxRatio 最大比例因子,如1.1表示110%
-     * @return 随机有效时长,单位=秒
-     */
-    public long randomDurationSeconds(double minRatio, double maxRatio) {
-        if (!hasExpiration()) {
-            throw new IllegalArgumentException("no-expiration key CANNOT calculate randomDurationSeconds");
-        }
-        return (long)(RandomUtil.randomDouble(this.durationSeconds * minRatio, this.durationSeconds * maxRatio));
-    }
-}
+package cc.uncarbon.framework.redis.model;
+
+import cc.uncarbon.framework.redis.enums.KeyTypeEnum;
+import cn.hutool.core.util.RandomUtil;
+
+/**
+ * 已固定好键名的 Redis Key
+ * @param key 已固定好的键名
+ * @param keyType 键类型
+ * @param valueClass 值类型
+ * @param durationSeconds 有效时长,永久为-1,可通过 hasExpiration() 方法判断
+ * @param <V>
+ */
+public record FormattedRedisKey<V>(
+        String key,
+        KeyTypeEnum keyType,
+        Class<V> valueClass,
+        long durationSeconds
+) {
+    /**
+     * 是否已设置有效时长
+     */
+    public boolean hasExpiration() {
+        return this.durationSeconds > 0;
+    }
+
+    /**
+     * 计算随机有效时长,有助于缓解缓存击穿、缓存雪崩
+     *
+     * @param minRatio 最小比例因子,如0.9表示90%
+     * @param maxRatio 最大比例因子,如1.1表示110%
+     * @return 随机有效时长,单位=秒
+     */
+    public long randomDurationSeconds(double minRatio, double maxRatio) {
+        if (!hasExpiration()) {
+            throw new IllegalArgumentException("no-expiration key CANNOT calculate randomDurationSeconds");
+        }
+        return (long)(RandomUtil.randomDouble(this.durationSeconds * minRatio, this.durationSeconds * maxRatio));
+    }
+}

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

@@ -1,83 +1,83 @@
-package cc.uncarbon.framework.redis.model;
-
-import cc.uncarbon.framework.redis.enums.KeyTypeEnum;
-import cn.hutool.core.text.CharSequenceUtil;
-import lombok.Getter;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * 统一 Redis Key 定义,方便集中管理
- * @param <V> 值类型
- */
-@Getter
-public class RedisKeyDefinition<V> {
-
-    /**
-     * 键名模板,占位符需要用 {}
-     */
-    private final String keyTemplate;
-
-    /**
-     * 键类型
-     */
-    private final KeyTypeEnum keyType;
-
-    /**
-     * 值类型对应 Class
-     */
-    private final Class<V> valueClass;
-
-    /**
-     * 有效时长,单位=秒
-     */
-    private final long durationSeconds;
-
-
-    /**
-     * 不设置有效时长
-     * @param keyTemplate 键名模板,占位符需要用 {}
-     * @param keyType 键类型
-     * @param valueClass 值类型对应 Class
-     */
-    public RedisKeyDefinition(String keyTemplate, KeyTypeEnum keyType, Class<V> valueClass) {
-        this.keyTemplate = keyTemplate;
-        this.keyType = keyType;
-        this.valueClass = valueClass;
-        this.durationSeconds = -1;
-    }
-
-    /**
-     * 有设置有效时长
-     * @param keyTemplate 键名模板,占位符需要用 {}
-     * @param keyType 键类型
-     * @param valueClass 值类型对应 Class
-     * @param duration 有效时长
-     * @param timeUnit 有效时长的单位,最低为秒
-     */
-    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");
-        }
-
-        this.keyTemplate = keyTemplate;
-        this.keyType = keyType;
-        this.valueClass = valueClass;
-
-        // 兜底:最短缓存1s
-        long asSeconds = timeUnit.toSeconds(duration);
-        if (asSeconds < 1) {
-            asSeconds = 1;
-        }
-        this.durationSeconds = asSeconds;
-    }
-
-    /**
-     * 固定一个 Redis Key 的键名
-     * @param params 键名模板占位符填充参数
-     */
-    public FormattedRedisKey<V> format(Object... params) {
-        return new FormattedRedisKey<>(CharSequenceUtil.format(this.keyTemplate, params),
-                this.keyType, this.valueClass, this.durationSeconds);
-    }
-}
+package cc.uncarbon.framework.redis.model;
+
+import cc.uncarbon.framework.redis.enums.KeyTypeEnum;
+import cn.hutool.core.text.CharSequenceUtil;
+import lombok.Getter;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 统一 Redis Key 定义,方便集中管理
+ * @param <V> 值类型
+ */
+@Getter
+public class RedisKeyDefinition<V> {
+
+    /**
+     * 键名模板,占位符需要用 {}
+     */
+    private final String keyTemplate;
+
+    /**
+     * 键类型
+     */
+    private final KeyTypeEnum keyType;
+
+    /**
+     * 值类型对应 Class
+     */
+    private final Class<V> valueClass;
+
+    /**
+     * 有效时长,单位=秒
+     */
+    private final long durationSeconds;
+
+
+    /**
+     * 不设置有效时长
+     * @param keyTemplate 键名模板,占位符需要用 {}
+     * @param keyType 键类型
+     * @param valueClass 值类型对应 Class
+     */
+    public RedisKeyDefinition(String keyTemplate, KeyTypeEnum keyType, Class<V> valueClass) {
+        this.keyTemplate = keyTemplate;
+        this.keyType = keyType;
+        this.valueClass = valueClass;
+        this.durationSeconds = -1;
+    }
+
+    /**
+     * 有设置有效时长
+     * @param keyTemplate 键名模板,占位符需要用 {}
+     * @param keyType 键类型
+     * @param valueClass 值类型对应 Class
+     * @param duration 有效时长
+     * @param timeUnit 有效时长的单位,最低为秒
+     */
+    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");
+        }
+
+        this.keyTemplate = keyTemplate;
+        this.keyType = keyType;
+        this.valueClass = valueClass;
+
+        // 兜底:最短缓存1s
+        long asSeconds = timeUnit.toSeconds(duration);
+        if (asSeconds < 1) {
+            asSeconds = 1;
+        }
+        this.durationSeconds = asSeconds;
+    }
+
+    /**
+     * 固定一个 Redis Key 的键名
+     * @param params 键名模板占位符填充参数
+     */
+    public FormattedRedisKey<V> format(Object... params) {
+        return new FormattedRedisKey<>(CharSequenceUtil.format(this.keyTemplate, params),
+                this.keyType, this.valueClass, this.durationSeconds);
+    }
+}

+ 68 - 68
helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/template/RedisDistributedLockTemplate.java

@@ -1,68 +1,68 @@
-package cc.uncarbon.framework.redis.template;
-
-import jakarta.annotation.Nonnull;
-import jakarta.annotation.Nullable;
-
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-
-/**
- * Redis分布式可重入锁模板类
- * 编程式获取分布式锁,模板化分布式锁的使用方式
- *
- * @author Uncarbon
- */
-public interface RedisDistributedLockTemplate {
-
-    /**
-     * 获取分布式锁并执行锁内代码,等待时长无限
-     *
-     * @param lockName       锁名称
-     * @param unit           时间单位
-     * @param holdDuration   锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
-     * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用
-     */
-    void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration,
-                         @Nonnull Runnable lockedRunnable);
-
-    /**
-     * 获取分布式锁并执行锁内代码,等待时长无限
-     *
-     * @param lockName         锁名称
-     * @param unit             时间单位
-     * @param holdDuration     锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
-     * @param lockedRunnable   锁内代码,建议单独开个子方法,通过lambda表达式引用
-     * @param exceptionHandler (可选)锁内代码异常处理过程
-     */
-    void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration,
-                         @Nonnull Runnable lockedRunnable, @Nullable Consumer<Exception> exceptionHandler);
-
-    /**
-     * 获取分布式锁并执行锁内代码
-     * 【注意】务必考虑到锁内代码有抛出异常的可能
-     *
-     * @param lockName       锁名称
-     * @param unit           时间单位
-     * @param waitDuration   最大等待时长
-     * @param holdDuration   锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
-     * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用
-     * @return 是否实际拿到锁
-     */
-    boolean executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int waitDuration, int holdDuration,
-                            @Nonnull Runnable lockedRunnable);
-
-    /**
-     * 获取分布式锁并执行锁内代码
-     * 可手动指定锁内代码异常处理过程
-     *
-     * @param lockName         锁名称
-     * @param unit             时间单位
-     * @param waitDuration     最大等待时长
-     * @param holdDuration     锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
-     * @param lockedRunnable   锁内代码,建议单独开个子方法,通过lambda表达式引用
-     * @param exceptionHandler (可选)锁内代码异常处理过程
-     * @return 是否实际拿到锁
-     */
-    boolean executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int waitDuration, int holdDuration,
-                            @Nonnull Runnable lockedRunnable, @Nullable Consumer<Exception> exceptionHandler);
-}
+package cc.uncarbon.framework.redis.template;
+
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Redis分布式可重入锁模板类
+ * 编程式获取分布式锁,模板化分布式锁的使用方式
+ *
+ * @author Uncarbon
+ */
+public interface RedisDistributedLockTemplate {
+
+    /**
+     * 获取分布式锁并执行锁内代码,等待时长无限
+     *
+     * @param lockName       锁名称
+     * @param unit           时间单位
+     * @param holdDuration   锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
+     * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用
+     */
+    void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration,
+                         @Nonnull Runnable lockedRunnable);
+
+    /**
+     * 获取分布式锁并执行锁内代码,等待时长无限
+     *
+     * @param lockName         锁名称
+     * @param unit             时间单位
+     * @param holdDuration     锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
+     * @param lockedRunnable   锁内代码,建议单独开个子方法,通过lambda表达式引用
+     * @param exceptionHandler (可选)锁内代码异常处理过程
+     */
+    void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration,
+                         @Nonnull Runnable lockedRunnable, @Nullable Consumer<Exception> exceptionHandler);
+
+    /**
+     * 获取分布式锁并执行锁内代码
+     * 【注意】务必考虑到锁内代码有抛出异常的可能
+     *
+     * @param lockName       锁名称
+     * @param unit           时间单位
+     * @param waitDuration   最大等待时长
+     * @param holdDuration   锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
+     * @param lockedRunnable 锁内代码,建议单独开个子方法,通过lambda表达式引用
+     * @return 是否实际拿到锁
+     */
+    boolean executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int waitDuration, int holdDuration,
+                            @Nonnull Runnable lockedRunnable);
+
+    /**
+     * 获取分布式锁并执行锁内代码
+     * 可手动指定锁内代码异常处理过程
+     *
+     * @param lockName         锁名称
+     * @param unit             时间单位
+     * @param waitDuration     最大等待时长
+     * @param holdDuration     锁持有时长;【注意】持有时长一定要大于业务的执行时间,锁不会自动续期
+     * @param lockedRunnable   锁内代码,建议单独开个子方法,通过lambda表达式引用
+     * @param exceptionHandler (可选)锁内代码异常处理过程
+     * @return 是否实际拿到锁
+     */
+    boolean executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int waitDuration, int holdDuration,
+                            @Nonnull Runnable lockedRunnable, @Nullable Consumer<Exception> exceptionHandler);
+}

+ 76 - 76
helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/template/impl/RedisDistributedLockTemplateImpl.java

@@ -1,76 +1,76 @@
-package cc.uncarbon.framework.redis.template.impl;
-
-import cc.uncarbon.framework.redis.lock.RedisDistributedLock;
-import cc.uncarbon.framework.redis.template.RedisDistributedLockTemplate;
-import cn.hutool.core.lang.Assert;
-import jakarta.annotation.Nonnull;
-import jakarta.annotation.Nullable;
-import lombok.RequiredArgsConstructor;
-import org.redisson.api.RLock;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-
-/**
- * Redis分布式可重入锁模板类实现
- *
- * @author Uncarbon
- */
-@ConditionalOnMissingBean(value = RedisDistributedLockTemplate.class)
-@ConditionalOnBean(value = RedisDistributedLock.class)
-@RequiredArgsConstructor
-public class RedisDistributedLockTemplateImpl implements RedisDistributedLockTemplate {
-
-    private final RedisDistributedLock redisDistributedLock;
-
-
-    @Override
-    public void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration, @Nonnull Runnable lockedRunnable) {
-        executeWithLock(lockName, unit, holdDuration, lockedRunnable, null);
-    }
-
-    @Override
-    public void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration, @Nonnull Runnable lockedRunnable, @Nullable Consumer<Exception> exceptionHandler) {
-        Assert.isTrue(holdDuration > 0, "holdDuration必须大于0");
-        RLock lock = redisDistributedLock.lock(lockName, unit, holdDuration);
-        Assert.notNull(lock, "获取{}的锁对象失败", lockName);
-
-        runAndAutoUnlock(lockName, lockedRunnable, exceptionHandler);
-    }
-
-    @Override
-    public boolean executeWithLock(@Nonnull final String lockName, @Nonnull final TimeUnit unit, int waitDuration, int holdDuration,
-                                   @Nonnull final Runnable lockedRunnable) {
-        return executeWithLock(lockName, unit, waitDuration, holdDuration, lockedRunnable, null);
-    }
-
-    @Override
-    public boolean executeWithLock(final @Nonnull String lockName, final @Nonnull TimeUnit unit, int waitDuration, int holdDuration,
-                                   final @Nonnull Runnable lockedRunnable, final @Nullable Consumer<Exception> exceptionHandler) {
-        Assert.isTrue(waitDuration > 0, "waitDuration必须大于0");
-        Assert.isTrue(holdDuration > 0, "holdDuration必须大于0");
-
-        boolean lockedFlag = redisDistributedLock.tryLock(lockName, unit, waitDuration, holdDuration);
-        if (!lockedFlag) {
-            return false;
-        }
-        runAndAutoUnlock(lockName, lockedRunnable, exceptionHandler);
-        return true;
-    }
-
-    protected void runAndAutoUnlock(String lockName, Runnable lockedRunnable, Consumer<Exception> exceptionHandler) {
-        try {
-            lockedRunnable.run();
-        } catch (Exception e) {
-            if (exceptionHandler != null) {
-                exceptionHandler.accept(e);
-            } else {
-                throw e;
-            }
-        } finally {
-            redisDistributedLock.unlockSafely(lockName);
-        }
-    }
-}
+package cc.uncarbon.framework.redis.template.impl;
+
+import cc.uncarbon.framework.redis.lock.RedisDistributedLock;
+import cc.uncarbon.framework.redis.template.RedisDistributedLockTemplate;
+import cn.hutool.core.lang.Assert;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
+import lombok.RequiredArgsConstructor;
+import org.redisson.api.RLock;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Redis分布式可重入锁模板类实现
+ *
+ * @author Uncarbon
+ */
+@ConditionalOnMissingBean(value = RedisDistributedLockTemplate.class)
+@ConditionalOnBean(value = RedisDistributedLock.class)
+@RequiredArgsConstructor
+public class RedisDistributedLockTemplateImpl implements RedisDistributedLockTemplate {
+
+    private final RedisDistributedLock redisDistributedLock;
+
+
+    @Override
+    public void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration, @Nonnull Runnable lockedRunnable) {
+        executeWithLock(lockName, unit, holdDuration, lockedRunnable, null);
+    }
+
+    @Override
+    public void executeWithLock(@Nonnull String lockName, @Nonnull TimeUnit unit, int holdDuration, @Nonnull Runnable lockedRunnable, @Nullable Consumer<Exception> exceptionHandler) {
+        Assert.isTrue(holdDuration > 0, "holdDuration必须大于0");
+        RLock lock = redisDistributedLock.lock(lockName, unit, holdDuration);
+        Assert.notNull(lock, "获取{}的锁对象失败", lockName);
+
+        runAndAutoUnlock(lockName, lockedRunnable, exceptionHandler);
+    }
+
+    @Override
+    public boolean executeWithLock(@Nonnull final String lockName, @Nonnull final TimeUnit unit, int waitDuration, int holdDuration,
+                                   @Nonnull final Runnable lockedRunnable) {
+        return executeWithLock(lockName, unit, waitDuration, holdDuration, lockedRunnable, null);
+    }
+
+    @Override
+    public boolean executeWithLock(final @Nonnull String lockName, final @Nonnull TimeUnit unit, int waitDuration, int holdDuration,
+                                   final @Nonnull Runnable lockedRunnable, final @Nullable Consumer<Exception> exceptionHandler) {
+        Assert.isTrue(waitDuration > 0, "waitDuration必须大于0");
+        Assert.isTrue(holdDuration > 0, "holdDuration必须大于0");
+
+        boolean lockedFlag = redisDistributedLock.tryLock(lockName, unit, waitDuration, holdDuration);
+        if (!lockedFlag) {
+            return false;
+        }
+        runAndAutoUnlock(lockName, lockedRunnable, exceptionHandler);
+        return true;
+    }
+
+    protected void runAndAutoUnlock(String lockName, Runnable lockedRunnable, Consumer<Exception> exceptionHandler) {
+        try {
+            lockedRunnable.run();
+        } catch (Exception e) {
+            if (exceptionHandler != null) {
+                exceptionHandler.accept(e);
+            } else {
+                throw e;
+            }
+        } finally {
+            redisDistributedLock.unlockSafely(lockName);
+        }
+    }
+}

+ 1 - 1
helio-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +1 @@
-cc.uncarbon.framework.redis.config.HelioRedisAutoConfiguration
+cc.uncarbon.framework.redis.config.HelioRedisAutoConfiguration

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

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

+ 2 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/container/RocketProducerContainer.java

@@ -56,7 +56,8 @@ public class RocketProducerContainer implements ApplicationContextAware {
 		ThreadPoolExecutor threadPoolExecutor = ThreadPoolFactory.createProducerThreadPoolExecutor(rocketProperties);
 		// 扫描标记 @RocketMessage 注解的类,再将其中标记了 @CommonMessage/@OrderMessage/@TransactionMessage 的方法包装一下
 		// 依次注册到阿里云SDK
-		applicationContext.getBeansWithAnnotation(RocketMessage.class).forEach((beanName, bean) -> RocketConsumerStrategy.putProducer(threadPoolExecutor, consumerContainer, bean, rocketProperties, applicationContext));
+		applicationContext.getBeansWithAnnotation(RocketMessage.class)
+				.forEach((beanName, bean) -> RocketConsumerStrategy.putProducer(threadPoolExecutor, consumerContainer, bean, rocketProperties, applicationContext));
 		// 销毁临时线程池
 		threadPoolExecutor.shutdown();
 	}

+ 4 - 4
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/DefaultMessageListener.java

@@ -36,7 +36,7 @@ import lombok.extern.slf4j.Slf4j;
 public class DefaultMessageListener extends AbstractRocketListener implements MessageListener {
 
     public DefaultMessageListener(MethodFactoryExecution methodFactoryExecution) {
-        super (methodFactoryExecution);
+        super(methodFactoryExecution);
     }
 
     /**
@@ -50,11 +50,11 @@ public class DefaultMessageListener extends AbstractRocketListener implements Me
      */
     @Override
     public Action consume(Message message, ConsumeContext context) {
-        log.info (">>>>message:{}>>>>", message);
+        log.info(">>>>message:{}>>>>", message);
         try {
-            super.getMethodFactoryExecution ().methodExecution (message.getBody ());
+            super.getMethodFactoryExecution().methodExecution(message.getBody());
         } catch (RocketException e) {
-            super.printErrorLog ();
+            super.printErrorLog();
             return Action.ReconsumeLater;
         }
         return Action.CommitMessage;

+ 4 - 4
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/consumer/DefaultMessageOrderListener.java

@@ -38,7 +38,7 @@ public class DefaultMessageOrderListener extends AbstractRocketListener implemen
 
 
     public DefaultMessageOrderListener(MethodFactoryExecution methodFactoryExecution) {
-        super (methodFactoryExecution);
+        super(methodFactoryExecution);
     }
 
     /**
@@ -52,11 +52,11 @@ public class DefaultMessageOrderListener extends AbstractRocketListener implemen
      */
     @Override
     public OrderAction consume(Message message, ConsumeOrderContext context) {
-        log.info (">>>> Order message:{}>>>>", message);
+        log.info(">>>> Order message:{}>>>>", message);
         try {
-            super.getMethodFactoryExecution ().methodExecution (message.getBody ());
+            super.getMethodFactoryExecution().methodExecution(message.getBody());
         } catch (RocketException e) {
-            super.printErrorLog ();
+            super.printErrorLog();
             return OrderAction.Suspend;
         }
         return OrderAction.Success;

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ConsumerFactory.java

@@ -32,7 +32,7 @@ import java.util.Properties;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class ConsumerFactory {
+public final class ConsumerFactory {
 	private ConsumerFactory() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ConsumerPropertiesFactory.java

@@ -30,7 +30,7 @@ import java.util.Properties;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class ConsumerPropertiesFactory {
+public final class ConsumerPropertiesFactory {
 	private ConsumerPropertiesFactory() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/MessageFactory.java

@@ -29,7 +29,7 @@ import com.aliyun.openservices.ons.api.Message;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class MessageFactory {
+public final class MessageFactory {
 	private MessageFactory() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/MethodFactory.java

@@ -27,7 +27,7 @@ import java.lang.reflect.Method;
  * @since JDK 1.8
  */
 
-public class MethodFactory {
+public final class MethodFactory {
 	private MethodFactory() {
 	}
 

+ 24 - 30
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerConsumerFactory.java

@@ -34,40 +34,34 @@ import java.util.Map;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class ProducerConsumerFactory {
-	private ProducerConsumerFactory() {
-	}
+public final class ProducerConsumerFactory {
+    private ProducerConsumerFactory() {
+    }
 
-	public static String getProducerConsumerKey(RocketMessage rocketMessage, CommonMessage commonMessage) {
-		return rocketMessage.groupID() +
-				commonMessage.topic() +
-				commonMessage.tag();
-	}
+    public static String getProducerConsumerKey(RocketMessage rocketMessage, CommonMessage commonMessage) {
+        return rocketMessage.groupID() + commonMessage.topic() + commonMessage.tag();
+    }
 
-	public static String getProducerConsumerKey(RocketMessage rocketMessage, OrderMessage orderMessage) {
-		return rocketMessage.groupID() +
-				orderMessage.topic() +
-				orderMessage.tag();
-	}
+    public static String getProducerConsumerKey(RocketMessage rocketMessage, OrderMessage orderMessage) {
+        return rocketMessage.groupID() + orderMessage.topic() + orderMessage.tag();
+    }
 
-	public static String getProducerConsumerKey(RocketMessage rocketMessage, TransactionMessage transactionMessage) {
-		return rocketMessage.groupID() +
-				transactionMessage.topic() +
-				transactionMessage.tag();
-	}
+    public static String getProducerConsumerKey(RocketMessage rocketMessage, TransactionMessage transactionMessage) {
+        return rocketMessage.groupID() + transactionMessage.topic() + transactionMessage.tag();
+    }
 
-	public static Producer getProducer(Map<String, Object> consumerContainer, RocketMessage rocketMessage, CommonMessage commonMessage) {
-		String producerConsumerKey = ProducerConsumerFactory.getProducerConsumerKey(rocketMessage, commonMessage);
-		return (Producer) consumerContainer.get(producerConsumerKey);
-	}
+    public static Producer getProducer(Map<String, Object> consumerContainer, RocketMessage rocketMessage, CommonMessage commonMessage) {
+        String producerConsumerKey = ProducerConsumerFactory.getProducerConsumerKey(rocketMessage, commonMessage);
+        return (Producer) consumerContainer.get(producerConsumerKey);
+    }
 
-	public static OrderProducer getProducer(Map<String, Object> consumerContainer, RocketMessage rocketMessage, OrderMessage orderMessage) {
-		String producerConsumerKey = ProducerConsumerFactory.getProducerConsumerKey(rocketMessage, orderMessage);
-		return (OrderProducer) consumerContainer.get(producerConsumerKey);
-	}
+    public static OrderProducer getProducer(Map<String, Object> consumerContainer, RocketMessage rocketMessage, OrderMessage orderMessage) {
+        String producerConsumerKey = ProducerConsumerFactory.getProducerConsumerKey(rocketMessage, orderMessage);
+        return (OrderProducer) consumerContainer.get(producerConsumerKey);
+    }
 
-	public static TransactionProducer getProducer(Map<String, Object> consumerContainer, RocketMessage rocketMessage, TransactionMessage transactionMessage) {
-		String producerConsumerKey = ProducerConsumerFactory.getProducerConsumerKey(rocketMessage, transactionMessage);
-		return (TransactionProducer) consumerContainer.get(producerConsumerKey);
-	}
+    public static TransactionProducer getProducer(Map<String, Object> consumerContainer, RocketMessage rocketMessage, TransactionMessage transactionMessage) {
+        String producerConsumerKey = ProducerConsumerFactory.getProducerConsumerKey(rocketMessage, transactionMessage);
+        return (TransactionProducer) consumerContainer.get(producerConsumerKey);
+    }
 }

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerFactory.java

@@ -35,7 +35,7 @@ import java.util.Properties;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class ProducerFactory {
+public final class ProducerFactory {
 	private ProducerFactory() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ProducerPropertiesFactory.java

@@ -30,7 +30,7 @@ import java.util.Properties;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class ProducerPropertiesFactory {
+public final class ProducerPropertiesFactory {
 	private ProducerPropertiesFactory() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/PropertiesFactory.java

@@ -29,7 +29,7 @@ import java.util.Properties;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class PropertiesFactory {
+public final class PropertiesFactory {
 	private PropertiesFactory() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/SendMessageFactory.java

@@ -36,7 +36,7 @@ import org.springframework.context.ApplicationContext;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class SendMessageFactory {
+public final class SendMessageFactory {
     private SendMessageFactory() {
     }
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ShardingKeyFactory.java

@@ -28,7 +28,7 @@ import java.lang.reflect.Parameter;
  * @author ThierrySquirrel
  * @since JDK 11
  */
-public class ShardingKeyFactory {
+public final class ShardingKeyFactory {
     private ShardingKeyFactory() {
     }
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/StartDeliverTimeFactory.java

@@ -29,7 +29,7 @@ import java.lang.reflect.Parameter;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class StartDeliverTimeFactory {
+public final class StartDeliverTimeFactory {
 
     private StartDeliverTimeFactory() {
     }

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/ThreadPoolFactory.java

@@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class ThreadPoolFactory {
+public final class ThreadPoolFactory {
     private ThreadPoolFactory() {
     }
 

+ 5 - 8
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/ConsumerFactoryExecution.java

@@ -59,19 +59,16 @@ public class ConsumerFactoryExecution extends AbstractConsumerThread {
             OrderConsumer orderConsumer = ConsumerFactory.createOrderConsumer(properties);
             orderConsumer.subscribe(consumerListener.topic(), consumerListener.tag(), new DefaultMessageOrderListener(methodFactoryExecution));
             orderConsumer.start();
-            return;
-        }
-        if (consumerListener.batchConsumer()) {
+        } else if (consumerListener.batchConsumer()) {
             properties.put(PropertyKeyConst.ConsumeMessageBatchMaxSize, consumerListener.consumeMessageBatchMaxSize());
             properties.put(PropertyKeyConst.BatchConsumeMaxAwaitDurationInSeconds, consumerListener.batchConsumeMaxAwaitDurationInSeconds());
             BatchConsumer batchConsumer = ConsumerFactory.createBatchConsumer(properties);
             batchConsumer.subscribe(consumerListener.topic(), consumerListener.tag(), new DefaultBatchMessageListener(methodFactoryExecution));
             batchConsumer.start();
-            return;
+        } else {
+            Consumer consumer = ConsumerFactory.createConsumer(properties);
+            consumer.subscribe(consumerListener.topic(), consumerListener.tag(), new DefaultMessageListener(methodFactoryExecution));
+            consumer.start();
         }
-
-        Consumer consumer = ConsumerFactory.createConsumer(properties);
-        consumer.subscribe(consumerListener.topic(), consumerListener.tag(), new DefaultMessageListener(methodFactoryExecution));
-        consumer.start();
     }
 }

+ 4 - 2
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/ProducerFactoryExecution.java

@@ -34,7 +34,8 @@ import java.util.Map;
  */
 public class ProducerFactoryExecution extends AbstractProducerThread {
 
-	public ProducerFactoryExecution(Map<String, Object> producerConsumer, RocketMessage rocketMessage, Object bean, AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
+	public ProducerFactoryExecution(Map<String, Object> producerConsumer, RocketMessage rocketMessage, Object bean,
+									AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
 		super(producerConsumer, rocketMessage, bean, rocketProperties, applicationContext);
 	}
 
@@ -48,7 +49,8 @@ public class ProducerFactoryExecution extends AbstractProducerThread {
 	 * @param applicationContext applicationContext
 	 */
 	@Override
-	protected void statsPutProducer(Map<String, Object> producerConsumer, RocketMessage rocketMessage, Object bean, AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
+	protected void statsPutProducer(Map<String, Object> producerConsumer, RocketMessage rocketMessage, Object bean,
+									AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
 		PutProducerStrategy.putProducer(producerConsumer, rocketMessage, bean, rocketProperties, applicationContext);
 	}
 }

+ 6 - 3
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/factory/execution/SendMessageFactoryExecution.java

@@ -37,8 +37,9 @@ import java.util.Map;
 @Slf4j
 public class SendMessageFactoryExecution extends AbstractSendMessageThread {
 
-
-    public SendMessageFactoryExecution(Long startDeliverTime, String shardingKeyFactory, Map<String, Object> consumerContainer, RocketMessage rocketMessage, Object message, byte[] bytes, ApplicationContext applicationContext) {
+    public SendMessageFactoryExecution(Long startDeliverTime, String shardingKeyFactory,
+                                       Map<String, Object> consumerContainer, RocketMessage rocketMessage,
+                                       Object message, byte[] bytes, ApplicationContext applicationContext) {
         super(startDeliverTime, shardingKeyFactory, consumerContainer, rocketMessage, message, bytes, applicationContext);
     }
 
@@ -54,7 +55,9 @@ public class SendMessageFactoryExecution extends AbstractSendMessageThread {
      * @param applicationContext applicationContext
      */
     @Override
-    protected void statsSendMessage(Long startDeliverTime, String shardingKeyFactory, Map<String, Object> consumerContainer, RocketMessage rocketMessage, Object message, byte[] bytes, ApplicationContext applicationContext) {
+    protected void statsSendMessage(Long startDeliverTime, String shardingKeyFactory,
+                                    Map<String, Object> consumerContainer, RocketMessage rocketMessage,
+                                    Object message, byte[] bytes, ApplicationContext applicationContext) {
         ProducerStrategy.statsSendMessage(startDeliverTime, shardingKeyFactory, consumerContainer, rocketMessage, message, bytes, applicationContext);
     }
 }

+ 3 - 2
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/strategy/RocketConsumerStrategy.java

@@ -38,11 +38,12 @@ import java.util.concurrent.ThreadPoolExecutor;
  * @since JDK 1.8
  */
 
-public class RocketConsumerStrategy {
+public final class RocketConsumerStrategy {
     private RocketConsumerStrategy() {
     }
 
-    public static void putProducer(ThreadPoolExecutor threadPoolExecutor, Map<String, Object> producerConsumer, Object bean, AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
+    public static void putProducer(ThreadPoolExecutor threadPoolExecutor, Map<String, Object> producerConsumer,
+                                   Object bean, AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
         RocketMessage rocketMessage = bean.getClass().getAnnotation(RocketMessage.class);
         AnnotatedMethodsUtils.getMethodAndAnnotation(bean, CommonMessage.class).
                 forEach((method, commonMessage) -> {

+ 3 - 7
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/strategy/SendMessageStrategy.java

@@ -32,20 +32,16 @@ import org.springframework.context.ApplicationContext;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class SendMessageStrategy {
+public final class SendMessageStrategy {
 	private SendMessageStrategy() {
 	}
 
 	public static void send(CommonMessage commonMessage, Producer producer, Message message, ApplicationContext applicationContext) {
 		if (commonMessage.messageSendType().equals(MessageSendType.SEND)) {
 			producer.send(message);
-			return;
-		}
-		if (commonMessage.messageSendType().equals(MessageSendType.SEND_ASYNC)) {
+		} else if (commonMessage.messageSendType().equals(MessageSendType.SEND_ASYNC)) {
 			producer.sendAsync(message, ApplicationContextUtils.getSendCallback(applicationContext, commonMessage.callback()));
-			return;
-		}
-		if (commonMessage.messageSendType().equals(MessageSendType.SEND_ONE_WAY)) {
+		} else if (commonMessage.messageSendType().equals(MessageSendType.SEND_ONE_WAY)) {
 			producer.sendOneway(message);
 		}
 	}

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/AnnotatedMethodsUtils.java

@@ -31,7 +31,7 @@ import java.util.Map;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class AnnotatedMethodsUtils {
+public final class AnnotatedMethodsUtils {
 	private AnnotatedMethodsUtils() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/ApplicationContextUtils.java

@@ -33,7 +33,7 @@ import org.springframework.context.ApplicationContext;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class ApplicationContextUtils {
+public final class ApplicationContextUtils {
 	private ApplicationContextUtils() {
 	}
 

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/AspectUtils.java

@@ -31,7 +31,7 @@ import java.lang.reflect.Parameter;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class AspectUtils {
+public final class AspectUtils {
 	private AspectUtils() {
 	}
 

+ 7 - 3
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/InterceptRocket.java

@@ -34,15 +34,19 @@ import java.util.concurrent.ThreadPoolExecutor;
  * @author ThierrySquirrel
  * @since JDK 1.8
  */
-public class InterceptRocket {
+public final class InterceptRocket {
     private InterceptRocket() {
     }
 
-    public static <T extends Annotation> Object intercept(Long startDeliverTime, String shardingKeyFactory, RocketMessage rocketMessage, T annotation, Object proceed, Map<String, Object> consumerContainer, ThreadPoolExecutor threadPoolExecutor, ApplicationContext applicationContext) {
+    public static <T extends Annotation> Object intercept(Long startDeliverTime, String shardingKeyFactory,
+                                                          RocketMessage rocketMessage, T annotation, Object proceed,
+                                                          Map<String, Object> consumerContainer, ThreadPoolExecutor threadPoolExecutor,
+                                                          ApplicationContext applicationContext) {
         RocketSerializer mqSerializer = applicationContext.getBean(RocketSerializer.class);
         byte[] body = mqSerializer.serialize(proceed);
 
-        ThreadPoolExecutorExecution.statsThread(threadPoolExecutor, new SendMessageFactoryExecution(startDeliverTime, shardingKeyFactory, consumerContainer, rocketMessage, annotation, body, applicationContext));
+        ThreadPoolExecutorExecution.statsThread(threadPoolExecutor,
+                new SendMessageFactoryExecution(startDeliverTime, shardingKeyFactory, consumerContainer, rocketMessage, annotation, body, applicationContext));
         return proceed;
     }
 }

+ 5 - 2
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/thread/AbstractProducerThread.java

@@ -39,7 +39,8 @@ public abstract class AbstractProducerThread implements Runnable {
 	private AliyunRocketProperties rocketProperties;
 	private ApplicationContext applicationContext;
 
-	protected AbstractProducerThread(Map<String, Object> producerConsumer, RocketMessage rocketMessage, Object bean, AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
+	protected AbstractProducerThread(Map<String, Object> producerConsumer, RocketMessage rocketMessage, Object bean,
+									 AliyunRocketProperties rocketProperties, ApplicationContext applicationContext) {
 		this.producerConsumer = producerConsumer;
 		this.rocketMessage = rocketMessage;
 		this.bean = bean;
@@ -56,7 +57,9 @@ public abstract class AbstractProducerThread implements Runnable {
 	 * @param rocketProperties   rocketProperties
 	 * @param applicationContext applicationContext
 	 */
-	protected abstract void statsPutProducer(Map<String, Object> producerConsumer, RocketMessage rocketMessage, Object bean, AliyunRocketProperties rocketProperties, ApplicationContext applicationContext);
+	protected abstract void statsPutProducer(Map<String, Object> producerConsumer, RocketMessage rocketMessage,
+											 Object bean, AliyunRocketProperties rocketProperties,
+											 ApplicationContext applicationContext);
 
 	@Override
 	public void run() {

+ 7 - 3
helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/thread/AbstractSendMessageThread.java

@@ -40,7 +40,9 @@ public abstract class AbstractSendMessageThread implements Runnable {
     private byte[] bytes;
     private ApplicationContext applicationContext;
 
-    protected AbstractSendMessageThread(Long startDeliverTime, String shardingKeyFactory, Map<String, Object> consumerContainer, RocketMessage rocketMessage, Object message, byte[] bytes, ApplicationContext applicationContext) {
+    protected AbstractSendMessageThread(Long startDeliverTime, String shardingKeyFactory,
+                                        Map<String, Object> consumerContainer, RocketMessage rocketMessage,
+                                        Object message, byte[] bytes, ApplicationContext applicationContext) {
         this.startDeliverTime = startDeliverTime;
         this.shardingKeyFactory = shardingKeyFactory;
         this.consumerContainer = consumerContainer;
@@ -61,11 +63,13 @@ public abstract class AbstractSendMessageThread implements Runnable {
      * @param bytes              bytes
      * @param applicationContext applicationContext
      */
-    protected abstract void statsSendMessage(Long startDeliverTime, String shardingKeyFactory, Map<String, Object> consumerContainer, RocketMessage rocketMessage, Object message, byte[] bytes, ApplicationContext applicationContext);
+    protected abstract void statsSendMessage(Long startDeliverTime, String shardingKeyFactory,
+                                             Map<String, Object> consumerContainer, RocketMessage rocketMessage,
+                                             Object message, byte[] bytes, ApplicationContext applicationContext);
 
     @Override
     public void run() {
-        statsSendMessage (startDeliverTime, shardingKeyFactory, consumerContainer,
+        statsSendMessage(startDeliverTime, shardingKeyFactory, consumerContainer,
                 rocketMessage,
                 message,
                 bytes,

+ 1 - 1
helio-starter-rocketmq-aliyun/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +1 @@
-cc.uncarbon.framework.rocketmq.config.AliyunRocketAutoConfiguration
+cc.uncarbon.framework.rocketmq.config.AliyunRocketAutoConfiguration

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

@@ -1,45 +1,45 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-satoken</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>jakarta.servlet</groupId>
-            <artifactId>jakarta.servlet-api</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>cn.dev33</groupId>
-            <artifactId>sa-token-spring-boot3-starter</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>cn.dev33</groupId>
-            <artifactId>sa-token-redis-jackson</artifactId>
-        </dependency>
-    </dependencies>
-
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-satoken</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-spring-boot3-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-redis-jackson</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 27 - 27
helio-starter-satoken/src/main/java/cc/uncarbon/framework/satoken/config/SaTokenLocalCacheAutoConfiguration.java

@@ -1,27 +1,27 @@
-package cc.uncarbon.framework.satoken.config;
-
-import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.satoken.dao.SaTokenLocalCacheDao;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Primary;
-
-/**
- * SA-Token 本地缓存自动配置类
- *
- * @author Uncarbon
- */
-@ConditionalOnExpression(value = "${helio.security.saTokenLocalCache.enabled:false} || ${helio.security.sa-token-local-cache.enabled:false}")
-@AutoConfiguration
-public class SaTokenLocalCacheAutoConfiguration {
-
-    @ConditionalOnMissingBean(value = SaTokenLocalCacheDao.class)
-    @Primary
-    @Bean
-    public SaTokenLocalCacheDao saTokenLocalCacheDao(HelioProperties helioProperties) {
-        HelioProperties.Security.SaTokenLocalCache props = helioProperties.getSecurity().getSaTokenLocalCache();
-        return new SaTokenLocalCacheDao(props.getDuration(), props.getCapacity());
-    }
-}
+package cc.uncarbon.framework.satoken.config;
+
+import cc.uncarbon.framework.core.props.HelioProperties;
+import cc.uncarbon.framework.satoken.dao.SaTokenLocalCacheDao;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+
+/**
+ * SA-Token 本地缓存自动配置类
+ *
+ * @author Uncarbon
+ */
+@ConditionalOnExpression(value = "${helio.security.saTokenLocalCache.enabled:false} || ${helio.security.sa-token-local-cache.enabled:false}")
+@AutoConfiguration
+public class SaTokenLocalCacheAutoConfiguration {
+
+    @ConditionalOnMissingBean(value = SaTokenLocalCacheDao.class)
+    @Primary
+    @Bean
+    public SaTokenLocalCacheDao saTokenLocalCacheDao(HelioProperties helioProperties) {
+        HelioProperties.Security.SaTokenLocalCache props = helioProperties.getSecurity().getSaTokenLocalCache();
+        return new SaTokenLocalCacheDao(props.getDuration(), props.getCapacity());
+    }
+}

+ 180 - 180
helio-starter-satoken/src/main/java/cc/uncarbon/framework/satoken/dao/SaTokenLocalCacheDao.java

@@ -1,180 +1,180 @@
-package cc.uncarbon.framework.satoken.dao;
-
-import cc.uncarbon.framework.core.cache.TimedCacheEx;
-import cn.dev33.satoken.dao.SaTokenDaoRedisJackson;
-
-/**
- * SA-Token 读取时,本地缓存一定时长,减少对 Redis 的 IO
- * 在多实例部署时存在一定安全风险,有可能用户登出时,A机器的缓存已清除,但B机器的缓存还认为有效。所以请谨慎设置本地缓存有效时长
- * 底层实现为读写锁,大量数据下需要注意 FullGC 风险
- *
- * @since 2.1.0
- * @author Uncarbon
- */
-public class SaTokenLocalCacheDao extends SaTokenDaoRedisJackson {
-
-    /**
-     * 标记 key 为空值
-     */
-    protected final TimedCacheEx<String, Boolean> nullValueMarker;
-    protected final TimedCacheEx<String, String> stringValueCache;
-    protected final TimedCacheEx<String, Object> objectValueCache;
-
-
-    /**
-     * 创建本地缓存 SaTokenDao
-     * @param duration 本地缓存有效时长,单位=秒
-     * @param capacity 本地缓存最大容量,注意:每次超过容量都会触发一次清理过程
-     */
-    public SaTokenLocalCacheDao(long duration, int capacity) {
-        if (duration < 1) {
-            throw new IllegalArgumentException("duration CANNOT less than 1");
-        }
-        if (capacity < 1) {
-            throw new IllegalArgumentException("capacity CANNOT less than 1");
-        }
-
-        long asMilliseconds = duration * 1000;
-
-        this.nullValueMarker = new TimedCacheEx<>(asMilliseconds, capacity);
-        this.stringValueCache = new TimedCacheEx<>(asMilliseconds, capacity);
-        this.objectValueCache = new TimedCacheEx<>(asMilliseconds, capacity);
-        // 默认开nullValueMarker的清理定时任务,避免长时间缓存空值
-        enableNullValueMarkerPruneSchedule(400);
-        // 不默认开stringValueCache、objectValueCache的清理定时任务
-    }
-
-    /**
-     * 开nullValueMarker的清理定时任务
-     * @param delay 间隔时长,单位毫秒
-     */
-    public void enableNullValueMarkerPruneSchedule(long delay) {
-        this.nullValueMarker.schedulePrune(delay);
-    }
-
-    /**
-     * 关nullValueMarker的清理定时任务
-     */
-    public void disableNullValueMarkerPruneSchedule() {
-        this.nullValueMarker.cancelPruneSchedule();
-    }
-
-    /**
-     * 开stringValueCache的清理定时任务
-     * @param delay 间隔时长,单位毫秒
-     */
-    public void enableStringValueCachePruneSchedule(long delay) {
-        this.stringValueCache.schedulePrune(delay);
-    }
-
-    /**
-     * 关stringValueCache的清理定时任务
-     */
-    public void disableStringValueCachePruneSchedule() {
-        this.stringValueCache.cancelPruneSchedule();
-    }
-
-    /**
-     * 开objectValueCache的清理定时任务
-     * @param delay 间隔时长,单位毫秒
-     */
-    public void enableObjectValueCachePruneSchedule(long delay) {
-        this.objectValueCache.schedulePrune(delay);
-    }
-
-    /**
-     * 关objectValueCache的清理定时任务
-     */
-    public void disableObjectValueCachePruneSchedule() {
-        this.objectValueCache.cancelPruneSchedule();
-    }
-
-    @Override
-    public String get(String key) {
-        if (nullValueMarker.containsKey(key)) {
-            // 明确知道为空,直接返回
-            return null;
-        }
-        String cacheValue = stringValueCache.get(key, false);
-        if (cacheValue == null) {
-            // 本地缓存为空,实时查询
-            cacheValue = super.get(key);
-        }
-        // cache-aside
-        if (cacheValue == null) {
-            nullValueMarker.put(key, Boolean.TRUE);
-        } else {
-            stringValueCache.put(key, cacheValue);
-        }
-        return cacheValue;
-    }
-
-    @Override
-    public void set(String key, String value, long timeout) {
-        super.set(key, value, timeout);
-        // cache-aside
-        nullValueMarker.remove(key);
-        stringValueCache.remove(key);
-    }
-
-    @Override
-    public void update(String key, String value) {
-        super.update(key, value);
-        // cache-aside
-        nullValueMarker.remove(key);
-        stringValueCache.remove(key);
-    }
-
-    @Override
-    public void delete(String key) {
-        super.delete(key);
-        // cache-aside
-        nullValueMarker.remove(key);
-        stringValueCache.remove(key);
-    }
-
-    @Override
-    public Object getObject(String key) {
-        if (nullValueMarker.containsKey(key)) {
-            // 明确知道为空,直接返回
-            return null;
-        }
-        Object cacheValue = objectValueCache.get(key, false);
-        if (cacheValue == null) {
-            // 本地缓存为空,实时查询
-            cacheValue = super.getObject(key);
-        }
-        // cache-aside
-        if (cacheValue == null) {
-            nullValueMarker.put(key, Boolean.TRUE);
-        } else {
-            objectValueCache.put(key, cacheValue);
-        }
-        return cacheValue;
-    }
-
-    @Override
-    public void setObject(String key, Object object, long timeout) {
-        super.setObject(key, object, timeout);
-        // cache-aside
-        nullValueMarker.remove(key);
-        objectValueCache.remove(key);
-    }
-
-    @Override
-    public void updateObject(String key, Object object) {
-        super.updateObject(key, object);
-        // cache-aside
-        nullValueMarker.remove(key);
-        objectValueCache.remove(key);
-    }
-
-    @Override
-    public void deleteObject(String key) {
-        super.deleteObject(key);
-        // cache-aside
-        nullValueMarker.remove(key);
-        objectValueCache.remove(key);
-    }
-
-}
+package cc.uncarbon.framework.satoken.dao;
+
+import cc.uncarbon.framework.core.cache.TimedCacheEx;
+import cn.dev33.satoken.dao.SaTokenDaoRedisJackson;
+
+/**
+ * SA-Token 读取时,本地缓存一定时长,减少对 Redis 的 IO
+ * 在多实例部署时存在一定安全风险,有可能用户登出时,A机器的缓存已清除,但B机器的缓存还认为有效。所以请谨慎设置本地缓存有效时长
+ * 底层实现为读写锁,大量数据下需要注意 FullGC 风险
+ *
+ * @since 2.1.0
+ * @author Uncarbon
+ */
+public class SaTokenLocalCacheDao extends SaTokenDaoRedisJackson {
+
+    /**
+     * 标记 key 为空值
+     */
+    protected final TimedCacheEx<String, Boolean> nullValueMarker;
+    protected final TimedCacheEx<String, String> stringValueCache;
+    protected final TimedCacheEx<String, Object> objectValueCache;
+
+
+    /**
+     * 创建本地缓存 SaTokenDao
+     * @param duration 本地缓存有效时长,单位=秒
+     * @param capacity 本地缓存最大容量,注意:每次超过容量都会触发一次清理过程
+     */
+    public SaTokenLocalCacheDao(long duration, int capacity) {
+        if (duration < 1) {
+            throw new IllegalArgumentException("duration CANNOT less than 1");
+        }
+        if (capacity < 1) {
+            throw new IllegalArgumentException("capacity CANNOT less than 1");
+        }
+
+        long asMilliseconds = duration * 1000;
+
+        this.nullValueMarker = new TimedCacheEx<>(asMilliseconds, capacity);
+        this.stringValueCache = new TimedCacheEx<>(asMilliseconds, capacity);
+        this.objectValueCache = new TimedCacheEx<>(asMilliseconds, capacity);
+        // 默认开nullValueMarker的清理定时任务,避免长时间缓存空值
+        enableNullValueMarkerPruneSchedule(400);
+        // 不默认开stringValueCache、objectValueCache的清理定时任务
+    }
+
+    /**
+     * 开nullValueMarker的清理定时任务
+     * @param delay 间隔时长,单位毫秒
+     */
+    public void enableNullValueMarkerPruneSchedule(long delay) {
+        this.nullValueMarker.schedulePrune(delay);
+    }
+
+    /**
+     * 关nullValueMarker的清理定时任务
+     */
+    public void disableNullValueMarkerPruneSchedule() {
+        this.nullValueMarker.cancelPruneSchedule();
+    }
+
+    /**
+     * 开stringValueCache的清理定时任务
+     * @param delay 间隔时长,单位毫秒
+     */
+    public void enableStringValueCachePruneSchedule(long delay) {
+        this.stringValueCache.schedulePrune(delay);
+    }
+
+    /**
+     * 关stringValueCache的清理定时任务
+     */
+    public void disableStringValueCachePruneSchedule() {
+        this.stringValueCache.cancelPruneSchedule();
+    }
+
+    /**
+     * 开objectValueCache的清理定时任务
+     * @param delay 间隔时长,单位毫秒
+     */
+    public void enableObjectValueCachePruneSchedule(long delay) {
+        this.objectValueCache.schedulePrune(delay);
+    }
+
+    /**
+     * 关objectValueCache的清理定时任务
+     */
+    public void disableObjectValueCachePruneSchedule() {
+        this.objectValueCache.cancelPruneSchedule();
+    }
+
+    @Override
+    public String get(String key) {
+        if (nullValueMarker.containsKey(key)) {
+            // 明确知道为空,直接返回
+            return null;
+        }
+        String cacheValue = stringValueCache.get(key, false);
+        if (cacheValue == null) {
+            // 本地缓存为空,实时查询
+            cacheValue = super.get(key);
+        }
+        // cache-aside
+        if (cacheValue == null) {
+            nullValueMarker.put(key, Boolean.TRUE);
+        } else {
+            stringValueCache.put(key, cacheValue);
+        }
+        return cacheValue;
+    }
+
+    @Override
+    public void set(String key, String value, long timeout) {
+        super.set(key, value, timeout);
+        // cache-aside
+        nullValueMarker.remove(key);
+        stringValueCache.remove(key);
+    }
+
+    @Override
+    public void update(String key, String value) {
+        super.update(key, value);
+        // cache-aside
+        nullValueMarker.remove(key);
+        stringValueCache.remove(key);
+    }
+
+    @Override
+    public void delete(String key) {
+        super.delete(key);
+        // cache-aside
+        nullValueMarker.remove(key);
+        stringValueCache.remove(key);
+    }
+
+    @Override
+    public Object getObject(String key) {
+        if (nullValueMarker.containsKey(key)) {
+            // 明确知道为空,直接返回
+            return null;
+        }
+        Object cacheValue = objectValueCache.get(key, false);
+        if (cacheValue == null) {
+            // 本地缓存为空,实时查询
+            cacheValue = super.getObject(key);
+        }
+        // cache-aside
+        if (cacheValue == null) {
+            nullValueMarker.put(key, Boolean.TRUE);
+        } else {
+            objectValueCache.put(key, cacheValue);
+        }
+        return cacheValue;
+    }
+
+    @Override
+    public void setObject(String key, Object object, long timeout) {
+        super.setObject(key, object, timeout);
+        // cache-aside
+        nullValueMarker.remove(key);
+        objectValueCache.remove(key);
+    }
+
+    @Override
+    public void updateObject(String key, Object object) {
+        super.updateObject(key, object);
+        // cache-aside
+        nullValueMarker.remove(key);
+        objectValueCache.remove(key);
+    }
+
+    @Override
+    public void deleteObject(String key) {
+        super.deleteObject(key);
+        // cache-aside
+        nullValueMarker.remove(key);
+        objectValueCache.remove(key);
+    }
+
+}

+ 1 - 1
helio-starter-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +1 @@
-cc.uncarbon.framework.satoken.config.SaTokenLocalCacheAutoConfiguration
+cc.uncarbon.framework.satoken.config.SaTokenLocalCacheAutoConfiguration

+ 0 - 28
helio-starter-seata/pom.xml

@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-seata</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>io.seata</groupId>
-            <artifactId>seata-spring-boot-starter</artifactId>
-        </dependency>
-    </dependencies>
-</project>

+ 0 - 9
helio-starter-seata/src/main/java/cc/uncarbon/framework/seata/stub/HelioStarterSeataStub.java

@@ -1,9 +0,0 @@
-package cc.uncarbon.framework.seata.stub;
-
-/**
- * 需要一个空类以解决:Missing: no javadoc jar found in folder
- */
-public final class HelioStarterSeataStub {
-    private HelioStarterSeataStub() {
-    }
-}

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

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

+ 22 - 22
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/annotation/EnableGlobalTenantDataSource.java

@@ -1,22 +1,22 @@
-package cc.uncarbon.framework.tenant.annotation;
-
-import cc.uncarbon.framework.tenant.config.GlobalTenantDataSourceConfiguration;
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import org.springframework.context.annotation.Import;
-
-/**
- * 启用基于全局 AOP 的数据源级多租户
- *
- * @author Uncarbon
- */
-@Import(value = GlobalTenantDataSourceConfiguration.class)
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface EnableGlobalTenantDataSource {
-
-}
+package cc.uncarbon.framework.tenant.annotation;
+
+import cc.uncarbon.framework.tenant.config.GlobalTenantDataSourceConfiguration;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.springframework.context.annotation.Import;
+
+/**
+ * 启用基于全局 AOP 的数据源级多租户
+ *
+ * @author Uncarbon
+ */
+@Import(value = GlobalTenantDataSourceConfiguration.class)
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface EnableGlobalTenantDataSource {
+
+}

+ 19 - 19
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/annotation/IgnoreTenantDataSource.java

@@ -1,19 +1,19 @@
-package cc.uncarbon.framework.tenant.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * 数据源级多租户专用,忽略 AOP 拦截
- *
- * @author Uncarbon
- */
-@Target({ElementType.TYPE, ElementType.METHOD})
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface IgnoreTenantDataSource {
-
-}
+package cc.uncarbon.framework.tenant.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 数据源级多租户专用,忽略 AOP 拦截
+ *
+ * @author Uncarbon
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface IgnoreTenantDataSource {
+
+}

+ 1 - 2
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/config/GlobalTenantDataSourceConfiguration.java

@@ -12,8 +12,7 @@ import org.springframework.context.annotation.Role;
 
 /**
  * 基于全局 AOP 的数据源级多租户配置类
- * 参考
- * https://blog.csdn.net/w57685321/article/details/106823660
+ * <a href="https://blog.csdn.net/w57685321/article/details/106823660">参考文章</a>
  *
  * @author Uncarbon
  */

+ 45 - 46
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/config/HelioTenantAutoConfiguration.java

@@ -1,46 +1,45 @@
-package cc.uncarbon.framework.tenant.config;
-
-import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.crud.support.TenantSupport;
-import cc.uncarbon.framework.crud.support.impl.DefaultTenantSupport;
-import cc.uncarbon.framework.tenant.support.TenantDataSourceSupport;
-import cc.uncarbon.framework.tenant.support.TenantLineSupport;
-import lombok.RequiredArgsConstructor;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Primary;
-
-/**
- * Helio 多租户自动配置类
- * 当启用多租户功能,且多租户隔离级别配置正确,则向 IoC 容器注入对应处理 Bean
- *
- * @author Uncarbon
- */
-@RequiredArgsConstructor
-@AutoConfiguration
-public class HelioTenantAutoConfiguration {
-
-    private final HelioProperties helioProperties;
-
-
-    @Bean
-    @Primary
-    public TenantSupport tenantSupport() {
-        if (!Boolean.TRUE.equals(helioProperties.getTenant().getEnabled())) {
-            // 引入了 starter,但未启用多租户
-            return new DefaultTenantSupport();
-        }
-
-        switch (helioProperties.getTenant().getIsolateLevel()) {
-            case LINE:
-                // 行级
-                return new TenantLineSupport();
-            case DATASOURCE:
-                // 数据源级
-                return new TenantDataSourceSupport();
-            default:
-                throw new IllegalArgumentException("启用多租户功能后,请正确配置对应的多租户隔离级别(helio.tenant.isolate-level)");
-        }
-    }
-
-}
+package cc.uncarbon.framework.tenant.config;
+
+import cc.uncarbon.framework.core.enums.TenantIsolateLevelEnum;
+import cc.uncarbon.framework.core.props.HelioProperties;
+import cc.uncarbon.framework.crud.support.TenantSupport;
+import cc.uncarbon.framework.crud.support.impl.DefaultTenantSupport;
+import cc.uncarbon.framework.tenant.support.TenantDataSourceSupport;
+import cc.uncarbon.framework.tenant.support.TenantLineSupport;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+
+/**
+ * Helio 多租户自动配置类
+ * 当启用多租户功能,且多租户隔离级别配置正确,则向 IoC 容器注入对应处理 Bean
+ *
+ * @author Uncarbon
+ */
+@RequiredArgsConstructor
+@AutoConfiguration
+public class HelioTenantAutoConfiguration {
+
+    private final HelioProperties helioProperties;
+
+
+    @Bean
+    @Primary
+    public TenantSupport tenantSupport() {
+        if (!Boolean.TRUE.equals(helioProperties.getTenant().getEnabled())) {
+            // 引入了 starter,但未启用多租户
+            return new DefaultTenantSupport();
+        }
+
+        TenantIsolateLevelEnum isolateLevel = helioProperties.getTenant().getIsolateLevel();
+        if (isolateLevel == TenantIsolateLevelEnum.LINE) {
+            // 行级
+            return new TenantLineSupport();
+        } else if (isolateLevel == TenantIsolateLevelEnum.DATASOURCE) {
+            // 数据源级
+            return new TenantDataSourceSupport();
+        }
+        throw new IllegalArgumentException("启用多租户功能后,请正确配置对应的多租户隔离级别(helio.tenant.isolate-level)");
+    }
+}

+ 30 - 30
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantDataSourceSupport.java

@@ -1,30 +1,30 @@
-package cc.uncarbon.framework.tenant.support;
-
-import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.crud.support.TenantSupport;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.util.ClassUtils;
-
-/**
- * 多租户支持-数据源级
- *
- * @author Uncarbon
- */
-@Slf4j
-public class TenantDataSourceSupport implements TenantSupport {
-
-    @Override
-    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
-        log.info("\n\n[多租户支持] >> 隔离级别: 数据源级");
-
-        System.err.println("数据源级多租户支持还处于试验阶段,请经过测试后再投入生产使用!");
-
-        try {
-            Class.forName("com.baomidou.dynamic.datasource.DynamicRoutingDataSource",
-                    false, ClassUtils.getDefaultClassLoader());
-        } catch (ClassNotFoundException cnfe) {
-            System.err.println("\n\n ERROR: 没有找到 dynamic-datasource-spring-boot-starter 依赖,请检查是否引入");
-        }
-    }
-}
+package cc.uncarbon.framework.tenant.support;
+
+import cc.uncarbon.framework.core.props.HelioProperties;
+import cc.uncarbon.framework.crud.support.TenantSupport;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.ClassUtils;
+
+/**
+ * 多租户支持-数据源级
+ *
+ * @author Uncarbon
+ */
+@Slf4j
+public class TenantDataSourceSupport implements TenantSupport {
+
+    @Override
+    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
+        log.info("\n\n[多租户支持] >> 隔离级别: 数据源级");
+
+        System.err.println("数据源级多租户支持还处于试验阶段,请经过测试后再投入生产使用!");
+
+        try {
+            Class.forName("com.baomidou.dynamic.datasource.DynamicRoutingDataSource",
+                    false, ClassUtils.getDefaultClassLoader());
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("\n\n ERROR: 没有找到 dynamic-datasource-spring-boot-starter 依赖,请检查是否引入");
+        }
+    }
+}

+ 90 - 90
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantLineSupport.java

@@ -1,90 +1,90 @@
-package cc.uncarbon.framework.tenant.support;
-
-import cc.uncarbon.framework.core.constant.HelioConstant;
-import cc.uncarbon.framework.core.context.TenantContextHolder;
-import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.crud.support.TenantSupport;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
-import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import net.sf.jsqlparser.expression.Expression;
-import net.sf.jsqlparser.expression.LongValue;
-
-import java.util.Collection;
-import java.util.Objects;
-
-/**
- * 多租户支持-行级
- *
- * @author Uncarbon
- */
-@Slf4j
-public class TenantLineSupport implements TenantSupport {
-
-    @Override
-    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
-        Collection<String> ignoredTables = helioProperties.getTenant().getIgnoredTables();
-
-        // 添加行级租户内联拦截器
-        interceptor.addInnerInterceptor(
-                new TenantLineInnerInterceptor(new HelioLineTenantHandler(
-                        helioProperties.getTenant().getPrivilegedTenantId(), ignoredTables)
-                )
-        );
-
-        log.info("\n\n[多租户支持] >> 隔离级别: 行级");
-
-        System.err.println("以下数据表不参与租户隔离: " + ignoredTables);
-    }
-
-
-    /**
-     * 行级租户mybatis-plus拦截器实现类
-     */
-    @AllArgsConstructor
-    public static class HelioLineTenantHandler implements TenantLineHandler {
-
-        /**
-         * 特权租户ID
-         */
-        private final Long privilegedTenantId;
-
-        /**
-         * 忽略租户隔离的表
-         */
-        private final Collection<String> ignoredTables;
-
-
-        @Override
-        public Expression getTenantId() {
-            Long currentTenantId = TenantContextHolder.getTenantId();
-            if (currentTenantId == null) {
-                return null;
-            }
-
-            return new LongValue(currentTenantId);
-        }
-
-        @Override
-        public String getTenantIdColumn() {
-            return HelioConstant.CRUD.COLUMN_TENANT_ID;
-        }
-
-        @Override
-        public boolean ignoreTable(String tableName) {
-            Long currentTenantId = TenantContextHolder.getTenantId();
-
-            if (Objects.nonNull(privilegedTenantId)
-                    && Objects.nonNull(currentTenantId)
-                    && privilegedTenantId.equals(currentTenantId)) {
-                return true;
-            }
-
-            return ignoredTables.contains(tableName);
-        }
-
-    }
-
-}
+package cc.uncarbon.framework.tenant.support;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import cc.uncarbon.framework.core.context.TenantContextHolder;
+import cc.uncarbon.framework.core.props.HelioProperties;
+import cc.uncarbon.framework.crud.support.TenantSupport;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.LongValue;
+
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * 多租户支持-行级
+ *
+ * @author Uncarbon
+ */
+@Slf4j
+public class TenantLineSupport implements TenantSupport {
+
+    @Override
+    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
+        Collection<String> ignoredTables = helioProperties.getTenant().getIgnoredTables();
+
+        // 添加行级租户内联拦截器
+        interceptor.addInnerInterceptor(
+                new TenantLineInnerInterceptor(new HelioLineTenantHandler(
+                        helioProperties.getTenant().getPrivilegedTenantId(), ignoredTables)
+                )
+        );
+
+        log.info("\n\n[多租户支持] >> 隔离级别: 行级");
+
+        System.err.println("以下数据表不参与租户隔离: " + ignoredTables);
+    }
+
+
+    /**
+     * 行级租户mybatis-plus拦截器实现类
+     */
+    @AllArgsConstructor
+    public static class HelioLineTenantHandler implements TenantLineHandler {
+
+        /**
+         * 特权租户ID
+         */
+        private final Long privilegedTenantId;
+
+        /**
+         * 忽略租户隔离的表
+         */
+        private final Collection<String> ignoredTables;
+
+
+        @Override
+        public Expression getTenantId() {
+            Long currentTenantId = TenantContextHolder.getTenantId();
+            if (currentTenantId == null) {
+                return null;
+            }
+
+            return new LongValue(currentTenantId);
+        }
+
+        @Override
+        public String getTenantIdColumn() {
+            return HelioConstant.CRUD.COLUMN_TENANT_ID;
+        }
+
+        @Override
+        public boolean ignoreTable(String tableName) {
+            Long currentTenantId = TenantContextHolder.getTenantId();
+
+            if (Objects.nonNull(privilegedTenantId)
+                    && Objects.nonNull(currentTenantId)
+                    && privilegedTenantId.equals(currentTenantId)) {
+                return true;
+            }
+
+            return ignoredTables.contains(tableName);
+        }
+
+    }
+
+}

+ 60 - 60
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantTableSupport.java

@@ -1,60 +1,60 @@
-package cc.uncarbon.framework.tenant.support;
-
-import cc.uncarbon.framework.core.context.TenantContextHolder;
-import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.crud.support.TenantSupport;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
-import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.Collection;
-
-/**
- * 多租户支持-表级
- * @deprecated 后来发现并不是太适合实践使用,仅保留本类
- *
- * @author Uncarbon
- */
-@Slf4j
-@Deprecated
-public class TenantTableSupport implements TenantSupport {
-
-    @Override
-    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
-        Collection<String> ignoredTables = helioProperties.getTenant().getIgnoredTables();
-
-        DynamicTableNameInnerInterceptor innerInterceptor = new DynamicTableNameInnerInterceptor();
-        innerInterceptor.setTableNameHandler(new HelioTableTenantHandler(ignoredTables));
-
-        // 添加表级租户内联拦截器
-        interceptor.addInnerInterceptor(innerInterceptor);
-
-        log.info("\n\n[多租户支持] >> 隔离级别: 表级,以下数据表不参与租户隔离: {}\n",
-                ignoredTables);
-    }
-
-    /**
-     * 表级租户mybatis-plus拦截器实现类
-     */
-    @AllArgsConstructor
-    public static class HelioTableTenantHandler implements TableNameHandler {
-
-        /**
-         * 忽略租户隔离的表
-         */
-        private Collection<String> ignoredTables;
-
-        @Override
-        public String dynamicTableName(String sql, String tableName) {
-            if (ignoredTables.contains(tableName)) {
-                return tableName;
-            }
-
-            // 拼接新表名
-            return String.format("%s_%s", tableName, TenantContextHolder.getTenantId());
-        }
-
-    }
-}
+package cc.uncarbon.framework.tenant.support;
+
+import cc.uncarbon.framework.core.context.TenantContextHolder;
+import cc.uncarbon.framework.core.props.HelioProperties;
+import cc.uncarbon.framework.crud.support.TenantSupport;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
+import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Collection;
+
+/**
+ * 多租户支持-表级
+ * @deprecated 后来发现并不是太适合实践使用,仅保留本类
+ *
+ * @author Uncarbon
+ */
+@Slf4j
+@Deprecated
+public class TenantTableSupport implements TenantSupport {
+
+    @Override
+    public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
+        Collection<String> ignoredTables = helioProperties.getTenant().getIgnoredTables();
+
+        DynamicTableNameInnerInterceptor innerInterceptor = new DynamicTableNameInnerInterceptor();
+        innerInterceptor.setTableNameHandler(new HelioTableTenantHandler(ignoredTables));
+
+        // 添加表级租户内联拦截器
+        interceptor.addInnerInterceptor(innerInterceptor);
+
+        log.info("\n\n[多租户支持] >> 隔离级别: 表级,以下数据表不参与租户隔离: {}\n",
+                ignoredTables);
+    }
+
+    /**
+     * 表级租户mybatis-plus拦截器实现类
+     */
+    @AllArgsConstructor
+    public static class HelioTableTenantHandler implements TableNameHandler {
+
+        /**
+         * 忽略租户隔离的表
+         */
+        private Collection<String> ignoredTables;
+
+        @Override
+        public String dynamicTableName(String sql, String tableName) {
+            if (ignoredTables.contains(tableName)) {
+                return tableName;
+            }
+
+            // 拼接新表名
+            return String.format("%s_%s", tableName, TenantContextHolder.getTenantId());
+        }
+
+    }
+}

+ 53 - 53
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/tenantdatasource/GlobalTenantDataSourceInterceptor.java

@@ -1,53 +1,53 @@
-package cc.uncarbon.framework.tenant.tenantdatasource;
-
-import cc.uncarbon.framework.core.constant.HelioConstant.CRUD;
-import cc.uncarbon.framework.core.context.TenantContextHolder;
-import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegistry;
-import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
-import java.util.Objects;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.aopalliance.intercept.MethodInterceptor;
-import org.aopalliance.intercept.MethodInvocation;
-
-/**
- * 数据源级多租户全局 AOP 处理过程
- *
- * @author chill
- * @author Uncarbon
- */
-@RequiredArgsConstructor
-@Slf4j
-public class GlobalTenantDataSourceInterceptor implements MethodInterceptor {
-
-    private final HelioDynamicDataSourceRegistry dataSourceRegistry;
-
-
-    @Override
-    public Object invoke(@NonNull MethodInvocation invocation) throws Throwable {
-        Long currentTenantId = TenantContextHolder.getTenantId();
-        if (Objects.isNull(currentTenantId)) {
-            // 没有租户信息,直接跳过
-            return invocation.proceed();
-        }
-
-        // 是否有切换过数据源(入栈)
-        boolean pushedFlag = false;
-        try {
-            // 不适合纯数字作为数据源名称,给他拼个前缀
-            String tenantDataSourceName = CRUD.COLUMN_TENANT_ID + currentTenantId;
-            if (dataSourceRegistry.containsDataSource(tenantDataSourceName, true)) {
-                log.debug("[多租户][数据源级] 使用租户数据源 >> {}", tenantDataSourceName);
-                DynamicDataSourceContextHolder.push(tenantDataSourceName);
-                pushedFlag = true;
-            }
-            return invocation.proceed();
-        } finally {
-            if (pushedFlag) {
-                // 数据源出栈
-                DynamicDataSourceContextHolder.poll();
-            }
-        }
-    }
-}
+package cc.uncarbon.framework.tenant.tenantdatasource;
+
+import cc.uncarbon.framework.core.constant.HelioConstant.CRUD;
+import cc.uncarbon.framework.core.context.TenantContextHolder;
+import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegistry;
+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import java.util.Objects;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * 数据源级多租户全局 AOP 处理过程
+ *
+ * @author chill
+ * @author Uncarbon
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class GlobalTenantDataSourceInterceptor implements MethodInterceptor {
+
+    private final HelioDynamicDataSourceRegistry dataSourceRegistry;
+
+
+    @Override
+    public Object invoke(@NonNull MethodInvocation invocation) throws Throwable {
+        Long currentTenantId = TenantContextHolder.getTenantId();
+        if (Objects.isNull(currentTenantId)) {
+            // 没有租户信息,直接跳过
+            return invocation.proceed();
+        }
+
+        // 是否有切换过数据源(入栈)
+        boolean pushedFlag = false;
+        try {
+            // 不适合纯数字作为数据源名称,给他拼个前缀
+            String tenantDataSourceName = CRUD.COLUMN_TENANT_ID + currentTenantId;
+            if (dataSourceRegistry.containsDataSource(tenantDataSourceName, true)) {
+                log.debug("[多租户][数据源级] 使用租户数据源 >> {}", tenantDataSourceName);
+                DynamicDataSourceContextHolder.push(tenantDataSourceName);
+                pushedFlag = true;
+            }
+            return invocation.proceed();
+        } finally {
+            if (pushedFlag) {
+                // 数据源出栈
+                DynamicDataSourceContextHolder.poll();
+            }
+        }
+    }
+}

+ 1 - 1
helio-starter-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +1 @@
-cc.uncarbon.framework.tenant.config.HelioTenantAutoConfiguration
+cc.uncarbon.framework.tenant.config.HelioTenantAutoConfiguration

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

@@ -1,28 +1,28 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-test</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-test</artifactId>
-        </dependency>
-    </dependencies>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-test</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 9 - 9
helio-starter-test/src/main/java/cc/uncarbon/framework/test/stub/HelioStarterTestStub.java

@@ -1,9 +1,9 @@
-package cc.uncarbon.framework.test.stub;
-
-/**
- * 需要一个空类以解决:Missing: no javadoc jar found in folder
- */
-public final class HelioStarterTestStub {
-    private HelioStarterTestStub() {
-    }
-}
+package cc.uncarbon.framework.test.stub;
+
+/**
+ * 需要一个空类以解决:Missing: no javadoc jar found in folder
+ */
+public final class HelioStarterTestStub {
+    private HelioStarterTestStub() {
+    }
+}

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

@@ -1,61 +1,61 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-
-    <parent>
-        <artifactId>helio-starters</artifactId>
-        <groupId>cc.uncarbon.framework</groupId>
-        <version>2.1.0</version><!-- BATCH REPLACEMENT MARK -->
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>helio-starter-web</artifactId>
-
-    <properties>
-
-    </properties>
-
-    <dependencies>
-        <!-- 脚手架starter -->
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-core</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-starter-aop</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>cc.uncarbon.framework</groupId>
-            <artifactId>helio-starter-i18n</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-        </dependency>
-
-        <!-- 这里写其他第三方依赖 -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-undertow</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>cn.dev33</groupId>
-            <artifactId>sa-token-core</artifactId>
-            <version>${sa-token.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-validation</artifactId>
-        </dependency>
-    </dependencies>
-
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+
+    <parent>
+        <artifactId>helio-starters</artifactId>
+        <groupId>cc.uncarbon.framework</groupId>
+        <version>2.2.0</version><!-- BATCH REPLACEMENT MARK -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>helio-starter-web</artifactId>
+
+    <properties>
+
+    </properties>
+
+    <dependencies>
+        <!-- 脚手架starter -->
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-starter-aop</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.uncarbon.framework</groupId>
+            <artifactId>helio-starter-i18n</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- 这里写其他第三方依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-core</artifactId>
+            <version>${sa-token.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 121 - 123
helio-starter-web/src/main/java/cc/uncarbon/framework/web/aspect/WebLoggingAspect.java

@@ -1,123 +1,121 @@
-package cc.uncarbon.framework.web.aspect;
-
-import jakarta.servlet.http.HttpServletRequest;
-import lombok.extern.slf4j.Slf4j;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-
-/**
- * Web 请求及响应日志切面
- *
- * @author Uncarbon
- */
-@ConditionalOnExpression("${helio.web.logging.enabled:false}")
-@Aspect
-@AutoConfiguration
-@Slf4j
-public class WebLoggingAspect {
-
-    /**
-     * Pointcut that matches all repositories, services and Web REST endpoints.
-     */
-    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
-    public void restControllerPointcut() {
-        // AOP Pointcut
-    }
-
-    /**
-     * Pointcut that matches all Spring beans in the application's main packages.
-     */
-    @Pointcut("within(cc.uncarbon.framework..*) || within(cc.uncarbon.module..*)")
-    public void applicationPackagePointcut() {
-        // AOP Pointcut
-    }
-
-    /**
-     * 日志记录请求体及响应体
-     * 切面一定程度上来说会有性能损失,影响 QPS,不建议在生产环境使用本切面类
-     *
-     * @param point 织入点,由切面引擎自动注入
-     * @return 织入点最终执行结果
-     * @throws Throwable 被抛出的异常
-     */
-    @Around("restControllerPointcut() && applicationPackagePointcut()")
-    public Object restControllerAround(ProceedingJoinPoint point) throws Throwable {
-        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-        assert requestAttributes != null;
-        HttpServletRequest request = requestAttributes.getRequest();
-
-        recordRequest(point, request);
-
-        // 记录响应体
-        StringBuilder repLog = new StringBuilder(200);
-        List<Object> repLogParameters = new ArrayList<>();
-
-        repLog.append("\n\n================  Response Start  ================\n");
-
-        long beginAt = System.nanoTime();
-        try {
-            Object result = point.proceed();
-            repLog.append("result: {} \n");
-            repLogParameters.add(result);
-            return result;
-        } catch (IllegalArgumentException e) {
-            log.error("Illegal argument: {} in {}.{}()", Arrays.toString(point.getArgs()),
-                    point.getSignature().getDeclaringTypeName(), point.getSignature().getName());
-            throw e;
-        } finally {
-            if (log.isDebugEnabled()) {
-                long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - beginAt);
-                repLog.append("{}: {} (elapsed {} ms)\n");
-                repLogParameters.add(request.getMethod());
-                repLogParameters.add(request.getRequestURI());
-                repLogParameters.add(duration);
-                repLog.append("================   Response End   ================\n");
-                log.debug(repLog.toString(), repLogParameters.toArray());
-            }
-        }
-    }
-
-    /*
-    ----------------------------------------------------------------
-                        私有方法 private methods
-    ----------------------------------------------------------------
-     */
-
-    /**
-     * 记录请求体
-     */
-    private void recordRequest(ProceedingJoinPoint point, HttpServletRequest request) {
-        if (log.isDebugEnabled()) {
-            StringBuilder reqLog = new StringBuilder(500);
-            List<Object> reqLogParameters = new ArrayList<>();
-
-            reqLog.append("\n\n================  Request Start  ================\n");
-
-            // 打印路由  e.g. POST: /api/v1/doSth
-            reqLog.append("{}: {} \n");
-            reqLogParameters.add(request.getMethod());
-            reqLogParameters.add(request.getRequestURI());
-
-            // 打印入参
-            reqLog.append("Parameters: {} \n");
-            reqLogParameters.add(Arrays.stream(point.getArgs()).map(Object::toString).collect(Collectors.joining("\n")));
-
-            reqLog.append("================   Request End   ================\n");
-
-            log.debug(reqLog.toString(), reqLogParameters.toArray());
-        }
-    }
-}
+package cc.uncarbon.framework.web.aspect;
+
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+
+/**
+ * Web 请求及响应日志切面
+ *
+ * @author Uncarbon
+ */
+@ConditionalOnExpression("${helio.web.logging.enabled:false}")
+@Aspect
+@AutoConfiguration
+@Slf4j
+public class WebLoggingAspect {
+
+    /**
+     * Pointcut that matches all repositories, services and Web REST endpoints.
+     */
+    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
+    public void restControllerPointcut() {
+    }
+
+    /**
+     * Pointcut that matches all Spring beans in the application's main packages.
+     */
+    @Pointcut("within(cc.uncarbon.framework..*) || within(cc.uncarbon.module..*)")
+    public void applicationPackagePointcut() {
+    }
+
+    /**
+     * 日志记录请求体及响应体
+     * 切面一定程度上来说会有性能损失,影响 QPS,不建议在生产环境使用本切面类
+     *
+     * @param point 织入点,由切面引擎自动注入
+     * @return 织入点最终执行结果
+     * @throws Throwable 被抛出的异常
+     */
+    @Around("restControllerPointcut() && applicationPackagePointcut()")
+    public Object restControllerAround(ProceedingJoinPoint point) throws Throwable {
+        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        assert requestAttributes != null;
+        HttpServletRequest request = requestAttributes.getRequest();
+
+        recordRequest(point, request);
+
+        // 记录响应体
+        StringBuilder repLog = new StringBuilder(200);
+        List<Object> repLogParameters = new ArrayList<>();
+
+        repLog.append("\n\n================  Response Start  ================\n");
+
+        long beginAt = System.nanoTime();
+        try {
+            Object result = point.proceed();
+            repLog.append("result: {} \n");
+            repLogParameters.add(result);
+            return result;
+        } catch (IllegalArgumentException e) {
+            log.error("Illegal argument: {} in {}.{}()", Arrays.toString(point.getArgs()),
+                    point.getSignature().getDeclaringTypeName(), point.getSignature().getName());
+            throw e;
+        } finally {
+            if (log.isDebugEnabled()) {
+                long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - beginAt);
+                repLog.append("{}: {} (elapsed {} ms)\n");
+                repLogParameters.add(request.getMethod());
+                repLogParameters.add(request.getRequestURI());
+                repLogParameters.add(duration);
+                repLog.append("================   Response End   ================\n");
+                log.debug(repLog.toString(), repLogParameters.toArray());
+            }
+        }
+    }
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 记录请求体
+     */
+    private void recordRequest(ProceedingJoinPoint point, HttpServletRequest request) {
+        if (log.isDebugEnabled()) {
+            StringBuilder reqLog = new StringBuilder(500);
+            List<Object> reqLogParameters = new ArrayList<>();
+
+            reqLog.append("\n\n================  Request Start  ================\n");
+
+            // 打印路由  e.g. POST: /api/v1/doSth
+            reqLog.append("{}: {} \n");
+            reqLogParameters.add(request.getMethod());
+            reqLogParameters.add(request.getRequestURI());
+
+            // 打印入参
+            reqLog.append("Parameters: {} \n");
+            reqLogParameters.add(Arrays.stream(point.getArgs()).map(Object::toString).collect(Collectors.joining("\n")));
+
+            reqLog.append("================   Request End   ================\n");
+
+            log.debug(reqLog.toString(), reqLogParameters.toArray());
+        }
+    }
+}

+ 7 - 3
helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/GlobalWebExceptionHandlerAutoConfiguration.java

@@ -12,6 +12,7 @@ import cn.dev33.satoken.exception.NotLoginException;
 import cn.dev33.satoken.exception.NotPermissionException;
 import cn.dev33.satoken.exception.NotRoleException;
 import com.fasterxml.jackson.core.JsonParseException;
+import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -23,6 +24,7 @@ import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.validation.BindException;
+import org.springframework.web.HttpMediaTypeNotSupportedException;
 import org.springframework.web.HttpRequestMethodNotSupportedException;
 import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -179,11 +181,13 @@ public class GlobalWebExceptionHandlerAutoConfiguration {
     }
 
     /**
-     * 请求方式不对, 比如POST接口用了GET请求
+     * 请求方式不对
+     * HttpRequestMethodNotSupportedException 如:POST接口用了GET请求
+     * HttpMediaTypeNotSupportedException 如:Content-type应为application/json的,使用了text/plain
      */
     @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
-    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
-    public ResponseEntity<ApiResult<Void>> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
+    @ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class})
+    public ResponseEntity<ApiResult<Void>> handleServletException(ServletException e, HttpServletRequest request) {
         this.logError(e, request);
 
         GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__METHOD_NOT_ALLOWED;

+ 33 - 35
helio-starter-web/src/main/java/cc/uncarbon/framework/web/enums/GlobalWebExceptionI18nMessageEnum.java

@@ -1,35 +1,33 @@
-package cc.uncarbon.framework.web.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * Web 全局异常处理国际化消息枚举
- * 枚举值name 同时视为 i18nCode
- *
- * @author Uncarbon
- */
-@AllArgsConstructor
-@Getter
-public enum GlobalWebExceptionI18nMessageEnum {
-
-    GLOBAL__NO_LOGIN("请您先登录"),
-    GLOBAL__PERMISSION_NOT_MATCH("您权限不足"),
-    GLOBAL__ROLE_NOT_MATCH("您与要求角色不符"),
-    GLOBAL__NOT_FOUND("你迷路啦"),
-    GLOBAL__UNACCEPTABLE_PARAMETERS("错误参数格式或值"),
-    GLOBAL__METHOD_NOT_ALLOWED("错误的请求方式"),
-
-    /**
-     * 一般为最后兜底使用
-     */
-    GLOBAL__INTERNAL_ERROR("请稍后再试"),
-
-    ;
-
-    private final String defaultValue;
-
-    public String i18nCode() {
-        return name();
-    }
-}
+package cc.uncarbon.framework.web.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Web 全局异常处理国际化消息枚举
+ * 枚举值name 同时视为 i18nCode
+ *
+ * @author Uncarbon
+ */
+@AllArgsConstructor
+@Getter
+public enum GlobalWebExceptionI18nMessageEnum {
+
+    GLOBAL__NO_LOGIN("请您先登录"),
+    GLOBAL__PERMISSION_NOT_MATCH("您权限不足"),
+    GLOBAL__ROLE_NOT_MATCH("您与要求角色不符"),
+    GLOBAL__NOT_FOUND("你迷路啦"),
+    GLOBAL__UNACCEPTABLE_PARAMETERS("错误参数格式或值"),
+    GLOBAL__METHOD_NOT_ALLOWED("错误的请求方式"),
+
+    /**
+     * 一般为最后兜底使用
+     */
+    GLOBAL__INTERNAL_ERROR("请稍后再试"),;
+
+    private final String defaultValue;
+
+    public String i18nCode() {
+        return name();
+    }
+}

+ 7 - 3
helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/EnumModule.java

@@ -4,6 +4,7 @@ import cc.uncarbon.framework.core.enums.HelioBaseEnum;
 import cn.hutool.core.text.CharSequenceUtil;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonStreamContext;
 import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
@@ -78,9 +79,12 @@ public class EnumModule extends SimpleModule {
         public void serialize(HelioBaseEnum data, JsonGenerator jsonGenerator, SerializerProvider provider)
                 throws IOException {
             jsonGenerator.writeObject(data.getValue());
-            jsonGenerator.writeFieldName(jsonGenerator.getOutputContext().getCurrentName() + "Label");
-            jsonGenerator.writeString(data.getLabel());
+            JsonStreamContext outputContext = jsonGenerator.getOutputContext();
+            if (!outputContext.inArray()) {
+                // 不在数组或集合中,序列化标签
+                jsonGenerator.writeFieldName(outputContext.getCurrentName() + "Label");
+                jsonGenerator.writeString(data.getLabel());
+            }
         }
     }
-
 }

+ 1 - 1
helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/HelioJacksonModule.java

@@ -35,7 +35,7 @@ public class HelioJacksonModule extends SimpleModule {
         /*
         时间相关;时区跟随JVM设置
         */
-        // emmm knife4j-aggression用的hutool 5.4.1版本还没有DatePattern.*FORMATTER常量,为了兼容只能先手动构造了
+        // knife4j-aggression用的hutool 5.4.1版本还没有DatePattern.*FORMATTER常量,为了兼容只能先手动构造了
         DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(HelioConstant.Jackson.DATE_TIME_FORMAT);
         DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(HelioConstant.Jackson.DATE_FORMAT);
         DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(HelioConstant.Jackson.TIME_FORMAT);

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott