Commit f5c2ee2ae5aa1840d130560ada903ae6b361d7b5

Authored by YunaiV
1 parent e9c5464a

feat:【IoT 物联网】设备消息查询时,增加 pair 查询

Showing 16 changed files with 214 additions and 25 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.http
... ... @@ -88,4 +88,14 @@ Authorization: Bearer {{token}}
88 88 "fileUrl": "http://example.com/firmware.bin",
89 89 "information": "{\"desc\":\"升级到最新版本\"}"
90 90 }
91   -}
92 91 \ No newline at end of file
  92 +}
  93 +
  94 +### 查询设备消息对分页 - 基础查询(设备编号25)
  95 +GET {{baseUrl}}/iot/device/message/pair-page?deviceId=25&pageNo=1&pageSize=10
  96 +Authorization: Bearer {{token}}
  97 +tenant-id: {{adminTenantId}}
  98 +
  99 +### 查询设备消息对分页 - 按标识符过滤(identifier=eat)
  100 +GET {{baseUrl}}/iot/device/message/pair-page?deviceId=25&identifier=eat&pageNo=1&pageSize=10
  101 +Authorization: Bearer {{token}}
  102 +tenant-id: {{adminTenantId}}
93 103 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.java
1 1 package cn.iocoder.yudao.module.iot.controller.admin.device;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespVO;
7 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespPairVO;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespVO;
8 10 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageSendReqVO;
9 11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
10 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;
11 15 import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
  16 +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
12 17 import io.swagger.v3.oas.annotations.Operation;
13 18 import io.swagger.v3.oas.annotations.tags.Tag;
14 19 import jakarta.annotation.Resource;
... ... @@ -17,7 +22,12 @@ import org.springframework.security.access.prepost.PreAuthorize;
17 22 import org.springframework.validation.annotation.Validated;
18 23 import org.springframework.web.bind.annotation.*;
19 24  
  25 +import java.util.List;
  26 +import java.util.Map;
  27 +
20 28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
21 31  
22 32 @Tag(name = "管理后台 - IoT 设备消息")
23 33 @RestController
... ... @@ -27,19 +37,54 @@ public class IotDeviceMessageController {
27 37  
28 38 @Resource
29 39 private IotDeviceMessageService deviceMessageService;
  40 + @Resource
  41 + private IotDeviceService deviceService;
  42 + @Resource
  43 + private IotThingModelService thingModelService;
  44 + @Resource
  45 + private IotDeviceMessageMapper deviceMessageMapper;
30 46  
31 47 @GetMapping("/page")
32 48 @Operation(summary = "获得设备消息分页")
33 49 @PreAuthorize("@ss.hasPermission('iot:device:message-query')")
34   - public CommonResult<PageResult<IotDeviceMessageRespVO>> getDeviceLogPage(@Valid IotDeviceMessagePageReqVO pageReqVO) {
  50 + public CommonResult<PageResult<IotDeviceMessageRespVO>> getDeviceMessagePage(
  51 + @Valid IotDeviceMessagePageReqVO pageReqVO) {
35 52 PageResult<IotDeviceMessageDO> pageResult = deviceMessageService.getDeviceMessagePage(pageReqVO);
36 53 return success(BeanUtils.toBean(pageResult, IotDeviceMessageRespVO.class));
37 54 }
38 55  
  56 + @GetMapping("/pair-page")
  57 + @Operation(summary = "获得设备消息对分页")
  58 + @PreAuthorize("@ss.hasPermission('iot:device:message-query')")
  59 + public CommonResult<PageResult<IotDeviceMessageRespPairVO>> getDeviceMessagePairPage(
  60 + @Valid IotDeviceMessagePageReqVO pageReqVO) {
  61 + // 1.1 先按照条件,查询 request 的消息(非 reply)
  62 + pageReqVO.setReply(false);
  63 + PageResult<IotDeviceMessageDO> requestMessagePageResult = deviceMessageService.getDeviceMessagePage(pageReqVO);
  64 + if (CollUtil.isEmpty(requestMessagePageResult.getList())) {
  65 + return success(PageResult.empty());
  66 + }
  67 + // 1.2 接着按照 requestIds,批量查询 reply 消息
  68 + List<String> requestIds = convertList(requestMessagePageResult.getList(), IotDeviceMessageDO::getRequestId);
  69 + List<IotDeviceMessageDO> replyMessageList = deviceMessageService.getDeviceMessageListByRequestIdsAndReply(
  70 + pageReqVO.getDeviceId(), requestIds, true);
  71 + Map<String, IotDeviceMessageDO> replyMessages = convertMap(replyMessageList, IotDeviceMessageDO::getRequestId);
  72 +
  73 + // 2. 组装结果
  74 + List<IotDeviceMessageRespPairVO> pairMessages = convertList(requestMessagePageResult.getList(),
  75 + requestMessage -> {
  76 + IotDeviceMessageDO replyMessage = replyMessages.get(requestMessage.getRequestId());
  77 + return new IotDeviceMessageRespPairVO()
  78 + .setRequest(BeanUtils.toBean(requestMessage, IotDeviceMessageRespVO.class))
  79 + .setReply(BeanUtils.toBean(replyMessage, IotDeviceMessageRespVO.class));
  80 + });
  81 + return success(new PageResult<>(pairMessages, requestMessagePageResult.getTotal()));
  82 + }
  83 +
39 84 @PostMapping("/send")
40 85 @Operation(summary = "发送消息", description = "可用于设备模拟")
41 86 @PreAuthorize("@ss.hasPermission('iot:device:message-end')")
42   - public CommonResult<Boolean> upstreamDevice(@Valid @RequestBody IotDeviceMessageSendReqVO sendReqVO) {
  87 + public CommonResult<Boolean> sendDeviceMessage(@Valid @RequestBody IotDeviceMessageSendReqVO sendReqVO) {
43 88 deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class));
44 89 return success(true);
45 90 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessagePageReqVO.java
... ... @@ -5,7 +5,13 @@ import cn.iocoder.yudao.framework.common.validation.InEnum;
5 5 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
6 6 import io.swagger.v3.oas.annotations.media.Schema;
7 7 import jakarta.validation.constraints.NotNull;
  8 +import jakarta.validation.constraints.Size;
8 9 import lombok.Data;
  10 +import org.springframework.format.annotation.DateTimeFormat;
  11 +
  12 +import java.time.LocalDateTime;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
9 15  
10 16 @Schema(description = "管理后台 - IoT 设备消息分页查询 Request VO")
11 17 @Data
... ... @@ -22,4 +28,15 @@ public class IotDeviceMessagePageReqVO extends PageParam {
22 28 @Schema(description = "是否上行", example = "true")
23 29 private Boolean upstream;
24 30  
  31 + @Schema(description = "是否回复", example = "true")
  32 + private Boolean reply;
  33 +
  34 + @Schema(description = "标识符", example = "temperature")
  35 + private String identifier;
  36 +
  37 + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
  38 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  39 + @Size(min = 2, max = 2, message = "请选择时间范围")
  40 + private LocalDateTime[] times;
  41 +
25 42 }
26 43 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespPairVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - IoT 设备消息对 Response VO")
  7 +@Data
  8 +public class IotDeviceMessageRespPairVO {
  9 +
  10 + @Schema(description = "请求消息", requiredMode = Schema.RequiredMode.REQUIRED)
  11 + private IotDeviceMessageRespVO request;
  12 +
  13 + @Schema(description = "响应消息")
  14 + private IotDeviceMessageRespVO reply; // 通过 requestId 配对
  15 +
  16 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespVO.java
... ... @@ -30,6 +30,9 @@ public class IotDeviceMessageRespVO {
30 30 @Schema(description = "是否回复消息", example = "false", examples = "true")
31 31 private Boolean reply;
32 32  
  33 + @Schema(description = "标识符", example = "temperature")
  34 + private String identifier;
  35 +
33 36 // ========== codec(编解码)字段 ==========
34 37  
35 38 @Schema(description = "请求编号", example = "req_123")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device;
2 2  
3 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.thingmodel.IotThingModelDO;
5 6 import lombok.AllArgsConstructor;
6 7 import lombok.Builder;
7 8 import lombok.Data;
... ... @@ -63,6 +64,13 @@ public class IotDeviceMessageDO {
63 64 * 计算并存储的目的:方便计算多少条请求、多少条回复
64 65 */
65 66 private Boolean reply;
  67 + /**
  68 + * 标识符
  69 + *
  70 + * 例如说:{@link IotThingModelDO#getIdentifier()}
  71 + * 目前,只有事件上报、服务调用才有!!!
  72 + */
  73 + private String identifier;
66 74  
67 75 // ========== codec(编解码)字段 ==========
68 76  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java
... ... @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelP
8 8 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
9 9 import org.apache.ibatis.annotations.Mapper;
10 10  
11   -import java.time.LocalDateTime;
  11 +import java.util.Collection;
12 12 import java.util.List;
13 13  
14 14 /**
... ... @@ -50,6 +50,12 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; {
50 50 return selectList(IotThingModelDO::getProductId, productId);
51 51 }
52 52  
  53 + default List<IotThingModelDO> selectListByProductIdAndIdentifiers(Long productId, Collection<String> identifiers) {
  54 + return selectList(new LambdaQueryWrapperX<IotThingModelDO>()
  55 + .eq(IotThingModelDO::getProductId, productId)
  56 + .in(IotThingModelDO::getIdentifier, identifiers));
  57 + }
  58 +
53 59 default List<IotThingModelDO> selectListByProductIdAndType(Long productId, Integer type) {
54 60 return selectList(IotThingModelDO::getProductId, productId,
55 61 IotThingModelDO::getType, type);
... ... @@ -69,16 +75,4 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; {
69 75 IotThingModelDO::getName, name);
70 76 }
71 77  
72   - // TODO @super:用不到,删除下;
73   - /**
74   - * 统计物模型数量
75   - *
76   - * @param createTime 创建时间,如果为空,则统计所有物模型数量
77   - * @return 物模型数量
78   - */
79   - default Long selectCountByCreateTime(LocalDateTime createTime) {
80   - return selectCount(new LambdaQueryWrapperX<IotThingModelDO>()
81   - .geIfPresent(IotThingModelDO::getCreateTime, createTime));
82   - }
83   -
84 78 }
85 79 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java
... ... @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9 import org.apache.ibatis.annotations.Param;
10 10  
  11 +import java.util.Collection;
11 12 import java.util.List;
12 13 import java.util.Map;
13 14  
... ... @@ -58,6 +59,18 @@ public interface IotDeviceMessageMapper {
58 59 Long selectCountByCreateTime(@Param("createTime") Long createTime);
59 60  
60 61 /**
  62 + * 按照 requestIds 批量查询消息
  63 + *
  64 + * @param deviceId 设备编号
  65 + * @param requestIds 请求编号集合
  66 + * @param reply 是否回复消息
  67 + * @return 消息列表
  68 + */
  69 + List<IotDeviceMessageDO> selectListByRequestIdsAndReply(@Param("deviceId") Long deviceId,
  70 + @Param("requestIds") Collection<String> requestIds,
  71 + @Param("reply") Boolean reply);
  72 +
  73 + /**
61 74 * 按照时间范围(小时),统计设备的消息数量
62 75 */
63 76 List<Map<String, Object>> selectDeviceMessageCountGroupByDate(@Param("startTime") Long startTime,
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java
... ... @@ -32,7 +32,7 @@ public interface IotDevicePropertyMapper {
32 32 List<TDengineTableField> oldFields,
33 33 List<TDengineTableField> newFields) {
34 34 oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(),
35   - TDengineTableField.FIELD_TS, "report_time"));
  35 + TDengineTableField.FIELD_TS, "report_time", "device_id"));
36 36 List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段
37 37 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField())))
38 38 .collect(Collectors.toList());
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java
... ... @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsD
7 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
9 9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
  10 +import jakarta.validation.constraints.NotEmpty;
  11 +import jakarta.validation.constraints.NotNull;
10 12  
11 13 import javax.annotation.Nullable;
12 14 import java.time.LocalDateTime;
... ... @@ -64,6 +66,19 @@ public interface IotDeviceMessageService {
64 66 PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO);
65 67  
66 68 /**
  69 + * 获得指定 requestId 的设备消息列表
  70 + *
  71 + * @param deviceId 设备编号
  72 + * @param requestIds requestId 列表
  73 + * @param reply 是否回复
  74 + * @return 设备消息列表
  75 + */
  76 + List<IotDeviceMessageDO> getDeviceMessageListByRequestIdsAndReply(
  77 + @NotNull(message = "设备编号不能为空") Long deviceId,
  78 + @NotEmpty(message = "请求编号不能为空") List<String> requestIds,
  79 + Boolean reply);
  80 +
  81 + /**
67 82 * 获得设备消息数量
68 83 *
69 84 * @param createTime 创建时间,如果为空,则统计所有消息数量
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
... ... @@ -76,7 +76,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
76 76 void createDeviceLogAsync(IotDeviceMessage message) {
77 77 IotDeviceMessageDO messageDO = BeanUtils.toBean(message, IotDeviceMessageDO.class)
78 78 .setUpstream(IotDeviceMessageUtils.isUpstreamMessage(message))
79   - .setReply(IotDeviceMessageUtils.isReplyMessage(message));
  79 + .setReply(IotDeviceMessageUtils.isReplyMessage(message))
  80 + .setIdentifier(IotDeviceMessageUtils.getIdentifier(message));
80 81 if (message.getParams() != null) {
81 82 messageDO.setParams(JsonUtils.toJsonString(messageDO.getParams()));
82 83 }
... ... @@ -213,6 +214,13 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
213 214 }
214 215  
215 216 @Override
  217 + public List<IotDeviceMessageDO> getDeviceMessageListByRequestIdsAndReply(Long deviceId,
  218 + List<String> requestIds,
  219 + Boolean reply) {
  220 + return deviceMessageMapper.selectListByRequestIdsAndReply(deviceId, requestIds, reply);
  221 + }
  222 +
  223 + @Override
216 224 public Long getDeviceMessageCount(LocalDateTime createTime) {
217 225 return deviceMessageMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null);
218 226 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelS
7 7 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
8 8 import jakarta.validation.Valid;
9 9  
  10 +import java.util.Collection;
10 11 import java.util.List;
11 12  
12 13 /**
... ... @@ -58,6 +59,15 @@ public interface IotThingModelService {
58 59 * 获得产品物模型列表
59 60 *
60 61 * @param productId 产品编号
  62 + * @param identifiers 功能标识列表
  63 + * @return 产品物模型列表
  64 + */
  65 + List<IotThingModelDO> getThingModelListByProductIdAndIdentifiers(Long productId, Collection<String> identifiers);
  66 +
  67 + /**
  68 + * 获得产品物模型列表
  69 + *
  70 + * @param productId 产品编号
61 71 * @param type 物模型类型
62 72 * @return 产品物模型列表
63 73 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
... ... @@ -132,6 +132,11 @@ public class IotThingModelServiceImpl implements IotThingModelService {
132 132 }
133 133  
134 134 @Override
  135 + public List<IotThingModelDO> getThingModelListByProductIdAndIdentifiers(Long productId, Collection<String> identifiers) {
  136 + return thingModelMapper.selectListByProductIdAndIdentifiers(productId, identifiers);
  137 + }
  138 +
  139 + @Override
135 140 public List<IotThingModelDO> getThingModelListByProductIdAndType(Long productId, Integer type) {
136 141 return thingModelMapper.selectListByProductIdAndType(productId, type);
137 142 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml
... ... @@ -13,6 +13,7 @@
13 13 server_id NCHAR(50),
14 14 upstream BOOL,
15 15 reply BOOL,
  16 + identifier NCHAR(100),
16 17 request_id NCHAR(50),
17 18 method NCHAR(100),
18 19 params NCHAR(2048),
... ... @@ -31,22 +32,22 @@
31 32 <insert id="insert">
32 33 INSERT INTO device_message_${deviceId} (
33 34 ts, id, report_time, tenant_id, server_id,
34   - upstream, reply, request_id, method, params,
35   - data, code, msg
  35 + upstream, reply, identifier, request_id, method,
  36 + params, data, code, msg
36 37 )
37 38 USING device_message
38 39 TAGS (#{deviceId})
39 40 VALUES (
40 41 NOW, #{id}, #{reportTime}, #{tenantId}, #{serverId},
41   - #{upstream}, #{reply}, #{requestId}, #{method}, #{params},
42   - #{data}, #{code}, #{msg}
  42 + #{upstream}, #{reply}, #{identifier}, #{requestId}, #{method},
  43 + #{params}, #{data}, #{code}, #{msg}
43 44 )
44 45 </insert>
45 46  
46 47 <select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO">
47 48 SELECT ts, id, report_time, tenant_id, server_id,
48   - upstream, reply, request_id, method, params,
49   - data, code, msg
  49 + upstream, reply, identifier, request_id, method,
  50 + params, data, code, msg
50 51 FROM device_message_${reqVO.deviceId}
51 52 <where>
52 53 <if test="reqVO.method != null and reqVO.method != ''">
... ... @@ -55,10 +56,29 @@
55 56 <if test="reqVO.upstream != null">
56 57 AND upstream = #{reqVO.upstream}
57 58 </if>
  59 + <if test="reqVO.reply != null">
  60 + AND reply = #{reqVO.reply}
  61 + </if>
  62 + <if test="reqVO.identifier != null and reqVO.identifier != ''">
  63 + AND identifier = #{reqVO.identifier}
  64 + </if>
58 65 </where>
59 66 ORDER BY ts DESC
60 67 </select>
61 68  
  69 + <select id="selectListByRequestIdsAndReply" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO">
  70 + SELECT ts, id, report_time, tenant_id, server_id,
  71 + upstream, reply, identifier, request_id, method,
  72 + params, data, code, msg
  73 + FROM device_message_${deviceId}
  74 + WHERE reply = #{reply}
  75 + AND request_id IN
  76 + <foreach collection="requestIds" item="requestId" open="(" close=")" separator=",">
  77 + #{requestId}
  78 + </foreach>
  79 + ORDER BY ts DESC
  80 + </select>
  81 +
62 82 <select id="selectCountByCreateTime" resultType="Long">
63 83 SELECT COUNT(*)
64 84 FROM device_message
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
... ... @@ -32,6 +32,10 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; {
32 32  
33 33 EVENT_POST("thing.event.post", "事件上报", true),
34 34  
  35 + // ========== 设备服务调用 ==========
  36 +
  37 + SERVICE_INVOKE("thing.service.invoke", "服务调用", false),
  38 +
35 39 ;
36 40  
37 41 public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod)
... ...
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 3 import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.map.MapUtil;
4 5 import cn.hutool.core.util.IdUtil;
  6 +import cn.hutool.core.util.StrUtil;
5 7 import cn.hutool.system.SystemUtil;
6 8 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
7 9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 10  
  11 +import java.util.Map;
  12 +
9 13 /**
10 14 * IoT 设备【消息】的工具类
11 15 *
... ... @@ -43,6 +47,23 @@ public class IotDeviceMessageUtils {
43 47 return message.getCode() != null;
44 48 }
45 49  
  50 + /**
  51 + * 提取消息中的标识符
  52 + *
  53 + * @param message 消息
  54 + * @return 标识符
  55 + */
  56 + @SuppressWarnings("unchecked")
  57 + public static String getIdentifier(IotDeviceMessage message) {
  58 + if (StrUtil.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),
  59 + message.getMethod(), IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod())
  60 + && message.getParams() != null) {
  61 + Map<String, Object> params = (Map<String, Object>) message.getParams();
  62 + return MapUtil.getStr(params, "identifier");
  63 + }
  64 + return null;
  65 + }
  66 +
46 67 // ========== Topic 相关 ==========
47 68  
48 69 public static String buildMessageBusGatewayDeviceMessageTopic(String serverId) {
... ...