Commit ecf6a4a8463d416dc5a6b8b1d143458f4b98dd89

Authored by YunaiV
1 parent 4749d93d

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

Showing 23 changed files with 277 additions and 448 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskRecordController.java
... ... @@ -9,12 +9,16 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
9 9 import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService;
10 10 import io.swagger.v3.oas.annotations.Operation;
11 11 import io.swagger.v3.oas.annotations.Parameter;
  12 +import io.swagger.v3.oas.annotations.Parameters;
12 13 import io.swagger.v3.oas.annotations.tags.Tag;
13 14 import jakarta.annotation.Resource;
14 15 import jakarta.validation.Valid;
15 16 import org.springframework.security.access.prepost.PreAuthorize;
16 17 import org.springframework.validation.annotation.Validated;
17   -import org.springframework.web.bind.annotation.*;
  18 +import org.springframework.web.bind.annotation.GetMapping;
  19 +import org.springframework.web.bind.annotation.RequestMapping;
  20 +import org.springframework.web.bind.annotation.RequestParam;
  21 +import org.springframework.web.bind.annotation.RestController;
18 22  
19 23 import java.util.Map;
20 24  
... ... @@ -22,31 +26,29 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
22 26  
23 27 @Tag(name = "管理后台 - IoT OTA 升级任务记录")
24 28 @RestController
25   -@RequestMapping("/iot/ota/task-record")
  29 +@RequestMapping("/iot/ota/task/record")
26 30 @Validated
27 31 public class IotOtaTaskRecordController {
28 32  
29 33 @Resource
30 34 private IotOtaTaskRecordService otaTaskRecordService;
31 35  
32   - @GetMapping("/get-statistics")
33   - @Operation(summary = "固件升级设备统计")
  36 + @GetMapping("/get-status-count")
  37 + @Operation(summary = "获得 OTA 升级记录状态统计")
  38 + @Parameters({
  39 + @Parameter(name = "firmwareId", description = "固件编号", example = "1024"),
  40 + @Parameter(name = "taskId", description = "升级任务编号", example = "2048")
  41 + })
34 42 @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
35   - @Parameter(name = "firmwareId", description = "固件编号", required = true, example = "1024")
36   - public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatistics(@RequestParam(value = "firmwareId") Long firmwareId) {
37   - return success(otaTaskRecordService.getOtaTaskRecordStatistics(firmwareId));
38   - }
39   -
40   - @GetMapping("/get-count")
41   - @Operation(summary = "获得升级记录分页 tab 数量")
42   - @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
43   - public CommonResult<Map<Integer, Long>> getOtaTaskRecordCount(@Valid IotOtaTaskRecordPageReqVO pageReqVO) {
44   - return success(otaTaskRecordService.getOtaTaskRecordCount(pageReqVO));
  43 + public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatusCountMap(
  44 + @RequestParam(value = "firmwareId", required = false) Long firmwareId,
  45 + @RequestParam(value = "taskId", required = false) Long taskId) {
  46 + return success(otaTaskRecordService.getOtaTaskRecordStatusCountMap(firmwareId, taskId));
45 47 }
46 48  
47 49 @GetMapping("/page")
48   - @Operation(summary = "获得升级记录分页")
49   - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")
  50 + @Operation(summary = "获得 OTA 升级记录分页")
  51 + @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
50 52 public CommonResult<PageResult<IotOtaTaskRecordRespVO>> getOtaTaskRecordPage(
51 53 @Valid IotOtaTaskRecordPageReqVO pageReqVO) {
52 54 PageResult<IotOtaTaskRecordDO> pageResult = otaTaskRecordService.getOtaTaskRecordPage(pageReqVO);
... ... @@ -54,8 +56,8 @@ public class IotOtaTaskRecordController {
54 56 }
55 57  
56 58 @GetMapping("/get")
57   - @Operation(summary = "获得升级记录")
58   - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")
  59 + @Operation(summary = "获得 OTA 升级记录")
  60 + @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
59 61 @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024")
60 62 public CommonResult<IotOtaTaskRecordRespVO> getOtaTaskRecord(@RequestParam("id") Long id) {
61 63 IotOtaTaskRecordDO upgradeRecord = otaTaskRecordService.getOtaTaskRecord(id);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskCreateReqVO.java
... ... @@ -32,4 +32,6 @@ public class IotOtaTaskCreateReqVO {
32 32 @Schema(description = "选中的设备编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
33 33 private List<Long> deviceIds;
34 34  
  35 + // TODO @li:如果 deviceScope 等于 2 时,deviceIds 校验非空;
  36 +
35 37 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordPageReqVO.java
... ... @@ -2,19 +2,16 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 4 import io.swagger.v3.oas.annotations.media.Schema;
5   -import jakarta.validation.constraints.NotNull;
6 5 import lombok.Data;
7 6  
8 7 @Schema(description = "管理后台 - IoT OTA 升级记录分页 Request VO")
9 8 @Data
10 9 public class IotOtaTaskRecordPageReqVO extends PageParam {
11 10  
12   - // TODO @芋艿:分页条件字段梳理;
13   - @Schema(description = "升级任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
14   - @NotNull(message = "升级任务编号不能为空")
  11 + @Schema(description = "升级任务编号", example = "1024")
15 12 private Long taskId;
16 13  
17   - @Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "摄像头A1-1")
  14 + @Schema(description = "设备标识", example = "摄像头A1-1")
18 15 private String deviceName;
19 16  
20 17 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordRespVO.java
... ... @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6   -import java.time.LocalDateTime;
7   -
8 6 @Schema(description = "管理后台 - IoT OTA 升级记录 Response VO")
9 7 @Data
10 8 public class IotOtaTaskRecordRespVO {
... ... @@ -23,12 +21,6 @@ public class IotOtaTaskRecordRespVO {
23 21 @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
24 22 private Long taskId;
25 23  
26   - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "iot")
27   - private String productKey;
28   -
29   - @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iot")
30   - private String deviceName;
31   -
32 24 @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
33 25 private String deviceId;
34 26  
... ... @@ -47,10 +39,4 @@ public class IotOtaTaskRecordRespVO {
47 39 @Schema(description = "升级进度描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
48 40 private String description;
49 41  
50   - @Schema(description = "升级开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
51   - private LocalDateTime startTime;
52   -
53   - @Schema(description = "升级结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
54   - private LocalDateTime endTime;
55   -
56 42 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
... ... @@ -117,7 +117,7 @@ public class IotDeviceDO extends TenantBaseDO {
117 117 *
118 118 * 关联 {@link IotOtaFirmwareDO#getId()}
119 119 */
120   - private String firmwareId;
  120 + private Long firmwareId;
121 121  
122 122 /**
123 123 * 设备密钥,用于设备认证
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskDO.java
... ... @@ -61,7 +61,7 @@ public class IotOtaTaskDO extends BaseDO {
61 61 /**
62 62 * 设备总数数量
63 63 */
64   - private Long deviceTotalCount;
  64 + private Integer deviceTotalCount;
65 65 /**
66 66 * 设备成功数量
67 67 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskRecordDO.java
... ... @@ -7,9 +7,10 @@ import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
7 7 import com.baomidou.mybatisplus.annotation.KeySequence;
8 8 import com.baomidou.mybatisplus.annotation.TableId;
9 9 import com.baomidou.mybatisplus.annotation.TableName;
10   -import lombok.*;
11   -
12   -import java.time.LocalDateTime;
  10 +import lombok.AllArgsConstructor;
  11 +import lombok.Builder;
  12 +import lombok.Data;
  13 +import lombok.NoArgsConstructor;
13 14  
14 15 /**
15 16 * IoT OTA 升级任务记录 DO
... ... @@ -70,13 +71,5 @@ public class IotOtaTaskRecordDO extends BaseDO {
70 71 * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志
71 72 */
72 73 private String description;
73   - /**
74   - * 升级开始时间
75   - */
76   - private LocalDateTime startTime;
77   - /**
78   - * 升级结束时间
79   - */
80   - private LocalDateTime endTime;
81 74  
82 75 }
83 76 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java
... ... @@ -7,20 +7,11 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa
7 7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
10   -import java.util.List;
11   -
12 10 @Mapper
13 11 public interface IotOtaFirmwareMapper extends BaseMapperX<IotOtaFirmwareDO> {
14 12  
15   - /**
16   - * 根据产品ID和固件版本号查询固件信息列表。
17   - *
18   - * @param productId 产品ID,用于筛选固件信息。
19   - * @param version 固件版本号,用于筛选固件信息。
20   - * @return 返回符合条件的固件信息列表。
21   - */
22   - default List<IotOtaFirmwareDO> selectByProductIdAndVersion(Long productId, String version) {
23   - return selectList(IotOtaFirmwareDO::getProductId, productId,
  13 + default IotOtaFirmwareDO selectByProductIdAndVersion(Long productId, String version) {
  14 + return selectOne(IotOtaFirmwareDO::getProductId, productId,
24 15 IotOtaFirmwareDO::getVersion, version);
25 16 }
26 17  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskMapper.java
... ... @@ -7,20 +7,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageRe
7 7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
10   -import java.util.List;
11   -
12   -/**
13   - * OTA 升级任务 Mapper
14   - *
15   - * @author Shelly
16   - */
17 10 @Mapper
18 11 public interface IotOtaTaskMapper extends BaseMapperX<IotOtaTaskDO> {
19 12  
20   - default List<IotOtaTaskDO> selectByFirmwareIdAndName(Long firmwareId, String name) {
21   - return selectList(new LambdaQueryWrapperX<IotOtaTaskDO>()
22   - .eqIfPresent(IotOtaTaskDO::getFirmwareId, firmwareId)
23   - .eqIfPresent(IotOtaTaskDO::getName, name));
  13 + default IotOtaTaskDO selectByFirmwareIdAndName(Long firmwareId, String name) {
  14 + return selectOne(IotOtaTaskDO::getFirmwareId, firmwareId,
  15 + IotOtaTaskDO::getName, name);
24 16 }
25 17  
26 18 default PageResult<IotOtaTaskDO> selectUpgradeTaskPage(IotOtaTaskPageReqVO pageReqVO) {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.ota;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +import java.util.Set;
  13 +
  14 +@Mapper
  15 +public interface IotOtaTaskRecordMapper extends BaseMapperX<IotOtaTaskRecordDO> {
  16 +
  17 + default List<IotOtaTaskRecordDO> selectListByFirmwareIdAndTaskId(Long firmwareId, Long taskId) {
  18 + return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
  19 + .eqIfPresent(IotOtaTaskRecordDO::getFirmwareId, firmwareId)
  20 + .eqIfPresent(IotOtaTaskRecordDO::getTaskId, taskId)
  21 + .select(IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus));
  22 + }
  23 +
  24 + default PageResult<IotOtaTaskRecordDO> selectPage(IotOtaTaskRecordPageReqVO pageReqVO) {
  25 + return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
  26 + .eqIfPresent(IotOtaTaskRecordDO::getTaskId, pageReqVO.getTaskId()));
  27 + }
  28 +
  29 + default void updateByTaskIdAndStatus(Long taskId, Integer fromStatus, IotOtaTaskRecordDO updateRecord) {
  30 + update(updateRecord, new LambdaUpdateWrapper<IotOtaTaskRecordDO>()
  31 + .eq(IotOtaTaskRecordDO::getTaskId, taskId)
  32 + .eq(IotOtaTaskRecordDO::getStatus, fromStatus));
  33 + }
  34 +
  35 + default List<IotOtaTaskRecordDO> selectListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {
  36 + return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
  37 + .inIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceIds)
  38 + .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses));
  39 + }
  40 +
  41 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java deleted
1   -package cn.iocoder.yudao.module.iot.dal.mysql.ota;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
8   -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
9   -import org.apache.ibatis.annotations.Mapper;
10   -import org.apache.ibatis.annotations.Param;
11   -import org.apache.ibatis.annotations.Select;
12   -
13   -import java.util.List;
14   -import java.util.Map;
15   -
16   -@Mapper
17   -public interface IotOtaUpgradeRecordMapper extends BaseMapperX<IotOtaTaskRecordDO> {
18   -
19   - // TODO @li:selectByFirmwareIdAndTaskIdAndDeviceId;让方法自解释
20   - /**
21   - * 根据条件查询单个OTA升级记录
22   - *
23   - * @param firmwareId 固件ID,可选参数,用于筛选固件ID匹配的记录
24   - * @param taskId 任务ID,可选参数,用于筛选任务ID匹配的记录
25   - * @param deviceId 设备ID,可选参数,用于筛选设备ID匹配的记录
26   - * @return 返回符合条件的单个OTA升级记录,如果不存在则返回null
27   - */
28   - default IotOtaTaskRecordDO selectByConditions(Long firmwareId, Long taskId, String deviceId) {
29   - // 使用LambdaQueryWrapperX构建查询条件,根据传入的参数动态添加查询条件
30   - return selectOne(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
31   - .eqIfPresent(IotOtaTaskRecordDO::getFirmwareId, firmwareId)
32   - .eqIfPresent(IotOtaTaskRecordDO::getTaskId, taskId)
33   - .eqIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceId));
34   - }
35   -
36   - // TODO @li:这个是不是 groupby status 就 ok 拉?
37   - /**
38   - * 根据任务ID和设备名称查询OTA升级记录的状态统计信息。
39   - * 该函数通过SQL查询统计不同状态(0到5)的记录数量,并返回一个包含统计结果的Map列表。
40   - *
41   - * @param taskId 任务ID,用于筛选特定任务的OTA升级记录。
42   - * @param deviceName 设备名称,支持模糊查询,用于筛选特定设备的OTA升级记录。
43   - * @return 返回一个Map列表,每个Map包含不同状态(0到5)的记录数量。
44   - */
45   - @Select("select count(case when status = 0 then 1 else 0) as `0` " +
46   - "count(case when status = 1 then 1 else 0) as `1` " +
47   - "count(case when status = 2 then 1 else 0) as `2` " +
48   - "count(case when status = 3 then 1 else 0) as `3` " +
49   - "count(case when status = 4 then 1 else 0) as `4` " +
50   - "count(case when status = 5 then 1 else 0) as `5` " +
51   - "from iot_ota_upgrade_record " +
52   - "where task_id = #{taskId} " +
53   - "and device_name like concat('%', #{deviceName}, '%') " +
54   - "and status = #{status}")
55   - List<Map<String, Object>> selectOtaUpgradeRecordCount(@Param("taskId") Long taskId,
56   - @Param("deviceName") String deviceName);
57   -
58   - /**
59   - * 根据固件ID查询OTA升级记录的状态统计信息。
60   - * 该函数通过SQL查询统计不同状态(0到5)的记录数量,并返回一个包含统计结果的Map列表。
61   - *
62   - * @param firmwareId 固件ID,用于筛选特定固件的OTA升级记录。
63   - * @return 返回一个Map列表,每个Map包含不同状态(0到5)的记录数量。
64   - */
65   - @Select("select count(case when status = 0 then 1 else 0) as `0` " +
66   - "count(case when status = 1 then 1 else 0) as `1` " +
67   - "count(case when status = 2 then 1 else 0) as `2` " +
68   - "count(case when status = 3 then 1 else 0) as `3` " +
69   - "count(case when status = 4 then 1 else 0) as `4` " +
70   - "count(case when status = 5 then 1 else 0) as `5` " +
71   - "from iot_ota_upgrade_record " +
72   - "where firmware_id = #{firmwareId}")
73   - List<Map<String, Object>> selectOtaUpgradeRecordStatistics(Long firmwareId);
74   -
75   - // TODO @li:这里的注释,可以去掉哈
76   - /**
77   - * 根据分页查询条件获取 OTA升级记录的分页结果
78   - *
79   - * @param pageReqVO 分页查询请求参数,包含设备名称、任务ID等查询条件
80   - * @return 返回分页查询结果,包含符合条件的 OTA升级记录列表
81   - */
82   - // TODO @li:selectPage 就 ok 拉。
83   - default PageResult<IotOtaTaskRecordDO> selectUpgradeRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) {
84   - // TODO @li:这里的注释,可以去掉哈;然后下面的“如果”。。。也没必要注释
85   - // 使用LambdaQueryWrapperX构建查询条件,并根据请求参数动态添加查询条件
86   - return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
87   -// .likeIfPresent(IotOtaTaskRecordDO::getDeviceName, pageReqVO.getDeviceName()) // 如果设备名称存在,则添加模糊查询条件
88   - .eqIfPresent(IotOtaTaskRecordDO::getTaskId, pageReqVO.getTaskId())); // 如果任务ID存在,则添加等值查询条件
89   - }
90   -
91   - // TODO @li:这里的注释,可以去掉哈
92   - /**
93   - * 根据任务ID和状态更新升级记录的状态
94   - * <p>
95   - * 该函数用于将符合指定任务ID和状态的升级记录的状态更新为新的状态。
96   - *
97   - * @param setStatus 要设置的新状态值,类型为Integer
98   - * @param taskId 要更新的升级记录对应的任务ID,类型为Long
99   - * @param whereStatus 用于筛选升级记录的当前状态值,类型为Integer
100   - */
101   - // TODO @li:改成 updateByTaskIdAndStatus(taskId, status, IotOtaUpgradeRecordDO) 更通用一些。
102   - default void updateUpgradeRecordStatusByTaskIdAndStatus(Integer setStatus, Long taskId, Integer whereStatus) {
103   - // 使用LambdaUpdateWrapper构建更新条件,将指定状态的记录更新为指定状态
104   - update(new LambdaUpdateWrapper<IotOtaTaskRecordDO>()
105   - .set(IotOtaTaskRecordDO::getStatus, setStatus)
106   - .eq(IotOtaTaskRecordDO::getTaskId, taskId)
107   - .eq(IotOtaTaskRecordDO::getStatus, whereStatus)
108   - );
109   - }
110   -
111   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
... ... @@ -16,4 +16,7 @@ public class DictTypeConstants {
16 16  
17 17 public static final String ALERT_LEVEL = "iot_alert_level";
18 18  
  19 + public static final String OTA_TASK_DEVICE_SCOPE = "iot_ota_task_device_scope";
  20 + public static final String OTA_TASK_STATUS = "iot_ota_task_status";
  21 +
19 22 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
... ... @@ -41,20 +41,25 @@ public interface ErrorCodeConstants {
41 41 ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在");
42 42 ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除");
43 43  
44   - // ========== 固件相关 1-050-008-000 ==========
  44 + // ========== OTA 固件相关 1-050-008-000 ==========
45 45  
46 46 ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在");
47 47 ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, "产品版本号重复");
48 48  
49   - ErrorCode OTA_UPGRADE_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, "升级任务不存在");
50   - ErrorCode OTA_UPGRADE_TASK_NAME_DUPLICATE = new ErrorCode(1_050_008_101, "升级任务名称重复");
51   - ErrorCode OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY = new ErrorCode(1_050_008_102, "设备编号列表不能为空");
52   - ErrorCode OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY = new ErrorCode(1_050_008_103, "设备列表不能为空");
53   - ErrorCode OTA_UPGRADE_TASK_CANNOT_CANCEL = new ErrorCode(1_050_008_104, "升级任务不能取消");
  49 + // ========== OTA 升级任务相关 1-050-008-100 ==========
54 50  
55   - ErrorCode OTA_UPGRADE_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在");
56   - ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_201, "升级记录重复");
57   - ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_202, "升级记录不能重试");
  51 + ErrorCode OTA_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, "升级任务不存在");
  52 + ErrorCode OTA_TASK_CREATE_FAIL_NAME_DUPLICATE = new ErrorCode(1_050_008_101, "创建 OTA 任务失败,原因:任务名称重复");
  53 + ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS = new ErrorCode(1_050_008_102,
  54 + "创建 OTA 任务失败,原因:设备({})已经是该固件版本");
  55 + ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS = new ErrorCode(1_050_008_102,
  56 + "创建 OTA 任务失败,原因:设备({})已经在升级中...");
  57 + ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_EMPTY = new ErrorCode(1_050_008_103, "创建 OTA 任务失败,原因:没有可升级的设备");
  58 + ErrorCode OTA_TASK_CANCEL_FAIL_STATUS_END = new ErrorCode(1_050_008_104, "取消 OTA 任务失败,原因:任务状态不是进行中");
  59 +
  60 + // ========== OTA 升级任务相关 1-050-008-100 ==========
  61 +
  62 + ErrorCode OTA_TASK_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在");
58 63  
59 64 // ========== IoT 数据流转规则 1-050-010-000 ==========
60 65 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
... ... @@ -2,10 +2,13 @@ package cn.iocoder.yudao.module.iot.enums.ota;
2 2  
3 3  
4 4 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  5 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
5 6 import lombok.Getter;
6 7 import lombok.RequiredArgsConstructor;
7 8  
8 9 import java.util.Arrays;
  10 +import java.util.List;
  11 +import java.util.Set;
9 12  
10 13 /**
11 14 * IoT OTA 升级任务记录的状态枚举
... ... @@ -26,6 +29,17 @@ public enum IotOtaTaskRecordStatusEnum implements ArrayValuable&lt;Integer&gt; {
26 29 public static final Integer[] ARRAYS = Arrays.stream(values())
27 30 .map(IotOtaTaskRecordStatusEnum::getStatus).toArray(Integer[]::new);
28 31  
  32 + public static final Set<Integer> IN_PROCESS_STATUSES = SetUtils.asSet(
  33 + PENDING.getStatus(),
  34 + PUSHED.getStatus(),
  35 + UPGRADING.getStatus(),
  36 + SUCCESS.getStatus());
  37 +
  38 + public static final List<Integer> PRIORITY_STATUSES = Arrays.asList(
  39 + SUCCESS.getStatus(),
  40 + PENDING.getStatus(), PUSHED.getStatus(), UPGRADING.getStatus(),
  41 + FAILURE.getStatus(), CANCELED.getStatus());
  42 +
29 43 /**
30 44 * 状态
31 45 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
... ... @@ -12,7 +12,6 @@ import java.time.LocalDateTime;
12 12 import java.util.Collection;
13 13 import java.util.List;
14 14 import java.util.Map;
15   -import java.util.Set;
16 15  
17 16 /**
18 17 * IoT 设备 Service 接口
... ... @@ -155,22 +154,14 @@ public interface IotDeviceService {
155 154 List<IotDeviceDO> getDeviceListByState(Integer state);
156 155  
157 156 /**
158   - * 根据产品ID获取设备列表
  157 + * 根据产品编号,获取设备列表
159 158 *
160   - * @param productId 产品ID,用于查询特定产品的设备列表
161   - * @return 返回与指定产品ID关联的设备列表,列表中的每个元素为IotDeviceDO对象
  159 + * @param productId 产品编号
  160 + * @return 设备列表
162 161 */
163 162 List<IotDeviceDO> getDeviceListByProductId(Long productId);
164 163  
165 164 /**
166   - * 根据设备ID列表获取设备信息列表
167   - *
168   - * @param deviceIdList 设备ID列表,包含需要查询的设备ID
169   - * @return 返回与设备ID列表对应的设备信息列表,列表中的每个元素为IotDeviceDO对象
170   - */
171   - List<IotDeviceDO> getDeviceListByIdList(List<Long> deviceIdList);
172   -
173   - /**
174 165 * 基于产品编号,获得设备数量
175 166 *
176 167 * @param productId 产品编号
... ... @@ -248,6 +239,6 @@ public interface IotDeviceService {
248 239 *
249 240 * @param ids 设备编号数组
250 241 */
251   - void validateDevicesExist(Set<Long> ids);
  242 + List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids);
252 243  
253 244 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
... ... @@ -279,11 +279,6 @@ public class IotDeviceServiceImpl implements IotDeviceService {
279 279 }
280 280  
281 281 @Override
282   - public List<IotDeviceDO> getDeviceListByIdList(List<Long> deviceIdList) {
283   - return deviceMapper.selectByIds(deviceIdList);
284   - }
285   -
286   - @Override
287 282 public void updateDeviceState(IotDeviceDO device, Integer state) {
288 283 // 1. 更新状态和时间
289 284 IotDeviceDO updateObj = new IotDeviceDO().setId(device.getId()).setState(state);
... ... @@ -474,14 +469,15 @@ public class IotDeviceServiceImpl implements IotDeviceService {
474 469 }
475 470  
476 471 @Override
477   - public void validateDevicesExist(Set<Long> ids) {
  472 + public List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids) {
478 473 if (CollUtil.isEmpty(ids)) {
479   - return;
  474 + return Collections.emptyList();
480 475 }
481   - List<IotDeviceDO> deviceIds = deviceMapper.selectByIds(ids);
482   - if (deviceIds.size() != ids.size()) {
  476 + List<IotDeviceDO> devices = deviceMapper.selectByIds(ids);
  477 + if (devices.size() != ids.size()) {
483 478 throw exception(DEVICE_NOT_EXISTS);
484 479 }
  480 + return devices;
485 481 }
486 482  
487 483 private IotDeviceServiceImpl getSelf() {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java
... ... @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
8 8 import jakarta.validation.Valid;
9 9  
10 10 /**
11   - * OTA 固件管理 Service
  11 + * OTA 固件管理 Service 接口
12 12 *
13 13 * @author Shelly Chan
14 14 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java
... ... @@ -23,9 +23,14 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
23 23 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS;
24 24 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE;
25 25  
26   -@Slf4j
  26 +/**
  27 + * OTA 固件管理 Service 实现类
  28 + *
  29 + * @author Shelly Chan
  30 + */
27 31 @Service
28 32 @Validated
  33 +@Slf4j
29 34 public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
30 35  
31 36 @Resource
... ... @@ -37,7 +42,9 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
37 42 @Override
38 43 public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) {
39 44 // 1.1 校验固件产品 + 版本号不能重复
40   - validateProductAndVersionDuplicate(saveReqVO.getProductId(), saveReqVO.getVersion());
  45 + if (otaFirmwareMapper.selectByProductIdAndVersion(saveReqVO.getProductId(), saveReqVO.getVersion()) != null) {
  46 + throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);
  47 + }
41 48 // 1.2 校验产品存在
42 49 productService.validateProductExists(saveReqVO.getProductId());
43 50  
... ... @@ -84,24 +91,11 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
84 91 }
85 92  
86 93 /**
87   - * 验证产品和版本号是否重复
88   - */
89   - private void validateProductAndVersionDuplicate(Long productId, String version) {
90   - // 只查询1条记录检查是否存在
91   - IotOtaFirmwareDO firmware = otaFirmwareMapper.selectOne(IotOtaFirmwareDO::getProductId, productId,
92   - IotOtaFirmwareDO::getVersion, version);
93   - if (firmware != null) {
94   - throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);
95   - }
96   - }
97   -
98   - /**
99 94 * 计算文件签名
100   - *
  95 + *
101 96 * @param firmware 固件对象
102   - * @throws Exception 下载或计算签名失败时抛出异常
103 97 */
104   - private void calculateFileDigest(IotOtaFirmwareDO firmware) throws Exception {
  98 + private void calculateFileDigest(IotOtaFirmwareDO firmware) {
105 99 String fileUrl = firmware.getFileUrl();
106 100 // 下载文件并计算签名
107 101 byte[] fileBytes = HttpUtil.downloadBytes(fileUrl);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java
... ... @@ -2,67 +2,67 @@ package cn.iocoder.yudao.module.iot.service.ota;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
5 6 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
6 7 import jakarta.validation.Valid;
7 8  
8 9 import java.util.List;
9 10 import java.util.Map;
  11 +import java.util.Set;
10 12  
11   -// TODO @li:注释写的有点冗余,可以看看别的模块哈。= = AI 生成的注释,有的时候太啰嗦了,需要处理下的哈
12 13 /**
13   - * IotOtaUpgradeRecordService 接口定义了与物联网设备OTA升级记录相关的操作。
14   - * 该接口提供了创建、更新、查询、统计和重试升级记录的功能。
  14 + * IoT OTA 升级记录 Service 接口
15 15 */
16 16 public interface IotOtaTaskRecordService {
17 17  
18 18 /**
19 19 * 批量创建 OTA 升级记录
20   - * 该函数用于为指定的设备列表、固件ID和升级任务ID创建OTA升级记录。
21 20 *
22   - * @param deviceIds 设备ID列表,表示需要升级的设备集合。
23   - * @param firmwareId 固件ID,表示要升级到的固件版本。
24   - * @param upgradeTaskId 升级任务ID,表示此次升级任务的唯一标识。
  21 + * @param devices 设备列表
  22 + * @param firmwareId 固件编号
  23 + * @param taskId 任务编号
25 24 */
26   - void createOtaTaskRecordBatch(List<Long> deviceIds, Long firmwareId, Long upgradeTaskId);
  25 + void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId);
27 26  
28 27 /**
29   - * 获取 OTA 升级记录的数量统计
  28 + * 获取 OTA 升级记录的状态统计
30 29 *
31   - * @return 返回一个 Map,其中键为状态码,值为对应状态的升级记录数量
  30 + * @param firmwareId 固件编号
  31 + * @param taskId 任务编号
  32 + * @return 状态统计 Map,key 为状态码,value 为对应状态的升级记录数量
32 33 */
33   - Map<Integer, Long> getOtaTaskRecordCount(@Valid IotOtaTaskRecordPageReqVO pageReqVO);
  34 + Map<Integer, Long> getOtaTaskRecordStatusCountMap(Long firmwareId, Long taskId);
34 35  
35 36 /**
36   - * 获取 OTA 升级记录的统计信息
  37 + * 获取 OTA 升级记录
37 38 *
38   - * @return 返回一个 Map,其中键为状态码,值为对应状态的升级记录统计信息
  39 + * @param id 编号
  40 + * @return OTA 升级记录
39 41 */
40   - Map<Integer, Long> getOtaTaskRecordStatistics(Long firmwareId);
  42 + IotOtaTaskRecordDO getOtaTaskRecord(Long id);
41 43  
42 44 /**
43   - * 获取指定 ID 的 OTA 升级记录的详细信息
  45 + * 获取 OTA 升级记录分页
44 46 *
45   - * @param id 需要查询的升级记录的 ID
46   - * @return 返回包含升级记录详细信息的响应对象
  47 + * @param pageReqVO 分页查询
  48 + * @return OTA 升级记录分页
47 49 */
48   - IotOtaTaskRecordDO getOtaTaskRecord(Long id);
  50 + PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(@Valid IotOtaTaskRecordPageReqVO pageReqVO);
49 51  
50 52 /**
51   - * 分页查询 OTA 升级记录
  53 + * 根据 OTA 任务编号,取消未结束的升级记录
52 54 *
53   - * @param pageReqVO 包含分页查询条件的请求对象,必须经过验证。
54   - * @return 返回包含分页查询结果的响应对象。
  55 + * @param taskId 升级任务编号
55 56 */
56   - PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(@Valid IotOtaTaskRecordPageReqVO pageReqVO);
  57 + void cancelTaskRecordListByTaskId(Long taskId);
57 58  
58 59 /**
59   - * 根据任务 ID 取消升级记录
60   - * <p>
61   - * 该函数用于根据给定的任务ID,取消与该任务相关的升级记录。通常用于在任务执行失败或用户手动取消时,
62   - * 清理或标记相关的升级记录为取消状态。
  60 + * 根据设备编号和记录状态,获取 OTA 升级记录列表
63 61 *
64   - * @param taskId 要取消升级记录的任务ID。该ID唯一标识一个任务,通常由任务管理系统生成。
  62 + * @param deviceIds 设备编号集合
  63 + * @param statuses 记录状态集合
  64 + * @return OTA 升级记录列表
65 65 */
66   - void cancelUpgradeRecordByTaskId(Long taskId);
  66 + List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses);
67 67  
68 68 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java
... ... @@ -2,177 +2,100 @@ package cn.iocoder.yudao.module.iot.service.ota;
2 2  
3 3 import cn.hutool.core.convert.Convert;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
6 5 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
7 6 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
8   -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
9 7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
10   -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
11   -import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeRecordMapper;
  8 +import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskRecordMapper;
12 9 import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
13   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
14 10 import jakarta.annotation.Resource;
15 11 import lombok.extern.slf4j.Slf4j;
16 12 import org.springframework.stereotype.Service;
17   -import org.springframework.transaction.annotation.Transactional;
18 13 import org.springframework.validation.annotation.Validated;
19 14  
  15 +import java.util.Arrays;
20 16 import java.util.List;
21 17 import java.util.Map;
22   -import java.util.Objects;
23   -import java.util.stream.Collectors;
  18 +import java.util.Set;
24 19  
25 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26   -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  22 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_TASK_RECORD_NOT_EXISTS;
27 23  
28   -// TODO @li:@Service、@Validated、@Slf4j,先用关键注解;2)类注释,简单写
29   -@Slf4j
  24 +/**
  25 + * OTA 升级任务记录 Service 实现类
  26 + */
30 27 @Service
31 28 @Validated
  29 +@Slf4j
32 30 public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService {
33 31  
34 32 @Resource
35   - private IotOtaUpgradeRecordMapper iotOtaUpgradeRecordMapper;
36   -
37   - @Resource
38   - private IotDeviceService deviceService;
39   - @Resource
40   - private IotOtaFirmwareService firmwareService;
41   - @Resource
42   - private IotOtaTaskService upgradeTaskService;
  33 + private IotOtaTaskRecordMapper otaTaskRecordMapper;
43 34  
44 35 @Override
45   - public void createOtaTaskRecordBatch(List<Long> deviceIds, Long firmwareId, Long upgradeTaskId) {
46   - // 1. 校验升级记录信息是否存在,并且已经取消的任务可以重新开始
47   - deviceIds.forEach(deviceId -> validateUpgradeRecordDuplicate(firmwareId, upgradeTaskId, String.valueOf(deviceId)));
48   -
49   - // 2. 初始化OTA升级记录列表信息
50   - IotOtaTaskDO upgradeTask = upgradeTaskService.getOtaTask(upgradeTaskId);
51   - IotOtaFirmwareDO firmware = firmwareService.getOtaFirmware(firmwareId);
52   - List<IotDeviceDO> deviceList = deviceService.getDeviceListByIdList(deviceIds);
53   - List<IotOtaTaskRecordDO> upgradeRecordList = deviceList.stream().map(device -> {
54   - IotOtaTaskRecordDO upgradeRecord = new IotOtaTaskRecordDO();
55   - upgradeRecord.setFirmwareId(firmware.getId());
56   - upgradeRecord.setTaskId(upgradeTask.getId());
57   - upgradeRecord.setDeviceId(device.getId());
58   - upgradeRecord.setFromFirmwareId(Convert.toLong(device.getFirmwareId()));
59   - upgradeRecord.setStatus(IotOtaTaskRecordStatusEnum.PENDING.getStatus());
60   - upgradeRecord.setProgress(0);
61   - return upgradeRecord;
62   - }).toList();
63   - // 3. 保存数据
64   - iotOtaUpgradeRecordMapper.insertBatch(upgradeRecordList);
65   - // TODO @芋艿:在这里需要处理推送升级任务的逻辑
  36 + public void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId) {
  37 + List<IotOtaTaskRecordDO> records = convertList(devices, device ->
  38 + IotOtaTaskRecordDO.builder().firmwareId(firmwareId).taskId(taskId)
  39 + .deviceId(device.getId()).fromFirmwareId(Convert.toLong(device.getFirmwareId()))
  40 + .status(IotOtaTaskRecordStatusEnum.PENDING.getStatus()).progress(0).build());
  41 + otaTaskRecordMapper.insertBatch(records);
66 42 }
67 43  
68   - // TODO @li:1)方法注释,简单写;2)父类写了注释,子类就不用写了。。。
69   - /**
70   - * 获取OTA升级记录的数量统计。
71   - * 该方法根据传入的查询条件,统计不同状态的OTA升级记录数量,并返回一个包含各状态数量的映射。
72   - *
73   - * @param pageReqVO 包含查询条件的请求对象,主要包括任务ID和设备名称等信息。
74   - * @return 返回一个Map,其中键为状态常量,值为对应状态的记录数量。
75   - */
76 44 @Override
77   - @Transactional
78   - public Map<Integer, Long> getOtaTaskRecordCount(IotOtaTaskRecordPageReqVO pageReqVO) {
79   - // 分别查询不同状态的OTA升级记录数量
80   - List<Map<String, Object>> upgradeRecordCountList = iotOtaUpgradeRecordMapper.selectOtaUpgradeRecordCount(
81   - pageReqVO.getTaskId(), pageReqVO.getDeviceName());
82   - Map<String, Object> upgradeRecordCountMap = ObjectUtils.defaultIfNull(upgradeRecordCountList.get(0));
83   - Objects.requireNonNull(upgradeRecordCountMap);
84   - return upgradeRecordCountMap.entrySet().stream().collect(Collectors.toMap(
85   - entry -> Convert.toInt(entry.getKey()),
86   - entry -> Convert.toLong(entry.getValue())));
  45 + public Map<Integer, Long> getOtaTaskRecordStatusCountMap(Long firmwareId, Long taskId) {
  46 + // 按照 status 枚举,初始化 countMap 为 0
  47 + Map<Integer, Long> countMap = convertMap(Arrays.asList(IotOtaTaskRecordStatusEnum.values()),
  48 + IotOtaTaskRecordStatusEnum::getStatus, iotOtaTaskRecordStatusEnum -> 0L);
  49 +
  50 + // 查询记录,只返回 id、status 字段
  51 + List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByFirmwareIdAndTaskId(firmwareId, taskId);
  52 + Map<Long, List<Integer>> deviceStatusesMap = convertMultiMap(records,
  53 + IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus);
  54 + // 找到第一个匹配的优先级状态,避免重复计算
  55 + deviceStatusesMap.forEach((deviceId, statuses) -> {
  56 + for (Integer priorityStatus : IotOtaTaskRecordStatusEnum.PRIORITY_STATUSES) {
  57 + if (statuses.contains(priorityStatus)) {
  58 + countMap.put(priorityStatus, countMap.get(priorityStatus) + 1);
  59 + return;
  60 + }
  61 + }
  62 + });
  63 + return countMap;
87 64 }
88 65  
89 66 @Override
90   - @Transactional
91   - public Map<Integer, Long> getOtaTaskRecordStatistics(Long firmwareId) {
92   - // 查询并统计不同状态的OTA升级记录数量
93   - List<Map<String, Object>> upgradeRecordStatisticsList = iotOtaUpgradeRecordMapper.selectOtaUpgradeRecordStatistics(firmwareId);
94   - Map<String, Object> upgradeRecordStatisticsMap = ObjectUtils.defaultIfNull(upgradeRecordStatisticsList.get(0));
95   - Objects.requireNonNull(upgradeRecordStatisticsMap);
96   - return upgradeRecordStatisticsMap.entrySet().stream().collect(Collectors.toMap(
97   - entry -> Convert.toInt(entry.getKey()),
98   - entry -> Convert.toLong(entry.getValue())));
  67 + public IotOtaTaskRecordDO getOtaTaskRecord(Long id) {
  68 + return otaTaskRecordMapper.selectById(id);
99 69 }
100 70  
101 71 @Override
102   - public IotOtaTaskRecordDO getOtaTaskRecord(Long id) {
103   - return iotOtaUpgradeRecordMapper.selectById(id);
  72 + public PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) {
  73 + return otaTaskRecordMapper.selectPage(pageReqVO);
104 74 }
105 75  
106 76 @Override
107   - public PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) {
108   - return iotOtaUpgradeRecordMapper.selectUpgradeRecordPage(pageReqVO);
  77 + 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);
109 86 }
110 87  
111 88 @Override
112   - public void cancelUpgradeRecordByTaskId(Long taskId) {
113   - // 暂定只有待推送的升级记录可以取消 TODO @芋艿:可以看看阿里云,哪些可以取消
114   - iotOtaUpgradeRecordMapper.updateUpgradeRecordStatusByTaskIdAndStatus(
115   - IotOtaTaskRecordStatusEnum.CANCELED.getStatus(), taskId,
116   - IotOtaTaskRecordStatusEnum.PENDING.getStatus());
  89 + public List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {
  90 + return otaTaskRecordMapper.selectListByDeviceIdAndStatus(deviceIds, statuses);
117 91 }
118 92  
119   - /**
120   - * 验证指定的升级记录是否存在。
121   - * <p>
122   - * 该函数通过给定的ID查询升级记录,如果查询结果为空,则抛出异常,表示升级记录不存在。
123   - *
124   - * @param id 升级记录的唯一标识符,类型为Long。
125   - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出异常,异常类型为OTA_UPGRADE_RECORD_NOT_EXISTS。
126   - */
127 93 private IotOtaTaskRecordDO validateUpgradeRecordExists(Long id) {
128   - // 根据ID查询升级记录
129   - IotOtaTaskRecordDO upgradeRecord = iotOtaUpgradeRecordMapper.selectById(id);
130   - // 如果查询结果为空,抛出异常
  94 + IotOtaTaskRecordDO upgradeRecord = otaTaskRecordMapper.selectById(id);
131 95 if (upgradeRecord == null) {
132   - throw exception(OTA_UPGRADE_RECORD_NOT_EXISTS);
  96 + throw exception(OTA_TASK_RECORD_NOT_EXISTS);
133 97 }
134 98 return upgradeRecord;
135 99 }
136 100  
137   - // TODO @li:注释有点冗余
138   - /**
139   - * 校验固件升级记录是否重复。
140   - * <p>
141   - * 该函数用于检查给定的固件ID、任务ID和设备ID是否已经存在未取消的升级记录。
142   - * 如果存在未取消的记录,则抛出异常,提示升级记录重复。
143   - *
144   - * @param firmwareId 固件ID,用于标识特定的固件版本
145   - * @param taskId 任务ID,用于标识特定的升级任务
146   - * @param deviceId 设备ID,用于标识特定的设备
147   - */
148   - private void validateUpgradeRecordDuplicate(Long firmwareId, Long taskId, String deviceId) {
149   - IotOtaTaskRecordDO upgradeRecord = iotOtaUpgradeRecordMapper.selectByConditions(firmwareId, taskId, deviceId);
150   - if (upgradeRecord == null) {
151   - return;
152   - }
153   - if (!Objects.equals(upgradeRecord.getStatus(), IotOtaTaskRecordStatusEnum.CANCELED.getStatus())) {
154   - throw exception(OTA_UPGRADE_RECORD_DUPLICATE);
155   - }
156   - }
157   -
158   - // TODO @li:注释有点冗余
159   - /**
160   - * 验证升级记录是否可以重试。
161   - * <p>
162   - * 该方法用于检查给定的升级记录是否处于允许重试的状态。如果升级记录的状态为
163   - * PENDING、PUSHED 或 UPGRADING,则抛出异常,表示不允许重试。
164   - *
165   - * @param upgradeRecord 需要验证的升级记录对象,类型为 IotOtaUpgradeRecordDO
166   - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出 OTA_UPGRADE_RECORD_CANNOT_RETRY 异常
167   - */
168   - // TODO @li:这种一次性的方法(不复用的),其实一步一定要抽成小方法;
169   - private void validateUpgradeRecordCanRetry(IotOtaTaskRecordDO upgradeRecord) {
170   - if (ObjectUtils.equalsAny(upgradeRecord.getStatus(),
171   - IotOtaTaskRecordStatusEnum.PENDING.getStatus(),
172   - IotOtaTaskRecordStatusEnum.PUSHED.getStatus(),
173   - IotOtaTaskRecordStatusEnum.UPGRADING.getStatus())) {
174   - throw exception(OTA_UPGRADE_RECORD_CANNOT_RETRY);
175   - }
176   - }
177   -
178 101 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskService.java
... ... @@ -14,7 +14,7 @@ import jakarta.validation.Valid;
14 14 public interface IotOtaTaskService {
15 15  
16 16 /**
17   - * 创建 OTA升 级任务
  17 + * 创建 OTA 升级任务
18 18 *
19 19 * @param createReqVO 创建请求对象
20 20 * @return 升级任务编号
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.service.ota;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.convert.Convert;
  4 +import cn.hutool.core.util.ObjUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 7 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
9 9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
10 10 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
11 11 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
  12 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
12 13 import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskMapper;
13 14 import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;
  15 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
14 16 import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum;
15 17 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
16 18 import jakarta.annotation.Resource;
... ... @@ -24,6 +26,7 @@ import java.util.List;
24 26 import java.util.Objects;
25 27  
26 28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
27 30 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
28 31  
29 32 /**
... ... @@ -45,26 +48,29 @@ public class IotOtaTaskServiceImpl implements IotOtaTaskService {
45 48 private IotOtaFirmwareService otaFirmwareService;
46 49 @Resource
47 50 @Lazy // 延迟,避免循环依赖报错
48   - private IotOtaTaskRecordService otaUpgradeRecordService;
  51 + private IotOtaTaskRecordService otaTaskRecordService;
49 52  
50 53 @Override
51 54 @Transactional(rollbackFor = Exception.class)
52 55 public Long createOtaTask(IotOtaTaskCreateReqVO createReqVO) {
53   - // 1.1 校验同一固件的升级任务名称不重复
54   - validateFirmwareTaskDuplicate(createReqVO.getFirmwareId(), createReqVO.getName());
55   - // 1.2 校验固件信息是否存在
  56 + // 1.1 校验固件信息是否存在
56 57 IotOtaFirmwareDO firmware = otaFirmwareService.validateFirmwareExists(createReqVO.getFirmwareId());
57   - // 1.3 补全设备范围信息,并且校验是否又设备可以升级,如果没有设备可以升级,则报错
58   - validateScopeAndDevice(createReqVO.getDeviceScope(), createReqVO.getDeviceIds(), firmware.getProductId());
59   -
60   - // 2. 保存 OTA 升级任务信息到数据库
61   - IotOtaTaskDO upgradeTask = initOtaUpgradeTask(createReqVO, firmware.getProductId());
62   - otaTaskMapper.insert(upgradeTask);
63   -
64   - // 3. 生成设备升级记录信息并存储,等待定时任务轮询
65   - // TODO @芋艿:在处理;
66   -// otaUpgradeRecordService.createOtaUpgradeRecordBatch(upgradeTask.getDeviceIds(), firmware.getId(), upgradeTask.getId());
67   - return upgradeTask.getId();
  58 + // 1.2 校验同一固件的升级任务名称不重复
  59 + if (otaTaskMapper.selectByFirmwareIdAndName(firmware.getId(), createReqVO.getName()) != null) {
  60 + throw exception(OTA_TASK_CREATE_FAIL_NAME_DUPLICATE);
  61 + }
  62 + // 1.3 校验设备范围信息
  63 + List<IotDeviceDO> devices = validateOtaTaskDeviceScope(createReqVO, firmware.getProductId());
  64 +
  65 + // 2. 保存升级任务,直接转换
  66 + IotOtaTaskDO task = BeanUtils.toBean(createReqVO, IotOtaTaskDO.class)
  67 + .setStatus(IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())
  68 + .setDeviceTotalCount(devices.size()).setDeviceSuccessCount(0);
  69 + otaTaskMapper.insert(task);
  70 +
  71 + // 3. 生成设备升级记录
  72 + otaTaskRecordService.createOtaTaskRecordList(devices, firmware.getId(), task.getId());
  73 + return task.getId();
68 74 }
69 75  
70 76 @Override
... ... @@ -73,18 +79,17 @@ public class IotOtaTaskServiceImpl implements IotOtaTaskService {
73 79 // 1.1 校验升级任务是否存在
74 80 IotOtaTaskDO upgradeTask = validateUpgradeTaskExists(id);
75 81 // 1.2 校验升级任务是否可以取消
76   - // TODO @li:ObjUtil notequals
77   - if (!Objects.equals(upgradeTask.getStatus(), IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())) {
78   - throw exception(OTA_UPGRADE_TASK_CANNOT_CANCEL);
  82 + if (ObjUtil.notEqual(upgradeTask.getStatus(), IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())) {
  83 + throw exception(OTA_TASK_CANCEL_FAIL_STATUS_END);
79 84 }
80 85  
81   - // 2. 更新 OTA 升级任务状态为已取消
  86 + // 2. 更新升级任务状态为已取消
82 87 otaTaskMapper.updateById(IotOtaTaskDO.builder()
83 88 .id(id).status(IotOtaTaskStatusEnum.CANCELED.getStatus())
84 89 .build());
85 90  
86   - // 3. 更新 OTA 升级记录状态为已取消
87   - otaUpgradeRecordService.cancelUpgradeRecordByTaskId(id);
  91 + // 3. 更新升级记录状态为已取消
  92 + otaTaskRecordService.cancelTaskRecordListByTaskId(id);
88 93 }
89 94  
90 95 @Override
... ... @@ -97,50 +102,55 @@ public class IotOtaTaskServiceImpl implements IotOtaTaskService {
97 102 return otaTaskMapper.selectUpgradeTaskPage(pageReqVO);
98 103 }
99 104  
100   - /**
101   - * 校验固件升级任务是否重复
102   - */
103   - private void validateFirmwareTaskDuplicate(Long firmwareId, String taskName) {
104   - List<IotOtaTaskDO> upgradeTaskList = otaTaskMapper.selectByFirmwareIdAndName(firmwareId, taskName);
105   - if (CollUtil.isNotEmpty(upgradeTaskList)) {
106   - throw exception(OTA_UPGRADE_TASK_NAME_DUPLICATE);
107   - }
108   - }
109   -
110   - private void validateScopeAndDevice(Integer scope, List<Long> deviceIds, Long productId) {
111   - if (Objects.equals(scope, IotOtaTaskDeviceScopeEnum.SELECT.getScope())) {
112   - if (CollUtil.isEmpty(deviceIds)) {
113   - throw exception(OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY);
  105 + private List<IotDeviceDO> validateOtaTaskDeviceScope(IotOtaTaskCreateReqVO createReqVO, Long productId) {
  106 + // 情况一:选择设备
  107 + if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.SELECT.getScope())) {
  108 + // 1.1 校验设备存在
  109 + List<IotDeviceDO> devices = deviceService.validateDeviceListExists(createReqVO.getDeviceIds());
  110 + for (IotDeviceDO device : devices) {
  111 + if (ObjUtil.notEqual(device.getProductId(), productId)) {
  112 + throw exception(DEVICE_NOT_EXISTS);
  113 + }
114 114 }
115   - return;
  115 + // 1.2 校验设备是否已经是该固件版本
  116 + devices.forEach(device -> {
  117 + if (Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId())) {
  118 + throw exception(OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS, device.getDeviceName());
  119 + }
  120 + });
  121 + // 1.3 校验设备是否已经在升级中
  122 + List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(
  123 + convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
  124 + devices.forEach(device -> {
  125 + if (CollUtil.contains(records, item -> item.getDeviceId().equals(device.getId()))) {
  126 + throw exception(OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS, device.getDeviceName());
  127 + }
  128 + });
  129 + return devices;
116 130 }
117   -
118   - if (Objects.equals(scope, IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
119   - List<IotDeviceDO> deviceList = deviceService.getDeviceListByProductId(productId);
120   - if (CollUtil.isEmpty(deviceList)) {
121   - throw exception(OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY);
  131 + // 情况二:全部设备
  132 + if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
  133 + List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(productId);
  134 + // 2.1.1 移除已经是该固件版本的设备
  135 + devices.removeIf(device -> Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId()));
  136 + // 2.1.2 移除已经在升级中的设备
  137 + List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(
  138 + convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
  139 + devices.removeIf(device -> CollUtil.contains(records,
  140 + item -> item.getDeviceId().equals(device.getId())));
  141 + // 2.2 校验是否有可升级的设备
  142 + if (CollUtil.isEmpty(devices)) {
  143 + throw exception(OTA_TASK_CREATE_FAIL_DEVICE_EMPTY);
122 144 }
  145 + return devices;
123 146 }
  147 + throw new IllegalArgumentException("不支持的设备范围:" + createReqVO.getDeviceScope());
124 148 }
125 149  
126 150 private IotOtaTaskDO validateUpgradeTaskExists(Long id) {
127 151 IotOtaTaskDO upgradeTask = otaTaskMapper.selectById(id);
128 152 if (Objects.isNull(upgradeTask)) {
129   - throw exception(OTA_UPGRADE_TASK_NOT_EXISTS);
130   - }
131   - return upgradeTask;
132   - }
133   -
134   - private IotOtaTaskDO initOtaUpgradeTask(IotOtaTaskCreateReqVO createReqVO, Long productId) {
135   - IotOtaTaskDO upgradeTask = BeanUtils.toBean(createReqVO, IotOtaTaskDO.class);
136   - upgradeTask.setDeviceTotalCount(Convert.toLong(CollUtil.size(createReqVO.getDeviceIds())))
137   - .setStatus(IotOtaTaskStatusEnum.IN_PROGRESS.getStatus());
138   -
139   - if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
140   - List<IotDeviceDO> deviceList = deviceService.getDeviceListByProductId(productId);
141   - upgradeTask.setDeviceTotalCount((long) deviceList.size());
142   -// upgradeTask.setDeviceIds(
143   -// deviceList.stream().map(IotDeviceDO::getId).collect(Collectors.toList()));
  153 + throw exception(OTA_TASK_NOT_EXISTS);
144 154 }
145 155 return upgradeTask;
146 156 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleServiceImpl.java
... ... @@ -121,7 +121,7 @@ public class IotDataRuleServiceImpl implements IotDataRuleService {
121 121 convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getProductId));
122 122  
123 123 // 2. 校验设备
124   - deviceService.validateDevicesExist(convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getDeviceId,
  124 + deviceService.validateDeviceListExists(convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getDeviceId,
125 125 config -> ObjUtil.notEqual(config.getDeviceId(), IotDeviceDO.DEVICE_ID_ALL)));
126 126  
127 127 // 3. 校验物模型存在
... ...