RealExamService.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. package com.ruoyi.sim.service.impl;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Objects;
  5. import com.ruoyi.common.core.domain.AjaxResult;
  6. import com.ruoyi.common.utils.DateUtils;
  7. import com.ruoyi.sim.config.SimConfig;
  8. import com.ruoyi.sim.config.SimDebugConfig;
  9. import com.ruoyi.sim.domain.*;
  10. import com.ruoyi.sim.domain.vo.RealExamVo;
  11. import com.ruoyi.sim.domain.vo.StudentRealExamIngVo;
  12. import com.ruoyi.sim.domain.vo.StudentRealExamPostVo;
  13. import com.ruoyi.sim.domain.vo.StudentRealExamPreVo;
  14. import org.slf4j.Logger;
  15. import org.slf4j.LoggerFactory;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.context.annotation.Lazy;
  18. import org.springframework.stereotype.Service;
  19. import com.ruoyi.sim.mapper.RealExamMapper;
  20. import org.springframework.transaction.annotation.Transactional;
  21. /**
  22. * 考试Service业务层处理
  23. *
  24. * @author tom
  25. * @date 2024-12-15
  26. */
  27. @Service
  28. public class RealExamService {
  29. @Autowired
  30. private RealExamMapper realExamMapper;
  31. /**
  32. * 查询考试
  33. *
  34. * @param examId 考试主键
  35. * @return 考试
  36. */
  37. public RealExam selectRealExamByExamId(Long examId) {
  38. return realExamMapper.selectRealExamByExamId(examId);
  39. }
  40. /**
  41. * 查询考试列表
  42. *
  43. * @param realExam 考试
  44. * @return 考试
  45. */
  46. public List<RealExam> selectRealExamList(RealExam realExam) {
  47. return realExamMapper.selectRealExamList(realExam);
  48. }
  49. /**
  50. * 新增考试
  51. *
  52. * @param realExam 考试
  53. * @return 结果
  54. */
  55. public int insertRealExam(RealExam realExam) {
  56. realExam.setCreateTime(DateUtils.getNowDate());
  57. return realExamMapper.insertRealExam(realExam);
  58. }
  59. /**
  60. * 修改考试
  61. *
  62. * @param realExam 考试
  63. * @return 结果
  64. */
  65. public int updateRealExam(RealExam realExam) {
  66. realExam.setUpdateTime(DateUtils.getNowDate());
  67. return realExamMapper.updateRealExam(realExam);
  68. }
  69. /**
  70. * 批量删除考试
  71. *
  72. * @param examIds 需要删除的考试主键
  73. * @return 结果
  74. */
  75. public int deleteRealExamByExamIds(Long[] examIds) {
  76. return realExamMapper.deleteRealExamByExamIds(examIds);
  77. }
  78. /**
  79. * 删除考试信息
  80. *
  81. * @param examId 考试主键
  82. * @return 结果
  83. */
  84. public int deleteRealExamByExamId(Long examId) {
  85. return realExamMapper.deleteRealExamByExamId(examId);
  86. }
  87. // -------------------------------- tom add --------------------------------
  88. private static final Logger l = LoggerFactory.getLogger(CommSendService.class);
  89. @Autowired
  90. private CommReceiveService commReceiveService;
  91. @Autowired
  92. private StudentService studentService;
  93. @Autowired
  94. private SimService simService;
  95. @Autowired
  96. private SeatService seatService;
  97. @Autowired
  98. private RealExamCollectionService realExamCollectionService;
  99. @Autowired
  100. private RealExamFaultService realExamFaultService;
  101. @Autowired
  102. @Lazy
  103. private CommSendService commSendService;
  104. @Autowired
  105. private SimConfig simConfig;
  106. /**
  107. * examId 是否有效。
  108. *
  109. * @param examId
  110. * @return
  111. */
  112. public boolean exist(Long examId) {
  113. if (examId == null) {
  114. return false;
  115. }
  116. if (examId == 0) {
  117. return false;
  118. }
  119. RealExam re = selectRealExamByExamId(examId);
  120. if (re == null) {
  121. return false;
  122. }
  123. return true;
  124. }
  125. public List<RealExamVo> list(RealExam q) {
  126. List<RealExamVo> list = new ArrayList<>();
  127. realExamMapper.selectRealExamList(q).forEach(re -> {
  128. RealExamVo v = new RealExamVo();
  129. RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  130. v.setRealExam(re);
  131. v.setRealExamCollection(rec);
  132. list.add(v);
  133. });
  134. return list;
  135. }
  136. public List<RealExam> listAllByStatus(String state) {
  137. RealExam q = new RealExam();
  138. q.setExamStatus(state);
  139. return selectRealExamList(q);
  140. }
  141. /**
  142. * 交卷自动修改关联状态
  143. *
  144. * @param examId
  145. * @param state
  146. * @return
  147. */
  148. @Transactional
  149. public int updateOneState(long examId, final String state) {
  150. if (false) {
  151. // php项目维护exam_status字段。java这边不操作。
  152. // 屏蔽
  153. return 0;
  154. }
  155. RealExam q = selectRealExamByExamId(examId);
  156. // todo:屏蔽
  157. if (false && RealExam.State.SUBMITTED.equals(state)) {
  158. // 关联故障list同步锁死。
  159. realExamFaultService.listAllType2State2and3ByExamId(q.getExamId())
  160. .forEach(ref -> {
  161. ref.setRefState(RealExamFault.State.FINISH);
  162. realExamFaultService.updateRealExamFault(ref);
  163. });
  164. }
  165. q.setExamStatus(state);
  166. return updateRealExam(q);
  167. }
  168. /**
  169. * [学生]进入考试。
  170. *
  171. * @return RealExam
  172. */
  173. public AjaxResult studentEnterRealExam(Long examId) {
  174. RealExam re = selectRealExamByExamId(examId);
  175. if (re == null) {
  176. AjaxResult.error("realExamId error!");
  177. }
  178. // todo:应该在登录位置实现
  179. // todo: temp
  180. updateOneState(examId, RealExam.State.LOGGED_IN);
  181. // todo: temp
  182. realExamFaultService.resetAllType2(examId);
  183. return AjaxResult.success(re);
  184. }
  185. /**
  186. * [轮询][学生]准备考试界面。
  187. *
  188. * @param realExamId
  189. * @return StudentRealExamPreVo
  190. */
  191. public AjaxResult studentLoopPrepareRealExam(Long realExamId) {
  192. l.info("studentLoopPrepareRealExam");
  193. // check
  194. if (realExamId == null || realExamId == 0) {
  195. // todo:
  196. }
  197. //
  198. RealExam re = selectRealExamByExamId(realExamId);
  199. if (re == null) {
  200. // todo:
  201. }
  202. // todo: 日期,不可进入考试。
  203. //
  204. // todo: 验证学生登录身份`
  205. Objects.requireNonNull(re);
  206. RealExamCollection collection =
  207. realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  208. // check collection
  209. Sim sim = simService.selectSimBySimId(re.getSimId());
  210. // check sim
  211. Student student = studentService.selectStudentByUserId(re.getUserId());
  212. // check student
  213. Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
  214. // check seat
  215. StudentRealExamPreVo vo = new StudentRealExamPreVo();
  216. vo.setRealExam(re);
  217. vo.setRealExamCollection(collection);
  218. vo.setSim(sim);
  219. vo.setStudent(student);
  220. vo.setSeat(seat);
  221. // todo:多人请求同时进入的问题
  222. boolean next = studentPrepareRealExamCheck(vo);
  223. vo.setNext(next);
  224. if (!next) {
  225. // 执行模拟器通信,让模拟器准备好。
  226. // 异步执行
  227. commSendService.clearListFaultByRealExamAsync(re);
  228. }
  229. l.info("vo = {}", vo);
  230. return AjaxResult.success(vo);
  231. }
  232. public boolean studentPrepareRealExamCheck(StudentRealExamPreVo v) {
  233. if (v == null ||
  234. v.getRealExam() == null ||
  235. v.getRealExamCollection() == null ||
  236. v.getSim() == null ||
  237. v.getStudent() == null ||
  238. v.getSeat() == null) {
  239. return false;
  240. }
  241. // todo:在考试日期内。
  242. // check 一个模拟器的所有选中故障点位都下发成功,准备是否可以
  243. //
  244. // todo:??
  245. // 学生答题中可以再次进入。
  246. String examStatus = v.getRealExam().getExamStatus();
  247. String simStatus = v.getSim().getSimState();
  248. // 兜底
  249. {
  250. if (realExamFaultService.isAllType2StateXiaFa(v.getRealExam().getExamId())) {
  251. // updateOneState(v.getRealExam().getExamId(), RealExam.State.SIM_PREPARE_OK);
  252. }
  253. }
  254. // 模拟器不通信。
  255. if (!simConfig.isCommGlobal()) {
  256. return true;
  257. }
  258. if ((RealExam.State.SIM_PREPARE_OK.equals(examStatus) ||
  259. RealExam.State.ANSWERING.equals(examStatus)) &&
  260. Sim.State.ONLINE.equals(simStatus)
  261. ) {
  262. return true;
  263. }
  264. return false;
  265. }
  266. /**
  267. * [学生]开始考试
  268. *
  269. * @param examId
  270. * @return RealExam
  271. */
  272. @Transactional
  273. public AjaxResult studentStartRealExam(final Long examId) {
  274. l.info("studentStartRealExam = {}", examId);
  275. // todo: 暂时没有解决方案 检查 考试的sim和seat,是否正确对应。
  276. {
  277. // todo:delete
  278. // re.setSimId(getFakeSimId(re));
  279. // l.info("fake re = {}", re);
  280. }
  281. // check id data.
  282. {
  283. AjaxResult arE1 = checkExamId(examId);
  284. if (arE1.isError()) {
  285. return arE1;
  286. }
  287. }
  288. RealExam re = selectRealExamByExamId(examId);
  289. Sim s = simService.selectSimBySimId(re.getSimId());
  290. // Step 1 主动查询一次模拟器状态。获取模拟器在线/离线状态。
  291. {
  292. commSendService.checkOneSimState(s, true);
  293. // 如果模拟器离线
  294. if (s == null || !Sim.State.ONLINE.equals(s.getSimState())) {
  295. return AjaxResult.error("未连接模拟器,请检查连接!");
  296. }
  297. }
  298. // Step 2 读取对应一台模拟器 所有故障部位值。检查模拟器所有的 真实的 故障部位 是否异常 或者 空值。特殊的故障部位要单独判断。
  299. if (SimDebugConfig.CHECK_REPLACE_EMPTY) {
  300. AjaxResult arE2 = commSendService.readOneSimAllFaultCheck(s);
  301. if (arE2.isError()) {
  302. return arE2;
  303. }
  304. }
  305. // Step 3 清除对应一台模拟器 所有 真实的 故障部位故障。
  306. {
  307. commSendService.clearOneSimAllFaultByExam(re);
  308. }
  309. // Step 4 下发对应一台模拟器 出题选中的 故障位置故障。
  310. {
  311. commSendService.writeOneSimAllSelectFaultByExam(re);
  312. }
  313. // Step 5 读取对应一台模拟器 所有的 真实的 故障部位 电阻值代表值 作为出题值。
  314. // 修改关联状态
  315. {
  316. commSendService.readOneSimAllFaultFirstTimeByExam(re);
  317. }
  318. // Step 6 修改当前Exam状态。
  319. if (realExamFaultService.isType2ExamPrepareStartOk(re.getExamId())) {
  320. updateOneState(re.getExamId(), RealExam.State.SIM_PREPARE_OK);
  321. updateOneState(re.getExamId(), RealExam.State.ANSWERING);
  322. // 修改真实考试开始时间。
  323. re.setStartTime(DateUtils.getNowDate());
  324. updateRealExam(re);
  325. return AjaxResult.success("开始考试成功!");
  326. } else {
  327. return AjaxResult.error("连接超时,请检查模拟器连接!");
  328. }
  329. }
  330. public AjaxResult checkExamId(final Long examId) {
  331. // check
  332. // 检查 examId 是否正确存在
  333. {
  334. if (!exist(examId)) {
  335. return AjaxResult.error("对应考试Id不存在!");
  336. }
  337. }
  338. RealExam re = selectRealExamByExamId(examId);
  339. // 检查 seat_id 是否正确存在
  340. {
  341. if (!seatService.exist(re.getSeatId())) {
  342. return AjaxResult.error("对应座Id不存在!");
  343. }
  344. }
  345. // 检查 sim_id 是否正确存在
  346. {
  347. if (!simService.existBySimId(re.getSimId())) {
  348. return AjaxResult.error("对应模拟器Id不存在!");
  349. }
  350. }
  351. return AjaxResult.success();
  352. }
  353. /**
  354. * 根据考试集合获得sim_id
  355. *
  356. * @param exam
  357. * @return
  358. */
  359. @Deprecated
  360. public long getFakeSimId(RealExam exam) {
  361. {
  362. RealExamCollection coll = realExamCollectionService.selectRealExamCollectionByExamCollectionId(exam.getExamCollectionId());
  363. String simType = coll.getSimType();
  364. switch (simType) {
  365. case Sim.TYPE_0001 -> {
  366. return 11L;
  367. }
  368. case Sim.TYPE_0002 -> {
  369. return 12L;
  370. }
  371. case Sim.TYPE_0003 -> {
  372. return 31L;
  373. }
  374. }
  375. }
  376. return 0L;
  377. }
  378. /**
  379. * [轮询][学生]正在考试界面。
  380. *
  381. * @param realExamId
  382. * @return StudentRealExamIngVo
  383. */
  384. public AjaxResult studentLoopAnsweringRealExam(Long realExamId) {
  385. RealExam re = selectRealExamByExamId(realExamId);
  386. RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  387. StudentRealExamIngVo vo = new StudentRealExamIngVo();
  388. vo.setRealExam(re);
  389. long remaining = (re.getStartTime().getTime() + rec.getLimitDuration() * 60 * 1000) - DateUtils.getNowDate().getTime();
  390. vo.setRemainingMilliseconds(remaining);
  391. vo.setCompulsiveSubmit(remaining >= RealExam.EXAM_TIMEOUT_LIMIT);
  392. l.info("studentLoopAnsweringRealExam vo = {}", vo);
  393. return AjaxResult.success(vo);
  394. }
  395. /**
  396. * [学生]交卷
  397. *
  398. * @param examId
  399. * @return RealExam
  400. */
  401. @Transactional
  402. public AjaxResult studentSubmitRealExam(Long examId) {
  403. RealExam re = selectRealExamByExamId(examId);
  404. {
  405. // todo:delete
  406. // re.setSimId(getFakeSimId(re));
  407. }
  408. // check part.
  409. {
  410. AjaxResult arE1 = checkExamId(examId);
  411. if (arE1.isError()) {
  412. return arE1;
  413. }
  414. }
  415. // 检查一下模拟器状态。
  416. Sim s = simService.selectSimBySimId(re.getSimId());
  417. // 如果模拟器离线
  418. if (s == null || !Sim.State.ONLINE.equals(s.getSimState())) {
  419. return AjaxResult.error("未连接模拟器,请检查连接!");
  420. }
  421. // 最后读取一下模拟器电阻值。
  422. commSendService.readOneExamAtLast(re);
  423. if (realExamFaultService.isType2ExamPrepareSubmitOk(re.getExamId())) {
  424. re.setExamStatus(RealExam.State.SUBMITTED);
  425. // 修改真实考试结束时间。
  426. re.setEndTime(DateUtils.getNowDate());
  427. updateRealExam(re);
  428. return AjaxResult.success("成功交卷!");
  429. } else {
  430. return AjaxResult.error("失败交卷!");
  431. }
  432. }
  433. /**
  434. * [轮询][学生]结束考试界面。
  435. *
  436. * @param examId
  437. * @return StudentRealExamPostVo
  438. */
  439. public AjaxResult studentLoopPostRealExam(Long examId) {
  440. RealExam re = selectRealExamByExamId(examId);
  441. StudentRealExamPostVo vo = new StudentRealExamPostVo();
  442. {
  443. }
  444. vo.setRealExam(re);
  445. vo.setListPart1(realExamFaultService.getReportListPart1(examId));
  446. vo.setListPart2(realExamFaultService.getReportListPart2(examId));
  447. vo.setPart3(realExamFaultService.getReportPart3(examId));
  448. return AjaxResult.success(vo);
  449. }
  450. }