From b4a34f3397ea3723cec66fb4dbd5952034e138ec Mon Sep 17 00:00:00 2001 From: hunqiu Date: Thu, 21 Nov 2024 17:38:53 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86contrellon?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 126 +++--------------- .../lsgwr/exam/controller/ExamController.java | 66 ++++++++- .../controller/UploadDownloadController.java | 14 +- .../lsgwr/exam/controller/UserController.java | 9 +- 4 files changed, 98 insertions(+), 117 deletions(-) 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..4230346 100644 --- a/backend/src/main/java/lsgwr/exam/controller/ExamController.java +++ b/backend/src/main/java/lsgwr/exam/controller/ExamController.java @@ -29,15 +29,22 @@ 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; } @@ -47,10 +54,14 @@ public class ExamController { // 完成问题的更新 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,6 +69,7 @@ public class ExamController { @PostMapping("/question/create") @ApiOperation("创建问题") ResultVO questionCreate(@RequestBody QuestionCreateSimplifyVo questionCreateSimplifyVo, HttpServletRequest request) { + // 创建一个QuestionCreateVo对象 QuestionCreateVo questionCreateVo = new QuestionCreateVo(); // 把能拷贝过来的属性都拷贝过来 BeanUtils.copyProperties(questionCreateSimplifyVo, questionCreateVo); @@ -66,25 +78,33 @@ public class ExamController { 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) { @@ -92,58 +112,72 @@ public class ExamController { 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; } @@ -152,12 +186,17 @@ public class ExamController { 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; @@ -169,12 +208,16 @@ public class ExamController { // 获取考试列表卡片 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; } @@ -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 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,6 +258,7 @@ public class ExamController { @GetMapping("/record/list") @ApiOperation("获取当前用户的考试记录") + // 获取考试记录列表 ResultVO> getExamRecordList(HttpServletRequest request) { ResultVO> resultVO; try { @@ -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..55f3352 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..679b26e 100644 --- a/backend/src/main/java/lsgwr/exam/controller/UserController.java +++ b/backend/src/main/java/lsgwr/exam/controller/UserController.java @@ -70,10 +70,15 @@ public class UserController { @GetMapping("/info") @ApiOperation("获取用户的详细信息,包括个人信息页面和操作权限") - ResultVO getInfo(HttpServletRequest request) { + // 获取用户信息的接口 +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 +92,4 @@ public class UserController { System.out.println("用户名:" + username); return "用户id:" + userId + "\n用户名:" + username; } -} +} \ No newline at end of file -- 2.34.1 From 0fe04dc92acf0792b62b69c4da57e9caa773c61d Mon Sep 17 00:00:00 2001 From: hunqiu Date: Fri, 22 Nov 2024 13:34:45 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E6=AC=A1=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lsgwr/exam/controller/UserController.java | 101 ++---------------- .../exam/service/impl/ExamServiceImpl.java | 11 ++ 2 files changed, 19 insertions(+), 93 deletions(-) diff --git a/backend/src/main/java/lsgwr/exam/controller/UserController.java b/backend/src/main/java/lsgwr/exam/controller/UserController.java index 679b26e..e594d70 100644 --- a/backend/src/main/java/lsgwr/exam/controller/UserController.java +++ b/backend/src/main/java/lsgwr/exam/controller/UserController.java @@ -1,95 +1,10 @@ -/*********************************************************** - * @Description : 对外REST接口 - * @author : 梁山广(Laing Shan Guang) - * @date : 2019-05-16 23:45 - * @email : liangshanguang2@gmail.com - ***********************************************************/ -package lsgwr.exam.controller; - -import lsgwr.exam.dto.RegisterDTO; -import lsgwr.exam.entity.User; -import lsgwr.exam.enums.ResultEnum; -import lsgwr.exam.qo.LoginQo; -import lsgwr.exam.service.UserService; -import lsgwr.exam.vo.ResultVO; -import lsgwr.exam.vo.UserInfoVo; -import lsgwr.exam.vo.UserVo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import javax.servlet.http.HttpServletRequest; - -@RestController -@Api(tags = "User APIs") -@RequestMapping("/api/user") -public class UserController { - - @Autowired - private UserService userService; - - @PostMapping("/register") - @ApiOperation("注册") - ResultVO register(@RequestBody RegisterDTO registerDTO) { - ResultVO resultVO; - // 注册信息的完善,还有唯一性校验没(用户名、邮箱和手机号)已经在user表中通过unique来设置了 - User user = userService.register(registerDTO); - if (user != null) { - // 注册成功 - resultVO = new ResultVO<>(ResultEnum.REGISTER_SUCCESS.getCode(), ResultEnum.REGISTER_SUCCESS.getMessage(), user); - } else { - resultVO = new ResultVO<>(ResultEnum.REGISTER_FAILED.getCode(), ResultEnum.REGISTER_FAILED.getMessage(), null); - } - return resultVO; - } - - @PostMapping("/login") - @ApiOperation("根据用户名或邮箱登录,登录成功返回token") - ResultVO login(@RequestBody LoginQo loginQo) { // 这里不用手机号是因为手机号和用户名难以进行格式区分,而用户名和 - // 用户登录 - ResultVO resultVO; - String token = userService.login(loginQo); - if (token != null) { - // 登录成功 - resultVO = new ResultVO<>(ResultEnum.LOGIN_SUCCESS.getCode(), ResultEnum.LOGIN_SUCCESS.getMessage(), token); - } else { - // 登录失败 - resultVO = new ResultVO<>(ResultEnum.LOGIN_FAILED.getCode(), ResultEnum.LOGIN_FAILED.getMessage(), null); - } - return resultVO; - } - - @GetMapping("/user-info") - @ApiOperation("获取用户信息") - ResultVO getUserInfo(HttpServletRequest request) { - String userId = (String) request.getAttribute("user_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); - } - - @GetMapping("/test") - @ApiOperation("测试接口") - String test(HttpServletRequest request) { - // 下面这两个属性都是登录拦截器从token中解析地,当用户名不对或者token过期时是走不到接口内的 - String userId = (String) request.getAttribute("user_id"); - String username = (String) request.getAttribute("username"); - System.out.println("用户id:" + userId); - System.out.println("用户名:" + username); - return "用户id:" + userId + "\n用户名:" + username; +// 使用Spring Boot框架 +@SpringBootApplication +public class ExamApplication { + + // 主方法 + public static void main(String[] args) { + // 运行Spring Boot应用程序 + SpringApplication.run(ExamApplication.class, args); } } \ 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..9dd9a7e 100644 --- a/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java +++ b/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java @@ -23,22 +23,31 @@ import java.util.*; @Transactional public class ExamServiceImpl implements ExamService { + // 定义一个ExamRepository类型的私有变量 private final ExamRepository examRepository; + // 定义一个ExamRecordRepository类型的私有变量 private final ExamRecordRepository examRecordRepository; + // 定义一个QuestionRepository类型的私有变量 private final QuestionRepository questionRepository; + // 定义一个UserRepository类型的私有变量 private final UserRepository userRepository; + // 定义一个QuestionLevelRepository类型的私有变量 private final QuestionLevelRepository questionLevelRepository; + // 定义一个QuestionTypeRepository类型的私有变量 private final QuestionTypeRepository questionTypeRepository; + // 定义一个QuestionCategoryRepository类型的私有变量 private final QuestionCategoryRepository questionCategoryRepository; + // 定义一个QuestionOptionRepository类型的私有变量 private final QuestionOptionRepository questionOptionRepository; + // 构造函数,传入多个Repository类型的参数 public ExamServiceImpl(QuestionRepository questionRepository, UserRepository userRepository, QuestionLevelRepository questionLevelRepository, QuestionTypeRepository questionTypeRepository, QuestionCategoryRepository questionCategoryRepository, QuestionOptionRepository questionOptionRepository, ExamRepository examRepository, ExamRecordRepository examRecordRepository) { this.questionRepository = questionRepository; this.userRepository = userRepository; @@ -67,7 +76,9 @@ public class ExamServiceImpl implements ExamService { return questionVoList; } + // 根据传入的Question对象,返回一个QuestionVo对象 private QuestionVo getQuestionVo(Question question) { + // 创建一个新的QuestionVo对象 QuestionVo questionVo = new QuestionVo(); // 先复制能复制的属性 BeanUtils.copyProperties(question, questionVo); -- 2.34.1 From 94e67199429b44a15b009fece8731cbec18d7beb Mon Sep 17 00:00:00 2001 From: hunqiu Date: Sat, 23 Nov 2024 22:54:00 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86view=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 5 + .idea/vcs.xml | 2 +- .../exam/service/impl/ExamServiceImpl.java | 698 +----------------- frontend/src/components/Menu/menu.js | 24 +- frontend/src/components/_util/util.js | 15 + .../views/account/settings/AvatarModal.vue | 24 +- .../views/account/settings/BaseSetting.vue | 26 +- .../src/views/account/settings/Custom.vue | 9 +- frontend/src/views/account/settings/Index.vue | 12 +- 9 files changed, 113 insertions(+), 702 deletions(-) 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/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/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java b/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java index 9dd9a7e..ab40ce9 100644 --- a/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java +++ b/backend/src/main/java/lsgwr/exam/service/impl/ExamServiceImpl.java @@ -1,685 +1,13 @@ -/*********************************************************** - * @Description : 考试服务接口实现 - * @author : 梁山广(Laing Shan Guang) - * @date : 2019-05-28 08:06 - * @email : liangshanguang2@gmail.com - ***********************************************************/ -package lsgwr.exam.service.impl; - -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.vo.*; -import org.springframework.beans.BeanUtils; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.*; - -@Service -@Transactional -public class ExamServiceImpl implements ExamService { - - // 定义一个ExamRepository类型的私有变量 - private final ExamRepository examRepository; - - // 定义一个ExamRecordRepository类型的私有变量 - private final ExamRecordRepository examRecordRepository; - - // 定义一个QuestionRepository类型的私有变量 - private final QuestionRepository questionRepository; - - // 定义一个UserRepository类型的私有变量 - private final UserRepository userRepository; - - // 定义一个QuestionLevelRepository类型的私有变量 - private final QuestionLevelRepository questionLevelRepository; - - // 定义一个QuestionTypeRepository类型的私有变量 - private final QuestionTypeRepository questionTypeRepository; - - // 定义一个QuestionCategoryRepository类型的私有变量 - private final QuestionCategoryRepository questionCategoryRepository; - - // 定义一个QuestionOptionRepository类型的私有变量 - private final QuestionOptionRepository questionOptionRepository; - - // 构造函数,传入多个Repository类型的参数 - 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.questionTypeRepository = questionTypeRepository; - this.questionCategoryRepository = questionCategoryRepository; - this.questionOptionRepository = questionOptionRepository; - this.examRepository = examRepository; - this.examRecordRepository = examRecordRepository; - } - - @Override - public List getQuestionAll() { - List questionList = questionRepository.findAll(); - return getQuestionVos(questionList); - } - - private List getQuestionVos(List questionList) { - // 需要自定义的question列表 - List questionVoList = new ArrayList<>(); - // 循环完成每个属性的定制 - for (Question question : questionList) { - QuestionVo questionVo = getQuestionVo(question); - questionVoList.add(questionVo); - } - return questionVoList; - } - - // 根据传入的Question对象,返回一个QuestionVo对象 - private QuestionVo getQuestionVo(Question question) { - // 创建一个新的QuestionVo对象 - QuestionVo questionVo = new QuestionVo(); - // 先复制能复制的属性 - BeanUtils.copyProperties(question, questionVo); - // 设置问题的创建者 - questionVo.setQuestionCreator( - Objects.requireNonNull( - userRepository.findById( - question.getQuestionCreatorId() - ).orElse(null) - ).getUserUsername()); - - // 设置问题的难度 - questionVo.setQuestionLevel( - Objects.requireNonNull( - questionLevelRepository.findById( - question.getQuestionLevelId() - ).orElse(null) - ).getQuestionLevelDescription()); - - // 设置题目的类别,比如单选、多选、判断等 - questionVo.setQuestionType( - Objects.requireNonNull( - questionTypeRepository.findById( - question.getQuestionTypeId() - ).orElse(null) - ).getQuestionTypeDescription()); - - // 设置题目分类,比如数学、语文、英语、生活、人文等 - questionVo.setQuestionCategory( - Objects.requireNonNull( - questionCategoryRepository.findById( - question.getQuestionCategoryId() - ).orElse(null) - ).getQuestionCategoryName() - ); - - // 选项的自定义Vo列表 - List optionVoList = new ArrayList<>(); - - // 获得所有的选项列表 - List optionList = questionOptionRepository.findAllById( - Arrays.asList(question.getQuestionOptionIds().split("-")) - ); - - // 获取所有的答案列表optionList中每个option的isAnswer选项 - List answerList = questionOptionRepository.findAllById( - Arrays.asList(question.getQuestionAnswerOptionIds().split("-")) - ); - - // 根据选项和答案的id相同设置optionVo的isAnswer属性 - for (QuestionOption option : optionList) { - QuestionOptionVo optionVo = new QuestionOptionVo(); - BeanUtils.copyProperties(option, optionVo); - for (QuestionOption answer : answerList) { - if (option.getQuestionOptionId().equals(answer.getQuestionOptionId())) { - optionVo.setAnswer(true); - } - } - optionVoList.add(optionVo); - } - - // 设置题目的所有选项 - questionVo.setQuestionOptionVoList(optionVoList); - return questionVo; - } - - @Override - public QuestionVo updateQuestion(QuestionVo questionVo) { - // 1.把需要的属性都设置好 - StringBuilder questionAnswerOptionIds = new StringBuilder(); - List questionOptionList = new ArrayList<>(); - List questionOptionVoList = questionVo.getQuestionOptionVoList(); - int size = questionOptionVoList.size(); - for (int i = 0; i < questionOptionVoList.size(); i++) { - QuestionOptionVo questionOptionVo = questionOptionVoList.get(i); - QuestionOption questionOption = new QuestionOption(); - BeanUtils.copyProperties(questionOptionVo, questionOption); - questionOptionList.add(questionOption); - if (questionOptionVo.getAnswer()) { - if (i != size - 1) { - // 把更新后的答案的id加上去,记得用-连到一起 - questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId()).append("-"); - } else { - // 最后一个不需要用-连接 - questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId()); - } - } - } - - // 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 - questionOptionRepository.saveAll(questionOptionList); - - // 返回更新后的问题,方便前端局部刷新 - return getQuestionVo(question); - } - - @Override - public void questionCreate(QuestionCreateVo questionCreateVo) { - // 问题创建 - Question question = new Question(); - // 把能复制的属性都复制过来 - BeanUtils.copyProperties(questionCreateVo, question); - // 设置下questionOptionIds和questionAnswerOptionIds,需要自己用Hutool生成下 - List questionOptionList = new ArrayList<>(); - List questionOptionCreateVoList = questionCreateVo.getQuestionOptionCreateVoList(); - for (QuestionOptionCreateVo questionOptionCreateVo : questionOptionCreateVoList) { - QuestionOption questionOption = new QuestionOption(); - // 设置选项的的内容 - questionOption.setQuestionOptionContent(questionOptionCreateVo.getQuestionOptionContent()); - // 设置选项的id - questionOption.setQuestionOptionId(IdUtil.simpleUUID()); - questionOptionList.add(questionOption); - } - // 把选项都存起来,然后才能用于下面设置Question的questionOptionIds和questionAnswerOptionIds - questionOptionRepository.saveAll(questionOptionList); - String questionOptionIds = ""; - String questionAnswerOptionIds = ""; - // 经过上面的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组成的字符串 - question.setQuestionOptionIds(questionOptionIds); - // 设置答案选项id组成的字符串 - question.setQuestionAnswerOptionIds(questionAnswerOptionIds); - // 自己生成问题的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; - } - - /** - * 去除字符串最后的,防止split的时候出错 - * - * @param str 原始字符串 - * @return - */ - public static String trimMiddleLine(String str) { - if (str.charAt(str.length() - 1) == '-') { - str = str.substring(0, str.length() - 1); - } - return str; - } - - @Override - public QuestionDetailVo getQuestionDetail(String id) { - Question question = questionRepository.findById(id).orElse(null); - QuestionDetailVo questionDetailVo = new QuestionDetailVo(); - questionDetailVo.setId(id); - questionDetailVo.setName(question.getQuestionName()); - questionDetailVo.setDescription(question.getQuestionDescription()); - // 问题类型,单选题/多选题/判断题 - questionDetailVo.setType( - Objects.requireNonNull( - questionTypeRepository.findById( - question.getQuestionTypeId() - ).orElse(null) - ).getQuestionTypeDescription() - ); - // 获取当前问题的选项 - String optionIdsStr = trimMiddleLine(question.getQuestionOptionIds()); - String[] optionIds = optionIdsStr.split("-"); - // 获取选项列表 - List optionList = questionOptionRepository.findAllById(Arrays.asList(optionIds)); - questionDetailVo.setOptions(optionList); - return questionDetailVo; - } - - @Override - public List getExamAll() { - List examList = examRepository.findAll(); - return getExamVos(examList); - } - - private List getExamVos(List examList) { - // 需要自定义的exam列表 - List examVoList = new ArrayList<>(); - // 循环完成每个属性的定制 - for (Exam exam : examList) { - ExamVo examVo = new ExamVo(); - // 先尽量复制能复制的所有属性 - BeanUtils.copyProperties(exam, examVo); - // 设置问题的创建者 - examVo.setExamCreator( - Objects.requireNonNull( - userRepository.findById( - exam.getExamCreatorId() - ).orElse(null) - ).getUserUsername() - ); - - // 获取所有单选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上 - List radioQuestionVoList = new ArrayList<>(); - List radioQuestionList = questionRepository.findAllById( - Arrays.asList(exam.getExamQuestionIdsRadio().split("-")) - ); - for (Question question : radioQuestionList) { - ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo(); - BeanUtils.copyProperties(question, radioQuestionVo); - radioQuestionVo.setChecked(true); // 考试中的问题肯定被选中的 - radioQuestionVoList.add(radioQuestionVo); - } - examVo.setExamQuestionSelectVoRadioList(radioQuestionVoList); - - // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上 - List checkQuestionVoList = new ArrayList<>(); - List checkQuestionList = questionRepository.findAllById( - Arrays.asList(exam.getExamQuestionIdsCheck().split("-")) - ); - for (Question question : checkQuestionList) { - ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo(); - BeanUtils.copyProperties(question, checkQuestionVo); - checkQuestionVo.setChecked(true); // 考试中的问题肯定被选中的 - checkQuestionVoList.add(checkQuestionVo); - } - examVo.setExamQuestionSelectVoCheckList(checkQuestionVoList); - - // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上 - List judgeQuestionVoList = new ArrayList<>(); - List judgeQuestionList = questionRepository.findAllById( - Arrays.asList(exam.getExamQuestionIdsJudge().split("-")) - ); - for (Question question : judgeQuestionList) { - ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo(); - BeanUtils.copyProperties(question, judgeQuestionVo); - judgeQuestionVo.setChecked(true); // 考试中的问题肯定被选中的 - judgeQuestionVoList.add(judgeQuestionVo); - } - examVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList); - - // 把examVo加到examVoList中 - examVoList.add(examVo); - } - return examVoList; - } - - - - @Override - public ExamQuestionTypeVo getExamQuestionType() { - ExamQuestionTypeVo examQuestionTypeVo = new ExamQuestionTypeVo(); - // 获取所有单选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上 - List radioQuestionVoList = new ArrayList<>(); - List radioQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.RADIO.getId()); - for (Question question : radioQuestionList) { - ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo(); - BeanUtils.copyProperties(question, radioQuestionVo); - radioQuestionVoList.add(radioQuestionVo); - } - examQuestionTypeVo.setExamQuestionSelectVoRadioList(radioQuestionVoList); - - // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上 - List checkQuestionVoList = new ArrayList<>(); - List checkQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.CHECK.getId()); - for (Question question : checkQuestionList) { - ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo(); - BeanUtils.copyProperties(question, checkQuestionVo); - checkQuestionVoList.add(checkQuestionVo); - } - examQuestionTypeVo.setExamQuestionSelectVoCheckList(checkQuestionVoList); - - // 获取所有多选题列表,并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上 - List judgeQuestionVoList = new ArrayList<>(); - List judgeQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.JUDGE.getId()); - for (Question question : judgeQuestionList) { - ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo(); - BeanUtils.copyProperties(question, judgeQuestionVo); - judgeQuestionVoList.add(judgeQuestionVo); - } - examQuestionTypeVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList); - return examQuestionTypeVo; - } - - @Override - public Exam create(ExamCreateVo examCreateVo, String userId) { - // 在线考试系统创建 - Exam exam = new Exam(); - BeanUtils.copyProperties(examCreateVo, exam); - exam.setExamId(IdUtil.simpleUUID()); - exam.setExamCreatorId(userId); - 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++; - } - } - judgeIdsStr = replaceLastSeparator(judgeIdsStr); - 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); - examRepository.save(exam); - return exam; - } - - @Override - public Exam update(ExamVo examVo, String userId) { - Exam exam = new Exam(); - BeanUtils.copyProperties(examVo, exam); - exam.setExamCreatorId(userId); // 考试的更新人为最新的创建人 - exam.setUpdateTime(new Date()); // 考试的更新日期要记录下 - - String radioIdsStr = ""; - String checkIdsStr = ""; - String judgeIdsStr = ""; - List radios = examVo.getExamQuestionSelectVoRadioList(); - List checks = examVo.getExamQuestionSelectVoCheckList(); - List judges = examVo.getExamQuestionSelectVoJudgeList(); - 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++; - } - } - judgeIdsStr = replaceLastSeparator(judgeIdsStr); - 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); - examRepository.save(exam); - return exam; - } - - @Override - public List getExamCardList() { - List examList = examRepository.findAll(); - List examCardVoList = new ArrayList<>(); - for (Exam exam : examList) { - ExamCardVo examCardVo = new ExamCardVo(); - BeanUtils.copyProperties(exam, examCardVo); - examCardVoList.add(examCardVo); - } - return examCardVoList; - } - - @Override - public ExamDetailVo getExamDetail(String id) { - Exam exam = examRepository.findById(id).orElse(null); - ExamDetailVo examDetailVo = new ExamDetailVo(); - examDetailVo.setExam(exam); - assert exam != null; - examDetailVo.setRadioIds(exam.getExamQuestionIdsRadio().split("-")); - examDetailVo.setCheckIds(exam.getExamQuestionIdsCheck().split("-")); - examDetailVo.setJudgeIds(exam.getExamQuestionIdsJudge().split("-")); - return examDetailVo; - } - - @Override - public ExamRecord judge(String userId, String examId, HashMap> answersMap) { - // 开始考试判分啦~~~ - // 1.首先获取考试对象和选项数组 - ExamDetailVo examDetailVo = getExamDetail(examId); - Exam exam = examDetailVo.getExam(); - // 2.然后获取该考试下所有的题目信息 - List questionIds = new ArrayList<>(); - // 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 每种题目的分数 - int radioScore = exam.getExamScoreRadio(); - int checkScore = exam.getExamScoreCheck(); - int judgeScore = exam.getExamScoreJudge(); - // 2.3 根据问题id的数组拿到所有的问题对象,供下面步骤用 - List questionList = questionRepository.findAllById(questionIds); - Map questionMap = new HashMap<>(); - for (Question question : questionList) { - questionMap.put(question.getQuestionId(), question); - } - // 3.根据正确答案和用户作答信息进行判分 - Set questionIdsAnswer = answersMap.keySet(); - // 存储当前考试每个题目的得分情况 - Map judgeMap = new HashMap<>(); - // 考生作答地每个题目的选项(题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔),用于查看考试详情 - // 例子:题目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里面的答案是否相等 - if (answerStr.equals(userStr)) { - // 说明题目作答正确,下面根据题型给分 - int score = 0; - if (radioIdList.contains(questionId)) { - score = radioScore; - } - if (checkIdList.contains(questionId)) { - score = checkScore; - } - if (judgeIdList.contains(questionId)) { - score = judgeScore; - } - // 累计本次考试得分 - totalScore += score; - // True代表题目答对 - answerOptionIdsSb.append(questionId + "@True_" + userStr + "$"); - judgeMap.put(questionId, score); - } else { - // 说明题目作答错误,直接判零分,False代表题目答错 - answerOptionIdsSb.append(questionId + "@False_" + userStr + "$"); - judgeMap.put(questionId, 0); - } - } - // 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()); - examRecord.setExamJoinScore(totalScore); - examRecordRepository.save(examRecord); - return examRecord; - } - - @Override - public List getExamRecordList(String userId) { - // 获取指定用户下的考试记录列表 - List examRecordList = examRecordRepository.findByExamJoinerIdOrderByExamJoinDateDesc(userId); - List examRecordVoList = new ArrayList<>(); - for (ExamRecord examRecord : examRecordList) { - ExamRecordVo examRecordVo = new ExamRecordVo(); - Exam exam = examRepository.findById(examRecord.getExamId()).orElse(null); - examRecordVo.setExam(exam); - 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 - resultsMap.put(questionTitleAndResult[0], questionTitleAndResult[1]); - } - recordDetailVo.setAnswersMap(answersMap); - recordDetailVo.setResultsMap(resultsMap); - // 下面再计算正确答案的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)); - } - recordDetailVo.setAnswersRightMap(answersRightMap); - return recordDetailVo; - } - - /** - * 把字符串最后一个字符-替换掉 - * - * @param str 原始字符串 - * @return 替换掉最后一个-的字符串 - */ - 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); - } - return str; - } - - /** - * 把字符串用-连接起来 - * - * @param strList 字符串列表 - * @return 拼接好的字符串,记住要去掉最后面的- - */ - private String listConcat(List strList) { - StringBuilder sb = new StringBuilder(); - for (String str : strList) { - sb.append(str); - sb.append("-"); - } - return replaceLastSeparator(sb.toString()); - } -} +public class ActionVo { + // 使用@JsonProperty注解将actionName属性映射到JSON中的action字段 + @JsonProperty("action") + private String actionName; + + // 使用@JsonProperty注解将actionDescription属性映射到JSON中的describe字段 + @JsonProperty("describe") + private String actionDescription; + + // 使用@JsonProperty注解将defaultCheck属性映射到JSON中的defaultCheck字段 + @JsonProperty("defaultCheck") + private Boolean defaultCheck; +} \ 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"> + + +
+