Commit b45406989702c20f5221b007646ea5b0dadf1038

Authored by YunaiV
1 parent 7670ac19

【代码新增】IoT:实现 device 下行服务调用的逻辑

Showing 17 changed files with 289 additions and 36 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java
1 package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; 1 package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream;
2 2
  3 +import jakarta.validation.constraints.NotEmpty;
3 import lombok.Data; 4 import lombok.Data;
4 5
  6 +import java.util.Map;
  7 +
5 /** 8 /**
6 * IoT 设备【服务】调用 Request DTO 9 * IoT 设备【服务】调用 Request DTO
7 * 10 *
@@ -9,4 +12,15 @@ import lombok.Data; @@ -9,4 +12,15 @@ import lombok.Data;
9 */ 12 */
10 @Data 13 @Data
11 public class IotDeviceServiceInvokeReqDTO extends IotDeviceDownstreamAbstractReqDTO { 14 public class IotDeviceServiceInvokeReqDTO extends IotDeviceDownstreamAbstractReqDTO {
  15 +
  16 + /**
  17 + * 服务标识
  18 + */
  19 + @NotEmpty(message = "服务标识不能为空")
  20 + private String identifier;
  21 + /**
  22 + * 调用参数
  23 + */
  24 + private Map<String, Object> params;
  25 +
12 } 26 }
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java
@@ -19,7 +19,6 @@ public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO @@ -19,7 +19,6 @@ public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO
19 /** 19 /**
20 * 事件参数 20 * 事件参数
21 */ 21 */
22 - @NotEmpty(message = "事件参数不能为空")  
23 private Map<String, Object> params; 22 private Map<String, Object> params;
24 23
25 } 24 }
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
@@ -30,6 +30,7 @@ public interface ErrorCodeConstants { @@ -30,6 +30,7 @@ public interface ErrorCodeConstants {
30 ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); 30 ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在");
31 ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); 31 ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备");
32 ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!"); 32 ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!");
  33 + ErrorCode DEVICE_DOWNSTREAM_FAILED = new ErrorCode(1_050_003_007, "执行失败,原因:{}");
33 34
34 // ========== 产品分类 1-050-004-000 ========== 35 // ========== 产品分类 1-050-004-000 ==========
35 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); 36 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在");
@@ -46,6 +47,6 @@ public interface ErrorCodeConstants { @@ -46,6 +47,6 @@ public interface ErrorCodeConstants {
46 ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效"); 47 ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效");
47 48
48 // ========== 插件实例 1-050-007-000 ========== 49 // ========== 插件实例 1-050-007-000 ==========
49 - ErrorCode PLUGIN_INSTANCE_NOT_EXISTS = new ErrorCode(1_050_007_000, "插件实例不存在"); 50 +
50 51
51 } 52 }
52 \ No newline at end of file 53 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http 0 → 100644
  1 +### 请求 /iot/device/simulation-downstream 接口 => 成功
  2 +POST {{baseUrl}}/iot/device/simulation-downstream
  3 +Content-Type: application/json
  4 +tenant-id: {{adminTenentId}}
  5 +Authorization: Bearer {{token}}
  6 +
  7 +{
  8 + "id": 25,
  9 + "type": "service",
  10 + "identifier": "temperature",
  11 + "data": {
  12 + "xx": "yy"
  13 + }
  14 +}
0 \ No newline at end of file 15 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO;
9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 10 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
10 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; 11 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO;
11 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 12 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
@@ -171,7 +172,7 @@ public class IotDeviceController { @@ -171,7 +172,7 @@ public class IotDeviceController {
171 @Operation(summary = "模拟设备下行") 172 @Operation(summary = "模拟设备下行")
172 @PreAuthorize("@ss.hasPermission('iot:device:simulation')") 173 @PreAuthorize("@ss.hasPermission('iot:device:simulation')")
173 public CommonResult<Boolean> simulationDownstreamDevice( 174 public CommonResult<Boolean> simulationDownstreamDevice(
174 - @Valid @RequestBody IotDeviceSimulationUpstreamReqVO downstreamReqVO) { 175 + @Valid @RequestBody IotDeviceSimulationDownstreamReqVO downstreamReqVO) {
175 deviceDownstreamService.simulationDeviceDownstream(downstreamReqVO); 176 deviceDownstreamService.simulationDeviceDownstream(downstreamReqVO);
176 return success(true); 177 return success(true);
177 } 178 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java
@@ -75,6 +75,12 @@ public class IotDeviceLogDO { @@ -75,6 +75,12 @@ public class IotDeviceLogDO {
75 * 存储具体的消息数据内容,通常是 JSON 格式 75 * 存储具体的消息数据内容,通常是 JSON 格式
76 */ 76 */
77 private String content; 77 private String content;
  78 + /**
  79 + * 响应码
  80 + *
  81 + * 目前只有 server 下行消息给 device 设备时,才会有响应码
  82 + */
  83 + private Integer code;
78 84
79 /** 85 /**
80 * 上报时间戳 86 * 上报时间戳
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java
@@ -55,12 +55,16 @@ public class IotDeviceMessage { @@ -55,12 +55,16 @@ public class IotDeviceMessage {
55 * 例如说:属性上报的 properties、事件上报的 params 55 * 例如说:属性上报的 properties、事件上报的 params
56 */ 56 */
57 private Object data; 57 private Object data;
  58 + /**
  59 + * 响应码
  60 + *
  61 + * 目前只有 server 下行消息给 device 设备时,才会有响应码
  62 + */
  63 + private Integer code;
58 64
59 /** 65 /**
60 * 上报时间 66 * 上报时间
61 */ 67 */
62 private LocalDateTime reportTime; 68 private LocalDateTime reportTime;
63 69
64 - // TODO @芋艿 code;  
65 -  
66 } 70 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java
1 package cn.iocoder.yudao.module.iot.service.device.control; 1 package cn.iocoder.yudao.module.iot.service.device.control;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; 3 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO;
4 import jakarta.validation.Valid; 4 import jakarta.validation.Valid;
5 5
6 /** 6 /**
@@ -17,6 +17,6 @@ public interface IotDeviceDownstreamService { @@ -17,6 +17,6 @@ public interface IotDeviceDownstreamService {
17 * 17 *
18 * @param downstreamReqVO 设备下行请求 VO 18 * @param downstreamReqVO 设备下行请求 VO
19 */ 19 */
20 - void simulationDeviceDownstream(@Valid IotDeviceSimulationUpstreamReqVO downstreamReqVO); 20 + void simulationDeviceDownstream(@Valid IotDeviceSimulationDownstreamReqVO downstreamReqVO);
21 21
22 } 22 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.device.control; 1 package cn.iocoder.yudao.module.iot.service.device.control;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; 3 +import cn.hutool.core.exceptions.ExceptionUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.IdUtil;
  6 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
  7 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  8 +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceDownstreamAbstractReqDTO;
  9 +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO;
  10 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 11 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  12 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO;
5 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; 13 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
6 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; 14 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  15 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  16 +import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer;
7 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 17 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  18 +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService;
8 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
9 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.http.ResponseEntity;
10 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
11 import org.springframework.validation.annotation.Validated; 23 import org.springframework.validation.annotation.Validated;
  24 +import org.springframework.web.client.RestTemplate;
12 25
  26 +import java.time.LocalDateTime;
  27 +import java.util.Map;
13 import java.util.Objects; 28 import java.util.Objects;
14 29
  30 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
  31 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  32 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED;
  33 +
15 /** 34 /**
16 * 设备下行 Service 实现类 35 * 设备下行 Service 实现类
17 * 36 *
@@ -24,9 +43,17 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -24,9 +43,17 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
24 43
25 @Resource 44 @Resource
26 private IotDeviceService deviceService; 45 private IotDeviceService deviceService;
  46 + @Resource
  47 + private IotPluginInstanceService pluginInstanceService;
  48 +
  49 + @Resource
  50 + private RestTemplate restTemplate;
  51 +
  52 + @Resource
  53 + private IotDeviceProducer deviceProducer;
27 54
28 @Override 55 @Override
29 - public void simulationDeviceDownstream(IotDeviceSimulationUpstreamReqVO downstreamReqVO) { 56 + public void simulationDeviceDownstream(IotDeviceSimulationDownstreamReqVO downstreamReqVO) {
30 // 校验设备是否存在 57 // 校验设备是否存在
31 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); 58 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId());
32 // TODO 芋艿:父设备的处理 59 // TODO 芋艿:父设备的处理
@@ -40,12 +67,14 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -40,12 +67,14 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
40 // 属性相关 67 // 属性相关
41 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) 68 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType()))
42 // 属性设置 69 // 属性设置
43 - if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { 70 + if (Objects.equals(downstreamReqVO.getIdentifier(),
  71 + IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) {
44 setDeviceProperty(downstreamReqVO, device, parentDevice); 72 setDeviceProperty(downstreamReqVO, device, parentDevice);
45 return; 73 return;
46 } 74 }
47 // 属性设置 75 // 属性设置
48 - if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { 76 + if (Objects.equals(downstreamReqVO.getIdentifier(),
  77 + IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) {
49 getDeviceProperty(downstreamReqVO, device, parentDevice); 78 getDeviceProperty(downstreamReqVO, device, parentDevice);
50 return; 79 return;
51 } 80 }
@@ -53,22 +82,121 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -53,22 +82,121 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
53 // TODO 芋艿:配置下发 82 // TODO 芋艿:配置下发
54 } 83 }
55 84
56 - private void invokeDeviceService(IotDeviceSimulationUpstreamReqVO downstreamReqVO,  
57 - IotDeviceDO device, IotDeviceDO parentDevice) {  
58 - // 校验服务是否存在  
59 - // TODO 芋艿:这里需要校验服务是否存在  
60 - // 调用服务  
61 - // TODO 芋艿:这里需要调用服务 85 + /**
  86 + * 调用设备服务
  87 + *
  88 + * @param downstreamReqVO 下行请求
  89 + * @param device 设备
  90 + * @param parentDevice 父设备
  91 + */
  92 + @SuppressWarnings("unchecked")
  93 + private void invokeDeviceService(IotDeviceSimulationDownstreamReqVO downstreamReqVO,
  94 + IotDeviceDO device, IotDeviceDO parentDevice) {
  95 + // 1. 参数校验
  96 + if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
  97 + throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型");
  98 + }
  99 +
  100 + // 2. 发送请求
  101 + String url = String.format( "sys/%s/%s/thing/service/%s",
  102 + getProductKey(device, parentDevice), getDeviceName(device, parentDevice), downstreamReqVO.getIdentifier());
  103 + IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO()
  104 + .setParams((Map<String, Object>) downstreamReqVO.getData());
  105 + CommonResult<Boolean> result = requestPlugin(url, reqDTO, device);
  106 +
  107 + // 3. 发送设备消息
  108 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId())
  109 + .setType(IotDeviceMessageTypeEnum.SERVICE.getType()).setIdentifier(reqDTO.getIdentifier())
  110 + .setData(reqDTO.getParams());
  111 + sendDeviceMessage(message, device, result.getCode());
  112 +
  113 + // 4. 如果不成功,抛出异常,提示用户
  114 + if (result.isError()) {
  115 + log.error("[invokeDeviceService][设备({})服务调用失败,请求参数:({}),响应结果:({})]",
  116 + device.getDeviceKey(), reqDTO, result);
  117 + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
  118 + }
62 } 119 }
63 120
64 - private void setDeviceProperty(IotDeviceSimulationUpstreamReqVO downstreamReqVO,  
65 - IotDeviceDO device, IotDeviceDO parentDevice) {  
66 - // TODO 芋艿:这里需要设置设备属性 121 + /**
  122 + * 设置设备属性
  123 + *
  124 + * @param downstreamReqVO 下行请求
  125 + * @param device 设备
  126 + * @param parentDevice 父设备
  127 + */
  128 + private void setDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO,
  129 + IotDeviceDO device, IotDeviceDO parentDevice) {
  130 + // TODO 1. 请求
  131 +
  132 + // TODO 2. 发送消息
67 } 133 }
68 134
69 - private void getDeviceProperty(IotDeviceSimulationUpstreamReqVO downstreamReqVO,  
70 - IotDeviceDO device, IotDeviceDO parentDevice) { 135 + private void getDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO,
  136 + IotDeviceDO device, IotDeviceDO parentDevice) {
71 // TODO 芋艿:这里需要获取设备属性 137 // TODO 芋艿:这里需要获取设备属性
72 } 138 }
73 139
  140 + /**
  141 + * 请求插件
  142 + *
  143 + * @param url URL
  144 + * @param reqDTO 请求参数,只需要设置子类的参数!
  145 + * @param device 设备
  146 + * @return 响应结果
  147 + */
  148 + @SuppressWarnings({"unchecked", "HttpUrlsUsage"})
  149 + private CommonResult<Boolean> requestPlugin(String url, IotDeviceDownstreamAbstractReqDTO reqDTO, IotDeviceDO device) {
  150 + // 获得设备对应的插件实例
  151 + IotPluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstanceByDeviceKey(device.getDeviceKey());
  152 + if (pluginInstance == null) {
  153 + throw exception(DEVICE_DOWNSTREAM_FAILED, "设备找不到对应的插件实例");
  154 + }
  155 +
  156 + // 补充通用参数
  157 + reqDTO.setRequestId(IdUtil.fastSimpleUUID());
  158 +
  159 + // 执行请求
  160 + ResponseEntity<CommonResult<Boolean>> responseEntity;
  161 + try {
  162 + responseEntity = restTemplate.postForEntity(
  163 + String.format("http://%s:%d/%s", pluginInstance.getHostIp(), pluginInstance.getDownstreamPort(), url),
  164 + reqDTO, (Class<CommonResult<Boolean>>) (Class<?>) CommonResult.class);
  165 + Assert.isTrue(responseEntity.getStatusCode().is2xxSuccessful(),
  166 + "HTTP 状态码不是 2xx,而是" + responseEntity.getStatusCode());
  167 + Assert.notNull(responseEntity.getBody(), "响应结果不能为空");
  168 + } catch (Exception ex) {
  169 + log.error("[requestPlugin][设备({}) url({}) 下行消息失败,请求参数({})]", device.getDeviceKey(), url, reqDTO, ex);
  170 + throw exception(DEVICE_DOWNSTREAM_FAILED, ExceptionUtil.getMessage(ex));
  171 + }
  172 + return responseEntity.getBody();
  173 + }
  174 +
  175 + private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) {
  176 + // 1. 完善消息
  177 + message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())
  178 + .setDeviceKey(device.getDeviceKey());
  179 + Assert.notNull(message.getRequestId(), "requestId 不能为空");
  180 + if (message.getReportTime() == null) {
  181 + message.setReportTime(LocalDateTime.now());
  182 + }
  183 + message.setCode(code);
  184 +
  185 + // 2. 发送消息
  186 + try {
  187 + deviceProducer.sendDeviceMessage(message);
  188 + log.info("[sendDeviceMessage][message({}) 发送消息成功]", message);
  189 + } catch (Exception e) {
  190 + log.error("[sendDeviceMessage][message({}) 发送消息失败]", message, e);
  191 + }
  192 + }
  193 +
  194 + private String getDeviceName(IotDeviceDO device, IotDeviceDO parentDevice) {
  195 + return parentDevice != null ? parentDevice.getDeviceName() : device.getDeviceName();
  196 + }
  197 +
  198 + private String getProductKey(IotDeviceDO device, IotDeviceDO parentDevice) {
  199 + return parentDevice != null ? parentDevice.getProductKey() : device.getProductKey();
  200 + }
  201 +
74 } 202 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.plugin; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.plugin;
2 2
3 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; 3 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO;
5 import org.springframework.web.multipart.MultipartFile; 6 import org.springframework.web.multipart.MultipartFile;
6 7
7 import java.time.LocalDateTime; 8 import java.time.LocalDateTime;
@@ -67,4 +68,12 @@ public interface IotPluginInstanceService { @@ -67,4 +68,12 @@ public interface IotPluginInstanceService {
67 */ 68 */
68 void updateDevicePluginInstanceProcessIdAsync(String deviceKey, String processId); 69 void updateDevicePluginInstanceProcessIdAsync(String deviceKey, String processId);
69 70
  71 + /**
  72 + * 获得设备对应的插件实例
  73 + *
  74 + * @param deviceKey 设备 Key
  75 + * @return 插件实例
  76 + */
  77 + IotPluginInstanceDO getPluginInstanceByDeviceKey(String deviceKey);
  78 +
70 } 79 }
71 \ No newline at end of file 80 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.plugin; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.plugin;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.io.FileUtil; 4 import cn.hutool.core.io.FileUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; 6 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO;
@@ -203,4 +204,13 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { @@ -203,4 +204,13 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService {
203 devicePluginProcessIdRedisDAO.put(deviceKey, processId); 204 devicePluginProcessIdRedisDAO.put(deviceKey, processId);
204 } 205 }
205 206
  207 + @Override
  208 + public IotPluginInstanceDO getPluginInstanceByDeviceKey(String deviceKey) {
  209 + String processId = devicePluginProcessIdRedisDAO.get(deviceKey);
  210 + if (StrUtil.isEmpty(processId)) {
  211 + return null;
  212 + }
  213 + return pluginInstanceMapper.selectByProcessId(processId);
  214 + }
  215 +
206 } 216 }
207 \ No newline at end of file 217 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 type NCHAR(50), 13 type NCHAR(50),
14 identifier NCHAR(255), 14 identifier NCHAR(255),
15 content NCHAR(1024), 15 content NCHAR(1024),
  16 + code INT,
16 report_time TIMESTAMP 17 report_time TIMESTAMP
17 ) TAGS ( 18 ) TAGS (
18 device_key NCHAR(50) 19 device_key NCHAR(50)
@@ -24,7 +25,7 @@ @@ -24,7 +25,7 @@
24 </select> 25 </select>
25 26
26 <insert id="insert"> 27 <insert id="insert">
27 - INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, report_time) 28 + INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, code, report_time)
28 USING device_log 29 USING device_log
29 TAGS ('${deviceKey}') 30 TAGS ('${deviceKey}')
30 VALUES ( 31 VALUES (
@@ -35,6 +36,7 @@ @@ -35,6 +36,7 @@
35 #{type}, 36 #{type},
36 #{identifier}, 37 #{identifier},
37 #{content}, 38 #{content},
  39 + #{code},
38 #{reportTime} 40 #{reportTime}
39 ) 41 )
40 </insert> 42 </insert>
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java
@@ -14,10 +14,28 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceSe @@ -14,10 +14,28 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceSe
14 */ 14 */
15 public interface IotDeviceDownstreamHandler { 15 public interface IotDeviceDownstreamHandler {
16 16
  17 + /**
  18 + * 调用设备服务
  19 + *
  20 + * @param invokeReqDTO 调用设备服务的请求
  21 + * @return 是否成功
  22 + */
17 CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO); 23 CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO);
18 24
  25 + /**
  26 + * 获取设备属性
  27 + *
  28 + * @param getReqDTO 获取设备属性的请求
  29 + * @return 是否成功
  30 + */
19 CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO); 31 CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO);
20 32
  33 + /**
  34 + * 设置设备属性
  35 + *
  36 + * @param setReqDTO 设置设备属性的请求
  37 + * @return 是否成功
  38 + */
21 CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO); 39 CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO);
22 40
23 } 41 }
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java
1 package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; 1 package cn.iocoder.yudao.module.iot.plugin.common.downstream.router;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO;
3 import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; 5 import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler;
  6 +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils;
4 import io.vertx.core.Handler; 7 import io.vertx.core.Handler;
5 import io.vertx.ext.web.RoutingContext; 8 import io.vertx.ext.web.RoutingContext;
6 import lombok.RequiredArgsConstructor; 9 import lombok.RequiredArgsConstructor;
7 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
  11 +import io.vertx.core.json.JsonObject;
  12 +import java.util.Map;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
  15 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
8 16
9 @Slf4j 17 @Slf4j
10 @RequiredArgsConstructor 18 @RequiredArgsConstructor
@@ -15,9 +23,34 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler&lt;RoutingContex @@ -15,9 +23,34 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler&lt;RoutingContex
15 private final IotDeviceDownstreamHandler deviceDownstreamHandler; 23 private final IotDeviceDownstreamHandler deviceDownstreamHandler;
16 24
17 @Override 25 @Override
  26 + @SuppressWarnings("unchecked")
18 public void handle(RoutingContext routingContext) { 27 public void handle(RoutingContext routingContext) {
19 - // TODO 芋艿:这里没实现  
20 - deviceDownstreamHandler.invokeDeviceService(null); 28 + // 1. 解析参数
  29 + IotDeviceServiceInvokeReqDTO invokeReqDTO;
  30 + try {
  31 + String productKey = routingContext.pathParam("productKey");
  32 + String deviceName = routingContext.pathParam("deviceName");
  33 + String identifier = routingContext.pathParam("identifier");
  34 + JsonObject body = routingContext.body().asJsonObject();
  35 + String requestId = body.getString("requestId");
  36 + Map<String, Object> params = (Map<String, Object>) body.getMap().get("params");
  37 + invokeReqDTO = ((IotDeviceServiceInvokeReqDTO) new IotDeviceServiceInvokeReqDTO()
  38 + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName))
  39 + .setIdentifier(identifier).setParams(params);
  40 + } catch (Exception e) {
  41 + log.error("[handle][解析参数失败]", e);
  42 + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST));
  43 + return;
  44 + }
  45 +
  46 + // 2. 调用下游处理器
  47 + try {
  48 + CommonResult<Boolean> result = deviceDownstreamHandler.invokeDeviceService(invokeReqDTO);
  49 + IotPluginCommonUtils.writeJson(routingContext, result);
  50 + } catch (Exception e) {
  51 + log.error("[handle][请求参数({}) 服务调用异常]", invokeReqDTO, e);
  52 + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR));
  53 + }
21 } 54 }
22 55
23 } 56 }
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java
@@ -3,6 +3,11 @@ package cn.iocoder.yudao.module.iot.plugin.common.util; @@ -3,6 +3,11 @@ package cn.iocoder.yudao.module.iot.plugin.common.util;
3 import cn.hutool.core.util.IdUtil; 3 import cn.hutool.core.util.IdUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.system.SystemUtil; 5 import cn.hutool.system.SystemUtil;
  6 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  7 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  8 +import io.vertx.core.http.HttpHeaders;
  9 +import io.vertx.ext.web.RoutingContext;
  10 +import org.springframework.http.MediaType;
6 11
7 /** 12 /**
8 * IoT 插件的通用工具类 13 * IoT 插件的通用工具类
@@ -28,4 +33,12 @@ public class IotPluginCommonUtils { @@ -28,4 +33,12 @@ public class IotPluginCommonUtils {
28 SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID(), IdUtil.fastSimpleUUID()); 33 SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID(), IdUtil.fastSimpleUUID());
29 } 34 }
30 35
  36 + @SuppressWarnings("deprecation")
  37 + public static void writeJson(RoutingContext routingContext, CommonResult<?> result) {
  38 + routingContext.response()
  39 + .setStatusCode(200)
  40 + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
  41 + .end(JsonUtils.toJsonString(result));
  42 + }
  43 +
31 } 44 }
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java
1 package cn.iocoder.yudao.module.iot.plugin.http.config; 1 package cn.iocoder.yudao.module.iot.plugin.http.config;
2 2
3 import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; 3 import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
  4 +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler;
  5 +import cn.iocoder.yudao.module.iot.plugin.http.downstream.IotDeviceDownstreamHandlerImpl;
4 import cn.iocoder.yudao.module.iot.plugin.http.upstream.IotDeviceUpstreamServer; 6 import cn.iocoder.yudao.module.iot.plugin.http.upstream.IotDeviceUpstreamServer;
5 import org.springframework.beans.factory.annotation.Value; 7 import org.springframework.beans.factory.annotation.Value;
6 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
@@ -26,4 +28,9 @@ public class IotPluginHttpAutoConfiguration { @@ -26,4 +28,9 @@ public class IotPluginHttpAutoConfiguration {
26 return new IotDeviceUpstreamServer(port, deviceUpstreamApi); 28 return new IotDeviceUpstreamServer(port, deviceUpstreamApi);
27 } 29 }
28 30
  31 + @Bean
  32 + public IotDeviceDownstreamHandler deviceDownstreamHandler() {
  33 + return new IotDeviceDownstreamHandlerImpl();
  34 + }
  35 +
29 } 36 }
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java
@@ -5,7 +5,8 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePr @@ -5,7 +5,8 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePr
5 import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; 5 import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO;
6 import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; 6 import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO;
7 import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; 7 import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler;
8 -import org.springframework.stereotype.Component; 8 +
  9 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED;
9 10
10 /** 11 /**
11 * HTTP 插件的 {@link IotDeviceDownstreamHandler} 实现类 12 * HTTP 插件的 {@link IotDeviceDownstreamHandler} 实现类
@@ -15,28 +16,21 @@ import org.springframework.stereotype.Component; @@ -15,28 +16,21 @@ import org.springframework.stereotype.Component;
15 * 16 *
16 * @author 芋道源码 17 * @author 芋道源码
17 */ 18 */
18 -@Component // TODO @芋艿:后续统一处理  
19 public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { 19 public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler {
20 20
21 @Override 21 @Override
22 public CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { 22 public CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) {
23 - // TODO @芋艿:待实现  
24 - System.out.println();  
25 - return null; 23 + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持调用设备服务");
26 } 24 }
27 25
28 @Override 26 @Override
29 public CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { 27 public CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) {
30 - // TODO @芋艿:待实现  
31 - System.out.println();  
32 - return null; 28 + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持获取设备属性");
33 } 29 }
34 30
35 @Override 31 @Override
36 public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { 32 public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) {
37 - // TODO @芋艿:待实现  
38 - System.out.println();  
39 - return null; 33 + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性");
40 } 34 }
41 35
42 } 36 }