Commit ca9575226637284b00fc07545b0b1082267aa743
1 parent
8e7bbfe0
【功能新增】IoT: 添加 EMQX 插件,支持设备连接认证和 MQTT 连接参数获取,优化配置文件
Showing
32 changed files
with
683 additions
and
161 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java
| @@ -71,6 +71,14 @@ public interface IotDeviceUpstreamApi { | @@ -71,6 +71,14 @@ public interface IotDeviceUpstreamApi { | ||
| 71 | @PostMapping(PREFIX + "/add-topology") | 71 | @PostMapping(PREFIX + "/add-topology") |
| 72 | CommonResult<Boolean> addDeviceTopology(@Valid @RequestBody IotDeviceTopologyAddReqDTO addReqDTO); | 72 | CommonResult<Boolean> addDeviceTopology(@Valid @RequestBody IotDeviceTopologyAddReqDTO addReqDTO); |
| 73 | 73 | ||
| 74 | + /** | ||
| 75 | + * 认证 Emqx 连接 | ||
| 76 | + * | ||
| 77 | + * @param authReqDTO 认证 Emqx 连接 DTO | ||
| 78 | + */ | ||
| 79 | + @PostMapping(PREFIX + "/authenticate-emqx-connection") | ||
| 80 | + CommonResult<Boolean> authenticateEmqxConnection(@Valid @RequestBody IotDeviceEmqxAuthReqDTO authReqDTO); | ||
| 81 | + | ||
| 74 | // ========== 插件相关 ========== | 82 | // ========== 插件相关 ========== |
| 75 | 83 | ||
| 76 | /** | 84 | /** |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; | ||
| 2 | + | ||
| 3 | +import jakarta.validation.constraints.NotEmpty; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * IoT 认证 Emqx 连接 Request DTO | ||
| 8 | + * | ||
| 9 | + * @author 芋道源码 | ||
| 10 | + */ | ||
| 11 | +@Data | ||
| 12 | +public class IotDeviceEmqxAuthReqDTO { | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 客户端 ID | ||
| 16 | + */ | ||
| 17 | + @NotEmpty(message = "客户端 ID 不能为空") | ||
| 18 | + private String clientId; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 用户名 | ||
| 22 | + */ | ||
| 23 | + @NotEmpty(message = "用户名不能为空") | ||
| 24 | + private String username; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 密码 | ||
| 28 | + */ | ||
| 29 | + @NotEmpty(message = "密码不能为空") | ||
| 30 | + private String password; | ||
| 31 | + | ||
| 32 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java
| @@ -4,15 +4,14 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; | @@ -4,15 +4,14 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; | 4 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; |
| 5 | import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService; | 5 | import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService; |
| 6 | import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; | 6 | import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; |
| 7 | +import jakarta.annotation.Resource; | ||
| 7 | import org.springframework.validation.annotation.Validated; | 8 | import org.springframework.validation.annotation.Validated; |
| 8 | import org.springframework.web.bind.annotation.RestController; | 9 | import org.springframework.web.bind.annotation.RestController; |
| 9 | 10 | ||
| 10 | -import jakarta.annotation.Resource; | ||
| 11 | - | ||
| 12 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 11 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 13 | 12 | ||
| 14 | /** | 13 | /** |
| 15 | - * * 设备数据 Upstream 上行 API 实现类 | 14 | + * * 设备数据 Upstream 上行 API 实现类 |
| 16 | */ | 15 | */ |
| 17 | @RestController | 16 | @RestController |
| 18 | @Validated | 17 | @Validated |
| @@ -61,6 +60,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { | @@ -61,6 +60,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { | ||
| 61 | return success(true); | 60 | return success(true); |
| 62 | } | 61 | } |
| 63 | 62 | ||
| 63 | + @Override | ||
| 64 | + public CommonResult<Boolean> authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { | ||
| 65 | + Boolean result = deviceUpstreamService.authenticateEmqxConnection(authReqDTO); | ||
| 66 | + return success(result); | ||
| 67 | + } | ||
| 68 | + | ||
| 64 | // ========== 插件相关 ========== | 69 | // ========== 插件相关 ========== |
| 65 | 70 | ||
| 66 | @Override | 71 | @Override |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
| @@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | @@ -7,8 +7,8 @@ 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.IotDeviceDownstreamReqVO; | 9 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; |
| 10 | -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; | ||
| 11 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; | 10 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; |
| 11 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; | ||
| 12 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 12 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 13 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | 13 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 14 | import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService; | 14 | import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService; |
| @@ -177,4 +177,11 @@ public class IotDeviceController { | @@ -177,4 +177,11 @@ public class IotDeviceController { | ||
| 177 | return success(true); | 177 | return success(true); |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | + @GetMapping("/mqtt-connection-params") | ||
| 181 | + @Operation(summary = "获取 MQTT 连接参数") | ||
| 182 | + @PreAuthorize("@ss.hasPermission('iot:device:mqtt-connection-params')") | ||
| 183 | + public CommonResult<IotDeviceMqttConnectionParamsRespVO> getMqttConnectionParams(@RequestParam("deviceId") Long deviceId) { | ||
| 184 | + return success(deviceService.getMqttConnectionParams(deviceId)); | ||
| 185 | + } | ||
| 186 | + | ||
| 180 | } | 187 | } |
| 181 | \ No newline at end of file | 188 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
| @@ -168,4 +168,12 @@ public interface IotDeviceService { | @@ -168,4 +168,12 @@ public interface IotDeviceService { | ||
| 168 | */ | 168 | */ |
| 169 | IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport); | 169 | IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport); |
| 170 | 170 | ||
| 171 | + /** | ||
| 172 | + * 获取 MQTT 连接参数 | ||
| 173 | + * | ||
| 174 | + * @param deviceId 设备 ID | ||
| 175 | + * @return MQTT 连接参数 | ||
| 176 | + */ | ||
| 177 | + IotDeviceMqttConnectionParamsRespVO getMqttConnectionParams(Long deviceId); | ||
| 178 | + | ||
| 171 | } | 179 | } |
| 172 | \ No newline at end of file | 180 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
| @@ -20,6 +20,8 @@ import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; | @@ -20,6 +20,8 @@ import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; | ||
| 20 | import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; | 20 | import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; |
| 21 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; | 21 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 22 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; | 22 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 23 | +import cn.iocoder.yudao.module.iot.util.MqttSignUtils; | ||
| 24 | +import cn.iocoder.yudao.module.iot.util.MqttSignUtils.MqttSignResult; | ||
| 23 | import jakarta.annotation.Resource; | 25 | import jakarta.annotation.Resource; |
| 24 | import jakarta.validation.ConstraintViolationException; | 26 | import jakarta.validation.ConstraintViolationException; |
| 25 | import lombok.extern.slf4j.Slf4j; | 27 | import lombok.extern.slf4j.Slf4j; |
| @@ -123,10 +125,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -123,10 +125,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 123 | .setDeviceType(product.getDeviceType()); | 125 | .setDeviceType(product.getDeviceType()); |
| 124 | // 生成并设置必要的字段 | 126 | // 生成并设置必要的字段 |
| 125 | // TODO @芋艿:各种 mqtt 是不是可以简化! | 127 | // TODO @芋艿:各种 mqtt 是不是可以简化! |
| 126 | - device.setDeviceSecret(generateDeviceSecret()) | ||
| 127 | - .setMqttClientId(generateMqttClientId()) | ||
| 128 | - .setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey())) | ||
| 129 | - .setMqttPassword(generateMqttPassword()); | 128 | + // clientId、username、password 根据规则实时生成 |
| 129 | + device.setDeviceSecret(generateDeviceSecret()); | ||
| 130 | // 设置设备状态为未激活 | 130 | // 设置设备状态为未激活 |
| 131 | device.setState(IotDeviceStateEnum.INACTIVE.getState()); | 131 | device.setState(IotDeviceStateEnum.INACTIVE.getState()); |
| 132 | } | 132 | } |
| @@ -318,35 +318,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -318,35 +318,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 318 | return IdUtil.fastSimpleUUID(); | 318 | return IdUtil.fastSimpleUUID(); |
| 319 | } | 319 | } |
| 320 | 320 | ||
| 321 | - /** | ||
| 322 | - * 生成 MQTT Client ID | ||
| 323 | - * | ||
| 324 | - * @return 生成的 MQTT Client ID | ||
| 325 | - */ | ||
| 326 | - private String generateMqttClientId() { | ||
| 327 | - return IdUtil.fastSimpleUUID(); | ||
| 328 | - } | ||
| 329 | - | ||
| 330 | - /** | ||
| 331 | - * 生成 MQTT Username | ||
| 332 | - * | ||
| 333 | - * @param deviceName 设备名称 | ||
| 334 | - * @param productKey 产品 Key | ||
| 335 | - * @return 生成的 MQTT Username | ||
| 336 | - */ | ||
| 337 | - private String generateMqttUsername(String deviceName, String productKey) { | ||
| 338 | - return deviceName + "&" + productKey; | ||
| 339 | - } | ||
| 340 | - | ||
| 341 | - /** | ||
| 342 | - * 生成 MQTT Password | ||
| 343 | - * | ||
| 344 | - * @return 生成的 MQTT Password | ||
| 345 | - */ | ||
| 346 | - private String generateMqttPassword() { | ||
| 347 | - return RandomUtil.randomString(32); | ||
| 348 | - } | ||
| 349 | - | ||
| 350 | @Override | 321 | @Override |
| 351 | @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 | 322 | @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 |
| 352 | public IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport) { | 323 | public IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport) { |
| @@ -417,6 +388,17 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -417,6 +388,17 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 417 | return respVO; | 388 | return respVO; |
| 418 | } | 389 | } |
| 419 | 390 | ||
| 391 | + @Override | ||
| 392 | + public IotDeviceMqttConnectionParamsRespVO getMqttConnectionParams(Long deviceId) { | ||
| 393 | + IotDeviceDO device = validateDeviceExists(deviceId); | ||
| 394 | + MqttSignResult mqttSignResult = MqttSignUtils.calculate(device.getProductKey(), device.getDeviceName(), | ||
| 395 | + device.getDeviceSecret()); | ||
| 396 | + return new IotDeviceMqttConnectionParamsRespVO() | ||
| 397 | + .setMqttClientId(mqttSignResult.getClientId()) | ||
| 398 | + .setMqttUsername(mqttSignResult.getUsername()) | ||
| 399 | + .setMqttPassword(mqttSignResult.getPassword()); | ||
| 400 | + } | ||
| 401 | + | ||
| 420 | private void deleteDeviceCache(IotDeviceDO device) { | 402 | private void deleteDeviceCache(IotDeviceDO device) { |
| 421 | // 保证 Spring AOP 触发 | 403 | // 保证 Spring AOP 触发 |
| 422 | getSelf().deleteDeviceCache0(device); | 404 | getSelf().deleteDeviceCache0(device); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java
| @@ -62,4 +62,11 @@ public interface IotDeviceUpstreamService { | @@ -62,4 +62,11 @@ public interface IotDeviceUpstreamService { | ||
| 62 | */ | 62 | */ |
| 63 | void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO); | 63 | void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO); |
| 64 | 64 | ||
| 65 | + /** | ||
| 66 | + * Emqx 连接认证 | ||
| 67 | + * | ||
| 68 | + * @param authReqDTO Emqx 连接认证 DTO | ||
| 69 | + */ | ||
| 70 | + Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO); | ||
| 71 | + | ||
| 65 | } | 72 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java
| @@ -20,6 +20,8 @@ import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer; | @@ -20,6 +20,8 @@ import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer; | ||
| 20 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | 20 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 21 | import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; | 21 | import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; |
| 22 | import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; | 22 | import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; |
| 23 | +import cn.iocoder.yudao.module.iot.util.MqttSignUtils; | ||
| 24 | +import cn.iocoder.yudao.module.iot.util.MqttSignUtils.MqttSignResult; | ||
| 23 | import jakarta.annotation.Resource; | 25 | import jakarta.annotation.Resource; |
| 24 | import lombok.extern.slf4j.Slf4j; | 26 | import lombok.extern.slf4j.Slf4j; |
| 25 | import org.springframework.stereotype.Service; | 27 | import org.springframework.stereotype.Service; |
| @@ -58,25 +60,26 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { | @@ -58,25 +60,26 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { | ||
| 58 | // 2.1 情况一:属性上报 | 60 | // 2.1 情况一:属性上报 |
| 59 | String requestId = IdUtil.fastSimpleUUID(); | 61 | String requestId = IdUtil.fastSimpleUUID(); |
| 60 | if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { | 62 | if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { |
| 61 | - reportDeviceProperty(((IotDevicePropertyReportReqDTO) | ||
| 62 | - new IotDevicePropertyReportReqDTO().setRequestId(requestId).setReportTime(LocalDateTime.now()) | ||
| 63 | - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) | 63 | + reportDeviceProperty(((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO() |
| 64 | + .setRequestId(requestId).setReportTime(LocalDateTime.now()) | ||
| 65 | + .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) | ||
| 64 | .setProperties((Map<String, Object>) simulatorReqVO.getData())); | 66 | .setProperties((Map<String, Object>) simulatorReqVO.getData())); |
| 65 | return; | 67 | return; |
| 66 | } | 68 | } |
| 67 | // 2.2 情况二:事件上报 | 69 | // 2.2 情况二:事件上报 |
| 68 | if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.EVENT.getType())) { | 70 | if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.EVENT.getType())) { |
| 69 | - reportDeviceEvent(((IotDeviceEventReportReqDTO) | ||
| 70 | - new IotDeviceEventReportReqDTO().setRequestId(requestId).setReportTime(LocalDateTime.now()) | ||
| 71 | - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) | ||
| 72 | - .setIdentifier(simulatorReqVO.getIdentifier()).setParams((Map<String, Object>) simulatorReqVO.getData())); | 71 | + reportDeviceEvent(((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) |
| 72 | + .setReportTime(LocalDateTime.now()) | ||
| 73 | + .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) | ||
| 74 | + .setIdentifier(simulatorReqVO.getIdentifier()) | ||
| 75 | + .setParams((Map<String, Object>) simulatorReqVO.getData())); | ||
| 73 | return; | 76 | return; |
| 74 | } | 77 | } |
| 75 | // 2.3 情况三:状态变更 | 78 | // 2.3 情况三:状态变更 |
| 76 | if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.STATE.getType())) { | 79 | if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.STATE.getType())) { |
| 77 | - updateDeviceState(((IotDeviceStateUpdateReqDTO) | ||
| 78 | - new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()).setReportTime(LocalDateTime.now()) | ||
| 79 | - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) | 80 | + updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO() |
| 81 | + .setRequestId(IdUtil.fastSimpleUUID()).setReportTime(LocalDateTime.now()) | ||
| 82 | + .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) | ||
| 80 | .setState((Integer) simulatorReqVO.getData())); | 83 | .setState((Integer) simulatorReqVO.getData())); |
| 81 | return; | 84 | return; |
| 82 | } | 85 | } |
| @@ -277,6 +280,37 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { | @@ -277,6 +280,37 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { | ||
| 277 | sendDeviceMessage(message, device); | 280 | sendDeviceMessage(message, device); |
| 278 | } | 281 | } |
| 279 | 282 | ||
| 283 | + @Override | ||
| 284 | + public Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { | ||
| 285 | + log.info("[authenticateEmqxConnection][认证 Emqx 连接: {}]", authReqDTO); | ||
| 286 | + // 1. 校验设备是否存在 | ||
| 287 | + // username 格式:${DeviceName}&${ProductKey} | ||
| 288 | + String[] usernameParts = authReqDTO.getUsername().split("&"); | ||
| 289 | + if (usernameParts.length != 2) { | ||
| 290 | + log.error("[authenticateEmqxConnection][认证失败,username 格式不正确]"); | ||
| 291 | + return Boolean.FALSE; | ||
| 292 | + } | ||
| 293 | + String deviceName = usernameParts[0]; | ||
| 294 | + String productKey = usernameParts[1]; | ||
| 295 | + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( | ||
| 296 | + productKey, deviceName); | ||
| 297 | + if (device == null) { | ||
| 298 | + log.error("[authenticateEmqxConnection][设备({}/{}) 不存在]", | ||
| 299 | + productKey, deviceName); | ||
| 300 | + return Boolean.FALSE; | ||
| 301 | + } | ||
| 302 | + // 2. 校验密码 | ||
| 303 | + String deviceSecret = device.getDeviceSecret(); | ||
| 304 | + String clientId = authReqDTO.getClientId(); | ||
| 305 | + MqttSignResult sign = MqttSignUtils.calculate(productKey, deviceName, deviceSecret, clientId); | ||
| 306 | + if (!StrUtil.equals(sign.getPassword(), authReqDTO.getPassword())) { | ||
| 307 | + log.error("[authenticateEmqxConnection][认证失败,密码不正确]"); | ||
| 308 | + return Boolean.FALSE; | ||
| 309 | + } | ||
| 310 | + log.info("[authenticateEmqxConnection][认证成功]"); | ||
| 311 | + return Boolean.TRUE; | ||
| 312 | + } | ||
| 313 | + | ||
| 280 | private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) { | 314 | private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) { |
| 281 | // 1. 【异步】记录设备与插件实例的映射 | 315 | // 1. 【异步】记录设备与插件实例的映射 |
| 282 | pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId()); | 316 | pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId()); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.util; | ||
| 2 | + | ||
| 3 | +import lombok.Getter; | ||
| 4 | + | ||
| 5 | +import javax.crypto.Mac; | ||
| 6 | +import javax.crypto.spec.SecretKeySpec; | ||
| 7 | +import java.nio.charset.StandardCharsets; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * MQTT 签名工具类 | ||
| 11 | + * 提供静态方法来计算 MQTT 连接参数。 | ||
| 12 | + */ | ||
| 13 | +public class MqttSignUtils { | ||
| 14 | + | ||
| 15 | + private static final String SIGN_METHOD = "hmacsha256"; | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 计算 MQTT 连接参数 | ||
| 19 | + * | ||
| 20 | + * @param productKey 产品密钥 | ||
| 21 | + * @param deviceName 设备名称 | ||
| 22 | + * @param deviceSecret 设备密钥 | ||
| 23 | + * @return 包含 clientId, username, password 的结果对象 | ||
| 24 | + */ | ||
| 25 | + public static MqttSignResult calculate(String productKey, String deviceName, String deviceSecret) { | ||
| 26 | + String clientId = productKey + "." + deviceName; | ||
| 27 | + String username = deviceName + "&" + productKey; | ||
| 28 | + String signContent = String.format("clientId%sdeviceName%sdeviceSecret%sproductKey%s", | ||
| 29 | + clientId, deviceName, deviceSecret, productKey); | ||
| 30 | + | ||
| 31 | + String password = sign(signContent, deviceSecret); | ||
| 32 | + | ||
| 33 | + return new MqttSignResult(clientId, username, password); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 计算 MQTT 连接参数 | ||
| 38 | + * | ||
| 39 | + * @param productKey 产品密钥 | ||
| 40 | + * @param deviceName 设备名称 | ||
| 41 | + * @param deviceSecret 设备密钥 | ||
| 42 | + * @param clientId 客户端 ID | ||
| 43 | + * @return 包含 clientId, username, password 的结果对象 | ||
| 44 | + */ | ||
| 45 | + public static MqttSignResult calculate(String productKey, String deviceName, String deviceSecret, String clientId) { | ||
| 46 | + String username = deviceName + "&" + productKey; | ||
| 47 | + String signContentBuilder = "clientId" + clientId + | ||
| 48 | + "deviceName" + deviceName + | ||
| 49 | + "deviceSecret" + deviceSecret + | ||
| 50 | + "productKey" + productKey; | ||
| 51 | + | ||
| 52 | + String password = sign(signContentBuilder, deviceSecret); | ||
| 53 | + | ||
| 54 | + return new MqttSignResult(clientId, username, password); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + private static String sign(String content, String key) { | ||
| 58 | + try { | ||
| 59 | + Mac mac = Mac.getInstance(SIGN_METHOD); | ||
| 60 | + mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), SIGN_METHOD)); | ||
| 61 | + byte[] signData = mac.doFinal(content.getBytes(StandardCharsets.UTF_8)); | ||
| 62 | + return bytesToHex(signData); | ||
| 63 | + } catch (Exception e) { | ||
| 64 | + throw new RuntimeException("Failed to sign content with HmacSHA256", e); | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + private static String bytesToHex(byte[] bytes) { | ||
| 69 | + StringBuilder hexString = new StringBuilder(bytes.length * 2); | ||
| 70 | + for (byte b : bytes) { | ||
| 71 | + String hex = Integer.toHexString(0xFF & b); | ||
| 72 | + if (hex.length() == 1) { | ||
| 73 | + hexString.append('0'); | ||
| 74 | + } | ||
| 75 | + hexString.append(hex); | ||
| 76 | + } | ||
| 77 | + return hexString.toString(); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + /** | ||
| 81 | + * MQTT 签名结果类 | ||
| 82 | + */ | ||
| 83 | + @Getter | ||
| 84 | + public static class MqttSignResult { | ||
| 85 | + private final String clientId; | ||
| 86 | + private final String username; | ||
| 87 | + private final String password; | ||
| 88 | + | ||
| 89 | + public MqttSignResult(String clientId, String username, String password) { | ||
| 90 | + this.clientId = clientId; | ||
| 91 | + this.username = username; | ||
| 92 | + this.password = password; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + } | ||
| 96 | +} | ||
| 0 | \ No newline at end of file | 97 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java
| @@ -44,8 +44,8 @@ public class IotPluginCommonAutoConfiguration { | @@ -44,8 +44,8 @@ public class IotPluginCommonAutoConfiguration { | ||
| 44 | 44 | ||
| 45 | @Bean(initMethod = "init", destroyMethod = "stop") | 45 | @Bean(initMethod = "init", destroyMethod = "stop") |
| 46 | public IotPluginInstanceHeartbeatJob pluginInstanceHeartbeatJob( | 46 | public IotPluginInstanceHeartbeatJob pluginInstanceHeartbeatJob( |
| 47 | - IotDeviceUpstreamApi deviceDataApi, IotDeviceDownstreamServer deviceDownstreamServer) { | ||
| 48 | - return new IotPluginInstanceHeartbeatJob(deviceDataApi, deviceDownstreamServer); | 47 | + IotDeviceUpstreamApi deviceDataApi, IotDeviceDownstreamServer deviceDownstreamServer, IotPluginCommonProperties commonProperties) { |
| 48 | + return new IotPluginInstanceHeartbeatJob(deviceDataApi, deviceDownstreamServer, commonProperties); | ||
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | } | 51 | } |
| 52 | \ No newline at end of file | 52 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java
| @@ -7,6 +7,11 @@ import org.springframework.validation.annotation.Validated; | @@ -7,6 +7,11 @@ import org.springframework.validation.annotation.Validated; | ||
| 7 | 7 | ||
| 8 | import java.time.Duration; | 8 | import java.time.Duration; |
| 9 | 9 | ||
| 10 | +/** | ||
| 11 | + * IoT 插件的通用配置类 | ||
| 12 | + * | ||
| 13 | + * @author haohao | ||
| 14 | + */ | ||
| 10 | @ConfigurationProperties(prefix = "yudao.iot.plugin.common") | 15 | @ConfigurationProperties(prefix = "yudao.iot.plugin.common") |
| 11 | @Validated | 16 | @Validated |
| 12 | @Data | 17 | @Data |
| @@ -45,4 +50,10 @@ public class IotPluginCommonProperties { | @@ -45,4 +50,10 @@ public class IotPluginCommonProperties { | ||
| 45 | */ | 50 | */ |
| 46 | private Integer downstreamPort = DOWNSTREAM_PORT_RANDOM; | 51 | private Integer downstreamPort = DOWNSTREAM_PORT_RANDOM; |
| 47 | 52 | ||
| 53 | + /** | ||
| 54 | + * 插件包标识符 | ||
| 55 | + */ | ||
| 56 | + @NotEmpty(message = "插件包标识符不能为空") | ||
| 57 | + private String pluginKey; | ||
| 58 | + | ||
| 48 | } | 59 | } |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java
| @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | ||
| 16 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | 16 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | - * IOT 设备配置设置 Vertx Handler | 19 | + * IoT 设备配置设置 Vertx Handler |
| 20 | * | 20 | * |
| 21 | * 芋道源码 | 21 | * 芋道源码 |
| 22 | */ | 22 | */ |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java
| @@ -5,14 +5,19 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceOt | @@ -5,14 +5,19 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceOt | ||
| 5 | 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; | 6 | import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; |
| 7 | import io.vertx.core.Handler; | 7 | import io.vertx.core.Handler; |
| 8 | +import io.vertx.core.json.JsonObject; | ||
| 8 | import io.vertx.ext.web.RoutingContext; | 9 | import io.vertx.ext.web.RoutingContext; |
| 9 | import lombok.RequiredArgsConstructor; | 10 | import lombok.RequiredArgsConstructor; |
| 10 | import lombok.extern.slf4j.Slf4j; | 11 | import lombok.extern.slf4j.Slf4j; |
| 11 | -import io.vertx.core.json.JsonObject; | ||
| 12 | 12 | ||
| 13 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | 13 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; |
| 14 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | 14 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; |
| 15 | 15 | ||
| 16 | +/** | ||
| 17 | + * IoT 设备 OTA 升级 Vertx Handler | ||
| 18 | + * <p> | ||
| 19 | + * 芋道源码 | ||
| 20 | + */ | ||
| 16 | @Slf4j | 21 | @Slf4j |
| 17 | @RequiredArgsConstructor | 22 | @RequiredArgsConstructor |
| 18 | public class IotDeviceOtaUpgradeVertxHandler implements Handler<RoutingContext> { | 23 | public class IotDeviceOtaUpgradeVertxHandler implements Handler<RoutingContext> { |
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
| @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | ||
| 16 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | 16 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | - * IOT 设备服务获取 Vertx Handler | 19 | + * IoT 设备服务获取 Vertx Handler |
| 20 | * | 20 | * |
| 21 | * 芋道源码 | 21 | * 芋道源码 |
| 22 | */ | 22 | */ |
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
| @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | ||
| 16 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | 16 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | - * IOT 设备服务设置 Vertx Handler | 19 | + * IoT 设备服务设置 Vertx Handler |
| 20 | * | 20 | * |
| 21 | * 芋道源码 | 21 | * 芋道源码 |
| 22 | */ | 22 | */ |
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
| @@ -5,17 +5,18 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceSe | @@ -5,17 +5,18 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceSe | ||
| 5 | 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; | 6 | import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; |
| 7 | import io.vertx.core.Handler; | 7 | import io.vertx.core.Handler; |
| 8 | +import io.vertx.core.json.JsonObject; | ||
| 8 | import io.vertx.ext.web.RoutingContext; | 9 | import io.vertx.ext.web.RoutingContext; |
| 9 | import lombok.RequiredArgsConstructor; | 10 | import lombok.RequiredArgsConstructor; |
| 10 | import lombok.extern.slf4j.Slf4j; | 11 | import lombok.extern.slf4j.Slf4j; |
| 11 | -import io.vertx.core.json.JsonObject; | 12 | + |
| 12 | import java.util.Map; | 13 | import java.util.Map; |
| 13 | 14 | ||
| 14 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | 15 | 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; | 16 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; |
| 16 | 17 | ||
| 17 | /** | 18 | /** |
| 18 | - * IOT 设备服务调用 Vertx Handler | 19 | + * IoT 设备服务调用 Vertx Handler |
| 19 | * | 20 | * |
| 20 | * 芋道源码 | 21 | * 芋道源码 |
| 21 | */ | 22 | */ |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java
| @@ -4,6 +4,7 @@ import cn.hutool.system.SystemUtil; | @@ -4,6 +4,7 @@ import cn.hutool.system.SystemUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | 5 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 6 | 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; |
| 7 | +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; | ||
| 7 | import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; | 8 | import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; |
| 8 | import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; | 9 | import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; |
| 9 | import lombok.RequiredArgsConstructor; | 10 | import lombok.RequiredArgsConstructor; |
| @@ -23,6 +24,7 @@ public class IotPluginInstanceHeartbeatJob { | @@ -23,6 +24,7 @@ public class IotPluginInstanceHeartbeatJob { | ||
| 23 | 24 | ||
| 24 | private final IotDeviceUpstreamApi deviceUpstreamApi; | 25 | private final IotDeviceUpstreamApi deviceUpstreamApi; |
| 25 | private final IotDeviceDownstreamServer deviceDownstreamServer; | 26 | private final IotDeviceDownstreamServer deviceDownstreamServer; |
| 27 | + private final IotPluginCommonProperties commonProperties; | ||
| 26 | 28 | ||
| 27 | public void init() { | 29 | public void init() { |
| 28 | CommonResult<Boolean> result = deviceUpstreamApi.heartbeatPluginInstance(buildPluginInstanceHeartbeatReqDTO(true)); | 30 | CommonResult<Boolean> result = deviceUpstreamApi.heartbeatPluginInstance(buildPluginInstanceHeartbeatReqDTO(true)); |
| @@ -41,9 +43,8 @@ public class IotPluginInstanceHeartbeatJob { | @@ -41,9 +43,8 @@ public class IotPluginInstanceHeartbeatJob { | ||
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | private IotPluginInstanceHeartbeatReqDTO buildPluginInstanceHeartbeatReqDTO(Boolean online) { | 45 | private IotPluginInstanceHeartbeatReqDTO buildPluginInstanceHeartbeatReqDTO(Boolean online) { |
| 44 | - // TODO @haohao:pluginKey 的获取??? | ||
| 45 | return new IotPluginInstanceHeartbeatReqDTO() | 46 | return new IotPluginInstanceHeartbeatReqDTO() |
| 46 | - .setPluginKey("yudao-module-iot-plugin-http").setProcessId(IotPluginCommonUtils.getProcessId()) | 47 | + .setPluginKey(commonProperties.getPluginKey()).setProcessId(IotPluginCommonUtils.getProcessId()) |
| 47 | .setHostIp(SystemUtil.getHostInfo().getAddress()).setDownstreamPort(deviceDownstreamServer.getPort()) | 48 | .setHostIp(SystemUtil.getHostInfo().getAddress()).setDownstreamPort(deviceDownstreamServer.getPort()) |
| 48 | .setOnline(online); | 49 | .setOnline(online); |
| 49 | } | 50 | } |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java
| @@ -58,6 +58,12 @@ public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi { | @@ -58,6 +58,12 @@ public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi { | ||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | @Override | 60 | @Override |
| 61 | + public CommonResult<Boolean> authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { | ||
| 62 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/authenticate-emqx-connection"; | ||
| 63 | + return doPost(url, authReqDTO); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + @Override | ||
| 61 | public CommonResult<Boolean> reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { | 67 | public CommonResult<Boolean> reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { |
| 62 | String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property"; | 68 | String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property"; |
| 63 | return doPost(url, reportReqDTO); | 69 | return doPost(url, reportReqDTO); |
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
| @@ -41,4 +41,12 @@ public class IotPluginCommonUtils { | @@ -41,4 +41,12 @@ public class IotPluginCommonUtils { | ||
| 41 | .end(JsonUtils.toJsonString(result)); | 41 | .end(JsonUtils.toJsonString(result)); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | + @SuppressWarnings("deprecation") | ||
| 45 | + public static void writeJson(RoutingContext routingContext, String result) { | ||
| 46 | + routingContext.response() | ||
| 47 | + .setStatusCode(200) | ||
| 48 | + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) | ||
| 49 | + .end(result); | ||
| 50 | + } | ||
| 51 | + | ||
| 44 | } | 52 | } |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/plugin.properties
| 1 | -plugin.id=plugin-emqx | ||
| 2 | -plugin.class=cn.iocoder.yudao.module.iot.plugin.EmqxPlugin | 1 | +plugin.id=yudao-module-iot-plugin-emqx |
| 2 | +plugin.class=cn.iocoder.yudao.module.iot.plugin.emqx.config.IotEmqxPlugin | ||
| 3 | plugin.version=1.0.0 | 3 | plugin.version=1.0.0 |
| 4 | -plugin.provider=ahh | 4 | +plugin.provider=yudao |
| 5 | plugin.dependencies= | 5 | plugin.dependencies= |
| 6 | -plugin.description=plugin-emqx-1.0.0 | 6 | +plugin.description=yudao-module-iot-plugin-emqx-1.0.0 |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml
| @@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
| 12 | <packaging>jar</packaging> | 12 | <packaging>jar</packaging> |
| 13 | 13 | ||
| 14 | <artifactId>yudao-module-iot-plugin-emqx</artifactId> | 14 | <artifactId>yudao-module-iot-plugin-emqx</artifactId> |
| 15 | + <version>1.0.0</version> | ||
| 15 | 16 | ||
| 16 | <name>${project.artifactId}</name> | 17 | <name>${project.artifactId}</name> |
| 17 | <description> | 18 | <description> |
| @@ -21,36 +22,16 @@ | @@ -21,36 +22,16 @@ | ||
| 21 | <properties> | 22 | <properties> |
| 22 | <!-- 插件相关 --> | 23 | <!-- 插件相关 --> |
| 23 | <plugin.id>emqx-plugin</plugin.id> | 24 | <plugin.id>emqx-plugin</plugin.id> |
| 24 | - <plugin.class>cn.iocoder.yudao.module.iot.plugin.EmqxPlugin</plugin.class> | ||
| 25 | - <plugin.version>0.0.1</plugin.version> | ||
| 26 | - <plugin.provider>ahh</plugin.provider> | ||
| 27 | - <plugin.description>emqx-plugin-0.0.1</plugin.description> | 25 | + <plugin.class>cn.iocoder.yudao.module.iot.plugin.emqx.config.IotEmqxPlugin</plugin.class> |
| 26 | + <plugin.version>${project.version}</plugin.version> | ||
| 27 | + <plugin.provider>yudao</plugin.provider> | ||
| 28 | + <plugin.description>${project.artifactId}-${project.version}</plugin.description> | ||
| 28 | <plugin.dependencies/> | 29 | <plugin.dependencies/> |
| 29 | </properties> | 30 | </properties> |
| 30 | 31 | ||
| 31 | <build> | 32 | <build> |
| 32 | <plugins> | 33 | <plugins> |
| 33 | - <!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section) | ||
| 34 | - <plugin> | ||
| 35 | - <groupId>org.codehaus.mojo</groupId> | ||
| 36 | - <artifactId>properties-maven-plugin</artifactId> | ||
| 37 | - <version>1.0-alpha-2</version> | ||
| 38 | - <executions> | ||
| 39 | - <execution> | ||
| 40 | - <phase>initialize</phase> | ||
| 41 | - <goals> | ||
| 42 | - <goal>read-project-properties</goal> | ||
| 43 | - </goals> | ||
| 44 | - <configuration> | ||
| 45 | - <files> | ||
| 46 | - <file>plugin.properties</file> | ||
| 47 | - </files> | ||
| 48 | - </configuration> | ||
| 49 | - </execution> | ||
| 50 | - </executions> | ||
| 51 | - </plugin> | ||
| 52 | - --> | ||
| 53 | - | 34 | + <!-- 插件模式 zip --> |
| 54 | <plugin> | 35 | <plugin> |
| 55 | <groupId>org.apache.maven.plugins</groupId> | 36 | <groupId>org.apache.maven.plugins</groupId> |
| 56 | <artifactId>maven-antrun-plugin</artifactId> | 37 | <artifactId>maven-antrun-plugin</artifactId> |
| @@ -94,6 +75,7 @@ | @@ -94,6 +75,7 @@ | ||
| 94 | </executions> | 75 | </executions> |
| 95 | </plugin> | 76 | </plugin> |
| 96 | 77 | ||
| 78 | + <!-- 插件模式 jar --> | ||
| 97 | <plugin> | 79 | <plugin> |
| 98 | <groupId>org.apache.maven.plugins</groupId> | 80 | <groupId>org.apache.maven.plugins</groupId> |
| 99 | <artifactId>maven-jar-plugin</artifactId> | 81 | <artifactId>maven-jar-plugin</artifactId> |
| @@ -111,54 +93,72 @@ | @@ -111,54 +93,72 @@ | ||
| 111 | </archive> | 93 | </archive> |
| 112 | </configuration> | 94 | </configuration> |
| 113 | </plugin> | 95 | </plugin> |
| 96 | + <!-- <plugin>--> | ||
| 97 | + <!-- <groupId>org.apache.maven.plugins</groupId>--> | ||
| 98 | + <!-- <artifactId>maven-shade-plugin</artifactId>--> | ||
| 99 | + <!-- <version>3.6.0</version>--> | ||
| 100 | + <!-- <executions>--> | ||
| 101 | + <!-- <execution>--> | ||
| 102 | + <!-- <phase>package</phase>--> | ||
| 103 | + <!-- <goals>--> | ||
| 104 | + <!-- <goal>shade</goal>--> | ||
| 105 | + <!-- </goals>--> | ||
| 106 | + <!-- <configuration>--> | ||
| 107 | + <!-- <minimizeJar>true</minimizeJar>--> | ||
| 108 | + <!-- </configuration>--> | ||
| 109 | + <!-- </execution>--> | ||
| 110 | + <!-- </executions>--> | ||
| 111 | + <!-- <configuration>--> | ||
| 112 | + <!-- <archive>--> | ||
| 113 | + <!-- <manifestEntries>--> | ||
| 114 | + <!-- <Plugin-Id>${plugin.id}</Plugin-Id>--> | ||
| 115 | + <!-- <Plugin-Class>${plugin.class}</Plugin-Class>--> | ||
| 116 | + <!-- <Plugin-Version>${plugin.version}</Plugin-Version>--> | ||
| 117 | + <!-- <Plugin-Provider>${plugin.provider}</Plugin-Provider>--> | ||
| 118 | + <!-- <Plugin-Description>${plugin.description}</Plugin-Description>--> | ||
| 119 | + <!-- <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies>--> | ||
| 120 | + <!-- </manifestEntries>--> | ||
| 121 | + <!-- </archive>--> | ||
| 122 | + <!-- </configuration>--> | ||
| 123 | + <!-- </plugin>--> | ||
| 114 | 124 | ||
| 125 | + <!-- 独立模式 --> | ||
| 115 | <plugin> | 126 | <plugin> |
| 116 | - <artifactId>maven-deploy-plugin</artifactId> | ||
| 117 | - <configuration> | ||
| 118 | - <skip>true</skip> | ||
| 119 | - </configuration> | 127 | + <groupId>org.springframework.boot</groupId> |
| 128 | + <artifactId>spring-boot-maven-plugin</artifactId> | ||
| 129 | + <version>${spring.boot.version}</version> | ||
| 130 | + <executions> | ||
| 131 | + <execution> | ||
| 132 | + <goals> | ||
| 133 | + <goal>repackage</goal> | ||
| 134 | + </goals> | ||
| 135 | + <configuration> | ||
| 136 | + <classifier>-standalone</classifier> | ||
| 137 | + </configuration> | ||
| 138 | + </execution> | ||
| 139 | + </executions> | ||
| 120 | </plugin> | 140 | </plugin> |
| 141 | + | ||
| 121 | </plugins> | 142 | </plugins> |
| 122 | </build> | 143 | </build> |
| 123 | 144 | ||
| 124 | <dependencies> | 145 | <dependencies> |
| 125 | - <!-- 其他依赖项 --> | ||
| 126 | - <dependency> | ||
| 127 | - <groupId>org.springframework.boot</groupId> | ||
| 128 | - <artifactId>spring-boot-starter-web</artifactId> | ||
| 129 | - </dependency> | ||
| 130 | - <!-- PF4J Spring 集成 --> | ||
| 131 | - <dependency> | ||
| 132 | - <groupId>org.pf4j</groupId> | ||
| 133 | - <artifactId>pf4j-spring</artifactId> | ||
| 134 | - <scope>provided</scope> | ||
| 135 | - </dependency> | ||
| 136 | - <!-- 项目依赖 --> | ||
| 137 | <dependency> | 146 | <dependency> |
| 138 | <groupId>cn.iocoder.boot</groupId> | 147 | <groupId>cn.iocoder.boot</groupId> |
| 139 | - <artifactId>yudao-module-iot-api</artifactId> | 148 | + <artifactId>yudao-module-iot-plugin-common</artifactId> |
| 140 | <version>${revision}</version> | 149 | <version>${revision}</version> |
| 141 | </dependency> | 150 | </dependency> |
| 151 | + | ||
| 152 | + <!-- Web 相关 --> | ||
| 142 | <dependency> | 153 | <dependency> |
| 143 | - <groupId>org.projectlombok</groupId> | ||
| 144 | - <artifactId>lombok</artifactId> | ||
| 145 | - <version>${lombok.version}</version> | ||
| 146 | - <scope>provided</scope> | ||
| 147 | - </dependency> | ||
| 148 | - <!-- Vert.x 核心依赖 --> | ||
| 149 | - <dependency> | ||
| 150 | - <groupId>io.vertx</groupId> | ||
| 151 | - <artifactId>vertx-core</artifactId> | 154 | + <groupId>org.springframework.boot</groupId> |
| 155 | + <artifactId>spring-boot-starter-web</artifactId> | ||
| 152 | </dependency> | 156 | </dependency> |
| 153 | - <!-- Vert.x Web 模块 --> | 157 | + |
| 158 | + <!-- 工具类相关 --> | ||
| 154 | <dependency> | 159 | <dependency> |
| 155 | <groupId>io.vertx</groupId> | 160 | <groupId>io.vertx</groupId> |
| 156 | <artifactId>vertx-web</artifactId> | 161 | <artifactId>vertx-web</artifactId> |
| 157 | </dependency> | 162 | </dependency> |
| 158 | - <!-- MQTT --> | ||
| 159 | - <dependency> | ||
| 160 | - <groupId>org.eclipse.paho</groupId> | ||
| 161 | - <artifactId>org.eclipse.paho.client.mqttv3</artifactId> | ||
| 162 | - </dependency> | ||
| 163 | </dependencies> | 163 | </dependencies> |
| 164 | </project> | 164 | </project> |
| 165 | \ No newline at end of file | 165 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.plugin; | ||
| 2 | - | ||
| 3 | -import cn.hutool.extra.spring.SpringUtil; | ||
| 4 | -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | ||
| 5 | -import lombok.extern.slf4j.Slf4j; | ||
| 6 | -import org.pf4j.Plugin; | ||
| 7 | -import org.pf4j.PluginWrapper; | ||
| 8 | - | ||
| 9 | -import java.util.concurrent.ExecutorService; | ||
| 10 | -import java.util.concurrent.Executors; | ||
| 11 | - | ||
| 12 | -@Slf4j | ||
| 13 | -public class EmqxPlugin extends Plugin { | ||
| 14 | - | ||
| 15 | - private ExecutorService executorService; | ||
| 16 | - | ||
| 17 | - public EmqxPlugin(PluginWrapper wrapper) { | ||
| 18 | - super(wrapper); | ||
| 19 | - this.executorService = Executors.newSingleThreadExecutor(); | ||
| 20 | - } | ||
| 21 | - | ||
| 22 | - @Override | ||
| 23 | - public void start() { | ||
| 24 | - log.info("EmqxPlugin.start()"); | ||
| 25 | - | ||
| 26 | - if (executorService.isShutdown() || executorService.isTerminated()) { | ||
| 27 | - executorService = Executors.newSingleThreadExecutor(); | ||
| 28 | - } | ||
| 29 | - | ||
| 30 | - IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class); | ||
| 31 | - if (deviceDataApi == null) { | ||
| 32 | - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); | ||
| 33 | - return; | ||
| 34 | - } | ||
| 35 | - | ||
| 36 | - } | ||
| 37 | - | ||
| 38 | - @Override | ||
| 39 | - public void stop() { | ||
| 40 | - log.info("EmqxPlugin.stop()"); | ||
| 41 | - } | ||
| 42 | -} | ||
| 43 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/IotEmqxPluginApplication.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.emqx; | ||
| 2 | + | ||
| 3 | +import lombok.extern.slf4j.Slf4j; | ||
| 4 | +import org.springframework.boot.SpringApplication; | ||
| 5 | +import org.springframework.boot.WebApplicationType; | ||
| 6 | +import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * IoT Emqx 插件的独立运行入口 | ||
| 10 | + */ | ||
| 11 | +@Slf4j | ||
| 12 | +@SpringBootApplication | ||
| 13 | +public class IotEmqxPluginApplication { | ||
| 14 | + | ||
| 15 | + public static void main(String[] args) { | ||
| 16 | + SpringApplication application = new SpringApplication(IotEmqxPluginApplication.class); | ||
| 17 | + application.setWebApplicationType(WebApplicationType.NONE); | ||
| 18 | + application.run(args); | ||
| 19 | + log.info("[main][独立模式启动完成]"); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | +} | ||
| 0 | \ No newline at end of file | 23 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.emqx.config; | ||
| 2 | + | ||
| 3 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 4 | +import lombok.extern.slf4j.Slf4j; | ||
| 5 | +import org.pf4j.PluginWrapper; | ||
| 6 | +import org.pf4j.spring.SpringPlugin; | ||
| 7 | +import org.springframework.context.ApplicationContext; | ||
| 8 | +import org.springframework.context.annotation.AnnotationConfigApplicationContext; | ||
| 9 | + | ||
| 10 | +// TODO @芋艿:完善注释 | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 负责插件的启动和停止 | ||
| 14 | + */ | ||
| 15 | +@Slf4j | ||
| 16 | +public class IotEmqxPlugin extends SpringPlugin { | ||
| 17 | + | ||
| 18 | + public IotEmqxPlugin(PluginWrapper wrapper) { | ||
| 19 | + super(wrapper); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public void start() { | ||
| 24 | + log.info("[EmqxPlugin][EmqxPlugin 插件启动开始...]"); | ||
| 25 | + try { | ||
| 26 | + | ||
| 27 | + log.info("[EmqxPlugin][EmqxPlugin 插件启动成功...]"); | ||
| 28 | + } catch (Exception e) { | ||
| 29 | + log.error("[EmqxPlugin][EmqxPlugin 插件开启动异常...]", e); | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + @Override | ||
| 34 | + public void stop() { | ||
| 35 | + log.info("[EmqxPlugin][EmqxPlugin 插件停止开始...]"); | ||
| 36 | + try { | ||
| 37 | + log.info("[EmqxPlugin][EmqxPlugin 插件停止成功...]"); | ||
| 38 | + } catch (Exception e) { | ||
| 39 | + log.error("[EmqxPlugin][EmqxPlugin 插件停止异常...]", e); | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Override | ||
| 44 | + protected ApplicationContext createApplicationContext() { | ||
| 45 | + // 创建插件自己的 ApplicationContext | ||
| 46 | + AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext(); | ||
| 47 | + // 设置父容器为主应用的 ApplicationContext (确保主应用中提供的类可用) | ||
| 48 | + pluginContext.setParent(SpringUtil.getApplicationContext()); | ||
| 49 | + // 继续使用插件自己的 ClassLoader 以加载插件内部的类 | ||
| 50 | + pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); | ||
| 51 | + // 扫描当前插件的自动配置包 | ||
| 52 | + pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.emqx.config"); | ||
| 53 | + pluginContext.refresh(); | ||
| 54 | + return pluginContext; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | +} | ||
| 0 | \ No newline at end of file | 58 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.emqx.config; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | ||
| 4 | +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; | ||
| 5 | +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; | ||
| 6 | +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; | ||
| 7 | +import cn.iocoder.yudao.module.iot.plugin.emqx.downstream.IotDeviceDownstreamHandlerImpl; | ||
| 8 | +import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.IotDeviceUpstreamServer; | ||
| 9 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
| 10 | +import org.springframework.context.annotation.Bean; | ||
| 11 | +import org.springframework.context.annotation.Configuration; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * IoT 插件 Emqx 的专用自动配置类 | ||
| 15 | + * | ||
| 16 | + * @author haohao | ||
| 17 | + */ | ||
| 18 | +@Configuration | ||
| 19 | +@EnableConfigurationProperties(IotPluginEmqxProperties.class) | ||
| 20 | +public class IotPluginEmqxAutoConfiguration { | ||
| 21 | + | ||
| 22 | + @Bean(initMethod = "start", destroyMethod = "stop") | ||
| 23 | + public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi, | ||
| 24 | + IotPluginCommonProperties commonProperties, | ||
| 25 | + IotPluginEmqxProperties emqxProperties, | ||
| 26 | + IotDeviceDownstreamServer deviceDownstreamServer) { | ||
| 27 | + return new IotDeviceUpstreamServer(commonProperties, emqxProperties, deviceUpstreamApi, deviceDownstreamServer); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + @Bean | ||
| 31 | + public IotDeviceDownstreamHandler deviceDownstreamHandler() { | ||
| 32 | + return new IotDeviceDownstreamHandlerImpl(); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | +} |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.emqx.config; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | ||
| 5 | +import org.springframework.validation.annotation.Validated; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 物联网插件 - EMQX 配置 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@ConfigurationProperties(prefix = "yudao.iot.plugin.emqx") | ||
| 13 | +@Validated | ||
| 14 | +@Data | ||
| 15 | +public class IotPluginEmqxProperties { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 服务主机 | ||
| 19 | + */ | ||
| 20 | + private String host; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 服务端口 | ||
| 24 | + */ | ||
| 25 | + private int port; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 是否启用 SSL | ||
| 29 | + */ | ||
| 30 | + private boolean ssl; | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 订阅的主题 | ||
| 34 | + */ | ||
| 35 | + private String topics; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 认证端口 | ||
| 39 | + */ | ||
| 40 | + private int authPort; | ||
| 41 | + | ||
| 42 | +} |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.emqx.downstream; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; | ||
| 5 | +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * EMQX 插件的 {@link IotDeviceDownstreamHandler} 实现类 | ||
| 9 | + * <p> | ||
| 10 | + * 但是:由于设备通过 HTTP 短链接接入,导致其实无法下行指导给 device 设备,所以基本都是直接返回失败!!! | ||
| 11 | + * 类似 MQTT、WebSocket、TCP 插件,是可以实现下行指令的。 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + */ | ||
| 15 | +public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { | ||
| 16 | + | ||
| 17 | + @Override | ||
| 18 | + public CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { | ||
| 19 | + return CommonResult.success(true); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { | ||
| 24 | + return CommonResult.success(true); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { | ||
| 29 | + return CommonResult.success(true); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public CommonResult<Boolean> setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) { | ||
| 34 | + return CommonResult.success(true); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public CommonResult<Boolean> upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) { | ||
| 39 | + return CommonResult.success(true); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | +} | ||
| 0 | \ No newline at end of file | 43 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.emqx.upstream; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | ||
| 4 | +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; | ||
| 5 | +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; | ||
| 6 | +import cn.iocoder.yudao.module.iot.plugin.emqx.config.IotPluginEmqxProperties; | ||
| 7 | +import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceAuthVertxHandler; | ||
| 8 | +import io.vertx.core.Vertx; | ||
| 9 | +import io.vertx.core.http.HttpServer; | ||
| 10 | +import io.vertx.ext.web.Router; | ||
| 11 | +import io.vertx.ext.web.handler.BodyHandler; | ||
| 12 | +import lombok.extern.slf4j.Slf4j; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * IoT 设备下行服务端,接收来自 device 设备的请求,转发给 server 服务器 | ||
| 16 | + * <p> | ||
| 17 | + * 协议:HTTP | ||
| 18 | + * | ||
| 19 | + * @author haohao | ||
| 20 | + */ | ||
| 21 | +@Slf4j | ||
| 22 | +public class IotDeviceUpstreamServer { | ||
| 23 | + | ||
| 24 | + private final Vertx vertx; | ||
| 25 | + private final HttpServer server; | ||
| 26 | + private final IotPluginEmqxProperties emqxProperties; | ||
| 27 | + | ||
| 28 | + public IotDeviceUpstreamServer(IotPluginCommonProperties commonProperties, | ||
| 29 | + IotPluginEmqxProperties emqxProperties, | ||
| 30 | + IotDeviceUpstreamApi deviceUpstreamApi, | ||
| 31 | + IotDeviceDownstreamServer deviceDownstreamServer) { | ||
| 32 | + this.emqxProperties = emqxProperties; | ||
| 33 | + // 创建 Vertx 实例 | ||
| 34 | + this.vertx = Vertx.vertx(); | ||
| 35 | + // 创建 Router 实例 | ||
| 36 | + Router router = Router.router(vertx); | ||
| 37 | + router.route().handler(BodyHandler.create()); // 处理 Body | ||
| 38 | + router.post(IotDeviceAuthVertxHandler.PATH) | ||
| 39 | + .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); | ||
| 40 | + // 创建 HttpServer 实例 | ||
| 41 | + this.server = vertx.createHttpServer().requestHandler(router); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 启动 HTTP 服务器 | ||
| 46 | + */ | ||
| 47 | + public void start() { | ||
| 48 | + log.info("[start][开始启动]"); | ||
| 49 | + server.listen(emqxProperties.getAuthPort()) | ||
| 50 | + .toCompletionStage() | ||
| 51 | + .toCompletableFuture() | ||
| 52 | + .join(); | ||
| 53 | + log.info("[start][启动完成,端口({})]", this.server.actualPort()); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 停止所有 | ||
| 58 | + */ | ||
| 59 | + public void stop() { | ||
| 60 | + log.info("[stop][开始关闭]"); | ||
| 61 | + try { | ||
| 62 | + // 关闭 HTTP 服务器 | ||
| 63 | + if (server != null) { | ||
| 64 | + server.close() | ||
| 65 | + .toCompletionStage() | ||
| 66 | + .toCompletableFuture() | ||
| 67 | + .join(); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + // 关闭 Vertx 实例 | ||
| 71 | + if (vertx != null) { | ||
| 72 | + vertx.close() | ||
| 73 | + .toCompletionStage() | ||
| 74 | + .toCompletableFuture() | ||
| 75 | + .join(); | ||
| 76 | + } | ||
| 77 | + log.info("[stop][关闭完成]"); | ||
| 78 | + } catch (Exception e) { | ||
| 79 | + log.error("[stop][关闭异常]", e); | ||
| 80 | + throw new RuntimeException(e); | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | +} |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | ||
| 5 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEmqxAuthReqDTO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; | ||
| 7 | +import io.vertx.core.Handler; | ||
| 8 | +import io.vertx.core.json.JsonObject; | ||
| 9 | +import io.vertx.ext.web.RoutingContext; | ||
| 10 | +import lombok.RequiredArgsConstructor; | ||
| 11 | +import lombok.extern.slf4j.Slf4j; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * IoT Emqx 连接认证的 Vert.x Handler | ||
| 15 | + * <a href="https://docs.emqx.com/zh/emqx/latest/access-control/authn/http.html">...</a> | ||
| 16 | + * | ||
| 17 | + * @author haohao | ||
| 18 | + */ | ||
| 19 | +@RequiredArgsConstructor | ||
| 20 | +@Slf4j | ||
| 21 | +public class IotDeviceAuthVertxHandler implements Handler<RoutingContext> { | ||
| 22 | + | ||
| 23 | + public static final String PATH = "/mqtt/auth"; | ||
| 24 | + | ||
| 25 | + private final IotDeviceUpstreamApi deviceUpstreamApi; | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + @SuppressWarnings("unchecked") | ||
| 29 | + public void handle(RoutingContext routingContext) { | ||
| 30 | + | ||
| 31 | + JsonObject json = routingContext.body().asJsonObject(); | ||
| 32 | + String clientId = json.getString("clientid"); | ||
| 33 | + String username = json.getString("username"); | ||
| 34 | + String password = json.getString("password"); | ||
| 35 | + | ||
| 36 | + IotDeviceEmqxAuthReqDTO authReqDTO = buildDeviceEmqxAuthReqDTO(clientId, username, password); | ||
| 37 | + | ||
| 38 | + CommonResult<Boolean> authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); | ||
| 39 | + if (authResult.getCode() != 0 || !authResult.getData()) { | ||
| 40 | + denyAccess(routingContext); | ||
| 41 | + return; | ||
| 42 | + } | ||
| 43 | + IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"allow\"}"); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + private void denyAccess(RoutingContext routingContext) { | ||
| 47 | + IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"deny\"}"); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + private IotDeviceEmqxAuthReqDTO buildDeviceEmqxAuthReqDTO(String clientId, String username, String password) { | ||
| 51 | + return new IotDeviceEmqxAuthReqDTO().setClientId(clientId).setUsername(username).setPassword(password); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | +} | ||
| 0 | \ No newline at end of file | 55 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml
0 → 100644
| 1 | +spring: | ||
| 2 | + application: | ||
| 3 | + name: yudao-module-iot-plugin-emqx | ||
| 4 | + | ||
| 5 | +yudao: | ||
| 6 | + iot: | ||
| 7 | + plugin: | ||
| 8 | + common: | ||
| 9 | + upstream-url: http://127.0.0.1:48080 | ||
| 10 | + downstream-port: 8100 | ||
| 11 | + plugin-key: yudao-module-iot-plugin-emqx | ||
| 12 | + emqx: | ||
| 13 | + host: 127.0.0.1 | ||
| 14 | + port: 1883 | ||
| 15 | + ssl: false | ||
| 16 | + topics: "/sys/#" | ||
| 17 | + auth-port: 8101 |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java
| @@ -21,7 +21,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | @@ -21,7 +21,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC | ||
| 21 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | 21 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | - * IoT 设备设备上报的 Vert.x Handler | 24 | + * IoT 设备事件上报的 Vert.x Handler |
| 25 | */ | 25 | */ |
| 26 | @RequiredArgsConstructor | 26 | @RequiredArgsConstructor |
| 27 | @Slf4j | 27 | @Slf4j |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml