From 6f0a0ce3c12d12f453eeb43fe331fbffa12acde5 Mon Sep 17 00:00:00 2001 From: yurui Date: Sat, 6 May 2023 11:46:53 +0800 Subject: [PATCH] v2 --- .../biology/config/StaticScheduleTask.java | 27 +++ .../biology/controller/JobController.java | 177 +++++++------- .../biology/core/data/dto/AppConfigDto.java | 94 ++++++++ .../weilab/biology/core/data/dto/JobDto.java | 8 +- .../core/data/enums/JobStatusEnum.java | 25 +- .../biology/core/data/po/AppConfig.java | 70 ++++++ .../com/weilab/biology/core/data/po/Job.java | 17 +- .../core/data/vo/result/error/JobError.java | 5 +- .../biology/mapper/AppConfigMapper.java | 19 ++ .../com/weilab/biology/mapper/JobMapper.java | 11 +- .../biology/service/AppConfigService.java | 35 +++ .../weilab/biology/service/JobService.java | 215 ++++++++---------- src/main/resources/mapper/AppConfigMapper.xml | 28 +++ src/main/resources/mapper/JobMapper.xml | 37 ++- 14 files changed, 494 insertions(+), 274 deletions(-) create mode 100644 src/main/java/com/weilab/biology/config/StaticScheduleTask.java create mode 100644 src/main/java/com/weilab/biology/core/data/dto/AppConfigDto.java create mode 100644 src/main/java/com/weilab/biology/core/data/po/AppConfig.java create mode 100644 src/main/java/com/weilab/biology/mapper/AppConfigMapper.java create mode 100644 src/main/java/com/weilab/biology/service/AppConfigService.java create mode 100644 src/main/resources/mapper/AppConfigMapper.xml diff --git a/src/main/java/com/weilab/biology/config/StaticScheduleTask.java b/src/main/java/com/weilab/biology/config/StaticScheduleTask.java new file mode 100644 index 0000000..0c2279a --- /dev/null +++ b/src/main/java/com/weilab/biology/config/StaticScheduleTask.java @@ -0,0 +1,27 @@ +package com.weilab.biology.config; + +import com.weilab.biology.service.JobService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + + +@Configuration +@EnableScheduling +@Slf4j +public class StaticScheduleTask { + + @Autowired + private JobService jobService; + + /** + * 一分钟执行一次 + */ + @Scheduled(fixedRate = 60 * 1000L) + public void crawlStudyRoomFrequently() { + jobService.runJob(); + } + +} diff --git a/src/main/java/com/weilab/biology/controller/JobController.java b/src/main/java/com/weilab/biology/controller/JobController.java index 44b6a2b..21d3ebd 100644 --- a/src/main/java/com/weilab/biology/controller/JobController.java +++ b/src/main/java/com/weilab/biology/controller/JobController.java @@ -3,9 +3,10 @@ package com.weilab.biology.controller; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.weilab.biology.core.data.dto.AppConfigDto; import com.weilab.biology.core.data.dto.JobDto; import com.weilab.biology.core.data.enums.JobStatusEnum; import com.weilab.biology.core.data.po.Job; @@ -14,18 +15,19 @@ 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.mapper.JobMapper; +import com.weilab.biology.service.AppConfigService; 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.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import java.io.BufferedInputStream; -import java.io.IOException; import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.List; @RestController @@ -35,6 +37,9 @@ public class JobController { @Autowired private JobService jobService; + @Autowired + private AppConfigService appConfigService; + @Autowired private JobMapper jobMapper; @@ -42,66 +47,20 @@ public class JobController { private String requestPath; @PostMapping("/submit") - public Result submit(@RequestParam(required = false) Long jobId, + public Result submit(@RequestParam String appName, + @RequestParam(required = false) Long jobId, @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)) { + @RequestParam(defaultValue = "0") Integer type) { + if (dataFile == null && StrUtil.isBlank(dataStr)) { return Result.getResult(JobError.PARAM_CAN_NOT_BE_EMPTY); } - // 解析param字符串为map对象 - JSONObject reqParamMap = null; - try { - reqParamMap = JSON.parseObject(param); - } catch (Exception e) { - return Result.getResult(CommonError.PARAM_WRONG); - } - - // 写入file文件,并将文件路径放入paramMap - try { - List 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); - reqParamMap.put(file.getName(), filePath); - } - } - } catch (IOException e) { - e.printStackTrace(); - return Result.getResult(JobError.FILE_READ_FAIL); - } - - // 将请求数据file转为InputStream - BufferedInputStream dataStream = null; - if (dataFile != null) { - dataStream = FileUtil.getInputStream(FileUtils.multipartToFile(dataFile)); + AppConfigDto appConfig = appConfigService.getAppConfig(appName); + if (appConfig == null) { + return Result.getResult(JobError.APP_NOT_FOUND); } Job job = null; @@ -115,32 +74,46 @@ public class JobController { job = new Job(); } - if (!StrUtil.isEmpty(job.getParam())) { - JSONObject params = JSON.parseObject(job.getParam()); - params.putAll(reqParamMap); - reqParamMap = params; - job.setParam(JSON.toJSONString(reqParamMap)); + // 解析param字符串为map对象 + JSONObject reqParamMap = null; + try { + reqParamMap = JSON.parseObject(param); + } catch (Exception e) { + return Result.getResult(CommonError.PARAM_WRONG); } - job.setData(""); + // 将reqParam覆盖添加到原有job的param中 + JSONObject params = JSON.parseObject(job.getParam()); + params.putAll(reqParamMap); + job.setParam(JSON.toJSONString(params)); + job.setAppId(appConfig.getAppId()); job.setMail(mail); - job.setStatus(JobStatusEnum.WAIT.getKey()); - job.setRequestTime(LocalDateTime.now()); + job.setStatus(JobStatusEnum.WAITING.getKey()); + job.setCreateTime(LocalDateTime.now()); job.setType(type); - return jobService.submit(dataStr, dataStream, reqParamMap, job); + return jobService.submit(appConfig, dataStr, dataFile, reqParamMap, job); } @PostMapping("/create") - public Result createJob() { + public Result createJob(@RequestParam String appName) { + AppConfigDto appConfig = appConfigService.getAppConfig(appName); + if (appConfig == null) { + return Result.getResult(JobError.APP_NOT_FOUND); + } + Job job = new Job(); + job.setAppId(appConfig.getAppId()); job.setParam("{}"); job.setStatus(JobStatusEnum.CREATING.getKey()); - job.setRequestTime(LocalDateTime.now()); + job.setCreateTime(LocalDateTime.now()); jobMapper.insert(job); return Result.success(JobDto.parseJob(job)); } + /** + * 上传文件接口,将文件地址保存至本地,并将路径放入参数中 + */ @PostMapping("/file/upload") public Result uploadFile(@RequestParam Long jobId, @RequestParam String fileKey, @@ -152,43 +125,44 @@ public class JobController { JSONObject params = JSON.parseObject(job.getParam()); try { - if (file != null) { - String extName = FileUtil.extName(file.getOriginalFilename()); - if (!StrUtil.isEmpty(extName)) { - extName = StrUtil.DOT + extName; - } - String filePath = requestPath + FileUtil.FILE_SEPARATOR + jobId + FileUtil.FILE_SEPARATOR - + IdUtil.fastUUID() + extName; - FileUtil.writeFromStream(file.getInputStream(), filePath); - params.put(fileKey, filePath); - - job.setParam(JSON.toJSONString(params)); - jobMapper.updateById(job); - return Result.success(JobDto.parseJob(job)); + String extName = FileUtil.extName(file.getOriginalFilename()); + if (!StrUtil.isEmpty(extName)) { + extName = StrUtil.DOT + extName; } + String filePath = requestPath + FileUtil.FILE_SEPARATOR + jobId + FileUtil.FILE_SEPARATOR + + IdUtil.fastUUID() + extName; + FileUtil.writeFromStream(file.getInputStream(), filePath); + params.put(fileKey, filePath); + + job.setParam(JSON.toJSONString(params)); + jobMapper.updateById(job); + return Result.success(JobDto.parseJob(job)); } catch (Exception e) { e.printStackTrace(); + return Result.fail(); } - return Result.fail(); } @PostMapping("/status/update") public Result updateJobStatus(@RequestParam Integer jobId, - @EnumValidation(clazz = JobStatusEnum.class, message = "没有此状态") - @RequestParam Integer status, + @EnumValidation(clazz = JobStatusEnum.class, message = "没有此状态") @RequestParam Integer status, @RequestParam(required = false) String result) { - if (status.equals(JobStatusEnum.WAIT.getKey())) + if (status.equals(JobStatusEnum.WAITING.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) { + if (!JSONUtil.isJson(result)) { return Result.getResult(CommonError.PARAM_WRONG); } } - return jobService.updateJobStatus(jobId, JobStatusEnum.getEnumByKey(status), result); + + Job job = jobMapper.selectById(jobId); + if (job == null) { + return Result.getResult(CommonError.CONTENT_NOT_FOUND); + } + + return jobService.updateJobStatus(job, JobStatusEnum.getEnumByKey(status), result); } @GetMapping("/info/{jobId}") @@ -196,16 +170,17 @@ public class JobController { return jobService.getJobInfo(jobId); } - @GetMapping("/list") - public Result getJobList(@RequestParam(required = false) Integer type) { - return jobService.getJobList(type); - } - @GetMapping("/list/v2") - public Result getJobList(@RequestParam(required = false) Integer type, + public Result getJobList(@RequestParam String appName, + @RequestParam(required = false) Integer type, @RequestParam(defaultValue = "true") Boolean filterCreating, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "100") Integer size) { - return jobService.getJobList(type, filterCreating, page, size); + AppConfigDto appConfig = appConfigService.getAppConfig(appName); + if (appConfig == null) { + return Result.getResult(JobError.APP_NOT_FOUND); + } + + return jobService.getJobList(appConfig.getAppId(), type, filterCreating, page, size); } } diff --git a/src/main/java/com/weilab/biology/core/data/dto/AppConfigDto.java b/src/main/java/com/weilab/biology/core/data/dto/AppConfigDto.java new file mode 100644 index 0000000..3e7d238 --- /dev/null +++ b/src/main/java/com/weilab/biology/core/data/dto/AppConfigDto.java @@ -0,0 +1,94 @@ +package com.weilab.biology.core.data.dto; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.weilab.biology.core.data.po.AppConfig; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.File; +import java.io.Serializable; + +/** + * 应用配置信息Dto + * + * @author yurui + * @date 2023/5/5 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class AppConfigDto implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 应用ID + */ + private Integer appId; + + /** + * 应用名 + */ + private String appName; + + /** + * 并发数 + */ + private Integer concurrentNum; + + /** + * 命令行 + */ + private String cmd; + + /** + * 任务超时时间(单位:分钟) + */ + private Long timeout; + + /** + * 放置文件的路径 + */ + private String localPath; + + /** + * 放置请求参数文件的路径 + */ + private String requestPath; + + /** + * 放置结果文件的路径 + */ + private String resultPath; + + /** + * 放置日志文件的路径 + */ + private String logPath; + + /** + * 邮件格式(json格式:{"subject":"**","success":"**"}) + */ + private JSONObject emailTemplate; + + + public static AppConfigDto parse(AppConfig o) { + if (o == null) { + return null; + } + return new AppConfigDto() + .setAppId(o.getAppId()) + .setAppName(o.getAppName()) + .setConcurrentNum(o.getConcurrentNum()) + .setCmd(o.getCmd()) + .setTimeout(o.getTimeout()) + .setLocalPath(o.getLocalPath()) + .setRequestPath(o.getLocalPath() + File.separator + "request" + File.separator) + .setResultPath(o.getLocalPath() + File.separator + "result" + File.separator) + .setLogPath(o.getLocalPath() + File.separator + "log" + File.separator) + .setEmailTemplate(JSON.parseObject(o.getEmailTemplate())); + } + +} diff --git a/src/main/java/com/weilab/biology/core/data/dto/JobDto.java b/src/main/java/com/weilab/biology/core/data/dto/JobDto.java index 971f14c..1a209f4 100644 --- a/src/main/java/com/weilab/biology/core/data/dto/JobDto.java +++ b/src/main/java/com/weilab/biology/core/data/dto/JobDto.java @@ -2,9 +2,6 @@ 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; @@ -12,12 +9,15 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.time.LocalDateTime; @Data @AllArgsConstructor @NoArgsConstructor -public class JobDto { +public class JobDto implements Serializable { + + private static final long serialVersionUID = 1L; /** * jobId diff --git a/src/main/java/com/weilab/biology/core/data/enums/JobStatusEnum.java b/src/main/java/com/weilab/biology/core/data/enums/JobStatusEnum.java index bcfa664..ce3e564 100644 --- a/src/main/java/com/weilab/biology/core/data/enums/JobStatusEnum.java +++ b/src/main/java/com/weilab/biology/core/data/enums/JobStatusEnum.java @@ -6,16 +6,37 @@ import java.util.HashMap; import java.util.Map; /** - * 校区的枚举类 + * 任务状态的枚举类 */ @Getter public enum JobStatusEnum { - WAIT(0, "waiting"), + /** + * 等待运行 + */ + WAITING(0, "waiting"), + /** + * 正在运行 + */ RUNNING(1, "running"), + /** + * 任务执行成功 + */ SUCCESS(2, "success"), + /** + * 已发送请求 + */ REQED(3, "requested"), + /** + * 任务执行失败 + */ FAIL(-1, "failed"), + /** + * 运行超时 + */ TIMEOUT(-2, "timeout"), + /** + * 正在创建 + */ CREATING(-3, "creating"), ; diff --git a/src/main/java/com/weilab/biology/core/data/po/AppConfig.java b/src/main/java/com/weilab/biology/core/data/po/AppConfig.java new file mode 100644 index 0000000..2b6befd --- /dev/null +++ b/src/main/java/com/weilab/biology/core/data/po/AppConfig.java @@ -0,0 +1,70 @@ +package com.weilab.biology.core.data.po; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + *

+ * 应用配置信息 + *

+ * + * @author skyyemperor + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("app_config") +public class AppConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 应用ID + */ + @TableId(value = "app_id", type = IdType.AUTO) + private Integer appId; + + /** + * 应用名 + */ + @TableField("app_name") + private String appName; + + /** + * 并发数 + */ + @TableField("concurrent_num") + private Integer concurrentNum; + + /** + * 命令行 + */ + @TableField("cmd") + private String cmd; + + /** + * 任务超时时间(单位:分钟) + */ + @TableField("timeout") + private Long timeout; + + /** + * 放置文件的路径 + */ + @TableField("local_path") + private String localPath; + + /** + * 邮件格式(json格式:{"subject":"**","success":"**"}) + */ + @TableField("email_template") + private String emailTemplate; + + +} diff --git a/src/main/java/com/weilab/biology/core/data/po/Job.java b/src/main/java/com/weilab/biology/core/data/po/Job.java index 9a52746..87a4e30 100644 --- a/src/main/java/com/weilab/biology/core/data/po/Job.java +++ b/src/main/java/com/weilab/biology/core/data/po/Job.java @@ -4,17 +4,13 @@ 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; +import java.io.Serializable; +import java.time.LocalDateTime; + /** * Created by skyyemperor on 2021-09-19 * Description : @@ -31,10 +27,10 @@ public class Job implements Serializable { private Integer jobId; /** - * 基因序列数据 + * 应用ID */ - @TableField(value = "`data`") - private String data; + @TableField(value = "app_id") + private Integer appId; /** * 请求参数 @@ -85,7 +81,6 @@ public class Job implements Serializable { 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; diff --git a/src/main/java/com/weilab/biology/core/data/vo/result/error/JobError.java b/src/main/java/com/weilab/biology/core/data/vo/result/error/JobError.java index 0347ae3..e3a95f1 100644 --- a/src/main/java/com/weilab/biology/core/data/vo/result/error/JobError.java +++ b/src/main/java/com/weilab/biology/core/data/vo/result/error/JobError.java @@ -9,8 +9,9 @@ import com.weilab.biology.core.data.vo.result.ResultError; public enum JobError implements ResultError { PARAM_CAN_NOT_BE_EMPTY(40100, "文本框和文件不能同时为空"), FILE_READ_FAIL(40101, "文件读取出错"), - STATUS_UPDATE_FAIL(40102,"当前状态下不允许更新为指定状态"), - JOB_NOT_FOUND(40103,"当前状态下不允许更新为指定状态"), + STATUS_UPDATE_FAIL(40102, "当前状态下不允许更新为指定状态"), + JOB_NOT_FOUND(40103, "任务不存在"), + APP_NOT_FOUND(40104, "应用不存在"), ; private int code; diff --git a/src/main/java/com/weilab/biology/mapper/AppConfigMapper.java b/src/main/java/com/weilab/biology/mapper/AppConfigMapper.java new file mode 100644 index 0000000..c86c3ab --- /dev/null +++ b/src/main/java/com/weilab/biology/mapper/AppConfigMapper.java @@ -0,0 +1,19 @@ +package com.weilab.biology.mapper; + +import com.weilab.biology.core.data.po.AppConfig; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +/** + *

+ * 应用配置信息 Mapper 接口 + *

+ * + * @author + * @since 2023-05-05 + */ +public interface AppConfigMapper extends BaseMapper { + + AppConfig getAppConfigByAppName(@Param("appName") String appName); + +} diff --git a/src/main/java/com/weilab/biology/mapper/JobMapper.java b/src/main/java/com/weilab/biology/mapper/JobMapper.java index e2ea8d1..9ff9523 100644 --- a/src/main/java/com/weilab/biology/mapper/JobMapper.java +++ b/src/main/java/com/weilab/biology/mapper/JobMapper.java @@ -14,14 +14,13 @@ import java.util.List; @Mapper public interface JobMapper extends BaseMapper { - List selectRunningJobs(); + List selectRunningJobs(@Param("appId") Integer appId); - Job selectNextWaitingJob(); + List selectWaitingJobs(@Param("appId") Integer appId, @Param("size") Integer size); - List selectJobList(@Param("type") Integer type); - - List selectJobListByPage(@Param("type") Integer type, - @Param("filterCreating") Boolean filterCreating, + List selectJobListByPage(@Param("appId") Integer appId, + @Param("type") Integer type, + @Param("filterCreating") Boolean filterCreating, @Param("offset") Integer offset, @Param("count") Integer count); diff --git a/src/main/java/com/weilab/biology/service/AppConfigService.java b/src/main/java/com/weilab/biology/service/AppConfigService.java new file mode 100644 index 0000000..6235d64 --- /dev/null +++ b/src/main/java/com/weilab/biology/service/AppConfigService.java @@ -0,0 +1,35 @@ +package com.weilab.biology.service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.weilab.biology.core.data.dto.AppConfigDto; +import com.weilab.biology.core.data.po.AppConfig; +import com.weilab.biology.mapper.AppConfigMapper; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Service; + +/** + *

+ * 应用配置信息 服务实现类 + *

+ * + * @author + * @since 2023-05-05 + */ +@Service +public class AppConfigService extends ServiceImpl { + + @Mapper + private AppConfigMapper appConfigMapper; + + /** + * 通过appName获取应用配置 + * @param appName 唯一应用名 + * @return AppConfigDto + */ + public AppConfigDto getAppConfig( String appName) { + return AppConfigDto.parse(appConfigMapper.getAppConfigByAppName(appName)); + } + + + +} diff --git a/src/main/java/com/weilab/biology/service/JobService.java b/src/main/java/com/weilab/biology/service/JobService.java index dff1531..5264bb9 100644 --- a/src/main/java/com/weilab/biology/service/JobService.java +++ b/src/main/java/com/weilab/biology/service/JobService.java @@ -1,11 +1,15 @@ package com.weilab.biology.service; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.weilab.biology.core.data.dto.AppConfigDto; 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.AppConfig; 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; @@ -19,9 +23,9 @@ 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 org.springframework.web.multipart.MultipartFile; -import java.io.BufferedInputStream; -import java.io.File; +import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; @@ -34,22 +38,7 @@ import java.util.stream.Collectors; */ @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; +public class JobService extends ServiceImpl { @Autowired private JobMapper jobMapper; @@ -60,79 +49,66 @@ public class JobService { @Autowired private TaskExecutorUtil taskExecutorUtil; - @Value("${email.subject}") - private String SUBJECT; - - @Value("${email.content.success}") - private String SUCCESS_EMAIL_CONTENT; - - @Value("${email.content.fail}") - private String FAIL_EMAIL_CONTENT; - - @Value("${email.content.timeout}") - private String TIMEOUT_EMAIL_CONTENT; - - @Value("${email.content.received}") - private String RECEIVED_EMAIL_CONTENT; - - @Value("${email.content.running}") - private String START_RUNNING_EMAIL_CONTENT; + @Autowired + private AppConfigService appConfigService; /** * 提交job */ @Transactional - public Result submit(String dataStr, BufferedInputStream dataStream, JSONObject param, Job job) { - jobMapper.insert(job); - sendEmail(job, JobStatusEnum.WAIT, job.getMail()); + public Result submit(AppConfigDto appConfig, String dataStr, MultipartFile dataFile, JSONObject param, Job job) { + this.saveOrUpdate(job); try { - //将请求数据写入本地文件,之后向python传递文件路径参数 - String dataPath = String.format(requestPath + File.separator + "job-%d-dataStr.txt", job.getJobId()); - if (dataStream != null) { - FileUtil.writeFromStream(dataStream, dataPath); - } else { + // dataStr和dataFile均为蛋白质序列,两者含义相同只保留其一,dataFile的优先级大于dataStr + // 若dataFile为空,则将dataStr写入本地文件,之后向python传递文件路径参数 + String dataPath = String.format(appConfig.getRequestPath() + "job-%d-dataStr.txt", job.getJobId()); + if (dataFile != null) { + FileUtil.writeFromStream(dataFile.getInputStream(), dataPath); + } else if (!StrUtil.isBlank(dataStr)) { FileUtils.writeStringToFile(dataPath, dataStr); } //将jobId和dataPath参数添加至param中 param.put("jobId", job.getJobId()); param.put("requestDataPath", dataPath); - param.put("resultDataPath", resultDataPath); + param.put("resultDataPath", appConfig.getResultPath()); - //更新数据库param字段 + // 更新数据库param字段 job.setParam(JSON.toJSONString(param)); - jobMapper.updateById(job); - - runNextJob(); + this.saveOrUpdate(job); + updateJobStatus(job, JobStatusEnum.WAITING); } catch (Exception e) { e.printStackTrace(); - updateJobStatus(job.getJobId(), JobStatusEnum.FAIL); + updateJobStatus(job, 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); } + return Result.success(JobDto.parseJob(job)); + } + public Result updateJobStatus(Job job, JobStatusEnum status) { + return updateJobStatus(job, status, null); + } + + /** + * 更新任务状态 + * + * @param job 任务 + * @param status 状态 + * @param result 任务运行结果 + */ + public Result updateJobStatus(Job job, JobStatusEnum status, String result) { switch (status) { - case WAIT: + case WAITING: + case CREATING: break; case SUCCESS: job.setCompleteTime(LocalDateTime.now()); @@ -141,7 +117,7 @@ public class JobService { case FAIL: case TIMEOUT: if (!job.getStatus().equals(JobStatusEnum.RUNNING.getKey()) - && !job.getStatus().equals(JobStatusEnum.REQED.getKey())){ + && !job.getStatus().equals(JobStatusEnum.REQED.getKey())) { return Result.getResult(JobError.STATUS_UPDATE_FAIL); } job.setCompleteTime(LocalDateTime.now()); @@ -150,7 +126,7 @@ public class JobService { if (!job.getStatus().equals(JobStatusEnum.REQED.getKey())) { return Result.getResult(JobError.STATUS_UPDATE_FAIL); } - job.setCreateTime(LocalDateTime.now()); + job.setRequestTime(LocalDateTime.now()); break; } @@ -158,104 +134,91 @@ public class JobService { jobMapper.updateById(job); sendEmail(job, status, job.getMail()); - runNextJob(); - return getJobInfo(jobId); + return getJobInfo(job.getJobId()); } - public Result getJobList(Integer type) { - List jobs = jobMapper.selectJobList(type); - return Result.success(jobs.stream().map(JobLessDto::parseJob).collect(Collectors.toList())); - } - - public Result getJobList(Integer type, Boolean filterCreating, Integer page, Integer size) { - List jobs = jobMapper.selectJobListByPage(type, filterCreating, (page - 1) * size, size); + public Result getJobList(Integer appId, Integer type, Boolean filterCreating, Integer page, Integer size) { + List jobs = jobMapper.selectJobListByPage(appId, type, filterCreating, (page - 1) * size, size); return Result.success(jobs.stream().map(JobLessDto::parseJob).collect(Collectors.toList())); } /** - * 运行下一个job + * 运行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); - } + public synchronized void runJob() { + List appConfigList = appConfigService.list().stream() + .map(AppConfigDto::parse) + .collect(Collectors.toList()); + appConfigList.forEach(appConfig -> { + //并发数小于concurrentNum,运行job + int size = appConfig.getConcurrentNum() - jobMapper.selectRunningJobs(appConfig.getAppId()).size(); + if (size <= 0) { + return; } + + jobMapper.selectWaitingJobs(appConfig.getAppId(), size) + .forEach(job -> runJob(appConfig, job)); + }); + } + + private void runJob(AppConfigDto appConfig, Job job) { + try { + String logFilePath = String.format(appConfig.getLogPath() + "task-log-%s.txt", job.getJobId()); + String cmd = String.format("%s -setting '%s' >> %s 2>&1", appConfig.getCmd(), job.getParam(), logFilePath); + String[] cmds = new String[]{"/bin/sh", "-c", cmd}; + Runtime.getRuntime().exec(cmds); + log.info("执行命令: " + cmd); + + // 更新job状态 + updateJobStatus(job, JobStatusEnum.REQED); + + // 异步检测超时 + asyncScheduleTask(appConfig, job.getJobId()); } catch (Exception e) { e.printStackTrace(); - if (nextJob != null) { - nextJob.setStatus(JobStatusEnum.FAIL.getKey()); - jobMapper.updateById(nextJob); - } + updateJobStatus(job, JobStatusEnum.FAIL); } } /** - * 等待运行 + * 开启异步定时任务,校验超时任务 */ - private void waitRunning(Integer jobId) { + private void asyncScheduleTask(AppConfigDto appConfig, Integer jobId) { //等待120秒,检查是否已运行 taskExecutorUtil.schedule(() -> { Job job = jobMapper.selectById(jobId); if (job.getStatus().equals(JobStatusEnum.REQED.getKey())) { - updateJobStatus(jobId, JobStatusEnum.FAIL); + updateJobStatus(job, JobStatusEnum.FAIL); } }, 120, TimeUnit.SECONDS); - //等待14小时,查看是否执行完成 + //等待一段时间后,查看是否执行完成 taskExecutorUtil.schedule(() -> { Job job = jobMapper.selectById(jobId); if (job.getStatus().equals(JobStatusEnum.RUNNING.getKey())) { - updateJobStatus(jobId, JobStatusEnum.TIMEOUT); + updateJobStatus(job, JobStatusEnum.TIMEOUT); } - }, 14, TimeUnit.HOURS); + }, appConfig.getTimeout(), TimeUnit.MINUTES); } private void sendEmail(Job job, JobStatusEnum status, String mail) { + AppConfigDto appConfig = AppConfigDto.parse(appConfigService.getById(job.getAppId())); + if (appConfig == null) { + return; + } + // 多个参数 String param1 = job.getRequestTime().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + job.getJobId(); String param2 = param1; - String content = null; - switch (status) { - case WAIT: - content = RECEIVED_EMAIL_CONTENT; - break; - case SUCCESS: - content = SUCCESS_EMAIL_CONTENT; - break; - case FAIL: - content = FAIL_EMAIL_CONTENT; - break; - case TIMEOUT: - content = TIMEOUT_EMAIL_CONTENT; - break; - case RUNNING: - content = START_RUNNING_EMAIL_CONTENT; - break; + String content = appConfig.getEmailTemplate().getString(status.getRemark()); + String subject = appConfig.getEmailTemplate().getString("subject"); + if (StrUtil.isBlank(content) || StrUtil.isBlank(subject)) { + return; } - - if (content != null) { - mailUtil.send(mail, String.format(SUBJECT, param1), String.format(content, param1, param2)); - } + mailUtil.send(mail, String.format(subject, param1), String.format(content, param1, param2)); } } diff --git a/src/main/resources/mapper/AppConfigMapper.xml b/src/main/resources/mapper/AppConfigMapper.xml new file mode 100644 index 0000000..771e4f7 --- /dev/null +++ b/src/main/resources/mapper/AppConfigMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + SELECT app_id, app_name, concurrent_num, cmd, timeout, local_path, email_template + FROM app_config + + + + + + diff --git a/src/main/resources/mapper/JobMapper.xml b/src/main/resources/mapper/JobMapper.xml index 8acf17a..ddd9686 100644 --- a/src/main/resources/mapper/JobMapper.xml +++ b/src/main/resources/mapper/JobMapper.xml @@ -3,7 +3,6 @@ - @@ -15,15 +14,14 @@ SELECT job_id, - `data`, param, mail, - result, + `result`, `status`, request_time, create_time, complete_time, - type + `type` FROM job @@ -32,34 +30,29 @@ request_time, create_time, complete_time, - type + `type` FROM job - - WHERE status = '${@com.weilab.biology.core.data.enums.JobStatusEnum@WAIT.getKey()}' - ORDER BY request_time LIMIT 1 - - \ No newline at end of file