2026-05-16 a2d3c6f8d43fbf9daa372fea53acf50642094de4
feat(task): 添加定时任务启用禁用功能

- 将 MaintenanceTask 和 TimingTask 的 isActive 字段改为 isEnabled 整型字段
- 在控制器中添加 changeEnable 接口用于切换任务启用状态
- 实现任务启用状态切换时同步更新 Quartz 调度状态
- 在任务列表查询中支持按 isEnabled 筛选
- 新增数据库脚本添加 is_enabled 列到 timing_task 和 maintenance_task 表
- 更新 Swagger 注解中的标签和操作描述为英文
- 在新增和编辑任务时处理 isEnabled 默认值逻辑
已添加2个文件
已修改12个文件
443 ■■■■ 文件已修改
doc/20260516_timing_task_enable_frontend_api.md 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/timing_and_maintenance_task_add_is_enabled.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/MaintenanceTaskService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/controller/TimingTaskController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/TimingTaskService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260516_timing_task_enable_frontend_api.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
# å®šæ—¶ä»»åŠ¡ã€Œæ˜¯å¦å¯ç”¨ã€å‰ç«¯è”è°ƒæ–‡æ¡£
## 1. å˜æ›´èŒƒå›´
本次改动覆盖两个模块的定时任务管理:
1. è®¾å¤‡å·¡æ£€å®šæ—¶ä»»åŠ¡ï¼ˆ`inspectiontask`)
2. è®¾å¤‡ä¿å…»å®šæ—¶ä»»åŠ¡ï¼ˆ`device`)
两个模块均新增并启用字段:`isEnabled`。
## 2. å­—段定义
| å­—段名 | ç±»åž‹ | è¯´æ˜Ž | å–值 |
|---|---|---|---|
| isEnabled | number | æ˜¯å¦å¯ç”¨ | `1`=启用,`0`=禁用 |
说明:
1. æ–°å¢žä»»åŠ¡æ—¶ï¼Œå¦‚æžœå‰ç«¯ä¸ä¼  `isEnabled`,后端默认按 `1`(启用)处理。
2. ä»»åŠ¡ç¦ç”¨åŽï¼Œä¸å†ç”Ÿæˆæ–°çš„å·¡æ£€/保养记录。
3. åˆ‡æ¢å¯ç”¨çŠ¶æ€ä¼šåŒæ­¥æ›´æ–° Quartz è°ƒåº¦ï¼ˆå¯ç”¨=恢复,禁用=暂停)。
## 3. æ•°æ®åº“脚本
执行脚本:
`sql/timing_and_maintenance_task_add_is_enabled.sql`
新增列:
1. `timing_task.is_enabled`
2. `maintenance_task.is_enabled`
## 4. å·¡æ£€å®šæ—¶ä»»åŠ¡æŽ¥å£ï¼ˆinspectiontask)
基础路径:`/timingTask`
### 4.1 åˆ—表查询
- `GET /timingTask/list`
- æ”¯æŒç­›é€‰å‚数:`isEnabled`
示例参数:
```http
GET /timingTask/list?current=1&size=10&isEnabled=1
```
### 4.2 æ–°å¢ž/编辑任务
- `POST /timingTask/addOrEditTimingTask`
- è¯·æ±‚体可带 `isEnabled`
示例:
```json
{
  "id": null,
  "taskName": "空压机巡检",
  "frequencyType": "DAILY",
  "frequencyDetail": "09:00",
  "taskIds": [101, 102],
  "isEnabled": 1
}
```
### 4.3 åˆ‡æ¢å¯ç”¨çŠ¶æ€ï¼ˆæŽ¨èå‰ç«¯å¼€å…³ç›´æŽ¥è°ƒç”¨ï¼‰
- `POST /timingTask/changeEnable`
示例:
```json
{
  "id": 12,
  "isEnabled": 0
}
```
返回:`R`,`data` ä¸ºå—影响行数(成功通常为 `1`)。
## 5. ä¿å…»å®šæ—¶ä»»åŠ¡æŽ¥å£ï¼ˆdevice)
基础路径:`/deviceMaintenanceTask`
### 5.1 åˆ—表查询
- `GET /deviceMaintenanceTask/listPage`
- æ”¯æŒç­›é€‰å‚数:`isEnabled`
示例参数:
```http
GET /deviceMaintenanceTask/listPage?current=1&size=10&isEnabled=1
```
### 5.2 æ–°å¢žä»»åŠ¡
- `POST /deviceMaintenanceTask/add`
- è¯·æ±‚体可带 `isEnabled`
示例:
```json
{
  "taskName": "反应釜保养",
  "frequencyType": "WEEKLY",
  "frequencyDetail": "MON,10:00",
  "deviceLedgerIds": [301, 302],
  "isEnabled": 1
}
```
### 5.3 ç¼–辑任务
- `POST /deviceMaintenanceTask/update`
- è¯·æ±‚体可带 `isEnabled`
### 5.4 åˆ‡æ¢å¯ç”¨çŠ¶æ€ï¼ˆæŽ¨èå‰ç«¯å¼€å…³ç›´æŽ¥è°ƒç”¨ï¼‰
- `POST /deviceMaintenanceTask/changeEnable`
示例:
```json
{
  "id": 26,
  "isEnabled": 0
}
```
返回:`AjaxResult`。
## 6. å‰ç«¯è”调建议
1. åˆ—表页新增「是否启用」列,使用开关组件映射:开=`1`,关=`0`。
2. å¼€å…³å˜æ›´æ—¶ç›´æŽ¥è°ƒç”¨å¯¹åº” `changeEnable` æŽ¥å£ï¼ŒæˆåŠŸåŽåˆ·æ–°å½“å‰è¡Œæˆ–åˆ—è¡¨ã€‚
3. æ–°å¢ž/编辑弹窗中增加「是否启用」字段,默认勾选(`1`)。
4. å¦‚果后端返回 `id和isEnabled不能为空`,检查开关提交参数是否完整。
sql/timing_and_maintenance_task_add_is_enabled.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
ALTER TABLE `timing_task`
    ADD COLUMN `is_enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用(1=启用,0=禁用)' AFTER `last_execution_time`;
ALTER TABLE `maintenance_task`
    ADD COLUMN `is_enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用(1=启用,0=禁用)' AFTER `last_execution_time`;
src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java
@@ -10,51 +10,57 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/12/22 14:58
 */
@Api(tags = "设备保养定时任务管理")
@Api(tags = "Maintenance Timing Task")
@RestController
@RequestMapping("/deviceMaintenanceTask")
public class MaintenanceTaskController extends BaseController {
    @Autowired
    private MaintenanceTaskService maintenanceTaskService;
    @GetMapping("/listPage")
    @ApiOperation(value = "设备保养定时任务列表")
    @ApiOperation(value = "Maintenance timing task list")
    public AjaxResult listPage(Page page, MaintenanceTask maintenanceTask) {
        return maintenanceTaskService.listPage(page,maintenanceTask);
        return maintenanceTaskService.listPage(page, maintenanceTask);
    }
    @PostMapping("/add")
    @ApiOperation(value = "添加设备保养定时任务")
    @Log(title = "设备保养定时任务", businessType = BusinessType.INSERT)
    @ApiOperation(value = "Create maintenance timing task")
    @Log(title = "Maintenance Timing Task", businessType = BusinessType.INSERT)
    public AjaxResult add(@RequestBody MaintenanceTask maintenanceTask) {
        return maintenanceTaskService.add(maintenanceTask);
    }
    @PostMapping("/update")
    @ApiOperation(value = "修改设备保养定时任务")
    @Log(title = "设备保养定时任务", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "Update maintenance timing task")
    @Log(title = "Maintenance Timing Task", businessType = BusinessType.UPDATE)
    public AjaxResult update(@RequestBody MaintenanceTask maintenanceTask) {
        return maintenanceTaskService.updateByMaintenanceTaskId(maintenanceTask);
    }
    @PostMapping("/changeEnable")
    @ApiOperation(value = "Change enable status")
    @Log(title = "Maintenance Timing Task", businessType = BusinessType.UPDATE)
    public AjaxResult changeEnable(@RequestBody MaintenanceTask maintenanceTask) {
        if (maintenanceTask.getId() == null || maintenanceTask.getIsEnabled() == null) {
            return AjaxResult.error("id and isEnabled are required");
        }
        return maintenanceTaskService.changeEnable(maintenanceTask.getId(), maintenanceTask.getIsEnabled());
    }
    @DeleteMapping("/delete")
    @ApiOperation(value = "删除设备保养定时任务")
    @Log(title = "设备保养定时任务", businessType = BusinessType.DELETE)
    @ApiOperation(value = "Delete maintenance timing tasks")
    @Log(title = "Maintenance Timing Task", businessType = BusinessType.DELETE)
    public AjaxResult delete(@RequestBody List<Long> ids) {
        return maintenanceTaskService.delete(ids);
    }
}
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
@@ -63,8 +63,8 @@
    @ApiModelProperty(value = "最后执行时间")
    private LocalDateTime lastExecutionTime;
    @ApiModelProperty(value = "是否激活")
    private boolean isActive;
    @ApiModelProperty(value = "是否启用(1=启用,0=禁用)")
    private Integer isEnabled;
    @ApiModelProperty(value = "备注")
    @Excel(name = "备注")
src/main/java/com/ruoyi/device/service/MaintenanceTaskService.java
@@ -19,4 +19,6 @@
    AjaxResult updateByMaintenanceTaskId(MaintenanceTask maintenanceTask);
    AjaxResult delete(List<Long> ids);
    AjaxResult changeEnable(Long taskId, Integer isEnabled);
}
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
@@ -55,6 +55,9 @@
            if (timingTask == null) {
                throw new JobExecutionException("MaintenanceTaskJob找不到定时任务 " + taskId);
            }
            if (Integer.valueOf(0).equals(timingTask.getIsEnabled())) {
                return;
            }
            List<Long> deviceIds = resolveDeviceIds(timingTask);
            List<DeviceMaintenance> maintenanceList = new ArrayList<>();
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
@@ -45,8 +45,14 @@
        try {
            TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId());
            Trigger oldTrigger = scheduler.getTrigger(triggerKey);
            if (oldTrigger == null) {
                scheduleMaintenanceTask(task);
                return;
            }
            if (!(oldTrigger instanceof CronTrigger)) {
                throw new SchedulerException("Existing trigger is not a CronTrigger");
                scheduler.unscheduleJob(triggerKey);
                scheduleMaintenanceTask(task);
                return;
            }
            CronTrigger newTrigger = TriggerBuilder.newTrigger()
@@ -234,4 +240,4 @@
                throw new IllegalArgumentException("无效的星期: " + dayName);
        }
    }
}
}
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
@@ -17,8 +17,10 @@
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
@@ -36,14 +38,19 @@
    @Autowired
    private MaintenanceTaskMapper maintenanceTaskMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private TimingTaskServiceImpl timingTaskService;
    @Autowired
    private MaintenanceTaskScheduler maintenanceTaskScheduler;
    @Autowired
    private IDeviceLedgerService deviceLedgerService;
    @Autowired
    private DeviceAreaMapper deviceAreaMapper;
@@ -57,6 +64,10 @@
        if (maintenanceTask.getAreaId() != null) {
            queryWrapper.eq(MaintenanceTask::getAreaId, maintenanceTask.getAreaId());
        }
        if (maintenanceTask.getIsEnabled() != null) {
            queryWrapper.eq(MaintenanceTask::getIsEnabled, maintenanceTask.getIsEnabled());
        }
        Page<MaintenanceTask> taskPage = maintenanceTaskMapper.selectPage(page, queryWrapper);
        if (taskPage.getRecords().isEmpty()) {
            return AjaxResult.success(taskPage);
@@ -74,6 +85,7 @@
            List<SysUser> users = sysUserMapper.selectUserByIds(new ArrayList<>(userIds));
            users.forEach(user -> userNickNameMap.put(user.getUserId(), user.getNickName()));
        }
        Map<Long, String> areaNameMap = deviceAreaMapper.selectBatchIds(taskPage.getRecords().stream()
                        .map(MaintenanceTask::getAreaId)
                        .filter(java.util.Objects::nonNull)
@@ -81,6 +93,7 @@
                        .collect(Collectors.toList()))
                .stream()
                .collect(Collectors.toMap(DeviceArea::getId, DeviceArea::getAreaName, (left, right) -> left, HashMap::new));
        taskPage.getRecords().forEach(task -> {
            if (task.getRegistrantId() != null) {
                task.setRegistrant(userNickNameMap.getOrDefault(task.getRegistrantId(), "未知用户"));
@@ -91,37 +104,70 @@
    }
    @Override
    @Transactional
    public AjaxResult add(MaintenanceTask maintenanceTask) {
        if (!prepareMaintenanceTask(maintenanceTask)) {
            return AjaxResult.error("请选择设备");
        }
        maintenanceTask.setActive(true);
        if (maintenanceTask.getIsEnabled() == null) {
            maintenanceTask.setIsEnabled(1);
        }
        TimingTask task = new TimingTask();
        task.setFrequencyType(maintenanceTask.getFrequencyType());
        task.setFrequencyDetail(maintenanceTask.getFrequencyDetail());
        LocalDateTime firstExecutionTime = timingTaskService.calculateFirstExecutionTime(task);
        maintenanceTask.setNextExecutionTime(firstExecutionTime);
        int insert = maintenanceTaskMapper.insert(maintenanceTask);
        if (insert > 0) {
            maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask);
            AjaxResult schedulerResult = resetSchedulerStatus(maintenanceTask);
            if (schedulerResult.isError()) {
                return schedulerResult;
            }
        }
        return AjaxResult.success("添加成功");
    }
    @Override
    @Transactional
    public AjaxResult updateByMaintenanceTaskId(MaintenanceTask maintenanceTask) {
        MaintenanceTask maintenanceTask1 = maintenanceTaskMapper.selectById(maintenanceTask.getId());
        if (maintenanceTask1 == null) {
        if (maintenanceTask.getId() == null) {
            return AjaxResult.error("id不能为空");
        }
        MaintenanceTask currentTask = maintenanceTaskMapper.selectById(maintenanceTask.getId());
        if (currentTask == null) {
            return AjaxResult.warn("没有此数据");
        }
        BeanUtils.copyProperties(maintenanceTask, maintenanceTask1);
        if (!prepareMaintenanceTask(maintenanceTask1)) {
        Integer oldEnabled = currentTask.getIsEnabled();
        LocalDateTime oldNextExecutionTime = currentTask.getNextExecutionTime();
        BeanUtils.copyProperties(maintenanceTask, currentTask);
        if (maintenanceTask.getIsEnabled() == null) {
            currentTask.setIsEnabled(oldEnabled);
        }
        if (maintenanceTask.getNextExecutionTime() == null) {
            currentTask.setNextExecutionTime(oldNextExecutionTime);
        }
        if (!prepareMaintenanceTask(currentTask)) {
            return AjaxResult.error("请选择设备");
        }
        maintenanceTask1.setDeviceLedgerIds(null);
        int update = maintenanceTaskMapper.updateById(maintenanceTask1);
        currentTask.setDeviceLedgerIds(null);
        if (currentTask.getNextExecutionTime() == null) {
            TimingTask task = new TimingTask();
            task.setFrequencyType(currentTask.getFrequencyType());
            task.setFrequencyDetail(currentTask.getFrequencyDetail());
            currentTask.setNextExecutionTime(timingTaskService.calculateFirstExecutionTime(task));
        }
        int update = maintenanceTaskMapper.updateById(currentTask);
        if (update > 0) {
            maintenanceTaskScheduler.rescheduleMaintenanceTask(maintenanceTask1);
            AjaxResult schedulerResult = resetSchedulerStatus(currentTask);
            if (schedulerResult.isError()) {
                return schedulerResult;
            }
        }
        return AjaxResult.success("更新成功");
    }
@@ -133,6 +179,57 @@
            ids.forEach(id -> maintenanceTaskScheduler.unscheduleMaintenanceTask(id));
        }
        return AjaxResult.success("删除成功");
    }
    @Override
    @Transactional
    public AjaxResult changeEnable(Long taskId, Integer isEnabled) {
        if (taskId == null || isEnabled == null) {
            return AjaxResult.error("id和isEnabled不能为空");
        }
        MaintenanceTask task = maintenanceTaskMapper.selectById(taskId);
        if (task == null) {
            return AjaxResult.warn("没有此数据");
        }
        task.setIsEnabled(isEnabled);
        if (task.getNextExecutionTime() == null) {
            TimingTask timingTask = new TimingTask();
            timingTask.setFrequencyType(task.getFrequencyType());
            timingTask.setFrequencyDetail(task.getFrequencyDetail());
            task.setNextExecutionTime(timingTaskService.calculateFirstExecutionTime(timingTask));
        }
        int update = maintenanceTaskMapper.updateById(task);
        if (update > 0) {
            AjaxResult schedulerResult = resetSchedulerStatus(task);
            if (schedulerResult.isError()) {
                return schedulerResult;
            }
        }
        return AjaxResult.success("更新成功");
    }
    private AjaxResult resetSchedulerStatus(MaintenanceTask task) {
        try {
            maintenanceTaskScheduler.rescheduleMaintenanceTask(task);
            if (isTaskEnabled(task.getIsEnabled())) {
                maintenanceTaskScheduler.resumeMaintenanceTask(task.getId());
            } else {
                maintenanceTaskScheduler.pauseMaintenanceTask(task.getId());
            }
            return AjaxResult.success();
        } catch (SchedulerException e) {
            log.error("resetSchedulerStatus error, taskId={}", task.getId(), e);
            return AjaxResult.error("更新调度状态失败");
        } catch (RuntimeException e) {
            log.error("resetSchedulerStatus runtime error, taskId={}", task.getId(), e);
            return AjaxResult.error("更新调度状态失败");
        }
    }
    private boolean isTaskEnabled(Integer isEnabled) {
        return isEnabled == null || isEnabled == 1;
    }
    private boolean prepareMaintenanceTask(MaintenanceTask task) {
@@ -150,6 +247,7 @@
        if (validIds.isEmpty()) {
            return false;
        }
        List<DeviceLedger> devices = validIds.stream()
                .map(deviceLedgerService::getById)
                .filter(device -> device != null)
@@ -157,6 +255,7 @@
        if (devices.isEmpty()) {
            return false;
        }
        task.setTaskId(devices.get(0).getId());
        if (task.getAreaId() == null) {
            task.setAreaId(devices.get(0).getAreaId());
src/main/java/com/ruoyi/inspectiontask/controller/TimingTaskController.java
@@ -9,7 +9,6 @@
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.inspectiontask.dto.InspectionTaskDto;
import com.ruoyi.inspectiontask.dto.TimingTaskDto;
import com.ruoyi.inspectiontask.pojo.InspectionTask;
import com.ruoyi.inspectiontask.pojo.TimingTask;
import com.ruoyi.inspectiontask.service.InspectionTaskService;
import com.ruoyi.inspectiontask.service.TimingTaskService;
@@ -28,12 +27,8 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/9/19 10:53
 */
@RestController
@Api(tags = "巡检管理")
@Api(tags = "Inspection Timing Task")
@RequestMapping("/timingTask")
public class TimingTaskController extends BaseController {
@@ -44,38 +39,48 @@
    private InspectionTaskService inspectionTaskService;
    @GetMapping("/list")
    @ApiOperation(value = "定时任务列表")
    @ApiOperation(value = "Timing task list")
    public R<IPage<TimingTaskDto>> list(Page<TimingTask> page, TimingTask timingTask) {
        IPage<TimingTaskDto> list = timingTaskService.selectTimingTaskList(page, timingTask);
        return R.ok(list);
    }
    @GetMapping("/recordList/{timingId}")
    @ApiOperation(value = "根据定时任务ID查询巡检记录列表")
    @ApiOperation(value = "Inspection records by timing task id")
    public R<List<InspectionTaskDto>> recordList(@PathVariable Long timingId) {
        return R.ok(inspectionTaskService.selectInspectionTaskRecordListByTimingId(timingId));
    }
    @PostMapping("/export")
    @ApiOperation(value = "导出定时任务")
    @ApiOperation(value = "Export timing tasks")
    public void export(HttpServletResponse response) {
        Page page = new Page<>(-1, -1);
        TimingTaskDto timingTask = new TimingTaskDto();
        IPage<TimingTaskDto> list = timingTaskService.selectTimingTaskList(page, timingTask);
        ExcelUtil<TimingTaskDto> util = new ExcelUtil<>(TimingTaskDto.class);
        util.exportExcel(response, list.getRecords(), "导出定时任务");
        util.exportExcel(response, list.getRecords(), "timing_task_export");
    }
    @PostMapping("/addOrEditTimingTask")
    @ApiOperation(value = "新增修改定时任务")
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    @ApiOperation(value = "Create or update timing task")
    @Log(title = "Timing Task", businessType = BusinessType.INSERT)
    public R addOrEditTimingTask(@RequestBody TimingTaskDto timingTaskDto) throws SchedulerException {
        return R.ok(timingTaskService.addOrEditTimingTask(timingTaskDto));
    }
    @PostMapping("/changeEnable")
    @ApiOperation(value = "Change enable status")
    @Log(title = "Timing Task", businessType = BusinessType.UPDATE)
    public R changeEnable(@RequestBody TimingTask timingTask) throws SchedulerException {
        if (timingTask.getId() == null || timingTask.getIsEnabled() == null) {
            return R.fail("id and isEnabled are required");
        }
        return R.ok(timingTaskService.changeEnable(timingTask.getId(), timingTask.getIsEnabled()));
    }
    @DeleteMapping("/delTimingTask")
    @ApiOperation(value = "删除定时任务")
    @Log(title = "定时任务", businessType = BusinessType.DELETE)
    @ApiOperation(value = "Delete timing tasks")
    @Log(title = "Timing Task", businessType = BusinessType.DELETE)
    public R remove(@RequestBody Long[] ids) {
        return R.ok(timingTaskService.delByIds(ids));
    }
src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java
@@ -65,8 +65,8 @@
    @ApiModelProperty(value = "最后执行时间")
    private LocalDateTime lastExecutionTime;
    @ApiModelProperty(value = "是否激活")
    private boolean isActive;
    @ApiModelProperty(value = "是否启用(1=启用,0=禁用)")
    private Integer isEnabled;
    @ApiModelProperty(value = "备注")
    @Excel(name = "备注")
src/main/java/com/ruoyi/inspectiontask/service/TimingTaskService.java
@@ -20,4 +20,6 @@
    int delByIds(Long[] ids);
    void updateTaskExecutionTime(Long taskId);
    int changeEnable(Long taskId, Integer isEnabled) throws SchedulerException;
}
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java
@@ -68,6 +68,9 @@
            if (timingTask == null) {
                throw new RuntimeException("定时任务不存在: " + taskId);
            }
            if (Integer.valueOf(0).equals(timingTask.getIsEnabled())) {
                return;
            }
            List<Long> deviceIds = resolveTaskIds(timingTask);
            if (deviceIds.isEmpty()) {
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
@@ -37,8 +37,14 @@
    public void rescheduleTimingTask(TimingTask task) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId());
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        if (oldTrigger == null) {
            scheduleTimingTask(task);
            return;
        }
        if (!(oldTrigger instanceof CronTrigger)) {
            throw new SchedulerException("Existing trigger is not a CronTrigger");
            scheduler.unscheduleJob(triggerKey);
            scheduleTimingTask(task);
            return;
        }
        CronTrigger newTrigger = TriggerBuilder.newTrigger()
@@ -223,4 +229,4 @@
                throw new IllegalArgumentException("无效的星期: " + dayName);
        }
    }
}
}
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
@@ -77,6 +77,9 @@
        if (timingTask.getAreaId() != null) {
            queryWrapper.eq(TimingTask::getAreaId, timingTask.getAreaId());
        }
        if (timingTask.getIsEnabled() != null) {
            queryWrapper.eq(TimingTask::getIsEnabled, timingTask.getIsEnabled());
        }
        IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, queryWrapper);
        if (taskPage.getRecords().isEmpty()) {
            return new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal());
@@ -154,18 +157,40 @@
        if (Objects.isNull(timingTaskDto.getId())) {
            timingTask.setRegistrationDate(LocalDate.now());
            timingTask.setActive(true);
            if (timingTask.getIsEnabled() == null) {
                timingTask.setIsEnabled(1);
            }
            timingTask.setNextExecutionTime(calculateFirstExecutionTime(timingTask));
            int result = timingTaskMapper.insert(timingTask);
            if (result > 0) {
                timingTaskScheduler.scheduleTimingTask(timingTask);
                resetSchedulerStatus(timingTask);
            }
            return result;
        }
        TimingTask existingTask = timingTaskMapper.selectById(timingTaskDto.getId());
        if (existingTask == null) {
            throw new IllegalArgumentException("定时任务不存在: " + timingTaskDto.getId());
        }
        if (timingTask.getIsEnabled() == null) {
            timingTask.setIsEnabled(existingTask.getIsEnabled());
        }
        if (timingTask.getNextExecutionTime() == null) {
            timingTask.setNextExecutionTime(existingTask.getNextExecutionTime());
        }
        if (timingTask.getRegistrationDate() == null) {
            timingTask.setRegistrationDate(existingTask.getRegistrationDate());
        }
        int result = timingTaskMapper.updateById(timingTask);
        if (result > 0) {
            timingTaskScheduler.rescheduleTimingTask(timingTask);
            TimingTask latestTask = timingTaskMapper.selectById(timingTask.getId());
            if (latestTask != null) {
                if (latestTask.getNextExecutionTime() == null) {
                    latestTask.setNextExecutionTime(calculateFirstExecutionTime(latestTask));
                    timingTaskMapper.updateById(latestTask);
                }
                resetSchedulerStatus(latestTask);
            }
        }
        return result;
    }
@@ -492,6 +517,41 @@
    }
    @Override
    @Transactional
    public int changeEnable(Long taskId, Integer isEnabled) throws SchedulerException {
        if (taskId == null || isEnabled == null) {
            throw new IllegalArgumentException("id和isEnabled不能为空");
        }
        TimingTask task = timingTaskMapper.selectById(taskId);
        if (task == null) {
            throw new IllegalArgumentException("定时任务不存在: " + taskId);
        }
        task.setIsEnabled(isEnabled);
        int updated = timingTaskMapper.updateById(task);
        if (updated > 0) {
            if (task.getNextExecutionTime() == null) {
                task.setNextExecutionTime(calculateFirstExecutionTime(task));
                timingTaskMapper.updateById(task);
            }
            resetSchedulerStatus(task);
        }
        return updated;
    }
    private void resetSchedulerStatus(TimingTask task) throws SchedulerException {
        timingTaskScheduler.rescheduleTimingTask(task);
        if (isTaskEnabled(task.getIsEnabled())) {
            timingTaskScheduler.resumeTimingTask(task.getId());
        } else {
            timingTaskScheduler.pauseTimingTask(task.getId());
        }
    }
    private boolean isTaskEnabled(Integer isEnabled) {
        return isEnabled == null || isEnabled == 1;
    }
    @Override
    public int delByIds(Long[] ids) {
        int i = timingTaskMapper.deleteBatchIds(Arrays.asList(ids));
        if (i > 0) {