init
This commit is contained in:
當前提交
34e481f0a5
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
src/main/resources/application.yml
|
||||
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
135
pom.xml
Normal file
135
pom.xml
Normal file
@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.weilab</groupId>
|
||||
<artifactId>biology</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>biology</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.76</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.python</groupId>
|
||||
<artifactId>jython-standalone</artifactId>
|
||||
<version>2.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>2.0.1.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.7.19</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>biology</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>public</id>
|
||||
<name>aliyun nexus</name>
|
||||
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>public</id>
|
||||
<name>aliyun nexus</name>
|
||||
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
</project>
|
13
src/main/java/com/weilab/biology/BiologyApplication.java
Normal file
13
src/main/java/com/weilab/biology/BiologyApplication.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.weilab.biology;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class BiologyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BiologyApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
33
src/main/java/com/weilab/biology/config/FilterConfig.java
Normal file
33
src/main/java/com/weilab/biology/config/FilterConfig.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.weilab.biology.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2020-12-27 10:54
|
||||
* Description :
|
||||
*/
|
||||
@Configuration
|
||||
public class FilterConfig {
|
||||
|
||||
@Autowired
|
||||
private TokenFilter tokenFilter;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Bean
|
||||
public FilterRegistrationBean tokenFilterRegistration() {
|
||||
FilterRegistrationBean registration = new FilterRegistrationBean();
|
||||
registration.setFilter(tokenFilter);
|
||||
registration.addUrlPatterns("/*");
|
||||
registration.setName("tokenFilter");
|
||||
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
|
||||
return registration;
|
||||
}
|
||||
}
|
33
src/main/java/com/weilab/biology/config/TokenFilter.java
Normal file
33
src/main/java/com/weilab/biology/config/TokenFilter.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.weilab.biology.config;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@Component
|
||||
public class TokenFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
response.setHeader("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS");
|
||||
response.setHeader("Access-Control-Allow-Headers", "*");
|
||||
response.setHeader("Access-Control-Expose-Headers", "*");
|
||||
response.setHeader("Access-Control-Max-Age", "36000");
|
||||
|
||||
if ("OPTIONS".equals(request.getMethod())) {
|
||||
response.setStatus(200);
|
||||
return;
|
||||
}
|
||||
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
}
|
124
src/main/java/com/weilab/biology/controller/JobController.java
Normal file
124
src/main/java/com/weilab/biology/controller/JobController.java
Normal file
@ -0,0 +1,124 @@
|
||||
package com.weilab.biology.controller;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.weilab.biology.core.data.enums.JobStatusEnum;
|
||||
import com.weilab.biology.core.data.vo.result.CommonError;
|
||||
import com.weilab.biology.core.data.vo.result.Result;
|
||||
import com.weilab.biology.core.data.vo.result.error.JobError;
|
||||
import com.weilab.biology.core.validation.EnumValidation;
|
||||
import com.weilab.biology.service.JobService;
|
||||
import com.weilab.biology.util.FileUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("job")
|
||||
public class JobController {
|
||||
|
||||
@Autowired
|
||||
private JobService jobService;
|
||||
|
||||
@Value("${biology.request-path}")
|
||||
private String requestPath;
|
||||
|
||||
@PostMapping("/submit")
|
||||
public Result submit(@RequestParam(required = false) String dataStr,
|
||||
@RequestParam(required = false) MultipartFile dataFile,
|
||||
@RequestParam String param,
|
||||
@RequestParam String mail,
|
||||
@RequestParam(defaultValue = "0") Integer type,
|
||||
@RequestParam(required = false) MultipartFile file1,
|
||||
@RequestParam(required = false) MultipartFile file2,
|
||||
@RequestParam(required = false) MultipartFile file3,
|
||||
@RequestParam(required = false) MultipartFile file4,
|
||||
@RequestParam(required = false) MultipartFile file5,
|
||||
@RequestParam(required = false) MultipartFile file6,
|
||||
@RequestParam(required = false) MultipartFile file7,
|
||||
@RequestParam(required = false) MultipartFile file8,
|
||||
@RequestParam(required = false) MultipartFile file9,
|
||||
@RequestParam(required = false) MultipartFile file10,
|
||||
@RequestParam(required = false) MultipartFile file11,
|
||||
@RequestParam(required = false) MultipartFile file12,
|
||||
@RequestParam(required = false) MultipartFile file13,
|
||||
@RequestParam(required = false) MultipartFile file14,
|
||||
@RequestParam(required = false) MultipartFile file15,
|
||||
@RequestParam(required = false) MultipartFile file16,
|
||||
@RequestParam(required = false) MultipartFile file17,
|
||||
@RequestParam(required = false) MultipartFile file18,
|
||||
@RequestParam(required = false) MultipartFile file19,
|
||||
@RequestParam(required = false) MultipartFile file20) {
|
||||
if (dataFile == null && StringUtils.isBlank(dataStr))
|
||||
return Result.getResult(JobError.PARAM_CAN_NOT_BE_EMPTY);
|
||||
|
||||
JSONObject obj = null;
|
||||
try {
|
||||
obj = JSON.parseObject(param);
|
||||
} catch (Exception e) {
|
||||
return Result.getResult(CommonError.PARAM_WRONG);
|
||||
}
|
||||
|
||||
BufferedInputStream dataStream = null;
|
||||
if (dataFile != null) {
|
||||
dataStream = FileUtil.getInputStream(FileUtils.multipartToFile(dataFile));
|
||||
}
|
||||
|
||||
try {
|
||||
List<MultipartFile> files = Arrays.asList(file1, file2, file3, file4, file5,
|
||||
file6, file7, file8, file9, file10, file11, file12, file13, file14, file15,
|
||||
file16, file17, file18, file19, file20);
|
||||
for (MultipartFile file : files) {
|
||||
if (file != null) {
|
||||
String filePath = requestPath + FileUtil.FILE_SEPARATOR + "file" + FileUtil.FILE_SEPARATOR + IdUtil.fastUUID() + "." + FileUtil.extName(file.getOriginalFilename());
|
||||
FileUtil.writeFromStream(file.getInputStream(), filePath);
|
||||
obj.put(file.getName(), filePath);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return Result.getResult(JobError.FILE_READ_FAIL);
|
||||
}
|
||||
|
||||
return jobService.submit(dataStr, dataStream, obj, mail, type);
|
||||
}
|
||||
|
||||
@PostMapping("/status/update")
|
||||
public Result updateJobStatus(@RequestParam Integer jobId,
|
||||
@EnumValidation(clazz = JobStatusEnum.class, message = "没有此状态")
|
||||
@RequestParam Integer status,
|
||||
@RequestParam(required = false) String result) {
|
||||
if (status.equals(JobStatusEnum.WAIT.getKey()))
|
||||
return Result.getResult(CommonError.PARAM_WRONG);
|
||||
if (status.equals(JobStatusEnum.SUCCESS.getKey())) {
|
||||
if (StringUtils.isBlank(result))
|
||||
return Result.getResult(CommonError.PARAM_WRONG);
|
||||
try {
|
||||
JSON.parseObject(result);
|
||||
} catch (Exception e) {
|
||||
return Result.getResult(CommonError.PARAM_WRONG);
|
||||
}
|
||||
}
|
||||
return jobService.updateJobStatus(jobId, JobStatusEnum.getEnumByKey(status), result);
|
||||
}
|
||||
|
||||
@GetMapping("/info/{jobId}")
|
||||
public Result getJobInfo(@PathVariable Integer jobId) {
|
||||
return jobService.getJobInfo(jobId);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public Result getJobList(@RequestParam(required = false) Integer type) {
|
||||
return jobService.getJobList(type);
|
||||
}
|
||||
}
|
78
src/main/java/com/weilab/biology/core/data/dto/JobDto.java
Normal file
78
src/main/java/com/weilab/biology/core/data/dto/JobDto.java
Normal file
@ -0,0 +1,78 @@
|
||||
package com.weilab.biology.core.data.dto;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.weilab.biology.core.data.enums.JobStatusEnum;
|
||||
import com.weilab.biology.core.data.po.Job;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class JobDto {
|
||||
|
||||
/**
|
||||
* jobId
|
||||
*/
|
||||
private Integer jobId;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
private JSONObject param;
|
||||
|
||||
/**
|
||||
* 运行结果
|
||||
*/
|
||||
private JSONObject result;
|
||||
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime requestTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime completeTime;
|
||||
|
||||
/**
|
||||
* 请求类型
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
public static JobDto parseJob(Job job) {
|
||||
return new JobDto(
|
||||
job.getJobId(),
|
||||
JobStatusEnum.getRemark(job.getStatus()),
|
||||
JSON.parseObject(job.getParam()),
|
||||
job.getResult() == null ? null : JSON.parseObject(job.getResult()),
|
||||
job.getRequestTime(),
|
||||
job.getCreateTime(),
|
||||
job.getCompleteTime(),
|
||||
job.getType()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.weilab.biology.core.data.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.weilab.biology.core.data.enums.JobStatusEnum;
|
||||
import com.weilab.biology.core.data.po.Job;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-09-22
|
||||
* Description :
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class JobLessDto {
|
||||
|
||||
/**
|
||||
* jobId
|
||||
*/
|
||||
private Integer jobId;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime requestTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime completeTime;
|
||||
|
||||
/**
|
||||
* 请求类型
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
public static JobLessDto parseJob(Job job) {
|
||||
return new JobLessDto(
|
||||
job.getJobId(),
|
||||
JobStatusEnum.getRemark(job.getStatus()),
|
||||
job.getRequestTime(),
|
||||
job.getCreateTime(),
|
||||
job.getCompleteTime(),
|
||||
job.getType()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.weilab.biology.core.data.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 校区的枚举类
|
||||
*/
|
||||
@Getter
|
||||
public enum JobStatusEnum {
|
||||
WAIT(0, "waiting"),
|
||||
REQED(3, "requested"),
|
||||
RUNNING(1, "running"),
|
||||
SUCCESS(2, "success"),
|
||||
FAIL(-1, "failed"),
|
||||
TIMEOUT(-2, "timeout"),
|
||||
;
|
||||
|
||||
private final Integer key;
|
||||
private final String remark;
|
||||
|
||||
private JobStatusEnum(Integer key, String remark) {
|
||||
this.key = key;
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public static String getRemark(Integer key) {
|
||||
for (JobStatusEnum enums : JobStatusEnum.values()) {
|
||||
if (enums.key.equals(key))
|
||||
return enums.getRemark();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JobStatusEnum getEnumByKey(Integer key) {
|
||||
for (JobStatusEnum enums : JobStatusEnum.values()) {
|
||||
if (enums.key.equals(key))
|
||||
return enums;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
97
src/main/java/com/weilab/biology/core/data/po/Job.java
Normal file
97
src/main/java/com/weilab/biology/core/data/po/Job.java
Normal file
@ -0,0 +1,97 @@
|
||||
package com.weilab.biology.core.data.po;
|
||||
|
||||
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 java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
import jnr.ffi.annotations.In;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-09-19
|
||||
* Description :
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName(value = "job")
|
||||
public class Job implements Serializable {
|
||||
/**
|
||||
* jobId
|
||||
*/
|
||||
@TableId(value = "job_id", type = IdType.AUTO)
|
||||
private Integer jobId;
|
||||
|
||||
/**
|
||||
* 基因序列数据
|
||||
*/
|
||||
@TableField(value = "`data`")
|
||||
private String data;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
@TableField(value = "param")
|
||||
private String param;
|
||||
|
||||
/**
|
||||
* 联系邮箱
|
||||
*/
|
||||
@TableField(value = "mail")
|
||||
private String mail;
|
||||
|
||||
/**
|
||||
* 运行结果
|
||||
*/
|
||||
@TableField(value = "result")
|
||||
private String result;
|
||||
|
||||
/**
|
||||
* 任务状态。0为待运行,1为正在运行,2为运行成功,-1为运行失败
|
||||
*/
|
||||
@TableField(value = "`status`")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
@TableField(value = "request_time")
|
||||
private LocalDateTime requestTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(value = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@TableField(value = "complete_time")
|
||||
private LocalDateTime completeTime;
|
||||
|
||||
/**
|
||||
* 请求类型
|
||||
*/
|
||||
@TableField(value = "type")
|
||||
private Integer type;
|
||||
|
||||
public Job(String data, String param, String mail, Integer status, LocalDateTime requestTime, Integer type) {
|
||||
this.data = data;
|
||||
this.param = param;
|
||||
this.mail = mail;
|
||||
this.status = status;
|
||||
this.requestTime = requestTime;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.weilab.biology.core.data.vo.result;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-01-30
|
||||
* Description : 通用异常返回
|
||||
*/
|
||||
public enum CommonError implements ResultError {
|
||||
PARAM_WRONG(40000, "参数范围或格式错误"),
|
||||
NETWORK_WRONG(40001, "网络错误"),
|
||||
REQUEST_NOT_ALLOW(40002, "当前条件或时间不允许〒▽〒"),
|
||||
REQUEST_FREQUENTLY(40003, "请求繁忙,请稍后再试"),
|
||||
CONTENT_NOT_FOUND(40004, "你要找的东西好像走丢啦X﹏X"),
|
||||
METHOD_NOT_ALLOW(40005, "方法不允许"),
|
||||
THIS_IS_LAST_PAGE(40006, "这是最后一页,再怎么找也没有啦"),
|
||||
THIS_IS_FIRST_PAGE(40007, "没有上一页啦"),
|
||||
PIC_FORMAT_ERROR(40008, "图片格式只能为jpg, jpeg, png, gif, bmp, webp"),
|
||||
;
|
||||
|
||||
private int code;
|
||||
|
||||
private String message;
|
||||
|
||||
private CommonError(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.weilab.biology.core.data.vo.result;
|
||||
|
||||
/**
|
||||
* web层统一返回类型
|
||||
*/
|
||||
public class Result {
|
||||
private int code = 0;
|
||||
|
||||
private String message;
|
||||
|
||||
private Object data;
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
public Result(int code, String message, Object data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Result(ResultError resultError) {
|
||||
this.code = resultError.getCode();
|
||||
this.message = resultError.getMessage();
|
||||
}
|
||||
|
||||
public static Result getResult(int code, String message) {
|
||||
return getResult(code, message, null);
|
||||
}
|
||||
|
||||
public static Result getResult(int code, String message, Object data) {
|
||||
return new Result(code, message, data);
|
||||
}
|
||||
|
||||
public static Result success() {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
public static Result success(Object data) {
|
||||
return success("success", data);
|
||||
}
|
||||
|
||||
public static Result success(String message, Object data) {
|
||||
return new Result(0, message, data);
|
||||
}
|
||||
|
||||
public static Result fail() {
|
||||
return fail(null);
|
||||
}
|
||||
|
||||
public static Result fail(Object data) {
|
||||
return fail("请求失败", data);
|
||||
}
|
||||
|
||||
public static Result fail(String message, Object data) {
|
||||
return new Result(-1, message, data);
|
||||
}
|
||||
|
||||
public static Result getResult(ResultError resultError) {
|
||||
return getResult(resultError.getCode(), resultError.getMessage());
|
||||
}
|
||||
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Info{" +
|
||||
"code=" + code +
|
||||
", message='" + message + '\'' +
|
||||
", data=" + data +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.weilab.biology.core.data.vo.result;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-01-30
|
||||
* Description : 通用异常返回的父接口
|
||||
*/
|
||||
public interface ResultError {
|
||||
|
||||
int getCode();
|
||||
|
||||
String getMessage();
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.weilab.biology.core.data.vo.result.error;
|
||||
|
||||
import com.weilab.biology.core.data.vo.result.ResultError;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-09-19
|
||||
* Description :
|
||||
*/
|
||||
public enum JobError implements ResultError {
|
||||
PARAM_CAN_NOT_BE_EMPTY(40100, "文本框和文件不能同时为空"),
|
||||
FILE_READ_FAIL(40101, "文件读取出错"),
|
||||
STATUS_UPDATE_FAIL(40102,"当前状态下不允许更新为指定状态"),
|
||||
;
|
||||
|
||||
private int code;
|
||||
|
||||
private String message;
|
||||
|
||||
|
||||
private JobError(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.weilab.biology.core.exception;
|
||||
|
||||
import com.weilab.biology.core.data.vo.result.ResultError;
|
||||
|
||||
public class BaseException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code;
|
||||
|
||||
private String message;
|
||||
|
||||
private Object data;
|
||||
|
||||
public BaseException(int code, String message, Object data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public BaseException(int code, String message) {
|
||||
this(code, message, null);
|
||||
}
|
||||
|
||||
public BaseException(ResultError error) {
|
||||
this(error.getCode(), error.getMessage(), null);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package com.weilab.biology.core.exception;
|
||||
|
||||
import com.weilab.biology.core.data.vo.result.CommonError;
|
||||
import com.weilab.biology.core.data.vo.result.Result;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.util.List;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 自定义异常捕获
|
||||
*/
|
||||
@ExceptionHandler(BaseException.class)
|
||||
public Result handleBaseException(BaseException e) {
|
||||
return Result.getResult(e.getCode(), e.getMessage(), e.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务器错误。常见有数据库错误
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Result handleException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.fail();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON解析异常,最常见NullPointerException,这里均将其过滤
|
||||
*/
|
||||
@ExceptionHandler({NullPointerException.class})
|
||||
public Result handleJSONException(Exception e){
|
||||
return Result.getResult(CommonError.REQUEST_NOT_ALLOW);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验错误异常
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Result validationBodyException(MethodArgumentNotValidException e) {
|
||||
BindingResult result = e.getBindingResult();
|
||||
String message = CommonError.PARAM_WRONG.getMessage();
|
||||
if (result.hasErrors()) {
|
||||
List<ObjectError> errors = result.getAllErrors();
|
||||
if (errors.size() > 0) {
|
||||
FieldError fieldError = (FieldError) errors.get(0);
|
||||
message = fieldError.getDefaultMessage();
|
||||
}
|
||||
}
|
||||
return Result.getResult(CommonError.PARAM_WRONG.getCode(), message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 参数类型转换错误
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public Result methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
|
||||
return Result.getResult(CommonError.PARAM_WRONG.getCode(),
|
||||
e.getName() + "参数类型转换错误");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 参数转换JSON出错
|
||||
*/
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public Result httpMessageNotReadableException(HttpMessageNotReadableException e) {
|
||||
return Result.getResult(CommonError.PARAM_WRONG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求方法不允许
|
||||
*/
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public Result methodNotSupportedException(HttpRequestMethodNotSupportedException e) {
|
||||
return Result.getResult(CommonError.METHOD_NOT_ALLOW.getCode(),
|
||||
e.getMethod() + "方法不允许");
|
||||
}
|
||||
|
||||
/**
|
||||
* 缺少请求参数
|
||||
*/
|
||||
@ExceptionHandler({MissingServletRequestParameterException.class, MissingServletRequestPartException.class})
|
||||
public Result missingParameterException(Exception e) {
|
||||
String message = CommonError.PARAM_WRONG.getMessage();
|
||||
if (e instanceof MissingServletRequestParameterException) {
|
||||
message = ((MissingServletRequestParameterException) e).getParameterName() + "不能为空";
|
||||
} else if (e instanceof MissingServletRequestPartException) {
|
||||
message = ((MissingServletRequestPartException) e).getRequestPartName() + "不能为空";
|
||||
}
|
||||
return Result.getResult(CommonError.PARAM_WRONG.getCode(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求参数格式错误
|
||||
*/
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public Result ConstraintViolationException(ConstraintViolationException e) {
|
||||
if (e.getConstraintViolations().size() > 0) {
|
||||
return Result.getResult(CommonError.PARAM_WRONG.getCode(),
|
||||
e.getConstraintViolations().iterator().next().getMessageTemplate());
|
||||
}
|
||||
return Result.getResult(CommonError.PARAM_WRONG);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.weilab.biology.core.exception;
|
||||
|
||||
import com.weilab.biology.core.data.vo.result.CommonError;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-04-30
|
||||
* Description : 网络异常
|
||||
*/
|
||||
public class NetworkException extends BaseException {
|
||||
public NetworkException() {
|
||||
super(CommonError.NETWORK_WRONG);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.weilab.biology.core.validation;
|
||||
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
|
||||
@Documented
|
||||
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(EnumValidation.List.class)
|
||||
@Constraint(validatedBy = {EnumValidator.class})
|
||||
public @interface EnumValidation {
|
||||
String message() default "{*.validation.constraint.Enum.message}";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
/**
|
||||
* the enum's class-type
|
||||
*
|
||||
* @return Class
|
||||
*/
|
||||
Class<?> clazz();
|
||||
|
||||
/**
|
||||
* the method's name ,which used to validate the enum's value
|
||||
*
|
||||
* @return method's name
|
||||
*/
|
||||
String method() default "getKey";
|
||||
|
||||
/**
|
||||
* 是否允许为空
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
boolean allowNull() default true;
|
||||
|
||||
/**
|
||||
* Defines several {@link EnumValidation} annotations on the same element.
|
||||
*
|
||||
* @see EnumValidation
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
|
||||
@Retention(RUNTIME)
|
||||
@interface List {
|
||||
EnumValidation[] value();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
package com.weilab.biology.core.validation;
|
||||
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Controller入参对象中属性枚举项校验
|
||||
*/
|
||||
public class EnumValidator implements ConstraintValidator<EnumValidation, Object> {
|
||||
|
||||
private EnumValidation annotation;
|
||||
|
||||
/**
|
||||
* Initializes the validator in preparation for
|
||||
* {@link #isValid(Object, ConstraintValidatorContext)} calls.
|
||||
* The constraint annotation for a given constraint declaration
|
||||
* is passed.
|
||||
* <p>
|
||||
* This method is guaranteed to be called before any use of this instance for
|
||||
* validation.
|
||||
* <p>
|
||||
* The default implementation is a no-op.
|
||||
*
|
||||
* @param constraintAnnotation annotation instance for a given constraint declaration
|
||||
*/
|
||||
@Override
|
||||
public void initialize(EnumValidation constraintAnnotation) {
|
||||
this.annotation = constraintAnnotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the validation logic.
|
||||
* The state of {@code value} must not be altered.
|
||||
* <p>
|
||||
* This method can be accessed concurrently, thread-safety must be ensured
|
||||
* by the implementation.
|
||||
*
|
||||
* @param value object to validate
|
||||
* @param context context in which the constraint is evaluated
|
||||
* @return {@code false} if {@code value} does not pass the constraint
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||
if (value == null) {
|
||||
return annotation.allowNull();
|
||||
}
|
||||
|
||||
Object[] objects = annotation.clazz().getEnumConstants();
|
||||
try {
|
||||
Method method = annotation.clazz().getMethod(annotation.method());
|
||||
for (Object o : objects) {
|
||||
if (value.equals(method.invoke(o))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
26
src/main/java/com/weilab/biology/mapper/JobMapper.java
Normal file
26
src/main/java/com/weilab/biology/mapper/JobMapper.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.weilab.biology.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.weilab.biology.core.data.dto.JobLessDto;
|
||||
import com.weilab.biology.core.data.po.Job;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.python.modules.itertools.count;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-09-19
|
||||
* Description :
|
||||
*/
|
||||
@Mapper
|
||||
public interface JobMapper extends BaseMapper<Job> {
|
||||
|
||||
List<Job> selectRunningJobs();
|
||||
|
||||
Job selectNextWaitingJob();
|
||||
|
||||
List<Job> selectJobList(@Param("type") Integer type,
|
||||
@Param("count") Integer count);
|
||||
|
||||
}
|
242
src/main/java/com/weilab/biology/service/JobService.java
Normal file
242
src/main/java/com/weilab/biology/service/JobService.java
Normal file
@ -0,0 +1,242 @@
|
||||
package com.weilab.biology.service;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.weilab.biology.core.data.dto.JobDto;
|
||||
import com.weilab.biology.core.data.dto.JobLessDto;
|
||||
import com.weilab.biology.core.data.enums.JobStatusEnum;
|
||||
import com.weilab.biology.core.data.po.Job;
|
||||
import com.weilab.biology.core.data.vo.result.CommonError;
|
||||
import com.weilab.biology.core.data.vo.result.Result;
|
||||
import com.weilab.biology.core.data.vo.result.error.JobError;
|
||||
import com.weilab.biology.mapper.JobMapper;
|
||||
import com.weilab.biology.util.FileUtils;
|
||||
import com.weilab.biology.util.MailUtil;
|
||||
import com.weilab.biology.util.TaskExecutorUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by skyyemperor on 2021-09-19
|
||||
* Description :
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class JobService {
|
||||
|
||||
@Value("${biology.python-cmd}")
|
||||
private String pythonCmd;
|
||||
|
||||
@Value("${biology.request-path}")
|
||||
private String requestPath;
|
||||
|
||||
@Value("${biology.result-path}")
|
||||
private String resultDataPath;
|
||||
|
||||
@Value("${biology.log-path}")
|
||||
private String logPath;
|
||||
|
||||
@Value("${biology.concurrent-num}")
|
||||
private Integer concurrentNum;
|
||||
|
||||
@Autowired
|
||||
private JobMapper jobMapper;
|
||||
|
||||
@Autowired
|
||||
private MailUtil mailUtil;
|
||||
|
||||
@Autowired
|
||||
private TaskExecutorUtil<?> taskExecutorUtil;
|
||||
|
||||
private static final String SUBJECT = "【DeepBIO Result Notice】";
|
||||
private static final String SUCCESS_EMAIL_CONTENT = "Your request has been completed, click http://server.wei-group.net/front/biology/#/resultMail?jobId=%s to check the detail information";
|
||||
private static final String FAIL_EMAIL_CONTENT = "We are very sorry, but some errors occurred in the task you submitted, click http://server.wei-group.net/front/biology/#/resultMail?jobId=%s to check the detail information";
|
||||
private static final String TIMEOUT_EMAIL_CONTENT = "We are very sorry, but the task you submitted was overtime, click http://server.wei-group.net/front/biology/#/resultMail?jobId=%s to check the detail information";
|
||||
private static final String RECEIVED_EMAIL_CONTENT = "Your request has been received, click http://server.wei-group.net/front/biology/#/resultMail?jobId=%s to check the detail information";
|
||||
private static final String START_RUNNING_EMAIL_CONTENT = "Your task has started running, click http://server.wei-group.net/front/biology/#/resultMail?jobId=%s to check the detail information";
|
||||
|
||||
/**
|
||||
* 提交job
|
||||
*
|
||||
* @param dataStr 文本内容
|
||||
* @param param 其他参数(json格式)
|
||||
* @param mail 邮箱
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Transactional
|
||||
public Result submit(String dataStr, BufferedInputStream dataStream, JSONObject param, String mail, Integer type) {
|
||||
Job job = new Job("", JSON.toJSONString(param), mail, JobStatusEnum.WAIT.getKey(), LocalDateTime.now(), type);
|
||||
jobMapper.insert(job);
|
||||
sendEmail(job.getJobId(), JobStatusEnum.WAIT, mail);
|
||||
|
||||
try {
|
||||
//将请求数据写入本地文件,之后向python传递文件路径参数
|
||||
String dataPath = String.format(requestPath + File.separator + "job-%d-dataStr.txt", job.getJobId());
|
||||
if (dataStream != null) {
|
||||
FileUtil.writeFromStream(dataStream, dataPath);
|
||||
} else {
|
||||
FileUtils.writeStringToFile(dataPath, dataStr);
|
||||
}
|
||||
|
||||
//将jobId和dataPath参数添加至param中
|
||||
param.put("jobId", job.getJobId());
|
||||
param.put("requestDataPath", dataPath);
|
||||
param.put("resultDataPath", resultDataPath);
|
||||
|
||||
//更新数据库param字段
|
||||
job.setParam(JSON.toJSONString(param));
|
||||
jobMapper.updateById(job);
|
||||
|
||||
runNextJob();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
updateJobStatus(job.getJobId(), JobStatusEnum.FAIL);
|
||||
}
|
||||
|
||||
return getJobInfo(job.getJobId());
|
||||
}
|
||||
|
||||
public Result getJobInfo(Integer jobId) {
|
||||
Job job = jobMapper.selectById(jobId);
|
||||
if (job == null)
|
||||
return Result.getResult(CommonError.CONTENT_NOT_FOUND);
|
||||
return Result.success(JobDto.parseJob(job));
|
||||
}
|
||||
|
||||
public Result updateJobStatus(Integer jobId, JobStatusEnum status) {
|
||||
return updateJobStatus(jobId, status, null);
|
||||
}
|
||||
|
||||
public Result updateJobStatus(Integer jobId, JobStatusEnum status, String result) {
|
||||
System.out.println(status.getRemark());
|
||||
Job job = jobMapper.selectById(jobId);
|
||||
if (job == null)
|
||||
return Result.getResult(CommonError.CONTENT_NOT_FOUND);
|
||||
|
||||
switch (status) {
|
||||
case WAIT:
|
||||
break;
|
||||
case SUCCESS:
|
||||
job.setCompleteTime(LocalDateTime.now());
|
||||
job.setResult(result);
|
||||
break;
|
||||
case FAIL:
|
||||
case TIMEOUT:
|
||||
if (!job.getStatus().equals(JobStatusEnum.RUNNING.getKey())
|
||||
&& !job.getStatus().equals(JobStatusEnum.REQED.getKey()))
|
||||
return Result.getResult(JobError.STATUS_UPDATE_FAIL);
|
||||
job.setCompleteTime(LocalDateTime.now());
|
||||
break;
|
||||
case RUNNING:
|
||||
if (!job.getStatus().equals(JobStatusEnum.REQED.getKey()))
|
||||
return Result.getResult(JobError.STATUS_UPDATE_FAIL);
|
||||
job.setCreateTime(LocalDateTime.now());
|
||||
break;
|
||||
}
|
||||
|
||||
job.setStatus(status.getKey());
|
||||
jobMapper.updateById(job);
|
||||
|
||||
sendEmail(jobId, status, job.getMail());
|
||||
runNextJob();
|
||||
|
||||
return getJobInfo(jobId);
|
||||
}
|
||||
|
||||
public Result getJobList(Integer type) {
|
||||
List<Job> jobs = jobMapper.selectJobList(type, 200);
|
||||
return Result.success(jobs.stream().map(JobLessDto::parseJob).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行下一个job
|
||||
*/
|
||||
private synchronized void runNextJob() {
|
||||
Job nextJob = null;
|
||||
try {
|
||||
//并发数小于concurrentNum,运行该job
|
||||
if (jobMapper.selectRunningJobs().size() < concurrentNum) {
|
||||
if ((nextJob = jobMapper.selectNextWaitingJob()) != null) {
|
||||
//更新job状态
|
||||
nextJob.setStatus(JobStatusEnum.REQED.getKey());
|
||||
jobMapper.updateById(nextJob);
|
||||
|
||||
waitRunning(nextJob.getJobId());
|
||||
|
||||
String logFilePath = String.format(logPath + File.separator + "task-log-%s.txt", nextJob.getJobId());
|
||||
String cmd = String.format("%s -setting '%s' >> %s 2>&1", pythonCmd, nextJob.getParam(), logFilePath);
|
||||
|
||||
log.info("执行命令: " + cmd);
|
||||
|
||||
String[] cmds = new String[]{"/bin/sh", "-c", cmd};
|
||||
|
||||
Runtime.getRuntime().exec(cmds);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (nextJob != null) {
|
||||
nextJob.setStatus(JobStatusEnum.FAIL.getKey());
|
||||
jobMapper.updateById(nextJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待运行,超时时间60秒
|
||||
*/
|
||||
private void waitRunning(Integer jobId) {
|
||||
//等待60秒,检查是否已运行
|
||||
taskExecutorUtil.schedule(() -> {
|
||||
Job job = jobMapper.selectById(jobId);
|
||||
if (job.getStatus().equals(JobStatusEnum.REQED.getKey())) {
|
||||
updateJobStatus(jobId, JobStatusEnum.FAIL);
|
||||
}
|
||||
}, 60, TimeUnit.SECONDS);
|
||||
|
||||
//等待4小时,查看是否执行完成
|
||||
taskExecutorUtil.schedule(() -> {
|
||||
Job job = jobMapper.selectById(jobId);
|
||||
if (job.getStatus().equals(JobStatusEnum.RUNNING.getKey())) {
|
||||
updateJobStatus(jobId, JobStatusEnum.TIMEOUT);
|
||||
}
|
||||
}, 4, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
private void sendEmail(Integer jobId, JobStatusEnum status, String mail) {
|
||||
String content = null;
|
||||
switch (status) {
|
||||
case WAIT:
|
||||
content = String.format(RECEIVED_EMAIL_CONTENT, jobId);
|
||||
break;
|
||||
case SUCCESS:
|
||||
content = String.format(SUCCESS_EMAIL_CONTENT, jobId);
|
||||
break;
|
||||
case FAIL:
|
||||
content = String.format(FAIL_EMAIL_CONTENT, jobId);
|
||||
break;
|
||||
case TIMEOUT:
|
||||
content = String.format(TIMEOUT_EMAIL_CONTENT, jobId);
|
||||
break;
|
||||
case RUNNING:
|
||||
content = String.format(START_RUNNING_EMAIL_CONTENT, jobId);
|
||||
break;
|
||||
}
|
||||
if (content != null)
|
||||
mailUtil.send(mail, SUBJECT, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
118
src/main/java/com/weilab/biology/util/FileUtils.java
Normal file
118
src/main/java/com/weilab/biology/util/FileUtils.java
Normal file
@ -0,0 +1,118 @@
|
||||
package com.weilab.biology.util;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* 文件读取工具类
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
/**
|
||||
* MultipartFile 转换成File
|
||||
*
|
||||
* @param multfile 原文件类型
|
||||
* @return File
|
||||
*/
|
||||
public static File multipartToFile(MultipartFile multfile) {
|
||||
File file = null;
|
||||
try {
|
||||
file = File.createTempFile("prefix", "_" + multfile.getOriginalFilename());
|
||||
multfile.transferTo(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容,作为字符串返回
|
||||
*/
|
||||
public static String readFileAsString(String filePath) throws IOException {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(filePath);
|
||||
}
|
||||
|
||||
return readFileAsString(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容,作为字符串返回
|
||||
*/
|
||||
public static String readFileAsString(File file) throws IOException {
|
||||
StringBuilder sb = new StringBuilder((int) (file.length()));
|
||||
// 创建字节输入流
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
// 创建一个长度为10240的Buffer
|
||||
byte[] bbuf = new byte[10240];
|
||||
// 用于保存实际读取的字节数
|
||||
int hasRead = 0;
|
||||
while ((hasRead = fis.read(bbuf)) > 0) {
|
||||
sb.append(new String(bbuf, 0, hasRead));
|
||||
}
|
||||
fis.close();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件路径读取byte[] 数组
|
||||
*/
|
||||
public static byte[] readFileAsBytes(String filePath) throws IOException {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(filePath);
|
||||
} else {
|
||||
return readFileAsBytes(file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件读取byte[]数组
|
||||
*/
|
||||
public static byte[] readFileAsBytes(File file) throws IOException {
|
||||
if (file == null) {
|
||||
throw new FileNotFoundException();
|
||||
} else {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
|
||||
BufferedInputStream in = null;
|
||||
|
||||
try {
|
||||
in = new BufferedInputStream(new FileInputStream(file));
|
||||
short bufSize = 1024;
|
||||
byte[] buffer = new byte[bufSize];
|
||||
int len1;
|
||||
while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
|
||||
bos.write(buffer, 0, len1);
|
||||
}
|
||||
|
||||
return bos.toByteArray();
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException var14) {
|
||||
var14.printStackTrace();
|
||||
}
|
||||
bos.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void writeStringToFile(String filePath, String content) throws IOException {
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath));
|
||||
bufferedOutputStream.write(content.getBytes());
|
||||
bufferedOutputStream.flush();
|
||||
bufferedOutputStream.close();
|
||||
}
|
||||
|
||||
public static void writeByteToFile(String filePath, byte[] content) throws IOException {
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath));
|
||||
bufferedOutputStream.write(content);
|
||||
bufferedOutputStream.flush();
|
||||
bufferedOutputStream.close();
|
||||
}
|
||||
}
|
39
src/main/java/com/weilab/biology/util/MailUtil.java
Normal file
39
src/main/java/com/weilab/biology/util/MailUtil.java
Normal file
@ -0,0 +1,39 @@
|
||||
package com.weilab.biology.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MailUtil {
|
||||
@Autowired
|
||||
private JavaMailSenderImpl mailSender;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String sender;
|
||||
|
||||
@Autowired
|
||||
private TaskExecutorUtil<?> taskExecutorUtil;
|
||||
|
||||
public void send(String receiver, String subject, String content) {
|
||||
taskExecutorUtil.run(() -> {
|
||||
SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setSubject(subject);//设置标题
|
||||
message.setText(content);//设置内容
|
||||
|
||||
message.setTo(receiver);
|
||||
message.setFrom(sender);
|
||||
|
||||
mailSender.send(message);
|
||||
|
||||
log.info("Mail已发送: " + message);
|
||||
});
|
||||
}
|
||||
}
|
43
src/main/java/com/weilab/biology/util/TaskExecutorUtil.java
Normal file
43
src/main/java/com/weilab/biology/util/TaskExecutorUtil.java
Normal file
@ -0,0 +1,43 @@
|
||||
package com.weilab.biology.util;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
@Component
|
||||
public class TaskExecutorUtil<C> {
|
||||
|
||||
public TaskExecutorUtil() {
|
||||
|
||||
}
|
||||
|
||||
private static final ThreadFactory factory = new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r);
|
||||
}
|
||||
};
|
||||
|
||||
private static final ThreadPoolExecutor cachePool = new ThreadPoolExecutor(
|
||||
2, 40,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>(),
|
||||
factory
|
||||
);
|
||||
|
||||
private static final ScheduledExecutorService scheduledThreadPool =
|
||||
Executors.newScheduledThreadPool(5);
|
||||
|
||||
public void run(Runnable r) {
|
||||
cachePool.execute(r);
|
||||
}
|
||||
|
||||
public Future<C> submit(Callable<C> c) {
|
||||
return cachePool.submit(c);
|
||||
}
|
||||
|
||||
public void schedule(Runnable runnable, long delay, TimeUnit timeUnit) {
|
||||
scheduledThreadPool.schedule(runnable, delay, timeUnit);
|
||||
}
|
||||
|
||||
}
|
59
src/main/resources/mapper/JobMapper.xml
Normal file
59
src/main/resources/mapper/JobMapper.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.weilab.biology.mapper.JobMapper">
|
||||
<resultMap id="BaseResultMap" type="com.weilab.biology.core.data.po.Job">
|
||||
<id column="job_id" jdbcType="INTEGER" property="jobId"/>
|
||||
<result column="data" jdbcType="LONGVARCHAR" property="data"/>
|
||||
<result column="param" jdbcType="VARCHAR" property="param"/>
|
||||
<result column="mail" jdbcType="VARCHAR" property="mail"/>
|
||||
<result column="result" jdbcType="LONGVARCHAR" property="result"/>
|
||||
<result column="status" jdbcType="TINYINT" property="status"/>
|
||||
<result column="request_time" jdbcType="TIMESTAMP" property="requestTime"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
<result column="complete_time" jdbcType="TIMESTAMP" property="completeTime"/>
|
||||
<result column="type" jdbcType="INTEGER" property="type"/>
|
||||
</resultMap>
|
||||
<sql id="JobSql">
|
||||
SELECT job_id,
|
||||
`data`,
|
||||
param,
|
||||
mail,
|
||||
result,
|
||||
`status`,
|
||||
request_time,
|
||||
create_time,
|
||||
complete_time,
|
||||
type
|
||||
FROM job
|
||||
</sql>
|
||||
<sql id="JobLessSql">
|
||||
SELECT job_id,
|
||||
`status`,
|
||||
request_time,
|
||||
create_time,
|
||||
complete_time,
|
||||
type
|
||||
FROM job
|
||||
</sql>
|
||||
<select id="selectRunningJobs" resultMap="BaseResultMap">
|
||||
<include refid="JobSql"/>
|
||||
WHERE status = '${@com.weilab.biology.core.data.enums.JobStatusEnum@RUNNING.getKey()}'
|
||||
OR status = '${@com.weilab.biology.core.data.enums.JobStatusEnum@REQED.getKey()}'
|
||||
</select>
|
||||
<select id="selectNextWaitingJob" resultMap="BaseResultMap">
|
||||
<include refid="JobSql"/>
|
||||
WHERE status = '${@com.weilab.biology.core.data.enums.JobStatusEnum@WAIT.getKey()}'
|
||||
ORDER BY request_time
|
||||
LIMIT 1
|
||||
</select>
|
||||
<select id="selectJobList" resultMap="BaseResultMap">
|
||||
<include refid="JobLessSql"/>
|
||||
<where>
|
||||
<if test="type != null">
|
||||
AND type = #{type}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY job_id DESC
|
||||
LIMIT #{count}
|
||||
</select>
|
||||
</mapper>
|
@ -0,0 +1,13 @@
|
||||
package com.weilab.biology;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class BiologyApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
載入中…
x
新增問題並參考
Block a user