home 2 rokov pred
rodič
commit
19b7ac9c75
100 zmenil súbory, kde vykonal 3114 pridanie a 2125 odobranie
  1. 43 0
      bladex-tool/README.md
  2. 84 37
      bladex-tool/blade-bom/pom.xml
  3. 12 0
      bladex-tool/blade-core-auto/README.md
  4. 1 1
      bladex-tool/blade-core-auto/pom.xml
  5. 11 7
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoEnvPostProcessor.java
  6. 19 6
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/BootAutoType.java
  7. 1 1
      bladex-tool/blade-core-boot/pom.xml
  8. 2 2
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeBootAutoConfiguration.java
  9. 2 2
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeRetryConfiguration.java
  10. 2 2
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeWebMvcConfiguration.java
  11. 2 2
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/RequestConfiguration.java
  12. 11 7
      bladex-tool/blade-core-boot/src/main/resources/blade-boot.yml
  13. 8 56
      bladex-tool/blade-core-cloud/pom.xml
  14. 2 2
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/client/BladeCloudApplication.java
  15. 1 1
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFallbackFactory.java
  16. 42 42
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/EnableBladeFeign.java
  17. 2 2
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpConfiguration.java
  18. 4 4
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java
  19. 0 126
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixAutoConfiguration.java
  20. 0 91
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixConcurrencyStrategy.java
  21. 1 1
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeFeignSentinel.java
  22. 2 4
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelAutoConfiguration.java
  23. 73 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelFilterConfiguration.java
  24. 1 1
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelInvocationHandler.java
  25. 2 4
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/server/UndertowHttp2Configuration.java
  26. 2 2
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/VersionMappingAutoConfiguration.java
  27. 0 229
      bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeFeignClientsRegistrar.java
  28. 0 99
      bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeHystrixTargeter.java
  29. 0 39
      bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/Targeter.java
  30. 1 1
      bladex-tool/blade-core-context/pom.xml
  31. 2 2
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeContextAutoConfiguration.java
  32. 2 2
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeServletListenerConfiguration.java
  33. 1 1
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/props/BladeContextProperties.java
  34. 10 1
      bladex-tool/blade-core-db/pom.xml
  35. 2 2
      bladex-tool/blade-core-db/src/main/java/org/springblade/core/db/config/DbConfiguration.java
  36. 5 1
      bladex-tool/blade-core-launch/pom.xml
  37. 1 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java
  38. 2 2
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/StartEventListener.java
  39. 2 2
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladeLaunchConfiguration.java
  40. 3 3
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladePropertyConfiguration.java
  41. 1 6
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java
  42. 2 2
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/server/ServerInfo.java
  43. 85 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/utils/INetUtil.java
  44. 1 1
      bladex-tool/blade-core-log4j2/pom.xml
  45. 1 1
      bladex-tool/blade-core-secure/pom.xml
  46. 2 4
      bladex-tool/blade-core-secure/src/main/java/org/springblade/core/secure/config/RegistryConfiguration.java
  47. 2 2
      bladex-tool/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java
  48. 10 5
      bladex-tool/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java
  49. 1 1
      bladex-tool/blade-core-test/pom.xml
  50. 2 0
      bladex-tool/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTest.java
  51. 19 12
      bladex-tool/blade-core-test/src/main/java/org/springblade/core/test/BladeSpringExtension.java
  52. 1 1
      bladex-tool/blade-core-tool/pom.xml
  53. 199 272
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/api/R.java
  54. 2 2
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/BladeConverterConfiguration.java
  55. 2 4
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/JacksonConfiguration.java
  56. 2 2
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/MessageConfiguration.java
  57. 2 2
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/ToolConfiguration.java
  58. 1 1
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/AbstractReadWriteJackson2HttpMessageConverter.java
  59. 28 0
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/JsonUtil.java
  60. 20 2
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/node/TreeNode.java
  61. 0 130
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/utils/SpringUtils.java
  62. 12 0
      bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java
  63. 34 0
      bladex-tool/blade-starter-actuate/README.md
  64. 1 1
      bladex-tool/blade-starter-actuate/pom.xml
  65. 2 2
      bladex-tool/blade-starter-actuate/src/main/java/org/springblade/core/http/cache/HttpCacheConfiguration.java
  66. 4 0
      bladex-tool/blade-starter-api-crypto/README.md
  67. 1 1
      bladex-tool/blade-starter-api-crypto/pom.xml
  68. 2 2
      bladex-tool/blade-starter-api-crypto/src/main/java/org/springblade/core/api/crypto/config/ApiCryptoConfiguration.java
  69. 2 2
      bladex-tool/blade-starter-api-crypto/src/main/java/org/springblade/core/api/crypto/core/ApiDecryptRequestBodyAdvice.java
  70. 2 2
      bladex-tool/blade-starter-api-crypto/src/main/java/org/springblade/core/api/crypto/core/ApiEncryptResponseBodyAdvice.java
  71. 1 1
      bladex-tool/blade-starter-auth/pom.xml
  72. 402 410
      bladex-tool/blade-starter-auth/src/main/java/org/springblade/core/secure/utils/AuthUtil.java
  73. 1 1
      bladex-tool/blade-starter-cache/pom.xml
  74. 2 2
      bladex-tool/blade-starter-cache/src/main/java/org/springblade/core/cache/config/CacheConfiguration.java
  75. 1 1
      bladex-tool/blade-starter-datascope/pom.xml
  76. 4 4
      bladex-tool/blade-starter-datascope/src/main/java/org/springblade/core/datascope/config/DataScopeConfiguration.java
  77. 5 11
      bladex-tool/blade-starter-develop/pom.xml
  78. 14 3
      bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/CodeGenerator.java
  79. 31 1
      bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/constant/DevelopConstant.java
  80. 195 217
      bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/support/BladeCodeGenerator.java
  81. 132 0
      bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/support/BladeTemplateEngine.java
  82. 10 0
      bladex-tool/blade-starter-develop/src/main/resources/beetl.properties
  83. 222 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/controller.java.btl
  84. 61 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/entity.java.btl
  85. 6 11
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/entityDTO.java.btl
  86. 87 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/entityVO.java.btl
  87. 49 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/feign.java.btl
  88. 53 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/feignclient.java.btl
  89. 20 15
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/mapper.java.btl
  90. 39 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/mapper.xml.btl
  91. 23 15
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/service.java.btl
  92. 52 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/serviceImpl.java.btl
  93. 61 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/api/wrapper.java.btl
  94. 0 181
      bladex-tool/blade-starter-develop/src/main/resources/templates/controller.java.vm
  95. 5 5
      bladex-tool/blade-starter-develop/src/main/resources/templates/element/crud/api.js.btl
  96. 31 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/element/crud/const.js.btl
  97. 347 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/element/crud/crud.vue.btl
  98. 50 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/element/sub/api.js.btl
  99. 31 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/element/sub/const.js.btl
  100. 375 0
      bladex-tool/blade-starter-develop/src/main/resources/templates/element/sub/crud.vue.btl

+ 43 - 0
bladex-tool/README.md

@@ -0,0 +1,43 @@
+## 版权声明
+* BladeX是一个商业化软件,系列产品知识产权归**上海布雷德科技有限公司**独立所有
+* 您一旦开始复制、下载、安装或者使用本产品,即被视为完全理解并接受本协议的各项条款
+* 更多详情请看:[BladeX商业授权许可协议](/LICENSE)
+
+## 答疑流程
+>1. 遇到问题或Bug
+>2. 业务型问题打断点调试尝试找出问题所在
+>3. 系统型问题通过百度、谷歌、社区查找解决方案
+>4. 未解决问题则进入技术社区进行发帖提问:[https://sns.bladex.vip/](https://sns.bladex.vip/)
+>5. 将帖子地址发至商业群,特别简单三言两语就能描述清楚的也可在答疑时间内发至商业群提问
+>6. 发帖的时候一定要描述清楚,详细描述遇到问题的**重现步骤**、**报错详细信息**、**相关代码与逻辑**、**使用软件版本**以及**操作系统版本**,否则随意发帖提问将会提高我们的答疑难度。
+
+## 答疑时间
+* 工作日:9:00 ~ 17:00 提供答疑,周末、节假日休息,暂停答疑
+* 请勿**私聊提问**,以免被其他用户的消息覆盖从而无法获得答疑
+* 答疑时间外遇到问题可以将问题发帖至[技术社区](https://sns.bladex.vip/),我们后续会逐个回复
+
+## 授权范围
+* 专业版:只可用于**个人学习**及**个人私活**项目,不可用于公司或团队,不可泄露给任何第三方
+* 企业版:可用于**企业名下**的任何项目,企业版员工在**未购买**专业版授权前,只授权开发**所在授权企业名下**的项目,**不得将BladeX用于个人私活**
+* 共同遵守:若甲方需要您提供项目源码,则需代为甲方购买BladeX企业授权,甲方购买后续的所有项目都无需再次购买授权
+
+## 商用权益
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,将BladeX系列产品用于授权范围内的商用项目,并上线运营
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,不限制项目数,不限制服务器数
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,将自行编写的业务代码申请软件著作权
+
+## 何为侵权
+* ❌ 不遵守商业协议,私自销售商业源码
+* ❌ 以任何理由将BladeX源码用于申请软件著作权
+* ❌ 将商业源码以任何途径任何理由泄露给未授权的单位或个人
+* ❌ 开发完毕项目,没有为甲方购买企业授权,向甲方提供了BladeX代码
+* ❌ 基于BladeX拓展研发与BladeX有竞争关系的衍生框架,并将其开源或销售
+
+## 侵权后果
+* 情节较轻:第一次发现警告处理
+* 情节较重:封禁账号,踢出商业群,并保留追究法律责任的权利
+* 情节严重:与本地律师事务所合作,以公司名义起诉侵犯计算机软件著作权
+
+## 举报有奖
+* 向官方提供有用线索并成功捣毁盗版个人或窝点,将会看成果给予 500~10000 不等的现金奖励
+* 官方唯一指定QQ:1272154962

+ 84 - 37
bladex-tool/blade-bom/pom.xml

@@ -8,7 +8,7 @@
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>2.3.12.RELEASE</version>
+        <version>2.7.1</version>
         <relativePath />
     </parent>
 
@@ -19,31 +19,34 @@
     <description>bladex统一版本配置</description>
 
     <properties>
-        <bladex.tool.version>2.8.2.RELEASE</bladex.tool.version>
+        <bladex.tool.version>3.0.1.RELEASE</bladex.tool.version>
 
         <swagger.version>2.10.5</swagger.version>
         <swagger.models.version>1.6.2</swagger.models.version>
         <knife4j.version>2.0.9</knife4j.version>
-        <mybatis.plus.version>3.4.2</mybatis.plus.version>
-        <mybatis.plus.generator.version>3.4.1</mybatis.plus.generator.version>
+        <mybatis.plus.version>3.5.2</mybatis.plus.version>
+        <mybatis.plus.generator.version>3.5.3</mybatis.plus.generator.version>
         <mybatis.plus.dynamic.version>3.3.6</mybatis.plus.dynamic.version>
         <protostuff.version>1.6.0</protostuff.version>
         <disruptor.version>3.4.2</disruptor.version>
         <logstash.version>6.2</logstash.version>
-        <druid.version>1.2.5</druid.version>
-        <jackson.version>2.11.4</jackson.version>
-        <okhttp.version>3.14.5</okhttp.version>
+        <druid.version>1.2.8</druid.version>
+        <jackson.version>2.13.3</jackson.version>
+        <okhttp.version>4.9.3</okhttp.version>
         <xxl.job.version>2.1.2</xxl.job.version>
+        <log4j2.version>2.18.0</log4j2.version>
+        <logback.version>1.2.11</logback.version>
 
         <mysql.connector.version>8.0.22</mysql.connector.version>
         <oracle.connector.version>12.2.0.1</oracle.connector.version>
         <postgresql.connector.version>42.2.22</postgresql.connector.version>
         <sqlserver.connector.version>8.4.1.jre8</sqlserver.connector.version>
+        <dameng.connector.version>8.1.2.79</dameng.connector.version>
 
-        <spring.boot.admin.version>2.3.1</spring.boot.admin.version>
-        <alibaba.cloud.version>2.2.5.RELEASE</alibaba.cloud.version>
-        <alibaba.seata.version>1.4.2</alibaba.seata.version>
-        <alibaba.nacos.version>2.0.2</alibaba.nacos.version>
+        <spring.boot.admin.version>2.7.1</spring.boot.admin.version>
+        <alibaba.cloud.version>2021.0.1.0</alibaba.cloud.version>
+        <alibaba.seata.version>1.5.2</alibaba.seata.version>
+        <alibaba.nacos.version>2.1.0</alibaba.nacos.version>
     </properties>
 
     <dependencyManagement>
@@ -91,7 +94,7 @@
                 <version>${bladex.tool.version}</version>
             </dependency>
             <dependency>
-                <groupId>org.springblade</groupId>
+                <groupId>org.springblade</groupId>l
                 <artifactId>blade-core-test</artifactId>
                 <version>${bladex.tool.version}</version>
             </dependency>
@@ -142,6 +145,11 @@
             </dependency>
             <dependency>
                 <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-flowable</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
                 <artifactId>blade-starter-http</artifactId>
                 <version>${bladex.tool.version}</version>
             </dependency>
@@ -192,7 +200,7 @@
             </dependency>
             <dependency>
                 <groupId>org.springblade</groupId>
-                <artifactId>blade-starter-ribbon</artifactId>
+                <artifactId>blade-starter-loadbalancer</artifactId>
                 <version>${bladex.tool.version}</version>
             </dependency>
             <dependency>
@@ -291,16 +299,6 @@
             </dependency>
             <dependency>
                 <groupId>org.mybatis</groupId>
-                <artifactId>mybatis-spring</artifactId>
-                <version>2.0.6</version>
-            </dependency>
-            <dependency>
-                <groupId>org.mybatis</groupId>
-                <artifactId>mybatis</artifactId>
-                <version>3.5.6</version>
-            </dependency>
-            <dependency>
-                <groupId>org.mybatis</groupId>
                 <artifactId>mybatis-typehandlers-jsr310</artifactId>
                 <version>1.0.2</version>
             </dependency>
@@ -361,6 +359,12 @@
                 <artifactId>mssql-jdbc</artifactId>
                 <version>${sqlserver.connector.version}</version>
             </dependency>
+            <!-- DaMeng -->
+            <dependency>
+                <groupId>com.dameng</groupId>
+                <artifactId>DmJdbcDriver18</artifactId>
+                <version>${dameng.connector.version}</version>
+            </dependency>
             <!--Swagger-->
             <dependency>
                 <groupId>io.springfox</groupId>
@@ -469,25 +473,25 @@
             <dependency>
                 <groupId>com.alibaba</groupId>
                 <artifactId>fastjson</artifactId>
-                <version>1.2.76</version>
+                <version>1.2.83_noneautotype</version>
             </dependency>
             <!-- sentinel -->
             <dependency>
                 <groupId>com.alibaba.csp</groupId>
                 <artifactId>sentinel-core</artifactId>
-                <version>1.8.0</version>
+                <version>1.8.4</version>
             </dependency>
             <!-- easyexcel -->
             <dependency>
                 <groupId>com.alibaba</groupId>
                 <artifactId>easyexcel</artifactId>
-                <version>2.2.10</version>
+                <version>2.2.11</version>
             </dependency>
             <!-- JustAuth -->
             <dependency>
                 <groupId>me.zhyd.oauth</groupId>
                 <artifactId>JustAuth</artifactId>
-                <version>1.16.1</version>
+                <version>1.16.5</version>
             </dependency>
             <!-- Guava -->
             <dependency>
@@ -499,48 +503,48 @@
             <dependency>
                 <groupId>io.micrometer</groupId>
                 <artifactId>micrometer-core</artifactId>
-                <version>1.6.3</version>
+                <version>1.9.1</version>
             </dependency>
             <dependency>
                 <groupId>io.micrometer</groupId>
                 <artifactId>micrometer-registry-prometheus</artifactId>
-                <version>1.6.3</version>
+                <version>1.9.1</version>
             </dependency>
             <!--AliOss-->
             <dependency>
                 <groupId>com.aliyun.oss</groupId>
                 <artifactId>aliyun-sdk-oss</artifactId>
-                <version>3.11.3</version>
+                <version>3.14.0</version>
             </dependency>
             <!--MinIO-->
             <dependency>
                 <groupId>io.minio</groupId>
                 <artifactId>minio</artifactId>
-                <version>8.1.0</version>
+                <version>8.3.7</version>
             </dependency>
             <!--QiNiu-->
             <dependency>
                 <groupId>com.qiniu</groupId>
                 <artifactId>qiniu-java-sdk</artifactId>
-                <version>7.4.0</version>
+                <version>7.9.4</version>
             </dependency>
             <!--腾讯COS-->
             <dependency>
                 <groupId>com.qcloud</groupId>
                 <artifactId>cos_api</artifactId>
-                <version>5.6.36</version>
+                <version>5.6.69</version>
             </dependency>
             <!--华为云Obs-->
             <dependency>
                 <groupId>com.huaweicloud</groupId>
                 <artifactId>esdk-obs-java</artifactId>
-                <version>3.19.7</version>
+                <version>3.21.12</version>
             </dependency>
             <!--AliSms-->
             <dependency>
                 <groupId>com.aliyun</groupId>
                 <artifactId>aliyun-java-sdk-core</artifactId>
-                <version>4.5.18</version>
+                <version>4.5.30</version>
             </dependency>
             <!--YunPian-->
             <dependency>
@@ -554,6 +558,12 @@
                 <artifactId>qcloudsms</artifactId>
                 <version>1.0.6</version>
             </dependency>
+            <!-- oauth2 -->
+            <dependency>
+                <groupId>org.springframework.security.oauth</groupId>
+                <artifactId>spring-security-oauth2</artifactId>
+                <version>2.3.8.RELEASE</version>
+            </dependency>
             <!-- findbugs -->
             <dependency>
                 <groupId>com.google.code.findbugs</groupId>
@@ -563,7 +573,44 @@
             <dependency>
                 <groupId>org.projectlombok</groupId>
                 <artifactId>lombok</artifactId>
-                <version>1.18.18</version>
+                <version>1.18.22</version>
+            </dependency>
+            <!-- log4j2 -->
+            <dependency>
+                <groupId>org.apache.logging.log4j</groupId>
+                <artifactId>log4j-api</artifactId>
+                <version>${log4j2.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.logging.log4j</groupId>
+                <artifactId>log4j-core</artifactId>
+                <version>${log4j2.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.logging.log4j</groupId>
+                <artifactId>log4j-jul</artifactId>
+                <version>${log4j2.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.logging.log4j</groupId>
+                <artifactId>log4j-slf4j-impl</artifactId>
+                <version>${log4j2.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.logging.log4j</groupId>
+                <artifactId>log4j-to-slf4j</artifactId>
+                <version>${log4j2.version}</version>
+            </dependency>
+            <!-- logback -->
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>${logback.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-core</artifactId>
+                <version>${logback.version}</version>
             </dependency>
         </dependencies>
     </dependencyManagement>
@@ -572,7 +619,7 @@
         <repository>
             <id>blade-release</id>
             <name>Release Repository</name>
-            <url>http://nexus.bladex.vip/repository/maven-releases/</url>
+            <url>http://nexus.javablade.com/repository/maven-releases/</url>
         </repository>
     </distributionManagement>
 

+ 12 - 0
bladex-tool/blade-core-auto/README.md

@@ -0,0 +1,12 @@
+# auto 代码自动生成
+
+## 规划
+1. 生成 java spi
+2. 用来生成 `spring.factories`
+3. 考虑生成 `spring-devtools.properties`
+4. 考虑?生成 `swagger` 注解 
+
+## 参考
+Google Auto: https://github.com/google/auto
+
+Spring 5 - spring-context-indexer:https://github.com/spring-projects/spring-framework/tree/master/spring-context-indexer

+ 1 - 1
bladex-tool/blade-core-auto/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 11 - 7
bladex-tool/blade-core-test/src/main/java/org/springblade/core/test/BladeBaseTest.java → bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoEnvPostProcessor.java

@@ -14,18 +14,22 @@
  *  this software without specific prior written permission.
  *  Author: DreamLu 卢春梦 (596392912@qq.com)
  */
+package org.springblade.core.auto.annotation;
 
-package org.springblade.core.test;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
 
-import org.junit.runner.RunWith;
-import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 /**
- * boot test 基类
+ * EnvironmentPostProcessor 处理
  *
  * @author L.cm
  */
-@RunWith(BladeSpringRunner.class)
-public abstract class BladeBaseTest extends AbstractJUnit4SpringContextTests {
-
+@Documented
+@Retention(SOURCE)
+@Target(TYPE)
+public @interface AutoEnvPostProcessor {
 }

+ 19 - 6
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/BootAutoType.java

@@ -16,10 +16,7 @@
  */
 package org.springblade.core.auto.common;
 
-import org.springblade.core.auto.annotation.AutoContextInitializer;
-import org.springblade.core.auto.annotation.AutoFailureAnalyzer;
-import org.springblade.core.auto.annotation.AutoListener;
-import org.springblade.core.auto.annotation.AutoRunListener;
+import org.springblade.core.auto.annotation.*;
 
 /**
  * 注解类型
@@ -28,13 +25,29 @@ import org.springblade.core.auto.annotation.AutoRunListener;
  */
 public enum BootAutoType {
 	/**
-	 * 注解处理的类型
+	 * Component,组合注解,添加到 spring.factories
+	 */
+	COMPONENT("org.springframework.stereotype.Component", "org.springframework.boot.autoconfigure.EnableAutoConfiguration"),
+	/**
+	 * ApplicationContextInitializer 添加到 spring.factories
 	 */
 	CONTEXT_INITIALIZER(AutoContextInitializer.class.getName(), "org.springframework.context.ApplicationContextInitializer"),
+	/**
+	 * ApplicationListener 添加到 spring.factories
+	 */
 	LISTENER(AutoListener.class.getName(), "org.springframework.context.ApplicationListener"),
+	/**
+	 * SpringApplicationRunListener 添加到 spring.factories
+	 */
 	RUN_LISTENER(AutoRunListener.class.getName(), "org.springframework.boot.SpringApplicationRunListener"),
+	/**
+	 * FailureAnalyzer 添加到 spring.factories
+	 */
 	FAILURE_ANALYZER(AutoFailureAnalyzer.class.getName(), "org.springframework.boot.diagnostics.FailureAnalyzer"),
-	COMPONENT("org.springframework.stereotype.Component", "org.springframework.boot.autoconfigure.EnableAutoConfiguration");
+	/**
+	 * EnvironmentPostProcessor 添加到 spring.factories
+	 */
+	ENV_POST_PROCESSOR(AutoEnvPostProcessor.class.getName(), "org.springframework.boot.env.EnvironmentPostProcessor");
 
 	private final String annotationName;
 	private final String configureKey;

+ 1 - 1
bladex-tool/blade-core-boot/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.springblade</groupId>
         <artifactId>BladeX-Tool</artifactId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 2 - 2
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeBootAutoConfiguration.java

@@ -19,7 +19,7 @@ package org.springblade.core.boot.config;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springblade.core.launch.props.BladePropertySource;
-import org.springframework.context.annotation.Configuration;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 
 /**
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
  * @author Chill
  */
 @Slf4j
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @AllArgsConstructor
 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
 @BladePropertySource(value = "classpath:/blade-boot.yml")

+ 2 - 2
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeRetryConfiguration.java

@@ -17,9 +17,9 @@
 package org.springblade.core.boot.config;
 
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.retry.interceptor.RetryInterceptorBuilder;
 import org.springframework.retry.interceptor.RetryOperationsInterceptor;
 
@@ -29,7 +29,7 @@ import org.springframework.retry.interceptor.RetryOperationsInterceptor;
  * @author Chill
  */
 @Slf4j
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 public class BladeRetryConfiguration {
 
 	@Bean

+ 2 - 2
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeWebMvcConfiguration.java

@@ -21,8 +21,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.springblade.core.boot.props.BladeFileProperties;
 import org.springblade.core.boot.props.BladeUploadProperties;
 import org.springblade.core.boot.resolver.TokenArgumentResolver;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -37,7 +37,7 @@ import java.util.List;
  * @author Chill
  */
 @Slf4j
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @Order(Ordered.HIGHEST_PRECEDENCE)
 @AllArgsConstructor
 @EnableConfigurationProperties({

+ 2 - 2
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/RequestConfiguration.java

@@ -20,10 +20,10 @@ import lombok.AllArgsConstructor;
 import org.springblade.core.boot.request.BladeRequestFilter;
 import org.springblade.core.boot.request.RequestProperties;
 import org.springblade.core.boot.request.XssProperties;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.Ordered;
 
 import javax.servlet.DispatcherType;
@@ -33,7 +33,7 @@ import javax.servlet.DispatcherType;
  *
  * @author Chill
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @AllArgsConstructor
 @EnableConfigurationProperties({RequestProperties.class, XssProperties.class})
 public class RequestConfiguration {

+ 11 - 7
bladex-tool/blade-core-boot/src/main/resources/blade-boot.yml

@@ -1,6 +1,7 @@
 #服务器配置
 server:
   undertow:
+    # 线程配置
     threads:
       # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
       io: 16
@@ -10,21 +11,24 @@ server:
     buffer-size: 1024
     # 是否分配的直接内存
     direct-buffers: true
-
-#spring配置
-spring:
-  http:
+  servlet:
+    # 编码配置
     encoding:
       charset: UTF-8
       force: true
+
+#spring配置
+spring:
   servlet:
     multipart:
-      max-file-size: 256MB
+      enabled: true
+      max-file-size: 1024MB
       max-request-size: 1024MB
   mvc:
     throw-exception-if-no-handler-found: true
-  resources:
-    add-mappings: false
+  web:
+    resources:
+      add-mappings: false
   devtools:
     restart:
       log-condition-evaluation-delta: false

+ 8 - 56
bladex-tool/blade-core-cloud/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -16,76 +16,28 @@
 
 
     <dependencies>
-        <!--Blade-->
+        <!-- Blade -->
         <dependency>
             <groupId>org.springblade</groupId>
-            <artifactId>blade-core-context</artifactId>
+            <artifactId>blade-core-launch</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springblade</groupId>
-            <artifactId>blade-starter-auth</artifactId>
-        </dependency>
-        <!--Feign-->
-        <dependency>
-            <groupId>io.github.openfeign</groupId>
-            <artifactId>feign-okhttp</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-openfeign</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>commons-logging</groupId>
-                    <artifactId>commons-logging</artifactId>
-                </exclusion>
-            </exclusions>
+            <artifactId>blade-core-context</artifactId>
         </dependency>
-        <!--Hystrix-->
         <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>commons-logging</groupId>
-                    <artifactId>commons-logging</artifactId>
-                </exclusion>
-            </exclusions>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-auth</artifactId>
         </dependency>
-        <!-- Actuator -->
         <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-loadbalancer</artifactId>
         </dependency>
         <!-- Admin -->
         <dependency>
             <groupId>de.codecentric</groupId>
             <artifactId>spring-boot-admin-starter-client</artifactId>
         </dependency>
-        <!-- Nacos -->
-        <dependency>
-            <groupId>com.alibaba.cloud</groupId>
-            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.alibaba.nacos</groupId>
-                    <artifactId>nacos-client</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>com.alibaba.cloud</groupId>
-            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.alibaba.nacos</groupId>
-                    <artifactId>nacos-client</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>com.alibaba.nacos</groupId>
-            <artifactId>nacos-client</artifactId>
-        </dependency>
         <!-- Sentinel -->
         <dependency>
             <groupId>com.alibaba.cloud</groupId>

+ 2 - 2
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/client/BladeCloudApplication.java

@@ -17,7 +17,7 @@
 package org.springblade.core.cloud.client;
 
 import org.springblade.core.launch.constant.AppConstant;
-import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.openfeign.EnableFeignClients;
 
@@ -33,8 +33,8 @@ import java.lang.annotation.*;
 @Documented
 @Inherited
 @EnableDiscoveryClient
-@EnableCircuitBreaker
 @EnableFeignClients(AppConstant.BASE_PACKAGES)
+@SpringBootApplication
 public @interface BladeCloudApplication {
 
 }

+ 1 - 1
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFallbackFactory.java

@@ -17,7 +17,7 @@
 package org.springblade.core.cloud.feign;
 
 import feign.Target;
-import feign.hystrix.FallbackFactory;
+import org.springframework.cloud.openfeign.FallbackFactory;
 import lombok.AllArgsConstructor;
 import org.springframework.cglib.proxy.Enhancer;
 

+ 42 - 42
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/EnableBladeFeign.java

@@ -31,51 +31,51 @@ import java.lang.annotation.*;
 @Documented
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
-@EnableFeignClients({AppConstant.BASE_PACKAGES, AppConstant.HX_BASE_PACKAGES})
+@EnableFeignClients(AppConstant.BASE_PACKAGES)
 public @interface EnableBladeFeign {
-    /**
-     * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
-     * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
-     * {@code @ComponentScan(basePackages="org.my.pkg")}.
-     *
-     * @return the array of 'basePackages'.
-     */
-    String[] value() default {};
+	/**
+	 * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
+	 * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
+	 * {@code @ComponentScan(basePackages="org.my.pkg")}.
+	 *
+	 * @return the array of 'basePackages'.
+	 */
+	String[] value() default {};
 
-    /**
-     * Base packages to scan for annotated components.
-     * <p>
-     * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
-     * <p>
-     * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
-     * package names.
-     *
-     * @return the array of 'basePackages'.
-     */
-    String[] basePackages() default {};
+	/**
+	 * Base packages to scan for annotated components.
+	 * <p>
+	 * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
+	 * <p>
+	 * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
+	 * package names.
+	 *
+	 * @return the array of 'basePackages'.
+	 */
+	String[] basePackages() default {};
 
-    /**
-     * Type-safe alternative to {@link #basePackages()} for specifying the packages to
-     * scan for annotated components. The package of each class specified will be scanned.
-     * <p>
-     * Consider creating a special no-op marker class or interface in each package that
-     * serves no purpose other than being referenced by this attribute.
-     *
-     * @return the array of 'basePackageClasses'.
-     */
-    Class<?>[] basePackageClasses() default {};
+	/**
+	 * Type-safe alternative to {@link #basePackages()} for specifying the packages to
+	 * scan for annotated components. The package of each class specified will be scanned.
+	 * <p>
+	 * Consider creating a special no-op marker class or interface in each package that
+	 * serves no purpose other than being referenced by this attribute.
+	 *
+	 * @return the array of 'basePackageClasses'.
+	 */
+	Class<?>[] basePackageClasses() default {};
 
-    /**
-     * A custom <code>@Configuration</code> for all feign clients. Can contain override
-     * <code>@Bean</code> definition for the pieces that make up the client, for instance
-     * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
-     */
-    Class<?>[] defaultConfiguration() default {};
+	/**
+	 * A custom <code>@Configuration</code> for all feign clients. Can contain override
+	 * <code>@Bean</code> definition for the pieces that make up the client, for instance
+	 * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
+	 */
+	Class<?>[] defaultConfiguration() default {};
 
-    /**
-     * List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
-     *
-     * @return
-     */
-    Class<?>[] clients() default {};
+	/**
+	 * List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
+	 *
+	 * @return
+	 */
+	Class<?>[] clients() default {};
 }

+ 2 - 2
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpConfiguration.java

@@ -16,15 +16,15 @@
  */
 package org.springblade.core.cloud.http;
 
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
 
 /**
  * http 配置
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @EnableConfigurationProperties(BladeHttpProperties.class)
 public class BladeHttpConfiguration {
 }

+ 4 - 4
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java

@@ -28,6 +28,7 @@ import org.springblade.core.tool.ssl.DisableValidationTrustManager;
 import org.springblade.core.tool.ssl.TrustAllHostNames;
 import org.springblade.core.tool.utils.Charsets;
 import org.springblade.core.tool.utils.Holder;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -35,7 +36,6 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.cloud.client.loadbalancer.LoadBalanced;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.StringHttpMessageConverter;
@@ -58,7 +58,7 @@ import java.util.concurrent.TimeUnit;
  */
 @Slf4j
 @RequiredArgsConstructor
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @ConditionalOnClass(OkHttpClient.class)
 @ConditionalOnProperty(value = "blade.http.enabled", matchIfMissing = true)
 public class RestTemplateConfiguration {
@@ -135,7 +135,7 @@ public class RestTemplateConfiguration {
 		return new RestTemplateHeaderInterceptor();
 	}
 
-	@Configuration(proxyBeanMethods = false)
+	@AutoConfiguration
 	@RequiredArgsConstructor
 	@ConditionalOnClass(OkHttpClient.class)
 	@ConditionalOnProperty(value = "blade.http.rest-template.enable")
@@ -158,7 +158,7 @@ public class RestTemplateConfiguration {
 		}
 	}
 
-	@Configuration(proxyBeanMethods = false)
+	@AutoConfiguration
 	@RequiredArgsConstructor
 	@ConditionalOnClass(OkHttpClient.class)
 	@ConditionalOnProperty(value = "blade.http.lb-rest-template.enable")

+ 0 - 126
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixAutoConfiguration.java

@@ -1,126 +0,0 @@
-/*
- *      Copyright (c) 2018-2028, DreamLu All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *
- *  Redistributions of source code must retain the above copyright notice,
- *  this list of conditions and the following disclaimer.
- *  Redistributions in binary form must reproduce the above copyright
- *  notice, this list of conditions and the following disclaimer in the
- *  documentation and/or other materials provided with the distribution.
- *  Neither the name of the dreamlu.net developer nor the names of its
- *  contributors may be used to endorse or promote products derived from
- *  this software without specific prior written permission.
- *  Author: DreamLu 卢春梦 (596392912@qq.com)
- */
-package org.springblade.core.cloud.hystrix;
-
-import com.netflix.hystrix.Hystrix;
-import com.netflix.hystrix.strategy.HystrixPlugins;
-import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
-import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
-import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
-import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
-import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
-import feign.Contract;
-import feign.Feign;
-import feign.RequestInterceptor;
-import feign.hystrix.HystrixFeign;
-import org.springblade.core.cloud.feign.BladeFeignRequestInterceptor;
-import org.springblade.core.cloud.version.BladeSpringMvcContract;
-import org.springblade.core.tool.convert.EnumToStringConverter;
-import org.springblade.core.tool.convert.StringToEnumConverter;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.cloud.openfeign.BladeFeignClientsRegistrar;
-import org.springframework.cloud.openfeign.BladeHystrixTargeter;
-import org.springframework.cloud.openfeign.EnableFeignClients;
-import org.springframework.cloud.openfeign.Targeter;
-import org.springframework.context.annotation.*;
-import org.springframework.core.convert.ConversionService;
-import org.springframework.core.convert.converter.ConverterRegistry;
-import org.springframework.core.convert.support.DefaultConversionService;
-import org.springframework.lang.Nullable;
-
-import javax.annotation.PostConstruct;
-import java.util.ArrayList;
-
-/**
- * Hystrix 配置
- *
- * @author L.cm
- */
-@Configuration(proxyBeanMethods = false)
-@ConditionalOnClass({Hystrix.class, Feign.class})
-@Import(BladeFeignClientsRegistrar.class)
-@AutoConfigureAfter(EnableFeignClients.class)
-@ConditionalOnProperty(value = "feign.hystrix.enabled")
-public class BladeHystrixAutoConfiguration {
-	@Nullable
-	@Autowired(required = false)
-	private HystrixConcurrencyStrategy existingConcurrencyStrategy;
-
-	@PostConstruct
-	public void init() {
-		// Keeps references of existing Hystrix plugins.
-		HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
-		HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
-		HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
-		HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
-
-		HystrixPlugins.reset();
-
-		// Registers existing plugins excepts the Concurrent Strategy plugin.
-		HystrixConcurrencyStrategy strategy = new BladeHystrixConcurrencyStrategy(existingConcurrencyStrategy);
-		HystrixPlugins.getInstance().registerConcurrencyStrategy(strategy);
-		HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
-		HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
-		HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
-		HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
-	}
-
-	@Bean
-	@ConditionalOnMissingBean(Targeter.class)
-	public BladeHystrixTargeter bladeFeignTargeter() {
-		return new BladeHystrixTargeter();
-	}
-
-	@Bean
-	@Primary
-	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-	public Feign.Builder feignHystrixBuilder(RequestInterceptor requestInterceptor, Contract feignContract) {
-		return HystrixFeign.builder()
-			.contract(feignContract)
-			.decode404()
-			.requestInterceptor(requestInterceptor);
-	}
-
-	@Bean
-	@ConditionalOnMissingBean
-	public RequestInterceptor requestInterceptor() {
-		return new BladeFeignRequestInterceptor();
-	}
-
-	/**
-	 * blade enum 《-》 String 转换配置
-	 *
-	 * @param objectProvider ObjectProvider
-	 * @return SpringMvcContract
-	 */
-	@Bean
-	public Contract feignContract(@Qualifier("mvcConversionService") ObjectProvider<ConversionService> objectProvider) {
-		ConversionService conversionService = objectProvider.getIfAvailable(DefaultConversionService::new);
-		ConverterRegistry converterRegistry = ((ConverterRegistry) conversionService);
-		converterRegistry.addConverter(new EnumToStringConverter());
-		converterRegistry.addConverter(new StringToEnumConverter());
-		return new BladeSpringMvcContract(new ArrayList<>(), conversionService);
-	}
-
-}

+ 0 - 91
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixConcurrencyStrategy.java

@@ -1,91 +0,0 @@
-/*
- *      Copyright (c) 2018-2028, DreamLu All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *
- *  Redistributions of source code must retain the above copyright notice,
- *  this list of conditions and the following disclaimer.
- *  Redistributions in binary form must reproduce the above copyright
- *  notice, this list of conditions and the following disclaimer in the
- *  documentation and/or other materials provided with the distribution.
- *  Neither the name of the dreamlu.net developer nor the names of its
- *  contributors may be used to endorse or promote products derived from
- *  this software without specific prior written permission.
- *  Author: DreamLu 卢春梦 (596392912@qq.com)
- */
-package org.springblade.core.cloud.hystrix;
-
-import com.netflix.hystrix.HystrixThreadPoolKey;
-import com.netflix.hystrix.HystrixThreadPoolProperties;
-import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
-import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
-import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
-import com.netflix.hystrix.strategy.properties.HystrixProperty;
-import lombok.AllArgsConstructor;
-import org.springblade.core.context.BladeCallableWrapper;
-import org.springframework.lang.Nullable;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Hystrix传递ThreaLocal中的一些变量
- *
- * <p>
- * https://github.com/Netflix/Hystrix/issues/92#issuecomment-260548068
- * https://github.com/spring-cloud/spring-cloud-sleuth/issues/39
- * https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/security
- * https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java
- * </p>
- *
- * @author L.cm
- */
-@AllArgsConstructor
-public class BladeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
-	@Nullable
-	private final HystrixConcurrencyStrategy existingConcurrencyStrategy;
-
-	@Override
-	public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
-		return existingConcurrencyStrategy != null
-			? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize)
-			: super.getBlockingQueue(maxQueueSize);
-	}
-
-	@Override
-	public <T> HystrixRequestVariable<T> getRequestVariable(
-		HystrixRequestVariableLifecycle<T> rv) {
-		return existingConcurrencyStrategy != null
-			? existingConcurrencyStrategy.getRequestVariable(rv)
-			: super.getRequestVariable(rv);
-	}
-
-	@Override
-	public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
-											HystrixProperty<Integer> corePoolSize,
-											HystrixProperty<Integer> maximumPoolSize,
-											HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
-											BlockingQueue<Runnable> workQueue) {
-		return existingConcurrencyStrategy != null
-			? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
-			: super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
-	}
-
-	@Override
-	public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) {
-		return existingConcurrencyStrategy != null
-			? existingConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties)
-			: super.getThreadPool(threadPoolKey, threadPoolProperties);
-	}
-
-	@Override
-	public <T> Callable<T> wrapCallable(Callable<T> callable) {
-		Callable<T> wrapCallable = new BladeCallableWrapper<>(callable);
-		return existingConcurrencyStrategy != null
-			? existingConcurrencyStrategy.wrapCallable(wrapCallable)
-			: super.wrapCallable(wrapCallable);
-	}
-}

+ 1 - 1
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeFeignSentinel.java

@@ -20,7 +20,7 @@ import feign.Contract;
 import feign.Feign;
 import feign.InvocationHandlerFactory;
 import feign.Target;
-import feign.hystrix.FallbackFactory;
+import org.springframework.cloud.openfeign.FallbackFactory;
 import lombok.SneakyThrows;
 import org.springblade.core.cloud.feign.BladeFallbackFactory;
 import org.springframework.beans.BeansException;

+ 2 - 4
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelAutoConfiguration.java

@@ -23,11 +23,10 @@ import feign.RequestInterceptor;
 import lombok.AllArgsConstructor;
 import org.springblade.core.cloud.feign.BladeFeignRequestInterceptor;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Primary;
 import org.springframework.context.annotation.Scope;
 
@@ -37,8 +36,7 @@ import org.springframework.context.annotation.Scope;
  * @author Chill
  */
 @AllArgsConstructor
-@Configuration(proxyBeanMethods = false)
-@AutoConfigureBefore(SentinelFeignAutoConfiguration.class)
+@AutoConfiguration(before = SentinelFeignAutoConfiguration.class)
 @ConditionalOnProperty(name = "feign.sentinel.enabled")
 public class BladeSentinelAutoConfiguration {
 

+ 73 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelFilterConfiguration.java

@@ -0,0 +1,73 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.cloud.sentinel;
+
+import com.alibaba.cloud.sentinel.SentinelProperties;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.DefaultBlockExceptionHandler;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.util.StringUtils;
+
+import java.util.Optional;
+
+/**
+ * 处理sentinel2021兼容问题
+ *
+ * @author Chill
+ */
+@RequiredArgsConstructor
+@Import(BladeSentinelFilterConfiguration.class)
+@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
+public class BladeSentinelFilterConfiguration {
+
+	@Bean
+	public SentinelWebInterceptor sentinelWebInterceptor(SentinelWebMvcConfig sentinelWebMvcConfig) {
+		return new SentinelWebInterceptor(sentinelWebMvcConfig);
+	}
+
+	@Bean
+	public SentinelWebMvcConfig sentinelWebMvcConfig(SentinelProperties properties,
+													 Optional<UrlCleaner> urlCleanerOptional, Optional<BlockExceptionHandler> blockExceptionHandlerOptional,
+													 Optional<RequestOriginParser> requestOriginParserOptional) {
+		SentinelWebMvcConfig sentinelWebMvcConfig = new SentinelWebMvcConfig();
+		sentinelWebMvcConfig.setHttpMethodSpecify(properties.getHttpMethodSpecify());
+		sentinelWebMvcConfig.setWebContextUnify(properties.getWebContextUnify());
+
+		if (blockExceptionHandlerOptional.isPresent()) {
+			blockExceptionHandlerOptional.ifPresent(sentinelWebMvcConfig::setBlockExceptionHandler);
+		} else {
+			if (StringUtils.hasText(properties.getBlockPage())) {
+				sentinelWebMvcConfig.setBlockExceptionHandler(
+					((request, response, e) -> response.sendRedirect(properties.getBlockPage())));
+			} else {
+				sentinelWebMvcConfig.setBlockExceptionHandler(new DefaultBlockExceptionHandler());
+			}
+		}
+
+		urlCleanerOptional.ifPresent(sentinelWebMvcConfig::setUrlCleaner);
+		requestOriginParserOptional.ifPresent(sentinelWebMvcConfig::setOriginParser);
+		return sentinelWebMvcConfig;
+	}
+
+}

+ 1 - 1
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelInvocationHandler.java

@@ -27,7 +27,7 @@ import feign.Feign;
 import feign.InvocationHandlerFactory;
 import feign.MethodMetadata;
 import feign.Target;
-import feign.hystrix.FallbackFactory;
+import org.springframework.cloud.openfeign.FallbackFactory;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;

+ 2 - 4
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/server/UndertowHttp2Configuration.java

@@ -17,13 +17,12 @@
 package org.springblade.core.cloud.server;
 
 import io.undertow.Undertow;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
 import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
 import org.springframework.boot.web.server.WebServerFactoryCustomizer;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 
 import static io.undertow.UndertowOptions.ENABLE_HTTP2;
 
@@ -33,9 +32,8 @@ import static io.undertow.UndertowOptions.ENABLE_HTTP2;
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration(before = ServletWebServerFactoryAutoConfiguration.class)
 @ConditionalOnClass(Undertow.class)
-@AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class)
 public class UndertowHttp2Configuration {
 
 	@Bean

+ 2 - 2
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/VersionMappingAutoConfiguration.java

@@ -16,10 +16,10 @@
  */
 package org.springblade.core.cloud.version;
 
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
 import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 
 /**
  * url版本号处理
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @ConditionalOnWebApplication
 public class VersionMappingAutoConfiguration {
 	@Bean

+ 0 - 229
bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeFeignClientsRegistrar.java

@@ -1,229 +0,0 @@
-/*
- *      Copyright (c) 2018-2028, DreamLu All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *
- *  Redistributions of source code must retain the above copyright notice,
- *  this list of conditions and the following disclaimer.
- *  Redistributions in binary form must reproduce the above copyright
- *  notice, this list of conditions and the following disclaimer in the
- *  documentation and/or other materials provided with the distribution.
- *  Neither the name of the dreamlu.net developer nor the names of its
- *  contributors may be used to endorse or promote products derived from
- *  this software without specific prior written permission.
- *  Author: DreamLu 卢春梦 (596392912@qq.com)
- */
-package org.springframework.cloud.openfeign;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springblade.core.cloud.hystrix.BladeHystrixAutoConfiguration;
-import org.springframework.beans.factory.BeanClassLoaderAware;
-import org.springframework.beans.factory.config.BeanDefinitionHolder;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.context.EnvironmentAware;
-import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
-import org.springframework.core.annotation.AnnotatedElementUtils;
-import org.springframework.core.annotation.AnnotationAttributes;
-import org.springframework.core.env.Environment;
-import org.springframework.core.io.support.SpringFactoriesLoader;
-import org.springframework.core.type.AnnotationMetadata;
-import org.springframework.lang.Nullable;
-import org.springframework.util.StringUtils;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * feign 自动配置
- *
- * @author L.cm
- */
-@NoArgsConstructor
-public class BladeFeignClientsRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware, EnvironmentAware {
-	@Getter
-	private ClassLoader beanClassLoader;
-	@Getter
-	private Environment environment;
-
-	@Override
-	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
-		registerFeignClients(metadata, registry);
-	}
-
-	@Override
-	public void setBeanClassLoader(ClassLoader classLoader) {
-		this.beanClassLoader = classLoader;
-	}
-
-	private void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
-		List<String> feignClients = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
-		// 如果 spring.factories 里为空
-		if (feignClients.isEmpty()) {
-			return;
-		}
-		for (String className : feignClients) {
-			try {
-				Class<?> clazz = beanClassLoader.loadClass(className);
-				AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(clazz, FeignClient.class);
-				if (attributes == null) {
-					continue;
-				}
-				// 如果已经存在该 bean,支持原生的 Feign
-				if (registry.containsBeanDefinition(className)) {
-					continue;
-				}
-				registerClientConfiguration(registry, getClientName(attributes), attributes.get("configuration"));
-
-				validate(attributes);
-				BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
-				definition.addPropertyValue("url", getUrl(attributes));
-				definition.addPropertyValue("path", getPath(attributes));
-				String name = getName(attributes);
-				definition.addPropertyValue("name", name);
-
-				// 兼容最新版本的 spring-cloud-openfeign,尚未发布
-				StringBuilder aliasBuilder = new StringBuilder(18);
-				if (attributes.containsKey("contextId")) {
-					String contextId = getContextId(attributes);
-					aliasBuilder.append(contextId);
-					definition.addPropertyValue("contextId", contextId);
-				} else {
-					aliasBuilder.append(name);
-				}
-
-				definition.addPropertyValue("type", className);
-				definition.addPropertyValue("decode404", attributes.get("decode404"));
-				definition.addPropertyValue("fallback", attributes.get("fallback"));
-				definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
-				definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
-
-				AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
-
-				// alias
-				String alias = aliasBuilder.append("FeignClient").toString();
-
-				// has a default, won't be null
-				boolean primary = (Boolean)attributes.get("primary");
-
-				beanDefinition.setPrimary(primary);
-
-				String qualifier = getQualifier(attributes);
-				if (StringUtils.hasText(qualifier)) {
-					alias = qualifier;
-				}
-
-				BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
-				BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
-
-			} catch (ClassNotFoundException e) {
-				e.printStackTrace();
-			}
-		}
-	}
-
-	/**
-	 * Return the class used by {@link SpringFactoriesLoader} to load configuration
-	 * candidates.
-	 * @return the factory class
-	 */
-	private Class<?> getSpringFactoriesLoaderFactoryClass() {
-		return BladeHystrixAutoConfiguration.class;
-	}
-
-	private void validate(Map<String, Object> attributes) {
-		AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
-		FeignClientsRegistrar.validateFallback(annotation.getClass("fallback"));
-		FeignClientsRegistrar.validateFallbackFactory(annotation.getClass("fallbackFactory"));
-	}
-
-	private String getName(Map<String, Object> attributes) {
-		String name = (String) attributes.get("serviceId");
-		if (!StringUtils.hasText(name)) {
-			name = (String) attributes.get("name");
-		}
-		if (!StringUtils.hasText(name)) {
-			name = (String) attributes.get("value");
-		}
-		name = resolve(name);
-		return FeignClientsRegistrar.getName(name);
-	}
-
-	private String getContextId(Map<String, Object> attributes) {
-		String contextId = (String) attributes.get("contextId");
-		if (!StringUtils.hasText(contextId)) {
-			return getName(attributes);
-		}
-
-		contextId = resolve(contextId);
-		return FeignClientsRegistrar.getName(contextId);
-	}
-
-	private String resolve(String value) {
-		if (StringUtils.hasText(value)) {
-			return this.environment.resolvePlaceholders(value);
-		}
-		return value;
-	}
-
-	private String getUrl(Map<String, Object> attributes) {
-		String url = resolve((String) attributes.get("url"));
-		return FeignClientsRegistrar.getUrl(url);
-	}
-
-	private String getPath(Map<String, Object> attributes) {
-		String path = resolve((String) attributes.get("path"));
-		return FeignClientsRegistrar.getPath(path);
-	}
-
-	@Nullable
-	private String getQualifier(@Nullable Map<String, Object> client) {
-		if (client == null) {
-			return null;
-		}
-		String qualifier = (String) client.get("qualifier");
-		if (StringUtils.hasText(qualifier)) {
-			return qualifier;
-		}
-		return null;
-	}
-
-	@Nullable
-	private String getClientName(@Nullable Map<String, Object> client) {
-		if (client == null) {
-			return null;
-		}
-		String value = (String) client.get("contextId");
-		if (!StringUtils.hasText(value)) {
-			value = (String) client.get("value");
-		}
-		if (!StringUtils.hasText(value)) {
-			value = (String) client.get("name");
-		}
-		if (!StringUtils.hasText(value)) {
-			value = (String) client.get("serviceId");
-		}
-		if (StringUtils.hasText(value)) {
-			return value;
-		}
-
-		throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
-	}
-
-	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
-		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
-		builder.addConstructorArgValue(name);
-		builder.addConstructorArgValue(configuration);
-		registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());
-	}
-
-	@Override
-	public void setEnvironment(Environment environment) {
-		this.environment = environment;
-	}
-
-}

+ 0 - 99
bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeHystrixTargeter.java

@@ -1,99 +0,0 @@
-/*
- * Copyright 2013-2018 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.springframework.cloud.openfeign;
-
-import feign.Feign;
-import feign.Target;
-import feign.hystrix.FallbackFactory;
-import feign.hystrix.HystrixFeign;
-import feign.hystrix.SetterFactory;
-import org.springblade.core.cloud.feign.BladeFallbackFactory;
-import org.springframework.lang.Nullable;
-
-/**
- * 添加 blade 默认的 fallbackFactory L.cm 2019.01.19
- *
- * @author L.cm
- * @author Spencer Gibb
- * @author Erik Kringen
- */
-@SuppressWarnings("unchecked")
-public class BladeHystrixTargeter implements Targeter {
-
-	@Override
-	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
-						Target.HardCodedTarget<T> target) {
-		if (!(feign instanceof HystrixFeign.Builder)) {
-			return feign.target(target);
-		}
-		HystrixFeign.Builder builder = (HystrixFeign.Builder) feign;
-		SetterFactory setterFactory = getOptional(factory.getName(), context, SetterFactory.class);
-		if (setterFactory != null) {
-			builder.setterFactory(setterFactory);
-		}
-		Class<?> fallback = factory.getFallback();
-		if (fallback != void.class) {
-			return targetWithFallback(factory.getName(), context, target, builder, fallback);
-		}
-		Class<?> fallbackFactory = factory.getFallbackFactory();
-		if (fallbackFactory != void.class) {
-			return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
-		}
-		// blade 默认的 fallbackFactory
-		BladeFallbackFactory bladeFallbackFactory = new BladeFallbackFactory(target);
-		return (T) builder.target(target, bladeFallbackFactory);
-	}
-
-	private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
-											Target.HardCodedTarget<T> target,
-											HystrixFeign.Builder builder,
-											Class<?> fallbackFactoryClass) {
-		FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
-			getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
-		return builder.target(target, fallbackFactory);
-	}
-
-
-	private <T> T targetWithFallback(String feignClientName, FeignContext context,
-									 Target.HardCodedTarget<T> target,
-									 HystrixFeign.Builder builder, Class<?> fallback) {
-		T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
-		return builder.target(target, fallbackInstance);
-	}
-
-	private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, Class<?> beanType,
-								 Class<T> targetType) {
-		Object fallbackInstance = context.getInstance(feignClientName, beanType);
-		if (fallbackInstance == null) {
-			throw new IllegalStateException(String.format("No " + fallbackMechanism +
-				" instance of type %s found for feign client %s", beanType, feignClientName));
-		}
-
-		if (!targetType.isAssignableFrom(beanType)) {
-			throw new IllegalStateException(String.format(
-				"Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of " +
-					"type %s is not assignable to %s for feign client %s", beanType, targetType, feignClientName));
-		}
-		return (T) fallbackInstance;
-	}
-
-	@Nullable
-	private <T> T getOptional(String feignClientName, FeignContext context, Class<T> beanType) {
-		return context.getInstance(feignClientName, beanType);
-	}
-}

+ 0 - 39
bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/Targeter.java

@@ -1,39 +0,0 @@
-/*
- * Copyright 2013-2016 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.springframework.cloud.openfeign;
-
-import feign.Feign;
-import feign.Target;
-
-/**
- * @author Spencer Gibb
- */
-public interface Targeter {
-	/**
-	 * target
-	 *
-	 * @param factory
-	 * @param feign
-	 * @param context
-	 * @param target
-	 * @param <T>
-	 * @return T
-	 */
-	<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
-				 Target.HardCodedTarget<T> target);
-}

+ 1 - 1
bladex-tool/blade-core-context/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 2 - 2
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeContextAutoConfiguration.java

@@ -21,10 +21,10 @@ import org.springblade.core.context.BladeHttpHeadersGetter;
 import org.springblade.core.context.BladeServletContext;
 import org.springblade.core.context.ServletHttpHeadersGetter;
 import org.springblade.core.context.props.BladeContextProperties;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 
@@ -33,7 +33,7 @@ import org.springframework.core.annotation.Order;
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @Order(Ordered.HIGHEST_PRECEDENCE)
 @EnableConfigurationProperties(BladeContextProperties.class)
 public class BladeContextAutoConfiguration {

+ 2 - 2
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeServletListenerConfiguration.java

@@ -19,17 +19,17 @@ package org.springblade.core.context.config;
 import org.springblade.core.context.BladeHttpHeadersGetter;
 import org.springblade.core.context.listener.BladeServletRequestListener;
 import org.springblade.core.context.props.BladeContextProperties;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
 import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 
 /**
  * Servlet 监听器自动配置
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
 public class BladeServletListenerConfiguration {
 

+ 1 - 1
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/props/BladeContextProperties.java

@@ -61,7 +61,7 @@ public class BladeContextProperties {
 		/**
 		 * 自定义 RestTemplate 和 Feign 透传到下层的 Headers 名称列表
 		 */
-		private List<String> allowed = Arrays.asList("X-Real-IP", "x-forwarded-for", "authorization", "Authorization", TokenConstant.HEADER.toLowerCase(), TokenConstant.HEADER);
+		private List<String> allowed = Arrays.asList("X-Real-IP", "x-forwarded-for", "version", "VERSION", "authorization", "Authorization", TokenConstant.HEADER.toLowerCase(), TokenConstant.HEADER);
 	}
 
 	/**

+ 10 - 1
bladex-tool/blade-core-db/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -50,16 +50,25 @@
         <dependency>
             <groupId>com.oracle</groupId>
             <artifactId>ojdbc7</artifactId>
+            <optional>true</optional>
         </dependency>
         <!-- PostgreSql -->
         <dependency>
             <groupId>org.postgresql</groupId>
             <artifactId>postgresql</artifactId>
+            <optional>true</optional>
         </dependency>
         <!-- SqlServer -->
         <dependency>
             <groupId>com.microsoft.sqlserver</groupId>
             <artifactId>mssql-jdbc</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!-- DaMeng -->
+        <dependency>
+            <groupId>com.dameng</groupId>
+            <artifactId>DmJdbcDriver18</artifactId>
+            <optional>true</optional>
         </dependency>
         <!-- Auto -->
         <dependency>

+ 2 - 2
bladex-tool/blade-core-db/src/main/java/org/springblade/core/db/config/DbConfiguration.java

@@ -17,14 +17,14 @@
 package org.springblade.core.db.config;
 
 import org.springblade.core.launch.props.BladePropertySource;
-import org.springframework.context.annotation.Configuration;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 
 /**
  * 数据源配置类
  *
  * @author Chill
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @BladePropertySource(value = "classpath:/blade-db.yml")
 public class DbConfiguration {
 

+ 5 - 1
bladex-tool/blade-core-launch/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -31,6 +31,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-undertow</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+        </dependency>
         <!-- Auto -->
         <dependency>
             <groupId>org.springblade</groupId>

+ 1 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java

@@ -96,6 +96,7 @@ public class BladeApplication {
 		props.setProperty("blade.is-local", String.valueOf(isLocalDev()));
 		props.setProperty("blade.dev-mode", profile.equals(AppConstant.PROD_CODE) ? "false" : "true");
 		props.setProperty("blade.service.version", AppConstant.APPLICATION_VERSION);
+		props.setProperty("loadbalancer.client.name", appName);
 		Properties defaultProperties = new Properties();
 		defaultProperties.setProperty("spring.main.allow-bean-definition-overriding", "true");
 		defaultProperties.setProperty("spring.sleuth.sampler.percentage", "1.0");

+ 2 - 2
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/StartEventListener.java

@@ -17,8 +17,8 @@
 package org.springblade.core.launch;
 
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.web.context.WebServerInitializedEvent;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.context.event.EventListener;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.env.Environment;
@@ -31,7 +31,7 @@ import org.springframework.util.StringUtils;
  * @author Chill
  */
 @Slf4j
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 public class StartEventListener {
 
 	@Async

+ 2 - 2
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladeLaunchConfiguration.java

@@ -17,7 +17,7 @@
 package org.springblade.core.launch.config;
 
 import lombok.AllArgsConstructor;
-import org.springframework.context.annotation.Configuration;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 
@@ -26,7 +26,7 @@ import org.springframework.core.annotation.Order;
  *
  * @author Chill
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @AllArgsConstructor
 @Order(Ordered.HIGHEST_PRECEDENCE)
 public class BladeLaunchConfiguration {

+ 3 - 3
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladePropertyConfiguration.java

@@ -17,11 +17,11 @@
 
 package org.springblade.core.launch.config;
 
-import org.springblade.core.launch.props.BladePropertySourcePostProcessor;
 import org.springblade.core.launch.props.BladeProperties;
+import org.springblade.core.launch.props.BladePropertySourcePostProcessor;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 
@@ -30,7 +30,7 @@ import org.springframework.core.annotation.Order;
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @Order(Ordered.HIGHEST_PRECEDENCE)
 @EnableConfigurationProperties(BladeProperties.class)
 public class BladePropertyConfiguration {

+ 1 - 6
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java

@@ -26,7 +26,7 @@ public interface AppConstant {
 	/**
 	 * 应用版本
 	 */
-	String APPLICATION_VERSION = "2.8.2.RELEASE";
+	String APPLICATION_VERSION = "3.0.1.RELEASE";
 
 	/**
 	 * 基础包
@@ -34,11 +34,6 @@ public interface AppConstant {
 	String BASE_PACKAGES = "org.springblade";
 
 	/**
-	 * 基础包
-	 */
-	String HX_BASE_PACKAGES = "com.fjhx";
-
-	/**
 	 * 应用名前缀
 	 */
 	String APPLICATION_NAME_PREFIX = "blade-";

+ 2 - 2
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/server/ServerInfo.java

@@ -20,8 +20,8 @@ import lombok.Getter;
 import org.springblade.core.launch.utils.INetUtil;
 import org.springframework.beans.factory.SmartInitializingSingleton;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.web.ServerProperties;
-import org.springframework.context.annotation.Configuration;
 
 /**
  * 服务器信息
@@ -29,7 +29,7 @@ import org.springframework.context.annotation.Configuration;
  * @author Chill
  */
 @Getter
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 public class ServerInfo implements SmartInitializingSingleton {
 	private final ServerProperties serverProperties;
 	private String hostName;

+ 85 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/utils/INetUtil.java

@@ -156,4 +156,89 @@ public class INetUtil {
 			return false;
 		}
 	}
+
+	/**
+	 * 将 ip 转成 InetAddress
+	 *
+	 * @param ip ip
+	 * @return InetAddress
+	 */
+	public static InetAddress getInetAddress(String ip) {
+		try {
+			return InetAddress.getByName(ip);
+		} catch (UnknownHostException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * 判断是否内网 ip
+	 *
+	 * @param ip ip
+	 * @return boolean
+	 */
+	public static boolean isInternalIp(String ip) {
+		return isInternalIp(getInetAddress(ip));
+	}
+
+	/**
+	 * 判断是否内网 ip
+	 *
+	 * @param address InetAddress
+	 * @return boolean
+	 */
+	public static boolean isInternalIp(InetAddress address) {
+		if (isLocalIp(address)) {
+			return true;
+		}
+		return isInternalIp(address.getAddress());
+	}
+
+	/**
+	 * 判断是否本地 ip
+	 *
+	 * @param address InetAddress
+	 * @return boolean
+	 */
+	public static boolean isLocalIp(InetAddress address) {
+		return address.isAnyLocalAddress()
+			|| address.isLoopbackAddress()
+			|| address.isSiteLocalAddress();
+	}
+
+	/**
+	 * 判断是否内网 ip
+	 *
+	 * @param addr ip
+	 * @return boolean
+	 */
+	public static boolean isInternalIp(byte[] addr) {
+		final byte b0 = addr[0];
+		final byte b1 = addr[1];
+		//10.x.x.x/8
+		final byte section1 = 0x0A;
+		//172.16.x.x/12
+		final byte section2 = (byte) 0xAC;
+		final byte section3 = (byte) 0x10;
+		final byte section4 = (byte) 0x1F;
+		//192.168.x.x/16
+		final byte section5 = (byte) 0xC0;
+		final byte section6 = (byte) 0xA8;
+		switch (b0) {
+			case section1:
+				return true;
+			case section2:
+				if (b1 >= section3 && b1 <= section4) {
+					return true;
+				}
+			case section5:
+				if (b1 == section6) {
+					return true;
+				}
+			default:
+				return false;
+		}
+	}
+
+
 }

+ 1 - 1
bladex-tool/blade-core-log4j2/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 1 - 1
bladex-tool/blade-core-secure/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 2 - 4
bladex-tool/blade-core-secure/src/main/java/org/springblade/core/secure/config/RegistryConfiguration.java

@@ -23,10 +23,9 @@ import org.springblade.core.secure.handler.IPermissionHandler;
 import org.springblade.core.secure.handler.ISecureHandler;
 import org.springblade.core.secure.handler.SecureHandlerHandler;
 import org.springblade.core.secure.registry.SecureRegistry;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
 import org.springframework.jdbc.core.JdbcTemplate;
 
@@ -36,9 +35,8 @@ import org.springframework.jdbc.core.JdbcTemplate;
  * @author Chill
  */
 @Order
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration(before = SecureConfiguration.class)
 @AllArgsConstructor
-@AutoConfigureBefore(SecureConfiguration.class)
 public class RegistryConfiguration {
 
 	private final JdbcTemplate jdbcTemplate;

+ 2 - 2
bladex-tool/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java

@@ -27,10 +27,10 @@ import org.springblade.core.secure.props.SignSecure;
 import org.springblade.core.secure.provider.ClientDetailsServiceImpl;
 import org.springblade.core.secure.provider.IClientDetailsService;
 import org.springblade.core.secure.registry.SecureRegistry;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.lang.NonNull;
@@ -46,7 +46,7 @@ import java.util.stream.Collectors;
  * @author Chill
  */
 @Order
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @AllArgsConstructor
 @EnableConfigurationProperties({BladeSecureProperties.class})
 public class SecureConfiguration implements WebMvcConfigurer {

+ 10 - 5
bladex-tool/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java

@@ -90,7 +90,7 @@ public class SecureUtil extends AuthUtil {
 
 		// 校验客户端信息
 		if (!validateClient(clientDetails, clientId, clientSecret)) {
-			throw new SecureException("client authentication failed, please check the header parameters");
+			throw new SecureException("客户端认证失败, 请检查请求头 [Authorization] 信息");
 		}
 
 		SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
@@ -138,7 +138,12 @@ public class SecureUtil extends AuthUtil {
 			String userId = String.valueOf(user.get(TokenConstant.USER_ID));
 			JwtUtil.addAccessToken(tenantId, userId, tokenInfo.getToken(), tokenInfo.getExpire());
 		}
-
+		//Token状态配置, 仅在生成RefreshToken时候执行
+		if (getJwtProperties().getState() && getJwtProperties().getSingle() && TokenConstant.REFRESH_TOKEN.equals(tokenType)) {
+			String tenantId = String.valueOf(user.get(TokenConstant.TENANT_ID));
+			String userId = String.valueOf(user.get(TokenConstant.USER_ID));
+			JwtUtil.addRefreshToken(tenantId, userId, tokenInfo.getToken(), tokenInfo.getExpire());
+		}
 		return tokenInfo;
 	}
 
@@ -166,7 +171,7 @@ public class SecureUtil extends AuthUtil {
 		String header = Objects.requireNonNull(WebUtil.getRequest()).getHeader(SecureConstant.BASIC_HEADER_KEY);
 		header = Func.toStr(header).replace(SecureConstant.BASIC_HEADER_PREFIX_EXT, SecureConstant.BASIC_HEADER_PREFIX);
 		if (!header.startsWith(SecureConstant.BASIC_HEADER_PREFIX)) {
-			throw new SecureException("no client information in request header");
+			throw new SecureException("未获取到请求头[Authorization]的信息");
 		}
 		byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
 
@@ -174,13 +179,13 @@ public class SecureUtil extends AuthUtil {
 		try {
 			decoded = Base64.getDecoder().decode(base64Token);
 		} catch (IllegalArgumentException var7) {
-			throw new RuntimeException("failed to decode basic authentication token");
+			throw new RuntimeException("客户端令牌解析失败");
 		}
 
 		String token = new String(decoded, Charsets.UTF_8_NAME);
 		int index = token.indexOf(StringPool.COLON);
 		if (index == -1) {
-			throw new RuntimeException("invalid basic authentication token");
+			throw new RuntimeException("客户端令牌不合法");
 		} else {
 			return new String[]{token.substring(0, index), token.substring(index + 1)};
 		}

+ 1 - 1
bladex-tool/blade-core-test/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 2 - 0
bladex-tool/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTest.java

@@ -17,6 +17,7 @@
 
 package org.springblade.core.test;
 
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.core.annotation.AliasFor;
 
@@ -32,6 +33,7 @@ import java.lang.annotation.*;
 @Documented
 @Inherited
 @SpringBootTest
+@ExtendWith(BladeSpringExtension.class)
 public @interface BladeBootTest {
 	/**
 	 * 服务名:appName

+ 19 - 12
bladex-tool/blade-core-test/src/main/java/org/springblade/core/test/BladeSpringRunner.java → bladex-tool/blade-core-test/src/main/java/org/springblade/core/test/BladeSpringExtension.java

@@ -18,7 +18,7 @@
 package org.springblade.core.test;
 
 
-import org.junit.runners.model.InitializationError;
+import org.junit.jupiter.api.extension.ExtensionContext;
 import org.springblade.core.launch.BladeApplication;
 import org.springblade.core.launch.constant.AppConstant;
 import org.springblade.core.launch.constant.NacosConstant;
@@ -26,7 +26,8 @@ import org.springblade.core.launch.constant.SentinelConstant;
 import org.springblade.core.launch.service.LauncherService;
 import org.springframework.boot.builder.SpringApplicationBuilder;
 import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.lang.NonNull;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -36,14 +37,16 @@ import java.util.stream.Collectors;
  *
  * @author L.cm
  */
-public class BladeSpringRunner extends SpringJUnit4ClassRunner {
+public class BladeSpringExtension extends SpringExtension {
 
-	public BladeSpringRunner(Class<?> clazz) throws InitializationError {
-		super(clazz);
-		setUpTestClass(clazz);
+	@Override
+	public void beforeAll(@NonNull ExtensionContext context) throws Exception {
+		super.beforeAll(context);
+		setUpTestClass(context);
 	}
 
-	private void setUpTestClass(Class<?> clazz) {
+	private void setUpTestClass(ExtensionContext context) {
+		Class<?> clazz = context.getRequiredTestClass();
 		BladeBootTest bladeBootTest = AnnotationUtils.getAnnotation(clazz, BladeBootTest.class);
 		if (bladeBootTest == null) {
 			throw new BladeBootTestException(String.format("%s must be @BladeBootTest .", clazz));
@@ -61,12 +64,16 @@ public class BladeSpringRunner extends SpringJUnit4ClassRunner {
 		props.setProperty("spring.profiles.active", profile);
 		props.setProperty("info.version", AppConstant.APPLICATION_VERSION);
 		props.setProperty("info.desc", appName);
-		props.setProperty("spring.cloud.nacos.discovery.server-addr", NacosConstant.NACOS_ADDR);
-		props.setProperty("spring.cloud.nacos.config.server-addr", NacosConstant.NACOS_ADDR);
-		props.setProperty("spring.cloud.nacos.config.prefix", NacosConstant.NACOS_CONFIG_PREFIX);
-		props.setProperty("spring.cloud.nacos.config.file-extension", NacosConstant.NACOS_CONFIG_FORMAT);
+		props.setProperty("loadbalancer.client.name", appName);
 		props.setProperty("spring.cloud.sentinel.transport.dashboard", SentinelConstant.SENTINEL_ADDR);
 		props.setProperty("spring.main.allow-bean-definition-overriding", "true");
+		props.setProperty("spring.cloud.nacos.config.shared-configs[0].data-id", NacosConstant.sharedDataId());
+		props.setProperty("spring.cloud.nacos.config.shared-configs[0].group", NacosConstant.NACOS_CONFIG_GROUP);
+		props.setProperty("spring.cloud.nacos.config.shared-configs[0].refresh", NacosConstant.NACOS_CONFIG_REFRESH);
+		props.setProperty("spring.cloud.nacos.config.file-extension", NacosConstant.NACOS_CONFIG_FORMAT);
+		props.setProperty("spring.cloud.nacos.config.shared-configs[1].data-id", NacosConstant.sharedDataId(profile));
+		props.setProperty("spring.cloud.nacos.config.shared-configs[1].group", NacosConstant.NACOS_CONFIG_GROUP);
+		props.setProperty("spring.cloud.nacos.config.shared-configs[1].refresh", NacosConstant.NACOS_CONFIG_REFRESH);
 		// 加载自定义组件
 		if (bladeBootTest.enableLoader()) {
 			SpringApplicationBuilder builder = new SpringApplicationBuilder(clazz);
@@ -75,7 +82,7 @@ public class BladeSpringRunner extends SpringJUnit4ClassRunner {
 			launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList())
 				.forEach(launcherService -> launcherService.launcher(builder, appName, profile, isLocalDev));
 		}
-		System.err.println(String.format("---[junit.test]:[%s]---启动中,读取到的环境变量:[%s]", appName, profile));
+		System.err.printf("---[junit.test]:[%s]---启动中,读取到的环境变量:[%s]%n", appName, profile);
 	}
 
 }

+ 1 - 1
bladex-tool/blade-core-tool/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.springblade</groupId>
         <artifactId>BladeX-Tool</artifactId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 199 - 272
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/api/R.java

@@ -1,21 +1,30 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
 package org.springblade.core.tool.api;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.ToString;
+import lombok.*;
 import org.springblade.core.tool.constant.BladeConstant;
 import org.springblade.core.tool.utils.ObjectUtil;
 import org.springframework.lang.Nullable;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 
 /**
@@ -30,269 +39,187 @@ import java.util.Optional;
 @NoArgsConstructor
 public class R<T> implements Serializable {
 
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "状态码", required = true)
-    private int code;
-
-    @ApiModelProperty(value = "是否成功", required = true)
-    private boolean success;
-
-    @ApiModelProperty(value = "返回消息", required = true)
-    private String msg;
-
-    @ApiModelProperty(value = "承载数据")
-    private T data;
-
-    private R(IResultCode resultCode) {
-        this(resultCode, null, resultCode.getMessage());
-    }
-
-    private R(IResultCode resultCode, String msg) {
-        this(resultCode, null, msg);
-    }
-
-    private R(IResultCode resultCode, T data) {
-        this(resultCode, data, resultCode.getMessage());
-    }
-
-    private R(IResultCode resultCode, T data, String msg) {
-        this(resultCode.getCode(), data, msg);
-    }
-
-    private R(int code, T data, String msg) {
-        this.code = code;
-        this.msg = msg;
-        this.success = ResultCode.SUCCESS.code == code;
-        this.data = data;
-    }
-
-    /**
-     * 返回统一列表报文
-     *
-     * @param data 数据
-     * @return R
-     */
-    public static <T> R<T> list(List<?> data) {
-        Map<String, List<?>> map = new HashMap<>();
-        map.put("list", Optional.ofNullable(data).orElse(new ArrayList<>()));
-        return success(map);
-    }
-
-    /**
-     * 返回统一数字报文
-     *
-     * @param count 数据
-     * @return R
-     */
-    public static <T> R<T> count(Integer count) {
-        Map<String, Integer> map = new HashMap<>();
-        map.put("count", Optional.ofNullable(count).orElse(0));
-        return success(map);
-    }
-
-    /**
-     * 返回统一数字报文
-     *
-     * @param count 数据
-     * @return R
-     */
-    public static <T> R<T> count(Long count) {
-        Map<String, Long> map = new HashMap<>();
-        map.put("count", Optional.ofNullable(count).orElse(0L));
-        return success(map);
-    }
-
-    /**
-     * 返回统一详情报文
-     *
-     * @param data 数据
-     * @return R
-     */
-    public static <T> R<T> details(Object data) {
-        Map<String, Object> map = new HashMap<>();
-        map.put("details", Optional.ofNullable(data).orElse(new HashMap<>()));
-        return success(map);
-    }
-
-    /**
-     * 返回统一id报文
-     *
-     * @param id 数据id
-     * @return R
-     */
-    public static <T> R<T> id(Object id) {
-        Map<String, Object> map = new HashMap<>();
-        map.put("id", Optional.ofNullable(id).orElse(""));
-        return success(map);
-    }
-
-    /**
-     * 判断返回是否为成功
-     *
-     * @param result Result
-     * @return 是否成功
-     */
-    public static boolean isSuccess(@Nullable R<?> result) {
-        return Optional.ofNullable(result)
-                .map(x -> ObjectUtil.nullSafeEquals(ResultCode.SUCCESS.code, x.code))
-                .orElse(Boolean.FALSE);
-    }
-
-    /**
-     * 判断返回是否为成功
-     *
-     * @param result Result
-     * @return 是否成功
-     */
-    public static boolean isNotSuccess(@Nullable R<?> result) {
-        return !R.isSuccess(result);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param data 数据
-     * @param <T>  T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> data(T data) {
-        return data(data, BladeConstant.DEFAULT_SUCCESS_MESSAGE);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param data 数据
-     * @param msg  消息
-     * @param <T>  T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> data(T data, String msg) {
-        return data(HttpServletResponse.SC_OK, data, msg);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param code 状态码
-     * @param data 数据
-     * @param msg  消息
-     * @param <T>  T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> data(int code, T data, String msg) {
-        return new R<>(code, data, data == null ? BladeConstant.DEFAULT_NULL_MESSAGE : msg);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param <T> T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> success() {
-        return new R<>(ResultCode.SUCCESS, BladeConstant.DEFAULT_SUCCESS_MESSAGE);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param <T> T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> success(Object data) {
-        return new R(ResultCode.SUCCESS, data, BladeConstant.DEFAULT_SUCCESS_MESSAGE);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param msg 消息
-     * @param <T> T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> success(String msg) {
-        return new R<>(ResultCode.SUCCESS, msg);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param resultCode 业务代码
-     * @param <T>        T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> success(IResultCode resultCode) {
-        return new R<>(resultCode);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param resultCode 业务代码
-     * @param msg        消息
-     * @param <T>        T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> success(IResultCode resultCode, String msg) {
-        return new R<>(resultCode, msg);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param msg 消息
-     * @param <T> T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> fail(String msg) {
-        return new R<>(ResultCode.FAILURE, msg);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param code 状态码
-     * @param msg  消息
-     * @param <T>  T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> fail(int code, String msg) {
-        return new R<>(code, null, msg);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param resultCode 业务代码
-     * @param <T>        T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> fail(IResultCode resultCode) {
-        return new R<>(resultCode);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param resultCode 业务代码
-     * @param msg        消息
-     * @param <T>        T 泛型标记
-     * @return R
-     */
-    public static <T> R<T> fail(IResultCode resultCode, String msg) {
-        return new R<>(resultCode, msg);
-    }
-
-    /**
-     * 返回R
-     *
-     * @param flag 成功状态
-     * @return R
-     */
-    public static <T> R<T> status(boolean flag) {
-        return flag ? success(BladeConstant.DEFAULT_SUCCESS_MESSAGE) : fail(BladeConstant.DEFAULT_FAILURE_MESSAGE);
-    }
+	private static final long serialVersionUID = 1L;
+
+	@ApiModelProperty(value = "状态码", required = true)
+	private int code;
+	@ApiModelProperty(value = "是否成功", required = true)
+	private boolean success;
+	@ApiModelProperty(value = "承载数据")
+	private T data;
+	@ApiModelProperty(value = "返回消息", required = true)
+	private String msg;
+
+	private R(IResultCode resultCode) {
+		this(resultCode, null, resultCode.getMessage());
+	}
+
+	private R(IResultCode resultCode, String msg) {
+		this(resultCode, null, msg);
+	}
+
+	private R(IResultCode resultCode, T data) {
+		this(resultCode, data, resultCode.getMessage());
+	}
+
+	private R(IResultCode resultCode, T data, String msg) {
+		this(resultCode.getCode(), data, msg);
+	}
+
+	private R(int code, T data, String msg) {
+		this.code = code;
+		this.data = data;
+		this.msg = msg;
+		this.success = ResultCode.SUCCESS.code == code;
+	}
+
+	/**
+	 * 判断返回是否为成功
+	 *
+	 * @param result Result
+	 * @return 是否成功
+	 */
+	public static boolean isSuccess(@Nullable R<?> result) {
+		return Optional.ofNullable(result)
+			.map(x -> ObjectUtil.nullSafeEquals(ResultCode.SUCCESS.code, x.code))
+			.orElse(Boolean.FALSE);
+	}
+
+	/**
+	 * 判断返回是否为成功
+	 *
+	 * @param result Result
+	 * @return 是否成功
+	 */
+	public static boolean isNotSuccess(@Nullable R<?> result) {
+		return !R.isSuccess(result);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param data 数据
+	 * @param <T>  T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> data(T data) {
+		return data(data, BladeConstant.DEFAULT_SUCCESS_MESSAGE);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param data 数据
+	 * @param msg  消息
+	 * @param <T>  T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> data(T data, String msg) {
+		return data(HttpServletResponse.SC_OK, data, msg);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param code 状态码
+	 * @param data 数据
+	 * @param msg  消息
+	 * @param <T>  T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> data(int code, T data, String msg) {
+		return new R<>(code, data, data == null ? BladeConstant.DEFAULT_NULL_MESSAGE : msg);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param msg 消息
+	 * @param <T> T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> success(String msg) {
+		return new R<>(ResultCode.SUCCESS, msg);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param resultCode 业务代码
+	 * @param <T>        T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> success(IResultCode resultCode) {
+		return new R<>(resultCode);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param resultCode 业务代码
+	 * @param msg        消息
+	 * @param <T>        T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> success(IResultCode resultCode, String msg) {
+		return new R<>(resultCode, msg);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param msg 消息
+	 * @param <T> T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> fail(String msg) {
+		return new R<>(ResultCode.FAILURE, msg);
+	}
+
+
+	/**
+	 * 返回R
+	 *
+	 * @param code 状态码
+	 * @param msg  消息
+	 * @param <T>  T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> fail(int code, String msg) {
+		return new R<>(code, null, msg);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param resultCode 业务代码
+	 * @param <T>        T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> fail(IResultCode resultCode) {
+		return new R<>(resultCode);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param resultCode 业务代码
+	 * @param msg        消息
+	 * @param <T>        T 泛型标记
+	 * @return R
+	 */
+	public static <T> R<T> fail(IResultCode resultCode, String msg) {
+		return new R<>(resultCode, msg);
+	}
+
+	/**
+	 * 返回R
+	 *
+	 * @param flag 成功状态
+	 * @return R
+	 */
+	public static <T> R<T> status(boolean flag) {
+		return flag ? success(BladeConstant.DEFAULT_SUCCESS_MESSAGE) : fail(BladeConstant.DEFAULT_FAILURE_MESSAGE);
+	}
 
 }

+ 2 - 2
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/BladeConverterConfiguration.java

@@ -2,7 +2,7 @@ package org.springblade.core.tool.config;
 
 import org.springblade.core.tool.convert.EnumToStringConverter;
 import org.springblade.core.tool.convert.StringToEnumConverter;
-import org.springframework.context.annotation.Configuration;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.format.FormatterRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@@ -11,7 +11,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 public class BladeConverterConfiguration implements WebMvcConfigurer {
 
 	@Override

+ 2 - 4
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/JacksonConfiguration.java

@@ -23,13 +23,12 @@ import com.fasterxml.jackson.databind.SerializationFeature;
 import org.springblade.core.tool.jackson.BladeJacksonProperties;
 import org.springblade.core.tool.jackson.BladeJavaTimeModule;
 import org.springblade.core.tool.utils.DateUtil;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
 import java.text.SimpleDateFormat;
@@ -42,9 +41,8 @@ import java.util.TimeZone;
  *
  * @author Chill
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration(before = JacksonAutoConfiguration.class)
 @ConditionalOnClass(ObjectMapper.class)
-@AutoConfigureBefore(JacksonAutoConfiguration.class)
 @EnableConfigurationProperties(BladeJacksonProperties.class)
 public class JacksonConfiguration {
 

+ 2 - 2
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/MessageConfiguration.java

@@ -22,7 +22,7 @@ import lombok.AllArgsConstructor;
 import org.springblade.core.tool.jackson.BladeJacksonProperties;
 import org.springblade.core.tool.jackson.MappingApiJackson2HttpMessageConverter;
 import org.springblade.core.tool.utils.DateUtil;
-import org.springframework.context.annotation.Configuration;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 import org.springframework.format.FormatterRegistry;
@@ -39,7 +39,7 @@ import java.util.List;
  *
  * @author Chill
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @AllArgsConstructor
 @Order(Ordered.HIGHEST_PRECEDENCE)
 public class MessageConfiguration implements WebMvcConfigurer {

+ 2 - 2
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/config/ToolConfiguration.java

@@ -19,9 +19,9 @@ package org.springblade.core.tool.config;
 
 import org.springblade.core.tool.support.BinderSupplier;
 import org.springblade.core.tool.utils.SpringUtil;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 
 import java.util.function.Supplier;
 
@@ -30,7 +30,7 @@ import java.util.function.Supplier;
  *
  * @author Chill
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 public class ToolConfiguration {
 
 	/**

+ 1 - 1
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/AbstractReadWriteJackson2HttpMessageConverter.java

@@ -68,7 +68,7 @@ public abstract class AbstractReadWriteJackson2HttpMessageConverter extends Abst
 			return false;
 		}
 		AtomicReference<Throwable> causeRef = new AtomicReference<>();
-		if (this.objectMapper.canSerialize(clazz, causeRef)) {
+		if (this.defaultObjectMapper.canSerialize(clazz, causeRef)) {
 			return true;
 		}
 		logWarningIfNecessary(clazz, causeRef.get());

+ 28 - 0
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/JsonUtil.java

@@ -521,6 +521,34 @@ public class JsonUtil {
 	}
 
 	/**
+	 * 读取集合
+	 *
+	 * @param content bytes
+	 * @return 集合
+	 */
+	public static Map<String, Object> readMap(@Nullable String content) {
+		return readMap(content, String.class, Object.class);
+	}
+
+	/**
+	 * 读取集合
+	 *
+	 * @param content bytes
+	 * @return 集合
+	 */
+	public static List<Map<String, Object>> readListMap(@Nullable String content) {
+		if (ObjectUtil.isEmpty(content)) {
+			return Collections.emptyList();
+		}
+		try {
+			return getInstance().readValue(content, new TypeReference<List<Map<String, Object>>>() {
+			});
+		} catch (IOException e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+
+	/**
 	 * jackson 的类型转换
 	 *
 	 * @param fromValue   来源对象

+ 20 - 2
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/node/TreeNode.java

@@ -19,7 +19,9 @@ package org.springblade.core.tool.node;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import lombok.Data;
-import lombok.EqualsAndHashCode;
+import org.springblade.core.tool.utils.Func;
+
+import java.util.Objects;
 
 /**
  * 树型节点类
@@ -27,7 +29,6 @@ import lombok.EqualsAndHashCode;
  * @author smallchill
  */
 @Data
-@EqualsAndHashCode(callSuper = false)
 public class TreeNode extends BaseNode<TreeNode> {
 
 	private static final long serialVersionUID = 1L;
@@ -40,4 +41,21 @@ public class TreeNode extends BaseNode<TreeNode> {
 	@JsonSerialize(using = ToStringSerializer.class)
 	private Long value;
 
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null) {
+			return false;
+		}
+		TreeNode other = (TreeNode) obj;
+		return Func.equals(this.getId(), other.getId());
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(id, parentId);
+	}
+
 }

+ 0 - 130
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/utils/SpringUtils.java

@@ -1,130 +0,0 @@
-package org.springblade.core.tool.utils;
-
-import org.springframework.aop.framework.AopContext;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.stereotype.Component;
-
-/**
- * spring工具类 方便在非spring管理环境中获取bean
- */
-@Component
-public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
-
-    /**
-     * Spring应用上下文环境
-     */
-    private static ConfigurableListableBeanFactory beanFactory;
-
-    private static ApplicationContext applicationContext;
-
-    @Override
-    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
-        SpringUtils.beanFactory = beanFactory;
-    }
-
-    @Override
-    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        SpringUtils.applicationContext = applicationContext;
-    }
-
-    /**
-     * 获取对象
-     *
-     * @param name
-     * @return Object 一个以所给名字注册的bean的实例
-     * @throws BeansException
-     */
-    @SuppressWarnings("unchecked")
-    public static <T> T getBean(String name) throws BeansException {
-        return (T) beanFactory.getBean(name);
-    }
-
-    /**
-     * 获取类型为requiredType的对象
-     *
-     * @param clz
-     * @return
-     * @throws BeansException
-     */
-    public static <T> T getBean(Class<T> clz) throws BeansException {
-        T result = (T) beanFactory.getBean(clz);
-        return result;
-    }
-
-    /**
-     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
-     *
-     * @param name
-     * @return boolean
-     */
-    public static boolean containsBean(String name) {
-        return beanFactory.containsBean(name);
-    }
-
-    /**
-     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
-     *
-     * @param name
-     * @return boolean
-     * @throws NoSuchBeanDefinitionException
-     */
-    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
-        return beanFactory.isSingleton(name);
-    }
-
-    /**
-     * @param name
-     * @return Class 注册对象的类型
-     * @throws NoSuchBeanDefinitionException
-     */
-    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
-        return beanFactory.getType(name);
-    }
-
-    /**
-     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
-     *
-     * @param name
-     * @return
-     * @throws NoSuchBeanDefinitionException
-     */
-    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
-        return beanFactory.getAliases(name);
-    }
-
-    /**
-     * 获取aop代理对象
-     *
-     * @param invoker
-     * @return
-     */
-    @SuppressWarnings("unchecked")
-    public static <T> T getAopProxy(T invoker) {
-        return (T) AopContext.currentProxy();
-    }
-
-    /**
-     * 获取当前的环境配置,无配置返回null
-     *
-     * @return 当前的环境配置
-     */
-    public static String[] getActiveProfiles() {
-        return applicationContext.getEnvironment().getActiveProfiles();
-    }
-
-//    /**
-//     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
-//     *
-//     * @return 当前的环境配置
-//     */
-//    public static String getActiveProfile() {
-//        final String[] activeProfiles = getActiveProfiles();
-//        return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
-//    }
-
-}

+ 12 - 0
bladex-tool/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java

@@ -1489,6 +1489,9 @@ public class StringUtil extends org.springframework.util.StringUtils {
 	 * @return {String}
 	 */
 	public static String underlineToHump(String para) {
+		if (isBlank(para)) {
+			return StringPool.EMPTY;
+		}
 		StringBuilder result = new StringBuilder();
 		String[] a = para.split("_");
 		for (String s : a) {
@@ -1509,6 +1512,9 @@ public class StringUtil extends org.springframework.util.StringUtils {
 	 * @return {String}
 	 */
 	public static String humpToUnderline(String para) {
+		if (isBlank(para)) {
+			return StringPool.EMPTY;
+		}
 		para = firstCharToLower(para);
 		StringBuilder sb = new StringBuilder(para);
 		int temp = 0;
@@ -1528,6 +1534,9 @@ public class StringUtil extends org.springframework.util.StringUtils {
 	 * @return {String}
 	 */
 	public static String lineToHump(String para) {
+		if (isBlank(para)) {
+			return StringPool.EMPTY;
+		}
 		StringBuilder result = new StringBuilder();
 		String[] a = para.split("-");
 		for (String s : a) {
@@ -1548,6 +1557,9 @@ public class StringUtil extends org.springframework.util.StringUtils {
 	 * @return {String}
 	 */
 	public static String humpToLine(String para) {
+		if (isBlank(para)) {
+			return StringPool.EMPTY;
+		}
 		para = firstCharToLower(para);
 		StringBuilder sb = new StringBuilder(para);
 		int temp = 0;

+ 34 - 0
bladex-tool/blade-starter-actuate/README.md

@@ -0,0 +1,34 @@
+## 想法
+暴露一些端点,提供一些功能。
+
+1. http-cache
+
+2. RateLimiter
+
+3. ... ...
+
+### 不是用网关,单体应用
+拦截器处理,基于 redis 的 cache 时间或者 RateLimiter处理。
+
+结构:serviceName:http-cache:/user/1?queryString If-Modified-Since
+结构:serviceName:RateLimiter:/user/1 99
+
+### 使用网关
+将端点信息存储到 redis 里,供 网关使用。
+结构:serviceName:http-cache:endpoint:/user/{id}  100s
+
+结构:serviceName:RateLimiter:endpoint:/user/{id} 100/s
+
+## RateLimiter Headers
+```text
+#=============================#===================================================#
+# HTTP Header                 # Description                                       #
+#=============================#===================================================#
+| X-RateLimit-Limit           | Request limit per day / per 5 minutes             |
++-----------------------------+---------------------------------------------------+
+| X-RateLimit-Remaining       | The number of requests left for the time window   |
++-----------------------------+---------------------------------------------------+
+| X-RateLimit-Reset           | The remaining window before the rate limit resets |
+|                             |  in UTC epoch seconds                             |
++-----------------------------+---------------------------------------------------+
+```

+ 1 - 1
bladex-tool/blade-starter-actuate/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 2 - 2
bladex-tool/blade-starter-actuate/src/main/java/org/springblade/core/http/cache/HttpCacheConfiguration.java

@@ -18,12 +18,12 @@
 package org.springblade.core.http.cache;
 
 import lombok.AllArgsConstructor;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.cache.CacheManager;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.lang.NonNull;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -36,7 +36,7 @@ import java.util.Set;
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @AllArgsConstructor
 @EnableConfigurationProperties(BladeHttpCacheProperties.class)
 @ConditionalOnProperty(value = "blade.http.cache.enabled", havingValue = "true")

+ 4 - 0
bladex-tool/blade-starter-api-crypto/README.md

@@ -0,0 +1,4 @@
+## 参考
+encrypt-body-spring-boot-starter: https://github.com/Licoy/encrypt-body-spring-boot-starter
+
+mica:https://github.com/lets-mica/mica

+ 1 - 1
bladex-tool/blade-starter-api-crypto/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 2 - 2
bladex-tool/blade-starter-api-crypto/src/main/java/org/springblade/core/api/crypto/config/ApiCryptoConfiguration.java

@@ -2,9 +2,9 @@ package org.springblade.core.api.crypto.config;
 
 import lombok.RequiredArgsConstructor;
 import org.springblade.core.api.crypto.core.ApiDecryptParamResolver;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@@ -15,7 +15,7 @@ import java.util.List;
  *
  * @author L.cm
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @RequiredArgsConstructor
 @EnableConfigurationProperties(ApiCryptoProperties.class)
 @ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)

+ 2 - 2
bladex-tool/blade-starter-api-crypto/src/main/java/org/springblade/core/api/crypto/core/ApiDecryptRequestBodyAdvice.java

@@ -9,8 +9,8 @@ import org.springblade.core.api.crypto.config.ApiCryptoProperties;
 import org.springblade.core.api.crypto.exception.DecryptBodyFailException;
 import org.springblade.core.api.crypto.util.ApiCryptoUtil;
 import org.springblade.core.tool.utils.ClassUtil;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.MethodParameter;
 import org.springframework.core.annotation.Order;
 import org.springframework.http.HttpInputMessage;
@@ -35,7 +35,7 @@ import java.lang.reflect.Type;
  */
 @Slf4j
 @Order(1)
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @ControllerAdvice
 @RequiredArgsConstructor
 @ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)

+ 2 - 2
bladex-tool/blade-starter-api-crypto/src/main/java/org/springblade/core/api/crypto/core/ApiEncryptResponseBodyAdvice.java

@@ -9,8 +9,8 @@ import org.springblade.core.api.crypto.exception.EncryptBodyFailException;
 import org.springblade.core.api.crypto.util.ApiCryptoUtil;
 import org.springblade.core.tool.jackson.JsonUtil;
 import org.springblade.core.tool.utils.ClassUtil;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.core.MethodParameter;
 import org.springframework.core.annotation.Order;
 import org.springframework.http.MediaType;
@@ -33,7 +33,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  */
 @Slf4j
 @Order(1)
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @ControllerAdvice
 @RequiredArgsConstructor
 @ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)

+ 1 - 1
bladex-tool/blade-starter-auth/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 402 - 410
bladex-tool/blade-starter-auth/src/main/java/org/springblade/core/secure/utils/AuthUtil.java

@@ -19,10 +19,8 @@ package org.springblade.core.secure.utils;
 import io.jsonwebtoken.Claims;
 import org.springblade.core.jwt.JwtUtil;
 import org.springblade.core.jwt.props.JwtProperties;
-import org.springblade.core.launch.BladeApplication;
 import org.springblade.core.launch.constant.TokenConstant;
 import org.springblade.core.secure.BladeUser;
-import org.springblade.core.tool.constant.BladeConstant;
 import org.springblade.core.tool.constant.RoleConstant;
 import org.springblade.core.tool.support.Kv;
 import org.springblade.core.tool.utils.*;
@@ -37,413 +35,407 @@ import java.util.Objects;
  * @author Chill
  */
 public class AuthUtil {
-    private static final String BLADE_USER_REQUEST_ATTR = "_BLADE_USER_REQUEST_ATTR_";
-
-    private final static String HEADER = TokenConstant.HEADER;
-    private final static String ACCOUNT = TokenConstant.ACCOUNT;
-    private final static String USER_NAME = TokenConstant.USER_NAME;
-    private final static String NICK_NAME = TokenConstant.NICK_NAME;
-    private final static String USER_ID = TokenConstant.USER_ID;
-    private final static String DEPT_ID = TokenConstant.DEPT_ID;
-    private final static String POST_ID = TokenConstant.POST_ID;
-    private final static String ROLE_ID = TokenConstant.ROLE_ID;
-    private final static String ROLE_NAME = TokenConstant.ROLE_NAME;
-    private final static String TENANT_ID = TokenConstant.TENANT_ID;
-    private final static String OAUTH_ID = TokenConstant.OAUTH_ID;
-    private final static String CLIENT_ID = TokenConstant.CLIENT_ID;
-    private final static String DETAIL = TokenConstant.DETAIL;
-
-    private static JwtProperties jwtProperties;
-
-    /**
-     * 获取配置类
-     *
-     * @return jwtProperties
-     */
-    private static JwtProperties getJwtProperties() {
-        if (jwtProperties == null) {
-            jwtProperties = SpringUtil.getBean(JwtProperties.class);
-        }
-        return jwtProperties;
-    }
-
-    /**
-     * 获取用户信息
-     *
-     * @return BladeUser
-     */
-    public static BladeUser getUser() {
-        HttpServletRequest request = WebUtil.getRequest();
-        if (request == null) {
-            return null;
-        }
-        // 优先从 request 中获取
-        Object bladeUser = request.getAttribute(BLADE_USER_REQUEST_ATTR);
-        if (bladeUser == null) {
-            bladeUser = getUser(request);
-            if (bladeUser != null) {
-                // 设置到 request 中
-                request.setAttribute(BLADE_USER_REQUEST_ATTR, bladeUser);
-            }
-        }
-        return (BladeUser) bladeUser;
-    }
-
-    /**
-     * 获取用户信息
-     *
-     * @param request request
-     * @return BladeUser
-     */
-    @SuppressWarnings("unchecked")
-    public static BladeUser getUser(HttpServletRequest request) {
-        Claims claims = getClaims(request);
-        if (claims == null) {
-            return null;
-        }
-        String clientId = Func.toStr(claims.get(AuthUtil.CLIENT_ID));
-        Long userId = Func.toLong(claims.get(AuthUtil.USER_ID));
-        String tenantId = Func.toStr(claims.get(AuthUtil.TENANT_ID));
-        String oauthId = Func.toStr(claims.get(AuthUtil.OAUTH_ID));
-        String deptId = Func.toStrWithEmpty(claims.get(AuthUtil.DEPT_ID), StringPool.MINUS_ONE);
-        String postId = Func.toStrWithEmpty(claims.get(AuthUtil.POST_ID), StringPool.MINUS_ONE);
-        String roleId = Func.toStrWithEmpty(claims.get(AuthUtil.ROLE_ID), StringPool.MINUS_ONE);
-        String account = Func.toStr(claims.get(AuthUtil.ACCOUNT));
-        String roleName = Func.toStr(claims.get(AuthUtil.ROLE_NAME));
-        String userName = Func.toStr(claims.get(AuthUtil.USER_NAME));
-        String nickName = Func.toStr(claims.get(AuthUtil.NICK_NAME));
-        Kv detail = Kv.create().setAll((Map<? extends String, ?>) claims.get(AuthUtil.DETAIL));
-        BladeUser bladeUser = new BladeUser();
-        bladeUser.setClientId(clientId);
-        bladeUser.setUserId(userId);
-        bladeUser.setTenantId(tenantId);
-        bladeUser.setOauthId(oauthId);
-        bladeUser.setAccount(account);
-        bladeUser.setDeptId(deptId);
-        bladeUser.setPostId(postId);
-        bladeUser.setRoleId(roleId);
-        bladeUser.setRoleName(roleName);
-        bladeUser.setUserName(userName);
-        bladeUser.setNickName(nickName);
-        bladeUser.setDetail(detail);
-        return bladeUser;
-    }
-
-    /**
-     * 是否为超管
-     *
-     * @return boolean
-     */
-    public static boolean isAdministrator() {
-        return StringUtil.containsAny(getUserRole(), RoleConstant.ADMINISTRATOR);
-    }
-
-    /**
-     * 是否为管理员
-     *
-     * @return boolean
-     */
-    public static boolean isAdmin() {
-        return StringUtil.containsAny(getUserRole(), RoleConstant.ADMIN);
-    }
-
-    /**
-     * 获取用户id
-     *
-     * @return userId
-     */
-    public static Long getUserId() {
-        BladeUser user = getUser();
-        return (null == user) ? -1 : user.getUserId();
-    }
-
-    /**
-     * 获取用户id
-     *
-     * @param request request
-     * @return userId
-     */
-    public static Long getUserId(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? -1 : user.getUserId();
-    }
-
-    /**
-     * 获取用户账号
-     *
-     * @return userAccount
-     */
-    public static String getUserAccount() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getAccount();
-    }
-
-    /**
-     * 获取用户账号
-     *
-     * @param request request
-     * @return userAccount
-     */
-    public static String getUserAccount(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getAccount();
-    }
-
-    /**
-     * 获取用户名
-     *
-     * @return userName
-     */
-    public static String getUserName() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getUserName();
-    }
-
-    /**
-     * 获取用户名
-     *
-     * @param request request
-     * @return userName
-     */
-    public static String getUserName(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getUserName();
-    }
-
-    /**
-     * 获取昵称
-     *
-     * @return userName
-     */
-    public static String getNickName() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getNickName();
-    }
-
-    /**
-     * 获取昵称
-     *
-     * @param request request
-     * @return userName
-     */
-    public static String getNickName(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getNickName();
-    }
-
-    /**
-     * 获取用户部门
-     *
-     * @return userName
-     */
-    public static String getDeptId() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getDeptId();
-    }
-
-    /**
-     * 获取用户部门
-     *
-     * @param request request
-     * @return userName
-     */
-    public static String getDeptId(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getDeptId();
-    }
-
-    /**
-     * 获取用户岗位
-     *
-     * @return userName
-     */
-    public static String getPostId() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getPostId();
-    }
-
-    /**
-     * 获取用户岗位
-     *
-     * @param request request
-     * @return userName
-     */
-    public static String getPostId(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getPostId();
-    }
-
-    /**
-     * 获取用户角色
-     *
-     * @return userName
-     */
-    public static String getUserRole() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getRoleName();
-    }
-
-    /**
-     * 获取用角色
-     *
-     * @param request request
-     * @return userName
-     */
-    public static String getUserRole(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getRoleName();
-    }
-
-    /**
-     * 获取租户ID
-     *
-     * @return tenantId
-     */
-    public static String getTenantId() {
-        BladeUser user = getUser();
-        if (null == user) {
-            if (BladeApplication.isLocalDev()) {
-                return BladeConstant.ADMIN_TENANT_ID;
-            }
-            return StringPool.EMPTY;
-        }
-        return user.getTenantId();
-    }
-
-    /**
-     * 获取租户ID
-     *
-     * @param request request
-     * @return tenantId
-     */
-    public static String getTenantId(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getTenantId();
-    }
-
-    /**
-     * 获取第三方认证ID
-     *
-     * @return tenantId
-     */
-    public static String getOauthId() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getOauthId();
-    }
-
-    /**
-     * 获取第三方认证ID
-     *
-     * @param request request
-     * @return tenantId
-     */
-    public static String getOauthId(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getOauthId();
-    }
-
-    /**
-     * 获取客户端id
-     *
-     * @return clientId
-     */
-    public static String getClientId() {
-        BladeUser user = getUser();
-        return (null == user) ? StringPool.EMPTY : user.getClientId();
-    }
-
-    /**
-     * 获取客户端id
-     *
-     * @param request request
-     * @return clientId
-     */
-    public static String getClientId(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? StringPool.EMPTY : user.getClientId();
-    }
-
-    /**
-     * 获取用户详情
-     *
-     * @return clientId
-     */
-    public static Kv getDetail() {
-        BladeUser user = getUser();
-        return (null == user) ? Kv.create() : user.getDetail();
-    }
-
-    /**
-     * 获取用户详情
-     *
-     * @param request request
-     * @return clientId
-     */
-    public static Kv getDetail(HttpServletRequest request) {
-        BladeUser user = getUser(request);
-        return (null == user) ? Kv.create() : user.getDetail();
-    }
-
-    /**
-     * 获取Claims
-     *
-     * @param request request
-     * @return Claims
-     */
-    public static Claims getClaims(HttpServletRequest request) {
-        String auth = request.getHeader(AuthUtil.HEADER);
-        Claims claims = null;
-        String token;
-        // 获取 Token 参数
-        if (StringUtil.isNotBlank(auth)) {
-            token = JwtUtil.getToken(auth);
-        } else {
-            String parameter = request.getParameter(AuthUtil.HEADER);
-            token = JwtUtil.getToken(parameter);
-        }
-        // 获取 Token 值
-        if (StringUtil.isNotBlank(token)) {
-            claims = AuthUtil.parseJWT(token);
-        }
-        // 判断 Token 状态
-        if (ObjectUtil.isNotEmpty(claims) && getJwtProperties().getState()) {
-            String tenantId = Func.toStr(claims.get(AuthUtil.TENANT_ID));
-            String userId = Func.toStr(claims.get(AuthUtil.USER_ID));
-            String accessToken = JwtUtil.getAccessToken(tenantId, userId, token);
-            if (!token.equalsIgnoreCase(accessToken)) {
-                return null;
-            }
-        }
-        return claims;
-    }
-
-    /**
-     * 获取请求头
-     *
-     * @return header
-     */
-    public static String getHeader() {
-        return getHeader(Objects.requireNonNull(WebUtil.getRequest()));
-    }
-
-    /**
-     * 获取请求头
-     *
-     * @param request request
-     * @return header
-     */
-    public static String getHeader(HttpServletRequest request) {
-        return request.getHeader(HEADER);
-    }
-
-    /**
-     * 解析jsonWebToken
-     *
-     * @param jsonWebToken jsonWebToken
-     * @return Claims
-     */
-    public static Claims parseJWT(String jsonWebToken) {
-        return JwtUtil.parseJWT(jsonWebToken);
-    }
+	private static final String BLADE_USER_REQUEST_ATTR = "_BLADE_USER_REQUEST_ATTR_";
+
+	private final static String HEADER = TokenConstant.HEADER;
+	private final static String ACCOUNT = TokenConstant.ACCOUNT;
+	private final static String USER_NAME = TokenConstant.USER_NAME;
+	private final static String NICK_NAME = TokenConstant.NICK_NAME;
+	private final static String USER_ID = TokenConstant.USER_ID;
+	private final static String DEPT_ID = TokenConstant.DEPT_ID;
+	private final static String POST_ID = TokenConstant.POST_ID;
+	private final static String ROLE_ID = TokenConstant.ROLE_ID;
+	private final static String ROLE_NAME = TokenConstant.ROLE_NAME;
+	private final static String TENANT_ID = TokenConstant.TENANT_ID;
+	private final static String OAUTH_ID = TokenConstant.OAUTH_ID;
+	private final static String CLIENT_ID = TokenConstant.CLIENT_ID;
+	private final static String DETAIL = TokenConstant.DETAIL;
+
+	private static JwtProperties jwtProperties;
+
+	/**
+	 * 获取配置类
+	 *
+	 * @return jwtProperties
+	 */
+	private static JwtProperties getJwtProperties() {
+		if (jwtProperties == null) {
+			jwtProperties = SpringUtil.getBean(JwtProperties.class);
+		}
+		return jwtProperties;
+	}
+
+	/**
+	 * 获取用户信息
+	 *
+	 * @return BladeUser
+	 */
+	public static BladeUser getUser() {
+		HttpServletRequest request = WebUtil.getRequest();
+		if (request == null) {
+			return null;
+		}
+		// 优先从 request 中获取
+		Object bladeUser = request.getAttribute(BLADE_USER_REQUEST_ATTR);
+		if (bladeUser == null) {
+			bladeUser = getUser(request);
+			if (bladeUser != null) {
+				// 设置到 request 中
+				request.setAttribute(BLADE_USER_REQUEST_ATTR, bladeUser);
+			}
+		}
+		return (BladeUser) bladeUser;
+	}
+
+	/**
+	 * 获取用户信息
+	 *
+	 * @param request request
+	 * @return BladeUser
+	 */
+	@SuppressWarnings("unchecked")
+	public static BladeUser getUser(HttpServletRequest request) {
+		Claims claims = getClaims(request);
+		if (claims == null) {
+			return null;
+		}
+		String clientId = Func.toStr(claims.get(AuthUtil.CLIENT_ID));
+		Long userId = Func.toLong(claims.get(AuthUtil.USER_ID));
+		String tenantId = Func.toStr(claims.get(AuthUtil.TENANT_ID));
+		String oauthId = Func.toStr(claims.get(AuthUtil.OAUTH_ID));
+		String deptId = Func.toStrWithEmpty(claims.get(AuthUtil.DEPT_ID), StringPool.MINUS_ONE);
+		String postId = Func.toStrWithEmpty(claims.get(AuthUtil.POST_ID), StringPool.MINUS_ONE);
+		String roleId = Func.toStrWithEmpty(claims.get(AuthUtil.ROLE_ID), StringPool.MINUS_ONE);
+		String account = Func.toStr(claims.get(AuthUtil.ACCOUNT));
+		String roleName = Func.toStr(claims.get(AuthUtil.ROLE_NAME));
+		String userName = Func.toStr(claims.get(AuthUtil.USER_NAME));
+		String nickName = Func.toStr(claims.get(AuthUtil.NICK_NAME));
+		Kv detail = Kv.create().setAll((Map<? extends String, ?>) claims.get(AuthUtil.DETAIL));
+		BladeUser bladeUser = new BladeUser();
+		bladeUser.setClientId(clientId);
+		bladeUser.setUserId(userId);
+		bladeUser.setTenantId(tenantId);
+		bladeUser.setOauthId(oauthId);
+		bladeUser.setAccount(account);
+		bladeUser.setDeptId(deptId);
+		bladeUser.setPostId(postId);
+		bladeUser.setRoleId(roleId);
+		bladeUser.setRoleName(roleName);
+		bladeUser.setUserName(userName);
+		bladeUser.setNickName(nickName);
+		bladeUser.setDetail(detail);
+		return bladeUser;
+	}
+
+	/**
+	 * 是否为超管
+	 *
+	 * @return boolean
+	 */
+	public static boolean isAdministrator() {
+		return StringUtil.containsAny(getUserRole(), RoleConstant.ADMINISTRATOR);
+	}
+
+	/**
+	 * 是否为管理员
+	 *
+	 * @return boolean
+	 */
+	public static boolean isAdmin() {
+		return StringUtil.containsAny(getUserRole(), RoleConstant.ADMIN);
+	}
+
+	/**
+	 * 获取用户id
+	 *
+	 * @return userId
+	 */
+	public static Long getUserId() {
+		BladeUser user = getUser();
+		return (null == user) ? -1 : user.getUserId();
+	}
+
+	/**
+	 * 获取用户id
+	 *
+	 * @param request request
+	 * @return userId
+	 */
+	public static Long getUserId(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? -1 : user.getUserId();
+	}
+
+	/**
+	 * 获取用户账号
+	 *
+	 * @return userAccount
+	 */
+	public static String getUserAccount() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getAccount();
+	}
+
+	/**
+	 * 获取用户账号
+	 *
+	 * @param request request
+	 * @return userAccount
+	 */
+	public static String getUserAccount(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getAccount();
+	}
+
+	/**
+	 * 获取用户名
+	 *
+	 * @return userName
+	 */
+	public static String getUserName() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getUserName();
+	}
+
+	/**
+	 * 获取用户名
+	 *
+	 * @param request request
+	 * @return userName
+	 */
+	public static String getUserName(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getUserName();
+	}
+
+	/**
+	 * 获取昵称
+	 *
+	 * @return userName
+	 */
+	public static String getNickName() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getNickName();
+	}
+
+	/**
+	 * 获取昵称
+	 *
+	 * @param request request
+	 * @return userName
+	 */
+	public static String getNickName(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getNickName();
+	}
+
+	/**
+	 * 获取用户部门
+	 *
+	 * @return userName
+	 */
+	public static String getDeptId() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getDeptId();
+	}
+
+	/**
+	 * 获取用户部门
+	 *
+	 * @param request request
+	 * @return userName
+	 */
+	public static String getDeptId(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getDeptId();
+	}
+
+	/**
+	 * 获取用户岗位
+	 *
+	 * @return userName
+	 */
+	public static String getPostId() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getPostId();
+	}
+
+	/**
+	 * 获取用户岗位
+	 *
+	 * @param request request
+	 * @return userName
+	 */
+	public static String getPostId(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getPostId();
+	}
+
+	/**
+	 * 获取用户角色
+	 *
+	 * @return userName
+	 */
+	public static String getUserRole() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getRoleName();
+	}
+
+	/**
+	 * 获取用角色
+	 *
+	 * @param request request
+	 * @return userName
+	 */
+	public static String getUserRole(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getRoleName();
+	}
+
+	/**
+	 * 获取租户ID
+	 *
+	 * @return tenantId
+	 */
+	public static String getTenantId() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getTenantId();
+	}
+
+	/**
+	 * 获取租户ID
+	 *
+	 * @param request request
+	 * @return tenantId
+	 */
+	public static String getTenantId(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getTenantId();
+	}
+
+	/**
+	 * 获取第三方认证ID
+	 *
+	 * @return tenantId
+	 */
+	public static String getOauthId() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getOauthId();
+	}
+
+	/**
+	 * 获取第三方认证ID
+	 *
+	 * @param request request
+	 * @return tenantId
+	 */
+	public static String getOauthId(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getOauthId();
+	}
+
+	/**
+	 * 获取客户端id
+	 *
+	 * @return clientId
+	 */
+	public static String getClientId() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getClientId();
+	}
+
+	/**
+	 * 获取客户端id
+	 *
+	 * @param request request
+	 * @return clientId
+	 */
+	public static String getClientId(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getClientId();
+	}
+
+	/**
+	 * 获取用户详情
+	 *
+	 * @return clientId
+	 */
+	public static Kv getDetail() {
+		BladeUser user = getUser();
+		return (null == user) ? Kv.create() : user.getDetail();
+	}
+
+	/**
+	 * 获取用户详情
+	 *
+	 * @param request request
+	 * @return clientId
+	 */
+	public static Kv getDetail(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? Kv.create() : user.getDetail();
+	}
+
+	/**
+	 * 获取Claims
+	 *
+	 * @param request request
+	 * @return Claims
+	 */
+	public static Claims getClaims(HttpServletRequest request) {
+		String auth = request.getHeader(AuthUtil.HEADER);
+		Claims claims = null;
+		String token;
+		// 获取 Token 参数
+		if (StringUtil.isNotBlank(auth)) {
+			token = JwtUtil.getToken(auth);
+		} else {
+			String parameter = request.getParameter(AuthUtil.HEADER);
+			token = JwtUtil.getToken(parameter);
+		}
+		// 获取 Token 值
+		if (StringUtil.isNotBlank(token)) {
+			claims = AuthUtil.parseJWT(token);
+		}
+		// 判断 Token 状态
+		if (ObjectUtil.isNotEmpty(claims) && getJwtProperties().getState()) {
+			String tenantId = Func.toStr(claims.get(AuthUtil.TENANT_ID));
+			String userId = Func.toStr(claims.get(AuthUtil.USER_ID));
+			String accessToken = JwtUtil.getAccessToken(tenantId, userId, token);
+			if (!token.equalsIgnoreCase(accessToken)) {
+				return null;
+			}
+		}
+		return claims;
+	}
+
+	/**
+	 * 获取请求头
+	 *
+	 * @return header
+	 */
+	public static String getHeader() {
+		return getHeader(Objects.requireNonNull(WebUtil.getRequest()));
+	}
+
+	/**
+	 * 获取请求头
+	 *
+	 * @param request request
+	 * @return header
+	 */
+	public static String getHeader(HttpServletRequest request) {
+		return request.getHeader(HEADER);
+	}
+
+	/**
+	 * 解析jsonWebToken
+	 *
+	 * @param jsonWebToken jsonWebToken
+	 * @return Claims
+	 */
+	public static Claims parseJWT(String jsonWebToken) {
+		return JwtUtil.parseJWT(jsonWebToken);
+	}
 
 }

+ 1 - 1
bladex-tool/blade-starter-cache/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 2 - 2
bladex-tool/blade-starter-cache/src/main/java/org/springblade/core/cache/config/CacheConfiguration.java

@@ -16,8 +16,8 @@
  */
 package org.springblade.core.cache.config;
 
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.cache.annotation.EnableCaching;
-import org.springframework.context.annotation.Configuration;
 
 /**
  * Cache配置类
@@ -25,6 +25,6 @@ import org.springframework.context.annotation.Configuration;
  * @author Chill
  */
 @EnableCaching
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 public class CacheConfiguration {
 }

+ 1 - 1
bladex-tool/blade-starter-datascope/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 4 - 4
bladex-tool/blade-starter-datascope/src/main/java/org/springblade/core/datascope/config/DataScopeConfiguration.java

@@ -17,17 +17,17 @@
 package org.springblade.core.datascope.config;
 
 import lombok.AllArgsConstructor;
-import org.springblade.core.datascope.interceptor.DataScopeInterceptor;
-import org.springblade.core.datascope.props.DataScopeProperties;
 import org.springblade.core.datascope.handler.BladeDataScopeHandler;
 import org.springblade.core.datascope.handler.BladeScopeModelHandler;
 import org.springblade.core.datascope.handler.DataScopeHandler;
 import org.springblade.core.datascope.handler.ScopeModelHandler;
+import org.springblade.core.datascope.interceptor.DataScopeInterceptor;
+import org.springblade.core.datascope.props.DataScopeProperties;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.jdbc.core.JdbcTemplate;
 
 /**
@@ -35,7 +35,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
  *
  * @author Chill
  */
-@Configuration(proxyBeanMethods = false)
+@AutoConfiguration
 @AllArgsConstructor
 @EnableConfigurationProperties(DataScopeProperties.class)
 public class DataScopeConfiguration {

+ 5 - 11
bladex-tool/blade-starter-develop/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>BladeX-Tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.8.2.RELEASE</version>
+        <version>3.0.1.RELEASE</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -24,22 +24,16 @@
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-generator</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.baomidou</groupId>
-                    <artifactId>mybatis-plus-extension</artifactId>
-                </exclusion>
-            </exclusions>
         </dependency>
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-extension</artifactId>
         </dependency>
-        <!--Velocity-->
+        <!--Beetl-->
         <dependency>
-            <groupId>org.apache.velocity</groupId>
-            <artifactId>velocity</artifactId>
-            <version>1.7</version>
+            <groupId>com.ibeetl</groupId>
+            <artifactId>beetl</artifactId>
+            <version>3.10.0.Antlr4.5-RELEASE</version>
         </dependency>
     </dependencies>
 

+ 14 - 3
bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/CodeGenerator.java

@@ -17,6 +17,7 @@
 package org.springblade.develop;
 
 
+import org.springblade.develop.constant.DevelopConstant;
 import org.springblade.develop.support.BladeCodeGenerator;
 
 /**
@@ -39,9 +40,9 @@ public class CodeGenerator {
 	 */
 	public static String PACKAGE_NAME = "org.springblade.system";
 	/**
-	 * 前端代码生成所属系统
+	 * 前端代码生成风格
 	 */
-	public static String SYSTEM_NAME = "saber";
+	public static String CODE_STYLE = DevelopConstant.SABER_NAME;
 	/**
 	 * 前端代码生成地址
 	 */
@@ -66,6 +67,14 @@ public class CodeGenerator {
 	 * 基础业务字段
 	 */
 	public static String[] SUPER_ENTITY_COLUMNS = {"id", "create_time", "create_user", "create_dept", "update_time", "update_user", "status", "is_deleted"};
+	/**
+	 * 是否包含包装器
+	 */
+	public static Boolean HAS_WRAPPER = Boolean.TRUE;
+	/**
+	 * 是否包含远程调用
+	 */
+	public static Boolean HAS_FEIGN = Boolean.FALSE;
 
 
 	/**
@@ -75,7 +84,7 @@ public class CodeGenerator {
 		BladeCodeGenerator generator = new BladeCodeGenerator();
 		generator.setCodeName(CODE_NAME);
 		generator.setServiceName(SERVICE_NAME);
-		generator.setSystemName(SYSTEM_NAME);
+		generator.setCodeStyle(CODE_STYLE);
 		generator.setPackageName(PACKAGE_NAME);
 		generator.setPackageWebDir(PACKAGE_WEB_DIR);
 		generator.setTablePrefix(TABLE_PREFIX);
@@ -83,6 +92,8 @@ public class CodeGenerator {
 		generator.setExcludeTables(EXCLUDE_TABLES);
 		generator.setHasSuperEntity(HAS_SUPER_ENTITY);
 		generator.setSuperEntityColumns(SUPER_ENTITY_COLUMNS);
+		generator.setHasWrapper(HAS_WRAPPER);
+		generator.setHasFeign(HAS_FEIGN);
 		generator.run();
 	}
 

+ 31 - 1
bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/constant/DevelopConstant.java

@@ -17,7 +17,7 @@
 package org.springblade.develop.constant;
 
 /**
- * 系统常量.
+ * 代码生成系统常量.
  *
  * @author Chill
  */
@@ -31,4 +31,34 @@ public interface DevelopConstant {
 	 * saber 系统名
 	 */
 	String SABER_NAME = "saber";
+
+	/**
+	 * lemon 系统名
+	 */
+	String LEMON_NAME = "lemon";
+
+	/**
+	 * element 系统名
+	 */
+	String ELEMENT_NAME = "element";
+
+	/**
+	 * 单表模式
+	 */
+	String TEMPLATE_CRUD = "crud";
+
+	/**
+	 * 树表模式
+	 */
+	String TEMPLATE_TREE = "tree";
+
+	/**
+	 * 主子表模式
+	 */
+	String TEMPLATE_SUB = "sub";
+
+	/**
+	 * 主模块
+	 */
+	String TEMPLATE_MAIN = "main";
 }

+ 195 - 217
bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/support/BladeCodeGenerator.java

@@ -16,31 +16,24 @@
  */
 package org.springblade.develop.support;
 
-import com.baomidou.mybatisplus.annotation.DbType;
-import com.baomidou.mybatisplus.core.toolkit.IdWorker;
-import com.baomidou.mybatisplus.core.toolkit.StringPool;
-import com.baomidou.mybatisplus.core.toolkit.StringUtils;
-import com.baomidou.mybatisplus.generator.AutoGenerator;
-import com.baomidou.mybatisplus.generator.InjectionConfig;
-import com.baomidou.mybatisplus.generator.config.*;
-import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
-import com.baomidou.mybatisplus.generator.config.converts.OracleTypeConvert;
-import com.baomidou.mybatisplus.generator.config.converts.PostgreSqlTypeConvert;
-import com.baomidou.mybatisplus.generator.config.converts.SqlServerTypeConvert;
-import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.FastAutoGenerator;
+import com.baomidou.mybatisplus.generator.config.TemplateType;
+import com.baomidou.mybatisplus.generator.config.rules.DateType;
 import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.annotations.Mapper;
 import org.springblade.core.tool.utils.Func;
 import org.springblade.core.tool.utils.StringUtil;
-import org.springblade.develop.constant.DevelopConstant;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PropertiesLoaderUtils;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.springblade.develop.constant.DevelopConstant.*;
 
 /**
  * 代码生成器配置类
@@ -51,14 +44,22 @@ import java.util.*;
 @Slf4j
 public class BladeCodeGenerator {
 	/**
-	 * 代码所在系统
+	 * 代码风格
 	 */
-	private String systemName = DevelopConstant.SWORD_NAME;
+	private String codeStyle = SABER_NAME;
 	/**
 	 * 代码模块名称
 	 */
 	private String codeName;
 	/**
+	 * 模型编号
+	 */
+	private String modelCode;
+	/**
+	 * 模型实体类
+	 */
+	private String modelClass;
+	/**
 	 * 代码所在服务名
 	 */
 	private String serviceName = "blade-service";
@@ -67,6 +68,34 @@ public class BladeCodeGenerator {
 	 */
 	private String packageName = "org.springblade.test";
 	/**
+	 * 模版类型
+	 */
+	private String templateType;
+	/**
+	 * 作者信息
+	 */
+	private String author;
+	/**
+	 * 子表模型主键
+	 */
+	private String subModelId;
+	/**
+	 * 子表绑定外键
+	 */
+	private String subFkId;
+	/**
+	 * 树主键字段
+	 */
+	private String treeId;
+	/**
+	 * 树父主键字段
+	 */
+	private String treePid;
+	/**
+	 * 树名称字段
+	 */
+	private String treeName;
+	/**
 	 * 代码后端生成的地址
 	 */
 	private String packageDir;
@@ -89,24 +118,28 @@ public class BladeCodeGenerator {
 	/**
 	 * 是否包含基础业务字段
 	 */
-	private Boolean hasSuperEntity = Boolean.FALSE;
+	private Boolean hasSuperEntity = Boolean.TRUE;
 	/**
 	 * 是否包含包装器
 	 */
-	private Boolean hasWrapper = Boolean.FALSE;
+	private Boolean hasWrapper = Boolean.TRUE;
+	/**
+	 * 是否包含远程调用
+	 */
+	private Boolean hasFeign = Boolean.FALSE;
+	/**
+	 * 是否包含服务名
+	 */
+	private Boolean hasServiceName = Boolean.FALSE;
 	/**
 	 * 基础业务字段
 	 */
-	private String[] superEntityColumns = {"id", "create_time", "create_user", "create_dept", "update_time", "update_user", "status", "is_deleted"};
+	private String[] superEntityColumns = {"create_time", "create_user", "create_dept", "update_time", "update_user", "status", "is_deleted"};
 	/**
 	 * 租户字段
 	 */
 	private String tenantColumn = "tenant_id";
 	/**
-	 * 是否启用swagger
-	 */
-	private Boolean isSwagger2 = Boolean.TRUE;
-	/**
 	 * 数据库驱动名
 	 */
 	private String driverName;
@@ -122,199 +155,156 @@ public class BladeCodeGenerator {
 	 * 数据库密码
 	 */
 	private String password;
+	/**
+	 * 数据模型
+	 */
+	private Map<String, Object> model;
+	/**
+	 * 数据原型
+	 */
+	private List<Map<String, Object>> prototypes;
+	/**
+	 * 子数据模型
+	 */
+	private Map<String, Object> subModel;
+	/**
+	 * 子数据原型
+	 */
+	private List<Map<String, Object>> subPrototypes;
 
+	/**
+	 * 代码生成执行
+	 */
 	public void run() {
-		Properties props = getProperties();
-		AutoGenerator mpg = new AutoGenerator();
-		GlobalConfig gc = new GlobalConfig();
-		String outputDir = getOutputDir();
-		String author = props.getProperty("author");
-		gc.setOutputDir(outputDir);
-		gc.setAuthor(author);
-		gc.setFileOverride(true);
-		gc.setOpen(false);
-		gc.setActiveRecord(false);
-		gc.setEnableCache(false);
-		gc.setBaseResultMap(true);
-		gc.setBaseColumnList(true);
-		gc.setMapperName("%sMapper");
-		gc.setXmlName("%sMapper");
-		gc.setServiceName("I%sService");
-		gc.setServiceImplName("%sServiceImpl");
-		gc.setControllerName("%sController");
-		gc.setSwagger2(isSwagger2);
-		mpg.setGlobalConfig(gc);
-		DataSourceConfig dsc = new DataSourceConfig();
-		String driverName = Func.toStr(this.driverName, props.getProperty("spring.datasource.driver-class-name"));
-		if (StringUtil.containsAny(driverName, DbType.MYSQL.getDb())) {
-			dsc.setDbType(DbType.MYSQL);
-			dsc.setTypeConvert(new MySqlTypeConvert());
-		} else if (StringUtil.containsAny(driverName, DbType.POSTGRE_SQL.getDb())) {
-			dsc.setDbType(DbType.POSTGRE_SQL);
-			dsc.setTypeConvert(new PostgreSqlTypeConvert());
-		} else if (StringUtil.containsAny(driverName, DbType.SQL_SERVER.getDb())) {
-			dsc.setDbType(DbType.SQL_SERVER);
-			dsc.setTypeConvert(new SqlServerTypeConvert());
-		} else {
-			dsc.setDbType(DbType.ORACLE);
-			dsc.setTypeConvert(new OracleTypeConvert());
-		}
-		dsc.setDriverName(driverName);
-		dsc.setUrl(Func.toStr(this.url, props.getProperty("spring.datasource.url")));
-		dsc.setUsername(Func.toStr(this.username, props.getProperty("spring.datasource.username")));
-		dsc.setPassword(Func.toStr(this.password, props.getProperty("spring.datasource.password")));
-		mpg.setDataSource(dsc);
-		// 策略配置
-		StrategyConfig strategy = new StrategyConfig();
-		// strategy.setCapitalMode(true);// 全局大写命名
-		// strategy.setDbColumnUnderline(true);//全局下划线命名
-		strategy.setNaming(NamingStrategy.underline_to_camel);
-		strategy.setColumnNaming(NamingStrategy.underline_to_camel);
-		strategy.setTablePrefix(tablePrefix);
-		if (includeTables.length > 0) {
-			strategy.setInclude(includeTables);
+		// 主模块代码生成
+		getAutoGenerator(getCustomMap(TEMPLATE_MAIN), getCustomFile(TEMPLATE_MAIN)).templateEngine(new BladeTemplateEngine(getOutputDir(), getOutputWebDir())).execute();
+		// 子模块代码生成
+		if (Func.equals(templateType, TEMPLATE_SUB) && StringUtil.isNotBlank(subModelId)) {
+			getAutoGenerator(getCustomMap(TEMPLATE_SUB), getCustomFile(TEMPLATE_SUB)).templateEngine(new BladeTemplateEngine(getOutputDir(), getOutputWebDir())).execute();
 		}
-		if (excludeTables.length > 0) {
-			strategy.setExclude(excludeTables);
-		}
-		if (hasSuperEntity) {
-			strategy.setSuperEntityClass("org.springblade.core.mp.base.BaseEntity");
-			strategy.setSuperEntityColumns(superEntityColumns);
-			strategy.setSuperServiceClass("org.springblade.core.mp.base.BaseService");
-			strategy.setSuperServiceImplClass("org.springblade.core.mp.base.BaseServiceImpl");
+	}
+
+	/**
+	 * 设置 customMap
+	 */
+	private Map<String, Object> getCustomMap(String generateType) {
+		List<Map<String, Object>> prototypeList;
+		String[] split = packageName.split("\\.");
+		String serviceCode = split[split.length - 1];
+		Map<String, Object> customMap = new HashMap<>(11);
+		customMap.put("generateType", generateType);
+		customMap.put("codeName", codeName);
+		customMap.put("serviceName", serviceName);
+		customMap.put("serviceCode", serviceCode);
+		customMap.put("packageName", packageName);
+		customMap.put("tenantColumn", tenantColumn);
+		customMap.put("hasWrapper", hasWrapper);
+		customMap.put("hasServiceName", hasServiceName);
+		customMap.put("templateType", templateType);
+		customMap.put("author", author);
+		customMap.put("subModelId", subModelId);
+		customMap.put("subFkId", subFkId);
+		customMap.put("treeId", treeId);
+		customMap.put("treePid", treePid);
+		customMap.put("treeName", treeName);
+		customMap.put("subFkIdHump", StringUtil.underlineToHump(subFkId));
+		customMap.put("treeIdHump", StringUtil.underlineToHump(treeId));
+		customMap.put("treePidHump", StringUtil.underlineToHump(treePid));
+		if (Func.equals(generateType, TEMPLATE_SUB)) {
+			prototypeList = subPrototypes;
+			customMap.put("model", subModel);
+			customMap.put("prototypes", subPrototypes);
+			customMap.put("modelCode", subModel.get("modelCode"));
+			customMap.put("modelClass", subModel.get("modelClass"));
+			customMap.put("modelTable", subModel.get("modelTable"));
 		} else {
-			strategy.setSuperServiceClass("com.baomidou.mybatisplus.extension.service.IService");
-			strategy.setSuperServiceImplClass("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
+			prototypeList = prototypes;
+			customMap.put("model", model);
+			customMap.put("prototypes", prototypes);
+			customMap.put("subModel", subModel);
+			customMap.put("subPrototypes", subPrototypes);
+			customMap.put("modelCode", model.get("modelCode"));
+			customMap.put("modelClass", model.get("modelClass"));
+			customMap.put("modelTable", model.get("modelTable"));
 		}
-		// 自定义 controller 父类
-		strategy.setSuperControllerClass("org.springblade.core.boot.ctrl.BladeController");
-		strategy.setEntityBuilderModel(false);
-		strategy.setEntityLombokModel(true);
-		strategy.setControllerMappingHyphenStyle(true);
-		mpg.setStrategy(strategy);
-		// 包配置
-		PackageConfig pc = new PackageConfig();
-		// 控制台扫描
-		pc.setModuleName(null);
-		pc.setParent(packageName);
-		pc.setController("controller");
-		pc.setEntity("entity");
-		pc.setXml("mapper");
-		mpg.setPackageInfo(pc);
-		mpg.setCfg(getInjectionConfig());
-		mpg.execute();
+		List<String> propertyImport = prototypeList.stream().filter(prototype -> {
+			String propertyType = String.valueOf(prototype.get("propertyType"));
+			return !"String".equals(propertyType) && !"Integer".equals(propertyType) && !"Long".equals(propertyType);
+		}).map(prototype -> String.valueOf(prototype.get("propertyEntity"))).distinct().collect(Collectors.toList());
+		customMap.put("propertyImport", propertyImport);
+		return customMap;
 	}
 
-	private InjectionConfig getInjectionConfig() {
-		String servicePackage = serviceName.split("-").length > 1 ? serviceName.split("-")[1] : serviceName;
-		// 自定义配置
-		Map<String, Object> map = new HashMap<>(16);
-		InjectionConfig cfg = new InjectionConfig() {
-			@Override
-			public void initMap() {
-				map.put("codeName", codeName);
-				map.put("serviceName", serviceName);
-				map.put("servicePackage", servicePackage);
-				map.put("servicePackageLowerCase", servicePackage.toLowerCase());
-				map.put("tenantColumn", tenantColumn);
-				map.put("hasWrapper", hasWrapper);
-				this.setMap(map);
-			}
-		};
-		List<FileOutConfig> focList = new ArrayList<>();
-		focList.add(new FileOutConfig("/templates/sql/menu.sql.vm") {
-			@Override
-			public String outputFile(TableInfo tableInfo) {
-				map.put("entityKey", (tableInfo.getEntityName().toLowerCase()));
-				map.put("menuId", IdWorker.getId());
-				map.put("addMenuId", IdWorker.getId());
-				map.put("editMenuId", IdWorker.getId());
-				map.put("removeMenuId", IdWorker.getId());
-				map.put("viewMenuId", IdWorker.getId());
-				return getOutputDir() + "/" + "/sql/" + tableInfo.getEntityName().toLowerCase() + ".menu.mysql";
-			}
-		});
-		focList.add(new FileOutConfig("/templates/entityVO.java.vm") {
-			@Override
-			public String outputFile(TableInfo tableInfo) {
-				return getOutputDir() + "/" + packageName.replace(".", "/") + "/" + "vo" + "/" + tableInfo.getEntityName() + "VO" + StringPool.DOT_JAVA;
-			}
-		});
-		focList.add(new FileOutConfig("/templates/entityDTO.java.vm") {
-			@Override
-			public String outputFile(TableInfo tableInfo) {
-				return getOutputDir() + "/" + packageName.replace(".", "/") + "/" + "dto" + "/" + tableInfo.getEntityName() + "DTO" + StringPool.DOT_JAVA;
-			}
-		});
+	/**
+	 * 设置 customFile
+	 */
+	private Map<String, String> getCustomFile(String type) {
+		Map<String, String> customFile = new HashMap<>(15);
+		if (!Func.equals(type, TEMPLATE_SUB)) {
+			customFile.put("menu.sql", "/templates/sql/menu.sql.btl");
+		}
+		customFile.put("entityVO.java", "/templates/api/entityVO.java.btl");
+		customFile.put("entityDTO.java", "/templates/api/entityDTO.java.btl");
 		if (hasWrapper) {
-			focList.add(new FileOutConfig("/templates/wrapper.java.vm") {
-				@Override
-				public String outputFile(TableInfo tableInfo) {
-					return getOutputDir() + "/" + packageName.replace(".", "/") + "/" + "wrapper" + "/" + tableInfo.getEntityName() + "Wrapper" + StringPool.DOT_JAVA;
-				}
-			});
+			customFile.put("wrapper.java", "/templates/api/wrapper.java.btl");
+		}
+		if (hasFeign) {
+			customFile.put("feign.java", "/templates/api/feign.java.btl");
+			customFile.put("feignclient.java", "/templates/api/feignclient.java.btl");
 		}
 		if (Func.isNotBlank(packageWebDir)) {
-			if (Func.equals(systemName, DevelopConstant.SWORD_NAME)) {
-				focList.add(new FileOutConfig("/templates/sword/action.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/actions" + "/" + tableInfo.getEntityName().toLowerCase() + ".js";
-					}
-				});
-				focList.add(new FileOutConfig("/templates/sword/model.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/models" + "/" + tableInfo.getEntityName().toLowerCase() + ".js";
-					}
-				});
-				focList.add(new FileOutConfig("/templates/sword/service.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/services" + "/" + tableInfo.getEntityName().toLowerCase() + ".js";
-					}
-				});
-				focList.add(new FileOutConfig("/templates/sword/list.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/pages" + "/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + ".js";
-					}
-				});
-				focList.add(new FileOutConfig("/templates/sword/add.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/pages" + "/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + "Add.js";
-					}
-				});
-				focList.add(new FileOutConfig("/templates/sword/edit.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/pages" + "/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + "Edit.js";
-					}
-				});
-				focList.add(new FileOutConfig("/templates/sword/view.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/pages" + "/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + "View.js";
-					}
-				});
-			} else if (Func.equals(systemName, DevelopConstant.SABER_NAME)) {
-				focList.add(new FileOutConfig("/templates/saber/api.js.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/api" + "/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + ".js";
-					}
-				});
-				focList.add(new FileOutConfig("/templates/saber/crud.vue.vm") {
-					@Override
-					public String outputFile(TableInfo tableInfo) {
-						return getOutputWebDir() + "/views" + "/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + ".vue";
-					}
-				});
+			if (Func.equals(codeStyle, SWORD_NAME)) {
+				customFile.put("action.js", "/templates/sword/action.js.btl");
+				customFile.put("model.js", "/templates/sword/model.js.btl");
+				customFile.put("service.js", "/templates/sword/service.js.btl");
+				customFile.put("list.js", "/templates/sword/list.js.btl");
+				customFile.put("add.js", "/templates/sword/add.js.btl");
+				customFile.put("edit.js", "/templates/sword/edit.js.btl");
+				customFile.put("view.js", "/templates/sword/view.js.btl");
+			} else if (Func.equals(codeStyle, SABER_NAME)) {
+				customFile.put("api.js", "/templates/saber/" + templateType + "/api.js.btl");
+				customFile.put("const.js", "/templates/saber/" + templateType + "/const.js.btl");
+				if (!Func.equals(type, TEMPLATE_SUB)) {
+					customFile.put("crud.vue", "/templates/saber/" + templateType + "/crud.vue.btl");
+				}
+			} else if (Func.equals(codeStyle, ELEMENT_NAME)) {
+				customFile.put("api.js", "/templates/element/" + templateType + "/api.js.btl");
+				customFile.put("const.js", "/templates/element/" + templateType + "/const.js.btl");
+				if (!Func.equals(type, TEMPLATE_SUB)) {
+					customFile.put("crud.vue", "/templates/element/" + templateType + "/crud.vue.btl");
+				} else {
+					customFile.put("sub.vue", "/templates/element/" + templateType + "/sub.vue.btl");
+				}
 			}
 		}
-		cfg.setFileOutConfigList(focList);
-		return cfg;
+		return customFile;
+	}
+
+	private FastAutoGenerator getAutoGenerator(Map<String, Object> customMap, Map<String, String> customFile) {
+		Properties props = getProperties();
+		String url = Func.toStr(this.url, props.getProperty("spring.datasource.url"));
+		String username = Func.toStr(this.username, props.getProperty("spring.datasource.username"));
+		String password = Func.toStr(this.password, props.getProperty("spring.datasource.password"));
+		return FastAutoGenerator.create(url, username, password)
+			.globalConfig(builder -> builder.author(StringUtil.isBlank(author) ? props.getProperty("author") : author).dateType(DateType.TIME_PACK).enableSwagger().outputDir(getOutputDir()).disableOpenDir())
+			.packageConfig(builder -> builder.parent(packageName).controller("controller").entity("entity").service("service").serviceImpl("service.impl").mapper("mapper").xml("mapper"))
+			.strategyConfig(builder -> builder.addTablePrefix(tablePrefix).addInclude(Func.toStrArray(String.valueOf(customMap.get("modelTable")))).addExclude(excludeTables)
+				.entityBuilder().naming(NamingStrategy.underline_to_camel).columnNaming(NamingStrategy.underline_to_camel).enableLombok().superClass("org.springblade.core.mp.base.BaseEntity").formatFileName("%sEntity").addSuperEntityColumns(superEntityColumns).enableFileOverride()
+				.serviceBuilder().superServiceClass("org.springblade.core.mp.base.BaseService").superServiceImplClass("org.springblade.core.mp.base.BaseServiceImpl").formatServiceFileName("I%sService").formatServiceImplFileName("%sServiceImpl").enableFileOverride()
+				.mapperBuilder().mapperAnnotation(Mapper.class).enableBaseResultMap().enableBaseColumnList().formatMapperFileName("%sMapper").formatXmlFileName("%sMapper").enableFileOverride()
+				.controllerBuilder().superClass("org.springblade.core.boot.ctrl.BladeController").formatFileName("%sController").enableRestStyle().enableHyphenStyle().enableFileOverride()
+			)
+			.templateConfig(builder -> builder.disable(TemplateType.ENTITY)
+				.entity("/templates/api/entity.java")
+				.service("/templates/api/service.java")
+				.serviceImpl("/templates/api/serviceImpl.java")
+				.mapper("/templates/api/mapper.java")
+				.xml("/templates/api/mapper.xml")
+				.controller("/templates/api/controller.java"))
+			.injectionConfig(builder -> builder.beforeOutputFile(
+					(tableInfo, objectMap) -> System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size())
+				).customMap(customMap).customFile(customFile)
+			);
 	}
 
 	/**
@@ -343,6 +333,7 @@ public class BladeCodeGenerator {
 		return (Func.isBlank(packageDir) ? System.getProperty("user.dir") + "/blade-ops/blade-develop" : packageDir) + "/src/main/java";
 	}
 
+
 	/**
 	 * 生成到Web项目中
 	 *
@@ -352,17 +343,4 @@ public class BladeCodeGenerator {
 		return (Func.isBlank(packageWebDir) ? System.getProperty("user.dir") : packageWebDir) + "/src";
 	}
 
-	/**
-	 * 页面生成的文件名
-	 */
-	private String getGeneratorViewPath(String viewOutputDir, TableInfo tableInfo, String suffixPath) {
-		String name = StringUtils.firstToLowerCase(tableInfo.getEntityName());
-		String path = viewOutputDir + "/" + name + "/" + name + suffixPath;
-		File viewDir = new File(path).getParentFile();
-		if (!viewDir.exists()) {
-			viewDir.mkdirs();
-		}
-		return path;
-	}
-
 }

+ 132 - 0
bladex-tool/blade-starter-develop/src/main/java/org/springblade/develop/support/BladeTemplateEngine.java

@@ -0,0 +1,132 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.develop.support;
+
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.generator.config.OutputFile;
+import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.engine.BeetlTemplateEngine;
+import lombok.AllArgsConstructor;
+import org.springblade.core.tool.utils.StringUtil;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 代码模版生成实现类
+ *
+ * @author Chill
+ */
+@AllArgsConstructor
+public class BladeTemplateEngine extends BeetlTemplateEngine {
+
+	private String outputDir;
+	private String outputWebDir;
+
+	@Override
+	protected void outputCustomFile(List<CustomFile> customFiles, TableInfo tableInfo, Map<String, Object> objectMap) {
+		String packageName = String.valueOf(objectMap.get("packageName"));
+		String serviceCode = String.valueOf(objectMap.get("serviceCode"));
+		String modelCode = String.valueOf(objectMap.get("modelCode"));
+		String entityName = String.valueOf(objectMap.get("modelClass"));
+		String entityNameLower = entityName.toLowerCase();
+
+		customFiles.forEach(customFile -> {
+			String key = customFile.getFileName();
+			String value = customFile.getTemplatePath();
+			String outputPath = getPathInfo(OutputFile.parent);
+			objectMap.put("entityKey", entityNameLower);
+			if (StringUtil.equals(key, "menu.sql")) {
+				objectMap.put("menuId", IdWorker.getId());
+				objectMap.put("addMenuId", IdWorker.getId());
+				objectMap.put("editMenuId", IdWorker.getId());
+				objectMap.put("removeMenuId", IdWorker.getId());
+				objectMap.put("viewMenuId", IdWorker.getId());
+				outputPath = outputDir + StringPool.SLASH + "sql" + StringPool.SLASH + entityNameLower + ".menu.sql";
+			}
+			if (StringUtil.equals(key, "entityVO.java")) {
+				outputPath = outputDir + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH) + StringPool.SLASH + "vo" + StringPool.SLASH + entityName + "VO" + StringPool.DOT_JAVA;
+			}
+
+			if (StringUtil.equals(key, "entityDTO.java")) {
+				outputPath = outputDir + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH) + StringPool.SLASH + "dto" + StringPool.SLASH + entityName + "DTO" + StringPool.DOT_JAVA;
+			}
+
+			if (StringUtil.equals(key, "wrapper.java")) {
+				outputPath = outputDir + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH) + StringPool.SLASH + "wrapper" + StringPool.SLASH + entityName + "Wrapper" + StringPool.DOT_JAVA;
+			}
+
+			if (StringUtil.equals(key, "feign.java")) {
+				outputPath = outputDir + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH) + StringPool.SLASH + "feign" + StringPool.SLASH + "I" + entityName + "Client" + StringPool.DOT_JAVA;
+			}
+
+			if (StringUtil.equals(key, "feignclient.java")) {
+				outputPath = outputDir + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH) + StringPool.SLASH + "feign" + StringPool.SLASH + entityName + "Client" + StringPool.DOT_JAVA;
+			}
+
+			if (StringUtil.equals(key, "action.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "actions" + StringPool.SLASH + entityNameLower + ".js";
+			}
+
+			if (StringUtil.equals(key, "model.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "models" + StringPool.SLASH + entityNameLower + ".js";
+			}
+
+			if (StringUtil.equals(key, "service.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "services" + StringPool.SLASH + entityNameLower + ".js";
+			}
+
+			if (StringUtil.equals(key, "list.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "pages" + StringPool.SLASH + StringUtil.firstCharToUpper(modelCode) + StringPool.SLASH + entityName + StringPool.SLASH + entityName + ".js";
+			}
+
+			if (StringUtil.equals(key, "add.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "pages" + StringPool.SLASH + StringUtil.firstCharToUpper(modelCode) + StringPool.SLASH + entityName + StringPool.SLASH + entityName + "Add.js";
+			}
+
+			if (StringUtil.equals(key, "edit.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "pages" + StringPool.SLASH + StringUtil.firstCharToUpper(modelCode) + StringPool.SLASH + entityName + StringPool.SLASH + entityName + "Edit.js";
+			}
+
+			if (StringUtil.equals(key, "view.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "pages" + StringPool.SLASH + StringUtil.firstCharToUpper(modelCode) + StringPool.SLASH + entityName + StringPool.SLASH + entityName + "View.js";
+			}
+
+			if (StringUtil.equals(key, "api.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "api" + StringPool.SLASH + serviceCode + StringPool.SLASH + modelCode + ".js";
+			}
+
+			if (StringUtil.equals(key, "const.js")) {
+				outputPath = outputWebDir + StringPool.SLASH + "const" + StringPool.SLASH + serviceCode + StringPool.SLASH + modelCode + ".js";
+			}
+
+			if (StringUtil.equals(key, "crud.vue")) {
+				outputPath = outputWebDir + StringPool.SLASH + "views" + StringPool.SLASH + serviceCode + StringPool.SLASH + modelCode + ".vue";
+			}
+
+			if (StringUtil.equals(key, "sub.vue")) {
+				outputPath = outputWebDir + StringPool.SLASH + "views" + StringPool.SLASH + serviceCode + StringPool.SLASH + modelCode + "Sub.vue";
+			}
+			outputFile(new File(String.valueOf(outputPath)), objectMap, value, Boolean.TRUE);
+		});
+	}
+
+
+}

+ 10 - 0
bladex-tool/blade-starter-develop/src/main/resources/beetl.properties

@@ -0,0 +1,10 @@
+#默认配置
+ENGINE = org.beetl.core.engine.FastRuntimeEngine
+
+#开始结束占位符
+DELIMITER_PLACEHOLDER_START = ${
+DELIMITER_PLACEHOLDER_END = }
+
+#开始结束标签
+DELIMITER_STATEMENT_START = #
+DELIMITER_STATEMENT_END = null

+ 222 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/controller.java.btl

@@ -0,0 +1,222 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package ${package.Controller};
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import lombok.AllArgsConstructor;
+import javax.validation.Valid;
+
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springframework.web.bind.annotation.*;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import ${packageName!}.entity.${modelClass!}Entity;
+import ${packageName!}.vo.${modelClass!}VO;
+#if(hasWrapper) {
+import ${packageName!}.wrapper.${modelClass!}Wrapper;
+#}
+import ${packageName!}.service.I${modelClass!}Service;
+#if(isNotEmpty(superControllerClassPackage)){
+import ${superControllerClassPackage!};
+#}
+#if(templateType=="tree"){
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.springblade.core.tool.constant.BladeConstant;
+import java.util.List;
+#}
+#if(templateType=="tree"&&!hasWrapper){
+import ${packageName!}.wrapper.${modelClass!}Wrapper;
+#}
+
+/**
+ * ${codeName!} 控制器
+ *
+ * @author ${author!}
+ * @since ${date!}
+ */
+@RestController
+@AllArgsConstructor
+#if(hasServiceName) {
+@RequestMapping("${serviceName!}/${modelCode!}")
+#}else{
+@RequestMapping("/${modelCode!}")
+#}
+@Api(value = "${codeName!}", tags = "${codeName!}接口")
+#if(isNotEmpty(superControllerClass)){
+public class ${modelClass!}Controller extends ${superControllerClass!} {
+#}
+#else{
+public class ${modelClass!}Controller {
+#}
+
+	private final I${modelClass!}Service ${modelCode!}Service;
+
+#if(hasWrapper){
+	/**
+	 * ${codeName!} 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入${modelCode!}")
+	public R<${modelClass!}VO> detail(${modelClass!}Entity ${modelCode!}) {
+		${modelClass!}Entity detail = ${modelCode!}Service.getOne(Condition.getQueryWrapper(${modelCode!}));
+		return R.data(${modelClass!}Wrapper.build().entityVO(detail));
+	}
+#if(templateType=="tree"){
+	/**
+	 * ${codeName!} 树列表
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入notice")
+	public R<List<${modelClass!}VO>> list(${modelClass!}Entity ${modelCode!}, BladeUser bladeUser) {
+		QueryWrapper<${modelClass!}Entity> queryWrapper = Condition.getQueryWrapper(${modelCode!});
+		List<${modelClass!}Entity> list = ${modelCode!}Service.list((!bladeUser.getTenantId().equals(BladeConstant.ADMIN_TENANT_ID)) ? queryWrapper.lambda().eq(${modelClass!}Entity::getTenantId, bladeUser.getTenantId()) : queryWrapper);
+		return R.data(${modelClass!}Wrapper.build().treeNodeVO(list));
+	}
+#}else{
+	/**
+	 * ${codeName!} 分页
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入${modelCode!}")
+	public R<IPage<${modelClass!}VO>> list(${modelClass!}Entity ${modelCode!}, Query query) {
+		IPage<${modelClass!}Entity> pages = ${modelCode!}Service.page(Condition.getPage(query), Condition.getQueryWrapper(${modelCode!}));
+		return R.data(${modelClass!}Wrapper.build().pageVO(pages));
+	}
+#}
+#}else{
+	/**
+	 * ${codeName!} 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入${modelCode!}")
+	public R<${modelClass!}Entity> detail(${modelClass!}Entity ${modelCode!}) {
+		${modelClass!}Entity detail = ${modelCode!}Service.getOne(Condition.getQueryWrapper(${modelCode!}));
+		return R.data(detail);
+	}
+#if(templateType=="tree"){
+	/**
+	 * ${codeName!} 树列表
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入notice")
+	public R<List<${modelClass!}VO>> list(${modelClass!}Entity ${modelCode!}, BladeUser bladeUser) {
+		QueryWrapper<${modelClass!}Entity> queryWrapper = Condition.getQueryWrapper(${modelCode!});
+		List<${modelClass!}Entity> list = ${modelCode!}Service.list((!bladeUser.getTenantId().equals(BladeConstant.ADMIN_TENANT_ID)) ? queryWrapper.lambda().eq(${modelClass!}Entity::getTenantId, bladeUser.getTenantId()) : queryWrapper);
+		return R.data(${modelClass!}Wrapper.build().treeNodeVO(list));
+	}
+#}else{
+	/**
+	 * ${codeName!} 分页
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入${modelCode!}")
+	public R<IPage<${modelClass!}Entity>> list(${modelClass!}Entity ${modelCode!}, Query query) {
+		IPage<${modelClass!}Entity> pages = ${modelCode!}Service.page(Condition.getPage(query), Condition.getQueryWrapper(${modelCode!}));
+		return R.data(pages);
+	}
+#}
+#}
+
+	/**
+	 * ${codeName!} 自定义分页
+	 */
+	@GetMapping("/page")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "分页", notes = "传入${modelCode!}")
+	public R<IPage<${modelClass!}VO>> page(${modelClass!}VO ${modelCode!}, Query query) {
+		IPage<${modelClass!}VO> pages = ${modelCode!}Service.select${modelClass!}Page(Condition.getPage(query), ${modelCode!});
+		return R.data(pages);
+	}
+
+	/**
+	 * ${codeName!} 新增
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "新增", notes = "传入${modelCode!}")
+	public R save(@Valid @RequestBody ${modelClass!}Entity ${modelCode!}) {
+		return R.status(${modelCode!}Service.save(${modelCode!}));
+	}
+
+	/**
+	 * ${codeName!} 修改
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "修改", notes = "传入${modelCode!}")
+	public R update(@Valid @RequestBody ${modelClass!}Entity ${modelCode!}) {
+		return R.status(${modelCode!}Service.updateById(${modelCode!}));
+	}
+
+	/**
+	 * ${codeName!} 新增或修改
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "新增或修改", notes = "传入${modelCode!}")
+	public R submit(@Valid @RequestBody ${modelClass!}Entity ${modelCode!}) {
+		return R.status(${modelCode!}Service.saveOrUpdate(${modelCode!}));
+	}
+
+#if(isNotEmpty(superEntityClass)){
+	/**
+	 * ${codeName!} 删除
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		return R.status(${modelCode!}Service.deleteLogic(Func.toLongList(ids)));
+	}
+#}else{
+	/**
+	 * ${codeName!} 删除
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		return R.status(${modelCode!}Service.removeByIds(Func.toLongList(ids)));
+	}
+#}
+
+#if(templateType=="tree"){
+	/**
+	 * ${codeName!} 树形结构
+	 */
+	@GetMapping("/tree")
+	@ApiOperationSupport(order = 8)
+	@ApiOperation(value = "树形结构", notes = "树形结构")
+	public R<List<${modelClass!}VO>> tree(String tenantId, BladeUser bladeUser) {
+		List<${modelClass!}VO> tree = ${modelCode!}Service.tree(Func.toStrWithEmpty(tenantId, bladeUser.getTenantId()));
+		return R.data(tree);
+	}
+#}
+
+}

+ 61 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/entity.java.btl

@@ -0,0 +1,61 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package ${package.Entity!};
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+#for(x in propertyImport){
+import ${x!};
+#}
+#if(isNotEmpty(superEntityClass)){
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+#}else{
+import java.io.Serializable;
+#}
+
+/**
+ * ${codeName!} 实体类
+ *
+ * @author ${author!}
+ * @since ${date!}
+ */
+@Data
+@TableName("${model.modelTable!}")
+@ApiModel(value = "${modelClass!}对象", description = "${codeName!}")
+#if(isNotEmpty(superEntityClass)){
+@EqualsAndHashCode(callSuper = true)
+public class ${modelClass!}Entity extends TenantEntity {
+#}else{
+public class ${modelClass!}Entity implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+#}
+
+	#for(x in prototypes) {
+	#if(isNotEmpty(superEntityClass)&&x.propertyName!="id"&&x.propertyName!="createUser"&&x.propertyName!="createDept"&&x.propertyName!="createTime"&&x.propertyName!="updateUser"&&x.propertyName!="updateTime"&&x.propertyName!="status"&&x.propertyName!="isDeleted"&&x.propertyName!="tenantId"){
+	/**
+	 * ${x.comment!}
+	 */
+	@ApiModelProperty(value = "${x.comment!}")
+	private ${x.propertyType!} ${x.propertyName!};
+	#}
+	#}
+
+}

+ 6 - 11
bladex-tool/blade-starter-develop/src/main/resources/templates/entityDTO.java.vm → bladex-tool/blade-starter-develop/src/main/resources/templates/api/entityDTO.java.btl

@@ -14,26 +14,21 @@
  *  this software without specific prior written permission.
  *  Author: Chill 庄骞 (smallchill@163.com)
  */
-#set($dtoPackage=$package.Entity.replace("entity","dto"))
-package $!{dtoPackage};
+package ${strutil.replace(package.Entity,"entity","dto")};
 
-import $!{package.Entity}.$!{entity};
-#if($!{entityLombokModel})
+import ${packageName!}.entity.${modelClass!}Entity;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-#end
 
 /**
- * $!{table.comment}数据传输对象实体类
+ * ${codeName!} 数据传输对象实体类
  *
- * @author $!{author}
- * @since $!{date}
+ * @author ${author!}
+ * @since ${date!}
  */
-#if($!{entityLombokModel})
 @Data
 @EqualsAndHashCode(callSuper = true)
-#end
-public class $!{entity}DTO extends $!{entity} {
+public class ${modelClass!}DTO extends ${modelClass!}Entity {
 	private static final long serialVersionUID = 1L;
 
 }

+ 87 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/entityVO.java.btl

@@ -0,0 +1,87 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package ${strutil.replace(package.Entity,"entity","vo")};
+
+import ${packageName!}.entity.${modelClass!}Entity;
+import org.springblade.core.tool.node.INode;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+#if(templateType=="tree"){
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+
+import java.util.ArrayList;
+import java.util.List;
+#}
+
+/**
+ * ${codeName!} 视图实体类
+ *
+ * @author ${author!}
+ * @since ${date!}
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+#if(templateType=="tree"){
+public class ${modelClass!}VO extends ${modelClass!}Entity implements INode<${modelClass!}VO> {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 主键ID
+	 */
+	@JsonSerialize(using = ToStringSerializer.class)
+	private Long id;
+
+	/**
+	 * 父节点ID
+	 */
+	@JsonSerialize(using = ToStringSerializer.class)
+	private Long parentId;
+
+	/**
+ 	 * 父节点名称
+	 */
+	private String parentName;
+
+	/**
+	 * 子孙节点
+	 */
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	private List<${modelClass!}VO> children;
+
+	/**
+	 * 是否有子孙节点
+	 */
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	private Boolean hasChildren;
+
+	@Override
+	public List<${modelClass!}VO> getChildren() {
+		if (this.children == null) {
+			this.children = new ArrayList<>();
+		}
+		return this.children;
+	}
+
+}
+#}else{
+public class ${modelClass!}VO extends ${modelClass!}Entity {
+	private static final long serialVersionUID = 1L;
+
+}
+#}

+ 49 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/feign.java.btl

@@ -0,0 +1,49 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package ${strutil.replace(package.Entity,"entity","feign")};
+
+import org.springblade.core.mp.support.BladePage;
+import ${packageName!}.entity.${modelClass!}Entity;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * ${codeName!} Feign接口类
+ *
+ * @author ${author!}
+ * @since ${date!}
+ */
+@FeignClient(
+    value = "${serviceName!}"
+)
+public interface I${modelClass!}Client {
+
+    String API_PREFIX = "/client";
+    String TOP = API_PREFIX + "/top";
+
+    /**
+     * 获取${codeName!}列表
+     *
+     * @param current   页号
+     * @param size      页数
+     * @return BladePage
+     */
+    @GetMapping(TOP)
+    BladePage<${modelClass!}Entity> top(@RequestParam("current") Integer current, @RequestParam("size") Integer size);
+
+}

+ 53 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/feignclient.java.btl

@@ -0,0 +1,53 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package ${strutil.replace(package.Entity,"entity","feign")};
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import lombok.AllArgsConstructor;
+import org.springblade.core.mp.support.BladePage;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import ${packageName!}.entity.${modelClass!}Entity;
+import ${packageName!}.service.I${modelClass!}Service;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.annotations.ApiIgnore;
+
+/**
+ * ${codeName!} Feign实现类
+ *
+ * @author ${author!}
+ * @since ${date!}
+ */
+@ApiIgnore()
+@RestController
+@AllArgsConstructor
+public class ${modelClass!}Client implements I${modelClass!}Client {
+
+    private final I${modelClass!}Service ${modelCode!}Service;
+
+    @Override
+    @GetMapping(TOP)
+    public BladePage<${modelClass!}Entity> top(Integer current, Integer size) {
+        Query query = new Query();
+        query.setCurrent(current);
+        query.setSize(size);
+        IPage<${modelClass!}Entity> page = service.page(Condition.getPage(query));
+        return BladePage.of(page);
+    }
+
+}

+ 20 - 15
bladex-tool/blade-starter-develop/src/main/resources/templates/mapper.java.vm → bladex-tool/blade-starter-develop/src/main/resources/templates/api/mapper.java.btl

@@ -14,34 +14,39 @@
  *  this software without specific prior written permission.
  *  Author: Chill 庄骞 (smallchill@163.com)
  */
-package $!{package.Mapper};
+package ${package.Mapper!};
 
-import $!{package.Entity}.$!{entity};
-#set($voPackage=$package.Entity.replace("entity","vo"))
-import $!{voPackage}.$!{entity}VO;
-import $!{superMapperClassPackage};
+import ${packageName!}.entity.${modelClass!}Entity;
+import ${packageName!}.vo.${modelClass!}VO;
+import ${superMapperClassPackage!};
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import java.util.List;
 
 /**
- * $!{table.comment} Mapper 接口
+ * ${codeName!} Mapper 接口
  *
- * @author $!{author}
- * @since $!{date}
+ * @author ${author!}
+ * @since ${date!}
  */
-#if($!{kotlin})
-interface $!{table.mapperName} : $!{superMapperClass}<$!{entity}>
-#else
-public interface $!{table.mapperName} extends $!{superMapperClass}<$!{entity}> {
+public interface ${modelClass!}Mapper extends ${superMapperClass!}<${modelClass!}Entity> {
 
 	/**
 	 * 自定义分页
 	 *
 	 * @param page
-	 * @param $!{table.entityPath}
+	 * @param ${modelCode!}
 	 * @return
 	 */
-	List<$!{entity}VO> select$!{entity}Page(IPage page, $!{entity}VO $!{table.entityPath});
+	List<${modelClass!}VO> select${modelClass!}Page(IPage page, ${modelClass!}VO ${modelCode!});
+
+#if(templateType=="tree"){
+	/**
+	 * 获取树形节点
+	 *
+	 * @param tenantId
+	 * @return
+	 */
+	List<${modelClass!}VO> tree(String tenantId);
+#}
 
 }
-#end

+ 39 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/mapper.xml.btl

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="${package.Mapper!}.${modelClass!}Mapper">
+
+#if(enableCache){
+    <!-- 开启二级缓存 -->
+    <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>
+#}
+    <!-- 通用查询映射结果 -->
+    <resultMap id="${modelCode!}ResultMap" type="${package.Entity!}.${modelClass!}Entity">
+        #for(x in prototypes) {
+        <result column="${x.jdbcName!}" property="${x.propertyName!}"/>
+        #}
+    </resultMap>
+
+#if(templateType=="tree"){
+    <resultMap id="treeNodeResultMap" type="org.springblade.core.tool.node.TreeNode">
+        <id column="id" property="id"/>
+        <result column="parent_id" property="parentId"/>
+        <result column="title" property="title"/>
+        <result column="value" property="value"/>
+        <result column="key" property="key"/>
+    </resultMap>
+#}
+
+    <select id="select${modelClass!}Page" resultMap="${modelCode!}ResultMap">
+        select * from ${model.modelTable} where is_deleted = 0
+    </select>
+
+#if(templateType=="tree"){
+    <select id="tree" resultMap="treeNodeResultMap">
+        select ${treeId!} as id, ${treePid!} as parent_id, ${treeName!} as title, ${treeId!} as 'value', ${treeId!} as 'key' from ${model.modelTable!} where is_deleted = 0
+        <if test="_parameter!=null">
+            and tenant_id = \#{_parameter}
+        </if>
+    </select>
+#}
+
+</mapper>

+ 23 - 15
bladex-tool/blade-starter-develop/src/main/resources/templates/service.java.vm → bladex-tool/blade-starter-develop/src/main/resources/templates/api/service.java.btl

@@ -14,33 +14,41 @@
  *  this software without specific prior written permission.
  *  Author: Chill 庄骞 (smallchill@163.com)
  */
-package $!{package.Service};
+package ${package.Service!};
 
-import $!{package.Entity}.$!{entity};
-#set($voPackage=$package.Entity.replace("entity","vo"))
-import $!{voPackage}.$!{entity}VO;
-import $!{superServiceClassPackage};
+import ${packageName!}.entity.${modelClass!}Entity;
+import ${packageName!}.vo.${modelClass!}VO;
+import ${superServiceClassPackage!};
 import com.baomidou.mybatisplus.core.metadata.IPage;
+#if(templateType=="tree"){
+import java.util.List;
+#}
 
 /**
- * $!{table.comment} 服务类
+ * ${codeName!} 服务类
  *
- * @author $!{author}
- * @since $!{date}
+ * @author ${author!}
+ * @since ${date!}
  */
-#if($!{kotlin})
-interface $!{table.serviceName} : $!{superServiceClass}<$!{entity}>
-#else
-public interface $!{table.serviceName} extends $!{superServiceClass}<$!{entity}> {
+public interface I${modelClass!}Service extends ${superServiceClass!}<${modelClass!}Entity> {
 
 	/**
 	 * 自定义分页
 	 *
 	 * @param page
-	 * @param $!{table.entityPath}
+	 * @param ${modelCode!}
 	 * @return
 	 */
-	IPage<$!{entity}VO> select$!{entity}Page(IPage<$!{entity}VO> page, $!{entity}VO $!{table.entityPath});
+	IPage<${modelClass!}VO> select${modelClass!}Page(IPage<${modelClass!}VO> page, ${modelClass!}VO ${modelCode!});
+
+#if(templateType=="tree"){
+	/**
+	 * 树形结构
+	 *
+	 * @param tenantId
+	 * @return
+	 */
+	List<${modelClass!}VO> tree(String tenantId);
+#}
 
 }
-#end

+ 52 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/serviceImpl.java.btl

@@ -0,0 +1,52 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package ${package.ServiceImpl!};
+
+import ${packageName!}.entity.${modelClass!}Entity;
+import ${packageName!}.vo.${modelClass!}VO;
+import ${packageName!}.mapper.${model.modelClass!}Mapper;
+import ${packageName!}.service.I${model.modelClass!}Service;
+import ${superServiceImplClassPackage!};
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+#if(templateType=="tree"){
+import org.springblade.core.tool.node.ForestNodeMerger;
+import java.util.List;
+#}
+
+/**
+ * ${codeName!} 服务实现类
+ *
+ * @author ${author!}
+ * @since ${date!}
+ */
+@Service
+public class ${modelClass!}ServiceImpl extends ${superServiceImplClass!}<${modelClass!}Mapper, ${modelClass!}Entity> implements I${model.modelClass!}Service {
+
+	@Override
+	public IPage<${modelClass!}VO> select${modelClass!}Page(IPage<${modelClass!}VO> page, ${modelClass!}VO ${modelCode!}) {
+		return page.setRecords(baseMapper.select${modelClass!}Page(page, ${modelCode!}));
+	}
+
+#if(templateType=="tree"){
+	@Override
+	public List<${modelClass!}VO> tree(String tenantId) {
+		return ForestNodeMerger.merge(baseMapper.tree(tenantId));
+	}
+#}
+
+}

+ 61 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/api/wrapper.java.btl

@@ -0,0 +1,61 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package ${strutil.replace(package.Entity,"entity","wrapper")};
+
+import org.springblade.core.mp.support.BaseEntityWrapper;
+import org.springblade.core.tool.utils.BeanUtil;
+import ${packageName!}.entity.${modelClass!}Entity;
+import ${packageName!}.vo.${modelClass!}VO;
+import java.util.Objects;
+#if(templateType=="tree"){
+import org.springblade.core.tool.node.ForestNodeMerger;
+import java.util.List;
+import java.util.stream.Collectors;
+#}
+
+/**
+ * ${codeName!} 包装类,返回视图层所需的字段
+ *
+ * @author ${author!}
+ * @since ${date!}
+ */
+public class ${modelClass!}Wrapper extends BaseEntityWrapper<${modelClass!}Entity, ${modelClass!}VO>  {
+
+	public static ${modelClass!}Wrapper build() {
+		return new ${modelClass!}Wrapper();
+ 	}
+
+	@Override
+	public ${modelClass!}VO entityVO(${modelClass!}Entity ${modelCode!}) {
+		${modelClass!}VO ${modelCode!}VO = Objects.requireNonNull(BeanUtil.copy(${modelCode!}, ${modelClass!}VO.class));
+
+		//User createUser = UserCache.getUser(${modelCode!}.getCreateUser());
+		//User updateUser = UserCache.getUser(${modelCode!}.getUpdateUser());
+		//${modelCode!}VO.setCreateUserName(createUser.getName());
+		//${modelCode!}VO.setUpdateUserName(updateUser.getName());
+
+		return ${modelCode!}VO;
+	}
+
+#if(templateType=="tree"){
+	public List<${modelClass!}VO> treeNodeVO(List<${modelClass!}Entity> list) {
+		List<${modelClass!}VO> collect = list.stream().map(${modelCode!} -> BeanUtil.copy(${modelCode!}, ${modelClass!}VO.class)).collect(Collectors.toList());
+		return ForestNodeMerger.merge(collect);
+	}
+#}
+
+}

+ 0 - 181
bladex-tool/blade-starter-develop/src/main/resources/templates/controller.java.vm

@@ -1,181 +0,0 @@
-/*
- *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *
- *  Redistributions of source code must retain the above copyright notice,
- *  this list of conditions and the following disclaimer.
- *  Redistributions in binary form must reproduce the above copyright
- *  notice, this list of conditions and the following disclaimer in the
- *  documentation and/or other materials provided with the distribution.
- *  Neither the name of the dreamlu.net developer nor the names of its
- *  contributors may be used to endorse or promote products derived from
- *  this software without specific prior written permission.
- *  Author: Chill 庄骞 (smallchill@163.com)
- */
-package $!{package.Controller};
-
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
-import lombok.AllArgsConstructor;
-import javax.validation.Valid;
-
-import org.springblade.core.mp.support.Condition;
-import org.springblade.core.mp.support.Query;
-import org.springblade.core.tool.api.R;
-import org.springblade.core.tool.utils.Func;
-import org.springframework.web.bind.annotation.*;
-#if($!{superEntityClass})
-import org.springframework.web.bind.annotation.RequestParam;
-#end
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import $!{package.Entity}.$!{entity};
-#set($voPackage=$package.Entity.replace("entity","vo"))
-import $!{voPackage}.$!{entity}VO;
-#set($wrapperPackage=$package.Entity.replace("entity","wrapper"))
-#if($!{cfg.hasWrapper})
-import $!{wrapperPackage}.$!{entity}Wrapper;
-#end
-import $!{package.Service}.$!{table.serviceName};
-#if($!{superControllerClassPackage})
-import $!{superControllerClassPackage};
-#end
-#if(!$!{superEntityClass})
-#end
-
-/**
- * $!{table.comment} 控制器
- *
- * @author $!{author}
- * @since $!{date}
- */
-@RestController
-@AllArgsConstructor
-@RequestMapping("#if($!{package.ModuleName})/$!{package.ModuleName}#end/$!{cfg.entityKey}")
-@Api(value = "$!{table.comment}", tags = "$!{table.comment}接口")
-#if($!{superControllerClass})
-public class $!{table.controllerName} extends $!{superControllerClass} {
-#else
-public class $!{table.controllerName} {
-#end
-
-	private final $!{table.serviceName} $!{table.entityPath}Service;
-
-#if($!{cfg.hasWrapper})
-	/**
-	 * 详情
-	 */
-	@GetMapping("/detail")
-	@ApiOperationSupport(order = 1)
-	@ApiOperation(value = "详情", notes = "传入$!{table.entityPath}")
-	public R<$!{entity}VO> detail($!{entity} $!{table.entityPath}) {
-		$!{entity} detail = $!{table.entityPath}Service.getOne(Condition.getQueryWrapper($!{table.entityPath}));
-		return R.data($!{entity}Wrapper.build().entityVO(detail));
-	}
-
-	/**
-	 * 分页 $!{table.comment}
-	 */
-	@GetMapping("/list")
-	@ApiOperationSupport(order = 2)
-	@ApiOperation(value = "分页", notes = "传入$!{table.entityPath}")
-	public R<IPage<$!{entity}VO>> list($!{entity} $!{table.entityPath}, Query query) {
-		IPage<$!{entity}> pages = $!{table.entityPath}Service.page(Condition.getPage(query), Condition.getQueryWrapper($!{table.entityPath}));
-		return R.data($!{entity}Wrapper.build().pageVO(pages));
-	}
-
-#else
-	/**
-	 * 详情
-	 */
-	@GetMapping("/detail")
-	@ApiOperationSupport(order = 1)
-	@ApiOperation(value = "详情", notes = "传入$!{table.entityPath}")
-	public R<$!{entity}> detail($!{entity} $!{table.entityPath}) {
-		$!{entity} detail = $!{table.entityPath}Service.getOne(Condition.getQueryWrapper($!{table.entityPath}));
-		return R.data(detail);
-	}
-
-	/**
-	 * 分页 $!{table.comment}
-	 */
-	@GetMapping("/list")
-	@ApiOperationSupport(order = 2)
-	@ApiOperation(value = "分页", notes = "传入$!{table.entityPath}")
-	public R<IPage<$!{entity}>> list($!{entity} $!{table.entityPath}, Query query) {
-		IPage<$!{entity}> pages = $!{table.entityPath}Service.page(Condition.getPage(query), Condition.getQueryWrapper($!{table.entityPath}));
-		return R.data(pages);
-	}
-#end
-
-	/**
-	 * 自定义分页 $!{table.comment}
-	 */
-	@GetMapping("/page")
-	@ApiOperationSupport(order = 3)
-	@ApiOperation(value = "分页", notes = "传入$!{table.entityPath}")
-	public R<IPage<$!{entity}VO>> page($!{entity}VO $!{table.entityPath}, Query query) {
-		IPage<$!{entity}VO> pages = $!{table.entityPath}Service.select$!{entity}Page(Condition.getPage(query), $!{table.entityPath});
-		return R.data(pages);
-	}
-
-	/**
-	 * 新增 $!{table.comment}
-	 */
-	@PostMapping("/save")
-	@ApiOperationSupport(order = 4)
-	@ApiOperation(value = "新增", notes = "传入$!{table.entityPath}")
-	public R save(@Valid @RequestBody $!{entity} $!{table.entityPath}) {
-		return R.status($!{table.entityPath}Service.save($!{table.entityPath}));
-	}
-
-	/**
-	 * 修改 $!{table.comment}
-	 */
-	@PostMapping("/update")
-	@ApiOperationSupport(order = 5)
-	@ApiOperation(value = "修改", notes = "传入$!{table.entityPath}")
-	public R update(@Valid @RequestBody $!{entity} $!{table.entityPath}) {
-		return R.status($!{table.entityPath}Service.updateById($!{table.entityPath}));
-	}
-
-	/**
-	 * 新增或修改 $!{table.comment}
-	 */
-	@PostMapping("/submit")
-	@ApiOperationSupport(order = 6)
-	@ApiOperation(value = "新增或修改", notes = "传入$!{table.entityPath}")
-	public R submit(@Valid @RequestBody $!{entity} $!{table.entityPath}) {
-		return R.status($!{table.entityPath}Service.saveOrUpdate($!{table.entityPath}));
-	}
-
-	#if($!{superEntityClass})
-
-	/**
-	 * 删除 $!{table.comment}
-	 */
-	@PostMapping("/remove")
-	@ApiOperationSupport(order = 7)
-	@ApiOperation(value = "逻辑删除", notes = "传入ids")
-	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
-		return R.status($!{table.entityPath}Service.deleteLogic(Func.toLongList(ids)));
-	}
-
-	#else
-
-	/**
-	 * 删除 $!{table.comment}
-	 */
-	@PostMapping("/remove")
-	@ApiOperationSupport(order = 8)
-	@ApiOperation(value = "删除", notes = "传入ids")
-	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
-		return R.status($!{table.entityPath}Service.removeByIds(Func.toLongList(ids)));
-	}
-
-	#end
-
-}

+ 5 - 5
bladex-tool/blade-starter-develop/src/main/resources/templates/saber/api.js.vm → bladex-tool/blade-starter-develop/src/main/resources/templates/element/crud/api.js.btl

@@ -2,7 +2,7 @@ import request from '@/router/axios';
 
 export const getList = (current, size, params) => {
   return request({
-    url: '/api/$!{cfg.serviceName}/$!{cfg.entityKey}/list',
+    url: '/api/${serviceName!}/${modelCode!}/list',
     method: 'get',
     params: {
       ...params,
@@ -14,7 +14,7 @@ export const getList = (current, size, params) => {
 
 export const getDetail = (id) => {
   return request({
-    url: '/api/$!{cfg.serviceName}/$!{cfg.entityKey}/detail',
+    url: '/api/${serviceName!}/${modelCode!}/detail',
     method: 'get',
     params: {
       id
@@ -24,7 +24,7 @@ export const getDetail = (id) => {
 
 export const remove = (ids) => {
   return request({
-    url: '/api/$!{cfg.serviceName}/$!{cfg.entityKey}/remove',
+    url: '/api/${serviceName!}/${modelCode!}/remove',
     method: 'post',
     params: {
       ids,
@@ -34,7 +34,7 @@ export const remove = (ids) => {
 
 export const add = (row) => {
   return request({
-    url: '/api/$!{cfg.serviceName}/$!{cfg.entityKey}/submit',
+    url: '/api/${serviceName!}/${modelCode!}/submit',
     method: 'post',
     data: row
   })
@@ -42,7 +42,7 @@ export const add = (row) => {
 
 export const update = (row) => {
   return request({
-    url: '/api/$!{cfg.serviceName}/$!{cfg.entityKey}/submit',
+    url: '/api/${serviceName!}/${modelCode!}/submit',
     method: 'post',
     data: row
   })

+ 31 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/element/crud/const.js.btl

@@ -0,0 +1,31 @@
+export default {
+  size: 'small',
+  expand: false,
+  index: true,
+  border: true,
+  selection: true,
+  column: [
+#for(x in prototypes) {
+    {
+      label: "${x.comment!}",
+      prop: "${x.propertyName!}",
+#if(strutil.contain(x.componentType,"date")||strutil.contain(x.componentType,"time")){
+      format: "yyyy-MM-dd hh:mm:ss",
+      valueFormat: "yyyy-MM-dd hh:mm:ss",
+#}
+#if(x.isForm==0){
+      display: false,
+#}
+#if(x.isRow==1){
+      span: 24,
+#}
+#if(x.isList==0){
+      hide: true,
+#}
+#if(x.isQuery==1){
+      search: true,
+#}
+    },
+#}
+  ]
+}

+ 347 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/element/crud/crud.vue.btl

@@ -0,0 +1,347 @@
+<template>
+  <basic-container>
+    <div class="avue-crud">
+      <el-row :hidden="!search" style="padding:5px">
+        <!-- 查询模块 -->
+        <el-form :inline="true" :size="option.size" :model="query">
+          <template>
+#for(x in prototypes) {
+  #if(x.isQuery==1){
+            <el-form-item label="字段">
+              <el-input v-model="query.${x.propertyName!}" placeholder="请输入${x.comment!}"></el-input>
+            </el-form-item>
+  #}
+#}
+          </template>
+          <!-- 查询按钮 -->
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" @click="searchChange">搜索</el-button>
+            <el-button icon="el-icon-delete" @click="searchReset()">清空</el-button>
+          </el-form-item>
+        </el-form>
+      </el-row>
+      <el-row>
+        <div class="avue-crud__menu">
+          <!-- 头部左侧按钮模块 -->
+          <div class="avue-crud__left">
+            <el-button :size="option.size" type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
+            <el-button :size="option.size" type="danger" icon="el-icon-delete" @click="handleDelete" plain>删除
+            </el-button>
+          </div>
+          <!-- 头部右侧按钮模块 -->
+          <div class="avue-crud__right">
+            <el-button :size="option.size" icon="el-icon-refresh" @click="searchChange" circle></el-button>
+            <el-button :size="option.size" icon="el-icon-search" @click="searchHide" circle></el-button>
+          </div>
+        </div>
+      </el-row>
+      <el-row>
+        <!-- 列表模块 -->
+        <el-table ref="table" v-loading="loading" :size="option.size" @selection-change="selectionChange" :data="data"
+                  style="width: 100%"
+                  :border="option.border">
+          <el-table-column type="selection" v-if="option.selection" width="55" align="center"></el-table-column>
+          <el-table-column type="expand" v-if="option.expand" align="center"></el-table-column>
+          <el-table-column v-if="option.index" label="\#" type="index" width="50" align="center">
+          </el-table-column>
+          <template v-for="(item,index) in option.column">
+            <!-- table字段 -->
+            <el-table-column v-if="item.hide!==true"
+                             :prop="item.prop"
+                             :label="item.label"
+                             :width="item.width"
+                             :key="index">
+            </el-table-column>
+          </template>
+          <!-- 操作栏模块 -->
+          <el-table-column prop="menu" label="操作" :width="180" align="center">
+            <template slot-scope="{row}">
+              <el-button :size="option.size" type="text" icon="el-icon-view" @click="handleView(row)">查看</el-button>
+              <el-button :size="option.size" type="text" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
+              <el-button :size="option.size" type="text" icon="el-icon-delete" @click="rowDel(row)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+      <el-row>
+        <!-- 分页模块 -->
+        <el-pagination
+          align="right" background
+          @size-change="sizeChange"
+          @current-change="currentChange"
+          :current-page="page.currentPage"
+          :page-sizes="[10, 20, 30, 40, 50, 100]"
+          :page-size="page.pageSize"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="page.total">
+        </el-pagination>
+      </el-row>
+      <!-- 表单模块 -->
+      <el-dialog :title="title" :visible.sync="box" width="50%" :before-close="beforeClose" append-to-body>
+        <el-form :disabled="view" :size="option.size" ref="form" :model="form" label-width="80px">
+          <!-- 表单字段 -->
+#for(x in prototypes) {
+  #if(x.isForm!=0){
+    #if(x.componentType=="input"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-input v-model="form.${x.propertyName!}" placeholder="请输入${x.comment!}"/>
+          </el-form-item>
+    #}else if(x.componentType=="textarea"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-input type="textarea" :rows="5" v-model="form.${x.propertyName!}" placeholder="请输入${x.comment!}"/>
+          </el-form-item>
+    #}else if(x.componentType=="select"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-select v-model="form.${x.propertyName!}" clearable placeholder="请选择${x.comment!}">
+                  <el-option
+                    v-for="item in ${x.propertyName!}Data"
+                    :key="item.dictKey"
+                    :label="item.dictValue"
+                    :value="item.dictKey">
+                  </el-option>
+              </el-select>
+          </el-form-item>
+    #}else if(x.componentType=="tree"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-select v-model="form.${x.propertyName!}" clearable placeholder="请选择${x.comment!}">
+                  <el-option
+                    v-for="item in ${x.propertyName!}Data"
+                    :key="item.dictKey"
+                    :label="item.dictValue"
+                    :value="item.dictKey">
+                  </el-option>
+              </el-select>
+          </el-form-item>
+    #}else if(x.componentType=="radio"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-radio-group v-model="form.${x.propertyName!}">
+                <el-radio v-for="(item,index) in ${x.propertyName!}Data" :key="index" :label="item.dictKey">
+                 {{item.dictValue}}
+                </el-radio>
+              </el-radio-group>
+          </el-form-item>
+    #}else if(x.componentType=="checkbox"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-checkbox-group v-model="form.${x.propertyName!}">
+                <el-checkbox v-for="(item,index) in ${x.propertyName!}Data" :label="item.dictValue" :key="index">{{item.dictValue}}</el-checkbox>
+              </el-checkbox-group>
+          </el-form-item>
+    #}else if(x.componentType=="switch"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-switch v-model="form.${x.propertyName!}" </el-switch>
+          </el-form-item>
+    #}else if(x.componentType=="date"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-date-picker v-model="form.${x.propertyName!}" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择${x.comment!}"></el-date-picker>
+          </el-form-item>
+    #}
+  #}
+#}
+        </el-form>
+        <!-- 表单按钮 -->
+        <span v-if="!view" slot="footer" class="dialog-footer">
+          <el-button type="primary" icon="el-icon-circle-check" :size="option.size" @click="handleSubmit">提 交</el-button>
+          <el-button icon="el-icon-circle-close" :size="option.size" @click="box = false">取 消</el-button>
+        </span>
+      </el-dialog>
+    </div>
+  </basic-container>
+</template>
+
+<script>
+  import {getList, getDetail, add, update, remove} from "@/api/${serviceCode!}/${modelCode!}";
+  import option from "@/const/${serviceCode!}/${modelCode!}";
+  import {mapGetters} from "vuex";
+  import {getDictionary} from '@/api/system/dict'
+
+export default {
+  data() {
+    return {
+      // 弹框标题
+      title: '',
+      // 是否展示弹框
+      box: false,
+      // 是否显示查询
+      search: true,
+      // 加载中
+      loading: true,
+      // 是否为查看模式
+      view: false,
+      // 查询信息
+      query: {},
+      // 分页信息
+      page: {
+        currentPage: 1,
+        pageSize: 10,
+        total: 40
+      },
+      // 表单数据
+      form: {},
+      // 选择行
+      selectionList: [],
+      // 表单配置
+      option: option,
+      // 表单列表
+      data: [],
+#for(x in prototypes) {
+    #if(isNotEmpty(x.dictCode)){
+      // ${x.comment!}字典数据
+      ${x.propertyName!}Data: [],
+    #}
+#}
+    }
+  },
+  mounted() {
+    this.init();
+    this.onLoad(this.page);
+  },
+  computed: {
+    ...mapGetters(["permission"]),
+    ids() {
+      let ids = [];
+      this.selectionList.forEach(ele => {
+        ids.push(ele.id);
+      });
+      return ids.join(",");
+    }
+  },
+  methods: {
+    init() {
+#for(x in prototypes) {
+    #if(isNotEmpty(x.dictCode)){
+      getDictionary({code: '${x.dictCode!}'}).then(res => {
+        this.${x.propertyName!}Data = res.data.data;
+      });
+    #}
+#}
+    },
+    searchHide() {
+      this.search = !this.search;
+    },
+    searchChange() {
+      this.onLoad(this.page);
+    },
+    searchReset() {
+      this.query = {};
+      this.page.currentPage = 1;
+      this.onLoad(this.page);
+    },
+    handleSubmit() {
+      if (!this.form.id) {
+        add(this.form).then(() => {
+          this.box = false;
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        });
+      } else {
+        update(this.form).then(() => {
+          this.box = false;
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        })
+      }
+    },
+    handleAdd() {
+      this.title = '新增'
+      this.form = {}
+      this.box = true
+    },
+    handleEdit(row) {
+      this.title = '编辑'
+      this.box = true
+      getDetail(row.id).then(res => {
+        this.form = res.data.data;
+      });
+    },
+    handleView(row) {
+      this.title = '查看'
+      this.view = true;
+      this.box = true;
+      getDetail(row.id).then(res => {
+        this.form = res.data.data;
+      });
+    },
+    handleDelete() {
+      if (this.selectionList.length === 0) {
+        this.$message.warning("请选择至少一条数据");
+        return;
+      }
+      this.$confirm("确定将选择数据删除?", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          return remove(this.ids);
+        })
+        .then(() => {
+          this.selectionClear();
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        });
+    },
+    rowDel(row) {
+      this.$confirm("确定将选择数据删除?", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          return remove(row.id);
+        })
+        .then(() => {
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        });
+    },
+    beforeClose(done) {
+      done()
+      this.form = {};
+      this.view = false;
+    },
+    selectionChange(list) {
+      this.selectionList = list;
+    },
+    selectionClear() {
+      this.selectionList = [];
+      this.$refs.table.clearSelection();
+    },
+    currentChange(currentPage) {
+      this.page.currentPage = currentPage;
+      this.onLoad(this.page);
+    },
+    sizeChange(pageSize) {
+      this.page.pageSize = pageSize;
+      this.onLoad(this.page);
+    },
+    onLoad(page, params = {}) {
+      this.loading = true;
+      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
+        const data = res.data.data;
+        this.page.total = data.total;
+        this.data = data.records;
+        this.loading = false;
+        this.selectionClear();
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.el-pagination {
+  margin-top: 20px;
+}
+</style>

+ 50 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/element/sub/api.js.btl

@@ -0,0 +1,50 @@
+import request from '@/router/axios';
+
+export const getList = (current, size, params) => {
+  return request({
+    url: '/api/${serviceName!}/${modelCode!}/list',
+    method: 'get',
+    params: {
+      ...params,
+      current,
+      size,
+    }
+  })
+}
+
+export const getDetail = (id) => {
+  return request({
+    url: '/api/${serviceName!}/${modelCode!}/detail',
+    method: 'get',
+    params: {
+      id
+    }
+  })
+}
+
+export const remove = (ids) => {
+  return request({
+    url: '/api/${serviceName!}/${modelCode!}/remove',
+    method: 'post',
+    params: {
+      ids,
+    }
+  })
+}
+
+export const add = (row) => {
+  return request({
+    url: '/api/${serviceName!}/${modelCode!}/submit',
+    method: 'post',
+    data: row
+  })
+}
+
+export const update = (row) => {
+  return request({
+    url: '/api/${serviceName!}/${modelCode!}/submit',
+    method: 'post',
+    data: row
+  })
+}
+

+ 31 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/element/sub/const.js.btl

@@ -0,0 +1,31 @@
+export default {
+  size: 'small',
+  expand: false,
+  index: true,
+  border: true,
+  selection: true,
+  column: [
+#for(x in prototypes) {
+    {
+      label: "${x.comment!}",
+      prop: "${x.propertyName!}",
+#if(strutil.contain(x.componentType,"date")||strutil.contain(x.componentType,"time")){
+      format: "yyyy-MM-dd hh:mm:ss",
+      valueFormat: "yyyy-MM-dd hh:mm:ss",
+#}
+#if(x.isForm==0){
+      display: false,
+#}
+#if(x.isRow==1){
+      span: 24,
+#}
+#if(x.isList==0){
+      hide: true,
+#}
+#if(x.isQuery==1){
+      search: true,
+#}
+    },
+#}
+  ]
+}

+ 375 - 0
bladex-tool/blade-starter-develop/src/main/resources/templates/element/sub/crud.vue.btl

@@ -0,0 +1,375 @@
+<template>
+  <basic-container>
+    <div class="avue-crud">
+      <el-row :hidden="!search" style="padding:5px">
+        <!-- 查询模块 -->
+        <el-form :inline="true" :size="option.size" :model="query">
+          <template>
+#for(x in prototypes) {
+  #if(x.isQuery==1){
+            <el-form-item label="字段">
+              <el-input v-model="query.${x.propertyName!}" placeholder="请输入${x.comment!}"></el-input>
+            </el-form-item>
+  #}
+#}
+          </template>
+          <!-- 查询按钮 -->
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" @click="searchChange">搜索</el-button>
+            <el-button icon="el-icon-delete" @click="searchReset()">清空</el-button>
+          </el-form-item>
+        </el-form>
+      </el-row>
+      <el-row>
+        <div class="avue-crud__menu">
+          <!-- 头部左侧按钮模块 -->
+          <div class="avue-crud__left">
+            <el-button :size="option.size" type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
+            <el-button :size="option.size" type="danger" icon="el-icon-delete" @click="handleDelete" plain>删除
+            </el-button>
+          </div>
+          <!-- 头部右侧按钮模块 -->
+          <div class="avue-crud__right">
+            <el-button :size="option.size" icon="el-icon-refresh" @click="searchChange" circle></el-button>
+            <el-button :size="option.size" icon="el-icon-search" @click="searchHide" circle></el-button>
+          </div>
+        </div>
+      </el-row>
+      <el-row>
+        <!-- 列表模块 -->
+        <el-table ref="table" v-loading="loading" :size="option.size" @selection-change="selectionChange" :data="data"
+                  style="width: 100%"
+                  :border="option.border">
+          <el-table-column type="selection" v-if="option.selection" width="55" align="center"></el-table-column>
+          <el-table-column type="expand" v-if="option.expand" align="center"></el-table-column>
+          <el-table-column v-if="option.index" label="\#" type="index" width="50" align="center">
+          </el-table-column>
+          <template v-for="(item,index) in option.column">
+            <!-- table字段 -->
+            <el-table-column v-if="item.hide!==true"
+                             :prop="item.prop"
+                             :label="item.label"
+                             :width="item.width"
+                             :key="index">
+            </el-table-column>
+          </template>
+          <!-- 操作栏模块 -->
+          <el-table-column prop="menu" label="操作" :width="300" align="center">
+            <template slot-scope="{row}">
+              <el-button :size="option.size" type="text" icon="el-icon-view" @click="handleView(row)">查看</el-button>
+              <el-button :size="option.size" type="text" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
+              <el-button :size="option.size" type="text" icon="el-icon-delete" @click="rowDel(row)">删除</el-button>
+              <el-button :size="option.size" type="text" icon="el-icon-setting" @click="handleDrawer(row)">子表配置</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+      <el-row>
+        <!-- 分页模块 -->
+        <el-pagination
+          align="right" background
+          @size-change="sizeChange"
+          @current-change="currentChange"
+          :current-page="page.currentPage"
+          :page-sizes="[10, 20, 30, 40, 50, 100]"
+          :page-size="page.pageSize"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="page.total">
+        </el-pagination>
+      </el-row>
+      <!-- 表单模块 -->
+      <el-dialog :title="title" :visible.sync="box" width="50%" :before-close="beforeClose" append-to-body>
+        <el-form :disabled="view" :size="option.size" ref="form" :model="form" label-width="80px">
+          <!-- 表单字段 -->
+#for(x in prototypes) {
+  #if(x.isForm!=0){
+    #if(x.componentType=="input"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-input v-model="form.${x.propertyName!}" placeholder="请输入${x.comment!}"/>
+          </el-form-item>
+    #}else if(x.componentType=="textarea"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-input type="textarea" :rows="5" v-model="form.${x.propertyName!}" placeholder="请输入${x.comment!}"/>
+          </el-form-item>
+    #}else if(x.componentType=="select"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-select v-model="form.${x.propertyName!}" clearable placeholder="请选择${x.comment!}">
+                  <el-option
+                    v-for="item in ${x.propertyName!}Data"
+                    :key="item.dictKey"
+                    :label="item.dictValue"
+                    :value="item.dictKey">
+                  </el-option>
+              </el-select>
+          </el-form-item>
+    #}else if(x.componentType=="tree"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-select v-model="form.${x.propertyName!}" clearable placeholder="请选择${x.comment!}">
+                  <el-option
+                    v-for="item in ${x.propertyName!}Data"
+                    :key="item.dictKey"
+                    :label="item.dictValue"
+                    :value="item.dictKey">
+                  </el-option>
+              </el-select>
+          </el-form-item>
+    #}else if(x.componentType=="radio"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-radio-group v-model="form.${x.propertyName!}">
+                <el-radio v-for="(item,index) in ${x.propertyName!}Data" :key="index" :label="item.dictKey">
+                 {{item.dictValue}}
+                </el-radio>
+              </el-radio-group>
+          </el-form-item>
+    #}else if(x.componentType=="checkbox"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+              <el-checkbox-group v-model="form.${x.propertyName!}">
+                <el-checkbox v-for="(item,index) in ${x.propertyName!}Data" :label="item.dictValue" :key="index">{{item.dictValue}}</el-checkbox>
+              </el-checkbox-group>
+          </el-form-item>
+    #}else if(x.componentType=="switch"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-switch v-model="form.${x.propertyName!}" </el-switch>
+          </el-form-item>
+    #}else if(x.componentType=="date"){
+          <el-form-item label="${x.comment!}" prop="${x.propertyName!}">
+            <el-date-picker v-model="form.${x.propertyName!}" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择${x.comment!}"></el-date-picker>
+          </el-form-item>
+    #}
+  #}
+#}
+        </el-form>
+        <!-- 表单按钮 -->
+        <span v-if="!view" slot="footer" class="dialog-footer">
+          <el-button type="primary" icon="el-icon-circle-check" :size="option.size" @click="handleSubmit">提 交</el-button>
+          <el-button icon="el-icon-circle-close" :size="option.size" @click="box = false">取 消</el-button>
+        </span>
+      </el-dialog>
+      <el-drawer
+        title="子表操作"
+        append-to-body
+        size="60%"
+        :visible.sync="drawer"
+        :direction="direction"
+        :before-close="handleDrawerClose">
+            <${subModel.modelClass!}Sub :mainId="${modelCode!}Id"></${subModel.modelClass!}Sub>
+      </el-drawer>
+    </div>
+  </basic-container>
+</template>
+
+<script>
+  import {getList, getDetail, add, update, remove} from "@/api/${serviceCode!}/${modelCode!}";
+  import option from "@/const/${serviceCode!}/${modelCode!}";
+  import {mapGetters} from "vuex";
+  import {getDictionary} from '@/api/system/dict'
+  import ${subModel.modelClass!}Sub from "@/views/${serviceCode!}/${subModel.modelCode!}Sub";
+
+export default {
+  components:{
+    ${subModel.modelClass!}Sub
+  },
+  data() {
+    return {
+      // 主键
+      ${modelCode!}Id: '',
+      // 弹框标题
+      title: '',
+      // 是否展示弹框
+      box: false,
+      // 是否展示抽屉
+      drawer: false,
+      // 抽屉方向
+      direction: 'rtl',
+      // 是否显示查询
+      search: true,
+      // 加载中
+      loading: true,
+      // 是否为查看模式
+      view: false,
+      // 查询信息
+      query: {},
+      // 分页信息
+      page: {
+        currentPage: 1,
+        pageSize: 10,
+        total: 40
+      },
+      // 表单数据
+      form: {},
+      // 选择行
+      selectionList: [],
+      // 表单配置
+      option: option,
+      // 表单列表
+      data: [],
+#for(x in prototypes) {
+    #if(isNotEmpty(x.dictCode)){
+      // ${x.comment!}字典数据
+      ${x.propertyName!}Data: [],
+    #}
+#}
+    }
+  },
+  mounted() {
+    this.init();
+    this.onLoad(this.page);
+  },
+  computed: {
+    ...mapGetters(["permission"]),
+    ids() {
+      let ids = [];
+      this.selectionList.forEach(ele => {
+        ids.push(ele.id);
+      });
+      return ids.join(",");
+    }
+  },
+  methods: {
+    init() {
+#for(x in prototypes) {
+    #if(isNotEmpty(x.dictCode)){
+      getDictionary({code: '${x.dictCode!}'}).then(res => {
+        this.${x.propertyName!}Data = res.data.data;
+      });
+    #}
+#}
+    },
+    searchHide() {
+      this.search = !this.search;
+    },
+    searchChange() {
+      this.onLoad(this.page);
+    },
+    searchReset() {
+      this.query = {};
+      this.page.currentPage = 1;
+      this.onLoad(this.page);
+    },
+    handleSubmit() {
+      if (!this.form.id) {
+        add(this.form).then(() => {
+          this.box = false;
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        });
+      } else {
+        update(this.form).then(() => {
+          this.box = false;
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        })
+      }
+    },
+    handleAdd() {
+      this.title = '新增'
+      this.form = {}
+      this.box = true
+    },
+    handleEdit(row) {
+      this.title = '编辑'
+      this.box = true
+      getDetail(row.id).then(res => {
+        this.form = res.data.data;
+      });
+    },
+    handleView(row) {
+      this.title = '查看'
+      this.view = true;
+      this.box = true;
+      getDetail(row.id).then(res => {
+        this.form = res.data.data;
+      });
+    },
+    handleDrawer(row) {
+      this.${modelCode!}Id = row.id;
+      this.drawer = true;
+    },
+    handleDrawerClose(){
+      this.${modelCode!}Id = '';
+      this.drawer = false;
+    },
+    handleDelete() {
+      if (this.selectionList.length === 0) {
+        this.$message.warning("请选择至少一条数据");
+        return;
+      }
+      this.$confirm("确定将选择数据删除?", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          return remove(this.ids);
+        })
+        .then(() => {
+          this.selectionClear();
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        });
+    },
+    rowDel(row) {
+      this.$confirm("确定将选择数据删除?", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          return remove(row.id);
+        })
+        .then(() => {
+          this.onLoad(this.page);
+          this.$message({
+            type: "success",
+            message: "操作成功!"
+          });
+        });
+    },
+    beforeClose(done) {
+      done()
+      this.form = {};
+      this.view = false;
+    },
+    selectionChange(list) {
+      this.selectionList = list;
+    },
+    selectionClear() {
+      this.selectionList = [];
+      this.$refs.table.clearSelection();
+    },
+    currentChange(currentPage) {
+      this.page.currentPage = currentPage;
+      this.onLoad(this.page);
+    },
+    sizeChange(pageSize) {
+      this.page.pageSize = pageSize;
+      this.onLoad(this.page);
+    },
+    onLoad(page, params = {}) {
+      this.loading = true;
+      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
+        const data = res.data.data;
+        this.page.total = data.total;
+        this.data = data.records;
+        this.loading = false;
+        this.selectionClear();
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.el-pagination {
+  margin-top: 20px;
+}
+</style>

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov