diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http index 9518f10aa3..34df37e93b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http @@ -1,4 +1,4 @@ -### 请求 /iot/device/simulation-downstream 接口 => 成功 +### 请求 /iot/device/simulation-downstream 接口(服务调用) => 成功 POST {{baseUrl}}/iot/device/simulation-downstream Content-Type: application/json tenant-id: {{adminTenentId}} @@ -11,4 +11,32 @@ Authorization: Bearer {{token}} "data": { "xx": "yy" } +} + +### 请求 /iot/device/simulation-downstream 接口(属性设置) => 成功 +POST {{baseUrl}}/iot/device/simulation-downstream +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 25, + "type": "property", + "identifier": "set", + "data": { + "xx": "yy" + } +} + +### 请求 /iot/device/simulation-downstream 接口(属性获取) => 成功 +POST {{baseUrl}}/iot/device/simulation-downstream +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 25, + "type": "property", + "identifier": "get", + "data": ["xx", "yy"] } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 4fa7e45a96..7f341f57c3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -6,6 +6,8 @@ import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceDownstreamAbstractReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -24,6 +26,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.client.RestTemplate; import java.time.LocalDateTime; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -91,11 +94,12 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic */ @SuppressWarnings("unchecked") private void invokeDeviceService(IotDeviceSimulationDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { + IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof Map)) { throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); } + // TODO @super:【可优化】过滤掉不合法的服务 // 2. 发送请求 String url = String.format( "sys/%s/%s/thing/service/%s", @@ -125,16 +129,73 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param device 设备 * @param parentDevice 父设备 */ + @SuppressWarnings("unchecked") private void setDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { - // TODO 1. 请求 + IotDeviceDO device, IotDeviceDO parentDevice) { + // 1. 参数校验 + if (!(downstreamReqVO.getData() instanceof Map)) { + throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); + } + // TODO @super:【可优化】过滤掉不合法的属性 - // TODO 2. 发送消息 + // 2. 发送请求 + String url = String.format( "sys/%s/%s/thing/service/property/set", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); + IotDevicePropertySetReqDTO reqDTO = new IotDevicePropertySetReqDTO() + .setProperties((Map) downstreamReqVO.getData()); + CommonResult result = requestPlugin(url, reqDTO, device); + + // 3. 发送设备消息 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) + .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()) + .setData(reqDTO.getProperties()); + sendDeviceMessage(message, device, result.getCode()); + + // 4. 如果不成功,抛出异常,提示用户 + if (result.isError()) { + log.error("[setDeviceProperty][设备({})属性设置失败,请求参数:({}),响应结果:({})]", + device.getDeviceKey(), reqDTO, result); + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); + } } + /** + * 获取设备属性 + * + * @param downstreamReqVO 下行请求 + * @param device 设备 + * @param parentDevice 父设备 + */ + @SuppressWarnings("unchecked") private void getDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { - // TODO 芋艿:这里需要获取设备属性 + IotDeviceDO device, IotDeviceDO parentDevice) { + // 1. 参数校验 + if (!(downstreamReqVO.getData() instanceof List)) { + throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型"); + } + // TODO @super:【可优化】过滤掉不合法的属性 + + // 2. 发送请求 + String url = String.format( "sys/%s/%s/thing/service/property/get", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); + IotDevicePropertyGetReqDTO reqDTO = new IotDevicePropertyGetReqDTO() + .setIdentifiers((List) downstreamReqVO.getData()); + CommonResult result = requestPlugin(url, reqDTO, device); + + // 3. 发送设备消息 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) + .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()) + .setData(reqDTO.getIdentifiers()); + sendDeviceMessage(message, device, result.getCode()); + + // 4. 如果不成功,抛出异常,提示用户 + if (result.isError()) { + log.error("[getDeviceProperty][设备({})属性获取失败,请求参数:({}),响应结果:({})]", + device.getDeviceKey(), reqDTO, result); + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); + } } /** diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java index b87a2fc9db..4c45e972e5 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertyGetVertxHandler; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertySetVertxHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDeviceServiceInvokeVertxHandler; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; @@ -24,8 +26,12 @@ public class IotDeviceDownstreamServer { // 创建 Router 实例 Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); // 处理 Body - router.post(IotDeviceServiceInvokeVertxHandler.PATH).handler( - new IotDeviceServiceInvokeVertxHandler(deviceDownstreamHandler)); // 处理 Service Invoke + router.post(IotDeviceServiceInvokeVertxHandler.PATH) + .handler(new IotDeviceServiceInvokeVertxHandler(deviceDownstreamHandler)); + router.post(IotDevicePropertySetVertxHandler.PATH) + .handler(new IotDevicePropertySetVertxHandler(deviceDownstreamHandler)); + router.post(IotDevicePropertyGetVertxHandler.PATH) + .handler(new IotDevicePropertyGetVertxHandler(deviceDownstreamHandler)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java new file mode 100644 index 0000000000..5f9906fdc2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * IOT 设备服务获取 Vertx Handler + * + * 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class IotDevicePropertyGetVertxHandler implements Handler { + + public static final String PATH = "/sys/:productKey/:deviceName/thing/service/property/get"; + + private final IotDeviceDownstreamHandler deviceDownstreamHandler; + + @Override + @SuppressWarnings("unchecked") + public void handle(RoutingContext routingContext) { + // 1. 解析参数 + IotDevicePropertyGetReqDTO reqDTO; + try { + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + JsonObject body = routingContext.body().asJsonObject(); + String requestId = body.getString("requestId"); + List identifiers = (List) body.getMap().get("identifiers"); + reqDTO = ((IotDevicePropertyGetReqDTO) new IotDevicePropertyGetReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setIdentifiers(identifiers); + } catch (Exception e) { + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用处理器 + try { + CommonResult result = deviceDownstreamHandler.getDeviceProperty(reqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 属性获取异常]", reqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java new file mode 100644 index 0000000000..c8a60c7708 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * IOT 设备服务设置 Vertx Handler + * + * 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class IotDevicePropertySetVertxHandler implements Handler { + + public static final String PATH = "/sys/:productKey/:deviceName/thing/service/property/set"; + + private final IotDeviceDownstreamHandler deviceDownstreamHandler; + + @Override + @SuppressWarnings("unchecked") + public void handle(RoutingContext routingContext) { + // 1. 解析参数 + IotDevicePropertySetReqDTO reqDTO; + try { + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + JsonObject body = routingContext.body().asJsonObject(); + String requestId = body.getString("requestId"); + Map properties = (Map) body.getMap().get("properties"); + reqDTO = ((IotDevicePropertySetReqDTO) new IotDevicePropertySetReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setProperties(properties); + } catch (Exception e) { + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用处理器 + try { + CommonResult result = deviceDownstreamHandler.setDeviceProperty(reqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 属性设置异常]", reqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } + } + +} diff --git a/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 b/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 index 22ae7012d8..421fe7484a 100644 --- a/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 +++ b/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 @@ -14,6 +14,11 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; +/** + * IOT 设备服务调用 Vertx Handler + * + * 芋道源码 + */ @Slf4j @RequiredArgsConstructor public class IotDeviceServiceInvokeVertxHandler implements Handler { @@ -26,7 +31,7 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler params = (Map) body.getMap().get("params"); - invokeReqDTO = ((IotDeviceServiceInvokeReqDTO) new IotDeviceServiceInvokeReqDTO() + reqDTO = ((IotDeviceServiceInvokeReqDTO) new IotDeviceServiceInvokeReqDTO() .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) .setIdentifier(identifier).setParams(params); } catch (Exception e) { - log.error("[handle][解析参数失败]", e); + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); return; } - // 2. 调用下游处理器 + // 2. 调用处理器 try { - CommonResult result = deviceDownstreamHandler.invokeDeviceService(invokeReqDTO); + CommonResult result = deviceDownstreamHandler.invokeDeviceService(reqDTO); IotPluginCommonUtils.writeJson(routingContext, result); } catch (Exception e) { - log.error("[handle][请求参数({}) 服务调用异常]", invokeReqDTO, e); + log.error("[handle][请求参数({}) 服务调用异常]", reqDTO, e); IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); } }