123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- package com.qmth.themis.exam.api;
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.google.gson.Gson;
- import com.qmth.themis.business.annotation.ApiJsonObject;
- import com.qmth.themis.business.annotation.ApiJsonProperty;
- import com.qmth.themis.business.bean.exam.ExamActivityUnFinishBean;
- import com.qmth.themis.business.bean.exam.ExamUnFinishBean;
- import com.qmth.themis.business.cache.ExamRecordCacheUtil;
- import com.qmth.themis.business.cache.ExamingDataCacheUtil;
- import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
- import com.qmth.themis.business.cache.bean.ExamCacheBean;
- import com.qmth.themis.business.cache.bean.ExamCourseCacheBean;
- import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
- import com.qmth.themis.business.constant.SystemConstant;
- import com.qmth.themis.business.dto.AuthDto;
- import com.qmth.themis.business.dto.ExpireTimeDTO;
- import com.qmth.themis.business.dto.MqDto;
- import com.qmth.themis.business.dto.cache.TEStudentCacheDto;
- import com.qmth.themis.business.dto.response.TEExamActivityWaitDto;
- import com.qmth.themis.business.dto.response.TEExamResultDto;
- import com.qmth.themis.business.dto.response.TEExamWaitDto;
- import com.qmth.themis.business.entity.TBOrg;
- import com.qmth.themis.business.entity.TBSession;
- import com.qmth.themis.business.entity.TEConfig;
- import com.qmth.themis.business.entity.TEStudent;
- import com.qmth.themis.business.enums.ExamRecordStatusEnum;
- import com.qmth.themis.business.enums.MqTagEnum;
- import com.qmth.themis.business.enums.SystemOperationEnum;
- import com.qmth.themis.business.enums.WebsocketStatusEnum;
- import com.qmth.themis.business.service.*;
- import com.qmth.themis.business.util.*;
- import com.qmth.themis.common.enums.ExceptionResultEnum;
- import com.qmth.themis.common.enums.Platform;
- import com.qmth.themis.common.enums.Source;
- import com.qmth.themis.common.exception.BusinessException;
- import com.qmth.themis.common.util.Result;
- import com.qmth.themis.common.util.ResultUtil;
- import com.qmth.themis.exam.config.ExamConstant;
- import io.swagger.annotations.*;
- import org.apache.commons.lang3.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.web.bind.annotation.RequestBody;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.RestController;
- import javax.annotation.Resource;
- import java.io.File;
- import java.security.NoSuchAlgorithmException;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Objects;
- /**
- * @Description: 考生 前端控制器
- * @Param:
- * @return:
- * @Author: wangliang
- * @Date: 2020/6/25
- */
- @Api(tags = "考生Controller")
- @RestController
- @RequestMapping("/${prefix.url.exam}/student")
- public class TEStudentController {
- private final static Logger log = LoggerFactory.getLogger(TEStudentController.class);
- @Resource
- TEStudentService teStudentService;
- @Resource
- CacheService cacheService;
- @Resource
- RedisUtil redisUtil;
- @Resource
- MqDtoService mqDtoService;
- @Resource
- TEConfigService teConfigService;
- @Resource
- TEExamService teExamService;
- @Resource
- TEExamStudentService teExamStudentService;
- @Resource
- TEExamActivityService teExamActivityService;
- @Resource
- TEExamCourseService teExamCourseService;
- @Resource
- OssUtil ossUtil;
- @Resource
- TOeExamRecordService tOeExamRecordService;
- @Resource
- TBOrgService tbOrgService;
- @Resource
- MqUtil mqUtil;
- @ApiOperation(value = "学生登录接口")
- @RequestMapping(value = "/login", method = RequestMethod.POST)
- @ApiResponses({@ApiResponse(code = 200, message = "学生信息", response = TEExamResultDto.class)})
- public Result login(
- @ApiJsonObject(name = "loginStudent", value = {@ApiJsonProperty(key = "identity", description = "证件号"),
- @ApiJsonProperty(key = "password", description = "密码"),
- @ApiJsonProperty(key = "orgId", type = "long", example = "1", description = "机构id"),
- @ApiJsonProperty(key = "examId", type = "long", example = "1", description = "批次id")}) @ApiParam(value = "学生信息", required = true) @RequestBody Map<String, Object> mapParameter)
- throws NoSuchAlgorithmException {
- if (Objects.isNull(mapParameter)) {
- throw new BusinessException(ExceptionResultEnum.STUDENT_IS_NULL);
- }
- if (Objects.isNull(mapParameter.get("identity")) || Objects.equals(mapParameter.get("identity"), "")) {
- throw new BusinessException(ExceptionResultEnum.LOGIN_NAME_IS_NULL);
- }
- if (Objects.isNull(mapParameter.get("password")) || Objects.equals(mapParameter.get("password"), "")) {
- throw new BusinessException(ExceptionResultEnum.PASSWORD_IS_NULL);
- }
- Long orgId = null;
- if (Objects.nonNull(mapParameter.get(SystemConstant.ORG_ID))) {
- orgId = Long.parseLong(String.valueOf(mapParameter.get(SystemConstant.ORG_ID)));
- }
- Long examId = null;
- TBOrg tbOrg = null;
- if (Objects.nonNull(mapParameter.get(SystemConstant.EXAM_ID))) {
- examId = Long.parseLong(String.valueOf(mapParameter.get(SystemConstant.EXAM_ID)));
- ExamCacheBean ec = teExamService.getExamCacheBean(examId);
- tbOrg = cacheService.addOrgCache(ec.getOrgId());
- }
- if (Objects.isNull(orgId) && Objects.isNull(examId)) {
- throw new BusinessException(ExceptionResultEnum.ORG_ID_OR_EXAM_ID_NOT_CHOOSE);
- }
- if (Objects.nonNull(orgId)) {
- tbOrg = cacheService.addOrgCache(orgId);
- }
- if (Objects.isNull(tbOrg)) {
- throw new BusinessException(ExceptionResultEnum.ORG_NO);
- }
- if (Objects.nonNull(tbOrg.getEnable()) && tbOrg.getEnable().intValue() == 0) {
- throw new BusinessException(ExceptionResultEnum.ORG_ENABLE);
- }
- String identity = String.valueOf(mapParameter.get("identity"));
- String password = String.valueOf(mapParameter.get("password"));
- QueryWrapper<TEStudent> wrapper = new QueryWrapper<>();
- wrapper.lambda().eq(TEStudent::getIdentity, identity).eq(TEStudent::getOrgId, tbOrg.getId());
- TEStudent user = teStudentService.getOne(wrapper);
- //学生不存在
- if (Objects.isNull(user)) {
- throw new BusinessException(ExceptionResultEnum.STUDENT_NO);
- }
- if (StringUtils.isNotBlank(user.getBasePhotoPath())) {
- user.setBasePhotoPath(
- ossUtil.getAliYunOssPublicDomain().getPublicUrl() + File.separator + user.getBasePhotoPath());
- }
- // String loginPassword = AesUtil.decryptCs7(password, Constants.AES_RULE);
- // 密码错误
- // String aesPassword = AesUtil.decryptCs7(user.getPassword(), Constants.AES_RULE);
- if (!Objects.equals(password, user.getPassword())) {
- throw new BusinessException(ExceptionResultEnum.PASSWORD_ERROR);
- }
- String lockKey = SystemConstant.REDIS_LOCK_STUDENT_PREFIX + user.getId();
- redisUtil.waitLock(lockKey, SystemConstant.REDIS_LOCK_STUDENT_TIME_OUT);
- try {
- //判断是否有正在考试的记录
- Long examingRecordId = ExamingDataCacheUtil.getExamingRecordId(user.getId());
- if (Objects.nonNull(examingRecordId)) {
- WebsocketStatusEnum sta = ExamRecordCacheUtil.getClientWebsocketStatus(examingRecordId);
- //有正在考试的记录,且考生端websocket在线则不允许登陆
- if (WebsocketStatusEnum.ON_LINE.equals(sta)) {
- throw new BusinessException(ExceptionResultEnum.STUDENT_NOT_ALLOW_LOGIN);
- }
- }
- return userLoginCommon(user, examId, orgId);
- } finally {
- redisUtil.releaseLock(lockKey);
- }
- }
- /**
- * 学生登录公用
- *
- * @param teStudent
- * @param examId
- * @param orgId
- * @return
- * @throws NoSuchAlgorithmException
- */
- private Result userLoginCommon(TEStudent teStudent, Long examId, Long orgId) throws NoSuchAlgorithmException {
- //停用
- if (teStudent.getEnable() == 0) {
- throw new BusinessException(ExceptionResultEnum.STUDENT_ENABLE);
- }
- Platform platform = ServletUtil.getRequestPlatform();
- String deviceId = ServletUtil.getRequestDeviceId();
- //添加用户鉴权缓存
- AuthDto authDto = cacheService.addStudentAuthCache(teStudent.getId());
- //生成token
- String token = SystemConstant.getUuid();
- cacheService.addStudentAccountCache(teStudent.getId());
- //判断会话来源
- Source source = null;
- if (Platform.WIN == platform || Platform.MAC == platform) {
- source = Source.OE_CLIENT;
- } else {
- throw new BusinessException(ExceptionResultEnum.PLATFORM_INVALID);
- }
- //添加用户会话缓存
- String sessionId = SessionUtil
- .digest(teStudent.getId(), Math.abs(authDto.getRoleCodes().toString().hashCode()), source);
- ExpireTimeDTO expireTime = SystemConstant.getExpireTime(source, platform);
- TBSession tbSession = new TBSession(sessionId, String.valueOf(teStudent.getId()),
- authDto.getRoleCodes().toString(), source.name(), platform.name(), deviceId,
- ServletUtil.getRequest().getLocalAddr(), token, expireTime.getDate().getTime());
- redisUtil.setUserSession(sessionId, tbSession, expireTime.getExpireSeconds());
- //mq发送消息start
- MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), platform.name(), SystemOperationEnum.SESSION,
- sessionId);
- mqDtoService.assembleSendOneWayMsg(mqDto);
- MqDto mqDtoLog = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.STUDENT.name(),
- SystemOperationEnum.LOGIN, MqTagEnum.STUDENT, String.valueOf(teStudent.getId()),
- teStudent.getIdentity());
- mqDtoService.assembleSendOneWayMsg(mqDtoLog);
- //mq发送消息end
- //测试
- // String test = SignatureInfo.build(SignatureType.TOKEN, sessionId, token);
- Map<String, Object> map = new HashMap<>();
- //获取未完考试
- Long recordId = ExamingDataCacheUtil.getExamingRecordId(teStudent.getId());
- if (Objects.isNull(recordId)) {
- map = this.getWaitList(teStudent.getId(), examId, orgId, map);
- } else {
- //获取考试记录缓存
- ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(recordId);
- if (Objects.isNull(status)) {
- //map = this.getWaitList(teStudent.getId(), examId, orgId, map);
- log.error("ExamRecordStatus in cache is null, recordId=" + recordId);
- throw new BusinessException(ExceptionResultEnum.EXAM_STATUS_ERROR);
- } else {
- Long ecExamId = ExamRecordCacheUtil.getExamId(recordId);
- Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
- Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
- ExamCacheBean ec = teExamService.getExamCacheBean(ecExamId);//考试缓存
- ExamActivityCacheBean examActivityCacheBean = teExamActivityService
- .getExamActivityCacheBean(examActivityId);//考试场次缓存
- ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
- //如果断点时间大于整体断点时间,则强制交卷
- if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING) || Objects
- .equals(status, ExamRecordStatusEnum.BREAK_OFF) || Objects
- .equals(status, ExamRecordStatusEnum.RESUME_PREPARE)) {
- //只有ANSWERING状态才生成断点
- if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING)) {
- ExamConstant.sendExamStopMsg(recordId, true, false);
- tOeExamRecordService.setExamBreak(recordId);
- }
- Boolean finished = tOeExamRecordService.examBreakLogic(recordId, false);
- if (finished) {
- map = this.getWaitList(teStudent.getId(), examId, orgId, map);
- } else {
- ExamUnFinishBean examUnFinishBean = this
- .unFinishCommon(recordId, ec, examStudentCacheBean, examActivityCacheBean,
- examStudentId);
- map.put("unFinished", examUnFinishBean);
- }
- } else {
- map = this.getWaitList(teStudent.getId(), examId, orgId, map);
- }
- }
- }
- //获取全局考试配置
- TEConfig teConfig = teConfigService.getGlobalConfig();
- map.put(SystemConstant.ACCESS_TOKEN, token);
- map.put(SystemConstant.GLOBAL, teConfig);
- // map.put(SystemConstant.ACCESS_TOKEN, test);
- map.put(SystemConstant.STUDENT_ACCOUNT, teStudent);
- map.put(SystemConstant.SESSION_ID, sessionId);
- return ResultUtil.ok(map);
- }
- @ApiOperation(value = "登出接口")
- @RequestMapping(value = "/logout", method = RequestMethod.POST)
- @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
- public Result logout() throws NoSuchAlgorithmException {
- TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
- TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
- if (Objects.isNull(tbSession)) {
- throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
- }
- AuthDto authDto = cacheService.addStudentAuthCache(teStudent.getId());
- redisUtil.deleteUserSession(tbSession.getId());
- //循环检查该用户下其他平台是否存在session,不存在则删除用户缓存和鉴权缓存
- boolean delete = true;
- for (Source s : Source.values()) {
- String sessionId = SessionUtil
- .digest(teStudent.getId(), Math.abs(authDto.getRoleCodes().toString().hashCode()), s.name());
- if (Objects.nonNull(redisUtil.getUserSession(sessionId))) {
- delete = false;
- break;
- }
- }
- if (delete) {
- cacheService.removeStudentAuthCache(teStudent.getId());
- cacheService.removeStudentAccountCache(teStudent.getId());
- }
- //mq发送消息start
- MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.STUDENT.name(),
- SystemOperationEnum.LOGOUT, MqTagEnum.STUDENT, String.valueOf(teStudent.getId()),
- teStudent.getIdentity());
- mqDtoService.assembleSendOneWayMsg(mqDto);
- //mq发送消息end
- return ResultUtil.ok(true);
- }
- /**
- * 未完成公用
- *
- * @param recordId
- * @param ec
- * @param examStudentCacheBean
- * @param examActivityCacheBean
- * @param examStudentId
- * @return
- */
- private ExamUnFinishBean unFinishCommon(Long recordId, ExamCacheBean ec, ExamStudentCacheBean examStudentCacheBean,
- ExamActivityCacheBean examActivityCacheBean, Long examStudentId) {
- ExamCourseCacheBean examCourseCacheBean = teExamCourseService
- .getExamCourseCacheBean(ec.getId(), examStudentCacheBean.getCourseCode());
- TEExamActivityWaitDto teExamActivityWaitDto = new TEExamActivityWaitDto(ec, examActivityCacheBean,
- examStudentCacheBean, examStudentId, examCourseCacheBean, ExamRecordCacheUtil.getStartTime(recordId),
- ExamRecordCacheUtil.getEndTime(recordId), ExamRecordCacheUtil.getOpeningSeconds(recordId),
- ExamRecordCacheUtil.getMinDurationSeconds(recordId),
- ExamRecordCacheUtil.getMaxDurationSeconds(recordId), ExamRecordCacheUtil.getForceFinish(recordId));
- Gson gson = new Gson();
- ExamActivityUnFinishBean examActivityUnFinishBean = gson
- .fromJson(gson.toJson(teExamActivityWaitDto), ExamActivityUnFinishBean.class);
- examActivityUnFinishBean.setRecordId(recordId);
- return new ExamUnFinishBean(ec.getId(), ec.getName(), examActivityUnFinishBean);
- }
- /**
- * 获取待考列表
- *
- * @param studentId
- * @param examId
- * @param orgId
- * @param map
- * @return
- */
- private Map<String, Object> getWaitList(Long studentId, Long examId, Long orgId, Map<String, Object> map) {
- //获取待考列表
- List<TEExamWaitDto> list = teExamService.getWaitingExam(studentId, examId, orgId);
- if (Objects.nonNull(list) && list.size() > 0) {
- map.put("waiting", list);
- }
- return map;
- }
- }
|