diff --git a/admin/src/main/java/com/multictrl/modules/business/controller/MediaFileController.java b/admin/src/main/java/com/multictrl/modules/business/controller/MediaFileController.java index e1c2229..b885353 100644 --- a/admin/src/main/java/com/multictrl/modules/business/controller/MediaFileController.java +++ b/admin/src/main/java/com/multictrl/modules/business/controller/MediaFileController.java @@ -65,7 +65,7 @@ public class MediaFileController { }) @DataFilter(tableAlias = "t") @RequiresPermissions("bus:media:album") - public Result> getHangarAlbum(@Parameter(hidden = true) @RequestParam Map params) { + public Result> getAlbum(@Parameter(hidden = true) @RequestParam Map params) { PageData page = mediaFileService.getAlbum(params); return new Result>().ok(page); @@ -97,7 +97,7 @@ public class MediaFileController { @GetMapping("/conditionDownloadMedia") @RequiresPermissions("bus:media:download") @Parameters({ - @Parameter(name = "hangarSn", description = "机库sn"), + @Parameter(name = "dockSn", description = "机库sn"), @Parameter(name = "routeId", description = "航线标识"), @Parameter(name = "startTime", description = "开始时间"), @Parameter(name = "endTime", description = "结束时间") @@ -113,7 +113,7 @@ public class MediaFileController { @GetMapping("/oneClickGetDownloadPath") @RequiresPermissions("bus:media:download") @Parameters({ - @Parameter(name = "hangarSn", description = "机库sn"), + @Parameter(name = "dockSn", description = "机库sn"), @Parameter(name = "routeId", description = "航线标识"), @Parameter(name = "startTime", description = "开始时间"), @Parameter(name = "endTime", description = "结束时间") diff --git a/admin/src/main/java/com/multictrl/modules/business/controller/RouteFlightController.java b/admin/src/main/java/com/multictrl/modules/business/controller/RouteFlightController.java index f8c55d0..a70352a 100644 --- a/admin/src/main/java/com/multictrl/modules/business/controller/RouteFlightController.java +++ b/admin/src/main/java/com/multictrl/modules/business/controller/RouteFlightController.java @@ -91,7 +91,7 @@ public class RouteFlightController { public Result flightExecute(@PathVariable String dockSn, @RequestBody FlightExecute flightExecute) { - return new Result<>().ok(routeFlightService.flightExecute(dockSn, flightExecute)); + return new Result<>().ok(routeFlightService.flightExecute(dockSn, flightExecute, true)); } @LogOperation("空中下发航线") diff --git a/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java b/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java index 88426b8..946ac9f 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java @@ -28,6 +28,8 @@ public interface FlightTaskService extends CrudService taskIds); //执行航线 - String flightExecute(String dockSn, FlightExecute flightExecute); + String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord); //空中下发航线 String inFlightWaylineDeliver(String dockSn, Long routeId); diff --git a/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java b/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java index 5b741ce..e54ec9c 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java @@ -22,7 +22,12 @@ import com.multictrl.modules.business.dto.RouteDTO; import com.multictrl.modules.business.entity.*; import com.multictrl.modules.business.influxdb.UavReport; import com.multictrl.modules.business.service.*; +import com.multictrl.modules.job.dao.ScheduleJobDao; +import com.multictrl.modules.job.dto.ScheduleJobDTO; +import com.multictrl.modules.job.entity.ScheduleJobEntity; import com.multictrl.modules.security.user.SecurityUser; +import com.multictrl.modules.sys.dto.SysUserDTO; +import com.multictrl.modules.sys.service.SysUserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -43,6 +48,8 @@ public class FlightTaskServiceImpl extends CrudServiceImpl getWrapper(Map params) { @@ -111,6 +118,27 @@ public class FlightTaskServiceImpl extends CrudServiceImpl new ReentrantLock()); lock.lock(); try { @@ -122,7 +122,9 @@ public class RouteFlightServiceImpl implements RouteFlightService { //执行任务 Utils.sleep(1000); String resultMsg = djiBaseService.executeAndReturnResult(dockSn, "flighttask_execute", new JSONObject().set("flight_id", taskId)); - flightTaskService.addRouteTask(taskId, dockSn, route.getId()); + if (isRecord) { + flightTaskService.addRouteTask(taskId, dockSn, route.getId()); + } return resultMsg; } finally { Utils.sleep(2000); diff --git a/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobController.java b/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobController.java index da73584..99fc96d 100644 --- a/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobController.java +++ b/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobController.java @@ -1,5 +1,7 @@ package com.multictrl.modules.job.controller; +import com.multictrl.common.annotation.ApiOrder; +import com.multictrl.common.annotation.DataFilter; import com.multictrl.common.annotation.LogOperation; import com.multictrl.common.constant.Constant; import com.multictrl.common.page.PageData; @@ -13,9 +15,8 @@ import com.multictrl.modules.job.service.ScheduleJobService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.web.bind.annotation.*; @@ -28,8 +29,9 @@ import java.util.Map; */ @RestController @RequestMapping("/sys/schedule") -@Tag(name = "定时任务") -@AllArgsConstructor +@Tag(name = "计划飞行") +@ApiOrder(19) +@RequiredArgsConstructor public class ScheduleJobController { private final ScheduleJobService scheduleJobService; @@ -37,8 +39,12 @@ public class ScheduleJobController { @Operation(summary = "分页") @Parameters({ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), - @Parameter(name = Constant.LIMIT, description = "每页显示记录数") + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "beanName", description = "beanName") }) + @DataFilter(tableAlias = "s") @RequiresPermissions("sys:schedule:page") public Result> page(@Parameter(hidden = true) @RequestParam Map params) { PageData page = scheduleJobService.page(params); @@ -57,66 +63,66 @@ public class ScheduleJobController { @PostMapping @Operation(summary = "保存") - @LogOperation("保存") + @LogOperation("新增计划") @RequiresPermissions("sys:schedule:save") - public Result save(@RequestBody ScheduleJobDTO dto) { + public Result save(@RequestBody ScheduleJobDTO dto) { ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); scheduleJobService.save(dto); - return new Result(); + return new Result(); } @PutMapping @Operation(summary = "修改") - @LogOperation("修改") + @LogOperation("修改计划") @RequiresPermissions("sys:schedule:update") - public Result update(@RequestBody ScheduleJobDTO dto) { + public Result update(@RequestBody ScheduleJobDTO dto) { ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); scheduleJobService.update(dto); - return new Result(); + return new Result(); } @DeleteMapping @Operation(summary = "删除") - @LogOperation("删除") + @LogOperation("删除计划") @RequiresPermissions("sys:schedule:delete") - public Result delete(@RequestBody Long[] ids) { + public Result delete(@RequestBody Long[] ids) { scheduleJobService.deleteBatch(ids); - return new Result(); + return new Result(); } @PutMapping("/run") @Operation(summary = "立即执行") @LogOperation("立即执行") @RequiresPermissions("sys:schedule:run") - public Result run(@RequestBody Long[] ids) { + public Result run(@RequestBody Long[] ids) { scheduleJobService.run(ids); - return new Result(); + return new Result(); } @PutMapping("/pause") @Operation(summary = "暂停") - @LogOperation("暂停") + @LogOperation("暂停计划") @RequiresPermissions("sys:schedule:pause") - public Result pause(@RequestBody Long[] ids) { + public Result pause(@RequestBody Long[] ids) { scheduleJobService.pause(ids); - return new Result(); + return new Result(); } @PutMapping("/resume") @Operation(summary = "恢复") - @LogOperation("恢复") + @LogOperation("恢复计划") @RequiresPermissions("sys:schedule:resume") - public Result resume(@RequestBody Long[] ids) { + public Result resume(@RequestBody Long[] ids) { scheduleJobService.resume(ids); - return new Result(); + return new Result(); } } diff --git a/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobLogController.java b/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobLogController.java index 9a9854d..803315f 100644 --- a/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobLogController.java +++ b/admin/src/main/java/com/multictrl/modules/job/controller/ScheduleJobLogController.java @@ -1,5 +1,7 @@ package com.multictrl.modules.job.controller; +import com.multictrl.common.annotation.ApiOrder; +import com.multictrl.common.annotation.DataFilter; import com.multictrl.common.constant.Constant; import com.multictrl.common.page.PageData; import com.multictrl.common.utils.Result; @@ -10,7 +12,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.web.bind.annotation.*; @@ -23,8 +25,9 @@ import java.util.Map; */ @RestController @RequestMapping("/sys/scheduleLog") -@Tag(name = "定时任务日志") -@AllArgsConstructor +@Tag(name = "计划飞行日志") +@ApiOrder(20) +@RequiredArgsConstructor public class ScheduleJobLogController { private final ScheduleJobLogService scheduleJobLogService; @@ -32,8 +35,12 @@ public class ScheduleJobLogController { @Operation(summary = "分页") @Parameters({ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), - @Parameter(name = Constant.LIMIT, description = "每页显示记录数") + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "jobId", description = "jobId") }) + @DataFilter @RequiresPermissions("sys:schedule:log") public Result> page(@Parameter(hidden = true) @RequestParam Map params) { PageData page = scheduleJobLogService.page(params); diff --git a/admin/src/main/java/com/multictrl/modules/job/dao/ScheduleJobDao.java b/admin/src/main/java/com/multictrl/modules/job/dao/ScheduleJobDao.java index 693c58b..5329a95 100644 --- a/admin/src/main/java/com/multictrl/modules/job/dao/ScheduleJobDao.java +++ b/admin/src/main/java/com/multictrl/modules/job/dao/ScheduleJobDao.java @@ -1,8 +1,11 @@ package com.multictrl.modules.job.dao; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.multictrl.common.dao.BaseDao; import com.multictrl.modules.job.entity.ScheduleJobEntity; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import java.util.Map; @@ -13,9 +16,19 @@ import java.util.Map; */ @Mapper public interface ScheduleJobDao extends BaseDao { - - /** - * 批量更新状态 - */ - int updateBatch(Map map); + + /** + * 批量更新状态 + */ + int updateBatch(Map map); + + /** + * 分页查询 + */ + IPage pageList(@Param("ipage") IPage ipage, @Param("ew") QueryWrapper ew); + + /** + * 根据id查询详情 + */ + ScheduleJobEntity getById(@Param("id") Long id); } diff --git a/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobDTO.java b/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobDTO.java index 35804c1..217cf54 100644 --- a/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobDTO.java +++ b/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobDTO.java @@ -1,19 +1,25 @@ package com.multictrl.modules.job.dto; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.multictrl.common.validator.group.AddGroup; import com.multictrl.common.validator.group.DefaultGroup; import com.multictrl.common.validator.group.UpdateGroup; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Null; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.Range; import java.io.Serial; import java.io.Serializable; import java.util.Date; +import java.util.List; +import java.util.Objects; /** * 定时任务 @@ -27,31 +33,121 @@ public class ScheduleJobDTO implements Serializable { @Serial private static final long serialVersionUID = 1L; - @Schema(title = "id") - @Null(message="{id.null}", groups = AddGroup.class) - @NotNull(message="{id.require}", groups = UpdateGroup.class) + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + @NotNull(message = "{id.require}", groups = UpdateGroup.class) private Long id; - @Schema(title = "spring bean名称") - @NotBlank(message = "{schedule.bean.require}", groups = DefaultGroup.class) - private String beanName; + @NotBlank(message = "任务名称不能为空", groups = DefaultGroup.class) + @Schema(description = "任务名称") + private String jobName; - @Schema(title = "参数") + @Schema(description = "任务执行类") + private String beanName = "routeTask"; + + @NotNull(message = "定时类型不能为空", groups = DefaultGroup.class) + @Schema(description = "定时类型 1指定时间 2每天几点 3每周周几几点 4每月几号几点") + private Integer cronType; + + @Schema(description = "年") + private Integer year; + + @Schema(description = "月") + private Integer month; + + @Schema(description = "日") + private List day; + + @Schema(description = "时") + private Integer hour; + + @Schema(description = "分") + private Integer minute; + + @Schema(description = "参数(航线标识)") private String params; - @Schema(title = "cron表达式") - @NotBlank(message = "{schedule.cron.require}", groups = DefaultGroup.class) - private String cronExpression; - - @Schema(title = "任务状态 0:暂停 1:正常") - @Range(min=0, max=1, message = "{schedule.status.range}", groups = DefaultGroup.class) + @Schema(description = "任务状态 0:暂停 1:正常 -1失效") + @Range(min = -1, max = 1, message = "{schedule.status.range}", groups = DefaultGroup.class) private Integer status; - @Schema(title = "备注") + @Schema(description = "备注") private String remark; - @Schema(title = "创建时间") + @Schema(description = "创建时间") @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Date createDate; + @NotBlank(message = "机库sn不能为空", groups = DefaultGroup.class) + @Schema(description = "机库sn") + private String dockSn; + + @Schema(description = "航线名称") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private String routeName; + + @Schema(description = "机库名称") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private String dockName; + + @Schema(description = "下次执行时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private String nextRunTime; + + @Schema(description = "执行规则") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private String cronRules; + + @JsonIgnore + @Schema(hidden = true) + @AssertTrue(message = "参数不能为空", groups = DefaultGroup.class) + public boolean isParamsValid() { + return !Objects.equals(beanName, "routeTask") || StrUtil.isNotBlank(params); + } + + @JsonIgnore + @Schema(hidden = true) + @AssertTrue(message = "时间格式有问题", groups = DefaultGroup.class) + public boolean isCronTypeValid() { + if (cronType == null) return false; + return switch (cronType) { + case 1 -> // 指定时间(年、月、日、时、分) + year != null && month != null && !day.isEmpty() && hour != null && minute != null; + case 2 -> // 每天几点(时、分) + hour != null && minute != null; + case 3 ->// 每周周几几点(周几、时、分) + !day.isEmpty() && hour != null && minute != null && day.stream().allMatch(d -> d >= 1 && d <= 7); + case 4 ->// 每月几号几点(日、时、分) + !day.isEmpty() && hour != null && minute != null && day.stream().allMatch(d -> d >= 1 && d <= 31); + default -> false; + }; + } + + /** + * 是否断点续飞 + */ + @Schema(description = "是否断点续飞") + private Boolean isBreakpointFly; + /** + * 断点续飞最大时间 + */ + @Schema(description = "断点续飞最大时间 HH:mm:ss") + private String breakMaxTime; + + /** + * 断点续飞最小时间 + */ + @Schema(description = "断点续飞最小时间 HH:mm:ss") + private String breakMinTime; + + @JsonIgnore + @Schema(hidden = true) + @AssertTrue(message = "断点续飞时间不能为空") + public boolean isBreakTimeValid() { + if (isBreakpointFly) { + return StringUtils.isNotBlank(breakMaxTime) && StringUtils.isNotBlank(breakMinTime); + } else { + return true; + } + } } diff --git a/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobLogDTO.java b/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobLogDTO.java index c44136a..7d8b9b8 100644 --- a/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobLogDTO.java +++ b/admin/src/main/java/com/multictrl/modules/job/dto/ScheduleJobLogDTO.java @@ -19,28 +19,30 @@ public class ScheduleJobLogDTO implements Serializable { @Serial private static final long serialVersionUID = 1L; - @Schema(title = "id") + @Schema(description = "id") private Long id; - @Schema(title = "任务id") + @Schema(description = "任务id") private Long jobId; - @Schema(title = "spring bean名称") + /* @Schema(title = "spring bean名称") private String beanName; @Schema(title = "参数") - private String params; + private String params;*/ - @Schema(title = "任务状态 0:失败 1:成功") + @Schema(description = "任务状态 0:失败 1:成功") private Integer status; - @Schema(title = "失败信息") + @Schema(description = "失败信息") private String error; - @Schema(title = "耗时(单位:毫秒)") + @Schema(description = "耗时(单位:毫秒)") private Integer times; - @Schema(title = "创建时间") + @Schema(description = "创建时间") private Date createDate; + @Schema(description = "任务名称") + private String jobName; } diff --git a/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobEntity.java b/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobEntity.java index 2535bd9..ba0fa5b 100644 --- a/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobEntity.java +++ b/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobEntity.java @@ -9,6 +9,7 @@ import lombok.EqualsAndHashCode; import java.io.Serial; import java.util.Date; +import java.util.List; /** * 定时任务 @@ -16,40 +17,110 @@ import java.util.Date; * @author Sdy */ @Data -@EqualsAndHashCode(callSuper=false) +@EqualsAndHashCode(callSuper = false) @TableName("schedule_job") public class ScheduleJobEntity extends BaseEntity { - @Serial - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * spring bean名称 - */ - private String beanName; - /** - * 参数 - */ - private String params; - /** - * cron表达式 - */ - private String cronExpression; - /** - * 任务状态 0:暂停 1:正常 - */ - private Integer status; - /** - * 备注 - */ - private String remark; - /** - * 更新者 - */ - @TableField(fill = FieldFill.INSERT_UPDATE) - private Long updater; - /** - * 更新时间 - */ - @TableField(fill = FieldFill.INSERT_UPDATE) - private Date updateDate; + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * cron表达式 + */ + private String cronExpression; + /** + * 任务状态 0:暂停 1:正常 + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; + /** + * 任务名称 + */ + private String jobName; + /** + * 定时类型 1指定时间 2每天几点 3每周周几几点 4每月几号几点 + */ + private Integer cronType; + /** + * 年 + */ + private Integer year; + /** + * 月 + */ + private Integer month; + /** + * 日 + */ + private String days; + + @TableField(exist = false) + private List day; + /** + * 时 + */ + private Integer hour; + /** + * 分 + */ + private Integer minute; + /** + * 部门标识 + */ + private Long deptId; + /** + * 机库sn + */ + private String dockSn; + /** + * 是否断点续飞 + */ + private Boolean isBreakpointFly; + /** + * 断点续飞最大时间 + */ + private String breakMaxTime; + + /** + * 断点续飞最小时间 + */ + private String breakMinTime; + + @TableField(exist = false) + private String dockName; + + @TableField(exist = false) + private String routeName; + + /** + * 下次执行时间 + */ + @TableField(exist = false) + private String nextRunTime; + + /** + * 执行规则 + */ + @TableField(exist = false) + private String cronRules; } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobLogEntity.java b/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobLogEntity.java index bce7b7d..a35b19c 100644 --- a/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobLogEntity.java +++ b/admin/src/main/java/com/multictrl/modules/job/entity/ScheduleJobLogEntity.java @@ -16,41 +16,48 @@ import java.util.Date; @Data @TableName("schedule_job_log") public class ScheduleJobLogEntity implements Serializable { - @Serial - private static final long serialVersionUID = 1L; - - /** - * id - */ - @TableId - private Long id; - /** - * 任务id - */ - private Long jobId; - /** - * spring bean名称 - */ - private String beanName; - /** - * 参数 - */ - private String params; - /** - * 任务状态 0:失败 1:成功 - */ - private Integer status; - /** - * 失败信息 - */ - private String error; - /** - * 耗时(单位:毫秒) - */ - private Integer times; - /** - * 创建时间 - */ - private Date createDate; + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + @TableId + private Long id; + /** + * 任务id + */ + private Long jobId; + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * 任务状态 0:失败 1:成功 + */ + private Integer status; + /** + * 失败信息 + */ + private String error; + /** + * 耗时(单位:毫秒) + */ + private Integer times; + /** + * 创建时间 + */ + private Date createDate; + /** + * 部门 + */ + private Long deptId; + /** + * 任务名称 + */ + private String jobName; } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/job/init/JobCommandLineRunner.java b/admin/src/main/java/com/multictrl/modules/job/init/JobCommandLineRunner.java index 1398ee4..230abc5 100644 --- a/admin/src/main/java/com/multictrl/modules/job/init/JobCommandLineRunner.java +++ b/admin/src/main/java/com/multictrl/modules/job/init/JobCommandLineRunner.java @@ -1,14 +1,19 @@ package com.multictrl.modules.job.init; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.multictrl.modules.job.dao.ScheduleJobDao; import com.multictrl.modules.job.entity.ScheduleJobEntity; +import com.multictrl.modules.job.utils.CronParser; import com.multictrl.modules.job.utils.ScheduleUtils; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.quartz.CronTrigger; import org.quartz.Scheduler; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; +import java.util.Date; +import java.util.Iterator; import java.util.List; /** @@ -17,14 +22,27 @@ import java.util.List; * @author Sdy */ @Component -@AllArgsConstructor +@RequiredArgsConstructor public class JobCommandLineRunner implements CommandLineRunner { private final Scheduler scheduler; private final ScheduleJobDao scheduleJobDao; @Override public void run(String... args) { - List scheduleJobList = scheduleJobDao.selectList(null); + Date now = new Date(); + List scheduleJobList = scheduleJobDao.selectList(new QueryWrapper().ne("status", -1)); + Iterator iterator = scheduleJobList.iterator(); + while (iterator.hasNext()) { + ScheduleJobEntity item = iterator.next(); + Integer cronType = item.getCronType(); + if (1 == cronType) { + boolean valid = CronParser.isValid(item.getCronExpression(), now); + if (!valid) { + scheduleJobDao.update(null, new UpdateWrapper().eq("id", item.getId()).set("status", -1)); + iterator.remove(); // 安全删除 + } + } + } for (ScheduleJobEntity scheduleJob : scheduleJobList) { CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId()); //如果不存在,则创建 diff --git a/admin/src/main/java/com/multictrl/modules/job/service/ScheduleJobService.java b/admin/src/main/java/com/multictrl/modules/job/service/ScheduleJobService.java index f9f5eb1..be0fb70 100644 --- a/admin/src/main/java/com/multictrl/modules/job/service/ScheduleJobService.java +++ b/admin/src/main/java/com/multictrl/modules/job/service/ScheduleJobService.java @@ -22,32 +22,32 @@ public interface ScheduleJobService extends BaseService { * 保存定时任务 */ void save(ScheduleJobDTO dto); - + /** * 更新定时任务 */ void update(ScheduleJobDTO dto); - + /** * 批量删除定时任务 */ void deleteBatch(Long[] ids); - + /** * 批量更新定时任务状态 */ int updateBatch(Long[] ids, int status); - + /** * 立即执行 */ void run(Long[] ids); - + /** * 暂停运行 */ void pause(Long[] ids); - + /** * 恢复运行 */ diff --git a/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobLogServiceImpl.java b/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobLogServiceImpl.java index 007a629..f1c91df 100644 --- a/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobLogServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobLogServiceImpl.java @@ -18,29 +18,29 @@ import java.util.Map; @Service public class ScheduleJobLogServiceImpl extends BaseServiceImpl implements ScheduleJobLogService { - @Override - public PageData page(Map params) { - IPage page = baseDao.selectPage( - getPage(params, Constant.CREATE_DATE, false), - getWrapper(params) - ); - return getPageData(page, ScheduleJobLogDTO.class); - } + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + return getPageData(page, ScheduleJobLogDTO.class); + } - private QueryWrapper getWrapper(Map params){ - String jobId = (String)params.get("jobId"); + private QueryWrapper getWrapper(Map params) { + String jobId = (String) params.get("jobId"); - QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.eq(StrUtil.isNotBlank(jobId), "job_id", jobId); + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq(StrUtil.isNotBlank(jobId), "job_id", jobId); - return wrapper; - } + return wrapper; + } - @Override - public ScheduleJobLogDTO get(Long id) { - ScheduleJobLogEntity entity = baseDao.selectById(id); + @Override + public ScheduleJobLogDTO get(Long id) { + ScheduleJobLogEntity entity = baseDao.selectById(id); - return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class); - } + return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class); + } } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobServiceImpl.java b/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobServiceImpl.java index c19dc7f..8167fbd 100644 --- a/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/job/service/impl/ScheduleJobServiceImpl.java @@ -1,5 +1,6 @@ package com.multictrl.modules.job.service.impl; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -11,42 +12,73 @@ import com.multictrl.modules.job.dao.ScheduleJobDao; import com.multictrl.modules.job.dto.ScheduleJobDTO; import com.multictrl.modules.job.entity.ScheduleJobEntity; import com.multictrl.modules.job.service.ScheduleJobService; +import com.multictrl.modules.job.utils.CronGenerator; +import com.multictrl.modules.job.utils.CronParser; +import com.multictrl.modules.job.utils.CronToChinese; import com.multictrl.modules.job.utils.ScheduleUtils; -import lombok.AllArgsConstructor; +import com.multictrl.modules.security.user.SecurityUser; +import lombok.RequiredArgsConstructor; import org.quartz.Scheduler; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; @Service -@AllArgsConstructor +@RequiredArgsConstructor public class ScheduleJobServiceImpl extends BaseServiceImpl implements ScheduleJobService { private final Scheduler scheduler; @Override public PageData page(Map params) { - IPage page = baseDao.selectPage( + IPage page = baseDao.pageList( getPage(params, Constant.CREATE_DATE, false), getWrapper(params) ); + page.getRecords().forEach(item -> { + String days = item.getDays(); + if (StrUtil.isNotEmpty(days)) { + List dayList = Arrays.stream(days.trim().split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(Integer::parseInt) + .collect(Collectors.toList()); + item.setDay(dayList); + } + if (item.getStatus() != -1) { + String cronExpression = item.getCronExpression(); + String nextTime = CronParser.getNextTime(cronExpression); + item.setNextRunTime(nextTime); + } + item.setCronRules(CronToChinese.convert(item.getYear(), item.getMonth(), item.getDays(), item.getHour(), item.getMinute(), item.getCronType())); + }); return getPageData(page, ScheduleJobDTO.class); } @Override public ScheduleJobDTO get(Long id) { - ScheduleJobEntity entity = baseDao.selectById(id); + ScheduleJobEntity entity = baseDao.getById(id); + if (entity != null) { + String days = entity.getDays(); + List dayList = Arrays.stream(days.trim().split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(Integer::parseInt) + .collect(Collectors.toList()); + entity.setDay(dayList); + } return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class); } private QueryWrapper getWrapper(Map params) { String beanName = (String) params.get("beanName"); + String jobName = (String) params.get("jobName"); QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.like(StrUtil.isNotBlank(beanName), "bean_name", beanName); + wrapper.like(StrUtil.isNotBlank(beanName), "s.bean_name", beanName); + wrapper.like(StrUtil.isNotBlank(jobName), "s.job_name", jobName); return wrapper; } @@ -55,8 +87,12 @@ public class ScheduleJobServiceImpl extends BaseServiceImpl failure = new ArrayList<>(); for (Long id : ids) { - ScheduleUtils.run(scheduler, this.selectById(id)); + //是否判断过期 + ScheduleJobDTO jobDTO = get(id); + int result = ScheduleUtils.run(scheduler, this.selectById(id), jobDTO.getCronType() == 1); + if (result == Constant.ScheduleStatus.FAILURE.getValue()) { + failure.add(id); + } + } + if (CollectionUtil.isNotEmpty(failure)) { + updateBatch(failure.toArray(new Long[0]), Constant.ScheduleStatus.FAILURE.getValue()); } } @Override @Transactional(rollbackFor = Exception.class) public void pause(Long[] ids) { + List failure = new ArrayList<>(); + List normal = new ArrayList<>(); for (Long id : ids) { - ScheduleUtils.pauseJob(scheduler, id); + //是否判断过期 + ScheduleJobDTO jobDTO = get(id); + int result = ScheduleUtils.pauseJob(scheduler, id, jobDTO.getCronType() == 1); + if (result == Constant.ScheduleStatus.FAILURE.getValue()) { + failure.add(id); + } else { + normal.add(id); + } + } + if (CollectionUtil.isNotEmpty(failure)) { + updateBatch(failure.toArray(new Long[0]), Constant.ScheduleStatus.FAILURE.getValue()); + } + if (CollectionUtil.isNotEmpty(normal)) { + updateBatch(normal.toArray(new Long[0]), Constant.ScheduleStatus.PAUSE.getValue()); } - - updateBatch(ids, Constant.ScheduleStatus.PAUSE.getValue()); } @Override @Transactional(rollbackFor = Exception.class) public void resume(Long[] ids) { + List failure = new ArrayList<>(); + List normal = new ArrayList<>(); for (Long id : ids) { - ScheduleUtils.resumeJob(scheduler, id); + //是否判断过期 + ScheduleJobDTO jobDTO = get(id); + int result = ScheduleUtils.resumeJob(scheduler, id, jobDTO.getCronType() == 1); + if (result == Constant.ScheduleStatus.FAILURE.getValue()) { + failure.add(id); + } else { + normal.add(id); + } + } + if (CollectionUtil.isNotEmpty(failure)) { + updateBatch(failure.toArray(new Long[0]), Constant.ScheduleStatus.FAILURE.getValue()); + } + if (CollectionUtil.isNotEmpty(normal)) { + updateBatch(normal.toArray(new Long[0]), Constant.ScheduleStatus.NORMAL.getValue()); } - - updateBatch(ids, Constant.ScheduleStatus.NORMAL.getValue()); } - } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/job/task/RouteTask.java b/admin/src/main/java/com/multictrl/modules/job/task/RouteTask.java new file mode 100644 index 0000000..26c9e3b --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/job/task/RouteTask.java @@ -0,0 +1,59 @@ +package com.multictrl.modules.job.task; + +import cn.hutool.json.JSONObject; +import com.multictrl.common.constant.BusinessConstant; +import com.multictrl.common.utils.CacheUtils; +import com.multictrl.common.utils.JsonUtils; +import com.multictrl.common.utils.Utils; +import com.multictrl.modules.business.dto.flight.FlightExecute; +import com.multictrl.modules.business.service.FlightTaskService; +import com.multictrl.modules.business.service.RouteFlightService; +import com.multictrl.modules.job.dto.ScheduleJobDTO; +import com.multictrl.modules.job.entity.ScheduleJobEntity; +import com.multictrl.modules.job.service.ScheduleJobService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 航线任务 + * + * @author Sdy + * @since 1.0.0 2026/5/28 + */ +@Slf4j +@Component("routeTask") +@RequiredArgsConstructor +public class RouteTask implements ITask { + private final ScheduleJobService scheduleJobService; + private final RouteFlightService routeFlightService; + private final FlightTaskService flightTaskService; + + @Override + public void run(String params) { + JSONObject jsonObject = JsonUtils.parseObject(params, JSONObject.class); + if (jsonObject == null) { + log.error("计划飞行任务无效,拒绝执行:{}", params); + return; + } + Long jobId = jsonObject.getLong("jobId"); + Integer cronType = jsonObject.getInt("cronType"); + if (cronType == 1) { + //一次性任务 执行完失效 不然初始化job会因为时间过期而报错 + ScheduleJobEntity scheduleJobEntity = new ScheduleJobEntity(); + scheduleJobEntity.setStatus(-1); + scheduleJobEntity.setId(jobId); + scheduleJobService.updateById(scheduleJobEntity); + } + ScheduleJobDTO dto = scheduleJobService.get(jobId); + String dockSn = dto.getDockSn(); + //执行任务 + FlightExecute flightExecute = new FlightExecute(); + flightExecute.setRouteId(Long.parseLong(dto.getParams())); + routeFlightService.flightExecute(dockSn, flightExecute, false); + //添加日志 + Utils.sleep(1000); + String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn); + flightTaskService.addJobRouteTask(taskId, jobId); + } +} diff --git a/admin/src/main/java/com/multictrl/modules/job/utils/CronGenerator.java b/admin/src/main/java/com/multictrl/modules/job/utils/CronGenerator.java new file mode 100644 index 0000000..ff97332 --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/job/utils/CronGenerator.java @@ -0,0 +1,96 @@ +package com.multictrl.modules.job.utils; + +import cn.hutool.core.util.StrUtil; +import org.quartz.CronExpression; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * cron表达式生成器 + * + * @author Sdy + * @since 2025/6/27 + */ +public class CronGenerator { + + /** + * 1. 某一天的几点执行(年、月、日、小时、分钟) + * + * @param year 年(如 2024) + * @param month 月(1-12) + * @param day 日期(1-31) + * @param hour 小时(0-23) + * @param minute 分钟(0-59) + * @return Cron 表达式 + */ + public static String generateOnceCron(int year, int month, List day, int hour, int minute) { + return String.format("0 %d %d %d %d ? %d", minute, hour, day.get(0), month, year); + } + + /** + * 2. 每天的几点执行(小时、分钟) + * + * @param hour 小时(0-23) + * @param minute 分钟(0-59) + * @return Cron 表达式 + */ + public static String generateDailyCron(int hour, int minute) { + return String.format("0 %d %d * * ? *", minute, hour); + } + + /** + * 3. 每周的周几几点执行(周几、小时、分钟) + * + * @param dayOfWeek 周几(1-7,1=周一) + * @param hour 小时(0-23) + * @param minute 分钟(0-59) + * @return Cron 表达式 + */ + public static String generateWeeklyCron(List dayOfWeek, int hour, int minute) { + // 转换星期表示(将国内习惯的1-7转换为Cron表达式的1-7) + // 国内习惯:1=周一, 2=周二, 3=周三, 4=周四, 5=周五, 6=周六, 7=周日 + // Cron表达式:1=周日, 2=周一, 3=周二, 4=周三, 5=周四, 6=周五, 7=周六 + List cronDays = dayOfWeek.stream() + .map(d -> { + if (d == 7) return 1; // 7(周日) → 1(周日) + return d + 1; // 1(周一)→2, 2(周二)→3, ... 6(周六)→7 + }) + .collect(Collectors.toList()); + + return String.format("0 %d %d ? * %s *", minute, hour, StrUtil.join(",", cronDays)); + } + + /** + * 4. 每月的几号几点执行(日期、小时、分钟) + * + * @param dayOfMonth 日期(1-31) + * @param hour 小时(0-23) + * @param minute 分钟(0-59) + * @return Cron 表达式 + */ + public static String generateMonthlyCron(List dayOfMonth, int hour, int minute) { + return String.format("0 %d %d %s * ? *", minute, hour, StrUtil.join(",", dayOfMonth)); + } + + // 可选:验证 Cron 表达式是否合法(需引入 quartz 或 cronutils 依赖) + public static boolean isValid(String cronExpression) { + try { + new CronExpression(cronExpression); // 需要 quartz 依赖 + return true; + } catch (Exception e) { + return false; + } + } + + //获取cron表达式 + public static String getCron(Integer cronType, Integer year, Integer month, List day, Integer hour, Integer minute) { + return switch (cronType) { + case 1 -> generateOnceCron(year, month, day, hour, minute); + case 2 -> generateDailyCron(hour, minute); + case 3 -> generateWeeklyCron(day, hour, minute); + case 4 -> generateMonthlyCron(day, hour, minute); + default -> null; + }; + } +} diff --git a/admin/src/main/java/com/multictrl/modules/job/utils/CronParser.java b/admin/src/main/java/com/multictrl/modules/job/utils/CronParser.java new file mode 100644 index 0000000..bd83d5c --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/job/utils/CronParser.java @@ -0,0 +1,51 @@ +package com.multictrl.modules.job.utils; + +import com.multictrl.common.exception.ExceptionUtils; +import com.multictrl.common.utils.DateUtils; +import lombok.extern.slf4j.Slf4j; +import org.quartz.CronExpression; + +import java.text.ParseException; +import java.util.Date; + +/** + * cron表达式解析 + * + * @author Sdy + * @since 1.0.0 2025/7/22 + */ +@Slf4j +public class CronParser { + + /** + * 获取下一次执行时间 + */ + public static String getNextTime(String cronExpression) { + try { + CronExpression cron = new CronExpression(cronExpression); + Date now = new Date(); // 当前时间 + Date nextTime = cron.getNextValidTimeAfter(now); // 获取下一次执行时间 + return DateUtils.format(nextTime, "yyyy-MM-dd HH:mm"); + } catch (ParseException e) { + log.error(ExceptionUtils.getErrorStackTrace(e)); + return null; + } + } + + /** + * 是否有效 + */ + public static boolean isValid(String cronExpression, Date now) { + try { + boolean validExpression = CronExpression.isValidExpression(cronExpression); + if (validExpression) { + CronExpression cron = new CronExpression(cronExpression); + Date nextFireTime = cron.getNextValidTimeAfter(now); + return !(nextFireTime == null || nextFireTime.before(now)); + } + return false; + } catch (Exception e) { + return false; + } + } +} diff --git a/admin/src/main/java/com/multictrl/modules/job/utils/CronToChinese.java b/admin/src/main/java/com/multictrl/modules/job/utils/CronToChinese.java new file mode 100644 index 0000000..8d01917 --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/job/utils/CronToChinese.java @@ -0,0 +1,66 @@ +package com.multictrl.modules.job.utils; + +/** + * 中文转换工具 + * + * @author Sdy + * @since 1.0.0 2025/7/24 + */ +public class CronToChinese { + /** + * 将 Cron 表达式转换为中文描述 + */ + public static String convert(Integer year, Integer month, String days, Integer hour, Integer minute, Integer cronType) { + switch (cronType) { + case 1: + return year + "年" + month + "月" + days + "日" + hour + "点" + minute + "分执行"; + case 2: + return "每天" + hour + "点" + minute + "分执行"; + case 3: + String weekday = ""; + String[] split = days.split(","); + for (String s : split) { + if (s.equals("1")) { + weekday += "周一、"; + } else if (s.equals("2")) { + weekday += "周二、"; + } else if (s.equals("3")) { + weekday += "周三、"; + } else if (s.equals("4")) { + weekday += "周四、"; + } else if (s.equals("5")) { + weekday += "周五、"; + } else if (s.equals("6")) { + weekday += "周六、"; + } else if (s.equals("7")) { + weekday += "周日、"; + } + } + return "每周" + weekday.substring(0, weekday.length() - 1) + "的" + hour + "点" + minute + "分执行"; + case 4: + String monthday = ""; + String[] day = days.split(","); + for (String s : day) { + monthday += s + "号、"; + } + return "每月" + monthday.substring(0, monthday.length() - 1) + "的" + hour + "点" + minute + "分执行"; + default: + return null; + } + } + + // 示例用法 + /*public static void main(String[] args) { + System.out.println(convert(2025, 7, "24", 3, 23, 1)); + // 输出:2025年7月每月24日的上午3点23分执行 + + System.out.println(convert(0, 0, "", 12, 29, 2)); + // 输出:每天中午12点29分执行 + + System.out.println(convert(0, 0, "1,3,7", 11, 55, 3)); + // 输出:每周一、周三、周日上午11点55分执行 + + System.out.println(convert(0, 0, "1,3,5", 12, 56, 4)); + // 输出:每月1日、3日和5日的中午12点56分执行 + }*/ +} diff --git a/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleJob.java b/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleJob.java index 5dfeec6..23aa898 100644 --- a/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleJob.java +++ b/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleJob.java @@ -1,12 +1,13 @@ package com.multictrl.modules.job.utils; +import cn.hutool.json.JSONObject; import com.multictrl.common.constant.Constant; import com.multictrl.common.exception.ExceptionUtils; +import com.multictrl.common.utils.JsonUtils; import com.multictrl.common.utils.SpringContextUtils; import com.multictrl.modules.job.entity.ScheduleJobEntity; import com.multictrl.modules.job.entity.ScheduleJobLogEntity; import com.multictrl.modules.job.service.ScheduleJobLogService; -import lombok.extern.slf4j.Slf4j; import org.quartz.JobExecutionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,58 +16,60 @@ import org.springframework.scheduling.quartz.QuartzJobBean; import java.lang.reflect.Method; import java.util.Date; - /** * 定时任务 - * - * @author Sdy */ -@Slf4j public class ScheduleJob extends QuartzJobBean { + private final Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void executeInternal(JobExecutionContext context) { ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap(). - get(ScheduleUtils.JOB_PARAM_KEY); + get(ScheduleUtils.JOB_PARAM_KEY); //数据库保存执行记录 - ScheduleJobLogEntity logEntity = new ScheduleJobLogEntity(); - logEntity.setJobId(scheduleJob.getId()); - logEntity.setBeanName(scheduleJob.getBeanName()); - logEntity.setParams(scheduleJob.getParams()); - logEntity.setCreateDate(new Date()); + ScheduleJobLogEntity log = new ScheduleJobLogEntity(); + log.setJobId(scheduleJob.getId()); + log.setBeanName(scheduleJob.getBeanName()); + log.setParams(scheduleJob.getParams()); + log.setCreateDate(new Date()); + log.setDeptId(scheduleJob.getDeptId()); + log.setJobName(scheduleJob.getJobName()); //任务开始时间 long startTime = System.currentTimeMillis(); - + try { - //执行任务 - log.info("任务准备执行,任务ID:{}", scheduleJob.getId()); - Object target = SpringContextUtils.getBean(scheduleJob.getBeanName()); - Method method = target.getClass().getDeclaredMethod("run", String.class); - method.invoke(target, scheduleJob.getParams()); + //执行任务 + logger.info("任务准备执行,任务ID:{}", scheduleJob.getId()); + Object target = SpringContextUtils.getBean(scheduleJob.getBeanName()); + Method method = target.getClass().getDeclaredMethod("run", String.class); + JSONObject jsonObject = new JSONObject(); + jsonObject.set("jobId", scheduleJob.getId()); + jsonObject.set("cronType", scheduleJob.getCronType()); + method.invoke(target, JsonUtils.toJsonString(jsonObject)); - //任务执行总时长 - long times = System.currentTimeMillis() - startTime; - logEntity.setTimes((int)times); - //任务状态 - logEntity.setStatus(Constant.SUCCESS); + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int) times); + //任务状态 + log.setStatus(Constant.SUCCESS); - log.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times); - } catch (Exception e) { - log.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e); - - //任务执行总时长 - long times = System.currentTimeMillis() - startTime; - logEntity.setTimes((int)times); - - //任务状态 - logEntity.setStatus(Constant.FAIL); - logEntity.setError(ExceptionUtils.getErrorStackTrace(e)); - }finally { - //获取spring bean - ScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class); - scheduleJobLogService.insert(logEntity); - } + logger.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times); + } catch (Exception e) { + logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e); + + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int) times); + + //任务状态 + log.setStatus(Constant.FAIL); + log.setError(ExceptionUtils.getErrorStackTrace(e)); + } finally { + //获取spring bean + ScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class); + scheduleJobLogService.insert(log); + } } } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleUtils.java b/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleUtils.java index 1ea4e28..066f9b5 100644 --- a/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleUtils.java +++ b/admin/src/main/java/com/multictrl/modules/job/utils/ScheduleUtils.java @@ -2,29 +2,32 @@ package com.multictrl.modules.job.utils; import com.multictrl.common.constant.Constant; import com.multictrl.common.exception.ErrorCode; +import com.multictrl.common.exception.ExceptionUtils; import com.multictrl.common.exception.RenException; import com.multictrl.modules.job.entity.ScheduleJobEntity; +import lombok.extern.slf4j.Slf4j; import org.quartz.*; +import java.util.Date; + /** * 定时任务工具类 - * - * @author Sdy */ +@Slf4j public class ScheduleUtils { private final static String JOB_NAME = "TASK_"; /** * 任务调度参数key */ public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY"; - + /** * 获取触发器key */ public static TriggerKey getTriggerKey(Long jobId) { return TriggerKey.triggerKey(JOB_NAME + jobId); } - + /** * 获取jobKey */ @@ -48,12 +51,12 @@ public class ScheduleUtils { */ public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) { try { - //构建job信息 + //构建job信息 JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build(); //表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) - .withMisfireHandlingInstructionDoNothing(); + .withMisfireHandlingInstructionDoNothing(); //按新的cronExpression表达式构建一个新的trigger CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build(); @@ -62,16 +65,16 @@ public class ScheduleUtils { jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); scheduler.scheduleJob(jobDetail, trigger); - + //暂停任务 - if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){ - pauseJob(scheduler, scheduleJob.getId()); + if (scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()) { + pauseJob(scheduler, scheduleJob.getId(), false); } } catch (SchedulerException e) { throw new RenException(ErrorCode.JOB_ERROR, e); } } - + /** * 更新定时任务 */ @@ -81,23 +84,23 @@ public class ScheduleUtils { //表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) - .withMisfireHandlingInstructionDoNothing(); + .withMisfireHandlingInstructionDoNothing(); CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId()); - + //按新的cronExpression表达式重新构建trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); - + //参数 trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); - + scheduler.rescheduleJob(triggerKey, trigger); - + //暂停任务 - if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){ - pauseJob(scheduler, scheduleJob.getId()); + if (scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()) { + pauseJob(scheduler, scheduleJob.getId(), false); } - + } catch (SchedulerException e) { throw new RenException(ErrorCode.JOB_ERROR, e); } @@ -106,13 +109,20 @@ public class ScheduleUtils { /** * 立即执行任务 */ - public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + public static int run(Scheduler scheduler, ScheduleJobEntity scheduleJob, boolean validateExpired) { try { - //参数 - JobDataMap dataMap = new JobDataMap(); - dataMap.put(JOB_PARAM_KEY, scheduleJob); - - scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap); + Long jobId = scheduleJob.getId(); + if (validateExpired && isJobExpired(scheduler, jobId)) { + removeExpiredJob(scheduler, jobId); + return Constant.ScheduleStatus.FAILURE.getValue(); + } + + //参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(JOB_PARAM_KEY, scheduleJob); + + scheduler.triggerJob(getJobKey(jobId), dataMap); + return Constant.ScheduleStatus.NORMAL.getValue(); } catch (SchedulerException e) { throw new RenException(ErrorCode.JOB_ERROR, e); } @@ -121,9 +131,15 @@ public class ScheduleUtils { /** * 暂停任务 */ - public static void pauseJob(Scheduler scheduler, Long jobId) { + public static int pauseJob(Scheduler scheduler, Long jobId, boolean validateExpired) { try { + if (validateExpired && isJobExpired(scheduler, jobId)) { + removeExpiredJob(scheduler, jobId); + return Constant.ScheduleStatus.FAILURE.getValue(); + } + scheduler.pauseJob(getJobKey(jobId)); + return Constant.ScheduleStatus.PAUSE.getValue(); } catch (SchedulerException e) { throw new RenException(ErrorCode.JOB_ERROR, e); } @@ -132,9 +148,15 @@ public class ScheduleUtils { /** * 恢复任务 */ - public static void resumeJob(Scheduler scheduler, Long jobId) { + public static int resumeJob(Scheduler scheduler, Long jobId, boolean validateExpired) { try { + if (validateExpired && isJobExpired(scheduler, jobId)) { + removeExpiredJob(scheduler, jobId); + return Constant.ScheduleStatus.FAILURE.getValue(); + } + scheduler.resumeJob(getJobKey(jobId)); + return Constant.ScheduleStatus.NORMAL.getValue(); } catch (SchedulerException e) { throw new RenException(ErrorCode.JOB_ERROR, e); } @@ -150,4 +172,41 @@ public class ScheduleUtils { throw new RenException(ErrorCode.JOB_ERROR, e); } } + + /** + * 检查任务是否已过期 + */ + private static boolean isJobExpired(Scheduler scheduler, Long jobId) { + try { + Trigger trigger = scheduler.getTrigger(TriggerKey.triggerKey(JOB_NAME + jobId)); + Date nextFireTime = trigger.getNextFireTime(); + return nextFireTime != null && nextFireTime.before(new Date()); + /* TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + jobId); + // 1. Trigger 不存在(已被删除)→ 视为过期 + if (!scheduler.checkExists(triggerKey)) { + return true; + } + // 2. 检查 Trigger 状态:仅 COMPLETE 表示“已永久完成” + Trigger.TriggerState state = scheduler.getTriggerState(triggerKey); + return state == Trigger.TriggerState.COMPLETE;*/ + } catch (SchedulerException e) { + log.debug("判断任务 {} 是否过期失败", jobId); + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 移除过期任务(从调度器中移除并更新状态) + */ + private static void removeExpiredJob(Scheduler scheduler, Long jobId) { + try { + // 从调度器中移除 + scheduler.deleteJob(getJobKey(jobId)); + + log.debug("任务 {} 已过期,已从调度器中移除并标记为失效", jobId); + } catch (SchedulerException e) { + log.error("移除过期任务失败: {}", ExceptionUtils.getErrorStackTrace(e)); + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } } \ No newline at end of file diff --git a/admin/src/main/resources/mapper/job/ScheduleJobDao.xml b/admin/src/main/resources/mapper/job/ScheduleJobDao.xml index f84972b..89b5028 100644 --- a/admin/src/main/resources/mapper/job/ScheduleJobDao.xml +++ b/admin/src/main/resources/mapper/job/ScheduleJobDao.xml @@ -2,13 +2,29 @@ - + - + update schedule_job set status = #{status} where id in - + #{id} + + + + \ No newline at end of file diff --git a/admin/src/main/resources/mapper/job/ScheduleJobLogDao.xml b/admin/src/main/resources/mapper/job/ScheduleJobLogDao.xml index c197a9d..b3774f7 100644 --- a/admin/src/main/resources/mapper/job/ScheduleJobLogDao.xml +++ b/admin/src/main/resources/mapper/job/ScheduleJobLogDao.xml @@ -3,5 +3,4 @@ - \ No newline at end of file diff --git a/common/src/main/java/com/multictrl/common/constant/Constant.java b/common/src/main/java/com/multictrl/common/constant/Constant.java index 5ff3576..7b26fb2 100644 --- a/common/src/main/java/com/multictrl/common/constant/Constant.java +++ b/common/src/main/java/com/multictrl/common/constant/Constant.java @@ -25,7 +25,7 @@ public interface Constant { */ Long DEPT_ROOT = 0L; /** - * 升序 + * 升序 */ String ASC = "asc"; /** @@ -74,7 +74,11 @@ public interface Constant { /** * 正常 */ - NORMAL(1); + NORMAL(1), + /** + * 失效 + */ + FAILURE(-1); private final int value; diff --git a/prj-deploy/file/pgsql/init.sql b/prj-deploy/file/pgsql/init.sql index 95d5a53..7337b70 100644 --- a/prj-deploy/file/pgsql/init.sql +++ b/prj-deploy/file/pgsql/init.sql @@ -632,20 +632,32 @@ ON TABLE "public"."bus_waypoint_action" IS '航点动作信息'; -- ---------------------------- -- Table structure for schedule_job --- ---------------------------- DROP TABLE IF EXISTS "public"."schedule_job"; CREATE TABLE "public"."schedule_job" ( - "id" int8 NOT NULL, - "bean_name" varchar(200) COLLATE "pg_catalog"."default", - "params" varchar(2000) COLLATE "pg_catalog"."default", - "cron_expression" varchar(100) COLLATE "pg_catalog"."default", - "status" int4, - "remark" varchar(255) COLLATE "pg_catalog"."default", - "creator" int8, - "create_date" timestamp(6), - "updater" int8, - "update_date" timestamp(6) + "id" int8 NOT NULL, + "bean_name" varchar(200) COLLATE "pg_catalog"."default", + "params" varchar(2000) COLLATE "pg_catalog"."default", + "cron_expression" varchar(100) COLLATE "pg_catalog"."default", + "status" int4, + "remark" varchar(255) COLLATE "pg_catalog"."default", + "creator" int8, + "create_date" timestamp(6), + "updater" int8, + "update_date" timestamp(6), + "job_name" varchar(50) COLLATE "pg_catalog"."default", + "cron_type" int4, + "year" int4, + "month" int4, + "days" varchar(100) COLLATE "pg_catalog"."default", + "hour" int4, + "minute" int4, + "dept_id" int8, + "params_remark" varchar(200) COLLATE "pg_catalog"."default", + "dock_sn" varchar(50) COLLATE "pg_catalog"."default", + "is_breakpoint_fly" bool, + "break_max_time" varchar(50) COLLATE "pg_catalog"."default", + "break_min_time" varchar(50) COLLATE "pg_catalog"."default" ) ; COMMENT @@ -657,7 +669,7 @@ ON COLUMN "public"."schedule_job"."params" IS '参数'; COMMENT ON COLUMN "public"."schedule_job"."cron_expression" IS 'cron表达式'; COMMENT -ON COLUMN "public"."schedule_job"."status" IS '任务状态 0:暂停 1:正常'; +ON COLUMN "public"."schedule_job"."status" IS '任务状态 0:暂停 1:正常 -1:失效'; COMMENT ON COLUMN "public"."schedule_job"."remark" IS '备注'; COMMENT @@ -669,12 +681,33 @@ ON COLUMN "public"."schedule_job"."updater" IS '更新者'; COMMENT ON COLUMN "public"."schedule_job"."update_date" IS '更新时间'; COMMENT +ON COLUMN "public"."schedule_job"."job_name" IS '任务名称'; +COMMENT +ON COLUMN "public"."schedule_job"."cron_type" IS '定时类型 1指定时间 2每天几点 3每周周几几点 4每月几号几点'; +COMMENT +ON COLUMN "public"."schedule_job"."year" IS '年'; +COMMENT +ON COLUMN "public"."schedule_job"."month" IS '月'; +COMMENT +ON COLUMN "public"."schedule_job"."days" IS '日'; +COMMENT +ON COLUMN "public"."schedule_job"."hour" IS '时'; +COMMENT +ON COLUMN "public"."schedule_job"."minute" IS '分'; +COMMENT +ON COLUMN "public"."schedule_job"."dept_id" IS '部门标识'; +COMMENT +ON COLUMN "public"."schedule_job"."params_remark" IS '参数备注'; +COMMENT +ON COLUMN "public"."schedule_job"."dock_sn" IS '机库sn'; +COMMENT +ON COLUMN "public"."schedule_job"."is_breakpoint_fly" IS '是否断点续飞'; +COMMENT +ON COLUMN "public"."schedule_job"."break_max_time" IS '断点续飞最大时间'; +COMMENT +ON COLUMN "public"."schedule_job"."break_min_time" IS '断点续飞最小时间'; +COMMENT ON TABLE "public"."schedule_job" IS '定时任务'; - --- ---------------------------- --- Records of schedule_job --- ---------------------------- - -- ---------------------------- -- Table structure for schedule_job_log -- ---------------------------- @@ -688,7 +721,9 @@ CREATE TABLE "public"."schedule_job_log" "status" int4, "error" varchar(2000) COLLATE "pg_catalog"."default", "times" int4, - "create_date" timestamp(6) + "create_date" timestamp(6), + "dept_id" int8, + "job_name" varchar(50) COLLATE "pg_catalog"."default" ) ; COMMENT @@ -704,6 +739,10 @@ ON COLUMN "public"."schedule_job_log"."error" IS '失败信息'; COMMENT ON COLUMN "public"."schedule_job_log"."times" IS '耗时(单位:毫秒)'; COMMENT +ON COLUMN "public"."schedule_job_log"."dept_id" IS '部门标识'; +COMMENT +ON COLUMN "public"."schedule_job_log"."job_name" IS '任务名称'; +COMMENT ON TABLE "public"."schedule_job_log" IS '定时任务日志'; -- ----------------------------