瀏覽代碼

粤菜基础代码

lijj 2 年之前
父節點
當前提交
1ce89cd045
共有 100 個文件被更改,包括 7787 次插入17 次删除
  1. 0 14
      .gitignore
  2. 0 3
      README.md
  3. 21 0
      admin/.editorconfig
  4. 27 0
      admin/.gitignore
  5. 15 0
      admin/Dockerfile
  6. 1 0
      admin/README.md
  7. 276 0
      admin/pom.xml
  8. 38 0
      admin/src/main/java/org/springblade/Application.java
  9. 61 0
      admin/src/main/java/org/springblade/common/cache/CacheNames.java
  10. 116 0
      admin/src/main/java/org/springblade/common/cache/DictBizCache.java
  11. 143 0
      admin/src/main/java/org/springblade/common/cache/DictCache.java
  12. 62 0
      admin/src/main/java/org/springblade/common/cache/ParamCache.java
  13. 57 0
      admin/src/main/java/org/springblade/common/cache/RegionCache.java
  14. 317 0
      admin/src/main/java/org/springblade/common/cache/SysCache.java
  15. 77 0
      admin/src/main/java/org/springblade/common/cache/UserCache.java
  16. 70 0
      admin/src/main/java/org/springblade/common/config/BladeConfiguration.java
  17. 58 0
      admin/src/main/java/org/springblade/common/config/BladeLogConfiguration.java
  18. 26 0
      admin/src/main/java/org/springblade/common/config/BladePreviewConfiguration.java
  19. 43 0
      admin/src/main/java/org/springblade/common/config/BladeReportConfiguration.java
  20. 120 0
      admin/src/main/java/org/springblade/common/config/SwaggerConfiguration.java
  21. 84 0
      admin/src/main/java/org/springblade/common/constant/CommonConstant.java
  22. 38 0
      admin/src/main/java/org/springblade/common/constant/DictConstant.java
  23. 93 0
      admin/src/main/java/org/springblade/common/constant/LauncherConstant.java
  24. 66 0
      admin/src/main/java/org/springblade/common/constant/TenantConstant.java
  25. 39 0
      admin/src/main/java/org/springblade/common/enums/DictBizEnum.java
  26. 95 0
      admin/src/main/java/org/springblade/common/enums/DictEnum.java
  27. 60 0
      admin/src/main/java/org/springblade/common/event/ApiLogListener.java
  28. 57 0
      admin/src/main/java/org/springblade/common/event/ErrorLogListener.java
  29. 58 0
      admin/src/main/java/org/springblade/common/event/UsualLogListener.java
  30. 66 0
      admin/src/main/java/org/springblade/common/filter/PreviewFilter.java
  31. 44 0
      admin/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java
  32. 26 0
      admin/src/main/java/org/springblade/common/utils/CommonUtil.java
  33. 97 0
      admin/src/main/java/org/springblade/modules/auth/endpoint/BladeSocialEndpoint.java
  34. 149 0
      admin/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java
  35. 50 0
      admin/src/main/java/org/springblade/modules/auth/enums/UserEnum.java
  36. 128 0
      admin/src/main/java/org/springblade/modules/auth/granter/CaptchaTokenGranter.java
  37. 117 0
      admin/src/main/java/org/springblade/modules/auth/granter/PasswordTokenGranter.java
  38. 114 0
      admin/src/main/java/org/springblade/modules/auth/granter/RefreshTokenGranter.java
  39. 92 0
      admin/src/main/java/org/springblade/modules/auth/granter/SocialTokenGranter.java
  40. 36 0
      admin/src/main/java/org/springblade/modules/auth/provider/ITokenGranter.java
  41. 66 0
      admin/src/main/java/org/springblade/modules/auth/provider/TokenGranterBuilder.java
  42. 32 0
      admin/src/main/java/org/springblade/modules/auth/provider/TokenParameter.java
  43. 171 0
      admin/src/main/java/org/springblade/modules/auth/utils/TokenUtil.java
  44. 212 0
      admin/src/main/java/org/springblade/modules/desk/controller/DashBoardController.java
  45. 138 0
      admin/src/main/java/org/springblade/modules/desk/controller/NoticeController.java
  46. 64 0
      admin/src/main/java/org/springblade/modules/desk/entity/Notice.java
  47. 50 0
      admin/src/main/java/org/springblade/modules/desk/mapper/NoticeMapper.java
  48. 53 0
      admin/src/main/java/org/springblade/modules/desk/mapper/NoticeMapper.xml
  49. 39 0
      admin/src/main/java/org/springblade/modules/desk/service/INoticeService.java
  50. 43 0
      admin/src/main/java/org/springblade/modules/desk/service/impl/NoticeServiceImpl.java
  51. 23 0
      admin/src/main/java/org/springblade/modules/desk/vo/NoticeVO.java
  52. 64 0
      admin/src/main/java/org/springblade/modules/desk/wrapper/NoticeWrapper.java
  53. 193 0
      admin/src/main/java/org/springblade/modules/develop/controller/CodeController.java
  54. 128 0
      admin/src/main/java/org/springblade/modules/develop/controller/DatasourceController.java
  55. 251 0
      admin/src/main/java/org/springblade/modules/develop/controller/ModelController.java
  56. 137 0
      admin/src/main/java/org/springblade/modules/develop/controller/ModelPrototypeController.java
  57. 42 0
      admin/src/main/java/org/springblade/modules/develop/dto/ModelDTO.java
  58. 180 0
      admin/src/main/java/org/springblade/modules/develop/entity/Code.java
  59. 71 0
      admin/src/main/java/org/springblade/modules/develop/entity/Datasource.java
  60. 74 0
      admin/src/main/java/org/springblade/modules/develop/entity/Model.java
  61. 119 0
      admin/src/main/java/org/springblade/modules/develop/entity/ModelPrototype.java
  62. 29 0
      admin/src/main/java/org/springblade/modules/develop/mapper/CodeMapper.java
  63. 22 0
      admin/src/main/java/org/springblade/modules/develop/mapper/CodeMapper.xml
  64. 29 0
      admin/src/main/java/org/springblade/modules/develop/mapper/DatasourceMapper.java
  65. 22 0
      admin/src/main/java/org/springblade/modules/develop/mapper/DatasourceMapper.xml
  66. 29 0
      admin/src/main/java/org/springblade/modules/develop/mapper/ModelMapper.java
  67. 27 0
      admin/src/main/java/org/springblade/modules/develop/mapper/ModelMapper.xml
  68. 29 0
      admin/src/main/java/org/springblade/modules/develop/mapper/ModelPrototypeMapper.java
  69. 35 0
      admin/src/main/java/org/springblade/modules/develop/mapper/ModelPrototypeMapper.xml
  70. 38 0
      admin/src/main/java/org/springblade/modules/develop/service/ICodeService.java
  71. 29 0
      admin/src/main/java/org/springblade/modules/develop/service/IDatasourceService.java
  72. 47 0
      admin/src/main/java/org/springblade/modules/develop/service/IModelPrototypeService.java
  73. 39 0
      admin/src/main/java/org/springblade/modules/develop/service/IModelService.java
  74. 40 0
      admin/src/main/java/org/springblade/modules/develop/service/impl/CodeServiceImpl.java
  75. 33 0
      admin/src/main/java/org/springblade/modules/develop/service/impl/DatasourceServiceImpl.java
  76. 55 0
      admin/src/main/java/org/springblade/modules/develop/service/impl/ModelPrototypeServiceImpl.java
  77. 67 0
      admin/src/main/java/org/springblade/modules/develop/service/impl/ModelServiceImpl.java
  78. 65 0
      admin/src/main/java/org/springblade/modules/resource/builder/oss/AliOssBuilder.java
  79. 50 0
      admin/src/main/java/org/springblade/modules/resource/builder/oss/MinioOssBuilder.java
  80. 159 0
      admin/src/main/java/org/springblade/modules/resource/builder/oss/OssBuilder.java
  81. 52 0
      admin/src/main/java/org/springblade/modules/resource/builder/oss/QiniuOssBuilder.java
  82. 66 0
      admin/src/main/java/org/springblade/modules/resource/builder/oss/TencentOssBuilder.java
  83. 50 0
      admin/src/main/java/org/springblade/modules/resource/builder/sms/AliSmsBuilder.java
  84. 47 0
      admin/src/main/java/org/springblade/modules/resource/builder/sms/QiniuSmsBuilder.java
  85. 157 0
      admin/src/main/java/org/springblade/modules/resource/builder/sms/SmsBuilder.java
  86. 46 0
      admin/src/main/java/org/springblade/modules/resource/builder/sms/TencentSmsBuilder.java
  87. 44 0
      admin/src/main/java/org/springblade/modules/resource/builder/sms/YunpianSmsBuilder.java
  88. 44 0
      admin/src/main/java/org/springblade/modules/resource/config/BladeOssConfiguration.java
  89. 47 0
      admin/src/main/java/org/springblade/modules/resource/config/BladeSmsConfiguration.java
  90. 128 0
      admin/src/main/java/org/springblade/modules/resource/controller/AttachController.java
  91. 152 0
      admin/src/main/java/org/springblade/modules/resource/controller/OssController.java
  92. 153 0
      admin/src/main/java/org/springblade/modules/resource/controller/SmsController.java
  93. 247 0
      admin/src/main/java/org/springblade/modules/resource/endpoint/OssEndpoint.java
  94. 179 0
      admin/src/main/java/org/springblade/modules/resource/endpoint/SmsEndpoint.java
  95. 71 0
      admin/src/main/java/org/springblade/modules/resource/entity/Attach.java
  96. 88 0
      admin/src/main/java/org/springblade/modules/resource/entity/Oss.java
  97. 82 0
      admin/src/main/java/org/springblade/modules/resource/entity/Sms.java
  98. 62 0
      admin/src/main/java/org/springblade/modules/resource/enums/SmsCodeEnum.java
  99. 41 0
      admin/src/main/java/org/springblade/modules/resource/mapper/AttachMapper.java
  100. 0 0
      admin/src/main/java/org/springblade/modules/resource/mapper/AttachMapper.xml

+ 0 - 14
.gitignore

@@ -1,14 +0,0 @@
-# ---> Java
-*.class
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
-*.jar
-*.war
-*.ear
-
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
-

+ 0 - 3
README.md

@@ -1,3 +0,0 @@
-# ycwf
-
-粤菜文化

+ 21 - 0
admin/.editorconfig

@@ -0,0 +1,21 @@
+# http://editorconfig.org
+root = true
+
+# 空格替代Tab缩进在各种编辑工具下效果一致
+[*]
+indent_style = space
+indent_size = 4
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.java]
+indent_style = tab
+
+[*.{json,yml}]
+indent_size = 2
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 27 - 0
admin/.gitignore

@@ -0,0 +1,27 @@
+# maven #
+target
+
+logs
+
+# windows #
+Thumbs.db
+
+# Mac #
+.DS_Store
+
+# eclipse #
+.settings
+.project
+.classpath
+.log
+*.class
+
+# idea #
+.idea
+*.iml
+
+# Package Files #
+*.jar
+*.war
+*.ear
+/target

+ 15 - 0
admin/Dockerfile

@@ -0,0 +1,15 @@
+FROM bladex/alpine-java:openjdk8-openj9_cn_slim
+
+MAINTAINER smallchill@163.com
+
+RUN mkdir -p /blade
+
+WORKDIR /blade
+
+EXPOSE 8800
+
+ADD ./target/blade-api.jar ./app.jar
+
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=test"]

+ 1 - 0
admin/README.md

@@ -0,0 +1 @@
+## 综合阅卷平台后台

+ 276 - 0
admin/pom.xml

@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.springblade</groupId>
+    <artifactId>MingCai</artifactId>
+    <packaging>jar</packaging>
+    <version>3.1.0.RELEASE</version>
+
+    <properties>
+        <bladex.project.id>blade-api</bladex.project.id>
+        <bladex.project.version>3.1.0.RELEASE</bladex.project.version>
+
+        <java.version>1.8</java.version>
+        <maven.plugin.version>3.8.1</maven.plugin.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+        <!-- 推荐使用Harbor -->
+        <docker.registry.url>192.168.0.157</docker.registry.url>
+        <docker.registry.host>http://${docker.registry.url}:2375</docker.registry.host>
+        <docker.username>admin</docker.username>
+        <docker.password>Harbor12345</docker.password>
+        <docker.namespace>blade</docker.namespace>
+        <docker.plugin.version>1.4.13</docker.plugin.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springblade.platform</groupId>
+                <artifactId>blade-bom</artifactId>
+                <version>${bladex.project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- Blade -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-boot</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springblade</groupId>
+                    <artifactId>blade-core-cloud</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-api-crypto</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-datascope</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-tenant</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-develop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-swagger</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-excel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-social</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi2-ui</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- 报表 -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-report</artifactId>
+        </dependency>
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+        </dependency>
+        <!-- Mybatis-Plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-generator</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!--Oss-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-oss</artifactId>
+        </dependency>
+        <!--Sms-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-sms</artifactId>
+        </dependency>
+        <!--Aws S3-->
+        <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-java-sdk-s3</artifactId>
+        </dependency>
+        <!--MinIO-->
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+        </dependency>
+        <!--Alioss-->
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+        </dependency>
+        <!--AliSms-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+        </dependency>
+        <!--腾讯COS-->
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+        </dependency>
+        <!--腾讯SMS-->
+        <dependency>
+            <groupId>com.github.qcloudsms</groupId>
+            <artifactId>qcloudsms</artifactId>
+        </dependency>
+        <!--QiNiu-->
+        <dependency>
+            <groupId>com.qiniu</groupId>
+            <artifactId>qiniu-java-sdk</artifactId>
+        </dependency>
+        <!--YunPian-->
+        <dependency>
+            <groupId>com.yunpian.sdk</groupId>
+            <artifactId>yunpian-java-sdk</artifactId>
+        </dependency>
+        <!-- lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${bladex.project.id}</finalName>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.xml</include>
+                </includes>
+            </resource>
+        </resources>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-maven-plugin</artifactId>
+                    <version>2.7.10</version>
+                    <configuration>
+                        <finalName>${project.build.finalName}</finalName>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <goals>
+                                <goal>repackage</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>com.spotify</groupId>
+                    <artifactId>dockerfile-maven-plugin</artifactId>
+                    <version>${docker.plugin.version}</version>
+                    <configuration>
+                        <username>${docker.username}</username>
+                        <password>${docker.password}</password>
+                        <repository>${docker.registry.url}/${docker.namespace}/${project.build.finalName}</repository>
+                        <tag>${project.version}</tag>
+                        <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+                        <buildArgs>
+                            <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
+                        </buildArgs>
+                    </configuration>
+                    <!--添加如下配置,运行 mvn deploy 命令便会自动打包镜像-->
+                    <!--<executions>
+                        <execution>
+                            <id>default</id>
+                            <goals>
+                                <goal>build</goal>
+                                <goal>push</goal>
+                            </goals>
+                        </execution>
+                    </executions>-->
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven.plugin.version}</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>UTF-8</encoding>
+                    <compilerArgs>
+                        <arg>-parameters</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <repositories>
+        <repository>
+            <id>aliyun-repos</id>
+            <url>https://maven.aliyun.com/repository/public/</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>blade-release</id>
+            <name>Release Repository</name>
+            <url>http://nexus.javablade.com/repository/maven-releases/</url>
+        </repository>
+    </repositories>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>aliyun-plugin</id>
+            <url>https://maven.aliyun.com/repository/public/</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+
+</project>

+ 38 - 0
admin/src/main/java/org/springblade/Application.java

@@ -0,0 +1,38 @@
+/*
+ *      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;
+
+import org.springblade.common.constant.CommonConstant;
+import org.springblade.core.launch.BladeApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 启动器
+ *
+ * @author Chill
+ */
+@EnableScheduling
+@SpringBootApplication
+public class Application {
+
+	public static void main(String[] args) {
+		BladeApplication.run(CommonConstant.APPLICATION_NAME, Application.class, args);
+	}
+
+}
+

+ 61 - 0
admin/src/main/java/org/springblade/common/cache/CacheNames.java

@@ -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 org.springblade.common.cache;
+
+import org.springblade.core.tool.utils.StringPool;
+
+/**
+ * 缓存名
+ *
+ * @author Chill
+ */
+public interface CacheNames {
+
+	/**
+	 * 返回拼接后的key
+	 *
+	 * @param cacheKey      缓存key
+	 * @param cacheKeyValue 缓存key值
+	 * @return tenantKey
+	 */
+	static String cacheKey(String cacheKey, String cacheKeyValue) {
+		return cacheKey.concat(cacheKeyValue);
+	}
+
+	/**
+	 * 返回租户格式的key
+	 *
+	 * @param tenantId      租户编号
+	 * @param cacheKey      缓存key
+	 * @param cacheKeyValue 缓存key值
+	 * @return tenantKey
+	 */
+	static String tenantKey(String tenantId, String cacheKey, String cacheKeyValue) {
+		return tenantId.concat(StringPool.COLON).concat(cacheKey).concat(cacheKeyValue);
+	}
+
+	/**
+	 * 验证码key
+	 */
+	String CAPTCHA_KEY = "blade:auth::blade:captcha:";
+
+	/**
+	 * 登录失败key
+	 */
+	String USER_FAIL_KEY = "blade:user::blade:fail:";
+
+}

+ 116 - 0
admin/src/main/java/org/springblade/common/cache/DictBizCache.java

@@ -0,0 +1,116 @@
+/*
+ *      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.common.cache;
+
+import org.springblade.common.enums.DictBizEnum;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.modules.system.entity.DictBiz;
+import org.springblade.modules.system.service.IDictBizService;
+
+import java.util.List;
+
+import static org.springblade.core.cache.constant.CacheConstant.DICT_CACHE;
+
+/**
+ * 业务字典缓存工具类
+ *
+ * @author Chill
+ */
+public class DictBizCache {
+
+	private static final String DICT_ID = "dictBiz:id";
+	private static final String DICT_VALUE = "dictBiz:value";
+	private static final String DICT_LIST = "dictBiz:list";
+
+	private static final IDictBizService dictService;
+
+	static {
+		dictService = SpringUtil.getBean(IDictBizService.class);
+	}
+
+	/**
+	 * 获取字典实体
+	 *
+	 * @param id 主键
+	 * @return DictBiz
+	 */
+	public static DictBiz getById(Long id) {
+		String keyPrefix = DICT_ID.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
+		return CacheUtil.get(DICT_CACHE, keyPrefix, id, () -> dictService.getById(id));
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号枚举
+	 * @param dictKey Integer型字典键
+	 * @return String
+	 */
+	public static String getValue(DictBizEnum code, Integer dictKey) {
+		return getValue(code.getName(), dictKey);
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号
+	 * @param dictKey Integer型字典键
+	 * @return String
+	 */
+	public static String getValue(String code, Integer dictKey) {
+		String keyPrefix = DICT_VALUE.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
+		return CacheUtil.get(DICT_CACHE, keyPrefix + code + StringPool.COLON, String.valueOf(dictKey), () -> dictService.getValue(code, String.valueOf(dictKey)));
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号枚举
+	 * @param dictKey String型字典键
+	 * @return String
+	 */
+	public static String getValue(DictBizEnum code, String dictKey) {
+		return getValue(code.getName(), dictKey);
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号
+	 * @param dictKey String型字典键
+	 * @return String
+	 */
+	public static String getValue(String code, String dictKey) {
+		String keyPrefix = DICT_VALUE.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
+		return CacheUtil.get(DICT_CACHE, keyPrefix + code + StringPool.COLON, dictKey, () -> dictService.getValue(code, dictKey));
+	}
+
+	/**
+	 * 获取字典集合
+	 *
+	 * @param code 字典编号
+	 * @return List<DictBiz>
+	 */
+	public static List<DictBiz> getList(String code) {
+		String keyPrefix = DICT_LIST.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
+		return CacheUtil.get(DICT_CACHE, keyPrefix, code, () -> dictService.getList(code));
+	}
+
+}

+ 143 - 0
admin/src/main/java/org/springblade/common/cache/DictCache.java

@@ -0,0 +1,143 @@
+/*
+ *      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.common.cache;
+
+import org.springblade.common.enums.DictEnum;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.modules.system.entity.Dict;
+import org.springblade.modules.system.service.IDictService;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.springblade.core.cache.constant.CacheConstant.DICT_CACHE;
+
+/**
+ * 字典缓存工具类
+ *
+ * @author Chill
+ */
+public class DictCache {
+
+	private static final String DICT_ID = "dict:id:";
+	private static final String DICT_KEY = "dict:key:";
+	private static final String DICT_VALUE = "dict:value:";
+	private static final String DICT_LIST = "dict:list:";
+
+	private static final Boolean TENANT_MODE = Boolean.FALSE;
+
+	private static final IDictService dictService;
+
+	static {
+		dictService = SpringUtil.getBean(IDictService.class);
+	}
+
+	/**
+	 * 获取字典实体
+	 *
+	 * @param id 主键
+	 * @return Dict
+	 */
+	public static Dict getById(Long id) {
+		return CacheUtil.get(DICT_CACHE, DICT_ID, id, () -> dictService.getById(id), TENANT_MODE);
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code      字典编号枚举
+	 * @param dictValue 字典值
+	 * @return String
+	 */
+	public static String getKey(DictEnum code, String dictValue) {
+		return getKey(code.getName(), dictValue);
+	}
+
+	/**
+	 * 获取字典键
+	 *
+	 * @param code      字典编号
+	 * @param dictValue 字典值
+	 * @return String
+	 */
+	public static String getKey(String code, String dictValue) {
+		return CacheUtil.get(DICT_CACHE, DICT_KEY + code + StringPool.COLON, dictValue, () -> {
+			List<Dict> list = getList(code);
+			Optional<String> key = list.stream().filter(
+				dict -> dict.getDictValue().equalsIgnoreCase(dictValue)
+			).map(Dict::getDictKey).findFirst();
+			return key.orElse(StringPool.EMPTY);
+		}, TENANT_MODE);
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号枚举
+	 * @param dictKey Integer型字典键
+	 * @return String
+	 */
+	public static String getValue(DictEnum code, Integer dictKey) {
+		return getValue(code.getName(), dictKey);
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号
+	 * @param dictKey Integer型字典键
+	 * @return String
+	 */
+	public static String getValue(String code, Integer dictKey) {
+		return CacheUtil.get(DICT_CACHE, DICT_VALUE + code + StringPool.COLON, String.valueOf(dictKey), () -> dictService.getValue(code, String.valueOf(dictKey)), TENANT_MODE);
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号枚举
+	 * @param dictKey String型字典键
+	 * @return String
+	 */
+	public static String getValue(DictEnum code, String dictKey) {
+		return getValue(code.getName(), dictKey);
+	}
+
+	/**
+	 * 获取字典值
+	 *
+	 * @param code    字典编号
+	 * @param dictKey String型字典键
+	 * @return String
+	 */
+	public static String getValue(String code, String dictKey) {
+		return CacheUtil.get(DICT_CACHE, DICT_VALUE + code + StringPool.COLON, dictKey, () -> dictService.getValue(code, dictKey), TENANT_MODE);
+	}
+
+	/**
+	 * 获取字典集合
+	 *
+	 * @param code 字典编号
+	 * @return List<Dict>
+	 */
+	public static List<Dict> getList(String code) {
+		return CacheUtil.get(DICT_CACHE, DICT_LIST, code, () -> dictService.getList(code), TENANT_MODE);
+	}
+
+}

+ 62 - 0
admin/src/main/java/org/springblade/common/cache/ParamCache.java

@@ -0,0 +1,62 @@
+/*
+ *      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.common.cache;
+
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.modules.system.entity.Param;
+import org.springblade.modules.system.service.IParamService;
+
+import static org.springblade.core.cache.constant.CacheConstant.PARAM_CACHE;
+
+/**
+ * 参数缓存工具类
+ *
+ * @author Chill
+ */
+public class ParamCache {
+
+	private static final String PARAM_ID = "param:id:";
+	private static final String PARAM_VALUE = "param:value:";
+
+	private static final IParamService paramService;
+
+	static {
+		paramService = SpringUtil.getBean(IParamService.class);
+	}
+
+	/**
+	 * 获取参数实体
+	 *
+	 * @param id 主键
+	 * @return Param
+	 */
+	public static Param getById(Long id) {
+		return CacheUtil.get(PARAM_CACHE, PARAM_ID, id, () -> paramService.getById(id));
+	}
+
+	/**
+	 * 获取参数配置
+	 *
+	 * @param paramKey 参数值
+	 * @return String
+	 */
+	public static String getValue(String paramKey) {
+		return CacheUtil.get(PARAM_CACHE, PARAM_VALUE, paramKey, () -> paramService.getValue(paramKey));
+	}
+
+}

+ 57 - 0
admin/src/main/java/org/springblade/common/cache/RegionCache.java

@@ -0,0 +1,57 @@
+/*
+ *      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.common.cache;
+
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.modules.system.entity.Region;
+import org.springblade.modules.system.service.IRegionService;
+
+import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
+
+/**
+ * 行政区划缓存工具类
+ *
+ * @author Chill
+ */
+public class RegionCache {
+	public static final String MAIN_CODE = "00";
+	public static final int PROVINCE_LEVEL = 1;
+	public static final int CITY_LEVEL = 2;
+	public static final int DISTRICT_LEVEL = 3;
+	public static final int TOWN_LEVEL = 4;
+	public static final int VILLAGE_LEVEL = 5;
+
+	private static final String REGION_CODE = "region:code:";
+
+	private static final IRegionService regionService;
+
+	static {
+		regionService = SpringUtil.getBean(IRegionService.class);
+	}
+
+	/**
+	 * 获取行政区划实体
+	 *
+	 * @param code 区划编号
+	 * @return Param
+	 */
+	public static Region getByCode(String code) {
+		return CacheUtil.get(SYS_CACHE, REGION_CODE, code, () -> regionService.getById(code));
+	}
+
+}

+ 317 - 0
admin/src/main/java/org/springblade/common/cache/SysCache.java

@@ -0,0 +1,317 @@
+/*
+ *      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.common.cache;
+
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.modules.system.entity.*;
+import org.springblade.modules.system.service.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
+
+/**
+ * 系统缓存
+ *
+ * @author Chill
+ */
+public class SysCache {
+	private static final String MENU_ID = "menu:id:";
+	private static final String DEPT_ID = "dept:id:";
+	private static final String DEPT_NAME = "dept:name:";
+	private static final String DEPT_NAME_FUZZY = "dept:nameFuzzy:";
+	private static final String DEPT_NAME_ID = "deptName:id:";
+	private static final String DEPT_NAMES_ID = "deptNames:id:";
+	private static final String DEPT_CHILD_ID = "deptChild:id:";
+	private static final String DEPT_CHILDIDS_ID = "deptChildIds:id:";
+	private static final String POST_ID = "post:id:";
+	private static final String POST_NAME = "post:name:";
+	private static final String POST_NAME_FUZZY = "post:nameFuzzy:";
+	private static final String POST_NAME_ID = "postName:id:";
+	private static final String POST_NAMES_ID = "postNames:id:";
+	private static final String ROLE_ID = "role:id:";
+	private static final String ROLE_NAME = "role:name:";
+	private static final String ROLE_NAME_ID = "roleName:id:";
+	private static final String ROLE_NAMES_ID = "roleNames:id:";
+	private static final String ROLE_ALIAS_ID = "roleAlias:id:";
+	private static final String ROLE_ALIASES_ID = "roleAliases:id:";
+	public static final String TENANT_ID = "tenant:id:";
+	public static final String TENANT_TENANT_ID = "tenant:tenantId:";
+	public static final String TENANT_PACKAGE_ID = "tenant:packageId:";
+
+	private static final IMenuService menuService;
+	private static final IDeptService deptService;
+	private static final IPostService postService;
+	private static final IRoleService roleService;
+	private static final ITenantService tenantService;
+	private static final ITenantPackageService tenantPackageService;
+
+	static {
+		menuService = SpringUtil.getBean(IMenuService.class);
+		deptService = SpringUtil.getBean(IDeptService.class);
+		postService = SpringUtil.getBean(IPostService.class);
+		roleService = SpringUtil.getBean(IRoleService.class);
+		tenantService = SpringUtil.getBean(ITenantService.class);
+		tenantPackageService = SpringUtil.getBean(ITenantPackageService.class);
+	}
+
+	/**
+	 * 获取菜单
+	 *
+	 * @param id 主键
+	 * @return 菜单
+	 */
+	public static Menu getMenu(Long id) {
+		return CacheUtil.get(SYS_CACHE, MENU_ID, id, () -> menuService.getById(id));
+	}
+
+	/**
+	 * 获取部门
+	 *
+	 * @param id 主键
+	 * @return 部门
+	 */
+	public static Dept getDept(Long id) {
+		return CacheUtil.get(SYS_CACHE, DEPT_ID, id, () -> deptService.getById(id));
+	}
+
+	/**
+	 * 获取部门id
+	 *
+	 * @param tenantId  租户id
+	 * @param deptNames 部门名
+	 * @return 部门id
+	 */
+	public static String getDeptIds(String tenantId, String deptNames) {
+		return CacheUtil.get(SYS_CACHE, DEPT_NAME, tenantId + StringPool.DASH + deptNames, () -> deptService.getDeptIds(tenantId, deptNames));
+	}
+
+	/**
+	 * 获取部门id
+	 *
+	 * @param tenantId  租户id
+	 * @param deptNames 部门名模糊查询
+	 * @return 部门id
+	 */
+	public static String getDeptIdsByFuzzy(String tenantId, String deptNames) {
+		return CacheUtil.get(SYS_CACHE, DEPT_NAME_FUZZY, tenantId + StringPool.DASH + deptNames, () -> deptService.getDeptIdsByFuzzy(tenantId, deptNames));
+	}
+
+	/**
+	 * 获取部门名
+	 *
+	 * @param id 主键
+	 * @return 部门名
+	 */
+	public static String getDeptName(Long id) {
+		return CacheUtil.get(SYS_CACHE, DEPT_NAME_ID, id, () -> deptService.getById(id).getDeptName());
+	}
+
+
+	/**
+	 * 获取部门名集合
+	 *
+	 * @param deptIds 主键集合
+	 * @return 部门名
+	 */
+	public static List<String> getDeptNames(String deptIds) {
+		return CacheUtil.get(SYS_CACHE, DEPT_NAMES_ID, deptIds, () -> deptService.getDeptNames(deptIds));
+	}
+
+	/**
+	 * 获取子部门集合
+	 *
+	 * @param deptId 主键
+	 * @return 子部门
+	 */
+	public static List<Dept> getDeptChild(Long deptId) {
+		return CacheUtil.get(SYS_CACHE, DEPT_CHILD_ID, deptId, () -> deptService.getDeptChild(deptId));
+	}
+
+	/**
+	 * 获取子部门ID集合
+	 *
+	 * @param deptId 主键
+	 * @return 子部门ID
+	 */
+	public static List<Long> getDeptChildIds(Long deptId) {
+		if (deptId == null) {
+			return null;
+		}
+		List<Long> deptIdList = CacheUtil.get(SYS_CACHE, DEPT_CHILDIDS_ID, deptId, List.class);
+		if (deptIdList == null) {
+			deptIdList = new ArrayList<>();
+			List<Dept> deptChild = getDeptChild(deptId);
+			if (deptChild != null) {
+				List<Long> collect = deptChild.stream().map(Dept::getId).collect(Collectors.toList());
+				deptIdList.addAll(collect);
+			}
+			deptIdList.add(deptId);
+			CacheUtil.put(SYS_CACHE, DEPT_CHILDIDS_ID, deptId, deptIdList);
+		}
+		return deptIdList;
+	}
+
+	/**
+	 * 获取岗位
+	 *
+	 * @param id 主键
+	 * @return
+	 */
+	public static Post getPost(Long id) {
+		return CacheUtil.get(SYS_CACHE, POST_ID, id, () -> postService.getById(id));
+	}
+
+	/**
+	 * 获取岗位id
+	 *
+	 * @param tenantId  租户id
+	 * @param postNames 岗位名
+	 * @return
+	 */
+	public static String getPostIds(String tenantId, String postNames) {
+		return CacheUtil.get(SYS_CACHE, POST_NAME, tenantId + StringPool.DASH + postNames, () -> postService.getPostIds(tenantId, postNames));
+	}
+
+	/**
+	 * 获取岗位id
+	 *
+	 * @param tenantId  租户id
+	 * @param postNames 岗位名模糊查询
+	 * @return
+	 */
+	public static String getPostIdsByFuzzy(String tenantId, String postNames) {
+		return CacheUtil.get(SYS_CACHE, POST_NAME_FUZZY, tenantId + StringPool.DASH + postNames, () -> postService.getPostIdsByFuzzy(tenantId, postNames));
+	}
+
+	/**
+	 * 获取岗位名
+	 *
+	 * @param id 主键
+	 * @return 岗位名
+	 */
+	public static String getPostName(Long id) {
+		return CacheUtil.get(SYS_CACHE, POST_NAME_ID, id, () -> postService.getById(id).getPostName());
+	}
+
+	/**
+	 * 获取岗位名集合
+	 *
+	 * @param postIds 主键集合
+	 * @return 岗位名
+	 */
+	public static List<String> getPostNames(String postIds) {
+		return CacheUtil.get(SYS_CACHE, POST_NAMES_ID, postIds, () -> postService.getPostNames(postIds));
+	}
+
+	/**
+	 * 获取角色
+	 *
+	 * @param id 主键
+	 * @return Role
+	 */
+	public static Role getRole(Long id) {
+		return CacheUtil.get(SYS_CACHE, ROLE_ID, id, () -> roleService.getById(id));
+	}
+
+	/**
+	 * 获取角色id
+	 *
+	 * @param tenantId  租户id
+	 * @param roleNames 角色名
+	 * @return
+	 */
+	public static String getRoleIds(String tenantId, String roleNames) {
+		return CacheUtil.get(SYS_CACHE, ROLE_NAME, tenantId + StringPool.DASH + roleNames, () -> roleService.getRoleIds(tenantId, roleNames));
+	}
+
+	/**
+	 * 获取角色名
+	 *
+	 * @param id 主键
+	 * @return 角色名
+	 */
+	public static String getRoleName(Long id) {
+		return CacheUtil.get(SYS_CACHE, ROLE_NAME_ID, id, () -> roleService.getById(id).getRoleName());
+	}
+
+	/**
+	 * 获取角色名集合
+	 *
+	 * @param roleIds 主键集合
+	 * @return 角色名
+	 */
+	public static List<String> getRoleNames(String roleIds) {
+		return CacheUtil.get(SYS_CACHE, ROLE_NAMES_ID, roleIds, () -> roleService.getRoleNames(roleIds));
+	}
+
+	/**
+	 * 获取角色别名
+	 *
+	 * @param id 主键
+	 * @return 角色别名
+	 */
+	public static String getRoleAlias(Long id) {
+		return CacheUtil.get(SYS_CACHE, ROLE_ALIAS_ID, id, () -> roleService.getById(id).getRoleAlias());
+	}
+
+	/**
+	 * 获取角色别名集合
+	 *
+	 * @param roleIds 主键集合
+	 * @return 角色别名
+	 */
+	public static List<String> getRoleAliases(String roleIds) {
+		return CacheUtil.get(SYS_CACHE, ROLE_ALIASES_ID, roleIds, () -> roleService.getRoleAliases(roleIds));
+	}
+
+	/**
+	 * 获取租户
+	 *
+	 * @param id 主键
+	 * @return Tenant
+	 */
+	public static Tenant getTenant(Long id) {
+		return CacheUtil.get(SYS_CACHE, TENANT_ID, id, () -> tenantService.getById(id), Boolean.FALSE);
+	}
+
+	/**
+	 * 获取租户
+	 *
+	 * @param tenantId 租户id
+	 * @return Tenant
+	 */
+	public static Tenant getTenant(String tenantId) {
+		return CacheUtil.get(SYS_CACHE, TENANT_TENANT_ID, tenantId, () -> tenantService.getByTenantId(tenantId), Boolean.FALSE);
+	}
+
+	/**
+	 * 获取租户产品包
+	 *
+	 * @param tenantId 租户id
+	 * @return Tenant
+	 */
+	public static TenantPackage getTenantPackage(String tenantId) {
+		Tenant tenant = getTenant(tenantId);
+		return CacheUtil.get(SYS_CACHE, TENANT_PACKAGE_ID, tenantId, () -> tenantPackageService.getById(tenant.getPackageId()), Boolean.FALSE);
+	}
+
+}

+ 77 - 0
admin/src/main/java/org/springblade/common/cache/UserCache.java

@@ -0,0 +1,77 @@
+/*
+ *      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.common.cache;
+
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.modules.system.entity.User;
+import org.springblade.modules.system.service.IUserService;
+
+import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
+import static org.springblade.core.launch.constant.FlowConstant.TASK_USR_PREFIX;
+
+/**
+ * 系统缓存
+ *
+ * @author Chill
+ */
+public class UserCache {
+	private static final String USER_CACHE_ID = "user:id:";
+	private static final String USER_CACHE_ACCOUNT = "user:account:";
+
+	private static final IUserService userService;
+
+	static {
+		userService = SpringUtil.getBean(IUserService.class);
+	}
+
+	/**
+	 * 根据任务用户id获取用户信息
+	 *
+	 * @param taskUserId 任务用户id
+	 * @return
+	 */
+	public static User getUserByTaskUser(String taskUserId) {
+		Long userId = Func.toLong(StringUtil.removePrefix(taskUserId, TASK_USR_PREFIX));
+		return getUser(userId);
+	}
+
+	/**
+	 * 获取用户
+	 *
+	 * @param userId 用户id
+	 * @return
+	 */
+	public static User getUser(Long userId) {
+		return CacheUtil.get(USER_CACHE, USER_CACHE_ID, userId, () -> userService.getById(userId));
+	}
+
+	/**
+	 * 获取用户
+	 *
+	 * @param tenantId 租户id
+	 * @param account  账号名
+	 * @return
+	 */
+	public static User getUser(String tenantId, String account) {
+		return CacheUtil.get(USER_CACHE, USER_CACHE_ACCOUNT, tenantId + StringPool.DASH + account, () -> userService.userByAccount(tenantId, account));
+	}
+
+}

+ 70 - 0
admin/src/main/java/org/springblade/common/config/BladeConfiguration.java

@@ -0,0 +1,70 @@
+/*
+ *      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.common.config;
+
+
+import org.springblade.core.secure.registry.SecureRegistry;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Blade配置
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+public class BladeConfiguration implements WebMvcConfigurer {
+
+	/**
+	 * 安全框架配置
+	 */
+	@Bean
+	public SecureRegistry secureRegistry() {
+		SecureRegistry secureRegistry = new SecureRegistry();
+		secureRegistry.setEnabled(true);
+		secureRegistry.excludePathPatterns("/blade-auth/**");
+		secureRegistry.excludePathPatterns("/blade-system/menu/routes");
+		secureRegistry.excludePathPatterns("/blade-system/menu/auth-routes");
+		secureRegistry.excludePathPatterns("/blade-system/menu/top-menu");
+		secureRegistry.excludePathPatterns("/blade-system/tenant/info");
+		secureRegistry.excludePathPatterns("/blade-flow/process/resource-view");
+		secureRegistry.excludePathPatterns("/blade-flow/process/diagram-view");
+		secureRegistry.excludePathPatterns("/blade-flow/manager/check-upload");
+		secureRegistry.excludePathPatterns("/doc.html");
+		secureRegistry.excludePathPatterns("/js/**");
+		secureRegistry.excludePathPatterns("/webjars/**");
+		secureRegistry.excludePathPatterns("/swagger-resources/**");
+		secureRegistry.excludePathPatterns("/druid/**");
+		return secureRegistry;
+	}
+
+	/**
+	 * 跨域配置
+	 */
+	@Override
+	public void addCorsMappings(CorsRegistry registry) {
+		registry.addMapping("/cors/**")
+			.allowedOrigins("*")
+			.allowedHeaders("*")
+			.allowedMethods("*")
+			.maxAge(3600)
+			.allowCredentials(true);
+	}
+
+}

+ 58 - 0
admin/src/main/java/org/springblade/common/config/BladeLogConfiguration.java

@@ -0,0 +1,58 @@
+/*
+ *      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.common.config;
+
+import lombok.AllArgsConstructor;
+import org.springblade.common.event.ApiLogListener;
+import org.springblade.common.event.ErrorLogListener;
+import org.springblade.common.event.UsualLogListener;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springblade.core.launch.server.ServerInfo;
+import org.springblade.modules.system.service.ILogService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 日志工具自动配置
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@AllArgsConstructor
+public class BladeLogConfiguration {
+
+	private final ILogService logService;
+	private final ServerInfo serverInfo;
+	private final BladeProperties bladeProperties;
+
+	@Bean(name = "apiLogListener")
+	public ApiLogListener apiLogListener() {
+		return new ApiLogListener(logService, serverInfo, bladeProperties);
+	}
+
+	@Bean(name = "errorEventListener")
+	public ErrorLogListener errorEventListener() {
+		return new ErrorLogListener(logService, serverInfo, bladeProperties);
+	}
+
+	@Bean(name = "usualEventListener")
+	public UsualLogListener usualEventListener() {
+		return new UsualLogListener(logService, serverInfo, bladeProperties);
+	}
+
+}

+ 26 - 0
admin/src/main/java/org/springblade/common/config/BladePreviewConfiguration.java

@@ -0,0 +1,26 @@
+package org.springblade.common.config;
+
+import org.springblade.common.filter.PreviewFilter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 演示配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnProperty(value = "blade.preview.enabled", havingValue = "true")
+public class BladePreviewConfiguration {
+
+	/**
+	 * 演示模式配置
+	 */
+	@Bean
+	public PreviewFilter previewFilter() {
+		return new PreviewFilter();
+	}
+
+
+}

+ 43 - 0
admin/src/main/java/org/springblade/common/config/BladeReportConfiguration.java

@@ -0,0 +1,43 @@
+/*
+ *      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.common.config;
+
+import org.springblade.core.report.datasource.ReportDataSource;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+
+/**
+ * 报表配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnProperty(value = "report.enabled", havingValue = "true", matchIfMissing = true)
+public class BladeReportConfiguration {
+
+	/**
+	 * 自定义报表可选数据源
+	 */
+	@Bean
+	public ReportDataSource reportDataSource(DataSource dataSource) {
+		return new ReportDataSource(dataSource);
+	}
+
+}

+ 120 - 0
admin/src/main/java/org/springblade/common/config/SwaggerConfiguration.java

@@ -0,0 +1,120 @@
+/*
+ *      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.common.config;
+
+import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
+import com.google.common.collect.Lists;
+import lombok.AllArgsConstructor;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.swagger.EnableSwagger;
+import org.springblade.core.swagger.SwaggerProperties;
+import org.springblade.core.swagger.SwaggerUtil;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Swagger配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableSwagger
+@AllArgsConstructor
+public class SwaggerConfiguration {
+
+	/**
+	 * 引入swagger配置类
+	 */
+	private final SwaggerProperties swaggerProperties;
+
+	/**
+	 * 引入Knife4j扩展类
+	 */
+	private final OpenApiExtensionResolver openApiExtensionResolver;
+
+	@Bean
+	public Docket authDocket() {
+		return docket("授权模块", Collections.singletonList(AppConstant.BASE_PACKAGES + ".modules.auth"));
+	}
+
+	@Bean
+	public Docket sysDocket() {
+		return docket("系统模块",
+			Arrays.asList(AppConstant.BASE_PACKAGES + ".modules.system", AppConstant.BASE_PACKAGES + ".modules.resource"));
+	}
+
+	@Bean
+	public Docket flowDocket() {
+		return docket("工作流模块", Collections.singletonList(AppConstant.BASE_PACKAGES + ".flow"));
+	}
+
+	private Docket docket(String groupName, List<String> basePackages) {
+		return new Docket(DocumentationType.SWAGGER_2)
+			.groupName(groupName)
+			.apiInfo(apiInfo())
+			.ignoredParameterTypes(BladeUser.class)
+			.select()
+			.apis(SwaggerUtil.basePackages(basePackages))
+			.paths(PathSelectors.any())
+			.build().securityContexts(securityContexts()).securitySchemes(securitySchemas())
+			.extensions(openApiExtensionResolver.buildExtensions(groupName));
+	}
+
+	private List<SecurityContext> securityContexts() {
+		return Collections.singletonList(SecurityContext.builder()
+			.securityReferences(defaultAuth())
+			.forPaths(PathSelectors.regex("^.*$"))
+			.build());
+	}
+
+	List<SecurityReference> defaultAuth() {
+		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverywhere");
+		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+		authorizationScopes[0] = authorizationScope;
+		return Lists.newArrayList(new SecurityReference(SwaggerUtil.clientInfo().getName(), authorizationScopes),
+			new SecurityReference(SwaggerUtil.bladeAuth().getName(), authorizationScopes),
+			new SecurityReference(SwaggerUtil.bladeTenant().getName(), authorizationScopes));
+	}
+
+	private List<SecurityScheme> securitySchemas() {
+		return Lists.newArrayList(SwaggerUtil.clientInfo(), SwaggerUtil.bladeAuth(), SwaggerUtil.bladeTenant());
+	}
+
+	private ApiInfo apiInfo() {
+		return new ApiInfoBuilder()
+			.title(swaggerProperties.getTitle())
+			.description(swaggerProperties.getDescription())
+			.license(swaggerProperties.getLicense())
+			.licenseUrl(swaggerProperties.getLicenseUrl())
+			.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
+			.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
+			.version(swaggerProperties.getVersion())
+			.build();
+	}
+
+}

+ 84 - 0
admin/src/main/java/org/springblade/common/constant/CommonConstant.java

@@ -0,0 +1,84 @@
+/*
+ *      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.common.constant;
+
+import org.springblade.core.launch.constant.AppConstant;
+
+/**
+ * 通用常量
+ *
+ * @author Chill
+ */
+public interface CommonConstant {
+
+	/**
+	 * app name
+	 */
+	String APPLICATION_NAME = "yuecai";
+
+	/**
+	 * sword 系统名
+	 */
+	String SWORD_NAME = "sword";
+
+	/**
+	 * saber 系统名
+	 */
+	String SABER_NAME = "saber";
+
+	/**
+	 * 顶级父节点id
+	 */
+	Long TOP_PARENT_ID = 0L;
+
+	/**
+	 * 顶级父节点名称
+	 */
+	String TOP_PARENT_NAME = "顶级";
+
+	/**
+	 * 未封存状态值
+	 */
+	Integer NOT_SEALED_ID = 0;
+
+	/**
+	 * 默认密码
+	 */
+	String DEFAULT_PASSWORD = "123456";
+
+	/**
+	 * 默认密码参数值
+	 */
+	String DEFAULT_PARAM_PASSWORD = "account.initPassword";
+
+	/**
+	 * 默认排序字段
+	 */
+	String SORT_FIELD = "sort";
+
+	/**
+	 * 数据权限类型
+	 */
+	Integer DATA_SCOPE_CATEGORY = 1;
+
+	/**
+	 * 接口权限类型
+	 */
+	Integer API_SCOPE_CATEGORY = 2;
+
+
+}

+ 38 - 0
admin/src/main/java/org/springblade/common/constant/DictConstant.java

@@ -0,0 +1,38 @@
+/*
+ *      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.common.constant;
+
+/**
+ * 字典常量.
+ *
+ * @author zhuangqian
+ */
+public interface DictConstant {
+
+	String SEX_CODE = "sex";
+
+	String NOTICE_CODE = "notice";
+
+	String MENU_CATEGORY_CODE = "menu_category";
+
+	String BUTTON_FUNC_CODE = "button_func";
+
+	String YES_NO_CODE = "yes_no";
+
+	String FLOW_CATEGORY_CODE = "flow_category";
+
+}

+ 93 - 0
admin/src/main/java/org/springblade/common/constant/LauncherConstant.java

@@ -0,0 +1,93 @@
+/*
+ *      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.common.constant;
+
+import org.springblade.core.launch.constant.AppConstant;
+
+/**
+ * 启动常量
+ *
+ * @author Chill
+ */
+public interface LauncherConstant {
+
+	/**
+	 * sentinel dev 地址
+	 */
+	String SENTINEL_DEV_ADDR = "127.0.0.1:8858";
+
+	/**
+	 * sentinel prod 地址
+	 */
+	String SENTINEL_PROD_ADDR = "10.211.55.5:8858";
+
+	/**
+	 * sentinel test 地址
+	 */
+	String SENTINEL_TEST_ADDR = "172.30.0.58:8858";
+
+	/**
+	 * elk dev 地址
+	 */
+	String ELK_DEV_ADDR = "127.0.0.1:9000";
+
+	/**
+	 * elk prod 地址
+	 */
+	String ELK_PROD_ADDR = "172.30.0.58:9000";
+
+	/**
+	 * elk test 地址
+	 */
+	String ELK_TEST_ADDR = "172.30.0.58:9000";
+
+	/**
+	 * 动态获取sentinel地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String sentinelAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return SENTINEL_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return SENTINEL_TEST_ADDR;
+			default:
+				return SENTINEL_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取elk地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String elkAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return ELK_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return ELK_TEST_ADDR;
+			default:
+				return ELK_DEV_ADDR;
+		}
+	}
+
+
+}

+ 66 - 0
admin/src/main/java/org/springblade/common/constant/TenantConstant.java

@@ -0,0 +1,66 @@
+/*
+ *      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.common.constant;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 租户常量
+ *
+ * @author Chill
+ */
+public interface TenantConstant {
+
+	/**
+	 * 租户默认密码KEY
+	 */
+	String PASSWORD_KEY = "tenant.default.password";
+
+	/**
+	 * 租户默认账号额度KEY
+	 */
+	String ACCOUNT_NUMBER_KEY = "tenant.default.accountNumber";
+
+	/**
+	 * 租户默认菜单集合KEY
+	 */
+	String ACCOUNT_MENU_CODE_KEY = "tenant.default.menuCode";
+
+	/**
+	 * 租户默认密码
+	 */
+	String DEFAULT_PASSWORD = "123456";
+
+	/**
+	 * 租户授权码默认16位密钥
+	 */
+	String DES_KEY = "0000000000000000";
+
+	/**
+	 * 租户默认账号额度
+	 */
+	Integer DEFAULT_ACCOUNT_NUMBER = -1;
+
+	/**
+	 * 租户默认菜单集合
+	 */
+	List<String> MENU_CODES = Arrays.asList(
+		"desk", "flow", "work", "monitor", "resource", "role", "user", "dept", "dictbiz", "topmenu"
+	);
+
+}

+ 39 - 0
admin/src/main/java/org/springblade/common/enums/DictBizEnum.java

@@ -0,0 +1,39 @@
+/*
+ *      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.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 业务字典枚举类
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum DictBizEnum {
+
+	/**
+	 * 测试
+	 */
+	TEST("test"),
+	;
+
+	final String name;
+
+}

+ 95 - 0
admin/src/main/java/org/springblade/common/enums/DictEnum.java

@@ -0,0 +1,95 @@
+/*
+ *      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.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 系统字典枚举类
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum DictEnum {
+
+	/**
+	 * 性别
+	 */
+	SEX("sex"),
+	/**
+	 * 通知类型
+	 */
+	NOTICE("notice"),
+	/**
+	 * 菜单类型
+	 */
+	MENU_CATEGORY("menu_category"),
+	/**
+	 * 按钮功能
+	 */
+	BUTTON_FUNC("button_func"),
+	/**
+	 * 是否
+	 */
+	YES_NO("yes_no"),
+	/**
+	 * 流程类型
+	 */
+	FLOW("flow"),
+	/**
+	 * 机构类型
+	 */
+	ORG_CATEGORY("org_category"),
+	/**
+	 * 数据权限
+	 */
+	DATA_SCOPE_TYPE("data_scope_type"),
+	/**
+	 * 接口权限
+	 */
+	API_SCOPE_TYPE("api_scope_type"),
+	/**
+	 * 权限类型
+	 */
+	SCOPE_CATEGORY("scope_category"),
+	/**
+	 * 对象存储类型
+	 */
+	OSS("oss"),
+	/**
+	 * 短信服务类型
+	 */
+	SMS("sms"),
+	/**
+	 * 岗位类型
+	 */
+	POST_CATEGORY("post_category"),
+	/**
+	 * 行政区划
+	 */
+	REGION("region"),
+	/**
+	 * 用户平台
+	 */
+	USER_TYPE("user_type"),
+	;
+
+	final String name;
+
+}

+ 60 - 0
admin/src/main/java/org/springblade/common/event/ApiLogListener.java

@@ -0,0 +1,60 @@
+/*
+ *      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.common.event;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springblade.core.launch.server.ServerInfo;
+import org.springblade.core.log.constant.EventConstant;
+import org.springblade.core.log.event.ApiLogEvent;
+import org.springblade.core.log.model.LogApi;
+import org.springblade.core.log.utils.LogAbstractUtil;
+import org.springblade.modules.system.service.ILogService;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.annotation.Async;
+
+import java.util.Map;
+
+
+/**
+ * 异步监听日志事件
+ *
+ * @author Chill
+ */
+@Slf4j
+@AllArgsConstructor
+public class ApiLogListener {
+
+	private final ILogService logService;
+	private final ServerInfo serverInfo;
+	private final BladeProperties bladeProperties;
+
+
+	@Async
+	@Order
+	@EventListener(ApiLogEvent.class)
+	public void saveApiLog(ApiLogEvent event) {
+		Map<String, Object> source = (Map<String, Object>) event.getSource();
+		LogApi logApi = (LogApi) source.get(EventConstant.EVENT_LOG);
+		LogAbstractUtil.addOtherInfoToLog(logApi, bladeProperties, serverInfo);
+		logService.saveApiLog(logApi);
+	}
+
+}

+ 57 - 0
admin/src/main/java/org/springblade/common/event/ErrorLogListener.java

@@ -0,0 +1,57 @@
+/*
+ *      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.common.event;
+
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springblade.core.launch.server.ServerInfo;
+import org.springblade.core.log.constant.EventConstant;
+import org.springblade.core.log.event.ErrorLogEvent;
+import org.springblade.core.log.model.LogError;
+import org.springblade.core.log.utils.LogAbstractUtil;
+import org.springblade.modules.system.service.ILogService;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.annotation.Async;
+
+import java.util.Map;
+
+/**
+ * 异步监听错误日志事件
+ *
+ * @author Chill
+ */
+@Slf4j
+@AllArgsConstructor
+public class ErrorLogListener {
+
+	private final ILogService logService;
+	private final ServerInfo serverInfo;
+	private final BladeProperties bladeProperties;
+
+	@Async
+	@Order
+	@EventListener(ErrorLogEvent.class)
+	public void saveErrorLog(ErrorLogEvent event) {
+		Map<String, Object> source = (Map<String, Object>) event.getSource();
+		LogError logError = (LogError) source.get(EventConstant.EVENT_LOG);
+		LogAbstractUtil.addOtherInfoToLog(logError, bladeProperties, serverInfo);
+		logService.saveErrorLog(logError);
+	}
+}

+ 58 - 0
admin/src/main/java/org/springblade/common/event/UsualLogListener.java

@@ -0,0 +1,58 @@
+/*
+ *      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.common.event;
+
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springblade.core.launch.server.ServerInfo;
+import org.springblade.core.log.constant.EventConstant;
+import org.springblade.core.log.event.UsualLogEvent;
+import org.springblade.core.log.model.LogUsual;
+import org.springblade.core.log.utils.LogAbstractUtil;
+import org.springblade.modules.system.service.ILogService;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.annotation.Async;
+
+import java.util.Map;
+
+/**
+ * 异步监听日志事件
+ *
+ * @author Chill
+ */
+@Slf4j
+@AllArgsConstructor
+public class UsualLogListener {
+
+	private final ILogService logService;
+	private final ServerInfo serverInfo;
+	private final BladeProperties bladeProperties;
+
+	@Async
+	@Order
+	@EventListener(UsualLogEvent.class)
+	public void saveUsualLog(UsualLogEvent event) {
+		Map<String, Object> source = (Map<String, Object>) event.getSource();
+		LogUsual logUsual = (LogUsual) source.get(EventConstant.EVENT_LOG);
+		LogAbstractUtil.addOtherInfoToLog(logUsual, bladeProperties, serverInfo);
+		logService.saveUsualLog(logUsual);
+	}
+
+}

+ 66 - 0
admin/src/main/java/org/springblade/common/filter/PreviewFilter.java

@@ -0,0 +1,66 @@
+/*
+ *      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.common.filter;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 演示过滤器
+ *
+ * @author Chill
+ */
+public class PreviewFilter implements Filter {
+
+	private static final List<String> KEYS = new ArrayList<>();
+
+	static {
+		KEYS.add("notice");
+		KEYS.add("process");
+		KEYS.add("work");
+		KEYS.add("token");
+	}
+
+
+	@Override
+	public void init(FilterConfig filterConfig) {
+	}
+
+
+	@Override
+	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+
+		HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+		String path = httpServletRequest.getServletPath();
+		String method = httpServletRequest.getMethod();
+
+		String get = "GET";
+		if (method.equals(get) || KEYS.stream().anyMatch(path::contains)) {
+			filterChain.doFilter(servletRequest, servletResponse);
+		} else {
+			throw new RuntimeException("演示环境暂时无法操作!");
+		}
+
+	}
+
+	@Override
+	public void destroy() {
+	}
+}

+ 44 - 0
admin/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java

@@ -0,0 +1,44 @@
+/*
+ *      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.common.launch;
+
+import org.springblade.common.constant.LauncherConstant;
+import org.springblade.core.auto.service.AutoService;
+import org.springblade.core.launch.service.LauncherService;
+import org.springblade.core.launch.utils.PropsUtil;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+import java.util.Properties;
+
+/**
+ * 启动参数拓展
+ *
+ * @author smallchil
+ */
+@AutoService(LauncherService.class)
+public class LauncherServiceImpl implements LauncherService {
+
+	@Override
+	public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
+		Properties props = System.getProperties();
+		PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile));
+		PropsUtil.setProperty(props, "spring.datasource.dynamic.enabled", "false");
+		// 开启elk日志
+		//PropsUtil.setProperty(props, "blade.log.elk.destination", LauncherConstant.elkAddr(profile));
+	}
+
+}

+ 26 - 0
admin/src/main/java/org/springblade/common/utils/CommonUtil.java

@@ -0,0 +1,26 @@
+/*
+ *      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.common.utils;
+
+/**
+ * 通用工具类
+ *
+ * @author Chill
+ */
+public class CommonUtil {
+
+}

+ 97 - 0
admin/src/main/java/org/springblade/modules/auth/endpoint/BladeSocialEndpoint.java

@@ -0,0 +1,97 @@
+/*
+ *      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.modules.auth.endpoint;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.request.AuthRequest;
+import me.zhyd.oauth.utils.AuthStateUtils;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.core.social.utils.SocialUtil;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 第三方登陆端点
+ *
+ * @author Chill
+ */
+@NonDS
+@Slf4j
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_AUTH_NAME)
+@ConditionalOnProperty(value = "social.enabled", havingValue = "true")
+@Api(value = "第三方登陆", tags = "第三方登陆端点")
+public class BladeSocialEndpoint {
+
+	private final SocialProperties socialProperties;
+
+	/**
+	 * 授权完毕跳转
+	 */
+	@ApiOperation(value = "授权完毕跳转")
+	@RequestMapping("/oauth/render/{source}")
+	public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
+		response.sendRedirect(authorizeUrl);
+	}
+
+	/**
+	 * 获取认证信息
+	 */
+	@ApiOperation(value = "获取认证信息")
+	@RequestMapping("/oauth/callback/{source}")
+	public Object login(@PathVariable("source") String source, AuthCallback callback) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.login(callback);
+	}
+
+	/**
+	 * 撤销授权
+	 */
+	@ApiOperation(value = "撤销授权")
+	@RequestMapping("/oauth/revoke/{source}/{token}")
+	public Object revokeAuth(@PathVariable("source") String source, @PathVariable("token") String token) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.revoke(AuthToken.builder().accessToken(token).build());
+	}
+
+	/**
+	 * 续期accessToken
+	 */
+	@ApiOperation(value = "续期令牌")
+	@RequestMapping("/oauth/refresh/{source}")
+	public Object refreshAuth(@PathVariable("source") String source, String token) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.refresh(AuthToken.builder().refreshToken(token).build());
+	}
+
+
+}

+ 149 - 0
admin/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java

@@ -0,0 +1,149 @@
+/*
+ *      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.modules.auth.endpoint;
+
+import com.github.xiaoymin.knife4j.annotations.ApiSort;
+import com.wf.captcha.SpecCaptcha;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.log.annotation.ApiLog;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.support.Kv;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.modules.auth.provider.ITokenGranter;
+import org.springblade.modules.auth.provider.TokenGranterBuilder;
+import org.springblade.modules.auth.provider.TokenParameter;
+import org.springblade.modules.auth.utils.TokenUtil;
+import org.springblade.modules.system.entity.UserInfo;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.servlet.http.HttpServletResponse;
+import java.time.Duration;
+import java.util.UUID;
+
+import static org.springblade.core.cache.constant.CacheConstant.*;
+
+/**
+ * 令牌端点
+ *
+ * @author Chill
+ */
+@NonDS
+@ApiSort(1)
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_AUTH_NAME)
+@Api(value = "用户授权认证", tags = "授权接口")
+public class BladeTokenEndPoint {
+
+	private final BladeRedis bladeRedis;
+	private final JwtProperties jwtProperties;
+
+	@ApiLog("登录用户验证")
+	@PostMapping("/oauth/token")
+	@ApiOperation(value = "获取认证令牌", notes = "传入租户ID:tenantId,账号:account,密码:password")
+	public Kv token(@ApiParam(value = "租户ID", required = true) @RequestParam String tenantId,
+					@ApiParam(value = "账号", required = true) @RequestParam(required = false) String username,
+					@ApiParam(value = "密码", required = true) @RequestParam(required = false) String password,
+					@ApiIgnore @RequestHeader(name = TokenUtil.DEPT_HEADER_KEY, required = false) String deptId,
+					@ApiIgnore @RequestHeader(name = TokenUtil.ROLE_HEADER_KEY, required = false) String roleId) {
+
+		Kv authInfo = Kv.create();
+
+		String grantType = WebUtil.getRequest().getParameter("grant_type");
+		String refreshToken = WebUtil.getRequest().getParameter("refresh_token");
+
+		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
+
+		TokenParameter tokenParameter = new TokenParameter();
+		tokenParameter.getArgs().set("tenantId", tenantId)
+			.set("username", username)
+			.set("password", password)
+			.set("grantType", grantType)
+			.set("refreshToken", refreshToken)
+			.set("userType", userType)
+			.set("deptId", deptId).set("roleId", roleId);
+
+		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
+		UserInfo userInfo = granter.grant(tokenParameter);
+
+		if (userInfo == null || userInfo.getUser() == null) {
+			return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "用户名或密码不正确");
+		}
+
+		if (Func.isEmpty(userInfo.getRoles())) {
+			return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "未获得用户的角色信息");
+		}
+
+		return TokenUtil.createAuthInfo(userInfo);
+	}
+
+
+	@GetMapping("/oauth/logout")
+	@ApiOperation(value = "退出登录")
+	public Kv logout() {
+		BladeUser user = AuthUtil.getUser();
+		if (user != null && jwtProperties.getState()) {
+			String token = JwtUtil.getToken(WebUtil.getRequest().getHeader(TokenConstant.HEADER));
+			JwtUtil.removeAccessToken(user.getTenantId(), String.valueOf(user.getUserId()), token);
+		}
+		return Kv.create().set("success", "true").set("msg", "success");
+	}
+
+	@GetMapping("/oauth/captcha")
+	@ApiOperation(value = "获取验证码")
+	public Kv captcha() {
+		SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
+		String verCode = specCaptcha.text().toLowerCase();
+		String key = UUID.randomUUID().toString();
+		// 存入redis并设置过期时间为30分钟
+		bladeRedis.setEx(CacheNames.CAPTCHA_KEY + key, verCode, Duration.ofMinutes(30));
+		// 将key和base64返回给前端
+		return Kv.create().set("key", key).set("image", specCaptcha.toBase64());
+	}
+
+	@GetMapping("/oauth/clear-cache")
+	@ApiOperation(value = "清除缓存")
+	public Kv clearCache() {
+		CacheUtil.clear(BIZ_CACHE);
+		CacheUtil.clear(USER_CACHE);
+		CacheUtil.clear(DICT_CACHE);
+		CacheUtil.clear(FLOW_CACHE);
+		CacheUtil.clear(SYS_CACHE);
+		CacheUtil.clear(PARAM_CACHE);
+		CacheUtil.clear(RESOURCE_CACHE);
+		CacheUtil.clear(MENU_CACHE);
+		CacheUtil.clear(DICT_CACHE, Boolean.FALSE);
+		CacheUtil.clear(MENU_CACHE, Boolean.FALSE);
+		CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
+		CacheUtil.clear(PARAM_CACHE, Boolean.FALSE);
+		return Kv.create().set("success", "true").set("msg", "success");
+	}
+}

+ 50 - 0
admin/src/main/java/org/springblade/modules/auth/enums/UserEnum.java

@@ -0,0 +1,50 @@
+/*
+ *      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.modules.auth.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 用户类型枚举
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum UserEnum {
+
+	/**
+	 * web
+	 */
+	WEB("web", 1),
+
+	/**
+	 * app
+	 */
+	APP("app", 2),
+
+	/**
+	 * other
+	 */
+	OTHER("other", 3),
+	;
+
+	final String name;
+	final int category;
+
+}

+ 128 - 0
admin/src/main/java/org/springblade/modules/auth/granter/CaptchaTokenGranter.java

@@ -0,0 +1,128 @@
+/*
+ *      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.modules.auth.granter;
+
+import lombok.AllArgsConstructor;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.common.cache.ParamCache;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.tool.utils.DigestUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.modules.auth.enums.UserEnum;
+import org.springblade.modules.auth.provider.ITokenGranter;
+import org.springblade.modules.auth.provider.TokenParameter;
+import org.springblade.modules.auth.utils.TokenUtil;
+import org.springblade.modules.system.entity.Tenant;
+import org.springblade.modules.system.entity.UserInfo;
+import org.springblade.modules.system.service.IRoleService;
+import org.springblade.modules.system.service.ITenantService;
+import org.springblade.modules.system.service.IUserService;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * 验证码TokenGranter
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class CaptchaTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "captcha";
+	public static final Integer FAIL_COUNT = 5;
+	public static final String FAIL_COUNT_VALUE = "account.failCount";
+
+	private final IUserService userService;
+	private final IRoleService roleService;
+	private final ITenantService tenantService;
+	private final BladeRedis bladeRedis;
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		HttpServletRequest request = WebUtil.getRequest();
+
+		// 获取用户绑定ID
+		String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
+		String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
+		// 获取验证码信息
+		String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
+		String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
+		// 获取验证码
+		String redisCode = bladeRedis.get(CacheNames.CAPTCHA_KEY + key);
+		// 判断验证码
+		if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
+			throw new ServiceException(TokenUtil.CAPTCHA_NOT_CORRECT);
+		}
+
+		String tenantId = tokenParameter.getArgs().getStr("tenantId");
+		String username = tokenParameter.getArgs().getStr("username");
+		String password = tokenParameter.getArgs().getStr("password");
+
+		// 判断登录是否锁定
+		int cnt = Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
+		int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
+		if (cnt >= failCount) {
+			throw new ServiceException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+		}
+
+		UserInfo userInfo = null;
+		if (Func.isNoneBlank(username, password)) {
+			// 获取租户信息
+			Tenant tenant = tenantService.getByTenantId(tenantId);
+			if (TokenUtil.judgeTenant(tenant)) {
+				throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+			}
+			// 获取用户类型
+			String userType = tokenParameter.getArgs().getStr("userType");
+			// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+			if (userType.equals(UserEnum.WEB.getName())) {
+				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.WEB);
+			} else if (userType.equals(UserEnum.APP.getName())) {
+				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.APP);
+			} else {
+				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.OTHER);
+			}
+		}
+		if (userInfo == null || userInfo.getUser() == null) {
+			// 增加错误锁定次数
+			bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), cnt + 1, Duration.ofMinutes(30));
+		} else {
+			// 成功则清除登录缓存
+			bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
+			bladeRedis.del(CacheNames.CAPTCHA_KEY + key);
+		}
+		// 多部门情况下指定单部门
+		if (Func.isNotEmpty(headerDept) && userInfo != null && userInfo.getUser().getDeptId().contains(headerDept)) {
+			userInfo.getUser().setDeptId(headerDept);
+		}
+		// 多角色情况下指定单角色
+		if (Func.isNotEmpty(headerRole) && userInfo != null && userInfo.getUser().getRoleId().contains(headerRole)) {
+			List<String> roleResult = roleService.getRoleAliases(headerRole);
+			userInfo.setRoles(roleResult);
+			userInfo.getUser().setRoleId(headerRole);
+		}
+		return userInfo;
+	}
+
+}

+ 117 - 0
admin/src/main/java/org/springblade/modules/auth/granter/PasswordTokenGranter.java

@@ -0,0 +1,117 @@
+/*
+ *      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.modules.auth.granter;
+
+import lombok.AllArgsConstructor;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.common.cache.ParamCache;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.tool.utils.DigestUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.modules.auth.enums.UserEnum;
+import org.springblade.modules.auth.provider.ITokenGranter;
+import org.springblade.modules.auth.provider.TokenParameter;
+import org.springblade.modules.auth.utils.TokenUtil;
+import org.springblade.modules.system.entity.Tenant;
+import org.springblade.modules.system.entity.UserInfo;
+import org.springblade.modules.system.service.IRoleService;
+import org.springblade.modules.system.service.ITenantService;
+import org.springblade.modules.system.service.IUserService;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * PasswordTokenGranter
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class PasswordTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "password";
+	public static final Integer FAIL_COUNT = 5;
+	public static final String FAIL_COUNT_VALUE = "account.failCount";
+
+	private final IUserService userService;
+	private final IRoleService roleService;
+	private final ITenantService tenantService;
+	private final BladeRedis bladeRedis;
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		HttpServletRequest request = WebUtil.getRequest();
+		// 获取用户绑定ID
+		String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
+		String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
+
+		// 获取用户信息
+		String tenantId = tokenParameter.getArgs().getStr("tenantId");
+		String username = tokenParameter.getArgs().getStr("username");
+		String password = tokenParameter.getArgs().getStr("password");
+
+		// 判断登录是否锁定
+		int cnt = Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
+		int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
+		if (cnt >= failCount) {
+			throw new ServiceException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+		}
+
+		UserInfo userInfo = null;
+		if (Func.isNoneBlank(username, password)) {
+			// 获取租户信息
+			Tenant tenant = tenantService.getByTenantId(tenantId);
+			if (TokenUtil.judgeTenant(tenant)) {
+				throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+			}
+			// 获取用户类型
+			String userType = tokenParameter.getArgs().getStr("userType");
+			// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+			if (userType.equals(UserEnum.WEB.getName())) {
+				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.WEB);
+			} else if (userType.equals(UserEnum.APP.getName())) {
+				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.APP);
+			} else {
+				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.OTHER);
+			}
+		}
+		if (userInfo == null || userInfo.getUser() == null) {
+			// 增加错误锁定次数
+			bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), cnt + 1, Duration.ofMinutes(30));
+		} else {
+			// 成功则清除登录错误次数
+			bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
+		}
+		// 多部门情况下指定单部门
+		if (Func.isNotEmpty(headerDept) && userInfo != null && userInfo.getUser().getDeptId().contains(headerDept)) {
+			userInfo.getUser().setDeptId(headerDept);
+		}
+		// 多角色情况下指定单角色
+		if (Func.isNotEmpty(headerRole) && userInfo != null && userInfo.getUser().getRoleId().contains(headerRole)) {
+			List<String> roleAliases = roleService.getRoleAliases(headerRole);
+			userInfo.setRoles(roleAliases);
+			userInfo.getUser().setRoleId(headerRole);
+		}
+		return userInfo;
+	}
+
+}

+ 114 - 0
admin/src/main/java/org/springblade/modules/auth/granter/RefreshTokenGranter.java

@@ -0,0 +1,114 @@
+/*
+ *      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.modules.auth.granter;
+
+import io.jsonwebtoken.Claims;
+import lombok.AllArgsConstructor;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.modules.auth.provider.ITokenGranter;
+import org.springblade.modules.auth.provider.TokenParameter;
+import org.springblade.modules.auth.utils.TokenUtil;
+import org.springblade.modules.system.entity.Tenant;
+import org.springblade.modules.system.entity.UserInfo;
+import org.springblade.modules.system.service.IRoleService;
+import org.springblade.modules.system.service.ITenantService;
+import org.springblade.modules.system.service.IUserService;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * RefreshTokenGranter
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class RefreshTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "refresh_token";
+
+	private final IUserService userService;
+	private final IRoleService roleService;
+	private final ITenantService tenantService;
+	private final JwtProperties jwtProperties;
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		String tenantId = tokenParameter.getArgs().getStr("tenantId");
+		String grantType = tokenParameter.getArgs().getStr("grantType");
+		String refreshToken = tokenParameter.getArgs().getStr("refreshToken");
+		String deptId = tokenParameter.getArgs().getStr("deptId");
+		String roleId = tokenParameter.getArgs().getStr("roleId");
+		UserInfo userInfo = null;
+		if (Func.isNoneBlank(grantType, refreshToken) && grantType.equals(TokenConstant.REFRESH_TOKEN)) {
+			// 判断令牌合法性
+			if (!judgeRefreshToken(grantType, refreshToken)) {
+				throw new ServiceException(TokenUtil.TOKEN_NOT_PERMISSION);
+			}
+			Claims claims = AuthUtil.parseJWT(refreshToken);
+			if (claims != null) {
+				String tokenType = Func.toStr(claims.get(TokenConstant.TOKEN_TYPE));
+				if (tokenType.equals(TokenConstant.REFRESH_TOKEN)) {
+					// 获取租户信息
+					Tenant tenant = tenantService.getByTenantId(tenantId);
+					if (TokenUtil.judgeTenant(tenant)) {
+						throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+					}
+					// 获取用户信息
+					userInfo = userService.userInfo(Func.toLong(claims.get(TokenConstant.USER_ID)));
+					// 设置多部门信息
+					if (Func.isNotEmpty(deptId) && userInfo.getUser().getDeptId().contains(deptId)) {
+						userInfo.getUser().setDeptId(deptId);
+					}
+					// 设置多角色信息
+					if (Func.isNotEmpty(roleId) && userInfo.getUser().getRoleId().contains(roleId)) {
+						userInfo.getUser().setRoleId(roleId);
+						List<String> roleAliases = roleService.getRoleAliases(roleId);
+						userInfo.setRoles(roleAliases);
+					}
+				}
+			}
+		}
+		return userInfo;
+	}
+
+	/**
+	 * 校验refreshToken合法性
+	 *
+	 * @param grantType    认证类型
+	 * @param refreshToken refreshToken
+	 */
+	private boolean judgeRefreshToken(String grantType, String refreshToken) {
+		if (jwtProperties.getState() && jwtProperties.getSingle()) {
+			Claims claims = JwtUtil.parseJWT(refreshToken);
+			String tenantId = String.valueOf(claims.get("tenant_id"));
+			String userId = String.valueOf(claims.get("user_id"));
+			String token = JwtUtil.getRefreshToken(tenantId, userId, refreshToken);
+			return StringUtil.equalsIgnoreCase(token, refreshToken);
+		}
+		return true;
+	}
+
+
+}

+ 92 - 0
admin/src/main/java/org/springblade/modules/auth/granter/SocialTokenGranter.java

@@ -0,0 +1,92 @@
+/*
+ *      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.modules.auth.granter;
+
+import lombok.AllArgsConstructor;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthRequest;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.core.social.utils.SocialUtil;
+import org.springblade.core.tool.utils.BeanUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.modules.auth.provider.ITokenGranter;
+import org.springblade.modules.auth.provider.TokenParameter;
+import org.springblade.modules.auth.utils.TokenUtil;
+import org.springblade.modules.system.entity.UserInfo;
+import org.springblade.modules.system.entity.UserOauth;
+import org.springblade.modules.system.service.IUserService;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Objects;
+
+/**
+ * SocialTokenGranter
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class SocialTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "social";
+
+	private static final Integer AUTH_SUCCESS_CODE = 2000;
+
+	private final IUserService userService;
+	private final SocialProperties socialProperties;
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		HttpServletRequest request = WebUtil.getRequest();
+		String tenantId = Func.toStr(request.getHeader(TokenUtil.TENANT_HEADER_KEY), TokenUtil.DEFAULT_TENANT_ID);
+		// 开放平台来源
+		String sourceParameter = request.getParameter("source");
+		// 匹配是否有别名定义
+		String source = socialProperties.getAlias().getOrDefault(sourceParameter, sourceParameter);
+		// 开放平台授权码
+		String code = request.getParameter("code");
+		// 开放平台状态吗
+		String state = request.getParameter("state");
+
+		// 获取开放平台授权数据
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		AuthCallback authCallback = new AuthCallback();
+		authCallback.setCode(code);
+		authCallback.setState(state);
+		AuthResponse authResponse = authRequest.login(authCallback);
+		AuthUser authUser;
+		if (authResponse.getCode() == AUTH_SUCCESS_CODE) {
+			authUser = (AuthUser) authResponse.getData();
+		} else {
+			throw new ServiceException("social grant failure, auth response is not success");
+		}
+
+		// 组装数据
+		UserOauth userOauth = Objects.requireNonNull(BeanUtil.copy(authUser, UserOauth.class));
+		userOauth.setSource(authUser.getSource());
+		userOauth.setTenantId(tenantId);
+		userOauth.setUuid(authUser.getUuid());
+		// 返回UserInfo
+		return userService.userInfo(userOauth);
+	}
+
+}

+ 36 - 0
admin/src/main/java/org/springblade/modules/auth/provider/ITokenGranter.java

@@ -0,0 +1,36 @@
+/*
+ *      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.modules.auth.provider;
+
+import org.springblade.modules.system.entity.UserInfo;
+
+/**
+ * 授权认证统一接口.
+ *
+ * @author Chill
+ */
+public interface ITokenGranter {
+
+	/**
+	 * 获取用户信息
+	 *
+	 * @param tokenParameter 授权参数
+	 * @return UserInfo
+	 */
+	UserInfo grant(TokenParameter tokenParameter);
+
+}

+ 66 - 0
admin/src/main/java/org/springblade/modules/auth/provider/TokenGranterBuilder.java

@@ -0,0 +1,66 @@
+/*
+ *      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.modules.auth.provider;
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.secure.exception.SecureException;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.modules.auth.granter.CaptchaTokenGranter;
+import org.springblade.modules.auth.granter.PasswordTokenGranter;
+import org.springblade.modules.auth.granter.RefreshTokenGranter;
+import org.springblade.modules.auth.granter.SocialTokenGranter;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * TokenGranterBuilder
+ *
+ * @author Chill
+ */
+@AllArgsConstructor
+public class TokenGranterBuilder {
+
+	/**
+	 * TokenGranter缓存池
+	 */
+	private static final Map<String, ITokenGranter> GRANTER_POOL = new ConcurrentHashMap<>();
+
+	static {
+		GRANTER_POOL.put(PasswordTokenGranter.GRANT_TYPE, SpringUtil.getBean(PasswordTokenGranter.class));
+		GRANTER_POOL.put(CaptchaTokenGranter.GRANT_TYPE, SpringUtil.getBean(CaptchaTokenGranter.class));
+		GRANTER_POOL.put(RefreshTokenGranter.GRANT_TYPE, SpringUtil.getBean(RefreshTokenGranter.class));
+		GRANTER_POOL.put(SocialTokenGranter.GRANT_TYPE, SpringUtil.getBean(SocialTokenGranter.class));
+	}
+
+	/**
+	 * 获取TokenGranter
+	 *
+	 * @param grantType 授权类型
+	 * @return ITokenGranter
+	 */
+	public static ITokenGranter getGranter(String grantType) {
+		ITokenGranter tokenGranter = GRANTER_POOL.get(Func.toStr(grantType, PasswordTokenGranter.GRANT_TYPE));
+		if (tokenGranter == null) {
+			throw new SecureException("no grantType was found");
+		} else {
+			return GRANTER_POOL.get(Func.toStr(grantType, PasswordTokenGranter.GRANT_TYPE));
+		}
+	}
+
+}

+ 32 - 0
admin/src/main/java/org/springblade/modules/auth/provider/TokenParameter.java

@@ -0,0 +1,32 @@
+/*
+ *      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.modules.auth.provider;
+
+import lombok.Data;
+import org.springblade.core.tool.support.Kv;
+
+/**
+ * TokenParameter
+ *
+ * @author Chill
+ */
+@Data
+public class TokenParameter {
+
+	private Kv args = Kv.create();
+
+}

+ 171 - 0
admin/src/main/java/org/springblade/modules/auth/utils/TokenUtil.java

@@ -0,0 +1,171 @@
+/*
+ *      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.modules.auth.utils;
+
+import org.springblade.common.constant.TenantConstant;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.secure.TokenInfo;
+import org.springblade.core.secure.utils.SecureUtil;
+import org.springblade.core.tenant.BladeTenantProperties;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.support.Kv;
+import org.springblade.core.tool.utils.*;
+import org.springblade.modules.system.entity.Tenant;
+import org.springblade.modules.system.entity.User;
+import org.springblade.modules.system.entity.UserInfo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 认证工具类
+ *
+ * @author Chill
+ */
+public class TokenUtil {
+
+	public final static String DEPT_HEADER_KEY = "Dept-Id";
+	public final static String ROLE_HEADER_KEY = "Role-Id";
+	public final static String CAPTCHA_HEADER_KEY = "Captcha-Key";
+	public final static String CAPTCHA_HEADER_CODE = "Captcha-Code";
+	public final static String CAPTCHA_NOT_CORRECT = "验证码不正确";
+	public final static String TENANT_HEADER_KEY = "Tenant-Id";
+	public final static String DEFAULT_TENANT_ID = "000000";
+	public final static String USER_TYPE_HEADER_KEY = "User-Type";
+	public final static String DEFAULT_USER_TYPE = "web";
+	public final static String TOKEN_NOT_PERMISSION = "令牌授权已过期";
+	public final static String USER_NOT_FOUND = "用户名或密码错误";
+	public final static String USER_HAS_NO_ROLE = "未获得用户的角色信息";
+	public final static String USER_HAS_NO_TENANT = "未获得用户的租户信息";
+	public final static String USER_HAS_NO_TENANT_PERMISSION = "租户授权已过期,请联系管理员";
+	public final static String USER_HAS_TOO_MANY_FAILS = "登录错误次数过多,请稍后再试";
+	public final static String HEADER_KEY = "Authorization";
+	public final static String HEADER_PREFIX = "Basic ";
+	public final static String DEFAULT_AVATAR = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";
+
+	private static BladeTenantProperties tenantProperties;
+
+	/**
+	 * 获取租户配置
+	 *
+	 * @return tenantProperties
+	 */
+	private static BladeTenantProperties getTenantProperties() {
+		if (tenantProperties == null) {
+			tenantProperties = SpringUtil.getBean(BladeTenantProperties.class);
+		}
+		return tenantProperties;
+	}
+
+	/**
+	 * 创建认证token
+	 *
+	 * @param userInfo 用户信息
+	 * @return token
+	 */
+	public static Kv createAuthInfo(UserInfo userInfo) {
+		Kv authInfo = Kv.create();
+		User user = userInfo.getUser();
+		//设置jwt参数
+		Map<String, Object> param = new HashMap<>(16);
+		param.put(TokenConstant.TOKEN_TYPE, TokenConstant.ACCESS_TOKEN);
+		param.put(TokenConstant.TENANT_ID, user.getTenantId());
+		param.put(TokenConstant.USER_ID, Func.toStr(user.getId()));
+		param.put(TokenConstant.DEPT_ID, user.getDeptId());
+		param.put(TokenConstant.POST_ID, user.getPostId());
+		param.put(TokenConstant.ROLE_ID, user.getRoleId());
+		param.put(TokenConstant.OAUTH_ID, userInfo.getOauthId());
+		param.put(TokenConstant.ACCOUNT, user.getAccount());
+		param.put(TokenConstant.USER_NAME, user.getAccount());
+		param.put(TokenConstant.NICK_NAME, user.getName());
+		param.put(TokenConstant.REAL_NAME, user.getRealName());
+		param.put(TokenConstant.ROLE_NAME, Func.join(userInfo.getRoles()));
+		param.put(TokenConstant.DETAIL, userInfo.getDetail());
+
+		//拼装accessToken
+		try {
+			TokenInfo accessToken = SecureUtil.createJWT(param, "audience", "issuser", TokenConstant.ACCESS_TOKEN);
+			//返回accessToken
+			return authInfo.set(TokenConstant.TENANT_ID, user.getTenantId())
+				.set(TokenConstant.USER_ID, Func.toStr(user.getId()))
+				.set(TokenConstant.DEPT_ID, user.getDeptId())
+				.set(TokenConstant.POST_ID, user.getPostId())
+				.set(TokenConstant.ROLE_ID, user.getRoleId())
+				.set(TokenConstant.OAUTH_ID, userInfo.getOauthId())
+				.set(TokenConstant.ACCOUNT, user.getAccount())
+				.set(TokenConstant.USER_NAME, user.getAccount())
+				.set(TokenConstant.NICK_NAME, user.getName())
+				.set(TokenConstant.REAL_NAME, user.getRealName())
+				.set(TokenConstant.ROLE_NAME, Func.join(userInfo.getRoles()))
+				.set(TokenConstant.AVATAR, Func.toStr(user.getAvatar(), TokenConstant.DEFAULT_AVATAR))
+				.set(TokenConstant.ACCESS_TOKEN, accessToken.getToken())
+				.set(TokenConstant.REFRESH_TOKEN, createRefreshToken(userInfo).getToken())
+				.set(TokenConstant.TOKEN_TYPE, TokenConstant.BEARER)
+				.set(TokenConstant.EXPIRES_IN, accessToken.getExpire())
+				.set(TokenConstant.DETAIL, userInfo.getDetail())
+				.set(TokenConstant.LICENSE, TokenConstant.LICENSE_NAME);
+		} catch (Exception ex) {
+			return authInfo.set("error_code", HttpServletResponse.SC_UNAUTHORIZED).set("error_description", ex.getMessage());
+		}
+	}
+
+	/**
+	 * 创建refreshToken
+	 *
+	 * @param userInfo 用户信息
+	 * @return refreshToken
+	 */
+	private static TokenInfo createRefreshToken(UserInfo userInfo) {
+		User user = userInfo.getUser();
+		Map<String, Object> param = new HashMap<>(16);
+		param.put(TokenConstant.TOKEN_TYPE, TokenConstant.REFRESH_TOKEN);
+		param.put(TokenConstant.USER_ID, Func.toStr(user.getId()));
+		param.put(TokenConstant.DEPT_ID, Func.toStr(user.getDeptId()));
+		param.put(TokenConstant.ROLE_ID, Func.toStr(user.getRoleId()));
+		return SecureUtil.createJWT(param, "audience", "issuser", TokenConstant.REFRESH_TOKEN);
+	}
+
+	/**
+	 * 判断租户权限
+	 *
+	 * @param tenant 租户信息
+	 * @return boolean
+	 */
+	public static boolean judgeTenant(Tenant tenant) {
+		if (tenant == null) {
+			throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT);
+		}
+		if (StringUtil.equalsIgnoreCase(tenant.getTenantId(), BladeConstant.ADMIN_TENANT_ID)) {
+			return false;
+		}
+		Date expireTime = tenant.getExpireTime();
+		if (getTenantProperties().getLicense()) {
+			String licenseKey = tenant.getLicenseKey();
+			String decrypt = DesUtil.decryptFormHex(licenseKey, TenantConstant.DES_KEY);
+			expireTime = JsonUtil.parse(decrypt, Tenant.class).getExpireTime();
+		}
+		if (expireTime != null && expireTime.before(DateUtil.now())) {
+			throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+		}
+		return false;
+	}
+
+}

+ 212 - 0
admin/src/main/java/org/springblade/modules/desk/controller/DashBoardController.java

@@ -0,0 +1,212 @@
+package org.springblade.modules.desk.controller;
+
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.support.Kv;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.annotations.ApiIgnore;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 首页
+ *
+ * @author Chill
+ */
+@NonDS
+@ApiIgnore
+@RestController
+@RequestMapping(AppConstant.APPLICATION_DESK_NAME)
+@AllArgsConstructor
+@Api(value = "首页", tags = "首页")
+public class DashBoardController {
+
+	/**
+	 * 活跃用户
+	 */
+	@GetMapping("/dashboard/activities")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "活跃用户", notes = "活跃用户")
+	public R activities() {
+		List<Map<String, Object>> list = new ArrayList<>();
+
+		Map<String, Object> map1 = new HashMap<>(16);
+		map1.put("id", "trend-1");
+		map1.put("updatedAt", "2019-01-01");
+		map1.put("user", Kv.create().set("name", "曲丽丽").set("avatar", "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"));
+		map1.put("group", Kv.create().set("name", "高逼格设计天团").set("link", "http://github.com/"));
+		map1.put("project", Kv.create().set("name", "六月迭代").set("link", "http://github.com/"));
+		map1.put("template", "在 @{group} 新建项目 @{project}");
+		list.add(map1);
+
+		Map<String, Object> map2 = new HashMap<>(16);
+		map2.put("id", "trend-2");
+		map2.put("updatedAt", "2019-01-01");
+		map2.put("user", Kv.create().set("name", "付小小").set("avatar", "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"));
+		map2.put("group", Kv.create().set("name", "高逼格设计天团").set("link", "http://github.com/"));
+		map2.put("project", Kv.create().set("name", "七月月迭代").set("link", "http://github.com/"));
+		map2.put("template", "在  @{group} 新建项目 @{project}");
+		list.add(map2);
+
+		return R.data(list);
+	}
+
+	/**
+	 * 用户信息
+	 */
+	@GetMapping("/dashboard/info")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "用户信息", notes = "用户信息")
+	public R info() {
+		Map<String, Object> map = new HashMap<>(16);
+		map.put("id", "trend-1");
+		map.put("updatedAt", "2019-01-01");
+		map.put("user", Kv.create().set("name", "曲丽丽").set("avatar", "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"));
+		map.put("group", Kv.create().set("name", "高逼格设计天团").set("link", "http://github.com/"));
+		map.put("project", Kv.create().set("name", "六月迭代").set("link", "http://github.com/"));
+		map.put("template", "在 @{group} 新建项目 @{project}");
+		return R.data(map);
+	}
+
+	/**
+	 * 签名信息
+	 */
+	@PostMapping("/dashboard/sign")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "签名信息", notes = "签名信息")
+	public R sign() {
+		Map<String, Object> map = new HashMap<>(16);
+		map.put("user", Kv.create().set("name", "曲丽丽").set("avatar", "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"));
+		return R.data(map);
+	}
+
+	/**
+	 * 获取消息
+	 */
+	@GetMapping("/notice/notices")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "消息", notes = "消息")
+	public R notices() {
+		List<Map<String, String>> list = new ArrayList<>();
+		Map<String, String> map1 = new HashMap<>(16);
+		map1.put("logo", "https://spring.io/img/homepage/icon-spring-framework.svg");
+		map1.put("title", "SpringBoot");
+		map1.put("description", "现在的web项目几乎都会用到spring框架,而要使用spring难免需要配置大量的xml配置文件,而 springboot的出现解   决了这一问题,一个项目甚至不用部署到服务器上直接开跑,真像springboot所说:“just run”。");
+		map1.put("member", "Chill");
+		map1.put("href", "http://spring.io/projects/spring-boot");
+		list.add(map1);
+
+		Map<String, String> map2 = new HashMap<>(16);
+		map2.put("logo", "https://spring.io/img/homepage/icon-spring-cloud.svg");
+		map2.put("title", "SpringCloud");
+		map2.put("description", "SpringCloud是基于SpringBoot的一整套实现微服务的框架。他提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。");
+		map2.put("member", "Chill");
+		map2.put("href", "http://spring.io/projects/spring-cloud");
+		list.add(map2);
+
+		Map<String, String> map3 = new HashMap<>(16);
+		map3.put("logo", "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1546359961068&di=05ff9406e6675ca9a58a525a7e7950b9&imgtype=jpg&src=http%3A%2F%2Fimg0.imgtn.bdimg.com%2Fit%2Fu%3D575314515%2C4268715674%26fm%3D214%26gp%3D0.jpg");
+		map3.put("title", "Mybatis");
+		map3.put("description", "MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。");
+		map3.put("member", "Chill");
+		map3.put("href", "http://www.mybatis.org/mybatis-3/getting-started.html");
+		list.add(map3);
+
+		Map<String, String> map4 = new HashMap<>(16);
+		map4.put("logo", "https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png");
+		map4.put("title", "React");
+		map4.put("description", "React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。");
+		map4.put("member", "Chill");
+		map4.put("href", "https://reactjs.org/");
+		list.add(map4);
+
+		Map<String, String> map5 = new HashMap<>(16);
+		map5.put("logo", "https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png");
+		map5.put("title", "Ant Design");
+		map5.put("description", "蚂蚁金服体验技术部经过大量的项目实践和总结,沉淀出设计语言 Ant Design,这可不单纯只是设计原则、控件规范和视觉尺寸,还配套有前端代码实现方案。也就是说采用Ant Design后,UI设计和前端界面研发可同步完成,效率大大提升。");
+		map5.put("member", "Chill");
+		map5.put("href", "https://ant.design/docs/spec/introduce-cn");
+		list.add(map5);
+
+		Map<String, String> map6 = new HashMap<>(16);
+		map6.put("logo", "https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png");
+		map6.put("title", "Ant Design Pro");
+		map6.put("description", "Ant Design Pro 是一个企业级开箱即用的中后台前端/设计解决方案。符合阿里追求的'敏捷的前端+强大的中台'的思想。");
+		map6.put("member", "Chill");
+		map6.put("href", "https://pro.ant.design");
+		list.add(map6);
+
+		return R.data(list);
+	}
+
+	/**
+	 * 获取我的消息
+	 */
+	@GetMapping("/notice/my-notices")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "消息", notes = "消息")
+	public R myNotices() {
+		List<Map<String, String>> list = new ArrayList<>();
+		Map<String, String> map1 = new HashMap<>(16);
+		map1.put("id", "000000001");
+		map1.put("avatar", "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png");
+		map1.put("title", "你收到了 14 份新周报");
+		map1.put("datetime", "2018-08-09");
+		map1.put("type", "notification");
+		list.add(map1);
+
+		Map<String, String> map2 = new HashMap<>(16);
+		map2.put("id", "000000002");
+		map2.put("avatar", "https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png");
+		map2.put("title", "你推荐的 曲妮妮 已通过第三轮面试");
+		map2.put("datetime", "2018-08-08");
+		map2.put("type", "notification");
+		list.add(map2);
+
+
+		Map<String, String> map3 = new HashMap<>(16);
+		map3.put("id", "000000003");
+		map3.put("avatar", "https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg");
+		map3.put("title", "曲丽丽 评论了你");
+		map3.put("description", "描述信息描述信息描述信息");
+		map3.put("datetime", "2018-08-07");
+		map3.put("type", "message");
+		map3.put("clickClose", "true");
+		list.add(map3);
+
+
+		Map<String, String> map4 = new HashMap<>(16);
+		map4.put("id", "000000004");
+		map4.put("avatar", "https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg");
+		map4.put("title", "朱偏右 回复了你");
+		map4.put("description", "这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像");
+		map4.put("type", "message");
+		map4.put("datetime", "2018-08-07");
+		map4.put("clickClose", "true");
+		list.add(map4);
+
+
+		Map<String, String> map5 = new HashMap<>(16);
+		map5.put("id", "000000005");
+		map5.put("title", "任务名称");
+		map5.put("description", "任务需要在 2018-01-12 20:00 前启动");
+		map5.put("extra", "未开始");
+		map5.put("status", "todo");
+		map5.put("type", "event");
+		list.add(map5);
+
+		return R.data(list);
+	}
+
+}

+ 138 - 0
admin/src/main/java/org/springblade/modules/desk/controller/NoticeController.java

@@ -0,0 +1,138 @@
+/*
+ *      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.modules.desk.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSort;
+import io.swagger.annotations.*;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.tenant.annotation.TenantDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.desk.entity.Notice;
+import org.springblade.modules.desk.service.INoticeService;
+import org.springblade.modules.desk.vo.NoticeVO;
+import org.springblade.modules.desk.wrapper.NoticeWrapper;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import java.util.Map;
+
+/**
+ * 控制器
+ *
+ * @author Chill
+ */
+@TenantDS
+@RestController
+@RequestMapping(AppConstant.APPLICATION_DESK_NAME + "/notice")
+@AllArgsConstructor
+@ApiSort(2)
+@Api(value = "用户博客", tags = "博客接口")
+public class NoticeController extends BladeController {
+
+	private final INoticeService noticeService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入notice")
+	public R<NoticeVO> detail(Notice notice) {
+		Notice detail = noticeService.getOne(Condition.getQueryWrapper(notice));
+		return R.data(NoticeWrapper.build().entityVO(detail));
+	}
+
+	/**
+	 * 分页
+	 */
+	@GetMapping("/list")
+	@ApiImplicitParams({
+		@ApiImplicitParam(name = "category", value = "公告类型", paramType = "query", dataType = "integer"),
+		@ApiImplicitParam(name = "title", value = "公告标题", paramType = "query", dataType = "string")
+	})
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入notice")
+	public R<IPage<NoticeVO>> list(@ApiIgnore @RequestParam Map<String, Object> notice, Query query) {
+		NoticeWrapper.build().noticeQuery(notice);
+		IPage<Notice> pages = noticeService.page(Condition.getPage(query), Condition.getQueryWrapper(notice, Notice.class));
+		return R.data(NoticeWrapper.build().pageVO(pages));
+	}
+
+	/**
+	 * 多表联合查询自定义分页
+	 */
+	@GetMapping("/page")
+	@ApiImplicitParams({
+		@ApiImplicitParam(name = "category", value = "公告类型", paramType = "query", dataType = "integer"),
+		@ApiImplicitParam(name = "title", value = "公告标题", paramType = "query", dataType = "string")
+	})
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "分页", notes = "传入notice")
+	public R<IPage<NoticeVO>> page(@ApiIgnore NoticeVO notice, Query query) {
+		IPage<NoticeVO> pages = noticeService.selectNoticePage(Condition.getPage(query), notice);
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "新增", notes = "传入notice")
+	public R save(@RequestBody Notice notice) {
+		return R.status(noticeService.save(notice));
+	}
+
+	/**
+	 * 修改
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "修改", notes = "传入notice")
+	public R update(@RequestBody Notice notice) {
+		return R.status(noticeService.updateById(notice));
+	}
+
+	/**
+	 * 新增或修改
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "新增或修改", notes = "传入notice")
+	public R submit(@RequestBody Notice notice) {
+		return R.status(noticeService.saveOrUpdate(notice));
+	}
+
+	/**
+	 * 删除
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "逻辑删除", notes = "传入notice")
+	public R remove(@ApiParam(value = "主键集合") @RequestParam String ids) {
+		boolean temp = noticeService.deleteLogic(Func.toLongList(ids));
+		return R.status(temp);
+	}
+
+}

+ 64 - 0
admin/src/main/java/org/springblade/modules/desk/entity/Notice.java

@@ -0,0 +1,64 @@
+/*
+ *      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.modules.desk.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+import java.util.Date;
+
+/**
+ * 实体类
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("blade_notice")
+public class Notice extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 标题
+	 */
+	@ApiModelProperty(value = "标题")
+	private String title;
+
+	/**
+	 * 通知类型
+	 */
+	@ApiModelProperty(value = "通知类型")
+	private Integer category;
+
+	/**
+	 * 发布日期
+	 */
+	@ApiModelProperty(value = "发布日期")
+	private Date releaseTime;
+
+	/**
+	 * 内容
+	 */
+	@ApiModelProperty(value = "内容")
+	private String content;
+
+
+}

+ 50 - 0
admin/src/main/java/org/springblade/modules/desk/mapper/NoticeMapper.java

@@ -0,0 +1,50 @@
+/*
+ *      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.modules.desk.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springblade.modules.desk.entity.Notice;
+import org.springblade.modules.desk.vo.NoticeVO;
+
+import java.util.List;
+
+/**
+ * Mapper 接口
+ *
+ * @author Chill
+ */
+public interface NoticeMapper extends BaseMapper<Notice> {
+
+	/**
+	 * 前N条数据
+	 *
+	 * @param number 数量
+	 * @return List<Notice>
+	 */
+	List<Notice> topList(Integer number);
+
+	/**
+	 * 自定义分页
+	 *
+	 * @param page   分页
+	 * @param notice 实体
+	 * @return List<NoticeVO>
+	 */
+	List<NoticeVO> selectNoticePage(IPage page, NoticeVO notice);
+
+}

+ 53 - 0
admin/src/main/java/org/springblade/modules/desk/mapper/NoticeMapper.xml

@@ -0,0 +1,53 @@
+<?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="org.springblade.modules.desk.mapper.NoticeMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="noticeResultMap" type="org.springblade.modules.desk.entity.Notice">
+        <result column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="release_time" property="releaseTime"/>
+        <result column="title" property="title"/>
+        <result column="content" property="content"/>
+    </resultMap>
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="noticeVOResultMap" type="org.springblade.modules.desk.vo.NoticeVO">
+        <result column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="release_time" property="releaseTime"/>
+        <result column="title" property="title"/>
+        <result column="content" property="content"/>
+    </resultMap>
+
+    <select id="topList" resultMap="noticeResultMap">
+        select * from blade_notice limit #{number}
+    </select>
+
+    <select id="selectNoticePage" resultMap="noticeVOResultMap">
+        SELECT
+        n.*,
+        d.dict_value AS categoryName
+        FROM
+        blade_notice n
+        LEFT JOIN ( SELECT * FROM blade_dict WHERE CODE = 'notice' ) d ON n.category = d.dict_key
+        WHERE
+        n.is_deleted = 0 and n.tenant_id = #{notice.tenantId}
+        <if test="notice.title!=null">
+            and n.title like concat(concat('%', #{notice.title}), '%')
+        </if>
+        <if test="notice.category!=null">
+            and n.category = #{notice.category}
+        </if>
+    </select>
+</mapper>

+ 39 - 0
admin/src/main/java/org/springblade/modules/desk/service/INoticeService.java

@@ -0,0 +1,39 @@
+/*
+ *      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.modules.desk.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.modules.desk.entity.Notice;
+import org.springblade.modules.desk.vo.NoticeVO;
+
+/**
+ * 服务类
+ *
+ * @author Chill
+ */
+public interface INoticeService extends BaseService<Notice> {
+
+	/**
+	 * 自定义分页
+	 * @param page
+	 * @param notice
+	 * @return
+	 */
+	IPage<NoticeVO> selectNoticePage(IPage<NoticeVO> page, NoticeVO notice);
+
+}

+ 43 - 0
admin/src/main/java/org/springblade/modules/desk/service/impl/NoticeServiceImpl.java

@@ -0,0 +1,43 @@
+/*
+ *      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.modules.desk.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.modules.desk.entity.Notice;
+import org.springblade.modules.desk.mapper.NoticeMapper;
+import org.springblade.modules.desk.service.INoticeService;
+import org.springblade.modules.desk.vo.NoticeVO;
+import org.springframework.stereotype.Service;
+
+/**
+ * 服务实现类
+ *
+ * @author Chill
+ */
+@Service
+public class NoticeServiceImpl extends BaseServiceImpl<NoticeMapper, Notice> implements INoticeService {
+
+	@Override
+	public IPage<NoticeVO> selectNoticePage(IPage<NoticeVO> page, NoticeVO notice) {
+		// 若不使用mybatis-plus自带的分页方法,则不会自动带入tenantId,所以我们需要自行注入
+		notice.setTenantId(AuthUtil.getTenantId());
+		return page.setRecords(baseMapper.selectNoticePage(page, notice));
+	}
+
+}

+ 23 - 0
admin/src/main/java/org/springblade/modules/desk/vo/NoticeVO.java

@@ -0,0 +1,23 @@
+package org.springblade.modules.desk.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.modules.desk.entity.Notice;
+
+/**
+ * 通知公告视图类
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class NoticeVO extends Notice {
+
+	@ApiModelProperty(value = "通知类型名")
+	private String categoryName;
+
+	@ApiModelProperty(value = "租户编号")
+	private String tenantId;
+
+}

+ 64 - 0
admin/src/main/java/org/springblade/modules/desk/wrapper/NoticeWrapper.java

@@ -0,0 +1,64 @@
+/*
+ *      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.modules.desk.wrapper;
+
+import org.springblade.common.cache.DictCache;
+import org.springblade.common.enums.DictEnum;
+import org.springblade.core.mp.support.BaseEntityWrapper;
+import org.springblade.core.tool.utils.BeanUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.desk.entity.Notice;
+import org.springblade.modules.desk.vo.NoticeVO;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Notice包装类,返回视图层所需的字段
+ *
+ * @author Chill
+ */
+public class NoticeWrapper extends BaseEntityWrapper<Notice, NoticeVO> {
+
+	public static NoticeWrapper build() {
+		return new NoticeWrapper();
+	}
+
+	@Override
+	public NoticeVO entityVO(Notice notice) {
+		NoticeVO noticeVO = Objects.requireNonNull(BeanUtil.copy(notice, NoticeVO.class));
+		String dictValue = DictCache.getValue(DictEnum.NOTICE, noticeVO.getCategory());
+		noticeVO.setCategoryName(dictValue);
+		return noticeVO;
+	}
+
+	/**
+	 * 查询条件处理
+	 */
+	public void noticeQuery(Map<String, Object> notice) {
+		// 此场景仅在 pg数据库 map类型传参的情况下需要处理,entity传参已经包含数据类型,则无需关心
+		// 针对 pg数据库 int类型字段查询需要强转的处理示例
+		String searchKey = "category";
+		if (Func.isNotEmpty(notice.get(searchKey))) {
+			// 数据库字段为int类型,设置"="查询,具体查询参数请见 @org.springblade.core.mp.support.SqlKeyword
+			notice.put(searchKey.concat("_equal"), Func.toInt(notice.get(searchKey)));
+			// 默认"like"查询,pg数据库 场景会报错,所以将其删除
+			notice.remove(searchKey);
+		}
+	}
+
+}

+ 193 - 0
admin/src/main/java/org/springblade/modules/develop/controller/CodeController.java

@@ -0,0 +1,193 @@
+/*
+ *      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.modules.develop.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.*;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.secure.annotation.PreAuth;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.constant.RoleConstant;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.develop.constant.DevelopConstant;
+import org.springblade.develop.support.BladeCodeGenerator;
+import org.springblade.modules.develop.entity.Code;
+import org.springblade.modules.develop.entity.Datasource;
+import org.springblade.modules.develop.entity.Model;
+import org.springblade.modules.develop.entity.ModelPrototype;
+import org.springblade.modules.develop.service.ICodeService;
+import org.springblade.modules.develop.service.IDatasourceService;
+import org.springblade.modules.develop.service.IModelPrototypeService;
+import org.springblade.modules.develop.service.IModelService;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 控制器
+ *
+ * @author Chill
+ */
+@NonDS
+@ApiIgnore
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_DEVELOP_NAME + "/code")
+@Api(value = "代码生成", tags = "代码生成")
+@PreAuth(RoleConstant.HAS_ROLE_ADMINISTRATOR)
+public class CodeController extends BladeController {
+
+	private final ICodeService codeService;
+	private final IDatasourceService datasourceService;
+	private final IModelService modelService;
+	private final IModelPrototypeService modelPrototypeService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入code")
+	public R<Code> detail(Code code) {
+		Code detail = codeService.getOne(Condition.getQueryWrapper(code));
+		return R.data(detail);
+	}
+
+	/**
+	 * 分页
+	 */
+	@GetMapping("/list")
+	@ApiImplicitParams({
+		@ApiImplicitParam(name = "codeName", value = "模块名", paramType = "query", dataType = "string"),
+		@ApiImplicitParam(name = "tableName", value = "表名", paramType = "query", dataType = "string"),
+		@ApiImplicitParam(name = "modelName", value = "实体名", paramType = "query", dataType = "string")
+	})
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入code")
+	public R<IPage<Code>> list(@ApiIgnore @RequestParam Map<String, Object> code, Query query) {
+		IPage<Code> pages = codeService.page(Condition.getPage(query), Condition.getQueryWrapper(code, Code.class));
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增或修改
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "新增或修改", notes = "传入code")
+	public R submit(@Valid @RequestBody Code code) {
+		return R.status(codeService.submit(code));
+	}
+
+
+	/**
+	 * 删除
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		return R.status(codeService.removeByIds(Func.toLongList(ids)));
+	}
+
+	/**
+	 * 复制
+	 */
+	@PostMapping("/copy")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "复制", notes = "传入id")
+	public R copy(@ApiParam(value = "主键", required = true) @RequestParam Long id) {
+		Code code = codeService.getById(id);
+		code.setId(null);
+		code.setCodeName(code.getCodeName() + "-copy");
+		return R.status(codeService.save(code));
+	}
+
+	/**
+	 * 代码生成
+	 */
+	@PostMapping("/gen-code")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "代码生成", notes = "传入ids")
+	public R genCode(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		Collection<Code> codes = codeService.listByIds(Func.toLongList(ids));
+		codes.forEach(code -> {
+			BladeCodeGenerator generator = new BladeCodeGenerator();
+			// 设置基础模型
+			Model model = modelService.getById(code.getModelId());
+			generator.setModelCode(model.getModelCode());
+			generator.setModelClass(model.getModelClass());
+			// 设置模型集合
+			List<ModelPrototype> prototypes = modelPrototypeService.prototypeList(model.getId());
+			generator.setModel(JsonUtil.readMap(JsonUtil.toJson(model)));
+			generator.setPrototypes(JsonUtil.readListMap(JsonUtil.toJson(prototypes)));
+			if (StringUtil.isNotBlank(code.getSubModelId()) && StringUtil.equals(code.getTemplateType(), DevelopConstant.TEMPLATE_SUB)) {
+				Model subModel = modelService.getById(Func.toLong(code.getSubModelId()));
+				List<ModelPrototype> subPrototypes = modelPrototypeService.prototypeList(subModel.getId());
+				generator.setSubModel(JsonUtil.readMap(JsonUtil.toJson(subModel)));
+				generator.setSubPrototypes(JsonUtil.readListMap(JsonUtil.toJson(subPrototypes)));
+			}
+			// 设置数据源
+			Datasource datasource = datasourceService.getById(model.getDatasourceId());
+			generator.setDriverName(datasource.getDriverClass());
+			generator.setUrl(datasource.getUrl());
+			generator.setUsername(datasource.getUsername());
+			generator.setPassword(datasource.getPassword());
+			// 设置基础配置
+			generator.setCodeStyle(code.getCodeStyle());
+			generator.setCodeName(code.getCodeName());
+			generator.setServiceName(code.getServiceName());
+			generator.setPackageName(code.getPackageName());
+			generator.setPackageDir(code.getApiPath());
+			generator.setPackageWebDir(code.getWebPath());
+			generator.setTablePrefix(Func.toStrArray(code.getTablePrefix()));
+			generator.setIncludeTables(Func.toStrArray(code.getTableName()));
+			// 设置模版信息
+			generator.setTemplateType(code.getTemplateType());
+			generator.setAuthor(code.getAuthor());
+			generator.setSubModelId(code.getSubModelId());
+			generator.setSubFkId(code.getSubFkId());
+			generator.setTreeId(code.getTreeId());
+			generator.setTreePid(code.getTreePid());
+			generator.setTreeName(code.getTreeName());
+			// 设置是否继承基础业务字段
+			generator.setHasSuperEntity(code.getBaseMode() == 2);
+			// 设置是否开启包装器模式
+			generator.setHasWrapper(code.getWrapMode() == 2);
+			// 设置是否开启远程调用模式
+			generator.setHasFeign(code.getFeignMode() == 2);
+			// 设置控制器服务名前缀
+			generator.setHasServiceName(Boolean.TRUE);
+			// 启动代码生成
+			generator.run();
+		});
+		return R.success("代码生成成功");
+	}
+
+}

+ 128 - 0
admin/src/main/java/org/springblade/modules/develop/controller/DatasourceController.java

@@ -0,0 +1,128 @@
+/*
+ *      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.modules.develop.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.develop.entity.Datasource;
+import org.springblade.modules.develop.service.IDatasourceService;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 数据源配置表 控制器
+ *
+ * @author Chill
+ */
+@NonDS
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_DEVELOP_NAME + "/datasource")
+@Api(value = "数据源配置表", tags = "数据源配置表接口")
+public class DatasourceController extends BladeController {
+
+	private final IDatasourceService datasourceService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入datasource")
+	public R<Datasource> detail(Datasource datasource) {
+		Datasource detail = datasourceService.getOne(Condition.getQueryWrapper(datasource));
+		return R.data(detail);
+	}
+
+	/**
+	 * 分页 数据源配置表
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入datasource")
+	public R<IPage<Datasource>> list(Datasource datasource, Query query) {
+		IPage<Datasource> pages = datasourceService.page(Condition.getPage(query), Condition.getQueryWrapper(datasource));
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增 数据源配置表
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "新增", notes = "传入datasource")
+	public R save(@Valid @RequestBody Datasource datasource) {
+		return R.status(datasourceService.save(datasource));
+	}
+
+	/**
+	 * 修改 数据源配置表
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "修改", notes = "传入datasource")
+	public R update(@Valid @RequestBody Datasource datasource) {
+		return R.status(datasourceService.updateById(datasource));
+	}
+
+	/**
+	 * 新增或修改 数据源配置表
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "新增或修改", notes = "传入datasource")
+	public R submit(@Valid @RequestBody Datasource datasource) {
+		datasource.setUrl(datasource.getUrl().replace("&amp;", "&"));
+		return R.status(datasourceService.saveOrUpdate(datasource));
+	}
+
+
+	/**
+	 * 删除 数据源配置表
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		return R.status(datasourceService.deleteLogic(Func.toLongList(ids)));
+	}
+
+	/**
+	 * 数据源列表
+	 */
+	@GetMapping("/select")
+	@ApiOperationSupport(order = 8)
+	@ApiOperation(value = "下拉数据源", notes = "查询列表")
+	public R<List<Datasource>> select() {
+		List<Datasource> list = datasourceService.list();
+		return R.data(list);
+	}
+
+}

+ 251 - 0
admin/src/main/java/org/springblade/modules/develop/controller/ModelController.java

@@ -0,0 +1,251 @@
+/*
+ *      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.modules.develop.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
+import com.baomidou.mybatisplus.generator.config.StrategyConfig;
+import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.launch.constant.AppConstant;
+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.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.modules.develop.entity.Datasource;
+import org.springblade.modules.develop.entity.Model;
+import org.springblade.modules.develop.entity.ModelPrototype;
+import org.springblade.modules.develop.service.IDatasourceService;
+import org.springblade.modules.develop.service.IModelPrototypeService;
+import org.springblade.modules.develop.service.IModelService;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 数据模型表 控制器
+ *
+ * @author Chill
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_DEVELOP_NAME + "/model")
+@Api(value = "数据模型表", tags = "数据模型表接口")
+public class ModelController extends BladeController {
+
+	private final IModelService modelService;
+	private final IModelPrototypeService modelPrototypeService;
+	private final IDatasourceService datasourceService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入model")
+	public R<Model> detail(Model model) {
+		Model detail = modelService.getOne(Condition.getQueryWrapper(model));
+		return R.data(detail);
+	}
+
+	/**
+	 * 分页 数据模型表
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入model")
+	public R<IPage<Model>> list(Model model, Query query) {
+		IPage<Model> pages = modelService.page(Condition.getPage(query), Condition.getQueryWrapper(model));
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增 数据模型表
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "新增", notes = "传入model")
+	public R save(@Valid @RequestBody Model model) {
+		return R.status(modelService.save(model));
+	}
+
+	/**
+	 * 修改 数据模型表
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "修改", notes = "传入model")
+	public R update(@Valid @RequestBody Model model) {
+		return R.status(modelService.updateById(model));
+	}
+
+	/**
+	 * 新增或修改 数据模型表
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "新增或修改", notes = "传入model")
+	public R submit(@Valid @RequestBody Model model) {
+		boolean temp = modelService.saveOrUpdate(model);
+		if (temp) {
+			return R.data(model);
+		} else {
+			return R.status(Boolean.FALSE);
+		}
+	}
+
+	/**
+	 * 删除 数据模型表
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		return R.status(modelService.delete(Func.toLongList(ids)));
+	}
+
+	/**
+	 * 模型列表
+	 */
+	@GetMapping("/select")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "模型列表", notes = "模型列表")
+	public R<List<Model>> select() {
+		List<Model> list = modelService.list();
+		list.forEach(model -> model.setModelName(model.getModelTable() + StringPool.COLON + StringPool.SPACE + model.getModelName()));
+		return R.data(list);
+	}
+
+	/**
+	 * 获取物理表列表
+	 */
+	@GetMapping("/table-list")
+	@ApiOperationSupport(order = 8)
+	@ApiOperation(value = "物理表列表", notes = "传入datasourceId")
+	public R<List<TableInfo>> tableList(Long datasourceId) {
+		Datasource datasource = datasourceService.getById(datasourceId);
+		ConfigBuilder config = getConfigBuilder(datasource);
+		List<TableInfo> tableInfoList = config.getTableInfoList().stream()
+			.filter(tableInfo -> !StringUtil.startsWithIgnoreCase(tableInfo.getName(), "ACT_"))
+			.map(tableInfo -> tableInfo.setComment(tableInfo.getName() + StringPool.COLON + tableInfo.getComment()))
+			.collect(Collectors.toList());
+		return R.data(tableInfoList);
+	}
+
+	/**
+	 * 获取物理表信息
+	 */
+	@GetMapping("/table-info")
+	@ApiOperationSupport(order = 9)
+	@ApiOperation(value = "物理表信息", notes = "传入model信息")
+	public R<TableInfo> tableInfo(Long modelId, String tableName, Long datasourceId) {
+		if (StringUtil.isBlank(tableName)) {
+			Model model = modelService.getById(modelId);
+			tableName = model.getModelTable();
+		}
+		TableInfo tableInfo = getTableInfo(tableName, datasourceId);
+		return R.data(tableInfo);
+	}
+
+	/**
+	 * 获取字段信息
+	 */
+	@GetMapping("/model-prototype")
+	@ApiOperationSupport(order = 10)
+	@ApiOperation(value = "物理表字段信息", notes = "传入modelId与datasourceId")
+	public R modelPrototype(Long modelId, Long datasourceId) {
+		List<ModelPrototype> modelPrototypeList = modelPrototypeService.list(Wrappers.<ModelPrototype>query().lambda().eq(ModelPrototype::getModelId, modelId));
+		if (modelPrototypeList.size() > 0) {
+			return R.data(modelPrototypeList);
+		}
+		Model model = modelService.getById(modelId);
+		String tableName = model.getModelTable();
+		TableInfo tableInfo = getTableInfo(tableName, datasourceId);
+		if (tableInfo != null) {
+			return R.data(tableInfo.getFields());
+		} else {
+			return R.fail("未获得相关表信息");
+		}
+	}
+
+	/**
+	 * 获取表信息
+	 *
+	 * @param tableName    表名
+	 * @param datasourceId 数据源主键
+	 */
+	private TableInfo getTableInfo(String tableName, Long datasourceId) {
+		Datasource datasource = datasourceService.getById(datasourceId);
+		ConfigBuilder config = getConfigBuilder(datasource, tableName);
+		List<TableInfo> tableInfoList = config.getTableInfoList();
+		TableInfo tableInfo = null;
+		Iterator<TableInfo> iterator = tableInfoList.stream().filter(table -> table.getName().equals(tableName)).collect(Collectors.toList()).iterator();
+		if (iterator.hasNext()) {
+			tableInfo = iterator.next();
+			if (tableName.contains(StringPool.UNDERSCORE)) {
+				tableInfo.setEntityName(tableInfo.getEntityName().replace(StringUtil.firstCharToUpper(tableName.split(StringPool.UNDERSCORE)[0]), StringPool.EMPTY));
+			} else {
+				tableInfo.setEntityName(StringUtil.firstCharToUpper(tableName));
+			}
+		}
+		return tableInfo;
+	}
+
+	/**
+	 * 获取表配置信息
+	 *
+	 * @param datasource 数据源信息
+	 */
+	private ConfigBuilder getConfigBuilder(Datasource datasource) {
+		return getConfigBuilder(datasource, null);
+	}
+
+	/**
+	 * 获取表配置信息
+	 *
+	 * @param datasource 数据源信息
+	 * @param tableName  表名
+	 */
+	private ConfigBuilder getConfigBuilder(Datasource datasource, String tableName) {
+		StrategyConfig.Builder builder = new StrategyConfig.Builder();
+		if (StringUtil.isNotBlank(tableName)) {
+			builder.addInclude(tableName);
+		}
+		StrategyConfig strategyConfig = builder.entityBuilder()
+			.naming(NamingStrategy.underline_to_camel)
+			.columnNaming(NamingStrategy.underline_to_camel).build();
+		DataSourceConfig datasourceConfig = new DataSourceConfig.Builder(
+			datasource.getUrl(), datasource.getUsername(), datasource.getPassword()
+		).build();
+		return new ConfigBuilder(null, datasourceConfig, strategyConfig, null, null, null);
+	}
+
+
+}

+ 137 - 0
admin/src/main/java/org/springblade/modules/develop/controller/ModelPrototypeController.java

@@ -0,0 +1,137 @@
+/*
+ *      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.modules.develop.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.launch.constant.AppConstant;
+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.springblade.core.tool.utils.StringPool;
+import org.springblade.modules.develop.entity.ModelPrototype;
+import org.springblade.modules.develop.service.IModelPrototypeService;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 数据原型表 控制器
+ *
+ * @author Chill
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_DEVELOP_NAME + "/model-prototype")
+@Api(value = "数据原型表", tags = "数据原型表接口")
+public class ModelPrototypeController extends BladeController {
+
+	private final IModelPrototypeService modelPrototypeService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入modelPrototype")
+	public R<ModelPrototype> detail(ModelPrototype modelPrototype) {
+		ModelPrototype detail = modelPrototypeService.getOne(Condition.getQueryWrapper(modelPrototype));
+		return R.data(detail);
+	}
+
+	/**
+	 * 分页 数据原型表
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入modelPrototype")
+	public R<IPage<ModelPrototype>> list(ModelPrototype modelPrototype, Query query) {
+		IPage<ModelPrototype> pages = modelPrototypeService.page(Condition.getPage(query), Condition.getQueryWrapper(modelPrototype));
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增 数据原型表
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "新增", notes = "传入modelPrototype")
+	public R save(@Valid @RequestBody ModelPrototype modelPrototype) {
+		return R.status(modelPrototypeService.save(modelPrototype));
+	}
+
+	/**
+	 * 修改 数据原型表
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "修改", notes = "传入modelPrototype")
+	public R update(@Valid @RequestBody ModelPrototype modelPrototype) {
+		return R.status(modelPrototypeService.updateById(modelPrototype));
+	}
+
+	/**
+	 * 新增或修改 数据原型表
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "新增或修改", notes = "传入modelPrototype")
+	public R submit(@Valid @RequestBody ModelPrototype modelPrototype) {
+		return R.status(modelPrototypeService.saveOrUpdate(modelPrototype));
+	}
+
+	/**
+	 * 批量新增或修改 数据原型表
+	 */
+	@PostMapping("/submit-list")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "批量新增或修改", notes = "传入modelPrototype集合")
+	public R submitList(@Valid @RequestBody List<ModelPrototype> modelPrototypes) {
+		return R.status(modelPrototypeService.submitList(modelPrototypes));
+	}
+
+	/**
+	 * 删除 数据原型表
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 8)
+	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		return R.status(modelPrototypeService.deleteLogic(Func.toLongList(ids)));
+	}
+
+	/**
+	 * 数据原型列表
+	 */
+	@GetMapping("/select")
+	@ApiOperationSupport(order = 9)
+	@ApiOperation(value = "数据原型列表", notes = "数据原型列表")
+	public R<List<ModelPrototype>> select(@ApiParam(value = "数据模型Id", required = true) @RequestParam Long modelId) {
+		List<ModelPrototype> list = modelPrototypeService.list(Wrappers.<ModelPrototype>query().lambda().eq(ModelPrototype::getModelId, modelId));
+		list.forEach(prototype -> prototype.setComment(prototype.getJdbcName() + StringPool.COLON + StringPool.SPACE + prototype.getComment()));
+		return R.data(list);
+	}
+
+}

+ 42 - 0
admin/src/main/java/org/springblade/modules/develop/dto/ModelDTO.java

@@ -0,0 +1,42 @@
+/*
+ *      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.modules.develop.dto;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.modules.develop.entity.Model;
+import org.springblade.modules.develop.entity.ModelPrototype;
+
+import java.util.List;
+
+/**
+ * 代码模型DTO
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ModelDTO extends Model {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 代码建模原型
+	 */
+	private List<ModelPrototype> prototypes;
+
+}

+ 180 - 0
admin/src/main/java/org/springblade/modules/develop/entity/Code.java

@@ -0,0 +1,180 @@
+/*
+ *      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.modules.develop.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_code")
+@ApiModel(value = "Code对象", description = "Code对象")
+public class Code implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 主键
+	 */
+	@JsonSerialize(using = ToStringSerializer.class)
+	@ApiModelProperty(value = "主键")
+	@TableId(value = "id", type = IdType.ASSIGN_ID)
+	private Long id;
+
+	/**
+	 * 数据模型主键
+	 */
+	@JsonSerialize(using = ToStringSerializer.class)
+	@ApiModelProperty(value = "数据模型主键")
+	private Long modelId;
+
+	/**
+	 * 模块名称
+	 */
+	@ApiModelProperty(value = "服务名称")
+	private String serviceName;
+
+	/**
+	 * 模块名称
+	 */
+	@ApiModelProperty(value = "模块名称")
+	private String codeName;
+
+	/**
+	 * 表名
+	 */
+	@ApiModelProperty(value = "表名")
+	private String tableName;
+
+	/**
+	 * 实体名
+	 */
+	@ApiModelProperty(value = "表前缀")
+	private String tablePrefix;
+
+	/**
+	 * 主键名
+	 */
+	@ApiModelProperty(value = "主键名")
+	private String pkName;
+
+	/**
+	 * 后端包名
+	 */
+	@ApiModelProperty(value = "后端包名")
+	private String packageName;
+
+	/**
+	 * 模版类型
+	 */
+	@ApiModelProperty(value = "模版类型")
+	private String templateType;
+
+	/**
+	 * 作者信息
+	 */
+	@ApiModelProperty(value = "作者信息")
+	private String author;
+
+	/**
+	 * 子表模型主键
+	 */
+	@ApiModelProperty(value = "子表模型主键")
+	private String subModelId;
+
+	/**
+	 * 子表绑定外键
+	 */
+	@ApiModelProperty(value = "子表绑定外键")
+	private String subFkId;
+
+	/**
+	 * 树主键字段
+	 */
+	@ApiModelProperty(value = "树主键字段")
+	private String treeId;
+
+	/**
+	 * 树父主键字段
+	 */
+	@ApiModelProperty(value = "树父主键字段")
+	private String treePid;
+
+	/**
+	 * 树名称字段
+	 */
+	@ApiModelProperty(value = "树名称字段")
+	private String treeName;
+
+	/**
+	 * 基础业务模式
+	 */
+	@ApiModelProperty(value = "基础业务模式")
+	private Integer baseMode;
+
+	/**
+	 * 包装器模式
+	 */
+	@ApiModelProperty(value = "包装器模式")
+	private Integer wrapMode;
+
+	/**
+	 * 远程调用模式
+	 */
+	@ApiModelProperty(value = "远程调用模式")
+	private Integer feignMode;
+
+	/**
+	 * 代码风格
+	 */
+	@ApiModelProperty(value = "代码风格")
+	private String codeStyle;
+
+	/**
+	 * 后端路径
+	 */
+	@ApiModelProperty(value = "后端路径")
+	private String apiPath;
+
+	/**
+	 * 前端路径
+	 */
+	@ApiModelProperty(value = "前端路径")
+	private String webPath;
+
+	/**
+	 * 是否已删除
+	 */
+	@TableLogic
+	@ApiModelProperty(value = "是否已删除")
+	private Integer isDeleted;
+
+
+}

+ 71 - 0
admin/src/main/java/org/springblade/modules/develop/entity/Datasource.java

@@ -0,0 +1,71 @@
+/*
+ *      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.modules.develop.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+
+/**
+ * 数据源配置表实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_datasource")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Datasource对象", description = "数据源配置表")
+public class Datasource extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 名称
+	 */
+	@ApiModelProperty(value = "名称")
+	private String name;
+	/**
+	 * 驱动类
+	 */
+	@ApiModelProperty(value = "驱动类")
+	private String driverClass;
+	/**
+	 * 连接地址
+	 */
+	@ApiModelProperty(value = "连接地址")
+	private String url;
+	/**
+	 * 用户名
+	 */
+	@ApiModelProperty(value = "用户名")
+	private String username;
+	/**
+	 * 密码
+	 */
+	@ApiModelProperty(value = "密码")
+	private String password;
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+
+}

+ 74 - 0
admin/src/main/java/org/springblade/modules/develop/entity/Model.java

@@ -0,0 +1,74 @@
+/*
+ *      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.modules.develop.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+
+/**
+ * 数据模型表实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_model")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Model对象", description = "数据模型表")
+public class Model extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 数据源主键
+	 */
+	@ApiModelProperty(value = "数据源主键")
+	@JsonSerialize(using = ToStringSerializer.class)
+	private Long datasourceId;
+	/**
+	 * 模型名称
+	 */
+	@ApiModelProperty(value = "模型名称")
+	private String modelName;
+	/**
+	 * 模型编号
+	 */
+	@ApiModelProperty(value = "模型编号")
+	private String modelCode;
+	/**
+	 * 物理表名
+	 */
+	@ApiModelProperty(value = "物理表名")
+	private String modelTable;
+	/**
+	 * 模型类名
+	 */
+	@ApiModelProperty(value = "模型类名")
+	private String modelClass;
+	/**
+	 * 模型备注
+	 */
+	@ApiModelProperty(value = "模型备注")
+	private String modelRemark;
+
+
+}

+ 119 - 0
admin/src/main/java/org/springblade/modules/develop/entity/ModelPrototype.java

@@ -0,0 +1,119 @@
+/*
+ *      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.modules.develop.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+
+/**
+ * 数据原型表实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_model_prototype")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "ModelPrototype对象", description = "数据原型表")
+public class ModelPrototype extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 模型主键
+	 */
+	@ApiModelProperty(value = "模型主键")
+	@JsonSerialize(using = ToStringSerializer.class)
+	private Long modelId;
+	/**
+	 * 物理列名
+	 */
+	@ApiModelProperty(value = "物理列名")
+	private String jdbcName;
+	/**
+	 * 物理类型
+	 */
+	@ApiModelProperty(value = "物理类型")
+	private String jdbcType;
+	/**
+	 * 实体列名
+	 */
+	@ApiModelProperty(value = "实体列名")
+	private String propertyName;
+	/**
+	 * 实体类型
+	 */
+	@ApiModelProperty(value = "实体类型")
+	private String propertyType;
+	/**
+	 * 实体类型引用
+	 */
+	@ApiModelProperty(value = "实体类型引用")
+	private String propertyEntity;
+	/**
+	 * 注释说明
+	 */
+	@ApiModelProperty(value = "注释说明")
+	private String comment;
+	/**
+	 * 列表显示
+	 */
+	@ApiModelProperty(value = "列表显示")
+	private Integer isList;
+	/**
+	 * 表单显示
+	 */
+	@ApiModelProperty(value = "表单显示")
+	private Integer isForm;
+	/**
+	 * 独占一行
+	 */
+	@ApiModelProperty(value = "独占一行")
+	private Integer isRow;
+	/**
+	 * 组件类型
+	 */
+	@ApiModelProperty(value = "组件类型")
+	private String componentType;
+	/**
+	 * 字典编码
+	 */
+	@ApiModelProperty(value = "字典编码")
+	private String dictCode;
+	/**
+	 * 是否必填
+	 */
+	@ApiModelProperty(value = "是否必填")
+	private Integer isRequired;
+	/**
+	 * 查询配置
+	 */
+	@ApiModelProperty(value = "查询配置")
+	private Integer isQuery;
+	/**
+	 * 查询类型
+	 */
+	@ApiModelProperty(value = "查询类型")
+	private String queryType;
+
+
+}

+ 29 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/CodeMapper.java

@@ -0,0 +1,29 @@
+/*
+ *      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.modules.develop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.modules.develop.entity.Code;
+
+/**
+ * Mapper 接口
+ *
+ * @author Chill
+ */
+public interface CodeMapper extends BaseMapper<Code> {
+
+}

+ 22 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/CodeMapper.xml

@@ -0,0 +1,22 @@
+<?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="org.springblade.modules.develop.mapper.CodeMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="codeResultMap" type="org.springblade.modules.develop.entity.Code">
+        <id column="id" property="id"/>
+        <result column="datasource_id" property="datasourceId"/>
+        <result column="service_name" property="serviceName"/>
+        <result column="code_name" property="codeName"/>
+        <result column="table_name" property="tableName"/>
+        <result column="pk_name" property="pkName"/>
+        <result column="base_mode" property="baseMode"/>
+        <result column="wrap_mode" property="wrapMode"/>
+        <result column="table_prefix" property="tablePrefix"/>
+        <result column="package_name" property="packageName"/>
+        <result column="api_path" property="apiPath"/>
+        <result column="web_path" property="webPath"/>
+        <result column="is_deleted" property="isDeleted"/>
+    </resultMap>
+
+</mapper>

+ 29 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/DatasourceMapper.java

@@ -0,0 +1,29 @@
+/*
+ *      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.modules.develop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.modules.develop.entity.Datasource;
+
+/**
+ * 数据源配置表 Mapper 接口
+ *
+ * @author Chill
+ */
+public interface DatasourceMapper extends BaseMapper<Datasource> {
+
+}

+ 22 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/DatasourceMapper.xml

@@ -0,0 +1,22 @@
+<?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="org.springblade.modules.develop.mapper.DatasourceMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="datasourceResultMap" type="org.springblade.modules.develop.entity.Datasource">
+        <result column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_dept" property="createDept"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="driver_class" property="driverClass"/>
+        <result column="url" property="url"/>
+        <result column="username" property="username"/>
+        <result column="password" property="password"/>
+        <result column="remark" property="remark"/>
+    </resultMap>
+
+</mapper>

+ 29 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/ModelMapper.java

@@ -0,0 +1,29 @@
+/*
+ *      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.modules.develop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.modules.develop.entity.Model;
+
+/**
+ * 数据模型表 Mapper 接口
+ *
+ * @author Chill
+ */
+public interface ModelMapper extends BaseMapper<Model> {
+
+}

+ 27 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/ModelMapper.xml

@@ -0,0 +1,27 @@
+<?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="org.springblade.modules.develop.mapper.ModelMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="modelResultMap" type="org.springblade.modules.develop.entity.Model">
+        <id column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="datasource_id" property="datasourceId"/>
+        <result column="model_name" property="modelName"/>
+        <result column="model_code" property="modelCode"/>
+        <result column="model_table" property="modelTable"/>
+        <result column="model_class" property="modelClass"/>
+        <result column="model_remark" property="modelRemark"/>
+    </resultMap>
+
+
+    <select id="selectModelPage" resultMap="modelResultMap">
+        select * from blade_model where is_deleted = 0
+    </select>
+
+</mapper>

+ 29 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/ModelPrototypeMapper.java

@@ -0,0 +1,29 @@
+/*
+ *      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.modules.develop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.modules.develop.entity.ModelPrototype;
+
+/**
+ * 数据原型表 Mapper 接口
+ *
+ * @author Chill
+ */
+public interface ModelPrototypeMapper extends BaseMapper<ModelPrototype> {
+
+}

+ 35 - 0
admin/src/main/java/org/springblade/modules/develop/mapper/ModelPrototypeMapper.xml

@@ -0,0 +1,35 @@
+<?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="org.springblade.modules.develop.mapper.ModelPrototypeMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="modelPrototypeResultMap" type="org.springblade.modules.develop.entity.ModelPrototype">
+        <id column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="jdbc_name" property="jdbcName"/>
+        <result column="jdbc_type" property="jdbcType"/>
+        <result column="comment" property="comment"/>
+        <result column="property_type" property="propertyType"/>
+        <result column="property_entity" property="propertyEntity"/>
+        <result column="property_name" property="propertyName"/>
+        <result column="is_form" property="isForm"/>
+        <result column="is_row" property="isRow"/>
+        <result column="component_type" property="componentType"/>
+        <result column="dict_code" property="dictCode"/>
+        <result column="is_required" property="isRequired"/>
+        <result column="is_list" property="isList"/>
+        <result column="is_query" property="isQuery"/>
+        <result column="query_type" property="queryType"/>
+    </resultMap>
+
+
+    <select id="selectModelPrototypePage" resultMap="modelPrototypeResultMap">
+        select * from blade_model_prototype where is_deleted = 0
+    </select>
+
+</mapper>

+ 38 - 0
admin/src/main/java/org/springblade/modules/develop/service/ICodeService.java

@@ -0,0 +1,38 @@
+/*
+ *      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.modules.develop.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springblade.modules.develop.entity.Code;
+
+/**
+ * 服务类
+ *
+ * @author Chill
+ */
+public interface ICodeService extends IService<Code> {
+
+	/**
+	 * 提交
+	 *
+	 * @param code
+	 * @return
+	 */
+	boolean submit(Code code);
+
+}

+ 29 - 0
admin/src/main/java/org/springblade/modules/develop/service/IDatasourceService.java

@@ -0,0 +1,29 @@
+/*
+ *      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.modules.develop.service;
+
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.modules.develop.entity.Datasource;
+
+/**
+ * 数据源配置表 服务类
+ *
+ * @author Chill
+ */
+public interface IDatasourceService extends BaseService<Datasource> {
+
+}

+ 47 - 0
admin/src/main/java/org/springblade/modules/develop/service/IModelPrototypeService.java

@@ -0,0 +1,47 @@
+/*
+ *      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.modules.develop.service;
+
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.modules.develop.entity.ModelPrototype;
+
+import java.util.List;
+
+/**
+ * 数据原型表 服务类
+ *
+ * @author Chill
+ */
+public interface IModelPrototypeService extends BaseService<ModelPrototype> {
+
+	/**
+	 * 批量提交
+	 *
+	 * @param modelPrototypes 原型集合
+	 * @return boolean
+	 */
+	boolean submitList(List<ModelPrototype> modelPrototypes);
+
+	/**
+	 * 原型列表
+	 *
+	 * @param modelId 模型ID
+	 * @return List<ModelPrototype>
+	 */
+	List<ModelPrototype> prototypeList(Long modelId);
+
+}

+ 39 - 0
admin/src/main/java/org/springblade/modules/develop/service/IModelService.java

@@ -0,0 +1,39 @@
+/*
+ *      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.modules.develop.service;
+
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.modules.develop.entity.Model;
+
+import java.util.List;
+
+/**
+ * 数据模型表 服务类
+ *
+ * @author Chill
+ */
+public interface IModelService extends BaseService<Model> {
+
+	/**
+	 * 删除模型
+	 *
+	 * @param ids 主键集合
+	 * @return boolean
+	 */
+	boolean delete(List<Long> ids);
+
+}

+ 40 - 0
admin/src/main/java/org/springblade/modules/develop/service/impl/CodeServiceImpl.java

@@ -0,0 +1,40 @@
+/*
+ *      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.modules.develop.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.modules.develop.entity.Code;
+import org.springblade.modules.develop.mapper.CodeMapper;
+import org.springblade.modules.develop.service.ICodeService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 服务实现类
+ *
+ * @author Chill
+ */
+@Service
+public class CodeServiceImpl extends ServiceImpl<CodeMapper, Code> implements ICodeService {
+
+	@Override
+	public boolean submit(Code code) {
+		code.setIsDeleted(BladeConstant.DB_NOT_DELETED);
+		return saveOrUpdate(code);
+	}
+
+}

+ 33 - 0
admin/src/main/java/org/springblade/modules/develop/service/impl/DatasourceServiceImpl.java

@@ -0,0 +1,33 @@
+/*
+ *      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.modules.develop.service.impl;
+
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.modules.develop.entity.Datasource;
+import org.springblade.modules.develop.mapper.DatasourceMapper;
+import org.springblade.modules.develop.service.IDatasourceService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 数据源配置表 服务实现类
+ *
+ * @author Chill
+ */
+@Service
+public class DatasourceServiceImpl extends BaseServiceImpl<DatasourceMapper, Datasource> implements IDatasourceService {
+
+}

+ 55 - 0
admin/src/main/java/org/springblade/modules/develop/service/impl/ModelPrototypeServiceImpl.java

@@ -0,0 +1,55 @@
+/*
+ *      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.modules.develop.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.modules.develop.entity.ModelPrototype;
+import org.springblade.modules.develop.mapper.ModelPrototypeMapper;
+import org.springblade.modules.develop.service.IModelPrototypeService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 数据原型表 服务实现类
+ *
+ * @author Chill
+ */
+@Service
+public class ModelPrototypeServiceImpl extends BaseServiceImpl<ModelPrototypeMapper, ModelPrototype> implements IModelPrototypeService {
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public boolean submitList(List<ModelPrototype> modelPrototypes) {
+		modelPrototypes.forEach(modelPrototype -> {
+			if (modelPrototype.getId() == null) {
+				this.save(modelPrototype);
+			} else {
+				this.updateById(modelPrototype);
+			}
+		});
+		return true;
+	}
+
+	@Override
+	public List<ModelPrototype> prototypeList(Long modelId) {
+		return this.list(Wrappers.<ModelPrototype>lambdaQuery().eq(ModelPrototype::getModelId, modelId));
+	}
+
+}

+ 67 - 0
admin/src/main/java/org/springblade/modules/develop/service/impl/ModelServiceImpl.java

@@ -0,0 +1,67 @@
+/*
+ *      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.modules.develop.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.modules.develop.entity.Code;
+import org.springblade.modules.develop.entity.Model;
+import org.springblade.modules.develop.entity.ModelPrototype;
+import org.springblade.modules.develop.mapper.ModelMapper;
+import org.springblade.modules.develop.service.ICodeService;
+import org.springblade.modules.develop.service.IModelPrototypeService;
+import org.springblade.modules.develop.service.IModelService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 数据模型表 服务实现类
+ *
+ * @author Chill
+ */
+@Service
+@RequiredArgsConstructor
+public class ModelServiceImpl extends BaseServiceImpl<ModelMapper, Model> implements IModelService {
+
+	private final IModelPrototypeService modelPrototypeService;
+	private final ICodeService codeService;
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public boolean delete(List<Long> ids) {
+		boolean modelTemp = this.deleteLogic(ids);
+		if (modelTemp) {
+			if (modelPrototypeService.count(Wrappers.<ModelPrototype>lambdaQuery().in(ModelPrototype::getModelId, ids)) > 0) {
+				boolean prototypeTemp = modelPrototypeService.remove(Wrappers.<ModelPrototype>lambdaQuery().in(ModelPrototype::getModelId, ids));
+				if (!prototypeTemp) {
+					throw new ServiceException("删除数据模型成功,关联数据原型删除失败");
+				}
+			}
+			if (codeService.count(Wrappers.<Code>lambdaQuery().in(Code::getModelId, ids)) > 0) {
+				boolean codeTemp = codeService.remove(Wrappers.<Code>lambdaQuery().in(Code::getModelId, ids));
+				if (!codeTemp) {
+					throw new ServiceException("删除数据模型成功,关联代码生成配置删除失败");
+				}
+			}
+		}
+		return true;
+	}
+}

+ 65 - 0
admin/src/main/java/org/springblade/modules/resource/builder/oss/AliOssBuilder.java

@@ -0,0 +1,65 @@
+/*
+ *      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.modules.resource.builder.oss;
+
+import com.aliyun.oss.ClientConfiguration;
+import com.aliyun.oss.OSSClient;
+import com.aliyun.oss.common.auth.CredentialsProvider;
+import com.aliyun.oss.common.auth.DefaultCredentialProvider;
+import lombok.SneakyThrows;
+import org.springblade.core.oss.OssTemplate;
+import org.springblade.core.oss.AliossTemplate;
+import org.springblade.core.oss.props.OssProperties;
+import org.springblade.core.oss.rule.OssRule;
+import org.springblade.modules.resource.entity.Oss;
+
+/**
+ * 阿里云存储构建类
+ *
+ * @author Chill
+ */
+public class AliOssBuilder {
+
+	@SneakyThrows
+	public static OssTemplate template(Oss oss, OssRule ossRule) {
+		// 创建配置类
+		OssProperties ossProperties = new OssProperties();
+		ossProperties.setEndpoint(oss.getEndpoint());
+		ossProperties.setAccessKey(oss.getAccessKey());
+		ossProperties.setSecretKey(oss.getSecretKey());
+		ossProperties.setBucketName(oss.getBucketName());
+		// 创建ClientConfiguration。ClientConfiguration是OSSClient的配置类,可配置代理、连接超时、最大连接数等参数。
+		ClientConfiguration conf = new ClientConfiguration();
+		// 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
+		conf.setMaxConnections(1024);
+		// 设置Socket层传输数据的超时时间,默认为50000毫秒。
+		conf.setSocketTimeout(50000);
+		// 设置建立连接的超时时间,默认为50000毫秒。
+		conf.setConnectionTimeout(50000);
+		// 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
+		conf.setConnectionRequestTimeout(1000);
+		// 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
+		conf.setIdleConnectionTime(60000);
+		// 设置失败请求重试次数,默认为3次。
+		conf.setMaxErrorRetry(5);
+		CredentialsProvider credentialsProvider = new DefaultCredentialProvider(ossProperties.getAccessKey(), ossProperties.getSecretKey());
+		// 创建客户端
+		OSSClient ossClient = new OSSClient(ossProperties.getEndpoint(), credentialsProvider, conf);
+		return new AliossTemplate(ossClient, ossProperties, ossRule);
+	}
+
+}

+ 50 - 0
admin/src/main/java/org/springblade/modules/resource/builder/oss/MinioOssBuilder.java

@@ -0,0 +1,50 @@
+/*
+ *      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.modules.resource.builder.oss;
+
+import io.minio.MinioClient;
+import lombok.SneakyThrows;
+import org.springblade.core.oss.OssTemplate;
+import org.springblade.core.oss.MinioTemplate;
+import org.springblade.core.oss.props.OssProperties;
+import org.springblade.core.oss.rule.OssRule;
+import org.springblade.modules.resource.entity.Oss;
+
+/**
+ * Minio云存储构建类
+ *
+ * @author Chill
+ */
+public class MinioOssBuilder {
+
+	@SneakyThrows
+	public static OssTemplate template(Oss oss, OssRule ossRule) {
+		// 创建配置类
+		OssProperties ossProperties = new OssProperties();
+		ossProperties.setEndpoint(oss.getEndpoint());
+		ossProperties.setAccessKey(oss.getAccessKey());
+		ossProperties.setSecretKey(oss.getSecretKey());
+		ossProperties.setBucketName(oss.getBucketName());
+		// 创建客户端
+		MinioClient minioClient = MinioClient.builder()
+			.endpoint(oss.getEndpoint())
+			.credentials(oss.getAccessKey(), oss.getSecretKey())
+			.build();
+		return new MinioTemplate(minioClient, ossRule, ossProperties);
+	}
+
+}

+ 159 - 0
admin/src/main/java/org/springblade/modules/resource/builder/oss/OssBuilder.java

@@ -0,0 +1,159 @@
+/*
+ *      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.modules.resource.builder.oss;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.oss.OssTemplate;
+import org.springblade.core.oss.enums.OssEnum;
+import org.springblade.core.oss.enums.OssStatusEnum;
+import org.springblade.core.oss.props.OssProperties;
+import org.springblade.core.oss.rule.BladeOssRule;
+import org.springblade.core.oss.rule.OssRule;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.modules.resource.entity.Oss;
+import org.springblade.modules.resource.service.IOssService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.springblade.core.cache.constant.CacheConstant.RESOURCE_CACHE;
+
+/**
+ * Oss云存储统一构建类
+ *
+ * @author Chill
+ */
+public class OssBuilder {
+
+	public static final String OSS_CODE = "oss:code:";
+	public static final String OSS_PARAM_KEY = "code";
+
+	private final OssProperties ossProperties;
+	private final IOssService ossService;
+
+	public OssBuilder(OssProperties ossProperties, IOssService ossService) {
+		this.ossProperties = ossProperties;
+		this.ossService = ossService;
+	}
+
+	/**
+	 * OssTemplate配置缓存池
+	 */
+	private final Map<String, OssTemplate> templatePool = new ConcurrentHashMap<>();
+
+	/**
+	 * oss配置缓存池
+	 */
+	private final Map<String, Oss> ossPool = new ConcurrentHashMap<>();
+
+	/**
+	 * 获取template
+	 *
+	 * @return OssTemplate
+	 */
+	public OssTemplate template() {
+		return template(StringPool.EMPTY);
+	}
+
+	/**
+	 * 获取template
+	 *
+	 * @param code 资源编号
+	 * @return OssTemplate
+	 */
+	public OssTemplate template(String code) {
+		String tenantId = AuthUtil.getTenantId();
+		Oss oss = getOss(tenantId, code);
+		Oss ossCached = ossPool.get(tenantId);
+		OssTemplate template = templatePool.get(tenantId);
+		// 若为空或者不一致,则重新加载
+		if (Func.hasEmpty(template, ossCached) || !oss.getEndpoint().equals(ossCached.getEndpoint()) || !oss.getAccessKey().equals(ossCached.getAccessKey())) {
+			synchronized (OssBuilder.class) {
+				template = templatePool.get(tenantId);
+				if (Func.hasEmpty(template, ossCached) || !oss.getEndpoint().equals(ossCached.getEndpoint()) || !oss.getAccessKey().equals(ossCached.getAccessKey())) {
+					OssRule ossRule;
+					// 若采用默认设置则开启多租户模式, 若是用户自定义oss则不开启
+					if (oss.getEndpoint().equals(ossProperties.getEndpoint()) && oss.getAccessKey().equals(ossProperties.getAccessKey()) && ossProperties.getTenantMode()) {
+						ossRule = new BladeOssRule(Boolean.TRUE);
+					} else {
+						ossRule = new BladeOssRule(Boolean.FALSE);
+					}
+					if (oss.getCategory() == OssEnum.MINIO.getCategory()) {
+						template = MinioOssBuilder.template(oss, ossRule);
+					} else if (oss.getCategory() == OssEnum.QINIU.getCategory()) {
+						template = QiniuOssBuilder.template(oss, ossRule);
+					} else if (oss.getCategory() == OssEnum.ALI.getCategory()) {
+						template = AliOssBuilder.template(oss, ossRule);
+					} else if (oss.getCategory() == OssEnum.TENCENT.getCategory()) {
+						template = TencentOssBuilder.template(oss, ossRule);
+					}
+					templatePool.put(tenantId, template);
+					ossPool.put(tenantId, oss);
+				}
+			}
+		}
+		return template;
+	}
+
+	/**
+	 * 获取对象存储实体
+	 *
+	 * @param tenantId 租户ID
+	 * @return Oss
+	 */
+	public Oss getOss(String tenantId, String code) {
+		String key = tenantId;
+		LambdaQueryWrapper<Oss> lqw = Wrappers.<Oss>query().lambda().eq(Oss::getTenantId, tenantId);
+		// 获取传参的资源编号并查询,若有则返回,若没有则调启用的配置
+		String ossCode = StringUtil.isBlank(code) ? WebUtil.getParameter(OSS_PARAM_KEY) : code;
+		if (StringUtil.isNotBlank(ossCode)) {
+			key = key.concat(StringPool.DASH).concat(ossCode);
+			lqw.eq(Oss::getOssCode, ossCode);
+		} else {
+			lqw.eq(Oss::getStatus, OssStatusEnum.ENABLE.getNum());
+		}
+		Oss oss = CacheUtil.get(RESOURCE_CACHE, OSS_CODE, key, () -> {
+			Oss o = ossService.getOne(lqw);
+			// 若为空则调用默认配置
+			if (o == null || o.getId() == null) {
+				Oss defaultOss = new Oss();
+				defaultOss.setId(0L);
+				defaultOss.setCategory(OssEnum.of(ossProperties.getName()).getCategory());
+				defaultOss.setEndpoint(ossProperties.getEndpoint());
+				defaultOss.setBucketName(ossProperties.getBucketName());
+				defaultOss.setAccessKey(ossProperties.getAccessKey());
+				defaultOss.setSecretKey(ossProperties.getSecretKey());
+				return defaultOss;
+			} else {
+				return o;
+			}
+		});
+		if (oss == null || oss.getId() == null) {
+			throw new ServiceException("未获取到对应的对象存储配置");
+		} else {
+			return oss;
+		}
+	}
+
+}

+ 52 - 0
admin/src/main/java/org/springblade/modules/resource/builder/oss/QiniuOssBuilder.java

@@ -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 org.springblade.modules.resource.builder.oss;
+
+import com.qiniu.storage.BucketManager;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.Region;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.util.Auth;
+import lombok.SneakyThrows;
+import org.springblade.core.oss.OssTemplate;
+import org.springblade.core.oss.QiniuTemplate;
+import org.springblade.core.oss.props.OssProperties;
+import org.springblade.core.oss.rule.OssRule;
+import org.springblade.modules.resource.entity.Oss;
+
+/**
+ * 七牛云存储构建类
+ *
+ * @author Chill
+ */
+public class QiniuOssBuilder {
+
+	@SneakyThrows
+	public static OssTemplate template(Oss oss, OssRule ossRule) {
+		OssProperties ossProperties = new OssProperties();
+		ossProperties.setEndpoint(oss.getEndpoint());
+		ossProperties.setAccessKey(oss.getAccessKey());
+		ossProperties.setSecretKey(oss.getSecretKey());
+		ossProperties.setBucketName(oss.getBucketName());
+		Configuration cfg = new Configuration(Region.autoRegion());
+		Auth auth = Auth.create(oss.getAccessKey(), oss.getSecretKey());
+		UploadManager uploadManager = new UploadManager(cfg);
+		BucketManager bucketManager = new BucketManager(auth, cfg);
+		return new QiniuTemplate(auth, uploadManager, bucketManager, ossProperties, ossRule);
+	}
+
+}

+ 66 - 0
admin/src/main/java/org/springblade/modules/resource/builder/oss/TencentOssBuilder.java

@@ -0,0 +1,66 @@
+/*
+ *      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.modules.resource.builder.oss;
+
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.region.Region;
+import lombok.SneakyThrows;
+import org.springblade.core.oss.OssTemplate;
+import org.springblade.core.oss.props.OssProperties;
+import org.springblade.core.oss.rule.OssRule;
+import org.springblade.core.oss.TencentCosTemplate;
+import org.springblade.modules.resource.entity.Oss;
+
+/**
+ * 腾讯云存储构建类
+ *
+ * @author Chill
+ */
+public class TencentOssBuilder {
+
+	@SneakyThrows
+	public static OssTemplate template(Oss oss, OssRule ossRule) {
+		// 创建配置类
+		OssProperties ossProperties = new OssProperties();
+		ossProperties.setEndpoint(oss.getEndpoint());
+		ossProperties.setAccessKey(oss.getAccessKey());
+		ossProperties.setSecretKey(oss.getSecretKey());
+		ossProperties.setBucketName(oss.getBucketName());
+		ossProperties.setAppId(oss.getAppId());
+		ossProperties.setRegion(oss.getRegion());
+		// 初始化用户身份信息(secretId, secretKey)
+		COSCredentials credentials = new BasicCOSCredentials(ossProperties.getAccessKey(), ossProperties.getSecretKey());
+		// 设置 bucket 的区域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
+		Region region = new Region(ossProperties.getRegion());
+		// clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
+		ClientConfig clientConfig = new ClientConfig(region);
+		// 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
+		clientConfig.setMaxConnectionsCount(1024);
+		// 设置Socket层传输数据的超时时间,默认为50000毫秒。
+		clientConfig.setSocketTimeout(50000);
+		// 设置建立连接的超时时间,默认为50000毫秒。
+		clientConfig.setConnectionTimeout(50000);
+		// 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
+		clientConfig.setConnectionRequestTimeout(1000);
+		COSClient cosClient = new COSClient(credentials, clientConfig);
+		return new TencentCosTemplate(cosClient, ossProperties, ossRule);
+	}
+
+}

+ 50 - 0
admin/src/main/java/org/springblade/modules/resource/builder/sms/AliSmsBuilder.java

@@ -0,0 +1,50 @@
+/*
+ *      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.modules.resource.builder.sms;
+
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import lombok.SneakyThrows;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.sms.SmsTemplate;
+import org.springblade.core.sms.AliSmsTemplate;
+import org.springblade.core.sms.props.SmsProperties;
+import org.springblade.modules.resource.entity.Sms;
+
+/**
+ * 阿里云短信构建类
+ *
+ * @author Chill
+ */
+public class AliSmsBuilder {
+
+	@SneakyThrows
+	public static SmsTemplate template(Sms sms, BladeRedis bladeRedis) {
+		SmsProperties smsProperties = new SmsProperties();
+		smsProperties.setTemplateId(sms.getTemplateId());
+		smsProperties.setAccessKey(sms.getAccessKey());
+		smsProperties.setSecretKey(sms.getSecretKey());
+		smsProperties.setRegionId(sms.getRegionId());
+		smsProperties.setSignName(sms.getSignName());
+		IClientProfile profile = DefaultProfile.getProfile(smsProperties.getRegionId(), smsProperties.getAccessKey(), smsProperties.getSecretKey());
+		IAcsClient acsClient = new DefaultAcsClient(profile);
+		return new AliSmsTemplate(smsProperties, acsClient, bladeRedis);
+	}
+
+}

+ 47 - 0
admin/src/main/java/org/springblade/modules/resource/builder/sms/QiniuSmsBuilder.java

@@ -0,0 +1,47 @@
+/*
+ *      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.modules.resource.builder.sms;
+
+import com.qiniu.sms.SmsManager;
+import com.qiniu.util.Auth;
+import lombok.SneakyThrows;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.sms.SmsTemplate;
+import org.springblade.core.sms.props.SmsProperties;
+import org.springblade.core.sms.QiniuSmsTemplate;
+import org.springblade.modules.resource.entity.Sms;
+
+/**
+ * 七牛云短信构建类
+ *
+ * @author Chill
+ */
+public class QiniuSmsBuilder {
+
+	@SneakyThrows
+	public static SmsTemplate template(Sms sms, BladeRedis bladeRedis) {
+		SmsProperties smsProperties = new SmsProperties();
+		smsProperties.setTemplateId(sms.getTemplateId());
+		smsProperties.setAccessKey(sms.getAccessKey());
+		smsProperties.setSecretKey(sms.getSecretKey());
+		smsProperties.setSignName(sms.getSignName());
+		Auth auth = Auth.create(smsProperties.getAccessKey(), smsProperties.getSecretKey());
+		SmsManager smsManager = new SmsManager(auth);
+		return new QiniuSmsTemplate(smsProperties, smsManager, bladeRedis);
+	}
+
+}

+ 157 - 0
admin/src/main/java/org/springblade/modules/resource/builder/sms/SmsBuilder.java

@@ -0,0 +1,157 @@
+/*
+ *      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.modules.resource.builder.sms;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.sms.SmsTemplate;
+import org.springblade.core.sms.enums.SmsEnum;
+import org.springblade.core.sms.enums.SmsStatusEnum;
+import org.springblade.core.sms.props.SmsProperties;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.modules.resource.entity.Sms;
+import org.springblade.modules.resource.service.ISmsService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.springblade.core.cache.constant.CacheConstant.RESOURCE_CACHE;
+
+/**
+ * Sms短信服务统一构建类
+ *
+ * @author Chill
+ */
+public class SmsBuilder {
+
+	public static final String SMS_CODE = "sms:code:";
+	public static final String SMS_PARAM_KEY = "code";
+
+	private final SmsProperties smsProperties;
+	private final ISmsService smsService;
+	private final BladeRedis bladeRedis;
+
+
+	public SmsBuilder(SmsProperties smsProperties, ISmsService smsService, BladeRedis bladeRedis) {
+		this.smsProperties = smsProperties;
+		this.smsService = smsService;
+		this.bladeRedis = bladeRedis;
+	}
+
+	/**
+	 * SmsTemplate配置缓存池
+	 */
+	private final Map<String, SmsTemplate> templatePool = new ConcurrentHashMap<>();
+
+	/**
+	 * Sms配置缓存池
+	 */
+	private final Map<String, Sms> smsPool = new ConcurrentHashMap<>();
+
+
+	/**
+	 * 获取template
+	 *
+	 * @return SmsTemplate
+	 */
+	public SmsTemplate template() {
+		return template(StringPool.EMPTY);
+	}
+
+	/**
+	 * 获取template
+	 *
+	 * @param code 资源编号
+	 * @return SmsTemplate
+	 */
+	public SmsTemplate template(String code) {
+		String tenantId = AuthUtil.getTenantId();
+		Sms sms = getSms(tenantId, code);
+		Sms smsCached = smsPool.get(tenantId);
+		SmsTemplate template = templatePool.get(tenantId);
+		// 若为空或者不一致,则重新加载
+		if (Func.hasEmpty(template, smsCached) || !sms.getTemplateId().equals(smsCached.getTemplateId()) || !sms.getAccessKey().equals(smsCached.getAccessKey())) {
+			synchronized (SmsBuilder.class) {
+				template = templatePool.get(tenantId);
+				if (Func.hasEmpty(template, smsCached) || !sms.getTemplateId().equals(smsCached.getTemplateId()) || !sms.getAccessKey().equals(smsCached.getAccessKey())) {
+					if (sms.getCategory() == SmsEnum.YUNPIAN.getCategory()) {
+						template = YunpianSmsBuilder.template(sms, bladeRedis);
+					} else if (sms.getCategory() == SmsEnum.QINIU.getCategory()) {
+						template = QiniuSmsBuilder.template(sms, bladeRedis);
+					} else if (sms.getCategory() == SmsEnum.ALI.getCategory()) {
+						template = AliSmsBuilder.template(sms, bladeRedis);
+					} else if (sms.getCategory() == SmsEnum.TENCENT.getCategory()) {
+						template = TencentSmsBuilder.template(sms, bladeRedis);
+					}
+					templatePool.put(tenantId, template);
+					smsPool.put(tenantId, sms);
+				}
+			}
+		}
+		return template;
+	}
+
+
+	/**
+	 * 获取短信实体
+	 *
+	 * @param tenantId 租户ID
+	 * @return Sms
+	 */
+	public Sms getSms(String tenantId, String code) {
+		String key = tenantId;
+		LambdaQueryWrapper<Sms> lqw = Wrappers.<Sms>query().lambda().eq(Sms::getTenantId, tenantId);
+		// 获取传参的资源编号并查询,若有则返回,若没有则调启用的配置
+		String smsCode = StringUtil.isBlank(code) ? WebUtil.getParameter(SMS_PARAM_KEY) : code;
+		if (StringUtil.isNotBlank(smsCode)) {
+			key = key.concat(StringPool.DASH).concat(smsCode);
+			lqw.eq(Sms::getSmsCode, smsCode);
+		} else {
+			lqw.eq(Sms::getStatus, SmsStatusEnum.ENABLE.getNum());
+		}
+		Sms sms = CacheUtil.get(RESOURCE_CACHE, SMS_CODE, key, () -> {
+			Sms s = smsService.getOne(lqw);
+			// 若为空则调用默认配置
+			if (s == null || s.getId() == null) {
+				Sms defaultSms = new Sms();
+				defaultSms.setId(0L);
+				defaultSms.setTemplateId(smsProperties.getTemplateId());
+				defaultSms.setRegionId(smsProperties.getRegionId());
+				defaultSms.setCategory(SmsEnum.of(smsProperties.getName()).getCategory());
+				defaultSms.setAccessKey(smsProperties.getAccessKey());
+				defaultSms.setSecretKey(smsProperties.getSecretKey());
+				defaultSms.setSignName(smsProperties.getSignName());
+				return defaultSms;
+			} else {
+				return s;
+			}
+		});
+		if (sms == null || sms.getId() == null) {
+			throw new ServiceException("未获取到对应的短信配置");
+		} else {
+			return sms;
+		}
+	}
+
+}

+ 46 - 0
admin/src/main/java/org/springblade/modules/resource/builder/sms/TencentSmsBuilder.java

@@ -0,0 +1,46 @@
+/*
+ *      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.modules.resource.builder.sms;
+
+import com.github.qcloudsms.SmsMultiSender;
+import lombok.SneakyThrows;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.sms.SmsTemplate;
+import org.springblade.core.sms.props.SmsProperties;
+import org.springblade.core.sms.TencentSmsTemplate;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.resource.entity.Sms;
+
+/**
+ * 腾讯云短信构建类
+ *
+ * @author Chill
+ */
+public class TencentSmsBuilder {
+
+	@SneakyThrows
+	public static SmsTemplate template(Sms sms, BladeRedis bladeRedis) {
+		SmsProperties smsProperties = new SmsProperties();
+		smsProperties.setTemplateId(sms.getTemplateId());
+		smsProperties.setAccessKey(sms.getAccessKey());
+		smsProperties.setSecretKey(sms.getSecretKey());
+		smsProperties.setSignName(sms.getSignName());
+		SmsMultiSender smsSender = new SmsMultiSender(Func.toInt(smsProperties.getAccessKey()), sms.getSecretKey());
+		return new TencentSmsTemplate(smsProperties, smsSender, bladeRedis);
+	}
+
+}

+ 44 - 0
admin/src/main/java/org/springblade/modules/resource/builder/sms/YunpianSmsBuilder.java

@@ -0,0 +1,44 @@
+/*
+ *      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.modules.resource.builder.sms;
+
+import com.yunpian.sdk.YunpianClient;
+import lombok.SneakyThrows;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.sms.SmsTemplate;
+import org.springblade.core.sms.props.SmsProperties;
+import org.springblade.core.sms.YunpianSmsTemplate;
+import org.springblade.modules.resource.entity.Sms;
+
+/**
+ * 云片短信构建类
+ *
+ * @author Chill
+ */
+public class YunpianSmsBuilder {
+
+	@SneakyThrows
+	public static SmsTemplate template(Sms sms, BladeRedis bladeRedis) {
+		SmsProperties smsProperties = new SmsProperties();
+		smsProperties.setTemplateId(sms.getTemplateId());
+		smsProperties.setAccessKey(sms.getAccessKey());
+		smsProperties.setSignName(sms.getSignName());
+		YunpianClient client = new YunpianClient(smsProperties.getAccessKey()).init();
+		return new YunpianSmsTemplate(smsProperties, client, bladeRedis);
+	}
+
+}

+ 44 - 0
admin/src/main/java/org/springblade/modules/resource/config/BladeOssConfiguration.java

@@ -0,0 +1,44 @@
+/*
+ *      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.modules.resource.config;
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.oss.props.OssProperties;
+import org.springblade.modules.resource.builder.oss.OssBuilder;
+import org.springblade.modules.resource.service.IOssService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Oss配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@AllArgsConstructor
+public class BladeOssConfiguration {
+
+	private final OssProperties ossProperties;
+
+	private final IOssService ossService;
+
+	@Bean
+	public OssBuilder ossBuilder() {
+		return new OssBuilder(ossProperties, ossService);
+	}
+
+}

+ 47 - 0
admin/src/main/java/org/springblade/modules/resource/config/BladeSmsConfiguration.java

@@ -0,0 +1,47 @@
+/*
+ *      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.modules.resource.config;
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.sms.props.SmsProperties;
+import org.springblade.modules.resource.builder.sms.SmsBuilder;
+import org.springblade.modules.resource.service.ISmsService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Sms配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@AllArgsConstructor
+public class BladeSmsConfiguration {
+
+	private final SmsProperties smsProperties;
+
+	private final ISmsService smsService;
+
+	private final BladeRedis bladeRedis;
+
+	@Bean
+	public SmsBuilder smsBuilder() {
+		return new SmsBuilder(smsProperties, smsService, bladeRedis);
+	}
+
+}

+ 128 - 0
admin/src/main/java/org/springblade/modules/resource/controller/AttachController.java

@@ -0,0 +1,128 @@
+/*
+ *      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.modules.resource.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.resource.entity.Attach;
+import org.springblade.modules.resource.service.IAttachService;
+import org.springblade.modules.resource.vo.AttachVO;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * 附件表 控制器
+ *
+ * @author Chill
+ */
+@NonDS
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_RESOURCE_NAME + "/attach")
+@Api(value = "附件", tags = "附件")
+public class AttachController extends BladeController {
+
+	private final IAttachService attachService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入attach")
+	public R<Attach> detail(Attach attach) {
+		Attach detail = attachService.getOne(Condition.getQueryWrapper(attach));
+		return R.data(detail);
+	}
+
+	/**
+	 * 分页 附件表
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入attach")
+	public R<IPage<Attach>> list(Attach attach, Query query) {
+		IPage<Attach> pages = attachService.page(Condition.getPage(query), Condition.getQueryWrapper(attach));
+		return R.data(pages);
+	}
+
+	/**
+	 * 自定义分页 附件表
+	 */
+	@GetMapping("/page")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "分页", notes = "传入attach")
+	public R<IPage<AttachVO>> page(AttachVO attach, Query query) {
+		IPage<AttachVO> pages = attachService.selectAttachPage(Condition.getPage(query), attach);
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增 附件表
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "新增", notes = "传入attach")
+	public R save(@Valid @RequestBody Attach attach) {
+		return R.status(attachService.save(attach));
+	}
+
+	/**
+	 * 修改 附件表
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "修改", notes = "传入attach")
+	public R update(@Valid @RequestBody Attach attach) {
+		return R.status(attachService.updateById(attach));
+	}
+
+	/**
+	 * 新增或修改 附件表
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "新增或修改", notes = "传入attach")
+	public R submit(@Valid @RequestBody Attach attach) {
+		return R.status(attachService.saveOrUpdate(attach));
+	}
+
+
+	/**
+	 * 删除 附件表
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		return R.status(attachService.deleteLogic(Func.toLongList(ids)));
+	}
+
+
+}

+ 152 - 0
admin/src/main/java/org/springblade/modules/resource/controller/OssController.java

@@ -0,0 +1,152 @@
+/*
+ *      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.modules.resource.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.secure.annotation.PreAuth;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.constant.RoleConstant;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.resource.entity.Oss;
+import org.springblade.modules.resource.service.IOssService;
+import org.springblade.modules.resource.vo.OssVO;
+import org.springblade.modules.resource.wrapper.OssWrapper;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.validation.Valid;
+
+import static org.springblade.core.cache.constant.CacheConstant.RESOURCE_CACHE;
+
+/**
+ * 控制器
+ *
+ * @author BladeX
+ */
+@NonDS
+@ApiIgnore
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_RESOURCE_NAME + "/oss")
+@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+@Api(value = "对象存储接口", tags = "对象存储接口")
+public class OssController extends BladeController {
+
+	private final IOssService ossService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入oss")
+	public R<OssVO> detail(Oss oss) {
+		Oss detail = ossService.getOne(Condition.getQueryWrapper(oss));
+		return R.data(OssWrapper.build().entityVO(detail));
+	}
+
+	/**
+	 * 分页
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入oss")
+	public R<IPage<OssVO>> list(Oss oss, Query query) {
+		IPage<Oss> pages = ossService.page(Condition.getPage(query), Condition.getQueryWrapper(oss));
+		return R.data(OssWrapper.build().pageVO(pages));
+	}
+
+	/**
+	 * 自定义分页
+	 */
+	@GetMapping("/page")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "分页", notes = "传入oss")
+	public R<IPage<OssVO>> page(OssVO oss, Query query) {
+		IPage<OssVO> pages = ossService.selectOssPage(Condition.getPage(query), oss);
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "新增", notes = "传入oss")
+	public R save(@Valid @RequestBody Oss oss) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(ossService.save(oss));
+	}
+
+	/**
+	 * 修改
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "修改", notes = "传入oss")
+	public R update(@Valid @RequestBody Oss oss) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(ossService.updateById(oss));
+	}
+
+	/**
+	 * 新增或修改
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "新增或修改", notes = "传入oss")
+	public R submit(@Valid @RequestBody Oss oss) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(ossService.submit(oss));
+	}
+
+
+	/**
+	 * 删除
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(ossService.deleteLogic(Func.toLongList(ids)));
+	}
+
+
+	/**
+	 * 启用
+	 */
+	@PostMapping("/enable")
+	@ApiOperationSupport(order = 8)
+	@ApiOperation(value = "配置启用", notes = "传入id")
+	public R enable(@ApiParam(value = "主键", required = true) @RequestParam Long id) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(ossService.enable(id));
+	}
+
+}

+ 153 - 0
admin/src/main/java/org/springblade/modules/resource/controller/SmsController.java

@@ -0,0 +1,153 @@
+/*
+ *      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.modules.resource.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.secure.annotation.PreAuth;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.constant.RoleConstant;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.resource.entity.Sms;
+import org.springblade.modules.resource.service.ISmsService;
+import org.springblade.modules.resource.vo.SmsVO;
+import org.springblade.modules.resource.wrapper.SmsWrapper;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.validation.Valid;
+
+import static org.springblade.core.cache.constant.CacheConstant.RESOURCE_CACHE;
+
+/**
+ * 短信配置表 控制器
+ *
+ * @author BladeX
+ */
+@NonDS
+@ApiIgnore
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_RESOURCE_NAME + "/sms")
+@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+@Api(value = "短信配置表", tags = "短信配置表接口")
+public class SmsController extends BladeController {
+
+	private final ISmsService smsService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入sms")
+	public R<SmsVO> detail(Sms sms) {
+		Sms detail = smsService.getOne(Condition.getQueryWrapper(sms));
+		return R.data(SmsWrapper.build().entityVO(detail));
+	}
+
+	/**
+	 * 分页 短信配置表
+	 */
+	@GetMapping("/list")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页", notes = "传入sms")
+	public R<IPage<SmsVO>> list(Sms sms, Query query) {
+		IPage<Sms> pages = smsService.page(Condition.getPage(query), Condition.getQueryWrapper(sms));
+		return R.data(SmsWrapper.build().pageVO(pages));
+	}
+
+
+	/**
+	 * 自定义分页 短信配置表
+	 */
+	@GetMapping("/page")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "分页", notes = "传入sms")
+	public R<IPage<SmsVO>> page(SmsVO sms, Query query) {
+		IPage<SmsVO> pages = smsService.selectSmsPage(Condition.getPage(query), sms);
+		return R.data(pages);
+	}
+
+	/**
+	 * 新增 短信配置表
+	 */
+	@PostMapping("/save")
+	@ApiOperationSupport(order = 4)
+	@ApiOperation(value = "新增", notes = "传入sms")
+	public R save(@Valid @RequestBody Sms sms) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(smsService.save(sms));
+	}
+
+	/**
+	 * 修改 短信配置表
+	 */
+	@PostMapping("/update")
+	@ApiOperationSupport(order = 5)
+	@ApiOperation(value = "修改", notes = "传入sms")
+	public R update(@Valid @RequestBody Sms sms) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(smsService.updateById(sms));
+	}
+
+	/**
+	 * 新增或修改 短信配置表
+	 */
+	@PostMapping("/submit")
+	@ApiOperationSupport(order = 6)
+	@ApiOperation(value = "新增或修改", notes = "传入sms")
+	public R submit(@Valid @RequestBody Sms sms) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(smsService.submit(sms));
+	}
+
+
+	/**
+	 * 删除 短信配置表
+	 */
+	@PostMapping("/remove")
+	@ApiOperationSupport(order = 7)
+	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(smsService.deleteLogic(Func.toLongList(ids)));
+	}
+
+	/**
+	 * 启用
+	 */
+	@PostMapping("/enable")
+	@ApiOperationSupport(order = 8)
+	@ApiOperation(value = "配置启用", notes = "传入id")
+	public R enable(@ApiParam(value = "主键", required = true) @RequestParam Long id) {
+		CacheUtil.clear(RESOURCE_CACHE);
+		return R.status(smsService.enable(id));
+	}
+
+
+}

+ 247 - 0
admin/src/main/java/org/springblade/modules/resource/endpoint/OssEndpoint.java

@@ -0,0 +1,247 @@
+/*
+ *      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.modules.resource.endpoint;
+
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.oss.model.BladeFile;
+import org.springblade.core.oss.model.OssFile;
+import org.springblade.core.secure.annotation.PreAuth;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.constant.RoleConstant;
+import org.springblade.core.tool.utils.FileUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.resource.builder.oss.OssBuilder;
+import org.springblade.modules.resource.entity.Attach;
+import org.springblade.modules.resource.service.IAttachService;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 对象存储端点
+ *
+ * @author Chill
+ */
+@NonDS
+@RestController
+@AllArgsConstructor
+@Api(value = "对象存储端点", tags = "对象存储端点")
+@RequestMapping(AppConstant.APPLICATION_RESOURCE_NAME + "/oss/endpoint")
+public class OssEndpoint {
+
+	/**
+	 * 对象存储构建类
+	 */
+	private final OssBuilder ossBuilder;
+
+	/**
+	 * 附件表服务
+	 */
+	private final IAttachService attachService;
+
+	/**
+	 * 创建存储桶
+	 *
+	 * @param bucketName 存储桶名称
+	 * @return Bucket
+	 */
+	@SneakyThrows
+	@PostMapping("/make-bucket")
+	@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+	public R makeBucket(@RequestParam String bucketName) {
+		ossBuilder.template().makeBucket(bucketName);
+		return R.success("创建成功");
+	}
+
+	/**
+	 * 创建存储桶
+	 *
+	 * @param bucketName 存储桶名称
+	 * @return R
+	 */
+	@SneakyThrows
+	@PostMapping("/remove-bucket")
+	@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+	public R removeBucket(@RequestParam String bucketName) {
+		ossBuilder.template().removeBucket(bucketName);
+		return R.success("删除成功");
+	}
+
+	/**
+	 * 拷贝文件
+	 *
+	 * @param fileName       存储桶对象名称
+	 * @param destBucketName 目标存储桶名称
+	 * @param destFileName   目标存储桶对象名称
+	 * @return R
+	 */
+	@SneakyThrows
+	@PostMapping("/copy-file")
+	public R copyFile(@RequestParam String fileName, @RequestParam String destBucketName, String destFileName) {
+		ossBuilder.template().copyFile(fileName, destBucketName, destFileName);
+		return R.success("操作成功");
+	}
+
+	/**
+	 * 获取文件信息
+	 *
+	 * @param fileName 存储桶对象名称
+	 * @return InputStream
+	 */
+	@SneakyThrows
+	@GetMapping("/stat-file")
+	public R<OssFile> statFile(@RequestParam String fileName) {
+		return R.data(ossBuilder.template().statFile(fileName));
+	}
+
+	/**
+	 * 获取文件相对路径
+	 *
+	 * @param fileName 存储桶对象名称
+	 * @return String
+	 */
+	@SneakyThrows
+	@GetMapping("/file-path")
+	public R<String> filePath(@RequestParam String fileName) {
+		return R.data(ossBuilder.template().filePath(fileName));
+	}
+
+
+	/**
+	 * 获取文件外链
+	 *
+	 * @param fileName 存储桶对象名称
+	 * @return String
+	 */
+	@SneakyThrows
+	@GetMapping("/file-link")
+	public R<String> fileLink(@RequestParam String fileName) {
+		return R.data(ossBuilder.template().fileLink(fileName));
+	}
+
+	/**
+	 * 上传文件
+	 *
+	 * @param file 文件
+	 * @return ObjectStat
+	 */
+	@SneakyThrows
+	@PostMapping("/put-file")
+	public R<BladeFile> putFile(@RequestParam MultipartFile file) {
+		BladeFile bladeFile = ossBuilder.template().putFile(file.getOriginalFilename(), file.getInputStream());
+		return R.data(bladeFile);
+	}
+
+	/**
+	 * 上传文件
+	 *
+	 * @param fileName 存储桶对象名称
+	 * @param file     文件
+	 * @return ObjectStat
+	 */
+	@SneakyThrows
+	@PostMapping("/put-file-by-name")
+	public R<BladeFile> putFile(@RequestParam String fileName, @RequestParam MultipartFile file) {
+		BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream());
+		return R.data(bladeFile);
+	}
+
+	/**
+	 * 上传文件并保存至附件表
+	 *
+	 * @param file 文件
+	 * @return ObjectStat
+	 */
+	@SneakyThrows
+	@PostMapping("/put-file-attach")
+	public R<BladeFile> putFileAttach(@RequestParam MultipartFile file) {
+		String fileName = file.getOriginalFilename();
+		BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream());
+		Long attachId = buildAttach(fileName, file.getSize(), bladeFile);
+		bladeFile.setAttachId(attachId);
+		return R.data(bladeFile);
+	}
+
+	/**
+	 * 上传文件并保存至附件表
+	 *
+	 * @param fileName 存储桶对象名称
+	 * @param file     文件
+	 * @return ObjectStat
+	 */
+	@SneakyThrows
+	@PostMapping("/put-file-attach-by-name")
+	public R<BladeFile> putFileAttach(@RequestParam String fileName, @RequestParam MultipartFile file) {
+		BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream());
+		Long attachId = buildAttach(fileName, file.getSize(), bladeFile);
+		bladeFile.setAttachId(attachId);
+		return R.data(bladeFile);
+	}
+
+	/**
+	 * 构建附件表
+	 *
+	 * @param fileName  文件名
+	 * @param fileSize  文件大小
+	 * @param bladeFile 对象存储文件
+	 * @return attachId
+	 */
+	private Long buildAttach(String fileName, Long fileSize, BladeFile bladeFile) {
+		String fileExtension = FileUtil.getFileExtension(fileName);
+		Attach attach = new Attach();
+		attach.setDomainUrl(bladeFile.getDomain());
+		attach.setLink(bladeFile.getLink());
+		attach.setName(bladeFile.getName());
+		attach.setOriginalName(bladeFile.getOriginalName());
+		attach.setAttachSize(fileSize);
+		attach.setExtension(fileExtension);
+		attachService.save(attach);
+		return attach.getId();
+	}
+
+	/**
+	 * 删除文件
+	 *
+	 * @param fileName 存储桶对象名称
+	 * @return R
+	 */
+	@SneakyThrows
+	@PostMapping("/remove-file")
+	@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+	public R removeFile(@RequestParam String fileName) {
+		ossBuilder.template().removeFile(fileName);
+		return R.success("操作成功");
+	}
+
+	/**
+	 * 批量删除文件
+	 *
+	 * @param fileNames 存储桶对象名称集合
+	 * @return R
+	 */
+	@SneakyThrows
+	@PostMapping("/remove-files")
+	@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+	public R removeFiles(@RequestParam String fileNames) {
+		ossBuilder.template().removeFiles(Func.toStrList(fileNames));
+		return R.success("操作成功");
+	}
+
+}

+ 179 - 0
admin/src/main/java/org/springblade/modules/resource/endpoint/SmsEndpoint.java

@@ -0,0 +1,179 @@
+/*
+ *      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.modules.resource.endpoint;
+
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.sms.model.SmsCode;
+import org.springblade.core.sms.model.SmsData;
+import org.springblade.core.sms.model.SmsResponse;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.modules.resource.builder.sms.SmsBuilder;
+import org.springblade.modules.resource.utils.SmsUtil;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.springblade.modules.resource.utils.SmsUtil.*;
+
+/**
+ * 短信服务端点
+ *
+ * @author Chill
+ */
+@NonDS
+@RestController
+@AllArgsConstructor
+@RequestMapping(AppConstant.APPLICATION_RESOURCE_NAME + "/sms/endpoint")
+@Api(value = "短信服务端点", tags = "短信服务端点")
+public class SmsEndpoint {
+
+	/**
+	 * 短信服务构建类
+	 */
+	private final SmsBuilder smsBuilder;
+
+	//================================= 短信服务校验 =================================
+
+	/**
+	 * 短信验证码发送
+	 *
+	 * @param phone 手机号
+	 */
+	@SneakyThrows
+	@PostMapping("/send-validate")
+	public R sendValidate(@RequestParam String phone) {
+		Map<String, String> params = SmsUtil.getValidateParams();
+		SmsCode smsCode = smsBuilder.template().sendValidate(new SmsData(params).setKey(PARAM_KEY), phone);
+		return smsCode.isSuccess() ? R.data(smsCode, SEND_SUCCESS) : R.fail(SEND_FAIL);
+	}
+
+	/**
+	 * 校验短信
+	 *
+	 * @param smsCode 短信校验信息
+	 */
+	@SneakyThrows
+	@PostMapping("/validate-message")
+	public R validateMessage(SmsCode smsCode) {
+		boolean validate = smsBuilder.template().validateMessage(smsCode);
+		return validate ? R.success(VALIDATE_SUCCESS) : R.fail(VALIDATE_FAIL);
+	}
+
+	//========== 通用短信自定义发送(支持自定义params参数传递, 推荐用于测试, 不推荐用于生产环境) ==========
+
+	/**
+	 * 发送信息
+	 *
+	 * @param code   资源编号
+	 * @param params 自定义短信参数
+	 * @param phones 手机号集合
+	 */
+	@SneakyThrows
+	@PostMapping("/send-message")
+	public R sendMessage(@RequestParam String code, @RequestParam String params, @RequestParam String phones) {
+		SmsData smsData = new SmsData(JsonUtil.readMap(params, String.class, String.class));
+		return send(code, smsData, phones);
+	}
+
+	//========== 指定短信服务发送(可根据各种场景自定拓展定制, 损失灵活性增加安全性, 推荐用于生产环境) ==========
+
+	/**
+	 * 短信通知
+	 *
+	 * @param phones 手机号集合
+	 */
+	@SneakyThrows
+	@PostMapping("/send-notice")
+	public R sendNotice(@RequestParam String phones) {
+		Map<String, String> params = new HashMap<>(3);
+		params.put("title", "通知标题");
+		params.put("content", "通知内容");
+		params.put("date", "通知时间");
+		SmsData smsData = new SmsData(params);
+		return send(smsData, phones);
+	}
+
+	/**
+	 * 订单通知
+	 *
+	 * @param phones 手机号集合
+	 */
+	@SneakyThrows
+	@PostMapping("/send-order")
+	public R sendOrder(@RequestParam String phones) {
+		Map<String, String> params = new HashMap<>(3);
+		params.put("orderNo", "订单编号");
+		params.put("packageNo", "快递单号");
+		params.put("user", "收件人");
+		SmsData smsData = new SmsData(params);
+		return send(smsData, phones);
+	}
+
+	/**
+	 * 会议通知
+	 *
+	 * @param phones 手机号集合
+	 */
+	@SneakyThrows
+	@PostMapping("/send-meeting")
+	public R sendMeeting(@RequestParam String phones) {
+		Map<String, String> params = new HashMap<>(2);
+		params.put("roomId", "会议室");
+		params.put("topic", "会议主题");
+		params.put("date", "会议时间");
+		SmsData smsData = new SmsData(params);
+		return send(smsData, phones);
+	}
+
+	//================================= 通用短信发送接口 =================================
+
+	/**
+	 * 通用短信发送接口
+	 *
+	 * @param smsData 短信内容
+	 * @param phones  手机号列表
+	 * @return 是否发送成功
+	 */
+	private R send(SmsData smsData, String phones) {
+		SmsResponse response = smsBuilder.template().sendMessage(smsData, Func.toStrList(phones));
+		return response.isSuccess() ? R.success(SEND_SUCCESS) : R.fail(SEND_FAIL);
+	}
+
+	/**
+	 * 通用短信发送接口
+	 *
+	 * @param code    资源编号
+	 * @param smsData 短信内容
+	 * @param phones  手机号列表
+	 * @return 是否发送成功
+	 */
+	private R send(String code, SmsData smsData, String phones) {
+		SmsResponse response = smsBuilder.template(code).sendMessage(smsData, Func.toStrList(phones));
+		return response.isSuccess() ? R.success(SEND_SUCCESS) : R.fail(SEND_FAIL);
+	}
+
+}

+ 71 - 0
admin/src/main/java/org/springblade/modules/resource/entity/Attach.java

@@ -0,0 +1,71 @@
+/*
+ *      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.modules.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 附件表实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_attach")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Attach对象", description = "附件表")
+public class Attach extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 附件地址
+	 */
+	@ApiModelProperty(value = "附件地址")
+	private String link;
+	/**
+	 * 附件域名
+	 */
+	@ApiModelProperty(value = "附件域名")
+	private String domainUrl;
+	/**
+	 * 附件名称
+	 */
+	@ApiModelProperty(value = "附件名称")
+	private String name;
+	/**
+	 * 附件原名
+	 */
+	@ApiModelProperty(value = "附件原名")
+	private String originalName;
+	/**
+	 * 附件拓展名
+	 */
+	@ApiModelProperty(value = "附件拓展名")
+	private String extension;
+	/**
+	 * 附件大小
+	 */
+	@ApiModelProperty(value = "附件大小")
+	private Long attachSize;
+
+
+}

+ 88 - 0
admin/src/main/java/org/springblade/modules/resource/entity/Oss.java

@@ -0,0 +1,88 @@
+/*
+ *      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.modules.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 实体类
+ *
+ * @author BladeX
+ */
+@Data
+@TableName("blade_oss")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Oss对象", description = "Oss对象")
+public class Oss extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 所属分类
+	 */
+	@ApiModelProperty(value = "所属分类")
+	private Integer category;
+
+	/**
+	 * 资源编号
+	 */
+	@ApiModelProperty(value = "资源编号")
+	private String ossCode;
+
+	/**
+	 * oss地址
+	 */
+	@ApiModelProperty(value = "资源地址")
+	private String endpoint;
+	/**
+	 * accessKey
+	 */
+	@ApiModelProperty(value = "accessKey")
+	private String accessKey;
+	/**
+	 * secretKey
+	 */
+	@ApiModelProperty(value = "secretKey")
+	private String secretKey;
+	/**
+	 * 空间名
+	 */
+	@ApiModelProperty(value = "空间名")
+	private String bucketName;
+	/**
+	 * 应用ID TencentCOS需要
+	 */
+	@ApiModelProperty(value = "应用ID")
+	private String appId;
+	/**
+	 * 地域简称 TencentCOS需要
+	 */
+	@ApiModelProperty(value = "地域简称")
+	private String region;
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+
+}

+ 82 - 0
admin/src/main/java/org/springblade/modules/resource/entity/Sms.java

@@ -0,0 +1,82 @@
+/*
+ *      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.modules.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 短信配置表实体类
+ *
+ * @author BladeX
+ */
+@Data
+@TableName("blade_sms")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Sms对象", description = "短信配置表")
+public class Sms extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 资源编号
+	 */
+	@ApiModelProperty(value = "资源编号")
+	private String smsCode;
+
+	/**
+	 * 模板ID
+	 */
+	@ApiModelProperty(value = "模板ID")
+	private String templateId;
+	/**
+	 * 分类
+	 */
+	@ApiModelProperty(value = "分类")
+	private Integer category;
+	/**
+	 * accessKey
+	 */
+	@ApiModelProperty(value = "accessKey")
+	private String accessKey;
+	/**
+	 * secretKey
+	 */
+	@ApiModelProperty(value = "secretKey")
+	private String secretKey;
+	/**
+	 * regionId
+	 */
+	@ApiModelProperty(value = "regionId")
+	private String regionId;
+	/**
+	 * 短信签名
+	 */
+	@ApiModelProperty(value = "短信签名")
+	private String signName;
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+
+}

+ 62 - 0
admin/src/main/java/org/springblade/modules/resource/enums/SmsCodeEnum.java

@@ -0,0 +1,62 @@
+/*
+ *      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.modules.resource.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springblade.core.tool.utils.StringPool;
+
+/**
+ * Sms资源编码枚举类
+ *
+ * @author Chill
+ * @apiNote 该枚举类对应短信配置模块的资源编码,可根据业务需求自行拓展
+ */
+@Getter
+@AllArgsConstructor
+public enum SmsCodeEnum {
+
+	/**
+	 * 默认编号
+	 */
+	DEFAULT(StringPool.EMPTY, 1),
+
+	/**
+	 * 验证码编号
+	 */
+	VALIDATE("qiniu-validate", 2),
+
+	/**
+	 * 通知公告编号
+	 */
+	NOTICE("notice", 3),
+
+	/**
+	 * 下单通知编号
+	 */
+	ORDER("order", 4),
+
+	/**
+	 * 会议通知编号
+	 */
+	MEETING("meeting", 5),
+	;
+
+	final String name;
+	final int category;
+
+}

+ 41 - 0
admin/src/main/java/org/springblade/modules/resource/mapper/AttachMapper.java

@@ -0,0 +1,41 @@
+/*
+ *      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.modules.resource.mapper;
+
+import org.springblade.modules.resource.entity.Attach;
+import org.springblade.modules.resource.vo.AttachVO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import java.util.List;
+
+/**
+ * 附件表 Mapper 接口
+ *
+ * @author Chill
+ */
+public interface AttachMapper extends BaseMapper<Attach> {
+
+	/**
+	 * 自定义分页
+	 *
+	 * @param page
+	 * @param attach
+	 * @return
+	 */
+	List<AttachVO> selectAttachPage(IPage page, AttachVO attach);
+
+}

+ 0 - 0
admin/src/main/java/org/springblade/modules/resource/mapper/AttachMapper.xml


部分文件因文件數量過多而無法顯示