Compare commits

..

54 Commits
xz ... main

Author SHA1 Message Date
pyc5qsxp3 e7b557c382 Merge pull request 'add' (#3) from dq into main
1 year ago
Tian 666c4ea19e figureout conflicts
1 year ago
Tian 1c81bdb6c1 sovled problem
1 year ago
dq 6a38ed381d 66
1 year ago
pyc5qsxp3 e133a261bc Merge pull request 'fmy' (#1) from fmy into main
1 year ago
dq 4ebf25342f 66
1 year ago
dq e27cb6dbd4 66
1 year ago
dq f19aaaf5f2 66
1 year ago
dq 514fd36931 66
1 year ago
dq d9b9546f18 66
1 year ago
dq 3e5050668a 66
1 year ago
dq b8a2391af2 66
1 year ago
dq 09eb7afcc6 66
1 year ago
dq ea2b04ee2c 66
1 year ago
dq 1b26c492c1 66
1 year ago
dq ef677aa5da 66
1 year ago
fmy 71041f45b0 main1122
1 year ago
fmy 89a1ee70f4 main1122
1 year ago
dq 80623a9e2f 66
1 year ago
dq e6f8da772e 66
1 year ago
fmy c6b83d29f1 main1122
1 year ago
fmy 092da9cd0a main1122
1 year ago
fmy 75a12190b9 main1122
1 year ago
fmy a8361f16b1 main1122
1 year ago
dq 675254876e 66
1 year ago
dq 2f6785f470 66
1 year ago
dq 4a0056d8c2 55
1 year ago
dq 8c09dc7162 44
1 year ago
dq 12f9ed14c6 33
1 year ago
dq a5db41e01c 222
1 year ago
kilo 6c2dae282f 222
1 year ago
Tian 73d8098ca2 222
1 year ago
Tian e6047daa65 999
1 year ago
Tian 65a895120d 777
1 year ago
Tian 74245d62ec 4556
1 year ago
Tian b0337148c1 677
1 year ago
Tian fd3339c2bb 111
1 year ago
Tian 98f01bf630 22
1 year ago
Tian 9491099a6f 77
1 year ago
Tian b55113bbb0 44
1 year ago
Tian dc5ff0ff0f global
1 year ago
Tian f945fc6b1e handler
1 year ago
Tian cb4f555052 answer
1 year ago
Tian 8b76b795cf app
1 year ago
Tian 0208d80240 AsPect
1 year ago
Tian f89a845c63 222
1 year ago
Tian 959ff63f9a 222
1 year ago
fmy 593c5e0a37 main1122
1 year ago
Tian a5018f3027 222
1 year ago
fmy ffcd518bfd main112
1 year ago
Tian f148764a15 222
1 year ago
fmy 844711fec9 main11
1 year ago
Tian b93aba5c45 1111
1 year ago
fmy 059a70f2c9 main
1 year ago

@ -1,88 +1,163 @@
package cn.org.alan.exam.common.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
package cn.org.alan.exam.common.aop; // 声明当前类所在的包路径,用于组织类结构,避免命名冲突。
import org.aspectj.lang.JoinPoint; // 导入AspectJ的JoinPoint接口代表程序执行过程中的连接点如方法执行点
import org.aspectj.lang.ProceedingJoinPoint; // 导入ProceedingJoinPoint接口用于环绕通知可控制方法执行。
import org.aspectj.lang.Signature; // 导入Signature接口表示被通知方法的签名。
import org.aspectj.lang.annotation.Around; // 导入Around注解用于声明环绕通知。
import org.aspectj.lang.annotation.Aspect; // 导入Aspect注解用于声明一个切面类。
import org.aspectj.lang.annotation.Before; // 导入Before注解用于声明前置通知即方法执行前的操作。
import org.aspectj.lang.annotation.Pointcut; // 导入Pointcut注解用于定义切点即指定拦截哪些方法。
import org.springframework.stereotype.Component; // 导入Spring的Component注解声明该类为Spring组件纳入Spring管理。
import cn.hutool.core.util.RandomUtil; // 导入Hutool工具包中的RandomUtil类用于生成随机数。
import com.alibaba.fastjson.JSONObject; // 导入阿里巴巴Fastjson库中的JSONObject类用于处理JSON数据。
import com.alibaba.fastjson.support.spring.PropertyPreFilters; // 导入Fastjson的PropertyPreFilters类用于Spring环境下JSON属性过滤。
import jakarta.servlet.ServletRequest; // 导入Jakarta Servlet API的ServletRequest接口表示客户端请求。
import jakarta.servlet.ServletResponse; // 导入Jakarta Servlet API的ServletResponse接口表示服务器响应。
import jakarta.servlet.http.HttpServletRequest; // 导入Jakarta Servlet API的HttpServletRequest接口表示HTTP请求。
import org.slf4j.Logger; // 导入SLF4J日志框架的Logger接口用于记录日志。
import org.slf4j.LoggerFactory; // 导入SLF4J日志框架的LoggerFactory类用于获取Logger实例。
import org.slf4j.MDC; // 导入SLF4J的MDC类用于存储与当前线程绑定的诊断信息。
import org.springframework.web.context.request.RequestContextHolder; // 导入Spring的RequestContextHolder类用于持有请求上下文信息。
import org.springframework.web.context.request.ServletRequestAttributes; // 导入Spring的ServletRequestAttributes类用于获取HTTP请求和响应的详细信息。
import org.springframework.web.multipart.MultipartFile; // 导入Spring的MultipartFile接口用于处理文件上传。
/**
* @Author Alan
* @Version
* @Date 2024/5/13 2:33 PM111
* @Date 2024/5/13 2:33 PM
*/
@Aspect
@Component
public class LogAsPect {
// 声明一个私有的静态的Logger对象用于记录日志。通过LoggerFactory根据当前类LogAsPect的类名来获取对应的日志记录器实例
// 后续可以使用这个实例输出不同级别的日志信息如DEBUG、INFO、WARN等具体的日志输出配置例如输出到控制台还是文件、日志级别设置等
// 通常由项目所集成的日志框架如Log4j、Slf4j等结合对应的配置文件来决定。
private final static Logger LOG = LoggerFactory.getLogger(LogAsPect.class);
// 这是一个被注释掉的Around注解配置原本意图可能是匹配cn.org.alan.exam.controller包及其子包下所有类中的所有方法无论参数情况如何
// 但目前处于注释状态,未生效。如果启用,可用于定义一个环绕通知的切点,决定在哪些方法调用前后执行额外逻辑。
// @Around("execution(* cn.org.alan.exam.controller..*.*(..))")
// 使用@Pointcut注解定义一个切点表达式用于指定切面的作用范围。此表达式表示匹配cn.org.alan.exam.controller包及其子包下所有类名以Controller结尾的类中的
// 所有公共方法方法参数不限意味着后续定义的通知Advice将会作用于这些符合条件的方法上比如在这些方法执行前、执行后等添加自定义逻辑。
@Pointcut("execution(public * cn.org.alan.exam.controller..*Controller.*(..))")
public void controllerPointcut() {}
// @Before注解表明这是一个前置通知方法它会在匹配controllerPointcut切点所指定的那些目标方法执行之前被调用
// 在这里可以进行一些准备工作例如记录请求相关的信息等操作joinPoint参数可用于获取目标方法相关的各种信息如方法参数、方法签名等
@Before("controllerPointcut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 通过RequestContextHolder获取当前请求相关的属性它会尝试获取与当前线程绑定的ServletRequestAttributes对象
// 这个对象包含了与当前请求相关的信息例如HttpServletRequest、HttpServletResponse等此处将其强制转换为ServletRequestAttributes类型。
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// 从ServletRequestAttributes对象中获取HttpServletRequest对象后续可以通过这个对象获取请求的各种详细信息比如请求的URL、请求方法、请求头信息等。
HttpServletRequest request = attributes.getRequest();
// 获取目标方法的签名信息,包含了方法所在的类、方法名、参数类型等相关信息,通过这个签名对象可以进一步提取出方法相关的具体细节内容。
Signature signature = joinPoint.getSignature();
// 从方法签名中获取方法的名称,后续可用于在日志中记录具体是哪个方法被调用了,方便定位和排查问题。
String name = signature.getName();
// 使用日志记录器输出一条日志信息,表示后续要记录的是一次请求相关信息的开始部分,方便在日志中清晰区分不同请求的记录范围。
LOG.info("------------- 开始 -------------");
// 使用日志记录器输出请求的完整URL地址以及请求的方法例如GET、POST等通过调用HttpServletRequest对象的相关方法获取这些信息并记录到日志中
// 有助于了解请求的来源以及请求的方式,便于后续分析请求流程和排查问题。
LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
// 使用日志记录器记录被调用的类名和方法名通过方法签名获取类名使用getDeclaringTypeName方法和前面获取的方法名拼接成字符串记录到日志中
// 这样可以清晰知道具体是哪个控制器类的哪个方法发起了此次请求,方便定位业务逻辑所在的具体位置。
LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
// 使用日志记录器记录客户端的远程IP地址通过HttpServletRequest对象获取请求来源的IP地址并记录到日志中有助于了解请求是从哪个客户端发起的
// 对于安全审计、访问控制以及问题排查等方面都有帮助。
LOG.info("远程地址: {}", request.getRemoteAddr());
// 获取目标方法的所有参数,以数组形式返回,这些参数就是调用目标方法时传入的实际参数,后续可以根据需求对这些参数进行处理、分析或者记录等操作。
Object[] args = joinPoint.getArgs();
// 创建一个新的Object数组其长度与目标方法的参数数组长度相同用于存放经过筛选后的参数初始化为默认值null等取决于对象类型的默认值
// 目的是排除一些不需要详细记录到日志中的参数类型,重新整理参数列表。
Object[] arguments = new Object[args.length];
// 遍历目标方法的参数数组,对每个参数进行类型判断。
for (int i = 0; i < args.length; i++) {
// 如果参数的类型是ServletRequest例如HttpServletRequest等表示请求相关对象、ServletResponse表示响应相关对象
// 或者MultipartFile通常用于处理文件上传的对象则跳过当前参数不将其添加到要记录的参数列表arguments中因为这些类型的对象可能包含大量不必要在日志中展示的信息
// 或者可能涉及到敏感信息等情况,所以进行排除处理。
if (args[i] instanceof ServletRequest
|| args[i] instanceof ServletResponse
|| args[i] instanceof MultipartFile) {
continue;
}
// 如果参数类型不属于上述要排除的类型则将该参数复制到新的参数数组arguments中以便后续记录到日志里展示真正与业务逻辑相关的参数信息。
arguments[i] = args[i];
}
// 创建一个字符串数组,用于指定要排除的属性名称,这里定义了"password"(可能代表密码等敏感信息)和"file"(可能是文件相关的一些不想在日志中暴露的属性),
// 可以根据实际项目中敏感信息的情况进行调整这些属性在将对象转换为JSON字符串记录日志时会被排除掉以保护敏感数据。
String[] excludeProperties = {"password", "file"};
// 创建一个PropertyPreFilters对象这个对象通常用于对对象属性进行过滤处理比如在将对象转换为JSON字符串时按照一定规则排除某些属性
// 它提供了添加过滤器等功能来实现属性的筛选操作。
PropertyPreFilters filters = new PropertyPreFilters();
// 通过PropertyPreFilters对象添加一个简单的属性预过滤器实例后续可以通过这个过滤器实例设置具体要排除的属性等规则用于控制对象属性在转换为JSON字符串时的筛选行为。
PropertyPreFilters.MySimplePropertyPreFilter excludeFilter = filters.addFilter();
// 将前面定义的要排除的属性名称数组添加到过滤器中告诉过滤器在处理对象转换为JSON字符串时需要把这些指定名称的属性排除掉从而实现对敏感属性的过滤
// 保证日志记录中的参数信息既包含了必要的业务相关内容,又不会泄露敏感数据。
excludeFilter.addExcludes(excludeProperties);
// 使用日志记录器记录请求参数信息通过JSONObject将经过筛选后的参数数组arguments转换为JSON字符串进行记录在转换过程中会依据前面设置的excludeFilter过滤器规则
// 排除指定的敏感属性,这样在日志中看到的请求参数就是经过处理后的安全且关键的业务相关参数内容了。
LOG.info("请求参数: {}", JSONObject.toJSONString(arguments, excludeFilter));
}
// @Around注解定义了一个环绕通知方法环绕通知可以在目标方法执行前、执行后都添加自定义逻辑相比于前置通知和后置通知更加灵活
// 它可以完全控制目标方法的执行过程,比如可以在执行目标方法前进行一些前置操作,执行后进行一些后置操作,并且可以决定目标方法是否执行以及如何处理返回结果等,
// proceedingJoinPoint参数用于在环绕通知中操作目标方法如调用目标方法等以及获取相关信息。
@Around("controllerPointcut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 获取当前系统的时间戳(以毫秒为单位),记录目标方法开始执行的时间,后续通过与方法执行结束后的时间戳做差值运算,可以得到方法执行所耗费的时间,
// 用于性能分析和监控目标方法的执行效率。
long startTime = System.currentTimeMillis();
// 通过proceedingJoinPoint的proceed方法执行被拦截的目标方法这个方法调用会使得目标方法按照正常逻辑执行并且会返回目标方法的执行结果
// 将返回结果保存到result变量中以便后续进行处理如记录返回结果到日志中、对返回结果进行业务逻辑上的进一步操作等
Object result = proceedingJoinPoint.proceed();
// 创建一个字符串数组,用于指定要排除的属性名称,这里同样定义了"password"和"file",与前置通知中保持一致,用于对返回结果中的敏感属性进行过滤,
// 避免将敏感信息记录到日志中,可根据实际项目的敏感数据情况进行调整。
String[] excludeProperties = {"password", "file"};
// 创建一个PropertyPreFilters对象用于对返回结果对象的属性进行过滤处理以便在记录日志时按照规则排除某些属性实现对敏感信息的保护以及日志内容的优化。
PropertyPreFilters filters = new PropertyPreFilters();
// 通过PropertyPreFilters对象添加一个简单的属性预过滤器实例后续通过这个实例设置具体要排除的属性规则用于控制返回结果对象在转换为JSON字符串时的筛选行为。
PropertyPreFilters.MySimplePropertyPreFilter excludeFilter = filters.addFilter();
// 将前面定义的要排除的属性名称数组添加到过滤器中使得过滤器知道在处理返回结果对象转换为JSON字符串时需要把这些指定名称的属性排除掉
// 从而在日志中记录安全的返回结果信息,只展示非敏感且与业务相关的内容。
excludeFilter.addExcludes(excludeProperties);
// 使用日志记录器记录返回结果信息通过JSONObject将返回结果对象result转换为JSON字符串进行记录在转换过程中会依据前面设置的excludeFilter过滤器规则
// 排除指定的敏感属性,这样在日志中看到的返回结果就是经过处理后的安全且关键的业务相关内容了,方便查看方法执行后的返回情况以及进行后续的业务分析。
LOG.info("返回结果: {}", JSONObject.toJSONString(result, excludeFilter));
// 使用日志记录器记录一条日志信息表示目标方法执行结束并通过计算当前系统时间戳与开始记录的时间戳startTime的差值得到目标方法执行所耗费的时间以毫秒为单位
// 将耗时信息记录到日志中,有助于分析目标方法的性能表现,了解其执行效率是否符合预期,对于性能优化和问题排查等方面有重要作用。
LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
// 将目标方法的原始返回结果返回,确保在切面逻辑执行完毕后,调用目标方法的地方能够接收到正确的返回值,维持方法调用链路的正常返回流程,
// 不会因为添加了切面逻辑而改变目标方法原本的返回情况,保证业务逻辑的正确性。
return result;
}
}
}

@ -2,11 +2,13 @@ package cn.org.alan.exam.common.exception;
/**
* @Author WeiJin
* @Version 1.0
* @Version 1.022222
* @Date 2024/3/29 21:06
*/
public class AppException extends RuntimeException{
public AppException(String msg){
super(msg);
public class AppException extends RuntimeException { // 定义一个名为AppException的自定义异常类它继承自RuntimeException。
public AppException(String msg) { // 定义一个构造方法接收一个字符串参数msg作为异常信息。
super(msg); // 调用父类RuntimeException的构造方法将msg传递给父类以便父类可以处理这个异常信息。
}
}
} // 类定义结束。

@ -5,8 +5,13 @@ package cn.org.alan.exam.common.group;
* @Version 1.0
* @Date 2024/4/29 15:14
*/
public interface AnswerGroup {
public interface AnswerGroup { // 定义一个公开的接口AnswerGroup。接口是Java中一种抽象类型用于指定一组方法但不提供这些方法的实现。
interface CorrectGroup extends AnswerGroup {
// 在AnswerGroup接口内部定义了一个嵌套接口CorrectGroup。
// 嵌套接口是定义在另一个接口或类内部的接口。
interface CorrectGroup extends AnswerGroup { // CorrectGroup接口继承了AnswerGroup接口意味着它必须实现或间接实现AnswerGroup接口中声明的所有方法如果有的话
// 但在这个例子中CorrectGroup没有添加任何新的方法或属性。
// 它可能仅用于表示一个特定的、更具体的AnswerGroup类型或子集。
}
}
} // AnswerGroup接口定义结束。

@ -5,6 +5,6 @@ package cn.org.alan.exam.common.group;
* @ Version 1.0
* @ Date 2024/5/11 14:45
*/
public interface CertificateGroup {
interface CertificateInsertGroup extends CertificateGroup{}
public interface CertificateGroup { // 定义一个名为CertificateGroup的公共接口
interface CertificateInsertGroup extends CertificateGroup{} // 在CertificateGroup内部定义一个名为CertificateInsertGroup的接口它继承自CertificateGroup
}

@ -5,6 +5,6 @@ package cn.org.alan.exam.common.group;
* @Version 1.0
* @Date 2024/4/1 11:22
*/
public interface QuestionGroup {
interface QuestionAddGroup extends QuestionGroup{}
}
public interface QuestionGroup { // 声明一个公共接口 QuestionGroup。这是一个抽象类型用于定义一组问题的共同行为或属性。
interface QuestionAddGroup extends QuestionGroup{} // 在 QuestionGroup 接口内部声明另一个接口 QuestionAddGroup。这个内部接口继承自其外部接口 QuestionGroup。
}

@ -1,21 +1,21 @@
package cn.org.alan.exam.common.group;
package cn.org.alan.exam.common.group; // 这行代码声明了一个包路径用于组织Java类、接口等。这个包路径表明当前接口位于项目的cn.org.alan.exam.common.group目录下。
/**
* @Author WeiJin
* @Version 1.0
* @Date 2024/3/29 15:43
* @Author WeiJin // 这行是JavaDoc注释用于说明这个接口的作者。
* @Version 1.0 // 这行JavaDoc注释说明了接口的版本信息。
* @Date 2024/3/29 15:43 // 这行JavaDoc注释记录了接口创建或最后修改的时间。
*/
public interface UserGroup {
public interface UserGroup { // 声明了一个公共接口UserGroup。这个接口可能是为了定义与用户组相关的某些行为或属性而创建的。
/**
*
* // 这行是JavaDoc注释用于说明接下来的接口CreateUserGroup的用途或功能。
*/
interface CreateUserGroup extends UserGroup{}
interface CreateUserGroup extends UserGroup{} // 在UserGroup接口内部声明了一个名为CreateUserGroup的接口它继承自UserGroup接口。这个内部接口可能用于定义用户创建时参数校验的规则或行为。
/**
*
* // 这行是JavaDoc注释用于说明接下来的接口UpdatePasswordGroup的用途或功能。
*/
interface UpdatePasswordGroup extends UserGroup{}
interface UpdatePasswordGroup extends UserGroup{} // 在UserGroup接口内部声明了另一个名为UpdatePasswordGroup的接口它也继承自UserGroup接口。这个内部接口可能用于定义用户修改密码时参数校验的规则或行为。
interface RegisterGroup extends UserGroup{}
}
interface RegisterGroup extends UserGroup{} // 在UserGroup接口内部声明了第三个名为RegisterGroup的接口它同样继承自UserGroup接口。这个内部接口可能用于定义用户注册时相关的行为或规则尽管这里没有提供JavaDoc注释来说明其具体用途。
}

@ -1,17 +1,18 @@
package cn.org.alan.exam.common.handler;
import cn.org.alan.exam.util.DateTimeUtil;
import cn.org.alan.exam.util.SecurityUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;
package cn.org.alan.exam.common.handler; // 声明类所在的包路径。
// 导入所需的类和接口
import cn.org.alan.exam.util.DateTimeUtil; // 导入自定义的日期时间工具类。
import cn.org.alan.exam.util.SecurityUtil; // 导入自定义的安全工具类。
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; // 导入MyBatis Plus的元数据对象处理器接口。
import lombok.extern.slf4j.Slf4j; // 导入Lombok的日志注解。
import org.apache.ibatis.reflection.MetaObject; // 导入MyBatis的元数据对象接口。
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; // 导入Spring Security的BCrypt密码编码器虽然在这段代码中未使用
import org.springframework.stereotype.Component; // 导入Spring的组件注解。
import java.lang.reflect.Field; // 导入Java反射的Field类。
import java.time.LocalDateTime; // 导入Java 8的日期时间类。
import java.util.Arrays; // 导入Java的Arrays工具类。
import java.util.Objects; // 导入Java的Objects工具类。
/**
* mybatisPlus
@ -20,34 +21,37 @@ import java.util.Objects;
* @Version 1.0
* @Date 2024/3/31 10:00
*/
@Component
@Slf4j
public class FiledFullHandler implements MetaObjectHandler {
@Override
@Component // 声明该类为Spring组件以便Spring容器可以管理它。
@Slf4j // 使用Lombok的日志注解来自动注入日志对象。
public class FiledFullHandler implements MetaObjectHandler { // 定义一个类实现MyBatis Plus的MetaObjectHandler接口。
@Override // 重写insertFill方法用于在插入操作时自动填充字段。
public void insertFill(MetaObject metaObject) {
//没有创建人id就给他自动填充 放属性名而不是字段名
// 获取原始对象的类类型。
Class<?> clazz = metaObject.getOriginalObject().getClass();
// 获取该类声明的所有字段。
Field[] fields = clazz.getDeclaredFields();
// 使用Java 8的Stream API遍历字段数组。
Arrays.stream(fields).forEach(field -> {
//填充创建人
// 填充创建人ID
if ("userId".equals(field.getName()) && (Objects.isNull(getFieldValByName("userId", metaObject)))) {
log.info("user_id字段满足公共字段自动填充规则已填充");
this.strictInsertFill(metaObject, "userId", Integer.class, SecurityUtil.getUserId());
// 如果字段名为userId且当前值为null则进行填充。
log.info("user_id字段满足公共字段自动填充规则已填充"); // 记录日志。
// 调用strictInsertFill方法填充userId字段值为SecurityUtil.getUserId()的返回值。
this.strictInsertFill(metaObject, "userId", Integer.class, SecurityUtil.getUserId());
}
//填充创建时间
// 填充创建时间
if ("createTime".equals(field.getName()) && (Objects.isNull(getFieldValByName("createTime", metaObject)))) {
log.info("create_time字段满足公共字段自动填充规则已填充");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, DateTimeUtil.getDateTime());
// 如果字段名为createTime且当前值为null则进行填充。
log.info("create_time字段满足公共字段自动填充规则已填充"); // 记录日志。
// 调用strictInsertFill方法填充createTime字段值为DateTimeUtil.getDateTime()的返回值。
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, DateTimeUtil.getDateTime());
}
});
}
@Override
@Override // 重写updateFill方法但在此实现中为空表示在更新操作时不进行自动填充。
public void updateFill(MetaObject metaObject) {
// 此方法体为空,表示不实现更新时的自动填充逻辑。
}
}
}

@ -1,167 +1,115 @@
package cn.org.alan.exam.common.handler;
/**
* @Author WeiJin
* @Version 1.0
* @Date 2024/3/29 16:10
*/
import cn.org.alan.exam.common.exception.AppException;
import cn.org.alan.exam.common.result.Result;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLIntegrityConstraintViolationException;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(AppException.class)
package cn.org.alan.exam.common.handler; // 声明类所在的包路径。
// 导入所需的类和接口
import cn.org.alan.exam.common.exception.AppException; // 导入自定义的应用异常类。
import cn.org.alan.exam.common.result.Result; // 导入自定义的响应结果类。
import jakarta.validation.ConstraintViolationException; // 导入Jakarta Bean Validation的约束违反异常类。
import lombok.extern.slf4j.Slf4j; // 导入Lombok的日志注解。
import org.springframework.dao.DuplicateKeyException; // 导入Spring的数据库主键冲突异常类。
import org.springframework.http.converter.HttpMessageNotReadableException; // 导入Spring的HTTP消息无法解析异常类。
import org.springframework.security.access.AccessDeniedException; // 导入Spring Security的访问拒绝异常类。
import org.springframework.web.bind.MethodArgumentNotValidException; // 导入Spring MVC的方法参数校验异常类。
import org.springframework.web.bind.MissingServletRequestParameterException; // 导入Spring MVC的请求参数缺失异常类。
import org.springframework.web.bind.annotation.ExceptionHandler; // 导入Spring MVC的异常处理方法注解。
import org.springframework.web.bind.annotation.RestControllerAdvice; // 导入Spring MVC的控制器增强注解。
import org.springframework.web.multipart.MaxUploadSizeExceededException; // 导入Spring MVC的文件上传大小超限异常类。
import org.springframework.web.multipart.MultipartException; // 导入Spring MVC的文件上传异常基类虽然在此代码中未直接使用
import org.springframework.web.multipart.support.MissingServletRequestPartException; // 导入Spring MVC的文件部分缺失异常类。
import java.lang.reflect.InvocationTargetException; // 导入Java反射的调用目标异常类虽然在此代码中未使用
import java.sql.SQLIntegrityConstraintViolationException; // 导入Java SQL的异常类表示SQL完整性约束违反。
@RestControllerAdvice // 声明该类为Spring MVC的控制器增强类用于全局异常处理。
@Slf4j // 使用Lombok的日志注解来自动注入日志对象。
public class GlobalExceptionHandler { // 定义一个全局异常处理器类。
// 处理自定义异常的方法
@ExceptionHandler(AppException.class) // 指定该方法处理AppException类型的异常。
public Result<String> handleAppException(AppException e) {
log.error(e.getMessage(), e.getClass());
return Result.failed(e.getLocalizedMessage());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
return Result.failed(e.getLocalizedMessage()); // 返回自定义的响应结果,表示失败,并携带异常消息。
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
// 处理方法参数校验异常的方法
@ExceptionHandler(MethodArgumentNotValidException.class) // 指定该方法处理MethodArgumentNotValidException类型的异常。
public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 获取校验失败的第一个错误信息,并返回自定义的响应结果。
String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return Result.failed(message);
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
// 处理SQL完整性约束违反异常的方法
@ExceptionHandler(SQLIntegrityConstraintViolationException.class) // 指定该方法处理SQLIntegrityConstraintViolationException类型的异常。
public Result<String> handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 返回自定义的响应结果,表示失败,并携带固定的错误信息“重复”。
return Result.failed("重复");
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
// 处理HTTP消息无法解析异常的方法
@ExceptionHandler(HttpMessageNotReadableException.class) // 指定该方法处理HttpMessageNotReadableException类型的异常。
public Result<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 返回自定义的响应结果,表示失败,并携带固定的错误信息“请求参数无法解析”。
return Result.failed("请求参数无法解析");
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
// 处理请求参数缺失异常的方法
@ExceptionHandler(MissingServletRequestParameterException.class) // 指定该方法处理MissingServletRequestParameterException类型的异常。
public Result<String> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 返回自定义的响应结果,表示失败,并携带缺失的参数名作为错误信息。
return Result.failed(e.getParameterName() + "为必填项");
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(DuplicateKeyException.class)
// 处理主键冲突异常的方法
@ExceptionHandler(DuplicateKeyException.class) // 指定该方法处理DuplicateKeyException类型的异常。
public Result<String> handleDuplicateKeyException(DuplicateKeyException e) {
// 从异常消息中提取主键名,并记录异常日志。
String name = e.getMessage().split(":")[2].split(" ")[3];
log.error(e.getMessage(), e.getClass());
// 返回自定义的响应结果,表示失败,并携带主键冲突的错误信息。
return Result.failed("主键冲突" + name + "已存在");
}
/**
* 访
*
* @param e
* @return
*/
@ExceptionHandler(AccessDeniedException.class)
// 处理无权限访问异常的方法
@ExceptionHandler(AccessDeniedException.class) // 指定该方法处理AccessDeniedException类型的异常。
public Result<String> handleAccessDeniedException(AccessDeniedException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 返回自定义的响应结果,表示失败,并携带固定的错误信息“你没有该资源的访问权限”。
return Result.failed("你没有该资源的访问权限");
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
// 处理文件上传大小超限异常的方法
@ExceptionHandler(MaxUploadSizeExceededException.class) // 指定该方法处理MaxUploadSizeExceededException类型的异常。
public Result<String> handlerMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 返回自定义的响应结果表示失败并携带固定的错误信息“文件太大最大上传5MB”。
return Result.failed("文件太大最大上传5MB");
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(MissingServletRequestPartException.class)
// 处理文件部分缺失异常的方法
@ExceptionHandler(MissingServletRequestPartException.class) // 指定该方法处理MissingServletRequestPartException类型的异常。
public Result<String> handlerMissingServletRequestPartException(MissingServletRequestPartException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 返回自定义的响应结果,表示失败,并携带固定的错误信息“没有获取到文件”。
return Result.failed("没有获取到文件");
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
// 处理约束违反异常的方法
@ExceptionHandler(ConstraintViolationException.class) // 指定该方法处理ConstraintViolationException类型的异常。
public Result<String> handleConstraintViolationException(ConstraintViolationException e) {
log.error(e.getMessage(), e.getClass());
log.error(e.getMessage(), e.getClass()); // 记录异常日志。
// 返回自定义的响应结果,表示失败,并携带异常的完整消息。
return Result.failed(e.getMessage());
}
/**
*
*
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
// 处理其他未捕获异常的方法
@ExceptionHandler(Exception.class) // 指定该方法处理所有其他类型的异常。
public Result<String> handleException(Exception e) {
log.error(e.getMessage(), e.getClass(), e.getCause());
log.error(e.getMessage(), e.getClass(), e.getCause()); // 记录异常日志,包括异常原因。
// 返回自定义的响应结果,表示失败,并携带固定的错误信息“未知异常”。
return Result.failed("未知异常");
}
}
}

@ -1,29 +1,35 @@
package cn.org.alan.exam.common.result;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
package cn.org.alan.exam.common.result; // 指定当前类所在的包路径
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
// 导入所需的Spring框架和其他Java标准库类
import org.springframework.core.MethodParameter; // 提供对方法参数(如方法中的输入参数)的反射访问
import org.springframework.http.HttpInputMessage; // 表示HTTP请求体的输入消息
import org.springframework.http.HttpStatus; // HTTP状态码枚举
import org.springframework.http.converter.HttpMessageConverter; // HTTP消息转换器接口用于在HTTP请求和响应之间进行数据转换
import org.springframework.util.StreamUtils; // 提供对输入流和输出流操作的实用方法
import org.springframework.web.bind.annotation.ControllerAdvice; // 控制器增强器,用于全局配置控制器行为
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter; // 请求体增强适配器,用于在请求体绑定到控制器方法参数之前和之后进行增强处理
import java.io.ByteArrayInputStream; // 字节数组输入流
import java.io.IOException; // 输入输出异常
import java.io.InputStream; // 输入流接口
import java.lang.reflect.Type; // Java类型信息接口
/**
* @Author Alan
* @Version
* @Date 2024/6/8 11:47 AM
* @Author Alan // 类的作者
* @Version // 版本信息(此处未指定)
* @Date 2024/6/8 11:47 AM // 类的创建日期
*/
// 使用@ControllerAdvice注解标记此类为控制器增强器
@ControllerAdvice
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {
// 重写supports方法用于判断当前增强器是否适用于指定的方法参数、目标类型和转换器类型
// 此处返回false表示默认不应用此增强器实际使用中需要根据需求判断是否支持
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return false;
return false; // 暂时不支持任何情况,需要根据具体需求进行实现
}
}
//
// @Override
// public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

@ -1,53 +1,63 @@
package cn.org.alan.exam.common.result;
package cn.org.alan.exam.common.result; // 定义当前类所在的包路径
// 导入Lombok库的@Data注解用于自动生成getter、setter、toString等方法
import lombok.Data;
// 导入Java标准库中的Serializable接口用于序列化对象
import java.io.Serializable;
/**
*
* // 类注释,说明这是一个用于统一响应的结构体类
*
* @author haoxr
* @since 2022/1/30
* @author haoxr // 类的作者
* @since 2022/1/30 // 类的创建日期或版本
**/
@Data
public class Result<T> implements Serializable {
@Data // 使用Lombok的@Data注解自动生成getter、setter等方法
public class Result<T> implements Serializable { // 定义一个泛型类Result实现Serializable接口
// 定义一个私有成员变量code用于存储响应码
private Integer code;
// 定义一个泛型成员变量data用于存储响应数据
private T data;
// 定义一个私有成员变量msg用于存储响应消息
private String msg;
// 定义一个静态泛型方法success无参数版本默认返回成功响应无数据
public static <T> Result<T> success() {
return success(null);
return success(null); // 调用另一个success方法传入null作为数据
}
// 定义一个静态泛型方法success带有msg和data参数用于返回成功响应
public static <T> Result<T> success(String msg,T data) {
Result<T> result = new Result<>();
result.setCode(1);
result.setMsg(msg);
result.setData(data);
return result;
Result<T> result = new Result<>(); // 创建Result对象
result.setCode(1); // 设置响应码为1通常表示成功
result.setMsg(msg); // 设置响应消息
result.setData(data); // 设置响应数据
return result; // 返回Result对象
}
// 定义一个静态泛型方法success只带有msg参数用于返回成功响应无数据
public static <T> Result<T> success(String msg) {
Result<T> result = new Result<>();
result.setCode(1);
result.setMsg(msg);
result.setData(null);
return result;
Result<T> result = new Result<>(); // 创建Result对象
result.setCode(1); // 设置响应码为1通常表示成功
result.setMsg(msg); // 设置响应消息
result.setData(null); // 设置响应数据为null
return result; // 返回Result对象
}
// 定义一个静态泛型方法failed带有msg参数用于返回失败响应
public static <T> Result<T> failed(String msg) {
return result(0,msg , null);
return result(0,msg , null); // 调用私有方法result传入失败响应码、消息和null作为数据
}
// 定义一个私有静态泛型方法result带有code、msg和data参数用于创建并返回Result对象
private static <T> Result<T> result(Integer code, String msg, T data) {
Result<T> result = new Result<>();
result.setCode(code);
result.setData(data);
result.setMsg(msg);
return result;
Result<T> result = new Result<>(); // 创建Result对象
result.setCode(code); // 设置响应码
result.setData(data); // 设置响应数据
result.setMsg(msg); // 设置响应消息
return result; // 返回Result对象
}
}
}

@ -1,12 +1,22 @@
package cn.org.alan.exam.config;
package cn.org.alan.exam.config; // 定义当前类所在的包路径
// 导入Spring Boot的FilterRegistrationBean类用于注册过滤器
import org.springframework.boot.web.servlet.FilterRegistrationBean;
// 导入Spring的@Bean和@Configuration注解用于定义配置类和Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 导入Spring Web的CorsConfiguration类用于配置CORS跨源资源共享
import org.springframework.web.cors.CorsConfiguration;
// 导入Spring Web的UrlBasedCorsConfigurationSource类用于基于URL路径的CORS配置源
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
// 导入Spring Web的CorsFilter类用于实现CORS过滤功能
import org.springframework.web.filter.CorsFilter;
// 导入Java的Collections工具类用于操作集合
import java.util.Collections;
/**
@ -15,28 +25,33 @@ import java.util.Collections;
* @author Alan
* @since 2023/4/17
*/
@Configuration
@Configuration // 标识这是一个Spring配置类
public class CorsConfig {
@Bean
@Bean // 定义一个Bean方法用于创建并返回FilterRegistrationBean对象
public FilterRegistrationBean filterRegistrationBean() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
CorsConfiguration corsConfiguration = new CorsConfiguration(); // 创建CorsConfiguration对象用于配置CORS
//1.允许任何来源
corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*")); // 设置允许的来源模式为任意模式
//2.允许任何请求头
corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
corsConfiguration.addAllowedHeader(CorsConfiguration.ALL); // 允许所有的请求头
//3.允许任何方法
corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
corsConfiguration.addAllowedMethod(CorsConfiguration.ALL); // 允许所有的HTTP方法
//4.允许凭证
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setAllowCredentials(true); // 允许客户端发送凭证如Cookies
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 创建UrlBasedCorsConfigurationSource对象用于基于URL路径的CORS配置
source.registerCorsConfiguration("/**", corsConfiguration); // 对所有路径("/**"应用上述CORS配置
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
CorsFilter corsFilter = new CorsFilter(source);
CorsFilter corsFilter = new CorsFilter(source); // 创建CorsFilter对象并传入配置源
FilterRegistrationBean<CorsFilter> filterRegistrationBean=new FilterRegistrationBean<>(corsFilter);
filterRegistrationBean.setOrder(-101); // 小于 SpringSecurity Filter的 Order(-100) 即可
FilterRegistrationBean<CorsFilter> filterRegistrationBean = new FilterRegistrationBean<>(corsFilter); // 创建FilterRegistrationBean对象并传入CorsFilter
filterRegistrationBean.setOrder(-101); // 设置过滤器的顺序确保它在Spring Security过滤器之前执行Spring Security的默认顺序是-100
return filterRegistrationBean;
return filterRegistrationBean; // 返回配置好的FilterRegistrationBean对象
}
}

@ -1,38 +1,56 @@
package cn.org.alan.exam.config;
package cn.org.alan.exam.config; // 定义当前类所在的包路径
// 导入自定义的FiledFullHandler类可能用于处理MyBatis Plus中的字段自动填充
import cn.org.alan.exam.common.handler.FiledFullHandler;
// 导入MyBatis Plus的DbType枚举用于指定数据库类型
import com.baomidou.mybatisplus.annotation.DbType;
// 导入MyBatis Plus的GlobalConfig类用于全局配置
import com.baomidou.mybatisplus.core.config.GlobalConfig;
// 导入MyBatis Plus的MybatisPlusInterceptor类用于配置拦截器
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
// 导入MyBatis Plus的分页插件内部拦截器PaginationInnerInterceptor
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
// 导入Jakarta EE的@Resource注解用于注入依赖
import jakarta.annotation.Resource;
// 导入MyBatis的@MapperScan注解用于扫描Mapper接口所在的包
import org.mybatis.spring.annotation.MapperScan;
// 导入Spring的@Bean和@Configuration注解用于定义配置类和Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis Plus
*
* @Author Alan
* @Version
* @Version
* @Date 2024/3/28 3:57 PM
*/
@Configuration
@MapperScan("cn.org.alan.exam.mapper")
@Configuration // 标识这是一个Spring配置类
@MapperScan("cn.org.alan.exam.mapper") // 指定Mapper接口所在的包路径
public class MybatisPlusConfig {
@Resource
@Resource // 注入自定义的FieldFullHandler用于处理字段自动填充
private FiledFullHandler filedFullHandler;
@Bean
@Bean // 定义一个Bean方法用于创建并返回MybatisPlusInterceptor对象
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
//添加元数据对象处理器
return interceptor;
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 创建MybatisPlusInterceptor对象
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 添加分页插件内部拦截器并指定数据库类型为MySQL
//添加元数据对象处理器此注释实际上是对下一行代码的说明但下一行代码是return语句因此注释放在了这里
return interceptor; // 返回配置好的MybatisPlusInterceptor对象
}
@Bean
@Bean // 定义一个Bean方法用于创建并返回GlobalConfig对象
public GlobalConfig globalConfig() {
GlobalConfig config = new GlobalConfig();
config.setMetaObjectHandler(filedFullHandler);
return config;
GlobalConfig config = new GlobalConfig(); // 创建GlobalConfig对象
config.setMetaObjectHandler(filedFullHandler); // 设置元数据对象处理器,用于处理字段自动填充
return config; // 返回配置好的GlobalConfig对象
}
}
}

@ -1,37 +1,49 @@
package cn.org.alan.exam.config;
package cn.org.alan.exam.config; // 声明当前类所在的包路径为cn.org.alan.exam.config
// 导入Spring的@Bean和@Configuration注解用于定义配置类和Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 导入Spring Data Redis的RedisConnectionFactory接口用于提供Redis连接
import org.springframework.data.redis.connection.RedisConnectionFactory;
// 导入Spring Data Redis的RedisTemplate类用于操作Redis数据
import org.springframework.data.redis.core.RedisTemplate;
// 导入Spring Data Redis的序列化器类用于对象与Redis存储之间的转换
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis
*
* @Author Alan
* @Version
* @Version
* @Date 2024/6/9 11:07 PM
*/
@Configuration
@Configuration // 标识这是一个Spring配置类
public class RedisConfig {
@Bean
@Bean // 定义一个Bean方法用于创建并返回RedisTemplate对象
public RedisTemplate redisTemplateInit(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); // 创建RedisTemplate对象键类型为String值类型为Object
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置序列化Key的实例化对象
redisTemplate.setConnectionFactory(redisConnectionFactory); // 设置Redis连接工厂用于提供Redis连接
// 设置序列化Key的实例化对象使用StringRedisSerializer来序列化键确保键以字符串形式存储
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置序列化Value的实例化对象
// 设置序列化Value的实例化对象使用GenericJackson2JsonRedisSerializer来序列化值支持将对象序列化为JSON字符串存储
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
/**
*
* Hash
* Hash
* Hash
*/
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // 设置Hash键的序列化器为StringRedisSerializer
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); // 设置Hash值的序列化器为GenericJackson2JsonRedisSerializer
return redisTemplate; // 返回配置好的RedisTemplate对象
}
}
}

@ -1,25 +1,30 @@
package cn.org.alan.exam.config;
import cn.org.alan.exam.filter.VerifyTokenFilter;
import cn.org.alan.exam.common.result.Result;
import cn.org.alan.exam.util.ResponseUtil;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
package cn.org.alan.exam.config; // 声明当前类所在的包路径
// 导入项目中自定义的类
import cn.org.alan.exam.filter.VerifyTokenFilter; // 导入自定义的Token验证过滤器
import cn.org.alan.exam.common.result.Result; // 导入自定义的响应结果类
import cn.org.alan.exam.util.ResponseUtil; // 导入自定义的响应工具类
// 导入Jakarta EE的注解
import jakarta.annotation.Resource; // 用于注入依赖
// 导入Lombok库中的注解
import lombok.RequiredArgsConstructor; // 用于生成构造器
// 导入Spring框架的注解和类
import org.springframework.context.annotation.Bean; // 用于定义Bean
import org.springframework.context.annotation.Configuration; // 用于标识配置类
import org.springframework.security.authentication.AuthenticationManager; // 用于认证管理
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; // 用于配置认证
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; // 启用方法级别的安全配置
import org.springframework.security.config.annotation.web.builders.HttpSecurity; // 用于配置HTTP安全
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; // 启用Web安全配置
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; // 用于自定义Web安全配置
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; // 抽象HTTP配置器
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; // BCrypt密码编码器
import org.springframework.security.crypto.password.PasswordEncoder; // 密码编码器接口
import org.springframework.security.web.SecurityFilterChain; // 安全过滤器链
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; // 用户名密码认证过滤器
/**
* Spring Security
@ -27,51 +32,54 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
* @author Alan
* @since 2024/4/17
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
@Configuration // 标识这是一个Spring配置类
@EnableWebSecurity // 启用Spring Security的Web安全配置
@EnableMethodSecurity // 启用方法级别的安全配置
@RequiredArgsConstructor // 使用Lombok生成构造器自动注入final或@NonNull标注的字段
public class SecurityConfig {
@Resource
@Resource // 注入ResponseUtil依赖用于自定义响应处理
private ResponseUtil responseUtil;
@Resource
@Resource // 注入VerifyTokenFilter依赖用于自定义Token验证
private VerifyTokenFilter verifyTokenFilter;
@Bean
@Bean // 定义一个Bean方法用于创建并返回SecurityFilterChain对象
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(request -> {
//放开认证
http.authorizeHttpRequests(request -> { // 配置请求授权规则
// 放开对/api/auths/**路径的访问,无需认证
request.requestMatchers("/api/auths/**").permitAll();
// 配置/api/auths/logout路径需要认证才能访问
request.requestMatchers("/api/auths/logout").authenticated();
//所有请求的授权都需要认证
// 所有其他请求都需要认证才能访问
request.anyRequest().authenticated();
});
//关闭表单功能,使用自定义登录和退出
// 关闭默认的表单登录功能,使用自定义登录和退出逻辑
http.formLogin(AbstractHttpConfigurer::disable);
// 配置拒绝访问处理器
// 配置拒绝访问处理器,当访问被拒绝时执行自定义的响应处理
http.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler((request, response, accessDeniedException) ->
responseUtil.response(response, Result.failed("你没有该资源的访问权限"))));
// 配置请求拦截前处理器验证token
responseUtil.response(response, Result.failed("你没有该资源的访问权限")))); // 使用ResponseUtil工具类返回自定义的响应结果
// 在UsernamePasswordAuthenticationFilter之前添加自定义的Token验证过滤器
http.addFilterBefore(verifyTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 放开跨域请求
// 关闭CSRF保护允许跨域请求
http.csrf(AbstractHttpConfigurer::disable);
return http.build();
return http.build(); // 构建并返回SecurityFilterChain对象
}
/**
*
*/
@Bean
@Bean // 定义一个Bean方法用于创建并返回WebSecurityCustomizer对象
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
.requestMatchers(
// "/api/auths/**",
return (web) -> web.ignoring() // 配置不需要经过安全过滤器的路径
.requestMatchers( // 指定放行的路径
"/webjars/**",
"/doc.html",
"/swagger-resources/**",
@ -86,9 +94,9 @@ public class SecurityConfig {
/**
*
*/
@Bean
@Bean // 定义一个Bean方法用于创建并返回PasswordEncoder对象
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
return new BCryptPasswordEncoder(); // 使用BCrypt算法进行密码编码
}
/**
@ -96,8 +104,8 @@ public class SecurityConfig {
*
* @param authenticationConfiguration
*/
@Bean
@Bean // 定义一个Bean方法用于创建并返回AuthenticationManager对象
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
return authenticationConfiguration.getAuthenticationManager(); // 从认证配置中获取AuthenticationManager对象
}
}
}

@ -1,67 +1,79 @@
package cn.org.alan.exam.config;
// 定义包路径,便于组织代码和类
// import 语句被注释掉了,这些原本是用来导入其他类或接口的
// import com.zaxxer.hikari.HikariDataSource;
// 导入HikariDataSource类它是一个高性能的JDBC连接池
// import org.apache.shardingsphere.api.config.masterslave.LoadBalanceStrategyConfiguration;
// 导入负载均衡策略配置类
// import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration;
// 导入主从规则配置类
// import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
// 导入分片规则配置类
// import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
// 导入ShardingDataSourceFactory类用于创建分片数据源
// import org.springframework.context.annotation.Bean;
// 导入Bean注解用于定义Spring管理的bean
// import org.springframework.context.annotation.Configuration;
// 导入Configuration注解表示这是一个配置类
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource; // 导入DataSource接口它是JDBC的API用于表示数据库连接池
import java.sql.SQLException; // 导入SQLException类用于处理SQL异常
import java.util.HashMap; // 导入HashMap类用于存储键值对数据
import java.util.List; // 导入List接口用于表示一个有序的集合
import java.util.Map; // 导入Map接口用于表示键值对的集合
import java.util.Properties; // 导入Properties类用于表示一组持久的属性
/**
* @Author Alan
* @Version
* @Version
* @Date 2024/6/11 4:21 PM
*/
// @Configuration
// 使用@Configuration注解标记这个类为配置类Spring容器会基于此类生成Bean定义和服务请求
public class ShardingsphereConfig {
// 这个方法被注释掉了它原本用于创建一个HikariDataSource实例并配置数据库连接信息
// public DataSource hikariDataSource (String username, String pwd, String className, String url){
// HikariDataSource hikariDataSource1=new HikariDataSource();
// hikariDataSource1.setUsername(username);
// hikariDataSource1.setPassword(pwd);
// hikariDataSource1.setDriverClassName(className);
// hikariDataSource1.setJdbcUrl(url);
// return hikariDataSource1;
// hikariDataSource1.setUsername(username); // 设置数据库用户名
// hikariDataSource1.setPassword(pwd); // 设置数据库密码
// hikariDataSource1.setDriverClassName(className); // 设置JDBC驱动类名
// hikariDataSource1.setJdbcUrl(url); // 设置数据库连接URL
// return hikariDataSource1; // 返回配置好的数据源
// }
//
// @Bean
// 这个方法被注释掉了它原本用于创建一个ShardingRuleConfiguration实例并配置分片规则
// public ShardingRuleConfiguration shardingRuleConfiguration(){
// ShardingRuleConfiguration config=new ShardingRuleConfiguration();
// LoadBalanceStrategyConfiguration loadConfiguration=new LoadBalanceStrategyConfiguration("round_robin");
// ShardingRuleConfiguration config=new ShardingRuleConfiguration(); // 创建分片规则配置实例
// LoadBalanceStrategyConfiguration loadConfiguration=new LoadBalanceStrategyConfiguration("round_robin"); // 创建负载均衡策略配置实例,使用轮询算法
// MasterSlaveRuleConfiguration configuration=new MasterSlaveRuleConfiguration("dataSource",
// "master", List.of("slave"),
// loadConfiguration
// "master", List.of("slave"), // 配置主从数据源和从数据源列表
// loadConfiguration // 设置负载均衡策略
// );
// config.setMasterSlaveRuleConfigs(List.of(configuration));
// return config;
// config.setMasterSlaveRuleConfigs(List.of(configuration)); // 将主从规则配置添加到分片规则配置中
// return config; // 返回配置好的分片规则
// }
//
//
// @Bean
// 这个方法被注释掉了,它原本用于创建一个分片数据源
// public DataSource shardingDataSource(ShardingRuleConfiguration configuration){
// DataSource masterDataSource = hikariDataSource("root", "123456", "com.mysql.cj.jdbc.Driver", "jdbc:mysql://47.109.94.143:9506/db_exam");
// DataSource slaveDataSource = hikariDataSource("root", "123456", "com.mysql.cj.jdbc.Driver", "jdbc:mysql://47.109.94.143:9507/db_exam");
// DataSource masterDataSource = hikariDataSource("root", "123456", "com.mysql.cj.jdbc.Driver", "jdbc:mysql://47.109.94.143:9506/db_exam"); // 创建主数据源
// DataSource slaveDataSource = hikariDataSource("root", "123456", "com.mysql.cj.jdbc.Driver", "jdbc:mysql://47.109.94.143:9507/db_exam"); // 创建从数据源
//
// Map<String, DataSource> dataSourceMap=new HashMap<>();
// dataSourceMap.put("master",masterDataSource);
// dataSourceMap.put("slave",slaveDataSource);
// Map<String, DataSource> dataSourceMap=new HashMap<>(); // 创建一个数据源映射,用于存储主从数据源
// dataSourceMap.put("master",masterDataSource); // 将主数据源添加到映射中
// dataSourceMap.put("slave",slaveDataSource); // 将从数据源添加到映射中
//
// DataSource shardingDataSource= null;
// Properties props=new Properties();
// props.put("sql.show",true);
// DataSource shardingDataSource= null; // 声明分片数据源变量
// Properties props=new Properties(); // 创建属性对象用于配置Sharding-JDBC的属性
// props.put("sql.show",true); // 设置属性用于开启SQL显示功能
// try {
// shardingDataSource= ShardingDataSourceFactory.createDataSource(dataSourceMap,configuration,props);
// shardingDataSource= ShardingDataSourceFactory.createDataSource(dataSourceMap,configuration,props); // 创建分片数据源
// } catch (SQLException e) {
// throw new RuntimeException(e);
// throw new RuntimeException(e); // 如果创建数据源时发生异常,则抛出运行时异常
// }
// return shardingDataSource;
// return shardingDataSource; // 返回配置好的分片数据源
// }
}
}

@ -1,19 +1,21 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于对类进行组织和分类管理,表明此控制器类属于该指定包
import cn.org.alan.exam.common.group.AnswerGroup;
// 引入AnswerGroup类可能用于对答案相关操作的分组验证等功能从名字推测
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于封装操作的结果信息比如包含操作成功与否以及对应的数据等情况方便统一返回格式
import cn.org.alan.exam.model.form.answer.CorrectAnswerFrom;
import cn.org.alan.exam.model.vo.answer.AnswerExamVO;
import cn.org.alan.exam.model.vo.answer.UncorrectedUserVO;
import cn.org.alan.exam.model.vo.answer.UserAnswerDetailVO;
import cn.org.alan.exam.service.IManualScoreService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
// 引入CorrectAnswerFrom类可能是代表正确答案相关的表单数据模型类用于接收前端提交的正确答案相关的数据结构
import cn.org.alan.exam.model.vo.answer.AnswerExamVO; // 引入AnswerExamVO类大概率是视图对象Value ObjectVO用于向客户端传递特定的与考试相关的数据展示格式
import cn.org.alan.exam.model.vo.answer.UncorrectedUserVO; // 引入UncorrectedUserVO类同样是视图对象类可能用于展示待批阅用户相关的数据展示形式
import cn.org.alan.exam.model.vo.answer.UserAnswerDetailVO; // 引入UserAnswerDetailVO类用于向客户端展示答卷详细信息的视图对象
import cn.org.alan.exam.service.IManualScoreService; // 引入IManualScoreService类这应该是一个服务接口用于定义和实现与手动评分相关的业务逻辑
import com.baomidou.mybatisplus.core.metadata.IPage; // 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型用于处理分页查询相关业务逻辑
import jakarta.annotation.Resource; // 用于进行资源注入在这里可将实现了IManualScoreService接口的具体实例注入到当前控制器类中方便调用对应的服务方法
import org.springframework.security.access.prepost.PreAuthorize; // 用于在方法级别进行权限控制,根据配置的权限表达式来判断当前用户是否有权限访问对应的方法,保障系统安全性
import org.springframework.validation.annotation.Validated; // 结合具体的分组类如AnswerGroup.CorrectGroup.class可以对传入方法的参数进行基于分组的验证确保数据的合法性和有效性
import org.springframework.web.bind.annotation.*; // 通配符导入包含了很多Spring Web相关的注解用于定义控制器类中的请求处理方法以及映射对应的HTTP请求路径和请求方式等
import java.util.List; // 引入Java标准库中的List接口用于表示列表类型在这里用于处理返回多个数据元素的情况
/**
*
@ -22,44 +24,44 @@ import java.util.List;
* @Version
* @Date 2024/3/25 11:20 AM
*/
@RestController
@RequestMapping("/api/answers")
public class AnswerController {
@RestController // 这是Spring框架提供的复合注解结合了@Controller和@ResponseBody的功能意味着这个类是一个Spring MVC中的控制器类并且类中的方法返回值会直接以JSON等格式响应给客户端默认情况下无需额外配置视图解析等相关内容
@RequestMapping("/api/answers") // 用于给整个控制器类的所有请求处理方法定义一个基础的请求路径前缀表明该控制器类中所有处理请求的方法的URL路径都将以/api/answers开头方便对一组相关的API进行统一的路径管理
public class AnswerController { // 定义一个名为AnswerController的公共类作为控制器来处理相关业务逻辑请求
@Resource
private IManualScoreService manualScoreService;
@Resource // 使用@Resource注解将一个实现了IManualScoreService接口的实例注入到当前的AnswerController类中使得后续方法可以调用该服务接口所定义的业务逻辑方法
private IManualScoreService manualScoreService; // 声明一个私有变量manualScoreService类型为IManualScoreService用于后续调用相关服务方法实现具体业务逻辑
/**
*
* @return
*/
@GetMapping("/detail")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<List<UserAnswerDetailVO>> getDetail(@RequestParam Integer userId,
@RequestParam Integer examId) {
return manualScoreService.getDetail(userId, examId);
@GetMapping("/detail") // 表示这个方法用于处理HTTP GET请求对应的请求路径是在类级别定义的基础路径/api/answers基础上追加/detail即完整请求路径为/api/answers/detail
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 进行权限控制只有拥有role_teacher或者role_admin权限的用户才能访问这个方法确保只有教师或管理员角色的用户可以查询答卷详情增强系统的安全性
public Result<List<UserAnswerDetailVO>> getDetail(@RequestParam Integer userId, // 表示从请求中获取名为userId的参数参数类型为整数用于指定用户ID以便准确查询对应的用户答卷详情
@RequestParam Integer examId) { // 表示从请求中获取名为examId的参数参数类型为整数用于指定考试ID配合userId一起准确查询对应的用户答卷详情
return manualScoreService.getDetail(userId, examId); // 方法体内部直接调用注入的manualScoreService接口的getDetail方法并将获取到的请求参数传递进去最终将服务层返回的结果直接返回给客户端由服务层去具体实现根据传入的用户ID和考试ID查询答卷详细信息的业务逻辑
}
/**
*
* @return
*/
@PutMapping("/correct")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<String> Correct(@RequestBody @Validated(AnswerGroup.CorrectGroup.class) List<CorrectAnswerFrom> correctAnswerFroms) {
return manualScoreService.correct(correctAnswerFroms);
@PutMapping("/correct") // 指定这个方法处理HTTP PUT请求请求路径是在类基础路径上追加/correct也就是/api/answers/correctPUT请求常用于更新资源等操作这里用于批改试卷这个更新数据的场景比较合适
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 进行权限控制,限定只有教师或管理员角色能执行此操作
public Result<String> Correct(@RequestBody @Validated(AnswerGroup.CorrectGroup.class) List<CorrectAnswerFrom> correctAnswerFroms) { // @RequestBody表示方法的参数correctAnswerFroms是从请求的正文中获取数据即将前端发送过来的JSON等格式的数据转换为对应的Java对象列表同时通过@Validated结合指定分组类对传入的参数列表进行数据验证确保数据合法性只有验证通过的数据才进入方法内部进行后续处理
return manualScoreService.correct(correctAnswerFroms); // 方法返回Result<String>类型调用manualScoreService的correct方法由服务层去具体实现批改试卷的业务逻辑并将结果封装到Result对象中返回给控制器再由控制器返回给客户端这里的String可能用于表示批改试卷操作的一些相关反馈信息比如批改是否成功等简单提示
}
/**
*
* @return
*/
@GetMapping("/exam/page")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<IPage<AnswerExamVO>> examPage(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "examName", required = false) String examName) {
return manualScoreService.examPage(pageNum, pageSize, examName);
@GetMapping("/exam/page") // 表明此方法处理HTTP GET请求对应的请求路径是/api/answers/exam/page用于分页查找待阅卷考试相关信息
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 权限控制,保证只有教师或管理员角色能访问该方法
public Result<IPage<AnswerExamVO>> examPage(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, // 获取请求中的pageNum参数可选参数若请求中未提供则默认值为1用于指定分页查询的页码
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, // 获取pageSize参数同样可选默认值为10代表每页显示的记录数量
@RequestParam(value = "examName", required = false) String examName) { // 获取examName参数可选可能用于根据考试名称进行模糊查询等筛选操作以便更精准地查找待阅卷的考试信息
return manualScoreService.examPage(pageNum, pageSize, examName); // 方法返回Result<IPage<AnswerExamVO>>类型由服务层去实现具体的分页查询待阅卷考试信息的业务逻辑并返回相应结果给客户端IPage<AnswerExamVO>是MyBatis Plus中的分页对象包含了符合条件的AnswerExamVO类型的分页数据
}
/**
@ -69,12 +71,12 @@ public class AnswerController {
* @param examId
* @return
*/
@GetMapping("/exam/stu")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<IPage<UncorrectedUserVO>> stuExamPage(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "examId") Integer examId,
@RequestParam(value = "realName", required = false) String realName) {
return manualScoreService.stuExamPage(pageNum, pageSize, examId,realName);
@GetMapping("/exam/stu") // 注解说明这个方法处理HTTP GET请求请求路径为/api/answers/exam/stu用于查询待批阅的用户相关信息
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 进行权限控制,只有教师或管理员角色的用户可访问该方法
public Result<IPage<UncorrectedUserVO>> stuExamPage(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, // 用于指定页码可选参数若未提供则默认为1
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, // 用于指定每页记录数可选参数默认为10
@RequestParam(value = "examId") Integer examId, // 是必填参数用于指定考试的ID以此来确定要查询哪一场考试下待批阅的用户
@RequestParam(value = "realName", required = false) String realName) { // 是可选参数,可能用于根据用户的真实姓名进行模糊查询等操作,更精准地查找待批阅用户
return manualScoreService.stuExamPage(pageNum, pageSize, examId,realName); // 方法返回Result<IPage<UncorrectedUserVO>>类型,由服务层去实现具体根据给定条件查询待批阅用户信息的业务逻辑,并将结果封装在合适的对象中返回给客户端
}
}
}

@ -1,95 +1,90 @@
package cn.org.alan.exam.controller;
import cn.org.alan.exam.common.group.UserGroup;
import cn.org.alan.exam.common.result.Result;
import cn.org.alan.exam.model.form.Auth.LoginForm;
import cn.org.alan.exam.model.form.UserForm;
import cn.org.alan.exam.service.IAuthService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;
package cn.org.alan.exam.controller; // 定义该类所属的包名,通过包名对类进行分类管理,让代码结构更清晰,此控制器类属于该指定的包,便于在项目中区分不同功能模块的代码
import cn.org.alan.exam.common.group.UserGroup; // 引入UserGroup类可能用于对用户相关操作按照不同场景、规则等进行分组验证例如不同的用户注册、修改信息等操作的验证分组便于灵活控制数据验证逻辑
import cn.org.alan.exam.common.result.Result; // 引入Result类通常用来统一封装业务操作的结果包含操作是否成功的标识以及可能需要返回的数据等信息方便以一致的格式返回给调用者如前端
import cn.org.alan.exam.model.form.Auth.LoginForm; // 引入LoginForm类这是一个数据模型类用于承载用户登录时从前端传递过来的相关信息比如用户名、密码等登录所需的数据结构
import cn.org.alan.exam.model.form.UserForm; // 引入UserForm类同样是数据模型类用于接收用户相关的信息在不同场景如注册、信息更新等存储用户提交的各种数据内容符合相应的业务数据格式要求
import cn.org.alan.exam.service.IAuthService; // 引入IAuthService接口定义了与权限认证相关的一系列业务方法例如登录、注销、注册等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类会依赖这个接口来调用相应功能
import jakarta.annotation.Resource; // 这是用于进行资源注入的注解在这里的作用是将实现了IAuthService接口的具体实例注入到当前的AuthController类中使得类中的方法可以方便地调用对应的服务方法实现具体业务逻辑
import jakarta.servlet.http.HttpServletRequest; // 引入HttpServletRequest类它代表了客户端发送过来的HTTP请求对象在控制器方法中可借助它获取请求中的各种信息比如请求参数、请求头信息也常用于获取session相关信息像获取sessionId等操作
import jakarta.servlet.http.HttpServletResponse; // 引入HttpServletResponse类用于在控制器方法中对HTTP响应进行处理比如设置响应的状态码、响应头例如设置内容类型等以及向客户端输出响应内容如返回图片验证码等情况
import org.springframework.http.ResponseEntity; // 引入ResponseEntity类Spring框架中用于构建更灵活、全面的HTTP响应实体可方便地设置响应的各种属性如状态码、头信息以及响应体内容等不过在此代码中暂时未看到直接使用它的地方
import org.springframework.security.access.prepost.PreAuthorize; // 用于在方法级别进行权限控制的注解,根据配置的权限表达式判断当前用户是否有相应权限来访问对应的方法,以此保证系统资源只能被授权用户访问,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated; // 结合具体的分组类像这里的UserGroup.RegisterGroup.class等可以对传入方法的参数进行分组验证确保接收到的参数数据符合特定业务规则和验证要求保证数据的合法性和准确性
import org.springframework.web.bind.annotation.*; // 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口
import java.time.Duration; // 引入Duration类用于表示时间间隔常用于处理涉及时间长度相关的业务逻辑例如设置某个操作的超时时间、计算两个时间点之间的时长等但在当前代码片段中未明确体现其使用场景
import java.time.LocalDateTime; // 引入LocalDateTime类用于表示本地的日期和时间在业务逻辑中可能会用于记录操作发生的时间、判断时间有效性等情况不过此处暂时未看到具体使用它的地方
import java.util.Map; // 引入Java标准库中的Map接口用于存储键值对形式的数据结构常用于传递多个相关参数、存储配置信息等场景在当前代码里尚未看到具体运用的地方
/**
*
*
*
* @Author WeiJin
* @Version
* @Date 2024/3/25 11:05 AM
*/
@RestController
@RequestMapping("/api/auths")
public class AuthController {
@RestController // 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/auths") // 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/auths开头便于统一管理和组织与权限认证相关的一组API接口路径
public class AuthController { // 定义一个公共的类类名为AuthController作为权限管理相关业务逻辑的处理中心对外提供各种权限认证相关的接口接收和处理对应的HTTP请求
@Resource
private IAuthService iAuthService;
@Resource // 使用@Resource注解来进行依赖注入目的是让Spring容器查找并注入一个实现了IAuthService接口的实例到当前类中这样后续的方法就能方便地调用该服务实例所提供的各种权限认证相关的业务方法
private IAuthService iAuthService; // 声明一个私有成员变量iAuthService其类型为IAuthService接口类型通过依赖注入后它将指向对应的服务实现类实例用于在控制器方法中调用具体的权限认证相关业务逻辑方法
/**
*
* @param request requestsessionId
* @param user
* @return token
* @param request requestsessionIdsessionsessionId
* @param user LoginFormLoginForm
* @return tokentokentoken
*/
@PostMapping("/login")
public Result<String> login(HttpServletRequest request,
@Validated @RequestBody LoginForm loginForm) {
return iAuthService.login(request,loginForm);
@PostMapping("/login") // 该注解表明这个方法用于处理HTTP POST请求并且其请求路径是在类级别定义的基础路径/api/auths基础上添加/login即完整的请求路径为/api/auths/login。POST请求常用于向服务器提交数据在此处符合用户提交登录信息的场景
public Result<String> login(HttpServletRequest request, // 接收HttpServletRequest类型的参数request通过它可以获取请求相关的各种详细信息比如从请求头中获取客户端相关标识信息、从请求的Cookie中获取sessionId等在登录逻辑中会利用这些信息辅助完成登录验证等操作
@Validated @RequestBody LoginForm loginForm) { // @Validated注解结合@RequestBody注解@RequestBody表示从请求的正文中获取数据并将其转换为LoginForm类型的对象而@Validated用于对这个转换后的LoginForm对象依据相关规则此处可能结合了默认验证规则或特定分组验证规则等进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的登录业务逻辑处理
return iAuthService.login(request,loginForm); // 方法体内部直接调用通过依赖注入获取的iAuthService接口实例的login方法并将接收到的request和经过验证的loginForm参数传递进去由服务层去具体实现用户登录的详细业务逻辑比如验证用户名和密码是否匹配、生成有效的登录token等最后将服务层返回的包含登录结果及token信息等的Result对象直接返回给客户端
}
/**
*
* @param request requestsession
* @return
* @param request requestsessionsession访
* @return ResultResult
*/
@DeleteMapping("/logout")
public Result<String> logout(HttpServletRequest request) {
return iAuthService.logout(request);
@DeleteMapping("/logout") // 此注解指定这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/auths基础上追加/logout也就是/api/auths/logout。DELETE请求常用于删除资源等操作这里用于表示删除用户的登录状态这一资源符合注销操作的语义
public Result<String> logout(HttpServletRequest request) { // 接收HttpServletRequest类型的参数request借助它来操作session找到与当前登录用户对应的session信息进而执行清除session内容的操作实现注销用户登录状态的功能
return iAuthService.logout(request); // 调用通过依赖注入获取的iAuthService接口实例的logout方法由服务层去具体实现注销的详细业务逻辑比如清理session中的用户相关信息、更新相关的登录状态记录等然后将处理后的结果封装到Result对象中返回给客户端告知注销操作的执行情况
}
/**
*
* @param request requestsessionId
* @param userForm
* @return
* @param request requestsessionIdsessionsessionsessionId
* @param userForm userFormUserFormUserGroup.RegisterGroup.class
* @return Result
*/
@PostMapping("/register")
public Result<String> register(HttpServletRequest request,
@RequestBody @Validated(UserGroup.RegisterGroup.class) UserForm userForm) {
return iAuthService.register(request, userForm);
@PostMapping("/register") // 表明该方法用于处理HTTP POST请求其请求路径为/api/auths/registerPOST请求常用于向服务器提交新的数据在此场景下符合用户提交注册信息的操作特点
public Result<String> register(HttpServletRequest request, // 接收HttpServletRequest类型的参数request通过它可以获取到请求相关的各种信息例如获取sessionId用于关联注册操作与特定的会话或者从请求中获取一些必要的辅助验证信息等服务层在处理注册逻辑时会利用这些信息
@RequestBody @Validated(UserGroup.RegisterGroup.class) UserForm userForm) { // @RequestBody注解表示从请求正文中获取数据并转换为UserForm类型的对象@Validated结合特定的分组类UserGroup.RegisterGroup.class对转换后的userForm对象进行数据验证确保用户提交的注册信息符合相应的业务规则要求只有验证通过的数据才会进入后续的注册业务逻辑处理流程
return iAuthService.register(request, userForm); // 调用iAuthService接口的register方法由服务层去具体实现用户注册的详细业务逻辑比如将用户注册信息保存到数据库中、为新注册的学生用户设置默认权限等最后把注册操作的结果封装到Result对象中返回给客户端告知注册是否成功以及可能的提示信息等情况
}
/**
*
* @param request requestsessionId
* @param response response
* @param request requestsessionIdsession便sessionId
* @param response responseHTTPimage/png
*/
@GetMapping("/captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
iAuthService.getCaptcha(request, response);
@GetMapping("/captcha") // 表示这个方法用于处理HTTP GET请求其请求路径是/api/auths/captchaGET请求常用于获取资源这里用于获取图片验证码这一资源的操作符合GET请求的使用场景
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) { // 接收HttpServletRequest类型的参数request用于获取请求相关信息特别是获取sessionId等用于验证码相关操作的辅助信息接收HttpServletResponse类型的参数response用于对响应进行操作如设置响应头告知客户端返回的是图片数据以及将生成的图片验证码数据写入到响应流中返回给客户端等
iAuthService.getCaptcha(request, response); // 直接调用iAuthService接口的getCaptcha方法由服务层去具体实现生成图片验证码、将验证码相关信息存储到session通过操作request获取的session来存储以及把图片验证码通过response返回给客户端等一系列业务逻辑该方法没有返回值因为主要操作是通过response直接向客户端返回图片验证码数据
}
/**
*
* @param request requestsessionId
* @param code
* @return
* @param request requestsessionIdsessionrequestsessionIdsession
* @param code @PathVariablesession
* @return ResultResult
*/
@PostMapping("/verifyCode/{code}")
public Result<String> verifyCode(HttpServletRequest request, @PathVariable("code") String code) {
return iAuthService.verifyCode(request, code);
@PostMapping("/verifyCode/{code}") // 表示该方法用于处理HTTP POST请求其请求路径为/api/auths/verifyCode/{code},其中{code}是路径变量,通过@PathVariable注解来获取用户输入的验证码作为路径的一部分POST请求常用于提交数据进行验证等操作这里用于提交用户输入的验证码进行校验的场景符合POST请求的语义
public Result<String> verifyCode(HttpServletRequest request, @PathVariable("code") String code) { // 接收HttpServletRequest类型的参数request用于获取请求相关信息特别是通过获取sessionId来查找之前存储的验证码相关内容通过@PathVariable("code")注解获取用户输入的验证码字符串,然后将这些信息传递给服务层进行验证码校验的业务逻辑处理
return iAuthService.verifyCode(request, code); // 调用iAuthService接口的verifyCode方法由服务层去具体实现校验用户输入的验证码与之前存储在session中的原始验证码是否一致、判断验证码是否在有效期内等业务逻辑最后把校验结果封装到Result对象中返回给客户端告知验证码校验的情况
}
@PostMapping("/track-presence")
public Result<String> trackPresence(HttpServletRequest request) {
return iAuthService.sendHeartbeat(request);
@PostMapping("/track-presence") // 表示此方法用于处理HTTP POST请求其请求路径为/api/auths/track-presence从方法名推测可能是用于跟踪用户的某种存在状态比如在线状态、活跃状态等POST请求常用于提交相关数据来触发相应的业务逻辑这里用于提交相关信息以启动对用户状态的跟踪操作
public Result<String> trackPresence(HttpServletRequest request) { // 接收HttpServletRequest类型的参数request用于获取请求相关信息比如获取用户的标识信息等以便服务层能够准确判断是哪个用户的状态需要被跟踪为后续的业务逻辑处理提供必要的数据基础
return iAuthService.sendHeartbeat(request); // 调用iAuthService接口的sendHeartbeat方法由服务层去具体实现发送心跳推测是类似定期发送信号来表明用户处于某种期望的状态比如在线状态等相关业务逻辑最后把操作的结果封装到Result对象中返回给客户端告知用户状态跟踪的相关情况
}
}
}

@ -1,97 +1,96 @@
package cn.org.alan.exam.controller;
import cn.org.alan.exam.common.group.CertificateGroup;
import cn.org.alan.exam.common.result.Result;
import cn.org.alan.exam.model.entity.Certificate;
import cn.org.alan.exam.model.form.CertificateForm;
import cn.org.alan.exam.model.vo.certificate.MyCertificateVO;
import cn.org.alan.exam.service.ICertificateService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
package cn.org.alan.exam.controller; // 声明该类所在的包名,用于在项目中对类进行分类组织,使代码结构更清晰,表明此控制器类属于该特定的包,便于区分不同功能模块的代码
import cn.org.alan.exam.common.group.CertificateGroup; // 引入CertificateGroup类可能用于对证书相关操作进行分组验证比如不同的证书添加、修改等操作按照特定规则进行分组便于灵活控制数据验证逻辑
import cn.org.alan.exam.common.result.Result; // 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功以及可能需要返回的数据等内容以一致的格式返回给调用者如前端
import cn.org.alan.exam.model.entity.Certificate; // 引入Certificate类这大概率是代表证书实体的数据模型类对应数据库中存储证书相关信息的表结构用于在业务逻辑中操作证书的实际数据
import cn.org.alan.exam.model.form.CertificateForm; // 引入CertificateForm类是用于接收前端传递过来的与证书相关操作如添加、修改证书的参数数据模型符合前端提交的相应业务数据格式要求
import cn.org.alan.exam.model.vo.certificate.MyCertificateVO; // 引入MyCertificateVO类应该是视图对象Value ObjectVO用于向客户端展示特定格式的已获证书相关信息通常是经过处理、整合后适合展示的数据形式
import cn.org.alan.exam.service.ICertificateService; // 引入ICertificateService接口定义了与证书管理相关的一系列业务方法例如证书的添加、查询、修改、删除等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类会依赖这个接口来调用相应功能
import com.baomidou.mybatisplus.core.metadata.IPage; // 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在进行分页查询相关业务逻辑时用于承载分页后的证书数据等信息
import jakarta.annotation.Resource; // 用于进行资源注入的注解在这里的作用是将实现了ICertificateService接口的具体实例注入到当前的CertificateController类中使得类中的方法可以方便地调用对应的服务方法实现具体业务逻辑
import org.springframework.security.access.prepost.PreAuthorize; // 用于在方法级别进行权限控制的注解,根据配置的权限表达式判断当前用户是否有相应权限来访问对应的方法,以此保证系统资源只能被授权用户访问,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated; // 结合具体的分组类像这里的CertificateGroup.CertificateInsertGroup.class等可以对传入方法的参数进行分组验证确保接收到的参数数据符合特定业务规则和验证要求保证数据的合法性和准确性
import org.springframework.web.bind.annotation.*; // 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口
/**
*
*
*
*
* @author zsx
* @since 2024-04-1
*/
@RestController
@RequestMapping("/api/certificate")
public class CertificateController {
@RestController // 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/certificate") // 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/certificate开头便于统一管理和组织与证书管理相关的一组API接口路径
public class CertificateController { // 定义一个公共的类类名为CertificateController作为证书管理相关业务逻辑的处理中心对外提供各种证书管理相关的接口接收和处理对应的HTTP请求
@Resource
private ICertificateService iCertificateService;
@Resource // 使用@Resource注解来进行依赖注入目的是让Spring容器查找并注入一个实现了ICertificateService接口的实例到当前类中这样后续的方法就能方便地调用该服务实例所提供的各种证书管理相关的业务方法
private ICertificateService iCertificateService; // 声明一个私有成员变量iCertificateService其类型为ICertificateService接口类型通过依赖注入后它将指向对应的服务实现类实例用于在控制器方法中调用具体的证书管理相关业务逻辑方法
/**
*
* @param certificateForm
* @return
* @param certificateForm CertificateForm
* @return Result
*/
@PostMapping
@PreAuthorize("hasAnyAuthority('role_admin')")
@PostMapping // 该注解表明这个方法用于处理HTTP POST请求其请求路径就是类级别定义的基础路径/api/certificate因为这里没有额外指定路径POST请求常用于向服务器提交数据在此处符合向服务器提交证书添加相关信息的场景
@PreAuthorize("hasAnyAuthority('role_admin')") // 此注解进行权限控制,只有拥有"role_admin"权限的用户才能访问这个方法,确保只有管理员角色的用户可以执行添加证书的操作,增强了系统对证书添加功能的权限管理
public Result<String> addCertificate(@RequestBody @Validated(CertificateGroup.CertificateInsertGroup.class)
CertificateForm certificateForm) {
//从token获取用户id放入创建人id属性
return iCertificateService.addCertificate(certificateForm);
CertificateForm certificateForm) { // @RequestBody表示从请求的正文中获取数据并将其转换为CertificateForm类型的对象@Validated结合指定的分组类CertificateGroup.CertificateInsertGroup.class对这个转换后的对象进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的添加证书业务逻辑处理。这里验证可能涉及到证书必填字段是否填写、格式是否正确等方面
//从token获取用户id放入创建人id属性这里应该是在具体的业务逻辑中通过解析用户登录的token获取当前操作的用户ID并将其设置到证书的创建人ID属性上以便记录证书是由谁创建的后续便于追溯等操作
return iCertificateService.addCertificate(certificateForm); // 方法体内部直接调用通过依赖注入获取的iCertificateService接口实例的addCertificate方法并将经过验证的certificateForm参数传递进去由服务层去具体实现添加证书的详细业务逻辑比如将证书信息保存到数据库中最后将服务层返回的包含添加结果等信息的Result对象直接返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @param certificateName
* @param certificationUnit
* @return
* @param pageNum pageNum1
* @param pageSize 1010
* @param certificateName
* @param certificationUnit
* @return ResultIPage<Certificate>IPageResult
*/
@GetMapping("/paging")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<IPage<Certificate>> pagingCertificate(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "certificateName", required = false) String certificateName,
@RequestParam(value = "certificationUnit", required = false) String certificationUnit) {
return iCertificateService.pagingCertificate(pageNum, pageSize, certificateName, certificationUnit);
@GetMapping("/paging") // 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/certificate基础上添加/paging即/api/certificate/pagingGET请求常用于获取资源这里用于获取分页后的证书信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行分页查询证书的操作,保障了该功能的权限安全性
public Result<IPage<Certificate>> pagingCertificate(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, // 通过@RequestParam注解获取请求中的pageNum参数设置其为可选参数required = false若客户端未传入则使用默认值1方便客户端灵活指定要查询的页码
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, // 同样通过@RequestParam获取pageSize参数也是可选参数默认值为10用于客户端指定每页显示的记录数量
@RequestParam(value = "certificateName", required = false) String certificateName, // 获取certificateName参数可选参数用于根据证书名称进行筛选查询
@RequestParam(value = "certificationUnit", required = false) String certificationUnit) { // 获取certificationUnit参数同样是可选参数用于按照认证单位进行筛选查询
return iCertificateService.pagingCertificate(pageNum, pageSize, certificateName, certificationUnit); // 调用通过依赖注入获取的iCertificateService接口实例的pagingCertificate方法由服务层去具体实现根据传入的页码、每页记录数以及筛选条件等信息进行分页查询证书的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
*
* @param id id
* @param certificateForm
* @return
* @param id idID
* @param certificateForm CertificateForm
* @return Result
*/
@PutMapping("/{id}")
@PreAuthorize("hasAnyAuthority('role_admin')")
public Result<String> updateCertificate(@PathVariable("id") Integer id,
@RequestBody CertificateForm certificateForm) {
certificateForm.setId(id);
return iCertificateService.updateCertificate(certificateForm);
@PutMapping("/{id}") // 此注解指定这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/certificate基础上添加/{id},其中{id}是路径变量用于接收要修改的证书的IDPUT请求常用于更新资源这里符合对指定ID的证书进行修改更新的操作场景
@PreAuthorize("hasAnyAuthority('role_admin')") // 进行权限控制,只有拥有"role_admin"权限的用户才能访问这个方法,确保只有管理员角色的用户可以执行修改证书的操作,保障了证书修改功能的权限安全性
public Result<String> updateCertificate(@PathVariable("id") Integer id, // 通过@PathVariable注解获取路径中传入的证书ID参数将其赋值给变量id以便在后续业务逻辑中准确找到对应的证书记录
@RequestBody CertificateForm certificateForm) { // @RequestBody表示从请求正文中获取数据并转换为CertificateForm类型的对象用于接收前端传来的修改证书的相关信息
certificateForm.setId(id); // 将获取到的证书ID设置到certificateForm对象中确保在服务层进行修改操作时能准确知道是对哪个证书进行修改将前端传来的更新信息与对应的证书记录关联起来
return iCertificateService.updateCertificate(certificateForm); // 调用iCertificateService接口的updateCertificate方法由服务层去具体实现根据传入的更新信息对指定ID的证书进行修改的详细业务逻辑比如更新数据库中对应证书记录的字段值等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param id id
* @return
* @param id idID
* @return Result
*/
@DeleteMapping("/delete/{id}")
@PreAuthorize("hasAnyAuthority('role_admin')")
public Result<String> deleteCertificate(@PathVariable("id") Integer id) {
return iCertificateService.deleteCertificate(id);
@DeleteMapping("/delete/{id}") // 该注解表明这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/certificate基础上添加/delete/{id},其中{id}是路径变量用于接收要删除的证书的IDDELETE请求常用于删除资源这里符合删除指定证书的操作场景
@PreAuthorize("hasAnyAuthority('role_admin')") // 进行权限控制,只有拥有"role_admin"权限的用户才能访问这个方法,确保只有管理员角色的用户可以执行删除证书的操作,保障了证书删除功能的权限安全性
public Result<String> deleteCertificate(@PathVariable("id") Integer id) { // 通过@PathVariable注解获取路径中传入的证书ID参数将其赋值给变量id以便在后续业务逻辑中准确找到对应的证书记录进行删除操作
return iCertificateService.deleteCertificate(id); // 调用iCertificateService接口的deleteCertificate方法由服务层去具体实现删除指定ID的证书的详细业务逻辑比如从数据库中移除对应的证书记录等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @return
* @param pageNum1
* @param pageSize10
* @return ResultIPage<MyCertificateVO>IPageResult
*/
@GetMapping("/paging/my")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<IPage<MyCertificateVO>> getMyCertificate(@RequestParam(value = "pageNum",required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "examName", required = false) String examName){
return iCertificateService.getMyCertificatePaging(pageNum, pageSize,examName);
@GetMapping("/paging/my") // 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/certificate基础上添加/paging/my即/api/certificate/paging/my用于获取分页后的已获证书相关信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')") // 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户都有权利进行分页查询已获证书的操作
public Result<IPage<MyCertificateVO>> getMyCertificate(@RequestParam(value = "pageNum",required = false, defaultValue = "1") Integer pageNum, // 获取请求中的pageNum参数为可选参数默认值为1用于指定页码
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize, // 获取pageSize参数也是可选参数默认值为10用于指定每页记录数
@RequestParam(value = "examName", required = false) String examName){ // 获取examName参数可选参数可能用于根据考试名称等相关因素来筛选已获证书信息具体筛选逻辑由服务层实现
return iCertificateService.getMyCertificatePaging(pageNum, pageSize,examName); // 调用iCertificateService接口的getMyCertificatePaging方法由服务层去具体实现根据传入的页码、每页记录数以及筛选条件等信息进行分页查询已获证书的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
}
}

@ -1,187 +1,121 @@
package cn.org.alan.exam.controller;
import cn.org.alan.exam.common.result.Result;
import cn.org.alan.exam.model.form.exam.ExamAddForm;
import cn.org.alan.exam.model.form.exam.ExamUpdateForm;
import cn.org.alan.exam.model.form.examquanswer.ExamQuAnswerAddForm;
import cn.org.alan.exam.model.vo.exam.*;
import cn.org.alan.exam.service.IExamService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
package cn.org.alan.exam.controller; // 声明该类所在的包名,用于在项目中对类进行分类组织,清晰地表明此控制器类属于特定的功能模块,方便代码的管理和维护
import cn.org.alan.exam.common.result.Result; // 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以标准的格式返回给调用者比如前端应用
import cn.org.alan.exam.model.form.exam.ExamAddForm; // 引入ExamAddForm类这是一个数据模型类用于承载创建考试时前端传递过来的相关信息例如考试名称、考试时间、考试科目等创建考试所需填写的各项内容
import cn.org.alan.exam.model.form.exam.ExamUpdateForm; // 引入ExamUpdateForm类同样是数据模型类用于接收修改考试时前端传来的包含更新后考试相关信息的数据比如修改后的考试时长、考试范围等信息
import cn.org.alan.exam.model.form.examquanswer.ExamQuAnswerAddForm; // 引入ExamQuAnswerAddForm类大概率是用于接收添加答案相关操作时前端传递过来的数据结构比如针对具体题目填写的答案内容等信息
import cn.org.alan.exam.model.vo.exam.*; // 通配符导入exam包下的所有视图对象Value ObjectVO这些VO类通常用于向客户端展示特定格式的与考试相关的数据经过了业务逻辑层的处理和整合方便前端展示和使用
import cn.org.alan.exam.service.IExamService; // 引入IExamService接口定义了与考试管理相关的一系列业务方法涵盖考试的创建、修改、查询、交卷等各种操作的逻辑抽象具体的实现由对应的服务类来完成本控制器类依赖此接口调用相应功能
import com.baomidou.mybatisplus.core.metadata.IPage; // 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询考试相关信息的业务逻辑中用于承载分页后的考试数据等内容
import jakarta.annotation.Resource; // 用于进行资源注入的注解作用是将实现了IExamService接口的具体实例注入到当前的ExamController类中使得类中的方法可以便捷地调用对应的服务方法来实现具体的考试管理业务逻辑
import jakarta.validation.constraints.NotBlank; // 引入验证约束注解用于确保对应参数的值不为空不仅不能为null还不能是空白字符串等情况在这里用于对相关参数进行数据合法性验证
import jakarta.validation.constraints.NotNull; // 也是验证约束注解用于保证参数的值不能为空即不能为null用于对参数进行基本的非空验证确保业务逻辑处理时有合理的数据输入
import jakarta.validation.constraints.Pattern; // 此注解用于按照指定的正则表达式模式对参数值进行格式验证,确保传入的参数符合特定的格式要求,增强数据的合法性和规范性
import org.springframework.security.access.prepost.PreAuthorize; // 用于在方法级别进行权限控制的注解,依据配置的权限表达式判断当前用户是否具备相应权限来访问对应的方法,以此保障系统资源只能被授权用户操作,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated; // 结合具体的验证规则(可以是默认规则或者像上面引入的分组验证等情况)对传入方法的参数进行数据合法性验证,保证接收到的参数符合业务要求,避免非法数据进入业务逻辑处理流程
import org.springframework.web.bind.annotation.*; // 通配符导入包含了众多Spring Web相关的注解像用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解便于在控制器类中构建各种API接口
import java.util.List; // 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个题目汇总信息等情况时会用到
/**
*
*
*
* @author Alan
* @since 2024-03-21
*/
@RestController
@RequestMapping("/api/exams")
public class ExamController {
@RestController // 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/exams") // 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/exams开头便于统一管理和组织与考试管理相关的一组API接口路径
public class ExamController { // 定义一个公共的类类名为ExamController作为考试管理相关业务逻辑的处理中心对外提供各类考试管理相关的接口接收并处理对应的HTTP请求
@Resource
private IExamService examService;
@Resource // 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IExamService接口的实例到当前类中方便后续的方法调用该服务实例所提供的各种考试管理相关业务方法
private IExamService examService; // 声明一个私有成员变量examService其类型为IExamService接口类型通过依赖注入后它将指向对应的服务实现类实例用于在控制器方法中调用具体的考试管理相关业务逻辑方法
/**
*
* @param examAddForm
* @return
* @param examAddFormExamAddForm
* @return ResultResult
*/
@PostMapping
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<String> createExam(@Validated @RequestBody ExamAddForm examAddForm) {
return examService.createExam(examAddForm);
@PostMapping // 该注解表明这个方法用于处理HTTP POST请求其请求路径就是类级别定义的基础路径/api/exams因为这里没有额外指定路径POST请求常用于向服务器提交数据在此处符合向服务器提交创建考试相关信息的场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 此注解进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行创建考试的操作,加强了对考试创建功能的权限管理
public Result<String> createExam(@Validated @RequestBody ExamAddForm examAddForm) { // @Validated注解结合@RequestBody注解@RequestBody表示从请求的正文中获取数据并将其转换为ExamAddForm类型的对象@Validated用于对这个转换后的对象依据相关规则进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的创建考试业务逻辑处理
return examService.createExam(examAddForm); // 方法体内部直接调用通过依赖注入获取的examService接口实例的createExam方法并将经过验证的examAddForm参数传递进去由服务层去具体实现创建考试的详细业务逻辑比如将考试信息保存到数据库中最后将服务层返回的包含创建结果等信息的Result对象直接返回给客户端
}
/**
*
* @param examId
* @return
* @param examIdID便@NotNullID
* @return Result
*/
@GetMapping("/start")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<String> startExam(@RequestParam("examId") @NotNull Integer examId) {
return examService.startExam(examId);
@GetMapping("/start") // 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/exams基础上添加/start即/api/exams/startGET请求常用于获取资源或触发某个操作这里用于触发开始考试这个操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')") // 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都能访问这个方法,意味着教师、管理员和学生角色的用户都有权利执行开始考试的操作
public Result<String> startExam(@RequestParam("examId") @NotNull Integer examId) { // 通过@RequestParam注解获取请求中名为"examId"的参数,并通过@NotNull注解确保该参数不能为空将获取到的考试ID传递给服务层以便服务层准确找到对应的考试记录来执行开始考试的业务逻辑
return examService.startExam(examId); // 调用通过依赖注入获取的examService接口实例的startExam方法由服务层去具体实现开始考试的详细业务逻辑比如更新考试状态为正在进行等操作最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param examUpdateForm
* @param id
* @return
* @param examUpdateFormExamUpdateForm
* @param idID@NotNull
* @return Result
*/
@PutMapping("/{id}")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<String> updateExam(@Validated @RequestBody ExamUpdateForm examUpdateForm, @PathVariable("id") @NotNull Integer id) {
return examService.updateExam(examUpdateForm,id);
@PutMapping("/{id}") // 此注解指定这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/exams基础上添加/{id},其中{id}是路径变量用于接收要修改的考试的IDPUT请求常用于更新资源这里符合对指定ID的考试进行修改更新的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行修改考试的操作,保障了考试修改功能的权限安全性
public Result<String> updateExam(@Validated @RequestBody ExamUpdateForm examUpdateForm, @PathVariable("id") @NotNull Integer id) { // @Validated结合@RequestBody对examUpdateForm参数进行数据合法性验证及从请求正文获取数据转换为对应对象@PathVariable注解用于获取路径中的考试ID参数并通过@NotNull保证其非空然后将这些信息传递给服务层进行修改考试的业务逻辑处理
return examService.updateExam(examUpdateForm,id); // 调用examService接口的updateExam方法由服务层去具体实现根据传入的更新信息对指定ID的考试进行修改的详细业务逻辑比如更新数据库中对应考试记录的字段值等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param ids
* @return
* @param ids@Pattern(regexp = "^\\d+(,\\d+)*$|^\\d+$")ID便
* @return Result
*/
@DeleteMapping("/{ids}")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<String> deleteExam(@PathVariable("ids") @Pattern(regexp = "^\\d+(,\\d+)*$|^\\d+$") String ids) {
return examService.deleteExam(ids);
@DeleteMapping("/{ids}") // 该注解表明这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/exams基础上添加/{ids},其中{ids}是路径变量用于接收要删除的考试的ID可以是多个用逗号隔开DELETE请求常用于删除资源这里符合删除指定考试的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行删除考试的操作,保障了考试删除功能的权限安全性
public Result<String> deleteExam(@PathVariable("ids") @Pattern(regexp = "^\\d+(,\\d+)*$|^\\d+$") String ids) { // 通过@PathVariable注解获取路径中传入的符合格式要求的考试ID字符串参数将其传递给服务层以便服务层依据这些ID准确找到并删除对应的考试记录
return examService.deleteExam(ids); // 调用examService接口的deleteExam方法由服务层去具体实现删除指定ID的考试的详细业务逻辑比如从数据库中移除对应的考试记录等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @param title
* @return
* @param pageNumpageNum11
* @param pageSize1010
* @param title
* @return ResultIPage<ExamVO>IPageResult
*/
@GetMapping("/paging")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<IPage<ExamVO>> getPagingExam(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "title", required = false) String title) {
return examService.getPagingExam(pageNum, pageSize, title);
@GetMapping("/paging") // 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/exams基础上添加/paging即/api/exams/pagingGET请求常用于获取资源这里用于获取分页后的考试列表信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')") // 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行分页查找考试列表的操作,保障了该功能的权限安全性
public Result<IPage<ExamVO>> getPagingExam(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, // 通过@RequestParam注解获取请求中的pageNum参数设置其为可选参数required = false若客户端未传入则使用默认值1方便客户端灵活指定要查询的页码
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, // 同样通过@RequestParam获取pageSize参数也是可选参数默认值为10用于客户端指定每页显示的记录数量
@RequestParam(value = "title", required = false) String title) { // 获取title参数可选参数用于根据考试标题进行筛选查询
return examService.getPagingExam(pageNum, pageSize, title); // 调用通过依赖注入获取的examService接口实例的getPagingExam方法由服务层去具体实现根据传入的页码、每页记录数以及筛选条件等信息进行分页查询考试列表的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
* id
* @param examId
* @return
*/
@GetMapping("/question/list/{examId}")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<ExamQuestionListVO> getQuestionList(@PathVariable("examId") @NotBlank Integer examId) {
return examService.getQuestionList(examId);
}
/**
*
* @param examId
* @param questionId
* @return
*/
@GetMapping("/question/single")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<ExamQuDetailVO> getQuestionSingle(@RequestParam("examId") Integer examId,
@RequestParam("questionId") Integer questionId) {
return examService.getQuestionSingle(examId, questionId);
}
/**
*
* @param examId
* @return
*/
@GetMapping("/collect/{id}")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<List<ExamQuCollectVO>> getCollect(@PathVariable("id") @NotNull Integer examId) {
return examService.getCollect(examId);
}
/**
*
* @param examId
* @return
*/
@GetMapping("/detail")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<ExamDetailVO> getDetail(@RequestParam("examId") @NotBlank Integer examId) {
return examService.getDetail(examId);
}
/**
*
* @param pageNum
* @param pageSize
* @return
*/
@GetMapping("/grade")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<IPage<ExamGradeListVO>> getGradeExamList(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "title", required = false) String title) {
return examService.getGradeExamList(pageNum,pageSize,title);
}
/**
*
* @param examId
* @return
* @param examId@NotBlankIDIDIDID
* @return ResultIDID
*/
@PutMapping("/cheat/{examId}")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<Integer> addCheat(@PathVariable("examId") @NotNull Integer examId) {
return examService.addCheat(examId);
@GetMapping("/question/list/{examId}") // 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/exams基础上添加/question/list/{examId},其中{examId}是路径变量用于接收要获取题目ID列表的考试的IDGET请求常用于获取资源这里用于获取特定考试的题目ID列表资源符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')") // 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法意味着教师、管理员和学生角色的用户都有权利执行获取考试题目ID列表的操作
public Result<ExamQuestionListVO> getQuestionList(@PathVariable("examId") @NotBlank Integer examId) { // 通过@PathVariable注解获取路径中传入的经过验证的考试ID参数将其传递给服务层以便服务层依据该考试ID查找并获取对应的题目ID列表信息用于后续返回给客户端
return examService.getQuestionList(examId); // 调用examService接口的getQuestionList方法由服务层去具体实现根据传入的考试ID查找并获取其对应题目ID列表的详细业务逻辑最后将获取到的题目ID列表数据封装到Result对象中返回给客户端
}
}
/**
*
* @param examQuAnswerForm
* @return
*/
@PostMapping("/full-answer")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<String> addAnswer(@Validated @RequestBody ExamQuAnswerAddForm examQuAnswerForm) {
return examService.addAnswer(examQuAnswerForm);
}
/**
*
* @param examId
* @return
*/
@GetMapping(value = "/hand-exam/{examId}")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
public Result<ExamQuDetailVO> handleExam(@PathVariable("examId") @NotNull Integer examId) {
return examService.handExam(examId);
}
}
/**
*
* @param examIdID**/

@ -1,107 +1,171 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,使代码结构更清晰,表明此控制器类属于特定的“刷题管理”功能模块相关的包
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容方便以一致的格式返回给调用者如前端
import cn.org.alan.exam.model.form.ExerciseFillAnswerFrom;
// 引入ExerciseFillAnswerFrom类这是一个数据模型类用于承载与填充答案相关操作时前端传递过来的数据结构比如填写的具体答案内容等信息
import cn.org.alan.exam.model.vo.QuestionVO;
// 引入QuestionVO类这大概率是视图对象Value ObjectVO用于向客户端展示特定格式的试题相关信息经过业务逻辑处理后以适合展示的形式传递给前端
import cn.org.alan.exam.model.vo.exercise.AnswerInfoVO;
// 引入AnswerInfoVO类同样是视图对象类用于展示用户回答详情相关的信息按照一定的格式组织数据方便前端展示和使用
import cn.org.alan.exam.model.vo.exercise.ExerciseRepoVO;
// 引入ExerciseRepoVO类也是视图对象类可能用于向客户端展示可刷题库相关信息比如包含题库名称、简介等展示用的数据
import cn.org.alan.exam.model.vo.exercise.QuestionSheetVO;
// 引入QuestionSheetVO类应该是用于向客户端展示试题相关信息列表的视图对象类例如以列表形式展示的试题基本信息等
import cn.org.alan.exam.service.IExerciseRecordService;
// 引入IExerciseRecordService接口定义了与刷题记录相关的一系列业务方法例如获取试题、填充答案、获取单题详情等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类会依赖这个接口来调用相应功能
import cn.org.alan.exam.service.IRepoService;
// 引入IRepoService接口定义了与题库相关的业务方法像获取可刷题库列表等操作的逻辑抽象控制器类通过此接口调用对应的服务方法实现相关功能
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询可刷题库等业务逻辑时用于承载分页后的相关数据
import jakarta.annotation.Nullable;
// 引入Nullable注解用于标记对应的参数可以为null值在参数验证等场景中表明该参数允许为空的情况
import jakarta.annotation.Resource;
// 用于进行资源注入的注解在这里的作用是将实现了IExerciseRecordService和IRepoService接口的具体实例注入到当前的ExerciseController类中使得类中的方法可以方便地调用对应的服务方法实现具体业务逻辑
import jakarta.validation.constraints.Max;
// 引入验证约束注解,用于确保对应参数的值不超过指定的最大值,在这里用于对相关参数进行数据合法性验证,限定其取值上限
import jakarta.validation.constraints.Min;
// 引入验证约束注解,用于保证对应参数的值不小于指定的最小值,用于对参数进行取值下限的合法性验证
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,根据配置的权限表达式判断当前用户是否有相应权限来访问对应的方法,以此保证系统资源只能被授权用户访问,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated;
// 结合具体的验证规则(可以是默认规则或者自定义规则)对传入方法的参数进行数据合法性验证,保证接收到的参数符合业务要求,避免非法数据进入业务逻辑处理流程
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个试题信息等情况时会用到
/**
*
*
*
* @Author Alan
* @Version
* @Date 2024/3/25 11:21 AM
*/
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/exercises")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/exercises开头便于统一管理和组织与刷题管理相关的一组API接口路径
@Validated
// 对整个类中的方法参数启用验证机制,确保传入方法的参数符合相应的验证规则(结合具体的约束注解来实现),保证数据合法性
public class ExerciseController {
@Resource
private IExerciseRecordService iExerciseRecordService;
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IExerciseRecordService接口的实例到当前类中方便后续的方法调用该服务实例所提供的各种刷题记录相关业务方法
@Resource
private IRepoService iRepoService;
// 同样使用@Resource注解进行依赖注入将实现了IRepoService接口的实例注入到当前类中使得类中的方法能够调用对应的题库相关业务方法
/**
* Id
*
* @param repoId Id
* @param quType
* @return
* @param repoId IdIDID
* @param quType @RequestParamrequired = false@Min@Max14@NullablenullID
* @return ResultIDID
*/
@GetMapping("/{repoId}")
// 该注解表明这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/exercises基础上添加/{repoId},其中{repoId}是路径变量用于接收要获取试题ID列表的题库的IDGET请求常用于获取资源这里用于获取特定题库中的试题ID列表资源符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"、"role_teacher"或者"role_admin"权限的用户都可以访问这个方法意味着学生、教师和管理员角色的用户都有权利执行获取试题ID列表的操作
public Result<List<QuestionSheetVO>> getQuestion(@PathVariable("repoId") Integer repoId,
// 通过@PathVariable注解获取路径中传入的题库ID参数将其赋值给变量repoId以便在后续业务逻辑中准确找到对应的题库来获取试题ID列表
@Min(value = 1, message = "试题类型最小值应为1")
@Max(value = 4, message = "试题类型最大值应为4")
@Nullable
@RequestParam(value = "quType", required = false) Integer quType) {
// 通过@RequestParam注解获取请求中名为"quType"的参数可选参数若请求中未提供则为null并通过@Min、@Max和@Nullable注解对其进行取值范围及可空性的验证用于筛选特定类型的试题
return iExerciseRecordService.getQuestionSheet(repoId, quType);
// 调用通过依赖注入获取的iExerciseRecordService接口实例的getQuestionSheet方法由服务层去具体实现根据传入的题库ID和试题类型可选获取试题ID列表的详细业务逻辑最后将获取到的试题ID列表数据封装到Result对象中返回给客户端
}
/**
*
*
* @param exerciseFillAnswerFrom
* @return
* @param exerciseFillAnswerFrom ExerciseFillAnswerFrom
* @return Result
*/
@PostMapping("/fillAnswer")
// 表明这个方法用于处理HTTP POST请求其请求路径是在类的基础路径/api/exercises基础上添加/fillAnswer即/api/exercises/fillAnswerPOST请求常用于向服务器提交数据在此处符合向服务器提交填充答案相关信息的场景
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"、"role_teacher"或者"role_admin"权限的用户都可以访问这个方法,意味着学生、教师和管理员角色的用户都有权利执行填充答案的操作
public Result<QuestionVO> fillAnswer(@RequestBody ExerciseFillAnswerFrom exerciseFillAnswerFrom) {
// @RequestBody表示从请求的正文中获取数据并将其转换为ExerciseFillAnswerFrom类型的对象以便获取前端提交的填充答案相关信息用于后续业务逻辑处理
return iExerciseRecordService.fillAnswer(exerciseFillAnswerFrom);
// 调用通过依赖注入获取的iExerciseRecordService接口实例的fillAnswer方法由服务层去具体实现填充答案以及获取相应试题信息可能经过处理比如判断答案正确性等的详细业务逻辑最后将获取到的试题信息数据封装到Result对象中返回给客户端
}
/**
*
*
* @param pageNum
* @param pageSize
* @param title
* @return
* @param pageNum pageNum11
* @param pageSize 1010
* @param title
* @return ResultIPage<ExerciseRepoVO>IPageResult
*/
@GetMapping("/getRepo")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/exercises基础上添加/getRepo即/api/exercises/getRepoGET请求常用于获取资源这里用于获取分页后的可刷题库列表信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"、"role_teacher"或者"role_admin"权限的用户都可以访问这个方法,意味着学生、教师和管理员角色的用户都有权利执行分页获取可刷题库列表的操作
public Result<IPage<ExerciseRepoVO>> getRepo(
@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
// 通过@RequestParam注解获取请求中的pageNum参数设置其为可选参数required = false若客户端未传入则使用默认值1方便客户端灵活指定要查询的页码
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
// 同样通过@RequestParam获取pageSize参数也是可选参数默认值为10用于客户端指定每页显示的记录数量
@RequestParam(value = "title", required = false) String title) {
// 获取title参数可选参数用于根据题库名称进行筛选查询
return iRepoService.getRepo(pageNum, pageSize, title);
// 调用通过依赖注入获取的iRepoService接口实例的getRepo方法由服务层去具体实现根据传入的页码、每页记录数以及筛选条件等信息进行分页查询可刷题库列表的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
*
* @param id id
* @return
* @param id idID
* @return Result
*/
@GetMapping("/question/{id}")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/exercises基础上添加/question/{id},其中{id}是路径变量用于接收要获取详情的试题的IDGET请求常用于获取资源这里用于获取特定试题的详情信息资源符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"、"role_teacher"或者"role_admin"权限的用户都可以访问这个方法,意味着学生、教师和管理员角色的用户都有权利执行获取单题详情的操作
public Result<QuestionVO> getSingle(@PathVariable("id")Integer id){
// 通过@PathVariable注解获取路径中传入的试题ID参数将其赋值给变量id以便在后续业务逻辑中准确找到对应的试题记录来获取其详情信息
return iExerciseRecordService.getSingle(id);
// 调用通过依赖注入获取的iExerciseRecordService接口实例的getSingle方法由服务层去具体实现根据传入的试题ID获取其详细信息不包含答案的详细业务逻辑最后将获取到的试题详情信息封装到Result对象中返回给客户端
}
/**
*
* @param
* @return
* @param repoId IdID
* @param quId idIDID
* @return Result
*/
@GetMapping("/answerInfo/{repoId}/{quId}")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/exercises基础上添加/answerInfo/{repoId}/{quId},其中{repoId}和{quId}是路径变量分别用于接收题库ID和试题IDGET请求常用于获取资源这里用于获取特定试题的用户回答详情资源符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"、"role_teacher"或者"role_admin"权限的用户都可以访问这个方法,意味着学生、教师和管理员角色的用户都有权利执行获取用户回答详情的操作
public Result<AnswerInfoVO> getAnswerInfo(@PathVariable("repoId")Integer repoId, @PathVariable("quId")Integer quId){
// 通过@PathVariable注解分别获取路径中传入的题库ID和试题ID参数将其赋值给变量repoId和quId以便在后续业务逻辑中准确找到对应的用户回答记录来获取其详细信息
return iExerciseRecordService.getAnswerInfo(repoId,quId);
// 调用通过依赖注入获取的iExerciseRecordService接口实例的getAnswerInfo方法由服务层去具体实现根据传入的题库ID和试题ID获取对应的用户回答详情的详细业务逻辑最后将获取到的用户回答详情信息封装到Result对象中返回给客户端
}
}
}

@ -1,100 +1,150 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,清晰表明此控制器类属于特定的功能模块,方便代码管理与维护,这里是和班级管理相关的控制器类所在的包
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以便以标准格式返回给调用者比如前端应用
import cn.org.alan.exam.model.form.GradeForm;
// 引入GradeForm类这是一个数据模型类用于承载与班级相关操作如新增、修改班级时前端传递过来的班级信息数据例如班级名称、班级描述等内容
import cn.org.alan.exam.model.vo.GradeVO;
// 引入GradeVO类大概率是视图对象Value ObjectVO用于向客户端展示特定格式的班级相关信息经过业务逻辑层处理整合后以适合展示的数据形式传递给前端
import cn.org.alan.exam.service.IGradeService;
// 引入IGradeService接口定义了与班级管理相关的一系列业务方法像班级的查询、新增、修改、删除等操作的逻辑抽象具体实现由对应的服务类来完成本控制器类依赖此接口调用相应功能
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询班级相关信息的业务逻辑中用于承载分页后的班级数据等内容
import jakarta.annotation.Resource;
// 用于进行资源注入的注解作用是将实现了IGradeService接口的具体实例注入到当前的GradeController类中使得类中的方法可以便捷地调用对应的服务方法实现具体业务逻辑
import jakarta.validation.constraints.NotBlank;
// 引入验证约束注解用于确保对应参数的值不为空不仅不能为null还不能是空白字符串等情况在这里用于对相关参数进行数据合法性验证保证传入的数据符合业务要求
import jakarta.validation.constraints.NotNull;
// 也是验证约束注解用于保证参数的值不能为空即不能为null用于对参数进行基本的非空验证避免因空值导致业务逻辑出错
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,依据配置的权限表达式判断当前用户是否具备相应权限来访问对应的方法,以此保障系统资源只能被授权用户操作,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated;
// 结合具体的验证规则(可以是默认规则或者自定义规则等情况)对传入方法的参数进行数据合法性验证,保证接收到的参数符合业务要求,防止非法数据进入业务逻辑处理流程
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解像用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解便于在控制器类中构建各种API接口
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如获取所有班级列表时返回多个班级信息时会用到
/**
*
* 退
*
* @author Alan
* @since 2024-03-21
*/
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/grades")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/grades开头便于统一管理和组织与班级管理相关的一组API接口路径
public class GradeController {
// 定义一个公共的类作为班级管理相关业务逻辑的处理中心对外提供各类班级管理相关的接口接收并处理对应的HTTP请求
@Resource
private IGradeService gradeService;
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IGradeService接口的实例到当前类中后续的方法就能调用该服务实例所提供的各种班级管理相关业务方法
/**
*
* @param pageNum
* @param pageSize
* @param gradeName
* @return
* @param pageNumpageNum11
* @param pageSize1010
* @param gradeName
* @return ResultIPage<GradeVO>IPageResult
*/
@GetMapping("/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/grades基础上添加/paging即/api/grades/pagingGET请求常用于获取资源这里用于获取分页后的班级列表信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行分页查询班级的操作,保障了该功能的权限安全性
public Result<IPage<GradeVO>> getGrade(@RequestParam(value = "pageNum",required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "gradeName",required = false) String gradeName) {
// 通过@RequestParam注解获取请求中的pageNum参数设置其为可选参数required = false若客户端未传入则使用默认值1方便客户端灵活指定要查询的页码同样获取pageSize参数默认值为10获取gradeName参数用于筛选查询
return gradeService.getPaging(pageNum, pageSize, gradeName);
// 调用通过依赖注入获取的gradeService接口实例的getPaging方法由服务层去具体实现根据传入的页码、每页记录数以及筛选条件等信息进行分页查询班级的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
*
* @param gradeForm
* @return
* @param gradeFormGradeForm
* @return ResultResult
*/
@PostMapping("/add")
// 该注解表明这个方法用于处理HTTP POST请求其请求路径是在类的基础路径/api/grades基础上添加/add即/api/grades/addPOST请求常用于向服务器提交数据在此处符合向服务器提交新增班级相关信息的场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 此注解进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行新增班级的操作,加强了对班级新增功能的权限管理
public Result<String> addGrade(@Validated @RequestBody GradeForm gradeForm) {
// @Validated注解结合@RequestBody注解@RequestBody表示从请求的正文中获取数据并将其转换为GradeForm类型的对象@Validated用于对这个转换后的对象依据相关规则进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的新增班级业务逻辑处理
return gradeService.addGrade(gradeForm);
// 方法体内部直接调用通过依赖注入获取的gradeService接口实例的addGrade方法并将经过验证的gradeForm参数传递进去由服务层去具体实现新增班级的详细业务逻辑比如将班级信息保存到数据库中最后将服务层返回的包含新增结果等信息的Result对象直接返回给客户端
}
/**
*
* @param id
* @param gradeForm
* @return
* @param idID@NotNull
* @param gradeFormGradeForm
* @return Result
*/
@PutMapping("/update/{id}")
// 此注解指定这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/grades基础上添加/update/{id},其中{id}是路径变量用于接收要修改的班级的IDPUT请求常用于更新资源这里符合对指定ID的班级进行修改更新的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行修改班级的操作,保障了班级修改功能的权限安全性
public Result<String> updateGrade(@PathVariable("id") @NotNull Integer id,@Validated @RequestBody GradeForm gradeForm) {
// @PathVariable注解用于获取路径中的班级ID参数并通过@NotNull保证其非空@Validated结合@RequestBody对gradeForm参数进行数据合法性验证及从请求正文获取数据转换为对应对象然后将这些信息传递给服务层进行修改班级的业务逻辑处理
return gradeService.updateGrade(id, gradeForm);
// 调用gradeService接口的updateGrade方法由服务层去具体实现根据传入的更新信息对指定ID的班级进行修改的详细业务逻辑比如更新数据库中对应班级记录的字段值等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param id
* @return
* @param idID@NotNull
* @return Result
*/
@DeleteMapping("/delete/{id}")
// 该注解表明这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/grades基础上添加/delete/{id},其中{id}是路径变量用于接收要删除的班级的IDDELETE请求常用于删除资源这里符合删除指定班级的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行删除班级的操作,保障了班级删除功能的权限安全性
public Result<String> deleteGrade(@PathVariable("id") @NotNull Integer id) {
// 通过@PathVariable注解获取路径中传入的班级ID参数并通过@NotNull确保其非空将其传递给服务层以便服务层依据该ID准确找到并删除对应的班级记录
return gradeService.deleteGrade(id);
// 调用gradeService接口的deleteGrade方法由服务层去具体实现删除指定ID的班级的详细业务逻辑比如从数据库中移除对应的班级记录等最后将操作结果封装到Result对象中返回给客户端
}
/**
* 退
* @param ids
* @return
* @param ids@NotBlank退ID
* @return Result退
*/
@PatchMapping("/remove/{ids}")
// 此注解表明这个方法用于处理HTTP PATCH请求其请求路径是在类的基础路径/api/grades基础上添加/remove/{ids},其中{ids}是路径变量用于接收要退出班级的相关标识信息PATCH请求常用于对资源进行部分更新操作这里符合对班级成员退出班级这一操作的语义可理解为对班级成员关系这一资源的部分修改
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_studnet')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_studnet"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户都有权利执行退出班级的操作
public Result<String> removeUserGrade(@PathVariable("ids") @NotBlank String ids) {
// 通过@PathVariable注解获取路径中传入的经过验证的班级相关标识参数将其传递给服务层以便服务层依据这些信息执行成员退出班级的业务逻辑
return gradeService.removeUserGrade(ids);
// 调用gradeService接口的removeUserGrade方法由服务层去具体实现根据传入的班级相关标识信息处理成员退出班级的详细业务逻辑比如更新数据库中班级成员关系表等操作最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @return
*/
@GetMapping("/list")
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
public Result<List<GradeVO>> getAllGrade(){
return gradeService.getAllGrade();
}
}
/**
*
* @return Result
*/
@GetMapping("/list")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/grades基础上添加/list即/api/grades/listGET请求常用于获取资源这里用于获取所有班级列表资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行获取所有班级列表的操作,保障了该功能的权限安全性
public Result<List<GradeVO>> getAllGrade(){
return gradeService.getAllGrade();
// 调用gradeService接口的getAllGrade方法由服务层去具体实现获取所有班级信息的详细业务逻辑比如从数据库中查询所有班级记录并转换为对应的视图对象列表等操作最后将获取到的班级列表数据封装到Result对象中返回给客户端
}
}

@ -1,91 +1,138 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“公告管理”功能模块相关的包,方便代码管理和维护
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以一致的格式返回给调用者如前端
import cn.org.alan.exam.model.form.NoticeForm;
// 引入NoticeForm类这是一个数据模型类用于承载添加、修改公告等操作时前端传递过来的与公告相关的信息例如公告标题、内容、发布时间等具体的数据内容
import cn.org.alan.exam.model.vo.NoticeVO;
// 引入NoticeVO类大概率是视图对象Value ObjectVO用于向客户端展示特定格式的公告相关信息经过业务逻辑处理后以适合展示的形式传递给前端
import cn.org.alan.exam.service.INoticeService;
// 引入INoticeService接口定义了与公告管理相关的一系列业务方法例如公告的添加、删除、修改、查询等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类会依赖这个接口来调用相应功能
import cn.org.alan.exam.util.JwtUtil;
// 引入JwtUtil类从名字推测它可能是用于处理JSON Web TokenJWT相关操作的工具类比如解析token获取用户信息、验证token有效性等不过在此控制器类中暂时未看到具体使用它的地方可能在服务层等其他地方会用到
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询公告相关信息的业务逻辑时用于承载分页后的公告数据等内容
import jakarta.annotation.Resource;
// 用于进行资源注入的注解在这里的作用是将实现了INoticeService接口的具体实例注入到当前的NoticeController类中使得类中的方法可以方便地调用对应的服务方法实现具体业务逻辑
import jakarta.servlet.http.HttpServletRequest;
// 引入HttpServletRequest类它代表了客户端发送过来的HTTP请求对象在一些需要获取请求相关信息的场景比如根据请求中的token获取用户权限等虽然此处未体现具体使用下会用到
import jakarta.validation.constraints.NotBlank;
// 引入验证约束注解用于确保对应参数的值不为空不仅不能为null还不能是空白字符串等情况在这里用于对相关参数进行数据合法性验证保证传入的数据符合业务要求
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,根据配置的权限表达式判断当前用户是否有相应权限来访问对应的方法,以此保证系统资源只能被授权用户访问,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated;
// 结合具体的验证规则(可以是默认规则或者自定义规则)对传入方法的参数进行数据合法性验证,保证接收到的参数符合业务要求,避免非法数据进入业务逻辑处理流程
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口
/**
*
*
*
* @author Alan
* @since 2024-03-21
*/
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/notices")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/notices开头便于统一管理和组织与公告管理相关的一组API接口路径
public class NoticeController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了INoticeService接口的实例到当前类中方便后续的方法调用该服务实例所提供的各种公告管理相关业务方法
private INoticeService noticeService;
/**
*
* @param noticeForm
* @return
* @param noticeFormNoticeForm
* @return Result
*/
@PostMapping
// 该注解表明这个方法用于处理HTTP POST请求其请求路径就是类级别定义的基础路径/api/notices因为这里没有额外指定路径POST请求常用于向服务器提交数据在此处符合向服务器提交添加公告相关信息的场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行添加公告的操作,加强了对公告添加功能的权限管理
public Result<String> addNotice(@Validated @RequestBody NoticeForm noticeForm) {
// @Validated注解结合@RequestBody注解@RequestBody表示从请求的正文中获取数据并将其转换为NoticeForm类型的对象@Validated用于对这个转换后的对象依据相关规则进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的添加公告业务逻辑处理
return noticeService.addNotice( noticeForm);
// 方法体内部直接调用通过依赖注入获取的noticeService接口实例的addNotice方法并将经过验证的noticeForm参数传递进去由服务层去具体实现添加公告的详细业务逻辑比如将公告信息保存到数据库中最后将服务层返回的包含添加结果等信息的Result对象直接返回给客户端
}
/**
*
* @param ids
* @return
* @param ids@NotBlankIDID
* @return Result
*/
@DeleteMapping("/{ids}")
// 该注解表明这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/notices基础上添加/{ids},其中{ids}是路径变量用于接收要删除的公告的相关标识信息DELETE请求常用于删除资源这里符合删除指定公告的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行删除公告的操作,保障了公告删除功能的权限安全性
public Result<String> deleteNotice(@PathVariable("ids") @NotBlank String ids) {
// 通过@PathVariable注解获取路径中传入的经过验证的公告相关标识参数将其传递给服务层以便服务层依据这些信息执行删除对应的公告记录的业务逻辑
return noticeService.deleteNotice(ids);
// 调用noticeService接口的deleteNotice方法由服务层去具体实现删除指定标识的公告的详细业务逻辑比如从数据库中移除对应的公告记录等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param id
* @param noticeForm
* @return
* @param id@NotBlank
* @param noticeFormNoticeForm
* @return Result
*/
@PutMapping("/{id}")
// 此注解指定这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/notices基础上添加/{id},其中{id}是路径变量用于接收要修改的公告的IDPUT请求常用于更新资源这里符合对指定ID的公告进行修改更新的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行修改公告的操作,保障了公告修改功能的权限安全性
public Result<String> updateNotice(@PathVariable("id") @NotBlank String id,@Validated @RequestBody NoticeForm noticeForm) {
// @PathVariable注解用于获取路径中的公告ID参数并通过@NotBlank保证其非空@Validated结合@RequestBody对noticeForm参数进行数据合法性验证及从请求正文获取数据转换为对应对象然后将这些信息传递给服务层进行修改公告的业务逻辑处理
return noticeService.updateNotice(id, noticeForm);
// 调用noticeService接口的updateNotice方法由服务层去具体实现根据传入的更新信息对指定ID的公告进行修改的详细业务逻辑比如更新数据库中对应公告记录的字段值等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @param title
* @return
* @param pageNumpageNum11
* @param pageSize1010
* @param title
* @return ResultIPage<NoticeVO>IPageResult
*/
@GetMapping("/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/notices基础上添加/paging即/api/notices/pagingGET请求常用于获取资源这里用于获取分页后的公告列表信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行分页查找公告的操作,保障了该功能的权限安全性
public Result<IPage<NoticeVO>> getNotice(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "title", required = false) String title) {
// 通过@RequestParam注解获取请求中的pageNum参数设置其为可选参数required = false若客户端未传入则使用默认值1方便客户端灵活指定要查询的页码同样获取pageSize参数默认值为10获取title参数用于根据公告标题筛选查询
return noticeService.getNotice( pageNum, pageSize, title);
// 调用通过依赖注入获取的noticeService接口实例的getNotice方法由服务层去具体实现根据传入的页码、每页记录数以及筛选条件等信息进行分页查询公告的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @return
* @param pageNum1
* @param pageSize10
* @return ResultIPage<NoticeVO>IPageResult
*/
@GetMapping("/new")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/notices基础上添加/new即/api/notices/newGET请求常用于获取资源这里用于获取最新公告信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户都有权利执行获取最新公告的操作
public Result<IPage<NoticeVO>> getNewNotice(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize){
// 通过@RequestParam注解获取请求中的pageNum和pageSize参数分别用于指定页码和每页记录数若未传入则使用默认值方便客户端灵活获取最新公告信息
return noticeService.getNewNotice(pageNum,pageSize);
// 调用通过依赖注入获取的noticeService接口实例的getNewNotice方法由服务层去具体实现根据传入的页码和每页记录数获取最新公告信息的详细业务逻辑比如按照发布时间等条件筛选出最新的公告并进行分页处理等最后将查询结果封装到Result对象中返回给客户端
}
}
}

@ -1,116 +1,175 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“试题管理”功能模块相关的包,方便代码管理与维护
import cn.org.alan.exam.common.group.QuestionGroup;
// 引入QuestionGroup类可能是用于对试题相关操作进行分组验证的类比如针对试题添加、修改等不同操作按照特定规则进行分组验证确保数据符合相应业务场景下的合法性要求
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以一致的格式返回给调用者如前端
import cn.org.alan.exam.model.form.question.QuestionFrom;
// 引入QuestionFrom类这是一个数据模型类用于承载添加、修改等试题相关操作时前端传递过来的试题信息例如试题内容、答案、分值等具体的数据内容
import cn.org.alan.exam.model.vo.QuestionVO;
// 引入QuestionVO类大概率是视图对象Value ObjectVO用于向客户端展示特定格式的试题相关信息经过业务逻辑处理后以适合展示的形式传递给前端
import cn.org.alan.exam.service.IQuestionService;
// 引入IQuestionService接口定义了与试题管理相关的一系列业务方法例如试题的添加、删除、查询、修改以及导入等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类会依赖这个接口来调用相应功能
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询试题相关信息的业务逻辑时用于承载分页后的试题数据等内容
import jakarta.annotation.Resource;
// 用于进行资源注入的注解在这里的作用是将实现了IQuestionService接口的具体实例注入到当前的QuestionController类中使得类中的方法可以方便地调用对应的服务方法实现具体业务逻辑
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,根据配置的权限表达式判断当前用户是否有相应权限来访问对应的方法,以此保证系统资源只能被授权用户访问,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated;
// 结合具体的验证规则(可以是默认规则或者像这里指定的分组验证规则)对传入方法的参数进行数据合法性验证,保证接收到的参数符合业务要求,避免非法数据进入业务逻辑处理流程
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口
import org.springframework.web.multipart.MultipartFile;
// 引入MultipartFile类用于处理文件上传相关操作在这里主要用于接收前端上传的Excel文件批量导入试题时以及图片文件上传图片操作时等情况
/**
*
*
*
* @author WeiJin
* @since 2024-03-21
*/
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/questions")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/questions开头便于统一管理和组织与试题管理相关的一组API接口路径
public class QuestionController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IQuestionService接口的实例到当前类中方便后续的方法调用该服务实例所提供的各种试题管理相关业务方法
private IQuestionService iQuestionService;
/**
*
* @param questionFrom
* @return
* @param questionFrom QuestionFromQuestionGroup.QuestionAddGroup.class
* @return Result
*/
@PostMapping("/single")
// 该注解表明这个方法用于处理HTTP POST请求其请求路径是在类级别定义的基础路径/api/questions基础上添加/single用于区分其他试题相关操作路径POST请求常用于向服务器提交数据在此处符合向服务器提交单题添加相关信息的场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行单题添加的操作,加强了对试题添加功能的权限管理
public Result<String> addSingleQuestion(@Validated(QuestionGroup.QuestionAddGroup.class) @RequestBody QuestionFrom questionFrom) {
// @Validated(QuestionGroup.QuestionAddGroup.class)结合@RequestBody注解@RequestBody表示从请求的正文中获取数据并将其转换为QuestionFrom类型的对象@Validated按照指定的分组验证规则对这个转换后的对象进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的单题添加业务逻辑处理
return iQuestionService.addSingleQuestion(questionFrom);
// 方法体内部直接调用通过依赖注入获取的iQuestionService接口实例的addSingleQuestion方法并将经过验证的questionFrom参数传递进去由服务层去具体实现添加单题的详细业务逻辑比如将试题信息保存到数据库中最后将服务层返回的包含添加结果等信息的Result对象直接返回给客户端
}
/**
*
* @param ids id
* @return
* @param ids idID
* @return Result
*/
@DeleteMapping("/batch/{ids}")
// 该注解表明这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/questions基础上添加/batch/{ids},其中{ids}是路径变量用于接收要批量删除的试题的相关标识信息DELETE请求常用于删除资源这里符合批量删除指定试题的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行批量删除试题的操作,保障了试题删除功能的权限安全性
public Result<String> deleteBatchQuestion(@PathVariable("ids") String ids) {
// 通过@PathVariable注解获取路径中传入的要批量删除的试题相关标识参数将其传递给服务层以便服务层依据这些信息执行批量删除对应的试题记录的业务逻辑
return iQuestionService.deleteBatchByIds(ids);
// 调用iQuestionService接口的deleteBatchByIds方法由服务层去具体实现根据传入的试题标识批量删除试题的详细业务逻辑比如从数据库中移除对应的多条试题记录等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @param content
* @param repoId id
* @param type
* @return
* @param pageNum pageNum11
* @param pageSize 1010
* @param content
* @param repoId idID
* @param type 便
* @return ResultIPage<QuestionVO>IPageResult
*/
@GetMapping("/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/questions基础上添加/paging即/api/questions/pagingGET请求常用于获取资源这里用于获取分页后的试题列表信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行分页查询试题的操作,保障了该功能的权限安全性
public Result<IPage<QuestionVO>> pagingQuestion(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "content", required = false) String content,
@RequestParam(value = "repoId", required = false) Integer repoId,
@RequestParam(value = "type", required = false) Integer type) {
// 通过@RequestParam注解获取请求中的pageNum参数设置其为可选参数required = false若客户端未传入则使用默认值1方便客户端灵活指定要查询的页码同样获取pageSize参数默认值为10获取content、repoId、type参数分别用于按试题名、题库ID、试题类型进行筛选查询
return iQuestionService.pagingQuestion(pageNum, pageSize, content, type, repoId);
// 调用通过依赖注入获取的iQuestionService接口实例的pagingQuestion方法由服务层去具体实现根据传入的页码、每页记录数以及各种筛选条件等信息进行分页查询试题的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
* id
* @param id id
* @return
* @param id idID
* @return Result
*/
@GetMapping("/single/{id}")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/questions基础上添加/single/{id},其中{id}是路径变量用于接收要获取详情的试题的IDGET请求常用于获取资源这里用于获取特定试题的详情信息资源符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行获取单题详情的操作,保障了该功能的权限安全性
public Result<QuestionVO> querySingle(@PathVariable("id") Integer id) {
// 通过@PathVariable注解获取路径中传入的试题ID参数将其传递给服务层以便服务层依据该ID准确找到对应的试题记录并获取其详细信息
return iQuestionService.querySingle(id);
// 调用iQuestionService接口的querySingle方法由服务层去具体实现根据传入的试题ID获取其详细信息的详细业务逻辑比如从数据库中查询对应试题记录的各个字段信息等最后将获取到的试题详情信息封装到Result对象中返回给客户端
}
/**
*
* @param id Id
* @param questionFrom
* @return
* @param id IdID
* @param questionFrom QuestionFrom
* @return Result
*/
@PutMapping("/{id}")
// 此注解指定这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/questions基础上添加/{id},其中{id}是路径变量用于接收要修改的试题的IDPUT请求常用于更新资源这里符合对指定ID的试题进行修改更新的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行修改试题的操作,保障了试题修改功能的权限安全性
public Result<String> updateQuestion(@PathVariable("id") Integer id, @RequestBody QuestionFrom questionFrom) {
// 通过@PathVariable注解获取路径中传入的试题ID参数将其赋值给变量id@RequestBody表示从请求正文中获取数据并转换为QuestionFrom类型的对象用于接收前端传来的修改试题的相关信息然后将试题ID设置到questionFrom对象中确保修改操作能准确对应到相应试题记录
questionFrom.setId(id);
return iQuestionService.updateQuestion(questionFrom);
// 调用iQuestionService接口的updateQuestion方法由服务层去具体实现根据传入的更新信息对指定ID的试题进行修改的详细业务逻辑比如更新数据库中对应试题记录的字段值等最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param id Id
* @param file Excel
* @return
* @param id IdID便
* @param file ExcelMultipartFileExcel
* @return Result
*/
@PostMapping("/import/{id}")
// 表明这个方法用于处理HTTP POST请求其请求路径是在类级别定义的基础路径/api/questions基础上添加/import/{id},其中{id}是路径变量用于接收要导入试题的题库IDPOST请求常用于向服务器提交数据在此处符合向服务器提交批量导入试题相关信息包含Excel文件及题库ID的场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行批量导入试题的操作,保障了试题导入功能的权限管理
public Result<String> importQuestion(@PathVariable("id") Integer id, @RequestParam("file") MultipartFile file) {
// 通过@PathVariable注解获取路径中传入的题库ID参数将其赋值给变量id通过@RequestParam("file")注解获取前端上传的文件将其赋值给变量file以便服务层依据这些信息进行批量导入试题到指定题库的业务逻辑处理
return iQuestionService.importQuestion(id,file);
// 调用iQuestionService接口的importQuestion方法由服务层去具体实现根据传入的题库ID和上传的Excel文件解析并批量导入试题到对应题库的详细业务逻辑比如读取文件内容、转换数据格式、插入数据库等操作最后将操作结果封装到Result对象中返回给客户端
}
/**
*
* @param file
* @return
* @param file MultipartFile
* @return Result便访
*/
@PostMapping("/uploadImage")
// 该注解表明这个方法用于处理HTTP POST请求其请求路径是
// @PreAuthorize注解用于在方法级别进行权限控制。
// 此处配置的"hasAnyAuthority('role_teacher','role_admin')"表示只有具备"role_teacher"(教师角色)或者"role_admin"管理员角色权限的用户才能够访问这个uploadImage方法。
// 通过这种方式保障了该图片上传功能只能被授权的特定角色用户操作,增强了系统的安全性和权限管理。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 这个方法用于处理图片上传的业务逻辑,接收前端上传的图片文件,最终返回图片上传后的相关结果信息。
// 方法返回值类型为Result<String>Result通常用于统一封装业务操作后的结果信息这里的<String>表示结果中可能包含的具体数据是字符串类型,比如可能是上传后图片的存储地址等相关信息。
// @RequestPart("file")注解用于获取前端以multipart/form-data格式上传的文件部分名称为"file"的文件将其绑定到MultipartFile类型的参数file上以便后续在方法中进行处理比如将图片保存到服务器指定位置等操作。
public Result<String> uploadImage(@RequestPart("file") MultipartFile file){
// 方法内部直接调用通过依赖注入获取的iQuestionService接口实例的uploadImage方法并将接收到的MultipartFile类型的file参数传递进去。
// 由服务层也就是iQuestionService对应的具体实现类去具体实现图片上传的详细业务逻辑比如把图片存储到服务器磁盘的某个位置、生成对应的访问地址等操作最后将服务层返回的包含图片上传结果以及相关信息如上传后的地址等的Result对象直接返回给客户端。
return iQuestionService.uploadImage(file);
}
}

@ -1,81 +1,130 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“考试记录”功能模块相关的包,方便代码管理和维护
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以标准格式返回给调用者比如前端应用
import cn.org.alan.exam.model.vo.record.ExamRecordDetailVO;
// 引入ExamRecordDetailVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的考试记录详细信息比如包含考试中每道题的答题情况、得分等详细内容方便前端展示和使用
import cn.org.alan.exam.model.vo.record.ExamRecordVO;
// 引入ExamRecordVO类同样是视图对象类用于展示考试记录的基本信息例如考试名称、考试时间、考生信息等内容以一种适合前端展示的形式传递数据
import cn.org.alan.exam.model.vo.record.ExerciseRecordDetailVO;
// 引入ExerciseRecordDetailVO类也是视图对象类用于呈现刷题记录的详细信息像刷题过程中每题的作答情况、所用时间等具体的细节信息便于前端展示给用户查看
import cn.org.alan.exam.model.vo.record.ExerciseRecordVO;
// 引入ExerciseRecordVO类用于向客户端展示刷题记录的基本信息例如刷题的题库名称、刷题时长、刷题的用户等相关基本情况的信息
import cn.org.alan.exam.service.IExerciseRecordService;
// 引入IExerciseRecordService接口定义了与考试记录、刷题记录相关的一系列业务方法像记录的分页查询、详情查询等操作的逻辑抽象具体的实现由对应的服务类来完成本控制器类依赖此接口调用相应功能
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询考试记录、刷题记录相关信息的业务逻辑中用于承载分页后的记录数据等内容
import jakarta.annotation.Resource;
// 用于进行资源注入的注解作用是将实现了IExerciseRecordService接口的具体实例注入到当前的RecordController类中使得类中的方法可以便捷地调用对应的服务方法来实现具体业务逻辑
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,依据配置的权限表达式判断当前用户是否具备相应权限来访问对应的方法,以此保障系统资源只能被授权用户操作,增强系统的安全性和权限管理
import org.springframework.web.bind.annotation.GetMapping;
// 这是Spring Web相关的注解用于标识该方法处理HTTP GET请求GET请求常用于获取资源在这里符合获取考试记录、刷题记录相关信息资源的操作场景
import org.springframework.web.bind.annotation.RequestMapping;
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/records开头便于统一管理和组织与考试记录相关的一组API接口路径
import org.springframework.web.bind.annotation.RequestParam;
// 用于获取请求中的参数,通过指定参数名、是否必填以及默认值等信息,将请求中的对应参数绑定到方法的参数上,方便后续业务逻辑使用这些参数进行相应处理
import org.springframework.web.bind.annotation.RestController;
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如查询到的多条考试记录详情等情况时会用到
/**
*
*
*
* @Author Alan
* @Version
* @Date 2024/3/25 11:22 AM
*/
@RestController
// 标注此类为Spring MVC中的控制器类且方法返回值默认以JSON等格式响应给客户端方便与前端交互
@RequestMapping("/api/records")
// 为该控制器类下的所有请求处理方法设置公共的请求路径前缀,后续具体方法的路径在此基础上扩展
public class RecordController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IExerciseRecordService接口的实例到当前类中方便后续调用相关业务方法
private IExerciseRecordService exerciseRecordService;
/**
*
* @param pageNum
* @param pageSize
* @return
* @param pageNumpageNum11
* @param pageSize1010
* @param examName
* @return ResultIPage<ExamRecordVO>IPageResult
*/
@GetMapping("/exam/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/records基础上添加/exam/paging即/api/records/exam/paging用于获取已考试试卷的分页记录信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户都有权利执行分页查询已考试试卷记录的操作
public Result<IPage<ExamRecordVO>> getExamRecordPage(@RequestParam(value = "pageNum",required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "examName", required = false) String examName){
// 通过@RequestParam注解分别获取请求中的pageNum、pageSize、examName参数若客户端未传入pageNum和pageSize则分别使用默认值1和10examName用于筛选查询条件
return exerciseRecordService.getExamRecordPage(pageNum,pageSize,examName);
// 调用通过依赖注入获取的exerciseRecordService接口实例的getExamRecordPage方法由服务层去具体实现根据传入的页码、每页记录数以及考试名称筛选条件等信息进行分页查询已考试试卷记录的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
*
* @param examId
* @return
* @param examIdID
* @return ResultList<ExamRecordDetailVO>
*/
@GetMapping("/exam/detail")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/records基础上添加/exam/detail即/api/records/exam/detail用于获取特定试卷的详细信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户都有权利执行查询试卷详情的操作
public Result<List<ExamRecordDetailVO>> getExamRecordDetail(@RequestParam("examId") Integer examId){
// 通过@RequestParam注解获取请求中名为"examId"的参数将其赋值给examId变量以便服务层依据该试卷ID准确找到对应的试卷记录并获取其详细信息
return exerciseRecordService.getExamRecordDetail(examId);
// 调用exerciseRecordService接口的getExamRecordDetail方法由服务层去具体实现根据传入的试卷ID获取其详细信息包含每题答题情况等的详细业务逻辑最后将获取到的试卷详情信息封装到Result对象中返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @return
* @param pageNum1
* @param pageSize1010
* @param repoName
* @return ResultIPage<ExerciseRecordVO>IPageResult
*/
@GetMapping("/exercise/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/records基础上添加/exercise/paging即/api/records/exercise/paging用于获取已考试刷题记录的分页信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户都有权利执行分页查询已考试刷题记录的操作
public Result<IPage<ExerciseRecordVO>> getExerciseRecordPage(@RequestParam(value = "pageNum",required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "repoName", required = false) String repoName){
// 通过@RequestParam注解获取请求中的pageNum、pageSize、repoName参数若客户端未传入pageNum和pageSize则分别使用默认值1和10repoName用于按刷题的题库名称进行筛选查询
return exerciseRecordService.getExerciseRecordPage(pageNum,pageSize,repoName);
// 调用exerciseRecordService接口的实例的getExerciseRecordPage方法由服务层去具体实现根据传入的页码、每页记录数以及题库名称筛选条件等信息进行分页查询已考试刷题记录的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
*
* @param exerciseId
* @return
* @param exerciseIdIDID
* @return ResultList<ExerciseRecordDetailVO>
*/
@GetMapping("/exercise/detail")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/records基础上添加/exercise/detail即/api/records/exercise/detail用于获取特定刷题记录的详细信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户都有权利执行查询刷题详情的操作
public Result<List<ExerciseRecordDetailVO>> getExerciseRecordDetail(@RequestParam("repoId") Integer exerciseId){
// 通过@RequestParam注解获取请求中名为"repoId"的参数此处参数名可能不太准确也许应该和前面定义的exerciseId参数名统一便于理解和维护将其赋值给exerciseId变量以便服务层依据该ID准确找到对应的刷题记录并获取其详细信息
return exerciseRecordService.getExerciseRecordDetail(exerciseId);
// 调用exerciseRecordService接口的getExerciseRecordDetail方法由服务层去具体实现根据传入的刷题记录ID获取其详细信息包含每题答题情况等的详细业务逻辑最后将获取到的刷题详情信息封装到Result对象中返回给客户端
}
}
}

@ -1,96 +1,140 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“题库管理”功能模块相关的包,方便代码管理与维护
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以一致的格式返回给调用者如前端
import cn.org.alan.exam.model.entity.Repo;
// 引入Repo类这是一个实体类对应数据库中存储题库相关信息的表结构包含了如题库的各种属性例如题库名称、创建人、创建时间等信息用于在业务逻辑中承载和传递与题库相关的数据
import cn.org.alan.exam.model.vo.repo.RepoListVO;
// 引入RepoListVO类大概率是视图对象Value ObjectVO用于向客户端展示特定格式的题库列表相关信息例如可能只包含题库ID和题库名称等关键信息方便前端展示和使用
import cn.org.alan.exam.model.vo.repo.RepoVO;
// 引入RepoVO类同样是视图对象类用于向客户端展示更详细的题库相关信息经过业务逻辑处理后以适合展示的形式传递给前端包含的信息可能比RepoListVO更丰富全面
import cn.org.alan.exam.service.IRepoService;
// 引入IRepoService接口定义了与题库管理相关的一系列业务方法例如题库的添加、修改、删除、查询等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类会依赖这个接口来调用相应功能
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询题库相关信息的业务逻辑时用于承载分页后的题库数据等内容
import jakarta.annotation.Resource;
// 用于进行资源注入的注解在这里的作用是将实现了IRepoService接口的具体实例注入到当前的RepoController类中使得类中的方法可以方便地调用对应的服务方法实现具体业务逻辑
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,根据配置的权限表达式判断当前用户是否有相应权限来访问对应的方法,以此保证系统资源只能被授权用户访问,增强系统的安全性和权限管理
import org.springframework.validation.annotation.Validated;
// 结合具体的验证规则(可以是默认规则或者自定义规则)对传入方法的参数进行数据合法性验证,保证接收到的参数符合业务要求,避免非法数据进入业务逻辑处理流程
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个题库的信息列表等情况时会用到
/**
*
*
*
* @author WeiJin
* @since 2024-03-21
*/
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/repo")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/repo开头便于统一管理和组织与题库管理相关的一组API接口路径
public class RepoController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IRepoService接口的实例到当前类中方便后续的方法调用该服务实例所提供的各种题库管理相关业务方法
private IRepoService iRepoService;
/**
*
*
* @param repo
* @return
* @param repo Repo
* @return Result
*/
@PostMapping
// 该注解表明这个方法用于处理HTTP POST请求其请求路径就是类级别定义的基础路径/api/repo因为这里没有额外指定路径POST请求常用于向服务器提交数据在此处符合向服务器提交添加题库相关信息的场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"(教师角色)或者"role_admin"(管理员角色)权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行添加题库的操作,加强了对题库添加功能的权限管理
public Result<String> addRepo(@Validated @RequestBody Repo repo) {
//从token获取用户id放入创建人id属性
// @Validated注解结合@RequestBody注解@RequestBody表示从请求的正文中获取数据并将其转换为Repo类型的对象@Validated用于对这个转换后的对象依据相关规则进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的添加题库业务逻辑处理
//从token获取用户id放入创建人id属性此处应该是在服务层或者更底层的代码逻辑中实现从请求携带的token通常用于用户认证授权里解析出当前操作的用户ID并将其设置到传入的repo对象的创建人ID属性上确保添加的题库能关联到正确的创建人
return iRepoService.addRepo(repo);
// 方法体内部直接调用通过依赖注入获取的iRepoService接口实例的addRepo方法并将经过验证且设置好创建人ID的repo参数传递进去由服务层去具体实现添加题库的详细业务逻辑比如将题库信息保存到数据库中最后将服务层返回的包含添加结果等信息的Result对象直接返回给客户端
}
/**
*
*
* @param repo
* @return
* @param repo Repo
* @return Result
*/
@PutMapping("/{id}")
// 此注解指定这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/repo基础上添加/{id},其中{id}是路径变量用于接收要修改的题库的IDPUT请求常用于更新资源这里符合对指定ID的题库进行修改更新的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行修改题库的操作,保障了题库修改功能的权限安全性
public Result<String> updateRepo(@Validated @RequestBody Repo repo, @PathVariable("id") Integer id) {
// @Validated结合@RequestBody对repo参数进行数据合法性验证及从请求正文获取数据转换为对应对象@PathVariable注解用于获取路径中的题库ID参数将其赋值给变量id以便服务层依据该ID准确找到对应的题库记录进行修改操作
return iRepoService.updateRepo(repo, id);
// 调用iRepoService接口的updateRepo方法由服务层去具体实现根据传入的更新信息对指定ID的题库进行修改的详细业务逻辑比如更新数据库中对应题库记录的字段值等最后将操作结果封装到Result对象中返回给客户端
}
/**
* id
*
* @param id id
* @return
* @param id idID
* @return Result
*/
@DeleteMapping("/{id}")
// 该注解表明这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/repo基础上添加/{id},其中{id}是路径变量用于接收要删除的题库的IDDELETE请求常用于删除资源这里符合删除指定题库的操作场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,确保只有教师或管理员角色的用户可以执行删除题库的操作,保障了题库删除功能的权限安全性
public Result<String> deleteRepoById(@PathVariable("id") Integer id) {
// 通过@PathVariable注解获取路径中传入的题库ID参数将其传递给服务层以便服务层依据这个ID准确找到并删除对应的题库记录
return iRepoService.deleteRepoById(id);
// 调用iRepoService接口的deleteRepoById方法由服务层去具体实现删除指定ID的题库的详细业务逻辑比如从数据库中移除对应的题库记录等最后将操作结果封装到Result对象中返回给客户端
}
/**
* id
* @param repoTitle
* @return
* @param repoTitle
* @return ResultIDList<RepoListVO>
*/
@GetMapping("/list")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/repo基础上添加/list即/api/repo/listGET请求常用于获取资源这里用于获取题库ID和名称列表信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法意味着教师和管理员角色的用户可以执行获取题库ID和名称列表的操作保障了该功能的权限安全性
public Result<List<RepoListVO>> getRepoList(@RequestParam(value = "repoTitle",required = false) String repoTitle) {
// 通过@RequestParam注解获取请求中的repoTitle参数设置其为可选参数required = false方便客户端根据实际需求决定是否传入此参数进行筛选查询
return iRepoService.getRepoList(repoTitle);
// 调用iRepoService接口的getRepoList方法由服务层去具体实现根据传入的题库名称可选获取相应的题库ID和名称列表信息的详细业务逻辑比如按照教师或管理员权限进行不同的数据筛选等最后将获取到的列表数据封装到Result对象中返回给客户端
}
/**
*
*
* @param pageNum
* @param pageSize
* @param title
* @return
* @param pageNum pageNum11
* @param pageSize 1010
* @param title
* @return ResultIPage<RepoVO>IPageResult
*/
@GetMapping("/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/repo基础上添加/paging即/api/repo/pagingGET请求常用于获取资源这里用于获取分页后的题库列表信息资源的操作符合GET请求的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户可以执行分页查询题库的操作,保障了该功能的权限安全性
public Result<IPage<RepoVO>> pagingRepo(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "title", required = false) String title) {
// 通过@RequestParam注解获取请求中的pageNum参数设置其为可选参数required = false若客户端未传入则使用默认值1方便客户端灵活指定要查询的页码同样获取pageSize参数默认值为10获取title参数用于根据题库名称筛选查询
return iRepoService.pagingRepo(pageNum, pageSize, title);
// 调用iRepoService接口的pagingRepo方法由服务层去具体实现根据传入的页码、每页记录数以及筛选条件等信息进行分页查询题库的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
}
}

@ -1,99 +1,144 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“成绩管理”功能模块相关的包,方便代码管理与维护
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以标准格式返回给调用者比如前端应用
import cn.org.alan.exam.model.vo.score.GradeScoreVO;
// 引入GradeScoreVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的班级成绩相关分析信息比如班级整体考试成绩的平均分、各分数段人数分布等内容方便前端展示和使用
import cn.org.alan.exam.model.vo.score.QuestionAnalyseVO;
// 引入QuestionAnalyseVO类同样是视图对象类用于呈现某道试题在考试中的作答情况分析信息例如该题的正确率、各选项选择人数等具体的分析数据便于前端展示给教师等查看分析试题情况
import cn.org.alan.exam.model.vo.score.UserScoreVO;
// 引入UserScoreVO类也是视图对象类用于向客户端展示学生个人的考试成绩相关信息像学生的姓名、考试得分、排名等具体的成绩情况内容
import cn.org.alan.exam.service.IStatService;
// 引入IStatService接口可能定义了一些与成绩统计相关的通用业务方法或者其他辅助性的统计逻辑操作不过在此控制器类中暂时未看到直接调用它的地方也许在其他相关类中会被调用协作实现功能
import cn.org.alan.exam.service.IExamQuAnswerService;
// 引入IExamQuAnswerService接口定义了与考试中试题作答情况分析相关的一系列业务方法例如获取某题的作答详情分析等操作的逻辑抽象具体的实现由对应的服务类来完成本控制器类会依赖此接口调用相应功能
import cn.org.alan.exam.service.IUserExamsScoreService;
// 引入IUserExamsScoreService接口定义了与用户考试成绩查询、成绩信息分页获取以及成绩导出等相关的一系列业务方法具体的实现由对应的服务类来完成该控制器类中的多个方法依赖此接口调用相应功能
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询成绩相关信息的业务逻辑中用于承载分页后的成绩数据等内容
import jakarta.annotation.Resource;
// 用于进行资源注入的注解作用是将实现了相关服务接口如IStatService、IUserExamsScoreService、IExamQuAnswerService的具体实例注入到当前的ScoreController类中使得类中的方法可以便捷地调用对应的服务方法来实现具体业务逻辑
import jakarta.servlet.http.HttpServletResponse;
// 引入HttpServletResponse类它代表了服务器对客户端的HTTP响应对象在成绩导出功能中会使用到用于设置响应的相关属性比如响应头信息、输出流等以便将成绩数据以合适的格式如Excel等返回给客户端进行下载
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,依据配置的权限表达式判断当前用户是否具备相应权限来访问对应的方法,以此保障系统资源只能被授权用户操作,增强系统的安全性和权限管理
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口
/**
*
*
*
* @Author WeiJin
* @Version
* @Date 2024/3/25 11:19 AM
*/
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
@RequestMapping("/api/score")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/score开头便于统一管理和组织与成绩管理相关的一组API接口路径
public class ScoreController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入实现了IStatService接口的具体实例到当前类中虽然当前类中暂时未看到直接调用它的地方但可能在后续业务逻辑扩展或者其他相关功能实现中会用到
private IStatService iStatService;
@Resource
// 同样使用@Resource注解注入实现了IUserExamsScoreService接口的实例后续多个与成绩查询、成绩分析、成绩导出等相关的方法会依赖这个服务实例来调用具体业务逻辑方法
private IUserExamsScoreService iUserExamsScoreService;
@Resource
// 注入实现了IExamQuAnswerService接口的实例用于调用与试题作答情况分析相关的业务方法比如获取某题的作答详情分析等操作
private IExamQuAnswerService iExamQuAnswerService;
/**
*
* @param pageNum
* @param pageSize
* @param gradeId Id
* @param examId Id
* @param realName
* @return
* @param pageNumpageNum11
* @param pageSize1010
* @param gradeIdID便
* @param examIdID便
* @param realName
* @return ResultIPage<UserScoreVO>IPageResult
*/
@GetMapping("/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/score基础上添加/paging即/api/score/paging用于获取分页后的成绩信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"(教师角色)或者"role_admin"(管理员角色)权限的用户都可以访问这个方法,意味着教师和管理员角色的用户有权利执行分页查询成绩信息的操作
public Result<IPage<UserScoreVO>> pagingScore(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "gradeId") Integer gradeId,
@RequestParam(value = "examId") Integer examId,
@RequestParam(value = "realName", required = false) String realName) {
// 通过@RequestParam注解分别获取请求中的pageNum、pageSize、gradeId、examId、realName参数若客户端未传入pageNum和pageSize则分别使用默认值1和10gradeId、examId用于筛选特定班级和考试的成绩realName用于按学生姓名进一步筛选
return iUserExamsScoreService.pagingScore(pageNum, pageSize, gradeId, examId, realName);
// 调用通过依赖注入获取的iUserExamsScoreService接口实例的pagingScore方法由服务层去具体实现根据传入的页码、每页记录数以及班级ID、考试ID、学生姓名等筛选条件进行分页查询成绩信息的详细业务逻辑最后将查询结果封装到Result对象中返回给客户端
}
/**
*
* @param examId id
* @param questionId id
* @return
* @param examIdID
* @param questionIdIDexamId
* @return ResultQuestionAnalyseVO
*/
@GetMapping("/question/{examId}/{questionId}")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/score基础上添加/question/{examId}/{questionId},其中{examId}和{questionId}是路径变量用于接收要分析的考试和试题的ID符合GET请求获取资源这里是获取试题作答情况资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户有权利执行获取试题作答情况分析的操作
public Result<QuestionAnalyseVO> questionAnalyse(@PathVariable("examId") Integer examId,
@PathVariable("questionId") Integer questionId) {
// 通过@PathVariable注解分别获取路径中的考试ID和试题ID参数将其赋值给对应的examId和questionId变量以便服务层依据这两个ID准确找到对应的考试和试题记录进而获取试题的作答情况分析信息
return iExamQuAnswerService.questionAnalyse(examId, questionId);
// 调用iExamQuAnswerService接口的questionAnalyse方法由服务层去具体实现根据传入的考试ID和试题ID获取该试题在对应考试中的作答情况分析比如各选项选择人数、正确率等的详细业务逻辑最后将获取到的分析信息封装到Result对象中返回给客户端
}
/**
*
* @param pageNum
* @param pageSize
* @param examTitle
* @return
* @param pageNum1
* @param pageSize1010
* @param examTitle便
* @param gradeIdID便
* @return ResultIPage<GradeScoreVO>IPageResult
*/
@GetMapping("/getExamScore")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/score基础上添加/getExamScore即/api/score/getExamScore用于获取班级考试情况分析信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户有权利执行根据班级分析考试情况的操作
public Result<IPage<GradeScoreVO>> getExamScoreInfo(
@RequestParam(value = "pageNum",required = false,defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize",required = false,defaultValue = "10") Integer pageSize,
@RequestParam(value = "examTitle",required = false) String examTitle,
@RequestParam(value = "gradeId" ,required = false) Integer gradeId){
@RequestParam(value = "gradeId",required = false) Integer gradeId){
// 通过@RequestParam注解获取请求中的pageNum、pageSize、examTitle、gradeId参数若客户端未传入pageNum和pageSize则分别使用默认值1和10examTitle用于按考试名称筛选gradeId用于按班级筛选分析考试情况
return iUserExamsScoreService.getExamScoreInfo(pageNum,pageSize,examTitle,gradeId);
// 调用iUserExamsScoreService接口的getExamScoreInfo方法由服务层去具体实现根据传入的页码、每页记录数以及考试名称、班级ID等筛选条件进行分页查询并分析班级考试情况比如班级平均分、各分数段人数等的详细业务逻辑最后将查询分析结果封装到Result对象中返回给客户端
}
/**
*
* @param response
* @param examId id
* @param gradeId id
* @param responseHttpServletResponseHTTP使Excel便
* @param examIdID
* @param gradeIdID便
*/
@GetMapping("/export/{examId}/{gradeId}")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/score基础上添加/export/{examId}/{gradeId},其中{examId}和{gradeId}是路径变量用于接收要导出成绩的考试和班级的ID符合GET请求获取资源这里是获取成绩数据并导出资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着教师和管理员角色的用户有权利执行成绩导出的操作
public void scoreExport(HttpServletResponse response,@PathVariable("examId") Integer examId, @PathVariable("gradeId") Integer gradeId) {
// 通过@PathVariable注解获取路径中的考试ID和班级ID参数将其赋值给对应的examId和gradeId变量以便服务层依据这两个ID准确找到对应的考试和班级记录进而获取相关成绩数据进行导出操作
iUserExamsScoreService.exportScores(response,examId,gradeId);
// 调用iUserExamsScoreService接口的exportScores方法由服务层去具体实现根据传入的HttpServletResponse对象以及考试ID、班级ID将相应的成绩数据按照一定格式如Excel进行组织并通过响应对象返回给客户端使得客户端可以下载成绩文件的详细业务逻辑
}
}
}

@ -1,80 +1,110 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“统计管理”功能模块相关的包,方便代码管理与维护
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以标准格式返回给调用者比如前端应用
import cn.org.alan.exam.model.vo.stat.AllStatsVO;
// 引入AllStatsVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的所有相关统计信息汇总结果例如可能包含班级数量、试卷数量、试题数量等综合统计数据方便前端展示和使用
import cn.org.alan.exam.model.vo.stat.DailyVO;
// 引入DailyVO类同样是视图对象类用于呈现每日相关的统计信息具体包含的内容需看其类内部定义可能是每日新增的各类数据量、每日活跃用户数等与日常统计相关的数据便于前端展示给用户查看
import cn.org.alan.exam.model.vo.stat.GradeExamVO;
// 引入GradeExamVO类也是视图对象类用于向客户端展示各班级试卷相关的统计信息比如每个班级已创建的试卷数量、已完成考试的试卷数量等班级与试卷维度的统计情况内容
import cn.org.alan.exam.model.vo.stat.GradeStudentVO;
// 引入GradeStudentVO类用于向客户端展示各班级学生人数相关的统计信息像每个班级的学生总数、不同状态如活跃、未活跃等具体看业务定义的学生数量等班级与学生人数维度的统计数据
import cn.org.alan.exam.service.IStatService;
// 引入IStatService接口定义了与统计管理相关的一系列业务方法例如获取各班级学生人数统计、各班试卷统计、所有数据总量统计以及日常统计等操作的逻辑抽象具体的实现由对应的服务类来完成本控制器类依赖此接口调用相应功能
import jakarta.annotation.Resource;
// 用于进行资源注入的注解作用是将实现了IStatService接口的具体实例注入到当前的StatController类中使得类中的方法可以便捷地调用对应的服务方法来实现具体业务逻辑
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,依据配置的权限表达式判断当前用户是否具备相应权限来访问对应的方法,以此保障系统资源只能被授权用户操作,增强系统的安全性和权限管理
import org.springframework.web.bind.annotation.GetMapping;
// 这是Spring Web相关的注解用于标识该方法处理HTTP GET请求GET请求常用于获取资源在这里符合获取各种统计信息资源的操作场景
import org.springframework.web.bind.annotation.RequestMapping;
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/stat开头便于统一管理和组织与统计管理相关的一组API接口路径
import org.springframework.web.bind.annotation.RestController;
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个班级的统计信息列表等情况时会用到
/**
*
*
*
* @Author Alan
* @Version
* @Date 2024/3/25 11:22 AM
*/
@RestController
// 标注此类为Spring MVC中的控制器类且方法返回值默认以JSON等格式响应给客户端方便与前端交互
@RequestMapping("/api/stat")
// 为该控制器类下的所有请求处理方法设置公共的请求路径前缀,后续具体方法的路径在此基础上扩展
public class StatController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IStatService接口的实例到当前类中方便后续调用相关业务方法
private IStatService statService;
/**
*
* @return
* @return ResultList<GradeStudentVO>
*/
@GetMapping("/student")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/stat基础上添加/student即/api/stat/student用于获取各班级人数统计信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"(教师角色)或者"role_admin"(管理员角色)权限的用户都可以访问这个方法,意味着教师和管理员角色的用户有权利执行获取各班级人数统计的操作
public Result<List<GradeStudentVO>> getStudentGradeCount() {
// 方法内部直接调用通过依赖注入获取的statService接口实例的getStudentGradeCount方法由服务层去具体实现获取各班级人数统计信息的详细业务逻辑比如从数据库中查询各班级的学生数量等相关数据并封装成相应的视图对象列表最后将查询结果封装到Result对象中返回给客户端
return statService.getStudentGradeCount();
}
/**
*
* @return
* @return ResultList<GradeExamVO>
*/
@GetMapping("/exam")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/stat基础上添加/exam即/api/stat/exam用于获取各班试卷统计信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"或者"role_admin"权限的用户都可以访问这个方法,意味着教师和管理员角色的用户有权利执行获取各班试卷统计的操作
public Result<List<GradeExamVO>> getExamGradeCount() {
// 调用statService接口的getExamGradeCount方法由服务层去具体实现获取各班试卷统计信息的详细业务逻辑比如查询每个班级对应的试卷数量等相关数据并整理成相应的视图对象列表最后将结果封装到Result对象中返回给客户端
return statService.getExamGradeCount();
}
/**
*
*
* @return
* @return ResultAllStatsVO
*/
@GetMapping("/allCounts")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/stat基础上添加/allCounts即/api/stat/allCounts用于获取所有班级、试卷、试题数量的综合统计信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"或者"role_admin"权限的用户都可以访问这个方法,意味着教师和管理员角色的用户有权利执行获取整体数据数量统计的操作
public Result<AllStatsVO> getAllCount(){
// 调用statService接口的getAllCount方法由服务层去具体实现统计所有班级、试卷、试题数量的详细业务逻辑比如分别查询班级总数、试卷总数、试题总数等数据并整合到对应的视图对象中最后将其封装到Result对象中返回给客户端
return statService.getAllCount();
}
/**
* DailyVO
* @return ResultList<DailyVO>
*/
@GetMapping("/daily")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/stat基础上添加/daily即/api/stat/daily用于获取日常相关统计信息符合GET请求获取资源的使用场景
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"(教师角色)、"role_admin"(管理员角色)或者"role_student"(学生角色)权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户有权利执行获取日常统计信息的操作
public Result<List<DailyVO>> getDaily(){
// 调用statService接口的getDaily方法由服务层去具体实现获取日常相关统计信息的详细业务逻辑比如查询每日新增的各类数据量等相关数据并整理成相应的视图对象列表最后将结果封装到Result对象中返回给客户端
return statService.getDaily();
}
}
}

@ -1,76 +1,112 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“错题本管理”功能模块相关的包,方便代码管理和维护
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以标准格式返回给调用者比如前端应用便于告知客户端对应业务操作的执行情况及相关返回数据。
import cn.org.alan.exam.model.form.userbook.ReUserBookForm;
// 引入ReUserBookForm类这是一个数据模型类用于承载与错题本相关操作比如填充答案操作时前端传递过来的特定格式的数据可能包含错题的一些原始信息、要填写的答案等具体内容方便在业务逻辑中进行数据传递和处理。
import cn.org.alan.exam.model.vo.userbook.*;
// 引入多个以“userbook”命名空间下的视图对象Value ObjectVO这些类用于向客户端展示特定格式的错题本相关信息不同的VO类对应不同的业务场景比如展示错题分页信息、错题ID列表信息、单题详细信息等方便前端按照合适的格式展示数据给用户查看。
import cn.org.alan.exam.service.IUserBookService;
// 引入IUserBookService接口定义了与错题本管理相关的一系列业务方法例如错题的分页查询、错题ID列表查询、单题查询以及答案填充等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类依赖这个接口来调用相应功能。
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询错题本相关信息的业务逻辑时用于承载分页后的错题数据等内容方便对分页数据进行统一的处理和传递。
import jakarta.annotation.Resource;
// 用于进行资源注入的注解作用是将实现了IUserBookService接口的具体实例注入到当前的UserBookController类中使得类中的方法可以便捷地调用对应的服务方法来实现具体业务逻辑。
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,依据配置的权限表达式判断当前用户是否具备相应权限来访问对应的方法,以此保障系统资源只能被授权用户操作,增强系统的安全性和权限管理,这里限定了不同操作对应的有权限的角色。
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口处理来自客户端的各种请求。
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个错题的ID列表等情况时会用到便于对一组数据进行统一操作和传递。
/**
*
* ID
*
* @author Alan
* @since 2024-03-21
*/
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作方便与前端进行数据交互。
@RequestMapping("/api/userbooks")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/userbooks开头便于统一管理和组织与错题本管理相关的一组API接口路径。
public class UserBookController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IUserBookService接口的实例到当前类中方便后续调用相关业务方法使得业务逻辑能够顺利执行。
private IUserBookService userBookService;
/**
*
* @param pageNum
* @param pageSize
* @param examName
* @return
* @param pageNumpageNum11便
* @param pageSize1010
* @param examName便
* @return ResultIPage<UserPageBookVO>IPageResult便使
*/
@GetMapping("/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/userbooks基础上添加/paging即/api/userbooks/paging用于获取分页后的错题考试信息符合GET请求获取资源的使用场景便于客户端发起获取错题数据的请求。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"(教师角色)、"role_admin"(管理员角色)或者"role_student"(学生角色)权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户有权利执行分页查询错题考试的操作,确保有权限的用户才能获取相关数据。
public Result<IPage<UserPageBookVO>> getPage(@RequestParam(value = "pageNum",required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize",required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "examName",required = false) String examName){
// 通过@RequestParam注解分别获取请求中的pageNum、pageSize、examName参数若客户端未传入pageNum和pageSize则分别使用默认值1和10examName用于筛选查询条件将获取到的参数传递给服务层进行后续处理。
return userBookService.getPage(pageNum,pageSize,examName);
// 调用通过依赖注入获取的userBookService接口实例的getPage方法由服务层去具体实现根据传入的页码、每页记录数以及考试名称筛选条件等信息进行分页查询错题考试信息的详细业务逻辑比如从数据库中查询符合条件的错题数据并进行分页处理最后将查询结果封装到Result对象中返回给客户端。
}
/**
* id
* @param examId
* @return
* @param examIdIDID便便
* @return ResultIDIDList<ReUserExamBookVO>便ID
*/
@GetMapping("/question/list/{examId}")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/userbooks基础上添加/question/list/{examId},其中{examId}是路径变量用于接收要查询错题ID列表对应的考试的ID符合GET请求获取资源这里是获取错题ID列表资源的使用场景便于客户端根据考试ID来请求获取错题ID。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法意味着教师、管理员和学生角色的用户有权利执行查询错题本错题ID列表的操作确保相应权限的用户才能获取该数据。
public Result<List<ReUserExamBookVO>> getReUserExamBook(@PathVariable("examId") Integer examId){
// 通过@PathVariable注解获取路径中传入的考试ID参数将其赋值给examId变量以便服务层依据该ID准确找到对应的考试记录进而获取该考试下错题本中的错题ID列表信息。
return userBookService.getReUserExamBook(examId);
// 调用userBookService接口的getReUserExamBook方法由服务层去具体实现根据传入的考试ID获取错题本中错题ID列表的详细业务逻辑比如从数据库中查询对应考试下的错题ID并整理成列表最后将获取到的列表数据封装到Result对象中返回给客户端。
}
/**
*
* @param quId
* @return
* @param quIdID便
* @return ResultBookOneQuVO便
*/
@GetMapping("/question/single/{quId}")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/userbooks基础上添加/question/single/{quId},其中{quId}是路径变量用于接收要查询的错题的ID符合GET请求获取资源这里是获取单题详细信息资源的使用场景便于客户端根据错题ID请求获取单题详情。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户有权利执行查询单题的操作,确保相应权限的用户才能获取单题详情数据。
public Result<BookOneQuVO> getBookOne(@PathVariable("quId") Integer quId){
// 通过@PathVariable注解获取路径中传入的错题ID参数将其赋值给quId变量以便服务层依据该ID准确找到对应的错题记录进而获取其详细信息。
return userBookService.getBookOne(quId);
// 调用userBookService接口的getBookOne方法由服务层去具体实现根据传入的错题ID获取该错题详细信息的详细业务逻辑比如从数据库中查询对应错题的各项详细数据并封装到对应的视图对象中最后将获取到的单题详情信息封装到Result对象中返回给客户端。
}
/**
*
* @param reUserBookForm
* @return
* @param reUserBookFormReUserBookFormID便
* @return ResultAddBookAnswerVO便
*/
@PostMapping("/full-book")
// 该注解表明这个方法用于处理HTTP POST请求其请求路径是在类的基础路径/api/userbooks基础上添加/full-book用于区分其他错题本相关操作路径POST请求常用于向服务器提交数据在此处符合向服务器提交填充错题答案相关信息的场景便于客户端将填写的答案数据发送给服务器。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin','role_student')")
// 进行权限控制,拥有"role_teacher"、"role_admin"或者"role_student"权限的用户都可以访问这个方法,意味着教师、管理员和学生角色的用户有权利执行填充错题答案的操作,确保有权限的用户才能进行该操作。
public Result<AddBookAnswerVO> addBookAnswer(@RequestBody ReUserBookForm reUserBookForm){
// @RequestBody注解表示从请求的正文中获取数据并将其转换为ReUserBookForm类型的对象以便获取前端传递过来的填充答案相关的完整数据信息传递给服务层进行后续处理。
return userBookService.addBookAnswer(reUserBookForm);
// 调用userBookService接口的addBookAnswer方法由服务层去具体实现根据传入的填充答案相关信息进行答案填充的详细业务逻辑比如更新数据库中错题的答案字段等操作最后将操作结果以及可能的相关反馈数据封装到Result对象中返回给客户端。
}
}
}

@ -1,136 +1,193 @@
package cn.org.alan.exam.controller;
// 声明该类所在的包名,用于在项目中对类进行分类组织,表明此控制器类属于特定的“用户管理”功能模块相关的包,方便代码管理与维护
import cn.org.alan.exam.common.result.Result;
// 引入Result类通常用于统一封装业务操作后的结果信息包含操作是否成功的标识以及可能需要返回的数据等内容以标准格式返回给调用者比如前端应用方便告知客户端业务操作的执行情况及对应返回数据情况。
import cn.org.alan.exam.common.group.UserGroup;
// 引入UserGroup类大概率是用于对用户相关操作进行分组验证的类比如针对用户创建、密码修改等不同操作按照特定规则进行分组验证确保传入的数据符合相应业务场景下的合法性要求增强数据校验的准确性和针对性。
import cn.org.alan.exam.model.form.UserForm;
// 引入UserForm类这是一个数据模型类用于承载用户相关操作如创建用户、修改密码等操作时前端传递过来的用户信息像用户名、真实姓名、角色ID等具体的数据内容方便在业务逻辑中传递和处理这些数据。
import cn.org.alan.exam.model.vo.UserVO;
// 引入UserVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的用户相关信息例如用户的基本信息、权限信息等经过整理和格式化后适合前端展示的数据便于前端展示给用户查看。
import cn.org.alan.exam.service.IUserService;
// 引入IUserService接口定义了与用户管理相关的一系列业务方法例如获取用户登录信息、创建用户、修改密码、删除用户、分页查询用户信息等操作的逻辑抽象具体的实现由对应的服务类来完成该控制器类依赖这个接口来调用相应功能。
import cn.org.alan.exam.util.AliOSSUtil;
// 引入AliOSSUtil类从名称推测可能是用于与阿里云对象存储服务OSS进行交互的工具类也许在用户上传头像等涉及文件存储的操作中会使用到方便进行文件的上传、管理等相关操作。
import cn.org.alan.exam.util.SecurityUtil;
// 引入SecurityUtil类应该是与安全相关的工具类可能用于处理用户认证、授权或者密码加密等安全相关的逻辑辅助保障系统的安全性在获取用户登录信息、修改密码等操作中或许会发挥作用。
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入IPage类这是MyBatis Plus框架提供的用于表示分页数据的类型在涉及分页查询用户信息等业务逻辑时用于承载分页后的用户数据等内容便于对分页数据进行统一的处理和传递。
import jakarta.annotation.Resource;
// 用于进行资源注入的注解作用是将实现了IUserService接口的具体实例注入到当前的UserController类中使得类中的方法可以便捷地调用对应的服务方法来实现具体业务逻辑确保业务功能的正常实现。
import org.springframework.security.access.prepost.PreAuthorize;
// 用于在方法级别进行权限控制的注解,依据配置的权限表达式判断当前用户是否具备相应权限来访问对应的方法,以此保障系统资源只能被授权用户操作,增强系统的安全性和权限管理,这里针对不同的用户操作明确了有权限执行的角色。
import org.springframework.validation.annotation.Validated;
// 结合具体的验证规则由UserGroup中不同分组定义对传入方法的参数进行数据合法性验证保证接收到的参数符合业务要求避免非法数据进入业务逻辑处理流程提高数据质量和业务操作的准确性。
import org.springframework.web.bind.annotation.*;
// 通配符导入包含了众多Spring Web相关的注解例如用于定义请求处理方法的不同请求方式注解如@GetMapping、@PostMapping等以及处理请求路径、请求参数等相关的注解方便在控制器类中构建API接口处理来自客户端的各种请求。
import org.springframework.web.multipart.MultipartFile;
// 引入MultipartFile类用于处理文件上传相关操作在用户上传头像、Excel导入用户数据等功能中会使用到方便接收前端上传的文件内容。
import java.util.Objects;
// 引入Objects类它提供了一些用于操作对象的实用方法虽然在这里暂时未看到直接使用它的地方但在一些涉及对象比较、判空等操作时可能会用到。
/**
*
*
* @Author WeiJin
* @Version 1.0
* @Date 2024/3/25 15:50
*/
//用户管理
// 这里是对该类功能的简单描述,表明这个类主要负责处理与用户相关的各种管理操作,比如获取用户登录信息、创建用户、修改密码、删除用户、分页查询用户信息以及涉及用户的一些特殊操作(如加入班级、上传头像等)。
//@Author WeiJin
//@Version 1.0
//@Date 2024/3/25 15:50
//
@RestController
// 这是Spring框架提供的复合注解兼具@Controller和@ResponseBody的功能。意味着这个类是Spring MVC中的控制器类并且类中方法的返回值默认会直接以JSON等格式响应给客户端无需额外配置视图解析相关操作方便与前端进行数据交互。
@RequestMapping("/api/user")
// 用于给整个控制器类下的所有请求处理方法设置一个公共的请求路径前缀表明此类中所有处理请求的方法对应的URL路径都将以/api/user开头便于统一管理和组织与用户管理相关的一组API接口路径。
public class UserController {
@Resource
// 使用@Resource注解来进行依赖注入让Spring容器查找并注入一个实现了IUserService接口的实例到当前类中方便后续调用相关业务方法确保业务逻辑能够顺利执行。
private IUserService iUserService;
/**
*
*
* @return
*/
// 获取用户登录信息
// @return 响应结果返回一个Result类型的对象用于告知客户端获取用户登录信息操作是否成功以及可能附带的相关提示信息等其内部封装了操作结果标识以及获取到的用户登录相关信息以UserVO形式等内容便于前端展示给当前登录用户查看自身信息。
@GetMapping("/info")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/user基础上添加/info即/api/user/info用于获取用户登录信息符合GET请求获取资源的使用场景便于客户端发起获取自身登录信息的请求。
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"(学生角色)、"role_teacher"(教师角色)或者"role_admin"(管理员角色)权限的用户都可以访问这个方法,意味着只要登录的这几种角色的用户都有权利获取自己的登录信息,确保信息的安全性和访问权限的合理性。
public Result<UserVO> info() {
// 方法内部直接调用通过依赖注入获取的iUserService接口实例的info方法由服务层去具体实现获取用户登录信息的详细业务逻辑比如从数据库中查询当前登录用户的基本信息、权限信息等相关数据并封装成对应的视图对象最后将查询结果封装到Result对象中返回给客户端。
return iUserService.info();
}
/**
*
*
* @param userForm [id]
* @return
*/
//创建用户,教师只能创建学生,管理员可以创建教师和学生
//@param userForm 请求参数,用户名、真实姓名[、角色id]这是一个UserForm类型的参数用于承载前端传来的创建用户时需要填写的各种详细信息像用户名、真实姓名是必填的基本信息角色ID根据不同创建者教师或管理员有不同的可选值范围并且会依据UserGroup.CreateUserGroup.class指定的验证规则进行数据合法性验证确保传入的数据符合创建用户的要求。
// @return 响应结果返回一个Result类型的对象用于告知客户端创建用户操作是否成功以及可能附带的相关提示信息等其内部封装了操作结果标识以及其他有关创建用户的反馈信息等内容便于前端知晓创建操作是否成功及获取相应提示。
@PostMapping
// 该注解表明这个方法用于处理HTTP POST请求其请求路径就是类级别定义的基础路径/api/user因为这里没有额外指定路径POST请求常用于向服务器提交数据在此处符合向服务器提交创建用户相关信息的场景便于客户端将创建用户的信息发送给服务器。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"(教师角色)或者"role_admin"(管理员角色)权限的用户才能访问这个方法,意味着只有这两种角色的用户有权利创建新用户,确保用户创建操作的权限安全性,防止非法创建用户。
public Result<String> createUser(@Validated(UserGroup.CreateUserGroup.class) @RequestBody UserForm userForm) {
// @Validated结合@RequestBody注解@RequestBody表示从请求的正文中获取数据并将其转换为UserForm类型的对象@Validated按照指定的创建用户分组验证规则对这个转换后的对象进行数据合法性验证只有验证通过的数据才会进入方法内部进行后续的创建用户业务逻辑处理。
return iUserService.createUser(userForm);
// 调用iUserService接口的createUser方法由服务层去具体实现创建用户的详细业务逻辑比如将用户信息保存到数据库中根据教师或管理员角色的不同限制创建不同类型的用户教师只能创建学生管理员可创建教师和学生最后将服务层返回的包含创建结果等信息的Result对象直接返回给客户端。
}
/**
*
*
* @param userForm
* @return
*/
// 用户修改密码
//@param userForm 入参这是一个UserForm类型的参数用于承载前端传来的修改密码时需要填写的相关信息例如原密码、新密码等内容并且会依据UserGroup.UpdatePasswordGroup.class指定的验证规则进行数据合法性验证保证传入的数据符合密码修改的业务要求。
// @return 响应结果返回一个Result类型的对象用于告知客户端修改密码操作是否成功以及可能附带的相关提示信息等其内部封装了操作结果标识以及其他与密码修改相关的反馈信息等内容便于前端知晓修改操作是否成功及获取相应提示。
@PutMapping
// 此注解指定这个方法用于处理HTTP PUT请求其请求路径就是类的基础路径/api/userPUT请求常用于更新资源这里符合用户更新自己密码的操作场景便于客户端向服务器提交密码修改相关信息进行密码更新。
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"、"role_teacher"或者"role_admin"权限的用户都可以访问这个方法,意味着这几种角色的用户都有权利修改自己的密码,确保用户对自身密码管理的权限合理性。
public Result<String> updatePassword(@Validated(UserGroup.UpdatePasswordGroup.class) @RequestBody UserForm userForm) {
// 通过@Validated结合@RequestBody对userForm参数进行数据合法性验证及从请求正文获取数据转换为对应对象只有符合密码修改验证规则的数据才进入后续业务逻辑处理。
return iUserService.updatePassword(userForm);
// 调用iUserService接口的updatePassword方法由服务层去具体实现根据传入的修改密码相关信息进行密码更新的详细业务逻辑比如验证原密码是否正确、更新数据库中用户的密码字段等操作最后将操作结果封装到Result对象中返回给客户端。
}
/**
*
*
* @param ids ids
* @return
*/
// 批量删除用户
//@param ids 字符串ids这是一个字符串类型的参数用于指定要批量删除的用户的相关标识可能是多个用户ID以某种格式拼接比如逗号分隔等情况通过这个参数可以准确找到对应的用户记录进行批量删除操作方便进行批量操作的信息传递。
// @return 相应结果返回一个Result类型的对象用于告知客户端批量删除用户操作是否成功以及可能的相关提示信息等其内部封装了操作结果标识以及其他相关的反馈信息等内容便于前端知晓删除操作是否成功及获取相应提示。
@DeleteMapping("/{ids}")
// 该注解表明这个方法用于处理HTTP DELETE请求其请求路径是在类的基础路径/api/user基础上添加/{ids},其中{ids}是路径变量用于接收要批量删除的用户的相关标识信息DELETE请求常用于删除资源这里符合批量删除指定用户的操作场景便于客户端根据用户标识发起删除请求。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法,意味着只有教师或管理员角色的用户有权利执行批量删除用户的操作,确保删除用户操作的权限安全性,防止非法删除用户数据。
public Result<String> deleteBatchByIds(@PathVariable("ids") String ids) {
// 通过@PathVariable注解获取路径中传入的要批量删除的用户相关标识参数将其传递给服务层以便服务层依据这些信息执行批量删除对应的用户记录的业务逻辑。
return iUserService.deleteBatchByIds(ids);
// 调用iUserService接口的deleteBatchByIds方法由服务层去具体实现根据传入的用户标识批量删除用户的详细业务逻辑比如从数据库中移除对应的多条用户记录等最后将操作结果封装到Result对象中返回给客户端。
}
/**
* Excel
*
* @param file
* @return
*/
// Excel导入用户数据
//@param file 文件这是一个MultipartFile类型的参数用于接收前端上传的包含用户数据的Excel文件服务层会对该文件进行解析等操作来批量导入用户数据到系统中方便批量添加用户信息提高添加效率。
// @return 响应结果返回一个Result类型的对象用于告知客户端Excel导入用户数据操作是否成功以及可能附带的相关提示信息等其内部封装了操作结果标识以及其他与导入操作相关的反馈信息等内容便于前端知晓导入是否成功及获取相应提示。
@PostMapping("/import")
// 表明这个方法用于处理HTTP POST请求其请求路径是在类级别定义的基础路径/api/user基础上添加/import用于区分其他用户管理相关操作路径POST请求常用于向服务器提交数据在此处符合向服务器提交Excel文件用于导入用户数据的场景便于客户端上传文件进行导入操作。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,只有拥有"role_teacher"或者"role_admin"权限的用户才能访问这个方法意味着只有教师或管理员角色的用户有权利执行Excel导入用户数据的操作确保导入操作的权限安全性防止非法导入数据。
public Result<String> importUsers(@RequestParam("file") MultipartFile file) {
// 通过@RequestParam("file")注解获取前端上传的文件将其赋值给file变量以便服务层依据该文件进行后续的用户数据解析和导入业务逻辑处理。
return iUserService.importUsers(file);
// 调用iUserService接口的importUsers方法由服务层去具体实现根据传入的Excel文件解析并导入用户数据的详细业务逻辑比如读取文件内容、转换数据格式、插入数据库等操作最后将操作结果封装到Result对象中返回给客户端。
}
/**
*
*
* @param code
* @return
*/
// 用户加入班级,只有学生才能加入班级
// @param code 班级口令,这是一个字符串类型的参数,用于指定要加入班级的口令信息,学生通过输入正确的班级口令来申请加入对应的班级,方便进行班级加入的验证操作,确保只有知道口令的学生能加入相应班级。
// @return 响应返回一个Result类型的对象用于告知客户端用户加入班级操作是否成功以及可能附带的相关提示信息等其内部封装了操作结果标识以及其他与班级加入相关的反馈信息等内容便于前端知晓加入操作是否成功及获取相应提示。
@PutMapping("/grade/join")
// 表示这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/user基础上添加/grade/joinPUT请求常用于更新资源这里符合学生更新自己班级归属即加入班级的操作场景便于客户端向服务器提交班级口令等信息进行班级加入操作。
@PreAuthorize("hasAnyAuthority('role_student')")
// 进行权限控制,只有拥有"role_student"(学生角色)权限的用户才能访问这个方法,意味着只有学生角色的用户有权利执行加入班级的操作,确保班级加入操作的权限合理性,防止其他角色非法操作班级成员信息。
public Result<String> joinGrade(@RequestParam("code") String code) {
// 通过@RequestParam("code")注解获取请求中名为"code"的班级口令参数将其赋值给code变量以便服务层依据该口令信息进行后续的验证和班级加入业务逻辑处理。
return iUserService.joinGrade(code);
// 调用iUserService接口的joinGrade方法由服务层去具体实现根据传入的班级口令进行验证并将学生加入相应班级的详细业务逻辑比如验证口令是否正确、更新数据库中用户与班级的关联关系等操作最后将操作结果封装到Result对象中返回给客户端。
}
/**
*
*
* @param pageNum
* @param pageSize
* @param gradeId Id
* @param realName
* @return
*/
// @param pageNum 页码是一个整数类型的参数用于指定要查询的分页数据的页码比如pageNum为1表示查询第一页的数据客户端可以根据需要传入相应页码来获取不同页的用户信息该参数为可选参数若未传入则默认值为1方便客户端灵活控制查询的页面位置以获取期望页面的用户数据展示。
//@param pageSize 每页记录数同样是整数类型参数用于设定每页显示的用户记录数量默认值为10表示如果客户端没有指定每页显示多少条记录就按照每页10条来进行分页查询便于控制每页展示的用户信息量满足不同展示需求。
// @param gradeId 班级Id是一个整数类型的可选参数用于根据班级的唯一标识符进行筛选查询通过传入班级ID可获取该班级下的用户信息方便教师、管理员按班级维度查看用户情况若不传入该参数则可能查询所有班级的用户信息具体看服务层实现逻辑
// @param realName 真实姓名,是一个字符串类型的可选参数,用于根据用户的真实姓名进行模糊查询等筛选操作,客户端可以传入用户真实姓名的部分或全部内容来查找符合条件的用户信息,便于精准查找特定用户或符合姓名特征的一批用户信息。
//@return 响应结果返回一个Result类型的对象其中封装了IPage<UserVO>类型的数据IPage用于承载分页后的用户实体对应的视图对象数据整体通过Result返回给客户端告知分页查询的结果以及相应的用户数据信息方便前端展示和使用查询到的用户分页数据。
@GetMapping("/paging")
// 表示这个方法用于处理HTTP GET请求其请求路径是在类的基础路径/api/user基础上添加/paging即/api/user/paging用于获取分页后的用户信息符合GET请求获取资源的使用场景便于客户端发起按条件分页查询用户数据的请求。
@PreAuthorize("hasAnyAuthority('role_teacher','role_admin')")
// 进行权限控制,拥有"role_teacher"(教师角色)或者"role_admin"(管理员角色)权限的用户都可以访问这个方法,意味着教师和管理员角色的用户有权利执行分页查询用户信息的操作,确保有权限的用户才能获取相关数据,保障数据访问的安全性和合理性。
public Result<IPage<UserVO>> pagingUser(@RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
@RequestParam(value = "gradeId", required = false) Integer gradeId,
@RequestParam(value = "realName", required = false) String realName) {
// 通过@RequestParam注解分别获取请求中的pageNum、pageSize、gradeId、realName参数若客户端未传入pageNum和pageSize则分别使用默认值1和10gradeId用于按班级筛选realName用于按真实姓名筛选将获取到的参数传递给服务层进行后续的分页查询逻辑处理。
return iUserService.pagingUser(pageNum, pageSize, gradeId, realName);
// 调用通过依赖注入获取的iUserService接口实例的pagingUser方法由服务层去具体实现根据传入的页码、每页记录数以及班级ID、真实姓名等筛选条件进行分页查询用户信息的详细业务逻辑比如从数据库中按照条件筛选并查询用户数据进行分页处理后封装成对应的视图对象最后将查询结果封装到Result对象中返回给客户端。
}
/**
*
*
* @param file
* @return
*/
///用户上传头像
// @param file 文件这是一个MultipartFile类型的参数用于接收前端上传的用户头像文件MultipartFile类型方便处理文件上传相关操作可获取文件的各种属性如文件名、文件内容等以便后续进行头像文件的保存、处理等业务逻辑。
// @return 响应结果返回一个Result类型的对象用于告知客户端用户上传头像操作是否成功以及可能附带的相关提示信息等其内部封装了操作结果标识以及其他与头像上传相关的反馈信息比如头像保存后的访问地址等内容具体看业务逻辑等内容便于前端知晓上传操作是否成功及获取相应提示。
@PutMapping("/uploadAvatar")
// 此注解指定这个方法用于处理HTTP PUT请求其请求路径是在类的基础路径/api/user基础上添加/uploadAvatarPUT请求常用于更新资源这里符合用户更新自己头像即上传新头像覆盖旧头像的操作场景便于客户端向服务器提交头像文件进行更新操作。
@PreAuthorize("hasAnyAuthority('role_student','role_teacher','role_admin')")
// 进行权限控制,拥有"role_student"(学生角色)、"role_teacher"(教师角色)或者"role_admin"(管理员角色)权限的用户都可以访问这个方法,意味着这几种角色的用户都有权利执行上传头像的操作,确保有权限的用户才能进行头像更新,保障头像管理的权限合理性。
public Result<String> uploadAvatar(@RequestPart("file") MultipartFile file) {
// 通过@RequestPart("file")注解获取前端上传的文件并将其赋值给file变量以便服务层依据该文件进行后续的头像文件保存、处理等业务逻辑比如将文件存储到指定位置更新数据库中用户头像相关的记录等操作。
return iUserService.uploadAvatar(file);
// 调用iUserService接口的uploadAvatar方法由服务层去具体实现根据传入的头像文件进行上传处理的详细业务逻辑例如利用相关工具类可能是前面引入的AliOSSUtil等将文件上传到存储服务获取文件存储后的相关信息最后将操作结果以及相关反馈数据封装到Result对象中返回给客户端。
}
}
}

@ -1,11 +1,23 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理和维护。
import cn.org.alan.exam.model.entity.Certificate;
// 引入Certificate实体类这个类通常对应数据库中的一张表结构用于持久化存储证书相关的信息包含如证书名称、颁发机构、有效期等具体的属性字段代表了证书在系统中的实际数据存储形式。
import cn.org.alan.exam.model.form.CertificateForm;
// 引入CertificateForm类这是一种数据传输对象Data Transfer ObjectDTO形式的类一般用于在前端与后端之间或者不同业务层之间传递数据它承载着与证书相关的输入信息比如用户在创建或更新证书时填写的证书名称等数据方便进行数据的交互和传递。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口Mapstruct会根据接口中定义的方法和映射规则在编译期自动生成对应的实现类来实现不同对象之间属性的转换简化了对象转换的代码编写工作。
import org.mapstruct.Mapping;
// 引入Mapstruct框架提供的Mapping注解用于在对象转换方法中明确指定源对象和目标对象之间具体属性的映射关系比如指定某个源属性对应到目标对象的哪个属性上使得属性的转换更加精确和可控。
import org.mapstruct.Mappings;
// 引入Mapstruct框架提供的Mappings注解它是一个容器注解用于包含多个@Mapping注解当需要定义多个属性的映射关系时就可以使用它来统一管理这些映射规则使代码结构更清晰。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件这样Spring容器在进行组件扫描时就能发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能。
/**
* @ Author JinXi
@ -13,11 +25,15 @@ import org.springframework.stereotype.Component;
* @ Date 2024/5/11 14:40
*/
@Component
// 将该接口标记为Spring组件使其能被Spring容器管理从而可以利用Spring的依赖注入等功能在其他类中方便地使用这个接口所定义的对象转换功能。
@Mapper(componentModel="spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使其可以像普通的Spring Bean一样被注入和使用。
public interface CertificateConverter {
@Mappings({
@Mapping(target = "certificateName",source = "certificateName")
// 使用@Mappings注解包裹@Mapping注解来定义属性的映射规则。这里的@Mapping注解表示将源对象CertificateForm类型中的"certificateName"属性值映射到目标对象Certificate类型的"certificateName"属性上,也就是在进行对象转换时,同名的证书名称属性会进行对应赋值。
})
Certificate fromToEntity(CertificateForm certificateForm);
}
// 定义了一个名为fromToEntity的方法它接收一个CertificateForm类型的参数certificateForm作用是根据定义好的映射规则上面的@Mappings注解中指定的将CertificateForm对象转换为Certificate实体对象方便在业务逻辑中例如将前端传入的证书相关表单数据转换为可持久化存储到数据库的实体对象形式。
}

@ -1,16 +1,35 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护。
import cn.org.alan.exam.model.entity.Exam;
// 引入Exam实体类它对应数据库中存储考试相关信息的表结构包含如考试名称、考试时间、考试总分等各种与考试相关的属性代表了考试在系统中的实际数据存储形式是业务操作中涉及考试数据持久化的核心对象。
import cn.org.alan.exam.model.entity.ExamQuestion;
// 引入ExamQuestion实体类用于表示考试题目相关的实体信息比如题目内容、题目类型选择题、填空题等、所属考试等属性体现了考试题目在数据库中的存储结构和相关关联关系与考试的具体题目内容相关。
import cn.org.alan.exam.model.entity.Option;
// 引入Option实体类通常用于表示题目选项相关的实体信息比如选择题的各个选项内容、选项是否正确等属性是针对有选项的题目如选择题在数据库中的具体数据存储形式与题目选项相关的数据操作有关。
import cn.org.alan.exam.model.form.exam.ExamAddForm;
// 引入ExamAddForm类这是一种数据传输对象Data Transfer ObjectDTO形式的类主要用于在前端与后端之间或者不同业务层之间传递数据它承载着添加考试时前端输入的各种信息像考试名称、考试时长等添加考试需要填写的数据方便进行数据的交互和传递专用于考试添加场景。
import cn.org.alan.exam.model.form.exam.ExamUpdateForm;
// 引入ExamUpdateForm类同样是数据传输对象DTO用于承载更新考试信息时前端传入的相关数据例如修改后的考试名称、时间等内容为考试信息更新操作提供要修改的数据来源专用于考试更新场景。
import cn.org.alan.exam.model.vo.exam.*;
// 引入多个以“exam”命名空间下的视图对象Value ObjectVO这些类用于向客户端展示特定格式的考试相关信息不同的VO类对应不同的业务场景比如展示考试分页信息、考试详情信息、题目信息等方便前端按照合适的格式展示数据给用户查看。
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 引入MyBatis Plus框架提供的Page类它用于表示分页数据的结构包含了分页相关的信息如页码、每页记录数、总记录数等以及具体的数据列表在涉及分页查询考试相关信息并转换展示格式时会用到这个类型。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口Mapstruct会根据接口中定义的方法和映射规则在编译期自动生成对应的实现类来实现不同对象之间属性的转换简化了对象转换的代码编写工作提高开发效率。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件这样Spring容器在进行组件扫描时就能发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现组件化管理。
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个考试题目信息列表、多个选项信息列表等情况时会用到便于对一组数据进行统一操作和传递。
/**
* @Author Alan
@ -18,24 +37,35 @@ import java.util.List;
* @Date 2024/4/1 3:18 PM
*/
@Component
// 将该接口标记为Spring组件使其能被Spring容器管理从而可以利用Spring的依赖注入等功能在其他类中方便地使用这个接口所定义的对象转换功能便于集成到整个Spring项目体系中。
@Mapper(componentModel="spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使其可以像普通的Spring Bean一样被注入和使用实现对象转换功能与Spring的无缝对接。
public interface ExamConverter {
Page<ExamVO> pageEntityToVo(Page<Exam> examPage);
// 定义了一个名为pageEntityToVo的方法它接收一个Page<Exam>类型的参数examPage作用是将包含Exam实体对象的分页数据结构MyBatis Plus中的Page类型里面包含了考试相关的实体数据以及分页信息按照一定的映射规则转换为包含ExamVO视图对象的分页数据结构以便将考试相关信息以适合前端展示的格式通过ExamVO来定义展示形式返回给客户端用于分页展示考试信息的场景。
Exam formToEntity(ExamUpdateForm examUpdateForm);
// 定义了一个名为formToEntity的方法它接收一个ExamUpdateForm类型的参数examUpdateForm功能是依据定义好的映射规则由Mapstruct自动生成实现类时根据对象属性名等规则确定也可手动配置更精确的映射将用于更新考试信息的ExamUpdateForm对象转换为Exam实体对象方便后续将更新后的考试数据持久化到数据库中适用于考试信息更新的业务逻辑场景。
Exam formToEntity(ExamAddForm examAddForm);
// 同样是一个将数据传输对象转换为实体对象的方法接收ExamAddForm类型的参数examAddForm作用是把包含添加考试所需信息的ExamAddForm对象转换为Exam实体对象以便将前端传来的添加考试信息保存到数据库中对应考试创建添加的业务操作场景。
List<ExamDetailRespVO> listEntityToExamDetailRespVO(List<ExamQuestion> examQuestion);
// 定义了一个方法接收一个List<ExamQuestion>类型的参数examQuestion即考试题目实体对象的列表目的是按照特定的映射规则将这些考试题目实体对象转换为ExamDetailRespVO视图对象列表用于将考试题目相关信息以适合前端展示的格式由ExamDetailRespVO定义进行展示比如在展示考试详情中的题目列表时会用到这个转换。
ExamDetailVO examToExamDetailVO(Exam exam);
// 该方法接收一个Exam实体对象作为参数exam用于将Exam实体按照相应的映射规则转换为ExamDetailVO视图对象ExamDetailVO通常会包含更详细、更适合前端展示的考试相关信息相比于原始的Exam实体对象比如考试的详细配置、关联信息等常用于向客户端展示考试详细情况的业务场景。
ExamGradeListVO entityToExamGradeListVO(Exam exam);
// 接收Exam实体对象exam按照一定的映射逻辑将其转换为ExamGradeListVO视图对象ExamGradeListVO大概率是用于展示考试与班级等相关关联信息比如某个班级参与此次考试的情况等具体看其内部定义的格式方便在涉及班级与考试关联展示的业务场景中使用。
ExamQuestionVO examQuestionEntityToVO(ExamQuestion examQuestion);
// 接收ExamQuestion实体对象examQuestion将其转换为ExamQuestionVO视图对象把考试题目实体的相关数据以适合前端展示的格式由ExamQuestionVO定义进行转换比如对题目内容、题目类型等属性进行整理和格式化便于前端展示具体的题目信息。
List<ExamQuestionVO> examQuestionListEntityToVO(List<ExamQuestion> examQuestion);
// 接收考试题目实体对象列表examQuestion把列表中的每个ExamQuestion实体按照相应映射规则转换为ExamQuestionVO视图对象并最终返回一个由这些ExamQuestionVO对象组成的列表用于批量将考试题目实体转换为适合前端展示的题目视图对象列表比如在展示一场考试的所有题目时会用到这个转换。
List<OptionVO> opListEntityToVO(List<Option> examQuestion);
}
// 这里参数名可能不太准确应该是List<Option> options更合适些它接收一个选项实体对象列表examQuestion实际应为表示选项的List<Option>类型作用是将这些选项实体对象按照特定映射规则转换为OptionVO视图对象列表用于将题目选项相关信息以适合前端展示的格式由OptionVO定义进行展示例如展示选择题的各个选项内容等情况。
}

@ -1,13 +1,29 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护让代码结构更清晰易于查找和理解不同功能模块的代码。
import cn.org.alan.exam.model.entity.ExerciseRecord;
// 引入ExerciseRecord实体类它通常对应数据库中存储练习记录相关信息的表结构包含了如练习的题目ID、用户作答情况、练习时间等具体属性代表了练习记录在系统中的实际数据存储形式是业务操作中涉及练习记录数据持久化的关键对象。
import cn.org.alan.exam.model.form.ExerciseFillAnswerFrom;
// 引入ExerciseFillAnswerFrom类这是一种数据传输对象Data Transfer ObjectDTO形式的类一般用于在前端与后端之间或者不同业务层之间传递数据它承载着用户填写练习答案相关的输入信息像题目ID、作答的答案内容等具体数据方便进行数据的交互和传递为后续处理练习作答相关业务提供数据来源。
import cn.org.alan.exam.model.vo.QuestionVO;
// 引入QuestionVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的题目相关信息比如题目内容、题目类型、所属章节等经过整理和格式化后适合前端展示的数据便于前端展示给用户查看题目详情等情况。
import cn.org.alan.exam.model.vo.exercise.AnswerInfoVO;
// 引入AnswerInfoVO类同样是视图对象VO大概率用于向客户端展示答案相关的详细信息例如答案的正确性、解析内容、得分情况等具体的与答案有关的展示数据方便在前端呈现给用户查看作答后的反馈信息。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口Mapstruct会根据接口中定义的方法和映射规则在编译期自动生成对应的实现类来实现不同对象之间属性的转换这样开发者就无需手动编写大量繁琐的对象属性赋值代码简化了对象转换的开发流程提高效率。
import org.mapstruct.Mapping;
// 引入Mapstruct框架提供的Mapping注解用于在对象转换方法中明确指定源对象和目标对象之间具体属性的映射关系也就是定义了从源对象的哪个属性取值赋给目标对象的哪个属性使得属性的转换更加精确、可控符合业务逻辑要求。
import org.mapstruct.Mappings;
// 引入Mapstruct框架提供的Mappings注解它是一个容器注解用于包含多个@Mapping注解当需要定义多个属性的映射关系时使用它来统一管理这些映射规则使代码结构更清晰一目了然地看到所有的属性映射配置情况。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件这样Spring容器在进行组件扫描时就能发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让接口能更好地融入Spring框架的整体架构中。
/**
* @Author WeiJin
@ -15,13 +31,19 @@ import org.springframework.stereotype.Component;
* @Date 2024/5/6 10:15
*/
@Component
// 将该接口标记为Spring组件使其可以被Spring容器纳入管理范围进而可以利用Spring的依赖注入机制在其他需要进行对象转换操作的类中方便地注入并使用这个接口所定义的转换功能增强了代码的可维护性和扩展性。
@Mapper(componentModel="spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成让生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入和生命周期管理中便于在业务逻辑中灵活调用。
public interface ExerciseConverter {
@Mappings({
@Mapping(source = "quId",target = "questionId"),
@Mapping(source = "quType",target = "questionType")
})
// 使用@Mappings注解包裹着两个@Mapping注解来详细定义属性的映射规则。第一个@Mapping注解指定将源对象ExerciseFillAnswerFrom类型中的"quId"属性值映射到目标对象ExerciseRecord类型的"questionId"属性上也就是在进行从填写练习答案的表单数据转换为练习记录实体数据时把对应的题目ID进行传递赋值第二个@Mapping注解表示将源对象中的"quType"属性值映射到目标对象的"questionType"属性,同样是为了在转换过程中正确传递题目类型信息,确保数据的一致性和准确性。
ExerciseRecord fromToEntity(ExerciseFillAnswerFrom exerciseFillAnswerFrom);
// 定义了一个名为fromToEntity的方法它接收一个ExerciseFillAnswerFrom类型的参数exerciseFillAnswerFrom其作用是根据上面定义好的映射规则通过@Mappings注解指定的将ExerciseFillAnswerFrom对象转换为ExerciseRecord实体对象方便后续将用户填写的练习答案相关信息以符合数据库存储要求的实体形式ExerciseRecord持久化保存到数据库中应用于处理练习作答记录保存的业务场景。
AnswerInfoVO quVOToAnswerInfoVO(QuestionVO questionVO);
}
// 定义了一个名为quVOToAnswerInfoVO的方法它接收一个QuestionVO类型的参数questionVO功能是按照某种可能是基于属性名称匹配等默认规则或者未来可扩展的自定义规则具体看业务需求和实现情况映射逻辑将QuestionVO对象转换为AnswerInfoVO对象目的是把题目相关信息通过QuestionVO展示的转换为答案相关的详细信息通过AnswerInfoVO展示以便在前端展示给用户更全面的关于答案的情况如答案解析、正确性判断等常用于展示作答反馈信息的业务场景。
}

@ -1,13 +1,26 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护使得不同功能的代码能各归其位便于查找和理解整个项目的代码结构。
import cn.org.alan.exam.model.entity.Grade;
// 引入Grade实体类它对应数据库中存储班级相关信息的表结构包含如班级名称、班级编号、所属年级等具体的属性字段代表了班级在系统中的实际数据存储形式是业务操作中涉及班级数据持久化以及相关业务逻辑处理的核心数据对象。
import cn.org.alan.exam.model.form.GradeForm;
// 引入GradeForm类这是一种数据传输对象Data Transfer ObjectDTO形式的类主要用于在前端与后端之间或者不同业务层之间传递数据它承载着与班级相关操作比如创建班级、更新班级信息时前端输入的各种信息像班级名称、班主任等需要填写的数据方便进行数据的交互和传递为后续业务处理提供数据基础。
import cn.org.alan.exam.model.vo.GradeVO;
// 引入GradeVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的班级相关信息例如班级名称、班级人数、班级平均成绩如果有相关业务关联的话等经过整理和格式化后适合前端展示的数据便于前端展示给用户查看班级的具体情况。
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 引入MyBatis Plus框架提供的Page类它用于表示分页数据的结构包含了分页相关的信息如页码、每页记录数、总记录数等以及具体的数据列表在涉及分页查询班级相关信息并转换展示格式时会用到这个类型方便对分页数据进行统一管理和操作。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口Mapstruct会根据接口中定义的方法和映射规则在编译期自动生成对应的实现类来实现不同对象之间属性的转换这样开发者无需手动编写大量重复的对象属性赋值代码简化了对象转换相关的开发工作提高了代码开发效率。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件使得Spring容器在进行组件扫描时能够发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让这个接口能更好地融入到整个Spring项目的体系架构中。
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个班级的VO列表、多个班级实体列表等情况时会用到便于对一组数据进行统一的操作和传递符合常见的数据处理需求。
/**
* @Author Alan
@ -15,14 +28,20 @@ import java.util.List;
* @Date 2024/3/28 2:03 PM
*/
@Component
// 将该接口标记为Spring组件目的是让Spring容器管理这个接口进而可以在其他类中通过Spring的依赖注入机制方便地获取该接口的实例调用其定义的对象转换方法以此增强代码的可维护性和扩展性使代码模块之间的耦合度更低。
@Mapper(componentModel="spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使得生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入、生命周期管理等流程中便于在业务逻辑中灵活运用。
public interface GradeConverter {
Page<GradeVO> pageEntityToVo(Page<Grade> page);
// 定义了一个名为pageEntityToVo的方法它接收一个Page<Grade>类型的参数page这里的Page<Grade>表示包含了班级Grade实体对象的分页数据结构由MyBatis Plus的Page类型承载包含了班级实体以及分页相关的信息。该方法的作用是按照一定的映射规则将这个包含班级实体的分页数据转换为包含GradeVO视图对象的分页数据结构以便把班级相关信息以适合前端展示的格式通过GradeVO来定义展示形式返回给客户端常用于分页展示班级信息的业务场景例如在班级列表分页查询结果展示时进行数据转换。
Grade formToEntity(GradeForm gradeForm);
// 定义了名为formToEntity的方法它接收一个GradeForm类型的参数gradeForm功能是依据一定的映射规则由Mapstruct自动生成实现类时根据对象属性名等规则确定也可手动配置更精确的映射将用于传递班级相关操作数据的GradeForm对象转换为Grade实体对象方便后续将前端传来的班级创建或更新等操作的信息保存到数据库中对应班级信息创建或更新的业务操作场景确保数据能正确持久化。
List<GradeVO> listEntityToVo(List<Grade> page);
GradeVO GradeToGradeVO(Grade grade);
// 定义的这个方法接收一个List<Grade>类型的参数page也就是班级实体对象的列表其目的是按照特定的映射规则将这些班级实体对象转换为GradeVO视图对象列表用于将班级相关信息以适合前端展示的格式由GradeVO定义进行批量展示比如在展示多个班级的基本信息时把从数据库查询出来的班级实体列表转换为适合前端展示的视图对象列表后返回给前端进行展示。
}
GradeVO GradeToGradeVO(Grade grade);
// 该方法接收一个Grade实体对象作为参数grade作用是将单个的Grade实体按照相应的映射规则转换为GradeVO视图对象GradeVO通常包含了更符合前端展示需求的班级相关信息相比于原始的Grade实体对象比如对班级名称、人数等属性进行整理和格式化便于前端更友好地展示单个班级的详细情况常用于在需要展示特定班级详细信息的业务场景中。
}

@ -1,12 +1,26 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包便于清晰划分代码结构让不同功能的代码归类存放方便后续的维护、查找以及理解整个项目代码逻辑。
import cn.org.alan.exam.model.entity.Notice;
// 引入Notice实体类它对应数据库中存储通知相关信息的表结构包含像通知标题、通知内容、发布时间、发布人等具体的属性字段代表了通知在系统中的实际数据存储形式是业务操作中涉及通知数据持久化以及围绕通知展开的各类业务逻辑处理的基础数据对象。
import cn.org.alan.exam.model.form.NoticeForm;
// 引入NoticeForm类这是一种数据传输对象Data Transfer ObjectDTO形式的类主要用于在前端与后端之间或者不同业务层之间传递数据它承载着创建或更新通知时前端输入的各种信息例如通知的标题、详细内容等需要填写的数据方便进行数据的交互和传递为后续的通知相关业务处理提供数据来源。
import cn.org.alan.exam.model.vo.GradeVO;
// 引入GradeVO类这是一个视图对象Value ObjectVO通常用于向客户端展示班级相关的特定格式信息虽然在这里看起来与通知转换功能没有直接关联但可能在整个项目的更广泛场景下存在关联或者是误引入具体需看项目实际情况比如班级名称、班级人数、班级平均成绩等经过整理和格式化后适合前端展示的数据便于前端展示给用户查看班级的详细情况。
import cn.org.alan.exam.model.vo.NoticeVO;
// 引入NoticeVO类这是一个视图对象VO用于向客户端展示特定格式的通知相关信息例如对通知标题、内容进行格式化处理可能添加一些样式、截取合适长度等还有可能包含发布时间的友好展示格式等经过整理后的适合前端展示的数据便于前端以更好的形式呈现通知给用户查看。
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 引入MyBatis Plus框架提供的Page类它用于表示分页数据的结构包含了分页相关的信息如页码、每页记录数、总记录数等以及具体的数据列表在涉及分页查询通知相关信息并转换展示格式时会用到这个类型方便对分页的通知数据进行统一管理以及后续的展示格式转换操作。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口Mapstruct会根据接口中定义的方法和映射规则在编译期自动生成对应的实现类来实现不同对象之间属性的转换这样就避免了开发者手动编写大量繁琐的对象属性赋值代码极大地简化了对象转换相关的开发工作提高了代码开发效率。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件使得Spring容器在进行组件扫描时能够发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让这个接口能更好地融入到整个Spring项目的体系架构中方便与其他Spring组件协作完成业务功能。
/**
* @Author Alan
@ -14,12 +28,17 @@ import org.springframework.stereotype.Component;
* @Date 2024/3/28 11:04 PM
*/
@Component
// 将该接口标记为Spring组件意味着Spring容器会对其进行管理其他类可以通过Spring的依赖注入机制获取这个接口的实例进而调用接口中定义的方法来完成对象转换操作这样有助于降低代码之间的耦合度增强代码的可维护性和可扩展性使项目的代码结构更加清晰合理。
@Mapper(componentModel="spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使得生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入、生命周期管理等流程中便于在业务逻辑中灵活地运用这些转换功能。
public interface NoticeConverter {
Notice formToEntity(NoticeForm noticeForm);
// 定义了一个名为formToEntity的方法它接收一个NoticeForm类型的参数noticeForm其功能是依据一定的映射规则这些规则由Mapstruct根据对象属性名等默认情况确定也可以手动配置更精确的映射关系将承载前端输入通知相关数据的NoticeForm对象转换为Notice实体对象方便后续将前端传来的创建或更新通知的信息保存到数据库中对应通知创建或更新的业务操作场景确保通知数据能正确地持久化存储。
Page<NoticeVO> pageEntityToVo(Page<Notice> noticePage);
// 定义的这个方法接收一个Page<Notice>类型的参数noticePage这里的Page<Notice>表示包含了通知Notice实体对象的分页数据结构由MyBatis Plus的Page类型承载包含了通知实体以及分页相关的信息。该方法的作用是按照特定的映射规则将这个包含通知实体的分页数据转换为包含NoticeVO视图对象的分页数据结构以便把通知相关信息以适合前端展示的格式通过NoticeVO来定义展示形式返回给客户端常用于分页展示通知信息的业务场景例如在通知列表分页查询结果展示时进行数据转换操作。
NoticeVO NoticeToNoticeVO(Notice notice);
}
// 该方法接收一个Notice实体对象作为参数notice其作用是将单个的Notice实体按照相应的映射规则转换为NoticeVO视图对象NoticeVO通常包含了更符合前端展示需求的通知相关信息相比于原始的Notice实体对象比如对通知标题、内容进行更友好的格式化处理添加发布时间的合适展示格式等便于前端更美观、清晰地展示单个通知的详细情况常用于在需要展示特定通知详细信息的业务场景中。
}

@ -1,7 +1,11 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护使代码按照功能模块划分存放易于后续开发人员查找、理解以及扩展相关代码功能。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口。在Mapstruct框架中通过定义这样的接口并添加相应的映射规则后续可在接口方法上通过相关注解来定义具体属性映射关系等它会在编译期自动生成对应的实现类实现不同对象之间属性的转换功能从而避免开发者手动编写大量重复且容易出错的对象属性赋值代码提高开发效率和代码质量。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件。当Spring容器进行组件扫描时能够识别并管理被该注解标记的接口或类进而可以利用Spring的依赖注入等功能在其他需要使用该接口功能的地方便捷地获取其实例并调用相关方法使得代码能够更好地遵循Spring框架的组件化开发模式增强代码的可维护性和扩展性。
/**
* @Author WeiJin
@ -9,7 +13,9 @@ import org.springframework.stereotype.Component;
* @Date 2024/4/11 14:20
*/
@Component
// 将该接口标记为Spring组件这样做的目的是让Spring容器管理这个接口意味着在整个Spring项目的运行环境中该接口可以参与到依赖注入等机制里。例如其他的业务逻辑类如果需要使用这个接口所定义的数据转换功能就可以通过Spring的依赖注入方式轻松获取到该接口的实例然后调用相应的转换方法有助于构建低耦合、易维护的代码结构。
@Mapper(componentModel="spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"。这个配置告知Mapstruct框架生成的对应实现类要以Spring组件的形式进行管理使其能够与Spring框架无缝集成。生成的实现类就如同普通的Spring Bean一样可以遵循Spring的生命周期管理、配置注入等规则方便在基于Spring的项目中灵活运用比如在不同的业务层之间进行对象数据转换操作时很容易就能获取并使用这个接口对应的转换功能。
public interface OptionConverter {
}
// 这里定义了一个空的接口虽然目前没有定义具体的方法但从接口名称以及所在的“converter”包通常用于数据转换相关功能可以推测它大概率是用于定义与选项Option可能是题目选项相关的数据对象具体需结合项目中其他相关类来看相关的数据转换功能。后续开发人员可以根据具体的业务需求在此接口中添加相应的方法并通过Mapstruct的相关注解来定义方法中涉及的对象属性之间的映射关系从而实现将一种与选项相关的数据对象转换为另一种数据对象的功能比如将前端传来的选项相关表单数据转换为可持久化存储的实体对象数据或者将实体对象中的选项数据转换为适合前端展示的视图对象数据等。
}

@ -1,16 +1,32 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护使得不同功能模块的代码在项目中条理清晰便于查找和理解代码的整体结构与逻辑。
import cn.org.alan.exam.model.entity.Question;
// 引入Question实体类它对应数据库中存储题目相关信息的表结构包含像题目内容、题目类型如选择题、填空题、简答题等、所属知识点、难度系数等具体的属性字段代表了题目在系统中的实际数据存储形式是业务操作中涉及题目数据持久化以及围绕题目展开的各类业务逻辑处理的核心数据对象。
import cn.org.alan.exam.model.form.question.QuestionFrom;
// 引入QuestionFrom类这是一种数据传输对象Data Transfer ObjectDTO形式的类主要用于在前端与后端之间或者不同业务层之间传递数据它承载着创建或更新题目时前端输入的各种信息例如题目内容、选项如果是选择题的话、答案等需要填写的数据方便进行数据的交互和传递为后续的题目相关业务处理提供数据来源。
import cn.org.alan.exam.model.vo.GradeVO;
// 引入GradeVO类这是一个视图对象Value ObjectVO通常用于向客户端展示班级相关的特定格式信息在这里看起来与题目转换功能没有直接关联但可能在整个项目更广泛的业务场景下存在关联比如题目统计可能按班级维度展示等情况像班级名称、班级人数、班级平均成绩等经过整理和格式化后适合前端展示的数据便于前端展示给用户查看班级的详细情况。
import cn.org.alan.exam.model.vo.QuestionVO;
// 引入QuestionVO类这是一个视图对象VO用于向客户端展示特定格式的题目相关信息例如对题目内容进行格式化处理可能添加一些样式、截取合适长度等展示题目类型的友好描述以及关联的知识点等经过整理后的适合前端展示的数据便于前端以更好的形式呈现题目给用户查看。
import cn.org.alan.exam.model.vo.exercise.QuestionSheetVO;
// 引入QuestionSheetVO类同样是一个视图对象VO大概率用于在练习相关场景下向客户端展示题目信息的特定格式可能包含题目在练习中的序号、题目内容以及与练习答题相关的一些特定属性等方便在练习功能模块中展示题目给用户进行作答等操作。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口。Mapstruct会依据接口中定义的方法以及所添加的映射规则通过@Mapping等注解来指定具体属性的映射关系在编译期自动生成对应的实现类以此来实现不同对象之间属性的转换这样就大大简化了开发者手动编写对象属性赋值代码的工作量提高了开发效率和代码的准确性。
import org.mapstruct.Mapping;
// 引入Mapstruct框架提供的Mapping注解用于在对象转换方法中明确指定源对象和目标对象之间具体属性的映射关系也就是定义了从源对象的哪个属性取值赋给目标对象的哪个属性使得属性的转换更加精确、可控符合业务逻辑要求确保转换后的数据符合预期的展示或存储需求。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件使得Spring容器在进行组件扫描时能够发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让这个接口能更好地融入到整个Spring项目的体系架构中方便与其他Spring组件协作完成业务功能。
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个题目信息的视图对象列表、多个实体对象列表等情况时会用到便于对一组数据进行统一的操作和传递符合常见的数据处理和展示需求。
/**
* @Author WeiJin
@ -18,16 +34,24 @@ import java.util.List;
* @Date 2024/4/1 15:46
*/
@Component
// 将该接口标记为Spring组件意味着Spring容器会对其进行管理其他类可以通过Spring的依赖注入机制获取这个接口的实例进而调用接口中定义的方法来完成对象转换操作。这样做有助于降低代码之间的耦合度增强代码的可维护性和可扩展性使项目的代码结构更加清晰合理方便在不同的业务场景中灵活运用该接口提供的数据转换功能。
@Mapper(componentModel = "spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使得生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入、生命周期管理等流程中便于在业务逻辑中灵活地运用这些转换功能与整个Spring项目的开发模式相契合。
public interface QuestionConverter {
@Mapping(target = "repoId",source = "repoId")
// 使用@Mapping注解来定义属性的映射规则这里表示将源对象QuestionFrom类型中的"repoId"属性值映射到目标对象Question类型的"repoId"属性上目的是在把前端传来的题目相关表单数据通过QuestionFrom承载转换为可持久化存储的题目实体数据Question类型确保"repoId"这个特定属性的值能正确传递和赋值,保证数据的一致性和完整性,以符合业务逻辑对于该属性的要求。
Question fromToEntity(QuestionFrom questionFrom);
// 定义了一个名为fromToEntity的方法它接收一个QuestionFrom类型的参数questionFrom其功能是依据上面定义好的映射规则通过@Mapping注解指定的以及Mapstruct根据对象属性名等默认情况确定的其他映射关系将QuestionFrom对象转换为Question实体对象方便后续将前端传来的创建或更新题目的信息保存到数据库中对应题目创建或更新的业务操作场景确保题目数据能正确地持久化存储。
List<QuestionSheetVO> listEntityToVO(List<Question> questions);
// 定义的这个方法接收一个List<Question>类型的参数questions也就是题目实体对象的列表。其目的是按照特定的映射规则可能由Mapstruct根据属性名等默认匹配也可后续添加更多@Mapping注解来精确配置将这些题目实体对象转换为QuestionSheetVO视图对象列表用于将题目相关信息以适合在练习相关场景下前端展示的格式由QuestionSheetVO定义进行批量展示比如在展示练习试卷中的所有题目时把从数据库查询出来的题目实体列表转换为适合前端展示的视图对象列表后返回给前端进行展示。
@Mapping(target = "quId",source = "id")
// 使用@Mapping注解指定属性映射规则这里表示将源对象Question类型中的"id"属性值映射到目标对象QuestionSheetVO类型的"quId"属性上,在将题目实体数据转换为适合在练习场景展示的视图对象数据时,保证题目唯一标识符(以不同属性名在不同对象中体现)能正确对应赋值,使得展示的数据能准确关联到对应的题目实体,符合数据展示和业务逻辑要求。
QuestionSheetVO entityToVO(Question question);
// 该方法接收一个Question实体对象作为参数question其作用是将单个的Question实体按照相应的映射规则转换为QuestionSheetVO视图对象QuestionSheetVO通常包含了更符合在练习场景下前端展示需求的题目相关信息相比于原始的Question实体对象比如对题目内容进行适合练习展示的格式化处理添加练习相关的序号等属性便于前端更友好地展示单个题目在练习中的情况常用于在练习功能模块中展示具体题目的业务场景中。
QuestionVO QuestionToQuestionVO(Question question);
}
// 该方法接收一个Question实体对象作为参数question作用是将Question实体按照一定的映射规则同样可由Mapstruct默认确定或后续精确配置转换为QuestionVO视图对象QuestionVO会对题目实体中的相关信息进行整理和格式化以更适合前端一般性展示题目详情的格式呈现出来比如展示题目详细内容、题目类型、所属知识点等信息便于前端更全面、清晰地展示单个题目的详细情况常用于在需要展示特定题目详细信息的各种业务场景中。
}

@ -1,14 +1,32 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护使代码结构更清晰不同功能的代码能各归其位便于后续开发人员理解和扩展相关功能。
import cn.org.alan.exam.model.entity.Exam;
// 引入Exam实体类它对应数据库中存储考试相关信息的表结构包含像考试名称、考试时间、考试总分、考试的题目集合等具体的属性字段代表了考试在系统中的实际数据存储形式是业务操作中涉及考试数据持久化以及围绕考试展开的各类业务逻辑处理的核心数据对象之一不过在这里暂时未看到直接与当前转换功能相关的使用可能在更广泛的项目业务关联中有作用。
import cn.org.alan.exam.model.entity.Grade;
// 引入Grade实体类对应数据库里存储班级相关信息的表结构涵盖班级名称、班级编号、所属年级等属性是班级数据在系统中的实体表现形式同样在这里暂时未体现与当前转换功能的直接关联但在整个项目大环境下可能涉及到关联查询、统计等业务场景中会有关联使用。
import cn.org.alan.exam.model.entity.Repo;
// 引入Repo实体类推测它是用于存储某种资源库相关信息的实体类比如题库等资源相关的数据存储结构包含如资源的标识、名称、内容等属性代表了对应资源在系统中的实际存储状态是当前这个数据转换功能中涉及到的关键数据源实体后续要对其进行转换操作。
import cn.org.alan.exam.model.vo.GradeVO;
// 引入GradeVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的班级相关信息例如班级名称、班级人数、班级平均成绩等经过整理和格式化后适合前端展示的数据便于前端展示给用户查看班级的详细情况在这里可能与整体的记录相关展示场景有一定关联但暂时未看到直接参与当前转换逻辑的体现。
import cn.org.alan.exam.model.vo.record.ExamRecordVO;
// 引入ExamRecordVO类是一个视图对象VO大概率用于向客户端展示考试记录相关的详细信息比如考生在某场考试中的得分、答题情况、考试时间等具体的考试记录相关展示数据方便前端呈现给用户查看考试相关的历史记录情况在整个项目关于考试记录展示的业务场景中发挥作用。
import cn.org.alan.exam.model.vo.record.ExerciseRecordVO;
// 引入ExerciseRecordVO类同样是视图对象VO应该是用于向客户端展示练习记录相关的特定格式信息例如练习的题目、用户作答情况、练习的时长等与练习记录有关的详细数据便于前端展示给用户查看练习相关的历史情况是当前要转换得到的目标视图对象类型用于展示练习记录相关内容。
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 引入MyBatis Plus框架提供的Page类它用于表示分页数据的结构包含了分页相关的信息如页码、每页记录数、总记录数等以及具体的数据列表在涉及分页查询资源此处是Repo资源相关信息并转换展示格式时会用到这个类型方便对分页数据进行统一管理以及后续的展示格式转换操作使得数据展示更有条理符合常见的分页展示需求。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口。Mapstruct会根据接口中定义的方法以及所添加的映射规则通过类似@Mapping注解等方式来指定具体属性的映射关系不过此处暂时未看到更多细化规则在编译期自动生成对应的实现类以此来实现不同对象之间属性的转换避免了开发者手动编写大量繁琐的对象属性赋值代码提高开发效率。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件使得Spring容器在进行组件扫描时能够发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让这个接口能更好地融入到整个Spring项目的体系架构中方便与其他Spring组件协作完成业务功能例如在需要进行数据转换的业务逻辑层方便地注入并调用这个接口的方法。
/**
* @Author Alan
@ -16,9 +34,11 @@ import org.springframework.stereotype.Component;
* @Date 2024/4/30 11:39 AM
*/
@Component
// 将该接口标记为Spring组件意味着Spring容器会对其进行管理这样在其他业务逻辑类中如果需要使用这个接口所定义的数据转换功能就可以通过Spring的依赖注入机制轻松获取到该接口的实例然后调用相应的转换方法有助于构建低耦合、易维护的代码结构使得各个功能模块之间的依赖关系更清晰明了。
@Mapper(componentModel = "spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使得生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入、生命周期管理等流程中便于在业务逻辑中灵活地运用这些转换功能与整个Spring项目的运行环境和开发模式相契合方便在不同业务场景下按需调用数据转换逻辑。
public interface RecordConverter {
Page<ExerciseRecordVO> pageRepoEntityToVo(Page<Repo> page);
}
// 定义了一个名为pageRepoEntityToVo的方法它接收一个Page<Repo>类型的参数page这里的Page<Repo>表示包含了Repo实体对象的分页数据结构由MyBatis Plus的Page类型承载包含了资源库实体以及分页相关的信息。该方法的作用是按照特定的映射规则目前代码中未详细展示可能后续在生成的实现类中根据对象属性名等默认匹配或者额外配置来确定将这个包含资源库实体的分页数据转换为包含ExerciseRecordVO视图对象的分页数据结构以便把资源库相关信息以适合前端展示练习记录的格式通过ExerciseRecordVO来定义展示形式返回给客户端常用于分页展示与练习记录相关的信息基于资源库数据转换而来的业务场景例如在展示基于某个资源库生成的练习记录列表分页查询结果时进行这样的数据转换操作。
}

@ -1,11 +1,20 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护使得代码按照功能模块清晰划分便于后续开发人员查找、理解和扩展相关功能代码。
import cn.org.alan.exam.model.entity.Repo;
// 引入Repo实体类它对应数据库中存储资源库相关信息的表结构包含诸如资源库名称、资源库类型例如是题库、素材库等具体类型具体依业务而定、创建者、创建时间等具体的属性字段代表了资源库在系统中的实际数据存储形式是业务操作中涉及资源库数据持久化以及围绕资源库展开的各类业务逻辑处理的核心数据对象是后续进行数据转换操作的数据源。
import cn.org.alan.exam.model.vo.repo.RepoVO;
// 引入RepoVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的资源库相关信息例如对资源库名称进行格式化展示可能添加一些样式、截取合适长度等展示资源库类型的友好描述以及包含一些适合前端展示的关联信息如资源数量等具体看业务需求等经过整理后的适合前端展示的数据便于前端以更好的形式呈现资源库信息给用户查看是数据转换后要生成的目标展示对象类型。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口。Mapstruct会依据接口中定义的方法以及后续可能添加的映射规则通过相关注解指定具体属性的映射关系当前代码里暂时没有更详细配置在编译期自动生成对应的实现类以此来实现不同对象之间属性的转换这样就避免了开发者手动编写大量繁琐的对象属性赋值代码提高了代码开发效率和准确性让数据转换逻辑更加清晰、易于维护。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件使得Spring容器在进行组件扫描时能够发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让这个接口能更好地融入到整个Spring项目的体系架构中方便与其他Spring组件协作完成业务功能例如在业务逻辑层需要进行资源库数据转换操作时能方便地注入并调用这个接口的方法。
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个资源库信息的视图对象列表、多个资源库实体对象列表等情况时会用到便于对一组数据进行统一的操作和传递符合这里将多个资源库实体转换为对应视图对象列表的需求方便进行批量数据转换及展示相关操作。
/**
* @Author WeiJin
@ -13,9 +22,11 @@ import java.util.List;
* @Date 2024/3/28 20:21
*/
@Component
// 将该接口标记为Spring组件意味着Spring容器会对其进行管理其他类可以通过Spring的依赖注入机制获取这个接口的实例进而调用接口中定义的方法来完成对象转换操作这样有助于降低代码之间的耦合度增强代码的可维护性和可扩展性使得整个项目的代码结构更加清晰合理方便在不同的业务场景中灵活运用该接口提供的数据转换功能。
@Mapper(componentModel = "spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使得生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入、生命周期管理等流程中便于在业务逻辑中灵活地运用这些转换功能使其与整个Spring项目的开发模式相契合便于在项目中按需调用数据转换逻辑。
public interface RepoConverter {
List<RepoVO> listEntityToVo(List<Repo> list);
}
// 定义了一个名为listEntityToVo的方法它接收一个List<Repo>类型的参数list也就是资源库实体对象的列表。其作用是按照特定的映射规则可能由Mapstruct根据对象属性名等默认匹配来确定也可以后续通过添加相关注解来进一步精确配置将这些资源库实体对象转换为RepoVO视图对象列表用于将资源库相关信息以适合前端展示的格式由RepoVO定义进行批量展示比如在前端展示多个资源库的基本信息或查询结果时把从数据库查询出来的资源库实体列表转换为适合前端展示的视图对象列表后返回给前端进行展示满足前端对资源库数据展示的需求对应资源库数据批量转换并展示的业务场景。
}

@ -1,12 +1,20 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护让不同功能的代码能按模块划分清晰明了便于后续开发人员查找、理解以及对相关功能进行扩展。
import cn.org.alan.exam.model.entity.UserBook;
// 引入UserBook实体类它对应数据库中存储用户错题本相关信息的表结构包含像错题本对应的用户标识、错题的相关记录例如错题的题目编号、答错次数等具体与错题相关的数据具体依业务而定等属性字段代表了用户错题本在系统中的实际数据存储形式是业务操作中涉及错题本数据持久化以及围绕错题本展开的各类业务逻辑处理的核心数据对象也是后续进行数据转换操作的数据源头。
import cn.org.alan.exam.model.vo.userbook.ReUserExamBookVO;
// 引入ReUserExamBookVO类这是一个视图对象Value ObjectVO用于向客户端展示特定格式的用户错题本中与考试错题相关的信息例如错题对应的考试名称、错题在该考试中的具体呈现形式可能包含题干、正确答案等部分展示信息具体看业务需求等经过整理和格式化后适合前端展示的数据便于前端展示给用户查看错题本里考试错题的详细情况是要通过数据转换生成的目标展示对象类型。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口。Mapstruct会根据接口中定义的方法以及后续可能设定的映射规则可通过相关注解来指定具体属性的映射关系不过当前代码里暂时没有更详细配置在编译期自动生成对应的实现类以此来实现不同对象之间属性的转换这样就避免了开发者手动编写大量繁琐的对象属性赋值代码提高了代码开发效率以及数据转换逻辑的可维护性。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件使得Spring容器在进行组件扫描时能够发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让这个接口能更好地融入到整个Spring项目的体系架构中方便与其他Spring组件协作完成业务功能例如在业务逻辑层处理错题本数据展示相关业务时能便捷地注入并调用这个接口的方法来进行数据转换操作。
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个错题本相关的视图对象列表、多个错题本实体对象列表等情况时会用到便于对一组数据进行统一的操作和传递符合这里将多个用户错题本实体转换为对应视图对象列表的业务场景需求方便进行批量的数据转换及展示操作。
/**
* @Author Alan
@ -14,9 +22,11 @@ import java.util.List;
* @Date 2024/3/29 15:51
*/
@Component
// 将该接口标记为Spring组件意味着Spring容器会对其进行管理其他类可以通过Spring的依赖注入机制获取这个接口的实例进而调用接口中定义的方法来完成对象转换操作这有助于降低代码之间的耦合度增强代码的可维护性和可扩展性使得整个项目的代码结构更加清晰合理便于在不同的业务场景中灵活运用该接口提供的数据转换功能。
@Mapper(componentModel = "spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使得生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入、生命周期管理等流程中便于在业务逻辑中灵活地运用这些转换功能使其与整个Spring项目的开发模式相契合方便在项目中按需调用数据转换逻辑。
public interface UserBookConverter {
List<ReUserExamBookVO> listEntityToVo(List<UserBook> list);
}
// 定义了一个名为listEntityToVo的方法它接收一个List<UserBook>类型的参数list也就是用户错题本实体对象的列表。其作用是按照特定的映射规则可能由Mapstruct根据对象属性名等默认匹配情况来确定也可以后续通过添加相应注解来进一步精确配置将这些用户错题本实体对象转换为ReUserExamBookVO视图对象列表用于将用户错题本相关信息以适合前端展示的格式由ReUserExamBookVO定义进行批量展示比如在前端展示用户错题本里与考试相关的错题信息列表时把从数据库查询出来的用户错题本实体列表转换为适合前端展示的视图对象列表后返回给前端进行展示满足前端对用户错题本数据展示的业务需求对应着将用户错题本实体数据转换为适合前端查看的视图数据的批量转换及展示业务场景。
}

@ -1,12 +1,20 @@
package cn.org.alan.exam.converter;
// 声明该接口所在的包名用于在项目中对类和接口进行分类组织表明此接口属于特定的“exam”项目下与数据转换相关的功能模块所在的包方便代码管理与维护使代码按照功能模块清晰划分便于后续开发人员查找、理解和扩展相关功能代码。
import cn.org.alan.exam.model.entity.User;
// 引入User实体类它对应数据库中存储用户相关信息的表结构包含像用户名、密码、用户角色、用户个人信息如姓名、性别、联系方式等具体依业务而定等具体的属性字段代表了用户在系统中的实际数据存储形式是业务操作中涉及用户数据持久化以及围绕用户展开的各类业务逻辑处理的核心数据对象也是后续进行数据转换操作的目标存储对象类型。
import cn.org.alan.exam.model.form.UserForm;
// 引入UserForm类这是一种数据传输对象Data Transfer ObjectDTO形式的类主要用于在前端与后端之间或者不同业务层之间传递数据它承载着创建或更新用户时前端输入的各种信息例如用户名、密码、确认密码、用户角色选择等需要填写的数据方便进行数据的交互和传递为后续的用户相关业务处理提供数据来源是进行数据转换的数据源对象类型。
import org.mapstruct.Mapper;
// 引入Mapstruct框架提供的Mapper注解用于标识这个接口是一个映射器接口。Mapstruct会依据接口中定义的方法以及后续可能添加的映射规则通过相关注解指定具体属性的映射关系当前代码里暂时没有更详细配置在编译期自动生成对应的实现类以此来实现不同对象之间属性的转换这样就避免了开发者手动编写大量繁琐的对象属性赋值代码提高了代码开发效率以及数据转换逻辑的可维护性。
import org.springframework.stereotype.Component;
// 引入Spring框架的@Component注解用于将这个接口标记为一个Spring组件使得Spring容器在进行组件扫描时能够发现并管理它便于后续在需要的地方通过依赖注入等方式使用该接口对应的功能实现了组件化管理让这个接口能更好地融入到整个Spring项目的体系架构中方便与其他Spring组件协作完成业务功能例如在业务逻辑层处理用户数据创建、更新等业务时能便捷地注入并调用这个接口的方法来进行数据转换操作。
import java.util.List;
// 引入Java标准库中的List接口用于表示列表类型的数据结构在需要返回多个同类型元素如多个用户实体对象列表、多个用户表单数据对象列表等情况时会用到便于对一组数据进行统一的操作和传递符合这里将多个用户表单数据转换为对应用户实体对象列表或者单个用户表单数据转换为单个用户实体对象的批量与单个处理的业务场景需求方便进行数据转换及后续操作。
/**
* @Author WeiJin
@ -14,11 +22,14 @@ import java.util.List;
* @Date 2024/3/29 15:51
*/
@Component
// 将该接口标记为Spring组件意味着Spring容器会对其进行管理其他类可以通过Spring的依赖注入机制获取这个接口的实例进而调用接口中定义的方法来完成对象转换操作这有助于降低代码之间的耦合度增强代码的可维护性和可扩展性使得整个项目的代码结构更加清晰合理便于在不同的业务场景中灵活运用该接口提供的数据转换功能。
@Mapper(componentModel = "spring")
// 使用@Mapper注解声明这是一个映射器接口并指定componentModel属性为"spring"告知Mapstruct生成的实现类要以Spring组件的形式进行管理方便与Spring框架集成使得生成的实现类可以像普通的Spring Bean一样参与到整个项目的依赖注入、生命周期管理等流程中便于在业务逻辑中灵活地运用这些转换功能使其与整个Spring项目的开发模式相契合方便在项目中按需调用数据转换逻辑。
public interface UserConverter {
User fromToEntity(UserForm userForm);
// 定义了一个名为fromToEntity的方法它接收一个UserForm类型的参数userForm其功能是依据一定的映射规则这些规则可能由Mapstruct根据对象属性名等默认情况确定也可以手动配置更精确的映射关系将承载前端输入用户相关数据的UserForm对象转换为User实体对象方便后续将前端传来的创建或更新用户的信息保存到数据库中对应着用户创建或更新的业务操作场景确保用户数据能正确地持久化存储将前端传来的符合展示和交互格式的数据转换为符合数据库存储要求的实体格式数据。
List<User> listFromToEntity(List<UserForm> list);
}
// 定义的这个方法接收一个List<UserForm>类型的参数list也就是用户表单数据对象的列表。其目的是按照特定的映射规则同样可能由Mapstruct根据对象属性名等默认匹配也可后续添加更多精确配置来完善将这些用户表单数据对象批量转换为User实体对象列表用于将前端传来的批量用户创建或更新相关的表单数据如批量导入用户信息等场景转换为可持久化存储的用户实体对象列表便于后续统一进行数据库插入或更新操作满足批量处理用户数据转换及存储的业务需求。
}

@ -3,14 +3,20 @@ package cn.org.alan.exam.mapper;
import cn.org.alan.exam.model.entity.Certificate;
import cn.org.alan.exam.model.form.CertificateForm;
import cn.org.alan.exam.model.vo.certificate.MyCertificateVO;
// 导入MyCertificateVO视图对象类用于封装返回给前端的数据
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类是IPage接口的一个实现用于分页查询
import org.apache.ibatis.annotations.Param;
// 导入Param注解用于MyBatis方法参数注解便于XML配置文件中引用
import org.springframework.stereotype.Repository;
// 导入Repository注解用于标识当前类是一个持久层DAO层的组件
import java.util.List;
// 导入List接口用于处理集合数据
/**
* <p>
@ -21,13 +27,19 @@ import java.util.List;
* @since 2024-03-21
*/
@Repository
// 使用Repository注解将当前接口标识为Spring管理的组件
public interface CertificateMapper extends BaseMapper<Certificate> {
// 声明CertificateMapper接口继承自BaseMapper<Certificate>,提供基本的证书数据操作
// 自定义查询方法根据分页信息、用户ID、考试名称查询用户的证书信息并返回Page<MyCertificateVO>对象
Page<MyCertificateVO> selectMyCertificate(Page<MyCertificateVO> myCertificateVOPage, Integer pageNum, Integer pageSize, Integer userId,String examName);
// 自定义统计方法根据用户ID、证书名称、认证单位统计符合条件的证书数量
int countByCondition(Integer userId, String certificateName, String certificationUnit);
// 自定义分页查询方法根据用户ID、证书名称、认证单位、偏移量、页面大小查询证书ID列表
List<Integer> selectCertificateIdsPage(Integer userId, String certificateName, String certificationUnit, Integer offset,Integer pageSize);
// 自定义批量查询方法根据证书ID列表批量查询证书信息
List<Certificate> batchSelectByIds(List<Integer> missIds);
}
}

@ -1,24 +1,25 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前接口所在的包路径
import cn.org.alan.exam.model.entity.CertificateUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.org.alan.exam.model.entity.CertificateUser; // 导入CertificateUser实体类代表用户证书关联的数据模型
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入MyBatis-Plus的BaseMapper接口提供基本的CRUD操作
import java.util.List;
import java.util.List; // 导入List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper CertificateUser
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口自哪个版本开始可用
*/
public interface CertificateUserMapper extends BaseMapper<CertificateUser> {
public interface CertificateUserMapper extends BaseMapper<CertificateUser> { // 声明CertificateUserMapper接口继承自BaseMapper<CertificateUser>提供基本的CertificateUser数据操作
/**
* id
* @param userIds id
* @return
*/
Integer deleteByUserIds(List<Integer> userIds);
}
*
* @param userIds idID
* @return
*/ // 方法注释,描述方法的功能、参数和返回值
Integer deleteByUserIds(List<Integer> userIds); // 自定义删除方法根据用户ID列表批量删除证书用户关联记录
}

@ -1,50 +1,71 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前接口所在的包路径
import cn.org.alan.exam.model.entity.Exam;
import cn.org.alan.exam.model.entity.ExamGrade;
import cn.org.alan.exam.model.vo.exam.ExamGradeListVO;
import cn.org.alan.exam.model.vo.score.GradeScoreVO;
import cn.org.alan.exam.model.vo.stat.GradeExamVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import cn.org.alan.exam.model.entity.Exam; // 导入Exam实体类本接口未直接使用可能是遗留或用于其他目的
import cn.org.alan.exam.model.entity.ExamGrade; // 导入ExamGrade实体类代表考试与班级的关联数据模型
import cn.org.alan.exam.model.vo.exam.ExamGradeListVO; // 导入ExamGradeListVO视图对象类用于封装考试班级列表的数据
import cn.org.alan.exam.model.vo.score.GradeScoreVO; // 导入GradeScoreVO视图对象类用于封装班级分数的数据
import cn.org.alan.exam.model.vo.stat.GradeExamVO; // 导入GradeExamVO视图对象类本接口未直接使用可能是遗留或用于其他目的
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类本接口未直接使用IPage的实现类但通常不直接引用
import org.apache.ibatis.annotations.Param; // 导入Param注解用于MyBatis方法参数注解
import java.util.List;
import java.util.List; // 导入List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper ExamGrade
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口自哪个版本开始可用
*/
public interface ExamGradeMapper extends BaseMapper<ExamGrade> {
public interface ExamGradeMapper extends BaseMapper<ExamGrade> { // 声明ExamGradeMapper接口继承自BaseMapper<ExamGrade>提供基本的ExamGrade数据操作
// 自定义方法为指定考试ID添加多个班级ID的关联记录
Integer addExamGrade(Integer id, List<Integer> gradeIds);
// 自定义方法根据考试ID删除考试与班级的关联记录
Integer delExamGrade(Integer id);
/**
*
* @param page
* @param roleId
* @return
*
* @param page
* @param examTitle
* @param userId ID
* @param gradeId ID
* @param roleId ID
* @return
*/
IPage<GradeScoreVO> getExamGrade(IPage<GradeScoreVO> page,String examTitle,Integer userId,Integer gradeId,Integer roleId);
IPage<GradeScoreVO> getExamGrade(IPage<GradeScoreVO> page, String examTitle, Integer userId, Integer gradeId, Integer roleId);
/**
* id
* @param id id
* @return
* ID
*
* @param id ID
* @return
*/
Integer selectClassSize(Integer id);
/**
*
*
* @param examPage
* @param userId ID
* @param title
* @return
*/
IPage<ExamGradeListVO> selectClassExam(IPage<ExamGradeListVO> examPage, Integer userId, String title);
IPage<ExamGradeListVO> selectClassExam(IPage<ExamGradeListVO> examPage, Integer userId,String title);
IPage<ExamGradeListVO> selectAdminClassExam(IPage<ExamGradeListVO> examPage, Integer userId,String title);
/**
*
*
* @param examPage
* @param userId ID
* @param title
* @return
*/
IPage<ExamGradeListVO> selectAdminClassExam(IPage<ExamGradeListVO> examPage, Integer userId, String title);
}
}

@ -1,49 +1,65 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前接口所在的包路径
import cn.org.alan.exam.model.entity.Exam;
import cn.org.alan.exam.model.vo.answer.AnswerExamVO;
import cn.org.alan.exam.model.vo.record.ExamRecordVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import cn.org.alan.exam.model.entity.Exam; // 导入Exam实体类代表考试数据模型
import cn.org.alan.exam.model.vo.answer.AnswerExamVO; // 导入AnswerExamVO视图对象类用于封装考试答题相关的数据
import cn.org.alan.exam.model.vo.record.ExamRecordVO; // 导入ExamRecordVO视图对象类用于封装考试记录相关的数据
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类是IPage接口的一个实现用于分页查询的具体实现
import org.apache.ibatis.annotations.Param; // 导入Param注解用于MyBatis方法参数注解
import org.springframework.stereotype.Repository; // 导入Repository注解本接口未直接使用通常用于标注数据访问层组件
import java.util.List;
import java.util.List; // 导入List接口用于处理集合数据
/**
* Mapper
* Mapper Exam
*
* @author Alan
* @since 2024-03-21
* @author Alan // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口自哪个版本开始可用
*/
@Repository // 注解通常用于标注数据访问层组件但Mapper接口一般通过MyBatis框架扫描不需要显式添加
public interface ExamMapper extends BaseMapper<Exam> { // 声明ExamMapper接口继承自BaseMapper<Exam>提供基本的Exam数据操作
public interface ExamMapper extends BaseMapper<Exam> {
// 自定义方法根据考试ID列表删除多个考试记录
int deleteExams(List<Integer> examIds);
// 自定义方法根据考试ID列表删除多个考试与班级的关联记录可能是业务逻辑上的关联不一定直接对应数据库表
int deleteExamGrades(List<Integer> examIds);
// 自定义方法根据考试ID列表删除多个考试资源如试题、答案等
int deleteExamRepos(List<Integer> examIds);
// 自定义方法根据考试ID列表删除多个考试与试题的关联记录
int deleteExamQuestions(List<Integer> examIds);
/**
*
*
* @param userIds id
* @return
* @param userIds ID
* @return
*/
Integer deleteByUserIds(List<Integer> userIds);
/**
* id
* ID
*
* @param userId
* @return
* @param page
* @param userId ID
* @param role
* @param examName
* @return
*/
IPage<AnswerExamVO> selectMarkedList(@Param("page") IPage<AnswerExamVO> page, @Param("userId") Integer userId,String role,String examName);
IPage<AnswerExamVO> selectMarkedList(@Param("page") IPage<AnswerExamVO> page, @Param("userId") Integer userId, String role, String examName);
Page<ExamRecordVO> getExamRecordPage(Page<ExamRecordVO> examPage, Integer userId,String examName);
}
/**
*
*
* @param examPage 使Page<ExamRecordVO>
* @param userId ID
* @param examName
* @return
*/
Page<ExamRecordVO> getExamRecordPage(Page<ExamRecordVO> examPage, Integer userId, String examName);
// 注意在实际使用中通常建议统一使用IPage接口作为分页对象的类型以避免具体实现类的依赖。
// 如果MyBatis-Plus的版本或配置允许可以考虑将getExamRecordPage方法的参数和返回值类型改为IPage<ExamRecordVO>。
}

@ -1,45 +1,46 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前接口所在的包路径
import cn.org.alan.exam.model.entity.ExamQuAnswer;
import cn.org.alan.exam.model.vo.answer.UserAnswerDetailVO;
import cn.org.alan.exam.model.vo.exam.ExamQuAnswerExtVO;
import cn.org.alan.exam.model.vo.score.QuestionAnalyseVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.org.alan.exam.model.entity.ExamQuAnswer; // 导入ExamQuAnswer实体类代表考试问题回答的数据模型
import cn.org.alan.exam.model.vo.answer.UserAnswerDetailVO; // 导入UserAnswerDetailVO视图对象类用于封装用户回答主观题的信息
import cn.org.alan.exam.model.vo.exam.ExamQuAnswerExtVO; // 导入ExamQuAnswerExtVO视图对象类用于扩展封装考试问题回答的信息
import cn.org.alan.exam.model.vo.score.QuestionAnalyseVO; // 导入QuestionAnalyseVO视图对象类用于封装试题分析的信息
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入MyBatis-Plus的BaseMapper接口提供基本的CRUD操作
import java.util.List;
import java.util.List; // 导入List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper ExamQuAnswer
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口自哪个版本开始可用
*/
public interface ExamQuAnswerMapper extends BaseMapper<ExamQuAnswer> {
public interface ExamQuAnswerMapper extends BaseMapper<ExamQuAnswer> { // 声明ExamQuAnswerMapper接口继承自BaseMapper<ExamQuAnswer>提供基本的ExamQuAnswer数据操作
List<ExamQuAnswerExtVO> list(String examId, String questionId);
// 自定义方法根据考试ID和试题ID列表查询考试问题回答信息扩展信息
List<ExamQuAnswerExtVO> list(String examId, String questionId); // 注意这里的参数类型通常为Integer除非有特定原因使用String
/**
*
* @param examId id
* @param questionId id
* @return
* @param examId ID
* @param questionId ID
* @return QuestionAnalyseVO
*/
QuestionAnalyseVO questionAnalyse(Integer examId, Integer questionId);
QuestionAnalyseVO questionAnalyse(Integer examId, Integer questionId); // 根据考试ID和试题ID查询试题作答分析信息
/**
*
* @param userIds id
* @return
* @param userIds ID
* @return
*/
Integer deleteByUserIds(List<Integer> userIds);
Integer deleteByUserIds(List<Integer> userIds); // 根据用户ID列表删除对应的考试作答记录
/**
*
* @param userId id
* @param examId id
* @return
* @param userId ID
* @param examId ID
* @return List<UserAnswerDetailVO>
*/
List<UserAnswerDetailVO> selectUserAnswer(Integer userId, Integer examId);
}
List<UserAnswerDetailVO> selectUserAnswer(Integer userId, Integer examId); // 根据用户ID和考试ID查询用户回答主观题的信息
}

@ -1,16 +1,25 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前接口所在的包路径
import cn.org.alan.exam.model.entity.ExamQuestion;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.org.alan.exam.model.entity.ExamQuestion; // 导入ExamQuestion实体类该类代表考试题目的数据模型
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入MyBatis-Plus的BaseMapper接口提供基本的CRUD操作
import java.util.List;
import java.util.Map;
import java.util.List; // 导入List接口用于处理集合数据
import java.util.Map; // 导入Map接口用于存储键值对数据
/**
* @Author Alan
* @Version
* @Date 2024/4/7 3:49 PM
* @Author Alan // 声明当前接口的作者
* @Version // 版本信息缺失,通常这里会指定接口的版本号或者留空
* @Date 2024/4/7 3:49 PM // 声明当前接口的创建日期和时间
*/
public interface ExamQuestionMapper extends BaseMapper<ExamQuestion> {
public interface ExamQuestionMapper extends BaseMapper<ExamQuestion> { // 声明ExamQuestionMapper接口继承自BaseMapper<ExamQuestion>提供基本的ExamQuestion数据操作
// 自定义方法,用于插入考试题目信息
// 参数说明:
// examId - 考试ID用于标识需要插入题目的考试
// quType - 题目类型,用于区分不同类型的题目(如单选题、多选题、判断题等)
// quScore - 题目分数,表示该题目的分值
// questionIdsAndSorts - 题目ID和排序的列表其中每个Map对象包含题目ID作为键和对应的排序值作为值
int insertQuestion(Integer examId, Integer quType, Integer quScore, List<Map<String, Object>> questionIdsAndSorts);
}
// 方法返回值为int类型通常表示插入操作影响的行数但在这里可能主要用于表示操作是否成功如返回1表示成功0或负数表示失败
// 注意这里的业务逻辑可能需要根据实际情况调整例如题目ID和排序的存储方式、如何确保数据的一致性等
}

@ -1,16 +1,25 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前Mapper接口所在的包路径
// 导入ExamRepo实体类该类是与数据库表对应的Java对象用于表示考试资源仓库的实体
import cn.org.alan.exam.model.entity.ExamRepo;
// 导入MyBatis-Plus提供的BaseMapper接口它提供了基础的CRUD操作方法
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper
* Mapper
* </p>
* <p>
* BaseMapperBaseMapper
* CRUD
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口的创建日期或版本号
*/
public interface ExamRepoMapper extends BaseMapper<ExamRepo> {
public interface ExamRepoMapper extends BaseMapper<ExamRepo> { // 声明ExamRepoMapper接口它继承自BaseMapper<ExamRepo>因此可以对ExamRepo实体进行基础的数据库操作
}
// 由于此接口仅继承了BaseMapper并未定义额外的方法
// 因此这里没有额外的代码需要解释。所有对ExamRepo实体的数据库操作都将通过BaseMapper中定义的方法来完成。
}

@ -1,24 +1,29 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前Mapper接口所在的包路径
// 导入ExerciseRecord实体类该类是与数据库表对应的Java对象用于表示用户的练习作答记录
import cn.org.alan.exam.model.entity.ExerciseRecord;
// 导入MyBatis-Plus提供的BaseMapper接口它提供了基础的CRUD操作方法
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入Java的List接口用于处理用户ID的集合
import java.util.List;
/**
* <p>
* Mapper
* Mapper ExerciseRecord
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口的创建日期或版本号
*/
public interface ExerciseRecordMapper extends BaseMapper<ExerciseRecord> {
public interface ExerciseRecordMapper extends BaseMapper<ExerciseRecord> { // 声明ExerciseRecordMapper接口它继承自BaseMapper<ExerciseRecord>因此可以对ExerciseRecord实体进行基础的数据库操作
/**
*
* @param userIds id
* @return
*
*
* @param userIds IDID
* @return 00
*/
Integer deleteByUserIds(List<Integer> userIds);
}
Integer deleteByUserIds(List<Integer> userIds); // 定义一个自定义方法用于根据用户ID列表删除对应的练习作答记录并返回被影响的记录数
}

@ -1,24 +1,29 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前Mapper接口所在的包路径
// 导入GradeExercise实体类该类是与数据库表对应的Java对象用于表示用户创建的可供学生练习的题库关联信息
import cn.org.alan.exam.model.entity.GradeExercise;
// 导入MyBatis-Plus提供的BaseMapper接口它提供了基础的CRUD操作方法
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入Java的List接口用于处理用户ID的集合
import java.util.List;
/**
* <p>
* Mapper
* Mapper GradeExercise
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口的创建日期或版本号
*/
public interface GradeExerciseMapper extends BaseMapper<GradeExercise> {
public interface GradeExerciseMapper extends BaseMapper<GradeExercise> { // 声明GradeExerciseMapper接口它继承自BaseMapper<GradeExercise>因此可以对GradeExercise实体进行基础的数据库操作
/**
*
* @param userIds id
* @return
*
*
* @param userIds IDID
* @return 00
*/
Integer deleteByUserIds(List<Integer> userIds);
}
Integer deleteByUserIds(List<Integer> userIds); // 定义一个自定义方法用于根据用户ID列表删除对应的题库关联记录并返回被影响的记录数
}

@ -1,32 +1,44 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前Mapper接口所在的包路径
// 导入Grade实体类该类是与数据库表对应的Java对象用于表示成绩信息
import cn.org.alan.exam.model.entity.Grade;
// 导入GradeVO类这是一个值对象Value Object通常用于封装从数据库查询后需要展示给前端的数据
import cn.org.alan.exam.model.vo.GradeVO;
// 导入MyBatis-Plus提供的BaseMapper接口它提供了基础的CRUD操作方法
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入MyBatis-Plus分页插件提供的Page类用于实现分页查询功能
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入Spring框架的Repository注解用于标识这是一个数据访问层的组件
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* <p>
* Mapper
* Mapper Grade
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口的创建日期或版本号
*/
@Repository
public interface GradeMapper extends BaseMapper<Grade> {
@Repository // 使用@Repository注解标识这是一个数据访问层的组件Spring会将其识别为一个Bean并进行管理
public interface GradeMapper extends BaseMapper<Grade> { // 声明GradeMapper接口它继承自BaseMapper<Grade>因此可以对Grade实体进行基础的数据库操作
// 根据用户ID列表查询对应的成绩ID列表
List<Integer> selectIdsByUserIds(List<Integer> userIds);
// 根据用户ID列表删除对应的成绩记录
Integer deleteByUserId(List<Integer> userIds);
Page<GradeVO> selectGradePage(Page<GradeVO> page, Integer userId , String gradeName, Integer role);
// 分页查询成绩信息返回Page<GradeVO>对象,包含分页信息和查询结果
Page<GradeVO> selectGradePage(Page<GradeVO> page, Integer userId, String gradeName, Integer role);
// 根据用户ID、角色、成绩名称和分页参数偏移量和页面大小查询成绩ID列表用于分页显示
List<Integer> selectGradeIdsPage(Integer userId, Integer role, String gradeName, int offset, Integer pageSize);
// 根据ID列表批量查询成绩信息返回GradeVO对象列表
List<GradeVO> batchSelectByIds(List<Integer> missIds);
// 根据用户ID、成绩名称和角色统计符合条件的成绩记录数
int countByCondition(Integer userId, String gradeName, Integer role);
}
}

@ -1,31 +1,35 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前Mapper接口所在的包路径
// 导入ManualScore实体类该类是与数据库表对应的Java对象用于表示用户批改的主观题分数信息
import cn.org.alan.exam.model.entity.ManualScore;
// 导入MyBatis-Plus提供的BaseMapper接口它提供了基础的CRUD操作方法
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
import java.util.List; // 导入Java的List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper ManualScore
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口的创建日期或版本号
*/
public interface ManualScoreMapper extends BaseMapper<ManualScore> {
public interface ManualScoreMapper extends BaseMapper<ManualScore> { // 声明ManualScoreMapper接口它继承自BaseMapper<ManualScore>因此可以对ManualScore实体进行基础的数据库操作
/**
*
* @param userIds id
* @return
* ID
*
* @param userIds IDID
* @return 00
*/
Integer deleteByUserIds(List<Integer> userIds);
Integer deleteByUserIds(List<Integer> userIds); // 定义一个自定义方法用于根据用户ID列表删除对应的主观题分数记录并返回被影响的记录数
/**
*
* @param manualScores
* @return
*
*
* @param manualScores
* @return 00
*/
Integer insertList(List<ManualScore> manualScores);
}
Integer insertList(List<ManualScore> manualScores); // 定义一个自定义方法,用于批量添加主观题分数记录到数据库,并返回被影响的记录数
}

@ -1,27 +1,38 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 声明当前Mapper接口所在的包路径
// 导入Grade实体类该类表示学生的成绩信息
import cn.org.alan.exam.model.entity.Grade;
// 导入NoticeGrade实体类该类表示公告与班级的关联信息
import cn.org.alan.exam.model.entity.NoticeGrade;
// 导入MyBatis-Plus提供的BaseMapper接口提供基础的CRUD操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
import java.util.List; // 导入Java的List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper NoticeGrade
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口的创建日期或版本号
*/
public interface NoticeGradeMapper extends BaseMapper<NoticeGrade> {
public interface NoticeGradeMapper extends BaseMapper<NoticeGrade> { // 声明NoticeGradeMapper接口继承自BaseMapper<NoticeGrade>提供基础的CRUD操作
/**
* id
* @param noticeIds id
* @return
* ID
*
* @param noticeIds IDID
* @return 00
*/
Integer deleteByNoticeIds(List<Integer> noticeIds);
Integer deleteByNoticeIds(List<Integer> noticeIds); // 定义一个自定义方法用于根据公告ID列表删除对应的公告与班级关联记录并返回被影响的记录数
int addNoticeGrade(Integer noticeId, List<Grade> grades);
}
/**
*
*
* @param noticeId ID
* @param grades
* @return 00
*/
int addNoticeGrade(Integer noticeId, List<Grade> grades); // 定义一个自定义方法,用于添加公告与成绩的关联记录到数据库,并返回被影响的记录数
}

@ -1,45 +1,94 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 指定当前Mapper接口所在的包路径
// 导入Notice实体类表示公告信息
import cn.org.alan.exam.model.entity.Notice;
// 导入NoticeVO值对象类通常用于封装传递给前端的数据可能包含Notice实体类的数据以及其他相关信息
import cn.org.alan.exam.model.vo.NoticeVO;
// 导入MyBatis-Plus提供的BaseMapper接口提供基础的CRUD操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入MyBatis-Plus分页插件提供的Page类用于分页查询
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.List; // 导入Java的List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper Notice
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明当前接口的作者
* @since 2024-03-21 // 声明当前接口的创建日期或版本号
*/
public interface NoticeMapper extends BaseMapper<Notice> {
public interface NoticeMapper extends BaseMapper<Notice> { // 声明NoticeMapper接口继承自BaseMapper<Notice>提供基础的CRUD操作
int removeNotice(List<Integer> noticeIds);
/**
* ID
*
* @param noticeIds IDID
* @return 00
*/
int removeNotice(List<Integer> noticeIds); // 定义一个自定义方法用于根据公告ID列表删除公告记录并返回被影响的记录数
/**
*
* @param userIds id
* @return
*
*
* @param userIds IDID
* @return 00
*/
Integer deleteByUserIds(List<Integer> userIds);
Integer deleteByUserIds(List<Integer> userIds); // 定义一个自定义方法用于根据用户ID列表删除用户创建的公告记录并返回被影响的记录数
/**
* idids
* @param userIds id
* @return
* IDID
*
* @param userIds IDIDID
* @return ID
*/
List<Integer> selectIdsByUserIds(List<Integer> userIds);
List<Integer> selectIdsByUserIds(List<Integer> userIds); // 定义一个自定义方法用于根据用户ID列表查询并返回对应的公告ID列表
Page<NoticeVO> selectNewNoticePage(Page<NoticeVO> page, Integer userId);
/**
*
*
* @param page
* @param userId ID
* @return NoticeVOPage
*/
Page<NoticeVO> selectNewNoticePage(Page<NoticeVO> page, Integer userId); // 定义一个自定义方法用于分页查询并返回用户的新公告信息结果封装在NoticeVO对象中
int countByCondition(Integer userId, String title);
/**
*
*
* @param userId IDID
* @param title
* @return
*/
int countByCondition(Integer userId, String title); // 定义一个自定义方法用于根据用户ID和公告标题统计公告数量并返回结果
List<Integer> selectNoticeIdsPage(Integer userId, String title, int offset, Integer pageSize);
/**
* ID
*
* @param userId IDIDID
* @param title
* @param offset
* @param pageSize
* @return ID
*/
List<Integer> selectNoticeIdsPage(Integer userId, String title, int offset, Integer pageSize); // 定义一个自定义方法用于分页查询并返回符合条件的公告ID列表
List<NoticeVO> batchSelectByIds(List<Integer> missIds);
/**
*
*
* @param missIds IDID
* @return NoticeVO
*/
List<NoticeVO> batchSelectByIds(List<Integer> missIds); // 定义一个自定义方法用于批量查询并返回对应的公告信息结果封装在NoticeVO对象中
List<Integer> selectNewNoticeIdsPage(Integer userId, int offset, Integer pageSize);
}
/**
* ID
*
* @param userId IDID
* @param offset
* @param pageSize
* @return ID
*/
List<Integer> selectNewNoticeIdsPage(Integer userId, int offset, Integer pageSize); // 定义一个自定义方法用于分页查询并返回用户的新公告ID列表
}

@ -1,52 +1,57 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 声明包路径,指明当前接口所在的包
import cn.org.alan.exam.model.entity.Option;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.org.alan.exam.model.entity.Option; // 导入Option实体类代表试题选项的数据模型
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入BaseMapper接口提供基础的CRUD操作
import java.util.List;
import java.util.List; // 导入List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper Option
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明接口作者
* @since 2024-03-21 // 声明接口创建日期或版本号
*/
public interface OptionMapper extends BaseMapper<Option> {
public interface OptionMapper extends BaseMapper<Option> { // 声明OptionMapper接口继承自BaseMapper<Option>提供基础的CRUD操作
/**
*
* @param options
* @return
*
*
* @param options Option
* @return
*/
Integer insertBatch(List<Option> options);
Integer insertBatch(List<Option> options); // 定义一个自定义方法,用于批量添加选项到数据库,并返回影响的记录数
/**
* id
* @param quIdList id
* @return
* ID
*
* @param quIdList IDID
* @return
*/
Integer deleteBatchByQuIds(List<Integer> quIdList);
Integer deleteBatchByQuIds(List<Integer> quIdList); // 定义一个自定义方法用于根据试题ID列表批量删除选项并返回影响的记录数
/**
* id
* @param id id
* @return
* ID
*
* @param id ID
* @return List<Option>
*/
List<Option> selectAllByQuestionId(Integer id);
/**
* id
* @param id id
* @return
List<Option> selectAllByQuestionId(Integer id); // 定义一个自定义方法用于根据试题ID查询并返回其所有选项的集合
/**
* ID
*
* @param id ID
* @return List<Option>
*/
List<Option> selectByQuestionId(Integer id);
List<Option> selectByQuestionId(Integer id); // 定义一个自定义方法用于根据试题ID查询并返回其所有选项不包含是否正确信息的集合
/**
* Id
* @param optionIds Id
* @return
* ID
*
* @param optionIds IDID
* @return ID
*/
Integer selectRightCountByIds(List<Integer> optionIds);
}
Integer selectRightCountByIds(List<Integer> optionIds); // 定义一个自定义方法用于根据选项ID列表查询并返回正确的选项个数
}

@ -1,74 +1,114 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 声明包路径包含当前Mapper接口
import cn.org.alan.exam.model.entity.Question;
import cn.org.alan.exam.model.vo.QuestionVO;
import cn.org.alan.exam.model.vo.exercise.QuestionSheetVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import cn.org.alan.exam.model.entity.Question; // 导入Question实体类代表试题的数据模型
import cn.org.alan.exam.model.vo.QuestionVO; // 导入QuestionVO类用于视图层展示试题的详细信息
import cn.org.alan.exam.model.vo.exercise.QuestionSheetVO; // 导入QuestionSheetVO类用于表示用户的试题答题情况
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入BaseMapper接口提供基础的CRUD操作
import com.baomidou.mybatisplus.core.metadata.IPage; // 导入IPage接口用于分页查询
import java.util.List;
import java.util.List; // 导入List接口用于处理集合数据
/**
* <p>
* Mapper
* Mapper Question
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 声明接口作者
* @since 2024-03-21 // 声明接口创建日期或版本号
*/
public interface QuestionMapper extends BaseMapper<Question> {
public interface QuestionMapper extends BaseMapper<Question> { // 声明QuestionMapper接口继承自BaseMapper<Question>提供基础的CRUD操作
/**
*
*
* @param page
* @param content
* @param repoId id
* @param type
* @param userId id
* @return
* @param page
* @param content
* @param repoId ID
* @param type
* @param userId ID
* @return IPage<QuestionVO>
*/
IPage<QuestionVO> pagingQuestion(IPage<QuestionVO> page, String content, Integer repoId, Integer type, Integer userId);
/**
* id
* @param id id
* @return
* ID
*
* @param id ID
* @return QuestionVO
*/
QuestionVO selectSingle(Integer id);
/**
*
* @param userIds id
* @return
*
* @param userIds ID
* @return
*/
Integer deleteByUserIds(List<Integer> userIds);
/**
* id
* @param userIds id
* @return
* ID
*
* @param userIds ID
* @return ID
*/
List<Integer> selectIdsByUserIds(List<Integer> userIds);
/**
* Id
* @param repoId Id
* @param quType
* @param userId Id
* @return
* ID
*
* @param repoId ID
* @param quType
* @param userId ID
* @return QuestionSheetVO
*/
List<QuestionSheetVO> selectQuestionSheet(Integer repoId, Integer quType, Integer userId);
/**
* ID
*
* @param id ID
* @return QuestionVOselectSingle
*/
QuestionVO selectDetail(Integer id);
/**
* ID
*
* @param list ID
* @return void
*/
void deleteBatchIdsQu(List<Integer> list);
/**
*
*
* @param userId ID
* @param title
* @param type
* @param repoId ID
* @return
*/
int countByCondition(Integer userId, String title, Integer type, Integer repoId);
/**
* ID
*
* @param userId ID
* @param title
* @param type
* @param repoId ID
* @param offset
* @param pageSize
* @return ID
*/
List<Integer> selectQuestionIdsPage(Integer userId, String title, Integer type, Integer repoId, int offset, Integer pageSize);
/**
* ID
*
* @param missIds ID
* @return QuestionVO
*/
List<QuestionVO> batchSelectByIds(List<Integer> missIds);
}
}

@ -1,30 +1,30 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 定义mapper接口所在的包
import cn.org.alan.exam.model.entity.Repo;
import cn.org.alan.exam.model.vo.repo.RepoListVO;
import cn.org.alan.exam.model.vo.repo.RepoVO;
import cn.org.alan.exam.model.vo.exercise.ExerciseRepoVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import cn.org.alan.exam.model.entity.Repo; // 引入Repo实体类
import cn.org.alan.exam.model.vo.repo.RepoListVO; // 引入RepoListVO视图对象
import cn.org.alan.exam.model.vo.repo.RepoVO; // 引入RepoVO视图对象
import cn.org.alan.exam.model.vo.exercise.ExerciseRepoVO; // 引入ExerciseRepoVO视图对象
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 引入MyBatis Plus的BaseMapper接口
import com.baomidou.mybatisplus.core.metadata.IPage; // 引入MyBatis Plus的分页接口
import org.apache.ibatis.annotations.Param; // 引入MyBatis的Param注解用于传递参数
import org.springframework.stereotype.Repository; // 引入Spring的Repository注解标记为持久层组件
import java.util.List;
import java.util.List; // 引入Java的List接口
/**
* @author WeiJin
* @since 2024-03-21
*/
@Repository
public interface RepoMapper extends BaseMapper<Repo> {
*/ // 类注释,说明作者和创建时间
@Repository // 标记为Spring的持久层组件
public interface RepoMapper extends BaseMapper<Repo> { // 定义RepoMapper接口继承BaseMapper提供基础的CRUD操作
/**
*
*
* @param page
* @param title
* @param userId
* @return
* @param page
* @param title
* @param userId ID
* @return
*/
IPage<RepoVO> pagingRepo(@Param("page") IPage<RepoVO> page, @Param("title") String title,
@Param("userId") Integer userId);
@ -32,20 +32,27 @@ public interface RepoMapper extends BaseMapper<Repo> {
/**
*
*
* @param userIds id
* @return
* @param userIds id
* @return
*/
Integer deleteByUserIds(List<Integer> userIds);
/**
*
*
* @param page
* @param title
* @return
* @param page
* @param title
* @return ExerciseRepoVO
*/
IPage<ExerciseRepoVO> selectRepo(IPage<ExerciseRepoVO> page,
String title);
/**
* ID
*
* @param repoTitle
* @param userId ID
* @return RepoListVO
*/
List<RepoListVO> selectRepoList(String repoTitle, int userId);
}
}

@ -1,19 +1,25 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 定义RoleMapper接口所在的包路径
import cn.org.alan.exam.model.entity.Role;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.org.alan.exam.model.entity.Role; // 引入Role实体类该类与数据库中的角色表对应
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 引入MyBatis Plus的BaseMapper接口提供基础的CRUD操作
import java.util.List;
import java.util.List; // 引入Java的List接口用于定义返回结果类型
/**
* <p>
* Mapper
* Mapper Role
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 作者信息
* @since 2024-03-21 // 创建时间
*/
public interface RoleMapper extends BaseMapper<Role> {
public interface RoleMapper extends BaseMapper<Role> { // 定义RoleMapper接口继承BaseMapper<Role>提供对Role实体的基础CRUD操作
List<String> selectCodeById(Integer roleId);
}
/**
* ID
*
* @param roleId ID
* @return
*/
List<String> selectCodeById(Integer roleId); // 自定义查询方法根据角色ID返回对应的角色编码列表
}

@ -1,35 +1,40 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 定义StatMapper接口所在的包路径
import cn.org.alan.exam.model.entity.Grade;
import cn.org.alan.exam.model.vo.stat.AllStatsVO;
import cn.org.alan.exam.model.vo.stat.GradeExamVO;
import cn.org.alan.exam.model.vo.stat.GradeStudentVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import cn.org.alan.exam.model.entity.Grade; // 引入Grade实体类代表年级或班级信息
import cn.org.alan.exam.model.vo.stat.AllStatsVO; // 引入AllStatsVO视图对象可能用于其他统计功能当前接口未使用
import cn.org.alan.exam.model.vo.stat.GradeExamVO; // 引入GradeExamVO视图对象用于封装各班试卷数的统计信息
import cn.org.alan.exam.model.vo.stat.GradeStudentVO; // 引入GradeStudentVO视图对象用于封装各班人数的统计信息
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 引入MyBatis Plus的BaseMapper接口提供基础的CRUD操作
import org.apache.ibatis.annotations.Param; // 引入MyBatis的Param注解用于传递参数
import org.springframework.stereotype.Repository; // 引入Spring的Repository注解标记为持久层组件
import java.util.List;
import java.util.List; // 引入Java的List接口用于定义返回结果类型
/**
* @ Author JinXi
* @ Version 1.0
* @ Date 2024/5/12 14:56
* @ Author JinXi // 作者信息
* @ Version 1.0 // 版本信息
* @ Date 2024/5/12 14:56 // 创建时间
*/
@Repository
public interface StatMapper extends BaseMapper<Grade> {
@Repository // 标记为Spring的持久层组件
public interface StatMapper extends BaseMapper<Grade> { // 定义StatMapper接口继承BaseMapper<Grade>提供对Grade实体的基础CRUD操作
/**
*
* @return
*
* @param roleId ID
* @param id ID
* @return GradeStudentVO
*/
List<GradeStudentVO> StudentGradeCount(@Param("roleId")Integer roleId, Integer id);
List<GradeStudentVO> StudentGradeCount(@Param("roleId") Integer roleId, Integer id);
/**
*
* @return
*
* @param roleId ID
* @param id ID
* @return GradeExamVO
*/
List<GradeExamVO> ExamGradeCount(@Param("roleId") Integer roleId, Integer id);
}
// 注意接口中缺少对AllStatsVO的使用如果后续需要使用应添加相应的方法
}

@ -1,31 +1,46 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 定义UserBookMapper接口所在的包路径
import cn.org.alan.exam.model.entity.UserBook;
import cn.org.alan.exam.model.vo.userbook.UserPageBookVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.org.alan.exam.model.entity.UserBook; // 引入UserBook实体类代表用户的错题本记录
import cn.org.alan.exam.model.vo.userbook.UserPageBookVO; // 引入UserPageBookVO视图对象用于封装分页查询结果
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 引入MyBatis Plus的BaseMapper接口提供基础的CRUD操作
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 引入MyBatis Plus的分页插件Page类用于分页查询
import java.util.List;
import java.util.List; // 引入Java的List接口用于定义返回结果类型或传递参数类型
/**
* <p>
* Mapper
* Mapper UserBook
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 作者信息
* @since 2024-03-21 // 创建时间
*/
public interface UserBookMapper extends BaseMapper<UserBook> {
public interface UserBookMapper extends BaseMapper<UserBook> { // 定义UserBookMapper接口继承BaseMapper<UserBook>提供对UserBook实体的基础CRUD操作
/**
*
* @param userIds id
* @return
*
*
* @param userIds ID
* @return
*/
Integer deleteByUserIds(List<Integer> userIds);
Integer deleteByUserIds(List<Integer> userIds); // 自定义删除方法根据用户ID列表删除对应的错题本记录
/**
*
*
* @param userBookArrayList UserBook
* @return
*/
int addUserBookList(List<UserBook> userBookArrayList); // 自定义添加方法,批量添加错题本记录
int addUserBookList(List<UserBook> userBookArrayList);
Page<UserPageBookVO> selectPageVo(Page<UserPageBookVO> page, String examName, Integer userId,Integer role);
}
/**
*
*
* @param page
* @param examName
* @param userId ID
* @param role ID
* @return
*/
Page<UserPageBookVO> selectPageVo(Page<UserPageBookVO> page, String examName, Integer userId, Integer role); // 自定义分页查询方法根据分页信息、考试名称、用户ID和角色ID查询错题本记录
}

@ -1,17 +1,24 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 定义UserDailyLoginDurationMapper接口所在的包路径
import cn.org.alan.exam.model.entity.UserDailyLoginDuration;
import cn.org.alan.exam.model.entity.UserExerciseRecord;
import cn.org.alan.exam.model.vo.stat.DailyVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.org.alan.exam.model.entity.UserDailyLoginDuration; // 引入UserDailyLoginDuration实体类表示用户的每日登录时长记录
import cn.org.alan.exam.model.entity.UserExerciseRecord; // 引入UserExerciseRecord实体类虽然在此接口中未直接使用可能是为了后续扩展或代码未完全展示
import cn.org.alan.exam.model.vo.stat.DailyVO; // 引入DailyVO视图对象用于封装每日统计信息
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 引入MyBatis Plus的BaseMapper接口提供基础的CRUD操作
import java.util.List;
// 不需要为import语句添加注释
/**
* @Author Alan
* @Version
* @Date 2024/5/28 10:47 PM
* @Author Alan // 作者信息
* @Version // 版本信息(此处未填写具体内容)
* @Date 2024/5/28 10:47 PM // 创建时间
*/
public interface UserDailyLoginDurationMapper extends BaseMapper<UserDailyLoginDuration> {
List<DailyVO> getDaily(Integer userId);
}
public interface UserDailyLoginDurationMapper extends BaseMapper<UserDailyLoginDuration> { // 定义UserDailyLoginDurationMapper接口继承BaseMapper<UserDailyLoginDuration>提供对UserDailyLoginDuration实体的基础CRUD操作
/**
* ID
*
* @param userId ID
* @return DailyVO
*/
List<DailyVO> getDaily(Integer userId); // 自定义查询方法根据用户ID获取用户的每日登录时长统计信息并封装为DailyVO对象列表返回
}

@ -1,66 +1,83 @@
package cn.org.alan.exam.mapper;
package cn.org.alan.exam.mapper; // 定义UserExamsScoreMapper接口所在的包路径
import cn.org.alan.exam.model.entity.UserExamsScore;
import cn.org.alan.exam.model.vo.answer.UncorrectedUserVO;
import cn.org.alan.exam.model.vo.score.ExportScoreVO;
import cn.org.alan.exam.model.vo.score.GradeScoreVO;
import cn.org.alan.exam.model.vo.score.UserScoreVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入相关的实体类和视图对象
import cn.org.alan.exam.model.entity.UserExamsScore; // 引入UserExamsScore实体类表示用户的考试成绩
import cn.org.alan.exam.model.vo.answer.UncorrectedUserVO; // 引入UncorrectedUserVO视图对象用于封装未考试用户的信息
import cn.org.alan.exam.model.vo.score.ExportScoreVO; // 引入ExportScoreVO视图对象用于导出考试成绩
import cn.org.alan.exam.model.vo.score.GradeScoreVO; // 引入GradeScoreVO视图对象用于封装班级用户的成绩分析信息
import cn.org.alan.exam.model.vo.score.UserScoreVO; // 引入UserScoreVO视图对象用于封装分页查询的用户成绩信息
import java.util.List;
// 引入MyBatis Plus的相关类
import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 引入BaseMapper接口提供基础的CRUD操作
import com.baomidou.mybatisplus.core.metadata.IPage; // 引入IPage接口用于分页查询
import java.util.List; // 引入Java的List接口用于定义返回结果类型或传递参数类型
/**
* <p>
* Mapper
* Mapper UserExamsScore
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 作者信息
* @since 2024-03-21 // 创建时间
*/
public interface UserExamsScoreMapper extends BaseMapper<UserExamsScore> {
public interface UserExamsScoreMapper extends BaseMapper<UserExamsScore> { // 定义UserExamsScoreMapper接口继承BaseMapper<UserExamsScore>提供对UserExamsScore实体的基础CRUD操作
/**
*
* @param page
* @param gradeId Id
* @param examTitle
* @param userId Id
* @param roleId Id
* @return
*
* @param page
* @param gradeId Id
* @param examTitle
* @param userId Idnull
* @param roleId Id
* @return
*/
IPage<GradeScoreVO> scoreStatistics(IPage<GradeScoreVO> page ,Integer gradeId, String examTitle,Integer userId,Integer roleId);
IPage<GradeScoreVO> scoreStatistics(IPage<GradeScoreVO> page, Integer gradeId, String examTitle, Integer userId, Integer roleId);
/**
*
*
* @param page
* @param gradeId Id
* @param examId Id
* @param realName
* @return
* @param page
* @param gradeId Id
* @param examId Id
* @param realName null
* @return
*/
IPage<UserScoreVO> pagingScore(IPage<UserScoreVO> page, Integer gradeId, Integer examId, String realName);
/**
*
*
* @param examId Id
* @return
*/
Integer getNumberOfApplicants(Integer examId);
/**
*
*
* @param examId Id
* @return
*/
Integer getCorrectedPaper(Integer examId);
/**
*
*
*
* @param examId id
* @param gradeId id
* @return
* @param examId id
* @param gradeId idnull
* @return idid
*/
List<ExportScoreVO> selectScores(Integer examId, Integer gradeId);
/**
* id
*
* @param page
* @param examId id
* @return
* @param page
* @param examId id
* @param realName null
* @return
*/
IPage<UncorrectedUserVO> uncorrectedUser(IPage<UncorrectedUserVO> page, Integer examId,String realName);
}
IPage<UncorrectedUserVO> uncorrectedUser(IPage<UncorrectedUserVO> page, Integer examId, String realName);
}

@ -1,16 +1,24 @@
// 定义UserExerciseRecordMapper接口所在的包路径
package cn.org.alan.exam.mapper;
// 引入UserExerciseRecord实体类该类表示用户的练习记录
import cn.org.alan.exam.model.entity.UserExerciseRecord;
// 引入MyBatis Plus的BaseMapper接口它提供了基础的CRUD操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper
* Mapper UserExerciseRecord
* </p>
* <p>
* BaseMapper<UserExerciseRecord>UserExerciseRecordCRUD
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 作者信息
* @since 2024-03-21 // 接口创建时间
*/
public interface UserExerciseRecordMapper extends BaseMapper<UserExerciseRecord> {
}
// 此处为空因为该接口仅继承了BaseMapper未定义额外的方法。
// 若需要自定义数据库操作方法,可在此处添加。
}

@ -1,34 +1,43 @@
// 定义UserMapper接口所在的包路径
package cn.org.alan.exam.mapper;
// 引入User实体类代表用户信息
import cn.org.alan.exam.model.entity.User;
// 引入ClassCountResult类用于统计和分组的结果
import cn.org.alan.exam.model.form.count.ClassCountResult;
// 引入UserVO类用于视图层展示的用户信息
import cn.org.alan.exam.model.vo.UserVO;
// 引入MyBatis Plus的BaseMapper接口提供基础的CRUD操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 引入MyBatis Plus的IPage接口用于分页查询
import com.baomidou.mybatisplus.core.metadata.IPage;
// 引入Spring的Repository注解标记这是一个持久层组件
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* <p>
* Mapper
* Mapper User
* </p>
*
* @author WeiJin
* @since 2024-03-21
* @author WeiJin // 作者信息
* @since 2024-03-21 // 接口创建时间
*/
@Repository
public interface UserMapper extends BaseMapper<User> {
@Repository // 标记为Spring的Repository组件
public interface UserMapper extends BaseMapper<User> { // 继承BaseMapper<User>提供User的基础CRUD操作
// 已被注释的方法原意是根据id获取用户列表但通常id应唯一此处逻辑可能有误
// List<User> getText(Integer id);
// 根据用户ID列表删除用户等级信息注意方法名可能不完全准确反映其功能需根据业务逻辑确认
Integer removeUserGrade(List<Integer> userIds);
/**
*
*
* @param list
* @return
* @param list
* @return
*/
Integer insertBatchUser(List<User> list);
@ -36,40 +45,51 @@ public interface UserMapper extends BaseMapper<User> {
*
*
* @param userId id
* @return
* @return UserVO
*/
UserVO info(Integer userId);
/**
* IdId
* IdId
*
* @param classId Id
* @return
* @return Id
*/
List<Integer> selectIdsByClassId(Integer classId);
/**
*
*
* @param page
* @param gradeId Id
* @param realName
* @param roleId Id
* @return
* @param page
* @param gradeId Id
* @param realName
* @param userId IdremoveUserGradeuserIdsId
* @param roleId Id
* @return
*/
IPage<UserVO> pagingUser(IPage<UserVO> page, Integer gradeId, String realName,Integer userId, Integer roleId);
IPage<UserVO> pagingUser(IPage<UserVO> page, Integer gradeId, String realName, Integer userId, Integer roleId);
/**
*
* Id
*
* @param gradeIds id
* @return
*/
Integer removeGradeIdByGradeIds(List<Integer> gradeIds);
/**
* Id
*
* @param roleId Id
* @return IdId
*/
List<ClassCountResult> countAndGroupByGradeAndRoleId(Integer roleId);
/**
* Id
*
* @param userIds Id
* @return
*/
Integer deleteByUserIds(List<Integer> userIds);
}
}

@ -9,9 +9,7 @@ import java.time.LocalDateTime;
/**
* @ Author JinXi
* @ Version 1.0
* @ Date 2024/4/25 14:08CertificateSerializable
* * t_certificate
* *
* @ Date 2024/4/25 14:08
*/
@TableName("t_certificate")
public class Certificate implements Serializable {
@ -19,39 +17,27 @@ public class Certificate implements Serializable {
/**
* ID
* t_certificateid
* * Integer使MyBatis Plus@TableIdIdType.AUTO
* *
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
*
* String
* * GetterSetter
*/
private String certificateName;
/**
*
* String
* * 便
*/
private String image;
/**
*
* * String
* *
*/
private String certificationNuit;
/**
*
* LocalDateTime
* * @TableFieldFieldFill.INSERT
* * 使@JsonFormatyyyy-MM-dd HH:mm:ssJSON
*/
@TableField(fill = FieldFill.INSERT)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@ -59,118 +45,53 @@ public class Certificate implements Serializable {
/**
*
* Integer01
* *
*/
private Integer isDeleted;
/**
* isDeleted
* Integer
* @return
*/
/**
* isDeleted
* Integer
* @return
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
* isDeleted
* isDeleted
* @param isDeleted
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDID
* 便IDInteger
* @return ID
*/
public Integer getId(){
return id;
}
/**
* IDID
* 使IDidID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
*
* String
* @return
*/
public String getCertificateName() {
return certificateName;
}
/**
*
* certificateName
* @param certificateName
*/
public void setCertificateName(String certificateName) {
this.certificateName = certificateName;
}
/**
*
* String
* @return
*/
public String getImage() {
return image;
}
/**
*
* image
* @param image
*/
public void setImage(String image) {
this.image = image;
}
/**
*
* String
* @return
*/
public String getCertificationNuit() {
return certificationNuit;
}
/**
*
* certificationNuit
* @param certificationNuit
*/
public void setCertificationNuit(String certificationNuit) {
this.certificationNuit = certificationNuit;
}
/**
*
* LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
*
*
* createTimeLocalDateTime
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* ObjecttoStringCertificate
* 便
* Certificate{=,...}
* @return Certificate
*/
@Override
public String toString() {
return "Certificate{" +

@ -7,8 +7,7 @@ import java.time.LocalDateTime;
/**
* <p>
* CertificateUserSerializable
* * t_certificate_user
*
* </p>
*
* @author WeiJin
@ -21,152 +20,80 @@ public class CertificateUser implements Serializable {
/**
* ID
* t_certificate_userid
* * IntegerMyBatis Plus@TableIdIdType.AUTO
* *
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* * @TableFieldFieldFill.INSERT
* * ID
* *
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
* id
* Integer
* * ID
*/
private Integer examId;
/**
*
* String
* *
*/
private String code;
/**
* id
* Integer
* * t_certificateID
*/
private Integer certificateId;
/**
* YYYY-MM-DD hh:mm:ss
* LocalDateTime
* * @TableFieldFieldFill.INSERT
* * 便YYYY-MM-DD hh:mm:ss
* * 便使
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* IDID
*
* @return IDInteger
*/
public Integer getId() {
return id;
}
/**
* IDID
*
* @param id IDInteger
*/
public void setId(Integer id) {
this.id = id;
}
/**
* ID
*
* @return IDInteger1
*/
public Integer getUserId() {
return userId;
}
/**
* IDID
* @param userId IDInteger
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* ID
*
* @return IDInteger
*/
public Integer getExamId() {
return examId;
}
/**
* ID
* ID
* @param examId IDInteger
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
*
* 便
* @return String
*/
public String getCode() {
return code;
}
/**
*
*
* @param code String
*/
public void setCode(String code) {
this.code = code;
}
/**
* ID
*
* @return IDInteger
*/
public Integer getCertificateId() {
return certificateId;
}
/**
* IDID
* ID
* @param certificateId IDInteger
*/
public void setCertificateId(Integer certificateId) {
this.certificateId = certificateId;
}
/**
*
*
* @return LocalDateTime
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
*
*
* @param createTime LocalDateTime1
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/* 便CertificateUser{=,...}
* @return
*/
@Override
public String toString() {
return "CertificateUser{" +

@ -7,8 +7,7 @@ import java.time.LocalDateTime;
/**
* <p>
* ExamSerializable
* t_exam
*
* </p>
*
* @author WeiJin
@ -18,492 +17,252 @@ import java.time.LocalDateTime;
public class Exam implements Serializable {
private static final long serialVersionUID = 1L;
// 以下是类中定义的各个成员变量分别对应数据库表“t_exam”中的不同字段用于存储考试相关的各种详细信息下面将逐个进行详细注释。
/**
* id
* t_examidInteger
* MyBatis Plus@TableIdIdType.AUTO
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
*
* String便
*/
private String title;
/**
*
* Integer9090使
*/
private Integer examDuration;
/**
*
* Integer线便
*/
private Integer passedScore;
/**
*
* Integer
*/
private Integer grossScore;
/**
*
* 线Integer
*/
private Integer maxCount;
/**
* id
* Integer@TableFieldFieldFill.INSERTID便
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
* id
* Integer便
*/
private Integer certificateId;
/**
*
* Integer
*/
private Integer radioCount;
/**
* *100/100
* Integer0.880100100
*/
private Integer radioScore;
/**
*
* Integer
*
*/
private Integer multiCount;
/**
* *100/100
* 100100Integer便
*/
private Integer multiScore;
/**
*
* Integer
*
*/
private Integer judgeCount;
/**
* *100/100
* 100100Integer便
*/
private Integer judgeScore;
/**
*
* Integer
*/
private Integer saqCount;
/**
* *100/100
* 100100Integer
*/
private Integer saqScore;
/**
* YYYY-MM-DD hh:mm:ss
* LocalDateTimeYYYY-MM-DD hh:mm:ss便使
*/
private LocalDateTime startTime;
/**
* YYYY-MM-DD hh:mm:ss
* LocalDateTimeYYYY-MM-DD hh:mm:ss
*/
private LocalDateTime endTime;
/**
* YYYY-MM-DD hh:mm:ss
* LocalDateTimeYYYY-MM-DD hh:mm:ss
* @TableFieldFieldFill.INSERT便
* YYYY-MM-DD hh:mm:ss
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
*
* Integer01
* 便
*/
private Integer isDeleted;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对Exam对象的属性进行访问和修改下面分别进行注释。
/**
* isDeleted
* Integer
* @return
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
* isDeleted
* isDeleted
* @param isDeleted
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDID
* 便IDInteger
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使IDidID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
*
* String
* @return
*/
public String getTitle() {
return title;
}
/**
*
* title
* @param title
*/
public void setTitle(String title) {
this.title = title;
}
/**
*
* Integer
* @return
*/
public Integer getExamDuration() {
return examDuration;
}
/**
*
* examDuration
* @param examDuration
*/
public void setExamDuration(Integer examDuration) {
this.examDuration = examDuration;
}
/**
*
* Integer
* @return
*/
public Integer getPassedScore() {
return passedScore;
}
/**
*
* 线passedScore
* @param passedScore
*/
public void setPassedScore(Integer passedScore) {
this.passedScore = passedScore;
}
/**
*
* Integer
* @return
*/
public Integer getGrossScore() {
return grossScore;
}
/**
*
* grossScore
* @param grossScore
*/
public void setGrossScore(Integer grossScore) {
this.grossScore = grossScore;
}
/**
*
* 线Integer
* @return
*/
public Integer getMaxCount() {
return maxCount;
}
/**
*
* maxCount
* @param maxCount
*/
public void setMaxCount(Integer maxCount) {
this.maxCount = maxCount;
}
/**
* idID
* 便Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* idID
* userIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* idID
* Integer
* @return ID
*/
public Integer getCertificateId() {
return certificateId;
}
/**
* idID
* certificateIdID
* @param certificateId ID
*/
public void setCertificateId(Integer certificateId) {
this.certificateId = certificateId;
}
/**
*
* Integer
* @return
*/
public Integer getRadioCount() {
return radioCount;
}
/**
*
* radioCount
* @param radioCount
*/
public void setRadioCount(Integer radioCount) {
this.radioCount = radioCount;
}
/**
*
* Integer
* @return
*/
public Integer getRadioScore() {
return radioScore;
}
/**
*
* radioScore
* @param radioScore
*/
public void setRadioScore(Integer radioScore) {
this.radioScore = radioScore;
}
/**
*
* Integer
* @return
*/
public Integer getMultiCount() {
return multiCount;
}
/**
*
* 使multiCount
* @param multiCount
*/
public void setMultiCount(Integer multiCount) {
this.multiCount = multiCount;
}
/**
*
* 100100Integer
* @return
*/
public Integer getMultiScore() {
return multiScore;
}
/**
*
* multilScoremultiScore
* @param multilScore
*/
public void setMultiScore(Integer multilScore) {
this.multiScore = multilScore;
}
/**
*
* Integer
* @return
*/
public Integer getJudgeCount() {
return judgeCount;
}
/**
*
* judgeCount
* @param judgeCount
*/
public void setJudgeCount(Integer judgeCount) {
this.judgeCount = judgeCount;
}
/**
*
* 100100Integer
* @return
*/
public Integer getJudgeScore() {
return judgeScore;
}
/**
*
* judgeScore
* @param judgeScore
*/
public void setJudgeScore(Integer judgeScore) {
this.judgeScore = judgeScore;
}
/**
*
* Integer
* @return
*/
public Integer getSaqCount() {
return saqCount;
}
/**
*
* saqCount
* @param saqCount
*/
public void setSaqCount(Integer saqCount) {
this.saqCount = saqCount;
}
/**
*
* 100100Integer
* @return
*/
public Integer getSaqScore() {
return saqScore;
}
/**
*
* saqScore
* @param saqScore
*/
public void setSaqScore(Integer saqScore) {
this.saqScore = saqScore;
}
/**
*
* LocalDateTimeYYYY-MM-DD hh:mm:ssLocalDateTime
* @return
*/
public LocalDateTime getStartTime() {
return startTime;
}
/**
*
* startTimeLocalDateTime
* @param startTime LocalDateTime
*/
public void setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
}
/**
*
* LocalDateTimeYYYY-MM-DD hh:mm:ssLocalDateTime
* @return
*/
public LocalDateTime getEndTime() {
return endTime;
}
/**
*
* endTimeLocalDateTime
* @param endTime LocalDateTime
*/
public void setEndTime(LocalDateTime endTime) {
this.endTime = endTime;
}
/**
*
* LocalDateTimeYYYY-MM-DD hh:mm:ss@TableField便LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
*
*
* createTimeLocalDateTime
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* ObjecttoStringExam
* Exam{=,...}便
* @return Exam
*/
@Override
public String toString() {
return "Exam{" +

@ -8,8 +8,7 @@ import java.io.Serializable;
/**
* <p>
* ExamGradeSerializable使便
* t_exam_grade
*
* </p>
*
* @author WeiJin
@ -19,97 +18,51 @@ import java.io.Serializable;
public class ExamGrade implements Serializable {
private static final long serialVersionUID = 1L;
// 以下是类中定义的各个成员变量分别对应数据库表“t_exam_grade”中的不同字段用于存储考试与班级关联关系的关键信息下面将逐个进行注释说明。
/**
* id
* t_exam_gradeidInteger
* MyBatis Plus@TableIdIdType.AUTO便
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* t_examID便
*/
private Integer examId;
/**
* id
* Integer
* ID
*/
private Integer gradeId;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对ExamGrade对象的属性进行访问和修改下面分别进行注释。
/**
* IDExamGradeID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDExamGradeID
* Integer
* @return ID
*/
public Integer getExamId() {
return examId;
}
/**
* IDID
* ExamGradeIDexamIdID
* @param examId ID
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
* IDExamGradeID
* Integer
* @return ID
*/
public Integer getGradeId() {
return gradeId;
}
/**
* IDID
* ExamGradeIDgradeIdID
* @param gradeId ID
*/
public void setGradeId(Integer gradeId) {
this.gradeId = gradeId;
}
/**
* ObjecttoStringExamGrade
* ExamGrade{=,...}便
* @return ExamGrade
*/
@Override
public String toString() {
return "ExamGrade{" +
"id=" + id +
", examId=" + examId +
", gradeId=" + gradeId +
"}";
"id=" + id +
", examId=" + examId +
", gradeId=" + gradeId +
"}";
}
}
}

@ -5,10 +5,10 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
* ExamQuAnswerSerializable便使
* t_exam_qu_answer
*
* </p>
*
* @author WeiJin
@ -18,272 +18,142 @@ import java.io.Serializable;
public class ExamQuAnswer implements Serializable {
private static final long serialVersionUID = 1L;
// 以下是类中定义的各个成员变量分别对应数据库表“t_exam_qu_answer”中的不同字段用于存储考试答题相关的详细信息下面将逐个进行注释说明。
/**
* id
* t_exam_qu_answeridInteger
* MyBatis Plus@TableIdIdType.AUTO便
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer便
*/
private Integer userId;
/**
* id
* Integer
* t_examID
*/
private Integer examId;
/**
* id
* Integer便使
*/
private Integer questionId;
/**
*
* Integer123
*
*/
private Integer questionType;
/**
* id id使
* StringID使便
*
*/
private String answerId;
/**
*
* String便
*/
private String answerContent;
/**
* 0 1
* Integer01线
*/
private Integer checkout;
/**
* 0 1
* Integer01便便
*/
private Integer isSign;
/**
* 0 1
* Integer01便
*/
private Integer isRight;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对ExamQuAnswer对象的属性进行访问和修改下面分别进行注释。
/**
* IDExamQuAnswerID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDExamQuAnswerID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* ExamQuAnswerIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* IDExamQuAnswerID
* Integer
* @return ID
*/
public Integer getExamId() {
return examId;
}
/**
* IDID
* ExamQuAnswerIDexamIdID
* @param examId ID
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
* IDExamQuAnswerID
* Integer
* @return ID
*/
public Integer getQuestionId() {
return questionId;
}
/**
* IDID
* ExamQuAnswerIDquestionIdID
* @param questionId ID
*/
public void setQuestionId(Integer questionId) {
this.questionId = questionId;
}
/**
* ExamQuAnswer
* Integer
* @return
*/
public Integer getQuestionType() {
return questionType;
}
/**
*
* ExamQuAnswerquestionType
* @param questionType
*/
public void setQuestionType(Integer questionType) {
this.questionType = questionType;
}
/**
* idExamQuAnswerID
* String
* @return ID
*/
public String getAnswerId() {
return answerId;
}
/**
* id
* ExamQuAnswerIDanswerIdID
* @param answerId ID
*/
public void setAnswerId(String answerId) {
this.answerId = answerId;
}
/**
* ExamQuAnswer
* String
* @return
*/
public String getAnswerContent() {
return answerContent;
}
/**
*
* ExamQuAnsweranswerContent
* @param answerContent
*/
public void setAnswerContent(String answerContent) {
this.answerContent = answerContent;
}
/**
* ExamQuAnswer01
* Integer
* @return 01
*/
public Integer getCheckout() {
return checkout;
}
/**
*
* ExamQuAnswercheckout01
* @param checkout 01
*/
public void setCheckout(Integer checkout) {
this.checkout = checkout;
}
/**
* ExamQuAnswer01
* 便Integer
* @return 01
*/
public Integer getIsSign() {
return isSign;
}
/**
*
* ExamQuAnswerisSign01
* @param isSign 01
*/
public void setIsSign(Integer isSign) {
this.isSign = isSign;
}
/**
* ExamQuAnswer01
* 便Integer
* @return 01
*/
public Integer getIsRight() {
return isRight;
}
/**
*
* ExamQuAnswerisRight01
*/
public void setIsRight(Integer isRight) {
this.isRight = isRight;
}
/**
* ObjecttoStringExamQuAnswer
* ExamQuAnswer{=,...}便
* @return ExamQuAnswer
*/
@Override
public String toString() {
return "ExamQuAnswer{" +
"id=" + id +
", userId=" + userId +
", examId=" + examId +
", questionId=" + questionId +
", questionType=" + questionType +
", answerId=" + answerId +
", answerContent=" + answerContent +
", checkout=" + checkout +
", isSign=" + isSign +
", isRight=" + isRight +
"}";
}
}
"id=" + id +
", userId=" + userId +
", examId=" + examId +
", questionId=" + questionId +
", questionType=" + questionType +
", answerId=" + answerId +
", answerContent=" + answerContent +
", checkout=" + checkout +
", isSign=" + isSign +
", isRight=" + isRight +
"}";
}
}

@ -8,300 +8,92 @@ import com.baomidou.mybatisplus.annotation.TableName;
* @Author Alan
* @Version
* @Date 2024/4/7 3:42 PM
* ExamQuestion
* t_exam_question
* Serializable使
* 便
*/
@TableName("t_exam_question")
public class ExamQuestion {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性这里初始化为1L。
// 以下是类中定义的各个成员变量分别对应数据库表“t_exam_question”中的不同字段用于存储试题相关的关键信息下面将逐个进行注释说明。
/**
* ID
* MyBatis Plus@TableIdIdType.AUTO
*
* 便
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* t_examID便
*
*/
private Integer examId;
/**
* id
* Integer便
* 便
*/
private Integer questionId;
/**
*
* Integer
*
*/
private Integer score;
/**
*
* Integer
* 便
*/
private Integer sort;
/**
*
* Integer123
*
*/
private Integer type;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对ExamQuestion对象的属性进行访问和修改下面分别进行注释。
/**
* IDExamQuestionID
* Integer
* @return ID
*/
public Integer getId() {
return id;
@Override
public String toString() {
return "ExamQuestion{" +
"id=" + id +
", examId=" + examId +
", questionId=" + questionId +
", score=" + score +
", sort=" + sort +
", type=" + type +
'}';
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDExamQuestionID
* Integer
* @return ID
*/
public Integer getExamId() {
return examId;
}
/**
* IDID
* ExamQuestionIDexamIdID
* @param examId ID
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
* IDExamQuestionID
* Integer
* @return ID
*/
public Integer getQuestionId() {
return questionId;
}
/**
* IDID
* ExamQuestionIDquestionIdID
* @param questionId ID
*/
public void setQuestionId(Integer questionId) {
this.questionId = questionId;
}
/**
* ExamQuestion
* Integer
* @return
*/
public Integer getScore() {
return score;
}
/**
*
* ExamQuestionscore
* @param score
*/
public void setScore(Integer score) {
this.score = score;
}
/**
* ExamQuestion
* Integer
* @return
*/
public Integer getSort() {
return sort;
}
/**
*
* ExamQuestionsort
* @param sort
*/
public void setSort(Integer sort) {
this.sort = sort;
}
/**
* ExamQuestion
* Integer
* @return
*/
public Integer getType() {
return type;
}
/**
*
* ExamQuestiontype
* @param type
*/
public void setType(Integer type) {
this.type = type;
}
/**
* ObjecttoStringExamQuestion
* ExamQuestion{=,...}便
* @return ExamQuestion
*/
@Override
public String toString() {
return "ExamQuestion{" +
"id=" + id +
", examId=" + examId +
", questionId=" + questionId +
", score=" + score +
", sort=" + sort +
", type=" + type +
'}';
public Integer getId() {
return id;
}
}
/**
* IDID`id``@TableId(value = "id", type = IdType.AUTO)`
*
* `id`ID`ExamQuestion``id`
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* ID`ExamQuestion``examId`
* ID
* ID`examId`
* @param examId ID
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
* ID`ExamQuestion``questionId`
* ID
* ID`questionId`
* @param questionId ID
*/
public void setQuestionId(Integer questionId) {
this.questionId = questionId;
}
/**
* `ExamQuestion``score`
*
* `score`
* @param score
*/
public void setScore(Integer score) {
this.score = score;
}
/**
* `ExamQuestion``sort`
*
* `sort`
* @param sort
*/
public void setSort(Integer sort) {
this.sort = sort;
}
/**
* `ExamQuestion``type`
*
* `type`便
* @param type
*/
public void setType(Integer type) {
this.type = type;
}
/**
* ID`ExamQuestion`ID
* ID使Integer
* @return ID
*/
public Integer getId() {
return id;
}
public Integer getExamId() {
return examId;
}
/**
* ID`ExamQuestion`ID
* 便Integer
* @return ID
*/
public Integer getExamId() {
return examId;
}
public Integer getQuestionId() {
return questionId;
}
/**
* ID`ExamQuestion`ID
* Integer
* @return ID
*/
public Integer getQuestionId() {
return questionId;
}
public Integer getScore() {
return score;
}
/**
* `ExamQuestion`
* Integer
* @return
*/
public Integer getScore() {
return score;
}
public Integer getSort() {
return sort;
}
/**
* `ExamQuestion`
* Integer
* @return
*/
public Integer getSort() {
return sort;
public Integer getType() {
return type;
}
}
/**
* `ExamQuestion`
* Integer
* @return
*/
public Integer getType() {
return type;
}

@ -8,8 +8,7 @@ import java.io.Serializable;
/**
* <p>
* ExamRepoSerializable使便使
* t_exam_repo
*
* </p>
*
* @author WeiJin
@ -19,98 +18,51 @@ import java.io.Serializable;
public class ExamRepo implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性这里初始化为1L。
// 以下是类中定义的各个成员变量分别对应数据库表“t_exam_repo”中的不同字段用于存储考试与题库关联关系的关键信息下面将逐个进行注释说明。
/**
* id
* t_exam_repoidInteger
* MyBatis Plus@TableIdIdType.AUTO便
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* t_examID便使
*/
private Integer examId;
/**
* id
* Integer
* ID使
*/
private Integer repoId;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对ExamRepo对象的属性进行访问和修改下面分别进行注释。
/**
* IDExamRepoID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDExamRepoID
* 使Integer
* @return ID
*/
public Integer getExamId() {
return examId;
}
/**
* IDID
* ExamRepoIDexamIdID
* @param examId ID
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
* IDExamRepoID
* Integer
* @return ID
*/
public Integer getRepoId() {
return repoId;
}
/**
* IDID
* ExamRepoIDrepoIdID
*/
public void setRepoId(Integer repoId) {
this.repoId = repoId;
}
/**
* ObjecttoStringExamRepo
* ExamRepo{=,...}便
* @return ExamRepo
*/
@Override
public String toString() {
return "ExamRepo{" +
"id=" + id +
", examId=" + examId +
", repoId=" + repoId +
"}";
"id=" + id +
", examId=" + examId +
", repoId=" + repoId +
"}";
}
}
}

@ -3,10 +3,10 @@ package cn.org.alan.exam.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
/**
* <p>
* ExerciseRecordSerializable使便使
* t_exercise_record
*
* </p>
*
* @author WeiJin
@ -16,457 +16,114 @@ import java.io.Serializable;
public class ExerciseRecord implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_exercise_record”中的不同字段用于存储练习答题记录相关的详细信息下面将逐个进行注释说明。
/**
* IDt_exercise_recordidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* ID便使
*/
private Integer repoId;
/**
* id
* Integer便使
* ID
*/
private Integer questionId;
/**
* id
* Integer便
* ID
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* String便
*
*/
private String answer;
/**
*
* Integer123
* 便
*/
private Integer questionType;
/**
* ,id使","
* StringID使,便
*
*/
private String options;
/**
*
* Integer01便
*
*/
private Integer isRight;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对ExerciseRecord对象的属性进行访问和修改下面分别进行注释。
/**
* IDExerciseRecordID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDExerciseRecordID
* 使Integer
* @return ID
*/
public Integer getRepoId() {
return repoId;
}
/**
* IDID
* ExerciseRecordIDrepoIdID
* @param repoId ID
*/
public void setRepoId(Integer repoId) {
this.repoId = repoId;
}
/**
* IDExerciseRecordID
* Integer
* @return ID
*/
public Integer getQuestionId() {
return questionId;
}
/**
* IDID
* ExerciseRecordIDquestionIdID
* @param questionId ID
*/
public void setQuestionId(Integer questionId) {
this.questionId = questionId;
}
/**
* IDExerciseRecordID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* ExerciseRecordIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* ExerciseRecord
* String
* @return
*/
public String getAnswer() {
return answer;
}
/**
*
* ExerciseRecordanswer
* @param answer
*/
public void setAnswer(String answer) {
this.answer = answer;
}
/**
* ExerciseRecord
* Integer
* @return
*/
public Integer getQuestionType() {
### 1.
### 2.
### 3.
使
### 4.
****
### 1.
湿使
### 2.
### 3.
### 4.
JavaScriptJest
使/**
* <p>
* ExerciseRecordSerializable使便使
* t_exercise_record
* </p>
*
* @author WeiJin
* @since 2024-03-21
*/
@TableName("t_exercise_record")
public class ExerciseRecord implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_exercise_record”中的不同字段用于存储练习答题记录相关的详细信息下面将逐个进行注释说明。
/**
* IDt_exercise_recordidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* ID便使
*/
private Integer repoId;
/**
* id
* Integer便使
* ID
*/
private Integer questionId;
/**
* id
* Integer便
* ID
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* String便
*
*/
private String answer;
/**
*
* Integer123
* 便
*/
private Integer questionType;
/**
* ,id使","
* StringID使,便
*
*/
private String options;
/**
*
* Integer01便
*
*/
private Integer isRight;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对ExerciseRecord对象的属性进行访问和修改下面分别进行注释。
/**
* IDExerciseRecordID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDExerciseRecordID
* 使Integer
* @return ID
*/
public Integer getRepoId() {
return repoId;
}
/**
* IDID
* ExerciseRecordIDrepoIdID
* @param repoId ID
*/
public void setRepoId(Integer repoId) {
this.repoId = repoId;
}
/**
* IDExerciseRecordID
* Integer
* @return ID
*/
public Integer getQuestionId() {
return questionId;
}
/**
* IDID
* ExerciseRecordIDquestionIdID
* @param questionId ID
*/
public void setQuestionId(Integer questionId) {
this.questionId = questionId;
}
/**
* IDExerciseRecordID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* ExerciseRecordIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* ExerciseRecord
* String
* @return
*/
public String getAnswer() {
return answer;
}
/**
*
* ExerciseRecordanswer
* @param answer
*/
public void setAnswer(String answer) {
this.answer = answer;
}
/**
* ExerciseRecord
* Integer
* @return
*/
public Integer getQuestionType() {
return questionType;
}
/**
* `ExerciseRecord``questionType`
*
* `questionType`
* 便
* @param questionType
*/
public void setQuestionType(Integer questionType) {
this.questionType = questionType;
}
/**
* `ExerciseRecord`
* 使,ID便
* `String`
* @return
*/
public String getOptions() {
return options;
}
return questionType;
}
/**
* `ExerciseRecord``options`
*
* `options`
* 使,
* @param options
*/
public void setOptions(String options) {
this.options = options;
}
public void setQuestionType(Integer questionType) {
this.questionType = questionType;
}
public String getOptions() {
return options;
}
/**
* `ExerciseRecord`
* 01便
* `Integer`
* @return 01
*/
public Integer getIsRight() {
return isRight;
}
public void setOptions(String options) {
this.options = options;
}
public Integer getIsRight() {
return isRight;
}
/**
* `ExerciseRecord``isRight`
*
* `isRight`01
*
* @param isRight 01
*/
public void setIsRight(Integer isRight) {
this.isRight = isRight;
}
public void setIsRight(Integer isRight) {
this.isRight = isRight;
}
/**
* `Object``toString``ExerciseRecord`
* ExerciseRecord{=,...}便`String`
* @return `ExerciseRecord`
*/
@Override
public String toString() {
return "ExerciseRecord{" +
"id=" + id +
" repoId=" + repoId +
" questionId=" + questionId +
" userId=" + userId +
" answer=" + answer +
" questionType=" + questionType +
" options=" + options +
" isRight=" + isRight +
"}";
}
@Override
public String toString() {
return "ExerciseRecord{" +
"id=" + id +
", repoId=" + repoId +
", questionId=" + questionId +
", userId=" + userId +
", answer=" + answer +
", questionType=" + questionType +
", options=" + options +
", isRight=" + isRight +
"}";
}
}

@ -6,8 +6,7 @@ import java.io.Serializable;
/**
* <p>
* GradeSerializable使便
* t_grade
*
* </p>
*
* @author WeiJin
@ -17,141 +16,72 @@ import java.io.Serializable;
public class Grade implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性这里初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_grade”中的不同字段用于存储班级相关的关键信息下面将逐个进行注释说明。
/**
* id
* t_gradeidInteger
* MyBatis Plus@TableIdIdType.AUTO便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
*
* String便便
*/
private String gradeName;
/**
* id
* Integer便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* 访String访线
*/
private String code;
/**
*
* Integer01便便
*/
private Integer isDeleted;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对Grade对象的属性进行访问和修改下面分别进行注释。
/**
* Grade
* Integer
* @return 01
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
*
* GradeisDeleted01
* @param isDeleted 01
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDGradeID
* IDInteger
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* Grade
* String
* @return
*/
public String getGradeName() {
return gradeName;
}
/**
*
* GradegradeName
* @param gradeName
*/
public void setGradeName(String gradeName) {
this.gradeName = gradeName;
}
/**
* IDGradeID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* GradeIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* Grade
* 访String
* @return
*/
public String getCode() {
return code;
}
/**
*
* Gradecode
* @param code
*/
public void setCode(String code) {
this.code = code;
}
/**
* ObjecttoStringGrade
* Grade{=,...}便
* @return Grade
*/
@Override
public String toString() {
return "Grade{" +
@ -162,4 +92,4 @@ public class Grade implements Serializable {
", isDeleted=" + isDeleted +
'}';
}
}
}

@ -4,10 +4,10 @@ import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* GradeExerciseSerializable使便使
* t_grade_exercise
*
* </p>
*
* @author WeiJin
@ -17,175 +17,79 @@ import java.time.LocalDateTime;
public class GradeExercise implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L使得在不同阶段或环境下对对象操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_grade_exercise”中的不同字段用于存储班级刷题活动相关的详细信息下面将逐个进行注释说明。
/**
* id
* t_grade_exerciseidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* ID便使
*/
private Integer repoId;
/**
* id
* Integer
* t_gradeID
*/
private Integer gradeId;
/**
* id
* Integer便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* LocalDateTimeYYYY-MM-DD HH:mm:ss@TableFieldFieldFill.INSERT
* 便
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对GradeExercise对象的属性进行访问和修改下面分别进行注释。
/**
* IDGradeExerciseID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDGradeExerciseID
* 使Integer
* @return ID
*/
public Integer getRepoId() {
return repoId;
}
/**
* IDID
* GradeExerciseIDrepoIdID
* @param repoId ID
*/
public void setRepoId(Integer repoId) {
this.repoId = repoId;
}
/**
* IDGradeExerciseID
* Integer
* @return ID
*/
public Integer getGradeId() {
return gradeId;
}
/**
* IDID
* GradeExerciseIDgradeIdID
* @param gradeId ID
*/
public void setGradeId(Integer gradeId) {
this.gradeId = gradeId;
}
/**
* IDGradeExerciseID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* GradeExerciseIDuserIdID
* @param userId
- ****
- ****
- `calculateTotalScore`
- 使 `id`
- ****
- 使使便
- 使使使
`**/
/**
* ID`GradeExercise`ID`userId`
* ID
*
* ID`userId`
* 便
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* `GradeExercise`
* `LocalDateTime`YYYY-MM-DD HH:mm:ss
*
* 便`LocalDateTime`
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* `GradeExercise``createTime`
* `@TableField(fill = FieldFill.INSERT)`
*
* `LocalDateTime``createTime`
*
* @param createTime `LocalDateTime`
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* `Object``toString``GradeExercise`
* GradeExercise{=,...}便`String`
* @return `GradeExercise`
*/
@Override
public String toString() {
return "GradeExercise{" +
"id=" + id +
", repoId=" + repoId +
", gradeId=" + gradeId +
", userId=" + userId +
", createTime=" + createTime +
"}";
}
"id=" + id +
", repoId=" + repoId +
", gradeId=" + gradeId +
", userId=" + userId +
", createTime=" + createTime +
"}";
}
}

@ -7,8 +7,7 @@ import java.time.LocalDateTime;
/**
* <p>
* ManualScoreSerializable使便
* t_manual_score
*
* </p>
*
* @author WeiJin
@ -18,157 +17,79 @@ import java.time.LocalDateTime;
public class ManualScore implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同阶段或环境下对对象操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_manual_score”中的不同字段用于存储人工评分相关的详细信息下面将逐个进行注释说明。
/**
* id
* t_manual_scoreidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* t_exam_qu_answerID便
*/
private Integer examQuAnswerId;
/**
* id
* Integer便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* Integer
*
*/
private Integer score;
/**
*
* LocalDateTimeYYYY-MM-DD HH:mm:ss@TableFieldFieldFill.INSERT
* 便
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对ManualScore对象的属性进行访问和修改下面分别进行注释。
/**
* IDManualScoreID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDManualScoreID
* Integer
* @return ID
*/
public Integer getExamQuAnswerId() {
return examQuAnswerId;
}
/**
* IDID
* ManualScoreIDexamQuAnswerIdID
* @param examQuAnswerId ID
*/
public void setExamQuAnswerId(Integer examQuAnswerId) {
this.examQuAnswerId = examQuAnswerId;
}
/**
* IDManualScoreID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* ManualScoreIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* ManualScore
* Integer
* @return
*/
public Integer getScore() {
return score;
}
/**
*
* ManualScorescore
* @param score
*/
public void setScore(Integer score) {
this.score = score;
}
/**
* ManualScore
* LocalDateTimeYYYY-MM-DD HH:mm:ss
* LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* @TableField(fill = FieldFill.INSERT)`
*
* LocalDateTime`createTime`
*
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* ObjecttoStringManualScore
* ManualScore{=,...}便String
* @return ManualScore
*/
@Override
public String toString() {
return "ManualScore{" +
"id=" + id +
", examQuAnswerId=" + examQuAnswerId +
", userId=" + userId +
", score=" + score +
", createTime=" + createTime +
"}";
"id=" + id +
", examQuAnswerId=" + examQuAnswerId +
", userId=" + userId +
", score=" + score +
", createTime=" + createTime +
"}";
}
}
}

@ -7,8 +7,7 @@ import java.time.LocalDateTime;
/**
* <p>
* NoticeSerializable使便使
* t_notice
*
* </p>
*
* @author WeiJin
@ -18,194 +17,101 @@ import java.time.LocalDateTime;
public class Notice implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性这里初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_notice”中的不同字段用于存储公告相关的关键信息下面将逐个进行注释说明。
/**
* ID
* t_noticeidInteger
* MyBatis Plus@TableIdIdType.AUTO便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
*
* String便
*/
private String title;
/**
*
* String
*/
private String image;
/**
*
* String
*/
private String content;
/**
* ID
* Integer便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* LocalDateTimeYYYY-MM-DD HH:mm:ss@TableFieldFieldFill.INSERT
* 便
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
*
* Integer01便便
*/
private Integer isDeleted;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对Notice对象的属性进行访问和修改下面分别进行注释。
/**
* Notice
* Integer
* @return 01
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
*
* NoticeisDeleted01
* @param isDeleted 01
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDNoticeID
* IDInteger
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* Notice
* String
* @return
*/
public String getTitle() {
return title;
}
/**
*
* Noticetitle
* @param title
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Notice
* String
* @return
*/
public String getImage() {
return image;
}
/**
*
* Noticeimage
* @param image
*/
public void setImage(String image) {
this.image = image;
}
/**
* Notice
* String
* @return
*/
public String getContent() {
return content;
}
/**
*
* Noticecontent
* @param content
*/
public void setContent(String content) {
this.content = content;
}
/**
* IDNoticeID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* NoticeIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* Notice
* LocalDateTimeYYYY-MM-DD HH:mm:ss
* LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* @TableField(fill = FieldFill.INSERT)`
*
* LocalDateTime`createTime`
*
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* ObjecttoStringNotice
* Notice{=,...}便String
* @return Notice
*/
@Override
public String toString() {
return "Notice{" +
@ -218,4 +124,4 @@ public class Notice implements Serializable {
", isDeleted=" + isDeleted +
'}';
}
}
}

@ -8,8 +8,7 @@ import java.io.Serializable;
/**
* <p>
* NoticeGradeSerializable使便使
* t_notice_grade
*
* </p>
*
* @author WeiJin
@ -19,101 +18,51 @@ import java.io.Serializable;
public class NoticeGrade implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同阶段或环境下对对象操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_notice_grade”中的不同字段用于存储公告与班级关联的关键信息下面将逐个进行注释说明。
/**
* ID
* t_notice_gradeidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer
* t_noticeID便
*/
private Integer noticeId;
/**
*
* Integer
* t_gradeID
*/
private Integer gradeId;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对NoticeGrade对象的属性进行访问和修改下面分别进行注释。
/**
* IDNoticeGradeID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDNoticeGradeID
* Integer
* @return ID
*/
public Integer getNoticeId() {
return noticeId;
}
/**
* IDID
* NoticeGradeIDnoticeIdID
* @param noticeId ID
*/
public void setNoticeId(Integer noticeId) {
this.noticeId = noticeId;
}
/**
* IDNoticeGradeID
* Integer
* @return ID
*/
public Integer getGradeId() {
return gradeId;
return gradeId;
}
/**
* ID`NoticeGrade`ID`gradeId`
* ID
* ID`gradeId`
* 便
* @param gradeId ID
*/
public void setGradeId(Integer gradeId) {
this.gradeId = gradeId;
}
/**
* `Object``toString``NoticeGrade`
* NoticeGrade{=,...}便`String`
* @return `NoticeGrade`
*/
@Override
public String toString() {
return "NoticeGrade{" +
"id=" + id +
", noticeId=" + noticeId +
", gradeId=" + gradeId +
"}";
}
"id=" + id +
", noticeId=" + noticeId +
", gradeId=" + gradeId +
"}";
}
}

@ -9,8 +9,7 @@ import java.io.Serializable;
/**
* <p>
* OptionSerializable使便使
* t_option
*
* </p>
*
* @author WeiJin
@ -20,190 +19,100 @@ import java.io.Serializable;
public class Option implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性这里初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_option”中的不同字段用于存储试题选项相关的关键信息下面将逐个进行注释说明。
/**
* id
* t_optionidInteger
* MyBatis Plus@TableIdIdType.AUTO便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer便ID
*/
private Integer quId;
/**
*
* Integer`@NotNull``@Min``@Max``@NotNull``@Min(value = 0)``@Max(value = 1)`01
*/
@NotNull(message = "选型是否正确(isRight)不能为空")
@Min(value = 0, message = "选项是否正确(isRight)只能是0错误1正确")
@Max(value = 1, message = "选项是否正确(isRight)只能是0错误1正确")
@Min(value = 0,message = "选项是否正确(isRight)只能是0错误1正确")
@Max(value = 1,message = "选项是否正确(isRight)只能是0错误1正确")
private Integer isRight;
/**
* 0 1
* String使
*/
private String image;
/**
*
* String`@NotBlank`
* @NotBlank(message = "选型内容(content)不能为空")
*/
@NotBlank(message = "选型内容(content)不能为空")
private String content;
/**
*
* Integer
*/
private Integer sort;
/**
*
* Integer01便便
*/
private Integer isDeleted;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对Option对象的属性进行访问和修改下面分别进行注释。
/**
* Option
* Integer
* @return 01
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
*
* OptionisDeleted01
* @param isDeleted 01
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDOptionID
* IDInteger
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDOptionID
* Integer
* @return ID
*/
public Integer getQuId() {
return quId;
}
/**
* IDID
* OptionIDquIdID
* @param quId ID
*/
public void setQuId(Integer quId) {
this.quId = quId;
}
/**
* Option
* Integer
* @return 01
*/
public Integer getIsRight() {
return isRight;
}
/**
*
* OptionisRight01
* @param isRight 01
*/
public void setIsRight(Integer isRight) {
this.isRight = isRight;
}
/**
* Option
* String
* @return
*/
public String getImage() {
return image;
}
/**
*
* Optionimage
* @param image
*/
public void setImage(String image) {
this.image = image;
}
/**
* Option
* String
* @return
*/
public String getContent() {
return content;
}
/**
*
* Optioncontent
* @param content
*/
public void setContent(String content) {
this.content = content;
}
/**
* Option
* Integer
* @return
*/
public Integer getSort() {
return sort;
}
/**
*
* Optionsort
* @param sort
*/
public void setSort(Integer sort) {
this.sort = sort;
}
/**
* ObjecttoStringOption
* Option{=,...}便String
* @return Option
*/
@Override
public String toString() {
return "Option{" +
@ -216,4 +125,4 @@ public class Option implements Serializable {
", isDeleted=" + isDeleted +
'}';
}
}
}

@ -7,8 +7,7 @@ import java.time.LocalDateTime;
/**
* <p>
* QuestionSerializable使便使
* t_question
*
* </p>
*
* @author WeiJin
@ -18,242 +17,126 @@ import java.time.LocalDateTime;
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性这里初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_question”中的不同字段用于存储试题相关的关键信息下面将逐个进行注释说明。
/**
* id
* t_questionidInteger
* MyBatis Plus@TableIdIdType.AUTO便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
*
* Integer123便
*/
private Integer quType;
/**
*
* String使
*/
private String image;
/**
*
* String
*/
private String content;
/**
*
* LocalDateTimeYYYY-MM-DD HH:mm:ss@TableFieldFieldFill.INSERT
* 便
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
*
* String便
*/
private String analysis;
/**
* id
* Integer便ID
*/
private Integer repoId;
/**
* id
* Integer便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* Integer01便便
*/
private Integer isDeleted;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对Question对象的属性进行访问和修改下面分别进行注释。
/**
* Question
* Integer
* @return 01
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
*
* QuestionisDeleted01
* @param isDeleted 01
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDQuestionID
* IDInteger
* @return ID
*/
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* Question
* Integer
* @return
*/
public Integer getQuType() {
return quType;
}
/**
*
* QuestionquType
* @param quType
*/
public void setQuType(Integer quType) {
this.quType = quType;
}
/**
* Question
* String
* @return
*/
public String getImage() {
return image;
}
/**
*
* Questionimage
* @param image
*/
public void setImage(String image) {
this.image = image;
}
/**
* Question
* String
* @return
*/
public String getContent() {
return content;
}
/**
*
* Questioncontent
* @param content
*/
public void setContent(String content) {
this.content = content;
}
/**
* Question
* LocalDateTimeYYYY-MM-DD HH:mm:ss
* LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* @TableField(fill = FieldFill.INSERT)`
*
* LocalDateTime`createTime`
*
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* Question
* String
* @return
*/
public String getAnalysis() {
return analysis;
}
/**
*
* Questionanalysis
* @param analysis
*/
public void setAnalysis(String analysis) {
this.analysis = analysis;
}
/**
* IDQuestionID
* Integer
* @return ID
*/
public Integer getRepoId() {
return repoId;
}
/**
* IDID
* QuestionIDrepoIdID
* @param repoId ID
*/
public void setRepoId(Integer repoId) {
this.repoId = repoId;
}
/**
* IDQuestionID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* QuestionIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* ObjecttoStringQuestion
* Question{=,...}便String
* @return Question
*/
@Override
public String toString() {
return "Question{" +
@ -268,4 +151,4 @@ public class Question implements Serializable {
", isDeleted=" + isDeleted +
'}';
}
}
}

@ -10,8 +10,7 @@ import java.time.LocalDateTime;
/**
* <p>
* RepoSerializable使便使
* t_repo使
*
* </p>
*
* @author WeiJin
@ -21,146 +20,73 @@ import java.time.LocalDateTime;
public class Repo implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性这里初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_repo”中的不同字段用于存储题库相关的关键信息下面将逐个进行注释说明。
/**
* id
* t_repoidInteger
* MyBatis Plus@TableIdIdType.AUTO便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
*
* String`@NotBlank`便
* @NotBlank(message = "题库名不能为空")
*/
@NotBlank(message = "题库名不能为空")
private String title;
/**
*
* LocalDateTimeYYYY-MM-DD HH:mm:ss@TableFieldFieldFill.INSERT
* 便
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
*
* Integer01便便
*/
private Integer isDeleted;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对Repo对象的属性进行访问和修改下面分别进行注释。
/**
* Repo
* Integer
* @return 01
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
*
* RepoisDeleted01
* @param isDeleted 01
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDRepoID
* IDInteger
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDRepoID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* RepoIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* Repo
* String
* @return
*/
public String getTitle() {
return title;
}
/**
*
* Repotitle
* @param title
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Repo
* LocalDateTimeYYYY-MM-DD HH:mm:ss
* LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* @TableField(fill = FieldFill.INSERT)`
*
* LocalDateTime`createTime`
*
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* ObjecttoStringRepo
* Repo{=,...}便String
* @return Repo
*/
@Override
public String toString() {
return "Repo{" +
@ -171,4 +97,4 @@ public class Repo implements Serializable {
", isDeleted=" + isDeleted +
'}';
}
}
}

@ -10,105 +10,55 @@ import java.io.Serializable;
* @ Author JinXi
* @ Version 1.0
* @ Date 2024/4/25 14:10
* RoleSerializable使便使
* t_role访
*/
@TableName("t_role")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同阶段或环境下对对象操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_role”中的不同字段用于存储角色相关的关键信息下面将逐个进行注释说明。
/**
* ID
* t_roleidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
*
* String便便
*/
private String roleName;
/**
*
* String便
*/
private String code;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对Role对象的属性进行访问和修改下面分别进行注释。
/**
* IDRoleID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* Role
* String
* @return
*/
public String getRoleName() {
return roleName;
}
/**
*
* RoleroleName
* @param roleName
*/
public void setRoleName(String roleName) {
this.roleName = roleName;
}
/**
* Role
* String
* @return
*/
public String getCode() {
return code;
}
/**
*
* Rolecode
* @param code
*/
public void setCode(String code) {
this.code = code;
}
/**
* ObjecttoStringRole
* Role{=,...}便String
* @return Role
*/
@Override
public String toString() {
return "Role{" +
"id=" + id +
",'roleName=" + roleName +
", code=" + code +
"}";
"id=" + id +
", roleName=" + roleName +
", code=" + code +
"}";
}
}
}

@ -8,8 +8,7 @@ import java.time.LocalDateTime;
/**
* <p>
* UserSerializable使便使
* t_user
*
* </p>
*
* @author WeiJin
@ -19,266 +18,133 @@ import java.time.LocalDateTime;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_user”中的不同字段用于存储用户相关的关键信息下面将逐个进行注释说明。
/**
* ID
* t_useridInteger
* MyBatis Plus@TableIdIdType.AUTO便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
*
* 使String便
*/
private String userName;
/**
*
* String线
*/
private String realName;
/**
*
* String@TableFieldFieldFill.INSERT
* @TableField(fill = FieldFill.INSERT)
*/
@TableField(fill = FieldFill.INSERT)
private String password;
/**
*
* String便
*/
private String avatar;
/**
* id
* Integer便访t_roleID
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer roleId;
/**
* id
* Integer便t_gradeID
*/
private Integer gradeId;
/**
* YYYY-MM-DD hh:mm:ss
* LocalDateTimeYYYY-MM-DD hh:mm:ss@TableFieldFieldFill.INSERT
* 便
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 10
* 使Integer1使0便
*/
private Integer status;
/**
*
* Integer01便便
*/
private Integer isDeleted;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对User对象的属性进行访问和修改下面分别进行注释。
/**
* User
* Integer
* @return 01
*/
public Integer getIsDeleted() {
return isDeleted;
}
/**
*
* UserisDeleted01
* @param isDeleted 01
*/
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
/**
* IDUserID
* IDInteger
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* User
* String
* @return
*/
public String getUserName() {
return userName;
}
/**
*
* UseruserName
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* User
* String
* @return
*/
public String getRealName() {
return realName;
}
/**
*
* UserrealName
* @param realName
*/
public void setRealName(String realName) {
this.realName = realName;
}
/**
* User
* String
* @return
*/
public String getPassword() {
return password;
}
/**
*
* Userpassword
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* User
* String
* @return
*/
public String getAvatar() {
return avatar;
}
/**
*
* Useravatar
* @param avatar
*/
public void setAvatar(String avatar) {
this.avatar = avatar;
}
/**
* IDUserID
* Integer
* @return ID
*/
public Integer getRoleId() {
return roleId;
}
/**
* ID
* UserIDroleIdID
* @param roleId ID
*/
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
/**
* IDUserID
* Integer
* @return ID
*/
public Integer getGradeId() {
return gradeId;
}
/**
* ID
* UserIDgradeIdID
* @param gradeId ID
*/
public void setGradeId(Integer gradeId) {
this.gradeId = gradeId;
}
/**
* User
* LocalDateTimeYYYY-MM-DD hh:mm:ss
* LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* @TableField(fill = FieldFill.INSERT)`
*
* LocalDateTime`createTime`
*
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* User10
* Integer
* @return 10
*/
public Integer getStatus() {
return status;
}
/**
*
* Userstate10
* @param state 10
*/
public void setStatus(Integer state) {
this.status = state;
}
/**
* ObjecttoStringUser
* User{=,...}便String
* @return User
*/
@Override
public String toString() {
return "User{" +
@ -294,4 +160,4 @@ public class User implements Serializable {
", isDeleted=" + isDeleted +
'}';
}
}
}

@ -7,8 +7,7 @@ import java.time.LocalDateTime;
/**
* <p>
* UserBookSerializable使便使
* t_user_book
*
* </p>
*
* @author WeiJin
@ -18,155 +17,79 @@ import java.time.LocalDateTime;
public class UserBook implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同阶段或环境下对对象操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_user_book”中的不同字段用于存储错题本相关的关键信息下面将逐个进行注释说明。
/**
* ID
* t_user_bookidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer便ID
*/
private Integer examId;
/**
* id
* Integer便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
* id
* Integer便t_questionID便
*/
private Integer quId;
/**
* YYYY-MM-DD hh:mm:ss
* LocalDateTimeYYYY-MM-DD hh:mm:ss@TableFieldFieldFill.INSERT
* 便
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对UserBook对象的属性进行访问和修改下面分别进行注释。
/**
* IDUserBookID
* Integer
* @return ID
*/
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDUserBookID
* Integer
* @return ID
*/
public Integer getExamId() {
return examId;
}
/**
* IDID
* UserBookIDexamIdID
* @param examId ID
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
* IDUserBookID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* UserBookIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* IDUserBookID
* Integer
* @return ID
*/
public Integer getQuId() {
return quId;
}
/**
* IDID
* UserBookIDquIdID
* @param quId ID
*/
public void setQuId(Integer quId) {
this.quId = quId;
}
/**
* UserBook
* LocalDateTimeYYYY-MM-DD hh:mm:ss
* LocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* @TableField(fill = FieldFill.INSERT)`
*
* LocalDateTime`createTime`
*
* @param createTime LocalDateTime
*/
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
/**
* ObjecttoStringUserBook
* UserBook{=,...}便String
* @return UserBook
*/
@Override
public String toString() {
return "UserBook{" +
"id=" + id +
", examId=" + examId +
", userId=" + userId +
", quId=" + quId +
", createTime=" + createTime +
"}";
"id=" + id +
", examId=" + examId +
", userId=" + userId +
", quId=" + quId +
", createTime=" + createTime +
"}";
}
}
}

@ -10,7 +10,6 @@ import java.io.Serializable;
/**
* *
* UserDailyLoginDurationSerializable使便使线
* @Author Alan
* @Version
* @Date 2024/5/28 10:44 PM
@ -19,106 +18,62 @@ import java.io.Serializable;
public class UserDailyLoginDuration implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同时间或环境下对对象进行序列化和反序列化操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_user_daily_login_duration”中的不同字段用于存储用户每日登录时长相关的关键信息下面将逐个进行注释说明。
/**
* ID
* t_user_daily_login_durationidInteger
* MyBatis Plus@TableIdIdType.AUTO便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* ID
* Integer便便t_userID
* @TableField("user_id")
*/
@TableField("user_id")
private Integer userId;
/**
*
* LocalDateYYYY-MM-DD便
* @TableField("login_date")
*/
@TableField("login_date")
private LocalDate loginDate;
/**
* 线
* 线Integer使使使
* @TableField("total_seconds")
*/
@TableField("total_seconds")
private Integer totalSeconds;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对UserDailyLoginDuration对象的属性进行访问和修改下面分别进行注释。
/**
* IDUserDailyLoginDurationID
* IDInteger
* @return ID
*/
// Getter 和 Setter 方法
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDUserDailyLoginDurationID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* UserDailyLoginDurationIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* UserDailyLoginDuration
* LocalDateYYYY-MM-DDLocalDate
* @return
*/
public LocalDate getLoginDate() {
return loginDate;
}
/**
*
* UserDailyLoginDurationloginDateLocalDate
* @param loginDate LocalDate
*/
public void setLoginDate(LocalDate loginDate) {
this.loginDate = loginDate;
}
/**
* 线UserDailyLoginDuration线
* 使Integer
* @return 线
*/
public Integer getTotalSeconds() {
return totalSeconds;
}
/**
* 线线
* UserDailyLoginDuration线totalSeconds线
* @param totalSeconds 线
*/
public void setTotalSeconds(Integer totalSeconds) {
this.totalSeconds = totalSeconds;
}

@ -4,10 +4,10 @@ import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* UserExamsScoreSerializable使便使
* t_user_exams_score
*
* </p>
*
* @author WeiJin
@ -17,269 +17,139 @@ import java.time.LocalDateTime;
public class UserExamsScore implements Serializable {
private static final long serialVersionUID = 1L;
// 定义序列化版本号用于在对象序列化和反序列化过程中保证版本兼容性初始化为1L确保在不同阶段或环境下对对象操作时数据能正确对应。
// 以下是类中定义的各个成员变量分别对应数据库表“t_user_exams_score”中的不同字段用于存储用户考试成绩相关的关键信息下面将逐个进行注释说明。
/**
* id
* t_user_exams_scoreidInteger
* MyBatis Plus@TableIdIdType.AUTO
* 便ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* id
* Integer便便
* @TableFieldFieldFill.INSERTID
*/
@TableField(fill = FieldFill.INSERT)
private Integer userId;
/**
* id
* Integer便ID便
*/
private Integer examId;
/**
* YYYY-MM-DD hh:mm:ss
* YYYY-MM-DD hh:mm:ssInteger便
*/
private Integer totalTime;
/**
* YYYY-MM-DD hh:mm:ss
* YYYY-MM-DD hh:mm:ssInteger
*/
private Integer userTime;
/**
*
* Integer便
*/
private Integer userScore;
/**
* YYYY-MM-DD hh:mm:ss
* LocalDateTimeYYYY-MM-DD hh:mm:ss
*/
private LocalDateTime limitTime;
/**
*
* Integer线
*/
private Integer count;
/**
* 0 1
* Integer01便
*/
private Integer state;
/**
* -1 0 1
* Integer -101便
*/
private Integer whetherMark;
// 以下是各个成员变量对应的Getter和Setter方法用于获取和设置对象的属性值方便在其他类中对UserExamsScore对象的属性进行访问和修改下面分别进行注释。
public Integer getWhetherMark() {
return whetherMark;
}
public void setWhetherMark(Integer whetherMark) {
this.whetherMark = whetherMark;
}
/**
* IDUserExamsScoreID
* IDInteger
* @return ID
* YYYY-MM-DD hh:mm:ss
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
public Integer getId() {
return id;
}
/**
* IDID
* 使idID
* @param id ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* IDUserExamsScoreID
* Integer
* @return ID
*/
public Integer getUserId() {
return userId;
}
/**
* ID
* UserExamsScoreIDuserIdID
* @param userId ID
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* IDUserExamsScoreID
* Integer
* @return ID
*/
public Integer getExamId() {
return examId;
}
/**
* IDID
* UserExamsScoreIDexamIdID
* @param examId ID
*/
public void setExamId(Integer examId) {
this.examId = examId;
}
/**
* UserExamsScore
* Integer
* @return
*/
public Integer getTotalTime() {
return totalTime;
}
/**
*
* UserExamsScoretotalTime
* @param totalTime
*/
public void setTotalTime(Integer totalTime) {
this.totalTime = totalTime;
}
/**
* UserExamsScore
* Integer
* @return
*/
public Integer getUserTime() {
return userTime;
}
/**
*
* UserExamsScoreuserTime
* @param userTime
*/
public void setUserTime(Integer userTime) {
this.userTime = userTime;
}
/**
* UserExamsScore
* Integer
* @return
*/
public Integer getUserScore() {
return userScore;
}
/**
*
* UserExamsScoreuserScore
* @param userScore
*/
public void setUserScore(Integer userScore) {
this.userScore = userScore;
}
/**
* UserExamsScore
* LocalDateTimeYYYY-MM-DD hh:mm:ssLocalDateTime
* @return
*/
public LocalDateTime getLimitTime() {
return limitTime;
}
/**
*
* UserExamsScorelimitTimeLocalDateTime
* @param limitTime LocalDateTime
*/
public void setLimitTime(LocalDateTime limitTime) {
this.limitTime = limitTime;
}
/**
* UserExamsScore
* Integer
* @return
*/
public Integer getCount() {
return count;
}
/**
*
* UserExamsScorecount
* @param count
*/
public void setCount(Integer count) {
this.count = count;
}
/**
* UserExamsScore01
* Integer
* @return
*/
public Integer getState() {
return state;
}
/**
* 01
* UserExamsScorestate01
* @param state 01
*/
public void setState(Integer state) {
this.state = state;
}
/**
* UserExamsScore-101
* Integer
* @return
*/
public Integer getWhetherMark() {
return whetherMark;
}
/**
* 01
* UserExamsScorewhetherMark-101
* @param whetherMark -101
*/
public void setWhetherMark(Integer whetherMark) {
this.whetherMark = whetherMark;
}
/**
* UserExamsScore
* LocalDateTimeYYYY-MM-DD hh:mm:ssLocalDateTime
* @return
*/
public LocalDateTime getCreateTime() {
return createTime;
}
/**
* @TableField(f
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}

@ -1,6 +1,5 @@
package cn.org.alan.exam.security;
import cn.org.alan.exam.model.entity.User;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -12,59 +11,75 @@ import java.util.Collection;
import java.util.List;
/**
* Spring Security
* Spring Security Spring SecurityUserDetails
* 便
*
* @author haoxr
* @since 3.0.0
*/
// 使用Lombok的 @Data注解自动生成类的Getter、Setter、toString、equals、hashCode等方法简化代码编写
@Data
// 使用Lombok的 @NoArgsConstructor注解自动生成无参构造函数方便对象的创建和初始化
@NoArgsConstructor
public class SysUserDetails implements UserDetails {
// 用于存储用户的权限列表权限类型为SimpleGrantedAuthority它是Spring Security中表示权限的一种简单实现通常包含权限名称如 "ROLE_ADMIN"、"ROLE_USER" 等)
private List<SimpleGrantedAuthority> permissions;
// 关联的实际业务中的用户实体对象,包含了如用户名、密码、用户其他属性等详细信息,用于获取和传递与用户相关的业务数据
private User user;
// 用户名用于在Spring Security认证等流程中作为用户的标识与UserDetails接口中定义的方法对应此处可能后续会根据业务逻辑有具体赋值或使用情况
private String username;
// 构造函数接收一个User对象用于初始化SysUserDetails对象时关联对应的业务用户实体方便后续获取用户相关信息进行认证授权等操作
public SysUserDetails(User user) {
this.user = user;
}
// 实现UserDetails接口的方法用于获取用户的权限集合在Spring Security的授权流程中会调用该方法来判断用户具有哪些权限这里直接返回之前定义的permissions列表
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return permissions;
}
// 设置用户权限的方法,外部可以通过调用该方法来为当前用户对象设置相应的权限列表,方便在用户权限管理等业务场景中更新用户权限信息
public void setPermissions(List<SimpleGrantedAuthority> permissions) {
this.permissions = permissions;
}
// 实现UserDetails接口的方法用于获取用户的密码从关联的User实体对象中获取密码信息并在获取后将User实体中的密码字段设置为空字符串可能出于安全考虑防止密码在后续不必要的场景中被意外获取到然后返回密码
@Override
public String getPassword() {
String myPassword=user.getPassword();
String myPassword = user.getPassword();
user.setPassword("");
return myPassword;
}
// 实现UserDetails接口的方法用于获取用户的用户名这里从关联的User实体对象中获取用户名通常是业务中定义的用户标识字段如账号名等并返回作为Spring Security认证流程中识别用户的重要依据
@Override
public String getUsername() {
return user.getUserName();
}
// 实现UserDetails接口的方法用于判断用户账号是否未过期在Spring Security的认证逻辑中会参考该方法的返回值来确定账号有效性这里直接返回true表示默认账号未过期可根据实际业务需求进行具体逻辑修改
@Override
public boolean isAccountNonExpired() {
return true;
}
// 实现UserDetails接口的方法用于判断用户账号是否未锁定同样在Spring Security的认证检查中起作用返回true表示默认账号未锁定可根据业务规则调整该逻辑比如根据数据库中账号锁定字段等来判断
@Override
public boolean isAccountNonLocked() {
return true;
}
// 实现UserDetails接口的方法用于判断用户的凭证通常就是密码是否未过期此处返回true表示默认凭证未过期若有密码有效期等业务需求可在此处实现相应逻辑判断
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 实现UserDetails接口的方法用于判断用户账号是否启用返回true表示默认用户账号是启用状态可根据业务情况如用户是否被禁用的标识字段等来具体实现该方法的逻辑判断
@Override
public boolean isEnabled() {
return true;
}
}
}

@ -1,264 +1,325 @@
package cn.org.alan.exam.service.impl;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.org.alan.exam.common.result.Result;
import cn.org.alan.exam.converter.UserConverter;
import cn.org.alan.exam.mapper.RoleMapper;
import cn.org.alan.exam.mapper.UserDailyLoginDurationMapper;
import cn.org.alan.exam.mapper.UserMapper;
import cn.org.alan.exam.model.entity.User;
import cn.org.alan.exam.model.entity.UserDailyLoginDuration;
import cn.org.alan.exam.model.form.Auth.LoginForm;
import cn.org.alan.exam.model.form.UserForm;
import cn.org.alan.exam.security.SysUserDetails;
import cn.org.alan.exam.service.IAuthService;
import cn.org.alan.exam.util.DateTimeUtil;
import cn.org.alan.exam.util.JwtUtil;
import cn.org.alan.exam.util.SecretUtils;
import cn.org.alan.exam.util.SecurityUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
package cn.org.alan.exam.service.impl; // 声明包路径
import cn.hutool.captcha.CaptchaUtil; // 引入Hutool工具类库中的验证码工具
import cn.hutool.captcha.LineCaptcha; // 引入Hutool工具类库中的线性验证码
import cn.org.alan.exam.common.result.Result; // 引入项目中的通用返回结果类
import cn.org.alan.exam.converter.UserConverter; // 引入用户转换器
import cn.org.alan.exam.mapper.RoleMapper; // 引入角色Mapper
import cn.org.alan.exam.mapper.UserDailyLoginDurationMapper; // 引入用户每日登录时长Mapper
import cn.org.alan.exam.mapper.UserMapper; // 引入用户Mapper
import cn.org.alan.exam.model.entity.User; // 引入用户实体类
import cn.org.alan.exam.model.entity.UserDailyLoginDuration; // 引入用户每日登录时长实体类
import cn.org.alan.exam.model.form.Auth.LoginForm; // 引入登录表单类
import cn.org.alan.exam.model.form.UserForm; // 引入用户表单类
import cn.org.alan.exam.security.SysUserDetails; // 引入系统用户详情类
import cn.org.alan.exam.service.IAuthService; // 引入认证服务接口
import cn.org.alan.exam.util.DateTimeUtil; // 引入日期时间工具类
import cn.org.alan.exam.util.JwtUtil; // 引入JWT工具类
import cn.org.alan.exam.util.SecretUtils; // 引入加密工具类
import cn.org.alan.exam.util.SecurityUtil; // 引入安全工具类
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 引入MyBatis Plus查询条件包装类
import com.baomidou.mybatisplus.core.toolkit.StringUtils; // 引入MyBatis Plus字符串工具类
import com.fasterxml.jackson.core.JsonProcessingException; // 引入Jackson JSON处理异常类
import com.fasterxml.jackson.databind.ObjectMapper; // 引入Jackson对象映射器
import jakarta.annotation.Resource; // 引入Jakarta注解的依赖注入注解
import jakarta.servlet.ServletOutputStream; // 引入Servlet输出流类
import jakarta.servlet.http.HttpServletRequest; // 引入Servlet HTTP请求类
import jakarta.servlet.http.HttpServletResponse; // 引入Servlet HTTP响应类
import jakarta.servlet.http.HttpSession; // 引入Servlet会话类
import lombok.SneakyThrows; // 引入Lombok异常处理注解
import org.springframework.data.redis.core.StringRedisTemplate; // 引入Spring Data Redis字符串模板类
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; // 引入Spring Security用户名密码认证令牌类
import org.springframework.security.core.authority.SimpleGrantedAuthority; // 引入Spring Security简单权限授权类
import org.springframework.security.core.context.SecurityContextHolder; // 引入Spring Security安全上下文持有者类
import org.springframework.security.core.userdetails.UsernameNotFoundException; // 引入Spring Security用户名未找到异常类
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; // 引入Spring Security BCrypt密码编码器类
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; // 引入Spring Security Web认证详情源类
import org.springframework.stereotype.Service; // 引入Spring服务注解
import java.io.IOException; // 引入IO异常类
import java.time.Duration; // 引入Java时间库中的持续时间类
import java.time.LocalDate; // 引入Java时间库中的本地日期类
import java.time.LocalDateTime; // 引入Java时间库中的本地日期时间类
import java.time.ZoneOffset; // 引入Java时间库中的时区偏移量类
import java.util.ArrayList; // 引入Java集合框架中的ArrayList类
import java.util.List; // 引入Java集合框架中的List接口
import java.util.Objects; // 引入Java工具类中的Objects类
import java.util.concurrent.TimeUnit; // 引入Java并发库中的时间单位枚举
/**
* @Author Alan
* @Version
* @Date 2024/3/28 1:33 PM
*/
@Service
public class AuthServiceImpl implements IAuthService {
private static final String HEARTBEAT_KEY_PREFIX = "user:heartbeat:";
private static final long HEARTBEAT_INTERVAL_MILLIS = 10 * 60 * 1000; // 10分钟
@Service // 声明这是一个Spring服务组件
public class AuthServiceImpl implements IAuthService { // 实现认证服务接口
private static final String HEARTBEAT_KEY_PREFIX = "user:heartbeat:"; // 定义心跳键前缀
private static final long HEARTBEAT_INTERVAL_MILLIS = 10 * 60 * 1000; // 定义心跳间隔时间为10分钟
@Resource
@Resource // 自动注入StringRedisTemplate对象
private StringRedisTemplate stringRedisTemplate;
@Resource
@Resource // 自动注入UserMapper对象
private UserMapper userMapper;
@Resource
@Resource // 自动注入RoleMapper对象
private RoleMapper roleMapper;
@Resource
@Resource // 自动注入UserConverter对象
private UserConverter userConverter;
@Resource
@Resource // 自动注入ObjectMapper对象
private ObjectMapper objectMapper;
@Resource
@Resource // 自动注入JwtUtil对象
private JwtUtil jwtUtil;
@Resource
@Resource // 自动注入UserDailyLoginDurationMapper对象
private UserDailyLoginDurationMapper userDailyLoginDurationMapper;
/**
*
* @param request
* @param loginForm
* @return
* @param request HTTP
* @param loginForm
* @return JWT
*/
@SneakyThrows(JsonProcessingException.class)
@Override
@SneakyThrows(JsonProcessingException.class) // 声明可能抛出的异常
@Override // 重写父类或接口中的方法
public Result<String> login(HttpServletRequest request, LoginForm loginForm) {
// 先判断用户是否通过校验
// 先判断用户是否通过校验(如验证码校验)
String s = stringRedisTemplate.opsForValue().get("isVerifyCode" + request.getSession().getId());
if (StringUtils.isBlank(s)) {
return Result.failed("请先验证验证码");
if (StringUtils.isBlank(s)) { // 如果验证码校验未通过
return Result.failed("请先验证验证码"); // 返回错误信息
}
// 根据用户名获取用户信息
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUserName, loginForm.getUsername());
User user = userMapper.selectOne(wrapper);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); // 创建查询条件包装对象
wrapper.eq(User::getUserName, loginForm.getUsername()); // 设置查询条件为用户名等于登录表单中的用户名
User user = userMapper.selectOne(wrapper); // 执行查询并获取用户对象
// 判读用户名是否存在
if (Objects.isNull(user)) {
throw new UsernameNotFoundException("该用户不存在");
if (Objects.isNull(user)) { // 如果用户不存在
throw new UsernameNotFoundException("该用户不存在"); // 抛出用户名未找到异常
}
if(user.getIsDeleted() == 1){
return Result.failed("该用户已注销");
if(user.getIsDeleted() == 1){ // 如果用户已被注销
return Result.failed("该用户已注销"); // 返回错误信息
}
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
if (!encoder.matches(SecretUtils.desEncrypt(loginForm.getPassword()), user.getPassword())) {
return Result.failed("密码错误");
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); // 创建BCrypt密码编码器对象
if (!encoder.matches(SecretUtils.desEncrypt(loginForm.getPassword()), user.getPassword())) { // 如果密码不匹配
return Result.failed("密码错误"); // 返回错误信息
}
user.setPassword(null);
user.setPassword(null); // 清空用户密码(出于安全考虑)
// 根据用户Id获取权限
List<String> permissions = roleMapper.selectCodeById(user.getRoleId());
List<String> permissions = roleMapper.selectCodeById(user.getRoleId()); // 获取用户角色对应的权限列表
// 数据库获取的权限是字符串springSecurity需要实现GrantedAuthority接口类型所有这里做一个类型转换
// 数据库获取的权限是字符串Spring Security需要实现GrantedAuthority接口类型所以这里做一个类型转换
List<SimpleGrantedAuthority> userPermissions = permissions.stream()
.map(permission -> new SimpleGrantedAuthority("role_" + permission)).toList();
.map(permission -> new SimpleGrantedAuthority("role_" + permission)).toList(); // 将权限字符串转换为SimpleGrantedAuthority对象列表
// 创建一个sysUserDetails对象该类实现了UserDetails接口
SysUserDetails sysUserDetails = new SysUserDetails(user);
SysUserDetails sysUserDetails = new SysUserDetails(user); // 使用用户对象创建系统用户详情对象
// 把转型后的权限放进sysUserDetails对象
sysUserDetails.setPermissions(userPermissions);
sysUserDetails.setPermissions(userPermissions); // 设置用户权限
// 将用户信息转为字符串(此行代码缺少具体操作,可能是后续代码被省略或遗漏)
// 将用户信息转为字符串
String userInfo = objectMapper.writeValueAsString(user);
// 将user对象转换为JSON字符串用于JWT载荷部分。
String token = jwtUtil.createJwt(userInfo, userPermissions.stream().map(String::valueOf).toList());
// 把token放到redis中
// 使用JWT工具创建JWT令牌载荷为用户信息和用户权限列表转换为字符串列表
// 把token放到redis中
stringRedisTemplate.opsForValue().set("token" + request.getSession().getId(), token, 30, TimeUnit.MINUTES);
// 将生成的JWT令牌存储到Redis中键为"token"加上session ID有效期为30分钟。
// 封装用户的身份信息,为后续的身份验证和授权操作提供必要的输入
// 创建UsernamePasswordAuthenticationToken 参数:用户信息,密码,权限列表
// 封装用户的身份信息,为后续的身份验证和授权操作提供必要的输入
// 创建UsernamePasswordAuthenticationToken 参数:用户信息,密码,权限列表
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(sysUserDetails, user.getPassword(), userPermissions);
// 创建一个UsernamePasswordAuthenticationToken对象包含用户详细信息、密码通常不存储明文密码此处可能是示例和权限列表。
// 可选添加Web认证细节
// 可选添加Web认证细节
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 为认证令牌添加Web认证细节如远程地址、会话ID等。
// 用户信息存放进上下文
// 用户信息存放进上下文
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
//用户信息放入
// 将认证令牌设置到安全上下文SecurityContextHolder供后续的身份验证和授权操作使用。
// 清除redis通过校验表示
// 清除redis通过校验表示
stringRedisTemplate.delete("isVerifyCode" + request.getSession().getId());
// 删除Redis中标记验证码已验证的键避免重复使用。
return Result.success("登录成功", token);
}
// 返回登录成功的结果和JWT令牌。
@Override
public Result<String> logout(HttpServletRequest request) {
@Override
public Result<String> logout(HttpServletRequest request) {
// 重写logout方法。
// 清除session
HttpSession session = request.getSession(false);
// 清除session
HttpSession session = request.getSession(false);
// 获取当前请求的会话如果不存在则返回null。
if (session != null) {
// 清除redis
stringRedisTemplate.delete("token" + session.getId());
session.invalidate();
}
if (session != null) {
// 清除redis
stringRedisTemplate.delete("token" + session.getId());
// 删除Redis中存储的JWT令牌。
session.invalidate();
// 使会话失效。
}
return Result.success("退出成功");
}
@SneakyThrows(IOException.class)
@Override
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
// 生成线性图形验证码的静态方法,参数:图片宽,图片高,验证码字符个数 干扰个数
LineCaptcha captcha = CaptchaUtil
.createLineCaptcha(200, 100, 4, 300);
// 把验证码存放进redis
// 获取验证码
String code = captcha.getCode();
String key = "code" + request.getSession().getId();
stringRedisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);
// 把图片响应到输出流
response.setContentType("image/jpeg");
ServletOutputStream os = response.getOutputStream();
captcha.write(os);
os.close();
}
@Override
public Result<String> verifyCode(HttpServletRequest request, String code) {
String key = "code" + request.getSession().getId();
String rightCode = stringRedisTemplate.opsForValue().get(key);
if (StringUtils.isBlank(rightCode)) {
return Result.failed("验证码已过期");
return Result.success("退出成功");
// 返回退出成功的结果。
}
if (!rightCode.equalsIgnoreCase(code)) {
return Result.failed("验证码错误");
}
// 验证码校验后redis清除验证码避免重复使用
stringRedisTemplate.delete(key);
// 验证码校验后redis存入校验成功避免用户登录和注册时不验证验证码直接提交
stringRedisTemplate.opsForValue().set("isVerifyCode" + request.getSession().getId(), "1", 5, TimeUnit.MINUTES);
return Result.success("验证码校验成功");
}
@Override
public Result<String> register(HttpServletRequest request, UserForm userForm) {
String s = stringRedisTemplate.opsForValue().get("isVerifyCode" + request.getSession().getId());
if (StringUtils.isBlank(s)) {
return Result.failed("请先验证验证码");
}
if (!SecretUtils.desEncrypt(userForm.getPassword()).equals(SecretUtils.desEncrypt(userForm.getCheckedPassword()))) {
return Result.failed("两次密码不一致");
@SneakyThrows(IOException.class)
@Override
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
// 重写getCaptcha方法用于生成验证码。
// 生成线性图形验证码的静态方法,参数:图片宽,图片高,验证码字符个数 干扰个数
LineCaptcha captcha = CaptchaUtil
.createLineCaptcha(200, 100, 4, 300);
// 创建一个线性图形验证码。
// 把验证码存放进redis
// 获取验证码
String code = captcha.getCode();
// 获取生成的验证码字符串。
String key = "code" + request.getSession().getId();
// 构造Redis键包含会话ID。
stringRedisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);
// 将验证码存储到Redis中有效期为5分钟。
// 把图片响应到输出流
response.setContentType("image/jpeg");
// 设置响应内容类型为JPEG图像。
ServletOutputStream os = response.getOutputStream();
// 获取响应的输出流。
captcha.write(os);
// 将验证码图片写入输出流。
os.close();
// 关闭输出流。
}
User user = userConverter.fromToEntity(userForm);
@Override
public Result<String> verifyCode(HttpServletRequest request, String code) {
// 重写verifyCode方法用于验证用户输入的验证码。
user.setPassword(new BCryptPasswordEncoder().encode(SecretUtils.desEncrypt(user.getPassword())));
user.setRoleId(1);
userMapper.insert(user);
// 注册成功把redis的是否通过校验验证码删除防止用户注册后立马登录还可以使用
stringRedisTemplate.delete("isVerifyCode" + request.getSession().getId());
return Result.success("注册成功");
}
String key = "code" + request.getSession().getId();
// 构造Redis键包含会话ID。
String rightCode = stringRedisTemplate.opsForValue().get(key);
// 从Redis中获取正确的验证码。
if (StringUtils.isBlank(rightCode)) {
return Result.failed("验证码已过期");
}
// 如果Redis中没有验证码则返回验证码已过期的结果。
if (!rightCode.equalsIgnoreCase(code)) {
return Result.failed("验证码错误");
}
// 如果用户输入的验证码与Redis中的不匹配则返回验证码错误的结果。
// 验证码校验后redis清除验证码避免重复使用
stringRedisTemplate.delete(key);
// 删除Redis中的验证码。
/**
*
*
* @return
*/
@Override
@SneakyThrows(value = JsonProcessingException.class)
public Result<String> sendHeartbeat(HttpServletRequest request) {
String key = HEARTBEAT_KEY_PREFIX + SecurityUtil.getUserId();
String lastHeartbeatStr = stringRedisTemplate.opsForValue().getAndDelete(key);
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
stringRedisTemplate.opsForValue().set(key, now.toString());
if (lastHeartbeatStr != null) {
LocalDateTime lastHeartbeat = LocalDateTime.parse(lastHeartbeatStr);
Duration durationSinceLastHeartbeat = Duration.between(lastHeartbeat, LocalDateTime.now(ZoneOffset.UTC));
LocalDate date = DateTimeUtil.getDate();
// 实现累加逻辑,比如更新数据库中的记录
LambdaQueryWrapper<UserDailyLoginDuration> userDailyLoginDurationLambdaQueryWrapper = new LambdaQueryWrapper<>();
userDailyLoginDurationLambdaQueryWrapper.eq(UserDailyLoginDuration::getUserId,SecurityUtil.getUserId())
.eq(UserDailyLoginDuration::getLoginDate, date);
List<UserDailyLoginDuration> userDailyLoginDurations =
userDailyLoginDurationMapper.selectList(userDailyLoginDurationLambdaQueryWrapper);
if(userDailyLoginDurations.isEmpty()){
UserDailyLoginDuration userDailyLoginDuration = new UserDailyLoginDuration();
userDailyLoginDuration.setUserId(SecurityUtil.getUserId());
userDailyLoginDuration.setLoginDate(date);
userDailyLoginDuration.setTotalSeconds(0);
userDailyLoginDurationMapper.insert(userDailyLoginDuration);
}else {
UserDailyLoginDuration userDailyLoginDuration = new UserDailyLoginDuration();
userDailyLoginDuration.setTotalSeconds(userDailyLoginDurations.get(0)
.getTotalSeconds()+(int)durationSinceLastHeartbeat.getSeconds());
userDailyLoginDuration.setId(userDailyLoginDurations.get(0).getId());
userDailyLoginDurationMapper.updateById(userDailyLoginDuration);
// 验证码校验后redis存入校验成功避免用户登录和注册时不验证验证码直接提交
stringRedisTemplate.opsForValue().set("isVerifyCode" + request.getSession().getId(), "1", 5, TimeUnit.MINUTES);
// 在Redis中标记验证码已验证有效期为5分钟。
return Result.success("验证码校验成功");
// 返回验证码校验成功的结果。
}
@Override
public Result<String> register(HttpServletRequest request, UserForm userForm) {
// 重写register方法用于用户注册。
String s = stringRedisTemplate.opsForValue().get("isVerifyCode" + request.getSession().getId());
// 检查Redis中是否有验证码已验证的标记。
if (StringUtils.isBlank(s)) {
return Result.failed("请先验证验证码");
}
// 如果没有,则返回请先验证验证码的结果。
if (!SecretUtils.desEncrypt(userForm.getPassword()).equals(SecretUtils.desEncrypt(userForm.getCheckedPassword()))) {
return Result.failed("两次密码不一致");
}
// 验证用户输入的两次密码是否一致。
User user = userConverter.fromToEntity(userForm);
// 将用户表单转换为用户实体。
user.setPassword(new BCryptPasswordEncoder().encode(SecretUtils.desEncrypt(user.getPassword())));
// 对用户密码进行加密。
user.setRoleId(1);
// 设置用户角色ID此处为示例通常根据用户选择或系统策略设置
userMapper.insert(user);
// 将用户实体插入数据库。
// 注册成功把redis的是否通过校验验证码删除防止用户注册后立马登录还可以使用
stringRedisTemplate.delete("isVerifyCode" + request.getSession().getId());
// 删除Redis中验证码已验证的标记防止重复注册。
return Result.success("注册成功");
// 返回注册成功的结果。
}
ArrayList<String> permissions = new ArrayList<>();
permissions.add(SecurityUtil.getRole());
SysUserDetails principal = (SysUserDetails) (SecurityContextHolder.getContext().getAuthentication().getPrincipal());
User user = principal.getUser();
String string = objectMapper.writeValueAsString(user);
String jwt = jwtUtil.createJwt(string, permissions);
stringRedisTemplate.opsForValue().set("token" + request.getSession().getId(), jwt, 30, TimeUnit.MINUTES);
return Result.success("请求成功",jwt);
}
}
/**
*
*
* @return
*/
@Override
@SneakyThrows(value = JsonProcessingException.class)
public Result<String> sendHeartbeat(HttpServletRequest request) {
// 重写sendHeartbeat方法用于用户发送心跳以更新最后活跃时间。
String key = HEARTBEAT_KEY_PREFIX + SecurityUtil.getUserId();
// 构造Redis键包含用户ID和前缀。
String lastHeartbeatStr = stringRedisTemplate.opsForValue().getAndDelete(key);
// 获取并删除Redis中存储的上一次心跳时间。
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
// 获取当前时间UTC时区
stringRedisTemplate.opsForValue().set(key, now.toString());
// 将当前时间存储到Redis中作为新的心跳时间。
if (lastHeartbeatStr != null) {
LocalDateTime lastHeartbeat = LocalDateTime.parse(lastHeartbeatStr);
// 解析上一次心跳时间。
Duration durationSinceLastHeartbeat = Duration.between(lastHeartbeat, LocalDateTime.now(ZoneOffset.UTC));
// 计算上一次心跳到现在的持续时间。
LocalDate date = DateTimeUtil.getDate();
// 获取当前日期。
// 实现累加逻辑,比如更新数据库中的记录
LambdaQueryWrapper<UserDailyLoginDuration> userDailyLoginDurationLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 构造查询条件。
userDailyLoginDurationLambdaQueryWrapper.eq(UserDailyLoginDuration::getUserId,SecurityUtil.getUserId())
.eq(UserDailyLoginDuration::getLoginDate, date);
// 设置查询条件为用户ID和登录日期。
List<UserDailyLoginDuration> userDailyLoginDurations =
userDailyLoginDurationMapper.selectList(userDailyLoginDurationLambdaQueryWrapper);
// 查询用户当日的登录时长记录。
if(userDailyLoginDurations.isEmpty()){
UserDailyLoginDuration userDailyLoginDuration = new UserDailyLoginDuration();
// 如果没有记录,则创建新记录。
userDailyLoginDuration.setUserId(SecurityUtil.getUserId());
userDailyLoginDuration.setLoginDate(date);
userDailyLoginDuration.setTotalSeconds(0);
userDailyLoginDurationMapper.insert(userDailyLoginDuration);
// 设置用户ID、登录日期和初始时长并插入数据库。
}else {
UserDailyLoginDuration userDailyLoginDuration = new UserDailyLoginDuration();
// 如果有记录

@ -1,36 +1,92 @@
package cn.org.alan.exam.service.impl;
// 指定包名,组织代码结构
import cn.org.alan.exam.common.result.Result;
// 导入通用结果类,用于封装返回结果
import cn.org.alan.exam.converter.CertificateConverter;
// 导入转换器类,用于表单对象与实体对象之间的转换
import cn.org.alan.exam.mapper.CertificateMapper;
// 导入Mapper接口用于数据库操作
import cn.org.alan.exam.model.entity.Certificate;
// 导入实体类,对应数据库中的表
import cn.org.alan.exam.model.form.CertificateForm;
// 导入表单类,用于接收前端传递的数据
import cn.org.alan.exam.model.vo.GradeVO;
// 导入视图对象类,用于封装返回给前端的数据
import cn.org.alan.exam.model.vo.NoticeVO;
// 导入通知视图对象类
import cn.org.alan.exam.model.vo.certificate.MyCertificateVO;
// 导入我的证书视图对象类
import cn.org.alan.exam.service.ICertificateService;
// 导入服务接口,定义服务层方法
import cn.org.alan.exam.util.CacheClient;
// 导入缓存客户端,用于操作缓存
import cn.org.alan.exam.util.DateTimeUtil;
// 导入日期时间工具类
import cn.org.alan.exam.util.SecurityUtil;
// 导入安全工具类,用于获取当前用户信息
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
// 导入LambdaQueryWrapper用于构建查询条件
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
// 导入LambdaUpdateWrapper用于构建更新条件
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入分页接口
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
// 导入字符串工具类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入Page类实现IPage接口用于分页查询
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入ServiceImpl类简化服务层实现
import jakarta.annotation.Resource;
// 导入资源注入注解
import org.springframework.data.redis.core.StringRedisTemplate;
// 导入Redis字符串模板类用于操作Redis字符串数据
import org.springframework.stereotype.Service;
// 导入服务注解,用于声明服务类
import org.springframework.transaction.annotation.Transactional;
// 导入事务注解,用于声明事务方法
import java.util.ArrayList;
// 导入ArrayList类用于创建动态数组
import java.util.List;
// 导入List接口用于声明集合
import java.util.Map;
// 导入Map接口用于声明键值对集合
import java.util.Objects;
// 导入Objects类包含一些实用方法如非空判断
import java.util.concurrent.TimeUnit;
// 导入TimeUnit枚举用于指定时间单位
import java.util.function.Function;
import java.util.stream.Collectors;
// 导入Function接口用于声明函数式接口
import java.util.stream.Collectors;
// 导入Collectors类用于声明收集器进行集合的聚合操作
/**
* <p>
@ -41,36 +97,48 @@ import java.util.stream.Collectors;
* @since 2024-03-21
*/
@Service
// 声明这是一个服务类Spring会自动管理这个类的实例
public class CertificateServiceImpl extends ServiceImpl<CertificateMapper, Certificate> implements ICertificateService {
@Resource
// 注入CertificateMapper用于数据库操作
private CertificateMapper certificateMapper;
@Resource
// 注入CertificateConverter用于表单对象与实体对象之间的转换
private CertificateConverter certificateConverter;
@Resource
// 注入CacheClient用于操作缓存
private CacheClient cacheClient;
@Resource
// 注入StringRedisTemplate用于操作Redis字符串数据
private StringRedisTemplate stringRedisTemplate;
//新增证书
@Override
@Transactional
// 声明这是一个事务方法,保证方法中的数据库操作要么全部成功,要么全部回滚
public Result<String> addCertificate(CertificateForm certificateForm) {
Certificate certificate = certificateConverter.fromToEntity(certificateForm);
//自动生成时间
// 将表单对象转换为实体对象
int insertRows = certificateMapper.insert(certificate);
// 调用Mapper的insert方法将实体对象插入数据库返回插入的行数
if (insertRows > 0) {
if (certificate.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
// 如果是更新操作这里逻辑可能有误新增操作通常不会检查ID,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:certificate:pagingCertificate:"+certificate.getId().toString()); // 删除旧缓存
// Map<Integer, Certificate> map = Map.of(certificate.getId(), certificate);
// cacheClient.batchPut("cache:certificate:pagingCertificate:",map,10L,TimeUnit.MINUTES); // 存储新数据
// 注释的代码是假设要存储新数据到缓存,但在这里并未执行
}
return Result.success("添加证书成功");
// 返回成功结果
} else {
return Result.failed("添加证书失败");
// 返回失败结果
}
}
@ -79,15 +147,20 @@ public class CertificateServiceImpl extends ServiceImpl<CertificateMapper, Certi
public Result<IPage<Certificate>> pagingCertificate(Integer pageNum, Integer pageSize, String certificateName,
String certificationUnit) {
// 查询满足条件的总记录数
int total = certificateMapper.countByCondition(SecurityUtil.getUserId(), certificateName,certificationUnit); // 假设gradeMapper中实现了根据条件计数的方法
int total = certificateMapper.countByCondition(SecurityUtil.getUserId(), certificateName,certificationUnit);
// 调用Mapper的countByCondition方法根据条件计数获取总记录数
// 计算偏移量
int offset = (pageNum - 1) * pageSize;
// 根据页码和页面大小计算偏移量
// 查询分页ID列表
List<Integer> certificateIds = certificateMapper.selectCertificateIdsPage(SecurityUtil.getUserId(), certificateName,certificationUnit, offset, pageSize);
// 调用Mapper的selectCertificateIdsPage方法根据条件分页查询ID列表
// 批量从缓存中获取GradeVO对象
// 批量从缓存中获取Certificate对象
Map<Integer, Certificate> cachedGradesMap = cacheClient.batchGet("cache:certificate:pagingCertificate:",certificateIds, Certificate.class);
// 从缓存中获取ID列表对应的Certificate对象
// 确定未命中的ID列表
List<Integer> missIds = new ArrayList<>();
@ -96,75 +169,49 @@ public class CertificateServiceImpl extends ServiceImpl<CertificateMapper, Certi
missIds.add(id);
}
}
// 遍历ID列表找出未缓存的ID
// 如果有未命中的ID从数据库批量查询并更新缓存
if (!missIds.isEmpty()) {
List<Certificate> missedGrades = certificateMapper.batchSelectByIds(missIds);
// 假设GradeVO的ID为getId()使用Collectors.toMap转换
// 从数据库查询未缓存的Certificate对象
// 使用Collectors.toMap转换构建Map
Map<Integer, Certificate> missedGradesMap = missedGrades.stream()
.collect(Collectors.toMap(Certificate::getId, Function.identity()));
// 将查询结果转换为Map
// 更新缓存
cacheClient.batchPut("cache:certificate:pagingCertificate:",missedGradesMap,10L, TimeUnit.MINUTES);
// 将查询结果存入缓存
// 合并缓存结果
cachedGradesMap.putAll(missedGradesMap);
// 将新查询的结果合并到原来的Map中
}
// 根据ID列表从缓存中获取完整的GradeVO对象列表
// 根据ID列表从缓存中获取完整的Certificate对象列表
List<Certificate> finalResult = new ArrayList<>(certificateIds.size());
for (Integer id : certificateIds) {
finalResult.add(cachedGradesMap.get(id));
}
// 根据ID列表从合并后的Map中获取Certificate对象构建结果列表
// 构建并返回IPage对象
IPage<Certificate> resultPage = new Page<>(pageNum, pageSize, Long.valueOf(total));
resultPage.setRecords(finalResult);
// 创建分页对象,设置页码、页面大小、总记录数和结果列表
return Result.success("查询成功", resultPage);
// 返回成功结果,包含分页对象
}
@Override
@Transactional
public Result<String> updateCertificate(CertificateForm certificateForm) {
Certificate certificate = certificateConverter.fromToEntity(certificateForm);
// 将表单对象转换为实体对象
// 调用mapper方法更新证书
int affectedRows = certificateMapper.updateById(certificate);
if (affectedRows > 0) {
if (certificate.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:certificate:pagingCertificate:"+certificate.getId().toString()); // 删除旧缓存
// Map<Integer, Certificate> map = Map.of(certificate.getId(), certificate);
// cacheClient.batchPut("cache:certificate:pagingCertificate:",map,10L,TimeUnit.MINUTES); // 存储新数据
}
return Result.success("修改证书成功");
} else {
return Result.failed("修改证书失败");
}
}
@Override
@Transactional
public Result<String> deleteCertificate(Integer id) {
LambdaUpdateWrapper<Certificate> certificateLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
certificateLambdaUpdateWrapper.eq(Certificate::getId,id)
.set(Certificate::getIsDeleted,1);
int affectedRows = certificateMapper.update(certificateLambdaUpdateWrapper);
if (affectedRows > 0) {
stringRedisTemplate.delete("cache:certificate:pagingCertificate:"+id);
return Result.success("删除证书成功");
} else {
return Result.failed("删除证书失败");
}
}
@Override
public Result<IPage<MyCertificateVO>> getMyCertificatePaging(Integer pageNum, Integer pageSize,String examName) {
Page<MyCertificateVO> myCertificateVOPage = new Page<>();
myCertificateVOPage = certificateMapper.selectMyCertificate(myCertificateVOPage,pageNum,pageSize, SecurityUtil.getUserId(),examName);
return Result.success("查询成功",myCertificateVOPage);
}
}
// 调用Mapper的updateById方法根据ID更新实体对象返回

@ -1,10 +1,26 @@
package cn.org.alan.exam.service.impl;
// 这行代码声明了该类所在的包名,表明这个类属于"cn.org.alan.exam.service.impl"包,包名通常按照一定的层次结构来组织代码,方便管理和区分不同功能模块的类,
// 在这里可以看出是该项目中与考试相关的服务实现类所在的包,后续其他类可以通过这个包名来引用该类等操作。
import cn.org.alan.exam.mapper.CertificateUserMapper;
// 导入"cn.org.alan.exam.mapper"包下的CertificateUserMapper类从类名推测这个Mapper类是用于操作CertificateUser证书用户相关的实体类可能对应数据库中的表
// 的数据库持久层接口,通过导入它可以在当前类中使用其定义的方法来进行与证书用户数据相关的数据库操作,比如插入、查询等操作。
import cn.org.alan.exam.model.entity.CertificateUser;
// 导入"cn.org.alan.exam.model.entity"包下的CertificateUser类这个类就是代表证书用户相关的实体类定义了与证书用户相关的各种属性比如用户ID、证书ID等
// 它通常与数据库中的表结构相对应,在代码中用于承载和传递证书用户相关的数据,方便进行业务逻辑处理以及与数据库的交互操作。
import cn.org.alan.exam.service.ICertificateUserService;
// 导入"cn.org.alan.exam.service"包下的ICertificateUserService接口这个接口应该是定义了关于证书用户相关服务的一系列方法签名比如获取证书用户信息、添加证书用户等方法的声明
// 当前类实现了这个接口,意味着要按照接口中定义的规范来实现具体的业务逻辑方法,保证服务层对证书用户操作的一致性和规范性。
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入MyBatis Plus框架中的ServiceImpl类它是一个基础的服务实现类提供了很多通用的数据库操作相关的方法实现比如基于实体类和对应的Mapper接口进行常见的增删改查操作等
// 在这里继承它可以复用其已有的功能代码,减少重复开发,方便快速构建针对特定实体类的服务实现类,只需要关注具体业务逻辑特有的部分即可。
import org.springframework.stereotype.Service;
// 导入Spring框架中的@Service注解这个注解用于标识当前类是一个服务层Service Layer的组件告诉Spring容器这个类是一个业务逻辑处理的服务类
// Spring容器会对其进行管理例如进行依赖注入等操作使得该类可以方便地在整个项目中被其他组件调用参与到业务流程中。
/**
* <p>
@ -14,7 +30,19 @@ import org.springframework.stereotype.Service;
* @author WeiJin
* @since 2024-03-21
*/
// 以上是JavaDoc格式的注释用于对类进行简要的说明这里说明这个类是一个服务实现类不过具体实现的服务相关内容没有详细描述
// 通常可以在这里进一步补充类的功能、作用等详细信息,方便后续开发人员查看和理解代码。
@Service
// 使用@Service注解将当前类标记为Spring框架中的服务组件让Spring容器识别并管理它这样在其他地方比如控制层等就可以通过依赖注入的方式来使用这个服务类提供的方法了。
public class CertificateUserServiceImpl extends ServiceImpl<CertificateUserMapper, CertificateUser> implements ICertificateUserService {
// 定义了一个名为CertificateUserServiceImpl的类它继承自MyBatis Plus框架中的ServiceImpl类
// 并指定了泛型参数为CertificateUserMapper用于操作证书用户数据的持久层接口和CertificateUser证书用户相关的实体类
// 这样就继承了ServiceImpl类中针对CertificateUser实体类的通用数据库操作方法实现比如可以直接调用父类的save、update等方法来操作数据库中的证书用户数据。
// 同时这个类还实现了ICertificateUserService接口意味着它需要按照该接口中定义的方法签名来实现具体的业务逻辑方法
// 从而提供符合接口规范的证书用户相关服务,确保在整个项目中,对于证书用户服务的调用具有统一的接口和行为表现,方便进行代码的维护和扩展。
// 不过目前这个类中没有添加额外的自定义方法实现(可能后续会根据业务需求添加具体的业务逻辑代码),只是通过继承和实现接口构建了一个基础的服务实现类框架,
// 复用了已有框架提供的功能来处理与证书用户相关的数据库操作和服务提供相关事宜。
}

@ -1,17 +1,38 @@
package cn.org.alan.exam.service.impl;
// 声明包名,用于组织类文件
import cn.org.alan.exam.common.result.Result;
// 导入Result类用于封装返回结果
import cn.org.alan.exam.mapper.ExamQuAnswerMapper;
// 导入Mapper接口用于数据库操作
import cn.org.alan.exam.model.entity.ExamQuAnswer;
// 导入实体类,对应数据库表
import cn.org.alan.exam.model.vo.score.QuestionAnalyseVO;
// 导入VO类用于封装返回给前端的数据
import cn.org.alan.exam.service.IExamQuAnswerService;
// 导入服务接口,定义业务逻辑
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入MyBatis Plus的QueryWrapper类用于构建查询条件
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入ServiceImpl类用于简化服务类的实现
import jakarta.annotation.Resource;
// 导入Resource注解用于注入依赖
import org.springframework.stereotype.Service;
// 导入Service注解用于声明服务类
import java.text.DecimalFormat;
// 导入DecimalFormat类用于格式化数字
import java.util.List;
// 导入List接口用于处理集合数据
/**
* <p>
@ -22,19 +43,31 @@ import java.util.List;
* @since 2024-03-21
*/
@Service
// 使用Service注解声明这是一个服务类Spring会自动管理这个类的实例
public class ExamQuAnswerServiceImpl extends ServiceImpl<ExamQuAnswerMapper, ExamQuAnswer> implements IExamQuAnswerService {
@Resource
// 使用Resource注解注入ExamQuAnswerMapper的实例用于数据库操作
private ExamQuAnswerMapper examQuAnswerMapper;
@Override
// 重写IExamQuAnswerService接口中的questionAnalyse方法
public Result<QuestionAnalyseVO> questionAnalyse(Integer examId, Integer questionId) {
QuestionAnalyseVO questionAnalyseVO = examQuAnswerMapper.questionAnalyse(examId, questionId);
// 调用Mapper接口的questionAnalyse方法获取题目分析数据
//正确率保留两位小数
DecimalFormat format = new DecimalFormat("#.00");
// 创建DecimalFormat对象用于格式化数字为保留两位小数的字符串
String strAccuracy = format.format(questionAnalyseVO.getRightCount() / questionAnalyseVO.getTotalCount());
// 计算正确率并格式化为保留两位小数的字符串
questionAnalyseVO.setAccuracy(Double.parseDouble(strAccuracy));
// 将格式化后的正确率字符串转换为Double类型并设置到VO对象中
return Result.success(null, questionAnalyseVO);
// 封装返回结果,返回成功信息和题目分析数据
}
}
}

@ -1,10 +1,33 @@
package cn.org.alan.exam.service.impl;
// 这行代码定义了该类所属的包名,表明这个类位于"cn.org.alan.exam.service.impl"这个包结构下。
// 在Java项目中包用于对类进行分类管理按照功能、模块等逻辑将相关的类组织在一起方便代码的维护、查找以及避免类名冲突等这里就是存放考试相关服务实现类的包。
import cn.org.alan.exam.mapper.ExamRepoMapper;
// 导入"cn.org.alan.exam.mapper"包下的ExamRepoMapper类。从类名可以推测ExamRepoMapper是一个数据持久层的映射器Mapper接口
// 它的作用是定义了一系列用于操作ExamRepo考试资源库相关实体类大概率对应数据库中的表实体对应数据库表的方法比如增删改查等操作方法的声明
// 导入它后就能在当前类中调用其相关方法与数据库中的考试资源库数据进行交互了。
import cn.org.alan.exam.model.entity.ExamRepo;
// 导入"cn.org.alan.exam.model.entity"包下的ExamRepo类这个类代表考试资源库相关的实体类它里面定义了和考试资源库对应的各种属性
// 例如可能包含资源库的名称、描述、包含的资源类型等属性,并且这些属性通常与数据库中存储考试资源库信息的表结构中的字段是一一对应的,
// 在代码中主要用于承载和传递考试资源库相关的数据,方便后续业务逻辑围绕这些数据进行处理以及与数据库之间的交互操作。
import cn.org.alan.exam.service.IExamRepoService;
// 导入"cn.org.alan.exam.service"包下的IExamRepoService接口。这个接口是定义了关于考试资源库相关服务的统一规范
// 里面声明了一系列针对考试资源库业务操作的方法,比如获取资源库列表、添加资源库、更新资源库信息等方法签名,
// 当前类实现了这个接口,那就意味着需要按照接口里规定的方法签名来具体实现相应的业务逻辑,以此保证在整个项目中,
// 对考试资源库服务的调用都遵循统一的接口标准,便于不同模块之间协同以及代码的后续扩展和维护。
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入MyBatis Plus框架提供的ServiceImpl类。它是一个非常实用的基础服务实现类封装了很多通用的、基于数据库操作的功能实现
// 比如基于给定的实体类和对应的Mapper接口它已经实现好了像常见的插入save、更新update、删除delete以及根据条件查询select等方法
// 在这里让当前类继承它,就能复用这些已经写好的通用数据库操作逻辑,我们只需要关注本类涉及的考试资源库业务逻辑中特有的部分就可以了,
// 这样可以大大减少代码的重复编写,提高开发效率。
import org.springframework.stereotype.Service;
// 导入Spring框架中的@Service注解类。这个注解用于向Spring容器表明当前类是一个服务层Service Layer的组件
// Spring容器在进行组件扫描时识别到这个注解就会把该类纳入管理范围例如会为其进行依赖注入等操作
// 使得这个类可以方便地在整个项目的其他地方被调用,参与到具体的业务流程中,实现业务逻辑的解耦和复用。
/**
* <p>
@ -14,7 +37,21 @@ import org.springframework.stereotype.Service;
* @author WeiJin
* @since 2024-03-21
*/
// 这是JavaDoc形式的注释部分用于对类做一个简单的介绍说明这里只是简单提及是服务实现类
// 其实可以更详细地描述下这个类针对考试资源库具体实现了哪些业务功能等信息,方便后续查看代码的开发人员快速理解类的作用。
@Service
// 使用@Service注解标记当前类告诉Spring容器这是一个需要被管理的服务类。
// 有了这个注解Spring容器会按照配置对这个类进行实例化、依赖注入等操作使其成为整个Spring应用上下文ApplicationContext中的一个组件
// 这样其他的组件(比如控制层的类等)就可以通过依赖注入的方式获取并使用这个服务类所提供的功能了。
public class ExamRepoServiceImpl extends ServiceImpl<ExamRepoMapper, ExamRepo> implements IExamRepoService {
// 定义了一个名为ExamRepoServiceImpl的公共类它继承自MyBatis Plus框架的ServiceImpl类
// 并指定了泛型参数为ExamRepoMapper前面导入的用于操作考试资源库数据的Mapper接口和ExamRepo考试资源库相关的实体类
// 通过这种继承方式当前类自动获得了ServiceImpl类中针对ExamRepo实体类的那些通用的数据库操作方法实现
// 比如可以直接调用父类中定义好的插入、更新、查询等方法来操作数据库里的考试资源库相关数据,减少了自行编写数据库操作代码的工作量。
// 同时这个类还实现了IExamRepoService接口这就要求它必须按照IExamRepoService接口里声明的各个方法签名来具体实现对应的业务逻辑
// 不过目前这个类里暂时没有添加额外的自定义方法实现代码(可能后续根据业务需求会添加具体的考试资源库相关业务逻辑代码),
// 现阶段只是搭建了一个符合框架规范的基础服务实现类的框架结构,借助已有的框架功能来处理与考试资源库相关的数据库操作以及服务提供相关事务。
}

@ -40,177 +40,223 @@ import java.util.stream.Collectors;
*/
@Service
public class GradeServiceImpl extends ServiceImpl<GradeMapper, Grade> implements IGradeService {
// 注入GradeMapper用于操作班级数据的持久层接口
@Resource
private GradeMapper gradeMapper;
// 注入ExamMapper可能用于操作考试相关数据的持久层接口此处从命名推测
@Resource
private ExamMapper examMapper;
// 注入QuestionMapper可能用于操作题目相关数据的持久层接口此处从命名推测
@Resource
private QuestionMapper questionMapper;
// 注入GradeConverter用于不同班级相关对象之间的转换比如表单对象和实体对象等
@Resource
private GradeConverter gradeConverter;
// 注入UserMapper用于操作用户相关数据的持久层接口
@Resource
private UserMapper userMapper;
// 注入IGradeService自身接口注入可能用于调用自身定义的其他服务方法此处从代码结构推测
@Resource
private IGradeService gradeService;
// 注入StringRedisTemplate用于操作Redis字符串类型数据常用于缓存相关操作
@Resource
private StringRedisTemplate stringRedisTemplate;
// 注入CacheClient自定义的缓存客户端用于更方便地进行缓存操作
@Resource
private CacheClient cacheClient;
// 添加班级的方法,事务性操作,成功返回添加成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> addGrade(GradeForm gradeForm) {
// 生成班级口令
// 生成班级口令调用工具类方法生成一个长度为18的班级口令
gradeForm.setCode(ClassTokenGenerator.generateClassToken(18));
// 实体转换
// 将GradeForm对象转换为Grade实体对象方便后续持久化操作
Grade grade = gradeConverter.formToEntity(gradeForm);
// 添加数据
// 调用gradeMapper的insert方法将班级数据插入到数据库中返回受影响的行数
int rows = gradeMapper.insert(grade);
// 如果插入操作没有影响到任何行,即插入失败,返回添加失败的结果信息
if (rows == 0) {
return Result.failed("添加失败");
}
// 更新缓存
if (grade.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:grade:getPaging:"+grade.getId().toString()); // 删除旧缓存
// 如果插入成功班级数据有了有效的ID这里假设插入后会自动生成ID进行缓存相关操作
if (grade.getId()!= null) { // 确保ID有效
// 如果是更新操作(此处逻辑可能不太准确,实际上是添加操作,但代码结构类似更新时的缓存处理逻辑),先从缓存中移除旧数据
// 根据班级ID删除对应的缓存数据缓存键格式为"cache:grade:getPaging:" + 班级ID
stringRedisTemplate.delete("cache:grade:getPaging:" + grade.getId().toString());
// GradeVO updatedGradeVO = gradeConverter.GradeToGradeVO(grade); // 转换为视图对象
// Map<Integer, GradeVO> map = Map.of(updatedGradeVO.getId(), updatedGradeVO);
// cacheClient.batchPut("cache:grade:getPaging:",map,10L,TimeUnit.MINUTES); // 存储新数据
}
stringRedisTemplate.delete("cache:grade:getAllGrade:"+SecurityUtil.getUserId());
// 根据当前用户ID删除获取所有班级的缓存数据缓存键格式为"cache:grade:getAllGrade:" + 当前用户ID
stringRedisTemplate.delete("cache:grade:getAllGrade:" + SecurityUtil.getUserId());
// 返回添加成功的结果信息
return Result.success("添加成功");
}
// 更新班级信息的方法,事务性操作,成功返回更新成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> updateGrade(Integer id, GradeForm gradeForm) {
// 建立更新条件
// 创建一个LambdaUpdateWrapper对象用于构建更新班级信息的条件和要更新的字段
LambdaUpdateWrapper<Grade> gradeUpdateWrapper = new LambdaUpdateWrapper<>();
// 设置要更新的班级名称字段值为传入的gradeForm中的班级名称
gradeUpdateWrapper
.set(Grade::getGradeName, gradeForm.getGradeName())
// 设置更新条件为班级ID等于传入的id参数
.eq(Grade::getId, id);
// 更新班级
// 调用gradeMapper的update方法根据上面构建的更新条件和要更新的字段来更新班级数据,返回受影响的行数
int rows = gradeMapper.update(gradeUpdateWrapper);
// 如果更新操作没有影响到任何行,即更新失败,返回修改失败的结果信息
if (rows == 0) {
return Result.failed("修改失败");
}
// 根据ID获取更新后的班级对象用于后续缓存操作等可能需要获取最新数据来更新缓存等
Grade byId = getById(id);
// 更新缓存
if (byId.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:grade:getPaging:"+byId.getId().toString()); // 删除旧缓存
// 如果获取到的班级对象有有效的ID进行缓存相关操作
if (byId.getId()!= null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,根据班级ID删除对应的缓存数据缓存键格式为"cache:grade:getPaging:" + 班级ID
stringRedisTemplate.delete("cache:grade:getPaging:" + byId.getId().toString());
// GradeVO updatedGradeVO = gradeConverter.GradeToGradeVO(byId); // 转换为视图对象
// Map<Integer, GradeVO> map = Map.of(updatedGradeVO.getId(), updatedGradeVO);
// cacheClient.batchPut("cache:grade:getPaging:",map,10L,TimeUnit.MINUTES); // 存储新数据
}
stringRedisTemplate.delete("cache:grade:getAllGrade:"+SecurityUtil.getUserId());
// 根据当前用户ID删除获取所有班级的缓存数据缓存键格式为"cache:grade:getAllGrade:" + 当前用户ID
stringRedisTemplate.delete("cache:grade:getAllGrade:" + SecurityUtil.getUserId());
// 返回更新成功的结果信息
return Result.success("修改成功");
}
// 删除班级的方法,事务性操作,成功返回删除成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> deleteGrade(Integer id) {
// 删除班级
// 创建一个LambdaUpdateWrapper对象用于构建删除班级的逻辑(这里实际是逻辑删除,设置删除标记字段)
LambdaUpdateWrapper<Grade> gradeLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
gradeLambdaUpdateWrapper.eq(Grade::getId,id)
.set(Grade::getIsDeleted,1);
// 设置更新条件为班级ID等于传入的id参数
gradeLambdaUpdateWrapper.eq(Grade::getId, id)
// 设置班级的删除标记字段假设名为isDeleted为1表示已删除
.set(Grade::getIsDeleted, 1);
// 调用gradeMapper的update方法根据上面构建的更新条件来执行逻辑删除操作返回受影响的行数
int rows = gradeMapper.update(gradeLambdaUpdateWrapper);
// 如果删除操作没有影响到任何行,即删除失败,返回删除失败的结果信息
if (rows == 0) {
return Result.failed("删除失败");
}
// 删除缓存
stringRedisTemplate.delete("cache:grade:getPaging:"+id.toString());
stringRedisTemplate.delete("cache:grade:getAllGrade:"+SecurityUtil.getUserId());
// 根据班级ID删除对应的缓存数据缓存键格式为"cache:grade:getPaging:" + 班级ID
stringRedisTemplate.delete("cache:grade:getPaging:" + id.toString());
// 根据当前用户ID删除获取所有班级的缓存数据缓存键格式为"cache:grade:getAllGrade:" + 当前用户ID
stringRedisTemplate.delete("cache:grade:getAllGrade:" + SecurityUtil.getUserId());
// 返回删除成功的结果信息
return Result.success("删除成功");
}
// 获取班级分页数据的方法,返回包含分页信息和班级视图对象列表的结果
@Override
public Result<IPage<GradeVO>> getPaging(Integer pageNum, Integer pageSize, String gradeName) {
// 初始化角色变量默认为0这里可能根据不同角色有不同的查询逻辑从后续代码推测
Integer role = 0;
if("role_teacher".equals(SecurityUtil.getRole())){
// 如果当前用户角色为"role_teacher"则将角色设置为2具体角色值含义根据业务定义
if ("role_teacher".equals(SecurityUtil.getRole())) {
role = 2;
}
// 查询满足条件的总记录数
int total = gradeMapper.countByCondition(SecurityUtil.getUserId(), gradeName,role); // 假设gradeMapper中实现了根据条件计数的方法
// 计算偏移量
// 调用gradeMapper的countByCondition方法根据当前用户ID、班级名称和角色来统计满足条件的班级总记录数假设gradeMapper中实现了该方法
int total = gradeMapper.countByCondition(SecurityUtil.getUserId(), gradeName, role);
// 计算分页查询的偏移量,根据当前页码和每页显示数量来计算
int offset = (pageNum - 1) * pageSize;
// 查询分页ID列表
List<Integer> gradeIds = gradeMapper.selectGradeIdsPage(SecurityUtil.getUserId(), role ,gradeName, offset, pageSize);
// 调用gradeMapper的selectGradeIdsPage方法根据当前用户ID、角色、班级名称以及偏移量和每页显示数量来查询分页的班级ID列表假设gradeMapper中实现了该方法
List<Integer> gradeIds = gradeMapper.selectGradeIdsPage(SecurityUtil.getUserId(), role, gradeName, offset, pageSize);
// 批量从缓存中获取GradeVO对象
Map<Integer, GradeVO> cachedGradesMap = cacheClient.batchGet("cache:grade:getPaging:",gradeIds, GradeVO.class);
// 使用cacheClient的batchGet方法从缓存中批量获取对应班级ID的GradeVO对象缓存键前缀为"cache:grade:getPaging:"
Map<Integer, GradeVO> cachedGradesMap = cacheClient.batchGet("cache:grade:getPaging:", gradeIds, GradeVO.class);
// 确定未命中的ID列表
// 创建一个列表用于存储在缓存中未命中的班级ID即缓存中不存在对应数据的班级ID
List<Integer> missIds = new ArrayList<>();
// 遍历查询到的班级ID列表检查每个ID在缓存中是否存在如果不存在则添加到未命中ID列表中
for (Integer id : gradeIds) {
if (!cachedGradesMap.containsKey(id)) {
missIds.add(id);
}
}
// 如果有未命中的ID从数据库批量查询并更新缓存
// 如果存在未命中缓存的班级ID即missIds列表不为空
if (!missIds.isEmpty()) {
// 调用gradeMapper的batchSelectByIds方法从数据库中批量查询未命中缓存的班级数据并转换为GradeVO对象列表假设gradeMapper中实现了该方法
List<GradeVO> missedGrades = gradeMapper.batchSelectByIds(missIds);
// 假设GradeVO的ID为getId()使用Collectors.toMap转换
// 使用Java 8的Stream API将GradeVO对象列表转换为以班级ID为键GradeVO对象为值的Map方便后续操作
Map<Integer, GradeVO> missedGradesMap = missedGrades.stream()
.collect(Collectors.toMap(GradeVO::getId, Function.identity()));
// 更新缓存
cacheClient.batchPut("cache:grade:getPaging:",missedGradesMap,10L,TimeUnit.MINUTES);
// 合并缓存结果
// 使用cacheClient的batchPut方法将从数据库中查询到的未命中缓存的班级数据更新缓存缓存有效期为10分钟
cacheClient.batchPut("cache:grade:getPaging:", missedGradesMap, 10L, TimeUnit.MINUTES);
// 将从数据库中查询到的未命中缓存的班级数据合并缓存结果的Map中确保缓存数据的完整性
cachedGradesMap.putAll(missedGradesMap);
}
// 根据ID列表从缓存中获取完整的GradeVO对象列表
// 创建一个列表用于存储最终要返回的完整的GradeVO对象列表初始大小为查询到的班级ID列表的大小
List<GradeVO> finalResult = new ArrayList<>(gradeIds.size());
// 遍历班级ID列表从缓存结果的Map中获取对应的GradeVO对象并添加到最终结果列表中
for (Integer id : gradeIds) {
finalResult.add(cachedGradesMap.get(id));
}
// 构建并返回IPage对象
// 创建一个IPage对象用于封装分页信息和最终的班级视图对象列表设置当前页码、每页显示数量以及总记录数
IPage<GradeVO> resultPage = new Page<>(pageNum, pageSize, Long.valueOf(total));
// 将最终的班级视图对象列表设置到IPage对象中
resultPage.setRecords(finalResult);
// 返回包含分页信息和班级视图对象列表的成功结果信息
return Result.success("查询成功", resultPage);
}
// 移除用户班级关联的方法,成功返回移除成功的提示信息,失败返回相应错误提示
@Override
public Result<String> removeUserGrade(String ids) {
// 字符串转换为列表
// 将传入的以逗号分隔的字符串形式的用户ID列表转换为Integer类型的列表使用Java 8的Stream API进行转换
List<Integer> userIds = Arrays.stream(ids.split(","))
.map(Integer::parseInt)
.toList();
// 移出班级
// 调用userMapper的removeUserGrade方法根据用户ID列表移除用户与班级的关联关系返回受影响的行数假设userMapper中实现了该方法
int rows = userMapper.removeUserGrade(userIds);
// 如果移除操作没有影响到任何行,即移除失败,返回移除失败的结果信息
if (rows == 0) {
return Result.failed("移除失败");
}
userIds.forEach(id->{
stringRedisTemplate.delete("cache:grade:getPaging:"+id.toString());
// 遍历用户ID列表根据每个用户ID删除对应的获取班级分页数据的缓存这里的逻辑可能需要根据实际业务进一步确认是否合理
userIds.forEach(id -> {
stringRedisTemplate.delete("cache:grade:getPaging:" + id.toString());
});
// 返回移除成功的结果信息
return Result.success("移除成功");
}
// 获取所有班级信息的方法,返回包含所有班级视图对象列表的结果
@Override
public Result<List<GradeVO>> getAllGrade() {
// 创建一个匿名内部类实现Function接口用于定义从用户ID获取班级视图对象列表的逻辑
Function<Integer, List> function = new Function<>() {
@Override
public List apply(Integer userId) {
// 创建一个LambdaQueryWrapper对象用于构建查询班级信息的条件这里查询当前用户ID下未删除的班级假设isDeleted字段表示是否删除
LambdaQueryWrapper<Grade> gradeLambdaQueryWrapper = new LambdaQueryWrapper<>();
gradeLambdaQueryWrapper.eq(Grade::getUserId, userId)
.eq(Grade::getIsDeleted,0);
.eq(Grade::getIsDeleted, 0);
// 调用gradeMapper的selectList方法根据上面构建的查询条件获取班级实体对象列表
List<Grade> grades = gradeMapper.selectList(gradeLambdaQueryWrapper);
// 使用gradeConverter的listEntityToVo方法将班级实体对象列表转换为班级视图对象列表
List<GradeVO> gradeVOS = gradeConverter.listEntityToVo(grades);
// 返回班级视图对象列表
return gradeVOS;
}
};
// 使用cacheClient的queryWithPassThrough方法通过缓存穿透机制获取所有班级信息缓存键前缀为"cache:grade:getAllGrade:"传入当前用户ID、期望返回的类型、查询逻辑函数、返回对象类型、缓存有效期等参数
List<GradeVO> result = cacheClient.queryWithPassThrough("cache:grade:getAllGrade:", SecurityUtil.getUserId(), List.class, function, GradeVO.class, 10L, TimeUnit.MINUTES);
if(result==null){
// 如果获取结果为null即查询失败返回查询失败的结果信息
if (result == null) {
return Result.failed("查询失败");
}
// 返回包含所有班级视图对象列表的成功结果信息
return Result.success("查询成功", result);
}
}
}

@ -23,134 +23,177 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* <p>
*
* </p>
*
* @author WeiJin
* @since 2024-03-21
*/
@Service
public class ManualScoreServiceImpl extends ServiceImpl<ManualScoreMapper, ManualScore> implements IManualScoreService {
// 注入ExamMapper用于操作考试相关数据的持久层接口从命名推测其功能
@Resource
private ExamMapper examMapper;
// 注入ExamGradeMapper可能用于操作考试与班级关联相关数据的持久层接口根据命名推测
@Resource
private ExamGradeMapper examGradeMapper;
// 注入UserExamsScoreMapper用于操作用户考试成绩相关数据的持久层接口推测用途
@Resource
private UserExamsScoreMapper userExamsScoreMapper;
// 注入ExamQuAnswerMapper可能用于操作考试题目作答相关数据的持久层接口从名字判断
@Resource
private ExamQuAnswerMapper examQuAnswerMapper;
// 注入ManualScoreMapper用于操作手动评分相关数据的持久层接口对应业务功能
@Resource
private ManualScoreMapper manualScoreMapper;
// 注入CertificateUserMapper可能用于操作证书与用户关联相关数据的持久层接口依据命名猜测
@Resource
private CertificateUserMapper certificateUserMapper;
// 获取用户答题详情的方法根据用户ID和考试ID从数据库获取相关答题详情信息并返回
@Override
public Result<List<UserAnswerDetailVO>> getDetail(Integer userId, Integer examId) {
// 调用examQuAnswerMapper的selectUserAnswer方法传入用户ID和考试ID获取用户答题详情列表
List<UserAnswerDetailVO> list = examQuAnswerMapper.selectUserAnswer(userId, examId);
// 返回包含答题详情列表的成功结果信息这里第一个参数为null可能是预留用于传递其他提示信息等情况
return Result.success(null, list);
}
// 批改答题的方法,事务性操作,用于处理答题批改相关逻辑,包括记录手动评分、更新用户考试记录、颁发证书等操作
@Override
@Transactional
public Result<String> correct(List<CorrectAnswerFrom> correctAnswerFroms) {
// 创建一个ManualScore类型的列表用于存储要插入数据库的手动评分记录初始大小为传入的答题批改表单列表的大小
List<ManualScore> list = new ArrayList<>(correctAnswerFroms.size());
// 创建一个AtomicInteger对象用于原子性地累加手动评分的总分方便在多线程环境下安全地更新总分虽然此处可能并非多线程场景但使用它保证操作的原子性
AtomicInteger manualTotalScore = new AtomicInteger();
// 遍历传入的答题批改表单列表,处理每个答题的批改信息
correctAnswerFroms.forEach(correctAnswerFrom -> {
//获取用户作答信息id
// 创建一个LambdaQueryWrapper对象用于构建查询考试题目作答信息的条件这里只选择作答信息的ID字段
LambdaQueryWrapper<ExamQuAnswer> wrapper = new LambdaQueryWrapper<ExamQuAnswer>()
.select(ExamQuAnswer::getId)
// 设置查询条件为考试ID等于当前答题批改表单中的考试ID
.eq(ExamQuAnswer::getExamId, correctAnswerFrom.getExamId())
// 设置查询条件为用户ID等于当前答题批改表单中的用户ID
.eq(ExamQuAnswer::getUserId, correctAnswerFrom.getUserId())
// 设置查询条件为题目ID等于当前答题批改表单中的题目ID
.eq(ExamQuAnswer::getQuestionId, correctAnswerFrom.getQuestionId());
// 创建一个ManualScore对象用于存储当前答题的手动评分记录信息
ManualScore manualScore = new ManualScore();
// 设置手动评分记录中的考试题目作答信息ID通过查询获取对应的作答信息ID并设置
manualScore.setExamQuAnswerId(examQuAnswerMapper.selectOne(wrapper).getId());
// 设置手动评分记录中的分数,值为当前答题批改表单中的分数
manualScore.setScore(correctAnswerFrom.getScore());
// 将当前手动评分记录添加到列表中,后续批量插入数据库
list.add(manualScore);
// 原子性地累加当前答题的分数到总分中
manualTotalScore.addAndGet(correctAnswerFrom.getScore());
});
// 调用manualScoreMapper的insertList方法将整理好的手动评分记录列表批量插入到数据库中
manualScoreMapper.insertList(list);
//把用户考试记录修改为已批改,并把简答题分数添加进去
// 把用户考试记录修改为已批改,并把简答题分数添加进去
// 获取答题批改表单列表中的第一个表单对象,用于后续构建更新用户考试记录的条件(这里假设所有表单对应的考试和用户是相同的,若不同则逻辑可能需要调整)
CorrectAnswerFrom correctAnswerFrom = correctAnswerFroms.get(0);
// 创建一个LambdaUpdateWrapper对象用于构建更新用户考试成绩记录的条件和要更新的字段
LambdaUpdateWrapper<UserExamsScore> userExamsScoreLambdaUpdateWrapper = new LambdaUpdateWrapper<UserExamsScore>()
// 设置更新条件为考试ID等于当前答题批改表单中的考试ID
.eq(UserExamsScore::getExamId, correctAnswerFrom.getExamId())
// 设置更新条件为用户ID等于当前答题批改表单中的用户ID
.eq(UserExamsScore::getUserId, correctAnswerFrom.getUserId())
// 设置要更新的字段为是否已批改标记为1表示已批改
.set(UserExamsScore::getWhetherMark, 1)
// 使用SQL语句设置更新用户成绩将原用户成绩加上手动评分的总分这里使用了原生SQL语句拼接可能存在SQL注入风险若有安全需求可考虑参数化方式
.setSql("user_score = user_score + " + manualTotalScore.get());
// 调用userExamsScoreMapper的update方法根据构建的更新条件和字段来更新用户考试成绩记录
userExamsScoreMapper.update(userExamsScoreLambdaUpdateWrapper);
//根据该考试是否有证书来给用户颁发对应证书
//判断该考试是否有证书
// 根据该考试是否有证书来给用户颁发对应证书
// 判断该考试是否有证书
LambdaQueryWrapper<Exam> examWrapper = new LambdaQueryWrapper<Exam>()
// 选择查询考试的ID、证书ID以及及格分数字段
.select(Exam::getId, Exam::getCertificateId, Exam::getPassedScore)
// 设置查询条件为考试ID等于当前答题批改表单中的考试ID
.eq(Exam::getId, correctAnswerFrom.getExamId());
// 调用examMapper的selectOne方法根据构建的查询条件获取对应的考试信息对象
Exam exam = examMapper.selectOne(examWrapper);
//不必对exam做非空验证这里一定不为null
if (exam.getCertificateId() != null && exam.getCertificateId() > 0) {
//有证书 获取用户得分
// 不必对exam做非空验证这里一定不为null(此处根据代码逻辑的前置假设,认为该查询必然能获取到数据,如果实际情况可能为空则需要添加空值判断处理)
if (exam.getCertificateId()!= null && exam.getCertificateId() > 0) {
// 有证书 获取用户得分
LambdaQueryWrapper<UserExamsScore> examsScoreWrapper = new LambdaQueryWrapper<UserExamsScore>()
// 选择查询用户考试成绩记录的ID和用户分数字段
.select(UserExamsScore::getId, UserExamsScore::getUserScore)
// 设置查询条件为考试ID等于当前答题批改表单中的考试ID
.eq(UserExamsScore::getExamId, correctAnswerFrom.getExamId())
// 设置查询条件为用户ID等于当前答题批改表单中的用户ID
.eq(UserExamsScore::getUserId, correctAnswerFrom.getUserId());
// 调用userExamsScoreMapper的selectOne方法根据构建的查询条件获取对应的用户考试成绩记录对象
UserExamsScore userExamsScore = userExamsScoreMapper.selectOne(examsScoreWrapper);
//不必对userExamsScore做非空验证这里一定不为null
// 不必对userExamsScore做非空验证这里一定不为null(同样基于前置假设,如果实际可能为空需处理)
if (userExamsScore.getUserScore() >= exam.getPassedScore()) {
//分数合格,判罚证书
// 分数合格,判罚证书(这里可能是“颁发”证书的意思,疑似笔误写为“判罚”,以下按颁发理解)
CertificateUser certificateUser = new CertificateUser();
certificateUser.setUserId(correctAnswerFrom.getUserId());
certificateUser.setExamId(correctAnswerFrom.getExamId());
certificateUser.setCode(ClassTokenGenerator.generateClassToken(18));
certificateUser.setCertificateId(exam.getCertificateId());
// 调用certificateUserMapper的insert方法将生成的证书与用户关联信息插入到数据库中完成证书颁发操作
certificateUserMapper.insert(certificateUser);
}
}
// 返回批改成功的结果信息
return Result.success("批改成功");
}
// 获取考试分页信息的方法,根据页码、每页数量以及考试名称等条件,返回包含相关考试信息的分页结果
@Override
public Result<IPage<AnswerExamVO>> examPage(Integer pageNum, Integer pageSize,String examName) {
public Result<IPage<AnswerExamVO>> examPage(Integer pageNum, Integer pageSize, String examName) {
// 创建一个Page对象用于封装分页相关信息设置当前页码和每页显示数量
Page<AnswerExamVO> page = new Page<>(pageNum, pageSize);
//获取自己创建的考试
List<AnswerExamVO> list = examMapper.selectMarkedList(page, SecurityUtil.getUserId(), SecurityUtil.getRole(),examName).getRecords();
// 调用examMapper的selectMarkedList方法传入分页对象、当前用户ID、当前用户角色以及考试名称获取自己创建的考试信息列表并获取其记录列表这里假设selectMarkedList方法返回的是包含分页信息和记录的对象
List<AnswerExamVO> list = examMapper.selectMarkedList(page, SecurityUtil.getUserId(), SecurityUtil.getRole(), examName).getRecords();
//获取相关信息
// 遍历获取到的考试信息列表,为每个考试信息对象补充相关统计信息
list.forEach(answerExamVO -> {
//需要参加考试人数
// 设置需要参加考试的人数调用examGradeMapper的selectClassSize方法传入考试ID获取班级规模即需要参加考试的人数
answerExamVO.setClassSize(examGradeMapper.selectClassSize(answerExamVO.getExamId()));
//实际参加考试人数
// 设置实际参加考试的人数创建一个LambdaQueryWrapper对象用于构建查询条件查询对应考试的用户考试成绩记录数量即实际参加考试的人数
LambdaQueryWrapper<UserExamsScore> numberWrapper = new LambdaQueryWrapper<UserExamsScore>()
.eq(UserExamsScore::getExamId, answerExamVO.getExamId());
answerExamVO.setNumberOfApplicants(userExamsScoreMapper.selectCount(numberWrapper).intValue());
//已阅人数
// 设置已阅试卷的数量创建一个LambdaQueryWrapper对象用于构建查询条件查询对应考试中已批改是否已批改标记为1的用户考试成绩记录数量即已阅试卷的数量
LambdaQueryWrapper<UserExamsScore> correctedWrapper = new LambdaQueryWrapper<UserExamsScore>()
.eq(UserExamsScore::getWhetherMark, 1)
.eq(UserExamsScore::getExamId,answerExamVO.getExamId());
.eq(UserExamsScore::getExamId, answerExamVO.getExamId());
answerExamVO.setCorrectedPaper(userExamsScoreMapper.selectCount(correctedWrapper).intValue());
});
//移除不需要批改的试卷
// 移除不需要批改的试卷通过流操作过滤出需要批改neededMark为1的考试信息列表重新设置到分页对象的记录列表中
page.setRecords(list.stream()
.filter(answerExamVO -> answerExamVO.getNeededMark() == 1)
.toList());
// 返回包含处理后考试信息分页结果的成功结果信息这里第一个参数为null可能是预留用于传递其他提示信息等情况
return Result.success(null, page);
}
// 获取学生考试分页信息的方法根据页码、每页数量、考试ID以及学生真实姓名等条件返回包含未批改学生考试信息的分页结果
@Override
public Result<IPage<UncorrectedUserVO>> stuExamPage(Integer pageNum, Integer pageSize, Integer examId,String realName) {
public Result<IPage<UncorrectedUserVO>> stuExamPage(Integer pageNum, Integer pageSize, Integer examId, String realName) {
// 创建一个IPage对象用于封装分页相关信息设置当前页码和每页显示数量
IPage<UncorrectedUserVO> page = new Page<>(pageNum, pageSize);
page = userExamsScoreMapper.uncorrectedUser(page, examId,realName);
// 调用userExamsScoreMapper的uncorrectedUser方法传入分页对象、考试ID以及学生真实姓名获取未批改学生考试信息的分页结果并赋值给page对象
page = userExamsScoreMapper.uncorrectedUser(page, examId, realName);
// 返回包含未批改学生考试信息分页结果的成功结果信息这里第一个参数为null可能是预留用于传递其他提示信息等情况
return Result.success(null, page);
}
}
}

@ -1,20 +1,29 @@
package cn.org.alan.exam.service.impl;
// 导入NoticeGradeMapper接口用于操作与通知班级关联相关的数据持久化操作从命名推测功能
import cn.org.alan.exam.mapper.NoticeGradeMapper;
// 导入NoticeGrade实体类代表通知班级相关的实体对象
import cn.org.alan.exam.model.entity.NoticeGrade;
// 导入INoticeGradeService接口定义了通知班级相关的业务服务方法
import cn.org.alan.exam.service.INoticeGradeService;
// 导入ServiceImpl类这是MyBatis Plus提供的基础服务实现类方便进行常见的数据库操作服务实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入Spring的Service注解用于标记该类是一个服务层组件由Spring容器进行管理
import org.springframework.stereotype.Service;
/**
* <p>
*
*
* </p>
*
* @author WeiJin
* @since 2024-03-21
*/
// 使用@Service注解将该类标记为Spring容器管理的服务层组件方便在其他地方通过依赖注入使用
@Service
// 定义NoticeGradeServiceImpl类继承自ServiceImpl<NoticeGradeMapper, NoticeGrade>实现了INoticeGradeService接口
// 这样可以复用ServiceImpl中的通用数据库操作方法并按照INoticeGradeService接口定义实现具体业务逻辑
public class NoticeGradeServiceImpl extends ServiceImpl<NoticeGradeMapper, NoticeGrade> implements INoticeGradeService {
}
// 此处类体为空意味着目前它继承了ServiceImpl中的通用方法还没有额外实现INoticeGradeService接口中特定的业务方法
// 如果后续需要添加如添加通知班级、删除通知班级、查询通知班级等业务逻辑相关的方法,会在这里进行具体实现
}

@ -35,205 +35,266 @@ import java.util.function.Function;
import java.util.stream.Collectors;
/**
*
*
*
* @author Alan
* @since 2024-03-21
*/
@Service
// NoticeServiceImpl类继承自ServiceImpl<NoticeMapper, Notice>实现了INoticeService接口
// 这样可以复用ServiceImpl中的通用数据库操作方法并按照INoticeService接口定义实现具体业务逻辑
public class NoticeServiceImpl extends ServiceImpl<NoticeMapper, Notice> implements INoticeService {
// 注入NoticeMapper用于操作公告数据的持久层接口比如插入、删除、查询公告等操作
@Resource
private NoticeMapper noticeMapper;
// 注入NoticeConverter用于不同公告相关对象之间的转换比如表单对象和实体对象等
@Resource
private NoticeConverter noticeConverter;
// 注入NoticeGradeMapper可能用于操作公告与班级关联相关数据的持久层接口从命名推测其功能
@Resource
private NoticeGradeMapper noticeGradeMapper;
// 注入GradeMapper用于操作班级相关数据的持久层接口可能在公告关联班级等业务中会用到
@Resource
private GradeMapper gradeMapper;
// 注入CacheClient自定义的缓存客户端用于更方便地进行缓存操作如批量获取、批量存入缓存等
@Resource
private CacheClient cacheClient;
// 注入StringRedisTemplate用于操作Redis字符串类型数据常用于缓存相关操作比如删除缓存等
@Resource
private StringRedisTemplate stringRedisTemplate;
// 添加公告的方法,事务性操作,成功返回添加成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> addNotice(NoticeForm noticeForm) {
// 设置创建人
// 设置公告的创建人ID为当前登录用户的ID通过SecurityUtil工具类获取当前用户ID
noticeForm.setUserId(SecurityUtil.getUserId());
// 添加公告
// 将NoticeForm对象转换为Notice实体对象方便后续持久化操作使用NoticeConverter进行转换
Notice notice = noticeConverter.formToEntity(noticeForm);
// 调用noticeMapper的insert方法将公告数据插入到数据库中返回受影响的行数即插入操作影响的记录数
int rowsAffected = noticeMapper.insert(notice);
// 如果插入操作没有影响到任何行,即插入失败,返回添加失败的结果信息
if (rowsAffected == 0) {
return Result.failed("添加失败");
}
// 创建一个LambdaQueryWrapper对象用于构建查询班级信息的条件这里查询当前用户创建的班级通过用户ID关联
LambdaQueryWrapper<Grade> gradeLambdaQueryWrapper = new LambdaQueryWrapper<>();
gradeLambdaQueryWrapper.eq(Grade::getUserId,SecurityUtil.getUserId());
gradeLambdaQueryWrapper.eq(Grade::getUserId, SecurityUtil.getUserId());
// 调用gradeMapper的selectList方法根据上面构建的查询条件获取班级实体对象列表即获取当前用户创建的所有班级
List<Grade> grades = gradeMapper.selectList(gradeLambdaQueryWrapper);
// 获取刚插入公告的ID用于后续关联班级等操作假设插入后会自动生成ID
Integer noticeId = notice.getId();
if(grades.isEmpty()){
return Result.failed("您还没有创建班级,请先创建班级后再添加公告尝试");
// 如果当前用户没有创建任何班级,返回相应提示信息,提示先创建班级再添加公告
if (grades.isEmpty()) {
return Result.failed("您还没有创建班级,请先创建班级后再添加公告尝试");
}
int addNoticeGradeRow = noticeGradeMapper.addNoticeGrade(noticeId,grades);
// 调用noticeGradeMapper的addNoticeGrade方法将公告与班级进行关联传入公告ID和班级列表返回受影响的行数假设该方法实现了关联操作并返回影响行数
int addNoticeGradeRow = noticeGradeMapper.addNoticeGrade(noticeId, grades);
// 如果关联操作没有影响到任何行,即关联失败,返回添加失败的结果信息
if (addNoticeGradeRow == 0) {
return Result.failed("添加失败");
}
// 更新缓存
if (notice.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:notice:getNotice:"+notice.getId().toString()); // 删除旧缓存
stringRedisTemplate.delete("cache:notice:getNewNotice:"+notice.getId().toString());
// 如果公告插入成功且关联班级等操作也成功公告数据有了有效的ID进行缓存相关操作
if (notice.getId()!= null) { // 确保ID有效
// 如果是更新操作(此处逻辑可能不太准确,实际上是添加操作,但代码结构类似更新时的缓存处理逻辑),先从缓存中移除旧数据
// 根据公告ID删除对应的缓存数据缓存键格式为"cache:notice:getNotice:" + 公告ID
stringRedisTemplate.delete("cache:notice:getNotice:" + notice.getId().toString());
// 根据公告ID删除对应的另一个缓存数据可能是用于获取新公告相关的缓存从命名推测缓存键格式为"cache:notice:getNewNotice:" + 公告ID
stringRedisTemplate.delete("cache:notice:getNewNotice:" + notice.getId().toString());
// NoticeVO updatedNoticeVO = noticeConverter.NoticeToNoticeVO(notice); // 转换为视图对象
// Map<Integer, NoticeVO> map = Map.of(updatedNoticeVO.getId(), updatedNoticeVO);
// cacheClient.batchPut("cache:notice:getNotice:",map,10L,TimeUnit.MINUTES); // 存储新数据
// cacheClient.batchPut("cache:notice:getNewNotice:",map,10L,TimeUnit.MINUTES); // 存储新数据
}
// 返回添加成功的结果信息
return Result.success("添加成功");
}
// 删除公告的方法,事务性操作,成功返回删除成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> deleteNotice(String ids) {
// 转换为集合
// 将传入的以逗号分隔的字符串形式的公告ID列表转换为Integer类型的列表使用Java 8的Stream API进行转换
List<Integer> noticeIds = Arrays.stream(ids.split(","))
.map(Integer::parseInt)
.toList();
// 删除公告
// 调用noticeMapper的removeNotice方法根据公告ID列表删除对应的公告数据,返回受影响的行数,即删除操作影响的记录数
int rowsAffected = noticeMapper.removeNotice(noticeIds);
// 如果删除操作没有影响到任何行,即删除失败,返回删除失败的结果信息
if (rowsAffected == 0) {
return Result.failed("删除失败");
}
noticeIds.forEach(id->{
stringRedisTemplate.delete("cache:notice:getNewNotice:"+id);
stringRedisTemplate.delete("cache:notice:getNotice:"+id);
// 遍历公告ID列表根据每个公告ID删除对应的两个缓存数据分别用于获取公告和获取新公告相关的缓存从缓存键命名推测
noticeIds.forEach(id -> {
stringRedisTemplate.delete("cache:notice:getNewNotice:" + id);
stringRedisTemplate.delete("cache:notice:getNotice:" + id);
});
// 返回删除成功的结果信息
return Result.success("删除成功");
}
// 更新公告的方法,事务性操作,成功返回更新成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> updateNotice(String id, NoticeForm noticeForm) {
// 创建更新条件
// 创建一个LambdaUpdateWrapper对象用于构建更新公告信息的条件和要更新的字段
LambdaUpdateWrapper<Notice> noticeWrapper = new LambdaUpdateWrapper<>();
// 设置更新条件为公告ID等于传入的id参数
noticeWrapper.eq(Notice::getId, id)
.eq(Notice::getIsDeleted,0)
// 设置更新条件为公告未被删除假设isDeleted字段表示是否删除0表示未删除
.eq(Notice::getIsDeleted, 0)
// 设置要更新的公告内容字段值为传入的noticeForm中的公告内容
.set(Notice::getContent, noticeForm.getContent());
// 更新公告
// 调用noticeMapper的update方法根据上面构建的更新条件和要更新的字段来更新公告数据,返回受影响的行数,即更新操作影响的记录数
int rowsAffected = noticeMapper.update(noticeWrapper);
// 如果更新操作没有影响到任何行,即更新失败,返回修改失败的结果信息
if (rowsAffected == 0) {
return Result.failed("修改失败");
}
// 根据ID获取更新后的公告对象用于后续缓存操作等可能需要获取最新数据来更新缓存等
Notice byId = getById(id);
if (byId.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:notice:getNotice:"+byId.getId().toString()); // 删除旧缓存
stringRedisTemplate.delete("cache:notice:getNewNotice:"+byId.getId().toString()); // 删除旧缓存
// 如果获取到的公告对象有有效的ID进行缓存相关操作
if (byId.getId()!= null) { // 确保ID有效
// 如果是更新操作先从缓存中移除旧数据根据公告ID删除对应的缓存数据缓存键格式为"cache:notice:getNotice:" + 公告ID
stringRedisTemplate.delete("cache:notice:getNotice:" + byId.getId().toString());
// 根据公告ID删除对应的另一个缓存数据可能是用于获取新公告相关的缓存从命名推测缓存键格式为"cache:notice:getNewNotice:" + 公告ID
stringRedisTemplate.delete("cache:notice:getNewNotice:" + byId.getId().toString());
// NoticeVO updatedNoticeVO = noticeConverter.NoticeToNoticeVO(byId); // 转换为视图对象
// Map<Integer, NoticeVO> map = Map.of(updatedNoticeVO.getId(), updatedNoticeVO);
// cacheClient.batchPut("cache:notice:getNotice:",map,10L,TimeUnit.MINUTES); // 存储新数据
// cacheClient.batchPut("cache:notice:getNewNotice:",map,10L,TimeUnit.MINUTES); // 存储新数据
}
// 返回更新成功的结果信息
return Result.success("修改成功");
}
// 获取公告分页数据的方法,返回包含分页信息和公告视图对象列表的结果
@Override
public Result<IPage<NoticeVO>> getNotice(Integer pageNum, Integer pageSize, String title) {
// 查询满足条件的总记录数
int total = noticeMapper.countByCondition(SecurityUtil.getUserId(), title); // 假设gradeMapper中实现了根据条件计数的方法
// 计算偏移量
// 调用noticeMapper的countByCondition方法根据当前用户ID和公告标题来统计满足条件的公告总记录数假设noticeMapper中实现了该方法
int total = noticeMapper.countByCondition(SecurityUtil.getUserId(), title);
// 计算分页查询的偏移量,根据当前页码和每页显示数量来计算
int offset = (pageNum - 1) * pageSize;
// 查询分页ID列表
// 调用noticeMapper的selectNoticeIdsPage方法根据当前用户ID、公告标题以及偏移量和每页显示数量来查询分页的公告ID列表假设noticeMapper中实现了该方法
List<Integer> noticeIds = noticeMapper.selectNoticeIdsPage(SecurityUtil.getUserId(), title, offset, pageSize);
// 批量从缓存中获取GradeVO对象
Map<Integer, NoticeVO> cachedGradesMap = cacheClient.batchGet("cache:notice:getNotice:",noticeIds, NoticeVO.class);
// 使用cacheClient的batchGet方法从缓存中批量获取对应公告ID的NoticeVO对象缓存键前缀为"cache:notice:getNotice:"
Map<Integer, NoticeVO> cachedGradesMap = cacheClient.batchGet("cache:notice:getNotice:", noticeIds, NoticeVO.class);
// 确定未命中的ID列表
// 创建一个列表用于存储在缓存中未命中的公告ID即缓存中不存在对应数据的公告ID
List<Integer> missIds = new ArrayList<>();
// 遍历查询到的公告ID列表检查每个ID在缓存中是否存在如果不存在则添加到未命中ID列表中
for (Integer id : noticeIds) {
if (!cachedGradesMap.containsKey(id)) {
missIds.add(id);
}
}
// 如果有未命中的ID从数据库批量查询并更新缓存
// 如果存在未命中缓存的公告ID即missIds列表不为空
if (!missIds.isEmpty()) {
// 调用noticeMapper的batchSelectByIds方法从数据库中批量查询未命中缓存的公告数据并转换为NoticeVO对象列表假设noticeMapper中实现了该方法
List<NoticeVO> missedGrades = noticeMapper.batchSelectByIds(missIds);
// 假设GradeVO的ID为getId()使用Collectors.toMap转换
// 使用Java 8的Stream API将NoticeVO对象列表转换为以公告ID为键NoticeVO对象为值的Map方便后续操作
Map<Integer, NoticeVO> missedGradesMap = missedGrades.stream()
.collect(Collectors.toMap(NoticeVO::getId, Function.identity()));
// 更新缓存
cacheClient.batchPut("cache:notice:getNotice:",missedGradesMap,10L, TimeUnit.MINUTES);
// 合并缓存结果
// 使用cacheClient的batchPut方法将从数据库中查询到的未命中缓存的公告数据更新缓存缓存有效期为10分钟
cacheClient.batchPut("cache:notice:getNotice:", missedGradesMap, 10L, TimeUnit.MINUTES);
// 将从数据库中查询到的未命中缓存的公告数据合并缓存结果的Map中确保缓存数据的完整性
cachedGradesMap.putAll(missedGradesMap);
}
// 根据ID列表从缓存中获取完整的GradeVO对象列表
// 创建一个列表用于存储最终要返回的完整的NoticeVO对象列表初始大小为查询到的公告ID列表的大小
List<NoticeVO> finalResult = new ArrayList<>(noticeIds.size());
// 遍历公告ID列表从缓存结果的Map中获取对应的NoticeVO对象并添加到最终结果列表中
for (Integer id : noticeIds) {
finalResult.add(cachedGradesMap.get(id));
}
// 构建并返回IPage对象
// 创建一个IPage对象用于封装分页信息和最终的公告视图对象列表设置当前页码、每页显示数量以及总记录数
IPage<NoticeVO> resultPage = new Page<>(pageNum, pageSize, Long.valueOf(total));
// 将最终的公告视图对象列表设置到IPage对象中
resultPage.setRecords(finalResult);
// 返回包含分页信息和公告视图对象列表的成功结果信息
return Result.success("查询成功", resultPage);
}
// 获取新公告分页数据的方法返回包含分页信息和公告视图对象列表的结果与getNotice方法逻辑有部分重复可能可进行适当优化抽取公共逻辑
@Override
public Result<IPage<NoticeVO>> getNewNotice(Integer pageNum, Integer pageSize) {
// 创建分页对象
// 创建一个Page对象用于封装分页相关信息设置当前页码和每页显示数量
Page<NoticeVO> page = new Page<>(pageNum, pageSize);
// 学生分页查询公告
page = noticeMapper.selectNewNoticePage(page,SecurityUtil.getUserId());
// 调用noticeMapper的selectNewNoticePage方法传入分页对象和当前用户ID进行学生分页查询公告操作(具体查询逻辑在该方法内部实现,从命名推测是获取新公告相关的分页数据)
page = noticeMapper.selectNewNoticePage(page, SecurityUtil.getUserId());
// return Result.success("查询成功", page);
// 查询满足条件的总记录数
int total = noticeMapper.countByCondition(SecurityUtil.getUserId(), null); // 假设gradeMapper中实现了根据条件计数的方法
// 计算偏移量
// 调用noticeMapper的countByCondition方法根据当前用户ID标题为null可能表示不按标题筛选来统计满足条件的公告总记录数假设noticeMapper中实现了该方法
int total = noticeMapper.countByCondition(SecurityUtil.getUserId(), null);
// 计算分页查询的偏移量,根据当前页码和每页显示数量来计算
int offset = (pageNum - 1) * pageSize;
// 查询分页ID列表
// 调用noticeMapper的selectNewNoticeIdsPage方法根据当前用户ID、偏移量和每页显示数量来查询分页的公告ID列表假设noticeMapper中实现了该方法
List<Integer> noticeIds = noticeMapper.selectNewNoticeIdsPage(SecurityUtil.getUserId(), offset, pageSize);
// 批量从缓存中获取GradeVO对象
Map<Integer, NoticeVO> cachedGradesMap = cacheClient.batchGet("cache:notice:getNewNotice:",noticeIds, NoticeVO.class);
// 使用cacheClient的batchGet方法从缓存中批量获取对应公告ID的NoticeVO对象缓存键前缀为"cache:notice:getNewNotice:"
Map<Integer, NoticeVO> cachedGradesMap = cacheClient.batchGet("cache:notice:getNewNotice:", noticeIds, NoticeVO.class);
// 确定未命中的ID列表
// 创建一个列表用于存储在缓存中未命中的公告ID即缓存中不存在对应数据的公告ID
List<Integer> missIds = new ArrayList<>();
// 遍历查询到的公告ID列表检查每个ID在缓存中是否存在如果不存在则添加到未命中ID列表中
for (Integer id : noticeIds) {
// 遍历noticeIds列表noticeIds可能是存储了通知Notice相关记录的ID集合这里每次循环取出一个通知的ID用于后续的缓存判断等操作。
if (!cachedGradesMap.containsKey(id)) {
// 判断cachedGradesMap推测是一个缓存中存储的已查询到的通知对象的映射键为通知ID值为对应的通知对象这里从命名来看可能是缓存了NoticeVO类型的对象但需结合上下文确定中是否包含当前遍历到的通知ID如果不包含表示该通知ID对应的通知对象在缓存中未找到缓存未命中
missIds.add(id);
// 将未在缓存中找到的通知ID添加到missIds列表中missIds用于收集所有缓存未命中的通知ID后续会根据这些ID从数据库中查询相应的通知信息并更新缓存。
}
}
// 如果有未命中的ID从数据库批量查询并更新缓存
// 如果有未命中的ID从数据库批量查询并更新缓存
if (!missIds.isEmpty()) {
// 判断missIds列表是否为空即是否存在缓存未命中的通知ID如果不为空表示有需要从数据库查询并更新缓存的数据执行以下操作。
List<NoticeVO> missedGrades = noticeMapper.batchSelectByIds(missIds);
// 调用noticeMapper可能是操作通知数据的持久层接口用于与数据库交互的batchSelectByIds方法传入收集到的缓存未命中的通知ID列表missIds从数据库中批量查询对应的通知信息返回的是NoticeVO类型的列表每个NoticeVO对象代表一条通知记录的相关信息存储在missedGrades变量中。
// 假设GradeVO的ID为getId()使用Collectors.toMap转换
Map<Integer, NoticeVO> missedGradesMap = missedGrades.stream()
.collect(Collectors.toMap(NoticeVO::getId, Function.identity()));
// 使用Java 8的Stream API对查询到的通知信息列表missedGrades进行处理将其转换为一个以通知ID为键通知对象NoticeVO为值的Map。Collectors.toMap方法接收两个参数第一个参数NoticeVO::getId表示使用NoticeVO对象的getId方法获取的返回值即通知ID作为键第二个参数Function.identity()表示直接使用原NoticeVO对象作为值这样就构建了方便后续操作的映射关系存储在missedGradesMap变量中。
// 更新缓存
cacheClient.batchPut("cache:notice:getNotice:",missedGradesMap,10L, TimeUnit.MINUTES);
cacheClient.batchPut("cache:notice:getNotice:", missedGradesMap, 10L, TimeUnit.MINUTES);
// 调用cacheClient可能是用于操作缓存的客户端工具类的batchPut方法将构建好的包含未命中通知对象的映射missedGradesMap存储到缓存中缓存的键前缀为 "cache:notice:getNotice:"同时设置缓存的过期时间为10分钟时间单位为分钟TimeUnit.MINUTES这样可以在一定时间内缓存这些通知信息提高后续查询效率。
// 合并缓存结果
cachedGradesMap.putAll(missedGradesMap);
// 将新查询到并存储到missedGradesMap中的通知对象映射合并到cachedGradesMap中使得cachedGradesMap包含了最新的缓存数据无论是之前已存在的缓存数据还是本次新查询并更新的缓存数据保证缓存数据的完整性。
}
// 根据ID列表从缓存中获取完整的GradeVO对象列表
// 根据ID列表从缓存中获取完整的GradeVO对象列表
List<NoticeVO> finalResult = new ArrayList<>(noticeIds.size());
// 创建一个ArrayList对象用于存储最终要返回的通知对象列表初始容量设置为noticeIds的大小这样可以在一定程度上避免列表扩容带来的性能开销提高性能虽然ArrayList会自动扩容但预先指定合适容量可以优化性能
for (Integer id : noticeIds) {
finalResult.add(cachedGradesMap.get(id));
// 再次遍历noticeIds列表从合并后的缓存映射cachedGradesMap中获取每个通知ID对应的通知对象NoticeVO并添加到finalResult列表中这样finalResult就包含了根据传入的通知ID列表从缓存中获取到的完整的通知对象集合。
}
// 构建并返回IPage对象
// 构建并返回IPage对象
IPage<NoticeVO> resultPage = new Page<>(pageNum, pageSize, Long.valueOf(total));
// 创建一个IPage类型的分页对象resultPage使用Page的构造函数进行初始化传入当前页码pageNum、每页显示数量pageSize以及总记录数total这里将total转换为Long类型传入具体total的来源和含义需结合上下文确定用于构建分页相关的数据结构方便前端进行分页展示等操作。
resultPage.setRecords(finalResult);
// 将包含通知对象的finalResult列表设置为分页对象resultPage的记录列表即将查询到的通知数据填充到分页对象中使其符合分页展示的要求包含了分页信息以及具体的数据记录。
return Result.success("查询成功", resultPage);
}
}
// 返回一个表示操作成功的Result结果对象其中包含提示信息“查询成功”以及构建好的分页对象resultPage方便前端等调用者获取查询结果并根据分页信息进行展示和后续处理具体Result对象的结构和使用方式需结合项目中相关定义来看。

@ -1,20 +1,29 @@
package cn.org.alan.exam.service.impl;
// 导入OptionMapper接口它是用于操作题目选项相关数据的持久层接口例如对选项数据进行增删改查等操作从命名推测其功能
import cn.org.alan.exam.mapper.OptionMapper;
// 导入Option实体类代表题目选项相关的实体对象包含了选项的各种属性信息如选项内容、是否正确等
import cn.org.alan.exam.model.entity.Option;
// 导入IOptionService接口该接口定义了题目选项相关的业务服务方法规定了具体有哪些业务操作需要实现
import cn.org.alan.exam.service.IOptionService;
// 导入ServiceImpl类这是MyBatis Plus提供的基础服务实现类它提供了很多通用的数据库操作方法方便在此基础上扩展具体业务逻辑的实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入Spring的Service注解用于标记该类是一个服务层组件Spring容器会对其进行管理使其能够参与依赖注入等相关机制
import org.springframework.stereotype.Service;
/**
* <p>
*
*
* </p>
*
* @author WeiJin
* @since 2024-03-21
*/
// 使用@Service注解将该类标记为Spring容器管理的服务层组件意味着在Spring应用中可以通过依赖注入的方式在其他地方使用这个服务类
@Service
// 定义OptionServiceImpl类它继承自ServiceImpl<OptionMapper, Option>同时实现了IOptionService接口
// 通过继承ServiceImpl可以复用其中已有的通用数据库操作方法再按照IOptionService接口中定义的业务方法来实现具体针对题目选项的业务逻辑
// 目前类体为空,表示暂时没有添加额外的业务方法实现,如果后续有添加选项、删除选项、查询选项等业务逻辑相关的方法,就需要在这里进行具体的编写
public class OptionServiceImpl extends ServiceImpl<OptionMapper, Option> implements IOptionService {
}
}

@ -38,7 +38,7 @@ import java.util.function.Function;
import java.util.stream.Collectors;
/**
*
*
*
* @author WeiJin
* @since 2024-03-21
@ -46,215 +46,292 @@ import java.util.stream.Collectors;
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements IQuestionService {
// 注入QuestionConverter用于不同题目相关对象之间的转换比如表单对象和实体对象等
@Resource
private QuestionConverter questionConverter;
// 注入QuestionMapper用于操作题目数据的持久层接口例如插入、查询、更新、删除题目等操作
@Resource
private QuestionMapper questionMapper;
// 注入OptionMapper用于操作题目选项数据的持久层接口像插入、更新、删除题目选项等操作与题目相关联
@Resource
private OptionMapper optionMapper;
// 注入AliOSSUtil可能是用于与阿里云对象存储服务OSS交互的工具类比如上传图片等操作从命名推测
@Resource
private AliOSSUtil aliOSSUtil;
// 注入ExerciseRecordMapper可能用于操作练习记录相关数据的持久层接口推测与用户做题目练习的记录有关
@Resource
private ExerciseRecordMapper exerciseRecordMapper;
// 注入CacheClient自定义的缓存客户端用于更方便地进行缓存操作如批量获取、批量存入缓存等
@Resource
private CacheClient cacheClient;
// 注入StringRedisTemplate用于操作Redis字符串类型数据常用于缓存相关操作比如删除缓存等
@Resource
private StringRedisTemplate stringRedisTemplate;
// 添加单个题目(可能是单选题、多选题、判断题、简答题等不同类型)的方法,事务性操作,成功返回添加成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> addSingleQuestion(QuestionFrom questionFrom) {
// 入参校验
// 入参校验,获取题目表单中的选项列表
List<Option> options = questionFrom.getOptions();
// 判断题目类型不是简答题假设题目类型4代表简答题且选项为空或者选项数量小于2时不符合要求返回错误提示
if (questionFrom.getQuType() != 4 && (Objects.isNull(options) || options.size() < 2)) {
return Result.failed("非简答题的试题选项不能少于两个");
}
// 将QuestionFrom对象转换为Question实体对象方便后续持久化操作使用QuestionConverter进行转换
Question question = questionConverter.fromToEntity(questionFrom);
// 调用questionMapper的insert方法将题目数据插入到数据库中
questionMapper.insert(question);
if (question.getQuType() == 4) {
// 简答题添加选项
// 如果题目类型是简答题
if (question.getQuType() == 4) {
// 简答题添加选项,获取题目表单中第一个选项(简答题通常可能只有一个用于填写答案的选项,这里从代码逻辑推测)
Option option = questionFrom.getOptions().get(0);
// 设置该选项所属题目的ID为刚插入题目的ID建立关联
option.setQuId(question.getId());
// 调用optionMapper的insert方法将选项数据插入到数据库中
optionMapper.insert(option);
} else {
// 非简答题添加选项
// 把新建试题获取的id填入选项中
// 遍历题目表单中的所有选项将每个选项所属题目的ID设置为刚插入题目的ID建立关联
options.forEach(option -> {
option.setQuId(question.getId());
});
// 调用optionMapper的insertBatch方法批量将选项数据插入到数据库中
optionMapper.insertBatch(options);
}
// 如果题目插入成功题目数据有了有效的ID这里假设插入后会自动生成ID进行缓存相关操作
if (question.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:question:pagingQuestion:"+question.getId().toString()); // 删除旧缓存
//
// 如果是更新操作(此处逻辑可能不太准确,实际上是添加操作,但代码结构类似更新时的缓存处理逻辑),先从缓存中移除旧数据
// 根据题目ID删除对应的缓存数据缓存键格式为"cache:question:pagingQuestion:" + 题目ID
stringRedisTemplate.delete("cache:question:pagingQuestion:" + question.getId().toString());
// QuestionVO questionVO = questionConverter.QuestionToQuestionVO(question); // 转换为视图对象
// Map<Integer, QuestionVO> map = Map.of(questionVO.getId(), questionVO);
// cacheClient.batchPut("cache:question:pagingQuestion:",map,10L,TimeUnit.MINUTES); // 存储新数据
}
// 返回添加成功的结果信息
return Result.success("添加成功");
}
// 批量删除题目根据题目ID列表的方法事务性操作成功返回删除成功的提示信息失败返回相应错误提示
@Override
@Transactional
public Result<String> deleteBatchByIds(String ids) {
// 将传入的以逗号分隔的字符串形式的题目ID列表转换为Integer类型的列表使用Java 8的Stream API进行转换
List<Integer> list = Arrays.stream(ids.split(",")).map(Integer::parseInt).toList();
//删除用户刷题记录表
// 删除用户刷题记录表中与要删除题目相关的记录创建一个LambdaUpdateWrapper对象用于构建删除条件
LambdaUpdateWrapper<ExerciseRecord> updateWrapper =
new LambdaUpdateWrapper<ExerciseRecord>().in(ExerciseRecord::getQuestionId, list);
// 调用exerciseRecordMapper的delete方法根据构建的条件删除相关记录返回受影响的行数
int delete = exerciseRecordMapper.delete(updateWrapper);
// 先删除选项
// 先删除题目对应的选项调用optionMapper的deleteBatchByQuIds方法传入题目ID列表批量删除选项假设该方法实现了批量删除逻辑
optionMapper.deleteBatchByQuIds(list);
// 再删除试题
// 再删除题目调用questionMapper的deleteBatchIdsQu方法传入题目ID列表删除对应的题目假设该方法实现了批量删除逻辑
questionMapper.deleteBatchIdsQu(list);
list.forEach(id->{
stringRedisTemplate.delete("cache:question:pagingQuestion:"+id);
// 遍历题目ID列表根据每个题目ID删除对应的缓存数据缓存键格式为"cache:question:pagingQuestion:" + 题目ID
list.forEach(id -> {
stringRedisTemplate.delete("cache:question:pagingQuestion:" + id);
});
// 返回删除成功的结果信息
return Result.success("删除成功");
}
// 获取题目分页数据的方法根据页码、每页数量、题目标题、题目类型以及题库ID等条件返回包含分页信息和题目视图对象列表的结果
@Override
public Result<IPage<QuestionVO>> pagingQuestion(Integer pageNum, Integer pageSize, String title, Integer type, Integer repoId) {
// 初始化用户ID变量默认为null后续根据用户角色进行赋值
Integer userId = null;
// 如果当前用户角色为"role_teacher"可能代表教师角色具体根据业务定义则将用户ID设置为当前登录用户的ID
if ("role_teacher".equals(SecurityUtil.getRole())) {
userId = SecurityUtil.getUserId();
} else {
// 否则设置为0这里0可能代表其他情况比如游客或者无特定用户身份等具体看业务逻辑
userId = 0;
}
// 查询满足条件的总记录数
int total = questionMapper.countByCondition(userId, title,type,repoId); // 假设gradeMapper中实现了根据条件计数的方法
// 计算偏移量
// 调用questionMapper的countByCondition方法根据用户ID、题目标题、题目类型以及题库ID来统计满足条件的题目总记录数假设questionMapper中实现了该方法
int total = questionMapper.countByCondition(userId, title, type, repoId);
// 计算分页查询的偏移量,根据当前页码和每页显示数量来计算
int offset = (pageNum - 1) * pageSize;
// 查询分页ID列表
List<Integer> quIds = questionMapper.selectQuestionIdsPage(userId, title,type,repoId, offset, pageSize);
// 调用questionMapper的selectQuestionIdsPage方法根据用户ID、题目标题、题目类型、题库ID以及偏移量和每页显示数量来查询分页的题目ID列表假设questionMapper中实现了该方法
List<Integer> quIds = questionMapper.selectQuestionIdsPage(userId, title, type, repoId, offset, pageSize);
// 批量从缓存中获取GradeVO对象
Map<Integer, QuestionVO> cachedQuestionsMap = cacheClient.batchGet("cache:question:pagingQuestion:",quIds, QuestionVO.class);
// 使用cacheClient的batchGet方法从缓存中批量获取对应题目ID的QuestionVO对象缓存键前缀为"cache:question:pagingQuestion:"
Map<Integer, QuestionVO> cachedQuestionsMap = cacheClient.batchGet("cache:question:pagingQuestion:", quIds, QuestionVO.class);
// 确定未命中的ID列表
// 创建一个列表用于存储在缓存中未命中的题目ID即缓存中不存在对应数据的题目ID
List<Integer> missIds = new ArrayList<>();
// 遍历查询到的题目ID列表检查每个ID在缓存中是否存在如果不存在则添加到未命中ID列表中
for (Integer id : quIds) {
if (!cachedQuestionsMap.containsKey(id)) {
missIds.add(id);
}
}
// 如果有未命中的ID从数据库批量查询并更新缓存
// 如果存在未命中缓存的题目ID即missIds列表不为空
if (!missIds.isEmpty()) {
// 调用questionMapper的batchSelectByIds方法从数据库中批量查询未命中缓存的题目数据并转换为QuestionVO对象列表假设questionMapper中实现了该方法
List<QuestionVO> missedGrades = questionMapper.batchSelectByIds(missIds);
// 假设GradeVO的ID为getId()使用Collectors.toMap转换
// 使用Java 8的Stream API将QuestionVO对象列表转换为以题目ID为键QuestionVO对象为值的Map方便后续操作
Map<Integer, QuestionVO> missedGradesMap = missedGrades.stream()
.collect(Collectors.toMap(QuestionVO::getId, Function.identity()));
// 更新缓存
cacheClient.batchPut("cache:question:pagingQuestion:",missedGradesMap,10L, TimeUnit.MINUTES);
// 合并缓存结果
// 使用cacheClient的batchPut方法将从数据库中查询到的未命中缓存的题目数据更新缓存缓存有效期为10分钟
cacheClient.batchPut("cache:question:pagingQuestion:", missedGradesMap, 10L, TimeUnit.MINUTES);
// 将从数据库中查询到的未命中缓存的题目数据合并缓存结果的Map中确保缓存数据的完整性
cachedQuestionsMap.putAll(missedGradesMap);
}
// 根据ID列表从缓存中获取完整的GradeVO对象列表
// 创建一个列表用于存储最终要返回的完整的QuestionVO对象列表初始大小为查询到的题目ID列表的大小
List<QuestionVO> finalResult = new ArrayList<>(quIds.size());
// 遍历题目ID列表从缓存结果的Map中获取对应的QuestionVO对象并添加到最终结果列表中
for (Integer id : quIds) {
finalResult.add(cachedQuestionsMap.get(id));
}
// 构建并返回IPage对象
// 创建一个IPage对象用于封装分页信息和最终的题目视图对象列表设置当前页码、每页显示数量以及总记录数
IPage<QuestionVO> resultPage = new Page<>(pageNum, pageSize, Long.valueOf(total));
// 将最终的题目视图对象列表设置到IPage对象中
resultPage.setRecords(finalResult);
// 返回包含分页信息和题目视图对象列表的成功结果信息这里第一个参数为null可能是预留用于传递其他提示信息等情况
return Result.success(null, resultPage);
}
// 查询单个题目的方法根据题目ID查询并返回对应的题目视图对象返回包含题目视图对象的结果信息
@Override
public Result<QuestionVO> querySingle(Integer id) {
// 调用questionMapper的selectSingle方法传入题目ID查询对应的题目数据并将其包装在成功结果信息中返回
return Result.success(null, questionMapper.selectSingle(id));
}
// 更新题目的方法,事务性操作,成功返回更新成功的提示信息,失败返回相应错误提示
@Override
@Transactional
public Result<String> updateQuestion(QuestionFrom questionFrom) {
// 修改试题
// 修改试题将QuestionFrom对象转换为Question实体对象方便后续更新操作使用QuestionConverter进行转换
Question question = questionConverter.fromToEntity(questionFrom);
// 调用questionMapper的updateById方法根据题目实体对象的ID更新对应的题目数据假设ID是唯一标识且不为空
questionMapper.updateById(question);
// 修改选项
// 修改选项,获取题目表单中的选项列表
List<Option> options = questionFrom.getOptions();
// 遍历题目表单中的每个选项调用optionMapper的updateById方法根据选项实体对象的ID更新对应的选项数据假设ID是唯一标识且不为空
for (Option option : options) {
optionMapper.updateById(option);
}
// 如果题目更新成功题目数据有了有效的ID这里假设更新前后ID不变且不为空进行缓存相关操作
if (question.getId() != null) { // 确保ID有效
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:question:pagingQuestion:"+question.getId().toString()); // 删除旧缓存
// 如果是更新操作,先从缓存中移除旧数据,根据题目ID删除对应的缓存数据缓存键格式为"cache:question:pagingQuestion:" + 题目ID
stringRedisTemplate.delete("cache:question:pagingQuestion:" + question.getId().toString());
// QuestionVO questionVO = questionConverter.QuestionToQuestionVO(question); // 转换为视图对象
// Map<Integer, QuestionVO> map = Map.of(questionVO.getId(), questionVO);
// cacheClient.batchPut("cache:question:pagingQuestion:",map,10L,TimeUnit.MINUTES); // 存储新数据
}
// 返回更新成功的结果信息
return Result.success("修改成功");
}
// 导入题目从Excel文件导入的方法事务性操作成功返回导入成功的提示信息失败返回相应错误提示使用了异常处理注解来简化异常抛出处理
@SneakyThrows(Exception.class)
@Override
@Transactional
public Result<String> importQuestion(Integer id, MultipartFile file) {
// 检查传入的文件是否是合法的Excel文件调用ExcelUtils工具类的isExcel方法进行判断
if (!ExcelUtils.isExcel(Objects.requireNonNull(file.getOriginalFilename()))) {
return Result.failed("该文件不是一个合法的Excel文件");
}
// 调用ExcelUtils的readMultipartFile方法读取传入的MultipartFile类型的Excel文件并转换为QuestionExcelFrom对象列表假设该方法实现了读取并转换逻辑
List<QuestionExcelFrom> questionExcelFroms = ExcelUtils.readMultipartFile(file, QuestionExcelFrom.class);
// 类型转换
// 进行类型转换将QuestionExcelFrom对象列表转换为QuestionFrom对象列表调用QuestionExcelFrom类中的静态方法进行转换假设该方法实现了转换逻辑
List<QuestionFrom> list = QuestionExcelFrom.converterQuestionFrom(questionExcelFroms);
// 遍历转换后的QuestionFrom对象列表处理每个题目表单对象对应的题目及选项数据插入逻辑
for (QuestionFrom questionFrom : list) {
// 遍历传入的QuestionFrom类型的列表QuestionFrom可能是用于接收前端传递过来的题目相关数据的表单对象从命名推测其功能每次循环处理一个题目相关的数据对象。
Question question = questionConverter.fromToEntity(questionFrom);
// 使用questionConverter可能是一个用于对象转换的工具类将QuestionFrom对象转换为Question实体对象方便后续持久化操作Question实体对象应该是对应数据库中存储题目信息的实体类实例包含了如题目内容、类型等各种属性。
question.setRepoId(id);
// 将题目实体对象的RepoId属性设置为传入的id值推测RepoId可能是用于关联题目所属的题库ID等相关信息具体含义需结合业务逻辑确定
// 添加单题获取Id
questionMapper.insert(question);
// 调用questionMapper可能是操作题目数据的持久层接口用于与数据库交互的insert方法将转换后的题目实体对象插入到数据库中插入成功后数据库会为该题目记录分配一个唯一的ID通常是自增长的主键并且这个ID会回填到question对象中假设使用了相应的数据库框架支持此功能
// 批量添加选项
List<Option> options = questionFrom.getOptions();
// 从QuestionFrom对象中获取其包含的选项列表Option对象应该是代表题目选项相关信息的实体类每个Option实例存储了一个选项的具体内容、是否正确等属性这里获取到的是当前题目对应的所有选项信息列表。
final int[] count = {0};
// 创建一个长度为1的整数数组用于记录选项的序号初始值设为0通过数组的方式可以在Lambda表达式中修改其值类似一种简单的引用传递效果因为Java的Lambda表达式中修改外部局部变量有限制
options.forEach(option -> {
//简答题答案默认给正确
// 简答题答案默认给正确
if (question.getQuType() == 4) {
// 判断当前题目question对象的题目类型QuType属性是否为4推测4代表简答题类型具体需结合业务定义如果是简答题类型则将当前选项的IsRight属性设置为1表示该选项为正确答案对于简答题可能默认只有一个正确答案的设定
option.setIsRight(1);
}
option.setSort(++count[0]);
// 为当前选项设置序号,通过自增操作(++count[0]给选项的Sort属性赋值使其按照顺序依次递增用于表示选项的排列顺序方便后续展示等操作。
option.setQuId(question.getId());
// 将当前选项的QuId属性设置为当前题目的ID即前面插入题目记录后回填到question对象中的ID值建立选项与题目的关联关系表明该选项属于当前这个题目。
});
// 避免简答题没有答案
if (!options.isEmpty()) {
// 判断选项列表是否为空,即确保当前题目有选项信息(虽然简答题可能默认有一个答案选项,但也需要排除没有任何选项数据的情况),如果不为空,则执行以下批量插入选项的操作。
optionMapper.insertBatch(options);
// 调用optionMapper可能是操作选项数据的持久层接口的insertBatch方法将整理好的选项列表批量插入到数据库中实现一次插入多个选项记录的操作提高插入效率。
}
if (question.getId() != null) { // 确保ID有效
// 如果题目实体对象的ID不为空即前面插入题目操作成功获取到了有效的ID执行以下缓存相关的操作可能是为了保证数据一致性更新缓存中的题目数据
// 如果是更新操作,先从缓存中移除旧数据,然后重新放入最新的数据
stringRedisTemplate.delete("cache:question:pagingQuestion:"+question.getId().toString()); // 删除旧缓存
stringRedisTemplate.delete("cache:question:pagingQuestion:" + question.getId().toString()); // 删除旧缓存
// 调用stringRedisTemplate用于操作Redis字符串类型数据的工具类常用于缓存相关操作的delete方法根据构建的缓存键格式为 "cache:question:pagingQuestion:" 加上题目ID的字符串形式删除对应的缓存数据目的是移除旧的题目缓存信息避免缓存数据不一致。
// QuestionVO questionVO = questionConverter.QuestionToQuestionVO(question); // 转换为视图对象
// 注释掉的这行代码原本可能是使用questionConverter前面用于对象转换的工具类将Question实体对象转换为QuestionVO对象推测是用于展示给前端的视图对象包含了前端需要展示的题目相关信息但当前代码中这行被注释掉了可能暂不需要此转换操作或者后续会有其他处理方式。
// Map<Integer, QuestionVO> map = Map.of(questionVO.getId(), questionVO);
// cacheClient.batchPut("cache:question:pagingQuestion:",map,10L,TimeUnit.MINUTES); // 存储新数据
// 注释掉的这行代码原本可能是创建一个只包含当前题目视图对象questionVO的Map以题目ID为键题目视图对象为值可能用于后续缓存存储等操作但同样被注释掉了可能暂不执行此逻辑。
// cacheClient.batchPut("cache:question:pagingQuestion:", map, 10L, TimeUnit.MINUTES); // 存储新数据
// 注释掉的这行代码原本可能是使用cacheClient可能是缓存操作相关的客户端工具类的batchPut方法将包含最新题目视图对象的Map数据存储到缓存中设置缓存过期时间为10分钟以实现缓存更新保证下次获取题目数据时能获取到最新的信息但目前代码中此操作被注释掉了暂不执行缓存存储新数据的逻辑。
}
}
return Result.success("导入成功");
}
// 当遍历完所有题目相关的数据对象并完成相应的插入、关联以及缓存相关操作如果有的话返回一个表示操作成功的Result结果对象其中包含提示信息“导入成功”用于告知调用者题目数据导入操作已顺利完成具体Result对象的结构和使用方式需结合项目中相关定义来看。
@SneakyThrows(IOException.class)
@Override
public Result<String> uploadImage(MultipartFile file) {
if (!aliOSSUtil.isImage(Objects.requireNonNull(file.getOriginalFilename()))) {
return Result.failed("该文件不是常用图片格式(png、jpg、jpeg、bmp)");
}
if (aliOSSUtil.isOverSize(file)) {
return Result.failed("图片大小不能超过50KB");
}
String url = aliOSSUtil.upload(file);
if (StringUtils.isBlank(url)) {
return Result.failed("图片上传失败");
@SneakyThrows(IOException.class)
// 使用 @SneakyThrows注解用于简化异常处理当方法内部抛出IOException异常时会自动将异常向上抛出避免了显式编写try-catch块来处理异常但需要注意在合适的调用层次上进行统一的异常处理这里表示该方法可能会抛出输入输出相关的异常比如文件读取等操作可能出现的异常
@Override
public Result<String> uploadImage (MultipartFile file){
// 实现接口中定义的上传图片方法接收一个MultipartFile类型的文件对象用于上传图片文件该方法的功能是将传入的图片文件上传到指定位置可能是云存储等地方并返回相应的结果信息。
if (!aliOSSUtil.isImage(Objects.requireNonNull(file.getOriginalFilename()))) {
// 调用aliOSSUtil可能是用于与阿里云对象存储服务交互或者进行文件类型判断等相关操作的工具类的isImage方法传入文件的原始文件名通过Objects.requireNonNull确保文件名不为空判断该文件是否是图片格式如果不是图片格式则执行以下返回操作。
return Result.failed("该文件不是常用图片格式(png、jpg、jpeg、bmp)");
// 返回一个表示操作失败的Result结果对象包含提示信息“该文件不是常用图片格式(png、jpg、jpeg、bmp)”告知调用者上传的文件不符合图片格式要求具体Result对象的结构和使用方式需结合项目中相关定义来看。
}
if (aliOSSUtil.isOverSize(file)) {
// 调用aliOSSUtil的isOverSize方法传入文件对象判断该文件大小是否超过了规定的限制从方法名推测是用于判断文件大小是否超出限制的功能如果文件大小超过限制则执行以下返回操作。
return Result.failed("图片大小不能超过50KB");
// 返回一个表示操作失败的Result结果对象包含提示信息“图片大小不能超过50KB”告知调用者上传的图片文件大小不符合要求具体Result对象的结构和使用方式需结合项目中相关定义来看。
}
String url = aliOSSUtil.upload(file);
// 调用aliOSSUtil的upload方法传入文件对象将图片文件上传到指定位置比如阿里云对象存储等地方并返回上传后文件的访问地址如果上传成功存储在url变量中。
if (StringUtils.isBlank(url)) {
// 判断返回的文件访问地址是否为空字符串,如果为空,表示图片上传失败,执行以下返回操作。
return Result.failed("图片上传失败");
// 返回一个表示操作失败的Result结果对象包含提示信息“图片上传失败”告知调用者图片上传过程出现问题具体Result对象的结构和使用方式需结合项目中相关定义来看。
}
return Result.success("图片上传成功", url);
// 如果文件访问地址不为空说明图片上传成功返回一个表示操作成功的Result结果对象包含提示信息“图片上传成功”以及上传后文件的访问地址通过第二个参数传递方便调用者获取图片的访问路径进行后续操作具体Result对象的结构和使用方式需结合项目中相关定义来看。
}
return Result.success("图片上传成功", url);
}
}
}

@ -30,95 +30,128 @@ import java.util.List;
/**
* @author WeiJin
* @since 2024-03-21
* IRepoServiceRepo
*/
@Service
public class RepoServiceImpl extends ServiceImpl<RepoMapper, Repo> implements IRepoService {
// 注入RepoMapper用于操作题库数据的持久层接口比如插入、更新、查询、删除题库记录等操作。
@Resource
private RepoMapper repoMapper;
// 注入QuestionMapper用于操作题目数据的持久层接口可能在涉及题库与题目关联的业务逻辑中使用比如更新题目所属题库等操作。
@Resource
private QuestionMapper questionMapper;
// 注入ExerciseRecordMapper用于操作练习记录数据的持久层接口或许在获取题库相关统计信息如刷题次数等时会用到。
@Resource
private ExerciseRecordMapper exerciseRecordMapper;
// 添加题库的方法将传入的Repo对象插入到数据库中成功返回保存成功的提示信息。
@Override
public Result<String> addRepo(Repo repo) {
// 调用repoMapper的insert方法将传入的Repo对象插入到数据库中执行插入操作。
repoMapper.insert(repo);
// 返回表示保存成功的结果信息,通常可以在前端等地方展示给用户相应提示。
return Result.success("保存成功");
}
// 更新题库信息的方法根据传入的Repo对象和要更新的题库ID更新对应题库的标题信息成功返回修改成功的提示信息。
@Override
public Result<String> updateRepo(Repo repo, Integer id) {
//修改题库
// 创建一个LambdaUpdateWrapper对象用于构建更新题库信息的条件和要更新的字段。
LambdaUpdateWrapper<Repo> updateWrapper = new LambdaUpdateWrapper<Repo>()
.eq(Repo::getId, id).set(Repo::getTitle, repo.getTitle());
// 设置更新条件为题库的ID等于传入的id参数确保更新的是指定的题库记录。
.eq(Repo::getId, id)
// 设置要更新的字段为题库的标题将其更新为传入Repo对象中的标题值。
.set(Repo::getTitle, repo.getTitle());
// 调用repoMapper的update方法根据构建好的更新条件和要更新的字段来更新数据库中的题库记录。
repoMapper.update(updateWrapper);
// 返回表示修改成功的结果信息,方便前端等地方知晓操作结果并展示相应提示给用户。
return Result.success("修改成功");
}
// 删除题库的方法事务性操作先清空题库内所有题目所属的题库ID然后将该题库标记为已删除逻辑删除根据操作结果返回相应提示信息。
@Override
@Transactional
public Result<String> deleteRepoById(Integer id) {
//题库内试题清空所属题库id
// 清空题库内试题所属的题库ID创建一个LambdaUpdateWrapper对象用于构建更新题目信息的条件和要更新的字段。
LambdaUpdateWrapper<Question> wrapper = new LambdaUpdateWrapper<Question>()
// 设置更新条件为题目所属的题库ID等于传入的要删除的题库ID。
.eq(Question::getRepoId, id)
// 设置要更新的字段为题目所属的题库ID将其更新为null表示清空所属题库关系。
.set(Question::getRepoId, null);
// 调用questionMapper的update方法根据构建的条件更新数据库中相关题目的所属题库ID字段。
questionMapper.update(wrapper);
//删除题库
// 创建一个LambdaUpdateWrapper对象用于构建删除题库的逻辑这里采用逻辑删除设置删除标记字段
LambdaUpdateWrapper<Repo> repoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
repoLambdaUpdateWrapper.eq(Repo::getId,id)
.set(Repo::getIsDeleted,1);
// 设置更新条件为题库的ID等于传入的要删除的题库ID。
repoLambdaUpdateWrapper.eq(Repo::getId, id)
// 设置题库的删除标记字段假设名为isDeleted为1表示已删除。
.set(Repo::getIsDeleted, 1);
// 调用repoMapper的update方法根据构建的条件执行逻辑删除操作返回受影响的行数即实际更新的记录数。
int result = repoMapper.update(repoLambdaUpdateWrapper);
// 如果更新操作影响的行数大于0表示删除操作成功返回删除成功的结果信息。
if (result > 0) {
return Result.success("删除成功");
}
// 如果更新操作影响的行数为0表示删除操作失败返回删除失败的结果信息。
return Result.failed("删除失败");
}
// 获取题库列表的方法,根据当前用户角色(教师或其他角色)以及传入的题库标题模糊查询条件,从数据库中获取相应的题库列表信息,返回包含查询结果的成功提示信息。
@Override
public Result<List<RepoListVO>> getRepoList(String repoTitle) {
List<RepoListVO> list;
// 判断当前用户角色是否为"role_teacher"(通常代表教师角色,具体含义由业务定义)。
if ("role_teacher".equals(SecurityUtil.getRole())) {
// 如果是教师角色调用repoMapper的selectRepoList方法传入题库标题和当前用户ID获取该教师创建的符合标题模糊查询条件的题库列表。
list = repoMapper.selectRepoList(repoTitle, SecurityUtil.getUserId());
} else {
// 如果不是教师角色可能是其他角色比如管理员等调用repoMapper的selectRepoList方法传入题库标题和默认值0具体含义由业务决定可能表示获取所有或公共的题库等情况获取相应的题库列表。
list = repoMapper.selectRepoList(repoTitle, 0);
}
// 返回包含查询到的题库列表信息的成功结果信息,方便前端展示给用户查看。
return Result.success("获取成功", list);
}
// 获取题库分页信息的方法,根据当前用户角色(教师或其他角色)、页码、每页数量以及传入的题库标题模糊查询条件,从数据库中获取相应的分页题库信息,返回包含分页结果的成功提示信息。
@Override
public Result<IPage<RepoVO>> pagingRepo(Integer pageNum, Integer pageSize, String title) {
// 创建一个IPage对象用于封装分页相关信息设置当前页码和每页显示数量。
IPage<RepoVO> page = new Page<>(pageNum, pageSize);
// 判断当前用户角色是否为"role_teacher"(通常代表教师角色,具体含义由业务定义)。
if ("role_teacher".equals(SecurityUtil.getRole())) {
//教师只查询自己的题库
// 如果是教师角色调用repoMapper的pagingRepo方法传入分页对象、题库标题和当前用户ID获取该教师创建的符合标题模糊查询条件的分页题库信息将结果赋值给page对象。
page = repoMapper.pagingRepo(page, title, SecurityUtil.getUserId());
} else {
//管理员可以获取所有题库
// 如果不是教师角色可能是其他角色比如管理员等调用repoMapper的pagingRepo方法传入分页对象、题库标题和默认值0具体含义由业务决定可能表示获取所有或公共的题库等情况获取相应的分页题库信息将结果赋值给page对象。
page = repoMapper.pagingRepo(page, title, 0);
}
// 返回包含分页题库信息的成功结果信息方便前端进行分页展示等操作这里第一个参数为null可能是预留用于传递其他提示信息等情况。
return Result.success(null, page);
}
// 获取包含练习相关信息的题库信息的方法,根据页码、每页数量以及传入的题库标题模糊查询条件,从数据库中获取相应的分页题库信息,并可以进一步填充每个题库的刷题次数等相关练习信息(当前代码中填充刷题次数的逻辑被注释掉了),返回包含分页结果的成功提示信息。
@Override
public Result<IPage<ExerciseRepoVO>> getRepo(Integer pageNum, Integer pageSize, String title) {
// 创建一个IPage对象用于封装分页相关信息设置当前页码和每页显示数量。
IPage<ExerciseRepoVO> page = new Page<>(pageNum, pageSize);
// 调用repoMapper的selectRepo方法传入分页对象和题库标题获取相应的分页题库信息将结果赋值给page对象。
page = repoMapper.selectRepo(page, title);
// page.getRecords().forEach(repoVO -> {
// //填充以刷题数
// LambdaQueryWrapper<ExerciseRecord> wrapper = new LambdaQueryWrapper<ExerciseRecord>().eq(ExerciseRecord::getRepoId, repoVO.getId())
// .eq(ExerciseRecord::getUserId, SecurityUtil.getUserId());
// .eq(ExerciseRecord::getUserId, SecurityUtil.getUserId());
//
// repoVO.setExerciseCount(exerciseRecordMapper.selectCount(wrapper).intValue());
// });
// 返回包含分页题库信息的成功结果信息方便前端进行分页展示等操作这里第一个参数为null可能是预留用于传递其他提示信息等情况。
return Result.success(null, page);
}
}
}

@ -1,20 +1,29 @@
package cn.org.alan.exam.service.impl;
// 导入RoleMapper接口它是用于操作角色Role相关数据的持久层接口比如对角色数据进行增删改查等操作从命名推测其功能
import cn.org.alan.exam.mapper.RoleMapper;
// 导入Role实体类代表角色相关的实体对象包含了角色的各种属性信息例如角色名称、角色权限等
import cn.org.alan.exam.model.entity.Role;
// 导入IRoleService接口该接口定义了角色相关的业务服务方法规定了具体有哪些业务操作需要实现例如获取角色列表、添加角色等
import cn.org.alan.exam.service.IRoleService;
// 导入ServiceImpl类这是MyBatis Plus提供的基础服务实现类它提供了很多通用的数据库操作方法方便在此基础上扩展具体业务逻辑的实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入Spring的Service注解用于标记该类是一个服务层组件Spring容器会对其进行管理使其能够参与依赖注入等相关机制
import org.springframework.stereotype.Service;
/**
* <p>
*
*
* </p>
*
* @author WeiJin
* @since 2024-03-21
*/
// 使用@Service注解将该类标记为Spring容器管理的服务层组件意味着在Spring应用中可以通过依赖注入的方式在其他地方使用这个服务类
@Service
// 定义RoleServiceImpl类它继承自ServiceImpl<RoleMapper, Role>同时实现了IRoleService接口
// 通过继承ServiceImpl可以复用其中已有的通用数据库操作方法再按照IRoleService接口中定义的业务方法来实现具体针对角色的业务逻辑
// 目前类体为空,表示暂时没有添加额外的业务方法实现,如果后续有添加角色、删除角色、查询角色等业务逻辑相关的方法,就需要在这里进行具体的编写
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {
}
}

@ -22,7 +22,7 @@ import java.util.List;
/**
* <p>
*
*
* </p>
*
* @author WeiJin
@ -31,82 +31,117 @@ import java.util.List;
@Service
public class StatServiceImpl extends ServiceImpl<ExamGradeMapper, ExamGrade> implements IStatService {
// 使用 @Resource 注解注入 StatMapper用于操作统计相关数据的持久层接口可能包含各种统计查询方法从命名推测其功能
@Resource
private StatMapper statMapper;
// 注入 GradeMapper用于操作班级相关数据的持久层接口比如查询班级数量等操作在统计班级相关信息时可能会用到
@Resource
private GradeMapper gradeMapper;
// 注入 ExamMapper用于操作考试相关数据的持久层接口例如查询考试数量等操作在统计考试相关信息时可能会用到
@Resource
private ExamMapper examMapper;
// 注入 QuestionMapper用于操作题目相关数据的持久层接口像查询题目数量等操作在统计题目相关信息时可能会用到
@Resource
private QuestionMapper questionMapper;
// 注入 UserDailyLoginDurationMapper用于操作用户每日登录时长相关数据的持久层接口推测在获取每日相关统计信息时会用到
@Resource
private UserDailyLoginDurationMapper userDailyLoginDurationMapper;
/**
*
*
*
* @return
* @return Result
*/
@Override
public Result<List<GradeStudentVO>> getStudentGradeCount() {
//获取班级
// 定义用于存储各班人数统计信息的列表,类型为 GradeStudentVO初始未赋值。
List<GradeStudentVO> gradeStudentVOs;
// 判断当前用户角色是否为 "role_teacher"(通常代表教师角色,具体含义由业务定义)。
if ("role_teacher".equals(SecurityUtil.getRole())) {
// 如果是教师角色,调用 statMapper 的 StudentGradeCount 方法,传入参数 2 和当前用户 ID获取该教师相关的各班人数统计信息。
// 这里的参数 2 具体含义可能由业务逻辑决定,可能是用于区分不同的统计条件或筛选情况等。
gradeStudentVOs = statMapper.StudentGradeCount(2, SecurityUtil.getUserId());
} else {
// 如果不是教师角色(可能是其他角色,比如管理员等),调用 statMapper 的 StudentGradeCount 方法,传入参数 3 和当前用户 ID获取相应的各班人数统计信息。
// 同样,参数 3 的具体含义由业务逻辑确定,用于特定的统计筛选等情况。
gradeStudentVOs = statMapper.StudentGradeCount(3, SecurityUtil.getUserId());
}
// 返回包含各班人数统计信息的成功结果信息,方便前端展示给用户查看,"查询成功" 为提示消息gradeStudentVOs 为具体的数据列表。
return Result.success("查询成功", gradeStudentVOs);
}
/**
*
*
*
* @return
* @return Result
*/
@Override
public Result<List<GradeExamVO>> getExamGradeCount() {
//获取班级
// 定义用于存储各班试卷统计信息的列表,类型为 GradeExamVO初始未赋值。
List<GradeExamVO> gradeExamVOs;
// 判断当前用户角色是否为 "role_teacher"(通常代表教师角色,具体含义由业务定义)。
if ("role_teacher".equals(SecurityUtil.getRole())) {
// 如果是教师角色,调用 statMapper 的 ExamGradeCount 方法,传入参数 2 和当前用户 ID获取该教师相关的各班试卷统计信息。
// 参数 2 的具体含义同上述类似,由业务逻辑决定,用于特定的统计条件或筛选情况。
gradeExamVOs = statMapper.ExamGradeCount(2, SecurityUtil.getUserId());
} else {
// 如果不是教师角色(可能是其他角色,比如管理员等),调用 statMapper 的 ExamGradeCount 方法,传入参数 3 和当前用户 ID获取相应的各班试卷统计信息。
gradeExamVOs = statMapper.ExamGradeCount(3, SecurityUtil.getUserId());
}
// 返回包含各班试卷统计信息的成功结果信息,便于前端展示给用户,"查询成功" 为提示消息gradeExamVOs 为具体的数据列表。
return Result.success("查询成功", gradeExamVOs);
}
/**
*
* AllStatsVO
*
* @return
* @return Result
*/
@Override
public Result<AllStatsVO> getAllCount() {
// 创建一个 AllStatsVO 对象,用于封装所有班级、试卷、试题数量的统计信息,初始各属性值为空。
AllStatsVO allStatsVO = new AllStatsVO();
// 获取当前用户的角色信息,用于后续判断是管理员还是教师角色来进行不同的统计逻辑。
String role = SecurityUtil.getRole();
if("role_admin".equals(role)){
// 判断当前用户角色是否为 "role_admin"(通常代表管理员角色,具体含义由业务定义)。
if ("role_admin".equals(role)) {
// 如果是管理员角色,通过 gradeMapper 的 selectCount 方法,传入 null表示无额外查询条件统计所有班级数量获取班级总数并设置到 allStatsVO 对象中。
allStatsVO.setClassCount(gradeMapper.selectCount(null).intValue());
// 同样,通过 examMapper 的 selectCount 方法,传入 null统计所有考试数量获取考试总数设置到 allStatsVO 对象中。
allStatsVO.setExamCount(examMapper.selectCount(null).intValue());
// 通过 questionMapper 的 selectCount 方法,传入 null统计所有题目数量获取题目总数设置到 allStatsVO 对象中。
allStatsVO.setQuestionCount(questionMapper.selectCount(null).intValue());
}else if("role_teacher".equals(role)){
} else if ("role_teacher".equals(role)) {
// 如果是教师角色,通过 gradeMapper 的 selectCount 方法,传入一个 LambdaQueryWrapper 对象,设置查询条件为班级所属用户 ID 等于当前教师的用户 ID获取该教师创建的班级数量并设置到 allStatsVO 对象中。
allStatsVO.setClassCount(gradeMapper.selectCount(new LambdaQueryWrapper<Grade>().eq(Grade::getUserId,
SecurityUtil.getUserId())).intValue());
SecurityUtil.getUserId()))
.intValue());
// 通过 examMapper 的 selectCount 方法,传入一个 LambdaQueryWrapper 对象,设置查询条件为考试创建者用户 ID 等于当前教师的用户 ID获取该教师创建的考试数量并设置到 allStatsVO 对象中。
allStatsVO.setExamCount(examMapper.selectCount(
new LambdaQueryWrapper<Exam>().eq(Exam::getUserId,SecurityUtil.getUserId())).intValue());
new LambdaQueryWrapper<Exam>().eq(Exam::getUserId, SecurityUtil.getUserId()))
.intValue());
// 通过 questionMapper 的 selectCount 方法,传入一个 LambdaQueryWrapper 对象,设置查询条件为题目创建者用户 ID 等于当前教师的用户 ID获取该教师创建的题目数量并设置到 allStatsVO 对象中。
allStatsVO.setQuestionCount(questionMapper.selectCount(
new LambdaQueryWrapper<Question>().eq(Question::getUserId,SecurityUtil.getUserId())).intValue());
new LambdaQueryWrapper<Question>().eq(Question::getUserId, SecurityUtil.getUserId()))
.intValue());
}
// 返回包含所有统计信息的成功结果信息,方便前端展示给用户查看,"查询成功" 为提示消息allStatsVO 为包含具体统计数据的对象。
return Result.success("查询成功", allStatsVO);
}
/**
* ID
*
* @return Result
*/
@Override
public Result<List<DailyVO>> getDaily() {
// 调用 userDailyLoginDurationMapper 的 getDaily 方法,传入当前用户 ID获取该用户的每日相关统计信息从方法名推测可能是登录时长等相关信息返回一个 DailyVO 类型的列表。
List<DailyVO> daily = userDailyLoginDurationMapper.getDaily(SecurityUtil.getUserId());
return Result.success("请求成功",daily);
// 返回包含每日统计信息的成功结果信息,便于前端展示给用户,"请求成功" 为提示消息daily 为具体的数据列表。
return Result.success("请求成功", daily);
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save