diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java index ea2ec39837..0a2b3f0bf8 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java @@ -1,7 +1,10 @@ package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; +import java.util.Map; + /** * IoT 设备【服务】调用 Request DTO * @@ -9,4 +12,15 @@ import lombok.Data; */ @Data public class IotDeviceServiceInvokeReqDTO extends IotDeviceDownstreamAbstractReqDTO { + + /** + * 服务标识 + */ + @NotEmpty(message = "服务标识不能为空") + private String identifier; + /** + * 调用参数 + */ + private Map params; + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java index ee054a2ce0..8a7f3ff14d 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java +++ b/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 /** * 事件参数 */ - @NotEmpty(message = "事件参数不能为空") private Map params; } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 4539f12591..ed9b0ef138 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/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 { ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!"); + ErrorCode DEVICE_DOWNSTREAM_FAILED = new ErrorCode(1_050_003_007, "执行失败,原因:{}"); // ========== 产品分类 1-050-004-000 ========== ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); @@ -46,6 +47,6 @@ public interface ErrorCodeConstants { ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效"); // ========== 插件实例 1-050-007-000 ========== - ErrorCode PLUGIN_INSTANCE_NOT_EXISTS = new ErrorCode(1_050_007_000, "插件实例不存在"); + } \ No newline at end of file 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 new file mode 100644 index 0000000000..9518f10aa3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http @@ -0,0 +1,14 @@ +### 请求 /iot/device/simulation-downstream 接口 => 成功 +POST {{baseUrl}}/iot/device/simulation-downstream +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 25, + "type": "service", + "identifier": "temperature", + "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/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 8b18f1633d..6d73e0a456 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -171,7 +172,7 @@ public class IotDeviceController { @Operation(summary = "模拟设备下行") @PreAuthorize("@ss.hasPermission('iot:device:simulation')") public CommonResult simulationDownstreamDevice( - @Valid @RequestBody IotDeviceSimulationUpstreamReqVO downstreamReqVO) { + @Valid @RequestBody IotDeviceSimulationDownstreamReqVO downstreamReqVO) { deviceDownstreamService.simulationDeviceDownstream(downstreamReqVO); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java index afd0669414..55cfb19d4e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java +++ b/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 { * 存储具体的消息数据内容,通常是 JSON 格式 */ private String content; + /** + * 响应码 + * + * 目前只有 server 下行消息给 device 设备时,才会有响应码 + */ + private Integer code; /** * 上报时间戳 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java index db42b4fe16..baf45fbab2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java +++ b/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 { * 例如说:属性上报的 properties、事件上报的 params */ private Object data; + /** + * 响应码 + * + * 目前只有 server 下行消息给 device 设备时,才会有响应码 + */ + private Integer code; /** * 上报时间 */ private LocalDateTime reportTime; - // TODO @芋艿 code; - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java index e628f772c2..a3079552d0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.service.device.control; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; import jakarta.validation.Valid; /** @@ -17,6 +17,6 @@ public interface IotDeviceDownstreamService { * * @param downstreamReqVO 设备下行请求 VO */ - void simulationDeviceDownstream(@Valid IotDeviceSimulationUpstreamReqVO downstreamReqVO); + void simulationDeviceDownstream(@Valid IotDeviceSimulationDownstreamReqVO downstreamReqVO); } 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 792b754ca8..4fa7e45a96 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 @@ -1,17 +1,36 @@ package cn.iocoder.yudao.module.iot.service.device.control; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.lang.Assert; +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.IotDeviceServiceInvokeReqDTO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import org.springframework.web.client.RestTemplate; +import java.time.LocalDateTime; +import java.util.Map; import java.util.Objects; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED; + /** * 设备下行 Service 实现类 * @@ -24,9 +43,17 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @Resource private IotDeviceService deviceService; + @Resource + private IotPluginInstanceService pluginInstanceService; + + @Resource + private RestTemplate restTemplate; + + @Resource + private IotDeviceProducer deviceProducer; @Override - public void simulationDeviceDownstream(IotDeviceSimulationUpstreamReqVO downstreamReqVO) { + public void simulationDeviceDownstream(IotDeviceSimulationDownstreamReqVO downstreamReqVO) { // 校验设备是否存在 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); // TODO 芋艿:父设备的处理 @@ -40,12 +67,14 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic // 属性相关 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) // 属性设置 - if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { + if (Objects.equals(downstreamReqVO.getIdentifier(), + IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { setDeviceProperty(downstreamReqVO, device, parentDevice); return; } // 属性设置 - if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { + if (Objects.equals(downstreamReqVO.getIdentifier(), + IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { getDeviceProperty(downstreamReqVO, device, parentDevice); return; } @@ -53,22 +82,121 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic // TODO 芋艿:配置下发 } - private void invokeDeviceService(IotDeviceSimulationUpstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { - // 校验服务是否存在 - // TODO 芋艿:这里需要校验服务是否存在 - // 调用服务 - // TODO 芋艿:这里需要调用服务 + /** + * 调用设备服务 + * + * @param downstreamReqVO 下行请求 + * @param device 设备 + * @param parentDevice 父设备 + */ + @SuppressWarnings("unchecked") + private void invokeDeviceService(IotDeviceSimulationDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { + // 1. 参数校验 + if (!(downstreamReqVO.getData() instanceof Map)) { + throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); + } + + // 2. 发送请求 + String url = String.format( "sys/%s/%s/thing/service/%s", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice), downstreamReqVO.getIdentifier()); + IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO() + .setParams((Map) downstreamReqVO.getData()); + CommonResult result = requestPlugin(url, reqDTO, device); + + // 3. 发送设备消息 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) + .setType(IotDeviceMessageTypeEnum.SERVICE.getType()).setIdentifier(reqDTO.getIdentifier()) + .setData(reqDTO.getParams()); + sendDeviceMessage(message, device, result.getCode()); + + // 4. 如果不成功,抛出异常,提示用户 + if (result.isError()) { + log.error("[invokeDeviceService][设备({})服务调用失败,请求参数:({}),响应结果:({})]", + device.getDeviceKey(), reqDTO, result); + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); + } } - private void setDeviceProperty(IotDeviceSimulationUpstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { - // TODO 芋艿:这里需要设置设备属性 + /** + * 设置设备属性 + * + * @param downstreamReqVO 下行请求 + * @param device 设备 + * @param parentDevice 父设备 + */ + private void setDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { + // TODO 1. 请求 + + // TODO 2. 发送消息 } - private void getDeviceProperty(IotDeviceSimulationUpstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { + private void getDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { // TODO 芋艿:这里需要获取设备属性 } + /** + * 请求插件 + * + * @param url URL + * @param reqDTO 请求参数,只需要设置子类的参数! + * @param device 设备 + * @return 响应结果 + */ + @SuppressWarnings({"unchecked", "HttpUrlsUsage"}) + private CommonResult requestPlugin(String url, IotDeviceDownstreamAbstractReqDTO reqDTO, IotDeviceDO device) { + // 获得设备对应的插件实例 + IotPluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstanceByDeviceKey(device.getDeviceKey()); + if (pluginInstance == null) { + throw exception(DEVICE_DOWNSTREAM_FAILED, "设备找不到对应的插件实例"); + } + + // 补充通用参数 + reqDTO.setRequestId(IdUtil.fastSimpleUUID()); + + // 执行请求 + ResponseEntity> responseEntity; + try { + responseEntity = restTemplate.postForEntity( + String.format("http://%s:%d/%s", pluginInstance.getHostIp(), pluginInstance.getDownstreamPort(), url), + reqDTO, (Class>) (Class) CommonResult.class); + Assert.isTrue(responseEntity.getStatusCode().is2xxSuccessful(), + "HTTP 状态码不是 2xx,而是" + responseEntity.getStatusCode()); + Assert.notNull(responseEntity.getBody(), "响应结果不能为空"); + } catch (Exception ex) { + log.error("[requestPlugin][设备({}) url({}) 下行消息失败,请求参数({})]", device.getDeviceKey(), url, reqDTO, ex); + throw exception(DEVICE_DOWNSTREAM_FAILED, ExceptionUtil.getMessage(ex)); + } + return responseEntity.getBody(); + } + + private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) { + // 1. 完善消息 + message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()) + .setDeviceKey(device.getDeviceKey()); + Assert.notNull(message.getRequestId(), "requestId 不能为空"); + if (message.getReportTime() == null) { + message.setReportTime(LocalDateTime.now()); + } + message.setCode(code); + + // 2. 发送消息 + try { + deviceProducer.sendDeviceMessage(message); + log.info("[sendDeviceMessage][message({}) 发送消息成功]", message); + } catch (Exception e) { + log.error("[sendDeviceMessage][message({}) 发送消息失败]", message, e); + } + } + + private String getDeviceName(IotDeviceDO device, IotDeviceDO parentDevice) { + return parentDevice != null ? parentDevice.getDeviceName() : device.getDeviceName(); + } + + private String getProductKey(IotDeviceDO device, IotDeviceDO parentDevice) { + return parentDevice != null ? parentDevice.getProductKey() : device.getProductKey(); + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java index 482bad399b..1e48070806 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java +++ b/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; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; import org.springframework.web.multipart.MultipartFile; import java.time.LocalDateTime; @@ -67,4 +68,12 @@ public interface IotPluginInstanceService { */ void updateDevicePluginInstanceProcessIdAsync(String deviceKey, String processId); + /** + * 获得设备对应的插件实例 + * + * @param deviceKey 设备 Key + * @return 插件实例 + */ + IotPluginInstanceDO getPluginInstanceByDeviceKey(String deviceKey); + } \ 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/plugin/IotPluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java index d851399645..6742eccd79 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; @@ -203,4 +204,13 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { devicePluginProcessIdRedisDAO.put(deviceKey, processId); } + @Override + public IotPluginInstanceDO getPluginInstanceByDeviceKey(String deviceKey) { + String processId = devicePluginProcessIdRedisDAO.get(deviceKey); + if (StrUtil.isEmpty(processId)) { + return null; + } + return pluginInstanceMapper.selectByProcessId(processId); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml index 2ff555da9f..6522483b1b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml @@ -13,6 +13,7 @@ type NCHAR(50), identifier NCHAR(255), content NCHAR(1024), + code INT, report_time TIMESTAMP ) TAGS ( device_key NCHAR(50) @@ -24,7 +25,7 @@ - INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, report_time) + INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, code, report_time) USING device_log TAGS ('${deviceKey}') VALUES ( @@ -35,6 +36,7 @@ #{type}, #{identifier}, #{content}, + #{code}, #{reportTime} ) 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/IotDeviceDownstreamHandler.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/IotDeviceDownstreamHandler.java index 3c6fee7a44..127332cc7f 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/IotDeviceDownstreamHandler.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/IotDeviceDownstreamHandler.java @@ -14,10 +14,28 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceSe */ public interface IotDeviceDownstreamHandler { + /** + * 调用设备服务 + * + * @param invokeReqDTO 调用设备服务的请求 + * @return 是否成功 + */ CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO); + /** + * 获取设备属性 + * + * @param getReqDTO 获取设备属性的请求 + * @return 是否成功 + */ CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO); + /** + * 设置设备属性 + * + * @param setReqDTO 设置设备属性的请求 + * @return 是否成功 + */ CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO); } 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 27e1ff8d76..22ae7012d8 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 @@ -1,10 +1,18 @@ 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.IotDeviceServiceInvokeReqDTO; 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.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import io.vertx.core.json.JsonObject; +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; @Slf4j @RequiredArgsConstructor @@ -15,9 +23,34 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler params = (Map) body.getMap().get("params"); + invokeReqDTO = ((IotDeviceServiceInvokeReqDTO) new IotDeviceServiceInvokeReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setIdentifier(identifier).setParams(params); + } catch (Exception e) { + log.error("[handle][解析参数失败]", e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用下游处理器 + try { + CommonResult result = deviceDownstreamHandler.invokeDeviceService(invokeReqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 服务调用异常]", invokeReqDTO, 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/util/IotPluginCommonUtils.java b/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 index d44a4e02e7..f93717386e 100644 --- a/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 +++ b/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; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.system.SystemUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import io.vertx.core.http.HttpHeaders; +import io.vertx.ext.web.RoutingContext; +import org.springframework.http.MediaType; /** * IoT 插件的通用工具类 @@ -28,4 +33,12 @@ public class IotPluginCommonUtils { SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID(), IdUtil.fastSimpleUUID()); } + @SuppressWarnings("deprecation") + public static void writeJson(RoutingContext routingContext, CommonResult result) { + routingContext.response() + .setStatusCode(200) + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) + .end(JsonUtils.toJsonString(result)); + } + } diff --git a/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 b/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 index 388ad8ac41..4e402f91fd 100644 --- a/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 +++ b/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,6 +1,8 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.http.downstream.IotDeviceDownstreamHandlerImpl; import cn.iocoder.yudao.module.iot.plugin.http.upstream.IotDeviceUpstreamServer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -26,4 +28,9 @@ public class IotPluginHttpAutoConfiguration { return new IotDeviceUpstreamServer(port, deviceUpstreamApi); } + @Bean + public IotDeviceDownstreamHandler deviceDownstreamHandler() { + return new IotDeviceDownstreamHandlerImpl(); + } + } diff --git a/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 b/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 index 816a172537..b70052530c 100644 --- a/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 +++ b/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 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.plugin.common.downstream.IotDeviceDownstreamHandler; -import org.springframework.stereotype.Component; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED; /** * HTTP 插件的 {@link IotDeviceDownstreamHandler} 实现类 @@ -15,28 +16,21 @@ import org.springframework.stereotype.Component; * * @author 芋道源码 */ -@Component // TODO @芋艿:后续统一处理 public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { @Override public CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { - // TODO @芋艿:待实现 - System.out.println(); - return null; + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持调用设备服务"); } @Override public CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { - // TODO @芋艿:待实现 - System.out.println(); - return null; + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持获取设备属性"); } @Override public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { - // TODO @芋艿:待实现 - System.out.println(); - return null; + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); } }