Przeglądaj źródła

Merge branch 'dev' into release
111

wangliang 4 lat temu
rodzic
commit
5496ba207c
17 zmienionych plików z 218 dodań i 76 usunięć
  1. 11 2
      themis-backend/src/main/java/com/qmth/themis/backend/api/TIeInvigilateCallMobileController.java
  2. 13 5
      themis-backend/src/main/java/com/qmth/themis/backend/api/TIeInvigilateController.java
  3. 13 5
      themis-business/src/main/java/com/qmth/themis/business/bean/backend/InvigilateListDetailBean.java
  4. 2 1
      themis-business/src/main/java/com/qmth/themis/business/dao/TIeExamInvigilateCallMapper.java
  5. 38 0
      themis-business/src/main/java/com/qmth/themis/business/dto/response/TIeExamInvigilateCallDto.java
  6. 23 0
      themis-business/src/main/java/com/qmth/themis/business/entity/TEExamStudentLog.java
  7. 5 0
      themis-business/src/main/java/com/qmth/themis/business/enums/MqGroupEnum.java
  8. 1 1
      themis-business/src/main/java/com/qmth/themis/business/enums/MqTagEnum.java
  9. 2 1
      themis-business/src/main/java/com/qmth/themis/business/service/TIeExamInvigilateCallService.java
  10. 2 1
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TIeExamInvigilateCallServiceImpl.java
  11. 1 0
      themis-business/src/main/resources/db/init.sql
  12. 11 5
      themis-business/src/main/resources/mapper/TIeExamInvigilateCallMapper.xml
  13. 11 4
      themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java
  14. 19 17
      themis-exam/src/main/java/com/qmth/themis/exam/api/TEStudentController.java
  15. 3 1
      themis-exam/src/main/java/com/qmth/themis/exam/start/StartRunning.java
  16. 1 1
      themis-exam/src/main/java/com/qmth/themis/exam/websocket/WebSocketMobileServer.java
  17. 62 32
      themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

+ 11 - 2
themis-backend/src/main/java/com/qmth/themis/backend/api/TIeInvigilateCallMobileController.java

@@ -8,9 +8,11 @@ import com.qmth.themis.business.annotation.ApiJsonProperty;
 import com.qmth.themis.business.base.BasePage;
 import com.qmth.themis.business.bean.mobile.MobileAuthorizationMonitorBean;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
+import com.qmth.themis.business.config.SystemConfig;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.AuthDto;
 import com.qmth.themis.business.dto.MqDto;
+import com.qmth.themis.business.dto.response.TIeExamInvigilateCallDto;
 import com.qmth.themis.business.entity.TBSession;
 import com.qmth.themis.business.entity.TBUser;
 import com.qmth.themis.business.entity.TIeExamInvigilateCall;
@@ -32,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.io.File;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -62,9 +65,12 @@ public class TIeInvigilateCallMobileController {
     @Resource
     TencentYunUtil tencentYunUtil;
 
+    @Resource
+    SystemConfig systemConfig;
+
     @ApiOperation(value = "监考监控通话查询接口")
     @RequestMapping(value = "/call/list", method = RequestMethod.POST)
-    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = TIeExamInvigilateCall.class)})
+    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = TIeExamInvigilateCallDto.class)})
     public Result callList(@ApiParam(value = "考试批次id", required = true) @RequestParam(required = true) Long examId, @ApiParam(value = "通话状态", required = false) @RequestParam(required = false) String callStatus, @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber, @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
         if (Objects.isNull(examId) || Objects.equals(examId, "")) {
             throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
@@ -80,7 +86,10 @@ public class TIeInvigilateCallMobileController {
         if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
             userId = tbUser.getId();
         }
-        IPage<TIeExamInvigilateCall> tIeExamInvigilateCallIPage = tIeExamInvigilateCallService.examInvigilateCallQuery(new Page<>(pageNumber, pageSize), examId, userId, tbUser.getOrgId(), MonitorStatusSourceEnum.START.name(), callStatus);
+        IPage<TIeExamInvigilateCallDto> tIeExamInvigilateCallIPage = tIeExamInvigilateCallService.examInvigilateCallQuery(new Page<>(pageNumber, pageSize), examId, userId, tbUser.getOrgId(), MonitorStatusSourceEnum.START.name(), callStatus);
+        tIeExamInvigilateCallIPage.getRecords().forEach(s -> {
+            s.setBasePhotoPath(systemConfig.getProperty("aliyun.oss.url") + File.separator + s.getBasePhotoPath());
+        });
         BasePage basePage = new BasePage(tIeExamInvigilateCallIPage.getRecords(), tIeExamInvigilateCallIPage.getCurrent(), tIeExamInvigilateCallIPage.getSize(), tIeExamInvigilateCallIPage.getTotal());
         return ResultUtil.ok(basePage);
     }

+ 13 - 5
themis-backend/src/main/java/com/qmth/themis/backend/api/TIeInvigilateController.java

@@ -3,8 +3,6 @@ package com.qmth.themis.backend.api;
 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.google.common.reflect.TypeToken;
-import com.google.gson.Gson;
 import com.qmth.themis.business.annotation.ApiJsonObject;
 import com.qmth.themis.business.annotation.ApiJsonProperty;
 import com.qmth.themis.business.base.BasePage;
@@ -14,6 +12,7 @@ import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
 import com.qmth.themis.business.cache.bean.ExamCacheBean;
 import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
+import com.qmth.themis.business.config.SystemConfig;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.AuthDto;
 import com.qmth.themis.business.dto.ExamPropCountDto;
@@ -34,6 +33,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.io.File;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
@@ -87,6 +87,12 @@ public class TIeInvigilateController {
     @Resource
     TEExamService teExamService;
 
+    @Resource
+    TEStudentService teStudentService;
+
+    @Resource
+    SystemConfig systemConfig;
+
     @ApiOperation(value = "实时监控台列表接口")
     @RequestMapping(value = "/list", method = RequestMethod.POST)
     @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = InvigilateListBean.class)})
@@ -191,6 +197,8 @@ public class TIeInvigilateController {
         ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(examRecordId);
         Integer breachStatus = Objects.isNull(ExamRecordCacheUtil.getBreachStatus(examRecordId)) ? null : ExamRecordCacheUtil.getBreachStatus(examRecordId);
         ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
+        TEStudent teStudent = teStudentService.getById(examStudentCacheBean.getStudentId());
+        String basePhotoPath = systemConfig.getProperty("aliyun.oss.url") + File.separator + teStudent.getBasePhotoPath();
         String identity = examStudentCacheBean.getIdentity();
         String examStudentName = examStudentCacheBean.getName();
         String courseNameCode = examStudentCacheBean.getCourseName() + "(" + examStudentCacheBean.getCourseCode() + ")";
@@ -198,12 +206,12 @@ public class TIeInvigilateController {
         String roomName = examStudentCacheBean.getRoomName();
         ExamActivityCacheBean examActivityCacheBean = teExamActivityService.getExamActivityCacheBean(examActivityId);
         ExamCacheBean examCacheBean = teExamService.getExamCacheBean(examId);
-        InvigilateListDetailBean invigilateListDetailBean = new InvigilateListDetailBean(examCacheBean.getName(), examActivityCacheBean.getCode(), examId, examActivityId, examStudentId, examRecordId, identity, examStudentName, courseNameCode, status, roomCode, roomName, breachStatus);
+        InvigilateListDetailBean invigilateListDetailBean = new InvigilateListDetailBean(examCacheBean.getName(), examActivityCacheBean.getCode(), examId, examActivityId, examStudentId, examRecordId, identity, examStudentName, courseNameCode, status, roomCode, roomName, breachStatus, basePhotoPath);
 
         //考生轨迹
         QueryWrapper<TEExamStudentLog> teExamStudentLogQueryWrapper = new QueryWrapper<>();
-        teExamStudentLogQueryWrapper.lambda().eq(TEExamStudentLog::getExamRecordId,examRecordId)
-                .eq(TEExamStudentLog::getExamStudentId,examStudentId);
+        teExamStudentLogQueryWrapper.lambda().eq(TEExamStudentLog::getExamRecordId, examRecordId)
+                .eq(TEExamStudentLog::getExamStudentId, examStudentId);
         List<TEExamStudentLog> teExamStudentLogList = teExamStudentLogService.list(teExamStudentLogQueryWrapper);
         invigilateListDetailBean.setExamStudentLogList(teExamStudentLogList);
 

+ 13 - 5
themis-business/src/main/java/com/qmth/themis/business/bean/backend/InvigilateListDetailBean.java

@@ -2,14 +2,10 @@ package com.qmth.themis.business.bean.backend;
 
 import com.qmth.themis.business.entity.TEExamStudentLog;
 import com.qmth.themis.business.enums.ExamRecordStatusEnum;
-import com.qmth.themis.business.enums.ExamTypeEnum;
-import com.qmth.themis.business.enums.ExceptionEnum;
-import com.qmth.themis.business.enums.VerifyExceptionEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
 import java.io.Serializable;
-import java.util.Date;
 import java.util.List;
 
 /**
@@ -73,9 +69,20 @@ public class InvigilateListDetailBean implements Serializable {
     @ApiModelProperty(name = "是否违纪,0:是,1:不是")
     private Integer breachStatus;
 
+    @ApiModelProperty(value = "考生底照地址")
+    private String basePhotoPath;
+
     @ApiModelProperty(name = "考生轨迹")
     private List<TEExamStudentLog> examStudentLogList;
 
+    public String getBasePhotoPath() {
+        return basePhotoPath;
+    }
+
+    public void setBasePhotoPath(String basePhotoPath) {
+        this.basePhotoPath = basePhotoPath;
+    }
+
     public List<TEExamStudentLog> getExamStudentLogList() {
         return examStudentLogList;
     }
@@ -84,7 +91,7 @@ public class InvigilateListDetailBean implements Serializable {
         this.examStudentLogList = examStudentLogList;
     }
 
-    public InvigilateListDetailBean(String examName, String examActivityCode, Long examId, Long examActivityId, Long examStudentId, Long examRecordId, String identity, String examStudentName, String courseNameCode, ExamRecordStatusEnum statusCode, String roomCode, String roomName, Integer breachStatus) {
+    public InvigilateListDetailBean(String examName, String examActivityCode, Long examId, Long examActivityId, Long examStudentId, Long examRecordId, String identity, String examStudentName, String courseNameCode, ExamRecordStatusEnum statusCode, String roomCode, String roomName, Integer breachStatus, String basePhotoPath) {
         this.examName = examName;
         this.examActivityCode = examActivityCode;
         this.examId = examId;
@@ -98,6 +105,7 @@ public class InvigilateListDetailBean implements Serializable {
         this.roomCode = roomCode;
         this.roomName = roomName;
         this.breachStatus = breachStatus;
+        this.basePhotoPath = basePhotoPath;
     }
 
     public String getExamName() {

+ 2 - 1
themis-business/src/main/java/com/qmth/themis/business/dao/TIeExamInvigilateCallMapper.java

@@ -2,6 +2,7 @@ package com.qmth.themis.business.dao;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.qmth.themis.business.dto.response.TIeExamInvigilateCallDto;
 import com.qmth.themis.business.entity.TIeExamInvigilateCall;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -29,7 +30,7 @@ public interface TIeExamInvigilateCallMapper extends BaseMapper<TIeExamInvigilat
      * @param callStatus
      * @return
      */
-    public IPage<TIeExamInvigilateCall> examInvigilateCallQuery(IPage<Map> iPage, @Param("examId") Long examId, @Param("userId") Long userId, @Param("orgId") Long orgId, @Param("status") String status, @Param("callStatus") String callStatus);
+    public IPage<TIeExamInvigilateCallDto> examInvigilateCallQuery(IPage<Map> iPage, @Param("examId") Long examId, @Param("userId") Long userId, @Param("orgId") Long orgId, @Param("status") String status, @Param("callStatus") String callStatus);
 
     /**
      * 监考监控通话count查询

+ 38 - 0
themis-business/src/main/java/com/qmth/themis/business/dto/response/TIeExamInvigilateCallDto.java

@@ -0,0 +1,38 @@
+package com.qmth.themis.business.dto.response;
+
+import com.qmth.themis.business.entity.TIeExamInvigilateCall;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+* @Description: 监考 监控通话申请 dto
+* @Param:
+* @return:
+* @Author: wangliang
+* @Date: 2020/9/17
+*/
+public class TIeExamInvigilateCallDto extends TIeExamInvigilateCall implements Serializable{
+
+    @ApiModelProperty(value = "考生姓名")
+    private String examStudentName;
+
+    @ApiModelProperty(value = "考生底照地址")
+    private String basePhotoPath;
+
+    public String getExamStudentName() {
+        return examStudentName;
+    }
+
+    public void setExamStudentName(String examStudentName) {
+        this.examStudentName = examStudentName;
+    }
+
+    public String getBasePhotoPath() {
+        return basePhotoPath;
+    }
+
+    public void setBasePhotoPath(String basePhotoPath) {
+        this.basePhotoPath = basePhotoPath;
+    }
+}

+ 23 - 0
themis-business/src/main/java/com/qmth/themis/business/entity/TEExamStudentLog.java

@@ -54,6 +54,10 @@ public class TEExamStudentLog implements Serializable {
     @TableField(value = "create_time", fill = FieldFill.INSERT)
     private Date createTime;
 
+    @ApiModelProperty(value = "业务id")
+    @TableField(value = "obj_id")
+    private Long objId;
+
     public TEExamStudentLog() {
 
     }
@@ -76,6 +80,25 @@ public class TEExamStudentLog implements Serializable {
         this.examRecordId = examRecordId;
     }
 
+    public TEExamStudentLog(String type, String info, String remark, Long studentId, Long examStudentId, Long examRecordId,Long objId) {
+        this.id = Constants.idGen.next();
+        this.type = type;
+        this.info = info;
+        this.remark = remark;
+        this.studentId = studentId;
+        this.examStudentId = examStudentId;
+        this.examRecordId = examRecordId;
+        this.objId = objId;
+    }
+
+    public Long getObjId() {
+        return objId;
+    }
+
+    public void setObjId(Long objId) {
+        this.objId = objId;
+    }
+
     public Long getExamStudentId() {
         return examStudentId;
     }

+ 5 - 0
themis-business/src/main/java/com/qmth/themis/business/enums/MqGroupEnum.java

@@ -34,6 +34,11 @@ public enum MqGroupEnum {
      */
     WEBSOCKET_OE_GROUP("themis-group-exam-websocketOe"),
 
+    /**
+     * websocket oe 考生 group
+     */
+    WEBSOCKET_OE_MOBILE_GROUP("themis-group-exam-websocketOeMobile"),
+
     /**
      * websocket超时退出 考生 group
      */

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

@@ -42,7 +42,7 @@ public enum MqTagEnum {
     EXAM_SCORE_CALCULATE("重新算分标签", "重新算分", "normal", 30),
     EXAM_STUDNET_UPDATE("考生数据更新标签", "考生数据更新", "normal", 31),
     EXAM_BREAK("考试断点标签", "考试断点", "normal", 32),
-    EXAM_STOP("考试移动端监控暂停标签", "考试移动端监控暂停", "normal", 33);
+    EXAM_STOP("考试移动端监控退出标签", "考试移动端退出暂停", "normal", 33);
 
     private MqTagEnum(String desc, String code, String type, int id) {
         this.desc = desc;

+ 2 - 1
themis-business/src/main/java/com/qmth/themis/business/service/TIeExamInvigilateCallService.java

@@ -2,6 +2,7 @@ package com.qmth.themis.business.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.themis.business.dto.response.TIeExamInvigilateCallDto;
 import com.qmth.themis.business.entity.TIeExamInvigilateCall;
 
 import java.util.Map;
@@ -26,7 +27,7 @@ public interface TIeExamInvigilateCallService extends IService<TIeExamInvigilate
      * @param callStatus
      * @return
      */
-    public IPage<TIeExamInvigilateCall> examInvigilateCallQuery(IPage<Map> iPage, Long examId, Long userId, Long orgId, String status, String callStatus);
+    public IPage<TIeExamInvigilateCallDto> examInvigilateCallQuery(IPage<Map> iPage, Long examId, Long userId, Long orgId, String status, String callStatus);
 
     /**
      * 监考监控通话count查询

+ 2 - 1
themis-business/src/main/java/com/qmth/themis/business/service/impl/TIeExamInvigilateCallServiceImpl.java

@@ -3,6 +3,7 @@ package com.qmth.themis.business.service.impl;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.themis.business.dao.TIeExamInvigilateCallMapper;
+import com.qmth.themis.business.dto.response.TIeExamInvigilateCallDto;
 import com.qmth.themis.business.entity.TIeExamInvigilateCall;
 import com.qmth.themis.business.service.TIeExamInvigilateCallService;
 import org.springframework.stereotype.Service;
@@ -35,7 +36,7 @@ public class TIeExamInvigilateCallServiceImpl extends ServiceImpl<TIeExamInvigil
      * @return
      */
     @Override
-    public IPage<TIeExamInvigilateCall> examInvigilateCallQuery(IPage<Map> iPage, Long examId, Long userId, Long orgId, String status, String callStatus) {
+    public IPage<TIeExamInvigilateCallDto> examInvigilateCallQuery(IPage<Map> iPage, Long examId, Long userId, Long orgId, String status, String callStatus) {
         return tIeExamInvigilateCallMapper.examInvigilateCallQuery(iPage, examId, userId, orgId, status, callStatus);
     }
 

+ 1 - 0
themis-business/src/main/resources/db/init.sql

@@ -1164,6 +1164,7 @@ CREATE TABLE `t_e_exam_student_log` (
   `info` varchar(1000) DEFAULT NULL COMMENT '描述',
   `remark` mediumtext COMMENT '备注',
   `create_time` timestamp NULL COMMENT '创建时间',
+  `obj_id` bigint(20) DEFAULT NULL COMMENT '业务id',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='考生轨迹';
 

+ 11 - 5
themis-business/src/main/resources/mapper/TIeExamInvigilateCallMapper.xml

@@ -2,13 +2,17 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.qmth.themis.business.dao.TIeExamInvigilateCallMapper">
 
-    <select id="examInvigilateCallQuery" resultType="com.qmth.themis.business.entity.TIeExamInvigilateCall">
+    <select id="examInvigilateCallQuery" resultType="com.qmth.themis.business.dto.response.TIeExamInvigilateCallDto">
         select
-            tieic.*
+            tieic.*,
+            tees.name as examStudentName,
+            tes.base_photo_path as basePhotoPath
         from
             t_ie_exam_invigilate_call tieic
         left join t_e_exam_student tees on
             tees.id = tieic.exam_student_id
+        left join t_e_student tes on
+            tes.id = tees.student_id
         left join t_b_exam_invigilate_user tbeiu on
             tbeiu.exam_id = tieic.exam_id
         and tbeiu.room_code = tees.room_code
@@ -35,11 +39,13 @@
         select
         count(1)
         from
-        t_ie_exam_invigilate_call tieic
+            t_ie_exam_invigilate_call tieic
         left join t_e_exam_student tees on
-        tees.id = tieic.exam_student_id
+            tees.id = tieic.exam_student_id
+        left join t_e_student tes on
+            tes.id = tees.student_id
         left join t_b_exam_invigilate_user tbeiu on
-        tbeiu.exam_id = tieic.exam_id
+            tbeiu.exam_id = tieic.exam_id
         and tbeiu.room_code = tees.room_code
         <where>
             <if test="examId != null and examId != ''">

+ 11 - 4
themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java

@@ -1,9 +1,11 @@
 package com.qmth.themis.exam.api;
 
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.qmth.themis.business.annotation.ApiJsonObject;
 import com.qmth.themis.business.annotation.ApiJsonProperty;
 import com.qmth.themis.business.bean.exam.*;
+import com.qmth.themis.business.cache.ExamBreakCacheUtil;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.MqDto;
@@ -12,10 +14,7 @@ import com.qmth.themis.business.dto.cache.TEStudentCacheDto;
 import com.qmth.themis.business.dto.response.TEExamDto;
 import com.qmth.themis.business.dto.response.TEExamResultDto;
 import com.qmth.themis.business.entity.TEExam;
-import com.qmth.themis.business.enums.MqTagEnum;
-import com.qmth.themis.business.enums.MqTopicEnum;
-import com.qmth.themis.business.enums.SystemOperationEnum;
-import com.qmth.themis.business.enums.WebsocketTypeEnum;
+import com.qmth.themis.business.enums.*;
 import com.qmth.themis.business.service.MqDtoService;
 import com.qmth.themis.business.service.TEExamService;
 import com.qmth.themis.business.service.TEExamStudentService;
@@ -126,6 +125,14 @@ public class TEExamController {
             }
             ExamStartBean examStartBean = teExamService.start(teStudent.getId(), param.getRecordId());
             if (Objects.nonNull(param.getReason())) {
+                Long breakId = ExamRecordCacheUtil.getLastBreakId(param.getRecordId());
+                if (Objects.nonNull(breakId)) {
+                    JSONObject jsonObject = JSONObject.parseObject(param.getReason());
+                    ExceptionEnum exceptionEnum = ExceptionEnum.valueOf(ExceptionEnum.convertToName(String.valueOf(jsonObject.getJSONObject("reason").get("type"))));
+                    String reason = String.valueOf(jsonObject.getJSONObject("reason").get("reason"));
+                    ExamBreakCacheUtil.setBreakReason(breakId, exceptionEnum);
+                    ExamBreakCacheUtil.setResumeReason(breakId, reason);
+                }
                 //考试断点异常原因 发送mq start
                 MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXCEPTION_LOG.name(), JacksonUtil.parseJson(param), MqTagEnum.EXCEPTION_LOG, String.valueOf(param.getRecordId()), param.getReason());
                 mqDtoService.assembleSendOneWayMsg(mqDto);

+ 19 - 17
themis-exam/src/main/java/com/qmth/themis/exam/api/TEStudentController.java

@@ -24,7 +24,6 @@ import com.qmth.themis.business.entity.TEConfig;
 import com.qmth.themis.business.entity.TEStudent;
 import com.qmth.themis.business.enums.*;
 import com.qmth.themis.business.service.*;
-import com.qmth.themis.business.util.JacksonUtil;
 import com.qmth.themis.business.util.RedisUtil;
 import com.qmth.themis.business.util.ServletUtil;
 import com.qmth.themis.business.util.SessionUtil;
@@ -209,24 +208,24 @@ public class TEStudentController {
             if (Objects.isNull(status)) {
                 throw new BusinessException("考试状态不能为空");
             }
-            Long examIdMap = ExamRecordCacheUtil.getExamId(recordId);
+            Long ecExamId = ExamRecordCacheUtil.getExamId(recordId);
             Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
             Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
             Integer durationSeconds = Objects.isNull(ExamRecordCacheUtil.getDurationSeconds(recordId)) ? 0 : ExamRecordCacheUtil.getDurationSeconds(recordId);
-            ExamCacheBean ec = teExamService.getExamCacheBean(examIdMap);//考试缓存
+            ExamCacheBean ec = teExamService.getExamCacheBean(ecExamId);//考试缓存
             Integer breakExpireSeconds = Objects.isNull(ec.getBreakExpireSeconds()) ? 0 : ec.getBreakExpireSeconds();
             ExamActivityCacheBean examActivityCacheBean = teExamActivityService.getExamActivityCacheBean(examActivityId);//考试场次缓存
             ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
 
-            //获取最近同步时间
-            Date clientLastSyncTime = ExamRecordCacheUtil.getClientLastSyncTime(recordId);
+            //获取断点时间
+            Date lastBreakTime = ExamRecordCacheUtil.getLastBreakTime(recordId);
             //获取剩余断点次数
             Integer alreadyBreakCount = ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
             Integer leftBreakResumeCount = ec.getBreakResumeCount() - alreadyBreakCount;
             leftBreakResumeCount = leftBreakResumeCount <= 0 ? 0 : leftBreakResumeCount;
             //如果断点时间大于整体断点时间,则强制交卷
             if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING) || Objects.equals(status, ExamRecordStatusEnum.BREAK_OFF) || Objects.equals(status, ExamRecordStatusEnum.RESUME_PREPARE)) {
-                if (Objects.nonNull(clientLastSyncTime) && (System.currentTimeMillis() - clientLastSyncTime.getTime()) / 1000 > breakExpireSeconds) {
+                if (Objects.nonNull(lastBreakTime) && (System.currentTimeMillis() - lastBreakTime.getTime()) / 1000 > breakExpireSeconds) {
                     teExamService.finish(teStudentCacheDto.getId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
                     List<TEExamDto> list = teExamService.getWaitingExam(teStudent.getId(), examId, orgId);
                     if (Objects.nonNull(list) && list.size() > 0) {
@@ -240,17 +239,20 @@ public class TEStudentController {
                         if (Objects.nonNull(list) && list.size() > 0) {
                             map.put("waiting", list);
                         }
-                    } else {//否则断点次数+1
-                        alreadyBreakCount++;
-                        ExamRecordCacheUtil.setLastBreakId(recordId, Constants.idGen.next());
-                        ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.RESUME_PREPARE);
-                        ExamRecordCacheUtil.setLastBreakTime(recordId);
-                        ExamRecordCacheUtil.setAlreadyBreakCount(recordId, alreadyBreakCount);
-                        ExamRecordCacheUtil.setLastStartTime(recordId);
-                        //考试断点异常原因 发送mq start
-                        MqDto mqDtoBreak = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_BREAK.name(), ExceptionEnum.NET_TIME_OUT, MqTagEnum.EXAM_BREAK, String.valueOf(recordId), String.valueOf(recordId));
-                        mqDtoService.assembleSendOneWayMsg(mqDtoBreak);
-                        //考试断点异常原因 发送mq end
+                    } else {
+                        //只有ANSWERING状态才生成断点
+                        if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING)) {
+                            alreadyBreakCount++;
+                            ExamRecordCacheUtil.setLastBreakId(recordId, Constants.idGen.next());
+                            ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.RESUME_PREPARE);
+                            ExamRecordCacheUtil.setLastBreakTime(recordId);
+                            ExamRecordCacheUtil.setAlreadyBreakCount(recordId, alreadyBreakCount);
+                            ExamRecordCacheUtil.setLastStartTime(recordId);
+                            //考试断点异常原因 发送mq start
+                            MqDto mqDtoBreak = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_BREAK.name(), ExceptionEnum.NET_TIME_OUT, MqTagEnum.EXAM_BREAK, String.valueOf(recordId), String.valueOf(recordId));
+                            mqDtoService.assembleSendOneWayMsg(mqDtoBreak);
+                            //考试断点异常原因 发送mq end
+                        }
                         ExamUnFinishBean examUnFinishBean = this.unFinishCommon(recordId, ec, examStudentCacheBean, examActivityCacheBean, examStudentId);
                         map.put("unFinished", examUnFinishBean);
                     }

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

@@ -5,6 +5,7 @@ import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.enums.MqGroupEnum;
 import com.qmth.themis.business.enums.MqTagEnum;
 import com.qmth.themis.business.enums.MqTopicEnum;
+import com.qmth.themis.exam.websocket.WebSocketMobileServer;
 import com.qmth.themis.exam.websocket.WebSocketOeServer;
 import com.qmth.themis.mq.listener.RocketMessageConsumer;
 import com.qmth.themis.mq.templete.impl.*;
@@ -56,7 +57,8 @@ public class StartRunning implements CommandLineRunner {
         /**
          * websocket mq start
          */
-        rocketMessageConsumer.setRocketMQConsumer(nameServer, MqGroupEnum.WEBSOCKET_OE_GROUP.getCode(), MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.OE_HARD_FINISH.name() + "||" + MqTagEnum.OE_IM_BROADCASTING.name() + "||" + MqTagEnum.OE_IM_CLUSTERING.name() + "||" + MqTagEnum.OE_LIVENESS_VERIFY.name() + "||" + MqTagEnum.OE_MONITOR_FINISH.name() + "||" + MqTagEnum.OE_WARNING_FINISH.name() + "||" + MqTagEnum.EXAM_STOP.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(WebSocketOeServer.class));
+        rocketMessageConsumer.setRocketMQConsumer(nameServer, MqGroupEnum.WEBSOCKET_OE_GROUP.getCode(), MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.OE_HARD_FINISH.name() + "||" + MqTagEnum.OE_IM_BROADCASTING.name() + "||" + MqTagEnum.OE_IM_CLUSTERING.name() + "||" + MqTagEnum.OE_LIVENESS_VERIFY.name() + "||" + MqTagEnum.OE_MONITOR_FINISH.name() + "||" + MqTagEnum.OE_WARNING_FINISH.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(WebSocketOeServer.class));
+        rocketMessageConsumer.setRocketMQConsumer(nameServer, MqGroupEnum.WEBSOCKET_OE_MOBILE_GROUP.getCode(), MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_STOP.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(WebSocketMobileServer.class));
         rocketMessageConsumer.setRocketMQConsumer(nameServer, MqGroupEnum.WEBSOCKET_DELAY_GROUP.getCode(), MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.OE_UN_NORMAL.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(WebsocketUnNormalConcurrentlyImpl.class));
         /**
          * websocket mq end

+ 1 - 1
themis-exam/src/main/java/com/qmth/themis/exam/websocket/WebSocketMobileServer.java

@@ -278,7 +278,7 @@ public class WebSocketMobileServer implements Concurrently {
                     mqOeLogicService.execMqMaxReconsumeTime(mqDto, SystemConstant.MQ_TOPIC_BUFFER_LIST);
                 } else {
                     if (Objects.nonNull(mqDto.getAck()) && mqDto.getAck().intValue() != SystemConstant.STANDARD_ACK_TYPE && Objects.nonNull(redisUtil.get(SystemConstant.MQ_TOPIC_BUFFER_LIST, mqDto.getId())) && redisUtil.lock(SystemConstant.REDIS_LOCK_MQ_PREFIX + mqDto.getId(), SystemConstant.REDIS_LOCK_MQ_TIME_OUT)) {
-                        mqOeLogicService.execMqOeLogic(mqDto, SystemConstant.MQ_TOPIC_BUFFER_LIST);
+                        mqOeLogicService.execMqOeMobileLogic(mqDto, SystemConstant.MQ_TOPIC_BUFFER_LIST);
                         return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                     } else {
                         log.info(":{}-:{} 消息ack未确认,重发", threadId, threadName);

+ 62 - 32
themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

@@ -2,15 +2,16 @@ package com.qmth.themis.mq.service.impl;
 
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.google.gson.Gson;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
 import com.qmth.themis.business.cache.RedisKeyHelper;
+import com.qmth.themis.business.cache.bean.ExamCacheBean;
 import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
 import com.qmth.themis.business.constant.SpringContextHolder;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.MqDto;
 import com.qmth.themis.business.dto.WarningDto;
-import com.qmth.themis.business.dto.WebsocketDto;
 import com.qmth.themis.business.entity.*;
 import com.qmth.themis.business.enums.*;
 import com.qmth.themis.business.service.*;
@@ -34,7 +35,6 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 import java.io.IOException;
 import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @Description: mq执行逻辑 impl
@@ -235,24 +235,47 @@ public class MqLogicServiceImpl implements MqLogicService {
                 Long l = (System.currentTimeMillis() - clientLastSyncTime.getTime()) / 1000;
                 if (l >= 2 && !Objects.equals(ExamRecordCacheUtil.getStatus(recordId), ExamRecordStatusEnum.FINISHED)) {
                     diff = l.intValue();
-                    //是否增加断点记录或者交卷处理统一放登录逻辑里处理,这里只更新考试记录状态为中断
                     ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.BREAK_OFF);
                 }
             }
-            //发送考试暂停mq消息 start
-            MqDto mqDtoExamStop = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_STOP.name(), recordId, MqTagEnum.EXAM_STOP, String.valueOf(recordId), String.valueOf(recordId));
-            mqDtoService.assembleSendOneWayMsg(mqDtoExamStop);
-            //发送考试暂停mq消息 end
-
-            //增加异常日志
+            ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(recordId);
             Long examId = ExamRecordCacheUtil.getExamId(recordId);
             Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
             Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
             ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
-            TIeInvigilateExceptionInfo tIeInvigilateExceptionInfo = new TIeInvigilateExceptionInfo(examId, examActivityId, recordId, examStudentId, ExceptionEnum.NET_TIME_OUT.getCode(), ExceptionEnum.NET_TIME_OUT, diff);
-            tIeInvigilateExceptionInfoService.saveOrUpdate(tIeInvigilateExceptionInfo);
-            TEExamStudentLog teExamStudentLog = new TEExamStudentLog(SystemOperationEnum.BREAK_OFF.name(), SystemOperationEnum.BREAK_OFF.getCode(), SystemOperationEnum.BREAK_OFF.getCode(), examStudentCacheBean.getStudentId(), examStudentId, recordId);
-            teExamStudentLogService.save(teExamStudentLog);
+            ExamCacheBean ec = teExamService.getExamCacheBean(examId);//考试缓存
+            Long breakId = null;
+            if (!Objects.equals(status, ExamRecordStatusEnum.FIRST_PREPARE)) {
+                //增加断点记录,获取剩余断点次数
+                Integer alreadyBreakCount = ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
+                Integer leftBreakResumeCount = ec.getBreakResumeCount() - alreadyBreakCount;
+                leftBreakResumeCount = leftBreakResumeCount <= 0 ? 0 : leftBreakResumeCount;
+                if (leftBreakResumeCount <= 0) {
+                    Integer durationSeconds = Objects.isNull(ExamRecordCacheUtil.getDurationSeconds(recordId)) ? 0 : ExamRecordCacheUtil.getDurationSeconds(recordId);
+                    teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
+                } else {
+                    alreadyBreakCount++;
+                    breakId = Constants.idGen.next();
+                    ExamRecordCacheUtil.setLastBreakId(recordId, breakId);
+                    ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.BREAK_OFF);
+                    ExamRecordCacheUtil.setLastBreakTime(recordId);
+                    ExamRecordCacheUtil.setAlreadyBreakCount(recordId, alreadyBreakCount);
+                    ExamRecordCacheUtil.setLastStartTime(recordId);
+                    //考试断点异常原因 发送mq start
+                    MqDto mqDtoBreak = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_BREAK.name(), ExceptionEnum.NET_TIME_OUT, MqTagEnum.EXAM_BREAK, String.valueOf(recordId), String.valueOf(recordId));
+                    mqDtoService.assembleSendOneWayMsg(mqDtoBreak);
+                    //考试断点异常原因 发送mq end
+                }
+                //增加异常日志
+                TIeInvigilateExceptionInfo tIeInvigilateExceptionInfo = new TIeInvigilateExceptionInfo(examId, examActivityId, recordId, examStudentId, ExceptionEnum.NET_TIME_OUT.getCode(), ExceptionEnum.NET_TIME_OUT, diff);
+                tIeInvigilateExceptionInfoService.saveOrUpdate(tIeInvigilateExceptionInfo);
+                TEExamStudentLog teExamStudentLog = new TEExamStudentLog(SystemOperationEnum.BREAK_OFF.name(), SystemOperationEnum.BREAK_OFF.getCode(), SystemOperationEnum.BREAK_OFF.getCode(), examStudentCacheBean.getStudentId(), examStudentId, recordId, breakId);
+                teExamStudentLogService.save(teExamStudentLog);
+            }
+            //发送移动端监考退出考试mq消息 start
+            MqDto mqDtoExamStop = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_STOP.name(), recordId, MqTagEnum.EXAM_STOP, String.valueOf(recordId), String.valueOf(recordId));
+            mqDtoService.assembleSendOneWayMsg(mqDtoExamStop);
+            //发送移动端监考退出考试mq消息 end
         }
         TMRocketMessage tmRocketMessage = gson.fromJson(gson.toJson(mqDto), TMRocketMessage.class);
         Map map = new HashMap();
@@ -481,25 +504,32 @@ public class MqLogicServiceImpl implements MqLogicService {
             JSONObject jsonObject = JSONObject.parseObject(String.valueOf(mqDto.getBody()));
             ExceptionEnum exceptionEnum = ExceptionEnum.valueOf(ExceptionEnum.convertToName(String.valueOf(jsonObject.getJSONObject("reason").get("type"))));
             Long recordId = Long.parseLong(mqDto.getObjId());
-            //获取最近同步时间
-            Date clientLastSyncTime = ExamRecordCacheUtil.getClientLastSyncTime(recordId);
-            Integer diff = 0;
-            if (Objects.nonNull(clientLastSyncTime)) {
-                Long l = ((System.currentTimeMillis() - clientLastSyncTime.getTime()) / 1000);
-                diff = l.intValue();
+            Long breakId = ExamRecordCacheUtil.getLastBreakId(recordId);
+            if (Objects.nonNull(breakId)) {
+                //获取断点时间
+                Date lastBreakTime = ExamRecordCacheUtil.getLastBreakTime(recordId);
+                Integer diff = 0;
+                if (Objects.nonNull(lastBreakTime)) {
+                    Long l = ((System.currentTimeMillis() - lastBreakTime.getTime()) / 1000);
+                    diff = l.intValue();
+                }
+                //修改异常日志
+                String reason = String.valueOf(jsonObject.getJSONObject("reason").get("reason"));
+                UpdateWrapper<TIeInvigilateExceptionInfo> tIeInvigilateExceptionInfoUpdateWrapper = new UpdateWrapper<>();
+                tIeInvigilateExceptionInfoUpdateWrapper.lambda().set(TIeInvigilateExceptionInfo::getInfo, reason)
+                        .set(TIeInvigilateExceptionInfo::getType, exceptionEnum)
+                        .set(TIeInvigilateExceptionInfo::getDifference, diff)
+                        .eq(TIeInvigilateExceptionInfo::getId, breakId);
+                tIeInvigilateExceptionInfoService.update(tIeInvigilateExceptionInfoUpdateWrapper);
+
+                //修改考生轨迹
+                UpdateWrapper<TEExamStudentLog> teExamStudentLogUpdateWrapper = new UpdateWrapper<>();
+                teExamStudentLogUpdateWrapper.lambda().set(TEExamStudentLog::getType, exceptionEnum.name())
+                        .set(TEExamStudentLog::getInfo, exceptionEnum.getCode())
+                        .set(TEExamStudentLog::getRemark, reason)
+                        .eq(TEExamStudentLog::getObjId, breakId);
+                teExamStudentLogService.update(teExamStudentLogUpdateWrapper);
             }
-            Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
-            ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
-
-            //增加异常日志
-            Long examId = ExamRecordCacheUtil.getExamId(recordId);
-            Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
-            String reason = String.valueOf(jsonObject.getJSONObject("reason").get("reason"));
-            TIeInvigilateExceptionInfo tIeInvigilateExceptionInfo = new TIeInvigilateExceptionInfo(examId, examActivityId, recordId, examStudentId, reason, exceptionEnum, diff);
-            tIeInvigilateExceptionInfoService.saveOrUpdate(tIeInvigilateExceptionInfo);
-
-            TEExamStudentLog teExamStudentLog = new TEExamStudentLog(exceptionEnum.name(), exceptionEnum.getCode(), reason, examStudentCacheBean.getStudentId(), examStudentCacheBean.getId(), recordId);
-            teExamStudentLogService.saveOrUpdate(teExamStudentLog);
         } else if (tag.contains(MqTagEnum.WARNING_LOG.name())) {//考试预警日志
             //todo 预警先预留
         }
@@ -571,7 +601,7 @@ public class MqLogicServiceImpl implements MqLogicService {
         }
         //增加断点记录
         TOeExamBreakHistory tOeExamBreakHistory = new TOeExamBreakHistory(ExamRecordCacheUtil.getLastBreakId(recordId), recordId, new Date(), exceptionEnum, exceptionEnum.name());
-        tOeExamBreakHistoryService.save(tOeExamBreakHistory);
+        tOeExamBreakHistoryService.saveOrUpdate(tOeExamBreakHistory);
         redisUtil.setForHash(RedisKeyHelper.examBreakCacheKey(tOeExamBreakHistory.getId()), SimpleBeanUtil.objectToMap(tOeExamBreakHistory));
 
         Gson gson = new Gson();