Bläddra i källkod

bump: 发布1.11.0

details see: https://helio.uncarbon.cc/#/i18n/zh-CN/appendix/change-log
Uncarbon 2 år sedan
förälder
incheckning
14bbb8da27
77 ändrade filer med 712 tillägg och 542 borttagningar
  1. 6 6
      README.md
  2. 185 0
      checkstyle-v1.xml
  3. 1 1
      helio-core/pom.xml
  4. 6 6
      helio-core/src/main/java/cc/uncarbon/framework/core/config/AdaptHistoryVersionAutoConfiguration.java
  5. 22 0
      helio-core/src/main/java/cc/uncarbon/framework/core/config/StaticMethodAutoConfiguration.java
  6. 19 11
      helio-core/src/main/java/cc/uncarbon/framework/core/constant/HelioConstant.java
  7. 1 1
      helio-core/src/main/java/cc/uncarbon/framework/core/context/TenantContextHolder.java
  8. 1 1
      helio-core/src/main/java/cc/uncarbon/framework/core/context/UserContextHolder.java
  9. 88 40
      helio-core/src/main/java/cc/uncarbon/framework/core/enums/HelioBaseEnum.java
  10. 6 8
      helio-core/src/main/java/cc/uncarbon/framework/core/exception/BusinessException.java
  11. 2 3
      helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitStrategyException.java
  12. 2 2
      helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitedException.java
  13. 1 1
      helio-core/src/main/java/cc/uncarbon/framework/core/function/StreamFunction.java
  14. 1 0
      helio-core/src/main/java/cc/uncarbon/framework/core/page/PageResult.java
  15. 19 1
      helio-core/src/main/java/cc/uncarbon/framework/core/props/HelioProperties.java
  16. 1 0
      helio-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  17. 1 1
      helio-starter-aop/pom.xml
  18. 1 1
      helio-starter-aop/src/main/java/cc/uncarbon/aop/stub/HelioStarterAopStub.java
  19. 1 1
      helio-starter-cloud/pom.xml
  20. 1 1
      helio-starter-crud/pom.xml
  21. 5 2
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/config/DynamicDataSourceAutoConfiguration.java
  22. 5 1
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/DataSourceDefinition.java
  23. 3 0
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/DataSourceDefinitionProvider.java
  24. 3 3
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/HelioDynamicDataSourceRegistry.java
  25. 1 1
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/entity/HelioBaseEntity.java
  26. 3 2
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/entity/HelioNoTenantBaseEntity.java
  27. 9 7
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/handler/MybatisPlusAutoFillColumnHandler.java
  28. 10 11
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/mapper/HelioBaseMapper.java
  29. 1 1
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/service/HelioBaseService.java
  30. 2 2
      helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/service/impl/HelioBaseServiceImpl.java
  31. 1 1
      helio-starter-dubbo/pom.xml
  32. 55 51
      helio-starter-dubbo/src/main/java/cc/uncarbon/framework/dubbo/filter/HelioDubboExceptionFilter.java
  33. 1 1
      helio-starter-i18n/pom.xml
  34. 35 7
      helio-starter-i18n/src/main/java/cc/uncarbon/framework/i18n/util/I18nUtil.java
  35. 1 1
      helio-starter-knife4j/pom.xml
  36. 2 3
      helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/config/HelioKnife4jAutoConfiguration.java
  37. 1 1
      helio-starter-rate-limit-redis/pom.xml
  38. 2 1
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/constant/RateLimitConstant.java
  39. 1 1
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/RateLimitStrategy.java
  40. 5 2
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByClientIPStrategy.java
  41. 3 1
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/RateLimitByUserIdStrategy.java
  42. 18 15
      helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/stratrgy/impl/SimpleRedisBasedRateLimitStrategy.java
  43. 1 1
      helio-starter-redis/pom.xml
  44. 0 1
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/config/HelioRedisAutoConfiguration.java
  45. 0 2
      helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/lock/impl/RedisDistributedLockImpl.java
  46. 1 1
      helio-starter-rocketmq-aliyun/pom.xml
  47. 0 1
      helio-starter-rocketmq-aliyun/src/main/java/cc/uncarbon/framework/rocketmq/core/utils/InterceptRocket.java
  48. 1 1
      helio-starter-satoken/pom.xml
  49. 1 1
      helio-starter-satoken/src/main/java/cc/uncarbon/framework/satoken/stub/HelioStarterSaTokenStub.java
  50. 1 1
      helio-starter-seata/pom.xml
  51. 1 1
      helio-starter-seata/src/main/java/cc/uncarbon/framework/seata/stub/HelioStarterSeataStub.java
  52. 1 1
      helio-starter-tenant/pom.xml
  53. 1 1
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/config/HelioTenantAutoConfiguration.java
  54. 2 2
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantDataSourceSupport.java
  55. 8 7
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantLineSupport.java
  56. 5 1
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/support/TenantTableSupport.java
  57. 0 3
      helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/tenantdatasource/GlobalTenantDataSourceAdvisor.java
  58. 1 1
      helio-starter-test/pom.xml
  59. 1 1
      helio-starter-test/src/main/java/cc/uncarbon/framework/test/stub/HelioStarterTestStub.java
  60. 1 1
      helio-starter-web/pom.xml
  61. 43 35
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/aspect/WebLoggingAspect.java
  62. 6 5
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/JacksonExtendAutoConfiguration.java
  63. 4 7
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/MessageConverterAutoConfiguration.java
  64. 7 3
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/XssAutoConfiguration.java
  65. 0 1
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/enums/GlobalWebExceptionI18nMessageEnum.java
  66. 2 2
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/EnumConverterFactory.java
  67. 14 13
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/EnumModule.java
  68. 14 9
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/HelioJacksonModule.java
  69. 0 193
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/LocalDateTimeDeserializer.java
  70. 7 8
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/listener/LaunchEventListener.java
  71. 4 2
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/model/response/ApiResult.java
  72. 9 9
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/util/InvalidFieldUtil.java
  73. 2 4
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/HTMLFilter.java
  74. 1 1
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/SQLFilter.java
  75. 36 3
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/XssFilter.java
  76. 2 2
      helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/XssHttpServletRequestWrapper.java
  77. 3 17
      pom.xml

+ 6 - 6
README.md

@@ -4,14 +4,14 @@
 
 `helio-starters`是Helio脚手架及其业务微服务模块的基础支撑构件,可以快速引入其他中间件和基础配置
 
-JDK compatibility: 1.8 - 17
+JDK compatibility: 1.8 - 21
 
 [官方文档](https://helio.uncarbon.cc/)
 [主要依赖](https://helio.uncarbon.cc/#/i18n/zh-CN/helio-starters/dependencies)
 
 ## Helio系列脚手架
-
-| 项目名 | 简介 | Gitee | GitHub |
-| ---- | ---- | ---- | ---- |
-| helio-boot | 单体项目脚手架 | [Gitee](https://gitee.com/uncarbon97/helio-boot) | [GitHub](https://github.com/uncarbon97/helio-boot)
-| helio-cloud | 微服务项目脚手架 | [Gitee](https://gitee.com/uncarbon97/helio-cloud) | [GitHub](https://github.com/uncarbon97/helio-cloud)
+| 项目名                | 简介                                                                | Gitee                                                    | GitHub                                                     |
+|--------------------|-------------------------------------------------------------------|----------------------------------------------------------|------------------------------------------------------------|
+| helio-boot         | 单Maven模块,定位为快速开发脚手架,适合初学者学习 JavaWeb 开发的良好实践                       | [Gitee](https://gitee.com/uncarbon97/helio-boot)         | [GitHub](https://github.com/uncarbon97/helio-boot)         |
+| helio-boot-modular | 按职责拆分为多Maven模块,依然是大单体,但命名及用途对标`helio-cloud`;适合多人协作、但不打算使用微服务架构的团队 | [Gitee](https://gitee.com/uncarbon97/helio-boot-modular) | [GitHub](https://github.com/uncarbon97/helio-boot-modular) |
+| helio-cloud        | 微服务项目脚手架                                                          | [Gitee](https://gitee.com/uncarbon97/helio-cloud)        | [GitHub](https://github.com/uncarbon97/helio-cloud)        |

+ 185 - 0
checkstyle-v1.xml

@@ -0,0 +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版本-->

+ 1 - 1
helio-core/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

+ 6 - 6
helio-core/src/main/java/cc/uncarbon/framework/core/config/AdaptHistoryVersionAutoConfiguration.java

@@ -30,12 +30,12 @@ public class AdaptHistoryVersionAutoConfiguration implements EnvironmentAware {
     /**
      * 旧的配置keys
      */
-    private static final String[] oldPropertyPrefixes = {"helio.crud.tenant."};
+    private static final String[] OLD_PROPERTY_PREFIXES = {"helio.crud.tenant."};
 
     /**
      * 新的配置keys
      */
-    private static final String[] newPropertyPrefixes = {"helio.tenant."};
+    private static final String[] NEW_PROPERTY_PREFIXES = {"helio.tenant."};
 
     /**
      * 过时属性警告提示文案
@@ -80,15 +80,15 @@ public class AdaptHistoryVersionAutoConfiguration implements EnvironmentAware {
 
         for (Map.Entry<String, Object> entry : bootProp.entrySet()) {
             String key = entry.getKey();
-            for (int i = 0; i < oldPropertyPrefixes.length; i++) {
-                if (key.startsWith(oldPropertyPrefixes[i])) {
-                    String newKey = key.replace(oldPropertyPrefixes[i], newPropertyPrefixes[i]);
+            for (int i = 0; i < OLD_PROPERTY_PREFIXES.length; i++) {
+                if (key.startsWith(OLD_PROPERTY_PREFIXES[i])) {
+                    String newKey = key.replace(OLD_PROPERTY_PREFIXES[i], NEW_PROPERTY_PREFIXES[i]);
                     newMap.put(newKey, bootProp.get(key));
 
                     // 前缀相同的话,提示信息就保持一条(利用 Set 特性)
                     deprecatedPropertyWarnings.add(
                             CharSequenceUtil.format("配置文件属性前缀 {}* 已过时、不向下兼容,请转移到 {}* 下",
-                                    oldPropertyPrefixes[i], newPropertyPrefixes[i])
+                                    OLD_PROPERTY_PREFIXES[i], NEW_PROPERTY_PREFIXES[i])
                     );
                 }
             }

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

@@ -0,0 +1,22 @@
+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);
+    }
+}

+ 19 - 11
helio-core/src/main/java/cc/uncarbon/framework/core/constant/HelioConstant.java

@@ -1,17 +1,18 @@
 package cc.uncarbon.framework.core.constant;
 
 import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.lang.RegexPool;
 
 /**
  * Helio 基础常量定义
  *
  * @author Uncarbon
  */
-public class HelioConstant {
+public final class HelioConstant {
     private HelioConstant() {
     }
 
-    public static class Dubbo {
+    public static final class Dubbo {
         private Dubbo() {
         }
 
@@ -21,7 +22,7 @@ public class HelioConstant {
         public static final int RPC_EXCEPTION_RESPONSE_CODE = 1;
     }
 
-    public static class Message {
+    public static final class Message {
         private Message() {
         }
 
@@ -29,7 +30,7 @@ public class HelioConstant {
         public static final String SUCCESS = "操作成功";
     }
 
-    public static class Version {
+    public static final class Version {
         private Version() {
         }
 
@@ -44,7 +45,7 @@ public class HelioConstant {
         public static final String DUBBO_VERSION_V1 = "1.0.0";
     }
 
-    public static class Jackson {
+    public static final class Jackson {
         private Jackson() {
         }
 
@@ -53,7 +54,7 @@ public class HelioConstant {
         public static final String DATE_TIME_FORMAT = DatePattern.NORM_DATETIME_PATTERN;
     }
 
-    public static class CRUD {
+    public static final class CRUD {
         private CRUD() {
         }
 
@@ -94,15 +95,22 @@ public class HelioConstant {
 
     }
 
-    public static class Regex {
+    public static final class Regex {
         private Regex() {
         }
 
-        public static final String CHINA_MAINLAND_PHONE_NO = "^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}$";
-        public static final String EMAIL = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
+        /**
+         * 中国大陆手机号
+         */
+        public static final String CHINA_MAINLAND_PHONE_NO = RegexPool.MOBILE;
+
+        /**
+         * 电子邮箱
+         */
+        public static final String EMAIL = RegexPool.EMAIL;
     }
 
-    public static class Permission {
+    public static final class Permission {
         private Permission() {
         }
 
@@ -112,7 +120,7 @@ public class HelioConstant {
         public static final String DELETE = "delete";
     }
 
-    public static class Tenant {
+    public static final class Tenant {
         private Tenant() {
         }
 

+ 1 - 1
helio-core/src/main/java/cc/uncarbon/framework/core/context/TenantContextHolder.java

@@ -13,7 +13,7 @@ import java.util.Optional;
 @UtilityClass
 public class TenantContextHolder {
 
-    private final TransmittableThreadLocal<TenantContext> THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>();
+    private static final TransmittableThreadLocal<TenantContext> THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>();
 
 
     /**

+ 1 - 1
helio-core/src/main/java/cc/uncarbon/framework/core/context/UserContextHolder.java

@@ -13,7 +13,7 @@ import java.util.Optional;
 @UtilityClass
 public class UserContextHolder {
 
-    private final TransmittableThreadLocal<UserContext> THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>();
+    private static final TransmittableThreadLocal<UserContext> THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>();
 
 
     /**

+ 88 - 40
helio-core/src/main/java/cc/uncarbon/framework/core/enums/HelioBaseEnum.java

@@ -2,19 +2,22 @@ package cc.uncarbon.framework.core.enums;
 
 import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.core.exception.HelioFrameworkException;
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.IterUtil;
 import cn.hutool.core.text.CharSequenceUtil;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.NumberUtil;
-import cn.hutool.core.util.ObjectUtil;
 
 import java.io.Serializable;
-import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Predicate;
 
 /**
  * 基础枚举类
+ * @param <T> 枚举类value字段的类型,建议为整数类型
  *
  * @author Zhu JW
  * @author Uncarbon
@@ -46,7 +49,11 @@ public interface HelioBaseEnum<T> extends Serializable {
      * @see #find(Class, Predicate)
      */
     static <T extends Enum<?> & HelioBaseEnum<?>> Optional<T> findByValue(Class<T> type, Object value) {
-        return find(type, e -> e.getValue() == value || e.getValue().equals(value) || String.valueOf(e.getValue()).equalsIgnoreCase(String.valueOf(value)));
+        return find(type,
+                e -> e.getValue() == value
+                        || e.getValue().equals(value)
+                        || String.valueOf(e.getValue()).equalsIgnoreCase(String.valueOf(value))
+        );
     }
 
     /**
@@ -70,7 +77,10 @@ public interface HelioBaseEnum<T> extends Serializable {
     static <E extends HelioBaseEnum<?>> Optional<E> of(Class<E> type, Object value) {
         if (type.isEnum()) {
             for (E enumConstant : type.getEnumConstants()) {
-                Predicate<E> predicate = e -> e.getValue() == value || e.getValue().equals(value) || String.valueOf(e.getValue()).equalsIgnoreCase(String.valueOf(value));
+                Predicate<E> predicate =
+                        e -> e.getValue() == value
+                                || e.getValue().equals(value)
+                                || String.valueOf(e.getValue()).equalsIgnoreCase(String.valueOf(value));
                 if (predicate.test(enumConstant)) {
                     return Optional.of(enumConstant);
                 }
@@ -99,13 +109,12 @@ public interface HelioBaseEnum<T> extends Serializable {
      * @param v value
      * @return 是否相等
      */
-    @SuppressWarnings("all")
     default boolean eq(Object v) {
         if (v == null) {
             return false;
         }
         if (v instanceof Object[]) {
-            v = Arrays.asList(v);
+            v = Collections.singletonList(v);
         }
         return this == v
                 || getValue() == v
@@ -118,84 +127,123 @@ public interface HelioBaseEnum<T> extends Serializable {
     /**
      * 将不定类型的 value 统一转换为 int
      * 按常见的数据类型依次判断,提高命中率
+     * @deprecated since 1.10.1, replaced with getValueAsInt()
      *
      * @return int value
      */
+    @Deprecated
     default int convertValue2Int() {
-        if (this.getValue() == null) {
+        return getValueAsInt();
+    }
+
+    /**
+     * 将不定类型的 value 统一转换为 int
+     * 按常见的数据类型依次判断,提高命中率
+     *
+     * @return int value
+     */
+    default int getValueAsInt() {
+        T value = getValue();
+        if (value == null) {
             throw new IllegalArgumentException("enum's value CANNOT be null");
         }
-
-        if (this.getValue() instanceof Integer) {
-            return (Integer) this.getValue();
+        if (value instanceof Integer) {
+            return (Integer) value;
         }
-
-        if (this.getValue() instanceof Long) {
-            return ((Long) this.getValue()).intValue();
+        if (value instanceof Long) {
+            return ((Long) value).intValue();
         }
-
-        if (this.getValue() instanceof Number) {
-            return ((Number) this.getValue()).intValue();
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
         }
-
-        if (this.getValue() instanceof String) {
-            return NumberUtil.toBigDecimal((String) this.getValue()).intValue();
+        if (value instanceof String) {
+            return NumberUtil.toBigDecimal((String) value).intValue();
         }
-
         throw new HelioFrameworkException("enum's value CANNOT convert to int");
     }
 
     default String formatLabel(Object... templateParams) {
         if (ArrayUtil.isEmpty(templateParams)) {
-            return this.getLabel();
+            return getLabel();
         }
 
-        return CharSequenceUtil.format(this.getLabel(), templateParams);
+        return CharSequenceUtil.format(getLabel(), templateParams);
     }
 
     /**
      * 断言不为null
-     * 注意:枚举选项的 value 建议为整数类型
      *
      * @param object 需要判断的对象
      * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
      */
     default void assertNotNull(Object object, Object... templateParams) {
-        if (ObjectUtil.isNotNull(object)) {
-            return;
-        }
-
-        throw new BusinessException(this.convertValue2Int(), this.formatLabel(templateParams));
+        assertTrue(Objects.nonNull(object), templateParams);
     }
 
     /**
      * 断言不为空文本
-     * 注意:枚举选项的 value 建议为整数类型
      *
-     * @param cs 需要判断的对象
+     * @param cs 需要判断的文本
      * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
      */
     default void assertNotBlank(CharSequence cs, Object... templateParams) {
-        if (CharSequenceUtil.isNotBlank(cs)) {
-            return;
-        }
-
-        throw new BusinessException(this.convertValue2Int(), this.formatLabel(templateParams));
+        assertTrue(CharSequenceUtil.isNotBlank(cs), templateParams);
     }
 
     /**
      * 断言不为空集合
-     * 注意:枚举选项的 value 建议为整数类型
      *
-     * @param iterable 需要判断的对象
+     * @param iterable 需要判断的集合
      * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
      */
     default void assertNotEmpty(Iterable<?> iterable, Object... templateParams) {
-        if (IterUtil.isNotEmpty(iterable)) {
-            return;
+        assertTrue(IterUtil.isNotEmpty(iterable), templateParams);
+    }
+
+    /**
+     * 断言集合中存在
+     *
+     * @param collection 需要判断的集合
+     * @param item 需要寻找的集合元素
+     * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
+     */
+    default <E> void assertContains(Collection<E> collection, E item, Object... templateParams) {
+        assertTrue(CollUtil.contains(collection, item), templateParams);
+    }
+
+    /**
+     * 断言集合中不存在
+     *
+     * @param collection 需要判断的集合
+     * @param item 需要寻找的集合元素
+     * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
+     */
+    default <E> void assertNotContains(Collection<E> collection, E item, Object... templateParams) {
+        assertTrue(!CollUtil.contains(collection, item), templateParams);
+    }
+
+    /**
+     * 断言为真
+     *
+     * @param expression 需要判断的表达式
+     * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
+     */
+    default void assertTrue(boolean expression, Object... templateParams) {
+        if (!expression) {
+            throw new BusinessException(this, templateParams);
         }
+    }
 
-        throw new BusinessException(this.convertValue2Int(), this.formatLabel(templateParams));
+    /**
+     * 断言为假
+     *
+     * @param expression 需要判断的表达式
+     * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
+     */
+    default void assertFalse(boolean expression, Object... templateParams) {
+        if (expression) {
+            throw new BusinessException(this, templateParams);
+        }
     }
 
 }

+ 6 - 8
helio-core/src/main/java/cc/uncarbon/framework/core/exception/BusinessException.java

@@ -2,17 +2,15 @@ package cc.uncarbon.framework.core.exception;
 
 import cc.uncarbon.framework.core.enums.HelioBaseEnum;
 import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.http.HttpStatus;
 import lombok.Getter;
 import lombok.NonNull;
 
-import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
-
 /**
  * 业务异常类
  *
  * @author Uncarbon
  */
-@SuppressWarnings("squid:S1948")
 @Getter
 public class BusinessException extends RuntimeException {
 
@@ -32,7 +30,7 @@ public class BusinessException extends RuntimeException {
      */
     public BusinessException(String msg) {
         super(msg);
-        this.code = HTTP_INTERNAL_ERROR;
+        this.code = HttpStatus.HTTP_INTERNAL_ERROR;
         this.customEnumField = null;
         this.templateParams = null;
     }
@@ -69,9 +67,9 @@ public class BusinessException extends RuntimeException {
      *
      * @param customEnum 枚举类对象
      */
-    public BusinessException(@NonNull HelioBaseEnum<Integer> customEnum) {
+    public BusinessException(@NonNull HelioBaseEnum<?> customEnum) {
         super(customEnum.getLabel());
-        this.code = customEnum.getValue();
+        this.code = customEnum.getValueAsInt();
 
         /*
         @since 1.7.2 国际化支持
@@ -91,9 +89,9 @@ public class BusinessException extends RuntimeException {
      * @param customEnum      枚举类对象
      * @param templateParams  label 中如果有占位符的话,向里面填充的模板参数
      */
-    public BusinessException(@NonNull HelioBaseEnum<Integer> customEnum, Object... templateParams) {
+    public BusinessException(@NonNull HelioBaseEnum<?> customEnum, Object... templateParams) {
         super(customEnum.formatLabel(templateParams));
-        this.code = customEnum.getValue();
+        this.code = customEnum.getValueAsInt();
 
         /*
         @since 1.7.2 国际化支持

+ 2 - 3
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/exception/RateLimitStrategyException.java → helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitStrategyException.java

@@ -1,9 +1,8 @@
-package cc.uncarbon.framework.ratelimit.exception;
-
-import cc.uncarbon.framework.core.exception.HelioFrameworkException;
+package cc.uncarbon.framework.core.exception;
 
 /**
  * 限流策略异常
+ * 用于helio-starter-rate-limit-redis
  */
 public class RateLimitStrategyException extends HelioFrameworkException {
 

+ 2 - 2
helio-starter-rate-limit-redis/src/main/java/cc/uncarbon/framework/ratelimit/exception/RateLimitedException.java → helio-core/src/main/java/cc/uncarbon/framework/core/exception/RateLimitedException.java

@@ -1,12 +1,12 @@
-package cc.uncarbon.framework.ratelimit.exception;
+package cc.uncarbon.framework.core.exception;
 
 import cc.uncarbon.framework.core.enums.HelioBaseEnum;
-import cc.uncarbon.framework.core.exception.BusinessException;
 import cn.hutool.http.HttpStatus;
 import lombok.NonNull;
 
 /**
  * 已被限流异常
+ * 用于helio-starter-rate-limit-redis
  */
 public class RateLimitedException extends BusinessException {
 

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

@@ -8,7 +8,7 @@ import java.util.function.BinaryOperator;
  *
  * @author Uncarbon
  */
-public class StreamFunction {
+public final class StreamFunction {
 
     private StreamFunction() {
     }

+ 1 - 0
helio-core/src/main/java/cc/uncarbon/framework/core/page/PageResult.java

@@ -12,6 +12,7 @@ import java.util.List;
 
 /**
  * 分页查询结果
+ * @param <T> records字段的泛型类型
  *
  * @author Uncarbon
  */

+ 19 - 1
helio-core/src/main/java/cc/uncarbon/framework/core/props/HelioProperties.java

@@ -32,10 +32,28 @@ public class HelioProperties {
     public static class Security {
 
         /**
-         * 放行路由地址,不进行登录校验
+         * 放行路由地址,不进行登录校验(默认用于app-api)
          */
         private final List<String> excludeRoutes = new ArrayList<>(64);
 
+        private final Xss xss = new Xss();
+
+
+        @Data
+        public static class Xss {
+
+            /**
+             * 是否启用XSS Filter(默认启用)
+             */
+            private boolean enabled = true;
+
+            /**
+             * 不进行XSS过滤的路由,直接放行
+             * 注意单词是被动态噢
+             */
+            private List<String> excludedRoutes = new ArrayList<>();
+
+        }
     }
 
     @Data

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

@@ -1,2 +1,3 @@
 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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

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

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

@@ -5,6 +5,7 @@ import cc.uncarbon.framework.crud.dynamicdatasource.HelioDynamicDataSourceRegist
 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;
 
@@ -13,15 +14,17 @@ import org.springframework.context.annotation.Bean;
  *
  * @author Uncarbon
  */
-@ConditionalOnClass(value = DynamicRoutingDataSource.class)
+@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);
+        return new HelioDynamicDataSourceRegistry(
+                dynamicRoutingDataSource, dataSourceCreator, dataSourceDefinitionProviders);
     }
 }

+ 5 - 1
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/DataSourceDefinition.java

@@ -1,13 +1,17 @@
 package cc.uncarbon.framework.crud.dynamicdatasource;
 
 import io.swagger.annotations.ApiModelProperty;
-import java.io.Serializable;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
 import lombok.experimental.SuperBuilder;
 
+import java.io.Serializable;
+
+/**
+ * 动态数据源定义
+ */
 @Accessors(chain = true)
 @SuperBuilder
 @AllArgsConstructor

+ 3 - 0
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/DataSourceDefinitionProvider.java

@@ -1,5 +1,8 @@
 package cc.uncarbon.framework.crud.dynamicdatasource;
 
+/**
+ * 动态数据源定义提供者
+ */
 public interface DataSourceDefinitionProvider {
 
     /**

+ 3 - 3
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/dynamicdatasource/HelioDynamicDataSourceRegistry.java

@@ -68,8 +68,7 @@ public class HelioDynamicDataSourceRegistry {
                 // 补充字段
                 dataSourceProperty
                         .setPoolName(dataSourceDefinition.getName())
-                        .setType(HikariDataSource.class)
-                ;
+                        .setType(HikariDataSource.class);
 
                 // 创建动态数据源
                 DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
@@ -84,7 +83,8 @@ public class HelioDynamicDataSourceRegistry {
         }
 
         if (!successFlag) {
-            log.error("动态添加新数据源失败,原因:没有 DataSourceDefinitionProvider 提供 {} 的 DataSourceDefinition", dataSourceName);
+            log.error("动态添加新数据源失败,原因:没有 DataSourceDefinitionProvider 提供 {} 的 DataSourceDefinition",
+                    dataSourceName);
         }
 
         return successFlag;

+ 1 - 1
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/entity/HelioBaseEntity.java

@@ -16,7 +16,7 @@ import java.time.LocalDateTime;
 
 /**
  * 基础实体类,默认含[行级租户ID]
- * T = 主键类型,一般用 Long
+ * @param <T> 主键类型,一般用 Long
  *
  * @author Uncarbon
  */

+ 3 - 2
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/entity/HelioNoTenantBaseEntity.java

@@ -2,7 +2,6 @@ package cc.uncarbon.framework.crud.entity;
 
 import cc.uncarbon.framework.core.constant.HelioConstant;
 import com.baomidou.mybatisplus.annotation.TableField;
-import java.io.Serializable;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -10,9 +9,11 @@ import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
 import lombok.experimental.SuperBuilder;
 
+import java.io.Serializable;
+
 /**
  * 基础实体类,去除[行级租户ID]
- * T = 主键类型,一般用 Long
+ * @param <T> 主键类型,一般用 Long
  *
  * @author Uncarbon
  */

+ 9 - 7
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/handler/MybatisPlusAutoFillColumnHandler.java

@@ -1,12 +1,14 @@
 package cc.uncarbon.framework.crud.handler;
 
-import cc.uncarbon.framework.core.constant.HelioConstant;
 import cc.uncarbon.framework.core.context.TenantContextHolder;
 import cc.uncarbon.framework.core.context.UserContextHolder;
 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
-import java.time.LocalDateTime;
 import org.apache.ibatis.reflection.MetaObject;
 
+import java.time.LocalDateTime;
+
+import static cc.uncarbon.framework.core.constant.HelioConstant.CRUD.*;
+
 /**
  * 字段自动填充, 摘自Mybatis-Plus官方例程
  * @author nieqiurong
@@ -16,9 +18,9 @@ public class MybatisPlusAutoFillColumnHandler implements MetaObjectHandler {
 
     @Override
     public void insertFill(MetaObject metaObject) {
-        this.strictInsertFill(metaObject, HelioConstant.CRUD.ENTITY_FIELD_TENANT_ID, Long.class, TenantContextHolder.getTenantId());
-        this.strictInsertFill(metaObject, HelioConstant.CRUD.ENTITY_FIELD_CREATED_AT, LocalDateTime.class, LocalDateTime.now());
-        this.strictInsertFill(metaObject, HelioConstant.CRUD.ENTITY_FIELD_CREATED_BY, String.class, UserContextHolder.getUserName());
+        this.strictInsertFill(metaObject, ENTITY_FIELD_TENANT_ID, Long.class, TenantContextHolder.getTenantId());
+        this.strictInsertFill(metaObject, ENTITY_FIELD_CREATED_AT, LocalDateTime.class, LocalDateTime.now());
+        this.strictInsertFill(metaObject, ENTITY_FIELD_CREATED_BY, String.class, UserContextHolder.getUserName());
 
         // 同时加入更新字段
         this.updateFill(metaObject);
@@ -26,8 +28,8 @@ public class MybatisPlusAutoFillColumnHandler implements MetaObjectHandler {
 
     @Override
     public void updateFill(MetaObject metaObject) {
-        this.strictUpdateFill(metaObject, HelioConstant.CRUD.ENTITY_FIELD_UPDATED_AT, LocalDateTime.class, LocalDateTime.now());
-        this.strictUpdateFill(metaObject, HelioConstant.CRUD.ENTITY_FIELD_UPDATED_BY, String.class, UserContextHolder.getUserName());
+        this.strictUpdateFill(metaObject, ENTITY_FIELD_UPDATED_AT, LocalDateTime.class, LocalDateTime.now());
+        this.strictUpdateFill(metaObject, ENTITY_FIELD_UPDATED_BY, String.class, UserContextHolder.getUserName());
     }
 
 }

+ 10 - 11
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/mapper/HelioBaseMapper.java

@@ -3,7 +3,6 @@ package cc.uncarbon.framework.crud.mapper;
 import cc.uncarbon.framework.core.function.StreamFunction;
 import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
 import cn.hutool.core.collection.CollUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 
 import java.io.Serializable;
@@ -16,30 +15,30 @@ import java.util.stream.Collectors;
 
 /**
  * 持久层基础模板,在Mybatis-Plus BaseMapper上进一步增加功能
- * E = ENTITY 实体类
- * T = 实体类主键ID类型
+ * @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) {
-        if (CollUtil.isEmpty(ids)) {
-            return Collections.emptyList();
-        }
-        return this.selectList(new LambdaQueryWrapper<E>().in(HelioBaseEntity::getId, ids));
+        return selectBatchIds(ids);
     }
 
     /**
      * 取ID👉名 映射map
      */
-    default Map<T, String> getNameMap(Class<E> entityClass, Collection<T> ids,
+    default Map<T, String> getNameMap(Collection<T> ids,
                                       Function<E, T> idMapper, Function<E, String> nameMapper) {
-
-        List<E> entityList = this.selectByIds(ids);
+        List<E> entityList = selectBatchIds(ids);
         if (CollUtil.isEmpty(entityList)) {
             return Collections.emptyMap();
         }
-        return entityList.stream().collect(Collectors.toMap(idMapper, nameMapper, StreamFunction.ignoredThrowingMerger()));
+        return entityList.stream()
+                .collect(Collectors.toMap(idMapper, nameMapper, StreamFunction.ignoredThrowingMerger()));
     }
 }

+ 1 - 1
helio-starter-crud/src/main/java/cc/uncarbon/framework/crud/service/HelioBaseService.java

@@ -5,7 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 
 /**
  * 服务基础模板
- * E = ENTITY 实体类
+ * @param <E> 实体类
  *
  * @author Uncarbon
  */

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

@@ -9,8 +9,8 @@ import lombok.NoArgsConstructor;
 
 /**
  * 服务实现类基础模板
- * M = MAPPER 持久层 interface
- * E = ENTITY 实体类
+ * @param <M> 持久层接口
+ * @param <E> 实体类
  *
  * @author Uncarbon
  */

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

+ 55 - 51
helio-starter-dubbo/src/main/java/cc/uncarbon/framework/dubbo/filter/HelioDubboExceptionFilter.java

@@ -1,6 +1,7 @@
 package cc.uncarbon.framework.dubbo.filter;
 
 import cc.uncarbon.framework.core.exception.BusinessException;
+import cn.hutool.core.text.CharSequenceUtil;
 import cn.hutool.core.util.StrUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.common.utils.ReflectUtils;
@@ -21,57 +22,23 @@ public class HelioDubboExceptionFilter extends ExceptionFilter {
 
     @Override
     public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
-        if (!(
-                appResponse.hasException() && GenericService.class != invoker.getInterface()
-        )) {
+        if (!(appResponse.hasException() && GenericService.class != invoker.getInterface())) {
             return;
         }
 
         try {
             Throwable exception = appResponse.getException();
-
-            // directly throw if it's checked exception
-            if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
+            if (isExceptionCheckedOrAppearsInSign(invoker, invocation, exception)) {
                 return;
             }
 
-            // directly throw if the exception appears in the signature
-            Method method = invoker.getInterface()
-                    .getMethod(invocation.getMethodName(), invocation.getParameterTypes());
-            Class<?>[] exceptionClasses = method.getExceptionTypes();
-            for (Class<?> exceptionClass : exceptionClasses) {
-                if (exception.getClass().equals(exceptionClass)) {
-                    return;
-                }
-            }
-
             // for the exception not found in method's signature, print ERROR message in server's log.
             log.error(
                     "Got unchecked and undeclared exception which called by {}. service: {}, method: {}, exception: {} >> {}",
-                    RpcContext.getServiceContext().getRemoteAddress(),
-                    invoker.getInterface().getName(),
-                    invocation.getMethodName(),
-                    exception.getClass().getName(),
-                    exception.getMessage(),
-                    exception);
+                    RpcContext.getServiceContext().getRemoteAddress(), invoker.getInterface().getName(),
+                    invocation.getMethodName(), exception.getClass().getName(), exception.getMessage(), exception);
 
-            // directly throw if exception class and interface class are in the same jar file.
-            String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
-            String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
-            if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
-                return;
-            }
-            // directly throw if it's JDK exception
-            String className = exception.getClass().getName();
-            if (className.startsWith("java.") || className.startsWith("javax.")) {
-                return;
-            }
-            // directly throw if it's dubbo exception
-            if (exception instanceof RpcException) {
-                return;
-            }
-            // directly throw if it's BusinessException
-            if (exception instanceof BusinessException) {
+            if (canReturnDirectly(invoker, exception)) {
                 return;
             }
 
@@ -81,12 +48,8 @@ public class HelioDubboExceptionFilter extends ExceptionFilter {
             // ignored
         } catch (Throwable cause) {
             log.warn("Fail to ExceptionFilter when called by {}. service: {}, method: {}, exception: {} >> {}",
-                    RpcContext.getServiceContext().getRemoteHost(),
-                    invoker.getInterface().getName(),
-                    invocation.getMethodName(),
-                    cause.getClass().getName(),
-                    cause.getMessage(),
-                    cause);
+                    RpcContext.getServiceContext().getRemoteHost(), invoker.getInterface().getName(),
+                    invocation.getMethodName(), cause.getClass().getName(), cause.getMessage(), cause);
         }
     }
 
@@ -94,12 +57,53 @@ public class HelioDubboExceptionFilter extends ExceptionFilter {
     public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {
         log.error(
                 "Got unchecked and undeclared exception which called by {}. service: {}, method: {}, exception: {} >> {}",
-                RpcContext.getServiceContext().getRemoteHost(),
-                invoker.getInterface().getName(),
-                invocation.getMethodName(),
-                e.getClass().getName(),
-                e.getMessage(),
-                e);
+                RpcContext.getServiceContext().getRemoteHost(), invoker.getInterface().getName(),
+                invocation.getMethodName(), e.getClass().getName(), e.getMessage(), e);
+    }
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    private static boolean isExceptionCheckedOrAppearsInSign(Invoker<?> invoker, Invocation invocation,
+                                                             Throwable exception) throws NoSuchMethodException {
+        // directly throw if it's checked exception
+        if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
+            return true;
+        }
+
+        // directly throw if the exception appears in the signature
+        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+        Class<?>[] exceptionClasses = method.getExceptionTypes();
+        for (Class<?> exceptionClass : exceptionClasses) {
+            if (exception.getClass().equals(exceptionClass)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean canReturnDirectly(Invoker<?> invoker, Throwable exception) {
+        // directly throw if exception class and interface class are in the same jar file.
+        String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
+        String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
+        if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
+            return true;
+        }
+
+        // directly throw if it's JDK exception
+        String className = exception.getClass().getName();
+        if (CharSequenceUtil.startWithAny(className, "java.", "javax.", "jakarta.")) {
+            return true;
+        }
+        // directly throw if it's dubbo exception
+        if (exception instanceof RpcException) {
+            return true;
+        }
+        // directly throw if it's BusinessException
+        return exception instanceof BusinessException;
     }
 
 }

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

@@ -10,6 +10,7 @@ import org.springframework.context.MessageSource;
 import org.springframework.context.NoSuchMessageException;
 
 import java.util.Locale;
+import java.util.function.Supplier;
 
 /**
  * 获取 i18n 资源
@@ -21,9 +22,22 @@ import java.util.Locale;
 @Slf4j
 public class I18nUtil {
 
-    private final MessageSource MESSAGE_SOURCE = SpringUtil.getBean(MessageSource.class);
+    /**
+     * 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
      */
@@ -32,13 +46,27 @@ public class I18nUtil {
     }
 
     /**
-     * 取默认locale
-     * 强国际化SaaS需求,也许可以改造成从特定ThreadLocal或者UserContextHolder中获取
+     * 取默认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 '{}'?
@@ -48,7 +76,7 @@ public class I18nUtil {
      * @return null or 国际化翻译值
      */
     public String messageOf(String code, Object... templateParams) {
-        return messageOf(getDefaultLocale(), code, templateParams);
+        return messageOf(determineLocale(), code, templateParams);
     }
 
     /**
@@ -66,7 +94,7 @@ public class I18nUtil {
         }
 
         try {
-            String msg = getMessageSource().getMessage(code, null, locale != null ? locale : getDefaultLocale());
+            String msg = getMessageSource().getMessage(code, null, locale != null ? locale : determineLocale());
             if (CharSequenceUtil.isEmpty(msg)) {
                 return msg;
             }
@@ -93,7 +121,7 @@ public class I18nUtil {
      * @return null or 国际化翻译值
      */
     public String messageOf(String code, String defaultValue, Object... templateParams) {
-        return messageOf(getDefaultLocale(), code, defaultValue, templateParams);
+        return messageOf(determineLocale(), code, defaultValue, templateParams);
     }
 
     /**
@@ -120,7 +148,7 @@ public class I18nUtil {
      * @return null or 国际化翻译值
      */
     public String messageOf(Enum<?> enumField, Object... templateParams) {
-        return messageOf(getDefaultLocale(), enumField, templateParams);
+        return messageOf(determineLocale(), enumField, templateParams);
     }
 
     /**

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

+ 2 - 3
helio-starter-knife4j/src/main/java/cc/uncarbon/framework/knife4j/config/HelioKnife4jAutoConfiguration.java

@@ -59,11 +59,10 @@ public class HelioKnife4jAutoConfiguration {
                 .build()
                 // 在调试页上附加请求头
                 .securityContexts(Collections.singletonList(this.securityContext()))
-                .securitySchemes(Collections.singletonList(this.apiKeyOfToken()))
-                ;
+                .securitySchemes(Collections.singletonList(this.apiKeyOfToken()));
     }
 
-    private ApiInfo apiInfo(){
+    private ApiInfo apiInfo() {
         return new ApiInfoBuilder()
                 .title(helioProperties.getKnife4j().getTitle())
                 .description(helioProperties.getKnife4j().getDescription())

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

@@ -5,7 +5,8 @@ package cc.uncarbon.framework.ratelimit.constant;
  */
 public final class RateLimitConstant {
 
-    private RateLimitConstant() {}
+    private RateLimitConstant() {
+    }
 
     /**
      * Redis Key前缀

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

@@ -1,7 +1,7 @@
 package cc.uncarbon.framework.ratelimit.stratrgy;
 
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
-import cc.uncarbon.framework.ratelimit.exception.RateLimitedException;
+import cc.uncarbon.framework.core.exception.RateLimitedException;
 import org.aspectj.lang.JoinPoint;
 
 /**

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

@@ -32,7 +32,9 @@ public class RateLimitByClientIPStrategy extends SimpleRedisBasedRateLimitStrate
     @Override
     protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
         final String splitter = StrPool.COLON;
-        return RateLimitConstant.REDIS_KEY_PREFIX + "ip" + splitter + currentClientIP() + splitter + determineMark(annotation, point);
+        // 编译器将自动优化
+        return RateLimitConstant.REDIS_KEY_PREFIX
+                + "ip" + splitter + currentClientIP() + splitter + determineMark(annotation, point);
     }
 
     /**
@@ -46,7 +48,8 @@ public class RateLimitByClientIPStrategy extends SimpleRedisBasedRateLimitStrate
 
         // 尝试从request中得到未登录用户的IP地址
         try {
-            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            ServletRequestAttributes servletRequestAttributes =
+                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
             if (servletRequestAttributes != null) {
                 return IPUtil.getClientIPAddress(servletRequestAttributes.getRequest(), 0);
             }

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

@@ -31,7 +31,9 @@ public class RateLimitByUserIdStrategy extends SimpleRedisBasedRateLimitStrategy
     @Override
     protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
         final String splitter = StrPool.COLON;
-        return RateLimitConstant.REDIS_KEY_PREFIX + "userId" + splitter + currentUserId() + splitter + determineMark(annotation, point);
+        // 编译器将自动优化
+        return RateLimitConstant.REDIS_KEY_PREFIX
+                + "userId" + splitter + currentUserId() + splitter + determineMark(annotation, point);
     }
 
     /**

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

@@ -1,8 +1,8 @@
 package cc.uncarbon.framework.ratelimit.stratrgy.impl;
 
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
-import cc.uncarbon.framework.ratelimit.exception.RateLimitStrategyException;
-import cc.uncarbon.framework.ratelimit.exception.RateLimitedException;
+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;
@@ -28,19 +28,20 @@ public abstract class SimpleRedisBasedRateLimitStrategy {
 
     /**
      * 预置限流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);";
+    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 类实例
@@ -48,7 +49,9 @@ public abstract class SimpleRedisBasedRateLimitStrategy {
     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 {
+    protected void performRateLimitCheck(UseRateLimit annotation, JoinPoint point,
+                                         Supplier<RateLimitedException> rateLimitedExceptionSupplier)
+            throws RateLimitStrategyException {
         long duration = annotation.duration();
         long max = annotation.max();
 

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

+ 0 - 1
helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/config/HelioRedisAutoConfiguration.java

@@ -31,7 +31,6 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
 @AutoConfiguration
 public class HelioRedisAutoConfiguration {
 
-    @SuppressWarnings("squid:S1452")
     @Bean
     @ConditionalOnMissingBean
     public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory factory) {

+ 0 - 2
helio-starter-redis/src/main/java/cc/uncarbon/framework/redis/lock/impl/RedisDistributedLockImpl.java

@@ -29,7 +29,6 @@ public class RedisDistributedLockImpl implements RedisDistributedLock {
         return this.lock(lockName, TimeUnit.SECONDS, holdDuration);
     }
 
-    @SuppressWarnings("squid:S2222")
     @Override
     public RLock lock(String lockName, TimeUnit unit, int holdDuration) {
         RLock lock = this.getRedissonLockByName(lockName);
@@ -43,7 +42,6 @@ public class RedisDistributedLockImpl implements RedisDistributedLock {
         return this.tryLock(lockName, TimeUnit.SECONDS, waitDuration, holdDuration);
     }
 
-    @SuppressWarnings({"squid:S2142", "squid:S2222"})
     @Override
     public boolean tryLock(String lockName, TimeUnit unit, int waitDuration, int holdDuration) {
         RLock lock = this.getRedissonLockByName(lockName);

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

@@ -38,7 +38,6 @@ public class InterceptRocket {
     private InterceptRocket() {
     }
 
-    @SuppressWarnings("squid:S107")
     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);

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

+ 1 - 1
helio-starter-satoken/src/main/java/cc/uncarbon/framework/satoken/stub/HelioStarterSaTokenStub.java

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

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

@@ -3,7 +3,7 @@ package cc.uncarbon.framework.seata.stub;
 /**
  * 需要一个空类以解决:Missing: no javadoc jar found in folder
  */
-public class HelioStarterSeataStub {
+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>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

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

@@ -37,7 +37,7 @@ public class HelioTenantAutoConfiguration {
                 // 数据源级
                 return new TenantDataSourceSupport();
             default:
-                throw new IllegalArgumentException("启用多租户功能后,请正确配置对应的多租户隔离级别 (helio.tenant.isolate-level) ");
+                throw new IllegalArgumentException("启用多租户功能后,请正确配置对应的多租户隔离级别(helio.tenant.isolate-level)");
         }
     }
 

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

@@ -14,7 +14,6 @@ import org.springframework.util.ClassUtils;
 @Slf4j
 public class TenantDataSourceSupport implements TenantSupport {
 
-    @SuppressWarnings("squid:S106")
     @Override
     public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
         log.info("\n\n[多租户支持] >> 隔离级别: 数据源级");
@@ -22,7 +21,8 @@ public class TenantDataSourceSupport implements TenantSupport {
         System.err.println("数据源级多租户支持还处于试验阶段,请经过测试后再投入生产使用!");
 
         try {
-            Class.forName("com.baomidou.dynamic.datasource.DynamicRoutingDataSource", false, ClassUtils.getDefaultClassLoader());
+            Class.forName("com.baomidou.dynamic.datasource.DynamicRoutingDataSource",
+                    false, ClassUtils.getDefaultClassLoader());
         } catch (ClassNotFoundException cnfe) {
             System.err.println("\n\n ERROR: 没有找到 dynamic-datasource-spring-boot-starter 依赖,请检查是否引入");
         }

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

@@ -7,13 +7,14 @@ 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 java.util.Collection;
-import java.util.Objects;
 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;
+
 /**
  * 多租户支持-行级
  *
@@ -22,17 +23,14 @@ import net.sf.jsqlparser.expression.LongValue;
 @Slf4j
 public class TenantLineSupport implements TenantSupport {
 
-    @SuppressWarnings("squid:S106")
     @Override
     public void support(HelioProperties helioProperties, MybatisPlusInterceptor interceptor) {
         Collection<String> ignoredTables = helioProperties.getTenant().getIgnoredTables();
 
         // 添加行级租户内联拦截器
         interceptor.addInnerInterceptor(
-                new TenantLineInnerInterceptor(
-                        new HelioLineTenantHandler(
-                                helioProperties.getTenant().getPrivilegedTenantId(),
-                                ignoredTables)
+                new TenantLineInnerInterceptor(new HelioLineTenantHandler(
+                        helioProperties.getTenant().getPrivilegedTenantId(), ignoredTables)
                 )
         );
 
@@ -42,6 +40,9 @@ public class TenantLineSupport implements TenantSupport {
     }
 
 
+    /**
+     * 行级租户mybatis-plus拦截器实现类
+     */
     @AllArgsConstructor
     public static class HelioLineTenantHandler implements TenantLineHandler {
 

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

@@ -6,10 +6,11 @@ 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 java.util.Collection;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
+import java.util.Collection;
+
 /**
  * 多租户支持-表级
  * @deprecated 后来发现并不是太适合实践使用,仅保留本类
@@ -34,6 +35,9 @@ public class TenantTableSupport implements TenantSupport {
                 ignoredTables);
     }
 
+    /**
+     * 表级租户mybatis-plus拦截器实现类
+     */
     @AllArgsConstructor
     public static class HelioTableTenantHandler implements TableNameHandler {
 

+ 0 - 3
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/tenantdatasource/GlobalTenantDataSourceAdvisor.java

@@ -24,10 +24,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
 @Getter
 public class GlobalTenantDataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
 
-    @SuppressWarnings("squid:S1948")
     private final Advice advice;
-
-    @SuppressWarnings("squid:S1948")
     private final Pointcut pointcut;
 
     public GlobalTenantDataSourceAdvisor(@NonNull GlobalTenantDataSourceInterceptor globalTenantDataSourceInterceptor) {

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

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

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

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>helio-starters</artifactId>
         <groupId>cc.uncarbon.framework</groupId>
-        <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+        <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>

+ 43 - 35
helio-starter-web/src/main/java/cc/uncarbon/framework/web/aspect/WebLoggingAspect.java

@@ -9,7 +9,6 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
 
 import javax.servlet.http.HttpServletRequest;
 import java.util.ArrayList;
@@ -58,32 +57,11 @@ public class WebLoggingAspect {
     public Object restControllerAround(ProceedingJoinPoint point) throws Throwable {
         RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
         assert requestAttributes != null;
-        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
+        HttpServletRequest request = (HttpServletRequest) RequestContextHolder.getRequestAttributes();
 
-        /*
-        请求体记录
-         */
-        StringBuilder reqLog = new StringBuilder(500);
-        List<Object> reqLogParameters = new ArrayList<>();
+        recordRequest(point, request);
 
-        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());
-
-        /*
-        响应体记录
-         */
+        // 记录响应体
         StringBuilder repLog = new StringBuilder(200);
         List<Object> repLogParameters = new ArrayList<>();
 
@@ -94,22 +72,52 @@ public class WebLoggingAspect {
             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 {
-            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());
+            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());
         }
     }
 }

+ 6 - 5
helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/JacksonExtendAutoConfiguration.java

@@ -11,10 +11,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import java.text.SimpleDateFormat;
-import java.time.ZoneId;
-import java.util.Locale;
-import java.util.TimeZone;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -23,6 +19,11 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Primary;
 import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
+import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.util.Locale;
+import java.util.TimeZone;
+
 /**
  * Jackson 自动配置类
  *
@@ -64,7 +65,7 @@ public class JacksonExtendAutoConfiguration {
                 .configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true)
                 // 忽略不能转移的字符
                 .configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true)
-                // 在使用spring boot + jpa/hibernate,如果实体字段上加有FetchType.LAZY,并使用jackson序列化为json串时,会遇到SerializationFeature.FAIL_ON_EMPTY_BEANS异常
+                // 如果实体字段上加有FetchType.LAZY,并使用jackson序列化为json串时,会遇到FAIL_ON_EMPTY_BEANS异常
                 .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
                 // 忽略未知字段
                 .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

+ 4 - 7
helio-starter-web/src/main/java/cc/uncarbon/framework/web/config/MessageConverterAutoConfiguration.java

@@ -1,21 +1,18 @@
 package cc.uncarbon.framework.web.config;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
 import lombok.RequiredArgsConstructor;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
-import org.springframework.http.converter.ByteArrayHttpMessageConverter;
-import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.ResourceHttpMessageConverter;
-import org.springframework.http.converter.ResourceRegionHttpMessageConverter;
-import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.*;
 import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
 /**
  * Jackson 类型转换器自动配置类
  *

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

@@ -1,10 +1,13 @@
 package cc.uncarbon.framework.web.config;
 
+import cc.uncarbon.framework.core.props.HelioProperties;
 import cc.uncarbon.framework.web.xss.XssFilter;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
+import org.springframework.core.Ordered;
 
 import javax.servlet.DispatcherType;
 
@@ -18,13 +21,14 @@ public class XssAutoConfiguration {
 
     @Bean
     @ConditionalOnMissingBean
-    public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
+    @ConditionalOnProperty(value = "helio.security.xss.enabled", havingValue = "true", matchIfMissing = true)
+    public FilterRegistrationBean<XssFilter> xssFilterRegistration(HelioProperties helioProperties) {
         FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
         registration.setDispatcherTypes(DispatcherType.REQUEST);
-        registration.setFilter(new XssFilter());
+        registration.setFilter(new XssFilter(helioProperties.getSecurity().getXss().getExcludedRoutes()));
         registration.addUrlPatterns("/*");
         registration.setName("xssFilter");
-        registration.setOrder(Integer.MAX_VALUE);
+        registration.setOrder(Ordered.LOWEST_PRECEDENCE);
         return registration;
     }
 }

+ 0 - 1
helio-starter-web/src/main/java/cc/uncarbon/framework/web/enums/GlobalWebExceptionI18nMessageEnum.java

@@ -9,7 +9,6 @@ import lombok.Getter;
  *
  * @author Uncarbon
  */
-@SuppressWarnings("squid:S115")
 @AllArgsConstructor
 @Getter
 public enum GlobalWebExceptionI18nMessageEnum {

+ 2 - 2
helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/EnumConverterFactory.java

@@ -14,7 +14,6 @@ import java.util.WeakHashMap;
  *
  * @author Zhu JW
  **/
-@SuppressWarnings({"rawtypes", "unchecked"})
 public class EnumConverterFactory implements ConverterFactory<String, HelioBaseEnum> {
     private final Map<Class, Converter> converterCache = new WeakHashMap<>();
 
@@ -35,7 +34,8 @@ public class EnumConverterFactory implements ConverterFactory<String, HelioBaseE
 
         @Override
         public T convert(@NonNull Object value) {
-            return HelioBaseEnum.of(this.enumType, value).orElseThrow(() -> new BusinessException("Contains illegal enumeration value"));
+            return HelioBaseEnum.of(this.enumType, value)
+                    .orElseThrow(() -> new BusinessException("Contains illegal enumeration value"));
         }
     }
 }

+ 14 - 13
helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/EnumModule.java

@@ -30,21 +30,20 @@ public class EnumModule extends SimpleModule {
         this.addSerializer(new EnumSerializer());
     }
 
-    private static class CustomDeserializers extends SimpleDeserializers {
+    private static final class CustomDeserializers extends SimpleDeserializers {
         private CustomDeserializers() {
-
         }
 
         @Override
-        @SuppressWarnings({"rawtypes", "unchecked"})
-        public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException {
+        public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config,
+                                                        BeanDescription beanDesc) throws JsonMappingException {
             // HelioBaseEnum<?>,调用此序列化方法,否则使用 jackson 默认的序列化方法
-            return HelioBaseEnum.class.isAssignableFrom(type) ?
-                    new EnumDeserializer(type) :
-                    super.findEnumDeserializer(type, config, beanDesc);
+            return HelioBaseEnum.class.isAssignableFrom(type)
+                    ? new EnumDeserializer(type)
+                    : super.findEnumDeserializer(type, config, beanDesc);
         }
 
-        private static class EnumDeserializer<E extends HelioBaseEnum<?>> extends StdScalarDeserializer<E> {
+        private static final class EnumDeserializer<E extends HelioBaseEnum<?>> extends StdScalarDeserializer<E> {
             private final Class<E> enumType;
 
             private EnumDeserializer(Class<E> clazz) {
@@ -56,10 +55,12 @@ public class EnumModule extends SimpleModule {
             public E deserialize(JsonParser parser, DeserializationContext context) throws IOException {
                 if (parser.getCurrentToken().isNumeric()) {
                     // 前端传递value
-                    return HelioBaseEnum.of(this.enumType, parser.getIntValue()).orElseThrow(() -> new IllegalArgumentException("Unable to parse input value"));
+                    return HelioBaseEnum.of(this.enumType, parser.getIntValue())
+                            .orElseThrow(() -> new IllegalArgumentException("Unable to parse input value"));
                 } else if (CharSequenceUtil.isNotBlank(parser.getText())) {
                     // 前端传递label
-                    return HelioBaseEnum.of(this.enumType, parser.getText()).orElseThrow(() -> new IllegalArgumentException("Unable to parse input value"));
+                    return HelioBaseEnum.of(this.enumType, parser.getText())
+                            .orElseThrow(() -> new IllegalArgumentException("Unable to parse input value"));
                 } else {
                     throw new IllegalArgumentException("Unable to parse input value 'cause wrong type");
                 }
@@ -67,15 +68,15 @@ public class EnumModule extends SimpleModule {
         }
     }
 
-    @SuppressWarnings({"rawtypes"})
-    private static class EnumSerializer extends StdSerializer<HelioBaseEnum> {
+    private static final class EnumSerializer extends StdSerializer<HelioBaseEnum> {
 
         private EnumSerializer() {
             super(HelioBaseEnum.class);
         }
 
         @Override
-        public void serialize(HelioBaseEnum data, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
+        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());

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

@@ -4,6 +4,7 @@ import cc.uncarbon.framework.core.constant.HelioConstant;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
 import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
@@ -24,7 +25,7 @@ public class HelioJacksonModule extends SimpleModule {
     public HelioJacksonModule() {
         super();
         /*
-         大整数转字符串, 避免精度丢失问题
+        大整数转字符串, 避免精度丢失问题
          */
         this.addSerializer(Long.class, ToStringSerializer.instance);
         this.addSerializer(Long.TYPE, ToStringSerializer.instance);
@@ -32,14 +33,18 @@ public class HelioJacksonModule extends SimpleModule {
         this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
 
         /*
-        时间相关
-         */
-        this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(HelioConstant.Jackson.DATE_TIME_FORMAT)));
-        this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(HelioConstant.Jackson.DATE_FORMAT)));
-        this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(HelioConstant.Jackson.TIME_FORMAT)));
-        this.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
-        this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(HelioConstant.Jackson.DATE_FORMAT)));
-        this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(HelioConstant.Jackson.TIME_FORMAT)));
+        时间相关;时区跟随JVM设置
+        */
+        // emmm 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);
+        this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
+        this.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
+        this.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
+        this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
+        this.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
+        this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
     }
 
 }

+ 0 - 193
helio-starter-web/src/main/java/cc/uncarbon/framework/web/jackson/LocalDateTimeDeserializer.java

@@ -1,193 +0,0 @@
-package cc.uncarbon.framework.web.jackson;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.core.JsonTokenId;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.datatype.jsr310.deser.JSR310DateTimeDeserializerBase;
-
-import java.io.IOException;
-import java.time.*;
-import java.time.format.DateTimeFormatter;
-
-import static cn.hutool.core.date.DatePattern.*;
-
-/**
- * 字段类型是LocalDateTime时,可以按照以下6种格式反序列化:
- * 1. yyyy-MM-dd
- * 2. yyyy年MM月dd日
- * 3. yyyy/MM/dd
- * 4. yyyy-MM-dd HH:mm:ss
- * 5. yyyy年MM月dd日HH时mm分ss秒
- * 6. yyyy/MM/dd HH:mm:ss
- *
- * @author zuihou
- */
-@SuppressWarnings({"squid:S110", "squid:S6396"})
-public class LocalDateTimeDeserializer extends JSR310DateTimeDeserializerBase<LocalDateTime> {
-    public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer();
-    private static final long serialVersionUID = 1L;
-    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
-    /**
-     * 以下是支持的6种参数格式
-     */
-    private static final DateTimeFormatter DEFAULT_DATE_FORMAT_DTF = DateTimeFormatter.ofPattern(NORM_DATE_PATTERN);
-    private static final DateTimeFormatter DEFAULT_DATE_FORMAT_EN_DTF = DateTimeFormatter.ofPattern(CHINESE_DATE_PATTERN);
-    private static final DateTimeFormatter SLASH_DATE_FORMAT_DTF = DateTimeFormatter.ofPattern("yyyy/MM/dd");
-    private static final DateTimeFormatter DEFAULT_DATE_TIME_FORMAT_DTF = DateTimeFormatter.ofPattern(NORM_DATETIME_PATTERN);
-    private static final DateTimeFormatter DEFAULT_DATE_TIME_FORMAT_EN_DTF = DateTimeFormatter.ofPattern(CHINESE_DATE_TIME_PATTERN);
-    private static final DateTimeFormatter SLASH_DATE_TIME_FORMAT_DTF = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
-
-    /**
-     * 正则匹配
-     */
-    private static final String DEFAULT_DATE_FORMAT_MATCHES = "^\\d{4}-\\d{1,2}-\\d{1,2}$";
-    private static final String DEFAULT_DATE_TIME_FORMAT_MATCHES = "^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$";
-    private static final String DEFAULT_DATE_FORMAT_EN_MATCHES = "^\\d{4}年\\d{1,2}月\\d{1,2}日$";
-    public static final String DEFAULT_DATE_TIME_FORMAT_EN_MATCHES = "^\\d{4}年\\d{1,2}月\\d{1,2}日\\d{1,2}时\\d{1,2}分\\d{1,2}秒$";
-    private static final String SLASH_DATE_FORMAT_MATCHES = "^\\d{4}/\\d{1,2}/\\d{1,2}$";
-    private static final String SLASH_DATE_TIME_FORMAT_MATCHES = "^\\d{4}/\\d{1,2}/\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$";
-
-    @Override
-    protected JSR310DateTimeDeserializerBase<LocalDateTime> withShape(JsonFormat.Shape shape) {
-        return this;
-    }
-
-    private LocalDateTimeDeserializer() {
-        this(DEFAULT_FORMATTER);
-    }
-
-    public LocalDateTimeDeserializer(DateTimeFormatter formatter) {
-        super(LocalDateTime.class, formatter);
-    }
-
-    protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, Boolean leniency) {
-        super(base, leniency);
-    }
-
-    @Override
-    protected LocalDateTimeDeserializer withLeniency(Boolean leniency) {
-        return new LocalDateTimeDeserializer(this, leniency);
-    }
-
-    @Override
-    protected JSR310DateTimeDeserializerBase<LocalDateTime> withDateFormat(DateTimeFormatter formatter) {
-        return new com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer(formatter);
-    }
-
-    private LocalDateTime convert(String source) {
-        if (source.matches(DEFAULT_DATE_FORMAT_MATCHES)) {
-            return LocalDateTime.of(LocalDate.parse(source, DEFAULT_DATE_FORMAT_DTF), LocalTime.MIN);
-        }
-        if (source.matches(DEFAULT_DATE_FORMAT_EN_MATCHES)) {
-            return LocalDateTime.of(LocalDate.parse(source, DEFAULT_DATE_FORMAT_EN_DTF), LocalTime.MIN);
-        }
-        if (source.matches(SLASH_DATE_FORMAT_MATCHES)) {
-            return LocalDateTime.of(LocalDate.parse(source, SLASH_DATE_FORMAT_DTF), LocalTime.MIN);
-        }
-        if (source.matches(DEFAULT_DATE_TIME_FORMAT_MATCHES)) {
-            return LocalDateTime.parse(source, DEFAULT_DATE_TIME_FORMAT_DTF);
-        }
-        if (source.matches(DEFAULT_DATE_TIME_FORMAT_EN_MATCHES)) {
-            return LocalDateTime.parse(source, DEFAULT_DATE_TIME_FORMAT_EN_DTF);
-        }
-        if (source.matches(SLASH_DATE_TIME_FORMAT_MATCHES)) {
-            return LocalDateTime.parse(source, SLASH_DATE_TIME_FORMAT_DTF);
-        }
-        return null;
-    }
-
-    @SuppressWarnings({"squid:S6541", "squid:S3776"})
-    @Override
-    public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
-        // 字符串
-        if (parser.hasTokenId(JsonTokenId.ID_STRING)) {
-            String string = parser.getText().trim();
-            if (string.isEmpty()) {
-                return null;
-            }
-
-            try {
-                if (_formatter == null) {
-                    return convert(string);
-                }
-                if (_formatter == DEFAULT_FORMATTER) {
-                    // JavaScript by default includes time and zone in JSON serialized Dates (UTC/ISO instant format).
-                    if (string.length() > 10 && string.charAt(10) == 'T') {
-                        if (string.endsWith("Z")) {
-                            return LocalDateTime.ofInstant(Instant.parse(string), ZoneOffset.UTC);
-                        } else {
-                            return LocalDateTime.parse(string, DEFAULT_FORMATTER);
-                        }
-                    }
-                    return convert(string);
-                }
-
-                return LocalDateTime.parse(string, this._formatter);
-            } catch (DateTimeException e) {
-                return _handleDateTimeException(context, e, string);
-            }
-        }
-        // 数组
-        if (parser.isExpectedStartArrayToken()) {
-            JsonToken t = parser.nextToken();
-            if (t == JsonToken.END_ARRAY) {
-                return null;
-            }
-            if ((t == JsonToken.VALUE_STRING || t == JsonToken.VALUE_EMBEDDED_OBJECT)
-                    && context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-                final LocalDateTime parsed = deserialize(parser, context);
-                if (parser.nextToken() != JsonToken.END_ARRAY) {
-                    handleMissingEndArrayForSingle(parser, context);
-                }
-                return parsed;
-            }
-            if (t == JsonToken.VALUE_NUMBER_INT) {
-                LocalDateTime result;
-
-                int year = parser.getIntValue();
-                int month = parser.nextIntValue(-1);
-                int day = parser.nextIntValue(-1);
-                int hour = parser.nextIntValue(-1);
-                int minute = parser.nextIntValue(-1);
-
-                t = parser.nextToken();
-                if (t == JsonToken.END_ARRAY) {
-                    result = LocalDateTime.of(year, month, day, hour, minute);
-                } else {
-                    int second = parser.getIntValue();
-                    t = parser.nextToken();
-                    if (t == JsonToken.END_ARRAY) {
-                        result = LocalDateTime.of(year, month, day, hour, minute, second);
-                    } else {
-                        int partialSecond = parser.getIntValue();
-                        if (partialSecond < 1_000 &&
-                                !context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) {
-                            // value is milliseconds, convert it to nanoseconds
-                            partialSecond *= 1_000_000;
-                        }
-                        if (parser.nextToken() != JsonToken.END_ARRAY) {
-                            throw context.wrongTokenException(parser, handledType(), JsonToken.END_ARRAY, "Expected array to end");
-                        }
-                        result = LocalDateTime.of(year, month, day, hour, minute, second, partialSecond);
-                    }
-                }
-                return result;
-            }
-            context.reportInputMismatch(handledType(), "Unexpected token (%s) within Array, expected VALUE_NUMBER_INT", t);
-        }
-        // 数字
-        if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) {
-            return Instant.ofEpochMilli(parser.getLongValue()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
-        }
-        // 没看懂这个是啥
-        if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) {
-            return (LocalDateTime) parser.getEmbeddedObject();
-        }
-
-        return _handleUnexpectedToken(context, parser, "Current parameters require array, string, timestamp");
-    }
-
-}

+ 7 - 8
helio-starter-web/src/main/java/cc/uncarbon/framework/web/listener/LaunchEventListener.java

@@ -20,7 +20,6 @@ import java.net.UnknownHostException;
 @Slf4j
 public class LaunchEventListener {
 
-    @SuppressWarnings("squid:S106")
     @Async
     @Order
     @EventListener(WebServerInitializedEvent.class)
@@ -47,13 +46,13 @@ public class LaunchEventListener {
 
         System.out.println(
                 CharSequenceUtil.format(
-                        "\n"+
-                                "----------------------------------------------------------" +
-                                "\n\tApplication '{}' is running! Access URLs:" +
-                                "\n\tLocal: \t\t{}://127.0.0.1:{}{}" +
-                                "\n\tExternal: \t{}://{}:{}{}" +
-                                "\n----------------------------------------------------------" +
-                                "\n",
+                        "\n"
+                                + "----------------------------------------------------------"
+                                + "\n\tApplication '{}' is running! Access URLs:"
+                                + "\n\tLocal: \t\t{}://127.0.0.1:{}{}"
+                                + "\n\tExternal: \t{}://{}:{}{}"
+                                + "\n----------------------------------------------------------"
+                                + "\n",
                         env.getProperty("spring.application.name"),
                         protocol,
                         port,

+ 4 - 2
helio-starter-web/src/main/java/cc/uncarbon/framework/web/model/response/ApiResult.java

@@ -14,10 +14,10 @@ import java.io.Serializable;
 
 /**
  * HTTP接口通用返回对象
+ * @param <T> 承载数据类型
  *
  * @author Uncarbon
  */
-@SuppressWarnings("squid:S1948")
 @Accessors(chain = true)
 @AllArgsConstructor
 @NoArgsConstructor
@@ -50,7 +50,9 @@ public class ApiResult<T> implements Serializable {
     }
 
     public static <T> ApiResult<T> data(T data) {
-        return build(HttpStatus.HTTP_OK, ObjectUtil.isEmpty(data) ? HelioConstant.Message.NULL : HelioConstant.Message.SUCCESS, data);
+        return build(HttpStatus.HTTP_OK,
+                ObjectUtil.isEmpty(data) ? HelioConstant.Message.NULL : HelioConstant.Message.SUCCESS,
+                data);
     }
 
     public static <T> ApiResult<T> data(String msg, T data) {

+ 9 - 9
helio-starter-web/src/main/java/cc/uncarbon/framework/web/util/InvalidFieldUtil.java

@@ -26,20 +26,20 @@ import java.util.concurrent.ConcurrentHashMap;
 @UtilityClass
 public class InvalidFieldUtil {
 
-    /**
-     * 特定关键词时-指定文案映射关系
-     * 使用 public 修饰符以支持外部变更
-     */
-    public final Map<String, String> MESSAGE_MASKING_MAPPER = new ConcurrentHashMap<>(16);
+    private static final Map<String, String> MESSAGE_MASKING_MAPPER = new ConcurrentHashMap<>(16);
 
     static {
-        /*
-        完整的默认消息
-        Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'beginAt'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@com.fasterxml.jackson.annotation.JsonFormat @io.swagger.annotations.ApiModelProperty @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime] for value '2022-08-18'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-08-18]
-         */
+        // Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'beginAt'; ...
         MESSAGE_MASKING_MAPPER.put("Failed to convert property value of type", "错误参数格式或值");
     }
 
+    /**
+     * @return 返回内置的「特定关键词时-指定文案映射关系 map」
+     */
+    public Map<String, String> mapper() {
+        return MESSAGE_MASKING_MAPPER;
+    }
+
     /**
      * 参数绑定验证
      *

+ 2 - 4
helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/HTMLFilter.java

@@ -57,7 +57,6 @@ public final class HTMLFilter {
     private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
     private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
     private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
-    @SuppressWarnings("squid:S6019")
     private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
     private static final Pattern P_END_ARROW = Pattern.compile("^>");
     private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
@@ -123,7 +122,7 @@ public final class HTMLFilter {
      * unbalanced angle brackets will be html escaped.
      */
     private final boolean alwaysMakeTags;
-    private boolean vDebug = false;
+    private final boolean vDebug = false;
 
     /**
      * Default constructor.
@@ -319,7 +318,6 @@ public final class HTMLFilter {
         return result;
     }
 
-    @SuppressWarnings({"squid:S6541", "squid:S3776"})
     private String processTag(final String s) {
         // ending tags
         Matcher m = P_END_TAG.matcher(s);
@@ -376,7 +374,7 @@ public final class HTMLFilter {
                     ending = "";
                 }
 
-                if (ending == null || ending.length() < 1) {
+                if (ending == null || ending.isEmpty()) {
                     if (vTagCounts.containsKey(name)) {
                         vTagCounts.put(name, vTagCounts.get(name) + 1);
                     } else {

+ 1 - 1
helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/SQLFilter.java

@@ -9,7 +9,7 @@ import org.springframework.util.StringUtils;
  *
  * @author Mark sunlightcs@gmail.com
  */
-public class SQLFilter {
+public final class SQLFilter {
 
     private SQLFilter() {
     }

+ 36 - 3
helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/XssFilter.java

@@ -1,22 +1,55 @@
 package cc.uncarbon.framework.web.xss;
 
+import cn.hutool.core.collection.CollUtil;
+import lombok.RequiredArgsConstructor;
+import org.springframework.util.AntPathMatcher;
+
 import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
+import java.util.List;
 
 /**
- * XSS过滤
+ * XSS过滤
  *
  * @author Mark sunlightcs@gmail.com
  */
+@RequiredArgsConstructor
 public class XssFilter implements Filter {
 
+	/**
+	 * 不进行过滤的路由(直接放行)
+	 */
+	private final List<String> excludedRoutes;
+
+	private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();
+
+
 	@Override
 	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
             throws IOException, ServletException {
-		XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
-				(HttpServletRequest) request);
+		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+		if (canExclude(httpServletRequest)) {
+			// 不经过XSS过滤,交给下一个过滤器
+			chain.doFilter(httpServletRequest, response);
+			return;
+		}
+
+		XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(httpServletRequest);
 		chain.doFilter(xssRequest, response);
 	}
 
+	/**
+	 * 该路由是否可以排除XSS过滤
+	 */
+	private boolean canExclude(HttpServletRequest request) {
+		if (CollUtil.isEmpty(excludedRoutes)) {
+			return false;
+		}
+
+		String requestUri = request.getServletPath();
+		return excludedRoutes.stream().anyMatch(
+				excludedRoute -> ANT_PATH_MATCHER.match(excludedRoute, requestUri)
+		);
+	}
 }

+ 2 - 2
helio-starter-web/src/main/java/cc/uncarbon/framework/web/xss/XssHttpServletRequestWrapper.java

@@ -27,7 +27,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
     /**
      * html过滤器
      */
-    private static final HTMLFilter htmlFilter = new HTMLFilter();
+    private static final HTMLFilter HTML_FILTER = new HTMLFilter();
 
     /**
      * 没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
@@ -141,6 +141,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
     }
 
     private String xssEncode(String input) {
-        return htmlFilter.filter(input);
+        return HTML_FILTER.filter(input);
     }
 }

+ 3 - 17
pom.xml

@@ -31,11 +31,11 @@
 
     <groupId>cc.uncarbon.framework</groupId>
     <artifactId>helio-starters</artifactId>
-    <version>1.10.0</version><!-- BATCH REPLACEMENT MARK -->
+    <version>1.11.0</version><!-- BATCH REPLACEMENT MARK -->
 
     <properties>
         <!-- core versions -->
-        <revision>1.10.0</revision>
+        <revision>1.11.0</revision>
         <helio-starters.version>${revision}</helio-starters.version>
         <!-- 可根据实际JDK版本修改;JDK8则改为'1.8'  JDK11则改为'11'  JDK17则改为'17'  JDK21则改为'21' -->
         <java.version>1.8</java.version>
@@ -58,7 +58,7 @@
         <!-- 3rd-party versions -->
         <lombok.version>1.18.30</lombok.version>
         <slf4j.version>1.7.36</slf4j.version>
-        <hutool.version>5.8.24</hutool.version>
+        <hutool.version>5.8.25</hutool.version>
         <sa-token.version>1.37.0</sa-token.version>
         <transmittable-thread-local.version>2.14.2</transmittable-thread-local.version>
         <!-- 如果 mp 较新版存在问题,可以回退至 3.5.3.1 或 3.5.4 试试  -->
@@ -92,10 +92,8 @@
         <guava.version>33.0.0-jre</guava.version>
         <snakeyaml.version>1.33</snakeyaml.version>
         <protobuf-java.version>3.25.1</protobuf-java.version>
-        <undertow-core.version>2.3.10.Final</undertow-core.version>
         <icu4j.version>74.2</icu4j.version>
         <json-path.version>2.8.0</json-path.version>
-        <xnio-api.version>3.8.12.Final</xnio-api.version>
     </properties>
 
     <!-- 全局使用 -->
@@ -135,12 +133,6 @@
                 <version>${protobuf-java.version}</version>
             </dependency>
 
-            <dependency>
-                <groupId>io.undertow</groupId>
-                <artifactId>undertow-core</artifactId>
-                <version>${undertow-core.version}</version>
-            </dependency>
-
             <dependency>
                 <groupId>com.ibm.icu</groupId>
                 <artifactId>icu4j</artifactId>
@@ -153,12 +145,6 @@
                 <version>${json-path.version}</version>
             </dependency>
 
-            <dependency>
-                <groupId>org.jboss.xnio</groupId>
-                <artifactId>xnio-api</artifactId>
-                <version>${xnio-api.version}</version>
-            </dependency>
-
 
             <!-- sub starters -->
             <dependency>