Commit 66b42367cb7c0178e756398144ec4254ed86fbc1

Authored by YunaiV
1 parent 4ea6e08f

reactor:【IoT 物联网】重新梳理下行消息的逻辑(未测试,用于相互 review 作用)

Showing 46 changed files with 622 additions and 1424 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java
... ... @@ -60,4 +60,8 @@ public class ObjectUtils {
60 60 return Arrays.asList(array).contains(obj);
61 61 }
62 62  
  63 + public static boolean isNotAllEmpty(Object... objs) {
  64 + return !ObjectUtil.isAllEmpty(objs);
  65 + }
  66 +
63 67 }
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
2   -
3   -import lombok.Data;
4   -
5   -/**
6   - * IoT 设备【注册】自己 Request DTO
7   - *
8   - * @author 芋道源码
9   - */
10   -@Data
11   -public class IotDeviceRegisterReqDTO extends IotDeviceUpstreamAbstractReqDTO {
12   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
2   -
3   -import jakarta.validation.constraints.NotEmpty;
4   -import lombok.Data;
5   -
6   -import java.util.List;
7   -
8   -/**
9   - * IoT 设备【注册】子设备 Request DTO
10   - *
11   - * @author 芋道源码
12   - */
13   -@Data
14   -public class IotDeviceRegisterSubReqDTO extends IotDeviceUpstreamAbstractReqDTO {
15   -
16   - // TODO @芋艿:看看要不要优化命名
17   - /**
18   - * 子设备数组
19   - */
20   - @NotEmpty(message = "子设备不能为空")
21   - private List<Device> params;
22   -
23   - /**
24   - * 设备信息
25   - */
26   - @Data
27   - public static class Device {
28   -
29   - /**
30   - * 产品标识
31   - */
32   - @NotEmpty(message = "产品标识不能为空")
33   - private String productKey;
34   -
35   - /**
36   - * 设备名称
37   - */
38   - @NotEmpty(message = "设备名称不能为空")
39   - private String deviceName;
40   -
41   - }
42   -
43   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
2   -
3   -import jakarta.validation.constraints.NotEmpty;
4   -import lombok.Data;
5   -
6   -import java.util.List;
7   -
8   -// TODO @芋艿:要写清楚,是来自设备网关,还是设备。
9   -/**
10   - * IoT 设备【拓扑】添加 Request DTO
11   - */
12   -@Data
13   -public class IotDeviceTopologyAddReqDTO extends IotDeviceUpstreamAbstractReqDTO {
14   -
15   - // TODO @芋艿:看看要不要优化命名
16   - /**
17   - * 子设备数组
18   - */
19   - @NotEmpty(message = "子设备不能为空")
20   - private List<IotDeviceRegisterSubReqDTO.Device> params;
21   -
22   - /**
23   - * 设备信息
24   - */
25   - @Data
26   - public static class Device {
27   -
28   - /**
29   - * 产品标识
30   - */
31   - @NotEmpty(message = "产品标识不能为空")
32   - private String productKey;
33   -
34   - /**
35   - * 设备名称
36   - */
37   - @NotEmpty(message = "设备名称不能为空")
38   - private String deviceName;
39   -
40   - // TODO @芋艿:阿里云还有 sign 签名
41   -
42   - }
43   -
44   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
... ... @@ -6,13 +6,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
10   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
11 9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
  10 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageSendReqVO;
  11 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
12 12 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
13 13 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
14   -import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService;
15   -import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService;
  14 +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
16 15 import io.swagger.v3.oas.annotations.Operation;
17 16 import io.swagger.v3.oas.annotations.Parameter;
18 17 import io.swagger.v3.oas.annotations.tags.Tag;
... ... @@ -42,9 +41,7 @@ public class IotDeviceController {
42 41 @Resource
43 42 private IotDeviceService deviceService;
44 43 @Resource
45   - private IotDeviceUpstreamService deviceUpstreamService;
46   - @Resource
47   - private IotDeviceDownstreamService deviceDownstreamService;
  44 + private IotDeviceMessageService deviceMessageService;
48 45  
49 46 @PostMapping("/create")
50 47 @Operation(summary = "创建设备")
... ... @@ -161,19 +158,12 @@ public class IotDeviceController {
161 158 ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list);
162 159 }
163 160  
164   - @PostMapping("/upstream")
165   - @Operation(summary = "设备上行", description = "可用于设备模拟")
  161 + // TODO @芋艿:需要重构
  162 + @PostMapping("/send-message")
  163 + @Operation(summary = "发送消息", description = "可用于设备模拟")
166 164 @PreAuthorize("@ss.hasPermission('iot:device:upstream')")
167   - public CommonResult<Boolean> upstreamDevice(@Valid @RequestBody IotDeviceUpstreamReqVO upstreamReqVO) {
168   - deviceUpstreamService.upstreamDevice(upstreamReqVO);
169   - return success(true);
170   - }
171   -
172   - @PostMapping("/downstream")
173   - @Operation(summary = "设备下行", description = "可用于设备模拟")
174   - @PreAuthorize("@ss.hasPermission('iot:device:downstream')")
175   - public CommonResult<Boolean> downstreamDevice(@Valid @RequestBody IotDeviceDownstreamReqVO downstreamReqVO) {
176   - deviceDownstreamService.downstreamDevice(downstreamReqVO);
  165 + public CommonResult<Boolean> upstreamDevice(@Valid @RequestBody IotDeviceMessageSendReqVO sendReqVO) {
  166 + deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class));
177 167 return success(true);
178 168 }
179 169  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java
... ... @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO;
7 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogRespVO;
8   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
9   -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
  9 +import cn.iocoder.yudao.module.iot.service.device.property.IotDeviceLogService;
10 10 import io.swagger.v3.oas.annotations.Operation;
11 11 import io.swagger.v3.oas.annotations.tags.Tag;
12 12 import jakarta.annotation.Resource;
... ... @@ -32,7 +32,7 @@ public class IotDeviceLogController {
32 32 @Operation(summary = "获得设备日志分页")
33 33 @PreAuthorize("@ss.hasPermission('iot:device:log-query')")
34 34 public CommonResult<PageResult<IotDeviceLogRespVO>> getDeviceLogPage(@Valid IotDeviceLogPageReqVO pageReqVO) {
35   - PageResult<IotDeviceLogDO> pageResult = deviceLogService.getDeviceLogPage(pageReqVO);
  35 + PageResult<IotDeviceMessageDO> pageResult = deviceLogService.getDeviceLogPage(pageReqVO);
36 36 return success(BeanUtils.toBean(pageResult, IotDeviceLogRespVO.class));
37 37 }
38 38  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java
... ... @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
12 12 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
13 13 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
14 14 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
15   -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
  15 +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
16 16 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
17 17 import io.swagger.v3.oas.annotations.Operation;
18 18 import io.swagger.v3.oas.annotations.Parameter;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java deleted
1   -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.control;
2   -
3   -import cn.iocoder.yudao.framework.common.validation.InEnum;
4   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
5   -import io.swagger.v3.oas.annotations.media.Schema;
6   -import jakarta.validation.constraints.NotEmpty;
7   -import jakarta.validation.constraints.NotNull;
8   -import lombok.Data;
9   -
10   -@Schema(description = "管理后台 - IoT 设备下行 Request VO") // 服务调用、属性设置、属性获取等
11   -@Data
12   -public class IotDeviceDownstreamReqVO {
13   -
14   - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
15   - @NotNull(message = "设备编号不能为空")
16   - private Long id;
17   -
18   - @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property")
19   - @NotEmpty(message = "消息类型不能为空")
20   - @InEnum(IotDeviceMessageTypeEnum.class)
21   - private String type;
22   -
23   - @Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "report")
24   - @NotEmpty(message = "标识符不能为空")
25   - private String identifier; // 参见 IotDeviceMessageIdentifierEnum 枚举类
26   -
27   - @Schema(description = "请求参数", requiredMode = Schema.RequiredMode.REQUIRED)
28   - private Object data; // 例如说:服务调用的 params、属性设置的 properties
29   -
30   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java deleted
1   -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.control;
2   -
3   -import cn.iocoder.yudao.framework.common.validation.InEnum;
4   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
5   -import io.swagger.v3.oas.annotations.media.Schema;
6   -import jakarta.validation.constraints.NotEmpty;
7   -import jakarta.validation.constraints.NotNull;
8   -import lombok.Data;
9   -
10   -@Schema(description = "管理后台 - IoT 设备上行 Request VO") // 属性上报、事件上报、状态变更等
11   -@Data
12   -public class IotDeviceUpstreamReqVO {
13   -
14   - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
15   - @NotNull(message = "设备编号不能为空")
16   - private Long id;
17   -
18   - @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property")
19   - @NotEmpty(message = "消息类型不能为空")
20   - @InEnum(IotDeviceMessageTypeEnum.class)
21   - private String type;
22   -
23   - @Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "report")
24   - @NotEmpty(message = "标识符不能为空")
25   - private String identifier; // 参见 IotDeviceMessageIdentifierEnum 枚举类
26   -
27   - @Schema(description = "请求参数", requiredMode = Schema.RequiredMode.REQUIRED)
28   - private Object data; // 例如说:属性上报的 properties、事件上报的 params
29   -
30   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotEmpty;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +
  10 +@Schema(description = "管理后台 - IoT 设备消息发送 Request VO") // 属性上报、事件上报、状态变更等
  11 +@Data
  12 +public class IotDeviceMessageSendReqVO {
  13 +
  14 + @Schema(description = "请求方法", requiredMode = Schema.RequiredMode.REQUIRED, example = "report")
  15 + @NotEmpty(message = "请求方法不能为空")
  16 + @InEnum(IotDeviceMessageMethodEnum.class)
  17 + private String method;
  18 +
  19 + @Schema(description = "请求参数")
  20 + private Object params; // 例如说:属性上报的 properties、事件上报的 params
  21 +
  22 + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
  23 + @NotNull(message = "设备编号不能为空")
  24 + private Long deviceId;
  25 +
  26 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java
1 1 package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record;
2 2  
3 3 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
4 5 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
5 6 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;
6 7 import com.fhs.core.trans.anno.Trans;
... ... @@ -89,7 +90,7 @@ public class IotOtaUpgradeRecordRespVO {
89 90 * 升级进度描述
90 91 * <p>
91 92 * 注意,只记录设备最后一次的升级进度描述
92   - * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志
  93 + * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志
93 94 */
94 95 @Schema(description = "升级进度描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
95 96 private String description;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java
... ... @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsR
6 6 import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO;
7 7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
8 8 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
9   -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
  9 +import cn.iocoder.yudao.module.iot.service.device.property.IotDeviceLogService;
10 10 import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;
11 11 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
12 12 import io.swagger.v3.oas.annotations.Operation;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java
1 1 package cn.iocoder.yudao.module.iot.dal.dataobject.device;
2 2  
3   -import cn.hutool.core.util.IdUtil;
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
4 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5   -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
6   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
7   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
8 5 import lombok.AllArgsConstructor;
9 6 import lombok.Builder;
10 7 import lombok.Data;
11 8 import lombok.NoArgsConstructor;
12 9  
13 10 /**
14   - * IoT 设备日志数据 DO
  11 + * IoT 设备消息数据 DO
15 12 *
16 13 * 目前使用 TDengine 存储
17 14 *
... ... @@ -21,80 +18,84 @@ import lombok.NoArgsConstructor;
21 18 @Builder
22 19 @NoArgsConstructor
23 20 @AllArgsConstructor
24   -public class IotDeviceLogDO {
25   -
26   - /**
27   - * 日志编号
28   - *
29   - * 通过 {@link IdUtil#fastSimpleUUID()} 生成
30   - */
31   - private String id;
  21 +public class IotDeviceMessageDO {
32 22  
33 23 /**
34 24 * 消息编号
35   - *
36   - * 对应 {@link IotDeviceMessage#getMessageId()} 字段
37 25 */
38   - private String messageId;
39   -
  26 + private String id;
40 27 /**
41   - * 产品标识
42   - * <p>
43   - * 关联 {@link IotProductDO#getProductKey()}
  28 + * 上报时间戳
44 29 */
45   - private String productKey;
  30 + private Long reportTime;
46 31 /**
47   - * 设备名称
48   - *
49   - * 关联 {@link IotDeviceDO#getDeviceName()}
  32 + * 存储时序时间
50 33 */
51   - private String deviceName;
  34 + private Long ts;
  35 +
52 36 /**
53 37 * 设备编号
54 38 *
55 39 * 关联 {@link IotDeviceDO#getId()}
56 40 */
57 41 private Long deviceId;
  42 + /**
  43 + * 租户编号
  44 + */
  45 + private Long tenantId;
58 46  
59 47 /**
60   - * 日志类型
  48 + * 服务编号,该消息由哪个 server 发送
  49 + */
  50 + private String serverId;
  51 +
  52 + /**
  53 + * 是否上行消息
61 54 *
62   - * 枚举 {@link IotDeviceMessageTypeEnum}
  55 + * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isUpstreamMessage(IotDeviceMessage)} 计算。
  56 + * 计算并存储的目的:方便计算多少条上行、多少条下行
63 57 */
64   - private String type;
  58 + private Boolean upstream;
65 59 /**
66   - * 标识符
  60 + * 是否回复消息
67 61 *
68   - * 枚举 {@link IotDeviceMessageIdentifierEnum}
  62 + * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isReplyMessage(IotDeviceMessage)} 计算。
  63 + * 计算并存储的目的:方便计算多少条请求、多少条回复
69 64 */
70   - private String identifier;
  65 + private Boolean reply;
  66 +
  67 + // ========== codec(编解码)字段 ==========
71 68  
72 69 /**
73   - * 数据内容
  70 + * 请求编号
74 71 *
75   - * 存储具体的消息数据内容,通常是 JSON 格式
  72 + * 由设备生成,对应阿里云 IoT 的 Alink 协议中的 id、华为云 IoTDA 协议的 request_id
76 73 */
77   - private String content;
  74 + private String requestId;
78 75 /**
79   - * 响应码
  76 + * 请求方法
80 77 *
81   - * 目前只有 server 下行消息给 device 设备时,才会有响应码
  78 + * 枚举 {@link IotDeviceMessageMethodEnum}
  79 + * 例如说:thing.property.report 属性上报
82 80 */
83   - private Integer code;
84   -
  81 + private String method;
85 82 /**
86   - * 上报时间戳
  83 + * 请求参数
  84 + *
  85 + * 例如说:属性上报的 properties、事件上报的 params
87 86 */
88   - private Long reportTime;
89   -
  87 + private Object params;
90 88 /**
91   - * 租户编号
  89 + * 响应结果
92 90 */
93   - private Long tenantId;
94   -
  91 + private Object data;
95 92 /**
96   - * 时序时间
  93 + * 响应错误码
97 94 */
98   - private Long ts;
  95 + private Integer code;
  96 + /**
  97 + * 响应提示
  98 + */
  99 + private String msg;
99 100  
100 101 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.ota;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
5 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 7 import com.baomidou.mybatisplus.annotation.TableId;
7 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -77,7 +78,7 @@ public class IotOtaUpgradeRecordDO extends BaseDO {
77 78 * 升级进度描述
78 79 *
79 80 * 注意,只记录设备最后一次的升级进度描述
80   - * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志
  81 + * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志
81 82 */
82 83 private String description;
83 84 /**
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java
... ... @@ -43,17 +43,6 @@ public class DeviceServerIdRedisDAO {
43 43 }
44 44  
45 45 /**
46   - * 删除设备关联的网关 serverId
47   - *
48   - * @param productKey 产品标识
49   - * @param deviceName 设备名称
50   - */
51   - public void delete(String productKey, String deviceName) {
52   - String hashKey = buildHashKey(productKey, deviceName);
53   - stringRedisTemplate.opsForHash().delete(RedisKeyConstants.DEVICE_SERVER_ID, hashKey);
54   - }
55   -
56   - /**
57 46 * 构建 HASH KEY
58 47 *
59 48 * @param productKey 产品标识
... ... @@ -64,4 +53,4 @@ public class DeviceServerIdRedisDAO {
64 53 return productKey + StrUtil.COMMA + deviceName;
65 54 }
66 55  
67   -}
68 56 \ No newline at end of file
  57 +}
69 58 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java
1 1 package cn.iocoder.yudao.module.iot.dal.tdengine;
2 2  
3 3 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO;
4   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
5 5 import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;
6 6 import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
7 7 import com.baomidou.mybatisplus.core.metadata.IPage;
... ... @@ -12,48 +12,48 @@ import java.util.List;
12 12 import java.util.Map;
13 13  
14 14 /**
15   - * 设备日志 {@link IotDeviceLogDO} Mapper 接口
  15 + * 设备消息 {@link IotDeviceMessageDO} Mapper 接口
16 16 */
17 17 @Mapper
18 18 @TDengineDS
19 19 @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错
20   -public interface IotDeviceLogMapper {
  20 +public interface IotDeviceMessageMapper {
21 21  
22 22 /**
23   - * 创建设备日志超级表
  23 + * 创建设备消息超级表
24 24 */
25   - void createDeviceLogSTable();
  25 + void createSTable();
26 26  
27 27 /**
28   - * 查询设备日志表是否存在
  28 + * 查询设备消息表是否存在
29 29 *
30 30 * @return 存在则返回表名;不存在则返回 null
31 31 */
32   - String showDeviceLogSTable();
  32 + String showSTable();
33 33  
34 34 /**
35   - * 插入设备日志数据
  35 + * 插入设备消息数据
36 36 *
37 37 * 如果子表不存在,会自动创建子表
38 38 *
39   - * @param log 设备日志数据
  39 + * @param message 设备消息数据
40 40 */
41   - void insert(IotDeviceLogDO log);
  41 + void insert(IotDeviceMessageDO message);
42 42  
43 43 /**
44   - * 获得设备日志分页
  44 + * 获得设备消息分页
45 45 *
46 46 * @param reqVO 分页查询条件
47   - * @return 设备日志列表
  47 + * @return 设备消息列表
48 48 */
49   - IPage<IotDeviceLogDO> selectPage(IPage<IotDeviceLogDO> page,
50   - @Param("reqVO") IotDeviceLogPageReqVO reqVO);
  49 + IPage<IotDeviceMessageDO> selectPage(IPage<IotDeviceMessageDO> page,
  50 + @Param("reqVO") IotDeviceLogPageReqVO reqVO);
51 51  
52 52 /**
53   - * 统计设备日志数量
  53 + * 统计设备消息数量
54 54 *
55   - * @param createTime 创建时间,如果为空,则统计所有日志数量
56   - * @return 日志数量
  55 + * @param createTime 创建时间,如果为空,则统计所有消息数量
  56 + * @return 消息数量
57 57 */
58 58 Long selectCountByCreateTime(@Param("createTime") Long createTime);
59 59  
... ... @@ -62,14 +62,14 @@ public interface IotDeviceLogMapper {
62 62 /**
63 63 * 查询每个小时设备上行消息数量
64 64 */
65   - List<Map<String, Object>> selectDeviceLogUpCountByHour(@Param("deviceKey") String deviceKey,
  65 + List<Map<String, Object>> selectDeviceLogUpCountByHour(@Param("deviceId") Long deviceId,
66 66 @Param("startTime") Long startTime,
67 67 @Param("endTime") Long endTime);
68 68  
69 69 /**
70 70 * 查询每个小时设备下行消息数量
71 71 */
72   - List<Map<String, Object>> selectDeviceLogDownCountByHour(@Param("deviceKey") String deviceKey,
  72 + List<Map<String, Object>> selectDeviceLogDownCountByHour(@Param("deviceId") Long deviceId,
73 73 @Param("startTime") Long startTime,
74 74 @Param("endTime") Long endTime);
75 75  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java
1 1 package cn.iocoder.yudao.module.iot.framework.tdengine.config;
2 2  
3   -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
  3 +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
4 4 import lombok.RequiredArgsConstructor;
5 5 import lombok.extern.slf4j.Slf4j;
6 6 import org.springframework.boot.ApplicationArguments;
... ... @@ -17,16 +17,16 @@ import org.springframework.stereotype.Component;
17 17 @Slf4j
18 18 public class TDengineTableInitRunner implements ApplicationRunner {
19 19  
20   - private final IotDeviceLogService deviceLogService;
  20 + private final IotDeviceMessageService deviceMessageService;
21 21  
22 22 @Override
23 23 public void run(ApplicationArguments args) {
24 24 try {
25   - // 初始化设备日志表
26   - deviceLogService.defineDeviceLog();
  25 + // 初始化设备消息表
  26 + deviceMessageService.defineDeviceMessageStable();
27 27 } catch (Exception ex) {
28   - // 初始化失败时打印错误日志并退出系统
29   - log.error("[run][TDengine初始化设备日志表结构失败,系统无法正常运行,即将退出]", ex);
  28 + // 初始化失败时打印错误消息并退出系统
  29 + log.error("[run][TDengine初始化设备消息表结构失败,系统无法正常运行,即将退出]", ex);
30 30 System.exit(1);
31 31 }
32 32 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java
... ... @@ -7,10 +7,10 @@ import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
7 7 import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
8 8 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
9 9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
10   -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
11 10 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
12 11 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
13   -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
  12 +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
  13 +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
14 14 import jakarta.annotation.Resource;
15 15 import org.springframework.stereotype.Component;
16 16  
... ... @@ -43,10 +43,10 @@ public class IotDeviceOfflineCheckJob implements JobHandler {
43 43 private IotDeviceService deviceService;
44 44 @Resource
45 45 private IotDevicePropertyService devicePropertyService;
46   -
47 46 @Resource
48   - private IotDeviceMessageProducer deviceMessageProducer;
  47 + private IotDeviceMessageService deviceMessageService;
49 48  
  49 + // TODO @芋艿:需要重构下;
50 50 @Override
51 51 @TenantJob
52 52 public String execute(String param) {
... ... @@ -69,8 +69,7 @@ public class IotDeviceOfflineCheckJob implements JobHandler {
69 69 }
70 70 offlineDeviceKeys.add(new String[]{device.getProductKey(), device.getDeviceName()});
71 71 // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志等等
72   - deviceMessageProducer.sendDeviceMessage(IotDeviceMessage.of(device.getProductKey(), device.getDeviceName())
73   - .ofStateOffline());
  72 + deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateOffline().setDeviceId(device.getId()));
74 73 }
75 74 return JsonUtils.toJsonString(offlineDeviceKeys);
76 75 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageSubscriber.java deleted
1   -package cn.iocoder.yudao.module.iot.mq.consumer.device;
2   -
3   -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4   -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
5   -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6   -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
7   -import jakarta.annotation.PostConstruct;
8   -import jakarta.annotation.Resource;
9   -import lombok.extern.slf4j.Slf4j;
10   -import org.springframework.stereotype.Component;
11   -
12   -/**
13   - * 针对 {@link IotDeviceMessage} 的消费者:记录设备日志
14   - *
15   - * @author 芋道源码
16   - */
17   -@Component
18   -@Slf4j
19   -public class IotDeviceLogMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
20   -
21   - @Resource
22   - private IotMessageBus messageBus;
23   -
24   - @Resource
25   - private IotDeviceLogService deviceLogService;
26   -
27   - @PostConstruct
28   - public void init() {
29   - messageBus.register(this);
30   - }
31   -
32   - @Override
33   - public String getTopic() {
34   - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;
35   - }
36   -
37   - @Override
38   - public String getGroup() {
39   - return "iot_device_log_consumer";
40   - }
41   -
42   - @Override
43   - public void onMessage(IotDeviceMessage message) {
44   - log.info("[onMessage][消息内容({})]", message);
45   - deviceLogService.createDeviceLog(message);
46   - }
47   -
48   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceStateMessageSubscriber.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java
1 1 package cn.iocoder.yudao.module.iot.mq.consumer.device;
2 2  
3 3 import cn.hutool.core.util.ObjectUtil;
4   -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
5   -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageIdentifierEnum;
6   -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageTypeEnum;
  4 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
7 6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
8 7 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
9 8 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
10 9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
11   -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
12 10 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
13 11 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
14 12 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
15   -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
  13 +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
  14 +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
16 15 import jakarta.annotation.PostConstruct;
17 16 import jakarta.annotation.Resource;
18 17 import lombok.extern.slf4j.Slf4j;
19 18 import org.springframework.stereotype.Component;
20 19  
21 20 import java.time.LocalDateTime;
22   -import java.util.Objects;
23 21  
24 22 /**
25   - * 针对 {@link IotDeviceMessage} 的消费者:记录设备状态
  23 + * 针对 {@link IotDeviceMessage} 的业务处理器:调用 method 对应的逻辑。例如说:
  24 + * 1. {@link IotDeviceMessageMethodEnum#PROPERTY_REPORT} 属性上报时,记录设备属性
26 25 *
27   - * 特殊:如果是离线的设备,将自动上线
28   - *
29   - * @author 芋道源码
  26 + * @author alwayssuper
30 27 */
31 28 @Component
32 29 @Slf4j
33   -public class IotDeviceStateMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
  30 +public class IotDeviceMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
34 31  
35 32 @Resource
36 33 private IotDeviceService deviceService;
37 34 @Resource
38 35 private IotDevicePropertyService devicePropertyService;
  36 + @Resource
  37 + private IotDeviceMessageService deviceMessageService;
39 38  
40 39 @Resource
41 40 private IotMessageBus messageBus;
42   - @Resource
43   - private IotDeviceMessageProducer deviceMessageProducer;
44 41  
45 42 @PostConstruct
46 43 public void init() {
... ... @@ -54,53 +51,49 @@ public class IotDeviceStateMessageSubscriber implements IotMessageSubscriber&lt;Iot
54 51  
55 52 @Override
56 53 public String getGroup() {
57   - return "iot_device_state_consumer";
  54 + return "iot_device_message_consumer";
58 55 }
59 56  
60 57 @Override
61 58 public void onMessage(IotDeviceMessage message) {
62   - // 1.1 只处理上行消息,或者是 STATE 相关的消息
63   - if (!IotDeviceMessageUtils.isUpstreamMessage(message)
64   - && ObjectUtil.notEqual(message.getType(), IotDeviceMessageTypeEnum.STATE.getType())) {
  59 + if (!IotDeviceMessageUtils.isUpstreamMessage(message)) {
  60 + log.error("[onMessage][message({}) 非上行消息,不进行处理]", message);
65 61 return;
66 62 }
67   - // 1.2 校验设备是否存在
68   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
69   - message.getProductKey(), message.getDeviceName());
70   - if (device == null) {
71   - log.error("[onMessage][消息({}) 对应的设备部存在]", message);
72   - return;
73   - }
74   -
75   - // 2. 处理消息
76   - TenantUtils.execute(device.getTenantId(), () -> onMessage(message, device));
77   - }
78 63  
79   - private void onMessage(IotDeviceMessage message, IotDeviceDO device) {
80   - // 更新设备的最后时间
  64 + // 1.1 更新设备的最后时间
  65 + // TODO 芋艿:后续加缓存;
  66 + IotDeviceDO device = deviceService.validateDeviceExists(message.getDeviceId());
81 67 devicePropertyService.updateDeviceReportTime(device.getProductKey(), device.getDeviceName(), LocalDateTime.now());
  68 + // 1.2 更新设备的连接 server
  69 + devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId());
  70 +
  71 + // 2. 未上线的设备,强制上线
  72 + forceDeviceOnline(message, device);
  73 +
  74 + // 3. 核心:处理消息
  75 + deviceMessageService.handleUpstreamDeviceMessage(message, device);
  76 + }
82 77  
83   - // 情况一:STATE 相关的消息
84   - if (Objects.equals(message.getType(), IotDeviceMessageTypeEnum.STATE.getType())) {
85   - if (Objects.equals(message.getIdentifier(), IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier())) {
86   - deviceService.updateDeviceState(device.getId(), IotDeviceStateEnum.ONLINE.getState());
87   - devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId());
88   - } else {
89   - deviceService.updateDeviceState(device.getId(), IotDeviceStateEnum.OFFLINE.getState());
90   - devicePropertyService.deleteDeviceServerId(device.getProductKey(), device.getDeviceName());
91   - }
92   - // TODO 芋艿:子设备的关联
  78 + private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) {
  79 + // 已经在线,无需处理
  80 + if (ObjectUtil.equal(device.getState(), IotDeviceStateEnum.ONLINE.getState())) {
  81 + return;
  82 + }
  83 + // 如果是 STATE 相关的消息,无需处理,不然就重复处理状态了
  84 + if (ObjectUtils.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(),
  85 + IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod())) {
93 86 return;
94 87 }
95 88  
96   - // 情况二:非 STATE 相关的消息
97   - devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId());
98 89 // 特殊:设备非在线时,主动标记设备为在线
99   - // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志等等
100   - if (ObjectUtil.notEqual(device.getState(), IotDeviceStateEnum.ONLINE.getState())) {
101   - deviceMessageProducer.sendDeviceMessage(IotDeviceMessage.of(message.getProductKey(), message.getDeviceName())
102   - .ofStateOnline());
  90 + // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志、规则引擎等等
  91 + try {
  92 + deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateOnline().setDeviceId(device.getId()));
  93 + } catch (Exception e) {
  94 + // 注意:即使执行失败,也不影响主流程
  95 + log.error("[forceDeviceOnline][message({}) device({}) 强制设备上线失败]", message, device, e);
103 96 }
104 97 }
105 98  
106   -}
107 99 \ No newline at end of file
  100 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageSubscriber.java deleted
1   -package cn.iocoder.yudao.module.iot.mq.consumer.device;
2   -
3   -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4   -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
5   -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
7   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
8   -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
9   -import com.google.common.base.Objects;
10   -import jakarta.annotation.PostConstruct;
11   -import jakarta.annotation.Resource;
12   -import lombok.extern.slf4j.Slf4j;
13   -import org.springframework.stereotype.Component;
14   -
15   -/**
16   - * 针对 {@link IotDeviceMessage} 的消费者:记录设备属性
17   - *
18   - * @author alwayssuper
19   - */
20   -@Component
21   -@Slf4j
22   -public class IotDevicePropertyMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
23   -
24   - @Resource
25   - private IotDevicePropertyService deviceDataService;
26   -
27   - @Resource
28   - private IotMessageBus messageBus;
29   -
30   - @PostConstruct
31   - public void init() {
32   - messageBus.register(this);
33   - }
34   -
35   - @Override
36   - public String getTopic() {
37   - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;
38   - }
39   -
40   - @Override
41   - public String getGroup() {
42   - return "iot_device_property_consumer";
43   - }
44   -
45   - @Override
46   - public void onMessage(IotDeviceMessage message) {
47   - if (Objects.equal(message.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())
48   - && Objects.equal(message.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())) {
49   - // 保存设备属性
50   - deviceDataService.saveDeviceProperty(message);
51   - }
52   - }
53   -
54   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
... ... @@ -63,6 +63,14 @@ public interface IotDeviceService {
63 63 /**
64 64 * 更新设备状态
65 65 *
  66 + * @param device 设备信息
  67 + * @param state 状态
  68 + */
  69 + void updateDeviceState(IotDeviceDO device, Integer state);
  70 +
  71 + /**
  72 + * 更新设备状态
  73 + *
66 74 * @param id 编号
67 75 * @param state 状态
68 76 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
... ... @@ -272,12 +272,9 @@ public class IotDeviceServiceImpl implements IotDeviceService {
272 272 }
273 273  
274 274 @Override
275   - public void updateDeviceState(Long id, Integer state) {
276   - // 1. 校验存在
277   - IotDeviceDO device = validateDeviceExists(id);
278   -
279   - // 2. 更新状态和时间
280   - IotDeviceDO updateObj = new IotDeviceDO().setId(id).setState(state);
  275 + public void updateDeviceState(IotDeviceDO device, Integer state) {
  276 + // 1. 更新状态和时间
  277 + IotDeviceDO updateObj = new IotDeviceDO().setId(device.getId()).setState(state);
281 278 if (device.getOnlineTime() == null
282 279 && Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) {
283 280 updateObj.setActiveTime(LocalDateTime.now());
... ... @@ -289,11 +286,19 @@ public class IotDeviceServiceImpl implements IotDeviceService {
289 286 }
290 287 deviceMapper.updateById(updateObj);
291 288  
292   - // 3. 清空对应缓存
  289 + // 2. 清空对应缓存
293 290 deleteDeviceCache(device);
294 291 }
295 292  
296 293 @Override
  294 + public void updateDeviceState(Long id, Integer state) {
  295 + // 校验存在
  296 + IotDeviceDO device = validateDeviceExists(id);
  297 + // 执行更新
  298 + updateDeviceState(device, state);
  299 + }
  300 +
  301 + @Override
297 302 public Long getDeviceCountByProductId(Long productId) {
298 303 return deviceMapper.selectCountByProductId(productId);
299 304 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java deleted
1   -package cn.iocoder.yudao.module.iot.service.device.control;
2   -
3   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
4   -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5   -import jakarta.validation.Valid;
6   -
7   -/**
8   - * IoT 设备下行 Service 接口
9   - *
10   - * 目的:服务端 -> 网关 -> 设备
11   - *
12   - * @author 芋道源码
13   - */
14   -public interface IotDeviceDownstreamService {
15   -
16   - /**
17   - * 设备下行,可用于设备模拟
18   - *
19   - * @param downstreamReqVO 设备下行请求 VO
20   - * @return 下行消息
21   - */
22   - IotDeviceMessage downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO);
23   -
24   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.iot.service.device.control;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.framework.common.exception.ServiceException;
5   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
6   -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7   -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
8   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
9   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
10   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
11   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
12   -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
13   -import jakarta.annotation.Resource;
14   -import lombok.extern.slf4j.Slf4j;
15   -import org.springframework.stereotype.Service;
16   -import org.springframework.validation.annotation.Validated;
17   -
18   -import java.util.Map;
19   -import java.util.Objects;
20   -
21   -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
22   -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
23   -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL;
24   -
25   -/**
26   - * IoT 设备下行 Service 实现类
27   - *
28   - * @author 芋道源码
29   - */
30   -@Service
31   -@Validated
32   -@Slf4j
33   -public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamService {
34   -
35   - @Resource
36   - private IotDeviceService deviceService;
37   - @Resource
38   - private IotDevicePropertyService devicePropertyService;
39   -
40   - @Resource
41   - private IotDeviceMessageProducer deviceMessageProducer;
42   -
43   - @Override
44   - public IotDeviceMessage downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) {
45   - // 1. 校验设备是否存在
46   - IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId());
47   - // TODO 芋艿:父设备的处理
48   - IotDeviceDO parentDevice = null;
49   -
50   - // 2. 构建消息
51   - IotDeviceMessage message = buildDownstreamDeviceMessage(downstreamReqVO, device, parentDevice);
52   -
53   - // 3.1 发送给网关
54   - String serverId = devicePropertyService.getDeviceServerId(message.getProductKey(), message.getDeviceName());
55   - if (StrUtil.isEmpty(serverId)) {
56   - throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL);
57   - }
58   - deviceMessageProducer.sendDeviceMessageToGateway(serverId, message);
59   -
60   - // 3.2 发送给服务器(用于设备日志等的记录)
61   - deviceMessageProducer.sendDeviceMessage(message);
62   - return message;
63   - }
64   -
65   - @SuppressWarnings("unchecked")
66   - private IotDeviceMessage buildDownstreamDeviceMessage(IotDeviceDownstreamReqVO downstreamReqVO,
67   - IotDeviceDO device, IotDeviceDO parentDevice) {
68   - IotDeviceMessage message = IotDeviceMessage.of(getProductKey(device, parentDevice),
69   - getDeviceName(device, parentDevice));
70   - // 服务调用
71   - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) {
72   - // TODO @芋艿:待实现
73   -// return invokeDeviceService(downstreamReqVO, device, parentDevice);
74   - }
75   - // 属性相关
76   - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) {
77   - // 属性设置
78   - if (Objects.equals(downstreamReqVO.getIdentifier(),
79   - IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) {
80   - if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
81   - throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型");
82   - }
83   - return message.ofPropertySet((Map<String, Object>) downstreamReqVO.getData());
84   - }
85   - // 属性获取
86   - if (Objects.equals(downstreamReqVO.getIdentifier(),
87   - IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) {
88   - // TODO @芋艿:待实现
89   -// return getDeviceProperty(downstreamReqVO, device, parentDevice);
90   - }
91   - }
92   - // 配置下发
93   - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType())
94   - && Objects.equals(downstreamReqVO.getIdentifier(),
95   - IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) {
96   - // TODO @芋艿:待实现
97   -// return setDeviceConfig(downstreamReqVO, device, parentDevice);
98   - }
99   - // OTA 升级
100   - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.OTA.getType())) {
101   - // TODO @芋艿:待实现
102   -// return otaUpgrade(downstreamReqVO, device, parentDevice);
103   - }
104   - // TODO @芋艿:取消设备的网关的时,要不要下发 REGISTER_UNREGISTER_SUB ?
105   - throw new IllegalArgumentException("不支持的下行消息类型:" + downstreamReqVO);
106   - }
107   -
108   -// /**
109   -// * 调用设备服务
110   -// *
111   -// * @param downstreamReqVO 下行请求
112   -// * @param device 设备
113   -// * @param parentDevice 父设备
114   -// * @return 下发消息
115   -// */
116   -// @SuppressWarnings("unchecked")
117   -// private IotDeviceMessage invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO,
118   -// IotDeviceDO device, IotDeviceDO parentDevice) {
119   -// // 1. 参数校验
120   -// if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
121   -// throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型");
122   -// }
123   -// // TODO @super:【可优化】过滤掉不合法的服务
124   -//
125   -// // 2. 发送请求
126   -// String url = String.format("sys/%s/%s/thing/service/%s",
127   -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice),
128   -// downstreamReqVO.getIdentifier());
129   -// IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO()
130   -// .setParams((Map<String, Object>) downstreamReqVO.getData());
131   -//// CommonResult<Boolean> result = requestPlugin(url, reqDTO, device);
132   -// CommonResult<Boolean> result = null;
133   -//
134   -// // 3. 发送设备消息
135   -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId())
136   -// .setType(IotDeviceMessageTypeEnum.SERVICE.getType()).setIdentifier(reqDTO.getIdentifier())
137   -// .setData(reqDTO.getParams());
138   -// sendDeviceMessage(message, device, result.getCode());
139   -//
140   -// // 4. 如果不成功,抛出异常,提示用户
141   -// if (result.isError()) {
142   -// log.error("[invokeDeviceService][设备({})服务调用失败,请求参数:({}),响应结果:({})]",
143   -// device.getDeviceKey(), reqDTO, result);
144   -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
145   -// }
146   -// return message;
147   -// }
148   -
149   -// /**
150   -// * 获取设备属性
151   -// *
152   -// * @param downstreamReqVO 下行请求
153   -// * @param device 设备
154   -// * @param parentDevice 父设备
155   -// * @return 下发消息
156   -// */
157   -// @SuppressWarnings("unchecked")
158   -// private IotDeviceMessage getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,
159   -// IotDeviceDO device, IotDeviceDO parentDevice) {
160   -// // 1. 参数校验
161   -// if (!(downstreamReqVO.getData() instanceof List<?>)) {
162   -// throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型");
163   -// }
164   -// // TODO @super:【可优化】过滤掉不合法的属性
165   -//
166   -// // 2. 发送请求
167   -// String url = String.format("sys/%s/%s/thing/service/property/get",
168   -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice));
169   -// IotDevicePropertyGetReqDTO reqDTO = new IotDevicePropertyGetReqDTO()
170   -// .setIdentifiers((List<String>) downstreamReqVO.getData());
171   -//// CommonResult<Boolean> result = requestPlugin(url, reqDTO, device);
172   -// CommonResult<Boolean> result = null;
173   -//
174   -// // 3. 发送设备消息
175   -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId())
176   -// .setType(IotDeviceMessageTypeEnum.PROPERTY.getType())
177   -// .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())
178   -// .setData(reqDTO.getIdentifiers());
179   -// sendDeviceMessage(message, device, result.getCode());
180   -//
181   -// // 4. 如果不成功,抛出异常,提示用户
182   -// if (result.isError()) {
183   -// log.error("[getDeviceProperty][设备({})属性获取失败,请求参数:({}),响应结果:({})]",
184   -// device.getDeviceKey(), reqDTO, result);
185   -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
186   -// }
187   -// return message;
188   -// }
189   -
190   -// /**
191   -// * 设置设备配置
192   -// *
193   -// * @param downstreamReqVO 下行请求
194   -// * @param device 设备
195   -// * @param parentDevice 父设备
196   -// * @return 下发消息
197   -// */
198   -// @SuppressWarnings({ "unchecked", "unused" })
199   -// private IotDeviceMessage setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO,
200   -// IotDeviceDO device, IotDeviceDO parentDevice) {
201   -// // 1. 参数转换,无需校验
202   -// Map<String, Object> config = JsonUtils.parseObject(device.getConfig(), Map.class);
203   -//
204   -// // 2. 发送请求
205   -// String url = String.format("sys/%s/%s/thing/service/config/set",
206   -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice));
207   -// IotDeviceConfigSetReqDTO reqDTO = new IotDeviceConfigSetReqDTO()
208   -// .setConfig(config);
209   -//// CommonResult<Boolean> result = requestPlugin(url, reqDTO, device);
210   -// CommonResult<Boolean> result = null;
211   -//
212   -// // 3. 发送设备消息
213   -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId())
214   -// .setType(IotDeviceMessageTypeEnum.CONFIG.getType())
215   -// .setIdentifier(IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())
216   -// .setData(reqDTO.getConfig());
217   -// sendDeviceMessage(message, device, result.getCode());
218   -//
219   -// // 4. 如果不成功,抛出异常,提示用户
220   -// if (result.isError()) {
221   -// log.error("[setDeviceConfig][设备({})配置下发失败,请求参数:({}),响应结果:({})]",
222   -// device.getDeviceKey(), reqDTO, result);
223   -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
224   -// }
225   -// return message;
226   -// }
227   -
228   -// /**
229   -// * 设备 OTA 升级
230   -// *
231   -// * @param downstreamReqVO 下行请求
232   -// * @param device 设备
233   -// * @param parentDevice 父设备
234   -// * @return 下发消息
235   -// */
236   -// private IotDeviceMessage otaUpgrade(IotDeviceDownstreamReqVO downstreamReqVO,
237   -// IotDeviceDO device, IotDeviceDO parentDevice) {
238   -// // 1. 参数校验
239   -// if (!(downstreamReqVO.getData() instanceof Map<?, ?> data)) {
240   -// throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型");
241   -// }
242   -//
243   -// // 2. 发送请求
244   -// String url = String.format("ota/%s/%s/upgrade",
245   -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice));
246   -// IotDeviceOtaUpgradeReqDTO reqDTO = IotDeviceOtaUpgradeReqDTO.build(data);
247   -//// CommonResult<Boolean> result = requestPlugin(url, reqDTO, device);
248   -// CommonResult<Boolean> result = null;
249   -//
250   -// // 3. 发送设备消息
251   -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId())
252   -// .setType(IotDeviceMessageTypeEnum.OTA.getType())
253   -// .setIdentifier(IotDeviceMessageIdentifierEnum.OTA_UPGRADE.getIdentifier())
254   -// .setData(downstreamReqVO.getData());
255   -// sendDeviceMessage(message, device, result.getCode());
256   -//
257   -// // 4. 如果不成功,抛出异常,提示用户
258   -// if (result.isError()) {
259   -// log.error("[otaUpgrade][设备({}) OTA 升级失败,请求参数:({}),响应结果:({})]",
260   -// device.getDeviceKey(), reqDTO, result);
261   -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
262   -// }
263   -// return message;
264   -// }
265   -
266   - private String getDeviceName(IotDeviceDO device, IotDeviceDO parentDevice) {
267   - return parentDevice != null ? parentDevice.getDeviceName() : device.getDeviceName();
268   - }
269   -
270   - private String getProductKey(IotDeviceDO device, IotDeviceDO parentDevice) {
271   - return parentDevice != null ? parentDevice.getProductKey() : device.getProductKey();
272   - }
273   -
274   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java deleted
1   -package cn.iocoder.yudao.module.iot.service.device.control;
2   -
3   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
4   -import jakarta.validation.Valid;
5   -
6   -/**
7   - * IoT 设备上行 Service 接口
8   - *
9   - * 目的:设备 -> 网关 -> 服务端
10   - *
11   - * @author 芋道源码
12   - */
13   -public interface IotDeviceUpstreamService {
14   -
15   - /**
16   - * 设备上行,可用于设备模拟
17   - *
18   - * @param simulatorReqVO 设备上行请求 VO
19   - */
20   - void upstreamDevice(@Valid IotDeviceUpstreamReqVO simulatorReqVO);
21   -
22   -// /**
23   -// * 上报设备事件数据
24   -// *
25   -// * @param reportReqDTO 设备事件
26   -// */
27   -// void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO);
28   -//
29   -// /**
30   -// * 注册设备
31   -// *
32   -// * @param registerReqDTO 注册设备 DTO
33   -// */
34   -// void registerDevice(IotDeviceRegisterReqDTO registerReqDTO);
35   -//
36   -// /**
37   -// * 注册子设备
38   -// *
39   -// * @param registerReqDTO 注册子设备 DTO
40   -// */
41   -// void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO);
42   -//
43   -// /**
44   -// * 添加设备拓扑
45   -// *
46   -// * @param addReqDTO 添加设备拓扑 DTO
47   -// */
48   -// void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO);
49   -
50   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.iot.service.device.control;
2   -
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.util.IdUtil;
5   -import cn.hutool.core.util.ObjUtil;
6   -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
7   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
8   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
9   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
10   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
11   -import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
12   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
13   -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
14   -import jakarta.annotation.Resource;
15   -import lombok.extern.slf4j.Slf4j;
16   -import org.springframework.stereotype.Service;
17   -import org.springframework.validation.annotation.Validated;
18   -
19   -import java.time.LocalDateTime;
20   -import java.util.Map;
21   -import java.util.Objects;
22   -
23   -/**
24   - * IoT 设备上行 Service 实现类
25   - *
26   - * @author 芋道源码
27   - */
28   -@Service
29   -@Validated
30   -@Slf4j
31   -public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
32   -
33   - @Resource
34   - private IotDeviceService deviceService;
35   - @Resource
36   - private IotDevicePropertyService devicePropertyService;
37   -
38   - // TODO @芋艿:需要重新实现下;
39   - @Override
40   - @SuppressWarnings("unchecked")
41   - public void upstreamDevice(IotDeviceUpstreamReqVO simulatorReqVO) {
42   - // 1. 校验存在
43   - IotDeviceDO device = deviceService.validateDeviceExists(simulatorReqVO.getId());
44   -
45   - // 2.1 情况一:属性上报
46   - String requestId = IdUtil.fastSimpleUUID();
47   - if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) {
48   - reportDeviceProperty(((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO()
49   - .setRequestId(requestId).setReportTime(LocalDateTime.now())
50   - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()))
51   - .setProperties((Map<String, Object>) simulatorReqVO.getData()));
52   - return;
53   - }
54   - // 2.2 情况二:事件上报
55   - if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.EVENT.getType())) {
56   - reportDeviceEvent(((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId)
57   - .setReportTime(LocalDateTime.now())
58   - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()))
59   - .setIdentifier(simulatorReqVO.getIdentifier())
60   - .setParams((Map<String, Object>) simulatorReqVO.getData()));
61   - return;
62   - }
63   - // 2.3 情况三:状态变更
64   - if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.STATE.getType())) {
65   - // TODO @芋艿:这里未搞完
66   - return;
67   - }
68   - throw new IllegalArgumentException("未知的类型:" + simulatorReqVO.getType());
69   - }
70   -
71   -// @Override TODO 芋艿:待重新实现
72   - public void reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) {
73   - // 1.1 获得设备
74   - log.info("[reportDeviceProperty][上报设备属性: {}]", reportReqDTO);
75   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
76   - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName());
77   - if (device == null) {
78   - log.error("[reportDeviceProperty][设备({}/{})不存在]",
79   - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName());
80   - return;
81   - }
82   -
83   - // 2. 发送设备消息
84   -// IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class)
85   -// .setType(IotDeviceMessageTypeEnum.PROPERTY.getType())
86   -// .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())
87   -// .setData(reportReqDTO.getProperties());
88   -// sendDeviceMessage(message, device);
89   - }
90   -
91   - // @Override TODO 芋艿:待重新实现
92   - public void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) {
93   - // 1.1 获得设备
94   - log.info("[reportDeviceEvent][上报设备事件: {}]", reportReqDTO);
95   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
96   - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName());
97   - if (device == null) {
98   - log.error("[reportDeviceEvent][设备({}/{})不存在]",
99   - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName());
100   - return;
101   - }
102   -
103   - // 2. 发送设备消息
104   -// IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class)
105   -// .setType(IotDeviceMessageTypeEnum.EVENT.getType())
106   -// .setIdentifier(reportReqDTO.getIdentifier())
107   -// .setData(reportReqDTO.getParams());
108   -// sendDeviceMessage(message, device);
109   - }
110   -
111   - // @Override TODO 芋艿:待重新实现
112   - public void registerDevice(IotDeviceRegisterReqDTO registerReqDTO) {
113   - log.info("[registerDevice][注册设备: {}]", registerReqDTO);
114   - registerDevice0(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), null, registerReqDTO);
115   - }
116   -
117   - private void registerDevice0(String productKey, String deviceName, Long gatewayId,
118   - IotDeviceUpstreamAbstractReqDTO registerReqDTO) {
119   - // 1.1 注册设备
120   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName);
121   - boolean registerNew = device == null;
122   - if (device == null) {
123   - device = deviceService.createDevice(productKey, deviceName, gatewayId);
124   - log.info("[registerDevice0][消息({}) 设备({}/{}) 成功注册]", registerReqDTO, productKey, device);
125   - } else if (gatewayId != null && ObjUtil.notEqual(device.getGatewayId(), gatewayId)) {
126   - Long deviceId = device.getId();
127   - TenantUtils.execute(device.getTenantId(),
128   - () -> deviceService.updateDeviceGateway(deviceId, gatewayId));
129   - log.info("[registerDevice0][消息({}) 设备({}/{}) 更新网关设备编号({})]",
130   - registerReqDTO, productKey, device, gatewayId);
131   - }
132   - // 1.2 记录设备的最后时间
133   -// updateDeviceLastTime(device, registerReqDTO);
134   -
135   - // 2. 发送设备消息
136   - if (registerNew) {
137   -// IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class)
138   -// .setType(IotDeviceMessageTypeEnum.REGISTER.getType())
139   -// .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER.getIdentifier());
140   -// sendDeviceMessage(message, device);
141   - }
142   - }
143   -
144   - // @Override TODO 芋艿:待重新实现
145   - public void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) {
146   - // 1.1 注册子设备
147   - log.info("[registerSubDevice][注册子设备: {}]", registerReqDTO);
148   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
149   - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
150   - if (device == null) {
151   - log.error("[registerSubDevice][设备({}/{}) 不存在]",
152   - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
153   - return;
154   - }
155   - if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
156   - log.error("[registerSubDevice][设备({}/{}) 不是网关设备({}),无法进行注册]",
157   - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), device);
158   - return;
159   - }
160   - // 1.2 记录设备的最后时间
161   -// updateDeviceLastTime(device, registerReqDTO);
162   -
163   - // 2. 处理子设备
164   - if (CollUtil.isNotEmpty(registerReqDTO.getParams())) {
165   - registerReqDTO.getParams().forEach(subDevice -> registerDevice0(
166   - subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO));
167   - // TODO @芋艿:后续要处理,每个设备是否成功
168   - }
169   -
170   - // 3. 发送设备消息
171   -// IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class)
172   -// .setType(IotDeviceMessageTypeEnum.REGISTER.getType())
173   -// .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER_SUB.getIdentifier())
174   -// .setData(registerReqDTO.getParams());
175   -// sendDeviceMessage(message, device);
176   - }
177   -
178   - // @Override TODO 芋艿:待重新实现
179   - public void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) {
180   - // 1.1 获得设备
181   - log.info("[addDeviceTopology][添加设备拓扑: {}]", addReqDTO);
182   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
183   - addReqDTO.getProductKey(), addReqDTO.getDeviceName());
184   - if (device == null) {
185   - log.error("[addDeviceTopology][设备({}/{}) 不存在]",
186   - addReqDTO.getProductKey(), addReqDTO.getDeviceName());
187   - return;
188   - }
189   - if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
190   - log.error("[addDeviceTopology][设备({}/{}) 不是网关设备({}),无法进行拓扑添加]",
191   - addReqDTO.getProductKey(), addReqDTO.getDeviceName(), device);
192   - return;
193   - }
194   -
195   - // 2. 处理拓扑
196   - if (CollUtil.isNotEmpty(addReqDTO.getParams())) {
197   - TenantUtils.execute(device.getTenantId(), () -> {
198   - addReqDTO.getParams().forEach(subDevice -> {
199   - IotDeviceDO subDeviceDO = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
200   - subDevice.getProductKey(), subDevice.getDeviceName());
201   - // TODO @芋艿:后续要处理,每个设备是否成功
202   - if (subDeviceDO == null) {
203   - log.error("[addDeviceTopology][子设备({}/{}) 不存在]",
204   - subDevice.getProductKey(), subDevice.getDeviceName());
205   - return;
206   - }
207   - deviceService.updateDeviceGateway(subDeviceDO.getId(), device.getId());
208   - log.info("[addDeviceTopology][子设备({}/{}) 添加到网关设备({}) 成功]",
209   - subDevice.getProductKey(), subDevice.getDeviceName(), device);
210   - });
211   - });
212   - }
213   -
214   - // 3. 发送设备消息
215   -// IotDeviceMessage message = BeanUtils.toBean(addReqDTO, IotDeviceMessage.class)
216   -// .setType(IotDeviceMessageTypeEnum.TOPOLOGY.getType())
217   -// .setIdentifier(IotDeviceMessageIdentifierEnum.TOPOLOGY_ADD.getIdentifier())
218   -// .setData(addReqDTO.getParams());
219   -// sendDeviceMessage(message, device);
220   - }
221   -
222   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device.message;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  5 +
  6 +/**
  7 + * IoT 设备消息 Service 接口
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public interface IotDeviceMessageService {
  12 +
  13 + /**
  14 + * 初始化设备消息的 TDengine 超级表
  15 + *
  16 + * 系统启动时,会自动初始化一次
  17 + */
  18 + void defineDeviceMessageStable();
  19 +
  20 + /**
  21 + * 发送设备消息
  22 + *
  23 + * @param message 消息(“codec(编解码)字段” 部分字段)
  24 + * @param device 设备
  25 + * @return 设备消息
  26 + */
  27 + IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device);
  28 +
  29 + /**
  30 + * 发送设备消息
  31 + *
  32 + * @param message 消息(“codec(编解码)字段” 部分字段)
  33 + * @return 设备消息
  34 + */
  35 + IotDeviceMessage sendDeviceMessage(IotDeviceMessage message);
  36 +
  37 + /**
  38 + * 处理设备上行的消息,包括如下步骤:
  39 + *
  40 + * 1. 处理消息
  41 + * 2. 记录消息
  42 + * 3. 回复消息
  43 + *
  44 + * @param message 消息
  45 + * @param device 设备
  46 + */
  47 + void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device);
  48 +
  49 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device.message;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  7 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
  8 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  9 +import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
  10 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  12 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
  13 +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper;
  14 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  15 +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
  16 +import com.google.common.base.Objects;
  17 +import jakarta.annotation.Resource;
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.stereotype.Service;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import java.time.LocalDateTime;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  25 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL;
  26 +
  27 +/**
  28 + * IoT 设备消息 Service 实现类
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +@Service
  33 +@Validated
  34 +@Slf4j
  35 +public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
  36 +
  37 + @Resource
  38 + private IotDeviceService deviceService;
  39 + @Resource
  40 + private IotDevicePropertyService devicePropertyService;
  41 +
  42 + @Resource
  43 + private IotDeviceMessageMapper deviceLogMapper;
  44 +
  45 + @Resource
  46 + private IotDeviceMessageProducer deviceMessageProducer;
  47 +
  48 + @Override
  49 + public void defineDeviceMessageStable() {
  50 + if (StrUtil.isNotEmpty(deviceLogMapper.showSTable())) {
  51 + log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]");
  52 + return;
  53 + }
  54 + log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]");
  55 + deviceLogMapper.createSTable();
  56 + log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]");
  57 + }
  58 +
  59 + // TODO @芋艿:要不要异步记录;
  60 + private void createDeviceLog(IotDeviceMessage message) {
  61 + IotDeviceMessageDO messageDO = BeanUtils.toBean(message, IotDeviceMessageDO.class)
  62 + .setUpstream(IotDeviceMessageUtils.isUpstreamMessage(message));
  63 + deviceLogMapper.insert(messageDO);
  64 + }
  65 +
  66 + @Override
  67 + public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message) {
  68 + IotDeviceDO device = deviceService.validateDeviceExists(message.getDeviceId());
  69 + return sendDeviceMessage(message, device);
  70 + }
  71 +
  72 + // TODO @芋艿:针对连接网关的设备,是不是 productKey、deviceName 需要调整下;
  73 + @Override
  74 + public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
  75 + // 1. 补充信息
  76 + appendDeviceMessage(message, device);
  77 +
  78 + // 2.1 情况一:发送上行消息
  79 + boolean upstream = IotDeviceMessageUtils.isUpstreamMessage(message);
  80 + if (upstream) {
  81 + deviceMessageProducer.sendDeviceMessage(message);
  82 + return message;
  83 + }
  84 +
  85 + // 2.2 情况二:发送下行消息
  86 + // 如果是下行消息,需要校验 serverId 存在
  87 + String serverId = devicePropertyService.getDeviceServerId(device.getProductKey(), device.getDeviceName());
  88 + if (StrUtil.isEmpty(serverId)) {
  89 + throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL);
  90 + }
  91 + deviceMessageProducer.sendDeviceMessageToGateway(serverId, message);
  92 + // 特殊:记录消息日志。原因:上行消息,消费时,已经会记录;下行消息,因为消费在 Gateway 端,所以需要在这里记录
  93 + createDeviceLog(message);
  94 + return message;
  95 + }
  96 +
  97 + /**
  98 + * 补充消息的后端字段
  99 + *
  100 + * @param message 消息
  101 + * @param device 设备信息
  102 + * @return 消息
  103 + */
  104 + private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
  105 + message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())
  106 + .setDeviceId(device.getId()).setTenantId(device.getTenantId());
  107 + // 特殊:如果设备没有指定 requestId,则使用 messageId
  108 + if (StrUtil.isEmpty(message.getRequestId())) {
  109 + message.setRequestId(message.getId());
  110 + }
  111 + return message;
  112 + }
  113 +
  114 + @Override
  115 + public void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
  116 + // 1. 理消息
  117 + Object replyData = null;
  118 + ServiceException serviceException = null;
  119 + try {
  120 + replyData = handleUpstreamDeviceMessage0(message, device);
  121 + } catch (ServiceException ex) {
  122 + serviceException = ex;
  123 + log.warn("[onMessage][message({}) 业务异常]", message, serviceException);
  124 + } catch (Exception ex) {
  125 + log.error("[onMessage][message({}) 发生异常]", message, ex);
  126 + throw ex;
  127 + }
  128 +
  129 + // 2. 记录消息
  130 + createDeviceLog(message);
  131 +
  132 + // 3. 回复消息。前提:非 _reply 消息,并且非禁用回复的消息
  133 + if (IotDeviceMessageUtils.isReplyMessage(message)
  134 + || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod())) {
  135 + return;
  136 + }
  137 + sendDeviceMessage(IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(), replyData,
  138 + serviceException != null ? serviceException.getCode() : null,
  139 + serviceException != null ? serviceException.getMessage() : null));
  140 + }
  141 +
  142 + // TODO @芋艿:可优化:未来逻辑复杂后,可以独立拆除 Processor 处理器
  143 + private Object handleUpstreamDeviceMessage0(IotDeviceMessage message, IotDeviceDO device) {
  144 + // 设备上线
  145 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod())) {
  146 + deviceService.updateDeviceState(device, IotDeviceStateEnum.ONLINE.getState());
  147 + // TODO 芋艿:子设备的关联
  148 + return null;
  149 + }
  150 + // 设备下线
  151 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod())) {
  152 + deviceService.updateDeviceState(device, IotDeviceStateEnum.OFFLINE.getState());
  153 + // TODO 芋艿:子设备的关联
  154 + return null;
  155 + }
  156 +
  157 + // 属性上报
  158 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_REPORT.getMethod())) {
  159 + devicePropertyService.saveDeviceProperty(device, message);
  160 + return null;
  161 + }
  162 +
  163 + // TODO @芋艿:这里可以按需,添加别的逻辑;
  164 + return null;
  165 + }
  166 +
  167 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java
1   -package cn.iocoder.yudao.module.iot.service.device.data;
  1 +package cn.iocoder.yudao.module.iot.service.device.property;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO;
5   -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
7 6  
8 7 import javax.annotation.Nullable;
9 8 import java.time.LocalDateTime;
... ... @@ -18,26 +17,12 @@ import java.util.Map;
18 17 public interface IotDeviceLogService {
19 18  
20 19 /**
21   - * 初始化 TDengine 超级表
22   - *
23   - * 系统启动时,会自动初始化一次
24   - */
25   - void defineDeviceLog();
26   -
27   - /**
28   - * 插入设备日志
29   - *
30   - * @param message 设备数据
31   - */
32   - void createDeviceLog(IotDeviceMessage message);
33   -
34   - /**
35 20 * 获得设备日志分页
36 21 *
37 22 * @param pageReqVO 分页查询
38 23 * @return 设备日志分页
39 24 */
40   - PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO);
  25 + PageResult<IotDeviceMessageDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO);
41 26  
42 27 /**
43 28 * 获得设备日志数量
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java
1   -package cn.iocoder.yudao.module.iot.service.device.data;
  1 +package cn.iocoder.yudao.module.iot.service.device.property;
2 2  
3 3 import cn.hutool.core.date.LocalDateTimeUtil;
4 4 import cn.hutool.core.map.MapUtil;
5   -import cn.hutool.core.util.IdUtil;
6   -import cn.hutool.core.util.StrUtil;
7 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO;
11   -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
12   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
13   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
14   -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
  8 +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper;
15 9 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
16 10 import com.baomidou.mybatisplus.core.metadata.IPage;
17 11 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
... ... @@ -40,39 +34,12 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService {
40 34 private IotDeviceService deviceService;
41 35  
42 36 @Resource
43   - private IotDeviceLogMapper deviceLogMapper;
  37 + private IotDeviceMessageMapper deviceLogMapper;
44 38  
45 39 @Override
46   - public void defineDeviceLog() {
47   - if (StrUtil.isNotEmpty(deviceLogMapper.showDeviceLogSTable())) {
48   - log.info("[defineDeviceLog][设备日志超级表已存在,创建跳过]");
49   - return;
50   - }
51   -
52   - log.info("[defineDeviceLog][设备日志超级表不存在,创建开始...]");
53   - deviceLogMapper.createDeviceLogSTable();
54   - log.info("[defineDeviceLog][设备日志超级表不存在,创建成功]");
55   - }
56   -
57   - @Override
58   - public void createDeviceLog(IotDeviceMessage message) {
59   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
60   - message.getProductKey(), message.getDeviceName());
61   - if (device == null) {
62   - log.error("[createDeviceLog][设备({}/{}) 不存在]", message.getProductKey(), message.getDeviceName());
63   - return;
64   - }
65   - IotDeviceLogDO log = BeanUtils.toBean(message, IotDeviceLogDO.class)
66   - .setId(IdUtil.fastSimpleUUID())
67   - .setContent(JsonUtils.toJsonString(message.getData()))
68   - .setTenantId(device.getTenantId());
69   - deviceLogMapper.insert(log);
70   - }
71   -
72   - @Override
73   - public PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) {
  40 + public PageResult<IotDeviceMessageDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) {
74 41 try {
75   - IPage<IotDeviceLogDO> page = deviceLogMapper.selectPage(
  42 + IPage<IotDeviceMessageDO> page = deviceLogMapper.selectPage(
76 43 new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO);
77 44 return new PageResult<>(page.getRecords(), page.getTotal());
78 45 } catch (Exception exception) {
... ... @@ -92,7 +59,8 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService {
92 59 @Override
93 60 public List<Map<Long, Integer>> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) {
94 61 // TODO @super:不能只基于数据库统计。因为有一些小时,可能出现没数据的情况,导致前端展示的图是不全的。可以参考 CrmStatisticsCustomerService 来实现
95   - List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime);
  62 + // TODO @芋艿:这里实现,需要调整
  63 + List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogUpCountByHour(0L, startTime, endTime);
96 64 return list.stream()
97 65 .map(map -> {
98 66 // 从Timestamp获取时间戳
... ... @@ -108,7 +76,8 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService {
108 76 // TODO @super:getDeviceLogDownCountByHour 融合到 getDeviceLogUpCountByHour
109 77 @Override
110 78 public List<Map<Long, Integer>> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) {
111   - List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime);
  79 + // TODO @芋艿:这里实现,需要调整
  80 + List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogDownCountByHour(0L, startTime, endTime);
112 81 return list.stream()
113 82 .map(map -> {
114 83 // 从Timestamp获取时间戳
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java
1   -package cn.iocoder.yudao.module.iot.service.device.data;
  1 +package cn.iocoder.yudao.module.iot.service.device.property;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO;
5 5 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO;
6 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
7 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
8 9 import jakarta.validation.Valid;
9 10  
... ... @@ -30,9 +31,10 @@ public interface IotDevicePropertyService {
30 31 /**
31 32 * 保存设备数据
32 33 *
  34 + * @param device 设备
33 35 * @param message 设备消息
34 36 */
35   - void saveDeviceProperty(IotDeviceMessage message);
  37 + void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message);
36 38  
37 39 /**
38 40 * 获得设备属性最新数据
... ... @@ -79,14 +81,6 @@ public interface IotDevicePropertyService {
79 81 void updateDeviceServerId(String productKey, String deviceName, String serverId);
80 82  
81 83 /**
82   - * 删除设备关联的网关 serverId
83   - *
84   - * @param productKey 产品标识
85   - * @param deviceName 设备名称
86   - */
87   - void deleteDeviceServerId(String productKey, String deviceName);
88   -
89   - /**
90 84 * 获得设备关联的网关 serverId
91 85 *
92 86 * @param productKey 产品标识
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java
1   -package cn.iocoder.yudao.module.iot.service.device.data;
  1 +package cn.iocoder.yudao.module.iot.service.device.property;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 5 import cn.hutool.core.map.MapUtil;
6 6 import cn.hutool.core.util.StrUtil;
7 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8   -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
9 8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO;
10 9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO;
11 10 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs;
... ... @@ -121,21 +120,13 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
121 120 }
122 121  
123 122 @Override
124   - @TenantIgnore
125   - public void saveDeviceProperty(IotDeviceMessage message) {
  123 + public void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message) {
126 124 if (!(message.getData() instanceof Map)) {
127 125 log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message);
128 126 return;
129 127 }
130   - // 1. 获得设备信息
131   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
132   - message.getProductKey(), message.getDeviceName());
133   - if (device == null) {
134   - log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message);
135   - return;
136   - }
137 128  
138   - // 2. 根据物模型,拼接合法的属性
  129 + // 1. 根据物模型,拼接合法的属性
139 130 // TODO @芋艿:【待定 004】赋能后,属性到底以 thingModel 为准(ik),还是 db 的表结构为准(tl)?
140 131 List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductKeyFromCache(device.getProductKey());
141 132 Map<String, Object> properties = new HashMap<>();
... ... @@ -151,11 +142,11 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
151 142 return;
152 143 }
153 144  
154   - // 3.1 保存设备属性【数据】
  145 + // 2.1 保存设备属性【数据】
155 146 devicePropertyMapper.insert(device, properties,
156 147 LocalDateTimeUtil.toEpochMilli(message.getReportTime()));
157 148  
158   - // 3.2 保存设备属性【日志】
  149 + // 2.2 保存设备属性【日志】
159 150 Map<String, IotDevicePropertyDO> properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry ->
160 151 IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build());
161 152 deviceDataRedisDAO.putAll(device.getProductKey(), device.getDeviceName(), properties2);
... ... @@ -210,11 +201,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
210 201 }
211 202  
212 203 @Override
213   - public void deleteDeviceServerId(String productKey, String deviceName) {
214   - deviceServerIdRedisDAO.delete(productKey, deviceName);
215   - }
216   -
217   - @Override
218 204 public String getDeviceServerId(String productKey, String deviceName) {
219 205 return deviceServerIdRedisDAO.get(productKey, deviceName);
220 206 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
... ... @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc
8 8 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
9 9 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
10 10 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
11   -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
  11 +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
12 12 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
13 13 import jakarta.annotation.Resource;
14 14 import org.springframework.context.annotation.Lazy;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java
... ... @@ -309,8 +309,10 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
309 309 private List<IotRuleSceneDO> getMatchedRuleSceneListByMessage(IotDeviceMessage message) {
310 310 // 1. 匹配设备
311 311 // TODO @芋艿:可能需要 getSelf(); 缓存
312   - List<IotRuleSceneDO> ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache(
313   - message.getProductKey(), message.getDeviceName());
  312 + List<IotRuleSceneDO> ruleScenes = null;
  313 + // TODO @芋艿:这里需要适配
  314 +// List<IotRuleSceneDO> ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache(
  315 +// message.getProductKey(), message.getDeviceName());
314 316 if (CollUtil.isEmpty(ruleScenes)) {
315 317 return ruleScenes;
316 318 }
... ... @@ -329,10 +331,11 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
329 331 }
330 332 // 2.3 多个条件,只需要满足一个即可
331 333 IotRuleSceneDO.TriggerCondition matchedCondition = CollUtil.findOne(trigger.getConditions(), condition -> {
332   - if (ObjUtil.notEqual(message.getType(), condition.getType())
333   - || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) {
334   - return false;
335   - }
  334 + // TODO @芋艿:这里的逻辑,需要适配
  335 +// if (ObjUtil.notEqual(message.getType(), condition.getType())
  336 +// || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) {
  337 +// return false;
  338 +// }
336 339 // 多个条件参数,必须全部满足。所以,下面的逻辑就是找到一个不满足的条件参数
337 340 IotRuleSceneDO.TriggerConditionParameter notMatchedParameter = CollUtil.findOne(condition.getParameters(),
338 341 parameter -> !isTriggerConditionParameterMatched(message, parameter, ruleScene, trigger));
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.action;
2 2  
3 3 import cn.hutool.core.lang.Assert;
4   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
5 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
7 6 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
8 7 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
9 8 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
10   -import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService;
  9 +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
11 10 import jakarta.annotation.Resource;
12 11 import lombok.extern.slf4j.Slf4j;
13 12 import org.springframework.stereotype.Component;
... ... @@ -22,9 +21,9 @@ import org.springframework.stereotype.Component;
22 21 public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction {
23 22  
24 23 @Resource
25   - private IotDeviceDownstreamService deviceDownstreamService;
26   - @Resource
27 24 private IotDeviceService deviceService;
  25 + @Resource
  26 + private IotDeviceMessageService deviceMessageService;
28 27  
29 28 @Override
30 29 public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
... ... @@ -38,9 +37,9 @@ public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction {
38 37 return;
39 38 }
40 39 try {
41   - IotDeviceMessage downstreamMessage = deviceDownstreamService.downstreamDevice(new IotDeviceDownstreamReqVO()
42   - .setId(device.getId()).setType(control.getType()).setIdentifier(control.getIdentifier())
43   - .setData(control.getData()));
  40 + // TODO @芋艿:@puhui999:这块可能要改,从 type => method
  41 + IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf(
  42 + control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId()));
44 43 log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage);
45 44 } catch (Exception e) {
46 45 log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml renamed to yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml
... ... @@ -2,51 +2,62 @@
2 2 <!DOCTYPE mapper
3 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5   -<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper">
  5 +<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper">
6 6  
7   - <update id="createDeviceLogSTable">
8   - CREATE STABLE IF NOT EXISTS device_log (
  7 + <update id="createSTable">
  8 + CREATE STABLE IF NOT EXISTS device_message (
9 9 ts TIMESTAMP,
10 10 id NCHAR(50),
11   - message_id NCHAR(50),
12   - type NCHAR(50),
13   - identifier NCHAR(255),
14   - content NCHAR(1024),
15   - code INT,
16 11 report_time TIMESTAMP,
17   - tenant_id BIGINT
  12 + device_id BIGINT,
  13 + tenant_id BIGINT,
  14 + server_id NCHAR(50),
  15 + upstream BOOL,
  16 + request_id NCHAR(50),
  17 + method NCHAR(100),
  18 + params NCHAR(2048),
  19 + data NCHAR(2048),
  20 + code INT
18 21 ) TAGS (
19   - product_key NCHAR(50),
20   - device_name NCHAR(50)
  22 + device_id BIGINT
21 23 )
22 24 </update>
23 25  
24   - <select id="showDeviceLogSTable" resultType="String">
25   - SHOW STABLES LIKE 'device_log'
  26 + <select id="showSTable" resultType="String">
  27 + SHOW STABLES LIKE 'device_message'
26 28 </select>
27 29  
28 30 <insert id="insert">
29   - INSERT INTO device_log_${productKey}_${deviceName} (
30   - ts, id, message_id, type, identifier,
31   - content, code, report_time, tenant_id
  31 + INSERT INTO device_message_${deviceId} (
  32 + ts, id, report_time, device_id, tenant_id,
  33 + server_id, upstream, request_id, method, params,
  34 + data, code
32 35 )
33   - USING device_log
34   - TAGS ('${productKey}', '${deviceName}')
  36 + USING device_message
  37 + TAGS (#{deviceId})
35 38 VALUES (
36   - NOW, #{id}, #{messageId}, #{type}, #{identifier},
37   - #{content}, #{code}, #{reportTime}, #{tenantId}
  39 + #{ts}, #{id}, #{reportTime}, #{deviceId}, #{tenantId},
  40 + #{serverId}, #{upstream}, #{requestId}, #{method}, #{params},
  41 + #{data}, #{code}
38 42 )
39 43 </insert>
40 44  
41   - <select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO">
42   - SELECT ts, id, device_key, product_key, type, identifier, content, report_time
43   - FROM device_log_${productKey}_${deviceName}
  45 + <select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO">
  46 + SELECT ts, id, report_time, device_id, tenant_id, server_id, upstream,
  47 + request_id, method, params, data, code
  48 + FROM device_message_${reqVO.deviceId}
44 49 <where>
45   - <if test="reqVO.type != null and reqVO.type != ''">
46   - AND type = #{reqVO.type}
  50 + <if test="reqVO.method != null and reqVO.method != ''">
  51 + AND method = #{reqVO.method}
47 52 </if>
48   - <if test="reqVO.identifier != null and reqVO.identifier != ''">
49   - AND identifier LIKE CONCAT('%',#{reqVO.identifier},'%')
  53 + <if test="reqVO.upstream != null">
  54 + AND upstream = #{reqVO.upstream}
  55 + </if>
  56 + <if test="reqVO.startTime != null">
  57 + AND ts >= #{reqVO.startTime}
  58 + </if>
  59 + <if test="reqVO.endTime != null">
  60 + AND ts &lt;= #{reqVO.endTime}
50 61 </if>
51 62 </where>
52 63 ORDER BY ts DESC
... ... @@ -54,7 +65,7 @@
54 65  
55 66 <select id="selectCountByCreateTime" resultType="Long">
56 67 SELECT COUNT(*)
57   - FROM device_log
  68 + FROM device_message
58 69 <where>
59 70 <if test="createTime != null">
60 71 AND ts >= #{createTime}
... ... @@ -68,11 +79,11 @@
68 79 COUNT(*) as data
69 80 FROM
70 81 <choose>
71   - <when test="deviceKey != null and deviceKey != ''">
72   - device_log_${deviceKey}
  82 + <when test="deviceId != null">
  83 + device_message_${deviceId}
73 84 </when>
74 85 <otherwise>
75   - device_log
  86 + device_message
76 87 </otherwise>
77 88 </choose>
78 89 <where>
... ... @@ -82,9 +93,7 @@
82 93 <if test="endTime != null">
83 94 AND ts &lt;= #{endTime}
84 95 </if>
85   - AND (
86   - identifier IN ('online', 'offline', 'pull', 'progress', 'report', 'register', 'register_sub')
87   - )
  96 + AND upstream = true
88 97 </where>
89 98 GROUP BY TIMETRUNCATE(ts, 1h)
90 99 ORDER BY time ASC
... ... @@ -96,11 +105,11 @@
96 105 COUNT(*) as data
97 106 FROM
98 107 <choose>
99   - <when test="deviceKey != null and deviceKey != ''">
100   - device_log_${deviceKey}
  108 + <when test="deviceId != null">
  109 + device_message_${deviceId}
101 110 </when>
102 111 <otherwise>
103   - device_log
  112 + device_message
104 113 </otherwise>
105 114 </choose>
106 115 <where>
... ... @@ -110,10 +119,10 @@
110 119 <if test="endTime != null">
111 120 AND ts &lt;= #{endTime}
112 121 </if>
113   - AND identifier IN ('set', 'get', 'upgrade', 'unregister_sub', 'topology_add')
  122 + AND upstream = false
114 123 </where>
115 124 GROUP BY TIMETRUNCATE(ts, 1h)
116 125 ORDER BY time ASC
117 126 </select>
118 127  
119   -</mapper>
120 128 \ No newline at end of file
  129 +</mapper>
121 130 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageIdentifierEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.core.enums;
2   -
3   -import lombok.Getter;
4   -import lombok.RequiredArgsConstructor;
5   -
6   -// TODO @芋艿:需要添加对应的 DTO,以及上下行的链路,网关、网关服务、设备等
7   -
8   -/**
9   - * IoT 设备消息标识符枚举
10   - */
11   -@Getter
12   -@RequiredArgsConstructor
13   -public enum IotDeviceMessageIdentifierEnum {
14   -
15   - PROPERTY_GET("get"), // 下行 TODO 芋艿:【讨论】貌似这个“上行”更合理?device 主动拉取配置。和 IotDevicePropertyGetReqDTO 一样的配置
16   - PROPERTY_SET("set"), // 下行
17   - PROPERTY_REPORT("report"), // 上行
18   -
19   - STATE_ONLINE("online"), // 上行
20   - STATE_OFFLINE("offline"), // 上行
21   -
22   - CONFIG_GET("get"), // 上行 TODO 芋艿:【讨论】暂时没有上行的场景
23   - CONFIG_SET("set"), // 下行
24   -
25   - SERVICE_INVOKE("${identifier}"), // 下行
26   - SERVICE_REPLY_SUFFIX("_reply"), // 芋艿:TODO 芋艿:【讨论】上行 or 下行
27   -
28   - OTA_UPGRADE("upgrade"), // 下行
29   - OTA_PULL("pull"), // 上行
30   - OTA_PROGRESS("progress"), // 上行
31   - OTA_REPORT("report"), // 上行
32   -
33   - REGISTER_REGISTER("register"), // 上行
34   - REGISTER_REGISTER_SUB("register_sub"), // 上行
35   - REGISTER_UNREGISTER_SUB("unregister_sub"), // 下行
36   -
37   - TOPOLOGY_ADD("topology_add"), // 下行;
38   - ;
39   -
40   - /**
41   - * 标志符
42   - */
43   - private final String identifier;
44   -
45   -}
46 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
1 1 package cn.iocoder.yudao.module.iot.core.enums;
2 2  
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
3 5 import lombok.AllArgsConstructor;
4 6 import lombok.Getter;
5 7  
  8 +import java.util.Arrays;
  9 +import java.util.Set;
  10 +
6 11 /**
7 12 * IoT 设备消息的方法枚举
8 13 *
... ... @@ -10,16 +15,47 @@ import lombok.Getter;
10 15 */
11 16 @Getter
12 17 @AllArgsConstructor
13   -public enum IotDeviceMessageMethodEnum {
  18 +public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> {
14 19  
15 20 // ========== 设备状态 ==========
16 21  
17   - STATE_ONLINE("thing.state.online"),
18   - STATE_OFFLINE("thing.state.offline"),
  22 + STATE_ONLINE("thing.state.online", true),
  23 + STATE_OFFLINE("thing.state.offline", true),
  24 +
  25 + // ========== 设备属性 ==========
  26 + // 可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services
  27 + PROPERTY_REPORT("thing.property.report", true),
  28 +
  29 + PROPERTY_SET("thing.property.set", false),
  30 + PROPERTY_GET("thing.property.get", false),
  31 +
  32 + //
19 33  
20 34 ;
21 35  
  36 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod).toArray(String[]::new);
  37 +
  38 + /**
  39 + * 不进行 reply 回复的方法集合
  40 + */
  41 + public static final Set<String> REPLY_DISABLED = Set.of(STATE_ONLINE.getMethod(), STATE_OFFLINE.getMethod());
  42 +
22 43 private final String method;
23 44  
  45 + private final Boolean upstream;
  46 +
  47 + @Override
  48 + public String[] array() {
  49 + return ARRAYS;
  50 + }
  51 +
  52 + public static IotDeviceMessageMethodEnum of(String method) {
  53 + return ArrayUtil.firstMatch(item -> item.getMethod().equals(method),
  54 + IotDeviceMessageMethodEnum.values());
  55 + }
  56 +
  57 + public static boolean isReplyDisabled(String method) {
  58 + return REPLY_DISABLED.contains(method);
  59 + }
24 60  
25 61 }
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageTypeEnum.java
... ... @@ -14,7 +14,7 @@ import java.util.Arrays;
14 14 public enum IotDeviceMessageTypeEnum implements ArrayValuable<String> {
15 15  
16 16 STATE("state"), // 设备状态
17   - PROPERTY("property"), // 设备属性:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
  17 +// PROPERTY("property"), // 设备属性:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
18 18 EVENT("event"), // 设备事件:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
19 19 SERVICE("service"), // 设备服务:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
20 20 CONFIG("config"), // 设备配置:可参考 https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 远程配置
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java
1 1 package cn.iocoder.yudao.module.iot.core.mq.message;
2 2  
  3 +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
3 4 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
4 5 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
5 6 import lombok.AllArgsConstructor;
... ... @@ -43,6 +44,20 @@ public class IotDeviceMessage {
43 44 */
44 45 private LocalDateTime reportTime;
45 46  
  47 + /**
  48 + * 设备编号
  49 + */
  50 + private Long deviceId;
  51 + /**
  52 + * 租户编号
  53 + */
  54 + private Long tenantId;
  55 +
  56 + /**
  57 + * 服务编号,该消息由哪个 server 发送
  58 + */
  59 + private String serverId;
  60 +
46 61 // ========== codec(编解码)字段 ==========
47 62  
48 63 /**
... ... @@ -72,84 +87,53 @@ public class IotDeviceMessage {
72 87 * 响应错误码
73 88 */
74 89 private Integer code;
75   -
76   - // ========== 后端字段 ==========
77   -
78 90 /**
79   - * 设备编号
  91 + * 返回结果信息
80 92 */
81   - private Long deviceId;
82   - /**
83   - * 租户编号
84   - */
85   - private Long tenantId;
  93 + private String msg;
86 94  
87   - /**
88   - * 服务编号,该消息由哪个 server 服务进行消费
89   - */
90   - private String serverId;
  95 + // ========== 基础方法:只传递“codec(编解码)字段” ==========
  96 +
  97 + public static IotDeviceMessage requestOf(String method) {
  98 + return requestOf(null, method, null);
  99 + }
91 100  
92   -// public IotDeviceMessage ofPropertyReport(Map<String, Object> properties) {
93   -// this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
94   -// this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier());
95   -// this.setData(properties);
96   -// return this;
97   -// }
98   -//
99   -// public IotDeviceMessage ofPropertySet(Map<String, Object> properties) {
100   -// this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
101   -// this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier());
102   -// this.setData(properties);
103   -// return this;
104   -// }
105   -//
106   -// public IotDeviceMessage ofStateOnline() {
107   -// this.setType(IotDeviceMessageTypeEnum.STATE.getType());
108   -// this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());
109   -// return this;
110   -// }
111   -//
112   -// public IotDeviceMessage ofStateOffline() {
113   -// this.setType(IotDeviceMessageTypeEnum.STATE.getType());
114   -// this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());
115   -// return this;
116   -// }
117   -//
118   -// public static IotDeviceMessage of(String productKey, String deviceName) {
119   -// return of(productKey, deviceName,
120   -// null, null);
121   -// }
122   -//
123   -// public static IotDeviceMessage of(String productKey, String deviceName,
124   -// String serverId) {
125   -// return of(productKey, deviceName,
126   -// null, serverId);
127   -// }
128   -//
129   -// public static IotDeviceMessage of(String productKey, String deviceName,
130   -// LocalDateTime reportTime, String serverId) {
131   -// if (reportTime == null) {
132   -// reportTime = LocalDateTime.now();
133   -// }
134   -// String messageId = IotDeviceMessageUtils.generateMessageId();
135   -// return IotDeviceMessage.builder()
136   -// .messageId(messageId).reportTime(reportTime)
137   -// .productKey(productKey).deviceName(deviceName)
138   -// .serverId(serverId).build();
139   -// }
140   -
141   - public static IotDeviceMessage of(String requestId, String method, Object params) {
142   - return of(requestId, method, params, null, null);
  101 + public static IotDeviceMessage requestOf(String method, Object params) {
  102 + return requestOf(null, method, params);
  103 + }
  104 +
  105 + public static IotDeviceMessage requestOf(String requestId, String method, Object params) {
  106 + return of(requestId, method, params, null, null, null);
  107 + }
  108 +
  109 + public static IotDeviceMessage replyOf(String requestId, String method,
  110 + Object data, Integer code, String msg) {
  111 + if (code == null) {
  112 + code = GlobalErrorCodeConstants.SUCCESS.getCode();
  113 + msg = GlobalErrorCodeConstants.SUCCESS.getMsg();
  114 + }
  115 + return of(requestId, method, null, data, code, msg);
143 116 }
144 117  
145 118 public static IotDeviceMessage of(String requestId, String method,
146   - Object params, Object data, Integer code) {
  119 + Object params, Object data, Integer code, String msg) {
147 120 // 通用参数
148 121 IotDeviceMessage message = new IotDeviceMessage()
149 122 .setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now());
150 123 // 当前参数
151   - message.setRequestId(requestId).setMethod(method).setParams(params).setData(data).setCode(code);
  124 + message.setRequestId(requestId).setMethod(method).setParams(params)
  125 + .setData(data).setCode(code).setMsg(msg);
152 126 return message;
153 127 }
154 128  
  129 + // ========== 核心方法:在 of 基础方法之上,添加对应 method ==========
  130 +
  131 + public static IotDeviceMessage buildStateOnline() {
  132 + return requestOf(IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod());
  133 + }
  134 +
  135 + public static IotDeviceMessage buildStateOffline() {
  136 + return requestOf(IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod());
  137 + }
  138 +
155 139 }
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java
1 1 package cn.iocoder.yudao.module.iot.core.util;
2 2  
  3 +import cn.hutool.core.lang.Assert;
3 4 import cn.hutool.core.util.IdUtil;
4   -import cn.hutool.core.util.StrUtil;
5 5 import cn.hutool.system.SystemUtil;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
6 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7 8  
8 9 /**
... ... @@ -18,15 +19,28 @@ public class IotDeviceMessageUtils {
18 19 return IdUtil.fastSimpleUUID();
19 20 }
20 21  
21   - // TODO @芋艿:需要优化下;
22 22 /**
23 23 * 是否是上行消息:由设备发送
24 24 *
25 25 * @param message 消息
26 26 * @return 是否
27 27 */
  28 + @SuppressWarnings("SimplifiableConditionalExpression")
28 29 public static boolean isUpstreamMessage(IotDeviceMessage message) {
29   - return StrUtil.isNotEmpty(message.getServerId());
  30 + IotDeviceMessageMethodEnum methodEnum = IotDeviceMessageMethodEnum.of(message.getMethod());
  31 + Assert.notNull(methodEnum, "无法识别的消息方法:" + message.getMethod());
  32 + // 注意:回复消息时,需要取反
  33 + return !isReplyMessage(message) ? methodEnum.getUpstream() : !methodEnum.getUpstream();
  34 + }
  35 +
  36 + /**
  37 + * 是否是回复消息,通过 {@link IotDeviceMessage#getCode()} 非空进行识别
  38 + *
  39 + * @param message 消息
  40 + * @return 是否
  41 + */
  42 + public static boolean isReplyMessage(IotDeviceMessage message) {
  43 + return message.getCode() != null;
30 44 }
31 45  
32 46 // ========== Topic 相关 ==========
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java
1 1 package cn.iocoder.yudao.module.iot.gateway.codec.alink;
2 2  
3 3 import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 7 import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;
... ... @@ -48,18 +49,23 @@ public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec {
48 49 * 响应结果
49 50 */
50 51 private Object data;
51   -
52 52 /**
53 53 * 响应错误码
54 54 */
55 55 private Integer code;
  56 + /**
  57 + * 响应提示
  58 + *
  59 + * 特殊:这里阿里云是 message,为了保持和项目的 {@link CommonResult#getMsg()} 一致。
  60 + */
  61 + private String msg;
56 62  
57 63 }
58 64  
59 65 @Override
60 66 public byte[] encode(IotDeviceMessage message) {
61 67 AlinkMessage alinkMessage = new AlinkMessage(message.getRequestId(), AlinkMessage.VERSION_1,
62   - message.getMethod(), message.getParams(), message.getData(), message.getCode());
  68 + message.getMethod(), message.getParams(), message.getData(), message.getCode(), message.getMsg());
63 69 return JsonUtils.toJsonByte(alinkMessage);
64 70 }
65 71  
... ... @@ -69,8 +75,8 @@ public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec {
69 75 AlinkMessage alinkMessage = JsonUtils.parseObject(bytes, AlinkMessage.class);
70 76 Assert.notNull(alinkMessage, "消息不能为空");
71 77 Assert.equals(alinkMessage.getVersion(), AlinkMessage.VERSION_1, "消息版本号必须是 1.0");
72   - return IotDeviceMessage.of(alinkMessage.getId(),
73   - alinkMessage.getMethod(), alinkMessage.getParams(), alinkMessage.getData(), alinkMessage.getCode());
  78 + return IotDeviceMessage.of(alinkMessage.getId(), alinkMessage.getMethod(), alinkMessage.getParams(),
  79 + alinkMessage.getData(), alinkMessage.getCode(), alinkMessage.getMsg());
74 80 }
75 81  
76 82 @Override
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
... ... @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.iot.gateway.config;
3 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 4 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber;
5 5 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
6   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber;
7   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
8 6 import lombok.extern.slf4j.Slf4j;
9 7 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
10 8 import org.springframework.boot.context.properties.EnableConfigurationProperties;
... ... @@ -44,16 +42,17 @@ public class IotGatewayConfiguration {
44 42 @Slf4j
45 43 public static class MqttProtocolConfiguration {
46 44  
47   - @Bean
48   - public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties) {
49   - return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getEmqx());
50   - }
51   -
52   - @Bean
53   - public IotMqttDownstreamSubscriber iotMqttDownstreamSubscriber(IotMqttUpstreamProtocol mqttUpstreamProtocol,
54   - IotMessageBus messageBus) {
55   - return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, messageBus);
56   - }
  45 + // TODO @haohao:临时注释,避免报错
  46 +// @Bean
  47 +// public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties) {
  48 +// return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getEmqx());
  49 +// }
  50 +//
  51 +// @Bean
  52 +// public IotMqttDownstreamSubscriber iotMqttDownstreamSubscriber(IotMqttUpstreamProtocol mqttUpstreamProtocol,
  53 +// IotMessageBus messageBus) {
  54 +// return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, messageBus);
  55 +// }
57 56 }
58 57  
59 58 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java
1 1 package cn.iocoder.yudao.module.iot.gateway.protocol.http.router;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4 3 import cn.hutool.core.lang.Assert;
5 4 import cn.hutool.core.map.MapUtil;
6 5 import cn.hutool.core.text.StrPool;
7   -import cn.hutool.core.util.StrUtil;
8 6 import cn.hutool.extra.spring.SpringUtil;
9 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
10 8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
11 9 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
12   -import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
13 10 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
14 11 import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
15   -import io.vertx.core.json.JsonObject;
16 12 import io.vertx.ext.web.RoutingContext;
17 13 import lombok.RequiredArgsConstructor;
18 14 import lombok.extern.slf4j.Slf4j;
19 15  
20   -import java.util.Map;
21   -
22 16 /**
23 17 * IoT 网关 HTTP 协议的【上行】处理器
24 18 *
... ... @@ -61,135 +55,4 @@ public class IotHttpUpstreamHandler extends IotHttpAbstractHandler {
61 55 return CommonResult.success(MapUtil.of("messageId", message.getId()));
62 56 }
63 57  
64   - /**
65   - * 判断是否是属性上报路径
66   - *
67   - * @param path 路径
68   - * @return 是否是属性上报路径
69   - */
70   - private boolean isPropertyPostPath(String path) {
71   - return StrUtil.endWith(path, IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic());
72   - }
73   -
74   - /**
75   - * 判断是否是事件上报路径
76   - *
77   - * @param path 路径
78   - * @return 是否是事件上报路径
79   - */
80   - private boolean isEventPostPath(String path) {
81   - return StrUtil.contains(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic())
82   - && StrUtil.endWith(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic());
83   - }
84   -
85   - /**
86   - * 处理属性上报请求
87   - *
88   - * @param routingContext 路由上下文
89   - * @param productKey 产品 Key
90   - * @param deviceName 设备名称
91   - * @param body 请求体
92   - */
93   - private void handlePropertyPost(RoutingContext routingContext, String productKey, String deviceName,
94   - JsonObject body) {
95   - // 1.1 构建设备消息
96   - IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, protocol.getServerId())
97   -// .ofPropertyReport(parsePropertiesFromBody(body))
98   - ;
99   - // 1.2 发送消息
100   - deviceMessageProducer.sendDeviceMessage(message);
101   -
102   -// // 2. 返回响应
103   -// sendResponse(routingContext, null);
104   - }
105   -
106   - /**
107   - * 处理事件上报请求
108   - *
109   - * @param routingContext 路由上下文
110   - * @param productKey 产品 Key
111   - * @param deviceName 设备名称
112   - * @param identifier 事件标识符
113   - * @param body 请求体
114   - */
115   - private void handleEventPost(RoutingContext routingContext, String productKey, String deviceName,
116   - String identifier, JsonObject body) {
117   -// // 处理事件上报
118   -// IotDeviceEventReportReqDTO reportReqDTO = parseEventReportRequest(productKey, deviceName, identifier,
119   -// requestId, body);
120   -//
121   -// // 事件上报
122   -// CommonResult<Boolean> result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO);
123   - }
124   -
125   - // TODO @芋艿:这块在看看
126   - /**
127   - * 从请求体解析属性
128   - *
129   - * @param body 请求体
130   - * @return 属性映射
131   - */
132   - private Map<String, Object> parsePropertiesFromBody(JsonObject body) {
133   - Map<String, Object> properties = MapUtil.newHashMap();
134   - JsonObject params = body.getJsonObject("params");
135   - if (CollUtil.isEmpty(params)) {
136   - return properties;
137   - }
138   -
139   - // 将标准格式的 params 转换为平台需要的 properties 格式
140   - for (String key : params.fieldNames()) {
141   - Object valueObj = params.getValue(key);
142   - // 如果是复杂结构(包含 value 和 time)
143   - if (valueObj instanceof JsonObject) {
144   - JsonObject valueJson = (JsonObject) valueObj;
145   - properties.put(key, valueJson.containsKey("value") ? valueJson.getValue("value") : valueObj);
146   - } else {
147   - properties.put(key, valueObj);
148   - }
149   - }
150   - return properties;
151   - }
152   -
153   -// /**
154   -// * 解析事件上报请求
155   -// *
156   -// * @param productKey 产品 Key
157   -// * @param deviceName 设备名称
158   -// * @param identifier 事件标识符
159   -// * @param requestId 请求 ID
160   -// * @param body 请求体
161   -// * @return 事件上报请求 DTO
162   -// */
163   -// private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier,
164   -// String requestId, JsonObject body) {
165   -// // 解析参数
166   -// Map<String, Object> params = parseParamsFromBody(body);
167   -//
168   -// // 构建事件上报请求 DTO
169   -// return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO()
170   -// .setRequestId(requestId)
171   -// .setProcessId(IotNetComponentCommonUtils.getProcessId())
172   -// .setReportTime(LocalDateTime.now())
173   -// .setProductKey(productKey)
174   -// .setDeviceName(deviceName)).setIdentifier(identifier).setParams(params);
175   -// }
176   -
177   - /**
178   - * 从请求体解析参数
179   - *
180   - * @param body 请求体
181   - * @return 参数映射
182   - */
183   - private Map<String, Object> parseParamsFromBody(JsonObject body) {
184   - Map<String, Object> params = MapUtil.newHashMap();
185   - JsonObject paramsJson = body.getJsonObject("params");
186   - if (CollUtil.isEmpty(paramsJson)) {
187   - return params;
188   - }
189   -
190   - for (String key : paramsJson.fieldNames()) {
191   - params.put(key, paramsJson.getValue(key));
192   - }
193   - return params;
194   - }
195 58 }
196 59 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java
... ... @@ -96,7 +96,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
96 96 return null;
97 97 }
98 98  
99   - IotDeviceMessage message = IotDeviceMessage.of(null,
  99 + IotDeviceMessage message = IotDeviceMessage.requestOf(null,
100 100 IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), null);
101 101  
102 102 return appendDeviceMessage(message, deviceInfo, serverId);
... ... @@ -112,9 +112,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
112 112 return null;
113 113 }
114 114  
115   - IotDeviceMessage message = IotDeviceMessage.of(null,
116   - IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod(), null);
117   -
  115 + IotDeviceMessage message = IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod(),
  116 + null);
118 117 return appendDeviceMessage(message, deviceInfo, serverId);
119 118 }
120 119  
... ... @@ -122,23 +121,18 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
122 121 * 补充消息的后端字段
123 122 *
124 123 * @param message 消息
125   - * @param deviceInfo 设备信息
  124 + * @param device 设备信息
126 125 * @param serverId 设备连接的 serverId
127 126 * @return 消息
128 127 */
129 128 private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message,
130   - IotDeviceCacheService.DeviceInfo deviceInfo, String serverId) {
  129 + IotDeviceCacheService.DeviceInfo device, String serverId) {
131 130 message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())
132   - .setDeviceId(deviceInfo.getDeviceId()).setTenantId(deviceInfo.getTenantId()).setServerId(serverId);
133   -
  131 + .setDeviceId(device.getDeviceId()).setTenantId(device.getTenantId()).setServerId(serverId);
134 132 // 特殊:如果设备没有指定 requestId,则使用 messageId
135 133 if (StrUtil.isEmpty(message.getRequestId())) {
136 134 message.setRequestId(message.getId());
137 135 }
138   -
139   - log.debug("[appendDeviceMessage][消息字段补充完成][deviceId: {}][tenantId: {}]",
140   - deviceInfo.getDeviceId(), deviceInfo.getTenantId());
141   -
142 136 return message;
143 137 }
144 138  
... ...