Ver código fonte

考试过程预警修改

wangliang 4 anos atrás
pai
commit
b0533c542c

+ 107 - 7
themis-backend/src/main/java/com/qmth/themis/backend/api/TEExamController.java

@@ -6,19 +6,18 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.qmth.themis.business.annotation.ApiJsonObject;
 import com.qmth.themis.business.annotation.ApiJsonProperty;
 import com.qmth.themis.business.base.BasePage;
+import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.dto.ExamPropCountDto;
 import com.qmth.themis.business.dto.MqDto;
+import com.qmth.themis.business.dto.cache.TEStudentCacheDto;
 import com.qmth.themis.business.dto.request.TEExamDto;
 import com.qmth.themis.business.dto.response.TEExamQueryDto;
-import com.qmth.themis.business.entity.TBOrg;
-import com.qmth.themis.business.entity.TBUser;
-import com.qmth.themis.business.entity.TEExam;
-import com.qmth.themis.business.entity.TEExamActivity;
+import com.qmth.themis.business.entity.*;
 import com.qmth.themis.business.enums.*;
-import com.qmth.themis.business.service.MqDtoService;
-import com.qmth.themis.business.service.TEExamActivityService;
-import com.qmth.themis.business.service.TEExamService;
+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.common.contanst.Constants;
 import com.qmth.themis.common.enums.ExceptionResultEnum;
@@ -32,6 +31,8 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 /**
  * @Description: 考试批次 前端控制器
@@ -54,6 +55,15 @@ public class TEExamController {
     @Resource
     MqDtoService mqDtoService;
 
+    @Resource
+    TBExamInvigilateUserService tbExamInvigilateUserService;
+
+    @Resource
+    TEExamStudentService teExamStudentService;
+
+    @Resource
+    RedisUtil redisUtil;
+
     @ApiOperation(value = "考试批次修改/新增接口")
     @RequestMapping(value = "/save", method = RequestMethod.POST)
     @Transactional
@@ -322,4 +332,94 @@ public class TEExamController {
         map.put(SystemConstant.RECORDS, basePage);
         return ResultUtil.ok(map);
     }
+
+    @ApiOperation(value = "考试属性统计接口")
+    @RequestMapping(value = "/prop/count", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "考试批次信息", response = TEExam.class)})
+    public Result propCount(@ApiParam(value = "考试id", required = true) @RequestParam Long examId) {
+        if (Objects.isNull(examId) || Objects.equals(examId, "")) {
+            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
+        }
+        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
+        TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
+        if (Objects.isNull(tbSession)) {
+            throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
+        }
+        //首先查询当前监考老师所要监控的roomCode
+        QueryWrapper<TBExamInvigilateUser> examInvigilateUserQueryWrapper = new QueryWrapper<>();
+        examInvigilateUserQueryWrapper.lambda().in(TBExamInvigilateUser::getUserId, tbUser.getId()).eq(TBExamInvigilateUser::getOrgId, tbUser.getOrgId());
+        List<TBExamInvigilateUser> tbExamInvigilateUserList = tbExamInvigilateUserService.list(examInvigilateUserQueryWrapper);
+        Set<String> roomCodeSet = null;
+        if (Objects.nonNull(tbExamInvigilateUserList) && tbExamInvigilateUserList.size() > 0) {
+            roomCodeSet = tbExamInvigilateUserList.stream().map(s -> s.getRoomCode()).collect(Collectors.toSet());
+        }
+        if (Objects.nonNull(roomCodeSet) || roomCodeSet.size() == 0) {
+            throw new BusinessException("当前监考老师未设置任何监考考场");
+        }
+        //根据roomCode获取当前老师所要监考的全部应考学生数
+        QueryWrapper<TEExamStudent> teExamStudentServiceQueryWrapper = new QueryWrapper<>();
+        teExamStudentServiceQueryWrapper.lambda().in(TEExamStudent::getRoomCode, roomCodeSet);
+        List<TEExamStudent> teExamStudentList = teExamStudentService.list(teExamStudentServiceQueryWrapper);
+        Set<String> studentSet = null;
+        Set<Long> examActivityIdSet = null;
+        Integer allCount = 0;
+        Integer loginCount = 0;
+        AtomicReference<Integer> prepareCount = new AtomicReference<>(0);
+        AtomicReference<Integer> examCount = new AtomicReference<>(0);
+        AtomicReference<Integer> clientCommunicationStatusCount = new AtomicReference<>(0);
+        AtomicReference<Integer> monitorStatusSourceCount = new AtomicReference<>(0);
+        AtomicReference<Integer> alreadyComplete = new AtomicReference<>(0);
+        Integer notComplete = 0;
+        if (Objects.nonNull(teExamStudentList) && teExamStudentList.size() > 0) {
+            teExamStudentList.forEach(s -> {
+                studentSet.add(SystemConstant.STUDENT + s.getStudentId());
+                examActivityIdSet.add(s.getExamActivityId());
+            });
+            allCount = studentSet.size();
+        }
+
+        //获取已登录学生
+        List<TEStudentCacheDto> teStudentCacheDto = (List<TEStudentCacheDto>) redisUtil.multiGet(studentSet);
+        if (Objects.nonNull(teExamStudentList) && teExamStudentList.size() > 0) {
+            loginCount = teStudentCacheDto.size();
+        }
+
+        //获取已待考、考试中、已完成学生
+        examActivityIdSet.forEach(s -> {
+            Map<Long, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examActivityRecordCacheKey(s));
+            if (Objects.nonNull(objectMap) && objectMap.size() > 0) {
+                objectMap.forEach((k, v) -> {
+                    Map<String, Object> recordIdObjectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(k));
+                    if (Objects.nonNull(recordIdObjectMap) && recordIdObjectMap.size() > 0) {
+                        //客户端通讯状态
+                        Integer clientStatus = Objects.isNull(recordIdObjectMap.get("clientWebsocketStatus")) ? 0 : (Integer) recordIdObjectMap.get("clientWebsocketStatus");
+                        if (clientStatus == 0) {
+                            clientCommunicationStatusCount.getAndSet(clientCommunicationStatusCount.get() + 1);
+                        }
+                        //监控端通讯状态
+                        MonitorStatusSourceEnum monitorStatus = Objects.isNull(recordIdObjectMap.get("monitorStatusSource")) ? null : (MonitorStatusSourceEnum) recordIdObjectMap.get("monitorStatusSource");
+                        if (Objects.nonNull(monitorStatus) && Objects.equals(monitorStatus, MonitorStatusSourceEnum.STOP)) {
+                            monitorStatusSourceCount.getAndSet(monitorStatusSourceCount.get() + 1);
+                        }
+                    }
+                    ExamRecordStatusEnum examRecordStatusEnum = (ExamRecordStatusEnum) v;
+                    //已待考
+                    if (Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.FIRST_PREPARE)) {
+                        prepareCount.getAndSet(prepareCount.get() + 1);
+                    }
+                    //考试中
+                    else if (Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.ANSWERING) || Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.RESUME_PREPARE)) {
+                        examCount.getAndSet(examCount.get() + 1);
+                    }
+                    //已完成
+                    else if (Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.FINISHED) || Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.PERSISTED)) {
+                        alreadyComplete.getAndSet(alreadyComplete.get() + 1);
+                    }
+                });
+            }
+        });
+        notComplete = allCount - alreadyComplete.get();
+        ExamPropCountDto examPropCountDto = new ExamPropCountDto(examId, allCount, loginCount, prepareCount.get(), examCount.get(), clientCommunicationStatusCount.get(), monitorStatusSourceCount.get(), alreadyComplete.get(), notComplete);
+        return ResultUtil.ok(examPropCountDto);
+    }
 }

+ 1 - 1
themis-backend/src/main/java/com/qmth/themis/backend/websocket/WebSocketAdminServer.java

@@ -159,7 +159,7 @@ public class WebSocketAdminServer implements Concurrently {
                     //todo 加入当前时间和time比较的校验
                     Method method = webSocketAdminMessageTemplete.getClass().getDeclaredMethod(WebsocketTypeEnum.valueOf(websocketDto.getType()).getDesc(), String.class);
                     WebsocketDto result = (WebsocketDto) method.invoke(webSocketAdminMessageTemplete, String.valueOf(websocketDto.getBody()));
-                    this.sendMessage(JSONObject.toJSONString(result));
+                    this.sendMessage(result);
                 }
             } catch (Exception e) {
                 e.printStackTrace();

+ 111 - 0
themis-business/src/main/java/com/qmth/themis/business/dto/ExamPropCountDto.java

@@ -0,0 +1,111 @@
+package com.qmth.themis.business.dto;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 考试属性统计
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2020/8/19
+ */
+public class ExamPropCountDto implements Serializable {
+
+    private Long examId;//考试批次id
+    private Integer allCount;//应考人数
+    private Integer loginCount;//已登录
+    private Integer prepareCount;//已待考
+    private Integer examCount;//考试中
+    private Integer clientCommunicationStatusCount;//客户端通讯故障
+    private Integer monitorStatusSourceCount;//监控设备通讯故障
+    private Integer alreadyComplete;//已完成
+    private Integer notComplete;//未完成
+
+    public ExamPropCountDto() {
+
+    }
+
+    public ExamPropCountDto(Long examId, Integer allCount, Integer loginCount, Integer prepareCount, Integer examCount, Integer clientCommunicationStatusCount, Integer monitorStatusSourceCount, Integer alreadyComplete, Integer notComplete) {
+        this.examId = examId;
+        this.allCount = allCount;
+        this.loginCount = loginCount;
+        this.prepareCount = prepareCount;
+        this.examCount = examCount;
+        this.clientCommunicationStatusCount = clientCommunicationStatusCount;
+        this.monitorStatusSourceCount = monitorStatusSourceCount;
+        this.alreadyComplete = alreadyComplete;
+        this.notComplete = notComplete;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public Integer getAllCount() {
+        return allCount;
+    }
+
+    public void setAllCount(Integer allCount) {
+        this.allCount = allCount;
+    }
+
+    public Integer getLoginCount() {
+        return loginCount;
+    }
+
+    public void setLoginCount(Integer loginCount) {
+        this.loginCount = loginCount;
+    }
+
+    public Integer getPrepareCount() {
+        return prepareCount;
+    }
+
+    public void setPrepareCount(Integer prepareCount) {
+        this.prepareCount = prepareCount;
+    }
+
+    public Integer getExamCount() {
+        return examCount;
+    }
+
+    public void setExamCount(Integer examCount) {
+        this.examCount = examCount;
+    }
+
+    public Integer getClientCommunicationStatusCount() {
+        return clientCommunicationStatusCount;
+    }
+
+    public void setClientCommunicationStatusCount(Integer clientCommunicationStatusCount) {
+        this.clientCommunicationStatusCount = clientCommunicationStatusCount;
+    }
+
+    public Integer getMonitorStatusSourceCount() {
+        return monitorStatusSourceCount;
+    }
+
+    public void setMonitorStatusSourceCount(Integer monitorStatusSourceCount) {
+        this.monitorStatusSourceCount = monitorStatusSourceCount;
+    }
+
+    public Integer getAlreadyComplete() {
+        return alreadyComplete;
+    }
+
+    public void setAlreadyComplete(Integer alreadyComplete) {
+        this.alreadyComplete = alreadyComplete;
+    }
+
+    public Integer getNotComplete() {
+        return notComplete;
+    }
+
+    public void setNotComplete(Integer notComplete) {
+        this.notComplete = notComplete;
+    }
+}

+ 19 - 3
themis-business/src/main/java/com/qmth/themis/business/util/RedisUtil.java

@@ -162,6 +162,20 @@ public class RedisUtil {
         return redisTemplate.opsForValue().multiGet(keys);
     }
 
+    /**
+     * 获取key like
+     *
+     * @param key
+     * @return
+     */
+    public Set<?> getKeyPatterns(String key) {
+        if (null != key) {
+            return redisTemplate.keys(key);
+        } else {
+            return null;
+        }
+    }
+
 
     /**
      * 设置hash
@@ -299,12 +313,14 @@ public class RedisUtil {
     public void setForHash(String key, Map<String, Object> map) {
         redisTemplate.opsForHash().putAll(key, map);
     }
-    
-    /**设置过期时间(秒)
+
+    /**
+     * 设置过期时间(秒)
+     *
      * @param key
      * @param timeOutSecond
      */
-    public void expire(String key,int timeOutSecond) {
+    public void expire(String key, int timeOutSecond) {
         redisTemplate.expire(key, timeOutSecond, TimeUnit.SECONDS);
     }
 }

+ 3 - 4
themis-task/src/main/java/com/qmth/themis/task/quartz/service/impl/QuartzLogicServiceImpl.java

@@ -52,18 +52,17 @@ public class QuartzLogicServiceImpl implements QuartzLogicService {
         teExamActivityQueryWrapper.lambda().eq(TEExamActivity::getCode, strings[1]);
         TEExamActivity teExamActivity = teExamActivityService.getOne(teExamActivityQueryWrapper);
         if (Objects.nonNull(teExamActivity) && teExamActivity.getEnable() == 1) {
-            Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examActivityRecordCacheKey(teExamActivity.getId()));
+            Map<Long, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examActivityRecordCacheKey(teExamActivity.getId()));
             if (Objects.nonNull(objectMap) && objectMap.size() > 0) {
                 objectMap.forEach((k, v) -> {
                     ExamRecordStatusEnum examRecordStatusEnum = (ExamRecordStatusEnum) v;
                     //获取该考试批次下所有未交卷的考生的考试记录
                     if (Objects.nonNull(examRecordStatusEnum) && !Objects.equals(ExamRecordStatusEnum.FINISHED, examRecordStatusEnum)) {
-                        Long recordId = Long.parseLong(k);
-                        Map<String, Object> recordObjectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
+                        Map<String, Object> recordObjectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(k));
                         Integer durationSeconds = Integer.parseInt(String.valueOf(recordObjectMap.get("durationSeconds")));
                         Long studentId = Long.parseLong(String.valueOf(recordObjectMap.get("studentId")));
                         //交卷
-                        teExamService.finish(studentId, recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
+                        teExamService.finish(studentId, k, FinishTypeEnum.AUTO.name(), durationSeconds);
                     }
                 });
             }