diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index c8b1f76..383a244 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -10,4 +10,9 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..dfc8d74
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 03d9549..49798c3 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -1,6 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..2b63946
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 6c0b863..94a25f7 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 5f10b26..a899b07 100644
--- a/README.md
+++ b/README.md
@@ -1,108 +1,18 @@
-# spring-boot-online-exam
-
-> 在线Demo预览,http://129.211.88.191 ,账户分别是admin、teacher、student,密码是admin123。视频讲解代码:https://www.bilibili.com/video/BV1FP4y1L7xt/
-
-> 好消息!!!一个小伙伴做了Python实现,欢迎大家star:https://github.com/xingxingzaixian/django-drf-online-exam
-
-## 1.快速体验
-### 1.1 事先准备
-> clone代码并进入代码路径
-
-```shell
-git clone git@github.com:lsgwr/spring-boot-online-exam.git
-cd spring-boot-online-exam
-```
-
-下面按照Linux和windows说明快速搭建的方法
-### 1.2 Linux
-执行代码下的脚本start.sh即可
-
-然后访问 http://ip:80 即可访问自己搭建的考试系统
-
-### 1.3 windows
-+ 1.安装JDK,推荐JDK8
-+ 2.从官方仓库下载发布的jar包,建议选择最新版:https://github.com/lsgwr/spring-boot-online-exam/releases
-+ 3.安装MySQL,创建数据库exam,并初始化密码为aA111111,导入doc/sql/exam.sql文件来创建数据库
-+ 4.启动jar包:`java -jar exam.jar`
-+ 5.访问:http://ip:9527 即可访问自己搭建的考试系统
-
-## 2.介绍
-基于springboot的在线考试系统
-
-### 2.1 功能简介
-
-+ 支持单选题、多选题、判断题
-+ 支持学生(student)、教师(teacher)、管理员(admin)三种角色
- + 学生:参加考试和查看我的考试
- + 教师:学生的所有权限+创建/编辑题目+创建/编辑考试
- + 管理员:教师的所有权限+管理用户
-
-### 2.3 软件架构
-
-> 前后端分离,前段组件化,方便二次开发;后端
-
-+ 后端采用SpringBoot+JPA++Swagger2+JWT校验,根据不同用户的权限返回给用户不同的数据
-+ 后端采用Vue+AntDesign,组件化拆分,封装了很多年公共组件,方便维护和二次开发
-
-### 2.3 使用教程
-
-+ 1.下载代码
- ```shell
- git clone https://github.com/19920625lsg/spring-boot-online-exam.git
- ```
-+ 2.初始化数据库
- > 安装mysql的步骤这里省略,网上的教程很多。安装好mysql后,新建exam数据库,密码和`spring-boot-online-exam/backend/exam/src/main/resources/application.yml`的`password: xxxxxx`保持一致,然后导入`spring-boot-online-exam/doc/sql/exam.sql`
-+ 3.启动后端
- > 打开`spring-boot-online-exam/backend/exam`这个Maven项目,可以在IDE里启动或者执行`mvn install`生成jar包启动
-+ 4.启动前端
- + 进入到前端代码路径 `cd spring-boot-online-exam/frontend/exam/`
- + 安装依赖 `npm install`
- + 启动前端 `npm run serve`
-+ 5.部署完毕,查看效果
- > 打开 http://localhost:8000 或者 http://本机ip:8000 即可查看演示效果
-
-## 3.功能图示
-
-+ 1.管理题目
- + 1.1 题目列表
- > ![题目查看](doc/images/question_list.png)
- + 1.2 题目创建
- > ![题目创建](doc/images/question_create.png)
- + 1.3 题目更新
- > ![题目更新](doc/images/question_update.png)
-+ 2.考试管理
- + 2.1 考试列表
- > ![考试查看](doc/images/exam_list.png)
- + 2.2 考试创建
- > ![考试创建](doc/images/exam_create.png)
- + 2.3 考试更新(`还有点小bug,开发中`)
- > ![考试更新](doc/images/exam_update.png)
-+ 3.我的考试
- + 3.1 参加考试
- > 在"考试列表"模块点击自己想参加的考试卡片即可
- > ![参加考试1](doc/images/exam_join.png)
- > ![参加考试2](doc/images/exam_join2.png)
- + 3.2 考试记录查看
- > ![考试记录查看](doc/images/exam_detail.png)
-
-## 4.参与贡献
-
-1. Fork 本仓库
-2. 新建 exam_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
-
-## 5.Todo
-+ `√`0.修复issue提地bug:题目创建失败
-+ `√`1.考试详情编辑
-+ 2.支持题目和考试的删除`删除的话比较麻烦,先不做了,最好是弄个visible字段,不实际删除,要不后面有些关联数据找不到就不好了`
- > 如果题目有关联的考试则必须先删除对应的考试,反过来删除考试则不用删除题目
-+ 3.图片改成base64存到数据库中
-+ 4.题干和选项支持富文本
-+ 5.支持批量导入题目
-+ 6.新增用户管理、学科管理功能
-+ 7.老师能考到所有学生的成绩以及考试的统计信息
-+ 8.更多的数据分析功能
-+ 9.支持容器化一键式部署(编好Dockerfile)
-+ 10.支持移动端,最好用uniapp做
-+ ......抓紧做吧,争取每周末做一点......
+ ResultVO getExamRecordDetail(@PathVariable String recordId) {
+ // 定义返回结果
+ ResultVO resultVO;
+ try {
+ // 调用examService获取考试记录详情
+ RecordDetailVo recordDetailVo = examService.getRecordDetail(recordId);
+ // 返回成功结果
+ resultVO = new ResultVO<>(0, "获取考试记录详情成功", recordDetailVo);
+ } catch (Exception e) {
+ // 打印异常信息
+ e.printStackTrace();
+ // 返回失败结果
+ resultVO = new ResultVO<>(-1, "获取考试记录详情失败", null);
+ }
+ // 返回结果
+ return resultVO;
+ }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/lsgwr/exam/controller/ExamController.java b/backend/src/main/java/lsgwr/exam/controller/ExamController.java
index e6ed4d0..99b50ad 100644
--- a/backend/src/main/java/lsgwr/exam/controller/ExamController.java
+++ b/backend/src/main/java/lsgwr/exam/controller/ExamController.java
@@ -29,28 +29,39 @@ public class ExamController {
@GetMapping("/question/all")
@ApiOperation("获取所有问题的列表")
- ResultVO> getQuestionAll() {
+ // 获取全部问题列表。
+ResultVO> getQuestionAll() {
+ // 定义返回值。
ResultVO> resultVO;
try {
+ // 调用examService获取全部问题列表。
List 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 questionUpdate(@RequestBody QuestionVo questionVo) {
- // 完成问题的更新
+ // 完成问题的更新。
System.out.println(questionVo);
try {
+ // 调用examService的updateQuestion方法,更新问题。
QuestionVo questionVoResult = examService.updateQuestion(questionVo);
+ // 返回更新成功的结果。
return new ResultVO<>(0, "更新问题成功", questionVoResult);
} catch (Exception e) {
+ // 打印异常信息。
e.printStackTrace();
+ // 返回更新失败的结果。
return new ResultVO<>(-1, "更新问题失败", null);
}
}
@@ -58,106 +69,134 @@ public class ExamController {
@PostMapping("/question/create")
@ApiOperation("创建问题")
ResultVO questionCreate(@RequestBody QuestionCreateSimplifyVo questionCreateSimplifyVo, HttpServletRequest request) {
+ // 创建一个QuestionCreateVo对象。
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方法创建问题。
examService.questionCreate(questionCreateVo);
+ // 返回问题创建成功的ResultVO。
return new ResultVO<>(0, "问题创建成功", null);
} catch (Exception e) {
+ // 打印异常信息。
e.printStackTrace();
+ // 返回创建问题失败的ResultVO。
return new ResultVO<>(-1, "创建问题失败", null);
}
}
@GetMapping("/question/selection")
@ApiOperation("获取问题分类的相关选项")
- ResultVO getSelections() {
+ // 获取问题分类选项。
+ResultVO getSelections() {
+ // 调用examService的getSelections方法获取问题分类选项。
QuestionSelectionVo questionSelectionVo = examService.getSelections();
+ // 如果获取成功。
if (questionSelectionVo != null) {
+ // 返回成功的结果。
return new ResultVO<>(0, "获取问题分类选项成功", questionSelectionVo);
} else {
+ // 否则返回失败的结果。
return new ResultVO<>(-1, "获取问题分类选项失败", null);
}
}
-
@GetMapping("/question/detail/{id}")
@ApiOperation("根据问题的id获取问题的详细信息")
ResultVO getQuestionDetail(@PathVariable String id) {
- // 根据问题id获取问题的详细信息
+ // 根据问题id获取问题的详细信息。
System.out.println(id);
ResultVO resultVO;
try {
+ // 调用examService的getQuestionDetail方法,根据问题id获取问题的详细信息。
QuestionDetailVo questionDetailVo = examService.getQuestionDetail(id);
+ // 如果获取成功,则返回ResultVO对象,状态码为0,提示信息为"获取问题详情成功",数据为questionDetailVo。
resultVO = new ResultVO<>(0, "获取问题详情成功", questionDetailVo);
} catch (Exception e) {
+ // 如果获取失败,则打印异常信息,并返回ResultVO对象,状态码为-1,提示信息为"获取问题详情失败",数据为null。
e.printStackTrace();
resultVO = new ResultVO<>(-1, "获取问题详情失败", null);
}
+ // 返回ResultVO对象。
return resultVO;
}
-
@GetMapping("/all")
@ApiOperation("获取全部考试的列表")
ResultVO> getExamAll() {
- // 需要拼接前端需要的考试列表对象
+ // 需要拼接前端需要的考试列表对象。
ResultVO> resultVO;
try {
+ // 调用examService的getExamAll方法获取全部考试的列表。
List examVos = examService.getExamAll();
+ // 将获取到的考试列表封装到ResultVO对象中,并返回。
resultVO = new ResultVO<>(0, "获取全部考试的列表成功", examVos);
} catch (Exception e) {
+ // 捕获异常,并打印异常信息。
e.printStackTrace();
+ // 将异常信息封装到ResultVO对象中,并返回。
resultVO = new ResultVO<>(-1, "获取全部考试的列表失败", null);
}
return resultVO;
}
-
@GetMapping("/question/type/list")
@ApiOperation("获取问题列表,按照单选、多选和判断题分类返回")
ResultVO getExamQuestionTypeList() {
- // 获取问题的分类列表
+ // 获取问题的分类列表。
ResultVO resultVO;
try {
+ // 调用examService的getExamQuestionType方法获取问题分类列表。
ExamQuestionTypeVo examQuestionTypeVo = examService.getExamQuestionType();
+ // 如果获取成功,则返回成功的结果。
resultVO = new ResultVO<>(0, "获取问题列表成功", examQuestionTypeVo);
} catch (Exception e) {
+ // 如果获取失败,则打印异常信息,并返回失败的结果。
e.printStackTrace();
resultVO = new ResultVO<>(-1, "获取问题列表失败", null);
}
+ // 返回结果。
return resultVO;
}
-
@PostMapping("/create")
@ApiOperation("创建考试")
ResultVO createExam(@RequestBody ExamCreateVo examCreateVo, HttpServletRequest request) {
- // 从前端传参数过来,在这里完成考试的入库
+ // 从前端传参数过来,在这里完成考试的入库。
ResultVO resultVO;
+ // 获取当前用户的id。
String userId = (String) request.getAttribute("user_id");
try {
+ // 调用examService的create方法,将examCreateVo和userId作为参数传入,创建考试。
Exam exam = examService.create(examCreateVo, userId);
+ // 创建一个ResultVO对象,将创建成功的考试信息返回。
resultVO = new ResultVO<>(0, "创建考试成功", exam);
} catch (Exception e) {
+ // 捕获异常,打印异常信息,并创建一个ResultVO对象,将创建失败的考试信息返回。
e.printStackTrace();
resultVO = new ResultVO<>(-1, "创建考试失败", null);
}
+ // 返回ResultVO对象。
return resultVO;
}
@PostMapping("/update")
@ApiOperation("更新考试")
ResultVO updateExam(@RequestBody ExamVo examVo, HttpServletRequest request) {
- // 从前端传参数过来,在这里完成考试的入库
+ // 从前端传参数过来,在这里完成考试的入库。
ResultVO resultVO;
+ // 获取当前用户id。
String userId = (String) request.getAttribute("user_id");
try {
+ // 调用service层更新考试。
Exam exam = examService.update(examVo, userId);
+ // 返回更新成功的resultVO。
resultVO = new ResultVO<>(0, "更新考试成功", exam);
} catch (Exception e) {
+ // 打印异常信息。
e.printStackTrace();
+ // 返回更新失败的resultVO。
resultVO = new ResultVO<>(-1, "更新考试失败", null);
}
return resultVO;
@@ -166,22 +205,26 @@ public class ExamController {
@GetMapping("/card/list")
@ApiOperation("获取考试列表,适配前端卡片列表")
ResultVO> getExamCardList() {
- // 获取考试列表卡片
+ // 获取考试列表卡片。
ResultVO> resultVO;
try {
+ // 调用examService的getExamCardList方法获取考试列表卡片。
List 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 getExamDetail(@PathVariable String id) {
- // 根据id获取考试详情
+ // 根据id获取考试详情。
ResultVO resultVO;
try {
ExamDetailVo examDetail = examService.getExamDetail(id);
@@ -194,16 +237,20 @@ public class ExamController {
@PostMapping("/finish/{examId}")
@ApiOperation("根据用户提交的答案对指定id的考试判分")
+ // 完成考试
ResultVO finishExam(@PathVariable String examId, @RequestBody HashMap> answersMap, HttpServletRequest request) {
+ // 定义返回结果。
ResultVO resultVO;
try {
- // 拦截器里设置上的用户id
+ // 拦截器里设置上的用户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;
@@ -211,12 +258,13 @@ public class ExamController {
@GetMapping("/record/list")
@ApiOperation("获取当前用户的考试记录")
+ // 获取考试记录列表。
ResultVO> getExamRecordList(HttpServletRequest request) {
ResultVO> resultVO;
try {
- // 拦截器里设置上的用户id
+ // 拦截器里设置上的用户id。
String userId = (String) request.getAttribute("user_id");
- // 下面根据用户账号拿到他(她所有的考试信息),注意要用VO封装下
+ // 下面根据用户账号拿到他(她所有的考试信息),注意要用VO封装下。
List examRecordVoList = examService.getExamRecordList(userId);
resultVO = new ResultVO<>(0, "获取考试记录成功", examRecordVoList);
} catch (Exception e) {
@@ -229,14 +277,20 @@ public class ExamController {
@GetMapping("/record/detail/{recordId}")
@ApiOperation("根据考试记录id获取考试记录详情")
ResultVO getExamRecordDetail(@PathVariable String recordId) {
+ // 定义返回结果。
ResultVO resultVO;
try {
+ // 调用examService获取考试记录详情。
RecordDetailVo recordDetailVo = examService.getRecordDetail(recordId);
+ // 返回成功结果。
resultVO = new ResultVO<>(0, "获取考试记录详情成功", recordDetailVo);
} catch (Exception e) {
+ // 打印异常信息。
e.printStackTrace();
+ // 返回失败结果。
resultVO = new ResultVO<>(-1, "获取考试记录详情失败", null);
}
+ // 返回结果。
return resultVO;
}
}
diff --git a/backend/src/main/java/lsgwr/exam/controller/UploadDownloadController.java b/backend/src/main/java/lsgwr/exam/controller/UploadDownloadController.java
index e9d2392..35c7cc2 100644
--- a/backend/src/main/java/lsgwr/exam/controller/UploadDownloadController.java
+++ b/backend/src/main/java/lsgwr/exam/controller/UploadDownloadController.java
@@ -42,37 +42,49 @@ public class UploadDownloadController {
@ApiOperation("单文件上传,支持同时传入参数")
@PostMapping("/api/upload/singleAndparas")
- public String uploadFileSingle(@RequestParam("dir") String dir, @RequestParam("file") MultipartFile uploadfile) {
+ // 单文件上传。
+ public String uploadFileSingle(@RequestParam("dir") String dir, @RequestParam("file") MultipartFile uploadfile) {
+ // 调用FileTransUtil工具类中的uploadFile方法,将上传的文件和目录作为参数传入。
return FileTransUtil.uploadFile(uploadfile, dir);
}
+ // 单文件上传,支持同时传入参数,Model。
@ApiOperation("单文件上传,支持同时传入参数,Model")
@PostMapping("/upload/single/model")
public String singleUploadFileModel(@ModelAttribute("model") UploadModel2 model) {
+ // 调用FileTransUtil工具类中的uploadFile方法,将上传的文件和目录作为参数传入。
return FileTransUtil.uploadFile(model.getFile(), model.getDir());
}
+ // 多文件上传,支持同时传入参数。
@ApiOperation("多文件上传,支持同时传入参数")
@PostMapping("upload/multiAndparas")
public String uploadFileMulti(@RequestParam("dir") String dir, @RequestParam("files") MultipartFile[] uploadfiles) {
+ // 调用FileTransUtil工具类中的uploadFiles方法,将上传的文件数组和目录作为参数传入。
return FileTransUtil.uploadFiles(uploadfiles, dir);
}
+ // 多文件上传,支持同时传入参数。
@ApiOperation("多文件上传,支持同时传入参数")
@PostMapping(value = "/upload/multi/model")
public String multiUploadFileModel(@ModelAttribute(("model")) UploadModel model) {
+ // 调用FileTransUtil工具类中的uploadFiles方法,将上传的文件数组和目录作为参数传入。
return FileTransUtil.uploadFiles(model.getFiles(), model.getDir());
}
+ // Get下载文件
@ApiOperation("Get下载文件")
@GetMapping(value = "/download/get")
public ResponseEntity downloadFileGet(@RequestParam String filePath) throws IOException {
+ // 调用FileTransUtil工具类中的downloadFile方法,将文件路径作为参数传入。
return FileTransUtil.downloadFile(filePath);
}
+ // Post下载文件
@ApiOperation("Post下载文件")
@PostMapping(value = "/download/post")
public ResponseEntity downloadFilePost(@RequestBody DownloadQo downloadQo) throws IOException {
+ // 调用FileTransUtil工具类中的downloadFile方法,将文件路径作为参数传入。
return FileTransUtil.downloadFile(downloadQo.getPath());
}
}
diff --git a/backend/src/main/java/lsgwr/exam/controller/UserController.java b/backend/src/main/java/lsgwr/exam/controller/UserController.java
index 768b84d..af0536a 100644
--- a/backend/src/main/java/lsgwr/exam/controller/UserController.java
+++ b/backend/src/main/java/lsgwr/exam/controller/UserController.java
@@ -62,18 +62,27 @@ public class UserController {
@GetMapping("/user-info")
@ApiOperation("获取用户信息")
+ // 根据请求获取用户信息
ResultVO getUserInfo(HttpServletRequest request) {
+ // 从请求中获取用户ID
String userId = (String) request.getAttribute("user_id");
+ // 根据用户ID获取用户信息
UserVo userVo = userService.getUserInfo(userId);
+ // 返回用户信息
return new ResultVO<>(ResultEnum.GET_INFO_SUCCESS.getCode(), ResultEnum.GET_INFO_SUCCESS.getMessage(), userVo);
}
@GetMapping("/info")
@ApiOperation("获取用户的详细信息,包括个人信息页面和操作权限")
+ // 获取用户信息的接口
ResultVO getInfo(HttpServletRequest request) {
+ // 打印进入接口的日志
System.out.println("进入/user/info的获取用户信息的接口");
+ // 获取用户ID
String userId = (String) request.getAttribute("user_id");
+ // 调用userService获取用户信息
UserInfoVo userInfoVo = userService.getInfo(userId);
+ // 返回结果
return new ResultVO<>(ResultEnum.GET_INFO_SUCCESS.getCode(), ResultEnum.GET_INFO_SUCCESS.getMessage(), userInfoVo);
}
@@ -87,4 +96,4 @@ public class UserController {
System.out.println("用户名:" + username);
return "用户id:" + userId + "\n用户名:" + username;
}
-}
+}
\ No newline at end of file
diff --git a/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java b/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java
index e58d825..ca1099d 100644
--- a/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java
+++ b/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java
@@ -10,8 +10,8 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lsgwr.exam.entity.*;
import lsgwr.exam.enums.QuestionEnum;
-import lsgwr.exam.service.ExamService;
import lsgwr.exam.repository.*;
+import lsgwr.exam.service.ExamService;
import lsgwr.exam.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
@@ -40,9 +40,9 @@ public class ExamServiceImpl implements ExamService {
private final QuestionOptionRepository questionOptionRepository;
public ExamServiceImpl(QuestionRepository questionRepository, UserRepository userRepository, QuestionLevelRepository questionLevelRepository, QuestionTypeRepository questionTypeRepository, QuestionCategoryRepository questionCategoryRepository, QuestionOptionRepository questionOptionRepository, ExamRepository examRepository, ExamRecordRepository examRecordRepository) {
- this.questionRepository = questionRepository;
- this.userRepository = userRepository;
- this.questionLevelRepository = questionLevelRepository;
+ this.questionRepository = questionRepository;//
+ this.userRepository = userRepository;//
+ this.questionLevelRepository = questionLevelRepository;//
this.questionTypeRepository = questionTypeRepository;
this.questionCategoryRepository = questionCategoryRepository;
this.questionOptionRepository = questionOptionRepository;
@@ -57,9 +57,9 @@ public class ExamServiceImpl implements ExamService {
}
private List getQuestionVos(List questionList) {
- // 需要自定义的question列表
+ // 需要自定义的question列表,
List questionVoList = new ArrayList<>();
- // 循环完成每个属性的定制
+ // 循环完成每个属性的定制,
for (Question question : questionList) {
QuestionVo questionVo = getQuestionVo(question);
questionVoList.add(questionVo);
@@ -69,9 +69,9 @@ public class ExamServiceImpl implements ExamService {
private QuestionVo getQuestionVo(Question question) {
QuestionVo questionVo = new QuestionVo();
- // 先复制能复制的属性
+ // 先复制能复制的属性,
BeanUtils.copyProperties(question, questionVo);
- // 设置问题的创建者
+ // 设置问题的创建者。
questionVo.setQuestionCreator(
Objects.requireNonNull(
userRepository.findById(
@@ -79,7 +79,7 @@ public class ExamServiceImpl implements ExamService {
).orElse(null)
).getUserUsername());
- // 设置问题的难度
+ // 设置问题的难度。
questionVo.setQuestionLevel(
Objects.requireNonNull(
questionLevelRepository.findById(
@@ -87,7 +87,7 @@ public class ExamServiceImpl implements ExamService {
).orElse(null)
).getQuestionLevelDescription());
- // 设置题目的类别,比如单选、多选、判断等
+ // 设置题目的类别,比如单选、多选、判断等。
questionVo.setQuestionType(
Objects.requireNonNull(
questionTypeRepository.findById(
@@ -95,7 +95,7 @@ public class ExamServiceImpl implements ExamService {
).orElse(null)
).getQuestionTypeDescription());
- // 设置题目分类,比如数学、语文、英语、生活、人文等
+ // 设置题目分类,比如数学、语文、英语、生活、人文等。
questionVo.setQuestionCategory(
Objects.requireNonNull(
questionCategoryRepository.findById(
@@ -104,20 +104,20 @@ public class ExamServiceImpl implements ExamService {
).getQuestionCategoryName()
);
- // 选项的自定义Vo列表
+ // 选项的自定义Vo列表。
List optionVoList = new ArrayList<>();
- // 获得所有的选项列表
+ // 获得所有的选项列表。
List optionList = questionOptionRepository.findAllById(
Arrays.asList(question.getQuestionOptionIds().split("-"))
);
- // 获取所有的答案列表optionList中每个option的isAnswer选项
+ // 获取所有的答案列表optionList中每个option的isAnswer选项。
List answerList = questionOptionRepository.findAllById(
Arrays.asList(question.getQuestionAnswerOptionIds().split("-"))
);
- // 根据选项和答案的id相同设置optionVo的isAnswer属性
+ // 根据选项和答案的id相同设置optionVo的isAnswer属性。
for (QuestionOption option : optionList) {
QuestionOptionVo optionVo = new QuestionOptionVo();
BeanUtils.copyProperties(option, optionVo);
@@ -129,14 +129,14 @@ public class ExamServiceImpl implements ExamService {
optionVoList.add(optionVo);
}
- // 设置题目的所有选项
+ // 设置题目的所有选项。
questionVo.setQuestionOptionVoList(optionVoList);
return questionVo;
}
@Override
public QuestionVo updateQuestion(QuestionVo questionVo) {
- // 1.把需要的属性都设置好
+ // 1.把需要的属性都设置好。
StringBuilder questionAnswerOptionIds = new StringBuilder();
List questionOptionList = new ArrayList<>();
List questionOptionVoList = questionVo.getQuestionOptionVoList();
@@ -148,26 +148,26 @@ public class ExamServiceImpl implements ExamService {
questionOptionList.add(questionOption);
if (questionOptionVo.getAnswer()) {
if (i != size - 1) {
- // 把更新后的答案的id加上去,记得用-连到一起
+ // 把更新后的答案的id加上去,记得用-连到一起。
questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId()).append("-");
} else {
- // 最后一个不需要用-连接
+ // 最后一个不需要用-连接。
questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId());
}
}
}
- // 1.更新问题
+ // 1.更新问题。
Question question = questionRepository.findById(questionVo.getQuestionId()).orElse(null);
assert question != null;
BeanUtils.copyProperties(questionVo, question);
question.setQuestionAnswerOptionIds(questionAnswerOptionIds.toString());
questionRepository.save(question);
- // 2.更新所有的option
+ // 2.更新所有的option。
questionOptionRepository.saveAll(questionOptionList);
- // 返回更新后的问题,方便前端局部刷新
+ // 返回更新后的问题,方便前端局部刷新。
return getQuestionVo(question);
}
@@ -175,58 +175,64 @@ public class ExamServiceImpl implements ExamService {
public void questionCreate(QuestionCreateVo questionCreateVo) {
// 问题创建
Question question = new Question();
- // 把能复制的属性都复制过来
+ // 把能复制的属性都复制过来。
BeanUtils.copyProperties(questionCreateVo, question);
- // 设置下questionOptionIds和questionAnswerOptionIds,需要自己用Hutool生成下
+ // 设置下questionOptionIds和questionAnswerOptionIds,需要自己用Hutool生成下。
List questionOptionList = new ArrayList<>();
List questionOptionCreateVoList = questionCreateVo.getQuestionOptionCreateVoList();
for (QuestionOptionCreateVo questionOptionCreateVo : questionOptionCreateVoList) {
QuestionOption questionOption = new QuestionOption();
- // 设置选项的的内容
+ // 设置选项的的内容。
questionOption.setQuestionOptionContent(questionOptionCreateVo.getQuestionOptionContent());
- // 设置选项的id
+ // 设置选项的id。
questionOption.setQuestionOptionId(IdUtil.simpleUUID());
questionOptionList.add(questionOption);
}
- // 把选项都存起来,然后才能用于下面设置Question的questionOptionIds和questionAnswerOptionIds
+ // 把选项都存起来,然后才能用于下面设置Question的questionOptionIds和questionAnswerOptionIds。
questionOptionRepository.saveAll(questionOptionList);
String questionOptionIds = "";
String questionAnswerOptionIds = "";
- // 经过上面的saveAll方法,所有的option的主键id都已经持久化了
+ // 经过上面的saveAll方法,所有的option的主键id都已经持久化了。
for (int i = 0; i < questionOptionCreateVoList.size(); i++) {
- // 获取指定选项
+ // 获取指定选项。
QuestionOptionCreateVo questionOptionCreateVo = questionOptionCreateVoList.get(i);
- // 获取保存后的指定对象
+ // 获取保存后的指定对象。
QuestionOption questionOption = questionOptionList.get(i);
questionOptionIds += questionOption.getQuestionOptionId() + "-";
if (questionOptionCreateVo.getAnswer()) {
- // 如果是答案的话
+ // 如果是答案的话。
questionAnswerOptionIds += questionOption.getQuestionOptionId() + "-";
}
}
- // 把字符串最后面的"-"给去掉
+ // 把字符串最后面的"-"给去掉。
questionAnswerOptionIds = replaceLastSeparator(questionAnswerOptionIds);
questionOptionIds = replaceLastSeparator(questionOptionIds);
- // 设置选项id组成的字符串
+ // 设置选项id组成的字符串。
question.setQuestionOptionIds(questionOptionIds);
- // 设置答案选项id组成的字符串
+ // 设置答案选项id组成的字符串。
question.setQuestionAnswerOptionIds(questionAnswerOptionIds);
- // 自己生成问题的id
+ // 自己生成问题的id。
question.setQuestionId(IdUtil.simpleUUID());
- // 先把创建时间和更新时间每次都取当前时间吧
+ // 先把创建时间和更新时间每次都取当前时间吧。
question.setCreateTime(new Date());
question.setUpdateTime(new Date());
- // 保存问题到数据库
+ // 保存问题到数据库。
questionRepository.save(question);
}
@Override
+ // 重写父类方法,获取问题选择列表
public QuestionSelectionVo getSelections() {
+ // 创建问题选择对象
QuestionSelectionVo questionSelectionVo = new QuestionSelectionVo();
+ // 设置问题类别列表
questionSelectionVo.setQuestionCategoryList(questionCategoryRepository.findAll());
+ // 设置问题等级列表
questionSelectionVo.setQuestionLevelList(questionLevelRepository.findAll());
+ // 设置问题类型列表
questionSelectionVo.setQuestionTypeList(questionTypeRepository.findAll());
+ // 返回问题选择对象
return questionSelectionVo;
}
@@ -245,10 +251,15 @@ public class ExamServiceImpl implements ExamService {
@Override
public QuestionDetailVo getQuestionDetail(String id) {
+ // 根据id查询问题
Question question = questionRepository.findById(id).orElse(null);
+ // 创建问题详情对象
QuestionDetailVo questionDetailVo = new QuestionDetailVo();
+ // 设置问题id
questionDetailVo.setId(id);
+ // 设置问题名称
questionDetailVo.setName(question.getQuestionName());
+ // 设置问题描述
questionDetailVo.setDescription(question.getQuestionDescription());
// 问题类型,单选题/多选题/判断题
questionDetailVo.setType(
@@ -258,10 +269,10 @@ public class ExamServiceImpl implements ExamService {
).orElse(null)
).getQuestionTypeDescription()
);
- // 获取当前问题的选项
+ // 获取当前问题的选项。
String optionIdsStr = trimMiddleLine(question.getQuestionOptionIds());
String[] optionIds = optionIdsStr.split("-");
- // 获取选项列表
+ // 获取选项列表。
List optionList = questionOptionRepository.findAllById(Arrays.asList(optionIds));
questionDetailVo.setOptions(optionList);
return questionDetailVo;
@@ -274,14 +285,14 @@ public class ExamServiceImpl implements ExamService {
}
private List getExamVos(List examList) {
- // 需要自定义的exam列表
+ // 需要自定义的exam列表。
List examVoList = new ArrayList<>();
- // 循环完成每个属性的定制
+ // 循环完成每个属性的定制。
for (Exam exam : examList) {
ExamVo examVo = new ExamVo();
- // 先尽量复制能复制的所有属性
+ // 先尽量复制能复制的所有属性。
BeanUtils.copyProperties(exam, examVo);
- // 设置问题的创建者
+ // 设置问题的创建者。
examVo.setExamCreator(
Objects.requireNonNull(
userRepository.findById(
@@ -290,7 +301,7 @@ public class ExamServiceImpl implements ExamService {
).getUserUsername()
);
- // 获取所有单选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上
+ // 获取所有单选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上。
List radioQuestionVoList = new ArrayList<>();
List radioQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsRadio().split("-"))
@@ -298,12 +309,12 @@ public class ExamServiceImpl implements ExamService {
for (Question question : radioQuestionList) {
ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, radioQuestionVo);
- radioQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
+ radioQuestionVo.setChecked(true); // 考试中的问题肯定被选中的。
radioQuestionVoList.add(radioQuestionVo);
}
examVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);
- // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上
+ // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上。
List checkQuestionVoList = new ArrayList<>();
List checkQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsCheck().split("-"))
@@ -311,12 +322,12 @@ public class ExamServiceImpl implements ExamService {
for (Question question : checkQuestionList) {
ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, checkQuestionVo);
- checkQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
+ checkQuestionVo.setChecked(true); // 考试中的问题肯定被选中的。
checkQuestionVoList.add(checkQuestionVo);
}
examVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);
- // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上
+ // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上。
List judgeQuestionVoList = new ArrayList<>();
List judgeQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsJudge().split("-"))
@@ -324,7 +335,7 @@ public class ExamServiceImpl implements ExamService {
for (Question question : judgeQuestionList) {
ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, judgeQuestionVo);
- judgeQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
+ judgeQuestionVo.setChecked(true); // 考试中的问题肯定被选中的。
judgeQuestionVoList.add(judgeQuestionVo);
}
examVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList);
@@ -340,7 +351,7 @@ public class ExamServiceImpl implements ExamService {
@Override
public ExamQuestionTypeVo getExamQuestionType() {
ExamQuestionTypeVo examQuestionTypeVo = new ExamQuestionTypeVo();
- // 获取所有单选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上
+ // 获取所有单选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上。
List radioQuestionVoList = new ArrayList<>();
List radioQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.RADIO.getId());
for (Question question : radioQuestionList) {
@@ -350,7 +361,7 @@ public class ExamServiceImpl implements ExamService {
}
examQuestionTypeVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);
- // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上
+ // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上。
List checkQuestionVoList = new ArrayList<>();
List checkQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.CHECK.getId());
for (Question question : checkQuestionList) {
@@ -360,7 +371,7 @@ public class ExamServiceImpl implements ExamService {
}
examQuestionTypeVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);
- // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上
+ // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上。
List judgeQuestionVoList = new ArrayList<>();
List judgeQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.JUDGE.getId());
for (Question question : judgeQuestionList) {
@@ -374,7 +385,7 @@ public class ExamServiceImpl implements ExamService {
@Override
public Exam create(ExamCreateVo examCreateVo, String userId) {
- // 在线考试系统创建
+ // 在线考试系统创建。
Exam exam = new Exam();
BeanUtils.copyProperties(examCreateVo, exam);
exam.setExamId(IdUtil.simpleUUID());
@@ -382,35 +393,53 @@ public class ExamServiceImpl implements ExamService {
exam.setCreateTime(new Date());
exam.setUpdateTime(new Date());
// Todo:这两个日志后面是要在前端传入的,这里暂时定为当前日期
- exam.setExamStartDate(new Date());
- exam.setExamEndDate(new Date());
- String radioIdsStr = "";
- String checkIdsStr = "";
- String judgeIdsStr = "";
- List radios = examCreateVo.getRadios();
- List checks = examCreateVo.getChecks();
- List judges = examCreateVo.getJudges();
- int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
- for (ExamQuestionSelectVo radio : radios) {
- if (radio.getChecked()) {
- radioIdsStr += radio.getQuestionId() + "-";
- radioCnt++;
- }
- }
- radioIdsStr = replaceLastSeparator(radioIdsStr);
- for (ExamQuestionSelectVo check : checks) {
- if (check.getChecked()) {
- checkIdsStr += check.getQuestionId() + "-";
- checkCnt++;
- }
- }
- checkIdsStr = replaceLastSeparator(checkIdsStr);
- for (ExamQuestionSelectVo judge : judges) {
- if (judge.getChecked()) {
- judgeIdsStr += judge.getQuestionId() + "-";
- judgeCnt++;
- }
- }
+ // 设置考试开始时间和结束时间
+ exam.setExamStartDate(new Date());
+ exam.setExamEndDate(new Date());
+ // 初始化选择题、判断题和单选题的id字符串
+ String radioIdsStr = "";
+ String checkIdsStr = "";
+ String judgeIdsStr = "";
+ // 获取选择题、判断题和单选题的列表
+ List radios = examCreateVo.getRadios();
+ List checks = examCreateVo.getChecks();
+ List judges = examCreateVo.getJudges();
+ // 初始化选择题、判断题和单选题的计数器
+ int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
+ // 遍历单选题列表
+ for (ExamQuestionSelectVo radio : radios) {
+ // 如果单选题被选中
+ if (radio.getChecked()) {
+ // 将单选题的id添加到字符串中
+ radioIdsStr += radio.getQuestionId() + "-";
+ // 单选题计数器加1
+ radioCnt++;
+ }
+ }
+ // 去掉最后一个分隔符
+ radioIdsStr = replaceLastSeparator(radioIdsStr);
+ // 遍历判断题列表
+ for (ExamQuestionSelectVo check : checks) {
+ // 如果判断题被选中
+ if (check.getChecked()) {
+ // 将判断题的id添加到字符串中
+ checkIdsStr += check.getQuestionId() + "-";
+ // 判断题计数器加1
+ checkCnt++;
+ }
+ }
+ // 去掉最后一个分隔符
+ checkIdsStr = replaceLastSeparator(checkIdsStr);
+ // 遍历选择题列表
+ for (ExamQuestionSelectVo judge : judges) {
+ // 如果选择题被选中
+ if (judge.getChecked()) {
+ // 将选择题的id添加到字符串中
+ judgeIdsStr += judge.getQuestionId() + "-";
+ // 选择题计数器加1
+ judgeCnt++;
+ }
+ }
judgeIdsStr = replaceLastSeparator(judgeIdsStr);
exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
// 设置各个题目的id
@@ -427,49 +456,64 @@ public class ExamServiceImpl implements ExamService {
@Override
public Exam update(ExamVo examVo, String userId) {
+ // 创建一个新的Exam对象
Exam exam = new Exam();
+ // 将examVo中的属性值复制到exam对象中
BeanUtils.copyProperties(examVo, exam);
- exam.setExamCreatorId(userId); // 考试的更新人为最新的创建人
- exam.setUpdateTime(new Date()); // 考试的更新日期要记录下
+ // 设置考试更新人为最新的创建人
+ exam.setExamCreatorId(userId);
+ // 记录考试更新日期
+ exam.setUpdateTime(new Date());
+ // 初始化选择题、判断题、单选题的id字符串
String radioIdsStr = "";
String checkIdsStr = "";
- String judgeIdsStr = "";
+ String judgeIdsStr = "";//djfndiosbchjdshjiowceduikmn
+ // 获取选择题、判断题、单选题的列表
List radios = examVo.getExamQuestionSelectVoRadioList();
List checks = examVo.getExamQuestionSelectVoCheckList();
List judges = examVo.getExamQuestionSelectVoJudgeList();
+ // 初始化选择题、判断题、单选题的数量
int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
+ // 遍历选择题列表,将选中的题目id添加到字符串中,并统计选择题数量
for (ExamQuestionSelectVo radio : radios) {
if (radio.getChecked()) {
radioIdsStr += radio.getQuestionId() + "-";
radioCnt++;
}
}
+ // 去除最后一个分隔符
radioIdsStr = replaceLastSeparator(radioIdsStr);
+ // 遍历判断题列表,将选中的题目id添加到字符串中,并统计判断题数量
for (ExamQuestionSelectVo check : checks) {
if (check.getChecked()) {
checkIdsStr += check.getQuestionId() + "-";
checkCnt++;
}
}
+ // 去除最后一个分隔符
checkIdsStr = replaceLastSeparator(checkIdsStr);
+ // 遍历单选题列表,将选中的题目id添加到字符串中,并统计单选题数量
for (ExamQuestionSelectVo judge : judges) {
if (judge.getChecked()) {
judgeIdsStr += judge.getQuestionId() + "-";
judgeCnt++;
}
}
+ // 去除最后一个分隔符。
judgeIdsStr = replaceLastSeparator(judgeIdsStr);
+ // 设置各个题目的id。
exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
- // 设置各个题目的id
exam.setExamQuestionIdsRadio(radioIdsStr);
exam.setExamQuestionIdsCheck(checkIdsStr);
exam.setExamQuestionIdsJudge(judgeIdsStr);
- // 计算总分数
+ // 计算总分数。
int examScore = radioCnt * exam.getExamScoreRadio() + checkCnt * exam.getExamScoreCheck() + judgeCnt * exam.getExamScoreJudge();
exam.setExamScore(examScore);
+ // 保存exam对象。
examRepository.save(exam);
+ // 返回exam对象。
return exam;
}
@@ -499,53 +543,53 @@ public class ExamServiceImpl implements ExamService {
@Override
public ExamRecord judge(String userId, String examId, HashMap> answersMap) {
- // 开始考试判分啦~~~
- // 1.首先获取考试对象和选项数组
+ // 开始考试判分啦~~~。
+ // 1.首先获取考试对象和选项数组。
ExamDetailVo examDetailVo = getExamDetail(examId);
Exam exam = examDetailVo.getExam();
- // 2.然后获取该考试下所有的题目信息
+ // 2.然后获取该考试下所有的题目信息。
List questionIds = new ArrayList<>();
- // 2.1 题目id的数组
+ // 2.1 题目id的数组。
List radioIdList = Arrays.asList(examDetailVo.getRadioIds());
List checkIdList = Arrays.asList(examDetailVo.getCheckIds());
List judgeIdList = Arrays.asList(examDetailVo.getJudgeIds());
questionIds.addAll(radioIdList);
questionIds.addAll(checkIdList);
questionIds.addAll(judgeIdList);
- // 2.2 每种题目的分数
+ // 2.2 每种题目的分数。
int radioScore = exam.getExamScoreRadio();
int checkScore = exam.getExamScoreCheck();
int judgeScore = exam.getExamScoreJudge();
- // 2.3 根据问题id的数组拿到所有的问题对象,供下面步骤用
+ // 2.3 根据问题id的数组拿到所有的问题对象,供下面步骤用。
List questionList = questionRepository.findAllById(questionIds);
Map questionMap = new HashMap<>();
for (Question question : questionList) {
questionMap.put(question.getQuestionId(), question);
}
- // 3.根据正确答案和用户作答信息进行判分
+ // 3.根据正确答案和用户作答信息进行判分。
Set questionIdsAnswer = answersMap.keySet();
- // 存储当前考试每个题目的得分情况
+ // 存储当前考试每个题目的得分情况。
Map judgeMap = new HashMap<>();
- // 考生作答地每个题目的选项(题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔),用于查看考试详情
- // 例子:题目1的id_作答选项1-作答选项2&题目2的id_作答选项1&题目3_作答选项1-作答选项2-作答选项3
+ // 考生作答地每个题目的选项(题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔),用于查看考试详情。
+ // 例子:题目1的id_作答选项1-作答选项2&题目2的id_作答选项1&题目3_作答选项1-作答选项2-作答选项3。
StringBuilder answerOptionIdsSb = new StringBuilder();
- // 用户此次考试的总分
+ // 用户此次考试的总分。
int totalScore = 0;
for (String questionId : questionIdsAnswer) {
- // 获取用户作答地这个题的答案信息
+ // 获取用户作答地这个题的答案信息。
Question question = questionMap.get(questionId);
- // 获取答案选项
+ // 获取答案选项。
String questionAnswerOptionIds = replaceLastSeparator(question.getQuestionAnswerOptionIds());
List questionAnswerOptionIdList = Arrays.asList(questionAnswerOptionIds.split("-"));
Collections.sort(questionAnswerOptionIdList);
String answerStr = listConcat(questionAnswerOptionIdList);
- // 获取用户作答
+ // 获取用户作答。
List questionUserOptionIdList = answersMap.get(questionId);
Collections.sort(questionUserOptionIdList);
String userStr = listConcat(questionUserOptionIdList);
- // 判断questionAnswerOptionIds和answersMap里面的答案是否相等
+ // 判断questionAnswerOptionIds和answersMap里面的答案是否相等。
if (answerStr.equals(userStr)) {
- // 说明题目作答正确,下面根据题型给分
+ // 说明题目作答正确,下面根据题型给分。
int score = 0;
if (radioIdList.contains(questionId)) {
score = radioScore;
@@ -556,22 +600,22 @@ public class ExamServiceImpl implements ExamService {
if (judgeIdList.contains(questionId)) {
score = judgeScore;
}
- // 累计本次考试得分
+ // 累计本次考试得分。
totalScore += score;
- // True代表题目答对
+ // True代表题目答对。
answerOptionIdsSb.append(questionId + "@True_" + userStr + "$");
judgeMap.put(questionId, score);
} else {
- // 说明题目作答错误,直接判零分,False代表题目答错
+ // 说明题目作答错误,直接判零分,False代表题目答错。
answerOptionIdsSb.append(questionId + "@False_" + userStr + "$");
judgeMap.put(questionId, 0);
}
}
- // 4.计算得分,记录本次考试结果,存到ExamRecord中
+ // 4.计算得分,记录本次考试结果,存到ExamRecord中。
ExamRecord examRecord = new ExamRecord();
examRecord.setExamRecordId(IdUtil.simpleUUID());
examRecord.setExamId(examId);
- // 注意去掉最后可能有的&_-
+ // 注意去掉最后可能有的&_-。
examRecord.setAnswerOptionIds(replaceLastSeparator(answerOptionIdsSb.toString()));
examRecord.setExamJoinerId(userId);
examRecord.setExamJoinDate(new Date());
@@ -582,58 +626,60 @@ public class ExamServiceImpl implements ExamService {
@Override
public List getExamRecordList(String userId) {
- // 获取指定用户下的考试记录列表
+ // 获取指定用户下的考试记录列表。
List examRecordList = examRecordRepository.findByExamJoinerIdOrderByExamJoinDateDesc(userId);
List examRecordVoList = new ArrayList<>();
for (ExamRecord examRecord : examRecordList) {
ExamRecordVo examRecordVo = new ExamRecordVo();
+ // 根据考试记录中的考试ID获取考试信息
Exam exam = examRepository.findById(examRecord.getExamId()).orElse(null);
examRecordVo.setExam(exam);
+ // 根据用户ID获取用户信息
User user = userRepository.findById(userId).orElse(null);
examRecordVo.setUser(user);
+ // 设置考试记录信息
examRecordVo.setExamRecord(examRecord);
examRecordVoList.add(examRecordVo);
}
return examRecordVoList;
}
-
@Override
public RecordDetailVo getRecordDetail(String recordId) {
- // 获取考试详情的封装对象
+ // 获取考试详情的封装对象。
ExamRecord record = examRecordRepository.findById(recordId).orElse(null);
RecordDetailVo recordDetailVo = new RecordDetailVo();
recordDetailVo.setExamRecord(record);
- // 用户的答案,需要解析
+ // 用户的答案,需要解析。
HashMap> answersMap = new HashMap<>();
HashMap resultsMap = new HashMap<>();
assert record != null;
String answersStr = record.getAnswerOptionIds();
- // $分隔题目,因为$在正则中有特殊用途(行尾),所以需要括起来
+ // $分隔题目,因为$在正则中有特殊用途(行尾),所以需要括起来。
String[] questionArr = answersStr.split("[$]");
for (String questionStr : questionArr) {
System.out.println(questionStr);
- // 区分开题目标题和选项
+ // 区分开题目标题和选项。
String[] questionTitleResultAndOption = questionStr.split("_");
String[] questionTitleAndResult = questionTitleResultAndOption[0].split("@");
String[] questionOptions = questionTitleResultAndOption[1].split("-");
- // 题目:答案选项
+ // 题目:答案选项。
answersMap.put(questionTitleAndResult[0], Arrays.asList(questionOptions));
- // 题目:True / False
+ // 题目:True / False。
resultsMap.put(questionTitleAndResult[0], questionTitleAndResult[1]);
}
recordDetailVo.setAnswersMap(answersMap);
recordDetailVo.setResultsMap(resultsMap);
- // 下面再计算正确答案的map
+ // 下面再计算正确答案的map。
ExamDetailVo examDetailVo = getExamDetail(record.getExamId());
List questionIdList = new ArrayList<>();
questionIdList.addAll(Arrays.asList(examDetailVo.getRadioIds()));
questionIdList.addAll(Arrays.asList(examDetailVo.getCheckIds()));
questionIdList.addAll(Arrays.asList(examDetailVo.getJudgeIds()));
- // 获取所有的问题对象
+ // 获取所有的问题对象。
List questionList = questionRepository.findAllById(questionIdList);
HashMap> answersRightMap = new HashMap<>();
for (Question question : questionList) {
- // 记得去掉最后可能出现的特殊字符
+ // 记得去掉最后可能出现的特殊字符。
String questionAnswerOptionIdsStr = replaceLastSeparator(question.getQuestionAnswerOptionIds());
String[] questionAnswerOptionIds = questionAnswerOptionIdsStr.split("-");
answersRightMap.put(question.getQuestionId(), Arrays.asList(questionAnswerOptionIds));
@@ -641,7 +687,6 @@ public class ExamServiceImpl implements ExamService {
recordDetailVo.setAnswersRightMap(answersRightMap);
return recordDetailVo;
}
-
/**
* 把字符串最后一个字符-替换掉
*
@@ -650,7 +695,7 @@ public class ExamServiceImpl implements ExamService {
*/
private String replaceLastSeparator(String str) {
String lastChar = str.substring(str.length() - 1);
- // 题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔
+ // 题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔。
if ("-".equals(lastChar) || "_".equals(lastChar) || "$".equals(lastChar)) {
str = StrUtil.sub(str, 0, str.length() - 1);
}
@@ -672,3 +717,4 @@ public class ExamServiceImpl implements ExamService {
return replaceLastSeparator(sb.toString());
}
}
+//注释完毕
\ No newline at end of file
diff --git a/frontend/src/components/Menu/menu.js b/frontend/src/components/Menu/menu.js
index 458c19f..be5b0e0 100644
--- a/frontend/src/components/Menu/menu.js
+++ b/frontend/src/components/Menu/menu.js
@@ -6,20 +6,24 @@ const { Item, SubMenu } = Menu
export default {
name: 'SMenu',
props: {
+ // 菜单数据
menu: {
type: Array,
required: true
},
+ // 主题
theme: {
type: String,
required: false,
default: 'dark'
},
+ // 模式
mode: {
type: String,
required: false,
default: 'inline'
},
+ // 是否折叠
collapsed: {
type: Boolean,
required: false,
@@ -28,12 +32,16 @@ export default {
},
data () {
return {
+ // 打开的菜单项
openKeys: [],
+ // 选择的菜单项
selectedKeys: [],
+ // 缓存的打开的菜单项
cachedOpenKeys: []
}
},
computed: {
+ // 根菜单项的key
rootSubmenuKeys: vm => {
const keys = []
vm.menu.forEach(item => keys.push(item.path))
@@ -41,23 +49,29 @@ export default {
}
},
mounted () {
+ // 组件挂载时更新菜单
this.updateMenu()
},
watch: {
+ // 监听折叠状态的变化
collapsed (val) {
if (val) {
+ // 折叠时,缓存打开的菜单项
this.cachedOpenKeys = this.openKeys.concat()
this.openKeys = []
} else {
+ // 展开时,恢复打开的菜单项
this.openKeys = this.cachedOpenKeys
}
},
+ // 监听路由的变化
$route: function () {
+ // 更新菜单
this.updateMenu()
}
},
methods: {
- // select menu item
+ // 选择菜单项
onOpenChange (openKeys) {
// 在水平模式下时执行,并且不再执行后续
if (this.mode === 'horizontal') {
@@ -72,6 +86,7 @@ export default {
this.openKeys = latestOpenKey ? [latestOpenKey] : []
}
},
+ // 更新菜单
updateMenu () {
const routes = this.$route.matched.concat()
const { hidden } = this.$route.meta
@@ -91,13 +106,14 @@ export default {
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
},
- // render
+ // 渲染
renderItem (menu) {
if (!menu.hidden) {
return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
}
return null
},
+ // 渲染菜单项
renderMenuItem (menu) {
const target = menu.meta.target || null
const tag = target && 'a' || 'router-link'
@@ -122,6 +138,7 @@ export default {
)
},
+ // 渲染子菜单
renderSubMenu (menu) {
const itemArr = []
if (!menu.hideChildrenInMenu) {
@@ -137,6 +154,7 @@ export default {
)
},
+ // 渲染图标
renderIcon (icon) {
if (icon === 'none' || icon === undefined) {
return null
@@ -177,4 +195,4 @@ export default {
)
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/components/_util/util.js b/frontend/src/components/_util/util.js
index dd33231..651534e 100644
--- a/frontend/src/components/_util/util.js
+++ b/frontend/src/components/_util/util.js
@@ -7,7 +7,9 @@
* @param children
* @returns {*[]}
*/
+// 导出一个函数,用于过滤掉空节点
export function filterEmpty (children = []) {
+ // 过滤掉没有tag属性和text属性为空字符串的节点
return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
}
@@ -15,12 +17,17 @@ export function filterEmpty (children = []) {
* 获取字符串长度,英文字符 长度1,中文字符长度2
* @param {*} str
*/
+// 导出一个函数,用于获取字符串长度
export const getStrFullLength = (str = '') =>
+ // 将字符串分割成字符数组,然后使用reduce方法遍历数组,计算每个字符的长度
str.split('').reduce((pre, cur) => {
+ // 获取字符的Unicode编码
const charCode = cur.charCodeAt(0)
+ // 如果字符的Unicode编码在0-128之间,说明是英文字符,长度为1
if (charCode >= 0 && charCode <= 128) {
return pre + 1
}
+ // 否则,说明是中文字符,长度为2
return pre + 2
}, 0)
@@ -29,18 +36,26 @@ export const getStrFullLength = (str = '') =>
* @param {*} str
* @param {*} maxLength
*/
+// 导出一个函数,用于截取字符串
export const cutStrByFullLength = (str = '', maxLength) => {
+ // 初始化显示长度为0
let showLength = 0
+ // 将字符串分割成字符数组,然后使用reduce方法遍历数组,截取字符串
return str.split('').reduce((pre, cur) => {
+ // 获取字符的Unicode编码
const charCode = cur.charCodeAt(0)
+ // 如果字符的Unicode编码在0-128之间,说明是英文字符,长度为1
if (charCode >= 0 && charCode <= 128) {
showLength += 1
} else {
+ // 否则,说明是中文字符,长度为2
showLength += 2
}
+ // 如果显示长度小于等于maxLength,则将字符添加到结果中
if (showLength <= maxLength) {
return pre + cur
}
+ // 否则,返回结果
return pre
}, '')
}
diff --git a/frontend/src/views/account/settings/AvatarModal.vue b/frontend/src/views/account/settings/AvatarModal.vue
index 0ad9c41..6c0f31c 100644
--- a/frontend/src/views/account/settings/AvatarModal.vue
+++ b/frontend/src/views/account/settings/AvatarModal.vue
@@ -6,8 +6,10 @@
:confirmLoading="confirmLoading"
:width="800"
@cancel="cancelHandel">
+
+
+
+
取消
保存
@@ -44,18 +48,18 @@ export default {
*/
data () {
return {
- visible: false,
- id: null,
- confirmLoading: false,
+ visible: false, // 弹窗是否可见
+ id: null, // 头像id
+ confirmLoading: false, // 确认按钮是否加载中
options: {
- img: '/avatar2.jpg',
- autoCrop: true,
- autoCropWidth: 200,
- autoCropHeight: 200,
- fixedBox: true
+ img: '/avatar2.jpg', // 默认图片
+ autoCrop: true, // 是否自动裁剪
+ autoCropWidth: 200, // 自动裁剪宽度
+ autoCropHeight: 200, // 自动裁剪高度
+ fixedBox: true // 固定框
},
- previews: {}
+ previews: {} // 预览数据
}
},
methods: {
@@ -106,4 +110,4 @@ export default {
height: 100%;
}
}
-
+
\ No newline at end of file
diff --git a/frontend/src/views/account/settings/BaseSetting.vue b/frontend/src/views/account/settings/BaseSetting.vue
index 2980c28..9740680 100644
--- a/frontend/src/views/account/settings/BaseSetting.vue
+++ b/frontend/src/views/account/settings/BaseSetting.vue
@@ -1,26 +1,33 @@
+
+
+
+
+
+
+
+
提交
保存
@@ -35,18 +43,25 @@
+
+
+
+
+
+
@@ -54,6 +69,7 @@