feat:【IoT 物联网】增加 OTA 升级任务的实现

This commit is contained in:
YunaiV
2025-07-01 22:10:29 +08:00
parent 4749d93d0e
commit ecf6a4a846
23 changed files with 278 additions and 449 deletions

View File

@@ -9,12 +9,16 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService;
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.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@@ -22,31 +26,29 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - IoT OTA 升级任务记录")
@RestController
@RequestMapping("/iot/ota/task-record")
@RequestMapping("/iot/ota/task/record")
@Validated
public class IotOtaTaskRecordController {
@Resource
private IotOtaTaskRecordService otaTaskRecordService;
@GetMapping("/get-statistics")
@Operation(summary = "固件升级设备统计")
@GetMapping("/get-status-count")
@Operation(summary = "获得 OTA 升级记录状态统计")
@Parameters({
@Parameter(name = "firmwareId", description = "固件编号", example = "1024"),
@Parameter(name = "taskId", description = "升级任务编号", example = "2048")
})
@PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
@Parameter(name = "firmwareId", description = "固件编号", required = true, example = "1024")
public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatistics(@RequestParam(value = "firmwareId") Long firmwareId) {
return success(otaTaskRecordService.getOtaTaskRecordStatistics(firmwareId));
}
@GetMapping("/get-count")
@Operation(summary = "获得升级记录分页 tab 数量")
@PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
public CommonResult<Map<Integer, Long>> getOtaTaskRecordCount(@Valid IotOtaTaskRecordPageReqVO pageReqVO) {
return success(otaTaskRecordService.getOtaTaskRecordCount(pageReqVO));
public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatusCountMap(
@RequestParam(value = "firmwareId", required = false) Long firmwareId,
@RequestParam(value = "taskId", required = false) Long taskId) {
return success(otaTaskRecordService.getOtaTaskRecordStatusCountMap(firmwareId, taskId));
}
@GetMapping("/page")
@Operation(summary = "获得升级记录分页")
@PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")
@Operation(summary = "获得 OTA 升级记录分页")
@PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
public CommonResult<PageResult<IotOtaTaskRecordRespVO>> getOtaTaskRecordPage(
@Valid IotOtaTaskRecordPageReqVO pageReqVO) {
PageResult<IotOtaTaskRecordDO> pageResult = otaTaskRecordService.getOtaTaskRecordPage(pageReqVO);
@@ -54,8 +56,8 @@ public class IotOtaTaskRecordController {
}
@GetMapping("/get")
@Operation(summary = "获得升级记录")
@PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")
@Operation(summary = "获得 OTA 升级记录")
@PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
@Parameter(name = "id", description = "升级记录编号", required = true, example = "1024")
public CommonResult<IotOtaTaskRecordRespVO> getOtaTaskRecord(@RequestParam("id") Long id) {
IotOtaTaskRecordDO upgradeRecord = otaTaskRecordService.getOtaTaskRecord(id);

View File

@@ -32,4 +32,6 @@ public class IotOtaTaskCreateReqVO {
@Schema(description = "选中的设备编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
private List<Long> deviceIds;
// TODO @li如果 deviceScope 等于 2 时deviceIds 校验非空;
}

View File

@@ -2,19 +2,16 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - IoT OTA 升级记录分页 Request VO")
@Data
public class IotOtaTaskRecordPageReqVO extends PageParam {
// TODO @芋艿:分页条件字段梳理;
@Schema(description = "升级任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "升级任务编号不能为空")
@Schema(description = "升级任务编号", example = "1024")
private Long taskId;
@Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "摄像头A1-1")
@Schema(description = "设备标识", example = "摄像头A1-1")
private String deviceName;
}

View File

@@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IoT OTA 升级记录 Response VO")
@Data
public class IotOtaTaskRecordRespVO {
@@ -23,12 +21,6 @@ public class IotOtaTaskRecordRespVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long taskId;
@Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "iot")
private String productKey;
@Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iot")
private String deviceName;
@Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String deviceId;
@@ -47,10 +39,4 @@ public class IotOtaTaskRecordRespVO {
@Schema(description = "升级进度描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private String description;
@Schema(description = "升级开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "升级结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
}

View File

@@ -117,7 +117,7 @@ public class IotDeviceDO extends TenantBaseDO {
*
* 关联 {@link IotOtaFirmwareDO#getId()}
*/
private String firmwareId;
private Long firmwareId;
/**
* 设备密钥,用于设备认证

View File

@@ -61,7 +61,7 @@ public class IotOtaTaskDO extends BaseDO {
/**
* 设备总数数量
*/
private Long deviceTotalCount;
private Integer deviceTotalCount;
/**
* 设备成功数量
*/

View File

@@ -7,9 +7,10 @@ import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* IoT OTA 升级任务记录 DO
@@ -70,13 +71,5 @@ public class IotOtaTaskRecordDO extends BaseDO {
* 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志
*/
private String description;
/**
* 升级开始时间
*/
private LocalDateTime startTime;
/**
* 升级结束时间
*/
private LocalDateTime endTime;
}

View File

@@ -7,20 +7,11 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface IotOtaFirmwareMapper extends BaseMapperX<IotOtaFirmwareDO> {
/**
* 根据产品ID和固件版本号查询固件信息列表。
*
* @param productId 产品ID用于筛选固件信息。
* @param version 固件版本号,用于筛选固件信息。
* @return 返回符合条件的固件信息列表。
*/
default List<IotOtaFirmwareDO> selectByProductIdAndVersion(Long productId, String version) {
return selectList(IotOtaFirmwareDO::getProductId, productId,
default IotOtaFirmwareDO selectByProductIdAndVersion(Long productId, String version) {
return selectOne(IotOtaFirmwareDO::getProductId, productId,
IotOtaFirmwareDO::getVersion, version);
}

View File

@@ -7,20 +7,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageRe
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* OTA 升级任务 Mapper
*
* @author Shelly
*/
@Mapper
public interface IotOtaTaskMapper extends BaseMapperX<IotOtaTaskDO> {
default List<IotOtaTaskDO> selectByFirmwareIdAndName(Long firmwareId, String name) {
return selectList(new LambdaQueryWrapperX<IotOtaTaskDO>()
.eqIfPresent(IotOtaTaskDO::getFirmwareId, firmwareId)
.eqIfPresent(IotOtaTaskDO::getName, name));
default IotOtaTaskDO selectByFirmwareIdAndName(Long firmwareId, String name) {
return selectOne(IotOtaTaskDO::getFirmwareId, firmwareId,
IotOtaTaskDO::getName, name);
}
default PageResult<IotOtaTaskDO> selectUpgradeTaskPage(IotOtaTaskPageReqVO pageReqVO) {

View File

@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.iot.dal.mysql.ota;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Set;
@Mapper
public interface IotOtaTaskRecordMapper extends BaseMapperX<IotOtaTaskRecordDO> {
default List<IotOtaTaskRecordDO> selectListByFirmwareIdAndTaskId(Long firmwareId, Long taskId) {
return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
.eqIfPresent(IotOtaTaskRecordDO::getFirmwareId, firmwareId)
.eqIfPresent(IotOtaTaskRecordDO::getTaskId, taskId)
.select(IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus));
}
default PageResult<IotOtaTaskRecordDO> selectPage(IotOtaTaskRecordPageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
.eqIfPresent(IotOtaTaskRecordDO::getTaskId, pageReqVO.getTaskId()));
}
default void updateByTaskIdAndStatus(Long taskId, Integer fromStatus, IotOtaTaskRecordDO updateRecord) {
update(updateRecord, new LambdaUpdateWrapper<IotOtaTaskRecordDO>()
.eq(IotOtaTaskRecordDO::getTaskId, taskId)
.eq(IotOtaTaskRecordDO::getStatus, fromStatus));
}
default List<IotOtaTaskRecordDO> selectListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {
return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
.inIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceIds)
.inIfPresent(IotOtaTaskRecordDO::getStatus, statuses));
}
}

View File

@@ -1,111 +0,0 @@
package cn.iocoder.yudao.module.iot.dal.mysql.ota;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
@Mapper
public interface IotOtaUpgradeRecordMapper extends BaseMapperX<IotOtaTaskRecordDO> {
// TODO @liselectByFirmwareIdAndTaskIdAndDeviceId让方法自解释
/**
* 根据条件查询单个OTA升级记录
*
* @param firmwareId 固件ID可选参数用于筛选固件ID匹配的记录
* @param taskId 任务ID可选参数用于筛选任务ID匹配的记录
* @param deviceId 设备ID可选参数用于筛选设备ID匹配的记录
* @return 返回符合条件的单个OTA升级记录如果不存在则返回null
*/
default IotOtaTaskRecordDO selectByConditions(Long firmwareId, Long taskId, String deviceId) {
// 使用LambdaQueryWrapperX构建查询条件根据传入的参数动态添加查询条件
return selectOne(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
.eqIfPresent(IotOtaTaskRecordDO::getFirmwareId, firmwareId)
.eqIfPresent(IotOtaTaskRecordDO::getTaskId, taskId)
.eqIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceId));
}
// TODO @li这个是不是 groupby status 就 ok 拉?
/**
* 根据任务ID和设备名称查询OTA升级记录的状态统计信息。
* 该函数通过SQL查询统计不同状态0到5的记录数量并返回一个包含统计结果的Map列表。
*
* @param taskId 任务ID用于筛选特定任务的OTA升级记录。
* @param deviceName 设备名称支持模糊查询用于筛选特定设备的OTA升级记录。
* @return 返回一个Map列表每个Map包含不同状态0到5的记录数量。
*/
@Select("select count(case when status = 0 then 1 else 0) as `0` " +
"count(case when status = 1 then 1 else 0) as `1` " +
"count(case when status = 2 then 1 else 0) as `2` " +
"count(case when status = 3 then 1 else 0) as `3` " +
"count(case when status = 4 then 1 else 0) as `4` " +
"count(case when status = 5 then 1 else 0) as `5` " +
"from iot_ota_upgrade_record " +
"where task_id = #{taskId} " +
"and device_name like concat('%', #{deviceName}, '%') " +
"and status = #{status}")
List<Map<String, Object>> selectOtaUpgradeRecordCount(@Param("taskId") Long taskId,
@Param("deviceName") String deviceName);
/**
* 根据固件ID查询OTA升级记录的状态统计信息。
* 该函数通过SQL查询统计不同状态0到5的记录数量并返回一个包含统计结果的Map列表。
*
* @param firmwareId 固件ID用于筛选特定固件的OTA升级记录。
* @return 返回一个Map列表每个Map包含不同状态0到5的记录数量。
*/
@Select("select count(case when status = 0 then 1 else 0) as `0` " +
"count(case when status = 1 then 1 else 0) as `1` " +
"count(case when status = 2 then 1 else 0) as `2` " +
"count(case when status = 3 then 1 else 0) as `3` " +
"count(case when status = 4 then 1 else 0) as `4` " +
"count(case when status = 5 then 1 else 0) as `5` " +
"from iot_ota_upgrade_record " +
"where firmware_id = #{firmwareId}")
List<Map<String, Object>> selectOtaUpgradeRecordStatistics(Long firmwareId);
// TODO @li这里的注释可以去掉哈
/**
* 根据分页查询条件获取 OTA升级记录的分页结果
*
* @param pageReqVO 分页查询请求参数包含设备名称、任务ID等查询条件
* @return 返回分页查询结果,包含符合条件的 OTA升级记录列表
*/
// TODO @liselectPage 就 ok 拉。
default PageResult<IotOtaTaskRecordDO> selectUpgradeRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) {
// TODO @li这里的注释可以去掉哈然后下面的“如果”。。。也没必要注释
// 使用LambdaQueryWrapperX构建查询条件并根据请求参数动态添加查询条件
return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
// .likeIfPresent(IotOtaTaskRecordDO::getDeviceName, pageReqVO.getDeviceName()) // 如果设备名称存在,则添加模糊查询条件
.eqIfPresent(IotOtaTaskRecordDO::getTaskId, pageReqVO.getTaskId())); // 如果任务ID存在则添加等值查询条件
}
// TODO @li这里的注释可以去掉哈
/**
* 根据任务ID和状态更新升级记录的状态
* <p>
* 该函数用于将符合指定任务ID和状态的升级记录的状态更新为新的状态。
*
* @param setStatus 要设置的新状态值类型为Integer
* @param taskId 要更新的升级记录对应的任务ID类型为Long
* @param whereStatus 用于筛选升级记录的当前状态值类型为Integer
*/
// TODO @li改成 updateByTaskIdAndStatus(taskId, status, IotOtaUpgradeRecordDO) 更通用一些。
default void updateUpgradeRecordStatusByTaskIdAndStatus(Integer setStatus, Long taskId, Integer whereStatus) {
// 使用LambdaUpdateWrapper构建更新条件将指定状态的记录更新为指定状态
update(new LambdaUpdateWrapper<IotOtaTaskRecordDO>()
.set(IotOtaTaskRecordDO::getStatus, setStatus)
.eq(IotOtaTaskRecordDO::getTaskId, taskId)
.eq(IotOtaTaskRecordDO::getStatus, whereStatus)
);
}
}

View File

@@ -16,4 +16,7 @@ public class DictTypeConstants {
public static final String ALERT_LEVEL = "iot_alert_level";
public static final String OTA_TASK_DEVICE_SCOPE = "iot_ota_task_device_scope";
public static final String OTA_TASK_STATUS = "iot_ota_task_status";
}

View File

@@ -41,20 +41,25 @@ public interface ErrorCodeConstants {
ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在");
ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除");
// ========== 固件相关 1-050-008-000 ==========
// ========== OTA 固件相关 1-050-008-000 ==========
ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在");
ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, "产品版本号重复");
ErrorCode OTA_UPGRADE_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, "升级任务不存在");
ErrorCode OTA_UPGRADE_TASK_NAME_DUPLICATE = new ErrorCode(1_050_008_101, "升级任务名称重复");
ErrorCode OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY = new ErrorCode(1_050_008_102, "设备编号列表不能为空");
ErrorCode OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY = new ErrorCode(1_050_008_103, "设备列表不能为空");
ErrorCode OTA_UPGRADE_TASK_CANNOT_CANCEL = new ErrorCode(1_050_008_104, "升级任务不能取消");
// ========== OTA 升级任务相关 1-050-008-100 ==========
ErrorCode OTA_UPGRADE_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在");
ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_201, "升级记录重复");
ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_202, "升级记录不能重试");
ErrorCode OTA_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, "升级任务不存在");
ErrorCode OTA_TASK_CREATE_FAIL_NAME_DUPLICATE = new ErrorCode(1_050_008_101, "创建 OTA 任务失败,原因:任务名称重复");
ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS = new ErrorCode(1_050_008_102,
"创建 OTA 任务失败,原因:设备({})已经是该固件版本");
ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS = new ErrorCode(1_050_008_102,
"创建 OTA 任务失败,原因:设备({})已经在升级中...");
ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_EMPTY = new ErrorCode(1_050_008_103, "创建 OTA 任务失败,原因:没有可升级的设备");
ErrorCode OTA_TASK_CANCEL_FAIL_STATUS_END = new ErrorCode(1_050_008_104, "取消 OTA 任务失败,原因:任务状态不是进行中");
// ========== OTA 升级任务相关 1-050-008-100 ==========
ErrorCode OTA_TASK_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在");
// ========== IoT 数据流转规则 1-050-010-000 ==========
ErrorCode DATA_RULE_NOT_EXISTS = new ErrorCode(1_050_010_000, "数据流转规则不存在");

View File

@@ -2,10 +2,13 @@ package cn.iocoder.yudao.module.iot.enums.ota;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* IoT OTA 升级任务记录的状态枚举
@@ -26,6 +29,17 @@ public enum IotOtaTaskRecordStatusEnum implements ArrayValuable<Integer> {
public static final Integer[] ARRAYS = Arrays.stream(values())
.map(IotOtaTaskRecordStatusEnum::getStatus).toArray(Integer[]::new);
public static final Set<Integer> IN_PROCESS_STATUSES = SetUtils.asSet(
PENDING.getStatus(),
PUSHED.getStatus(),
UPGRADING.getStatus(),
SUCCESS.getStatus());
public static final List<Integer> PRIORITY_STATUSES = Arrays.asList(
SUCCESS.getStatus(),
PENDING.getStatus(), PUSHED.getStatus(), UPGRADING.getStatus(),
FAILURE.getStatus(), CANCELED.getStatus());
/**
* 状态
*/

View File

@@ -12,7 +12,6 @@ import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* IoT 设备 Service 接口
@@ -155,21 +154,13 @@ public interface IotDeviceService {
List<IotDeviceDO> getDeviceListByState(Integer state);
/**
* 根据产品ID获取设备列表
* 根据产品编号,获取设备列表
*
* @param productId 产品ID用于查询特定产品的设备列表
* @return 返回与指定产品ID关联的设备列表列表中的每个元素为IotDeviceDO对象
* @param productId 产品编号
* @return 设备列表
*/
List<IotDeviceDO> getDeviceListByProductId(Long productId);
/**
* 根据设备ID列表获取设备信息列表
*
* @param deviceIdList 设备ID列表包含需要查询的设备ID
* @return 返回与设备ID列表对应的设备信息列表列表中的每个元素为IotDeviceDO对象
*/
List<IotDeviceDO> getDeviceListByIdList(List<Long> deviceIdList);
/**
* 基于产品编号,获得设备数量
*
@@ -248,6 +239,6 @@ public interface IotDeviceService {
*
* @param ids 设备编号数组
*/
void validateDevicesExist(Set<Long> ids);
List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids);
}

View File

@@ -278,11 +278,6 @@ public class IotDeviceServiceImpl implements IotDeviceService {
return deviceMapper.selectListByProductId(productId);
}
@Override
public List<IotDeviceDO> getDeviceListByIdList(List<Long> deviceIdList) {
return deviceMapper.selectByIds(deviceIdList);
}
@Override
public void updateDeviceState(IotDeviceDO device, Integer state) {
// 1. 更新状态和时间
@@ -474,14 +469,15 @@ public class IotDeviceServiceImpl implements IotDeviceService {
}
@Override
public void validateDevicesExist(Set<Long> ids) {
public List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
return Collections.emptyList();
}
List<IotDeviceDO> deviceIds = deviceMapper.selectByIds(ids);
if (deviceIds.size() != ids.size()) {
List<IotDeviceDO> devices = deviceMapper.selectByIds(ids);
if (devices.size() != ids.size()) {
throw exception(DEVICE_NOT_EXISTS);
}
return devices;
}
private IotDeviceServiceImpl getSelf() {

View File

@@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
import jakarta.validation.Valid;
/**
* OTA 固件管理 Service
* OTA 固件管理 Service 接口
*
* @author Shelly Chan
*/

View File

@@ -23,9 +23,14 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE;
@Slf4j
/**
* OTA 固件管理 Service 实现类
*
* @author Shelly Chan
*/
@Service
@Validated
@Slf4j
public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
@Resource
@@ -37,7 +42,9 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
@Override
public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) {
// 1.1 校验固件产品 + 版本号不能重复
validateProductAndVersionDuplicate(saveReqVO.getProductId(), saveReqVO.getVersion());
if (otaFirmwareMapper.selectByProductIdAndVersion(saveReqVO.getProductId(), saveReqVO.getVersion()) != null) {
throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);
}
// 1.2 校验产品存在
productService.validateProductExists(saveReqVO.getProductId());
@@ -83,25 +90,12 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
return firmware;
}
/**
* 验证产品和版本号是否重复
*/
private void validateProductAndVersionDuplicate(Long productId, String version) {
// 只查询1条记录检查是否存在
IotOtaFirmwareDO firmware = otaFirmwareMapper.selectOne(IotOtaFirmwareDO::getProductId, productId,
IotOtaFirmwareDO::getVersion, version);
if (firmware != null) {
throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);
}
}
/**
* 计算文件签名
*
*
* @param firmware 固件对象
* @throws Exception 下载或计算签名失败时抛出异常
*/
private void calculateFileDigest(IotOtaFirmwareDO firmware) throws Exception {
private void calculateFileDigest(IotOtaFirmwareDO firmware) {
String fileUrl = firmware.getFileUrl();
// 下载文件并计算签名
byte[] fileBytes = HttpUtil.downloadBytes(fileUrl);

View File

@@ -2,67 +2,67 @@ package cn.iocoder.yudao.module.iot.service.ota;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
// TODO @li注释写的有点冗余可以看看别的模块哈。= = AI 生成的注释,有的时候太啰嗦了,需要处理下的哈
/**
* IotOtaUpgradeRecordService 接口定义了与物联网设备OTA升级记录相关的操作。
* 该接口提供了创建、更新、查询、统计和重试升级记录的功能。
* IoT OTA 升级记录 Service 接口
*/
public interface IotOtaTaskRecordService {
/**
* 批量创建 OTA 升级记录
* 该函数用于为指定的设备列表、固件ID和升级任务ID创建OTA升级记录。
*
* @param deviceIds 设备ID列表表示需要升级的设备集合。
* @param firmwareId 固件ID表示要升级到的固件版本。
* @param upgradeTaskId 升级任务ID表示此次升级任务的唯一标识。
* @param devices 设备列表
* @param firmwareId 固件编号
* @param taskId 任务编号
*/
void createOtaTaskRecordBatch(List<Long> deviceIds, Long firmwareId, Long upgradeTaskId);
void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId);
/**
* 获取 OTA 升级记录的数量统计
* 获取 OTA 升级记录的状态统计
*
* @return 返回一个 Map其中键为状态码值为对应状态的升级记录数量
* @param firmwareId 固件编号
* @param taskId 任务编号
* @return 状态统计 Mapkey 为状态码value 为对应状态的升级记录数量
*/
Map<Integer, Long> getOtaTaskRecordCount(@Valid IotOtaTaskRecordPageReqVO pageReqVO);
Map<Integer, Long> getOtaTaskRecordStatusCountMap(Long firmwareId, Long taskId);
/**
* 获取 OTA 升级记录的统计信息
* 获取 OTA 升级记录
*
* @return 返回一个 Map其中键为状态码值为对应状态的升级记录统计信息
*/
Map<Integer, Long> getOtaTaskRecordStatistics(Long firmwareId);
/**
* 获取指定 ID 的 OTA 升级记录的详细信息
*
* @param id 需要查询的升级记录的 ID
* @return 返回包含升级记录详细信息的响应对象
* @param id 编号
* @return OTA 升级记录
*/
IotOtaTaskRecordDO getOtaTaskRecord(Long id);
/**
* 分页查询 OTA 升级记录
* 获取 OTA 升级记录分页
*
* @param pageReqVO 包含分页查询条件的请求对象,必须经过验证。
* @return 返回包含分页查询结果的响应对象。
* @param pageReqVO 分页查询
* @return OTA 升级记录分页
*/
PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(@Valid IotOtaTaskRecordPageReqVO pageReqVO);
/**
* 根据任务 ID 取消升级记录
* <p>
* 该函数用于根据给定的任务ID取消与该任务相关的升级记录。通常用于在任务执行失败或用户手动取消时
* 清理或标记相关的升级记录为取消状态。
* 根据 OTA 任务编号,取消未结束的升级记录
*
* @param taskId 要取消升级记录的任务ID。该ID唯一标识一个任务通常由任务管理系统生成。
* @param taskId 升级任务编号
*/
void cancelUpgradeRecordByTaskId(Long taskId);
void cancelTaskRecordListByTaskId(Long taskId);
/**
* 根据设备编号和记录状态,获取 OTA 升级记录列表
*
* @param deviceIds 设备编号集合
* @param statuses 记录状态集合
* @return OTA 升级记录列表
*/
List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses);
}

View File

@@ -2,177 +2,100 @@ package cn.iocoder.yudao.module.iot.service.ota;
import cn.hutool.core.convert.Convert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeRecordMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskRecordMapper;
import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_TASK_RECORD_NOT_EXISTS;
// TODO @li@Service、@Validated、@Slf4j先用关键注解2类注释简单写
@Slf4j
/**
* OTA 升级任务记录 Service 实现类
*/
@Service
@Validated
@Slf4j
public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService {
@Resource
private IotOtaUpgradeRecordMapper iotOtaUpgradeRecordMapper;
@Resource
private IotDeviceService deviceService;
@Resource
private IotOtaFirmwareService firmwareService;
@Resource
private IotOtaTaskService upgradeTaskService;
private IotOtaTaskRecordMapper otaTaskRecordMapper;
@Override
public void createOtaTaskRecordBatch(List<Long> deviceIds, Long firmwareId, Long upgradeTaskId) {
// 1. 校验升级记录信息是否存在,并且已经取消的任务可以重新开始
deviceIds.forEach(deviceId -> validateUpgradeRecordDuplicate(firmwareId, upgradeTaskId, String.valueOf(deviceId)));
// 2. 初始化OTA升级记录列表信息
IotOtaTaskDO upgradeTask = upgradeTaskService.getOtaTask(upgradeTaskId);
IotOtaFirmwareDO firmware = firmwareService.getOtaFirmware(firmwareId);
List<IotDeviceDO> deviceList = deviceService.getDeviceListByIdList(deviceIds);
List<IotOtaTaskRecordDO> upgradeRecordList = deviceList.stream().map(device -> {
IotOtaTaskRecordDO upgradeRecord = new IotOtaTaskRecordDO();
upgradeRecord.setFirmwareId(firmware.getId());
upgradeRecord.setTaskId(upgradeTask.getId());
upgradeRecord.setDeviceId(device.getId());
upgradeRecord.setFromFirmwareId(Convert.toLong(device.getFirmwareId()));
upgradeRecord.setStatus(IotOtaTaskRecordStatusEnum.PENDING.getStatus());
upgradeRecord.setProgress(0);
return upgradeRecord;
}).toList();
// 3. 保存数据
iotOtaUpgradeRecordMapper.insertBatch(upgradeRecordList);
// TODO @芋艿:在这里需要处理推送升级任务的逻辑
}
// TODO @li1方法注释简单写2父类写了注释子类就不用写了。。。
/**
* 获取OTA升级记录的数量统计。
* 该方法根据传入的查询条件统计不同状态的OTA升级记录数量并返回一个包含各状态数量的映射。
*
* @param pageReqVO 包含查询条件的请求对象主要包括任务ID和设备名称等信息。
* @return 返回一个Map其中键为状态常量值为对应状态的记录数量。
*/
@Override
@Transactional
public Map<Integer, Long> getOtaTaskRecordCount(IotOtaTaskRecordPageReqVO pageReqVO) {
// 分别查询不同状态的OTA升级记录数量
List<Map<String, Object>> upgradeRecordCountList = iotOtaUpgradeRecordMapper.selectOtaUpgradeRecordCount(
pageReqVO.getTaskId(), pageReqVO.getDeviceName());
Map<String, Object> upgradeRecordCountMap = ObjectUtils.defaultIfNull(upgradeRecordCountList.get(0));
Objects.requireNonNull(upgradeRecordCountMap);
return upgradeRecordCountMap.entrySet().stream().collect(Collectors.toMap(
entry -> Convert.toInt(entry.getKey()),
entry -> Convert.toLong(entry.getValue())));
public void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId) {
List<IotOtaTaskRecordDO> records = convertList(devices, device ->
IotOtaTaskRecordDO.builder().firmwareId(firmwareId).taskId(taskId)
.deviceId(device.getId()).fromFirmwareId(Convert.toLong(device.getFirmwareId()))
.status(IotOtaTaskRecordStatusEnum.PENDING.getStatus()).progress(0).build());
otaTaskRecordMapper.insertBatch(records);
}
@Override
@Transactional
public Map<Integer, Long> getOtaTaskRecordStatistics(Long firmwareId) {
// 查询并统计不同状态的OTA升级记录数量
List<Map<String, Object>> upgradeRecordStatisticsList = iotOtaUpgradeRecordMapper.selectOtaUpgradeRecordStatistics(firmwareId);
Map<String, Object> upgradeRecordStatisticsMap = ObjectUtils.defaultIfNull(upgradeRecordStatisticsList.get(0));
Objects.requireNonNull(upgradeRecordStatisticsMap);
return upgradeRecordStatisticsMap.entrySet().stream().collect(Collectors.toMap(
entry -> Convert.toInt(entry.getKey()),
entry -> Convert.toLong(entry.getValue())));
public Map<Integer, Long> getOtaTaskRecordStatusCountMap(Long firmwareId, Long taskId) {
// 按照 status 枚举,初始化 countMap 为 0
Map<Integer, Long> countMap = convertMap(Arrays.asList(IotOtaTaskRecordStatusEnum.values()),
IotOtaTaskRecordStatusEnum::getStatus, iotOtaTaskRecordStatusEnum -> 0L);
// 查询记录,只返回 id、status 字段
List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByFirmwareIdAndTaskId(firmwareId, taskId);
Map<Long, List<Integer>> deviceStatusesMap = convertMultiMap(records,
IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus);
// 找到第一个匹配的优先级状态,避免重复计算
deviceStatusesMap.forEach((deviceId, statuses) -> {
for (Integer priorityStatus : IotOtaTaskRecordStatusEnum.PRIORITY_STATUSES) {
if (statuses.contains(priorityStatus)) {
countMap.put(priorityStatus, countMap.get(priorityStatus) + 1);
return;
}
}
});
return countMap;
}
@Override
public IotOtaTaskRecordDO getOtaTaskRecord(Long id) {
return iotOtaUpgradeRecordMapper.selectById(id);
return otaTaskRecordMapper.selectById(id);
}
@Override
public PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) {
return iotOtaUpgradeRecordMapper.selectUpgradeRecordPage(pageReqVO);
return otaTaskRecordMapper.selectPage(pageReqVO);
}
@Override
public void cancelUpgradeRecordByTaskId(Long taskId) {
// 暂定只有待推送的升级记录可以取消 TODO @芋艿:可以看看阿里云,哪些可以取消
iotOtaUpgradeRecordMapper.updateUpgradeRecordStatusByTaskIdAndStatus(
IotOtaTaskRecordStatusEnum.CANCELED.getStatus(), taskId,
IotOtaTaskRecordStatusEnum.PENDING.getStatus());
public void cancelTaskRecordListByTaskId(Long taskId) {
// 设置取消记录的描述
IotOtaTaskRecordDO updateRecord = IotOtaTaskRecordDO.builder()
.status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus())
.description("管理员取消升级任务")
.build();
otaTaskRecordMapper.updateByTaskIdAndStatus(
taskId, IotOtaTaskRecordStatusEnum.PENDING.getStatus(), updateRecord);
}
@Override
public List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {
return otaTaskRecordMapper.selectListByDeviceIdAndStatus(deviceIds, statuses);
}
/**
* 验证指定的升级记录是否存在。
* <p>
* 该函数通过给定的ID查询升级记录如果查询结果为空则抛出异常表示升级记录不存在。
*
* @param id 升级记录的唯一标识符类型为Long。
* @throws cn.iocoder.yudao.framework.common.exception.ServiceException则抛出异常异常类型为OTA_UPGRADE_RECORD_NOT_EXISTS。
*/
private IotOtaTaskRecordDO validateUpgradeRecordExists(Long id) {
// 根据ID查询升级记录
IotOtaTaskRecordDO upgradeRecord = iotOtaUpgradeRecordMapper.selectById(id);
// 如果查询结果为空,抛出异常
IotOtaTaskRecordDO upgradeRecord = otaTaskRecordMapper.selectById(id);
if (upgradeRecord == null) {
throw exception(OTA_UPGRADE_RECORD_NOT_EXISTS);
throw exception(OTA_TASK_RECORD_NOT_EXISTS);
}
return upgradeRecord;
}
// TODO @li注释有点冗余
/**
* 校验固件升级记录是否重复。
* <p>
* 该函数用于检查给定的固件ID、任务ID和设备ID是否已经存在未取消的升级记录。
* 如果存在未取消的记录,则抛出异常,提示升级记录重复。
*
* @param firmwareId 固件ID用于标识特定的固件版本
* @param taskId 任务ID用于标识特定的升级任务
* @param deviceId 设备ID用于标识特定的设备
*/
private void validateUpgradeRecordDuplicate(Long firmwareId, Long taskId, String deviceId) {
IotOtaTaskRecordDO upgradeRecord = iotOtaUpgradeRecordMapper.selectByConditions(firmwareId, taskId, deviceId);
if (upgradeRecord == null) {
return;
}
if (!Objects.equals(upgradeRecord.getStatus(), IotOtaTaskRecordStatusEnum.CANCELED.getStatus())) {
throw exception(OTA_UPGRADE_RECORD_DUPLICATE);
}
}
// TODO @li注释有点冗余
/**
* 验证升级记录是否可以重试。
* <p>
* 该方法用于检查给定的升级记录是否处于允许重试的状态。如果升级记录的状态为
* PENDING、PUSHED 或 UPGRADING则抛出异常表示不允许重试。
*
* @param upgradeRecord 需要验证的升级记录对象,类型为 IotOtaUpgradeRecordDO
* @throws cn.iocoder.yudao.framework.common.exception.ServiceException则抛出 OTA_UPGRADE_RECORD_CANNOT_RETRY 异常
*/
// TODO @li这种一次性的方法不复用的其实一步一定要抽成小方法
private void validateUpgradeRecordCanRetry(IotOtaTaskRecordDO upgradeRecord) {
if (ObjectUtils.equalsAny(upgradeRecord.getStatus(),
IotOtaTaskRecordStatusEnum.PENDING.getStatus(),
IotOtaTaskRecordStatusEnum.PUSHED.getStatus(),
IotOtaTaskRecordStatusEnum.UPGRADING.getStatus())) {
throw exception(OTA_UPGRADE_RECORD_CANNOT_RETRY);
}
}
}

View File

@@ -14,7 +14,7 @@ import jakarta.validation.Valid;
public interface IotOtaTaskService {
/**
* 创建 OTA升 级任务
* 创建 OTA 升级任务
*
* @param createReqVO 创建请求对象
* @return 升级任务编号

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.iot.service.ota;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO;
@@ -9,8 +9,10 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageRe
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskMapper;
import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;
import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
import jakarta.annotation.Resource;
@@ -24,6 +26,7 @@ import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
/**
@@ -45,26 +48,29 @@ public class IotOtaTaskServiceImpl implements IotOtaTaskService {
private IotOtaFirmwareService otaFirmwareService;
@Resource
@Lazy // 延迟,避免循环依赖报错
private IotOtaTaskRecordService otaUpgradeRecordService;
private IotOtaTaskRecordService otaTaskRecordService;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createOtaTask(IotOtaTaskCreateReqVO createReqVO) {
// 1.1 校验同一固件的升级任务名称不重复
validateFirmwareTaskDuplicate(createReqVO.getFirmwareId(), createReqVO.getName());
// 1.2 校验固件信息是否存在
// 1.1 校验固件信息是否存在
IotOtaFirmwareDO firmware = otaFirmwareService.validateFirmwareExists(createReqVO.getFirmwareId());
// 1.3 补全设备范围信息,并且校验是否又设备可以升级,如果没有设备可以升级,则报错
validateScopeAndDevice(createReqVO.getDeviceScope(), createReqVO.getDeviceIds(), firmware.getProductId());
// 1.2 校验同一固件的升级任务名称不重复
if (otaTaskMapper.selectByFirmwareIdAndName(firmware.getId(), createReqVO.getName()) != null) {
throw exception(OTA_TASK_CREATE_FAIL_NAME_DUPLICATE);
}
// 1.3 校验设备范围信息
List<IotDeviceDO> devices = validateOtaTaskDeviceScope(createReqVO, firmware.getProductId());
// 2. 保存 OTA 升级任务信息到数据库
IotOtaTaskDO upgradeTask = initOtaUpgradeTask(createReqVO, firmware.getProductId());
otaTaskMapper.insert(upgradeTask);
// 2. 保存升级任务,直接转换
IotOtaTaskDO task = BeanUtils.toBean(createReqVO, IotOtaTaskDO.class)
.setStatus(IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())
.setDeviceTotalCount(devices.size()).setDeviceSuccessCount(0);
otaTaskMapper.insert(task);
// 3. 生成设备升级记录信息并存储,等待定时任务轮询
// TODO @芋艿:在处理;
// otaUpgradeRecordService.createOtaUpgradeRecordBatch(upgradeTask.getDeviceIds(), firmware.getId(), upgradeTask.getId());
return upgradeTask.getId();
// 3. 生成设备升级记录
otaTaskRecordService.createOtaTaskRecordList(devices, firmware.getId(), task.getId());
return task.getId();
}
@Override
@@ -73,18 +79,17 @@ public class IotOtaTaskServiceImpl implements IotOtaTaskService {
// 1.1 校验升级任务是否存在
IotOtaTaskDO upgradeTask = validateUpgradeTaskExists(id);
// 1.2 校验升级任务是否可以取消
// TODO @liObjUtil notequals
if (!Objects.equals(upgradeTask.getStatus(), IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())) {
throw exception(OTA_UPGRADE_TASK_CANNOT_CANCEL);
if (ObjUtil.notEqual(upgradeTask.getStatus(), IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())) {
throw exception(OTA_TASK_CANCEL_FAIL_STATUS_END);
}
// 2. 更新 OTA 升级任务状态为已取消
// 2. 更新升级任务状态为已取消
otaTaskMapper.updateById(IotOtaTaskDO.builder()
.id(id).status(IotOtaTaskStatusEnum.CANCELED.getStatus())
.build());
// 3. 更新 OTA 升级记录状态为已取消
otaUpgradeRecordService.cancelUpgradeRecordByTaskId(id);
// 3. 更新升级记录状态为已取消
otaTaskRecordService.cancelTaskRecordListByTaskId(id);
}
@Override
@@ -97,50 +102,55 @@ public class IotOtaTaskServiceImpl implements IotOtaTaskService {
return otaTaskMapper.selectUpgradeTaskPage(pageReqVO);
}
/**
* 校验固件升级任务是否重复
*/
private void validateFirmwareTaskDuplicate(Long firmwareId, String taskName) {
List<IotOtaTaskDO> upgradeTaskList = otaTaskMapper.selectByFirmwareIdAndName(firmwareId, taskName);
if (CollUtil.isNotEmpty(upgradeTaskList)) {
throw exception(OTA_UPGRADE_TASK_NAME_DUPLICATE);
}
}
private void validateScopeAndDevice(Integer scope, List<Long> deviceIds, Long productId) {
if (Objects.equals(scope, IotOtaTaskDeviceScopeEnum.SELECT.getScope())) {
if (CollUtil.isEmpty(deviceIds)) {
throw exception(OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY);
private List<IotDeviceDO> validateOtaTaskDeviceScope(IotOtaTaskCreateReqVO createReqVO, Long productId) {
// 情况一:选择设备
if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.SELECT.getScope())) {
// 1.1 校验设备存在
List<IotDeviceDO> devices = deviceService.validateDeviceListExists(createReqVO.getDeviceIds());
for (IotDeviceDO device : devices) {
if (ObjUtil.notEqual(device.getProductId(), productId)) {
throw exception(DEVICE_NOT_EXISTS);
}
}
return;
// 1.2 校验设备是否已经是该固件版本
devices.forEach(device -> {
if (Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId())) {
throw exception(OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS, device.getDeviceName());
}
});
// 1.3 校验设备是否已经在升级中
List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(
convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
devices.forEach(device -> {
if (CollUtil.contains(records, item -> item.getDeviceId().equals(device.getId()))) {
throw exception(OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS, device.getDeviceName());
}
});
return devices;
}
if (Objects.equals(scope, IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
List<IotDeviceDO> deviceList = deviceService.getDeviceListByProductId(productId);
if (CollUtil.isEmpty(deviceList)) {
throw exception(OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY);
// 情况二:全部设备
if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(productId);
// 2.1.1 移除已经是该固件版本的设备
devices.removeIf(device -> Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId()));
// 2.1.2 移除已经在升级中的设备
List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(
convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
devices.removeIf(device -> CollUtil.contains(records,
item -> item.getDeviceId().equals(device.getId())));
// 2.2 校验是否有可升级的设备
if (CollUtil.isEmpty(devices)) {
throw exception(OTA_TASK_CREATE_FAIL_DEVICE_EMPTY);
}
return devices;
}
throw new IllegalArgumentException("不支持的设备范围:" + createReqVO.getDeviceScope());
}
private IotOtaTaskDO validateUpgradeTaskExists(Long id) {
IotOtaTaskDO upgradeTask = otaTaskMapper.selectById(id);
if (Objects.isNull(upgradeTask)) {
throw exception(OTA_UPGRADE_TASK_NOT_EXISTS);
}
return upgradeTask;
}
private IotOtaTaskDO initOtaUpgradeTask(IotOtaTaskCreateReqVO createReqVO, Long productId) {
IotOtaTaskDO upgradeTask = BeanUtils.toBean(createReqVO, IotOtaTaskDO.class);
upgradeTask.setDeviceTotalCount(Convert.toLong(CollUtil.size(createReqVO.getDeviceIds())))
.setStatus(IotOtaTaskStatusEnum.IN_PROGRESS.getStatus());
if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
List<IotDeviceDO> deviceList = deviceService.getDeviceListByProductId(productId);
upgradeTask.setDeviceTotalCount((long) deviceList.size());
// upgradeTask.setDeviceIds(
// deviceList.stream().map(IotDeviceDO::getId).collect(Collectors.toList()));
throw exception(OTA_TASK_NOT_EXISTS);
}
return upgradeTask;
}

View File

@@ -121,7 +121,7 @@ public class IotDataRuleServiceImpl implements IotDataRuleService {
convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getProductId));
// 2. 校验设备
deviceService.validateDevicesExist(convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getDeviceId,
deviceService.validateDeviceListExists(convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getDeviceId,
config -> ObjUtil.notEqual(config.getDeviceId(), IotDeviceDO.DEVICE_ID_ALL)));
// 3. 校验物模型存在