xiaofei 6 сар өмнө
parent
commit
0d10cf15b8
52 өөрчлөгдсөн 2149 нэмэгдсэн , 28 устгасан
  1. 2 2
      distributed-print-business/pom.xml
  2. 1 1
      distributed-print/install/config.ini
  3. 36 0
      distributed-print/install/mysql/upgrade/3.4.3.sql
  4. 6 2
      distributed-print/pom.xml
  5. 2 2
      distributed-print/src/main/java/com/qmth/distributed/print/DistributedPrintApplication.java
  6. 54 0
      distributed-print/src/main/java/com/qmth/distributed/print/api/SysAdminSetController.java
  7. 91 0
      distributed-print/src/main/java/com/qmth/distributed/print/api/admin/DataController.java
  8. 34 0
      distributed-print/src/main/java/com/qmth/distributed/print/start/StartRunning.java
  9. 38 1
      distributed-print/src/main/resources/application-base.properties
  10. 7 1
      pom.xml
  11. 2 2
      teachcloud-common-api/pom.xml
  12. 2 2
      teachcloud-common/pom.xml
  13. 7 1
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/contant/SystemConstant.java
  14. 11 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/BasicCourse.java
  15. 2 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/BasicCourseService.java
  16. 5 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/SysOrgService.java
  17. 2 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/SysUserService.java
  18. 15 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/BasicCourseServiceImpl.java
  19. 19 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/SysOrgServiceImpl.java
  20. 5 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/SysUserServiceImpl.java
  21. 33 0
      teachcloud-data/.gitignore
  22. 58 0
      teachcloud-data/pom.xml
  23. 299 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/DataUtil.java
  24. 203 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/entity/TSyncData.java
  25. 117 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/entity/TSyncDataLog.java
  26. 17 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/mapper/TSyncDataLogMapper.java
  27. 17 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/mapper/TSyncDataMapper.java
  28. 13 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/DataService.java
  29. 17 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/TSyncDataLogService.java
  30. 27 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/TSyncDataService.java
  31. 137 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/impl/DataServiceImpl.java
  32. 32 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/impl/TSyncDataLogServiceImpl.java
  33. 114 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/impl/TSyncDataServiceImpl.java
  34. 126 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/util/DruidJdbcUtils.java
  35. 58 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/util/JdbcUtil.java
  36. 50 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/CourseDataVo.java
  37. 180 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/ExamStudentDataVo.java
  38. 100 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/ExamTaskDataVo.java
  39. 28 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/OrgDataVo.java
  40. 73 0
      teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/UserDataVo.java
  41. 15 0
      teachcloud-data/src/main/resources/mapper/TSyncDataLogMapper.xml
  42. 19 0
      teachcloud-data/src/main/resources/mapper/TSyncDataMapper.xml
  43. 13 0
      teachcloud-data/src/test/java/com/qmth/teachcloud/data/TeachcloudDataApplicationTests.java
  44. 2 2
      teachcloud-mark/pom.xml
  45. 2 2
      teachcloud-obe/pom.xml
  46. 2 2
      teachcloud-report-business/pom.xml
  47. 2 2
      teachcloud-report/pom.xml
  48. 6 2
      teachcloud-task/pom.xml
  49. 1 0
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/enums/JobEnum.java
  50. 30 0
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/DataSyncJob.java
  51. 2 0
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/JobService.java
  52. 15 4
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/impl/JobServiceImpl.java

+ 2 - 2
distributed-print-business/pom.xml

@@ -4,7 +4,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.distributed.print.business</groupId>
     <artifactId>distributed-print-business</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <build>
         <plugins>
             <plugin>
@@ -22,7 +22,7 @@
     <parent>
         <groupId>com.qmth.distributed.print.service</groupId>
         <artifactId>distributed-print-service</artifactId>
-        <version>3.4.2</version>
+        <version>3.4.3</version>
     </parent>
 
     <dependencies>

+ 1 - 1
distributed-print/install/config.ini

@@ -1,5 +1,5 @@
 [app]
-version=3.4.2
+version=3.4.3
 name=֪ѧ֪¿¼
 portal=http://localhost:7400/
 module=print

+ 36 - 0
distributed-print/install/mysql/upgrade/3.4.3.sql

@@ -0,0 +1,36 @@
+USE teachcloud_db;
+
+ALTER TABLE `client_upgrade`
+    ADD COLUMN `tool_type` VARCHAR(45) NOT NULL COMMENT '文件类型' AFTER `platform`,
+DROP PRIMARY KEY,
+ADD PRIMARY KEY USING BTREE (`platform`, `tool_type`);
+UPDATE `client_upgrade` SET `tool_type` = 'TEACHCLOUD_CLIENT' WHERE (`platform` = 'WINDOWS') and (`tool_type` is null);
+
+CREATE TABLE `t_sync_data` (
+       `school_id` BIGINT(20) NOT NULL COMMENT '学校ID',
+       `semester_id` BIGINT(20) NOT NULL COMMENT '学期ID',
+       `exam_id` BIGINT(20) NOT NULL COMMENT '考试ID',
+       `data_type` VARCHAR(45) NOT NULL COMMENT '同步数据类型(A:机构同步,B:用户同步,C:课程同步,D:考生同步,E:命题任务同步)',
+       `start_time` BIGINT(20) NULL COMMENT '同步开始时间',
+       `end_time` BIGINT(20) NULL COMMENT '同步结束时间',
+       `last_sync_time` BIGINT(20) NULL COMMENT '最后一次同步时间',
+       `status` TINYINT(1) NULL DEFAULT 0 COMMENT '状态(0:未同步/已完成,1:同步中)',
+       `enable` TINYINT(1) NULL DEFAULT 1 COMMENT '启用/禁用',
+       `cron` VARCHAR(45) NULL COMMENT 'cron表达式',
+       `phone_number` VARCHAR(100) NULL COMMENT '手机号(多个用逗号分隔)',
+       `detail` VARCHAR(200) NULL COMMENT '同步明细',
+       `create_time` BIGINT(20) NULL COMMENT '创建时间',
+       `update_time` BIGINT(20) NULL COMMENT '更新时间',
+       PRIMARY KEY (`school_id`, `exam_id`))
+    COMMENT = '数据同步设置';
+
+CREATE TABLE `t_sync_data_log` (
+      `school_id` BIGINT(20) NOT NULL COMMENT '学校ID',
+      `exam_id` BIGINT(20) NOT NULL COMMENT '考试ID',
+      `data_type` VARCHAR(45) NOT NULL COMMENT '同步数据类型',
+      `start_time` BIGINT(20) NULL COMMENT '开始同步时间',
+      `end_time` BIGINT(20) NULL COMMENT '结束同步时间',
+      `count` INT NULL COMMENT '同步数量',
+      `error_msg` MEDIUMTEXT NULL COMMENT '失败原因',
+      PRIMARY KEY (`school_id`, `exam_id`, `data_type`))
+    COMMENT = '数据同步日志';

+ 6 - 2
distributed-print/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.distributed.print</groupId>
     <artifactId>distributed-print</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.distributed.print.service</groupId>
         <artifactId>distributed-print-service</artifactId>
-        <version>3.4.2</version>
+        <version>3.4.3</version>
     </parent>
 
     <dependencies>
@@ -40,6 +40,10 @@
             <groupId>com.qmth.teachcloud.task</groupId>
             <artifactId>teachcloud-task</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.qmth.teachcloud.data</groupId>
+            <artifactId>teachcloud-data</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.qmth.teachcloud.common.api</groupId>
             <artifactId>teachcloud-common-api</artifactId>

+ 2 - 2
distributed-print/src/main/java/com/qmth/distributed/print/DistributedPrintApplication.java

@@ -22,9 +22,9 @@ import javax.annotation.Resource;
 
 @EnableMPP
 @SpringBootApplication(scanBasePackages = "com.qmth.*", exclude = {SecurityAutoConfiguration.class})
-@MapperScan({"com.qmth.distributed.print.business.mapper", "com.qmth.teachcloud.report.business.mapper", "com.qmth.teachcloud.common.mapper", "com.qmth.teachcloud.mark.mapper", "com.qmth.teachcloud.obe.mapper"})
+@MapperScan({"com.qmth.distributed.print.business.mapper", "com.qmth.teachcloud.report.business.mapper", "com.qmth.teachcloud.common.mapper", "com.qmth.teachcloud.mark.mapper", "com.qmth.teachcloud.obe.mapper", "com.qmth.teachcloud.data.mapper",})
 //主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中,做过web开发的同学一定都有用过@Controller,@Service,@Repository注解,查看其源码你会发现,他们中有一个共同的注解@Component,没错@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中
-@EntityScan(basePackages = {"com.qmth.distributed.print.business.entity", "com.qmth.teachcloud.report.business.entity", "com.qmth.teachcloud.common.entity", "com.qmth.teachcloud.mark.entity", "com.qmth.teachcloud.obe.entity"})
+@EntityScan(basePackages = {"com.qmth.distributed.print.business.entity", "com.qmth.teachcloud.report.business.entity", "com.qmth.teachcloud.common.entity", "com.qmth.teachcloud.mark.entity", "com.qmth.teachcloud.obe.entity", "com.qmth.teachcloud.data.entity"})
 //用来扫描和发现指定包及其子包中的Entity定义
 @EnableTransactionManagement // spring开启事务支持
 @EnableAsync // 开启异步任务

+ 54 - 0
distributed-print/src/main/java/com/qmth/distributed/print/api/SysAdminSetController.java

@@ -22,6 +22,7 @@ import com.qmth.teachcloud.common.util.RedisUtil;
 import com.qmth.teachcloud.common.util.Result;
 import com.qmth.teachcloud.common.util.ResultUtil;
 import io.swagger.annotations.*;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 import org.springframework.validation.BindingResult;
@@ -574,4 +575,57 @@ public class SysAdminSetController {
         }
         return ResultUtil.ok(sysConfigResult);
     }
+
+    @ApiOperation(value = "数据同步参数查询")
+    @ApiResponses({@ApiResponse(code = 200, message = "系统试卷规格配置信息", response = CustomPrivilegeResult.class)})
+    @RequestMapping(value = "/data/sync/select", method = RequestMethod.POST)
+    public Result dataSyncSelect(@ApiParam(value = "schoolId") @Param(value = "schoolId") Long schoolId) {
+        SysAdminSetResult sysAdminSetResult = new SysAdminSetResult();
+        List<SysConfigResult> list = new ArrayList<>();
+        // 数据库类型
+        SysConfig datasourceType = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_TYPE);
+        list.add(datasourceType != null ? new SysConfigResult(datasourceType) : new SysConfigResult(null, SystemConstant.DATA_DATASOURCE_TYPE, "数据库类型", "", 1));
+        // 数据库HOST
+        SysConfig datasourceHost = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_HOST);
+        list.add(datasourceHost != null ? new SysConfigResult(datasourceHost) : new SysConfigResult(null, SystemConstant.DATA_DATASOURCE_HOST, "HOST", "", 2));
+        // 数据库PORT
+        SysConfig datasourcePort = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_PORT);
+        list.add(datasourcePort != null ? new SysConfigResult(datasourcePort) : new SysConfigResult(null, SystemConstant.DATA_DATASOURCE_PORT, "端口", "", 3));
+        // 数据库DataName
+        SysConfig datasourceDataName = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_DATANAME);
+        list.add(datasourceDataName != null ? new SysConfigResult(datasourceDataName) : new SysConfigResult(null, SystemConstant.DATA_DATASOURCE_DATANAME, "数据库名", "", 4));
+        // 数据库用户名
+        SysConfig datasourceUserName = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_USERNAME);
+        list.add(datasourceUserName != null ? new SysConfigResult(datasourceUserName) : new SysConfigResult(null, SystemConstant.DATA_DATASOURCE_USERNAME, "用户名", "", 5));
+        // 数据库密码
+        SysConfig datasourcePassword = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_PASSWORD);
+        list.add(datasourcePassword != null ? new SysConfigResult(datasourcePassword) : new SysConfigResult(null, SystemConstant.DATA_DATASOURCE_PASSWORD, "密码", "", 6));
+
+        sysAdminSetResult.setResult(list);
+        return ResultUtil.ok(sysAdminSetResult);
+    }
+
+    @ApiOperation(value = "数据同步参数保存")
+    @ApiResponses({@ApiResponse(code = 200, message = "同步配置信息", response = ResultUtil.class)})
+    @RequestMapping(value = "/data/sync/save", method = RequestMethod.POST)
+    @Transactional
+    @OperationLogDetail(operationType = OperationTypeEnum.SAVE)
+    public Result dataSyncSave(@Valid @RequestBody SysAdminSetParam sysAdminSetParam, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
+        }
+        Optional.ofNullable(sysAdminSetParam.getParam()).orElseThrow(() -> ExceptionResultEnum.PARAMS_ERROR.exception("数据同步参数未填写"));
+
+        List<SysConfigResult> sysConfigResultList = sysAdminSetParam.getParam();
+        List<SysConfig> sysConfigList = new ArrayList<>();
+        for (SysConfigResult s : sysConfigResultList) {
+            sysConfigList.add(new SysConfig(sysAdminSetParam.getSchoolId(), s));
+        }
+        sysConfigService.saveOrUpdateBatch(sysConfigList);
+
+        for (SysConfigResult s : sysConfigResultList) {
+            commonCacheService.updateSysConfigCache(sysAdminSetParam.getSchoolId(), s.getCode());
+        }
+        return ResultUtil.ok(true);
+    }
 }

+ 91 - 0
distributed-print/src/main/java/com/qmth/distributed/print/api/admin/DataController.java

@@ -0,0 +1,91 @@
+package com.qmth.distributed.print.api.admin;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.distributed.print.business.bean.params.SysAdminSetParam;
+import com.qmth.distributed.print.business.service.ClientUpgradeService;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.enums.ClientUpgradeToolTypeEnum;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.enums.clientpackage.ClientPackageEnum;
+import com.qmth.teachcloud.common.util.Result;
+import com.qmth.teachcloud.common.util.ResultUtil;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.qmth.teachcloud.data.service.DataService;
+import com.qmth.teachcloud.data.service.TSyncDataService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import java.util.Optional;
+
+/**
+ * 客户端升级 前端控制器
+ */
+@Api(tags = "超管-数据同步Controller")
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + SystemConstant.PREFIX_URL_SYS + "/school/data")
+@Aac(auth = false)
+public class DataController {
+
+    @Resource
+    private DataService dataService;
+    @Resource
+    private TSyncDataService tSyncDataService;
+
+    @ApiOperation(value = "测试连接")
+    @RequestMapping(value = "/test/connect", method = RequestMethod.POST)
+    public Result testConnect(@Valid @RequestBody SysAdminSetParam sysAdminSetParam, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
+        }
+        Optional.ofNullable(sysAdminSetParam.getParam()).orElseThrow(() -> ExceptionResultEnum.PARAMS_ERROR.exception("数据同步参数未填写"));
+        dataService.testConnect(sysAdminSetParam);
+        return ResultUtil.ok(true);
+    }
+
+    @ApiOperation(value = "查看日志")
+    @RequestMapping(value = "/param/list", method = RequestMethod.POST)
+    public Result listParam(@ApiParam(value = "学校ID") @RequestParam(value = "schoolId") Long schoolId,
+                            @ApiParam(value = "分页页码", required = true) @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                            @ApiParam(value = "分页数", required = true) @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        return ResultUtil.ok(tSyncDataService.listParam(schoolId, pageNumber, pageSize));
+    }
+
+    @ApiOperation(value = "新增/修改同步任务")
+    @RequestMapping(value = "/param/save", method = RequestMethod.POST)
+    public Result paramSave(@ApiParam(value = "任务对象") @RequestBody TSyncData tSyncData) {
+        return ResultUtil.ok(tSyncDataService.saveData(tSyncData));
+    }
+
+    @ApiOperation(value = "手动同步")
+    @RequestMapping(value = "/start/sync", method = RequestMethod.POST)
+    public Result startSync(@ApiParam(value = "学校ID") @RequestParam(value = "schoolId") Long schoolId,
+                            @ApiParam(value = "考试ID") @RequestParam(value = "examId") Long examId) {
+        return ResultUtil.ok(tSyncDataService.syncData(schoolId, examId));
+    }
+
+    @ApiOperation(value = "查看日志")
+    @RequestMapping(value = "/sync/log", method = RequestMethod.POST)
+    public Result listLog(@ApiParam(value = "学校ID") @RequestParam(value = "schoolId") Long schoolId,
+                          @ApiParam(value = "考试ID") @RequestParam(value = "examId") Long examId,
+                          @ApiParam(value = "分页页码", required = true) @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                          @ApiParam(value = "分页数", required = true) @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        return ResultUtil.ok(tSyncDataService.getDataLog(schoolId, examId, pageNumber, pageSize));
+    }
+
+    @ApiOperation(value = "启用/禁用")
+    @RequestMapping(value = "/enable/sync", method = RequestMethod.POST)
+    public Result enable(@RequestBody TSyncData tSyncData) {
+        return ResultUtil.ok(tSyncDataService.enable(tSyncData));
+    }
+
+}
+

+ 34 - 0
distributed-print/src/main/java/com/qmth/distributed/print/start/StartRunning.java

@@ -1,11 +1,16 @@
 package com.qmth.distributed.print.start;
 
+import com.qmth.distributed.print.business.service.BasicExamService;
+import com.qmth.teachcloud.common.entity.BasicExam;
 import com.qmth.teachcloud.common.service.AuthInfoService;
 import com.qmth.teachcloud.common.service.SysConfigService;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.qmth.teachcloud.data.service.TSyncDataService;
 import com.qmth.teachcloud.task.enums.JobEnum;
 import com.qmth.teachcloud.task.job.*;
 import com.qmth.teachcloud.task.job.service.JobService;
 import com.qmth.teachcloud.task.service.QuartzService;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.boot.CommandLineRunner;
@@ -13,6 +18,7 @@ import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -34,6 +40,10 @@ public class StartRunning implements CommandLineRunner {
     private QuartzService quartzService;
     @Resource
     private JobService jobService;
+    @Resource
+    private TSyncDataService tSyncDataService;
+    @Resource
+    private BasicExamService basicExamService;
 
     @Override
     public void run(String... args) throws Exception {
@@ -130,6 +140,30 @@ public class StartRunning implements CommandLineRunner {
         quartzService.addJob(CreatePdfTaskJob.class, JobEnum.CREATE_PDF_JOB.name(), JobEnum.CREATE_PDF_JOB.getGroupName(), "0 0/2 * * * ?", createPdfJobMap);
         log.info("服务器启动时执行,PDF生成定时任务 end");
 
+        // 数据同步
+//        dataSync();
+
         log.info("服务器启动时执行 end");
     }
+
+    private void dataSync() {
+        log.info("服务器启动时执行,数据同步定时任务 start");
+        long currentTime = System.currentTimeMillis();
+        List<TSyncData> tSyncDataList = tSyncDataService.list();
+
+        Map expireJobMap = new HashMap();
+        expireJobMap.computeIfAbsent("name", v -> DataSyncJob.class.getName());
+        for (TSyncData syncData : tSyncDataList) {
+            Long startTime = syncData.getStartTime();
+            Long endTime = syncData.getEndTime();
+            BasicExam basicExam = basicExamService.getById(syncData.getExamId());
+            // 考试已禁用、未到开始时间、超过结束时间,未设置执行时间 。不执行
+            quartzService.deleteJob(JobEnum.DATA_SYNC.name() + syncData.getSchoolId(), JobEnum.DATA_SYNC.getGroupName());
+            if (!basicExam.getEnable() || currentTime < startTime || currentTime > endTime || StringUtils.isBlank(syncData.getCron())) {
+                continue;
+            }
+            quartzService.addJob(DataSyncJob.class, JobEnum.DATA_SYNC.name() + syncData.getSchoolId(), JobEnum.DATA_SYNC.getGroupName(), syncData.getCron(), expireJobMap);
+        }
+        log.info("服务器启动时执行,数据同步定时任务 end");
+    }
 }

+ 38 - 1
distributed-print/src/main/resources/application-base.properties

@@ -94,4 +94,41 @@ com.qmth.logging.file-path=/opt/logs/distributed-print.log
 #com.qmth.solar.access-key=274f823e5f59410f8b3bb6edcd8e2b6e
 #com.qmth.solar.access-secret=y7AO6W0TOdTF8HpWBwGHbp3wfIHsmUKr
 
-com.qmth.sms.server=https://solar.qmth.com.cn
+com.qmth.sms.server=https://solar.qmth.com.cn
+
+#============================================================================
+# \u914D\u7F6EJobStore
+#============================================================================
+spring.quartz.job-store-type=jdbc
+spring.quartz.jdbc.initialize-schema=never
+# JobDataMaps\u662F\u5426\u90FD\u4E3AString\u7C7B\u578B\uFF0C\u9ED8\u8BA4false
+spring.quartz.properties.org.quartz.jobStore.useProperties=false
+# \u8868\u7684\u524D\u7F00\uFF0C\u9ED8\u8BA4QRTZ_
+spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
+# \u662F\u5426\u52A0\u5165\u96C6\u7FA4
+spring.quartz.properties.org.quartz.jobStore.isClustered=true
+# \u8C03\u5EA6\u5B9E\u4F8B\u5931\u6548\u7684\u68C0\u67E5\u65F6\u95F4\u95F4\u9694 ms
+spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=5000
+# \u5F53\u8BBE\u7F6E\u4E3A\u201Ctrue\u201D\u65F6\uFF0C\u6B64\u5C5E\u6027\u544A\u8BC9Quartz \u5728\u975E\u6258\u7BA1JDBC\u8FDE\u63A5\u4E0A\u8C03\u7528setTransactionIsolation\uFF08Connection.TRANSACTION_READ_COMMITTED\uFF09\u3002
+spring.quartz.properties.org.quartz.jobStore.txIsolationLevelReadCommitted=true
+# \u6570\u636E\u4FDD\u5B58\u65B9\u5F0F\u4E3A\u6570\u636E\u5E93\u6301\u4E45\u5316
+spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
+# \u6570\u636E\u5E93\u4EE3\u7406\u7C7B\uFF0C\u4E00\u822Corg.quartz.impl.jdbcjobstore.StdJDBCDelegate\u53EF\u4EE5\u6EE1\u8DB3\u5927\u90E8\u5206\u6570\u636E\u5E93
+spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+#============================================================================
+# Scheduler \u8C03\u5EA6\u5668\u5C5E\u6027\u914D\u7F6E
+#============================================================================
+# \u8C03\u5EA6\u6807\u8BC6\u540D \u96C6\u7FA4\u4E2D\u6BCF\u4E00\u4E2A\u5B9E\u4F8B\u90FD\u5FC5\u987B\u4F7F\u7528\u76F8\u540C\u7684\u540D\u79F0
+spring.quartz.properties.org.quartz.scheduler.instanceName=ClusterQuartz
+# ID\u8BBE\u7F6E\u4E3A\u81EA\u52A8\u83B7\u53D6 \u6BCF\u4E00\u4E2A\u5FC5\u987B\u4E0D\u540C
+spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
+
+#============================================================================
+# \u914D\u7F6EThreadPool
+#============================================================================
+# \u7EBF\u7A0B\u6C60\u7684\u5B9E\u73B0\u7C7B\uFF08\u4E00\u822C\u4F7F\u7528SimpleThreadPool\u5373\u53EF\u6EE1\u8DB3\u51E0\u4E4E\u6240\u6709\u7528\u6237\u7684\u9700\u6C42\uFF09
+spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
+# \u6307\u5B9A\u7EBF\u7A0B\u6570\uFF0C\u4E00\u822C\u8BBE\u7F6E\u4E3A1-100\u76F4\u63A5\u7684\u6574\u6570\uFF0C\u6839\u636E\u7CFB\u7EDF\u8D44\u6E90\u914D\u7F6E
+spring.quartz.properties.org.quartz.threadPool.threadCount=10
+# \u8BBE\u7F6E\u7EBF\u7A0B\u7684\u4F18\u5148\u7EA7(\u53EF\u4EE5\u662FThread.MIN_PRIORITY\uFF08\u53731\uFF09\u548CThread.MAX_PRIORITY\uFF08\u8FD9\u662F10\uFF09\u4E4B\u95F4\u7684\u4EFB\u4F55int \u3002\u9ED8\u8BA4\u503C\u4E3AThread.NORM_PRIORITY\uFF085\uFF09\u3002)
+spring.quartz.properties.org.quartz.threadPool.threadPriority=5

+ 7 - 1
pom.xml

@@ -4,7 +4,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.distributed.print.service</groupId>
     <artifactId>distributed-print-service</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <packaging>pom</packaging>
 
     <modules>
@@ -17,6 +17,7 @@
         <module>teachcloud-report</module>
         <module>teachcloud-report-business</module>
         <module>teachcloud-obe</module>
+        <module>teachcloud-data</module>
     </modules>
 
     <properties>
@@ -98,6 +99,11 @@
                 <artifactId>teachcloud-task</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.qmth.teachcloud.data</groupId>
+                <artifactId>teachcloud-data</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.qmth.teachcloud.report</groupId>
                 <artifactId>teachcloud-report</artifactId>

+ 2 - 2
teachcloud-common-api/pom.xml

@@ -4,13 +4,13 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>com.qmth.teachcloud.common.api</groupId>
 	<artifactId>teachcloud-common-api</artifactId>
-	<version>3.4.2</version>
+	<version>3.4.3</version>
 	<packaging>jar</packaging>
 
 	<parent>
 		<groupId>com.qmth.distributed.print.service</groupId>
 		<artifactId>distributed-print-service</artifactId>
-		<version>3.4.2</version>
+		<version>3.4.3</version>
 	</parent>
 
 	<dependencies>

+ 2 - 2
teachcloud-common/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.teachcloud.common</groupId>
     <artifactId>teachcloud-common</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.distributed.print.service</groupId>
         <artifactId>distributed-print-service</artifactId>
-        <version>3.4.2</version>
+        <version>3.4.3</version>
     </parent>
 
     <dependencies>

+ 7 - 1
teachcloud-common/src/main/java/com/qmth/teachcloud/common/contant/SystemConstant.java

@@ -223,6 +223,12 @@ public class SystemConstant {
     public static final String PDF_SIZE_LIST = "pdf.size.list";
     public static final String CARD_SIZE_LIST = "card.size.list";
     public static final String STUDENT_NUMBER_ALLOCATION = "student.number.allocation";
+    public static final String DATA_DATASOURCE_TYPE = "data.datasource.type";
+    public static final String DATA_DATASOURCE_HOST = "data.datasource.host";
+    public static final String DATA_DATASOURCE_DATANAME = "data.datasource.dataname";
+    public static final String DATA_DATASOURCE_PORT = "data.datasource.port";
+    public static final String DATA_DATASOURCE_USERNAME = "data.datasource.username";
+    public static final String DATA_DATASOURCE_PASSWORD = "data.datasource.password";
 
     /**
      * 系统常量
@@ -270,7 +276,7 @@ public class SystemConstant {
     public static final String ID = "id";
     public static final String MODEL = "model";
     public static final String VERSION = "version";
-    public static final String VERSION_VALUE = "3.4.2";
+    public static final String VERSION_VALUE = "3.4.3";
     public static final String FLOW_ENTITY_ID = "flowEntityId";
     public static final String CUSTOM_FLOW_ID = "customFlowId";
     public static final String ACT_FLOW_ID = "actFlowId";

+ 11 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/BasicCourse.java

@@ -57,6 +57,9 @@ public class BasicCourse extends BaseEntity implements Serializable {
     @ApiModelProperty("历史课程名称")
     private String historicName;
 
+    @TableField(exist = false)
+    private String collegeName;
+
     public BasicCourse() {
     }
     public BasicCourse(Long schoolId, Long teachingRoomId, String code, String name) {
@@ -134,4 +137,12 @@ public class BasicCourse extends BaseEntity implements Serializable {
     public void setTeachingRoomId(Long teachingRoomId) {
         this.teachingRoomId = teachingRoomId;
     }
+
+    public String getCollegeName() {
+        return collegeName;
+    }
+
+    public void setCollegeName(String collegeName) {
+        this.collegeName = collegeName;
+    }
 }

+ 2 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/BasicCourseService.java

@@ -96,4 +96,6 @@ public interface BasicCourseService extends IService<BasicCourse> {
     BasicCourse getByTeachRoomIdAndCode(Long teachRoomId, String courseCode);
 
     List<BasicCourse> listBySchoolId(Long schoolId);
+
+    void saveBasicCourseFormSync(BasicCourse transferToBasicCourse);
 }

+ 5 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/SysOrgService.java

@@ -184,4 +184,9 @@ public interface SysOrgService extends IService<SysOrg> {
     boolean delete(Long id);
 
     List<SysOrg> listPrintHouse(Long schoolId, OrgTypeEnum type);
+
+    void saveOrgFromSync(SysOrg transferToSysOrg);
+
+    SysOrg findByParentIdAndName(Long parentId, String name);
+
 }

+ 2 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/SysUserService.java

@@ -250,4 +250,6 @@ public interface SysUserService extends IService<SysUser> {
     SysUser getByLoginName(Long schoolId, String loginName);
 
     List<SysUserResult> listBySchoolId(Long schoolId);
+
+    void saveUserFromSync(SysUser sysUser);
 }

+ 15 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/BasicCourseServiceImpl.java

@@ -358,4 +358,19 @@ public class BasicCourseServiceImpl extends ServiceImpl<BasicCourseMapper, Basic
         queryWrapper.lambda().eq(BasicCourse::getSchoolId, schoolId);
         return this.list(queryWrapper);
     }
+
+    @Override
+    public void saveBasicCourseFormSync(BasicCourse basicCourse) {
+        SysOrg rootOrg = sysOrgService.findRootOrg(basicCourse.getSchoolId());
+        SysOrg sysOrg = sysOrgService.findByParentIdAndName(rootOrg.getId(), basicCourse.getCollegeName());
+        if (sysOrg == null) {
+            throw ExceptionResultEnum.ERROR.exception("开课学院[" + sysOrg.getName() + "]在组织架构中不存在");
+        }
+        BasicCourse basicCourse1 = this.getByTeachRoomIdAndCode(sysOrg.getId(), basicCourse.getCode());
+        if (basicCourse1 == null) {
+            basicCourse.setId(SystemConstant.getDbUuid());
+            basicCourse.setCreateTime(System.currentTimeMillis());
+            this.save(basicCourse);
+        }
+    }
 }

+ 19 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/SysOrgServiceImpl.java

@@ -586,6 +586,25 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
         return this.list(queryWrapper);
     }
 
+    @Override
+    public void saveOrgFromSync(SysOrg sysOrg) {
+        SysOrg rootOrg = this.findRootOrg(sysOrg.getSchoolId());
+        SysOrg org = this.findByParentIdAndName(rootOrg.getId(), sysOrg.getName());
+        if (org == null) {
+            org.setId(SystemConstant.getDbUuid());
+            org.setParentId(rootOrg.getId());
+            this.save(org);
+        }
+    }
+
+    @Override
+    public SysOrg findByParentIdAndName(Long parentId, String name) {
+        QueryWrapper<SysOrg> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(SysOrg::getParentId, parentId)
+                .eq(SysOrg::getName, name);
+        return this.getOne(queryWrapper);
+    }
+
     /**
      * 根据子机构id深度优先搜索其父机构们
      *

+ 5 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/SysUserServiceImpl.java

@@ -1169,4 +1169,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
     public List<SysUserResult> listBySchoolId(Long schoolId) {
         return this.baseMapper.listBySchoolId(schoolId);
     }
+
+    @Override
+    public void saveUserFromSync(SysUser sysUser) {
+
+    }
 }

+ 33 - 0
teachcloud-data/.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 58 - 0
teachcloud-data/pom.xml

@@ -0,0 +1,58 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.qmth.teachcloud.data</groupId>
+    <artifactId>teachcloud-data</artifactId>
+    <version>3.4.3</version>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <packaging>jar</packaging>
+
+    <parent>
+        <groupId>com.qmth.distributed.print.service</groupId>
+        <artifactId>distributed-print-service</artifactId>
+        <version>3.4.3</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.qmth.teachcloud.common</groupId>
+            <artifactId>teachcloud-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.qmth.distributed.print.business</groupId>
+            <artifactId>distributed-print-business</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>1.2.15</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-dbutils</groupId>
+            <artifactId>commons-dbutils</artifactId>
+            <version>1.8.1</version>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 299 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/DataUtil.java

@@ -0,0 +1,299 @@
+package com.qmth.teachcloud.data;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.qmth.teachcloud.common.bean.result.SysConfigResult;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.entity.SysConfig;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.service.CommonCacheService;
+import com.qmth.teachcloud.data.vo.*;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class DataUtil {
+
+    // 一页1000条
+    private static int PAGE_SIZE = 1000;
+
+    @Resource
+    private CommonCacheService commonCacheService;
+
+    public List<OrgDataVo> listOrg(JdbcTemplate jdbcTemplate) {
+        //sql语句
+        String sql = "select * from sys_user";
+        return query(jdbcTemplate, sql, OrgDataVo.class);
+    }
+
+    public List<UserDataVo> listUser(JdbcTemplate jdbcTemplate) {
+        //sql语句
+        String sql = "select * from sys_user limit ? ?";
+        return page(jdbcTemplate, sql, UserDataVo.class);
+    }
+
+    public List<CourseDataVo> listCourse(JdbcTemplate jdbcTemplate) {
+        //sql语句
+        String sql = "select * from sys_user";
+        return query(jdbcTemplate, sql, CourseDataVo.class);
+    }
+
+    public List<ExamTaskDataVo> listExamTask(JdbcTemplate jdbcTemplate) {
+        //sql语句
+        String sql = "select * from sys_user";
+        return query(jdbcTemplate, sql, ExamTaskDataVo.class);
+
+    }
+
+    /**
+     * 不分页
+     *
+     * @param sql
+     */
+    private <T> List<T> query(JdbcTemplate jdbcTemplate, String sql, Class<T> tClass) {
+        if (sql.endsWith("limit ? ?")) {
+            throw ExceptionResultEnum.ERROR.exception("sql中设置了分页参数");
+        }
+        List<T> list;
+        try {
+            list = jdbcTemplate.queryForList(sql, tClass);
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception("查询失败:" + e.getMessage());
+        }
+        return list;
+    }
+
+    /**
+     * 分页
+     *
+     * @param sql
+     */
+    private <T> List<T> page(JdbcTemplate jdbcTemplate, String sql, Class<T> tClass) {
+        if (!sql.endsWith("limit ? ?")) {
+            throw ExceptionResultEnum.ERROR.exception("sql中未设置分页参数");
+        }
+        //将查询的语句封装到List集合中,集合中存储每一个员工实体
+        List<T> listAll = new ArrayList<>();
+        try {
+            List<T> list;
+            int pageNumber = 0;
+            do {
+                int offset = pageNumber * PAGE_SIZE + PAGE_SIZE;
+                list = jdbcTemplate.queryForList(sql, tClass, pageNumber, offset);
+                if (CollectionUtils.isNotEmpty(list)) {
+                    listAll.addAll(list);
+                    pageNumber++;
+                }
+            } while (CollectionUtils.isNotEmpty(list));
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception("查询失败:" + e.getMessage());
+        }
+        return listAll;
+    }
+
+    /**
+     * 不分页
+     *
+     * @param sql
+     */
+    /*private <T> List<T> query(String sql, Class<T> tClass) {
+        if (sql.endsWith("limit ? ?")) {
+            throw ExceptionResultEnum.ERROR.exception("sql中设置了分页参数");
+        }
+        //创建QueryRunner
+        QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource(createDataSourceMap()));
+        List<T> list;
+        try {
+            list = qr.query(sql, new BeanListHandler<>(tClass));
+        } catch (SQLException e) {
+            throw ExceptionResultEnum.ERROR.exception("查询失败:" + e.getMessage());
+        }
+        return list;
+    }
+
+    */
+
+    /**
+     * 分页
+     *//*
+    private <T> List<T> page(String sql, Class<T> tClass) {
+        if (!sql.endsWith("limit ? ?")) {
+            throw ExceptionResultEnum.ERROR.exception("sql中未设置分页参数");
+        }
+        //创建QueryRunner
+        QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource(createDataSourceMap()));
+        //将查询的语句封装到List集合中,集合中存储每一个员工实体
+        List<T> listAll = new ArrayList<>();
+        try {
+            List<T> list;
+            int pageNumber = 0;
+            do {
+                int offset = pageNumber * PAGE_SIZE + PAGE_SIZE;
+                list = qr.query(sql, new BeanListHandler<>(tClass), pageNumber, offset);
+                if (CollectionUtils.isNotEmpty(list)) {
+                    listAll.addAll(list);
+                    pageNumber++;
+                }
+            } while (CollectionUtils.isNotEmpty(list));
+        } catch (SQLException e) {
+            throw ExceptionResultEnum.ERROR.exception("查询失败:" + e.getMessage());
+        }
+        return listAll;
+    }*/
+    public DataSource createDataSourceMap(Long schoolId) {
+        SysConfig datasourceType = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_TYPE);
+        if (datasourceType == null || StringUtils.isBlank(datasourceType.getConfigValue())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置数据库类型");
+        }
+        // 数据库HOST
+        SysConfig datasourceHost = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_HOST);
+        if (datasourceHost == null || StringUtils.isBlank(datasourceHost.getConfigValue())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置HOST");
+        }
+        // 数据库PORT
+        SysConfig datasourcePort = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_PORT);
+        if (datasourcePort == null || StringUtils.isBlank(datasourcePort.getConfigValue())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置端口");
+        }
+        // 数据库DataName
+        SysConfig datasourceDataName = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_DATANAME);
+        if (datasourceDataName == null || StringUtils.isBlank(datasourceDataName.getConfigValue())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置数据库名");
+        }
+        // 数据库用户名
+        SysConfig datasourceUserName = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_USERNAME);
+        if (datasourceUserName == null || StringUtils.isBlank(datasourceUserName.getConfigValue())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置用户名");
+        }
+        String userName = datasourceUserName.getConfigValue().trim();
+        // 数据库密码
+        SysConfig datasourcePassword = commonCacheService.addSysConfigCache(schoolId, SystemConstant.DATA_DATASOURCE_PASSWORD);
+        if (datasourcePassword == null || StringUtils.isBlank(datasourcePassword.getConfigValue())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置密码");
+        }
+        String password = datasourcePassword.getConfigValue().trim();
+        Map<String, String> map = new HashMap<>();
+
+        String driverClassName;
+        String url;
+        if ("ORACLE".equals(datasourceType.getConfigValue())) {
+            driverClassName = "com.mysql.jdbc.Driver";
+            url = "jdbc:oracle:thin:@" + datasourceHost.getConfigValue().trim() + ":" + datasourcePort.getConfigValue().trim() + ":" + datasourceDataName.getConfigValue().trim();
+        } else if ("MYSQL".equals(datasourceType.getConfigValue())) {
+            driverClassName = "com.mysql.jdbc.Driver";
+            url = "jdbc:mysql://" + datasourceHost.getConfigValue().trim() + ":" + datasourcePort.getConfigValue().trim() + "/" + datasourceDataName.getConfigValue().trim() + "?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8";
+        } else {
+            throw ExceptionResultEnum.ERROR.exception("只支持Oracle和Mysql数据库");
+        }
+        try {
+            Class.forName(driverClassName);
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        return new DriverManagerDataSource(url, userName, password);
+    }
+
+
+    public DataSource createDataSourceMap(List<SysConfigResult> params) {
+        SysConfigResult datasourceType = params.stream().filter(m->m.getCode().equals(SystemConstant.DATA_DATASOURCE_TYPE)).findFirst().orElse(null);
+        if (datasourceType == null || StringUtils.isBlank(datasourceType.getValue().toString())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置数据库类型");
+        }
+        String type = datasourceType.getValue().toString().trim();
+        // 数据库HOST
+        SysConfigResult datasourceHost = params.stream().filter(m->m.getCode().equals(SystemConstant.DATA_DATASOURCE_HOST)).findFirst().orElse(null);
+        if (datasourceHost == null || StringUtils.isBlank(datasourceHost.getValue().toString())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置HOST");
+        }
+        String host = datasourceHost.getValue().toString().trim();
+        // 数据库PORT
+        SysConfigResult datasourcePort = params.stream().filter(m->m.getCode().equals(SystemConstant.DATA_DATASOURCE_PORT)).findFirst().orElse(null);
+        if (datasourcePort == null || StringUtils.isBlank(datasourcePort.getValue().toString())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置端口");
+        }
+        String port = datasourcePort.getValue().toString().trim();
+        // 数据库DataName
+        SysConfigResult datasourceDataName = params.stream().filter(m->m.getCode().equals(SystemConstant.DATA_DATASOURCE_DATANAME)).findFirst().orElse(null);
+        if (datasourceDataName == null || StringUtils.isBlank(datasourceDataName.getValue().toString())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置数据库名");
+        }
+        String dataName = datasourceDataName.getValue().toString().trim();
+        // 数据库用户名
+        SysConfigResult datasourceUserName = params.stream().filter(m->m.getCode().equals(SystemConstant.DATA_DATASOURCE_USERNAME)).findFirst().orElse(null);
+        if (datasourceUserName == null || StringUtils.isBlank(datasourceUserName.getValue().toString())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置用户名");
+        }
+        String userName = datasourceUserName.getValue().toString().trim();
+        // 数据库密码
+        SysConfigResult datasourcePassword = params.stream().filter(m->m.getCode().equals(SystemConstant.DATA_DATASOURCE_PASSWORD)).findFirst().orElse(null);
+        if (datasourcePassword == null || StringUtils.isBlank(datasourcePassword.getValue().toString())) {
+            throw ExceptionResultEnum.ERROR.exception("未设置密码");
+        }
+        String password = datasourcePassword.getValue().toString().trim();
+        Map<String, String> map = new HashMap<>();
+
+        String driverClassName;
+        String url;
+        if ("ORACLE".equals(type)) {
+            driverClassName = "com.mysql.jdbc.Driver";
+            url = "jdbc:oracle:thin:@" + host + ":" + port + ":" + dataName;
+        } else if ("MYSQL".equals(type)) {
+            driverClassName = "com.mysql.jdbc.Driver";
+            url = "jdbc:mysql://" + host + ":" + port + "/" + dataName + "?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&nullCatalogMeansCurrent=true";
+        } else {
+            throw ExceptionResultEnum.ERROR.exception("只支持Oracle和Mysql数据库");
+        }
+        try {
+            Class.forName(driverClassName);
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        return new DriverManagerDataSource(url, userName, password);
+    }
+
+    public static void main(String[] args) {
+        sqlConn("0", "localhost", "3306", "teachcloud-3.4.3", "root", "12345678");
+
+    }
+
+    public static String sqlConn(String databaseType, String address, String port, String databaseName, String userName, String password) {
+        String driverName = null;
+        String url = null;
+        if ("0".equals(databaseType)) {
+            //mysql
+            driverName = "com.mysql.cj.jdbc.Driver";
+            url = "jdbc:mysql://" + address + ":" + port + "/" + databaseName + "?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8";
+        } else if ("1".equals(databaseType)) {
+            //oracle
+            driverName = "oracle.jdbc.driver.OracleDriver";
+            url = "jdbc:oracle:thin:@" + address + ":" + port + ":" + databaseName;
+        } else if ("2".equals(databaseType)) {
+            //sqlserver
+            driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
+            url = "jdbc:sqlserver://" + address + ":" + port + ";DatabaseName=" + databaseName;
+        }
+        try {
+            Class.forName(driverName);
+            Connection connection = DriverManager.getConnection(url, userName, password);
+            connection.close();
+        } catch (Exception e) {
+            return "测试失败";
+        }
+        return "测试成功";
+    }
+
+    public List<ExamStudentDataVo> listExamStudent(JdbcTemplate jdbcTemplate) {
+        return null;
+    }
+}

+ 203 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/entity/TSyncData.java

@@ -0,0 +1,203 @@
+package com.qmth.teachcloud.data.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * <p>
+ * 数据同步设置
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+@TableName("t_sync_data")
+@ApiModel(value="TSyncData对象", description="数据同步设置")
+public class TSyncData implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @JsonSerialize(using = ToStringSerializer.class)
+    @ApiModelProperty(value = "学校ID")
+    @MppMultiId(value = "school_id")
+    private Long schoolId;
+
+    @ApiModelProperty(value = "学期ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long semesterId;
+
+    @ApiModelProperty(value = "考试ID")
+    @MppMultiId(value = "exam_id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long examId;
+
+    @ApiModelProperty(value = "同步数据类型(A:机构同步,B:用户同步,C:课程同步,D:考生同步,E:命题任务同步)")
+    private String dataType;
+
+    @ApiModelProperty(value = "同步开始时间")
+    private Long startTime;
+
+    @ApiModelProperty(value = "同步结束时间")
+    private Long endTime;
+
+    @ApiModelProperty(value = "最后一次同步时间")
+    private Long lastSyncTime;
+
+    @ApiModelProperty(value = "状态(0:未同步/已完成,1:同步中)")
+    private Boolean status;
+
+    @ApiModelProperty(value = "cron表达式")
+    private String cron;
+
+    @ApiModelProperty(value = "手机号(多个用逗号分隔)")
+    private String phoneNumber;
+    @ApiModelProperty(value = "同步明细")
+    private String detail;
+
+    @ApiModelProperty(value = "创建时间")
+    private Long createTime;
+
+    @ApiModelProperty(value = "更新时间")
+    private Long updateTime;
+
+    @ApiModelProperty(value = "启用/禁用")
+    private Boolean enable;
+
+    public TSyncData() {
+    }
+
+    public TSyncData(Long schoolId, Long examId) {
+        this.schoolId = schoolId;
+        this.examId = examId;
+    }
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+    public Long getSemesterId() {
+        return semesterId;
+    }
+
+    public void setSemesterId(Long semesterId) {
+        this.semesterId = semesterId;
+    }
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getDataType() {
+        return dataType;
+    }
+
+    public void setDataType(String dataType) {
+        this.dataType = dataType;
+    }
+
+    public Long getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Long startTime) {
+        this.startTime = startTime;
+    }
+    public Long getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Long endTime) {
+        this.endTime = endTime;
+    }
+    public Long getLastSyncTime() {
+        return lastSyncTime;
+    }
+
+    public void setLastSyncTime(Long lastSyncTime) {
+        this.lastSyncTime = lastSyncTime;
+    }
+
+    public Boolean getStatus() {
+        return status;
+    }
+
+    public void setStatus(Boolean status) {
+        this.status = status;
+    }
+
+    public String getCron() {
+        return cron;
+    }
+
+    public void setCron(String cron) {
+        this.cron = cron;
+    }
+
+    public String getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public void setPhoneNumber(String phoneNumber) {
+        this.phoneNumber = phoneNumber;
+    }
+
+    public String getDetail() {
+        return detail;
+    }
+
+    public void setDetail(String detail) {
+        this.detail = detail;
+    }
+    public Long getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Long createTime) {
+        this.createTime = createTime;
+    }
+    public Long getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Long updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+
+    @Override
+    public String toString() {
+        return "TSyncData{" +
+            "schoolId=" + schoolId +
+            ", semesterId=" + semesterId +
+            ", examId=" + examId +
+            ", dataType=" + dataType +
+            ", startTime=" + startTime +
+            ", endTime=" + endTime +
+            ", lastSyncTime=" + lastSyncTime +
+            ", detail=" + detail +
+            ", createTime=" + createTime +
+            ", updateTime=" + updateTime +
+        "}";
+    }
+}

+ 117 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/entity/TSyncDataLog.java

@@ -0,0 +1,117 @@
+package com.qmth.teachcloud.data.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * <p>
+ * 数据同步日志
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+@TableName("t_sync_data_log")
+@ApiModel(value="TSyncDataLog对象", description="数据同步日志")
+public class TSyncDataLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @JsonSerialize(using = ToStringSerializer.class)
+    @ApiModelProperty(value = "学校ID")
+    @MppMultiId(value = "school_id")
+    private Long schoolId;
+
+    @JsonSerialize(using = ToStringSerializer.class)
+    @ApiModelProperty(value = "考试ID")
+    @MppMultiId(value = "exam_id")
+    private Long examId;
+
+    @ApiModelProperty(value = "同步数据类型")
+    @MppMultiId(value = "data_type")
+    private String dataType;
+
+    @ApiModelProperty(value = "开始同步时间")
+    private Long startTime;
+
+    @ApiModelProperty(value = "结束同步时间")
+    private Long endTime;
+
+    @ApiModelProperty(value = "同步数量")
+    private Integer count;
+
+    @ApiModelProperty(value = "失败原因")
+    private String errorMsg;
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+    public String getDataType() {
+        return dataType;
+    }
+
+    public void setDataType(String dataType) {
+        this.dataType = dataType;
+    }
+    public Long getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Long startTime) {
+        this.startTime = startTime;
+    }
+    public Long getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Long endTime) {
+        this.endTime = endTime;
+    }
+    public Integer getCount() {
+        return count;
+    }
+
+    public void setCount(Integer count) {
+        this.count = count;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+    @Override
+    public String toString() {
+        return "TSyncDataLog{" +
+            "schoolId=" + schoolId +
+            ", examId=" + examId +
+            ", dataType=" + dataType +
+            ", startTime=" + startTime +
+            ", endTime=" + endTime +
+            ", count=" + count +
+            ", errorMsg=" + errorMsg +
+        "}";
+    }
+}

+ 17 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/mapper/TSyncDataLogMapper.java

@@ -0,0 +1,17 @@
+package com.qmth.teachcloud.data.mapper;
+
+import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
+import com.qmth.teachcloud.data.entity.TSyncDataLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 数据同步日志 Mapper 接口
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+public interface TSyncDataLogMapper extends MppBaseMapper<TSyncDataLog> {
+
+}

+ 17 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/mapper/TSyncDataMapper.java

@@ -0,0 +1,17 @@
+package com.qmth.teachcloud.data.mapper;
+
+import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 数据同步设置 Mapper 接口
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+public interface TSyncDataMapper extends MppBaseMapper<TSyncData> {
+
+}

+ 13 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/DataService.java

@@ -0,0 +1,13 @@
+package com.qmth.teachcloud.data.service;
+
+import com.qmth.distributed.print.business.bean.params.SysAdminSetParam;
+import com.qmth.teachcloud.common.entity.SysUser;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.qmth.teachcloud.data.vo.*;
+
+import java.util.List;
+
+public interface DataService {
+    boolean testConnect(SysAdminSetParam sysAdminSetParam);
+    void syncData(Long schoolId, Long examId, TSyncData syncData);
+}

+ 17 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/TSyncDataLogService.java

@@ -0,0 +1,17 @@
+package com.qmth.teachcloud.data.service;
+
+import com.github.jeffreyning.mybatisplus.service.IMppService;
+import com.qmth.teachcloud.data.entity.TSyncDataLog;
+
+/**
+ * <p>
+ * 数据同步日志 服务类
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+public interface TSyncDataLogService extends IMppService<TSyncDataLog> {
+
+    void saveLog(Long schoolId, Long examId, String dataType, long startTime, long endTime, int count, String errorMsg);
+}

+ 27 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/TSyncDataService.java

@@ -0,0 +1,27 @@
+package com.qmth.teachcloud.data.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.jeffreyning.mybatisplus.service.IMppService;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.qmth.teachcloud.data.entity.TSyncDataLog;
+
+/**
+ * <p>
+ * 数据同步设置 服务类
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+public interface TSyncDataService extends IMppService<TSyncData> {
+
+    boolean saveData(TSyncData tSyncData);
+
+    boolean syncData(Long schoolId, Long examId);
+
+    IPage<TSyncDataLog> getDataLog(Long schoolId, Long examId, Integer pageNumber, Integer pageSize);
+
+    IPage<TSyncData> listParam(Long schoolId, Integer pageNumber, Integer pageSize);
+
+    boolean enable(TSyncData tSyncData);
+}

+ 137 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/impl/DataServiceImpl.java

@@ -0,0 +1,137 @@
+package com.qmth.teachcloud.data.service.impl;
+
+import com.qmth.distributed.print.business.bean.params.SysAdminSetParam;
+import com.qmth.teachcloud.common.entity.SysOrg;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.service.BasicCourseService;
+import com.qmth.teachcloud.common.service.SysOrgService;
+import com.qmth.teachcloud.common.service.SysUserService;
+import com.qmth.teachcloud.data.DataUtil;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.qmth.teachcloud.data.service.DataService;
+import com.qmth.teachcloud.data.service.TSyncDataLogService;
+import com.qmth.teachcloud.data.util.DruidJdbcUtils;
+import com.qmth.teachcloud.data.util.JdbcUtil;
+import com.qmth.teachcloud.data.vo.*;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+
+@Service
+public class DataServiceImpl implements DataService {
+
+    @Resource
+    private DataUtil dataUtil;
+    @Resource
+    private TSyncDataLogService tSyncDataLogService;
+    @Resource
+    private BasicCourseService basicCourseService;
+    @Resource
+    private SysOrgService sysOrgService;
+    @Resource
+    private SysUserService sysUserService;
+
+    @Override
+    public boolean testConnect(SysAdminSetParam sysAdminSetParam) {
+        try {
+            JdbcTemplate jdbcTemplate = JdbcUtil.getJdbcTemplate(dataUtil.createDataSourceMap(sysAdminSetParam.getParam()));
+            String sql = "select 1 from dual";
+            String object = jdbcTemplate.queryForObject(sql, String.class);
+            if (StringUtils.isNotBlank(object)) {
+                return true;
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
+        }
+    }
+
+    @Override
+    public void syncData(Long schoolId, Long examId, TSyncData syncData) {
+        JdbcTemplate jdbcTemplate = JdbcUtil.getJdbcTemplate(dataUtil.createDataSourceMap(schoolId));
+        String dataRange = syncData.getDataType();
+        // 机构同步
+        if (dataRange.contains("A")) {
+            long startTime = System.currentTimeMillis();
+            List<OrgDataVo> dataVoList = dataUtil.listOrg(jdbcTemplate);
+            int count = dataVoList.size();
+            String errorMsg = null;
+            try {
+                for (OrgDataVo orgDataVo : dataVoList) {
+                    sysOrgService.saveOrgFromSync(orgDataVo.transferToSysOrg(schoolId, orgDataVo));
+                }
+            } catch (Exception e) {
+                count = 0;
+                errorMsg = e.getMessage();
+            } finally {
+                long endTime = System.currentTimeMillis();
+                tSyncDataLogService.saveLog(schoolId, examId, "A", startTime, endTime, count, errorMsg);
+            }
+        }
+        // 用户同步
+        if (dataRange.contains("B")) {
+            long startTime = System.currentTimeMillis();
+            List<UserDataVo> dataVoList = dataUtil.listUser(jdbcTemplate);
+            int count = dataVoList.size();
+            String errorMsg = null;
+            try {
+                for (UserDataVo userDataVo : dataVoList) {
+                    sysUserService.saveUserFromSync(userDataVo.transferToUser(schoolId, userDataVo));
+                }
+            } catch (Exception e) {
+                count = 0;
+                errorMsg = e.getMessage();
+            } finally {
+                long endTime = System.currentTimeMillis();
+                tSyncDataLogService.saveLog(schoolId, examId, "B", startTime, endTime, count, errorMsg);
+            }
+        }
+        // 课程同步
+        if (dataRange.contains("C")) {
+            long startTime = System.currentTimeMillis();
+            List<CourseDataVo> dataVoList = dataUtil.listCourse(jdbcTemplate);
+            int count = dataVoList.size();
+            String errorMsg = null;
+            try {
+                for (CourseDataVo courseDataVo : dataVoList) {
+                    basicCourseService.saveBasicCourseFormSync(courseDataVo.transferToBasicCourse(schoolId, courseDataVo));
+                }
+            } catch (Exception e) {
+                count = 0;
+                errorMsg = e.getMessage();
+            } finally {
+                long endTime = System.currentTimeMillis();
+                tSyncDataLogService.saveLog(schoolId, examId, "C", startTime, endTime, count, errorMsg);
+            }
+        }
+        // 考生同步
+        if (dataRange.contains("D")) {
+            List<ExamStudentDataVo> dataVoList = dataUtil.listExamStudent(jdbcTemplate);
+        }
+        // 命题任务同步
+        if (dataRange.contains("E")) {
+            long startTime = System.currentTimeMillis();
+            List<ExamTaskDataVo> dataVoList = dataUtil.listExamTask(jdbcTemplate);
+            int count = dataVoList.size();
+            String errorMsg = null;
+            try {
+                for (ExamTaskDataVo courseDataVo : dataVoList) {
+//                    basicCourseService.saveBasicCourseFormSync(courseDataVo.transferToBasicCourse(schoolId, courseDataVo));
+                }
+            } catch (Exception e) {
+                count = 0;
+                errorMsg = e.getMessage();
+            } finally {
+                long endTime = System.currentTimeMillis();
+                tSyncDataLogService.saveLog(schoolId, examId, "C", startTime, endTime, count, errorMsg);
+            }
+        }
+    }
+}

+ 32 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/impl/TSyncDataLogServiceImpl.java

@@ -0,0 +1,32 @@
+package com.qmth.teachcloud.data.service.impl;
+
+import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
+import com.qmth.teachcloud.data.entity.TSyncDataLog;
+import com.qmth.teachcloud.data.mapper.TSyncDataLogMapper;
+import com.qmth.teachcloud.data.service.TSyncDataLogService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 数据同步日志 服务实现类
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+@Service
+public class TSyncDataLogServiceImpl extends MppServiceImpl<TSyncDataLogMapper, TSyncDataLog> implements TSyncDataLogService {
+
+    @Override
+    public void saveLog(Long schoolId, Long examId, String dataType, long startTime, long endTime, int count, String errorMsg) {
+        TSyncDataLog tSyncDataLog = new TSyncDataLog();
+        tSyncDataLog.setSchoolId(schoolId);
+        tSyncDataLog.setExamId(examId);
+        tSyncDataLog.setDataType(dataType);
+        tSyncDataLog.setStartTime(startTime);
+        tSyncDataLog.setEndTime(endTime);
+        tSyncDataLog.setCount(count);
+        tSyncDataLog.setErrorMsg(errorMsg);
+        this.save(tSyncDataLog);
+    }
+}

+ 114 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/service/impl/TSyncDataServiceImpl.java

@@ -0,0 +1,114 @@
+package com.qmth.teachcloud.data.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.data.DataUtil;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.qmth.teachcloud.data.entity.TSyncDataLog;
+import com.qmth.teachcloud.data.mapper.TSyncDataMapper;
+import com.qmth.teachcloud.data.service.DataService;
+import com.qmth.teachcloud.data.service.TSyncDataLogService;
+import com.qmth.teachcloud.data.service.TSyncDataService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * <p>
+ * 数据同步设置 服务实现类
+ * </p>
+ *
+ * @author xf
+ * @since 2024-12-05
+ */
+@Service
+public class TSyncDataServiceImpl extends MppServiceImpl<TSyncDataMapper, TSyncData> implements TSyncDataService {
+
+    @Resource
+    private DataService dataService;
+    @Resource
+    private TSyncDataLogService tSyncDataLogService;
+    private DataUtil dataUtil;
+    @Resource
+    private ConcurrentService concurrentService;
+
+    @Override
+    public boolean saveData(TSyncData tSyncData) {
+        TSyncData syncData = this.selectByMultiId(new TSyncData(tSyncData.getSchoolId(), tSyncData.getExamId()));
+        if (syncData == null) {
+            tSyncData.setCreateTime(System.currentTimeMillis());
+            this.saveOrUpdateByMultiId(tSyncData);
+        } else {
+            if (syncData.getStatus()) {
+                throw ExceptionResultEnum.ERROR.exception("数据同步中,不能修改");
+            }
+            syncData.setDataType(tSyncData.getDataType());
+            syncData.setStartTime(tSyncData.getStartTime());
+            syncData.setEndTime(tSyncData.getEndTime());
+            syncData.setCron(tSyncData.getCron());
+            syncData.setPhoneNumber(tSyncData.getPhoneNumber());
+            syncData.setUpdateTime(System.currentTimeMillis());
+            this.updateByMultiId(syncData);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean syncData(Long schoolId, Long examId) {
+        concurrentService.getReadWriteLock("TEACHCLOUD_SYNC_DATA" + "-" + schoolId + "-" + examId).writeLock().lock();
+        TSyncData syncData = this.selectByMultiId(new TSyncData(schoolId, examId));
+        try {
+            if (syncData == null) {
+                throw ExceptionResultEnum.ERROR.exception("数据不存在");
+            } else if (syncData.getStatus()) {
+                throw ExceptionResultEnum.ERROR.exception("数据正在同步中");
+            }
+            // 更新状态
+            syncData.setStatus(true);
+            this.updateByMultiId(syncData);
+            dataService.syncData(schoolId, examId, syncData);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            syncData.setStatus(false);
+            syncData.setLastSyncTime(System.currentTimeMillis());
+            this.updateByMultiId(syncData);
+            concurrentService.getReadWriteLock("TEACHCLOUD_SYNC_DATA" + "-" + schoolId + "-" + examId).writeLock().unlock();
+        }
+        return true;
+    }
+
+    @Override
+    public IPage<TSyncDataLog> getDataLog(Long schoolId, Long examId, Integer pageNumber, Integer pageSize) {
+        Page<TSyncDataLog> page = new Page<>(pageNumber, pageSize);
+        QueryWrapper<TSyncDataLog> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(TSyncDataLog::getSchoolId, schoolId)
+                .eq(TSyncDataLog::getExamId, examId)
+                .orderByAsc(TSyncDataLog::getDataType);
+        return tSyncDataLogService.page(page, queryWrapper);
+    }
+
+    @Override
+    public IPage<TSyncData> listParam(Long schoolId, Integer pageNumber, Integer pageSize) {
+        Page<TSyncData> page = new Page<>(pageNumber, pageSize);
+        QueryWrapper<TSyncData> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(TSyncData::getSchoolId, schoolId)
+                .orderByDesc(TSyncData::getExamId);
+        return this.page(page, queryWrapper);
+    }
+
+    @Override
+    public boolean enable(TSyncData tSyncData) {
+        UpdateWrapper<TSyncData> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(TSyncData::getEnable, tSyncData.getEnable())
+                .eq(TSyncData::getSchoolId, tSyncData.getSchoolId())
+                .eq(TSyncData::getExamId, tSyncData.getExamId());
+        return this.update(updateWrapper);
+    }
+
+}

+ 126 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/util/DruidJdbcUtils.java

@@ -0,0 +1,126 @@
+package com.qmth.teachcloud.data.util;
+
+import com.alibaba.druid.pool.DruidDataSourceFactory;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+
+public class DruidJdbcUtils {
+    //声明
+    private static DataSource ds;
+
+    //构造方法私有化
+    private DruidJdbcUtils() {
+    }
+
+    //获取DataSource数据源信息(连接池中所有的参数)
+    public static DataSource getDataSource(Map map) {
+        try {
+            //读取德鲁伊的配置文件,让德鲁伊自己封装配置文件参数
+            //获取字符输入流
+            if (map.isEmpty()) {
+                throw ExceptionResultEnum.ERROR.exception("未配置数据库连接参数");
+            }
+            //创建DruidDataSource(连接池)DruidDataSourceFactory
+            ds = DruidDataSourceFactory.createDataSource(map);
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
+        }
+        return ds;
+    }
+
+    //获取DataSource数据源信息(连接池的所有参数)
+    public static Connection getConnection() {
+        //1)从当前线程中获取存储的内容
+        Connection conn = null;
+
+        try {
+            //2)判断是否获取到连接对象
+            if (conn == null) {
+                //3)从数据源(连接池中)获取Connection
+                conn = ds.getConnection();
+            }
+
+        } catch (SQLException e) {
+//            e.printStackTrace();
+            throw new RuntimeException(e.getMessage());
+        }
+        return conn;
+    }
+
+    //释放资源---在配合dbutils使用时不需要释放资源,dbutils里面会自己释放资源
+    public static void close(ResultSet rs, Statement stmt, Connection conn) {
+        if (rs != null) {
+            try {
+                rs.close();
+            } catch (SQLException e) {
+                e.printStackTrace();
+            }
+        }
+        if (stmt != null) {
+            try {
+                stmt.close();
+            } catch (SQLException e) {
+                e.printStackTrace();
+            }
+        }
+        if (conn != null) {
+            try {
+                conn.close();
+            } catch (SQLException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    //主要针对DDL/DML语句操作释放资源---在配合dbutils使用时不需要释放资源,dbutils里面会自己释放资源
+    public static void close(Statement stmt, Connection conn) {
+        close(null, stmt, conn);
+    }
+
+    //开启事务
+    public static void setAutoCommit() {
+        //获取连接对象
+        Connection conn = getConnection();
+        //开启手动提交
+        try {
+            conn.setAutoCommit(false);
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    //事务回滚
+    public static void rollbackAndClose() throws SQLException {
+        //获取连接对象
+        Connection conn = getConnection();
+        //回滚
+        conn.rollback();
+        //释放连接对象,从当前线程中解绑
+        conn.close();
+    }
+
+    //提交事务
+    public static void commitAndClose() throws SQLException {
+        //获取连接对象
+        Connection conn = getConnection();
+        //提交事务
+        conn.commit();
+        //释放资源,从当前线程中解绑
+        conn.close();
+
+    }
+
+
+    public static void main(String[] args) {
+//        System.out.println(DruidJdbcUtils.getDataSource());
+        System.out.println(DruidJdbcUtils.getConnection());
+    }
+
+}

+ 58 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/util/JdbcUtil.java

@@ -0,0 +1,58 @@
+package com.qmth.teachcloud.data.util;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.pool.DruidDataSourceFactory;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Map;
+
+public class JdbcUtil {
+    //声明
+
+
+    //构造方法私有化
+    private JdbcUtil() {
+    }
+
+    public static JdbcTemplate getJdbcTemplate(DataSource dataSource) {
+        try {
+            JdbcTemplate jdbcTemplate = null;
+            //2)判断是否获取到连接对象
+            if (jdbcTemplate == null) {
+                //创建jdbc模板对象
+                jdbcTemplate = new JdbcTemplate();
+                //设置数据源对象
+                jdbcTemplate.setDataSource(dataSource);
+            }
+            return jdbcTemplate;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    //获取DataSource数据源信息(连接池的所有参数)
+    public static Connection getConnection(DruidDataSource druidDataSource) {
+        //1)从当前线程中获取存储的内容
+        Connection conn = null;
+
+        try {
+            //2)判断是否获取到连接对象
+            if (conn == null) {
+                //3)从数据源(连接池中)获取Connection
+                conn = druidDataSource.getConnection();
+            }
+
+        } catch (SQLException e) {
+//            e.printStackTrace();
+            throw new RuntimeException(e.getMessage());
+        }
+        return conn;
+    }
+}

+ 50 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/CourseDataVo.java

@@ -0,0 +1,50 @@
+package com.qmth.teachcloud.data.vo;
+
+import com.qmth.teachcloud.common.entity.BasicCourse;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 课程数据
+ */
+public class CourseDataVo {
+
+    @ApiModelProperty(value = "课程代码")
+    private String code;
+    @ApiModelProperty(value = "课程名称")
+    private String name;
+    @ApiModelProperty(value = "机构名称(二级学院)")
+    private String collegeName;
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCollegeName() {
+        return collegeName;
+    }
+
+    public void setCollegeName(String collegeName) {
+        this.collegeName = collegeName;
+    }
+
+    public BasicCourse transferToBasicCourse(Long schoolId, CourseDataVo courseDataVo) {
+        BasicCourse basicCourse = new BasicCourse();
+        basicCourse.setSchoolId(schoolId);
+        basicCourse.setCode(courseDataVo.getCode());
+        basicCourse.setName(courseDataVo.getName());
+        basicCourse.setCollegeName(courseDataVo.collegeName);
+        return basicCourse;
+    }
+}

+ 180 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/ExamStudentDataVo.java

@@ -0,0 +1,180 @@
+package com.qmth.teachcloud.data.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 考生考务数据
+ */
+public class ExamStudentDataVo {
+
+    @ApiModelProperty(value = "课程代码")
+    private String courseCode;
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+    @ApiModelProperty(value = "课程开课学院(二级学院)")
+    private String courseCollegeName;
+    @ApiModelProperty(value = "试卷编号")
+    private String paperNumber;
+    @ApiModelProperty(value = "学号")
+    private String studentCode;
+    @ApiModelProperty(value = "姓名")
+    private String studentName;
+    @ApiModelProperty(value = "座位号")
+    private String siteNumber;
+    @ApiModelProperty(value = "学生学院")
+    private String studentCollegeName;
+    @ApiModelProperty(value = "学生专业")
+    private String majorName;
+    @ApiModelProperty(value = "教学班")
+    private String teachClassName;
+    @ApiModelProperty(value = "行政班")
+    private String className;
+    @ApiModelProperty(value = "考试开始时间")
+    private Long examStartTime;
+    @ApiModelProperty(value = "考试结束时间")
+    private Long examEndTime;
+    @ApiModelProperty(value = "考点")
+    private String examPlace;
+    @ApiModelProperty(value = "考场")
+    private String examRoom;
+    @ApiModelProperty(value = "命题老师工号")
+    private String teacherCode;
+    @ApiModelProperty(value = "命题老师名称")
+    private String teacherName;
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseCollegeName() {
+        return courseCollegeName;
+    }
+
+    public void setCourseCollegeName(String courseCollegeName) {
+        this.courseCollegeName = courseCollegeName;
+    }
+
+    public String getPaperNumber() {
+        return paperNumber;
+    }
+
+    public void setPaperNumber(String paperNumber) {
+        this.paperNumber = paperNumber;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getSiteNumber() {
+        return siteNumber;
+    }
+
+    public void setSiteNumber(String siteNumber) {
+        this.siteNumber = siteNumber;
+    }
+
+    public String getStudentCollegeName() {
+        return studentCollegeName;
+    }
+
+    public void setStudentCollegeName(String studentCollegeName) {
+        this.studentCollegeName = studentCollegeName;
+    }
+
+    public String getMajorName() {
+        return majorName;
+    }
+
+    public void setMajorName(String majorName) {
+        this.majorName = majorName;
+    }
+
+    public String getTeachClassName() {
+        return teachClassName;
+    }
+
+    public void setTeachClassName(String teachClassName) {
+        this.teachClassName = teachClassName;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public void setClassName(String className) {
+        this.className = className;
+    }
+
+    public Long getExamStartTime() {
+        return examStartTime;
+    }
+
+    public void setExamStartTime(Long examStartTime) {
+        this.examStartTime = examStartTime;
+    }
+
+    public Long getExamEndTime() {
+        return examEndTime;
+    }
+
+    public void setExamEndTime(Long examEndTime) {
+        this.examEndTime = examEndTime;
+    }
+
+    public String getExamPlace() {
+        return examPlace;
+    }
+
+    public void setExamPlace(String examPlace) {
+        this.examPlace = examPlace;
+    }
+
+    public String getExamRoom() {
+        return examRoom;
+    }
+
+    public void setExamRoom(String examRoom) {
+        this.examRoom = examRoom;
+    }
+
+    public String getTeacherCode() {
+        return teacherCode;
+    }
+
+    public void setTeacherCode(String teacherCode) {
+        this.teacherCode = teacherCode;
+    }
+
+    public String getTeacherName() {
+        return teacherName;
+    }
+
+    public void setTeacherName(String teacherName) {
+        this.teacherName = teacherName;
+    }
+}

+ 100 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/ExamTaskDataVo.java

@@ -0,0 +1,100 @@
+package com.qmth.teachcloud.data.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 命题任务数据
+ */
+public class ExamTaskDataVo {
+
+    @ApiModelProperty(value = "课程代码")
+    private String courseCode;
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+    @ApiModelProperty(value = "课程开课学院(二级学院)")
+    private String courseCollegeName;
+    @ApiModelProperty(value = "试卷编号")
+    private String paperNumber;
+    @ApiModelProperty(value = "命题开始时间")
+    private Long startTime;
+    @ApiModelProperty(value = "命题结束时间")
+    private Long endTime;
+    @ApiModelProperty(value = "命题老师工号")
+    private String teacherCode;
+    @ApiModelProperty(value = "命题老师名称")
+    private String teacherName;
+    @ApiModelProperty(value = "是否启用")
+    private Boolean enable;
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseCollegeName() {
+        return courseCollegeName;
+    }
+
+    public void setCourseCollegeName(String courseCollegeName) {
+        this.courseCollegeName = courseCollegeName;
+    }
+
+    public String getPaperNumber() {
+        return paperNumber;
+    }
+
+    public void setPaperNumber(String paperNumber) {
+        this.paperNumber = paperNumber;
+    }
+
+    public Long getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Long startTime) {
+        this.startTime = startTime;
+    }
+
+    public Long getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Long endTime) {
+        this.endTime = endTime;
+    }
+
+    public String getTeacherCode() {
+        return teacherCode;
+    }
+
+    public void setTeacherCode(String teacherCode) {
+        this.teacherCode = teacherCode;
+    }
+
+    public String getTeacherName() {
+        return teacherName;
+    }
+
+    public void setTeacherName(String teacherName) {
+        this.teacherName = teacherName;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+}

+ 28 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/OrgDataVo.java

@@ -0,0 +1,28 @@
+package com.qmth.teachcloud.data.vo;
+
+import com.qmth.teachcloud.common.entity.SysOrg;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 机构数据
+ */
+public class OrgDataVo {
+
+    @ApiModelProperty(value = "机构名称(二级学院)")
+    private String collegeName;
+
+    public String getCollegeName() {
+        return collegeName;
+    }
+
+    public void setCollegeName(String collegeName) {
+        this.collegeName = collegeName;
+    }
+
+    public SysOrg transferToSysOrg(Long schoolId, OrgDataVo orgDataVo) {
+        SysOrg sysOrg = new SysOrg();
+        sysOrg.setSchoolId(schoolId);
+        sysOrg.setName(orgDataVo.getCollegeName());
+        return sysOrg;
+    }
+}

+ 73 - 0
teachcloud-data/src/main/java/com/qmth/teachcloud/data/vo/UserDataVo.java

@@ -0,0 +1,73 @@
+package com.qmth.teachcloud.data.vo;
+
+import com.qmth.teachcloud.common.entity.SysUser;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 用户数据
+ */
+public class UserDataVo {
+
+    @ApiModelProperty(value = "教师工号")
+    private String code;
+    @ApiModelProperty(value = "教师姓名")
+    private String name;
+    @ApiModelProperty(value = "机构名称(二级学院)")
+    private String collegeName;
+    @ApiModelProperty(value = "手机号")
+    private String phoneNumber;
+    @ApiModelProperty(value = "是否启用")
+    private Boolean enable;
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCollegeName() {
+        return collegeName;
+    }
+
+    public void setCollegeName(String collegeName) {
+        this.collegeName = collegeName;
+    }
+
+    public String getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public void setPhoneNumber(String phoneNumber) {
+        this.phoneNumber = phoneNumber;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+
+    public SysUser transferToUser(Long schoolId, UserDataVo userDataVo) {
+        SysUser sysUser = new SysUser();
+        sysUser.setSchoolId(schoolId);
+        sysUser.setLoginName(userDataVo.getCode());
+        sysUser.setCode(userDataVo.getCode());
+        sysUser.setRealName(userDataVo.getName());
+        sysUser.setMobileNumber(userDataVo.getPhoneNumber());
+        sysUser.setOrgName(userDataVo.getCollegeName());
+        sysUser.setEnable(userDataVo.getEnable());
+        return sysUser;
+    }
+}

+ 15 - 0
teachcloud-data/src/main/resources/mapper/TSyncDataLogMapper.xml

@@ -0,0 +1,15 @@
+<?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.qmth.teachcloud.data.mapper.TSyncDataLogMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.qmth.teachcloud.data.entity.TSyncDataLog">
+        <id column="school_id" property="schoolId" />
+        <result column="exam_id" property="examId" />
+        <result column="data_type" property="dataType" />
+        <result column="start_time" property="startTime" />
+        <result column="end_time" property="endTime" />
+        <result column="count" property="count" />
+    </resultMap>
+
+</mapper>

+ 19 - 0
teachcloud-data/src/main/resources/mapper/TSyncDataMapper.xml

@@ -0,0 +1,19 @@
+<?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.qmth.teachcloud.data.mapper.TSyncDataMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.qmth.teachcloud.data.entity.TSyncData">
+        <id column="school_id" property="schoolId" />
+        <result column="semester_id" property="semesterId" />
+        <result column="exam_id" property="examId" />
+        <result column="data_range" property="dataRange" />
+        <result column="start_time" property="startTime" />
+        <result column="end_time" property="endTime" />
+        <result column="last_sync_time" property="lastSyncTime" />
+        <result column="detail" property="detail" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+</mapper>

+ 13 - 0
teachcloud-data/src/test/java/com/qmth/teachcloud/data/TeachcloudDataApplicationTests.java

@@ -0,0 +1,13 @@
+package com.qmth.teachcloud.data;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class TeachcloudDataApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}

+ 2 - 2
teachcloud-mark/pom.xml

@@ -4,7 +4,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.teachcloud.mark</groupId>
     <artifactId>teachcloud-mark</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <build>
         <plugins>
             <plugin>
@@ -22,7 +22,7 @@
     <parent>
         <groupId>com.qmth.distributed.print.service</groupId>
         <artifactId>distributed-print-service</artifactId>
-        <version>3.4.2</version>
+        <version>3.4.3</version>
     </parent>
 
     <dependencies>

+ 2 - 2
teachcloud-obe/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.teachcloud.obe</groupId>
     <artifactId>teachcloud-obe</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.distributed.print.service</groupId>
         <artifactId>distributed-print-service</artifactId>
-        <version>3.4.2</version>
+        <version>3.4.3</version>
     </parent>
 
 

+ 2 - 2
teachcloud-report-business/pom.xml

@@ -4,7 +4,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.teachcloud.report.business</groupId>
     <artifactId>teachcloud-report-business</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <build>
         <plugins>
             <plugin>
@@ -22,7 +22,7 @@
     <parent>
         <groupId>com.qmth.distributed.print.service</groupId>
         <artifactId>distributed-print-service</artifactId>
-        <version>3.4.2</version>
+        <version>3.4.3</version>
     </parent>
 
     <dependencies>

+ 2 - 2
teachcloud-report/pom.xml

@@ -4,13 +4,13 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>com.qmth.teachcloud.report</groupId>
 	<artifactId>teachcloud-report</artifactId>
-	<version>3.4.2</version>
+	<version>3.4.3</version>
 	<packaging>jar</packaging>
 
 	<parent>
 		<groupId>com.qmth.distributed.print.service</groupId>
 		<artifactId>distributed-print-service</artifactId>
-		<version>3.4.2</version>
+		<version>3.4.3</version>
 	</parent>
 
 	<dependencies>

+ 6 - 2
teachcloud-task/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.teachcloud.task</groupId>
     <artifactId>teachcloud-task</artifactId>
-    <version>3.4.2</version>
+    <version>3.4.3</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.distributed.print.service</groupId>
         <artifactId>distributed-print-service</artifactId>
-        <version>3.4.2</version>
+        <version>3.4.3</version>
     </parent>
 
     <dependencies>
@@ -22,6 +22,10 @@
             <groupId>com.qmth.teachcloud.mark</groupId>
             <artifactId>teachcloud-mark</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.qmth.teachcloud.data</groupId>
+            <artifactId>teachcloud-data</artifactId>
+        </dependency>
 <!--        <dependency>-->
 <!--            <groupId>com.qmth.teachcloud.report.business</groupId>-->
 <!--            <artifactId>teachcloud-report-business</artifactId>-->

+ 1 - 0
teachcloud-task/src/main/java/com/qmth/teachcloud/task/enums/JobEnum.java

@@ -20,6 +20,7 @@ public enum JobEnum {
     UPDATE_MARKER_QUALITY("UPDATE_MARKER_QUALITY_GROUP", "更新评卷员质量监控指标"),
     BUILD_MARK_TASK("BUILD_MARK_TASK_GROUP", "创建评卷任务"),
     INIT_MARK_DATA("INIT_MARK_DATA_GROUP", "初始化阅卷数据"),
+    DATA_SYNC("DATA_SYNC_GROUP", "数据同步"),
     CLEAR_TIMEOUT_TASK("CLEAR_TIMEOUT_TASK_GROUP", "清空过期任务");
 
     private String groupName;

+ 30 - 0
teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/DataSyncJob.java

@@ -0,0 +1,30 @@
+package com.qmth.teachcloud.task.job;
+
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.task.job.service.JobService;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+import javax.annotation.Resource;
+
+/**
+ * 数据同步
+ */
+public class DataSyncJob extends QuartzJobBean {
+    private final static Logger log = LoggerFactory.getLogger(DataSyncJob.class);
+
+    @Resource
+    JobService jobService;
+
+    @Override
+    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
+        try {
+            jobService.sentDataSync();
+        } catch (Exception e) {
+            log.error(SystemConstant.LOG_ERROR, e);
+        }
+    }
+}

+ 2 - 0
teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/JobService.java

@@ -51,4 +51,6 @@ public interface JobService {
     void clearTimeoutTask();
 
     void createPdfTask();
+
+    void sentDataSync();
 }

+ 15 - 4
teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/impl/JobServiceImpl.java

@@ -11,6 +11,7 @@ import com.qmth.teachcloud.common.bean.dto.MqDto;
 import com.qmth.teachcloud.common.bean.vo.PaperInfoVo;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.entity.BasicCourse;
+import com.qmth.teachcloud.common.entity.BasicExam;
 import com.qmth.teachcloud.common.entity.TSAuth;
 import com.qmth.teachcloud.common.enums.AuthEnum;
 import com.qmth.teachcloud.common.enums.PushTypeEnum;
@@ -23,6 +24,8 @@ import com.qmth.teachcloud.common.threadPool.DynamicMyThreadPool;
 import com.qmth.teachcloud.common.util.DateDisposeUtils;
 import com.qmth.distributed.print.business.util.ExamTaskUtil;
 import com.qmth.teachcloud.common.util.RedisUtil;
+import com.qmth.teachcloud.data.entity.TSyncData;
+import com.qmth.teachcloud.data.service.TSyncDataService;
 import com.qmth.teachcloud.mark.entity.MarkGroup;
 import com.qmth.teachcloud.mark.entity.MarkPaper;
 import com.qmth.teachcloud.mark.entity.MarkUserGroup;
@@ -33,18 +36,20 @@ import com.qmth.teachcloud.mark.service.MarkPaperService;
 import com.qmth.teachcloud.mark.service.MarkService;
 import com.qmth.teachcloud.mark.service.MarkUserGroupService;
 import com.qmth.teachcloud.mark.utils.TaskLockUtil;
+import com.qmth.teachcloud.task.enums.JobEnum;
+import com.qmth.teachcloud.task.job.DataSyncJob;
+import com.qmth.teachcloud.task.job.SendSmsExpireJob;
 import com.qmth.teachcloud.task.job.service.JobService;
 import com.qmth.teachcloud.task.service.PrintFinishService;
+import com.qmth.teachcloud.task.service.QuartzService;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -101,6 +106,8 @@ public class JobServiceImpl implements JobService {
     private ExamTaskService examTaskService;
     @Resource
     private BasicCardRuleService basicCardRuleService;
+    @Resource
+    private TSyncDataService tSyncDataService;
 
     @Override
     public void sendSmsExpireTask() {
@@ -255,6 +262,10 @@ public class JobServiceImpl implements JobService {
         }
     }
 
+    @Override
+    public void sentDataSync() {
+    }
+
     /**
      * 获取线程使用率
      *