diff --git a/repo/controller/RepoController.java b/repo/controller/RepoController.java
new file mode 100644
index 0000000..7d1c420
--- /dev/null
+++ b/repo/controller/RepoController.java
@@ -0,0 +1,132 @@
+package com.yf.exam.modules.repo.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yf.exam.core.api.ApiRest;
+import com.yf.exam.core.api.controller.BaseController;
+import com.yf.exam.core.api.dto.BaseIdReqDTO;
+import com.yf.exam.core.api.dto.BaseIdsReqDTO;
+import com.yf.exam.core.api.dto.PagingReqDTO;
+import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO;
+import com.yf.exam.modules.qu.service.QuRepoService;
+import com.yf.exam.modules.repo.dto.RepoDTO;
+import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
+import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
+import com.yf.exam.modules.repo.entity.Repo;
+import com.yf.exam.modules.repo.service.RepoService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * 题库控制器,处理与题库相关的 HTTP 请求
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:25
+ */
+@Api(tags={"题库"})
+@RestController
+@RequestMapping("/exam/api/repo")
+public class RepoController extends BaseController {
+
+ /**
+ * 注入题库服务,用于处理题库相关的业务逻辑
+ */
+ @Autowired
+ private RepoService baseService;
+
+ /**
+ * 注入题目与题库关联服务,用于处理题目与题库的批量操作
+ */
+ @Autowired
+ private QuRepoService quRepoService;
+
+ /**
+ * 添加或修改题库信息
+ * @param reqDTO 包含题库信息的请求数据传输对象
+ * @return 操作结果,封装在 ApiRest 对象中
+ */
+ @RequiresRoles("sa")
+ @ApiOperation(value = "添加或修改")
+ @RequestMapping(value = "/save", method = { RequestMethod.POST})
+ public ApiRest save(@RequestBody RepoDTO reqDTO) {
+ // 调用服务层方法保存或更新题库信息
+ baseService.save(reqDTO);
+ // 返回操作成功的响应
+ return super.success();
+ }
+
+ /**
+ * 批量删除题库
+ * @param reqDTO 包含要删除的题库 ID 列表的请求数据传输对象
+ * @return 操作结果,封装在 ApiRest 对象中
+ */
+ @RequiresRoles("sa")
+ @ApiOperation(value = "批量删除")
+ @RequestMapping(value = "/delete", method = { RequestMethod.POST})
+ public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) {
+ // 根据 ID 列表删除题库
+ baseService.removeByIds(reqDTO.getIds());
+ // 返回操作成功的响应
+ return super.success();
+ }
+
+ /**
+ * 查找单个题库的详情信息
+ * @param reqDTO 包含要查找的题库 ID 的请求数据传输对象
+ * @return 包含题库详情信息的操作结果,封装在 ApiRest 对象中
+ */
+ @RequiresRoles("sa")
+ @ApiOperation(value = "查找详情")
+ @RequestMapping(value = "/detail", method = { RequestMethod.POST})
+ public ApiRest find(@RequestBody BaseIdReqDTO reqDTO) {
+ // 根据 ID 获取题库实体
+ Repo entity = baseService.getById(reqDTO.getId());
+ // 创建一个新的 DTO 对象
+ RepoDTO dto = new RepoDTO();
+ // 将实体对象的属性复制到 DTO 对象中
+ BeanUtils.copyProperties(entity, dto);
+ // 返回包含 DTO 对象的操作成功响应
+ return super.success(dto);
+ }
+
+ /**
+ * 分页查找题库信息
+ * @param reqDTO 包含分页和查询条件的请求数据传输对象
+ * @return 包含分页查询结果的操作结果,封装在 ApiRest 对象中
+ */
+ @RequiresRoles("sa")
+ @ApiOperation(value = "分页查找")
+ @RequestMapping(value = "/paging", method = { RequestMethod.POST})
+ public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) {
+
+ // 调用服务层方法进行分页查询并转换结果
+ IPage page = baseService.paging(reqDTO);
+
+ // 返回包含分页结果的操作成功响应
+ return super.success(page);
+ }
+
+ /**
+ * 批量操作,批量加入或从题库移除题目
+ * @param reqDTO 包含批量操作信息的请求数据传输对象
+ * @return 操作结果,封装在 ApiRest 对象中
+ */
+ @RequiresRoles("sa")
+ @ApiOperation(value = "批量操作", notes = "批量加入或从题库移除")
+ @RequestMapping(value = "/batch-action", method = { RequestMethod.POST})
+ public ApiRest batchAction(@RequestBody QuRepoBatchReqDTO reqDTO) {
+
+ // 调用服务层方法进行批量操作
+ quRepoService.batchAction(reqDTO);
+ // 返回操作成功的响应
+ return super.success();
+ }
+}
diff --git a/repo/dto/RepoDTO.java b/repo/dto/RepoDTO.java
new file mode 100644
index 0000000..c2063da
--- /dev/null
+++ b/repo/dto/RepoDTO.java
@@ -0,0 +1,67 @@
+package com.yf.exam.modules.repo.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ *
+ * 题库请求类,用于在与题库相关的业务操作中传输数据,实现了 Serializable 接口,可进行序列化操作。
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:23
+ */
+@Data
+@ApiModel(value="题库", description="题库")
+public class RepoDTO implements Serializable {
+
+ // 序列化版本号,确保序列化和反序列化的兼容性
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 题库的唯一标识 ID,在业务操作中用于唯一确定一个题库。
+ * 该属性为必填项。
+ */
+ @ApiModelProperty(value = "题库ID", required=true)
+ private String id;
+
+ /**
+ * 题库的编号,可用于对题库进行分类或标识。
+ * 该属性为必填项。
+ */
+ @ApiModelProperty(value = "题库编号", required=true)
+ private String code;
+
+ /**
+ * 题库的名称,用于直观地表示题库的主题或内容。
+ * 该属性为必填项。
+ */
+ @ApiModelProperty(value = "题库名称", required=true)
+ private String title;
+
+ /**
+ * 题库的备注信息,可用于记录关于题库的额外说明或注意事项。
+ * 该属性为必填项。
+ */
+ @ApiModelProperty(value = "题库备注", required=true)
+ private String remark;
+
+ /**
+ * 题库的创建时间,记录该题库创建的具体时间点。
+ * 该属性为必填项。
+ */
+ @ApiModelProperty(value = "创建时间", required=true)
+ private Date createTime;
+
+ /**
+ * 题库的更新时间,记录该题库最后一次被修改的具体时间点。
+ * 该属性为必填项。
+ */
+ @ApiModelProperty(value = "更新时间", required=true)
+ private Date updateTime;
+
+}
diff --git a/repo/dto/request/RepoReqDTO.java b/repo/dto/request/RepoReqDTO.java
new file mode 100644
index 0000000..89cef56
--- /dev/null
+++ b/repo/dto/request/RepoReqDTO.java
@@ -0,0 +1,40 @@
+package com.yf.exam.modules.repo.dto.request;
+
+import com.yf.exam.modules.repo.dto.RepoDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ *
+ * 题库请求类,用于在分页查询题库信息时传递请求参数,继承自 RepoDTO 类,可复用其属性。
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:23
+ */
+@Data
+@ApiModel(value="题库分页请求类", description="题库分页请求类")
+public class RepoReqDTO extends RepoDTO {
+
+ // 序列化版本号,确保序列化和反序列化的兼容性
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 排除题库 ID 列表,在查询题库时,会排除这些 ID 对应的题库。
+ * 该参数为必填项。
+ */
+ @ApiModelProperty(value = "排除题库ID", required=true)
+ private List excludes;
+
+ /**
+ * 题库标题,用于筛选具有特定标题的题库。
+ * 此处注解中的描述与字段名不符,原注解描述为单选题数量,可能存在错误,应根据实际需求调整。
+ * 该参数为必填项。
+ */
+ @ApiModelProperty(value = "单选题数量", required=true)
+ private String title;
+
+}
diff --git a/repo/dto/response/RepoRespDTO.java b/repo/dto/response/RepoRespDTO.java
new file mode 100644
index 0000000..e871768
--- /dev/null
+++ b/repo/dto/response/RepoRespDTO.java
@@ -0,0 +1,45 @@
+package com.yf.exam.modules.repo.dto.response;
+
+import com.yf.exam.modules.repo.dto.RepoDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ *
+ * 题库分页响应类,用于封装题库分页查询的响应数据。
+ * 该类继承自 RepoDTO,在其基础上扩展了不同题型数量的属性。
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:23
+ */
+@Data
+@ApiModel(value="题库分页响应类", description="题库分页响应类")
+public class RepoRespDTO extends RepoDTO {
+
+ // 序列化版本号,用于在序列化和反序列化过程中确保版本的兼容性
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 多选题数量,代表该题库中多选题的总数。
+ * 该属性为必填项,在响应数据中必须包含。
+ */
+ @ApiModelProperty(value = "多选题数量", required=true)
+ private Integer multiCount;
+
+ /**
+ * 单选题数量,代表该题库中单选题的总数。
+ * 该属性为必填项,在响应数据中必须包含。
+ */
+ @ApiModelProperty(value = "单选题数量", required=true)
+ private Integer radioCount;
+
+ /**
+ * 判断题数量,代表该题库中判断题的总数。
+ * 该属性为必填项,在响应数据中必须包含。
+ */
+ @ApiModelProperty(value = "判断题数量", required=true)
+ private Integer judgeCount;
+
+}
diff --git a/repo/entity/Repo.java b/repo/entity/Repo.java
new file mode 100644
index 0000000..cb37a3c
--- /dev/null
+++ b/repo/entity/Repo.java
@@ -0,0 +1,62 @@
+package com.yf.exam.modules.repo.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ *
+ * 题库实体类,对应数据库中的 `el_repo` 表,用于封装题库相关的数据。
+ * 继承自 MyBatis-Plus 的 Model 类,可使用其提供的 ActiveRecord 功能进行数据库操作。
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:23
+ */
+@Data
+// 表明该实体类对应数据库中的 el_repo 表
+@TableName("el_repo")
+public class Repo extends Model {
+
+ // 序列化版本号,确保序列化和反序列化的兼容性
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 题库ID,作为表的主键,使用 MyBatis-Plus 的 ASSIGN_ID 策略自动分配 ID。
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 题库编号,用于标识不同的题库。
+ */
+ private String code;
+
+ /**
+ * 题库名称,直观反映题库的主题或内容。
+ */
+ private String title;
+
+ /**
+ * 题库备注,可用于记录关于题库的额外说明信息。
+ */
+ private String remark;
+
+ /**
+ * 创建时间,记录题库创建的时间,对应数据库表中的 create_time 字段。
+ */
+ @TableField("create_time")
+ private Date createTime;
+
+ /**
+ * 更新时间,记录题库最后一次更新的时间,对应数据库表中的 update_time 字段。
+ */
+ @TableField("update_time")
+ private Date updateTime;
+
+}
diff --git a/repo/mapper/RepoMapper.java b/repo/mapper/RepoMapper.java
new file mode 100644
index 0000000..f6d009e
--- /dev/null
+++ b/repo/mapper/RepoMapper.java
@@ -0,0 +1,31 @@
+package com.yf.exam.modules.repo.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
+import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
+import com.yf.exam.modules.repo.entity.Repo;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ *
+ * 题库Mapper接口,继承自 MyBatis-Plus 的 BaseMapper 接口,用于操作题库实体类与数据库的交互。
+ * BaseMapper 接口提供了基本的增删改查操作,该接口在此基础上扩展了分页查询功能。
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:23
+ */
+public interface RepoMapper extends BaseMapper {
+
+ /**
+ * 分页查询题库信息
+ *
+ * @param page 分页对象,包含分页的相关信息,如当前页码、每页记录数等,用于控制查询结果的分页展示。
+ * @param query 题库查询请求对象,包含查询条件,如排除题库ID、题库标题等,用于筛选符合条件的题库记录。
+ * @return 返回一个包含 RepoRespDTO 对象的分页数据,RepoRespDTO 是题库分页响应类,封装了题库的相关信息以及不同题型的数量。
+ */
+ IPage paging(Page page, @Param("query") RepoReqDTO query);
+
+}
diff --git a/repo/service/RepoService.java b/repo/service/RepoService.java
new file mode 100644
index 0000000..85dbed7
--- /dev/null
+++ b/repo/service/RepoService.java
@@ -0,0 +1,38 @@
+package com.yf.exam.modules.repo.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yf.exam.core.api.dto.PagingReqDTO;
+import com.yf.exam.modules.repo.dto.RepoDTO;
+import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
+import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
+import com.yf.exam.modules.repo.entity.Repo;
+
+/**
+ *
+ * 题库业务类,定义了与题库相关的业务操作接口。
+ * 该接口继承自 MyBatis-Plus 的 IService 接口,可使用其提供的基础 CRUD 操作方法。
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:23
+ */
+public interface RepoService extends IService {
+
+ /**
+ * 分页查询题库数据
+ *
+ * @param reqDTO 包含分页信息和查询条件的请求对象。
+ * PagingReqDTO 封装了分页参数,RepoReqDTO 封装了具体的查询条件,如排除题库 ID、题库标题等。
+ * @return 一个 IPage 对象,包含分页查询结果,结果元素为 RepoRespDTO 类型,该类型封装了题库的详细信息及题型数量。
+ */
+ IPage paging(PagingReqDTO reqDTO);
+
+ /**
+ * 保存或更新题库信息
+ *
+ * @param reqDTO 包含题库信息的数据传输对象,包含题库的 ID、编号、名称、备注等信息。
+ * 若 DTO 中的 ID 存在,则进行更新操作;若 ID 不存在,则进行新增操作。
+ */
+ void save(RepoDTO reqDTO);
+}
diff --git a/repo/service/impl/RepoServiceImpl.java b/repo/service/impl/RepoServiceImpl.java
new file mode 100644
index 0000000..454d167
--- /dev/null
+++ b/repo/service/impl/RepoServiceImpl.java
@@ -0,0 +1,53 @@
+package com.yf.exam.modules.repo.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yf.exam.core.api.dto.PagingReqDTO;
+import com.yf.exam.core.utils.BeanMapper;
+import com.yf.exam.modules.repo.dto.RepoDTO;
+import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
+import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
+import com.yf.exam.modules.repo.entity.Repo;
+import com.yf.exam.modules.repo.mapper.RepoMapper;
+import com.yf.exam.modules.repo.service.RepoService;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 题库服务实现类,实现了 RepoService 接口,继承自 MyBatis-Plus 的 ServiceImpl,
+ * 用于处理题库相关的业务逻辑,与数据库进行交互。
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2020-05-25 13:23
+ */
+@Service
+public class RepoServiceImpl extends ServiceImpl implements RepoService {
+
+ /**
+ * 分页查询题库信息
+ *
+ * @param reqDTO 包含分页信息和查询参数的请求对象
+ * @return 包含分页结果的 IPage 对象,其中元素为 RepoRespDTO 类型
+ */
+ @Override
+ public IPage paging(PagingReqDTO reqDTO) {
+ // 调用 RepoMapper 的 paging 方法进行分页查询,传入分页对象和查询参数
+ return baseMapper.paging(reqDTO.toPage(), reqDTO.getParams());
+ }
+
+ /**
+ * 保存或更新题库信息
+ *
+ * @param reqDTO 包含题库信息的请求数据传输对象
+ */
+ @Override
+ public void save(RepoDTO reqDTO) {
+
+ // 复制 DTO 对象的属性到实体对象
+ Repo entity = new Repo();
+ BeanMapper.copy(reqDTO, entity);
+ // 调用 MyBatis-Plus 的 saveOrUpdate 方法保存或更新实体对象
+ this.saveOrUpdate(entity);
+ }
+}
diff --git a/src/main/java/com/yf/exam/ability/Constant.java b/src/main/java/com/yf/exam/ability/Constant.java
deleted file mode 100644
index 91b40a7..0000000
--- a/src/main/java/com/yf/exam/ability/Constant.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.yf.exam.ability;
-
-
- //通用常量类
- //该类定义了应用程序中使用的通用常量。
- // @author bool
-
-public class Constant {
-
-
- //文件上传路径
- //该常量定义了文件上传时的默认路径前缀。
-
- public static final String FILE_PREFIX = "/upload/file/";
-}
diff --git a/src/main/java/com/yf/exam/ability/job/enums/JobGroup.java b/src/main/java/com/yf/exam/ability/job/enums/JobGroup.java
deleted file mode 100644
index 9b5f078..0000000
--- a/src/main/java/com/yf/exam/ability/job/enums/JobGroup.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.yf.exam.ability.job.enums;
-
-/**
- * 任务分组
- * @author van
- */
-public interface JobGroup {
-
- //定义了一个常量字符串,表示系统任务。
- //这个常量可以用于标识在系统中执行的任务组,通常用于任务调度和管理。
-
- String SYSTEM = "system";
-}
-
diff --git a/src/main/java/com/yf/exam/ability/job/enums/JobPrefix.java b/src/main/java/com/yf/exam/ability/job/enums/JobPrefix.java
deleted file mode 100644
index 404a996..0000000
--- a/src/main/java/com/yf/exam/ability/job/enums/JobPrefix.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.yf.exam.ability.job.enums;
-
-/**
- * 任务前缀
- * @author bool
- */
-public interface JobPrefix {
-
- //定义了一个常量字符串,表示强制交卷的操作前缀。
- //这个常量可以用于标识在系统中强制交卷的任务或操作,通常用于考试系统中的任务管理。
-
- String BREAK_EXAM = "break_exam_";
-}
-
diff --git a/src/main/java/com/yf/exam/ability/job/service/JobService.java b/src/main/java/com/yf/exam/ability/job/service/JobService.java
deleted file mode 100644
index c48c118..0000000
--- a/src/main/java/com/yf/exam/ability/job/service/JobService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.yf.exam.ability.job.service;
-
-
- //任务业务类,用于动态处理任务信息
- //@author bool
- //@date 2020/11/29 下午2:17
-
-public interface JobService {
-
-
-
- //任务数据
-
- String TASK_DATA = "taskData";
-
-
- //添加定时任务
- //@param jobClass
- //@param jobName
- //@param cron
- //@param data
-
- void addCronJob(Class jobClass, String jobName, String cron, String data);
-
-
- //添加立即执行的任务
- //@param jobClass
- //@param jobName
- //@param data
-
- void addCronJob(Class jobClass, String jobName, String data);
-
-
- //暂停任务
- //@param jobName
- //@param jobGroup
-
- void pauseJob(String jobName, String jobGroup);
-
-
- //恢复任务
- //@param triggerName
- //@param triggerGroup
-
- void resumeJob(String triggerName, String triggerGroup);
-
- //删除job
- //@param jobName
- //@param jobGroup
-
- void deleteJob(String jobName, String jobGroup);
-}
diff --git a/src/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java b/src/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java
deleted file mode 100644
index d8152e2..0000000
--- a/src/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package com.yf.exam.ability.job.service.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.toolkit.IdWorker;
-import com.yf.exam.ability.job.enums.JobGroup;
-import com.yf.exam.ability.job.service.JobService;
-import lombok.extern.log4j.Log4j2;
-import org.quartz.*;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.quartz.SchedulerFactoryBean;
-import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
-
-/**
- * Quartz定时任务服务实现类
- * @author bool
- */
-@Log4j2
-@Service
-public class JobServiceImpl implements JobService {
-
-
- //Quartz调度器,用于管理定时任务
-
- private Scheduler scheduler;
-
-
- //构造函数,注入SchedulerFactoryBean并初始化scheduler
- //@param schedulerFactoryBean Spring的SchedulerFactoryBean实例
-
- public JobServiceImpl(@Autowired SchedulerFactoryBean schedulerFactoryBean) {
- scheduler = schedulerFactoryBean.getScheduler();
- }
-
-
- //添加一个新的定时任务
- //@param jobClass 定时任务的类
- //@param jobName 定时任务的名称
- //@param cron 定时任务的cron表达式
- //@param data 传递给任务的数据
-
- @Override
- public void addCronJob(Class jobClass, String jobName, String cron, String data) {
- String jobGroup = JobGroup.SYSTEM;
- // 如果jobName为空,则自动生成一个唯一名称
- if(StringUtils.isEmpty(jobName)){
- jobName = jobClass.getSimpleName().toUpperCase() + "_"+IdWorker.getIdStr();
- }
- try {
- JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
- JobDetail jobDetail = scheduler.getJobDetail(jobKey);
- // 如果任务已经存在,则删除旧任务
- if (jobDetail != null) {
- log.info("++++++++++任务:{} 已存在", jobName);
- this.deleteJob(jobName, jobGroup);
- }
- log.info("++++++++++构建任务:{},{},{},{},{} ", jobClass.toString(), jobName, jobGroup, cron, data);
- // 构建新的JobDetail实例
- jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
- // 使用JobDataMap传递数据给任务
- jobDetail.getJobDataMap().put(TASK_DATA, data);
- // 构建Trigger实例
- Trigger trigger = null;
- // 如果cron表达式不为空,则使用cron表达式构建Trigger
- if(!StringUtils.isEmpty(cron)){
- log.info("+++++表达式执行:"+ JSON.toJSONString(jobDetail));
- CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
- trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
- } else {
- // 如果cron表达式为空,则立即执行任务
- log.info("+++++立即执行:"+ JSON.toJSONString(jobDetail));
- trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startNow().build();
- }
- // 将JobDetail和Trigger添加到调度器中
- scheduler.scheduleJob(jobDetail, trigger);
- } catch (Exception e) {
- // 打印异常堆栈信息
- e.printStackTrace();
- }
- }
-
-
- //添加一个新的立即执行的定时任务
- //@param jobClass 定时任务的类
- //@param jobName 定时任务的名称
- //@param data 传递给任务的数据
-
- @Override
- public void addCronJob(Class jobClass, String jobName, String data) {
- // 调用addCronJob方法,设置cron为null以立即执行任务
- this.addCronJob(jobClass, jobName, null, data);
- }
-
-
- //暂停指定的定时任务
- //@param jobName 定时任务的名称
- //@param jobGroup 定时任务的组名
-
- @Override
- public void pauseJob(String jobName, String jobGroup) {
- try {
- // 创建TriggerKey以标识要暂停的任务
- TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
- // 使用调度器暂停任务触发器
- scheduler.pauseTrigger(triggerKey);
- // 记录日志,表示任务已成功暂停
- log.info("++++++++++暂停任务:{}", jobName);
- } catch (SchedulerException e) {
- // 打印调度异常堆栈信息
- e.printStackTrace();
- }
- }
-
-
- //恢复指定的暂停定时任务
- //@param jobName 定时任务的名称
- //@param jobGroup 定时任务的组名
-
- @Override
- public void resumeJob(String jobName, String jobGroup) {
- try {
- // 创建TriggerKey以标识要恢复的任务
- TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
- // 使用调度器恢复任务触发器
- scheduler.resumeTrigger(triggerKey);
- // 记录日志,表示任务已成功恢复
- log.info("++++++++++重启任务:{}", jobName);
- } catch (SchedulerException e) {
- // 打印调度异常堆栈信息
- e.printStackTrace();
- }
- }
-
-
- //删除指定的定时任务
- //@param jobName 定时任务的名称
- //@param jobGroup 定时任务的组名
-
- @Override
- public void deleteJob(String jobName, String jobGroup) {
- try {
- // 创建JobKey以标识要删除的任务
- JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
- // 使用调度器删除任务
- scheduler.deleteJob(jobKey);
- // 记录日志,表示任务已成功删除
- log.info("++++++++++删除任务:{}", jobKey);
- } catch (SchedulerException e) {
- // 打印调度异常堆栈信息
- e.printStackTrace();
- }
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java b/src/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java
deleted file mode 100644
index 0c9b7a3..0000000
--- a/src/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.yf.exam.ability.shiro;
-
-import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
-import org.apache.shiro.web.filter.InvalidRequestFilter;
-import org.apache.shiro.web.filter.mgt.DefaultFilter;
-import org.apache.shiro.web.filter.mgt.FilterChainManager;
-import javax.servlet.Filter;
-import java.util.Map;
-
-
- //自定义过滤器工厂类,用于处理包含中文字符的URL问题
- //当URL中包含中文字符时,Shiro默认会返回400错误,这是因为InvalidRequestFilter会阻止非ASCII字符的请求。
- //该自定义过滤器重写了createFilterChainManager方法,修改了InvalidRequestFilter的配置,允许非ASCII字符的请求。
- //例如:https://youdomain.com/upload/file/云帆考试系统用户手册.pdf
- //@author van
-
-public class CNFilterFactoryBean extends ShiroFilterFactoryBean {
-
- //重写createFilterChainManager方法,以允许包含中文字符的URL请求
- //@return 自定义的FilterChainManager
-
- @Override
- protected FilterChainManager createFilterChainManager() {
- // 调用父类方法创建FilterChainManager
- FilterChainManager manager = super.createFilterChainManager();
- // 获取所有过滤器
- Map filterMap = manager.getFilters();
- // 获取InvalidRequestFilter
- Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name());
- // 检查invalidRequestFilter是否为InvalidRequestFilter实例
- if (invalidRequestFilter instanceof InvalidRequestFilter) {
- // 设置InvalidRequestFilter允许非ASCII字符的请求
- ((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);
- }
- // 返回自定义的FilterChainManager
- return manager;
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/shiro/ShiroRealm.java b/src/main/java/com/yf/exam/ability/shiro/ShiroRealm.java
deleted file mode 100644
index df91d62..0000000
--- a/src/main/java/com/yf/exam/ability/shiro/ShiroRealm.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package com.yf.exam.ability.shiro;
-
-
-import com.yf.exam.ability.shiro.jwt.JwtToken;
-import com.yf.exam.ability.shiro.jwt.JwtUtils;
-import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
-import com.yf.exam.modules.sys.user.service.SysUserRoleService;
-import com.yf.exam.modules.sys.user.service.SysUserService;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.SimpleAuthenticationInfo;
-import org.apache.shiro.authz.AuthorizationInfo;
-import org.apache.shiro.authz.SimpleAuthorizationInfo;
-import org.apache.shiro.realm.AuthorizingRealm;
-import org.apache.shiro.subject.PrincipalCollection;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
-import java.util.HashSet;
-import java.util.List;
-
-
- //用户登录鉴权和获取用户授权
- //@author bool
-
-@Component
-@Slf4j
-public class ShiroRealm extends AuthorizingRealm {
- @Autowired
- @Lazy
- private SysUserService sysUserService; // 注入用户服务,用于处理用户信息相关操作
-
- @Autowired
- @Lazy
- private SysUserRoleService sysUserRoleService; // 注入用户角色服务,用于处理用户角色相关操作
-
-
- //判断该Realm是否支持指定的AuthenticationToken。
- //仅支持JwtToken类型的token。
-
- //@param token 需要验证的AuthenticationToken对象。
- //@return 如果支持该token类型则返回true,否则返回false。
-
- @Override
- public boolean supports(AuthenticationToken token) {
- return token instanceof JwtToken;
- }
-
-
- //获取指定用户主体的授权信息。
- //该方法从用户主体中获取用户ID,然后查询与该用户关联的角色信息,并返回授权信息。
-
- //@param principals 用户主体集合,包含了用户的权限信息。
- //@return 包含用户角色信息的AuthorizationInfo对象。
-
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- String userId = null;
- if (principals != null) {
- SysUserLoginDTO user = (SysUserLoginDTO) principals.getPrimaryPrincipal();
- userId = user.getId();
- }
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- // 查找用户角色
- List roles = sysUserRoleService.listRoles(userId);
- info.setRoles(new HashSet<>(roles));
- log.info("++++++++++校验详细权限完成");
- return info;
- }
-
-
- //校验用户的账号密码是否正确。
- //该方法通过获取token并调用checkToken方法验证token的有效性,然后返回认证信息。
-
- //@param auth 包含用户认证信息的AuthenticationToken对象。
- //@return 包含认证信息的AuthenticationInfo对象。
- //@throws AuthenticationException 如果认证失败则抛出此异常。
-
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
- String token = (String) auth.getCredentials();
- if (token == null) {
- throw new AuthenticationException("token为空!");
- }
- // 校验token有效性
- SysUserLoginDTO user = this.checkToken(token);
- return new SimpleAuthenticationInfo(user, token, getName());
- }
-
-
- //校验Token的有效性。
- //该方法从token中提取用户名,并验证token是否有效。如果无效则抛出异常。
-
- //@param token 需要验证的token字符串。
- //@return 包含用户信息的SysUserLoginDTO对象。
- //@throws AuthenticationException 如果token无效或已过期则抛出此异常。
-
- public SysUserLoginDTO checkToken(String token) throws AuthenticationException {
- // 查询用户信息
- log.debug("++++++++++校验用户token: " + token);
- // 从token中获取用户名
- String username = JwtUtils.getUsername(token);
- log.debug("++++++++++用户名: " + username);
- if (username == null) {
- throw new AuthenticationException("无效的token");
- }
- // 查找登录用户对象
- SysUserLoginDTO user = sysUserService.token(token);
- // 校验token是否失效
- if (!JwtUtils.verify(token, username)) {
- throw new AuthenticationException("登陆失效,请重试登陆!");
- }
- return user;
- }
-
-
- //清除指定用户主体的权限认证缓存。
- //该方法调用父类的clearCache方法来实现缓存清除功能。
-
- //@param principals 用户主体集合,包含了用户的权限信息。
-
- @Override
- public void clearCache(PrincipalCollection principals) {
- super.clearCache(principals);
- }
-}
-
diff --git a/src/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java b/src/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java
deleted file mode 100644
index ba02f75..0000000
--- a/src/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.yf.exam.ability.shiro.aop;
-
-import com.yf.exam.ability.shiro.jwt.JwtToken;
-import com.yf.exam.aspect.utils.InjectUtils;
-import com.yf.exam.modules.Constant;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
- //鉴权登录拦截器,用于处理JWT(JSON Web Token)认证
- //@author bool
-
-@Slf4j
-public class JwtFilter extends BasicHttpAuthenticationFilter {
-
-
- //检查用户是否被允许访问资源
- //@param request 包含用户请求信息的ServletRequest对象
- //@param response 包含用户响应信息的ServletResponse对象
- // @param mappedValue 在Shiro配置中映射的值,通常用于权限检查
- //@return 如果用户成功登录并被允许访问资源,则返回true;否则返回false
-
- @Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
- try {
- // 调用executeLogin方法执行登录认证
- executeLogin(request, response);
- // 如果登录成功,返回true表示用户被允许访问
- return true;
- } catch (Exception e) {
- // 如果发生异常,调用InjectUtils.restError方法写出统一错误信息
- InjectUtils.restError((HttpServletResponse) response);
- // 返回false表示用户未被允许访问
- return false;
- }
- }
-
-
- //执行JWT登录认证
- //@param request 包含用户请求信息的ServletRequest对象
- //@param response 包含用户响应信息的ServletResponse对象
- // @return 如果登录成功,则返回true;否则抛出异常
- //@throws Exception 如果登录过程中发生异常
-
- @Override
- protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
- HttpServletRequest httpServletRequest = (HttpServletRequest) request;
- // 从请求头中获取JWT token
- String token = httpServletRequest.getHeader(Constant.TOKEN);
- // 创建JwtToken对象
- JwtToken jwtToken = new JwtToken(token);
- try {
- // 提交给realm进行登录认证,如果认证失败会抛出异常
- getSubject(request, response).login(jwtToken);
- // 如果没有抛出异常则代表登录成功,返回true
- return true;
- } catch (Exception e) {
- // 如果发生异常,记录日志
- log.error("JWT登录认证失败: ", e);
- // 抛出异常以便在调用处捕获并处理
- throw e;
- }
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java b/src/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java
deleted file mode 100644
index 969b715..0000000
--- a/src/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.yf.exam.ability.shiro.jwt;
-
-import lombok.Data;
-import org.apache.shiro.authc.AuthenticationToken;
-
-/**
- * JWT(JSON Web Token)认证令牌,用于Shiro认证过程
- * @author bool
- */
-@Data
-public class JwtToken implements AuthenticationToken {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * JWT的字符token,用于认证用户身份
- */
- private String token;
-
- /**
- * 构造函数,初始化JWT token
- * @param token JWT的字符token
- */
- public JwtToken(String token) {
- this.token = token;
- }
-
- /**
- * 获取认证主体,这里返回JWT token
- * @return JWT token
- */
- @Override
- public Object getPrincipal() {
- return token;
- }
-
- /**
- * 获取认证凭证,这里返回JWT token
- * @return JWT token
- */
- @Override
- public Object getCredentials() {
- return token;
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java b/src/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java
deleted file mode 100644
index 1ca221f..0000000
--- a/src/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.yf.exam.ability.shiro.jwt;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.auth0.jwt.exceptions.JWTDecodeException;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import com.yf.exam.core.utils.file.Md5Util;
-import java.util.Calendar;
-import java.util.Date;
-
-/**
- * JWT工具类,用于处理JWT(JSON Web Token)的生成、验证和解析
- * @author bool
- */
-public class JwtUtils {
- /**
- * 有效期24小时(以毫秒为单位)
- */
- private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000;
-
- /**
- * 校验JWT token是否正确
- * @param token JWT token字符串
- * @param username 用户名
- * @return 如果token有效且用户名匹配,则返回true;否则返回false
- */
- public static boolean verify(String token, String username) {
- try {
- // 根据用户名生成HMAC256算法的密钥
- Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username));
- // 创建JWT验证器,并指定需要验证的用户名声明
- JWTVerifier verifier = JWT.require(algorithm)
- .withClaim("username", username)
- .build();
- // 验证token
- verifier.verify(token);
- return true;
- } catch (Exception exception) {
- return false;
- }
- }
-
- /**
- * 从JWT token中解密并获取用户名
- * @param token JWT token字符串
- * @return 如果解析成功,则返回用户名;否则返回null
- */
- public static String getUsername(String token) {
- try {
- // 解码JWT token
- DecodedJWT jwt = JWT.decode(token);
- // 获取用户名声明
- return jwt.getClaim("username").asString();
- } catch (JWTDecodeException e) {
- return null;
- }
- }
-
- /**
- * 生成JWT token字符串
- * @param username 用户名
- * @return 生成的JWT token字符串
- */
- public static String sign(String username) {
- // 计算token的过期时间
- Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
- // 根据用户名生成HMAC256算法的密钥
- Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username));
- // 创建JWT并附带用户名信息及过期时间
- return JWT.create()
- .withClaim("username", username)
- .withExpiresAt(date)
- .sign(algorithm);
- }
-
- /**
- * 根据用户名生成一个新的密钥,用于增强JWT的安全性
- * @param userName 用户名
- * @return 生成的密钥
- */
- private static String encryptSecret(String userName) {
- // 获取当前时间
- Calendar cl = Calendar.getInstance();
- cl.setTimeInMillis(System.currentTimeMillis());
- // 创建一个简单的加密串,包含用户名和当前月份
- StringBuffer sb = new StringBuffer(userName)
- .append("&")
- .append(cl.get(Calendar.MONTH));
- // 对加密串进行MD5哈希
- String secret = Md5Util.md5(sb.toString());
- // 再次对用户名和上一步的MD5哈希结果进行MD5哈希,生成最终的密钥
- return Md5Util.md5(userName + "&" + secret);
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/config/UploadConfig.java b/src/main/java/com/yf/exam/ability/upload/config/UploadConfig.java
deleted file mode 100644
index 00c8207..0000000
--- a/src/main/java/com/yf/exam/ability/upload/config/UploadConfig.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.yf.exam.ability.upload.config;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-
- //文件上传配置类,用于定义文件上传的相关配置属性。
- //这些属性可以通过application.properties或application.yml文件进行配置。
-
- //@author van
-
-@Data
-@Configuration
-@ConfigurationProperties(prefix = "conf.upload")
-public class UploadConfig {
-
- //文件上传的访问路径,用户可以通过该路径访问上传的文件。
- //例如:http://example.com/upload/
-
- private String url;
-
-
- //文件上传的物理目录路径,指定文件在服务器上的存储位置。
- //例如:/var/www/upload/
-
- private String dir;
-
-
- //允许上传的文件后缀名数组,定义了哪些类型的文件可以被上传。
- //例如:{"jpg", "png", "pdf"}
-
- private String[] allowExtensions;
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/controller/UploadController.java b/src/main/java/com/yf/exam/ability/upload/controller/UploadController.java
deleted file mode 100644
index a54e6fa..0000000
--- a/src/main/java/com/yf/exam/ability/upload/controller/UploadController.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.yf.exam.ability.upload.controller;
-
-import com.yf.exam.ability.Constant;
-import com.yf.exam.ability.upload.dto.UploadReqDTO;
-import com.yf.exam.ability.upload.dto.UploadRespDTO;
-import com.yf.exam.ability.upload.service.UploadService;
-import com.yf.exam.core.api.ApiRest;
-import com.yf.exam.core.api.controller.BaseController;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RestController;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
- //本地文件上传下载请求类
- //该类提供文件上传和下载的功能,继承自BaseController。
- //@author bool
-
-@Log4j2
-@Api(tags = {"文件上传"})
-@RestController
-public class UploadController extends BaseController {
-
- @Autowired
- private UploadService uploadService;
-
-
- //文件上传
- //该方法处理文件上传请求,参数通过表单方式提交。
- //@param reqDTO 包含上传文件信息的请求DTO
- //@return 包含上传文件结果的响应DTO
-
- @PostMapping("/common/api/file/upload")
- @ApiOperation(value = "文件上传", notes = "此接口较为特殊,参数都通过表单方式提交,而非JSON")
- public ApiRest upload(@ModelAttribute UploadReqDTO reqDTO) {
- // 上传并返回URL
- UploadRespDTO respDTO = uploadService.upload(reqDTO);
- return super.success(respDTO);
- }
-
-
- //独立文件下载
- //该方法处理文件下载请求。
- //@param request HTTP请求对象
- //@param response HTTP响应对象
-
- @GetMapping(Constant.FILE_PREFIX + "**")
- @ApiOperation(value = "文件下载", notes = "文件下载")
- public void download(HttpServletRequest request, HttpServletResponse response) {
- uploadService.download(request, response);
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java b/src/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java
deleted file mode 100644
index 1879fcb..0000000
--- a/src/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.yf.exam.ability.upload.dto;
-
-import com.yf.exam.core.api.dto.BaseDTO;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import org.springframework.web.multipart.MultipartFile;
-
-
- //文件上传请求类。
- //该类用于封装文件上传请求的参数,继承自BaseDTO。
-
- // @author bool
- // @date 2019-12-26 17:54
-
-@Data
-@ApiModel(value = "文件上传参数", description = "包含上传文件信息的请求参数")
-public class UploadReqDTO extends BaseDTO {
-
- //上传文件的内容。
- //该字段是必填项,包含了用户需要上传的文件。
- //@ApiModelProperty(value = "上传文件内容", required = true)
-
- private MultipartFile file;
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java b/src/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java
deleted file mode 100644
index 6aa5903..0000000
--- a/src/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.yf.exam.ability.upload.dto;
-
-import com.yf.exam.core.api.dto.BaseDTO;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-
- //文件上传响应类。
- //该类用于封装文件上传后的响应信息,继承自BaseDTO。
-
- // @author bool
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-@ApiModel(value = "文件上传响应", description = "包含上传文件后的URL地址的响应信息")
-public class UploadRespDTO extends BaseDTO {
-
- //上传后的完整的URL地址。
- //该字段是必填项,包含了上传文件后可以访问的URL。
- //@ApiModelProperty(value = "上传后的完整的URL地址", required = true)
-
- private String url;
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/service/UploadService.java b/src/main/java/com/yf/exam/ability/upload/service/UploadService.java
deleted file mode 100644
index 3a89174..0000000
--- a/src/main/java/com/yf/exam/ability/upload/service/UploadService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.yf.exam.ability.upload.service;
-
-import com.yf.exam.ability.upload.dto.UploadReqDTO;
-import com.yf.exam.ability.upload.dto.UploadRespDTO;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
- //阿里云OSS业务类
- //该接口定义了文件上传和下载的功能,具体实现由其实现类提供。
- //@author bool
- //@date 2019-07-12 16:45
-
-public interface UploadService {
-
-
- //处理文件上传请求
- //@param reqDTO 文件上传请求对象,包含上传的文件信息
- //@return 文件上传响应对象,包含上传文件后的URL地址
-
- UploadRespDTO upload(UploadReqDTO reqDTO);
-
-
- //处理文件下载请求
- //@param request HTTP请求对象,包含下载文件的URI信息
- //@param response HTTP响应对象,用于返回下载的文件内容
- //@throws RuntimeException 如果下载过程中发生错误
-
- void download(HttpServletRequest request, HttpServletResponse response);
-
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java b/src/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java
deleted file mode 100644
index 6a750ef..0000000
--- a/src/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.yf.exam.ability.upload.service.impl;
-
-import com.yf.exam.ability.Constant;
-import com.yf.exam.ability.upload.config.UploadConfig;
-import com.yf.exam.ability.upload.dto.UploadReqDTO;
-import com.yf.exam.ability.upload.dto.UploadRespDTO;
-import com.yf.exam.ability.upload.service.UploadService;
-import com.yf.exam.ability.upload.utils.FileUtils;
-import com.yf.exam.core.exception.ServiceException;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.io.FilenameUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.util.FileCopyUtils;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-
- //文件上传业务类。
- //该类实现了文件上传和下载的功能,继承自UploadService接口。
-
- //@author bool
- //@date 2019-07-30 21:02
-
-@Log4j2
-@Service
-public class UploadServiceImpl implements UploadService {
-
- @Autowired
- private UploadConfig conf;
-
-
- //处理文件上传请求。
-
- // @param reqDTO 文件上传请求对象,包含上传的文件信息。
- //@return 文件上传响应对象,包含上传文件后的URL地址。
- //@throws ServiceException 如果文件类型不允许上传或上传过程中发生IO异常。
-
- @Override
- public UploadRespDTO upload(UploadReqDTO reqDTO) {
- // 获取上传文件的内容
- MultipartFile file = reqDTO.getFile();
- // 验证文件后缀是否在允许的范围内
- boolean allow = FilenameUtils.isExtension(file.getOriginalFilename(), conf.getAllowExtensions());
- if (!allow) {
- throw new ServiceException("文件类型不允许上传!");
- }
- // 获取上传文件夹的路径
- String fileDir = conf.getDir();
- // 构造真实物理地址
- String fullPath;
- try {
- // 处理文件路径
- String filePath = FileUtils.processPath(file);
- // 构造文件保存地址
- fullPath = fileDir + filePath;
- // 创建文件夹(如果不存在)
- FileUtils.checkDir(fullPath);
- // 上传文件到指定路径
- FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(fullPath));
- // 生成并返回上传结果
- return this.generateResult(filePath);
- } catch (IOException e) {
- e.printStackTrace();
- throw new ServiceException("文件上传失败:" + e.getMessage());
- }
- }
-
-
- //处理文件下载请求。
-
- //@param request HTTP请求对象,包含请求的URI。
- //@param response HTTP响应对象,用于返回文件内容。
- //@throws RuntimeException 如果URL解码过程中发生UnsupportedEncodingException。
-
- @Override
- public void download(HttpServletRequest request, HttpServletResponse response) {
- // 获取真实的文件路径
- String filePath = this.getRealPath(request.getRequestURI());
- // 处理中文文件名解码问题
- try {
- filePath = URLDecoder.decode(filePath, "utf-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e);
- }
- // 输出完整路径到控制台(调试使用)
- System.out.println("++++完整路径为:" + filePath);
- try {
- // 将文件内容写入HTTP响应
- FileUtils.writeRange(request, response, filePath);
- } catch (IOException e) {
- // 如果文件不存在,设置HTTP响应状态为404
- response.setStatus(404);
- // 记录错误日志
- log.error("预览文件失败:" + e.getMessage());
- }
- }
-
-
- //构造文件上传响应结果。
-
- //@param fileName 上传文件的路径。
- //@return 文件上传响应对象,包含上传文件后的完整URL地址。
-
- private UploadRespDTO generateResult(String fileName) {
- // 获取加速域名
- String domain = conf.getUrl();
- // 构造并返回文件上传响应对象
- return new UploadRespDTO(domain + fileName);
- }
-
-
- //获取真实物理文件地址。
- //@param uri 请求的URI,包含文件的相对路径。
- //@return 真实物理文件的完整路径。
-
- public String getRealPath(String uri) {
- // 定义正则表达式,匹配文件路径
- String regx = Constant.FILE_PREFIX + "(.*)";
- // 查找匹配的文件路径
- Pattern pattern = Pattern.compile(regx);
- Matcher m = pattern.matcher(uri);
- if (m.find()) {
- // 获取匹配的文件路径部分
- String str = m.group(1);
- // 构造真实物理文件的完整路径
- return conf.getDir() + str;
- }
- // 如果未找到匹配路径,返回null
- return null;
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/utils/FileUtils.java b/src/main/java/com/yf/exam/ability/upload/utils/FileUtils.java
deleted file mode 100644
index dd92e55..0000000
--- a/src/main/java/com/yf/exam/ability/upload/utils/FileUtils.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package com.yf.exam.ability.upload.utils;
-
-import com.baomidou.mybatisplus.core.toolkit.IdWorker;
-import com.yf.exam.core.utils.DateUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.springframework.web.multipart.MultipartFile;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.Date;
-
-
- //文件工具类
- //该类提供了文件上传、下载和处理的相关工具方法。
- //@author bool
-
-public class FileUtils {
-
-
- //后缀分割符号
-
- private static final String SUFFIX_SPLIT = ".";
-
-
- //持以断点的方式输出文件,提供文件在线预览和视频在线播放
- //@param request HTTP请求对象,包含请求头信息如Range
- //@param response HTTP响应对象,用于返回文件内容
- //@param filePath 文件的物理路径
- //@throws IOException 如果文件读取或写入过程中发生IO异常
-
- public static void writeRange(HttpServletRequest request,
- HttpServletResponse response, String filePath) throws IOException {
- // 读取文件
- File file = new File(filePath);
- // 只读模式
- RandomAccessFile randomFile = new RandomAccessFile(file, "r");
- long contentLength = randomFile.length();
- String range = request.getHeader("Range");
- int start = 0, end = 0;
-
- if (range != null && range.startsWith("bytes=")) {
- String[] values = range.split("=")[1].split("-");
- start = Integer.parseInt(values[0]);
- if (values.length > 1) {
- end = Integer.parseInt(values[1]);
- }
- }
-
- int requestSize;
- if (end != 0 && end > start) {
- requestSize = end - start + 1;
- } else {
- requestSize = Integer.MAX_VALUE;
- }
-
- byte[] buffer = new byte[128];
- response.setContentType(MediaUtils.getContentType(filePath));
- response.setHeader("Accept-Ranges", "bytes");
- response.setHeader("ETag", file.getName());
- response.setHeader("Last-Modified", new Date().toString());
-
- // 第一次请求只返回content length来让客户端请求多次实际数据
- if (range == null) {
- response.setHeader("Content-length", contentLength + "");
- } else {
- // 以后的多次以断点续传的方式来返回视频数据
- response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
- long requestStart = 0, requestEnd = 0;
- String[] ranges = range.split("=");
- if (ranges.length > 1) {
- String[] rangeData = ranges[1].split("-");
- requestStart = Integer.parseInt(rangeData[0]);
- if (rangeData.length > 1) {
- requestEnd = Integer.parseInt(rangeData[1]);
- }
- }
-
- long length;
- if (requestEnd > 0) {
- length = requestEnd - requestStart + 1;
- response.setHeader("Content-length", "" + length);
- response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength);
- } else {
- length = contentLength - requestStart;
- response.setHeader("Content-length", "" + length);
- response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1) + "/" + contentLength);
- }
- }
-
- ServletOutputStream out = response.getOutputStream();
- int needSize = requestSize;
- randomFile.seek(start);
-
- while (needSize > 0) {
- int len = randomFile.read(buffer);
- if (needSize < buffer.length) {
- out.write(buffer, 0, needSize);
- } else {
- out.write(buffer, 0, len);
- if (len < buffer.length) {
- break;
- }
- }
- needSize -= len;
- }
-
- randomFile.close();
- out.close();
- }
-
-
- //重命名文件
- //@param fileName 原始文件名
- //@return 重命名后的文件名
-
- public static String renameFile(String fileName) {
- // 没有后缀名不处理
- if (!fileName.contains(SUFFIX_SPLIT)) {
- return fileName;
- }
- // 文件后缀
- String extension = FilenameUtils.getExtension(fileName);
- // 以系统时间命名
- return IdWorker.getIdStr() + "." + extension;
- }
-
- //处理新的文件路径,为上传文件预设目录,如:2021/01/01/xxx.jpg
- //注意:前面没有斜杠
- //@param file 文件对象
- //@return 处理后的文件路径
-
- public static String processPath(MultipartFile file) {
- // 获取原始文件名
- String fileName = file.getOriginalFilename();
- // 需要重命名
- fileName = renameFile(fileName);
- // 获得上传的文件夹
- String dir = DateUtils.formatDate(new Date(), "yyyy/MM/dd/");
- return new StringBuffer(dir).append(fileName).toString();
- }
-
-
- //检查文件夹是否存在,不存在则创建
- //@param fileName 文件路径,包含文件夹信息
-
- public static void checkDir(String fileName) {
- int index = fileName.lastIndexOf("/");
- if (index == -1) {
- return;
- }
- File file = new File(fileName.substring(0, index));
- if (!file.exists()) {
- file.mkdirs();
- }
- }
-}
diff --git a/src/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java b/src/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java
deleted file mode 100644
index e1ff58f..0000000
--- a/src/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.yf.exam.ability.upload.utils;
-
-import org.apache.commons.lang3.StringUtils;
-import java.util.HashMap;
-import java.util.Map;
-
-
- //媒体工具类,用于判断文件的媒体类型
- //该类包含一个媒体类型映射表,并提供获取文件内容类型的方法。
- //@author bool
- //@date 2019-11-14 16:21
-
-public class MediaUtils {
-
-
- //媒体类型映射表
- //该映射表存储了文件后缀与对应的MIME类型。
-
- public static final Map MEDIA_MAP = new HashMap() {
- {
- // PDF文件
- put(".pdf", "application/pdf");
- // MP4视频文件
- put(".mp4", "video/mp4");
- }
- };
-
-
- //获取文件的MIME类型
- //@param filePath 文件路径
- //@return 文件的MIME类型,如果文件路径无效或无法识别,则返回 "application/octet-stream"
-
- public static String getContentType(String filePath) {
- if (!StringUtils.isBlank(filePath) && filePath.indexOf(".") != -1) {
- // 后缀转换成小写
- String suffix = filePath.substring(filePath.lastIndexOf(".")).toLowerCase();
- if (MEDIA_MAP.containsKey(suffix)) {
- return MEDIA_MAP.get(suffix);
- }
- }
- return "application/octet-stream";
- }
-}
diff --git a/src/main/java/com/yf/exam/aspect/DictAspect.java b/src/main/java/com/yf/exam/aspect/DictAspect.java
deleted file mode 100644
index cc191e7..0000000
--- a/src/main/java/com/yf/exam/aspect/DictAspect.java
+++ /dev/null
@@ -1,315 +0,0 @@
-package com.yf.exam.aspect;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.yf.exam.core.annon.Dict;
-import com.yf.exam.core.api.ApiRest;
-import com.yf.exam.core.utils.Reflections;
-import com.yf.exam.modules.sys.system.service.SysDictService;
-import lombok.extern.slf4j.Slf4j;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-
- //数据字典AOP类,处理数据字典值
-
- //@author bool
-
-@Aspect
-@Component
-@Slf4j
-public class DictAspect {
-
- @Autowired
- private SysDictService sysDictService;
-
-
-// 切入Controller执行
-// @param pjp
-// @return
-// @throws Throwable
-//
- @Around("execution(public * com.yf.exam..*.*Controller.*(..))")
- public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
- return this.translate(pjp);
- }
-
-// *
-// * 进行翻译并返回,调用前必须实现:BaseDictService
-// *
-// * @param pjp
-// * @return
-// * @throws Throwable
- public Object translate(ProceedingJoinPoint pjp) throws Throwable {
- // 处理字典
- return this.parseAllDictText(pjp.proceed());
- }
-
-// *
-// * 转换全部数据字典
-// *
-// * @param result
-//
- private Object parseAllDictText(Object result) {
-
- // 非ApiRest类型不处理
- if (result instanceof ApiRest) {
- parseFullDictText(result);
- }
-
- return result;
- }
-
-
-// *
-// * 转换所有类型的数据字典、包含子列表
-// *
-// * @param result
- private void parseFullDictText(Object result) {
-
- try {
-
- Object rest = ((ApiRest) result).getData();
-
- // 不处理普通数据类型
- if (rest == null || this.isBaseType(rest.getClass())) {
- return;
- }
-
- // 分页的
- if (rest instanceof IPage) {
- List