浏览代码

新增同步云阅卷成绩接口

wangliang 2 年之前
父节点
当前提交
ab55f105b6
共有 29 个文件被更改,包括 685 次插入29 次删除
  1. 1 1
      pom.xml
  2. 2 2
      themis-admin/pom.xml
  3. 103 5
      themis-admin/src/main/java/com/qmth/themis/admin/api/TEOpenController.java
  4. 11 0
      themis-admin/src/main/java/com/qmth/themis/admin/config/DictionaryConfig.java
  5. 41 0
      themis-admin/src/main/java/com/qmth/themis/admin/config/SSLConfig.java
  6. 3 2
      themis-admin/src/main/java/com/qmth/themis/admin/config/WebMvcConfig.java
  7. 15 2
      themis-admin/src/main/java/com/qmth/themis/admin/interceptor/AuthThirdInterceptor.java
  8. 12 0
      themis-admin/src/main/resources/application.properties
  9. 二进制
      themis-admin/src/main/resources/online.jks
  10. 2 2
      themis-business/pom.xml
  11. 13 2
      themis-business/src/main/java/com/qmth/themis/business/constant/SystemConstant.java
  12. 42 0
      themis-business/src/main/java/com/qmth/themis/business/domain/YunMarkDomain.java
  13. 2 1
      themis-business/src/main/java/com/qmth/themis/business/enums/MqTagEnum.java
  14. 3 1
      themis-business/src/main/java/com/qmth/themis/business/enums/TaskTypeEnum.java
  15. 187 0
      themis-business/src/main/java/com/qmth/themis/business/templete/TaskSyncCommon.java
  16. 24 0
      themis-business/src/main/java/com/qmth/themis/business/templete/TaskSyncTemplete.java
  17. 77 0
      themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskSyncMarkCloudTemplete.java
  18. 95 0
      themis-business/src/main/java/com/qmth/themis/business/util/CloudMarkUtil.java
  19. 1 1
      themis-business/src/main/java/com/qmth/themis/business/util/HttpUtil.java
  20. 2 2
      themis-common/pom.xml
  21. 2 2
      themis-exam/pom.xml
  22. 11 0
      themis-exam/src/main/java/com/qmth/themis/exam/config/DictionaryConfig.java
  23. 4 0
      themis-exam/src/main/resources/application.properties
  24. 2 2
      themis-mq/pom.xml
  25. 11 0
      themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java
  26. 2 2
      themis-task/pom.xml
  27. 11 0
      themis-task/src/main/java/com/qmth/themis/task/config/DictionaryConfig.java
  28. 1 1
      themis-task/src/main/java/com/qmth/themis/task/start/StartRunning.java
  29. 5 1
      themis-task/src/main/resources/application.properties

+ 1 - 1
pom.xml

@@ -4,7 +4,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.themis</groupId>
     <artifactId>themis-service</artifactId>
-    <version>1.1.3</version>
+    <version>1.2.0</version>
     <packaging>pom</packaging>
 
     <modules>

+ 2 - 2
themis-admin/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.themis.admin</groupId>
     <artifactId>themis-admin</artifactId>
-    <version>1.1.3</version>
+    <version>1.2.0</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.themis</groupId>
         <artifactId>themis-service</artifactId>
-        <version>1.1.3</version>
+        <version>1.2.0</version>
     </parent>
 
     <dependencies>

+ 103 - 5
themis-admin/src/main/java/com/qmth/themis/admin/api/TEOpenController.java

@@ -2,10 +2,25 @@ package com.qmth.themis.admin.api;
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.qmth.themis.business.service.TEOpenService;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qmth.themis.business.cache.bean.ExamCacheBean;
+import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.dto.MqDto;
+import com.qmth.themis.business.entity.TBTaskHistory;
+import com.qmth.themis.business.entity.TBUser;
+import com.qmth.themis.business.entity.TEExamCourse;
+import com.qmth.themis.business.enums.*;
+import com.qmth.themis.business.service.*;
+import com.qmth.themis.business.util.MqUtil;
+import com.qmth.themis.business.util.ServletUtil;
+import com.qmth.themis.common.enums.ExceptionResultEnum;
 import com.qmth.themis.common.exception.BusinessException;
+import com.qmth.themis.common.util.Result;
+import com.qmth.themis.common.util.ResultUtil;
 import io.swagger.annotations.*;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -13,6 +28,8 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 开放接口
@@ -25,13 +42,29 @@ import java.io.IOException;
 @RestController
 @RequestMapping("/${prefix.url.open}")
 public class TEOpenController {
+    private final static Logger log = LoggerFactory.getLogger(TEOpenController.class);
 
     @Resource
     private TEOpenService openService;
 
+    @Resource
+    TEExamService teExamService;
+
+    @Resource
+    MqDtoService mqDtoService;
+
+    @Resource
+    TEExamCourseService teExamCourseService;
+
+    @Resource
+    TBTaskHistoryService taskHistoryService;
+
+    @Resource
+    MqUtil mqUtil;
+
     @ApiOperation(value = "获取考试详情")
     @RequestMapping(value = "/exam/query", method = RequestMethod.POST)
-    @ApiResponses({ @ApiResponse(code = 200, message = "结果信息") })
+    @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
     public Object examQueryPage(@ApiParam(value = "考试id", required = false) @RequestParam(required = false) Long id,
                                 @ApiParam(value = "考试code", required = false) @RequestParam(required = false) String code,
                                 @ApiParam(value = "分页页码", required = false) @RequestParam(required = false) Integer pageNumber,
@@ -47,7 +80,7 @@ public class TEOpenController {
 
     @ApiOperation(value = "获取考试课程详情")
     @RequestMapping(value = "/exam/course/query", method = RequestMethod.POST)
-    @ApiResponses({ @ApiResponse(code = 200, message = "结果信息") })
+    @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
     public Object examCourseQueryPage(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                       @ApiParam(value = "课程code", required = false) @RequestParam(required = false) String courseCode,
                                       @ApiParam(value = "是否有试卷", required = false) @RequestParam(required = false) Boolean hasPaper,
@@ -68,7 +101,7 @@ public class TEOpenController {
 
     @ApiOperation(value = "获取考试试卷详情")
     @RequestMapping(value = "/exam/paper/detail", method = RequestMethod.POST)
-    @ApiResponses({ @ApiResponse(code = 200, message = "结果信息") })
+    @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
     public JSONObject examPaperDetail(@ApiParam(value = "试卷id", required = true) @RequestParam Long id,
                                       @ApiParam(value = "内容过滤", required = false) @RequestParam(required = false) String filter)
             throws IOException {
@@ -81,7 +114,7 @@ public class TEOpenController {
 
     @ApiOperation(value = "待评卷考试记录查询")
     @RequestMapping(value = "/exam/record/need_mark", method = RequestMethod.POST)
-    @ApiResponses({ @ApiResponse(code = 200, message = "结果信息") })
+    @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
     public JSONArray examRecordNeedMark(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                         @ApiParam(value = "课程代码", required = false) @RequestParam(required = false) String courseCode,
                                         @ApiParam(value = "考生ID大于此参数", required = false) @RequestParam(required = false) Long examStudentIdGt,
@@ -94,4 +127,69 @@ public class TEOpenController {
         }
         return openService.examRecordNeedMark(examId, StringUtils.trimToNull(courseCode), examStudentIdGt, count);
     }
+
+    @ApiOperation(value = "成绩查询同步")
+    @ApiResponses({@ApiResponse(code = 200, message = "成绩查询同步信息", response = Result.class)})
+    @RequestMapping(value = "/score/sync", method = RequestMethod.POST)
+    public Result scoreSync(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
+                            @ApiParam(value = "课程编码", required = false) @RequestParam(required = false) String courseCode) {
+        TBTaskHistory tbTaskHistory = null;
+        try {
+            ExamCacheBean examCacheBean = teExamService.getExamCacheBean(Long.parseLong(examId));
+            Optional.ofNullable(examCacheBean).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));
+            InvigilateMonitorStatusEnum invigilateMonitorStatusEnum = examCacheBean.getMonitorStatus();//监考状态
+            if (Objects.nonNull(invigilateMonitorStatusEnum) && !Objects
+                    .equals(invigilateMonitorStatusEnum, InvigilateMonitorStatusEnum.FINISHED)) {
+                throw new BusinessException("当前批次监考未结束,请在结束后操作");
+            }
+            ScoreStatusEnum scoreStatusEnum = examCacheBean.getScoreStatus();//算分状态
+            if (Objects.nonNull(scoreStatusEnum) && (Objects.equals(scoreStatusEnum, ScoreStatusEnum.CALCULATING) || Objects
+                    .equals(scoreStatusEnum, ScoreStatusEnum.NEED_CALCULATE))) {
+                throw new BusinessException("当前批次算分未结束,请在结束后操作");
+            }
+            QueryWrapper<TEExamCourse> teExamCourseQueryWrapper = new QueryWrapper<>();
+            teExamCourseQueryWrapper.lambda().eq(TEExamCourse::getExamId, examId);
+            List<TEExamCourse> teExamCourseList = teExamCourseService.list(teExamCourseQueryWrapper);
+            Set answerList = null;
+            if (Objects.nonNull(teExamCourseList) && teExamCourseList.size() > 0) {
+                answerList = teExamCourseList.stream().filter(s -> {
+                    if (Objects.nonNull(s.getHasAnswer()) && s.getHasAnswer().intValue() == 0) {
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }).collect(Collectors.toSet());
+            }
+            if (Objects.nonNull(answerList) && answerList.size() > 0) {
+                throw new BusinessException("当前批次标答未补齐,请补齐后操作");
+            }
+
+            Map transMap = new HashMap();
+            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
+            //往任务表里插一条数据
+            tbTaskHistory = new TBTaskHistory(TaskTypeEnum.SCORE_PUSH, TaskStatusEnum.INIT,
+                    SystemConstant.SCORE_PUSH_INIT, 0d, tbUser.getId(), tbUser.getOrgId());
+            tbTaskHistory.setExamId(Long.parseLong(examId));
+            taskHistoryService.save(tbTaskHistory);
+
+            transMap.put("tbTaskHistory", tbTaskHistory);
+            transMap.put(SystemConstant.CREATE_ID, tbUser.getId());
+            transMap.put(SystemConstant.ORG_ID, tbUser.getOrgId());
+            transMap.put(SystemConstant.EXAM_ID, Long.parseLong(examId));
+            transMap.put(SystemConstant.EXAM_CODE, examCacheBean.getCode());
+            //mq发送消息start
+            MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.MARK_CLOUD_SCORE_PUSH.name(),
+                    transMap, MqTagEnum.MARK_CLOUD_SCORE_PUSH, String.valueOf(tbTaskHistory.getId()),
+                    tbUser.getName());
+            mqDtoService.assembleSendOneWayMsg(mqDto);
+        } catch (Exception e) {
+            log.error(SystemConstant.LOG_ERROR, e);
+            if (e instanceof BusinessException) {
+                throw new BusinessException(e.getMessage());
+            } else {
+                throw new RuntimeException(e);
+            }
+        }
+        return ResultUtil.ok(Collections.singletonMap(SystemConstant.TASK_ID, tbTaskHistory.getId()));
+    }
 }

+ 11 - 0
themis-admin/src/main/java/com/qmth/themis/admin/config/DictionaryConfig.java

@@ -113,4 +113,15 @@ public class DictionaryConfig {
     public MonitorDomain monitorDomain() {
         return new MonitorDomain();
     }
+
+    /**
+     * 同步云阅卷配置
+     *
+     * @return
+     */
+    @Bean
+    @ConfigurationProperties(prefix = "yun.mark", ignoreUnknownFields = false)
+    public YunMarkDomain yunMarkDomain() {
+        return new YunMarkDomain();
+    }
 }

+ 41 - 0
themis-admin/src/main/java/com/qmth/themis/admin/config/SSLConfig.java

@@ -0,0 +1,41 @@
+//package com.qmth.themis.admin.config;
+//
+//import org.apache.catalina.Context;
+//import org.apache.catalina.connector.Connector;
+//import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+//import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+//import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+//import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//
+//@Configuration
+//public class SSLConfig {
+//
+//    @Bean
+//    public ServletWebServerFactory servletWebServerFactory() {
+//        TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory() {
+//            @Override
+//            protected void postProcessContext(Context context) {
+//                SecurityConstraint securityConstraint = new SecurityConstraint();
+//                securityConstraint.setUserConstraint("CONFIDENTIAL");
+//                SecurityCollection securityCollection = new SecurityCollection();
+//                securityCollection.addPattern("/*");
+//                securityConstraint.addCollection(securityCollection);
+//                context.addConstraint(securityConstraint);
+//            }
+//        };
+//        serverFactory.addAdditionalTomcatConnectors(initiateHttpConnector());
+//        return serverFactory;
+//    }
+//
+//    private Connector initiateHttpConnector() {
+//        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
+//        connector.setScheme("http");
+//        connector.setPort(8080);
+//        connector.setSecure(false);
+//        connector.setRedirectPort(8443);
+//        return connector;
+//    }
+//}
+//

+ 3 - 2
themis-admin/src/main/java/com/qmth/themis/admin/config/WebMvcConfig.java

@@ -37,8 +37,9 @@ public class WebMvcConfig implements WebMvcConfigurer {
     public void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(AuthInterceptor()).addPathPatterns(SystemConstant.ADMIN_ALL_PATH)
                 .excludePathPatterns(dictionaryConfig.authNoUrlDomain().getUrls());
-        registry.addInterceptor(AuthThirdInterceptor()).addPathPatterns(SystemConstant.THIRD_ALL_PATH)
-                .excludePathPatterns(dictionaryConfig.authNoUrlDomain().getUrls());
+        registry.addInterceptor(AuthThirdInterceptor()).addPathPatterns(SystemConstant.SCORE_PUSH_PATH)
+                .excludePathPatterns(dictionaryConfig.authNoUrlDomain().getUrls())
+                .excludePathPatterns(SystemConstant.THIRD_ALL_PATH_LIST);
     }
 
     //    @Override

+ 15 - 2
themis-admin/src/main/java/com/qmth/themis/admin/interceptor/AuthThirdInterceptor.java

@@ -1,13 +1,18 @@
 package com.qmth.themis.admin.interceptor;
 
+import com.qmth.themis.admin.config.DictionaryConfig;
 import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.util.AuthUtil;
+import com.qmth.themis.business.util.ServletUtil;
 import com.qmth.themis.common.enums.ExceptionResultEnum;
+import com.qmth.themis.common.enums.Platform;
 import com.qmth.themis.common.exception.BusinessException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -21,6 +26,9 @@ import javax.servlet.http.HttpServletResponse;
 public class AuthThirdInterceptor implements HandlerInterceptor {
     private final static Logger log = LoggerFactory.getLogger(AuthThirdInterceptor.class);
 
+    @Resource
+    DictionaryConfig dictionaryConfig;
+
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
         log.info("admin authThirdInterceptor preHandle is come in");
@@ -29,8 +37,13 @@ public class AuthThirdInterceptor implements HandlerInterceptor {
         if (url.equalsIgnoreCase(SystemConstant.ERROR)) {
             throw new BusinessException(ExceptionResultEnum.NOT_FOUND);
         }
-        response.setStatus(ExceptionResultEnum.SUCCESS.getCode());
-        return true;
+        Platform platform = ServletUtil.getRequestPlatform();
+        String deviceId = ServletUtil.getRequestDeviceId();
+        String authorization = ServletUtil.getRequestAuthorization();
+        Long time = ServletUtil.getRequestTime();
+        log.info("Start authorization: url:{}, method:{}, platform:{}, deviceId:{}, authorization:{}, time:{}", url,
+                method, platform, deviceId, authorization, time);
+        return AuthUtil.adminAuthInterceptor(platform, deviceId, authorization, time, dictionaryConfig.systemUrlDomain().getUrls(), request, response);
     }
 
     @Override

+ 12 - 0
themis-admin/src/main/resources/application.properties

@@ -1,5 +1,13 @@
 #\u7AEF\u53E3\u914D\u7F6E
 server.port=6001
+#server.port=8443
+#server.ssl.protocol=TLS
+#server.ssl.key-store=classpath:online.jks
+#server.ssl.key-store-password=123456
+#server.ssl.key-store-type=JKS
+#server.ssl.enabled=true
+#server.ssl.key-alias=tomcat
+
 #tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200
 server.tomcat.threads.max=250
 #tomcat\u6700\u5927\u8FDE\u63A5\u6570
@@ -152,6 +160,10 @@ sys.config.serverHost=localhost:6001
 sys.config.adminLogoUrl=http://qmth-test.oss-cn-shenzhen.aliyuncs.com/frontend/wx_logo.png
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 
+#\u4E91\u9605\u5377\u540C\u6B65\u914D\u7F6E
+yun.mark.url=http://localhost:8081
+yun.mark.studentScoreApi=/api/exam/student/score
+
 #============================================================================
 # \u914D\u7F6Erocketmq
 #============================================================================

二进制
themis-admin/src/main/resources/online.jks


+ 2 - 2
themis-business/pom.xml

@@ -5,13 +5,13 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>com.qmth.themis.business</groupId>
 	<artifactId>themis-business</artifactId>
-	<version>1.1.3</version>
+	<version>1.2.0</version>
 	<packaging>jar</packaging>
 
 	<parent>
 		<groupId>com.qmth.themis</groupId>
 		<artifactId>themis-service</artifactId>
-		<version>1.1.3</version>
+		<version>1.2.0</version>
 	</parent>
 
 	<dependencies>

+ 13 - 2
themis-business/src/main/java/com/qmth/themis/business/constant/SystemConstant.java

@@ -15,7 +15,6 @@ import com.qmth.themis.common.enums.ExceptionResultEnum;
 import com.qmth.themis.common.enums.Platform;
 import com.qmth.themis.common.enums.Source;
 import com.qmth.themis.common.exception.BusinessException;
-import org.checkerframework.checker.units.qual.A;
 
 import java.io.File;
 import java.security.NoSuchAlgorithmException;
@@ -98,6 +97,7 @@ public class SystemConstant {
     /**
      * 系统相关
      */
+    public static final String METHOD = "post";
     public static final int PAGE_SIZE_MIN = 10;
     public static final int PAGE_SIZE_MAX = 1000;
     public static final int SELECT_PAGE_SIZE_MAX = 1000000;
@@ -106,6 +106,8 @@ public class SystemConstant {
 
     public static final String EXAM_ID = "examId";
 
+    public static final String EXAM_CODE = "examCode";
+
     public static final String ORG_ID = "orgId";
 
     public static final String REMARK = "remark";
@@ -144,6 +146,8 @@ public class SystemConstant {
 
     public static final String EXPORT_INIT = "准备开始处理导出数据";
 
+    public static final String SCORE_PUSH_INIT = "准备同步云阅卷成绩数据";
+
     public static final String RECORD_ID = "recordId";
 
     public static final String STUDENT_ID = "studentId";
@@ -168,7 +172,14 @@ public class SystemConstant {
 
     public static final String ADMIN_ALL_PATH = "/api/admin/**";
 
-    public static final String THIRD_ALL_PATH = "/api/open/**";
+//    public static final String THIRD_ALL_PATH = "/api/open/**";
+
+    public static final String SCORE_PUSH_PATH = "/api/open/score/sync";
+
+    public static final List<String> THIRD_ALL_PATH_LIST = Arrays.asList("/api/open/exam/course/query",
+            "/api/open/exam/paper/detail",
+            "/api/open/exam/record/need_mark",
+            "/api/open/exam/query");
 
     public static final String ACCOUNT = "account";
 

+ 42 - 0
themis-business/src/main/java/com/qmth/themis/business/domain/YunMarkDomain.java

@@ -0,0 +1,42 @@
+package com.qmth.themis.business.domain;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 同步云阅卷参数
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2022/7/13
+ */
+public class YunMarkDomain implements Serializable {
+
+    private String url;
+
+    private String studentScoreApi;
+
+    public YunMarkDomain() {
+
+    }
+
+    public YunMarkDomain(String url, String studentScoreApi) {
+        this.url = url;
+        this.studentScoreApi = studentScoreApi;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getStudentScoreApi() {
+        return studentScoreApi;
+    }
+
+    public void setStudentScoreApi(String studentScoreApi) {
+        this.studentScoreApi = studentScoreApi;
+    }
+}

+ 2 - 1
themis-business/src/main/java/com/qmth/themis/business/enums/MqTagEnum.java

@@ -54,7 +54,8 @@ public enum MqTagEnum {
     MONITOR_STOP("监控结束标签", "监控结束","broadcast", 46),
     EXAM_START("考试移动端监控开始标签", "考试移动端开始", "broadcast", 47),
     OE_WEBSOCKET_MOBILE_MONITOR_STATUS("通知客户端移动端当前监控状态标签", "通知客户端移动端当前监控状态", "broadcast", 48),
-    TENCENT_VIDEO("腾讯云视频回调标签", "腾讯云视频回调", "delay", 49);
+    TENCENT_VIDEO("腾讯云视频回调标签", "腾讯云视频回调", "delay", 49),
+    MARK_CLOUD_SCORE_PUSH("同步云阅卷成绩任务标签", "同步云阅卷成绩任务", "normal", 50);
 
     private MqTagEnum(String desc, String code, String type, int id) {
         this.desc = desc;

+ 3 - 1
themis-business/src/main/java/com/qmth/themis/business/enums/TaskTypeEnum.java

@@ -23,7 +23,9 @@ public enum TaskTypeEnum {
 
     EXPORT_MARK_RESULT_SIMPLE("导出成绩简版"),
 
-    EXPORT_MARK_RESULT_STANDARD("导出成绩标准版");
+    EXPORT_MARK_RESULT_STANDARD("导出成绩标准版"),
+
+    SCORE_PUSH("成绩推送");
 
     private String code;
 

+ 187 - 0
themis-business/src/main/java/com/qmth/themis/business/templete/TaskSyncCommon.java

@@ -0,0 +1,187 @@
+package com.qmth.themis.business.templete;
+
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.Gson;
+import com.qmth.themis.business.constant.SpringContextHolder;
+import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.entity.TBTaskHistory;
+import com.qmth.themis.business.enums.TaskStatusEnum;
+import com.qmth.themis.business.enums.UploadFileEnum;
+import com.qmth.themis.business.service.TBAttachmentService;
+import com.qmth.themis.business.service.TBTaskHistoryService;
+import com.qmth.themis.business.util.OssUtil;
+import com.qmth.themis.common.contanst.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+/**
+ * @Description: 同步任务公用
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2020/7/19
+ */
+public class TaskSyncCommon {
+
+    private final static Logger log = LoggerFactory.getLogger(TaskSyncCommon.class);
+
+    private Map<String, Object> map = null;
+
+    private Long orgId = null;
+
+    private Long createId = null;
+
+    private TBTaskHistoryService tbTaskHistoryService = null;
+
+    private OssUtil ossUtil = null;
+
+    private Map<String, Object> ossEnv = null;
+
+    private TBTaskHistory tbTaskHistory = null;
+
+    private boolean exception = false;
+
+    private TBAttachmentService tbAttachmentService = null;
+
+    private String type = null;
+
+    private List<String> txtList = null;
+
+    public TaskSyncCommon() {
+
+    }
+
+    public TaskSyncCommon(Map<String, Object> map) {
+        this.map = map;
+    }
+
+    /**
+     * 初始化
+     */
+    public void init() {
+        OssUtil ossUtil = SpringContextHolder.getBean(OssUtil.class);
+        Gson gson = new Gson();
+        if (Objects.nonNull(this.map.get(SystemConstant.ORG_ID))) {
+            this.orgId = Long.parseLong(String.valueOf(this.map.get(SystemConstant.ORG_ID)));
+        }
+        this.createId = Long.parseLong(String.valueOf(this.map.get(SystemConstant.CREATE_ID)));
+        this.ossUtil = SpringContextHolder.getBean(OssUtil.class);
+        this.ossEnv = ossUtil.getAliYunOssPrivateDomain().getMap();
+        this.type = (boolean) this.ossEnv.get(SystemConstant.OSS) ? SystemConstant.OSS : SystemConstant.LOCAL;
+        Map tbTaskHistoryMap = (Map) this.map.get("tbTaskHistory");
+        this.tbTaskHistory = gson.fromJson(gson.toJson(tbTaskHistoryMap), TBTaskHistory.class);
+        this.tbTaskHistoryService = SpringContextHolder.getBean(TBTaskHistoryService.class);
+        this.tbAttachmentService = SpringContextHolder.getBean(TBAttachmentService.class);
+        map.put("tbTaskHistory", tbTaskHistory);
+    }
+
+    /**
+     * 写入txt文件
+     *
+     * @param path
+     * @param txtStr
+     * @throws IOException
+     */
+    public void writeSyncResultTxt(String path, String txtStr) throws IOException {
+        ByteArrayOutputStream out = null;
+        InputStream inputStream = null;
+        try {
+            out = new ByteArrayOutputStream();
+            out.write(txtStr.getBytes(StandardCharsets.UTF_8));
+            byte[] bookByteAry = out.toByteArray();
+            inputStream = new ByteArrayInputStream(bookByteAry);
+            JSONObject json = new JSONObject();
+            StringJoiner stringJoiner = new StringJoiner("");
+            if (Objects.equals(this.type, SystemConstant.OSS)) {//上传至oss
+                stringJoiner.add(path).add(File.separator).add(SystemConstant.getUuid())
+                        .add(SystemConstant.TXT_PREFIX).toString();
+                this.ossUtil.upload(false, stringJoiner.toString(), inputStream, null);
+            }
+            json.put(SystemConstant.PATH, stringJoiner.toString());
+            json.put(SystemConstant.TYPE, this.type);
+            json.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.file.ordinal());
+            this.tbTaskHistory.setReportFilePath(json.toJSONString());
+            tbTaskHistoryService.updateById(this.tbTaskHistory);
+        } catch (IOException e) {
+            log.error(SystemConstant.LOG_ERROR, e);
+        } finally {
+            if (Objects.nonNull(inputStream)) {
+                inputStream.close();
+            }
+            if (Objects.nonNull(out)) {
+                out.flush();
+                out.close();
+            }
+        }
+    }
+
+    /**
+     * 异常处理
+     *
+     * @param e
+     * @param txtList
+     * @return
+     */
+    public List<String> exception(Exception e, List<String> txtList) {
+        this.exception = true;
+        String exceptionStr = "数据同步时发生异常,请联系系统管理员处理!";
+        txtList.add(DateUtil.format(new Date(), Constants.DEFAULT_DATE_PATTERN) + "->" + exceptionStr + "错误信息:[" + e.getMessage() + "]");
+        this.tbTaskHistory.setSummary(exceptionStr);
+        this.tbTaskHistory.setStatus(TaskStatusEnum.FINISH);
+        return txtList;
+    }
+
+    public void setException(boolean exception) {
+        this.exception = exception;
+    }
+
+    public boolean isException() {
+        return exception;
+    }
+
+    public Map<String, Object> getMap() {
+        return map;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public Long getCreateId() {
+        return createId;
+    }
+
+    public TBTaskHistoryService getTbTaskHistoryService() {
+        return tbTaskHistoryService;
+    }
+
+    public OssUtil getOssUtil() {
+        return ossUtil;
+    }
+
+    public Map<String, Object> getOssEnv() {
+        return ossEnv;
+    }
+
+    public TBTaskHistory getTbTaskHistory() {
+        return tbTaskHistory;
+    }
+
+    public TBAttachmentService getTbAttachmentService() {
+        return tbAttachmentService;
+    }
+
+    public List<String> getTxtList() {
+        return txtList;
+    }
+
+    public void setTxtList(List<String> txtList) {
+        this.txtList = txtList;
+        map.put("txtList", txtList);
+    }
+}

+ 24 - 0
themis-business/src/main/java/com/qmth/themis/business/templete/TaskSyncTemplete.java

@@ -0,0 +1,24 @@
+package com.qmth.themis.business.templete;
+
+import com.qmth.themis.common.util.Result;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * @Description: 同步任务模版
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2020/7/15
+ */
+public interface TaskSyncTemplete {
+
+    /**
+     * 同步任务
+     *
+     * @param map
+     * @return
+     */
+    Result syncTask(Map<String, Object> map) throws IOException;
+}

+ 77 - 0
themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskSyncMarkCloudTemplete.java

@@ -0,0 +1,77 @@
+package com.qmth.themis.business.templete.impl;
+
+import cn.hutool.core.date.DateUtil;
+import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.templete.TaskSyncCommon;
+import com.qmth.themis.business.templete.TaskSyncTemplete;
+import com.qmth.themis.business.util.CloudMarkUtil;
+import com.qmth.themis.common.contanst.Constants;
+import com.qmth.themis.common.util.Result;
+import com.qmth.themis.common.util.ResultUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * @Description: 同步云阅卷成绩任务
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2020/10/20
+ */
+@Service
+public class TaskSyncMarkCloudTemplete implements TaskSyncTemplete {
+
+    private final static Logger log = LoggerFactory.getLogger(TaskSyncMarkCloudTemplete.class);
+
+    @Resource
+    CloudMarkUtil cloudMarkUtil;
+
+    /**
+     * 同步模版
+     *
+     * @param map
+     * @return
+     */
+    @Override
+    public Result syncTask(Map<String, Object> map) throws IOException {
+        log.info("开始同步云阅卷成绩数据");
+        long start = System.currentTimeMillis();
+        TaskSyncCommon taskSyncCommon = new TaskSyncCommon(map);
+        taskSyncCommon.init();
+        taskSyncCommon.setTxtList(new ArrayList());
+        StringJoiner stringJoiner = null;
+        try {
+            taskSyncCommon.getTxtList()
+                    .add(DateUtil.format(new Date(), Constants.DEFAULT_DATE_PATTERN) + "->开始准备同步云阅卷成绩数据");
+            Long orgId = (Long) map.get(SystemConstant.ORG_ID);
+            String examCode = (String) map.get(SystemConstant.EXAM_CODE);
+
+            boolean oss = (boolean) taskSyncCommon.getOssEnv().get(SystemConstant.OSS);
+            stringJoiner = new StringJoiner("");
+            if (!oss) {
+                stringJoiner.add(SystemConstant.FILES_DIR).add(File.separator);
+            }
+            LocalDateTime nowTime = LocalDateTime.now();
+            stringJoiner.add(String.valueOf(nowTime.getYear())).add(File.separator)
+                    .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
+                    .add(String.format("%02d", nowTime.getDayOfMonth()));
+            List<Map> finalList = cloudMarkUtil.callStudentScore(orgId, null, examCode);
+            long end = System.currentTimeMillis();
+            log.info("同步云阅卷成绩数据结束,============耗时============:{}秒", (end - start) / 1000);
+        } catch (Exception e) {
+            log.error("同步云阅卷成绩数据报错", e);
+            taskSyncCommon.exception(e, taskSyncCommon.getTxtList());
+        } finally {
+            //这里写入txt文件
+            taskSyncCommon.writeSyncResultTxt(stringJoiner.toString(), taskSyncCommon.getTxtList().toString());
+        }
+        return ResultUtil.ok(true);
+    }
+}

+ 95 - 0
themis-business/src/main/java/com/qmth/themis/business/util/CloudMarkUtil.java

@@ -0,0 +1,95 @@
+package com.qmth.themis.business.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.domain.YunMarkDomain;
+import com.qmth.themis.business.entity.TBOrg;
+import com.qmth.themis.business.service.CacheService;
+import com.qmth.themis.common.enums.ExceptionResultEnum;
+import com.qmth.themis.common.exception.BusinessException;
+import com.qmth.themis.common.signature.SignatureInfo;
+import com.qmth.themis.common.signature.SignatureType;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * 同步云阅卷接口工具类
+ * <p>
+ * Date: 2021/10/29.
+ */
+@Component
+public class CloudMarkUtil {
+    private final static Logger log = LoggerFactory.getLogger(CloudMarkUtil.class);
+
+    @Resource
+    CacheService cacheService;
+
+    private YunMarkDomain yunMarkDomain;
+
+    @Bean
+    public YunMarkDomain yunMarkEnv(YunMarkDomain yunMarkDomain) {
+        this.yunMarkDomain = new YunMarkDomain(yunMarkDomain.getUrl(), yunMarkDomain.getStudentScoreApi());
+        return this.yunMarkDomain;
+    }
+
+    public YunMarkDomain getYunMarkDomain() {
+        return yunMarkDomain;
+    }
+
+    /**
+     * 考试成绩考生查询接口
+     *
+     * @param orgId
+     * @param examId
+     * @param examCode
+     * @return
+     */
+    public List<Map> callStudentScore(Long orgId, Long examId, String examCode) throws IOException {
+        String url = yunMarkDomain.getUrl() + yunMarkDomain.getStudentScoreApi();
+        Map<String, Object> params = new HashMap<>();
+        if (Objects.nonNull(examId)) {
+            params.put("examId", examId);
+        } else if (Objects.nonNull(examCode)) {
+            params.put("examCode", examCode);
+        }
+        TBOrg tbOrg = cacheService.addOrgCache(orgId);
+        Optional.ofNullable(tbOrg).orElseThrow(() -> new BusinessException(ExceptionResultEnum.ORG_NO));
+
+        int pageNumber = 1;
+        int pageSize = 100;
+        List<Map> students = null;
+        for (; ; ) {
+            params.put("pageNumber", pageNumber);
+            params.put("pageSize", pageSize);
+            Long timestamp = System.currentTimeMillis();
+            String accessToken = SignatureInfo.build(SignatureType.SECRET, SystemConstant.METHOD, yunMarkDomain.getStudentScoreApi(), timestamp, tbOrg.getAccessKey(), tbOrg.getAccessSecret());
+            String result = HttpUtil.post(url, params, accessToken, timestamp);
+            result = StringEscapeUtils.unescapeHtml4(result);
+            if (Objects.nonNull(result)) {
+                if (result.contains("HTTP")) {
+                    throw new BusinessException("云阅卷鉴权失败");
+                }
+                List<Map> tempList = JSONObject.parseArray(JSONObject.toJSON(result).toString(), Map.class);
+                if (Objects.nonNull(tempList) && tempList.size() > 0) {
+                    if (Objects.isNull(students)) {
+                        students = new ArrayList<>();
+                    }
+                    students.addAll(tempList);
+                    pageNumber++;
+                } else {
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+        return students;
+    }
+}

+ 1 - 1
themis-business/src/main/java/com/qmth/themis/business/util/HttpUtil.java

@@ -112,7 +112,7 @@ public class HttpUtil {
         HttpPost post = new HttpPost(url);
         post.setHeader(SystemConstant.HEADER_AUTHORIZATION, secret);
         post.setHeader(SystemConstant.HEADER_TIME, String.valueOf(timestamp));
-        post.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
+        post.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded;charset=utf-8");
         // 构建请求参数
         List<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
         if (params != null) {

+ 2 - 2
themis-common/pom.xml

@@ -5,13 +5,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.themis.common</groupId>
     <artifactId>themis-common</artifactId>
-    <version>1.1.3</version>
+    <version>1.2.0</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.themis</groupId>
         <artifactId>themis-service</artifactId>
-        <version>1.1.3</version>
+        <version>1.2.0</version>
     </parent>
 
     <dependencies>

+ 2 - 2
themis-exam/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.themis.exam</groupId>
     <artifactId>themis-exam</artifactId>
-    <version>1.1.3</version>
+    <version>1.2.0</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.themis</groupId>
         <artifactId>themis-service</artifactId>
-        <version>1.1.3</version>
+        <version>1.2.0</version>
     </parent>
 
     <dependencies>

+ 11 - 0
themis-exam/src/main/java/com/qmth/themis/exam/config/DictionaryConfig.java

@@ -124,4 +124,15 @@ public class DictionaryConfig {
     public MonitorDomain monitorDomain() {
         return new MonitorDomain();
     }
+
+    /**
+     * 同步云阅卷配置
+     *
+     * @return
+     */
+    @Bean
+    @ConfigurationProperties(prefix = "yun.mark", ignoreUnknownFields = false)
+    public YunMarkDomain yunMarkDomain() {
+        return new YunMarkDomain();
+    }
 }

+ 4 - 0
themis-exam/src/main/resources/application.properties

@@ -187,6 +187,10 @@ sys.config.tempDataDir=/home/project/themis/exam/files/
 sys.config.qrExpireTime=30
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 
+#\u4E91\u9605\u5377\u540C\u6B65\u914D\u7F6E
+yun.mark.url=http://localhost:8081
+yun.mark.studentScoreApi=/api/exam/student/score
+
 #api\u524D\u7F00
 prefix.url.exam=api/oe
 prefix.url.mobile=api/mobile

+ 2 - 2
themis-mq/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.themis.mq</groupId>
     <artifactId>themis-mq</artifactId>
-    <version>1.1.3</version>
+    <version>1.2.0</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.themis</groupId>
         <artifactId>themis-service</artifactId>
-        <version>1.1.3</version>
+        <version>1.2.0</version>
     </parent>
 
     <dependencies>

+ 11 - 0
themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

@@ -17,6 +17,7 @@ import com.qmth.themis.business.enums.*;
 import com.qmth.themis.business.service.*;
 import com.qmth.themis.business.templete.TaskExportTemplete;
 import com.qmth.themis.business.templete.TaskImportTemplete;
+import com.qmth.themis.business.templete.TaskSyncTemplete;
 import com.qmth.themis.business.templete.impl.*;
 import com.qmth.themis.business.util.JacksonUtil;
 import com.qmth.themis.business.util.RedisUtil;
@@ -222,6 +223,16 @@ public class MqLogicServiceImpl implements MqLogicService {
             } catch (IOException e) {
                 log.error(SystemConstant.LOG_ERROR, e);
             }
+        } else if (tag.contains("Push".toUpperCase())) {
+            TaskSyncTemplete taskSyncTemplete = null;
+            if (Objects.equals(MqTagEnum.MARK_CLOUD_SCORE_PUSH.name(), tag)) {
+                taskSyncTemplete = SpringContextHolder.getBean(TaskSyncMarkCloudTemplete.class);
+            }
+            try {
+                taskSyncTemplete.syncTask(map);
+            } catch (IOException e) {
+                log.error(SystemConstant.LOG_ERROR, e);
+            }
         } else {
             TaskExportTemplete taskExportTemplete = null;
             if (Objects.equals(MqTagEnum.ROOM_CODE_EXPORT.name(), tag)) {

+ 2 - 2
themis-task/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.qmth.themis.task</groupId>
     <artifactId>themis-task</artifactId>
-    <version>1.1.3</version>
+    <version>1.2.0</version>
     <packaging>jar</packaging>
 
     <parent>
         <groupId>com.qmth.themis</groupId>
         <artifactId>themis-service</artifactId>
-        <version>1.1.3</version>
+        <version>1.2.0</version>
     </parent>
 
     <dependencies>

+ 11 - 0
themis-task/src/main/java/com/qmth/themis/task/config/DictionaryConfig.java

@@ -80,4 +80,15 @@ public class DictionaryConfig {
     public MonitorDomain monitorDomain() {
         return new MonitorDomain();
     }
+
+    /**
+     * 同步云阅卷配置
+     *
+     * @return
+     */
+    @Bean
+    @ConfigurationProperties(prefix = "yun.mark", ignoreUnknownFields = false)
+    public YunMarkDomain yunMarkDomain() {
+        return new YunMarkDomain();
+    }
 }

+ 1 - 1
themis-task/src/main/java/com/qmth/themis/task/start/StartRunning.java

@@ -87,7 +87,7 @@ public class StartRunning implements CommandLineRunner {
         /**
          * task
          */
-        rocketMessageConsumer.setRocketMQConsumer(nameServer, dictionaryConfig.mqConfigDomain().getMap().get(MqGroupEnum.TASK_GROUP.name()), dictionaryConfig.mqConfigDomain().getTopic(), MqTagEnum.EXAM_STUDENT_IMPORT.name() + "||" + MqTagEnum.ROOM_CODE_IMPORT.name() + "||" + MqTagEnum.ROOM_CODE_EXPORT.name() + "||" + MqTagEnum.EXAM_PAPER_IMPORT.name() + "||" + MqTagEnum.EXAM_STUDENT_EXPORT.name() + "||" + MqTagEnum.MARK_RESULT_SIMPLE_EXPORT.name() + "||" + MqTagEnum.MARK_RESULT_STANDARD_EXPORT.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(TaskConcurrentlyImpl.class));
+        rocketMessageConsumer.setRocketMQConsumer(nameServer, dictionaryConfig.mqConfigDomain().getMap().get(MqGroupEnum.TASK_GROUP.name()), dictionaryConfig.mqConfigDomain().getTopic(), MqTagEnum.EXAM_STUDENT_IMPORT.name() + "||" + MqTagEnum.ROOM_CODE_IMPORT.name() + "||" + MqTagEnum.ROOM_CODE_EXPORT.name() + "||" + MqTagEnum.EXAM_PAPER_IMPORT.name() + "||" + MqTagEnum.EXAM_STUDENT_EXPORT.name() + "||" + MqTagEnum.MARK_RESULT_SIMPLE_EXPORT.name() + "||" + MqTagEnum.MARK_RESULT_STANDARD_EXPORT.name() + "||" + MqTagEnum.MARK_CLOUD_SCORE_PUSH.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(TaskConcurrentlyImpl.class));
         /**
          * websocket mq start
          */

+ 5 - 1
themis-task/src/main/resources/application.properties

@@ -215,4 +215,8 @@ tencentyun.sdk.callbackTime=10m
 tencentyun.sdk.trtcQueryUrl=trtc.tencentcloudapi.com
 tencentyun.sdk.trtcRegion=ap-guangzhou
 
-monitor.config.prefix=oe_test
+monitor.config.prefix=oe_test
+
+#\u4E91\u9605\u5377\u540C\u6B65\u914D\u7F6E
+yun.mark.url=http://localhost:8081
+yun.mark.studentScoreApi=/api/exam/student/score