最近在搞一个含有在线考试模块的系统。既然是考试,必不可少的一部分就是计时了。
总的来说,需要计时的系统大体上分两种:1.规定时间内的倒计时。2.规定时间段内的计时判断。
对于第一种倒计时,我觉得下面这篇文章写的很好,这是链接:
https://blog.csdn.net/a13432421434/article/details/71346153
对于第二种倒计时,我并没有找到合适的,首先介绍一下这个项目的实现,总的是前后端分离,前端试用的是vue,后台用的是spring-boot。
一开始的想法:就单纯前端获取Internet时间,然后把与后台返还来的结束时间对比,如果结束就结束。后来一想- -如果考生懂点JS知识,控制台几行代码就可以无限延长考试时间了。
(PS:当然了,也会有很多人说用闭包写进去就行了)但是这样是不好的,同样存在隐患。
后来的想法:前端无时无刻在调用一个接口,将当前的时间传给后台(或是间隔一段时间传),这样是存在并发隐患,因为考试的不可能就是一个学生,spring-boot通俗的讲只有一个入口,这样无论间隔多久- -一起传送的数据仍然是一起的,占用过大。
再后来的想法:前端不传数据,由后台单独计时,从开始时间计时到规定的结束时间,一旦时间结束了,那就向前端发送考试结束的信息。但是转念一想,也是不存在的。如果是这样,前端还是需要不断的执行一个函数,和上面的想法几乎没差别不过就是避免了被懂js的人修改作弊。
最后的想法:利用springboot中的schelling机制,添加计时任务,比如每10分钟,5分钟检查一次考试的数据表数据。通过当前时间和规定开始时间结束时间的比较,来更新当前数据状态下的考试状态。未考,考中,已考。
代码如下
1.现在你的项目启动项APPLICATION中加入如下的注解
@EnableScheduling //boot定时任务
这是spring-boot的计时任务的必要注解。
2.新建一个类,输入如下代码
package com.project.myBeans; import com.project.entity.Exam; import com.project.service.ExamService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RestController; import java.sql.Timestamp; import java.util.List; /** * @Author: TateBrown * @date: 2018/7/3 15:51 * @param: * @return: */ @Component @EnableAutoConfiguration @RestController public class SchedulingTask { @Autowired ExamService examService; /** * @Description:定时扫描所有考试任务修改考试state. * @param: [] * @return: void * @author: TateBrown * @Date: 2018/7/3 */ @Scheduled(fixedRate = 60000)//每隔一分钟查一次 public void CheckExam() { List<Exam> list = examService.GetAllExam(); Timestamp curtime = new Timestamp(System.currentTimeMillis()); for (Exam tmp : list) { Timestamp tmpfinishtime = tmp.getFinishtime(); Timestamp tmpstarttime = tmp.getStarttime(); if (curtime.before(tmpstarttime)) { tmp.setState(1); examService.modify(tmp); } else if ((curtime.equals(tmpstarttime) || curtime.after(tmpstarttime)) && (curtime.equals(tmpfinishtime) || curtime.before(tmpfinishtime))) { tmp.setState(2); examService.modify(tmp); } else { tmp.setState(3); examService.modify(tmp); } } } }
我这里的数据是由时间戳进行存储的,所以上述也是时间戳比较前后的方法。其中第一句是获取全部考试的信息,第二句是获取当前的系统时间并转换成时间戳模式,后面的就是根据当前时间判断是否更新数据。
优化:存在的优化空间就是对于已考过的考试,我们不需要这样扫描,我们每次之找出全部未考和考中的考试。这样的做法是必然需要的,但是这里我就不贴代码了。
总结:最后的做法就是符合了逻辑,因为这必然是有几场考试同时发生的存在的。而且完全由后台控制,较被修改的可能较小。
逻辑上讲,后台才是控制进程的地方,前后端分离的特性应该是把前端后端尽可能的分离开来。可能还有更好的方法。