Commit 14cbdad374608249a65c4dd8fccd7f8e80de2ed8
1 parent
e5ce1952
feat:【IoT 物联网】增加 OTA 相关的任务、记录取消
Showing
11 changed files
with
128 additions
and
22 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskRecordController.java
| ... | ... | @@ -22,6 +22,7 @@ import org.dromara.hutool.core.collection.CollUtil; |
| 22 | 22 | import org.springframework.security.access.prepost.PreAuthorize; |
| 23 | 23 | import org.springframework.validation.annotation.Validated; |
| 24 | 24 | import org.springframework.web.bind.annotation.GetMapping; |
| 25 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 25 | 26 | import org.springframework.web.bind.annotation.RequestMapping; |
| 26 | 27 | import org.springframework.web.bind.annotation.RequestParam; |
| 27 | 28 | import org.springframework.web.bind.annotation.RestController; |
| ... | ... | @@ -90,4 +91,13 @@ public class IotOtaTaskRecordController { |
| 90 | 91 | return success(BeanUtils.toBean(upgradeRecord, IotOtaTaskRecordRespVO.class)); |
| 91 | 92 | } |
| 92 | 93 | |
| 94 | + @PutMapping("/cancel") | |
| 95 | + @Operation(summary = "取消 OTA 升级记录") | |
| 96 | + @PreAuthorize("@ss.hasPermission('iot:ota-task-record:cancel')") | |
| 97 | + @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024") | |
| 98 | + public CommonResult<Boolean> cancelOtaTaskRecord(@RequestParam("id") Long id) { | |
| 99 | + otaTaskRecordService.cancelOtaTaskRecord(id); | |
| 100 | + return success(true); | |
| 101 | + } | |
| 102 | + | |
| 93 | 103 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskRecordDO.java
| ... | ... | @@ -25,6 +25,13 @@ import lombok.NoArgsConstructor; |
| 25 | 25 | @AllArgsConstructor |
| 26 | 26 | public class IotOtaTaskRecordDO extends BaseDO { |
| 27 | 27 | |
| 28 | + public static final String DESCRIPTION_CANCEL_BY_TASK = "管理员手动取消升级任务(批量)"; | |
| 29 | + | |
| 30 | + public static final String DESCRIPTION_CANCEL_BY_RECORD = "管理员手动取消升级记录(单个)"; | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 升级记录编号 | |
| 34 | + */ | |
| 28 | 35 | @TableId |
| 29 | 36 | private Long id; |
| 30 | 37 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskMapper.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 8 | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | |
| 10 | 11 | @Mapper |
| ... | ... | @@ -15,10 +16,17 @@ public interface IotOtaTaskMapper extends BaseMapperX<IotOtaTaskDO> { |
| 15 | 16 | IotOtaTaskDO::getName, name); |
| 16 | 17 | } |
| 17 | 18 | |
| 18 | - default PageResult<IotOtaTaskDO> selectUpgradeTaskPage(IotOtaTaskPageReqVO pageReqVO) { | |
| 19 | + default PageResult<IotOtaTaskDO> selectPage(IotOtaTaskPageReqVO pageReqVO) { | |
| 19 | 20 | return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskDO>() |
| 20 | 21 | .eqIfPresent(IotOtaTaskDO::getFirmwareId, pageReqVO.getFirmwareId()) |
| 21 | - .likeIfPresent(IotOtaTaskDO::getName, pageReqVO.getName())); | |
| 22 | + .likeIfPresent(IotOtaTaskDO::getName, pageReqVO.getName()) | |
| 23 | + .orderByDesc(IotOtaTaskDO::getId)); | |
| 24 | + } | |
| 25 | + | |
| 26 | + default int updateByIdAndStatus(Long id, Integer whereStatus, IotOtaTaskDO updateObj) { | |
| 27 | + return update(updateObj, new LambdaUpdateWrapper<IotOtaTaskDO>() | |
| 28 | + .eq(IotOtaTaskDO::getId, id) | |
| 29 | + .eq(IotOtaTaskDO::getStatus, whereStatus)); | |
| 22 | 30 | } |
| 23 | 31 | |
| 24 | 32 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java
| ... | ... | @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; |
| 8 | 8 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 9 | 9 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 10 | |
| 11 | +import java.util.Collection; | |
| 11 | 12 | import java.util.List; |
| 12 | 13 | import java.util.Set; |
| 13 | 14 | |
| ... | ... | @@ -27,10 +28,30 @@ public interface IotOtaTaskRecordMapper extends BaseMapperX<IotOtaTaskRecordDO> |
| 27 | 28 | .eqIfPresent(IotOtaTaskRecordDO::getStatus, pageReqVO.getStatus())); |
| 28 | 29 | } |
| 29 | 30 | |
| 30 | - default void updateByTaskIdAndStatus(Long taskId, Integer fromStatus, IotOtaTaskRecordDO updateRecord) { | |
| 31 | - update(updateRecord, new LambdaUpdateWrapper<IotOtaTaskRecordDO>() | |
| 31 | + default List<IotOtaTaskRecordDO> selectListByTaskIdAndStatus(Long taskId, Collection<Integer> statuses) { | |
| 32 | + return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>() | |
| 33 | + .eq(IotOtaTaskRecordDO::getTaskId, taskId) | |
| 34 | + .in(IotOtaTaskRecordDO::getStatus, statuses)); | |
| 35 | + } | |
| 36 | + | |
| 37 | + default Long selectCountByTaskIdAndStatus(Long taskId, Collection<Integer> statuses) { | |
| 38 | + return selectCount(new LambdaQueryWrapperX<IotOtaTaskRecordDO>() | |
| 32 | 39 | .eq(IotOtaTaskRecordDO::getTaskId, taskId) |
| 33 | - .eq(IotOtaTaskRecordDO::getStatus, fromStatus)); | |
| 40 | + .in(IotOtaTaskRecordDO::getStatus, statuses)); | |
| 41 | + } | |
| 42 | + | |
| 43 | + default int updateByIdAndStatus(Long id, Collection<Integer> whereStatuses, | |
| 44 | + IotOtaTaskRecordDO updateObj) { | |
| 45 | + return update(updateObj, new LambdaUpdateWrapper<IotOtaTaskRecordDO>() | |
| 46 | + .eq(IotOtaTaskRecordDO::getId, id) | |
| 47 | + .in(IotOtaTaskRecordDO::getStatus, whereStatuses)); | |
| 48 | + } | |
| 49 | + | |
| 50 | + default void updateListByIdAndStatus(Collection<Long> ids, Collection<Integer> whereStatuses, | |
| 51 | + IotOtaTaskRecordDO updateObj) { | |
| 52 | + update(updateObj, new LambdaUpdateWrapper<IotOtaTaskRecordDO>() | |
| 53 | + .in(IotOtaTaskRecordDO::getId, ids) | |
| 54 | + .in(IotOtaTaskRecordDO::getStatus, whereStatuses)); | |
| 34 | 55 | } |
| 35 | 56 | |
| 36 | 57 | default List<IotOtaTaskRecordDO> selectListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) { | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
| ... | ... | @@ -57,9 +57,10 @@ public interface ErrorCodeConstants { |
| 57 | 57 | ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_EMPTY = new ErrorCode(1_050_008_103, "创建 OTA 任务失败,原因:没有可升级的设备"); |
| 58 | 58 | ErrorCode OTA_TASK_CANCEL_FAIL_STATUS_END = new ErrorCode(1_050_008_104, "取消 OTA 任务失败,原因:任务状态不是进行中"); |
| 59 | 59 | |
| 60 | - // ========== OTA 升级任务相关 1-050-008-100 ========== | |
| 60 | + // ========== OTA 升级任务记录相关 1-050-008-200 ========== | |
| 61 | 61 | |
| 62 | 62 | ErrorCode OTA_TASK_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在"); |
| 63 | + ErrorCode OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR = new ErrorCode(1_050_008_201, "取消 OTA 升级记录失败,原因:记录状态不是进行中"); | |
| 63 | 64 | |
| 64 | 65 | // ========== IoT 数据流转规则 1-050-010-000 ========== |
| 65 | 66 | ErrorCode DATA_RULE_NOT_EXISTS = new ErrorCode(1_050_010_000, "数据流转规则不存在"); | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskRecordStatusEnum.java
| ... | ... | @@ -32,8 +32,7 @@ public enum IotOtaTaskRecordStatusEnum implements ArrayValuable<Integer> { |
| 32 | 32 | public static final Set<Integer> IN_PROCESS_STATUSES = SetUtils.asSet( |
| 33 | 33 | PENDING.getStatus(), |
| 34 | 34 | PUSHED.getStatus(), |
| 35 | - UPGRADING.getStatus(), | |
| 36 | - SUCCESS.getStatus()); | |
| 35 | + UPGRADING.getStatus()); | |
| 37 | 36 | |
| 38 | 37 | public static final List<Integer> PRIORITY_STATUSES = Arrays.asList( |
| 39 | 38 | SUCCESS.getStatus(), | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskStatusEnum.java
| ... | ... | @@ -16,7 +16,7 @@ import java.util.Arrays; |
| 16 | 16 | public enum IotOtaTaskStatusEnum implements ArrayValuable<Integer> { |
| 17 | 17 | |
| 18 | 18 | IN_PROGRESS(10), // 进行中(升级中) |
| 19 | - COMPLETED(20), // 已完成(包括全部成功、部分成功) | |
| 19 | + END(20), // 已结束(包括全部成功、部分成功) | |
| 20 | 20 | CANCELED(30),; // 已取消(一般是主动取消任务) |
| 21 | 21 | |
| 22 | 22 | public static final Integer[] ARRAYS = Arrays.stream(values()) | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java
| ... | ... | @@ -65,4 +65,11 @@ public interface IotOtaTaskRecordService { |
| 65 | 65 | */ |
| 66 | 66 | List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses); |
| 67 | 67 | |
| 68 | + /** | |
| 69 | + * 取消 OTA 升级记录 | |
| 70 | + * | |
| 71 | + * @param id 记录编号 | |
| 72 | + */ | |
| 73 | + void cancelOtaTaskRecord(Long id); | |
| 74 | + | |
| 68 | 75 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.service.ota; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.hutool.core.convert.Convert; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 6 | import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO; |
| ... | ... | @@ -12,14 +13,12 @@ import lombok.extern.slf4j.Slf4j; |
| 12 | 13 | import org.springframework.stereotype.Service; |
| 13 | 14 | import org.springframework.validation.annotation.Validated; |
| 14 | 15 | |
| 15 | -import java.util.Arrays; | |
| 16 | -import java.util.List; | |
| 17 | -import java.util.Map; | |
| 18 | -import java.util.Set; | |
| 16 | +import java.util.*; | |
| 19 | 17 | |
| 20 | 18 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 21 | 19 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 22 | 20 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_TASK_RECORD_NOT_EXISTS; |
| 21 | +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR; | |
| 23 | 22 | |
| 24 | 23 | /** |
| 25 | 24 | * OTA 升级任务记录 Service 实现类 |
| ... | ... | @@ -32,6 +31,9 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { |
| 32 | 31 | @Resource |
| 33 | 32 | private IotOtaTaskRecordMapper otaTaskRecordMapper; |
| 34 | 33 | |
| 34 | + @Resource | |
| 35 | + private IotOtaTaskService otaTaskService; | |
| 36 | + | |
| 35 | 37 | @Override |
| 36 | 38 | public void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId) { |
| 37 | 39 | List<IotOtaTaskRecordDO> records = convertList(devices, device -> |
| ... | ... | @@ -75,14 +77,16 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { |
| 75 | 77 | |
| 76 | 78 | @Override |
| 77 | 79 | public void cancelTaskRecordListByTaskId(Long taskId) { |
| 78 | - // 设置取消记录的描述 | |
| 79 | - IotOtaTaskRecordDO updateRecord = IotOtaTaskRecordDO.builder() | |
| 80 | - .status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus()) | |
| 81 | - .description("管理员取消升级任务") | |
| 82 | - .build(); | |
| 83 | - | |
| 84 | - otaTaskRecordMapper.updateByTaskIdAndStatus( | |
| 85 | - taskId, IotOtaTaskRecordStatusEnum.PENDING.getStatus(), updateRecord); | |
| 80 | + List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByTaskIdAndStatus( | |
| 81 | + taskId, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES); | |
| 82 | + if (CollUtil.isEmpty(records)) { | |
| 83 | + return; | |
| 84 | + } | |
| 85 | + // 批量更新 | |
| 86 | + Collection<Long> ids = convertSet(records, IotOtaTaskRecordDO::getId); | |
| 87 | + otaTaskRecordMapper.updateListByIdAndStatus(ids, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES, | |
| 88 | + IotOtaTaskRecordDO.builder().status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus()) | |
| 89 | + .description(IotOtaTaskRecordDO.DESCRIPTION_CANCEL_BY_RECORD).build()); | |
| 86 | 90 | } |
| 87 | 91 | |
| 88 | 92 | @Override |
| ... | ... | @@ -90,6 +94,23 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { |
| 90 | 94 | return otaTaskRecordMapper.selectListByDeviceIdAndStatus(deviceIds, statuses); |
| 91 | 95 | } |
| 92 | 96 | |
| 97 | + @Override | |
| 98 | + public void cancelOtaTaskRecord(Long id) { | |
| 99 | + // 1. 校验记录是否存在 | |
| 100 | + IotOtaTaskRecordDO record = validateUpgradeRecordExists(id); | |
| 101 | + | |
| 102 | + // 2. 更新记录状态为取消 | |
| 103 | + int updateCount = otaTaskRecordMapper.updateByIdAndStatus(record.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES, | |
| 104 | + IotOtaTaskRecordDO.builder().id(id).status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus()) | |
| 105 | + .description(IotOtaTaskRecordDO.DESCRIPTION_CANCEL_BY_RECORD).build()); | |
| 106 | + if (updateCount == 0) { | |
| 107 | + throw exception(OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR); | |
| 108 | + } | |
| 109 | + | |
| 110 | + // 3. 检查并更新任务状态 | |
| 111 | + checkAndUpdateOtaTaskStatus(record.getTaskId()); | |
| 112 | + } | |
| 113 | + | |
| 93 | 114 | private IotOtaTaskRecordDO validateUpgradeRecordExists(Long id) { |
| 94 | 115 | IotOtaTaskRecordDO upgradeRecord = otaTaskRecordMapper.selectById(id); |
| 95 | 116 | if (upgradeRecord == null) { |
| ... | ... | @@ -98,4 +119,20 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { |
| 98 | 119 | return upgradeRecord; |
| 99 | 120 | } |
| 100 | 121 | |
| 122 | + /** | |
| 123 | + * 检查并更新任务状态 | |
| 124 | + * 如果任务下没有进行中的记录,则将任务状态更新为已结束 | |
| 125 | + */ | |
| 126 | + private void checkAndUpdateOtaTaskStatus(Long taskId) { | |
| 127 | + // 如果还有进行中的记录,直接返回 | |
| 128 | + Long inProcessCount = otaTaskRecordMapper.selectCountByTaskIdAndStatus( | |
| 129 | + taskId, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES); | |
| 130 | + if (inProcessCount > 0) { | |
| 131 | + return; | |
| 132 | + } | |
| 133 | + | |
| 134 | + // 没有进行中的记录,将任务状态更新为已结束 | |
| 135 | + otaTaskService.updateOtaTaskStatusEnd(taskId); | |
| 136 | + } | |
| 137 | + | |
| 101 | 138 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskService.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java
| ... | ... | @@ -99,7 +99,16 @@ public class IotOtaTaskServiceImpl implements IotOtaTaskService { |
| 99 | 99 | |
| 100 | 100 | @Override |
| 101 | 101 | public PageResult<IotOtaTaskDO> getOtaTaskPage(IotOtaTaskPageReqVO pageReqVO) { |
| 102 | - return otaTaskMapper.selectUpgradeTaskPage(pageReqVO); | |
| 102 | + return otaTaskMapper.selectPage(pageReqVO); | |
| 103 | + } | |
| 104 | + | |
| 105 | + @Override | |
| 106 | + public void updateOtaTaskStatusEnd(Long taskId) { | |
| 107 | + int updateCount = otaTaskMapper.updateByIdAndStatus(taskId, IotOtaTaskStatusEnum.IN_PROGRESS.getStatus(), | |
| 108 | + new IotOtaTaskDO().setStatus(IotOtaTaskStatusEnum.END.getStatus())); | |
| 109 | + if (updateCount == 0) { | |
| 110 | + log.warn("[updateOtaTaskStatusEnd][任务({})不存在或状态不是进行中,无法更新]", taskId); | |
| 111 | + } | |
| 103 | 112 | } |
| 104 | 113 | |
| 105 | 114 | private List<IotDeviceDO> validateOtaTaskDeviceScope(IotOtaTaskCreateReqVO createReqVO, Long productId) { | ... | ... |