feat:【IoT 物联网】增加 OTA 升级任务的实现
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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 校验非空;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ public class IotDeviceDO extends TenantBaseDO {
|
||||
*
|
||||
* 关联 {@link IotOtaFirmwareDO#getId()}
|
||||
*/
|
||||
private String firmwareId;
|
||||
private Long firmwareId;
|
||||
|
||||
/**
|
||||
* 设备密钥,用于设备认证
|
||||
|
||||
@@ -61,7 +61,7 @@ public class IotOtaTaskDO extends BaseDO {
|
||||
/**
|
||||
* 设备总数数量
|
||||
*/
|
||||
private Long deviceTotalCount;
|
||||
private Integer deviceTotalCount;
|
||||
/**
|
||||
* 设备成功数量
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 @li:selectByFirmwareIdAndTaskIdAndDeviceId;让方法自解释
|
||||
/**
|
||||
* 根据条件查询单个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 @li:selectPage 就 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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
|
||||
@@ -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, "数据流转规则不存在");
|
||||
|
||||
@@ -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());
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 状态统计 Map,key 为状态码,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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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 @li:1)方法注释,简单写;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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import jakarta.validation.Valid;
|
||||
public interface IotOtaTaskService {
|
||||
|
||||
/**
|
||||
* 创建 OTA升 级任务
|
||||
* 创建 OTA 升级任务
|
||||
*
|
||||
* @param createReqVO 创建请求对象
|
||||
* @return 升级任务编号
|
||||
|
||||
@@ -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 @li:ObjUtil 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;
|
||||
}
|
||||
|
||||
@@ -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. 校验物模型存在
|
||||
|
||||
Reference in New Issue
Block a user