浏览代码

【新增】基础服务和算法服务

linfutong 3 年之前
父节点
当前提交
267def50f3
共有 100 个文件被更改,包括 7978 次插入91 次删除
  1. 10 12
      pom.xml
  2. 4 16
      zd-common/pom.xml
  3. 2 5
      zd-common/zd-common-core/pom.xml
  4. 1 1
      zd-common/zd-common-resultdata/src/main/java/com/zd/common/response/ResponseAdvice.java
  5. 1 1
      zd-common/zd-common-resultdata/src/main/java/com/zd/common/response/ResultData.java
  6. 2 6
      zd-common/zd-common-log/pom.xml
  7. 2 5
      zd-common/zd-common-redis/pom.xml
  8. 0 27
      zd-common/zd-common-resultdata/pom.xml
  9. 2 7
      zd-common/zd-common-security/pom.xml
  10. 2 11
      zd-common/zd-common-swagger/pom.xml
  11. 2 0
      zd-modules/pom.xml
  12. 22 0
      zd-modules/zd-algorithm/pom.xml
  13. 19 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/AlgorithmApplication.java
  14. 90 0
      zd-modules/zd-algorithm/src/main/resources/application.yml
  15. 162 0
      zd-modules/zd-base/pom.xml
  16. 17 0
      zd-modules/zd-base/src/main/java/com/zd/base/BaseApplication.java
  17. 42 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/config/RedisConfig.java
  18. 72 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/controller/UploadController.java
  19. 124 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/dto/FileChunkDTO.java
  20. 46 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/dto/FileChunkResultDTO.java
  21. 75 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/RestApiResponse.java
  22. 29 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/error/BaseErrorCode.java
  23. 42 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/error/BusinessErrorCode.java
  24. 34 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/error/BusinessException.java
  25. 46 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/service/IUploadService.java
  26. 306 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/service/impl/UploadServiceImpl.java
  27. 64 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/utils/FileUtils.java
  28. 74 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/config/MinioConfig.java
  29. 51 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/config/ResourcesConfig.java
  30. 43 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/controller/SysFileController.java
  31. 40 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/FastDfsSysFileServiceImpl.java
  32. 19 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/ISysFileService.java
  33. 55 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/LocalSysFileServiceImpl.java
  34. 44 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/MinioSysFileServiceImpl.java
  35. 201 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/file/utils/FileUploadUtils.java
  36. 123 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/ActionEnter.java
  37. 160 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/ConfigManager.java
  38. 18 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/Encoder.java
  39. 132 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/PathFormat.java
  40. 23 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/SpringUtil.java
  41. 56 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/contorller/UeditorController.java
  42. 35 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/ActionMap.java
  43. 8 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/ActionState.java
  44. 53 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/AppInfo.java
  45. 89 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/BaseState.java
  46. 25 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/FileType.java
  47. 24 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/MIMEType.java
  48. 87 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/MultiState.java
  49. 11 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/State.java
  50. 101 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/hunter/FileManager.java
  51. 110 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/hunter/ImageHunter.java
  52. 38 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/Base64Uploader.java
  53. 65 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/BinaryUploader.java
  54. 120 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/StorageManager.java
  55. 60 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/UploadUtils.java
  56. 28 0
      zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/Uploader.java
  57. 65 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/config/GenConfig.java
  58. 195 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/controller/GenController.java
  59. 363 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/domain/GenTable.java
  60. 362 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/domain/GenTableColumn.java
  61. 59 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/mapper/GenTableColumnMapper.java
  62. 82 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/mapper/GenTableMapper.java
  63. 64 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/service/GenTableColumnServiceImpl.java
  64. 437 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/service/GenTableServiceImpl.java
  65. 43 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/service/IGenTableColumnService.java
  66. 120 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/service/IGenTableService.java
  67. 223 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/utils/GenUtils.java
  68. 31 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/utils/VelocityInitializer.java
  69. 328 0
      zd-modules/zd-base/src/main/java/com/zd/base/gen/utils/VelocityUtils.java
  70. 55 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/config/ScheduleConfig.java
  71. 142 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/controller/SysJobController.java
  72. 83 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/controller/SysJobLogController.java
  73. 168 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/domain/SysJob.java
  74. 155 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/domain/SysJobLog.java
  75. 65 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/mapper/SysJobLogMapper.java
  76. 68 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/mapper/SysJobMapper.java
  77. 56 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/ISysJobLogService.java
  78. 102 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/ISysJobService.java
  79. 81 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/impl/SysJobLogServiceImpl.java
  80. 229 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/impl/SysJobServiceImpl.java
  81. 47 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/ChemicalTask.java
  82. 57 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/ExamTask.java
  83. 103 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/FileViewTask.java
  84. 64 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/LabTask.java
  85. 32 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/RyTask.java
  86. 96 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/AbstractQuartzJob.java
  87. 53 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/CronUtils.java
  88. 158 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/JobInvokeUtil.java
  89. 18 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/QuartzDisallowConcurrentExecution.java
  90. 16 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/QuartzJobExecution.java
  91. 94 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/ScheduleUtils.java
  92. 31 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/base/FridConsumer.java
  93. 121 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/controller/UserOpenIdController.java
  94. 32 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/controller/WXTokenController.java
  95. 138 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/controller/WechatMsgController.java
  96. 30 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/domain/UserOpenId.java
  97. 76 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/mapper/UserOpenIdMapper.java
  98. 108 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/properties/WeChatProperties.java
  99. 17 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/ISendService.java
  100. 0 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/IUserOpenIdService.java

+ 10 - 12
pom.xml

@@ -7,11 +7,20 @@
     <groupId>com.zd</groupId>
     <artifactId>zd</artifactId>
     <version>3.1.0</version>
-
+    <packaging>pom</packaging>
     <name>zd</name>
     <url>http://www.zd.vip</url>
     <description>云微服务系统</description>
 
+    <modules>
+        <module>zd-auth</module>
+        <module>zd-gateway</module>
+        <module>zd-visual</module>
+        <module>zd-modules</module>
+        <module>zd-api</module>
+        <module>zd-common</module>
+    </modules>
+
     <properties>
         <zd.version>3.1.0</zd.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -300,20 +309,9 @@
                 <artifactId>pinyin4j</artifactId>
                 <version>${pinyin4j.version}</version>
             </dependency>
-
         </dependencies>
     </dependencyManagement>
 
-    <modules>
-        <module>zd-auth</module>
-        <module>zd-gateway</module>
-        <module>zd-visual</module>
-        <module>zd-modules</module>
-        <module>zd-api</module>
-        <module>zd-common</module>
-    </modules>
-    <packaging>pom</packaging>
-
     <dependencies>
         <!-- bootstrap 启动器 -->
         <dependency>

+ 4 - 16
zd-common/pom.xml

@@ -7,6 +7,9 @@
         <version>3.1.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
+    <artifactId>zd-common</artifactId>
+    <packaging>pom</packaging>
+    <description>通用模块</description>
 
     <modules>
         <module>zd-common-log</module>
@@ -14,24 +17,9 @@
         <module>zd-common-redis</module>
         <module>zd-common-swagger</module>
         <module>zd-common-security</module>
-        <module>zd-common-datascope</module>
 		<module>zd-common-datasource</module>
-        <module>zd-common-resultdata</module>
         <module>zd-common-mqtt</module>
+        <module>zd-common-datascope</module>
     </modules>
 
-    <artifactId>zd-common</artifactId>
-    <packaging>pom</packaging>
-
-    <description>
-	    zd-common通用模块
-    </description>
-<!--    <dependencyManagement>-->
-
-<!--        <dependencies>-->
-
-
-<!--        </dependencies>-->
-<!--    </dependencyManagement>-->
-
 </project>

+ 2 - 5
zd-common/zd-common-core/pom.xml

@@ -10,10 +10,8 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>zd-common-core</artifactId>
-
-    <description>
-        zd-common-core核心模块
-    </description>
+    <packaging>jar</packaging>
+    <description>核心模块</description>
 
     <dependencies>
 
@@ -136,5 +134,4 @@
         </dependency>
 
     </dependencies>
-
 </project>

+ 1 - 1
zd-common/zd-common-resultdata/src/main/java/com/zd/common/response/ResponseAdvice.java

@@ -1,4 +1,4 @@
-package com.zd.common.response;
+package com.zd.common.core.web.domain;
 
 import com.zd.common.core.domain.R;
 import com.zd.common.core.web.domain.AjaxResult;

+ 1 - 1
zd-common/zd-common-resultdata/src/main/java/com/zd/common/response/ResultData.java

@@ -1,4 +1,4 @@
-package com.zd.common.response;
+package com.zd.common.core.web.domain;
 
 import com.zd.common.core.constant.HttpStatus;
 

+ 2 - 6
zd-common/zd-common-log/pom.xml

@@ -10,18 +10,14 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>zd-common-log</artifactId>
-
-    <description>
-        zd-common-log日志记录
-    </description>
+    <packaging>jar</packaging>
+    <description>日志记录</description>
 
     <dependencies>
-
         <!-- zd Common Security -->
         <dependency>
             <groupId>com.zd</groupId>
             <artifactId>zd-common-security</artifactId>
         </dependency>
-
     </dependencies>
 </project>

+ 2 - 5
zd-common/zd-common-redis/pom.xml

@@ -10,13 +10,10 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>zd-common-redis</artifactId>
-
-    <description>
-        zd-common-redis缓存服务
-    </description>
+    <packaging>jar</packaging>
+    <description>redis缓存服务</description>
 
     <dependencies>
-
         <!-- SpringBoot Boot Redis -->
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 0 - 27
zd-common/zd-common-resultdata/pom.xml

@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>com.zd</groupId>
-        <artifactId>zd-common</artifactId>
-        <version>3.1.0</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>zd-common-resultdata</artifactId>
-
-    <dependencies>
-
-        <!-- zd Common Core-->
-        <dependency>
-            <groupId>com.zd</groupId>
-            <artifactId>zd-common-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-webmvc</artifactId>
-        </dependency>
-
-    </dependencies>
-</project>

+ 2 - 7
zd-common/zd-common-security/pom.xml

@@ -9,13 +9,10 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>zd-common-security</artifactId>
-
-    <description>
-        zd-common-security安全模块
-    </description>
+    <packaging>jar</packaging>
+    <description>安全模块</description>
 
     <dependencies>
-
         <!-- zd Api System -->
         <dependency>
             <groupId>com.zd</groupId>
@@ -32,7 +29,5 @@
             <groupId>com.zd</groupId>
             <artifactId>zd-common-redis</artifactId>
         </dependency>
-
     </dependencies>
-
 </project>

+ 2 - 11
zd-common/zd-common-swagger/pom.xml

@@ -10,25 +10,16 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>zd-common-swagger</artifactId>
-
-    <description>
-        zd-common-swagger系统接口
-    </description>
+    <packaging>jar</packaging>
+    <description>swagger接口</description>
 
 	<dependencies>
-
         <!-- SpringBoot Web -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
 
-        <!-- Swagger -->
-<!--        <dependency>-->
-<!--            <groupId>io.springfox</groupId>-->
-<!--            <artifactId>springfox-swagger2</artifactId>-->
-<!--            <version>${swagger.fox.version}</version>-->
-<!--        </dependency>-->
         <dependency>
             <groupId>com.github.xiaoymin</groupId>
             <artifactId>knife4j-spring-boot-starter</artifactId>

+ 2 - 0
zd-modules/pom.xml

@@ -27,6 +27,8 @@
         <module>zd-netty</module>
         <module>zd-smartlock</module>
         <module>zd-bottle-parent</module>
+        <module>zd-base</module>
+        <module>zd-algorithm</module>
     </modules>
     <dependencies>
         <dependency>

+ 22 - 0
zd-modules/zd-algorithm/pom.xml

@@ -0,0 +1,22 @@
+<?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">
+    <parent>
+        <artifactId>zd-modules</artifactId>
+        <groupId>com.zd</groupId>
+        <version>3.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.zd.alg</groupId>
+    <artifactId>zd-algorithm</artifactId>
+    <version>1.0</version>
+    <description>算法服务</description>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+    </properties>
+
+</project>

+ 19 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/AlgorithmApplication.java

@@ -0,0 +1,19 @@
+package com.zd.alg;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import springfox.documentation.oas.annotations.EnableOpenApi;
+
+
+@ServletComponentScan("com.zd.iot.vmp.conf")
+@SpringBootApplication
+@EnableScheduling
+@EnableOpenApi
+public class AlgorithmApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AlgorithmApplication.class,args);
+    }
+}

+ 90 - 0
zd-modules/zd-algorithm/src/main/resources/application.yml

@@ -0,0 +1,90 @@
+spring:
+  #REDIS数据库配??
+  redis:
+    # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
+    # host: 127.0.0.1
+    # # [必须修改] 端口??
+    # port: 6379
+    # # [可???] 数据?? DB
+    database: 1
+    # # [可???] 访问密码,若你的redis服务器没有设置密码,就不??要用密码去连??
+    # password:
+    # # [可???] 超时时间
+    timeout: 10000
+    host: ${REDIS_HOST:192.168.1.43}
+    port: ${REDIS_PORT:16379}
+    password: ${REDIS_PASS:2ask8to9}
+  # [可???] jdbc数据库配??, 项目使用sqlite作为数据库,??般不??要配??
+  datasource:
+    #使用mysql 打开23-28行注释, 删除29-36??
+    name: wvp
+    url: jdbc:mysql://${MYSQL_HOST:192.168.1.43}:${MYSQL_PORT:13306}/${CAMERA_DATABASE:wvp}?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=GMT
+    username: ${MYSQL_USERNAME:root}
+    password: ${MYSQL_PASSWORD:root123456}
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    #        name: eiot
+    #        url: jdbc:sqlite::resource:wvp.sqlite
+    #        username:
+    #        password:
+    #        type: com.alibaba.druid.pool.DruidDataSource
+    #        driver-class-name:  org.sqlite.JDBC
+    max-active: 1
+    min-idle: 1
+
+# [可???] WVP监听的HTTP端口, 网页和接口调用都是这个端??
+server:
+  port: 18080
+
+# 作为28181服务器的配置
+sip:
+  # [必须修改] 本机的IP
+  ip: 192.168.1.88
+  # [可???] 28181服务监听的端??
+  port: 5060
+  # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级???区级???基层编号组成,参照GB/T 2260-2007??
+  # 后两位为行业编码,定义参照附录D.3
+  # 3701020049标识山东济南历下?? 信息行业接入
+  # [可???]
+  domain: 4401020049
+  # [可???]
+  id: 44010200492000000001
+  # [可???] 默认设备认证密码,后续扩展使用设备单独密??, 移除密码将不进行校验
+  password: admin123
+
+#zlm 默认服务器配??
+media:
+  # [必须修改] zlm服务器的内网IP
+  ip: 192.168.1.88
+  # [必须修改] zlm服务器的http.port
+  http-port: 8230
+  # [可???] zlm服务器的hook.admin_params=secret
+  secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
+  # [可???] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修??
+  auto-config: false
+  # 启用多端口模??, 多端口模式使用端口区分每路流,兼容???更好??? 单端口使用流的ssrc区分?? 点播超时建议使用多端口测??
+  # [可???] zlm服务器的general.streamNoneReaderDelayMS
+  stream-none-reader-delay-ms:  -1  # 无人观看多久自动关闭??, -1表示永不自动关闭,?? 关闭按需拉流
+  rtp:
+    # [可???] 是否启用多端口模??, ??启后会在portRange范围内???择端口用于媒体流传??
+    enable: true
+    # [可???] 在此范围内???择端口用于媒体流传??,
+    port-range: 30000,30500 # 端口范围
+  # 录像辅助服务?? 部署此服务可以实现zlm录像的管理与下载?? 0 表示不使??
+  record-assist-port: 0
+
+
+# [根据业务??求配置]
+user-settings:
+  # 推流直播是否录制
+  record-push-live: true
+
+# 在线文档?? swagger-ui(生产环境建议关闭)
+swagger-ui:
+  enabled: true
+
+# 版本信息?? 不需修改
+version:
+  version: "@project.version@"
+  description: "@project.description@"
+  artifact-id: "@project.artifactId@"

+ 162 - 0
zd-modules/zd-base/pom.xml

@@ -0,0 +1,162 @@
+<?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">
+    <parent>
+        <artifactId>zd-modules</artifactId>
+        <groupId>com.zd</groupId>
+        <version>3.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.zd.base</groupId>
+    <artifactId>zd-base</artifactId>
+    <version>1.0</version>
+    <description>基础服务</description>
+
+    <dependencies>
+        <!-- SpringCloud Alibaba Nacos -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <!-- SpringCloud Alibaba Sentinel -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+
+        <!-- SpringBoot Actuator -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!-- Mysql Connector -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <!-- redis -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <!-- zd Common DataSource -->
+        <dependency>
+            <groupId>com.zd</groupId>
+            <artifactId>zd-common-datasource</artifactId>
+        </dependency>
+
+        <!-- zd Common Log -->
+        <dependency>
+            <groupId>com.zd</groupId>
+            <artifactId>zd-common-log</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.zd</groupId>
+            <artifactId>zd-common-resultdata</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>connect</groupId>
+            <artifactId>connect-lib</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/libs/connect-lib.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>reader</groupId>
+            <artifactId>reader-lib</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/libs/reader-lib.jar</systemPath>
+        </dependency>
+
+        <!-- Quartz -->
+        <dependency>
+            <groupId>org.quartz-scheduler</groupId>
+            <artifactId>quartz</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.mchange</groupId>
+                    <artifactId>c3p0</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- Apache Velocity -->
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity</artifactId>
+        </dependency>
+
+        <!-- FastDFS -->
+        <dependency>
+            <groupId>com.github.tobato</groupId>
+            <artifactId>fastdfs-client</artifactId>
+        </dependency>
+
+        <!-- Minio -->
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+            <version>${minio.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20140107</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <includeSystemScope>true</includeSystemScope>
+                    <fork>true</fork>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- 打包时跳过test插件,不运行test测试用例 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${maven-surefire-plugin.version}</version>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 17 - 0
zd-modules/zd-base/src/main/java/com/zd/base/BaseApplication.java

@@ -0,0 +1,17 @@
+package com.zd.base;
+
+import com.zd.common.security.annotation.EnableCustomConfig;
+import com.zd.common.security.annotation.EnableRyFeignClients;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+
+@EnableCustomConfig
+@EnableRyFeignClients
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
+public class BaseApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(BaseApplication.class,args);
+    }
+}

+ 42 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/config/RedisConfig.java

@@ -0,0 +1,42 @@
+package com.zd.base.files.bigupload.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * @Author donggaosheng
+ * @Date 2020/12/2 11:26
+ * @Version 1.0
+ **/
+@Configuration
+public class RedisConfig {
+
+    @Bean
+    @SuppressWarnings("all")
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
+        template.setConnectionFactory(factory);
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        // key采用String的序列化方式
+        template.setKeySerializer(stringRedisSerializer);
+        // hash的key也采用String的序列化方式
+        template.setHashKeySerializer(stringRedisSerializer);
+        // value序列化方式采用jackson
+        template.setValueSerializer(jackson2JsonRedisSerializer);
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+        template.afterPropertiesSet();
+        return template;
+    }
+}

+ 72 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/controller/UploadController.java

@@ -0,0 +1,72 @@
+package com.zd.base.files.bigupload.controller;
+
+
+import com.zd.base.files.bigupload.dto.FileChunkDTO;
+import com.zd.base.files.bigupload.dto.FileChunkResultDTO;
+import com.zd.base.files.bigupload.response.RestApiResponse;
+import com.zd.base.files.bigupload.service.IUploadService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/16 19:22
+ * @Version 1.0
+ **/
+@RestController
+@RequestMapping("upload")
+public class UploadController {
+
+    @Autowired
+    private IUploadService uploadService;
+
+    /**
+     * 检查分片是否存在
+     *
+     * @return
+     */
+    @GetMapping("chunk")
+    public RestApiResponse<Object> checkChunkExist(FileChunkDTO chunkDTO) {
+        FileChunkResultDTO fileChunkCheckDTO;
+        try {
+            fileChunkCheckDTO = uploadService.checkChunkExist(chunkDTO);
+            return RestApiResponse.success(fileChunkCheckDTO);
+        } catch (Exception e) {
+            return RestApiResponse.error(e.getMessage());
+        }
+    }
+
+
+    /**
+     * 上传文件分片
+     *
+     * @param chunkDTO
+     * @return
+     */
+    @PostMapping("chunk")
+    public RestApiResponse<Object> uploadChunk(FileChunkDTO chunkDTO) {
+        try {
+            uploadService.uploadChunk(chunkDTO);
+            return RestApiResponse.success(chunkDTO.getIdentifier());
+        } catch (Exception e) {
+            return RestApiResponse.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 请求合并文件分片
+     *
+     * @param chunkDTO
+     * @return
+     */
+    @PostMapping("merge")
+    public RestApiResponse<Object> mergeChunks(@RequestBody FileChunkDTO chunkDTO) {
+        try {
+            String filePath = uploadService.mergeChunk(chunkDTO.getIdentifier(), chunkDTO.getFilename(), chunkDTO.getTotalChunks());
+            return RestApiResponse.success(filePath);
+        } catch (Exception e) {
+            return RestApiResponse.error(e.getMessage());
+        }
+    }
+
+}

+ 124 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/dto/FileChunkDTO.java

@@ -0,0 +1,124 @@
+package com.zd.base.files.bigupload.dto;
+
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 文件分片
+ *
+ * @Author donggaosheng
+ * @Date 2021/1/16 19:35
+ * @Version 1.0
+ **/
+public class FileChunkDTO {
+    /**
+     * 文件md5
+     */
+    private String identifier;
+    /**
+     * 分块文件
+     */
+    MultipartFile file;
+    /**
+     * 当前分块序号
+     */
+    private Integer chunkNumber;
+    /**
+     * 分块大小
+     */
+    private Long chunkSize;
+    /**
+     * 当前分块大小
+     */
+    private Long currentChunkSize;
+    /**
+     * 文件总大小
+     */
+    private Long totalSize;
+    /**
+     * 分块总数
+     */
+    private Integer totalChunks;
+    /**
+     * 文件名
+     */
+    private String filename;
+
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    public MultipartFile getFile() {
+        return file;
+    }
+
+    public void setFile(MultipartFile file) {
+        this.file = file;
+    }
+
+
+    public Integer getChunkNumber() {
+        return chunkNumber;
+    }
+
+    public void setChunkNumber(Integer chunkNumber) {
+        this.chunkNumber = chunkNumber;
+    }
+
+    public Long getChunkSize() {
+        return chunkSize;
+    }
+
+    public void setChunkSize(Long chunkSize) {
+        this.chunkSize = chunkSize;
+    }
+
+    public Long getCurrentChunkSize() {
+        return currentChunkSize;
+    }
+
+    public void setCurrentChunkSize(Long currentChunkSize) {
+        this.currentChunkSize = currentChunkSize;
+    }
+
+    public Long getTotalSize() {
+        return totalSize;
+    }
+
+    public void setTotalSize(Long totalSize) {
+        this.totalSize = totalSize;
+    }
+
+    public Integer getTotalChunks() {
+        return totalChunks;
+    }
+
+    public void setTotalChunks(Integer totalChunks) {
+        this.totalChunks = totalChunks;
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public void setFilename(String filename) {
+        this.filename = filename;
+    }
+
+    @Override
+    public String toString() {
+        return "FileChunkDTO{" +
+                "identifier='" + identifier + '\'' +
+                ", file=" + file +
+                ", chunkNumber=" + chunkNumber +
+                ", chunkSize=" + chunkSize +
+                ", currentChunkSize=" + currentChunkSize +
+                ", totalSize=" + totalSize +
+                ", totalChunks=" + totalChunks +
+                ", filename='" + filename + '\'' +
+                '}';
+    }
+}

+ 46 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/dto/FileChunkResultDTO.java

@@ -0,0 +1,46 @@
+package com.zd.base.files.bigupload.dto;
+
+import java.util.Set;
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/17 10:14
+ * @Version 1.0
+ **/
+public class FileChunkResultDTO {
+    /**
+     * 是否跳过上传
+     */
+    private Boolean skipUpload;
+
+    /**
+     * 已上传分片的集合
+     */
+    private Set<Integer> uploaded;
+
+    public Boolean getSkipUpload() {
+        return skipUpload;
+    }
+
+    public void setSkipUpload(Boolean skipUpload) {
+        this.skipUpload = skipUpload;
+    }
+
+    public Set<Integer> getUploaded() {
+        return uploaded;
+    }
+
+    public void setUploaded(Set<Integer> uploaded) {
+        this.uploaded = uploaded;
+    }
+
+
+    public FileChunkResultDTO(Boolean skipUpload, Set<Integer> uploaded) {
+        this.skipUpload = skipUpload;
+        this.uploaded = uploaded;
+    }
+
+    public FileChunkResultDTO(Boolean skipUpload) {
+        this.skipUpload = skipUpload;
+    }
+}

+ 75 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/RestApiResponse.java

@@ -0,0 +1,75 @@
+package com.zd.base.files.bigupload.response;
+
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/16 14:37
+ * @Version 1.0
+ **/
+public class RestApiResponse<T> {
+
+    private int code;
+
+    /**
+     * 是否成功
+     */
+    private boolean success;
+
+    /**
+     * 响应数据
+     */
+    private T data;
+
+    public boolean isSuccess() {
+        return success;
+    }
+
+    public void setSuccess(boolean success) {
+        this.success = success;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public static <T> RestApiResponse<T> success(T data) {
+        RestApiResponse<T> result = new RestApiResponse<>();
+        result.success = true;
+        result.code=200;
+        result.data = data;
+        return result;
+    }
+
+    public static <T> RestApiResponse<T> success() {
+        RestApiResponse<T> result = new RestApiResponse<>();
+        result.success = true;
+        result.code=200;
+        return result;
+    }
+
+    public static <T> RestApiResponse<T> error(T data) {
+        RestApiResponse<T> result = new RestApiResponse<>();
+        result.success = false;
+        result.code=500;
+        result.data = data;
+        return result;
+    }
+
+    public static <T> RestApiResponse<T> flag(boolean data) {
+        RestApiResponse<T> result = new RestApiResponse<>();
+        result.success = data;
+        return result;
+    }
+}

+ 29 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/error/BaseErrorCode.java

@@ -0,0 +1,29 @@
+package com.zd.base.files.bigupload.response.error;
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/16 14:40
+ * @Version 1.0
+ **/
+public interface BaseErrorCode {
+    /**
+     * 获取错误码
+     *
+     * @return
+     */
+    int getErrorCode();
+
+    /**
+     * 获取错误信息
+     *
+     * @return
+     */
+    String getErrorMsg();
+
+    /**
+     * 设置错误信息
+     *
+     * @param errorMsg
+     */
+    void setErrorMsg(String errorMsg);
+}

+ 42 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/error/BusinessErrorCode.java

@@ -0,0 +1,42 @@
+package com.zd.base.files.bigupload.response.error;
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/16 14:49
+ * @Version 1.0
+ **/
+public enum BusinessErrorCode implements BaseErrorCode {
+
+    USER_NOT_LOGIN(10001, "用户未登入"),
+    INVALID_PARAMETER(10002, "参数错误");
+
+    /**
+     * 错误消息
+     */
+    private String errorMsg;
+    /**
+     * 错误码
+     */
+    private Integer errorCode;
+
+    BusinessErrorCode(Integer errorCode, String errorMsg) {
+        this.errorMsg = errorMsg;
+        this.errorCode = errorCode;
+    }
+
+    @Override
+    public int getErrorCode() {
+        return this.errorCode;
+    }
+
+    @Override
+    public String getErrorMsg() {
+        return this.errorMsg;
+    }
+
+    @Override
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+}

+ 34 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/response/error/BusinessException.java

@@ -0,0 +1,34 @@
+package com.zd.base.files.bigupload.response.error;
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/16 14:45
+ * @Version 1.0
+ **/
+public class BusinessException extends Exception {
+
+    private BaseErrorCode errorCode;
+
+    public BusinessException(BaseErrorCode errorCode) {
+        super(errorCode.getErrorMsg());
+        this.errorCode = errorCode;
+    }
+
+    public BusinessException(BaseErrorCode errorCode, String customizedErrorMsg) {
+        super(customizedErrorMsg);
+        this.errorCode = errorCode;
+        errorCode.setErrorMsg(customizedErrorMsg);
+    }
+
+    public static void main(String[] args) throws BusinessException {
+        throw new BusinessException(BusinessErrorCode.USER_NOT_LOGIN);
+    }
+
+    public BaseErrorCode getErrorCode() {
+        return errorCode;
+    }
+
+    public void setErrorCode(BaseErrorCode errorCode) {
+        this.errorCode = errorCode;
+    }
+}

+ 46 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/service/IUploadService.java

@@ -0,0 +1,46 @@
+package com.zd.base.files.bigupload.service;
+
+
+import com.zd.base.files.bigupload.dto.FileChunkDTO;
+import com.zd.base.files.bigupload.dto.FileChunkResultDTO;
+import com.zd.base.files.bigupload.response.error.BusinessException;
+
+import java.io.IOException;
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/17 10:21
+ * @Version 1.0
+ * @Modify XJLZ
+ **/
+public interface IUploadService {
+
+    /**
+     * 检查文件是否存在,如果存在则跳过该文件的上传,如果不存在,返回需要上传的分片集合
+     *
+     * @param chunkDTO
+     * @return
+     */
+    FileChunkResultDTO checkChunkExist(FileChunkDTO chunkDTO) throws BusinessException;
+
+
+    /**
+     * 上传文件分片
+     *
+     * @param chunkDTO
+     */
+    void uploadChunk(FileChunkDTO chunkDTO) throws BusinessException, IOException;
+
+
+    /**
+     * 合并文件分片
+     *
+     * @param identifier
+     * @param fileName
+     * @param totalChunks
+     * @return
+     * @throws BusinessException
+     * @throws IOException
+     */
+    String mergeChunk(String identifier, String fileName, Integer totalChunks) throws BusinessException, IOException;
+}

+ 306 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/service/impl/UploadServiceImpl.java

@@ -0,0 +1,306 @@
+package com.zd.base.files.bigupload.service.impl;
+
+
+import com.zd.base.files.bigupload.dto.FileChunkDTO;
+import com.zd.base.files.bigupload.dto.FileChunkResultDTO;
+import com.zd.base.files.bigupload.response.error.BusinessErrorCode;
+import com.zd.base.files.bigupload.response.error.BusinessException;
+import com.zd.base.files.bigupload.service.IUploadService;
+import com.zd.base.files.bigupload.utils.FileUtils;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import java.io.*;
+import java.net.UnknownHostException;
+import java.util.*;
+
+
+/**
+ * @Author donggaosheng
+ * @Date 2021/1/17 10:24
+ * @Version 1.0
+ * @Modify XJLZ
+ **/
+@Service
+@SuppressWarnings("all")
+public class UploadServiceImpl implements IUploadService {
+
+    private static final String FILE_PREFIX = "bigfile:";
+
+    private Logger logger = LoggerFactory.getLogger(UploadServiceImpl.class);
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    /**
+     * 域名或本机访问地址
+     */
+    @Value("${file.domain}")
+    public String domain;
+
+    /**
+     * 上传文件存储在本地的根路径
+     */
+    @Value("${file.path}")
+    private String uploadFolder;
+
+    /**
+     * 资源映射路径 前缀
+     */
+    @Value("${file.prefix}")
+    public String filePrefix;
+
+    /**
+     * 资源映射路径 前缀
+     */
+    @Value("${file.bigFile}")
+    public String bigFile;
+
+    /**
+     * 检查文件是否存在,如果存在则跳过该文件的上传,如果不存在,返回需要上传的分片集合
+     *
+     * @param chunkDTO
+     * @return
+     */
+    @Override
+    public FileChunkResultDTO checkChunkExist(FileChunkDTO chunkDTO) throws BusinessException {
+        //1.检查文件是否已上传过
+        //1.1)检查在磁盘中是否存在
+        String fileFolderPath = getFileFolderPath(chunkDTO.getIdentifier());
+        logger.info("fileFolderPath-->{}", fileFolderPath);
+        String filePath = getFilePath(chunkDTO.getIdentifier(), chunkDTO.getFilename());
+        File file = new File(filePath);
+        boolean exists = file.exists();
+        //1.2)检查Redis中是否存在,并且所有分片已经上传完成。
+        Set<Integer> uploaded = (Set<Integer>) redisTemplate.opsForHash().get(FILE_PREFIX + chunkDTO.getIdentifier(), "uploaded");
+        if (uploaded != null && uploaded.size() == chunkDTO.getTotalChunks() && exists) {
+            return new FileChunkResultDTO(true);
+        }
+        File fileFolder = new File(fileFolderPath);
+        if (!fileFolder.exists()) {
+            boolean mkdirs = fileFolder.mkdirs();
+            logger.info("准备工作,创建文件夹,fileFolderPath:{},mkdirs:{}", fileFolderPath, mkdirs);
+        }
+        // 断点续传,返回已上传的分片
+        return new FileChunkResultDTO(false, uploaded);
+    }
+
+
+    /**
+     * 上传分片
+     *
+     * @param chunkDTO
+     */
+    @Override
+    public void uploadChunk(FileChunkDTO chunkDTO) throws BusinessException {
+        //分块的目录
+        String chunkFileFolderPath = getChunkFileFolderPath(chunkDTO.getIdentifier());
+        logger.info("分块的目录 -> {}", chunkFileFolderPath);
+        File chunkFileFolder = new File(chunkFileFolderPath);
+        if (!chunkFileFolder.exists()) {
+            boolean mkdirs = chunkFileFolder.mkdirs();
+            logger.info("创建分片文件夹:{}", mkdirs);
+        }
+        //写入分片
+        try (
+                InputStream inputStream = chunkDTO.getFile().getInputStream();
+                FileOutputStream outputStream = new FileOutputStream(new File(chunkFileFolderPath + chunkDTO.getChunkNumber()))
+        ) {
+            IOUtils.copy(inputStream, outputStream);
+            logger.info("文件标识:{},chunkNumber:{}", chunkDTO.getIdentifier(), chunkDTO.getChunkNumber());
+            //将该分片写入redis
+            long size = saveToRedis(chunkDTO);
+        } catch (Exception e) {
+            throw new BusinessException(BusinessErrorCode.INVALID_PARAMETER, e.getMessage());
+        }
+    }
+
+
+    @Override
+    public String mergeChunk(String identifier, String fileName, Integer totalChunks) throws BusinessException, IOException {
+        String suffix = fileName.substring(fileName.lastIndexOf("."));
+        if(null==suffix){
+            throw new RuntimeException("文件格式有误");
+        }
+        fileName= UUID.randomUUID().toString()+suffix;
+        return mergeChunks(identifier, fileName, totalChunks);
+    }
+
+    /**
+     * 合并分片
+     *
+     * @param identifier
+     * @param filename
+     */
+    private String mergeChunks(String identifier,String filename, Integer totalChunks) throws IOException {
+        String chunkFileFolderPath = getChunkFileFolderPath(identifier);
+        String prefixFileFolderPath = getPrefixFileFolderPath(identifier);
+        String filePath = getFilePath(identifier, filename);
+        String accessPath = getNetWorkPath(filename);
+        // 检查分片是否都存在
+        if (checkChunks(chunkFileFolderPath, totalChunks)) {
+            File chunkFileFolder = new File(chunkFileFolderPath);
+            File mergeFile = new File(filePath);
+            if (!mergeFile.exists()) {
+                mergeFile.createNewFile();
+            }
+            File[] chunks = chunkFileFolder.listFiles();
+            //排序
+            List fileList = Arrays.asList(chunks);
+            Collections.sort(fileList, (Comparator<File>) (o1, o2) -> {
+                return Integer.parseInt(o1.getName()) - (Integer.parseInt(o2.getName()));
+            });
+            try {
+                RandomAccessFile randomAccessFileWriter = new RandomAccessFile(mergeFile, "rw");
+                byte[] bytes = new byte[1024];
+                for (File chunk : chunks) {
+                    RandomAccessFile randomAccessFileReader = new RandomAccessFile(chunk, "r");
+                    int len;
+                    while ((len = randomAccessFileReader.read(bytes)) != -1) {
+                        randomAccessFileWriter.write(bytes, 0, len);
+                    }
+                    randomAccessFileReader.close();
+                }
+                randomAccessFileWriter.close();
+                FileUtils.delFolder(prefixFileFolderPath);
+            } catch (Exception e) {
+                return null;
+            }
+            return accessPath;
+        }
+        return accessPath;
+    }
+
+    private String getAcessPath(String prefix, String filename) {
+        if (!StringUtils.isEmpty(prefix)) {
+            return prefix + "/" + FileUtils.getFormatter() + "/" + filename;
+        }
+        return FileUtils.getFormatter() + "/" + filename;
+    }
+
+
+    private String getNetWorkPath(String fileName) throws UnknownHostException {
+        /*String urlPrefix = domain;
+        String localIP = "127.0.0.1";
+        if (urlPrefix.contains(localIP)) {
+            String ip = InetAddress.getLocalHost().getHostAddress();;
+            urlPrefix = urlPrefix.replace(localIP, ip);
+        }
+        String url = urlPrefix + filePrefix+"/"+bigFile+"/"+ FileUtils.getFormatter() + "/" + fileName;*/
+        String url = filePrefix+"/"+bigFile+"/"+ FileUtils.getFormatter() + "/" + fileName;
+        return url.replace("//","/").replace("./",".//");
+    }
+
+    /**
+     * 检查分片是否都存在
+     *
+     * @param chunkFileFolderPath
+     * @param totalChunks
+     * @return
+     */
+    private boolean checkChunks(String chunkFileFolderPath, Integer totalChunks) {
+        try {
+            for (int i = 1; i <= totalChunks + 1; i++) {
+                File file = new File(chunkFileFolderPath + File.separator + i);
+                if (file.exists()) {
+                    continue;
+                } else {
+                    return false;
+                }
+            }
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 分片写入Redis
+     *
+     * @param chunkDTO
+     */
+    private synchronized long saveToRedis(FileChunkDTO chunkDTO) {
+        Set<Integer> uploaded = (Set<Integer>) redisTemplate.opsForHash().get(FILE_PREFIX + chunkDTO.getIdentifier(), "uploaded");
+        if (uploaded == null) {
+            uploaded = new HashSet<>(Arrays.asList(chunkDTO.getChunkNumber()));
+            HashMap<String, Object> objectObjectHashMap = new HashMap<>();
+            objectObjectHashMap.put("uploaded", uploaded);
+            objectObjectHashMap.put("totalChunks", chunkDTO.getTotalChunks());
+            objectObjectHashMap.put("totalSize", chunkDTO.getTotalSize());
+            objectObjectHashMap.put("path", chunkDTO.getFilename());
+            redisTemplate.opsForHash().putAll(FILE_PREFIX + chunkDTO.getIdentifier(), objectObjectHashMap);
+        } else {
+            uploaded.add(chunkDTO.getChunkNumber());
+            redisTemplate.opsForHash().put(FILE_PREFIX + chunkDTO.getIdentifier(), "uploaded", uploaded);
+        }
+        return uploaded.size();
+    }
+
+    /**
+     * 得到文件的绝对路径
+     *
+     * @param identifier
+     * @param filename
+     * @return
+     */
+    private String getFilePath(String identifier, String filename) {
+        String ext = filename.substring(filename.lastIndexOf("."));
+        String filePath=uploadFolder +"/"+bigFile+"/"+ FileUtils.getFormatter();
+        File file = new File(filePath);
+        if (!file.exists()) {
+            file.mkdirs();
+        }
+        filePath=filePath+"/" + filename;
+        return filePath.replace("//","/");
+    }
+
+    /**
+     * 得到文件的相对路径
+     *
+     * @param identifier
+     * @param filename
+     * @return
+     */
+    private String getFileRelativelyPath(String identifier, String filename) {
+        String ext = filename.substring(filename.lastIndexOf("."));
+        return "/" + identifier.substring(0, 1) + "/" +
+                identifier.substring(1, 2) + "/" +
+                identifier + "/" + identifier
+                + ext;
+    }
+
+
+    /**
+     * 得到分块文件所属的目录
+     *
+     * @param identifier
+     * @return
+     */
+    private String getChunkFileFolderPath(String identifier) {
+        return getFileFolderPath(identifier) + "chunks" + File.separator;
+    }
+
+
+    private String getPrefixFileFolderPath(String identifier) {
+        String prefixFile=uploadFolder +"/"+filePrefix+"/"+bigFile +"/"+identifier.substring(0, 1);
+        return prefixFile.replace("//","/");
+    }
+
+    /**
+     * 得到文件所属的目录
+     *
+     * @param identifier
+     * @return
+     */
+    private String getFileFolderPath(String identifier) {
+        String fileFolderPath=uploadFolder+"/"+filePrefix+"/"+bigFile+"/"+identifier.substring(0, 1) + File.separator +
+                identifier.substring(1, 2) + File.separator +
+                identifier + File.separator;
+        return fileFolderPath.replace("//","/");
+    }
+}

+ 64 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/bigupload/utils/FileUtils.java

@@ -0,0 +1,64 @@
+package com.zd.base.files.bigupload.utils;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class FileUtils {
+    //删除文件夹
+    public static void delFolder(String folderPath) {
+        try {
+            delAllFile(folderPath); //删除完里面所有内容
+            String filePath = folderPath;
+            File myFilePath = new File(filePath);
+            myFilePath.delete(); //删除空文件夹
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    //删除指定文件夹下的所有文件
+
+    public static boolean delAllFile(String path) {
+        boolean flag = false;
+        File file = new File(path);
+        if (!file.exists()) {
+            return flag;
+        }
+        if (!file.isDirectory()) {
+            return flag;
+        }
+        String[] tempList = file.list();
+        File temp = null;
+        for (int i = 0; i < tempList.length; i++) {
+            if (path.endsWith(File.separator)) {
+                temp = new File(path + tempList[i]);
+            } else {
+                temp = new File(path + File.separator + tempList[i]);
+            }
+            if (temp.isFile()) {
+                temp.delete();
+            }
+            if (temp.isDirectory()) {
+                delAllFile(path + File.separator + tempList[i]);//先删除文件夹里面的文件
+                delFolder(path + File.separator + tempList[i]);//再删除空文件夹
+                flag = true;
+            }
+        }
+        return flag;
+    }
+
+    /**
+     * 格式化数据
+     *
+     * @return
+     */
+    public static String getFormatter() {
+        Date d = new Date();
+        System.out.println(d);
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
+        String dateNowStr = sdf.format(d);
+        return dateNowStr;
+    }
+
+}

+ 74 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/config/MinioConfig.java

@@ -0,0 +1,74 @@
+package com.zd.base.files.file.config;
+
+import io.minio.MinioClient;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Minio 配置信息
+ *
+ * @author zd
+ */
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "minio")
+public class MinioConfig {
+    /**
+     * 服务地址
+     */
+    private String url;
+
+    /**
+     * 用户名
+     */
+    private String accessKey;
+
+    /**
+     * 密码
+     */
+    private String secretKey;
+
+    /**
+     * 存储桶名称
+     */
+    private String bucketName;
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+    public String getBucketName() {
+        return bucketName;
+    }
+
+    public void setBucketName(String bucketName) {
+        this.bucketName = bucketName;
+    }
+
+    @Bean
+    public MinioClient getMinioClient() {
+        return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
+    }
+}

+ 51 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/config/ResourcesConfig.java

@@ -0,0 +1,51 @@
+package com.zd.base.files.file.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.io.File;
+
+/**
+ * 通用映射配置
+ *
+ * @author zd
+ */
+@Configuration
+@RefreshScope
+public class ResourcesConfig implements WebMvcConfigurer {
+    /**
+     * 上传文件存储在本地的根路径
+     */
+    @Value("${file.path}")
+    private String localFilePath;
+
+    /**
+     * 资源映射路径 前缀
+     */
+    @Value("${file.prefix}")
+    public String localFilePrefix;
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        /** 本地文件上传路径 */
+        registry.addResourceHandler(localFilePrefix + "/**")
+                .addResourceLocations("file:" + localFilePath + File.separator);
+    }
+
+    /**
+     * 开启跨域
+     */
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        // 设置允许跨域的路由
+        registry.addMapping(localFilePrefix + "/**")
+                // 设置允许跨域请求的域名
+                .allowedOrigins("*")
+                // 设置允许的方法
+                .allowedMethods("GET");
+    }
+}

+ 43 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/controller/SysFileController.java

@@ -0,0 +1,43 @@
+package com.zd.base.files.file.controller;
+
+import com.zd.base.files.file.service.ISysFileService;
+import com.zd.common.core.domain.R;
+import com.zd.common.core.utils.file.FileUtils;
+import com.zd.system.api.domain.SysFile;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 文件请求处理
+ *
+ * @author zd
+ */
+@RestController
+public class SysFileController {
+    private static final Logger log = LoggerFactory.getLogger(SysFileController.class);
+
+    @Autowired
+    private ISysFileService sysFileService;
+
+    /**
+     * 文件上传请求
+     */
+    @PostMapping("upload")
+    public R<SysFile> upload(MultipartFile file) {
+        try {
+            // 上传并返回访问地址
+            String url = sysFileService.uploadFile(file);
+            SysFile sysFile = new SysFile();
+            sysFile.setName(FileUtils.getName(url));
+            sysFile.setUrl(url);
+            return R.ok(sysFile);
+        } catch (Exception e) {
+            log.error("上传文件失败", e);
+            return R.fail(e.getMessage());
+        }
+    }
+}

+ 40 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/FastDfsSysFileServiceImpl.java

@@ -0,0 +1,40 @@
+package com.zd.base.files.file.service;
+
+import com.github.tobato.fastdfs.domain.fdfs.StorePath;
+import com.github.tobato.fastdfs.service.FastFileStorageClient;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * FastDFS 文件存储
+ *
+ * @author zd
+ */
+@Service
+public class FastDfsSysFileServiceImpl implements ISysFileService {
+    /**
+     * 域名或本机访问地址
+     */
+    @Value("${fdfs.domain}")
+    public String domain;
+
+    @Autowired
+    private FastFileStorageClient storageClient;
+
+    /**
+     * FastDfs文件上传接口
+     *
+     * @param file 上传的文件
+     * @return 访问地址
+     * @throws Exception
+     */
+    @Override
+    public String uploadFile(MultipartFile file) throws Exception {
+        StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(),
+                FilenameUtils.getExtension(file.getOriginalFilename()), null);
+        return domain + "/" + storePath.getFullPath();
+    }
+}

+ 19 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/ISysFileService.java

@@ -0,0 +1,19 @@
+package com.zd.base.files.file.service;
+
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 文件上传接口
+ *
+ * @author zd
+ */
+public interface ISysFileService {
+    /**
+     * 文件上传接口
+     *
+     * @param file 上传的文件
+     * @return 访问地址
+     * @throws Exception
+     */
+    public String uploadFile(MultipartFile file) throws Exception;
+}

+ 55 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/LocalSysFileServiceImpl.java

@@ -0,0 +1,55 @@
+package com.zd.base.files.file.service;
+
+
+import com.zd.base.files.file.utils.FileUploadUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+/**
+ * 本地文件存储
+ *
+ * @author zd
+ */
+@Primary
+@Service
+public class LocalSysFileServiceImpl implements ISysFileService {
+    /**
+     * 资源映射路径 前缀
+     */
+    @Value("${file.prefix}")
+    public String localFilePrefix;
+
+    /**
+     * 域名或本机访问地址
+     */
+    @Value("${file.domain}")
+    public String domain;
+
+    /**
+     * 上传文件存储在本地的根路径
+     */
+    @Value("${file.path}")
+    private String localFilePath;
+
+    /**
+     * 本地文件上传接口
+     *
+     * @param file 上传的文件
+     * @return 访问地址
+     * @throws Exception
+     */
+    @Override
+    public String uploadFile(MultipartFile file) throws Exception {
+        String name = FileUploadUtils.upload(localFilePath, file);
+        //TODO 去掉拼接前缀
+//        String urlPrefix = domain;
+//        String localIP = "127.0.0.1";
+//        if (urlPrefix.contains(localIP)) {
+//            String ip = InetAddress.getLocalHost().getHostAddress();;
+//            urlPrefix = urlPrefix.replace(localIP, ip);
+//        }
+        String url =localFilePrefix + name;
+        return url;
+    }
+}

+ 44 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/service/MinioSysFileServiceImpl.java

@@ -0,0 +1,44 @@
+package com.zd.base.files.file.service;
+
+
+import com.zd.base.files.file.config.MinioConfig;
+import com.zd.base.files.file.utils.FileUploadUtils;
+import io.minio.MinioClient;
+import io.minio.PutObjectArgs;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * Minio 文件存储
+ *
+ * @author zd
+ */
+@Service
+public class MinioSysFileServiceImpl implements ISysFileService {
+    @Autowired
+    private MinioConfig minioConfig;
+
+    @Autowired
+    private MinioClient client;
+
+    /**
+     * 本地文件上传接口
+     *
+     * @param file 上传的文件
+     * @return 访问地址
+     * @throws Exception
+     */
+    @Override
+    public String uploadFile(MultipartFile file) throws Exception {
+        String fileName = FileUploadUtils.extractFilename(file);
+        PutObjectArgs args = PutObjectArgs.builder()
+                .bucket(minioConfig.getBucketName())
+                .object(fileName)
+                .stream(file.getInputStream(), file.getSize(), -1)
+                .contentType(file.getContentType())
+                .build();
+        client.putObject(args);
+        return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
+    }
+}

+ 201 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/file/utils/FileUploadUtils.java

@@ -0,0 +1,201 @@
+package com.zd.base.files.file.utils;
+
+import com.zd.common.core.exception.ServiceException;
+import com.zd.common.core.exception.file.FileNameLengthLimitExceededException;
+import com.zd.common.core.exception.file.FileSizeLimitExceededException;
+import com.zd.common.core.exception.file.InvalidExtensionException;
+import com.zd.common.core.utils.DateUtils;
+import com.zd.common.core.utils.IdUtils;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.common.core.utils.file.MimeTypeUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * 文件上传工具类
+ *
+ * @author zd
+ */
+public class FileUploadUtils {
+    /**
+     * 默认大小 50M
+     */
+    public static final long DEFAULT_MAX_SIZE = 3000000 * 1024 * 1024;
+
+    /**
+     * 默认的文件名最大长度 100
+     */
+    public static final int DEFAULT_FILE_NAME_LENGTH = 100;
+
+    /**
+     * 根据文件路径上传
+     *
+     * @param baseDir 相对应用的基目录
+     * @param file    上传的文件
+     * @return 文件名称
+     * @throws IOException
+     */
+    public static final String upload(String baseDir, MultipartFile file) throws IOException {
+        try {
+            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 文件上传
+     *
+     * @param baseDir          相对应用的基目录
+     * @param file             上传的文件
+     * @param allowedExtension 上传文件类型
+     * @return 返回上传成功的文件名
+     * @throws FileSizeLimitExceededException       如果超出最大大小
+     * @throws FileNameLengthLimitExceededException 文件名太长
+     * @throws IOException                          比如读写文件出错时
+     * @throws InvalidExtensionException            文件校验异常
+     */
+    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+            InvalidExtensionException {
+        int fileNamelength = file.getOriginalFilename().length();
+        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
+//            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+            throw new ServiceException("图片名字太长,请修改名字后,从新上传!");
+        }
+
+        assertAllowed(file, allowedExtension);
+
+        String fileName = extractFilename(file);
+
+        File desc = getAbsoluteFile(baseDir, fileName);
+        file.transferTo(desc);
+        String pathFileName = getPathFileName(fileName);
+        return pathFileName;
+    }
+//
+//    /**
+//     * 文件上传 -不修改文件名
+//     *
+//     * @param baseDir          相对应用的基目录
+//     * @param file             上传的文件
+//     * @param allowedExtension 上传文件类型
+//     * @return 返回上传成功的文件名
+//     * @throws FileSizeLimitExceededException       如果超出最大大小
+//     * @throws FileNameLengthLimitExceededException 文件名太长
+//     * @throws IOException                          比如读写文件出错时
+//     * @throws InvalidExtensionException            文件校验异常
+//     */
+//    public static final String uploadNoUpdateName(String baseDir, MultipartFile file, String[] allowedExtension)
+//            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+//            InvalidExtensionException {
+//        int fileNamelength = file.getOriginalFilename().length();
+//        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
+//            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+//        }
+//
+//        assertAllowed(file, allowedExtension);
+//
+////        String fileName = extractFilename(file);
+//
+//        String originalFilename = file.getOriginalFilename();
+//        File desc = getAbsoluteFile(baseDir, originalFilename);
+//        file.transferTo(desc);
+//        String pathFileName = getPathFileName(originalFilename);
+//        return pathFileName;
+//    }
+
+    /**
+     * 编码文件名
+     */
+    public static final String extractFilename(MultipartFile file) {
+        String fileName = file.getOriginalFilename();
+        String extension = getExtension(file);
+        fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
+        return fileName;
+    }
+
+    private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
+        File desc = new File(uploadDir + File.separator + fileName);
+
+        if (!desc.exists()) {
+            if (!desc.getParentFile().exists()) {
+                desc.getParentFile().mkdirs();
+            }
+        }
+        return desc.isAbsolute() ? desc : desc.getAbsoluteFile();
+    }
+
+    private static final String getPathFileName(String fileName) throws IOException {
+        String pathFileName = "/" + fileName;
+        return pathFileName;
+    }
+
+    /**
+     * 文件大小校验
+     *
+     * @param file 上传的文件
+     * @throws FileSizeLimitExceededException 如果超出最大大小
+     * @throws InvalidExtensionException      文件校验异常
+     */
+    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, InvalidExtensionException {
+        long size = file.getSize();
+        if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) {
+            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
+        }
+
+        String fileName = file.getOriginalFilename();
+        String extension = getExtension(file);
+        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
+            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
+                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
+                        fileName);
+            } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
+                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
+                        fileName);
+            } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
+                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
+                        fileName);
+            } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
+                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
+                        fileName);
+            } else {
+                throw new InvalidExtensionException(allowedExtension, extension, fileName);
+            }
+        }
+    }
+
+    /**
+     * 判断MIME类型是否是允许的MIME类型
+     *
+     * @param extension        上传文件类型
+     * @param allowedExtension 允许上传文件类型
+     * @return true/false
+     */
+    public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
+        for (String str : allowedExtension) {
+            if (str.equalsIgnoreCase(extension)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取文件名的后缀
+     *
+     * @param file 表单文件
+     * @return 后缀名
+     */
+    public static final String getExtension(MultipartFile file) {
+        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+        if (StringUtils.isEmpty(extension)) {
+            extension = MimeTypeUtils.getExtension(file.getContentType());
+        }
+        return extension;
+    }
+}

+ 123 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/ActionEnter.java

@@ -0,0 +1,123 @@
+package com.zd.base.files.ueditor;
+
+import com.alibaba.fastjson.JSONObject;
+import com.zd.base.files.ueditor.define.ActionMap;
+import com.zd.base.files.ueditor.define.BaseState;
+import com.zd.base.files.ueditor.define.State;
+import com.zd.base.files.ueditor.hunter.FileManager;
+import com.zd.base.files.ueditor.hunter.ImageHunter;
+import com.zd.base.files.ueditor.upload.Uploader;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+public class ActionEnter {
+    private HttpServletRequest request;
+    private String rootPath;
+    private String contextPath;
+    private String actionType;
+    private String localFilePath;
+    private String localFilePrefix;
+    private String domain;
+    private ConfigManager configManager;
+
+    public ActionEnter(HttpServletRequest request, String rootPath,String localFilePath,String localFilePrefix,String domain) {
+        this.request = null;
+        this.rootPath = null;
+        this.contextPath = null;
+        this.actionType = null;
+        this.configManager = null;
+        this.localFilePath=localFilePath;
+        this.localFilePrefix=localFilePrefix;
+        this.domain=domain;
+        this.request = request;
+        this.rootPath = rootPath;
+        this.actionType = request.getParameter("action");
+        this.contextPath = request.getContextPath();
+        this.configManager = ConfigManager.getInstance(this.rootPath, this.contextPath, request.getRequestURI(),localFilePath);
+    }
+
+    public String exec() {
+        String callbackName = this.request.getParameter("callback");
+        if (callbackName == null) {
+            return this.invoke();
+        }
+        if (!this.validCallbackName(callbackName)) {
+            return new BaseState(false, 401).toJSONString();
+        }
+        return callbackName + "(" + this.invoke() + ");";
+    }
+
+    public String invoke() {
+        if (this.actionType == null || !ActionMap.mapping.containsKey(this.actionType)) {
+            return new BaseState(false, 101).toJSONString();
+        }
+        if (this.configManager == null || !this.configManager.valid()) {
+            return new BaseState(false, 102).toJSONString();
+        }
+        State state = null;
+        int actionCode = ActionMap.getType(this.actionType);
+        Map<String, Object> conf = null;
+        switch (actionCode) {
+            case 0: {
+                return this.configManager.getAllConfig().toString();
+            }
+            case 1:
+            case 2:
+            case 3:
+            case 4: {
+                conf = this.configManager.getConfig(actionCode);
+                state = new Uploader(this.request, conf).doExec();
+                String localIP = "127.0.0.1";
+                String urlPrefix=this.domain;
+                String jsonStr=state.toJSONString();
+                JSONObject jsonObject=JSONObject.parseObject(jsonStr);
+                String url=getIpUrl(localIP, urlPrefix, localFilePrefix);
+                state.putInfo("url",url+jsonObject.getString("url"));
+                break;
+            }
+            case 5: {
+                conf = this.configManager.getConfig(actionCode);
+                String[] list = this.request.getParameterValues((String) conf.get("fieldName"));
+                state = new ImageHunter(conf).capture(list);
+                break;
+            }
+            case 6:
+            case 7: {
+                conf = this.configManager.getConfig(actionCode);
+                conf.put("domain",this.domain);
+                conf.put("localFilePrefix",this.localFilePrefix);
+                int start = this.getStartIndex();
+                state = new FileManager(conf).listFile(start);
+                break;
+            }
+        }
+        return state.toJSONString();
+    }
+
+    public static String getIpUrl(String localIP, String urlPrefix, String localFilePrefix) {
+//        if (urlPrefix.contains(localIP)) {
+//            String ip= null;
+//            try {
+//                ip = InetAddress.getLocalHost().getHostAddress();
+//            } catch (UnknownHostException e) {
+//                ip="";
+//                e.printStackTrace();
+//            }
+//            urlPrefix = urlPrefix.replace(localIP, ip);
+//        }
+        return localFilePrefix;
+    }
+
+    public int getStartIndex() {
+        String start = this.request.getParameter("start");
+        try {
+            return Integer.parseInt(start);
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+
+    public boolean validCallbackName(String name) {
+        return name.matches("^[a-zA-Z_]+[\\w0-9_]*$");
+    }
+}

+ 160 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/ConfigManager.java

@@ -0,0 +1,160 @@
+package com.zd.base.files.ueditor;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ConfigManager {
+    private String rootPath;
+    private String originalPath;
+    private String contextPath;
+    private String realPath;
+    private static String configFileName = "config.json";
+    private String parentPath;
+    private JSONObject jsonConfig;
+    private static String SCRAWL_FILE_NAME = "scrawl";
+    private static String REMOTE_FILE_NAME = "remote";
+
+    private ConfigManager(String rootPath, String contextPath, String uri,String realPath) throws FileNotFoundException, IOException {
+        this.parentPath = null;
+        this.jsonConfig = null;
+        rootPath = rootPath.replace("\\", "/");
+        this.rootPath = rootPath;
+        this.realPath = realPath;
+        this.contextPath = contextPath;
+        if (contextPath.length() > 0) {
+            this.originalPath = this.rootPath + uri.substring(contextPath.length());
+        } else {
+            this.originalPath = this.rootPath + uri;
+        }
+        this.initEnv();
+    }
+
+    public static ConfigManager getInstance(String rootPath, String contextPath, String uri,String realPath) {
+        try {
+            return new ConfigManager(rootPath, contextPath, uri,realPath);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public boolean valid() {
+        return this.jsonConfig != null;
+    }
+
+    public JSONObject getAllConfig() {
+        return this.jsonConfig;
+    }
+
+    public Map<String, Object> getConfig(int type) {
+        Map<String, Object> conf = new HashMap<String, Object>();
+        String savePath = null;
+        switch (type) {
+            case 4: {
+                conf.put("isBase64", "false");
+                conf.put("maxSize", this.jsonConfig.getLong("fileMaxSize"));
+                conf.put("allowFiles", this.getArray("fileAllowFiles"));
+                conf.put("fieldName", this.jsonConfig.getString("fileFieldName"));
+                savePath = this.jsonConfig.getString("filePathFormat");
+                break;
+            }
+            case 1: {
+                conf.put("isBase64", "false");
+                conf.put("maxSize", this.jsonConfig.getLong("imageMaxSize"));
+                conf.put("allowFiles", this.getArray("imageAllowFiles"));
+                conf.put("fieldName", this.jsonConfig.getString("imageFieldName"));
+                savePath = this.jsonConfig.getString("imagePathFormat");
+                break;
+            }
+            case 3: {
+                conf.put("maxSize", this.jsonConfig.getLong("videoMaxSize"));
+                conf.put("allowFiles", this.getArray("videoAllowFiles"));
+                conf.put("fieldName", this.jsonConfig.getString("videoFieldName"));
+                savePath = this.jsonConfig.getString("videoPathFormat");
+                break;
+            }
+            case 2: {
+                conf.put("filename", "scrawl");
+                conf.put("maxSize", this.jsonConfig.getLong("scrawlMaxSize"));
+                conf.put("fieldName", this.jsonConfig.getString("scrawlFieldName"));
+                conf.put("isBase64", "true");
+                savePath = this.jsonConfig.getString("scrawlPathFormat");
+                break;
+            }
+            case 5: {
+                conf.put("filename", "remote");
+                conf.put("filter", this.getArray("catcherLocalDomain"));
+                conf.put("maxSize", this.jsonConfig.getLong("catcherMaxSize"));
+                conf.put("allowFiles", this.getArray("catcherAllowFiles"));
+                conf.put("fieldName", String.valueOf(this.jsonConfig.getString("catcherFieldName")) + "[]");
+                savePath = this.jsonConfig.getString("catcherPathFormat");
+                break;
+            }
+            case 7: {
+                conf.put("allowFiles", this.getArray("imageManagerAllowFiles"));
+                conf.put("dir", this.jsonConfig.getString("imageManagerListPath"));
+                conf.put("count", this.jsonConfig.getInt("imageManagerListSize"));
+                break;
+            }
+            case 6: {
+                conf.put("allowFiles", this.getArray("fileManagerAllowFiles"));
+                conf.put("dir", this.jsonConfig.getString("fileManagerListPath"));
+                conf.put("count", this.jsonConfig.getInt("fileManagerListSize"));
+                break;
+            }
+        }
+        conf.put("savePath", savePath);
+        conf.put("rootPath", this.realPath);
+        return conf;
+    }
+
+    private void initEnv() throws FileNotFoundException, IOException {
+        File file = new File(this.originalPath);
+        if (!file.isAbsolute()) {
+            file = new File(file.getAbsolutePath());
+        }
+        this.parentPath = file.getParent();
+        String configContent = this.readFile(this.getConfigPath());
+        try {
+            JSONObject jsonConfig = new JSONObject(configContent);
+            this.jsonConfig = jsonConfig;
+        } catch (Exception e) {
+            this.jsonConfig = null;
+        }
+    }
+
+    private String getConfigPath() {
+        return String.valueOf(this.parentPath) + File.separator + "config.json";
+    }
+
+    private String[] getArray(String key) {
+        JSONArray jsonArray = this.jsonConfig.getJSONArray(key);
+        String[] result = new String[jsonArray.length()];
+        for (int i = 0, len = jsonArray.length(); i < len; ++i) {
+            result[i] = jsonArray.getString(i);
+        }
+        return result;
+    }
+
+    private String readFile(String path) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        try {
+            InputStreamReader reader = new InputStreamReader(new FileInputStream(path), "UTF-8");
+            BufferedReader bfReader = new BufferedReader(reader);
+            String tmpContent = null;
+            while ((tmpContent = bfReader.readLine()) != null) {
+                builder.append(tmpContent);
+            }
+            bfReader.close();
+        } catch (UnsupportedEncodingException ex) {
+        }
+        return this.filter(builder.toString());
+    }
+
+    private String filter(String input) {
+        return input.replaceAll("/\\*[\\s\\S]*?\\*/", "");
+    }
+}

+ 18 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/Encoder.java

@@ -0,0 +1,18 @@
+package com.zd.base.files.ueditor;
+
+public class Encoder {
+    public static String toUnicode(String input) {
+        StringBuilder builder = new StringBuilder();
+        char[] chars = input.toCharArray();
+        char[] array;
+        for (int length = (array = chars).length, i = 0; i < length; ++i) {
+            char ch = array[i];
+            if (ch < '\u0100') {
+                builder.append(ch);
+            } else {
+                builder.append("\\u" + Integer.toHexString(ch & '\uffff'));
+            }
+        }
+        return builder.toString();
+    }
+}

+ 132 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/PathFormat.java

@@ -0,0 +1,132 @@
+package com.zd.base.files.ueditor;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PathFormat {
+    private static String TIME = "time";
+    private static String FULL_YEAR = "yyyy";
+    private static String YEAR = "yy";
+    private static String MONTH = "mm";
+    private static String DAY = "dd";
+    private static String HOUR = "hh";
+    private static String MINUTE = "ii";
+    private static String SECOND = "ss";
+    private static String RAND = "rand";
+    private static Date currentDate;
+
+    static {
+        PathFormat.currentDate = null;
+    }
+
+    public static String parse(String input) {
+        Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}", 2);
+        Matcher matcher = pattern.matcher(input);
+        PathFormat.currentDate = new Date();
+        StringBuffer sb = new StringBuffer();
+        while (matcher.find()) {
+            matcher.appendReplacement(sb, getString(matcher.group(1)));
+        }
+        matcher.appendTail(sb);
+        return sb.toString();
+    }
+
+    public static String format(String input) {
+        return input.replace("\\", "/");
+    }
+
+    public static String parse(String input, String filename) {
+        Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}", 2);
+        Matcher matcher = pattern.matcher(input);
+        String matchStr = null;
+        PathFormat.currentDate = new Date();
+        StringBuffer sb = new StringBuffer();
+        while (matcher.find()) {
+            matchStr = matcher.group(1);
+            if (matchStr.indexOf("filename") != -1) {
+                filename = filename.replace("$", "\\$").replaceAll("[\\/:*?\"<>|]", "");
+                matcher.appendReplacement(sb, filename);
+            } else {
+                matcher.appendReplacement(sb, getString(matchStr));
+            }
+        }
+        matcher.appendTail(sb);
+        return sb.toString();
+    }
+
+    private static String getString(String pattern) {
+        pattern = pattern.toLowerCase();
+        if (pattern.indexOf("time") != -1) {
+            return getTimestamp();
+        }
+        if (pattern.indexOf("yyyy") != -1) {
+            return getFullYear();
+        }
+        if (pattern.indexOf("yy") != -1) {
+            return getYear();
+        }
+        if (pattern.indexOf("mm") != -1) {
+            return getMonth();
+        }
+        if (pattern.indexOf("dd") != -1) {
+            return getDay();
+        }
+        if (pattern.indexOf("hh") != -1) {
+            return getHour();
+        }
+        if (pattern.indexOf("ii") != -1) {
+            return getMinute();
+        }
+        if (pattern.indexOf("ss") != -1) {
+            return getSecond();
+        }
+        if (pattern.indexOf("rand") != -1) {
+            return getRandom(pattern);
+        }
+        return pattern;
+    }
+
+    private static String getTimestamp() {
+        return new StringBuilder(String.valueOf(System.currentTimeMillis())).toString();
+    }
+
+    private static String getFullYear() {
+        return new SimpleDateFormat("yyyy").format(PathFormat.currentDate);
+    }
+
+    private static String getYear() {
+        return new SimpleDateFormat("yy").format(PathFormat.currentDate);
+    }
+
+    private static String getMonth() {
+        return new SimpleDateFormat("MM").format(PathFormat.currentDate);
+    }
+
+    private static String getDay() {
+        return new SimpleDateFormat("dd").format(PathFormat.currentDate);
+    }
+
+    private static String getHour() {
+        return new SimpleDateFormat("HH").format(PathFormat.currentDate);
+    }
+
+    private static String getMinute() {
+        return new SimpleDateFormat("mm").format(PathFormat.currentDate);
+    }
+
+    private static String getSecond() {
+        return new SimpleDateFormat("ss").format(PathFormat.currentDate);
+    }
+
+    private static String getRandom(String pattern) {
+        int length = 0;
+        pattern = pattern.split(":")[1].trim();
+        length = Integer.parseInt(pattern);
+        return new StringBuilder(String.valueOf(Math.random())).toString().replace(".", "").substring(0, length);
+    }
+
+    public static void main(String[] args) {
+    }
+}

+ 23 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/SpringUtil.java

@@ -0,0 +1,23 @@
+package com.zd.base.files.ueditor;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SpringUtil implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        if (SpringUtil.applicationContext == null) {
+            SpringUtil.applicationContext = applicationContext;
+        }
+    }
+
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+}

+ 56 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/contorller/UeditorController.java

@@ -0,0 +1,56 @@
+package com.zd.base.files.ueditor.contorller;
+
+import com.alibaba.fastjson.JSONException;
+import com.zd.base.files.ueditor.ActionEnter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * 用于处理关于ueditor插件相关的请求
+ *
+ * @date 2017-11-29
+ */
+@Controller
+public class UeditorController {
+
+    @Value("${file.config}")
+    private String rootConfig;
+
+    /**
+     * 上传文件存储在本地的根路径
+     */
+    @Value("${file.path}")
+    private String localFilePath;
+
+    /**
+     * 资源映射路径 前缀
+     */
+    @Value("${file.prefix}")
+    public String localFilePrefix;
+
+
+    @Value("${file.domain}")
+    public String domain;
+
+
+    @RequestMapping("exec")
+    public void getConfigInfo(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            request.setCharacterEncoding("utf-8");
+            response.setContentType("application/json");
+            String exec = new ActionEnter(request, rootConfig,localFilePath,localFilePrefix,domain).exec();
+            PrintWriter writer = response.getWriter();
+            writer.write(exec);
+            writer.flush();
+            writer.close();
+        } catch (IOException | JSONException e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 35 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/ActionMap.java

@@ -0,0 +1,35 @@
+package com.zd.base.files.ueditor.define;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class ActionMap {
+    public static final Map<String, Integer> mapping;
+    public static final int CONFIG = 0;
+    public static final int UPLOAD_IMAGE = 1;
+    public static final int UPLOAD_SCRAWL = 2;
+    public static final int UPLOAD_VIDEO = 3;
+    public static final int UPLOAD_FILE = 4;
+    public static final int CATCH_IMAGE = 5;
+    public static final int LIST_FILE = 6;
+    public static final int LIST_IMAGE = 7;
+
+    static {
+        mapping = new HashMap<String, Integer>() {
+            {
+                this.put("config", 0);
+                this.put("uploadimage", 1);
+                this.put("uploadscrawl", 2);
+                this.put("uploadvideo", 3);
+                this.put("uploadfile", 4);
+                this.put("catchimage", 5);
+                this.put("listfile", 6);
+                this.put("listimage", 7);
+            }
+        };
+    }
+
+    public static int getType(final String key) {
+        return ActionMap.mapping.get(key);
+    }
+}

+ 8 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/ActionState.java

@@ -0,0 +1,8 @@
+package com.zd.base.files.ueditor.define;
+
+public enum ActionState {
+    UNKNOW_ERROR("UNKNOW_ERROR", 0);
+
+    private ActionState(String s,int n) {
+    }
+}

+ 53 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/AppInfo.java

@@ -0,0 +1,53 @@
+package com.zd.base.files.ueditor.define;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class AppInfo {
+    public static final int SUCCESS = 0;
+    public static final int MAX_SIZE = 1;
+    public static final int PERMISSION_DENIED = 2;
+    public static final int FAILED_CREATE_FILE = 3;
+    public static final int IO_ERROR = 4;
+    public static final int NOT_MULTIPART_CONTENT = 5;
+    public static final int PARSE_REQUEST_ERROR = 6;
+    public static final int NOTFOUND_UPLOAD_DATA = 7;
+    public static final int NOT_ALLOW_FILE_TYPE = 8;
+    public static final int INVALID_ACTION = 101;
+    public static final int CONFIG_ERROR = 102;
+    public static final int PREVENT_HOST = 201;
+    public static final int CONNECTION_ERROR = 202;
+    public static final int REMOTE_FAIL = 203;
+    public static final int NOT_DIRECTORY = 301;
+    public static final int NOT_EXIST = 302;
+    public static final int ILLEGAL = 401;
+    public static Map<Integer, String> info;
+
+    static {
+        AppInfo.info = new HashMap<Integer, String>() {
+            {
+                this.put(0, "SUCCESS");
+                this.put(101, "\u65e0\u6548\u7684Action");
+                this.put(102, "\u914d\u7f6e\u6587\u4ef6\u521d\u59cb\u5316\u5931\u8d25");
+                this.put(203, "\u6293\u53d6\u8fdc\u7a0b\u56fe\u7247\u5931\u8d25");
+                this.put(201, "\u88ab\u963b\u6b62\u7684\u8fdc\u7a0b\u4e3b\u673a");
+                this.put(202, "\u8fdc\u7a0b\u8fde\u63a5\u51fa\u9519");
+                this.put(1, "\u6587\u4ef6\u5927\u5c0f\u8d85\u51fa\u9650\u5236");
+                this.put(2, "\u6743\u9650\u4e0d\u8db3");
+                this.put(3, "\u521b\u5efa\u6587\u4ef6\u5931\u8d25");
+                this.put(4, "IO\u9519\u8bef");
+                this.put(5, "\u4e0a\u4f20\u8868\u5355\u4e0d\u662fmultipart/form-data\u7c7b\u578b");
+                this.put(6, "\u89e3\u6790\u4e0a\u4f20\u8868\u5355\u9519\u8bef");
+                this.put(7, "\u672a\u627e\u5230\u4e0a\u4f20\u6570\u636e");
+                this.put(8, "\u4e0d\u5141\u8bb8\u7684\u6587\u4ef6\u7c7b\u578b");
+                this.put(301, "\u6307\u5b9a\u8def\u5f84\u4e0d\u662f\u76ee\u5f55");
+                this.put(302, "\u6307\u5b9a\u8def\u5f84\u5e76\u4e0d\u5b58\u5728");
+                this.put(401, "Callback\u53c2\u6570\u540d\u4e0d\u5408\u6cd5");
+            }
+        };
+    }
+
+    public static String getStateInfo(final int key) {
+        return AppInfo.info.get(key);
+    }
+}

+ 89 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/BaseState.java

@@ -0,0 +1,89 @@
+package com.zd.base.files.ueditor.define;
+
+import com.zd.base.files.ueditor.Encoder;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class BaseState implements State {
+    private boolean state;
+    private String info;
+    private Map<String, String> infoMap;
+
+    public BaseState() {
+        this.state = false;
+        this.info = null;
+        this.infoMap = new HashMap<String, String>();
+        this.state = true;
+    }
+
+    public BaseState(final boolean state) {
+        this.state = false;
+        this.info = null;
+        this.infoMap = new HashMap<String, String>();
+        this.setState(state);
+    }
+
+    public BaseState(final boolean state, final String info) {
+        this.state = false;
+        this.info = null;
+        this.infoMap = new HashMap<String, String>();
+        this.setState(state);
+        this.info = info;
+    }
+
+    public BaseState(final boolean state, final int infoCode) {
+        this.state = false;
+        this.info = null;
+        this.infoMap = new HashMap<String, String>();
+        this.setState(state);
+        this.info = AppInfo.getStateInfo(infoCode);
+    }
+
+    @Override
+    public boolean isSuccess() {
+        return this.state;
+    }
+
+    public void setState(final boolean state) {
+        this.state = state;
+    }
+
+    public void setInfo(final String info) {
+        this.info = info;
+    }
+
+    public void setInfo(final int infoCode) {
+        this.info = AppInfo.getStateInfo(infoCode);
+    }
+
+    @Override
+    public String toJSONString() {
+        return this.toString();
+    }
+
+    @Override
+    public String toString() {
+        String key = null;
+        final String stateVal = this.isSuccess() ? AppInfo.getStateInfo(0) : this.info;
+        final StringBuilder builder = new StringBuilder();
+        builder.append("{\"state\": \"" + stateVal + "\"");
+        final Iterator<String> iterator = this.infoMap.keySet().iterator();
+        while (iterator.hasNext()) {
+            key = iterator.next();
+            builder.append(",\"" + key + "\": \"" + this.infoMap.get(key) + "\"");
+        }
+        builder.append("}");
+        return Encoder.toUnicode(builder.toString());
+    }
+
+    @Override
+    public void putInfo(final String name, final String val) {
+        this.infoMap.put(name, val);
+    }
+
+    @Override
+    public void putInfo(final String name, final long val) {
+        this.putInfo(name, new StringBuilder(String.valueOf(val)).toString());
+    }
+}

+ 25 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/FileType.java

@@ -0,0 +1,25 @@
+package com.zd.base.files.ueditor.define;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class FileType {
+    public static final String JPG = "JPG";
+    private static final Map<String, String> types;
+
+    static {
+        types = new HashMap<String, String>() {
+            {
+                this.put("JPG", ".jpg");
+            }
+        };
+    }
+
+    public static String getSuffix(final String key) {
+        return FileType.types.get(key);
+    }
+
+    public static String getSuffixByFilename(final String filename) {
+        return filename.substring(filename.lastIndexOf(".")).toLowerCase();
+    }
+}

+ 24 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/MIMEType.java

@@ -0,0 +1,24 @@
+package com.zd.base.files.ueditor.define;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MIMEType {
+    public static final Map<String, String> types;
+
+    static {
+        types = new HashMap<String, String>() {
+            {
+                this.put("image/gif", ".gif");
+                this.put("image/jpeg", ".jpg");
+                this.put("image/jpg", ".jpg");
+                this.put("image/png", ".png");
+                this.put("image/bmp", ".bmp");
+            }
+        };
+    }
+
+    public static String getSuffix(final String mime) {
+        return MIMEType.types.get(mime);
+    }
+}

+ 87 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/MultiState.java

@@ -0,0 +1,87 @@
+package com.zd.base.files.ueditor.define;
+
+import com.zd.base.files.ueditor.Encoder;
+import java.util.*;
+
+public class MultiState implements State {
+    private boolean state;
+    private String info;
+    private Map<String, Long> intMap;
+    private Map<String, String> infoMap;
+    private List<String> stateList;
+
+    public MultiState(final boolean state) {
+        this.state = false;
+        this.info = null;
+        this.intMap = new HashMap<String, Long>();
+        this.infoMap = new HashMap<String, String>();
+        this.stateList = new ArrayList<String>();
+        this.state = state;
+    }
+
+    public MultiState(final boolean state, final String info) {
+        this.state = false;
+        this.info = null;
+        this.intMap = new HashMap<String, Long>();
+        this.infoMap = new HashMap<String, String>();
+        this.stateList = new ArrayList<String>();
+        this.state = state;
+        this.info = info;
+    }
+
+    public MultiState(final boolean state, final int infoKey) {
+        this.state = false;
+        this.info = null;
+        this.intMap = new HashMap<String, Long>();
+        this.infoMap = new HashMap<String, String>();
+        this.stateList = new ArrayList<String>();
+        this.state = state;
+        this.info = AppInfo.getStateInfo(infoKey);
+    }
+
+    @Override
+    public boolean isSuccess() {
+        return this.state;
+    }
+
+    public void addState(final State state) {
+        this.stateList.add(state.toJSONString());
+    }
+
+    @Override
+    public void putInfo(final String name, final String val) {
+        this.infoMap.put(name, val);
+    }
+
+    @Override
+    public String toJSONString() {
+        String stateVal = this.isSuccess() ? AppInfo.getStateInfo(0) : this.info;
+        final StringBuilder builder = new StringBuilder();
+        builder.append("{\"state\": \"" + stateVal + "\"");
+        Iterator<String> iterator = this.intMap.keySet().iterator();
+        while (iterator.hasNext()) {
+            stateVal = iterator.next();
+            builder.append(",\"" + stateVal + "\": " + this.intMap.get(stateVal));
+        }
+        iterator = this.infoMap.keySet().iterator();
+        while (iterator.hasNext()) {
+            stateVal = iterator.next();
+            builder.append(",\"" + stateVal + "\": \"" + this.infoMap.get(stateVal) + "\"");
+        }
+        builder.append(", list: [");
+        iterator = this.stateList.iterator();
+        while (iterator.hasNext()) {
+            builder.append(String.valueOf(iterator.next()) + ",");
+        }
+        if (this.stateList.size() > 0) {
+            builder.deleteCharAt(builder.length() - 1);
+        }
+        builder.append(" ]}");
+        return Encoder.toUnicode(builder.toString());
+    }
+
+    @Override
+    public void putInfo(final String name, final long val) {
+        this.intMap.put(name, val);
+    }
+}

+ 11 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/define/State.java

@@ -0,0 +1,11 @@
+package com.zd.base.files.ueditor.define;
+
+public interface State {
+    boolean isSuccess();
+
+    void putInfo(String p0,String p1);
+
+    void putInfo(String p0,long p1);
+
+    String toJSONString();
+}

+ 101 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/hunter/FileManager.java

@@ -0,0 +1,101 @@
+package com.zd.base.files.ueditor.hunter;
+
+import com.zd.base.files.ueditor.ActionEnter;
+import com.zd.base.files.ueditor.define.BaseState;
+import com.zd.base.files.ueditor.define.MultiState;
+import com.zd.base.files.ueditor.define.State;
+import org.apache.commons.io.FileUtils;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+
+public class FileManager {
+    private String dir;
+    private String rootPath;
+    private String[] allowFiles;
+    private int count;
+    private String domain;
+    private String localFilePrefix;
+
+    public FileManager(Map<String, Object> conf) {
+        this.dir = null;
+        this.rootPath = null;
+        this.allowFiles = null;
+        this.count = 0;
+        this.domain=String.valueOf(conf.get("domain"));
+        this.localFilePrefix=String.valueOf(conf.get("localFilePrefix"));
+        this.rootPath = String.valueOf(conf.get("rootPath"));
+        this.dir = String.valueOf(this.rootPath) + conf.get("dir");
+        this.allowFiles = this.getAllowFiles(conf.get("allowFiles"));
+        this.count = Integer.parseInt(conf.get("count") + "");
+    }
+
+    public State listFile(int index) {
+        File dir = new File(this.dir);
+        State state = null;
+        if (!dir.exists()) {
+            return new BaseState(false, 302);
+        }
+        if (!dir.isDirectory()) {
+            return new BaseState(false, 301);
+        }
+        Collection<File> list = (Collection<File>) FileUtils.listFiles(dir, this.allowFiles, true);
+        if (index < 0 || index > list.size()) {
+            state = new MultiState(true);
+        } else {
+            Object[] fileList = Arrays.copyOfRange(list.toArray(), index, index + this.count);
+            state = this.getState(fileList);
+        }
+        state.putInfo("start", index);
+        state.putInfo("total", list.size());
+        return state;
+    }
+
+    private State getState(Object[] files) {
+        MultiState state = new MultiState(true);
+        BaseState fileState = null;
+        File file = null;
+        for (Object obj : files) {
+            if (obj == null) {
+                break;
+            }
+            file = (File) obj;
+            String realPath = file.getAbsolutePath();
+            String oSystem=System.getProperty("os.name").toLowerCase();
+            if(oSystem.equals("windows")){
+                String changePath=rootPath.replace("/","\\");
+                realPath=realPath.replace(changePath,"/").replace("\\","/");
+            }else{
+                realPath=realPath.replace(rootPath,"/");
+            }
+            String urlPrefix=this.domain;
+            String localIP="127.0.0.1";
+            String url=ActionEnter.getIpUrl(localIP, urlPrefix, localFilePrefix);
+            fileState = new BaseState(true);
+            fileState.putInfo("url", url+realPath);
+            state.addState(fileState);
+        }
+        return state;
+    }
+
+    private String getPath(File file) {
+        String path = file.getAbsolutePath();
+        return path.replace(this.rootPath, "/");
+    }
+
+    private String[] getAllowFiles(Object fileExt) {
+        String[] exts = null;
+        String ext = null;
+        if (fileExt == null) {
+            return new String[0];
+        }
+        exts = (String[]) fileExt;
+        for (int i = 0, len = exts.length; i < len; ++i) {
+            ext = exts[i];
+            exts[i] = ext.replace(".", "");
+        }
+        return exts;
+    }
+}

+ 110 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/hunter/ImageHunter.java

@@ -0,0 +1,110 @@
+package com.zd.base.files.ueditor.hunter;
+
+import com.zd.base.files.ueditor.PathFormat;
+import com.zd.base.files.ueditor.define.BaseState;
+import com.zd.base.files.ueditor.define.MIMEType;
+import com.zd.base.files.ueditor.define.MultiState;
+import com.zd.base.files.ueditor.define.State;
+import com.zd.base.files.ueditor.upload.StorageManager;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class ImageHunter {
+    private String filename;
+    private String savePath;
+    private String rootPath;
+    private List<String> allowTypes;
+    private long maxSize;
+    private List<String> filters;
+
+    public ImageHunter(Map<String, Object> conf) {
+        this.filename = null;
+        this.savePath = null;
+        this.rootPath = null;
+        this.allowTypes = null;
+        this.maxSize = -1L;
+        this.filters = null;
+        this.filename = String.valueOf(conf.get("filename"));
+        this.savePath = String.valueOf(conf.get("savePath"));
+        this.rootPath = String.valueOf(conf.get("rootPath"));
+        this.maxSize = Long.parseLong(conf.get("maxSize") + "");
+        this.allowTypes = Arrays.asList((String[]) conf.get("allowFiles"));
+        this.filters = Arrays.asList((String[]) conf.get("filter"));
+    }
+
+    public State capture(String[] list) {
+        MultiState state = new MultiState(true);
+        for (String source : list) {
+            state.addState(this.captureRemoteData(source));
+        }
+        return state;
+    }
+
+    public State captureRemoteData(String urlStr) {
+        HttpURLConnection connection = null;
+        URL url = null;
+        String suffix = null;
+        try {
+            url = new URL(urlStr);
+            if (!this.validHost(url.getHost())) {
+                return new BaseState(false, 201);
+            }
+            connection = (HttpURLConnection) url.openConnection();
+            connection.setInstanceFollowRedirects(true);
+            connection.setUseCaches(true);
+            if (!this.validContentState(connection.getResponseCode())) {
+                return new BaseState(false, 202);
+            }
+            suffix = MIMEType.getSuffix(connection.getContentType());
+            if (!this.validFileType(suffix)) {
+                return new BaseState(false, 8);
+            }
+            if (!this.validFileSize(connection.getContentLength())) {
+                return new BaseState(false, 1);
+            }
+            String savePath = this.getPath(this.savePath, this.filename, suffix);
+            String physicalPath = this.rootPath + savePath;
+            State state = StorageManager.saveFileByInputStream(connection.getInputStream(), physicalPath);
+            if (state.isSuccess()) {
+                state.putInfo("url", PathFormat.format(savePath));
+                state.putInfo("source", urlStr);
+            }
+            return state;
+        } catch (Exception e) {
+            return new BaseState(false, 203);
+        }
+    }
+
+    private String getPath(String savePath, String filename, String suffix) {
+        return PathFormat.parse(savePath + suffix, filename);
+    }
+
+    private boolean validHost(String hostname) {
+        try {
+            InetAddress ip = InetAddress.getByName(hostname);
+            if (ip.isSiteLocalAddress()) {
+                return false;
+            }
+        } catch (UnknownHostException e) {
+            return false;
+        }
+        return !this.filters.contains(hostname);
+    }
+
+    private boolean validContentState(int code) {
+        return 200 == code;
+    }
+
+    private boolean validFileType(String type) {
+        return this.allowTypes.contains(type);
+    }
+
+    private boolean validFileSize(int size) {
+        return size < this.maxSize;
+    }
+}

+ 38 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/Base64Uploader.java

@@ -0,0 +1,38 @@
+package com.zd.base.files.ueditor.upload;
+
+import com.zd.base.files.ueditor.PathFormat;
+import com.zd.base.files.ueditor.define.BaseState;
+import com.zd.base.files.ueditor.define.FileType;
+import com.zd.base.files.ueditor.define.State;
+import org.apache.commons.codec.binary.Base64;
+
+import java.util.Map;
+
+public class Base64Uploader {
+    public static State save(String content, Map<String, Object> conf) {
+        byte[] data = decode(content);
+        long maxSize = Long.parseLong(conf.get("maxSize") + "");
+        if (!validSize(data, maxSize)) {
+            return new BaseState(false, 1);
+        }
+        String suffix = FileType.getSuffix("JPG");
+        String savePath = PathFormat.parse(String.valueOf(conf.get("savePath")), String.valueOf(conf.get("filename")));
+        savePath = savePath + suffix;
+        String physicalPath = conf.get("rootPath") + savePath;
+        State storageState = StorageManager.saveBinaryFile(data, physicalPath);
+        if (storageState.isSuccess()) {
+            storageState.putInfo("url", PathFormat.format(savePath));
+            storageState.putInfo("type", suffix);
+            storageState.putInfo("original", "");
+        }
+        return storageState;
+    }
+
+    private static byte[] decode(String content) {
+        return Base64.decodeBase64(content);
+    }
+
+    private static boolean validSize(byte[] data, long length) {
+        return data.length <= length;
+    }
+}

+ 65 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/BinaryUploader.java

@@ -0,0 +1,65 @@
+package com.zd.base.files.ueditor.upload;
+
+import com.zd.base.files.ueditor.PathFormat;
+import com.zd.base.files.ueditor.define.BaseState;
+import com.zd.base.files.ueditor.define.FileType;
+import com.zd.base.files.ueditor.define.State;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class BinaryUploader {
+
+    public static State save(HttpServletRequest request, Map<String, Object> conf) {
+        boolean isAjaxUpload = request.getHeader("X_Requested_With") != null;
+        if (!ServletFileUpload.isMultipartContent(request)) {
+            return new BaseState(false, 5);
+        }
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        if (isAjaxUpload) {
+            upload.setHeaderEncoding("UTF-8");
+        }
+        try {
+            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
+            MultipartFile file = multipartRequest.getFile("upfile");
+            if (file == null) {
+                return new BaseState(false, 7);
+            }
+            String savePath = String.valueOf(conf.get("savePath"));
+            String originFileName = file.getOriginalFilename();
+            String suffix = FileType.getSuffixByFilename(originFileName);
+            originFileName = originFileName.substring(0, originFileName.length() - suffix.length());
+            savePath = savePath + suffix;
+            long maxSize = Long.parseLong(conf.get("maxSize") + "");
+            if (!validType(suffix, (String[]) conf.get("allowFiles"))) {
+                return new BaseState(false, 8);
+            }
+            savePath = PathFormat.parse(savePath, originFileName);
+            String physicalPath = conf.get("rootPath") + savePath;
+            InputStream is = file.getInputStream();
+            State storageState = StorageManager.saveFileByInputStream(is, physicalPath, maxSize);
+            is.close();
+            if (storageState.isSuccess()) {
+                storageState.putInfo("url",PathFormat.format(savePath));
+                storageState.putInfo("type", suffix);
+                storageState.putInfo("original", originFileName + suffix);
+            }
+            return storageState;
+        } catch (IOException ex) {
+            return new BaseState(false, 4);
+        }
+    }
+
+    private static boolean validType(String type, String[] allowTypes) {
+        List<String> list = Arrays.asList(allowTypes);
+        return list.contains(type);
+    }
+}

+ 120 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/StorageManager.java

@@ -0,0 +1,120 @@
+package com.zd.base.files.ueditor.upload;
+
+import com.zd.base.files.ueditor.define.BaseState;
+import com.zd.base.files.ueditor.define.State;
+import org.apache.commons.io.FileUtils;
+
+import java.io.*;
+
+public class StorageManager
+{
+    public static final int BUFFER_SIZE = 8192;
+
+    public static State saveBinaryFile( byte[] data,  String path) {
+         File file = new File(path);
+        State state = valid(file);
+        if (!state.isSuccess()) {
+            return state;
+        }
+        try {
+             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
+            bos.write(data);
+            bos.flush();
+            bos.close();
+        }
+        catch (IOException ioe) {
+            return new BaseState(false, 4);
+        }
+        state = new BaseState(true, file.getAbsolutePath());
+        state.putInfo("size", data.length);
+        state.putInfo("title", file.getName());
+        return state;
+    }
+
+    public static State saveFileByInputStream( InputStream is,  String path,  long maxSize) {
+        State state = null;
+         File tmpFile = getTmpFile();
+         byte[] dataBuf = new byte[2048];
+         BufferedInputStream bis = new BufferedInputStream(is, 8192);
+        try {
+             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmpFile), 8192);
+            int count = 0;
+            while ((count = bis.read(dataBuf)) != -1) {
+                bos.write(dataBuf, 0, count);
+            }
+            bos.flush();
+            bos.close();
+            if (tmpFile.length() > maxSize) {
+                tmpFile.delete();
+                return new BaseState(false, 1);
+            }
+            state = saveTmpFile(tmpFile, path);
+            if (!state.isSuccess()) {
+                tmpFile.delete();
+            }
+            return state;
+        }
+        catch (IOException ex) {
+            return new BaseState(false, 4);
+        }
+    }
+
+    public static State saveFileByInputStream( InputStream is,  String path) {
+        State state = null;
+         File tmpFile = getTmpFile();
+         byte[] dataBuf = new byte[2048];
+         BufferedInputStream bis = new BufferedInputStream(is, 8192);
+        try {
+             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmpFile), 8192);
+            int count = 0;
+            while ((count = bis.read(dataBuf)) != -1) {
+                bos.write(dataBuf, 0, count);
+            }
+            bos.flush();
+            bos.close();
+            state = saveTmpFile(tmpFile, path);
+            if (!state.isSuccess()) {
+                tmpFile.delete();
+            }
+            return state;
+        }
+        catch (IOException ex) {
+            return new BaseState(false, 4);
+        }
+    }
+
+    private static File getTmpFile() {
+         File tmpDir = FileUtils.getTempDirectory();
+         String tmpFileName = new StringBuilder(String.valueOf(Math.random() * 10000.0)).toString().replace(".", "");
+        return new File(tmpDir, tmpFileName);
+    }
+
+    private static State saveTmpFile( File tmpFile,  String path) {
+        State state = null;
+         File targetFile = new File(path);
+        if (targetFile.canWrite()) {
+            return new BaseState(false, 2);
+        }
+        try {
+            FileUtils.moveFile(tmpFile, targetFile);
+        }
+        catch (IOException e) {
+            return new BaseState(false, 4);
+        }
+        state = new BaseState(true);
+        state.putInfo("size", targetFile.length());
+        state.putInfo("title", targetFile.getName());
+        return state;
+    }
+
+    private static State valid( File file) {
+        File parentPath = file.getParentFile();
+        if (!parentPath.exists() && !parentPath.mkdirs()) {
+            return new BaseState(false, 3);
+        }
+        if (!parentPath.canWrite()) {
+            return new BaseState(false, 2);
+        }
+        return new BaseState(true);
+    }
+}

+ 60 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/UploadUtils.java

@@ -0,0 +1,60 @@
+package com.zd.base.files.ueditor.upload;
+
+import io.netty.util.internal.StringUtil;
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+@Component
+public class UploadUtils {
+
+    private static String filePathStatic;
+
+    @Value("${file.path}")
+    public void setFilePathStatic(String filePath) {
+        this.filePathStatic = filePath;
+    }
+
+    /**
+     * 上传方法
+     *
+     * @param in
+     * @param typePath
+     * @param picName
+     * @return
+     * @throws IOException
+     */
+    public boolean uploadFile(InputStream in, String typePath, String picName) throws IOException {
+        String appendPath = typePath;
+        File filepath = new File(filePathStatic + File.separator + appendPath);
+        if (!filepath.exists()) { //如果不存在则创建
+            filepath.mkdirs();
+        }
+        filepath = new File(filePathStatic + File.separator + appendPath + File.separator + picName);
+        FileUtils.touch(filepath);
+        FileUtils.copyInputStreamToFile(in, filepath);
+        return true;
+    }
+
+
+    /**
+     * 添加上传文件目录
+     *
+     * @param typePath
+     * @return
+     */
+    private String getFilesPath(String typePath) {
+        StringBuilder path = new StringBuilder("/uploadFile");
+        if (StringUtil.isNullOrEmpty(typePath)) {
+            path.append("/uploads");
+        } else {
+            path.append("/" + typePath);
+        }
+        path = path.append("/");
+        return path.toString();
+    }
+}

+ 28 - 0
zd-modules/zd-base/src/main/java/com/zd/base/files/ueditor/upload/Uploader.java

@@ -0,0 +1,28 @@
+package com.zd.base.files.ueditor.upload;
+
+import com.zd.base.files.ueditor.define.State;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+public class Uploader {
+    private HttpServletRequest request;
+    private Map<String, Object> conf;
+
+    public Uploader(HttpServletRequest request, Map<String, Object> conf) {
+        this.request = null;
+        this.conf = null;
+        this.request = request;
+        this.conf = conf;
+    }
+
+    public State doExec() {
+        String filedName = String.valueOf(this.conf.get("fieldName"));
+        State state = null;
+        if ("true".equals(this.conf.get("isBase64"))) {
+            state = Base64Uploader.save(this.request.getParameter(filedName), this.conf);
+        } else {
+            state = BinaryUploader.save(this.request, this.conf);
+        }
+        return state;
+    }
+}

+ 65 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/config/GenConfig.java

@@ -0,0 +1,65 @@
+package com.zd.base.gen.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 代码生成相关配置
+ *
+ * @author zd
+ */
+@Component
+@ConfigurationProperties(prefix = "gen")
+public class GenConfig {
+    /**
+     * 作者
+     */
+    public static String author;
+
+    /**
+     * 生成包路径
+     */
+    public static String packageName;
+
+    /**
+     * 自动去除表前缀,默认是false
+     */
+    public static boolean autoRemovePre;
+
+    /**
+     * 表前缀(类名不会包含表前缀)
+     */
+    public static String tablePrefix;
+
+    public static String getAuthor() {
+        return author;
+    }
+
+    public void setAuthor(String author) {
+        GenConfig.author = author;
+    }
+
+    public static String getPackageName() {
+        return packageName;
+    }
+
+    public void setPackageName(String packageName) {
+        GenConfig.packageName = packageName;
+    }
+
+    public static boolean getAutoRemovePre() {
+        return autoRemovePre;
+    }
+
+    public void setAutoRemovePre(boolean autoRemovePre) {
+        GenConfig.autoRemovePre = autoRemovePre;
+    }
+
+    public static String getTablePrefix() {
+        return tablePrefix;
+    }
+
+    public void setTablePrefix(String tablePrefix) {
+        GenConfig.tablePrefix = tablePrefix;
+    }
+}

+ 195 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/controller/GenController.java

@@ -0,0 +1,195 @@
+package com.zd.base.gen.controller;
+
+import com.zd.common.core.domain.per.PerFun;
+import com.zd.common.core.domain.per.PerPrefix;
+import com.zd.common.core.text.Convert;
+import com.zd.common.core.web.controller.BaseController;
+import com.zd.common.core.web.domain.AjaxResult;
+import com.zd.common.core.web.page.TableDataInfo;
+import com.zd.common.log.annotation.Log;
+import com.zd.common.log.enums.BusinessType;
+import com.zd.common.security.annotation.PreAuthorize;
+import com.zd.base.gen.domain.GenTable;
+import com.zd.base.gen.domain.GenTableColumn;
+import com.zd.base.gen.service.IGenTableColumnService;
+import com.zd.base.gen.service.IGenTableService;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 代码生成 操作处理
+ *
+ * @author zd
+ */
+@RequestMapping("/gen")
+@RestController
+public class GenController extends BaseController {
+    @Autowired
+    private IGenTableService genTableService;
+
+    @Autowired
+    private IGenTableColumnService genTableColumnService;
+
+    /**
+     * 查询代码生成列表
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.LIST)
+    @GetMapping("/list")
+    public TableDataInfo genList(GenTable genTable) {
+        startPage();
+        List<GenTable> list = genTableService.selectGenTableList(genTable);
+        return getDataTable(list);
+    }
+
+    /**
+     * 修改代码生成业务
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.QUERY)
+    @GetMapping(value = "/{talbleId}")
+    public AjaxResult getInfo(@PathVariable Long talbleId) {
+        GenTable table = genTableService.selectGenTableById(talbleId);
+        List<GenTable> tables = genTableService.selectGenTableAll();
+        List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(talbleId);
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("info", table);
+        map.put("rows", list);
+        map.put("tables", tables);
+        return AjaxResult.success(map);
+    }
+
+    /**
+     * 查询数据库列表
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.LIST)
+    @GetMapping("/db/list")
+    public TableDataInfo dataList(GenTable genTable) {
+        startPage();
+        List<GenTable> list = genTableService.selectDbTableList(genTable);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询数据表字段列表
+     */
+    @GetMapping(value = "/column/{talbleId}")
+    public TableDataInfo columnList(Long tableId) {
+        TableDataInfo dataInfo = new TableDataInfo();
+        List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId);
+        dataInfo.setRows(list);
+        dataInfo.setTotal(list.size());
+        return dataInfo;
+    }
+
+    /**
+     * 导入表结构(保存)
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.IMPORT)
+    @Log(title = "代码生成", businessType = BusinessType.IMPORT)
+    @PostMapping("/importTable")
+    public AjaxResult importTableSave(String tables) {
+        String[] tableNames = Convert.toStrArray(tables);
+        // 查询表信息
+        List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
+        genTableService.importGenTable(tableList);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 修改保存代码生成业务
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.EDIT)
+    @Log(title = "代码生成", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult editSave(@Validated @RequestBody GenTable genTable) {
+        genTableService.validateEdit(genTable);
+        genTableService.updateGenTable(genTable);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 删除代码生成
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.REMOVE)
+    @Log(title = "代码生成", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{tableIds}")
+    public AjaxResult remove(@PathVariable Long[] tableIds) {
+        genTableService.deleteGenTableByIds(tableIds);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 预览代码
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.PREVIEW)
+    @GetMapping("/preview/{tableId}")
+    public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException {
+        Map<String, String> dataMap = genTableService.previewCode(tableId);
+        return AjaxResult.success(dataMap);
+    }
+
+    /**
+     * 生成代码(下载方式)
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.CODE)
+    @Log(title = "代码生成", businessType = BusinessType.GENCODE)
+    @GetMapping("/download/{tableName}")
+    public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException {
+        byte[] data = genTableService.downloadCode(tableName);
+        genCode(response, data);
+    }
+
+    /**
+     * 生成代码(自定义路径)
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.CODE)
+    @Log(title = "代码生成", businessType = BusinessType.GENCODE)
+    @GetMapping("/genCode/{tableName}")
+    public AjaxResult genCode(@PathVariable("tableName") String tableName) {
+        genTableService.generatorCode(tableName);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 同步数据库
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.EDIT)
+    @Log(title = "代码生成", businessType = BusinessType.UPDATE)
+    @GetMapping("/synchDb/{tableName}")
+    public AjaxResult synchDb(@PathVariable("tableName") String tableName) {
+        genTableService.synchDb(tableName);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 批量生成代码
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_TOOLGEN + PerFun.CODE)
+    @Log(title = "代码生成", businessType = BusinessType.GENCODE)
+    @GetMapping("/batchGenCode")
+    public void batchGenCode(HttpServletResponse response, String tables) throws IOException {
+        String[] tableNames = Convert.toStrArray(tables);
+        byte[] data = genTableService.downloadCode(tableNames);
+        genCode(response, data);
+    }
+
+    /**
+     * 生成zip文件
+     */
+    private void genCode(HttpServletResponse response, byte[] data) throws IOException {
+        response.reset();
+        response.addHeader("Access-Control-Allow-Origin", "*");
+        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
+        response.setHeader("Content-Disposition", "attachment; filename=\"zd.zip\"");
+        response.addHeader("Content-Length", "" + data.length);
+        response.setContentType("application/octet-stream; charset=UTF-8");
+        IOUtils.write(data, response.getOutputStream());
+    }
+}

+ 363 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/domain/GenTable.java

@@ -0,0 +1,363 @@
+package com.zd.base.gen.domain;
+
+import com.zd.common.core.constant.GenConstants;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.common.core.web.domain.BaseEntity;
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import java.util.List;
+
+/**
+ * 业务表 gen_table
+ *
+ * @author zd
+ */
+public class GenTable extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 编号
+     */
+    private Long tableId;
+
+    /**
+     * 表名称
+     */
+    @NotBlank(message = "表名称不能为空")
+    private String tableName;
+
+    /**
+     * 表描述
+     */
+    @NotBlank(message = "表描述不能为空")
+    private String tableComment;
+
+    /**
+     * 关联父表的表名
+     */
+    private String subTableName;
+
+    /**
+     * 本表关联父表的外键名
+     */
+    private String subTableFkName;
+
+    /**
+     * 实体类名称(首字母大写)
+     */
+    @NotBlank(message = "实体类名称不能为空")
+    private String className;
+
+    /**
+     * 使用的模板(crud单表操作 tree树表操作 sub主子表操作)
+     */
+    private String tplCategory;
+
+    /**
+     * 生成包路径
+     */
+    @NotBlank(message = "生成包路径不能为空")
+    private String packageName;
+
+    /**
+     * 生成模块名
+     */
+    @NotBlank(message = "生成模块名不能为空")
+    private String moduleName;
+
+    /**
+     * 生成业务名
+     */
+    @NotBlank(message = "生成业务名不能为空")
+    private String businessName;
+
+    /**
+     * 生成功能名
+     */
+    @NotBlank(message = "生成功能名不能为空")
+    private String functionName;
+
+    /**
+     * 生成作者
+     */
+    @NotBlank(message = "作者不能为空")
+    private String functionAuthor;
+
+    /**
+     * 生成代码方式(0zip压缩包 1自定义路径)
+     */
+    private String genType;
+
+    /**
+     * 生成路径(不填默认项目路径)
+     */
+    private String genPath;
+
+    /**
+     * 主键信息
+     */
+    private GenTableColumn pkColumn;
+
+    /**
+     * 子表信息
+     */
+    private GenTable subTable;
+
+    /**
+     * 表列信息
+     */
+    @Valid
+    private List<GenTableColumn> columns;
+
+    /**
+     * 其它生成选项
+     */
+    private String options;
+
+    /**
+     * 树编码字段
+     */
+    private String treeCode;
+
+    /**
+     * 树父编码字段
+     */
+    private String treeParentCode;
+
+    /**
+     * 树名称字段
+     */
+    private String treeName;
+
+    /**
+     * 上级菜单ID字段
+     */
+    private String parentMenuId;
+
+    /**
+     * 上级菜单名称字段
+     */
+    private String parentMenuName;
+
+    public Long getTableId() {
+        return tableId;
+    }
+
+    public void setTableId(Long tableId) {
+        this.tableId = tableId;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    public String getTableComment() {
+        return tableComment;
+    }
+
+    public void setTableComment(String tableComment) {
+        this.tableComment = tableComment;
+    }
+
+    public String getSubTableName() {
+        return subTableName;
+    }
+
+    public void setSubTableName(String subTableName) {
+        this.subTableName = subTableName;
+    }
+
+    public String getSubTableFkName() {
+        return subTableFkName;
+    }
+
+    public void setSubTableFkName(String subTableFkName) {
+        this.subTableFkName = subTableFkName;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public void setClassName(String className) {
+        this.className = className;
+    }
+
+    public String getTplCategory() {
+        return tplCategory;
+    }
+
+    public void setTplCategory(String tplCategory) {
+        this.tplCategory = tplCategory;
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public void setPackageName(String packageName) {
+        this.packageName = packageName;
+    }
+
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    public void setModuleName(String moduleName) {
+        this.moduleName = moduleName;
+    }
+
+    public String getBusinessName() {
+        return businessName;
+    }
+
+    public void setBusinessName(String businessName) {
+        this.businessName = businessName;
+    }
+
+    public String getFunctionName() {
+        return functionName;
+    }
+
+    public void setFunctionName(String functionName) {
+        this.functionName = functionName;
+    }
+
+    public String getFunctionAuthor() {
+        return functionAuthor;
+    }
+
+    public void setFunctionAuthor(String functionAuthor) {
+        this.functionAuthor = functionAuthor;
+    }
+
+    public String getGenType() {
+        return genType;
+    }
+
+    public void setGenType(String genType) {
+        this.genType = genType;
+    }
+
+    public String getGenPath() {
+        return genPath;
+    }
+
+    public void setGenPath(String genPath) {
+        this.genPath = genPath;
+    }
+
+    public GenTableColumn getPkColumn() {
+        return pkColumn;
+    }
+
+    public void setPkColumn(GenTableColumn pkColumn) {
+        this.pkColumn = pkColumn;
+    }
+
+    public GenTable getSubTable() {
+        return subTable;
+    }
+
+    public void setSubTable(GenTable subTable) {
+        this.subTable = subTable;
+    }
+
+    public List<GenTableColumn> getColumns() {
+        return columns;
+    }
+
+    public void setColumns(List<GenTableColumn> columns) {
+        this.columns = columns;
+    }
+
+    public String getOptions() {
+        return options;
+    }
+
+    public void setOptions(String options) {
+        this.options = options;
+    }
+
+    public String getTreeCode() {
+        return treeCode;
+    }
+
+    public void setTreeCode(String treeCode) {
+        this.treeCode = treeCode;
+    }
+
+    public String getTreeParentCode() {
+        return treeParentCode;
+    }
+
+    public void setTreeParentCode(String treeParentCode) {
+        this.treeParentCode = treeParentCode;
+    }
+
+    public String getTreeName() {
+        return treeName;
+    }
+
+    public void setTreeName(String treeName) {
+        this.treeName = treeName;
+    }
+
+    public String getParentMenuId() {
+        return parentMenuId;
+    }
+
+    public void setParentMenuId(String parentMenuId) {
+        this.parentMenuId = parentMenuId;
+    }
+
+    public String getParentMenuName() {
+        return parentMenuName;
+    }
+
+    public void setParentMenuName(String parentMenuName) {
+        this.parentMenuName = parentMenuName;
+    }
+
+    public boolean isSub() {
+        return isSub(this.tplCategory);
+    }
+
+    public static boolean isSub(String tplCategory) {
+        return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory);
+    }
+
+    public boolean isTree() {
+        return isTree(this.tplCategory);
+    }
+
+    public static boolean isTree(String tplCategory) {
+        return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory);
+    }
+
+    public boolean isCrud() {
+        return isCrud(this.tplCategory);
+    }
+
+    public static boolean isCrud(String tplCategory) {
+        return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory);
+    }
+
+    public boolean isSuperColumn(String javaField) {
+        return isSuperColumn(this.tplCategory, javaField);
+    }
+
+    public static boolean isSuperColumn(String tplCategory, String javaField) {
+        if (isTree(tplCategory)) {
+            return StringUtils.equalsAnyIgnoreCase(javaField,
+                    ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY));
+        }
+        return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
+    }
+}

+ 362 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/domain/GenTableColumn.java

@@ -0,0 +1,362 @@
+package com.zd.base.gen.domain;
+
+import com.zd.common.core.utils.StringUtils;
+import com.zd.common.core.web.domain.BaseEntity;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 代码生成业务字段表 gen_table_column
+ *
+ * @author zd
+ */
+public class GenTableColumn extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 编号
+     */
+    private Long columnId;
+
+    /**
+     * 归属表编号
+     */
+    private Long tableId;
+
+    /**
+     * 列名称
+     */
+    private String columnName;
+
+    /**
+     * 列描述
+     */
+    private String columnComment;
+
+    /**
+     * 列类型
+     */
+    private String columnType;
+
+    /**
+     * JAVA类型
+     */
+    private String javaType;
+
+    /**
+     * JAVA字段名
+     */
+    @NotBlank(message = "Java属性不能为空")
+    private String javaField;
+
+    /**
+     * 是否主键(1是)
+     */
+    private String isPk;
+
+    /**
+     * 是否自增(1是)
+     */
+    private String isIncrement;
+
+    /**
+     * 是否必填(1是)
+     */
+    private String isRequired;
+
+    /**
+     * 是否为插入字段(1是)
+     */
+    private String isInsert;
+
+    /**
+     * 是否编辑字段(1是)
+     */
+    private String isEdit;
+
+    /**
+     * 是否列表字段(1是)
+     */
+    private String isList;
+
+    /**
+     * 是否查询字段(1是)
+     */
+    private String isQuery;
+
+    /**
+     * 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围)
+     */
+    private String queryType;
+
+    /**
+     * 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件)
+     */
+    private String htmlType;
+
+    /**
+     * 字典类型
+     */
+    private String dictType;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+    /**
+     * 长度 当为Strting 类型有值
+     */
+    private Integer length;
+
+
+    public Integer getLength() {
+        return length;
+    }
+
+    public void setLength(Integer length) {
+        this.length = length;
+    }
+
+    public void setColumnId(Long columnId) {
+        this.columnId = columnId;
+    }
+
+    public Long getColumnId() {
+        return columnId;
+    }
+
+    public void setTableId(Long tableId) {
+        this.tableId = tableId;
+    }
+
+    public Long getTableId() {
+        return tableId;
+    }
+
+    public void setColumnName(String columnName) {
+        this.columnName = columnName;
+    }
+
+    public String getColumnName() {
+        return columnName;
+    }
+
+    public void setColumnComment(String columnComment) {
+        this.columnComment = columnComment;
+    }
+
+    public String getColumnComment() {
+        return columnComment;
+    }
+
+    public void setColumnType(String columnType) {
+        this.columnType = columnType;
+    }
+
+    public String getColumnType() {
+        return columnType;
+    }
+
+    public void setJavaType(String javaType) {
+        this.javaType = javaType;
+    }
+
+    public String getJavaType() {
+        return javaType;
+    }
+
+    public void setJavaField(String javaField) {
+        this.javaField = javaField;
+    }
+
+    public String getJavaField() {
+        return javaField;
+    }
+
+    public String getCapJavaField() {
+        return StringUtils.capitalize(javaField);
+    }
+
+    public void setIsPk(String isPk) {
+        this.isPk = isPk;
+    }
+
+    public String getIsPk() {
+        return isPk;
+    }
+
+    public boolean isPk() {
+        return isPk(this.isPk);
+    }
+
+    public boolean isPk(String isPk) {
+        return isPk != null && StringUtils.equals("1", isPk);
+    }
+
+    public String getIsIncrement() {
+        return isIncrement;
+    }
+
+    public void setIsIncrement(String isIncrement) {
+        this.isIncrement = isIncrement;
+    }
+
+    public boolean isIncrement() {
+        return isIncrement(this.isIncrement);
+    }
+
+    public boolean isIncrement(String isIncrement) {
+        return isIncrement != null && StringUtils.equals("1", isIncrement);
+    }
+
+    public void setIsRequired(String isRequired) {
+        this.isRequired = isRequired;
+    }
+
+    public String getIsRequired() {
+        return isRequired;
+    }
+
+    public boolean isRequired() {
+        return isRequired(this.isRequired);
+    }
+
+    public boolean isRequired(String isRequired) {
+        return isRequired != null && StringUtils.equals("1", isRequired);
+    }
+
+    public void setIsInsert(String isInsert) {
+        this.isInsert = isInsert;
+    }
+
+    public String getIsInsert() {
+        return isInsert;
+    }
+
+    public boolean isInsert() {
+        return isInsert(this.isInsert);
+    }
+
+    public boolean isInsert(String isInsert) {
+        return isInsert != null && StringUtils.equals("1", isInsert);
+    }
+
+    public void setIsEdit(String isEdit) {
+        this.isEdit = isEdit;
+    }
+
+    public String getIsEdit() {
+        return isEdit;
+    }
+
+    public boolean isEdit() {
+        return isInsert(this.isEdit);
+    }
+
+    public boolean isEdit(String isEdit) {
+        return isEdit != null && StringUtils.equals("1", isEdit);
+    }
+
+    public void setIsList(String isList) {
+        this.isList = isList;
+    }
+
+    public String getIsList() {
+        return isList;
+    }
+
+    public boolean isList() {
+        return isList(this.isList);
+    }
+
+    public boolean isList(String isList) {
+        return isList != null && StringUtils.equals("1", isList);
+    }
+
+    public void setIsQuery(String isQuery) {
+        this.isQuery = isQuery;
+    }
+
+    public String getIsQuery() {
+        return isQuery;
+    }
+
+    public boolean isQuery() {
+        return isQuery(this.isQuery);
+    }
+
+    public boolean isQuery(String isQuery) {
+        return isQuery != null && StringUtils.equals("1", isQuery);
+    }
+
+    public void setQueryType(String queryType) {
+        this.queryType = queryType;
+    }
+
+    public String getQueryType() {
+        return queryType;
+    }
+
+    public String getHtmlType() {
+        return htmlType;
+    }
+
+    public void setHtmlType(String htmlType) {
+        this.htmlType = htmlType;
+    }
+
+    public void setDictType(String dictType) {
+        this.dictType = dictType;
+    }
+
+    public String getDictType() {
+        return dictType;
+    }
+
+    public void setSort(Integer sort) {
+        this.sort = sort;
+    }
+
+    public Integer getSort() {
+        return sort;
+    }
+
+    public boolean isSuperColumn() {
+        return isSuperColumn(this.javaField);
+    }
+
+    public static boolean isSuperColumn(String javaField) {
+        return StringUtils.equalsAnyIgnoreCase(javaField,
+                // BaseEntity
+                "createBy", "createTime", "updateBy", "updateTime", "remark", "userId", "deptName", "deptId",
+                // TreeEntity
+                "parentName", "parentId", "orderNum", "ancestors");
+    }
+
+    public boolean isUsableColumn() {
+        return isUsableColumn(javaField);
+    }
+
+    public static boolean isUsableColumn(String javaField) {
+        // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单
+        return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark");
+    }
+
+    public String readConverterExp() {
+        String remarks = StringUtils.substringBetween(this.columnComment, "(", ")");
+        StringBuffer sb = new StringBuffer();
+        if (StringUtils.isNotEmpty(remarks)) {
+            for (String value : remarks.split(" ")) {
+                if (StringUtils.isNotEmpty(value)) {
+                    Object startStr = value.subSequence(0, 1);
+                    String endStr = value.substring(1);
+                    sb.append("").append(startStr).append("=").append(endStr).append(",");
+                }
+            }
+            return sb.deleteCharAt(sb.length() - 1).toString();
+        } else {
+            return this.columnComment;
+        }
+    }
+}

+ 59 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/mapper/GenTableColumnMapper.java

@@ -0,0 +1,59 @@
+package com.zd.base.gen.mapper;
+
+import com.zd.base.gen.domain.GenTableColumn;
+import java.util.List;
+
+/**
+ * 业务字段 数据层
+ *
+ * @author zd
+ */
+public interface GenTableColumnMapper {
+    /**
+     * 根据表名称查询列信息
+     *
+     * @param tableName 表名称
+     * @return 列信息
+     */
+    public List<GenTableColumn> selectDbTableColumnsByName(String tableName);
+
+    /**
+     * 查询业务字段列表
+     *
+     * @param tableId 业务字段编号
+     * @return 业务字段集合
+     */
+    public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId);
+
+    /**
+     * 新增业务字段
+     *
+     * @param genTableColumn 业务字段信息
+     * @return 结果
+     */
+    public int insertGenTableColumn(GenTableColumn genTableColumn);
+
+    /**
+     * 修改业务字段
+     *
+     * @param genTableColumn 业务字段信息
+     * @return 结果
+     */
+    public int updateGenTableColumn(GenTableColumn genTableColumn);
+
+    /**
+     * 删除业务字段
+     *
+     * @param genTableColumns 列数据
+     * @return 结果
+     */
+    public int deleteGenTableColumns(List<GenTableColumn> genTableColumns);
+
+    /**
+     * 批量删除业务字段
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteGenTableColumnByIds(Long[] ids);
+}

+ 82 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/mapper/GenTableMapper.java

@@ -0,0 +1,82 @@
+package com.zd.base.gen.mapper;
+
+import com.zd.base.gen.domain.GenTable;
+import java.util.List;
+
+/**
+ * 业务 数据层
+ *
+ * @author zd
+ */
+public interface GenTableMapper {
+    /**
+     * 查询业务列表
+     *
+     * @param genTable 业务信息
+     * @return 业务集合
+     */
+    public List<GenTable> selectGenTableList(GenTable genTable);
+
+    /**
+     * 查询据库列表
+     *
+     * @param genTable 业务信息
+     * @return 数据库表集合
+     */
+    public List<GenTable> selectDbTableList(GenTable genTable);
+
+    /**
+     * 查询据库列表
+     *
+     * @param tableNames 表名称组
+     * @return 数据库表集合
+     */
+    public List<GenTable> selectDbTableListByNames(String[] tableNames);
+
+    /**
+     * 查询所有表信息
+     *
+     * @return 表信息集合
+     */
+    public List<GenTable> selectGenTableAll();
+
+    /**
+     * 查询表ID业务信息
+     *
+     * @param id 业务ID
+     * @return 业务信息
+     */
+    public GenTable selectGenTableById(Long id);
+
+    /**
+     * 查询表名称业务信息
+     *
+     * @param tableName 表名称
+     * @return 业务信息
+     */
+    public GenTable selectGenTableByName(String tableName);
+
+    /**
+     * 新增业务
+     *
+     * @param genTable 业务信息
+     * @return 结果
+     */
+    public int insertGenTable(GenTable genTable);
+
+    /**
+     * 修改业务
+     *
+     * @param genTable 业务信息
+     * @return 结果
+     */
+    public int updateGenTable(GenTable genTable);
+
+    /**
+     * 批量删除业务
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteGenTableByIds(Long[] ids);
+}

+ 64 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/service/GenTableColumnServiceImpl.java

@@ -0,0 +1,64 @@
+package com.zd.base.gen.service;
+
+import com.zd.common.core.text.Convert;
+import com.zd.base.gen.domain.GenTableColumn;
+import com.zd.base.gen.mapper.GenTableColumnMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 业务字段 服务层实现
+ *
+ * @author zd
+ */
+@Service
+public class GenTableColumnServiceImpl implements IGenTableColumnService {
+    @Autowired
+    private GenTableColumnMapper genTableColumnMapper;
+
+    /**
+     * 查询业务字段列表
+     *
+     * @param tableId 业务字段编号
+     * @return 业务字段集合
+     */
+    @Override
+    public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId) {
+        return genTableColumnMapper.selectGenTableColumnListByTableId(tableId);
+    }
+
+    /**
+     * 新增业务字段
+     *
+     * @param genTableColumn 业务字段信息
+     * @return 结果
+     */
+    @Override
+    public int insertGenTableColumn(GenTableColumn genTableColumn) {
+        return genTableColumnMapper.insertGenTableColumn(genTableColumn);
+    }
+
+    /**
+     * 修改业务字段
+     *
+     * @param genTableColumn 业务字段信息
+     * @return 结果
+     */
+    @Override
+    public int updateGenTableColumn(GenTableColumn genTableColumn) {
+        return genTableColumnMapper.updateGenTableColumn(genTableColumn);
+    }
+
+    /**
+     * 删除业务字段对象
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    @Override
+    public int deleteGenTableColumnByIds(String ids) {
+        return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids));
+    }
+}

+ 437 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/service/GenTableServiceImpl.java

@@ -0,0 +1,437 @@
+package com.zd.base.gen.service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.zd.base.gen.utils.GenUtils;
+import com.zd.base.gen.utils.VelocityInitializer;
+import com.zd.base.gen.utils.VelocityUtils;
+import com.zd.common.core.constant.Constants;
+import com.zd.common.core.constant.GenConstants;
+import com.zd.common.core.exception.ServiceException;
+import com.zd.common.core.text.CharsetKit;
+import com.zd.common.core.utils.SecurityUtils;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.base.gen.domain.GenTable;
+import com.zd.base.gen.domain.GenTableColumn;
+import com.zd.base.gen.mapper.GenTableColumnMapper;
+import com.zd.base.gen.mapper.GenTableMapper;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * 业务 服务层实现
+ *
+ * @author zd
+ */
+@Service
+public class GenTableServiceImpl implements IGenTableService {
+    private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class);
+
+    @Autowired
+    private GenTableMapper genTableMapper;
+
+    @Autowired
+    private GenTableColumnMapper genTableColumnMapper;
+
+    /**
+     * 查询业务信息
+     *
+     * @param id 业务ID
+     * @return 业务信息
+     */
+    @Override
+    public GenTable selectGenTableById(Long id) {
+        GenTable genTable = genTableMapper.selectGenTableById(id);
+        setTableFromOptions(genTable);
+        return genTable;
+    }
+
+    /**
+     * 查询业务列表
+     *
+     * @param genTable 业务信息
+     * @return 业务集合
+     */
+    @Override
+    public List<GenTable> selectGenTableList(GenTable genTable) {
+        return genTableMapper.selectGenTableList(genTable);
+    }
+
+    /**
+     * 查询据库列表
+     *
+     * @param genTable 业务信息
+     * @return 数据库表集合
+     */
+    @Override
+    public List<GenTable> selectDbTableList(GenTable genTable) {
+        return genTableMapper.selectDbTableList(genTable);
+    }
+
+    /**
+     * 查询据库列表
+     *
+     * @param tableNames 表名称组
+     * @return 数据库表集合
+     */
+    @Override
+    public List<GenTable> selectDbTableListByNames(String[] tableNames) {
+        return genTableMapper.selectDbTableListByNames(tableNames);
+    }
+
+    /**
+     * 查询所有表信息
+     *
+     * @return 表信息集合
+     */
+    @Override
+    public List<GenTable> selectGenTableAll() {
+        return genTableMapper.selectGenTableAll();
+    }
+
+    /**
+     * 修改业务
+     *
+     * @param genTable 业务信息
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public void updateGenTable(GenTable genTable) {
+        String options = JSON.toJSONString(genTable.getParams());
+        genTable.setOptions(options);
+        int row = genTableMapper.updateGenTable(genTable);
+        if (row > 0) {
+            for (GenTableColumn cenTableColumn : genTable.getColumns()) {
+                genTableColumnMapper.updateGenTableColumn(cenTableColumn);
+            }
+        }
+    }
+
+    /**
+     * 删除业务对象
+     *
+     * @param tableIds 需要删除的数据ID
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public void deleteGenTableByIds(Long[] tableIds) {
+        genTableMapper.deleteGenTableByIds(tableIds);
+        genTableColumnMapper.deleteGenTableColumnByIds(tableIds);
+    }
+
+    /**
+     * 导入表结构
+     *
+     * @param tableList 导入表列表
+     */
+    @Override
+    @Transactional
+    public void importGenTable(List<GenTable> tableList) {
+        String operName = SecurityUtils.getUsername();
+        try {
+            for (GenTable table : tableList) {
+                String tableName = table.getTableName();
+                GenUtils.initTable(table, operName);
+                int row = genTableMapper.insertGenTable(table);
+                if (row > 0) {
+                    // 保存列信息
+                    List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
+                    for (GenTableColumn column : genTableColumns) {
+                        GenUtils.initColumnField(column, table);
+                        genTableColumnMapper.insertGenTableColumn(column);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            throw new ServiceException("导入失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 预览代码
+     *
+     * @param tableId 表编号
+     * @return 预览数据列表
+     */
+    @Override
+    public Map<String, String> previewCode(Long tableId) {
+        Map<String, String> dataMap = new LinkedHashMap<>();
+        // 查询表信息
+        GenTable table = genTableMapper.selectGenTableById(tableId);
+        // 设置主子表信息
+        setSubTable(table);
+        // 设置主键列信息
+        setPkColumn(table);
+        VelocityInitializer.initVelocity();
+
+        VelocityContext context = VelocityUtils.prepareContext(table);
+
+        // 获取模板列表
+        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
+        for (String template : templates) {
+            // 渲染模板
+            StringWriter sw = new StringWriter();
+            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
+            tpl.merge(context, sw);
+            dataMap.put(template, sw.toString());
+        }
+        return dataMap;
+    }
+
+    /**
+     * 生成代码(下载方式)
+     *
+     * @param tableName 表名称
+     * @return 数据
+     */
+    @Override
+    public byte[] downloadCode(String tableName) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ZipOutputStream zip = new ZipOutputStream(outputStream);
+        generatorCode(tableName, zip);
+        IOUtils.closeQuietly(zip);
+        return outputStream.toByteArray();
+    }
+
+    /**
+     * 生成代码(自定义路径)
+     *
+     * @param tableName 表名称
+     */
+    @Override
+    public void generatorCode(String tableName) {
+        // 查询表信息
+        GenTable table = genTableMapper.selectGenTableByName(tableName);
+        // 设置主子表信息
+        setSubTable(table);
+        // 设置主键列信息
+        setPkColumn(table);
+
+        VelocityInitializer.initVelocity();
+
+        VelocityContext context = VelocityUtils.prepareContext(table);
+
+        // 获取模板列表
+        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
+        for (String template : templates) {
+            if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) {
+                // 渲染模板
+                StringWriter sw = new StringWriter();
+                Template tpl = Velocity.getTemplate(template, Constants.UTF8);
+                tpl.merge(context, sw);
+                try {
+                    String path = getGenPath(table, template);
+                    FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8);
+                } catch (IOException e) {
+                    throw new ServiceException("渲染模板失败,表名:" + table.getTableName());
+                }
+            }
+        }
+    }
+
+    /**
+     * 同步数据库
+     *
+     * @param tableName 表名称
+     */
+    @Override
+    @Transactional
+    public void synchDb(String tableName) {
+        GenTable table = genTableMapper.selectGenTableByName(tableName);
+        List<GenTableColumn> tableColumns = table.getColumns();
+        List<String> tableColumnNames = tableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
+
+        List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
+        if (StringUtils.isEmpty(dbTableColumns)) {
+            throw new ServiceException("同步数据失败,原表结构不存在");
+        }
+        List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
+
+        dbTableColumns.forEach(column -> {
+            if (!tableColumnNames.contains(column.getColumnName())) {
+                GenUtils.initColumnField(column, table);
+                genTableColumnMapper.insertGenTableColumn(column);
+            }
+        });
+
+        List<GenTableColumn> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());
+        if (StringUtils.isNotEmpty(delColumns)) {
+            genTableColumnMapper.deleteGenTableColumns(delColumns);
+        }
+    }
+
+    /**
+     * 批量生成代码(下载方式)
+     *
+     * @param tableNames 表数组
+     * @return 数据
+     */
+    @Override
+    public byte[] downloadCode(String[] tableNames) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ZipOutputStream zip = new ZipOutputStream(outputStream);
+        for (String tableName : tableNames) {
+            generatorCode(tableName, zip);
+        }
+        IOUtils.closeQuietly(zip);
+        return outputStream.toByteArray();
+    }
+
+    /**
+     * 查询表信息并生成代码
+     */
+    private void generatorCode(String tableName, ZipOutputStream zip) {
+        // 查询表信息
+        GenTable table = genTableMapper.selectGenTableByName(tableName);
+        // 设置主子表信息
+        setSubTable(table);
+        // 设置主键列信息
+        setPkColumn(table);
+
+        VelocityInitializer.initVelocity();
+
+        VelocityContext context = VelocityUtils.prepareContext(table);
+
+        // 获取模板列表
+        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
+        for (String template : templates) {
+            // 渲染模板
+            StringWriter sw = new StringWriter();
+            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
+            tpl.merge(context, sw);
+            try {
+                // 添加到zip
+                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
+                IOUtils.write(sw.toString(), zip, Constants.UTF8);
+                IOUtils.closeQuietly(sw);
+                zip.flush();
+                zip.closeEntry();
+            } catch (IOException e) {
+                log.error("渲染模板失败,表名:" + table.getTableName(), e);
+            }
+        }
+    }
+
+    /**
+     * 修改保存参数校验
+     *
+     * @param genTable 业务信息
+     */
+    @Override
+    public void validateEdit(GenTable genTable) {
+        if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
+            String options = JSON.toJSONString(genTable.getParams());
+            JSONObject paramsObj = JSONObject.parseObject(options);
+            if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) {
+                throw new ServiceException("树编码字段不能为空");
+            } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) {
+                throw new ServiceException("树父编码字段不能为空");
+            } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) {
+                throw new ServiceException("树名称字段不能为空");
+            } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {
+                if (StringUtils.isEmpty(genTable.getSubTableName())) {
+                    throw new ServiceException("关联子表的表名不能为空");
+                } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) {
+                    throw new ServiceException("子表关联的外键名不能为空");
+                }
+            }
+        }
+    }
+
+    /**
+     * 设置主键列信息
+     *
+     * @param table 业务表信息
+     */
+    public void setPkColumn(GenTable table) {
+        for (GenTableColumn column : table.getColumns()) {
+            if (column.isPk()) {
+                table.setPkColumn(column);
+                break;
+            }
+        }
+        if (StringUtils.isNull(table.getPkColumn())) {
+            table.setPkColumn(table.getColumns().get(0));
+        }
+        if (GenConstants.TPL_SUB.equals(table.getTplCategory())) {
+            for (GenTableColumn column : table.getSubTable().getColumns()) {
+                if (column.isPk()) {
+                    table.getSubTable().setPkColumn(column);
+                    break;
+                }
+            }
+            if (StringUtils.isNull(table.getSubTable().getPkColumn())) {
+                table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));
+            }
+        }
+    }
+
+    /**
+     * 设置主子表信息
+     *
+     * @param table 业务表信息
+     */
+    public void setSubTable(GenTable table) {
+        String subTableName = table.getSubTableName();
+        if (StringUtils.isNotEmpty(subTableName)) {
+            table.setSubTable(genTableMapper.selectGenTableByName(subTableName));
+        }
+    }
+
+    /**
+     * 设置代码生成其他选项值
+     *
+     * @param genTable 设置后的生成对象
+     */
+    public void setTableFromOptions(GenTable genTable) {
+        JSONObject paramsObj = JSONObject.parseObject(genTable.getOptions());
+        if (StringUtils.isNotNull(paramsObj)) {
+            String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
+            String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
+            String treeName = paramsObj.getString(GenConstants.TREE_NAME);
+            String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID);
+            String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME);
+
+            genTable.setTreeCode(treeCode);
+            genTable.setTreeParentCode(treeParentCode);
+            genTable.setTreeName(treeName);
+            genTable.setParentMenuId(parentMenuId);
+            genTable.setParentMenuName(parentMenuName);
+        }
+    }
+
+    /**
+     * 获取代码生成地址
+     *
+     * @param table    业务表信息
+     * @param template 模板文件路径
+     * @return 生成地址
+     */
+    public static String getGenPath(GenTable table, String template) {
+        String genPath = table.getGenPath();
+        if (StringUtils.equals(genPath, "/")) {
+            return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table);
+        }
+        return genPath + File.separator + VelocityUtils.getFileName(template, table);
+    }
+}

+ 43 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/service/IGenTableColumnService.java

@@ -0,0 +1,43 @@
+package com.zd.base.gen.service;
+
+import com.zd.base.gen.domain.GenTableColumn;
+import java.util.List;
+
+/**
+ * 业务字段 服务层
+ *
+ * @author zd
+ */
+public interface IGenTableColumnService {
+    /**
+     * 查询业务字段列表
+     *
+     * @param tableId 业务字段编号
+     * @return 业务字段集合
+     */
+    public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId);
+
+    /**
+     * 新增业务字段
+     *
+     * @param genTableColumn 业务字段信息
+     * @return 结果
+     */
+    public int insertGenTableColumn(GenTableColumn genTableColumn);
+
+    /**
+     * 修改业务字段
+     *
+     * @param genTableColumn 业务字段信息
+     * @return 结果
+     */
+    public int updateGenTableColumn(GenTableColumn genTableColumn);
+
+    /**
+     * 删除业务字段信息
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteGenTableColumnByIds(String ids);
+}

+ 120 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/service/IGenTableService.java

@@ -0,0 +1,120 @@
+package com.zd.base.gen.service;
+
+import com.zd.base.gen.domain.GenTable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 业务 服务层
+ *
+ * @author zd
+ */
+public interface IGenTableService {
+    /**
+     * 查询业务列表
+     *
+     * @param genTable 业务信息
+     * @return 业务集合
+     */
+    public List<GenTable> selectGenTableList(GenTable genTable);
+
+    /**
+     * 查询据库列表
+     *
+     * @param genTable 业务信息
+     * @return 数据库表集合
+     */
+    public List<GenTable> selectDbTableList(GenTable genTable);
+
+    /**
+     * 查询据库列表
+     *
+     * @param tableNames 表名称组
+     * @return 数据库表集合
+     */
+    public List<GenTable> selectDbTableListByNames(String[] tableNames);
+
+    /**
+     * 查询所有表信息
+     *
+     * @return 表信息集合
+     */
+    public List<GenTable> selectGenTableAll();
+
+    /**
+     * 查询业务信息
+     *
+     * @param id 业务ID
+     * @return 业务信息
+     */
+    public GenTable selectGenTableById(Long id);
+
+    /**
+     * 修改业务
+     *
+     * @param genTable 业务信息
+     * @return 结果
+     */
+    public void updateGenTable(GenTable genTable);
+
+    /**
+     * 删除业务信息
+     *
+     * @param tableIds 需要删除的表数据ID
+     * @return 结果
+     */
+    public void deleteGenTableByIds(Long[] tableIds);
+
+    /**
+     * 导入表结构
+     *
+     * @param tableList 导入表列表
+     */
+    public void importGenTable(List<GenTable> tableList);
+
+    /**
+     * 预览代码
+     *
+     * @param tableId 表编号
+     * @return 预览数据列表
+     */
+    public Map<String, String> previewCode(Long tableId);
+
+    /**
+     * 生成代码(下载方式)
+     *
+     * @param tableName 表名称
+     * @return 数据
+     */
+    public byte[] downloadCode(String tableName);
+
+    /**
+     * 生成代码(自定义路径)
+     *
+     * @param tableName 表名称
+     * @return 数据
+     */
+    public void generatorCode(String tableName);
+
+    /**
+     * 同步数据库
+     *
+     * @param tableName 表名称
+     */
+    public void synchDb(String tableName);
+
+    /**
+     * 批量生成代码(下载方式)
+     *
+     * @param tableNames 表数组
+     * @return 数据
+     */
+    public byte[] downloadCode(String[] tableNames);
+
+    /**
+     * 修改保存参数校验
+     *
+     * @param genTable 业务信息
+     */
+    public void validateEdit(GenTable genTable);
+}

+ 223 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/utils/GenUtils.java

@@ -0,0 +1,223 @@
+package com.zd.base.gen.utils;
+
+import com.zd.common.core.constant.GenConstants;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.base.gen.config.GenConfig;
+import com.zd.base.gen.domain.GenTable;
+import com.zd.base.gen.domain.GenTableColumn;
+import org.apache.commons.lang3.RegExUtils;
+
+import java.util.Arrays;
+
+/**
+ * 代码生成器 工具类
+ *
+ * @author zd
+ */
+public class GenUtils {
+    /**
+     * 初始化表信息
+     */
+    public static void initTable(GenTable genTable, String operName) {
+        genTable.setClassName(convertClassName(genTable.getTableName()));
+        genTable.setPackageName(GenConfig.getPackageName());
+        genTable.setModuleName(getModuleName(GenConfig.getPackageName()));
+        genTable.setBusinessName(getBusinessName(genTable.getTableName()));
+        genTable.setFunctionName(replaceText(genTable.getTableComment()));
+        genTable.setFunctionAuthor(GenConfig.getAuthor());
+        genTable.setCreateBy(operName);
+    }
+
+    /**
+     * 初始化列属性字段
+     */
+    public static void initColumnField(GenTableColumn column, GenTable table) {
+        String dataType = getDbType(column.getColumnType());
+        String columnName = column.getColumnName();
+        column.setTableId(table.getTableId());
+        column.setCreateBy(table.getCreateBy());
+        // 设置java字段名
+        column.setJavaField(StringUtils.toCamelCase(columnName));
+        // 设置默认类型
+        column.setJavaType(GenConstants.TYPE_STRING);
+        column.setLength(-1);
+        if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) {
+            // 字符串长度超过500设置为文本域
+            Integer columnLength = getColumnLength(column.getColumnType());
+            String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
+            column.setHtmlType(htmlType);
+            column.setLength(columnLength);
+        } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) {
+            column.setJavaType(GenConstants.TYPE_DATE);
+            column.setHtmlType(GenConstants.HTML_DATETIME);
+        } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) {
+            column.setHtmlType(GenConstants.HTML_INPUT);
+
+            // 如果是浮点型 统一用BigDecimal
+            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
+            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {
+                column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
+            }
+            // 如果是整形
+            else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) {
+                column.setJavaType(GenConstants.TYPE_INTEGER);
+            }
+            // 长整形
+            else {
+                column.setJavaType(GenConstants.TYPE_LONG);
+            }
+        }
+
+        // 插入字段(默认所有字段都需要插入)
+        column.setIsInsert(GenConstants.REQUIRE);
+
+        // 编辑字段
+        if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) {
+            column.setIsEdit(GenConstants.REQUIRE);
+        }
+        // 列表字段
+        if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) {
+            column.setIsList(GenConstants.REQUIRE);
+        }
+        // 查询字段
+        if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) {
+            column.setIsQuery(GenConstants.REQUIRE);
+        }
+
+        // 查询字段类型
+        if (StringUtils.endsWithIgnoreCase(columnName, "name")) {
+            column.setQueryType(GenConstants.QUERY_LIKE);
+        }
+        // 状态字段设置单选框
+        if (StringUtils.endsWithIgnoreCase(columnName, "status")) {
+            column.setHtmlType(GenConstants.HTML_RADIO);
+        }
+        // 类型&性别字段设置下拉框
+        else if (StringUtils.endsWithIgnoreCase(columnName, "type")
+                || StringUtils.endsWithIgnoreCase(columnName, "sex")) {
+            column.setHtmlType(GenConstants.HTML_SELECT);
+        }
+        // 图片字段设置图片上传控件
+        else if (StringUtils.endsWithIgnoreCase(columnName, "image")) {
+            column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);
+        }
+        // 文件字段设置文件上传控件
+        else if (StringUtils.endsWithIgnoreCase(columnName, "file")) {
+            column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);
+        }
+        // 内容字段设置富文本控件
+        else if (StringUtils.endsWithIgnoreCase(columnName, "content")) {
+            column.setHtmlType(GenConstants.HTML_EDITOR);
+        }
+    }
+
+    /**
+     * 校验数组是否包含指定值
+     *
+     * @param arr         数组
+     * @param targetValue 值
+     * @return 是否包含
+     */
+    public static boolean arraysContains(String[] arr, String targetValue) {
+        return Arrays.asList(arr).contains(targetValue);
+    }
+
+    /**
+     * 获取模块名
+     *
+     * @param packageName 包名
+     * @return 模块名
+     */
+    public static String getModuleName(String packageName) {
+        int lastIndex = packageName.lastIndexOf(".");
+        int nameLength = packageName.length();
+        String moduleName = StringUtils.substring(packageName, lastIndex + 1, nameLength);
+        return moduleName;
+    }
+
+    /**
+     * 获取业务名
+     *
+     * @param tableName 表名
+     * @return 业务名
+     */
+    public static String getBusinessName(String tableName) {
+        int lastIndex = tableName.lastIndexOf("_");
+        int nameLength = tableName.length();
+        String businessName = StringUtils.substring(tableName, lastIndex + 1, nameLength);
+        return businessName;
+    }
+
+    /**
+     * 表名转换成Java类名
+     *
+     * @param tableName 表名称
+     * @return 类名
+     */
+    public static String convertClassName(String tableName) {
+        boolean autoRemovePre = GenConfig.getAutoRemovePre();
+        String tablePrefix = GenConfig.getTablePrefix();
+        if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {
+            String[] searchList = StringUtils.split(tablePrefix, ",");
+            tableName = replaceFirst(tableName, searchList);
+        }
+        return StringUtils.convertToCamelCase(tableName);
+    }
+
+    /**
+     * 批量替换前缀
+     *
+     * @param replacementm 替换值
+     * @param searchList   替换列表
+     * @return
+     */
+    public static String replaceFirst(String replacementm, String[] searchList) {
+        String text = replacementm;
+        for (String searchString : searchList) {
+            if (replacementm.startsWith(searchString)) {
+                text = replacementm.replaceFirst(searchString, "");
+                break;
+            }
+        }
+        return text;
+    }
+
+    /**
+     * 关键字替换
+     *
+     * @param text 需要被替换的名字
+     * @return 替换后的名字
+     */
+    public static String replaceText(String text) {
+        return RegExUtils.replaceAll(text, "(?:表|云)", "");
+    }
+
+    /**
+     * 获取数据库类型字段
+     *
+     * @param columnType 列类型
+     * @return 截取后的列类型
+     */
+    public static String getDbType(String columnType) {
+        if (StringUtils.indexOf(columnType, "(") > 0) {
+            return StringUtils.substringBefore(columnType, "(");
+        } else {
+            return columnType;
+        }
+    }
+
+    /**
+     * 获取字段长度
+     *
+     * @param columnType 列类型
+     * @return 截取后的列类型
+     */
+    public static Integer getColumnLength(String columnType) {
+        if (StringUtils.indexOf(columnType, "(") > 0) {
+            String length = StringUtils.substringBetween(columnType, "(", ")");
+            return Integer.valueOf(length);
+        } else {
+            return 0;
+        }
+    }
+}

+ 31 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/utils/VelocityInitializer.java

@@ -0,0 +1,31 @@
+package com.zd.base.gen.utils;
+
+import com.zd.common.core.constant.Constants;
+import org.apache.velocity.app.Velocity;
+
+import java.util.Properties;
+
+/**
+ * VelocityEngine工厂
+ *
+ * @author zd
+ */
+public class VelocityInitializer {
+    /**
+     * 初始化vm方法
+     */
+    public static void initVelocity() {
+        Properties p = new Properties();
+        try {
+            // 加载classpath目录下的vm文件
+            p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+            // 定义字符集
+            p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8);
+            p.setProperty(Velocity.OUTPUT_ENCODING, Constants.UTF8);
+            // 初始化Velocity引擎,指定配置Properties
+            Velocity.init(p);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 328 - 0
zd-modules/zd-base/src/main/java/com/zd/base/gen/utils/VelocityUtils.java

@@ -0,0 +1,328 @@
+package com.zd.base.gen.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.zd.common.core.constant.GenConstants;
+import com.zd.common.core.utils.DateUtils;
+import com.zd.base.gen.domain.GenTable;
+import com.zd.base.gen.domain.GenTableColumn;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.velocity.VelocityContext;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 模板工具类
+ *
+ * @author zd
+ */
+public class VelocityUtils {
+
+    private VelocityUtils() {
+        throw new IllegalStateException("VelocityUtils class");
+    }
+    /**
+     * 项目空间路径
+     */
+    private static final String PROJECT_PATH = "main/java";
+
+    /**
+     * mybatis空间路径
+     */
+    private static final String MYBATIS_PATH = "main/resources/mapper";
+
+    /**
+     * 默认上级菜单,系统工具
+     */
+    private static final String DEFAULT_PARENT_MENU_ID = "3";
+
+    /**
+     * 设置模板变量信息
+     *
+     * @return 模板列表
+     */
+    public static VelocityContext prepareContext(GenTable genTable) {
+        String moduleName = genTable.getModuleName();
+        String businessName = genTable.getBusinessName();
+        String packageName = genTable.getPackageName();
+        String tplCategory = genTable.getTplCategory();
+        String functionName = genTable.getFunctionName();
+
+        VelocityContext velocityContext = new VelocityContext();
+        velocityContext.put("tplCategory", genTable.getTplCategory());
+        velocityContext.put("tableName", genTable.getTableName());
+        velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
+        velocityContext.put("ClassName", genTable.getClassName());
+        velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
+        velocityContext.put("moduleName", genTable.getModuleName());
+        velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
+        velocityContext.put("businessName", genTable.getBusinessName());
+        velocityContext.put("basePackage", getPackagePrefix(packageName));
+        velocityContext.put("packageName", packageName);
+        velocityContext.put("author", genTable.getFunctionAuthor());
+        velocityContext.put("datetime", DateUtils.getDate());
+        velocityContext.put("pkColumn", genTable.getPkColumn());
+        velocityContext.put("importList", getImportList(genTable));
+        velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
+        velocityContext.put("columns", genTable.getColumns());
+        velocityContext.put("table", genTable);
+        setMenuVelocityContext(velocityContext, genTable);
+        if (GenConstants.TPL_TREE.equals(tplCategory)) {
+            setTreeVelocityContext(velocityContext, genTable);
+        }
+        if (GenConstants.TPL_SUB.equals(tplCategory)) {
+            setSubVelocityContext(velocityContext, genTable);
+        }
+        return velocityContext;
+    }
+
+    public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) {
+        String options = genTable.getOptions();
+        JSONObject paramsObj = JSON.parseObject(options);
+        String parentMenuId = getParentMenuId(paramsObj);
+        context.put("parentMenuId", parentMenuId);
+    }
+
+    public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) {
+        String options = genTable.getOptions();
+        JSONObject paramsObj = JSON.parseObject(options);
+        String treeCode = getTreecode(paramsObj);
+        String treeParentCode = getTreeParentCode(paramsObj);
+        String treeName = getTreeName(paramsObj);
+
+        context.put("treeCode", treeCode);
+        context.put("treeParentCode", treeParentCode);
+        context.put("treeName", treeName);
+        context.put("expandColumn", getExpandColumn(genTable));
+        if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
+            context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE));
+        }
+        if (paramsObj.containsKey(GenConstants.TREE_NAME)) {
+            context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME));
+        }
+    }
+
+    public static void setSubVelocityContext(VelocityContext context, GenTable genTable) {
+        GenTable subTable = genTable.getSubTable();
+        String subTableName = genTable.getSubTableName();
+        String subTableFkName = genTable.getSubTableFkName();
+        String subClassName = genTable.getSubTable().getClassName();
+        String subTableFkClassName = com.zd.common.core.utils.StringUtils.convertToCamelCase(subTableFkName);
+
+        context.put("subTable", subTable);
+        context.put("subTableName", subTableName);
+        context.put("subTableFkName", subTableFkName);
+        context.put("subTableFkClassName", subTableFkClassName);
+        context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName));
+        context.put("subClassName", subClassName);
+        context.put("subclassName", StringUtils.uncapitalize(subClassName));
+        context.put("subImportList", getImportList(genTable.getSubTable()));
+    }
+
+    /**
+     * 获取模板信息
+     *
+     * @return 模板列表
+     */
+    public static List<String> getTemplateList(String tplCategory) {
+        List<String> templates = new ArrayList<>();
+        templates.add("vm/java/domain.java.vm");
+        templates.add("vm/java/mapper.java.vm");
+        templates.add("vm/java/service.java.vm");
+        templates.add("vm/java/serviceImpl.java.vm");
+        templates.add("vm/java/controller.java.vm");
+        templates.add("vm/xml/mapper.xml.vm");
+        templates.add("vm/sql/sql.vm");
+        templates.add("vm/js/api.js.vm");
+        if (GenConstants.TPL_CRUD.equals(tplCategory)) {
+            templates.add("vm/vue/index.vue.vm");
+        } else if (GenConstants.TPL_TREE.equals(tplCategory)) {
+            templates.add("vm/vue/index-tree.vue.vm");
+        } else if (GenConstants.TPL_SUB.equals(tplCategory)) {
+            templates.add("vm/vue/index.vue.vm");
+            templates.add("vm/java/sub-domain.java.vm");
+        }
+        return templates;
+    }
+
+    /**
+     * 获取文件名
+     */
+    public static String getFileName(String template, GenTable genTable) {
+        // 文件名称
+        String fileName = "";
+        // 包路径
+        String packageName = genTable.getPackageName();
+        // 模块名
+        String moduleName = genTable.getModuleName();
+        // 大写类名
+        String className = genTable.getClassName();
+        // 业务名称
+        String businessName = genTable.getBusinessName();
+
+        String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
+        String mybatisPath = MYBATIS_PATH + "/" + moduleName;
+        String vuePath = "vue";
+
+        if (template.contains("domain.java.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/domain/{}.java", javaPath, className);
+        }
+        if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName());
+        } else if (template.contains("mapper.java.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
+        } else if (template.contains("service.java.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/service/I{}Service.java", javaPath, className);
+        } else if (template.contains("serviceImpl.java.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
+        } else if (template.contains("controller.java.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
+        } else if (template.contains("mapper.xml.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
+        } else if (template.contains("sql.vm")) {
+            fileName = businessName + "Menu.sql";
+        } else if (template.contains("api.js.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName);
+        } else if (template.contains("index.vue.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
+        } else if (template.contains("index-tree.vue.vm")) {
+            fileName = com.zd.common.core.utils.StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
+        }
+        return fileName;
+    }
+
+    /**
+     * 获取包前缀
+     *
+     * @param packageName 包名称
+     * @return 包前缀名称
+     */
+    public static String getPackagePrefix(String packageName) {
+        int lastIndex = packageName.lastIndexOf(".");
+        return StringUtils.substring(packageName, 0, lastIndex);
+    }
+
+    /**
+     * 根据列类型获取导入包
+     *
+     * @param genTable 业务表对象
+     * @return 返回需要导入的包列表
+     */
+    public static Set<String> getImportList(GenTable genTable) {
+        List<GenTableColumn> columns = genTable.getColumns();
+        GenTable subGenTable = genTable.getSubTable();
+        HashSet<String> importList = new HashSet<>();
+        if (com.zd.common.core.utils.StringUtils.isNotNull(subGenTable)) {
+            importList.add("java.util.List");
+        }
+        for (GenTableColumn column : columns) {
+            if (!genTable.isSuperColumn(column.getJavaField()) && GenConstants.TYPE_DATE.equals(column.getJavaType())) {
+                importList.add("java.util.Date");
+                importList.add("com.fasterxml.jackson.annotation.JsonFormat");
+            } else if (!genTable.isSuperColumn(column.getJavaField()) && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
+                importList.add("java.math.BigDecimal");
+            }
+            //验证相关的包
+            //NotBlank
+            if (column.isRequired() && GenConstants.TYPE_STRING.equals(column.getJavaType())) {
+                importList.add("javax.validation.constraints.NotBlank");
+                importList.add("org.hibernate.validator.constraints.Length");
+            }
+            if (column.isRequired() && !GenConstants.TYPE_STRING.equals(column.getJavaType())) {
+                importList.add("javax.validation.constraints.NotNull");
+            }
+
+        }
+        return importList;
+    }
+
+    /**
+     * 获取权限前缀
+     *
+     * @param moduleName   模块名称
+     * @param businessName 业务名称
+     * @return 返回权限前缀
+     */
+    public static String getPermissionPrefix(String moduleName, String businessName) {
+        return com.zd.common.core.utils.StringUtils.format("{}:{}", moduleName, businessName);
+    }
+
+    /**
+     * 获取上级菜单ID字段
+     *
+     * @param paramsObj 生成其他选项
+     * @return 上级菜单ID字段
+     */
+    public static String getParentMenuId(JSONObject paramsObj) {
+        if (com.zd.common.core.utils.StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)
+                && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) {
+            return paramsObj.getString(GenConstants.PARENT_MENU_ID);
+        }
+        return DEFAULT_PARENT_MENU_ID;
+    }
+
+    /**
+     * 获取树编码
+     *
+     * @param paramsObj 生成其他选项
+     * @return 树编码
+     */
+    public static String getTreecode(JSONObject paramsObj) {
+        if (paramsObj.containsKey(GenConstants.TREE_CODE)) {
+            return com.zd.common.core.utils.StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE));
+        }
+        return StringUtils.EMPTY;
+    }
+
+    /**
+     * 获取树父编码
+     *
+     * @param paramsObj 生成其他选项
+     * @return 树父编码
+     */
+    public static String getTreeParentCode(JSONObject paramsObj) {
+        if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
+            return com.zd.common.core.utils.StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE));
+        }
+        return StringUtils.EMPTY;
+    }
+
+    /**
+     * 获取树名称
+     *
+     * @param paramsObj 生成其他选项
+     * @return 树名称
+     */
+    public static String getTreeName(JSONObject paramsObj) {
+        if (paramsObj.containsKey(GenConstants.TREE_NAME)) {
+            return com.zd.common.core.utils.StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME));
+        }
+        return StringUtils.EMPTY;
+    }
+
+    /**
+     * 获取需要在哪一列上面显示展开按钮
+     *
+     * @param genTable 业务表对象
+     * @return 展开按钮列序号
+     */
+    public static int getExpandColumn(GenTable genTable) {
+        String options = genTable.getOptions();
+        JSONObject paramsObj = JSON.parseObject(options);
+        String treeName = paramsObj.getString(GenConstants.TREE_NAME);
+        int num = 0;
+        for (GenTableColumn column : genTable.getColumns()) {
+            if (column.isList()) {
+                num++;
+                String columnName = column.getColumnName();
+                if (columnName.equals(treeName)) {
+                    break;
+                }
+            }
+        }
+        return num;
+    }
+}

+ 55 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/config/ScheduleConfig.java

@@ -0,0 +1,55 @@
+package com.zd.base.job.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import javax.sql.DataSource;
+import java.util.Properties;
+
+/**
+ * 定时任务配置
+ *
+ * @author zd
+ */
+@Configuration
+public class ScheduleConfig {
+    @Bean
+    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
+        SchedulerFactoryBean factory = new SchedulerFactoryBean();
+        factory.setDataSource(dataSource);
+
+        // quartz参数
+        Properties prop = new Properties();
+        prop.put("org.quartz.scheduler.instanceName", "zdScheduler");
+        prop.put("org.quartz.scheduler.instanceId", "AUTO");
+        // 线程池配置
+        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
+        prop.put("org.quartz.threadPool.threadCount", "20");
+        prop.put("org.quartz.threadPool.threadPriority", "5");
+        // JobStore配置
+        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
+        // 集群配置
+        prop.put("org.quartz.jobStore.isClustered", "true");
+        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
+        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
+        prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
+
+        // sqlserver 启用
+        // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
+        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
+        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
+        factory.setQuartzProperties(prop);
+
+        factory.setSchedulerName("zdScheduler");
+        // 延时启动
+        factory.setStartupDelay(1);
+        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
+        // 可选,QuartzScheduler
+        // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
+        factory.setOverwriteExistingJobs(true);
+        // 设置自动启动,默认为true
+        factory.setAutoStartup(true);
+
+        return factory;
+    }
+}

+ 142 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/controller/SysJobController.java

@@ -0,0 +1,142 @@
+package com.zd.base.job.controller;
+
+import com.zd.base.job.domain.SysJob;
+import com.zd.base.job.service.ISysJobService;
+import com.zd.base.job.utils.CronUtils;
+import com.zd.common.core.constant.Constants;
+import com.zd.common.core.domain.per.PerFun;
+import com.zd.common.core.domain.per.PerPrefix;
+import com.zd.common.core.exception.job.TaskException;
+import com.zd.common.core.utils.SecurityUtils;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.common.core.utils.poi.ExcelUtil;
+import com.zd.common.core.web.controller.BaseController;
+import com.zd.common.core.web.domain.AjaxResult;
+import com.zd.common.core.web.page.TableDataInfo;
+import com.zd.common.log.annotation.Log;
+import com.zd.common.log.enums.BusinessType;
+import com.zd.common.security.annotation.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+import org.quartz.SchedulerException;
+
+/**
+ * 调度任务信息操作处理
+ *
+ * @author zd
+ */
+@RestController
+@RequestMapping("/job")
+public class SysJobController extends BaseController {
+    @Autowired
+    private ISysJobService jobService;
+
+    /**
+     * 查询定时任务列表
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.LIST)
+    @GetMapping("/list")
+    public TableDataInfo list(SysJob sysJob) {
+        startPage();
+        List<SysJob> list = jobService.selectJobList(sysJob);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出定时任务列表
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.EXPORT)
+    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, SysJob sysJob) throws IOException {
+        List<SysJob> list = jobService.selectJobList(sysJob);
+        ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
+        util.exportExcel(response, list, "定时任务");
+    }
+
+    /**
+     * 获取定时任务详细信息
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.QUERY)
+    @GetMapping(value = "/{jobId}")
+    public AjaxResult getInfo(@PathVariable("jobId") Long jobId) {
+        return AjaxResult.success(jobService.selectJobById(jobId));
+    }
+
+    /**
+     * 新增定时任务
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.ADD)
+    @Log(title = "定时任务", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException {
+        if (!CronUtils.isValid(job.getCronExpression())) {
+            return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+        } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) {
+            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
+        } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP)) {
+            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
+            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
+        }
+        job.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(jobService.insertJob(job));
+    }
+
+    /**
+     * 修改定时任务
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.EDIT)
+    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException {
+        if (!CronUtils.isValid(job.getCronExpression())) {
+            return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+        } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) {
+            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
+        } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP)) {
+            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
+            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
+        }
+        job.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(jobService.updateJob(job));
+    }
+
+    /**
+     * 定时任务状态修改
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.CHANGESTATUS)
+    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException {
+        SysJob newJob = jobService.selectJobById(job.getJobId());
+        newJob.setStatus(job.getStatus());
+        return toAjax(jobService.changeStatus(newJob));
+    }
+
+    /**
+     * 定时任务立即执行一次
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.CHANGESTATUS)
+    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+    @PutMapping("/run")
+    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException {
+        jobService.run(job);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 删除定时任务
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.REMOVE)
+    @Log(title = "定时任务", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{jobIds}")
+    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException {
+        jobService.deleteJobByIds(jobIds);
+        return AjaxResult.success();
+    }
+}

+ 83 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/controller/SysJobLogController.java

@@ -0,0 +1,83 @@
+package com.zd.base.job.controller;
+
+import com.zd.base.job.domain.SysJobLog;
+import com.zd.base.job.service.ISysJobLogService;
+import com.zd.common.core.domain.per.PerFun;
+import com.zd.common.core.domain.per.PerPrefix;
+import com.zd.common.core.utils.poi.ExcelUtil;
+import com.zd.common.core.web.controller.BaseController;
+import com.zd.common.core.web.domain.AjaxResult;
+import com.zd.common.core.web.page.TableDataInfo;
+import com.zd.common.log.annotation.Log;
+import com.zd.common.log.enums.BusinessType;
+import com.zd.common.security.annotation.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 调度日志操作处理
+ *
+ * @author zd
+ */
+@RestController
+@RequestMapping("/job/log")
+public class SysJobLogController extends BaseController {
+    @Autowired
+    private ISysJobLogService jobLogService;
+
+    /**
+     * 查询定时任务调度日志列表
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.LIST)
+    @GetMapping("/list")
+    public TableDataInfo list(SysJobLog sysJobLog) {
+        startPage();
+        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出定时任务调度日志列表
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.EXPORT)
+    @Log(title = "任务调度日志", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, SysJobLog sysJobLog) throws IOException {
+        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+        ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
+        util.exportExcel(response, list, "调度日志");
+    }
+
+    /**
+     * 根据调度编号获取详细信息
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.QUERY)
+    @GetMapping(value = "/{configId}")
+    public AjaxResult getInfo(@PathVariable Long jobLogId) {
+        return AjaxResult.success(jobLogService.selectJobLogById(jobLogId));
+    }
+
+    /**
+     * 删除定时任务调度日志
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.REMOVE)
+    @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{jobLogIds}")
+    public AjaxResult remove(@PathVariable Long[] jobLogIds) {
+        return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
+    }
+
+    /**
+     * 清空定时任务调度日志
+     */
+    @PreAuthorize(hasPermi = PerPrefix.LABORATORY_JOB + PerFun.REMOVE)
+    @Log(title = "调度日志", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/clean")
+    public AjaxResult clean() {
+        jobLogService.cleanJobLog();
+        return AjaxResult.success();
+    }
+}

+ 168 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/domain/SysJob.java

@@ -0,0 +1,168 @@
+package com.zd.base.job.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.zd.base.job.utils.CronUtils;
+import com.zd.common.core.annotation.Excel;
+import com.zd.common.core.annotation.Excel.ColumnType;
+import com.zd.common.core.constant.ScheduleConstants;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.common.core.web.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.util.Date;
+
+/**
+ * 定时任务调度表 sys_job
+ *
+ * @author zd
+ */
+public class SysJob extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 任务ID
+     */
+    @Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
+    private Long jobId;
+
+    /**
+     * 任务名称
+     */
+    @Excel(name = "任务名称")
+    private String jobName;
+
+    /**
+     * 任务组名
+     */
+    @Excel(name = "任务组名")
+    private String jobGroup;
+
+    /**
+     * 调用目标字符串
+     */
+    @Excel(name = "调用目标字符串")
+    private String invokeTarget;
+
+    /**
+     * cron执行表达式
+     */
+    @Excel(name = "执行表达式 ")
+    private String cronExpression;
+
+    /**
+     * cron计划策略
+     */
+    @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
+    private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
+
+    /**
+     * 是否并发执行(0允许 1禁止)
+     */
+    @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
+    private String concurrent;
+
+    /**
+     * 任务状态(0正常 1暂停)
+     */
+    @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
+    private String status;
+
+    public Long getJobId() {
+        return jobId;
+    }
+
+    public void setJobId(Long jobId) {
+        this.jobId = jobId;
+    }
+
+    @NotBlank(message = "任务名称不能为空")
+    @Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
+    public String getJobName() {
+        return jobName;
+    }
+
+    public void setJobName(String jobName) {
+        this.jobName = jobName;
+    }
+
+    public String getJobGroup() {
+        return jobGroup;
+    }
+
+    public void setJobGroup(String jobGroup) {
+        this.jobGroup = jobGroup;
+    }
+
+    @NotBlank(message = "调用目标字符串不能为空")
+    @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符")
+    public String getInvokeTarget() {
+        return invokeTarget;
+    }
+
+    public void setInvokeTarget(String invokeTarget) {
+        this.invokeTarget = invokeTarget;
+    }
+
+    @NotBlank(message = "Cron执行表达式不能为空")
+    @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
+    public String getCronExpression() {
+        return cronExpression;
+    }
+
+    public void setCronExpression(String cronExpression) {
+        this.cronExpression = cronExpression;
+    }
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    public Date getNextValidTime() {
+        if (StringUtils.isNotEmpty(cronExpression)) {
+            return CronUtils.getNextExecution(cronExpression);
+        }
+        return null;
+    }
+
+    public String getMisfirePolicy() {
+        return misfirePolicy;
+    }
+
+    public void setMisfirePolicy(String misfirePolicy) {
+        this.misfirePolicy = misfirePolicy;
+    }
+
+    public String getConcurrent() {
+        return concurrent;
+    }
+
+    public void setConcurrent(String concurrent) {
+        this.concurrent = concurrent;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("jobId", getJobId())
+                .append("jobName", getJobName())
+                .append("jobGroup", getJobGroup())
+                .append("cronExpression", getCronExpression())
+                .append("nextValidTime", getNextValidTime())
+                .append("misfirePolicy", getMisfirePolicy())
+                .append("concurrent", getConcurrent())
+                .append("status", getStatus())
+                .append("createBy", getCreateBy())
+                .append("createTime", getCreateTime())
+                .append("updateBy", getUpdateBy())
+                .append("updateTime", getUpdateTime())
+                .append("remark", getRemark())
+                .toString();
+    }
+}

+ 155 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/domain/SysJobLog.java

@@ -0,0 +1,155 @@
+package com.zd.base.job.domain;
+
+import com.zd.common.core.annotation.Excel;
+import com.zd.common.core.web.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.util.Date;
+
+/**
+ * 定时任务调度日志表 sys_job_log
+ *
+ * @author zd
+ */
+public class SysJobLog extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @Excel(name = "日志序号")
+    private Long jobLogId;
+
+    /**
+     * 任务名称
+     */
+    @Excel(name = "任务名称")
+    private String jobName;
+
+    /**
+     * 任务组名
+     */
+    @Excel(name = "任务组名")
+    private String jobGroup;
+
+    /**
+     * 调用目标字符串
+     */
+    @Excel(name = "调用目标字符串")
+    private String invokeTarget;
+
+    /**
+     * 日志信息
+     */
+    @Excel(name = "日志信息")
+    private String jobMessage;
+
+    /**
+     * 执行状态(0正常 1失败)
+     */
+    @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
+    private String status;
+
+    /**
+     * 异常信息
+     */
+    @Excel(name = "异常信息")
+    private String exceptionInfo;
+
+    /**
+     * 开始时间
+     */
+    private Date startTime;
+
+    /**
+     * 停止时间
+     */
+    private Date stopTime;
+
+    public Long getJobLogId() {
+        return jobLogId;
+    }
+
+    public void setJobLogId(Long jobLogId) {
+        this.jobLogId = jobLogId;
+    }
+
+    public String getJobName() {
+        return jobName;
+    }
+
+    public void setJobName(String jobName) {
+        this.jobName = jobName;
+    }
+
+    public String getJobGroup() {
+        return jobGroup;
+    }
+
+    public void setJobGroup(String jobGroup) {
+        this.jobGroup = jobGroup;
+    }
+
+    public String getInvokeTarget() {
+        return invokeTarget;
+    }
+
+    public void setInvokeTarget(String invokeTarget) {
+        this.invokeTarget = invokeTarget;
+    }
+
+    public String getJobMessage() {
+        return jobMessage;
+    }
+
+    public void setJobMessage(String jobMessage) {
+        this.jobMessage = jobMessage;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getExceptionInfo() {
+        return exceptionInfo;
+    }
+
+    public void setExceptionInfo(String exceptionInfo) {
+        this.exceptionInfo = exceptionInfo;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getStopTime() {
+        return stopTime;
+    }
+
+    public void setStopTime(Date stopTime) {
+        this.stopTime = stopTime;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("jobLogId", getJobLogId())
+                .append("jobName", getJobName())
+                .append("jobGroup", getJobGroup())
+                .append("jobMessage", getJobMessage())
+                .append("status", getStatus())
+                .append("exceptionInfo", getExceptionInfo())
+                .append("startTime", getStartTime())
+                .append("stopTime", getStopTime())
+                .toString();
+    }
+}

+ 65 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/mapper/SysJobLogMapper.java

@@ -0,0 +1,65 @@
+package com.zd.base.job.mapper;
+
+
+import com.zd.base.job.domain.SysJobLog;
+
+import java.util.List;
+
+/**
+ * 调度任务日志信息 数据层
+ *
+ * @author zd
+ */
+public interface SysJobLogMapper {
+    /**
+     * 获取quartz调度器日志的计划任务
+     *
+     * @param jobLog 调度日志信息
+     * @return 调度任务日志集合
+     */
+    public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
+
+    /**
+     * 查询所有调度任务日志
+     *
+     * @return 调度任务日志列表
+     */
+    public List<SysJobLog> selectJobLogAll();
+
+    /**
+     * 通过调度任务日志ID查询调度信息
+     *
+     * @param jobLogId 调度任务日志ID
+     * @return 调度任务日志对象信息
+     */
+    public SysJobLog selectJobLogById(Long jobLogId);
+
+    /**
+     * 新增任务日志
+     *
+     * @param jobLog 调度日志信息
+     * @return 结果
+     */
+    public int insertJobLog(SysJobLog jobLog);
+
+    /**
+     * 批量删除调度日志信息
+     *
+     * @param logIds 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteJobLogByIds(Long[] logIds);
+
+    /**
+     * 删除任务日志
+     *
+     * @param jobId 调度日志ID
+     * @return 结果
+     */
+    public int deleteJobLogById(Long jobId);
+
+    /**
+     * 清空任务日志
+     */
+    public void cleanJobLog();
+}

+ 68 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/mapper/SysJobMapper.java

@@ -0,0 +1,68 @@
+package com.zd.base.job.mapper;
+
+
+import com.zd.base.job.domain.SysJob;
+
+import java.util.List;
+
+/**
+ * 调度任务信息 数据层
+ *
+ * @author zd
+ */
+public interface SysJobMapper {
+    /**
+     * 查询调度任务日志集合
+     *
+     * @param job 调度信息
+     * @return 操作日志集合
+     */
+    public List<SysJob> selectJobList(SysJob job);
+
+    /**
+     * 查询所有调度任务
+     *
+     * @return 调度任务列表
+     */
+    public List<SysJob> selectJobAll();
+
+    /**
+     * 通过调度ID查询调度任务信息
+     *
+     * @param jobId 调度ID
+     * @return 角色对象信息
+     */
+    public SysJob selectJobById(Long jobId);
+
+    /**
+     * 通过调度ID删除调度任务信息
+     *
+     * @param jobId 调度ID
+     * @return 结果
+     */
+    public int deleteJobById(Long jobId);
+
+    /**
+     * 批量删除调度任务信息
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteJobByIds(Long[] ids);
+
+    /**
+     * 修改调度任务信息
+     *
+     * @param job 调度任务信息
+     * @return 结果
+     */
+    public int updateJob(SysJob job);
+
+    /**
+     * 新增调度任务信息
+     *
+     * @param job 调度任务信息
+     * @return 结果
+     */
+    public int insertJob(SysJob job);
+}

+ 56 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/service/ISysJobLogService.java

@@ -0,0 +1,56 @@
+package com.zd.base.job.service;
+
+import com.zd.base.job.domain.SysJobLog;
+
+import java.util.List;
+
+/**
+ * 定时任务调度日志信息信息 服务层
+ *
+ * @author zd
+ */
+public interface ISysJobLogService {
+    /**
+     * 获取quartz调度器日志的计划任务
+     *
+     * @param jobLog 调度日志信息
+     * @return 调度任务日志集合
+     */
+    public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
+
+    /**
+     * 通过调度任务日志ID查询调度信息
+     *
+     * @param jobLogId 调度任务日志ID
+     * @return 调度任务日志对象信息
+     */
+    public SysJobLog selectJobLogById(Long jobLogId);
+
+    /**
+     * 新增任务日志
+     *
+     * @param jobLog 调度日志信息
+     */
+    public void addJobLog(SysJobLog jobLog);
+
+    /**
+     * 批量删除调度日志信息
+     *
+     * @param logIds 需要删除的日志ID
+     * @return 结果
+     */
+    public int deleteJobLogByIds(Long[] logIds);
+
+    /**
+     * 删除任务日志
+     *
+     * @param jobId 调度日志ID
+     * @return 结果
+     */
+    public int deleteJobLogById(Long jobId);
+
+    /**
+     * 清空任务日志
+     */
+    public void cleanJobLog();
+}

+ 102 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/service/ISysJobService.java

@@ -0,0 +1,102 @@
+package com.zd.base.job.service;
+
+import com.zd.common.core.exception.job.TaskException;
+import com.zd.base.job.domain.SysJob;
+import org.quartz.SchedulerException;
+
+import java.util.List;
+
+/**
+ * 定时任务调度信息信息 服务层
+ *
+ * @author zd
+ */
+public interface ISysJobService {
+    /**
+     * 获取quartz调度器的计划任务
+     *
+     * @param job 调度信息
+     * @return 调度任务集合
+     */
+    public List<SysJob> selectJobList(SysJob job);
+
+    /**
+     * 通过调度任务ID查询调度信息
+     *
+     * @param jobId 调度任务ID
+     * @return 调度任务对象信息
+     */
+    public SysJob selectJobById(Long jobId);
+
+    /**
+     * 暂停任务
+     *
+     * @param job 调度信息
+     * @return 结果
+     */
+    public int pauseJob(SysJob job) throws SchedulerException;
+
+    /**
+     * 恢复任务
+     *
+     * @param job 调度信息
+     * @return 结果
+     */
+    public int resumeJob(SysJob job) throws SchedulerException;
+
+    /**
+     * 删除任务后,所对应的trigger也将被删除
+     *
+     * @param job 调度信息
+     * @return 结果
+     */
+    public int deleteJob(SysJob job) throws SchedulerException;
+
+    /**
+     * 批量删除调度信息
+     *
+     * @param jobIds 需要删除的任务ID
+     * @return 结果
+     */
+    public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
+
+    /**
+     * 任务调度状态修改
+     *
+     * @param job 调度信息
+     * @return 结果
+     */
+    public int changeStatus(SysJob job) throws SchedulerException;
+
+    /**
+     * 立即运行任务
+     *
+     * @param job 调度信息
+     * @return 结果
+     */
+    public void run(SysJob job) throws SchedulerException;
+
+    /**
+     * 新增任务
+     *
+     * @param job 调度信息
+     * @return 结果
+     */
+    public int insertJob(SysJob job) throws SchedulerException, TaskException;
+
+    /**
+     * 更新任务
+     *
+     * @param job 调度信息
+     * @return 结果
+     */
+    public int updateJob(SysJob job) throws SchedulerException, TaskException;
+
+    /**
+     * 校验cron表达式是否有效
+     *
+     * @param cronExpression 表达式
+     * @return 结果
+     */
+    public boolean checkCronExpressionIsValid(String cronExpression);
+}

+ 81 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/service/impl/SysJobLogServiceImpl.java

@@ -0,0 +1,81 @@
+package com.zd.base.job.service.impl;
+
+import com.zd.base.job.domain.SysJobLog;
+import com.zd.base.job.mapper.SysJobLogMapper;
+import com.zd.base.job.service.ISysJobLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 定时任务调度日志信息 服务层
+ *
+ * @author zd
+ */
+@Service
+public class SysJobLogServiceImpl implements ISysJobLogService {
+    @Autowired
+    private SysJobLogMapper jobLogMapper;
+
+    /**
+     * 获取quartz调度器日志的计划任务
+     *
+     * @param jobLog 调度日志信息
+     * @return 调度任务日志集合
+     */
+    @Override
+    public List<SysJobLog> selectJobLogList(SysJobLog jobLog) {
+        return jobLogMapper.selectJobLogList(jobLog);
+    }
+
+    /**
+     * 通过调度任务日志ID查询调度信息
+     *
+     * @param jobLogId 调度任务日志ID
+     * @return 调度任务日志对象信息
+     */
+    @Override
+    public SysJobLog selectJobLogById(Long jobLogId) {
+        return jobLogMapper.selectJobLogById(jobLogId);
+    }
+
+    /**
+     * 新增任务日志
+     *
+     * @param jobLog 调度日志信息
+     */
+    @Override
+    public void addJobLog(SysJobLog jobLog) {
+        jobLogMapper.insertJobLog(jobLog);
+    }
+
+    /**
+     * 批量删除调度日志信息
+     *
+     * @param logIds 需要删除的数据ID
+     * @return 结果
+     */
+    @Override
+    public int deleteJobLogByIds(Long[] logIds) {
+        return jobLogMapper.deleteJobLogByIds(logIds);
+    }
+
+    /**
+     * 删除任务日志
+     *
+     * @param jobId 调度日志ID
+     */
+    @Override
+    public int deleteJobLogById(Long jobId) {
+        return jobLogMapper.deleteJobLogById(jobId);
+    }
+
+    /**
+     * 清空任务日志
+     */
+    @Override
+    public void cleanJobLog() {
+        jobLogMapper.cleanJobLog();
+    }
+}

+ 229 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/service/impl/SysJobServiceImpl.java

@@ -0,0 +1,229 @@
+package com.zd.base.job.service.impl;
+
+import com.zd.base.job.service.ISysJobService;
+import com.zd.base.job.utils.CronUtils;
+import com.zd.base.job.utils.ScheduleUtils;
+import com.zd.common.core.constant.ScheduleConstants;
+import com.zd.common.core.exception.job.TaskException;
+import com.zd.base.job.domain.SysJob;
+import com.zd.base.job.mapper.SysJobMapper;
+import org.quartz.JobDataMap;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.PostConstruct;
+import java.util.List;
+
+/**
+ * 定时任务调度信息 服务层
+ *
+ * @author zd
+ */
+@Service
+public class SysJobServiceImpl implements ISysJobService {
+    @Autowired
+    private Scheduler scheduler;
+
+    @Autowired
+    private SysJobMapper jobMapper;
+
+    /**
+     * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
+     */
+    @PostConstruct
+    public void init() throws SchedulerException, TaskException {
+        scheduler.clear();
+        List<SysJob> jobList = jobMapper.selectJobAll();
+        for (SysJob job : jobList) {
+            ScheduleUtils.createScheduleJob(scheduler, job);
+        }
+    }
+
+    /**
+     * 获取quartz调度器的计划任务列表
+     *
+     * @param job 调度信息
+     * @return
+     */
+    @Override
+    public List<SysJob> selectJobList(SysJob job) {
+        return jobMapper.selectJobList(job);
+    }
+
+    /**
+     * 通过调度任务ID查询调度信息
+     *
+     * @param jobId 调度任务ID
+     * @return 调度任务对象信息
+     */
+    @Override
+    public SysJob selectJobById(Long jobId) {
+        return jobMapper.selectJobById(jobId);
+    }
+
+    /**
+     * 暂停任务
+     *
+     * @param job 调度信息
+     */
+    @Override
+    @Transactional
+    public int pauseJob(SysJob job) throws SchedulerException {
+        Long jobId = job.getJobId();
+        String jobGroup = job.getJobGroup();
+        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+        int rows = jobMapper.updateJob(job);
+        if (rows > 0) {
+            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+        }
+        return rows;
+    }
+
+    /**
+     * 恢复任务
+     *
+     * @param job 调度信息
+     */
+    @Override
+    @Transactional
+    public int resumeJob(SysJob job) throws SchedulerException {
+        Long jobId = job.getJobId();
+        String jobGroup = job.getJobGroup();
+        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
+        int rows = jobMapper.updateJob(job);
+        if (rows > 0) {
+            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+        }
+        return rows;
+    }
+
+    /**
+     * 删除任务后,所对应的trigger也将被删除
+     *
+     * @param job 调度信息
+     */
+    @Override
+    @Transactional
+    public int deleteJob(SysJob job) throws SchedulerException {
+        Long jobId = job.getJobId();
+        String jobGroup = job.getJobGroup();
+        int rows = jobMapper.deleteJobById(jobId);
+        if (rows > 0) {
+            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+        }
+        return rows;
+    }
+
+    /**
+     * 批量删除调度信息
+     *
+     * @param jobIds 需要删除的任务ID
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public void deleteJobByIds(Long[] jobIds) throws SchedulerException {
+        for (Long jobId : jobIds) {
+            SysJob job = jobMapper.selectJobById(jobId);
+            deleteJob(job);
+        }
+    }
+
+    /**
+     * 任务调度状态修改
+     *
+     * @param job 调度信息
+     */
+    @Override
+    @Transactional
+    public int changeStatus(SysJob job) throws SchedulerException {
+        int rows = 0;
+        String status = job.getStatus();
+        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
+            rows = resumeJob(job);
+        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
+            rows = pauseJob(job);
+        }
+        return rows;
+    }
+
+    /**
+     * 立即运行任务
+     *
+     * @param job 调度信息
+     */
+    @Override
+    @Transactional
+    public void run(SysJob job) throws SchedulerException {
+        Long jobId = job.getJobId();
+        String jobGroup = job.getJobGroup();
+        SysJob properties = selectJobById(job.getJobId());
+        // 参数
+        JobDataMap dataMap = new JobDataMap();
+        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
+        scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
+    }
+
+    /**
+     * 新增任务
+     *
+     * @param job 调度信息 调度信息
+     */
+    @Override
+    @Transactional
+    public int insertJob(SysJob job) throws SchedulerException, TaskException {
+        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+        int rows = jobMapper.insertJob(job);
+        if (rows > 0) {
+            ScheduleUtils.createScheduleJob(scheduler, job);
+        }
+        return rows;
+    }
+
+    /**
+     * 更新任务的时间表达式
+     *
+     * @param job 调度信息
+     */
+    @Override
+    @Transactional
+    public int updateJob(SysJob job) throws SchedulerException, TaskException {
+        SysJob properties = selectJobById(job.getJobId());
+        int rows = jobMapper.updateJob(job);
+        if (rows > 0) {
+            updateSchedulerJob(job, properties.getJobGroup());
+        }
+        return rows;
+    }
+
+    /**
+     * 更新任务
+     *
+     * @param job      任务对象
+     * @param jobGroup 任务组名
+     */
+    public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException {
+        Long jobId = job.getJobId();
+        // 判断是否存在
+        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
+        if (scheduler.checkExists(jobKey)) {
+            // 防止创建时存在数据问题 先移除,然后在执行创建操作
+            scheduler.deleteJob(jobKey);
+        }
+        ScheduleUtils.createScheduleJob(scheduler, job);
+    }
+
+    /**
+     * 校验cron表达式是否有效
+     *
+     * @param cronExpression 表达式
+     * @return 结果
+     */
+    @Override
+    public boolean checkCronExpressionIsValid(String cronExpression) {
+        return CronUtils.isValid(cronExpression);
+    }
+}

+ 47 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/task/ChemicalTask.java

@@ -0,0 +1,47 @@
+package com.zd.base.job.task;
+
+import com.zd.system.api.chemical.RemoteStockService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component("chemicalTask")
+public class ChemicalTask {
+
+    @Autowired
+    private RemoteStockService remoteStockService;
+
+    /**
+     * 化学品过期检测
+     */
+    public void expireCheck(){
+        remoteStockService.expireCheck();
+    }
+
+    /**
+     * 检测领用超时
+     */
+    public void checkOvertime(){
+        remoteStockService.checkOvertime();
+    }
+
+    /**
+     * 定时监测实验室化学品存放风险指标
+     */
+    public void indicatorMonitoring() {
+        remoteStockService.indicatorMonitoring();
+    }
+
+    /**
+     * 定时监测四医大短信上行结果
+     */
+    public void queryUplinkResult(){
+        remoteStockService.queryUplinkResult();
+    }
+
+    /**
+     * 四医大短信告警方案阶梯式通知流程
+     */
+    public void sendPhoneBySyd(){
+        remoteStockService.sendPhoneBySyd();
+    }
+}

+ 57 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/task/ExamTask.java

@@ -0,0 +1,57 @@
+package com.zd.base.job.task;
+
+import com.zd.common.core.domain.R;
+import com.zd.system.api.exam.RemoteExamService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 考试定时调度任务
+ *
+ * @author liubo
+ */
+@Component("examTask")
+public class ExamTask {
+
+    @Autowired
+    private RemoteExamService remoteExamService;
+
+    /**
+     * 超时弃考 调度任务
+     */
+    public void abandonPaper() {
+        remoteExamService.abandonPaper();
+    }
+
+    /**
+     * 一个月无违规获取配置表的奖励分 调度任务
+     */
+    public void getPointByNoViolation() {
+        Map map = new HashMap();
+        R r = remoteExamService.getBonusPointsConfig(map);
+        List <Map<String,Object>> list = (List<Map<String,Object>>) r.getData();
+        if(list.size()>0){
+            Map<String,Object> noViolation = list.get(0);
+            Integer monthNoviolationScore= Integer.parseInt(noViolation.get("monthNoviolationScore")+"");
+            if(monthNoviolationScore!=null){
+                Map violationMap = new HashMap();
+                R rlv = remoteExamService.getLastMonthViolation(violationMap);
+                List <Map<String,String>> recordList = (List <Map<String,String>>) rlv.getData();
+                for(Map key:recordList){
+                    Map <String,Object> obtainMap = new HashMap<>();
+                    // 用户
+                    obtainMap.put("joinUserId", key.get("joinUserId"));
+                    // 奖励的分数
+                    obtainMap.put("deductPoints", monthNoviolationScore);
+                    // 奖励的原因
+                    obtainMap.put("reason", "一个月无违规,奖励积分:"+monthNoviolationScore);
+                    remoteExamService.obtainBonusPoints(obtainMap);
+                }
+            }
+        }
+    }
+}

+ 103 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/task/FileViewTask.java

@@ -0,0 +1,103 @@
+package com.zd.base.job.task;
+
+import com.zd.common.core.domain.R;
+import com.zd.common.redis.service.RedisService;
+import com.zd.system.api.exam.RemoteExamService;
+import com.zd.system.api.exam.domain.ElResources;
+import com.zd.system.api.kkFile.RemoteKkFileService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.util.Base64;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 文件预览
+ */
+@Component("fileViewTask")
+public class FileViewTask {
+
+    private static Logger logger = LoggerFactory.getLogger(FileViewTask.class);
+
+    /** 固定的线程池(当前线程池大小为30) */
+    private static final ExecutorService executor = Executors.newFixedThreadPool(30);
+
+    @Autowired
+    private RemoteKkFileService remoteKkFileService;
+    @Autowired
+    private RedisService redisService;
+    @Autowired
+    private RemoteExamService remoteExamService;
+
+    private URI url;
+    {
+        try {
+            url = new URI("http://127.0.0.1:8012");
+        } catch (URISyntaxException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     *  两个要点:
+     *  1.用Executors实现固定大小的线程池,从而达到控制硬件资源消耗的目的。
+     *  2.用CountDownLatch(闭锁),来确保循环内的多线程都执行完成后,再执行后续代码
+     */
+    public void cacheKkFile() throws InterruptedException {
+        logger.info("***********文件分片任务开始***********");
+        R resourcesR = remoteExamService.listAll();
+        if (resourcesR.getCode() != 200) {
+            return;
+        }
+        List<ElResources> resourcesList = (List<ElResources>) resourcesR.getData();
+        for(ElResources resources:resourcesList){
+            resources.setPath("http://192.168.251.2/labSystem/"+resources.getPath());
+        }
+        if(null!=resourcesList && resourcesList.size()>0){
+            logger.info("resourcesList.size()***********"+resourcesList.size()+"***********");
+        }
+        // 初始化计时器
+        CountDownLatch cdl = new CountDownLatch(resourcesList.size());
+        for (ElResources resources : resourcesList) {
+            executor.submit(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        logger.info("ThreadName"+Thread.currentThread().getName()+"resourcesId:"+resources.getId());
+                        getKkFileAsync(resources.getPath());
+                    }catch (Exception e){
+                        e.printStackTrace();
+                        logger.info("error msg:"+e.getMessage());
+                    }
+                    // 闭锁-1
+                    cdl.countDown();
+                }
+            });
+        }
+        try {
+            cdl.await();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        //关闭线程池
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+        logger.info("***********文件分片任务完成***********");
+    }
+
+    public String getKkFileAsync(String resource) {
+        String resultStr = remoteKkFileService.getKkFilePath(url, URLEncoder.encode(Base64.getEncoder().encodeToString(resource.getBytes())));
+        if ("image".equals(resultStr)) {
+            logger.info("***********远程分片操作成功***********");
+        }
+        return resultStr;
+    }
+}

+ 64 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/task/LabTask.java

@@ -0,0 +1,64 @@
+package com.zd.base.job.task;
+
+import com.zd.system.api.laboratory.RemoteLaboratoryService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 实验室调度任务
+ *
+ * @author liubo
+ */
+@Component("labTask")
+public class LabTask {
+
+    @Autowired
+    private RemoteLaboratoryService remoteLaboratoryService;
+
+    /**
+     * 定时器定时查询离开时间超过24小时,记违规
+     */
+    public void outTimeRecord() {
+        remoteLaboratoryService.outTimeRecord();
+    }
+
+    /**
+     * 定时任务分级管控工作过期记录 - 并排列下次工作计划
+     */
+    public void workArrange() {
+        remoteLaboratoryService.workArrange();
+    }
+
+    /**
+     * 定时任务 早8点 管控工作逾期通知
+     */
+    public void workOverdue(){
+        remoteLaboratoryService.workOverdue();
+    };
+
+    /**
+     * 定时任务 下午7点 天管控工作执行通知
+     */
+    public void workExecuteByDay(){
+        remoteLaboratoryService.workExecuteByDay();
+    };
+
+    /**
+     * 定时任务 早8点 其他管控工作执行通知
+     */
+    public void workExecute(){
+        remoteLaboratoryService.workExecute();
+    };
+
+    /**
+     * 定时器定时查询预案向一体机推送的消息,如果超过两小时,需要清理
+     */
+    public void outTimeClearMessage() {
+        remoteLaboratoryService.outTimeClearMessage();
+    }
+
+    /**
+     * 安全检查隐患项未整改逾期通知
+     */
+    public  void checkSendMsgBeOverdue(){remoteLaboratoryService.checkSendMsgBeOverdue();}
+}

+ 32 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/task/RyTask.java

@@ -0,0 +1,32 @@
+package com.zd.base.job.task;
+
+import com.zd.common.core.utils.StringUtils;
+import com.zd.base.job.domain.SysJob;
+import com.zd.base.job.service.ISysJobService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 定时任务调度测试
+ *
+ * @author zd
+ */
+@Component("ryTask")
+public class RyTask {
+    @Autowired
+    private ISysJobService jobService;
+
+    public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) {
+        System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
+    }
+
+    public void ryParams(String params) {
+        SysJob job = new SysJob();
+        jobService.selectJobList(job);
+        System.out.println("执行有参方法:" + params);
+    }
+
+    public void ryNoParams() {
+        System.out.println("执行无参方法");
+    }
+}

+ 96 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/utils/AbstractQuartzJob.java

@@ -0,0 +1,96 @@
+package com.zd.base.job.utils;
+
+import com.zd.common.core.constant.ScheduleConstants;
+import com.zd.common.core.utils.ExceptionUtil;
+import com.zd.common.core.utils.SpringUtils;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.common.core.utils.bean.BeanUtils;
+import com.zd.base.job.domain.SysJob;
+import com.zd.base.job.domain.SysJobLog;
+import com.zd.base.job.service.ISysJobLogService;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+/**
+ * 抽象quartz调用
+ *
+ * @author zd
+ */
+public abstract class AbstractQuartzJob implements Job {
+    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
+
+    /**
+     * 线程本地变量
+     */
+    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
+
+    @Override
+    public void execute(JobExecutionContext context) throws JobExecutionException {
+        SysJob sysJob = new SysJob();
+        BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
+        try {
+            before(context, sysJob);
+            if (sysJob != null) {
+                doExecute(context, sysJob);
+            }
+            after(context, sysJob, null);
+        } catch (Exception e) {
+            log.error("任务执行异常  - :", e);
+            after(context, sysJob, e);
+        }
+    }
+
+    /**
+     * 执行前
+     *
+     * @param context 工作执行上下文对象
+     * @param sysJob  系统计划任务
+     */
+    protected void before(JobExecutionContext context, SysJob sysJob) {
+        threadLocal.set(new Date());
+    }
+
+    /**
+     * 执行后
+     *
+     * @param context 工作执行上下文对象
+     * @param sysJob  系统计划任务
+     */
+    protected void after(JobExecutionContext context, SysJob sysJob, Exception e) {
+        Date startTime = threadLocal.get();
+        threadLocal.remove();
+
+        final SysJobLog sysJobLog = new SysJobLog();
+        sysJobLog.setJobName(sysJob.getJobName());
+        sysJobLog.setJobGroup(sysJob.getJobGroup());
+        sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
+        sysJobLog.setStartTime(startTime);
+        sysJobLog.setStopTime(new Date());
+        long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
+        sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
+        if (e != null) {
+            sysJobLog.setStatus("1");
+            String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
+            sysJobLog.setExceptionInfo(errorMsg);
+        } else {
+            sysJobLog.setStatus("0");
+        }
+
+        // 写入数据库当中
+        SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
+    }
+
+    /**
+     * 执行方法,由子类重载
+     *
+     * @param context 工作执行上下文对象
+     * @param sysJob  系统计划任务
+     * @throws Exception 执行过程中的异常
+     */
+    protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
+}

+ 53 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/utils/CronUtils.java

@@ -0,0 +1,53 @@
+package com.zd.base.job.utils;
+
+import org.quartz.CronExpression;
+
+import java.text.ParseException;
+import java.util.Date;
+
+/**
+ * cron表达式工具类
+ *
+ * @author zd
+ */
+public class CronUtils {
+    /**
+     * 返回一个布尔值代表一个给定的Cron表达式的有效性
+     *
+     * @param cronExpression Cron表达式
+     * @return boolean 表达式是否有效
+     */
+    public static boolean isValid(String cronExpression) {
+        return CronExpression.isValidExpression(cronExpression);
+    }
+
+    /**
+     * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
+     *
+     * @param cronExpression Cron表达式
+     * @return String 无效时返回表达式错误描述,如果有效返回null
+     */
+    public static String getInvalidMessage(String cronExpression) {
+        try {
+            new CronExpression(cronExpression);
+            return null;
+        } catch (ParseException pe) {
+            return pe.getMessage();
+        }
+    }
+
+    /**
+     * 返回下一个执行时间根据给定的Cron表达式
+     *
+     * @param cronExpression Cron表达式
+     * @return Date 下次Cron表达式执行时间
+     */
+    public static Date getNextExecution(String cronExpression) {
+        try {
+            CronExpression cron = new CronExpression(cronExpression);
+            return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
+        } catch (ParseException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        }
+    }
+}

+ 158 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/utils/JobInvokeUtil.java

@@ -0,0 +1,158 @@
+package com.zd.base.job.utils;
+
+import com.zd.common.core.utils.SpringUtils;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.base.job.domain.SysJob;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 任务执行工具
+ *
+ * @author zd
+ */
+public class JobInvokeUtil {
+    /**
+     * 执行方法
+     *
+     * @param sysJob 系统任务
+     */
+    public static void invokeMethod(SysJob sysJob) throws Exception {
+        String invokeTarget = sysJob.getInvokeTarget();
+        String beanName = getBeanName(invokeTarget);
+        String methodName = getMethodName(invokeTarget);
+        List<Object[]> methodParams = getMethodParams(invokeTarget);
+
+        if (!isValidClassName(beanName)) {
+            Object bean = SpringUtils.getBean(beanName);
+            invokeMethod(bean, methodName, methodParams);
+        } else {
+            Object bean = Class.forName(beanName).newInstance();
+            invokeMethod(bean, methodName, methodParams);
+        }
+    }
+
+    /**
+     * 调用任务方法
+     *
+     * @param bean         目标对象
+     * @param methodName   方法名称
+     * @param methodParams 方法参数
+     */
+    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
+            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
+            InvocationTargetException {
+        if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) {
+            Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
+            method.invoke(bean, getMethodParamsValue(methodParams));
+        } else {
+            Method method = bean.getClass().getDeclaredMethod(methodName);
+            method.invoke(bean);
+        }
+    }
+
+    /**
+     * 校验是否为为class包名
+     *
+     * @param invokeTarget 名称
+     * @return true是 false否
+     */
+    public static boolean isValidClassName(String invokeTarget) {
+        return StringUtils.countMatches(invokeTarget, ".") > 1;
+    }
+
+    /**
+     * 获取bean名称
+     *
+     * @param invokeTarget 目标字符串
+     * @return bean名称
+     */
+    public static String getBeanName(String invokeTarget) {
+        String beanName = StringUtils.substringBefore(invokeTarget, "(");
+        return StringUtils.substringBeforeLast(beanName, ".");
+    }
+
+    /**
+     * 获取bean方法
+     *
+     * @param invokeTarget 目标字符串
+     * @return method方法
+     */
+    public static String getMethodName(String invokeTarget) {
+        String methodName = StringUtils.substringBefore(invokeTarget, "(");
+        return StringUtils.substringAfterLast(methodName, ".");
+    }
+
+    /**
+     * 获取method方法参数相关列表
+     *
+     * @param invokeTarget 目标字符串
+     * @return method方法相关参数列表
+     */
+    public static List<Object[]> getMethodParams(String invokeTarget) {
+        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
+        if (StringUtils.isEmpty(methodStr)) {
+            return null;
+        }
+        String[] methodParams = methodStr.split(",");
+        List<Object[]> classs = new LinkedList<>();
+        for (int i = 0; i < methodParams.length; i++) {
+            String str = StringUtils.trimToEmpty(methodParams[i]);
+            // String字符串类型,包含'
+            if (StringUtils.contains(str, "'")) {
+                classs.add(new Object[]{StringUtils.replace(str, "'", ""), String.class});
+            }
+            // boolean布尔类型,等于true或者false
+            else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false")) {
+                classs.add(new Object[]{Boolean.valueOf(str), Boolean.class});
+            }
+            // long长整形,包含L
+            else if (StringUtils.containsIgnoreCase(str, "L")) {
+                classs.add(new Object[]{Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class});
+            }
+            // double浮点类型,包含D
+            else if (StringUtils.containsIgnoreCase(str, "D")) {
+                classs.add(new Object[]{Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), Double.class});
+            }
+            // 其他类型归类为整形
+            else {
+                classs.add(new Object[]{Integer.valueOf(str), Integer.class});
+            }
+        }
+        return classs;
+    }
+
+    /**
+     * 获取参数类型
+     *
+     * @param methodParams 参数相关列表
+     * @return 参数类型列表
+     */
+    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams) {
+        Class<?>[] classs = new Class<?>[methodParams.size()];
+        int index = 0;
+        for (Object[] os : methodParams) {
+            classs[index] = (Class<?>) os[1];
+            index++;
+        }
+        return classs;
+    }
+
+    /**
+     * 获取参数值
+     *
+     * @param methodParams 参数相关列表
+     * @return 参数值列表
+     */
+    public static Object[] getMethodParamsValue(List<Object[]> methodParams) {
+        Object[] classs = new Object[methodParams.size()];
+        int index = 0;
+        for (Object[] os : methodParams) {
+            classs[index] = (Object) os[0];
+            index++;
+        }
+        return classs;
+    }
+}

+ 18 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/utils/QuartzDisallowConcurrentExecution.java

@@ -0,0 +1,18 @@
+package com.zd.base.job.utils;
+
+import com.zd.base.job.domain.SysJob;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+
+/**
+ * 定时任务处理(禁止并发执行)
+ *
+ * @author zd
+ */
+@DisallowConcurrentExecution
+public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
+    @Override
+    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
+        JobInvokeUtil.invokeMethod(sysJob);
+    }
+}

+ 16 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/utils/QuartzJobExecution.java

@@ -0,0 +1,16 @@
+package com.zd.base.job.utils;
+
+import com.zd.base.job.domain.SysJob;
+import org.quartz.JobExecutionContext;
+
+/**
+ * 定时任务处理(允许并发执行)
+ *
+ * @author zd
+ */
+public class QuartzJobExecution extends AbstractQuartzJob {
+    @Override
+    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
+        JobInvokeUtil.invokeMethod(sysJob);
+    }
+}

+ 94 - 0
zd-modules/zd-base/src/main/java/com/zd/base/job/utils/ScheduleUtils.java

@@ -0,0 +1,94 @@
+package com.zd.base.job.utils;
+
+import com.zd.common.core.constant.ScheduleConstants;
+import com.zd.common.core.exception.job.TaskException;
+import com.zd.common.core.exception.job.TaskException.Code;
+import com.zd.base.job.domain.SysJob;
+import org.quartz.*;
+
+/**
+ * 定时任务工具类
+ *
+ * @author zd
+ */
+public class ScheduleUtils {
+    /**
+     * 得到quartz任务类
+     *
+     * @param sysJob 执行计划
+     * @return 具体执行任务类
+     */
+    private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) {
+        boolean isConcurrent = "0".equals(sysJob.getConcurrent());
+        return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
+    }
+
+    /**
+     * 构建任务触发对象
+     */
+    public static TriggerKey getTriggerKey(Long jobId, String jobGroup) {
+        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+    }
+
+    /**
+     * 构建任务键对象
+     */
+    public static JobKey getJobKey(Long jobId, String jobGroup) {
+        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+    }
+
+    /**
+     * 创建定时任务
+     */
+    public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException {
+        Class<? extends Job> jobClass = getQuartzJobClass(job);
+        // 构建job信息
+        Long jobId = job.getJobId();
+        String jobGroup = job.getJobGroup();
+        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
+
+        // 表达式调度构建器
+        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
+
+        // 按新的cronExpression表达式构建一个新的trigger
+        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
+                .withSchedule(cronScheduleBuilder).build();
+
+        // 放入参数,运行时的方法可以获取
+        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
+
+        // 判断是否存在
+        if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
+            // 防止创建时存在数据问题 先移除,然后在执行创建操作
+            scheduler.deleteJob(getJobKey(jobId, jobGroup));
+        }
+
+        scheduler.scheduleJob(jobDetail, trigger);
+
+        // 暂停任务
+        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
+            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+        }
+    }
+
+    /**
+     * 设置定时任务策略
+     */
+    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
+            throws TaskException {
+        switch (job.getMisfirePolicy()) {
+            case ScheduleConstants.MISFIRE_DEFAULT:
+                return cb;
+            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
+                return cb.withMisfireHandlingInstructionIgnoreMisfires();
+            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
+                return cb.withMisfireHandlingInstructionFireAndProceed();
+            case ScheduleConstants.MISFIRE_DO_NOTHING:
+                return cb.withMisfireHandlingInstructionDoNothing();
+            default:
+                throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
+                        + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
+        }
+    }
+}

+ 31 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/base/FridConsumer.java

@@ -0,0 +1,31 @@
+package com.zd.base.message.base;
+
+import com.payne.reader.base.Consumer;
+import com.payne.reader.bean.receive.InventoryTag;
+import com.zd.base.message.service.ISendService;
+import com.zd.common.core.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class FridConsumer implements Consumer<InventoryTag> {
+
+    @Autowired
+    private ISendService sendService;
+
+    @Override
+    public void accept(InventoryTag tag) {
+        try {
+            tag.setEpc(tag.getEpc().replace(" ", ""));
+            com.zd.system.api.domain.InventoryTag  inventoryTag = new com.zd.system.api.domain.InventoryTag ();
+            BeanUtils.copyProperties(tag,inventoryTag);
+            sendService.send(inventoryTag);
+        } catch (BeansException e) {
+            throw new ServiceException(e.getMessage());
+        }
+    }
+}

+ 121 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/controller/UserOpenIdController.java

@@ -0,0 +1,121 @@
+package com.zd.base.message.controller;
+
+import com.zd.base.message.domain.UserOpenId;
+import com.zd.base.message.service.IUserOpenIdService;
+import com.zd.common.core.domain.R;
+import com.zd.common.core.exception.ServiceException;
+import com.zd.common.core.utils.poi.ExcelUtil;
+import com.zd.common.core.web.controller.BaseController;
+import com.zd.common.core.web.page.TableDataInfo;
+import com.zd.common.log.annotation.Log;
+import com.zd.common.log.enums.BusinessType;
+import com.zd.common.response.ResultData;
+import com.zd.common.security.annotation.PreAuthorize;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 用户和微信openID对应表Controller
+ *
+ * @author hanson
+ */
+@RestController
+@Api(tags = "【用户和微信openID对应表】")
+@RequestMapping("/user/wx")
+public class UserOpenIdController extends BaseController<UserOpenId> {
+    @Autowired
+    private IUserOpenIdService userOpenIdService;
+
+    /**
+     * 查询用户和微信openID对应表列表
+     */
+    @GetMapping("/list")
+    @ApiOperation(value = "查询用户和微信openID对应表列表")
+    public TableDataInfo<UserOpenId> list(UserOpenId userOpenId) {
+        startPage();
+        List<UserOpenId> list = userOpenIdService.selectUserOpenIdList(userOpenId);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出用户和微信openID对应表列表
+     */
+    @ApiOperation(value = "导出用户和微信openID对应表列表")
+    @Log(title = "用户和微信openID对应表", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, UserOpenId userOpenId) throws IOException {
+        List<UserOpenId> list = userOpenIdService.selectUserOpenIdList(userOpenId);
+        ExcelUtil<UserOpenId> util = new ExcelUtil<>(UserOpenId.class);
+        util.exportExcel(response, list, "用户和微信openID对应表数据");
+    }
+
+    /**
+     * 获取用户和微信openID对应表详细信息
+     */
+    @ApiOperation(value = "获取用户和微信openID对应表详细信息")
+    @GetMapping(value = "/{id}")
+    public ResultData<UserOpenId> getInfo(@PathVariable("id") Long id) {
+        return ResultData.success(userOpenIdService.selectUserOpenIdById(id));
+    }
+
+    /**
+     * 获取用户和微信openID对应表详细信息
+     */
+    @ApiOperation(value = "获取用户和微信openID对应表详细信息")
+    @GetMapping(value = "openId/{userId}")
+    public R<String> getOpenIdByUserId(@PathVariable("userId") Long userId) {
+        UserOpenId openId = new UserOpenId();
+        openId.setUserId(userId);
+        List<UserOpenId> list = userOpenIdService.selectUserOpenIdList(openId);
+        if (list.size() != 1) {
+            throw new ServiceException("用户关联错误");
+        }
+        UserOpenId userOpenId = list.get(0);
+        return R.ok(userOpenId.getOpenId());
+    }
+
+    /**
+     * 根据小程序端传回的code,获取openId
+     */
+    @ApiOperation(value = "获取openID")
+    @Log(title = "获取openID", businessType = BusinessType.INSERT)
+    @GetMapping("getOpenId")
+    public ResultData<UserOpenId> getOpenId(String code) {
+        return ResultData.success(userOpenIdService.getOpenId(code));
+    }
+    /**
+     * 新增用户和微信openID对应表
+     */
+    @ApiOperation(value = "新增用户和微信openID对应表")
+    @Log(title = "用户和微信openID对应表", businessType = BusinessType.INSERT)
+    @PostMapping
+    public ResultData<Boolean> add(@RequestBody UserOpenId userOpenId) {
+        return ResultData.result(userOpenIdService.insertUserOpenId(userOpenId));
+    }
+
+    /**
+     * 修改用户和微信openID对应表
+     */
+    @ApiOperation(value = "修改用户和微信openID对应表")
+    @Log(title = "用户和微信openID对应表", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public ResultData<Boolean> edit(@RequestBody UserOpenId userOpenId) {
+        return ResultData.result(userOpenIdService.updateUserOpenId(userOpenId));
+    }
+
+    /**
+     * 删除用户和微信openID对应表
+     */
+    @ApiOperation(value = "删除用户和微信openID对应表")
+    @PreAuthorize(hasPermi = "airbottle:id:remove")
+    @Log(title = "用户和微信openID对应表", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public ResultData<Boolean> remove(@PathVariable Long[] ids) {
+        return ResultData.result(userOpenIdService.deleteUserOpenIdByIds(ids));
+    }
+}

+ 32 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/controller/WXTokenController.java

@@ -0,0 +1,32 @@
+package com.zd.base.message.controller;
+
+import com.zd.base.message.properties.WeChatProperties;
+import com.zd.base.message.utils.WXPublicUtils;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("/wx")
+@Api(tags = "验证公众平台token")
+@Slf4j
+public class WXTokenController {
+
+    @Autowired
+    private WeChatProperties properties;
+
+    @RequestMapping("/verify")
+    public String verifyWXToken(HttpServletRequest request) {
+        String msgSignature = request.getParameter("signature");
+        String msgTimestamp = request.getParameter("timestamp");
+        String msgNonce = request.getParameter("nonce");
+        String echostr = request.getParameter("echostr");
+        if (WXPublicUtils.verifyUrl(msgSignature, msgTimestamp, msgNonce,properties.getToken())) {
+            return echostr;
+        }
+        return null;
+    }
+}

+ 138 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/controller/WechatMsgController.java

@@ -0,0 +1,138 @@
+package com.zd.base.message.controller;
+
+import com.zd.base.message.service.IWechatMsgSendService;
+import com.zd.common.core.domain.R;
+import com.zd.common.core.template.TemplateResult;
+import com.zd.common.response.ResultData;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+@RestController
+@Api(tags = "微信模板消息发送")
+@RequestMapping("/wx")
+public class WechatMsgController {
+
+    @Autowired
+    private IWechatMsgSendService sendService;
+
+    /**
+     * 待办事项消息发送
+     */
+    @GetMapping("/list")
+    @ApiOperation(value = "订阅列表查询")
+    public ResultData<List<String>> list() {
+        return ResultData.success(sendService.getList());
+    }
+
+    /**
+     * 待办事项消息发送
+     * @param backlogName 待办事项名称
+     * @param remark 备注
+     * @return TemplateResult
+     */
+    @GetMapping("/backlog")
+    @ApiOperation(value = "待办事项消息发送")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "backlogName",value = "待办事项名称"),
+            @ApiImplicitParam(name = "remark",value = "备注")
+    })
+    public R<TemplateResult> backlog(Long userId, String backlogName, String remark) {
+        return  R.ok(sendService.sendBacklogResult(userId,backlogName,remark));
+    }
+
+    /**
+     * 资格申请、用气申请通知发送
+     * @param checkType 审核类型 1:资格申请 2:用气申请
+     * @param checkStatus 审核状态
+     * @param checkTime 审核时间
+     * @return TemplateResult 通知发送结果
+     */
+    @GetMapping("/check")
+    @ApiOperation(value = "审核消息发送")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "checkType",value = "审核类型 1:资格申请 2:用气申请"),
+            @ApiImplicitParam(name = "checkStatus",value = "审核状态 1:通过 2:已驳回"),
+            @ApiImplicitParam(name = "checkTime",value = "审核时间")
+    })
+    public R<TemplateResult> check(Long userId,Integer checkType, Integer checkStatus, Date checkTime,Long taskId) {
+        return R.ok(sendService.sendCheckResult(userId,checkType,checkStatus,checkTime,taskId));
+    }
+
+    /**
+     * 资格申请、用气申请通知发送
+     * @param checkType 审核类型 1:资格申请 2:用气申请
+     * @param checkStatus 审核状态
+     * @param checkTime 审核时间
+     * @return TemplateResult 通知发送结果
+     */
+    @GetMapping("stu/check")
+    @ApiOperation(value = "学生端审核消息发送")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "checkType",value = "审核类型 1:资格申请 2:用气申请"),
+            @ApiImplicitParam(name = "checkStatus",value = "审核状态 1:通过 2:已驳回"),
+            @ApiImplicitParam(name = "checkTime",value = "审核时间")
+    })
+    public R<TemplateResult> stuCheck(Long userId,Integer checkType, Integer checkStatus, Date checkTime,Long taskId) {
+        return R.ok(sendService.sendStuCheckResult(userId,checkType,checkStatus,checkTime,taskId));
+    }
+
+    /**
+     * 出库确认通知
+     *
+     * @param outType 出库类型
+     * @param outTime   出库时间
+     * @return TemplateResult 通知发送结果
+     */
+    @GetMapping("/storage/out")
+    @ApiOperation(value = "出库确认通知")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "outType",value = "出库类型"),
+            @ApiImplicitParam(name = "outTime",value = "出库时间")
+    })
+    public R<TemplateResult> storageOut(Long userId,String outType, Date outTime) {
+        return R.ok(sendService.sendStorageOutResult(userId,outType,outTime));
+    }
+
+    /**
+     * 资格申请、用气申请待审核通知发送
+     *
+     * @param checkType   审核类型 1:资格申请 2:用气申请
+     * @param name 申请人
+     * @param applyTime   申请时间
+     * @return TemplateResult 通知发送结果
+     */
+    @GetMapping("/wait/check")
+    @ApiOperation(value = "待审核消息发送")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "checkType",value = "审核类型 1:资格申请 2:用气申请"),
+            @ApiImplicitParam(name = "name",value = "申请人"),
+            @ApiImplicitParam(name = "applyTime",value = "申请时间")
+    })
+    public R<TemplateResult> waitCheck(Long userId,Integer checkType,String name, Date applyTime) {
+        return R.ok(sendService.sendWaitCheckResult(userId,checkType,name,applyTime));
+    }
+
+    /**
+     * 设备报警通知发送
+     *
+     * @param userIds   上报接收人
+     * @param address 报警位置
+     * @return TemplateResult 通知发送结果
+     */
+    @PostMapping("/send/alarm")
+    @ApiOperation(value = "气瓶超出范围通知")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userIds",value = "上报接收人"),
+            @ApiImplicitParam(name = "address",value = "报警位置")
+    })
+    public R<TemplateResult> sendAlarm(@RequestBody List<Long> userIds, @RequestParam("address") String address) {
+        return R.ok(sendService.sendAlarm(userIds,address));
+    }
+}

+ 30 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/domain/UserOpenId.java

@@ -0,0 +1,30 @@
+package com.zd.base.message.domain;
+
+import com.zd.common.core.web.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+/**
+ * 用户和微信openID对应表对象 sys_user_open_id
+ * 
+ * @author hanson
+ */
+@ApiModel("用户和微信openID对应表")
+@Data
+@Accessors(chain = true)
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = false)
+public class UserOpenId extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "${comment}")
+    private Long id;
+    @ApiModelProperty(value = "微信openId")
+    private String openId;
+
+}

+ 76 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/mapper/UserOpenIdMapper.java

@@ -0,0 +1,76 @@
+package com.zd.base.message.mapper;
+
+import com.zd.base.message.domain.UserOpenId;
+
+import java.util.List;
+
+/**
+ * 用户和微信openID对应表Mapper接口
+ * 
+ * @author hanson
+ */
+public interface UserOpenIdMapper 
+{
+    /**
+     * 查询用户和微信openID对应表
+     * 
+     * @param id 用户和微信openID对应表主键
+     * @return 用户和微信openID对应表
+     */
+    UserOpenId selectUserOpenIdById(Long id);
+
+    /**
+     * 查询用户和微信openID对应表列表
+     * 
+     * @param userOpenId 用户和微信openID对应表
+     * @return 用户和微信openID对应表集合
+     */
+    List<UserOpenId> selectUserOpenIdList(UserOpenId userOpenId);
+
+    /**
+     * 根据主键集合查询用户和微信openID对应表列表
+     *
+     * @param ids 主键集合
+     * @return 用户和微信openID对应表集合
+     */
+    List<UserOpenId> getListByIds(List<Long> ids);
+
+    /**
+     * 新增用户和微信openID对应表
+     * 
+     * @param userOpenId 用户和微信openID对应表
+     * @return 结果
+     */
+    int insertUserOpenId(UserOpenId userOpenId);
+
+    /**
+     * 修改用户和微信openID对应表
+     * 
+     * @param userOpenId 用户和微信openID对应表
+     * @return 结果
+     */
+    int updateUserOpenId(UserOpenId userOpenId);
+
+    /**
+     * 删除用户和微信openID对应表
+     * 
+     * @param id 用户和微信openID对应表主键
+     * @return 结果
+     */
+    int deleteUserOpenIdById(Long id);
+
+    /**
+     * 批量删除用户和微信openID对应表
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteUserOpenIdByIds(Long[] ids);
+
+    /**
+     * 根据用户ID查询openID
+     * @param userId 用户ID
+     * @return UserOpenId
+     */
+    UserOpenId getByUserId(Long userId);
+}

+ 108 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/properties/WeChatProperties.java

@@ -0,0 +1,108 @@
+package com.zd.base.message.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 微信小程序属性配置
+ */
+@RefreshScope
+@Data
+@Component
+@ConfigurationProperties(prefix = "sys.wx")
+public class WeChatProperties {
+
+    /**
+     * 小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态)
+     */
+    private String appId;
+    /**
+     * 小程序唯一凭证密钥,即 AppSecret,获取方式同 appid
+     */
+    private String secret;
+
+    /**
+     * 微信消息推送地址,默认为:"<a href="https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN">https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN</a>"
+     */
+    private String url;
+    /**
+     * 获取微信openID地址
+     */
+    private String openIdUrl;
+
+    /**
+     * 获取微信token地址
+     */
+    private String tokenUrl;
+
+    /**
+     * 出库确认消息模板ID
+     */
+    private String storageOutTemplateId;
+
+    /**
+     * 待审核消息模板ID
+     */
+    private String waitCheckTemplateId;
+
+    /**
+     * 设备报警消息模板ID
+     */
+    private String deviceAlarmTemplateId;
+
+    /**
+     * 用气待审核跳转微信小程序地址
+     */
+    private String waitCheckUrl;
+
+    private String initPage="pages/login";
+
+    /**
+     * 待办事项消息模板ID
+     */
+    private String backlogTemplateId;
+    /**
+     * 待办事项跳转微信小程序地址
+     */
+    private String backlogUrl;
+
+    /**
+     * 资格申请、用气申请消息模板ID
+     */
+    private String checkTemplateId;
+
+    /**
+     * 用气申请跳转微信小程序地址
+     */
+    private String airCheckUrl;
+
+    /**
+     * 学生端用气申请跳转微信小程序地址
+     */
+    private String stuAirCheckUrl;
+
+    /**
+     * 资格申请跳转微信小程序地址
+     */
+    private String qualificationCheckUrl;
+
+    /**
+     * 学生段端资格申请跳转微信小程序地址
+     */
+    private String stuQualificationCheckUrl;
+
+    /**
+     * Token(令牌),微信小程序消息推送配置的token值
+     */
+    private String token;
+
+    /**
+     * 模板ID集合
+     */
+    private List<String> templateIds;
+
+}

+ 17 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/service/ISendService.java

@@ -0,0 +1,17 @@
+package com.zd.base.message.service;
+
+import com.zd.system.api.domain.InventoryTag;
+
+public interface ISendService {
+
+    /**
+     * 发送读取到的编码
+     * @param tag frid数据
+     */
+    void send(InventoryTag tag);
+    /**
+     * 发送异常信息
+     * @param msg 异常消息
+     */
+    void sendError(String msg);
+}

+ 0 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/service/IUserOpenIdService.java


部分文件因为文件数量过多而无法显示