wangliang 3 tahun lalu
induk
melakukan
55d3e1195d
87 mengubah file dengan 4077 tambahan dan 55 penghapusan
  1. 1 1
      pom.xml
  2. 20 1
      src/main/java/com/qmth/eds/api/BasicSchoolController.java
  3. 71 0
      src/main/java/com/qmth/eds/api/ExamDownloadRecordController.java
  4. 65 0
      src/main/java/com/qmth/eds/api/ExamScheduleTaskController.java
  5. 36 0
      src/main/java/com/qmth/eds/api/ExamSemesterController.java
  6. 72 0
      src/main/java/com/qmth/eds/api/ExamSyncTotalController.java
  7. 33 0
      src/main/java/com/qmth/eds/api/ExamTypeController.java
  8. 102 0
      src/main/java/com/qmth/eds/api/SysController.java
  9. 30 1
      src/main/java/com/qmth/eds/api/SysRoleController.java
  10. 73 3
      src/main/java/com/qmth/eds/api/SysUserController.java
  11. 101 0
      src/main/java/com/qmth/eds/bean/dto/ExamDownloadRecordDto.java
  12. 63 0
      src/main/java/com/qmth/eds/bean/params/ClientLoginParam.java
  13. 50 0
      src/main/java/com/qmth/eds/bean/params/LoginParam.java
  14. 107 0
      src/main/java/com/qmth/eds/bean/result/EditResult.java
  15. 6 0
      src/main/java/com/qmth/eds/config/DictionaryConfig.java
  16. 3 1
      src/main/java/com/qmth/eds/contant/SystemConstant.java
  17. 49 0
      src/main/java/com/qmth/eds/domain/whuDomain.java
  18. 87 0
      src/main/java/com/qmth/eds/entity/ExamDownloadRecord.java
  19. 129 0
      src/main/java/com/qmth/eds/entity/ExamScheduleTask.java
  20. 41 0
      src/main/java/com/qmth/eds/entity/ExamSemester.java
  21. 267 0
      src/main/java/com/qmth/eds/entity/ExamSyncStudent.java
  22. 262 0
      src/main/java/com/qmth/eds/entity/ExamSyncStudentTemp.java
  23. 194 0
      src/main/java/com/qmth/eds/entity/ExamSyncTotal.java
  24. 27 0
      src/main/java/com/qmth/eds/entity/ExamType.java
  25. 22 0
      src/main/java/com/qmth/eds/entity/SysUser.java
  26. 136 0
      src/main/java/com/qmth/eds/entity/TBSyncTask.java
  27. 2 1
      src/main/java/com/qmth/eds/enums/JobEnum.java
  28. 5 33
      src/main/java/com/qmth/eds/enums/RoleTypeEnum.java
  29. 42 0
      src/main/java/com/qmth/eds/enums/TaskResultEnum.java
  30. 59 0
      src/main/java/com/qmth/eds/enums/TaskStatusEnum.java
  31. 37 0
      src/main/java/com/qmth/eds/enums/UseSceneEnum.java
  32. 5 0
      src/main/java/com/qmth/eds/job/service/JobService.java
  33. 24 0
      src/main/java/com/qmth/eds/job/service/TimedSyncTaskJob.java
  34. 24 8
      src/main/java/com/qmth/eds/job/service/impl/JobServiceImpl.java
  35. 15 0
      src/main/java/com/qmth/eds/mapper/ExamDownloadRecordMapper.java
  36. 12 0
      src/main/java/com/qmth/eds/mapper/ExamScheduleTaskMapper.java
  37. 10 0
      src/main/java/com/qmth/eds/mapper/ExamSemesterMapper.java
  38. 10 0
      src/main/java/com/qmth/eds/mapper/ExamSyncStudentMapper.java
  39. 10 0
      src/main/java/com/qmth/eds/mapper/ExamSyncStudentTempMapper.java
  40. 10 0
      src/main/java/com/qmth/eds/mapper/ExamSyncTotalMapper.java
  41. 10 0
      src/main/java/com/qmth/eds/mapper/ExamTypeMapper.java
  42. 3 0
      src/main/java/com/qmth/eds/mapper/SysRoleMapper.java
  43. 1 0
      src/main/java/com/qmth/eds/mapper/SysUserMapper.java
  44. 13 0
      src/main/java/com/qmth/eds/mapper/TBSyncTaskMapper.java
  45. 29 0
      src/main/java/com/qmth/eds/service/AsyncTaskService.java
  46. 18 0
      src/main/java/com/qmth/eds/service/DataSyncService.java
  47. 16 0
      src/main/java/com/qmth/eds/service/ExamDownloadRecordService.java
  48. 19 0
      src/main/java/com/qmth/eds/service/ExamScheduleTaskService.java
  49. 14 0
      src/main/java/com/qmth/eds/service/ExamSemesterService.java
  50. 11 0
      src/main/java/com/qmth/eds/service/ExamSyncStudentService.java
  51. 12 0
      src/main/java/com/qmth/eds/service/ExamSyncStudentTempService.java
  52. 19 0
      src/main/java/com/qmth/eds/service/ExamSyncTotalService.java
  53. 11 0
      src/main/java/com/qmth/eds/service/ExamTypeService.java
  54. 3 0
      src/main/java/com/qmth/eds/service/SysRoleService.java
  55. 1 0
      src/main/java/com/qmth/eds/service/SysUserRoleService.java
  56. 8 0
      src/main/java/com/qmth/eds/service/SysUserService.java
  57. 19 0
      src/main/java/com/qmth/eds/service/TBSyncTaskService.java
  58. 25 0
      src/main/java/com/qmth/eds/service/TeachcloudCommonService.java
  59. 318 0
      src/main/java/com/qmth/eds/service/impl/DataSyncServiceImpl.java
  60. 53 0
      src/main/java/com/qmth/eds/service/impl/ExamDownloadRecordServiceImpl.java
  61. 80 0
      src/main/java/com/qmth/eds/service/impl/ExamScheduleTaskServiceImpl.java
  62. 20 0
      src/main/java/com/qmth/eds/service/impl/ExamSemesterServiceImpl.java
  63. 11 0
      src/main/java/com/qmth/eds/service/impl/ExamSyncStudentServiceImpl.java
  64. 18 0
      src/main/java/com/qmth/eds/service/impl/ExamSyncStudentServiceTempImpl.java
  65. 84 0
      src/main/java/com/qmth/eds/service/impl/ExamSyncTotalServiceImpl.java
  66. 11 0
      src/main/java/com/qmth/eds/service/impl/ExamTypeServiceImpl.java
  67. 6 0
      src/main/java/com/qmth/eds/service/impl/SysRoleServiceImpl.java
  68. 7 0
      src/main/java/com/qmth/eds/service/impl/SysUserRoleServiceImpl.java
  69. 82 1
      src/main/java/com/qmth/eds/service/impl/SysUserServiceImpl.java
  70. 56 0
      src/main/java/com/qmth/eds/service/impl/TBSyncTaskServiceImpl.java
  71. 120 0
      src/main/java/com/qmth/eds/service/impl/TeachcloudCommonServiceImpl.java
  72. 9 3
      src/main/java/com/qmth/eds/start/StartRunning.java
  73. 74 0
      src/main/java/com/qmth/eds/util/FileUtil.java
  74. 287 0
      src/main/java/com/qmth/eds/util/HttpKit.java
  75. 8 0
      src/main/java/com/qmth/eds/util/ServletUtil.java
  76. 118 0
      src/main/java/com/qmth/eds/util/WuhanUniversityUtils.java
  77. 8 2
      src/main/resources/application-dev.properties
  78. 36 0
      src/main/resources/mapper/ExamDownloadRecordMapper.xml
  79. 21 0
      src/main/resources/mapper/ExamScheduleTaskMapper.xml
  80. 4 0
      src/main/resources/mapper/ExamSemesterMapper.xml
  81. 4 0
      src/main/resources/mapper/ExamSyncStudentMapper.xml
  82. 4 0
      src/main/resources/mapper/ExamSyncStudentTempMapper.xml
  83. 4 0
      src/main/resources/mapper/ExamSyncTotalMapper.xml
  84. 4 0
      src/main/resources/mapper/ExamTypeMapper.xml
  85. 22 0
      src/main/resources/mapper/SysRoleMapper.xml
  86. 20 0
      src/main/resources/mapper/SysUserMapper.xml
  87. 44 0
      src/test/java/com/qmth/eds/WhuTest.java

+ 1 - 1
pom.xml

@@ -8,7 +8,7 @@
     <packaging>jar</packaging>
 
     <properties>
-        <java.version>11</java.version>
+        <java.version>1.8</java.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>

+ 20 - 1
src/main/java/com/qmth/eds/api/BasicSchoolController.java

@@ -1,8 +1,18 @@
 package com.qmth.eds.api;
 
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.service.BasicSchoolService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
+
 /**
  * <p>
  * 学校表 前端控制器
@@ -12,7 +22,16 @@ import org.springframework.web.bind.annotation.RestController;
  * @since 2022-05-14
  */
 @RestController
-@RequestMapping("/basic-school")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/basic_school")
+@Aac(auth = BOOL.FALSE)
 public class BasicSchoolController {
 
+    @Resource
+    private BasicSchoolService basicSchoolService;
+
+    @ApiOperation(value = "查询")
+    @PostMapping("/list")
+    public Result list() {
+        return ResultUtil.ok(basicSchoolService.list());
+    }
 }

+ 71 - 0
src/main/java/com/qmth/eds/api/ExamDownloadRecordController.java

@@ -0,0 +1,71 @@
+package com.qmth.eds.api;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.enums.UseSceneEnum;
+import com.qmth.eds.service.ExamDownloadRecordService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/exam_download_record")
+@Aac(auth = BOOL.FALSE)
+public class ExamDownloadRecordController {
+
+    @Resource
+    private ExamDownloadRecordService examDownloadRecordService;
+
+    /**
+     * 查询
+     *
+     * @param operateDate 下载时间
+     * @param used        是否使用
+     * @param useScene    下载用途
+     * @param pageNumber  分页参数
+     * @param pageSize    分页参数
+     */
+    @ApiOperation(value = "查询")
+    @PostMapping("/page")
+    public Result page(@RequestParam(value = "operateDate", required = false) Long operateDate,
+                       @RequestParam(value = "used", required = false) Boolean used,
+                       @RequestParam(value = "useScene", required = false) String useScene,
+                       @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                       @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        return ResultUtil.ok(examDownloadRecordService.list(operateDate, used, useScene, pageNumber, pageSize));
+    }
+
+    /**
+     * 用途列表
+     */
+    @ApiOperation(value = "用途")
+    @PostMapping("/list_use_scene")
+    public Result listUseScene() {
+        return ResultUtil.ok(UseSceneEnum.listTypes());
+    }
+
+    /**
+     * 标记用途
+     * @param id id
+     * @param used 是否使用
+     * @param useScene 下载用途
+     */
+    @ApiOperation(value = "标记用途")
+    @PostMapping("/used")
+    public Result used(@RequestParam(value = "id") String id,
+                       @RequestParam(value = "used") Boolean used,
+                       @RequestParam(value = "useScene") String useScene) {
+        return ResultUtil.ok(examDownloadRecordService.used(id, used, useScene));
+    }
+
+}

+ 65 - 0
src/main/java/com/qmth/eds/api/ExamScheduleTaskController.java

@@ -0,0 +1,65 @@
+package com.qmth.eds.api;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.entity.ExamScheduleTask;
+import com.qmth.eds.service.ExamScheduleTaskService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/exam_schedule_task")
+@Aac(auth = BOOL.FALSE)
+public class ExamScheduleTaskController {
+
+    @Resource
+    private ExamScheduleTaskService examScheduleTaskService;
+
+    /**
+     * 查询
+     *
+     * @param schoolId   学校ID
+     * @param semesterId 学期ID
+     * @param examTypeId 考试类型ID
+     * @param startTime  定时任务开始时间
+     * @param endTime    定时任务结束时间
+     * @param pageNumber 分页参数
+     * @param pageSize   分页参数
+     */
+    @ApiOperation(value = "查询")
+    @PostMapping("/page")
+    public Result page(@RequestParam(value = "schoolId", required = false) Long schoolId,
+                       @RequestParam(value = "semesterId", required = false) Long semesterId,
+                       @RequestParam(value = "examTypeId", required = false) Long examTypeId,
+                       @RequestParam(value = "startTime", required = false) Long startTime,
+                       @RequestParam(value = "endTime", required = false) Long endTime,
+                       @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                       @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        return ResultUtil.ok(examScheduleTaskService.list(schoolId, semesterId, examTypeId, startTime, endTime, pageNumber, pageSize));
+    }
+
+    /**
+     * 新增/修改
+     *
+     * @param examScheduleTask 定时任务信息对象
+     */
+    @ApiOperation(value = "新增/修改")
+    @PostMapping("/save")
+    public Result save(@ApiParam(value = "定时任务信息", required = true) @Valid @RequestBody ExamScheduleTask examScheduleTask, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
+        }
+        return ResultUtil.ok(examScheduleTaskService.saveTask(examScheduleTask));
+    }
+}

+ 36 - 0
src/main/java/com/qmth/eds/api/ExamSemesterController.java

@@ -0,0 +1,36 @@
+package com.qmth.eds.api;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.service.ExamSemesterService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/exam_semester")
+@Aac(auth = BOOL.FALSE)
+public class ExamSemesterController {
+
+    @Resource
+    private ExamSemesterService examSemesterService;
+
+    /**
+     * 查询
+     *
+     * @param schoolId 学校ID
+     */
+    @ApiOperation(value = "查询")
+    @PostMapping("/list")
+    public Result list(@RequestParam(value = "schoolId", required = false) Long schoolId) {
+        return ResultUtil.ok(examSemesterService.listBySchoolId(schoolId));
+    }
+
+}

+ 72 - 0
src/main/java/com/qmth/eds/api/ExamSyncTotalController.java

@@ -0,0 +1,72 @@
+package com.qmth.eds.api;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.job.service.JobService;
+import com.qmth.eds.service.ExamSyncTotalService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/exam_sync_total")
+@Aac(auth = BOOL.FALSE)
+public class ExamSyncTotalController {
+
+    @Resource
+    private ExamSyncTotalService examSyncTotalService;
+
+    @Resource
+    private JobService jobService;
+
+    /**
+     * 查询
+     *
+     * @param syncDate   同步日期
+     * @param pageNumber 分页参数
+     * @param pageSize   分页参数
+     */
+    @ApiOperation(value = "查询")
+    @PostMapping("/page")
+    public Result page(@RequestParam(value = "syncDate", required = false) Long syncDate,
+                       @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                       @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        return ResultUtil.ok(examSyncTotalService.list(syncDate, pageNumber, pageSize));
+    }
+
+    /**
+     * 下载
+     *
+     * @param id id
+     * @param writeLog 是否写入日志(考务数据列表下载需要,下载列表下载不需要)
+     */
+    @ApiOperation(value = "下载")
+    @PostMapping("/download")
+    public void page(@RequestParam(value = "id", required = false) Long id,
+                     @RequestParam(value = "writeLog", defaultValue = "true") Boolean writeLog,
+                     HttpServletResponse response) {
+        examSyncTotalService.download(id, writeLog, response);
+    }
+
+    /**
+     * 人工同步
+     */
+    @ApiOperation(value = "人工同步")
+    @PostMapping("/manual_sync")
+    public Result manualSync() {
+        jobService.getExamData();
+        return ResultUtil.success(true);
+    }
+
+}

+ 33 - 0
src/main/java/com/qmth/eds/api/ExamTypeController.java

@@ -0,0 +1,33 @@
+package com.qmth.eds.api;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.service.ExamTypeService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/exam_type")
+@Aac(auth = BOOL.FALSE)
+public class ExamTypeController {
+
+    @Resource
+    private ExamTypeService examTypeService;
+
+    /**
+     * 查询
+     */
+    @ApiOperation(value = "查询")
+    @PostMapping("/list")
+    public Result list() {
+        return ResultUtil.ok(examTypeService.list());
+    }
+
+}

+ 102 - 0
src/main/java/com/qmth/eds/api/SysController.java

@@ -0,0 +1,102 @@
+package com.qmth.eds.api;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.bean.params.LoginParam;
+import com.qmth.eds.bean.result.EditResult;
+import com.qmth.eds.bean.result.LoginResult;
+import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.enums.AppSourceEnum;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import com.qmth.eds.service.SysUserService;
+import com.qmth.eds.service.TeachcloudCommonService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import com.qmth.eds.util.ServletUtil;
+import io.swagger.annotations.*;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+/**
+ * 系统公共Controller
+ */
+@Api(tags = "系统Controller")
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/common")
+public class SysController {
+    @Resource
+    SysUserService sysUserService;
+
+    @Resource
+    TeachcloudCommonService teachcloudCommonService;
+
+    /**
+     * 登录
+     *
+     * @param login         登录参数
+     * @param bindingResult 错误
+     */
+    @ApiOperation(value = "登录")
+    @PostMapping("/login")
+    @ApiResponses({@ApiResponse(code = 200, message = "用户信息", response = LoginResult.class)})
+    @Aac(auth = BOOL.FALSE)
+    public Result login(@ApiParam(value = "用户信息", required = true) @Valid @RequestBody LoginParam login, BindingResult bindingResult) throws NoSuchAlgorithmException {
+        if (bindingResult.hasErrors()) {
+            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
+        }
+
+        // 账号密码登录
+        String loginName = login.getLoginName();
+        String password = login.getPassword();
+        // 非空校验
+        if (StringUtils.isBlank(loginName)) {
+            throw ExceptionResultEnum.ERROR.exception("用户名不能为空");
+        }
+        if (StringUtils.isBlank(password)) {
+            throw ExceptionResultEnum.ERROR.exception("密码不能为空");
+        }
+        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
+        wrapper.lambda().and(w -> w.eq(SysUser::getLoginName, loginName).or().eq(SysUser::getCode, loginName));
+        List<SysUser> userList = sysUserService.list(wrapper);
+        //用户不存在
+        if (userList == null || userList.isEmpty()) {
+            throw ExceptionResultEnum.ERROR.exception("用户不存在");
+        }
+        if (userList.size() > 1) {
+            throw ExceptionResultEnum.ERROR.exception("查出多个用户");
+        }
+        SysUser sysUser = userList.get(0);
+        if (!sysUser.getEnable()) {
+            throw ExceptionResultEnum.ERROR.exception("用户被禁用");
+        }
+        if (!password.equals(userList.get(0).getPassword())) {
+            throw ExceptionResultEnum.ERROR.exception("密码错误");
+        }
+        LoginResult loginResult = teachcloudCommonService.login(login.getPassword(), sysUser, AppSourceEnum.SYSTEM);
+        return ResultUtil.ok(loginResult);
+    }
+
+    /**
+     * 登出
+     */
+    @ApiOperation(value = "登出")
+    @PostMapping("/logout")
+    @ApiResponses({@ApiResponse(code = 200, message = "返回信息", response = EditResult.class)})
+    public Result logout() throws NoSuchAlgorithmException {
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        teachcloudCommonService.removeUserInfo(sysUser.getId(), false);
+        return ResultUtil.ok();
+    }
+
+}

+ 30 - 1
src/main/java/com/qmth/eds/api/SysRoleController.java

@@ -1,8 +1,17 @@
 package com.qmth.eds.api;
 
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.enums.RoleTypeEnum;
+import com.qmth.eds.service.SysRoleService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
+
 /**
  * <p>
  * 角色表 前端控制器
@@ -12,7 +21,27 @@ import org.springframework.web.bind.annotation.RestController;
  * @since 2022-05-14
  */
 @RestController
-@RequestMapping("/sys-role")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/sys_role")
 public class SysRoleController {
 
+    @Resource
+    SysRoleService sysRoleService;
+
+    /**
+     * 查询
+     */
+    @ApiOperation(value = "查询")
+    @PostMapping("/list")
+    public Result list() {
+        return ResultUtil.ok(sysRoleService.list());
+    }
+
+    /**
+     * 用途列表
+     */
+    @ApiOperation(value = "角色")
+    @PostMapping("/list_role")
+    public Result listRole() {
+        return ResultUtil.ok(RoleTypeEnum.listTypes());
+    }
 }

+ 73 - 3
src/main/java/com/qmth/eds/api/SysUserController.java

@@ -1,7 +1,20 @@
 package com.qmth.eds.api;
 
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.service.SysUserService;
+import com.qmth.eds.util.Result;
+import com.qmth.eds.util.ResultUtil;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
 
 /**
  * <p>
@@ -12,7 +25,64 @@ import org.springframework.web.bind.annotation.RestController;
  * @since 2022-05-14
  */
 @RestController
-@RequestMapping("/sys-user")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/sys_user")
 public class SysUserController {
 
+    @Resource
+    private SysUserService sysUserService;
+
+    /**
+     * 查询
+     *
+     * @param loginName  用户名
+     * @param realName   姓名
+     * @param enable     状态
+     * @param pageNumber 分页参数
+     * @param pageSize   分页参数
+     */
+    @ApiOperation(value = "查询")
+    @PostMapping("/page")
+    public Result page(@RequestParam(value = "loginName", required = false) String loginName,
+                       @RequestParam(value = "realName", required = false) String realName,
+                       @RequestParam(value = "enable", required = false) Boolean enable,
+                       @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                       @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        return ResultUtil.ok(sysUserService.list(loginName, enable, realName, pageNumber, pageSize));
+    }
+
+    /**
+     * 新增/修改
+     *
+     * @param sysUser 用户对象
+     */
+    @ApiOperation(value = "新增/修改")
+    @PostMapping("/save")
+    public Result save(@ApiParam(value = "用户信息", required = true) @Valid @RequestBody SysUser sysUser, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
+        }
+        return ResultUtil.ok(sysUserService.saveUser(sysUser));
+    }
+
+    /**
+     * 启用/禁用
+     *
+     * @param sysUser 用户对象
+     */
+    @ApiOperation(value = "启用/禁用")
+    @PostMapping("/enable")
+    public Result enable(@RequestBody SysUser sysUser) {
+        return ResultUtil.ok(sysUserService.enable(sysUser));
+    }
+
+    /**
+     * 重置密码
+     *
+     * @param sysUser 用户对象
+     */
+    @ApiOperation(value = "重置密码")
+    @PostMapping("/reset_password")
+    public Result resetPassword(@RequestBody SysUser sysUser) {
+        return ResultUtil.ok(sysUserService.resetPassword(sysUser.getId()));
+    }
 }

+ 101 - 0
src/main/java/com/qmth/eds/bean/dto/ExamDownloadRecordDto.java

@@ -0,0 +1,101 @@
+package com.qmth.eds.bean.dto;
+
+import com.qmth.eds.entity.ExamDownloadRecord;
+
+import java.io.Serializable;
+
+public class ExamDownloadRecordDto extends ExamDownloadRecord implements Serializable {
+
+    private String fileName;
+
+    private String filePath;
+
+    private String schoolName;
+
+    private String semesterName;
+
+    private String examTypeName;
+
+    private Integer colleges;
+
+    private Integer subjects;
+
+    private Integer students;
+
+    /**
+     * 用途描述
+     */
+    private String useSceneStr;
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    public String getSchoolName() {
+        return schoolName;
+    }
+
+    public void setSchoolName(String schoolName) {
+        this.schoolName = schoolName;
+    }
+
+    public String getSemesterName() {
+        return semesterName;
+    }
+
+    public void setSemesterName(String semesterName) {
+        this.semesterName = semesterName;
+    }
+
+    public String getExamTypeName() {
+        return examTypeName;
+    }
+
+    public void setExamTypeName(String examTypeName) {
+        this.examTypeName = examTypeName;
+    }
+
+    public Integer getColleges() {
+        return colleges;
+    }
+
+    public void setColleges(Integer colleges) {
+        this.colleges = colleges;
+    }
+
+    public Integer getSubjects() {
+        return subjects;
+    }
+
+    public void setSubjects(Integer subjects) {
+        this.subjects = subjects;
+    }
+
+    public Integer getStudents() {
+        return students;
+    }
+
+    public void setStudents(Integer students) {
+        this.students = students;
+    }
+
+    public String getUseSceneStr() {
+        return useSceneStr;
+    }
+
+    public void setUseSceneStr(String useSceneStr) {
+        this.useSceneStr = useSceneStr;
+    }
+}

+ 63 - 0
src/main/java/com/qmth/eds/bean/params/ClientLoginParam.java

@@ -0,0 +1,63 @@
+package com.qmth.eds.bean.params;
+
+import io.swagger.annotations.ApiModelProperty;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * @Description: 客户端登录
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2021/4/23
+ */
+public class ClientLoginParam {
+
+    @ApiModelProperty(value = "登录名")
+//    @NotBlank(message = "请输入登录名")
+    @Length(message = "登录名不能超过{max}个字符", max = 50)
+    private String loginName;
+
+    @ApiModelProperty(value = "密码")
+//    @NotBlank(message = "请输入密码")
+    @Length(message = "密码不能少于{min}位", min = 6)
+    @Length(message = "密码不能超过{max}位", max = 30)
+    private String password;
+
+    @ApiModelProperty(value = "学校code")
+    private String schoolCode;
+
+    @ApiModelProperty(value = "登录类型")
+    private String type;
+
+    public String getLoginName() {
+        return loginName;
+    }
+
+    public void setLoginName(String loginName) {
+        this.loginName = loginName;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getSchoolCode() {
+        return schoolCode;
+    }
+
+    public void setSchoolCode(String schoolCode) {
+        this.schoolCode = schoolCode;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+}

+ 50 - 0
src/main/java/com/qmth/eds/bean/params/LoginParam.java

@@ -0,0 +1,50 @@
+package com.qmth.eds.bean.params;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @Description: 用户登录
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2021/4/23
+ */
+public class LoginParam extends ClientLoginParam {
+
+    @ApiModelProperty(value = "验证码")
+    private String code;
+
+    @ApiModelProperty(value = "学校代码")
+    private String schoolCode;
+
+    @ApiModelProperty(value = "用户手机号(优先给用户填的手机号发短信)")
+    private String mobileNumber;
+
+    @Override
+    public String getSchoolCode() {
+        return schoolCode;
+    }
+
+    @Override
+    public void setSchoolCode(String schoolCode) {
+        this.schoolCode = schoolCode;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMobileNumber() {
+        return mobileNumber;
+    }
+
+    public void setMobileNumber(String mobileNumber) {
+        this.mobileNumber = mobileNumber;
+    }
+}

+ 107 - 0
src/main/java/com/qmth/eds/bean/result/EditResult.java

@@ -0,0 +1,107 @@
+package com.qmth.eds.bean.result;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.eds.util.ServletUtil;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * @Description: 新增/更新返回值
+ * @Author: CaoZixuan
+ * @Date: 2021-03-22
+ */
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class EditResult implements Serializable {
+
+    @JsonSerialize(using = ToStringSerializer.class)
+    @ApiModelProperty(value = "主键")
+    private Long id;
+
+    @ApiModelProperty(value = "更新时间")
+    Long updateTime;
+
+    @ApiModelProperty(value = "成功状态")
+    Boolean success;
+
+    @ApiModelProperty(value = "附件url")
+    String url;
+
+    @ApiModelProperty(value = "pdf总页数")
+    Integer pages;
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public Boolean getSuccess() {
+        return success;
+    }
+
+    public void setSuccess(Boolean success) {
+        this.success = success;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getPages() {
+        return pages;
+    }
+
+    public void setPages(Integer pages) {
+        this.pages = pages;
+    }
+
+    public Long getUpdateTime() {
+        if (Objects.isNull(updateTime)) {
+            return System.currentTimeMillis();
+        } else {
+            return updateTime;
+        }
+    }
+
+    public void setUpdateTime(Long updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public EditResult(Long id) {
+        this.id = id;
+        ServletUtil.setRequestId(Arrays.asList(id));
+    }
+
+    public EditResult(Boolean success) {
+        this.success = success;
+    }
+
+    public EditResult(Long id, String url) {
+        this.id = id;
+        this.url = url;
+    }
+
+    public EditResult(Long id, String url, Integer pages) {
+        this.id = id;
+        this.url = url;
+        this.pages = pages;
+    }
+
+    public EditResult(String url) {
+        this.url = url;
+    }
+
+    public EditResult() {
+    }
+}

+ 6 - 0
src/main/java/com/qmth/eds/config/DictionaryConfig.java

@@ -59,4 +59,10 @@ public class DictionaryConfig {
     public FssPrivateDomain fssPrivateDomain() {
         return new FssPrivateDomain();
     }
+
+    @Bean
+    @ConfigurationProperties(prefix = "whu.config", ignoreUnknownFields = false)
+    public whuDomain whuDomain() {
+        return new whuDomain();
+    }
 }

+ 3 - 1
src/main/java/com/qmth/eds/contant/SystemConstant.java

@@ -35,13 +35,14 @@ public class SystemConstant {
     public static final String ORG = "org";
     public static final String ERROR = "/error";
     public static final String METHOD = "post";
-    public static final String DEFAULT_PASSWORD = "MTIzNDU2";
+    public static final String DEFAULT_PASSWORD = "123456";
     public static final String UPDATE_TIME = "updateTime";
     public static final String PATH = "path";
     public static final String TYPE = "type";
     public static final String LOCAL = "local";
     public static final String OSS = "oss";
     public static final String LOGO = "logo";
+    public static final String ADMIN_CODE = "admin";
     public static final String UPLOAD_TYPE = "uploadType";
     public static final String HTTP = "http://";
     public static final String ID = "id";
@@ -54,6 +55,7 @@ public class SystemConstant {
     public static final String JPG_PREFIX = ".jpg";
     public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
     public static final int PAGE_NUMBER = 1;
+    public static final int PAGE_NUMBER_MIN = 1;
     public static final int PAGE_SIZE = 10000000;
     public static final int PAGE_SIZE_MIN = 10;
     public static final int PAGE_SIZE_MAX = 500;

+ 49 - 0
src/main/java/com/qmth/eds/domain/whuDomain.java

@@ -0,0 +1,49 @@
+package com.qmth.eds.domain;
+
+import java.io.Serializable;
+
+/**
+ * 武汉大学配置
+ */
+public class whuDomain implements Serializable {
+
+    private String appKey;
+
+    private String appSecret;
+
+    private String tokenUrl;
+
+    private String kwUrl;
+
+    public String getAppKey() {
+        return appKey;
+    }
+
+    public void setAppKey(String appKey) {
+        this.appKey = appKey;
+    }
+
+    public String getAppSecret() {
+        return appSecret;
+    }
+
+    public void setAppSecret(String appSecret) {
+        this.appSecret = appSecret;
+    }
+
+    public String getTokenUrl() {
+        return tokenUrl;
+    }
+
+    public void setTokenUrl(String tokenUrl) {
+        this.tokenUrl = tokenUrl;
+    }
+
+    public String getKwUrl() {
+        return kwUrl;
+    }
+
+    public void setKwUrl(String kwUrl) {
+        this.kwUrl = kwUrl;
+    }
+}

+ 87 - 0
src/main/java/com/qmth/eds/entity/ExamDownloadRecord.java

@@ -0,0 +1,87 @@
+package com.qmth.eds.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.eds.base.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * 下载记录表
+ */
+@ApiModel(value = "ExamDownloadRecord对象", description = "下载记录表")
+public class ExamDownloadRecord extends BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "下载日期")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long operateTime;
+
+    @ApiModelProperty(value = "用户id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long userId;
+
+    @ApiModelProperty(value = "用户名称")
+    private String userName;
+
+    @ApiModelProperty(value = "考务数据同步记录表ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long syncTotalId;
+
+    @ApiModelProperty(value = "是否使用")
+    private Boolean used;
+
+    @ApiModelProperty(value = "用途")
+    private String useScene;
+
+    public Long getOperateTime() {
+        return operateTime;
+    }
+
+    public void setOperateTime(Long operateTime) {
+        this.operateTime = operateTime;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public Long getSyncTotalId() {
+        return syncTotalId;
+    }
+
+    public void setSyncTotalId(Long syncTotalId) {
+        this.syncTotalId = syncTotalId;
+    }
+
+    public Boolean getUsed() {
+        return used;
+    }
+
+    public void setUsed(Boolean used) {
+        this.used = used;
+    }
+
+    public String getUseScene() {
+        return useScene;
+    }
+
+    public void setUseScene(String useScene) {
+        this.useScene = useScene;
+    }
+}

+ 129 - 0
src/main/java/com/qmth/eds/entity/ExamScheduleTask.java

@@ -0,0 +1,129 @@
+package com.qmth.eds.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.eds.base.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 定时任务设置表
+ */
+@ApiModel(value = "ExamScheduleTask对象", description = "定时任务设置表")
+public class ExamScheduleTask extends BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "学校id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @NotNull(message = "请选择学校")
+    private Long schoolId;
+
+    @ApiModelProperty(value = "学校名称")
+    private String schoolName;
+
+    @ApiModelProperty(value = "学期id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @NotNull(message = "请选择学期")
+    private Long semesterId;
+
+    @ApiModelProperty(value = "学期名称")
+    private String semesterName;
+
+    @ApiModelProperty(value = "考试类型id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @NotNull(message = "请选择考试类型")
+    private Long examTypeId;
+
+    @ApiModelProperty(value = "考试类型名称")
+    private String examTypeName;
+
+    @ApiModelProperty(value = "定时任务开始时间")
+    @NotNull(message = "请选择定时任务开始时间")
+    private Long startTime;
+
+    @ApiModelProperty(value = "定时任务截止时间")
+    @NotNull(message = "请选择定时任务截止时间")
+    private Long endTime;
+
+    @TableField(exist = false)
+    @ApiModelProperty(value = "操作人")
+    private String userName;
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public String getSchoolName() {
+        return schoolName;
+    }
+
+    public void setSchoolName(String schoolName) {
+        this.schoolName = schoolName;
+    }
+
+    public Long getSemesterId() {
+        return semesterId;
+    }
+
+    public void setSemesterId(Long semesterId) {
+        this.semesterId = semesterId;
+    }
+
+    public String getSemesterName() {
+        return semesterName;
+    }
+
+    public void setSemesterName(String semesterName) {
+        this.semesterName = semesterName;
+    }
+
+    public Long getExamTypeId() {
+        return examTypeId;
+    }
+
+    public void setExamTypeId(Long examTypeId) {
+        this.examTypeId = examTypeId;
+    }
+
+    public String getExamTypeName() {
+        return examTypeName;
+    }
+
+    public void setExamTypeName(String examTypeName) {
+        this.examTypeName = examTypeName;
+    }
+
+    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 getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+}

+ 41 - 0
src/main/java/com/qmth/eds/entity/ExamSemester.java

@@ -0,0 +1,41 @@
+package com.qmth.eds.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.eds.base.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * 学期表
+ */
+@ApiModel(value = "ExamSemester对象", description = "学期表")
+public class ExamSemester extends BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "学校id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long schoolId;
+
+    @ApiModelProperty(value = "名称")
+    private String name;
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 267 - 0
src/main/java/com/qmth/eds/entity/ExamSyncStudent.java

@@ -0,0 +1,267 @@
+package com.qmth.eds.entity;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.eds.base.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * 学期表
+ */
+@ApiModel(value = "ExamSyncStudent对象", description = "考务数据同步考生信息表")
+public class ExamSyncStudent implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long id;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "学校id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long schoolId;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "汇总表ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long examSyncTotalId;
+
+    @ApiModelProperty(value = "学年")
+    private String xnm;
+
+    @ApiModelProperty(value = "学期")
+    private String xqm;
+
+    @ApiModelProperty(value = "教学班id")
+    private String jxbId;
+
+    @ApiModelProperty(value = "教学班名称")
+    private String jxbmc;
+
+    @ApiModelProperty(value = "课程号")
+    private String kch;
+
+    @ApiModelProperty(value = "课程名称")
+    private String kcmc;
+
+    @ApiModelProperty(value = "工号")
+    private String jgh;
+
+    @ApiModelProperty(value = "姓名")
+    private String xm;
+
+    @ApiModelProperty(value = "开课部门")
+    private String kkbm;
+
+    @ApiModelProperty(value = "学分")
+    private String xf;
+
+    @ApiModelProperty(value = "学号")
+    private String xh;
+
+    @ApiModelProperty(value = "学生姓名")
+    private String xsxm;
+
+    @ApiModelProperty(value = "学院")
+    private String jgmc;
+
+    @ApiModelProperty(value = "专业")
+    private String zymc;
+
+    @ApiModelProperty(value = "重修标记")
+    private String cxbj;
+
+    @ApiModelProperty(value = "年级")
+    private String njdmId;
+
+    @ApiModelProperty(value = "座位号")
+    private String zwh;
+
+    @ApiModelProperty(value = "场地名称")
+    private String cdmc;
+
+    @ApiModelProperty(value = "考试备注")
+    private String ksbz;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public Long getExamSyncTotalId() {
+        return examSyncTotalId;
+    }
+
+    public void setExamSyncTotalId(Long examSyncTotalId) {
+        this.examSyncTotalId = examSyncTotalId;
+    }
+
+    public String getXnm() {
+        return xnm;
+    }
+
+    public void setXnm(String xnm) {
+        this.xnm = xnm;
+    }
+
+    public String getXqm() {
+        return xqm;
+    }
+
+    public void setXqm(String xqm) {
+        this.xqm = xqm;
+    }
+
+    public String getJxbId() {
+        return jxbId;
+    }
+
+    public void setJxbId(String jxbId) {
+        this.jxbId = jxbId;
+    }
+
+    public String getJxbmc() {
+        return jxbmc;
+    }
+
+    public void setJxbmc(String jxbmc) {
+        this.jxbmc = jxbmc;
+    }
+
+    public String getKch() {
+        return kch;
+    }
+
+    public void setKch(String kch) {
+        this.kch = kch;
+    }
+
+    public String getKcmc() {
+        return kcmc;
+    }
+
+    public void setKcmc(String kcmc) {
+        this.kcmc = kcmc;
+    }
+
+    public String getJgh() {
+        return jgh;
+    }
+
+    public void setJgh(String jgh) {
+        this.jgh = jgh;
+    }
+
+    public String getXm() {
+        return xm;
+    }
+
+    public void setXm(String xm) {
+        this.xm = xm;
+    }
+
+    public String getKkbm() {
+        return kkbm;
+    }
+
+    public void setKkbm(String kkbm) {
+        this.kkbm = kkbm;
+    }
+
+    public String getXf() {
+        return xf;
+    }
+
+    public void setXf(String xf) {
+        this.xf = xf;
+    }
+
+    public String getXh() {
+        return xh;
+    }
+
+    public void setXh(String xh) {
+        this.xh = xh;
+    }
+
+    public String getXsxm() {
+        return xsxm;
+    }
+
+    public void setXsxm(String xsxm) {
+        this.xsxm = xsxm;
+    }
+
+    public String getJgmc() {
+        return jgmc;
+    }
+
+    public void setJgmc(String jgmc) {
+        this.jgmc = jgmc;
+    }
+
+    public String getZymc() {
+        return zymc;
+    }
+
+    public void setZymc(String zymc) {
+        this.zymc = zymc;
+    }
+
+    public String getCxbj() {
+        return cxbj;
+    }
+
+    public void setCxbj(String cxbj) {
+        this.cxbj = cxbj;
+    }
+
+    public String getNjdmId() {
+        return njdmId;
+    }
+
+    public void setNjdmId(String njdmId) {
+        this.njdmId = njdmId;
+    }
+
+    public String getZwh() {
+        return zwh;
+    }
+
+    public void setZwh(String zwh) {
+        this.zwh = zwh;
+    }
+
+    public String getCdmc() {
+        return cdmc;
+    }
+
+    public void setCdmc(String cdmc) {
+        this.cdmc = cdmc;
+    }
+
+    public String getKsbz() {
+        return ksbz;
+    }
+
+    public void setKsbz(String ksbz) {
+        this.ksbz = ksbz;
+    }
+}

+ 262 - 0
src/main/java/com/qmth/eds/entity/ExamSyncStudentTemp.java

@@ -0,0 +1,262 @@
+package com.qmth.eds.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * 学期表
+ */
+@ApiModel(value = "ExamSyncStudent对象", description = "考务数据同步考生信息表")
+public class ExamSyncStudentTemp implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long id;
+
+    @ApiModelProperty(value = "学校id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long schoolId;
+
+    @ApiModelProperty(value = "汇总表ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long examSyncTotalId;
+
+    @ApiModelProperty(value = "学年")
+    private String xnm;
+
+    @ApiModelProperty(value = "学期")
+    private String xqm;
+
+    @ApiModelProperty(value = "教学班id")
+    private String jxbId;
+
+    @ApiModelProperty(value = "教学班名称")
+    private String jxbmc;
+
+    @ApiModelProperty(value = "课程号")
+    private String kch;
+
+    @ApiModelProperty(value = "课程名称")
+    private String kcmc;
+
+    @ApiModelProperty(value = "工号")
+    private String jgh;
+
+    @ApiModelProperty(value = "姓名")
+    private String xm;
+
+    @ApiModelProperty(value = "开课部门")
+    private String kkbm;
+
+    @ApiModelProperty(value = "学分")
+    private String xf;
+
+    @ApiModelProperty(value = "学号")
+    private String xh;
+
+    @ApiModelProperty(value = "学生姓名")
+    private String xsxm;
+
+    @ApiModelProperty(value = "学院")
+    private String jgmc;
+
+    @ApiModelProperty(value = "专业")
+    private String zymc;
+
+    @ApiModelProperty(value = "重修标记")
+    private String cxbj;
+
+    @ApiModelProperty(value = "年级")
+    private String njdmId;
+
+    @ApiModelProperty(value = "座位号")
+    private String zwh;
+
+    @ApiModelProperty(value = "场地名称")
+    private String cdmc;
+
+    @ApiModelProperty(value = "考试备注")
+    private String ksbz;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public Long getExamSyncTotalId() {
+        return examSyncTotalId;
+    }
+
+    public void setExamSyncTotalId(Long examSyncTotalId) {
+        this.examSyncTotalId = examSyncTotalId;
+    }
+
+    public String getXnm() {
+        return xnm;
+    }
+
+    public void setXnm(String xnm) {
+        this.xnm = xnm;
+    }
+
+    public String getXqm() {
+        return xqm;
+    }
+
+    public void setXqm(String xqm) {
+        this.xqm = xqm;
+    }
+
+    public String getJxbId() {
+        return jxbId;
+    }
+
+    public void setJxbId(String jxbId) {
+        this.jxbId = jxbId;
+    }
+
+    public String getJxbmc() {
+        return jxbmc;
+    }
+
+    public void setJxbmc(String jxbmc) {
+        this.jxbmc = jxbmc;
+    }
+
+    public String getKch() {
+        return kch;
+    }
+
+    public void setKch(String kch) {
+        this.kch = kch;
+    }
+
+    public String getKcmc() {
+        return kcmc;
+    }
+
+    public void setKcmc(String kcmc) {
+        this.kcmc = kcmc;
+    }
+
+    public String getJgh() {
+        return jgh;
+    }
+
+    public void setJgh(String jgh) {
+        this.jgh = jgh;
+    }
+
+    public String getXm() {
+        return xm;
+    }
+
+    public void setXm(String xm) {
+        this.xm = xm;
+    }
+
+    public String getKkbm() {
+        return kkbm;
+    }
+
+    public void setKkbm(String kkbm) {
+        this.kkbm = kkbm;
+    }
+
+    public String getXf() {
+        return xf;
+    }
+
+    public void setXf(String xf) {
+        this.xf = xf;
+    }
+
+    public String getXh() {
+        return xh;
+    }
+
+    public void setXh(String xh) {
+        this.xh = xh;
+    }
+
+    public String getXsxm() {
+        return xsxm;
+    }
+
+    public void setXsxm(String xsxm) {
+        this.xsxm = xsxm;
+    }
+
+    public String getJgmc() {
+        return jgmc;
+    }
+
+    public void setJgmc(String jgmc) {
+        this.jgmc = jgmc;
+    }
+
+    public String getZymc() {
+        return zymc;
+    }
+
+    public void setZymc(String zymc) {
+        this.zymc = zymc;
+    }
+
+    public String getCxbj() {
+        return cxbj;
+    }
+
+    public void setCxbj(String cxbj) {
+        this.cxbj = cxbj;
+    }
+
+    public String getNjdmId() {
+        return njdmId;
+    }
+
+    public void setNjdmId(String njdmId) {
+        this.njdmId = njdmId;
+    }
+
+    public String getZwh() {
+        return zwh;
+    }
+
+    public void setZwh(String zwh) {
+        this.zwh = zwh;
+    }
+
+    public String getCdmc() {
+        return cdmc;
+    }
+
+    public void setCdmc(String cdmc) {
+        this.cdmc = cdmc;
+    }
+
+    public String getKsbz() {
+        return ksbz;
+    }
+
+    public void setKsbz(String ksbz) {
+        this.ksbz = ksbz;
+    }
+}

+ 194 - 0
src/main/java/com/qmth/eds/entity/ExamSyncTotal.java

@@ -0,0 +1,194 @@
+package com.qmth.eds.entity;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.eds.base.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * 学期表
+ */
+@ApiModel(value = "ExamSemester对象", description = "学期表")
+public class ExamSyncTotal extends BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "同步日期")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long syncDate;
+
+    @ApiModelProperty(value = "学校id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long schoolId;
+
+    @ApiModelProperty(value = "学校名称")
+    private String schoolName;
+
+    @ApiModelProperty(value = "学期id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long semesterId;
+
+    @ApiModelProperty(value = "学期名称")
+    private String semesterName;
+
+    @ApiModelProperty(value = "考试类型id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long examTypeId;
+
+    @ApiModelProperty(value = "考试类型名称")
+    private String examTypeName;
+
+    @ApiModelProperty(value = "学院数量")
+    private Integer colleges;
+
+    @ApiModelProperty(value = "科目数量")
+    private Integer subjects;
+
+    @ApiModelProperty(value = "科次")
+    private Integer students;
+
+    @ApiModelProperty(value = "文件名称")
+    private String fileName;
+
+    @ApiModelProperty(value = "文件地址")
+    private String filePath;
+
+    @ApiModelProperty(value = "是否可以下载")
+    private Boolean downloadStatus;
+
+    @ApiModelProperty(value = "数据md5值")
+    private String dataMd5;
+
+    public ExamSyncTotal() {
+    }
+
+    public ExamSyncTotal(Long syncDate, Long schoolId, String schoolName, Long semesterId, String semesterName, Long examTypeId, String examTypeName, Integer colleges, Integer subjects, Integer students, String dataMd5) {
+        this.syncDate = syncDate;
+        this.schoolId = schoolId;
+        this.schoolName = schoolName;
+        this.semesterId = semesterId;
+        this.semesterName = semesterName;
+        this.examTypeId = examTypeId;
+        this.examTypeName = examTypeName;
+        this.colleges = colleges;
+        this.subjects = subjects;
+        this.students = students;
+        this.dataMd5 = dataMd5;
+        this.downloadStatus = true;
+    }
+
+    public Long getSyncDate() {
+        return syncDate;
+    }
+
+    public void setSyncDate(Long syncDate) {
+        this.syncDate = syncDate;
+    }
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public String getSchoolName() {
+        return schoolName;
+    }
+
+    public void setSchoolName(String schoolName) {
+        this.schoolName = schoolName;
+    }
+
+    public Long getSemesterId() {
+        return semesterId;
+    }
+
+    public void setSemesterId(Long semesterId) {
+        this.semesterId = semesterId;
+    }
+
+    public String getSemesterName() {
+        return semesterName;
+    }
+
+    public void setSemesterName(String semesterName) {
+        this.semesterName = semesterName;
+    }
+
+    public Long getExamTypeId() {
+        return examTypeId;
+    }
+
+    public void setExamTypeId(Long examTypeId) {
+        this.examTypeId = examTypeId;
+    }
+
+    public String getExamTypeName() {
+        return examTypeName;
+    }
+
+    public void setExamTypeName(String examTypeName) {
+        this.examTypeName = examTypeName;
+    }
+
+    public Integer getColleges() {
+        return colleges;
+    }
+
+    public void setColleges(Integer colleges) {
+        this.colleges = colleges;
+    }
+
+    public Integer getSubjects() {
+        return subjects;
+    }
+
+    public void setSubjects(Integer subjects) {
+        this.subjects = subjects;
+    }
+
+    public Integer getStudents() {
+        return students;
+    }
+
+    public void setStudents(Integer students) {
+        this.students = students;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    public Boolean getDownloadStatus() {
+        return downloadStatus;
+    }
+
+    public void setDownloadStatus(Boolean downloadStatus) {
+        this.downloadStatus = downloadStatus;
+    }
+
+    public String getDataMd5() {
+        return dataMd5;
+    }
+
+    public void setDataMd5(String dataMd5) {
+        this.dataMd5 = dataMd5;
+    }
+}

+ 27 - 0
src/main/java/com/qmth/eds/entity/ExamType.java

@@ -0,0 +1,27 @@
+package com.qmth.eds.entity;
+
+import com.qmth.eds.base.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * 考试类型表
+ */
+@ApiModel(value = "ExamType对象", description = "考试类型表")
+public class ExamType extends BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "名称")
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 22 - 0
src/main/java/com/qmth/eds/entity/SysUser.java

@@ -2,11 +2,15 @@ package com.qmth.eds.entity;
 
 import java.io.Serializable;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.qmth.eds.base.BaseEntity;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
 
 /**
  * <p>
@@ -26,9 +30,13 @@ public class SysUser extends BaseEntity implements Serializable {
     private Long schoolId;
 
     @ApiModelProperty(value = "用户名")
+    @NotBlank(message = "请输入用户名")
+    @Length(message = "用户名不能超过{max}个字符", max = 50)
     private String loginName;
 
     @ApiModelProperty(value = "姓名")
+    @NotBlank(message = "请输入姓名")
+    @Length(message = "姓名不能超过{max}个字符", max = 50)
     private String realName;
 
     @ApiModelProperty(value = "工号")
@@ -38,6 +46,8 @@ public class SysUser extends BaseEntity implements Serializable {
     private String password;
 
     @ApiModelProperty(value = "手机号")
+    @NotBlank(message = "请输入手机号码")
+    @Length(message = "手机号码不能超过{max}个字符", max = 11)
     private String mobileNumber;
 
     @ApiModelProperty(value = "机构id")
@@ -56,6 +66,10 @@ public class SysUser extends BaseEntity implements Serializable {
     @ApiModelProperty(value = "备注")
     private String remark;
 
+    @TableField(exist = false)
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long roleId;
+
     public Long getSchoolId() {
         return schoolId;
     }
@@ -143,4 +157,12 @@ public class SysUser extends BaseEntity implements Serializable {
     public void setRemark(String remark) {
         this.remark = remark;
     }
+
+    public Long getRoleId() {
+        return roleId;
+    }
+
+    public void setRoleId(Long roleId) {
+        this.roleId = roleId;
+    }
 }

+ 136 - 0
src/main/java/com/qmth/eds/entity/TBSyncTask.java

@@ -0,0 +1,136 @@
+package com.qmth.eds.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.eds.base.BaseEntity;
+import com.qmth.eds.enums.TaskResultEnum;
+import com.qmth.eds.enums.TaskStatusEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 同步任务表
+ * </p>
+ */
+@ApiModel(value = "TBSyncTask对象", description = "同步任务表")
+public class TBSyncTask extends BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "学校ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableField("school_id")
+    private Long schoolId;
+
+    @ApiModelProperty(value = "学期ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableField(value = "semester_id")
+    private Long semesterId;
+
+    @ApiModelProperty(value = "考试类型ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableField("exam_type_id")
+    private Long examTypeId;
+
+    @ApiModelProperty(value = "执行时间")
+    @TableField(value = "sync_time")
+    private Long syncTime;
+
+    @ApiModelProperty(value = "任务状态,INIT:未开始,RUNNING:进行中,FINISH:已完成")
+    @TableField(value = "status")
+    private TaskStatusEnum status;
+
+    @ApiModelProperty(value = "数据结果,SUCCESS:成功,ERROR:失败")
+    @TableField(value = "result")
+    private TaskResultEnum result;
+
+    @ApiModelProperty(value = "备注")
+    @TableField(value = "remark")
+    private String remark;
+
+    @ApiModelProperty(value = "错误原因")
+    @TableField(value = "error_message", updateStrategy = FieldStrategy.IGNORED)
+    private String errorMessage;
+
+    public TBSyncTask() {
+    }
+
+    public TBSyncTask(Long schoolId, Long semesterId, Long examTypeId, Long createId) {
+        this.schoolId = schoolId;
+        this.semesterId = semesterId;
+        this.examTypeId = examTypeId;
+        this.status = TaskStatusEnum.INIT;
+        this.syncTime = System.currentTimeMillis();
+        this.setCreateId(createId);
+        this.setCreateTime(System.currentTimeMillis());
+    }
+
+    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 getExamTypeId() {
+        return examTypeId;
+    }
+
+    public void setExamTypeId(Long examTypeId) {
+        this.examTypeId = examTypeId;
+    }
+
+    public Long getSyncTime() {
+        return syncTime;
+    }
+
+    public void setSyncTime(Long syncTime) {
+        this.syncTime = syncTime;
+    }
+
+    public TaskStatusEnum getStatus() {
+        return status;
+    }
+
+    public void setStatus(TaskStatusEnum status) {
+        this.status = status;
+    }
+
+    public TaskResultEnum getResult() {
+        return result;
+    }
+
+    public void setResult(TaskResultEnum result) {
+        this.result = result;
+    }
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+}

+ 2 - 1
src/main/java/com/qmth/eds/enums/JobEnum.java

@@ -11,7 +11,8 @@ import java.util.Objects;
  */
 public enum JobEnum {
 
-    TIMED_SYNC_SCHOOL_JOB("学校信息同步定时任务");
+    TIMED_SYNC_SCHOOL_JOB("学校信息同步定时任务"),
+    TIMED_SYNC_TASK_JOB("同步武大考务数据");
 
     private String title;
 

+ 5 - 33
src/main/java/com/qmth/eds/enums/RoleTypeEnum.java

@@ -5,20 +5,13 @@ import java.util.List;
 
 /**
  * 角色类型
+ *
  * @Date: 2021/3/23.
  */
 public enum RoleTypeEnum {
 
-    ADMIN("系统管理员"),
-    SCHOOL_ADMIN("学校管理员"),
-    EXAM_TEACHER("考务老师"),
-    QUESTION_TEACHER("命题老师"),
-    CUSTOMER("客服员"),
-    PRINTER("印刷员"),
-    CUSTOM("自定义"),
-    OFFICE_TEACHER("教务处老师"),
-    PRESIDENT("学院院长"),
-    TEACHER("任课老师");
+    ADMIN("管理员"),
+    IMPLEMENT("实施人员");
 
     RoleTypeEnum(String desc) {
         this.desc = desc;
@@ -30,16 +23,9 @@ public enum RoleTypeEnum {
         return desc;
     }
 
-    /**
-     * @param hasAdmin 是否需要管理员
-     * @return
-     */
-    public static List<EnumResult> listTypes(boolean hasAdmin) {
-        List<EnumResult> list = new ArrayList<EnumResult>();
+    public static List<EnumResult> listTypes() {
+        List<EnumResult> list = new ArrayList<>();
         for (RoleTypeEnum value : RoleTypeEnum.values()) {
-            if (!hasAdmin && "ADMIN".equals(value.name())) {
-                continue;
-            }
             EnumResult result = new EnumResult();
             result.setName(value.name());
             result.setOrdinal(value.ordinal());
@@ -50,18 +36,4 @@ public enum RoleTypeEnum {
         return list;
     }
 
-    /**
-     * 根据desc查找枚举类
-     * @param desc desc
-     * @return 枚举类
-     */
-    public static RoleTypeEnum convertDescToEnum(String desc){
-        RoleTypeEnum res = null;
-        for (RoleTypeEnum value : RoleTypeEnum.values()) {
-            if (desc.equals(value.getDesc())) {
-                res = value;
-            }
-        }
-        return res;
-    }
 }

+ 42 - 0
src/main/java/com/qmth/eds/enums/TaskResultEnum.java

@@ -0,0 +1,42 @@
+package com.qmth.eds.enums;
+
+import java.util.Objects;
+
+/**
+ * @Description: 任务结果enum
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2021/3/29
+ */
+public enum TaskResultEnum {
+
+    SUCCESS("成功"),
+
+    ERROR("失败");
+
+    private String title;
+
+    private TaskResultEnum(String title) {
+        this.title = title;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * 状态转换 toName
+     *
+     * @param title
+     * @return
+     */
+    public static String convertToName(String title) {
+        for (TaskResultEnum e : TaskResultEnum.values()) {
+            if (Objects.equals(title, e.getTitle())) {
+                return e.name();
+            }
+        }
+        return null;
+    }
+}

+ 59 - 0
src/main/java/com/qmth/eds/enums/TaskStatusEnum.java

@@ -0,0 +1,59 @@
+package com.qmth.eds.enums;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @Description: 任务状态enum
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2021/3/29
+ */
+public enum TaskStatusEnum {
+
+    INIT("未开始"),
+
+    RUNNING("进行中"),
+
+    FINISH("已完成");
+
+    private String title;
+
+    private TaskStatusEnum(String title) {
+        this.title = title;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * 状态转换 toName
+     *
+     * @param title
+     * @return
+     */
+    public static String convertToName(String title) {
+        for (TaskStatusEnum e : TaskStatusEnum.values()) {
+            if (Objects.equals(title, e.getTitle())) {
+                return e.name();
+            }
+        }
+        return null;
+    }
+
+    public static List<EnumResult> listTypes() {
+        List<EnumResult> list = new ArrayList<>();
+        for (TaskStatusEnum value : TaskStatusEnum.values()) {
+            EnumResult result = new EnumResult();
+            result.setName(value.name());
+            result.setOrdinal(value.ordinal());
+            result.setCode(null);
+            result.setDesc(value.getTitle());
+            list.add(result);
+        }
+        return list;
+    }
+}

+ 37 - 0
src/main/java/com/qmth/eds/enums/UseSceneEnum.java

@@ -0,0 +1,37 @@
+package com.qmth.eds.enums;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 用途类型
+ */
+public enum UseSceneEnum {
+
+    PRINT("变量印刷"),
+    SCAN_MARK("扫描阅卷"),
+    TEACH_ANALYSIS("教研分析");
+
+    UseSceneEnum(String desc) {
+        this.desc = desc;
+    }
+
+    private String desc;
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public static List<EnumResult> listTypes() {
+        List<EnumResult> list = new ArrayList<>();
+        for (UseSceneEnum value : UseSceneEnum.values()) {
+            EnumResult result = new EnumResult();
+            result.setName(value.name());
+            result.setOrdinal(value.ordinal());
+            result.setCode(null);
+            result.setDesc(value.getDesc());
+            list.add(result);
+        }
+        return list;
+    }
+}

+ 5 - 0
src/main/java/com/qmth/eds/job/service/JobService.java

@@ -15,4 +15,9 @@ public interface JobService {
      * 同步学校信息
      */
     void updateSchoolInfo() throws IOException;
+
+    /**
+     * 同步武大考务数据
+     */
+    void getExamData();
 }

+ 24 - 0
src/main/java/com/qmth/eds/job/service/TimedSyncTaskJob.java

@@ -0,0 +1,24 @@
+package com.qmth.eds.job.service;
+
+import org.jetbrains.annotations.NotNull;
+import org.quartz.JobExecutionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+import javax.annotation.Resource;
+
+/**
+ * 获取考务数据定时任务
+ */
+public class TimedSyncTaskJob extends QuartzJobBean {
+    private final static Logger log = LoggerFactory.getLogger(TimedSyncTaskJob.class);
+
+    @Resource
+    JobService jobService;
+
+    @Override
+    protected void executeInternal(@NotNull JobExecutionContext jobExecutionContext) {
+        jobService.getExamData();
+    }
+}

+ 24 - 8
src/main/java/com/qmth/eds/job/service/impl/JobServiceImpl.java

@@ -1,25 +1,41 @@
 package com.qmth.eds.job.service.impl;
 
+import com.qmth.eds.entity.ExamScheduleTask;
 import com.qmth.eds.job.service.JobService;
+import com.qmth.eds.service.AsyncTaskService;
+import com.qmth.eds.service.ExamScheduleTaskService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
-import java.io.IOException;
+import javax.annotation.Resource;
+import java.util.List;
 
 /**
- * @Description: job service impl
- * @Param:
- * @return:
- * @Author: wangliang
- * @Date: 2021/3/31
+ * job service impl
  */
 @Service
 public class JobServiceImpl implements JobService {
-    private final static Logger log = LoggerFactory.getLogger(JobServiceImpl.class);
+    private static final Logger log = LoggerFactory.getLogger(JobServiceImpl.class);
+
+    @Resource
+    private ExamScheduleTaskService examScheduleTaskService;
+
+    @Resource
+    private AsyncTaskService asyncTaskService;
 
     @Override
-    public void updateSchoolInfo() throws IOException {
+    public void updateSchoolInfo() {
 
     }
+
+    @Override
+    public void getExamData() {
+        List<ExamScheduleTask> examScheduleTaskList = examScheduleTaskService.listByCurrentDate();
+        if (!examScheduleTaskList.isEmpty()) {
+            for (ExamScheduleTask scheduleTask : examScheduleTaskList) {
+                asyncTaskService.syncExamData(scheduleTask);
+            }
+        }
+    }
 }

+ 15 - 0
src/main/java/com/qmth/eds/mapper/ExamDownloadRecordMapper.java

@@ -0,0 +1,15 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.qmth.eds.bean.dto.ExamDownloadRecordDto;
+import com.qmth.eds.entity.ExamDownloadRecord;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 用户下载记录表 Mapper 接口
+ */
+public interface ExamDownloadRecordMapper extends BaseMapper<ExamDownloadRecord> {
+    IPage<ExamDownloadRecordDto> list(@Param("page") Page<ExamDownloadRecordDto> page, @Param("operateDate") Long operateDate, @Param("used") Boolean used, @Param("useScene") String useScene);
+}

+ 12 - 0
src/main/java/com/qmth/eds/mapper/ExamScheduleTaskMapper.java

@@ -0,0 +1,12 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.eds.entity.ExamScheduleTask;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 定时任务设置表 Mapper 接口
+ */
+public interface ExamScheduleTaskMapper extends BaseMapper<ExamScheduleTask> {
+    ExamScheduleTask findBySchoolIdAndSemesterIdAndExamTypeId(@Param("schoolId") Long schoolId, @Param("semesterId") Long semesterId, @Param("examTypeId") Long examTypeId);
+}

+ 10 - 0
src/main/java/com/qmth/eds/mapper/ExamSemesterMapper.java

@@ -0,0 +1,10 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.eds.entity.ExamSemester;
+
+/**
+ * 学期表 Mapper 接口
+ */
+public interface ExamSemesterMapper extends BaseMapper<ExamSemester> {
+}

+ 10 - 0
src/main/java/com/qmth/eds/mapper/ExamSyncStudentMapper.java

@@ -0,0 +1,10 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.eds.entity.ExamSyncStudent;
+
+/**
+ * 考务数据同步考生信息表 Mapper 接口
+ */
+public interface ExamSyncStudentMapper extends BaseMapper<ExamSyncStudent> {
+}

+ 10 - 0
src/main/java/com/qmth/eds/mapper/ExamSyncStudentTempMapper.java

@@ -0,0 +1,10 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.eds.entity.ExamSyncStudentTemp;
+
+/**
+ * 考务数据同步考生信息表 Mapper 接口
+ */
+public interface ExamSyncStudentTempMapper extends BaseMapper<ExamSyncStudentTemp> {
+}

+ 10 - 0
src/main/java/com/qmth/eds/mapper/ExamSyncTotalMapper.java

@@ -0,0 +1,10 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.eds.entity.ExamSyncTotal;
+
+/**
+ * 考务数据同步记录表 Mapper 接口
+ */
+public interface ExamSyncTotalMapper extends BaseMapper<ExamSyncTotal> {
+}

+ 10 - 0
src/main/java/com/qmth/eds/mapper/ExamTypeMapper.java

@@ -0,0 +1,10 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.eds.entity.ExamType;
+
+/**
+ * 考试类型表 Mapper 接口
+ */
+public interface ExamTypeMapper extends BaseMapper<ExamType> {
+}

+ 3 - 0
src/main/java/com/qmth/eds/mapper/SysRoleMapper.java

@@ -3,6 +3,8 @@ package com.qmth.eds.mapper;
 import com.qmth.eds.entity.SysRole;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 
+import java.util.List;
+
 /**
  * <p>
  * 角色表 Mapper 接口
@@ -13,4 +15,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface SysRoleMapper extends BaseMapper<SysRole> {
 
+    List<SysRole> listByUserId(Long userId);
 }

+ 1 - 0
src/main/java/com/qmth/eds/mapper/SysUserMapper.java

@@ -13,4 +13,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface SysUserMapper extends BaseMapper<SysUser> {
 
+    SysUser findByMobileNumber(String mobileNumber);
 }

+ 13 - 0
src/main/java/com/qmth/eds/mapper/TBSyncTaskMapper.java

@@ -0,0 +1,13 @@
+package com.qmth.eds.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.eds.entity.TBSyncTask;
+
+/**
+ * <p>
+ * 同步数据 Mapper 接口
+ * </p>
+ */
+public interface TBSyncTaskMapper extends BaseMapper<TBSyncTask> {
+
+}

+ 29 - 0
src/main/java/com/qmth/eds/service/AsyncTaskService.java

@@ -0,0 +1,29 @@
+package com.qmth.eds.service;
+
+import com.qmth.eds.entity.ExamScheduleTask;
+import com.qmth.eds.entity.TBSyncTask;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * 异步同步云阅卷
+ */
+@Service
+public class AsyncTaskService {
+
+    @Resource
+    private DataSyncService dataSyncService;
+
+    @Resource
+    private TBSyncTaskService tbSyncTaskService;
+
+    /**
+     * 同步试卷结构、分组信息、评卷员绑定关系
+     */
+    public void syncExamData(ExamScheduleTask examScheduleTask) {
+        // 记录日志
+        TBSyncTask tbSyncTask = tbSyncTaskService.saveTask(examScheduleTask);
+        dataSyncService.syncWuhanUniversityExamData(examScheduleTask, tbSyncTask);
+    }
+}

+ 18 - 0
src/main/java/com/qmth/eds/service/DataSyncService.java

@@ -0,0 +1,18 @@
+package com.qmth.eds.service;
+
+import com.qmth.eds.entity.ExamScheduleTask;
+import com.qmth.eds.entity.TBSyncTask;
+
+/**
+ * 同步数据到云阅卷 服务类
+ */
+public interface DataSyncService {
+
+    /**
+     * 武汉大学考务数据同步
+     *
+     * @param examScheduleTask examScheduleTask
+     * @param tbSyncTask       tbSyncTask
+     */
+    void syncWuhanUniversityExamData(ExamScheduleTask examScheduleTask, TBSyncTask tbSyncTask);
+}

+ 16 - 0
src/main/java/com/qmth/eds/service/ExamDownloadRecordService.java

@@ -0,0 +1,16 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.bean.dto.ExamDownloadRecordDto;
+import com.qmth.eds.entity.ExamDownloadRecord;
+
+/**
+ * 考务数据同步记录 服务类
+ */
+public interface ExamDownloadRecordService extends IService<ExamDownloadRecord> {
+
+    IPage<ExamDownloadRecordDto> list(Long operateDate, Boolean used, String useScene, Integer pageNumber, Integer pageSize);
+
+    boolean used(String id, Boolean used, String useScene);
+}

+ 19 - 0
src/main/java/com/qmth/eds/service/ExamScheduleTaskService.java

@@ -0,0 +1,19 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.entity.ExamScheduleTask;
+
+import java.util.List;
+
+/**
+ * 定时任务设置 服务类
+ */
+public interface ExamScheduleTaskService extends IService<ExamScheduleTask> {
+
+    IPage<ExamScheduleTask> list(Long schoolId, Long semesterId, Long examTypeId, Long startTime, Long endTime, Integer pageNumber, Integer pageSize);
+
+    boolean saveTask(ExamScheduleTask examScheduleTask);
+
+    List<ExamScheduleTask> listByCurrentDate();
+}

+ 14 - 0
src/main/java/com/qmth/eds/service/ExamSemesterService.java

@@ -0,0 +1,14 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.entity.ExamSemester;
+
+import java.util.List;
+
+/**
+ * 学期表 服务类
+ */
+public interface ExamSemesterService extends IService<ExamSemester> {
+
+    List<ExamSemester> listBySchoolId(Long schoolId);
+}

+ 11 - 0
src/main/java/com/qmth/eds/service/ExamSyncStudentService.java

@@ -0,0 +1,11 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.entity.ExamSyncStudent;
+
+/**
+ * 考试类型表 服务类
+ */
+public interface ExamSyncStudentService extends IService<ExamSyncStudent> {
+
+}

+ 12 - 0
src/main/java/com/qmth/eds/service/ExamSyncStudentTempService.java

@@ -0,0 +1,12 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.entity.ExamSyncStudentTemp;
+
+/**
+ * 考务数据临时表 服务类
+ */
+public interface ExamSyncStudentTempService extends IService<ExamSyncStudentTemp> {
+
+    void deleteByExamSyncTotalId(Long examSyncTotalId);
+}

+ 19 - 0
src/main/java/com/qmth/eds/service/ExamSyncTotalService.java

@@ -0,0 +1,19 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.entity.ExamSyncTotal;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 考务数据同步记录 服务类
+ */
+public interface ExamSyncTotalService extends IService<ExamSyncTotal> {
+
+    IPage<ExamSyncTotal> list(Long syncDate, Integer pageNumber, Integer pageSize);
+
+    void download(Long id, Boolean writeLog, HttpServletResponse response);
+
+    void updateDownloadStatusBySchoolIdAndSemesterIdAndExamTypeId(Long schoolId, Long semesterId, Long examTypeId);
+}

+ 11 - 0
src/main/java/com/qmth/eds/service/ExamTypeService.java

@@ -0,0 +1,11 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.entity.ExamType;
+
+/**
+ * 考试类型表 服务类
+ */
+public interface ExamTypeService extends IService<ExamType> {
+
+}

+ 3 - 0
src/main/java/com/qmth/eds/service/SysRoleService.java

@@ -3,6 +3,8 @@ package com.qmth.eds.service;
 import com.qmth.eds.entity.SysRole;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
+
 /**
  * <p>
  * 角色表 服务类
@@ -13,4 +15,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface SysRoleService extends IService<SysRole> {
 
+    List<SysRole> listByUserId(Long userId);
 }

+ 1 - 0
src/main/java/com/qmth/eds/service/SysUserRoleService.java

@@ -13,4 +13,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface SysUserRoleService extends IService<SysUserRole> {
 
+    void deleteByUserId(Long userId);
 }

+ 8 - 0
src/main/java/com/qmth/eds/service/SysUserService.java

@@ -1,5 +1,6 @@
 package com.qmth.eds.service;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.qmth.eds.entity.SysUser;
 import com.baomidou.mybatisplus.extension.service.IService;
 
@@ -13,4 +14,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface SysUserService extends IService<SysUser> {
 
+    IPage<SysUser> list(String loginName, Boolean enable, String realName, Integer pageNumber, Integer pageSize);
+
+    boolean saveUser(SysUser sysUser);
+
+    boolean enable(SysUser user);
+
+    boolean resetPassword(Long id);
 }

+ 19 - 0
src/main/java/com/qmth/eds/service/TBSyncTaskService.java

@@ -0,0 +1,19 @@
+package com.qmth.eds.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.eds.entity.ExamScheduleTask;
+import com.qmth.eds.entity.TBSyncTask;
+import com.qmth.eds.enums.TaskResultEnum;
+import com.qmth.eds.enums.TaskStatusEnum;
+
+/**
+ * <p>
+ * 同步数据 服务类
+ * </p>
+ */
+public interface TBSyncTaskService extends IService<TBSyncTask> {
+
+    TBSyncTask saveTask(ExamScheduleTask examScheduleTask);
+
+    void updateStatusAndResultById(Long id,TaskStatusEnum status, TaskResultEnum result, String errorMessage);
+}

+ 25 - 0
src/main/java/com/qmth/eds/service/TeachcloudCommonService.java

@@ -0,0 +1,25 @@
+package com.qmth.eds.service;
+
+import com.qmth.eds.bean.result.LoginResult;
+import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.enums.AppSourceEnum;
+
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * 公共服务service
+ */
+public interface TeachcloudCommonService {
+
+    /**
+     * 删除用户信息
+     *
+     * @param userId
+     * @param all
+     */
+    void removeUserInfo(Long userId, boolean all) throws NoSuchAlgorithmException;
+
+
+    LoginResult login(String password, SysUser sysUser, AppSourceEnum appSource) throws NoSuchAlgorithmException;
+
+}

+ 318 - 0
src/main/java/com/qmth/eds/service/impl/DataSyncServiceImpl.java

@@ -0,0 +1,318 @@
+package com.qmth.eds.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qmth.boot.api.exception.ApiException;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.entity.*;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import com.qmth.eds.enums.TaskResultEnum;
+import com.qmth.eds.enums.TaskStatusEnum;
+import com.qmth.eds.service.*;
+import com.qmth.eds.util.FileUtil;
+import com.qmth.eds.util.WuhanUniversityUtils;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 同步服务类
+ */
+@Service
+public class DataSyncServiceImpl implements DataSyncService {
+
+    private static final Logger log = LoggerFactory.getLogger(DataSyncServiceImpl.class);
+
+    @Resource
+    private TBSyncTaskService tbSyncTaskService;
+
+    @Resource
+    private WuhanUniversityUtils wuhanUniversityUtils;
+
+    @Resource
+    private ExamSyncStudentService examSyncStudentService;
+
+    @Resource
+    private ExamSyncStudentTempService examSyncStudentTempService;
+
+    @Resource
+    private ExamSyncTotalService examSyncTotalService;
+
+
+    @Async
+    @Override
+    public void syncWuhanUniversityExamData(ExamScheduleTask examScheduleTask, TBSyncTask tbSyncTask) {
+        // 同步初始参数
+        TaskResultEnum result = null;
+        String errorMessage = "";
+        Long schoolId = examScheduleTask.getSchoolId();
+        String schoolName = examScheduleTask.getSchoolName();
+        Long semesterId = examScheduleTask.getSemesterId();
+        String semesterName = examScheduleTask.getSemesterName();
+        Long examTypeId = examScheduleTask.getExamTypeId();
+        String examTypeName = examScheduleTask.getExamTypeName();
+        File txtFile = null;
+        FileInputStream fis = null;
+        try {
+            // 同步中
+            tbSyncTaskService.updateStatusAndResultById(tbSyncTask.getId(), TaskStatusEnum.RUNNING, null, null);
+            // 获取token
+            String token = wuhanUniversityUtils.getAccessToken();
+            if (StringUtils.isBlank(token)) {
+                throw ExceptionResultEnum.ERROR.exception("获取AccessToken失败");
+            }
+            // 调用接口
+            List<ExamSyncStudentTemp> examSyncStudentTemps = wuhanUniversityUtils.getKwData(token);
+            // 汇总表ID
+            Long examSyncTotalId = SystemConstant.getDbUuid();
+            examSyncStudentTemps.forEach(m -> {
+                m.setId(SystemConstant.getDbUuid());
+                m.setSchoolId(schoolId);
+                m.setExamSyncTotalId(examSyncTotalId);
+            });
+            // 保存临时考务数据
+            examSyncStudentTempService.saveBatch(examSyncStudentTemps);
+
+            // 生成数据文件,用来比较内容是否有变动
+            txtFile = createTxt(schoolId, examSyncStudentTemps);
+            String txtFileMd5 = null;
+
+            if (txtFile != null) {
+                fis = new FileInputStream(txtFile);
+                txtFileMd5 = DigestUtils.md5Hex(fis);
+            }
+            // 校验数据是否变动,若变动,需要新增
+            if (canAddTotal(schoolId, semesterId, examTypeId, examSyncStudentTemps.size(), txtFileMd5)) {
+                // 保存到正式表
+                List<ExamSyncStudent> examSyncStudents = examSyncStudentTemps.stream().map(m -> {
+                    ExamSyncStudent examSyncStudent = new ExamSyncStudent();
+                    BeanUtil.copyProperties(m, examSyncStudent);
+                    examSyncStudent.setId(SystemConstant.getDbUuid());
+                    return examSyncStudent;
+                }).collect(Collectors.toList());
+                examSyncStudentService.saveBatch(examSyncStudents);
+
+                // 将以前数据设置为不可下载
+                examSyncTotalService.updateDownloadStatusBySchoolIdAndSemesterIdAndExamTypeId(schoolId, semesterId, examTypeId);
+
+                // 生成汇总数据
+                ZoneId zone = ZoneId.systemDefault();
+                Instant instant = LocalDate.now().atStartOfDay().atZone(zone).toInstant();
+                Date date = Date.from(instant);
+                Long syncDate = date.getTime();
+                int colleges = Integer.parseInt(String.valueOf(examSyncStudentTemps.stream().map(ExamSyncStudentTemp::getJgmc).filter(StringUtils::isNotBlank).distinct().count()));
+                int subjects = Integer.parseInt(String.valueOf(examSyncStudentTemps.stream().map(ExamSyncStudentTemp::getKch).filter(StringUtils::isNotBlank).distinct().count()));
+                int students = examSyncStudentTemps.size();
+                ExamSyncTotal examSyncTotal = new ExamSyncTotal(syncDate, schoolId, schoolName, semesterId, semesterName, examTypeId, examTypeName, colleges, subjects, students, txtFileMd5);
+                examSyncTotalService.save(examSyncTotal);
+                // 生成excel文件
+                File excelFile = createExcel(schoolId, examSyncStudents);
+
+                // 更新文件路径
+                examSyncTotal.setFileName(excelFile.getName());
+                examSyncTotal.setFilePath(excelFile.getPath());
+                examSyncTotalService.updateById(examSyncTotal);
+            }
+            // 删除临时表数据
+            examSyncStudentTempService.deleteByExamSyncTotalId(examSyncTotalId);
+
+            result = TaskResultEnum.SUCCESS;
+        } catch (ApiException | IOException e) {
+            result = TaskResultEnum.ERROR;
+            errorMessage = errorMessage + e.getMessage();
+        } finally {
+            tbSyncTaskService.updateStatusAndResultById(tbSyncTask.getId(), TaskStatusEnum.FINISH, result, errorMessage);
+
+            // 关闭fis流
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+
+            // 删除txt文件
+            if (txtFile != null) {
+                txtFile.delete();
+            }
+        }
+    }
+
+    /**
+     * 检查数据是否有变动,需要新增一条新同步记录
+     *
+     * @param schoolId   学校ID
+     * @param semesterId 学期ID
+     * @param examTypeId 考试类型ID
+     * @param count      考生数量
+     * @param txtFileMd5 txt文件md5
+     * @return true:可以新增  false:不能新增
+     */
+    private boolean canAddTotal(Long schoolId, Long semesterId, Long examTypeId, int count, String txtFileMd5) {
+        // txtFileMd5为空,无法校验,返回true
+        if (txtFileMd5 == null) {
+            return true;
+        }
+
+        // 查询上一次同步数据,先比较数量
+        QueryWrapper<ExamSyncTotal> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(ExamSyncTotal::getSchoolId, schoolId)
+                .eq(ExamSyncTotal::getSemesterId, semesterId)
+                .eq(ExamSyncTotal::getExamTypeId, examTypeId)
+                .orderByDesc(ExamSyncTotal::getSyncDate)
+                .orderByDesc(ExamSyncTotal::getCreateTime);
+        List<ExamSyncTotal> examSyncTotals = examSyncTotalService.list(queryWrapper);
+        // 没有同步记录,返回true
+        if (examSyncTotals.isEmpty()) {
+            return true;
+        }
+        ExamSyncTotal examSyncTotal = examSyncTotals.get(0);
+        // 上一次同步数据量与本次不一样,返回true
+        if (count != examSyncTotal.getStudents()) {
+            return true;
+        }
+        // 数据内容有变动,返回 true
+        return !txtFileMd5.equals(examSyncTotal.getDataMd5());
+    }
+
+    /**
+     * 生成txt,比较md5
+     *
+     * @param examSyncStudentTemps list
+     */
+    private File createTxt(Long schoolId, List<ExamSyncStudentTemp> examSyncStudentTemps) {
+        // 按学号排序
+        examSyncStudentTemps.sort(Comparator.comparing(ExamSyncStudentTemp::getXh));
+
+        List<String> list = examSyncStudentTemps.stream().map(m -> {
+            StringJoiner stringJoiner = new StringJoiner("|");
+            stringJoiner.add(m.getXnm())
+                    .add(m.getXqm())
+                    .add(m.getJxbId())
+                    .add(m.getJxbmc())
+                    .add(m.getKch())
+                    .add(m.getKcmc())
+                    .add(m.getJgh())
+                    .add(m.getXm())
+                    .add(m.getKkbm())
+                    .add(m.getXf())
+                    .add(m.getXh())
+                    .add(m.getXsxm())
+                    .add(m.getJgmc())
+                    .add(m.getZymc())
+                    .add(m.getCxbj())
+                    .add(m.getNjdmId())
+                    .add(m.getZwh())
+                    .add(m.getCdmc())
+                    .add(m.getKsbz());
+            return stringJoiner.toString();
+        }).collect(Collectors.toList());
+
+        File file = null;
+        try {
+            String fileName = "file-folder" + File.separator + schoolId + File.separator + System.currentTimeMillis() + ".txt";
+            file = FileUtil.writeTxt(fileName, list);
+        } catch (IOException e) {
+            log.error("数据txt文件生成失败");
+        }
+        return file;
+    }
+
+    /**
+     * 生成excel文件
+     *
+     * @param examSyncStudents 考务数据
+     */
+    private File createExcel(Long schoolId, List<ExamSyncStudent> examSyncStudents) throws IOException {
+        List<List<String>> head = new ArrayList<>();
+        List<String> headTitle0 = new ArrayList<>();
+        headTitle0.add("学年");
+        List<String> headTitle1 = new ArrayList<>();
+        headTitle1.add("学期");
+        List<String> headTitle2 = new ArrayList<>();
+        headTitle2.add("教学班ID");
+        List<String> headTitle3 = new ArrayList<>();
+        headTitle3.add("教学班名称");
+        List<String> headTitle4 = new ArrayList<>();
+        headTitle4.add("课程号");
+        List<String> headTitle5 = new ArrayList<>();
+        headTitle5.add("课程名称");
+        List<String> headTitle6 = new ArrayList<>();
+        headTitle6.add("工号");
+        List<String> headTitle7 = new ArrayList<>();
+        headTitle7.add("姓名");
+        List<String> headTitle8 = new ArrayList<>();
+        headTitle8.add("开课部门");
+        List<String> headTitle9 = new ArrayList<>();
+        headTitle9.add("学分");
+        List<String> headTitle10 = new ArrayList<>();
+        headTitle10.add("学号");
+        List<String> headTitle11 = new ArrayList<>();
+        headTitle11.add("学生姓名");
+        List<String> headTitle12 = new ArrayList<>();
+        headTitle12.add("学院");
+        List<String> headTitle13 = new ArrayList<>();
+        headTitle13.add("专业");
+        List<String> headTitle14 = new ArrayList<>();
+        headTitle14.add("重修标记");
+        List<String> headTitle15 = new ArrayList<>();
+        headTitle15.add("年级");
+        List<String> headTitle16 = new ArrayList<>();
+        headTitle16.add("座位号");
+        List<String> headTitle17 = new ArrayList<>();
+        headTitle17.add("场地名称");
+        List<String> headTitle18 = new ArrayList<>();
+        headTitle18.add("考试备注");
+
+
+        head.add(headTitle0);
+        head.add(headTitle1);
+        head.add(headTitle2);
+        head.add(headTitle3);
+        head.add(headTitle4);
+        head.add(headTitle5);
+        head.add(headTitle6);
+        head.add(headTitle7);
+        head.add(headTitle8);
+        head.add(headTitle9);
+        head.add(headTitle10);
+        head.add(headTitle11);
+        head.add(headTitle12);
+        head.add(headTitle13);
+        head.add(headTitle14);
+        head.add(headTitle15);
+        head.add(headTitle16);
+        head.add(headTitle17);
+        head.add(headTitle18);
+
+        String fileName = "file-folder" + File.separator + schoolId + File.separator + System.currentTimeMillis() + ".xlsx";
+        File file = new File(fileName);
+        if (!file.exists()) {
+            file.createNewFile();
+        }
+        ExcelWriter excelWriter = EasyExcel.write(file).build();
+        //动态表头
+        WriteSheet writeSheet1 = EasyExcel.writerSheet(0, "考务数据").head(head).build();
+        excelWriter.write(examSyncStudents, writeSheet1);
+        excelWriter.finish();
+
+        return file;
+    }
+}

+ 53 - 0
src/main/java/com/qmth/eds/service/impl/ExamDownloadRecordServiceImpl.java

@@ -0,0 +1,53 @@
+package com.qmth.eds.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.bean.dto.ExamDownloadRecordDto;
+import com.qmth.eds.entity.ExamDownloadRecord;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import com.qmth.eds.enums.UseSceneEnum;
+import com.qmth.eds.mapper.ExamDownloadRecordMapper;
+import com.qmth.eds.service.ExamDownloadRecordService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.StringJoiner;
+
+@Service
+public class ExamDownloadRecordServiceImpl extends ServiceImpl<ExamDownloadRecordMapper, ExamDownloadRecord> implements ExamDownloadRecordService {
+    @Override
+    public IPage<ExamDownloadRecordDto> list(Long operateDate, Boolean used, String useScene, Integer pageNumber, Integer pageSize) {
+        Page<ExamDownloadRecordDto> page = new Page<>(pageNumber, pageSize);
+        IPage<ExamDownloadRecordDto> examDownloadRecordDtoIPage = this.baseMapper.list(page, operateDate, used, useScene);
+        for (ExamDownloadRecordDto record : examDownloadRecordDtoIPage.getRecords()) {
+            String scene = record.getUseScene();
+            if (StringUtils.isNotBlank(scene)) {
+                String[] strings = scene.split(",");
+                StringJoiner stringJoiner = new StringJoiner(",");
+                for (String string : strings) {
+                    stringJoiner.add(UseSceneEnum.valueOf(string).getDesc());
+                }
+                record.setUseSceneStr(stringJoiner.toString());
+            }
+        }
+        return examDownloadRecordDtoIPage;
+    }
+
+    @Transactional
+    @Override
+    public boolean used(String id, Boolean used, String useScene) {
+        String[] ids = id.split(",");
+        for (String s : ids) {
+            ExamDownloadRecord downloadRecord = this.baseMapper.selectById(Long.parseLong(s));
+            if (downloadRecord == null) {
+                throw ExceptionResultEnum.ERROR.exception("下载记录数据不存在");
+            }
+            downloadRecord.setUsed(used);
+            downloadRecord.setUseScene(useScene);
+            this.baseMapper.updateById(downloadRecord);
+        }
+        return true;
+    }
+}

+ 80 - 0
src/main/java/com/qmth/eds/service/impl/ExamScheduleTaskServiceImpl.java

@@ -0,0 +1,80 @@
+package com.qmth.eds.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.entity.ExamScheduleTask;
+import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import com.qmth.eds.mapper.ExamScheduleTaskMapper;
+import com.qmth.eds.service.ExamScheduleTaskService;
+import com.qmth.eds.service.SysUserService;
+import com.qmth.eds.util.ServletUtil;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Service
+public class ExamScheduleTaskServiceImpl extends ServiceImpl<ExamScheduleTaskMapper, ExamScheduleTask> implements ExamScheduleTaskService {
+
+    @Resource
+    private SysUserService sysUserService;
+
+    @Override
+    public IPage<ExamScheduleTask> list(Long schoolId, Long semesterId, Long examTypeId, Long startTime, Long endTime, Integer pageNumber, Integer pageSize) {
+        QueryWrapper<ExamScheduleTask> queryWrapper = new QueryWrapper<>();
+        Page<ExamScheduleTask> page = new Page<>(pageNumber, pageSize);
+        if (schoolId != null) {
+            queryWrapper.lambda().eq(ExamScheduleTask::getSchoolId, schoolId);
+        }
+        if (semesterId != null) {
+            queryWrapper.lambda().eq(ExamScheduleTask::getSemesterId, semesterId);
+        }
+        if (examTypeId != null) {
+            queryWrapper.lambda().eq(ExamScheduleTask::getExamTypeId, examTypeId);
+        }
+        if (startTime != null) {
+            queryWrapper.lambda().ge(ExamScheduleTask::getStartTime, startTime);
+        }
+        if (endTime != null) {
+            queryWrapper.lambda().le(ExamScheduleTask::getEndTime, endTime);
+        }
+        Page<ExamScheduleTask> examScheduleTaskPage = this.baseMapper.selectPage(page, queryWrapper);
+        for (ExamScheduleTask record : examScheduleTaskPage.getRecords()) {
+            SysUser sysUser = sysUserService.getById(record.getCreateId());
+            record.setUserName(sysUser != null ? sysUser.getRealName() : null);
+        }
+        return examScheduleTaskPage;
+    }
+
+    @Override
+    public boolean saveTask(ExamScheduleTask examScheduleTask) {
+        Long userId = ServletUtil.getRequestUserId();
+        if (examScheduleTask.getId() == null) {
+            ExamScheduleTask scheduleTask = this.baseMapper.findBySchoolIdAndSemesterIdAndExamTypeId(examScheduleTask.getSchoolId(), examScheduleTask.getSemesterId(), examScheduleTask.getExamTypeId());
+            if (scheduleTask != null) {
+                throw ExceptionResultEnum.ERROR.exception("学校+学期+考试类型 已设置定时任务时间");
+            }
+            examScheduleTask.setId(SystemConstant.getDbUuid());
+            examScheduleTask.setCreateId(userId);
+            examScheduleTask.setCreateTime(System.currentTimeMillis());
+            this.baseMapper.insert(examScheduleTask);
+        } else {
+            examScheduleTask.setUpdateId(userId);
+            examScheduleTask.setUpdateTime(System.currentTimeMillis());
+            this.baseMapper.updateById(examScheduleTask);
+        }
+        return true;
+    }
+
+    @Override
+    public List<ExamScheduleTask> listByCurrentDate() {
+        long time = System.currentTimeMillis();
+        QueryWrapper<ExamScheduleTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().le(ExamScheduleTask::getStartTime, time).ge(ExamScheduleTask::getEndTime, time);
+        return this.baseMapper.selectList(queryWrapper);
+    }
+}

+ 20 - 0
src/main/java/com/qmth/eds/service/impl/ExamSemesterServiceImpl.java

@@ -0,0 +1,20 @@
+package com.qmth.eds.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.entity.ExamSemester;
+import com.qmth.eds.mapper.ExamSemesterMapper;
+import com.qmth.eds.service.ExamSemesterService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class ExamSemesterServiceImpl extends ServiceImpl<ExamSemesterMapper, ExamSemester> implements ExamSemesterService {
+    @Override
+    public List<ExamSemester> listBySchoolId(Long schoolId) {
+        QueryWrapper<ExamSemester> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(ExamSemester::getSchoolId, schoolId);
+        return this.baseMapper.selectList(queryWrapper);
+    }
+}

+ 11 - 0
src/main/java/com/qmth/eds/service/impl/ExamSyncStudentServiceImpl.java

@@ -0,0 +1,11 @@
+package com.qmth.eds.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.entity.ExamSyncStudent;
+import com.qmth.eds.mapper.ExamSyncStudentMapper;
+import com.qmth.eds.service.ExamSyncStudentService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ExamSyncStudentServiceImpl extends ServiceImpl<ExamSyncStudentMapper, ExamSyncStudent> implements ExamSyncStudentService {
+}

+ 18 - 0
src/main/java/com/qmth/eds/service/impl/ExamSyncStudentServiceTempImpl.java

@@ -0,0 +1,18 @@
+package com.qmth.eds.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.entity.ExamSyncStudentTemp;
+import com.qmth.eds.mapper.ExamSyncStudentTempMapper;
+import com.qmth.eds.service.ExamSyncStudentTempService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ExamSyncStudentServiceTempImpl extends ServiceImpl<ExamSyncStudentTempMapper, ExamSyncStudentTemp> implements ExamSyncStudentTempService {
+    @Override
+    public void deleteByExamSyncTotalId(Long examSyncTotalId) {
+        UpdateWrapper<ExamSyncStudentTemp> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(ExamSyncStudentTemp::getExamSyncTotalId, examSyncTotalId);
+        this.remove(updateWrapper);
+    }
+}

+ 84 - 0
src/main/java/com/qmth/eds/service/impl/ExamSyncTotalServiceImpl.java

@@ -0,0 +1,84 @@
+package com.qmth.eds.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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.entity.ExamDownloadRecord;
+import com.qmth.eds.entity.ExamSyncTotal;
+import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import com.qmth.eds.mapper.ExamSyncTotalMapper;
+import com.qmth.eds.service.ExamDownloadRecordService;
+import com.qmth.eds.service.ExamSyncTotalService;
+import com.qmth.eds.service.SysUserService;
+import com.qmth.eds.util.FileUtil;
+import com.qmth.eds.util.ServletUtil;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+
+@Service
+public class ExamSyncTotalServiceImpl extends ServiceImpl<ExamSyncTotalMapper, ExamSyncTotal> implements ExamSyncTotalService {
+
+    @Resource
+    private SysUserService sysUserService;
+
+    @Resource
+    private ExamDownloadRecordService examDownloadRecordService;
+
+    @Override
+    public IPage<ExamSyncTotal> list(Long syncDate, Integer pageNumber, Integer pageSize) {
+        QueryWrapper<ExamSyncTotal> queryWrapper = new QueryWrapper<>();
+        Page<ExamSyncTotal> page = new Page<>(pageNumber, pageSize);
+        if (syncDate != null) {
+            queryWrapper.lambda().eq(ExamSyncTotal::getSyncDate, syncDate);
+        }
+        return this.baseMapper.selectPage(page, queryWrapper);
+    }
+
+    @Override
+    public void download(Long id, Boolean writeLog, HttpServletResponse response) {
+        ExamSyncTotal examSyncTotal = this.getById(id);
+        String fileName = examSyncTotal.getFileName();
+        String filePath = examSyncTotal.getFilePath();
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw ExceptionResultEnum.ERROR.exception("文件不存在");
+        }
+
+        if (writeLog) {
+            // 记录日志
+            Long userId = ServletUtil.getRequestUserId();
+            SysUser sysUser = sysUserService.getById(userId);
+
+            ExamDownloadRecord examDownloadRecord = new ExamDownloadRecord();
+            examDownloadRecord.setId(SystemConstant.getDbUuid());
+            examDownloadRecord.setOperateTime(System.currentTimeMillis());
+            examDownloadRecord.setUserId(userId);
+            examDownloadRecord.setUserName(sysUser.getRealName());
+            examDownloadRecord.setSyncTotalId(id);
+            examDownloadRecord.setUsed(false);
+            examDownloadRecord.setCreateId(userId);
+            examDownloadRecord.setCreateTime(System.currentTimeMillis());
+            examDownloadRecordService.save(examDownloadRecord);
+        }
+
+        // 下载
+        FileUtil.outputFile(response, file, fileName);
+    }
+
+    @Override
+    public void updateDownloadStatusBySchoolIdAndSemesterIdAndExamTypeId(Long schoolId, Long semesterId, Long examTypeId) {
+        UpdateWrapper<ExamSyncTotal> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(ExamSyncTotal::getDownloadStatus, false)
+                .eq(ExamSyncTotal::getSchoolId, schoolId)
+                .eq(ExamSyncTotal::getSemesterId, semesterId)
+                .eq(ExamSyncTotal::getExamTypeId, examTypeId);
+        this.update(updateWrapper);
+    }
+}

+ 11 - 0
src/main/java/com/qmth/eds/service/impl/ExamTypeServiceImpl.java

@@ -0,0 +1,11 @@
+package com.qmth.eds.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.entity.ExamType;
+import com.qmth.eds.mapper.ExamTypeMapper;
+import com.qmth.eds.service.ExamTypeService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ExamTypeServiceImpl extends ServiceImpl<ExamTypeMapper, ExamType> implements ExamTypeService {
+}

+ 6 - 0
src/main/java/com/qmth/eds/service/impl/SysRoleServiceImpl.java

@@ -6,6 +6,8 @@ import com.qmth.eds.service.SysRoleService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * <p>
  * 角色表 服务实现类
@@ -17,4 +19,8 @@ import org.springframework.stereotype.Service;
 @Service
 public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
 
+    @Override
+    public List<SysRole> listByUserId(Long userId) {
+        return this.baseMapper.listByUserId(userId);
+    }
 }

+ 7 - 0
src/main/java/com/qmth/eds/service/impl/SysUserRoleServiceImpl.java

@@ -1,5 +1,6 @@
 package com.qmth.eds.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.qmth.eds.entity.SysUserRole;
 import com.qmth.eds.mapper.SysUserRoleMapper;
 import com.qmth.eds.service.SysUserRoleService;
@@ -17,4 +18,10 @@ import org.springframework.stereotype.Service;
 @Service
 public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleMapper, SysUserRole> implements SysUserRoleService {
 
+    @Override
+    public void deleteByUserId(Long userId) {
+        UpdateWrapper<SysUserRole> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(SysUserRole::getUserId, userId);
+        this.baseMapper.delete(updateWrapper);
+    }
 }

+ 82 - 1
src/main/java/com/qmth/eds/service/impl/SysUserServiceImpl.java

@@ -1,10 +1,24 @@
 package com.qmth.eds.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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.contant.SystemConstant;
 import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.entity.SysUserRole;
+import com.qmth.eds.enums.ExceptionResultEnum;
 import com.qmth.eds.mapper.SysUserMapper;
+import com.qmth.eds.service.SysUserRoleService;
 import com.qmth.eds.service.SysUserService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.util.Base64Util;
+import com.qmth.eds.util.ServletUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
 
 /**
  * <p>
@@ -17,4 +31,71 @@ import org.springframework.stereotype.Service;
 @Service
 public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
 
+    @Resource
+    private SysUserRoleService sysUserRoleService;
+
+    @Override
+    public IPage<SysUser> list(String loginName, Boolean enable, String realName, Integer pageNumber, Integer pageSize) {
+        Page<SysUser> page = new Page<>(pageNumber, pageSize);
+        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
+        if (StringUtils.isNotBlank(loginName)) {
+            queryWrapper.lambda().eq(SysUser::getLoginName, loginName);
+        }
+        if (StringUtils.isNotBlank(realName)) {
+            queryWrapper.lambda().like(SysUser::getRealName, realName);
+        }
+        if (enable != null) {
+            queryWrapper.lambda().eq(SysUser::getEnable, enable);
+        }
+        Page<SysUser> sysUserPage = this.baseMapper.selectPage(page, queryWrapper);
+        for (SysUser record : sysUserPage.getRecords()) {
+            QueryWrapper<SysUserRole> queryWrapper1 = new QueryWrapper<>();
+            queryWrapper1.lambda().eq(SysUserRole::getUserId, record.getId());
+            SysUserRole sysUserRole = sysUserRoleService.getOne(queryWrapper1);
+            record.setRoleId(sysUserRole != null ? sysUserRole.getRoleId() : null);
+        }
+        return sysUserPage;
+    }
+
+    @Transactional
+    @Override
+    public boolean saveUser(SysUser sysUser) {
+        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
+        if (sysUser.getId() == null) {
+            SysUser user = this.baseMapper.findByMobileNumber(sysUser.getMobileNumber());
+            if (user != null) {
+                throw ExceptionResultEnum.ERROR.exception("手机号已注册");
+            }
+            sysUser.setId(SystemConstant.getDbUuid());
+            sysUser.setCreateId(requestUser.getCreateId());
+            sysUser.setCreateTime(System.currentTimeMillis());
+            this.baseMapper.insert(sysUser);
+        } else {
+            sysUser.setUpdateId(requestUser.getUpdateId());
+            sysUser.setUpdateTime(System.currentTimeMillis());
+            this.baseMapper.updateById(sysUser);
+        }
+        // 保存角色
+        sysUserRoleService.deleteByUserId(sysUser.getId());
+        SysUserRole sysUserRole = new SysUserRole();
+        sysUserRole.setId(SystemConstant.getDbUuid());
+        sysUserRole.setUserId(sysUser.getId());
+        sysUserRole.setRoleId(sysUser.getRoleId());
+        return sysUserRoleService.save(sysUserRole);
+    }
+
+    @Override
+    public boolean enable(SysUser user) {
+        UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(SysUser::getEnable, user.getEnable()).eq(SysUser::getId, user.getId());
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean resetPassword(Long id) {
+        String md5Password = Base64Util.encode(SystemConstant.DEFAULT_PASSWORD.getBytes());
+        SysUser user = this.getById(id);
+        user.setPassword(md5Password);
+        return this.updateById(user);
+    }
 }

+ 56 - 0
src/main/java/com/qmth/eds/service/impl/TBSyncTaskServiceImpl.java

@@ -0,0 +1,56 @@
+package com.qmth.eds.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.entity.ExamScheduleTask;
+import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.entity.TBSyncTask;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import com.qmth.eds.enums.TaskResultEnum;
+import com.qmth.eds.enums.TaskStatusEnum;
+import com.qmth.eds.mapper.TBSyncTaskMapper;
+import com.qmth.eds.service.TBSyncTaskService;
+import com.qmth.eds.util.ServletUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 异步任务日志
+ */
+@Service
+public class TBSyncTaskServiceImpl extends ServiceImpl<TBSyncTaskMapper, TBSyncTask> implements TBSyncTaskService {
+
+    @Transactional
+    @Override
+    public TBSyncTask saveTask(ExamScheduleTask examScheduleTask) {
+        Long userId = ServletUtil.getRequestUserId();
+        QueryWrapper<TBSyncTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(TBSyncTask::getSchoolId, examScheduleTask.getSchoolId())
+                .eq(TBSyncTask::getSemesterId, examScheduleTask.getSemesterId())
+                .eq(TBSyncTask::getExamTypeId, examScheduleTask.getExamTypeId());
+        TBSyncTask tbSyncTask = this.getOne(queryWrapper);
+        if (tbSyncTask != null) {
+            if (!TaskStatusEnum.FINISH.equals(tbSyncTask.getStatus())) {
+                throw ExceptionResultEnum.ERROR.exception("有任务未结束");
+            }
+            this.removeById(tbSyncTask.getId());
+        }
+        tbSyncTask = new TBSyncTask(examScheduleTask.getSchoolId(), examScheduleTask.getSemesterId(), examScheduleTask.getExamTypeId(), userId);
+        tbSyncTask.setId(SystemConstant.getDbUuid());
+        this.save(tbSyncTask);
+        return tbSyncTask;
+    }
+
+    @Override
+    public void updateStatusAndResultById(Long id, TaskStatusEnum status, TaskResultEnum result, String errorMessage) {
+        UpdateWrapper<TBSyncTask> tbSyncTaskUpdateWrapper = new UpdateWrapper<>();
+        tbSyncTaskUpdateWrapper.lambda().set(TBSyncTask::getStatus, status)
+                .set(TBSyncTask::getResult, result)
+                .set(TBSyncTask::getErrorMessage, errorMessage)
+                .eq(TBSyncTask::getId, id);
+        this.update(tbSyncTaskUpdateWrapper);
+    }
+
+}

+ 120 - 0
src/main/java/com/qmth/eds/service/impl/TeachcloudCommonServiceImpl.java

@@ -0,0 +1,120 @@
+package com.qmth.eds.service.impl;
+
+import com.qmth.boot.core.enums.Platform;
+import com.qmth.eds.bean.auth.AuthBean;
+import com.qmth.eds.bean.auth.ExpireTimeBean;
+import com.qmth.eds.bean.result.LoginResult;
+import com.qmth.eds.bean.result.UserLoginCheckResult;
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.entity.SysRole;
+import com.qmth.eds.entity.SysUser;
+import com.qmth.eds.entity.TBSession;
+import com.qmth.eds.enums.AppSourceEnum;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import com.qmth.eds.service.CommonCacheService;
+import com.qmth.eds.service.SysRoleService;
+import com.qmth.eds.service.TBSessionService;
+import com.qmth.eds.service.TeachcloudCommonService;
+import com.qmth.eds.util.AuthUtil;
+import com.qmth.eds.util.RedisUtil;
+import com.qmth.eds.util.ServletUtil;
+import com.qmth.eds.util.SessionUtil;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 公共服务service impl
+ */
+@Service
+public class TeachcloudCommonServiceImpl implements TeachcloudCommonService {
+
+    @Resource
+    CommonCacheService commonCacheService;
+
+
+    @Resource
+    TBSessionService tbSessionService;
+
+    @Resource
+    SysRoleService sysRoleService;
+
+    @Resource
+    RedisUtil redisUtil;
+
+    /**
+     * 登录公用
+     *
+     * @param password  密码
+     * @param sysUser   用户对象
+     * @param appSource 来源
+     */
+    @Override
+    public LoginResult login(String password, SysUser sysUser, AppSourceEnum appSource) throws NoSuchAlgorithmException {
+        Platform platform = ServletUtil.getRequestPlatform();
+        String deviceId = ServletUtil.getRequestDeviceId();
+        //生成token
+        String token = SystemConstant.getUuid();
+        commonCacheService.userCache(sysUser.getId());
+        //添加用户会话缓存
+        Set<String> roleNames = new HashSet<>();
+        List<SysRole> sysRoles = sysRoleService.listByUserId(sysUser.getId());
+        if (!sysRoles.isEmpty()) {
+            roleNames = sysRoles.stream().map(m -> m.getType().name()).collect(Collectors.toSet());
+        }
+        String sessionId = SessionUtil.digest(sysUser.getId(), Math.abs(roleNames.toString().hashCode()), platform.name());
+        // TODO 测试用
+//      String test = SignatureEntityTest.build(SignatureType.TOKEN, sessionId, token);
+        ExpireTimeBean expireTime = AuthUtil.getExpireTime(platform);
+        TBSession tbSession = new TBSession(sysUser.getSchoolId(), sessionId, String.valueOf(sysUser.getId()), roleNames.toString(),
+                platform.name(), platform.name(), deviceId, ServletUtil.getRequest().getLocalAddr(), token,
+                expireTime.getDate().getTime(), appSource);
+        tbSessionService.saveOrUpdate(tbSession);
+        redisUtil.setUserSession(sessionId, tbSession, expireTime.getExpireSeconds());
+
+//        LoginResult loginResult = new LoginResult(sysUser, sessionId, test, Objects.nonNull(roleTypes) && roleTypes.size() > 0 ? roleTypes : roleNames, appSource);
+        LoginResult loginResult = new LoginResult(sysUser, sessionId, token, roleNames, appSource);
+        loginResult.setTime(System.currentTimeMillis());
+        String mobileNumber = sysUser.getMobileNumber();
+        int pwdCount = sysUser.getPwdCount();
+        loginResult.setUserLoginCheckResult(new UserLoginCheckResult(sysUser.getId(), mobileNumber, pwdCount));
+        return loginResult;
+    }
+
+    /**
+     * 删除用户信息
+     *
+     * @param userId 用户ID
+     * @param all    all
+     */
+    @Override
+    public void removeUserInfo(Long userId, boolean all) throws NoSuchAlgorithmException {
+        AuthBean authBean = commonCacheService.userAuthCache(userId);
+        if (Objects.isNull(authBean)) {
+            throw ExceptionResultEnum.NOT_LOGIN.exception();
+        }
+        if (all) {
+            for (Platform p : Platform.values()) {
+                Set<String> roleNames = authBean.getRoleList().stream().map(SysRole::getName).collect(Collectors.toSet());
+                String sessionId = SessionUtil.digest(userId, Math.abs(roleNames.toString().hashCode()), p.name());
+                tbSessionService.removeById(sessionId);
+                redisUtil.deleteUserSession(sessionId);
+            }
+        } else {
+            TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
+            tbSessionService.removeById(tbSession.getId());
+            redisUtil.deleteUserSession(tbSession.getId());
+        }
+        commonCacheService.removeUserCache(userId);
+        commonCacheService.removeUserAuthCache(userId);
+        commonCacheService.removeUserMenuCache(userId);
+        commonCacheService.removeUserRolePrivilegeCache(userId);
+    }
+
+}

+ 9 - 3
src/main/java/com/qmth/eds/start/StartRunning.java

@@ -2,7 +2,7 @@ package com.qmth.eds.start;
 
 import com.qmth.eds.contant.SystemConstant;
 import com.qmth.eds.enums.JobEnum;
-import com.qmth.eds.job.service.TimedSyncSchoolJob;
+import com.qmth.eds.job.service.TimedSyncTaskJob;
 import com.qmth.eds.service.QuartzService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,13 +32,19 @@ public class StartRunning implements CommandLineRunner {
         log.info("服务器启动时执行 start");
         SystemConstant.initTempFiles();
 
-        log.info("增加学校信息同步定时任务 start");
+        /*log.info("增加学校信息同步定时任务 start");
         Map schoolJobMap = new HashMap();
         schoolJobMap.computeIfAbsent("name", v -> TimedSyncSchoolJob.class.getName());
         quartzService.deleteJob(JobEnum.TIMED_SYNC_SCHOOL_JOB.name(), JobEnum.TIMED_SYNC_SCHOOL_JOB.name());
         // 每天0点定时任务
         quartzService.addJob(TimedSyncSchoolJob.class, JobEnum.TIMED_SYNC_SCHOOL_JOB.name(), JobEnum.TIMED_SYNC_SCHOOL_JOB.name(), "0 0 0 * * ?", schoolJobMap);
-        log.info("增加学校信息同步定时任务 end");
+        log.info("增加学校信息同步定时任务 end");*/
+
+        Map schoolJobMap = new HashMap();
+        schoolJobMap.computeIfAbsent("name", v -> TimedSyncTaskJob.class.getName());
+        quartzService.deleteJob(JobEnum.TIMED_SYNC_TASK_JOB.name(), JobEnum.TIMED_SYNC_TASK_JOB.name());
+        // 每天0点定时任务
+        quartzService.addJob(TimedSyncTaskJob.class, JobEnum.TIMED_SYNC_TASK_JOB.name(), JobEnum.TIMED_SYNC_TASK_JOB.name(), "0 0 0 * * ?", schoolJobMap);
 
         log.info("服务器启动时执行 end");
     }

+ 74 - 0
src/main/java/com/qmth/eds/util/FileUtil.java

@@ -11,6 +11,7 @@ import javax.crypto.Cipher;
 import javax.crypto.CipherOutputStream;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.URL;
@@ -541,4 +542,77 @@ public class FileUtil {
             log.error(SystemConstant.LOG_ERROR, e);
         }
     }
+
+    /**
+     * 生成txt文件
+     *
+     * @param fileName 文件名
+     * @param list     内容集合
+     */
+    public static File writeTxt(String fileName, List<String> list) throws IOException {
+        File file = new File(fileName);
+        if (!file.exists()) {
+            file.createNewFile();
+        }
+        FileWriter write = null;
+        BufferedWriter bw = null;
+        try {
+            write = new FileWriter(file);
+            bw = new BufferedWriter(write);
+            for (String s : list) {
+                bw.write(s.trim());
+                bw.newLine();
+            }
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (bw != null) {
+                bw.close();
+            }
+            if (write != null) {
+                write.close();
+            }
+        }
+        return file;
+    }
+
+    /**
+     * 输出流
+     *
+     * @param response response
+     * @param file     源文件
+     * @param fileName 文件名
+     */
+    public static void outputFile(HttpServletResponse response, File file, String fileName) {
+        try {
+
+            if (!file.exists()) {
+                response.sendError(404, "File not found!");
+            }
+
+            BufferedInputStream br = new BufferedInputStream(new FileInputStream(file));
+            byte[] buf = new byte[1024];
+            int len = 0;
+
+            String fName = new String(fileName.getBytes(), "ISO-8859-1");
+
+            response.reset();
+            response.setContentType("application/x-msdownload");
+            response.setHeader("Content-Disposition", "attachment; filename=" + fName);
+
+            OutputStream outStream = response.getOutputStream();
+
+            while ((len = br.read(buf)) > 0) {
+                outStream.write(buf, 0, len);
+            }
+
+            br.close();
+            outStream.close();
+        } catch (FileNotFoundException e) {
+            log.error(SystemConstant.LOG_ERROR, e);
+        } catch (IOException e) {
+            log.error(SystemConstant.LOG_ERROR, e);
+        }
+    }
 }

+ 287 - 0
src/main/java/com/qmth/eds/util/HttpKit.java

@@ -0,0 +1,287 @@
+package com.qmth.eds.util;
+
+import com.qmth.eds.contant.SystemConstant;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.List;
+import java.util.Map;
+
+public class HttpKit {
+    private final static Logger log = LoggerFactory.getLogger(HttpKit.class);
+
+
+    /**
+     * 向指定URL发送GET方法的请求
+     * @param url:发送请求的URL
+     * @param params:请求参数,请求参数应该是 name1=value1&name2=value2&name3=value3 的形式。
+     * @return String[result]:所代表远程资源的响应结果
+     */
+    public static String sendGet(String url,  Map<String, String> params, Map<String, String> requestHeader) {
+        String result = "";
+        BufferedReader in = null;
+        try {
+            String urlNameString = url;
+            // 发送请求参数
+            if (params != null) {
+                StringBuilder param = new StringBuilder();
+                for (Map.Entry<String, String> entry : params.entrySet()) {
+                    if (param.length() > 0) {
+                        param.append("&");
+                    }
+                    param.append(entry.getKey());
+                    param.append("=");
+                    param.append(entry.getValue());
+                }
+                urlNameString = urlNameString.concat("?").concat(param.toString());
+            }
+            URL realUrl = new URL(urlNameString);
+
+            // 打开和URL之间的连接
+            URLConnection connection = realUrl.openConnection();
+
+            // 设置通用的请求属性
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+
+            if (requestHeader != null && requestHeader.size() > 0) {
+                for (Map.Entry<String, String> entry : requestHeader.entrySet()) {
+                    connection.setRequestProperty(entry.getKey(), entry.getValue());
+                }
+            }
+            // 建立实际的连接
+            connection.connect();
+
+            // 定义 BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(new InputStreamReader(
+                    connection.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+            }
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
+        }
+
+        // 使用finally块来关闭输入流
+        finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (Exception e2) {
+                throw ExceptionResultEnum.ERROR.exception(e2.getMessage());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url    发送请求的 URL
+     * @param params 请求的参数集合
+     * @return 远程资源的响应结果
+     */
+    @SuppressWarnings("unused")
+    public static String sendPost(String url, Map<String, String> params, Map<String, String> requestHeader) {
+        OutputStreamWriter out = null;
+        BufferedReader reader = null;
+        int responseCode = 200;
+        StringBuilder result = new StringBuilder();
+        try {
+            URL realUrl = new URL(url);
+            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
+            // 发送POST请求必须设置如下两行
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            // POST方法
+            connection.setRequestMethod("POST");
+            // 设置通用的请求属性
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            if (requestHeader != null && requestHeader.size() > 0) {
+                for (Map.Entry<String, String> entry : requestHeader.entrySet()) {
+                    connection.setRequestProperty(entry.getKey(), entry.getValue());
+                }
+            }
+            connection.connect();
+            // 获取URLConnection对象对应的输出流
+            out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
+            // 发送请求参数
+            if (params != null) {
+                StringBuilder param = new StringBuilder();
+                for (Map.Entry<String, String> entry : params.entrySet()) {
+                    if (param.length() > 0) {
+                        param.append("&");
+                    }
+                    param.append(entry.getKey());
+                    param.append("=");
+                    param.append(entry.getValue());
+                }
+                out.write(param.toString());
+            }
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            responseCode = connection.getResponseCode();
+            reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                result.append(line);
+            }
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception("调用接口异常:错误码[" + responseCode + "],错误原因[" + e.getMessage() + "]");
+        }
+        //使用finally块来关闭输出流、输入流
+        finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (IOException ex) {
+                log.error(SystemConstant.LOG_ERROR, ex);
+            }
+        }
+        return result.toString();
+    }
+
+
+    /**
+     * 发送post请求
+     *
+     * @param requestUrl       请求url
+     * @param requestHeader    请求头
+     * @param formTexts        表单数据
+     * @param files            上传文件
+     * @param requestEncoding  请求编码
+     * @param responseEncoding 响应编码
+     * @return 页面响应html
+     */
+    public static String sendPost(String requestUrl, Map<String, String> requestHeader, Map<String, Object> formTexts, Map<String, String> files, String requestEncoding, String responseEncoding) {
+        OutputStream out = null;
+        BufferedReader reader = null;
+        StringBuilder result = new StringBuilder();
+        int responseCode = 200;
+        try {
+            if (requestUrl == null || requestUrl.isEmpty()) {
+                return result.toString();
+            }
+            URL realUrl = new URL(requestUrl);
+            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
+            connection.setRequestProperty("accept", "text/html, application/xhtml+xml, image/jxr, */*");
+            connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0");
+            if (requestHeader != null && requestHeader.size() > 0) {
+                for (Map.Entry<String, String> entry : requestHeader.entrySet()) {
+                    connection.setRequestProperty(entry.getKey(), entry.getValue());
+                }
+            }
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            connection.setRequestMethod("POST");
+            if (requestEncoding == null || requestEncoding.isEmpty()) {
+                requestEncoding = "UTF-8";
+            }
+            if (responseEncoding == null || responseEncoding.isEmpty()) {
+                responseEncoding = "UTF-8";
+            }
+            if (files == null || files.size() == 0) {
+                connection.setRequestProperty("content-type", "application/x-www-form-urlencoded");
+                out = new DataOutputStream(connection.getOutputStream());
+                if (formTexts != null && formTexts.size() > 0) {
+                    String formData = "";
+                    for (Map.Entry<String, Object> entry : formTexts.entrySet()) {
+                        formData += entry.getKey() + "=" + entry.getValue() + "&";
+                    }
+                    formData = formData.substring(0, formData.length() - 1);
+                    out.write(formData.getBytes(requestEncoding));
+                }
+            } else {
+                String boundary = "-----------------------------" + System.currentTimeMillis();
+                connection.setRequestProperty("content-type", "multipart/form-data; boundary=" + boundary);
+                out = new DataOutputStream(connection.getOutputStream());
+                if (formTexts != null && formTexts.size() > 0) {
+                    StringBuilder sbFormData = new StringBuilder();
+                    for (Map.Entry<String, Object> entry : formTexts.entrySet()) {
+                        sbFormData.append("--" + boundary + "\r\n");
+                        sbFormData.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n");
+                        sbFormData.append(entry.getValue() + "\r\n");
+                    }
+                    out.write(sbFormData.toString().getBytes(requestEncoding));
+                }
+                for (Map.Entry<String, String> entry : files.entrySet()) {
+                    String fileName = entry.getKey();
+                    String filePath = entry.getValue();
+                    if (fileName == null || fileName.isEmpty() || filePath == null || filePath.isEmpty()) {
+                        continue;
+                    }
+                    File file = new File(filePath);
+                    if (!file.exists()) {
+                        continue;
+                    }
+                    out.write(("--" + boundary + "\r\n").getBytes(requestEncoding));
+                    out.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"\r\n").getBytes(requestEncoding));
+                    out.write(("Content-Type: application/octet-stream\r\n\r\n").getBytes(requestEncoding));
+                    DataInputStream in = new DataInputStream(new FileInputStream(file));
+                    int bytes = 0;
+                    byte[] bufferOut = new byte[1024];
+                    while ((bytes = in.read(bufferOut)) != -1) {
+                        out.write(bufferOut, 0, bytes);
+                    }
+                    in.close();
+                    out.write(("\r\n").getBytes(requestEncoding));
+                }
+                out.write(("--" + boundary + "--").getBytes(requestEncoding));
+            }
+            out.flush();
+            out.close();
+
+
+            responseCode = connection.getResponseCode();
+            if (responseCode >= 400) {
+//                reader = new BufferedReader(new InputStreamReader(connection.getErrorStream(), responseEncoding));
+                Map<String, List<String>> responseProperties = connection.getHeaderFields();
+                if (responseProperties.containsKey("error-info")) {
+                    List<String> strings = responseProperties.get("error-info");
+                    if (!strings.isEmpty()) {
+                        String message = String.join(";", strings);
+                        throw ExceptionResultEnum.ERROR.exception(message);
+                    }
+                }
+                throw ExceptionResultEnum.ERROR.exception("接口调用错误");
+            } else {
+                reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), responseEncoding));
+            }
+            String line;
+            while ((line = reader.readLine()) != null) {
+                result.append(line);
+            }
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception("调用接口异常:错误码[" + responseCode + "],错误原因[" + e.getMessage() + "]");
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (IOException ex) {
+                log.error(SystemConstant.LOG_ERROR, ex);
+            }
+        }
+        return result.toString();
+    }
+
+}

+ 8 - 0
src/main/java/com/qmth/eds/util/ServletUtil.java

@@ -144,6 +144,14 @@ public class ServletUtil {
         return object;
     }
 
+    public static Long getRequestUserId() {
+        Object object = getRequest().getHeader("userId");
+        if (Objects.isNull(object)) {
+            throw ExceptionResultEnum.NOT_LOGIN.exception();
+        }
+        return Long.parseLong(object.toString());
+    }
+
     /**
      * 获取header中schoolId
      *

+ 118 - 0
src/main/java/com/qmth/eds/util/WuhanUniversityUtils.java

@@ -0,0 +1,118 @@
+package com.qmth.eds.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.qmth.eds.config.DictionaryConfig;
+import com.qmth.eds.entity.ExamSyncStudentTemp;
+import com.qmth.eds.enums.ExceptionResultEnum;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 武汉大学工具类
+ */
+@Component
+public class WuhanUniversityUtils {
+
+    @Resource
+    private DictionaryConfig dictionaryConfig;
+
+    /**
+     * 获取token
+     */
+    public String getAccessToken() {
+        String appKey = dictionaryConfig.whuDomain().getAppKey();
+        String appSecret = dictionaryConfig.whuDomain().getAppSecret();
+        String tokenUrl = dictionaryConfig.whuDomain().getTokenUrl();
+        validUrl(appKey, appSecret, tokenUrl);
+        try {
+            //参数
+            Map<String, String> paramMap = new HashMap<>();
+            paramMap.put("appKey", appKey);
+            paramMap.put("appSecret", appSecret);
+
+            String result = HttpKit.sendPost(tokenUrl, paramMap, null);
+            JSONObject jsonResult = JSON.parseObject(result);
+            if (jsonResult.containsKey("status") && "success".equals(jsonResult.getString("status"))) {
+                String token = jsonResult.getString("token");
+                JSONObject jsonToken = JSON.parseObject(token);
+                if (jsonToken.containsKey("accessToken")) {
+                    return jsonToken.getString("accessToken");
+                }
+            }
+            return null;
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
+        }
+    }
+
+    /**
+     * 获取考务数据
+     */
+    public List<ExamSyncStudentTemp> getKwData(String token) {
+        String kwUrl = dictionaryConfig.whuDomain().getKwUrl();
+        validUrl(kwUrl);
+        try {
+            //参数
+            Map<String, String> headerMap = new HashMap<>();
+            headerMap.put("Authorization", "Bearer " + token);
+
+            String result = HttpKit.sendGet(kwUrl, null, headerMap);
+            List<ExamSyncStudentTemp> examSyncStudentTemps = null;
+            JSONObject jsonObject = JSON.parseObject(result);
+            if (jsonObject.containsKey("status") && jsonObject.getBoolean("status")) {
+                String data = jsonObject.getString("data");
+                examSyncStudentTemps = JSONObject.parseArray(data, ExamSyncStudentTemp.class);
+            }
+            return examSyncStudentTemps;
+//            return listTemps();
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
+        }
+    }
+
+    /**
+     * 校验url是否配置
+     *
+     * @param urls URL数组
+     */
+    private void validUrl(String... urls) {
+        if (StringUtils.isAnyBlank(urls)) {
+            throw ExceptionResultEnum.ERROR.exception("接口参数未配置");
+        }
+    }
+
+    private List<ExamSyncStudentTemp> listTemps() {
+        List<ExamSyncStudentTemp> examSyncStudentTemps = new ArrayList<>();
+        for (int i = 0; i < 4; i++) {
+            ExamSyncStudentTemp examSyncStudentTemp = new ExamSyncStudentTemp();
+            examSyncStudentTemp.setXnm("100" + i);
+            examSyncStudentTemp.setXqm("200" + i);
+            examSyncStudentTemp.setJxbId("300" + i);
+            examSyncStudentTemp.setJxbmc("教学班" + i);
+            examSyncStudentTemp.setKch("400" + i);
+            examSyncStudentTemp.setKcmc("课程" + i);
+            examSyncStudentTemp.setJgh("500" + i);
+            examSyncStudentTemp.setXm("姓名" + i);
+            examSyncStudentTemp.setKkbm("开课学院" + i);
+            examSyncStudentTemp.setXf("35");
+            examSyncStudentTemp.setXh("100000100" + i);
+            examSyncStudentTemp.setXsxm("学生" + i);
+            examSyncStudentTemp.setJgmc("机构" + i);
+            examSyncStudentTemp.setZymc("专业" + i);
+            examSyncStudentTemp.setCxbj("0");
+            examSyncStudentTemp.setNjdmId("年级" + i);
+            examSyncStudentTemp.setZwh("1" + i);
+            examSyncStudentTemp.setCdmc("考场" + i);
+            examSyncStudentTemp.setKsbz("无");
+            examSyncStudentTemps.add(examSyncStudentTemp);
+        }
+        return examSyncStudentTemps;
+    }
+}

+ 8 - 2
src/main/resources/application-dev.properties

@@ -14,7 +14,7 @@ db.host=localhost
 db.port=3306
 db.name=eds-v1.0.0
 db.username=root
-db.password=123456789
+db.password=root
 
 #redis\u6570\u636E\u6E90\u914D\u7F6E
 com.qmth.redis.host=${db.host}
@@ -109,4 +109,10 @@ spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThre
 # \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
+spring.quartz.properties.org.quartz.threadPool.threadPriority=5
+
+
+whu.config.appKey=DD1198B44CD624B6E0530107010AF5DF
+whu.config.appSecret=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAojkSbVqi/6v7hNdHZIPU4EKLyyxO7akNAIXilJ0+c0sYHV4Z6vctSrds4LIPp3fXUuMA9F7d1vp6s7HOQfuIfwIDAQABAkBiCowQew635oEEL/d90A7/2jgSepzZFao9/qyJlN8BXg8M6zwBFiEB51GbW0642bWTftFT0Arrq6ipYkXTyH7BAiEA8oMCG7HqUYXX4bt6h9Bz8LIwI4oFx3x90RpK+b0iPmMCIQCrPuAZB90EJtr4fOE3+lQBORoKVOF/cwFQX14mBXRKNQIhAKLeVVYgQMwLZZcWgA4icxsdsLLNiWAWPbFnRR9AZHUdAiA5tGJHjQLJF17KRSqPdmdAUkGYIaorDFHxB3HuVgAt0QIgIMcILJ3lJx6URAEG9e055KXh8J+nXItScHZ4bXMWls4=
+whu.config.tokenUrl=http://120.76.177.81/zfdsb/oauth2/accessToken
+whu.config.kwUrl=http://120.76.177.81/zfdsb/api/kw/ksmd

+ 36 - 0
src/main/resources/mapper/ExamDownloadRecordMapper.xml

@@ -0,0 +1,36 @@
+<?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.eds.mapper.ExamDownloadRecordMapper">
+    <select id="list" resultType="com.qmth.eds.bean.dto.ExamDownloadRecordDto">
+        SELECT
+            a.id,
+            a.operate_time operateTime,
+            a.user_id userId,
+            a.user_name userName,
+            b.school_name schoolName,
+            b.semester_name semesterName,
+            b.exam_type_name examTypeName,
+            b.file_name fileName,
+            b.file_path filePath,
+            b.colleges,
+            b.subjects,
+            b.students,
+            a.used,
+            a.use_scene useScene
+        FROM
+            exam_download_record a
+                JOIN
+            exam_sync_total b ON a.sync_total_id = b.id
+        <where>
+            <if test="operateDate != null and operateDate != ''">
+                and a.operate_time = #{operateDate}
+            </if>
+            <if test="used != null">
+                and a.used = #{used}
+            </if>
+            <if test="operateDate != null and operateDate != ''">
+                and a.operate_time like concat('%',#{operateDate}, '%')
+            </if>
+        </where>
+    </select>
+</mapper>

+ 21 - 0
src/main/resources/mapper/ExamScheduleTaskMapper.xml

@@ -0,0 +1,21 @@
+<?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.eds.mapper.ExamScheduleTaskMapper">
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.qmth.eds.entity.ExamScheduleTask">
+        <result column="id" property="id"/>
+        <result column="school_id" property="schoolId"/>
+        <result column="semester_id" property="semesterId"/>
+        <result column="semester_name" property="semesterName"/>
+        <result column="exam_type_id" property="examTypeId"/>
+        <result column="exam_type_name" property="examTypeName"/>
+        <result column="start_time" property="startTime"/>
+        <result column="end_time" property="endTime"/>
+        <result column="create_id" property="createId"/>
+        <result column="create_time" property="createTime"/>
+    </resultMap>
+
+    <select id="findBySchoolIdAndSemesterIdAndExamTypeId" resultMap="BaseResultMap">
+        select * from exam_schedule_task where school_id = #{schoolId} and semester_id = #{semesterId} and exam_type_id = #{examTypeId}
+    </select>
+</mapper>

+ 4 - 0
src/main/resources/mapper/ExamSemesterMapper.xml

@@ -0,0 +1,4 @@
+<?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.eds.mapper.ExamSemesterMapper">
+</mapper>

+ 4 - 0
src/main/resources/mapper/ExamSyncStudentMapper.xml

@@ -0,0 +1,4 @@
+<?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.eds.mapper.ExamSyncStudentMapper">
+</mapper>

+ 4 - 0
src/main/resources/mapper/ExamSyncStudentTempMapper.xml

@@ -0,0 +1,4 @@
+<?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.eds.mapper.ExamSyncStudentTempMapper">
+</mapper>

+ 4 - 0
src/main/resources/mapper/ExamSyncTotalMapper.xml

@@ -0,0 +1,4 @@
+<?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.eds.mapper.ExamSyncTotalMapper">
+</mapper>

+ 4 - 0
src/main/resources/mapper/ExamTypeMapper.xml

@@ -0,0 +1,4 @@
+<?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.eds.mapper.ExamTypeMapper">
+</mapper>

+ 22 - 0
src/main/resources/mapper/SysRoleMapper.xml

@@ -2,4 +2,26 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.qmth.eds.mapper.SysRoleMapper">
 
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.qmth.eds.entity.SysRole">
+        <result column="id" property="id"/>
+        <result column="school_id" property="schoolId"/>
+        <result column="org_id" property="orgId"/>
+        <result column="name" property="name"/>
+        <result column="enable" property="enable"/>
+        <result column="type" property="type"/>
+        <result column="create_id" property="createId"/>
+        <result column="create_time" property="createTime"/>
+    </resultMap>
+
+    <select id="listByUserId" resultMap="BaseResultMap">
+        SELECT
+            a.*
+        FROM
+            sys_role a
+                JOIN
+            sys_user_role b ON a.id = b.role_id
+        WHERE
+            b.user_id = #{userId}
+    </select>
 </mapper>

+ 20 - 0
src/main/resources/mapper/SysUserMapper.xml

@@ -2,4 +2,24 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.qmth.eds.mapper.SysUserMapper">
 
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.qmth.eds.entity.SysUser">
+        <result column="id" property="id"/>
+        <result column="school_id" property="schoolId"/>
+        <result column="login_name" property="loginName"/>
+        <result column="real_name" property="realName"/>
+        <result column="password" property="password"/>
+        <result column="mobile_number" property="mobileNumber"/>
+        <result column="org_id" property="orgId"/>
+        <result column="enable" property="enable"/>
+        <result column="pwd_count" property="pwdCount"/>
+        <result column="pwd_update_time" property="pwdUpdateTime"/>
+        <result column="remark" property="remark"/>
+        <result column="create_id" property="createId"/>
+        <result column="create_time" property="createTime"/>
+    </resultMap>
+
+    <select id="findByMobileNumber" resultMap="BaseResultMap">
+        select * from sys_user where mobile_number = #{mobileNumber}
+    </select>
 </mapper>

+ 44 - 0
src/test/java/com/qmth/eds/WhuTest.java

@@ -0,0 +1,44 @@
+package com.qmth.eds;
+
+import com.qmth.eds.entity.ExamSyncStudent;
+import com.qmth.eds.entity.ExamSyncStudentTemp;
+import com.qmth.eds.job.service.JobService;
+import com.qmth.eds.util.ServletUtil;
+import com.qmth.eds.util.WuhanUniversityUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+/**
+ * 武汉大学测试类
+ */
+@SpringBootTest
+@RunWith(SpringRunner.class)
+public class WhuTest {
+
+    @Resource
+    WuhanUniversityUtils wuhanUniversityUtils;
+
+    @Resource
+    JobService jobService;
+
+    @Test
+    public void test() {
+        String token = wuhanUniversityUtils.getAccessToken();
+
+        List<ExamSyncStudentTemp> kwData = wuhanUniversityUtils.getKwData(token);
+        System.out.println(kwData);
+
+    }
+
+    @Test
+    public void testKw(){
+        jobService.getExamData();
+    }
+
+}