init
This commit is contained in:
		
							
								
								
									
										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() { | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user