Commit 0fb6f2b590c65ddc7159236b8f131fd2020966b8

Authored by YunaiV
1 parent af1f993a

feat:【IoT 物联网】实现 OTA updateOtaRecordProgress

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java
... ... @@ -67,6 +67,12 @@ public interface IotOtaTaskRecordMapper extends BaseMapperX<IotOtaTaskRecordDO>
67 67 .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses));
68 68 }
69 69  
  70 + default List<IotOtaTaskRecordDO> selectListByDeviceIdAndStatus(Long deviceId, Set<Integer> statuses) {
  71 + return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
  72 + .eqIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceId)
  73 + .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses));
  74 + }
  75 +
70 76 default List<IotOtaTaskRecordDO> selectListByStatus(Integer status) {
71 77 return selectList(IotOtaTaskRecordDO::getStatus, status);
72 78 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
... ... @@ -61,6 +61,7 @@ public interface ErrorCodeConstants {
61 61  
62 62 ErrorCode OTA_TASK_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在");
63 63 ErrorCode OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR = new ErrorCode(1_050_008_201, "取消 OTA 升级记录失败,原因:记录状态不是进行中");
  64 + ErrorCode OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS = new ErrorCode(1_050_008_202, "更新 OTA 升级记录进度失败,原因:该设备没有进行中的升级记录");
64 65  
65 66 // ========== IoT 数据流转规则 1-050-010-000 ==========
66 67 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
1 1 package cn.iocoder.yudao.module.iot.enums.ota;
2 2  
3 3  
  4 +import cn.hutool.core.util.ArrayUtil;
4 5 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
5 6 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
6 7 import lombok.Getter;
... ... @@ -49,4 +50,8 @@ public enum IotOtaTaskRecordStatusEnum implements ArrayValuable&lt;Integer&gt; {
49 50 return ARRAYS;
50 51 }
51 52  
  53 + public static IotOtaTaskRecordStatusEnum of(Integer status) {
  54 + return ArrayUtil.firstMatch(o -> o.getStatus().equals(status), values());
  55 + }
  56 +
52 57 }
53 58 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
... ... @@ -263,4 +263,12 @@ public interface IotDeviceService {
263 263 return convertMap(getDeviceList(ids), IotDeviceDO::getId);
264 264 }
265 265  
  266 + /**
  267 + * 更新设备固件版本
  268 + *
  269 + * @param deviceId 设备编号
  270 + * @param firmwareId 固件编号
  271 + */
  272 + void updateDeviceFirmware(Long deviceId, Long firmwareId);
  273 +
266 274 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
... ... @@ -485,6 +485,19 @@ public class IotDeviceServiceImpl implements IotDeviceService {
485 485 return deviceMapper.selectByIds(ids);
486 486 }
487 487  
  488 + @Override
  489 + public void updateDeviceFirmware(Long deviceId, Long firmwareId) {
  490 + // 1. 校验设备是否存在
  491 + IotDeviceDO device = validateDeviceExists(deviceId);
  492 +
  493 + // 2. 更新设备固件版本
  494 + IotDeviceDO updateObj = new IotDeviceDO().setId(deviceId).setFirmwareId(firmwareId);
  495 + deviceMapper.updateById(updateObj);
  496 +
  497 + // 3. 清空对应缓存
  498 + deleteDeviceCache(device);
  499 + }
  500 +
488 501 private IotDeviceServiceImpl getSelf() {
489 502 return SpringUtil.getBean(getClass());
490 503 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
... ... @@ -22,11 +22,13 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
22 22 import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper;
23 23 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
24 24 import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
  25 +import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService;
25 26 import com.baomidou.mybatisplus.core.metadata.IPage;
26 27 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
27 28 import com.google.common.base.Objects;
28 29 import jakarta.annotation.Resource;
29 30 import lombok.extern.slf4j.Slf4j;
  31 +import org.springframework.context.annotation.Lazy;
30 32 import org.springframework.scheduling.annotation.Async;
31 33 import org.springframework.stereotype.Service;
32 34 import org.springframework.validation.annotation.Validated;
... ... @@ -54,6 +56,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
54 56 private IotDeviceService deviceService;
55 57 @Resource
56 58 private IotDevicePropertyService devicePropertyService;
  59 + @Resource
  60 + @Lazy // 延迟加载,避免循环依赖
  61 + private IotOtaTaskRecordService otaTaskRecordService;
57 62  
58 63 @Resource
59 64 private IotDeviceMessageMapper deviceMessageMapper;
... ... @@ -192,6 +197,12 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
192 197 return null;
193 198 }
194 199  
  200 + // OTA 上报升级进度
  201 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.OTA_PROGRESS.getMethod())) {
  202 + otaTaskRecordService.updateOtaRecordProgress(device, message);
  203 + return null;
  204 + }
  205 +
195 206 // TODO @芋艿:这里可以按需,添加别的逻辑;
196 207 return null;
197 208 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java
... ... @@ -7,12 +7,12 @@ 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 jakarta.validation.Valid;
9 9  
10   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
11   -
12 10 import java.util.Collection;
13 11 import java.util.List;
14 12 import java.util.Map;
15 13  
  14 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  15 +
16 16 /**
17 17 * OTA 固件管理 Service 接口
18 18 *
... ... @@ -44,6 +44,15 @@ public interface IotOtaFirmwareService {
44 44 IotOtaFirmwareDO getOtaFirmware(Long id);
45 45  
46 46 /**
  47 + * 根据产品、版本号,获取 OTA 固件信息
  48 + *
  49 + * @param productId 产品编号
  50 + * @param version 版本号
  51 + * @return OTA 固件信息
  52 + */
  53 + IotOtaFirmwareDO getOtaFirmwareByProductIdAndVersion(Long productId, String version);
  54 +
  55 + /**
47 56 * 获取 OTA 固件信息列表
48 57 *
49 58 * @param ids 固件编号集合
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java
... ... @@ -21,10 +21,8 @@ import java.io.ByteArrayInputStream;
21 21 import java.util.Collection;
22 22 import java.util.Collections;
23 23 import java.util.List;
24   -import java.util.Map;
25 24  
26 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
28 26 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS;
29 27 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE;
30 28  
... ... @@ -82,6 +80,11 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
82 80 }
83 81  
84 82 @Override
  83 + public IotOtaFirmwareDO getOtaFirmwareByProductIdAndVersion(Long productId, String version) {
  84 + return otaFirmwareMapper.selectByProductIdAndVersion(productId, version);
  85 + }
  86 +
  87 + @Override
85 88 public List<IotOtaFirmwareDO> getOtaFirmwareList(Collection<Long> ids) {
86 89 if (ids == null || ids.isEmpty()) {
87 90 return Collections.emptyList();
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java
... ... @@ -2,6 +2,7 @@ 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.core.mq.message.IotDeviceMessage;
5 6 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
6 7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
7 8 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
... ... @@ -91,4 +92,12 @@ public interface IotOtaTaskRecordService {
91 92 */
92 93 boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device);
93 94  
  95 + /**
  96 + * 更新 OTA 升级记录进度
  97 + *
  98 + * @param device 设备信息
  99 + * @param message 设备消息
  100 + */
  101 + void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message);
  102 +
94 103 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.ota;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.convert.Convert;
5 5 import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.map.MapUtil;
6 7 import cn.hutool.core.util.StrUtil;
7 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 9 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
... ... @@ -17,14 +18,14 @@ import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageServic
17 18 import jakarta.annotation.Resource;
18 19 import lombok.extern.slf4j.Slf4j;
19 20 import org.springframework.stereotype.Service;
  21 +import org.springframework.transaction.annotation.Transactional;
20 22 import org.springframework.validation.annotation.Validated;
21 23  
22 24 import java.util.*;
23 25  
24 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
26   -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_TASK_RECORD_NOT_EXISTS;
27   -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR;
  28 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
28 29  
29 30 /**
30 31 * OTA 升级任务记录 Service 实现类
... ... @@ -38,6 +39,8 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService {
38 39 private IotOtaTaskRecordMapper otaTaskRecordMapper;
39 40  
40 41 @Resource
  42 + private IotOtaFirmwareService otaFirmwareService;
  43 + @Resource
41 44 private IotOtaTaskService otaTaskService;
42 45 @Resource
43 46 private IotDeviceMessageService deviceMessageService;
... ... @@ -158,6 +161,57 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService {
158 161 return upgradeRecord;
159 162 }
160 163  
  164 + @Override
  165 + @Transactional(rollbackFor = Exception.class)
  166 + @SuppressWarnings("unchecked")
  167 + public void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message) {
  168 + // 1.1 参数解析
  169 + Map<String, Object> params = (Map<String, Object>) message.getParams();
  170 + String version = MapUtil.getStr(params, "version");
  171 + Assert.notBlank(version, "version 不能为空");
  172 + Integer status = MapUtil.getInt(params, "status");
  173 + Assert.notNull(status, "status 不能为空");
  174 + Assert.notNull(IotOtaTaskRecordStatusEnum.of(status), "status 状态不正确");
  175 + String description = MapUtil.getStr(params, "description");
  176 + Integer progress = MapUtil.getInt(params, "progress");
  177 + Assert.notNull(progress, "progress 不能为空");
  178 + Assert.isTrue(progress >= 0 && progress <= 100, "progress 必须在 0-100 之间");
  179 + // 1.2 查询 OTA 升级记录
  180 + List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByDeviceIdAndStatus(
  181 + device.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
  182 + if (CollUtil.isEmpty(records)) {
  183 + throw exception(OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS);
  184 + }
  185 + if (records.size() > 1) {
  186 + log.warn("[updateOtaRecordProgress][message({}) 对应升级记录过多({})]", message, records);
  187 + }
  188 + IotOtaTaskRecordDO record = CollUtil.getFirst(records);
  189 + // 1.3 查询 OTA 固件
  190 + IotOtaFirmwareDO firmware = otaFirmwareService.getOtaFirmwareByProductIdAndVersion(
  191 + device.getProductId(), version);
  192 + if (firmware == null) {
  193 + throw exception(OTA_FIRMWARE_NOT_EXISTS);
  194 + }
  195 +
  196 + // 2. 更新 OTA 升级记录状态
  197 + int updateCount = otaTaskRecordMapper.updateByIdAndStatus(
  198 + record.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES,
  199 + IotOtaTaskRecordDO.builder().status(status).description(description).progress(progress).build());
  200 + if (updateCount == 0) {
  201 + throw exception(OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS);
  202 + }
  203 +
  204 + // 3. 如果升级成功,则更新设备固件版本
  205 + if (IotOtaTaskRecordStatusEnum.SUCCESS.getStatus().equals(status)) {
  206 + deviceService.updateDeviceFirmware(device.getId(), firmware.getId());
  207 + }
  208 +
  209 + // 4. 如果状态是“已结束”(非进行中),则更新任务状态
  210 + if (!IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES.contains(status)) {
  211 + checkAndUpdateOtaTaskStatus(record.getTaskId());
  212 + }
  213 + }
  214 +
161 215 /**
162 216 * 检查并更新任务状态
163 217 * 如果任务下没有进行中的记录,则将任务状态更新为已结束
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
... ... @@ -30,10 +30,12 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; {
30 30 PROPERTY_SET("thing.property.set", "属性设置", false),
31 31  
32 32 // ========== 设备事件 ==========
  33 + // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services
33 34  
34 35 EVENT_POST("thing.event.post", "事件上报", true),
35 36  
36 37 // ========== 设备服务调用 ==========
  38 + // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services
37 39  
38 40 SERVICE_INVOKE("thing.service.invoke", "服务调用", false),
39 41  
... ... @@ -43,9 +45,10 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; {
43 45 CONFIG_PUSH("thing.config.push", "配置推送", true),
44 46  
45 47 // ========== OTA 固件 ==========
  48 + // 可参考:https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates
46 49  
47   - OTA_UPGRADE("thing.ota.upgrade", "OTA 推送固定信息", false),
48   - OTA_PROGRESS("thing.ota.progress", "OTA 上报升级进度", true),
  50 + OTA_UPGRADE("thing.ota.upgrade", "OTA 固定信息推送", false),
  51 + OTA_PROGRESS("thing.ota.progress", "OTA 升级进度上报", true),
49 52 ;
50 53  
51 54 public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod)
... ... @@ -54,7 +57,10 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; {
54 57 /**
55 58 * 不进行 reply 回复的方法集合
56 59 */
57   - public static final Set<String> REPLY_DISABLED = Set.of(STATE_UPDATE.getMethod());
  60 + public static final Set<String> REPLY_DISABLED = Set.of(
  61 + STATE_UPDATE.getMethod(),
  62 + OTA_PROGRESS.getMethod() // 参考阿里云,OTA 升级进度上报,不进行回复
  63 + );
58 64  
59 65 private final String method;
60 66  
... ...