package com.ruoyi.sim.service.impl; import java.util.ArrayList; import java.util.List; import java.util.Objects; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.sim.config.SimConfig; import com.ruoyi.sim.config.SimDebugConfig; import com.ruoyi.sim.domain.*; import com.ruoyi.sim.domain.vo.RealExamVo; import com.ruoyi.sim.domain.vo.StudentRealExamIngVo; import com.ruoyi.sim.domain.vo.StudentRealExamPostVo; import com.ruoyi.sim.domain.vo.StudentRealExamPreVo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import com.ruoyi.sim.mapper.RealExamMapper; import org.springframework.transaction.annotation.Transactional; /** * 考试Service业务层处理 * * @author tom * @date 2024-12-15 */ @Service public class RealExamService { @Autowired private RealExamMapper realExamMapper; /** * 查询考试 * * @param examId 考试主键 * @return 考试 */ public RealExam selectRealExamByExamId(Long examId) { return realExamMapper.selectRealExamByExamId(examId); } /** * 查询考试列表 * * @param realExam 考试 * @return 考试 */ public List selectRealExamList(RealExam realExam) { return realExamMapper.selectRealExamList(realExam); } /** * 新增考试 * * @param realExam 考试 * @return 结果 */ public int insertRealExam(RealExam realExam) { realExam.setCreateTime(DateUtils.getNowDate()); return realExamMapper.insertRealExam(realExam); } /** * 修改考试 * * @param realExam 考试 * @return 结果 */ public int updateRealExam(RealExam realExam) { realExam.setUpdateTime(DateUtils.getNowDate()); return realExamMapper.updateRealExam(realExam); } /** * 批量删除考试 * * @param examIds 需要删除的考试主键 * @return 结果 */ public int deleteRealExamByExamIds(Long[] examIds) { return realExamMapper.deleteRealExamByExamIds(examIds); } /** * 删除考试信息 * * @param examId 考试主键 * @return 结果 */ public int deleteRealExamByExamId(Long examId) { return realExamMapper.deleteRealExamByExamId(examId); } // -------------------------------- tom add -------------------------------- private static final Logger l = LoggerFactory.getLogger(CommSendService.class); @Autowired private CommReceiveService commReceiveService; @Autowired private StudentService studentService; @Autowired private SimService simService; @Autowired private SeatService seatService; @Autowired private RealExamCollectionService realExamCollectionService; @Autowired private RealExamFaultService realExamFaultService; @Autowired @Lazy private CommSendService commSendService; @Autowired private SimConfig simConfig; /** * examId 是否有效。 * * @param examId * @return */ public boolean exist(Long examId) { if (examId == null) { return false; } if (examId == 0) { return false; } RealExam re = selectRealExamByExamId(examId); if (re == null) { return false; } return true; } public List list(RealExam q) { List list = new ArrayList<>(); realExamMapper.selectRealExamList(q).forEach(re -> { RealExamVo v = new RealExamVo(); RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId()); v.setRealExam(re); v.setRealExamCollection(rec); list.add(v); }); return list; } public List listAllByStatus(String state) { RealExam q = new RealExam(); q.setExamStatus(state); return selectRealExamList(q); } /** * 交卷自动修改关联状态 * * @param examId * @param state * @return */ @Transactional public int updateOneState(long examId, final String state) { if (false) { // php项目维护exam_status字段。java这边不操作。 // 屏蔽 return 0; } RealExam q = selectRealExamByExamId(examId); // todo:屏蔽 if (false && RealExam.State.SUBMITTED.equals(state)) { // 关联故障list同步锁死。 realExamFaultService.listAllType2State2and3ByExamId(q.getExamId()) .forEach(ref -> { ref.setRefState(RealExamFault.State.FINISH); realExamFaultService.updateRealExamFault(ref); }); } q.setExamStatus(state); return updateRealExam(q); } /** * [学生]进入考试。 * * @return RealExam */ public AjaxResult studentEnterRealExam(Long examId) { RealExam re = selectRealExamByExamId(examId); if (re == null) { AjaxResult.error("realExamId error!"); } // todo:应该在登录位置实现 // todo: temp updateOneState(examId, RealExam.State.LOGGED_IN); // todo: temp realExamFaultService.resetAllType2(examId); return AjaxResult.success(re); } /** * [轮询][学生]准备考试界面。 * * @param realExamId * @return StudentRealExamPreVo */ public AjaxResult studentLoopPrepareRealExam(Long realExamId) { l.info("studentLoopPrepareRealExam"); // check if (realExamId == null || realExamId == 0) { // todo: } // RealExam re = selectRealExamByExamId(realExamId); if (re == null) { // todo: } // todo: 日期,不可进入考试。 // // todo: 验证学生登录身份` Objects.requireNonNull(re); RealExamCollection collection = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId()); // check collection Sim sim = simService.selectSimBySimId(re.getSimId()); // check sim Student student = studentService.selectStudentByUserId(re.getUserId()); // check student Seat seat = seatService.selectSeatBySeatId(re.getSeatId()); // check seat StudentRealExamPreVo vo = new StudentRealExamPreVo(); vo.setRealExam(re); vo.setRealExamCollection(collection); vo.setSim(sim); vo.setStudent(student); vo.setSeat(seat); // todo:多人请求同时进入的问题 boolean next = studentPrepareRealExamCheck(vo); vo.setNext(next); if (!next) { // 执行模拟器通信,让模拟器准备好。 // 异步执行 commSendService.clearListFaultByRealExamAsync(re); } l.info("vo = {}", vo); return AjaxResult.success(vo); } public boolean studentPrepareRealExamCheck(StudentRealExamPreVo v) { if (v == null || v.getRealExam() == null || v.getRealExamCollection() == null || v.getSim() == null || v.getStudent() == null || v.getSeat() == null) { return false; } // todo:在考试日期内。 // check 一个模拟器的所有选中故障点位都下发成功,准备是否可以 // // todo:?? // 学生答题中可以再次进入。 String examStatus = v.getRealExam().getExamStatus(); String simStatus = v.getSim().getSimState(); // 兜底 { if (realExamFaultService.isAllType2StateXiaFa(v.getRealExam().getExamId())) { // updateOneState(v.getRealExam().getExamId(), RealExam.State.SIM_PREPARE_OK); } } // 模拟器不通信。 if (!simConfig.isCommGlobal()) { return true; } if ((RealExam.State.SIM_PREPARE_OK.equals(examStatus) || RealExam.State.ANSWERING.equals(examStatus)) && Sim.State.ONLINE.equals(simStatus) ) { return true; } return false; } /** * [学生]开始考试 * * @param examId * @return RealExam */ @Transactional public AjaxResult studentStartRealExam(final Long examId) { l.info("studentStartRealExam = {}", examId); // todo: 暂时没有解决方案 检查 考试的sim和seat,是否正确对应。 { // todo:delete // re.setSimId(getFakeSimId(re)); // l.info("fake re = {}", re); } // check id data. { AjaxResult arE1 = checkExamId(examId); if (arE1.isError()) { return arE1; } } RealExam re = selectRealExamByExamId(examId); Sim s = simService.selectSimBySimId(re.getSimId()); // Step 1 主动查询一次模拟器状态。获取模拟器在线/离线状态。 { commSendService.checkOneSimState(s, true); // 如果模拟器离线 if (s == null || !Sim.State.ONLINE.equals(s.getSimState())) { return AjaxResult.error("未连接模拟器,请检查连接!"); } } // Step 2 读取对应一台模拟器 所有故障部位值。检查模拟器所有的 真实的 故障部位 是否异常 或者 空值。特殊的故障部位要单独判断。 if (SimDebugConfig.CHECK_REPLACE_EMPTY) { AjaxResult arE2 = commSendService.readOneSimAllFaultCheck(s); if (arE2.isError()) { return arE2; } } // Step 3 清除对应一台模拟器 所有 真实的 故障部位故障。 { commSendService.clearOneSimAllFaultByExam(re); } // Step 4 下发对应一台模拟器 出题选中的 故障位置故障。 { commSendService.writeOneSimAllSelectFaultByExam(re); } // Step 5 读取对应一台模拟器 所有的 真实的 故障部位 电阻值代表值 作为出题值。 // 修改关联状态 { commSendService.readOneSimAllFaultFirstTimeByExam(re); } // Step 6 修改当前Exam状态。 if (realExamFaultService.isType2ExamPrepareStartOk(re.getExamId())) { updateOneState(re.getExamId(), RealExam.State.SIM_PREPARE_OK); updateOneState(re.getExamId(), RealExam.State.ANSWERING); // 修改真实考试开始时间。 re.setStartTime(DateUtils.getNowDate()); updateRealExam(re); return AjaxResult.success("开始考试成功!"); } else { return AjaxResult.error("连接超时,请检查模拟器连接!"); } } public AjaxResult checkExamId(final Long examId) { // check // 检查 examId 是否正确存在 { if (!exist(examId)) { return AjaxResult.error("对应考试Id不存在!"); } } RealExam re = selectRealExamByExamId(examId); // 检查 seat_id 是否正确存在 { if (!seatService.exist(re.getSeatId())) { return AjaxResult.error("对应座Id不存在!"); } } // 检查 sim_id 是否正确存在 { if (!simService.existBySimId(re.getSimId())) { return AjaxResult.error("对应模拟器Id不存在!"); } } return AjaxResult.success(); } /** * 根据考试集合获得sim_id * * @param exam * @return */ @Deprecated public long getFakeSimId(RealExam exam) { { RealExamCollection coll = realExamCollectionService.selectRealExamCollectionByExamCollectionId(exam.getExamCollectionId()); String simType = coll.getSimType(); switch (simType) { case Sim.TYPE_0001 -> { return 11L; } case Sim.TYPE_0002 -> { return 12L; } case Sim.TYPE_0003 -> { return 31L; } } } return 0L; } /** * [轮询][学生]正在考试界面。 * * @param realExamId * @return StudentRealExamIngVo */ public AjaxResult studentLoopAnsweringRealExam(Long realExamId) { RealExam re = selectRealExamByExamId(realExamId); RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId()); StudentRealExamIngVo vo = new StudentRealExamIngVo(); vo.setRealExam(re); long remaining = (re.getStartTime().getTime() + rec.getLimitDuration() * 60 * 1000) - DateUtils.getNowDate().getTime(); vo.setRemainingMilliseconds(remaining); vo.setCompulsiveSubmit(remaining >= RealExam.EXAM_TIMEOUT_LIMIT); l.info("studentLoopAnsweringRealExam vo = {}", vo); return AjaxResult.success(vo); } /** * [学生]交卷 * * @param examId * @return RealExam */ @Transactional public AjaxResult studentSubmitRealExam(Long examId) { RealExam re = selectRealExamByExamId(examId); { // todo:delete // re.setSimId(getFakeSimId(re)); } // check part. { AjaxResult arE1 = checkExamId(examId); if (arE1.isError()) { return arE1; } } // 检查一下模拟器状态。 Sim s = simService.selectSimBySimId(re.getSimId()); // 如果模拟器离线 if (s == null || !Sim.State.ONLINE.equals(s.getSimState())) { return AjaxResult.error("未连接模拟器,请检查连接!"); } // 最后读取一下模拟器电阻值。 commSendService.readOneExamAtLast(re); if (realExamFaultService.isType2ExamPrepareSubmitOk(re.getExamId())) { re.setExamStatus(RealExam.State.SUBMITTED); // 修改真实考试结束时间。 re.setEndTime(DateUtils.getNowDate()); updateRealExam(re); return AjaxResult.success("成功交卷!"); } else { return AjaxResult.error("失败交卷!"); } } /** * [轮询][学生]结束考试界面。 * * @param examId * @return StudentRealExamPostVo */ public AjaxResult studentLoopPostRealExam(Long examId) { RealExam re = selectRealExamByExamId(examId); StudentRealExamPostVo vo = new StudentRealExamPostVo(); { } vo.setRealExam(re); vo.setListPart1(realExamFaultService.getReportListPart1(examId)); vo.setListPart2(realExamFaultService.getReportListPart2(examId)); vo.setPart3(realExamFaultService.getReportPart3(examId)); return AjaxResult.success(vo); } }