增加计划飞行
This commit is contained in:
parent
3162f12835
commit
4ce9541397
|
|
@ -65,7 +65,7 @@ public class MediaFileController {
|
|||
})
|
||||
@DataFilter(tableAlias = "t")
|
||||
@RequiresPermissions("bus:media:album")
|
||||
public Result<PageData<MediaFileDTO>> getHangarAlbum(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
public Result<PageData<MediaFileDTO>> getAlbum(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
PageData<MediaFileDTO> page = mediaFileService.getAlbum(params);
|
||||
|
||||
return new Result<PageData<MediaFileDTO>>().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 = "结束时间")
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ public class RouteFlightController {
|
|||
public Result<Object> 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("空中下发航线")
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ public interface FlightTaskService extends CrudService<FlightTaskEntity, FlightT
|
|||
|
||||
void addTakeoffTask(String taskId, String dockSn);//一键起飞任务
|
||||
|
||||
void addJobRouteTask(String taskId, Long jobId);//计划飞行任务
|
||||
|
||||
//更新架次完成信息
|
||||
void updateTaskComplete(String dockSn);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public interface RouteFlightService {
|
|||
String flightTaskUndo(String dockSn, List<String> taskIds);
|
||||
|
||||
//执行航线
|
||||
String flightExecute(String dockSn, FlightExecute flightExecute);
|
||||
String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord);
|
||||
|
||||
//空中下发航线
|
||||
String inFlightWaylineDeliver(String dockSn, Long routeId);
|
||||
|
|
|
|||
|
|
@ -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<FlightTaskDao, Flight
|
|||
private final MediaFileService mediaFileService;
|
||||
private final DockDeviceDao dockDeviceDao;
|
||||
private final SrsService srsService;
|
||||
private final ScheduleJobDao scheduleJobDao;
|
||||
private final SysUserService userService;
|
||||
|
||||
@Override
|
||||
public QueryWrapper<FlightTaskEntity> getWrapper(Map<String, Object> params) {
|
||||
|
|
@ -111,6 +118,27 @@ public class FlightTaskServiceImpl extends CrudServiceImpl<FlightTaskDao, Flight
|
|||
baseDao.insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJobRouteTask(String taskId, Long jobId) {
|
||||
FlightTaskEntity entity = new FlightTaskEntity();
|
||||
entity.setTaskId(taskId);
|
||||
entity.setTaskType(3);
|
||||
ScheduleJobEntity jobEntity = scheduleJobDao.getById(jobId);
|
||||
addDockInfo(entity, jobEntity.getDockSn());
|
||||
long routeId = Long.parseLong(jobEntity.getParams());
|
||||
entity.setRouteId(routeId);
|
||||
RouteDTO route = routeService.getRoute(routeId);
|
||||
entity.setRouteName(route.getRouteName());
|
||||
entity.setRouteType(route.getTemplateType());
|
||||
entity.setRouteDistance(route.getTotalDistance());
|
||||
entity.setWaypointNum(route.getRouteWaypointList().size());
|
||||
entity.setDeptId(jobEntity.getDeptId());
|
||||
entity.setTaskStatus(FlightTaskStatus.IN_PROGRESS.getCode());
|
||||
SysUserDTO userDTO = userService.get(jobEntity.getUpdater());
|
||||
entity.setCreateName(userDTO.getRealName());
|
||||
baseDao.insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTaskComplete(String dockSn) {
|
||||
String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public class RouteFlightServiceImpl implements RouteFlightService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String flightExecute(String dockSn, FlightExecute flightExecute) {
|
||||
public String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord) {
|
||||
ReentrantLock lock = cacheLock.computeIfAbsent(dockSn, k -> 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));
|
||||
if (isRecord) {
|
||||
flightTaskService.addRouteTask(taskId, dockSn, route.getId());
|
||||
}
|
||||
return resultMsg;
|
||||
} finally {
|
||||
Utils.sleep(2000);
|
||||
|
|
|
|||
|
|
@ -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<PageData<ScheduleJobDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
PageData<ScheduleJobDTO> page = scheduleJobService.page(params);
|
||||
|
|
@ -57,66 +63,66 @@ public class ScheduleJobController {
|
|||
|
||||
@PostMapping
|
||||
@Operation(summary = "保存")
|
||||
@LogOperation("保存")
|
||||
@LogOperation("新增计划")
|
||||
@RequiresPermissions("sys:schedule:save")
|
||||
public Result<Object> save(@RequestBody ScheduleJobDTO dto) {
|
||||
public Result<String> save(@RequestBody ScheduleJobDTO dto) {
|
||||
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
|
||||
|
||||
scheduleJobService.save(dto);
|
||||
|
||||
return new Result<Object>();
|
||||
return new Result<String>();
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "修改")
|
||||
@LogOperation("修改")
|
||||
@LogOperation("修改计划")
|
||||
@RequiresPermissions("sys:schedule:update")
|
||||
public Result<Object> update(@RequestBody ScheduleJobDTO dto) {
|
||||
public Result<String> update(@RequestBody ScheduleJobDTO dto) {
|
||||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
||||
|
||||
scheduleJobService.update(dto);
|
||||
|
||||
return new Result<Object>();
|
||||
return new Result<String>();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "删除")
|
||||
@LogOperation("删除")
|
||||
@LogOperation("删除计划")
|
||||
@RequiresPermissions("sys:schedule:delete")
|
||||
public Result<Object> delete(@RequestBody Long[] ids) {
|
||||
public Result<String> delete(@RequestBody Long[] ids) {
|
||||
scheduleJobService.deleteBatch(ids);
|
||||
|
||||
return new Result<Object>();
|
||||
return new Result<String>();
|
||||
}
|
||||
|
||||
@PutMapping("/run")
|
||||
@Operation(summary = "立即执行")
|
||||
@LogOperation("立即执行")
|
||||
@RequiresPermissions("sys:schedule:run")
|
||||
public Result<Object> run(@RequestBody Long[] ids) {
|
||||
public Result<String> run(@RequestBody Long[] ids) {
|
||||
scheduleJobService.run(ids);
|
||||
|
||||
return new Result<Object>();
|
||||
return new Result<String>();
|
||||
}
|
||||
|
||||
@PutMapping("/pause")
|
||||
@Operation(summary = "暂停")
|
||||
@LogOperation("暂停")
|
||||
@LogOperation("暂停计划")
|
||||
@RequiresPermissions("sys:schedule:pause")
|
||||
public Result<Object> pause(@RequestBody Long[] ids) {
|
||||
public Result<String> pause(@RequestBody Long[] ids) {
|
||||
scheduleJobService.pause(ids);
|
||||
|
||||
return new Result<Object>();
|
||||
return new Result<String>();
|
||||
}
|
||||
|
||||
@PutMapping("/resume")
|
||||
@Operation(summary = "恢复")
|
||||
@LogOperation("恢复")
|
||||
@LogOperation("恢复计划")
|
||||
@RequiresPermissions("sys:schedule:resume")
|
||||
public Result<Object> resume(@RequestBody Long[] ids) {
|
||||
public Result<String> resume(@RequestBody Long[] ids) {
|
||||
scheduleJobService.resume(ids);
|
||||
|
||||
return new Result<Object>();
|
||||
return new Result<String>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<PageData<ScheduleJobLogDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
PageData<ScheduleJobLogDTO> page = scheduleJobLogService.page(params);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -18,4 +21,14 @@ public interface ScheduleJobDao extends BaseDao<ScheduleJobEntity> {
|
|||
* 批量更新状态
|
||||
*/
|
||||
int updateBatch(Map<String, Object> map);
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*/
|
||||
IPage<ScheduleJobEntity> pageList(@Param("ipage") IPage<ScheduleJobEntity> ipage, @Param("ew") QueryWrapper<ScheduleJobEntity> ew);
|
||||
|
||||
/**
|
||||
* 根据id查询详情
|
||||
*/
|
||||
ScheduleJobEntity getById(@Param("id") Long id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
@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<Integer> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import lombok.EqualsAndHashCode;
|
|||
|
||||
import java.io.Serial;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 定时任务
|
||||
|
|
@ -52,4 +53,74 @@ public class ScheduleJobEntity extends BaseEntity {
|
|||
*/
|
||||
@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<Integer> 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;
|
||||
}
|
||||
|
|
@ -52,5 +52,12 @@ public class ScheduleJobLogEntity implements Serializable {
|
|||
* 创建时间
|
||||
*/
|
||||
private Date createDate;
|
||||
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
private Long deptId;
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
private String jobName;
|
||||
}
|
||||
|
|
@ -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<ScheduleJobEntity> scheduleJobList = scheduleJobDao.selectList(null);
|
||||
Date now = new Date();
|
||||
List<ScheduleJobEntity> scheduleJobList = scheduleJobDao.selectList(new QueryWrapper<ScheduleJobEntity>().ne("status", -1));
|
||||
Iterator<ScheduleJobEntity> 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<ScheduleJobEntity>().eq("id", item.getId()).set("status", -1));
|
||||
iterator.remove(); // 安全删除
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ScheduleJobEntity scheduleJob : scheduleJobList) {
|
||||
CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId());
|
||||
//如果不存在,则创建
|
||||
|
|
|
|||
|
|
@ -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<ScheduleJobDao, ScheduleJobEntity> implements ScheduleJobService {
|
||||
private final Scheduler scheduler;
|
||||
|
||||
@Override
|
||||
public PageData<ScheduleJobDTO> page(Map<String, Object> params) {
|
||||
IPage<ScheduleJobEntity> page = baseDao.selectPage(
|
||||
IPage<ScheduleJobEntity> page = baseDao.pageList(
|
||||
getPage(params, Constant.CREATE_DATE, false),
|
||||
getWrapper(params)
|
||||
);
|
||||
page.getRecords().forEach(item -> {
|
||||
String days = item.getDays();
|
||||
if (StrUtil.isNotEmpty(days)) {
|
||||
List<Integer> 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<Integer> 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<ScheduleJobEntity> getWrapper(Map<String, Object> params) {
|
||||
String beanName = (String) params.get("beanName");
|
||||
String jobName = (String) params.get("jobName");
|
||||
|
||||
QueryWrapper<ScheduleJobEntity> 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<ScheduleJobDao, Sche
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(ScheduleJobDTO dto) {
|
||||
ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);
|
||||
entity.setDeptId(SecurityUser.getDeptId());
|
||||
String cron = CronGenerator.getCron(dto.getCronType(), dto.getYear(), dto.getMonth(), dto.getDay(), dto.getHour(), dto.getMinute());
|
||||
entity.setCronExpression(cron);
|
||||
entity.setDays(StrUtil.join(",", dto.getDay()));
|
||||
|
||||
entity.setStatus(Constant.ScheduleStatus.NORMAL.getValue());
|
||||
entity.setStatus(Constant.ScheduleStatus.PAUSE.getValue());
|
||||
this.insert(entity);
|
||||
|
||||
ScheduleUtils.createScheduleJob(scheduler, entity);
|
||||
|
|
@ -66,8 +102,16 @@ public class ScheduleJobServiceImpl extends BaseServiceImpl<ScheduleJobDao, Sche
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(ScheduleJobDTO dto) {
|
||||
ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);
|
||||
|
||||
entity.setDeptId(SecurityUser.getDeptId());
|
||||
String cron = CronGenerator.getCron(dto.getCronType(), dto.getYear(), dto.getMonth(), dto.getDay(), dto.getHour(), dto.getMinute());
|
||||
entity.setCronExpression(cron);
|
||||
entity.setDays(StrUtil.join(",", dto.getDay()));
|
||||
if (dto.getStatus() == Constant.ScheduleStatus.FAILURE.getValue()) {
|
||||
entity.setStatus(Constant.ScheduleStatus.PAUSE.getValue());
|
||||
ScheduleUtils.createScheduleJob(scheduler, entity);
|
||||
} else {
|
||||
ScheduleUtils.updateScheduleJob(scheduler, entity);
|
||||
}
|
||||
|
||||
this.updateById(entity);
|
||||
}
|
||||
|
|
@ -94,29 +138,63 @@ public class ScheduleJobServiceImpl extends BaseServiceImpl<ScheduleJobDao, Sche
|
|||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void run(Long[] ids) {
|
||||
List<Long> 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<Long> failure = new ArrayList<>();
|
||||
List<Long> 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<Long> failure = new ArrayList<>();
|
||||
List<Long> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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分执行
|
||||
}*/
|
||||
}
|
||||
|
|
@ -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,14 +16,11 @@ 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) {
|
||||
|
|
@ -30,43 +28,48 @@ public class ScheduleJob extends QuartzJobBean {
|
|||
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());
|
||||
logger.info("任务准备执行,任务ID:{}", scheduleJob.getId());
|
||||
Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());
|
||||
Method method = target.getClass().getDeclaredMethod("run", String.class);
|
||||
method.invoke(target, scheduleJob.getParams());
|
||||
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);
|
||||
log.setTimes((int) times);
|
||||
//任务状态
|
||||
logEntity.setStatus(Constant.SUCCESS);
|
||||
log.setStatus(Constant.SUCCESS);
|
||||
|
||||
log.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times);
|
||||
logger.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times);
|
||||
} catch (Exception e) {
|
||||
log.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e);
|
||||
logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e);
|
||||
|
||||
//任务执行总时长
|
||||
long times = System.currentTimeMillis() - startTime;
|
||||
logEntity.setTimes((int)times);
|
||||
log.setTimes((int) times);
|
||||
|
||||
//任务状态
|
||||
logEntity.setStatus(Constant.FAIL);
|
||||
logEntity.setError(ExceptionUtils.getErrorStackTrace(e));
|
||||
log.setStatus(Constant.FAIL);
|
||||
log.setError(ExceptionUtils.getErrorStackTrace(e));
|
||||
} finally {
|
||||
//获取spring bean
|
||||
ScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class);
|
||||
scheduleJobLogService.insert(logEntity);
|
||||
scheduleJobLogService.insert(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,18 @@ 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_";
|
||||
/**
|
||||
|
|
@ -65,7 +68,7 @@ public class ScheduleUtils {
|
|||
|
||||
//暂停任务
|
||||
if (scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()) {
|
||||
pauseJob(scheduler, scheduleJob.getId());
|
||||
pauseJob(scheduler, scheduleJob.getId(), false);
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
throw new RenException(ErrorCode.JOB_ERROR, e);
|
||||
|
|
@ -95,7 +98,7 @@ public class ScheduleUtils {
|
|||
|
||||
//暂停任务
|
||||
if (scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()) {
|
||||
pauseJob(scheduler, scheduleJob.getId());
|
||||
pauseJob(scheduler, scheduleJob.getId(), false);
|
||||
}
|
||||
|
||||
} catch (SchedulerException 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 {
|
||||
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(scheduleJob.getId()), dataMap);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,4 +11,20 @@
|
|||
</foreach>
|
||||
</update>
|
||||
|
||||
<select id="pageList" resultType="com.multictrl.modules.job.entity.ScheduleJobEntity">
|
||||
SELECT s.*, uh.dock_name, ur.route_name
|
||||
FROM schedule_job s
|
||||
left join bus_dock uh on s.dock_sn = uh.dock_sn
|
||||
left join bus_route ur on CAST(s.params AS int8) = ur.id
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
<select id="getById" resultType="com.multictrl.modules.job.entity.ScheduleJobEntity">
|
||||
SELECT s.*, uh.dock_name, ur.route_name
|
||||
FROM schedule_job s
|
||||
left join bus_dock uh on s.dock_sn = uh.dock_sn
|
||||
left join bus_route ur on CAST(s.params AS int8) = ur.id
|
||||
where s.id = #{id}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -3,5 +3,4 @@
|
|||
|
||||
<mapper namespace="com.multictrl.modules.job.dao.ScheduleJobLogDao">
|
||||
|
||||
|
||||
</mapper>
|
||||
|
|
@ -74,7 +74,11 @@ public interface Constant {
|
|||
/**
|
||||
* 正常
|
||||
*/
|
||||
NORMAL(1);
|
||||
NORMAL(1),
|
||||
/**
|
||||
* 失效
|
||||
*/
|
||||
FAILURE(-1);
|
||||
|
||||
private final int value;
|
||||
|
||||
|
|
|
|||
|
|
@ -632,7 +632,6 @@ ON TABLE "public"."bus_waypoint_action" IS '航点动作信息';
|
|||
|
||||
-- ----------------------------
|
||||
-- Table structure for schedule_job
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS "public"."schedule_job";
|
||||
CREATE TABLE "public"."schedule_job"
|
||||
(
|
||||
|
|
@ -645,7 +644,20 @@ CREATE TABLE "public"."schedule_job"
|
|||
"creator" int8,
|
||||
"create_date" timestamp(6),
|
||||
"updater" int8,
|
||||
"update_date" timestamp(6)
|
||||
"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 '定时任务日志';
|
||||
|
||||
-- ----------------------------
|
||||
|
|
|
|||
Loading…
Reference in New Issue