|
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONArray;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
+import com.google.common.collect.Sets;
|
|
|
import com.google.gson.Gson;
|
|
|
import com.qmth.themis.business.cache.ExamRecordCacheUtil;
|
|
|
import com.qmth.themis.business.cache.RedisKeyHelper;
|
|
@@ -12,20 +13,23 @@ 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.cache.TEStudentCacheDto;
|
|
|
import com.qmth.themis.business.entity.*;
|
|
|
import com.qmth.themis.business.enums.*;
|
|
|
import com.qmth.themis.business.service.*;
|
|
|
import com.qmth.themis.business.templete.TaskExportTemplete;
|
|
|
import com.qmth.themis.business.templete.TaskImportTemplete;
|
|
|
import com.qmth.themis.business.templete.impl.*;
|
|
|
-import com.qmth.themis.business.util.JacksonUtil;
|
|
|
-import com.qmth.themis.business.util.MqUtil;
|
|
|
-import com.qmth.themis.business.util.RedisUtil;
|
|
|
-import com.qmth.themis.business.util.UidUtil;
|
|
|
+import com.qmth.themis.business.util.*;
|
|
|
import com.qmth.themis.common.contanst.Constants;
|
|
|
+import com.qmth.themis.common.enums.ExceptionResultEnum;
|
|
|
+import com.qmth.themis.common.enums.Source;
|
|
|
import com.qmth.themis.common.exception.BusinessException;
|
|
|
import com.qmth.themis.common.util.SimpleBeanUtil;
|
|
|
import com.qmth.themis.mq.service.MqLogicService;
|
|
|
+import com.tencentcloudapi.vod.v20180717.models.MediaBasicInfo;
|
|
|
+import com.tencentcloudapi.vod.v20180717.models.MediaInfo;
|
|
|
+import com.tencentcloudapi.vod.v20180717.models.SearchMediaResponse;
|
|
|
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
|
|
|
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
|
|
|
import org.apache.rocketmq.common.message.MessageExt;
|
|
@@ -37,10 +41,8 @@ import org.springframework.transaction.annotation.Transactional;
|
|
|
import javax.annotation.Resource;
|
|
|
import java.io.IOException;
|
|
|
import java.lang.reflect.Method;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.Objects;
|
|
|
-import java.util.UUID;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.util.*;
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
/**
|
|
@@ -118,6 +120,24 @@ public class MqLogicServiceImpl implements MqLogicService {
|
|
|
@Resource
|
|
|
MqUtil mqUtil;
|
|
|
|
|
|
+ @Resource
|
|
|
+ CacheService cacheService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ TencentYunUtil tencentYunUtil;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ TMTencentVideoMessageService tencentVideoMessageService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ MonitorUtil monitorUtil;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ OssUtil ossUtil;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ TOeExamRecordService tOeExamRecordService;
|
|
|
+
|
|
|
/**
|
|
|
* mq最大重试次数逻辑
|
|
|
*
|
|
@@ -265,13 +285,14 @@ public class MqLogicServiceImpl implements MqLogicService {
|
|
|
*/
|
|
|
@Override
|
|
|
@Transactional
|
|
|
- public void execMqWebsocketUnNormalLogic(MqDto mqDto, String key) {
|
|
|
+ public void execMqWebsocketUnNormalLogic(MqDto mqDto, String key) throws NoSuchAlgorithmException {
|
|
|
Gson gson = new Gson();
|
|
|
mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);//表示成功处理
|
|
|
Map<String, Object> tranMap = mqDto.getProperties();
|
|
|
Long recordId = Long.parseLong(String.valueOf(tranMap.get("recordId")));
|
|
|
ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(recordId);
|
|
|
WebsocketStatusEnum websocketStatusEnum = ExamRecordCacheUtil.getClientWebsocketStatus(recordId);
|
|
|
+ Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
|
|
|
if (Objects.nonNull(websocketStatusEnum) && (Objects.equals(websocketStatusEnum, WebsocketStatusEnum.OFF_LINE)
|
|
|
&& Objects.equals(status, ExamRecordStatusEnum.ANSWERING))) {
|
|
|
examRecordService.examBreakLogic(recordId, true);
|
|
@@ -292,10 +313,47 @@ public class MqLogicServiceImpl implements MqLogicService {
|
|
|
.setMonitorStatus(recordId, MonitorVideoSourceEnum.CLIENT_SCREEN, MonitorStatusSourceEnum.STOP,
|
|
|
timestamp);
|
|
|
}
|
|
|
+
|
|
|
+ ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
|
|
|
+ Optional.ofNullable(examStudentCacheBean).orElseThrow(() -> new BusinessException("考生数据为空"));
|
|
|
+
|
|
|
+ TEStudentCacheDto teStudent = cacheService.addStudentAccountCache(examStudentCacheBean.getStudentId());
|
|
|
+ Optional.ofNullable(teStudent).orElseThrow(() -> new BusinessException("学生数据为空"));
|
|
|
+
|
|
|
+ //2022/03/28加入更新移动端第一、第二机位推流状态
|
|
|
+ //更新移动端第一机位推流状态为stop
|
|
|
+ MonitorStatusSourceEnum mobileFirstStatusSourceEnum = ExamRecordCacheUtil
|
|
|
+ .getMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_FIRST);
|
|
|
+ if (Objects.nonNull(mobileFirstStatusSourceEnum)) {
|
|
|
+ ExamRecordCacheUtil
|
|
|
+ .setMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_FIRST, MonitorStatusSourceEnum.STOP,
|
|
|
+ timestamp);
|
|
|
+ ExamRecordCacheUtil.setMobileFirstWebsocketStatus(recordId, WebsocketStatusEnum.OFF_LINE, timestamp);
|
|
|
+ String sessionId = SessionUtil.digest(teStudent.getId(),
|
|
|
+ Math.abs(Sets.newHashSet(RoleEnum.STUDENT.name()).toString().hashCode()), Source.MOBILE_MONITOR_FIRST.name());
|
|
|
+ redisUtil.deleteUserSession(sessionId);
|
|
|
+ }
|
|
|
+
|
|
|
+ //更新移动端第二机位推流状态为stop
|
|
|
+ MonitorStatusSourceEnum mobileSecondStatusSourceEnum = ExamRecordCacheUtil
|
|
|
+ .getMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_SECOND);
|
|
|
+ if (Objects.nonNull(mobileSecondStatusSourceEnum)) {
|
|
|
+ ExamRecordCacheUtil
|
|
|
+ .setMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_SECOND, MonitorStatusSourceEnum.STOP,
|
|
|
+ timestamp);
|
|
|
+ ExamRecordCacheUtil.setMobileSecondWebsocketStatus(recordId, WebsocketStatusEnum.OFF_LINE, timestamp);
|
|
|
+ String sessionId = SessionUtil.digest(teStudent.getId(),
|
|
|
+ Math.abs(Sets.newHashSet(RoleEnum.STUDENT.name()).toString().hashCode()), Source.MOBILE_MONITOR_SECOND.name());
|
|
|
+ redisUtil.deleteUserSession(sessionId);
|
|
|
+ }
|
|
|
+
|
|
|
examRecordService.sendExamRecordDataSaveMq(recordId, timestamp);
|
|
|
+
|
|
|
+ Map<String, Object> properties = new HashMap<>();
|
|
|
+ properties.put(SystemConstant.MOBILE_REMOVE_WEBSOCKET, true);
|
|
|
//发送移动端监考退出考试mq消息 start
|
|
|
MqDto mqDtoExamStop = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.EXAM_STOP.name(), recordId,
|
|
|
- MqTagEnum.EXAM_STOP, String.valueOf(recordId), String.valueOf(recordId));
|
|
|
+ MqTagEnum.EXAM_STOP, String.valueOf(recordId), properties, String.valueOf(recordId));
|
|
|
mqDtoService.assembleSendOneOrderMsg(mqDtoExamStop);
|
|
|
//发送移动端监考退出考试mq消息 end
|
|
|
}
|
|
@@ -305,6 +363,80 @@ public class MqLogicServiceImpl implements MqLogicService {
|
|
|
redisUtil.delete(key, mqDto.getId());
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 腾讯云视频回调逻辑
|
|
|
+ *
|
|
|
+ * @param mqDto
|
|
|
+ * @param key
|
|
|
+ * @throws NoSuchAlgorithmException
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public void execMqTencentVideoLogic(MqDto mqDto, String key) throws NoSuchAlgorithmException, IOException {
|
|
|
+ Gson gson = new Gson();
|
|
|
+ Map<String, Object> tranMap = mqDto.getProperties();
|
|
|
+ Long recordId = Long.parseLong(String.valueOf(tranMap.get("recordId")));
|
|
|
+ TOeExamRecord tOeExamRecord = tOeExamRecordService.getById(recordId);
|
|
|
+ List<String> monitorRecordList = Arrays.asList(tOeExamRecord.getMonitorRecord().trim().toUpperCase().split(","));
|
|
|
+ String[] streamIds = new String[monitorRecordList.size()];
|
|
|
+ for (int i = 0; i < monitorRecordList.size(); i++) {
|
|
|
+ streamIds[i] = SystemConstant.setStreamId(monitorUtil.getMonitorDomain().getPrefix(), recordId, MonitorVideoSourceEnum.convertToName(monitorRecordList.get(i)));
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> resultMap = tencentYunUtil.tencentVodSdk(tencentYunUtil.getTencentYunDomain().getSecretId(),
|
|
|
+ tencentYunUtil.getTencentYunDomain().getSecretKey(),
|
|
|
+ tencentYunUtil.getTencentYunDomain().getQueryUrl(),
|
|
|
+ tencentYunUtil.getTencentYunDomain().getVodAppId(),
|
|
|
+ streamIds);
|
|
|
+
|
|
|
+ if (Objects.nonNull(resultMap) && resultMap.size() > 0) {
|
|
|
+ SearchMediaResponse response = (SearchMediaResponse) resultMap.get("object");
|
|
|
+ if (Objects.nonNull(response) && response.getTotalCount().intValue() > 0) {
|
|
|
+ Map<String, String> map = new HashMap<>();
|
|
|
+ JSONArray jsonArray = null;
|
|
|
+ int countDb = 0;
|
|
|
+ if (Objects.nonNull(tOeExamRecord.getTencentVideoUrl())) {
|
|
|
+ jsonArray = JSONArray.parseArray(tOeExamRecord.getTencentVideoUrl());
|
|
|
+ countDb = jsonArray.size();
|
|
|
+ for (int i = 0; i < countDb; i++) {
|
|
|
+ JSONObject jsonObject = (JSONObject) jsonArray.get(i);
|
|
|
+ String videoSource = SystemConstant.getMonitorRecordStreamId((String) jsonObject.get(SystemConstant.VIDEO_SOURCE));
|
|
|
+ map.put(videoSource, videoSource);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ jsonArray = new JSONArray();
|
|
|
+ }
|
|
|
+ MediaInfo[] mediaInfos = response.getMediaInfoSet();
|
|
|
+ for (int i = 0; i < mediaInfos.length; i++) {
|
|
|
+ MediaBasicInfo mediaBasicInfo = mediaInfos[i].getBasicInfo();
|
|
|
+ String videoSource = SystemConstant.getMonitorRecordStreamId(mediaBasicInfo.getName());
|
|
|
+ boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource, SystemConstant.REDIS_LOCK_TENCENT_VIDEO_TIME_OUT);
|
|
|
+ if (lock && Objects.isNull(map.get(videoSource))) {
|
|
|
+ try {
|
|
|
+ JSONObject jsonObject = new JSONObject();
|
|
|
+ jsonObject.put(SystemConstant.VIDEO_SOURCE, videoSource);
|
|
|
+ jsonObject.put(SystemConstant.VIDEO_URL, mediaBasicInfo.getMediaUrl());
|
|
|
+ jsonArray.add(jsonObject);
|
|
|
+ } finally {
|
|
|
+ redisUtil.releaseLock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (Objects.nonNull(jsonArray) && jsonArray.size() > 0 && countDb != jsonArray.size()) {
|
|
|
+ tOeExamRecord.setTencentVideoUrl(jsonArray.toJSONString());
|
|
|
+ tOeExamRecordService.updateById(tOeExamRecord);
|
|
|
+ }
|
|
|
+ TMTencentVideoMessage tencentVideoMessage = new TMTencentVideoMessage(response.getRequestId(), JacksonUtil.parseJson(response));
|
|
|
+ tencentVideoMessageService.save(tencentVideoMessage);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);//表示成功处理
|
|
|
+ TMRocketMessage tmRocketMessage = gson.fromJson(gson.toJson(mqDto), TMRocketMessage.class);
|
|
|
+ tmRocketMessage.setBody(JacksonUtil.parseJson(tmRocketMessage.getBody()));
|
|
|
+ tmRocketMessageService.saveOrUpdate(tmRocketMessage);
|
|
|
+ redisUtil.delete(key, mqDto.getId());
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 计算客观分逻辑
|
|
|
*
|
|
@@ -1014,4 +1146,59 @@ public class MqLogicServiceImpl implements MqLogicService {
|
|
|
}
|
|
|
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;//成功
|
|
|
}
|
|
|
+
|
|
|
+// /**
|
|
|
+// * 创建qrcode
|
|
|
+// *
|
|
|
+// * @param videoUrl
|
|
|
+// * @param jsonObject
|
|
|
+// * @return
|
|
|
+// * @throws IOException
|
|
|
+// */
|
|
|
+// private JSONObject createQrCode(String videoUrl, JSONObject jsonObject) throws IOException {
|
|
|
+// InputStream inputStream = null;
|
|
|
+// try {
|
|
|
+// byte[] b = QrCodeUtil.createQRCode(300, 300, videoUrl);
|
|
|
+// inputStream = new ByteArrayInputStream(b);
|
|
|
+// String md5 = DigestUtils.md5Hex(new ByteArrayInputStream(b));
|
|
|
+//
|
|
|
+// AliYunOssPublicDomain aliYunOssPublicDomain = ossUtil.getAliYunOssPublicDomain();
|
|
|
+// Boolean oss = (Boolean) aliYunOssPublicDomain.getMap().get(SystemConstant.OSS);
|
|
|
+//
|
|
|
+// LocalDateTime nowTime = LocalDateTime.now();
|
|
|
+// StringJoiner stringJoiner = new StringJoiner("");
|
|
|
+// if (!oss) {
|
|
|
+// stringJoiner.add(SystemConstant.FILES_DIR).add(File.separator);
|
|
|
+// }
|
|
|
+// String fileType = ".png";
|
|
|
+// UploadFileEnum type = UploadFileEnum.upload;
|
|
|
+// stringJoiner.add(type.name()).add(File.separator);
|
|
|
+// stringJoiner.add(String.valueOf(nowTime.getYear())).add(File.separator)
|
|
|
+// .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
|
|
|
+// .add(String.format("%02d", nowTime.getDayOfMonth()));
|
|
|
+// if (oss) {//上传至oss
|
|
|
+// stringJoiner.add(File.separator).add(String.valueOf(UUID.randomUUID()).replaceAll("-", "")).add(fileType);
|
|
|
+// ossUtil.upload(ossUtil.isPublic(type), stringJoiner.toString(), inputStream, md5);
|
|
|
+// jsonObject.put("qrCodeUrl", aliYunOssPublicDomain.getPublicUrl() + File.separator + stringJoiner.toString());
|
|
|
+// } else {//上传至服务器
|
|
|
+// File finalFile = new File(
|
|
|
+// stringJoiner.add(File.separator).add(String.valueOf(UUID.randomUUID()).replaceAll("-", ""))
|
|
|
+// .add(fileType).toString());
|
|
|
+// if (!finalFile.exists()) {
|
|
|
+// finalFile.getParentFile().mkdirs();
|
|
|
+// finalFile.createNewFile();
|
|
|
+// }
|
|
|
+// String fileHost = (String) aliYunOssPublicDomain.getMap().get(SystemConstant.FILE_HOST);
|
|
|
+// jsonObject.put("qrCodeUrl", "http://" + fileHost + File.separator + finalFile.getPath());
|
|
|
+// FileUtils.copyInputStreamToFile(inputStream, finalFile);
|
|
|
+// }
|
|
|
+// } catch (Exception e) {
|
|
|
+// log.error(SystemConstant.LOG_ERROR, e);
|
|
|
+// } finally {
|
|
|
+// if (Objects.nonNull(inputStream)) {
|
|
|
+// inputStream.close();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// return jsonObject;
|
|
|
+// }
|
|
|
}
|