parent
d87fd84338
commit
4b2c4b24ac
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 孤傲的小笼包
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
java -jar exam-api.jar --spring.config.location=application-local.yml
|
@ -0,0 +1 @@
|
|||||||
|
java -jar exam-api.jar --spring.config.location=application-local.yml
|
Binary file not shown.
@ -0,0 +1,215 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.yfhl</groupId>
|
||||||
|
<artifactId>exam-api</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>1.0</version>
|
||||||
|
<name>exam-api</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.1.4.RELEASE</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<fastjson.version>2.0.24</fastjson.version>
|
||||||
|
<oss.version>3.7.0</oss.version>
|
||||||
|
<aliyun.sdk.version>4.1.1</aliyun.sdk.version>
|
||||||
|
<swagger.version>2.9.2</swagger.version>
|
||||||
|
<dozer.version>5.5.1</dozer.version>
|
||||||
|
<apache.commons.version>3.8</apache.commons.version>
|
||||||
|
<mysql.driver.version>8.0.11</mysql.driver.version>
|
||||||
|
<mybatis-plus.version>3.4.1</mybatis-plus.version>
|
||||||
|
<lombok.version>1.18.4</lombok.version>
|
||||||
|
<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
|
||||||
|
<alicloud.version>2.1.1.RELEASE</alicloud.version>
|
||||||
|
<poi.version>3.9</poi.version>
|
||||||
|
<log4j2.version>2.17.2</log4j2.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- WEB支持 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--spring quartz依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjweaver</artifactId>
|
||||||
|
<version>1.9.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>${fastjson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.dozer</groupId>
|
||||||
|
<artifactId>dozer</artifactId>
|
||||||
|
<version>${dozer.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>commons-collections</groupId>
|
||||||
|
<artifactId>commons-collections</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>${mysql.driver.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dom4j</groupId>
|
||||||
|
<artifactId>dom4j</artifactId>
|
||||||
|
<version>2.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger2</artifactId>
|
||||||
|
<version>${swagger.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||||
|
<version>1.9.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- poi office -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml-schemas</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--JWT-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>java-jwt</artifactId>
|
||||||
|
<version>3.7.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Shiro -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.shiro</groupId>
|
||||||
|
<artifactId>shiro-spring-boot-starter</artifactId>
|
||||||
|
<version>1.8.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>1.2.6</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>${project.name}</finalName>
|
||||||
|
<defaultGoal>compile</defaultGoal>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.7.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.22.1</version>
|
||||||
|
<configuration>
|
||||||
|
<skipTests>true</skipTests>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.yf.exam;
|
||||||
|
|
||||||
|
import com.yf.exam.core.api.utils.JsonConverter;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 云帆在线考试系统
|
||||||
|
* @author bool
|
||||||
|
* @email 18365918@qq.com
|
||||||
|
* @date 2020-03-04 19:41
|
||||||
|
*/
|
||||||
|
@Log4j2
|
||||||
|
@SpringBootApplication
|
||||||
|
public class ExamApplication implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws UnknownHostException {
|
||||||
|
ConfigurableApplicationContext application = SpringApplication.run(ExamApplication.class, args);
|
||||||
|
Environment env = application.getEnvironment();
|
||||||
|
String ip = InetAddress.getLocalHost().getHostAddress();
|
||||||
|
String port = env.getProperty("server.port");
|
||||||
|
String path = env.getProperty("server.servlet.context-path");
|
||||||
|
|
||||||
|
// 未配置默认空白
|
||||||
|
if(path == null){
|
||||||
|
path = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log.info("\n----------------------------------------------------------\n\t" +
|
||||||
|
"云帆考试系统启动成功,访问路径如下:\n\t" +
|
||||||
|
"本地路径: \t\thttp://localhost:" + port + path + "/\n\t" +
|
||||||
|
"网络地址: \thttp://" + ip + ":" + port + path + "/\n\t" +
|
||||||
|
"API文档: \t\thttp://" + ip + ":" + port + path + "/doc.html\n" +
|
||||||
|
"----------------------------------------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||||
|
//保留原有converter,把新增fastConverter插入集合头,保证优先级
|
||||||
|
converters.add(0, JsonConverter.fastConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.yf.exam.ability;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用常量
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
public class Constant {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传路径
|
||||||
|
*/
|
||||||
|
public static final String FILE_PREFIX = "/upload/file/";
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.yf.exam.ability.job.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务分组
|
||||||
|
* @author van
|
||||||
|
*/
|
||||||
|
public interface JobGroup {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统任务
|
||||||
|
*/
|
||||||
|
String SYSTEM = "system";
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.yf.exam.ability.job.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务前缀
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
public interface JobPrefix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制交卷的
|
||||||
|
*/
|
||||||
|
String BREAK_EXAM = "break_exam_";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
@Log4j2
|
||||||
|
@Service
|
||||||
|
public class JobServiceImpl implements JobService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quartz定时任务核心的功能实现类
|
||||||
|
*/
|
||||||
|
private Scheduler scheduler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入
|
||||||
|
* @param schedulerFactoryBean
|
||||||
|
*/
|
||||||
|
public JobServiceImpl(@Autowired SchedulerFactoryBean schedulerFactoryBean) {
|
||||||
|
scheduler = schedulerFactoryBean.getScheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCronJob(Class jobClass, String jobName, String cron, String data) {
|
||||||
|
|
||||||
|
|
||||||
|
String jobGroup = JobGroup.SYSTEM;
|
||||||
|
|
||||||
|
// 自动命名
|
||||||
|
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);
|
||||||
|
|
||||||
|
//构建job信息
|
||||||
|
jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
|
||||||
|
//用JopDataMap来传递数据
|
||||||
|
jobDetail.getJobDataMap().put(TASK_DATA, data);
|
||||||
|
|
||||||
|
//按新的cronExpression表达式构建一个新的trigger
|
||||||
|
Trigger trigger = null;
|
||||||
|
|
||||||
|
// 有表达式的按表达式
|
||||||
|
if(!StringUtils.isEmpty(cron)){
|
||||||
|
log.info("+++++表达式执行:"+ JSON.toJSONString(jobDetail));
|
||||||
|
//表达式调度构建器
|
||||||
|
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
|
||||||
|
trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
|
||||||
|
}else{
|
||||||
|
// 无表达式则立即执行
|
||||||
|
log.info("+++++立即执行:"+ JSON.toJSONString(jobDetail));
|
||||||
|
trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startNow().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler.scheduleJob(jobDetail, trigger);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCronJob(Class jobClass, String jobName, String data) {
|
||||||
|
// 立即执行任务
|
||||||
|
this.addCronJob(jobClass, jobName, null, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pauseJob(String jobName, String jobGroup) {
|
||||||
|
try {
|
||||||
|
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
|
||||||
|
scheduler.pauseTrigger(triggerKey);
|
||||||
|
log.info("++++++++++暂停任务:{}", jobName);
|
||||||
|
} catch (SchedulerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resumeJob(String jobName, String jobGroup) {
|
||||||
|
try {
|
||||||
|
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
|
||||||
|
scheduler.resumeTrigger(triggerKey);
|
||||||
|
log.info("++++++++++重启任务:{}", jobName);
|
||||||
|
} catch (SchedulerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteJob(String jobName, String jobGroup) {
|
||||||
|
try {
|
||||||
|
JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
|
||||||
|
scheduler.deleteJob(jobKey);
|
||||||
|
log.info("++++++++++删除任务:{}", jobKey);
|
||||||
|
} catch (SchedulerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.yf.exam.ability.shiro.jwt;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.shiro.authc.AuthenticationToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class JwtToken implements AuthenticationToken {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT的字符token
|
||||||
|
*/
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
|
||||||
|
public JwtToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yf.exam.ability.upload.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传配置
|
||||||
|
* @author van
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "conf.upload")
|
||||||
|
public class UploadConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问路径
|
||||||
|
*/
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理目录
|
||||||
|
*/
|
||||||
|
private String dir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许的后缀
|
||||||
|
*/
|
||||||
|
private String [] allowExtensions;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传请求类
|
||||||
|
* @author
|
||||||
|
* @date 2019-12-26 17:54
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="文件上传参数", description="文件上传参数")
|
||||||
|
public class UploadReqDTO extends BaseDTO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "上传文件内容", required=true)
|
||||||
|
private MultipartFile file;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件结果
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@ApiModel(value="文件上传响应", description="文件上传响应")
|
||||||
|
public class UploadRespDTO extends BaseDTO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "上传后的完整的URL地址", required=true)
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
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
|
||||||
|
*/
|
||||||
|
UploadRespDTO upload(UploadReqDTO reqDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
*/
|
||||||
|
void download(HttpServletRequest request, HttpServletResponse response);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package com.yf.exam.ability.upload.service.impl;
|
||||||
|
|
||||||
|
import com.yf.exam.ability.Constant;
|
||||||
|
import com.yf.exam.ability.upload.config.UploadConfig;
|
||||||
|
import com.yf.exam.ability.upload.dto.UploadReqDTO;
|
||||||
|
import com.yf.exam.ability.upload.dto.UploadRespDTO;
|
||||||
|
import com.yf.exam.ability.upload.service.UploadService;
|
||||||
|
import com.yf.exam.ability.upload.utils.FileUtils;
|
||||||
|
import com.yf.exam.core.exception.ServiceException;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传业务类
|
||||||
|
* @author bool
|
||||||
|
* @date 2019-07-30 21:02
|
||||||
|
*/
|
||||||
|
@Log4j2
|
||||||
|
@Service
|
||||||
|
public class UploadServiceImpl implements UploadService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UploadConfig conf;
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
FileUtils.writeRange(request, response, filePath);
|
||||||
|
} catch (IOException e) {
|
||||||
|
response.setStatus(404);
|
||||||
|
log.error("预览文件失败" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造返回
|
||||||
|
* @param fileName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private UploadRespDTO generateResult(String fileName) {
|
||||||
|
|
||||||
|
//获取加速域名
|
||||||
|
String domain = conf.getUrl();
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return new UploadRespDTO(domain + fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取真实物理文件地址
|
||||||
|
* @param 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.yf.exam.aspect.mybatis;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.ibatis.executor.Executor;
|
||||||
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
|
import org.apache.ibatis.mapping.SqlCommandType;
|
||||||
|
import org.apache.ibatis.plugin.Interceptor;
|
||||||
|
import org.apache.ibatis.plugin.Intercepts;
|
||||||
|
import org.apache.ibatis.plugin.Invocation;
|
||||||
|
import org.apache.ibatis.plugin.Plugin;
|
||||||
|
import org.apache.ibatis.plugin.Signature;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动给创建时间个更新时间加值
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
@Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
|
||||||
|
public class UpdateInterceptor extends AbstractSqlParserHandler implements Interceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private static final String CREATE_TIME = "createTime";
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private static final String UPDATE_TIME = "updateTime";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object intercept(Invocation invocation) throws Throwable {
|
||||||
|
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
|
||||||
|
// SQL操作命令
|
||||||
|
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
|
||||||
|
// 获取新增或修改的对象参数
|
||||||
|
Object parameter = invocation.getArgs()[1];
|
||||||
|
// 获取对象中所有的私有成员变量(对应表字段)
|
||||||
|
Field[] declaredFields = parameter.getClass().getDeclaredFields();
|
||||||
|
if (parameter.getClass().getSuperclass() != null) {
|
||||||
|
Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields();
|
||||||
|
declaredFields = ArrayUtils.addAll(declaredFields, superField);
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = null;
|
||||||
|
for (Field field : declaredFields) {
|
||||||
|
fieldName = field.getName();
|
||||||
|
if (Objects.equals(CREATE_TIME, fieldName)) {
|
||||||
|
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(parameter, new Timestamp(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Objects.equals(UPDATE_TIME, fieldName)) {
|
||||||
|
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(parameter, new Timestamp(System.currentTimeMillis()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return invocation.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object plugin(Object target) {
|
||||||
|
if (target instanceof Executor) {
|
||||||
|
return Plugin.wrap(target, this);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProperties(Properties properties) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package com.yf.exam.aspect.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.yf.exam.core.api.ApiError;
|
||||||
|
import com.yf.exam.core.api.ApiRest;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入工具类
|
||||||
|
* @author bool
|
||||||
|
* @date 2019-07-17 09:32
|
||||||
|
*/
|
||||||
|
@Log4j2
|
||||||
|
@Component
|
||||||
|
public class InjectUtils {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给对象字段赋值
|
||||||
|
*
|
||||||
|
* @param object 赋值的对象
|
||||||
|
* @param value 值
|
||||||
|
* @param fields 字段
|
||||||
|
* @throws Exception 异常
|
||||||
|
*/
|
||||||
|
public void setValue(Object object, Object value, String... fields) throws Exception {
|
||||||
|
|
||||||
|
//设置同类的属性
|
||||||
|
for (String fieldName : fields) {
|
||||||
|
|
||||||
|
//获取当前
|
||||||
|
Field field = this.getFiled(object.getClass(), fieldName);
|
||||||
|
if(field == null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(object, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段名对应的字段
|
||||||
|
*
|
||||||
|
* @param clazz 目标类
|
||||||
|
* @param fieldName 字段名
|
||||||
|
*/
|
||||||
|
private Field getFiled(Class clazz, String fieldName) {
|
||||||
|
|
||||||
|
System.out.println("注入的类:"+clazz.toString());
|
||||||
|
|
||||||
|
//是否具有包含关系
|
||||||
|
try {
|
||||||
|
//获取当前类的属性
|
||||||
|
return clazz.getDeclaredField(fieldName);
|
||||||
|
}catch (Exception e){
|
||||||
|
|
||||||
|
log.error(clazz.toString() + ": not exist field, try superclass " + fieldName);
|
||||||
|
|
||||||
|
//如果为空且存在父类,则往上找
|
||||||
|
if(clazz.getSuperclass()!=null){
|
||||||
|
return this.getFiled(clazz.getSuperclass(), fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印结果返回
|
||||||
|
* @param response
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void restError(HttpServletResponse response) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
//固定错误
|
||||||
|
ApiRest apiRest = new ApiRest(ApiError.ERROR_10010002);
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("application/json");
|
||||||
|
response.getWriter().write(JSON.toJSONString(apiRest));
|
||||||
|
response.getWriter().close();
|
||||||
|
|
||||||
|
}catch (IOException e){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.yf.exam.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.servlet.MultipartConfigFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.util.unit.DataSize;
|
||||||
|
|
||||||
|
import javax.servlet.MultipartConfigElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传配置
|
||||||
|
* @author bool
|
||||||
|
* @date 2019-07-29 16:23
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MultipartConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MultipartConfigElement multipartConfigElement() {
|
||||||
|
MultipartConfigFactory factory = new MultipartConfigFactory();
|
||||||
|
// 单个数据大小
|
||||||
|
factory.setMaxFileSize(DataSize.ofMegabytes(5000L));
|
||||||
|
/// 总上传数据大小
|
||||||
|
factory.setMaxRequestSize(DataSize.ofMegabytes(5000L));
|
||||||
|
return factory.createMultipartConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package com.yf.exam.config;
|
||||||
|
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务调度配置
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
@Log4j2
|
||||||
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
|
@EnableAsync
|
||||||
|
public class ScheduledConfig implements SchedulingConfigurer, AsyncConfigurer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务使用的线程池
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean(destroyMethod = "shutdown", name = "taskScheduler")
|
||||||
|
public ThreadPoolTaskScheduler taskScheduler(){
|
||||||
|
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||||
|
scheduler.setPoolSize(10);
|
||||||
|
scheduler.setThreadNamePrefix("task-");
|
||||||
|
scheduler.setAwaitTerminationSeconds(600);
|
||||||
|
scheduler.setWaitForTasksToCompleteOnShutdown(true);
|
||||||
|
return scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步任务执行线程池
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean(name = "asyncExecutor")
|
||||||
|
public ThreadPoolTaskExecutor asyncExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(10);
|
||||||
|
executor.setQueueCapacity(1000);
|
||||||
|
executor.setKeepAliveSeconds(600);
|
||||||
|
executor.setMaxPoolSize(20);
|
||||||
|
executor.setThreadNamePrefix("taskExecutor-");
|
||||||
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
|
||||||
|
ThreadPoolTaskScheduler taskScheduler = taskScheduler();
|
||||||
|
scheduledTaskRegistrar.setTaskScheduler(taskScheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Executor getAsyncExecutor() {
|
||||||
|
return asyncExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||||
|
return (throwable, method, objects) -> {
|
||||||
|
log.error("异步任务执行出现异常, message {}, emthod {}, params {}", throwable, method, objects);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.yf.exam.config;
|
||||||
|
|
||||||
|
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import springfox.documentation.builders.ApiInfoBuilder;
|
||||||
|
import springfox.documentation.builders.PathSelectors;
|
||||||
|
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||||
|
import springfox.documentation.service.ApiInfo;
|
||||||
|
import springfox.documentation.service.ApiKey;
|
||||||
|
import springfox.documentation.service.Contact;
|
||||||
|
import springfox.documentation.service.SecurityScheme;
|
||||||
|
import springfox.documentation.spi.DocumentationType;
|
||||||
|
import springfox.documentation.spring.web.plugins.Docket;
|
||||||
|
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swagger配置
|
||||||
|
* @author bool
|
||||||
|
* @date 2020/8/19 20:53
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableSwagger2
|
||||||
|
@EnableSwaggerBootstrapUI
|
||||||
|
@ConfigurationProperties(prefix = "swagger")
|
||||||
|
public class SwaggerConfig {
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Docket examApi() {
|
||||||
|
return new Docket(DocumentationType.SWAGGER_2)
|
||||||
|
.apiInfo(apiInfo())
|
||||||
|
.groupName("考试模块接口")
|
||||||
|
.select()
|
||||||
|
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
|
||||||
|
.paths(PathSelectors.ant("/exam/api/**"))
|
||||||
|
.build()
|
||||||
|
.securitySchemes(Collections.singletonList(securityScheme()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ApiInfo apiInfo() {
|
||||||
|
return new ApiInfoBuilder().title("考试系统接口")
|
||||||
|
.description("考试系统接口")
|
||||||
|
.contact(new Contact("Van", "https://exam.yfhl.net", "18365918@qq.com"))
|
||||||
|
.version("1.0.0")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权头部
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
SecurityScheme securityScheme() {
|
||||||
|
return new ApiKey("token", "token", "header");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.yf.exam.core.annon;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据字典注解
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Dict {
|
||||||
|
|
||||||
|
String dicCode();
|
||||||
|
|
||||||
|
String dicText() default "";
|
||||||
|
|
||||||
|
String dictTable() default "";
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.yf.exam.core.api;
|
||||||
|
|
||||||
|
|
||||||
|
import com.yf.exam.core.api.ApiError;
|
||||||
|
import com.yf.exam.core.exception.ServiceException;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据结果返回的封装
|
||||||
|
* @author bool
|
||||||
|
* @date 2018/11/20 09:48
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@ApiModel(value="接口响应", description="接口响应")
|
||||||
|
public class ApiRest<T>{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应消息
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "响应消息")
|
||||||
|
private String msg;
|
||||||
|
/**
|
||||||
|
* 响应代码
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "响应代码,0为成功,1为失败", required = true)
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求或响应body
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "响应内容")
|
||||||
|
protected T data;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isSuccess(){
|
||||||
|
return code.equals(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
public ApiRest(ServiceException error){
|
||||||
|
this.code = error.getCode();
|
||||||
|
this.msg = error.getMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
public ApiRest(ApiError error){
|
||||||
|
this.code = error.getCode();
|
||||||
|
this.msg = error.msg;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.yf.exam.core.api.dto;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询类
|
||||||
|
* @param <T>
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
@ApiModel(value="分页参数", description="分页参数")
|
||||||
|
@Data
|
||||||
|
public class PagingReqDTO<T> {
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "当前页码", required = true, example = "1")
|
||||||
|
private Integer current;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "每页数量", required = true, example = "10")
|
||||||
|
private Integer size;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "查询参数")
|
||||||
|
private T params;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序字符")
|
||||||
|
private String orderBy;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@ApiModelProperty(value = "当前用户的ID")
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换成MyBatis的简单分页对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Page toPage(){
|
||||||
|
Page page = new Page();
|
||||||
|
page.setCurrent(this.current);
|
||||||
|
page.setSize(this.size);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.yf.exam.core.api.dto;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页响应类
|
||||||
|
* @author bool
|
||||||
|
* @date 2019-07-20 15:17
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public class PagingRespDTO<T> extends Page<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取页面总数量
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getPages() {
|
||||||
|
if (this.getSize() == 0L) {
|
||||||
|
return 0L;
|
||||||
|
} else {
|
||||||
|
long pages = this.getTotal() / this.getSize();
|
||||||
|
if (this.getTotal() % this.getSize() != 0L) {
|
||||||
|
++pages;
|
||||||
|
}
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.yf.exam.core.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开放方式
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
public interface OpenType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完全开放
|
||||||
|
*/
|
||||||
|
Integer OPEN = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门开放
|
||||||
|
*/
|
||||||
|
Integer DEPT_OPEN = 2;
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.yf.exam.core.exception;
|
||||||
|
|
||||||
|
import com.yf.exam.core.api.ApiError;
|
||||||
|
import com.yf.exam.core.api.ApiRest;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ServiceException extends RuntimeException{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误消息
|
||||||
|
*/
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从结果初始化
|
||||||
|
* @param apiRest
|
||||||
|
*/
|
||||||
|
public ServiceException(ApiRest apiRest){
|
||||||
|
this.code = apiRest.getCode();
|
||||||
|
this.msg = apiRest.getMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从枚举中获取参数
|
||||||
|
* @param apiError
|
||||||
|
*/
|
||||||
|
public ServiceException(ApiError apiError){
|
||||||
|
this.code = apiError.getCode();
|
||||||
|
this.msg = apiError.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常构造
|
||||||
|
* @param msg
|
||||||
|
*/
|
||||||
|
public ServiceException(String msg){
|
||||||
|
this.code = 1;
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.yf.exam.core.utils;
|
||||||
|
|
||||||
|
import org.dozer.DozerBeanMapper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单封装Dozer, 实现深度转换Bean<->Bean的Mapper.实现:
|
||||||
|
*
|
||||||
|
* 1. 持有Mapper的单例.
|
||||||
|
* 2. 返回值类型转换.
|
||||||
|
* 3. 批量转换Collection中的所有对象.
|
||||||
|
* 4. 区分创建新的B对象与将对象A值复制到已存在的B对象两种函数.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BeanMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 持有Dozer单例, 避免重复创建DozerMapper消耗资源.
|
||||||
|
*/
|
||||||
|
private static DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于Dozer转换对象的类型.
|
||||||
|
*/
|
||||||
|
public static <T> T map(Object source, Class<T> destinationClass) {
|
||||||
|
return dozerBeanMapper.map(source, destinationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于Dozer转换Collection中对象的类型.
|
||||||
|
*/
|
||||||
|
public static <T> List<T> mapList(Iterable<?> sourceList, Class<T> destinationClass) {
|
||||||
|
List<T> destinationList = new ArrayList();
|
||||||
|
for (Object sourceObject : sourceList) {
|
||||||
|
T destinationObject = dozerBeanMapper.map(sourceObject, destinationClass);
|
||||||
|
destinationList.add(destinationObject);
|
||||||
|
}
|
||||||
|
return destinationList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于Dozer将对象A的值拷贝到对象B中.
|
||||||
|
*/
|
||||||
|
public static void copy(Object source, Object destinationObject) {
|
||||||
|
if(source!=null) {
|
||||||
|
dozerBeanMapper.map(source, destinationObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, S> List<T> mapList(Collection<S> source, Function<? super S, ? extends T> mapper) {
|
||||||
|
return source.stream().map(mapper).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.yf.exam.core.utils;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间转换quartz表达式
|
||||||
|
* @author bool
|
||||||
|
* @date 2020/11/29 下午3:00
|
||||||
|
*/
|
||||||
|
public class CronUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化数据
|
||||||
|
*/
|
||||||
|
private static final String DATE_FORMAT = "ss mm HH dd MM ? yyyy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 准确的时间点到表达式
|
||||||
|
* @param date
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String dateToCron(final Date date){
|
||||||
|
SimpleDateFormat fmt = new SimpleDateFormat(DATE_FORMAT);
|
||||||
|
String formatTimeStr = "";
|
||||||
|
if (date != null) {
|
||||||
|
formatTimeStr = fmt.format(date);
|
||||||
|
}
|
||||||
|
return formatTimeStr;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yf.exam.core.utils;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring获取工具
|
||||||
|
*
|
||||||
|
* @author bool
|
||||||
|
* @date 2019-12-09 15:55
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SpringUtils implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||||
|
applicationContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> tClass) {
|
||||||
|
return applicationContext.getBean(tClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(String name, Class<T> type) {
|
||||||
|
return applicationContext.getBean(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.yf.exam.core.utils.file;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MD5工具类
|
||||||
|
* ClassName: MD5Util <br/>
|
||||||
|
* date: 2018年1月13日 下午6:54:53 <br/>
|
||||||
|
*
|
||||||
|
* @author Bool
|
||||||
|
* @version
|
||||||
|
*/
|
||||||
|
public class Md5Util {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单MD5
|
||||||
|
* @param str
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String md5(String str) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] array = md.digest(str.getBytes("UTF-8"));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte item : array) {
|
||||||
|
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}catch(Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.yf.exam.core.utils.passwd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码实体
|
||||||
|
* ClassName: PassInfo <br/>
|
||||||
|
* date: 2018年2月13日 下午7:13:50 <br/>
|
||||||
|
*
|
||||||
|
* @author Bool
|
||||||
|
* @version
|
||||||
|
*/
|
||||||
|
public class PassInfo {
|
||||||
|
|
||||||
|
//密码随机串码
|
||||||
|
private String salt;
|
||||||
|
|
||||||
|
//MD5后的密码
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public PassInfo(String salt, String password) {
|
||||||
|
super();
|
||||||
|
this.salt = salt;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSalt() {
|
||||||
|
return salt;
|
||||||
|
}
|
||||||
|
public void setSalt(String salt) {
|
||||||
|
this.salt = salt;
|
||||||
|
}
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.yf.exam.modules;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用常量
|
||||||
|
* @author bool
|
||||||
|
*/
|
||||||
|
public class Constant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话
|
||||||
|
*/
|
||||||
|
public static final String TOKEN = "token";
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
package com.yf.exam.modules.exam.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.yf.exam.core.api.ApiRest;
|
||||||
|
import com.yf.exam.core.api.controller.BaseController;
|
||||||
|
import com.yf.exam.core.api.dto.BaseIdReqDTO;
|
||||||
|
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
|
||||||
|
import com.yf.exam.core.api.dto.BaseStateReqDTO;
|
||||||
|
import com.yf.exam.core.api.dto.PagingReqDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.ExamDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.entity.Exam;
|
||||||
|
import com.yf.exam.modules.exam.service.ExamService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试控制器
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
@Api(tags={"考试"})
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/exam/api/exam/exam")
|
||||||
|
public class ExamController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ExamService baseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加或修改
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequiresRoles("sa")
|
||||||
|
@ApiOperation(value = "添加或修改")
|
||||||
|
@RequestMapping(value = "/save", method = { RequestMethod.POST})
|
||||||
|
public ApiRest save(@RequestBody ExamSaveReqDTO reqDTO) {
|
||||||
|
//复制参数
|
||||||
|
baseService.save(reqDTO);
|
||||||
|
return super.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequiresRoles("sa")
|
||||||
|
@ApiOperation(value = "批量删除")
|
||||||
|
@RequestMapping(value = "/delete", method = { RequestMethod.POST})
|
||||||
|
public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) {
|
||||||
|
//根据ID删除
|
||||||
|
baseService.removeByIds(reqDTO.getIds());
|
||||||
|
return super.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找详情
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "查找详情")
|
||||||
|
@RequestMapping(value = "/detail", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<ExamSaveReqDTO> find(@RequestBody BaseIdReqDTO reqDTO) {
|
||||||
|
ExamSaveReqDTO dto = baseService.findDetail(reqDTO.getId());
|
||||||
|
return super.success(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找详情
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequiresRoles("sa")
|
||||||
|
@ApiOperation(value = "查找详情")
|
||||||
|
@RequestMapping(value = "/state", method = { RequestMethod.POST})
|
||||||
|
public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) {
|
||||||
|
|
||||||
|
QueryWrapper<Exam> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.lambda().in(Exam::getId, reqDTO.getIds());
|
||||||
|
Exam exam = new Exam();
|
||||||
|
exam.setState(reqDTO.getState());
|
||||||
|
exam.setUpdateTime(new Date());
|
||||||
|
|
||||||
|
baseService.update(exam, wrapper);
|
||||||
|
return super.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查找
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "考试视角")
|
||||||
|
@RequestMapping(value = "/online-paging", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<IPage<ExamOnlineRespDTO>> myPaging(@RequestBody PagingReqDTO<ExamDTO> reqDTO) {
|
||||||
|
|
||||||
|
//分页查询并转换
|
||||||
|
IPage<ExamOnlineRespDTO> page = baseService.onlinePaging(reqDTO);
|
||||||
|
return super.success(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查找
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequiresRoles("sa")
|
||||||
|
@ApiOperation(value = "分页查找")
|
||||||
|
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<IPage<ExamDTO>> paging(@RequestBody PagingReqDTO<ExamDTO> reqDTO) {
|
||||||
|
|
||||||
|
//分页查询并转换
|
||||||
|
IPage<ExamDTO> page = baseService.paging(reqDTO);
|
||||||
|
|
||||||
|
return super.success(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查找
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequiresRoles("sa")
|
||||||
|
@ApiOperation(value = "待阅试卷")
|
||||||
|
@RequestMapping(value = "/review-paging", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<IPage<ExamReviewRespDTO>> reviewPaging(@RequestBody PagingReqDTO<ExamDTO> reqDTO) {
|
||||||
|
//分页查询并转换
|
||||||
|
IPage<ExamReviewRespDTO> page = baseService.reviewPaging(reqDTO);
|
||||||
|
return super.success(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package com.yf.exam.modules.exam.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.yf.exam.modules.paper.enums.ExamState;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试数据传输类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="考试", description="考试")
|
||||||
|
public class ExamDTO implements Serializable {
|
||||||
|
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "ID", required=true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试名称", required=true)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试描述", required=true)
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "1公开2部门3定员", required=true)
|
||||||
|
private Integer openType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试状态", required=true)
|
||||||
|
private Integer state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否限时", required=true)
|
||||||
|
private Boolean timeLimit;
|
||||||
|
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@ApiModelProperty(value = "开始时间", required=true)
|
||||||
|
private Date startTime;
|
||||||
|
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@ApiModelProperty(value = "结束时间", required=true)
|
||||||
|
private Date endTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间", required=true)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "更新时间", required=true)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "总分数", required=true)
|
||||||
|
private Integer totalScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "总时长(分钟)", required=true)
|
||||||
|
private Integer totalTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "及格分数", required=true)
|
||||||
|
private Integer qualifyScore;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否结束
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Integer getState(){
|
||||||
|
|
||||||
|
if(this.timeLimit!=null && this.timeLimit){
|
||||||
|
|
||||||
|
if(System.currentTimeMillis() < startTime.getTime() ){
|
||||||
|
return ExamState.READY_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(System.currentTimeMillis() > endTime.getTime()){
|
||||||
|
return ExamState.OVERDUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(System.currentTimeMillis() > startTime.getTime()
|
||||||
|
&& System.currentTimeMillis() < endTime.getTime()
|
||||||
|
&& !ExamState.DISABLED.equals(this.state)){
|
||||||
|
return ExamState.ENABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.state;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.yf.exam.modules.exam.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试部门数据传输类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-03 17:24
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="考试部门", description="考试部门")
|
||||||
|
public class ExamDepartDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "ID", required=true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试ID", required=true)
|
||||||
|
private String examId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "部门ID", required=true)
|
||||||
|
private String departId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.yf.exam.modules.exam.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试题库数据传输类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-05 11:14
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="考试题库", description="考试题库")
|
||||||
|
public class ExamRepoDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "ID", required=true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试ID", required=true)
|
||||||
|
private String examId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "题库ID", required=true)
|
||||||
|
private String repoId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "单选题数量", required=true)
|
||||||
|
private Integer radioCount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "单选题分数", required=true)
|
||||||
|
private Integer radioScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "多选题数量", required=true)
|
||||||
|
private Integer multiCount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "多选题分数", required=true)
|
||||||
|
private Integer multiScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "判断题数量", required=true)
|
||||||
|
private Integer judgeCount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "判断题分数", required=true)
|
||||||
|
private Integer judgeScore;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yf.exam.modules.exam.dto.ext;
|
||||||
|
|
||||||
|
import com.yf.exam.modules.exam.dto.ExamRepoDTO;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试题库数据传输类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-05 11:14
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="考试题库扩展响应类", description="考试题库扩展响应类")
|
||||||
|
public class ExamRepoExtDTO extends ExamRepoDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "单选题总量", required=true)
|
||||||
|
private Integer totalRadio;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "多选题总量", required=true)
|
||||||
|
private Integer totalMulti;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "判断题总量", required=true)
|
||||||
|
private Integer totalJudge;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yf.exam.modules.exam.dto.request;
|
||||||
|
|
||||||
|
import com.yf.exam.modules.exam.dto.ExamDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试保存请求类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="考试保存请求类", description="考试保存请求类")
|
||||||
|
public class ExamSaveReqDTO extends ExamDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "题库列表", required=true)
|
||||||
|
private List<ExamRepoExtDTO> repoList;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试部门列表", required=true)
|
||||||
|
private List<String> departIds;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.yf.exam.modules.exam.dto.response;
|
||||||
|
|
||||||
|
import com.yf.exam.modules.exam.dto.ExamDTO;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试分页响应类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="在线考试分页响应类", description="在线考试分页响应类")
|
||||||
|
public class ExamOnlineRespDTO extends ExamDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.yf.exam.modules.exam.dto.response;
|
||||||
|
|
||||||
|
import com.yf.exam.modules.exam.dto.ExamDTO;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试分页响应类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="阅卷分页响应类", description="阅卷分页响应类")
|
||||||
|
public class ExamReviewRespDTO extends ExamDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试人数", required=true)
|
||||||
|
private Integer examUser;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "待阅试卷", required=true)
|
||||||
|
private Integer unreadPaper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.yf.exam.modules.exam.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试部门实体类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-03 17:24
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("el_exam_depart")
|
||||||
|
public class ExamDepart extends Model<ExamDepart> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 考试ID
|
||||||
|
*/
|
||||||
|
@TableField("exam_id")
|
||||||
|
private String examId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
@TableField("depart_id")
|
||||||
|
private String departId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.yf.exam.modules.exam.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试题库实体类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-05 11:14
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("el_exam_repo")
|
||||||
|
public class ExamRepo extends Model<ExamRepo> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 考试ID
|
||||||
|
*/
|
||||||
|
@TableField("exam_id")
|
||||||
|
private String examId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题库ID
|
||||||
|
*/
|
||||||
|
@TableField("repo_id")
|
||||||
|
private String repoId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单选题数量
|
||||||
|
*/
|
||||||
|
@TableField("radio_count")
|
||||||
|
private Integer radioCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单选题分数
|
||||||
|
*/
|
||||||
|
@TableField("radio_score")
|
||||||
|
private Integer radioScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多选题数量
|
||||||
|
*/
|
||||||
|
@TableField("multi_count")
|
||||||
|
private Integer multiCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多选题分数
|
||||||
|
*/
|
||||||
|
@TableField("multi_score")
|
||||||
|
private Integer multiScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断题数量
|
||||||
|
*/
|
||||||
|
@TableField("judge_count")
|
||||||
|
private Integer judgeCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断题分数
|
||||||
|
*/
|
||||||
|
@TableField("judge_score")
|
||||||
|
private Integer judgeScore;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.yf.exam.modules.exam.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yf.exam.modules.exam.entity.ExamDepart;
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试部门Mapper
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-03 17:24
|
||||||
|
*/
|
||||||
|
public interface ExamDepartMapper extends BaseMapper<ExamDepart> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.yf.exam.modules.exam.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.exam.dto.ExamDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.entity.Exam;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试Mapper
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
public interface ExamMapper extends BaseMapper<Exam> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找分页内容
|
||||||
|
* @param page
|
||||||
|
* @param query
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IPage<ExamDTO> paging(Page page, @Param("query") ExamDTO query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找分页内容
|
||||||
|
* @param page
|
||||||
|
* @param query
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IPage<ExamReviewRespDTO> reviewPaging(Page page, @Param("query") ExamDTO query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在线考试分页响应类-考生视角
|
||||||
|
* @param page
|
||||||
|
* @param query
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IPage<ExamOnlineRespDTO> online(Page page, @Param("query") ExamDTO query);
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.yf.exam.modules.exam.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
|
||||||
|
import com.yf.exam.modules.exam.entity.ExamRepo;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试题库Mapper
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-05 11:14
|
||||||
|
*/
|
||||||
|
public interface ExamRepoMapper extends BaseMapper<ExamRepo> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找考试题库列表
|
||||||
|
* @param examId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<ExamRepoExtDTO> listByExam(@Param("examId") String examId);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yf.exam.modules.exam.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.yf.exam.modules.exam.entity.ExamDepart;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试部门业务类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-03 17:24
|
||||||
|
*/
|
||||||
|
public interface ExamDepartService extends IService<ExamDepart> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存全部
|
||||||
|
* @param examId
|
||||||
|
* @param departs
|
||||||
|
*/
|
||||||
|
void saveAll(String examId, List<String> departs);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据考试查找对应的部门
|
||||||
|
* @param examId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<String> listByExam(String examId);
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.yf.exam.modules.exam.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
|
||||||
|
import com.yf.exam.modules.exam.entity.ExamRepo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试题库业务类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-05 11:14
|
||||||
|
*/
|
||||||
|
public interface ExamRepoService extends IService<ExamRepo> {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存全部
|
||||||
|
* @param examId
|
||||||
|
* @param list
|
||||||
|
*/
|
||||||
|
void saveAll(String examId, List<ExamRepoExtDTO> list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找考试题库列表
|
||||||
|
* @param examId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<ExamRepoExtDTO> listByExam(String examId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理脏数据
|
||||||
|
* @param examId
|
||||||
|
*/
|
||||||
|
void clear(String examId);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.yf.exam.modules.exam.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.exam.dto.ExamDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.entity.Exam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试业务类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
public interface ExamService extends IService<Exam> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存考试信息
|
||||||
|
* @param reqDTO
|
||||||
|
*/
|
||||||
|
void save(ExamSaveReqDTO reqDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找考试详情
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ExamSaveReqDTO findDetail(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找考试详情--简要信息
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ExamDTO findById(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询数据
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IPage<ExamDTO> paging(PagingReqDTO<ExamDTO> reqDTO);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在线考试分页响应类-考生视角
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IPage<ExamOnlineRespDTO> onlinePaging(PagingReqDTO<ExamDTO> reqDTO);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待阅试卷列表
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IPage<ExamReviewRespDTO> reviewPaging(PagingReqDTO<ExamDTO> reqDTO);
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.yf.exam.modules.exam.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yf.exam.core.exception.ServiceException;
|
||||||
|
import com.yf.exam.modules.exam.entity.ExamDepart;
|
||||||
|
import com.yf.exam.modules.exam.mapper.ExamDepartMapper;
|
||||||
|
import com.yf.exam.modules.exam.service.ExamDepartService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试部门业务实现类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-03 17:24
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ExamDepartServiceImpl extends ServiceImpl<ExamDepartMapper, ExamDepart> implements ExamDepartService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAll(String examId, List<String> departs) {
|
||||||
|
|
||||||
|
// 先删除
|
||||||
|
QueryWrapper<ExamDepart> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.lambda().eq(ExamDepart::getExamId, examId);
|
||||||
|
this.remove(wrapper);
|
||||||
|
|
||||||
|
// 再增加
|
||||||
|
if(CollectionUtils.isEmpty(departs)){
|
||||||
|
throw new ServiceException(1, "请至少选择选择一个部门!!");
|
||||||
|
}
|
||||||
|
List<ExamDepart> list = new ArrayList<>();
|
||||||
|
|
||||||
|
for(String id: departs){
|
||||||
|
ExamDepart depart = new ExamDepart();
|
||||||
|
depart.setDepartId(id);
|
||||||
|
depart.setExamId(examId);
|
||||||
|
list.add(depart);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveBatch(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> listByExam(String examId) {
|
||||||
|
// 先删除
|
||||||
|
QueryWrapper<ExamDepart> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.lambda().eq(ExamDepart::getExamId, examId);
|
||||||
|
List<ExamDepart> list = this.list(wrapper);
|
||||||
|
List<String> ids = new ArrayList<>();
|
||||||
|
if(!CollectionUtils.isEmpty(list)){
|
||||||
|
for(ExamDepart item: list){
|
||||||
|
ids.add(item.getDepartId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.yf.exam.modules.exam.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yf.exam.core.exception.ServiceException;
|
||||||
|
import com.yf.exam.core.utils.BeanMapper;
|
||||||
|
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
|
||||||
|
import com.yf.exam.modules.exam.entity.ExamRepo;
|
||||||
|
import com.yf.exam.modules.exam.mapper.ExamRepoMapper;
|
||||||
|
import com.yf.exam.modules.exam.service.ExamRepoService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试题库业务实现类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-09-05 11:14
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ExamRepoServiceImpl extends ServiceImpl<ExamRepoMapper, ExamRepo> implements ExamRepoService {
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void saveAll(String examId, List<ExamRepoExtDTO> list) {
|
||||||
|
|
||||||
|
// 先删除
|
||||||
|
QueryWrapper<ExamRepo> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.lambda().eq(ExamRepo::getExamId, examId);
|
||||||
|
this.remove(wrapper);
|
||||||
|
|
||||||
|
// 再增加
|
||||||
|
if(CollectionUtils.isEmpty(list)){
|
||||||
|
throw new ServiceException(1, "必须选择题库!");
|
||||||
|
}
|
||||||
|
List<ExamRepo> repos = BeanMapper.mapList(list, ExamRepo.class);
|
||||||
|
for(ExamRepo item: repos){
|
||||||
|
item.setExamId(examId);
|
||||||
|
item.setId(IdWorker.getIdStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveBatch(repos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ExamRepoExtDTO> listByExam(String examId) {
|
||||||
|
return baseMapper.listByExam(examId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear(String examId) {
|
||||||
|
|
||||||
|
// 先删除
|
||||||
|
QueryWrapper<ExamRepo> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.lambda().eq(ExamRepo::getExamId, examId);
|
||||||
|
this.remove(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,194 @@
|
|||||||
|
package com.yf.exam.modules.exam.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yf.exam.core.api.dto.PagingReqDTO;
|
||||||
|
import com.yf.exam.core.enums.OpenType;
|
||||||
|
import com.yf.exam.core.exception.ServiceException;
|
||||||
|
import com.yf.exam.core.utils.BeanMapper;
|
||||||
|
import com.yf.exam.modules.exam.dto.ExamDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.ExamRepoDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
|
||||||
|
import com.yf.exam.modules.exam.entity.Exam;
|
||||||
|
import com.yf.exam.modules.exam.mapper.ExamMapper;
|
||||||
|
import com.yf.exam.modules.exam.service.ExamDepartService;
|
||||||
|
import com.yf.exam.modules.exam.service.ExamRepoService;
|
||||||
|
import com.yf.exam.modules.exam.service.ExamService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 考试业务实现类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-07-25 16:18
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService {
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ExamRepoService examRepoService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ExamDepartService examDepartService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(ExamSaveReqDTO reqDTO) {
|
||||||
|
|
||||||
|
// ID
|
||||||
|
String id = reqDTO.getId();
|
||||||
|
|
||||||
|
if(StringUtils.isBlank(id)){
|
||||||
|
id = IdWorker.getIdStr();
|
||||||
|
}
|
||||||
|
|
||||||
|
//复制参数
|
||||||
|
Exam entity = new Exam();
|
||||||
|
|
||||||
|
// 计算分值
|
||||||
|
this.calcScore(reqDTO);
|
||||||
|
|
||||||
|
|
||||||
|
// 复制基本数据
|
||||||
|
BeanMapper.copy(reqDTO, entity);
|
||||||
|
entity.setId(id);
|
||||||
|
|
||||||
|
// 修复状态
|
||||||
|
if (reqDTO.getTimeLimit()!=null
|
||||||
|
&& !reqDTO.getTimeLimit()
|
||||||
|
&& reqDTO.getState()!=null
|
||||||
|
&& reqDTO.getState() == 2) {
|
||||||
|
entity.setState(0);
|
||||||
|
} else {
|
||||||
|
entity.setState(reqDTO.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 题库组卷
|
||||||
|
try {
|
||||||
|
examRepoService.saveAll(id, reqDTO.getRepoList());
|
||||||
|
}catch (DuplicateKeyException e){
|
||||||
|
throw new ServiceException(1, "不能选择重复的题库!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 开放的部门
|
||||||
|
if(OpenType.DEPT_OPEN.equals(reqDTO.getOpenType())){
|
||||||
|
examDepartService.saveAll(id, reqDTO.getDepartIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveOrUpdate(entity);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExamSaveReqDTO findDetail(String id) {
|
||||||
|
ExamSaveReqDTO respDTO = new ExamSaveReqDTO();
|
||||||
|
Exam exam = this.getById(id);
|
||||||
|
BeanMapper.copy(exam, respDTO);
|
||||||
|
|
||||||
|
// 考试部门
|
||||||
|
List<String> departIds = examDepartService.listByExam(id);
|
||||||
|
respDTO.setDepartIds(departIds);
|
||||||
|
|
||||||
|
// 题库
|
||||||
|
List<ExamRepoExtDTO> repos = examRepoService.listByExam(id);
|
||||||
|
respDTO.setRepoList(repos);
|
||||||
|
|
||||||
|
return respDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExamDTO findById(String id) {
|
||||||
|
ExamDTO respDTO = new ExamDTO();
|
||||||
|
Exam exam = this.getById(id);
|
||||||
|
BeanMapper.copy(exam, respDTO);
|
||||||
|
return respDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<ExamDTO> paging(PagingReqDTO<ExamDTO> reqDTO) {
|
||||||
|
|
||||||
|
//创建分页对象
|
||||||
|
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
|
||||||
|
|
||||||
|
//转换结果
|
||||||
|
IPage<ExamDTO> pageData = baseMapper.paging(page, reqDTO.getParams());
|
||||||
|
return pageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<ExamOnlineRespDTO> onlinePaging(PagingReqDTO<ExamDTO> reqDTO) {
|
||||||
|
|
||||||
|
|
||||||
|
// 创建分页对象
|
||||||
|
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
|
||||||
|
|
||||||
|
// 查找分页
|
||||||
|
IPage<ExamOnlineRespDTO> pageData = baseMapper.online(page, reqDTO.getParams());
|
||||||
|
|
||||||
|
return pageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<ExamReviewRespDTO> reviewPaging(PagingReqDTO<ExamDTO> reqDTO) {
|
||||||
|
// 创建分页对象
|
||||||
|
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
|
||||||
|
|
||||||
|
// 查找分页
|
||||||
|
IPage<ExamReviewRespDTO> pageData = baseMapper.reviewPaging(page, reqDTO.getParams());
|
||||||
|
|
||||||
|
return pageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算分值
|
||||||
|
* @param reqDTO
|
||||||
|
*/
|
||||||
|
private void calcScore(ExamSaveReqDTO reqDTO){
|
||||||
|
|
||||||
|
// 主观题分数
|
||||||
|
int objScore = 0;
|
||||||
|
|
||||||
|
// 题库组卷
|
||||||
|
List<ExamRepoExtDTO> repoList = reqDTO.getRepoList();
|
||||||
|
|
||||||
|
for(ExamRepoDTO item: repoList){
|
||||||
|
if(item.getRadioCount()!=null
|
||||||
|
&& item.getRadioCount()>0
|
||||||
|
&& item.getRadioScore()!=null
|
||||||
|
&& item.getRadioScore()>0){
|
||||||
|
objScore+=item.getRadioCount()*item.getRadioScore();
|
||||||
|
}
|
||||||
|
if(item.getMultiCount()!=null
|
||||||
|
&& item.getMultiCount()>0
|
||||||
|
&& item.getMultiScore()!=null
|
||||||
|
&& item.getMultiScore()>0){
|
||||||
|
objScore+=item.getMultiCount()*item.getMultiScore();
|
||||||
|
}
|
||||||
|
if(item.getJudgeCount()!=null
|
||||||
|
&& item.getJudgeCount()>0
|
||||||
|
&& item.getJudgeScore()!=null
|
||||||
|
&& item.getJudgeScore()>0){
|
||||||
|
objScore+=item.getJudgeCount()*item.getJudgeScore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
reqDTO.setTotalScore(objScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,156 @@
|
|||||||
|
package com.yf.exam.modules.paper.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.BaseIdRespDTO;
|
||||||
|
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
|
||||||
|
import com.yf.exam.core.api.dto.PagingReqDTO;
|
||||||
|
import com.yf.exam.core.utils.BeanMapper;
|
||||||
|
import com.yf.exam.modules.paper.dto.PaperDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.request.PaperCreateReqDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.request.PaperListReqDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.request.PaperQuQueryDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO;
|
||||||
|
import com.yf.exam.modules.paper.dto.response.PaperListRespDTO;
|
||||||
|
import com.yf.exam.modules.paper.entity.Paper;
|
||||||
|
import com.yf.exam.modules.paper.service.PaperService;
|
||||||
|
import com.yf.exam.modules.user.UserUtils;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 试卷控制器
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-05-25 16:33
|
||||||
|
*/
|
||||||
|
@Api(tags={"试卷"})
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/exam/api/paper/paper")
|
||||||
|
public class PaperController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PaperService baseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查找
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "分页查找")
|
||||||
|
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<IPage<PaperListRespDTO>> paging(@RequestBody PagingReqDTO<PaperListReqDTO> reqDTO) {
|
||||||
|
//分页查询并转换
|
||||||
|
IPage<PaperListRespDTO> page = baseService.paging(reqDTO);
|
||||||
|
return super.success(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建试卷
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "创建试卷")
|
||||||
|
@RequestMapping(value = "/create-paper", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<BaseIdRespDTO> save(@RequestBody PaperCreateReqDTO reqDTO) {
|
||||||
|
//复制参数
|
||||||
|
String paperId = baseService.createPaper(UserUtils.getUserId(), reqDTO.getExamId());
|
||||||
|
return super.success(new BaseIdRespDTO(paperId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "试卷详情")
|
||||||
|
@RequestMapping(value = "/paper-detail", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<ExamDetailRespDTO> paperDetail(@RequestBody BaseIdReqDTO reqDTO) {
|
||||||
|
//根据ID删除
|
||||||
|
ExamDetailRespDTO respDTO = baseService.paperDetail(reqDTO.getId());
|
||||||
|
return super.success(respDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "试题详情")
|
||||||
|
@RequestMapping(value = "/qu-detail", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<PaperQuDetailDTO> quDetail(@RequestBody PaperQuQueryDTO reqDTO) {
|
||||||
|
//根据ID删除
|
||||||
|
PaperQuDetailDTO respDTO = baseService.findQuDetail(reqDTO.getPaperId(), reqDTO.getQuId());
|
||||||
|
return super.success(respDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充答案
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "填充答案")
|
||||||
|
@RequestMapping(value = "/fill-answer", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<PaperQuDetailDTO> fillAnswer(@RequestBody PaperAnswerDTO reqDTO) {
|
||||||
|
//根据ID删除
|
||||||
|
baseService.fillAnswer(reqDTO);
|
||||||
|
return super.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交卷操作
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "交卷操作")
|
||||||
|
@RequestMapping(value = "/hand-exam", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<PaperQuDetailDTO> handleExam(@RequestBody BaseIdReqDTO reqDTO) {
|
||||||
|
//根据ID删除
|
||||||
|
baseService.handExam(reqDTO.getId());
|
||||||
|
return super.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除
|
||||||
|
* @param reqDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "试卷详情")
|
||||||
|
@RequestMapping(value = "/paper-result", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<ExamResultRespDTO> paperResult(@RequestBody BaseIdReqDTO reqDTO) {
|
||||||
|
//根据ID删除
|
||||||
|
ExamResultRespDTO respDTO = baseService.paperResult(reqDTO.getId());
|
||||||
|
return super.success(respDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测用户有没有中断的考试
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "检测进行中的考试")
|
||||||
|
@RequestMapping(value = "/check-process", method = { RequestMethod.POST})
|
||||||
|
public ApiRest<PaperDTO> checkProcess() {
|
||||||
|
//复制参数
|
||||||
|
PaperDTO dto = baseService.checkProcess(UserUtils.getUserId());
|
||||||
|
return super.success(dto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.yf.exam.modules.paper.dto;
|
||||||
|
|
||||||
|
import com.yf.exam.core.annon.Dict;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 试卷请求类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-05-25 17:31
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="试卷", description="试卷")
|
||||||
|
public class PaperDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "试卷ID", required=true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Dict(dictTable = "sys_user", dicText = "real_name", dicCode = "id")
|
||||||
|
@ApiModelProperty(value = "用户ID", required=true)
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
@Dict(dictTable = "sys_depart", dicText = "dept_name", dicCode = "id")
|
||||||
|
@ApiModelProperty(value = "部门ID", required=true)
|
||||||
|
private String departId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "规则ID", required=true)
|
||||||
|
private String examId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试标题", required=true)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "考试时长", required=true)
|
||||||
|
private Integer totalTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户时长", required=true)
|
||||||
|
private Integer userTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "试卷总分", required=true)
|
||||||
|
private Integer totalScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "及格分", required=true)
|
||||||
|
private Integer qualifyScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "客观分", required=true)
|
||||||
|
private Integer objScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "主观分", required=true)
|
||||||
|
private Integer subjScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户得分", required=true)
|
||||||
|
private Integer userScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否包含简答题", required=true)
|
||||||
|
private Boolean hasSaq;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "试卷状态", required=true)
|
||||||
|
private Integer state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间", required=true)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "更新时间", required=true)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "截止时间")
|
||||||
|
private Date limitTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.yf.exam.modules.paper.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 试卷考题备选答案请求类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-05-25 17:31
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案")
|
||||||
|
public class PaperQuAnswerDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "自增ID", required=true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "试卷ID", required=true)
|
||||||
|
private String paperId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "回答项ID", required=true)
|
||||||
|
private String answerId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "题目ID", required=true)
|
||||||
|
private String quId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否正确项", required=true)
|
||||||
|
private Boolean isRight;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否选中", required=true)
|
||||||
|
private Boolean checked;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序", required=true)
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "选项标签", required=true)
|
||||||
|
private String abc;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.yf.exam.modules.paper.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 试卷考题请求类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-05-25 17:31
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="试卷考题", description="试卷考题")
|
||||||
|
public class PaperQuDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "ID", required=true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "试卷ID", required=true)
|
||||||
|
private String paperId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "题目ID", required=true)
|
||||||
|
private String quId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "题目类型", required=true)
|
||||||
|
private Integer quType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已答", required=true)
|
||||||
|
private Boolean answered;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "主观答案", required=true)
|
||||||
|
private String answer;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "问题排序", required=true)
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "单题分分值", required=true)
|
||||||
|
private Integer score;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "实际得分(主观题)", required=true)
|
||||||
|
private Integer actualScore;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否答对", required=true)
|
||||||
|
private Boolean isRight;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.yf.exam.modules.paper.dto.ext;
|
||||||
|
|
||||||
|
import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 试卷考题备选答案请求类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 聪明笨狗
|
||||||
|
* @since 2020-05-25 17:31
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案")
|
||||||
|
public class PaperQuAnswerExtDTO extends PaperQuAnswerDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "试题图片", required=true)
|
||||||
|
private String image;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "答案内容", required=true)
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue