wangliang 4 سال پیش
والد
کامیت
16646de5df
21فایلهای تغییر یافته به همراه268 افزوده شده و 53 حذف شده
  1. 2 6
      themis-backend/src/main/java/com/qmth/themis/backend/api/SysController.java
  2. 1 2
      themis-backend/src/main/java/com/qmth/themis/backend/api/TBUserController.java
  3. 17 1
      themis-backend/src/main/java/com/qmth/themis/backend/config/WebMvcConfig.java
  4. 63 0
      themis-backend/src/main/java/com/qmth/themis/backend/interceptor/AuthThirdInterceptor.java
  5. 16 0
      themis-business/src/main/java/com/qmth/themis/business/cache/ExamRecordCacheUtil.java
  6. 2 0
      themis-business/src/main/java/com/qmth/themis/business/constant/SystemConstant.java
  7. 11 0
      themis-business/src/main/java/com/qmth/themis/business/dao/TOeFaceVerifyHistoryMapper.java
  8. 10 0
      themis-business/src/main/java/com/qmth/themis/business/domain/PrefixUrlDomain.java
  9. 13 1
      themis-business/src/main/java/com/qmth/themis/business/entity/TEConfig.java
  10. 2 2
      themis-business/src/main/java/com/qmth/themis/business/enums/VerifyExceptionEnum.java
  11. 4 2
      themis-business/src/main/java/com/qmth/themis/business/enums/WarningLevelEnum.java
  12. 11 0
      themis-business/src/main/java/com/qmth/themis/business/service/TOeFaceVerifyHistoryService.java
  13. 14 0
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeFaceVerifyHistoryServiceImpl.java
  14. 24 5
      themis-business/src/main/java/com/qmth/themis/business/service/impl/WarningServiceImpl.java
  15. 3 2
      themis-business/src/main/resources/db/init.sql
  16. 1 1
      themis-business/src/main/resources/mapper/TIeInvigilateWarnInfoMapper.xml
  17. 16 20
      themis-business/src/main/resources/mapper/TOeExamRecordMapper.xml
  18. 30 0
      themis-business/src/main/resources/mapper/TOeFaceVerifyHistoryMapper.xml
  19. 1 5
      themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java
  20. 6 0
      themis-exam/src/main/java/com/qmth/themis/exam/config/WebMvcConfig.java
  21. 21 6
      themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

+ 2 - 6
themis-backend/src/main/java/com/qmth/themis/backend/api/SysController.java

@@ -183,9 +183,7 @@ public class SysController {
         } else {
             url = "http://" + dictionaryConfig.sysDomain().getFileHost() + File.separator + filePath;
         }
-        Map map = new HashMap();
-        map.put("url", url);
-        return ResultUtil.ok(map);
+        return ResultUtil.ok(Collections.singletonMap("url", url));
     }
 
     @ApiOperation(value = "根据机构代码查询机构信息接口")
@@ -207,9 +205,7 @@ public class SysController {
         if (Objects.isNull(tbOrg)) {
             throw new BusinessException(ExceptionResultEnum.ORG_NO);
         }
-        Map map = new HashMap<>();
-        map.put("logo", tbOrg.getLogo());
-        return ResultUtil.ok(map);
+        return ResultUtil.ok(Collections.singletonMap("logo", tbOrg.getLogo()));
     }
 
     @ApiOperation(value = "机构查询接口")

+ 1 - 2
themis-backend/src/main/java/com/qmth/themis/backend/api/TBUserController.java

@@ -649,8 +649,7 @@ public class TBUserController {
         if (Objects.isNull(loginName) || Objects.equals(loginName, "")) {
             throw new BusinessException(ExceptionResultEnum.LOGIN_NAME_IS_NULL);
         }
-        Map map = new HashMap();
-        return ResultUtil.ok(map);
+        return ResultUtil.ok(Collections.singletonMap(SystemConstant.SUCCESS, true));
     }
 
     @ApiOperation(value = "获取短信验证码接口")

+ 17 - 1
themis-backend/src/main/java/com/qmth/themis/backend/config/WebMvcConfig.java

@@ -1,13 +1,17 @@
 package com.qmth.themis.backend.config;
 
 import com.qmth.themis.backend.interceptor.AuthInterceptor;
+import com.qmth.themis.backend.interceptor.AuthThirdInterceptor;
 import com.qmth.themis.business.constant.SystemConstant;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 import javax.annotation.Resource;
+import java.awt.*;
+import java.util.List;
 
 /**
  * @Description: 路径拦截器
@@ -27,9 +31,15 @@ public class WebMvcConfig implements WebMvcConfigurer {
         return new AuthInterceptor();
     }
 
+    @Bean
+    public AuthThirdInterceptor AuthThirdInterceptor() {
+        return new AuthThirdInterceptor();
+    }
+
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
-        registry.addInterceptor(AuthInterceptor()).addPathPatterns(SystemConstant.ALL_PATH).excludePathPatterns(dictionaryConfig.authNoUrlDomain().getUrls());
+        registry.addInterceptor(AuthInterceptor()).addPathPatterns(SystemConstant.ADMIN_ALL_PATH).excludePathPatterns(dictionaryConfig.authNoUrlDomain().getUrls());
+        registry.addInterceptor(AuthThirdInterceptor()).addPathPatterns(SystemConstant.THIRD_ALL_PATH).excludePathPatterns(dictionaryConfig.authNoUrlDomain().getUrls());;
     }
 
 //    @Override
@@ -55,4 +65,10 @@ public class WebMvcConfig implements WebMvcConfigurer {
 //            }
 //        }
 //    }
+
+//    @Override
+//    public void addCorsMappings(CorsRegistry registry) {
+//        registry.addMapping("/**").allowedOrigins("*").allowCredentials(true)
+//                .allowedMethods("GET", "POST", "DELETE", "PUT").maxAge(3600);
+//    }
 }

+ 63 - 0
themis-backend/src/main/java/com/qmth/themis/backend/interceptor/AuthThirdInterceptor.java

@@ -0,0 +1,63 @@
+package com.qmth.themis.backend.interceptor;
+
+import com.qmth.themis.backend.config.DictionaryConfig;
+import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.dto.AuthDto;
+import com.qmth.themis.business.entity.TBSession;
+import com.qmth.themis.business.entity.TBUser;
+import com.qmth.themis.business.service.CacheService;
+import com.qmth.themis.business.service.TBUserService;
+import com.qmth.themis.business.util.RedisUtil;
+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 com.qmth.themis.common.signature.SignatureInfo;
+import com.qmth.themis.common.signature.SignatureType;
+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;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @Description: 第三方接口鉴权拦截器
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2020/10/16
+ */
+public class AuthThirdInterceptor implements HandlerInterceptor {
+    private final static Logger log = LoggerFactory.getLogger(AuthThirdInterceptor.class);
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
+        log.info("backend authThirdInterceptor preHandle is come in");
+        String url = request.getServletPath();
+        String method = request.getMethod();
+        if (url.equalsIgnoreCase(SystemConstant.ERROR)) {
+            throw new BusinessException(ExceptionResultEnum.NOT_FOUND);
+        }
+        response.setStatus(ExceptionResultEnum.SUCCESS.getStatusCode());
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request,
+                           HttpServletResponse response,
+                           Object o, ModelAndView modelAndView) throws Exception {
+
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request,
+                                HttpServletResponse response,
+                                Object o, Exception e) throws Exception {
+    }
+}

+ 16 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/ExamRecordCacheUtil.java

@@ -400,6 +400,10 @@ public class ExamRecordCacheUtil {
         redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), "faceCompareErrorRandom", faceCompareErrorRandom);
     }
 
+    public static void setEyeCloseErrorRandom(Long recordId, String eyeCloseErrorRandom) {
+        redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), "eyeCloseErrorRandom", eyeCloseErrorRandom);
+    }
+
     public static void setMultipleFaceCountErrorNum(Long recordId, Integer multipleFaceCountErrorNum) {
         redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), "multipleFaceCountErrorNum", multipleFaceCountErrorNum);
     }
@@ -412,6 +416,10 @@ public class ExamRecordCacheUtil {
         redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), "faceCompareErrorNum", faceCompareErrorNum);
     }
 
+    public static void setEyeCloseErrorNum(Long recordId, Integer eyeCloseErrorNum) {
+        redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), "eyeCloseErrorNum", eyeCloseErrorNum);
+    }
+
     public static String getMultipleFaceCountErrorRandom(Long recordId) {
         return (String) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "multipleFaceCountErrorRandom");
     }
@@ -424,6 +432,10 @@ public class ExamRecordCacheUtil {
         return (String) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "faceCompareErrorRandom");
     }
 
+    public static String getEyeCloseErrorRandom(Long recordId) {
+        return (String) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "eyeCloseErrorRandom");
+    }
+
     public static Integer getMultipleFaceCountErrorNum(Long recordId) {
         return Objects.nonNull(redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "multipleFaceCountErrorNum")) ? (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "multipleFaceCountErrorNum") : 0;
     }
@@ -436,6 +448,10 @@ public class ExamRecordCacheUtil {
         return Objects.nonNull(redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "faceCompareErrorNum")) ? (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "faceCompareErrorNum") : 0;
     }
 
+    public static Integer getEyeCloseErrorNum(Long recordId) {
+        return Objects.nonNull(redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "eyeCloseErrorNum")) ? (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "eyeCloseErrorNum") : 0;
+    }
+
     public static Integer getHasAnswerFile(Long recordId) {
         return (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), ExamRecordFieldEnum.has_answer_file.getCode());
     }

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

@@ -48,6 +48,8 @@ public class SystemConstant {
     public static final String ORG = "org:cache:";
     public static final String LINK = "LINK";
     public static final String ALL_PATH = "/**";
+    public static final String ADMIN_ALL_PATH = "/api/admin/**";
+    public static final String THIRD_ALL_PATH = "/api/open/**";
     public static final String ACCOUNT = "account";
     public static final String STUDENT_ACCOUNT = "student";
     public static final String COUNT = "count";

+ 11 - 0
themis-business/src/main/java/com/qmth/themis/business/dao/TOeFaceVerifyHistoryMapper.java

@@ -40,4 +40,15 @@ public interface TOeFaceVerifyHistoryMapper extends BaseMapper<TOeFaceVerifyHist
      * @return
      */
     public Map<String, Object> faceCompareError(@Param("recordId") Long recordId, @Param("exception") String exception, @Param("type") String type, @Param("batchNo") String batchNo);
+
+    /**
+     * 闭眼检测异常
+     *
+     * @param recordId
+     * @param exception
+     * @param type
+     * @param batchNo
+     * @return
+     */
+    public Map<String, Object> eyeCloseError(@Param("recordId") Long recordId, @Param("exception") String exception, @Param("type") String type, @Param("batchNo") String batchNo);
 }

+ 10 - 0
themis-business/src/main/java/com/qmth/themis/business/domain/PrefixUrlDomain.java

@@ -19,6 +19,8 @@ public class PrefixUrlDomain implements Serializable {
 
     String mobile;
 
+    String third;
+
     public String getAdmin() {
         return admin;
     }
@@ -50,4 +52,12 @@ public class PrefixUrlDomain implements Serializable {
     public void setMobile(String mobile) {
         this.mobile = mobile;
     }
+
+    public String getThird() {
+        return third;
+    }
+
+    public void setThird(String third) {
+        this.third = third;
+    }
 }

+ 13 - 1
themis-business/src/main/java/com/qmth/themis/business/entity/TEConfig.java

@@ -57,7 +57,7 @@ public class TEConfig implements Serializable {
     @TableField(value = "multiple_face_count_error")
     private Integer multipleFaceCountError;
 
-    @ApiModelProperty(value = "一个考试场次内人脸抓拍检测(D12)失败累计次数超过6次")
+    @ApiModelProperty(value = "一个考试场次中检测到人脸与底照不符次数超过3次(1分钟内80%检测到人脸与底照不符为1次)")
     @TableField(value = "total_face_compare_error_count")
     private Integer totalFaceCompareErrorCount;
 
@@ -65,6 +65,18 @@ public class TEConfig implements Serializable {
     @TableField(value = "in_process_face_verify_config")
     private String inProcessFaceVerifyConfig;
 
+    @ApiModelProperty(value = "一个考试场次内人脸抓拍检测(D13:1分钟内进行连续人脸抓拍检测不到闭眼动作)失败累计次数超过6次")
+    @TableField(value = "total_eye_close_error_count")
+    private Integer totalEyeCloseErrorCount;
+
+    public Integer getTotalEyeCloseErrorCount() {
+        return totalEyeCloseErrorCount;
+    }
+
+    public void setTotalEyeCloseErrorCount(Integer totalEyeCloseErrorCount) {
+        this.totalEyeCloseErrorCount = totalEyeCloseErrorCount;
+    }
+
     public String getInProcessFaceVerifyConfig() {
         return inProcessFaceVerifyConfig;
     }

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

@@ -14,9 +14,9 @@ public enum VerifyExceptionEnum {
 
     FACE_COUNT_ERROR("人脸数量异常", Arrays.asList("D4", "D8"), "违纪预警"),
 
-    FACE_COMPARE_ERROR("人脸比对异常", Arrays.asList("D6", "D14"), "违纪预警"),
+    FACE_COMPARE_ERROR("人脸比对异常", Arrays.asList("D6"), "违纪预警"),
 
-    EYE_CLOSE_ERROR("闭眼检测异常", Arrays.asList("-1"), "违纪预警"),
+    EYE_CLOSE_ERROR("闭眼检测异常", Arrays.asList("D14"), "违纪预警"),
 
     LIVENESS_ACTION_ERROR("活检动作错误", Arrays.asList("-1"), "违纪预警"),
 

+ 4 - 2
themis-business/src/main/java/com/qmth/themis/business/enums/WarningLevelEnum.java

@@ -15,9 +15,11 @@ public enum WarningLevelEnum {
 
     D6("一个考试场次中检测到人脸与底照不符次数超过3次(持续30秒连续帧画面90%比对底照不符记为次数1次)", "【疑似替考】系统多次检测到当前考生身份不符", "违纪预警"),
 
-    D14("一个考试场次内人脸抓拍检测(D12)失败累计次数超过6次", "疑似采用照片】系统检测到考生疑似在镜头前采用照片", "违纪预警"),
+    D14("一个考试场次内人脸抓拍检测(D13)失败累计次数超过6次", "疑似采用照片】系统检测到考生疑似在镜头前采用照片", "违纪预警"),
 
-    D15("真实性检测失败1次以上", "【疑似采用照片或虚拟摄像头】系统随机真实性检测失败", "违纪预警");
+    D15("真实性检测失败1次以上", "【疑似采用照片或虚拟摄像头】系统随机真实性检测失败", "违纪预警"),
+
+    D13("1分钟内进行连续人脸抓拍检测不到闭眼动作", "【疑似采用照片】系统检测到考生疑似在镜头前采用照片", "违纪预警");
 
     private String desc;
 

+ 11 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TOeFaceVerifyHistoryService.java

@@ -44,4 +44,15 @@ public interface TOeFaceVerifyHistoryService extends IService<TOeFaceVerifyHisto
      * @return
      */
     public Map<String, Object> faceCompareError(Long recordId, String exception, String type, String batchNo);
+
+    /**
+     * 闭眼检测异常
+     *
+     * @param recordId
+     * @param exception
+     * @param type
+     * @param batchNo
+     * @return
+     */
+    public Map<String, Object> eyeCloseError(Long recordId, String exception, String type, String batchNo);
 }

+ 14 - 0
themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeFaceVerifyHistoryServiceImpl.java

@@ -109,4 +109,18 @@ public class TOeFaceVerifyHistoryServiceImpl extends ServiceImpl<TOeFaceVerifyHi
     public Map<String, Object> faceCompareError(Long recordId, String exception, String type, String batchNo) {
         return tOeFaceVerifyHistoryMapper.faceCompareError(recordId, exception, type, batchNo);
     }
+
+    /**
+     * 闭眼检测异常
+     *
+     * @param recordId
+     * @param exception
+     * @param type
+     * @param batchNo
+     * @return
+     */
+    @Override
+    public Map<String, Object> eyeCloseError(Long recordId, String exception, String type, String batchNo) {
+        return tOeFaceVerifyHistoryMapper.faceCompareError(recordId, exception, type, batchNo);
+    }
 }

+ 24 - 5
themis-business/src/main/java/com/qmth/themis/business/service/impl/WarningServiceImpl.java

@@ -69,7 +69,7 @@ public class WarningServiceImpl implements WarningService {
             if (Objects.nonNull(map) && map.size() > 0) {
                 Integer count = Integer.parseInt(String.valueOf(map.get("tmpCount")));
                 count = Objects.isNull(count) ? 0 : count;
-                if (count >= teConfig.getMultipleFaceCountError()) {
+                if (count > teConfig.getMultipleFaceCountError()) {
                     TIeInvigilateWarnInfo tIeInvigilateWarnInfo = new TIeInvigilateWarnInfo(examId, examActivityId, recordId, examStudentId, warningEnum.getLevel().get(1), WarningLevelEnum.valueOf(warningEnum.getLevel().get(1)).getTitle(), warningEnum, photoUrl, Objects.nonNull(ExamRecordCacheUtil.getBreachStatus(recordId)) ? ExamRecordCacheUtil.getBreachStatus(recordId) : 1);
                     tIeInvigilateWarnInfoService.saveOrUpdate(tIeInvigilateWarnInfo);
                     this.setWarningCount(recordId);
@@ -81,7 +81,7 @@ public class WarningServiceImpl implements WarningService {
             if (Objects.nonNull(map) && map.size() > 0) {
                 Integer count = Integer.parseInt(String.valueOf(map.get("tmpCount")));
                 count = Objects.isNull(count) ? 0 : count;
-                if (count >= teConfig.getNoFaceCountError()) {
+                if (count > teConfig.getNoFaceCountError()) {
                     TIeInvigilateWarnInfo tIeInvigilateWarnInfo = new TIeInvigilateWarnInfo(examId, examActivityId, recordId, examStudentId, warningEnum.getLevel().get(0), WarningLevelEnum.valueOf(warningEnum.getLevel().get(0)).getTitle(), warningEnum, photoUrl, Objects.nonNull(ExamRecordCacheUtil.getBreachStatus(recordId)) ? ExamRecordCacheUtil.getBreachStatus(recordId) : 1);
                     tIeInvigilateWarnInfoService.saveOrUpdate(tIeInvigilateWarnInfo);
                     this.setWarningCount(recordId);
@@ -111,13 +111,13 @@ public class WarningServiceImpl implements WarningService {
         if (Objects.nonNull(map) && map.size() > 0) {
             Integer count = Integer.parseInt(String.valueOf(map.get("tmpCount")));
             count = Objects.isNull(count) ? 0 : count;
-            if (count >= teConfig.getMatchFaceCompareErrorCount()) {
+            if (count > teConfig.getMatchFaceCompareErrorCount()) {
                 TIeInvigilateWarnInfo tIeInvigilateWarnInfo = new TIeInvigilateWarnInfo(examId, examActivityId, recordId, examStudentId, warningEnum.getLevel().get(0), WarningLevelEnum.valueOf(warningEnum.getLevel().get(0)).getTitle(), warningEnum, photoUrl, Objects.nonNull(ExamRecordCacheUtil.getBreachStatus(recordId)) ? ExamRecordCacheUtil.getBreachStatus(recordId) : 1);
                 tIeInvigilateWarnInfoService.saveOrUpdate(tIeInvigilateWarnInfo);
                 this.setWarningCount(recordId);
                 this.setPhotoUrls(map, tIeInvigilateWarnInfo, examStudentCacheBean, recordId, teConfig.getMatchFaceCompareErrorCount());
             }
-            if (count >= teConfig.getTotalFaceCompareErrorCount()) {
+            if (count > teConfig.getTotalFaceCompareErrorCount()) {
                 TIeInvigilateWarnInfo tIeInvigilateWarnInfo = new TIeInvigilateWarnInfo(examId, examActivityId, recordId, examStudentId, warningEnum.getLevel().get(1), WarningLevelEnum.valueOf(warningEnum.getLevel().get(1)).getTitle(), warningEnum, photoUrl, Objects.nonNull(ExamRecordCacheUtil.getBreachStatus(recordId)) ? ExamRecordCacheUtil.getBreachStatus(recordId) : 1);
                 tIeInvigilateWarnInfoService.saveOrUpdate(tIeInvigilateWarnInfo);
                 this.setWarningCount(recordId);
@@ -132,8 +132,27 @@ public class WarningServiceImpl implements WarningService {
      * @param warningDto
      */
     @Override
+    @Transactional
     public void eyeCloseError(WarningDto warningDto) {
-
+        Long recordId = warningDto.getRecordId();
+        VerifyExceptionEnum warningEnum = warningDto.getWarningEnum();
+        String photoUrl = warningDto.getPhotoUrl();
+        TEConfig teConfig = teConfigService.getGlobalConfig();
+        Long examId = ExamRecordCacheUtil.getExamId(recordId);
+        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
+        Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
+        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
+        Map<String, Object> map = faceVerifyHistoryService.eyeCloseError(recordId, warningDto.getWarningEnum().name(), ExamTypeEnum.IN_PROCESS.name(), warningDto.getRandom());
+        if (Objects.nonNull(map) && map.size() > 0) {
+            Integer count = Integer.parseInt(String.valueOf(map.get("tmpCount")));
+            count = Objects.isNull(count) ? 0 : count;
+            if (count > teConfig.getTotalEyeCloseErrorCount()) {
+                TIeInvigilateWarnInfo tIeInvigilateWarnInfo = new TIeInvigilateWarnInfo(examId, examActivityId, recordId, examStudentId, warningEnum.getLevel().get(0), WarningLevelEnum.valueOf(warningEnum.getLevel().get(0)).getTitle(), warningEnum, photoUrl, Objects.nonNull(ExamRecordCacheUtil.getBreachStatus(recordId)) ? ExamRecordCacheUtil.getBreachStatus(recordId) : 1);
+                tIeInvigilateWarnInfoService.saveOrUpdate(tIeInvigilateWarnInfo);
+                this.setWarningCount(recordId);
+                this.setPhotoUrls(map, tIeInvigilateWarnInfo, examStudentCacheBean, recordId, teConfig.getMatchFaceCompareErrorCount());
+            }
+        }
     }
 
     /**

+ 3 - 2
themis-business/src/main/resources/db/init.sql

@@ -871,8 +871,9 @@ CREATE TABLE `t_e_config` (
   `match_face_compare_error_count` int DEFAULT NULL COMMENT '一个考试场次中检测到人脸与底照不符次数超过3次(持续30秒连续帧画面90%比对底照不符记为次数1次)',
   `realness_count` int DEFAULT NULL COMMENT '真实性检测数量',
   `multiple_face_count_error` int DEFAULT NULL COMMENT '一个考试场次中检测到多张人脸超过3次(持续15秒连续帧画面90%存在多张人脸记为次数1次)',
-  `total_face_compare_error_count` int DEFAULT NULL COMMENT '一个考试场次内人脸抓拍检测(D12)失败累计次数超过6次',
+  `total_face_compare_error_count` int DEFAULT NULL COMMENT '一个考试场次中检测到人脸与底照不符次数超过3次(1分钟内80%检测到人脸与底照不符为1次)',
   `in_process_face_verify_config` mediumtext COMMENT '过程人脸验证参数配置',
+  `total_eye_close_error_count` int DEFAULT NULL COMMENT '一个考试场次内人脸抓拍检测(D13:1分钟内进行连续人脸抓拍检测不到闭眼动作)失败累计次数超过6次',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='全局配置';
 
@@ -880,7 +881,7 @@ CREATE TABLE `t_e_config` (
 -- Records of t_e_config
 -- ----------------------------
 BEGIN;
-INSERT INTO `t_e_config` VALUES (1, 0.4, 3, 3, 30, 3, 3, 1, 3, 6, '{\"faceDetectIntervalSeconds\":1,\"faceDetectContinueSeconds\":30,\"faceDetectWarningThreshold\":3,\"faceCompareIntervalSeconds\":2,\"faceCompareContinueSeconds\":60,\"faceCompareFailurePercent\":80,\"faceCompareWarningThreshold\":3,\"eyeCloseDetectIntervalSeconds\":900,\"eyeCloseDetectContinueSeconds\":60,\"eyeCloseDetectWarningThreshold\":6,\"realnessDetectIntervalSeconds\":120,\"forceFinishThreshold\":3,\"forceFinishCondition\":[\"faceDetect\",\"faceCompare\"]}');
+INSERT INTO `t_e_config` VALUES (1, 0.4, 3, 3, 30, 3, 3, 1, 3, 3, '{\"faceDetectIntervalSeconds\":1,\"faceDetectContinueSeconds\":30,\"faceDetectWarningThreshold\":3,\"faceCompareIntervalSeconds\":2,\"faceCompareContinueSeconds\":60,\"faceCompareFailurePercent\":80,\"faceCompareWarningThreshold\":3,\"eyeCloseDetectIntervalSeconds\":900,\"eyeCloseDetectContinueSeconds\":60,\"eyeCloseDetectWarningThreshold\":6,\"realnessDetectIntervalSeconds\":120,\"forceFinishThreshold\":3,\"forceFinishCondition\":[\"faceDetect\",\"faceCompare\"]}', 6);
 COMMIT;
 
 -- ----------------------------

+ 1 - 1
themis-business/src/main/resources/mapper/TIeInvigilateWarnInfoMapper.xml

@@ -11,7 +11,7 @@
         <if test="userId != null and userId != ''">
             tbeiu.user_id = #{userId} and
         </if>
-        tbeiu.room_code = tees.room_code and toer.exam_student_id = tees.id))) t on t.id = tiiwi.exam_record_id
+        tbeiu.room_code = tees.room_code and toer.exam_student_id = tees.id and toer.exam_id = tbeiu.exam_id))) t on t.id = tiiwi.exam_record_id
         <where>
             <if test="examId != null and examId != ''">
                 and tiiwi.exam_id = #{examId}

+ 16 - 20
themis-business/src/main/resources/mapper/TOeExamRecordMapper.xml

@@ -212,11 +212,10 @@
         ,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and
         tiiwi.approve_status = 0) as warningNew
 		,case
-		when tee.mode = 'ANYTIME' then
-		date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s')
-		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then
-		date_format(date_sub(from_unixtime(IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000),INTERVAL 8 HOUR), '%H:%i:%s')
-		END	as remainTime
+		when tee.mode = 'ANYTIME' then SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then SEC_TO_TIME((IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000) / 1000)
+		else SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		end as remainTime
         <include refid="invigilatePageMiddle"/>
 		<include refid="invigilatePageFoot"/>
         <if test="paperDownload != null and paperDownload != '' or paperDownload == 0">
@@ -240,11 +239,10 @@
 		,t.monitor_live_url as monitorLiveUrl
 		,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and tiiwi.approve_status = 0) as warningNew
 		,case
-		when tee.mode = 'ANYTIME' then
-		date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s')
-		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then
-		date_format(date_sub(from_unixtime(IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000),INTERVAL 8 HOUR), '%H:%i:%s')
-		END	as remainTime
+		when tee.mode = 'ANYTIME' then SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then SEC_TO_TIME((IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000) / 1000)
+		else SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		end as remainTime
 		<include refid="invigilatePageMiddle" />
 		<include refid="invigilatePageFoot" />
 		<if test="paperDownload != null and paperDownload != '' or paperDownload == 0">
@@ -263,11 +261,10 @@
 		,t.monitor_live_url as monitorLiveUrl
 		,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and tiiwi.approve_status = 0) as warningNew
 		,case
-		when tee.mode = 'ANYTIME' then
-		date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s')
-		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then
-		date_format(date_sub(from_unixtime(IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000),INTERVAL 8 HOUR), '%H:%i:%s')
-		END	as remainTime
+		when tee.mode = 'ANYTIME' then SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then SEC_TO_TIME((IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000) / 1000)
+		else SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		end as remainTime
 		<include refid="invigilatePageMiddle" />
 		<where> 1 = 1
 		<if test="examId != null and examId != ''">
@@ -284,11 +281,10 @@
 		,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and tiiwi.`type` =
 		'FACE_COUNT_ERROR' and tiiwi.`level` = 'D8') as multipleFaceCount
 		,case
-		when tee.mode = 'ANYTIME' then
-		date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s')
-		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then
-		date_format(date_sub(from_unixtime(IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000),INTERVAL 8 HOUR), '%H:%i:%s')
-		END	as remainTime
+		when tee.mode = 'ANYTIME' then SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		when tee.mode = 'TOGETHER' and tee.force_finish = 1 then SEC_TO_TIME((IFNULL(teea.finish_time, tee.end_time) - unix_timestamp(current_timestamp()) * 1000) / 1000)
+		else SEC_TO_TIME(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds)
+		end as remainTime
 		<include refid="invigilatePageMiddle" />
 		<include refid="invigilatePageFoot" />
 			<if test="status == null or status == ''">

+ 30 - 0
themis-business/src/main/resources/mapper/TOeFaceVerifyHistoryMapper.xml

@@ -154,4 +154,34 @@
         ) t
             group by t.`exception`) t
     </select>
+
+    <select id="eyeCloseError" resultType="java.util.Map">
+        select
+        sum(t.tmpCount) as tmpCount,
+        group_concat(t.photoUrl) as photoUrls,
+        min(t.createTime) as createTime
+        from
+        (
+        select
+        count(1) as tmpCount,
+        group_concat(tofvh.photo_url) as photoUrl,
+        min(tofvh.create_time) as createTime
+        from
+        t_oe_face_verify_history tofvh
+        <where>
+            <if test="recordId != null and recordId != ''">
+                and tofvh.exam_record_id = #{recordId}
+            </if>
+            <if test="exception != null and exception != ''">
+                and tofvh.`exception` = #{exception}
+            </if>
+            <if test="type != null and type != ''">
+                and tofvh.`type` = #{type}
+            </if>
+            <if test="batchNo != null and batchNo != ''">
+                and tofvh.batch_no = #{batchNo}
+            </if>
+        </where>
+        group by tofvh.`exception`) t
+    </select>
 </mapper>

+ 1 - 5
themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java

@@ -75,11 +75,7 @@ public class TEExamController {
         Long examId = Objects.nonNull(mapParameter.get("examId")) ? Long.parseLong(String.valueOf(mapParameter.get("examId"))) : null;
         TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
         List<TEExamDto> list = teExamService.getWaitingExam(teStudent.getId(), examId, teStudent.getOrgId());
-        Map<String, Object> map = new HashMap<>();
-        if (Objects.nonNull(list) && list.size() > 0) {
-            map.put("waiting", list);
-        }
-        return ResultUtil.ok(map);
+        return ResultUtil.ok(Collections.singletonMap("waiting", list));
     }
 
     @ApiOperation(value = "开始候考")

+ 6 - 0
themis-exam/src/main/java/com/qmth/themis/exam/config/WebMvcConfig.java

@@ -55,4 +55,10 @@ public class WebMvcConfig implements WebMvcConfigurer {
 //            }
 //        }
 //    }
+
+//    @Override
+//    public void addCorsMappings(CorsRegistry registry) {
+//        registry.addMapping("/**").allowedOrigins("*").allowCredentials(true)
+//                .allowedMethods("GET", "POST", "DELETE", "PUT").maxAge(3600);
+//    }
 }

+ 21 - 6
themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

@@ -350,7 +350,7 @@ public class MqLogicServiceImpl implements MqLogicService {
                         warningDto.setRandom(multipleFaceRandom);
                         faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, multipleFaceRandom);
                         Integer count = ExamRecordCacheUtil.getMultipleFaceCountErrorNum(recordId);
-                        if (count >= teConfig.getMultipleFaceCountError()) {
+                        if (count > teConfig.getMultipleFaceCountError()) {
                             ExamRecordCacheUtil.setMultipleFaceCountErrorRandom(recordId, String.valueOf(UUID.randomUUID()).replaceAll("-", ""));
                             ExamRecordCacheUtil.setMultipleFaceCountErrorNum(recordId, 0);
                             warningService.faceCountError(warningDto);
@@ -365,7 +365,7 @@ public class MqLogicServiceImpl implements MqLogicService {
                         warningDto.setRandom(noFaceRandom);
                         faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, noFaceRandom);
                         Integer count = ExamRecordCacheUtil.getNoFaceCountErrorNum(recordId);
-                        if (count >= teConfig.getNoFaceCountError()) {
+                        if (count > teConfig.getNoFaceCountError()) {
                             ExamRecordCacheUtil.setNoFaceCountErrorRandom(recordId, String.valueOf(UUID.randomUUID()).replaceAll("-", ""));
                             ExamRecordCacheUtil.setNoFaceCountErrorNum(recordId, 0);
                             warningService.faceCountError(warningDto);
@@ -381,11 +381,26 @@ public class MqLogicServiceImpl implements MqLogicService {
                     warningDto.setRandom(faceCompareErrorRandom);
                     faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, null);
                     Integer count = ExamRecordCacheUtil.getFaceCompareErrorNum(recordId);
-                    if (count >= teConfig.getTotalFaceCompareErrorCount()) {
+                    if (count > teConfig.getTotalFaceCompareErrorCount()) {
                         ExamRecordCacheUtil.setFaceCompareErrorRandom(recordId, String.valueOf(UUID.randomUUID()).replaceAll("-", ""));
                         ExamRecordCacheUtil.setFaceCompareErrorNum(recordId, 0);
                         warningService.faceCompareError(warningDto);
                     }
+                } else if (Objects.equals(VerifyExceptionEnum.EYE_CLOSE_ERROR, warningEnum)) {//闭眼检测异常
+                    String eyeCloseErrorRandom = ExamRecordCacheUtil.getEyeCloseErrorRandom(recordId);
+                    if (Objects.isNull(eyeCloseErrorRandom)) {
+                        eyeCloseErrorRandom = String.valueOf(UUID.randomUUID()).replaceAll("-", "");
+                        ExamRecordCacheUtil.setEyeCloseErrorRandom(recordId, eyeCloseErrorRandom);
+                    }
+                    ExamRecordCacheUtil.setEyeCloseErrorNum(recordId, ExamRecordCacheUtil.getEyeCloseErrorNum(recordId) + 1);
+                    warningDto.setRandom(eyeCloseErrorRandom);
+                    faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, null);
+                    Integer count = ExamRecordCacheUtil.getEyeCloseErrorNum(recordId);
+                    if (count > teConfig.getTotalEyeCloseErrorCount()) {
+                        ExamRecordCacheUtil.setEyeCloseErrorRandom(recordId, String.valueOf(UUID.randomUUID()).replaceAll("-", ""));
+                        ExamRecordCacheUtil.setEyeCloseErrorNum(recordId, 0);
+                        warningService.eyeCloseError(warningDto);
+                    }
                 } else {
                     faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, null);
                     warningService.realnessError(warningDto);
@@ -459,7 +474,7 @@ public class MqLogicServiceImpl implements MqLogicService {
                             warningDto.setRandom(multipleFaceRandom);
                             livenessVerifyHistoryService.save(id, recordId, type, actions, retry, startTime, finishTime, exception, multipleFaceRandom);
                             Integer count = ExamRecordCacheUtil.getMultipleFaceCountErrorNum(recordId);
-                            if (count >= teConfig.getMultipleFaceCountError()) {
+                            if (count > teConfig.getMultipleFaceCountError()) {
                                 ExamRecordCacheUtil.setMultipleFaceCountErrorRandom(recordId, String.valueOf(UUID.randomUUID()).replaceAll("-", ""));
                                 ExamRecordCacheUtil.setMultipleFaceCountErrorNum(recordId, 0);
                                 warningService.faceCountError(warningDto);
@@ -474,7 +489,7 @@ public class MqLogicServiceImpl implements MqLogicService {
                             warningDto.setRandom(noFaceRandom);
                             livenessVerifyHistoryService.save(id, recordId, type, actions, retry, startTime, finishTime, exception, noFaceRandom);
                             Integer count = ExamRecordCacheUtil.getNoFaceCountErrorNum(recordId);
-                            if (count >= teConfig.getNoFaceCountError()) {
+                            if (count > teConfig.getNoFaceCountError()) {
                                 ExamRecordCacheUtil.setNoFaceCountErrorRandom(recordId, String.valueOf(UUID.randomUUID()).replaceAll("-", ""));
                                 ExamRecordCacheUtil.setNoFaceCountErrorNum(recordId, 0);
                                 warningService.faceCountError(warningDto);
@@ -491,7 +506,7 @@ public class MqLogicServiceImpl implements MqLogicService {
                         warningDto.setRandom(faceCompareErrorRandom);
                         livenessVerifyHistoryService.save(id, recordId, type, actions, retry, startTime, finishTime, exception, faceCompareErrorRandom);
                         Integer count = ExamRecordCacheUtil.getFaceCompareErrorNum(recordId);
-                        if (count >= teConfig.getTotalFaceCompareErrorCount()) {
+                        if (count > teConfig.getTotalFaceCompareErrorCount()) {
                             ExamRecordCacheUtil.setFaceCompareErrorRandom(recordId, String.valueOf(UUID.randomUUID()).replaceAll("-", ""));
                             ExamRecordCacheUtil.setFaceCompareErrorNum(recordId, 0);
                             warningService.faceCompareError(warningDto);