linfutong лет назад: 3
Родитель
Сommit
39dc956d8a
47 измененных файлов с 3520 добавлено и 0 удалено
  1. 1 0
      zd-modules/pom.xml
  2. 132 0
      zd-modules/zd-base/pom.xml
  3. 20 0
      zd-modules/zd-base/src/main/java/com/zd/base/BaseApplicaion.java
  4. 56 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/config/ScheduleConfig.java
  5. 143 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/controller/SysJobController.java
  6. 84 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/controller/SysJobLogController.java
  7. 169 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/domain/SysJob.java
  8. 155 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/domain/SysJobLog.java
  9. 65 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/mapper/SysJobLogMapper.java
  10. 68 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/mapper/SysJobMapper.java
  11. 56 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/ISysJobLogService.java
  12. 102 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/ISysJobService.java
  13. 81 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/impl/SysJobLogServiceImpl.java
  14. 230 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/service/impl/SysJobServiceImpl.java
  15. 47 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/ChemicalTask.java
  16. 57 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/ExamTask.java
  17. 103 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/FileViewTask.java
  18. 64 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/LabTask.java
  19. 32 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/task/RyTask.java
  20. 96 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/AbstractQuartzJob.java
  21. 53 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/CronUtils.java
  22. 159 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/JobInvokeUtil.java
  23. 18 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/QuartzDisallowConcurrentExecution.java
  24. 16 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/QuartzJobExecution.java
  25. 94 0
      zd-modules/zd-base/src/main/java/com/zd/base/job/utils/ScheduleUtils.java
  26. 31 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/base/FridConsumer.java
  27. 122 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/controller/UserOpenIdController.java
  28. 33 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/controller/WXTokenController.java
  29. 138 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/controller/WechatMsgController.java
  30. 30 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/domain/UserOpenId.java
  31. 76 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/mapper/UserOpenIdMapper.java
  32. 108 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/properties/WeChatProperties.java
  33. 17 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/ISendService.java
  34. 85 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/IUserOpenIdService.java
  35. 65 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/IWechatMsgSendService.java
  36. 22 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/impl/SendServiceImpl.java
  37. 143 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/impl/UserOpenIdServiceImpl.java
  38. 283 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/service/impl/WechatMsgSendServiceImpl.java
  39. 49 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/utils/SHA1.java
  40. 26 0
      zd-modules/zd-base/src/main/java/com/zd/base/message/utils/WXPublicUtils.java
  41. 10 0
      zd-modules/zd-base/src/main/resources/banner.txt
  42. 29 0
      zd-modules/zd-base/src/main/resources/bootstrap.yml
  43. BIN
      zd-modules/zd-base/src/main/resources/libs/connect-lib.jar
  44. BIN
      zd-modules/zd-base/src/main/resources/libs/reader-lib.jar
  45. 74 0
      zd-modules/zd-base/src/main/resources/logback.xml
  46. 0 0
      zd-modules/zd-base/src/main/resources/mapper/11.txt
  47. 78 0
      zd-modules/zd-base/src/main/resources/mapper/message/UserOpenIdMapper.xml

+ 1 - 0
zd-modules/pom.xml

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

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

@@ -0,0 +1,132 @@
+<?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>
+    <packaging>jar</packaging>
+    <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 Nacos Config -->
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-spring-context</artifactId>
+            <version>0.2.3-RC1</version>
+        </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>
+
+        <!-- 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>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <!-- zd Common DataScope -->
+        <dependency>
+            <groupId>com.zd</groupId>
+            <artifactId>zd-common-datascope</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>
+    </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>

+ 20 - 0
zd-modules/zd-base/src/main/java/com/zd/base/BaseApplicaion.java

@@ -0,0 +1,20 @@
+package com.zd.base;
+
+import com.zd.common.security.annotation.EnableCustomConfig;
+import com.zd.common.security.annotation.EnableRyFeignClients;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@EnableCustomConfig
+@EnableRyFeignClients
+@SpringBootApplication
+@EnableScheduling
+@Slf4j
+public class BaseApplicaion {
+    public static void main(String[] args) {
+        SpringApplication.run(BaseApplicaion.class, args);
+        log.info("(♥◠‿◠)ノ゙  消息模块启动成功   ლ(´ڡ`ლ)゙");
+    }
+}

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

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

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

@@ -0,0 +1,143 @@
+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.quartz.SchedulerException;
+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")
+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();
+    }
+}

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

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

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

@@ -0,0 +1,169 @@
+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.base.job.domain.SysJob;
+import com.zd.common.core.exception.job.TaskException;
+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();
+    }
+}

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

@@ -0,0 +1,230 @@
+package com.zd.base.job.service.impl;
+
+import com.zd.base.job.domain.SysJob;
+import com.zd.base.job.mapper.SysJobMapper;
+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 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.base.job.domain.SysJob;
+import com.zd.base.job.service.ISysJobService;
+import com.zd.common.core.utils.StringUtils;
+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.base.job.domain.SysJob;
+import com.zd.base.job.domain.SysJobLog;
+import com.zd.base.job.service.ISysJobLogService;
+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 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());
+        }
+    }
+}

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

@@ -0,0 +1,159 @@
+package com.zd.base.job.utils;
+
+import com.zd.base.job.domain.SysJob;
+import com.zd.common.core.utils.SpringUtils;
+import com.zd.common.core.utils.StringUtils;
+
+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.base.job.domain.SysJob;
+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 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());
+        }
+    }
+}

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

@@ -0,0 +1,122 @@
+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.domain.ResultData;
+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 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));
+    }
+}

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

@@ -0,0 +1,33 @@
+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.core.web.domain.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);
+}

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

@@ -0,0 +1,85 @@
+package com.zd.base.message.service;
+
+
+import com.zd.base.message.domain.UserOpenId;
+
+import java.util.List;
+
+/**
+ * 用户和微信openID对应表Service接口
+ * 
+ * @author hanson
+ * @date 2022-06-10
+ */
+public interface IUserOpenIdService 
+{
+    /**
+     * 查询用户和微信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 ids 需要删除的用户和微信openID对应表主键集合
+     * @return 结果
+     */
+    int deleteUserOpenIdByIds(Long[] ids);
+
+    /**
+     * 删除用户和微信openID对应表信息
+     * 
+     * @param id 用户和微信openID对应表主键
+     * @return 结果
+     */
+    int deleteUserOpenIdById(Long id);
+
+    /**
+     * 根据小程序端传回的code,获取openId
+     * @param code 小程序端传回的code
+     * @return openId
+     */
+    UserOpenId getOpenId(String code);
+
+    /**
+     * 根据用户ID查询openID
+     * @param userId 用户ID
+     * @return UserOpenId
+     */
+    UserOpenId getByUserId(Long userId);
+}

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

@@ -0,0 +1,65 @@
+package com.zd.base.message.service;
+
+import com.zd.common.core.template.TemplateResult;
+
+import java.util.Date;
+import java.util.List;
+
+public interface IWechatMsgSendService {
+
+    /**
+     * 资格申请、用气申请通知发送
+     *
+     * @param userId 目标用户ID
+     * @param checkType   审核类型 1:资格申请 2:用气申请
+     * @param checkStatus 审核状态
+     * @param checkTime   审核时间
+     * @param taskId 任务ID
+     * @return TemplateResult 通知发送结果
+     */
+    TemplateResult sendCheckResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId);
+
+    /**
+     * 根据小程序端传回的code,获取openId
+     * @param code 小程序端传回的code
+     * @return openId
+     */
+    String getOpenId(String code);
+
+    /**
+     * 出库确认通知
+     *
+     * @param userId 目标用户ID
+     * @param outType 出库类型
+     * @param outTime 出库时间
+     * @return TemplateResult 通知发送结果
+     */
+    TemplateResult sendStorageOutResult(Long userId, String outType, Date outTime);
+
+    /**
+     * 资格申请、用气申请待审核通知发送
+     *
+     * @param userId 目标用户ID
+     * @param checkType 审核类型 1:资格申请 2:用气申请
+     * @param name      申请人
+     * @param applyTime 申请时间
+     * @return TemplateResult 通知发送结果
+     */
+    TemplateResult sendWaitCheckResult(Long userId, Integer checkType, String name, Date applyTime);
+
+    /**
+     * 待办事项通知发送
+     *
+     * @param userId 目标用户ID
+     * @param backlogName 待办事项名称
+     * @param remark      备注
+     * @return TemplateResult 通知发送结果
+     */
+    TemplateResult sendBacklogResult(Long userId, String backlogName, String remark);
+
+    List<String> getList();
+
+    TemplateResult sendStuCheckResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId);
+
+    TemplateResult sendAlarm(List<Long> userIds,String address);
+}

+ 22 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/service/impl/SendServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zd.base.message.service.impl;
+
+
+import com.zd.base.message.service.ISendService;
+import com.zd.system.api.domain.InventoryTag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class SendServiceImpl implements ISendService {
+
+    @Override
+    public void send(InventoryTag tag) {
+        log.info("==============================================reader1 inventory tag :" + tag.toString());
+    }
+
+    @Override
+    public void sendError(String msg) {
+        log.info(msg);
+    }
+}

+ 143 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/service/impl/UserOpenIdServiceImpl.java

@@ -0,0 +1,143 @@
+package com.zd.base.message.service.impl;
+
+import com.zd.base.message.domain.UserOpenId;
+import com.zd.base.message.mapper.UserOpenIdMapper;
+import com.zd.base.message.service.IUserOpenIdService;
+import com.zd.base.message.service.IWechatMsgSendService;
+import com.zd.common.core.exception.ServiceException;
+import com.zd.common.core.utils.StringUtils;
+import com.zd.common.security.service.TokenService;
+import com.zd.system.api.model.LoginUser;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 用户和微信openID对应表Service业务层处理
+ *
+ * @author hanson
+ */
+@Service
+public class UserOpenIdServiceImpl implements IUserOpenIdService {
+    @Resource
+    private UserOpenIdMapper userOpenIdMapper;
+    @Resource
+    private IWechatMsgSendService wechatMsgSendService;
+
+    @Resource
+    private TokenService tokenService;
+
+    /**
+     * 查询用户和微信openID对应表
+     *
+     * @param id 用户和微信openID对应表主键
+     * @return 用户和微信openID对应表
+     */
+    @Override
+    public UserOpenId selectUserOpenIdById(Long id) {
+        return userOpenIdMapper.selectUserOpenIdById(id);
+    }
+
+    /**
+     * 查询用户和微信openID对应表列表
+     *
+     * @param userOpenId 用户和微信openID对应表
+     * @return 用户和微信openID对应表
+     */
+    @Override
+    public List<UserOpenId> selectUserOpenIdList(UserOpenId userOpenId) {
+        return userOpenIdMapper.selectUserOpenIdList(userOpenId);
+    }
+
+    @Override
+    public List<UserOpenId> getListByIds(List<Long> ids) {
+        if (ids.isEmpty()) {
+            new ArrayList<>();
+        }
+        return userOpenIdMapper.getListByIds(ids);
+    }
+
+    /**
+     * 新增用户和微信openID对应表
+     *
+     * @param userOpenId 用户和微信openID对应表
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int insertUserOpenId(UserOpenId userOpenId) {
+        return userOpenIdMapper.insertUserOpenId(userOpenId);
+    }
+
+    /**
+     * 修改用户和微信openID对应表
+     *
+     * @param userOpenId 用户和微信openID对应表
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int updateUserOpenId(UserOpenId userOpenId) {
+        return userOpenIdMapper.updateUserOpenId(userOpenId);
+    }
+
+    /**
+     * 批量删除用户和微信openID对应表
+     *
+     * @param ids 需要删除的用户和微信openID对应表主键
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int deleteUserOpenIdByIds(Long[] ids) {
+        return userOpenIdMapper.deleteUserOpenIdByIds(ids);
+    }
+
+    /**
+     * 删除用户和微信openID对应表信息
+     *
+     * @param id 用户和微信openID对应表主键
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int deleteUserOpenIdById(Long id) {
+        return userOpenIdMapper.deleteUserOpenIdById(id);
+    }
+
+    @Override
+    public UserOpenId getOpenId(String code) {
+        UserOpenId userOpenId;
+        LoginUser loginUser = tokenService.getLoginUser();
+        if (loginUser!=null){
+            Long userId = loginUser.getSysUser().getUserId();
+            userOpenId=userOpenIdMapper.getByUserId(userId);
+            if (userOpenId!=null){
+                return userOpenId;
+            }
+            String openId = wechatMsgSendService.getOpenId(code);
+            if (StringUtils.isEmpty(openId)){
+                return new UserOpenId();
+            }
+            userOpenId = new UserOpenId();
+            userOpenId.setOpenId(openId);
+            userOpenId.setUserId(userId);
+
+            List<UserOpenId> userOpenIds = userOpenIdMapper.selectUserOpenIdList(userOpenId);
+            if (userOpenIds.isEmpty()) {
+                userOpenIdMapper.insertUserOpenId(userOpenId);
+            }
+            return userOpenId;
+        }else {
+            throw new ServiceException("未检测到登录信息");
+        }
+    }
+
+    @Override
+    public UserOpenId getByUserId(Long userId) {
+        return userOpenIdMapper.getByUserId(userId);
+    }
+}

+ 283 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/service/impl/WechatMsgSendServiceImpl.java

@@ -0,0 +1,283 @@
+package com.zd.base.message.service.impl;
+
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.zd.base.message.domain.UserOpenId;
+import com.zd.base.message.properties.WeChatProperties;
+import com.zd.base.message.service.IUserOpenIdService;
+import com.zd.base.message.service.IWechatMsgSendService;
+import com.zd.common.core.exception.ServiceException;
+import com.zd.common.core.template.Template;
+import com.zd.common.core.template.TemplateParam;
+import com.zd.common.core.template.TemplateResult;
+import com.zd.common.core.template.WxUserInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 向微信小程序推送私密消息
+ */
+@Slf4j
+@Service
+public class WechatMsgSendServiceImpl implements IWechatMsgSendService {
+
+    @Resource(name = "redisTemplate")
+    protected RedisTemplate<String, String> redisTemplate;
+    @Autowired
+    private WeChatProperties weChatProperties;
+
+    @Resource
+    private IUserOpenIdService openIdService;
+
+    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    /**
+     * 微信获取openId的返回值
+     */
+    private static final Map<Integer, String> messageOpenIdMap = new HashMap<>();
+
+    static {
+        messageOpenIdMap.put(-1, "系统繁忙,此时请开发者稍候再试");
+        messageOpenIdMap.put(0, "请求成功");
+        messageOpenIdMap.put(40029, "code 无效");
+        messageOpenIdMap.put(45011, "频率限制,每个用户每分钟100次");
+        messageOpenIdMap.put(40226, "高风险等级用户,小程序登录拦截 。风险等级详见用户安全解方案");
+    }
+
+    /**
+     * 微信发送模板消息的返回值
+     */
+    private static final Map<Integer, String> sendMessageResultMap = new HashMap<>();
+
+    static {
+        sendMessageResultMap.put(40003, "openid 为空或者不正确");
+        sendMessageResultMap.put(40037, "订阅模板 id 为空不正确");
+        sendMessageResultMap.put(43101, "用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系");
+        sendMessageResultMap.put(47003, "模板参数不准确,可能为空或者不满足规则");//errmsg会提示具体是哪个字段出错
+        sendMessageResultMap.put(41030, "page路径不正确,需要保证在现网版本小程序中存在,与 app.json 保持一致");
+    }
+
+    /**
+     * 资格申请、用气申请通知发送(跳转页面:资格申请-资格详情、用气申请-申请详情)
+     *
+     * @param userId      用户ID
+     * @param checkType   审核类型 1:资格申请 2:用气申请
+     * @param checkStatus 审核状态
+     * @param checkTime   审核时间
+     * @param taskId      任务ID
+     * @return TemplateResult 通知发送结果
+     */
+    @Override
+    public TemplateResult sendCheckResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId) {
+        // checkType 双端页面不一致,判断跳转页面地址写入
+        String page = null;
+        if (checkType == 2) {
+            page = weChatProperties.getAirCheckUrl();
+        }
+        if (checkType == 1) {
+            page = weChatProperties.getQualificationCheckUrl();
+        }
+        return getTemplateResult(userId, checkType, checkStatus, checkTime, taskId, page);
+    }
+
+    private TemplateResult getTemplateResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId, String page) {
+        page = page + "?id=" + taskId;
+        List<TemplateParam> paras = new ArrayList<>();
+        paras.add(new TemplateParam("thing39", checkType == 1 ? "资格申请" : "用气申请"));//申请事项
+        paras.add(new TemplateParam("phrase5", checkStatus == 1 ? "成功" : "失败"));//审核结果
+        paras.add(new TemplateParam("time24", dateFormat.format(checkTime)));//审核时间
+        return sendMessage(userId, weChatProperties.getCheckTemplateId(), page, paras);
+    }
+
+    /**
+     * 待办事项通知发送
+     *
+     * @param userId      用户ID
+     * @param backlogName 待办事项名称
+     * @param remark      备注
+     * @return TemplateResult 通知发送结果
+     */
+    @Override
+    public TemplateResult sendBacklogResult(Long userId, String backlogName, String remark) {
+        Template template = new Template();
+        String openId = getOpenId(userId);
+        template.setToUser(openId);
+        template.setTemplateId(weChatProperties.getBacklogTemplateId());
+        // 供应商端-待办事项-需求明细
+        template.setPage(weChatProperties.getBacklogUrl());
+
+        List<TemplateParam> paras = new ArrayList<>();
+        paras.add(new TemplateParam("thing3", backlogName));//待办事项
+        paras.add(new TemplateParam("time5", dateFormat.format(Calendar.getInstance().getTime())));//通知时间
+        paras.add(new TemplateParam("thing6", remark));//备注
+        template.setData(paras);
+        return sendMsgService(template);
+    }
+
+    @Override
+    public List<String> getList() {
+        return weChatProperties.getTemplateIds();
+    }
+
+    @Override
+    public TemplateResult sendStuCheckResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId) {
+        // checkType 双端页面不一致,判断跳转页面地址写入
+        String page = null;
+        if (checkType == 2) {
+            page = weChatProperties.getStuAirCheckUrl();
+        }
+        if (checkType == 1) {
+            page = weChatProperties.getStuQualificationCheckUrl();
+        }
+        return getTemplateResult(userId, checkType, checkStatus, checkTime, taskId, page);
+    }
+
+    @Override
+    public TemplateResult sendAlarm(List<Long> userIds, String address) {
+        List<TemplateParam> paras = new ArrayList<>();
+        paras.add(new TemplateParam("thing1", address));//报警位置
+        paras.add(new TemplateParam("time3", dateFormat.format(Calendar.getInstance().getTime())));//上报时间
+        paras.add(new TemplateParam("thing2", "气瓶超出范围报警"));//报警原因
+        userIds.forEach(id -> sendMessage(id, weChatProperties.getDeviceAlarmTemplateId(), weChatProperties.getInitPage(), paras));
+        return null;
+    }
+
+    private TemplateResult sendMessage(Long userId, String weChatProperties, String page, List<TemplateParam> paras) {
+        Template template = getTemplate(userId, weChatProperties, page);
+        if (template==null){
+            return null;
+        }
+        template.setData(paras);
+        return sendMsgService(template);
+    }
+
+    /**
+     * 资格申请、用气申请管理端待审核通知发送
+     *
+     * @param userId    用户ID
+     * @param checkType 审核类型 1:资格申请 2:用气申请
+     * @param name      申请人
+     * @param applyTime 申请时间
+     * @return TemplateResult 通知发送结果
+     */
+    @Override
+    public TemplateResult sendWaitCheckResult(Long userId, Integer checkType, String name, Date applyTime) {
+        String page = weChatProperties.getWaitCheckUrl();
+        List<TemplateParam> paras = new ArrayList<>();
+        paras.add(new TemplateParam("name1", name));//申请事项
+        paras.add(new TemplateParam("thing3", checkType == 1 ? "资格申请" : "用气申请"));//审核结果
+        paras.add(new TemplateParam("time2", dateFormat.format(applyTime)));//审核时间
+        return sendMessage(userId, weChatProperties.getWaitCheckTemplateId(), page, paras);
+    }
+
+    private Template getTemplate(Long userId, String templateId, String page) {
+        Template template = new Template();
+        String openId = getOpenId(userId);
+        if (openId != null) {
+            template.setToUser(openId);
+            template.setTemplateId(templateId);
+            template.setPage(page);
+            return template;
+        }
+        return null;
+    }
+
+    /**
+     * 出库确认通知
+     *
+     * @param userId  用户ID
+     * @param outType 出库类型
+     * @param outTime 出库时间
+     * @return TemplateResult 通知发送结果
+     */
+    @Override
+    public TemplateResult sendStorageOutResult(Long userId, String outType, Date outTime) {
+        Template template = new Template();
+        String openId = getOpenId(userId);
+        template.setToUser(openId);
+        template.setTemplateId(weChatProperties.getStorageOutTemplateId());
+        List<TemplateParam> paras = new ArrayList<>();
+        paras.add(new TemplateParam("thing2", outType));//出库类型
+        paras.add(new TemplateParam("time9", dateFormat.format(outTime)));//出库时间
+        template.setData(paras);
+        return sendMsgService(template);
+    }
+
+    private String getOpenId(Long userId) {
+        UserOpenId userOpenId = openIdService.getByUserId(userId);
+        if (userOpenId == null) {
+            return null;
+        }
+        return userOpenId.getOpenId();
+    }
+
+    @Override
+    public String getOpenId(String code) {
+        String openIdUrl = weChatProperties.getOpenIdUrl().replace("APPID", weChatProperties.getAppId())
+                .replace("SECRET", weChatProperties.getSecret())
+                .replace("JSCODE", code);
+        String post = HttpUtil.get(openIdUrl);
+        JSONObject jsonObject = JSON.parseObject(post);
+        WxUserInfo wxUserInfo = JSON.toJavaObject(jsonObject, WxUserInfo.class);
+        if (wxUserInfo.getErrcode() != 0) {
+            String errMsg;
+            int errCode = wxUserInfo.getErrcode();
+            if (errCode == 40029) {
+                return null;
+            }
+            if (messageOpenIdMap.containsKey(errCode)) {
+                errMsg = messageOpenIdMap.get(errCode);
+                throw new ServiceException(errMsg);
+            }
+        }
+        return wxUserInfo.getOpenid();
+    }
+
+    private TemplateResult sendMsgService(Template template) {
+        String url = weChatProperties.getUrl().replace("ACCESS_TOKEN", getAccessToken());
+        String post = HttpUtil.post(url, template.toJSON());
+        JSONObject jsonObject = JSON.parseObject(post);
+        TemplateResult result = JSON.toJavaObject(jsonObject, TemplateResult.class);
+        int errCode = result.getErrcode();
+        if (errCode == 47003) {
+            result.setErrmsg(sendMessageResultMap.get(errCode) + ":" + result.getErrmsg());
+        } else if (sendMessageResultMap.containsKey(errCode)) {
+            result.setErrmsg(sendMessageResultMap.get(errCode));
+        }
+        return result;
+    }
+
+    private String getAccessToken() {
+        String accessToken = null;
+        try {
+            //查询是否还有缓存
+            BoundValueOperations<String, String> ops = redisTemplate.boundValueOps("account_token");
+            //缓存时长
+            if (StringUtils.isBlank(ops.get())) {
+                Map<String, Object> params = new HashMap<>();
+                params.put("grant_type", "client_credential");
+                params.put("appid", weChatProperties.getAppId());
+                params.put("secret", weChatProperties.getSecret());
+                String respData = HttpUtil.get(weChatProperties.getTokenUrl(), params);
+                log.info("get access_token=====\n{}", respData);
+                JSONObject json = JSON.parseObject(respData);
+                ops.set(json.getString("access_token"));
+                ops.expire(7100, TimeUnit.SECONDS);
+            }
+            accessToken = ops.get();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return accessToken;
+    }
+}

+ 49 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/utils/SHA1.java

@@ -0,0 +1,49 @@
+package com.zd.base.message.utils;
+
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+public class SHA1 {
+
+    private SHA1() {
+        throw new IllegalStateException("SHA1 class");
+    }
+    /**
+     * 用SHA1算法验证Token
+     *
+     * @param token     票据
+     * @param timestamp 时间戳
+     * @param nonce     随机字符串
+     * @return 安全签名
+     */
+    public static String getSHA1(String token, String timestamp, String nonce) {
+        try {
+            String[] array = new String[]{token, timestamp, nonce};
+            StringBuilder sb = new StringBuilder();
+            // 字符串排序
+            Arrays.sort(array);
+            for (int i = 0; i < 3; i++) {
+                sb.append(array[i]);
+            }
+            String str = sb.toString();
+            // SHA1签名生成
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            md.update(str.getBytes());
+            byte[] digest = md.digest();
+
+            StringBuilder hexstr = new StringBuilder();
+            String shaHex;
+            for (byte b : digest) {
+                shaHex = Integer.toHexString(b & 0xFF);
+                if (shaHex.length() < 2) {
+                    hexstr.append(0);
+                }
+                hexstr.append(shaHex);
+            }
+            return hexstr.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new SecurityException(e.getMessage());
+        }
+    }
+}

+ 26 - 0
zd-modules/zd-base/src/main/java/com/zd/base/message/utils/WXPublicUtils.java

@@ -0,0 +1,26 @@
+package com.zd.base.message.utils;
+
+import com.zd.common.core.exception.ServiceException;
+
+public class WXPublicUtils {
+
+    private WXPublicUtils() {
+        throw new IllegalStateException("WXPublicUtils class");
+    }
+
+    /**
+     * 验证Token
+     * @param msgSignature 签名串,对应URL参数的signature
+     * @param timeStamp 时间戳,对应URL参数的timestamp
+     * @param nonce 随机串,对应URL参数的nonce
+     * @param token Token(令牌)
+     * @return 是否为安全签名
+     */
+    public static boolean verifyUrl(String msgSignature, String timeStamp, String nonce, String token) {
+        String signature = SHA1.getSHA1(token, timeStamp, nonce);
+        if (!signature.equals(msgSignature)) {
+            throw new ServiceException("token 验证失败");
+        }
+        return true;
+    }
+}

+ 10 - 0
zd-modules/zd-base/src/main/resources/banner.txt

@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+                            _                           _                    
+                           (_)                         | |                   
+ _ __  _   _   ___   _   _  _  ______  ___  _   _  ___ | |_   ___  _ __ ___  
+| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \ 
+| |   | |_| || (_) || |_| || |        \__ \| |_| |\__ \| |_ |  __/| | | | | |
+|_|    \__,_| \___/  \__, ||_|        |___/ \__, ||___/ \__| \___||_| |_| |_|
+                      __/ |                  __/ |                           
+                     |___/                  |___/                            

+ 29 - 0
zd-modules/zd-base/src/main/resources/bootstrap.yml

@@ -0,0 +1,29 @@
+# Tomcat
+server:
+  port: 9500
+# Spring
+#logging:
+#  level:
+#    root: debug
+spring:
+  application:
+    # 应用名称
+    name: zd-message
+  profiles:
+    # 环境配置
+    active: dev
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
+      config:
+        # 配置中心地址
+        server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
+        # 配置文件格式
+        file-extension: yml
+        # 动态刷新
+        refresh-enabled: true
+        # 共享配置
+        shared-configs:
+          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

BIN
zd-modules/zd-base/src/main/resources/libs/connect-lib.jar


BIN
zd-modules/zd-base/src/main/resources/libs/reader-lib.jar


+ 74 - 0
zd-modules/zd-base/src/main/resources/logback.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+    <!-- 日志存放路径 -->
+    <property name="log.path" value="logs/zd-base"/>
+    <!-- 日志输出格式 -->
+    <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
+
+    <!-- 控制台输出 -->
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <!-- 系统日志输出 -->
+    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 系统模块日志级别控制  -->
+    <logger name="com.zd" level="info"/>
+    <!-- Spring日志级别控制  -->
+    <logger name="org.springframework" level="error"/>
+
+    <root level="info">
+        <appender-ref ref="console"/>
+    </root>
+
+    <!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info"/>
+        <appender-ref ref="file_error"/>
+    </root>
+</configuration>

+ 0 - 0
zd-modules/zd-base/src/main/resources/mapper/11.txt


+ 78 - 0
zd-modules/zd-base/src/main/resources/mapper/message/UserOpenIdMapper.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zd.message.mapper.UserOpenIdMapper">
+    
+    <resultMap type="com.zd.message.domain.UserOpenId" id="UserOpenIdResult">
+        <result property="id"    column="id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="openId"    column="open_id"    />
+    </resultMap>
+
+    <sql id="selectUserOpenIdVo">
+        select id, user_id, open_id from qp_user_open_id
+    </sql>
+    <sql id="selectUserOpenIdListVo">
+        select t.id, t.user_id, t.open_id from qp_user_open_id as t
+    </sql>
+    <select id="selectUserOpenIdList" parameterType="com.zd.message.domain.UserOpenId" resultMap="UserOpenIdResult">
+        <include refid="selectUserOpenIdVo"/>
+        <where>  
+            <if test="openId != null  and openId != ''"> and open_id = #{openId}</if>
+        </where>
+    </select>
+
+    <select id="getListByIds" resultMap="UserOpenIdResult">
+        <include refid="selectUserOpenIdVo"/>
+        <where>
+            id in
+            <foreach item="id" collection="list" open="(" separator="," close=")">
+                #{id}
+            </foreach>
+        </where>
+    </select>
+    
+    <select id="selectUserOpenIdById" resultMap="UserOpenIdResult">
+        <include refid="selectUserOpenIdVo"/>
+        where id = #{id}
+    </select>
+    <select id="getByUserId" resultType="com.zd.message.domain.UserOpenId" parameterType="java.lang.Long">
+        <include refid="selectUserOpenIdVo"/>
+        where user_id = #{userId}
+    </select>
+
+    <insert id="insertUserOpenId" parameterType="com.zd.message.domain.UserOpenId" useGeneratedKeys="true" keyProperty="id">
+        insert into qp_user_open_id
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+    <if test="userId != null">user_id,</if>
+
+    <if test="openId != null">open_id,</if>
+
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+    <if test="userId != null">#{userId},</if>
+    <if test="openId != null">#{openId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateUserOpenId" parameterType="com.zd.message.domain.UserOpenId">
+        update qp_user_open_id
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="openId != null">open_id = #{openId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteUserOpenIdById">
+        delete from qp_user_open_id where id = #{id}
+    </delete>
+
+    <delete id="deleteUserOpenIdByIds">
+        delete from qp_user_open_id where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>