From 07f12faadcc46918a5b357e43c8e0aa866065792 Mon Sep 17 00:00:00 2001 From: zl <3216908512@qq.com> Date: Wed, 27 Nov 2024 19:19:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B4=94=E6=99=BA=E5=B0=A7=E5=88=86=E6=94=AF?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/yf/exam/ability/Constant.java | 13 +- .../yf/exam/ability/job/enums/JobGroup.java | 14 +- .../yf/exam/ability/job/enums/JobPrefix.java | 15 +- .../exam/ability/job/service/JobService.java | 51 +- .../job/service/impl/JobServiceImpl.java | 129 +-- .../ability/shiro/CNFilterFactoryBean.java | 38 +- .../com/yf/exam/ability/shiro/ShiroRealm.java | 246 +++--- .../yf/exam/ability/shiro/aop/JwtFilter.java | 113 +-- .../yf/exam/ability/shiro/jwt/JwtToken.java | 41 +- .../yf/exam/ability/shiro/jwt/JwtUtils.java | 90 +- .../ability/upload/config/UploadConfig.java | 36 +- .../upload/controller/UploadController.java | 71 +- .../exam/ability/upload/dto/UploadReqDTO.java | 30 +- .../ability/upload/dto/UploadRespDTO.java | 35 +- .../ability/upload/service/UploadService.java | 58 +- .../service/impl/UploadServiceImpl.java | 144 ++- .../exam/ability/upload/utils/FileUtils.java | 321 +++---- .../exam/ability/upload/utils/MediaUtils.java | 76 +- .../java/com/yf/exam/aspect/DictAspect.java | 215 ++--- .../exam/aspect/mybatis/QueryInterceptor.java | 177 +--- .../aspect/mybatis/UpdateInterceptor.java | 126 +-- .../com/yf/exam/aspect/utils/InjectUtils.java | 111 +-- .../java/com/yf/exam/config/CorsConfig.java | 58 +- .../com/yf/exam/config/MultipartConfig.java | 53 +- .../com/yf/exam/config/MybatisConfig.java | 55 +- .../com/yf/exam/config/ScheduledConfig.java | 141 +-- .../java/com/yf/exam/config/ShiroConfig.java | 242 +++-- .../com/yf/exam/config/SwaggerConfig.java | 155 +--- .../exam/controller/ExamController.java | 233 ++--- .../com/yf/exam/modules/exam/dto/ExamDTO.java | 140 +-- .../exam/modules/exam/dto/ExamDepartDTO.java | 55 +- .../yf/exam/modules/exam/dto/ExamRepoDTO.java | 93 +- .../modules/exam/dto/ext/ExamRepoExtDTO.java | 50 +- .../exam/dto/request/ExamSaveReqDTO.java | 54 +- .../exam/dto/response/ExamOnlineRespDTO.java | 35 +- .../exam/dto/response/ExamReviewRespDTO.java | 53 +- .../com/yf/exam/modules/exam/entity/Exam.java | 155 ++-- .../exam/modules/exam/entity/ExamDepart.java | 65 +- .../yf/exam/modules/exam/entity/ExamRepo.java | 105 +-- .../modules/exam/mapper/ExamDepartMapper.java | 28 +- .../exam/modules/exam/mapper/ExamMapper.java | 61 +- .../modules/exam/mapper/ExamRepoMapper.java | 39 +- .../exam/service/ExamDepartService.java | 42 +- .../modules/exam/service/ExamRepoService.java | 49 +- .../modules/exam/service/ExamService.java | 77 +- .../service/impl/ExamDepartServiceImpl.java | 88 +- .../service/impl/ExamRepoServiceImpl.java | 92 +- .../exam/service/impl/ExamServiceImpl.java | 209 +++-- .../paper/controller/PaperController.java | 127 +-- .../yf/exam/modules/paper/dto/PaperDTO.java | 106 +-- .../modules/paper/dto/PaperQuAnswerDTO.java | 64 +- .../yf/exam/modules/paper/dto/PaperQuDTO.java | 71 +- .../paper/dto/ext/PaperQuAnswerExtDTO.java | 25 +- .../paper/dto/ext/PaperQuDetailDTO.java | 29 +- .../paper/dto/request/PaperAnswerDTO.java | 23 +- .../paper/dto/request/PaperCreateReqDTO.java | 22 +- .../paper/dto/request/PaperListReqDTO.java | 39 +- .../paper/dto/request/PaperQuQueryDTO.java | 22 +- .../paper/dto/response/ExamDetailRespDTO.java | 50 +- .../paper/dto/response/ExamResultRespDTO.java | 25 +- .../paper/dto/response/PaperListRespDTO.java | 35 +- .../yf/exam/modules/paper/entity/Paper.java | 105 +-- .../yf/exam/modules/paper/entity/PaperQu.java | 60 +- .../modules/paper/entity/PaperQuAnswer.java | 53 +- .../exam/modules/paper/enums/ExamState.java | 17 +- .../exam/modules/paper/enums/PaperState.java | 17 +- .../exam/modules/paper/job/BreakExamJob.java | 60 +- .../modules/paper/mapper/PaperMapper.java | 53 +- .../paper/mapper/PaperQuAnswerMapper.java | 32 +- .../modules/paper/mapper/PaperQuMapper.java | 44 +- .../paper/service/PaperQuAnswerService.java | 36 +- .../modules/paper/service/PaperQuService.java | 54 +- .../modules/paper/service/PaperService.java | 73 +- .../impl/PaperQuAnswerServiceImpl.java | 96 +- .../service/impl/PaperQuServiceImpl.java | 142 ++- .../paper/service/impl/PaperServiceImpl.java | 832 ++++++++++-------- .../modules/qu/controller/QuController.java | 294 +++---- .../yf/exam/modules/qu/dto/QuAnswerDTO.java | 64 +- .../com/yf/exam/modules/qu/dto/QuDTO.java | 91 +- .../com/yf/exam/modules/qu/dto/QuRepoDTO.java | 66 +- .../modules/qu/dto/export/QuExportDTO.java | 76 +- .../modules/qu/dto/export/QuImportDTO.java | 55 +- .../exam/modules/qu/dto/ext/QuDetailDTO.java | 40 +- .../modules/qu/dto/request/QuQueryReqDTO.java | 60 +- .../qu/dto/request/QuRepoBatchReqDTO.java | 51 +- .../com/yf/exam/modules/qu/entity/Qu.java | 60 +- .../yf/exam/modules/qu/entity/QuAnswer.java | 46 +- .../com/yf/exam/modules/qu/entity/QuRepo.java | 51 +- .../com/yf/exam/modules/qu/enums/QuType.java | 9 +- .../modules/qu/mapper/QuAnswerMapper.java | 14 +- .../yf/exam/modules/qu/mapper/QuMapper.java | 58 +- .../exam/modules/qu/mapper/QuRepoMapper.java | 9 +- .../modules/qu/service/QuAnswerService.java | 26 +- .../modules/qu/service/QuRepoService.java | 61 +- .../yf/exam/modules/qu/service/QuService.java | 69 +- .../qu/service/impl/QuAnswerServiceImpl.java | 102 ++- .../qu/service/impl/QuRepoServiceImpl.java | 127 +-- .../qu/service/impl/QuServiceImpl.java | 140 +-- .../modules/qu/utils/ImageCheckUtils.java | 23 +- 99 files changed, 4090 insertions(+), 4391 deletions(-) diff --git a/src-源文件/main/java/com/yf/exam/ability/Constant.java b/src-源文件/main/java/com/yf/exam/ability/Constant.java index 50e754d..9880ea6 100644 --- a/src-源文件/main/java/com/yf/exam/ability/Constant.java +++ b/src-源文件/main/java/com/yf/exam/ability/Constant.java @@ -1,18 +1,15 @@ -// 定义包路径,用于存放系统基础功能相关的类 package com.yf.exam.ability; + /** - * 通用常量类 - * 用于定义系统中使用的常量值,提供全局可访问的常量。 + * 通用常量 * @author bool */ public class Constant { + /** - * 文件上传路径的前缀常量 - * 用于指定上传文件的基础路径,所有上传的文件URL都会以这个路径开头。 - * 例如: /upload/file/example.jpg - * 这个前缀用于构建和解析文件的URL,以便在系统中统一管理和访问上传的文件。 + * 文件上传路径 */ public static final String FILE_PREFIX = "/upload/file/"; -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/ability/job/enums/JobGroup.java b/src-源文件/main/java/com/yf/exam/ability/job/enums/JobGroup.java index 058ab16..2159361 100644 --- a/src-源文件/main/java/com/yf/exam/ability/job/enums/JobGroup.java +++ b/src-源文件/main/java/com/yf/exam/ability/job/enums/JobGroup.java @@ -1,19 +1,13 @@ -// 定义包路径,用于存放任务分组相关的枚举类 package com.yf.exam.ability.job.enums; /** - * 任务分组枚举接口 - * 定义系统中不同类型任务的分组标识 - * 这个接口用于集中管理任务分组的常量值,确保代码的一致性和可维护性。 - * + * 任务分组 * @author van */ public interface JobGroup { /** - * 系统任务的分组标识 - * 用于标识系统级别的定时任务,如系统维护、数据清理等。 - * 这个标识符用于在任务调度系统中区分系统级别的任务,便于管理和执行。 + * 系统任务 */ - String SYSTEM = "system"; // 系统任务组的标识符 -} \ No newline at end of file + String SYSTEM = "system"; +} diff --git a/src-源文件/main/java/com/yf/exam/ability/job/enums/JobPrefix.java b/src-源文件/main/java/com/yf/exam/ability/job/enums/JobPrefix.java index dd51986..2536f0e 100644 --- a/src-源文件/main/java/com/yf/exam/ability/job/enums/JobPrefix.java +++ b/src-源文件/main/java/com/yf/exam/ability/job/enums/JobPrefix.java @@ -1,19 +1,14 @@ -// 定义包路径,用于存放任务前缀相关的枚举类 package com.yf.exam.ability.job.enums; /** - * 任务前缀枚举接口 - * 定义系统中不同任务类型的前缀标识 - * 这个接口用于集中管理任务的前缀常量值,确保代码的一致性和可维护性。 - * + * 任务前缀 * @author bool */ public interface JobPrefix { /** - * 强制交卷任务的前缀标识 - * 用于标识与强制交卷相关的定时任务,方便在任务调度系统中识别和处理。 - * 例如:break_exam_12345 表示ID为12345的考试的强制交卷任务。 + * 强制交卷的 */ - String BREAK_EXAM = "break_exam_"; // 强制交卷任务的前缀标识符 -} \ No newline at end of file + String BREAK_EXAM = "break_exam_"; + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/job/service/JobService.java b/src-源文件/main/java/com/yf/exam/ability/job/service/JobService.java index 410c006..cb465de 100644 --- a/src-源文件/main/java/com/yf/exam/ability/job/service/JobService.java +++ b/src-源文件/main/java/com/yf/exam/ability/job/service/JobService.java @@ -1,66 +1,53 @@ -// 定义包路径,用于存放任务服务接口 package com.yf.exam.ability.job.service; /** - * 任务业务接口类,用于定义任务相关的业务操作 - * 这个接口定义了定时任务的基本操作,包括添加、暂停、恢复和删除任务。 - * + * 任务业务类,用于动态处理任务信息 * @author bool * @date 2020/11/29 下午2:17 */ public interface JobService { + /** - * 任务数据的键名常量 - * 用于在任务的JobDataMap中存储和检索任务相关数据的键名。 + * 任务数据 */ - String TASK_DATA = "taskData"; // 用于存储任务相关数据的键名 + String TASK_DATA = "taskData"; /** * 添加定时任务 - * 方法用于根据给定的任务类、名称、cron表达式和任务数据,添加一个新的定时任务。 - * - * @param jobClass 任务类,指定任务的执行类 - * @param jobName 任务名称,用于唯一标识任务 - * @param cron cron表达式,用于指定任务的执行计划 - * @param data 任务数据,传递给任务执行时的参数 + * @param jobClass + * @param jobName + * @param cron + * @param data */ void addCronJob(Class jobClass, String jobName, String cron, String data); /** * 添加立即执行的任务 - * 方法用于根据给定的任务类、名称和任务数据,添加一个新的立即执行的任务。 - * - * @param jobClass 任务类,指定任务的执行类 - * @param jobName 任务名称,用于唯一标识任务 - * @param data 任务数据,传递给任务执行时的参数 + * @param jobClass + * @param jobName + * @param data */ void addCronJob(Class jobClass, String jobName, String data); /** * 暂停任务 - * 方法用于根据任务名称和任务组,暂停一个正在运行的定时任务。 - * - * @param jobName 任务名称 - * @param jobGroup 任务组 + * @param jobName + * @param jobGroup */ void pauseJob(String jobName, String jobGroup); /** * 恢复任务 - * 方法用于根据触发器名称和触发器组,恢复一个已暂停的定时任务。 - * - * @param triggerName 触发器名称 - * @param triggerGroup 触发器组 + * @param triggerName + * @param triggerGroup */ void resumeJob(String triggerName, String triggerGroup); /** - * 删除任务 - * 方法用于根据任务名称和任务组,删除一个定时任务,包括任务本身和相关的触发器。 - * - * @param jobName 任务名称 - * @param jobGroup 任务组 + * 删除job + * @param jobName + * @param jobGroup */ void deleteJob(String jobName, String jobGroup); -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java b/src-源文件/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java index b34ea8e..aafdfdb 100644 --- a/src-源文件/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java @@ -1,186 +1,123 @@ -// 定义包路径,用于存放任务服务实现类 package com.yf.exam.ability.job.service.impl; -// 导入所需的外部依赖包 -import com.alibaba.fastjson.JSON; // 用于JSON数据处理 -import com.baomidou.mybatisplus.core.toolkit.IdWorker; // 用于生成唯一ID -import com.yf.exam.ability.job.enums.JobGroup; // 任务分组枚举 -import com.yf.exam.ability.job.service.JobService; // 任务服务接口 -import lombok.extern.log4j.Log4j2; // 日志注解 -import org.quartz.*; // Quartz定时任务框架相关类 -import org.springframework.beans.factory.annotation.Autowired; // Spring自动注入注解 -import org.springframework.scheduling.quartz.SchedulerFactoryBean; // Quartz调度器工厂Bean -import org.springframework.stereotype.Service; // Spring服务注解 -import org.springframework.util.StringUtils; // Spring字符串工具类 +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.yf.exam.ability.job.enums.JobGroup; +import com.yf.exam.ability.job.service.JobService; +import lombok.extern.log4j.Log4j2; +import org.quartz.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; /** - * 定时任务服务实现类 - * 用于管理系统中的定时任务,包括添加、暂停、恢复和删除任务。 * @author bool */ -@Log4j2 // 启用Log4j2日志 -@Service // 标记为Spring服务组件 +@Log4j2 +@Service public class JobServiceImpl implements JobService { /** - * Quartz定时任务调度器 - * 用于管理和执行所有的定时任务 + * Quartz定时任务核心的功能实现类 */ - private Scheduler scheduler; // 定时任务调度器实例 + private Scheduler scheduler; /** - * 构造函数,注入SchedulerFactoryBean - * 从Spring容器中注入Quartz调度器工厂Bean,并从中获取调度器实例。 - * @param schedulerFactoryBean Quartz调度器工厂Bean + * 注入 + * @param schedulerFactoryBean */ public JobServiceImpl(@Autowired SchedulerFactoryBean schedulerFactoryBean) { - // 从工厂Bean中获取调度器实例 scheduler = schedulerFactoryBean.getScheduler(); } - /** - * 添加定时任务 - * 方法用于添加一个新的定时任务,包括cron表达式和立即执行两种情况。 - * - * @param jobClass 任务类,指定任务的执行类 - * @param jobName 任务名称,用于唯一标识任务 - * @param cron cron表达式,用于指定任务的执行计划 - * @param data 任务数据,传递给任务执行时的参数 - */ + @Override public void addCronJob(Class jobClass, String jobName, String cron, String data) { - // 设置任务组为系统任务组 + + String jobGroup = JobGroup.SYSTEM; - // 如果任务名为空,则自动生成任务名 + // 自动命名 if(StringUtils.isEmpty(jobName)){ - // 使用类名大写+下划线+唯一ID作为任务名 - jobName = jobClass.getSimpleName().toUpperCase() + "_" + IdWorker.getIdStr(); + jobName = jobClass.getSimpleName().toUpperCase() + "_"+IdWorker.getIdStr(); } try { - // 创建任务键,用于唯一标识任务 JobKey jobKey = JobKey.jobKey(jobName, jobGroup); - // 获取任务详情 JobDetail jobDetail = scheduler.getJobDetail(jobKey); - - // 如果任务已存在,则删除旧任务 if (jobDetail != null) { log.info("++++++++++任务:{} 已存在", jobName); this.deleteJob(jobName, jobGroup); } - // 记录任务构建信息 log.info("++++++++++构建任务:{},{},{},{},{} ", jobClass.toString(), jobName, jobGroup, cron, data); - // 构建新的任务详情 + //构建job信息 jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build(); - // 设置任务数据 + //用JopDataMap来传递数据 jobDetail.getJobDataMap().put(TASK_DATA, data); - // 声明触发器 + //按新的cronExpression表达式构建一个新的trigger Trigger trigger = null; - // 如果有cron表达式,则创建cron触发器 + // 有表达式的按表达式 if(!StringUtils.isEmpty(cron)){ log.info("+++++表达式执行:"+ JSON.toJSONString(jobDetail)); - // 创建cron调度构建器 + //表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron); - // 构建触发器 - trigger = TriggerBuilder.newTrigger() - .withIdentity(jobName, jobGroup) - .withSchedule(scheduleBuilder) - .build(); + trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build(); }else{ - // 无cron表达式则立即执行一次 + // 无表达式则立即执行 log.info("+++++立即执行:"+ JSON.toJSONString(jobDetail)); - // 构建立即执行的触发器 - trigger = TriggerBuilder.newTrigger() - .withIdentity(jobName, jobGroup) - .startNow() - .build(); + trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startNow().build(); } - // 调度任务 scheduler.scheduleJob(jobDetail, trigger); } catch (Exception e) { - // 打印异常堆栈信息 e.printStackTrace(); } } - /** - * 添加立即执行的任务 - * 方法用于添加一个不需要cron表达式,立即执行一次的任务。 - * - * @param jobClass 任务类 - * @param jobName 任务名称 - * @param data 任务数据 - */ + @Override public void addCronJob(Class jobClass, String jobName, String data) { - // 立即执行任务,不需要cron表达式,传入null + // 立即执行任务 this.addCronJob(jobClass, jobName, null, data); } - /** - * 暂停任务 - * 方法用于暂停一个正在运行的定时任务。 - * - * @param jobName 任务名称 - * @param jobGroup 任务组 - */ + @Override public void pauseJob(String jobName, String jobGroup) { try { - // 创建触发器键并暂停触发器 TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); scheduler.pauseTrigger(triggerKey); log.info("++++++++++暂停任务:{}", jobName); } catch (SchedulerException e) { - // 打印异常堆栈信息 e.printStackTrace(); } } - /** - * 恢复任务 - * 方法用于恢复一个已暂停的定时任务。 - * - * @param jobName 任务名称 - * @param jobGroup 任务组 - */ @Override public void resumeJob(String jobName, String jobGroup) { try { - // 创建触发器键并恢复触发器 TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); scheduler.resumeTrigger(triggerKey); log.info("++++++++++重启任务:{}", jobName); } catch (SchedulerException e) { - // 打印异常堆栈信息 e.printStackTrace(); } } - /** - * 删除任务 - * 方法用于删除一个定时任务,包括任务本身和相关的触发器。 - * - * @param jobName 任务名称 - * @param jobGroup 任务组 - */ @Override public void deleteJob(String jobName, String jobGroup) { try { - // 创建任务键并删除任务 JobKey jobKey = JobKey.jobKey(jobName,jobGroup); scheduler.deleteJob(jobKey); log.info("++++++++++删除任务:{}", jobKey); } catch (SchedulerException e) { - // 打印异常堆栈信息 e.printStackTrace(); } } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java b/src-源文件/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java index 8d54a3e..3bc2190 100644 --- a/src-源文件/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java +++ b/src-源文件/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java @@ -1,39 +1,29 @@ -// 定义包路径,用于存放自定义过滤器相关的类 package com.yf.exam.ability.shiro; -import org.apache.shiro.spring.web.ShiroFilterFactoryBean; // Shiro过滤器工厂类,用于配置Shiro过滤器 -import org.apache.shiro.web.filter.InvalidRequestFilter; // Shiro无效请求过滤器,用于处理无效请求 -import org.apache.shiro.web.filter.mgt.DefaultFilter; // Shiro默认过滤器,提供默认的过滤逻辑 -import org.apache.shiro.web.filter.mgt.FilterChainManager; // Shiro过滤器链管理器,管理过滤器链的配置 +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.filter.InvalidRequestFilter; +import org.apache.shiro.web.filter.mgt.DefaultFilter; +import org.apache.shiro.web.filter.mgt.FilterChainManager; -import javax.servlet.Filter; // Servlet过滤器接口,定义了过滤器的基本操作 -import java.util.Map; // Map集合类,用于存储键值对 +import javax.servlet.Filter; +import java.util.Map; /** - * 自定义过滤器工厂类,用于创建和管理Shiro过滤器链 - * 主要解决中文URL问题,如下载文件中包含中文字符时可能会返回400错误。 - * 例如:https://youdomain.com/upload/file/云帆考试系统用户手册.pdf 这样的URL可能会因为中文字符而导致问题。 + * 自定义过滤器,用于处理中文URL问题 + * 如:下载文件中包含中文会返回400错误,https://youdomain.com/upload/file/云帆考试系统用户手册.pdf * @author van */ public class CNFilterFactoryBean extends ShiroFilterFactoryBean { - /** - * 创建过滤器链管理器 - * 覆盖父类的创建方法,添加自定义的过滤器配置。 - * @return FilterChainManager 过滤器链管理器实例 - */ @Override protected FilterChainManager createFilterChainManager() { - FilterChainManager manager = super.createFilterChainManager(); // 调用父类方法创建过滤器链管理器 - - // 获取过滤器映射,以便修改特定过滤器的配置 - Map filterMap = manager.getFilters(); - // 获取无效请求过滤器实例 - Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name()); + FilterChainManager manager = super.createFilterChainManager(); + // URL携带中文400,servletPath中文校验bug + Map filterMap = manager.getFilters(); + Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name()); if (invalidRequestFilter instanceof InvalidRequestFilter) { - // 设置无效请求过滤器不阻止非ASCII字符,以允许中文URL ((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false); } - return manager; // 返回配置好的过滤器链管理器 + return manager; } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/ability/shiro/ShiroRealm.java b/src-源文件/main/java/com/yf/exam/ability/shiro/ShiroRealm.java index d9b5844..76af5c5 100644 --- a/src-源文件/main/java/com/yf/exam/ability/shiro/ShiroRealm.java +++ b/src-源文件/main/java/com/yf/exam/ability/shiro/ShiroRealm.java @@ -1,135 +1,131 @@ -// 定义包路径,用于存放Shiro领域相关的类 package com.yf.exam.ability.shiro; -import com.yf.exam.ability.shiro.jwt.JwtToken; // JWT令牌类 -import com.yf.exam.ability.shiro.jwt.JwtUtils; // JWT工具类 -import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; // 用户登录DTO -import com.yf.exam.modules.sys.user.service.SysUserRoleService; // 用户角色服务 -import com.yf.exam.modules.sys.user.service.SysUserService; // 用户服务 -import lombok.extern.slf4j.Slf4j; // 日志注解 -import org.apache.shiro.authc.AuthenticationException; // 认证异常 -import org.apache.shiro.authc.AuthenticationInfo; // 认证信息 -import org.apache.shiro.authc.AuthenticationToken; // 认证令牌 -import org.apache.shiro.authc.SimpleAuthenticationInfo; // 简单认证信息 -import org.apache.shiro.authz.AuthorizationInfo; // 授权信息 -import org.apache.shiro.authz.SimpleAuthorizationInfo; // 简单授权信息 -import org.apache.shiro.realm.AuthorizingRealm; // 授权领域 -import org.apache.shiro.subject.PrincipalCollection; // 主体集合 -import org.springframework.beans.factory.annotation.Autowired; // Spring自动注入注解 -import org.springframework.context.annotation.Lazy; // 延迟注入注解 -import org.springframework.stereotype.Component; // Spring组件注解 - -import java.util.HashSet; // 哈希集合 -import java.util.List; // 列表 + +import com.yf.exam.ability.shiro.jwt.JwtToken; +import com.yf.exam.ability.shiro.jwt.JwtUtils; +import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; +import com.yf.exam.modules.sys.user.service.SysUserRoleService; +import com.yf.exam.modules.sys.user.service.SysUserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; /** - * 用户登录鉴权和获取用户授权的Shiro领域类 - * 负责用户的认证和授权,是Shiro框架中的核心组件之一。 + * 用户登录鉴权和获取用户授权 * @author bool */ -@Component // 标记为Spring组件 -@Slf4j // 启用Slf4j日志 +@Component +@Slf4j public class ShiroRealm extends AuthorizingRealm { - @Autowired - @Lazy // 延迟注入,避免循环依赖 - private SysUserService sysUserService; // 用户服务 - - @Autowired - @Lazy // 延迟注入,避免循环依赖 - private SysUserRoleService sysUserRoleService; // 用户角色服务 - - /** - * 判断是否支持JWT令牌 - * 确定当前领域是否支持处理JWT令牌类型的认证。 - * @param token 认证令牌 - * @return 是否支持JWT令牌 - */ - @Override - public boolean supports(AuthenticationToken token) { - // 判断是否支持JWT令牌 - return token instanceof JwtToken; // 返回是否为JwtToken - } - - /** - * 详细授权认证 - * 获取用户的授权信息,包括角色和权限。 - * @param principals 主体集合 - * @return 授权信息 - */ - @Override - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { - String userId = null; // 用户ID - if (principals != null) { - SysUserLoginDTO user = (SysUserLoginDTO) principals.getPrimaryPrincipal(); // 获取用户信息 - userId = user.getId(); // 获取用户ID - } - SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 创建授权信息 - - // 查找用户角色 - List roles = sysUserRoleService.listRoles(userId); // 获取用户角色列表 - info.setRoles(new HashSet<>(roles)); // 设置角色 - - log.info("++++++++++校验详细权限完成"); // 日志记录 - return info; // 返回授权信息 - } - - /** - * 校验用户的账号密码是否正确 - * 根据传入的认证令牌,验证用户的账号密码。 - * @param auth 认证令牌 - * @return 认证信息 - * @throws AuthenticationException 认证异常 - */ - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { - String token = (String) auth.getCredentials(); // 获取token - if (token == null) { - throw new AuthenticationException("token为空!"); // 抛出异常 - } - - // 校验token有效性 - SysUserLoginDTO user = this.checkToken(token); // 验证token并获取用户信息 - return new SimpleAuthenticationInfo(user, token, getName()); // 返回认证信息 - } - - /** - * 校验Token的有效性 - * 验证JWT令牌的有效性,并获取对应的用户信息。 - * @param token JWT令牌 - * @return 用户登录DTO - * @throws AuthenticationException 认证异常 - */ - public SysUserLoginDTO checkToken(String token) throws AuthenticationException { - // 查询用户信息 - log.debug("++++++++++校验用户token: "+ token); // 日志记录 - - // 从token中获取用户名 - String username = JwtUtils.getUsername(token); // 获取用户名 - log.debug("++++++++++用户名: "+ username); // 日志记录 - - if (username == null) { - throw new AuthenticationException("无效的token"); // 抛出异常 - } - - // 查找登录用户对象 - SysUserLoginDTO user = sysUserService.token(token); // 获取用户信息 - - // 校验token是否失效 - if (!JwtUtils.verify(token, username)) { - throw new AuthenticationException("登陆失效,请重试登陆!"); // 抛出异常 - } - - return user; // 返回用户信息 - } - - /** - * 清除当前用户的权限认证缓存 - * 用于在用户信息变更后,清除缓存,确保权限信息的更新。 - * @param principals 主体集合 - */ - @Override + @Autowired + @Lazy + private SysUserService sysUserService; + + @Autowired + @Lazy + private SysUserRoleService sysUserRoleService; + + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof JwtToken; + } + + + /** + * 详细授权认证 + * @param principals + * @return + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + + String userId = null; + if (principals != null) { + SysUserLoginDTO user = (SysUserLoginDTO) principals.getPrimaryPrincipal(); + userId = user.getId(); + } + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + + // 查找用户角色 + List roles = sysUserRoleService.listRoles(userId); + info.setRoles(new HashSet<>(roles)); + + log.info("++++++++++校验详细权限完成"); + return info; + } + + /** + * 校验用户的账号密码是否正确 + * @param auth + * @return + * @throws AuthenticationException + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { + String token = (String) auth.getCredentials(); + if (token == null) { + throw new AuthenticationException("token为空!"); + } + + // 校验token有效性 + SysUserLoginDTO user = this.checkToken(token); + return new SimpleAuthenticationInfo(user, token, getName()); + } + + + /** + * 校验Token的有效性 + * @param token + * @return + * @throws AuthenticationException + */ + public SysUserLoginDTO checkToken(String token) throws AuthenticationException { + + // 查询用户信息 + log.debug("++++++++++校验用户token: "+ token); + + // 从token中获取用户名 + String username = JwtUtils.getUsername(token); + log.debug("++++++++++用户名: "+ username); + + if (username == null) { + throw new AuthenticationException("无效的token"); + } + + // 查找登录用户对象 + SysUserLoginDTO user = sysUserService.token(token); + + // 校验token是否失效 + if (!JwtUtils.verify(token, username)) { + throw new AuthenticationException("登陆失效,请重试登陆!"); + } + + return user; + } + + + + /** + * 清除当前用户的权限认证缓存 + * @param principals + */ + @Override public void clearCache(PrincipalCollection principals) { - super.clearCache(principals); // 清除缓存 + super.clearCache(principals); } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java b/src-源文件/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java index 1fe9ff2..88cf448 100644 --- a/src-源文件/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java +++ b/src-源文件/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java @@ -1,84 +1,53 @@ -// 定义包路径,用于存放Shiro JWT认证过滤器相关的类 package com.yf.exam.ability.shiro.aop; -// 导入所需的外部依赖包 -import com.yf.exam.ability.shiro.jwt.JwtToken; // JWT令牌类 -import com.yf.exam.aspect.utils.InjectUtils; // 工具类,用于注入错误信息 -import com.yf.exam.modules.Constant; // 常量类 -import lombok.extern.slf4j.Slf4j; // 日志注解 -import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; // Shiro基础认证过滤器 -import javax.servlet.ServletRequest; // Servlet请求接口 -import javax.servlet.ServletResponse; // Servlet响应接口 -import javax.servlet.http.HttpServletRequest; // HTTP请求类 -import javax.servlet.http.HttpServletResponse; // HTTP响应类 +import com.yf.exam.ability.shiro.jwt.JwtToken; +import com.yf.exam.aspect.utils.InjectUtils; +import com.yf.exam.modules.Constant; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** - * JWT认证过滤器 - * 用于处理基于JWT的身份认证,确保只有持有有效JWT令牌的请求才能访问受保护的资源。 + * 鉴权登录拦截器 * @author bool */ -@Slf4j // 启用Slf4j日志 +@Slf4j public class JwtFilter extends BasicHttpAuthenticationFilter { - /** - * 判断是否允许访问 - * 所有的请求都会经过这个方法,用于判断是否需要登录认证。 - * 如果请求不需要认证(如访问公开资源),则直接返回true允许访问; - * 如果需要认证,则尝试执行登录认证,根据认证结果决定是否允许访问。 - * - * @param request 请求对象 - * @param response 响应对象 - * @param mappedValue 映射值 - * @return 是否允许访问 - */ - @Override - protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { - try { - // 尝试执行登录认证 - executeLogin(request, response); - // 认证成功返回true - return true; - } catch (Exception e) { - // 认证失败时写入错误信息 - InjectUtils.restError((HttpServletResponse) response); - // 返回false表示不允许访问 - return false; - } - } + /** + * 执行登录认证 + * @param request + * @param response + * @param mappedValue + * @return + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + try { + executeLogin(request, response); + return true; + } catch (Exception e) { + // 写出统一错误信息 + InjectUtils.restError((HttpServletResponse) response); + return false; + } + } - /** - * 执行登录认证 - * 从请求头中获取JWT token并进行认证,如果认证成功,则请求可以继续执行; - * 如果认证失败(如token无效或过期),则抛出异常,由isAccessAllowed方法处理。 - * - * @param request 请求对象 - * @param response 响应对象 - * @return 是否认证成功 - */ - @Override - protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { - // 将ServletRequest转换为HttpServletRequest - HttpServletRequest httpServletRequest = (HttpServletRequest) request; - // 从请求头中获取token - String token = httpServletRequest.getHeader(Constant.TOKEN); - // 如果token为空,则抛出异常 - if (token == null || "".equals(token)) { - throw new Exception("token不能为空"); - } + @Override + protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String token = httpServletRequest.getHeader(Constant.TOKEN); - // 创建JWT token对象 - JwtToken jwtToken = new JwtToken(token); - // 提交给realm进行登录认证 - try { - getSubject(request, response).login(jwtToken); - // 如果没有抛出异常则表示登录成功 - return true; - } catch (Exception e) { - // 登录失败,记录日志 - log.error("JWT认证失败", e); - throw e; - } - } -} \ No newline at end of file + JwtToken jwtToken = new JwtToken(token); + // 提交给realm进行登入,如果错误他会抛出异常并被捕获 + getSubject(request, response).login(jwtToken); + // 如果没有抛出异常则代表登入成功,返回true + return true; + } +} diff --git a/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java b/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java index 1e1fe4c..d5baab3 100644 --- a/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java +++ b/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java @@ -1,50 +1,33 @@ -// 定义包路径,用于存放JWT令牌相关的类 package com.yf.exam.ability.shiro.jwt; - -import lombok.Data; // Lombok注解,用于生成getter和setter -import org.apache.shiro.authc.AuthenticationToken; // Shiro认证令牌接口 + +import lombok.Data; +import org.apache.shiro.authc.AuthenticationToken; /** - * JWT令牌实现类 - * 实现Shiro的AuthenticationToken接口,用于JWT认证 * @author bool */ -@Data // 自动生成getter和setter方法 +@Data public class JwtToken implements AuthenticationToken { - private static final long serialVersionUID = 1L; // 序列化ID + private static final long serialVersionUID = 1L; /** - * JWT的字符串token - * 用于存储实际的JWT令牌字符串 + * JWT的字符token */ - private String token; // JWT令牌字符串 + private String token; + - /** - * 构造函数 - * @param token JWT令牌字符串 - */ public JwtToken(String token) { - this.token = token; // 设置token + this.token = token; } - /** - * 获取身份信息 - * 实现AuthenticationToken接口的方法 - * @return 返回token作为身份信息 - */ @Override public Object getPrincipal() { - return token; // 返回token作为身份信息 + return token; } - /** - * 获取凭证信息 - * 实现AuthenticationToken接口的方法 - * @return 返回token作为凭证信息 - */ @Override public Object getCredentials() { - return token; // 返回token作为凭证信息 + return token; } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java b/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java index ec2d907..4a66759 100644 --- a/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java +++ b/src-源文件/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java @@ -1,15 +1,14 @@ -// 定义包路径,用于存放JWT工具类 package com.yf.exam.ability.shiro.jwt; -import com.auth0.jwt.JWT; // JWT工具类 -import com.auth0.jwt.JWTVerifier; // JWT验证器 -import com.auth0.jwt.algorithms.Algorithm; // JWT算法 -import com.auth0.jwt.exceptions.JWTDecodeException; // JWT解码异常 -import com.auth0.jwt.interfaces.DecodedJWT; // 解码后的JWT -import com.yf.exam.core.utils.file.Md5Util; // MD5工具类 +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.yf.exam.core.utils.file.Md5Util; -import java.util.Calendar; // 日历类 -import java.util.Date; // 日期类 +import java.util.Calendar; +import java.util.Date; /** * JWT工具类 @@ -20,74 +19,81 @@ public class JwtUtils { /** * 有效期24小时 */ - private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000; // JWT有效期 + private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000; + /** * 校验是否正确 - * @param token JWT令牌 - * @param username 用户名 - * @return 是否有效 + * @param token + * @param username + * @return */ public static boolean verify(String token, String username) { try { // 根据密码生成JWT效验器 - Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username)); // 创建算法 - JWTVerifier verifier = JWT.require(algorithm) // 创建验证器 - .withClaim("username", username) // 添加用户名声明 - .build(); // 构建验证器 + Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username)); + JWTVerifier verifier = JWT.require(algorithm) + .withClaim("username", username) + .build(); // 效验TOKEN - verifier.verify(token); // 验证token - return true; // 返回验证成功 + verifier.verify(token); + return true; } catch (Exception exception) { - return false; // 返回验证失败 + return false; } } + + + + /** * 从Token中解密获得用户名 - * @param token JWT令牌 - * @return 用户名 + * @param token + * @return */ public static String getUsername(String token) { try { - DecodedJWT jwt = JWT.decode(token); // 解码JWT - return jwt.getClaim("username").asString(); // 获取用户名 + DecodedJWT jwt = JWT.decode(token); + return jwt.getClaim("username").asString(); } catch (JWTDecodeException e) { - return null; // 返回null表示解码失败 + return null; } } /** * 生成JWT Token字符串 - * @param username 用户名 - * @return JWT令牌字符串 + * @param username + * @return */ public static String sign(String username) { - Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); // 设置过期时间 - Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username)); // 创建算法 + Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); + Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username)); // 附带username信息 - return JWT.create() // 创建JWT - .withClaim("username", username) // 添加用户名声明 - .withExpiresAt(date).sign(algorithm); // 设置过期时间并签名 + return JWT.create() + .withClaim("username", username) + .withExpiresAt(date).sign(algorithm); + } /** * 根据用户名和秘钥,生成一个新的秘钥,用于JWT加强一些安全性 - * @param userName 用户名 - * @return 加密后的秘钥 + * @param userName + * @return */ private static String encryptSecret(String userName){ + // 一个简单的登录规则,用户名+当前月份为加密串,意思每个月会变,要重新登录 // 可自行修改此规则 - Calendar cl = Calendar.getInstance(); // 获取当前日历 - cl.setTimeInMillis(System.currentTimeMillis()); // 设置当前时间 - StringBuffer sb = new StringBuffer(userName) // 创建字符串缓冲区 - .append("&") // 添加分隔符 - .append(cl.get(Calendar.MONTH)); // 添加当前月份 + Calendar cl = Calendar.getInstance(); + cl.setTimeInMillis(System.currentTimeMillis()); + StringBuffer sb = new StringBuffer(userName) + .append("&") + .append(cl.get(Calendar.MONTH)); // 获取MD5 - String secret = Md5Util.md5(sb.toString()); // 生成MD5秘钥 + String secret = Md5Util.md5(sb.toString()); - return Md5Util.md5(userName + "&" + secret); // 返回加密后的秘钥 + return Md5Util.md5(userName + "&" + secret); } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/config/UploadConfig.java b/src-源文件/main/java/com/yf/exam/ability/upload/config/UploadConfig.java index a0cb14b..e35d73d 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/config/UploadConfig.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/config/UploadConfig.java @@ -1,36 +1,32 @@ -// 定义包路径,用于存放文件上传配置相关的类 package com.yf.exam.ability.upload.config; -import lombok.Data; // Lombok注解,用于简化数据类的编写,自动生成getter和setter -import org.springframework.boot.context.properties.ConfigurationProperties; // Spring Boot配置属性注解,用于将配置文件中的属性绑定到Java对象 -import org.springframework.context.annotation.Configuration; // Spring配置注解,标记为配置类 +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + /** - * 文件上传配置类 - * 用于定义文件上传的相关配置,如访问路径、物理目录和允许的文件后缀等。 - * 这些配置通常在application.yml或application.properties中定义,并由Spring Boot自动加载。 + * 文件上传配置 * @author van */ -@Data // 使用Lombok注解,自动生成getter和setter方法 -@Configuration // 标记为Spring配置类,表示这是一个配置类 -@ConfigurationProperties(prefix = "conf.upload") // 指定配置文件中属性的前缀,这里是"conf.upload" +@Data +@Configuration +@ConfigurationProperties(prefix = "conf.upload") public class UploadConfig { /** - * 文件访问路径 - * 定义文件上传后对外访问的基础URL路径。 + * 访问路径 */ - private String url; // 文件访问的URL + private String url; /** - * 文件存储物理目录 - * 定义文件上传后在服务器上的存储路径。 + * 物理目录 */ - private String dir; // 文件存储的物理目录 + private String dir; /** - * 允许的文件后缀 - * 定义允许上传的文件类型,通过文件后缀来限制。 + * 允许的后缀 */ - private String[] allowExtensions; // 允许上传的文件后缀 -} \ No newline at end of file + private String [] allowExtensions; + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/controller/UploadController.java b/src-源文件/main/java/com/yf/exam/ability/upload/controller/UploadController.java index 1849a4b..4c85250 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/controller/UploadController.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/controller/UploadController.java @@ -1,62 +1,57 @@ -// 定义包路径,用于存放文件上传下载请求类 package com.yf.exam.ability.upload.controller; -import com.yf.exam.ability.Constant; // 常量类,包含系统配置的常量值 -import com.yf.exam.ability.upload.dto.UploadReqDTO; // 文件上传请求DTO,封装上传文件所需的数据 -import com.yf.exam.ability.upload.dto.UploadRespDTO; // 文件上传响应DTO,封装上传文件后的响应数据 -import com.yf.exam.ability.upload.service.UploadService; // 文件上传服务,提供文件上传和下载的业务逻辑 -import com.yf.exam.core.api.ApiRest; // API响应类,封装统一的API响应格式 -import com.yf.exam.core.api.controller.BaseController; // 基础控制器,提供基础的控制器功能 -import io.swagger.annotations.Api; // Swagger API注解,用于描述API信息 -import io.swagger.annotations.ApiOperation; // Swagger API操作注解,用于描述单个API操作 -import lombok.extern.log4j.Log4j2; // 日志注解,提供日志功能 -import org.springframework.beans.factory.annotation.Autowired; // Spring自动注入注解,用于注入Spring管理的Bean -import org.springframework.web.bind.annotation.GetMapping; // GET请求映射注解,用于映射GET请求到方法 -import org.springframework.web.bind.annotation.ModelAttribute; // 模型属性注解,用于将请求参数绑定到模型对象 -import org.springframework.web.bind.annotation.PostMapping; // POST请求映射注解,用于映射POST请求到方法 -import org.springframework.web.bind.annotation.RestController; // REST控制器注解,标记为REST风格的控制器 -import javax.servlet.http.HttpServletRequest; // HTTP请求类,表示HTTP请求 -import javax.servlet.http.HttpServletResponse; // HTTP响应类,表示HTTP响应 +import com.yf.exam.ability.Constant; +import com.yf.exam.ability.upload.dto.UploadReqDTO; +import com.yf.exam.ability.upload.dto.UploadRespDTO; +import com.yf.exam.ability.upload.service.UploadService; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * 本地文件上传下载请求类 - * 负责处理文件上传和下载的请求,提供RESTful API接口。 * @author bool */ -@Log4j2 // 启用Log4j2日志 -@Api(tags = {"文件上传"}) // Swagger API标签,用于分类API -@RestController // 标记为REST控制器,表示该类是一个REST风格的控制器 +@Log4j2 +@Api(tags = {"文件上传"}) +@RestController public class UploadController extends BaseController { @Autowired - private UploadService uploadService; // 文件上传服务,自动注入 + private UploadService uploadService; /** * 文件上传 - * 处理文件上传请求,接收上传文件的数据,并返回上传结果。 - * - * @param reqDTO 上传请求DTO,包含上传文件所需的数据 - * @return 上传响应,封装上传文件后的响应数据 + * @param reqDTO + * @return */ - @PostMapping("/common/api/file/upload") // POST请求映射,指定请求路径 - @ApiOperation(value = "文件上传", notes = "此接口较为特殊,参数都通过表单方式提交,而非JSON") // Swagger API操作描述 + @PostMapping("/common/api/file/upload") + @ApiOperation(value = "文件上传", notes = "此接口较为特殊,参数都通过表单方式提交,而非JSON") public ApiRest upload(@ModelAttribute UploadReqDTO reqDTO) { // 上传并返回URL - UploadRespDTO respDTO = uploadService.upload(reqDTO); // 调用上传服务 - return super.success(respDTO); // 返回成功响应 + UploadRespDTO respDTO = uploadService.upload(reqDTO); + return super.success(respDTO); } /** * 独立文件下载 - * 处理文件下载请求,根据请求参数返回对应的文件。 - * - * @param request HTTP请求,包含下载请求的信息 - * @param response HTTP响应,用于返回文件内容 + * @param request + * @param response */ - @GetMapping(Constant.FILE_PREFIX+"**") // GET请求映射,指定请求路径前缀 - @ApiOperation(value = "文件下载", notes = "文件下载") // Swagger API操作描述 + @GetMapping(Constant.FILE_PREFIX+"**") + @ApiOperation(value = "文件下载", notes = "文件下载") public void download(HttpServletRequest request, HttpServletResponse response) { - uploadService.download(request, response); // 调用下载服务 + uploadService.download(request, response); } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java b/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java index 0e4a0f1..df2f286 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java @@ -1,26 +1,22 @@ -// 定义包路径,用于存放文件上传请求DTO package com.yf.exam.ability.upload.dto; -import com.yf.exam.core.api.dto.BaseDTO; // 导入基础DTO类,提供通用的数据传输对象功能 -import io.swagger.annotations.ApiModel; // 导入Swagger API模型注解,用于描述API模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger API模型属性注解,用于描述API模型的属性 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写,自动生成getter和setter -import org.springframework.web.multipart.MultipartFile; // 导入Spring文件上传类,用于处理上传的文件 + +import com.yf.exam.core.api.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; /** * 文件上传请求类 - * 用于封装文件上传请求中的数据,包括上传的文件内容。 - * @author van + * @author * @date 2019-12-26 17:54 */ -@Data // 使用Lombok注解,自动生成getter和setter方法 -@ApiModel(value="文件上传参数", description="文件上传参数") // 使用Swagger注解,描述API模型 +@Data +@ApiModel(value="文件上传参数", description="文件上传参数") public class UploadReqDTO extends BaseDTO { - /** - * 上传文件内容 - * 用于存储上传文件的数据,包括文件名、文件类型、文件大小等信息。 - */ - @ApiModelProperty(value = "上传文件内容", required=true) // 使用Swagger注解,描述API模型属性 - private MultipartFile file; // 上传的文件内容 -} \ No newline at end of file + @ApiModelProperty(value = "上传文件内容", required=true) + private MultipartFile file; + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java b/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java index cbce697..b91106e 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java @@ -1,28 +1,23 @@ -// 定义包路径,用于存放文件上传响应DTO package com.yf.exam.ability.upload.dto; -import com.yf.exam.core.api.dto.BaseDTO; // 导入基础DTO类,提供通用的数据传输对象功能 -import io.swagger.annotations.ApiModel; // 导入Swagger API模型注解,用于描述API模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger API模型属性注解,用于描述API模型的属性 -import lombok.AllArgsConstructor; // 导入Lombok注解,用于生成全参构造函数 -import lombok.Data; // 导入Lombok注解,用于生成getter和setter方法 -import lombok.NoArgsConstructor; // 导入Lombok注解,用于生成无参构造函数 +import com.yf.exam.core.api.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; /** - * 文件上传响应DTO - * 用于封装文件上传操作的结果,包括上传后的文件URL等信息。 + * 上传文件结果 * @author bool */ -@Data // 使用Lombok注解,自动生成getter和setter方法 -@AllArgsConstructor // 使用Lombok注解,生成全参构造函数 -@NoArgsConstructor // 使用Lombok注解,生成无参构造函数 -@ApiModel(value="文件上传响应", description="文件上传响应") // 使用Swagger注解,描述API模型 +@Data +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(value="文件上传响应", description="文件上传响应") public class UploadRespDTO extends BaseDTO { - /** - * 上传后的完整URL地址 - * 存储文件上传成功后,文件的完整访问URL地址。 - */ - @ApiModelProperty(value = "上传后的完整的URL地址", required=true) // 使用Swagger注解,描述API模型属性 - private String url; // 上传后的完整URL地址 -} \ No newline at end of file + @ApiModelProperty(value = "上传后的完整的URL地址", required=true) + private String url; + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/service/UploadService.java b/src-源文件/main/java/com/yf/exam/ability/upload/service/UploadService.java index 44617b6..ef516ec 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/service/UploadService.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/service/UploadService.java @@ -1,62 +1,30 @@ package com.yf.exam.ability.upload.service; -// 这一行声明了该Java类所属的包名为com.yf.exam.ability.upload.service。 -// 包用于组织和管理相关的Java类,避免类名冲突,方便代码的模块化和复用。 -import com.yf.exam.ability.upload.dto.UploadReqDTO; // 导入文件上传请求DTO -// 导入了名为UploadReqDTO的类,它位于com.yf.exam.ability.upload.dto包下。 -// 这个类通常用于封装文件上传请求相关的数据,比如要上传的文件信息、上传的相关参数等。 +import com.yf.exam.ability.upload.dto.UploadReqDTO; +import com.yf.exam.ability.upload.dto.UploadRespDTO; -import com.yf.exam.ability.upload.dto.UploadRespDTO; // 导入文件上传响应DTO -// 导入了名为UploadRespDTO的类,同样位于com.yf.exam.ability.upload.dto包下。 -// 它主要用于封装文件上传操作完成后返回的响应数据,例如上传是否成功的标识、上传后的文件存储路径等信息。 - -import javax.servlet.http.HttpServletRequest; // 导入HTTP请求类 -// 引入了Java EE中用于处理HTTP请求的标准类HttpServletRequest。 -// 在文件下载等操作中,会通过这个类获取客户端发送过来的关于下载请求的各种信息,如请求的URL、请求头信息等。 - -import javax.servlet.http.HttpServletResponse; // 导入HTTP响应类 -// 引入了Java EE中用于处理HTTP响应的标准类HttpServletResponse。 -// 在文件下载操作中,会使用这个类来设置响应的状态码、响应头信息以及将文件内容返回给客户端。 +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** - * 文件上传服务接口 - * 定义文件上传和下载的相关业务操作,提供统一的接口供外部调用。 - * @author bool + * 阿里云OSS业务类 + * @author bool * @date 2019-07-12 16:45 */ -// 这是一个Java接口的文档注释,用于描述该接口的整体功能和用途。 -// 说明这个接口主要是用来定义与文件上传和下载相关的业务操作方法, -// 并且其他类可以通过实现这个接口来提供具体的实现,以达到统一调用的目的。 -// 同时标注了接口的作者是bool,创建日期是2019年7月12日16:45。 - public interface UploadService { - // 这里定义了一个名为UploadService的公共接口。 - // 接口中只包含方法的声明,不包含方法的具体实现,具体实现由实现该接口的类来完成。 /** * 文件上传 - * 方法用于处理文件上传请求,接收上传文件的数据,并返回上传结果。 - * - * @param reqDTO 上传请求DTO,包含上传文件所需的数据 - * @return 上传响应DTO,封装上传文件后的响应数据 + * @param reqDTO + * @return */ - // 这是接口中定义的一个方法声明,名为upload。 - // 它的功能是处理文件上传请求,通过接收传入的UploadReqDTO对象(其中包含了上传文件所需的各种数据), - // 然后在具体实现类中执行实际的上传操作,最后返回一个UploadRespDTO对象,该对象封装了上传文件后的响应数据。 - UploadRespDTO upload(UploadReqDTO reqDTO); /** - * 文件下载 - * 方法用于处理文件下载请求,根据请求参数返回对应的文件。 - * - * @param request HTTP请求,包含下载请求的信息 - * @param response HTTP响应,用于返回文件内容 + * 下载文件 + * @param request + * @param response */ - // 这是接口中定义的另一个方法声明,名为download。 - // 它用于处理文件下载请求,会接收一个HttpServletRequest对象(其中包含了客户端发送的关于下载请求的所有信息) - // 和一个HttpServletResponse对象(用于设置响应相关的信息并将文件内容返回给客户端), - // 在具体实现类中根据请求参数找到对应的要下载的文件,并通过HttpServletResponse将文件内容返回给客户端。 - void download(HttpServletRequest request, HttpServletResponse response); -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java b/src-源文件/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java index 6d1a60e..ef70f40 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java @@ -1,143 +1,135 @@ -// 定义包路径,用于存放文件上传服务实现类 package com.yf.exam.ability.upload.service.impl; -import com.yf.exam.ability.Constant; // 导入常量类,包含系统配置的常量值 -import com.yf.exam.ability.upload.config.UploadConfig; // 导入文件上传配置类,包含文件上传的相关配置 -import com.yf.exam.ability.upload.dto.UploadReqDTO; // 导入文件上传请求DTO,封装上传文件所需的数据 -import com.yf.exam.ability.upload.dto.UploadRespDTO; // 导入文件上传响应DTO,封装上传文件后的响应数据 -import com.yf.exam.ability.upload.service.UploadService; // 导入文件上传服务接口,定义文件上传的相关业务操作 -import com.yf.exam.ability.upload.utils.FileUtils; // 导入文件工具类,提供文件操作的辅助功能 -import com.yf.exam.core.exception.ServiceException; // 导入服务异常类,处理业务逻辑中的异常情况 -import lombok.extern.log4j.Log4j2; // 导入日志注解,提供日志功能 -import org.apache.commons.io.FilenameUtils; // 导入文件名工具类,提供文件名和扩展名操作的辅助功能 -import org.springframework.beans.factory.annotation.Autowired; // 导入Spring自动注入注解,用于注入Spring管理的Bean -import org.springframework.stereotype.Service; // 导入Spring服务注解,标记为服务组件 -import org.springframework.util.FileCopyUtils; // 导入文件复制工具类,提供文件复制的功能 -import org.springframework.web.multipart.MultipartFile; // 导入Spring文件上传类,处理上传的文件 - -import javax.servlet.http.HttpServletRequest; // 导入HTTP请求类,表示HTTP请求 -import javax.servlet.http.HttpServletResponse; // 导入HTTP响应类,表示HTTP响应 -import java.io.FileOutputStream; // 导入文件输出流,用于将文件内容写入文件 -import java.io.IOException; // 导入IO异常类,处理IO操作中的异常情况 -import java.io.UnsupportedEncodingException; // 导入不支持的编码异常类,处理编码问题 -import java.net.URLDecoder; // 导入URL解码类,用于解码URL -import java.util.regex.Matcher; // 导入正则表达式匹配器,用于匹配正则表达式 -import java.util.regex.Pattern; // 导入正则表达式类,用于编译正则表达式 +import com.yf.exam.ability.Constant; +import com.yf.exam.ability.upload.config.UploadConfig; +import com.yf.exam.ability.upload.dto.UploadReqDTO; +import com.yf.exam.ability.upload.dto.UploadRespDTO; +import com.yf.exam.ability.upload.service.UploadService; +import com.yf.exam.ability.upload.utils.FileUtils; +import com.yf.exam.core.exception.ServiceException; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * 文件上传业务类 - * 实现文件上传服务接口,提供文件上传和下载的业务逻辑。 * @author bool * @date 2019-07-30 21:02 */ -@Log4j2 // 使用Log4j2日志注解,启用日志功能 -@Service // 使用Spring服务注解,标记为服务组件 +@Log4j2 +@Service public class UploadServiceImpl implements UploadService { @Autowired - private UploadConfig conf; // 自动注入文件上传配置 + private UploadConfig conf; - /** - * 文件上传 - * 实现文件上传业务逻辑,包括文件验证、文件保存和返回上传结果。 - * - * @param reqDTO 上传请求DTO,包含上传文件所需的数据 - * @return 上传响应DTO,封装上传文件后的响应数据 - */ @Override public UploadRespDTO upload(UploadReqDTO reqDTO) { + + // 文件内容 - MultipartFile file = reqDTO.getFile(); // 获取上传的文件 + MultipartFile file = reqDTO.getFile(); // 验证文件后缀 - boolean allow = FilenameUtils.isExtension(file.getOriginalFilename(), conf.getAllowExtensions()); // 验证文件后缀 + boolean allow = FilenameUtils.isExtension(file.getOriginalFilename(), conf.getAllowExtensions()); if(!allow){ - throw new ServiceException("文件类型不允许上传!"); // 抛出异常 + throw new ServiceException("文件类型不允许上传!"); } // 上传文件夹 - String fileDir = conf.getDir(); // 获取文件存储目录 + String fileDir = conf.getDir(); // 真实物理地址 String fullPath; try { + // 新文件 - String filePath = FileUtils.processPath(file); // 处理文件路径 + String filePath = FileUtils.processPath(file); // 文件保存地址 - fullPath = fileDir + filePath; // 拼接完整路径 + fullPath = fileDir + filePath; // 创建文件夹 - FileUtils.checkDir(fullPath); // 检查并创建文件夹 + FileUtils.checkDir(fullPath); // 上传文件 - FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(fullPath)); // 复制文件内容到指定路径 + FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(fullPath)); - return this.generateResult(filePath); // 返回上传结果 + return this.generateResult(filePath); } catch (IOException e) { - e.printStackTrace(); // 打印异常堆栈 - throw new ServiceException("文件上传失败:"+e.getMessage()); // 抛出异常 + e.printStackTrace(); + throw new ServiceException("文件上传失败:"+e.getMessage()); } } - /** - * 独立文件下载 - * 实现文件下载业务逻辑,包括获取文件真实路径和返回文件内容。 - * - * @param request HTTP请求,包含下载请求的信息 - * @param response HTTP响应,用于返回文件内容 - */ + + @Override public void download(HttpServletRequest request, HttpServletResponse response) { + // 获取真实的文件路径 - String filePath = this.getRealPath(request.getRequestURI()); // 获取文件的真实路径 + String filePath = this.getRealPath(request.getRequestURI()); // 处理中文问题 try { - filePath = URLDecoder.decode(filePath, "utf-8"); // 解码文件路径 + filePath = URLDecoder.decode(filePath, "utf-8"); } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // 抛出运行时异常 + throw new RuntimeException(e); } - System.out.println("++++完整路径为:"+filePath); // 打印完整路径 + System.out.println("++++完整路径为:"+filePath); try { - FileUtils.writeRange(request, response, filePath); // 调用文件工具类进行文件写入 + FileUtils.writeRange(request, response, filePath); } catch (IOException e) { - response.setStatus(404); // 设置响应状态为404 - log.error("预览文件失败" + e.getMessage()); // 打印错误日志 + response.setStatus(404); + log.error("预览文件失败" + e.getMessage()); } } + /** * 构造返回 - * 根据文件名构造上传响应DTO,包含上传后的完整URL地址。 - * - * @param fileName 文件名 - * @return 上传响应DTO + * @param fileName + * @return */ private UploadRespDTO generateResult(String fileName) { + //获取加速域名 - String domain = conf.getUrl(); // 获取文件访问的URL + String domain = conf.getUrl(); // 返回结果 - return new UploadRespDTO(domain + fileName); // 返回上传响应DTO + return new UploadRespDTO(domain + fileName); } + /** * 获取真实物理文件地址 - * 根据请求URI获取文件的真实物理路径。 - * - * @param uri 请求URI - * @return 真实文件路径 + * @param uri + * @return */ public String getRealPath(String uri){ - String regx = Constant.FILE_PREFIX+"(.*)"; // 正则表达式匹配文件路径 + + String regx = Constant.FILE_PREFIX+"(.*)"; // 查找全部变量 - Pattern pattern = Pattern.compile(regx); // 编译正则表达式 - Matcher m = pattern.matcher(uri); // 创建匹配器 + Pattern pattern = Pattern.compile(regx); + Matcher m = pattern.matcher(uri); if (m.find()) { - String str = m.group(1); // 获取匹配的文件路径 - return conf.getDir() + str; // 返回真实文件路径 + String str = m.group(1); + return conf.getDir() + str; } - return null; // 返回null表示未找到 + return null; } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/utils/FileUtils.java b/src-源文件/main/java/com/yf/exam/ability/upload/utils/FileUtils.java index 0c45c41..539ecb0 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/utils/FileUtils.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/utils/FileUtils.java @@ -1,169 +1,172 @@ -// 定义包路径,用于存放文件工具类 package com.yf.exam.ability.upload.utils; -import com.baomidou.mybatisplus.core.toolkit.IdWorker; // 导入ID生成工具,用于生成唯一的文件名 -import com.yf.exam.core.utils.DateUtils; // 导入日期工具类,用于处理日期相关的操作 -import org.apache.commons.io.FilenameUtils; // 导入文件名工具类,用于处理文件名和扩展名 -import org.springframework.web.multipart.MultipartFile; // 导入Spring文件上传类,用于处理上传的文件 +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.yf.exam.core.utils.DateUtils; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; -import javax.servlet.ServletOutputStream; // 导入Servlet输出流,用于写入HTTP响应 -import javax.servlet.http.HttpServletRequest; // 导入HTTP请求类,表示客户端的请求 -import javax.servlet.http.HttpServletResponse; // 导入HTTP响应类,表示服务器的响应 -import java.io.File; // 导入文件类,用于操作文件和目录 -import java.io.IOException; // 导入IO异常类,处理IO操作中的异常 -import java.io.RandomAccessFile; // 导入随机访问文件类,用于高效地读写文件 -import java.util.Date; // 导入日期类,用于处理日期和时间 +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Date; /** * 文件工具类 - * 提供文件操作的辅助功能,包括文件上传、下载、重命名和目录管理等。 * @author bool */ public class FileUtils { - /** - * 后缀分割符号 - * 用于分割文件名和扩展名。 - */ - private static final String SUFFIX_SPLIT = "."; // 文件后缀分隔符 - - /** - * 支持以断点的方式输出文件,提供文件在线预览和视频在线播放 - * 方法用于处理HTTP请求,以断点续传的方式输出文件内容,支持文件在线预览和视频在线播放。 - * - * @param request HTTP请求,包含客户端的请求信息 - * @param response HTTP响应,包含服务器的响应信息 - * @param filePath 文件路径,指定要输出的文件 - * @throws IOException IO异常,处理文件操作中的异常 - */ - public static void writeRange(HttpServletRequest request, HttpServletResponse response, String filePath) throws IOException { - // 读取文件 - File file = new File(filePath); // 创建文件对象 - - // 只读模式 - RandomAccessFile randomFile = new RandomAccessFile(file, "r"); // 创建随机访问文件对象 - long contentLength = randomFile.length(); // 获取文件长度 - String range = request.getHeader("Range"); // 获取请求头中的Range - int start = 0, end = 0; // 初始化起始和结束位置 - if (range != null && range.startsWith("bytes=")) { - String[] values = range.split("=")[1].split("-"); // 解析Range - start = Integer.parseInt(values[0]); // 获取起始位置 - if (values.length > 1) { - end = Integer.parseInt(values[1]); // 获取结束位置 - } - } - int requestSize; // 请求大小 - if (end != 0 && end > start) { - requestSize = end - start + 1; // 计算请求大小 - } else { - requestSize = Integer.MAX_VALUE; // 设置为最大值 - } - - byte[] buffer = new byte[128]; // 创建缓冲区 - response.setContentType(MediaUtils.getContentType(filePath)); // 设置响应内容类型 - response.setHeader("Accept-Ranges", "bytes"); // 设置支持的范围 - response.setHeader("ETag", file.getName()); // 设置文件名 - response.setHeader("Last-Modified", new Date().toString()); // 设置最后修改时间 - // 第一次请求只返回content length来让客户端请求多次实际数据 - if (range == null) { - response.setHeader("Content-length", contentLength + ""); // 设置内容长度 - } else { - // 以后的多次以断点续传的方式来返回视频数据 - response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 设置部分内容状态 - long requestStart = 0, requestEnd = 0; // 初始化请求起始和结束位置 - String[] ranges = range.split("="); // 解析Range - if (ranges.length > 1) { - String[] rangeData = ranges[1].split("-"); // 获取范围数据 - requestStart = Integer.parseInt(rangeData[0]); // 获取请求起始位置 - if (rangeData.length > 1) { - requestEnd = Integer.parseInt(rangeData[1]); // 获取请求结束位置 - } - } - long length; // 请求长度 - if (requestEnd > 0) { - length = requestEnd - requestStart + 1; // 计算请求长度 - response.setHeader("Content-length", "" + length); // 设置内容长度 - response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength); // 设置内容范围 - } else { - length = contentLength - requestStart; // 计算请求长度 - response.setHeader("Content-length", "" + length); // 设置内容长度 - response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1) + "/" + contentLength); // 设置内容范围 - } - } - ServletOutputStream out = response.getOutputStream(); // 获取输出流 - int needSize = requestSize; // 需要的大小 - randomFile.seek(start); // 移动到起始位置 - while (needSize > 0) { - int len = randomFile.read(buffer); // 读取文件内容 - if (needSize < buffer.length) { - out.write(buffer, 0, needSize); // 写入需要的大小 - } else { - out.write(buffer, 0, len); // 写入缓冲区内容 - if (len < buffer.length) { - break; // 如果读取的长度小于缓冲区长度,退出循环 - } - } - needSize -= buffer.length; // 减少需要的大小 - } - randomFile.close(); // 关闭随机访问文件 - out.close(); // 关闭输出流 - } - - /** - * 重命名文件 - * 方法用于生成新的文件名,避免文件名冲突。 - * - * @param fileName 文件名 - * @return 新文件名 - */ - public static String renameFile(String fileName) { - // 没有后缀名不处理 - if (!fileName.contains(SUFFIX_SPLIT)) { - return fileName; // 返回原文件名 - } - - // 文件后缀 - String extension = FilenameUtils.getExtension(fileName); // 获取文件后缀 - - // 以系统时间命名 - return IdWorker.getIdStr() + "." + extension; // 返回新文件名 - } - - /** - * 处理新的文件路径,为上传文件预设目录,如:2021/01/01/xxx.jpg,要注意的是,前面没有斜杠 - * 方法用于处理上传文件的路径,根据当前日期生成目录结构。 - * - * @param file 文件 - * @return 处理后的文件路径 - */ - public static String processPath(MultipartFile file) { - // 创建OSSClient实例。 - String fileName = file.getOriginalFilename(); // 获取原始文件名 - - // 需要重命名 - fileName = renameFile(fileName); // 重命名文件 - - // 获得上传的文件夹 - String dir = DateUtils.formatDate(new Date(), "yyyy/MM/dd/"); // 获取当前日期格式化后的目录 - - return new StringBuffer(dir).append(fileName).toString(); // 返回处理后的文件路径 - } - - /** - * 检查文件夹是否存在,不存在则创建 - * 方法用于检查指定的文件夹是否存在,如果不存在则创建。 - * - * @param fileName 文件名 - */ - public static void checkDir(String fileName) { - int index = fileName.lastIndexOf("/"); // 获取最后一个斜杠的位置 - if (index == -1) { - return; // 如果没有斜杠,返回 - } - - File file = new File(fileName.substring(0, index)); // 创建文件对象 - if (!file.exists()) { - file.mkdirs(); // 如果文件夹不存在,创建文件夹 - } - } -} \ No newline at end of file + /** + * 后缀分割符号 + */ + private static final String SUFFIX_SPLIT = "."; + + + /** + * 支持以断点的方式输出文件,提供文件在线预览和视频在线播放 + * @param request + * @param response + * @param filePath + * @throws IOException + */ + public static void writeRange(HttpServletRequest request, + HttpServletResponse response, String filePath) throws IOException { + + // 读取文件 + File file = new File(filePath); + + //只读模式 + RandomAccessFile randomFile = new RandomAccessFile(file, "r"); + long contentLength = randomFile.length(); + String range = request.getHeader("Range"); + int start = 0, end = 0; + if (range != null && range.startsWith("bytes=")) { + String[] values = range.split("=")[1].split("-"); + start = Integer.parseInt(values[0]); + if (values.length > 1) { + end = Integer.parseInt(values[1]); + } + } + int requestSize; + if (end != 0 && end > start) { + requestSize = end - start + 1; + } else { + requestSize = Integer.MAX_VALUE; + } + + byte[] buffer = new byte[128]; + response.setContentType(MediaUtils.getContentType(filePath)); + response.setHeader("Accept-Ranges", "bytes"); + response.setHeader("ETag", file.getName()); + response.setHeader("Last-Modified", new Date().toString()); + //第一次请求只返回content length来让客户端请求多次实际数据 + if (range == null) { + response.setHeader("Content-length", contentLength + ""); + } else { + //以后的多次以断点续传的方式来返回视频数据 + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); + long requestStart = 0, requestEnd = 0; + String[] ranges = range.split("="); + if (ranges.length > 1) { + String[] rangeData = ranges[1].split("-"); + requestStart = Integer.parseInt(rangeData[0]); + if (rangeData.length > 1) { + requestEnd = Integer.parseInt(rangeData[1]); + } + } + long length; + if (requestEnd > 0) { + length = requestEnd - requestStart + 1; + response.setHeader("Content-length", "" + length); + response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength); + } else { + length = contentLength - requestStart; + response.setHeader("Content-length", "" + length); + response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1) + "/" + contentLength); + } + } + ServletOutputStream out = response.getOutputStream(); + int needSize = requestSize; + randomFile.seek(start); + while (needSize > 0) { + int len = randomFile.read(buffer); + if (needSize < buffer.length) { + out.write(buffer, 0, needSize); + } else { + out.write(buffer, 0, len); + if (len < buffer.length) { + break; + } + } + needSize -= buffer.length; + } + randomFile.close(); + out.close(); + } + + + + + /** + * 重命名文件 + * @param fileName + * @return + */ + public static String renameFile(String fileName) { + + //没有后缀名不处理 + if (!fileName.contains(SUFFIX_SPLIT)) { + return fileName; + } + + //文件后缀 + String extension = FilenameUtils.getExtension(fileName); + + //以系统时间命名 + return IdWorker.getIdStr() + "."+ extension; + + } + + + /** + * 处理新的文件路径,为上传文件预设目录,如:2021/01/01/xxx.jpg,要注意的是,前面没有斜杠 + * @param file 文件 + * @return + */ + public static String processPath(MultipartFile file){ + + // 创建OSSClient实例。 + String fileName = file.getOriginalFilename(); + + // 需要重命名 + fileName = renameFile(fileName); + + //获得上传的文件夹 + String dir = DateUtils.formatDate(new Date(), "yyyy/MM/dd/"); + + return new StringBuffer(dir).append(fileName).toString(); + + } + + /** + * 检查文件夹是否存在,不存在则创建 + * @param fileName + * @return + */ + public static void checkDir(String fileName){ + int index = fileName.lastIndexOf("/"); + if(index == -1){ + return; + } + + File file = new File(fileName.substring(0,index)); + if(!file.exists()){ + file.mkdirs(); + } + } + + +} diff --git a/src-源文件/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java b/src-源文件/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java index aff5557..b4394c0 100644 --- a/src-源文件/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java +++ b/src-源文件/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java @@ -1,75 +1,47 @@ package com.yf.exam.ability.upload.utils; -// 这一行声明了该Java类所属的包名为com.yf.exam.ability.upload.utils。 -// 包用于对相关的Java类进行组织和管理,方便代码的分类、复用以及避免类名冲突。 -import org.apache.commons.lang3.StringUtils; // 导入Apache Commons Lang的字符串工具类,用于字符串操作 -// 引入了Apache Commons Lang库中的StringUtils类。 -// 这个类提供了许多方便的字符串操作方法,比如判断字符串是否为空、是否空白(包含空格等空白字符)、字符串的拼接、截取等操作,在这里主要用于对文件路径字符串进行相关判断。 +import org.apache.commons.lang3.StringUtils; -import java.util.HashMap; // 导入Java的HashMap类,用于创建映射 -// 导入了Java标准库中的HashMap类。 -// HashMap是实现了Map接口的一个具体类,它用于存储键值对形式的数据,通过键可以快速获取对应的值,在这里用于创建文件后缀名到MIME类型的映射关系。 - -import java.util.Map; // 导入Java的Map接口,用于键值对映射 -// 引入了Java标准库中的Map接口。 -// Map接口定义了键值对数据结构的通用操作规范,如添加键值对、根据键获取值、删除键值对等操作。 -// 虽然这里同时导入了HashMap类,但导入Map接口使得代码在使用映射数据结构时更具通用性,方便后续可能的替换为其他实现Map接口的类。 +import java.util.HashMap; +import java.util.Map; /** - * 媒体工具类,用于判断和获取媒体文件的MIME类型 - * 该类提供了一个静态映射,用于将文件后缀名映射到对应的MIME类型,以便在处理文件上传和下载时确定正确的媒体类型。 - * @author bool + * 媒体工具,判断媒体类型 + * @author bool * @date 2019-11-14 16:21 */ -// 这是一个Java类的文档注释,用于描述该类的整体功能和用途。 -// 说明这个类主要是作为媒体工具类,其核心功能是判断和获取媒体文件的MIME类型。 -// 通过维护一个静态的映射关系(文件后缀名到MIME类型的映射),在文件上传和下载的业务场景中,能够依据文件的后缀名准确地确定其对应的MIME类型,从而正确处理文件的传输和展示等操作。 -// 同时标注了类的作者是bool,创建日期是2019年11月14日16:21。 - public class MediaUtils { - /** - * 媒体类型映射 - * 静态映射,包含文件后缀名到MIME类型的映射。 - */ - // 这是对下面定义的MEDIA_MAP成员变量的文档注释,说明它是一个静态的映射,用于存储文件后缀名和对应的MIME类型之间的映射关系。 + public static final Map MEDIA_MAP = new HashMap(){ + { - public static final Map MEDIA_MAP = new HashMap() {{ - // 初始化映射 - // PDF文件的MIME类型 - put(".pdf", "application/pdf"); - // 视频文件的MIME类型 - put(".mp4", "video/mp4"); - }}; - // 这里定义了一个名为MEDIA_MAP的公共静态最终变量,它是一个HashMap类型的映射。 - // 通过匿名内部类的初始化方式,在创建HashMap实例的同时向其中添加了一些常见的文件后缀名到MIME类型的映射关系,比如将".pdf"后缀名映射到"application/pdf"这个MIME类型,将".mp4"后缀名映射到"video/mp4"这个MIME类型。 - // 由于被声明为静态最终变量,它在类加载时就会被初始化,并且其值不能再被修改,方便在整个类的其他地方直接使用这个映射关系来获取文件的MIME类型。 + //本来是pdf的 + put(".pdf", "application/pdf"); + + //视频 + put(".mp4", "video,video/mp4"); + + } + }; /** - * 根据文件路径获取文件的MIME类型 - * 方法根据文件的后缀名,从MEDIA_MAP中获取对应的MIME类型。 - * - * @param filePath 文件路径 - * @return 文件的MIME类型 + * 获得文件类型 + * @param filePath + * @return */ - // 这是对下面定义的getContentType方法的文档注释,说明该方法的功能是根据传入的文件路径,提取出文件的后缀名,然后从MEDIA_MAP这个静态映射中获取对应的MIME类型并返回。 + public static String getContentType(String filePath){ + + if(!StringUtils.isBlank(filePath) + && filePath.indexOf(".")!=-1) { - public static String getContentType(String filePath) { - if (!StringUtils.isBlank(filePath) && filePath.indexOf(".")!= -1) { - // 提取文件后缀名,并转换为小写 + // 后缀转换成小写 String suffix = filePath.substring(filePath.lastIndexOf(".")).toLowerCase(); - // 从映射中获取MIME类型 if (MEDIA_MAP.containsKey(suffix)) { return MEDIA_MAP.get(suffix); } } - // 如果没有找到对应的MIME类型,返回默认值 return "application/octet-stream"; } - // 这是定义的一个公共静态方法getContentType,它接受一个字符串类型的参数filePath,表示文件的路径。 - // 首先,通过StringUtils.isBlank方法判断文件路径是否不为空且包含小数点(即有文件后缀名)。 - // 如果满足条件,就使用substring方法从文件路径中提取出文件的后缀名,并通过toLowerCase方法将其转换为小写形式。 - // 然后,检查提取出的后缀名是否存在于MEDIA_MAP这个静态映射中,如果存在,就返回对应的MIME类型;如果不存在,就返回默认的MIME类型"application/octet-stream",这个默认值通常用于表示未知类型的二进制数据文件。 -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/aspect/DictAspect.java b/src-源文件/main/java/com/yf/exam/aspect/DictAspect.java index a85ec9a..441a154 100644 --- a/src-源文件/main/java/com/yf/exam/aspect/DictAspect.java +++ b/src-源文件/main/java/com/yf/exam/aspect/DictAspect.java @@ -1,189 +1,156 @@ package com.yf.exam.aspect; -// 导入FastJSON库,用于将Java对象转换为JSON字符串以及从JSON字符串解析为Java对象等操作, -// 在本类的多个方法中用于对象与JSON字符串之间的转换,以便于处理数据字典相关的值。 import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; - -// 导入MyBatis Plus的接口,用于处理分页相关的数据结构,在本类中用于判断和处理分页数据中的数据字典值。 import com.baomidou.mybatisplus.core.metadata.IPage; - -// 导入Jackson库的注解,用于指定日期格式的序列化和反序列化方式, -// 在本类的parseObject方法中用于根据注解设置日期字段的格式化输出。 import com.fasterxml.jackson.annotation.JsonFormat; - -// 导入自定义的注解,可能用于标记与数据字典相关的字段,以便在本类中识别并处理这些字段的数据字典值。 import com.yf.exam.core.annon.Dict; - -// 导入自定义的API响应类,用于封装API调用的返回结果,包括数据、状态码等信息, -// 在本类的多个方法中用于获取和设置返回结果中的数据部分,以便处理其中的数据字典值。 import com.yf.exam.core.api.ApiRest; - -// 导入自定义的反射工具类,可能用于获取对象的所有字段等反射相关操作, -// 在本类的parseObject方法中用于获取对象的所有字段以便遍历处理数据字典值。 import com.yf.exam.core.utils.Reflections; - -// 导入系统数据字典服务类,用于查询数据字典表以获取数据字典值的翻译文本, -// 在本类的translateDictValue方法中用于根据字典代码、文本、表名和键值查询对应的字典文本。 import com.yf.exam.modules.sys.system.service.SysDictService; - -// 导入Lombok的Log4j2注解,用于简化日志记录的配置,通过该注解可以方便地在类中使用Log4j2进行日志输出。 -import lombok.extern.log4j.Log4j2; - -// 导入AspectJ的相关类,用于定义切面、切点和环绕通知等AOP相关的操作, -// 在本类中用于实现对特定方法的拦截和处理,以实现数据字典值的翻译等功能。 +import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; - -// 导入Spring框架的注解,用于自动注入依赖对象和标记类为Spring组件, -// 在本类中通过@Autowired注入SysDictService对象,并通过@Component标记本类为Spring组件,使其可被Spring容器管理。 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; - -// 导入Spring框架的工具类,用于判断字符串是否为空等操作, -// 在本类的多个方法中用于判断字符串是否为空,以便进行相应的处理逻辑。 import org.springframework.util.StringUtils; -// 导入Java标准库中的反射相关类,用于通过反射操作对象的字段、获取类型信息等, -// 在本类的多个方法中广泛用于获取对象的字段、判断字段类型、获取字段注解等操作。 import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; - -// 导入Java标准库中的日期格式化类和日期类,用于处理日期格式的转换和操作, -// 在本类的parseObject方法中用于根据注解或默认格式对日期字段进行格式化输出。 import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** - * 数据字典AOP类,主要功能是处理数据字典值。通过拦截特定的方法调用, - * 对返回结果中的数据字典相关字段进行翻译、格式化等处理,以提供更友好的展示效果。 + * 数据字典AOP类,处理数据字典值 * * @author bool */ -@Aspect // 标记该类为一个AspectJ切面类,用于定义切面相关的逻辑。 -@Component // 标记该类为Spring组件,使其能够被Spring容器管理和实例化,以便在应用中使用。 -@Log4j2 // 使用Log4j2注解启用日志记录功能,方便在类中记录相关操作的日志信息。 +@Aspect +@Component +@Slf4j public class DictAspect { @Autowired - private SysDictService sysDictService; // 通过自动注入获取系统数据字典服务对象,用于查询数据字典值。 + private SysDictService sysDictService; /** - * 定义一个环绕通知,切入到指定的Controller方法执行前后。 - * 这里的切点表达式指定了拦截所有在com.yf.exam包及其子包下的所有Controller类中的所有公有方法。 - * - * @param pjp 切入点对象,包含了被拦截方法的相关信息,如方法参数、目标对象等。 - * @return 返回结果,经过处理后的方法执行结果,可能经过了数据字典值的处理等操作。 - * @throws Throwable 如果在环绕通知的执行过程中出现异常,则抛出。 + * 切入Controller执行 + * @param pjp + * @return + * @throws Throwable */ @Around("execution(public * com.yf.exam..*.*Controller.*(..))") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { - return this.translate(pjp); // 调用translate方法对被拦截方法的执行结果进行处理,主要是处理数据字典值。 + return this.translate(pjp); } /** - * 对被拦截方法的执行结果进行翻译处理,并返回处理后的结果。 - * 在调用此方法之前,通常需要确保相关的BaseDictService(可能是数据字典相关的基础服务)已经实现。 + * 进行翻译并返回,调用前必须实现:BaseDictService * - * @param pjp 切入点对象,包含了被拦截方法的相关信息,如方法参数、目标对象等。 - * @return 返回结果,经过数据字典值处理后的方法执行结果。 - * @throws Throwable 如果在处理过程中出现异常,则抛出。 + * @param pjp + * @return + * @throws Throwable */ public Object translate(ProceedingJoinPoint pjp) throws Throwable { - // 调用被拦截方法获取原始结果,然后调用parseAllDictText方法对结果进行数据字典值的处理。 + // 处理字典 return this.parseAllDictText(pjp.proceed()); } /** - * 根据结果对象的类型,判断是否需要对其进行数据字典值的处理。 - * 如果结果对象是ApiRest类型,则调用parseFullDictText方法进行完整的数据字典值处理。 + * 转换全部数据字典 * - * @param result 结果对象,即被拦截方法执行后的返回结果。 + * @param result */ private Object parseAllDictText(Object result) { - // 判断结果对象是否是ApiRest类型,如果是则进行数据字典值的处理。 + + // 非ApiRest类型不处理 if (result instanceof ApiRest) { parseFullDictText(result); } + return result; } + /** - * 对ApiRest类型的结果对象进行完整的数据字典值处理,包括处理分页数据、列表数据以及单对象数据等情况。 + * 转换所有类型的数据字典、包含子列表 * - * @param result 结果对象,即ApiRest类型的返回结果,其中包含了要处理的数据部分。 + * @param result */ private void parseFullDictText(Object result) { + try { - Object rest = ((ApiRest) result).getData(); // 获取ApiRest对象中的数据部分,这部分数据可能包含数据字典相关字段。 - // 如果数据部分为空或者是基本数据类型,则不需要进行数据字典值的处理,直接返回。 + Object rest = ((ApiRest) result).getData(); + + // 不处理普通数据类型 if (rest == null || this.isBaseType(rest.getClass())) { return; } - // 如果数据部分是分页数据类型(IPage),则对分页数据中的每条记录进行数据字典值处理。 + // 分页的 if (rest instanceof IPage) { List items = new ArrayList<>(16); for (Object record : ((IPage) rest).getRecords()) { - Object item = this.parseObject(record); // 调用parseObject方法对每条记录进行数据字典值处理。 + Object item = this.parseObject(record); items.add(item); } - ((IPage) rest).setRecords(items); // 将处理后的记录列表重新设置回分页对象中。 + ((IPage) rest).setRecords(items); return; } - // 如果数据部分是列表数据类型(List),则对列表中的每条记录进行数据字典值处理。 + // 数据列表的 if (rest instanceof List) { List items = new ArrayList<>(); for (Object record : ((List) rest)) { - Object item = this.parseObject(record); // 调用parseObject方法对每条记录进行数据字典值处理。 + Object item = this.parseObject(record); items.add(item); } - // 将处理后的记录列表重新设置回ApiRest对象的数据部分。 + // 重新回写值 ((ApiRest) result).setData(items); return; } - // 如果数据部分是单对象数据,则对该单对象进行数据字典值处理。 + // 处理单对象 Object item = this.parseObject(((ApiRest) result).getData()); ((ApiRest) result).setData(item); } catch (Exception e) { - e.printStackTrace(); // 如果在处理过程中出现异常,则打印异常堆栈信息。 + e.printStackTrace(); } } /** - * 对单个记录对象进行数据字典值处理,包括处理列表字段、带有数据字典注解的普通字段以及日期字段等情况。 + * 处理数据字典值 * - * @param record 记录对象,即要进行数据字典值处理的对象。 - * @return 处理后的对象,经过数据字典值处理后的记录对象。 + * @param record + * @return */ public Object parseObject(Object record) { + if (record == null) { return null; } - // 如果记录对象是基本数据类型,则不需要进行数据字典值处理,直接返回原对象。 + // 不处理普通数据类型 if (this.isBaseType(record.getClass())) { return record; } - // 将记录对象转换为JSON字符串,再解析为JSONObject对象,以便于通过字段名获取和设置值。 + // 转换JSON字符 String json = JSON.toJSONString(record); JSONObject item = JSONObject.parseObject(json); - for (Field field : Reflections.getAllFields(record)) { // 遍历记录对象的所有字段。 + for (Field field : Reflections.getAllFields(record)) { - // 如果字段类型是List类型,则对列表字段进行特殊处理。 + // 如果是List类型 if (List.class.isAssignableFrom(field.getType())) { try { - List list = this.processList(field, item.getObject(field.getName(), List.class)); // 调用processList方法处理列表字段。 + List list = this.processList(field, item.getObject(field.getName(), List.class)); item.put(field.getName(), list); continue; } catch (Exception e) { @@ -192,14 +159,14 @@ public class DictAspect { continue; } - // 如果字段带有数据字典注解(Dict),则对该字段进行数据字典值的翻译处理。 - if (field.getAnnotation(Dict.class)!= null) { + // 处理普通字段 + if (field.getAnnotation(Dict.class) != null) { String code = field.getAnnotation(Dict.class).dicCode(); String text = field.getAnnotation(Dict.class).dicText(); String table = field.getAnnotation(Dict.class).dictTable(); String key = String.valueOf(item.get(field.getName())); - // 调用translateDictValue方法翻译字典值对应的文本,根据字典代码、文本、表名和键值查询对应的字典文本。 + //翻译字典值对应的txt String textValue = this.translateDictValue(code, text, table, key); if (StringUtils.isEmpty(textValue)) { textValue = ""; @@ -208,22 +175,24 @@ public class DictAspect { continue; } - // 如果字段类型是日期类型(java.util.Date)且字段值不为空,则对日期字段进行格式转换处理。 - if ("java.util.Date".equals(field.getType().getName()) && item.get(field.getName())!= null) { - // 获取字段上的JsonFormat注解。 + //日期格式转换 + if ("java.util.Date".equals(field.getType().getName()) && item.get(field.getName()) != null) { + + // 获取注解 JsonFormat ann = field.getAnnotation(JsonFormat.class); - // 定义日期格式化对象。 + // 格式化方式 SimpleDateFormat fmt; - // 如果注解不为空且指定了日期格式模式,则使用注解指定的格式创建日期格式化对象。 - if (ann!= null &&!StringUtils.isEmpty(ann.pattern())) { + // 使用注解指定的 + if (ann != null && !StringUtils.isEmpty(ann.pattern())) { fmt = new SimpleDateFormat(ann.pattern()); } else { - // 如果注解为空或未指定格式,则使用默认的日期格式创建日期格式化对象。 + // 默认时间样式 fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } item.put(field.getName(), fmt.format(new Date((Long) item.get(field.getName())))); continue; + } } @@ -231,53 +200,49 @@ public class DictAspect { } /** - * 处理类型为List的字段,对列表中的每个元素进行数据字典值处理等操作。 + * 获得类型为List的值 * - * @param field 字段对象,即要处理的List类型字段。 - , - * @param list 列表对象,即字段对应的列表值。 - * @return 处理后的列表,经过数据字典值处理后的列表对象。 + * @param field + * @return */ private List processList(Field field, List list) { - // 如果列表为空,则返回一个空的ArrayList对象。 + + // 空判断 if (list == null || list.size() == 0) { return new ArrayList<>(); } - // 获取List属性的真实类型,通过反射获取字段的泛型类型,再尝试获取其实际的类型参数。 + // 获得List属性的真实类 Type genericType = field.getGenericType(); Class actualType = null; if (genericType instanceof ParameterizedType) { + // 尝试获取数据类型 ParameterizedType pt = (ParameterizedType) genericType; try { actualType = (Class) pt.getActualTypeArguments()[0]; - } catch (Exception e) { + }catch (Exception e){ return list; } } - // 如果列表元素的类型是基本数据类型,则不需要进行数据字典值处理,直接返回原列表。 + // 常规列表无需处理 if (isBaseType(actualType)) { return list; } - // 创建一个新的ArrayList对象,用于存储处理后的列表元素。 + // 返回列表 List result = new ArrayList<>(16); for (int i = 0; i < list.size(); i++) { - // 获取列表中的每个元素。 + // 创建实例-->赋值-->字典处理 Object data = list.get(i); try { - // 将列表元素转换为JSON字符串,再解析为其真实类型的对象,以便进行数据字典值处理。 data = JSON.parseObject(JSON.toJSONString(data), actualType); - } catch (Exception e) { - // 如果转换过程中出现错误,则不进行处理,直接使用原元素。 - // 这里可以根据实际需求进一步处理错误情况,比如记录日志等。 - // 目前只是简单地忽略错误,继续处理下一个元素。 - ; + }catch (Exception e){ + // 转换出错不处理 } - // 对处理后的元素进行数据字典值处理,调用parseObject方法。 + // 处理后的数据 Object pds = this.parseObject(data); result.add(pds); } @@ -286,25 +251,23 @@ public class DictAspect { } /** - , - * 根据字典代码、文本、表名和键值翻译数据字典值对应的文本。 + * 翻译实现 * - * @param code 字典代码,用于在数据字典表中定位特定的字典项。 - * @param text 字典文本,可能是与字典代码相关的描述信息等。 - * @param table 字典表名,指定要查询的数据字典表。 - * @param key 字典键值,用于在字典表中查找对应的字典项。 - * @return 翻译后的值,即根据字典代码、文本、表名和键值查询到的字典文本值,如果未找到则返回空字符串。 + * @param code + * @param text + * @param table + * @param key + * @return */ - private String translateDictValue(String code, String text, String table, String键值) { + private String translateDictValue(String code, String text, String table, String key) { if (StringUtils.isEmpty(key)) { return null; } try { - // 定义变量用于存储翻译后的字典文本值。 + // 翻译值 String dictText = null; if (!StringUtils.isEmpty(table)) { - // 如果字典表名不为空,则调用sysDictService的findDict方法查询数据字典表,获取对应的字典文本值。 - dictText = sysDictService.findDict(table, text, code, key.trim()); + dictText = sysDictService.findDict(table, text, code, key.trim()); } if (!StringUtils.isEmpty(dictText)) { @@ -317,13 +280,15 @@ public class DictAspect { } /** - * 判断给定的类是否是基本数据类型,包括常见的整数、字节、长整数、双精度浮点数、单精度浮点数、字符、短整数、布尔值以及字符串和数字类型等。 + * 判断是否基本类型 * - * @param clazz 要判断的类对象。 - * @return 是否基本类型,如果是基本数据类型则返回true,否则返回false。 + * @param clazz + * @return */ private boolean isBaseType(Class clazz) { - // 判断是否是常见的基本数据类型,如整数、字节、长整数等。 + + + // 基础数据类型 if (clazz.equals(java.lang.Integer.class) || clazz.equals(java.lang.Byte.class) || clazz.equals(java.lang.Long.class) || @@ -335,16 +300,18 @@ public class DictAspect { return true; } - // 判断是否是字符串类型。 + // String类型 if (clazz.equals(java.lang.String.class)) { return true; } - // 判断是否是数字类型(这里的数字类型可能是指抽象的数字类型,比如Number的子类等)。 + // 数字 if (clazz.equals(java.lang.Number.class)) { return true; } return false; } -} \ No newline at end of file + + +} diff --git a/src-源文件/main/java/com/yf/exam/aspect/mybatis/QueryInterceptor.java b/src-源文件/main/java/com/yf/exam/aspect/mybatis/QueryInterceptor.java index 2ca40a4..6b958ca 100644 --- a/src-源文件/main/java/com/yf/exam/aspect/mybatis/QueryInterceptor.java +++ b/src-源文件/main/java/com/yf/exam/aspect/mybatis/QueryInterceptor.java @@ -1,255 +1,144 @@ package com.yf.exam.aspect.mybatis; -// 导入MyBatis Plus的分页拦截器类,用于实现分页功能, -// 本类继承自该类以在拦截查询操作时能正确处理分页相关逻辑。 import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; - -// 导入系统用户登录信息的数据传输对象(DTO)类,用于获取当前登录用户的相关信息, -// 比如在处理查询拦截时可能需要根据登录用户的信息来过滤或修改查询语句。 import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; - -// 导入Lombok的Log4j2注解,用于简化日志记录的配置,通过该注解可以方便地在类中使用Log4j2进行日志输出。 import lombok.extern.log4j.Log4j2; - -// 导入JSqlParser库中的解析器管理器类,用于解析SQL语句, -// 在本类中用于解析查询语句以便进行后续的处理,如替换用户ID等操作。 import net.sf.jsqlparser.parser.CCJSqlParserManager; - -// 导入JSqlParser库中表示简单查询语句的类,它是查询语句的一种具体表示形式, -// 在解析查询语句后可以获取到该对象来进一步处理查询语句的内容。 import net.sf.jsqlparser.statement.select.PlainSelect; - -// 导入JSqlParser库中表示查询语句的通用类,用于接收解析后的查询语句对象, -// 以便进行后续的类型转换和处理操作。 import net.sf.jsqlparser.statement.select.Select; - -// 导入Apache Commons Lang3库中的StringUtils类,用于处理字符串相关的操作, -// 如判断字符串是否为空、非空等情况,在本类中用于判断用户ID等字符串是否有效。 import org.apache.commons.lang3.StringUtils; - -// 导入MyBatis中的语句处理器接口类,它是MyBatis在执行SQL语句时涉及的一个重要组件, -// 在本类中作为拦截的目标对象类型,以便在其执行查询准备阶段进行拦截操作。 import org.apache.ibatis.executor.statement.StatementHandler; - -// 导入MyBatis中的映射语句类,它包含了关于SQL语句的映射信息, -// 如SQL语句的ID、参数映射、结果映射等,在本类中用于获取SQL语句的相关属性,如语句类型等。 import org.apache.ibatis.mapping.MappedStatement; - -// 导入MyBatis中的SQL命令类型枚举类,用于表示不同类型的SQL命令, -// 如SELECT、INSERT、UPDATE、DELETE等,在本类中用于判断当前拦截的SQL语句是否为查询语句。 import org.apache.ibatis.mapping.SqlCommandType; - -// 导入MyBatis的拦截器接口类,定义了拦截器的基本行为和方法, -// 本类实现了该接口以作为一个MyBatis的拦截器来拦截查询语句的执行过程。 import org.apache.ibatis.plugin.Interceptor; - -// 导入MyBatis的拦截器注解类,用于标注一个类是MyBatis的拦截器并指定拦截的目标和方法, -// 本类通过该注解指定了要拦截StatementHandler的prepare方法。 import org.apache.ibatis.plugin.Intercepts; - -// 导入MyBatis的拦截器调用类,用于在拦截器方法中传递被拦截方法的调用信息, -// 包括被拦截的目标对象、方法参数等,在本类的intercept方法中会接收到该对象来处理拦截逻辑。 import org.apache.ibatis.plugin.Invocation; - -// 导入MyBatis的插件包装类,用于将拦截器包装成MyBatis可识别的插件形式, -// 在本类的plugin方法中会使用该类来包装目标对象以便实现拦截功能。 import org.apache.ibatis.plugin.Plugin; - -// 导入MyBatis的拦截器签名类,用于定义拦截器拦截的具体目标、方法和参数类型, -// 在本类的@Intercepts注解中会使用该类来指定具体的拦截信息。 import org.apache.ibatis.plugin.Signature; - -// 导入MyBatis的默认反射工厂类,用于创建反射对象来访问和修改MyBatis相关对象的属性, -// 在本类中用于创建MetaObject对象来操作StatementHandler等对象的属性。 import org.apache.ibatis.reflection.DefaultReflectorFactory; - -// 导入MyBatis的元对象类,它提供了一种通过反射来访问和操作MyBatis相关对象属性的机制, -// 在本类中用于获取和设置StatementHandler等对象的内部属性,如SQL语句等。 import org.apache.ibatis.reflection.MetaObject; - -// 导入MyBatis的系统元对象类,它是MetaObject的一种特殊实现,提供了一些默认的对象工厂和包装器工厂, -// 在本类中用于创建MetaObject对象来操作StatementHandler等对象的属性。 import org.apache.ibatis.reflection.SystemMetaObject; - -// 导入Apache Shiro的安全工具类,用于获取当前安全上下文的相关信息, -// 在本类中用于获取当前登录用户的信息,以便根据用户信息来处理查询语句。 import org.apache.shiro.SecurityUtils; -// 导入Java标准库中的字符串读取器类,用于将字符串转换为可读取的流形式, -// 在本类中用于将SQL语句字符串提供给CCJSqlParserManager进行解析。 import java.io.StringReader; - -// 导入Java标准库中的数据库连接接口类,它是Java数据库操作中与数据库建立连接的关键接口, -// 在本类中作为StatementHandler的prepare方法的参数类型之一出现,虽然在本类代码中未直接对其进行复杂操作, -// 但它是MyBatis执行SQL语句过程中涉及到的重要组件之一。 import java.sql.Connection; - -// 导入Java标准库中的属性类,用于存储和管理键值对形式的属性信息, -// 在本类中作为Interceptor接口的setProperties方法的参数类型,虽然在本类代码中该方法暂未实现具体功能, -// 但它是符合Interceptor接口规范的一部分,可用于接收外部配置的属性信息来动态调整拦截器的行为。 import java.util.Properties; /** - * 查询拦截器类,用于拦截处理通用的信息,如用户ID、多租户信息等。 - * 特别注意:此处继承了PaginationInterceptor分页,分页必须在拦截数据后执行,否则容易出现分页不准确, - * 分页计数大于实际数量等问题。 + * 查询拦截器,用于拦截处理通用的信息、如用户ID、多租户信息等; + * 特别注意:此处继承了PaginationInterceptor分页,分页必须在拦截数据后执行,否则容易出现分页不准确,分页计数大于实际数量等问题 * @author bool */ @Log4j2 -// 使用@Intercepts注解指定该类作为MyBatis的拦截器要拦截的目标和方法。 -// 这里拦截的是StatementHandler类的prepare方法,并且该方法的参数类型为Connection和Integer。 -@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) +@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),}) public class QueryInterceptor extends PaginationInterceptor implements Interceptor { /** - * 客户ID的占位符字符串,在原始SQL语句中如果出现该占位符,将会被替换为实际的用户ID。 + * 客户ID */ private static final String USER_FILTER = "{{userId}}"; - /** - * 拦截器的核心方法,用于在目标方法(StatementHandler的prepare方法)被调用时进行拦截处理。 - * - * @param invocation 包含了被拦截方法的调用信息,如目标对象、方法参数等。 - * @return 返回处理后的结果,可能是继续执行被拦截方法后的结果,也可能是经过拦截器修改后的结果。 - * @throws Throwable 如果在拦截处理过程中出现异常,则抛出。 - */ + + @Override public Object intercept(Invocation invocation) throws Throwable { - // 从invocation对象中获取被拦截的目标对象,即StatementHandler对象。 StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); - - // 使用MetaObject.forObject方法创建一个元对象,用于通过反射访问和修改StatementHandler对象的属性。 - // 这里传入了默认的对象工厂、包装器工厂和反射工厂,以便能够正确地操作StatementHandler对象的内部属性。 MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); - - // 从元对象中获取MappedStatement对象,该对象包含了关于SQL语句的映射信息,如SQL语句的ID、参数映射、结果映射等。 MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); - // 获取当前SQL语句的类型,通过MappedStatement对象的getSqlCommandType方法获取。 - // 该类型是一个枚举值,如SELECT、INSERT、UPDATE、DELETE等,用于判断当前拦截的SQL语句是何种类型的操作。 + //sql语句类型 SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); - // 只对查询类型的SQL语句进行处理,如果当前SQL语句类型是SELECT,则进入下面的处理逻辑。 + // 只过滤查询的 if (SqlCommandType.SELECT == sqlCommandType) { - - // 获取原始的SQL语句,通过StatementHandler的getBoundSql方法获取绑定的SQL语句对象,再获取其SQL字符串。 + // 获得原始SQL String sql = statementHandler.getBoundSql().getSql(); - // 如果原始SQL语句中不包含用户ID占位符(USER_FILTER),则直接调用父类(PaginationInterceptor)的intercept方法, - // 即按照原有的分页逻辑进行处理,不进行用户ID相关的替换等操作。 - if (!sql.contains(USER_FILTER)) { + // 不处理 + if(!sql.contains(USER_FILTER)){ return super.intercept(invocation); } - - // 如果原始SQL语句中包含用户ID占位符,则需要对SQL语句进行处理。 - // 首先调用parseSql方法对SQL语句进行解析和处理,包括替换用户ID等操作。 + // 处理SQL语句 String outSql = this.parseSql(sql); - - // 将处理后的SQL语句设置回StatementHandler对象的内部属性中,通过元对象的setValue方法设置boundSql.sql属性的值。 + // 设置SQL metaObject.setValue("delegate.boundSql.sql", outSql); - - // 再次调用父类(PaginationInterceptor)的intercept方法,此时是在处理完用户ID等信息后, - // 按照正确的分页逻辑进行处理,以确保分页功能在拦截数据并处理相关信息后正确执行。 + // 再分页 return super.intercept(invocation); } - // 如果当前SQL语句不是查询类型(SELECT),则直接执行被拦截方法的原有逻辑,不进行额外的处理。 return invocation.proceed(); } - /** - * 将拦截器包装成MyBatis可识别的插件形式的方法。 - * - * @param target 要包装的目标对象,通常是被拦截的对象,如StatementHandler等。 - * @return 返回包装后的对象,以便MyBatis能够正确识别并应用拦截器的功能。 - */ @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } - /** - * 设置拦截器属性的方法,目前该方法在本类中暂未实现具体功能, - * 但它是符合Interceptor接口规范的一部分,可用于接收外部配置的属性信息来动态调整拦截器的行为。 - * - * @param properties 包含了外部配置的属性信息的Properties对象。 - */ @Override public void setProperties(Properties properties) { } + + /** - * 获取当前登录用户的方法,通过Apache Shiro的SecurityUtils工具类来获取当前安全上下文的主体对象, - * 并获取其主体信息(即登录用户的信息),如果获取成功则转换为SysUserLoginDTO类型返回,否则返回null。 - * - * @return 返回当前登录用户的SysUserLoginDTO对象,如果获取失败则返回null。 + * 获取当前登录用户 + * @return */ private SysUserLoginDTO getLoginUser() { try { - return SecurityUtils.getSubject().getPrincipal()!= null? (SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal() : null; + return SecurityUtils.getSubject().getPrincipal() != null ? (SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal() : null; } catch (Exception e) { return null; } } /** - * 替换用户ID的方法,根据当前登录用户的ID,将原始SQL语句中的用户ID占位符(USER_FILTER)替换为实际的用户ID。 - * - * @param sql 原始的SQL语句。 - * @return 返回替换用户ID后的SQL语句,如果用户ID为空,则返回null。 + * 替换用户ID + * @param sql + * @return */ private String processUserId(String sql) { - // 获取当前登录用户的信息,通过调用getLoginUser方法获取。 + // 当前用户 SysUserLoginDTO user = this.getLoginUser(); String userId = user.getId(); - - // 如果获取到的用户ID不为空,则将原始SQL语句中的用户ID占位符替换为实际的用户ID,并返回替换后的SQL语句。 - if (StringUtils.isNotBlank(userId)) { + if(StringUtils.isNotBlank(userId)){ return sql.replace(USER_FILTER, userId); } - - // 如果用户ID为空,则返回null。 return null; } /** - * 处理注入用户信息的方法,主要用于解析原始SQL语句,替换用户ID等操作,以实现根据用户信息来调整查询语句的目的。 - * - * @param src 原始的SQL语句。 - * @return 返回处理后的SQL语句,如果在处理过程中出现异常,则返回原始的SQL语句。 + * 处理注入用户信息 + * @param src + * @return */ private String parseSql(String src) { - // 创建一个CCJSqlParserManager对象,用于解析SQL语句。 CCJSqlParserManager parserManager = new CCJSqlParserManager(); try { - // 使用CCJSqlParserManager对象的parse方法解析原始SQL语句,将其转换为Select类型的对象。 - // 这里需要将原始SQL语句通过StringReader转换为可读取的流形式提供给parse方法进行解析。 Select select = (Select) parserManager.parse(new StringReader(src)); - - // 从Select对象中获取其查询主体部分,即PlainSelect对象,它包含了查询语句的具体内容,如查询条件、字段等。 PlainSelect selectBody = (PlainSelect) select.getSelectBody(); - // 将PlainSelect对象转换为字符串形式,得到初步处理后的SQL语句。 - // 这里主要是为了后续方便进行用户ID等信息的替换操作。 + // 过滤客户 String sql = selectBody.toString(); - // 调用processUserId方法对初步处理后的SQL语句进行用户ID的替换操作,将用户ID占位符替换为实际的用户ID。 + // 过滤用户ID sql = this.processUserId(sql); - // 返回处理后的SQL语句。 + // 获得SQL return sql; } catch (Exception e) { e.printStackTrace(); } - // 如果在处理过程中出现异常,则返回原始的SQL语句。 return src; } -} \ No newline at end of file + + +} diff --git a/src-源文件/main/java/com/yf/exam/aspect/mybatis/UpdateInterceptor.java b/src-源文件/main/java/com/yf/exam/aspect/mybatis/UpdateInterceptor.java index c85e5f0..8baae8f 100644 --- a/src-源文件/main/java/com/yf/exam/aspect/mybatis/UpdateInterceptor.java +++ b/src-源文件/main/java/com/yf/exam/aspect/mybatis/UpdateInterceptor.java @@ -1,188 +1,80 @@ -// 定义包路径,用于存放更新拦截器相关的类。这个包名表明该类属于特定的项目模块(com.yf.exam)下的aspect.mybatis部分, -// 通常用于组织和管理与MyBatis相关的切面逻辑代码。 package com.yf.exam.aspect.mybatis; -// 导入MyBatis-Plus的抽象SQL解析处理器类,它提供了一些基础的SQL解析处理功能, -// 本类继承自该类以便在处理更新操作时利用其相关功能或遵循其处理规范。 import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler; - -// 导入Apache Commons Lang3库中的数组工具类,用于对数组进行各种操作, -// 如合并数组等,在本类中用于合并对象的字段数组(当存在父类字段时)。 import org.apache.commons.lang3.ArrayUtils; - -// 导入MyBatis中的执行器接口类,它是MyBatis执行SQL语句的核心组件,负责实际执行数据库操作, -// 在本类中作为拦截的目标对象类型,通过拦截其update方法来实现自动设置创建时间和更新时间的功能。 import org.apache.ibatis.executor.Executor; - -// 导入MyBatis中的映射语句类,它包含了关于SQL语句的映射信息, -// 如SQL语句的ID、参数映射、结果映射等,在本类中用于获取SQL语句的相关属性,如语句类型等, -// 以便根据不同的操作类型(插入、更新等)来处理创建时间和更新时间的赋值。 import org.apache.ibatis.mapping.MappedStatement; - -// 导入MyBatis中的SQL命令类型枚举类,用于表示不同类型的SQL命令, -// 如SELECT、INSERT、UPDATE、DELETE等,在本类中用于判断当前拦截的SQL语句是何种类型的操作, -// 从而确定是否需要对创建时间和更新时间进行赋值。 import org.apache.ibatis.mapping.SqlCommandType; - -// 导入MyBatis的拦截器接口类,定义了拦截器的基本行为和方法, -// 本类实现了该接口以作为一个MyBatis的拦截器来拦截更新语句的执行过程,实现自动设置时间的功能。 import org.apache.ibatis.plugin.Interceptor; - -// 导入MyBatis的拦截器注解类,用于标注一个类是MyBatis的拦截器并指定拦截的目标和方法, -// 本类通过该注解指定了要拦截Executor类的update方法。 import org.apache.ibatis.plugin.Intercepts; - -// 导入MyBatis的拦截器调用类,用于在拦截器方法中传递被拦截方法的调用信息, -// 包括被拦截的目标对象、方法参数等,在本类的intercept方法中会接收到该对象来处理拦截逻辑。 import org.apache.ibatis.plugin.Invocation; - -// 导入MyBatis的插件包装类,用于将拦截器包装成MyBatis可识别的插件形式, -// 在本类的plugin方法中会使用该类来包装目标对象以便实现拦截功能。 import org.apache.ibatis.plugin.Plugin; - -// 导入MyBatis的拦截器签名类,用于定义拦截器拦截的具体目标、方法和参数类型, -// 在本类的@Intercepts注解中会使用该类来指定具体的拦截信息。 import org.apache.ibatis.plugin.Signature; -// 导入Java标准库中的反射字段类,用于通过反射操作对象的私有字段, -// 在本类中用于获取和设置对象的创建时间和更新时间字段的值。 import java.lang.reflect.Field; - -// 导入Java标准库中的时间戳类,用于表示某个特定时刻的时间点, -// 在本类中用于设置创建时间和更新时间为当前的时间戳,以记录操作发生的准确时间。 import java.sql.Timestamp; - -// 导入Java标准库中的对象工具类,提供了一些用于比较和操作对象的方法, -// 在本类中用于比较字段名与预定义的创建时间、更新时间字段名是否相等。 import java.util.Objects; - -// 导入Java标准库中的属性类,用于存储和管理键值对形式的属性信息, -// 在本类中作为Interceptor接口的setProperties方法的参数类型,虽然在本类代码中该方法暂未实现具体功能, -// 但它是符合Interceptor接口规范的一部分,可用于接收外部配置的属性信息来动态调整拦截器的行为。 import java.util.Properties; /** - * 该类是一个更新拦截器,主要功能是自动给创建时间和更新时间字段赋值。 - * 它会拦截MyBatis执行器(Executor)的update方法,根据操作类型(插入或更新)以及对象的字段情况, - * 为创建时间和更新时间字段设置当前的时间戳值。 + * 自动给创建时间个更新时间加值 * @author bool */ @Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) -// 使用@Intercepts注解并通过@Signature指定该类作为MyBatis的拦截器要拦截的目标和方法。 -// 这里拦截的是Executor类的update方法,并且该方法的参数类型为MappedStatement和Object。 public class UpdateInterceptor extends AbstractSqlParserHandler implements Interceptor { /** - * 定义创建时间字段的名称常量,用于在后续代码中方便地识别和操作对象中的创建时间字段。 + * 创建时间 */ private static final String CREATE_TIME = "createTime"; - /** - * 定义更新时间字段的名称常量,用于在后续代码中方便地识别和操作对象中的更新时间字段。 + * 更新时间 */ private static final String UPDATE_TIME = "updateTime"; - /** - * 拦截器的核心方法,用于在目标方法(Executor的update方法)被调用时进行拦截处理。 - * - * @param invocation 包含了被拦截方法的调用信息,如目标对象、方法参数等。 - * @return 返回处理后的结果,可能是继续执行被拦截方法后的结果,也可能是经过拦截器修改后的结果。 - * @throws Throwable 如果在拦截处理过程中出现异常,则抛出。 - */ @Override public Object intercept(Invocation invocation) throws Throwable { - // 从invocation对象的参数数组中获取第一个参数,即MappedStatement对象, - // 它包含了关于SQL语句的映射信息,用于后续获取SQL命令类型等操作。 MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; - - // 获取当前SQL语句的类型,通过MappedStatement对象的getSqlCommandType方法获取。 - // 该类型是一个枚举值,如SELECT、INSERT、UPDATE、DELETE等,用于判断当前拦截的SQL语句是何种类型的操作。 + // SQL操作命令 SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); - - // 从invocation对象的参数数组中获取第二个参数,即要进行新增或修改操作的对象参数, - // 后续会通过反射操作该对象来设置创建时间和更新时间字段的值。 + // 获取新增或修改的对象参数 Object parameter = invocation.getArgs()[1]; - - // 获取对象中所有的私有成员变量(对应表字段),通过反射获取parameter对象的声明字段数组。 + // 获取对象中所有的私有成员变量(对应表字段) Field[] declaredFields = parameter.getClass().getDeclaredFields(); - - // 判断parameter对象的父类是否存在,如果存在则获取父类的声明字段数组, - // 并将其与之前获取的子类声明字段数组进行合并,以获取完整的包含父类和子类字段的数组。 - if (parameter.getClass().getSuperclass()!= null) { + if (parameter.getClass().getSuperclass() != null) { Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields(); declaredFields = ArrayUtils.addAll(declaredFields, superField); } - // 用于临时存储当前遍历到的字段名,以便后续与预定义的创建时间、更新时间字段名进行比较。 String fieldName = null; - - // 遍历获取到的所有字段数组(包含父类和子类字段),对每个字段进行处理。 for (Field field : declaredFields) { fieldName = field.getName(); - - // 如果当前遍历到的字段名与预定义的创建时间字段名相等,说明找到了创建时间字段。 if (Objects.equals(CREATE_TIME, fieldName)) { - - // 如果当前SQL语句的操作类型是插入操作(INSERT),则需要为创建时间字段设置当前时间。 if (SqlCommandType.INSERT.equals(sqlCommandType)) { - - // 通过反射设置该字段可访问,因为私有字段默认是不可直接访问的。 field.setAccessible(true); - - // 使用反射设置该字段的值为当前的时间戳,通过System.currentTimeMillis()获取当前时间的毫秒数, - // 再创建一个Timestamp对象来表示该时间点,从而为创建时间字段赋值。 field.set(parameter, new Timestamp(System.currentTimeMillis())); } } - - // 如果当前遍历到的字段名与预定义的更新时间字段名相等,说明找到了更新时间字段。 if (Objects.equals(UPDATE_TIME, fieldName)) { - - // 如果当前SQL语句的操作类型是插入操作(INSERT)或者更新操作(UPDATE), - // 则需要为更新时间字段设置当前时间。 if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { - - // 通过反射设置该字段可访问,因为私有字段默认是不可直接访问的。 field.setAccessible(true); - - // 使用反射设置该字段的值为当前的时间戳,通过System.currentTimeMillis()获取当前时间的毫秒数, - // 再创建一个Timestamp对象来表示该时间点,从而为更新时间字段赋值。 field.set(parameter, new Timestamp(System.currentTimeMillis())); + } } } - - // 继续执行被拦截的Executor的update方法的原有逻辑,经过上述处理后, - // 已经为创建时间和更新时间字段设置了合适的值(如果符合条件的话),现在继续执行原始的更新操作。 return invocation.proceed(); } - /** - * 将拦截器包装成MyBatis可识别的插件形式的方法。 - * - * @param target 要包装的目标对象,通常是被拦截的对象,如Executor等。 - * @return 返回包装后的对象,以便MyBatis能够正确识别并应用拦截器的功能。 - */ @Override public Object plugin(Object target) { - // 如果目标对象是Executor类型,说明是我们要拦截的对象, - // 则使用Plugin.wrap方法将拦截器(this)包装到目标对象上,以便实现拦截功能。 if (target instanceof Executor) { return Plugin.wrap(target, this); } - - // 如果目标对象不是Executor类型,说明不是我们要拦截的对象,直接返回该目标对象即可。 return target; } - /** - * 设置拦截器属性的方法,目前该方法在本类中暂未实现具体功能, - * 但它是符合Interceptor接口规范的一部分,可用于接收外部配置的属性信息来动态调整拦截器的行为。 - * - * @param properties 包含了外部配置的属性信息的Properties对象。 - */ @Override public void setProperties(Properties properties) { - // 这里暂时没有具体的设置属性操作,可根据后续需求添加相关逻辑来处理接收到的属性信息。 } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/aspect/utils/InjectUtils.java b/src-源文件/main/java/com/yf/exam/aspect/utils/InjectUtils.java index 77ed253..4b47fee 100644 --- a/src-源文件/main/java/com/yf/exam/aspect/utils/InjectUtils.java +++ b/src-源文件/main/java/com/yf/exam/aspect/utils/InjectUtils.java @@ -1,128 +1,99 @@ -// 定义包路径,用于存放注入工具类相关的代码。这个包名表明该类属于特定的项目模块(com.yf.exam)下的aspect.utils部分, -// 通常用于组织和管理与注入操作相关的工具类代码。 package com.yf.exam.aspect.utils; -// 导入FastJSON库,它是一个用于将Java对象与JSON字符串进行相互转换的高性能库, -// 在本类的restError方法中用于将API响应对象转换为JSON字符串以便写入HTTP响应中。 import com.alibaba.fastjson.JSON; - -// 导入API错误类,它可能定义了一系列表示不同类型API错误的常量或属性, -// 在本类的restError方法中用于创建包含特定错误信息的API响应对象。 import com.yf.exam.core.api.ApiError; - -// 导入API响应类,它用于封装API调用的返回结果,包括成功的结果以及可能出现的错误信息等, -// 在本类的restError方法中用于创建要返回给客户端的错误响应对象。 import com.yf.exam.core.api.ApiRest; - -// 导入Lombok的Log4j2注解,用于简化日志记录的配置,通过该注解可以方便地在类中使用Log4j2进行日志输出。 import lombok.extern.log4j.Log4j2; - -// 导入Spring框架的组件注解,用于标记该类是一个Spring组件,这样Spring容器可以对其进行管理和实例化, -// 使其能够在Spring应用程序中被其他组件所使用。 import org.springframework.stereotype.Component; -// 导入Java标准库中的HTTP响应类,用于处理HTTP协议相关的响应操作, -// 在本类的restError方法中用于设置响应的编码、内容类型以及写入响应内容等操作。 import javax.servlet.http.HttpServletResponse; - -// 导入Java标准库中的IO异常类,用于处理输入输出操作过程中可能出现的异常情况, -// 在本类的restError方法以及其他可能涉及到IO操作的地方用于捕获和处理相关异常。 import java.io.IOException; - -// 导入Java标准库中的反射字段类,用于通过反射操作对象的私有字段, -// 在本类的setValue和getFiled方法中用于获取和设置对象的指定字段的值。 import java.lang.reflect.Field; /** - * 注入工具类,提供了一些用于对象字段赋值以及处理错误响应返回等功能的方法。 + * 注入工具类 * @author bool * @date 2019-07-17 09:32 */ -@Log4j2 // 使用Log4j2注解启用日志记录功能,方便在类中记录相关操作的日志信息。 -@Component // 将该类标记为Spring组件,使其能够被Spring容器管理和使用。 +@Log4j2 +@Component public class InjectUtils { + + /** - * 给指定对象的指定字段赋值的方法。 + * 给对象字段赋值 * - * @param object 要进行赋值操作的对象。 - * @param value 要赋给指定字段的值。 - * @param fields 一个可变参数,用于指定要赋值的字段名数组。 - * @throws Exception 如果在获取字段或设置字段值的过程中出现异常,则抛出。 + * @param object 赋值的对象 + * @param value 值 + * @param fields 字段 + * @throws Exception 异常 */ public void setValue(Object object, Object value, String... fields) throws Exception { - // 遍历要赋值的字段名数组 + + //设置同类的属性 for (String fieldName : fields) { - // 根据对象的类和字段名获取对应的字段对象 - Field field = this.getFiled(object.getClass(), fieldName); - // 如果获取到的字段对象为空,说明未找到对应的字段,继续下一个字段的处理。 - if (field == null) { + //获取当前 + Field field = this.getFiled(object.getClass(), fieldName); + if(field == null){ continue; } - // 通过反射设置该字段可访问,因为私有字段默认是不可直接访问的。 field.setAccessible(true); - - // 使用反射设置该字段的值为传入的值,即将指定的值赋给对象的指定字段。 field.set(object, value); } + } /** - * 根据目标类和字段名获取对应的字段对象的方法。 + * 获取字段名对应的字段 * - * @param clazz 目标类,即要在其中查找字段的类。 - * @param fieldName 要查找的字段名。 - * @return 返回找到的字段对象,如果未找到则返回null。 + * @param clazz 目标类 + * @param fieldName 字段名 */ private Field getFiled(Class clazz, String fieldName) { - System.out.println("注入的类:" + clazz.toString()); // 打印当前正在查找字段的目标类的信息,方便调试查看。 - // 尝试获取当前类中指定字段名的字段对象 + System.out.println("注入的类:"+clazz.toString()); + + //是否具有包含关系 try { + //获取当前类的属性 return clazz.getDeclaredField(fieldName); - } catch (Exception e) { - log.error(clazz.toString() + ": not exist field, try superclass " + fieldName); // 如果获取字段失败,记录错误日志, - // 提示在当前类中不存在指定字段, - // 并准备尝试在父类中查找。 + }catch (Exception e){ - // 如果当前类未找到指定字段且存在父类,则递归调用本方法在父类中继续查找指定字段。 - if (clazz.getSuperclass()!= null) { + log.error(clazz.toString() + ": not exist field, try superclass " + fieldName); + + //如果为空且存在父类,则往上找 + if(clazz.getSuperclass()!=null){ return this.getFiled(clazz.getSuperclass(), fieldName); } - // 如果在当前类及其所有父类中都未找到指定字段,则返回null。 return null; } } + /** - * 用于处理错误情况并将错误结果返回给客户端的方法,通过设置HTTP响应的相关属性和写入错误响应内容来实现。 - * - * @param response HTTP响应对象,用于向客户端发送响应信息。 - * @throws IOException 如果在设置响应属性或写入响应内容过程中出现IO异常,则抛出。 + * 打印结果返回 + * @param response + * @throws IOException */ public static void restError(HttpServletResponse response) { + try { - // 创建一个包含特定API错误信息的API响应对象,这里使用了ApiError.ERROR_10010002作为错误码, - // 具体含义可能在ApiError类中定义,通常表示某种常见的错误情况。 - ApiRest apiRest = new ApiRest(ApiError.ERROR_10010002); - // 设置HTTP响应的字符编码为UTF-8,确保能够正确处理和传输各种字符,特别是中文等非ASCII字符。 + //固定错误 + ApiRest apiRest = new ApiRest(ApiError.ERROR_10010002); response.setCharacterEncoding("UTF-8"); - - // 设置HTTP响应的内容类型为application/json,表明返回给客户端的内容是JSON格式的数据。 response.setContentType("application/json"); - - // 将创建的API响应对象转换为JSON字符串,并写入到HTTP响应的输出流中,以便客户端能够接收到错误信息。 response.getWriter().write(JSON.toJSONString(apiRest)); - - // 关闭HTTP响应的写入流,释放相关资源。 response.getWriter().close(); - } catch (IOException e) { - // 如果在设置响应属性或写入响应内容过程中出现IO异常,这里只是简单地捕获异常, - // 可以根据实际需求进一步处理异常,比如记录更详细的日志信息等。 + + }catch (IOException e){ + } + } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/config/CorsConfig.java b/src-源文件/main/java/com/yf/exam/config/CorsConfig.java index 83b86ab..e88cb08 100644 --- a/src-源文件/main/java/com/yf/exam/config/CorsConfig.java +++ b/src-源文件/main/java/com/yf/exam/config/CorsConfig.java @@ -1,81 +1,35 @@ package com.yf.exam.config; -// 导入Spring Boot中用于注册Servlet过滤器的类,通过它可以将自定义的过滤器注册到Servlet容器中, -// 并配置相关属性,如过滤器的执行顺序等。在本类中用于注册跨域过滤器(CorsFilter)。 import org.springframework.boot.web.servlet.FilterRegistrationBean; - -// 导入Spring框架中用于标记一个类是配置类的注解,被该注解标记的类可以在其中定义各种Bean配置方法, -// 这些方法会被Spring容器在启动时自动识别并执行,用于创建和配置应用程序所需的各种组件。 import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -// 导入Spring框架中用于指定顺序的接口,实现该接口可以定义对象的顺序, -// 在本类中用于指定跨域过滤器(CorsFilter)在过滤器链中的执行顺序,使其具有最高优先级。 import org.springframework.core.Ordered; - -// 导入Spring框架中用于配置跨域资源共享(Cors)的类,通过它可以设置允许的源、请求头、请求方法等跨域相关的配置项。 import org.springframework.web.cors.CorsConfiguration; - -// 导入Spring框架中基于URL的跨域配置源类,它允许根据不同的URL模式来配置不同的跨域设置, -// 在本类中用于创建一个基于URL的跨域配置源,并将统一的跨域配置应用到所有的URL路径("**")上。 import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -// 导入Spring框架中实现跨域过滤功能的类,它会根据配置的跨域规则对请求进行过滤和处理, -// 以实现允许跨域访问的功能。在本类中创建该过滤器并将其注册到Servlet容器中。 import org.springframework.web.filter.CorsFilter; + /** - * 网关全局设置类,主要功能是配置允许跨域访问的相关设置。 - * 通过创建和注册跨域过滤器(CorsFilter),并设置允许的源、请求头、请求方法等配置项, - * 使得应用程序能够处理来自不同域的请求,避免跨域访问限制。 - * + * 网关全局设置,允许跨域 * @author bool * @date 2019-08-13 17:28 */ -@Configuration // 标记该类为Spring框架的配置类,表明其中可以定义各种Bean的创建和配置方法。 + +@Configuration public class CorsConfig { - /** - * 定义一个Bean方法,用于创建并返回一个FilterRegistrationBean对象,该对象用于注册跨域过滤器(CorsFilter)。 - * - * @return 返回一个FilterRegistrationBean对象,其中包含了配置好的跨域过滤器以及相关的执行顺序等设置。 - */ @Bean public FilterRegistrationBean corsFilter() { - // 创建一个基于URL的跨域配置源对象,它将作为跨域配置的基础,用于后续设置跨域规则并应用到特定的URL路径上。 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - - // 创建一个CorsConfiguration对象,用于设置具体的跨域配置项,如允许的源、请求头、请求方法等。 CorsConfiguration config = new CorsConfiguration(); - - // 设置是否允许携带凭证(如Cookie等)进行跨域请求,这里设置为true,表示允许携带凭证。 config.setAllowCredentials(true); - - // 添加允许的源,这里使用CorsConfiguration.ALL表示允许所有的源进行跨域访问。 - // 可以根据实际需求设置具体的源地址,如"http://example.com"等,多个源地址可以多次调用addAllowedOrigin方法添加。 config.addAllowedOrigin(CorsConfiguration.ALL); - - // 添加允许的请求头,同样使用CorsConfiguration.ALL表示允许所有的请求头进行跨域请求。 - // 如果需要限制特定的请求头,可以通过其他方式设置具体的请求头列表。 config.addAllowedHeader(CorsConfiguration.ALL); - - // 添加允许的请求方法,使用CorsConfiguration.ALL表示允许所有的请求方法进行跨域请求, - // 包括常见的GET、POST、PUT、DELETE等方法。也可以根据实际需求设置具体的请求方法列表。 config.addAllowedMethod(CorsConfiguration.ALL); - - // 将上述设置好的跨域配置应用到所有的URL路径上,"**"表示匹配任意的URL路径。 - // 这样,对于应用程序接收到的任何请求,都会根据这个统一的跨域配置进行处理。 source.registerCorsConfiguration("/**", config); - - // 创建一个FilterRegistrationBean对象,用于注册跨域过滤器(CorsFilter)。 - // 将创建好的基于上述跨域配置源的跨域过滤器(new CorsFilter(source))作为参数传入FilterRegistrationBean的构造函数。 FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); - - // 设置跨域过滤器在过滤器链中的执行顺序,这里设置为Ordered.HIGHEST_PRECEDENCE,表示具有最高优先级。 - // 即该过滤器会在其他过滤器之前首先对请求进行处理,以确保跨域配置能够最先生效。 bean.setOrder(Ordered.HIGHEST_PRECEDENCE); - - // 返回配置好的FilterRegistrationBean对象,完成跨域过滤器的注册和相关设置。 return bean; } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/config/MultipartConfig.java b/src-源文件/main/java/com/yf/exam/config/MultipartConfig.java index 69d16f5..75e6dfa 100644 --- a/src-源文件/main/java/com/yf/exam/config/MultipartConfig.java +++ b/src-源文件/main/java/com/yf/exam/config/MultipartConfig.java @@ -1,67 +1,28 @@ -// 定义包路径,用于存放文件上传配置类相关的代码。这个包名表明该类属于特定的项目模块(com.yf.exam)下的config部分, -// 通常用于组织和管理与文件上传配置相关的代码。 package com.yf.exam.config; -// 导入Spring Boot中用于创建和配置多部分(Multipart)上传相关设置的工厂类。 -// 通过这个工厂类可以方便地设置如单个文件大小限制、总上传数据大小限制等参数, -// 在本类中用于创建和配置文件上传的相关限制。 import org.springframework.boot.web.servlet.MultipartConfigFactory; - -// 导入Spring框架中用于标记一个方法返回值为Spring Bean的注解。 -// 被该注解标记的方法,其返回值会被Spring容器管理,作为一个可被注入到其他组件中的Bean实例。 -// 在本类中用于将multipartConfigElement方法返回的MultipartConfigElement对象注册为Spring Bean。 import org.springframework.context.annotation.Bean; - -// 导入Spring框架中用于标记一个类是配置类的注解。 -// 被该注解标记的类可以在其中定义各种Bean配置方法,这些方法会被Spring容器在启动时自动识别并执行, -// 用于创建和配置应用程序所需的各种组件。在本类中用于标记该类为Spring配置类,以便进行文件上传相关的配置。 import org.springframework.context.annotation.Configuration; - -// 导入Spring框架中用于处理数据大小单位转换和表示的工具类。 -// 它可以方便地将不同单位的数据大小表示转换为统一的格式, -// 在本类中用于设置文件上传的单个文件大小和总上传数据大小的限制,以兆字节(MB)为单位进行设置。 import org.springframework.util.unit.DataSize; -// 导入Java标准库中的用于表示多部分(Multipart)上传配置元素的接口。 -// 这个接口定义了多部分上传相关的配置参数,如文件大小限制、请求大小限制等, -// 在本类中通过MultipartConfigFactory创建并返回实现了该接口的对象,以完成文件上传的配置设置。 import javax.servlet.MultipartConfigElement; /** - * 文件上传配置类,主要功能是配置文件上传过程中的相关参数, -// 包括单个文件的最大允许大小以及整个上传请求的最大允许数据大小等。 - * + * 文件上传配置 * @author bool * @date 2019-07-29 16:23 */ -@Configuration // 使用该注解标记此为Spring配置类,表明这个类是用来进行Spring应用程序的配置工作的。 +@Configuration public class MultipartConfig { - /** - * 定义一个方法,并使用@Bean注解将其返回值声明为Spring Bean。 - * 该方法用于创建并返回一个MultipartConfigElement对象,这个对象包含了文件上传的具体配置参数。 - * - * @return 返回一个MultipartConfigElement对象,其中设置了单个文件大小和总上传数据大小的限制等配置信息。 - */ @Bean public MultipartConfigElement multipartConfigElement() { - MultipartConfigFactory factory = new MultipartConfigFactory(); // 创建一个MultipartConfigFactory对象, - // 它是用于创建MultipartConfigElement对象的工厂类, - // 通过它可以方便地设置各种文件上传的配置参数。 - - // 设置单个文件的最大允许大小。 - // 这里使用DataSize.ofMegabytes(5000L)将大小设置为5000兆字节(MB), - // 即限制单个上传的文件大小不能超过5000MB。 + MultipartConfigFactory factory = new MultipartConfigFactory(); + // 单个数据大小 factory.setMaxFileSize(DataSize.ofMegabytes(5000L)); - - // 设置整个上传请求的最大允许数据大小。 - // 同样使用DataSize.ofMegabytes(5000L)将大小设置为5000兆字节(MB), - // 也就是说,整个上传请求(包括可能上传的多个文件以及其他相关数据)的总数据量不能超过5000MB。 + /// 总上传数据大小 factory.setMaxRequestSize(DataSize.ofMegabytes(5000L)); - - // 通过MultipartConfigFactory对象的createMultipartConfig方法创建并返回一个MultipartConfigElement对象, - // 这个对象包含了我们刚刚设置的单个文件大小和总上传数据大小等配置信息, - // 它会被Spring容器管理并应用到文件上传的相关处理中。 return factory.createMultipartConfig(); } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/config/MybatisConfig.java b/src-源文件/main/java/com/yf/exam/config/MybatisConfig.java index 1645a7f..642fc25 100644 --- a/src-源文件/main/java/com/yf/exam/config/MybatisConfig.java +++ b/src-源文件/main/java/com/yf/exam/config/MybatisConfig.java @@ -1,70 +1,37 @@ -// 定义包路径,用于存放MyBatis配置类相关的代码。这个包名表明该类属于特定的项目模块(com.yf.exam)下的config部分, -// 通常用于组织和管理与MyBatis配置相关的代码。 package com.yf.exam.config; -// 导入自定义的查询拦截器类,该拦截器用于在查询操作时进行一些额外的处理, -// 比如可能会根据特定条件对查询语句进行修改、添加过滤条件等操作。 import com.yf.exam.aspect.mybatis.QueryInterceptor; - -// 导入自定义的更新拦截器类,该拦截器用于在插入或更新操作时进行相关处理, -// 例如自动设置创建时间、更新时间等字段的值。 import com.yf.exam.aspect.mybatis.UpdateInterceptor; - -// 导入MyBatis与Spring集成时用于扫描MyBatis映射接口的注解。 -// 通过该注解可以指定要扫描的包路径,以便Spring能够自动发现并注册MyBatis的映射接口, -// 使得这些接口可以被正确地用于数据库操作。 import org.mybatis.spring.annotation.MapperScan; - -// 导入Spring框架中用于标记一个方法返回值为Spring Bean的注解。 -// 被该注解标记的方法,其返回值会被Spring容器管理,作为一个可被注入到其他组件中的Bean实例。 -// 在本类中用于将queryInterceptor和updateInterceptor方法返回的拦截器对象注册为Spring Bean。 import org.springframework.context.annotation.Bean; - -// 导入Spring框架中用于标记一个类是配置类的注解。 -// 被该注解标记的类可以在其中定义各种Bean配置方法,这些方法会被Spring容器在启动时自动识别并执行, -// 用于创建和配置应用程序所需的各种组件。在本类中用于标记该类为Spring配置类,以便进行MyBatis相关的配置。 import org.springframework.context.annotation.Configuration; /** - * Mybatis过滤器配置类,主要功能是配置MyBatis的相关过滤器, - * 包括查询拦截器和更新拦截器等,并设置相关参数。同时通过注解指定MyBatis映射接口的扫描路径。 - * 注意:必须按顺序进行配置,否则容易出现业务异常。 + * Mybatis过滤器配置 + * 注意:必须按顺序进行配置,否则容易出现业务异常 * @author bool */ -@Configuration // 使用该注解标记此为Spring配置类,表明这个类是用来进行Spring应用程序的配置工作的。 -@MapperScan("com.yf.exam.modules.**.mapper") // 使用MapperScan注解指定要扫描的MyBatis映射接口所在的包路径, - // "com.yf.exam.modules.**.mapper"表示会扫描com.yf.exam.modules包及其子包下的所有mapper接口。 - +@Configuration +@MapperScan("com.yf.exam.modules.**.mapper") public class MybatisConfig { /** - * 定义一个方法,并使用@Bean注解将其返回值声明为Spring Bean。 - * 该方法用于创建并返回一个查询拦截器(QueryInterceptor)对象,用于在查询操作时进行拦截处理。 - * - * @return 返回一个查询拦截器(QueryInterceptor)对象,该对象已进行了相关设置(如设置查询限制)。 + * 数据查询过滤器 */ - @Bean // 将该方法返回的查询拦截器对象声明为Spring Bean,以便Spring容器能够管理和注入到其他需要使用的地方。 + @Bean public QueryInterceptor queryInterceptor() { - QueryInterceptor query = new QueryInterceptor(); // 创建一个新的查询拦截器对象。 - - // 设置查询限制,这里将查询限制设置为 -1L,具体含义可能根据QueryInterceptor类的内部逻辑而定, - // 可能表示不限制查询结果的数量或者有其他特殊的处理方式与该值相关。 + QueryInterceptor query = new QueryInterceptor(); query.setLimit(-1L); - - // 返回设置好的查询拦截器对象,该对象将被Spring容器管理并在合适的查询操作场景中被调用执行拦截处理。 return query; } /** - * 定义一个方法,并使用@Bean注解将其返回值声明为Spring Bean。 - * 该方法用于创建并返回一个更新拦截器(UpdateInterceptor)对象,用于在插入或更新操作时进行拦截处理。 - * - * @return 返回一个更新拦截器(UpdateInterceptor)对象,该对象可直接用于插入或更新操作的拦截处理。 + * 插入数据过滤器 */ - @Bean // 将该方法返回的更新拦截器对象声明为Spring Bean,以便Spring容器能够管理和注入到其他需要使用的地方。 + @Bean public UpdateInterceptor updateInterceptor() { - // 创建一个新的更新拦截器对象,这里没有进行额外的设置操作(可能在UpdateInterceptor类内部有默认的处理逻辑), - // 直接返回该对象,它将被Spring容器管理并在合适的插入或更新操作场景中被调用执行拦截处理。 return new UpdateInterceptor(); } + + } \ No newline at end of file diff --git a/src-源文件/main/java/com/yf/exam/config/ScheduledConfig.java b/src-源文件/main/java/com/yf/exam/config/ScheduledConfig.java index 994b761..b08f96f 100644 --- a/src-源文件/main/java/com/yf/exam/config/ScheduledConfig.java +++ b/src-源文件/main/java/com/yf/exam/config/ScheduledConfig.java @@ -1,182 +1,77 @@ -// 定义包路径,用于存放任务调度配置类相关的代码。这个包名表明该类属于特定的项目模块(com.yf.exam)下的config部分, -// 通常用于组织和管理与任务调度配置相关的代码。 package com.yf.exam.config; -// 导入Lombok的Log4j2注解,用于简化日志记录的配置,通过该注解可以方便地在类中使用Log4j2进行日志输出。 import lombok.extern.log4j.Log4j2; - -// 导入Spring框架中用于处理异步方法执行过程中未捕获异常的处理器接口。 -// 当异步方法抛出异常且未在方法内部被捕获时,会由该处理器来处理异常情况, -// 在本类中实现该接口来定义异步未捕获异常的处理逻辑。 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; - -// 导入Spring框架中用于标记一个方法返回值为Spring Bean的注解。 -// 被该注解标记的方法,其返回值会被Spring容器管理,作为一个可被注入到其他组件中的Bean实例。 -// 在本类中用于将taskScheduler和asyncExecutor等方法返回的对象注册为Spring Bean。 import org.springframework.context.annotation.Bean; - -// 导入Spring框架中用于标记一个类是配置类的注解。 -// 被该注解标记的类可以在其中定义各种Bean配置方法,这些方法会被Spring容器在启动时自动识别并执行, -// 用于创建和配置应用程序所需的各种组件。在本类中用于标记该类为Spring配置类,以便进行任务调度相关的配置。 import org.springframework.context.annotation.Configuration; - -// 导入Spring框架中用于配置异步任务执行相关设置的接口。 -// 实现该接口可以定义异步任务执行的线程池等配置信息,在本类中实现该接口来配置异步任务执行的相关参数。 import org.springframework.scheduling.annotation.AsyncConfigurer; - -// 导入Spring框架中用于启用异步任务执行功能的注解。 -// 当在类上添加该注解后,Spring会自动识别并处理类中的异步方法,使其能够在独立的线程中执行。 -// 在本类上添加该注解以启用异步任务执行功能。 import org.springframework.scheduling.annotation.EnableAsync; - -// 导入Spring框架中用于启用任务调度功能的注解。 -// 当在类上添加该注解后,Spring会自动识别并处理类中的定时任务等调度相关的设置,使其能够按照预定的时间执行任务。 -// 在本类上添加该注解以启用任务调度功能。 import org.springframework.scheduling.annotation.EnableScheduling; - -// 导入Spring框架中用于配置任务调度相关设置的接口。 -// 实现该接口可以定义任务调度的线程池、任务注册等配置信息,在本类中实现该接口来配置任务调度的相关参数。 import org.springframework.scheduling.annotation.SchedulingConfigurer; - -// 导入Spring框架中用于执行线程池任务的执行器类。 -// 它可以创建一个线程池,并通过该线程池来执行任务,在本类的asyncExecutor方法中用于创建异步任务执行的线程池。 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; - -// 导入Spring框架中用于调度线程池任务的调度器类。 -// 它可以创建一个线程池,并通过该线程池来调度任务的执行时间,在本类的taskScheduler方法中用于创建定时任务执行的线程池。 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -// 导入Spring框架中用于注册调度任务的类。 -// 通过它可以将需要调度执行的任务注册到相应的调度器中,在本类的configureTasks方法中用于设置任务调度器。 import org.springframework.scheduling.config.ScheduledTaskRegistrar; -// 导入Java标准库中的执行器接口,它定义了执行任务的通用规范, -// 在本类的getAsyncExecutor方法中用于返回异步任务执行的执行器对象。 import java.util.concurrent.Executor; - -// 导入Java标准库中的线程池执行器类,它是实现了线程池功能的具体类, -// 在本类的asyncExecutor方法中用于设置异步任务执行线程池的拒绝策略。 import java.util.concurrent.ThreadPoolExecutor; /** - * 任务调度配置类,主要功能是配置任务调度和异步任务执行相关的设置, - * 包括创建定时任务执行的线程池和异步任务执行的线程池,设置线程池的大小、名称前缀、 - * 等待终止时间、队列容量等参数,同时实现了处理异步未捕获异常的逻辑。 + * 任务调度配置 * @author bool */ -@Log4j2 // 使用Log4j2注解启用日志记录功能,方便在类中记录相关操作的日志信息。 -@Configuration // 使用该注解标记此为Spring配置类,表明这个类是用来进行Spring应用程序的配置工作的。 -@EnableScheduling // 使用该注解启用任务调度功能,使得Spring能够识别并处理类中的定时任务等调度相关设置。 -@EnableAsync // 使用该注解启用异步任务执行功能,使得Spring能够识别并处理类中的异步方法,使其能够在独立的线程中执行。 +@Log4j2 +@Configuration +@EnableScheduling +@EnableAsync public class ScheduledConfig implements SchedulingConfigurer, AsyncConfigurer { /** - * 定义一个方法,并使用@Bean注解将其返回值声明为Spring Bean。 - * 该方法用于创建并返回一个线程池任务调度器(ThreadPoolTaskScheduler)对象,用于定时任务的执行调度。 - * - * @return 返回一个线程池任务调度器(ThreadPoolTaskScheduler)对象,该对象已进行了相关设置(如线程池大小、名称前缀等)。 + * 定时任务使用的线程池 + * @return */ - @Bean(destroyMethod = "shutdown", name = "taskScheduler") // 将该方法返回的线程池任务调度器对象声明为Spring Bean, - // 并指定销毁方法为"shutdown",以便在容器关闭时正确关闭线程池。 - public ThreadPoolTaskScheduler taskScheduler() { - ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); // 创建一个新的线程池任务调度器对象。 - - // 设置线程池的大小,即同时可执行的定时任务数量,这里设置为10,表示线程池最多可同时执行10个定时任务。 + @Bean(destroyMethod = "shutdown", name = "taskScheduler") + public ThreadPoolTaskScheduler taskScheduler(){ + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); - - // 设置线程的名称前缀,用于在日志等场景中方便识别线程所属的任务调度器,这里设置为"task-", - // 生成的线程名称可能类似"task-1"、"task-2"等。 scheduler.setThreadNamePrefix("task-"); - - // 设置线程池在关闭时等待任务完成的时间,单位为秒,这里设置为600秒(10分钟), - // 表示在容器关闭时,线程池会等待正在执行的任务完成,最长等待时间为10分钟。 scheduler.setAwaitTerminationSeconds(600); - - // 设置在关闭时是否等待任务完成,这里设置为true,表示在容器关闭时,线程池会等待所有任务完成后再关闭。 scheduler.setWaitForTasksToCompleteOnShutdown(true); - - // 返回设置好的线程池任务调度器对象,该对象将被Spring容器管理并在定时任务执行场景中被调用进行任务调度。 return scheduler; } /** - * 定义一个方法,并使用@Bean注解将其返回值声明为Spring Bean。 - * 该方法用于创建并返回一个线程池任务执行器(ThreadPoolTaskExecutor)对象,用于异步任务的执行。 - * - * @return 返回一个线程池任务执行器(ThreadPoolTaskExecutor)对象,该对象已进行了相关设置(如核心线程数、队列容量等)。 + * 异步任务执行线程池 + * @return */ - @Bean(name = "asyncExecutor") // 将该方法返回的线程池任务执行器对象声明为Spring Bean,以便Spring容器能够管理和注入到其他需要使用的地方。 + @Bean(name = "asyncExecutor") public ThreadPoolTaskExecutor asyncExecutor() { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 创建一个新的线程池任务执行器对象。 - - // 设置线程池的核心线程数,即线程池始终保持的活跃线程数量,这里设置为10,表示线程池至少会保持10个线程处于活跃状态。 + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); - - // 设置线程池的队列容量,即用于存储等待执行任务的队列大小,这里设置为1000,表示队列最多可容纳1000个等待执行的任务。 executor.setQueueCapacity(1000); - - // 设置线程池中非核心线程的保持活动时间,单位为秒,这里设置为600秒(10分钟), - // 表示当非核心线程空闲时间超过10分钟时,可能会被回收。 executor.setKeepAliveSeconds(600); - - // 设置线程池的最大线程数,即线程池最多可同时拥有的线程数量,这里设置为20,表示线程池最多可扩展到20个线程同时执行任务。 executor.setMaxPoolSize(20); - - // 设置线程的名称前缀,用于在日志等场景中方便识别线程所属的任务执行器,这里设置为"taskExecutor-", - // 生成的线程名称可能类似"taskExecutor-1"、"taskExecutor-2"等。 executor.setThreadNamePrefix("taskExecutor-"); - - // 设置线程池的拒绝策略,这里采用ThreadPoolExecutor.CallerRunsPolicy(), - // 表示当线程池和队列都已满,无法再接受新任务时,会由调用线程来执行该任务,而不是直接抛出异常。 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); - - // 初始化线程池,使其处于可使用状态,完成各项参数的设置和内部资源的初始化。 executor.initialize(); - - // 返回设置好的线程池任务执行器对象,该对象将被Spring容器管理并在异步任务执行场景中被调用进行任务执行。 return executor; } - /** - * 实现SchedulingConfigurer接口的方法,用于配置任务调度相关的设置。 - * 在本方法中,获取之前创建的线程池任务调度器对象,并将其设置到调度任务注册类中, - * 以便后续注册的定时任务能够使用该调度器进行任务调度。 - * - * @param scheduledTaskRegistrar 调度任务注册类对象,用于注册调度任务和设置调度器等操作。 - */ @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { - ThreadPoolTaskScheduler taskScheduler = taskScheduler(); // 获取之前创建的线程池任务调度器对象。 - - // 将获取到的线程池任务调度器对象设置到调度任务注册类中,使得后续注册的定时任务能够使用该调度器进行任务调度。 + ThreadPoolTaskScheduler taskScheduler = taskScheduler(); scheduledTaskRegistrar.setTaskScheduler(taskScheduler); } - /** - * 实现AsyncConfigurer接口的方法,用于返回异步任务执行的执行器对象。 - * 在本方法中,直接返回之前创建的线程池任务执行器对象,该对象将作为异步任务执行的执行器。 - * - * @return 返回异步任务执行的执行器对象,即之前创建的线程池任务执行器对象。 - */ @Override public Executor getAsyncExecutor() { return asyncExecutor(); } - /** - * 实现AsyncConfigurer接口的方法,用于定义异步未捕获异常的处理逻辑。 - * 在本方法中,当异步任务执行出现异常且未在方法内部被捕获时,会通过日志记录异常信息, - * 包括异常对象、执行的方法以及方法的参数等内容。 - * - * @param throwable 异步任务执行过程中抛出的异常对象。 - * @param method 执行异步任务的方法对象。 - * @param objects 执行异步任务的方法的参数对象数组。 - * @return 返回值无实际意义,这里主要是为了符合AsyncUncaughtExceptionHandler接口的定义。 - */ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (throwable, method, objects) -> { - log.error("异步任务执行出现异常, message {}, method {}, params {}", throwable, method, objects); + log.error("异步任务执行出现异常, message {}, emthod {}, params {}", throwable, method, objects); }; } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/config/ShiroConfig.java b/src-源文件/main/java/com/yf/exam/config/ShiroConfig.java index 52d179c..cbb3f38 100644 --- a/src-源文件/main/java/com/yf/exam/config/ShiroConfig.java +++ b/src-源文件/main/java/com/yf/exam/config/ShiroConfig.java @@ -1,133 +1,127 @@ -// 定义包路径,用于存放Shiro配置类 package com.yf.exam.config; -import com.yf.exam.ability.shiro.CNFilterFactoryBean; // 自定义过滤器工厂类 -import com.yf.exam.ability.shiro.ShiroRealm; // 自定义Shiro领域类 -import com.yf.exam.ability.shiro.aop.JwtFilter; // JWT认证过滤器 -import lombok.extern.slf4j.Slf4j; // Lombok日志注解 -import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; // Shiro默认会话存储评估器 -import org.apache.shiro.mgt.DefaultSubjectDAO; // Shiro默认主体DAO -import org.apache.shiro.mgt.SecurityManager; // Shiro安全管理器 -import org.apache.shiro.spring.LifecycleBeanPostProcessor; // Spring生命周期Bean后处理器 -import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; // Shiro授权属性源顾问 -import org.apache.shiro.spring.web.ShiroFilterFactoryBean; // Shiro过滤器工厂类 -import org.apache.shiro.web.mgt.DefaultWebSecurityManager; // Shiro默认Web安全管理器 -import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; // Spring默认顾问自动代理创建器 -import org.springframework.context.annotation.Bean; // Spring Bean注解 -import org.springframework.context.annotation.Configuration; // Spring配置注解 -import org.springframework.context.annotation.DependsOn; // Spring依赖注解 - -import javax.servlet.Filter; // Servlet过滤器接口 -import java.util.HashMap; // Java哈希映射类 -import java.util.LinkedHashMap; // Java链接哈希映射类 -import java.util.Map; // Java映射类 +import com.yf.exam.ability.shiro.CNFilterFactoryBean; +import com.yf.exam.ability.shiro.ShiroRealm; +import com.yf.exam.ability.shiro.aop.JwtFilter; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; +import org.apache.shiro.mgt.DefaultSubjectDAO; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; + +import javax.servlet.Filter; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + /** * Shiro配置类 - * 用于配置和自定义Shiro框架的相关组件,包括安全管理器、过滤器工厂、Realm等。 * @author bool */ -@Slf4j // 使用Lombok注解,启用Log4j2日志 -@Configuration // 使用Spring注解,标记为配置类 +@Slf4j +@Configuration public class ShiroConfig { - /** - * 定义Shiro过滤器链 - * 方法用于配置Shiro过滤器链,定义哪些URL路径需要通过哪些过滤器。 - * - * @param securityManager Shiro安全管理器 - * @return ShiroFilterFactoryBean Shiro过滤器工厂Bean - */ - @Bean("shiroFilterFactoryBean") // 使用Spring注解,声明为Bean,并指定Bean名称 - public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { - CNFilterFactoryBean shiroFilterFactoryBean = new CNFilterFactoryBean(); // 创建自定义过滤器工厂Bean - shiroFilterFactoryBean.setSecurityManager(securityManager); // 设置安全管理器 - - // 定义过滤器链 - Map map = new LinkedHashMap<>(); // 创建拦截器映射 - - // 配置不需要认证的路径 - map.put("/exam/api/sys/user/login", "anon"); // 登录接口不需要认证 - map.put("/exam/api/sys/user/reg", "anon"); // 注册接口不需要认证 - map.put("/exam/api/sys/user/quick-reg", "anon"); // 快速注册接口不需要认证 - - // 配置其他不需要认证的静态资源路径 - map.put("/upload/file/**", "anon"); // 文件上传路径不需要认证 - map.put("/", "anon"); // 根路径不需要认证 - map.put("/v2/**", "anon"); // Swagger路径不需要认证 - map.put("/doc.html", "anon"); // Swagger文档不需要认证 - // ... 省略其他静态资源配置 - - // 添加自定义JWT过滤器 - Map filterMap = new HashMap<>(); // 创建过滤器映射 - filterMap.put("jwt", new JwtFilter()); // 添加JWT过滤器 - shiroFilterFactoryBean.setFilters(filterMap); // 设置过滤器 - - // 设置过滤器链定义 - map.put("/**", "jwt"); // 所有请求都需要通过JWT过滤器 - shiroFilterFactoryBean.setFilterChainDefinitionMap(map); // 设置过滤器链定义 - - return shiroFilterFactoryBean; // 返回Shiro过滤器工厂Bean - } - - /** - * 定义Shiro安全管理器 - * 方法用于配置Shiro安全管理器,设置自定义Realm和禁用Shiro的会话存储。 - * - * @param myRealm 自定义Shiro领域 - * @return DefaultWebSecurityManager Shiro安全管理器 - */ - @Bean("securityManager") // 使用Spring注解,声明为Bean,并指定Bean名称 - public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) { - DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 创建默认Web安全管理器 - securityManager.setRealm(myRealm); // 设置自定义Realm - - // 禁用Shiro的会话存储 - DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); // 创建默认主体DAO - DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); // 创建默认会话存储评估器 - defaultSessionStorageEvaluator.setSessionStorageEnabled(false); // 禁用会话存储 - subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); // 设置会话存储评估器 - securityManager.setSubjectDAO(subjectDAO); // 设置主体DAO - - return securityManager; // 返回安全管理器 - } - - /** - * 定义注解支持 - * 方法用于添加注解支持,使得Shiro的注解可以正常工作。 - * - * @return DefaultAdvisorAutoProxyCreator 顾问自动代理创建器 - */ - @Bean // 使用Spring注解,声明为Bean - @DependsOn("lifecycleBeanPostProcessor") // 依赖于生命周期Bean后处理器 - public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { - DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); // 创建顾问自动代理创建器 - defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); // 设置代理目标类 - return defaultAdvisorAutoProxyCreator; // 返回顾问自动代理创建器 - } - - /** - * 定义生命周期Bean后处理器 - * 方法用于定义Shiro的生命周期Bean后处理器,确保Shiro的组件能够按照预期的生命周期工作。 - * - * @return LifecycleBeanPostProcessor 生命周期Bean后处理器 - */ - @Bean // 使用Spring注解,声明为Bean - public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { - return new LifecycleBeanPostProcessor(); // 返回生命周期Bean后处理器 - } - - /** - * 定义授权属性源顾问 - * 方法用于定义Shiro的授权属性源顾问,用于处理Shiro注解中的权限控制。 - * - * @param securityManager Shiro安全管理器 - * @return AuthorizationAttributeSourceAdvisor 授权属性源顾问 - */ - @Bean // 使用Spring注解,声明为Bean - public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { - AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); // 创建授权属性源顾问 - advisor.setSecurityManager(securityManager); // 设置安全管理器 - return advisor; // 返回授权属性源顾问 - } -} \ No newline at end of file + /** + * Filter Chain定义说明 + * + * 1、一个URL可以配置多个Filter,使用逗号分隔 + * 2、当设置多个过滤器时,全部验证通过,才视为通过 + * 3、部分过滤器可指定参数,如perms,roles + */ + @Bean("shiroFilterFactoryBean") + public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean shiroFilterFactoryBean = new CNFilterFactoryBean(); + shiroFilterFactoryBean.setSecurityManager(securityManager); + // 拦截器 + Map map = new LinkedHashMap<>(); + + // 需要排除的一些接口 + map.put("/exam/api/sys/user/login", "anon"); + map.put("/exam/api/sys/user/reg", "anon"); + map.put("/exam/api/sys/user/quick-reg", "anon"); + + // 获取网站基本信息 + map.put("/exam/api/sys/config/detail", "anon"); + + // 文件读取 + map.put("/upload/file/**", "anon"); + + map.put("/", "anon"); + map.put("/v2/**", "anon"); + map.put("/doc.html", "anon"); + map.put("/**/*.js", "anon"); + map.put("/**/*.css", "anon"); + map.put("/**/*.html", "anon"); + map.put("/**/*.svg", "anon"); + map.put("/**/*.pdf", "anon"); + map.put("/**/*.jpg", "anon"); + map.put("/**/*.png", "anon"); + map.put("/**/*.ico", "anon"); + + // 字体 + map.put("/**/*.ttf", "anon"); + map.put("/**/*.woff", "anon"); + map.put("/**/*.woff2", "anon"); + map.put("/druid/**", "anon"); + map.put("/swagger-ui.html", "anon"); + map.put("/swagger**/**", "anon"); + map.put("/webjars/**", "anon"); + + // 添加自己的过滤器并且取名为jwt + Map filterMap = new HashMap(1); + filterMap.put("jwt", new JwtFilter()); + shiroFilterFactoryBean.setFilters(filterMap); + map.put("/**", "jwt"); + + shiroFilterFactoryBean.setFilterChainDefinitionMap(map); + return shiroFilterFactoryBean; + } + + @Bean("securityManager") + public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(myRealm); + DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); + DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); + defaultSessionStorageEvaluator.setSessionStorageEnabled(false); + subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); + securityManager.setSubjectDAO(subjectDAO); + return securityManager; + } + + /** + * 下面的代码是添加注解支持 + * @return + */ + @Bean + @DependsOn("lifecycleBeanPostProcessor") + public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { + DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); + defaultAdvisorAutoProxyCreator.setUsePrefix(true); + defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor"); + return defaultAdvisorAutoProxyCreator; + } + + @Bean + public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } + + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); + advisor.setSecurityManager(securityManager); + return advisor; + } + +} diff --git a/src-源文件/main/java/com/yf/exam/config/SwaggerConfig.java b/src-源文件/main/java/com/yf/exam/config/SwaggerConfig.java index 038caa6..d4208aa 100644 --- a/src-源文件/main/java/com/yf/exam/config/SwaggerConfig.java +++ b/src-源文件/main/java/com/yf/exam/config/SwaggerConfig.java @@ -1,164 +1,65 @@ -// 定义包路径,用于存放Swagger配置类相关的代码。这个包名表明该类属于特定的项目模块(com.yf.exam)下的config部分, -// 通常用于组织和管理与Swagger配置相关的代码。 package com.yf.exam.config; -// 导入用于启用Swagger Bootstrap UI的注解,Swagger Bootstrap UI是对Swagger UI的一种增强, -// 提供了更友好的界面展示和交互功能,通过该注解可以在项目中启用这一特性。 import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; - -// 导入Swagger用于标注API操作的注解,通过在方法上添加该注解,可以为该方法在Swagger文档中生成详细的操作描述信息, -// 包括方法的功能、参数、返回值等内容,方便开发者和使用者了解API的具体使用方式。 import io.swagger.annotations.ApiOperation; - -// 导入Spring框架中用于将类的属性与配置文件中的属性进行绑定的注解。 -// 通过指定prefix属性,可以将配置文件中以该前缀开头的属性值绑定到类的对应属性上, -// 在本类中用于绑定以"swagger"为前缀的配置属性。 import org.springframework.boot.context.properties.ConfigurationProperties; - -// 导入Spring框架中用于标记一个方法返回值为Spring Bean的注解。 -// 被该注解标记的方法,其返回值会被Spring容器管理,作为一个可被注入到其他组件中的Bean实例。 -// 在本类中用于将examApi、securityScheme等方法返回的对象注册为Spring Bean。 import org.springframework.context.annotation.Bean; - -// 导入Spring框架中用于标记一个类是配置类的注解。 -// 被该注解标记的类可以在其中定义各种Bean配置方法,这些方法会被Spring容器在启动时自动识别并执行, -// 用于创建和配置应用程序所需的各种组件。在本类中用于标记该类为Spring配置类,以便进行Swagger相关的配置。 import org.springframework.context.annotation.Configuration; - -// 导入Swagger用于构建API信息的构建器类,通过该构建器可以方便地设置API的标题、描述、联系人、版本等信息, -// 在本类的apiInfo方法中用于创建并返回包含详细API信息的ApiInfo对象。 import springfox.documentation.builders.ApiInfoBuilder; - -// 导入Swagger用于选择路径的选择器类,通过该选择器可以指定哪些路径下的API需要被Swagger生成文档并展示, -// 在本类的examApi方法中用于选择符合特定路径模式的API。 import springfox.documentation.builders.PathSelectors; - -// 导入Swagger用于选择请求处理器(即包含API方法的类或接口)的选择器类, -// 通过该选择器可以指定哪些请求处理器中的方法需要被Swagger生成文档并展示, -// 在本类的examApi方法中用于选择带有ApiOperation注解的方法所在的请求处理器。 import springfox.documentation.builders.RequestHandlerSelectors; - -// 导入Swagger用于表示API信息的类,该类包含了API的标题、描述、联系人、版本等详细信息, -// 在本类的examApi方法中通过调用apiInfo方法获取该对象并设置到Docket中,以便在Swagger文档中展示这些信息。 import springfox.documentation.service.ApiInfo; - -// 导入Swagger用于表示API密钥的类,用于设置API的授权相关信息,如授权的键名、值的位置(如在请求头中)等, -// 在本类的securityScheme方法中用于创建并返回一个ApiKey对象,用于设置API的授权方案。 import springfox.documentation.service.ApiKey; - -// 导入Swagger用于表示联系人信息的类,通过该类可以设置API的联系人姓名、联系方式、网址等信息, -// 在本类的apiInfo方法中用于创建并返回一个包含联系人信息的Contact对象,并设置到ApiInfo中。 import springfox.documentation.service.Contact; - -// 导入Swagger用于表示安全方案的类,它是一个通用的接口,用于定义不同类型的安全方案, -// 在本类的examApi方法中通过调用securityScheme方法获取具体的安全方案实现(如ApiKey)并设置到Docket中。 import springfox.documentation.service.SecurityScheme; - -// 导入Swagger用于表示文档类型的枚举类,目前主要有SWAGGER_2等类型, -// 在本类的examApi方法中用于指定创建Docket对象时所采用的文档类型为SWAGagger_2。 import springfox.documentation.spi.DocumentationType; - -// 导入Swagger用于生成Swagger文档的核心类,通过该类可以配置各种Swagger相关的设置,如API信息、路径选择、 -// 授权方案等,在本类的examApi方法中用于创建并配置Docket对象,以生成符合项目需求的Swagger文档。 import springfox.documentation.spring.web.plugins.Docket; - -// 导入用于启用Swagger 2的注解,通过在类上添加该注解,可以在项目中启用Swagger 2的功能, -// 使得Swagger能够为项目中的API生成详细的文档并提供交互界面,方便开发者和使用者查看和测试API。 import springfox.documentation.swagger2.annotations.EnableSwagger2; -// 导入Java标准库中的集合类,用于处理集合相关的操作,在本类的examApi方法中用于创建一个包含单个安全方案的列表, -// 以便将安全方案设置到Docket对象中。 import java.util.Collections; /** - * Swagger配置类,主要功能是配置Swagger在项目中的相关设置, - * 包括启用Swagger 2和Swagger Bootstrap UI功能,绑定以"swagger"为前缀的配置属性, - * 创建Docket对象并设置其API信息、分组名称、选择需要生成文档的API方法和路径、设置安全方案等, - * 以生成详细的API文档并提供友好的交互界面,方便对项目中的API进行查看、测试和使用。 + * Swagger配置 * @author bool * @date 2020/8/19 20:53 */ -@Configuration // 使用该注解标记此为Spring配置类,表明这个类是用来进行Spring应用程序的配置工作的。 -@EnableSwagger2 // 使用该注解启用Swagger 2功能,使得Swagger能够为项目中的API生成详细的文档并提供交互界面。 -@EnableSwaggerBootstrapUI // 使用该注解启用Swagger Bootstrap UI功能,提供更友好的界面展示和交互功能。 -@ConfigurationProperties(prefix = "swagger") // 使用该注解将类的属性与以"swagger"为前缀的配置文件中的属性进行绑定, - // 以便在类中可以方便地使用这些配置属性来定制Swagger的设置。 - +@Configuration +@EnableSwagger2 +@EnableSwaggerBootstrapUI +@ConfigurationProperties(prefix = "swagger") public class SwaggerConfig { - /** - * 定义一个方法,并使用@Bean注解将其返回值声明为Spring Bean。 - * 该方法用于创建并返回一个Docket对象,该对象是Swagger生成文档的核心组件, - * 通过对其进行一系列设置,可以定制生成的API文档的内容和展示方式。 - * - * @return 返回一个Docket对象,该对象经过了相关设置,包括API信息、分组名称、选择的API方法和路径、安全方案等。 - */ - @Bean // 将该方法返回的Docket对象声明为Spring Bean,以便Spring容器能够管理和注入到其他需要使用的地方。 - public Docket examApi() { - return new Docket(DocumentationType.SWAGGER_2) // 创建一个新的Docket对象,并指定文档类型为SWAGGER_2, - // 这是目前较为常用的Swagger文档类型,用于生成详细的API文档。 - - .apiInfo(apiInfo()) // 调用apiInfo方法获取包含详细API信息的ApiInfo对象,并设置到Docket对象中, - // 以便在生成的Swagger文档中展示API的标题、描述、联系人、版本等信息。 - - .groupName("考试模块接口") // 设置Docket对象的分组名称为"考试模块接口", - // 这样可以将项目中的API按照不同的模块或功能进行分组展示,方便查看和管理。 - - .select() // 开始选择需要生成文档的API,通过一系列选择器来指定具体的选择条件。 - - .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 使用RequestHandlerSelectors的withMethodAnnotation方法选择带有ApiOperation注解的方法, - // 即只选择那些在方法上标注了ApiOperation注解的API方法进行文档生成, - // 这样可以确保只生成我们希望展示的重要API的文档。 - .paths(PathSelectors.ant("/exam/api/**")) // 使用PathSelectors的ant方法选择符合特定路径模式的API, - // 这里选择的路径模式是"/exam/api/**",表示只选择以"/exam/api/"开头的任意路径下的API进行文档生成, - // 进一步限定了生成文档的API范围,使其更加聚焦于项目中的特定部分(如考试模块相关的API)。 - - .build() // 完成上述选择条件的设置后,调用build方法构建Docket对象,使其生效并包含我们所设置的所有选择条件和信息。 - - .securitySchemes(Collections.singletonList(securityScheme())) // 创建一个包含单个安全方案的列表, - // 通过调用securityScheme方法获取具体的安全方案对象(如ApiKey), - // 并将其添加到列表中,然后设置到Docket对象中, - // 以便在Swagger文档中展示API的授权相关信息,如需要在请求头中传递的授权密钥等。 - - ; + @Bean + public Docket examApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .groupName("考试模块接口") + .select() + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.ant("/exam/api/**")) + .build() + .securitySchemes(Collections.singletonList(securityScheme())); } - /** - * 用于创建并返回一个包含详细API信息的ApiInfo对象的方法。 - * 通过ApiInfoBuilder类可以方便地设置API的标题、描述、联系人、版本等信息。 - * - * @return 返回一个ApiInfo对象,该对象包含了API的标题、描述、联系人、版本等详细信息, - * 用于设置到Docket对象中,以便在Swagger文档中展示。 - */ - private ApiInfo apiInfo() { - return new ApiInfoBuilder().title("考试系统接口") // 使用ApiInfoBuilder的title方法设置API的标题为"考试系统接口", - // 这将在Swagger文档的顶部显著位置展示,让使用者快速了解该API所属的系统。 - - .description("考试系统接口") // 使用ApiInfoBuilder的description方法设置API的描述为"考试系统接口", - // 可以在这里详细描述API的功能、用途、特点等信息,方便使用者进一步了解API的具体情况。 - .contact(new Contact("Van", "https://exam.yfhl.net", "18365918@qq.com")) // 使用ApiInfoBuilder的contact方法创建一个包含联系人信息的Contact对象, - // 并设置联系人姓名为"Van",网址为"https://exam.yfhl.net",邮箱为"18365918@qq.com", - // 这些信息将在Swagger文档中展示,方便使用者在有问题时能够联系到相关人员。 - .version("1.0.0") // 使用ApiInfoBuilder的version方法设置API的版本号为"1.0.0", - // 让使用者了解该API的版本情况,以便在不同版本之间进行对比和选择。 - - .build(); // 完成上述各项信息的设置后,调用build方法构建ApiInfo对象,使其生效并包含我们所设置的所有信息。 + private ApiInfo apiInfo() { + return new ApiInfoBuilder().title("考试系统接口") + .description("考试系统接口") + .contact(new Contact("Van", "https://exam.yfhl.net", "18365918@qq.com")) + .version("1.0.0") + .build(); } + /** - * 用于创建并返回一个表示API密钥的ApiKey对象的方法,该对象用于设置API的授权方案。 - * - * @return 返回一个ApiKey对象,该对象定义了API的授权相关信息,如授权的键名、值的位置(如在请求头中)等, - * 用于设置到Docket对象中,以便在Swagger文档中展示API的授权要求。 + * 授权头部 + * @return */ - @Bean // 将该方法返回的SecurityScheme对象(实际为ApiKey类型)声明为Spring Bean,以便Spring容器能够管理和注入到其他需要使用的地方。 + @Bean SecurityScheme securityScheme() { - return new ApiKey("token", "token", "header"); // 创建一个新的ApiKey对象, - // 第一个参数"token"表示授权的键名,即客户端在请求时需要在指定位置(这里是请求头)传递的键名; - // 第二个参数"token"表示授权的值,这里简单设置为与键名相同,实际应用中可能是根据用户登录等情况生成的授权令牌; - // 第三个参数"header"表示授权的值应该放置的位置,这里指定为在请求头中传递。 + return new ApiKey("token", "token", "header"); } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/controller/ExamController.java b/src-源文件/main/java/com/yf/exam/modules/exam/controller/ExamController.java index 6adeb13..e5583f2 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/controller/ExamController.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/controller/ExamController.java @@ -1,144 +1,151 @@ -package com.yf.exam.modules.exam.controller; // 定义包名,控制器所在的包路径 - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; // 导入MyBatis Plus的查询包装类 -import com.baomidou.mybatisplus.core.metadata.IPage; // 导入MyBatis Plus的分页接口 -import com.yf.exam.core.api.ApiRest; // 导入自定义的API响应类 -import com.yf.exam.core.api.controller.BaseController; // 导入基控制器类 -import com.yf.exam.core.api.dto.BaseIdReqDTO; // 导入基础ID请求数据传输对象 -import com.yf.exam.core.api.dto.BaseIdsReqDTO; // 导入基础IDs请求数据传输对象 -import com.yf.exam.core.api.dto.BaseStateReqDTO; // 导入基础状态请求数据传输对象 -import com.yf.exam.core.api.dto.PagingReqDTO; // 导入分页请求数据传输对象 -import com.yf.exam.modules.exam.dto.ExamDTO; // 导入考试DTO -import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; // 导入考试保存请求DTO -import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; // 导入在线考试响应DTO -import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; // 导入考试审核响应DTO -import com.yf.exam.modules.exam.entity.Exam; // 导入考试实体类 -import com.yf.exam.modules.exam.service.ExamService; // 导入考试服务接口 -import io.swagger.annotations.Api; // 导入Swagger注解 -import io.swagger.annotations.ApiOperation; // 导入Swagger操作注解 -import org.apache.shiro.authz.annotation.RequiresRoles; // 导入Shiro权限注解 -import org.springframework.beans.factory.annotation.Autowired; // 导入Spring的自动注入注解 -import org.springframework.web.bind.annotation.RequestBody; // 导入Spring MVC的请求体注解 -import org.springframework.web.bind.annotation.RequestMapping; // 导入Spring MVC的请求映射注解 -import org.springframework.web.bind.annotation.RequestMethod; // 导入Spring MVC的请求方法注解 -import org.springframework.web.bind.annotation.RestController; // 导入Spring MVC的控制器注解 - -import java.util.Date; // 导入Java的日期类 +package com.yf.exam.modules.exam.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdReqDTO; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.BaseStateReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.entity.Exam; +import com.yf.exam.modules.exam.service.ExamService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; /** - *

- * 考试控制器,处理与考试相关的请求 - *

- * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -@Api(tags={"考试"}) // Swagger注解,定义API的标签 -@RestController // Spring MVC注解,声明这是一个REST控制器 -@RequestMapping("/exam/api/exam/exam") // Spring MVC注解,定义请求的基础路径 -public class ExamController extends BaseController { // 声明控制器类,继承自基控制器 +*

+* 考试控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Api(tags={"考试"}) +@RestController +@RequestMapping("/exam/api/exam/exam") +public class ExamController extends BaseController { @Autowired - private ExamService baseService; // 自动注入考试服务 + private ExamService baseService; /** - * 添加或修改考试信息 - * @param reqDTO 考试保存请求数据传输对象 - * @return ApiRest 返回操作结果 - */ - @RequiresRoles("sa") // Shiro权限注解,要求角色为"sa"(超级管理员) - @ApiOperation(value = "添加或修改") // Swagger注解,定义操作的描述 - @RequestMapping(value = "/save", method = { RequestMethod.POST}) // Spring MVC注解,定义请求的映射和方法 - public ApiRest save(@RequestBody ExamSaveReqDTO reqDTO) { // 定义添加或修改考试的方法 - // 复制参数并保存 - baseService.save(reqDTO); // 调用服务层保存考试信息 - return super.success(); // 返回成功响应 + * 添加或修改 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "添加或修改") + @RequestMapping(value = "/save", method = { RequestMethod.POST}) + public ApiRest save(@RequestBody ExamSaveReqDTO reqDTO) { + //复制参数 + baseService.save(reqDTO); + return super.success(); } /** - * 批量删除考试 - * @param reqDTO 包含要删除的考试ID列表 - * @return ApiRest 返回操作结果 - */ - @RequiresRoles("sa") // Shiro权限注解,要求角色为"sa"(超级管理员) - @ApiOperation(value = "批量删除") // Swagger注解,定义操作的描述 - @RequestMapping(value = "/delete", method = { RequestMethod.POST}) // Spring MVC注解,定义请求的映射和方法 - public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { // 定义批量删除考试的方法 - // 根据ID删除考试 - baseService.removeByIds(reqDTO.getIds()); // 调用服务层根据ID列表删除考试 - return super.success(); // 返回成功响应 + * 批量删除 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "批量删除") + @RequestMapping(value = "/delete", method = { RequestMethod.POST}) + public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { + //根据ID删除 + baseService.removeByIds(reqDTO.getIds()); + return super.success(); } /** - * 查找考试详情 - * @param reqDTO 包含考试ID - * @return ApiRest 返回考试详情 - */ - @ApiOperation(value = "查找详情") // Swagger注解,定义操作的描述 - @RequestMapping(value = "/detail", method = { RequestMethod.POST}) // Spring MVC注解,定义请求的映射和方法 - public ApiRest find(@RequestBody BaseIdReqDTO reqDTO) { // 定义查找考试详情的方法 - ExamSaveReqDTO dto = baseService.findDetail(reqDTO.getId()); // 调用服务层查找考试详情 - return super.success(dto); // 返回成功响应和考试详情 + * 查找详情 + * @param reqDTO + * @return + */ + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/detail", method = { RequestMethod.POST}) + public ApiRest find(@RequestBody BaseIdReqDTO reqDTO) { + ExamSaveReqDTO dto = baseService.findDetail(reqDTO.getId()); + return super.success(dto); } /** - * 更新考试状态 - * @param reqDTO 包含考试ID和新状态 - * @return ApiRest 返回操作结果 + * 查找详情 + * @param reqDTO + * @return */ - @RequiresRoles("sa") // Shiro权限注解,要求角色为"sa"(超级管理员) - @ApiOperation(value = "更新考试状态") // Swagger注解,定义操作的描述 - @RequestMapping(value = "/state", method = { RequestMethod.POST}) // Spring MVC注解,定义请求的映射和方法 - public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) { // 定义更新考试状态的方法 - // 创建查询条件 + @RequiresRoles("sa") + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/state", method = { RequestMethod.POST}) + public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) { + QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().in(Exam::getId, reqDTO.getIds()); // 构造查询条件,查询指定ID的考试 - Exam exam = new Exam(); // 创建考试实体 - exam.setState(reqDTO.getState()); // 设置新状态 - exam.setUpdateTime(new Date()); // 设置更新时间为当前时间 + wrapper.lambda().in(Exam::getId, reqDTO.getIds()); + Exam exam = new Exam(); + exam.setState(reqDTO.getState()); + exam.setUpdateTime(new Date()); - baseService.update(exam, wrapper); // 调用服务层更新考试状态 - return super.success(); // 返回成功响应 + baseService.update(exam, wrapper); + return super.success(); } + /** - * 分页查找考试 - * @param reqDTO 分页请求数据传输对象 - * @return ApiRest> 返回分页考试列表 + * 分页查找 + * @param reqDTO + * @return */ - @ApiOperation(value = "考试视角") // Swagger注解,定义操作的描述 - @RequestMapping(value = "/online-paging", method = { RequestMethod.POST}) // Spring MVC注解,定义请求的映射和方法 - public ApiRest> myPaging(@RequestBody PagingReqDTO reqDTO) { // 定义分页查找考试的方法 - // 分页查询并转换 - IPage page = baseService.onlinePaging(reqDTO); // 调用服务层进行分页查询 - return super.success(page); // 返回成功响应和分页结果 + @ApiOperation(value = "考试视角") + @RequestMapping(value = "/online-paging", method = { RequestMethod.POST}) + public ApiRest> myPaging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.onlinePaging(reqDTO); + return super.success(page); } /** - * 分页查找考试 - * @param reqDTO 分页请求数据传输对象 - * @return ApiRest> 返回分页考试列表 - */ - @RequiresRoles("sa") // Shiro权限注解,要求角色为"sa"(超级管理员) - @ApiOperation(value = "分页查找") // Swagger注解,定义操作的描述 - @RequestMapping(value = "/paging", method = { RequestMethod.POST}) // Spring MVC注解,定义请求的映射和方法 - public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { // 定义分页查找考试的方法 - // 分页查询并转换 - IPage page = baseService.paging(reqDTO); // 调用服务层进行分页查询 - return super.success(page); // 返回成功响应和分页结果 + * 分页查找 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + + return super.success(page); } + /** - * 分页查找待阅试卷 - * @param reqDTO 分页请求数据传输对象 - * @return ApiRest> 返回分页待阅试卷列表 + * 分页查找 + * @param reqDTO + * @return */ - @RequiresRoles("sa") // Shiro权限注解,要求角色为"sa"(超级管理员) - @ApiOperation(value = "待阅试卷") // Swagger注解,定义操作的描述 - @RequestMapping(value = "/review-paging", method = { RequestMethod.POST}) // Spring MVC注解,定义请求的映射和方法 - public ApiRest> reviewPaging(@RequestBody PagingReqDTO reqDTO) { // 定义分页查找待阅试卷的方法 - // 分页查询并转换 - IPage page = baseService.reviewPaging(reqDTO); // 调用服务层进行分页查询 - return super.success(page); // 返回成功响应和分页结果 + @RequiresRoles("sa") + @ApiOperation(value = "待阅试卷") + @RequestMapping(value = "/review-paging", method = { RequestMethod.POST}) + public ApiRest> reviewPaging(@RequestBody PagingReqDTO reqDTO) { + //分页查询并转换 + IPage page = baseService.reviewPaging(reqDTO); + return super.success(page); } + + } diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDTO.java b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDTO.java index 8f9b69e..90f86f1 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDTO.java @@ -1,93 +1,101 @@ -package com.yf.exam.modules.exam.dto; // 定义包名,DTO类所在的包路径 +package com.yf.exam.modules.exam.dto; -import com.fasterxml.jackson.annotation.JsonFormat; // 导入Jackson库的注解,用于格式化日期 -import com.yf.exam.modules.paper.enums.ExamState; // 导入考试状态枚举 -import io.swagger.annotations.ApiModel; // 导入Swagger注解,用于描述模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger注解,用于描述模型属性 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 -import org.springframework.format.annotation.DateTimeFormat; // 导入Spring的日期格式化注解 +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yf.exam.modules.paper.enums.ExamState; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; -import java.io.Serializable; // 导入Java的序列化接口,用于确保对象可以被序列化 -import java.util.Date; // 导入Java的日期类 +import java.io.Serializable; +import java.util.Date; /** - *

- * 考试数据传输类,封装考试的基本信息 - *

- * 此类用于封装考试的基本信息,以便在应用程序中传输。 - * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@ApiModel(value="考试", description="考试") // Swagger注解,描述这个类的用途 -public class ExamDTO implements Serializable { // 声明类,实现Serializable接口以确保可以序列化 +*

+* 考试数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="考试", description="考试") +public class ExamDTO implements Serializable { - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的版本唯一性 - @ApiModelProperty(value = "ID", required=true) // Swagger注解,描述字段的用途和是否必填 - private String id; // 考试ID + private static final long serialVersionUID = 1L; - @ApiModelProperty(value = "考试名称", required=true) // Swagger注解,描述字段的用途和是否必填 - private String title; // 考试名称 - @ApiModelProperty(value = "考试描述", required=true) // Swagger注解,描述字段的用途和是否必填 - private String content; // 考试描述 + @ApiModelProperty(value = "ID", required=true) + private String id; - @ApiModelProperty(value = "1公开2部门3定员", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer openType; // 开放类型,1表示公开,2表示部门,3表示定员 + @ApiModelProperty(value = "考试名称", required=true) + private String title; - @ApiModelProperty(value = "考试状态", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer state; // 考试状态 + @ApiModelProperty(value = "考试描述", required=true) + private String content; - @ApiModelProperty(value = "是否限时", required=true) // Swagger注解,描述字段的用途和是否必填 - private Boolean timeLimit; // 是否限时 + @ApiModelProperty(value = "1公开2部门3定员", required=true) + private Integer openType; - @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") // Jackson注解,格式化日期 - @DateTimeFormat(pattern = "yyyy-MM-dd") // Spring注解,格式化日期 - @ApiModelProperty(value = "开始时间", required=true) // Swagger注解,描述字段的用途和是否必填 - private Date startTime; // 考试开始时间 + @ApiModelProperty(value = "考试状态", required=true) + private Integer state; - @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") // Jackson注解,格式化日期 - @DateTimeFormat(pattern = "yyyy-MM-dd") // Spring注解,格式化日期 - @ApiModelProperty(value = "结束时间", required=true) // Swagger注解,描述字段的用途和是否必填 - private Date endTime; // 考试结束时间 + @ApiModelProperty(value = "是否限时", required=true) + private Boolean timeLimit; - @ApiModelProperty(value = "创建时间", required=true) // Swagger注解,描述字段的用途和是否必填 - private Date createTime; // 创建时间 + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "开始时间", required=true) + private Date startTime; - @ApiModelProperty(value = "更新时间", required=true) // Swagger注解,描述字段的用途和是否必填 - private Date updateTime; // 更新时间 + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "结束时间", required=true) + private Date endTime; + + @ApiModelProperty(value = "创建时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "更新时间", required=true) + private Date updateTime; + + @ApiModelProperty(value = "总分数", required=true) + private Integer totalScore; + + @ApiModelProperty(value = "总时长(分钟)", required=true) + private Integer totalTime; + + @ApiModelProperty(value = "及格分数", required=true) + private Integer qualifyScore; - @ApiModelProperty(value = "总分数", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer totalScore; // 总分数 - @ApiModelProperty(value = "总时长(分钟)", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer totalTime; // 总时长(分钟) - @ApiModelProperty(value = "及格分数", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer qualifyScore; // 及格分数 /** - * 判断考试状态 - * 根据当前时间和考试的限时设置,返回考试的实际状态。 - * - * @return 当前考试状态 + * 是否结束 + * @return */ - public Integer getState() { - if (this.timeLimit != null && this.timeLimit) { - if (System.currentTimeMillis() < startTime.getTime()) { - return ExamState.READY_START; // 如果当前时间小于开始时间,状态为准备开始 + public Integer getState(){ + + if(this.timeLimit!=null && this.timeLimit){ + + if(System.currentTimeMillis() < startTime.getTime() ){ + return ExamState.READY_START; } - if (System.currentTimeMillis() > endTime.getTime()) { - return ExamState.OVERDUE; // 如果当前时间大于结束时间,状态为已过期 + + if(System.currentTimeMillis() > endTime.getTime()){ + return ExamState.OVERDUE; } - if (System.currentTimeMillis() > startTime.getTime() + + if(System.currentTimeMillis() > startTime.getTime() && System.currentTimeMillis() < endTime.getTime() - && !ExamState.DISABLED.equals(this.state)) { - return ExamState.ENABLE; // 如果当前时间在开始时间和结束时间之间,并且状态不是禁用,状态为正在进行 + && !ExamState.DISABLED.equals(this.state)){ + return ExamState.ENABLE; } + } - return this.state; // 如果不满足上述条件,返回当前状态 + + return this.state; } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDepartDTO.java b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDepartDTO.java index 7710955..d0e54b7 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDepartDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamDepartDTO.java @@ -1,32 +1,33 @@ -package com.yf.exam.modules.exam.dto; // 定义包名,DTO类所在的包路径 +package com.yf.exam.modules.exam.dto; -import io.swagger.annotations.ApiModel; // 导入Swagger注解,用于描述模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger注解,用于描述模型属性 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; -import java.io.Serializable; // 导入Java的序列化接口,用于确保对象可以被序列化 +import java.io.Serializable; /** - *

- * 考试部门数据传输类,封装考试部门相关信息 - *

- * 此类用于封装与考试部门相关的数据,以便在应用程序中传输。 - * - * @author 聪明笨狗 - * @since 2020-09-03 17:24 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@ApiModel(value="考试部门", description="考试部门") // Swagger注解,描述这个类的用途 -public class ExamDepartDTO implements Serializable { // 声明类,实现Serializable接口以确保可以序列化 +*

+* 考试部门数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +@Data +@ApiModel(value="考试部门", description="考试部门") +public class ExamDepartDTO implements Serializable { - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的版本唯一性 - - @ApiModelProperty(value = "ID", required=true) // Swagger注解,描述字段的用途和是否必填 - private String id; // 唯一标识符,标识考试部门记录的ID - - @ApiModelProperty(value = "考试ID", required=true) // Swagger注解,描述字段的用途和是否必填 - private String examId; // 考试的唯一标识符,关联到具体的考试 - - @ApiModelProperty(value = "部门ID", required=true) // Swagger注解,描述字段的用途和是否必填 - private String departId; // 部门的唯一标识符,关联到具体的部门 -} \ No newline at end of file + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "考试ID", required=true) + private String examId; + + @ApiModelProperty(value = "部门ID", required=true) + private String departId; + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamRepoDTO.java b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamRepoDTO.java index 5cbc8a4..7244e9a 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamRepoDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ExamRepoDTO.java @@ -1,50 +1,51 @@ -package com.yf.exam.modules.exam.dto; // 定义包名,DTO类所在的包路径 +package com.yf.exam.modules.exam.dto; -import io.swagger.annotations.ApiModel; // 导入Swagger注解,用于描述模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger注解,用于描述模型属性 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; -import java.io.Serializable; // 导入Java的序列化接口,用于确保对象可以被序列化 +import java.io.Serializable; /** - *

- * 考试题库数据传输类,封装考试题库相关信息 - *

- * 此类用于封装考试题库的数据,以便在应用程序中传输。 - * - * @author 聪明笨狗 - * @since 2020-09-05 11:14 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@ApiModel(value="考试题库", description="考试题库") // Swagger注解,描述这个类的用途 -public class ExamRepoDTO implements Serializable { // 声明类,实现Serializable接口以确保可以序列化 - - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的版本唯一性 - - @ApiModelProperty(value = "ID", required=true) // Swagger注解,描述字段的用途和是否必填 - private String id; // 题库ID,唯一标识符 - - @ApiModelProperty(value = "考试ID", required=true) // Swagger注解,描述字段的用途和是否必填 - private String examId; // 关联的考试ID,标识与哪个考试相关联 - - @ApiModelProperty(value = "题库ID", required=true) // Swagger注解,描述字段的用途和是否必填 - private String repoId; // 题库ID,标识具体的题库 - - @ApiModelProperty(value = "单选题数量", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer radioCount; // 单选题数量,表示题库中单选题的总数 - - @ApiModelProperty(value = "单选题分数", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer radioScore; // 单选题分数,表示单选题的总分 - - @ApiModelProperty(value = "多选题数量", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer multiCount; // 多选题数量,表示题库中多选题的总数 - - @ApiModelProperty(value = "多选题分数", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer multiScore; // 多选题分数,表示多选题的总分 - - @ApiModelProperty(value = "判断题数量", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer judgeCount; // 判断题数量,表示题库中判断题的总数 - - @ApiModelProperty(value = "判断题分数", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer judgeScore; // 判断题分数,表示判断题的总分 -} \ No newline at end of file +*

+* 考试题库数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Data +@ApiModel(value="考试题库", description="考试题库") +public class ExamRepoDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "考试ID", required=true) + private String examId; + + @ApiModelProperty(value = "题库ID", required=true) + private String repoId; + + @ApiModelProperty(value = "单选题数量", required=true) + private Integer radioCount; + + @ApiModelProperty(value = "单选题分数", required=true) + private Integer radioScore; + + @ApiModelProperty(value = "多选题数量", required=true) + private Integer multiCount; + + @ApiModelProperty(value = "多选题分数", required=true) + private Integer multiScore; + + @ApiModelProperty(value = "判断题数量", required=true) + private Integer judgeCount; + + @ApiModelProperty(value = "判断题分数", required=true) + private Integer judgeScore; + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ext/ExamRepoExtDTO.java b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ext/ExamRepoExtDTO.java index e1887c3..a566fec 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/dto/ext/ExamRepoExtDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/dto/ext/ExamRepoExtDTO.java @@ -1,30 +1,32 @@ -package com.yf.exam.modules.exam.dto.ext; // 定义包名,DTO扩展类所在的包路径 +package com.yf.exam.modules.exam.dto.ext; -import com.yf.exam.modules.exam.dto.ExamRepoDTO; // 导入考试题库数据传输类 -import io.swagger.annotations.ApiModel; // 导入Swagger注解,用于描述模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger注解,用于描述模型属性 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 +import com.yf.exam.modules.exam.dto.ExamRepoDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; /** - *

- * 考试题库数据传输类的扩展,包含额外的题库信息 - *

- * - * @author 聪明笨狗 - * @since 2020-09-05 11:14 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@ApiModel(value="考试题库扩展响应类", description="考试题库扩展响应类") // Swagger注解,描述这个类的用途 -public class ExamRepoExtDTO extends ExamRepoDTO { // 声明类,继承自ExamRepoDTO +*

+* 考试题库数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Data +@ApiModel(value="考试题库扩展响应类", description="考试题库扩展响应类") +public class ExamRepoExtDTO extends ExamRepoDTO { - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制 + private static final long serialVersionUID = 1L; - @ApiModelProperty(value = "单选题总量", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer totalRadio; // 单选题总量 - - @ApiModelProperty(value = "多选题总量", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer totalMulti; // 多选题总量 - - @ApiModelProperty(value = "判断题总量", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer totalJudge; // 判断题总量 + + @ApiModelProperty(value = "单选题总量", required=true) + private Integer totalRadio; + + @ApiModelProperty(value = "多选题总量", required=true) + private Integer totalMulti; + + @ApiModelProperty(value = "判断题总量", required=true) + private Integer totalJudge; + } diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/dto/request/ExamSaveReqDTO.java b/src-源文件/main/java/com/yf/exam/modules/exam/dto/request/ExamSaveReqDTO.java index aa62266..5c1a95b 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/dto/request/ExamSaveReqDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/dto/request/ExamSaveReqDTO.java @@ -1,30 +1,32 @@ -package com.yf.exam.modules.exam.dto.request; // 定义包名,请求DTO类所在的包路径 +package com.yf.exam.modules.exam.dto.request; -import com.yf.exam.modules.exam.dto.ExamDTO; // 导入基础考试DTO类 -import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; // 导入考试题库扩展DTO类 -import io.swagger.annotations.ApiModel; // 导入Swagger注解,用于描述模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger注解,用于描述模型属性 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; -import java.util.List; // 导入Java的List接口,用于定义列表类型的属性 +import java.util.List; /** - *

- * 考试保存请求类,封装考试保存请求的相关信息 - *

- * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@ApiModel(value="考试保存请求类", description="考试保存请求类") // Swagger注解,描述这个类的用途 -public class ExamSaveReqDTO extends ExamDTO { // 声明类,继承自ExamDTO - - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制 - - @ApiModelProperty(value = "题库列表", required=true) // Swagger注解,描述字段的用途和是否必填 - private List repoList; // 题库列表,存储与考试相关的题库信息 - - @ApiModelProperty(value = "考试部门列表", required=true) // Swagger注解,描述字段的用途和是否必填 - private List departIds; // 考试部门ID列表,存储与考试相关的部门ID -} \ No newline at end of file +*

+* 考试保存请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="考试保存请求类", description="考试保存请求类") +public class ExamSaveReqDTO extends ExamDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "题库列表", required=true) + private List repoList; + + @ApiModelProperty(value = "考试部门列表", required=true) + private List departIds; + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamOnlineRespDTO.java b/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamOnlineRespDTO.java index ec344db..edbc5ce 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamOnlineRespDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamOnlineRespDTO.java @@ -1,21 +1,22 @@ -package com.yf.exam.modules.exam.dto.response; // 定义包名,响应DTO类所在的包路径 +package com.yf.exam.modules.exam.dto.response; -import com.yf.exam.modules.exam.dto.ExamDTO; // 导入基础考试DTO类 -import io.swagger.annotations.ApiModel; // 导入Swagger注解,用于描述模型 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 +import com.yf.exam.modules.exam.dto.ExamDTO; +import io.swagger.annotations.ApiModel; +import lombok.Data; /** - *

- * 在线考试分页响应类,封装在线考试的分页响应信息 - *

- * 此类用于封装在线考试相关的分页数据,提供给前端展示。 - * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@ApiModel(value="在线考试分页响应类", description="在线考试分页响应类") // Swagger注解,描述这个类的用途 -public class ExamOnlineRespDTO extends ExamDTO { // 声明类,继承自ExamDTO +*

+* 考试分页响应类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="在线考试分页响应类", description="在线考试分页响应类") +public class ExamOnlineRespDTO extends ExamDTO { - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的版本唯一性 -} \ No newline at end of file + private static final long serialVersionUID = 1L; + + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamReviewRespDTO.java b/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamReviewRespDTO.java index 68d4236..7d9ad36 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamReviewRespDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/dto/response/ExamReviewRespDTO.java @@ -1,28 +1,31 @@ -package com.yf.exam.modules.exam.dto.response; // 定义包名,响应DTO类所在的包路径 +package com.yf.exam.modules.exam.dto.response; -import com.yf.exam.modules.exam.dto.ExamDTO; // 导入基础考试DTO类 -import io.swagger.annotations.ApiModel; // 导入Swagger注解,用于描述模型 -import io.swagger.annotations.ApiModelProperty; // 导入Swagger注解,用于描述模型属性 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 +import com.yf.exam.modules.exam.dto.ExamDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; /** - *

- * 阅卷分页响应类,封装阅卷相关的分页响应信息 - *

- * 此类用于封装阅卷相关的分页数据,提供给前端展示。 - * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@ApiModel(value="阅卷分页响应类", description="阅卷分页响应类") // Swagger注解,描述这个类的用途 -public class ExamReviewRespDTO extends ExamDTO { // 声明类,继承自ExamDTO - - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的版本唯一性 - - @ApiModelProperty(value = "考试人数", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer examUser; // 考试人数,表示参与考试的用户总数 - - @ApiModelProperty(value = "待阅试卷", required=true) // Swagger注解,描述字段的用途和是否必填 - private Integer unreadPaper; // 待阅试卷数量,表示尚未被阅卷的试卷数量 -} \ No newline at end of file +*

+* 考试分页响应类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="阅卷分页响应类", description="阅卷分页响应类") +public class ExamReviewRespDTO extends ExamDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "考试人数", required=true) + private Integer examUser; + + @ApiModelProperty(value = "待阅试卷", required=true) + private Integer unreadPaper; + + + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/entity/Exam.java b/src-源文件/main/java/com/yf/exam/modules/exam/entity/Exam.java index cd2d284..e198c69 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/entity/Exam.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/entity/Exam.java @@ -1,61 +1,100 @@ -package com.yf.exam.modules.exam.entity; // 定义包名,实体类所在的包路径 +package com.yf.exam.modules.exam.entity; -import com.baomidou.mybatisplus.annotation.IdType; // 导入MyBatis Plus的ID类型注解 -import com.baomidou.mybatisplus.annotation.TableField; // 导入MyBatis Plus的表字段注解 -import com.baomidou.mybatisplus.annotation.TableId; // 导入MyBatis Plus的表ID注解 -import com.baomidou.mybatisplus.annotation.TableName; // 导入MyBatis Plus的表名注解 -import com.baomidou.mybatisplus.extension.activerecord.Model; // 导入MyBatis Plus的模型类 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 -import java.util.Date; // 导入Java的日期类 +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import java.util.Date; /** - *

- * 考试实体类,封装考试的基本信息 - *

- * 此类用于数据库操作,映射考试表的字段。 - * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@TableName("el_exam") // MyBatis Plus注解,指定这个实体类对应的数据库表名 -public class Exam extends Model { // 声明类,继承自MyBatis Plus的Model类,用于数据库操作 - - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的唯一性 - - @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定这个字段为表的主键,类型为自增ID - private String id; // 考试ID,唯一标识符 - - private String title; // 考试名称,描述考试的标题 - - private String content; // 考试描述,详细描述考试的内容 - - @TableField("open_type") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Integer openType; // 开放类型,表示考试的开放范围(1公开,2部门,3定员) - - private Integer state; // 考试状态,表示考试的当前状态(如:未开始、进行中、已结束) - - @TableField("time_limit") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Boolean timeLimit; // 是否限时,表示考试是否有时间限制 - - @TableField("start_time") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Date startTime; // 开始时间,表示考试的开始时间 - - @TableField("end_time") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Date endTime; // 结束时间,表示考试的结束时间 - - @TableField("create_time") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Date createTime; // 创建时间,表示记录的创建时间 - - @TableField("update_time") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Date updateTime; // 更新时间,表示记录的最后更新时间 - - @TableField("total_score") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Integer totalScore; // 总分数,表示考试的总分 - - @TableField("total_time") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Integer totalTime; // 总时长(分钟),表示考试的总时长 - - @TableField("qualify_score") // MyBatis Plus注解,指定这个字段在数据库表中的列名 - private Integer qualifyScore; // 及格分数,表示考试的及格分数线 -} \ No newline at end of file +*

+* 考试实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@TableName("el_exam") +public class Exam extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 考试名称 + */ + private String title; + + /** + * 考试描述 + */ + private String content; + + /** + * 1公开2部门3定员 + */ + @TableField("open_type") + private Integer openType; + + /** + * 考试状态 + */ + private Integer state; + + /** + * 是否限时 + */ + @TableField("time_limit") + private Boolean timeLimit; + + /** + * 开始时间 + */ + @TableField("start_time") + private Date startTime; + + /** + * 结束时间 + */ + @TableField("end_time") + private Date endTime; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 总分数 + */ + @TableField("total_score") + private Integer totalScore; + + /** + * 总时长(分钟) + */ + @TableField("total_time") + private Integer totalTime; + + /** + * 及格分数 + */ + @TableField("qualify_score") + private Integer qualifyScore; + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamDepart.java b/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamDepart.java index b39d71c..19a4238 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamDepart.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamDepart.java @@ -1,47 +1,42 @@ -package com.yf.exam.modules.exam.entity; // 定义包名,实体类所在的包路径 +package com.yf.exam.modules.exam.entity; -import com.baomidou.mybatisplus.annotation.IdType; // 导入MyBatis Plus的ID类型注解 -import com.baomidou.mybatisplus.annotation.TableField; // 导入MyBatis Plus的表字段注解 -import com.baomidou.mybatisplus.annotation.TableId; // 导入MyBatis Plus的表ID注解 -import com.baomidou.mybatisplus.annotation.TableName; // 导入MyBatis Plus的表名注解 -import com.baomidou.mybatisplus.extension.activerecord.Model; // 导入MyBatis Plus的模型类 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 - -import java.io.Serializable; // 导入Java的序列化接口,用于确保对象可以被序列化 +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; /** - *

- * 考试部门实体类,封装考试部门的基本信息 - *

- * 此类用于数据库操作,映射考试部门表的字段。 - * - * @author 聪明笨狗 - * @since 2020-09-03 17:24 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@TableName("el_exam_depart") // MyBatis Plus注解,指定这个实体类对应的数据库表名 -public class ExamDepart extends Model { // 声明类,继承自MyBatis Plus的Model类,用于数据库操作 - - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的唯一性 +*

+* 考试部门实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +@Data +@TableName("el_exam_depart") +public class ExamDepart extends Model { + private static final long serialVersionUID = 1L; + /** - * ID - * 唯一标识符,用于标识考试部门记录。 - */ - @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定这个字段为表的主键,类型为自增ID + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) private String id; /** - * 考试ID - * 关联的考试ID,标识这个部门所属的考试。 - */ - @TableField("exam_id") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 考试ID + */ + @TableField("exam_id") private String examId; /** - * 部门ID - * 关联的部门ID,标识参与考试的部门。 - */ - @TableField("depart_id") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 部门ID + */ + @TableField("depart_id") private String departId; -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamRepo.java b/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamRepo.java index 5249056..3884051 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamRepo.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/entity/ExamRepo.java @@ -1,87 +1,78 @@ -package com.yf.exam.modules.exam.entity; // 定义包名,实体类所在的包路径 +package com.yf.exam.modules.exam.entity; -import com.baomidou.mybatisplus.annotation.IdType; // 导入MyBatis Plus的ID类型注解 -import com.baomidou.mybatisplus.annotation.TableField; // 导入MyBatis Plus的表字段注解 -import com.baomidou.mybatisplus.annotation.TableId; // 导入MyBatis Plus的表ID注解 -import com.baomidou.mybatisplus.annotation.TableName; // 导入MyBatis Plus的表名注解 -import com.baomidou.mybatisplus.extension.activerecord.Model; // 导入MyBatis Plus的模型类 -import lombok.Data; // 导入Lombok注解,用于简化数据类的编写 +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; /** - *

- * 考试题库实体类,封装考试题库的基本信息 - *

- * 此类用于数据库操作,映射考试题库表的字段。 - * - * @author 聪明笨狗 - * @since 2020-09-05 11:14 - */ -@Data // Lombok注解,标记这个类为数据类,自动生成getter和setter方法 -@TableName("el_exam_repo") // MyBatis Plus注解,指定这个实体类对应的数据库表名 -public class ExamRepo extends Model { // 声明类,继承自MyBatis Plus的Model类,用于数据库操作 - - private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制,确保类的唯一性 +*

+* 考试题库实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Data +@TableName("el_exam_repo") +public class ExamRepo extends Model { + private static final long serialVersionUID = 1L; + /** - * ID - * 唯一标识符,用于标识考试题库记录。 - */ - @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定这个字段为表的主键,类型为自增ID + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) private String id; /** - * 考试ID - * 关联的考试ID,标识这个题库所属的考试。 - */ - @TableField("exam_id") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 考试ID + */ + @TableField("exam_id") private String examId; /** - * 题库ID - * 关联的题库ID,标识具体的题库。 - */ - @TableField("repo_id") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 题库ID + */ + @TableField("repo_id") private String repoId; /** - * 单选题数量 - * 题库中包含的单选题数量。 - */ - @TableField("radio_count") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 单选题数量 + */ + @TableField("radio_count") private Integer radioCount; /** - * 单选题分数 - * 单选题的总分。 - */ - @TableField("radio_score") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 单选题分数 + */ + @TableField("radio_score") private Integer radioScore; /** - * 多选题数量 - * 题库中包含的多选题数量。 - */ - @TableField("multi_count") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 多选题数量 + */ + @TableField("multi_count") private Integer multiCount; /** - * 多选题分数 - * 多选题的总分。 - */ - @TableField("multi_score") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 多选题分数 + */ + @TableField("multi_score") private Integer multiScore; /** - * 判断题数量 - * 题库中包含的判断题数量。 - */ - @TableField("judge_count") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 判断题数量 + */ + @TableField("judge_count") private Integer judgeCount; /** - * 判断题分数 - * 判断题的总分。 - */ - @TableField("judge_score") // MyBatis Plus注解,指定这个字段在数据库表中的列名 + * 判断题分数 + */ + @TableField("judge_score") private Integer judgeScore; -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamDepartMapper.java b/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamDepartMapper.java index 60c30d2..0c1f39a 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamDepartMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamDepartMapper.java @@ -1,17 +1,15 @@ -package com.yf.exam.modules.exam.mapper; // 定义包名,Mapper接口所在的包路径 - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入MyBatis Plus的基础Mapper接口 -import com.yf.exam.modules.exam.entity.ExamDepart; // 导入考试部门实体类 +package com.yf.exam.modules.exam.mapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.exam.entity.ExamDepart; /** - *

- * 考试部门Mapper,提供考试部门的数据库操作接口 - *

- * 此类是一个Mapper接口,继承自MyBatis Plus的BaseMapper,用于定义针对考试部门表的数据库操作。 - * - * @author 聪明笨狗 - * @since 2020-09-03 17:24 - */ -public interface ExamDepartMapper extends BaseMapper { // 声明接口,继承自BaseMapper并指定操作的实体类为ExamDepart - // 继承BaseMapper,提供基本的CRUD操作(创建、读取、更新、删除) -} \ No newline at end of file +*

+* 考试部门Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +public interface ExamDepartMapper extends BaseMapper { + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamMapper.java b/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamMapper.java index f4908e8..1ce4504 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamMapper.java @@ -1,52 +1,45 @@ -package com.yf.exam.modules.exam.mapper; // 定义包名,Mapper接口所在的包路径 +package com.yf.exam.modules.exam.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入MyBatis Plus的基础Mapper接口 -import com.baomidou.mybatisplus.core.metadata.IPage; // 导入MyBatis Plus的分页结果接口 -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入MyBatis Plus的分页对象 -import com.yf.exam.modules.exam.dto.ExamDTO; // 导入考试DTO -import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; // 导入阅卷分页响应DTO -import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; // 导入在线考试分页响应DTO -import com.yf.exam.modules.exam.entity.Exam; // 导入考试实体类 -import org.apache.ibatis.annotations.Param; // 导入MyBatis的参数注解 +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.entity.Exam; +import org.apache.ibatis.annotations.Param; /** - *

- * 考试Mapper,提供考试的数据库操作接口 - *

- * 此类是一个Mapper接口,继承自MyBatis Plus的BaseMapper,用于定义针对考试表的数据库操作。 - * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -public interface ExamMapper extends BaseMapper { // 声明接口,继承自BaseMapper并指定操作的实体类为Exam +*

+* 考试Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +public interface ExamMapper extends BaseMapper { /** * 查找分页内容 - * 方法用于根据给定的分页对象和查询条件,查询考试数据的分页结果。 - * - * @param page 分页对象,包含分页信息,如当前页码和每页大小 - * @param query 查询条件,封装了考试查询的相关信息 - * @return IPage 返回包含考试DTO的分页结果 + * @param page + * @param query + * @return */ IPage paging(Page page, @Param("query") ExamDTO query); /** * 查找分页内容 - * 方法用于根据给定的分页对象和查询条件,查询阅卷数据的分页结果。 - * - * @param page 分页对象,包含分页信息,如当前页码和每页大小 - * @param query 查询条件,封装了考试查询的相关信息 - * @return IPage 返回包含阅卷分页响应DTO的分页结果 + * @param page + * @param query + * @return */ IPage reviewPaging(Page page, @Param("query") ExamDTO query); /** * 在线考试分页响应类-考生视角 - * 方法用于根据给定的分页对象和查询条件,查询在线考试数据的分页结果,从考生视角。 - * - * @param page 分页对象,包含分页信息,如当前页码和每页大小 - * @param query 查询条件,封装了考试查询的相关信息 - * @return IPage 返回包含在线考试分页响应DTO的分页结果 + * @param page + * @param query + * @return */ IPage online(Page page, @Param("query") ExamDTO query); -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamRepoMapper.java b/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamRepoMapper.java index 69900d2..83407df 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamRepoMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/mapper/ExamRepoMapper.java @@ -1,29 +1,26 @@ -package com.yf.exam.modules.exam.mapper; // 定义包名,Mapper接口所在的包路径 +package com.yf.exam.modules.exam.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入MyBatis Plus的基础Mapper接口 -import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; // 导入考试题库扩展DTO -import com.yf.exam.modules.exam.entity.ExamRepo; // 导入考试题库实体类 -import org.apache.ibatis.annotations.Param; // 导入MyBatis的参数注解 +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.entity.ExamRepo; +import org.apache.ibatis.annotations.Param; -import java.util.List; // 导入Java的List接口 +import java.util.List; /** - *

- * 考试题库Mapper,提供考试题库的数据库操作接口 - *

- * 此类是一个Mapper接口,继承自MyBatis Plus的BaseMapper,用于定义针对考试题库表的数据库操作。 - * - * @author 聪明笨狗 - * @since 2020-09-05 11:14 - */ -public interface ExamRepoMapper extends BaseMapper { // 声明接口,继承自BaseMapper并指定操作的实体类为ExamRepo +*

+* 考试题库Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +public interface ExamRepoMapper extends BaseMapper { /** * 查找考试题库列表 - * 方法用于根据给定的考试ID,查询关联的考试题库列表,返回扩展的DTO对象。 - * - * @param examId 考试ID,用于指定查询哪个考试的题库列表 - * @return List 返回包含考试题库扩展信息的列表 + * @param examId + * @return */ - List listByExam(@Param("examId") String examId); // 使用MyBatis的@Param注解来指定方法参数的名称 -} \ No newline at end of file + List listByExam(@Param("examId") String examId); +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamDepartService.java b/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamDepartService.java index 068a6dc..301c010 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamDepartService.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamDepartService.java @@ -1,36 +1,32 @@ -package com.yf.exam.modules.exam.service; // 定义包名,服务接口所在的包路径 +package com.yf.exam.modules.exam.service; -import com.baomidou.mybatisplus.extension.service.IService; // 导入MyBatis Plus的服务接口 -import com.yf.exam.modules.exam.entity.ExamDepart; // 导入考试部门实体类 +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.exam.entity.ExamDepart; -import java.util.List; // 导入Java的List接口 +import java.util.List; /** - *

- * 考试部门业务类,提供考试部门的业务逻辑 - *

- * 此类是一个服务接口,继承自MyBatis Plus的IService,用于定义考试部门相关的业务操作。 - * - * @author 聪明笨狗 - * @since 2020-09-03 17:24 - */ -public interface ExamDepartService extends IService { // 声明接口,继承自IService并指定操作的实体类为ExamDepart +*

+* 考试部门业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +public interface ExamDepartService extends IService { /** - * 保存全部部门信息 - * 方法用于根据给定的考试ID和部门ID列表,保存考试与部门的关联关系。 - * - * @param examId 考试ID,标识要关联的考试 - * @param departs 部门ID列表,包含要关联的部门ID + * 保存全部 + * @param examId + * @param departs */ void saveAll(String examId, List departs); + /** * 根据考试查找对应的部门 - * 方法用于根据给定的考试ID,查询与之关联的部门ID列表。 - * - * @param examId 考试ID,用于指定查询哪个考试的部门 - * @return List 返回部门ID列表,包含所有与考试关联的部门ID + * @param examId + * @return */ List listByExam(String examId); -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamRepoService.java b/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamRepoService.java index 5758185..78b4ec1 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamRepoService.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamRepoService.java @@ -1,45 +1,40 @@ -package com.yf.exam.modules.exam.service; // 定义包名,服务接口所在的包路径 +package com.yf.exam.modules.exam.service; -import com.baomidou.mybatisplus.extension.service.IService; // 导入MyBatis Plus的服务接口 -import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; // 导入考试题库扩展DTO -import com.yf.exam.modules.exam.entity.ExamRepo; // 导入考试题库实体类 +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.entity.ExamRepo; -import java.util.List; // 导入Java的List接口 +import java.util.List; /** - *

- * 考试题库业务类,提供考试题库的业务逻辑 - *

- * 此类是一个服务接口,继承自MyBatis Plus的IService,用于定义考试题库相关的业务操作。 - * - * @author 聪明笨狗 - * @since 2020-09-05 11:14 - */ -public interface ExamRepoService extends IService { // 声明接口,继承自IService并指定操作的实体类为ExamRepo +*

+* 考试题库业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +public interface ExamRepoService extends IService { + /** - * 保存全部题库信息 - * 方法用于根据给定的考试ID和题库列表,保存考试与题库的关联关系。 - * - * @param examId 考试ID,标识要关联的考试 - * @param list 题库列表,包含要关联的题库信息 + * 保存全部 + * @param examId + * @param list */ void saveAll(String examId, List list); /** * 查找考试题库列表 - * 方法用于根据给定的考试ID,查询与之关联的题库列表。 - * - * @param examId 考试ID,用于指定查询哪个考试的题库 - * @return List 返回题库列表,包含所有与考试关联的题库信息 + * @param examId + * @return */ List listByExam(String examId); /** * 清理脏数据 - * 方法用于清除与指定考试ID关联的题库数据,用于数据清理或重置场景。 - * - * @param examId 考试ID,用于指定要清理的考试关联的题库数据 + * @param examId */ void clear(String examId); -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamService.java b/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamService.java index 47d77b8..3f75664 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamService.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/service/ExamService.java @@ -1,75 +1,64 @@ -package com.yf.exam.modules.exam.service; // 定义包名,服务接口所在的包路径 +package com.yf.exam.modules.exam.service; -import com.baomidou.mybatisplus.core.metadata.IPage; // 导入MyBatis Plus的分页结果接口 -import com.baomidou.mybatisplus.extension.service.IService; // 导入MyBatis Plus的服务接口 -import com.yf.exam.core.api.dto.PagingReqDTO; // 导入分页请求DTO -import com.yf.exam.modules.exam.dto.ExamDTO; // 导入考试DTO -import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; // 导入考试保存请求DTO -import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; // 导入在线考试响应DTO -import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; // 导入阅卷响应DTO -import com.yf.exam.modules.exam.entity.Exam; // 导入考试实体类 +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.entity.Exam; /** - *

- * 考试业务类,提供考试的业务逻辑 - *

- * 此类是一个服务接口,继承自MyBatis Plus的IService,用于定义考试相关的业务操作。 - * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -public interface ExamService extends IService { // 声明接口,继承自IService并指定操作的实体类为Exam +*

+* 考试业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +public interface ExamService extends IService { /** * 保存考试信息 - * 方法用于保存考试信息,包括考试的基本信息和相关联的题库、部门等。 - * - * @param reqDTO 考试保存请求数据传输对象,包含考试的详细信息 + * @param reqDTO */ void save(ExamSaveReqDTO reqDTO); /** * 查找考试详情 - * 方法用于根据考试ID查询考试的详细信息,包括考试的基本数据和相关联的部门、题库等。 - * - * @param id 考试ID,用于指定要查询的考试 - * @return ExamSaveReqDTO 返回考试详情,包含考试的详细信息 + * @param id + * @return */ ExamSaveReqDTO findDetail(String id); /** * 查找考试详情--简要信息 - * 方法用于根据考试ID查询考试的简要信息,通常用于列表展示。 - * - * @param id 考试ID,用于指定要查询的考试 - * @return ExamDTO 返回考试简要信息,包含考试的基本数据 + * @param id + * @return */ ExamDTO findById(String id); /** - * 分页查询数据 - * 方法用于分页查询考试数据,通常用于列表展示。 - * - * @param reqDTO 分页请求数据传输对象,包含分页信息和查询条件 - * @return IPage 返回分页考试数据,包含考试列表和分页信息 - */ + * 分页查询数据 + * @param reqDTO + * @return + */ IPage paging(PagingReqDTO reqDTO); + /** * 在线考试分页响应类-考生视角 - * 方法用于分页查询在线考试数据,从考生视角,通常用于考生查看可参加的考试列表。 - * - * @param reqDTO 分页请求数据传输对象,包含分页信息和查询条件 - * @return IPage 返回分页在线考试数据,包含在线考试列表和分页信息 + * @param reqDTO + * @return */ IPage onlinePaging(PagingReqDTO reqDTO); + /** * 待阅试卷列表 - * 方法用于分页查询待阅试卷数据,通常用于阅卷老师查看需要批改的试卷列表。 - * - * @param reqDTO 分页请求数据传输对象,包含分页信息和查询条件 - * @return IPage 返回分页待阅试卷数据,包含待阅试卷列表和分页信息 + * @param reqDTO + * @return */ IPage reviewPaging(PagingReqDTO reqDTO); -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamDepartServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamDepartServiceImpl.java index 3460e7c..37ca2ff 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamDepartServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamDepartServiceImpl.java @@ -1,64 +1,66 @@ -package com.yf.exam.modules.exam.service.impl; // 定义包名,服务实现类所在的包路径 +package com.yf.exam.modules.exam.service.impl; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; // 导入MyBatis Plus的查询包装类 -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; // 导入MyBatis Plus的服务实现类 -import com.yf.exam.core.exception.ServiceException; // 导入自定义的服务异常类 -import com.yf.exam.modules.exam.entity.ExamDepart; // 导入考试部门实体类 -import com.yf.exam.modules.exam.mapper.ExamDepartMapper; // 导入考试部门Mapper接口 -import com.yf.exam.modules.exam.service.ExamDepartService; // 导入考试部门服务接口 -import org.springframework.stereotype.Service; // 导入Spring的服务注解 -import org.springframework.util.CollectionUtils; // 导入Spring的集合工具类 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.modules.exam.entity.ExamDepart; +import com.yf.exam.modules.exam.mapper.ExamDepartMapper; +import com.yf.exam.modules.exam.service.ExamDepartService; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; -import java.util.ArrayList; // 导入Java的ArrayList类 -import java.util.List; // 导入Java的List接口 +import java.util.ArrayList; +import java.util.List; /** - *

- * 考试部门业务实现类,提供考试部门的具体业务逻辑实现 - *

- * 此类实现了ExamDepartService接口,用于处理考试部门相关的业务逻辑。 - * - * @author 聪明笨狗 - * @since 2020-09-03 17:24 - */ -@Service // Spring注解,声明这是一个服务组件 -public class ExamDepartServiceImpl extends ServiceImpl implements ExamDepartService { // 声明类,继承自ServiceImpl并实现ExamDepartService接口 +*

+* 考试部门业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +@Service +public class ExamDepartServiceImpl extends ServiceImpl implements ExamDepartService { @Override public void saveAll(String examId, List departs) { - // 先删除已有的部门 + + // 先删除 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(ExamDepart::getExamId, examId); // 构造查询条件,查询指定考试ID的部门 - this.remove(wrapper); // 根据条件删除部门 + wrapper.lambda().eq(ExamDepart::getExamId, examId); + this.remove(wrapper); - // 再增加新的部门 - if (CollectionUtils.isEmpty(departs)) { // 检查部门列表是否为空 - throw new ServiceException(1, "请至少选择选择一个部门!!"); // 如果为空,抛出异常 + // 再增加 + if(CollectionUtils.isEmpty(departs)){ + throw new ServiceException(1, "请至少选择选择一个部门!!"); } - List list = new ArrayList<>(); // 创建考试部门列表 + List list = new ArrayList<>(); - for (String id : departs) { - ExamDepart depart = new ExamDepart(); // 创建考试部门对象 - depart.setDepartId(id); // 设置部门ID - depart.setExamId(examId); // 设置考试ID - list.add(depart); // 添加到列表 + for(String id: departs){ + ExamDepart depart = new ExamDepart(); + depart.setDepartId(id); + depart.setExamId(examId); + list.add(depart); } - this.saveBatch(list); // 批量保存部门 + this.saveBatch(list); } @Override public List listByExam(String examId) { - // 查找考试对应的部门 + // 先删除 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(ExamDepart::getExamId, examId); // 构造查询条件,查询指定考试ID的部门 - List list = this.list(wrapper); // 根据条件查询部门列表 - List ids = new ArrayList<>(); // 创建部门ID列表 - if (!CollectionUtils.isEmpty(list)) { // 检查部门列表是否为空 - for (ExamDepart item : list) { - ids.add(item.getDepartId()); // 添加部门ID到列表 + wrapper.lambda().eq(ExamDepart::getExamId, examId); + List list = this.list(wrapper); + List ids = new ArrayList<>(); + if(!CollectionUtils.isEmpty(list)){ + for(ExamDepart item: list){ + ids.add(item.getDepartId()); } } - return ids; // 返回部门ID列表 + + return ids; + } -} \ No newline at end of file +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamRepoServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamRepoServiceImpl.java index e161f56..ea630e6 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamRepoServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamRepoServiceImpl.java @@ -1,63 +1,67 @@ -package com.yf.exam.modules.exam.service.impl; // 定义包名,服务实现类所在的包路径 - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; // 导入MyBatis Plus的查询包装类 -import com.baomidou.mybatisplus.core.toolkit.IdWorker; // 导入MyBatis Plus的ID生成工具类 -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; // 导入MyBatis Plus的服务实现类 -import com.yf.exam.core.exception.ServiceException; // 导入自定义的服务异常类 -import com.yf.exam.core.utils.BeanMapper; // 导入自定义的Bean映射工具类 -import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; // 导入考试题库扩展DTO -import com.yf.exam.modules.exam.entity.ExamRepo; // 导入考试题库实体类 -import com.yf.exam.modules.exam.mapper.ExamRepoMapper; // 导入考试题库Mapper接口 -import com.yf.exam.modules.exam.service.ExamRepoService; // 导入考试题库服务接口 -import org.springframework.stereotype.Service; // 导入Spring的服务注解 -import org.springframework.transaction.annotation.Transactional; // 导入Spring的事务注解 -import org.springframework.util.CollectionUtils; // 导入Spring的集合工具类 - -import java.util.List; // 导入Java的List接口 +package com.yf.exam.modules.exam.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.entity.ExamRepo; +import com.yf.exam.modules.exam.mapper.ExamRepoMapper; +import com.yf.exam.modules.exam.service.ExamRepoService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.List; /** - *

- * 考试题库业务实现类,提供考试题库的具体业务逻辑实现 - *

- * 此类实现了ExamRepoService接口,用于处理考试题库相关的业务逻辑。 - * - * @author 聪明笨狗 - * @since 2020-09-05 11:14 - */ -@Service // Spring注解,声明这是一个服务组件 -public class ExamRepoServiceImpl extends ServiceImpl implements ExamRepoService { // 声明类,继承自ServiceImpl并实现ExamRepoService接口 - - @Transactional(rollbackFor = Exception.class) // Spring事务注解,声明事务边界和回滚条件 +*

+* 考试题库业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Service +public class ExamRepoServiceImpl extends ServiceImpl implements ExamRepoService { + + + @Transactional(rollbackFor = Exception.class) @Override public void saveAll(String examId, List list) { - // 先删除已有的题库 + + // 先删除 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(ExamRepo::getExamId, examId); // 构造查询条件,查询指定考试ID的题库 - this.remove(wrapper); // 根据条件删除题库 + wrapper.lambda().eq(ExamRepo::getExamId, examId); + this.remove(wrapper); - // 再增加新的题库 - if (CollectionUtils.isEmpty(list)) { // 检查题库列表是否为空 - throw new ServiceException(1, "必须选择题库!"); // 如果为空,抛出异常 + // 再增加 + if(CollectionUtils.isEmpty(list)){ + throw new ServiceException(1, "必须选择题库!"); } - List repos = BeanMapper.mapList(list, ExamRepo.class); // 使用BeanMapper将DTO列表转换为实体类列表 - for (ExamRepo item : repos) { - item.setExamId(examId); // 设置考试ID - item.setId(IdWorker.getIdStr()); // 使用IdWorker生成ID + List repos = BeanMapper.mapList(list, ExamRepo.class); + for(ExamRepo item: repos){ + item.setExamId(examId); + item.setId(IdWorker.getIdStr()); } - this.saveBatch(repos); // 批量保存题库 + this.saveBatch(repos); } @Override public List listByExam(String examId) { - return baseMapper.listByExam(examId); // 调用Mapper接口的方法,查找考试题库列表 + return baseMapper.listByExam(examId); } @Override public void clear(String examId) { - // 先删除已有的题库 + + // 先删除 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(ExamRepo::getExamId, examId); // 构造查询条件,查询指定考试ID的题库 - this.remove(wrapper); // 根据条件删除题库 + wrapper.lambda().eq(ExamRepo::getExamId, examId); + this.remove(wrapper); } -} \ No newline at end of file + + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamServiceImpl.java index 50ae151..7451bd9 100644 --- a/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/exam/service/impl/ExamServiceImpl.java @@ -1,173 +1,194 @@ -package com.yf.exam.modules.exam.service.impl; // 定义包名,服务实现类所在的包路径 - -import com.baomidou.mybatisplus.core.metadata.IPage; // 导入MyBatis Plus的分页结果接口 -import com.baomidou.mybatisplus.core.toolkit.IdWorker; // 导入MyBatis Plus的ID生成工具类 -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入MyBatis Plus的分页对象 -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; // 导入MyBatis Plus的服务实现类 -import com.yf.exam.core.api.dto.PagingReqDTO; // 导入分页请求DTO -import com.yf.exam.core.enums.OpenType; // 导入开放类型枚举 -import com.yf.exam.core.exception.ServiceException; // 导入自定义的服务异常类 -import com.yf.exam.core.utils.BeanMapper; // 导入自定义的Bean映射工具类 -import com.yf.exam.modules.exam.dto.ExamDTO; // 导入考试DTO -import com.yf.exam.modules.exam.dto.ExamRepoDTO; // 导入考试题库DTO -import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; // 导入考试题库扩展DTO -import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; // 导入考试保存请求DTO -import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; // 导入在线考试响应DTO -import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; // 导入阅卷响应DTO -import com.yf.exam.modules.exam.entity.Exam; // 导入考试实体类 -import com.yf.exam.modules.exam.mapper.ExamMapper; // 导入考试Mapper接口 -import com.yf.exam.modules.exam.service.ExamDepartService; // 导入考试部门服务接口 -import com.yf.exam.modules.exam.service.ExamRepoService; // 导入考试题库服务接口 -import com.yf.exam.modules.exam.service.ExamService; // 导入考试服务接口 -import org.apache.commons.lang3.StringUtils; // 导入Apache Commons Lang的字符串工具类 -import org.springframework.beans.factory.annotation.Autowired; // 导入Spring的自动注入注解 -import org.springframework.dao.DuplicateKeyException; // 导入Spring的重复键异常类 -import org.springframework.stereotype.Service; // 导入Spring的服务注解 - -import java.util.List; // 导入Java的List接口 +package com.yf.exam.modules.exam.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.enums.OpenType; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.ExamRepoDTO; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.entity.Exam; +import com.yf.exam.modules.exam.mapper.ExamMapper; +import com.yf.exam.modules.exam.service.ExamDepartService; +import com.yf.exam.modules.exam.service.ExamRepoService; +import com.yf.exam.modules.exam.service.ExamService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; + +import java.util.List; /** - *

- * 考试业务实现类 - *

- * 此类实现了ExamService接口,用于处理考试相关的业务逻辑。 - * - * @author 聪明笨狗 - * @since 2020-07-25 16:18 - */ -@Service // Spring注解,声明这是一个服务组件 -public class ExamServiceImpl extends ServiceImpl implements ExamService { // 声明类,继承自ServiceImpl并实现ExamService接口 +*

+* 考试业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Service +public class ExamServiceImpl extends ServiceImpl implements ExamService { + @Autowired - private ExamRepoService examRepoService; // 自动注入考试题库服务 + private ExamRepoService examRepoService; @Autowired - private ExamDepartService examDepartService; // 自动注入考试部门服务 + private ExamDepartService examDepartService; @Override public void save(ExamSaveReqDTO reqDTO) { + // ID String id = reqDTO.getId(); - if(StringUtils.isBlank(id)){ // 如果ID为空,则生成新的ID - id = IdWorker.getIdStr(); // 使用IdWorker生成ID + if(StringUtils.isBlank(id)){ + id = IdWorker.getIdStr(); } - // 复制参数 - Exam entity = new Exam(); // 创建考试实体 + //复制参数 + Exam entity = new Exam(); // 计算分值 - this.calcScore(reqDTO); // 调用方法计算分值 + this.calcScore(reqDTO); + // 复制基本数据 - BeanMapper.copy(reqDTO, entity); // 使用BeanMapper复制属性 - entity.setId(id); // 设置ID + BeanMapper.copy(reqDTO, entity); + entity.setId(id); // 修复状态 - if (reqDTO.getTimeLimit() != null + if (reqDTO.getTimeLimit()!=null && !reqDTO.getTimeLimit() - && reqDTO.getState() != null + && reqDTO.getState()!=null && reqDTO.getState() == 2) { - entity.setState(0); // 如果不限时且状态为2,则状态设置为0 + entity.setState(0); } else { - entity.setState(reqDTO.getState()); // 否则直接设置状态 + entity.setState(reqDTO.getState()); } // 题库组卷 try { - examRepoService.saveAll(id, reqDTO.getRepoList()); // 调用考试题库服务保存题库 + examRepoService.saveAll(id, reqDTO.getRepoList()); }catch (DuplicateKeyException e){ - throw new ServiceException(1, "不能选择重复的题库!"); // 如果出现重复键异常,则抛出服务异常 + throw new ServiceException(1, "不能选择重复的题库!"); } + // 开放的部门 - if(OpenType.DEPT_OPEN.equals(reqDTO.getOpenType())){ // 如果开放类型为部门开放 - examDepartService.saveAll(id, reqDTO.getDepartIds()); // 调用考试部门服务保存部门 + if(OpenType.DEPT_OPEN.equals(reqDTO.getOpenType())){ + examDepartService.saveAll(id, reqDTO.getDepartIds()); } - this.saveOrUpdate(entity); // 保存或更新考试实体 + this.saveOrUpdate(entity); + } @Override public ExamSaveReqDTO findDetail(String id) { - ExamSaveReqDTO respDTO = new ExamSaveReqDTO(); // 创建响应DTO - Exam exam = this.getById(id); // 根据ID查询考试实体 - BeanMapper.copy(exam, respDTO); // 使用BeanMapper复制属性 + ExamSaveReqDTO respDTO = new ExamSaveReqDTO(); + Exam exam = this.getById(id); + BeanMapper.copy(exam, respDTO); // 考试部门 - List departIds = examDepartService.listByExam(id); // 调用考试部门服务查询部门ID列表 - respDTO.setDepartIds(departIds); // 设置部门ID列表 + List departIds = examDepartService.listByExam(id); + respDTO.setDepartIds(departIds); // 题库 - List repos = examRepoService.listByExam(id); // 调用考试题库服务查询题库列表 - respDTO.setRepoList(repos); // 设置题库列表 + List repos = examRepoService.listByExam(id); + respDTO.setRepoList(repos); - return respDTO; // 返回响应DTO + return respDTO; } @Override public ExamDTO findById(String id) { - ExamDTO respDTO = new ExamDTO(); // 创建响应DTO - Exam exam = this.getById(id); // 根据ID查询考试实体 - BeanMapper.copy(exam, respDTO); // 使用BeanMapper复制属性 - return respDTO; // 返回响应DTO + ExamDTO respDTO = new ExamDTO(); + Exam exam = this.getById(id); + BeanMapper.copy(exam, respDTO); + return respDTO; } @Override public IPage paging(PagingReqDTO reqDTO) { - // 创建分页对象 - Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); // 使用分页请求DTO创建分页对象 - // 转换结果 - IPage pageData = baseMapper.paging(page, reqDTO.getParams()); // 调用Mapper接口的分页方法 - return pageData; // 返回分页结果 - } + //创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); + + //转换结果 + IPage pageData = baseMapper.paging(page, reqDTO.getParams()); + return pageData; + } @Override public IPage onlinePaging(PagingReqDTO reqDTO) { + + // 创建分页对象 - Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); // 使用分页请求DTO创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); // 查找分页 - IPage pageData = baseMapper.online(page, reqDTO.getParams()); // 调用Mapper接口的在线考试分页方法 - return pageData; // 返回分页结果 + IPage pageData = baseMapper.online(page, reqDTO.getParams()); + + return pageData; } @Override public IPage reviewPaging(PagingReqDTO reqDTO) { // 创建分页对象 - Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); // 使用分页请求DTO创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); // 查找分页 - IPage pageData = baseMapper.reviewPaging(page, reqDTO.getParams()); // 调用Mapper接口的阅卷分页方法 - return pageData; // 返回分页结果 + IPage pageData = baseMapper.reviewPaging(page, reqDTO.getParams()); + + return pageData; } + /** * 计算分值 - * 方法用于计算考试的总分值,根据题库中的题目数量和分数。 - * - * @param reqDTO 考试保存请求DTO + * @param reqDTO */ - private void calcScore(ExamSaveReqDTO reqDTO) { + private void calcScore(ExamSaveReqDTO reqDTO){ + // 主观题分数 int objScore = 0; // 题库组卷 - List repoList = reqDTO.getRepoList(); // 获取题库列表 - - for(ExamRepoDTO item : repoList){ // 遍历题库列表 - if(item.getRadioCount() != null && item.getRadioCount() > 0 && item.getRadioScore() != null && item.getRadioScore() > 0){ - objScore += item.getRadioCount() * item.getRadioScore(); // 计算单选题分数 + List repoList = reqDTO.getRepoList(); + + for(ExamRepoDTO item: repoList){ + if(item.getRadioCount()!=null + && item.getRadioCount()>0 + && item.getRadioScore()!=null + && item.getRadioScore()>0){ + objScore+=item.getRadioCount()*item.getRadioScore(); } - if(item.getMultiCount() != null && item.getMultiCount() > 0 && item.getMultiScore() != null && item.getMultiScore() > 0){ - objScore += item.getMultiCount() * item.getMultiScore(); // 计算多选题分数 + if(item.getMultiCount()!=null + && item.getMultiCount()>0 + && item.getMultiScore()!=null + && item.getMultiScore()>0){ + objScore+=item.getMultiCount()*item.getMultiScore(); } - if(item.getJudgeCount() != null && item.getJudgeCount() > 0 && item.getJudgeScore() != null && item.getJudgeScore() > 0){ - objScore += item.getJudgeCount() * item.getJudgeScore(); // 计算判断题分数 + if(item.getJudgeCount()!=null + && item.getJudgeCount()>0 + && item.getJudgeScore()!=null + && item.getJudgeScore()>0){ + objScore+=item.getJudgeCount()*item.getJudgeScore(); } } - reqDTO.setTotalScore(objScore); // 设置总分值 + + + reqDTO.setTotalScore(objScore); } -} \ No newline at end of file + +} diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/controller/PaperController.java b/src-源文件/main/java/com/yf/exam/modules/paper/controller/PaperController.java index 5549670..a3ad8ed 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/controller/PaperController.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/controller/PaperController.java @@ -1,13 +1,19 @@ -package com.yf.exam.modules.paper.controller; +package com.yf.exam.modules.paper.controller; // 定义当前类所在的包路径 +// 引入分页查询结果的接口 import com.baomidou.mybatisplus.core.metadata.IPage; +// 引入统一的API响应封装类 import com.yf.exam.core.api.ApiRest; +// 引入基础控制器类,提供通用的API响应方法 import com.yf.exam.core.api.controller.BaseController; +// 引入各种DTO类,用于请求和响应数据传输 import com.yf.exam.core.api.dto.BaseIdReqDTO; import com.yf.exam.core.api.dto.BaseIdRespDTO; import com.yf.exam.core.api.dto.BaseIdsReqDTO; import com.yf.exam.core.api.dto.PagingReqDTO; +// 引入工具类,用于对象之间的属性拷贝 import com.yf.exam.core.utils.BeanMapper; +// 引入试卷相关的DTO类 import com.yf.exam.modules.paper.dto.PaperDTO; import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO; @@ -17,14 +23,22 @@ import com.yf.exam.modules.paper.dto.request.PaperQuQueryDTO; import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO; import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO; import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; +// 引入试卷实体类 import com.yf.exam.modules.paper.entity.Paper; +// 引入试卷相关的业务处理服务类 import com.yf.exam.modules.paper.service.PaperService; +// 引入用户工具类,获取当前登录用户信息 import com.yf.exam.modules.user.UserUtils; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +// 引入Shiro注解,用于角色权限管理 import org.apache.shiro.authz.annotation.RequiresRoles; +// 引入Spring的Bean工具类,用于属性复制 import org.springframework.beans.BeanUtils; +// 引入Spring的自动注入注解,用于自动注入服务 import org.springframework.beans.factory.annotation.Autowired; +// 引入Spring的Web注解,定义HTTP请求的映射方式 import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -34,123 +48,126 @@ import org.springframework.web.bind.annotation.RestController; *

* 试卷控制器 *

-* +* 提供有关试卷的操作,如分页查询、创建试卷、查看试卷详情等 * @author 聪明笨狗 * @since 2020-05-25 16:33 */ -@Api(tags={"试卷"}) -@RestController -@RequestMapping("/exam/api/paper/paper") -public class PaperController extends BaseController { +@Api(tags={"试卷"}) // Swagger API 文档注解,用于描述该接口属于试卷相关操作 +@RestController // 标记为Spring的控制器,处理HTTP请求 +@RequestMapping("/exam/api/paper/paper") // 定义请求路径的基础路径 +public class PaperController extends BaseController { // 继承BaseController类,提供基本的API返回 - @Autowired + @Autowired // 自动注入PaperService,负责业务逻辑处理 private PaperService baseService; /** - * 分页查找 - * @param reqDTO - * @return + * 分页查找试卷列表 + * @param reqDTO 包含分页信息和查询条件的请求数据 + * @return 分页后的试卷列表 */ - @ApiOperation(value = "分页查找") - @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + @ApiOperation(value = "分页查找") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { - //分页查询并转换 + // 调用业务层进行分页查询 IPage page = baseService.paging(reqDTO); + // 返回成功响应,并附带查询结果 return super.success(page); } - - /** * 创建试卷 - * @param reqDTO - * @return + * @param reqDTO 创建试卷所需的请求数据 + * @return 返回创建成功后的试卷ID */ - @ApiOperation(value = "创建试卷") - @RequestMapping(value = "/create-paper", method = { RequestMethod.POST}) + @ApiOperation(value = "创建试卷") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/create-paper", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest save(@RequestBody PaperCreateReqDTO reqDTO) { - //复制参数 + // 调用业务层创建试卷,传入当前用户ID和考试ID String paperId = baseService.createPaper(UserUtils.getUserId(), reqDTO.getExamId()); + // 返回创建结果,包括试卷ID return super.success(new BaseIdRespDTO(paperId)); } /** - * 批量删除 - * @param reqDTO - * @return + * 获取试卷详情 + * @param reqDTO 请求参数,包含试卷ID + * @return 返回试卷的详细信息 */ - @ApiOperation(value = "试卷详情") - @RequestMapping(value = "/paper-detail", method = { RequestMethod.POST}) + @ApiOperation(value = "试卷详情") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/paper-detail", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest paperDetail(@RequestBody BaseIdReqDTO reqDTO) { - //根据ID删除 + // 调用业务层获取试卷详情 ExamDetailRespDTO respDTO = baseService.paperDetail(reqDTO.getId()); + // 返回成功响应,并附带试卷详情 return super.success(respDTO); } /** - * 批量删除 - * @param reqDTO - * @return + * 获取试题详情 + * @param reqDTO 请求参数,包含试卷ID和试题ID + * @return 返回试题的详细信息 */ - @ApiOperation(value = "试题详情") - @RequestMapping(value = "/qu-detail", method = { RequestMethod.POST}) + @ApiOperation(value = "试题详情") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/qu-detail", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest quDetail(@RequestBody PaperQuQueryDTO reqDTO) { - //根据ID删除 + // 调用业务层获取试题详情 PaperQuDetailDTO respDTO = baseService.findQuDetail(reqDTO.getPaperId(), reqDTO.getQuId()); + // 返回成功响应,并附带试题详情 return super.success(respDTO); } /** * 填充答案 - * @param reqDTO - * @return + * @param reqDTO 请求数据,包含填充的答案 + * @return 返回操作成功的响应 */ - @ApiOperation(value = "填充答案") - @RequestMapping(value = "/fill-answer", method = { RequestMethod.POST}) + @ApiOperation(value = "填充答案") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/fill-answer", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest fillAnswer(@RequestBody PaperAnswerDTO reqDTO) { - //根据ID删除 + // 调用业务层填充答案操作 baseService.fillAnswer(reqDTO); + // 返回成功响应 return super.success(); } - /** * 交卷操作 - * @param reqDTO - * @return + * @param reqDTO 请求数据,包含试卷ID + * @return 返回交卷操作成功的响应 */ - @ApiOperation(value = "交卷操作") - @RequestMapping(value = "/hand-exam", method = { RequestMethod.POST}) + @ApiOperation(value = "交卷操作") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/hand-exam", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest handleExam(@RequestBody BaseIdReqDTO reqDTO) { - //根据ID删除 + // 调用业务层进行交卷操作 baseService.handExam(reqDTO.getId()); + // 返回成功响应 return super.success(); } - /** - * 批量删除 - * @param reqDTO - * @return + * 获取试卷结果 + * @param reqDTO 请求数据,包含试卷ID + * @return 返回试卷的考试结果 */ - @ApiOperation(value = "试卷详情") - @RequestMapping(value = "/paper-result", method = { RequestMethod.POST}) + @ApiOperation(value = "试卷结果") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/paper-result", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest paperResult(@RequestBody BaseIdReqDTO reqDTO) { - //根据ID删除 + // 调用业务层获取试卷的考试结果 ExamResultRespDTO respDTO = baseService.paperResult(reqDTO.getId()); + // 返回成功响应,并附带试卷结果 return super.success(respDTO); } - /** * 检测用户有没有中断的考试 - * @return + * @return 返回用户未完成的考试信息 */ - @ApiOperation(value = "检测进行中的考试") - @RequestMapping(value = "/check-process", method = { RequestMethod.POST}) + @ApiOperation(value = "检测进行中的考试") // Swagger操作注解,用于描述接口 + @RequestMapping(value = "/check-process", method = { RequestMethod.POST}) // 定义POST请求路径 public ApiRest checkProcess() { - //复制参数 + // 调用业务层检测用户是否有未完成的考试 PaperDTO dto = baseService.checkProcess(UserUtils.getUserId()); + // 返回成功响应,并附带考试进程数据 return super.success(dto); } } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperDTO.java index dcfb9ef..8b59dcb 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperDTO.java @@ -1,79 +1,83 @@ -package com.yf.exam.modules.paper.dto; +package com.yf.exam.modules.paper.dto; // 定义当前类所在的包路径 +// 引入Dict注解,用于字典表的映射 import com.yf.exam.core.annon.Dict; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入Serializable接口,用于对象序列化 import java.io.Serializable; +// 引入Date类,用于表示日期和时间 import java.util.Date; /** -*

-* 试卷请求类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 17:31 -*/ -@Data -@ApiModel(value="试卷", description="试卷") -public class PaperDTO implements Serializable { - - private static final long serialVersionUID = 1L; + *

+ * 试卷请求类 + *

+ * 该类用于传输试卷的基本信息,包含试卷的ID、考试规则、标题、时长、得分等字段。 + * 提供了试卷的详细信息,通常在查询和展示试卷时使用。 + * + * @author 聪明笨狗 + * @since 2020-05-25 17:31 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷", description="试卷") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperDTO implements Serializable { // 实现Serializable接口,表示该类的对象可以被序列化 + private static final long serialVersionUID = 1L; // 序列化版本ID,用于序列化和反序列化操作 - @ApiModelProperty(value = "试卷ID", required=true) - private String id; + @ApiModelProperty(value = "试卷ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String id; // 试卷ID - @Dict(dictTable = "sys_user", dicText = "real_name", dicCode = "id") - @ApiModelProperty(value = "用户ID", required=true) - private String userId; + @Dict(dictTable = "sys_user", dicText = "real_name", dicCode = "id") // 字典表映射,映射用户表的姓名字段 + @ApiModelProperty(value = "用户ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String userId; // 用户ID - @Dict(dictTable = "sys_depart", dicText = "dept_name", dicCode = "id") - @ApiModelProperty(value = "部门ID", required=true) - private String departId; + @Dict(dictTable = "sys_depart", dicText = "dept_name", dicCode = "id") // 字典表映射,映射部门表的部门名称字段 + @ApiModelProperty(value = "部门ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String departId; // 部门ID - @ApiModelProperty(value = "规则ID", required=true) - private String examId; + @ApiModelProperty(value = "规则ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String examId; // 规则ID,表示该试卷对应的考试规则 - @ApiModelProperty(value = "考试标题", required=true) - private String title; + @ApiModelProperty(value = "考试标题", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String title; // 考试标题,表示试卷的名称 - @ApiModelProperty(value = "考试时长", required=true) - private Integer totalTime; + @ApiModelProperty(value = "考试时长", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer totalTime; // 考试时长(单位:分钟) - @ApiModelProperty(value = "用户时长", required=true) - private Integer userTime; + @ApiModelProperty(value = "用户时长", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer userTime; // 用户已使用的时间(单位:分钟) - @ApiModelProperty(value = "试卷总分", required=true) - private Integer totalScore; + @ApiModelProperty(value = "试卷总分", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer totalScore; // 试卷总分 - @ApiModelProperty(value = "及格分", required=true) - private Integer qualifyScore; + @ApiModelProperty(value = "及格分", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer qualifyScore; // 及格分数 - @ApiModelProperty(value = "客观分", required=true) - private Integer objScore; + @ApiModelProperty(value = "客观分", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer objScore; // 客观题分数 - @ApiModelProperty(value = "主观分", required=true) - private Integer subjScore; + @ApiModelProperty(value = "主观分", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer subjScore; // 主观题分数 - @ApiModelProperty(value = "用户得分", required=true) - private Integer userScore; + @ApiModelProperty(value = "用户得分", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer userScore; // 用户得分 - @ApiModelProperty(value = "是否包含简答题", required=true) - private Boolean hasSaq; + @ApiModelProperty(value = "是否包含简答题", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Boolean hasSaq; // 是否包含简答题,布尔类型,表示该试卷是否有简答题 - @ApiModelProperty(value = "试卷状态", required=true) - private Integer state; + @ApiModelProperty(value = "试卷状态", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer state; // 试卷状态,表示试卷的当前状态,如未开始、进行中、已结束等 - @ApiModelProperty(value = "创建时间", required=true) - private Date createTime; + @ApiModelProperty(value = "创建时间", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Date createTime; // 创建时间,表示试卷的创建时间 - @ApiModelProperty(value = "更新时间", required=true) - private Date updateTime; + @ApiModelProperty(value = "更新时间", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Date updateTime; // 更新时间,表示试卷的最后更新时间 - @ApiModelProperty(value = "截止时间") - private Date limitTime; - + @ApiModelProperty(value = "截止时间") // Swagger注解,描述该字段在API文档中的含义 + private Date limitTime; // 截止时间,表示考试的最后提交时间 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuAnswerDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuAnswerDTO.java index e449e79..48672a7 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuAnswerDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuAnswerDTO.java @@ -1,48 +1,50 @@ -package com.yf.exam.modules.paper.dto; +package com.yf.exam.modules.paper.dto; // 定义当前类所在的包路径 +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入Serializable接口,用于对象序列化 import java.io.Serializable; /** -*

-* 试卷考题备选答案请求类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 17:31 -*/ -@Data -@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案") -public class PaperQuAnswerDTO implements Serializable { - - private static final long serialVersionUID = 1L; + *

+ * 试卷考题备选答案请求类 + *

+ * 该类用于表示试卷考题的备选答案,包含每个备选答案的ID、题目ID、选项标签、是否选中等信息。 + * 适用于考题答案的请求传输。 + * + * @author 聪明笨狗 + * @since 2020-05-25 17:31 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperQuAnswerDTO implements Serializable { // 实现Serializable接口,表示该类的对象可以被序列化 + private static final long serialVersionUID = 1L; // 序列化版本ID,用于序列化和反序列化操作 - @ApiModelProperty(value = "自增ID", required=true) - private String id; + @ApiModelProperty(value = "自增ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String id; // 自增ID,表示备选答案的唯一标识符 - @ApiModelProperty(value = "试卷ID", required=true) - private String paperId; + @ApiModelProperty(value = "试卷ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String paperId; // 试卷ID,表示该备选答案所属的试卷 - @ApiModelProperty(value = "回答项ID", required=true) - private String answerId; + @ApiModelProperty(value = "回答项ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String answerId; // 回答项ID,表示该备选答案的唯一标识符 - @ApiModelProperty(value = "题目ID", required=true) - private String quId; + @ApiModelProperty(value = "题目ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String quId; // 题目ID,表示该备选答案所属的题目 - @ApiModelProperty(value = "是否正确项", required=true) - private Boolean isRight; + @ApiModelProperty(value = "是否正确项", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Boolean isRight; // 是否正确项,布尔值,表示该备选答案是否是正确答案 - @ApiModelProperty(value = "是否选中", required=true) - private Boolean checked; + @ApiModelProperty(value = "是否选中", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Boolean checked; // 是否选中,布尔值,表示该备选答案是否已被选中 - @ApiModelProperty(value = "排序", required=true) - private Integer sort; + @ApiModelProperty(value = "排序", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer sort; // 排序,表示该备选答案在题目中的排序位置 - @ApiModelProperty(value = "选项标签", required=true) - private String abc; - + @ApiModelProperty(value = "选项标签", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String abc; // 选项标签,通常为 A、B、C、D 等,表示该备选答案的标识符 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuDTO.java index 349868e..05bf6d7 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/PaperQuDTO.java @@ -1,54 +1,55 @@ -package com.yf.exam.modules.paper.dto; +package com.yf.exam.modules.paper.dto; // 定义当前类所在的包路径 +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入Serializable接口,用于对象序列化 import java.io.Serializable; /** -*

-* 试卷考题请求类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 17:31 -*/ -@Data -@ApiModel(value="试卷考题", description="试卷考题") -public class PaperQuDTO implements Serializable { - - private static final long serialVersionUID = 1L; + *

+ * 试卷考题请求类 + *

+ * 该类用于表示试卷中的每一道题目,包含题目的基本信息,如题目ID、试卷ID、题目类型、是否答对、得分等。 + * + * @author 聪明笨狗 + * @since 2020-05-25 17:31 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷考题", description="试卷考题") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperQuDTO implements Serializable { // 实现Serializable接口,表示该类的对象可以被序列化 + private static final long serialVersionUID = 1L; // 序列化版本ID,用于序列化和反序列化操作 - @ApiModelProperty(value = "ID", required=true) - private String id; + @ApiModelProperty(value = "ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String id; // 题目ID,唯一标识符 - @ApiModelProperty(value = "试卷ID", required=true) - private String paperId; + @ApiModelProperty(value = "试卷ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String paperId; // 试卷ID,表示该题目所属的试卷 - @ApiModelProperty(value = "题目ID", required=true) - private String quId; + @ApiModelProperty(value = "题目ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String quId; // 题目ID,唯一标识该题目 - @ApiModelProperty(value = "题目类型", required=true) - private Integer quType; + @ApiModelProperty(value = "题目类型", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer quType; // 题目类型,表示题目的分类,如选择题、判断题、主观题等 - @ApiModelProperty(value = "是否已答", required=true) - private Boolean answered; + @ApiModelProperty(value = "是否已答", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Boolean answered; // 是否已答,布尔值,表示该题目是否已被回答 - @ApiModelProperty(value = "主观答案", required=true) - private String answer; + @ApiModelProperty(value = "主观答案", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String answer; // 主观答案,表示对该题目的回答内容(适用于主观题) - @ApiModelProperty(value = "问题排序", required=true) - private Integer sort; + @ApiModelProperty(value = "问题排序", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer sort; // 问题排序,表示该题目在试卷中的顺序 - @ApiModelProperty(value = "单题分分值", required=true) - private Integer score; + @ApiModelProperty(value = "单题分分值", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer score; // 单题分值,表示该题目的满分 - @ApiModelProperty(value = "实际得分(主观题)", required=true) - private Integer actualScore; + @ApiModelProperty(value = "实际得分(主观题)", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer actualScore; // 实际得分,表示用户在该题目中实际得到的分数(适用于主观题) - @ApiModelProperty(value = "是否答对", required=true) - private Boolean isRight; - + @ApiModelProperty(value = "是否答对", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Boolean isRight; // 是否答对,布尔值,表示用户是否答对了该题目 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuAnswerExtDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuAnswerExtDTO.java index 3124d9e..357808a 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuAnswerExtDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuAnswerExtDTO.java @@ -1,29 +1,30 @@ -package com.yf.exam.modules.paper.dto.ext; +package com.yf.exam.modules.paper.dto.ext; // 定义该类所在的包路径 +// 引入试题答案DTO类,作为当前类的父类 import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; /** *

* 试卷考题备选答案请求类 *

-* +* 该类用于描述试卷考题的备选答案信息,继承自PaperQuAnswerDTO,扩展了额外的字段,如图片和内容。 * @author 聪明笨狗 * @since 2020-05-25 17:31 */ -@Data -@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案") -public class PaperQuAnswerExtDTO extends PaperQuAnswerDTO { +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案") // Swagger注解,描述模型信息,生成API文档时使用 +public class PaperQuAnswerExtDTO extends PaperQuAnswerDTO { // 继承自PaperQuAnswerDTO类,扩展了额外属性 - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本号,用于序列化和反序列化时的版本控制 - @ApiModelProperty(value = "试题图片", required=true) - private String image; + @ApiModelProperty(value = "试题图片", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String image; // 试题对应的图片内容 - @ApiModelProperty(value = "答案内容", required=true) - private String content; - - + @ApiModelProperty(value = "答案内容", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String content; // 备选答案的具体内容 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuDetailDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuDetailDTO.java index ee1e998..e2d693b 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuDetailDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuDetailDTO.java @@ -1,32 +1,35 @@ -package com.yf.exam.modules.paper.dto.ext; +package com.yf.exam.modules.paper.dto.ext; // 定义当前类所在的包路径 +// 引入试题DTO类,作为当前类的父类 import com.yf.exam.modules.paper.dto.PaperQuDTO; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入List集合,用于存储多个备选答案 import java.util.List; /** *

* 试卷考题请求类 *

-* +* 该类用于描述试卷中每个考题的详细信息,继承自PaperQuDTO,扩展了试题的图片、内容和答案列表等字段。 * @author 聪明笨狗 * @since 2020-05-25 17:31 */ -@Data -@ApiModel(value="试卷题目详情类", description="试卷题目详情类") -public class PaperQuDetailDTO extends PaperQuDTO { +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷题目详情类", description="试卷题目详情类") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperQuDetailDTO extends PaperQuDTO { // 继承自PaperQuDTO类,扩展了额外属性 - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本号,用于序列化和反序列化时的版本控制 - @ApiModelProperty(value = "图片", required=true) - private String image; + @ApiModelProperty(value = "图片", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String image; // 题目的图片内容 - @ApiModelProperty(value = "题目内容", required=true) - private String content; + @ApiModelProperty(value = "题目内容", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String content; // 试题的具体内容 - @ApiModelProperty(value = "答案内容", required=true) - List answerList; + @ApiModelProperty(value = "答案内容", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private List answerList; // 存储该题目的备选答案,使用List集合存储多个答案 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperAnswerDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperAnswerDTO.java index ccbe3ce..25df4c6 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperAnswerDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperAnswerDTO.java @@ -1,22 +1,25 @@ -package com.yf.exam.modules.paper.dto.request; +package com.yf.exam.modules.paper.dto.request; // 定义当前类所在的包路径 +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入List集合,用于存储多个答案 import java.util.List; /** * @author bool + * 查找试卷题目详情请求类,用于接收查找试卷题目详情的请求数据。 + * 继承自PaperQuQueryDTO,扩展了回答列表和主观答案字段。 */ -@Data -@ApiModel(value="查找试卷题目详情请求类", description="查找试卷题目详情请求类") -public class PaperAnswerDTO extends PaperQuQueryDTO { - - @ApiModelProperty(value = "回答列表", required=true) - private List answers; +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="查找试卷题目详情请求类", description="查找试卷题目详情请求类") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperAnswerDTO extends PaperQuQueryDTO { // 继承自PaperQuQueryDTO类,扩展了额外属性 - @ApiModelProperty(value = "主观答案", required=true) - private String answer; + @ApiModelProperty(value = "回答列表", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private List answers; // 存储多个选择题答案的列表 + @ApiModelProperty(value = "主观答案", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String answer; // 存储主观题的答案内容 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperCreateReqDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperCreateReqDTO.java index 6a8f8c5..22d11e9 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperCreateReqDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperCreateReqDTO.java @@ -1,22 +1,26 @@ -package com.yf.exam.modules.paper.dto.request; +package com.yf.exam.modules.paper.dto.request; // 定义当前类所在的包路径 +// 引入父类BaseDTO,用于继承基础字段和功能 import com.yf.exam.core.api.dto.BaseDTO; +// 引入Jackson注解,用于处理JSON序列化时忽略某些字段 import com.fasterxml.jackson.annotation.JsonIgnore; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; /** * @author bool + * 试卷创建请求类,用于接收试卷创建的请求数据,包含考试ID和用户ID等字段。 */ -@Data -@ApiModel(value="试卷创建请求类", description="试卷创建请求类") -public class PaperCreateReqDTO extends BaseDTO { +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷创建请求类", description="试卷创建请求类") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperCreateReqDTO extends BaseDTO { // 继承自BaseDTO类,扩展了考试ID和用户ID字段 - @JsonIgnore - private String userId; - - @ApiModelProperty(value = "考试ID", required=true) - private String examId; + @JsonIgnore // Jackson注解,表示在进行JSON序列化/反序列化时忽略该字段 + private String userId; // 存储用户ID,通常用于标识发起请求的用户,但在JSON序列化中不会被传递 + @ApiModelProperty(value = "考试ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String examId; // 存储考试ID,用于创建试卷时指定关联的考试 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperListReqDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperListReqDTO.java index c333241..a166dc8 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperListReqDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperListReqDTO.java @@ -1,39 +1,40 @@ -package com.yf.exam.modules.paper.dto.request; +package com.yf.exam.modules.paper.dto.request; // 定义当前类所在的包路径 +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入Serializable接口,用于对象序列化 import java.io.Serializable; /** *

* 试卷请求类 *

-* +* 该类用于接收请求数据,包含用户ID、部门ID、规则ID、用户昵称和试卷状态等信息, +* 用于查询试卷列表。 * @author 聪明笨狗 * @since 2020-05-25 17:31 */ -@Data -@ApiModel(value="试卷", description="试卷") -public class PaperListReqDTO implements Serializable { - - private static final long serialVersionUID = 1L; +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷", description="试卷") // Swagger注解,描述该类在API文档中的作用和说明 +public class PaperListReqDTO implements Serializable { // 实现Serializable接口,支持对象的序列化 - @ApiModelProperty(value = "用户ID", required=true) - private String userId; + private static final long serialVersionUID = 1L; // 序列化版本号,用于序列化和反序列化时的版本控制 - @ApiModelProperty(value = "部门ID", required=true) - private String departId; + @ApiModelProperty(value = "用户ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String userId; // 存储请求发起者的用户ID,通常用于标识用户 - @ApiModelProperty(value = "规则ID", required=true) - private String examId; + @ApiModelProperty(value = "部门ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String departId; // 存储请求者所在部门的ID,用于查询特定部门的试卷 - @ApiModelProperty(value = "用户昵称", required=true) - private String realName; + @ApiModelProperty(value = "规则ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String examId; // 存储与试卷相关的考试规则ID,用于标识试卷属于哪种考试 - @ApiModelProperty(value = "试卷状态", required=true) - private Integer state; + @ApiModelProperty(value = "用户昵称", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String realName; // 存储用户的真实姓名,用于标识请求者 - + @ApiModelProperty(value = "试卷状态", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private Integer state; // 存储试卷的状态,可能的值如:未开始、进行中、已完成等 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperQuQueryDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperQuQueryDTO.java index 48184b6..b91286c 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperQuQueryDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/request/PaperQuQueryDTO.java @@ -1,21 +1,25 @@ -package com.yf.exam.modules.paper.dto.request; +package com.yf.exam.modules.paper.dto.request; // 定义当前类所在的包路径 +// 引入父类BaseDTO,用于继承基础字段和功能 import com.yf.exam.core.api.dto.BaseDTO; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; /** * @author bool + * 查找试卷题目详情请求类,用于接收试卷和题目详情查询的请求数据。 + * 包含试卷ID和题目ID字段。 */ -@Data -@ApiModel(value="查找试卷题目详情请求类", description="查找试卷题目详情请求类") -public class PaperQuQueryDTO extends BaseDTO { +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="查找试卷题目详情请求类", description="查找试卷题目详情请求类") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperQuQueryDTO extends BaseDTO { // 继承自BaseDTO类,扩展了试卷ID和题目ID字段 - @ApiModelProperty(value = "试卷ID", required=true) - private String paperId; - - @ApiModelProperty(value = "题目ID", required=true) - private String quId; + @ApiModelProperty(value = "试卷ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String paperId; // 存储试卷ID,用于查询特定试卷的题目详情 + @ApiModelProperty(value = "题目ID", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String quId; // 存储题目ID,用于查询指定试卷中的某一道题目 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamDetailRespDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamDetailRespDTO.java index c307c84..12aecf2 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamDetailRespDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamDetailRespDTO.java @@ -1,38 +1,48 @@ -package com.yf.exam.modules.paper.dto.response; +package com.yf.exam.modules.paper.dto.response; // 定义当前类所在的包路径 +// 引入父类PaperDTO,用于继承基础字段和功能 import com.yf.exam.modules.paper.dto.PaperDTO; +// 引入PaperQuDTO类,用于表示试题的DTO import com.yf.exam.modules.paper.dto.PaperQuDTO; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入Calendar类,用于时间计算 import java.util.Calendar; +// 引入List集合,用于存储试题列表 import java.util.List; -@Data -@ApiModel(value="考试详情", description="考试详情") -public class ExamDetailRespDTO extends PaperDTO { - +/** + *

+ * 考试详情响应类 + *

+ * 该类继承自PaperDTO,扩展了多个属性,包含单选题、多选题、判断题的列表, + * 以及计算剩余时间的方法,用于提供考试的详细信息。 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="考试详情", description="考试详情") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class ExamDetailRespDTO extends PaperDTO { // 继承自PaperDTO,表示考试详情响应DTO - @ApiModelProperty(value = "单选题列表", required=true) - private List radioList; + @ApiModelProperty(value = "单选题列表", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private List radioList; // 存储单选题的列表,使用PaperQuDTO表示单个试题 - @ApiModelProperty(value = "多选题列表", required=true) - private List multiList; + @ApiModelProperty(value = "多选题列表", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private List multiList; // 存储多选题的列表,使用PaperQuDTO表示单个试题 - @ApiModelProperty(value = "判断题", required=true) - private List judgeList; + @ApiModelProperty(value = "判断题", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private List judgeList; // 存储判断题的列表,使用PaperQuDTO表示单个试题 - - @ApiModelProperty(value = "剩余结束秒数", required=true) - public Long getLeftSeconds(){ + @ApiModelProperty(value = "剩余结束秒数", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + public Long getLeftSeconds(){ // 计算剩余时间的方法,返回剩余的秒数 // 结束时间 - Calendar cl = Calendar.getInstance(); - cl.setTime(this.getCreateTime()); - cl.add(Calendar.MINUTE, getTotalTime()); + Calendar cl = Calendar.getInstance(); // 获取当前时间的Calendar实例 + cl.setTime(this.getCreateTime()); // 设置Calendar的时间为试卷的创建时间 + cl.add(Calendar.MINUTE, getTotalTime()); // 在创建时间的基础上加上试卷的总时间(分钟) - return (cl.getTimeInMillis() - System.currentTimeMillis()) / 1000; + // 计算剩余时间(单位:秒) + return (cl.getTimeInMillis() - System.currentTimeMillis()) / 1000; // 返回剩余时间(当前时间到结束时间的差值,以秒为单位) } - } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamResultRespDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamResultRespDTO.java index 64af63d..26c9710 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamResultRespDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/ExamResultRespDTO.java @@ -1,18 +1,29 @@ -package com.yf.exam.modules.paper.dto.response; +package com.yf.exam.modules.paper.dto.response; // 定义当前类所在的包路径 +// 引入父类PaperDTO,用于继承基础字段和功能 import com.yf.exam.modules.paper.dto.PaperDTO; +// 引入PaperQuDetailDTO类,用于表示试题详细信息 import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入List集合,用于存储试题列表 import java.util.List; -@Data -@ApiModel(value="考试结果展示响应类", description="考试结果展示响应类") -public class ExamResultRespDTO extends PaperDTO { +/** + *

+ * 考试结果展示响应类 + *

+ * 该类继承自PaperDTO,扩展了问题列表字段,用于展示考试结果的详细信息。 + * 包含试题的详细信息列表,返回考试时各个问题的答案或状态。 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="考试结果展示响应类", description="考试结果展示响应类") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class ExamResultRespDTO extends PaperDTO { // 继承自PaperDTO,表示考试结果展示响应DTO - @ApiModelProperty(value = "问题列表", required=true) - private List quList; + @ApiModelProperty(value = "问题列表", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private List quList; // 存储试题详细信息的列表,使用PaperQuDetailDTO表示每个试题的详细数据 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/PaperListRespDTO.java b/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/PaperListRespDTO.java index 9e003e4..379de39 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/PaperListRespDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/dto/response/PaperListRespDTO.java @@ -1,26 +1,29 @@ -package com.yf.exam.modules.paper.dto.response; +package com.yf.exam.modules.paper.dto.response; // 定义当前类所在的包路径 +// 引入父类PaperDTO,用于继承基本的试卷信息 import com.yf.exam.modules.paper.dto.PaperDTO; +// 引入Swagger注解,用于API文档生成 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; /** -*

-* 试卷请求类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 17:31 -*/ -@Data -@ApiModel(value="试卷列表响应类", description="试卷列表响应类") -public class PaperListRespDTO extends PaperDTO { + *

+ * 试卷列表响应类 + *

+ * 该类继承自PaperDTO,扩展了人员(realName)字段,用于展示试卷列表响应数据。 + * 包含试卷的基本信息以及相关人员信息。 + * + * @author 聪明笨狗 + * @since 2020-05-25 17:31 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@ApiModel(value="试卷列表响应类", description="试卷列表响应类") // Swagger注解,用于描述该类在API文档中的作用和说明 +public class PaperListRespDTO extends PaperDTO { // 继承自PaperDTO,表示试卷列表响应DTO - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本ID,用于序列化和反序列化操作 - @ApiModelProperty(value = "人员", required=true) - private String realName; - - + @ApiModelProperty(value = "人员", required=true) // Swagger注解,描述该字段在API文档中的含义,并标注该字段为必填项 + private String realName; // 存储人员姓名,用于表示与该试卷相关的人员信息 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/entity/Paper.java b/src-源文件/main/java/com/yf/exam/modules/paper/entity/Paper.java index e219471..f6baaac 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/entity/Paper.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/entity/Paper.java @@ -1,125 +1,104 @@ -package com.yf.exam.modules.paper.entity; +package com.yf.exam.modules.paper.entity; // 定义当前类所在的包路径 +// 引入MyBatis Plus的相关注解 import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; - +// 引入Date类,用于时间相关的字段 import java.util.Date; /** -*

-* 试卷实体类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 17:31 -*/ -@Data -@TableName("el_paper") -public class Paper extends Model { - - private static final long serialVersionUID = 1L; + *

+ * 试卷实体类 + *

+ * 该类对应数据库中的 `el_paper` 表,用于表示试卷的相关信息。 + * + * @author 聪明笨狗 + * @since 2020-05-25 17:31 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@TableName("el_paper") // MyBatis Plus注解,指定该实体类对应的数据库表名 +public class Paper extends Model { // 继承MyBatis Plus的Model类,提供了CRUD等基础操作 + + private static final long serialVersionUID = 1L; // 序列化版本ID,用于序列化和反序列化操作 /** * 试卷ID */ - @TableId(value = "id", type = IdType.ASSIGN_ID) - private String id; + @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定主键字段及其生成策略 + private String id; // 试卷ID,唯一标识符 /** * 用户ID */ - @TableField("user_id") - private String userId; + @TableField("user_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String userId; // 用户ID,表示创建该试卷的用户 /** * 部门ID */ - @TableField("depart_id") - private String departId; + @TableField("depart_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String departId; // 部门ID,表示该试卷所属的部门 /** * 规则ID */ - @TableField("exam_id") - private String examId; + @TableField("exam_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String examId; // 规则ID,表示该试卷所属的考试规则 /** * 考试标题 */ - private String title; + private String title; // 考试标题,表示试卷的名称或标题 /** * 考试时长 */ - @TableField("total_time") - private Integer totalTime; + @TableField("total_time") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer totalTime; // 考试时长,单位为分钟 /** * 用户时长 */ - @TableField("user_time") - private Integer userTime; + @TableField("user_time") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer userTime; // 用户实际用时,单位为分钟 /** * 试卷总分 */ - @TableField("total_score") - private Integer totalScore; + @TableField("total_score") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer totalScore; // 试卷的总分数 /** * 及格分 */ - @TableField("qualify_score") - private Integer qualifyScore; + @TableField("qualify_score") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer qualifyScore; // 及格分数,表示通过该试卷的最低分数 /** * 客观分 */ - @TableField("obj_score") - private Integer objScore; + @TableField("obj_score") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer objScore; // 客观题部分的得分 /** * 主观分 */ - @TableField("subj_score") - private Integer subjScore; + @TableField("subj_score") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer subjScore; // 主观题部分的得分 /** * 用户得分 */ - @TableField("user_score") - private Integer userScore; + @TableField("user_score") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer userScore; // 用户在该试卷上的得分 /** * 是否包含简答题 */ - @TableField("has_saq") - private Boolean hasSaq; - - /** - * 试卷状态 - */ - private Integer state; - - /** - * 创建时间 - */ - @TableField("create_time") - private Date createTime; - - /** - * 更新时间 - */ - @TableField("update_time") - private Date updateTime; - - /** - * 截止时间 - */ - @TableField("limit_time") - private Date limitTime; -} + @TableField("has_saq") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQu.java b/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQu.java index 3723791..f6be46d 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQu.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQu.java @@ -1,80 +1,82 @@ -package com.yf.exam.modules.paper.entity; +package com.yf.exam.modules.paper.entity; // 定义当前类所在的包路径 +// 引入MyBatis Plus的相关注解 import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; /** -*

-* 试卷考题实体类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 17:31 -*/ -@Data -@TableName("el_paper_qu") -public class PaperQu extends Model { + *

+ * 试卷考题实体类 + *

+ * 该类对应数据库中的 `el_paper_qu` 表,用于表示与试卷相关的考题信息。 + * + * @author 聪明笨狗 + * @since 2020-05-25 17:31 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@TableName("el_paper_qu") // MyBatis Plus注解,指定该实体类对应的数据库表名 +public class PaperQu extends Model { // 继承MyBatis Plus的Model类,提供了基础的CRUD等操作 - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本ID,用于序列化和反序列化操作 /** * ID */ - @TableId(value = "id", type = IdType.ASSIGN_ID) - private String id; + @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定主键字段及其生成策略 + private String id; // 题目ID,唯一标识符 /** * 试卷ID */ - @TableField("paper_id") - private String paperId; + @TableField("paper_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String paperId; // 试卷ID,表示该题目所属的试卷ID /** * 题目ID */ - @TableField("qu_id") - private String quId; + @TableField("qu_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String quId; // 题目ID,唯一标识符 /** * 题目类型 */ - @TableField("qu_type") - private Integer quType; + @TableField("qu_type") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer quType; // 题目类型,表示该题目属于哪种类型(例如单选题、多选题、主观题等) /** * 是否已答 */ - private Boolean answered; + private Boolean answered; // 是否已经回答,布尔值,表示该题目是否已经被回答 /** * 主观答案 */ - private String answer; + private String answer; // 主观题的答案,保存用户的回答内容 /** * 问题排序 */ - private Integer sort; + private Integer sort; // 问题在试卷中的排序,决定题目的显示顺序 /** * 单题分分值 */ - private Integer score; + private Integer score; // 每道题的分值,表示该题目的得分值 /** * 实际得分(主观题) */ - @TableField("actual_score") - private Integer actualScore; + @TableField("actual_score") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Integer actualScore; // 主观题的实际得分,可能与用户给出的答案相关 /** * 是否答对 */ - @TableField("is_right") - private Boolean isRight; - + @TableField("is_right") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Boolean isRight; // 是否答对,布尔值,表示用户是否答对了该题目 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQuAnswer.java b/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQuAnswer.java index b3999da..78c17ce 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQuAnswer.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/entity/PaperQuAnswer.java @@ -1,68 +1,71 @@ -package com.yf.exam.modules.paper.entity; +package com.yf.exam.modules.paper.entity; // 定义当前类所在的包路径 +// 引入MyBatis Plus的相关注解 import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; +// 引入Lombok注解,用于自动生成getter、setter等方法 import lombok.Data; /** -*

-* 试卷考题备选答案实体类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 17:31 -*/ -@Data -@TableName("el_paper_qu_answer") -public class PaperQuAnswer extends Model { + *

+ * 试卷考题备选答案实体类 + *

+ * 该类对应数据库中的 `el_paper_qu_answer` 表,用于表示试卷考题的备选答案。 + * + * @author 聪明笨狗 + * @since 2020-05-25 17:31 + */ +@Data // Lombok注解,自动生成getter、setter、toString、equals和hashCode方法 +@TableName("el_paper_qu_answer") // MyBatis Plus注解,指定该实体类对应的数据库表名 +public class PaperQuAnswer extends Model { // 继承MyBatis Plus的Model类,提供了基础的CRUD等操作 + private static final long serialVersionUID = 1L; // 序列化版本ID,用于序列化和反序列化操作 /** * 自增ID */ - @TableId(value = "id", type = IdType.ASSIGN_ID) - private String id; + @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定主键字段及其生成策略 + private String id; // 唯一标识符ID,数据库中的主键 /** * 试卷ID */ - @TableField("paper_id") - private String paperId; + @TableField("paper_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String paperId; // 该备选答案对应的试卷ID /** * 回答项ID */ - @TableField("answer_id") - private String answerId; + @TableField("answer_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String answerId; // 该备选答案的唯一标识符ID /** * 题目ID */ - @TableField("qu_id") - private String quId; + @TableField("qu_id") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private String quId; // 该备选答案所属的题目ID /** * 是否正确项 */ - @TableField("is_right") - private Boolean isRight; + @TableField("is_right") // MyBatis Plus注解,指定字段与数据库表字段的映射关系 + private Boolean isRight; // 是否是正确答案,布尔值(true 表示正确,false 表示错误) /** * 是否选中 */ - private Boolean checked; + private Boolean checked; // 该备选答案是否被选中,布尔值(true 表示已选中,false 表示未选中) /** * 排序 */ - private Integer sort; + private Integer sort; // 备选答案的排序,决定该答案在选项中的位置 /** * 选项标签 */ - private String abc; - + private String abc; // 备选答案的标签,通常为A、B、C、D等,用于显示选项 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/enums/ExamState.java b/src-源文件/main/java/com/yf/exam/modules/paper/enums/ExamState.java index a62f404..f681da9 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/enums/ExamState.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/enums/ExamState.java @@ -1,33 +1,38 @@ -package com.yf.exam.modules.paper.enums; - +package com.yf.exam.modules.paper.enums; // 定义当前类所在的包路径 /** - * 考试状态 - * @author bool + *

+ * 考试状态枚举接口 + *

+ * 该接口定义了考试的不同状态,作为常量使用,帮助系统在处理考试过程中进行状态标识。 + * + * @author bool * @date 2019-10-30 13:11 */ public interface ExamState { - /** * 考试中 + * 代表考试正在进行中,通常表示考试尚未结束,学生可以继续答题。 */ Integer ENABLE = 0; /** * 待阅卷 + * 代表考试已经结束,但还没有开始批阅试卷,通常表示考试已经提交,等待教师或系统自动阅卷。 */ Integer DISABLED = 1; /** * 已完成 + * 代表考试已经完成,阅卷完成,结果已公布,但没有开始考试。 */ Integer READY_START = 2; /** * 已结束 + * 代表考试已经结束,不再接受任何操作,考试时间已超出,无法继续答题。 */ Integer OVERDUE = 3; - } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/enums/PaperState.java b/src-源文件/main/java/com/yf/exam/modules/paper/enums/PaperState.java index ceed269..27e342c 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/enums/PaperState.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/enums/PaperState.java @@ -1,33 +1,38 @@ -package com.yf.exam.modules.paper.enums; - +package com.yf.exam.modules.paper.enums; // 定义当前类所在的包路径 /** - * 试卷状态 - * @author bool + *

+ * 试卷状态枚举接口 + *

+ * 该接口定义了试卷的不同状态,作为常量使用,帮助系统在处理试卷过程中进行状态标识。 + * + * @author bool * @date 2019-10-30 13:11 */ public interface PaperState { - /** * 考试中 + * 代表试卷正在进行中,学生正在参与考试,答题环节尚未结束。 */ Integer ING = 0; /** * 待阅卷 + * 代表考试已经结束,试卷已经提交,正在等待阅卷(人工或系统自动阅卷)。 */ Integer WAIT_OPT = 1; /** * 已完成 + * 代表试卷已完成,包括考试、阅卷等所有环节,成绩已经确定。 */ Integer FINISHED = 2; /** * 弃考 + * 代表学生中途放弃考试,考试未完成,可能是由于学生主动退出或其他原因(如时间到期)导致考试中止。 */ Integer BREAK = 3; - } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/job/BreakExamJob.java b/src-源文件/main/java/com/yf/exam/modules/paper/job/BreakExamJob.java index fb14f53..23a9762 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/job/BreakExamJob.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/job/BreakExamJob.java @@ -1,45 +1,61 @@ -package com.yf.exam.modules.paper.job; - -import com.yf.exam.ability.job.service.JobService; -import com.yf.exam.modules.paper.service.PaperService; -import lombok.extern.log4j.Log4j2; -import org.quartz.Job; -import org.quartz.JobDetail; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +package com.yf.exam.modules.paper.job; // 定义类所在的包路径 + +import com.yf.exam.ability.job.service.JobService; // 导入 JobService 类,用于获取任务数据 +import com.yf.exam.modules.paper.service.PaperService; // 导入 PaperService 类,用于处理试卷相关的业务逻辑 +import lombok.extern.log4j.Log4j2; // 引入 log4j2 日志工具 +import org.quartz.Job; // 导入 Quartz 作业接口 +import org.quartz.JobDetail; // 导入 Quartz JobDetail 类 +import org.quartz.JobExecutionContext; // 导入 Quartz JobExecutionContext 类 +import org.quartz.JobExecutionException; // 导入 Quartz JobExecutionException 类 +import org.springframework.beans.factory.annotation.Autowired; // 引入 Spring 注解,用于自动注入依赖 +import org.springframework.stereotype.Component; // 引入 Spring 组件注解,标识为 Spring Bean /** * 超时自动交卷任务 + *

+ * 该类是一个 Quartz 定时任务,用于在考试时间到期后强制交卷。 + * 它在定时任务触发时调用 PaperService 进行强制交卷操作。 + *

+ * * @author bool */ -@Log4j2 -@Component +@Log4j2 // 启用 log4j2 日志记录 +@Component // 标识该类为一个 Spring 组件,Spring 会自动将其注册为 Bean public class BreakExamJob implements Job { + // 自动注入 PaperService,用于处理与试卷相关的业务逻辑 @Autowired private PaperService paperService; + /** + * 定时任务执行的方法 + * + *

+ * Quartz 会在定时任务触发时调用此方法。该方法获取任务的详细信息, + * 并通过 PaperService 强制提交超时的考试。 + *

+ * + * @param jobExecutionContext 包含了 Quartz 定时任务的上下文信息 + * @throws JobExecutionException 如果任务执行过程中出现异常,将抛出该异常 + */ @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { - JobDetail detail = jobExecutionContext.getJobDetail(); - String name = detail.getKey().getName(); - String group = detail.getKey().getGroup(); + // 从 jobExecutionContext 中获取任务的详细信息 + JobDetail detail = jobExecutionContext.getJobDetail(); // 获取任务详情 + String name = detail.getKey().getName(); // 获取任务名称 + String group = detail.getKey().getGroup(); // 获取任务分组 + // 获取任务的附加数据,通常是任务触发时的相关参数 String data = String.valueOf(detail.getJobDataMap().get(JobService.TASK_DATA)); + // 打印任务执行日志,便于调试和跟踪 log.info("++++++++++定时任务:处理到期的交卷"); log.info("++++++++++jobName:{}", name); log.info("++++++++++jobGroup:{}", group); log.info("++++++++++taskData:{}", data); - - // 强制交卷 + // 调用 PaperService 进行强制交卷操作 + // data 参数通常是考试 ID 或者某种标识符,用于识别需要交卷的考试 paperService.handExam(data); - } - - - } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperMapper.java b/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperMapper.java index 1a52de5..833c518 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperMapper.java @@ -1,39 +1,46 @@ -package com.yf.exam.modules.paper.mapper; +package com.yf.exam.modules.paper.mapper; // 定义类所在的包路径 -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.yf.exam.modules.paper.dto.PaperDTO; -import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; -import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; -import com.yf.exam.modules.paper.entity.Paper; -import org.apache.ibatis.annotations.Param; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 引入 MyBatis-Plus 的 BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage; // 引入分页结果接口 IPage +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 引入分页插件 Page +import com.yf.exam.modules.paper.dto.PaperDTO; // 引入 DTO 类,表示试卷数据传输对象 +import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; // 引入请求 DTO 类,表示查询试卷时的请求参数 +import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; // 引入响应 DTO 类,表示查询试卷时的响应结果 +import com.yf.exam.modules.paper.entity.Paper; // 引入实体类,表示试卷数据表中的记录 +import org.apache.ibatis.annotations.Param; // 引入 MyBatis 注解,用于指定 SQL 查询中的参数 import java.util.List; /** -*

-* 试卷Mapper -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 16:33 -*/ -public interface PaperMapper extends BaseMapper { + *

+ * 试卷Mapper接口,用于操作试卷数据表 + *

+ * + * @author 聪明笨狗 + * @since 2020-05-25 16:33 + */ +public interface PaperMapper extends BaseMapper { // 继承 MyBatis-Plus 提供的 BaseMapper,自动实现 CRUD 操作 /** * 查找试卷分页 - * @param page - * @param query - * @return + *

+ * 根据分页参数 `Page` 和查询条件 `PaperListReqDTO` 返回试卷的分页结果。 + *

+ * + * @param page 分页参数,包含当前页、每页大小等 + * @param query 查询条件,通常包含用户 ID、考试 ID 等信息 + * @return 返回一个分页结果 `IPage`,包含了试卷数据和分页信息 */ IPage paging(Page page, @Param("query") PaperListReqDTO query); - /** * 试卷列表响应类 - * @param query - * @return + *

+ * 根据查询条件返回试卷的列表数据,通常用于返回所有试卷的列表。 + *

+ * + * @param query 查询条件,通常是试卷的基本信息,如试卷 ID、考试 ID 等 + * @return 返回一个试卷列表 `List`,每一项是试卷的响应 DTO */ List list(@Param("query") PaperDTO query); } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuAnswerMapper.java b/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuAnswerMapper.java index e795ba9..8ab65ee 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuAnswerMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuAnswerMapper.java @@ -1,27 +1,33 @@ -package com.yf.exam.modules.paper.mapper; +package com.yf.exam.modules.paper.mapper; // 指定该类所在的包路径 -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; -import com.yf.exam.modules.paper.entity.PaperQuAnswer; -import org.apache.ibatis.annotations.Param; +// 导入BaseMapper接口,提供通用的CRUD操作 +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +// 导入PaperQuAnswerExtDTO类,表示试卷考题备选答案的扩展数据传输对象 +import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; +// 导入PaperQuAnswer实体类,表示试卷考题备选答案的数据模型 +import com.yf.exam.modules.paper.entity.PaperQuAnswer; +// 导入MyBatis的注解,指定方法参数在SQL中的名称 +import org.apache.ibatis.annotations.Param; -import java.util.List; +// 导入List接口,表示返回类型为List集合,用于存储多个备选答案 +import java.util.List; /** *

-* 试卷考题备选答案Mapper +* 试卷考题备选答案Mapper接口 *

-* +* 该接口用于处理试卷考题备选答案的数据库操作,继承自MyBatis-Plus的BaseMapper,提供通用的CRUD方法。 +* * @author 聪明笨狗 * @since 2020-05-25 16:33 */ -public interface PaperQuAnswerMapper extends BaseMapper { +public interface PaperQuAnswerMapper extends BaseMapper { // 继承自BaseMapper接口,提供了所有的基本CRUD操作 /** * 查找试卷试题答案列表 - * @param paperId - * @param quId - * @return + * @param paperId 试卷ID,标识要查询的试卷 + * @param quId 题目ID,标识要查询的题目 + * @return 返回与试卷ID和题目ID相关的备选答案列表,包含更多的答案细节 */ - List list(@Param("paperId") String paperId, @Param("quId") String quId); + List list(@Param("paperId") String paperId, @Param("quId") String quId); // 根据试卷ID和题目ID查询对应的备选答案列表 } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuMapper.java b/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuMapper.java index f833a1b..a9b2257 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/mapper/PaperQuMapper.java @@ -1,42 +1,46 @@ -package com.yf.exam.modules.paper.mapper; +package com.yf.exam.modules.paper.mapper; // 定义该类所在的包路径 -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; -import com.yf.exam.modules.paper.entity.PaperQu; -import org.apache.ibatis.annotations.Param; +// 导入BaseMapper接口,提供MyBatis-Plus的通用CRUD操作 +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +// 导入PaperQuDetailDTO类,表示试卷考题详细数据传输对象 +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +// 导入PaperQu实体类,表示试卷考题的数据模型 +import com.yf.exam.modules.paper.entity.PaperQu; +// 导入MyBatis的@Param注解,用于方法参数与SQL查询中的参数进行映射 +import org.apache.ibatis.annotations.Param; -import java.util.List; +// 导入List接口,用于返回多个试题 +import java.util.List; /** *

-* 试卷考题Mapper +* 试卷考题Mapper接口 *

-* +* 该接口用于处理与试卷考题相关的数据库操作,继承自MyBatis-Plus的BaseMapper,提供通用的CRUD操作。 +* * @author 聪明笨狗 * @since 2020-05-25 16:33 */ -public interface PaperQuMapper extends BaseMapper { +public interface PaperQuMapper extends BaseMapper { // 继承自BaseMapper接口,提供通用的CRUD操作 /** * 统计客观分 - * @param paperId - * @return + * @param paperId 试卷ID,用于查询指定试卷的客观分数 + * @return 返回试卷的客观分数总和 */ - int sumObjective(@Param("paperId") String paperId); + int sumObjective(@Param("paperId") String paperId); // 根据试卷ID统计所有客观题的分数总和 /** * 统计主观分 - * @param paperId - * @return + * @param paperId 试卷ID,用于查询指定试卷的主观分数 + * @return 返回试卷的主观分数总和 */ - int sumSubjective(@Param("paperId") String paperId); + int sumSubjective(@Param("paperId") String paperId); // 根据试卷ID统计所有主观题的分数总和 /** * 找出全部试题列表 - * @param paperId - * @return + * @param paperId 试卷ID,用于查询指定试卷的所有试题 + * @return 返回试卷中的所有试题列表,类型为PaperQuDetailDTO */ - List listByPaper(@Param("paperId") String paperId); + List listByPaper(@Param("paperId") String paperId); // 根据试卷ID查询所有试题的详细信息 } - - diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuAnswerService.java b/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuAnswerService.java index a1cfffa..3d2f1d5 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuAnswerService.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuAnswerService.java @@ -1,18 +1,20 @@ +// 导入所需的包 package com.yf.exam.modules.paper.service; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.service.IService; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; -import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; -import com.yf.exam.modules.paper.entity.PaperQuAnswer; +import com.baomidou.mybatisplus.core.metadata.IPage; // 用于分页查询 +import com.baomidou.mybatisplus.extension.service.IService; // 用于继承通用服务接口 +import com.yf.exam.core.api.dto.PagingReqDTO; // 分页请求DTO +import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; // 试卷问题答案DTO +import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; // 扩展的试卷问题答案DTO +import com.yf.exam.modules.paper.entity.PaperQuAnswer; // 试卷问题答案实体类 -import java.util.List; +import java.util.List; // 导入List类,用于处理集合数据 /** *

* 试卷考题备选答案业务类 *

+* 该接口定义了与试卷考题备选答案相关的业务操作 * * @author 聪明笨狗 * @since 2020-05-25 16:33 @@ -21,24 +23,24 @@ public interface PaperQuAnswerService extends IService { /** * 分页查询数据 - * @param reqDTO - * @return + * @param reqDTO 分页请求DTO,包含查询的分页信息和条件 + * @return 返回分页查询结果,包含 PaperQuAnswerDTO 对象的数据 */ IPage paging(PagingReqDTO reqDTO); /** - * 查找试卷试题答案列表 - * @param paperId - * @param quId - * @return + * 查找试卷试题的答案列表 + * @param paperId 试卷ID,用于定位试卷 + * @param quId 试题ID,用于定位具体的试题 + * @return 返回该试卷和试题的所有答案列表,数据类型为 PaperQuAnswerExtDTO */ List listForExam(String paperId, String quId); /** - * 查找答案列表,用来填充 - * @param paperId - * @param quId - * @return + * 查找答案列表,用来填充试卷答案 + * @param paperId 试卷ID,用于定位试卷 + * @param quId 试题ID,用于定位具体试题的答案 + * @return 返回对应试题的答案列表,数据类型为 PaperQuAnswer 实体 */ List listForFill(String paperId, String quId); } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuService.java b/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuService.java index d0cc2e3..23d0af8 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuService.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperQuService.java @@ -1,18 +1,20 @@ +// 导入所需的包 package com.yf.exam.modules.paper.service; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.service.IService; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.modules.paper.dto.PaperQuDTO; -import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; -import com.yf.exam.modules.paper.entity.PaperQu; +import com.baomidou.mybatisplus.core.metadata.IPage; // 用于分页查询 +import com.baomidou.mybatisplus.extension.service.IService; // 用于继承通用服务接口 +import com.yf.exam.core.api.dto.PagingReqDTO; // 分页请求DTO +import com.yf.exam.modules.paper.dto.PaperQuDTO; // 试卷问题DTO +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; // 试卷问题详情DTO +import com.yf.exam.modules.paper.entity.PaperQu; // 试卷问题实体类 -import java.util.List; +import java.util.List; // 导入List类,用于处理集合数据 /** *

* 试卷考题业务类 *

+* 该接口定义了与试卷考题相关的业务操作 * * @author 聪明笨狗 * @since 2020-05-25 16:33 @@ -21,50 +23,50 @@ public interface PaperQuService extends IService { /** * 分页查询数据 - * @param reqDTO - * @return + * @param reqDTO 分页请求DTO,包含查询的分页信息和条件 + * @return 返回分页查询结果,包含 PaperQuDTO 对象的数据 */ IPage paging(PagingReqDTO reqDTO); /** * 根据试卷找出题目列表 - * @param paperId - * @return + * @param paperId 试卷ID,用于定位试卷 + * @return 返回该试卷的所有题目列表,数据类型为 PaperQuDTO */ List listByPaper(String paperId); /** - * 查找详情 - * @param paperId - * @param quId - * @return + * 查找试卷中某个问题的详细信息 + * @param paperId 试卷ID + * @param quId 试题ID,用于定位具体的试题 + * @return 返回该试卷和试题的详细信息,数据类型为 PaperQu */ PaperQu findByKey(String paperId, String quId); /** - * 根据组合索引更新 - * @param qu + * 根据组合索引更新试题 + * @param qu 试题实体,用于更新试卷中的试题 */ void updateByKey(PaperQu qu); /** - * 统计客观分 - * @param paperId - * @return + * 统计试卷中所有客观题的总分 + * @param paperId 试卷ID + * @return 返回该试卷客观题的总分 */ int sumObjective(String paperId); /** - * 统计主观分 - * @param paperId - * @return + * 统计试卷中所有主观题的总分 + * @param paperId 试卷ID + * @return 返回该试卷主观题的总分 */ int sumSubjective(String paperId); /** - * 找出全部试题列表 - * @param paperId - * @return + * 查找试卷的全部试题列表(用于展示结果) + * @param paperId 试卷ID + * @return 返回该试卷的详细试题列表,数据类型为 PaperQuDetailDTO */ List listForPaperResult(String paperId); } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperService.java b/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperService.java index 042bb7d..ec5f724 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperService.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/service/PaperService.java @@ -1,21 +1,23 @@ +// 导入所需的包 package com.yf.exam.modules.paper.service; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.service.IService; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.modules.paper.dto.PaperDTO; -import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; -import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO; -import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; -import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO; -import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO; -import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; -import com.yf.exam.modules.paper.entity.Paper; +import com.baomidou.mybatisplus.core.metadata.IPage; // 用于分页查询 +import com.baomidou.mybatisplus.extension.service.IService; // 用于继承通用服务接口 +import com.yf.exam.core.api.dto.PagingReqDTO; // 分页请求DTO +import com.yf.exam.modules.paper.dto.PaperDTO; // 试卷DTO +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; // 试卷题目详情DTO +import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO; // 提交答案请求DTO +import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; // 试卷列表请求DTO +import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO; // 考试详情响应DTO +import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO; // 考试结果响应DTO +import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; // 试卷列表响应DTO +import com.yf.exam.modules.paper.entity.Paper; // 试卷实体类 /** *

* 试卷业务类 *

+* 该接口定义了与试卷相关的所有业务操作 * * @author 聪明笨狗 * @since 2020-05-25 16:33 @@ -24,60 +26,57 @@ public interface PaperService extends IService { /** * 创建试卷 - * @param userId - * @param examId - * @return + * @param userId 用户ID,表示试卷创建者 + * @param examId 考试ID,表示该试卷属于哪个考试 + * @return 返回创建的试卷ID */ String createPaper(String userId, String examId); - /** - * 查找详情 - * @param paperId - * @return + * 查找试卷详情 + * @param paperId 试卷ID,表示要查找的试卷 + * @return 返回试卷的详细信息,数据类型为 ExamDetailRespDTO */ ExamDetailRespDTO paperDetail(String paperId); /** - * 考试结果 - * @param paperId - * @return + * 获取考试结果 + * @param paperId 试卷ID,表示要查询结果的试卷 + * @return 返回该试卷的考试结果,数据类型为 ExamResultRespDTO */ ExamResultRespDTO paperResult(String paperId); /** - * 查找题目详情 - * @param paperId - * @param quId - * @return + * 查找试卷题目详情 + * @param paperId 试卷ID + * @param quId 题目ID,表示要查询的具体题目 + * @return 返回该试卷和题目的详细信息,数据类型为 PaperQuDetailDTO */ PaperQuDetailDTO findQuDetail(String paperId, String quId); /** - * 填充答案 - * @param reqDTO + * 填充试卷答案 + * @param reqDTO 填写答案的请求DTO,包含试卷答案信息 */ void fillAnswer(PaperAnswerDTO reqDTO); /** - * 交卷操作 - * @param paperId - * @return + * 提交试卷(交卷) + * @param paperId 试卷ID,表示要交卷的试卷 */ void handExam(String paperId); /** - * 试卷列表响应类 - * @param reqDTO - * @return + * 分页查询试卷列表 + * @param reqDTO 分页请求DTO,包含分页信息和查询条件 + * @return 返回分页后的试卷列表,数据类型为 IPage */ IPage paging(PagingReqDTO reqDTO); /** - * 检测是否有进行中的考试 - * @param userId - * @return + * 检查用户是否有正在进行的考试 + * @param userId 用户ID,用于查找该用户是否有未完成的考试 + * @return 返回该用户正在进行的考试试卷信息,数据类型为 PaperDTO */ PaperDTO checkProcess(String userId); - } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuAnswerServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuAnswerServiceImpl.java index c2dc0b7..14e8306 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuAnswerServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuAnswerServiceImpl.java @@ -1,61 +1,95 @@ -package com.yf.exam.modules.paper.service.impl; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; -import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; -import com.yf.exam.modules.paper.entity.PaperQuAnswer; -import com.yf.exam.modules.paper.mapper.PaperQuAnswerMapper; -import com.yf.exam.modules.paper.service.PaperQuAnswerService; -import org.springframework.stereotype.Service; - -import java.util.List; +package com.yf.exam.modules.paper.service.impl; // 指定该类所在的包路径 + +// 导入FastJSON库,用于处理JSON格式数据的转换 +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +// 导入MyBatis-Plus的条件查询构造器 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +// 导入分页接口 +import com.baomidou.mybatisplus.core.metadata.IPage; +// 导入分页Page类 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +// 导入MyBatis-Plus的服务实现类 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +// 导入分页请求DTO,用于分页查询 +import com.yf.exam.core.api.dto.PagingReqDTO; +// 导入试卷考题备选答案DTO类 +import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; +// 导入试卷考题备选答案扩展DTO类 +import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; +// 导入试卷考题备选答案实体类 +import com.yf.exam.modules.paper.entity.PaperQuAnswer; +// 导入试卷考题备选答案Mapper接口 +import com.yf.exam.modules.paper.mapper.PaperQuAnswerMapper; +// 导入试卷考题备选答案服务接口 +import com.yf.exam.modules.paper.service.PaperQuAnswerService; +// 导入Spring的Service注解,表示这是一个服务实现类 +import org.springframework.stereotype.Service; + +import java.util.List; // 导入List接口,用于返回多个结果 /** *

-* 语言设置 服务实现类 +* 试卷考题备选答案 服务实现类 *

-* +* 该类实现了PaperQuAnswerService接口,处理与试卷考题备选答案相关的业务逻辑。 +* * @author 聪明笨狗 * @since 2020-05-25 16:33 */ -@Service -public class PaperQuAnswerServiceImpl extends ServiceImpl implements PaperQuAnswerService { +@Service // 标注为Spring的服务类 +public class PaperQuAnswerServiceImpl extends ServiceImpl implements PaperQuAnswerService { // 继承ServiceImpl类,提供基本的数据库操作功能 + /** + * 分页查询试卷考题备选答案 + * @param reqDTO 分页请求参数,包括当前页和页面大小 + * @return 分页后的试卷考题备选答案列表 + */ @Override public IPage paging(PagingReqDTO reqDTO) { - //创建分页对象 + // 创建分页对象 IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); - //查询条件 + // 查询条件,可以根据需要添加更多过滤条件 QueryWrapper wrapper = new QueryWrapper<>(); - //获得数据 + // 执行分页查询操作 IPage page = this.page(query, wrapper); - //转换结果 + + // 将查询结果转换为DTO对象,返回分页后的结果 IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); - return pageData; - } + + return pageData; // 返回分页结果 + } + /** + * 根据试卷ID和题目ID查询试题的备选答案列表 + * @param paperId 试卷ID + * @param quId 题目ID + * @return 试卷考题备选答案的扩展DTO列表 + */ @Override public List listForExam(String paperId, String quId) { + // 调用Mapper中的list方法,查询并返回试题的备选答案列表 return baseMapper.list(paperId, quId); } + /** + * 查询填空题的备选答案 + * @param paperId 试卷ID + * @param quId 题目ID + * @return 填空题的备选答案列表 + */ @Override public List listForFill(String paperId, String quId) { - //查询条件 + // 创建查询条件 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda() - .eq(PaperQuAnswer::getPaperId, paperId) - .eq(PaperQuAnswer::getQuId, quId); + wrapper.lambda() // 使用Lambda表达式,进行条件构造 + .eq(PaperQuAnswer::getPaperId, paperId) // 通过试卷ID过滤 + .eq(PaperQuAnswer::getQuId, quId); // 通过题目ID过滤 + // 查询并返回符合条件的结果列表 return this.list(wrapper); } } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuServiceImpl.java index a0d9a1d..46c9635 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperQuServiceImpl.java @@ -1,94 +1,150 @@ -package com.yf.exam.modules.paper.service.impl; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.core.utils.BeanMapper; -import com.yf.exam.modules.paper.dto.PaperQuDTO; -import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; -import com.yf.exam.modules.paper.entity.PaperQu; -import com.yf.exam.modules.paper.mapper.PaperQuMapper; -import com.yf.exam.modules.paper.service.PaperQuService; -import org.springframework.stereotype.Service; - -import java.util.List; +package com.yf.exam.modules.paper.service.impl; // 指定该类所在的包路径 + +// 导入FastJSON库,用于处理JSON格式数据的转换 +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +// 导入MyBatis-Plus的条件查询构造器 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +// 导入分页接口 +import com.baomidou.mybatisplus.core.metadata.IPage; +// 导入分页Page类 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +// 导入MyBatis-Plus的服务实现类 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +// 导入分页请求DTO,用于分页查询 +import com.yf.exam.core.api.dto.PagingReqDTO; +// 导入BeanMapper工具类,用于对象间的转换 +import com.yf.exam.core.utils.BeanMapper; +// 导入试卷考题DTO类 +import com.yf.exam.modules.paper.dto.PaperQuDTO; +// 导入试卷考题详情DTO类 +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +// 导入试卷考题实体类 +import com.yf.exam.modules.paper.entity.PaperQu; +// 导入试卷考题Mapper接口 +import com.yf.exam.modules.paper.mapper.PaperQuMapper; +// 导入试卷考题服务接口 +import com.yf.exam.modules.paper.service.PaperQuService; +// 导入Spring的Service注解,表示这是一个服务实现类 +import org.springframework.stereotype.Service; + +import java.util.List; // 导入List接口,用于返回多个结果 /** *

-* 语言设置 服务实现类 +* 试卷考题 服务实现类 *

-* +* 该类实现了PaperQuService接口,处理与试卷考题相关的业务逻辑。 +* * @author 聪明笨狗 * @since 2020-05-25 16:33 */ -@Service -public class PaperQuServiceImpl extends ServiceImpl implements PaperQuService { - +@Service // 标注为Spring的服务类 +public class PaperQuServiceImpl extends ServiceImpl implements PaperQuService { // 继承ServiceImpl类,提供基本的数据库操作功能 + + /** + * 分页查询试卷考题 + * @param reqDTO 分页请求参数,包括当前页和页面大小 + * @return 分页后的试卷考题列表 + */ @Override public IPage paging(PagingReqDTO reqDTO) { - //创建分页对象 + // 创建分页对象 IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); - //查询条件 + // 查询条件 QueryWrapper wrapper = new QueryWrapper<>(); - //获得数据 + // 执行分页查询操作 IPage page = this.page(query, wrapper); - //转换结果 + + // 将查询结果转换为DTO对象,返回分页后的结果 IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); - return pageData; - } + + return pageData; // 返回分页结果 + } + /** + * 根据试卷ID查询试卷考题列表 + * @param paperId 试卷ID + * @return 试卷考题DTO列表 + */ @Override public List listByPaper(String paperId) { - //查询条件 + // 查询条件 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(PaperQu::getPaperId, paperId) - .orderByAsc(PaperQu::getSort); + wrapper.lambda().eq(PaperQu::getPaperId, paperId) // 通过试卷ID过滤 + .orderByAsc(PaperQu::getSort); // 按照题目排序字段升序排列 + // 执行查询,获取试卷考题列表 List list = this.list(wrapper); + + // 使用BeanMapper工具类将实体对象列表转换为DTO对象列表 return BeanMapper.mapList(list, PaperQuDTO.class); } + /** + * 根据试卷ID和题目ID查找单个试卷考题 + * @param paperId 试卷ID + * @param quId 题目ID + * @return 找到的试卷考题对象 + */ @Override public PaperQu findByKey(String paperId, String quId) { - //查询条件 + // 查询条件 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(PaperQu::getPaperId, paperId) - .eq(PaperQu::getQuId, quId); + wrapper.lambda().eq(PaperQu::getPaperId, paperId) // 通过试卷ID过滤 + .eq(PaperQu::getQuId, quId); // 通过题目ID过滤 - return this.getOne(wrapper, false); + // 获取匹配的单个试卷考题对象 + return this.getOne(wrapper, false); // 返回查询到的结果,false表示未查询到时返回null } + /** + * 根据试卷ID和题目ID更新试卷考题 + * @param qu 需要更新的试卷考题对象 + */ @Override public void updateByKey(PaperQu qu) { - //查询条件 + // 查询条件 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(PaperQu::getPaperId, qu.getPaperId()) - .eq(PaperQu::getQuId, qu.getQuId()); + wrapper.lambda().eq(PaperQu::getPaperId, qu.getPaperId()) // 通过试卷ID过滤 + .eq(PaperQu::getQuId, qu.getQuId()); // 通过题目ID过滤 - this.update(qu, wrapper); + // 执行更新操作 + this.update(qu, wrapper); // 更新满足条件的试卷考题 } + /** + * 统计试卷中的客观题分数 + * @param paperId 试卷ID + * @return 客观题的总分 + */ @Override public int sumObjective(String paperId) { - return baseMapper.sumObjective(paperId); + return baseMapper.sumObjective(paperId); // 调用Mapper方法,统计客观题总分 } + /** + * 统计试卷中的主观题分数 + * @param paperId 试卷ID + * @return 主观题的总分 + */ @Override public int sumSubjective(String paperId) { - return baseMapper.sumSubjective(paperId); + return baseMapper.sumSubjective(paperId); // 调用Mapper方法,统计主观题总分 } + /** + * 根据试卷ID查询试卷考题的详细信息 + * @param paperId 试卷ID + * @return 试卷考题详细信息列表 + */ @Override public List listForPaperResult(String paperId) { - return baseMapper.listByPaper(paperId); + return baseMapper.listByPaper(paperId); // 调用Mapper方法,获取试卷考题详细信息 } } diff --git a/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperServiceImpl.java index 2eefec8..8f28028 100644 --- a/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/paper/service/impl/PaperServiceImpl.java @@ -1,103 +1,167 @@ +// 定义包名,表示该类属于com.yf.exam.modules.paper.service.impl包下 package com.yf.exam.modules.paper.service.impl; +// 导入MyBatis Plus框架的核心类,用于构建查询条件 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +// 导入MyBatis Plus框架的分页功能相关类 import com.baomidou.mybatisplus.core.metadata.IPage; +// 导入MyBatis Plus框架的工具类,用于生成唯一ID import com.baomidou.mybatisplus.core.toolkit.IdWorker; +// 导入MyBatis Plus框架的服务实现类 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +// 导入项目中定义的作业组枚举 import com.yf.exam.ability.job.enums.JobGroup; +// 导入项目中定义的作业前缀枚举 import com.yf.exam.ability.job.enums.JobPrefix; +// 导入项目中的作业服务接口 import com.yf.exam.ability.job.service.JobService; +// 导入项目中定义的API错误码类 import com.yf.exam.core.api.ApiError; +// 导入项目中定义的DTO类,用于分页请求 import com.yf.exam.core.api.dto.PagingReqDTO; +// 导入项目中的服务异常类 import com.yf.exam.core.exception.ServiceException; +// 导入项目中定义的Bean映射工具类 import com.yf.exam.core.utils.BeanMapper; +// 导入项目中定义的Cron表达式工具类 import com.yf.exam.core.utils.CronUtils; +// 导入项目中定义的考试DTO类 import com.yf.exam.modules.exam.dto.ExamDTO; +// 导入项目中定义的考试题库DTO类 import com.yf.exam.modules.exam.dto.ExamRepoDTO; +// 导入项目中定义的扩展考试题库DTO类 import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +// 导入项目中的考试服务接口 import com.yf.exam.modules.exam.service.ExamRepoService; +// 导入项目中的考试服务接口 import com.yf.exam.modules.exam.service.ExamService; +// 导入项目中定义的试卷DTO类 import com.yf.exam.modules.paper.dto.PaperDTO; +// 导入项目中定义的试卷题目DTO类 import com.yf.exam.modules.paper.dto.PaperQuDTO; +// 导入项目中定义的扩展试卷题目答案DTO类 import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; +// 导入项目中定义的试卷题目详情DTO类 import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +// 导入项目中定义的试卷答案DTO类 import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO; +// 导入项目中定义的试卷列表请求DTO类 import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; +// 导入项目中定义的试卷列表响应DTO类 import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO; +// 导入项目中定义的考试结果响应DTO类 import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO; +// 导入项目中定义的试卷列表响应DTO类 import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; +// 导入项目中定义的试卷实体类 import com.yf.exam.modules.paper.entity.Paper; +// 导入项目中定义的试卷题目实体类 import com.yf.exam.modules.paper.entity.PaperQu; +// 导入项目中定义的试卷题目答案实体类 import com.yf.exam.modules.paper.entity.PaperQuAnswer; +// 导入项目中定义的考试状态枚举 import com.yf.exam.modules.paper.enums.ExamState; +// 导入项目中定义的试卷状态枚举 import com.yf.exam.modules.paper.enums.PaperState; +// 导入项目中定义的强制交卷作业类 import com.yf.exam.modules.paper.job.BreakExamJob; +// 导入项目中定义的试卷Mapper接口 import com.yf.exam.modules.paper.mapper.PaperMapper; +// 导入项目中定义的试卷题目服务接口 import com.yf.exam.modules.paper.service.PaperQuAnswerService; +// 导入项目中定义的试卷题目服务接口 import com.yf.exam.modules.paper.service.PaperQuService; +// 导入项目中定义的试卷服务接口 import com.yf.exam.modules.paper.service.PaperService; +// 导入项目中定义的题目实体类 import com.yf.exam.modules.qu.entity.Qu; +// 导入项目中定义的题目答案实体类 import com.yf.exam.modules.qu.entity.QuAnswer; +// 导入项目中定义的题目类型枚举 import com.yf.exam.modules.qu.enums.QuType; +// 导入项目中定义的题目服务接口 import com.yf.exam.modules.qu.service.QuAnswerService; +// 导入项目中定义的题目服务接口 import com.yf.exam.modules.qu.service.QuService; +// 导入项目中定义的系统用户实体类 import com.yf.exam.modules.sys.user.entity.SysUser; +// 导入项目中定义的系统用户服务接口 import com.yf.exam.modules.sys.user.service.SysUserService; +// 导入项目中定义的用户书籍服务接口 import com.yf.exam.modules.user.book.service.UserBookService; +// 导入项目中定义的用户考试服务接口 import com.yf.exam.modules.user.exam.service.UserExamService; +// 导入Apache Commons Lang库中的StringUtils类,用于字符串操作 import org.apache.commons.lang3.StringUtils; +// 导入Spring框架中的注解,用于自动注入依赖 import org.springframework.beans.factory.annotation.Autowired; +// 导入Spring框架中的注解,用于声明服务组件 import org.springframework.stereotype.Service; +// 导入Spring框架中的注解,用于声明事务管理 import org.springframework.transaction.annotation.Transactional; +// 导入Spring框架中的类,用于工具操作 import org.springframework.util.CollectionUtils; +// 导入Java.util包下的类,用于集合操作 import java.util.*; +// 定义语言设置服务实现类,继承自ServiceImpl,并实现PaperService接口 /** -*

-* 语言设置 服务实现类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 16:33 -*/ + *

+ * 语言设置 服务实现类 + *

+ * + * @author 聪明笨狗 + * @since 2020-05-25 16:33 + */ @Service public class PaperServiceImpl extends ServiceImpl implements PaperService { - + // 自动注入系统用户服务 @Autowired private SysUserService sysUserService; + // 自动注入考试服务 @Autowired private ExamService examService; + // 自动注入题目服务 @Autowired private QuService quService; + // 自动注入题目答案服务 @Autowired private QuAnswerService quAnswerService; + // 自动注入试卷服务 @Autowired private PaperService paperService; + // 自动注入试卷题目服务 @Autowired private PaperQuService paperQuService; + // 自动注入试卷题目答案服务 @Autowired private PaperQuAnswerService paperQuAnswerService; + // 自动注入用户书籍服务 @Autowired private UserBookService userBookService; + // 自动注入考试题库服务 @Autowired private ExamRepoService examRepoService; + // 自动注入用户考试服务 @Autowired private UserExamService userExamService; + // 自动注入作业服务 @Autowired private JobService jobService; + // 定义展示的选项,如ABC这样的选项列表 /** * 展示的选项,ABC这样 */ @@ -106,437 +170,503 @@ public class PaperServiceImpl extends ServiceImpl implements ,"Y","Z" }); - - - - + // 声明创建试卷的方法,使用事务管理,如果发生异常则回滚 + /** + * 创建试卷的方法 + * @param userId 用户ID + * @param examId 考试ID + * @return 返回试卷ID + */ @Transactional(rollbackFor = Exception.class) @Override public String createPaper(String userId, String examId) { - // 校验是否有正在考试的试卷 + // 构建查询条件,查询是否有正在考试的试卷 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda() .eq(Paper::getUserId, userId) .eq(Paper::getState, PaperState.ING); + // 统计符合条件的试卷数量 int exists = this.count(wrapper); - + // 如果存在正在考试的试卷,则抛出服务异常 if (exists > 0) { throw new ServiceException(ApiError.ERROR_20010002); } - // 查找考试 + // 根据考试ID查询考试信息 ExamDTO exam = examService.findById(examId); + // 如果考试信息不存在,则抛出服务异常 if(exam == null){ throw new ServiceException(1, "考试不存在!"); } + // 如果考试状态不正确,则抛出服务异常 if(!ExamState.ENABLE.equals(exam.getState())){ throw new ServiceException(1, "考试状态不正确!"); } - // 考试题目列表 - List quList = this.generateByRepo(examId); - - if(CollectionUtils.isEmpty(quList)){ - throw new ServiceException(1, "规则不正确,无对应的考题!"); - } - - //保存试卷内容 - Paper paper = this.savePaper(userId, exam, quList); + // 根据考试ID生成试卷题目列表 - // 强制交卷任务 - String jobName = JobPrefix.BREAK_EXAM + paper.getId(); - jobService.addCronJob(BreakExamJob.class, jobName, CronUtils.dateToCron(paper.getLimitTime()), paper.getId()); + List quList = this.generateByRepo(examId); - return paper.getId(); + // 如果题目列表为空,则抛出服务异常 + if(CollectionUtils.isEmpty(quList)){ + throw new ServiceException(1, "规则不正确,无对应的考题!"); } - @Override - public ExamDetailRespDTO paperDetail(String paperId) { - - - ExamDetailRespDTO respDTO = new ExamDetailRespDTO(); + // 保存试卷内容,并返回试卷ID + Paper paper = this.savePaper(userId, exam, quList); - // 试题基本信息 - Paper paper = paperService.getById(paperId); - BeanMapper.copy(paper, respDTO); + // 添加强制交卷任务 + String jobName = JobPrefix.BREAK_EXAM + paper.getId(); + jobService.addCronJob(BreakExamJob.class, jobName, CronUtils.dateToCron(paper.getLimitTime()), paper.getId()); - // 查找题目列表 - List list = paperQuService.listByPaper(paperId); + return paper.getId(); +} - List radioList = new ArrayList<>(); - List multiList = new ArrayList<>(); - List judgeList = new ArrayList<>(); - for(PaperQuDTO item: list){ - if(QuType.RADIO.equals(item.getQuType())){ - radioList.add(item); - } - if(QuType.MULTI.equals(item.getQuType())){ - multiList.add(item); - } - if(QuType.JUDGE.equals(item.getQuType())){ - judgeList.add(item); - } +// 声明查询试卷详情的方法 +/** + * 查询试卷详情的方法 + * @param paperId 试卷ID + * @return 返回考试详情响应DTO + */ +@Override +public ExamDetailRespDTO paperDetail(String paperId) { + + // 创建考试详情响应DTO对象 + ExamDetailRespDTO respDTO = new ExamDetailRespDTO(); + + // 根据试卷ID查询试卷基本信息,并复制到响应DTO中 + Paper paper = paperService.getById(paperId); + BeanMapper.copy(paper, respDTO); + + // 根据试卷ID查询题目列表 + List list = paperQuService.listByPaper(paperId); + + // 分类题目列表 + List radioList = new ArrayList<>(); + List multiList = new ArrayList<>(); + List judgeList = new ArrayList<>(); + for(PaperQuDTO item: list){ + if(QuType.RADIO.equals(item.getQuType())){ + radioList.add(item); + } + if(QuType.MULTI.equals(item.getQuType())){ + multiList.add(item); + } + if(QuType.JUDGE.equals(item.getQuType())){ + judgeList.add(item); } - - respDTO.setRadioList(radioList); - respDTO.setMultiList(multiList); - respDTO.setJudgeList(judgeList); - return respDTO; - } - - @Override - public ExamResultRespDTO paperResult(String paperId) { - - ExamResultRespDTO respDTO = new ExamResultRespDTO(); - - // 试题基本信息 - Paper paper = paperService.getById(paperId); - BeanMapper.copy(paper, respDTO); - - List quList = paperQuService.listForPaperResult(paperId); - respDTO.setQuList(quList); - - return respDTO; } - @Override - public PaperQuDetailDTO findQuDetail(String paperId, String quId) { - - PaperQuDetailDTO respDTO = new PaperQuDetailDTO(); - // 问题 - Qu qu = quService.getById(quId); - - // 基本信息 - PaperQu paperQu = paperQuService.findByKey(paperId, quId); - BeanMapper.copy(paperQu, respDTO); - respDTO.setContent(qu.getContent()); - respDTO.setImage(qu.getImage()); - - // 答案列表 - List list = paperQuAnswerService.listForExam(paperId, quId); - respDTO.setAnswerList(list); - - return respDTO; - } + // 设置分类后的题目列表到响应DTO中 + respDTO.setRadioList(radioList); + respDTO.setMultiList(multiList); + respDTO.setJudgeList(judgeList); + return respDTO; +} +// 声明查询试卷结果的方法 +/** + * 查询试卷结果的方法 + * @param paperId 试卷ID + * @return 返回考试结果响应DTO + */ +@Override +public ExamResultRespDTO paperResult(String paperId) { - /** - * 题库组题方式产生题目列表 - * @param examId - * @return - */ - private List generateByRepo(String examId){ + // 创建考试结果响应DTO对象 + ExamResultRespDTO respDTO = new ExamResultRespDTO(); - // 查找规则指定的题库 - List list = examRepoService.listByExam(examId); + // 根据试卷ID查询试卷基本信息,并复制到响应DTO中 + Paper paper = paperService.getById(paperId); + BeanMapper.copy(paper, respDTO); - //最终的题目列表 - List quList = new ArrayList<>(); + // 根据试卷ID查询题目列表 + List quList = paperQuService.listForPaperResult(paperId); + respDTO.setQuList(quList); - //排除ID,避免题目重复 - List excludes = new ArrayList<>(); - excludes.add("none"); + return respDTO; +} - if (!CollectionUtils.isEmpty(list)) { - for (ExamRepoExtDTO item : list) { +// 声明查询题目详情的方法 +/** + * 查询题目详情的方法 + * @param paperId 试卷ID + * @param quId 题目ID + * @return 返回题目详情DTO + */ +@Override +public PaperQuDetailDTO findQuDetail(String paperId, String quId) { + + // 创建题目详情DTO对象 + PaperQuDetailDTO respDTO = new PaperQuDetailDTO(); + // 根据题目ID查询题目信息 + Qu qu = quService.getById(quId); + + // 根据试卷ID和题目ID查询试卷题目信息,并复制到响应DTO中 + PaperQu paperQu = paperQuService.findByKey(paperId, quId); + BeanMapper.copy(paperQu, respDTO); + respDTO.setContent(qu.getContent()); + respDTO.setImage(qu.getImage()); + + // 根据试卷ID和题目ID查询答案列表,并设置到响应DTO中 + List list = paperQuAnswerService.listForExam(paperId, quId); + respDTO.setAnswerList(list); + + return respDTO; +} - // 单选题 - if(item.getRadioCount() > 0){ - List radioList = quService.listByRandom(item.getRepoId(), QuType.RADIO, excludes, item.getRadioCount()); - for (Qu qu : radioList) { - PaperQu paperQu = this.processPaperQu(item, qu); - quList.add(paperQu); - excludes.add(qu.getId()); - } +// 声明题库组题方式产生题目列表的私有方法 +/** + * 题库组题方式产生题目列表的方法 + * @param examId 考试ID + * @return 返回题目列表 + */ +private List generateByRepo(String examId){ + + // 查询规则指定的题库 + List list = examRepoService.listByExam(examId); + + // 最终的题目列表 + List quList = new ArrayList<>(); + + // 排除ID,避免题目重复 + List excludes = new ArrayList<>(); + excludes.add("none"); + + // 如果题库列表不为空,则进行题目抽取 + if (!CollectionUtils.isEmpty(list)) { + for (ExamRepoExtDTO item : list) { + + // 抽取单选题 + if(item.getRadioCount() > 0){ + List radioList = quService.listByRandom(item.getRepoId(), QuType.RADIO, excludes, item.getRadioCount()); + for (Qu qu : radioList) { + PaperQu paperQu = this.processPaperQu(item, qu); + quList.add(paperQu); + excludes.add(qu.getId()); } + } - //多选题 - if(item.getMultiCount() > 0) { - List multiList = quService.listByRandom(item.getRepoId(), QuType.MULTI, excludes, - item.getMultiCount()); - for (Qu qu : multiList) { - PaperQu paperQu = this.processPaperQu(item, qu); - quList.add(paperQu); - excludes.add(qu.getId()); - } + // 抽取多选题 + if(item.getMultiCount() > 0) { + List multiList = quService.listByRandom(item.getRepoId(), QuType.MULTI, excludes, + item.getMultiCount()); + for (Qu qu : multiList) { + PaperQu paperQu = this.processPaperQu(item, qu); + quList.add(paperQu); + excludes.add(qu.getId()); } + } - // 判断题 - if(item.getJudgeCount() > 0) { - List judgeList = quService.listByRandom(item.getRepoId(), QuType.JUDGE, excludes, - item.getJudgeCount()); - for (Qu qu : judgeList) { - PaperQu paperQu = this.processPaperQu(item, qu); - quList.add(paperQu); - excludes.add(qu.getId()); - } + // 抽取判断题 + if(item.getJudgeCount() > 0) { + List judgeList = quService.listByRandom(item.getRepoId(), QuType.JUDGE, excludes, + item.getJudgeCount()); + for (Qu qu : judgeList) { + PaperQu paperQu = this.processPaperQu(item, qu); + quList.add(paperQu); + excludes.add(qu.getId()); } } } - return quList; } + return quList; +} +// 声明填充试题题目信息的私有方法 +/** + * 填充试题题目信息的方法 + * @param repo 考试题库DTO + * @param qu 题目实体 + * @return 返回试卷题目实体 + */ +private PaperQu processPaperQu(ExamRepoDTO repo, Qu qu) { + + // 创建试卷题目实体 + PaperQu paperQu = new PaperQu(); + paperQu.setQuId(qu.getId()); + paperQu.setAnswered(false); + paperQu.setIsRight(false); + paperQu.setQuType(qu.getQuType()); + + // 设置单选题分数 + if (QuType.RADIO.equals(qu.getQuType())) { + paperQu.setScore(repo.getRadioScore()); + paperQu.setActualScore(repo.getRadioScore()); + } - - /** - * 填充试题题目信息 - * @param repo - * @param qu - * @return - */ - private PaperQu processPaperQu(ExamRepoDTO repo, Qu qu) { - - //保存试题信息 - PaperQu paperQu = new PaperQu(); - paperQu.setQuId(qu.getId()); - paperQu.setAnswered(false); - paperQu.setIsRight(false); - paperQu.setQuType(qu.getQuType()); - - if (QuType.RADIO.equals(qu.getQuType())) { - paperQu.setScore(repo.getRadioScore()); - paperQu.setActualScore(repo.getRadioScore()); - } - - if (QuType.MULTI.equals(qu.getQuType())) { - paperQu.setScore(repo.getMultiScore()); - paperQu.setActualScore(repo.getMultiScore()); - } - - if (QuType.JUDGE.equals(qu.getQuType())) { - paperQu.setScore(repo.getJudgeScore()); - paperQu.setActualScore(repo.getJudgeScore()); - } - - return paperQu; + // 设置多选题分数 + if (QuType.MULTI.equals(qu.getQuType())) { + paperQu.setScore(repo.getMultiScore()); + paperQu.setActualScore(repo.getMultiScore()); } + // 设置判断题分数 + if (QuType.JUDGE.equals(qu.getQuType())) { + paperQu.setScore(repo.getJudgeScore()); + paperQu.setActualScore(repo.getJudgeScore()); + } - /** - * 保存试卷 - * @param userId - * @param exam - * @param quList - * @return - */ - private Paper savePaper(String userId, ExamDTO exam, List quList) { - - - // 查找用户 - SysUser user = sysUserService.getById(userId); - - //保存试卷基本信息 - Paper paper = new Paper(); - paper.setDepartId(user.getDepartId()); - paper.setExamId(exam.getId()); - paper.setTitle(exam.getTitle()); - paper.setTotalScore(exam.getTotalScore()); - paper.setTotalTime(exam.getTotalTime()); - paper.setUserScore(0); - paper.setUserId(userId); - paper.setCreateTime(new Date()); - paper.setUpdateTime(new Date()); - paper.setQualifyScore(exam.getQualifyScore()); - paper.setState(PaperState.ING); - paper.setHasSaq(false); - - // 截止时间 - Calendar cl = Calendar.getInstance(); - cl.setTimeInMillis(System.currentTimeMillis()); - cl.add(Calendar.MINUTE, exam.getTotalTime()); - paper.setLimitTime(cl.getTime()); - - paperService.save(paper); - - if (!CollectionUtils.isEmpty(quList)) { - this.savePaperQu(paper.getId(), quList); - } + return paperQu; +} - return paper; +// 声明保存试卷的私有方法 +/** + * 保存试卷的方法 + * @param userId 用户ID + * @param exam 考试DTO + * @param quList 题目列表 + * @return 返回试卷实体 + */ +private Paper savePaper(String userId, ExamDTO exam, List quList) { + + + // 根据用户ID查询用户信息 + SysUser user = sysUserService.getById(userId); + + // 创建试卷基本信息,并设置属性 + Paper paper = new Paper(); + paper.setDepartId(user.getDepartId()); + paper.setExamId(exam.getId()); + paper.setTitle(exam.getTitle()); + paper.setTotalScore(exam.getTotalScore()); + paper.setTotalTime(exam.getTotalTime()); + paper.setUserScore(0); + paper.setUserId(userId); + paper.setCreateTime(new Date()); + paper.setUpdateTime(new Date()); + paper.setQualifyScore(exam.getQualifyScore()); + paper.setState(PaperState.ING); + paper.setHasSaq(false); + + // 计算截止时间 + Calendar cl = Calendar.getInstance(); + cl.setTimeInMillis(System.currentTimeMillis()); + cl.add(Calendar.MINUTE, exam.getTotalTime()); + paper.setLimitTime(cl.getTime()); + + // 保存试卷基本信息 + paperService.save(paper); + + // 如果题目列表不为空,则保存试卷题目列表 + if (!CollectionUtils.isEmpty(quList)) { + this.savePaperQu(paper.getId(), quList); } + return paper; +} - - /** - * 保存试卷试题列表 - * @param paperId - * @param quList - */ - private void savePaperQu(String paperId, List quList){ - - List batchQuList = new ArrayList<>(); - List batchAnswerList = new ArrayList<>(); - - int sort = 0; - for (PaperQu item : quList) { - - item.setPaperId(paperId); - item.setSort(sort); - item.setId(IdWorker.getIdStr()); - - //回答列表 - List answerList = quAnswerService.listAnswerByRandom(item.getQuId()); - - if (!CollectionUtils.isEmpty(answerList)) { - - int ii = 0; - for (QuAnswer answer : answerList) { - PaperQuAnswer paperQuAnswer = new PaperQuAnswer(); - paperQuAnswer.setId(UUID.randomUUID().toString()); - paperQuAnswer.setPaperId(paperId); - paperQuAnswer.setQuId(answer.getQuId()); - paperQuAnswer.setAnswerId(answer.getId()); - paperQuAnswer.setChecked(false); - paperQuAnswer.setSort(ii); - paperQuAnswer.setAbc(ABC.get(ii)); - paperQuAnswer.setIsRight(answer.getIsRight()); - ii++; - batchAnswerList.add(paperQuAnswer); - } +// 声明保存试卷试题列表的私有方法 +/** + * 保存试卷试题列表的方法 + * @param paperId 试卷ID + * @param quList 题目列表 + */ +private void savePaperQu(String paperId, List quList){ + + // 创建批量保存的题目列表和答案列表 + List batchQuList = new ArrayList<>(); + List batchAnswerList = new ArrayList<>(); + + // 初始化排序号 + int sort = 0; + for (PaperQu item : quList) { + + // 设置试卷ID和排序号,并生成ID + item.setPaperId(paperId); + item.setSort(sort); + item.setId(IdWorker.getIdStr()); + + // 查询题目的答案列表 + List answerList = quAnswerService.listAnswerByRandom(item.getQuId()); + + // 如果答案列表不为空,则进行处理 + if (!CollectionUtils.isEmpty(answerList)) { + + // 初始化答案排序号 + int ii = 0; + for (QuAnswer answer : answerList) { + PaperQuAnswer paperQuAnswer = new PaperQuAnswer(); + paperQuAnswer.setId(UUID.randomUUID().toString()); + paperQuAnswer.setPaperId(paperId); + paperQuAnswer.setQuId(answer.getQuId()); + paperQuAnswer.setAnswerId(answer.getId()); + paperQuAnswer.setChecked(false); + paperQuAnswer.setSort(ii); + paperQuAnswer.setAbc(ABC.get(ii)); + paperQuAnswer.setIsRight(answer.getIsRight()); + ii++; + batchAnswerList.add(paperQuAnswer); } - - batchQuList.add(item); - sort++; } - //添加问题 - paperQuService.saveBatch(batchQuList); - - //批量添加问题答案 - paperQuAnswerService.saveBatch(batchAnswerList); + // 添加到批量保存的题目列表中 + batchQuList.add(item); + sort++; } - @Transactional(rollbackFor = Exception.class) - @Override - public void fillAnswer(PaperAnswerDTO reqDTO) { - + // 批量添加题目 + paperQuService.saveBatch(batchQuList); - // 未作答 - if(CollectionUtils.isEmpty(reqDTO.getAnswers()) - && StringUtils.isBlank(reqDTO.getAnswer())){ - return; - } + // 批量添加答案 + paperQuAnswerService.saveBatch(batchAnswerList); +} - //查找答案列表 - List list = paperQuAnswerService.listForFill(reqDTO.getPaperId(), reqDTO.getQuId()); +// 声明填充答案的方法,使用事务管理 +/** + * 填充答案的方法 + * @param reqDTO 试卷答案DTO + */ +@Transactional(rollbackFor = Exception.class) +@Override +public void fillAnswer(PaperAnswerDTO reqDTO) { + + // 如果答案列表为空且答案字符串也为空,则直接返回 + if(CollectionUtils.isEmpty(reqDTO.getAnswers()) + && StringUtils.isBlank(reqDTO.getAnswer())){ + return; + } - //是否正确 - boolean right = true; + // 查询答案列表 + List list = paperQuAnswerService.listForFill(reqDTO.getPaperId(), reqDTO.getQuId()); - //更新正确答案 - for (PaperQuAnswer item : list) { + // 初始化是否正确的标记 + boolean right = true; - if (reqDTO.getAnswers().contains(item.getId())) { - item.setChecked(true); - } else { - item.setChecked(false); - } + // 更新正确答案 + for (PaperQuAnswer item : list) { - //有一个对不上就是错的 - if (item.getIsRight()!=null && !item.getIsRight().equals(item.getChecked())) { - right = false; - } - paperQuAnswerService.updateById(item); + // 设置答案是否被选中 + if (reqDTO.getAnswers().contains(item.getId())) { + item.setChecked(true); + } else { + item.setChecked(false); } - //修改为已回答 - PaperQu qu = new PaperQu(); - qu.setQuId(reqDTO.getQuId()); - qu.setPaperId(reqDTO.getPaperId()); - qu.setIsRight(right); - qu.setAnswer(reqDTO.getAnswer()); - qu.setAnswered(true); - - paperQuService.updateByKey(qu); - - } - - @Transactional(rollbackFor = Exception.class) - @Override - public void handExam(String paperId) { - - //获取试卷信息 - Paper paper = paperService.getById(paperId); - - //如果不是正常的,抛出异常 - if(!PaperState.ING.equals(paper.getState())){ - throw new ServiceException(1, "试卷状态不正确!"); + // 如果有一个答案不正确,则标记为错误 + if (item.getIsRight()!=null && !item.getIsRight().equals(item.getChecked())) { + right = false; } + paperQuAnswerService.updateById(item); + } - // 客观分 - int objScore = paperQuService.sumObjective(paperId); - paper.setObjScore(objScore); - paper.setUserScore(objScore); + // 修改为已回答 + PaperQu qu = new PaperQu(); + qu.setQuId(reqDTO.getQuId()); + qu.setPaperId(reqDTO.getPaperId()); + qu.setIsRight(right); + qu.setAnswer(reqDTO.getAnswer()); + qu.setAnswered(true); - // 主观分,因为要阅卷,所以给0 - paper.setSubjScore(0); + paperQuService.updateByKey(qu); - // 待阅卷 - if(paper.getHasSaq()) { - paper.setState(PaperState.WAIT_OPT); - }else { +} - // 同步保存考试成绩 - userExamService.joinResult(paper.getUserId(), paper.getExamId(), objScore, objScore>=paper.getQualifyScore()); +// 声明交卷的方法,使用事务管理 +/** + * 交卷的方法 + * @param paperId 试卷ID + */ +@Transactional(rollbackFor = Exception.class) +@Override +public void handExam(String paperId) { + + // 获取试卷信息 + Paper paper = paperService.getById(paperId); + + // 如果试卷状态不正确,则抛出服务异常 + if(!PaperState.ING.equals(paper.getState())){ + throw new ServiceException(1, "试卷状态不正确!"); + } - paper.setState(PaperState.FINISHED); - } - paper.setUpdateTime(new Date()); - - //计算考试时长 - Calendar cl = Calendar.getInstance(); - cl.setTimeInMillis(System.currentTimeMillis()); - int userTime = (int)((System.currentTimeMillis() - paper.getCreateTime().getTime()) / 1000 / 60); - if(userTime == 0){ - userTime = 1; - } - paper.setUserTime(userTime); + // 计算客观题分数 + int objScore = paperQuService.sumObjective(paperId); + paper.setObjScore(objScore); + paper.setUserScore(objScore); - //更新试卷 - paperService.updateById(paper); + // 设置主观题分数为0 + paper.setSubjScore(0); + // 如果有主观题,则设置状态为待阅卷 + if(paper.getHasSaq()) { + paper.setState(PaperState.WAIT_OPT); + }else { - // 终止定时任务 - String name = JobPrefix.BREAK_EXAM + paperId; - jobService.deleteJob(name, JobGroup.SYSTEM); + // 同步保存考试成绩 + userExamService.joinResult(paper.getUserId(), paper.getExamId(), objScore, objScore>=paper.getQualifyScore()); - //把打错的问题加入错题本 - List list = paperQuService.listByPaper(paperId); - for(PaperQuDTO qu: list){ - // 主观题和对的都不加入错题库 - if(qu.getIsRight()){ - continue; - } - //加入错题本 - new Thread(() -> userBookService.addBook(paper.getExamId(), qu.getQuId())).run(); - } + // 设置状态为已完成 + paper.setState(PaperState.FINISHED); } - - @Override - public IPage paging(PagingReqDTO reqDTO) { - return baseMapper.paging(reqDTO.toPage(), reqDTO.getParams()); + paper.setUpdateTime(new Date()); + + // 计算考试时长 + Calendar cl = Calendar.getInstance(); + cl.setTimeInMillis(System.currentTimeMillis()); + int userTime = (int)((System.currentTimeMillis() - paper.getCreateTime().getTime()) / 1000 / 60); + if(userTime == 0){ + userTime = 1; } + paper.setUserTime(userTime); + // 更新试卷信息 + paperService.updateById(paper); - @Override - public PaperDTO checkProcess(String userId) { - - QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda() - .eq(Paper::getUserId, userId) - .eq(Paper::getState, PaperState.ING); - - Paper paper = this.getOne(wrapper, false); + // 终止定时任务 + String name = JobPrefix.BREAK_EXAM + paperId; + jobService.deleteJob(name, JobGroup.SYSTEM); - if (paper != null) { - return BeanMapper.map(paper, PaperDTO.class); + // 把打错的问题加入错题本 + List list = paperQuService.listByPaper(paperId); + for(PaperQuDTO qu: list){ + // 主观题和对的都不加入错题库 + if(qu.getIsRight()){ + continue; } + //加入错题本 + new Thread(() -> userBookService.addBook(paper.getExamId(), qu.getQuId())).run(); + } +} - return null; +// 声明分页查询试卷列表的方法 +/** + * 分页查询试卷列表的方法 + * @param reqDTO 分页请求DTO + * @return 返回分页响应 + */ +@Override +public IPage paging(PagingReqDTO reqDTO) { + return baseMapper.paging(reqDTO.toPage(), reqDTO.getParams()); +} + +// 声明检查考试进度的方法 +/** + * 检查考试进度的方法 + * @param userId 用户ID + * @return 返回试卷DTO + */ +@Override +public PaperDTO checkProcess(String userId) { + + // 构建查询条件,查询是否有正在进行的考试 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(Paper::getUserId, userId) + .eq(Paper::getState, PaperState.ING); + + // 查询正在进行的考试 + Paper paper = this.getOne(wrapper, false); + + // 如果存在正在进行的考试,则返回试卷DTO + if (paper != null) { + return BeanMapper.map(paper, PaperDTO.class); } + + // 如果不存在正在进行的考试,则返回null + return null; } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/controller/QuController.java b/src-源文件/main/java/com/yf/exam/modules/qu/controller/QuController.java index 07e7d76..99f62b6 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/controller/QuController.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/controller/QuController.java @@ -1,137 +1,129 @@ +// 导入所需的包 package com.yf.exam.modules.qu.controller; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.google.common.collect.Lists; -import com.yf.exam.core.api.ApiRest; -import com.yf.exam.core.api.controller.BaseController; -import com.yf.exam.core.api.dto.BaseIdReqDTO; -import com.yf.exam.core.api.dto.BaseIdRespDTO; -import com.yf.exam.core.api.dto.BaseIdsReqDTO; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.core.exception.ServiceException; -import com.yf.exam.core.utils.BeanMapper; -import com.yf.exam.core.utils.excel.ExportExcel; -import com.yf.exam.core.utils.excel.ImportExcel; -import com.yf.exam.modules.qu.dto.QuDTO; -import com.yf.exam.modules.qu.dto.export.QuExportDTO; -import com.yf.exam.modules.qu.dto.ext.QuDetailDTO; -import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; -import com.yf.exam.modules.qu.entity.Qu; -import com.yf.exam.modules.qu.service.QuService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.apache.commons.lang3.StringUtils; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.apache.shiro.authz.annotation.RequiresRoles; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.CollectionUtils; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; // 用于构建查询条件 +import com.baomidou.mybatisplus.core.metadata.IPage; // 用于分页 +import com.google.common.collect.Lists; // 用于操作列表 +import com.yf.exam.core.api.ApiRest; // API响应封装类 +import com.yf.exam.core.api.controller.BaseController; // 基础控制器类 +import com.yf.exam.core.api.dto.BaseIdReqDTO; // 基础ID请求DTO +import com.yf.exam.core.api.dto.BaseIdRespDTO; // 基础ID响应DTO +import com.yf.exam.core.api.dto.BaseIdsReqDTO; // 基础ID数组请求DTO +import com.yf.exam.core.api.dto.PagingReqDTO; // 分页请求DTO +import com.yf.exam.core.exception.ServiceException; // 自定义服务异常 +import com.yf.exam.core.utils.BeanMapper; // Bean映射工具 +import com.yf.exam.core.utils.excel.ExportExcel; // 导出Excel工具 +import com.yf.exam.core.utils.excel.ImportExcel; // 导入Excel工具 +import com.yf.exam.modules.qu.dto.QuDTO; // 问题DTO +import com.yf.exam.modules.qu.dto.export.QuExportDTO; // 问题导出DTO +import com.yf.exam.modules.qu.dto.ext.QuDetailDTO; // 问题详情DTO +import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; // 问题查询请求DTO +import com.yf.exam.modules.qu.entity.Qu; // 问题实体类 +import com.yf.exam.modules.qu.service.QuService; // 问题服务类 +import io.swagger.annotations.Api; // Swagger API注释 +import io.swagger.annotations.ApiOperation; // Swagger API操作注释 +import org.apache.commons.lang3.StringUtils; // 字符串操作工具类 +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; // 用于处理Excel格式异常 +import org.apache.shiro.authz.annotation.RequiresRoles; // Shiro权限控制注解 +import org.springframework.beans.factory.annotation.Autowired; // 自动注入依赖 +import org.springframework.util.CollectionUtils; // 集合工具类 +import org.springframework.web.bind.annotation.RequestBody; // 请求体注解 +import org.springframework.web.bind.annotation.RequestMapping; // 请求映射注解 +import org.springframework.web.bind.annotation.RequestMethod; // 请求方法类型注解 +import org.springframework.web.bind.annotation.RequestParam; // 请求参数注解 +import org.springframework.web.bind.annotation.ResponseBody; // 响应体注解 +import org.springframework.web.bind.annotation.RestController; // REST控制器注解 +import org.springframework.web.multipart.MultipartFile; // 用于处理文件上传 + +import javax.servlet.http.HttpServletResponse; // 用于处理HTTP响应 +import java.io.IOException; // IO异常处理 +import java.util.Arrays; // 数组工具类 +import java.util.List; // 列表工具类 /** *

* 问题题目控制器 *

+* 该控制器类负责管理题目相关的操作,包括添加、修改、删除、查询等 * * @author 聪明笨狗 * @since 2020-05-25 13:25 */ -@Api(tags={"问题题目"}) -@RestController -@RequestMapping("/exam/api/qu/qu") +@Api(tags={"问题题目"}) // Swagger注解,表示该控制器处理"问题题目"相关的请求 +@RestController // Spring注解,表示这是一个RESTful API控制器 +@RequestMapping("/exam/api/qu/qu") // 设置基础路径 public class QuController extends BaseController { @Autowired - private QuService baseService; + private QuService baseService; // 自动注入问题服务类 /** - * 添加或修改 + * 添加或修改问题题目 * - * @param reqDTO - * @return + * @param reqDTO 请求的详细数据,包含问题题目的详细信息 + * @return 返回操作结果 */ - @RequiresRoles("sa") - @ApiOperation(value = "添加或修改") - @RequestMapping(value = "/save", method = {RequestMethod.POST}) + @RequiresRoles("sa") // 限制只有角色为"sa"的用户可以访问此方法 + @ApiOperation(value = "添加或修改") // Swagger注解,描述该方法的功能 + @RequestMapping(value = "/save", method = {RequestMethod.POST}) // POST请求,表示保存操作 public ApiRest save(@RequestBody QuDetailDTO reqDTO) { - baseService.save(reqDTO); - return super.success(); + baseService.save(reqDTO); // 调用服务层保存或更新问题数据 + return super.success(); // 返回成功响应 } /** - * 批量删除 + * 批量删除问题题目 * - * @param reqDTO - * @return + * @param reqDTO 请求的ID数组,包含要删除的题目ID列表 + * @return 返回操作结果 */ - @RequiresRoles("sa") - @ApiOperation(value = "批量删除") - @RequestMapping(value = "/delete", method = {RequestMethod.POST}) + @RequiresRoles("sa") // 限制只有角色为"sa"的用户可以访问此方法 + @ApiOperation(value = "批量删除") // Swagger注解,描述该方法的功能 + @RequestMapping(value = "/delete", method = {RequestMethod.POST}) // POST请求,表示删除操作 public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { - //根据ID删除 - baseService.delete(reqDTO.getIds()); - return super.success(); + baseService.delete(reqDTO.getIds()); // 调用服务层进行批量删除 + return super.success(); // 返回成功响应 } /** - * 查找详情 + * 查找问题题目详情 * - * @param reqDTO - * @return + * @param reqDTO 请求的ID数据,包含要查找的题目ID + * @return 返回问题题目的详细信息 */ - @ApiOperation(value = "查找详情") - @RequestMapping(value = "/detail", method = {RequestMethod.POST}) + @ApiOperation(value = "查找详情") // Swagger注解,描述该方法的功能 + @RequestMapping(value = "/detail", method = {RequestMethod.POST}) // POST请求,表示获取详情操作 public ApiRest detail(@RequestBody BaseIdReqDTO reqDTO) { - QuDetailDTO dto = baseService.detail(reqDTO.getId()); - return super.success(dto); + QuDetailDTO dto = baseService.detail(reqDTO.getId()); // 调用服务层获取问题题目详情 + return super.success(dto); // 返回问题详情 } /** - * 分页查找 + * 分页查询问题题目 * - * @param reqDTO - * @return + * @param reqDTO 分页请求数据,包含查询条件和分页参数 + * @return 返回分页结果 */ - @RequiresRoles("sa") - @ApiOperation(value = "分页查找") - @RequestMapping(value = "/paging", method = {RequestMethod.POST}) + @RequiresRoles("sa") // 限制只有角色为"sa"的用户可以访问此方法 + @ApiOperation(value = "分页查找") // Swagger注解,描述该方法的功能 + @RequestMapping(value = "/paging", method = {RequestMethod.POST}) // POST请求,表示分页查询操作 public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { - - //分页查询并转换 - IPage page = baseService.paging(reqDTO); - - return super.success(page); + IPage page = baseService.paging(reqDTO); // 调用服务层进行分页查询 + return super.success(page); // 返回分页结果 } - /** - * 导出excel文件 + * 导出问题题目的Excel文件 */ - @RequiresRoles("sa") - @ResponseBody - @RequestMapping(value = "/export") + @RequiresRoles("sa") // 限制只有角色为"sa"的用户可以访问此方法 + @ResponseBody // 标明返回内容直接作为响应体 + @RequestMapping(value = "/export") // 导出请求路径 public ApiRest exportFile(HttpServletResponse response, @RequestBody QuQueryReqDTO reqDTO) { - - - // 导出文件名 - String fileName = "导出的试题-" + System.currentTimeMillis() + ".xlsx"; - + String fileName = "导出的试题-" + System.currentTimeMillis() + ".xlsx"; // 设置导出的文件名 try { - int no = 0; String quId = ""; - List list = baseService.listForExport(reqDTO); + List list = baseService.listForExport(reqDTO); // 获取导出数据 for (QuExportDTO item : list) { if (!quId.equals(item.getQId())) { quId = item.getQId(); @@ -144,135 +136,101 @@ public class QuController extends BaseController { item.setQImage(""); item.setQVideo(""); } - item.setNo(String.valueOf(no)); + item.setNo(String.valueOf(no)); // 设置题目序号 } - new ExportExcel("试题", QuExportDTO.class).setDataList(list).write(response, fileName).dispose(); - return super.success(); + new ExportExcel("试题", QuExportDTO.class).setDataList(list).write(response, fileName).dispose(); // 导出数据到Excel文件 + return super.success(); // 返回成功响应 } catch (Exception e) { - return failure(e.getMessage()); + return failure(e.getMessage()); // 捕获异常并返回失败响应 } } /** - * 导入Excel + * 导入问题题目的Excel文件 * - * @param file - * @return + * @param file 上传的Excel文件 + * @return 返回操作结果 */ - @RequiresRoles("sa") - @ResponseBody - @RequestMapping(value = "/import") + @RequiresRoles("sa") // 限制只有角色为"sa"的用户可以访问此方法 + @ResponseBody // 标明返回内容直接作为响应体 + @RequestMapping(value = "/import") // 导入请求路径 public ApiRest importFile(@RequestParam("file") MultipartFile file) { - try { - - ImportExcel ei = new ImportExcel(file, 1, 0); - List list = ei.getDataList(QuExportDTO.class); - - // 校验数据 - this.checkExcel(list); - - // 导入数据条数 - baseService.importExcel(list); - - // 导入成功 - return super.success(); - - } catch (IOException e) { - - } catch (InvalidFormatException e) { - - } catch (IllegalAccessException e) { - - } catch (InstantiationException e) { - + ImportExcel ei = new ImportExcel(file, 1, 0); // 创建导入Excel对象 + List list = ei.getDataList(QuExportDTO.class); // 获取Excel中的数据列表 + this.checkExcel(list); // 校验数据 + baseService.importExcel(list); // 调用服务层进行导入操作 + return super.success(); // 返回成功响应 + } catch (IOException | InvalidFormatException | IllegalAccessException | InstantiationException e) { + return super.failure(); // 捕获各种异常并返回失败响应 } - - return super.failure(); } /** - * 校验Excel + * 校验Excel文件中的数据 * - * @param list - * @throws Exception + * @param list 导入的题目数据列表 + * @throws ServiceException 可能抛出服务异常 */ private void checkExcel(List list) throws ServiceException { - - // 约定第三行开始导入 + // 校验Excel数据的逻辑,检查每一行数据的有效性 int line = 3; StringBuffer sb = new StringBuffer(); - if (CollectionUtils.isEmpty(list)) { - throw new ServiceException(1, "您导入的数据似乎是一个空表格!"); + throw new ServiceException(1, "您导入的数据似乎是一个空表格!"); // 如果表格为空,抛出异常 } - Integer quNo = null; for (QuExportDTO item : list) { - - System.out.println(item.getNo()); if (StringUtils.isBlank(item.getNo())) { line++; continue; } - - System.out.println(item.getQContent()); Integer no; - try { - no = Integer.parseInt(item.getNo()); + no = Integer.parseInt(item.getNo()); // 转换题目序号 } catch (Exception e) { line++; continue; } - if (no == null) { sb.append("第" + line + "行,题目序号不能为空!
"); } - + // 校验题目内容和其他字段是否为空 if (quNo == null || !quNo.equals(no)) { - if (item.getQuType() == null) { sb.append("第" + line + "行,题目类型不能为空
"); } - if (StringUtils.isBlank(item.getQContent())) { sb.append("第" + line + "行,题目内容不能为空
"); } - if (CollectionUtils.isEmpty(item.getRepoList())) { sb.append("第" + line + "行,题目必须包含一个题库
"); } } - if (StringUtils.isBlank(item.getAIsRight())) { sb.append("第" + line + "行,选项是否正确不能为空
"); } - if (StringUtils.isBlank(item.getAContent()) && StringUtils.isBlank(item.getAImage())) { sb.append("第" + line + "行,选项内容和选项图片必须有一个不为空
"); } - quNo = no; line++; } - - // 存在错误 if (!"".equals(sb.toString())) { - throw new ServiceException(1, sb.toString()); + throw new ServiceException(1, sb.toString()); // 如果有校验错误,抛出异常 } } /** - * 下载导入试题数据模板 + * 下载试题导入模板 */ @ResponseBody - @RequestMapping(value = "import/template") + @RequestMapping(value = "import/template") // 导入模板下载路径 public ApiRest importFileTemplate(HttpServletResponse response) { try { - String fileName = "试题导入模板.xlsx"; - List list = Lists.newArrayList(); - + String fileName = "试题导入模板.xlsx"; // 设置文件名 + List list = Lists.newArrayList(); // 创建模板数据列表 + // 模板数据(包含问题内容、题型、选项等) QuExportDTO l1 = new QuExportDTO(); l1.setNo("正式导入,请删除此说明行:数字,相同的数字表示同一题的序列"); l1.setQContent("问题内容"); @@ -286,39 +244,17 @@ public class QuController extends BaseController { l1.setAIsRight("只能填写0或1,0表示否,1表示是"); l1.setAAnalysis("这个项是正确的"); - - QuExportDTO l2 = new QuExportDTO(); - l2.setQContent("找出以下可以被2整除的数(多选)"); - l2.setQAnalysis("最基本的数学题,不做过多解析"); - l2.setQuType("2"); - l2.setNo("1"); - l2.setAIsRight("1"); - l2.setAContent("数字:2"); - l2.setAAnalysis("2除以2=1,对的"); - - QuExportDTO l3 = new QuExportDTO(); - l3.setNo("1"); - l3.setAIsRight("0"); - l3.setAContent("数字:3"); - l3.setAAnalysis("3除以2=1.5,不能被整除"); - - QuExportDTO l4 = new QuExportDTO(); - l4.setNo("1"); - l4.setAIsRight("1"); - l4.setAContent("数字:6"); - l4.setAAnalysis("6除以2=3,对的"); - - - + // 添加模板示例数据 list.add(l1); list.add(l2); list.add(l3); list.add(l4); + // 导出模板文件 new ExportExcel("试题数据", QuExportDTO.class, 1).setDataList(list).write(response, fileName).dispose(); - return super.success(); + return super.success(); // 返回成功响应 } catch (Exception e) { - return super.failure("导入模板下载失败!失败信息:"+e.getMessage()); + return super.failure("导入模板下载失败!失败信息:"+e.getMessage()); // 返回失败响应 } } } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuAnswerDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuAnswerDTO.java index 3b9c3a1..9a6a160 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuAnswerDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuAnswerDTO.java @@ -1,42 +1,66 @@ package com.yf.exam.modules.qu.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; +import io.swagger.annotations.ApiModel; // Swagger注解,用于生成API文档 +import io.swagger.annotations.ApiModelProperty; // Swagger注解,用于描述API模型的属性 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 -import java.io.Serializable; +import java.io.Serializable; // 可序列化接口 /** *

* 候选答案请求类 *

* +* 该类用于封装候选答案的请求信息,包含每个答案的具体内容、是否正确、答案分析等信息。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 @ApiModel(value="候选答案", description="候选答案") public class QuAnswerDTO implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID + /** + * 答案ID + * 用于唯一标识每个候选答案,通常由数据库自动生成。 + */ + @ApiModelProperty(value = "答案ID", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String id; // 答案ID - @ApiModelProperty(value = "答案ID", required=true) - private String id; + /** + * 题目ID + * 该字段表示该答案对应的题目ID。 + */ + @ApiModelProperty(value = "问题ID", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String quId; // 题目ID - @ApiModelProperty(value = "问题ID", required=true) - private String quId; + /** + * 是否正确 + * 标记该答案是否为正确答案,`true`表示正确,`false`表示错误。 + */ + @ApiModelProperty(value = "是否正确", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Boolean isRight; // 是否正确 - @ApiModelProperty(value = "是否正确", required=true) - private Boolean isRight; + /** + * 选项图片 + * 存储与该答案相关的图片URL,通常用于多媒体题目选项。 + */ + @ApiModelProperty(value = "选项图片", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String image; // 选项图片URL - @ApiModelProperty(value = "选项图片", required=true) - private String image; + /** + * 答案内容 + * 该字段用于存储该答案的文本内容,通常为答案的描述或选项文本。 + */ + @ApiModelProperty(value = "答案内容", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String content; // 答案内容 - @ApiModelProperty(value = "答案内容", required=true) - private String content; - - @ApiModelProperty(value = "答案分析", required=true) - private String analysis; - + /** + * 答案分析 + * 用于对该答案进行详细解析,解释为什么该答案正确或错误。 + */ + @ApiModelProperty(value = "答案分析", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String analysis; // 答案分析 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuDTO.java index 8f45eec..fe82154 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuDTO.java @@ -1,53 +1,88 @@ package com.yf.exam.modules.qu.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; +import io.swagger.annotations.ApiModel; // Swagger注解,用于生成API文档 +import io.swagger.annotations.ApiModelProperty; // Swagger注解,用于描述API模型的属性 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 -import java.io.Serializable; -import java.util.Date; +import java.io.Serializable; // 可序列化接口 +import java.util.Date; // 日期类型 /** *

* 问题题目请求类 *

* +* 该类用于封装问题题目的基本信息,包括题目的类型、难度、内容、解析等。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 @ApiModel(value="问题题目", description="问题题目") public class QuDTO implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID + /** + * 题目ID + * 每个题目都拥有唯一的ID,用于标识题目。 + */ + @ApiModelProperty(value = "题目ID", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String id; // 题目ID - @ApiModelProperty(value = "题目ID", required=true) - private String id; + /** + * 题目类型 + * 该字段用于标识题目的类型,例如选择题、填空题、判断题等。 + */ + @ApiModelProperty(value = "题目类型", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Integer quType; // 题目类型 - @ApiModelProperty(value = "题目类型", required=true) - private Integer quType; + /** + * 题目难度 + * 用于表示题目的难易程度。1表示普通,2表示较难,可能有更多级别。 + */ + @ApiModelProperty(value = "1普通,2较难", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Integer level; // 题目难度 - @ApiModelProperty(value = "1普通,2较难", required=true) - private Integer level; + /** + * 题目图片 + * 该字段存储与题目相关的图片URL,可以是题目内容的辅助说明。 + */ + @ApiModelProperty(value = "题目图片", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String image; // 题目图片URL - @ApiModelProperty(value = "题目图片", required=true) - private String image; + /** + * 题目内容 + * 存储题目的具体内容,描述问题的文本部分。 + */ + @ApiModelProperty(value = "题目内容", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String content; // 题目内容 - @ApiModelProperty(value = "题目内容", required=true) - private String content; + /** + * 创建时间 + * 标识题目创建的时间,通常由系统自动生成。 + */ + @ApiModelProperty(value = "创建时间", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Date createTime; // 创建时间 + /** + * 更新时间 + * 记录题目最后一次更新时间,通常由系统自动更新。 + */ + @ApiModelProperty(value = "更新时间", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Date updateTime; // 更新时间 - @ApiModelProperty(value = "创建时间", required=true) - private Date createTime; + /** + * 题目备注 + * 可选字段,用于记录关于题目的额外备注信息。 + */ + @ApiModelProperty(value = "题目备注", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String remark; // 题目备注 - @ApiModelProperty(value = "更新时间", required=true) - private Date updateTime; - - @ApiModelProperty(value = "题目备注", required=true) - private String remark; - - @ApiModelProperty(value = "整题解析", required=true) - private String analysis; - + /** + * 整题解析 + * 提供该题目的完整解析,帮助用户理解解题思路及过程。 + */ + @ApiModelProperty(value = "整题解析", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String analysis; // 整题解析 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuRepoDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuRepoDTO.java index 6e689b8..0fa058e 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuRepoDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/QuRepoDTO.java @@ -1,38 +1,58 @@ package com.yf.exam.modules.qu.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; +import io.swagger.annotations.ApiModel; // Swagger注解,用于生成API文档 +import io.swagger.annotations.ApiModelProperty; // Swagger注解,用于描述API模型的属性 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 -import java.io.Serializable; +import java.io.Serializable; // 可序列化接口 /** *

* 试题题库请求类 *

* +* 该类用于封装题库与题目之间的关联信息,包含题目的ID、题库ID、题目类型等信息。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 @ApiModel(value="试题题库", description="试题题库") public class QuRepoDTO implements Serializable { - private static final long serialVersionUID = 1L; - - - private String id; - - @ApiModelProperty(value = "试题", required=true) - private String quId; - - @ApiModelProperty(value = "归属题库", required=true) - private String repoId; - - @ApiModelProperty(value = "题目类型", required=true) - private Integer quType; - - @ApiModelProperty(value = "排序", required=true) - private Integer sort; - -} \ No newline at end of file + private static final long serialVersionUID = 1L; // 序列化版本UID + + /** + * 试题ID + * 唯一标识一道试题。 + */ + private String id; // 试题ID + + /** + * 题目ID + * 与题目ID进行关联,表示该试题属于某个具体题目。 + */ + @ApiModelProperty(value = "试题", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String quId; // 题目ID + + /** + * 题库ID + * 标识该题目所属的题库。 + */ + @ApiModelProperty(value = "归属题库", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private String repoId; // 题库ID + + /** + * 题目类型 + * 表示该题目在题库中的类型,例如选择题、填空题等。 + */ + @ApiModelProperty(value = "题目类型", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Integer quType; // 题目类型 + + /** + * 排序 + * 表示该题目在题库中的显示顺序,数字越小,顺序越靠前。 + */ + @ApiModelProperty(value = "排序", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Integer sort; // 排序 +} diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuExportDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuExportDTO.java index fe94dac..fff01c5 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuExportDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuExportDTO.java @@ -1,45 +1,59 @@ package com.yf.exam.modules.qu.dto.export; -import com.yf.exam.core.utils.excel.annotation.ExcelField; -import com.yf.exam.core.utils.excel.fieldtype.ListType; -import lombok.Data; +import com.yf.exam.core.utils.excel.annotation.ExcelField; // Excel导出注解 +import com.yf.exam.core.utils.excel.fieldtype.ListType; // 用于处理List类型字段的特殊注解 +import lombok.Data; // Lombok注解,用于自动生成getter、setter、toString等方法 -import java.util.List; +import java.util.List; // 用于表示列表类型的字段 /** * 用于导出的数据结构 + * + * 该类是导出试题相关数据时所使用的DTO(数据传输对象),包含了题目序号、题目内容、题目解析、选项内容等信息。 + * 主要用于Excel导出时的数据映射。 + * * @author bool */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 public class QuExportDTO { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID /** - * + * 题目ID */ - private String qId; - - @ExcelField(title="题目序号", align=2, sort=1) - private String no; - @ExcelField(title="题目类型", align=2, sort=2) - private String quType; - @ExcelField(title="题目内容", align=2, sort=3) - private String qContent; - @ExcelField(title="整体解析", align=2, sort=4) - private String qAnalysis; - @ExcelField(title="题目图片", align=2, sort=5) - private String qImage; - @ExcelField(title="题目视频", align=2, sort=6) - private String qVideo; - @ExcelField(title="所属题库", align=2, sort=7, fieldType = ListType.class) - private List repoList; - @ExcelField(title="是否正确项", align=2, sort=8) - private String aIsRight; - @ExcelField(title="选项内容", align=2, sort=9) - private String aContent; - @ExcelField(title="选项解析", align=2, sort=10) - private String aAnalysis; - @ExcelField(title="选项图片", align=2, sort=11) - private String aImage; + private String qId; // 题目的唯一标识符 + + @ExcelField(title="题目序号", align=2, sort=1) // 导出Excel时的列标题和排序,align为居中对齐,sort为排序位置 + private String no; // 题目序号,表示题目的编号 + + @ExcelField(title="题目类型", align=2, sort=2) // Excel导出列的标题和排序 + private String quType; // 题目类型,可能是单选题、多选题等 + + @ExcelField(title="题目内容", align=2, sort=3) // Excel导出列的标题和排序 + private String qContent; // 题目内容,包含问题的具体描述 + + @ExcelField(title="整体解析", align=2, sort=4) // Excel导出列的标题和排序 + private String qAnalysis; // 整个题目的解析说明 + + @ExcelField(title="题目图片", align=2, sort=5) // Excel导出列的标题和排序 + private String qImage; // 题目图片,存储图片URL或路径 + + @ExcelField(title="题目视频", align=2, sort=6) // Excel导出列的标题和排序 + private String qVideo; // 题目视频,存储视频URL或路径 + + @ExcelField(title="所属题库", align=2, sort=7, fieldType = ListType.class) // 题库列表,支持导出多个题库 + private List repoList; // 题目所属的题库列表 + + @ExcelField(title="是否正确项", align=2, sort=8) // Excel导出列的标题和排序 + private String aIsRight; // 是否为正确选项(0或1) + + @ExcelField(title="选项内容", align=2, sort=9) // Excel导出列的标题和排序 + private String aContent; // 选项内容,表示答案的具体内容 + + @ExcelField(title="选项解析", align=2, sort=10) // Excel导出列的标题和排序 + private String aAnalysis; // 选项解析,说明该选项的正确性或相关分析 + + @ExcelField(title="选项图片", align=2, sort=11) // Excel导出列的标题和排序 + private String aImage; // 选项图片,存储图片URL或路径 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuImportDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuImportDTO.java index 4ac03c0..79aa0fe 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuImportDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/export/QuImportDTO.java @@ -1,23 +1,56 @@ package com.yf.exam.modules.qu.dto.export; -import com.yf.exam.modules.qu.dto.QuAnswerDTO; -import lombok.Data; +import com.yf.exam.modules.qu.dto.QuAnswerDTO; // 导入的选项答案DTO +import lombok.Data; // Lombok注解,用于自动生成getter、setter、toString等方法 -import java.util.List; +import java.util.List; // 用于表示列表类型的字段 /** * 用于导出的数据结构 + * + * 该类用于导入试题数据时的DTO(数据传输对象),包含了题目类型、题目内容、解析、题目图片等信息。 + * 同时还包含了该题目的多个答案选项。 + * * @author bool */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 public class QuImportDTO { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID - private String quType; - private String qContent; - private String qAnalysis; - private String qImage; - private String repoName; - private List answerList; + /** + * 题目类型 + * 例如:1代表单选题,2代表多选题,3代表判断题,4代表主观题等 + */ + private String quType; // 题目类型,表示题目的类别 + + /** + * 题目内容 + * 例如:问题的具体描述 + */ + private String qContent; // 题目内容,表示题目的实际问题 + + /** + * 题目解析 + * 例如:题目解析或解释说明 + */ + private String qAnalysis; // 题目解析,解释题目的答案或相关说明 + + /** + * 题目图片 + * 例如:题目相关的图片URL + */ + private String qImage; // 题目图片,存储图片URL或路径 + + /** + * 题库名称 + * 例如:题目所属的题库名称 + */ + private String repoName; // 题目所属的题库名称 + + /** + * 答案选项列表 + * 该字段存储了该题目的多个答案选项及其相关信息。 + */ + private List answerList; // 答案选项列表,包含该题目的所有答案选项 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/ext/QuDetailDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/ext/QuDetailDTO.java index b36a4e8..0035a89 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/ext/QuDetailDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/ext/QuDetailDTO.java @@ -1,33 +1,43 @@ package com.yf.exam.modules.qu.dto.ext; -import com.yf.exam.modules.qu.dto.QuAnswerDTO; -import com.yf.exam.modules.qu.dto.QuDTO; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; +import com.yf.exam.modules.qu.dto.QuAnswerDTO; // 引入选项答案DTO +import com.yf.exam.modules.qu.dto.QuDTO; // 引入问题题目DTO +import io.swagger.annotations.ApiModel; // Swagger注解,用于生成API文档 +import io.swagger.annotations.ApiModelProperty; // Swagger注解,用于描述API模型的属性 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 -import java.util.List; +import java.util.List; // 用于表示列表类型的字段 /** *

* 问题题目请求类 *

* +* 该类用于表示问题的详细信息,继承自 `QuDTO`,包括题目的详细信息如备选项、题库列表等。 +* 主要用于在前后端交互时,传递包含问题详细信息的请求或响应数据。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 @ApiModel(value="问题题目详情", description="问题题目详情") public class QuDetailDTO extends QuDTO { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID - - @ApiModelProperty(value = "备选项列表", required=true) - private List answerList; - - @ApiModelProperty(value = "题库列表", required=true) - private List repoIds; + /** + * 备选项列表 + * 该字段包含了该题目的所有答案选项列表。 + * 每个选项都包含了选项内容、是否为正确答案、解析等信息。 + */ + @ApiModelProperty(value = "备选项列表", required=true) // Swagger注解,用于生成文档 + private List answerList; // 备选项列表,包含该题目的所有答案选项 - + /** + * 题库列表 + * 该字段包含了该题目所属的多个题库ID。 + * 用于标识该题目属于哪些题库,可能有多个题库关联。 + */ + @ApiModelProperty(value = "题库列表", required=true) // Swagger注解,用于生成文档 + private List repoIds; // 题库列表,存储题库ID的列表 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuQueryReqDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuQueryReqDTO.java index a927e9e..989cb8e 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuQueryReqDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuQueryReqDTO.java @@ -1,38 +1,54 @@ package com.yf.exam.modules.qu.dto.request; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; +import io.swagger.annotations.ApiModel; // Swagger注解,用于生成API文档 +import io.swagger.annotations.ApiModelProperty; // Swagger注解,用于描述API模型的属性 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 -import java.io.Serializable; -import java.util.List; +import java.io.Serializable; // 可序列化接口 +import java.util.List; // 用于表示列表类型的字段 /** *

* 问题题目请求类 *

* +* 该类用于封装前端请求查询题目的参数。通过该请求类,前端可以传递多个查询条件, +* 如题目类型、题库ID、题目内容等,以便进行题目的筛选和查询。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 @ApiModel(value="题目查询请求类", description="题目查询请求类") public class QuQueryReqDTO implements Serializable { - private static final long serialVersionUID = 1L; - - - @ApiModelProperty(value = "题目类型") - private Integer quType; - - @ApiModelProperty(value = "归属题库") - private List repoIds; - - @ApiModelProperty(value = "题目内容") - private String content; - - @ApiModelProperty(value = "排除ID列表") - private List excludes; - - + private static final long serialVersionUID = 1L; // 序列化版本UID + + /** + * 题目类型 + * 用于指定查询的题目类型。例如,单选题、多选题、判断题等。 + */ + @ApiModelProperty(value = "题目类型") // Swagger注解,描述字段信息 + private Integer quType; // 题目类型,通常是数字表示不同题型 + + /** + * 归属题库 + * 用于指定题目所属的题库ID列表。如果该字段不为空,查询会限制在指定的题库中。 + */ + @ApiModelProperty(value = "归属题库") // Swagger注解,描述字段信息 + private List repoIds; // 题库ID列表,题目可以归属于多个题库 + + /** + * 题目内容 + * 用于进行模糊查询,匹配包含特定内容的题目。 + */ + @ApiModelProperty(value = "题目内容") // Swagger注解,描述字段信息 + private String content; // 题目内容,支持模糊查询 + + /** + * 排除ID列表 + * 用于指定在查询中排除的题目ID列表,这些ID的题目不会出现在查询结果中。 + */ + @ApiModelProperty(value = "排除ID列表") // Swagger注解,描述字段信息 + private List excludes; // 排除的题目ID列表 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuRepoBatchReqDTO.java b/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuRepoBatchReqDTO.java index 3ca148d..0628495 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuRepoBatchReqDTO.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/dto/request/QuRepoBatchReqDTO.java @@ -1,34 +1,47 @@ package com.yf.exam.modules.qu.dto.request; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; +import io.swagger.annotations.ApiModel; // Swagger注解,用于生成API文档 +import io.swagger.annotations.ApiModelProperty; // Swagger注解,用于描述API模型的属性 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 -import java.io.Serializable; -import java.util.List; +import java.io.Serializable; // 可序列化接口 +import java.util.List; // 用于表示列表类型的字段 /** *

-* 问题题目请求类 +* 试题题库批量操作请求类 *

* +* 该类用于封装前端请求批量操作题目和题库关联的参数。 +* 通过该请求类,前端可以执行批量新增或移除题目与题库的关联操作。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data +@Data // Lombok注解,自动生成getter、setter等方法 @ApiModel(value="试题题库批量操作类", description="试题题库批量操作类") public class QuRepoBatchReqDTO implements Serializable { - private static final long serialVersionUID = 1L; - - - @ApiModelProperty(value = "题目ID", required=true) - private List quIds; - - @ApiModelProperty(value = "题目类型", required=true) - private List repoIds; - - @ApiModelProperty(value = "是否移除,否就新增;是就移除", required=true) - private Boolean remove; - + private static final long serialVersionUID = 1L; // 序列化版本UID + + /** + * 题目ID列表 + * 用于指定要操作的题目ID集合,可以是多个题目。 + */ + @ApiModelProperty(value = "题目ID", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private List quIds; // 要操作的题目ID列表 + + /** + * 题库ID列表 + * 用于指定题目与之关联的题库ID集合,可以是多个题库。 + */ + @ApiModelProperty(value = "题目类型", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private List repoIds; // 题库ID列表 + + /** + * 是否移除标志 + * 如果为 `true`,表示从题库中移除题目;如果为 `false`,表示新增题目到题库中。 + */ + @ApiModelProperty(value = "是否移除,否就新增;是就移除", required=true) // Swagger注解,描述字段信息,标明该字段是必填项 + private Boolean remove; // `true`表示移除,`false`表示新增 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/entity/Qu.java b/src-源文件/main/java/com/yf/exam/modules/qu/entity/Qu.java index 2380cdc..ccb4579 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/entity/Qu.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/entity/Qu.java @@ -1,75 +1,85 @@ package com.yf.exam.modules.qu.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; -import lombok.Data; +import com.baomidou.mybatisplus.annotation.IdType; // MyBatis Plus注解,指定ID生成策略 +import com.baomidou.mybatisplus.annotation.TableField; // MyBatis Plus注解,指定字段映射 +import com.baomidou.mybatisplus.annotation.TableId; // MyBatis Plus注解,指定主键字段 +import com.baomidou.mybatisplus.annotation.TableName; // MyBatis Plus注解,指定表名 +import com.baomidou.mybatisplus.extension.activerecord.Model; // MyBatis Plus扩展的活动记录模式类 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 -import java.util.Date; +import java.util.Date; // 日期类型 /** *

* 问题题目实体类 *

* +* 该类用于映射问题题目的数据结构,通过MyBatis-Plus框架进行数据库操作。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data -@TableName("el_qu") +@Data // Lombok注解,自动生成getter、setter等方法 +@TableName("el_qu") // MyBatis Plus注解,指定与数据库表的映射关系 public class Qu extends Model { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID /** * 题目ID + * 主键,唯一标识一道题目。 */ - @TableId(value = "id", type = IdType.ASSIGN_ID) - private String id; + @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定主键生成策略为自定义ID + private String id; // 题目ID /** * 题目类型 + * 表示该题目是选择题、判断题等类型。 */ - @TableField("qu_type") - private Integer quType; + @TableField("qu_type") // MyBatis Plus注解,指定字段名与数据库表字段名映射 + private Integer quType; // 题目类型 /** - * 1普通,2较难 + * 题目难度 + * 1表示普通,2表示较难。 */ - private Integer level; + private Integer level; // 题目难度,1普通,2较难 /** * 题目图片 + * 题目相关的图片资源,存储图片URL或路径。 */ - private String image; + private String image; // 题目图片 /** * 题目内容 + * 存储题目的实际内容,例如选择题或填空题的文本。 */ - private String content; + private String content; // 题目内容 /** * 创建时间 + * 记录该题目创建的时间戳。 */ - @TableField("create_time") - private Date createTime; + @TableField("create_time") // MyBatis Plus注解,映射数据库中的字段 + private Date createTime; // 创建时间 /** * 更新时间 + * 记录该题目最后更新时间戳。 */ - @TableField("update_time") - private Date updateTime; + @TableField("update_time") // MyBatis Plus注解,映射数据库中的字段 + private Date updateTime; // 更新时间 /** * 题目备注 + * 对题目附加的说明或备注。 */ - private String remark; + private String remark; // 题目备注 /** * 整题解析 + * 对该题目的详细解析,包括答案解析、解题思路等。 */ - private String analysis; - + private String analysis; // 整题解析 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuAnswer.java b/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuAnswer.java index 45e9bf4..6c01005 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuAnswer.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuAnswer.java @@ -1,58 +1,64 @@ package com.yf.exam.modules.qu.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; -import lombok.Data; +import com.baomidou.mybatisplus.annotation.IdType; // MyBatis Plus注解,指定ID生成策略 +import com.baomidou.mybatisplus.annotation.TableField; // MyBatis Plus注解,指定字段映射 +import com.baomidou.mybatisplus.annotation.TableId; // MyBatis Plus注解,指定主键字段 +import com.baomidou.mybatisplus.annotation.TableName; // MyBatis Plus注解,指定表名 +import com.baomidou.mybatisplus.extension.activerecord.Model; // MyBatis Plus扩展的活动记录模式类 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 /** *

* 候选答案实体类 *

* +* 该类用于映射候选答案的数据结构,通过MyBatis-Plus框架进行数据库操作。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data -@TableName("el_qu_answer") +@Data // Lombok注解,自动生成getter、setter等方法 +@TableName("el_qu_answer") // MyBatis Plus注解,指定与数据库表的映射关系 public class QuAnswer extends Model { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID /** * 答案ID + * 主键,唯一标识一个答案。 */ - @TableId(value = "id", type = IdType.ASSIGN_ID) - private String id; + @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定主键生成策略为自定义ID + private String id; // 答案ID /** * 问题ID + * 关联的题目ID,表示这个答案是属于哪道题目的。 */ - @TableField("qu_id") - private String quId; + @TableField("qu_id") // MyBatis Plus注解,指定字段名与数据库表字段名映射 + private String quId; // 问题ID /** * 是否正确 + * 该答案是否为正确答案,true表示正确,false表示错误。 */ - @TableField("is_right") - private Boolean isRight; + @TableField("is_right") // MyBatis Plus注解,映射数据库中的字段 + private Boolean isRight; // 是否正确 /** * 选项图片 + * 答案相关的图片资源,存储图片URL或路径。 */ - private String image; + private String image; // 选项图片 /** * 答案内容 + * 存储该选项的文本内容。 */ - private String content; - + private String content; // 答案内容 /** * 答案分析 + * 对该答案的详细分析,包括为什么是正确或错误的解析。 */ - private String analysis; - + private String analysis; // 答案分析 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuRepo.java b/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuRepo.java index baade9a..aa26d34 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuRepo.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/entity/QuRepo.java @@ -1,50 +1,59 @@ package com.yf.exam.modules.qu.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; -import lombok.Data; +import com.baomidou.mybatisplus.annotation.IdType; // MyBatis Plus注解,指定ID生成策略 +import com.baomidou.mybatisplus.annotation.TableField; // MyBatis Plus注解,指定字段映射 +import com.baomidou.mybatisplus.annotation.TableId; // MyBatis Plus注解,指定主键字段 +import com.baomidou.mybatisplus.annotation.TableName; // MyBatis Plus注解,指定表名 +import com.baomidou.mybatisplus.extension.activerecord.Model; // MyBatis Plus扩展的活动记录模式类 +import lombok.Data; // Lombok注解,用于自动生成getter、setter等方法 /** *

* 试题题库实体类 *

* +* 该类用于映射试题和题库之间的关系,表示一道题目属于某个题库,并可能具有排序。 +* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Data -@TableName("el_qu_repo") +@Data // Lombok注解,自动生成getter、setter等方法 +@TableName("el_qu_repo") // MyBatis Plus注解,指定与数据库表的映射关系 public class QuRepo extends Model { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本UID - @TableId(value = "id", type = IdType.ASSIGN_ID) - private String id; + /** + * 试题题库关系ID + * 主键,唯一标识一条题目与题库的关联记录。 + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) // MyBatis Plus注解,指定主键生成策略为自定义ID + private String id; // 试题题库关系ID /** - * 试题 + * 试题ID + * 关联的题目ID,表示某道题目属于哪个题库。 */ - @TableField("qu_id") - private String quId; + @TableField("qu_id") // MyBatis Plus注解,指定字段名与数据库表字段名映射 + private String quId; // 试题ID /** - * 归属题库 + * 题库ID + * 关联的题库ID,表示该题目属于哪个题库。 */ - @TableField("repo_id") - private String repoId; + @TableField("repo_id") // MyBatis Plus注解,指定字段名与数据库表字段名映射 + private String repoId; // 题库ID /** * 题目类型 + * 用于描述该题目所属的类型(例如:选择题、填空题等)。 */ - @TableField("qu_type") - private Integer quType; + @TableField("qu_type") // MyBatis Plus注解,映射数据库中的字段 + private Integer quType; // 题目类型 /** * 排序 + * 用于对题库中的题目进行排序。 */ - private Integer sort; - + private Integer sort; // 排序 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/enums/QuType.java b/src-源文件/main/java/com/yf/exam/modules/qu/enums/QuType.java index 70e2ee1..4e5f342 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/enums/QuType.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/enums/QuType.java @@ -1,8 +1,10 @@ package com.yf.exam.modules.qu.enums; - /** - * 题目类型 + * 题目类型接口 + * + * 该接口定义了不同类型的题目标识常量,适用于题库系统中对题目类型的区分。 + * * @author bool * @date 2019-10-30 13:11 */ @@ -10,16 +12,19 @@ public interface QuType { /** * 单选题 + * 表示一道题目是单选题,用户只能选择一个答案。 */ Integer RADIO = 1; /** * 多选题 + * 表示一道题目是多选题,用户可以选择多个答案。 */ Integer MULTI = 2; /** * 判断题 + * 表示一道题目是判断题,通常是选择“正确”或“错误”两种答案。 */ Integer JUDGE = 3; diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuAnswerMapper.java b/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuAnswerMapper.java index 23699ce..5fb575e 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuAnswerMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuAnswerMapper.java @@ -4,13 +4,13 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.yf.exam.modules.qu.entity.QuAnswer; /** -*

-* 候选答案Mapper -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 13:23 -*/ + *

+ * 候选答案Mapper + *

+ * + * @author 聪明笨狗 + * @since 2020-05-25 13:23 + */ public interface QuAnswerMapper extends BaseMapper { } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuMapper.java b/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuMapper.java index 14069a2..2ab579e 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuMapper.java @@ -1,15 +1,15 @@ -package com.yf.exam.modules.qu.mapper; +package com.yf.exam.modules.qu.mapper; // 定义包名,用于存放与问题题目相关的 Mapper 类 -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.yf.exam.modules.qu.dto.QuDTO; -import com.yf.exam.modules.qu.dto.export.QuExportDTO; -import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; -import com.yf.exam.modules.qu.entity.Qu; -import org.apache.ibatis.annotations.Param; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 MyBatis-Plus 的 BaseMapper,用于提供通用的 CRUD 方法 +import com.baomidou.mybatisplus.core.metadata.IPage; // 导入分页接口 IPage,用于处理分页结果 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入分页插件的 Page 类,用于分页查询 +import com.yf.exam.modules.qu.dto.QuDTO; // 导入 QuDTO 数据传输对象,用于封装题目数据 +import com.yf.exam.modules.qu.dto.export.QuExportDTO; // 导入 QuExportDTO 用于题目导出的数据结构 +import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; // 导入 QuQueryReqDTO 用于封装查询条件 +import com.yf.exam.modules.qu.entity.Qu; // 导入 Qu 实体类,表示题目表的对应数据 +import org.apache.ibatis.annotations.Param; // 导入 MyBatis 的 Param 注解,用于 SQL 查询中的参数传递 -import java.util.List; +import java.util.List; // 导入 List,用于返回多个对象的集合 /** *

@@ -19,38 +19,34 @@ import java.util.List; * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -public interface QuMapper extends BaseMapper { - - +public interface QuMapper extends BaseMapper { // QuMapper 继承自 BaseMapper,提供基本的 CRUD 操作 /** * 随机抽取题库的数据 - * @param repoId - * @param quType - * @param level + * @param repoId 题库ID + * @param quType 题目类型 + * @param level 题目难度 * @param excludes 要排除的ID列表 - * @param size - * @return + * @param size 抽取题目的数量 + * @return 随机抽取的题目列表 */ - List listByRandom(@Param("repoId") String repoId, - @Param("quType") Integer quType, - @Param("excludes") List excludes, - @Param("size") Integer size); + List listByRandom(@Param("repoId") String repoId, // 题库ID + @Param("quType") Integer quType, // 题目类型 + @Param("excludes") List excludes, // 要排除的题目ID列表 + @Param("size") Integer size); // 抽取的题目数量 /** * 查找导出列表 - * @param query - * @return + * @param query 查询条件对象 + * @return 返回符合条件的题目列表,用于导出 */ - List listForExport(@Param("query") QuQueryReqDTO query); + List listForExport(@Param("query") QuQueryReqDTO query); // 根据查询条件查找导出数据 /** * 分页查找 - * @param page - * @param query - * @return + * @param page 分页参数,包含当前页和每页大小 + * @param query 查询条件对象 + * @return 返回分页的题目数据 */ - IPage paging(Page page, @Param("query") QuQueryReqDTO query); - - + IPage paging(Page page, @Param("query") QuQueryReqDTO query); // 分页查询题目数据,返回 QuDTO 类型的数据 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuRepoMapper.java b/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuRepoMapper.java index 1015448..84fa2cb 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuRepoMapper.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/mapper/QuRepoMapper.java @@ -1,7 +1,7 @@ -package com.yf.exam.modules.qu.mapper; +package com.yf.exam.modules.qu.mapper; // 定义包名,用于存放与试题题库相关的 Mapper 类 -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.yf.exam.modules.qu.entity.QuRepo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 MyBatis-Plus 的 BaseMapper,用于提供通用的 CRUD 方法 +import com.yf.exam.modules.qu.entity.QuRepo; // 导入 QuRepo 实体类,表示试题题库表的数据 /** *

@@ -11,6 +11,5 @@ import com.yf.exam.modules.qu.entity.QuRepo; * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -public interface QuRepoMapper extends BaseMapper { - +public interface QuRepoMapper extends BaseMapper { // QuRepoMapper 继承自 BaseMapper,提供基本的 CRUD 操作 } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/service/QuAnswerService.java b/src-源文件/main/java/com/yf/exam/modules/qu/service/QuAnswerService.java index 4062e95..f6329f5 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/service/QuAnswerService.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/service/QuAnswerService.java @@ -10,7 +10,7 @@ import java.util.List; /** *

-* 候选答案业务类 +* 候选答案业务类接口,定义了与试题答案相关的业务操作 *

* * @author 聪明笨狗 @@ -19,30 +19,30 @@ import java.util.List; public interface QuAnswerService extends IService { /** - * 分页查询数据 - * @param reqDTO - * @return + * 分页查询答案数据 + * @param reqDTO 请求的分页和查询参数 + * @return 分页的答案数据 */ IPage paging(PagingReqDTO reqDTO); /** - * 根据题目ID查询答案并随机 - * @param quId - * @return + * 根据题目ID查询答案,并进行随机排序 + * @param quId 题目ID + * @return 随机排序后的答案列表 */ List listAnswerByRandom(String quId); /** - * 根据问题查找答案 - * @param quId - * @return + * 根据题目ID查询所有的答案 + * @param quId 题目ID + * @return 该题目的答案列表 */ List listByQu(String quId); /** - * 保存试题 - * @param quId - * @param list + * 保存所有选项数据 + * @param quId 题目ID + * @param list 题目的所有答案选项 */ void saveAll(String quId, List list); } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/service/QuRepoService.java b/src-源文件/main/java/com/yf/exam/modules/qu/service/QuRepoService.java index f528759..a53ebb1 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/service/QuRepoService.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/service/QuRepoService.java @@ -1,59 +1,66 @@ +// 定义包名,表示该接口属于com.yf.exam.modules.qu.service包下 package com.yf.exam.modules.qu.service; +// 导入MyBatis Plus框架的分页功能相关类 import com.baomidou.mybatisplus.core.metadata.IPage; +// 导入MyBatis Plus框架的服务接口 import com.baomidou.mybatisplus.extension.service.IService; +// 导入项目中定义的分页请求DTO类 import com.yf.exam.core.api.dto.PagingReqDTO; +// 导入项目中定义的题库DTO类 import com.yf.exam.modules.qu.dto.QuRepoDTO; +// 导入项目中定义的批量请求DTO类 import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO; +// 导入项目中定义的题库实体类 import com.yf.exam.modules.qu.entity.QuRepo; - +// 导入Java.util包下的List接口,用于操作列表 import java.util.List; /** -*

-* 试题题库业务类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 13:23 -*/ + *

+ * 试题题库业务接口,定义了题库相关的业务操作 + *

+ * + * @author 聪明笨狗 + * @since 2020-05-25 13:23 + */ public interface QuRepoService extends IService { /** - * 分页查询数据 - * @param reqDTO - * @return - */ + * 分页查询题库数据的方法 + * @param reqDTO 分页请求DTO,包含分页信息和查询条件 + * @return 返回分页响应,包含题库数据和分页信息 + */ IPage paging(PagingReqDTO reqDTO); /** - * 保存全部列表 - * @param quId - * @param quType - * @param ids + * 保存全部列表的方法,用于保存题目与题库的关系 + * @param quId 题目ID + * @param quType 题目类型 + * @param ids 题库ID列表 */ void saveAll(String quId, Integer quType, List ids); /** - * 根据问题查找题库 - * @param quId - * @return + * 根据题目查找题库的方法 + * @param quId 题目ID + * @return 返回与题目关联的题库ID列表 */ List listByQu(String quId); /** - * 根据题库查找题目ID列表 - * @param repoId - * @param quType - * @param rand - * @return + * 根据题库查找题目ID列表的方法 + * @param repoId 题库ID + * @param quType 题目类型 + * @param rand 是否随机选择 + * @return 返回题目ID列表 */ List listByRepo(String repoId, Integer quType, boolean rand); /** - * 批量操作 - * @param reqDTO + * 批量操作的方法,用于执行批量业务操作 + * @param reqDTO 批量请求DTO,包含批量操作信息 */ void batchAction(QuRepoBatchReqDTO reqDTO); -} +} \ No newline at end of file diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/service/QuService.java b/src-源文件/main/java/com/yf/exam/modules/qu/service/QuService.java index 81f43b0..51fe835 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/service/QuService.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/service/QuService.java @@ -1,46 +1,55 @@ +// 定义包名,表示该接口属于com.yf.exam.modules.qu.service包下 package com.yf.exam.modules.qu.service; +// 导入MyBatis Plus框架的分页功能相关类 import com.baomidou.mybatisplus.core.metadata.IPage; +// 导入MyBatis Plus框架的服务接口 import com.baomidou.mybatisplus.extension.service.IService; +// 导入项目中定义的分页请求DTO类 import com.yf.exam.core.api.dto.PagingReqDTO; +// 导入项目中定义的题目DTO类 import com.yf.exam.modules.qu.dto.QuDTO; +// 导入项目中定义的题目导出DTO类 import com.yf.exam.modules.qu.dto.export.QuExportDTO; +// 导入项目中定义的扩展题目详情DTO类 import com.yf.exam.modules.qu.dto.ext.QuDetailDTO; +// 导入项目中定义的题目查询请求DTO类 import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; +// 导入项目中定义的题目实体类 import com.yf.exam.modules.qu.entity.Qu; - +// 导入Java.util包下的List接口,用于操作列表 import java.util.List; /** -*

-* 问题题目业务类 -*

-* -* @author 聪明笨狗 -* @since 2020-05-25 13:23 -*/ + *

+ * 问题题目业务接口,定义了题目相关的业务操作 + *

+ * + * @author 聪明笨狗 + * @since 2020-05-25 13:23 + */ public interface QuService extends IService { /** - * 分页查询数据 - * @param reqDTO - * @return + * 分页查询题目数据的方法 + * @param reqDTO 分页请求DTO,包含分页信息和查询条件 + * @return 返回分页响应,包含题目数据和分页信息 */ IPage paging(PagingReqDTO reqDTO); /** - * 删除试题 - * @param ids + * 删除题目的方法 + * @param ids 题目ID列表 */ void delete(List ids); /** - * 随机抽取题库的数据 - * @param repoId - * @param quType + * 随机抽取题库中的数据的方法 + * @param repoId 题库ID + * @param quType 题目类型 * @param excludes 要排除的ID列表 - * @param size - * @return + * @param size 抽取的数量 + * @return 返回随机抽取的题目列表 */ List listByRandom(String repoId, Integer quType, @@ -48,29 +57,29 @@ public interface QuService extends IService { Integer size); /** - * 问题详情 - * @param id - * @return + * 查询题目详情的方法 + * @param id 题目ID + * @return 返回题目详情DTO */ QuDetailDTO detail(String id); /** - * 保存试题 - * @param reqDTO + * 保存题目的方法 + * @param reqDTO 题目详情DTO */ void save(QuDetailDTO reqDTO); /** - * 查找导出列表 - * @param query - * @return + * 查找导出列表的方法 + * @param query 题目查询请求DTO + * @return 返回题目导出列表 */ List listForExport(QuQueryReqDTO query); /** - * 导入Excel - * @param dtoList - * @return + * 导入Excel数据的方法 + * @param dtoList 题目导出DTO列表 + * @return 返回导入的结果,影响的行数 */ int importExcel(List dtoList); -} +} \ No newline at end of file diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuAnswerServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuAnswerServiceImpl.java index 2fd66cf..1c31577 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuAnswerServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuAnswerServiceImpl.java @@ -1,24 +1,24 @@ -package com.yf.exam.modules.qu.service.impl; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.core.utils.BeanMapper; -import com.yf.exam.modules.qu.dto.QuAnswerDTO; -import com.yf.exam.modules.qu.entity.QuAnswer; -import com.yf.exam.modules.qu.mapper.QuAnswerMapper; -import com.yf.exam.modules.qu.service.QuAnswerService; -import com.yf.exam.modules.qu.utils.ImageCheckUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.List; +package com.yf.exam.modules.qu.service.impl; // 定义包名,表示这是实现类部分,专注于处理与试题答案相关的逻辑 + +import com.alibaba.fastjson.JSON; // 导入 fastjson 库,用于 JSON 序列化和反序列化 +import com.alibaba.fastjson.TypeReference; // 导入 fastjson 库的 TypeReference,用于处理泛型类型 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; // 导入 MyBatis-Plus 的 QueryWrapper,用于构造查询条件 +import com.baomidou.mybatisplus.core.metadata.IPage; // 导入 MyBatis-Plus 的 IPage 接口,用于分页查询 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 MyBatis-Plus 的分页 Page 类 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; // 导入 MyBatis-Plus 的 ServiceImpl 基类 +import com.yf.exam.core.api.dto.PagingReqDTO; // 导入分页请求数据传输对象类 +import com.yf.exam.core.utils.BeanMapper; // 导入 BeanMapper 工具类,用于对象之间的映射 +import com.yf.exam.modules.qu.dto.QuAnswerDTO; // 导入试题答案的 DTO 类 +import com.yf.exam.modules.qu.entity.QuAnswer; // 导入试题答案的实体类 +import com.yf.exam.modules.qu.mapper.QuAnswerMapper; // 导入试题答案的 Mapper 接口 +import com.yf.exam.modules.qu.service.QuAnswerService; // 导入试题答案的服务接口 +import com.yf.exam.modules.qu.utils.ImageCheckUtils; // 导入图片校验工具类 +import org.springframework.beans.factory.annotation.Autowired; // 导入 Spring 的注解,自动注入依赖 +import org.springframework.stereotype.Service; // 导入 Spring 的服务注解,标识这是一个服务类 +import org.springframework.util.CollectionUtils; // 导入 Spring 的集合工具类,用于检查集合是否为空 + +import java.util.ArrayList; // 导入 ArrayList,用于动态数组 +import java.util.List; // 导入 List 接口,作为列表类型 /** *

@@ -28,65 +28,77 @@ import java.util.List; * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Service +@Service // 表示这是一个服务类,Spring 会自动扫描并管理该类 public class QuAnswerServiceImpl extends ServiceImpl implements QuAnswerService { @Autowired - private ImageCheckUtils imageCheckUtils; + private ImageCheckUtils imageCheckUtils; // 自动注入图片校验工具类,用于校验图片地址是否合法 @Override public IPage paging(PagingReqDTO reqDTO) { - - //创建分页对象 + // 创建分页对象,传入当前页和每页大小 IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); - //查询条件 + // 创建查询条件包装器 QueryWrapper wrapper = new QueryWrapper<>(); - //获得数据 + // 执行分页查询,获取分页结果 IPage page = this.page(query, wrapper); - //转换结果 + + // 将查询结果转换为 QuAnswerDTO 类型的分页结果 IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); return pageData; - } + } @Override public List listAnswerByRandom(String quId) { + // 创建查询条件包装器 QueryWrapper wrapper = new QueryWrapper<>(); + // 设置查询条件,过滤出与 quId 相同的记录 wrapper.lambda().eq(QuAnswer::getQuId, quId); + // 使用 SQL 的随机排序来随机获取答案 wrapper.last(" ORDER BY RAND() "); + // 执行查询并返回结果 return this.list(wrapper); } @Override public List listByQu(String quId) { + // 创建查询条件包装器 QueryWrapper wrapper = new QueryWrapper<>(); + // 设置查询条件,过滤出与 quId 相同的记录 wrapper.lambda().eq(QuAnswer::getQuId, quId); + // 执行查询,获取答案列表 List list = this.list(wrapper); if(!CollectionUtils.isEmpty(list)){ + // 将 QuAnswer 实体对象列表转换为 QuAnswerDTO 对象列表 return BeanMapper.mapList(list, QuAnswerDTO.class); } + // 如果没有找到记录,返回 null return null; } - /** * 查找已存在的列表 - * @param quId - * @return + * @param quId 试题ID + * @return 已存在答案的 ID 列表 */ public List findExistsList(String quId) { - //返回结果 + // 创建空的结果列表 List ids = new ArrayList<>(); + // 创建查询条件包装器 QueryWrapper wrapper = new QueryWrapper(); + // 设置查询条件,过滤出与 quId 相同的记录 wrapper.lambda().eq(QuAnswer::getQuId, quId); + // 执行查询,获取答案列表 List list = this.list(wrapper); if (!CollectionUtils.isEmpty(list)) { + // 将已有的答案 ID 添加到结果列表 for (QuAnswer item : list) { ids.add(item.getId()); } @@ -96,49 +108,49 @@ public class QuAnswerServiceImpl extends ServiceImpl i @Override public void saveAll(String quId, List list) { - - //最终要保存的列表 + // 创建保存的答案列表 List saveList = new ArrayList<>(); - //已存在的标签列表 + // 获取已有的答案 ID 列表 List ids = this.findExistsList(quId); + // 如果答案列表不为空,则进行处理 if(!CollectionUtils.isEmpty(list)){ for(QuAnswerDTO item: list){ - // 校验图片地址 + // 校验选项图片地址是否合法 imageCheckUtils.checkImage(item.getImage(), "选项图片地址错误!"); - //标签ID + // 获取答案 ID String id = item.getId(); QuAnswer answer = new QuAnswer(); + // 将 DTO 转换为实体类 BeanMapper.copy(item, answer); - answer.setQuId(quId); + answer.setQuId(quId); // 设置试题 ID - //补全ID避免新增 + // 如果该答案已存在,则从 IDs 列表中移除 if(ids.contains(id)){ ids.remove(id); } + // 添加答案到保存列表 saveList.add(answer); } - //保存标签列表 + // 如果有待保存的答案,则批量保存或更新 if(!CollectionUtils.isEmpty(saveList)) { this.saveOrUpdateBatch(saveList); } - //删除已移除 + // 如果有被移除的答案,则批量删除 if(!ids.isEmpty()){ this.removeByIds(ids); } }else{ - + // 如果答案列表为空,则删除所有与该试题 ID 相关的答案 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().eq(QuAnswer::getQuId, quId); this.remove(wrapper); } } - - } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuRepoServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuRepoServiceImpl.java index b7de030..c0e8f29 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuRepoServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuRepoServiceImpl.java @@ -1,175 +1,182 @@ -package com.yf.exam.modules.qu.service.impl; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.yf.exam.core.api.dto.PagingReqDTO; -import com.yf.exam.modules.qu.dto.QuRepoDTO; -import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO; -import com.yf.exam.modules.qu.entity.Qu; -import com.yf.exam.modules.qu.entity.QuRepo; -import com.yf.exam.modules.qu.mapper.QuMapper; -import com.yf.exam.modules.qu.mapper.QuRepoMapper; -import com.yf.exam.modules.qu.service.QuRepoService; -import com.yf.exam.modules.repo.service.RepoService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.List; +package com.yf.exam.modules.qu.service.impl; // 定义包名,表示这是服务实现类,负责处理与试题题库相关的业务逻辑 + +import com.alibaba.fastjson.JSON; // 导入 fastjson 库,用于 JSON 序列化和反序列化 +import com.alibaba.fastjson.TypeReference; // 导入 fastjson 库的 TypeReference,用于处理泛型类型 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; // 导入 MyBatis-Plus 的 QueryWrapper,用于构造查询条件 +import com.baomidou.mybatisplus.core.metadata.IPage; // 导入 MyBatis-Plus 的 IPage 接口,用于分页查询 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 MyBatis-Plus 的分页 Page 类 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; // 导入 MyBatis-Plus 的 ServiceImpl 基类 +import com.yf.exam.core.api.dto.PagingReqDTO; // 导入分页请求数据传输对象类 +import com.yf.exam.modules.qu.dto.QuRepoDTO; // 导入试题题库的 DTO 类 +import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO; // 导入试题题库批量操作请求类 +import com.yf.exam.modules.qu.entity.Qu; // 导入试题实体类 +import com.yf.exam.modules.qu.entity.QuRepo; // 导入试题题库实体类 +import com.yf.exam.modules.qu.mapper.QuMapper; // 导入试题的 Mapper 接口 +import com.yf.exam.modules.qu.mapper.QuRepoMapper; // 导入试题题库的 Mapper 接口 +import com.yf.exam.modules.qu.service.QuRepoService; // 导入试题题库服务接口 +import com.yf.exam.modules.repo.service.RepoService; // 导入题库服务接口 +import org.springframework.beans.factory.annotation.Autowired; // 导入 Spring 的注解,自动注入依赖 +import org.springframework.stereotype.Service; // 导入 Spring 的服务注解,标识这是一个服务类 +import org.springframework.util.CollectionUtils; // 导入 Spring 的集合工具类,用于检查集合是否为空 + +import java.util.ArrayList; // 导入 ArrayList,用于动态数组 +import java.util.List; // 导入 List 接口,作为列表类型 /** *

-* 语言设置 服务实现类 +* 试题题库 服务实现类 *

* * @author 聪明笨狗 * @since 2020-05-25 13:23 */ -@Service +@Service // 表示这是一个 Spring 服务类,Spring 会自动扫描并管理该类 public class QuRepoServiceImpl extends ServiceImpl implements QuRepoService { - @Autowired - private QuMapper quMapper; + private QuMapper quMapper; // 自动注入试题的 Mapper 接口 @Autowired - private RepoService repoService; + private RepoService repoService; // 自动注入题库服务接口 @Override public IPage paging(PagingReqDTO reqDTO) { - - //创建分页对象 + // 创建分页对象,传入当前页和每页大小 IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); - //查询条件 + // 创建查询条件包装器 QueryWrapper wrapper = new QueryWrapper<>(); - //获得数据 + // 执行分页查询,获取分页结果 IPage page = this.page(query, wrapper); - //转换结果 + + // 将查询结果转换为 QuRepoDTO 类型的分页结果 IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); return pageData; - } + } @Override public void saveAll(String quId, Integer quType, List ids) { - // 先删除 + // 先删除已有的试题题库记录 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().eq(QuRepo::getQuId, quId); this.remove(wrapper); - // 保存全部 + // 如果题库 ID 列表不为空,保存新的记录 if(!CollectionUtils.isEmpty(ids)){ List list = new ArrayList<>(); for(String id: ids){ QuRepo ref = new QuRepo(); - ref.setQuId(quId); - ref.setRepoId(id); - ref.setQuType(quType); + ref.setQuId(quId); // 设置试题 ID + ref.setRepoId(id); // 设置题库 ID + ref.setQuType(quType); // 设置题目类型 list.add(ref); } + // 批量保存试题题库记录 this.saveBatch(list); - + // 对每个题库进行排序 for(String id: ids){ this.sortRepo(id); } } - - } @Override public List listByQu(String quId) { - // 先删除 + // 根据试题 ID 查找题库记录 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().eq(QuRepo::getQuId, quId); List list = this.list(wrapper); List ids = new ArrayList<>(); if(!CollectionUtils.isEmpty(list)){ + // 提取题库 ID 列表 for(QuRepo item: list){ ids.add(item.getRepoId()); } } - return ids; + return ids; // 返回题库 ID 列表 } @Override public List listByRepo(String repoId, Integer quType, boolean rand) { + // 根据题库 ID 和题目类型查询题库记录 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda() - .eq(QuRepo::getRepoId, repoId); + wrapper.lambda().eq(QuRepo::getRepoId, repoId); - if(quType!=null){ + // 如果有题目类型,添加过滤条件 + if(quType != null){ wrapper.lambda().eq(QuRepo::getQuType, quType); } + // 根据是否需要随机排序决定排序方式 if(rand){ - wrapper.orderByAsc(" RAND() "); + wrapper.orderByAsc(" RAND() "); // 随机排序 }else{ - wrapper.lambda().orderByAsc(QuRepo::getSort); + wrapper.lambda().orderByAsc(QuRepo::getSort); // 按照排序字段排序 } + // 执行查询,获取题库记录列表 List list = this.list(wrapper); List ids = new ArrayList<>(); if(!CollectionUtils.isEmpty(list)){ + // 提取试题 ID 列表 for(QuRepo item: list){ ids.add(item.getQuId()); } } - return ids; + return ids; // 返回试题 ID 列表 } @Override public void batchAction(QuRepoBatchReqDTO reqDTO) { - - // 移除的 - if(reqDTO.getRemove()!=null && reqDTO.getRemove()){ + // 如果需要移除记录 + if(reqDTO.getRemove() != null && reqDTO.getRemove()){ + // 删除满足条件的题库记录 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda() .in(QuRepo::getRepoId, reqDTO.getRepoIds()) .in(QuRepo::getQuId, reqDTO.getQuIds()); this.remove(wrapper); }else{ - - // 新增的 + // 如果是新增记录,处理新增逻辑 for(String quId : reqDTO.getQuIds()){ + // 根据试题 ID 查询试题类型 Qu q = quMapper.selectById(quId); + // 保存新的题库记录 this.saveAll(quId, q.getQuType(), reqDTO.getRepoIds()); } } + // 对每个题库进行排序 for(String id: reqDTO.getRepoIds()){ this.sortRepo(id); } - } - /** * 单个题库进行排序 - * @param repoId + * @param repoId 题库 ID */ private void sortRepo(String repoId){ - + // 查询题库下的所有试题 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().eq(QuRepo::getRepoId, repoId); - List list = this.list(wrapper); + + // 如果题库下没有试题,返回 if(CollectionUtils.isEmpty(list)){ return; } + // 按照顺序设置每个试题的排序值 int sort = 1; for(QuRepo item: list){ item.setSort(sort); sort++; } + + // 批量更新排序值 this.updateBatchById(list); } } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuServiceImpl.java b/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuServiceImpl.java index f9ae656..3594ff6 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuServiceImpl.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/service/impl/QuServiceImpl.java @@ -1,9 +1,12 @@ package com.yf.exam.modules.qu.service.impl; +// 导入MyBatis Plus相关类 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +// 导入其他相关类 import com.yf.exam.ability.upload.config.UploadConfig; import com.yf.exam.core.api.dto.PagingReqDTO; import com.yf.exam.core.exception.ServiceException; @@ -23,6 +26,7 @@ import com.yf.exam.modules.qu.service.QuRepoService; import com.yf.exam.modules.qu.service.QuService; import com.yf.exam.modules.qu.utils.ImageCheckUtils; import com.yf.exam.modules.repo.service.RepoService; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -37,7 +41,7 @@ import java.util.Map; /** *

- * 语言设置 服务实现类 + * 题目管理服务实现类 *

* * @author 聪明笨狗 @@ -46,125 +50,136 @@ import java.util.Map; @Service public class QuServiceImpl extends ServiceImpl implements QuService { + // 注入QuAnswerService服务 @Autowired private QuAnswerService quAnswerService; + // 注入QuRepoService服务 @Autowired private QuRepoService quRepoService; + // 注入图片校验工具类 @Autowired private ImageCheckUtils imageCheckUtils; + // 分页查询题目列表 @Override public IPage paging(PagingReqDTO reqDTO) { - //创建分页对象 + // 创建分页对象 Page page = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); - //转换结果 + // 调用baseMapper的分页查询方法,获取分页数据 IPage pageData = baseMapper.paging(page, reqDTO.getParams()); return pageData; } + // 删除题目、答案和题库绑定 @Transactional(rollbackFor = Exception.class) @Override public void delete(List ids) { - // 移除题目 + // 删除题目 this.removeByIds(ids); - // 移除选项 + // 删除与题目相关的选项 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().in(QuAnswer::getQuId, ids); quAnswerService.remove(wrapper); - // 移除题库绑定 + // 删除题库与题目的绑定 QueryWrapper wrapper1 = new QueryWrapper<>(); wrapper1.lambda().in(QuRepo::getQuId, ids); quRepoService.remove(wrapper1); } + // 随机获取题目 @Override public List listByRandom(String repoId, Integer quType, List excludes, Integer size) { return baseMapper.listByRandom(repoId, quType, excludes, size); } + // 获取题目的详细信息 @Override public QuDetailDTO detail(String id) { QuDetailDTO respDTO = new QuDetailDTO(); + // 获取题目信息 Qu qu = this.getById(id); BeanMapper.copy(qu, respDTO); + // 获取题目的选项信息 List answerList = quAnswerService.listByQu(id); respDTO.setAnswerList(answerList); + // 获取题目所属的题库 List repoIds = quRepoService.listByQu(id); respDTO.setRepoIds(repoIds); return respDTO; } - + // 保存题目信息 @Transactional(rollbackFor = Exception.class) @Override public void save(QuDetailDTO reqDTO) { - - // 校验数据 + // 校验题目信息 this.checkData(reqDTO, ""); Qu qu = new Qu(); + // 将题目详情复制到实体类 BeanMapper.copy(reqDTO, qu); - // 校验图片地址 + // 校验图片地址是否正确 imageCheckUtils.checkImage(qu.getImage(), "题干图片地址错误!"); - // 更新 + // 保存或更新题目信息 this.saveOrUpdate(qu); - // 保存全部问题 + // 保存题目的选项 quAnswerService.saveAll(qu.getId(), reqDTO.getAnswerList()); - // 保存到题库 + // 保存题目与题库的绑定 quRepoService.saveAll(qu.getId(), qu.getQuType(), reqDTO.getRepoIds()); - } + // 获取题目导出的列表 @Override public List listForExport(QuQueryReqDTO query) { return baseMapper.listForExport(query); } + // 导入Excel数据 @Override public int importExcel(List dtoList) { - //根据题目名称分组 + // 根据题目名称分组 Map> anMap = new HashMap<>(16); - //题目本体信息 + // 存储题目信息 Map quMap = new HashMap<>(16); - //数据分组 + // 分组数据 for (QuExportDTO item : dtoList) { - // 空白的ID + // 如果题目ID为空,跳过 if (StringUtils.isEmpty(item.getNo())) { continue; } Integer key; - //序号 + // 获取题目序号 try { key = Integer.parseInt(item.getNo()); } catch (Exception e) { continue; } - //如果已经有题目了,直接处理选项 + // 如果题目已存在,直接处理选项 if (anMap.containsKey(key)) { anMap.get(key).add(item); } else { - //如果没有,将题目内容和选项一起 + // 如果没有,将题目内容和选项一起放入 List subList = new ArrayList<>(); subList.add(item); anMap.put(key, subList); @@ -174,49 +189,46 @@ public class QuServiceImpl extends ServiceImpl implements QuServic int count = 0; try { - - //循环题目插入 + // 遍历题目插入 for (Integer key : quMap.keySet()) { QuExportDTO im = quMap.get(key); - //题目基本信息 + // 处理题目的基本信息 QuDetailDTO qu = new QuDetailDTO(); qu.setContent(im.getQContent()); qu.setAnalysis(im.getQAnalysis()); qu.setQuType(Integer.parseInt(im.getQuType())); qu.setCreateTime(new Date()); - //设置回答列表 + // 设置题目的回答列表 List answerList = this.processAnswerList(anMap.get(key)); - //设置题目 qu.setAnswerList(answerList); - //设置引用题库 + + // 设置题目所属的题库 qu.setRepoIds(im.getRepoList()); - // 保存答案 + + // 保存题目 this.save(qu); count++; } } catch (ServiceException e) { e.printStackTrace(); + // 异常处理,抛出导入失败的异常 throw new ServiceException(1, "导入出现问题,行:" + count + "," + e.getMessage()); } return count; } - /** - * 处理回答列表 - * - * @param importList - * @return - */ + // 处理题目的回答列表 private List processAnswerList(List importList) { List list = new ArrayList<>(16); for (QuExportDTO item : importList) { QuAnswerDTO a = new QuAnswerDTO(); + // 设置选项是否正确 a.setIsRight("1".equals(item.getAIsRight())); a.setContent(item.getAContent()); a.setAnalysis(item.getAAnalysis()); @@ -226,58 +238,52 @@ public class QuServiceImpl extends ServiceImpl implements QuServic return list; } - /** - * 校验题目信息 - * - * @param qu - * @param no - * @throws Exception - */ + // 校验题目信息 public void checkData(QuDetailDTO qu, String no) { - + // 校验题目内容不能为空 if (StringUtils.isEmpty(qu.getContent())) { throw new ServiceException(1, no + "题目内容不能为空!"); } - + // 校验至少选择一个题库 if (CollectionUtils.isEmpty(qu.getRepoIds())) { throw new ServiceException(1, no + "至少要选择一个题库!"); } + // 校验回答选项 List answers = qu.getAnswerList(); + if (CollectionUtils.isEmpty(answers)) { + throw new ServiceException(1, no + "客观题至少要包含一个备选答案!"); + } + int trueCount = 0; + for (QuAnswerDTO a : answers) { - if (CollectionUtils.isEmpty(answers)) { - throw new ServiceException(1, no + "客观题至少要包含一个备选答案!"); + // 校验选项是否定义了正确标志 + if (a.getIsRight() == null) { + throw new ServiceException(1, no + "必须定义选项是否正确项!"); } - - int trueCount = 0; - for (QuAnswerDTO a : answers) { - - if (a.getIsRight() == null) { - throw new ServiceException(1, no + "必须定义选项是否正确项!"); - } - - if (StringUtils.isEmpty(a.getContent())) { - throw new ServiceException(1, no + "选项内容不为空!"); - } - - if (a.getIsRight()) { - trueCount += 1; - } + // 校验选项内容不能为空 + if (StringUtils.isEmpty(a.getContent())) { + throw new ServiceException(1, no + "选项内容不为空!"); } - if (trueCount == 0) { - throw new ServiceException(1, no + "至少要包含一个正确项!"); + // 统计正确选项的个数 + if (a.getIsRight()) { + trueCount += 1; } + } + // 校验至少包含一个正确选项 + if (trueCount == 0) { + throw new ServiceException(1, no + "至少要包含一个正确项!"); + } - //单选题 - if (qu.getQuType().equals(QuType.RADIO) && trueCount > 1) { - throw new ServiceException(1, no + "单选题不能包含多个正确项!"); - } - + // 单选题不能包含多个正确选项 + if (qu.getQuType().equals(QuType.RADIO) && trueCount > 1) { + throw new ServiceException(1, no + "单选题不能包含多个正确项!"); + } } } diff --git a/src-源文件/main/java/com/yf/exam/modules/qu/utils/ImageCheckUtils.java b/src-源文件/main/java/com/yf/exam/modules/qu/utils/ImageCheckUtils.java index 707fbcd..902c507 100644 --- a/src-源文件/main/java/com/yf/exam/modules/qu/utils/ImageCheckUtils.java +++ b/src-源文件/main/java/com/yf/exam/modules/qu/utils/ImageCheckUtils.java @@ -1,31 +1,42 @@ +// 定义包名,表示该类属于com.yf.exam.modules.qu.utils包下 package com.yf.exam.modules.qu.utils; +// 导入项目中定义的上传配置类 import com.yf.exam.ability.upload.config.UploadConfig; +// 导入项目中定义的服务异常类 import com.yf.exam.core.exception.ServiceException; +// 导入Apache Commons Lang库中的StringUtils类,用于字符串操作 import org.apache.commons.lang3.StringUtils; +// 导入Spring框架中的注解,用于自动注入依赖 import org.springframework.beans.factory.annotation.Autowired; +// 导入Spring框架中的注解,用于声明组件 import org.springframework.stereotype.Component; +/** + * 图片校验工具类,提供图片地址校验的功能 + */ @Component public class ImageCheckUtils { + // 自动注入上传配置,用于获取图片上传的相关配置 @Autowired private UploadConfig conf; /** - * 进行图片校验! - * @param image - * @param throwMsg + * 进行图片校验的方法 + * @param image 图片地址 + * @param throwMsg 校验失败时抛出的异常信息 */ public void checkImage(String image, String throwMsg) { - + // 如果图片地址为空或空白,则直接返回,不进行校验 if(StringUtils.isBlank(image)){ return; } - // 校验图片地址 + // 校验图片地址是否以配置的URL开头,确保图片地址是合法的 if(!image.startsWith(conf.getUrl())){ + // 如果图片地址不合法,则抛出服务异常 throw new ServiceException(throwMsg); } } -} +} \ No newline at end of file