基于javaweb的SpringBoot在线考试系统(java+springboot+vue+mysql+maven)

运行环境

Java≥8、MySQL≥5.7、Node.js≥14

开发工具

后端:eclipse/idea/myeclipse/sts等均可配置运行
前端:WebStorm/VSCode/HBuilderX等均可

❗没学过node.js的不要搞前后端分离项目

适用

课程设计,大作业,毕业设计,项目练习,学习演示等

功能说明

290123072402

300123072402

310123072402

320123072402

330123072402

基于javaweb的SpringBoot在线考试系统(java+springboot+vue+mysql+maven)

一、项目简述

本系统主要实现的功能有: 学生以及老师的注册登录,在线考试,错题查询,学生管理,问题管理,错题管理,错题查询,分数查询,试卷管 理,人工组卷。自动组卷,教师,班级,统计等等管理功能。

二、项目运行

环境配置: Jdk1.8 + mysql + Eclispe (IntelliJ IDEA,Eclispe,MyEclispe,Sts 都支持)

项目技术: VUE+Springboot+ SpringMVC + MyBatis + JavaScript + JQuery + Ajax + maven等等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@ApiOperation("获取所有问题的列表")
ResultVO<List<QuestionVo>> getQuestionAll() {
ResultVO<List<QuestionVo>> resultVO;
try {
List<QuestionVo> questionAll = examService.getQuestionAll();
resultVO = new ResultVO<>(0, "获取全部问题列表成功", questionAll);
} catch (Exception e) {
e.printStackTrace();
resultVO = new ResultVO<>(-1, "获取全部问题列表失败", null);
}
return resultVO;
}

@PostMapping("/question/update")
@ApiOperation("更新问题")
ResultVO<QuestionVo> questionUpdate(@RequestBody QuestionVo questionVo) {
// 完成问题的更新
System.out.println(questionVo);
try {
QuestionVo questionVoResult = examService.updateQuestion(questionVo);
return new ResultVO<>(0, "更新问题成功", questionVoResult);
} catch (Exception e) {
e.printStackTrace();
return new ResultVO<>(-1, "更新问题失败", null);
}
}

@PostMapping("/question/create")
@ApiOperation("创建问题")
ResultVO<String> questionCreate(@RequestBody QuestionCreateSimplifyVo questionCreateSimplifyVo, HttpServletRequest request) {
QuestionCreateVo questionCreateVo = new QuestionCreateVo();
// 把能拷贝过来的属性都拷贝过来
BeanUtils.copyProperties(questionCreateSimplifyVo, questionCreateVo);
// 设置创建者信息
String userId = (String) request.getAttribute("user_id");
questionCreateVo.setQuestionCreatorId(userId);
System.out.println(questionCreateVo);
try {
examService.questionCreate(questionCreateVo);
return new ResultVO<>(0, "问题创建成功", null);
} catch (Exception e) {
e.printStackTrace();
return new ResultVO<>(-1, "创建问题失败", null);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@GetMapping("/card/list")
@ApiOperation("获取考试列表,适配前端卡片列表")
ResultVO<List<ExamCardVo>> getExamCardList() {
// 获取考试列表卡片
ResultVO<List<ExamCardVo>> resultVO;
try {
List<ExamCardVo> examCardVoList = examService.getExamCardList();
resultVO = new ResultVO<>(0, "获取考试列表卡片成功", examCardVoList);
} catch (Exception e) {
e.printStackTrace();
resultVO = new ResultVO<>(-1, "获取考试列表卡片失败", null);
}
return resultVO;
}

@GetMapping("/detail/{id}")
@ApiOperation("根据考试的id,获取考试详情")
ResultVO<ExamDetailVo> getExamDetail(@PathVariable String id) {
// 根据id获取考试详情
ResultVO<ExamDetailVo> resultVO;
try {
ExamDetailVo examDetail = examService.getExamDetail(id);
resultVO = new ResultVO<>(0, "获取考试详情成功", examDetail);
} catch (Exception e) {
resultVO = new ResultVO<>(-1, "获取考试详情失败", null);
}
return resultVO;
}

@PostMapping("/finish/{examId}")
@ApiOperation("根据用户提交的答案对指定id的考试判分")
ResultVO<ExamRecord> finishExam(@PathVariable String examId, @RequestBody HashMap<String, List<String>> answersMap, HttpServletRequest request) {
ResultVO<ExamRecord> resultVO;
try {
// 拦截器里设置上的用户id
String userId = (String) request.getAttribute("user_id");
// 下面根据用户提交的信息进行判分,返回用户的得分情况
ExamRecord examRecord = examService.judge(userId, examId, answersMap);
resultVO = new ResultVO<>(0, "考卷提交成功", examRecord);
} catch (Exception e) {
e.printStackTrace();
resultVO = new ResultVO<>(-1, "考卷提交失败", null);
}
return resultVO;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41


/**
* https://stackoverflow.com/questions/43591582/application-properties-value-in-spring-boot-interceptor
*
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {

/**
* 有上面的@Component才能使得这个属性能从pplication.yml中取得拦截器的值
*/
@Value("${interceptors.auth-ignore-uris}")
private String authIgnoreUris;

/**
* 进入controller之前进行拦截
*
* @param request 请求体
* @param response 响应体
* @param handler 处理者
* @return 是否继续往下走
* @throws Exception 拦截中出的异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入拦截器啦!");
String uri = request.getRequestURI();
System.out.println(uri);
System.out.println("无需拦截的接口路径:" + authIgnoreUris);
String[] authIgnoreUriArr = authIgnoreUris.split(",");
// 登录和注册接口不需要进行token拦截和校验
for (String authIgnoreUri : authIgnoreUriArr) {
if (authIgnoreUri.equals(uri)) {
return true;
}
}
// 注意要和前端适配Access-Token属性,前端会在登陆后的每个接口请求头加Access-Token属性
String token = request.getHeader("Access-Token");
if (token == null) {
// token不在header中时,也可能在参数中(RequestParam)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
        }
// 注意要和前端适配Access-Token属性,前端会在登陆后的每个接口请求头加Access-Token属性
String token = request.getHeader("Access-Token");
if (token == null) {
// token不在header中时,也可能在参数中(RequestParam)
token = request.getParameter("token");
}
if (token != null) {
// 请求中是携带参数的
Claims claims = JwtUtils.checkJWT(token);
if (claims == null) {
// 返回null说明用户篡改了token,导致校验失败
sendJsonMessage(response, JsonData.buildError("token无效,请重新登录"));
return false;
}
// 用户的的主键id
String id = (String) claims.get("id");
// 用户名
String username = (String) claims.get("username");
// 把这两个参数放到请求中,从而可以在controller中获取到,不需要在controller中在用Jwt解密了,request.getAttribute("属性名")即可获取
request.setAttribute("user_id", id);
request.setAttribute("username", username);
return true;
}
sendJsonMessage(response, JsonData.buildError("token为null,请先登录!"));
return false;
}

/**
* 响应数据给前端
*
* @param response 响应
* @param obj 返回的消息体
* @throws Exception 处理异常
*/
public static void sendJsonMessage(HttpServletResponse response, Object obj) throws Exception {
Gson g = new Gson();
response.setContentType("application/json; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(g.toJson(obj));
writer.close();
response.flushBuffer();
}
}
package com.huawei.l00379880.exam.controller;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 ***********************************************************/
package com.huawei.l00379880.exam.interceptor;



/**
* https://stackoverflow.com/questions/43591582/application-properties-value-in-spring-boot-interceptor
*
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {

/**
* 有上面的@Component才能使得这个属性能从pplication.yml中取得拦截器的值
*/
@Value("${interceptors.auth-ignore-uris}")
private String authIgnoreUris;

/**
* 进入controller之前进行拦截
*
* @param request 请求体
* @param response 响应体
* @param handler 处理者
* @return 是否继续往下走
* @throws Exception 拦截中出的异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入拦截器啦!");
String uri = request.getRequestURI();
System.out.println(uri);
System.out.println("无需拦截的接口路径:" + authIgnoreUris);
String[] authIgnoreUriArr = authIgnoreUris.split(",");
// 登录和注册接口不需要进行token拦截和校验
for (String authIgnoreUri : authIgnoreUriArr) {
if (authIgnoreUri.equals(uri)) {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39


/**
* https://stackoverflow.com/questions/43591582/application-properties-value-in-spring-boot-interceptor
*
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {

/**
* 有上面的@Component才能使得这个属性能从pplication.yml中取得拦截器的值
*/
@Value("${interceptors.auth-ignore-uris}")
private String authIgnoreUris;

/**
* 进入controller之前进行拦截
*
* @param request 请求体
* @param response 响应体
* @param handler 处理者
* @return 是否继续往下走
* @throws Exception 拦截中出的异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入拦截器啦!");
String uri = request.getRequestURI();
System.out.println(uri);
System.out.println("无需拦截的接口路径:" + authIgnoreUris);
String[] authIgnoreUriArr = authIgnoreUris.split(",");
// 登录和注册接口不需要进行token拦截和校验
for (String authIgnoreUri : authIgnoreUriArr) {
if (authIgnoreUri.equals(uri)) {
return true;
}
}
// 注意要和前端适配Access-Token属性,前端会在登陆后的每个接口请求头加Access-Token属性
String token = request.getHeader("Access-Token");


项目链接:
https://javayms.github.io?id=011222062008200wb
https://javayms.pages.dev?id=011222062008200wb