Commit f5c2ee2ae5aa1840d130560ada903ae6b361d7b5
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,4 +88,14 @@ Authorization: Bearer {{token}} | ||
| 88 | "fileUrl": "http://example.com/firmware.bin", | 88 | "fileUrl": "http://example.com/firmware.bin", |
| 89 | "information": "{\"desc\":\"升级到最新版本\"}" | 89 | "information": "{\"desc\":\"升级到最新版本\"}" |
| 90 | } | 90 | } |
| 91 | -} | ||
| 92 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | package cn.iocoder.yudao.module.iot.controller.admin.device; | 1 | package cn.iocoder.yudao.module.iot.controller.admin.device; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespVO; | ||
| 7 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; | 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 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageSendReqVO; | 10 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageSendReqVO; |
| 9 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 11 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 10 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; | 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 | import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; | 15 | import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; |
| 16 | +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; | ||
| 12 | import io.swagger.v3.oas.annotations.Operation; | 17 | import io.swagger.v3.oas.annotations.Operation; |
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 18 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 14 | import jakarta.annotation.Resource; | 19 | import jakarta.annotation.Resource; |
| @@ -17,7 +22,12 @@ import org.springframework.security.access.prepost.PreAuthorize; | @@ -17,7 +22,12 @@ import org.springframework.security.access.prepost.PreAuthorize; | ||
| 17 | import org.springframework.validation.annotation.Validated; | 22 | import org.springframework.validation.annotation.Validated; |
| 18 | import org.springframework.web.bind.annotation.*; | 23 | import org.springframework.web.bind.annotation.*; |
| 19 | 24 | ||
| 25 | +import java.util.List; | ||
| 26 | +import java.util.Map; | ||
| 27 | + | ||
| 20 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 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 | @Tag(name = "管理后台 - IoT 设备消息") | 32 | @Tag(name = "管理后台 - IoT 设备消息") |
| 23 | @RestController | 33 | @RestController |
| @@ -27,19 +37,54 @@ public class IotDeviceMessageController { | @@ -27,19 +37,54 @@ public class IotDeviceMessageController { | ||
| 27 | 37 | ||
| 28 | @Resource | 38 | @Resource |
| 29 | private IotDeviceMessageService deviceMessageService; | 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 | @GetMapping("/page") | 47 | @GetMapping("/page") |
| 32 | @Operation(summary = "获得设备消息分页") | 48 | @Operation(summary = "获得设备消息分页") |
| 33 | @PreAuthorize("@ss.hasPermission('iot:device:message-query')") | 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 | PageResult<IotDeviceMessageDO> pageResult = deviceMessageService.getDeviceMessagePage(pageReqVO); | 52 | PageResult<IotDeviceMessageDO> pageResult = deviceMessageService.getDeviceMessagePage(pageReqVO); |
| 36 | return success(BeanUtils.toBean(pageResult, IotDeviceMessageRespVO.class)); | 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 | @PostMapping("/send") | 84 | @PostMapping("/send") |
| 40 | @Operation(summary = "发送消息", description = "可用于设备模拟") | 85 | @Operation(summary = "发送消息", description = "可用于设备模拟") |
| 41 | @PreAuthorize("@ss.hasPermission('iot:device:message-end')") | 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 | deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class)); | 88 | deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class)); |
| 44 | return success(true); | 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,7 +5,13 @@ import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 5 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 6 | import io.swagger.v3.oas.annotations.media.Schema; | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 7 | import jakarta.validation.constraints.NotNull; | 7 | import jakarta.validation.constraints.NotNull; |
| 8 | +import jakarta.validation.constraints.Size; | ||
| 8 | import lombok.Data; | 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 | @Schema(description = "管理后台 - IoT 设备消息分页查询 Request VO") | 16 | @Schema(description = "管理后台 - IoT 设备消息分页查询 Request VO") |
| 11 | @Data | 17 | @Data |
| @@ -22,4 +28,15 @@ public class IotDeviceMessagePageReqVO extends PageParam { | @@ -22,4 +28,15 @@ public class IotDeviceMessagePageReqVO extends PageParam { | ||
| 22 | @Schema(description = "是否上行", example = "true") | 28 | @Schema(description = "是否上行", example = "true") |
| 23 | private Boolean upstream; | 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 | \ No newline at end of file | 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,6 +30,9 @@ public class IotDeviceMessageRespVO { | ||
| 30 | @Schema(description = "是否回复消息", example = "false", examples = "true") | 30 | @Schema(description = "是否回复消息", example = "false", examples = "true") |
| 31 | private Boolean reply; | 31 | private Boolean reply; |
| 32 | 32 | ||
| 33 | + @Schema(description = "标识符", example = "temperature") | ||
| 34 | + private String identifier; | ||
| 35 | + | ||
| 33 | // ========== codec(编解码)字段 ========== | 36 | // ========== codec(编解码)字段 ========== |
| 34 | 37 | ||
| 35 | @Schema(description = "请求编号", example = "req_123") | 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,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 3 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 5 | import lombok.AllArgsConstructor; | 6 | import lombok.AllArgsConstructor; |
| 6 | import lombok.Builder; | 7 | import lombok.Builder; |
| 7 | import lombok.Data; | 8 | import lombok.Data; |
| @@ -63,6 +64,13 @@ public class IotDeviceMessageDO { | @@ -63,6 +64,13 @@ public class IotDeviceMessageDO { | ||
| 63 | * 计算并存储的目的:方便计算多少条请求、多少条回复 | 64 | * 计算并存储的目的:方便计算多少条请求、多少条回复 |
| 64 | */ | 65 | */ |
| 65 | private Boolean reply; | 66 | private Boolean reply; |
| 67 | + /** | ||
| 68 | + * 标识符 | ||
| 69 | + * | ||
| 70 | + * 例如说:{@link IotThingModelDO#getIdentifier()} | ||
| 71 | + * 目前,只有事件上报、服务调用才有!!! | ||
| 72 | + */ | ||
| 73 | + private String identifier; | ||
| 66 | 74 | ||
| 67 | // ========== codec(编解码)字段 ========== | 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,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelP | ||
| 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; |
| 9 | import org.apache.ibatis.annotations.Mapper; | 9 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 10 | ||
| 11 | -import java.time.LocalDateTime; | 11 | +import java.util.Collection; |
| 12 | import java.util.List; | 12 | import java.util.List; |
| 13 | 13 | ||
| 14 | /** | 14 | /** |
| @@ -50,6 +50,12 @@ public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> { | @@ -50,6 +50,12 @@ public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> { | ||
| 50 | return selectList(IotThingModelDO::getProductId, productId); | 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 | default List<IotThingModelDO> selectListByProductIdAndType(Long productId, Integer type) { | 59 | default List<IotThingModelDO> selectListByProductIdAndType(Long productId, Integer type) { |
| 54 | return selectList(IotThingModelDO::getProductId, productId, | 60 | return selectList(IotThingModelDO::getProductId, productId, |
| 55 | IotThingModelDO::getType, type); | 61 | IotThingModelDO::getType, type); |
| @@ -69,16 +75,4 @@ public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> { | @@ -69,16 +75,4 @@ public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> { | ||
| 69 | IotThingModelDO::getName, name); | 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 | \ No newline at end of file | 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,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 9 | import org.apache.ibatis.annotations.Param; | 9 | import org.apache.ibatis.annotations.Param; |
| 10 | 10 | ||
| 11 | +import java.util.Collection; | ||
| 11 | import java.util.List; | 12 | import java.util.List; |
| 12 | import java.util.Map; | 13 | import java.util.Map; |
| 13 | 14 | ||
| @@ -58,6 +59,18 @@ public interface IotDeviceMessageMapper { | @@ -58,6 +59,18 @@ public interface IotDeviceMessageMapper { | ||
| 58 | Long selectCountByCreateTime(@Param("createTime") Long createTime); | 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 | List<Map<String, Object>> selectDeviceMessageCountGroupByDate(@Param("startTime") Long startTime, | 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,7 +32,7 @@ public interface IotDevicePropertyMapper { | ||
| 32 | List<TDengineTableField> oldFields, | 32 | List<TDengineTableField> oldFields, |
| 33 | List<TDengineTableField> newFields) { | 33 | List<TDengineTableField> newFields) { |
| 34 | oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), | 34 | oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), |
| 35 | - TDengineTableField.FIELD_TS, "report_time")); | 35 | + TDengineTableField.FIELD_TS, "report_time", "device_id")); |
| 36 | List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段 | 36 | List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段 |
| 37 | newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) | 37 | newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) |
| 38 | .collect(Collectors.toList()); | 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,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsD | ||
| 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; | 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 | import javax.annotation.Nullable; | 13 | import javax.annotation.Nullable; |
| 12 | import java.time.LocalDateTime; | 14 | import java.time.LocalDateTime; |
| @@ -64,6 +66,19 @@ public interface IotDeviceMessageService { | @@ -64,6 +66,19 @@ public interface IotDeviceMessageService { | ||
| 64 | PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO); | 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 | * @param createTime 创建时间,如果为空,则统计所有消息数量 | 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,7 +76,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 76 | void createDeviceLogAsync(IotDeviceMessage message) { | 76 | void createDeviceLogAsync(IotDeviceMessage message) { |
| 77 | IotDeviceMessageDO messageDO = BeanUtils.toBean(message, IotDeviceMessageDO.class) | 77 | IotDeviceMessageDO messageDO = BeanUtils.toBean(message, IotDeviceMessageDO.class) |
| 78 | .setUpstream(IotDeviceMessageUtils.isUpstreamMessage(message)) | 78 | .setUpstream(IotDeviceMessageUtils.isUpstreamMessage(message)) |
| 79 | - .setReply(IotDeviceMessageUtils.isReplyMessage(message)); | 79 | + .setReply(IotDeviceMessageUtils.isReplyMessage(message)) |
| 80 | + .setIdentifier(IotDeviceMessageUtils.getIdentifier(message)); | ||
| 80 | if (message.getParams() != null) { | 81 | if (message.getParams() != null) { |
| 81 | messageDO.setParams(JsonUtils.toJsonString(messageDO.getParams())); | 82 | messageDO.setParams(JsonUtils.toJsonString(messageDO.getParams())); |
| 82 | } | 83 | } |
| @@ -213,6 +214,13 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -213,6 +214,13 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 213 | } | 214 | } |
| 214 | 215 | ||
| 215 | @Override | 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 | public Long getDeviceMessageCount(LocalDateTime createTime) { | 224 | public Long getDeviceMessageCount(LocalDateTime createTime) { |
| 217 | return deviceMessageMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); | 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,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelS | ||
| 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; |
| 8 | import jakarta.validation.Valid; | 8 | import jakarta.validation.Valid; |
| 9 | 9 | ||
| 10 | +import java.util.Collection; | ||
| 10 | import java.util.List; | 11 | import java.util.List; |
| 11 | 12 | ||
| 12 | /** | 13 | /** |
| @@ -58,6 +59,15 @@ public interface IotThingModelService { | @@ -58,6 +59,15 @@ public interface IotThingModelService { | ||
| 58 | * 获得产品物模型列表 | 59 | * 获得产品物模型列表 |
| 59 | * | 60 | * |
| 60 | * @param productId 产品编号 | 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 | * @param type 物模型类型 | 71 | * @param type 物模型类型 |
| 62 | * @return 产品物模型列表 | 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,6 +132,11 @@ public class IotThingModelServiceImpl implements IotThingModelService { | ||
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | @Override | 134 | @Override |
| 135 | + public List<IotThingModelDO> getThingModelListByProductIdAndIdentifiers(Long productId, Collection<String> identifiers) { | ||
| 136 | + return thingModelMapper.selectListByProductIdAndIdentifiers(productId, identifiers); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + @Override | ||
| 135 | public List<IotThingModelDO> getThingModelListByProductIdAndType(Long productId, Integer type) { | 140 | public List<IotThingModelDO> getThingModelListByProductIdAndType(Long productId, Integer type) { |
| 136 | return thingModelMapper.selectListByProductIdAndType(productId, type); | 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,6 +13,7 @@ | ||
| 13 | server_id NCHAR(50), | 13 | server_id NCHAR(50), |
| 14 | upstream BOOL, | 14 | upstream BOOL, |
| 15 | reply BOOL, | 15 | reply BOOL, |
| 16 | + identifier NCHAR(100), | ||
| 16 | request_id NCHAR(50), | 17 | request_id NCHAR(50), |
| 17 | method NCHAR(100), | 18 | method NCHAR(100), |
| 18 | params NCHAR(2048), | 19 | params NCHAR(2048), |
| @@ -31,22 +32,22 @@ | @@ -31,22 +32,22 @@ | ||
| 31 | <insert id="insert"> | 32 | <insert id="insert"> |
| 32 | INSERT INTO device_message_${deviceId} ( | 33 | INSERT INTO device_message_${deviceId} ( |
| 33 | ts, id, report_time, tenant_id, server_id, | 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 | USING device_message | 38 | USING device_message |
| 38 | TAGS (#{deviceId}) | 39 | TAGS (#{deviceId}) |
| 39 | VALUES ( | 40 | VALUES ( |
| 40 | NOW, #{id}, #{reportTime}, #{tenantId}, #{serverId}, | 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 | </insert> | 45 | </insert> |
| 45 | 46 | ||
| 46 | <select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO"> | 47 | <select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO"> |
| 47 | SELECT ts, id, report_time, tenant_id, server_id, | 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 | FROM device_message_${reqVO.deviceId} | 51 | FROM device_message_${reqVO.deviceId} |
| 51 | <where> | 52 | <where> |
| 52 | <if test="reqVO.method != null and reqVO.method != ''"> | 53 | <if test="reqVO.method != null and reqVO.method != ''"> |
| @@ -55,10 +56,29 @@ | @@ -55,10 +56,29 @@ | ||
| 55 | <if test="reqVO.upstream != null"> | 56 | <if test="reqVO.upstream != null"> |
| 56 | AND upstream = #{reqVO.upstream} | 57 | AND upstream = #{reqVO.upstream} |
| 57 | </if> | 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 | </where> | 65 | </where> |
| 59 | ORDER BY ts DESC | 66 | ORDER BY ts DESC |
| 60 | </select> | 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 | <select id="selectCountByCreateTime" resultType="Long"> | 82 | <select id="selectCountByCreateTime" resultType="Long"> |
| 63 | SELECT COUNT(*) | 83 | SELECT COUNT(*) |
| 64 | FROM device_message | 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<String> { | @@ -32,6 +32,10 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | ||
| 32 | 32 | ||
| 33 | EVENT_POST("thing.event.post", "事件上报", true), | 33 | EVENT_POST("thing.event.post", "事件上报", true), |
| 34 | 34 | ||
| 35 | + // ========== 设备服务调用 ========== | ||
| 36 | + | ||
| 37 | + SERVICE_INVOKE("thing.service.invoke", "服务调用", false), | ||
| 38 | + | ||
| 35 | ; | 39 | ; |
| 36 | 40 | ||
| 37 | public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod) | 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 | package cn.iocoder.yudao.module.iot.core.util; | 1 | package cn.iocoder.yudao.module.iot.core.util; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 4 | +import cn.hutool.core.map.MapUtil; | ||
| 4 | import cn.hutool.core.util.IdUtil; | 5 | import cn.hutool.core.util.IdUtil; |
| 6 | +import cn.hutool.core.util.StrUtil; | ||
| 5 | import cn.hutool.system.SystemUtil; | 7 | import cn.hutool.system.SystemUtil; |
| 6 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 8 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 9 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 8 | 10 | ||
| 11 | +import java.util.Map; | ||
| 12 | + | ||
| 9 | /** | 13 | /** |
| 10 | * IoT 设备【消息】的工具类 | 14 | * IoT 设备【消息】的工具类 |
| 11 | * | 15 | * |
| @@ -43,6 +47,23 @@ public class IotDeviceMessageUtils { | @@ -43,6 +47,23 @@ public class IotDeviceMessageUtils { | ||
| 43 | return message.getCode() != null; | 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 | // ========== Topic 相关 ========== | 67 | // ========== Topic 相关 ========== |
| 47 | 68 | ||
| 48 | public static String buildMessageBusGatewayDeviceMessageTopic(String serverId) { | 69 | public static String buildMessageBusGatewayDeviceMessageTopic(String serverId) { |